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.
@@ -2954,8 +2954,8 @@
2954
2954
  */
2955
2955
  function getA11ySelectionMessage(selectionParameters) {
2956
2956
  var selectedItem = selectionParameters.selectedItem,
2957
- itemToStringLocal = selectionParameters.itemToString;
2958
- return selectedItem ? itemToStringLocal(selectedItem) + " has been selected." : '';
2957
+ itemToString = selectionParameters.itemToString;
2958
+ return selectedItem ? itemToString(selectedItem) + " has been selected." : '';
2959
2959
  }
2960
2960
 
2961
2961
  /**
@@ -3025,9 +3025,6 @@
3025
3025
  }
3026
3026
  return [item, index];
3027
3027
  }
3028
- function itemToString(item) {
3029
- return item ? String(item) : '';
3030
- }
3031
3028
  function isAcceptedCharacterKey(key) {
3032
3029
  return /^\S{1}$/.test(key);
3033
3030
  }
@@ -3106,7 +3103,12 @@
3106
3103
  return [getState(state, props), dispatch];
3107
3104
  }
3108
3105
  var defaultProps$3 = {
3109
- itemToString: itemToString,
3106
+ itemToString: function itemToString(item) {
3107
+ return item ? String(item) : '';
3108
+ },
3109
+ itemToKey: function itemToKey(item) {
3110
+ return item;
3111
+ },
3110
3112
  stateReducer: stateReducer,
3111
3113
  getA11ySelectionMessage: getA11ySelectionMessage,
3112
3114
  scrollIntoView: scrollIntoView,
@@ -3143,7 +3145,9 @@
3143
3145
  var highlightedIndex = getInitialValue$1(props, 'highlightedIndex');
3144
3146
  var inputValue = getInitialValue$1(props, 'inputValue');
3145
3147
  return {
3146
- highlightedIndex: highlightedIndex < 0 && selectedItem && isOpen ? props.items.indexOf(selectedItem) : highlightedIndex,
3148
+ highlightedIndex: highlightedIndex < 0 && selectedItem && isOpen ? props.items.findIndex(function (item) {
3149
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
3150
+ }) : highlightedIndex,
3147
3151
  isOpen: isOpen,
3148
3152
  selectedItem: selectedItem,
3149
3153
  inputValue: inputValue
@@ -3152,7 +3156,8 @@
3152
3156
  function getHighlightedIndexOnOpen(props, state, offset) {
3153
3157
  var items = props.items,
3154
3158
  initialHighlightedIndex = props.initialHighlightedIndex,
3155
- defaultHighlightedIndex = props.defaultHighlightedIndex;
3159
+ defaultHighlightedIndex = props.defaultHighlightedIndex,
3160
+ itemToKey = props.itemToKey;
3156
3161
  var selectedItem = state.selectedItem,
3157
3162
  highlightedIndex = state.highlightedIndex;
3158
3163
  if (items.length === 0) {
@@ -3167,24 +3172,24 @@
3167
3172
  return defaultHighlightedIndex;
3168
3173
  }
3169
3174
  if (selectedItem) {
3170
- return items.indexOf(selectedItem);
3175
+ return items.findIndex(function (item) {
3176
+ return itemToKey(selectedItem) === itemToKey(item);
3177
+ });
3171
3178
  }
3172
3179
  if (offset === 0) {
3173
3180
  return -1;
3174
3181
  }
3175
3182
  return offset < 0 ? items.length - 1 : 0;
3176
3183
  }
3177
-
3178
3184
  /**
3179
- * Reuse the movement tracking of mouse and touch events.
3185
+ * Tracks mouse and touch events, such as mouseDown, touchMove and touchEnd.
3180
3186
  *
3181
- * @param {boolean} isOpen Whether the dropdown is open or not.
3182
- * @param {Array<Object>} downshiftElementRefs Downshift element refs to track movement (toggleButton, menu etc.)
3183
- * @param {Object} environment Environment where component/hook exists.
3184
- * @param {Function} handleBlur Handler on blur from mouse or touch.
3185
- * @returns {Object} Ref containing whether mouseDown or touchMove event is happening
3187
+ * @param {Object} environment The environment to add the event listeners to, for instance window.
3188
+ * @param {Array<HTMLElement>} downshiftElementRefs The refs for the element that should not trigger a blur action from mouseDown or touchEnd.
3189
+ * @param {Function} handleBlur The function that is called if mouseDown or touchEnd occured outside the downshiftElements.
3190
+ * @returns {Object} The mouse and touch events information, if any of are happening.
3186
3191
  */
