downshift 5.1.0 → 5.2.2

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.
@@ -626,7 +626,8 @@
626
626
 
627
627
  function getA11yStatusMessage(_ref2) {
628
628
  var isOpen = _ref2.isOpen,
629
- resultCount = _ref2.resultCount;
629
+ resultCount = _ref2.resultCount,
630
+ previousResultCount = _ref2.previousResultCount;
630
631
 
631
632
  if (!isOpen) {
632
633
  return '';
@@ -636,7 +637,11 @@
636
637
  return 'No results are available.';
637
638
  }
638
639
 
639
- return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter key to select.";
640
+ if (resultCount !== previousResultCount) {
641
+ return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter key to select.";
642
+ }
643
+
644
+ return '';
640
645
  }
641
646
  /**
642
647
  * Takes an argument and if it's an array, returns the first item in the array
@@ -2078,6 +2083,65 @@
2078
2083
  inputValue: ''
2079
2084
  };
2080
2085
 
2086
+ function callOnChangeProps(action, state, newState) {
2087
+ var props = action.props,
2088
+ type = action.type;
2089
+ var changes = {};
2090
+ Object.keys(state).forEach(function (key) {
2091
+ invokeOnChangeHandler(key, props, state, newState);
2092
+
2093
+ if (newState[key] !== state[key]) {
2094
+ changes[key] = newState[key];
2095
+ }
2096
+ });
2097
+
2098
+ if (props.onStateChange && Object.keys(changes).length) {
2099
+ props.onStateChange(_extends({
2100
+ type: type
2101
+ }, changes));
2102
+ }
2103
+ }
2104
+
2105
+ function invokeOnChangeHandler(key, props, state, newState) {
2106
+ var handler = "on" + capitalizeString(key) + "Change";
2107
+
2108
+ if (props[handler] && newState[key] !== undefined && newState[key] !== state[key]) {
2109
+ props[handler](newState);
2110
+ }
2111
+ }
2112
+ /**
2113
+ * Default state reducer that returns the changes.
2114
+ *
2115
+ * @param {Object} s state.
2116
+ * @param {Object} a action with changes.
2117
+ * @returns {Object} changes.
2118
+ */
2119
+
2120
+
2121
+ function stateReducer(s, a) {
2122
+ return a.changes;
2123
+ }
2124
+ /**
2125
+ * Returns a message to be added to aria-live region when item is selected.
2126
+ *
2127
+ * @param {Object} selectionParameters Parameters required to build the message.
2128
+ * @returns {string} The a11y message.
2129
+ */
2130
+
2131
+
2132
+ function getA11ySelectionMessage(selectionParameters) {
2133
+ var selectedItem = selectionParameters.selectedItem,
2134
+ itemToStringLocal = selectionParameters.itemToString;
2135
+ return selectedItem ? itemToStringLocal(selectedItem) + " has been selected." : '';
2136
+ }
2137
+ /**
2138
+ * Debounced call for updating the a11y message.
2139
+ */
2140
+
2141
+
2142
+ var updateA11yStatus = debounce(function (getA11yMessage, document) {
2143
+ setStatus(getA11yMessage(), document);
2144
+ }, 200);
2081
2145
  function getElementIds(_ref) {
2082
2146
  var id = _ref.id,
2083
2147
  labelId = _ref.labelId,
@@ -2094,7 +2158,6 @@
2094
2158
  toggleButtonId: toggleButtonId || uniqueId + "-toggle-button"
2095
2159
  };
2096
2160
  }
2097
-
2098
2161
  function getItemIndex(index, item, items) {
2099
2162
  if (index !== undefined) {
2100
2163
  return index;
@@ -2123,57 +2186,39 @@
2123
2186
  });
2124
2187
  };
2125
2188
  }
2126
-
2127
2189
  function isAcceptedCharacterKey(key) {
2128
2190
  return /^\S{1}$/.test(key);
2129
2191
  }
2130
-
2131
2192
  function capitalizeString(string) {
2132
2193
  return "" + string.slice(0, 1).toUpperCase() + string.slice(1);
2133
2194
  }
2195
+ /**
2196
+ * Computes the controlled state using a the previous state, props,
2197
+ * two reducers, one from downshift and an optional one from the user.
2198
+ * Also calls the onChange handlers for state values that have changed.
2199
+ *
2200
+ * @param {Function} reducer Reducer function from downshift.
2201
+ * @param {Object} initialState Initial state of the hook.
2202
+ * @param {Object} props The hook props.
2203
+ * @returns {Array} An array with the state and an action dispatcher.
2204
+ */
2134
2205
 
