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.
@@ -1831,8 +1831,8 @@
1831
1831
  */
1832
1832
  function getA11ySelectionMessage(selectionParameters) {
1833
1833
  var selectedItem = selectionParameters.selectedItem,
1834
- itemToStringLocal = selectionParameters.itemToString;
1835
- return selectedItem ? itemToStringLocal(selectedItem) + " has been selected." : '';
1834
+ itemToString = selectionParameters.itemToString;
1835
+ return selectedItem ? itemToString(selectedItem) + " has been selected." : '';
1836
1836
  }
1837
1837
 
1838
1838
  /**
@@ -1902,9 +1902,6 @@
1902
1902
  }
1903
1903
  return [item, index];
1904
1904
  }
1905
- function itemToString(item) {
1906
- return item ? String(item) : '';
1907
- }
1908
1905
  function isAcceptedCharacterKey(key) {
1909
1906
  return /^\S{1}$/.test(key);
1910
1907
  }
@@ -1983,7 +1980,12 @@
1983
1980
  return [getState(state, props), dispatch];
1984
1981
  }
1985
1982
  var defaultProps$3 = {
1986
- itemToString: itemToString,
1983
+ itemToString: function itemToString(item) {
1984
+ return item ? String(item) : '';
1985
+ },
1986
+ itemToKey: function itemToKey(item) {
1987
+ return item;
1988
+ },
1987
1989
  stateReducer: stateReducer,
1988
1990
  getA11ySelectionMessage: getA11ySelectionMessage,
1989
1991
  scrollIntoView: scrollIntoView,
@@ -2020,7 +2022,9 @@
2020
2022
  var highlightedIndex = getInitialValue$1(props, 'highlightedIndex');
2021
2023
  var inputValue = getInitialValue$1(props, 'inputValue');
2022
2024
  return {
2023
- highlightedIndex: highlightedIndex < 0 && selectedItem && isOpen ? props.items.indexOf(selectedItem) : highlightedIndex,
2025
+ highlightedIndex: highlightedIndex < 0 && selectedItem && isOpen ? props.items.findIndex(function (item) {
2026
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
2027
+ }) : highlightedIndex,
2024
2028
  isOpen: isOpen,
2025
2029
  selectedItem: selectedItem,
2026
2030
  inputValue: inputValue
@@ -2029,7 +2033,8 @@
2029
2033
  function getHighlightedIndexOnOpen(props, state, offset) {
2030
2034
  var items = props.items,
2031
2035
  initialHighlightedIndex = props.initialHighlightedIndex,
2032
- defaultHighlightedIndex = props.defaultHighlightedIndex;
2036
+ defaultHighlightedIndex = props.defaultHighlightedIndex,
2037
+ itemToKey = props.itemToKey;
2033
2038
  var selectedItem = state.selectedItem,
2034
2039
  highlightedIndex = state.highlightedIndex;
2035
2040
  if (items.length === 0) {
@@ -2044,24 +2049,24 @@
2044
2049
  return defaultHighlightedIndex;
2045
2050
  }
2046
2051
  if (selectedItem) {
2047
- return items.indexOf(selectedItem);
2052
+ return items.findIndex(function (item) {
2053
+ return itemToKey(selectedItem) === itemToKey(item);
2054
+ });
2048
2055
  }
2049
2056
  if (offset === 0) {
2050
2057
  return -1;
2051
2058
  }
2052
2059
  return offset < 0 ? items.length - 1 : 0;
2053
2060
  }
2054
-
2055
2061
  /**
2056
- * Reuse the movement tracking of mouse and touch events.
2062
+ * Tracks mouse and touch events, such as mouseDown, touchMove and touchEnd.
2057
2063
  *
2058
- * @param {boolean} isOpen Whether the dropdown is open or not.
2059
- * @param {Array<Object>} downshiftElementRefs Downshift element refs to track movement (toggleButton, menu etc.)
2060
- * @param {Object} environment Environment where component/hook exists.
2061
- * @param {Function} handleBlur Handler on blur from mouse or touch.
2062
- * @returns {Object} Ref containing whether mouseDown or touchMove event is happening
2064
+ * @param {Object} environment The environment to add the event listeners to, for instance window.
2065
+ * @param {Array<HTMLElement>} downshiftElementRefs The refs for the element that should not trigger a blur action from mouseDown or touchEnd.
2066
+ * @param {Function} handleBlur The function that is called if mouseDown or touchEnd occured outside the downshiftElements.
2067
+ * @returns {Object} The mouse and touch events information, if any of are happening.
2063
2068
  */