3187
- function useMouseAndTouchTracker(isOpen, downshiftElementRefs, environment, handleBlur) {
3192
+ function useMouseAndTouchTracker(environment, downshiftElementRefs, handleBlur) {
3188
3193
  var mouseAndTouchTrackersRef = React.useRef({
3189
3194
  isMouseDown: false,
3190
3195
  isTouchMove: false,
@@ -3192,45 +3197,39 @@
3192
3197
  });
3193
3198
  React.useEffect(function () {
3194
3199
  if (!environment) {
3195
- return;
3200
+ return noop;
3196
3201
  }
3197
-
3198
- // The same strategy for checking if a click occurred inside or outside downshift
3199
- // as in downshift.js.
3200
- var onMouseDown = function onMouseDown() {
3202
+ var downshiftElements = downshiftElementRefs.map(function (ref) {
3203
+ return ref.current;
3204
+ });
3205
+ function onMouseDown() {
3201
3206
  mouseAndTouchTrackersRef.current.isTouchEnd = false; // reset this one.
3202
3207
  mouseAndTouchTrackersRef.current.isMouseDown = true;
3203
- };
3204
- var onMouseUp = function onMouseUp(event) {
3208
+ }
3209
+ function onMouseUp(event) {
3205
3210
  mouseAndTouchTrackersRef.current.isMouseDown = false;
3206
- if (isOpen && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
3207
- return ref.current;
3208
- }), environment)) {
3211
+ if (!targetWithinDownshift(event.target, downshiftElements, environment)) {
3209
3212
  handleBlur();
3210
3213
  }
3211
- };
3212
- var onTouchStart = function onTouchStart() {
3214
+ }
3215
+ function onTouchStart() {
3213
3216
  mouseAndTouchTrackersRef.current.isTouchEnd = false;
3214
3217
  mouseAndTouchTrackersRef.current.isTouchMove = false;
3215
- };
3216
- var onTouchMove = function onTouchMove() {
3218
+ }
3219
+ function onTouchMove() {
3217
3220
  mouseAndTouchTrackersRef.current.isTouchMove = true;
3218
- };
3219
- var onTouchEnd = function onTouchEnd(event) {
3221
+ }
3222
+ function onTouchEnd(event) {
3220
3223
  mouseAndTouchTrackersRef.current.isTouchEnd = true;
3221
- if (isOpen && !mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
3222
- return ref.current;
3223
- }), environment, false)) {
3224
+ if (!mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, downshiftElements, environment, false)) {
3224
3225
  handleBlur();
3225
3226
  }
3226
- };
3227
+ }
3227
3228
  environment.addEventListener('mousedown', onMouseDown);
3228
3229
  environment.addEventListener('mouseup', onMouseUp);
3229
3230
  environment.addEventListener('touchstart', onTouchStart);
3230
3231
  environment.addEventListener('touchmove', onTouchMove);
3231
3232
  environment.addEventListener('touchend', onTouchEnd);
3232
-
3233
- // eslint-disable-next-line consistent-return
3234
3233
  return function cleanup() {
3235
3234
  environment.removeEventListener('mousedown', onMouseDown);
3236
3235
  environment.removeEventListener('mouseup', onMouseUp);
@@ -3238,9 +3237,9 @@
3238
3237
  environment.removeEventListener('touchmove', onTouchMove);
3239
3238
  environment.removeEventListener('touchend', onTouchEnd);
3240
3239
  };
