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.
@@ -1601,8 +1601,8 @@ function stateReducer(s, a) {
1601
1601
  */
1602
1602
  function getA11ySelectionMessage(selectionParameters) {
1603
1603
  var selectedItem = selectionParameters.selectedItem,
1604
- itemToStringLocal = selectionParameters.itemToString;
1605
- return selectedItem ? itemToStringLocal(selectedItem) + " has been selected." : '';
1604
+ itemToString = selectionParameters.itemToString;
1605
+ return selectedItem ? itemToString(selectedItem) + " has been selected." : '';
1606
1606
  }
1607
1607
 
1608
1608
  /**
@@ -1672,9 +1672,6 @@ function getItemAndIndex(itemProp, indexProp, items, errorMessage) {
1672
1672
  }
1673
1673
  return [item, index];
1674
1674
  }
1675
- function itemToString(item) {
1676
- return item ? String(item) : '';
1677
- }
1678
1675
  function isAcceptedCharacterKey(key) {
1679
1676
  return /^\S{1}$/.test(key);
1680
1677
  }
@@ -1753,7 +1750,12 @@ function useControlledReducer$1(reducer, props, createInitialState, isStateEqual
1753
1750
  return [getState(state, props), dispatch];
1754
1751
  }
1755
1752
  var defaultProps$3 = {
1756
- itemToString: itemToString,
1753
+ itemToString: function itemToString(item) {
1754
+ return item ? String(item) : '';
1755
+ },
1756
+ itemToKey: function itemToKey(item) {
1757
+ return item;
1758
+ },
1757
1759
  stateReducer: stateReducer,
1758
1760
  getA11ySelectionMessage: getA11ySelectionMessage,
1759
1761
  scrollIntoView: scrollIntoView,
@@ -1790,7 +1792,9 @@ function getInitialState$2(props) {
1790
1792
  var highlightedIndex = getInitialValue$1(props, 'highlightedIndex');
1791
1793
  var inputValue = getInitialValue$1(props, 'inputValue');
1792
1794
  return {
1793
- highlightedIndex: highlightedIndex < 0 && selectedItem && isOpen ? props.items.indexOf(selectedItem) : highlightedIndex,
1795
+ highlightedIndex: highlightedIndex < 0 && selectedItem && isOpen ? props.items.findIndex(function (item) {
1796
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
1797
+ }) : highlightedIndex,
1794
1798
  isOpen: isOpen,
1795
1799
  selectedItem: selectedItem,
1796
1800
  inputValue: inputValue
@@ -1799,7 +1803,8 @@ function getInitialState$2(props) {
1799
1803
  function getHighlightedIndexOnOpen(props, state, offset) {
1800
1804
  var items = props.items,
1801
1805
  initialHighlightedIndex = props.initialHighlightedIndex,
1802
- defaultHighlightedIndex = props.defaultHighlightedIndex;
1806
+ defaultHighlightedIndex = props.defaultHighlightedIndex,
1807
+ itemToKey = props.itemToKey;
1803
1808
  var selectedItem = state.selectedItem,
1804
1809
  highlightedIndex = state.highlightedIndex;
1805
1810
  if (items.length === 0) {
@@ -1814,24 +1819,24 @@ function getHighlightedIndexOnOpen(props, state, offset) {
1814
1819
  return defaultHighlightedIndex;
1815
1820
  }
1816
1821
  if (selectedItem) {
1817
- return items.indexOf(selectedItem);
1822
+ return items.findIndex(function (item) {
1823
+ return itemToKey(selectedItem) === itemToKey(item);
1824
+ });
1818
1825
  }
1819
1826
  if (offset === 0) {
1820
1827
  return -1;
1821
1828
  }
1822
1829
  return offset < 0 ? items.length - 1 : 0;
1823
1830
  }
1824
-
1825
1831
  /**
1826
- * Reuse the movement tracking of mouse and touch events.
1832
+ * Tracks mouse and touch events, such as mouseDown, touchMove and touchEnd.
1827
1833
  *
1828
- * @param {boolean} isOpen Whether the dropdown is open or not.
1829
- * @param {Array<Object>} downshiftElementRefs Downshift element refs to track movement (toggleButton, menu etc.)
1830
- * @param {Object} environment Environment where component/hook exists.
1831
- * @param {Function} handleBlur Handler on blur from mouse or touch.
1832
- * @returns {Object} Ref containing whether mouseDown or touchMove event is happening
1834
+ * @param {Object} environment The environment to add the event listeners to, for instance window.
1835
+ * @param {Array<HTMLElement>} downshiftElementRefs The refs for the element that should not trigger a blur action from mouseDown or touchEnd.
1836
+ * @param {Function} handleBlur The function that is called if mouseDown or touchEnd occured outside the downshiftElements.
1837
+ * @returns {Object} The mouse and touch events information, if any of are happening.
1833
1838
  */
1834
- function useMouseAndTouchTracker(isOpen, downshiftElementRefs, environment, handleBlur) {
1839
+ function useMouseAndTouchTracker(environment, downshiftElementRefs, handleBlur) {
1835
1840
  var mouseAndTouchTrackersRef = React.useRef({
1836
1841
  isMouseDown: false,
1837
1842
  isTouchMove: false,
@@ -1839,45 +1844,39 @@ function useMouseAndTouchTracker(isOpen, downshiftElementRefs, environment, hand
1839
1844
  });
1840
1845
  React.useEffect(function () {
1841
1846
  if (!environment) {
1842
- return;
1847
+ return noop;
1843
1848
  }
1844
-
1845
- // The same strategy for checking if a click occurred inside or outside downshift
1846
- // as in downshift.js.
1847
- var onMouseDown = function onMouseDown() {
1849
+ var downshiftElements = downshiftElementRefs.map(function (ref) {
1850
+ return ref.current;
1851
+ });
1852
+ function onMouseDown() {
1848
1853
  mouseAndTouchTrackersRef.current.isTouchEnd = false; // reset this one.
1849
1854
  mouseAndTouchTrackersRef.current.isMouseDown = true;
1850
- };
1851
- var onMouseUp = function onMouseUp(event) {
1855
+ }
1856
+ function onMouseUp(event) {
1852
1857
  mouseAndTouchTrackersRef.current.isMouseDown = false;
1853
- if (isOpen && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
1854
- return ref.current;
1855
- }), environment)) {
1858
+ if (!targetWithinDownshift(event.target, downshiftElements, environment)) {
1856
1859
  handleBlur();
1857
1860
  }
1858
- };
1859
- var onTouchStart = function onTouchStart() {
1861
+ }
1862
+ function onTouchStart() {
1860
1863
  mouseAndTouchTrackersRef.current.isTouchEnd = false;
1861
1864
  mouseAndTouchTrackersRef.current.isTouchMove = false;
1862
- };
1863
- var onTouchMove = function onTouchMove() {
1865
+ }
1866
+ function onTouchMove() {
1864
1867
  mouseAndTouchTrackersRef.current.isTouchMove = true;
1865
- };
1866
- var onTouchEnd = function onTouchEnd(event) {
1868
+ }
1869
+ function onTouchEnd(event) {
1867
1870
  mouseAndTouchTrackersRef.current.isTouchEnd = true;
1868
- if (isOpen && !mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
1869
- return ref.current;
1870
- }), environment, false)) {
1871
+ if (!mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, downshiftElements, environment, false)) {
1871
1872
  handleBlur();
1872
1873
  }
1873
- };
1874
+ }
1874
1875
  environment.addEventListener('mousedown', onMouseDown);
1875
1876
  environment.addEventListener('mouseup', onMouseUp);
1876
1877
  environment.addEventListener('touchstart', onTouchStart);
1877
1878
  environment.addEventListener('touchmove', onTouchMove);
1878
1879
  environment.addEventListener('touchend', onTouchEnd);
1879
-
1880
- // eslint-disable-next-line consistent-return
1881
1880
  return function cleanup() {
1882
1881
  environment.removeEventListener('mousedown', onMouseDown);
1883
1882
  environment.removeEventListener('mouseup', onMouseUp);
@@ -1885,9 +1884,9 @@ function useMouseAndTouchTracker(isOpen, downshiftElementRefs, environment, hand
1885
1884
  environment.removeEventListener('touchmove', onTouchMove);
1886
1885
  environment.removeEventListener('touchend', onTouchEnd);
1887
1886
  };
1888
- // eslint-disable-next-line react-hooks/exhaustive-deps
1889
- }, [isOpen, environment]);
1890
- return mouseAndTouchTrackersRef;
1887
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- refs don't change
1888
+ }, [environment, handleBlur]);
1889
+ return mouseAndTouchTrackersRef.current;
1891
1890
  }