2135
- function invokeOnChangeHandler(key, props, state, newState) {
2136
- var handler = "on" + capitalizeString(key) + "Change";
2137
-
2138
- if (props[handler] && newState[key] !== undefined && newState[key] !== state[key]) {
2139
- props[handler](newState);
2140
- }
2141
- }
2142
-
2143
- function callOnChangeProps(action, state, newState) {
2144
- var props = action.props,
2145
- type = action.type;
2146
- var changes = {};
2147
- Object.keys(state).forEach(function (key) {
2148
- invokeOnChangeHandler(key, props, state, newState);
2149
-
2150
- if (newState[key] !== state[key]) {
2151
- changes[key] = newState[key];
2152
- }
2153
- });
2206
+ function useControlledState(reducer, initialState, props) {
2207
+ var _useState = preact.useState(initialState),
2208
+ uncontrolledState = _useState[0],
2209
+ setState = _useState[1];
2154
2210
 
2155
- if (props.onStateChange && Object.keys(changes).length) {
2156
- props.onStateChange(_extends({
2157
- type: type
2158
- }, changes));
2159
- }
2160
- }
2211
+ var state = getState(uncontrolledState, props);
2161
2212
 
2162
- function useEnhancedReducer(reducer, initialState, props) {
2163
- var enhancedReducer = preact.useCallback(function (state, action) {
2164
- state = getState(state, action.props);
2165
- var stateReduceLocal = action.props.stateReducer;
2213
+ var dispatch = function (action) {
2214
+ var stateReducerFromProps = action.props.stateReducer;
2166
2215
  var changes = reducer(state, action);
2167
- var newState = stateReduceLocal(state, _extends({}, action, {
2216
+ var newState = stateReducerFromProps(state, _extends({}, action, {
2168
2217
  changes: changes
2169
2218
  }));
2170
2219
  callOnChangeProps(action, state, newState);
2171
- return newState;
2172
- }, [reducer]);
2173
-
2174
- var _useReducer = preact.useReducer(enhancedReducer, initialState),
2175
- state = _useReducer[0],
2176
- dispatch = _useReducer[1];
2220
+ setState(newState);
2221
+ };
2177
2222
 
2178
2223
  return [getState(state, props), function dispatchWithProps(action) {
2179
2224
  return dispatch(_extends({
@@ -2181,32 +2226,6 @@
2181
2226
  }, action));
2182
2227
  }];
2183
2228
  }
2184
- /**
2185
- * Default state reducer that returns the changes.
2186
- *
2187
- * @param {Object} s state.
2188
- * @param {Object} a action with changes.
2189
- * @returns {Object} changes.
2190
- */
2191
-
2192
-
2193
- function stateReducer(s, a) {
2194
- return a.changes;
2195
- }
2196
- /**
2197
- * Returns a message to be added to aria-live region when item is selected.
2198
- *
2199
- * @param {Object} selectionParameters Parameters required to build the message.
2200
- * @returns {string} The a11y message.
2201
- */
2202
-
2203
-
2204
- function getA11ySelectionMessage(selectionParameters) {
2205
- var selectedItem = selectionParameters.selectedItem,
2206
- itemToStringLocal = selectionParameters.itemToString;
2207
- return itemToStringLocal(selectedItem) + " has been selected.";
2208
- }
2209
-
2210
2229
  var defaultProps = {
2211
2230
  itemToString: itemToString,
2212
2231
  stateReducer: stateReducer,
@@ -2217,7 +2236,6 @@
2217
2236
  /* istanbul ignore next (ssr) */
2218
2237
  ? {} : window
2219
2238
  };
2220
-
2221
2239
  function getDefaultValue(props, propKey, defaultStateValues) {
2222
2240
  if (defaultStateValues === void 0) {
2223
2241
  defaultStateValues = dropdownDefaultStateValues;
@@ -2231,7 +2249,6 @@
2231
2249
 
2232
2250
  return defaultStateValues[propKey];
2233
2251
  }
2234
-
2235
2252
  function getInitialValue(props, propKey, defaultStateValues) {
2236
2253
  if (defaultStateValues === void 0) {
2237
2254
  defaultStateValues = dropdownDefaultStateValues;
@@ -2249,7 +2266,6 @@
2249
2266
 
2250
2267
  return getDefaultValue(props, propKey, defaultStateValues);
2251
2268
  }
2252
-
2253
2269
  function getInitialState(props) {
2254
2270
  var selectedItem = getInitialValue(props, 'selectedItem');
2255
2271
  var isOpen = getInitialValue(props, 'isOpen');
@@ -2262,7 +2278,6 @@
2262
2278
  inputValue: inputValue
2263
2279
  };
2264
2280
  }
2265
-
2266
2281
  function getHighlightedIndexOnOpen(props, state, offset, getItemNodeFromIndex) {
2267
2282
  var items = props.items,
2268
2283
  initialHighlightedIndex = props.initialHighlightedIndex,
@@ -2369,7 +2384,8 @@
2369
2384
 
2370
2385
  function getA11yStatusMessage$1(_ref) {
2371
2386
  var isOpen = _ref.isOpen,
2372
- resultCount = _ref.resultCount;
2387
+ resultCount = _ref.resultCount,
2388
+ previousResultCount = _ref.previousResultCount;
2373
2389
 
2374
2390
  if (!isOpen) {
2375
2391
  return '';
@@ -2379,7 +2395,11 @@
2379
2395
  return 'No results are available.';
2380
2396
  }
2381
2397
 
2382
- return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter or Space Bar keys to select.";
2398
+ if (resultCount !== previousResultCount) {
2399
+ return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter or Space Bar keys to select.";
2400
+ }
2401
+
2402
+ return '';
2383
2403
  }
2384
2404
 
2385
2405
  var defaultProps$1 = _extends({}, defaultProps, {
@@ -2626,82 +2646,91 @@
2626
2646
  var props = _extends({}, defaultProps$1, {}, userProps);
2627
2647
 
2628
2648
  var items = props.items,
2629
- itemToString = props.itemToString,
2630
- getA11yStatusMessage = props.getA11yStatusMessage,
2631
- getA11ySelectionMessage = props.getA11ySelectionMessage,
2632
2649
  scrollIntoView = props.scrollIntoView,
2633
2650
  environment = props.environment,
2634
2651
  initialIsOpen = props.initialIsOpen,
2635
- defaultIsOpen = props.defaultIsOpen; // Initial state depending on controlled props.
2652
+ defaultIsOpen = props.defaultIsOpen,
2653
+ itemToString = props.itemToString,
2654
+ getA11ySelectionMessage = props.getA11ySelectionMessage,
2655
+ getA11yStatusMessage = props.getA11yStatusMessage; // Initial state depending on controlled props.
2636
2656
 
2637
2657
  var initialState = getInitialState(props); // Reducer init.
2638
2658
 
2639
- var _useEnhancedReducer = useEnhancedReducer(downshiftSelectReducer, initialState, props),
2640
- _useEnhancedReducer$ = _useEnhancedReducer[0],
2641
- isOpen = _useEnhancedReducer$.isOpen,
2642
- highlightedIndex = _useEnhancedReducer$.highlightedIndex,
2643
- selectedItem = _useEnhancedReducer$.selectedItem,
2644
- inputValue = _useEnhancedReducer$.inputValue,
2645
- dispatch = _useEnhancedReducer[1];
2646
- /* Refs */
2659
+ var _useControlledState = useControlledState(downshiftSelectReducer, initialState, props),
2660
+ _useControlledState$ = _useControlledState[0],
2661
+ isOpen = _useControlledState$.isOpen,
2662
+ highlightedIndex = _useControlledState$.highlightedIndex,
2663
+ selectedItem = _useControlledState$.selectedItem,
2664
+ inputValue = _useControlledState$.inputValue,
2665
+ dispatch = _useControlledState[1]; // Refs
2647
2666
 
2648
2667
 
2649
2668
  var toggleButtonRef = preact.useRef(null);
2650
2669
  var menuRef = preact.useRef(null);
2651
- var isInitialMount = preact.useRef(true);
2652
- var shouldScroll = preact.useRef(true);
2653
- var clearTimeout = preact.useRef(null);
2654
- var mouseAndTouchTrackers = preact.useRef({
2670
+ var isInitialMountRef = preact.useRef(true);
2671
+ var shouldScrollRef = preact.useRef(true);
2672
+ var shouldBlurRef = preact.useRef(true);
2673
+ var clearTimeoutRef = preact.useRef(null);
2674
+ var mouseAndTouchTrackersRef = preact.useRef({
2655
2675
  isMouseDown: false,
2656
2676
  isTouchMove: false
2657
2677
  });
2658
- var elementIds = preact.useRef(getElementIds(props)); // Some utils.
2678
+ var elementIdsRef = preact.useRef(getElementIds(props));
2679
+ var previousResultCountRef = preact.useRef(); // Some utils.
2659
2680
 
2660
2681
  var getItemNodeFromIndex = function (index) {
2661
- return environment.document.getElementById(elementIds.current.getItemId(index));
2682
+ return environment.document.getElementById(elementIdsRef.current.getItemId(index));
2662
2683
  }; // Effects.
2663
2684
 
2664
- /* Sets a11y status message on changes in isOpen. */
2685
+ /* Sets a11y status message on changes in state. */
2665
2686
 
2666
2687
 
2667
2688
  preact.useEffect(function () {
2668
- if (isInitialMount.current) {
2689
+ if (isInitialMountRef.current) {
2669
2690
  return;
2670
2691
  }
2671
2692
 
2672
- setStatus(getA11yStatusMessage({
2673
- highlightedIndex: highlightedIndex,
2674
- inputValue: inputValue,
2675
- isOpen: isOpen,
2676
- itemToString: itemToString,
2677
- resultCount: items.length,
2678
- highlightedItem: items[highlightedIndex],
2679
- selectedItem: selectedItem
2680
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
2681
- }, [isOpen]);
2693
+ var previousResultCount = previousResultCountRef.current;
2694
+ updateA11yStatus(function () {
2695
+ return getA11yStatusMessage({
2696
+ isOpen: isOpen,
2697
+ highlightedIndex: highlightedIndex,
2698
+ selectedItem: selectedItem,
2699
+ inputValue: inputValue,
2700
+ highlightedItem: items[highlightedIndex],
2701
+ resultCount: items.length,
2702
+ itemToString: itemToString,
2703
+ previousResultCount: previousResultCount
2704
+ });
2705
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
2706
+ }, [isOpen, highlightedIndex, selectedItem, inputValue]);
2682
2707
  /* Sets a11y status message on changes in selectedItem. */
2683
2708
 
2684
2709
  preact.useEffect(function () {
2685
- if (isInitialMount.current) {
2710
+ if (isInitialMountRef.current) {
2686
2711
  return;
2687
2712
  }
2688
2713
 
2689
- setStatus(getA11ySelectionMessage({
2690
- highlightedIndex: highlightedIndex,
2691
- inputValue: inputValue,
2692
- isOpen: isOpen,
2693
- itemToString: itemToString,
2694
- resultCount: items.length,
2695
- highlightedItem: items[highlightedIndex],
2696
- selectedItem: selectedItem
2697
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
2714
+ var previousResultCount = previousResultCountRef.current;
2715
+ updateA11yStatus(function () {
2716
+ return getA11ySelectionMessage({
2717
+ isOpen: isOpen,
2718
+ highlightedIndex: highlightedIndex,
2719
+ selectedItem: selectedItem,
2720
+ inputValue: inputValue,
2721
+ highlightedItem: items[highlightedIndex],
2722
+ resultCount: items.length,
2723
+ itemToString: itemToString,
2724
+ previousResultCount: previousResultCount
2725
+ });
2726
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
2698
2727
  }, [selectedItem]);
2699
2728
  /* Sets cleanup for the keysSoFar after 500ms. */
2700
2729
 
2701
2730
  preact.useEffect(function () {
2702
2731
  // init the clean function here as we need access to dispatch.
2703
- if (isInitialMount.current) {
2704
- clearTimeout.current = debounce(function (outerDispatch) {
2732
+ if (isInitialMountRef.current) {
2733
+ clearTimeoutRef.current = debounce(function (outerDispatch) {
2705
2734
  outerDispatch({
2706
2735
  type: FunctionSetInputValue,
2707
2736
  inputValue: ''
@@ -2713,13 +2742,13 @@
2713
2742
  return;
2714
2743
  }
2715
2744
 
2716
- clearTimeout.current(dispatch); // eslint-disable-next-line react-hooks/exhaustive-deps
2745
+ clearTimeoutRef.current(dispatch); // eslint-disable-next-line react-hooks/exhaustive-deps
2717
2746
  }, [inputValue]);
2718
2747
  /* Controls the focus on the menu or the toggle button. */
2719
2748
 
2720
2749
  preact.useEffect(function () {
2721
2750
  // Don't focus menu on first render.
2722
- if (isInitialMount.current) {
2751
+ if (isInitialMountRef.current) {
2723
2752
  // Unless it was initialised as open.
2724
2753
  if ((initialIsOpen || defaultIsOpen || isOpen) && menuRef.current) {
2725
2754
  menuRef.current.focus();
@@ -2727,13 +2756,23 @@
2727
2756
 
2728
2757
  return;
2729
2758
  } // Focus menu on open.
2730
- // istanbul ignore next
2731
2759
 
2732
2760
 
2733
- if (isOpen && menuRef.current) {
2734
- menuRef.current.focus(); // Focus toggleButton on close.
2735
- } else if (environment.document.activeElement === menuRef.current && toggleButtonRef.current) {
2736
- toggleButtonRef.current.focus();
2761
+ if (isOpen) {
2762
+ // istanbul ignore else
2763
+ if (menuRef.current) {
2764
+ menuRef.current.focus();
2765
+ return;
2766
+ }
2767
+ } // Focus toggleButton on close, but on if was closed with (Shift+)Tab.
2768
+
2769
+
2770
+ if (environment.document.activeElement === menuRef.current) {
2771
+ // istanbul ignore else
2772
+ if (toggleButtonRef.current) {
2773
+ shouldBlurRef.current = false;
2774
+ toggleButtonRef.current.focus();
2775
+ }
2737
2776
  } // eslint-disable-next-line react-hooks/exhaustive-deps
2738
2777
 
2739
2778
  }, [isOpen]);
@@ -2744,17 +2783,24 @@
2744
2783
  return;
2745
2784
  }
2746
2785
 
2747
- if (shouldScroll.current === false) {
2748
- shouldScroll.current = true;
2786
+ if (shouldScrollRef.current === false) {
2787
+ shouldScrollRef.current = true;
2749
2788
  } else {
2750
2789
  scrollIntoView(getItemNodeFromIndex(highlightedIndex), menuRef.current);
2751
2790
  } // eslint-disable-next-line react-hooks/exhaustive-deps
2752
2791
 
2753
2792
  }, [highlightedIndex]);
2793
+ preact.useEffect(function () {
2794
+ if (isInitialMountRef.current) {
2795
+ return;
2796
+ }
2797
+
2798
+ previousResultCountRef.current = items.length;
2799
+ });
2754
2800
  /* Make initial ref false. */
2755
2801
 
2756
2802
  preact.useEffect(function () {
2757
- isInitialMount.current = false;
2803
+ isInitialMountRef.current = false;
2758
2804
  }, []);
2759
2805
  /* Add mouse/touch events to document. */
2760
2806
 
@@ -2762,11 +2808,11 @@
2762
2808
  // The same strategy for checking if a click occurred inside or outside downsift
2763
2809
  // as in downshift.js.
2764
2810
  var onMouseDown = function () {
2765
- mouseAndTouchTrackers.current.isMouseDown = true;
2811
+ mouseAndTouchTrackersRef.current.isMouseDown = true;
2766
2812
  };
2767
2813
 
2768
2814
  var onMouseUp = function (event) {
2769
- mouseAndTouchTrackers.current.isMouseDown = false;
2815
+ mouseAndTouchTrackersRef.current.isMouseDown = false;
2770
2816
 
2771
2817
  if (isOpen && !targetWithinDownshift(event.target, [toggleButtonRef.current, menuRef.current], environment.document)) {
2772
2818
  dispatch({
@@ -2776,15 +2822,15 @@
2776
2822
  };
2777
2823
 
2778
2824
  var onTouchStart = function () {
2779
- mouseAndTouchTrackers.current.isTouchMove = false;
2825
+ mouseAndTouchTrackersRef.current.isTouchMove = false;
2780
2826
  };
2781
2827
 
2782
2828
  var onTouchMove = function () {
2783
- mouseAndTouchTrackers.current.isTouchMove = true;
2829
+ mouseAndTouchTrackersRef.current.isTouchMove = true;
2784
2830
  };
2785
2831
 
2786
2832
  var onTouchEnd = function (event) {
2787
- if (isOpen && !mouseAndTouchTrackers.current.isTouchMove && !targetWithinDownshift(event.target, [toggleButtonRef.current, menuRef.current], environment.document, false)) {
2833
+ if (isOpen && !mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, [toggleButtonRef.current, menuRef.current], environment.document, false)) {
2788
2834
  dispatch({
2789
2835
  type: MenuBlur
2790
2836
  });
@@ -2888,7 +2934,13 @@
2888
2934
  };
2889
2935
 
2890
2936
  var menuHandleBlur = function () {
2891
- var shouldBlur = !mouseAndTouchTrackers.current.isMouseDown;
2937
+ // if the blur was a result of selection, we don't trigger this action.
2938
+ if (shouldBlurRef.current === false) {
2939
+ shouldBlurRef.current = true;
2940
+ return;
2941
+ }
2942
+
2943
+ var shouldBlur = !mouseAndTouchTrackersRef.current.isMouseDown;
2892
2944
  /* istanbul ignore else */
2893
2945
 
2894
2946
  if (shouldBlur) {
@@ -2929,7 +2981,7 @@
2929
2981
  return;
2930
2982
  }
2931
2983
 
2932
- shouldScroll.current = false;
2984
+ shouldScrollRef.current = false;
2933
2985
  dispatch({
2934
2986
  type: ItemMouseMove,
2935
2987
  index: index
@@ -2959,7 +3011,7 @@
2959
3011
 
2960
3012
  var toggleProps = _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (toggleButtonNode) {
2961
3013
  toggleButtonRef.current = toggleButtonNode;
2962
- }), _extends3.id = elementIds.current.toggleButtonId, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-expanded'] = isOpen, _extends3['aria-labelledby'] = elementIds.current.labelId + " " + elementIds.current.toggleButtonId, _extends3), rest);
3014
+ }), _extends3.id = elementIdsRef.current.toggleButtonId, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-expanded'] = isOpen, _extends3['aria-labelledby'] = elementIdsRef.current.labelId + " " + elementIdsRef.current.toggleButtonId, _extends3), rest);
2963
3015
 
2964
3016
  if (!rest.disabled) {
2965
3017
  toggleProps.onClick = callAllEventHandlers(onClick, toggleButtonHandleClick);
@@ -2970,8 +3022,8 @@
2970
3022
  },
2971
3023
  getLabelProps: function getLabelProps(labelProps) {
2972
3024
  return _extends({
2973
- id: elementIds.current.labelId,
2974
- htmlFor: elementIds.current.toggleButtonId
3025
+ id: elementIdsRef.current.labelId,
3026
+ htmlFor: elementIdsRef.current.toggleButtonId
2975
3027
  }, labelProps);
2976
3028
  },
2977
3029
  getMenuProps: function getMenuProps(_temp) {
@@ -2988,8 +3040,8 @@
2988
3040
 
2989
3041
  return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (menuNode) {
2990
3042
  menuRef.current = menuNode;
2991
- }), _extends2.id = elementIds.current.menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = elementIds.current.labelId, _extends2.tabIndex = -1, _extends2), isOpen && highlightedIndex > -1 && {
2992
- 'aria-activedescendant': elementIds.current.getItemId(highlightedIndex)
3043
+ }), _extends2.id = elementIdsRef.current.menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = elementIdsRef.current.labelId, _extends2.tabIndex = -1, _extends2), isOpen && highlightedIndex > -1 && {
3044
+ 'aria-activedescendant': elementIdsRef.current.getItemId(highlightedIndex)
2993
3045
  }, {
2994
3046
  onMouseLeave: callAllEventHandlers(onMouseLeave, menuHandleMouseLeave),
2995
3047
  onKeyDown: callAllEventHandlers(onKeyDown, menuHandleKeyDown),
@@ -3013,7 +3065,7 @@
3013
3065
  var itemProps = _extends({
3014
3066
  role: 'option',
3015
3067
  'aria-selected': "" + (itemIndex === highlightedIndex),
3016
- id: elementIds.current.getItemId(itemIndex)
3068
+ id: elementIdsRef.current.getItemId(itemIndex)
3017
3069
  }, rest);
3018
3070
 
3019
3071
  if (!rest.disabled) {
@@ -3074,6 +3126,51 @@
3074
3126
  };
3075
3127
  }
3076
3128
 
3129
+ var InputKeyDownArrowDown = '__input_keydown_arrow_down__';
3130
+ var InputKeyDownArrowUp = '__input_keydown_arrow_up__';
3131
+ var InputKeyDownEscape = '__input_keydown_escape__';
3132
+ var InputKeyDownHome = '__input_keydown_home__';
3133
+ var InputKeyDownEnd = '__input_keydown_end__';
3134
+ var InputKeyDownEnter = '__input_keydown_enter__';
3135
+ var InputChange = '__input_change__';
3136
+ var InputBlur = '__input_blur__';
3137
+ var MenuMouseLeave$1 = '__menu_mouse_leave__';
3138
+ var ItemMouseMove$1 = '__item_mouse_move__';
3139
+ var ItemClick$1 = '__item_click__';
3140
+ var ToggleButtonClick$1 = '__togglebutton_click__';
3141
+ var FunctionToggleMenu$1 = '__function_toggle_menu__';
3142
+ var FunctionOpenMenu$1 = '__function_open_menu__';
3143
+ var FunctionCloseMenu$1 = '__function_close_menu__';
3144
+ var FunctionSetHighlightedIndex$1 = '__function_set_highlighted_index__';
3145
+ var FunctionSelectItem$1 = '__function_select_item__';
3146
+ var FunctionSetInputValue$1 = '__function_set_input_value__';
3147
+ var FunctionReset$1 = '__function_reset__';
3148
+ var ControlledPropUpdatedSelectedItem = '__controlled_prop_updated_selected_item__';
3149
+
3150
+ var stateChangeTypes$2 = /*#__PURE__*/Object.freeze({
3151
+ __proto__: null,
3152
+ InputKeyDownArrowDown: InputKeyDownArrowDown,
3153
+ InputKeyDownArrowUp: InputKeyDownArrowUp,
3154
+ InputKeyDownEscape: InputKeyDownEscape,
3155
+ InputKeyDownHome: InputKeyDownHome,
3156
+ InputKeyDownEnd: InputKeyDownEnd,
3157
+ InputKeyDownEnter: InputKeyDownEnter,
3158
+ InputChange: InputChange,
3159
+ InputBlur: InputBlur,
3160
+ MenuMouseLeave: MenuMouseLeave$1,
3161
+ ItemMouseMove: ItemMouseMove$1,
3162
+ ItemClick: ItemClick$1,
3163
+ ToggleButtonClick: ToggleButtonClick$1,
3164
+ FunctionToggleMenu: FunctionToggleMenu$1,
3165
+ FunctionOpenMenu: FunctionOpenMenu$1,
3166
+ FunctionCloseMenu: FunctionCloseMenu$1,
3167
+ FunctionSetHighlightedIndex: FunctionSetHighlightedIndex$1,
3168
+ FunctionSelectItem: FunctionSelectItem$1,
3169
+ FunctionSetInputValue: FunctionSetInputValue$1,
3170
+ FunctionReset: FunctionReset$1,
3171
+ ControlledPropUpdatedSelectedItem: ControlledPropUpdatedSelectedItem
3172
+ });
3173
+
3077
3174
  function getElementIds$1(_ref) {
3078
3175
  var id = _ref.id,
3079
3176
  inputId = _ref.inputId,
@@ -3086,7 +3183,6 @@
3086
3183
  id: id
3087
3184
  }, rest)));
3088
3185
  }
3089
-
3090
3186
  function getInitialState$1(props) {
3091
3187
  var initialState = getInitialState(props);
3092
3188
  var selectedItem = initialState.selectedItem;
@@ -3100,7 +3196,6 @@
3100
3196
  inputValue: inputValue
3101
3197
  });
3102
3198
  }
3103
-
3104
3199
  var propTypes$1 = {
3105
3200
  items: PropTypes.array.isRequired,
3106
3201
  itemToString: PropTypes.func,
@@ -3141,55 +3236,43 @@
3141
3236
  })
3142
3237
  })
3143
3238
  };
3239
+ /**
3240
+ * The useCombobox version of useControlledState, which also
3241
+ * checks if the controlled prop selectedItem changed between
3242
+ * renders. If so, it will also update inputValue with its
3243
+ * string equivalent. It uses the common useControlledState to
3244
+ * compute the rest of the state.
3245
+ *
3246
+ * @param {Function} reducer Reducer function from downshift.
3247
+ * @param {Object} initialState Initial state of the hook.
3248
+ * @param {Object} props The hook props.
3249
+ * @returns {Array} An array with the state and an action dispatcher.
3250
+ */
3251
+
3252
+ function useControlledState$1(reducer, initialState, props) {
3253
+ var _useControlledStateCo = useControlledState(reducer, initialState, props),
3254
+ newState = _useControlledStateCo[0],
3255
+ dispatch = _useControlledStateCo[1];
3256
+
3257
+ var previousSelectedItemRef = preact.useRef(null);
3258
+ var selectedItem = props.selectedItem,
3259
+ itemToString = props.itemToString; // ToDo: if needed, make same approach as selectedItemChanged from Downshift.
3144
3260
 
3261
+ if (isControlledProp(props, 'selectedItem') && previousSelectedItemRef.current !== selectedItem) {
3262
+ dispatch({
3263
+ type: ControlledPropUpdatedSelectedItem,
3264
+ inputValue: itemToString(selectedItem)
3265
+ });
3266
+ }
3267
+
3268
+ previousSelectedItemRef.current = selectedItem;
3269
+ return [newState, dispatch];
3270
+ }
3145
3271
  var defaultProps$2 = _extends({}, defaultProps, {
3146
3272
  getA11yStatusMessage: getA11yStatusMessage,
3147
3273
  circularNavigation: true
3148
3274
  });
3149
3275
 
3150
- var InputKeyDownArrowDown = '__input_keydown_arrow_down__';
3151
- var InputKeyDownArrowUp = '__input_keydown_arrow_up__';
3152
- var InputKeyDownEscape = '__input_keydown_escape__';
3153
- var InputKeyDownHome = '__input_keydown_home__';
3154
- var InputKeyDownEnd = '__input_keydown_end__';
3155
- var InputKeyDownEnter = '__input_keydown_enter__';
3156
- var InputChange = '__input_change__';
3157
- var InputBlur = '__input_blur__';
3158
- var MenuMouseLeave$1 = '__menu_mouse_leave__';
3159
- var ItemMouseMove$1 = '__item_mouse_move__';
3160
- var ItemClick$1 = '__item_click__';
3161
- var ToggleButtonClick$1 = '__togglebutton_click__';
3162
- var FunctionToggleMenu$1 = '__function_toggle_menu__';
3163
- var FunctionOpenMenu$1 = '__function_open_menu__';
3164
- var FunctionCloseMenu$1 = '__function_close_menu__';
3165
- var FunctionSetHighlightedIndex$1 = '__function_set_highlighted_index__';
3166
- var FunctionSelectItem$1 = '__function_select_item__';
3167
- var FunctionSetInputValue$1 = '__function_set_input_value__';
3168
- var FunctionReset$1 = '__function_reset__';
3169
-
3170
- var stateChangeTypes$2 = /*#__PURE__*/Object.freeze({
3171
- __proto__: null,
3172
- InputKeyDownArrowDown: InputKeyDownArrowDown,
3173
- InputKeyDownArrowUp: InputKeyDownArrowUp,
3174
- InputKeyDownEscape: InputKeyDownEscape,
3175
- InputKeyDownHome: InputKeyDownHome,
3176
- InputKeyDownEnd: InputKeyDownEnd,
3177
- InputKeyDownEnter: InputKeyDownEnter,
3178
- InputChange: InputChange,
3179
- InputBlur: InputBlur,
3180
- MenuMouseLeave: MenuMouseLeave$1,
3181
- ItemMouseMove: ItemMouseMove$1,
3182
- ItemClick: ItemClick$1,
3183
- ToggleButtonClick: ToggleButtonClick$1,
3184
- FunctionToggleMenu: FunctionToggleMenu$1,
3185
- FunctionOpenMenu: FunctionOpenMenu$1,
3186
- FunctionCloseMenu: FunctionCloseMenu$1,
3187
- FunctionSetHighlightedIndex: FunctionSetHighlightedIndex$1,
3188
- FunctionSelectItem: FunctionSelectItem$1,
3189
- FunctionSetInputValue: FunctionSetInputValue$1,
3190
- FunctionReset: FunctionReset$1
3191
- });
3192
-
3193
3276
  /* eslint-disable complexity */
3194
3277
 
3195
3278
  function downshiftUseComboboxReducer(state, action) {
@@ -3329,6 +3412,7 @@
3329
3412
  };
3330
3413
  break;
3331
3414
 
3415
+ case ControlledPropUpdatedSelectedItem:
3332
3416
  case FunctionSetInputValue$1:
3333
3417
  changes = {
3334
3418
  inputValue: action.inputValue
@@ -3369,20 +3453,20 @@
3369
3453
  defaultIsOpen = props.defaultIsOpen,
3370
3454
  items = props.items,
3371
3455
  scrollIntoView = props.scrollIntoView,
3372
- getA11ySelectionMessage = props.getA11ySelectionMessage,
3456
+ environment = props.environment,
3373
3457
  getA11yStatusMessage = props.getA11yStatusMessage,
3374
- itemToString = props.itemToString,
3375
- environment = props.environment; // Initial state depending on controlled props.
3458
+ getA11ySelectionMessage = props.getA11ySelectionMessage,
3459
+ itemToString = props.itemToString; // Initial state depending on controlled props.
3376
3460
 
3377
3461
  var initialState = getInitialState$1(props); // Reducer init.
3378
3462
 
3379
- var _useEnhancedReducer = useEnhancedReducer(downshiftUseComboboxReducer, initialState, props),
3380
- _useEnhancedReducer$ = _useEnhancedReducer[0],
3381
- isOpen = _useEnhancedReducer$.isOpen,
3382
- highlightedIndex = _useEnhancedReducer$.highlightedIndex,
3383
- selectedItem = _useEnhancedReducer$.selectedItem,
3384
- inputValue = _useEnhancedReducer$.inputValue,
3385
- dispatch = _useEnhancedReducer[1];
3463
+ var _useControlledState = useControlledState$1(downshiftUseComboboxReducer, initialState, props),
3464
+ _useControlledState$ = _useControlledState[0],
3465
+ isOpen = _useControlledState$.isOpen,
3466
+ highlightedIndex = _useControlledState$.highlightedIndex,
3467
+ selectedItem = _useControlledState$.selectedItem,
3468
+ inputValue = _useControlledState$.inputValue,
3469
+ dispatch = _useControlledState[1];
3386
3470
  /* Refs */
3387
3471
 
3388
3472
 
@@ -3399,25 +3483,30 @@
3399
3483
  isTouchMove: false
3400
3484
  });
3401
3485
  var elementIds = preact.useRef(getElementIds$1(props));
3486
+ var previousResultCountRef = preact.useRef();
3402
3487
  /* Effects */
3403
3488
 
3404
- /* Sets a11y status message on changes in isOpen. */
3489
+ /* Sets a11y status message on changes in state. */
3405
3490
 
3406
3491
  preact.useEffect(function () {
3407
3492
  if (isInitialMount.current) {
3408
3493
  return;
3409
3494
  }
3410
3495
 
3411
- setStatus(getA11yStatusMessage({
3412
- highlightedIndex: highlightedIndex,
3413
- inputValue: inputValue,
3414
- isOpen: isOpen,
3415
- itemToString: itemToString,
3416
- resultCount: items.length,
3417
- highlightedItem: items[highlightedIndex],
3418
- selectedItem: selectedItem
3419
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3420
- }, [isOpen]);
3496
+ var previousResultCount = previousResultCountRef.current;
3497
+ updateA11yStatus(function () {
3498
+ return getA11yStatusMessage({
3499
+ isOpen: isOpen,
3500
+ highlightedIndex: highlightedIndex,
3501
+ selectedItem: selectedItem,
3502
+ inputValue: inputValue,
3503
+ highlightedItem: items[highlightedIndex],
3504
+ resultCount: items.length,
3505
+ itemToString: itemToString,
3506
+ previousResultCount: previousResultCount
3507
+ });
3508
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3509
+ }, [isOpen, highlightedIndex, selectedItem, inputValue]);
3421
3510
  /* Sets a11y status message on changes in selectedItem. */
3422
3511
 
3423
3512
  preact.useEffect(function () {
@@ -3425,15 +3514,19 @@
3425
3514
  return;
3426
3515
  }
3427
3516
 
3428
- setStatus(getA11ySelectionMessage({
3429
- highlightedIndex: highlightedIndex,
3430
- inputValue: inputValue,
3431
- isOpen: isOpen,
3432
- itemToString: itemToString,
3433
- resultCount: items.length,
3434
- highlightedItem: items[highlightedIndex],
3435
- selectedItem: selectedItem
3436
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3517
+ var previousResultCount = previousResultCountRef.current;
3518
+ updateA11yStatus(function () {
3519
+ return getA11ySelectionMessage({
3520
+ isOpen: isOpen,
3521
+ highlightedIndex: highlightedIndex,
3522
+ selectedItem: selectedItem,
3523
+ inputValue: inputValue,
3524
+ highlightedItem: items[highlightedIndex],
3525
+ resultCount: items.length,
3526
+ itemToString: itemToString,
3527
+ previousResultCount: previousResultCount
3528
+ });
3529
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3437
3530
  }, [selectedItem]);
3438
3531
  /* Scroll on highlighted item if change comes from keyboard. */
3439
3532
 
@@ -3463,8 +3556,13 @@
3463
3556
  } // eslint-disable-next-line react-hooks/exhaustive-deps
3464
3557
 
3465
3558
  }, [isOpen]);
3466
- /* Make initial ref false. */
3559
+ preact.useEffect(function () {
3560
+ if (isInitialMount.current) {
3561
+ return;
3562
+ }
3467
3563
 
3564
+ previousResultCountRef.current = items.length;
3565
+ });
3468
3566
  preact.useEffect(function () {
3469
3567
  isInitialMount.current = false;
3470
3568
  }, []);
@@ -4095,11 +4193,11 @@
4095
4193
  keyNavigationNext = props.keyNavigationNext,
4096
4194
  keyNavigationPrevious = props.keyNavigationPrevious; // Reducer init.
4097
4195
 
4098
- var _useEnhancedReducer = useEnhancedReducer(downshiftMultipleSelectionReducer, getInitialState$2(props), props),
4099
- _useEnhancedReducer$ = _useEnhancedReducer[0],
4100
- activeIndex = _useEnhancedReducer$.activeIndex,
4101
- selectedItems = _useEnhancedReducer$.selectedItems,
4102
- dispatch = _useEnhancedReducer[1]; // Refs.
4196
+ var _useControlledState = useControlledState(downshiftMultipleSelectionReducer, getInitialState$2(props), props),
4197
+ _useControlledState$ = _useControlledState[0],
4198
+ activeIndex = _useControlledState$.activeIndex,
4199
+ selectedItems = _useControlledState$.selectedItems,
4200
+ dispatch = _useControlledState[1]; // Refs.
4103
4201
 
4104
4202
 
4105
4203
  var isInitialMount = preact.useRef(true);