3241
- // eslint-disable-next-line react-hooks/exhaustive-deps
3242
- }, [isOpen, environment]);
3243
- return mouseAndTouchTrackersRef;
3240
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- refs don't change
3241
+ }, [environment, handleBlur]);
3242
+ return mouseAndTouchTrackersRef.current;
3244
3243
  }
3245
3244
 
3246
3245
  /* istanbul ignore next */
@@ -3426,6 +3425,7 @@
3426
3425
  Node: PropTypes.func.isRequired
3427
3426
  }),
3428
3427
  itemToString: PropTypes.func,
3428
+ itemToKey: PropTypes.func,
3429
3429
  stateReducer: PropTypes.func
3430
3430
  };
3431
3431
 
@@ -3659,7 +3659,9 @@
3659
3659
  {
3660
3660
  var lowercasedKey = action.key;
3661
3661
  var inputValue = "" + state.inputValue + lowercasedKey;
3662
- var prevHighlightedIndex = !state.isOpen && state.selectedItem ? props.items.indexOf(state.selectedItem) : state.highlightedIndex;
3662
+ var prevHighlightedIndex = !state.isOpen && state.selectedItem ? props.items.findIndex(function (item) {
3663
+ return props.itemToKey(item) === props.itemToKey(state.selectedItem);
3664
+ }) : state.highlightedIndex;
3663
3665
  var highlightedIndex = getItemIndexByCharacterKey({
3664
3666
  keysSoFar: inputValue,
3665
3667
  highlightedIndex: prevHighlightedIndex,
@@ -3861,12 +3863,13 @@
3861
3863
  }
3862
3864
  // eslint-disable-next-line react-hooks/exhaustive-deps
3863
3865
  }, []);
3864
- // Add mouse/touch events to document.
3865
- var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [menuRef, toggleButtonRef], environment, function () {
3866
- dispatch({
3867
- type: ToggleButtonBlur
3868
- });
3869
- });
3866
+ var mouseAndTouchTrackers = useMouseAndTouchTracker(environment, [toggleButtonRef, menuRef], React.useCallback(function handleBlur() {
3867
+ if (latest.current.state.isOpen) {
3868
+ dispatch({
3869
+ type: ToggleButtonBlur
3870
+ });
3871
+ }
3872
+ }, [dispatch, latest]));
3870
3873
  var setGetterPropCallInfo = useGetterPropsCalledChecker('getMenuProps', 'getToggleButtonProps');
3871
3874
  // Reset itemRefs on close.
3872
3875
  React.useEffect(function () {
@@ -4052,7 +4055,7 @@
4052
4055
  });
4053
4056
  };
