<commit_before><template>

  <div class="scrolling-header" :class="classes">
    <slot></slot>
  </div>

</template>


<script>

  import debounce from 'lodash/debounce';

  // Used to avoid large a gap between KPageContainer and top of view when hidden
  const TOP_THRESHOLD = 40;

  export default {
    name: 'ScrollingHeader',
    props: {
      // Current scroll offset of content pane
      scrollPosition: {
        type: Number,
        required: true,
        validator(value) {
          return value >= 0;
        },
      },
      // If 'true', keeps the header permanently pinned to the top
      alwaysVisible: {
        type: Boolean,
        default: false,
      },
      // The scrollable height of the content area
      mainWrapperScrollHeight: {
        type: Number,
      },
      // Synced with CoreBase to handle changes when window is resized
      isHidden: {
        type: Boolean,
        default: false,
      },
    },
    data() {
      return {
        scrollDistance: 0,
      };
    },
    computed: {
      resetDistanceDebounced() {
        return debounce(this.resetDistance, 500);
      },
      classes() {
        return {
          'is-hidden': this.isHidden,
          'dir-up': this.scrollDistance > 0,
          'dir-down': this.scrollDistance <= 0,
        };
      },
      scrollThreshold() {
        // Scroll thresholds are relative to how much you can scroll, since
        // users may also modulate their scrolling based on how much content they see.
        // Also, to mitigate overscroll rebound and other reasons,
        // the upward threshold is set higher than the downward one.
        // Capped at 240 px, which is half the height of a iPhone 4,
        // the page would need to be be at least 4800px high to reach this cap.
        const downThresh = Math.min(Math.round(this.mainWrapperScrollHeight * 0.05), 240);
        return {
          up: downThresh * 2,
          down: downThresh,
        };
      },
      pastMinScroll() {
        return this.scrollPosition > TOP_THRESHOLD;
      },
    },
    watch: {
      scrollPosition(newVal, oldVal) {
        if (!this.alwaysVisible) {
          this.handleNewScrollPosition(newVal, oldVal);
        }
      },
    },
    methods: {
      handleNewScrollPosition(newVal, oldVal) {
        const delta = newVal - oldVal;

        // If delta shouldn't cause a change in isHidden, then do nothing
        if ((this.isHidden && delta > 0) || (!this.isHidden && delta < 0)) {
          return;
        }

        // Update scrollDistance
        if (newVal === 0) {
          this.scrollDistance = 0;
        } else if (this.scrollDistance * delta < 0) {
          // Reset the distance if the direction changes
          this.scrollDistance = delta;
        } else {
          this.scrollDistance = this.scrollDistance + delta;
        }

        // If thresholds have been passed, then update isHidden
        if (delta < 0) {
          // Un-hide if near top or up-delta is past threshold
          if (!this.pastMinScroll || -this.scrollDistance > this.scrollThreshold.up) {
            this.$emit('update:isHidden', false);
          }
        } else if (delta > 0) {
          // Hide if past the top and down-delta is past threshold
          if (this.pastMinScroll && this.scrollDistance > this.scrollThreshold.down) {
            this.$emit('update:isHidden', true);
          }
        }

        this.resetDistanceDebounced(delta, this.scrollPosition);
      },
      // Reset the scrolling distance if user pauses scrolling for some time.
      resetDistance(delta, lastPos) {
        setTimeout(() => {
          if (this.scrollPosition === lastPos) {
            // Set to +/- 1 to maintain the direction
            this.scrollDistance = Math.sign(delta);
          }
        }, 2000);
      },
    },
  };

</script>
<commit_msg>don't allow scroll position to be negative<commit_after><template>

  <div class="scrolling-header" :class="classes">
    <slot></slot>
  </div>

</template>


<script>

  import debounce from 'lodash/debounce';

  // Used to avoid large a gap between KPageContainer and top of view when hidden
  const TOP_THRESHOLD = 40;

  export default {
    name: 'ScrollingHeader',
    props: {
      // Current scroll offset of content pane
      scrollPosition: {
        type: Number,
        required: true,
        validator(value) {
          return value >= 0;
        },
      },
      // If 'true', keeps the header permanently pinned to the top
      alwaysVisible: {
        type: Boolean,
        default: false,
      },
      // The scrollable height of the content area
      mainWrapperScrollHeight: {
        type: Number,
      },
      // Synced with CoreBase to handle changes when window is resized
      isHidden: {
        type: Boolean,
        default: false,
      },
    },
    data() {
      return {
        scrollDistance: 0,
      };
    },
    computed: {
      scrollPositionOr0() {
        // in case a negative number is passed in
        return Math.max(this.scrollPosition, 0);
      },
      resetDistanceDebounced() {
        return debounce(this.resetDistance, 500);
      },
      classes() {
        return {
          'is-hidden': this.isHidden,
          'dir-up': this.scrollDistance > 0,
          'dir-down': this.scrollDistance <= 0,
        };
      },
      scrollThreshold() {
        // Scroll thresholds are relative to how much you can scroll, since
        // users may also modulate their scrolling based on how much content they see.
        // Also, to mitigate overscroll rebound and other reasons,
        // the upward threshold is set higher than the downward one.
        // Capped at 240 px, which is half the height of a iPhone 4,
        // the page would need to be be at least 4800px high to reach this cap.
        const downThresh = Math.min(Math.round(this.mainWrapperScrollHeight * 0.05), 240);
        return {
          up: downThresh * 2,
          down: downThresh,
        };
      },
      pastMinScroll() {
        return this.scrollPositionOr0 > TOP_THRESHOLD;
      },
    },
    watch: {
      scrollPositionOr0(newVal, oldVal) {
        if (!this.alwaysVisible) {
          this.handleNewScrollPosition(newVal, oldVal);
        }
      },
    },
    methods: {
      handleNewScrollPosition(newVal, oldVal) {
        const delta = newVal - oldVal;

        // If delta shouldn't cause a change in isHidden, then do nothing
        if ((this.isHidden && delta > 0) || (!this.isHidden && delta < 0)) {
          return;
        }

        // Update scrollDistance
        if (newVal === 0) {
          this.scrollDistance = 0;
        } else if (this.scrollDistance * delta < 0) {
          // Reset the distance if the direction changes
          this.scrollDistance = delta;
        } else {
          this.scrollDistance = this.scrollDistance + delta;
        }

        // If thresholds have been passed, then update isHidden
        if (delta < 0) {
          // Un-hide if near top or up-delta is past threshold
          if (!this.pastMinScroll || -this.scrollDistance > this.scrollThreshold.up) {
            this.$emit('update:isHidden', false);
          }
        } else if (delta > 0) {
          // Hide if past the top and down-delta is past threshold
          if (this.pastMinScroll && this.scrollDistance > this.scrollThreshold.down) {
            this.$emit('update:isHidden', true);
          }
        }

        this.resetDistanceDebounced(delta, this.scrollPositionOr0);
      },
      // Reset the scrolling distance if user pauses scrolling for some time.
      resetDistance(delta, lastPos) {
        setTimeout(() => {
          if (this.scrollPositionOr0 === lastPos) {
            // Set to +/- 1 to maintain the direction
            this.scrollDistance = Math.sign(delta);
          }
        }, 2000);
      },
    },
  };

</script>

{'fraction_non_alphanumeric': 0.10310322989233692, 'fraction_numerical': 0.009879670677644078, 'mean_word_length': 1.8505415162454875, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 35, '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': '6124962', 'n_tokens_mistral': 2252, 'n_tokens_neox': 2115, 'n_words': 674}