<commit_before>import ReactiveElement from "../core/ReactiveElement.js"; // eslint-disable-line no-unused-vars
import {
  defaultState,
  goFirst,
  goLast,
  goNext,
  goPrevious,
  itemMatchesState,
  setState,
  state,
  stateEffects,
} from "./internal.js";

/**
 * Tracks and navigates the current item in a set of items
 *
 * @module ItemsCursorMixin
 * @param {Constructor<ReactiveElement>} Base
 */
export default function ItemsCursorMixin(Base) {
  // The class prototype added by the mixin.
  class ItemsCursor extends Base {
    get [defaultState]() {
      return Object.assign(super[defaultState] || {}, {
        canGoNext: false,
        canGoPrevious: false,
        currentIndex: -1,
        currentIndexPending: null,
        currentItem: null,
        currentItemRequired: false,
        cursorOperationsWrap: false,
        nextItemIndex: -1,
        previousItemIndex: -1,
      });
    }

    /**
     * Move to the first item in the set.
     *
     * @protected
     * @returns {Boolean} True if the current item changed, false if not.
     */
    [goFirst]() {
      if (super[goFirst]) {
        super[goFirst]();
      }
      return moveToIndex(this, 0);
    }

    /**
     * Move to the last item in the set.
     *
     * @protected
     * @returns {Boolean} True if the current item changed, false if not.
     */
    [goLast]() {
      if (super[goLast]) {
        super[goLast]();
      }
      return moveToIndex(this, this[state].items.length - 1);
    }

    /**
     * Move to the next item in the set.
     *
     * If no item is current, move to the first item.
     *
     * @protected
     * @returns {Boolean} True if the current item changed, false if not.
     */
    [goNext]() {
      if (super[goNext]) {
        super[goNext]();
      }
      const { nextItemIndex } = this[state];
      return nextItemIndex < 0 ? false : moveToIndex(this, nextItemIndex);
    }

    /**
     * Move to the previous item in the set.
     *
     * If no item is current, move to the last item.
     *
     * @protected
     * @returns {Boolean} True if the current item changed, false if not.
     */
    [goPrevious]() {
      if (super[goPrevious]) {
        super[goPrevious]();
      }
      const { previousItemIndex } = this[state];
      return previousItemIndex < 0 ? false : moveToIndex(this, previousItemIndex);
    }

    /**
     * Returns true if the given item should be shown in the indicated state.
     *
     * @param {ListItemElement} item
     * @param {PlainObject} state
     * @returns {boolean}
     */
    [itemMatchesState](item, state) {
      return super[itemMatchesState]
        ? super[itemMatchesState](item, state)
        : true;
    }

    [stateEffects](state, changed) {
      const effects = super[stateEffects]
        ? super[stateEffects](state, changed)
        : {};

      // Ensure currentIndex is valid.
      if (
        changed.items ||
        changed.currentIndex ||
        changed.currentItemRequired
      ) {
        const {
          items,
          currentIndex,
          currentIndexPending,
          currentItem,
          currentItemRequired,
        } = state;

        let newIndex = currentIndex;
        if (changed.items && items && !changed.currentIndex) {
          // The index stayed the same, but the item may have moved.
          if (items[currentIndex] !== currentItem) {
            // The item moved or was removed. See if we can find the item again
            // in the list of items.
            const newItemIndex = items.indexOf(currentItem);
            if (newItemIndex >= 0) {
              // Found the item again. Update the index to match.
              newIndex = newItemIndex;
            }
          }
        }

        const count = items ? items.length : 0;
        let newIndexPending = currentIndexPending;
        if (newIndex >= count) {
          // The requested index is beyond the bounds of the items array -- but
          // the items array may increase in size later. We remember which index
          // was requested as a pending index.
          newIndexPending = newIndex;
          // For now, force the index to be within bounds. If items array is
          // null or empty, this will be -1 (no selection).
          newIndex = count - 1;
        } else if (currentIndexPending !== null) {
          if (currentIndexPending < count) {
            // The items array has increased in size to the point where a pending
            // index can be applied and then discarded.
            newIndex = currentIndexPending;
            newIndexPending = null;
          } else {
            // Pick last index -- as close to pending index as we can get now.
            newIndex = count - 1;
          }
        } else if (newIndex === -1 && currentItemRequired && count > 0) {
          // We require a current item, don't have one yet, but do have items.
          // Take the 0th item as the current item.
          newIndex = 0;
        } else if (newIndex >= 0 && newIndex < count - 1) {
          // If a requested index is in bounds and not the last index, that
          // request supercedes any pending index.
          newIndexPending = null;
        } else {
          // Ensure index is at least -1 (no selection).
          newIndex = Math.max(newIndex, -1);
        }

        const newItem = items ? items[newIndex] : null;
        Object.assign(effects, {
          currentIndex: newIndex,
          currentIndexPending: newIndexPending,
          currentItem: newItem,
        });
      }

      // Update computed state members canGoNext/canGoPrevious.<commit_msg>Refine the condition under which we apply a pending current index.<commit_after>import ReactiveElement from "../core/ReactiveElement.js"; // eslint-disable-line no-unused-vars
import {
  defaultState,
  goFirst,
  goLast,
  goNext,
  goPrevious,
  itemMatchesState,
  setState,
  state,
  stateEffects,
} from "./internal.js";

/**
 * Tracks and navigates the current item in a set of items
 *
 * @module ItemsCursorMixin
 * @param {Constructor<ReactiveElement>} Base
 */
export default function ItemsCursorMixin(Base) {
  // The class prototype added by the mixin.
  class ItemsCursor extends Base {
    get [defaultState]() {
      return Object.assign(super[defaultState] || {}, {
        canGoNext: false,
        canGoPrevious: false,
        currentIndex: -1,
        currentIndexPending: null,
        currentItem: null,
        currentItemRequired: false,
        cursorOperationsWrap: false,
        nextItemIndex: -1,
        previousItemIndex: -1,
      });
    }

    /**
     * Move to the first item in the set.
     *
     * @protected
     * @returns {Boolean} True if the current item changed, false if not.
     */
    [goFirst]() {
      if (super[goFirst]) {
        super[goFirst]();
      }
      return moveToIndex(this, 0);
    }

    /**
     * Move to the last item in the set.
     *
     * @protected
     * @returns {Boolean} True if the current item changed, false if not.
     */
    [goLast]() {
      if (super[goLast]) {
        super[goLast]();
      }
      return moveToIndex(this, this[state].items.length - 1);
    }

    /**
     * Move to the next item in the set.
     *
     * If no item is current, move to the first item.
     *
     * @protected
     * @returns {Boolean} True if the current item changed, false if not.
     */
    [goNext]() {
      if (super[goNext]) {
        super[goNext]();
      }
      const { nextItemIndex } = this[state];
      return nextItemIndex < 0 ? false : moveToIndex(this, nextItemIndex);
    }

    /**
     * Move to the previous item in the set.
     *
     * If no item is current, move to the last item.
     *
     * @protected
     * @returns {Boolean} True if the current item changed, false if not.
     */
    [goPrevious]() {
      if (super[goPrevious]) {
        super[goPrevious]();
      }
      const { previousItemIndex } = this[state];
      return previousItemIndex < 0
        ? false
        : moveToIndex(this, previousItemIndex);
    }

    /**
     * Returns true if the given item should be shown in the indicated state.
     *
     * @param {ListItemElement} item
     * @param {PlainObject} state
     * @returns {boolean}
     */
    [itemMatchesState](item, state) {
      return super[itemMatchesState]
        ? super[itemMatchesState](item, state)
        : true;
    }

    [stateEffects](state, changed) {
      const effects = super[stateEffects]
        ? super[stateEffects](state, changed)
        : {};

      // Ensure currentIndex is valid.
      if (
        changed.items ||
        changed.currentIndex ||
        changed.currentItemRequired
      ) {
        const {
          items,
          currentIndex,
          currentIndexPending,
          currentItem,
          currentItemRequired,
        } = state;

        let newIndex = currentIndex;
        if (changed.items && items && !changed.currentIndex) {
          // The index stayed the same, but the item may have moved.
          if (items[currentIndex] !== currentItem) {
            // The item moved or was removed. See if we can find the item again
            // in the list of items.
            const newItemIndex = items.indexOf(currentItem);
            if (newItemIndex >= 0) {
              // Found the item again. Update the index to match.
              newIndex = newItemIndex;
            }
          }
        }

        const count = items ? items.length : 0;
        let newIndexPending = currentIndexPending;
        if (newIndex >= count) {
          // The requested index is beyond the bounds of the items array -- but
          // the items array may increase in size later. We remember which index
          // was requested as a pending index.
          newIndexPending = newIndex;
          // For now, force the index to be within bounds. If items array is
          // null or empty, this will be -1 (no selection).
          newIndex = count - 1;
        } else if (changed.items && currentIndexPending !== null) {
          if (currentIndexPending < count) {
            // The items array has increased in size to the point where a pending
            // index can be applied and then discarded.
            newIndex = currentIndexPending;
            newIndexPending = null;
          } else {
            // Pick last index -- as close to pending index as we can get now.
            newIndex = count - 1;
          }
        } else if (newIndex === -1 && currentItemRequired && count > 0) {
          // We require a current item, don't have one yet, but do have items.
          // Take the 0th item as the current item.
          newIndex = 0;
        } else if (newIndex >= 0 && newIndex < count - 1) {
          // If a requested index is in bounds and not the last index, that
          // request supercedes any pending index.
          newIndexPending = null;
        } else {
          // Ensure index is at least -1 (no selection).
          newIndex = Math.max(newIndex, -1);
        }

        const newItem = items ? items[newIndex] : null;
        Object.assign(effects, {
          currentIndex: newIndex,
          currentIndexPending: newIndexPending,
          currentItem: newItem,
        });
      }

      // Update computed state members canGoNext/canGoPrevious.
{'fraction_non_alphanumeric': 0.09796999117387467, 'fraction_numerical': 0.00353045013239188, 'mean_word_length': 1.8707879402077527, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, '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': '6618635', 'n_tokens_mistral': 3113, 'n_tokens_neox': 3084, 'n_words': 1052}