@rescui/select 0.15.1-RUI-280-Update-select-component-separate-components-e772abaa1.56 → 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 -174
  17. package/lib/native-select.js +67 -46
  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 -82
  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 +161 -97
  30. package/lib/select-searchable.d.ts +2 -2
  31. package/lib/select-searchable.js +208 -122
  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 -27
  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 +13 -22
  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 -64
  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,8 +1,7 @@
1
1
  import cn from 'classnames';
2
2
  import React from 'react';
3
- import LoadingOutlineIcon from '../_virtual/loading-outline.js';
4
- import DownOutlineIcon from '../_virtual/down-outline.js';
5
3
  import styles from '../styles/native-select-trigger.p.module.css.js';
4
+ import { Icons } from './icons.js';
6
5
  const SIZE_STYLES = {
7
6
  s: styles.sizeS,
8
7
  m: styles.sizeM,
@@ -15,40 +14,32 @@ const THEME_STYLES = {
15
14
 
16
15
  const NativeSelectTrigger = _ref => {
17
16
  let {
18
- isFocused,
19
- isHovered,
20
17
  isLoading,
21
18
  isDisabled,
19
+ isClearable,
22
20
  hasError,
23
21
  size,
24
22
  theme,
25
23
  placeholder,
26
- value
24
+ value,
25
+ onClear,
26
+ triggerId
27
27
  } = _ref;
28
28
  return /*#__PURE__*/React.createElement("div", {
29
- "aria-hidden": "true",
30
29
  className: cn(styles.trigger, SIZE_STYLES[size], THEME_STYLES[theme], isDisabled ? styles.disabled : styles.enabled, hasError && styles.error),
31
- "data-focused": isFocused,
32
- "data-hovered": isHovered,
33
30
  "data-test": 'native-select_trigger',
34
31
  "data-rs-internal": 'select-native-trigger'
35
32
  }, /*#__PURE__*/React.createElement("span", {
36
33
  className: cn(styles.triggerText, !(value !== null && value !== void 0 && value.value) && styles.labelNotSelected),
37
- "data-test": 'native-select_trigger_text'
38
- }, (value === null || value === void 0 ? void 0 : value.label) ?? placeholder), /*#__PURE__*/React.createElement("div", {
39
- className: styles.iconsWrapper
40
- }, isLoading && /*#__PURE__*/React.createElement(LoadingOutlineIcon, {
41
- style: {
42
- pointerEvents: 'none'
43
- },
44
- className: styles.icon,
45
- "data-test": "loading-icon",
46
- "data-render-all-sizes": true
47
- }), /*#__PURE__*/React.createElement(DownOutlineIcon, {
48
- className: styles.toggle,
49
- "data-test": "down-icon",
50
- "data-render-all-sizes": true
51
- })));
34
+ "data-test": 'native-select_trigger_text',
35
+ "aria-hidden": "true"
36
+ }, (value === null || value === void 0 ? void 0 : value.label) ?? placeholder), /*#__PURE__*/React.createElement(Icons, {
37
+ isLoading: isLoading,
38
+ triggerId: triggerId,
39
+ onClear: onClear,
40
+ canHaveClearIcon: isClearable && !isDisabled && Boolean(value === null || value === void 0 ? void 0 : value.value),
41
+ customStylesMap: styles
42
+ }));
52
43
  };
53
44
 
54
45
  export { NativeSelectTrigger };
@@ -1,3 +1,3 @@
1
1
  /// <reference types="react" />