4054
4057
  var toggleButtonHandleBlur = function toggleButtonHandleBlur() {
4055
- if (latestState.isOpen && !mouseAndTouchTrackersRef.current.isMouseDown) {
4058
+ if (latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
4056
4059
  dispatch({
4057
4060
  type: ToggleButtonBlur
4058
4061
  });
@@ -4081,7 +4084,7 @@
4081
4084
  }
4082
4085
  setGetterPropCallInfo('getToggleButtonProps', suppressRefError, refKey, toggleButtonRef);
4083
4086
  return toggleProps;
4084
- }, [latest, elementIds, setGetterPropCallInfo, dispatch, mouseAndTouchTrackersRef, toggleButtonKeyDownHandlers]);
4087
+ }, [dispatch, elementIds, latest, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers]);
4085
4088
  var getItemProps = React.useCallback(function (_temp6) {
4086
4089
  var _extends4;
4087
4090
  var _ref6 = _temp6 === void 0 ? {} : _temp6,
@@ -4107,7 +4110,7 @@
4107
4110
  index = _getItemAndIndex[1];
4108
4111
  var disabled = latestProps.isItemDisabled(item, index);
4109
4112
  var itemHandleMouseMove = function itemHandleMouseMove() {
4110
- if (mouseAndTouchTrackersRef.current.isTouchEnd || index === latestState.highlightedIndex) {
4113
+ if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
4111
4114
  return;
4112
4115
  }
4113
4116
  shouldScrollRef.current = false;
@@ -4141,7 +4144,7 @@
4141
4144
  itemProps.onMouseMove = callAllEventHandlers(onMouseMove, itemHandleMouseMove);
4142
4145
  itemProps.onMouseDown = callAllEventHandlers(onMouseDown, itemHandleMouseDown);
4143
4146
  return itemProps;
4144
- }, [latest, elementIds, mouseAndTouchTrackersRef, shouldScrollRef, dispatch]);
4147
+ }, [latest, elementIds, mouseAndTouchTrackers, shouldScrollRef, dispatch]);
4145
4148
  return {
4146
4149
  // prop getters.
4147
4150
  getToggleButtonProps: getToggleButtonProps,
@@ -4261,13 +4264,21 @@
4261
4264
  if (!isControlledProp(props, 'selectedItem')) {
4262
4265
  return;
4263
4266
  }
4264
- if (!isInitialMount &&
4265
- // on first mount we already have the proper inputValue for a initial selected item.
4266
- props.selectedItemChanged(previousSelectedItemRef.current, props.selectedItem)) {
4267
- dispatch({
4268
- type: ControlledPropUpdatedSelectedItem,
4269
- inputValue: props.itemToString(props.selectedItem)
4270
- });
4267
+ if (!isInitialMount // on first mount we already have the proper inputValue for a initial selected item.
4268
+ ) {
4269
+ var shouldCallDispatch;
4270
+ if (props.selectedItemChanged === undefined) {
4271
+ shouldCallDispatch = props.itemToKey(props.selectedItem) !== props.itemToKey(previousSelectedItemRef.current);
4272
+ } else {
4273
+ console.warn("The \"selectedItemChanged\" is deprecated. Please use \"itemToKey instead\". https://github.com/downshift-js/downshift/blob/master/src/hooks/useCombobox/README.md#selecteditemchanged");
4274
+ shouldCallDispatch = props.selectedItemChanged(previousSelectedItemRef.current, props.selectedItem);
4275
+ }
4276
+ if (shouldCallDispatch) {
4277
+ dispatch({
4278
+ type: ControlledPropUpdatedSelectedItem,
4279
+ inputValue: props.itemToString(props.selectedItem)
4280
+ });
4281
+ }
4271
4282
  }
4272
4283
  previousSelectedItemRef.current = state.selectedItem === previousSelectedItemRef.current ? props.selectedItem : state.selectedItem;
4273
4284
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -4284,9 +4295,6 @@
4284
4295
  };
4285
4296
  }
4286
4297
  var defaultProps$1 = _extends({}, defaultProps$3, {
4287
- selectedItemChanged: function selectedItemChanged(prevItem, item) {
4288
- return prevItem !== item;
4289
- },
4290
4298
  getA11yStatusMessage: getA11yStatusMessage$1,
4291
4299
  isItemDisabled: function isItemDisabled() {
4292
4300
  return false;
@@ -4497,13 +4505,14 @@
4497
4505
  previousResultCountRef.current = items.length;
4498
4506
  }
4499
4507
  });
4500
- // Add mouse/touch events to document.
4501
- var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [inputRef, menuRef, toggleButtonRef], environment, function () {
4502
- dispatch({
4503
- type: InputBlur,
4504
- selectItem: false
4505
- });
4506
- });
4508
+ var mouseAndTouchTrackers = useMouseAndTouchTracker(environment, [toggleButtonRef, menuRef, inputRef], React.useCallback(function handleBlur() {
4509
+ if (latest.current.state.isOpen) {
4510
+ dispatch({
4511
+ type: InputBlur,
4512
+ selectItem: false
4513
+ });
4514
+ }
4515
+ }, [dispatch, latest]));
4507
4516
  var setGetterPropCallInfo = useGetterPropsCalledChecker('getInputProps', 'getMenuProps');
4508
4517
  // Reset itemRefs on close.
