@rescui/select 0.15.1-RUI-280-Update-select-component-e516591ed.54 → 0.15.1-RUI-280-Update-select-component-downshift-cd2a98813.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/lib/_virtual/index.css.js +12 -12
  2. package/lib/constants.d.ts +0 -1
  3. package/lib/constants.js +1 -2
  4. package/lib/hooks/use-a11y.d.ts +29 -0
  5. package/lib/hooks/use-a11y.js +53 -0
  6. package/lib/hooks/use-backward-compatible-id.d.ts +4 -0
  7. package/lib/hooks/use-backward-compatible-id.js +14 -0
  8. package/lib/hooks/use-label-focus.d.ts +1 -3
  9. package/lib/hooks/use-label-focus.js +3 -9
  10. package/lib/hooks/use-multi-handlers.d.ts +11 -0
  11. package/lib/hooks/use-multi-handlers.js +48 -0
  12. package/lib/hooks/use-options.d.ts +13 -0
  13. package/lib/hooks/use-options.js +89 -0
  14. package/lib/index.css +323 -371
  15. package/lib/multiselect.d.ts +3 -29
  16. package/lib/multiselect.js +249 -171
  17. package/lib/native-select.js +67 -44
  18. package/lib/parts/hidden-select.d.ts +13 -0
  19. package/lib/parts/hidden-select.js +45 -0
  20. package/lib/parts/icons.d.ts +3 -1
  21. package/lib/parts/icons.js +11 -14
  22. package/lib/parts/list-box.d.ts +7 -15
  23. package/lib/parts/list-box.js +50 -80
  24. package/lib/parts/multi-trigger.d.ts +5 -4
  25. package/lib/parts/multi-trigger.js +27 -15
  26. package/lib/parts/native-select-trigger.d.ts +6 -8
  27. package/lib/parts/native-select-trigger.js +14 -23
  28. package/lib/select-basic.d.ts +2 -2
  29. package/lib/select-basic.js +162 -95
  30. package/lib/select-searchable.d.ts +2 -2
  31. package/lib/select-searchable.js +209 -119
  32. package/lib/select.d.ts +1 -1
  33. package/lib/select.js +38 -31
  34. package/lib/styles/common.p.module.css.js +25 -24
  35. package/lib/styles/form-trigger.p.module.css.js +12 -12
  36. package/lib/styles/list-box.p.module.css.js +11 -13
  37. package/lib/styles/native-select-trigger.p.module.css.js +15 -14
  38. package/lib/styles/native-select.p.module.css.js +6 -6
  39. package/lib/types.d.ts +26 -26
  40. package/lib/utils/get-real-items-cnt.d.ts +2 -2
  41. package/lib/utils/get-real-items-cnt.js +8 -10
  42. package/lib/utils/item-to-string.d.ts +2 -0
  43. package/lib/utils/item-to-string.js +7 -0
  44. package/lib/utils/merge-props.d.ts +29 -0
  45. package/lib/utils/merge-props.js +106 -0
  46. package/package.json +12 -8
  47. package/lib/hooks/use-css-context-transfer.d.ts +0 -14
  48. package/lib/hooks/use-css-context-transfer.js +0 -40
  49. package/lib/hooks/use-multi-option-serialization.d.ts +0 -10
  50. package/lib/hooks/use-multi-option-serialization.js +0 -82
  51. package/lib/hooks/use-observable-force-rerender.d.ts +0 -2
  52. package/lib/hooks/use-observable-force-rerender.js +0 -16
  53. package/lib/hooks/use-option-serialization.d.ts +0 -10
  54. package/lib/hooks/use-option-serialization.js +0 -59
  55. package/lib/parts/popover.d.ts +0 -12
  56. package/lib/parts/popover.js +0 -62
  57. package/lib/parts/popup-content.d.ts +0 -19
  58. package/lib/parts/popup-content.js +0 -76
  59. package/lib/styles/popover.p.module.css.js +0 -10
  60. package/lib/utils/convert-options.d.ts +0 -3
  61. package/lib/utils/convert-options.js +0 -52
  62. package/lib/utils/convert-placement.d.ts +0 -3
  63. package/lib/utils/convert-placement.js +0 -22
  64. package/lib/utils/css-vars-utils.d.ts +0 -3
  65. package/lib/utils/css-vars-utils.js +0 -20
