downshift 9.4.0-alpha.2 → 9.4.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.
Files changed (44) hide show
  1. package/dist/downshift.cjs.cjs +132 -126
  2. package/dist/downshift.d.ts +60 -56
  3. package/dist/downshift.esm.mjs +132 -126
  4. package/dist/downshift.native.cjs.cjs +129 -126
  5. package/dist/downshift.nativeweb.cjs.cjs +132 -126
  6. package/dist/downshift.umd.js +132 -126
  7. package/dist/downshift.umd.js.map +1 -1
  8. package/dist/downshift.umd.min.js +1 -1
  9. package/dist/downshift.umd.min.js.map +1 -1
  10. package/dist/hooks/testUtils/interactions.d.ts +7 -7
  11. package/dist/hooks/useCombobox/__tests__/utils/renderCombobox.d.ts +3 -59
  12. package/dist/hooks/useCombobox/__tests__/utils/renderUseCombobox.d.ts +1 -1
  13. package/dist/hooks/useCombobox/index.types.d.ts +0 -1
  14. package/dist/hooks/useCombobox/utils/getInitialState.d.ts +1 -6
  15. package/dist/hooks/useCombobox/utils/propTypes.d.ts +1 -43
  16. package/dist/hooks/useSelect/__tests__/utils/getItemIndexByCharacter.d.ts +1 -1
  17. package/dist/hooks/useSelect/__tests__/utils/renderSelect.d.ts +2 -58
  18. package/dist/hooks/useSelect/__tests__/utils/renderUseSelect.d.ts +1 -1
  19. package/dist/hooks/useSelect/__tests__/utils/stateChangeTestCases.d.ts +29 -50
  20. package/dist/hooks/useSelect/utils/propTypes.d.ts +1 -38
  21. package/dist/hooks/useTagGroup/__tests__/utils/renderTagGroup.d.ts +1 -63
  22. package/dist/hooks/useTagGroup/__tests__/utils/renderUseTagGroup.d.ts +2 -2
  23. package/dist/hooks/utils/dropdownDefaultProps.d.ts +3 -4
  24. package/dist/hooks/utils/useMouseAndTouchTracker.d.ts +3 -3
  25. package/dist/hooks/utils/useScrollIntoView.d.ts +4 -3
  26. package/dist/utils/getHighlightedIndex.d.ts +1 -1
  27. package/dist/utils/getNonDisabledIndex.d.ts +1 -1
  28. package/dist/utils/index.d.ts +1 -0
  29. package/dist/utils/useIsomorphicLayoutEffect.d.ts +2 -0
  30. package/dist/utils/useLatestRef.d.ts +1 -1
  31. package/package.json +15 -2
  32. package/preact/dist/downshift.cjs.cjs +132 -126
  33. package/preact/dist/downshift.esm.mjs +132 -126
  34. package/preact/dist/downshift.umd.js +132 -126
  35. package/preact/dist/downshift.umd.js.map +1 -1
  36. package/preact/dist/downshift.umd.min.js +1 -1
  37. package/preact/dist/downshift.umd.min.js.map +1 -1
  38. package/dist/hooks/useMultipleSelection/index.d.ts +0 -50
  39. package/dist/hooks/useMultipleSelection/reducer.d.ts +0 -1
  40. package/dist/hooks/useMultipleSelection/stateChangeTypes.d.ts +0 -13
  41. package/dist/hooks/useMultipleSelection/utils.d.ts +0 -44
  42. package/dist/types.d.ts +0 -12
  43. package/preact/dist/downshift.cjs.js +0 -4265
  44. package/preact/dist/downshift.esm.js +0 -4238
@@ -359,14 +359,18 @@
359
359
  idCounter = 0;
360
360
  }
361
361
 
362
- function useLatestRef(val) {
363
- var ref = React__namespace.useRef(val);
364
- // technically this is not "concurrent mode safe" because we're manipulating
365
- // the value during render (so it's not idempotent). However, the places this
366
- // hook is used is to support memoizing callbacks which will be called
367
- // *during* render, so we need the latest values *during* render.
368
- // If not for this, then we'd probably want to use useLayoutEffect instead.
369
- ref.current = val;
362
+ // istanbul ignore next
363
+ var canUseDOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
364
+
365
+ // React Native supports layout effects even though it does not have a DOM.
366
+ // istanbul ignore next
367
+ var useIsomorphicLayoutEffect = canUseDOM || false ? React__namespace.useLayoutEffect : React__namespace.useEffect;
368
+
369
+ function useLatestRef(value) {
370
+ var ref = React__namespace.useRef(value);
371
+ useIsomorphicLayoutEffect(function () {
372
+ ref.current = value;
373
+ }, [value]);
370
374
  return ref;
371
375
  }
372
376
 
@@ -1846,18 +1850,27 @@
1846
1850
  */
1847
1851
  function useEnhancedReducer(reducer, props, createInitialState, isStateEqual) {
1848
1852
  var prevStateRef = React__namespace.useRef({});
1849
- var actionRef = React__namespace.useRef();
1850
- var enhancedReducer = React__namespace.useCallback(function (state, action) {
1851
- actionRef.current = action;
1853
+ var enhancedReducer = React__namespace.useCallback(function (_ref, action) {
1854
+ var state = _ref.state;
1852
1855
  state = getState(state, action.props);
1853
1856
  var changes = reducer(state, action);
1854
1857
  var newState = action.props.stateReducer(state, _extends({}, action, {
1855
1858
  changes: changes
1856
1859
  }));
1857
- return _extends({}, state, newState);
1860
+ return {
1861
+ state: _extends({}, state, newState),
1862
+ lastAction: action
1863
+ };
1858
1864
  }, [reducer]);
1859
- var _React$useReducer = React__namespace.useReducer(enhancedReducer, props, createInitialState),
1860
- state = _React$useReducer[0],
1865
+ var _React$useReducer = React__namespace.useReducer(enhancedReducer, props, function (p) {
1866
+ return {
1867
+ state: createInitialState(p),
1868
+ lastAction: undefined
1869
+ };
1870
+ }),
1871
+ _React$useReducer$ = _React$useReducer[0],
1872
+ state = _React$useReducer$.state,
1873
+ lastAction = _React$useReducer$.lastAction,
1861
1874
  dispatch = _React$useReducer[1];
1862
1875
  var propsRef = useLatestRef(props);
1863
1876
  var dispatchWithProps = React__namespace.useCallback(function (action) {
@@ -1865,15 +1878,16 @@
1865
1878
  props: propsRef.current
1866
1879
  }));
1867
1880
  }, [propsRef]);
1868
- var action = actionRef.current;
1869
1881
  React__namespace.useEffect(function () {
1870
- var prevState = getState(prevStateRef.current, action == null ? void 0 : action.props);
1871
- var shouldCallOnChangeProps = action && !isStateEqual(prevState, state);
1872
- if (shouldCallOnChangeProps) {
1873
- callOnChangeProps(action, action.props, prevState, state);
1882
+ if (lastAction) {
1883
+ var prevState = getState(prevStateRef.current, lastAction.props);
1884
+ var shouldCallOnChangeProps = !isStateEqual(prevState, state);
1885
+ if (shouldCallOnChangeProps) {
1886
+ callOnChangeProps(lastAction, lastAction.props, prevState, state);
1887
+ }
1874
1888
  }
1875
1889
  prevStateRef.current = state;
1876
- }, [state, action, isStateEqual]);
1890
+ }, [state, lastAction, isStateEqual]);
1877
1891
  return [state, dispatchWithProps];
