@react-aria/combobox 3.14.2 → 3.15.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.
package/dist/types.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { AriaButtonProps } from "@react-types/button";
2
- import { AriaComboBoxProps } from "@react-types/combobox";
2
+ import { AriaComboBoxProps, SelectionMode } from "@react-types/combobox";
3
3
  import { AriaListBoxOptions } from "@react-aria/listbox";
4
4
  import { DOMAttributes, KeyboardDelegate, LayoutDelegate, RefObject, ValidationResult } from "@react-types/shared";
5
5
  import { ComboBoxState } from "@react-stately/combobox";
6
6
  import { InputHTMLAttributes } from "react";
7
- export interface AriaComboBoxOptions<T> extends Omit<AriaComboBoxProps<T>, 'children'> {
7
+ export interface AriaComboBoxOptions<T, M extends SelectionMode = 'single'> extends Omit<AriaComboBoxProps<T, M>, 'children'> {
8
8
  /** The ref for the input element. */
9
9
  inputRef: RefObject<HTMLInputElement | null>;
10
10
  /** The ref for the list box popover. */
@@ -31,6 +31,8 @@ export interface ComboBoxAria<T> extends ValidationResult {
31
31
  listBoxProps: AriaListBoxOptions<T>;
32
32
  /** Props for the optional trigger button, to be passed to `useButton`. */
33
33
  buttonProps: AriaButtonProps;
34
+ /** Props for the element representing the selected value. */
35
+ valueProps: DOMAttributes;
34
36
  /** Props for the combo box description element, if any. */
35
37
  descriptionProps: DOMAttributes;
36
38
  /** Props for the combo box error message element, if any. */
@@ -42,7 +44,7 @@ export interface ComboBoxAria<T> extends ValidationResult {
42
44
  * @param props - Props for the combo box.
43
45
  * @param state - State for the select, as returned by `useComboBoxState`.
44
46
  */
45
- export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxState<T>): ComboBoxAria<T>;
47
+ export function useComboBox<T, M extends SelectionMode = 'single'>(props: AriaComboBoxOptions<T, M>, state: ComboBoxState<T, M>): ComboBoxAria<T>;
46
48
  export type { AriaComboBoxProps } from '@react-types/combobox';
47
49
 
48
50
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"mappings":";;;;;;AA+BA,qCAAqC,CAAC,CAAE,SAAQ,IAAI,CAAC,kBAAkB,CAAC,CAAC,EAAE,UAAU,CAAC;IACpF,qCAAqC;IACrC,QAAQ,EAAE,UAAU,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC7C,wCAAwC;IACxC,UAAU,EAAE,UAAU,OAAO,GAAG,IAAI,CAAC,CAAC;IACtC,gCAAgC;IAChC,UAAU,EAAE,UAAU,WAAW,GAAG,IAAI,CAAC,CAAC;IAC1C,+DAA+D;IAC/D,SAAS,CAAC,EAAE,UAAU,OAAO,GAAG,IAAI,CAAC,CAAC;IACtC,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC;AAED,8BAA8B,CAAC,CAAE,SAAQ,gBAAgB;IACvD,mCAAmC;IACnC,UAAU,EAAE,aAAa,CAAC;IAC1B,6CAA6C;IAC7C,UAAU,EAAE,oBAAoB,gBAAgB,CAAC,CAAC;IAClD,4DAA4D;IAC5D,YAAY,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACpC,0EAA0E;IAC1E,WAAW,EAAE,eAAe,CAAC;IAC7B,2DAA2D;IAC3D,gBAAgB,EAAE,aAAa,CAAC;IAChC,6DAA6D;IAC7D,iBAAiB,EAAE,aAAa,CAAA;CACjC;AAED;;;;;GAKG;AACH,4BAA4B,CAAC,EAAE,KAAK,EAAE,oBAAoB,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAyUtG;ACnYD,YAAY,EAAC,iBAAiB,EAAC,MAAM,uBAAuB,CAAC","sources":["packages/@react-aria/combobox/src/packages/@react-aria/combobox/src/useComboBox.ts","packages/@react-aria/combobox/src/packages/@react-aria/combobox/src/index.ts","packages/@react-aria/combobox/src/index.ts"],"sourcesContent":[null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\nexport {useComboBox} from './useComboBox';\nexport type {AriaComboBoxOptions, ComboBoxAria} from './useComboBox';\nexport type {AriaComboBoxProps} from '@react-types/combobox';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
1
+ {"mappings":";;;;;;AAgCA,qCAAqC,CAAC,EAAE,CAAC,SAAS,aAAa,GAAG,QAAQ,CAAE,SAAQ,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC;IAC3H,qCAAqC;IACrC,QAAQ,EAAE,UAAU,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC7C,wCAAwC;IACxC,UAAU,EAAE,UAAU,OAAO,GAAG,IAAI,CAAC,CAAC;IACtC,gCAAgC;IAChC,UAAU,EAAE,UAAU,WAAW,GAAG,IAAI,CAAC,CAAC;IAC1C,+DAA+D;IAC/D,SAAS,CAAC,EAAE,UAAU,OAAO,GAAG,IAAI,CAAC,CAAC;IACtC,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC;AAED,8BAA8B,CAAC,CAAE,SAAQ,gBAAgB;IACvD,mCAAmC;IACnC,UAAU,EAAE,aAAa,CAAC;IAC1B,6CAA6C;IAC7C,UAAU,EAAE,oBAAoB,gBAAgB,CAAC,CAAC;IAClD,4DAA4D;IAC5D,YAAY,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACpC,0EAA0E;IAC1E,WAAW,EAAE,eAAe,CAAC;IAC7B,6DAA6D;IAC7D,UAAU,EAAE,aAAa,CAAC;IAC1B,2DAA2D;IAC3D,gBAAgB,EAAE,aAAa,CAAC;IAChC,6DAA6D;IAC7D,iBAAiB,EAAE,aAAa,CAAA;CACjC;AAED;;;;;GAKG;AACH,4BAA4B,CAAC,EAAE,CAAC,SAAS,aAAa,GAAG,QAAQ,EAAE,KAAK,EAAE,oBAAoB,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CA6VhJ;AC1ZD,YAAY,EAAC,iBAAiB,EAAC,MAAM,uBAAuB,CAAC","sources":["packages/@react-aria/combobox/src/packages/@react-aria/combobox/src/useComboBox.ts","packages/@react-aria/combobox/src/packages/@react-aria/combobox/src/index.ts","packages/@react-aria/combobox/src/index.ts"],"sourcesContent":[null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\nexport {useComboBox} from './useComboBox';\nexport type {AriaComboBoxOptions, ComboBoxAria} from './useComboBox';\nexport type {AriaComboBoxProps} from '@react-types/combobox';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
@@ -8,6 +8,7 @@ var $BGXP4$react = require("react");
8
8
  var $BGXP4$reactstatelycollections = require("@react-stately/collections");
9
9
  var $BGXP4$reactariaselection = require("@react-aria/selection");
10
10
  var $BGXP4$reactstatelyform = require("@react-stately/form");
11
+ var $BGXP4$reactariainteractions = require("@react-aria/interactions");
11
12
  var $BGXP4$reactariai18n = require("@react-aria/i18n");
12
13
  var $BGXP4$reactariamenu = require("@react-aria/menu");
13
14
  var $BGXP4$reactariatextfield = require("@react-aria/textfield");
@@ -45,6 +46,7 @@ $parcel$export(module.exports, "useComboBox", () => $242452271d1e4c0e$export$8c1
45
46
 
46
47
 
47
48
 
49
+
48
50
  function $242452271d1e4c0e$export$8c18d1b4f7232bbf(props, state) {
49
51
  let { buttonRef: buttonRef, popoverRef: popoverRef, inputRef: inputRef, listBoxRef: listBoxRef, keyboardDelegate: keyboardDelegate, layoutDelegate: layoutDelegate, shouldFocusWrap: // completionMode = 'suggest',
50
52
  shouldFocusWrap, isReadOnly: isReadOnly, isDisabled: isDisabled } = props;
@@ -112,7 +114,7 @@ function $242452271d1e4c0e$export$8c18d1b4f7232bbf(props, state) {
112
114
  state.commit();
113
115
  break;
114
116
  case 'Escape':
115
- if (state.selectedKey !== null || state.inputValue === '' || props.allowsCustomValue) e.continuePropagation();
117
+ if (!state.selectionManager.isEmpty || state.inputValue === '' || props.allowsCustomValue) e.continuePropagation();
116
118
  state.revert();
117
119
  break;
118
120
  case 'ArrowDown':
@@ -140,6 +142,10 @@ function $242452271d1e4c0e$export$8c18d1b4f7232bbf(props, state) {
140
142
  if (props.onFocus) props.onFocus(e);
141
143
  state.setFocused(true);
142
144
  };
145
+ let valueId = $242452271d1e4c0e$var$useValueId([
146
+ state.selectedItems,
147
+ state.selectionManager.selectionMode
148
+ ]);
143
149
  let { isInvalid: isInvalid, validationErrors: validationErrors, validationDetails: validationDetails } = state.displayValidation;
144
150
  let { labelProps: labelProps, inputProps: inputProps, descriptionProps: descriptionProps, errorMessageProps: errorMessageProps } = (0, $BGXP4$reactariatextfield.useTextField)({
145
151
  ...props,
@@ -151,9 +157,13 @@ function $242452271d1e4c0e$export$8c18d1b4f7232bbf(props, state) {
151
157
  onFocus: onFocus,
152
158
  autoComplete: 'off',
153
159
  validate: undefined,
154
- [(0, $BGXP4$reactstatelyform.privateValidationStateProp)]: state
160
+ [(0, $BGXP4$reactstatelyform.privateValidationStateProp)]: state,
161
+ 'aria-describedby': [
162
+ valueId,
163
+ props['aria-describedby']
164
+ ].filter(Boolean).join(' ') || undefined
155
165
  }, inputRef);
156
- (0, $BGXP4$reactariautils.useFormReset)(inputRef, state.defaultSelectedKey, state.setSelectedKey);
166
+ (0, $BGXP4$reactariautils.useFormReset)(inputRef, state.defaultValue, state.setValue);
157
167
  // Press handlers for the ComboBox button
158
168
  let onPress = (e)=>{
159
169
  if (e.pointerType === 'touch') {
@@ -191,7 +201,7 @@ function $242452271d1e4c0e$export$8c18d1b4f7232bbf(props, state) {
191
201
  (_inputRef_current = inputRef.current) === null || _inputRef_current === void 0 ? void 0 : _inputRef_current.focus();
192
202
  return;
193
203
  }
194
- let rect = e.target.getBoundingClientRect();
204
+ let rect = (0, $BGXP4$reactariautils.getEventTarget)(e).getBoundingClientRect();
195
205
  let touch = e.changedTouches[0];
196
206
  let centerX = Math.ceil(rect.left + .5 * rect.width);
197
207
  let centerY = Math.ceil(rect.top + .5 * rect.height);
@@ -252,6 +262,7 @@ function $242452271d1e4c0e$export$8c18d1b4f7232bbf(props, state) {
252
262
  lastOpen.current = state.isOpen;
253
263
  });
254
264
  // Announce when a selection occurs for VoiceOver. Other screen readers typically do this automatically.
265
+ // TODO: do we need to do this for multi-select?
255
266
  let lastSelectedKey = (0, $BGXP4$react.useRef)(state.selectedKey);
256
267
  (0, $BGXP4$react.useEffect)(()=>{
257
268
  if ((0, $BGXP4$reactariautils.isAppleDevice)() && state.isFocused && state.selectedItem && state.selectedKey !== lastSelectedKey.current) {
@@ -282,6 +293,17 @@ function $242452271d1e4c0e$export$8c18d1b4f7232bbf(props, state) {
282
293
  (0, $BGXP4$reactariautils.useEvent)(listBoxRef, 'react-aria-item-action', state.isOpen ? ()=>{
283
294
  state.close();
284
295
  } : undefined);
296
+ // usePopover -> useOverlay calls useInteractOutside, but ComboBox is non-modal, so `isDismissable` is false
297
+ // Because of this, onInteractOutside is not passed to useInteractOutside, so we need to call it here.
298
+ (0, $BGXP4$reactariainteractions.useInteractOutside)({
299
+ ref: popoverRef,
300
+ onInteractOutside: (e)=>{
301
+ let target = (0, $BGXP4$reactariautils.getEventTarget)(e);
302
+ if ((0, $BGXP4$reactariautils.nodeContains)(buttonRef === null || buttonRef === void 0 ? void 0 : buttonRef.current, target) || (0, $BGXP4$reactariautils.nodeContains)(inputRef.current, target)) return;
303
+ state.close();
304
+ },
305
+ isDisabled: !state.isOpen
306
+ });
285
307
  return {
286
308
  labelProps: labelProps,
287
309
  buttonProps: {
@@ -314,6 +336,9 @@ function $242452271d1e4c0e$export$8c18d1b4f7232bbf(props, state) {
314
336
  linkBehavior: 'selection',
315
337
  ['UNSTABLE_itemBehavior']: 'action'
316
338
  }),
339
+ valueProps: {
340
+ id: valueId
341
+ },
317
342
  descriptionProps: descriptionProps,
318
343
  errorMessageProps: errorMessageProps,
319
344
  isInvalid: isInvalid,
@@ -321,6 +346,30 @@ function $242452271d1e4c0e$export$8c18d1b4f7232bbf(props, state) {
321
346
  validationDetails: validationDetails
322
347
  };
323
348
  }
349
+ // This is a modified version of useSlotId that uses useEffect instead of useLayoutEffect.
350
+ // Triggering re-renders from useLayoutEffect breaks useComboBoxState's useEffect logic in React 18.
351
+ // These re-renders preempt async state updates in the useEffect, which ends up running multiple times
352
+ // prior to the state being updated. This results in onSelectionChange being called multiple times.
353
+ // TODO: refactor useComboBoxState to avoid this.
354
+ function $242452271d1e4c0e$var$useValueId(depArray = []) {
355
+ let id = (0, $BGXP4$reactariautils.useId)();
356
+ let [exists, setExists] = (0, $BGXP4$react.useState)(true);
357
+ let [lastDeps, setLastDeps] = (0, $BGXP4$react.useState)(depArray);
358
+ // If the deps changed, set exists to true so we can test whether the element exists.
359
+ if (lastDeps.some((v, i)=>!Object.is(v, depArray[i]))) {
360
+ setExists(true);
361
+ setLastDeps(depArray);
362
+ }
363
+ (0, $BGXP4$react.useEffect)(()=>{
364
+ if (exists && !document.getElementById(id)) // eslint-disable-next-line react-hooks/set-state-in-effect
365
+ setExists(false);
366
+ }, [
367
+ id,
368
+ exists,
369
+ lastDeps
370
+ ]);
371
+ return exists ? id : undefined;
372
+ }
324
373
 
325
374
 
326
375
  //# sourceMappingURL=useComboBox.main.js.map
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/@react-aria/combobox/src/useComboBox.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {announce} from '@react-aria/live-announcer';\nimport {AriaButtonProps} from '@react-types/button';\nimport {AriaComboBoxProps} from '@react-types/combobox';\nimport {ariaHideOutside} from '@react-aria/overlays';\nimport {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox';\nimport {BaseEvent, DOMAttributes, KeyboardDelegate, LayoutDelegate, PressEvent, RefObject, RouterOptions, ValidationResult} from '@react-types/shared';\nimport {chain, getActiveElement, getOwnerDocument, isAppleDevice, mergeProps, nodeContains, useEvent, useFormReset, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';\nimport {ComboBoxState} from '@react-stately/combobox';\nimport {dispatchVirtualFocus} from '@react-aria/focus';\nimport {FocusEvent, InputHTMLAttributes, KeyboardEvent, TouchEvent, useEffect, useMemo, useRef} from 'react';\nimport {getChildNodes, getItemCount} from '@react-stately/collections';\n// @ts-ignore\nimport intlMessages from '../intl/*.json';\nimport {ListKeyboardDelegate, useSelectableCollection} from '@react-aria/selection';\nimport {privateValidationStateProp} from '@react-stately/form';\nimport {useLocalizedStringFormatter} from '@react-aria/i18n';\nimport {useMenuTrigger} from '@react-aria/menu';\nimport {useTextField} from '@react-aria/textfield';\n\nexport interface AriaComboBoxOptions<T> extends Omit<AriaComboBoxProps<T>, 'children'> {\n /** The ref for the input element. */\n inputRef: RefObject<HTMLInputElement | null>,\n /** The ref for the list box popover. */\n popoverRef: RefObject<Element | null>,\n /** The ref for the list box. */\n listBoxRef: RefObject<HTMLElement | null>,\n /** The ref for the optional list box popup trigger button. */\n buttonRef?: RefObject<Element | null>,\n /** An optional keyboard delegate implementation, to override the default. */\n keyboardDelegate?: KeyboardDelegate,\n /**\n * A delegate object that provides layout information for items in the collection.\n * By default this uses the DOM, but this can be overridden to implement things like\n * virtualized scrolling.\n */\n layoutDelegate?: LayoutDelegate\n}\n\nexport interface ComboBoxAria<T> extends ValidationResult {\n /** Props for the label element. */\n labelProps: DOMAttributes,\n /** Props for the combo box input element. */\n inputProps: InputHTMLAttributes<HTMLInputElement>,\n /** Props for the list box, to be passed to `useListBox`. */\n listBoxProps: AriaListBoxOptions<T>,\n /** Props for the optional trigger button, to be passed to `useButton`. */\n buttonProps: AriaButtonProps,\n /** Props for the combo box description element, if any. */\n descriptionProps: DOMAttributes,\n /** Props for the combo box error message element, if any. */\n errorMessageProps: DOMAttributes\n}\n\n/**\n * Provides the behavior and accessibility implementation for a combo box component.\n * A combo box combines a text input with a listbox, allowing users to filter a list of options to items matching a query.\n * @param props - Props for the combo box.\n * @param state - State for the select, as returned by `useComboBoxState`.\n */\nexport function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxState<T>): ComboBoxAria<T> {\n let {\n buttonRef,\n popoverRef,\n inputRef,\n listBoxRef,\n keyboardDelegate,\n layoutDelegate,\n // completionMode = 'suggest',\n shouldFocusWrap,\n isReadOnly,\n isDisabled\n } = props;\n let backupBtnRef = useRef(null);\n buttonRef = buttonRef ?? backupBtnRef;\n\n let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/combobox');\n let {menuTriggerProps, menuProps} = useMenuTrigger<T>(\n {\n type: 'listbox',\n isDisabled: isDisabled || isReadOnly\n },\n state,\n buttonRef\n );\n\n // Set listbox id so it can be used when calling getItemId later\n listData.set(state, {id: menuProps.id});\n\n // By default, a KeyboardDelegate is provided which uses the DOM to query layout information (e.g. for page up/page down).\n // When virtualized, the layout object will be passed in as a prop and override this.\n let {collection} = state;\n let {disabledKeys} = state.selectionManager;\n let delegate = useMemo(() => (\n keyboardDelegate || new ListKeyboardDelegate({\n collection,\n disabledKeys,\n ref: listBoxRef,\n layoutDelegate\n })\n ), [keyboardDelegate, layoutDelegate, collection, disabledKeys, listBoxRef]);\n\n // Use useSelectableCollection to get the keyboard handlers to apply to the textfield\n let {collectionProps} = useSelectableCollection({\n selectionManager: state.selectionManager,\n keyboardDelegate: delegate,\n disallowTypeAhead: true,\n disallowEmptySelection: true,\n shouldFocusWrap,\n ref: inputRef,\n // Prevent item scroll behavior from being applied here, should be handled in the user's Popover + ListBox component\n isVirtualized: true\n });\n\n let router = useRouter();\n\n // For textfield specific keydown operations\n let onKeyDown = (e: BaseEvent<KeyboardEvent<any>>) => {\n if (e.nativeEvent.isComposing) {\n return;\n }\n switch (e.key) {\n case 'Enter':\n case 'Tab':\n // Prevent form submission if menu is open since we may be selecting a option\n if (state.isOpen && e.key === 'Enter') {\n e.preventDefault();\n }\n\n // If the focused item is a link, trigger opening it. Items that are links are not selectable.\n if (state.isOpen && listBoxRef.current && state.selectionManager.focusedKey != null) {\n let collectionItem = state.collection.getItem(state.selectionManager.focusedKey);\n if (collectionItem?.props.href) {\n let item = listBoxRef.current.querySelector(`[data-key=\"${CSS.escape(state.selectionManager.focusedKey.toString())}\"]`);\n if (e.key === 'Enter' && item instanceof HTMLAnchorElement) {\n router.open(item, e, collectionItem.props.href, collectionItem.props.routerOptions as RouterOptions);\n }\n state.close();\n break;\n } else if (collectionItem?.props.onAction) {\n collectionItem.props.onAction();\n state.close();\n break;\n }\n }\n state.commit();\n break;\n case 'Escape':\n if (\n state.selectedKey !== null ||\n state.inputValue === '' ||\n props.allowsCustomValue\n ) {\n e.continuePropagation();\n }\n state.revert();\n break;\n case 'ArrowDown':\n state.open('first', 'manual');\n break;\n case 'ArrowUp':\n state.open('last', 'manual');\n break;\n case 'ArrowLeft':\n case 'ArrowRight':\n state.selectionManager.setFocusedKey(null);\n break;\n }\n };\n\n let onBlur = (e: FocusEvent<HTMLInputElement>) => {\n let blurFromButton = buttonRef?.current && buttonRef.current === e.relatedTarget;\n let blurIntoPopover = nodeContains(popoverRef.current, e.relatedTarget);\n // Ignore blur if focused moved to the button(if exists) or into the popover.\n if (blurFromButton || blurIntoPopover) {\n return;\n }\n\n if (props.onBlur) {\n props.onBlur(e);\n }\n\n state.setFocused(false);\n };\n\n let onFocus = (e: FocusEvent<HTMLInputElement>) => {\n if (state.isFocused) {\n return;\n }\n\n if (props.onFocus) {\n props.onFocus(e);\n }\n\n state.setFocused(true);\n };\n\n let {isInvalid, validationErrors, validationDetails} = state.displayValidation;\n let {labelProps, inputProps, descriptionProps, errorMessageProps} = useTextField({\n ...props,\n onChange: state.setInputValue,\n onKeyDown: !isReadOnly ? chain(state.isOpen && collectionProps.onKeyDown, onKeyDown, props.onKeyDown) : props.onKeyDown,\n onBlur,\n value: state.inputValue,\n defaultValue: state.defaultInputValue,\n onFocus,\n autoComplete: 'off',\n validate: undefined,\n [privateValidationStateProp]: state\n }, inputRef);\n\n useFormReset(inputRef, state.defaultSelectedKey, state.setSelectedKey);\n \n // Press handlers for the ComboBox button\n let onPress = (e: PressEvent) => {\n if (e.pointerType === 'touch') {\n // Focus the input field in case it isn't focused yet\n inputRef.current?.focus();\n state.toggle(null, 'manual');\n }\n };\n\n let onPressStart = (e: PressEvent) => {\n if (e.pointerType !== 'touch') {\n inputRef.current?.focus();\n state.toggle((e.pointerType === 'keyboard' || e.pointerType === 'virtual') ? 'first' : null, 'manual');\n }\n };\n\n let triggerLabelProps = useLabels({\n id: menuTriggerProps.id,\n 'aria-label': stringFormatter.format('buttonLabel'),\n 'aria-labelledby': props['aria-labelledby'] || labelProps.id\n });\n\n let listBoxProps = useLabels({\n id: menuProps.id,\n 'aria-label': stringFormatter.format('listboxLabel'),\n 'aria-labelledby': props['aria-labelledby'] || labelProps.id\n });\n\n // If a touch happens on direct center of ComboBox input, might be virtual click from iPad so open ComboBox menu\n let lastEventTime = useRef(0);\n let onTouchEnd = (e: TouchEvent) => {\n if (isDisabled || isReadOnly) {\n return;\n }\n\n // Sometimes VoiceOver on iOS fires two touchend events in quick succession. Ignore the second one.\n if (e.timeStamp - lastEventTime.current < 500) {\n e.preventDefault();\n inputRef.current?.focus();\n return;\n }\n\n let rect = (e.target as Element).getBoundingClientRect();\n let touch = e.changedTouches[0];\n\n let centerX = Math.ceil(rect.left + .5 * rect.width);\n let centerY = Math.ceil(rect.top + .5 * rect.height);\n\n if (touch.clientX === centerX && touch.clientY === centerY) {\n e.preventDefault();\n inputRef.current?.focus();\n state.toggle(null, 'manual');\n\n lastEventTime.current = e.timeStamp;\n }\n };\n\n // VoiceOver has issues with announcing aria-activedescendant properly on change\n // (especially on iOS). We use a live region announcer to announce focus changes\n // manually. In addition, section titles are announced when navigating into a new section.\n let focusedItem = state.selectionManager.focusedKey != null && state.isOpen\n ? state.collection.getItem(state.selectionManager.focusedKey)\n : undefined;\n let sectionKey = focusedItem?.parentKey ?? null;\n let itemKey = state.selectionManager.focusedKey ?? null;\n let lastSection = useRef(sectionKey);\n let lastItem = useRef(itemKey);\n useEffect(() => {\n if (isAppleDevice() && focusedItem != null && itemKey != null && itemKey !== lastItem.current) {\n let isSelected = state.selectionManager.isSelected(itemKey);\n let section = sectionKey != null ? state.collection.getItem(sectionKey) : null;\n let sectionTitle = section?.['aria-label'] || (typeof section?.rendered === 'string' ? section.rendered : '') || '';\n\n let announcement = stringFormatter.format('focusAnnouncement', {\n isGroupChange: (section && sectionKey !== lastSection.current) ?? false,\n groupTitle: sectionTitle,\n groupCount: section ? [...getChildNodes(section, state.collection)].length : 0,\n optionText: focusedItem['aria-label'] || focusedItem.textValue || '',\n isSelected\n });\n\n announce(announcement);\n }\n\n lastSection.current = sectionKey;\n lastItem.current = itemKey;\n });\n\n // Announce the number of available suggestions when it changes\n let optionCount = getItemCount(state.collection);\n let lastSize = useRef(optionCount);\n let lastOpen = useRef(state.isOpen);\n useEffect(() => {\n // Only announce the number of options available when the menu opens if there is no\n // focused item, otherwise screen readers will typically read e.g. \"1 of 6\".\n // The exception is VoiceOver since this isn't included in the message above.\n let didOpenWithoutFocusedItem =\n state.isOpen !== lastOpen.current &&\n (state.selectionManager.focusedKey == null || isAppleDevice());\n\n if (state.isOpen && (didOpenWithoutFocusedItem || optionCount !== lastSize.current)) {\n let announcement = stringFormatter.format('countAnnouncement', {optionCount});\n announce(announcement);\n }\n\n lastSize.current = optionCount;\n lastOpen.current = state.isOpen;\n });\n\n // Announce when a selection occurs for VoiceOver. Other screen readers typically do this automatically.\n let lastSelectedKey = useRef(state.selectedKey);\n useEffect(() => {\n if (isAppleDevice() && state.isFocused && state.selectedItem && state.selectedKey !== lastSelectedKey.current) {\n let optionText = state.selectedItem['aria-label'] || state.selectedItem.textValue || '';\n let announcement = stringFormatter.format('selectedAnnouncement', {optionText});\n announce(announcement);\n }\n\n lastSelectedKey.current = state.selectedKey;\n });\n\n useEffect(() => {\n if (state.isOpen) {\n return ariaHideOutside([inputRef.current, popoverRef.current].filter(element => element != null));\n }\n }, [state.isOpen, inputRef, popoverRef]);\n\n useUpdateEffect(() => {\n // Re-show focus ring when there is no virtually focused item.\n if (!focusedItem && inputRef.current && getActiveElement(getOwnerDocument(inputRef.current)) === inputRef.current) {\n dispatchVirtualFocus(inputRef.current, null);\n }\n }, [focusedItem]);\n\n useEvent(listBoxRef, 'react-aria-item-action', state.isOpen ? () => {\n state.close();\n } : undefined);\n\n return {\n labelProps,\n buttonProps: {\n ...menuTriggerProps,\n ...triggerLabelProps,\n excludeFromTabOrder: true,\n preventFocusOnPress: true,\n onPress,\n onPressStart,\n isDisabled: isDisabled || isReadOnly\n },\n inputProps: mergeProps(inputProps, {\n role: 'combobox',\n 'aria-expanded': menuTriggerProps['aria-expanded'],\n 'aria-controls': state.isOpen ? menuProps.id : undefined,\n // TODO: readd proper logic for completionMode = complete (aria-autocomplete: both)\n 'aria-autocomplete': 'list',\n 'aria-activedescendant': focusedItem ? getItemId(state, focusedItem.key) : undefined,\n onTouchEnd,\n // This disable's iOS's autocorrect suggestions, since the combo box provides its own suggestions.\n autoCorrect: 'off',\n // This disable's the macOS Safari spell check auto corrections.\n spellCheck: 'false'\n }),\n listBoxProps: mergeProps(menuProps, listBoxProps, {\n autoFocus: state.focusStrategy || true,\n shouldUseVirtualFocus: true,\n shouldSelectOnPressUp: true,\n shouldFocusOnHover: true,\n linkBehavior: 'selection' as const,\n ['UNSTABLE_itemBehavior']: 'action'\n }),\n descriptionProps,\n errorMessageProps,\n isInvalid,\n validationErrors,\n validationDetails\n };\n}\n"],"names":[],"version":3,"file":"useComboBox.main.js.map"}
1
+ {"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;CAUC;;;;;;;;;;;;;;AAgEM,SAAS,0CAAmD,KAAgC,EAAE,KAA0B;IAC7H,IAAI,aACF,SAAS,cACT,UAAU,YACV,QAAQ,cACR,UAAU,oBACV,gBAAgB,kBAChB,cAAc,mBACd,8BAA8B;IAC9B,eAAe,cACf,UAAU,cACV,UAAU,EACX,GAAG;IACJ,IAAI,eAAe,CAAA,GAAA,mBAAK,EAAE;IAC1B,YAAY,sBAAA,uBAAA,YAAa;IAEzB,IAAI,kBAAkB,CAAA,GAAA,gDAA0B,EAAE,CAAA,GAAA,mDAAW,GAAG;IAChE,IAAI,oBAAC,gBAAgB,aAAE,SAAS,EAAC,GAAG,CAAA,GAAA,mCAAa,EAC/C;QACE,MAAM;QACN,YAAY,cAAc;IAC5B,GACA,OACA;IAGF,gEAAgE;IAChE,CAAA,GAAA,gCAAO,EAAE,GAAG,CAAC,OAAO;QAAC,IAAI,UAAU,EAAE;IAAA;IAErC,0HAA0H;IAC1H,qFAAqF;IACrF,IAAI,cAAC,UAAU,EAAC,GAAG;IACnB,IAAI,gBAAC,YAAY,EAAC,GAAG,MAAM,gBAAgB;IAC3C,IAAI,WAAW,CAAA,GAAA,oBAAM,EAAE,IACrB,oBAAoB,IAAI,CAAA,GAAA,8CAAmB,EAAE;wBAC3C;0BACA;YACA,KAAK;4BACL;QACF,IACC;QAAC;QAAkB;QAAgB;QAAY;QAAc;KAAW;IAE3E,qFAAqF;IACrF,IAAI,mBAAC,eAAe,EAAC,GAAG,CAAA,GAAA,iDAAsB,EAAE;QAC9C,kBAAkB,MAAM,gBAAgB;QACxC,kBAAkB;QAClB,mBAAmB;QACnB,wBAAwB;yBACxB;QACA,KAAK;QACL,oHAAoH;QACpH,eAAe;IACjB;IAEA,IAAI,SAAS,CAAA,GAAA,+BAAQ;IAErB,4CAA4C;IAC5C,IAAI,YAAY,CAAC;QACf,IAAI,EAAE,WAAW,CAAC,WAAW,EAC3B;QAEF,OAAQ,EAAE,GAAG;YACX,KAAK;YACL,KAAK;gBACH,6EAA6E;gBAC7E,IAAI,MAAM,MAAM,IAAI,EAAE,GAAG,KAAK,SAC5B,EAAE,cAAc;gBAGlB,8FAA8F;gBAC9F,IAAI,MAAM,MAAM,IAAI,WAAW,OAAO,IAAI,MAAM,gBAAgB,CAAC,UAAU,IAAI,MAAM;oBACnF,IAAI,iBAAiB,MAAM,UAAU,CAAC,OAAO,CAAC,MAAM,gBAAgB,CAAC,UAAU;oBAC/E,IAAI,2BAAA,qCAAA,eAAgB,KAAK,CAAC,IAAI,EAAE;wBAC9B,IAAI,OAAO,WAAW,OAAO,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC,MAAM,gBAAgB,CAAC,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC;wBACtH,IAAI,EAAE,GAAG,KAAK,WAAW,gBAAgB,mBACvC,OAAO,IAAI,CAAC,MAAM,GAAG,eAAe,KAAK,CAAC,IAAI,EAAE,eAAe,KAAK,CAAC,aAAa;wBAEpF,MAAM,KAAK;wBACX;oBACF,OAAO,IAAI,2BAAA,qCAAA,eAAgB,KAAK,CAAC,QAAQ,EAAE;wBACzC,eAAe,KAAK,CAAC,QAAQ;wBAC7B,MAAM,KAAK;wBACX;oBACF;gBACF;gBACA,MAAM,MAAM;gBACZ;YACF,KAAK;gBACH,IACE,CAAC,MAAM,gBAAgB,CAAC,OAAO,IAC/B,MAAM,UAAU,KAAK,MACrB,MAAM,iBAAiB,EAEvB,EAAE,mBAAmB;gBAEvB,MAAM,MAAM;gBACZ;YACF,KAAK;gBACH,MAAM,IAAI,CAAC,SAAS;gBACpB;YACF,KAAK;gBACH,MAAM,IAAI,CAAC,QAAQ;gBACnB;YACF,KAAK;YACL,KAAK;gBACH,MAAM,gBAAgB,CAAC,aAAa,CAAC;gBACrC;QACJ;IACF;IAEA,IAAI,SAAS,CAAC;QACZ,IAAI,iBAAiB,CAAA,sBAAA,gCAAA,UAAW,OAAO,KAAI,UAAU,OAAO,KAAK,EAAE,aAAa;QAChF,IAAI,kBAAkB,CAAA,GAAA,kCAAW,EAAE,WAAW,OAAO,EAAE,EAAE,aAAa;QACtE,6EAA6E;QAC7E,IAAI,kBAAkB,iBACpB;QAGF,IAAI,MAAM,MAAM,EACd,MAAM,MAAM,CAAC;QAGf,MAAM,UAAU,CAAC;IACnB;IAEA,IAAI,UAAU,CAAC;QACb,IAAI,MAAM,SAAS,EACjB;QAGF,IAAI,MAAM,OAAO,EACf,MAAM,OAAO,CAAC;QAGhB,MAAM,UAAU,CAAC;IACnB;IAEA,IAAI,UAAU,iCAAW;QAAC,MAAM,aAAa;QAAE,MAAM,gBAAgB,CAAC,aAAa;KAAC;IACpF,IAAI,aAAC,SAAS,oBAAE,gBAAgB,qBAAE,iBAAiB,EAAC,GAAG,MAAM,iBAAiB;IAC9E,IAAI,cAAC,UAAU,cAAE,UAAU,oBAAE,gBAAgB,qBAAE,iBAAiB,EAAC,GAAG,CAAA,GAAA,sCAAW,EAAE;QAC/E,GAAG,KAAK;QACR,UAAU,MAAM,aAAa;QAC7B,WAAW,CAAC,aAAa,CAAA,GAAA,2BAAI,EAAE,MAAM,MAAM,IAAI,gBAAgB,SAAS,EAAE,WAAW,MAAM,SAAS,IAAI,MAAM,SAAS;gBACvH;QACA,OAAO,MAAM,UAAU;QACvB,cAAc,MAAM,iBAAiB;iBACrC;QACA,cAAc;QACd,UAAU;QACV,CAAC,CAAA,GAAA,kDAAyB,EAAE,EAAE;QAC9B,oBAAoB;YAAC;YAAS,KAAK,CAAC,mBAAmB;SAAC,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,QAAQ;IACxF,GAAG;IAEH,CAAA,GAAA,kCAAW,EAAE,UAAU,MAAM,YAAY,EAAE,MAAM,QAAQ;IAEzD,yCAAyC;IACzC,IAAI,UAAU,CAAC;QACb,IAAI,EAAE,WAAW,KAAK,SAAS;gBAC7B,qDAAqD;YACrD;aAAA,oBAAA,SAAS,OAAO,cAAhB,wCAAA,kBAAkB,KAAK;YACvB,MAAM,MAAM,CAAC,MAAM;QACrB;IACF;IAEA,IAAI,eAAe,CAAC;QAClB,IAAI,EAAE,WAAW,KAAK,SAAS;gBAC7B;aAAA,oBAAA,SAAS,OAAO,cAAhB,wCAAA,kBAAkB,KAAK;YACvB,MAAM,MAAM,CAAC,AAAC,EAAE,WAAW,KAAK,cAAc,EAAE,WAAW,KAAK,YAAa,UAAU,MAAM;QAC/F;IACF;IAEA,IAAI,oBAAoB,CAAA,GAAA,+BAAQ,EAAE;QAChC,IAAI,iBAAiB,EAAE;QACvB,cAAc,gBAAgB,MAAM,CAAC;QACrC,mBAAmB,KAAK,CAAC,kBAAkB,IAAI,WAAW,EAAE;IAC9D;IAEA,IAAI,eAAe,CAAA,GAAA,+BAAQ,EAAE;QAC3B,IAAI,UAAU,EAAE;QAChB,cAAc,gBAAgB,MAAM,CAAC;QACrC,mBAAmB,KAAK,CAAC,kBAAkB,IAAI,WAAW,EAAE;IAC9D;IAEA,gHAAgH;IAChH,IAAI,gBAAgB,CAAA,GAAA,mBAAK,EAAE;IAC3B,IAAI,aAAa,CAAC;QAChB,IAAI,cAAc,YAChB;QAGF,mGAAmG;QACnG,IAAI,EAAE,SAAS,GAAG,cAAc,OAAO,GAAG,KAAK;gBAE7C;YADA,EAAE,cAAc;aAChB,oBAAA,SAAS,OAAO,cAAhB,wCAAA,kBAAkB,KAAK;YACvB;QACF;QAEA,IAAI,OAAO,AAAC,CAAA,GAAA,oCAAa,EAAE,GAAe,qBAAqB;QAC/D,IAAI,QAAQ,EAAE,cAAc,CAAC,EAAE;QAE/B,IAAI,UAAU,KAAK,IAAI,CAAC,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK;QACnD,IAAI,UAAU,KAAK,IAAI,CAAC,KAAK,GAAG,GAAG,KAAK,KAAK,MAAM;QAEnD,IAAI,MAAM,OAAO,KAAK,WAAW,MAAM,OAAO,KAAK,SAAS;gBAE1D;YADA,EAAE,cAAc;aAChB,qBAAA,SAAS,OAAO,cAAhB,yCAAA,mBAAkB,KAAK;YACvB,MAAM,MAAM,CAAC,MAAM;YAEnB,cAAc,OAAO,GAAG,EAAE,SAAS;QACrC;IACF;IAEA,gFAAgF;IAChF,gFAAgF;IAChF,0FAA0F;IAC1F,IAAI,cAAc,MAAM,gBAAgB,CAAC,UAAU,IAAI,QAAQ,MAAM,MAAM,GACvE,MAAM,UAAU,CAAC,OAAO,CAAC,MAAM,gBAAgB,CAAC,UAAU,IAC1D;QACa;IAAjB,IAAI,aAAa,CAAA,yBAAA,wBAAA,kCAAA,YAAa,SAAS,cAAtB,oCAAA,yBAA0B;QAC7B;IAAd,IAAI,UAAU,CAAA,qCAAA,MAAM,gBAAgB,CAAC,UAAU,cAAjC,gDAAA,qCAAqC;IACnD,IAAI,cAAc,CAAA,GAAA,mBAAK,EAAE;IACzB,IAAI,WAAW,CAAA,GAAA,mBAAK,EAAE;IACtB,CAAA,GAAA,sBAAQ,EAAE;QACR,IAAI,CAAA,GAAA,mCAAY,OAAO,eAAe,QAAQ,WAAW,QAAQ,YAAY,SAAS,OAAO,EAAE;YAC7F,IAAI,aAAa,MAAM,gBAAgB,CAAC,UAAU,CAAC;YACnD,IAAI,UAAU,cAAc,OAAO,MAAM,UAAU,CAAC,OAAO,CAAC,cAAc;YAC1E,IAAI,eAAe,CAAA,oBAAA,8BAAA,OAAS,CAAC,aAAa,KAAK,CAAA,QAAO,oBAAA,8BAAA,QAAS,QAAQ,MAAK,WAAW,QAAQ,QAAQ,GAAG,EAAC,KAAM;gBAG/F;YADlB,IAAI,eAAe,gBAAgB,MAAM,CAAC,qBAAqB;gBAC7D,eAAe,CAAC,OAAA,WAAW,eAAe,YAAY,OAAO,cAA7C,kBAAA,OAAkD;gBAClE,YAAY;gBACZ,YAAY,UAAU;uBAAI,CAAA,GAAA,4CAAY,EAAE,SAAS,MAAM,UAAU;iBAAE,CAAC,MAAM,GAAG;gBAC7E,YAAY,WAAW,CAAC,aAAa,IAAI,YAAY,SAAS,IAAI;4BAClE;YACF;YAEA,CAAA,GAAA,sCAAO,EAAE;QACX;QAEA,YAAY,OAAO,GAAG;QACtB,SAAS,OAAO,GAAG;IACrB;IAEA,+DAA+D;IAC/D,IAAI,cAAc,CAAA,GAAA,2CAAW,EAAE,MAAM,UAAU;IAC/C,IAAI,WAAW,CAAA,GAAA,mBAAK,EAAE;IACtB,IAAI,WAAW,CAAA,GAAA,mBAAK,EAAE,MAAM,MAAM;IAClC,CAAA,GAAA,sBAAQ,EAAE;QACR,mFAAmF;QACnF,4EAA4E;QAC5E,6EAA6E;QAC7E,IAAI,4BACF,MAAM,MAAM,KAAK,SAAS,OAAO,IAChC,CAAA,MAAM,gBAAgB,CAAC,UAAU,IAAI,QAAQ,CAAA,GAAA,mCAAY,GAAE;QAE9D,IAAI,MAAM,MAAM,IAAK,CAAA,6BAA6B,gBAAgB,SAAS,OAAO,AAAD,GAAI;YACnF,IAAI,eAAe,gBAAgB,MAAM,CAAC,qBAAqB;6BAAC;YAAW;YAC3E,CAAA,GAAA,sCAAO,EAAE;QACX;QAEA,SAAS,OAAO,GAAG;QACnB,SAAS,OAAO,GAAG,MAAM,MAAM;IACjC;IAEA,wGAAwG;IACxG,gDAAgD;IAChD,IAAI,kBAAkB,CAAA,GAAA,mBAAK,EAAE,MAAM,WAAW;IAC9C,CAAA,GAAA,sBAAQ,EAAE;QACR,IAAI,CAAA,GAAA,mCAAY,OAAO,MAAM,SAAS,IAAI,MAAM,YAAY,IAAI,MAAM,WAAW,KAAK,gBAAgB,OAAO,EAAE;YAC7G,IAAI,aAAa,MAAM,YAAY,CAAC,aAAa,IAAI,MAAM,YAAY,CAAC,SAAS,IAAI;YACrF,IAAI,eAAe,gBAAgB,MAAM,CAAC,wBAAwB;4BAAC;YAAU;YAC7E,CAAA,GAAA,sCAAO,EAAE;QACX;QAEA,gBAAgB,OAAO,GAAG,MAAM,WAAW;IAC7C;IAEA,CAAA,GAAA,sBAAQ,EAAE;QACR,IAAI,MAAM,MAAM,EACd,OAAO,CAAA,GAAA,wCAAc,EAAE;YAAC,SAAS,OAAO;YAAE,WAAW,OAAO;SAAC,CAAC,MAAM,CAAC,CAAA,UAAW,WAAW;IAE/F,GAAG;QAAC,MAAM,MAAM;QAAE;QAAU;KAAW;IAEvC,CAAA,GAAA,qCAAc,EAAE;QACd,8DAA8D;QAC9D,IAAI,CAAC,eAAe,SAAS,OAAO,IAAI,CAAA,GAAA,sCAAe,EAAE,CAAA,GAAA,sCAAe,EAAE,SAAS,OAAO,OAAO,SAAS,OAAO,EAC/G,CAAA,GAAA,0CAAmB,EAAE,SAAS,OAAO,EAAE;IAE3C,GAAG;QAAC;KAAY;IAEhB,CAAA,GAAA,8BAAO,EAAE,YAAY,0BAA0B,MAAM,MAAM,GAAG;QAC5D,MAAM,KAAK;IACb,IAAI;IAEJ,4GAA4G;IAC5G,sGAAsG;IACtG,CAAA,GAAA,+CAAiB,EAAE;QACjB,KAAK;QACL,mBAAmB,CAAC;YAClB,IAAI,SAAS,CAAA,GAAA,oCAAa,EAAE;YAC5B,IAAI,CAAA,GAAA,kCAAW,EAAE,sBAAA,gCAAA,UAAW,OAAO,EAAE,WAAW,CAAA,GAAA,kCAAW,EAAE,SAAS,OAAO,EAAE,SAC7E;YAEF,MAAM,KAAK;QACb;QACA,YAAY,CAAC,MAAM,MAAM;IAC3B;IAEA,OAAO;oBACL;QACA,aAAa;YACX,GAAG,gBAAgB;YACnB,GAAG,iBAAiB;YACpB,qBAAqB;YACrB,qBAAqB;qBACrB;0BACA;YACA,YAAY,cAAc;QAC5B;QACA,YAAY,CAAA,GAAA,gCAAS,EAAE,YAAY;YACjC,MAAM;YACN,iBAAiB,gBAAgB,CAAC,gBAAgB;YAClD,iBAAiB,MAAM,MAAM,GAAG,UAAU,EAAE,GAAG;YAC/C,mFAAmF;YACnF,qBAAqB;YACrB,yBAAyB,cAAc,CAAA,GAAA,iCAAQ,EAAE,OAAO,YAAY,GAAG,IAAI;wBAC3E;YACA,kGAAkG;YAClG,aAAa;YACb,gEAAgE;YAChE,YAAY;QACd;QACA,cAAc,CAAA,GAAA,gCAAS,EAAE,WAAW,cAAc;YAChD,WAAW,MAAM,aAAa,IAAI;YAClC,uBAAuB;YACvB,uBAAuB;YACvB,oBAAoB;YACpB,cAAc;YACd,CAAC,wBAAwB,EAAE;QAC7B;QACA,YAAY;YACV,IAAI;QACN;0BACA;2BACA;mBACA;0BACA;2BACA;IACF;AACF;AAEA,0FAA0F;AAC1F,oGAAoG;AACpG,sGAAsG;AACtG,mGAAmG;AACnG,iDAAiD;AACjD,SAAS,iCAAW,WAA+B,EAAE;IACnD,IAAI,KAAK,CAAA,GAAA,2BAAI;IACb,IAAI,CAAC,QAAQ,UAAU,GAAG,CAAA,GAAA,qBAAO,EAAE;IACnC,IAAI,CAAC,UAAU,YAAY,GAAG,CAAA,GAAA,qBAAO,EAAE;IAEvC,qFAAqF;IACrF,IAAI,SAAS,IAAI,CAAC,CAAC,GAAG,IAAM,CAAC,OAAO,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,IAAI;QACvD,UAAU;QACV,YAAY;IACd;IAEA,CAAA,GAAA,sBAAQ,EAAE;QACR,IAAI,UAAU,CAAC,SAAS,cAAc,CAAC,KACrC,2DAA2D;QAC3D,UAAU;IAEd,GAAG;QAAC;QAAI;QAAQ;KAAS;IAEzB,OAAO,SAAS,KAAK;AACvB","sources":["packages/@react-aria/combobox/src/useComboBox.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {announce} from '@react-aria/live-announcer';\nimport {AriaButtonProps} from '@react-types/button';\nimport {AriaComboBoxProps, SelectionMode} from '@react-types/combobox';\nimport {ariaHideOutside} from '@react-aria/overlays';\nimport {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox';\nimport {BaseEvent, DOMAttributes, KeyboardDelegate, LayoutDelegate, PressEvent, RefObject, RouterOptions, ValidationResult} from '@react-types/shared';\nimport {chain, getActiveElement, getEventTarget, getOwnerDocument, isAppleDevice, mergeProps, nodeContains, useEvent, useFormReset, useId, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';\nimport {ComboBoxState} from '@react-stately/combobox';\nimport {dispatchVirtualFocus} from '@react-aria/focus';\nimport {FocusEvent, InputHTMLAttributes, KeyboardEvent, TouchEvent, useEffect, useMemo, useRef, useState} from 'react';\nimport {getChildNodes, getItemCount} from '@react-stately/collections';\n// @ts-ignore\nimport intlMessages from '../intl/*.json';\nimport {ListKeyboardDelegate, useSelectableCollection} from '@react-aria/selection';\nimport {privateValidationStateProp} from '@react-stately/form';\nimport {useInteractOutside} from '@react-aria/interactions';\nimport {useLocalizedStringFormatter} from '@react-aria/i18n';\nimport {useMenuTrigger} from '@react-aria/menu';\nimport {useTextField} from '@react-aria/textfield';\n\nexport interface AriaComboBoxOptions<T, M extends SelectionMode = 'single'> extends Omit<AriaComboBoxProps<T, M>, 'children'> {\n /** The ref for the input element. */\n inputRef: RefObject<HTMLInputElement | null>,\n /** The ref for the list box popover. */\n popoverRef: RefObject<Element | null>,\n /** The ref for the list box. */\n listBoxRef: RefObject<HTMLElement | null>,\n /** The ref for the optional list box popup trigger button. */\n buttonRef?: RefObject<Element | null>,\n /** An optional keyboard delegate implementation, to override the default. */\n keyboardDelegate?: KeyboardDelegate,\n /**\n * A delegate object that provides layout information for items in the collection.\n * By default this uses the DOM, but this can be overridden to implement things like\n * virtualized scrolling.\n */\n layoutDelegate?: LayoutDelegate\n}\n\nexport interface ComboBoxAria<T> extends ValidationResult {\n /** Props for the label element. */\n labelProps: DOMAttributes,\n /** Props for the combo box input element. */\n inputProps: InputHTMLAttributes<HTMLInputElement>,\n /** Props for the list box, to be passed to `useListBox`. */\n listBoxProps: AriaListBoxOptions<T>,\n /** Props for the optional trigger button, to be passed to `useButton`. */\n buttonProps: AriaButtonProps,\n /** Props for the element representing the selected value. */\n valueProps: DOMAttributes,\n /** Props for the combo box description element, if any. */\n descriptionProps: DOMAttributes,\n /** Props for the combo box error message element, if any. */\n errorMessageProps: DOMAttributes\n}\n\n/**\n * Provides the behavior and accessibility implementation for a combo box component.\n * A combo box combines a text input with a listbox, allowing users to filter a list of options to items matching a query.\n * @param props - Props for the combo box.\n * @param state - State for the select, as returned by `useComboBoxState`.\n */\nexport function useComboBox<T, M extends SelectionMode = 'single'>(props: AriaComboBoxOptions<T, M>, state: ComboBoxState<T, M>): ComboBoxAria<T> {\n let {\n buttonRef,\n popoverRef,\n inputRef,\n listBoxRef,\n keyboardDelegate,\n layoutDelegate,\n // completionMode = 'suggest',\n shouldFocusWrap,\n isReadOnly,\n isDisabled\n } = props;\n let backupBtnRef = useRef(null);\n buttonRef = buttonRef ?? backupBtnRef;\n\n let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/combobox');\n let {menuTriggerProps, menuProps} = useMenuTrigger<T>(\n {\n type: 'listbox',\n isDisabled: isDisabled || isReadOnly\n },\n state,\n buttonRef\n );\n\n // Set listbox id so it can be used when calling getItemId later\n listData.set(state, {id: menuProps.id});\n\n // By default, a KeyboardDelegate is provided which uses the DOM to query layout information (e.g. for page up/page down).\n // When virtualized, the layout object will be passed in as a prop and override this.\n let {collection} = state;\n let {disabledKeys} = state.selectionManager;\n let delegate = useMemo(() => (\n keyboardDelegate || new ListKeyboardDelegate({\n collection,\n disabledKeys,\n ref: listBoxRef,\n layoutDelegate\n })\n ), [keyboardDelegate, layoutDelegate, collection, disabledKeys, listBoxRef]);\n\n // Use useSelectableCollection to get the keyboard handlers to apply to the textfield\n let {collectionProps} = useSelectableCollection({\n selectionManager: state.selectionManager,\n keyboardDelegate: delegate,\n disallowTypeAhead: true,\n disallowEmptySelection: true,\n shouldFocusWrap,\n ref: inputRef,\n // Prevent item scroll behavior from being applied here, should be handled in the user's Popover + ListBox component\n isVirtualized: true\n });\n\n let router = useRouter();\n\n // For textfield specific keydown operations\n let onKeyDown = (e: BaseEvent<KeyboardEvent<any>>) => {\n if (e.nativeEvent.isComposing) {\n return;\n }\n switch (e.key) {\n case 'Enter':\n case 'Tab':\n // Prevent form submission if menu is open since we may be selecting a option\n if (state.isOpen && e.key === 'Enter') {\n e.preventDefault();\n }\n\n // If the focused item is a link, trigger opening it. Items that are links are not selectable.\n if (state.isOpen && listBoxRef.current && state.selectionManager.focusedKey != null) {\n let collectionItem = state.collection.getItem(state.selectionManager.focusedKey);\n if (collectionItem?.props.href) {\n let item = listBoxRef.current.querySelector(`[data-key=\"${CSS.escape(state.selectionManager.focusedKey.toString())}\"]`);\n if (e.key === 'Enter' && item instanceof HTMLAnchorElement) {\n router.open(item, e, collectionItem.props.href, collectionItem.props.routerOptions as RouterOptions);\n }\n state.close();\n break;\n } else if (collectionItem?.props.onAction) {\n collectionItem.props.onAction();\n state.close();\n break;\n }\n }\n state.commit();\n break;\n case 'Escape':\n if (\n !state.selectionManager.isEmpty ||\n state.inputValue === '' ||\n props.allowsCustomValue\n ) {\n e.continuePropagation();\n }\n state.revert();\n break;\n case 'ArrowDown':\n state.open('first', 'manual');\n break;\n case 'ArrowUp':\n state.open('last', 'manual');\n break;\n case 'ArrowLeft':\n case 'ArrowRight':\n state.selectionManager.setFocusedKey(null);\n break;\n }\n };\n\n let onBlur = (e: FocusEvent<HTMLInputElement>) => {\n let blurFromButton = buttonRef?.current && buttonRef.current === e.relatedTarget;\n let blurIntoPopover = nodeContains(popoverRef.current, e.relatedTarget);\n // Ignore blur if focused moved to the button(if exists) or into the popover.\n if (blurFromButton || blurIntoPopover) {\n return;\n }\n\n if (props.onBlur) {\n props.onBlur(e);\n }\n\n state.setFocused(false);\n };\n\n let onFocus = (e: FocusEvent<HTMLInputElement>) => {\n if (state.isFocused) {\n return;\n }\n\n if (props.onFocus) {\n props.onFocus(e);\n }\n\n state.setFocused(true);\n };\n\n let valueId = useValueId([state.selectedItems, state.selectionManager.selectionMode]);\n let {isInvalid, validationErrors, validationDetails} = state.displayValidation;\n let {labelProps, inputProps, descriptionProps, errorMessageProps} = useTextField({\n ...props,\n onChange: state.setInputValue,\n onKeyDown: !isReadOnly ? chain(state.isOpen && collectionProps.onKeyDown, onKeyDown, props.onKeyDown) : props.onKeyDown,\n onBlur,\n value: state.inputValue,\n defaultValue: state.defaultInputValue,\n onFocus,\n autoComplete: 'off',\n validate: undefined,\n [privateValidationStateProp]: state,\n 'aria-describedby': [valueId, props['aria-describedby']].filter(Boolean).join(' ') || undefined\n }, inputRef);\n\n useFormReset(inputRef, state.defaultValue, state.setValue);\n\n // Press handlers for the ComboBox button\n let onPress = (e: PressEvent) => {\n if (e.pointerType === 'touch') {\n // Focus the input field in case it isn't focused yet\n inputRef.current?.focus();\n state.toggle(null, 'manual');\n }\n };\n\n let onPressStart = (e: PressEvent) => {\n if (e.pointerType !== 'touch') {\n inputRef.current?.focus();\n state.toggle((e.pointerType === 'keyboard' || e.pointerType === 'virtual') ? 'first' : null, 'manual');\n }\n };\n\n let triggerLabelProps = useLabels({\n id: menuTriggerProps.id,\n 'aria-label': stringFormatter.format('buttonLabel'),\n 'aria-labelledby': props['aria-labelledby'] || labelProps.id\n });\n\n let listBoxProps = useLabels({\n id: menuProps.id,\n 'aria-label': stringFormatter.format('listboxLabel'),\n 'aria-labelledby': props['aria-labelledby'] || labelProps.id\n });\n\n // If a touch happens on direct center of ComboBox input, might be virtual click from iPad so open ComboBox menu\n let lastEventTime = useRef(0);\n let onTouchEnd = (e: TouchEvent) => {\n if (isDisabled || isReadOnly) {\n return;\n }\n\n // Sometimes VoiceOver on iOS fires two touchend events in quick succession. Ignore the second one.\n if (e.timeStamp - lastEventTime.current < 500) {\n e.preventDefault();\n inputRef.current?.focus();\n return;\n }\n\n let rect = (getEventTarget(e) as Element).getBoundingClientRect();\n let touch = e.changedTouches[0];\n\n let centerX = Math.ceil(rect.left + .5 * rect.width);\n let centerY = Math.ceil(rect.top + .5 * rect.height);\n\n if (touch.clientX === centerX && touch.clientY === centerY) {\n e.preventDefault();\n inputRef.current?.focus();\n state.toggle(null, 'manual');\n\n lastEventTime.current = e.timeStamp;\n }\n };\n\n // VoiceOver has issues with announcing aria-activedescendant properly on change\n // (especially on iOS). We use a live region announcer to announce focus changes\n // manually. In addition, section titles are announced when navigating into a new section.\n let focusedItem = state.selectionManager.focusedKey != null && state.isOpen\n ? state.collection.getItem(state.selectionManager.focusedKey)\n : undefined;\n let sectionKey = focusedItem?.parentKey ?? null;\n let itemKey = state.selectionManager.focusedKey ?? null;\n let lastSection = useRef(sectionKey);\n let lastItem = useRef(itemKey);\n useEffect(() => {\n if (isAppleDevice() && focusedItem != null && itemKey != null && itemKey !== lastItem.current) {\n let isSelected = state.selectionManager.isSelected(itemKey);\n let section = sectionKey != null ? state.collection.getItem(sectionKey) : null;\n let sectionTitle = section?.['aria-label'] || (typeof section?.rendered === 'string' ? section.rendered : '') || '';\n\n let announcement = stringFormatter.format('focusAnnouncement', {\n isGroupChange: (section && sectionKey !== lastSection.current) ?? false,\n groupTitle: sectionTitle,\n groupCount: section ? [...getChildNodes(section, state.collection)].length : 0,\n optionText: focusedItem['aria-label'] || focusedItem.textValue || '',\n isSelected\n });\n\n announce(announcement);\n }\n\n lastSection.current = sectionKey;\n lastItem.current = itemKey;\n });\n\n // Announce the number of available suggestions when it changes\n let optionCount = getItemCount(state.collection);\n let lastSize = useRef(optionCount);\n let lastOpen = useRef(state.isOpen);\n useEffect(() => {\n // Only announce the number of options available when the menu opens if there is no\n // focused item, otherwise screen readers will typically read e.g. \"1 of 6\".\n // The exception is VoiceOver since this isn't included in the message above.\n let didOpenWithoutFocusedItem =\n state.isOpen !== lastOpen.current &&\n (state.selectionManager.focusedKey == null || isAppleDevice());\n\n if (state.isOpen && (didOpenWithoutFocusedItem || optionCount !== lastSize.current)) {\n let announcement = stringFormatter.format('countAnnouncement', {optionCount});\n announce(announcement);\n }\n\n lastSize.current = optionCount;\n lastOpen.current = state.isOpen;\n });\n\n // Announce when a selection occurs for VoiceOver. Other screen readers typically do this automatically.\n // TODO: do we need to do this for multi-select?\n let lastSelectedKey = useRef(state.selectedKey);\n useEffect(() => {\n if (isAppleDevice() && state.isFocused && state.selectedItem && state.selectedKey !== lastSelectedKey.current) {\n let optionText = state.selectedItem['aria-label'] || state.selectedItem.textValue || '';\n let announcement = stringFormatter.format('selectedAnnouncement', {optionText});\n announce(announcement);\n }\n\n lastSelectedKey.current = state.selectedKey;\n });\n\n useEffect(() => {\n if (state.isOpen) {\n return ariaHideOutside([inputRef.current, popoverRef.current].filter(element => element != null));\n }\n }, [state.isOpen, inputRef, popoverRef]);\n\n useUpdateEffect(() => {\n // Re-show focus ring when there is no virtually focused item.\n if (!focusedItem && inputRef.current && getActiveElement(getOwnerDocument(inputRef.current)) === inputRef.current) {\n dispatchVirtualFocus(inputRef.current, null);\n }\n }, [focusedItem]);\n\n useEvent(listBoxRef, 'react-aria-item-action', state.isOpen ? () => {\n state.close();\n } : undefined);\n\n // usePopover -> useOverlay calls useInteractOutside, but ComboBox is non-modal, so `isDismissable` is false\n // Because of this, onInteractOutside is not passed to useInteractOutside, so we need to call it here.\n useInteractOutside({\n ref: popoverRef,\n onInteractOutside: (e) => {\n let target = getEventTarget(e) as Element;\n if (nodeContains(buttonRef?.current, target) || nodeContains(inputRef.current, target)) {\n return;\n }\n state.close();\n },\n isDisabled: !state.isOpen\n });\n\n return {\n labelProps,\n buttonProps: {\n ...menuTriggerProps,\n ...triggerLabelProps,\n excludeFromTabOrder: true,\n preventFocusOnPress: true,\n onPress,\n onPressStart,\n isDisabled: isDisabled || isReadOnly\n },\n inputProps: mergeProps(inputProps, {\n role: 'combobox',\n 'aria-expanded': menuTriggerProps['aria-expanded'],\n 'aria-controls': state.isOpen ? menuProps.id : undefined,\n // TODO: readd proper logic for completionMode = complete (aria-autocomplete: both)\n 'aria-autocomplete': 'list',\n 'aria-activedescendant': focusedItem ? getItemId(state, focusedItem.key) : undefined,\n onTouchEnd,\n // This disable's iOS's autocorrect suggestions, since the combo box provides its own suggestions.\n autoCorrect: 'off',\n // This disable's the macOS Safari spell check auto corrections.\n spellCheck: 'false'\n }),\n listBoxProps: mergeProps(menuProps, listBoxProps, {\n autoFocus: state.focusStrategy || true,\n shouldUseVirtualFocus: true,\n shouldSelectOnPressUp: true,\n shouldFocusOnHover: true,\n linkBehavior: 'selection' as const,\n ['UNSTABLE_itemBehavior']: 'action'\n }),\n valueProps: {\n id: valueId\n },\n descriptionProps,\n errorMessageProps,\n isInvalid,\n validationErrors,\n validationDetails\n };\n}\n\n// This is a modified version of useSlotId that uses useEffect instead of useLayoutEffect.\n// Triggering re-renders from useLayoutEffect breaks useComboBoxState's useEffect logic in React 18.\n// These re-renders preempt async state updates in the useEffect, which ends up running multiple times\n// prior to the state being updated. This results in onSelectionChange being called multiple times.\n// TODO: refactor useComboBoxState to avoid this.\nfunction useValueId(depArray: ReadonlyArray<any> = []): string | undefined {\n let id = useId();\n let [exists, setExists] = useState(true);\n let [lastDeps, setLastDeps] = useState(depArray);\n\n // If the deps changed, set exists to true so we can test whether the element exists.\n if (lastDeps.some((v, i) => !Object.is(v, depArray[i]))) {\n setExists(true);\n setLastDeps(depArray);\n }\n\n useEffect(() => {\n if (exists && !document.getElementById(id)) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setExists(false);\n }\n }, [id, exists, lastDeps]);\n\n return exists ? id : undefined;\n}\n"],"names":[],"version":3,"file":"useComboBox.main.js.map"}
@@ -2,12 +2,13 @@ import $dShfP$intlStringsmodulejs from "./intlStrings.mjs";
2
2
  import {announce as $dShfP$announce} from "@react-aria/live-announcer";
3
3
  import {ariaHideOutside as $dShfP$ariaHideOutside} from "@react-aria/overlays";
4
4
  import {listData as $dShfP$listData, getItemId as $dShfP$getItemId} from "@react-aria/listbox";
5
- import {useRouter as $dShfP$useRouter, nodeContains as $dShfP$nodeContains, chain as $dShfP$chain, useFormReset as $dShfP$useFormReset, useLabels as $dShfP$useLabels, isAppleDevice as $dShfP$isAppleDevice, useUpdateEffect as $dShfP$useUpdateEffect, getActiveElement as $dShfP$getActiveElement, getOwnerDocument as $dShfP$getOwnerDocument, useEvent as $dShfP$useEvent, mergeProps as $dShfP$mergeProps} from "@react-aria/utils";
5
+ import {useRouter as $dShfP$useRouter, nodeContains as $dShfP$nodeContains, chain as $dShfP$chain, useFormReset as $dShfP$useFormReset, useLabels as $dShfP$useLabels, getEventTarget as $dShfP$getEventTarget, isAppleDevice as $dShfP$isAppleDevice, useUpdateEffect as $dShfP$useUpdateEffect, getActiveElement as $dShfP$getActiveElement, getOwnerDocument as $dShfP$getOwnerDocument, useEvent as $dShfP$useEvent, mergeProps as $dShfP$mergeProps, useId as $dShfP$useId} from "@react-aria/utils";
6
6
  import {dispatchVirtualFocus as $dShfP$dispatchVirtualFocus} from "@react-aria/focus";
7
- import {useRef as $dShfP$useRef, useMemo as $dShfP$useMemo, useEffect as $dShfP$useEffect} from "react";
7
+ import {useRef as $dShfP$useRef, useMemo as $dShfP$useMemo, useEffect as $dShfP$useEffect, useState as $dShfP$useState} from "react";
8
8
  import {getChildNodes as $dShfP$getChildNodes, getItemCount as $dShfP$getItemCount} from "@react-stately/collections";
9
9
  import {ListKeyboardDelegate as $dShfP$ListKeyboardDelegate, useSelectableCollection as $dShfP$useSelectableCollection} from "@react-aria/selection";
10
10
  import {privateValidationStateProp as $dShfP$privateValidationStateProp} from "@react-stately/form";
11
+ import {useInteractOutside as $dShfP$useInteractOutside} from "@react-aria/interactions";
11
12
  import {useLocalizedStringFormatter as $dShfP$useLocalizedStringFormatter} from "@react-aria/i18n";
12
13
  import {useMenuTrigger as $dShfP$useMenuTrigger} from "@react-aria/menu";
13
14
  import {useTextField as $dShfP$useTextField} from "@react-aria/textfield";
@@ -39,6 +40,7 @@ function $parcel$interopDefault(a) {
39
40
 
40
41
 
41
42
 
43
+
42
44
  function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
43
45
  let { buttonRef: buttonRef, popoverRef: popoverRef, inputRef: inputRef, listBoxRef: listBoxRef, keyboardDelegate: keyboardDelegate, layoutDelegate: layoutDelegate, shouldFocusWrap: // completionMode = 'suggest',
44
46
  shouldFocusWrap, isReadOnly: isReadOnly, isDisabled: isDisabled } = props;
@@ -106,7 +108,7 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
106
108
  state.commit();
107
109
  break;
108
110
  case 'Escape':
109
- if (state.selectedKey !== null || state.inputValue === '' || props.allowsCustomValue) e.continuePropagation();
111
+ if (!state.selectionManager.isEmpty || state.inputValue === '' || props.allowsCustomValue) e.continuePropagation();
110
112
  state.revert();
111
113
  break;
112
114
  case 'ArrowDown':
@@ -134,6 +136,10 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
134
136
  if (props.onFocus) props.onFocus(e);
135
137
  state.setFocused(true);
136
138
  };
139
+ let valueId = $c350ade66beef0af$var$useValueId([
140
+ state.selectedItems,
141
+ state.selectionManager.selectionMode
142
+ ]);
137
143
  let { isInvalid: isInvalid, validationErrors: validationErrors, validationDetails: validationDetails } = state.displayValidation;
138
144
  let { labelProps: labelProps, inputProps: inputProps, descriptionProps: descriptionProps, errorMessageProps: errorMessageProps } = (0, $dShfP$useTextField)({
139
145
  ...props,
@@ -145,9 +151,13 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
145
151
  onFocus: onFocus,
146
152
  autoComplete: 'off',
147
153
  validate: undefined,
148
- [(0, $dShfP$privateValidationStateProp)]: state
154
+ [(0, $dShfP$privateValidationStateProp)]: state,
155
+ 'aria-describedby': [
156
+ valueId,
157
+ props['aria-describedby']
158
+ ].filter(Boolean).join(' ') || undefined
149
159
  }, inputRef);
150
- (0, $dShfP$useFormReset)(inputRef, state.defaultSelectedKey, state.setSelectedKey);
160
+ (0, $dShfP$useFormReset)(inputRef, state.defaultValue, state.setValue);
151
161
  // Press handlers for the ComboBox button
152
162
  let onPress = (e)=>{
153
163
  if (e.pointerType === 'touch') {
@@ -185,7 +195,7 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
185
195
  (_inputRef_current = inputRef.current) === null || _inputRef_current === void 0 ? void 0 : _inputRef_current.focus();
186
196
  return;
187
197
  }
188
- let rect = e.target.getBoundingClientRect();
198
+ let rect = (0, $dShfP$getEventTarget)(e).getBoundingClientRect();
189
199
  let touch = e.changedTouches[0];
190
200
  let centerX = Math.ceil(rect.left + .5 * rect.width);
191
201
  let centerY = Math.ceil(rect.top + .5 * rect.height);
@@ -246,6 +256,7 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
246
256
  lastOpen.current = state.isOpen;
247
257
  });
248
258
  // Announce when a selection occurs for VoiceOver. Other screen readers typically do this automatically.
259
+ // TODO: do we need to do this for multi-select?
249
260
  let lastSelectedKey = (0, $dShfP$useRef)(state.selectedKey);
250
261
  (0, $dShfP$useEffect)(()=>{
251
262
  if ((0, $dShfP$isAppleDevice)() && state.isFocused && state.selectedItem && state.selectedKey !== lastSelectedKey.current) {
@@ -276,6 +287,17 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
276
287
  (0, $dShfP$useEvent)(listBoxRef, 'react-aria-item-action', state.isOpen ? ()=>{
277
288
  state.close();
278
289
  } : undefined);
290
+ // usePopover -> useOverlay calls useInteractOutside, but ComboBox is non-modal, so `isDismissable` is false
291
+ // Because of this, onInteractOutside is not passed to useInteractOutside, so we need to call it here.
292
+ (0, $dShfP$useInteractOutside)({
293
+ ref: popoverRef,
294
+ onInteractOutside: (e)=>{
295
+ let target = (0, $dShfP$getEventTarget)(e);
296
+ if ((0, $dShfP$nodeContains)(buttonRef === null || buttonRef === void 0 ? void 0 : buttonRef.current, target) || (0, $dShfP$nodeContains)(inputRef.current, target)) return;
297
+ state.close();
298
+ },
299
+ isDisabled: !state.isOpen
300
+ });
279
301
  return {
280
302
  labelProps: labelProps,
281
303
  buttonProps: {
@@ -308,6 +330,9 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
308
330
  linkBehavior: 'selection',
309
331
  ['UNSTABLE_itemBehavior']: 'action'
310
332
  }),
333
+ valueProps: {
334
+ id: valueId
335
+ },
311
336
  descriptionProps: descriptionProps,
312
337
  errorMessageProps: errorMessageProps,
313
338
  isInvalid: isInvalid,
@@ -315,6 +340,30 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
315
340
  validationDetails: validationDetails
316
341
  };
317
342
  }
343
+ // This is a modified version of useSlotId that uses useEffect instead of useLayoutEffect.
344
+ // Triggering re-renders from useLayoutEffect breaks useComboBoxState's useEffect logic in React 18.
345
+ // These re-renders preempt async state updates in the useEffect, which ends up running multiple times
346
+ // prior to the state being updated. This results in onSelectionChange being called multiple times.
347
+ // TODO: refactor useComboBoxState to avoid this.
348
+ function $c350ade66beef0af$var$useValueId(depArray = []) {
349
+ let id = (0, $dShfP$useId)();
350
+ let [exists, setExists] = (0, $dShfP$useState)(true);
351
+ let [lastDeps, setLastDeps] = (0, $dShfP$useState)(depArray);
352
+ // If the deps changed, set exists to true so we can test whether the element exists.
353
+ if (lastDeps.some((v, i)=>!Object.is(v, depArray[i]))) {
354
+ setExists(true);
355
+ setLastDeps(depArray);
356
+ }
357
+ (0, $dShfP$useEffect)(()=>{
358
+ if (exists && !document.getElementById(id)) // eslint-disable-next-line react-hooks/set-state-in-effect
359
+ setExists(false);
360
+ }, [
361
+ id,
362
+ exists,
363
+ lastDeps
364
+ ]);
365
+ return exists ? id : undefined;
366
+ }
318
367
 
319
368
 
320
369
  export {$c350ade66beef0af$export$8c18d1b4f7232bbf as useComboBox};
@@ -2,12 +2,13 @@ import $dShfP$intlStringsmodulejs from "./intlStrings.module.js";
2
2
  import {announce as $dShfP$announce} from "@react-aria/live-announcer";
3
3
  import {ariaHideOutside as $dShfP$ariaHideOutside} from "@react-aria/overlays";
4
4
  import {listData as $dShfP$listData, getItemId as $dShfP$getItemId} from "@react-aria/listbox";
5
- import {useRouter as $dShfP$useRouter, nodeContains as $dShfP$nodeContains, chain as $dShfP$chain, useFormReset as $dShfP$useFormReset, useLabels as $dShfP$useLabels, isAppleDevice as $dShfP$isAppleDevice, useUpdateEffect as $dShfP$useUpdateEffect, getActiveElement as $dShfP$getActiveElement, getOwnerDocument as $dShfP$getOwnerDocument, useEvent as $dShfP$useEvent, mergeProps as $dShfP$mergeProps} from "@react-aria/utils";
5
+ import {useRouter as $dShfP$useRouter, nodeContains as $dShfP$nodeContains, chain as $dShfP$chain, useFormReset as $dShfP$useFormReset, useLabels as $dShfP$useLabels, getEventTarget as $dShfP$getEventTarget, isAppleDevice as $dShfP$isAppleDevice, useUpdateEffect as $dShfP$useUpdateEffect, getActiveElement as $dShfP$getActiveElement, getOwnerDocument as $dShfP$getOwnerDocument, useEvent as $dShfP$useEvent, mergeProps as $dShfP$mergeProps, useId as $dShfP$useId} from "@react-aria/utils";
6
6
  import {dispatchVirtualFocus as $dShfP$dispatchVirtualFocus} from "@react-aria/focus";
7
- import {useRef as $dShfP$useRef, useMemo as $dShfP$useMemo, useEffect as $dShfP$useEffect} from "react";
7
+ import {useRef as $dShfP$useRef, useMemo as $dShfP$useMemo, useEffect as $dShfP$useEffect, useState as $dShfP$useState} from "react";
8
8
  import {getChildNodes as $dShfP$getChildNodes, getItemCount as $dShfP$getItemCount} from "@react-stately/collections";
9
9
  import {ListKeyboardDelegate as $dShfP$ListKeyboardDelegate, useSelectableCollection as $dShfP$useSelectableCollection} from "@react-aria/selection";
10
10
  import {privateValidationStateProp as $dShfP$privateValidationStateProp} from "@react-stately/form";
11
+ import {useInteractOutside as $dShfP$useInteractOutside} from "@react-aria/interactions";
11
12
  import {useLocalizedStringFormatter as $dShfP$useLocalizedStringFormatter} from "@react-aria/i18n";
12
13
  import {useMenuTrigger as $dShfP$useMenuTrigger} from "@react-aria/menu";
13
14
  import {useTextField as $dShfP$useTextField} from "@react-aria/textfield";
@@ -39,6 +40,7 @@ function $parcel$interopDefault(a) {
39
40
 
40
41
 
41
42
 
43
+
42
44
  function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
43
45
  let { buttonRef: buttonRef, popoverRef: popoverRef, inputRef: inputRef, listBoxRef: listBoxRef, keyboardDelegate: keyboardDelegate, layoutDelegate: layoutDelegate, shouldFocusWrap: // completionMode = 'suggest',
44
46
  shouldFocusWrap, isReadOnly: isReadOnly, isDisabled: isDisabled } = props;
@@ -106,7 +108,7 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
106
108
  state.commit();
107
109
  break;
108
110
  case 'Escape':
109
- if (state.selectedKey !== null || state.inputValue === '' || props.allowsCustomValue) e.continuePropagation();
111
+ if (!state.selectionManager.isEmpty || state.inputValue === '' || props.allowsCustomValue) e.continuePropagation();
110
112
  state.revert();
111
113
  break;
112
114
  case 'ArrowDown':
@@ -134,6 +136,10 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
134
136
  if (props.onFocus) props.onFocus(e);
135
137
  state.setFocused(true);
136
138
  };
139
+ let valueId = $c350ade66beef0af$var$useValueId([
140
+ state.selectedItems,
141
+ state.selectionManager.selectionMode
142
+ ]);
137
143
  let { isInvalid: isInvalid, validationErrors: validationErrors, validationDetails: validationDetails } = state.displayValidation;
138
144
  let { labelProps: labelProps, inputProps: inputProps, descriptionProps: descriptionProps, errorMessageProps: errorMessageProps } = (0, $dShfP$useTextField)({
139
145
  ...props,
@@ -145,9 +151,13 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
145
151
  onFocus: onFocus,
146
152
  autoComplete: 'off',
147
153
  validate: undefined,
148
- [(0, $dShfP$privateValidationStateProp)]: state
154
+ [(0, $dShfP$privateValidationStateProp)]: state,
155
+ 'aria-describedby': [
156
+ valueId,
157
+ props['aria-describedby']
158
+ ].filter(Boolean).join(' ') || undefined
149
159
  }, inputRef);
150
- (0, $dShfP$useFormReset)(inputRef, state.defaultSelectedKey, state.setSelectedKey);
160
+ (0, $dShfP$useFormReset)(inputRef, state.defaultValue, state.setValue);
151
161
  // Press handlers for the ComboBox button
152
162
  let onPress = (e)=>{
153
163
  if (e.pointerType === 'touch') {
@@ -185,7 +195,7 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
185
195
  (_inputRef_current = inputRef.current) === null || _inputRef_current === void 0 ? void 0 : _inputRef_current.focus();
186
196
  return;
187
197
  }
188
- let rect = e.target.getBoundingClientRect();
198
+ let rect = (0, $dShfP$getEventTarget)(e).getBoundingClientRect();
189
199
  let touch = e.changedTouches[0];
190
200
  let centerX = Math.ceil(rect.left + .5 * rect.width);
191
201
  let centerY = Math.ceil(rect.top + .5 * rect.height);
@@ -246,6 +256,7 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
246
256
  lastOpen.current = state.isOpen;
247
257
  });
248
258
  // Announce when a selection occurs for VoiceOver. Other screen readers typically do this automatically.
259
+ // TODO: do we need to do this for multi-select?
249
260
  let lastSelectedKey = (0, $dShfP$useRef)(state.selectedKey);
250
261
  (0, $dShfP$useEffect)(()=>{
251
262
  if ((0, $dShfP$isAppleDevice)() && state.isFocused && state.selectedItem && state.selectedKey !== lastSelectedKey.current) {
@@ -276,6 +287,17 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
276
287
  (0, $dShfP$useEvent)(listBoxRef, 'react-aria-item-action', state.isOpen ? ()=>{
277
288
  state.close();
278
289
  } : undefined);
290
+ // usePopover -> useOverlay calls useInteractOutside, but ComboBox is non-modal, so `isDismissable` is false
291
+ // Because of this, onInteractOutside is not passed to useInteractOutside, so we need to call it here.
292
+ (0, $dShfP$useInteractOutside)({
293
+ ref: popoverRef,
294
+ onInteractOutside: (e)=>{
295
+ let target = (0, $dShfP$getEventTarget)(e);
296
+ if ((0, $dShfP$nodeContains)(buttonRef === null || buttonRef === void 0 ? void 0 : buttonRef.current, target) || (0, $dShfP$nodeContains)(inputRef.current, target)) return;
297
+ state.close();
298
+ },
299
+ isDisabled: !state.isOpen
300
+ });
279
301
  return {
280
302
  labelProps: labelProps,
281
303
  buttonProps: {
@@ -308,6 +330,9 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
308
330
  linkBehavior: 'selection',
309
331
  ['UNSTABLE_itemBehavior']: 'action'
310
332
  }),
333
+ valueProps: {
334
+ id: valueId
335
+ },
311
336
  descriptionProps: descriptionProps,
312
337
  errorMessageProps: errorMessageProps,
313
338
  isInvalid: isInvalid,
@@ -315,6 +340,30 @@ function $c350ade66beef0af$export$8c18d1b4f7232bbf(props, state) {
315
340
  validationDetails: validationDetails
316
341
  };
317
342
  }
343
+ // This is a modified version of useSlotId that uses useEffect instead of useLayoutEffect.
344
+ // Triggering re-renders from useLayoutEffect breaks useComboBoxState's useEffect logic in React 18.
345
+ // These re-renders preempt async state updates in the useEffect, which ends up running multiple times
346
+ // prior to the state being updated. This results in onSelectionChange being called multiple times.
347
+ // TODO: refactor useComboBoxState to avoid this.
348
+ function $c350ade66beef0af$var$useValueId(depArray = []) {
349
+ let id = (0, $dShfP$useId)();
350
+ let [exists, setExists] = (0, $dShfP$useState)(true);
351
+ let [lastDeps, setLastDeps] = (0, $dShfP$useState)(depArray);
352
+ // If the deps changed, set exists to true so we can test whether the element exists.
353
+ if (lastDeps.some((v, i)=>!Object.is(v, depArray[i]))) {
354
+ setExists(true);
355
+ setLastDeps(depArray);
356
+ }
357
+ (0, $dShfP$useEffect)(()=>{
358
+ if (exists && !document.getElementById(id)) // eslint-disable-next-line react-hooks/set-state-in-effect
359
+ setExists(false);
360
+ }, [
361
+ id,
362
+ exists,
363
+ lastDeps
364
+ ]);
365
+ return exists ? id : undefined;
366
+ }
318
367
 
319
368
 
320
369
  export {$c350ade66beef0af$export$8c18d1b4f7232bbf as useComboBox};
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/@react-aria/combobox/src/useComboBox.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {announce} from '@react-aria/live-announcer';\nimport {AriaButtonProps} from '@react-types/button';\nimport {AriaComboBoxProps} from '@react-types/combobox';\nimport {ariaHideOutside} from '@react-aria/overlays';\nimport {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox';\nimport {BaseEvent, DOMAttributes, KeyboardDelegate, LayoutDelegate, PressEvent, RefObject, RouterOptions, ValidationResult} from '@react-types/shared';\nimport {chain, getActiveElement, getOwnerDocument, isAppleDevice, mergeProps, nodeContains, useEvent, useFormReset, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';\nimport {ComboBoxState} from '@react-stately/combobox';\nimport {dispatchVirtualFocus} from '@react-aria/focus';\nimport {FocusEvent, InputHTMLAttributes, KeyboardEvent, TouchEvent, useEffect, useMemo, useRef} from 'react';\nimport {getChildNodes, getItemCount} from '@react-stately/collections';\n// @ts-ignore\nimport intlMessages from '../intl/*.json';\nimport {ListKeyboardDelegate, useSelectableCollection} from '@react-aria/selection';\nimport {privateValidationStateProp} from '@react-stately/form';\nimport {useLocalizedStringFormatter} from '@react-aria/i18n';\nimport {useMenuTrigger} from '@react-aria/menu';\nimport {useTextField} from '@react-aria/textfield';\n\nexport interface AriaComboBoxOptions<T> extends Omit<AriaComboBoxProps<T>, 'children'> {\n /** The ref for the input element. */\n inputRef: RefObject<HTMLInputElement | null>,\n /** The ref for the list box popover. */\n popoverRef: RefObject<Element | null>,\n /** The ref for the list box. */\n listBoxRef: RefObject<HTMLElement | null>,\n /** The ref for the optional list box popup trigger button. */\n buttonRef?: RefObject<Element | null>,\n /** An optional keyboard delegate implementation, to override the default. */\n keyboardDelegate?: KeyboardDelegate,\n /**\n * A delegate object that provides layout information for items in the collection.\n * By default this uses the DOM, but this can be overridden to implement things like\n * virtualized scrolling.\n */\n layoutDelegate?: LayoutDelegate\n}\n\nexport interface ComboBoxAria<T> extends ValidationResult {\n /** Props for the label element. */\n labelProps: DOMAttributes,\n /** Props for the combo box input element. */\n inputProps: InputHTMLAttributes<HTMLInputElement>,\n /** Props for the list box, to be passed to `useListBox`. */\n listBoxProps: AriaListBoxOptions<T>,\n /** Props for the optional trigger button, to be passed to `useButton`. */\n buttonProps: AriaButtonProps,\n /** Props for the combo box description element, if any. */\n descriptionProps: DOMAttributes,\n /** Props for the combo box error message element, if any. */\n errorMessageProps: DOMAttributes\n}\n\n/**\n * Provides the behavior and accessibility implementation for a combo box component.\n * A combo box combines a text input with a listbox, allowing users to filter a list of options to items matching a query.\n * @param props - Props for the combo box.\n * @param state - State for the select, as returned by `useComboBoxState`.\n */\nexport function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxState<T>): ComboBoxAria<T> {\n let {\n buttonRef,\n popoverRef,\n inputRef,\n listBoxRef,\n keyboardDelegate,\n layoutDelegate,\n // completionMode = 'suggest',\n shouldFocusWrap,\n isReadOnly,\n isDisabled\n } = props;\n let backupBtnRef = useRef(null);\n buttonRef = buttonRef ?? backupBtnRef;\n\n let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/combobox');\n let {menuTriggerProps, menuProps} = useMenuTrigger<T>(\n {\n type: 'listbox',\n isDisabled: isDisabled || isReadOnly\n },\n state,\n buttonRef\n );\n\n // Set listbox id so it can be used when calling getItemId later\n listData.set(state, {id: menuProps.id});\n\n // By default, a KeyboardDelegate is provided which uses the DOM to query layout information (e.g. for page up/page down).\n // When virtualized, the layout object will be passed in as a prop and override this.\n let {collection} = state;\n let {disabledKeys} = state.selectionManager;\n let delegate = useMemo(() => (\n keyboardDelegate || new ListKeyboardDelegate({\n collection,\n disabledKeys,\n ref: listBoxRef,\n layoutDelegate\n })\n ), [keyboardDelegate, layoutDelegate, collection, disabledKeys, listBoxRef]);\n\n // Use useSelectableCollection to get the keyboard handlers to apply to the textfield\n let {collectionProps} = useSelectableCollection({\n selectionManager: state.selectionManager,\n keyboardDelegate: delegate,\n disallowTypeAhead: true,\n disallowEmptySelection: true,\n shouldFocusWrap,\n ref: inputRef,\n // Prevent item scroll behavior from being applied here, should be handled in the user's Popover + ListBox component\n isVirtualized: true\n });\n\n let router = useRouter();\n\n // For textfield specific keydown operations\n let onKeyDown = (e: BaseEvent<KeyboardEvent<any>>) => {\n if (e.nativeEvent.isComposing) {\n return;\n }\n switch (e.key) {\n case 'Enter':\n case 'Tab':\n // Prevent form submission if menu is open since we may be selecting a option\n if (state.isOpen && e.key === 'Enter') {\n e.preventDefault();\n }\n\n // If the focused item is a link, trigger opening it. Items that are links are not selectable.\n if (state.isOpen && listBoxRef.current && state.selectionManager.focusedKey != null) {\n let collectionItem = state.collection.getItem(state.selectionManager.focusedKey);\n if (collectionItem?.props.href) {\n let item = listBoxRef.current.querySelector(`[data-key=\"${CSS.escape(state.selectionManager.focusedKey.toString())}\"]`);\n if (e.key === 'Enter' && item instanceof HTMLAnchorElement) {\n router.open(item, e, collectionItem.props.href, collectionItem.props.routerOptions as RouterOptions);\n }\n state.close();\n break;\n } else if (collectionItem?.props.onAction) {\n collectionItem.props.onAction();\n state.close();\n break;\n }\n }\n state.commit();\n break;\n case 'Escape':\n if (\n state.selectedKey !== null ||\n state.inputValue === '' ||\n props.allowsCustomValue\n ) {\n e.continuePropagation();\n }\n state.revert();\n break;\n case 'ArrowDown':\n state.open('first', 'manual');\n break;\n case 'ArrowUp':\n state.open('last', 'manual');\n break;\n case 'ArrowLeft':\n case 'ArrowRight':\n state.selectionManager.setFocusedKey(null);\n break;\n }\n };\n\n let onBlur = (e: FocusEvent<HTMLInputElement>) => {\n let blurFromButton = buttonRef?.current && buttonRef.current === e.relatedTarget;\n let blurIntoPopover = nodeContains(popoverRef.current, e.relatedTarget);\n // Ignore blur if focused moved to the button(if exists) or into the popover.\n if (blurFromButton || blurIntoPopover) {\n return;\n }\n\n if (props.onBlur) {\n props.onBlur(e);\n }\n\n state.setFocused(false);\n };\n\n let onFocus = (e: FocusEvent<HTMLInputElement>) => {\n if (state.isFocused) {\n return;\n }\n\n if (props.onFocus) {\n props.onFocus(e);\n }\n\n state.setFocused(true);\n };\n\n let {isInvalid, validationErrors, validationDetails} = state.displayValidation;\n let {labelProps, inputProps, descriptionProps, errorMessageProps} = useTextField({\n ...props,\n onChange: state.setInputValue,\n onKeyDown: !isReadOnly ? chain(state.isOpen && collectionProps.onKeyDown, onKeyDown, props.onKeyDown) : props.onKeyDown,\n onBlur,\n value: state.inputValue,\n defaultValue: state.defaultInputValue,\n onFocus,\n autoComplete: 'off',\n validate: undefined,\n [privateValidationStateProp]: state\n }, inputRef);\n\n useFormReset(inputRef, state.defaultSelectedKey, state.setSelectedKey);\n \n // Press handlers for the ComboBox button\n let onPress = (e: PressEvent) => {\n if (e.pointerType === 'touch') {\n // Focus the input field in case it isn't focused yet\n inputRef.current?.focus();\n state.toggle(null, 'manual');\n }\n };\n\n let onPressStart = (e: PressEvent) => {\n if (e.pointerType !== 'touch') {\n inputRef.current?.focus();\n state.toggle((e.pointerType === 'keyboard' || e.pointerType === 'virtual') ? 'first' : null, 'manual');\n }\n };\n\n let triggerLabelProps = useLabels({\n id: menuTriggerProps.id,\n 'aria-label': stringFormatter.format('buttonLabel'),\n 'aria-labelledby': props['aria-labelledby'] || labelProps.id\n });\n\n let listBoxProps = useLabels({\n id: menuProps.id,\n 'aria-label': stringFormatter.format('listboxLabel'),\n 'aria-labelledby': props['aria-labelledby'] || labelProps.id\n });\n\n // If a touch happens on direct center of ComboBox input, might be virtual click from iPad so open ComboBox menu\n let lastEventTime = useRef(0);\n let onTouchEnd = (e: TouchEvent) => {\n if (isDisabled || isReadOnly) {\n return;\n }\n\n // Sometimes VoiceOver on iOS fires two touchend events in quick succession. Ignore the second one.\n if (e.timeStamp - lastEventTime.current < 500) {\n e.preventDefault();\n inputRef.current?.focus();\n return;\n }\n\n let rect = (e.target as Element).getBoundingClientRect();\n let touch = e.changedTouches[0];\n\n let centerX = Math.ceil(rect.left + .5 * rect.width);\n let centerY = Math.ceil(rect.top + .5 * rect.height);\n\n if (touch.clientX === centerX && touch.clientY === centerY) {\n e.preventDefault();\n inputRef.current?.focus();\n state.toggle(null, 'manual');\n\n lastEventTime.current = e.timeStamp;\n }\n };\n\n // VoiceOver has issues with announcing aria-activedescendant properly on change\n // (especially on iOS). We use a live region announcer to announce focus changes\n // manually. In addition, section titles are announced when navigating into a new section.\n let focusedItem = state.selectionManager.focusedKey != null && state.isOpen\n ? state.collection.getItem(state.selectionManager.focusedKey)\n : undefined;\n let sectionKey = focusedItem?.parentKey ?? null;\n let itemKey = state.selectionManager.focusedKey ?? null;\n let lastSection = useRef(sectionKey);\n let lastItem = useRef(itemKey);\n useEffect(() => {\n if (isAppleDevice() && focusedItem != null && itemKey != null && itemKey !== lastItem.current) {\n let isSelected = state.selectionManager.isSelected(itemKey);\n let section = sectionKey != null ? state.collection.getItem(sectionKey) : null;\n let sectionTitle = section?.['aria-label'] || (typeof section?.rendered === 'string' ? section.rendered : '') || '';\n\n let announcement = stringFormatter.format('focusAnnouncement', {\n isGroupChange: (section && sectionKey !== lastSection.current) ?? false,\n groupTitle: sectionTitle,\n groupCount: section ? [...getChildNodes(section, state.collection)].length : 0,\n optionText: focusedItem['aria-label'] || focusedItem.textValue || '',\n isSelected\n });\n\n announce(announcement);\n }\n\n lastSection.current = sectionKey;\n lastItem.current = itemKey;\n });\n\n // Announce the number of available suggestions when it changes\n let optionCount = getItemCount(state.collection);\n let lastSize = useRef(optionCount);\n let lastOpen = useRef(state.isOpen);\n useEffect(() => {\n // Only announce the number of options available when the menu opens if there is no\n // focused item, otherwise screen readers will typically read e.g. \"1 of 6\".\n // The exception is VoiceOver since this isn't included in the message above.\n let didOpenWithoutFocusedItem =\n state.isOpen !== lastOpen.current &&\n (state.selectionManager.focusedKey == null || isAppleDevice());\n\n if (state.isOpen && (didOpenWithoutFocusedItem || optionCount !== lastSize.current)) {\n let announcement = stringFormatter.format('countAnnouncement', {optionCount});\n announce(announcement);\n }\n\n lastSize.current = optionCount;\n lastOpen.current = state.isOpen;\n });\n\n // Announce when a selection occurs for VoiceOver. Other screen readers typically do this automatically.\n let lastSelectedKey = useRef(state.selectedKey);\n useEffect(() => {\n if (isAppleDevice() && state.isFocused && state.selectedItem && state.selectedKey !== lastSelectedKey.current) {\n let optionText = state.selectedItem['aria-label'] || state.selectedItem.textValue || '';\n let announcement = stringFormatter.format('selectedAnnouncement', {optionText});\n announce(announcement);\n }\n\n lastSelectedKey.current = state.selectedKey;\n });\n\n useEffect(() => {\n if (state.isOpen) {\n return ariaHideOutside([inputRef.current, popoverRef.current].filter(element => element != null));\n }\n }, [state.isOpen, inputRef, popoverRef]);\n\n useUpdateEffect(() => {\n // Re-show focus ring when there is no virtually focused item.\n if (!focusedItem && inputRef.current && getActiveElement(getOwnerDocument(inputRef.current)) === inputRef.current) {\n dispatchVirtualFocus(inputRef.current, null);\n }\n }, [focusedItem]);\n\n useEvent(listBoxRef, 'react-aria-item-action', state.isOpen ? () => {\n state.close();\n } : undefined);\n\n return {\n labelProps,\n buttonProps: {\n ...menuTriggerProps,\n ...triggerLabelProps,\n excludeFromTabOrder: true,\n preventFocusOnPress: true,\n onPress,\n onPressStart,\n isDisabled: isDisabled || isReadOnly\n },\n inputProps: mergeProps(inputProps, {\n role: 'combobox',\n 'aria-expanded': menuTriggerProps['aria-expanded'],\n 'aria-controls': state.isOpen ? menuProps.id : undefined,\n // TODO: readd proper logic for completionMode = complete (aria-autocomplete: both)\n 'aria-autocomplete': 'list',\n 'aria-activedescendant': focusedItem ? getItemId(state, focusedItem.key) : undefined,\n onTouchEnd,\n // This disable's iOS's autocorrect suggestions, since the combo box provides its own suggestions.\n autoCorrect: 'off',\n // This disable's the macOS Safari spell check auto corrections.\n spellCheck: 'false'\n }),\n listBoxProps: mergeProps(menuProps, listBoxProps, {\n autoFocus: state.focusStrategy || true,\n shouldUseVirtualFocus: true,\n shouldSelectOnPressUp: true,\n shouldFocusOnHover: true,\n linkBehavior: 'selection' as const,\n ['UNSTABLE_itemBehavior']: 'action'\n }),\n descriptionProps,\n errorMessageProps,\n isInvalid,\n validationErrors,\n validationDetails\n };\n}\n"],"names":[],"version":3,"file":"useComboBox.module.js.map"}
1
+ {"mappings":";;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;CAUC;;;;;;;;;;;;;;AAgEM,SAAS,0CAAmD,KAAgC,EAAE,KAA0B;IAC7H,IAAI,aACF,SAAS,cACT,UAAU,YACV,QAAQ,cACR,UAAU,oBACV,gBAAgB,kBAChB,cAAc,mBACd,8BAA8B;IAC9B,eAAe,cACf,UAAU,cACV,UAAU,EACX,GAAG;IACJ,IAAI,eAAe,CAAA,GAAA,aAAK,EAAE;IAC1B,YAAY,sBAAA,uBAAA,YAAa;IAEzB,IAAI,kBAAkB,CAAA,GAAA,kCAA0B,EAAE,CAAA,GAAA,oDAAW,GAAG;IAChE,IAAI,oBAAC,gBAAgB,aAAE,SAAS,EAAC,GAAG,CAAA,GAAA,qBAAa,EAC/C;QACE,MAAM;QACN,YAAY,cAAc;IAC5B,GACA,OACA;IAGF,gEAAgE;IAChE,CAAA,GAAA,eAAO,EAAE,GAAG,CAAC,OAAO;QAAC,IAAI,UAAU,EAAE;IAAA;IAErC,0HAA0H;IAC1H,qFAAqF;IACrF,IAAI,cAAC,UAAU,EAAC,GAAG;IACnB,IAAI,gBAAC,YAAY,EAAC,GAAG,MAAM,gBAAgB;IAC3C,IAAI,WAAW,CAAA,GAAA,cAAM,EAAE,IACrB,oBAAoB,IAAI,CAAA,GAAA,2BAAmB,EAAE;wBAC3C;0BACA;YACA,KAAK;4BACL;QACF,IACC;QAAC;QAAkB;QAAgB;QAAY;QAAc;KAAW;IAE3E,qFAAqF;IACrF,IAAI,mBAAC,eAAe,EAAC,GAAG,CAAA,GAAA,8BAAsB,EAAE;QAC9C,kBAAkB,MAAM,gBAAgB;QACxC,kBAAkB;QAClB,mBAAmB;QACnB,wBAAwB;yBACxB;QACA,KAAK;QACL,oHAAoH;QACpH,eAAe;IACjB;IAEA,IAAI,SAAS,CAAA,GAAA,gBAAQ;IAErB,4CAA4C;IAC5C,IAAI,YAAY,CAAC;QACf,IAAI,EAAE,WAAW,CAAC,WAAW,EAC3B;QAEF,OAAQ,EAAE,GAAG;YACX,KAAK;YACL,KAAK;gBACH,6EAA6E;gBAC7E,IAAI,MAAM,MAAM,IAAI,EAAE,GAAG,KAAK,SAC5B,EAAE,cAAc;gBAGlB,8FAA8F;gBAC9F,IAAI,MAAM,MAAM,IAAI,WAAW,OAAO,IAAI,MAAM,gBAAgB,CAAC,UAAU,IAAI,MAAM;oBACnF,IAAI,iBAAiB,MAAM,UAAU,CAAC,OAAO,CAAC,MAAM,gBAAgB,CAAC,UAAU;oBAC/E,IAAI,2BAAA,qCAAA,eAAgB,KAAK,CAAC,IAAI,EAAE;wBAC9B,IAAI,OAAO,WAAW,OAAO,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC,MAAM,gBAAgB,CAAC,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC;wBACtH,IAAI,EAAE,GAAG,KAAK,WAAW,gBAAgB,mBACvC,OAAO,IAAI,CAAC,MAAM,GAAG,eAAe,KAAK,CAAC,IAAI,EAAE,eAAe,KAAK,CAAC,aAAa;wBAEpF,MAAM,KAAK;wBACX;oBACF,OAAO,IAAI,2BAAA,qCAAA,eAAgB,KAAK,CAAC,QAAQ,EAAE;wBACzC,eAAe,KAAK,CAAC,QAAQ;wBAC7B,MAAM,KAAK;wBACX;oBACF;gBACF;gBACA,MAAM,MAAM;gBACZ;YACF,KAAK;gBACH,IACE,CAAC,MAAM,gBAAgB,CAAC,OAAO,IAC/B,MAAM,UAAU,KAAK,MACrB,MAAM,iBAAiB,EAEvB,EAAE,mBAAmB;gBAEvB,MAAM,MAAM;gBACZ;YACF,KAAK;gBACH,MAAM,IAAI,CAAC,SAAS;gBACpB;YACF,KAAK;gBACH,MAAM,IAAI,CAAC,QAAQ;gBACnB;YACF,KAAK;YACL,KAAK;gBACH,MAAM,gBAAgB,CAAC,aAAa,CAAC;gBACrC;QACJ;IACF;IAEA,IAAI,SAAS,CAAC;QACZ,IAAI,iBAAiB,CAAA,sBAAA,gCAAA,UAAW,OAAO,KAAI,UAAU,OAAO,KAAK,EAAE,aAAa;QAChF,IAAI,kBAAkB,CAAA,GAAA,mBAAW,EAAE,WAAW,OAAO,EAAE,EAAE,aAAa;QACtE,6EAA6E;QAC7E,IAAI,kBAAkB,iBACpB;QAGF,IAAI,MAAM,MAAM,EACd,MAAM,MAAM,CAAC;QAGf,MAAM,UAAU,CAAC;IACnB;IAEA,IAAI,UAAU,CAAC;QACb,IAAI,MAAM,SAAS,EACjB;QAGF,IAAI,MAAM,OAAO,EACf,MAAM,OAAO,CAAC;QAGhB,MAAM,UAAU,CAAC;IACnB;IAEA,IAAI,UAAU,iCAAW;QAAC,MAAM,aAAa;QAAE,MAAM,gBAAgB,CAAC,aAAa;KAAC;IACpF,IAAI,aAAC,SAAS,oBAAE,gBAAgB,qBAAE,iBAAiB,EAAC,GAAG,MAAM,iBAAiB;IAC9E,IAAI,cAAC,UAAU,cAAE,UAAU,oBAAE,gBAAgB,qBAAE,iBAAiB,EAAC,GAAG,CAAA,GAAA,mBAAW,EAAE;QAC/E,GAAG,KAAK;QACR,UAAU,MAAM,aAAa;QAC7B,WAAW,CAAC,aAAa,CAAA,GAAA,YAAI,EAAE,MAAM,MAAM,IAAI,gBAAgB,SAAS,EAAE,WAAW,MAAM,SAAS,IAAI,MAAM,SAAS;gBACvH;QACA,OAAO,MAAM,UAAU;QACvB,cAAc,MAAM,iBAAiB;iBACrC;QACA,cAAc;QACd,UAAU;QACV,CAAC,CAAA,GAAA,iCAAyB,EAAE,EAAE;QAC9B,oBAAoB;YAAC;YAAS,KAAK,CAAC,mBAAmB;SAAC,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,QAAQ;IACxF,GAAG;IAEH,CAAA,GAAA,mBAAW,EAAE,UAAU,MAAM,YAAY,EAAE,MAAM,QAAQ;IAEzD,yCAAyC;IACzC,IAAI,UAAU,CAAC;QACb,IAAI,EAAE,WAAW,KAAK,SAAS;gBAC7B,qDAAqD;YACrD;aAAA,oBAAA,SAAS,OAAO,cAAhB,wCAAA,kBAAkB,KAAK;YACvB,MAAM,MAAM,CAAC,MAAM;QACrB;IACF;IAEA,IAAI,eAAe,CAAC;QAClB,IAAI,EAAE,WAAW,KAAK,SAAS;gBAC7B;aAAA,oBAAA,SAAS,OAAO,cAAhB,wCAAA,kBAAkB,KAAK;YACvB,MAAM,MAAM,CAAC,AAAC,EAAE,WAAW,KAAK,cAAc,EAAE,WAAW,KAAK,YAAa,UAAU,MAAM;QAC/F;IACF;IAEA,IAAI,oBAAoB,CAAA,GAAA,gBAAQ,EAAE;QAChC,IAAI,iBAAiB,EAAE;QACvB,cAAc,gBAAgB,MAAM,CAAC;QACrC,mBAAmB,KAAK,CAAC,kBAAkB,IAAI,WAAW,EAAE;IAC9D;IAEA,IAAI,eAAe,CAAA,GAAA,gBAAQ,EAAE;QAC3B,IAAI,UAAU,EAAE;QAChB,cAAc,gBAAgB,MAAM,CAAC;QACrC,mBAAmB,KAAK,CAAC,kBAAkB,IAAI,WAAW,EAAE;IAC9D;IAEA,gHAAgH;IAChH,IAAI,gBAAgB,CAAA,GAAA,aAAK,EAAE;IAC3B,IAAI,aAAa,CAAC;QAChB,IAAI,cAAc,YAChB;QAGF,mGAAmG;QACnG,IAAI,EAAE,SAAS,GAAG,cAAc,OAAO,GAAG,KAAK;gBAE7C;YADA,EAAE,cAAc;aAChB,oBAAA,SAAS,OAAO,cAAhB,wCAAA,kBAAkB,KAAK;YACvB;QACF;QAEA,IAAI,OAAO,AAAC,CAAA,GAAA,qBAAa,EAAE,GAAe,qBAAqB;QAC/D,IAAI,QAAQ,EAAE,cAAc,CAAC,EAAE;QAE/B,IAAI,UAAU,KAAK,IAAI,CAAC,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK;QACnD,IAAI,UAAU,KAAK,IAAI,CAAC,KAAK,GAAG,GAAG,KAAK,KAAK,MAAM;QAEnD,IAAI,MAAM,OAAO,KAAK,WAAW,MAAM,OAAO,KAAK,SAAS;gBAE1D;YADA,EAAE,cAAc;aAChB,qBAAA,SAAS,OAAO,cAAhB,yCAAA,mBAAkB,KAAK;YACvB,MAAM,MAAM,CAAC,MAAM;YAEnB,cAAc,OAAO,GAAG,EAAE,SAAS;QACrC;IACF;IAEA,gFAAgF;IAChF,gFAAgF;IAChF,0FAA0F;IAC1F,IAAI,cAAc,MAAM,gBAAgB,CAAC,UAAU,IAAI,QAAQ,MAAM,MAAM,GACvE,MAAM,UAAU,CAAC,OAAO,CAAC,MAAM,gBAAgB,CAAC,UAAU,IAC1D;QACa;IAAjB,IAAI,aAAa,CAAA,yBAAA,wBAAA,kCAAA,YAAa,SAAS,cAAtB,oCAAA,yBAA0B;QAC7B;IAAd,IAAI,UAAU,CAAA,qCAAA,MAAM,gBAAgB,CAAC,UAAU,cAAjC,gDAAA,qCAAqC;IACnD,IAAI,cAAc,CAAA,GAAA,aAAK,EAAE;IACzB,IAAI,WAAW,CAAA,GAAA,aAAK,EAAE;IACtB,CAAA,GAAA,gBAAQ,EAAE;QACR,IAAI,CAAA,GAAA,oBAAY,OAAO,eAAe,QAAQ,WAAW,QAAQ,YAAY,SAAS,OAAO,EAAE;YAC7F,IAAI,aAAa,MAAM,gBAAgB,CAAC,UAAU,CAAC;YACnD,IAAI,UAAU,cAAc,OAAO,MAAM,UAAU,CAAC,OAAO,CAAC,cAAc;YAC1E,IAAI,eAAe,CAAA,oBAAA,8BAAA,OAAS,CAAC,aAAa,KAAK,CAAA,QAAO,oBAAA,8BAAA,QAAS,QAAQ,MAAK,WAAW,QAAQ,QAAQ,GAAG,EAAC,KAAM;gBAG/F;YADlB,IAAI,eAAe,gBAAgB,MAAM,CAAC,qBAAqB;gBAC7D,eAAe,CAAC,OAAA,WAAW,eAAe,YAAY,OAAO,cAA7C,kBAAA,OAAkD;gBAClE,YAAY;gBACZ,YAAY,UAAU;uBAAI,CAAA,GAAA,oBAAY,EAAE,SAAS,MAAM,UAAU;iBAAE,CAAC,MAAM,GAAG;gBAC7E,YAAY,WAAW,CAAC,aAAa,IAAI,YAAY,SAAS,IAAI;4BAClE;YACF;YAEA,CAAA,GAAA,eAAO,EAAE;QACX;QAEA,YAAY,OAAO,GAAG;QACtB,SAAS,OAAO,GAAG;IACrB;IAEA,+DAA+D;IAC/D,IAAI,cAAc,CAAA,GAAA,mBAAW,EAAE,MAAM,UAAU;IAC/C,IAAI,WAAW,CAAA,GAAA,aAAK,EAAE;IACtB,IAAI,WAAW,CAAA,GAAA,aAAK,EAAE,MAAM,MAAM;IAClC,CAAA,GAAA,gBAAQ,EAAE;QACR,mFAAmF;QACnF,4EAA4E;QAC5E,6EAA6E;QAC7E,IAAI,4BACF,MAAM,MAAM,KAAK,SAAS,OAAO,IAChC,CAAA,MAAM,gBAAgB,CAAC,UAAU,IAAI,QAAQ,CAAA,GAAA,oBAAY,GAAE;QAE9D,IAAI,MAAM,MAAM,IAAK,CAAA,6BAA6B,gBAAgB,SAAS,OAAO,AAAD,GAAI;YACnF,IAAI,eAAe,gBAAgB,MAAM,CAAC,qBAAqB;6BAAC;YAAW;YAC3E,CAAA,GAAA,eAAO,EAAE;QACX;QAEA,SAAS,OAAO,GAAG;QACnB,SAAS,OAAO,GAAG,MAAM,MAAM;IACjC;IAEA,wGAAwG;IACxG,gDAAgD;IAChD,IAAI,kBAAkB,CAAA,GAAA,aAAK,EAAE,MAAM,WAAW;IAC9C,CAAA,GAAA,gBAAQ,EAAE;QACR,IAAI,CAAA,GAAA,oBAAY,OAAO,MAAM,SAAS,IAAI,MAAM,YAAY,IAAI,MAAM,WAAW,KAAK,gBAAgB,OAAO,EAAE;YAC7G,IAAI,aAAa,MAAM,YAAY,CAAC,aAAa,IAAI,MAAM,YAAY,CAAC,SAAS,IAAI;YACrF,IAAI,eAAe,gBAAgB,MAAM,CAAC,wBAAwB;4BAAC;YAAU;YAC7E,CAAA,GAAA,eAAO,EAAE;QACX;QAEA,gBAAgB,OAAO,GAAG,MAAM,WAAW;IAC7C;IAEA,CAAA,GAAA,gBAAQ,EAAE;QACR,IAAI,MAAM,MAAM,EACd,OAAO,CAAA,GAAA,sBAAc,EAAE;YAAC,SAAS,OAAO;YAAE,WAAW,OAAO;SAAC,CAAC,MAAM,CAAC,CAAA,UAAW,WAAW;IAE/F,GAAG;QAAC,MAAM,MAAM;QAAE;QAAU;KAAW;IAEvC,CAAA,GAAA,sBAAc,EAAE;QACd,8DAA8D;QAC9D,IAAI,CAAC,eAAe,SAAS,OAAO,IAAI,CAAA,GAAA,uBAAe,EAAE,CAAA,GAAA,uBAAe,EAAE,SAAS,OAAO,OAAO,SAAS,OAAO,EAC/G,CAAA,GAAA,2BAAmB,EAAE,SAAS,OAAO,EAAE;IAE3C,GAAG;QAAC;KAAY;IAEhB,CAAA,GAAA,eAAO,EAAE,YAAY,0BAA0B,MAAM,MAAM,GAAG;QAC5D,MAAM,KAAK;IACb,IAAI;IAEJ,4GAA4G;IAC5G,sGAAsG;IACtG,CAAA,GAAA,yBAAiB,EAAE;QACjB,KAAK;QACL,mBAAmB,CAAC;YAClB,IAAI,SAAS,CAAA,GAAA,qBAAa,EAAE;YAC5B,IAAI,CAAA,GAAA,mBAAW,EAAE,sBAAA,gCAAA,UAAW,OAAO,EAAE,WAAW,CAAA,GAAA,mBAAW,EAAE,SAAS,OAAO,EAAE,SAC7E;YAEF,MAAM,KAAK;QACb;QACA,YAAY,CAAC,MAAM,MAAM;IAC3B;IAEA,OAAO;oBACL;QACA,aAAa;YACX,GAAG,gBAAgB;YACnB,GAAG,iBAAiB;YACpB,qBAAqB;YACrB,qBAAqB;qBACrB;0BACA;YACA,YAAY,cAAc;QAC5B;QACA,YAAY,CAAA,GAAA,iBAAS,EAAE,YAAY;YACjC,MAAM;YACN,iBAAiB,gBAAgB,CAAC,gBAAgB;YAClD,iBAAiB,MAAM,MAAM,GAAG,UAAU,EAAE,GAAG;YAC/C,mFAAmF;YACnF,qBAAqB;YACrB,yBAAyB,cAAc,CAAA,GAAA,gBAAQ,EAAE,OAAO,YAAY,GAAG,IAAI;wBAC3E;YACA,kGAAkG;YAClG,aAAa;YACb,gEAAgE;YAChE,YAAY;QACd;QACA,cAAc,CAAA,GAAA,iBAAS,EAAE,WAAW,cAAc;YAChD,WAAW,MAAM,aAAa,IAAI;YAClC,uBAAuB;YACvB,uBAAuB;YACvB,oBAAoB;YACpB,cAAc;YACd,CAAC,wBAAwB,EAAE;QAC7B;QACA,YAAY;YACV,IAAI;QACN;0BACA;2BACA;mBACA;0BACA;2BACA;IACF;AACF;AAEA,0FAA0F;AAC1F,oGAAoG;AACpG,sGAAsG;AACtG,mGAAmG;AACnG,iDAAiD;AACjD,SAAS,iCAAW,WAA+B,EAAE;IACnD,IAAI,KAAK,CAAA,GAAA,YAAI;IACb,IAAI,CAAC,QAAQ,UAAU,GAAG,CAAA,GAAA,eAAO,EAAE;IACnC,IAAI,CAAC,UAAU,YAAY,GAAG,CAAA,GAAA,eAAO,EAAE;IAEvC,qFAAqF;IACrF,IAAI,SAAS,IAAI,CAAC,CAAC,GAAG,IAAM,CAAC,OAAO,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,IAAI;QACvD,UAAU;QACV,YAAY;IACd;IAEA,CAAA,GAAA,gBAAQ,EAAE;QACR,IAAI,UAAU,CAAC,SAAS,cAAc,CAAC,KACrC,2DAA2D;QAC3D,UAAU;IAEd,GAAG;QAAC;QAAI;QAAQ;KAAS;IAEzB,OAAO,SAAS,KAAK;AACvB","sources":["packages/@react-aria/combobox/src/useComboBox.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {announce} from '@react-aria/live-announcer';\nimport {AriaButtonProps} from '@react-types/button';\nimport {AriaComboBoxProps, SelectionMode} from '@react-types/combobox';\nimport {ariaHideOutside} from '@react-aria/overlays';\nimport {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox';\nimport {BaseEvent, DOMAttributes, KeyboardDelegate, LayoutDelegate, PressEvent, RefObject, RouterOptions, ValidationResult} from '@react-types/shared';\nimport {chain, getActiveElement, getEventTarget, getOwnerDocument, isAppleDevice, mergeProps, nodeContains, useEvent, useFormReset, useId, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';\nimport {ComboBoxState} from '@react-stately/combobox';\nimport {dispatchVirtualFocus} from '@react-aria/focus';\nimport {FocusEvent, InputHTMLAttributes, KeyboardEvent, TouchEvent, useEffect, useMemo, useRef, useState} from 'react';\nimport {getChildNodes, getItemCount} from '@react-stately/collections';\n// @ts-ignore\nimport intlMessages from '../intl/*.json';\nimport {ListKeyboardDelegate, useSelectableCollection} from '@react-aria/selection';\nimport {privateValidationStateProp} from '@react-stately/form';\nimport {useInteractOutside} from '@react-aria/interactions';\nimport {useLocalizedStringFormatter} from '@react-aria/i18n';\nimport {useMenuTrigger} from '@react-aria/menu';\nimport {useTextField} from '@react-aria/textfield';\n\nexport interface AriaComboBoxOptions<T, M extends SelectionMode = 'single'> extends Omit<AriaComboBoxProps<T, M>, 'children'> {\n /** The ref for the input element. */\n inputRef: RefObject<HTMLInputElement | null>,\n /** The ref for the list box popover. */\n popoverRef: RefObject<Element | null>,\n /** The ref for the list box. */\n listBoxRef: RefObject<HTMLElement | null>,\n /** The ref for the optional list box popup trigger button. */\n buttonRef?: RefObject<Element | null>,\n /** An optional keyboard delegate implementation, to override the default. */\n keyboardDelegate?: KeyboardDelegate,\n /**\n * A delegate object that provides layout information for items in the collection.\n * By default this uses the DOM, but this can be overridden to implement things like\n * virtualized scrolling.\n */\n layoutDelegate?: LayoutDelegate\n}\n\nexport interface ComboBoxAria<T> extends ValidationResult {\n /** Props for the label element. */\n labelProps: DOMAttributes,\n /** Props for the combo box input element. */\n inputProps: InputHTMLAttributes<HTMLInputElement>,\n /** Props for the list box, to be passed to `useListBox`. */\n listBoxProps: AriaListBoxOptions<T>,\n /** Props for the optional trigger button, to be passed to `useButton`. */\n buttonProps: AriaButtonProps,\n /** Props for the element representing the selected value. */\n valueProps: DOMAttributes,\n /** Props for the combo box description element, if any. */\n descriptionProps: DOMAttributes,\n /** Props for the combo box error message element, if any. */\n errorMessageProps: DOMAttributes\n}\n\n/**\n * Provides the behavior and accessibility implementation for a combo box component.\n * A combo box combines a text input with a listbox, allowing users to filter a list of options to items matching a query.\n * @param props - Props for the combo box.\n * @param state - State for the select, as returned by `useComboBoxState`.\n */\nexport function useComboBox<T, M extends SelectionMode = 'single'>(props: AriaComboBoxOptions<T, M>, state: ComboBoxState<T, M>): ComboBoxAria<T> {\n let {\n buttonRef,\n popoverRef,\n inputRef,\n listBoxRef,\n keyboardDelegate,\n layoutDelegate,\n // completionMode = 'suggest',\n shouldFocusWrap,\n isReadOnly,\n isDisabled\n } = props;\n let backupBtnRef = useRef(null);\n buttonRef = buttonRef ?? backupBtnRef;\n\n let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/combobox');\n let {menuTriggerProps, menuProps} = useMenuTrigger<T>(\n {\n type: 'listbox',\n isDisabled: isDisabled || isReadOnly\n },\n state,\n buttonRef\n );\n\n // Set listbox id so it can be used when calling getItemId later\n listData.set(state, {id: menuProps.id});\n\n // By default, a KeyboardDelegate is provided which uses the DOM to query layout information (e.g. for page up/page down).\n // When virtualized, the layout object will be passed in as a prop and override this.\n let {collection} = state;\n let {disabledKeys} = state.selectionManager;\n let delegate = useMemo(() => (\n keyboardDelegate || new ListKeyboardDelegate({\n collection,\n disabledKeys,\n ref: listBoxRef,\n layoutDelegate\n })\n ), [keyboardDelegate, layoutDelegate, collection, disabledKeys, listBoxRef]);\n\n // Use useSelectableCollection to get the keyboard handlers to apply to the textfield\n let {collectionProps} = useSelectableCollection({\n selectionManager: state.selectionManager,\n keyboardDelegate: delegate,\n disallowTypeAhead: true,\n disallowEmptySelection: true,\n shouldFocusWrap,\n ref: inputRef,\n // Prevent item scroll behavior from being applied here, should be handled in the user's Popover + ListBox component\n isVirtualized: true\n });\n\n let router = useRouter();\n\n // For textfield specific keydown operations\n let onKeyDown = (e: BaseEvent<KeyboardEvent<any>>) => {\n if (e.nativeEvent.isComposing) {\n return;\n }\n switch (e.key) {\n case 'Enter':\n case 'Tab':\n // Prevent form submission if menu is open since we may be selecting a option\n if (state.isOpen && e.key === 'Enter') {\n e.preventDefault();\n }\n\n // If the focused item is a link, trigger opening it. Items that are links are not selectable.\n if (state.isOpen && listBoxRef.current && state.selectionManager.focusedKey != null) {\n let collectionItem = state.collection.getItem(state.selectionManager.focusedKey);\n if (collectionItem?.props.href) {\n let item = listBoxRef.current.querySelector(`[data-key=\"${CSS.escape(state.selectionManager.focusedKey.toString())}\"]`);\n if (e.key === 'Enter' && item instanceof HTMLAnchorElement) {\n router.open(item, e, collectionItem.props.href, collectionItem.props.routerOptions as RouterOptions);\n }\n state.close();\n break;\n } else if (collectionItem?.props.onAction) {\n collectionItem.props.onAction();\n state.close();\n break;\n }\n }\n state.commit();\n break;\n case 'Escape':\n if (\n !state.selectionManager.isEmpty ||\n state.inputValue === '' ||\n props.allowsCustomValue\n ) {\n e.continuePropagation();\n }\n state.revert();\n break;\n case 'ArrowDown':\n state.open('first', 'manual');\n break;\n case 'ArrowUp':\n state.open('last', 'manual');\n break;\n case 'ArrowLeft':\n case 'ArrowRight':\n state.selectionManager.setFocusedKey(null);\n break;\n }\n };\n\n let onBlur = (e: FocusEvent<HTMLInputElement>) => {\n let blurFromButton = buttonRef?.current && buttonRef.current === e.relatedTarget;\n let blurIntoPopover = nodeContains(popoverRef.current, e.relatedTarget);\n // Ignore blur if focused moved to the button(if exists) or into the popover.\n if (blurFromButton || blurIntoPopover) {\n return;\n }\n\n if (props.onBlur) {\n props.onBlur(e);\n }\n\n state.setFocused(false);\n };\n\n let onFocus = (e: FocusEvent<HTMLInputElement>) => {\n if (state.isFocused) {\n return;\n }\n\n if (props.onFocus) {\n props.onFocus(e);\n }\n\n state.setFocused(true);\n };\n\n let valueId = useValueId([state.selectedItems, state.selectionManager.selectionMode]);\n let {isInvalid, validationErrors, validationDetails} = state.displayValidation;\n let {labelProps, inputProps, descriptionProps, errorMessageProps} = useTextField({\n ...props,\n onChange: state.setInputValue,\n onKeyDown: !isReadOnly ? chain(state.isOpen && collectionProps.onKeyDown, onKeyDown, props.onKeyDown) : props.onKeyDown,\n onBlur,\n value: state.inputValue,\n defaultValue: state.defaultInputValue,\n onFocus,\n autoComplete: 'off',\n validate: undefined,\n [privateValidationStateProp]: state,\n 'aria-describedby': [valueId, props['aria-describedby']].filter(Boolean).join(' ') || undefined\n }, inputRef);\n\n useFormReset(inputRef, state.defaultValue, state.setValue);\n\n // Press handlers for the ComboBox button\n let onPress = (e: PressEvent) => {\n if (e.pointerType === 'touch') {\n // Focus the input field in case it isn't focused yet\n inputRef.current?.focus();\n state.toggle(null, 'manual');\n }\n };\n\n let onPressStart = (e: PressEvent) => {\n if (e.pointerType !== 'touch') {\n inputRef.current?.focus();\n state.toggle((e.pointerType === 'keyboard' || e.pointerType === 'virtual') ? 'first' : null, 'manual');\n }\n };\n\n let triggerLabelProps = useLabels({\n id: menuTriggerProps.id,\n 'aria-label': stringFormatter.format('buttonLabel'),\n 'aria-labelledby': props['aria-labelledby'] || labelProps.id\n });\n\n let listBoxProps = useLabels({\n id: menuProps.id,\n 'aria-label': stringFormatter.format('listboxLabel'),\n 'aria-labelledby': props['aria-labelledby'] || labelProps.id\n });\n\n // If a touch happens on direct center of ComboBox input, might be virtual click from iPad so open ComboBox menu\n let lastEventTime = useRef(0);\n let onTouchEnd = (e: TouchEvent) => {\n if (isDisabled || isReadOnly) {\n return;\n }\n\n // Sometimes VoiceOver on iOS fires two touchend events in quick succession. Ignore the second one.\n if (e.timeStamp - lastEventTime.current < 500) {\n e.preventDefault();\n inputRef.current?.focus();\n return;\n }\n\n let rect = (getEventTarget(e) as Element).getBoundingClientRect();\n let touch = e.changedTouches[0];\n\n let centerX = Math.ceil(rect.left + .5 * rect.width);\n let centerY = Math.ceil(rect.top + .5 * rect.height);\n\n if (touch.clientX === centerX && touch.clientY === centerY) {\n e.preventDefault();\n inputRef.current?.focus();\n state.toggle(null, 'manual');\n\n lastEventTime.current = e.timeStamp;\n }\n };\n\n // VoiceOver has issues with announcing aria-activedescendant properly on change\n // (especially on iOS). We use a live region announcer to announce focus changes\n // manually. In addition, section titles are announced when navigating into a new section.\n let focusedItem = state.selectionManager.focusedKey != null && state.isOpen\n ? state.collection.getItem(state.selectionManager.focusedKey)\n : undefined;\n let sectionKey = focusedItem?.parentKey ?? null;\n let itemKey = state.selectionManager.focusedKey ?? null;\n let lastSection = useRef(sectionKey);\n let lastItem = useRef(itemKey);\n useEffect(() => {\n if (isAppleDevice() && focusedItem != null && itemKey != null && itemKey !== lastItem.current) {\n let isSelected = state.selectionManager.isSelected(itemKey);\n let section = sectionKey != null ? state.collection.getItem(sectionKey) : null;\n let sectionTitle = section?.['aria-label'] || (typeof section?.rendered === 'string' ? section.rendered : '') || '';\n\n let announcement = stringFormatter.format('focusAnnouncement', {\n isGroupChange: (section && sectionKey !== lastSection.current) ?? false,\n groupTitle: sectionTitle,\n groupCount: section ? [...getChildNodes(section, state.collection)].length : 0,\n optionText: focusedItem['aria-label'] || focusedItem.textValue || '',\n isSelected\n });\n\n announce(announcement);\n }\n\n lastSection.current = sectionKey;\n lastItem.current = itemKey;\n });\n\n // Announce the number of available suggestions when it changes\n let optionCount = getItemCount(state.collection);\n let lastSize = useRef(optionCount);\n let lastOpen = useRef(state.isOpen);\n useEffect(() => {\n // Only announce the number of options available when the menu opens if there is no\n // focused item, otherwise screen readers will typically read e.g. \"1 of 6\".\n // The exception is VoiceOver since this isn't included in the message above.\n let didOpenWithoutFocusedItem =\n state.isOpen !== lastOpen.current &&\n (state.selectionManager.focusedKey == null || isAppleDevice());\n\n if (state.isOpen && (didOpenWithoutFocusedItem || optionCount !== lastSize.current)) {\n let announcement = stringFormatter.format('countAnnouncement', {optionCount});\n announce(announcement);\n }\n\n lastSize.current = optionCount;\n lastOpen.current = state.isOpen;\n });\n\n // Announce when a selection occurs for VoiceOver. Other screen readers typically do this automatically.\n // TODO: do we need to do this for multi-select?\n let lastSelectedKey = useRef(state.selectedKey);\n useEffect(() => {\n if (isAppleDevice() && state.isFocused && state.selectedItem && state.selectedKey !== lastSelectedKey.current) {\n let optionText = state.selectedItem['aria-label'] || state.selectedItem.textValue || '';\n let announcement = stringFormatter.format('selectedAnnouncement', {optionText});\n announce(announcement);\n }\n\n lastSelectedKey.current = state.selectedKey;\n });\n\n useEffect(() => {\n if (state.isOpen) {\n return ariaHideOutside([inputRef.current, popoverRef.current].filter(element => element != null));\n }\n }, [state.isOpen, inputRef, popoverRef]);\n\n useUpdateEffect(() => {\n // Re-show focus ring when there is no virtually focused item.\n if (!focusedItem && inputRef.current && getActiveElement(getOwnerDocument(inputRef.current)) === inputRef.current) {\n dispatchVirtualFocus(inputRef.current, null);\n }\n }, [focusedItem]);\n\n useEvent(listBoxRef, 'react-aria-item-action', state.isOpen ? () => {\n state.close();\n } : undefined);\n\n // usePopover -> useOverlay calls useInteractOutside, but ComboBox is non-modal, so `isDismissable` is false\n // Because of this, onInteractOutside is not passed to useInteractOutside, so we need to call it here.\n useInteractOutside({\n ref: popoverRef,\n onInteractOutside: (e) => {\n let target = getEventTarget(e) as Element;\n if (nodeContains(buttonRef?.current, target) || nodeContains(inputRef.current, target)) {\n return;\n }\n state.close();\n },\n isDisabled: !state.isOpen\n });\n\n return {\n labelProps,\n buttonProps: {\n ...menuTriggerProps,\n ...triggerLabelProps,\n excludeFromTabOrder: true,\n preventFocusOnPress: true,\n onPress,\n onPressStart,\n isDisabled: isDisabled || isReadOnly\n },\n inputProps: mergeProps(inputProps, {\n role: 'combobox',\n 'aria-expanded': menuTriggerProps['aria-expanded'],\n 'aria-controls': state.isOpen ? menuProps.id : undefined,\n // TODO: readd proper logic for completionMode = complete (aria-autocomplete: both)\n 'aria-autocomplete': 'list',\n 'aria-activedescendant': focusedItem ? getItemId(state, focusedItem.key) : undefined,\n onTouchEnd,\n // This disable's iOS's autocorrect suggestions, since the combo box provides its own suggestions.\n autoCorrect: 'off',\n // This disable's the macOS Safari spell check auto corrections.\n spellCheck: 'false'\n }),\n listBoxProps: mergeProps(menuProps, listBoxProps, {\n autoFocus: state.focusStrategy || true,\n shouldUseVirtualFocus: true,\n shouldSelectOnPressUp: true,\n shouldFocusOnHover: true,\n linkBehavior: 'selection' as const,\n ['UNSTABLE_itemBehavior']: 'action'\n }),\n valueProps: {\n id: valueId\n },\n descriptionProps,\n errorMessageProps,\n isInvalid,\n validationErrors,\n validationDetails\n };\n}\n\n// This is a modified version of useSlotId that uses useEffect instead of useLayoutEffect.\n// Triggering re-renders from useLayoutEffect breaks useComboBoxState's useEffect logic in React 18.\n// These re-renders preempt async state updates in the useEffect, which ends up running multiple times\n// prior to the state being updated. This results in onSelectionChange being called multiple times.\n// TODO: refactor useComboBoxState to avoid this.\nfunction useValueId(depArray: ReadonlyArray<any> = []): string | undefined {\n let id = useId();\n let [exists, setExists] = useState(true);\n let [lastDeps, setLastDeps] = useState(depArray);\n\n // If the deps changed, set exists to true so we can test whether the element exists.\n if (lastDeps.some((v, i) => !Object.is(v, depArray[i]))) {\n setExists(true);\n setLastDeps(depArray);\n }\n\n useEffect(() => {\n if (exists && !document.getElementById(id)) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setExists(false);\n }\n }, [id, exists, lastDeps]);\n\n return exists ? id : undefined;\n}\n"],"names":[],"version":3,"file":"useComboBox.module.js.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-aria/combobox",
3
- "version": "3.14.2",
3
+ "version": "3.15.0",
4
4
  "description": "Spectrum UI components in React",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
@@ -26,21 +26,22 @@
26
26
  "url": "https://github.com/adobe/react-spectrum"
27
27
  },
28
28
  "dependencies": {
29
- "@react-aria/focus": "^3.21.4",
30
- "@react-aria/i18n": "^3.12.15",
31
- "@react-aria/listbox": "^3.15.2",
29
+ "@react-aria/focus": "^3.21.5",
30
+ "@react-aria/i18n": "^3.12.16",
31
+ "@react-aria/interactions": "^3.27.1",
32
+ "@react-aria/listbox": "^3.15.3",
32
33
  "@react-aria/live-announcer": "^3.4.4",
33
- "@react-aria/menu": "^3.20.0",
34
- "@react-aria/overlays": "^3.31.1",
35
- "@react-aria/selection": "^3.27.1",
36
- "@react-aria/textfield": "^3.18.4",
37
- "@react-aria/utils": "^3.33.0",
38
- "@react-stately/collections": "^3.12.9",
39
- "@react-stately/combobox": "^3.12.2",
40
- "@react-stately/form": "^3.2.3",
41
- "@react-types/button": "^3.15.0",
42
- "@react-types/combobox": "^3.13.11",
43
- "@react-types/shared": "^3.33.0",
34
+ "@react-aria/menu": "^3.21.0",
35
+ "@react-aria/overlays": "^3.31.2",
36
+ "@react-aria/selection": "^3.27.2",
37
+ "@react-aria/textfield": "^3.18.5",
38
+ "@react-aria/utils": "^3.33.1",
39
+ "@react-stately/collections": "^3.12.10",
40
+ "@react-stately/combobox": "^3.13.0",
41
+ "@react-stately/form": "^3.2.4",
42
+ "@react-types/button": "^3.15.1",
43
+ "@react-types/combobox": "^3.14.0",
44
+ "@react-types/shared": "^3.33.1",
44
45
  "@swc/helpers": "^0.5.0"
45
46
  },
46
47
  "peerDependencies": {
@@ -50,5 +51,5 @@
50
51
  "publishConfig": {
51
52
  "access": "public"
52
53
  },
53
- "gitHead": "66e51757606b43a89ed02c574ca24517323a2ab9"
54
+ "gitHead": "8df187370053aa35f553cb388ad670f65e1ab371"
54
55
  }
@@ -12,24 +12,25 @@
12
12
 
13
13
  import {announce} from '@react-aria/live-announcer';
14
14
  import {AriaButtonProps} from '@react-types/button';
15
- import {AriaComboBoxProps} from '@react-types/combobox';
15
+ import {AriaComboBoxProps, SelectionMode} from '@react-types/combobox';
16
16
  import {ariaHideOutside} from '@react-aria/overlays';
17
17
  import {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox';
18
18
  import {BaseEvent, DOMAttributes, KeyboardDelegate, LayoutDelegate, PressEvent, RefObject, RouterOptions, ValidationResult} from '@react-types/shared';
19
- import {chain, getActiveElement, getOwnerDocument, isAppleDevice, mergeProps, nodeContains, useEvent, useFormReset, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';
19
+ import {chain, getActiveElement, getEventTarget, getOwnerDocument, isAppleDevice, mergeProps, nodeContains, useEvent, useFormReset, useId, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';
20
20
  import {ComboBoxState} from '@react-stately/combobox';
21
21
  import {dispatchVirtualFocus} from '@react-aria/focus';
22
- import {FocusEvent, InputHTMLAttributes, KeyboardEvent, TouchEvent, useEffect, useMemo, useRef} from 'react';
22
+ import {FocusEvent, InputHTMLAttributes, KeyboardEvent, TouchEvent, useEffect, useMemo, useRef, useState} from 'react';
23
23
  import {getChildNodes, getItemCount} from '@react-stately/collections';
24
24
  // @ts-ignore
25
25
  import intlMessages from '../intl/*.json';
26
26
  import {ListKeyboardDelegate, useSelectableCollection} from '@react-aria/selection';
27
27
  import {privateValidationStateProp} from '@react-stately/form';
28
+ import {useInteractOutside} from '@react-aria/interactions';
28
29
  import {useLocalizedStringFormatter} from '@react-aria/i18n';
29
30
  import {useMenuTrigger} from '@react-aria/menu';
30
31
  import {useTextField} from '@react-aria/textfield';
31
32
 
32
- export interface AriaComboBoxOptions<T> extends Omit<AriaComboBoxProps<T>, 'children'> {
33
+ export interface AriaComboBoxOptions<T, M extends SelectionMode = 'single'> extends Omit<AriaComboBoxProps<T, M>, 'children'> {
33
34
  /** The ref for the input element. */
34
35
  inputRef: RefObject<HTMLInputElement | null>,
35
36
  /** The ref for the list box popover. */
@@ -57,6 +58,8 @@ export interface ComboBoxAria<T> extends ValidationResult {
57
58
  listBoxProps: AriaListBoxOptions<T>,
58
59
  /** Props for the optional trigger button, to be passed to `useButton`. */
59
60
  buttonProps: AriaButtonProps,
61
+ /** Props for the element representing the selected value. */
62
+ valueProps: DOMAttributes,
60
63
  /** Props for the combo box description element, if any. */
61
64
  descriptionProps: DOMAttributes,
62
65
  /** Props for the combo box error message element, if any. */
@@ -69,7 +72,7 @@ export interface ComboBoxAria<T> extends ValidationResult {
69
72
  * @param props - Props for the combo box.
70
73
  * @param state - State for the select, as returned by `useComboBoxState`.
71
74
  */
72
- export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxState<T>): ComboBoxAria<T> {
75
+ export function useComboBox<T, M extends SelectionMode = 'single'>(props: AriaComboBoxOptions<T, M>, state: ComboBoxState<T, M>): ComboBoxAria<T> {
73
76
  let {
74
77
  buttonRef,
75
78
  popoverRef,
@@ -158,7 +161,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
158
161
  break;
159
162
  case 'Escape':
160
163
  if (
161
- state.selectedKey !== null ||
164
+ !state.selectionManager.isEmpty ||
162
165
  state.inputValue === '' ||
163
166
  props.allowsCustomValue
164
167
  ) {
@@ -206,6 +209,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
206
209
  state.setFocused(true);
207
210
  };
208
211
 
212
+ let valueId = useValueId([state.selectedItems, state.selectionManager.selectionMode]);
209
213
  let {isInvalid, validationErrors, validationDetails} = state.displayValidation;
210
214
  let {labelProps, inputProps, descriptionProps, errorMessageProps} = useTextField({
211
215
  ...props,
@@ -217,11 +221,12 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
217
221
  onFocus,
218
222
  autoComplete: 'off',
219
223
  validate: undefined,
220
- [privateValidationStateProp]: state
224
+ [privateValidationStateProp]: state,
225
+ 'aria-describedby': [valueId, props['aria-describedby']].filter(Boolean).join(' ') || undefined
221
226
  }, inputRef);
222
227
 
223
- useFormReset(inputRef, state.defaultSelectedKey, state.setSelectedKey);
224
-
228
+ useFormReset(inputRef, state.defaultValue, state.setValue);
229
+
225
230
  // Press handlers for the ComboBox button
226
231
  let onPress = (e: PressEvent) => {
227
232
  if (e.pointerType === 'touch') {
@@ -264,7 +269,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
264
269
  return;
265
270
  }
266
271
 
267
- let rect = (e.target as Element).getBoundingClientRect();
272
+ let rect = (getEventTarget(e) as Element).getBoundingClientRect();
268
273
  let touch = e.changedTouches[0];
269
274
 
270
275
  let centerX = Math.ceil(rect.left + .5 * rect.width);
@@ -332,6 +337,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
332
337
  });
333
338
 
334
339
  // Announce when a selection occurs for VoiceOver. Other screen readers typically do this automatically.
340
+ // TODO: do we need to do this for multi-select?
335
341
  let lastSelectedKey = useRef(state.selectedKey);
336
342
  useEffect(() => {
337
343
  if (isAppleDevice() && state.isFocused && state.selectedItem && state.selectedKey !== lastSelectedKey.current) {
@@ -360,6 +366,20 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
360
366
  state.close();
361
367
  } : undefined);
362
368
 
369
+ // usePopover -> useOverlay calls useInteractOutside, but ComboBox is non-modal, so `isDismissable` is false
370
+ // Because of this, onInteractOutside is not passed to useInteractOutside, so we need to call it here.
371
+ useInteractOutside({
372
+ ref: popoverRef,
373
+ onInteractOutside: (e) => {
374
+ let target = getEventTarget(e) as Element;
375
+ if (nodeContains(buttonRef?.current, target) || nodeContains(inputRef.current, target)) {
376
+ return;
377
+ }
378
+ state.close();
379
+ },
380
+ isDisabled: !state.isOpen
381
+ });
382
+
363
383
  return {
364
384
  labelProps,
365
385
  buttonProps: {
@@ -392,6 +412,9 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
392
412
  linkBehavior: 'selection' as const,
393
413
  ['UNSTABLE_itemBehavior']: 'action'
394
414
  }),
415
+ valueProps: {
416
+ id: valueId
417
+ },
395
418
  descriptionProps,
396
419
  errorMessageProps,
397
420
  isInvalid,
@@ -399,3 +422,29 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
399
422
  validationDetails
400
423
  };
401
424
  }
425
+
426
+ // This is a modified version of useSlotId that uses useEffect instead of useLayoutEffect.
427
+ // Triggering re-renders from useLayoutEffect breaks useComboBoxState's useEffect logic in React 18.
428
+ // These re-renders preempt async state updates in the useEffect, which ends up running multiple times
429
+ // prior to the state being updated. This results in onSelectionChange being called multiple times.
430
+ // TODO: refactor useComboBoxState to avoid this.
431
+ function useValueId(depArray: ReadonlyArray<any> = []): string | undefined {
432
+ let id = useId();
433
+ let [exists, setExists] = useState(true);
434
+ let [lastDeps, setLastDeps] = useState(depArray);
435
+
436
+ // If the deps changed, set exists to true so we can test whether the element exists.
437
+ if (lastDeps.some((v, i) => !Object.is(v, depArray[i]))) {
438
+ setExists(true);
439
+ setLastDeps(depArray);
440
+ }
441
+
442
+ useEffect(() => {
443
+ if (exists && !document.getElementById(id)) {
444
+ // eslint-disable-next-line react-hooks/set-state-in-effect
445
+ setExists(false);
446
+ }
447
+ }, [id, exists, lastDeps]);
448
+
449
+ return exists ? id : undefined;
450
+ }