@@ -1,78 +1,122 @@
1
- import React, { useRef } from 'react';
1
+ import React from 'react';
2
2
  import cn from 'classnames';
3
- import { useSelectState } from 'react-stately';
4
- import { useSelect, useButton, useFocusRing, mergeProps, HiddenSelect } from 'react-aria';
5
- import { PopupContent } from './parts/popup-content.js';
3
+ import { Dropdown } from '@rescui/dropdown';
4
+ import { useSelect } from 'downshift';
5
+ import { useFocusRing } from '@react-aria/focus';
6
+ import scrollIntoView from 'scroll-into-view-if-needed';
7
+ import { useLabelFocus } from './hooks/use-label-focus.js';
8
+ import { useOptions } from './hooks/use-options.js';
9
+ import { useA11y } from './hooks/use-a11y.js';
10
+ import { ListBox } from './parts/list-box.js';
6
11
  import { Icons } from './parts/icons.js';
12
+ import { HiddenSelect } from './parts/hidden-select.js';
7
13
  import { FormTriggerWrapper } from './parts/form-trigger-wrapper.js';
8
- import { useLabelFocus } from './hooks/use-label-focus.js';
9
14
  import { getEmptyMessage } from './utils/get-empty-message.js';
10
- import styles from './styles/common.p.module.css.js';
11
- const THEME_STYLES = {
12
- light: styles.themeLight,
13
- dark: styles.themeDark
14
- };
15
- const SIZE_STYLES = {
16
- s: styles.sizeS,
17
- m: styles.sizeM,
18
- l: styles.sizeL
19
- };
20
- const MODE_STYLES = {
21
- classic: styles.classic,
22
- rock: styles.rock
23
- }; // eslint-disable-next-line complexity
15
+ import { mergeProps } from './utils/merge-props.js';
16
+ import { itemToString } from './utils/item-to-string.js';
17
+ import { getOptionValue } from './utils/get-option-value.js';
18
+ import { getRealItemsCnt } from './utils/get-real-items-cnt.js';
19
+ import styles from './styles/common.p.module.css.js'; // eslint-disable-next-line complexity
24
20
 