1892
1891
 
1893
1892
  /* istanbul ignore next */
@@ -2073,6 +2072,7 @@ var commonPropTypes = {
2073
2072
  Node: PropTypes__default["default"].func.isRequired
2074
2073
  }),
2075
2074
  itemToString: PropTypes__default["default"].func,
2075
+ itemToKey: PropTypes__default["default"].func,
2076
2076
  stateReducer: PropTypes__default["default"].func
2077
2077
  };
2078
2078
 
@@ -2275,7 +2275,9 @@ function downshiftSelectReducer(state, action) {
2275
2275
  {
2276
2276
  var lowercasedKey = action.key;
2277
2277
  var inputValue = "" + state.inputValue + lowercasedKey;
2278
- var prevHighlightedIndex = !state.isOpen && state.selectedItem ? props.items.indexOf(state.selectedItem) : state.highlightedIndex;
2278
+ var prevHighlightedIndex = !state.isOpen && state.selectedItem ? props.items.findIndex(function (item) {
2279
+ return props.itemToKey(item) === props.itemToKey(state.selectedItem);
2280
+ }) : state.highlightedIndex;
2279
2281
  var highlightedIndex = getItemIndexByCharacterKey({
2280
2282
  keysSoFar: inputValue,
2281
2283
  highlightedIndex: prevHighlightedIndex,
@@ -2477,12 +2479,13 @@ function useSelect(userProps) {
2477
2479
  }
2478
2480
  // eslint-disable-next-line react-hooks/exhaustive-deps
2479
2481
  }, []);
2480
- // Add mouse/touch events to document.
2481
- var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [menuRef, toggleButtonRef], environment, function () {
2482
- dispatch({
2483
- type: ToggleButtonBlur
2484
- });
2485
- });
2482
+ var mouseAndTouchTrackers = useMouseAndTouchTracker(environment, [toggleButtonRef, menuRef], React.useCallback(function handleBlur() {
2483
+ if (latest.current.state.isOpen) {
2484
+ dispatch({
2485
+ type: ToggleButtonBlur
2486
+ });
2487
+ }
2488
+ }, [dispatch, latest]));
2486
2489
  var setGetterPropCallInfo = useGetterPropsCalledChecker('getMenuProps', 'getToggleButtonProps');
2487
2490
  // Reset itemRefs on close.
2488
2491
  React.useEffect(function () {
@@ -2668,7 +2671,7 @@ function useSelect(userProps) {
2668
2671
  });
2669
2672
  };
2670
2673
  var toggleButtonHandleBlur = function toggleButtonHandleBlur() {
2671
- if (latestState.isOpen && !mouseAndTouchTrackersRef.current.isMouseDown) {
2674
+ if (latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
2672
2675
  dispatch({
2673
2676
  type: ToggleButtonBlur
2674
2677
  });
@@ -2697,7 +2700,7 @@ function useSelect(userProps) {
2697
2700
  }
2698
2701
  setGetterPropCallInfo('getToggleButtonProps', suppressRefError, refKey, toggleButtonRef);
2699
2702
  return toggleProps;
2700
- }, [latest, elementIds, setGetterPropCallInfo, dispatch, mouseAndTouchTrackersRef, toggleButtonKeyDownHandlers]);
2703
+ }, [dispatch, elementIds, latest, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers]);
2701
2704
  var getItemProps = React.useCallback(function (_temp6) {
2702
2705
  var _extends4;
2703
2706
  var _ref6 = _temp6 === void 0 ? {} : _temp6,
@@ -2723,7 +2726,7 @@ function useSelect(userProps) {
2723
2726
  index = _getItemAndIndex[1];
2724
2727
  var disabled = latestProps.isItemDisabled(item, index);
2725
2728
  var itemHandleMouseMove = function itemHandleMouseMove() {
2726
- if (mouseAndTouchTrackersRef.current.isTouchEnd || index === latestState.highlightedIndex) {
2729
+ if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
2727
2730
  return;
2728
2731
  }
2729
2732
  shouldScrollRef.current = false;
@@ -2757,7 +2760,7 @@ function useSelect(userProps) {
2757
2760
  itemProps.onMouseMove = callAllEventHandlers(onMouseMove, itemHandleMouseMove);
2758
2761
  itemProps.onMouseDown = callAllEventHandlers(onMouseDown, itemHandleMouseDown);
2759
2762
  return itemProps;
2760
- }, [latest, elementIds, mouseAndTouchTrackersRef, shouldScrollRef, dispatch]);
2763
+ }, [latest, elementIds, mouseAndTouchTrackers, shouldScrollRef, dispatch]);
2761
2764
  return {
2762
2765
  // prop getters.
2763
2766
  getToggleButtonProps: getToggleButtonProps,
@@ -2877,13 +2880,21 @@ function useControlledReducer(reducer, props, createInitialState, isStateEqual)
2877
2880
  if (!isControlledProp(props, 'selectedItem')) {
2878
2881
  return;
2879
2882
  }
2880
- if (!isInitialMount &&
2881
- // on first mount we already have the proper inputValue for a initial selected item.
2882
- props.selectedItemChanged(previousSelectedItemRef.current, props.selectedItem)) {
2883
- dispatch({
2884
- type: ControlledPropUpdatedSelectedItem,
2885
- inputValue: props.itemToString(props.selectedItem)
2886
- });
2883
+ if (!isInitialMount // on first mount we already have the proper inputValue for a initial selected item.
2884
+ ) {
2885
+ var shouldCallDispatch;
2886
+ if (props.selectedItemChanged === undefined) {
2887
+ shouldCallDispatch = props.itemToKey(props.selectedItem) !== props.itemToKey(previousSelectedItemRef.current);
2888
+ } else {
2889
+ console.warn("The \"selectedItemChanged\" is deprecated. Please use \"itemToKey instead\". https://github.com/downshift-js/downshift/blob/master/src/hooks/useCombobox/README.md#selecteditemchanged");
2890
+ shouldCallDispatch = props.selectedItemChanged(previousSelectedItemRef.current, props.selectedItem);
2891
+ }
2892
+ if (shouldCallDispatch) {
2893
+ dispatch({
2894
+ type: ControlledPropUpdatedSelectedItem,
2895
+ inputValue: props.itemToString(props.selectedItem)
2896
+ });
2897
+ }
2887
2898
  }
2888
2899
  previousSelectedItemRef.current = state.selectedItem === previousSelectedItemRef.current ? props.selectedItem : state.selectedItem;
2889
2900
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -2900,9 +2911,6 @@ if (process.env.NODE_ENV !== 'production') {
2900
2911
  };
2901
2912
  }
2902
2913
  var defaultProps$1 = _extends__default["default"]({}, defaultProps$3, {
2903
- selectedItemChanged: function selectedItemChanged(prevItem, item) {
2904
- return prevItem !== item;
2905
- },
2906
2914
  getA11yStatusMessage: getA11yStatusMessage$1,
2907
2915
  isItemDisabled: function isItemDisabled() {
2908
2916
  return false;
@@ -3113,13 +3121,14 @@ function useCombobox(userProps) {
3113
3121
  previousResultCountRef.current = items.length;
3114
3122
  }
3115
3123
  });
3116
- // Add mouse/touch events to document.
3117
- var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [inputRef, menuRef, toggleButtonRef], environment, function () {
3118
- dispatch({
3119
- type: InputBlur,
3120
- selectItem: false
3121
- });
3122
- });
3124
+ var mouseAndTouchTrackers = useMouseAndTouchTracker(environment, [toggleButtonRef, menuRef, inputRef], React.useCallback(function handleBlur() {
3125
+ if (latest.current.state.isOpen) {
3126
+ dispatch({
3127
+ type: InputBlur,
3128
+ selectItem: false
3129
+ });
3130
+ }
3131
+ }, [dispatch, latest]));
3123
3132
  var setGetterPropCallInfo = useGetterPropsCalledChecker('getInputProps', 'getMenuProps');
3124
3133
  // Reset itemRefs on close.
3125
3134
  React.useEffect(function () {
@@ -3267,7 +3276,7 @@ function useCombobox(userProps) {
3267
3276
  var onSelectKey = 'onClick';
3268
3277
  var customClickHandler = onClick;
3269
3278
  var itemHandleMouseMove = function itemHandleMouseMove() {
3270
- if (mouseAndTouchTrackersRef.current.isTouchEnd || index === latestState.highlightedIndex) {
3279
+ if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
3271
3280
  return;
3272
3281
  }
3273
3282
  shouldScrollRef.current = false;
@@ -3295,7 +3304,7 @@ function useCombobox(userProps) {
3295
3304
  onMouseMove: callAllEventHandlers(onMouseMove, itemHandleMouseMove),
3296
3305
  onMouseDown: callAllEventHandlers(onMouseDown, itemHandleMouseDown)
3297
3306
  }, rest);
3298
- }, [dispatch, elementIds, latest, mouseAndTouchTrackersRef, shouldScrollRef]);
3307
+ }, [dispatch, elementIds, latest, mouseAndTouchTrackers, shouldScrollRef]);
3299
3308
  var getToggleButtonProps = React.useCallback(function (_temp4) {
3300
3309
  var _extends4;
3301
3310
  var _ref5 = _temp4 === void 0 ? {} : _temp4,
@@ -3349,7 +3358,7 @@ function useCombobox(userProps) {
3349
3358
  };
3350
3359
  var inputHandleBlur = function inputHandleBlur(event) {
3351
3360
  /* istanbul ignore else */
3352
- if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackersRef.current.isMouseDown) {
3361
+ if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
3353
3362
  var isBlurByTabChange = event.relatedTarget === null && environment.document.activeElement !== environment.document.body;
3354
3363
  dispatch({
3355
3364
  type: InputBlur,
@@ -3373,7 +3382,7 @@ function useCombobox(userProps) {
3373
3382
  return _extends__default["default"]((_extends5 = {}, _extends5[refKey] = handleRefs(ref, function (inputNode) {
3374
3383
  inputRef.current = inputNode;
3375
3384
  }), _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);
3376
- }, [setGetterPropCallInfo, latest, elementIds, inputKeyDownHandlers, dispatch, mouseAndTouchTrackersRef, environment]);
3385
+ }, [dispatch, elementIds, environment, inputKeyDownHandlers, latest, mouseAndTouchTrackers, setGetterPropCallInfo]);
3377
3386
 
3378
3387
  // returns
3379
3388
  var toggleMenu = React.useCallback(function () {
@@ -3546,6 +3555,7 @@ var propTypes = _extends__default["default"]({}, commonPropTypes, {
3546
3555
  });
3547
3556
  var defaultProps = {
3548
3557
  itemToString: defaultProps$3.itemToString,
3558
+ itemToKey: defaultProps$3.itemToKey,
3549
3559
  stateReducer: defaultProps$3.stateReducer,
3550
3560
  environment: defaultProps$3.environment,
3551
3561
  getA11yRemovalMessage: getA11yRemovalMessage,
@@ -3660,7 +3670,9 @@ function downshiftMultipleSelectionReducer(state, action) {
3660
3670
  case FunctionRemoveSelectedItem:
3661
3671
  {
3662
3672
  var _newActiveIndex = activeIndex;
3663
- var selectedItemIndex = selectedItems.indexOf(selectedItem);
3673
+ var selectedItemIndex = selectedItems.findIndex(function (item) {
3674
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
3675
+ });
3664
3676
  if (selectedItemIndex < 0) {
3665
3677
  break;
3666
3678
  }
@@ -3744,8 +3756,10 @@ function useMultipleSelection(userProps) {
3744
3756
  return;
3745
3757
  }
3746
3758
  if (selectedItems.length < previousSelectedItemsRef.current.length) {
3747
- var removedSelectedItem = previousSelectedItemsRef.current.find(function (item) {
3748
- return selectedItems.indexOf(item) < 0;
3759
+ var removedSelectedItem = previousSelectedItemsRef.current.find(function (selectedItem) {
3760
+ return selectedItems.findIndex(function (item) {
3761
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
3762
+ }) < 0;
3749
3763
  });
3750
3764
  setStatus(getA11yRemovalMessage({
3751
3765
  itemToString: itemToString,