downshift 8.3.3 → 8.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1549,8 +1549,8 @@ function stateReducer(s, a) {
1549
1549
  */
1550
1550
  function getA11ySelectionMessage(selectionParameters) {
1551
1551
  var selectedItem = selectionParameters.selectedItem,
1552
- itemToStringLocal = selectionParameters.itemToString;
1553
- return selectedItem ? itemToStringLocal(selectedItem) + " has been selected." : '';
1552
+ itemToString = selectionParameters.itemToString;
1553
+ return selectedItem ? itemToString(selectedItem) + " has been selected." : '';
1554
1554
  }
1555
1555
 
1556
1556
  /**
@@ -1620,9 +1620,6 @@ function getItemAndIndex(itemProp, indexProp, items, errorMessage) {
1620
1620
  }
1621
1621
  return [item, index];
1622
1622
  }
1623
- function itemToString(item) {
1624
- return item ? String(item) : '';
1625
- }
1626
1623
  function isAcceptedCharacterKey(key) {
1627
1624
  return /^\S{1}$/.test(key);
1628
1625
  }
@@ -1701,7 +1698,12 @@ function useControlledReducer$1(reducer, props, createInitialState, isStateEqual
1701
1698
  return [getState(state, props), dispatch];
1702
1699
  }
1703
1700
  var defaultProps$3 = {
1704
- itemToString: itemToString,
1701
+ itemToString: function itemToString(item) {
1702
+ return item ? String(item) : '';
1703
+ },
1704
+ itemToKey: function itemToKey(item) {
1705
+ return item;
1706
+ },
1705
1707
  stateReducer: stateReducer,
1706
1708
  getA11ySelectionMessage: getA11ySelectionMessage,
1707
1709
  scrollIntoView: scrollIntoView,
@@ -1738,7 +1740,9 @@ function getInitialState$2(props) {
1738
1740
  var highlightedIndex = getInitialValue$1(props, 'highlightedIndex');
1739
1741
  var inputValue = getInitialValue$1(props, 'inputValue');
1740
1742
  return {
1741
- highlightedIndex: highlightedIndex < 0 && selectedItem && isOpen ? props.items.indexOf(selectedItem) : highlightedIndex,
1743
+ highlightedIndex: highlightedIndex < 0 && selectedItem && isOpen ? props.items.findIndex(function (item) {
1744
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
1745
+ }) : highlightedIndex,
1742
1746
  isOpen: isOpen,
1743
1747
  selectedItem: selectedItem,
1744
1748
  inputValue: inputValue
@@ -1747,7 +1751,8 @@ function getInitialState$2(props) {
1747
1751
  function getHighlightedIndexOnOpen(props, state, offset) {
1748
1752
  var items = props.items,
1749
1753
  initialHighlightedIndex = props.initialHighlightedIndex,
1750
- defaultHighlightedIndex = props.defaultHighlightedIndex;
1754
+ defaultHighlightedIndex = props.defaultHighlightedIndex,
1755
+ itemToKey = props.itemToKey;
1751
1756
  var selectedItem = state.selectedItem,
1752
1757
  highlightedIndex = state.highlightedIndex;
1753
1758
  if (items.length === 0) {
@@ -1762,24 +1767,24 @@ function getHighlightedIndexOnOpen(props, state, offset) {
1762
1767
  return defaultHighlightedIndex;
1763
1768
  }
1764
1769
  if (selectedItem) {
1765
- return items.indexOf(selectedItem);
1770
+ return items.findIndex(function (item) {
1771
+ return itemToKey(selectedItem) === itemToKey(item);
1772
+ });
1766
1773
  }
1767
1774
  if (offset === 0) {
1768
1775
  return -1;
1769
1776
  }
1770
1777
  return offset < 0 ? items.length - 1 : 0;
1771
1778
  }
1772
-
1773
1779
  /**
1774
- * Reuse the movement tracking of mouse and touch events.
1780
+ * Tracks mouse and touch events, such as mouseDown, touchMove and touchEnd.
1775
1781
  *
1776
- * @param {boolean} isOpen Whether the dropdown is open or not.
1777
- * @param {Array<Object>} downshiftElementRefs Downshift element refs to track movement (toggleButton, menu etc.)
1778
- * @param {Object} environment Environment where component/hook exists.
1779
- * @param {Function} handleBlur Handler on blur from mouse or touch.
1780
- * @returns {Object} Ref containing whether mouseDown or touchMove event is happening
1782
+ * @param {Object} environment The environment to add the event listeners to, for instance window.
1783
+ * @param {Array<HTMLElement>} downshiftElementRefs The refs for the element that should not trigger a blur action from mouseDown or touchEnd.
1784
+ * @param {Function} handleBlur The function that is called if mouseDown or touchEnd occured outside the downshiftElements.
1785
+ * @returns {Object} The mouse and touch events information, if any of are happening.
1781
1786
  */
1782
- function useMouseAndTouchTracker(isOpen, downshiftElementRefs, environment, handleBlur) {
1787
+ function useMouseAndTouchTracker(environment, downshiftElementRefs, handleBlur) {
1783
1788
  var mouseAndTouchTrackersRef = useRef({
1784
1789
  isMouseDown: false,
1785
1790
  isTouchMove: false,
@@ -1787,45 +1792,39 @@ function useMouseAndTouchTracker(isOpen, downshiftElementRefs, environment, hand
1787
1792
  });
1788
1793
  useEffect(function () {
1789
1794
  if (!environment) {
1790
- return;
1795
+ return noop;
1791
1796
  }
1792
-
1793
- // The same strategy for checking if a click occurred inside or outside downshift
1794
- // as in downshift.js.
1795
- var onMouseDown = function onMouseDown() {
1797
+ var downshiftElements = downshiftElementRefs.map(function (ref) {
1798
+ return ref.current;
1799
+ });
1800
+ function onMouseDown() {
1796
1801
  mouseAndTouchTrackersRef.current.isTouchEnd = false; // reset this one.
1797
1802
  mouseAndTouchTrackersRef.current.isMouseDown = true;
1798
- };
1799
- var onMouseUp = function onMouseUp(event) {
1803
+ }
1804
+ function onMouseUp(event) {
1800
1805
  mouseAndTouchTrackersRef.current.isMouseDown = false;
1801
- if (isOpen && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
1802
- return ref.current;
1803
- }), environment)) {
1806
+ if (!targetWithinDownshift(event.target, downshiftElements, environment)) {
1804
1807
  handleBlur();
1805
1808
  }
1806
- };
1807
- var onTouchStart = function onTouchStart() {
1809
+ }
1810
+ function onTouchStart() {
1808
1811
  mouseAndTouchTrackersRef.current.isTouchEnd = false;
1809
1812
  mouseAndTouchTrackersRef.current.isTouchMove = false;
1810
- };
1811
- var onTouchMove = function onTouchMove() {
1813
+ }
1814
+ function onTouchMove() {
1812
1815
  mouseAndTouchTrackersRef.current.isTouchMove = true;
1813
- };
1814
- var onTouchEnd = function onTouchEnd(event) {
1816
+ }
1817
+ function onTouchEnd(event) {
1815
1818
  mouseAndTouchTrackersRef.current.isTouchEnd = true;
1816
- if (isOpen && !mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
1817
- return ref.current;
1818
- }), environment, false)) {
1819
+ if (!mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, downshiftElements, environment, false)) {
1819
1820
  handleBlur();
1820
1821
  }
1821
- };
1822
+ }
1822
1823
  environment.addEventListener('mousedown', onMouseDown);
1823
1824
  environment.addEventListener('mouseup', onMouseUp);
1824
1825
  environment.addEventListener('touchstart', onTouchStart);
1825
1826
  environment.addEventListener('touchmove', onTouchMove);
1826
1827
  environment.addEventListener('touchend', onTouchEnd);
1827
-
1828
- // eslint-disable-next-line consistent-return
1829
1828
  return function cleanup() {
1830
1829
  environment.removeEventListener('mousedown', onMouseDown);
1831
1830
  environment.removeEventListener('mouseup', onMouseUp);
@@ -1833,9 +1832,9 @@ function useMouseAndTouchTracker(isOpen, downshiftElementRefs, environment, hand
1833
1832
  environment.removeEventListener('touchmove', onTouchMove);
1834
1833
  environment.removeEventListener('touchend', onTouchEnd);
1835
1834
  };
1836
- // eslint-disable-next-line react-hooks/exhaustive-deps
1837
- }, [isOpen, environment]);
1838
- return mouseAndTouchTrackersRef;
1835
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- refs don't change
1836
+ }, [environment, handleBlur]);
1837
+ return mouseAndTouchTrackersRef.current;
1839
1838
  }