25
21
  function SelectBasic(_ref) {
26
- var _state$selectedItems$;
27
-
28
22
  let {
29
- id,
23
+ options,
24
+ value: selectedItem,
25
+ onChange,
26
+ error,
27
+ label,
28
+ note,
30
29
  size,
31
30
  mode,
32
31
  theme,
33
- style,
34
- className,
35
- isClearable,
36
- isLoading,
37
32
  placement,
38
33
  boldLabel,
39
34
  reserveErrorLine,
40
- noOptionsMessage,
41
35
  maxVisibleItems,
42
- ...ariaProps
36
+ isClearable,
37
+ isLoading,
38
+ isDisabled,
39
+ name,
40
+ noOptionsMessage,
41
+ className,
42
+ id,
43
+ style,
44
+ placeholder,
45
+ ...rest
43
46
  } = _ref;
44
- // Create state based on the incoming props
45
- const state = useSelectState({ ...ariaProps,
46
- allowsEmptyCollection: true
47
- });
48
- const hasValue = state.selectedItems.length > 0; // Get props for child elements from useSelect
49
-
50
- const triggerRef = useRef(null);
47
+ const triggerRef = React.useRef(null);
48
+ const dropdownRef = React.useRef(null);
49
+ const menuRef = React.useRef(null);
50
+ const {
51
+ flattenedOptions,
52
+ listBoxItems
53
+ } = useOptions(options);
54
+ const isListBoxEmpty = getRealItemsCnt(listBoxItems) === 0;
55
+ const hasValue = Boolean(selectedItem);
56
+ const selectedMap = selectedItem ? new Map([[getOptionValue(selectedItem), selectedItem]]) : new Map();
51
57
  const {
52
- labelProps,
53
- triggerProps,
54
- valueProps,
55
- menuProps: listBoxProps,
56
- hiddenSelectProps,
58
+ fieldProps,
57
59
  errorMessageProps,
58
- descriptionProps
59
- } = useSelect({ ...ariaProps,
60
- onKeyDown: e => {
61
- if (isClearable && hasValue) {
62
- if (e.key === 'Backspace' || e.key === 'Delete') {
63
- state.setValue(null);
64
- e.stopPropagation();
65
- }
66
- }
67
- }
68
- }, state, triggerRef);
60
+ descriptionProps,
61
+ baseId
62
+ } = useA11y({
63
+ prefix: 'select',
64
+ error,
65
+ description: note
66
+ });
69
67
  const {
70
- buttonProps
71
- } = useButton({ ...triggerProps,
72
- elementType: 'div'
73
- }, triggerRef);
74
- const isDisabled = ariaProps.isDisabled;
75
- const isOpen = state.isOpen;
68
+ isOpen,
69
+ getToggleButtonProps,
70
+ getLabelProps,
71
+ getMenuProps,
72
+ highlightedIndex,
73
+ getItemProps,
74
+ reset
75
+ } = useSelect({
76
+ items: flattenedOptions,
77
+ itemToString,
78
+ id: baseId,
79
+ selectedItem,
80
+ // defining our scroll behavior because on the first menu render the hook
81
+ // doesn't know menuRef and use page as the scroll boundary
82
+ scrollIntoView: (listItemEl, listEl) => {
83
+ scrollIntoView(listItemEl, {
84
+ scrollMode: 'if-needed',
85
+ block: 'nearest',
86
+ boundary: listEl ?? menuRef.current
87
+ });
88
+ },
89
+ onSelectedItemChange: _ref2 => {
90
+ let {
91
+ selectedItem: newSelectedItem
92
+ } = _ref2;
93
+ onChange === null || onChange === void 0 || onChange(newSelectedItem);
94
+ },
95
+
96
+ isItemDisabled(item) {
97
+ return item && item.disabled;
98
+ }
99
+
100
+ });
101
+ const menuProps = getMenuProps({
102
+ ref: menuRef
103
+ }, {
104
+ suppressRefError: true
105
+ });
106
+ const triggerProps = getToggleButtonProps({
107
+ ref: triggerRef,
108
+ onKeyDown: event => {
109
+ // backspace and backspace removing the last selected item (when popup is closed)
110
+ if (isClearable && !isOpen && (event.key === 'Backspace' || event.key === 'Delete')) {
111
+ reset();
112
+ }
113
+ },
114
+ // fix screen-reader focus on an opened popup with an empty list
115
+ ...(isOpen && isListBoxEmpty ? {
116
+ 'aria-activedescendant': menuProps.id
117
+ } : {})
118
+ }); // use it instead of ":has(:focus-visible), :focus-visible"
119
+
76
120
  const {
77
121
  isFocusVisible,
78
122
  focusProps
@@ -83,60 +127,83 @@ function SelectBasic(_ref) {
83
127
  isFocusedByLabel,
84
128
  labelClickProps,
85
129
  triggerFocusProps: labelFocusForTriggerProps
86
- } = useLabelFocus(triggerRef);
130
+ } = useLabelFocus();
87
131
  const emptyListMessage = getEmptyMessage({
88
132
  isLoading,
89
133
  isNothingFound: false,
90
134
  noOptionsMessage
91
135
  });