1878
1892
  }
1879
1893
 
@@ -1934,6 +1948,8 @@
1934
1948
  isInitialMountRef.current = true;
1935
1949
  };
1936
1950
  }, []);
1951
+
1952
+ // eslint-disable-next-line react-hooks/refs
1937
1953
  return isInitialMountRef.current;
1938
1954
  }
1939
1955
 
@@ -2113,8 +2129,10 @@
2113
2129
  getItemId = _ref2.getItemId,
2114
2130
  toggleButtonId = _ref2.toggleButtonId,
2115
2131
  inputId = _ref2.inputId;
2116
- var baseIdRef = React__namespace.useRef(id != null ? id : "downshift-" + generateId());
2117
- var baseId = baseIdRef.current;
2132
+ var _React$useState = React__namespace.useState(function () {
2133
+ return id != null ? id : "downshift-" + generateId();
2134
+ }),
2135
+ baseId = _React$useState[0];
2118
2136
  var elementIds = React__namespace.useMemo(function () {
2119
2137
  return {
2120
2138
  labelId: labelId != null ? labelId : baseId + "-label",
@@ -2188,10 +2206,10 @@
2188
2206
  }
2189
2207
 
2190
2208
  // initialHighlightedIndex will give value to highlightedIndex on initial state only.
2191
- if (initialHighlightedIndex !== undefined && highlightedIndex === initialHighlightedIndex && items[initialHighlightedIndex] && !isItemDisabled(items[initialHighlightedIndex], initialHighlightedIndex)) {
2209
+ if (initialHighlightedIndex !== undefined && highlightedIndex === initialHighlightedIndex && (initialHighlightedIndex === -1 || items[initialHighlightedIndex] !== undefined && !isItemDisabled(items[initialHighlightedIndex], initialHighlightedIndex))) {
2192
2210
  return initialHighlightedIndex;
2193
2211
  }
2194
- if (defaultHighlightedIndex !== undefined && items[defaultHighlightedIndex] && !isItemDisabled(items[defaultHighlightedIndex], defaultHighlightedIndex)) {
2212
+ if (defaultHighlightedIndex !== undefined && (defaultHighlightedIndex === -1 || items[defaultHighlightedIndex] !== undefined && !isItemDisabled(items[defaultHighlightedIndex], defaultHighlightedIndex))) {
2195
2213
  return defaultHighlightedIndex;
2196
2214
  }
2197
2215
  if (selectedItem) {
@@ -2263,7 +2281,7 @@
2263
2281
  * @param environment The environment to add the event listeners to, for instance window.
2264
2282
  * @param handleBlur The function that is called if mouseDown or touchEnd occured outside the downshiftElements.
2265
2283
  * @param downshiftRefs The refs for the elements that should not trigger a blur action from mouseDown or touchEnd.
2266
- * @returns The mouse and touch events information.
2284
+ * @returns A ref holding the mouse and touch events information. Read `.current` only inside event handlers/effects.
2267
2285
  */
2268
2286
  function useMouseAndTouchTracker(environment, handleBlur, downshiftRefs) {
2269
2287
  var mouseAndTouchTrackersRef = React__namespace.useRef({
@@ -2316,7 +2334,7 @@
2316
2334
  environment.removeEventListener('touchend', onTouchEnd);
2317
2335
  };
2318
2336
  }, [environment, getDownshiftElements, handleBlur]);
2319
- return mouseAndTouchTrackersRef.current;
2337
+ return mouseAndTouchTrackersRef;
2320
2338
  }
2321
2339
 
2322
2340
  /* istanbul ignore next */
@@ -2371,31 +2389,28 @@
2371
2389
  };
2372
2390
  }
2373
2391
 
2374
- // istanbul ignore next
2375
- var useIsomorphicLayoutEffect = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined' ? React__namespace.useLayoutEffect : React__namespace.useEffect;
2376
-
2377
2392
  /**
2378
2393
  * Utility hook that scrolls an item from a menu into view.
2379
2394
  * @param scrollIntoView The function that does the scroll.
2380
2395
  * @param highlightedIndex The index of the item that should be scrolled.
2381
2396
  * @param isOpen If the menu is open or not.
2382
- * @param menuElement The menu element.
2383
- * @param itemElements The object containing item elements.
2397
+ * @param menuRef The ref to the menu element.
2398
+ * @param itemsRef The ref to the object containing item elements.
2384
2399
  * @param getItemId The function to get the item id from index.
2385
2400
  * @returns Function that when called prevents the scroll.
2386
2401
  */
2387
- function useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuElement, itemElements, getItemId) {
2402
+ function useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, getItemId) {
2388
2403
  // used not to scroll on highlight by mouse.
2389
2404
  var shouldScrollRef = React__namespace.useRef(true);
2390
2405
  // Scroll on highlighted item if change comes from keyboard.
2391
2406
  useIsomorphicLayoutEffect(function () {
2392
- if (highlightedIndex < 0 || !isOpen || !Object.keys(itemElements).length) {
2407
+ if (highlightedIndex < 0 || !isOpen || !Object.keys(itemsRef.current).length) {
2393
2408
  return;
2394
2409
  }
2395
2410
  if (shouldScrollRef.current) {
2396
- var itemElement = itemElements[getItemId(highlightedIndex)];
2397
- if (itemElement && menuElement) {
2398
- scrollIntoView(itemElement, menuElement);
2411
+ var itemElement = itemsRef.current[getItemId(highlightedIndex)];
2412
+ if (itemElement && menuRef.current) {
2413
+ scrollIntoView(itemElement, menuRef.current);
2399
2414
  }
2400
2415
  } else {
2401
2416
  shouldScrollRef.current = true;
@@ -2663,7 +2678,7 @@
2663
2678
 
2664
2679
  var _excluded$3 = ["onClick"],
2665
2680
  _excluded2$3 = ["onMouseLeave", "refKey", "ref", "aria-label"],
2666
- _excluded3$2 = ["onBlur", "onClick", "onPress", "onKeyDown", "refKey", "ref", "disabled"],
2681
+ _excluded3$2 = ["onBlur", "onClick", "onPress", "onKeyDown", "refKey", "ref"],
2667
2682
  _excluded4$1 = ["item", "index", "onMouseMove", "onClick", "onMouseDown", "onPress", "refKey", "disabled", "ref"];
2668
2683
  useSelect.stateChangeTypes = stateChangeTypes$3;
2669
2684
  function useSelect(userProps) {
@@ -2673,7 +2688,9 @@
2673
2688
  validatePropTypes$1(userProps, useSelect, propTypes$3);
2674
2689
  // Props defaults and destructuring.
2675
2690
  var props = _extends({}, dropdownDefaultProps, userProps);
2676
- var scrollIntoView = props.scrollIntoView,
2691
+ var items = props.items,
2692
+ isItemDisabled = props.isItemDisabled,
2693
+ scrollIntoView = props.scrollIntoView,
2677
2694
  environment = props.environment,
2678
2695
  getA11yStatusMessage = props.getA11yStatusMessage;
2679
2696
  // Initial state depending on controlled props.
@@ -2694,17 +2711,18 @@
2694
2711
  var clearTimeoutRef = React.useRef(null);
2695
2712
  // prevent id re-generation between renders.
2696
2713
  var elementIds = useElementIds$1(props);
2697
- // utility callback to get item element.
2698
- var latest = useLatestRef({
2699
- state: state,
2700
- props: props
2701
- });
2714
+ /**
2715
+ * Ref to read `state` in handlers to preserve referential identity.
2716
+ * Only to be used in handlers and effects.
2717
+ * **never access this in getters**
2718
+ */
2719
+ var stateRef = useLatestRef(state);
2702
2720
 
2703
2721
  // Effects.
2704
2722
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
2705
2723
  useA11yMessageStatus(getA11yStatusMessage, state, [isOpen, highlightedIndex, selectedItem, inputValue], environment);
2706
2724
  // Scroll on highlighted item if change comes from keyboard.
2707
- var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef.current, itemsRef.current, elementIds.getItemId);
2725
+ var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, elementIds.getItemId);
2708
2726
  // Sets cleanup for the keysSoFar callback, debounced after 500ms.
2709
2727
  React.useEffect(function () {
2710
2728
  // init the clean function here as we need access to dispatch.
@@ -2741,12 +2759,12 @@
2741
2759
  // eslint-disable-next-line react-hooks/exhaustive-deps
2742
2760
  }, []);
2743
2761
  var handleBlurInTracker = React.useCallback(function handleBlur() {
2744
- if (latest.current.state.isOpen) {
2762
+ if (stateRef.current.isOpen) {
2745
2763
  dispatch({
2746
2764
  type: ToggleButtonBlur
2747
2765
  });
2748
2766
  }
2749
- }, [dispatch, latest]);
2767
+ }, [dispatch, stateRef]);
2750
2768
  var downshiftRefs = React.useMemo(function () {
2751
2769
  return [menuRef, toggleButtonRef];
2752
2770
  }, []);
@@ -2789,7 +2807,7 @@
2789
2807
  });
2790
2808
  },
2791
2809
  Escape: function Escape() {
2792
- if (latest.current.state.isOpen) {
2810
+ if (stateRef.current.isOpen) {
2793
2811
  dispatch({
2794
2812
  type: ToggleButtonKeyDownEscape
2795
2813
  });
@@ -2798,11 +2816,11 @@
2798
2816
  Enter: function Enter(event) {
2799
2817
  event.preventDefault();
2800
2818
  dispatch({
2801
- type: latest.current.state.isOpen ? ToggleButtonKeyDownEnter : ToggleButtonClick$1
2819
+ type: stateRef.current.isOpen ? ToggleButtonKeyDownEnter : ToggleButtonClick$1
2802
2820
  });
2803
2821
  },
2804
2822
  PageUp: function PageUp(event) {
2805
- if (latest.current.state.isOpen) {
2823
+ if (stateRef.current.isOpen) {
2806
2824
  event.preventDefault();
2807
2825
  dispatch({
2808
2826
  type: ToggleButtonKeyDownPageUp
@@ -2810,7 +2828,7 @@
2810
2828
  }
2811
2829
  },
2812
2830
  PageDown: function PageDown(event) {
2813
- if (latest.current.state.isOpen) {
2831
+ if (stateRef.current.isOpen) {
2814
2832
  event.preventDefault();
2815
2833
  dispatch({
2816
2834
  type: ToggleButtonKeyDownPageDown
@@ -2819,7 +2837,7 @@
2819
2837
  },
2820
2838
  ' ': function _(event) {
2821
2839
  event.preventDefault();
2822
- var currentState = latest.current.state;
2840
+ var currentState = stateRef.current;
2823
2841
  if (!currentState.isOpen) {
2824
2842
  dispatch({
2825
2843
  type: ToggleButtonClick$1
@@ -2838,7 +2856,7 @@
2838
2856
  }
2839
2857
  }
2840
2858
  };
2841
- }, [dispatch, latest]);
2859
+ }, [dispatch, stateRef]);
2842
2860
 
2843
2861
  // Getter functions.
2844
2862
  var getLabelProps = React.useCallback(function (labelProps) {
@@ -2887,19 +2905,17 @@
2887
2905
  _ref4$refKey = _ref4.refKey,
2888
2906
  refKey = _ref4$refKey === void 0 ? 'ref' : _ref4$refKey,
2889
2907
  ref = _ref4.ref,
2890
- disabled = _ref4.disabled,
2891
2908
  rest = _objectWithoutPropertiesLoose(_ref4, _excluded3$2);
2892
2909
  var _ref5 = otherProps != null ? otherProps : {},
2893
2910
  _ref5$suppressRefErro = _ref5.suppressRefError,
2894
2911
  suppressRefError = _ref5$suppressRefErro === void 0 ? false : _ref5$suppressRefErro;
2895
- var latestState = latest.current.state;
2896
2912
  var toggleButtonHandleClick = function toggleButtonHandleClick() {
2897
2913
  dispatch({
2898
2914
  type: ToggleButtonClick$1
2899
2915
  });
2900
2916
  };
2901
2917
  var toggleButtonHandleBlur = function toggleButtonHandleBlur() {
2902
- if (latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
2918
+ if (stateRef.current.isOpen && !mouseAndTouchTrackers.current.isMouseDown) {
2903
2919
  dispatch({
2904
2920
  type: ToggleButtonBlur
2905
2921
  });
@@ -2918,8 +2934,8 @@
2918
2934
  };
2919
2935
  var toggleProps = _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (toggleButtonNode) {
2920
2936
  toggleButtonRef.current = toggleButtonNode;
2921
- }), _extends3['aria-activedescendant'] = latestState.isOpen && latestState.highlightedIndex > -1 ? elementIds.getItemId(latestState.highlightedIndex) : '', _extends3['aria-controls'] = elementIds.menuId, _extends3['aria-expanded'] = latest.current.state.isOpen, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-labelledby'] = rest['aria-label'] ? undefined : "" + elementIds.labelId, _extends3.id = elementIds.toggleButtonId, _extends3.role = 'combobox', _extends3.tabIndex = 0, _extends3.onBlur = callAllEventHandlers(onBlur, toggleButtonHandleBlur), _extends3.disabled = disabled, _extends3), rest);
2922
- if (!disabled) {
2937
+ }), _extends3['aria-activedescendant'] = isOpen && highlightedIndex > -1 ? elementIds.getItemId(highlightedIndex) : '', _extends3['aria-controls'] = elementIds.menuId, _extends3['aria-expanded'] = isOpen, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-labelledby'] = rest['aria-label'] ? undefined : "" + elementIds.labelId, _extends3.id = elementIds.toggleButtonId, _extends3.role = 'combobox', _extends3.tabIndex = 0, _extends3.onBlur = callAllEventHandlers(onBlur, toggleButtonHandleBlur), _extends3), rest);
2938
+ if (!rest.disabled) {
2923
2939
  /* istanbul ignore if (react-native) */
2924
2940
  {
2925
2941
  Object.assign(toggleProps, {
@@ -2930,7 +2946,7 @@
2930
2946
  }
2931
2947
  setGetterPropCallInfo('getToggleButtonProps', suppressRefError, refKey, toggleButtonRef);
2932
2948
  return toggleProps;
2933
- }, [dispatch, elementIds, latest, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers]);
2949
+ }, [dispatch, elementIds, isOpen, highlightedIndex, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers, stateRef]);
2934
2950
  var getItemProps = React.useCallback(function (itemProps) {
2935
2951
  var _extends4;
2936
2952
  var _ref6 = itemProps != null ? itemProps : {},
@@ -2948,15 +2964,12 @@
2948
2964
  if (disabledProp !== undefined) {
2949
2965
  console.warn('Passing "disabled" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useSelect.');
2950
2966
  }
2951
- var _latest$current = latest.current,
2952
- latestState = _latest$current.state,
2953
- latestProps = _latest$current.props;
2954
- var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, latestProps.items, 'Pass either item or index to getItemProps!'),
2967
+ var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, items, 'Pass either item or index to getItemProps!'),
2955
2968
  item = _getItemAndIndex[0],
2956
2969
  index = _getItemAndIndex[1];
2957
- var disabled = latestProps.isItemDisabled(item, index);
2970
+ var disabled = isItemDisabled(item, index);
2958
2971
  var itemHandleMouseMove = function itemHandleMouseMove() {
2959
- if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
2972
+ if (mouseAndTouchTrackers.current.isTouchEnd || index === stateRef.current.highlightedIndex) {
2960
2973
  return;
2961
2974
  }
2962
2975
  preventScroll();
@@ -2980,7 +2993,7 @@
2980
2993
  if (itemNode) {
2981
2994
  itemsRef.current[elementIds.getItemId(index)] = itemNode;
2982
2995
  }
2983
- }), _extends4['aria-disabled'] = disabled, _extends4['aria-selected'] = item === latestState.selectedItem, _extends4.id = elementIds.getItemId(index), _extends4.role = 'option', _extends4.onMouseMove = callAllEventHandlers(onMouseMove, itemHandleMouseMove), _extends4.onMouseDown = callAllEventHandlers(onMouseDown, itemHandleMouseDown), _extends4), rest);
2996
+ }), _extends4['aria-disabled'] = disabled, _extends4['aria-selected'] = item === selectedItem, _extends4.id = elementIds.getItemId(index), _extends4.role = 'option', _extends4.onMouseMove = callAllEventHandlers(onMouseMove, itemHandleMouseMove), _extends4.onMouseDown = callAllEventHandlers(onMouseDown, itemHandleMouseDown), _extends4), rest);
2984
2997
  if (!disabled) {
2985
2998
  /* istanbul ignore next (react-native) */
2986
2999
  {
@@ -2990,7 +3003,7 @@
2990
3003
  }
2991
3004
  }
2992
3005
  return resultItemProps;
2993
- }, [latest, elementIds, mouseAndTouchTrackers, preventScroll, dispatch]);
3006
+ }, [items, isItemDisabled, selectedItem, elementIds, mouseAndTouchTrackers, preventScroll, dispatch, stateRef]);
2994
3007
 