4509
4518
  React.useEffect(function () {
@@ -4651,7 +4660,7 @@
4651
4660
  var onSelectKey = 'onClick';
4652
4661
  var customClickHandler = onClick;
4653
4662
  var itemHandleMouseMove = function itemHandleMouseMove() {
4654
- if (mouseAndTouchTrackersRef.current.isTouchEnd || index === latestState.highlightedIndex) {
4663
+ if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
4655
4664
  return;
4656
4665
  }
4657
4666
  shouldScrollRef.current = false;
@@ -4679,7 +4688,7 @@
4679
4688
  onMouseMove: callAllEventHandlers(onMouseMove, itemHandleMouseMove),
4680
4689
  onMouseDown: callAllEventHandlers(onMouseDown, itemHandleMouseDown)
4681
4690
  }, rest);
4682
- }, [dispatch, elementIds, latest, mouseAndTouchTrackersRef, shouldScrollRef]);
4691
+ }, [dispatch, elementIds, latest, mouseAndTouchTrackers, shouldScrollRef]);
4683
4692
  var getToggleButtonProps = React.useCallback(function (_temp4) {
4684
4693
  var _extends4;
4685
4694
  var _ref5 = _temp4 === void 0 ? {} : _temp4,
@@ -4733,7 +4742,7 @@
4733
4742
  };
4734
4743
  var inputHandleBlur = function inputHandleBlur(event) {
4735
4744
  /* istanbul ignore else */
4736
- if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackersRef.current.isMouseDown) {
4745
+ if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
4737
4746
  var isBlurByTabChange = event.relatedTarget === null && environment.document.activeElement !== environment.document.body;
4738
4747
  dispatch({
4739
4748
  type: InputBlur,
@@ -4757,7 +4766,7 @@
4757
4766
  return _extends((_extends5 = {}, _extends5[refKey] = handleRefs(ref, function (inputNode) {
4758
4767
  inputRef.current = inputNode;
4759
4768
  }), _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);
4760
- }, [setGetterPropCallInfo, latest, elementIds, inputKeyDownHandlers, dispatch, mouseAndTouchTrackersRef, environment]);
4769
+ }, [dispatch, elementIds, environment, inputKeyDownHandlers, latest, mouseAndTouchTrackers, setGetterPropCallInfo]);
4761
4770
 
4762
4771
  // returns
4763
4772
  var toggleMenu = React.useCallback(function () {
@@ -4930,6 +4939,7 @@
4930
4939
  });
4931
4940
  var defaultProps = {
4932
4941
  itemToString: defaultProps$3.itemToString,
4942
+ itemToKey: defaultProps$3.itemToKey,
4933
4943
  stateReducer: defaultProps$3.stateReducer,
4934
4944
  environment: defaultProps$3.environment,
4935
4945
  getA11yRemovalMessage: getA11yRemovalMessage,
@@ -5044,7 +5054,9 @@
5044
5054
  case FunctionRemoveSelectedItem:
5045
5055
  {
5046
5056
  var _newActiveIndex = activeIndex;
5047
- var selectedItemIndex = selectedItems.indexOf(selectedItem);
5057
+ var selectedItemIndex = selectedItems.findIndex(function (item) {
5058
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
5059
+ });
5048
5060
  if (selectedItemIndex < 0) {
5049
5061
  break;
5050
5062
  }
@@ -5128,8 +5140,10 @@
5128
5140
  return;
5129
5141
  }
5130
5142
  if (selectedItems.length < previousSelectedItemsRef.current.length) {
5131
- var removedSelectedItem = previousSelectedItemsRef.current.find(function (item) {
5132
- return selectedItems.indexOf(item) < 0;
5143
+ var removedSelectedItem = previousSelectedItemsRef.current.find(function (selectedItem) {
5144
+ return selectedItems.findIndex(function (item) {
5145
+ return props.itemToKey(item) === props.itemToKey(selectedItem);
5146
+ }) < 0;
5133
5147
  });
5134
5148
  setStatus(getA11yRemovalMessage({
5135
5149
  itemToString: itemToString,