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
@@ -68,14 +68,18 @@ function resetIdCounter() {
68
68
  idCounter = 0;
69
69
  }
70
70
 
71
- function useLatestRef(val) {
72
- var ref = React.useRef(val);
73
- // technically this is not "concurrent mode safe" because we're manipulating
74
- // the value during render (so it's not idempotent). However, the places this
75
- // hook is used is to support memoizing callbacks which will be called
76
- // *during* render, so we need the latest values *during* render.
77
- // If not for this, then we'd probably want to use useLayoutEffect instead.
78
- ref.current = val;
71
+ // istanbul ignore next
72
+ var canUseDOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
73
+
74
+ // React Native supports layout effects even though it does not have a DOM.
75
+ // istanbul ignore next
76
+ var useIsomorphicLayoutEffect = canUseDOM || false ? React.useLayoutEffect : React.useEffect;
77
+
78
+ function useLatestRef(value) {
79
+ var ref = React.useRef(value);
80
+ useIsomorphicLayoutEffect(function () {
81
+ ref.current = value;
82
+ }, [value]);
79
83
  return ref;
80
84
  }
81
85
 
@@ -1563,18 +1567,27 @@ function invokeOnChangeHandler(key, action, props, state, newState) {
1563
1567
  */
1564
1568
  function useEnhancedReducer(reducer, props, createInitialState, isStateEqual) {
1565
1569
  var prevStateRef = React.useRef({});
1566
- var actionRef = React.useRef();
1567
- var enhancedReducer = React.useCallback(function (state, action) {
1568
- actionRef.current = action;
1570
+ var enhancedReducer = React.useCallback(function (_ref, action) {
1571
+ var state = _ref.state;
1569
1572
  state = getState(state, action.props);
1570
1573
  var changes = reducer(state, action);
1571
1574
  var newState = action.props.stateReducer(state, _extends({}, action, {
1572
1575
  changes: changes
1573
1576
  }));
1574
- return _extends({}, state, newState);
1577
+ return {
1578
+ state: _extends({}, state, newState),
1579
+ lastAction: action
1580
+ };
1575
1581
  }, [reducer]);
1576
- var _React$useReducer = React.useReducer(enhancedReducer, props, createInitialState),
1577
- state = _React$useReducer[0],
1582
+ var _React$useReducer = React.useReducer(enhancedReducer, props, function (p) {
1583
+ return {
1584
+ state: createInitialState(p),
1585
+ lastAction: undefined
1586
+ };
1587
+ }),
1588
+ _React$useReducer$ = _React$useReducer[0],
1589
+ state = _React$useReducer$.state,
1590
+ lastAction = _React$useReducer$.lastAction,
1578
1591
  dispatch = _React$useReducer[1];
1579
1592
  var propsRef = useLatestRef(props);
1580
1593
  var dispatchWithProps = React.useCallback(function (action) {
@@ -1582,15 +1595,16 @@ function useEnhancedReducer(reducer, props, createInitialState, isStateEqual) {
1582
1595
  props: propsRef.current
1583
1596
  }));
1584
1597
  }, [propsRef]);
1585
- var action = actionRef.current;
1586
1598
  React.useEffect(function () {
1587
- var prevState = getState(prevStateRef.current, action == null ? void 0 : action.props);
1588
- var shouldCallOnChangeProps = action && !isStateEqual(prevState, state);
1589
- if (shouldCallOnChangeProps) {
1590
- callOnChangeProps(action, action.props, prevState, state);
1599
+ if (lastAction) {
1600
+ var prevState = getState(prevStateRef.current, lastAction.props);
1601
+ var shouldCallOnChangeProps = !isStateEqual(prevState, state);
1602
+ if (shouldCallOnChangeProps) {
1603
+ callOnChangeProps(lastAction, lastAction.props, prevState, state);
1604
+ }
1591
1605
  }
1592
1606
  prevStateRef.current = state;
1593
- }, [state, action, isStateEqual]);
1607
+ }, [state, lastAction, isStateEqual]);
1594
1608
  return [state, dispatchWithProps];
1595
1609
  }
1596
1610
 
@@ -1651,6 +1665,8 @@ function useIsInitialMount() {
1651
1665
  isInitialMountRef.current = true;
1652
1666
  };
1653
1667
  }, []);
1668
+
1669
+ // eslint-disable-next-line react-hooks/refs
1654
1670
  return isInitialMountRef.current;
1655
1671
  }
1656
1672
 