2995
3008
  // Action functions.
2996
3009
  var toggleMenu = React.useCallback(function () {
@@ -3281,8 +3294,8 @@
3281
3294
 
3282
3295
  var _excluded$2 = ["onMouseLeave", "refKey", "ref", "aria-label"],
3283
3296
  _excluded2$2 = ["item", "index", "refKey", "ref", "onMouseMove", "onMouseDown", "onClick", "onPress", "disabled"],
3284
- _excluded3$1 = ["onClick", "onPress", "refKey", "ref", "disabled"],
3285
- _excluded4 = ["aria-label", "disabled", "onKeyDown", "onChange", "onInput", "onBlur", "onChangeText", "onClick", "refKey", "ref"];
3297
+ _excluded3$1 = ["onClick", "onPress", "refKey", "ref"],
3298
+ _excluded4 = ["aria-label", "onKeyDown", "onChange", "onInput", "onBlur", "onChangeText", "onClick", "refKey", "ref"];
3286
3299
  useCombobox.stateChangeTypes = stateChangeTypes$2;
3287
3300
  function useCombobox(userProps) {
3288
3301
  if (userProps === void 0) {
@@ -3292,6 +3305,7 @@
3292
3305
  // Props defaults and destructuring.
3293
3306
  var props = _extends({}, dropdownDefaultProps, userProps);
3294
3307
  var items = props.items,
3308
+ isItemDisabled = props.isItemDisabled,
3295
3309
  scrollIntoView = props.scrollIntoView,
3296
3310
  environment = props.environment,
3297
3311
  getA11yStatusMessage = props.getA11yStatusMessage;
@@ -3315,17 +3329,18 @@
3315
3329
  var elementIds = useElementIds$1(props);
3316
3330
  // used to keep track of how many items we had on previous cycle.
3317
3331
  var previousResultCountRef = React.useRef();
3318
- // utility callback to get item element.
3319
- var latest = useLatestRef({
3320
- state: state,
3321
- props: props
3322
- });
3332
+ /**
3333
+ * Ref to read `state` in handlers to preserve referential identity.
3334
+ * Only to be used in handlers and effects.
3335
+ * **never access this in getters**
3336
+ */
3337
+ var stateRef = useLatestRef(state);
3323
3338
 
3324
3339
  // Effects.
3325
3340
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
3326
3341
  useA11yMessageStatus(getA11yStatusMessage, state, [isOpen, highlightedIndex, selectedItem, inputValue], environment);
3327
3342
  // Scroll on highlighted item if change comes from keyboard.
3328
- var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef.current, itemsRef.current, elementIds.getItemId);
3343
+ var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, elementIds.getItemId);
3329
3344
  useControlPropsValidator({
3330
3345
  state: state,
3331
3346
  props: props
@@ -3344,12 +3359,12 @@
3344
3359
  }
3345
3360
  });
3346
3361
  var handleBlurInTracker = React.useCallback(function handleBlur() {
3347
- if (latest.current.state.isOpen) {
3362
+ if (stateRef.current.isOpen) {
3348
3363
  dispatch({
3349
3364
  type: InputBlur
3350
3365
  });
3351
3366
  }
3352
- }, [dispatch, latest]);
3367
+ }, [dispatch, stateRef]);
3353
3368
  var downshiftRefs = React.useMemo(function () {
3354
3369
  return [menuRef, toggleButtonRef, inputRef];
3355
3370
  }, []);