92
- return /*#__PURE__*/React.createElement("div", {
93
- className: cn(className, styles.root, THEME_STYLES[theme], SIZE_STYLES[size], MODE_STYLES[mode], isDisabled ? styles.disabled : styles.enabled, Boolean(ariaProps.errorMessage) && styles.error),
94
- id: id,
95
- style: style,
96
- "data-test": "select"
97
- }, /*#__PURE__*/React.createElement(FormTriggerWrapper, {
136
+ const listBoxState = {
137
+ selectedMap,
138
+ selectionMode: 'single',
139
+ highlightedIndex,
140
+ getItemProps,
141
+ menuProps
142
+ };
143
+ return /*#__PURE__*/React.createElement(FormTriggerWrapper, {
98
144
  theme: theme,
99
145
  size: size,
100
146
  disabled: isDisabled,
101
- label: ariaProps.label,
102
- labelProps: mergeProps(labelClickProps, labelProps),
147
+ label: label,
148
+ labelProps: mergeProps(getLabelProps(), labelClickProps),
103
149
  boldLabel: boldLabel,
104
- error: ariaProps.errorMessage,
150
+ error: error,
105
151
  reserveErrorLine: reserveErrorLine,
106
152
  errorMessageProps: errorMessageProps,
107
- note: ariaProps.description,
153
+ note: note,
108
154
  descriptionProps: descriptionProps
109
- }, /*#__PURE__*/React.createElement(HiddenSelect, { ...hiddenSelectProps
110
- }), /*#__PURE__*/React.createElement("div", { ...mergeProps(buttonProps, focusProps, labelFocusForTriggerProps),
111
- ref: triggerRef,
112
- className: styles.trigger,
113
- "data-open": isOpen,
114
- "data-focus-visible": isFocusVisible,
115
- "data-label-focused": isFocusedByLabel,
116
- "data-test": "select__trigger"
117
- }, /*#__PURE__*/React.createElement("span", { ...valueProps,
118
- className: cn(styles.triggerText, !hasValue && styles.labelNotSelected),
119
- "data-test": "select__trigger_content"
120
- }, ((_state$selectedItems$ = state.selectedItems[0]) === null || _state$selectedItems$ === void 0 ? void 0 : _state$selectedItems$.rendered) ?? ariaProps.placeholder), /*#__PURE__*/React.createElement(Icons, {
121
- isLoading: isLoading,
122
- canHaveClearIcon: isClearable && !isDisabled && hasValue,
123
- onClear: () => {
124
- var _triggerRef$current;
125
-
126
- state.setValue(null);
127
- (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 || _triggerRef$current.focus();
128
- },
129
- triggerId: triggerProps.id
130
- })), /*#__PURE__*/React.createElement(PopupContent, {
155
+ }, /*#__PURE__*/React.createElement(HiddenSelect, {
156
+ name: name,
157
+ value: selectedItem,
158
+ isMultiple: false,
159
+ options: flattenedOptions
160
+ }), /*#__PURE__*/React.createElement(Dropdown, {
161
+ mode: mode,
131
162
  theme: theme,
132
- state: state,
133
- triggerRef: triggerRef,
163
+ ref: dropdownRef,
134
164
  placement: placement,
165
+ maxWidth: "trigger",
166
+ minWidth: "trigger",
167
+ isOpen: isOpen,
168
+ cancelCloseAnimation: true,
169
+ disableFocusLock: true,
170
+ trigger: _ref3 => {
171
+ let {
172
+ ref: triggerRefForDropdown
173
+ } = _ref3;
174
+ return /*#__PURE__*/React.createElement("div", { ...rest,
175
+ ...mergeProps(fieldProps, // aria-invalid & aria-describedby, rest are overridden by triggerProps
176
+ triggerProps, focusProps, labelFocusForTriggerProps, {
177
+ ref: triggerRefForDropdown
178
+ }),
179
+ className: styles.trigger,
180
+ "data-open": isOpen,
181
+ "data-has-focus-visible": isFocusVisible,
182
+ "data-label-focused": isFocusedByLabel,
183
+ "data-test": "select__trigger"
184
+ }, /*#__PURE__*/React.createElement("span", {
185
+ className: cn(styles.triggerText, !hasValue && styles.labelNotSelected),
186
+ "data-test": "select__trigger_content"
187
+ }, (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.label) ?? placeholder), /*#__PURE__*/React.createElement(Icons, {
188
+ isLoading: isLoading,
189
+ canHaveClearIcon: isClearable && !isDisabled && hasValue,
190
+ onClear: () => {
191
+ var _triggerRef$current;
192
+
193
+ reset();
194
+ (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 || _triggerRef$current.focus({
195
+ focusVisible: false
196
+ });
197
+ },
198
+ triggerId: triggerProps.id
199
+ }));
200
+ }
201
+ }, /*#__PURE__*/React.createElement(ListBox, {
202
+ state: listBoxState,
135
203
  size: size,
136
- mode: mode,
137
- listBoxProps: listBoxProps,
204
+ maxVisibleItems: maxVisibleItems,
138
205
  emptyListMessage: emptyListMessage,
139
- maxVisibleItems: maxVisibleItems
206
+ items: listBoxItems
140
207
  })));
141
208
  }
142
209
 
@@ -1,3 +1,3 @@
1
1
  /// <reference types="react" />
2
- import type { InnerSingleComponentProps } from './types';
3
- export declare function SelectSearchable<T extends object>({ id, size, mode, theme, className, style, isClearable, isLoading, placement, boldLabel, reserveErrorLine, noOptionsMessage, maxVisibleItems, ...ariaProps }: InnerSingleComponentProps<T>): JSX.Element;
2
+ import { InnerSingleComponentProps } from './types';
3
+ export declare function SelectSearchable({ options, value: selectedItem, onChange, error, label, note, size, mode, theme, placement, boldLabel, reserveErrorLine, maxVisibleItems, isClearable, isLoading, isDisabled, name, noOptionsMessage, className, id, style, placeholder, ...rest }: InnerSingleComponentProps): JSX.Element;
@@ -1,94 +1,163 @@
1
- import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
2
- import React, { useRef } from 'react';
3
- import cn from 'classnames';
4
- import { useComboBoxState } from 'react-stately';
5
- import { useComboBox, useButton, useFocusRing, mergeProps, HiddenSelect } from 'react-aria';
6
- import { PopupContent } from './parts/popup-content.js';
1
+ import React, { useRef, useState } from 'react';
2
+ import { Dropdown } from '@rescui/dropdown';
3
+ import { useCombobox } from 'downshift';
4
+ import { useFocusRing } from '@react-aria/focus';
5
+ import scrollIntoView from 'scroll-into-view-if-needed';
6
+ import { useLabelFocus } from './hooks/use-label-focus.js';
7
+ import { useOptions } from './hooks/use-options.js';
8
+ import { useA11y } from './hooks/use-a11y.js';
9
+ import { ListBox } from './parts/list-box.js';
7
10
  import { Icons } from './parts/icons.js';
11
+ import { HiddenSelect } from './parts/hidden-select.js';
8
12
  import { FormTriggerWrapper } from './parts/form-trigger-wrapper.js';
9
- import { useLabelFocus } from './hooks/use-label-focus.js';
10
13
  import { getEmptyMessage } from './utils/get-empty-message.js';
11
- import styles from './styles/common.p.module.css.js';
12
- const THEME_STYLES = {
13
- light: styles.themeLight,
14
- dark: styles.themeDark
15
- };
16
- const SIZE_STYLES = {
17
- s: styles.sizeS,
18
- m: styles.sizeM,
19
- l: styles.sizeL
20
- };
21
- const MODE_STYLES = {
22
- classic: styles.classic,
23
- rock: styles.rock
24
- }; // Do not use useFilter from 'react-aria' package as it's much slower for most cases
25
-
26
- const contains = (string, substring) => {
27
- var _context;
28
-
29
- if (substring.length === 0) {
30
- return true;
31
- }
32
-
33
- return _includesInstanceProperty(_context = string.toLowerCase()).call(_context, substring.toLowerCase());
34
- }; // eslint-disable-next-line complexity
35
-
14
+ import { mergeProps } from './utils/merge-props.js';
15
+ import { itemToString } from './utils/item-to-string.js';
16
+ import { getOptionValue } from './utils/get-option-value.js';
17
+ import { getRealItemsCnt } from './utils/get-real-items-cnt.js';
18
+ import styles from './styles/common.p.module.css.js'; // eslint-disable-next-line complexity
36
19
 
37
20
  function SelectSearchable(_ref) {
38
- var _state$inputValue;
39
-
40
21
  let {
41
- id,
22
+ options,
23
+ value: selectedItem,
24
+ onChange,
25
+ error,
26
+ label,
27
+ note,
42
28
  size,
43
29
  mode,
44
30
  theme,
45
- className,
46
- style,
47
- isClearable,
48
- isLoading,
49
31
  placement,
50
32
  boldLabel,
51
33
  reserveErrorLine,
52
- noOptionsMessage,
53
34
  maxVisibleItems,
54
- ...ariaProps
35
+ isClearable,
36
+ isLoading,
37
+ isDisabled,
38
+ name,
39
+ noOptionsMessage,
40
+ className,
41
+ id,
42
+ style,
43
+ placeholder,
44
+ ...rest
55
45
  } = _ref;
56
- // Create state based on the incoming props
57
- const state = useComboBoxState({ ...ariaProps,
58
- defaultFilter: contains,
59
- allowsEmptyCollection: true
60
- });
61
- const hasValue = state.selectedItems.length > 0; // Setup refs and get props for child elements.
62
-
63
46
  const triggerRef = useRef(null);
64
- const inputRef = useRef(null);
65
- const listBoxRef = useRef(null);
66
- const popoverRef = useRef(null);
67
- const {
68
- labelProps,
69
- buttonProps: triggerProps,
70
- listBoxProps,
71
- inputProps: inputPropsWithName,
72
- errorMessageProps,
73
- descriptionProps
74
- } = useComboBox({ ...ariaProps,
75
- inputRef,
76
- buttonRef: triggerRef,
77
- listBoxRef,
78
- popoverRef
79
- }, state); // Extract name from inputProps to use with HiddenSelect instead
47
+ const dropdownRef = useRef(null);
48
+ const menuRef = React.useRef(null); // use it instead of :has(.searchInput:focus)
80
49
 
50
+ const [inputFocused, setInputFocused] = useState(false);
81
51
  const {
82
- name,
83
- ...inputProps
84
- } = inputPropsWithName;
52
+ flattenedOptions,
53
+ listBoxItems,
54
+ setFilterString
55
+ } = useOptions(options);
56
+ const isListBoxEmpty = getRealItemsCnt(listBoxItems) === 0;
57
+ const hasValue = Boolean(selectedItem);
58
+ const selectedMap = selectedItem ? new Map([[getOptionValue(selectedItem), selectedItem]]) : new Map();
85
59
  const {
86
- buttonProps
87
- } = useButton({ ...triggerProps,
88
- elementType: 'div'
89
- }, triggerRef);
90
- const isDisabled = ariaProps.isDisabled;
91
- const isOpen = state.isOpen;
60
+ fieldProps,
61
+ errorMessageProps,
62
+ descriptionProps,
63
+ baseId
64
+ } = useA11y({
65
+ prefix: 'select',
66
+ error,
67
+ description: note
68
+ });
69
+ const {
70
+ isOpen,
71
+ getToggleButtonProps,
72
+ getLabelProps,
73
+ getMenuProps,
74
+ getInputProps,
75
+ highlightedIndex,
76
+ getItemProps,
77
+ reset,
78
+ setInputValue
79
+ } = useCombobox({
80
+ onInputValueChange(_ref2) {
81
+ let {
82
+ inputValue,
83
+ type
84
+ } = _ref2;
85
+
86
+ if (type === useCombobox.stateChangeTypes.InputChange) {
87
+ setFilterString(inputValue);
88
+ }
89
+ },
90
+
91
+ items: flattenedOptions,
92
+ id: baseId,
93
+ itemToString,
94
+ selectedItem,
95
+ // defining our scroll behavior because on the first menu render the hook
96
+ // doesn't know menuRef and use page as the scroll boundary
97
+ scrollIntoView: (listItemEl, listEl) => {
98
+ scrollIntoView(listItemEl, {
99
+ scrollMode: 'if-needed',
100
+ block: 'nearest',
101
+ boundary: listEl ?? menuRef.current
102
+ });
103
+ },
104
+ onSelectedItemChange: _ref3 => {
105
+ let {
106
+ selectedItem: newSelectedItem
107
+ } = _ref3;
108
+ onChange === null || onChange === void 0 || onChange(newSelectedItem);
109
+ },
110
+
111
+ isItemDisabled(item) {
112
+ return item && item.disabled;
113
+ },
114
+
115
+ onIsOpenChange(_ref4) {
116
+ let {
117
+ isOpen: newIsOpen,
118
+ selectedItem: newSelectedItem
119
+ } = _ref4;
120
+
121
+ if (newIsOpen) {
122
+ setFilterString('');
123
+ } else {
124
+ setInputValue(itemToString(newSelectedItem));
125
+ }
126
+ },
127
+
128
+ stateReducer(state, actionAndChanges) {
129
+ const {
130
+ changes,
131
+ type
132
+ } = actionAndChanges;
133
+
134
+ switch (type) {
135
+ case useCombobox.stateChangeTypes.InputKeyDownEscape:
136
+ if (!state.isOpen) {
137
+ return state;
138
+ }
139
+
140
+ return changes;
141
+
142
+ default:
143
+ return changes;
144
+ }
145
+ }
146
+
147
+ });
148
+ const menuProps = getMenuProps({
149
+ ref: menuRef
150
+ }, {
151
+ suppressRefError: true
152
+ });
153
+ const triggerProps = getToggleButtonProps({
154
+ ref: triggerRef,
155
+ // fix screen-reader focus on an opened popup with an empty list
156
+ ...(isOpen && isListBoxEmpty ? {
157
+ 'aria-activedescendant': menuProps.id
158
+ } : {})
159
+ }); // use it instead of ":has(:focus-visible), :focus-visible"
160
+
92
161
  const {
93
162
  isFocusVisible,
94
163
  focusProps
@@ -99,69 +168,90 @@ function SelectSearchable(_ref) {
99
168
  isFocusedByLabel,
100
169
  labelClickProps,
101
170
  triggerFocusProps: labelFocusForTriggerProps
102
- } = useLabelFocus(triggerRef);
171
+ } = useLabelFocus();
103
172
  const emptyListMessage = getEmptyMessage({
104
173
  isLoading,
105
- isNothingFound: ((_state$inputValue = state.inputValue) === null || _state$inputValue === void 0 ? void 0 : _state$inputValue.length) > 0,
174
+ isNothingFound: false,
106
175
  noOptionsMessage
107
176
  });
108
- return /*#__PURE__*/React.createElement("div", {
109
- className: cn(className, styles.root, THEME_STYLES[theme], SIZE_STYLES[size], MODE_STYLES[mode], isDisabled ? styles.disabled : styles.enabled, Boolean(ariaProps.errorMessage) && styles.error),
110
- id: id,
111
- style: style,
112
- "data-test": "select"
113
- }, /*#__PURE__*/React.createElement(FormTriggerWrapper, {
177
+ const listBoxState = {
178
+ selectedMap,
179
+ selectionMode: 'single',
180
+ highlightedIndex,
181
+ getItemProps,
182
+ menuProps
183
+ };
184
+ return /*#__PURE__*/React.createElement(FormTriggerWrapper, {
114
185
  theme: theme,
115
186
  size: size,
116
187
  disabled: isDisabled,
117
- label: ariaProps.label,
118
- labelProps: mergeProps(labelClickProps, labelProps),
188
+ label: label,
189
+ labelProps: mergeProps(getLabelProps(), labelClickProps),
119
190
  boldLabel: boldLabel,
120
- error: ariaProps.errorMessage,
191
+ error: error,
121
192
  reserveErrorLine: reserveErrorLine,
122
193
  errorMessageProps: errorMessageProps,
123
- note: ariaProps.description,
194
+ note: note,
124
195
  descriptionProps: descriptionProps
125
196
  }, /*#__PURE__*/React.createElement(HiddenSelect, {
126
- state: state,
127
- triggerRef: triggerRef,
128
- label: ariaProps.label,
129
197
  name: name,
130
- isDisabled: isDisabled
131
- }), /*#__PURE__*/React.createElement("div", { ...mergeProps(buttonProps, focusProps, labelFocusForTriggerProps),
132
- ref: triggerRef,
133
- className: styles.trigger,
134
- "data-open": isOpen,
135
- "data-focus-visible": isFocusVisible,
136
- "data-label-focused": isFocusedByLabel,
137
- "data-test": "select__trigger"
138
- }, /*#__PURE__*/React.createElement("input", { ...inputProps,
139
- ref: inputRef,
140
- className: styles.searchInput,
141
- value: inputProps.value,
142
- "data-test": "select__trigger_input"
143
- }), /*#__PURE__*/React.createElement(Icons, {
144
- isLoading: isLoading,
145
- canHaveClearIcon: isClearable && !isDisabled && hasValue,
146
- onClear: () => {
147
- var _inputRef$current;
148
-
149
- state.setValue(null);
150
- (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.focus();
151
- },
152
- triggerId: triggerProps.id
153
- })), /*#__PURE__*/React.createElement(PopupContent, {
198
+ value: selectedItem,
199
+ isMultiple: false,
200
+ options: flattenedOptions
201
+ }), /*#__PURE__*/React.createElement(Dropdown, {
202
+ mode: mode,
154
203
  theme: theme,
155
- state: state,
156
- triggerRef: triggerRef,
157
- popoverRef: popoverRef,
158
- listBoxRef: listBoxRef,
204
+ ref: dropdownRef,
159
205
  placement: placement,
206
+ maxWidth: "trigger",
207
+ minWidth: "trigger",
208
+ isOpen: isOpen,
209
+ cancelCloseAnimation: true,
210
+ disableFocusLock: true,
211
+ trigger: _ref5 => {
212
+ let {
213
+ ref: triggerRefForDropdown
214
+ } = _ref5;
215
+ return /*#__PURE__*/React.createElement("div", { ...rest,
216
+ ...mergeProps(fieldProps, // aria-invalid & aria-describedby, rest are overridden by triggerProps
217
+ triggerProps, focusProps, labelFocusForTriggerProps, {
218
+ ref: triggerRefForDropdown
219
+ }),
220
+ className: styles.trigger,
221
+ "data-open": isOpen,
222
+ "data-has-focus-visible": isFocusVisible || inputFocused,
223
+ "data-label-focused": isFocusedByLabel,
224
+ "data-test": "select__trigger"
225
+ }, /*#__PURE__*/React.createElement("input", { ...getInputProps({
226
+ onFocus: () => setInputFocused(true),
227
+ onBlur: () => setInputFocused(false)
228
+ }),
229
+ // by the downshift spec, it's expected to place input near the trigger and not nest them,
230
+ // but we try to keep basic select and searchable similar, so that's a little hack to nest these elements
231
+ onClick: () => {},
232
+ className: styles.searchInput,
233
+ placeholder: placeholder,
234
+ "data-test": "select__trigger_input"
235
+ }), /*#__PURE__*/React.createElement(Icons, {
236
+ isLoading: isLoading,
237
+ canHaveClearIcon: isClearable && !isDisabled && hasValue,
238
+ onClear: () => {
239
+ var _triggerRef$current;
240
+
241
+ reset();
242
+ (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 || _triggerRef$current.focus({
243
+ focusVisible: false
244
+ });
245
+ },
246
+ triggerId: triggerProps.id
247
+ }));
248
+ }
249
+ }, /*#__PURE__*/React.createElement(ListBox, {
250
+ state: listBoxState,
160
251
  size: size,
161
- mode: mode,
162
- listBoxProps: listBoxProps,
252
+ maxVisibleItems: maxVisibleItems,
163
253
  emptyListMessage: emptyListMessage,
164
- maxVisibleItems: maxVisibleItems
254
+ items: listBoxItems
165
255
  })));
166
256
  }
167
257