@snack-uikit/fields 0.23.8 → 0.23.9

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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 0.23.9 (2024-08-12)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **FF-0000:** select disabled error validation state ([c64c9e9](https://github.com/cloud-ru-tech/snack-uikit/commit/c64c9e9da6f48d2e7e28a54083b9dd4dfa44ed10))
12
+
13
+
14
+
15
+
16
+
6
17
  ## 0.23.8 (2024-08-07)
7
18
 
8
19
  ### Only dependencies have been changed
@@ -31,9 +31,9 @@ const defaultSelectedOptionFormatter = item =>
31
31
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
32
32
  // @ts-expect-error
33
33
  (item === null || item === void 0 ? void 0 : item.content.option) || '';
34
- export const FieldSelectMultiple = forwardRef((_a, ref) => {
35
- var _b;
36
- var { id, name, placeholder, size = 's', options, value: valueProp, defaultValue, onChange: onChangeProp, disabled = false, readonly = false, searchable = true, showClearButton = true, onKeyDown: onInputKeyDownProp, validationState = 'default', search, autocomplete = false, prefixIcon, removeByBackspace = false, addOptionByEnter = false, open: openProp, onOpenChange, selectedOptionFormatter = defaultSelectedOptionFormatter } = _a, rest = __rest(_a, ["id", "name", "placeholder", "size", "options", "value", "defaultValue", "onChange", "disabled", "readonly", "searchable", "showClearButton", "onKeyDown", "validationState", "search", "autocomplete", "prefixIcon", "removeByBackspace", "addOptionByEnter", "open", "onOpenChange", "selectedOptionFormatter"]);
34
+ export const FieldSelectMultiple = forwardRef((props, ref) => {
35
+ var _a;
36
+ const { id, name, placeholder, size = 's', options, value: valueProp, defaultValue, onChange: onChangeProp, disabled = false, readonly = false, searchable = true, showClearButton = true, onKeyDown: onInputKeyDownProp, validationState = 'default', search, autocomplete = false, prefixIcon, removeByBackspace = false, addOptionByEnter = false, open: openProp, onOpenChange, selectedOptionFormatter = defaultSelectedOptionFormatter } = props, rest = __rest(props, ["id", "name", "placeholder", "size", "options", "value", "defaultValue", "onChange", "disabled", "readonly", "searchable", "showClearButton", "onKeyDown", "validationState", "search", "autocomplete", "prefixIcon", "removeByBackspace", "addOptionByEnter", "open", "onOpenChange", "selectedOptionFormatter"]);
37
37
  const localRef = useRef(null);
38
38
  const inputPlugRef = useRef(null);
39
39
  const contentRef = useRef(null);
@@ -117,7 +117,7 @@ export const FieldSelectMultiple = forwardRef((_a, ref) => {
117
117
  const fuzzySearch = useFuzzySearch(items);
118
118
  const result = autocomplete || !searchable || prevInputValue.current === inputValue ? items : fuzzySearch(inputValue);
119
119
  const fieldValidationState = getValidationState({ validationState, error: rest.error });
120
- return (_jsx(FieldDecorator, Object.assign({}, extractSupportProps(rest), extractFieldDecoratorProps(rest), { labelFor: id, size: size, validationState: fieldValidationState, children: _jsx(Droplist, Object.assign({}, extractListProps(rest), { items: result, triggerElemRef: localRef, trigger: 'click', selection: {
120
+ return (_jsx(FieldDecorator, Object.assign({}, extractSupportProps(rest), extractFieldDecoratorProps(props), { validationState: fieldValidationState, children: _jsx(Droplist, Object.assign({}, extractListProps(props), { items: result, triggerElemRef: localRef, trigger: 'click', selection: {
121
121
  mode: 'multiple',
122
122
  value: value,
123
123
  onChange: value => {
@@ -128,7 +128,7 @@ export const FieldSelectMultiple = forwardRef((_a, ref) => {
128
128
  updateInputValue();
129
129
  }
130
130
  },
131
- }, dataFiltered: (_b = rest.dataFiltered) !== null && _b !== void 0 ? _b : Boolean(inputValue.length), size: size, open: !disabled && !readonly && open, onOpenChange: handleOpenChange, children: ({ onKeyDown }) => {
131
+ }, dataFiltered: (_a = rest.dataFiltered) !== null && _a !== void 0 ? _a : Boolean(inputValue.length), size: size, open: !disabled && !readonly && open, onOpenChange: handleOpenChange, children: ({ onKeyDown }) => {
132
132
  var _a, _b, _c, _d;
133
133
  return (_jsx(FieldContainerPrivate, { className: cn(styles.container, styles.tagContainer), validationState: fieldValidationState, disabled: disabled, readonly: readonly, focused: open, variant: 'single-line-container', inputRef: localRef, size: size, prefix: prefixIcon, children: _jsxs(_Fragment, { children: [_jsxs("div", { className: styles.contentWrapper, ref: contentRef, children: [selectedItems &&
134
134
  selectedItems.map(option => {
@@ -29,9 +29,9 @@ const defaultSelectedOptionFormatter = item =>
29
29
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
30
30
  // @ts-expect-error
31
31
  (item === null || item === void 0 ? void 0 : item.content.option) || '';
32
- export const FieldSelectSingle = forwardRef((_a, ref) => {
33
- var _b;
34
- var { id, name, placeholder, size = 's', options, value: valueProp, defaultValue, onChange: onChangeProp, disabled = false, readonly = false, searchable = true, showCopyButton = true, showClearButton = true, onKeyDown: onInputKeyDownProp, required = false, validationState = 'default', search, autocomplete = false, prefixIcon, addOptionByEnter = false, open: openProp, onOpenChange, selectedOptionFormatter = defaultSelectedOptionFormatter } = _a, rest = __rest(_a, ["id", "name", "placeholder", "size", "options", "value", "defaultValue", "onChange", "disabled", "readonly", "searchable", "showCopyButton", "showClearButton", "onKeyDown", "required", "validationState", "search", "autocomplete", "prefixIcon", "addOptionByEnter", "open", "onOpenChange", "selectedOptionFormatter"]);
32
+ export const FieldSelectSingle = forwardRef((props, ref) => {
33
+ var _a;
34
+ const { id, name, placeholder, size = 's', options, value: valueProp, defaultValue, onChange: onChangeProp, disabled = false, readonly = false, searchable = true, showCopyButton = true, showClearButton = true, onKeyDown: onInputKeyDownProp, required = false, validationState = 'default', search, autocomplete = false, prefixIcon, addOptionByEnter = false, open: openProp, onOpenChange, selectedOptionFormatter = defaultSelectedOptionFormatter } = props, rest = __rest(props, ["id", "name", "placeholder", "size", "options", "value", "defaultValue", "onChange", "disabled", "readonly", "searchable", "showCopyButton", "showClearButton", "onKeyDown", "required", "validationState", "search", "autocomplete", "prefixIcon", "addOptionByEnter", "open", "onOpenChange", "selectedOptionFormatter"]);
35
35
  const localRef = useRef(null);
36
36
  const [open = false, setOpen] = useValueControl({ value: openProp, onChange: onOpenChange });
37
37
  const [value, setValue] = useValueControl({
@@ -43,7 +43,7 @@ export const FieldSelectSingle = forwardRef((_a, ref) => {
43
43
  const { inputValue, setInputValue, prevInputValue, updateInputValue } = useSearchInput(Object.assign(Object.assign({}, search), {
44
44
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
45
45
  // @ts-expect-error
46
- defaultValue: (_b = selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.content.option) !== null && _b !== void 0 ? _b : '', selectedOptionFormatter }));
46
+ defaultValue: (_a = selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.content.option) !== null && _a !== void 0 ? _a : '', selectedOptionFormatter }));
47
47
  const prevSelectedItem = useRef(selectedItem);
48
48
  useLayoutEffect(() => {
49
49
  setItems(({ selectedItem }) => updateItems({ options, value, selectedItem }));
@@ -124,7 +124,7 @@ export const FieldSelectSingle = forwardRef((_a, ref) => {
124
124
  ? items
125
125
  : fuzzySearch(inputValue);
126
126
  const fieldValidationState = getValidationState({ validationState, error: rest.error });
127
- return (_jsx(FieldDecorator, Object.assign({}, extractSupportProps(rest), extractFieldDecoratorProps(rest), { validationState: fieldValidationState, required: required, readonly: readonly, labelFor: id, disabled: disabled, size: size, children: _jsx(Droplist, Object.assign({}, extractListProps(rest), { items: result, selection: {
127
+ return (_jsx(FieldDecorator, Object.assign({}, extractSupportProps(rest), extractFieldDecoratorProps(props), { validationState: fieldValidationState, children: _jsx(Droplist, Object.assign({}, extractListProps(props), { items: result, selection: {
128
128
  mode: 'single',
129
129
  value: value,
130
130
  onChange: handleSelectionChange,
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Fields",
7
- "version": "0.23.8",
7
+ "version": "0.23.9",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -59,5 +59,5 @@
59
59
  "peerDependencies": {
60
60
  "@snack-uikit/locale": "*"
61
61
  },
62
- "gitHead": "cd3b85a6cd111a1b9487d310b2eb5d5a85374841"
62
+ "gitHead": "14c15e506968994bb934ef059e62c49b6fd065e4"
63
63
  }
@@ -25,247 +25,239 @@ const defaultSelectedOptionFormatter: SelectedOptionFormatter = item =>
25
25
  // @ts-expect-error
26
26
  item?.content.option || '';
27
27
 
28
- export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMultipleProps>(
29
- (
30
- {
31
- id,
32
- name,
33
- placeholder,
34
- size = 's',
35
- options,
36
- value: valueProp,
37
- defaultValue,
38
- onChange: onChangeProp,
39
- disabled = false,
40
- readonly = false,
41
- searchable = true,
42
- showClearButton = true,
43
- onKeyDown: onInputKeyDownProp,
44
- validationState = 'default',
45
- search,
46
- autocomplete = false,
47
- prefixIcon,
48
- removeByBackspace = false,
49
- addOptionByEnter = false,
50
- open: openProp,
51
- onOpenChange,
52
- selectedOptionFormatter = defaultSelectedOptionFormatter,
53
- ...rest
54
- },
55
- ref,
56
- ) => {
57
- const localRef = useRef<HTMLInputElement>(null);
58
- const inputPlugRef = useRef<HTMLSpanElement>(null);
59
- const contentRef = useRef<HTMLDivElement>(null);
28
+ export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMultipleProps>((props, ref) => {
29
+ const {
30
+ id,
31
+ name,
32
+ placeholder,
33
+ size = 's',
34
+ options,
35
+ value: valueProp,
36
+ defaultValue,
37
+ onChange: onChangeProp,
38
+ disabled = false,
39
+ readonly = false,
40
+ searchable = true,
41
+ showClearButton = true,
42
+ onKeyDown: onInputKeyDownProp,
43
+ validationState = 'default',
44
+ search,
45
+ autocomplete = false,
46
+ prefixIcon,
47
+ removeByBackspace = false,
48
+ addOptionByEnter = false,
49
+ open: openProp,
50
+ onOpenChange,
51
+ selectedOptionFormatter = defaultSelectedOptionFormatter,
52
+ ...rest
53
+ } = props;
54
+ const localRef = useRef<HTMLInputElement>(null);
55
+ const inputPlugRef = useRef<HTMLSpanElement>(null);
56
+ const contentRef = useRef<HTMLDivElement>(null);
60
57
 
61
- const [open = false, setOpen] = useValueControl<boolean>({ value: openProp, onChange: onOpenChange });
58
+ const [open = false, setOpen] = useValueControl<boolean>({ value: openProp, onChange: onOpenChange });
62
59
 
63
- const [value, setValue] = useValueControl<SelectionSingleValueType[]>({
64
- value: valueProp,
65
- defaultValue,
66
- onChange: onChangeProp,
67
- });
60
+ const [value, setValue] = useValueControl<SelectionSingleValueType[]>({
61
+ value: valueProp,
62
+ defaultValue,
63
+ onChange: onChangeProp,
64
+ });
68
65
 
69
- const [{ selectedItems, items = [] }, setItems] = useState<{
70
- selectedItems?: ItemWithId[];
71
- items: ItemProps[];
72
- }>(() => updateMultipleItems({ options, value, currentItems: [], selectedItems: undefined }));
66
+ const [{ selectedItems, items = [] }, setItems] = useState<{
67
+ selectedItems?: ItemWithId[];
68
+ items: ItemProps[];
69
+ }>(() => updateMultipleItems({ options, value, currentItems: [], selectedItems: undefined }));
73
70
 
74
- const { inputValue, setInputValue, prevInputValue, updateInputValue } = useSearchInput({
75
- ...search,
76
- defaultValue: '',
77
- selectedOptionFormatter,
78
- });
71
+ const { inputValue, setInputValue, prevInputValue, updateInputValue } = useSearchInput({
72
+ ...search,
73
+ defaultValue: '',
74
+ selectedOptionFormatter,
75
+ });
79
76
 
80
- useLayoutEffect(() => {
81
- setItems(({ selectedItems }) => updateMultipleItems({ options, value, selectedItems }));
82
- }, [options, value]);
77
+ useLayoutEffect(() => {
78
+ setItems(({ selectedItems }) => updateMultipleItems({ options, value, selectedItems }));
79
+ }, [options, value]);
83
80
 
84
- const onClear = () => {
85
- setValue(selectedItems?.filter(item => item.disabled).map(item => item.id));
86
- localRef.current?.focus();
81
+ const onClear = () => {
82
+ setValue(selectedItems?.filter(item => item.disabled).map(item => item.id));
83
+ localRef.current?.focus();
87
84
 
88
- if (rest.required) {
89
- setOpen(true);
90
- }
91
- };
85
+ if (rest.required) {
86
+ setOpen(true);
87
+ }
88
+ };
92
89
 
93
- const { ArrowIcon, arrowIconSize } = getArrowIcon({ size, open });
90
+ const { ArrowIcon, arrowIconSize } = getArrowIcon({ size, open });
94
91
 
95
- const { buttons, inputKeyDownNavigationHandler, buttonsRefs } = useButtons({
96
- readonly,
97
- size,
98
- showClearButton:
99
- showClearButton && !disabled && !readonly && Boolean(selectedItems?.find(item => !item.disabled)),
100
- showCopyButton: false,
101
- inputRef: localRef,
102
- onClear,
103
- });
92
+ const { buttons, inputKeyDownNavigationHandler, buttonsRefs } = useButtons({
93
+ readonly,
94
+ size,
95
+ showClearButton: showClearButton && !disabled && !readonly && Boolean(selectedItems?.find(item => !item.disabled)),
96
+ showCopyButton: false,
97
+ inputRef: localRef,
98
+ onClear,
99
+ });
104
100
 
105
- const commonHandleOnKeyDown = useHandleOnKeyDown({
106
- inputKeyDownNavigationHandler,
107
- onInputKeyDownProp,
108
- setOpen,
109
- });
101
+ const commonHandleOnKeyDown = useHandleOnKeyDown({
102
+ inputKeyDownNavigationHandler,
103
+ onInputKeyDownProp,
104
+ setOpen,
105
+ });
110
106
 
111
- const handleItemDelete = useHandleDeleteItem(setValue);
112
- const handleOnKeyDown = (onKeyDown?: KeyboardEventHandler<HTMLElement>) => (e: KeyboardEvent<HTMLInputElement>) => {
113
- if (removeByBackspace && e.code === 'Backspace' && inputValue === '') {
114
- if (selectedItems?.length && !selectedItems.slice(-1)[0].disabled) {
115
- handleItemDelete(selectedItems.pop() as BaseItemProps)();
116
- }
107
+ const handleItemDelete = useHandleDeleteItem(setValue);
108
+ const handleOnKeyDown = (onKeyDown?: KeyboardEventHandler<HTMLElement>) => (e: KeyboardEvent<HTMLInputElement>) => {
109
+ if (removeByBackspace && e.code === 'Backspace' && inputValue === '') {
110
+ if (selectedItems?.length && !selectedItems.slice(-1)[0].disabled) {
111
+ handleItemDelete(selectedItems.pop() as BaseItemProps)();
117
112
  }
113
+ }
118
114
 
119
- if (e.code === 'Enter') {
120
- e.stopPropagation();
121
- e.preventDefault();
122
- }
115
+ if (e.code === 'Enter') {
116
+ e.stopPropagation();
117
+ e.preventDefault();
118
+ }
123
119
 
124
- if (addOptionByEnter && e.code === 'Enter' && inputValue !== '') {
125
- if (!(value ?? []).includes(inputValue)) {
126
- setValue((value: SelectionSingleValueType[]) => (value ?? []).concat(inputValue));
127
- updateInputValue();
128
- }
120
+ if (addOptionByEnter && e.code === 'Enter' && inputValue !== '') {
121
+ if (!(value ?? []).includes(inputValue)) {
122
+ setValue((value: SelectionSingleValueType[]) => (value ?? []).concat(inputValue));
123
+ updateInputValue();
129
124
  }
125
+ }
130
126
 
131
- if (!open && prevInputValue.current !== inputValue) {
132
- setOpen(true);
133
- }
127
+ if (!open && prevInputValue.current !== inputValue) {
128
+ setOpen(true);
129
+ }
134
130
 
135
- commonHandleOnKeyDown(onKeyDown)(e);
136
- };
131
+ commonHandleOnKeyDown(onKeyDown)(e);
132
+ };
137
133
 
138
- const handleOpenChange = (open: boolean) => {
139
- if (!readonly && !disabled && !buttonsRefs.includes(document.activeElement)) {
140
- setOpen(open);
134
+ const handleOpenChange = (open: boolean) => {
135
+ if (!readonly && !disabled && !buttonsRefs.includes(document.activeElement)) {
136
+ setOpen(open);
141
137
 
142
- if (!open) {
143
- if (inputPlugRef.current) {
144
- inputPlugRef.current.style.width = BASE_MIN_WIDTH + 'px';
145
- }
138
+ if (!open) {
139
+ if (inputPlugRef.current) {
140
+ inputPlugRef.current.style.width = BASE_MIN_WIDTH + 'px';
146
141
  }
142
+ }
147
143
 
148
- if (open) {
149
- if (inputPlugRef.current) {
150
- inputPlugRef.current.style.width = 'unset';
151
- }
144
+ if (open) {
145
+ if (inputPlugRef.current) {
146
+ inputPlugRef.current.style.width = 'unset';
152
147
  }
153
148
  }
154
- };
149
+ }
150
+ };
155
151
 
156
- const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
157
- if (!open && !buttonsRefs.filter(Boolean).includes(e.relatedTarget)) {
158
- updateInputValue();
152
+ const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
153
+ if (!open && !buttonsRefs.filter(Boolean).includes(e.relatedTarget)) {
154
+ updateInputValue();
159
155
 
160
- rest?.onBlur?.(e);
161
- }
162
- };
156
+ rest?.onBlur?.(e);
157
+ }
158
+ };
163
159
 
164
- const fuzzySearch = useFuzzySearch(items);
165
- const result =
166
- autocomplete || !searchable || prevInputValue.current === inputValue ? items : fuzzySearch(inputValue);
160
+ const fuzzySearch = useFuzzySearch(items);
161
+ const result = autocomplete || !searchable || prevInputValue.current === inputValue ? items : fuzzySearch(inputValue);
167
162
 
168
- const fieldValidationState = getValidationState({ validationState, error: rest.error });
163
+ const fieldValidationState = getValidationState({ validationState, error: rest.error });
169
164
 
170
- return (
171
- <FieldDecorator
172
- {...extractSupportProps(rest)}
173
- {...extractFieldDecoratorProps(rest)}
174
- labelFor={id}
165
+ return (
166
+ <FieldDecorator
167
+ {...extractSupportProps(rest)}
168
+ {...extractFieldDecoratorProps(props)}
169
+ validationState={fieldValidationState}
170
+ >
171
+ <Droplist
172
+ {...extractListProps(props)}
173
+ items={result}
174
+ triggerElemRef={localRef}
175
+ trigger='click'
176
+ selection={{
177
+ mode: 'multiple',
178
+ value: value,
179
+ onChange: value => {
180
+ setValue(value);
181
+ if (inputValue) {
182
+ localRef.current?.focus();
183
+ updateInputValue();
184
+ }
185
+ },
186
+ }}
187
+ dataFiltered={rest.dataFiltered ?? Boolean(inputValue.length)}
175
188
  size={size}
176
- validationState={fieldValidationState}
189
+ open={!disabled && !readonly && open}
190
+ onOpenChange={handleOpenChange}
177
191
  >
178
- <Droplist
179
- {...extractListProps(rest)}
180
- items={result}
181
- triggerElemRef={localRef}
182
- trigger='click'
183
- selection={{
184
- mode: 'multiple',
185
- value: value,
186
- onChange: value => {
187
- setValue(value);
188
- if (inputValue) {
189
- localRef.current?.focus();
190
- updateInputValue();
191
- }
192
- },
193
- }}
194
- dataFiltered={rest.dataFiltered ?? Boolean(inputValue.length)}
195
- size={size}
196
- open={!disabled && !readonly && open}
197
- onOpenChange={handleOpenChange}
198
- >
199
- {({ onKeyDown }) => (
200
- <FieldContainerPrivate
201
- className={cn(styles.container, styles.tagContainer)}
202
- validationState={fieldValidationState}
203
- disabled={disabled}
204
- readonly={readonly}
205
- focused={open}
206
- variant='single-line-container'
207
- inputRef={localRef}
208
- size={size}
209
- prefix={prefixIcon}
210
- >
211
- <>
212
- <div className={styles.contentWrapper} ref={contentRef}>
213
- {selectedItems &&
214
- selectedItems.map(option => (
215
- <Tag
216
- size={size === 'l' ? 's' : 'xs'}
217
- tabIndex={-1}
218
- label={selectedOptionFormatter(option)}
219
- key={option.id}
220
- appearance={option.appearance ?? 'neutral'}
221
- onDelete={!option.disabled && !disabled && !readonly ? handleItemDelete(option) : undefined}
222
- />
223
- ))}
224
-
225
- <div
226
- className={styles.inputWrapper}
227
- style={{
228
- minWidth: value
229
- ? Math.min(
230
- contentRef.current?.clientWidth ?? BASE_MIN_WIDTH,
231
- inputPlugRef.current?.clientWidth ?? BASE_MIN_WIDTH,
232
- )
233
- : '100%',
234
- }}
235
- >
236
- <InputPrivate
237
- id={id}
238
- name={name}
239
- type='text'
240
- disabled={disabled}
241
- placeholder={!selectedItems || !selectedItems.length ? placeholder : undefined}
242
- ref={mergeRefs(ref, localRef)}
243
- onChange={searchable ? setInputValue : undefined}
244
- value={searchable ? inputValue : ''}
245
- readonly={!searchable || readonly}
246
- data-test-id='field-select__input'
247
- onKeyDown={handleOnKeyDown(onKeyDown)}
248
- onBlur={handleBlur}
249
- className={cn({
250
- [styles.readonlyCursor]: !searchable,
251
- })}
192
+ {({ onKeyDown }) => (
193
+ <FieldContainerPrivate
194
+ className={cn(styles.container, styles.tagContainer)}
195
+ validationState={fieldValidationState}
196
+ disabled={disabled}
197
+ readonly={readonly}
198
+ focused={open}
199
+ variant='single-line-container'
200
+ inputRef={localRef}
201
+ size={size}
202
+ prefix={prefixIcon}
203
+ >
204
+ <>
205
+ <div className={styles.contentWrapper} ref={contentRef}>
206
+ {selectedItems &&
207
+ selectedItems.map(option => (
208
+ <Tag
209
+ size={size === 'l' ? 's' : 'xs'}
210
+ tabIndex={-1}
211
+ label={selectedOptionFormatter(option)}
212
+ key={option.id}
213
+ appearance={option.appearance ?? 'neutral'}
214
+ onDelete={!option.disabled && !disabled && !readonly ? handleItemDelete(option) : undefined}
252
215
  />
253
- </div>
254
- </div>
216
+ ))}
255
217
 
256
- <div className={styles.postfix}>
257
- {buttons}
258
- <ArrowIcon size={arrowIconSize} className={styles.arrowIcon} />
218
+ <div
219
+ className={styles.inputWrapper}
220
+ style={{
221
+ minWidth: value
222
+ ? Math.min(
223
+ contentRef.current?.clientWidth ?? BASE_MIN_WIDTH,
224
+ inputPlugRef.current?.clientWidth ?? BASE_MIN_WIDTH,
225
+ )
226
+ : '100%',
227
+ }}
228
+ >
229
+ <InputPrivate
230
+ id={id}
231
+ name={name}
232
+ type='text'
233
+ disabled={disabled}
234
+ placeholder={!selectedItems || !selectedItems.length ? placeholder : undefined}
235
+ ref={mergeRefs(ref, localRef)}
236
+ onChange={searchable ? setInputValue : undefined}
237
+ value={searchable ? inputValue : ''}
238
+ readonly={!searchable || readonly}
239
+ data-test-id='field-select__input'
240
+ onKeyDown={handleOnKeyDown(onKeyDown)}
241
+ onBlur={handleBlur}
242
+ className={cn({
243
+ [styles.readonlyCursor]: !searchable,
244
+ })}
245
+ />
259
246
  </div>
247
+ </div>
248
+
249
+ <div className={styles.postfix}>
250
+ {buttons}
251
+ <ArrowIcon size={arrowIconSize} className={styles.arrowIcon} />
252
+ </div>
260
253
 
261
- <span ref={inputPlugRef} className={styles.inputPlug}>
262
- {inputValue}
263
- </span>
264
- </>
265
- </FieldContainerPrivate>
266
- )}
267
- </Droplist>
268
- </FieldDecorator>
269
- );
270
- },
271
- );
254
+ <span ref={inputPlugRef} className={styles.inputPlug}>
255
+ {inputValue}
256
+ </span>
257
+ </>
258
+ </FieldContainerPrivate>
259
+ )}
260
+ </Droplist>
261
+ </FieldDecorator>
262
+ );
263
+ });
@@ -32,224 +32,215 @@ const defaultSelectedOptionFormatter: SelectedOptionFormatter = item =>
32
32
  // @ts-expect-error
33
33
  item?.content.option || '';
34
34
 
35
- export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleProps>(
36
- (
37
- {
38
- id,
39
- name,
40
- placeholder,
41
- size = 's',
42
- options,
43
- value: valueProp,
44
- defaultValue,
45
- onChange: onChangeProp,
46
- disabled = false,
47
- readonly = false,
48
- searchable = true,
49
- showCopyButton = true,
50
- showClearButton = true,
51
- onKeyDown: onInputKeyDownProp,
52
- required = false,
53
- validationState = 'default',
54
- search,
55
- autocomplete = false,
56
- prefixIcon,
57
- addOptionByEnter = false,
58
- open: openProp,
59
- onOpenChange,
60
- selectedOptionFormatter = defaultSelectedOptionFormatter,
61
- ...rest
62
- },
63
- ref,
64
- ) => {
65
- const localRef = useRef<HTMLInputElement>(null);
66
- const [open = false, setOpen] = useValueControl<boolean>({ value: openProp, onChange: onOpenChange });
67
- const [value, setValue] = useValueControl<SelectionSingleValueType>({
68
- value: valueProp,
69
- defaultValue,
70
- onChange: onChangeProp,
71
- });
72
-
73
- const [{ selectedItem, items = [] }, setItems] = useState<{
74
- selectedItem?: ItemWithId;
75
- items: ItemProps[];
76
- }>(() => updateItems({ options, value, currentItems: [], selectedItem: undefined }));
77
-
78
- const { inputValue, setInputValue, prevInputValue, updateInputValue } = useSearchInput({
79
- ...search,
35
+ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleProps>((props, ref) => {
36
+ const {
37
+ id,
38
+ name,
39
+ placeholder,
40
+ size = 's',
41
+ options,
42
+ value: valueProp,
43
+ defaultValue,
44
+ onChange: onChangeProp,
45
+ disabled = false,
46
+ readonly = false,
47
+ searchable = true,
48
+ showCopyButton = true,
49
+ showClearButton = true,
50
+ onKeyDown: onInputKeyDownProp,
51
+ required = false,
52
+ validationState = 'default',
53
+ search,
54
+ autocomplete = false,
55
+ prefixIcon,
56
+ addOptionByEnter = false,
57
+ open: openProp,
58
+ onOpenChange,
59
+ selectedOptionFormatter = defaultSelectedOptionFormatter,
60
+ ...rest
61
+ } = props;
62
+ const localRef = useRef<HTMLInputElement>(null);
63
+ const [open = false, setOpen] = useValueControl<boolean>({ value: openProp, onChange: onOpenChange });
64
+ const [value, setValue] = useValueControl<SelectionSingleValueType>({
65
+ value: valueProp,
66
+ defaultValue,
67
+ onChange: onChangeProp,
68
+ });
69
+
70
+ const [{ selectedItem, items = [] }, setItems] = useState<{
71
+ selectedItem?: ItemWithId;
72
+ items: ItemProps[];
73
+ }>(() => updateItems({ options, value, currentItems: [], selectedItem: undefined }));
74
+
75
+ const { inputValue, setInputValue, prevInputValue, updateInputValue } = useSearchInput({
76
+ ...search,
77
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
78
+ // @ts-expect-error
79
+ defaultValue: selectedItem?.content.option ?? '',
80
+ selectedOptionFormatter,
81
+ });
82
+
83
+ const prevSelectedItem = useRef<ItemWithId | undefined>(selectedItem);
84
+
85
+ useLayoutEffect(() => {
86
+ setItems(({ selectedItem }) => updateItems({ options, value, selectedItem }));
87
+ }, [options, value]);
88
+
89
+ useEffect(() => {
90
+ if (
91
+ prevSelectedItem.current &&
92
+ prevSelectedItem.current.id === selectedItem?.id &&
80
93
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
81
94
  // @ts-expect-error
82
- defaultValue: selectedItem?.content.option ?? '',
83
- selectedOptionFormatter,
84
- });
85
-
86
- const prevSelectedItem = useRef<ItemWithId | undefined>(selectedItem);
87
-
88
- useLayoutEffect(() => {
89
- setItems(({ selectedItem }) => updateItems({ options, value, selectedItem }));
90
- }, [options, value]);
91
-
92
- useEffect(() => {
93
- if (
94
- prevSelectedItem.current &&
95
- prevSelectedItem.current.id === selectedItem?.id &&
96
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
97
- // @ts-expect-error
98
- prevSelectedItem.current.content.option === selectedItem?.content.option
99
- ) {
100
- return;
101
- }
102
-
103
- prevSelectedItem.current = selectedItem;
95
+ prevSelectedItem.current.content.option === selectedItem?.content.option
96
+ ) {
97
+ return;
98
+ }
99
+
100
+ prevSelectedItem.current = selectedItem;
101
+ updateInputValue(selectedItem);
102
+
103
+ // eslint-disable-next-line react-hooks/exhaustive-deps
104
+ }, [selectedItem, prevSelectedItem]);
105
+
106
+ const onClear = useCallback(() => {
107
+ setValue(undefined);
108
+ localRef.current?.focus();
109
+ if (required) {
110
+ setOpen(true);
111
+ }
112
+ }, [required, setOpen, setValue]);
113
+
114
+ const { ArrowIcon, arrowIconSize } = getArrowIcon({ size, open });
115
+
116
+ const { buttons, inputKeyDownNavigationHandler, buttonsRefs } = useButtons({
117
+ readonly,
118
+ size,
119
+ showClearButton: showClearButton && !disabled && !readonly && value !== undefined,
120
+ showCopyButton,
121
+ inputRef: localRef,
122
+ onClear,
123
+ valueToCopy: selectedOptionFormatter(selectedItem),
124
+ });
125
+
126
+ const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
127
+ if (!open && !buttonsRefs.filter(Boolean).includes(e.relatedTarget)) {
104
128
  updateInputValue(selectedItem);
105
129
 
106
- // eslint-disable-next-line react-hooks/exhaustive-deps
107
- }, [selectedItem, prevSelectedItem]);
130
+ rest?.onBlur?.(e);
131
+ }
132
+ };
108
133
 
109
- const onClear = useCallback(() => {
110
- setValue(undefined);
111
- localRef.current?.focus();
112
- if (required) {
113
- setOpen(true);
114
- }
115
- }, [required, setOpen, setValue]);
116
-
117
- const { ArrowIcon, arrowIconSize } = getArrowIcon({ size, open });
118
-
119
- const { buttons, inputKeyDownNavigationHandler, buttonsRefs } = useButtons({
120
- readonly,
121
- size,
122
- showClearButton: showClearButton && !disabled && !readonly && value !== undefined,
123
- showCopyButton,
124
- inputRef: localRef,
125
- onClear,
126
- valueToCopy: selectedOptionFormatter(selectedItem),
127
- });
128
-
129
- const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
130
- if (!open && !buttonsRefs.filter(Boolean).includes(e.relatedTarget)) {
131
- updateInputValue(selectedItem);
134
+ const commonHandleOnKeyDown = useHandleOnKeyDown({
135
+ inputKeyDownNavigationHandler,
136
+ onInputKeyDownProp,
137
+ setOpen,
138
+ });
132
139
 
133
- rest?.onBlur?.(e);
134
- }
135
- };
136
-
137
- const commonHandleOnKeyDown = useHandleOnKeyDown({
138
- inputKeyDownNavigationHandler,
139
- onInputKeyDownProp,
140
- setOpen,
141
- });
142
-
143
- const handleSelectionChange = useCallback(
144
- (newValue?: SelectionSingleValueType) => {
145
- if (newValue !== undefined) {
146
- localRef.current?.focus();
147
-
148
- setOpen(false);
149
- setValue(newValue);
150
- }
151
- },
152
- [setOpen, setValue],
153
- );
154
-
155
- const handleOnKeyDown = (onKeyDown?: KeyboardEventHandler<HTMLElement>) => (e: KeyboardEvent<HTMLInputElement>) => {
156
- if (!open && prevInputValue.current !== inputValue) {
157
- setOpen(true);
158
- }
140
+ const handleSelectionChange = useCallback(
141
+ (newValue?: SelectionSingleValueType) => {
142
+ if (newValue !== undefined) {
143
+ localRef.current?.focus();
159
144
 
160
- if (e.code === 'Enter') {
161
- e.stopPropagation();
162
- e.preventDefault();
145
+ setOpen(false);
146
+ setValue(newValue);
163
147
  }
148
+ },
149
+ [setOpen, setValue],
150
+ );
164
151
 
165
- if (addOptionByEnter && e.code === 'Enter' && inputValue !== '') {
166
- handleSelectionChange(inputValue);
167
- }
152
+ const handleOnKeyDown = (onKeyDown?: KeyboardEventHandler<HTMLElement>) => (e: KeyboardEvent<HTMLInputElement>) => {
153
+ if (!open && prevInputValue.current !== inputValue) {
154
+ setOpen(true);
155
+ }
168
156
 
169
- commonHandleOnKeyDown(onKeyDown)(e);
170
- };
157
+ if (e.code === 'Enter') {
158
+ e.stopPropagation();
159
+ e.preventDefault();
160
+ }
171
161
 
172
- const handleOpenChange = (open: boolean) => {
173
- if (!readonly && !disabled && !buttonsRefs.includes(document.activeElement)) {
174
- setOpen(open);
162
+ if (addOptionByEnter && e.code === 'Enter' && inputValue !== '') {
163
+ handleSelectionChange(inputValue);
164
+ }
175
165
 
176
- if (!open) {
177
- updateInputValue(selectedItem);
178
- }
166
+ commonHandleOnKeyDown(onKeyDown)(e);
167
+ };
168
+
169
+ const handleOpenChange = (open: boolean) => {
170
+ if (!readonly && !disabled && !buttonsRefs.includes(document.activeElement)) {
171
+ setOpen(open);
172
+
173
+ if (!open) {
174
+ updateInputValue(selectedItem);
179
175
  }
180
- };
181
-
182
- const fuzzySearch = useFuzzySearch(items);
183
- const result =
184
- autocomplete || !searchable || selectedOptionFormatter(selectedItem) === inputValue
185
- ? items
186
- : fuzzySearch(inputValue);
187
-
188
- const fieldValidationState = getValidationState({ validationState, error: rest.error });
189
-
190
- return (
191
- <FieldDecorator
192
- {...extractSupportProps(rest)}
193
- {...extractFieldDecoratorProps(rest)}
194
- validationState={fieldValidationState}
195
- required={required}
196
- readonly={readonly}
197
- labelFor={id}
198
- disabled={disabled}
176
+ }
177
+ };
178
+
179
+ const fuzzySearch = useFuzzySearch(items);
180
+ const result =
181
+ autocomplete || !searchable || selectedOptionFormatter(selectedItem) === inputValue
182
+ ? items
183
+ : fuzzySearch(inputValue);
184
+
185
+ const fieldValidationState = getValidationState({ validationState, error: rest.error });
186
+
187
+ return (
188
+ <FieldDecorator
189
+ {...extractSupportProps(rest)}
190
+ {...extractFieldDecoratorProps(props)}
191
+ validationState={fieldValidationState}
192
+ >
193
+ <Droplist
194
+ {...extractListProps(props)}
195
+ items={result}
196
+ selection={{
197
+ mode: 'single',
198
+ value: value,
199
+ onChange: handleSelectionChange,
200
+ }}
199
201
  size={size}
202
+ open={open}
203
+ onOpenChange={handleOpenChange}
204
+ trigger='click'
205
+ triggerElemRef={localRef}
200
206
  >
201
- <Droplist
202
- {...extractListProps(rest)}
203
- items={result}
204
- selection={{
205
- mode: 'single',
206
- value: value,
207
- onChange: handleSelectionChange,
208
- }}
209
- size={size}
210
- open={open}
211
- onOpenChange={handleOpenChange}
212
- trigger='click'
213
- triggerElemRef={localRef}
214
- >
215
- {({ onKeyDown }) => (
216
- <FieldContainerPrivate
217
- className={styles.container}
218
- validationState={fieldValidationState}
207
+ {({ onKeyDown }) => (
208
+ <FieldContainerPrivate
209
+ className={styles.container}
210
+ validationState={fieldValidationState}
211
+ disabled={disabled}
212
+ readonly={readonly}
213
+ focused={open}
214
+ variant={'single-line-container'}
215
+ inputRef={localRef}
216
+ size={size}
217
+ prefix={prefixIcon}
218
+ >
219
+ <InputPrivate
220
+ id={id}
221
+ name={name}
222
+ type='text'
219
223
  disabled={disabled}
224
+ placeholder={placeholder}
225
+ ref={mergeRefs(ref, localRef)}
226
+ onChange={searchable ? setInputValue : undefined}
227
+ value={searchable ? inputValue : selectedOptionFormatter(selectedItem)}
220
228
  readonly={readonly}
221
- focused={open}
222
- variant={'single-line-container'}
223
- inputRef={localRef}
224
- size={size}
225
- prefix={prefixIcon}
226
- >
227
- <InputPrivate
228
- id={id}
229
- name={name}
230
- type='text'
231
- disabled={disabled}
232
- placeholder={placeholder}
233
- ref={mergeRefs(ref, localRef)}
234
- onChange={searchable ? setInputValue : undefined}
235
- value={searchable ? inputValue : selectedOptionFormatter(selectedItem)}
236
- readonly={readonly}
237
- data-test-id='field-select__input'
238
- onKeyDown={handleOnKeyDown(onKeyDown)}
239
- onBlur={handleBlur}
240
- className={cn({
241
- [styles.readonlyCursor]: !searchable,
242
- })}
243
- />
244
-
245
- <div className={styles.postfix}>
246
- {buttons}
247
- <ArrowIcon size={arrowIconSize} className={styles.arrowIcon} />
248
- </div>
249
- </FieldContainerPrivate>
250
- )}
251
- </Droplist>
252
- </FieldDecorator>
253
- );
254
- },
255
- );
229
+ data-test-id='field-select__input'
230
+ onKeyDown={handleOnKeyDown(onKeyDown)}
231
+ onBlur={handleBlur}
232
+ className={cn({
233
+ [styles.readonlyCursor]: !searchable,
234
+ })}
235
+ />
236
+
237
+ <div className={styles.postfix}>
238
+ {buttons}
239
+ <ArrowIcon size={arrowIconSize} className={styles.arrowIcon} />
240
+ </div>
241
+ </FieldContainerPrivate>
242
+ )}
243
+ </Droplist>
244
+ </FieldDecorator>
245
+ );
246
+ });