@@ -3390,7 +3405,7 @@
3390
3405
  });
3391
3406
  },
3392
3407
  Home: function Home(event) {
3393
- if (!latest.current.state.isOpen) {
3408
+ if (!stateRef.current.isOpen) {
3394
3409
  return;
3395
3410
  }
3396
3411
  event.preventDefault();
@@ -3399,7 +3414,7 @@
3399
3414
  });
3400
3415
  },
3401
3416
  End: function End(event) {
3402
- if (!latest.current.state.isOpen) {
3417
+ if (!stateRef.current.isOpen) {
3403
3418
  return;
3404
3419
  }
3405
3420
  event.preventDefault();
@@ -3408,7 +3423,7 @@
3408
3423
  });
3409
3424
  },
3410
3425
  Escape: function Escape(event) {
3411
- var latestState = latest.current.state;
3426
+ var latestState = stateRef.current;
3412
3427
  if (latestState.isOpen || latestState.inputValue || latestState.selectedItem || latestState.highlightedIndex > -1) {
3413
3428
  event.preventDefault();
3414
3429
  dispatch({
@@ -3417,7 +3432,7 @@
3417
3432
  }
3418
3433
  },
3419
3434
  Enter: function Enter(event) {
3420
- var latestState = latest.current.state;
3435
+ var latestState = stateRef.current;
3421
3436
  // if closed or no highlighted index, do nothing.
3422
3437
  if (!latestState.isOpen || event.which === 229 // if IME composing, wait for next Enter keydown event.
3423
3438
  ) {
@@ -3429,7 +3444,7 @@
3429
3444
  });
3430
3445
  },
3431
3446
  PageUp: function PageUp(event) {
3432
- if (latest.current.state.isOpen) {
3447
+ if (stateRef.current.isOpen) {
3433
3448
  event.preventDefault();
3434
3449
  dispatch({
3435
3450
  type: InputKeyDownPageUp
@@ -3437,7 +3452,7 @@
3437
3452
  }
3438
3453
  },
3439
3454
  PageDown: function PageDown(event) {
3440
- if (latest.current.state.isOpen) {
3455
+ if (stateRef.current.isOpen) {
3441
3456
  event.preventDefault();
3442
3457
  dispatch({
3443
3458
  type: InputKeyDownPageDown
@@ -3445,7 +3460,7 @@
3445
3460
  }
3446
3461
  }
3447
3462
  };
3448
- }, [dispatch, latest]);
3463
+ }, [dispatch, stateRef]);
3449
3464
 
3450
3465
  // Getter props.
3451
3466
  var getLabelProps = React.useCallback(function (labelProps) {
@@ -3492,17 +3507,14 @@
3492
3507
  if (disabledProp !== undefined) {
3493
3508
  console.warn('Passing "disabled" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useCombobox.');
3494
3509
  }
3495
- var _latest$current = latest.current,
3496
- latestProps = _latest$current.props,
3497
- latestState = _latest$current.state;
3498
- var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, latestProps.items, 'Pass either item or index to getItemProps!'),
3510
+ var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, items, 'Pass either item or index to getItemProps!'),
3499
3511
  item = _getItemAndIndex[0],
3500
3512
  index = _getItemAndIndex[1];
3501
- var disabled = latestProps.isItemDisabled(item, index);
3513
+ var disabled = isItemDisabled(item, index);
3502
3514
  var onSelectKey = 'onClick';
3503
3515
  var customClickHandler = onClick;
3504
3516
  var itemHandleMouseMove = function itemHandleMouseMove() {
3505
- if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
3517
+ if (mouseAndTouchTrackers.current.isTouchEnd || index === stateRef.current.highlightedIndex) {
3506
3518
  return;
3507
3519
  }
3508
3520
  preventScroll();
@@ -3526,11 +3538,11 @@
3526
3538
  if (itemNode) {
3527
3539
  itemsRef.current[elementIds.getItemId(index)] = itemNode;
3528
3540
  }
3529
- }), _extends3['aria-disabled'] = disabled, _extends3['aria-selected'] = index === latestState.highlightedIndex, _extends3.id = elementIds.getItemId(index), _extends3.role = 'option', _extends3), !disabled && (_ref4 = {}, _ref4[onSelectKey] = callAllEventHandlers(customClickHandler, itemHandleClick), _ref4), {
3541
+ }), _extends3['aria-disabled'] = disabled, _extends3['aria-selected'] = index === highlightedIndex, _extends3.id = elementIds.getItemId(index), _extends3.role = 'option', _extends3), !disabled && (_ref4 = {}, _ref4[onSelectKey] = callAllEventHandlers(customClickHandler, itemHandleClick), _ref4), {
3530
3542
  onMouseMove: callAllEventHandlers(onMouseMove, itemHandleMouseMove),
3531
3543
  onMouseDown: callAllEventHandlers(onMouseDown, itemHandleMouseDown)
3532
3544
  }, rest);
