<commit_before>React = require 'react'
BS = require 'react-bootstrap'
_ = require 'underscore'
camelCase = require 'camelcase'

{ScrollListenerMixin} = require 'react-scroll-components'

{PinnedHeader, CardBody, PinnableFooter} = require './sections'

module.exports = React.createClass
  displayName: 'PinnedHeaderFooterCard'
  propTypes:
    buffer: React.PropTypes.number
    fixedOffset: React.PropTypes.number

  getDefaultProps: ->
    buffer: 60
    scrollSpeedBuffer: 30
    forceShy: false
    containerBuffer: 30

  getInitialState: ->
    offset: 0
    shy: false
    pinned: false
    shouldBeShy: false
    headerHeight: 0

  mixins: [ScrollListenerMixin]

  componentWillMount: ->
    @previousBodyClasses = document.body.className
    cardBodyClass = @props.cardType
    document.body.className = "#{cardBodyClass}-view"
    document.body.classList.add('pinned-view')
    document.body.classList.add('pinned-force-shy') if @props.forceShy

  componentWillUnmount: ->
    document.body.className = @previousBodyClasses

  getPosition: (el) -> el.getBoundingClientRect().top - document.body.getBoundingClientRect().top

  getOffset: ->
    if @props.fixedOffset?
      offset = @props.fixedOffset
    else
      offset = @getPosition(@refs.header.getDOMNode())

    offset

  setOffset: ->
    offset = @getOffset()
    @setState(offset: offset)

  shouldPinHeader: (prevScrollTop, currentScrollTop) ->
    currentScrollTop >= @state.offset

  isScrollingSlowed: (prevScrollTop, currentScrollTop) ->
    Math.abs(prevScrollTop - currentScrollTop) <= @props.scrollSpeedBuffer

  isScrollingDown: (prevScrollTop, currentScrollTop) ->
    currentScrollTop > prevScrollTop

  isScrollPassBuffer: (prevScrollTop, currentScrollTop) ->
    currentScrollTop >= @props.buffer + @state.offset

  shouldBeShy: (prevScrollTop, currentScrollTop) ->
    # should not pin regardless of scroll direction if the scroll top is above buffer
    unless @isScrollPassBuffer(prevScrollTop, currentScrollTop)
      shouldBeShy = false

    # otherwise, when scroll top is below buffer
    # and on down scroll
    else if @isScrollingDown(prevScrollTop, currentScrollTop)
      # header should pin
      shouldBeShy = true

    # or when up scrolling is slow
    else if @isScrollingSlowed(prevScrollTop, currentScrollTop)
      # leave the pinning as is
      shouldBeShy = @state.shy

    # else, the only case left is if up scrolling is fast
    else
      # unpin on fast up scroll
      shouldBeShy = false

    shouldBeShy

  updatePinState: (prevScrollTop) ->
    addOrRemove = [
      'remove' # remove class if shouldPinHeader is false
      'add' # add class if shouldPinHeader is true
    ]
    # set the pinned state
    @setState(
      # allow shouldBeShy override if needed
      shy: @state.shouldBeShy or @shouldBeShy(prevScrollTop, @state.scrollTop)
      pinned: @shouldPinHeader(prevScrollTop, @state.scrollTop)
      # reset shouldBeShy
      shouldBeShy: false
    )
    shouldPinHeader = @state.pinned * 1
    shouldBeShy = @state.shy * 1

    pinnedClassAction = addOrRemove[shouldPinHeader]
    document.body.classList[pinnedClassAction]('pinned-on')

    shyClassAction = addOrRemove[shouldBeShy]
    document.body.classList[shyClassAction]('pinned-shy')

  forceShy: ->
    window.scroll(0, @props.buffer + @state.offset)
    @setState(shouldBeShy: true)

  getHeaderHeight: ->
    header = @refs.header.getDOMNode()
    headerHeight = header.offsetHeight

  setContainerMargin: ->
    headerHeight = @getHeaderHeight()
    container = @refs.container.getDOMNode()

    @setState(headerHeight: headerHeight)
    container.style.marginTop = (headerHeight + @props.containerBuffer) + 'px'

  componentDidMount: ->
    @setOffset()
    @updatePinState(0)
    @setContainerMargin()

  componentDidUpdate: (prevProps, prevState) ->
    didOffsetChange = (not @state.pinned) and not (@state.offset is @getOffset())
    didShouldPinChange = not prevState.pinned is @shouldPinHeader(prevState.scrollTop, @state.scrollTop)
    didShouldBeShyChange = not prevState.shy is @shouldBeShy(prevState.scrollTop, @state.scrollTop)
    didHeaderHeightChange = not @state.headerHeight is @getHeaderHeight()

    @setOffset() if didOffsetChange
    @updatePinState(prevState.scrollTop) if didShouldPinChange or didShouldBeShyChange
    @setContainerMargin() if didHeaderHeightChange

  componentWillReceiveProps: ->
    @forceShy() if @props.forceShy

  render: ->
    {className} = @props

    classes = ['pinned-container']
    classes.push(className) if className?
    classes = classes.join(' ')

    childrenProps = _.omit(@props, 'children', 'header', 'footer', 'className')<commit_msg>change margin from top based on header height<commit_after>React = require 'react'
BS = require 'react-bootstrap'
_ = require 'underscore'
camelCase = require 'camelcase'