2064
- function useMouseAndTouchTracker(isOpen, downshiftElementRefs, environment, handleBlur) {
2069
+ function useMouseAndTouchTracker(environment, downshiftElementRefs, handleBlur) {
2065
2070
  var mouseAndTouchTrackersRef = React.useRef({
2066
2071
  isMouseDown: false,
2067
2072
  isTouchMove: false,
@@ -2069,45 +2074,39 @@
2069
2074
  });
2070
2075
  React.useEffect(function () {
2071
2076
  if (!environment) {
2072
- return;
2077
+ return noop;
2073
2078
  }
2074
-
2075
- // The same strategy for checking if a click occurred inside or outside downshift
2076
- // as in downshift.js.
2077
- var onMouseDown = function onMouseDown() {
2079
+ var downshiftElements = downshiftElementRefs.map(function (ref) {
2080
+ return ref.current;
2081
+ });
2082
+ function onMouseDown() {
2078
2083
  mouseAndTouchTrackersRef.current.isTouchEnd = false; // reset this one.
2079
2084
  mouseAndTouchTrackersRef.current.isMouseDown = true;
2080
- };
2081
- var onMouseUp = function onMouseUp(event) {
2085
+ }
2086
+ function onMouseUp(event) {
2082
2087
  mouseAndTouchTrackersRef.current.isMouseDown = false;
2083
- if (isOpen && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
2084
- return ref.current;
2085
- }), environment)) {
2088
+ if (!targetWithinDownshift(event.target, downshiftElements, environment)) {
2086
2089
  handleBlur();
2087
2090
  }
2088
- };
2089
- var onTouchStart = function onTouchStart() {
2091
+ }
2092
+ function onTouchStart() {
2090
2093
  mouseAndTouchTrackersRef.current.isTouchEnd = false;
2091
2094
  mouseAndTouchTrackersRef.current.isTouchMove = false;
2092
- };
2093
- var onTouchMove = function onTouchMove() {
2095
+ }
2096
+ function onTouchMove() {
2094
2097
  mouseAndTouchTrackersRef.current.isTouchMove = true;
2095
- };
2096
- var onTouchEnd = function onTouchEnd(event) {
2098
+ }
2099
+ function onTouchEnd(event) {
2097
2100
  mouseAndTouchTrackersRef.current.isTouchEnd = true;
2098
- if (isOpen && !mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
2099
- return ref.current;
2100
- }), environment, false)) {
2101
+ if (!mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, downshiftElements, environment, false)) {
2101
2102
  handleBlur();
2102
2103
  }
2103
- };
2104
+ }
2104
2105
  environment.addEventListener('mousedown', onMouseDown);
2105
2106
  environment.addEventListener('mouseup', onMouseUp);
2106
2107
  environment.addEventListener('touchstart', onTouchStart);
2107
2108
  environment.addEventListener('touchmove', onTouchMove);
2108
2109
  environment.addEventListener('touchend', onTouchEnd);
2109
-
2110
- // eslint-disable-next-line consistent-return
2111
2110
  return function cleanup() {
2112
2111
  environment.removeEventListener('mousedown', onMouseDown);
2113
2112
  environment.removeEventListener('mouseup', onMouseUp);
@@ -2115,9 +2114,9 @@
2115
2114
  environment.removeEventListener('touchmove', onTouchMove);
2116
2115
  environment.removeEventListener('touchend', onTouchEnd);
2117
2116
  };