2
- import type { InnerSingleComponentProps } from './types';
3
- export declare function SelectBasic<T extends object>({ id, size, mode, theme, style, className, isClearable, isLoading, placement, boldLabel, reserveErrorLine, noOptionsMessage, maxVisibleItems, ...ariaProps }: InnerSingleComponentProps<T>): JSX.Element;
2
+ import { InnerSingleComponentProps } from './types';
3
+ export declare function SelectBasic({ 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,81 +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/select';
4
- import { useSelect, HiddenSelect } from '@react-aria/select';
5
- import { useButton } from '@react-aria/button';
3
+ import { Dropdown } from '@rescui/dropdown';
4
+ import { useSelect } from 'downshift';
6
5
  import { useFocusRing } from '@react-aria/focus';
7
- import { mergeProps } from '@react-aria/utils';
8
- import { PopupContent } from './parts/popup-content.js';
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';
9
11
  import { Icons } from './parts/icons.js';
12
+ import { HiddenSelect } from './parts/hidden-select.js';
10
13
  import { FormTriggerWrapper } from './parts/form-trigger-wrapper.js';
11
- import { useLabelFocus } from './hooks/use-label-focus.js';
12
14
  import { getEmptyMessage } from './utils/get-empty-message.js';
13
- import styles from './styles/common.p.module.css.js';
14
- const THEME_STYLES = {
15
- light: styles.themeLight,
16
- dark: styles.themeDark
17
- };
18
- const SIZE_STYLES = {
19
- s: styles.sizeS,
20
- m: styles.sizeM,
21
- l: styles.sizeL
22
- };
23
- const MODE_STYLES = {
24
- classic: styles.classic,
25
- rock: styles.rock
26
- }; // 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
27
20
 
28
21
  function SelectBasic(_ref) {
29
- var _state$selectedItems$;
30
-
31
22
  let {
32
- id,
23
+ options,
24
+ value: selectedItem,
25
+ onChange,
26
+ error,
27
+ label,
28
+ note,
33
29
  size,
34
30
  mode,
35
31
  theme,
36
- style,
37
- className,
38
- isClearable,
39
- isLoading,
40
32
  placement,
41
33
  boldLabel,
42
34
  reserveErrorLine,
43
- noOptionsMessage,
44
35
  maxVisibleItems,
45
- ...ariaProps
36
+ isClearable,
37
+ isLoading,
38
+ isDisabled,
39
+ name,
40
+ noOptionsMessage,
41
+ className,
42
+ id,
43
+ style,
44
+ placeholder,
45
+ ...rest
46
46
  } = _ref;
47
- // Create state based on the incoming props
48
- const state = useSelectState({ ...ariaProps,
49
- allowsEmptyCollection: true
50
- });
51
- const hasValue = state.selectedItems.length > 0; // Get props for child elements from useSelect
52
-
53
- 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();
54
57
  const {
55
- labelProps,
56
- triggerProps,
57
- valueProps,
58
- menuProps: listBoxProps,
59
- hiddenSelectProps,
58
+ fieldProps,
60
59
  errorMessageProps,
61
- descriptionProps
62
- } = useSelect({ ...ariaProps,
63
- onKeyDown: e => {
64
- if (isClearable && hasValue) {
65
- if (e.key === 'Backspace' || e.key === 'Delete') {
66
- state.setValue(null);
67
- e.stopPropagation();
68
- }
69
- }
70
- }
71
- }, state, triggerRef);
60
+ descriptionProps,
61
+ baseId
62
+ } = useA11y({
63
+ prefix: 'select',
64
+ error,
65
+ description: note
66
+ });
72
67
  const {
73
- buttonProps
74
- } = useButton({ ...triggerProps,
75
- elementType: 'div'
76
- }, triggerRef);
77
- const isDisabled = ariaProps.isDisabled;
78
- 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
+
79
120
  const {
80
121
  isFocusVisible,
81
122
  focusProps
@@ -86,60 +127,83 @@ function SelectBasic(_ref) {
86
127
  isFocusedByLabel,
87
128
  labelClickProps,
88
129
  triggerFocusProps: labelFocusForTriggerProps
89
- } = useLabelFocus(triggerRef);
130
+ } = useLabelFocus();
90
131
  const emptyListMessage = getEmptyMessage({
91
132
  isLoading,
92
133
  isNothingFound: false,
93
134
  noOptionsMessage
94
135
  });
95
- return /*#__PURE__*/React.createElement("div", {
96
- className: cn(className, styles.root, THEME_STYLES[theme], SIZE_STYLES[size], MODE_STYLES[mode], isDisabled ? styles.disabled : styles.enabled, Boolean(ariaProps.errorMessage) && styles.error),
97
- id: id,
98
- style: style,
99
- "data-test": "select"
100
- }, /*#__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, {
101
144
  theme: theme,
102
145
  size: size,
103
146
  disabled: isDisabled,
104
- label: ariaProps.label,
105
- labelProps: mergeProps(labelClickProps, labelProps),
147
+ label: label,
148
+ labelProps: mergeProps(getLabelProps(), labelClickProps),
106
149
  boldLabel: boldLabel,
107
- error: ariaProps.errorMessage,
150
+ error: error,
108
151
  reserveErrorLine: reserveErrorLine,
109
152
  errorMessageProps: errorMessageProps,
110
- note: ariaProps.description,
153
+ note: note,
111
154
  descriptionProps: descriptionProps
112
- }, /*#__PURE__*/React.createElement(HiddenSelect, { ...hiddenSelectProps
113
- }), /*#__PURE__*/React.createElement("div", { ...mergeProps(buttonProps, focusProps, labelFocusForTriggerProps),
114
- ref: triggerRef,
115
- className: styles.trigger,
116
- "data-open": isOpen,
117
- "data-focus-visible": isFocusVisible,
118
- "data-label-focused": isFocusedByLabel,
119
- "data-test": "select__trigger"
120
- }, /*#__PURE__*/React.createElement("span", { ...valueProps,
121
- className: cn(styles.triggerText, !hasValue && styles.labelNotSelected),
122
- "data-test": "select__trigger_content"
123
- }, ((_state$selectedItems$ = state.selectedItems[0]) === null || _state$selectedItems$ === void 0 ? void 0 : _state$selectedItems$.rendered) ?? ariaProps.placeholder), /*#__PURE__*/React.createElement(Icons, {
124
- isLoading: isLoading,
125
- canHaveClearIcon: isClearable && !isDisabled && hasValue,
126
- onClear: () => {
127
- var _triggerRef$current;
128
-
129
- state.setValue(null);
130
- (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 || _triggerRef$current.focus();
131
- },
132
- triggerId: triggerProps.id
133
- })), /*#__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,
134
162
  theme: theme,
135
- state: state,
136
- triggerRef: triggerRef,
163
+ ref: dropdownRef,
137
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,
138
203
  size: size,
139
- mode: mode,
140
- listBoxProps: listBoxProps,
204
+ maxVisibleItems: maxVisibleItems,
141
205
  emptyListMessage: emptyListMessage,
142
- maxVisibleItems: maxVisibleItems
206
+ items: listBoxItems
143
207
  })));
144
208
  }
145
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;