{ScrollListenerMixin} = require 'react-scroll-components'

{PinnedHeader, CardBody, PinnableFooter} = require './sections'

module.exports = React.createClass
  displayName: 'PinnedHeaderFooterCard'
  propTypes:
    buffer: React.PropTypes.number
    fixedOffset: React.PropTypes.number

  getDefaultProps: ->
    buffer: 60
    scrollSpeedBuffer: 30
    forceShy: false
    containerBuffer: 30

  getInitialState: ->
    offset: 0
    shy: false
    pinned: false
    shouldBeShy: false
    headerHeight: 0

  mixins: [ScrollListenerMixin]

  componentWillMount: ->
    @previousBodyClasses = document.body.className
    cardBodyClass = @props.cardType
    document.body.className = "#{cardBodyClass}-view"
    document.body.classList.add('pinned-view')
    document.body.classList.add('pinned-force-shy') if @props.forceShy

  componentWillUnmount: ->
    document.body.className = @previousBodyClasses

  getPosition: (el) -> el.getBoundingClientRect().top - document.body.getBoundingClientRect().top

  getOffset: ->
    if @props.fixedOffset?
      offset = @props.fixedOffset
    else
      offset = @getPosition(@refs.header.getDOMNode())

    offset

  setOffset: ->
    offset = @getOffset()
    @setState(offset: offset)

  shouldPinHeader: (prevScrollTop, currentScrollTop) ->
    currentScrollTop >= @state.offset

  isScrollingSlowed: (prevScrollTop, currentScrollTop) ->
    Math.abs(prevScrollTop - currentScrollTop) <= @props.scrollSpeedBuffer

  isScrollingDown: (prevScrollTop, currentScrollTop) ->
    currentScrollTop > prevScrollTop

  isScrollPassBuffer: (prevScrollTop, currentScrollTop) ->
    currentScrollTop >= @props.buffer + @state.offset

  shouldBeShy: (prevScrollTop, currentScrollTop) ->
    # should not pin regardless of scroll direction if the scroll top is above buffer
    unless @isScrollPassBuffer(prevScrollTop, currentScrollTop)
      shouldBeShy = false

    # otherwise, when scroll top is below buffer
    # and on down scroll
    else if @isScrollingDown(prevScrollTop, currentScrollTop)
      # header should pin
      shouldBeShy = true

    # or when up scrolling is slow
    else if @isScrollingSlowed(prevScrollTop, currentScrollTop)
      # leave the pinning as is
      shouldBeShy = @state.shy

    # else, the only case left is if up scrolling is fast
    else
      # unpin on fast up scroll
      shouldBeShy = false

    shouldBeShy

  updatePinState: (prevScrollTop) ->
    addOrRemove = [
      'remove' # remove class if shouldPinHeader is false
      'add' # add class if shouldPinHeader is true
    ]
    # set the pinned state
    @setState(
      # allow shouldBeShy override if needed
      shy: @state.shouldBeShy or @shouldBeShy(prevScrollTop, @state.scrollTop)
      pinned: @shouldPinHeader(prevScrollTop, @state.scrollTop)
      # reset shouldBeShy
      shouldBeShy: false
    )
    shouldPinHeader = @state.pinned * 1
    shouldBeShy = @state.shy * 1

    pinnedClassAction = addOrRemove[shouldPinHeader]
    document.body.classList[pinnedClassAction]('pinned-on')

    shyClassAction = addOrRemove[shouldBeShy]
    document.body.classList[shyClassAction]('pinned-shy')

  forceShy: ->
    window.scroll(0, @props.buffer + @state.offset)
    @setState(shouldBeShy: true)

  getHeaderHeight: ->
    header = @refs.header.getDOMNode()
    headerHeight = header.offsetHeight

  setContainerMargin: ->
    headerHeight = @getHeaderHeight()
    container = @refs.container.getDOMNode()

    @setState(headerHeight: headerHeight)
    container.style.marginTop = (headerHeight + @props.containerBuffer) + 'px'

  componentDidMount: ->
    @setOffset()
    @updatePinState(0)
    @setContainerMargin()

  componentDidUpdate: (prevProps, prevState) ->
    didOffsetChange = (not @state.pinned) and not (@state.offset is @getOffset())
    didShouldPinChange = not prevState.pinned is @shouldPinHeader(prevState.scrollTop, @state.scrollTop)
    didShouldBeShyChange = not prevState.shy is @shouldBeShy(prevState.scrollTop, @state.scrollTop)
    didHeaderHeightChange = not (prevState.headerHeight is @getHeaderHeight())

    @setOffset() if didOffsetChange
    @updatePinState(prevState.scrollTop) if didShouldPinChange or didShouldBeShyChange
    @setContainerMargin() if didHeaderHeightChange

  componentWillReceiveProps: ->
    @forceShy() if @props.forceShy

  render: ->
    {className} = @props

    classes = ['pinned-container']
    classes.push(className) if className?
    classes = classes.join(' ')

    childrenProps = _.omit(@props, 'children', 'header', 'footer', 'className')
{'fraction_non_alphanumeric': 0.0990971853425385, 'fraction_numerical': 0.0025491237387148167, 'mean_word_length': 3.843621399176955, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '366880', 'n_tokens_mistral': 2887, 'n_tokens_neox': 2814, 'n_words': 644}