2118
- // eslint-disable-next-line react-hooks/exhaustive-deps
2119
- }, [isOpen, environment]);
2120
- return mouseAndTouchTrackersRef;
2117
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- refs don't change
2118
+ }, [environment, handleBlur]);
2119
+ return mouseAndTouchTrackersRef.current;
2121
2120
  }
2122
2121
 
2123
2122
  /* istanbul ignore next */
@@ -2303,6 +2302,7 @@
2303
2302
  Node: PropTypes__default["default"].func.isRequired
2304
2303
  }),
2305
2304
  itemToString: PropTypes__default["default"].func,
2305
+ itemToKey: PropTypes__default["default"].func,
2306
2306
  stateReducer: PropTypes__default["default"].func
2307
2307
  };
2308
2308
 
@@ -2536,7 +2536,9 @@
2536
2536
  {
2537
2537
  var lowercasedKey = action.key;
2538
2538
  var inputValue = "" + state.inputValue + lowercasedKey;
2539
- var prevHighlightedIndex = !state.isOpen && state.selectedItem ? props.items.indexOf(state.selectedItem) : state.highlightedIndex;
2539
+ var prevHighlightedIndex = !state.isOpen && state.selectedItem ? props.items.findIndex(function (item) {
2540
+ return props.itemToKey(item) === props.itemToKey(state.selectedItem);
2541
+ }) : state.highlightedIndex;
2540
2542
  var highlightedIndex = getItemIndexByCharacterKey({
2541
2543
  keysSoFar: inputValue,
2542
2544
  highlightedIndex: prevHighlightedIndex,
@@ -2738,12 +2740,13 @@
2738
2740
  }
2739
2741
  // eslint-disable-next-line react-hooks/exhaustive-deps
2740
2742
  }, []);
2741
- // Add mouse/touch events to document.
2742
- var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [menuRef, toggleButtonRef], environment, function () {
2743
- dispatch({
2744
- type: ToggleButtonBlur
2745
- });
2746
- });
2743
+ var mouseAndTouchTrackers = useMouseAndTouchTracker(environment, [toggleButtonRef, menuRef], React.useCallback(function handleBlur() {
2744
+ if (latest.current.state.isOpen) {
2745
+ dispatch({
2746
+ type: ToggleButtonBlur
2747
+ });
2748
+ }
2749
+ }, [dispatch, latest]));
2747
2750
  var setGetterPropCallInfo = useGetterPropsCalledChecker('getMenuProps', 'getToggleButtonProps');
2748
2751
  // Reset itemRefs on close.
2749
2752
  React.useEffect(function () {
@@ -2929,7 +2932,7 @@
2929
2932
  });
2930
2933
  };