1840
1839
 
1841
1840
  /* istanbul ignore next */
@@ -2021,6 +2020,7 @@ var commonPropTypes = {
2021
2020
  Node: PropTypes.func.isRequired
2022
2021
  }),
2023
2022
  itemToString: PropTypes.func,
2023
+ itemToKey: PropTypes.func,
2024
2024
  stateReducer: PropTypes.func
2025
2025
  };
2026
2026
 
@@ -2223,7 +2223,9 @@ function downshiftSelectReducer(state, action) {
2223
2223
  {
2224
2224
  var lowercasedKey = action.key;
2225
2225
  var inputValue = "" + state.inputValue + lowercasedKey;
2226
- var prevHighlightedIndex = !state.isOpen && state.selectedItem ? props.items.indexOf(state.selectedItem) : state.highlightedIndex;
2226
+ var prevHighlightedIndex = !state.isOpen && state.selectedItem ? props.items.findIndex(function (item) {
2227
+ return props.itemToKey(item) === props.itemToKey(state.selectedItem);
2228
+ }) : state.highlightedIndex;
2227
2229
  var highlightedIndex = getItemIndexByCharacterKey({
2228
2230
  keysSoFar: inputValue,
2229
2231
  highlightedIndex: prevHighlightedIndex,
@@ -2425,12 +2427,13 @@ function useSelect(userProps) {
2425
2427
  }
2426
2428
  // eslint-disable-next-line react-hooks/exhaustive-deps
2427
2429
  }, []);
2428
- // Add mouse/touch events to document.
2429
- var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [menuRef, toggleButtonRef], environment, function () {
2430
- dispatch({
2431
- type: ToggleButtonBlur
2432
- });
2433
- });
2430
+ var mouseAndTouchTrackers = useMouseAndTouchTracker(environment, [toggleButtonRef, menuRef], useCallback(function handleBlur() {
2431
+ if (latest.current.state.isOpen) {
2432
+ dispatch({
2433
+ type: ToggleButtonBlur
2434
+ });
2435
+ }
2436
+ }, [dispatch, latest]));
2434
2437
  var setGetterPropCallInfo = useGetterPropsCalledChecker('getMenuProps', 'getToggleButtonProps');
2435
2438
  // Reset itemRefs on close.
2436
2439
  useEffect(function () {
@@ -2616,7 +2619,7 @@ function useSelect(userProps) {
2616
2619
  });
2617
2620
  };
2618
2621
  var toggleButtonHandleBlur = function toggleButtonHandleBlur() {
2619
- if (latestState.isOpen && !mouseAndTouchTrackersRef.current.isMouseDown) {
2622
+ if (latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
2620
2623
  dispatch({
2621
2624
  type: ToggleButtonBlur
2622
2625
  });
@@ -2645,7 +2648,7 @@ function useSelect(userProps) {
2645
2648
  }
2646
2649
  setGetterPropCallInfo('getToggleButtonProps', suppressRefError, refKey, toggleButtonRef);
2647
2650
  return toggleProps;
2648
- }, [latest, elementIds, setGetterPropCallInfo, dispatch, mouseAndTouchTrackersRef, toggleButtonKeyDownHandlers]);
2651
+ }, [dispatch, elementIds, latest, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers]);
2649
2652
  var getItemProps = useCallback(function (_temp6) {
2650
2653
  var _extends4;
2651
2654
  var _ref6 = _temp6 === void 0 ? {} : _temp6,
@@ -2671,7 +2674,7 @@ function useSelect(userProps) {
2671
2674
  index = _getItemAndIndex[1];
2672
2675
  var disabled = latestProps.isItemDisabled(item, index);
2673
2676
  var itemHandleMouseMove = function itemHandleMouseMove() {
2674
- if (mouseAndTouchTrackersRef.current.isTouchEnd || index === latestState.highlightedIndex) {
2677
+ if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
2675
2678
  return;
2676
2679
  }
2677
2680
  shouldScrollRef.current = false;
@@ -2705,7 +2708,7 @@ function useSelect(userProps) {
2705
2708
  itemProps.onMouseMove = callAllEventHandlers(onMouseMove, itemHandleMouseMove);
2706
2709
  itemProps.onMouseDown = callAllEventHandlers(onMouseDown, itemHandleMouseDown);
2707
2710
  return itemProps;
2708
- }, [latest, elementIds, mouseAndTouchTrackersRef, shouldScrollRef, dispatch]);
2711
+ }, [latest, elementIds, mouseAndTouchTrackers, shouldScrollRef, dispatch]);
2709
2712
  return {
2710
2713
  // prop getters.
2711
2714
  getToggleButtonProps: getToggleButtonProps,
@@ -2825,13 +2828,21 @@ function useControlledReducer(reducer, props, createInitialState, isStateEqual)
2825
2828
  if (!isControlledProp(props, 'selectedItem')) {
2826
2829
  return;
2827
2830
  }
2828
- if (!isInitialMount &&
2829
- // on first mount we already have the proper inputValue for a initial selected item.
2830
- props.selectedItemChanged(previousSelectedItemRef.current, props.selectedItem)) {
2831
- dispatch({
2832
- type: ControlledPropUpdatedSelectedItem,
2833
- inputValue: props.itemToString(props.selectedItem)
2834
- });
2831
+ if (!isInitialMount // on first mount we already have the proper inputValue for a initial selected item.
2832
+ ) {
2833
+ var shouldCallDispatch;
2834
+ if (props.selectedItemChanged === undefined) {
2835
+ shouldCallDispatch = props.itemToKey(props.selectedItem) !== props.itemToKey(previousSelectedItemRef.current);
2836
+ } else {
2837
+ console.warn("The \"selectedItemChanged\" is deprecated. Please use \"itemToKey instead\". https://github.com/downshift-js/downshift/blob/master/src/hooks/useCombobox/README.md#selecteditemchanged");
2838
+ shouldCallDispatch = props.selectedItemChanged(previousSelectedItemRef.current, props.selectedItem);
2839
+ }
2840
+ if (shouldCallDispatch) {
2841
+ dispatch({
2842
+ type: ControlledPropUpdatedSelectedItem,
2843
+ inputValue: props.itemToString(props.selectedItem)
2844
+ });
2845
+ }
2835
2846
  }
2836
2847
  previousSelectedItemRef.current = state.selectedItem === previousSelectedItemRef.current ? props.selectedItem : state.selectedItem;
2837
2848
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -2848,9 +2859,6 @@ if (process.env.NODE_ENV !== 'production') {
2848
2859
  };
2849
2860
  }
2850
2861
  var defaultProps$1 = _extends({}, defaultProps$3, {
2851
- selectedItemChanged: function selectedItemChanged(prevItem, item) {
2852
- return prevItem !== item;
2853
- },
2854
2862
  getA11yStatusMessage: getA11yStatusMessage$1,
2855
2863
  isItemDisabled: function isItemDisabled() {
2856
2864
  return false;
@@ -3061,13 +3069,14 @@ function useCombobox(userProps) {
3061
3069
  previousResultCountRef.current = items.length;
3062
3070
  }
3063
3071
  });
3064
- // Add mouse/touch events to document.
3065
- var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [inputRef, menuRef, toggleButtonRef], environment, function () {
3066
- dispatch({
3067
- type: InputBlur,
3068
- selectItem: false
3069
- });
3070
- });
3072
+ var mouseAndTouchTrackers = useMouseAndTouchTracker(environment, [toggleButtonRef, menuRef, inputRef], useCallback(function handleBlur() {
3073
+ if (latest.current.state.isOpen) {
3074
+ dispatch({
3075
+ type: InputBlur,
3076
+ selectItem: false
3077
+ });
3078
+ }
3079
+ }, [dispatch, latest]));
3071
3080
  var setGetterPropCallInfo = useGetterPropsCalledChecker('getInputProps', 'getMenuProps');
3072
3081
  // Reset itemRefs on close.
3073
3082
  useEffect(function () {
@@ -3215,7 +3224,7 @@ function useCombobox(userProps) {
3215
3224
  var onSelectKey = 'onClick';
3216
3225
  var customClickHandler = onClick;
3217
3226
  var itemHandleMouseMove = function itemHandleMouseMove() {
3218
- if (mouseAndTouchTrackersRef.current.isTouchEnd || index === latestState.highlightedIndex) {
3227
+ if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
3219
3228
  return;
3220
3229
  }
3221
3230
  shouldScrollRef.current = false;
@@ -3243,7 +3252,7 @@ function useCombobox(userProps) {
3243
3252
  onMouseMove: callAllEventHandlers(onMouseMove, itemHandleMouseMove),
3244
3253
  onMouseDown: callAllEventHandlers(onMouseDown, itemHandleMouseDown)
3245
3254
  }, rest);
3246
- }, [dispatch, elementIds, latest, mouseAndTouchTrackersRef, shouldScrollRef]);
3255
+ }, [dispatch, elementIds, latest, mouseAndTouchTrackers, shouldScrollRef]);
3247
3256
  var getToggleButtonProps = useCallback(function (_temp4) {
3248
3257
  var _extends4;
3249
3258
  var _ref5 = _temp4 === void 0 ? {} : _temp4,
@@ -3297,7 +3306,7 @@ function useCombobox(userProps) {
3297
3306
  };
3298
3307
  var inputHandleBlur = function inputHandleBlur(event) {
3299
3308
  /* istanbul ignore else */
3300
- if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackersRef.current.isMouseDown) {
3309
+ if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
3301
3310
  var isBlurByTabChange = event.relatedTarget === null && environment.document.activeElement !== environment.document.body;
3302
3311
  dispatch({
3303
3312
  type: InputBlur,
@@ -3321,7 +3330,7 @@ function useCombobox(userProps) {
3321
3330
  return _extends((_extends5 = {}, _extends5[refKey] = handleRefs(ref, function (inputNode) {
3322
3331
  inputRef.current = inputNode;
3323
3332
  }), _extends5['aria-activedescendant'] = latestState.isOpen && latestState.highlightedIndex > -1 ? elementIds.getItemId(latestState.highlightedIndex) : '', _extends5['aria-autocomplete'] = 'list', _extends5['aria-controls'] = elementIds.menuId, _extends5['aria-expanded'] = latestState.isOpen, _extends5['aria-labelledby'] = rest && rest['aria-label'] ? undefined : elementIds.labelId, _extends5.autoComplete = 'off', _extends5.id = elementIds.inputId, _extends5.role = 'combobox', _extends5.value = latestState.inputValue, _extends5), eventHandlers, rest);
3324
- }, [setGetterPropCallInfo, latest, elementIds, inputKeyDownHandlers, dispatch, mouseAndTouchTrackersRef, environment]);
3333
+ }, [dispatch, elementIds, environment, inputKeyDownHandlers, latest, mouseAndTouchTrackers, setGetterPropCallInfo]);
3325
3334
 
3326
3335
  // returns
3327
3336
  var toggleMenu = useCallback(function () {
@@ -3494,6 +3503,7 @@ var propTypes = _extends({}, commonPropTypes, {
3494
3503
  });
3495
3504
  var defaultProps = {
3496
3505
  itemToString: defaultProps$3.itemToString,
3506
+ itemToKey: defaultProps$3.itemToKey,
3497
3507
  stateReducer: defaultProps$3.stateReducer,
3498
3508
  environment: defaultProps$3.environment,
3499
3509
  getA11yRemovalMessage: getA11yRemovalMessage,
@@ -3608,7 +3618,9 @@ function downshiftMultipleSelectionReducer(state, action) {
3608
3618
  case FunctionRemoveSelectedItem:
3609
3619
  {
3610
3620
  var _newActiveIndex = activeIndex;
3611
- var selectedItemIndex = selectedItems.indexOf(selectedItem);
3621
+ var selectedItemIndex = selectedItems.findIndex(function (item) {
3622
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
3623
+ });
3612
3624
  if (selectedItemIndex < 0) {
3613
3625
  break;
3614
3626
  }
@@ -3692,8 +3704,10 @@ function useMultipleSelection(userProps) {
3692
3704
  return;
3693
3705
  }
3694
3706
  if (selectedItems.length < previousSelectedItemsRef.current.length) {
3695
- var removedSelectedItem = previousSelectedItemsRef.current.find(function (item) {
3696
- return selectedItems.indexOf(item) < 0;
3707
+ var removedSelectedItem = previousSelectedItemsRef.current.find(function (selectedItem) {
3708
+ return selectedItems.findIndex(function (item) {
3709
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
3710
+ }) < 0;
3697
3711
  });
3698
3712
  setStatus(getA11yRemovalMessage({
3699
3713
  itemToString: itemToString,