@@ -1830,8 +1846,10 @@ function useElementIdsLegacy$1(_ref2) {
1830
1846
  getItemId = _ref2.getItemId,
1831
1847
  toggleButtonId = _ref2.toggleButtonId,
1832
1848
  inputId = _ref2.inputId;
1833
- var baseIdRef = React.useRef(id != null ? id : "downshift-" + generateId());
1834
- var baseId = baseIdRef.current;
1849
+ var _React$useState = React.useState(function () {
1850
+ return id != null ? id : "downshift-" + generateId();
1851
+ }),
1852
+ baseId = _React$useState[0];
1835
1853
  var elementIds = React.useMemo(function () {
1836
1854
  return {
1837
1855
  labelId: labelId != null ? labelId : baseId + "-label",
@@ -1905,10 +1923,10 @@ function getHighlightedIndexOnOpen(items, initialHighlightedIndex, defaultHighli
1905
1923
  }
1906
1924
 
1907
1925
  // initialHighlightedIndex will give value to highlightedIndex on initial state only.
1908
- if (initialHighlightedIndex !== undefined && highlightedIndex === initialHighlightedIndex && items[initialHighlightedIndex] && !isItemDisabled(items[initialHighlightedIndex], initialHighlightedIndex)) {
1926
+ if (initialHighlightedIndex !== undefined && highlightedIndex === initialHighlightedIndex && (initialHighlightedIndex === -1 || items[initialHighlightedIndex] !== undefined && !isItemDisabled(items[initialHighlightedIndex], initialHighlightedIndex))) {
1909
1927
  return initialHighlightedIndex;
1910
1928
  }
1911
- if (defaultHighlightedIndex !== undefined && items[defaultHighlightedIndex] && !isItemDisabled(items[defaultHighlightedIndex], defaultHighlightedIndex)) {
1929
+ if (defaultHighlightedIndex !== undefined && (defaultHighlightedIndex === -1 || items[defaultHighlightedIndex] !== undefined && !isItemDisabled(items[defaultHighlightedIndex], defaultHighlightedIndex))) {
1912
1930
  return defaultHighlightedIndex;
1913
1931
  }
1914
1932
  if (selectedItem) {
@@ -1980,7 +1998,7 @@ if (process.env.NODE_ENV !== 'production') {
1980
1998
  * @param environment The environment to add the event listeners to, for instance window.
1981
1999
  * @param handleBlur The function that is called if mouseDown or touchEnd occured outside the downshiftElements.
1982
2000
  * @param downshiftRefs The refs for the elements that should not trigger a blur action from mouseDown or touchEnd.
1983
- * @returns The mouse and touch events information.
2001
+ * @returns A ref holding the mouse and touch events information. Read `.current` only inside event handlers/effects.
1984
2002
  */
1985
2003
  function useMouseAndTouchTracker(environment, handleBlur, downshiftRefs) {
1986
2004
  var mouseAndTouchTrackersRef = React.useRef({
@@ -2033,7 +2051,7 @@ function useMouseAndTouchTracker(environment, handleBlur, downshiftRefs) {
2033
2051
  environment.removeEventListener('touchend', onTouchEnd);
2034
2052
  };
2035
2053
  }, [environment, getDownshiftElements, handleBlur]);
2036
- return mouseAndTouchTrackersRef.current;
2054
+ return mouseAndTouchTrackersRef;
2037
2055
  }
2038
2056
 
2039
2057
  /* istanbul ignore next */
@@ -2088,31 +2106,28 @@ if (process.env.NODE_ENV !== 'production') {
2088
2106
  };
2089
2107
  }
2090
2108
 
2091
- // istanbul ignore next
2092
- var useIsomorphicLayoutEffect = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined' ? React.useLayoutEffect : React.useEffect;
2093
-
2094
2109
  /**
2095
2110
  * Utility hook that scrolls an item from a menu into view.
2096
2111
  * @param scrollIntoView The function that does the scroll.
2097
2112
  * @param highlightedIndex The index of the item that should be scrolled.
2098
2113
  * @param isOpen If the menu is open or not.
2099
- * @param menuElement The menu element.
2100
- * @param itemElements The object containing item elements.
2114
+ * @param menuRef The ref to the menu element.
2115
+ * @param itemsRef The ref to the object containing item elements.
2101
2116
  * @param getItemId The function to get the item id from index.
2102
2117
  * @returns Function that when called prevents the scroll.
2103
2118
  */
2104
- function useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuElement, itemElements, getItemId) {
2119
+ function useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, getItemId) {
2105
2120
  // used not to scroll on highlight by mouse.
2106
2121
  var shouldScrollRef = React.useRef(true);
2107
2122
  // Scroll on highlighted item if change comes from keyboard.
2108
2123
  useIsomorphicLayoutEffect(function () {
2109
- if (highlightedIndex < 0 || !isOpen || !Object.keys(itemElements).length) {
2124
+ if (highlightedIndex < 0 || !isOpen || !Object.keys(itemsRef.current).length) {
2110
2125
  return;
2111
2126
  }
2112
2127
  if (shouldScrollRef.current) {
2113
- var itemElement = itemElements[getItemId(highlightedIndex)];
2114
- if (itemElement && menuElement) {
2115
- scrollIntoView(itemElement, menuElement);
2128
+ var itemElement = itemsRef.current[getItemId(highlightedIndex)];
2129
+ if (itemElement && menuRef.current) {
2130
+ scrollIntoView(itemElement, menuRef.current);
2116
2131
  }
2117
2132
  } else {
2118
2133
  shouldScrollRef.current = true;
@@ -2380,7 +2395,7 @@ function downshiftSelectReducer(state, action) {
2380
2395
 
2381
2396
  var _excluded$3 = ["onClick"],
2382
2397
  _excluded2$3 = ["onMouseLeave", "refKey", "ref", "aria-label"],
2383
- _excluded3$2 = ["onBlur", "onClick", "onPress", "onKeyDown", "refKey", "ref", "disabled"],
2398
+ _excluded3$2 = ["onBlur", "onClick", "onPress", "onKeyDown", "refKey", "ref"],
2384
2399
  _excluded4$1 = ["item", "index", "onMouseMove", "onClick", "onMouseDown", "onPress", "refKey", "disabled", "ref"];
2385
2400
  useSelect.stateChangeTypes = stateChangeTypes$3;
2386
2401
  function useSelect(userProps) {
@@ -2390,7 +2405,9 @@ function useSelect(userProps) {
2390
2405
  validatePropTypes$1(userProps, useSelect, propTypes$3);
2391
2406
  // Props defaults and destructuring.
2392
2407
  var props = _extends({}, dropdownDefaultProps, userProps);
2393
- var scrollIntoView = props.scrollIntoView,
2408
+ var items = props.items,
2409
+ isItemDisabled = props.isItemDisabled,
2410
+ scrollIntoView = props.scrollIntoView,
2394
2411
  environment = props.environment,
2395
2412
  getA11yStatusMessage = props.getA11yStatusMessage;
2396
2413
  // Initial state depending on controlled props.
@@ -2411,17 +2428,18 @@ function useSelect(userProps) {
2411
2428
  var clearTimeoutRef = useRef(null);
2412
2429
  // prevent id re-generation between renders.
2413
2430
  var elementIds = useElementIds$1(props);
2414
- // utility callback to get item element.
2415
- var latest = useLatestRef({
2416
- state: state,
2417
- props: props
2418
- });
2431
+ /**
2432
+ * Ref to read `state` in handlers to preserve referential identity.
2433
+ * Only to be used in handlers and effects.
2434
+ * **never access this in getters**
2435
+ */
2436
+ var stateRef = useLatestRef(state);
2419
2437
 
2420
2438
  // Effects.
2421
2439
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
2422
2440
  useA11yMessageStatus(getA11yStatusMessage, state, [isOpen, highlightedIndex, selectedItem, inputValue], environment);
2423
2441
  // Scroll on highlighted item if change comes from keyboard.
2424
- var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef.current, itemsRef.current, elementIds.getItemId);
2442
+ var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, elementIds.getItemId);
2425
2443
  // Sets cleanup for the keysSoFar callback, debounced after 500ms.
2426
2444
  useEffect(function () {
2427
2445
  // init the clean function here as we need access to dispatch.
@@ -2458,12 +2476,12 @@ function useSelect(userProps) {
2458
2476
  // eslint-disable-next-line react-hooks/exhaustive-deps
2459
2477
  }, []);
2460
2478
  var handleBlurInTracker = useCallback(function handleBlur() {
2461
- if (latest.current.state.isOpen) {
2479
+ if (stateRef.current.isOpen) {
2462
2480
  dispatch({
2463
2481
  type: ToggleButtonBlur
2464
2482
  });
2465
2483
  }
2466
- }, [dispatch, latest]);
2484
+ }, [dispatch, stateRef]);
2467
2485
  var downshiftRefs = useMemo(function () {
2468
2486
  return [menuRef, toggleButtonRef];
2469
2487
  }, []);
@@ -2506,7 +2524,7 @@ function useSelect(userProps) {
2506
2524
  });
2507
2525
  },
2508
2526
  Escape: function Escape() {
2509
- if (latest.current.state.isOpen) {
2527
+ if (stateRef.current.isOpen) {
2510
2528
  dispatch({
2511
2529
  type: ToggleButtonKeyDownEscape
2512
2530
  });
@@ -2515,11 +2533,11 @@ function useSelect(userProps) {
2515
2533
  Enter: function Enter(event) {
2516
2534
  event.preventDefault();
2517
2535
  dispatch({
2518
- type: latest.current.state.isOpen ? ToggleButtonKeyDownEnter : ToggleButtonClick$1
2536
+ type: stateRef.current.isOpen ? ToggleButtonKeyDownEnter : ToggleButtonClick$1
2519
2537
  });
2520
2538
  },
2521
2539
  PageUp: function PageUp(event) {
2522
- if (latest.current.state.isOpen) {
2540
+ if (stateRef.current.isOpen) {
2523
2541
  event.preventDefault();
2524
2542
  dispatch({
2525
2543
  type: ToggleButtonKeyDownPageUp
@@ -2527,7 +2545,7 @@ function useSelect(userProps) {
2527
2545
  }
2528
2546
  },
2529
2547
  PageDown: function PageDown(event) {
2530
- if (latest.current.state.isOpen) {
2548
+ if (stateRef.current.isOpen) {
2531
2549
  event.preventDefault();
2532
2550
  dispatch({
2533
2551
  type: ToggleButtonKeyDownPageDown
@@ -2536,7 +2554,7 @@ function useSelect(userProps) {
2536
2554
  },
2537
2555
  ' ': function _(event) {
2538
2556
  event.preventDefault();
2539
- var currentState = latest.current.state;
2557
+ var currentState = stateRef.current;
2540
2558
  if (!currentState.isOpen) {
2541
2559
  dispatch({
2542
2560
  type: ToggleButtonClick$1
@@ -2555,7 +2573,7 @@ function useSelect(userProps) {
2555
2573
  }
2556
2574
  }
2557
2575
  };
2558
- }, [dispatch, latest]);
2576
+ }, [dispatch, stateRef]);
2559
2577
 
2560
2578
  // Getter functions.
2561
2579
  var getLabelProps = useCallback(function (labelProps) {
@@ -2604,19 +2622,17 @@ function useSelect(userProps) {
2604
2622
  _ref4$refKey = _ref4.refKey,
2605
2623
  refKey = _ref4$refKey === void 0 ? 'ref' : _ref4$refKey,
2606
2624
  ref = _ref4.ref,
2607
- disabled = _ref4.disabled,
2608
2625
  rest = _objectWithoutPropertiesLoose(_ref4, _excluded3$2);
2609
2626
  var _ref5 = otherProps != null ? otherProps : {},
2610
2627
  _ref5$suppressRefErro = _ref5.suppressRefError,
2611
2628
  suppressRefError = _ref5$suppressRefErro === void 0 ? false : _ref5$suppressRefErro;
2612
- var latestState = latest.current.state;
2613
2629
  var toggleButtonHandleClick = function toggleButtonHandleClick() {
2614
2630
  dispatch({
2615
2631
  type: ToggleButtonClick$1
2616
2632
  });
2617
2633
  };
2618
2634
  var toggleButtonHandleBlur = function toggleButtonHandleBlur() {
2619
- if (latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
2635
+ if (stateRef.current.isOpen && !mouseAndTouchTrackers.current.isMouseDown) {
2620
2636
  dispatch({
2621
2637
  type: ToggleButtonBlur
2622
2638
  });
@@ -2635,8 +2651,8 @@ function useSelect(userProps) {
2635
2651
  };
2636
2652
  var toggleProps = _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (toggleButtonNode) {
2637
2653
  toggleButtonRef.current = toggleButtonNode;
2638
- }), _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);
2639
- if (!disabled) {
2654
+ }), _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);
2655
+ if (!rest.disabled) {
2640
2656
  /* istanbul ignore if (react-native) */
2641
2657
  {
2642
2658
  Object.assign(toggleProps, {
@@ -2647,7 +2663,7 @@ function useSelect(userProps) {
2647
2663
  }
2648
2664
  setGetterPropCallInfo('getToggleButtonProps', suppressRefError, refKey, toggleButtonRef);
2649
2665
  return toggleProps;
2650
- }, [dispatch, elementIds, latest, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers]);
2666
+ }, [dispatch, elementIds, isOpen, highlightedIndex, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers, stateRef]);
2651
2667
  var getItemProps = useCallback(function (itemProps) {
2652
2668
  var _extends4;
2653
2669
  var _ref6 = itemProps != null ? itemProps : {},
@@ -2665,15 +2681,12 @@ function useSelect(userProps) {
2665
2681
  if (disabledProp !== undefined) {
2666
2682
  console.warn('Passing "disabled" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useSelect.');
2667
2683
  }
2668
- var _latest$current = latest.current,
2669
- latestState = _latest$current.state,
2670
- latestProps = _latest$current.props;
2671
- var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, latestProps.items, 'Pass either item or index to getItemProps!'),
2684
+ var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, items, 'Pass either item or index to getItemProps!'),
2672
2685
  item = _getItemAndIndex[0],
2673
2686
  index = _getItemAndIndex[1];
2674
- var disabled = latestProps.isItemDisabled(item, index);
2687
+ var disabled = isItemDisabled(item, index);
2675
2688
  var itemHandleMouseMove = function itemHandleMouseMove() {
2676
- if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
2689
+ if (mouseAndTouchTrackers.current.isTouchEnd || index === stateRef.current.highlightedIndex) {
2677
2690
  return;
2678
2691
  }
2679
2692
  preventScroll();
@@ -2697,7 +2710,7 @@ function useSelect(userProps) {
2697
2710
  if (itemNode) {
2698
2711
  itemsRef.current[elementIds.getItemId(index)] = itemNode;
2699
2712
  }
2700
- }), _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);
2713
+ }), _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);
2701
2714
  if (!disabled) {
2702
2715
  /* istanbul ignore next (react-native) */
2703
2716
  {
@@ -2707,7 +2720,7 @@ function useSelect(userProps) {
2707
2720
  }
2708
2721
  }
2709
2722
  return resultItemProps;
2710
- }, [latest, elementIds, mouseAndTouchTrackers, preventScroll, dispatch]);
2723
+ }, [items, isItemDisabled, selectedItem, elementIds, mouseAndTouchTrackers, preventScroll, dispatch, stateRef]);
2711
2724
 
2712
2725
  // Action functions.
2713
2726
  var toggleMenu = useCallback(function () {
@@ -2998,8 +3011,8 @@ function downshiftUseComboboxReducer(state, action) {
2998
3011
 
2999
3012
  var _excluded$2 = ["onMouseLeave", "refKey", "ref", "aria-label"],
3000
3013
  _excluded2$2 = ["item", "index", "refKey", "ref", "onMouseMove", "onMouseDown", "onClick", "onPress", "disabled"],
3001
- _excluded3$1 = ["onClick", "onPress", "refKey", "ref", "disabled"],
3002
- _excluded4 = ["aria-label", "disabled", "onKeyDown", "onChange", "onInput", "onBlur", "onChangeText", "onClick", "refKey", "ref"];
3014
+ _excluded3$1 = ["onClick", "onPress", "refKey", "ref"],
3015
+ _excluded4 = ["aria-label", "onKeyDown", "onChange", "onInput", "onBlur", "onChangeText", "onClick", "refKey", "ref"];
3003
3016
  useCombobox.stateChangeTypes = stateChangeTypes$2;
3004
3017
  function useCombobox(userProps) {
3005
3018
  if (userProps === void 0) {
@@ -3009,6 +3022,7 @@ function useCombobox(userProps) {
3009
3022
  // Props defaults and destructuring.
3010
3023
  var props = _extends({}, dropdownDefaultProps, userProps);
3011
3024
  var items = props.items,
3025
+ isItemDisabled = props.isItemDisabled,
3012
3026
  scrollIntoView = props.scrollIntoView,
3013
3027
  environment = props.environment,
3014
3028
  getA11yStatusMessage = props.getA11yStatusMessage;
@@ -3032,17 +3046,18 @@ function useCombobox(userProps) {
3032
3046
  var elementIds = useElementIds$1(props);
3033
3047
  // used to keep track of how many items we had on previous cycle.
3034
3048
  var previousResultCountRef = useRef();
3035
- // utility callback to get item element.
3036
- var latest = useLatestRef({
3037
- state: state,
3038
- props: props
3039
- });
3049
+ /**
3050
+ * Ref to read `state` in handlers to preserve referential identity.
3051
+ * Only to be used in handlers and effects.
3052
+ * **never access this in getters**
3053
+ */
3054
+ var stateRef = useLatestRef(state);
3040
3055
 
3041
3056
  // Effects.
3042
3057
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
3043
3058
  useA11yMessageStatus(getA11yStatusMessage, state, [isOpen, highlightedIndex, selectedItem, inputValue], environment);
3044
3059
  // Scroll on highlighted item if change comes from keyboard.
3045
- var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef.current, itemsRef.current, elementIds.getItemId);
3060
+ var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, elementIds.getItemId);
3046
3061
  useControlPropsValidator({
3047
3062
  state: state,
3048
3063
  props: props
@@ -3061,12 +3076,12 @@ function useCombobox(userProps) {
3061
3076
  }
3062
3077
  });
3063
3078
  var handleBlurInTracker = useCallback(function handleBlur() {
3064
- if (latest.current.state.isOpen) {
3079
+ if (stateRef.current.isOpen) {
3065
3080
  dispatch({
3066
3081
  type: InputBlur
3067
3082
  });
3068
3083
  }
3069
- }, [dispatch, latest]);
3084
+ }, [dispatch, stateRef]);
3070
3085
  var downshiftRefs = useMemo(function () {
3071
3086
  return [menuRef, toggleButtonRef, inputRef];
3072
3087
  }, []);
@@ -3107,7 +3122,7 @@ function useCombobox(userProps) {
3107
3122
  });
3108
3123
  },
3109
3124
  Home: function Home(event) {
3110
- if (!latest.current.state.isOpen) {
3125
+ if (!stateRef.current.isOpen) {
3111
3126
  return;
3112
3127
  }
3113
3128
  event.preventDefault();
@@ -3116,7 +3131,7 @@ function useCombobox(userProps) {
3116
3131
  });
3117
3132
  },
3118
3133
  End: function End(event) {
3119
- if (!latest.current.state.isOpen) {
3134
+ if (!stateRef.current.isOpen) {
3120
3135
  return;
3121
3136
  }
3122
3137
  event.preventDefault();
@@ -3125,7 +3140,7 @@ function useCombobox(userProps) {
3125
3140
  });
3126
3141
  },
3127
3142
  Escape: function Escape(event) {
3128
- var latestState = latest.current.state;
3143
+ var latestState = stateRef.current;
3129
3144
  if (latestState.isOpen || latestState.inputValue || latestState.selectedItem || latestState.highlightedIndex > -1) {
3130
3145
  event.preventDefault();
3131
3146
  dispatch({
@@ -3134,7 +3149,7 @@ function useCombobox(userProps) {
3134
3149
  }
3135
3150
  },
3136
3151
  Enter: function Enter(event) {
3137
- var latestState = latest.current.state;
3152
+ var latestState = stateRef.current;
3138
3153
  // if closed or no highlighted index, do nothing.
3139
3154
  if (!latestState.isOpen || event.which === 229 // if IME composing, wait for next Enter keydown event.
3140
3155
  ) {
@@ -3146,7 +3161,7 @@ function useCombobox(userProps) {
3146
3161
  });
3147
3162
  },
3148
3163
  PageUp: function PageUp(event) {
3149
- if (latest.current.state.isOpen) {
3164
+ if (stateRef.current.isOpen) {
3150
3165
  event.preventDefault();
3151
3166
  dispatch({
3152
3167
  type: InputKeyDownPageUp
@@ -3154,7 +3169,7 @@ function useCombobox(userProps) {
3154
3169
  }
3155
3170
  },
3156
3171
  PageDown: function PageDown(event) {
3157
- if (latest.current.state.isOpen) {
3172
+ if (stateRef.current.isOpen) {
3158
3173
  event.preventDefault();
3159
3174
  dispatch({
3160
3175
  type: InputKeyDownPageDown
@@ -3162,7 +3177,7 @@ function useCombobox(userProps) {
3162
3177
  }
3163
3178
  }
3164
3179
  };
3165
- }, [dispatch, latest]);
3180
+ }, [dispatch, stateRef]);
3166
3181
 
3167
3182
  // Getter props.
3168
3183
  var getLabelProps = useCallback(function (labelProps) {
@@ -3209,17 +3224,14 @@ function useCombobox(userProps) {
3209
3224
  if (disabledProp !== undefined) {
3210
3225
  console.warn('Passing "disabled" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useCombobox.');
3211
3226
  }
3212
- var _latest$current = latest.current,
3213
- latestProps = _latest$current.props,
3214
- latestState = _latest$current.state;
3215
- var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, latestProps.items, 'Pass either item or index to getItemProps!'),
3227
+ var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, items, 'Pass either item or index to getItemProps!'),
3216
3228
  item = _getItemAndIndex[0],
3217
3229
  index = _getItemAndIndex[1];
3218
- var disabled = latestProps.isItemDisabled(item, index);
3230
+ var disabled = isItemDisabled(item, index);
3219
3231
  var onSelectKey = 'onClick';
3220
3232
  var customClickHandler = onClick;
3221
3233
  var itemHandleMouseMove = function itemHandleMouseMove() {
3222
- if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
3234
+ if (mouseAndTouchTrackers.current.isTouchEnd || index === stateRef.current.highlightedIndex) {
3223
3235
  return;
3224
3236
  }
3225
3237
  preventScroll();
@@ -3243,11 +3255,11 @@ function useCombobox(userProps) {
3243
3255
  if (itemNode) {
3244
3256
  itemsRef.current[elementIds.getItemId(index)] = itemNode;
3245
3257
  }
3246
- }), _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), {
3258
+ }), _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), {
3247
3259
  onMouseMove: callAllEventHandlers(onMouseMove, itemHandleMouseMove),
3248
3260
  onMouseDown: callAllEventHandlers(onMouseDown, itemHandleMouseDown)
3249
3261
  }, rest);
3250
- }, [dispatch, elementIds, latest, mouseAndTouchTrackers, preventScroll]);
3262
+ }, [dispatch, elementIds, items, isItemDisabled, highlightedIndex, mouseAndTouchTrackers, preventScroll, stateRef]);
3251
3263
  var getToggleButtonProps = useCallback(function (toggleButtonProps) {
3252
3264
  var _extends4;
3253
3265
  var _ref5 = toggleButtonProps != null ? toggleButtonProps : {},
@@ -3256,9 +3268,7 @@ function useCombobox(userProps) {
3256
3268
  var _ref5$refKey = _ref5.refKey,
3257
3269
  refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
3258
3270
  ref = _ref5.ref,
3259
- disabled = _ref5.disabled,
3260
3271
  rest = _objectWithoutPropertiesLoose(_ref5, _excluded3$1);
3261
- var latestState = latest.current.state;
3262
3272
  var toggleButtonHandleClick = function toggleButtonHandleClick() {
3263
3273
  dispatch({
3264
3274
  type: ToggleButtonClick
@@ -3266,17 +3276,14 @@ function useCombobox(userProps) {
3266
3276
  };
3267
3277
  return _extends((_extends4 = {}, _extends4[refKey] = handleRefs(ref, function (toggleButtonNode) {
3268
3278
  toggleButtonRef.current = toggleButtonNode;
3269
- }), _extends4['aria-controls'] = elementIds.menuId, _extends4['aria-expanded'] = latestState.isOpen, _extends4.id = elementIds.toggleButtonId, _extends4.tabIndex = -1, _extends4), !disabled && _extends({}, {
3279
+ }), _extends4['aria-controls'] = elementIds.menuId, _extends4['aria-expanded'] = isOpen, _extends4.id = elementIds.toggleButtonId, _extends4.tabIndex = -1, _extends4), !rest.disabled && _extends({}, {
3270
3280
  onClick: callAllEventHandlers(onClick, toggleButtonHandleClick)
3271
- }), {
3272
- disabled: disabled
3273
- }, rest);
3274
- }, [dispatch, latest, elementIds]);
3281
+ }), rest);
3282
+ }, [dispatch, isOpen, elementIds]);
3275
3283
  var getInputProps = useCallback(function (inputProps, otherProps) {
3276
3284
  var _extends5;
3277
3285
  var _ref6 = inputProps != null ? inputProps : {},
3278
3286
  ariaLabel = _ref6['aria-label'],
3279
- disabled = _ref6.disabled,
3280
3287
  onKeyDown = _ref6.onKeyDown,
3281
3288
  onChange = _ref6.onChange,
3282
3289
  onInput = _ref6.onInput,
@@ -3291,7 +3298,6 @@ function useCombobox(userProps) {
3291
3298
  _ref7$suppressRefErro = _ref7.suppressRefError,
3292
3299
  suppressRefError = _ref7$suppressRefErro === void 0 ? false : _ref7$suppressRefErro;
3293
3300
  setGetterPropCallInfo('getInputProps', suppressRefError, refKey, inputRef);
3294
- var latestState = latest.current.state;
3295
3301
  var inputHandleKeyDown = function inputHandleKeyDown(event) {
3296
3302
  var key = normalizeArrowKey(event);
3297
3303
  if (key && key in inputKeyDownHandlers) {
@@ -3307,7 +3313,7 @@ function useCombobox(userProps) {
3307
3313
  };
3308
3314
  var inputHandleBlur = function inputHandleBlur(event) {
3309
3315
  /* istanbul ignore else */
3310
- if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
3316
+ if (environment != null && environment.document && stateRef.current.isOpen && !mouseAndTouchTrackers.current.isMouseDown) {
3311
3317
  var isBlurByTabChange = event.relatedTarget === null && environment.document.activeElement !== environment.document.body;
3312
3318
  dispatch({
3313
3319
  type: InputBlur,
@@ -3324,14 +3330,14 @@ function useCombobox(userProps) {
3324
3330
  /* istanbul ignore next (preact) */
3325
3331
  var onChangeKey = 'onInput' ;
3326
3332
  var eventHandlers = {};
3327
- if (!disabled) {
3333
+ if (!rest.disabled) {
3328
3334
  var _eventHandlers;
3329
3335
  eventHandlers = (_eventHandlers = {}, _eventHandlers[onChangeKey] = callAllEventHandlers(onChange, onInput, inputHandleChange), _eventHandlers.onKeyDown = callAllEventHandlers(onKeyDown, inputHandleKeyDown), _eventHandlers.onBlur = callAllEventHandlers(onBlur, inputHandleBlur), _eventHandlers.onClick = callAllEventHandlers(onClick, inputHandleClick), _eventHandlers);
3330
3336
  }
3331
3337
  return _extends((_extends5 = {}, _extends5[refKey] = handleRefs(ref, function (inputNode) {
3332
3338
  inputRef.current = inputNode;
3333
- }), _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);
3334
- }, [dispatch, elementIds, environment, inputKeyDownHandlers, latest, mouseAndTouchTrackers, setGetterPropCallInfo]);
3339
+ }), _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);
3340
+ }, [dispatch, elementIds, environment, inputKeyDownHandlers, isOpen, highlightedIndex, inputValue, mouseAndTouchTrackers, setGetterPropCallInfo, stateRef]);
3335
3341
 
3336
3342
  // returns
3337
3343
  var toggleMenu = useCallback(function () {
@@ -3653,12 +3659,13 @@ function useMultipleSelection(userProps) {
3653
3659
  // Refs.
3654
3660
  var isInitialMount = useIsInitialMount();
3655
3661
  var dropdownRef = useRef(null);
3656
- var selectedItemRefs = useRef();
3657
- selectedItemRefs.current = [];
3658
- var latest = useLatestRef({
3659
- state: state,
3660
- props: props
3661
- });
3662
+ // Map of selected-item index -> DOM node. Populated by the ref callback in
3663
+ // getSelectedItemProps and read by the focus effect. Keyed by
3664
+ // index so we never reset it during render.
3665
+ var selectedItemRefs = useRef(null);
3666
+ if (selectedItemRefs.current === null) {
3667
+ selectedItemRefs.current = new Map();
3668
+ }
3662
3669
 
3663
3670
  // Effects.
3664
3671
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
@@ -3670,8 +3677,9 @@ function useMultipleSelection(userProps) {
3670
3677
  }
3671
3678
  if (activeIndex === -1 && dropdownRef.current) {
3672
3679
  dropdownRef.current.focus();
3673
- } else if (selectedItemRefs.current[activeIndex]) {
3674
- selectedItemRefs.current[activeIndex].focus();
3680
+ } else {
3681
+ var _selectedItemRefs$cur;
3682
+ (_selectedItemRefs$cur = selectedItemRefs.current.get(activeIndex)) == null || _selectedItemRefs$cur.focus();
3675
3683
  }
3676
3684
  // eslint-disable-next-line react-hooks/exhaustive-deps
3677
3685
  }, [activeIndex]);
@@ -3731,10 +3739,9 @@ function useMultipleSelection(userProps) {
3731
3739
  selectedItemProp = _ref3.selectedItem,
3732
3740
  indexProp = _ref3.index,
3733
3741
  rest = _objectWithoutPropertiesLoose(_ref3, _excluded$1);
3734
- var latestState = latest.current.state;
3735
- var _getItemAndIndex = getItemAndIndex(selectedItemProp, indexProp, latestState.selectedItems, 'Pass either item or index to getSelectedItemProps!'),
3742
+ var _getItemAndIndex = getItemAndIndex(selectedItemProp, indexProp, selectedItems, 'Pass either item or index to getSelectedItemProps!'),
3736
3743
  index = _getItemAndIndex[1];
3737
- var isFocusable = index > -1 && index === latestState.activeIndex;
3744
+ var isFocusable = index > -1 && index === activeIndex;
3738
3745
  var selectedItemHandleClick = function selectedItemHandleClick() {
3739
3746
  dispatch({
3740
3747
  type: SelectedItemClick,
@@ -3749,10 +3756,12 @@ function useMultipleSelection(userProps) {
3749
3756
  };
3750
3757
  return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (selectedItemNode) {
3751
3758
  if (selectedItemNode) {
3752
- selectedItemRefs.current.push(selectedItemNode);
3759
+ selectedItemRefs.current.set(index, selectedItemNode);
3760
+ } else {
3761
+ selectedItemRefs.current["delete"](index);
3753
3762
  }
3754
3763
  }), _extends2.tabIndex = isFocusable ? 0 : -1, _extends2.onClick = callAllEventHandlers(onClick, selectedItemHandleClick), _extends2.onKeyDown = callAllEventHandlers(onKeyDown, selectedItemHandleKeyDown), _extends2), rest);
3755
- }, [dispatch, latest, selectedItemKeyDownHandlers]);
3764
+ }, [dispatch, selectedItems, activeIndex, selectedItemKeyDownHandlers]);
3756
3765
  var getDropdownProps = useCallback(function (_temp2, _temp3) {
3757
3766
  var _extends3;
3758
3767
  var _ref4 = _temp2 === void 0 ? {} : _temp2,
@@ -3940,8 +3949,10 @@ function useElementIdsLegacy(_ref2) {
3940
3949
  var id = _ref2.id,
3941
3950
  getTagId = _ref2.getTagId,
3942
3951
  tagGroupId = _ref2.tagGroupId;
3943
- var baseIdRef = React.useRef(id != null ? id : "downshift-" + generateId());
3944
- var baseId = baseIdRef.current;
3952
+ var _React$useState = React.useState(function () {
3953
+ return id != null ? id : "downshift-" + generateId();
3954
+ }),
3955
+ baseId = _React$useState[0];
3945
3956
  var elementIds = React.useMemo(function () {
3946
3957
  return {
3947
3958
  tagGroupId: tagGroupId != null ? tagGroupId : baseId + "-tag-group",
@@ -4038,10 +4049,6 @@ var _useTagGroup = function useTagGroup(userProps) {
4038
4049
 
4039
4050
  /* Refs */
4040
4051
 
4041
- var latest = useLatestRef({
4042
- state: state,
4043
- props: props
4044
- });
4045
4052
  var elementIds = useElementIds({
4046
4053
  getTagId: props.getTagId,
4047
4054
  id: props.id,
@@ -4104,7 +4111,6 @@ var _useTagGroup = function useTagGroup(userProps) {
4104
4111
  if (!Number.isInteger(index) || index < 0) {
4105
4112
  throw new Error('Pass correct item index to getTagProps!');
4106
4113
  }
4107
- var latestState = latest.current.state;
4108
4114
  var handleClick = function handleClick() {
4109
4115
  dispatch({
4110
4116
  type: TagClick,
@@ -4118,8 +4124,8 @@ var _useTagGroup = function useTagGroup(userProps) {
4118
4124
  if (itemNode) {
4119
4125
  itemRefs.current[tagId] = itemNode;
4120
4126
  }
4121
- }), _extends2['aria-labelledby'] = tagId, _extends2.role = 'option', _extends2.id = tagId, _extends2.onClick = callAllEventHandlers(onClick, handleClick), _extends2.tabIndex = latestState.activeIndex === index ? 0 : -1, _extends2), rest);
4122
- }, [dispatch, elementIds, latest, itemRefs]);
4127
+ }), _extends2['aria-labelledby'] = tagId, _extends2.role = 'option', _extends2.id = tagId, _extends2.onClick = callAllEventHandlers(onClick, handleClick), _extends2.tabIndex = activeIndex === index ? 0 : -1, _extends2), rest);
4128
+ }, [dispatch, elementIds, activeIndex, itemRefs]);
4123
4129
  var getTagRemoveProps = useCallback(function (options) {
4124
4130
  var index = options.index,
4125
4131
  onClick = options.onClick,