3533
- }, [dispatch, elementIds, latest, mouseAndTouchTrackers, preventScroll]);
3545
+ }, [dispatch, elementIds, items, isItemDisabled, highlightedIndex, mouseAndTouchTrackers, preventScroll, stateRef]);
3534
3546
  var getToggleButtonProps = React.useCallback(function (toggleButtonProps) {
3535
3547
  var _extends4;
3536
3548
  var _ref5 = toggleButtonProps != null ? toggleButtonProps : {},
@@ -3539,9 +3551,7 @@
3539
3551
  var _ref5$refKey = _ref5.refKey,
3540
3552
  refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
3541
3553
  ref = _ref5.ref,
3542
- disabled = _ref5.disabled,
3543
3554
  rest = _objectWithoutPropertiesLoose(_ref5, _excluded3$1);
3544
- var latestState = latest.current.state;
3545
3555
  var toggleButtonHandleClick = function toggleButtonHandleClick() {
3546
3556
  dispatch({
3547
3557
  type: ToggleButtonClick
@@ -3549,17 +3559,14 @@
3549
3559
  };
3550
3560
  return _extends((_extends4 = {}, _extends4[refKey] = handleRefs(ref, function (toggleButtonNode) {
3551
3561
  toggleButtonRef.current = toggleButtonNode;
3552
- }), _extends4['aria-controls'] = elementIds.menuId, _extends4['aria-expanded'] = latestState.isOpen, _extends4.id = elementIds.toggleButtonId, _extends4.tabIndex = -1, _extends4), !disabled && _extends({}, {
3562
+ }), _extends4['aria-controls'] = elementIds.menuId, _extends4['aria-expanded'] = isOpen, _extends4.id = elementIds.toggleButtonId, _extends4.tabIndex = -1, _extends4), !rest.disabled && _extends({}, {
3553
3563
  onClick: callAllEventHandlers(onClick, toggleButtonHandleClick)
3554
- }), {
3555
- disabled: disabled
3556
- }, rest);
3557
- }, [dispatch, latest, elementIds]);
3564
+ }), rest);
3565
+ }, [dispatch, isOpen, elementIds]);
3558
3566
  var getInputProps = React.useCallback(function (inputProps, otherProps) {
3559
3567
  var _extends5;
3560
3568
  var _ref6 = inputProps != null ? inputProps : {},
3561
3569
  ariaLabel = _ref6['aria-label'],
3562
- disabled = _ref6.disabled,
3563
3570
  onKeyDown = _ref6.onKeyDown,
3564
3571
  onChange = _ref6.onChange,
3565
3572
  onInput = _ref6.onInput,
@@ -3574,7 +3581,6 @@
3574
3581
  _ref7$suppressRefErro = _ref7.suppressRefError,
3575
3582
  suppressRefError = _ref7$suppressRefErro === void 0 ? false : _ref7$suppressRefErro;
3576
3583
  setGetterPropCallInfo('getInputProps', suppressRefError, refKey, inputRef);
3577
- var latestState = latest.current.state;
3578
3584
  var inputHandleKeyDown = function inputHandleKeyDown(event) {
3579
3585
  var key = normalizeArrowKey(event);
3580
3586
  if (key && key in inputKeyDownHandlers) {
@@ -3590,7 +3596,7 @@
3590
3596
  };
3591
3597
  var inputHandleBlur = function inputHandleBlur(event) {
3592
3598
  /* istanbul ignore else */
3593
- if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
3599
+ if (environment != null && environment.document && stateRef.current.isOpen && !mouseAndTouchTrackers.current.isMouseDown) {
3594
3600
  var isBlurByTabChange = event.relatedTarget === null && environment.document.activeElement !== environment.document.body;
3595
3601
  dispatch({
3596
3602
  type: InputBlur,
@@ -3607,14 +3613,14 @@
3607
3613
  /* istanbul ignore next (preact) */
3608
3614
  var onChangeKey = 'onInput' ;
3609
3615
  var eventHandlers = {};
3610
- if (!disabled) {
3616
+ if (!rest.disabled) {
3611
3617
  var _eventHandlers;
3612
3618
  eventHandlers = (_eventHandlers = {}, _eventHandlers[onChangeKey] = callAllEventHandlers(onChange, onInput, inputHandleChange), _eventHandlers.onKeyDown = callAllEventHandlers(onKeyDown, inputHandleKeyDown), _eventHandlers.onBlur = callAllEventHandlers(onBlur, inputHandleBlur), _eventHandlers.onClick = callAllEventHandlers(onClick, inputHandleClick), _eventHandlers);
3613
3619
  }
3614
3620
  return _extends((_extends5 = {}, _extends5[refKey] = handleRefs(ref, function (inputNode) {
3615
3621
  inputRef.current = inputNode;
3616
- }), _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'] = ariaLabel ? undefined : elementIds.labelId, _extends5['aria-label'] = ariaLabel, _extends5.autoComplete = 'off', _extends5.disabled = disabled, _extends5.id = elementIds.inputId, _extends5.role = 'combobox', _extends5.value = latestState.inputValue, _extends5), eventHandlers, rest);
3617
- }, [dispatch, elementIds, environment, inputKeyDownHandlers, latest, mouseAndTouchTrackers, setGetterPropCallInfo]);
3622
+ }), _extends5['aria-activedescendant'] = isOpen && highlightedIndex > -1 ? elementIds.getItemId(highlightedIndex) : '', _extends5['aria-autocomplete'] = 'list', _extends5['aria-controls'] = elementIds.menuId, _extends5['aria-expanded'] = isOpen, _extends5['aria-labelledby'] = ariaLabel ? undefined : elementIds.labelId, _extends5['aria-label'] = ariaLabel, _extends5.autoComplete = 'off', _extends5.id = elementIds.inputId, _extends5.role = 'combobox', _extends5.value = inputValue, _extends5), eventHandlers, rest);
3623
+ }, [dispatch, elementIds, environment, inputKeyDownHandlers, isOpen, highlightedIndex, inputValue, mouseAndTouchTrackers, setGetterPropCallInfo, stateRef]);
3618
3624
 
3619
3625
  // returns
3620
3626
  var toggleMenu = React.useCallback(function () {
@@ -3936,12 +3942,13 @@
3936
3942
  // Refs.
3937
3943
  var isInitialMount = useIsInitialMount();
3938
3944
  var dropdownRef = React.useRef(null);
3939
- var selectedItemRefs = React.useRef();
3940
- selectedItemRefs.current = [];
3941
- var latest = useLatestRef({
3942
- state: state,
3943
- props: props
3944
- });
3945
+ // Map of selected-item index -> DOM node. Populated by the ref callback in
3946
+ // getSelectedItemProps and read by the focus effect. Keyed by
3947
+ // index so we never reset it during render.
3948
+ var selectedItemRefs = React.useRef(null);
3949
+ if (selectedItemRefs.current === null) {
3950
+ selectedItemRefs.current = new Map();
3951
+ }
3945
3952
 
3946
3953
  // Effects.
3947
3954
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
@@ -3953,8 +3960,9 @@
3953
3960
  }
3954
3961
  if (activeIndex === -1 && dropdownRef.current) {
3955
3962
  dropdownRef.current.focus();
3956
- } else if (selectedItemRefs.current[activeIndex]) {
3957
- selectedItemRefs.current[activeIndex].focus();
3963
+ } else {
3964
+ var _selectedItemRefs$cur;
3965
+ (_selectedItemRefs$cur = selectedItemRefs.current.get(activeIndex)) == null || _selectedItemRefs$cur.focus();
3958
3966
  }
3959
3967
  // eslint-disable-next-line react-hooks/exhaustive-deps
3960
3968
  }, [activeIndex]);
@@ -4014,10 +4022,9 @@
4014
4022
  selectedItemProp = _ref3.selectedItem,
4015
4023
  indexProp = _ref3.index,
4016
4024
  rest = _objectWithoutPropertiesLoose(_ref3, _excluded$1);
4017
- var latestState = latest.current.state;
4018
- var _getItemAndIndex = getItemAndIndex(selectedItemProp, indexProp, latestState.selectedItems, 'Pass either item or index to getSelectedItemProps!'),
4025
+ var _getItemAndIndex = getItemAndIndex(selectedItemProp, indexProp, selectedItems, 'Pass either item or index to getSelectedItemProps!'),
4019
4026
  index = _getItemAndIndex[1];
4020
- var isFocusable = index > -1 && index === latestState.activeIndex;
4027
+ var isFocusable = index > -1 && index === activeIndex;
4021
4028
  var selectedItemHandleClick = function selectedItemHandleClick() {
4022
4029
  dispatch({
4023
4030
  type: SelectedItemClick,
@@ -4032,10 +4039,12 @@
4032
4039
  };
4033
4040
  return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (selectedItemNode) {
4034
4041
  if (selectedItemNode) {
4035
- selectedItemRefs.current.push(selectedItemNode);
4042
+ selectedItemRefs.current.set(index, selectedItemNode);
4043
+ } else {
4044
+ selectedItemRefs.current["delete"](index);
4036
4045
  }
4037
4046
  }), _extends2.tabIndex = isFocusable ? 0 : -1, _extends2.onClick = callAllEventHandlers(onClick, selectedItemHandleClick), _extends2.onKeyDown = callAllEventHandlers(onKeyDown, selectedItemHandleKeyDown), _extends2), rest);