2931
2934
  var toggleButtonHandleBlur = function toggleButtonHandleBlur() {
2932
- if (latestState.isOpen && !mouseAndTouchTrackersRef.current.isMouseDown) {
2935
+ if (latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
2933
2936
  dispatch({
2934
2937
  type: ToggleButtonBlur
2935
2938
  });
@@ -2958,7 +2961,7 @@
2958
2961
  }
2959
2962
  setGetterPropCallInfo('getToggleButtonProps', suppressRefError, refKey, toggleButtonRef);
2960
2963
  return toggleProps;
2961
- }, [latest, elementIds, setGetterPropCallInfo, dispatch, mouseAndTouchTrackersRef, toggleButtonKeyDownHandlers]);
2964
+ }, [dispatch, elementIds, latest, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers]);
2962
2965
  var getItemProps = React.useCallback(function (_temp6) {
2963
2966
  var _extends4;
2964
2967
  var _ref6 = _temp6 === void 0 ? {} : _temp6,
@@ -2984,7 +2987,7 @@
2984
2987
  index = _getItemAndIndex[1];
2985
2988
  var disabled = latestProps.isItemDisabled(item, index);
2986
2989
  var itemHandleMouseMove = function itemHandleMouseMove() {
2987
- if (mouseAndTouchTrackersRef.current.isTouchEnd || index === latestState.highlightedIndex) {
2990
+ if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
2988
2991
  return;
2989
2992
  }
2990
2993
  shouldScrollRef.current = false;
@@ -3018,7 +3021,7 @@
3018
3021
  itemProps.onMouseMove = callAllEventHandlers(onMouseMove, itemHandleMouseMove);
3019
3022
  itemProps.onMouseDown = callAllEventHandlers(onMouseDown, itemHandleMouseDown);
3020
3023
  return itemProps;
3021
- }, [latest, elementIds, mouseAndTouchTrackersRef, shouldScrollRef, dispatch]);
3024
+ }, [latest, elementIds, mouseAndTouchTrackers, shouldScrollRef, dispatch]);
3022
3025
  return {
3023
3026
  // prop getters.
3024
3027
  getToggleButtonProps: getToggleButtonProps,
@@ -3138,13 +3141,21 @@
3138
3141
  if (!isControlledProp(props, 'selectedItem')) {
3139
3142
  return;
3140
3143
  }
3141
- if (!isInitialMount &&
3142
- // on first mount we already have the proper inputValue for a initial selected item.
3143
- props.selectedItemChanged(previousSelectedItemRef.current, props.selectedItem)) {
3144
- dispatch({
3145
- type: ControlledPropUpdatedSelectedItem,
3146
- inputValue: props.itemToString(props.selectedItem)
3147
- });
3144
+ if (!isInitialMount // on first mount we already have the proper inputValue for a initial selected item.
3145
+ ) {
3146
+ var shouldCallDispatch;
3147
+ if (props.selectedItemChanged === undefined) {
3148
+ shouldCallDispatch = props.itemToKey(props.selectedItem) !== props.itemToKey(previousSelectedItemRef.current);
3149
+ } else {
3150
+ console.warn("The \"selectedItemChanged\" is deprecated. Please use \"itemToKey instead\". https://github.com/downshift-js/downshift/blob/master/src/hooks/useCombobox/README.md#selecteditemchanged");
3151
+ shouldCallDispatch = props.selectedItemChanged(previousSelectedItemRef.current, props.selectedItem);
3152
+ }
3153
+ if (shouldCallDispatch) {
3154
+ dispatch({
3155
+ type: ControlledPropUpdatedSelectedItem,
3156
+ inputValue: props.itemToString(props.selectedItem)
3157
+ });
3158
+ }
3148
3159
  }
3149
3160
  previousSelectedItemRef.current = state.selectedItem === previousSelectedItemRef.current ? props.selectedItem : state.selectedItem;
3150
3161
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -3161,9 +3172,6 @@
3161
3172
  };
3162
3173
  }
3163
3174
  var defaultProps$1 = _extends({}, defaultProps$3, {
3164
- selectedItemChanged: function selectedItemChanged(prevItem, item) {
3165
- return prevItem !== item;
3166
- },
3167
3175
  getA11yStatusMessage: getA11yStatusMessage$1,
3168
3176
  isItemDisabled: function isItemDisabled() {
3169
3177
  return false;
@@ -3374,13 +3382,14 @@
3374
3382
  previousResultCountRef.current = items.length;
3375
3383
  }
3376
3384
  });
3377
- // Add mouse/touch events to document.
3378
- var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [inputRef, menuRef, toggleButtonRef], environment, function () {
3379
- dispatch({
3380
- type: InputBlur,
3381
- selectItem: false
3382
- });
3383
- });
3385
+ var mouseAndTouchTrackers = useMouseAndTouchTracker(environment, [toggleButtonRef, menuRef, inputRef], React.useCallback(function handleBlur() {
3386
+ if (latest.current.state.isOpen) {
3387
+ dispatch({
3388
+ type: InputBlur,
3389
+ selectItem: false
3390
+ });
3391
+ }
3392
+ }, [dispatch, latest]));
3384
3393
  var setGetterPropCallInfo = useGetterPropsCalledChecker('getInputProps', 'getMenuProps');
3385
3394
  // Reset itemRefs on close.
