@synerise/ds-select 1.3.33 → 1.4.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/CHANGELOG.md +25 -0
- package/CLAUDE.md +175 -0
- package/README.md +163 -99
- package/dist/Option.d.ts +28 -0
- package/dist/Option.js +5 -0
- package/dist/Select.d.ts +82 -17
- package/dist/Select.js +386 -64
- package/dist/Select.styles.d.ts +39 -11
- package/dist/Select.styles.js +92 -65
- package/dist/Select.types.d.ts +137 -11
- package/dist/components/OptionList.d.ts +23 -0
- package/dist/components/OptionList.js +32 -0
- package/dist/components/SelectorContent.d.ts +34 -0
- package/dist/components/SelectorContent.js +72 -0
- package/dist/hooks/useSelectOptions.d.ts +28 -0
- package/dist/hooks/useSelectOptions.js +41 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +6 -1
- package/dist/utils/getOptionsFromChildren.d.ts +11 -0
- package/dist/utils/getOptionsFromChildren.js +41 -0
- package/dist/utils/helpers.d.ts +9 -0
- package/dist/utils/helpers.js +18 -0
- package/package.json +11 -7
- package/dist/assets/style/index-tn0RQdqM.css +0 -0
- package/dist/style/index.css +0 -1
package/dist/Select.d.ts
CHANGED
|
@@ -1,21 +1,86 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { default as React, FocusEvent, KeyboardEvent, ReactElement, ReactNode } from 'react';
|
|
2
|
+
import { Option } from './Option';
|
|
3
|
+
import { RawValueType, SelectOption, SelectValue } from './Select.types';
|
|
4
|
+
/**
|
|
5
|
+
* DS-native Select (antd-free). Single-select, `mode="multiple"` (chip selector),
|
|
6
|
+
* `mode="tags"` (free-text), in-selector `showSearch` with client `filterOption` /
|
|
7
|
+
* `optionFilterProp` and remote `onSearch` (`filterOption={false}`). Built on
|
|
8
|
+
* `@synerise/ds-dropdown` (floating-ui) + `ds-list-item`. Full keyboard support
|
|
9
|
+
* (Arrow/Home/End/Enter/Escape/Space, Backspace to drop the last chip) and
|
|
10
|
+
* combobox/listbox ARIA (`aria-activedescendant`) are wired in.
|
|
11
|
+
*/
|
|
12
|
+
declare const SelectInner: React.ForwardRefExoticComponent<{
|
|
13
|
+
[dataAttr: `data-${string}`]: unknown;
|
|
14
|
+
value?: SelectValue;
|
|
15
|
+
defaultValue?: SelectValue;
|
|
16
|
+
onChange?: ((value: SelectValue, option?: SelectOption | SelectOption[]) => void) | undefined;
|
|
17
|
+
onSelect?: (value: RawValueType, option?: SelectOption) => void;
|
|
18
|
+
onDeselect?: (value: RawValueType, option?: SelectOption) => void;
|
|
19
|
+
onBlur?: (event: FocusEvent<HTMLElement>) => void;
|
|
20
|
+
onFocus?: (event: FocusEvent<HTMLElement>) => void;
|
|
21
|
+
onDropdownVisibleChange?: (open: boolean) => void;
|
|
22
|
+
onSearch?: (value: string) => void;
|
|
23
|
+
onPopupScroll?: (event: React.UIEvent<HTMLDivElement>) => void;
|
|
24
|
+
onClear?: () => void;
|
|
25
|
+
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
26
|
+
onInputKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
|
|
27
|
+
onKeyDown?: (event: KeyboardEvent<HTMLElement>) => void;
|
|
28
|
+
mode?: import('./Select.types').SelectMode;
|
|
29
|
+
options?: SelectOption[];
|
|
30
|
+
showSearch?: boolean;
|
|
31
|
+
searchValue?: string;
|
|
32
|
+
maxLength?: number;
|
|
33
|
+
filterOption?: boolean | import('./Select.types').FilterOptionFn;
|
|
34
|
+
optionFilterProp?: string;
|
|
35
|
+
optionLabelProp?: string;
|
|
36
|
+
placeholder?: ReactNode;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
loading?: boolean;
|
|
39
|
+
allowClear?: boolean;
|
|
40
|
+
autoFocus?: boolean;
|
|
41
|
+
defaultActiveFirstOption?: boolean;
|
|
42
|
+
showArrow?: boolean;
|
|
43
|
+
suffixIcon?: ReactNode;
|
|
44
|
+
tabIndex?: number;
|
|
45
|
+
maxTagCount?: number;
|
|
46
|
+
maxTagTextLength?: number;
|
|
47
|
+
maxTagPlaceholder?: ReactNode | ((omittedValues: Array<{
|
|
48
|
+
value: RawValueType;
|
|
49
|
+
label: ReactNode;
|
|
50
|
+
}>) => ReactNode);
|
|
51
|
+
tokenSeparators?: string[];
|
|
52
|
+
open?: boolean;
|
|
53
|
+
defaultOpen?: boolean;
|
|
54
|
+
getPopupContainer?: (trigger: HTMLElement) => HTMLElement | ParentNode | null;
|
|
55
|
+
placement?: import('@synerise/ds-dropdown').DropdownPlacement;
|
|
56
|
+
dropdownClassName?: string;
|
|
57
|
+
popupClassName?: string;
|
|
58
|
+
dropdownAlign?: Record<string, unknown>;
|
|
59
|
+
dropdownStyle?: React.CSSProperties;
|
|
60
|
+
dropdownMatchSelectWidth?: boolean | number;
|
|
61
|
+
dropdownRender?: (menu: ReactElement) => ReactNode;
|
|
62
|
+
listHeight?: number | string;
|
|
63
|
+
listItemHeight?: number;
|
|
64
|
+
notFoundContent?: ReactNode;
|
|
65
|
+
clearIcon?: ReactNode;
|
|
66
|
+
rowKey?: (option: SelectOption) => React.Key;
|
|
67
|
+
prefixel?: ReactNode;
|
|
68
|
+
suffixel?: ReactNode;
|
|
9
69
|
grey?: boolean;
|
|
10
|
-
asFormElement?: boolean;
|
|
11
|
-
selectorStyle?: import('styled-components').CSSObject;
|
|
12
70
|
raw?: boolean;
|
|
13
71
|
readOnly?: boolean;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
72
|
+
asFormElement?: boolean;
|
|
73
|
+
selectorStyle?: import('styled-components').CSSObject;
|
|
74
|
+
clearTooltip?: string;
|
|
75
|
+
size?: "middle" | "large" | "default";
|
|
76
|
+
error?: boolean;
|
|
77
|
+
id?: string;
|
|
78
|
+
className?: string;
|
|
79
|
+
style?: React.CSSProperties;
|
|
80
|
+
children?: ReactNode;
|
|
81
|
+
} & Omit<import('@synerise/ds-form-field').ContentAboveProps, "id" | "rightSide"> & import('@synerise/ds-form-field').ContentBelowProps & React.AriaAttributes & React.RefAttributes<HTMLDivElement>>;
|
|
82
|
+
type SelectComponent = typeof SelectInner & {
|
|
83
|
+
Option: typeof Option;
|
|
19
84
|
};
|
|
20
|
-
declare const
|
|
21
|
-
export default
|
|
85
|
+
declare const Select: SelectComponent;
|
|
86
|
+
export default Select;
|
package/dist/Select.js
CHANGED
|
@@ -1,74 +1,396 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { forwardRef } from "react";
|
|
2
|
+
import { forwardRef, useState, useRef, useId, useEffect } from "react";
|
|
3
|
+
import Dropdown from "@synerise/ds-dropdown";
|
|
5
4
|
import FormField from "@synerise/ds-form-field";
|
|
6
|
-
import Icon, {
|
|
5
|
+
import Icon, { AngleDownS, Close3M } from "@synerise/ds-icon";
|
|
7
6
|
import Tooltip from "@synerise/ds-tooltip";
|
|
8
7
|
import { getPopupContainer } from "@synerise/ds-utils";
|
|
9
|
-
import {
|
|
10
|
-
import "./
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
8
|
+
import { Option } from "./Option.js";
|
|
9
|
+
import { SelectWrapper, PrefixWrapper, Selector, Arrow, ClearWrapper, SuffixWrapper, SelectContainer } from "./Select.styles.js";
|
|
10
|
+
import { OptionList } from "./components/OptionList.js";
|
|
11
|
+
import { SelectorContent } from "./components/SelectorContent.js";
|
|
12
|
+
import { useSelectOptions } from "./hooks/useSelectOptions.js";
|
|
13
|
+
import { findOption } from "./utils/getOptionsFromChildren.js";
|
|
14
|
+
import { DEFAULT_LIST_HEIGHT, cx, toArray } from "./utils/helpers.js";
|
|
15
|
+
const SelectInner = forwardRef((props, ref) => {
|
|
16
|
+
const {
|
|
17
|
+
value,
|
|
18
|
+
defaultValue,
|
|
19
|
+
onChange,
|
|
20
|
+
onSelect,
|
|
21
|
+
onDeselect,
|
|
22
|
+
onBlur,
|
|
23
|
+
onFocus,
|
|
24
|
+
onDropdownVisibleChange,
|
|
25
|
+
onSearch,
|
|
26
|
+
onPopupScroll,
|
|
27
|
+
onClear,
|
|
28
|
+
onClick,
|
|
29
|
+
onInputKeyDown,
|
|
30
|
+
onKeyDown,
|
|
31
|
+
mode,
|
|
32
|
+
options,
|
|
33
|
+
children,
|
|
34
|
+
placeholder,
|
|
35
|
+
disabled,
|
|
36
|
+
readOnly,
|
|
37
|
+
loading,
|
|
38
|
+
allowClear,
|
|
39
|
+
showSearch,
|
|
40
|
+
searchValue,
|
|
41
|
+
maxLength,
|
|
42
|
+
filterOption,
|
|
43
|
+
optionFilterProp,
|
|
44
|
+
optionLabelProp,
|
|
45
|
+
tokenSeparators,
|
|
46
|
+
maxTagCount,
|
|
47
|
+
maxTagTextLength,
|
|
48
|
+
maxTagPlaceholder,
|
|
49
|
+
showArrow = true,
|
|
50
|
+
suffixIcon,
|
|
51
|
+
tabIndex,
|
|
52
|
+
autoFocus,
|
|
53
|
+
open: openProp,
|
|
54
|
+
defaultOpen,
|
|
55
|
+
getPopupContainer: getPopupContainer$1 = getPopupContainer,
|
|
56
|
+
placement,
|
|
57
|
+
dropdownClassName,
|
|
58
|
+
popupClassName,
|
|
59
|
+
dropdownStyle,
|
|
60
|
+
dropdownMatchSelectWidth = true,
|
|
61
|
+
dropdownRender,
|
|
62
|
+
listHeight = DEFAULT_LIST_HEIGHT,
|
|
63
|
+
notFoundContent = "No data",
|
|
64
|
+
clearIcon,
|
|
65
|
+
clearTooltip,
|
|
66
|
+
prefixel,
|
|
67
|
+
suffixel,
|
|
68
|
+
grey,
|
|
69
|
+
raw,
|
|
70
|
+
asFormElement,
|
|
71
|
+
selectorStyle,
|
|
72
|
+
size,
|
|
73
|
+
error,
|
|
74
|
+
label,
|
|
75
|
+
description,
|
|
76
|
+
errorText,
|
|
77
|
+
tooltip,
|
|
78
|
+
tooltipConfig,
|
|
79
|
+
className,
|
|
80
|
+
style,
|
|
81
|
+
id,
|
|
82
|
+
rowKey
|
|
83
|
+
} = props;
|
|
84
|
+
const isTags = mode === "tags";
|
|
85
|
+
const isMultiple = mode === "multiple" || isTags;
|
|
86
|
+
const isDisabled = Boolean(disabled || readOnly);
|
|
87
|
+
const hasInput = Boolean(showSearch || isMultiple);
|
|
88
|
+
const passthroughAttrs = Object.fromEntries(Object.entries(props).filter(([key]) => key.startsWith("data-") || key.startsWith("aria-")));
|
|
89
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
90
|
+
const currentValue = value !== void 0 ? value : internalValue;
|
|
91
|
+
const selectedValues = toArray(currentValue);
|
|
92
|
+
const [internalOpen, setInternalOpen] = useState(!!defaultOpen);
|
|
93
|
+
const isControlledOpen = openProp !== void 0;
|
|
94
|
+
const isOpen = isControlledOpen ? !!openProp : internalOpen;
|
|
95
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
96
|
+
const isSearchControlled = searchValue !== void 0;
|
|
97
|
+
const effectiveQuery = isSearchControlled ? searchValue : searchQuery;
|
|
98
|
+
const inputRef = useRef(null);
|
|
99
|
+
const selectorRef = useRef(null);
|
|
100
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
101
|
+
const baseId = useId();
|
|
102
|
+
const listboxId = `${baseId}-listbox`;
|
|
103
|
+
const optionDomId = (index) => `${baseId}-option-${index}`;
|
|
104
|
+
const {
|
|
105
|
+
resolvedOptions,
|
|
106
|
+
displayedOptions
|
|
107
|
+
} = useSelectOptions({
|
|
108
|
+
options,
|
|
109
|
+
children,
|
|
110
|
+
filterOption,
|
|
111
|
+
optionFilterProp,
|
|
112
|
+
effectiveQuery,
|
|
113
|
+
isTags,
|
|
114
|
+
selectedValues
|
|
115
|
+
});
|
|
116
|
+
const firstEnabledIndex = () => displayedOptions.findIndex((option) => !option.disabled);
|
|
117
|
+
const lastEnabledIndex = () => {
|
|
118
|
+
for (let i = displayedOptions.length - 1; i >= 0; i -= 1) {
|
|
119
|
+
if (!displayedOptions[i].disabled) {
|
|
120
|
+
return i;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return -1;
|
|
124
|
+
};
|
|
125
|
+
const moveActive = (direction) => {
|
|
126
|
+
const count = displayedOptions.length;
|
|
127
|
+
if (count === 0) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
setActiveIndex((current) => {
|
|
131
|
+
let next = current;
|
|
132
|
+
for (let step = 0; step < count; step += 1) {
|
|
133
|
+
next = (next + direction + count) % count;
|
|
134
|
+
if (!displayedOptions[next].disabled) {
|
|
135
|
+
return next;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return current;
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (!isOpen) {
|
|
143
|
+
setActiveIndex(-1);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
setActiveIndex((current) => {
|
|
147
|
+
if (current >= 0 && current < displayedOptions.length && !displayedOptions[current].disabled) {
|
|
148
|
+
return current;
|
|
149
|
+
}
|
|
150
|
+
const selected = displayedOptions.findIndex((option) => !option.disabled && selectedValues.includes(option.value));
|
|
151
|
+
return selected >= 0 ? selected : firstEnabledIndex();
|
|
152
|
+
});
|
|
153
|
+
}, [isOpen]);
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
if (!isOpen) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
setActiveIndex(firstEnabledIndex());
|
|
159
|
+
}, [effectiveQuery]);
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (!isOpen || activeIndex < 0) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const activeEl = document.getElementById(optionDomId(activeIndex));
|
|
165
|
+
activeEl?.scrollIntoView?.({
|
|
166
|
+
block: "nearest"
|
|
167
|
+
});
|
|
168
|
+
}, [activeIndex, isOpen]);
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
if (autoFocus && !hasInput) {
|
|
171
|
+
selectorRef.current?.focus();
|
|
172
|
+
}
|
|
173
|
+
}, []);
|
|
174
|
+
const $size = size === "large" ? "large" : "default";
|
|
175
|
+
const labelFor = (v) => {
|
|
176
|
+
const option = findOption(resolvedOptions, v);
|
|
177
|
+
if (option && optionLabelProp === "value") {
|
|
178
|
+
return option.value;
|
|
179
|
+
}
|
|
180
|
+
return option?.label ?? v;
|
|
181
|
+
};
|
|
182
|
+
const setOpen = (next) => {
|
|
183
|
+
if (isDisabled) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (!isControlledOpen) {
|
|
187
|
+
setInternalOpen(next);
|
|
188
|
+
}
|
|
189
|
+
onDropdownVisibleChange?.(next);
|
|
190
|
+
};
|
|
191
|
+
const commit = (next) => {
|
|
192
|
+
if (value === void 0) {
|
|
193
|
+
setInternalValue(next);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
const clearQuery = () => {
|
|
197
|
+
if (effectiveQuery) {
|
|
198
|
+
if (!isSearchControlled) {
|
|
199
|
+
setSearchQuery("");
|
|
61
200
|
}
|
|
62
|
-
|
|
201
|
+
onSearch?.("");
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const addValue = (optionValue, option) => {
|
|
205
|
+
const next = [...selectedValues, optionValue];
|
|
206
|
+
commit(next);
|
|
207
|
+
onChange?.(next, next.map((v) => findOption(resolvedOptions, v) ?? {
|
|
208
|
+
value: v,
|
|
209
|
+
key: v
|
|
210
|
+
}));
|
|
211
|
+
onSelect?.(optionValue, option);
|
|
212
|
+
clearQuery();
|
|
213
|
+
};
|
|
214
|
+
const removeValue = (optionValue) => {
|
|
215
|
+
const next = selectedValues.filter((v) => v !== optionValue);
|
|
216
|
+
commit(next);
|
|
217
|
+
onChange?.(next, next.map((v) => findOption(resolvedOptions, v) ?? {
|
|
218
|
+
value: v,
|
|
219
|
+
key: v
|
|
220
|
+
}));
|
|
221
|
+
onDeselect?.(optionValue, findOption(resolvedOptions, optionValue));
|
|
222
|
+
};
|
|
223
|
+
const handleSelect = (option) => {
|
|
224
|
+
if (option.disabled) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (isMultiple) {
|
|
228
|
+
if (selectedValues.includes(option.value)) {
|
|
229
|
+
removeValue(option.value);
|
|
230
|
+
} else {
|
|
231
|
+
addValue(option.value, option);
|
|
232
|
+
}
|
|
233
|
+
inputRef.current?.focus();
|
|
234
|
+
} else {
|
|
235
|
+
commit(option.value);
|
|
236
|
+
onChange?.(option.value, option);
|
|
237
|
+
onSelect?.(option.value, option);
|
|
238
|
+
clearQuery();
|
|
239
|
+
setOpen(false);
|
|
240
|
+
inputRef.current?.blur();
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
const handleClear = (event) => {
|
|
244
|
+
event.stopPropagation();
|
|
245
|
+
const cleared = isMultiple ? [] : void 0;
|
|
246
|
+
commit(cleared);
|
|
247
|
+
onChange?.(cleared, isMultiple ? [] : void 0);
|
|
248
|
+
clearQuery();
|
|
249
|
+
onClear?.();
|
|
250
|
+
};
|
|
251
|
+
const handleSearchChange = (event) => {
|
|
252
|
+
const next = event.currentTarget.value;
|
|
253
|
+
if (!isSearchControlled) {
|
|
254
|
+
setSearchQuery(next);
|
|
255
|
+
}
|
|
256
|
+
onSearch?.(next);
|
|
257
|
+
if (!isOpen) {
|
|
258
|
+
setOpen(true);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
const commitActiveOrTag = () => {
|
|
262
|
+
const query = effectiveQuery.trim();
|
|
263
|
+
if (isOpen && activeIndex >= 0 && displayedOptions[activeIndex]) {
|
|
264
|
+
handleSelect(displayedOptions[activeIndex]);
|
|
265
|
+
} else if (isTags && query) {
|
|
266
|
+
if (selectedValues.some((v) => String(v) === query)) {
|
|
267
|
+
clearQuery();
|
|
268
|
+
} else {
|
|
269
|
+
addValue(query);
|
|
270
|
+
}
|
|
271
|
+
} else if (!isOpen) {
|
|
272
|
+
setOpen(true);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const handleKeyDown = (event) => {
|
|
276
|
+
if (isDisabled) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const query = effectiveQuery.trim();
|
|
280
|
+
switch (event.key) {
|
|
281
|
+
case "ArrowDown":
|
|
282
|
+
event.preventDefault();
|
|
283
|
+
if (!isOpen) {
|
|
284
|
+
setOpen(true);
|
|
285
|
+
} else {
|
|
286
|
+
moveActive(1);
|
|
287
|
+
}
|
|
288
|
+
return;
|
|
289
|
+
case "ArrowUp":
|
|
290
|
+
event.preventDefault();
|
|
291
|
+
if (!isOpen) {
|
|
292
|
+
setOpen(true);
|
|
293
|
+
} else {
|
|
294
|
+
moveActive(-1);
|
|
295
|
+
}
|
|
296
|
+
return;
|
|
297
|
+
case "Home":
|
|
298
|
+
if (isOpen) {
|
|
299
|
+
event.preventDefault();
|
|
300
|
+
setActiveIndex(firstEnabledIndex());
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
303
|
+
case "End":
|
|
304
|
+
if (isOpen) {
|
|
305
|
+
event.preventDefault();
|
|
306
|
+
setActiveIndex(lastEnabledIndex());
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
case "Escape":
|
|
310
|
+
if (isOpen) {
|
|
311
|
+
event.preventDefault();
|
|
312
|
+
setOpen(false);
|
|
313
|
+
clearQuery();
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
316
|
+
case "Tab":
|
|
317
|
+
if (isOpen) {
|
|
318
|
+
setOpen(false);
|
|
319
|
+
}
|
|
320
|
+
return;
|
|
321
|
+
case "Enter":
|
|
322
|
+
event.preventDefault();
|
|
323
|
+
commitActiveOrTag();
|
|
324
|
+
return;
|
|
325
|
+
case " ":
|
|
326
|
+
if (!hasInput) {
|
|
327
|
+
event.preventDefault();
|
|
328
|
+
if (!isOpen) {
|
|
329
|
+
setOpen(true);
|
|
330
|
+
} else if (activeIndex >= 0 && displayedOptions[activeIndex]) {
|
|
331
|
+
handleSelect(displayedOptions[activeIndex]);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return;
|
|
335
|
+
case "Backspace":
|
|
336
|
+
if (!effectiveQuery && isMultiple && selectedValues.length > 0) {
|
|
337
|
+
removeValue(selectedValues[selectedValues.length - 1]);
|
|
338
|
+
}
|
|
339
|
+
return;
|
|
340
|
+
default:
|
|
341
|
+
if (isTags && query && (tokenSeparators ?? []).includes(event.key)) {
|
|
342
|
+
event.preventDefault();
|
|
343
|
+
if (selectedValues.some((v) => String(v) === query)) {
|
|
344
|
+
clearQuery();
|
|
345
|
+
} else {
|
|
346
|
+
addValue(query);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
const handleRootFocus = (event) => {
|
|
352
|
+
if (event.relatedTarget && event.currentTarget.contains(event.relatedTarget)) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
onFocus?.(event);
|
|
356
|
+
};
|
|
357
|
+
const handleRootBlur = (event) => {
|
|
358
|
+
if (event.relatedTarget && event.currentTarget.contains(event.relatedTarget)) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
onBlur?.(event);
|
|
362
|
+
};
|
|
363
|
+
const hasValue = selectedValues.length > 0;
|
|
364
|
+
const showClear = allowClear && hasValue && !isDisabled;
|
|
365
|
+
const placeholderStr = typeof placeholder === "string" ? placeholder : void 0;
|
|
366
|
+
const menu = /* @__PURE__ */ jsx(OptionList, { loading, options: displayedOptions, notFoundContent, listHeight, isMultiple, listboxId, selectedValues, activeIndex, rowKey, optionDomId, onOptionActivate: setActiveIndex, onOptionSelect: handleSelect, onPopupScroll });
|
|
367
|
+
const handleSearchKeyDown = (event) => {
|
|
368
|
+
onInputKeyDown?.(event);
|
|
369
|
+
onKeyDown?.(event);
|
|
370
|
+
handleKeyDown(event);
|
|
371
|
+
};
|
|
372
|
+
const selectorContent = /* @__PURE__ */ jsx(SelectorContent, { isMultiple, showSearch, hasValue, placeholder, placeholderStr, selectedValues, effectiveQuery, labelFor, onRemoveValue: removeValue, maxTagCount, maxTagTextLength, maxTagPlaceholder, inputRef, isDisabled, hasInput, autoFocus, maxLength, id, isOpen, listboxId, activeIndex, optionDomId, onSearchChange: handleSearchChange, onSearchKeyDown: handleSearchKeyDown });
|
|
373
|
+
const selector = /* @__PURE__ */ jsxs(SelectWrapper, { className: "ds-select-wrapper", style, ref: raw ? ref : void 0, onClick, onFocus: handleRootFocus, onBlur: handleRootBlur, ...passthroughAttrs, children: [
|
|
374
|
+
!!prefixel && /* @__PURE__ */ jsx(PrefixWrapper, { children: prefixel }),
|
|
375
|
+
/* @__PURE__ */ jsx(Dropdown, { open: isOpen, onOpenChange: setOpen, disabled: isDisabled, placement, size: typeof dropdownMatchSelectWidth === "number" ? dropdownMatchSelectWidth : dropdownMatchSelectWidth ? "match-trigger" : "min-match-trigger", getPopupContainer: getPopupContainer$1, hideOnItemClick: false, overlayClassName: cx("ds-select-dropdown", "ps__child--consume", dropdownClassName, popupClassName), overlayStyle: dropdownStyle, asChild: true, overlay: dropdownRender ? dropdownRender(menu) : menu, children: /* @__PURE__ */ jsxs(Selector, { ref: selectorRef, className: cx("ds-select", isMultiple && "ds-select-multiple", isOpen && "ds-select-open", isDisabled && "ds-select-disabled", error && "error"), $size, $open: isOpen, $error: Boolean(errorText || error), $disabled: isDisabled, $readOnly: readOnly, $grey: grey, $multiple: isMultiple, $clearable: showClear, $withPrefixel: !!prefixel, $withSuffixel: !!suffixel, $selectorStyle: selectorStyle, "aria-disabled": isDisabled, tabIndex: tabIndex ?? (hasInput || isDisabled ? void 0 : 0), onKeyDown: hasInput ? void 0 : (event) => {
|
|
376
|
+
onKeyDown?.(event);
|
|
377
|
+
handleKeyDown(event);
|
|
378
|
+
}, role: hasInput ? void 0 : "combobox", "aria-expanded": hasInput ? void 0 : isOpen, "aria-haspopup": hasInput ? void 0 : "listbox", "aria-controls": !hasInput && isOpen ? listboxId : void 0, "aria-activedescendant": !hasInput && isOpen && activeIndex >= 0 ? optionDomId(activeIndex) : void 0, children: [
|
|
379
|
+
selectorContent,
|
|
380
|
+
suffixIcon ? /* @__PURE__ */ jsx(Arrow, { className: "ds-select-arrow", children: suffixIcon }) : showArrow && /* @__PURE__ */ jsx(Arrow, { className: "ds-select-arrow", $open: isOpen, children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(AngleDownS, {}) }) }),
|
|
381
|
+
showClear && /* @__PURE__ */ jsx(ClearWrapper, { className: "ds-select-clear", onClick: handleClear, "data-testid": "select-clear", children: /* @__PURE__ */ jsx(Tooltip, { title: clearTooltip, children: /* @__PURE__ */ jsx("span", { children: clearIcon ?? /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(Close3M, {}), size: 24 }) }) }) })
|
|
382
|
+
] }) }),
|
|
63
383
|
!!suffixel && /* @__PURE__ */ jsx(SuffixWrapper, { children: suffixel })
|
|
64
384
|
] });
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
385
|
+
if (raw) {
|
|
386
|
+
return selector;
|
|
387
|
+
}
|
|
388
|
+
const hasBottomMargin = Boolean(asFormElement || errorText || description);
|
|
389
|
+
return /* @__PURE__ */ jsx(SelectContainer, { className: cx("ds-select-container", className), hasBottomMargin, ref, children: /* @__PURE__ */ jsx(FormField, { errorText, description, label, tooltip, tooltipConfig, children: selector }) });
|
|
70
390
|
});
|
|
391
|
+
SelectInner.displayName = "Select";
|
|
392
|
+
const Select = SelectInner;
|
|
393
|
+
Select.Option = Option;
|
|
71
394
|
export {
|
|
72
|
-
Select
|
|
73
|
-
SelectWithComponents as default
|
|
395
|
+
Select as default
|
|
74
396
|
};
|
package/dist/Select.styles.d.ts
CHANGED
|
@@ -1,17 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { CSSObject } from 'styled-components';
|
|
2
|
+
import { StyledListItem } from '@synerise/ds-list-item';
|
|
3
|
+
type SelectorProps = {
|
|
4
|
+
$size?: string;
|
|
5
|
+
$open?: boolean;
|
|
6
|
+
$error?: boolean;
|
|
7
|
+
$disabled?: boolean;
|
|
8
|
+
$readOnly?: boolean;
|
|
9
|
+
$grey?: boolean;
|
|
10
|
+
$multiple?: boolean;
|
|
11
|
+
$clearable?: boolean;
|
|
12
|
+
$withPrefixel?: boolean;
|
|
13
|
+
$withSuffixel?: boolean;
|
|
14
|
+
$selectorStyle?: CSSObject;
|
|
15
|
+
};
|
|
3
16
|
export declare const SelectContainer: import('styled-components').StyledComponent<"div", any, {
|
|
4
17
|
hasBottomMargin?: boolean;
|
|
5
18
|
}, never>;
|
|
6
|
-
export declare const
|
|
7
|
-
size?: string;
|
|
8
|
-
withPrefixel?: boolean;
|
|
9
|
-
withSuffixel?: boolean;
|
|
10
|
-
readOnly?: boolean;
|
|
11
|
-
}, never>;
|
|
19
|
+
export declare const SelectWrapper: import('styled-components').StyledComponent<"div", any, {}, never>;
|
|
12
20
|
export declare const PrefixWrapper: import('styled-components').StyledComponent<"div", any, {}, never>;
|
|
13
21
|
export declare const SuffixWrapper: import('styled-components').StyledComponent<"div", any, {}, never>;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
/** The trigger box (the `.ds-select` selector). */
|
|
23
|
+
export declare const Selector: import('styled-components').StyledComponent<"div", any, SelectorProps, never>;
|
|
24
|
+
export declare const SelectionItem: import('styled-components').StyledComponent<"span", any, {}, never>;
|
|
25
|
+
export declare const Placeholder: import('styled-components').StyledComponent<"span", any, {}, never>;
|
|
26
|
+
export declare const Arrow: import('styled-components').StyledComponent<"span", any, {
|
|
27
|
+
$open?: boolean;
|
|
28
|
+
}, never>;
|
|
29
|
+
export declare const ClearWrapper: import('styled-components').StyledComponent<"span", any, {}, never>;
|
|
30
|
+
export declare const DropdownWrapper: import('styled-components').StyledComponent<"div", any, {}, never>;
|
|
31
|
+
export declare const ScrollList: import('styled-components').StyledComponent<({ children, onClick, maxToShowItems, texts, ...htmlAttributes }: import('@synerise/ds-list-item').ListWrapperProps) => React.JSX.Element, any, {}, never>;
|
|
32
|
+
export declare const Inner: import('styled-components').StyledComponent<"div", any, {}, never>;
|
|
33
|
+
export declare const NotFound: import('styled-components').StyledComponent<"div", any, {}, never>;
|
|
34
|
+
export declare const Loading: import('styled-components').StyledComponent<"div", any, {}, never>;
|
|
35
|
+
export declare const OptionItem: StyledListItem;
|
|
36
|
+
/** In-selector search input (showSearch / multiple / tags). */
|
|
37
|
+
export declare const SearchInputEl: import('styled-components').StyledComponent<"input", any, {
|
|
38
|
+
$overlay?: boolean;
|
|
17
39
|
}, never>;
|
|
40
|
+
/** Wraps chips + the search input for multiple/tags mode. */
|
|
41
|
+
export declare const MultiValueArea: import('styled-components').StyledComponent<"div", any, {}, never>;
|
|
42
|
+
export declare const Chip: import('styled-components').StyledComponent<"span", any, {}, never>;
|
|
43
|
+
export declare const ChipLabel: import('styled-components').StyledComponent<"span", any, {}, never>;
|
|
44
|
+
export declare const ChipRemove: import('styled-components').StyledComponent<"span", any, {}, never>;
|
|
45
|
+
export {};
|