4038
- }, [dispatch, latest, selectedItemKeyDownHandlers]);
4047
+ }, [dispatch, selectedItems, activeIndex, selectedItemKeyDownHandlers]);
4039
4048
  var getDropdownProps = React.useCallback(function (_temp2, _temp3) {
4040
4049
  var _extends3;
4041
4050
  var _ref4 = _temp2 === void 0 ? {} : _temp2,
@@ -4223,8 +4232,10 @@
4223
4232
  var id = _ref2.id,
4224
4233
  getTagId = _ref2.getTagId,
4225
4234
  tagGroupId = _ref2.tagGroupId;
4226
- var baseIdRef = React__namespace.useRef(id != null ? id : "downshift-" + generateId());
4227
- var baseId = baseIdRef.current;
4235
+ var _React$useState = React__namespace.useState(function () {
4236
+ return id != null ? id : "downshift-" + generateId();
4237
+ }),
4238
+ baseId = _React$useState[0];
4228
4239
  var elementIds = React__namespace.useMemo(function () {
4229
4240
  return {
4230
4241
  tagGroupId: tagGroupId != null ? tagGroupId : baseId + "-tag-group",
@@ -4321,10 +4332,6 @@
4321
4332
 
4322
4333
  /* Refs */
4323
4334
 
4324
- var latest = useLatestRef({
4325
- state: state,
4326
- props: props
4327
- });
4328
4335
  var elementIds = useElementIds({
4329
4336
  getTagId: props.getTagId,
4330
4337
  id: props.id,
@@ -4387,7 +4394,6 @@
4387
4394
  if (!Number.isInteger(index) || index < 0) {
4388
4395
  throw new Error('Pass correct item index to getTagProps!');
4389
4396
  }
4390
- var latestState = latest.current.state;
4391
4397
  var handleClick = function handleClick() {
4392
4398
  dispatch({
4393
4399
  type: TagClick,
@@ -4401,8 +4407,8 @@
4401
4407
  if (itemNode) {
4402
4408
  itemRefs.current[tagId] = itemNode;
4403
4409
  }
4404
- }), _extends2['aria-labelledby'] = tagId, _extends2.role = 'option', _extends2.id = tagId, _extends2.onClick = callAllEventHandlers(onClick, handleClick), _extends2.tabIndex = latestState.activeIndex === index ? 0 : -1, _extends2), rest);
4405
- }, [dispatch, elementIds, latest, itemRefs]);
4410
+ }), _extends2['aria-labelledby'] = tagId, _extends2.role = 'option', _extends2.id = tagId, _extends2.onClick = callAllEventHandlers(onClick, handleClick), _extends2.tabIndex = activeIndex === index ? 0 : -1, _extends2), rest);
4411
+ }, [dispatch, elementIds, activeIndex, itemRefs]);
4406
4412
  var getTagRemoveProps = React.useCallback(function (options) {
4407
4413
  var index = options.index,
4408
4414
  onClick = options.onClick,