3386
3395
  React.useEffect(function () {
@@ -3528,7 +3537,7 @@
3528
3537
  var onSelectKey = 'onClick';
3529
3538
  var customClickHandler = onClick;
3530
3539
  var itemHandleMouseMove = function itemHandleMouseMove() {
3531
- if (mouseAndTouchTrackersRef.current.isTouchEnd || index === latestState.highlightedIndex) {
3540
+ if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
3532
3541
  return;
3533
3542
  }
3534
3543
  shouldScrollRef.current = false;
@@ -3556,7 +3565,7 @@
3556
3565
  onMouseMove: callAllEventHandlers(onMouseMove, itemHandleMouseMove),
3557
3566
  onMouseDown: callAllEventHandlers(onMouseDown, itemHandleMouseDown)
3558
3567
  }, rest);
3559
- }, [dispatch, elementIds, latest, mouseAndTouchTrackersRef, shouldScrollRef]);
3568
+ }, [dispatch, elementIds, latest, mouseAndTouchTrackers, shouldScrollRef]);
3560
3569
  var getToggleButtonProps = React.useCallback(function (_temp4) {
3561
3570
  var _extends4;
3562
3571
  var _ref5 = _temp4 === void 0 ? {} : _temp4,
@@ -3610,7 +3619,7 @@
3610
3619
  };
3611
3620
  var inputHandleBlur = function inputHandleBlur(event) {
3612
3621
  /* istanbul ignore else */
3613
- if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackersRef.current.isMouseDown) {
3622
+ if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
3614
3623
  var isBlurByTabChange = event.relatedTarget === null && environment.document.activeElement !== environment.document.body;
3615
3624
  dispatch({
3616
3625
  type: InputBlur,
@@ -3634,7 +3643,7 @@
3634
3643
  return _extends((_extends5 = {}, _extends5[refKey] = handleRefs(ref, function (inputNode) {
3635
3644
  inputRef.current = inputNode;
3636
3645
  }), _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);
3637
- }, [setGetterPropCallInfo, latest, elementIds, inputKeyDownHandlers, dispatch, mouseAndTouchTrackersRef, environment]);
3646
+ }, [dispatch, elementIds, environment, inputKeyDownHandlers, latest, mouseAndTouchTrackers, setGetterPropCallInfo]);
3638
3647
 
3639
3648
  // returns
3640
3649
  var toggleMenu = React.useCallback(function () {
@@ -3807,6 +3816,7 @@
3807
3816
  });
3808
3817
  var defaultProps = {
3809
3818
  itemToString: defaultProps$3.itemToString,
3819
+ itemToKey: defaultProps$3.itemToKey,
3810
3820
  stateReducer: defaultProps$3.stateReducer,
3811
3821
  environment: defaultProps$3.environment,
3812
3822
  getA11yRemovalMessage: getA11yRemovalMessage,
@@ -3921,7 +3931,9 @@
3921
3931
  case FunctionRemoveSelectedItem:
3922
3932
  {
3923
3933
  var _newActiveIndex = activeIndex;
3924
- var selectedItemIndex = selectedItems.indexOf(selectedItem);
3934
+ var selectedItemIndex = selectedItems.findIndex(function (item) {
3935
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
3936
+ });
3925
3937
  if (selectedItemIndex < 0) {
3926
3938
  break;
3927
3939
  }
@@ -4005,8 +4017,10 @@
4005
4017
  return;
4006
4018
  }
4007
4019
  if (selectedItems.length < previousSelectedItemsRef.current.length) {
4008
- var removedSelectedItem = previousSelectedItemsRef.current.find(function (item) {
4009
- return selectedItems.indexOf(item) < 0;
4020
+ var removedSelectedItem = previousSelectedItemsRef.current.find(function (selectedItem) {
4021
+ return selectedItems.findIndex(function (item) {
4022
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
4023
+ }) < 0;
4010
4024
  });
4011
4025
  setStatus(getA11yRemovalMessage({
4012
4026
  itemToString: itemToString,