@synerise/ds-select 1.3.34 → 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 +21 -0
- package/CLAUDE.md +127 -92
- 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 +6 -3
- package/dist/assets/style/index-tn0RQdqM.css +0 -0
- package/dist/style/index.css +0 -1
package/dist/Select.styles.js
CHANGED
|
@@ -1,85 +1,112 @@
|
|
|
1
|
-
import { Select } from "antd";
|
|
2
1
|
import styled, { css } from "styled-components";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
`;
|
|
8
|
-
const searchIconWithCustomColor = (color) => {
|
|
9
|
-
const colorValueForSvg = color.replace(/#/, "%23");
|
|
10
|
-
const iconWithColor = `data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-6 -6 36 36' >/><path fill='none' d='M0 0h24v24H0z' /><path style='fill: ${colorValueForSvg};' d='M10.734 17.234a6.463 6.463 0 004.03-1.41l3.721 3.722a.75.75 0 001.06-1.06l-3.72-3.722a6.494 6.494 0 10-5.09 2.47zm0-11.5a5 5 0 11-5 5 5.006 5.006 0 015-5z'/></svg>`;
|
|
11
|
-
return iconWithColor;
|
|
2
|
+
import DSListItem, { ListWrapper } from "@synerise/ds-list-item";
|
|
3
|
+
const HEIGHT = {
|
|
4
|
+
large: 48,
|
|
5
|
+
default: 32
|
|
12
6
|
};
|
|
13
|
-
const
|
|
14
|
-
border-top-left-radius:0;
|
|
15
|
-
border-bottom-left-radius:0;
|
|
16
|
-
`;
|
|
17
|
-
const withSuffixStyles = () => `
|
|
18
|
-
border-top-right-radius:0;
|
|
19
|
-
border-bottom-right-radius:0;
|
|
20
|
-
`;
|
|
21
|
-
const addonStyles = (props) => `
|
|
22
|
-
display: flex;
|
|
23
|
-
align-items: center;
|
|
24
|
-
background: ${props.theme.palette["grey-050"]};
|
|
25
|
-
box-shadow: inset 0 0 0 1px ${props.theme.palette["grey-300"]};
|
|
26
|
-
color: ${props.theme.palette["grey-500"]};
|
|
27
|
-
font-size:13px;
|
|
28
|
-
line-height: 1.39;
|
|
29
|
-
`;
|
|
7
|
+
const addonStyles = /* @__PURE__ */ css(["display:flex;align-items:center;background:", ";box-shadow:inset 0 0 0 1px ", ";color:", ";font-size:13px;line-height:1.39;"], (props) => props.theme.palette["grey-050"], (props) => props.theme.palette["grey-300"], (props) => props.theme.palette["grey-500"]);
|
|
30
8
|
const SelectContainer = /* @__PURE__ */ styled.div.withConfig({
|
|
31
9
|
displayName: "Selectstyles__SelectContainer",
|
|
32
10
|
componentId: "sc-n9lk0v-0"
|
|
33
11
|
})(["display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;margin:0 0 ", "px;"], (props) => props.hasBottomMargin ? 16 : 0);
|
|
34
|
-
const
|
|
35
|
-
displayName: "
|
|
12
|
+
const SelectWrapper = /* @__PURE__ */ styled.div.withConfig({
|
|
13
|
+
displayName: "Selectstyles__SelectWrapper",
|
|
36
14
|
componentId: "sc-n9lk0v-1"
|
|
37
|
-
})(["
|
|
38
|
-
|
|
39
|
-
&.ant-select-single .ant-select-selector,
|
|
40
|
-
&.ant-select-single .ant-select-selection-search-input {
|
|
41
|
-
height:48px;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
&.ant-select-single .ant-select-selection-item,
|
|
45
|
-
&.ant-select-single .ant-select-selection-placeholder {
|
|
46
|
-
line-height:46px;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
&.ant-select-multiple.ant-select-lg {
|
|
50
|
-
.ant-select-arrow {
|
|
51
|
-
top: 23px
|
|
52
|
-
}
|
|
53
|
-
.ant-select-selector {
|
|
54
|
-
padding: 8px;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.ant-select-selector::after,
|
|
58
|
-
.ant-select-selection-item,
|
|
59
|
-
.ant-select-selection-search,
|
|
60
|
-
.ant-select-selection-search-input {
|
|
61
|
-
height: 24px;
|
|
62
|
-
line-height: 22px;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
`, (props) => !!props.selectorStyle && css(props.selectorStyle), (props) => !!props.withPrefixel && withPrefixStyles(), (props) => !!props.withSuffixel && withSuffixStyles(), (props) => `url("${searchIconWithCustomColor(props.theme.palette["grey-400"])}")`, (props) => errorStyle(props), (props) => props.theme.palette["red-050"], (props) => props.readOnly ? props.theme.palette["grey-600"] : props.theme.palette["grey-400"], (props) => props.readOnly ? "default" : "not-allowed", (props) => props.readOnly ? props.theme.palette.white : props.theme.palette["grey-050"]);
|
|
15
|
+
})(["display:flex;width:100%;"]);
|
|
67
16
|
const PrefixWrapper = /* @__PURE__ */ styled.div.withConfig({
|
|
68
17
|
displayName: "Selectstyles__PrefixWrapper",
|
|
69
18
|
componentId: "sc-n9lk0v-2"
|
|
70
|
-
})(["border-radius:3px 0 0 3px;margin-right:-2px;padding-right:1px;", ";"],
|
|
19
|
+
})(["border-radius:3px 0 0 3px;margin-right:-2px;padding-right:1px;", ";"], addonStyles);
|
|
71
20
|
const SuffixWrapper = /* @__PURE__ */ styled.div.withConfig({
|
|
72
21
|
displayName: "Selectstyles__SuffixWrapper",
|
|
73
22
|
componentId: "sc-n9lk0v-3"
|
|
74
|
-
})(["border-radius:0 3px 3px 0;margin-left:-1px;", ";"],
|
|
75
|
-
const
|
|
76
|
-
displayName: "
|
|
23
|
+
})(["border-radius:0 3px 3px 0;margin-left:-1px;", ";"], addonStyles);
|
|
24
|
+
const Selector = /* @__PURE__ */ styled.div.withConfig({
|
|
25
|
+
displayName: "Selectstyles__Selector",
|
|
77
26
|
componentId: "sc-n9lk0v-4"
|
|
78
|
-
})(["display:flex;", "
|
|
27
|
+
})(["position:relative;display:flex;align-items:center;width:100%;min-width:0;box-sizing:border-box;", " border:1px solid ", ";border-radius:3px;background-color:", ";color:", ";font-size:13px;cursor:pointer;transition:all 0.3s;", " ", " ", " ", " ", " ", " ", " ", ""], (props) => {
|
|
28
|
+
const h = HEIGHT[props.$size ?? "default"] ?? HEIGHT.default;
|
|
29
|
+
return props.$multiple ? css(["min-height:", "px;height:auto;padding:3px 30px 3px 8px;flex-wrap:wrap;gap:4px;"], h) : css(["height:", "px;padding:0 30px 0 12px;"], h);
|
|
30
|
+
}, (props) => props.theme.palette["grey-300"], (props) => props.$grey ? props.theme.palette["grey-050"] : props.theme.palette.white, (props) => props.theme.palette["grey-700"], (props) => props.$withPrefixel && css(["border-top-left-radius:0;border-bottom-left-radius:0;"]), (props) => props.$withSuffixel && css(["border-top-right-radius:0;border-bottom-right-radius:0;"]), (props) => !props.$open && !props.$error && css(["&:hover{border-color:", ";}"], props.theme.palette["grey-400"]), (props) => props.$clearable && css(["&:hover .ds-select-arrow{display:none;}&:hover .ds-select-clear{display:flex;}"]), (props) => props.$open && css(["box-shadow:inset 0 0 0 1px ", ";border-color:", ";background-color:", ";"], props.theme.palette["blue-600"], props.theme.palette["blue-600"], props.theme.palette["blue-050"]), (props) => props.$error && css(["border-color:", ";box-shadow:inset 0 0 0 1px ", ";background:", ";"], props.theme.palette["red-600"], props.theme.palette["red-600"], props.theme.palette["red-050"]), (props) => props.$disabled && css(["cursor:", ";color:", ";background-color:", ";&:hover{border-color:", ";}"], props.$readOnly ? "default" : "not-allowed", props.$readOnly ? props.theme.palette["grey-600"] : props.theme.palette["grey-400"], props.$readOnly ? props.theme.palette.white : props.theme.palette["grey-050"], props.theme.palette["grey-300"]), (props) => !!props.$selectorStyle && css(props.$selectorStyle));
|
|
31
|
+
const SelectionItem = /* @__PURE__ */ styled.span.withConfig({
|
|
32
|
+
displayName: "Selectstyles__SelectionItem",
|
|
33
|
+
componentId: "sc-n9lk0v-5"
|
|
34
|
+
})(["flex:1;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;"]);
|
|
35
|
+
const Placeholder = /* @__PURE__ */ styled.span.withConfig({
|
|
36
|
+
displayName: "Selectstyles__Placeholder",
|
|
37
|
+
componentId: "sc-n9lk0v-6"
|
|
38
|
+
})(["flex:1;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:", ";"], (props) => props.theme.palette["grey-500"]);
|
|
39
|
+
const Arrow = /* @__PURE__ */ styled.span.withConfig({
|
|
40
|
+
displayName: "Selectstyles__Arrow",
|
|
41
|
+
componentId: "sc-n9lk0v-7"
|
|
42
|
+
})(["position:absolute;right:8px;top:50%;display:flex;align-items:center;transform:translateY(-50%) ", ";transition:transform 0.3s;color:", ";pointer-events:none;"], (props) => props.$open ? "rotate(180deg)" : "", (props) => props.theme.palette["grey-500"]);
|
|
43
|
+
const ClearWrapper = /* @__PURE__ */ styled.span.withConfig({
|
|
44
|
+
displayName: "Selectstyles__ClearWrapper",
|
|
45
|
+
componentId: "sc-n9lk0v-8"
|
|
46
|
+
})(["position:absolute;right:8px;top:50%;transform:translateY(-50%);display:none;align-items:center;cursor:pointer;color:", ";"], (props) => props.theme.palette["red-600"]);
|
|
47
|
+
const DropdownWrapper = /* @__PURE__ */ styled.div.withConfig({
|
|
48
|
+
displayName: "Selectstyles__DropdownWrapper",
|
|
49
|
+
componentId: "sc-n9lk0v-9"
|
|
50
|
+
})(["display:flex;flex-direction:column;align-items:stretch;min-width:120px;"]);
|
|
51
|
+
const ScrollList = /* @__PURE__ */ styled(ListWrapper).withConfig({
|
|
52
|
+
displayName: "Selectstyles__ScrollList",
|
|
53
|
+
componentId: "sc-n9lk0v-10"
|
|
54
|
+
})(["&&{padding-right:0;}"]);
|
|
55
|
+
const Inner = /* @__PURE__ */ styled.div.withConfig({
|
|
56
|
+
displayName: "Selectstyles__Inner",
|
|
57
|
+
componentId: "sc-n9lk0v-11"
|
|
58
|
+
})(["padding-right:8px;"]);
|
|
59
|
+
const NotFound = /* @__PURE__ */ styled.div.withConfig({
|
|
60
|
+
displayName: "Selectstyles__NotFound",
|
|
61
|
+
componentId: "sc-n9lk0v-12"
|
|
62
|
+
})(["display:flex;align-items:center;justify-content:center;padding:8px 12px;color:", ";font-weight:normal;"], (props) => props.theme.palette["grey-600"]);
|
|
63
|
+
const Loading = /* @__PURE__ */ styled.div.withConfig({
|
|
64
|
+
displayName: "Selectstyles__Loading",
|
|
65
|
+
componentId: "sc-n9lk0v-13"
|
|
66
|
+
})(["display:flex;align-items:center;justify-content:center;padding:16px;"]);
|
|
67
|
+
const OptionItem = /* @__PURE__ */ styled(DSListItem).withConfig({
|
|
68
|
+
displayName: "Selectstyles__OptionItem",
|
|
69
|
+
componentId: "sc-n9lk0v-14"
|
|
70
|
+
})(["min-width:auto;font-weight:normal;&&:hover:not(.ds-list-item-disabled),&&.ds-select-item-option-active:not(.ds-list-item-disabled){background-color:", ";}"], (props) => props.theme.palette["blue-050"]);
|
|
71
|
+
const SearchInputEl = /* @__PURE__ */ styled.input.withConfig({
|
|
72
|
+
displayName: "Selectstyles__SearchInputEl",
|
|
73
|
+
componentId: "sc-n9lk0v-15"
|
|
74
|
+
})(["flex:1 1 30px;min-width:30px;width:100%;border:none;outline:none;background:transparent;padding:0;margin:0;font-family:inherit;font-size:13px;color:", ";cursor:inherit;&::placeholder{color:", ";}&:disabled{cursor:not-allowed;}", ""], (props) => props.theme.palette["grey-700"], (props) => props.theme.palette["grey-500"], (props) => props.$overlay && css(["position:absolute;top:0;left:0;width:100%;height:100%;flex:none;box-sizing:border-box;padding:0 30px 0 12px;"]));
|
|
75
|
+
const MultiValueArea = /* @__PURE__ */ styled.div.withConfig({
|
|
76
|
+
displayName: "Selectstyles__MultiValueArea",
|
|
77
|
+
componentId: "sc-n9lk0v-16"
|
|
78
|
+
})(["display:flex;flex-wrap:wrap;align-items:center;gap:4px;flex:1;min-width:0;"]);
|
|
79
|
+
const Chip = /* @__PURE__ */ styled.span.withConfig({
|
|
80
|
+
displayName: "Selectstyles__Chip",
|
|
81
|
+
componentId: "sc-n9lk0v-17"
|
|
82
|
+
})(["position:relative;display:inline-flex;align-items:center;box-sizing:border-box;width:max-content;max-width:100%;min-height:24px;padding:0 8px;background:", ";border:none;border-radius:3px;font-size:13px;line-height:1;color:", ";&:hover{background:", ";color:", ";}&:hover .ds-select-selection-item-label{max-width:calc(100% - 24px);}&:hover .ds-select-selection-item-remove{display:inline-flex;}"], (props) => props.theme.palette["grey-200"], (props) => props.theme.palette["grey-600"], (props) => props.theme.palette["grey-300"], (props) => props.theme.palette["grey-800"]);
|
|
83
|
+
const ChipLabel = /* @__PURE__ */ styled.span.withConfig({
|
|
84
|
+
displayName: "Selectstyles__ChipLabel",
|
|
85
|
+
componentId: "sc-n9lk0v-18"
|
|
86
|
+
})(["max-width:100%;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;"]);
|
|
87
|
+
const ChipRemove = /* @__PURE__ */ styled.span.withConfig({
|
|
88
|
+
displayName: "Selectstyles__ChipRemove",
|
|
89
|
+
componentId: "sc-n9lk0v-19"
|
|
90
|
+
})(["position:absolute;right:4px;top:50%;transform:translateY(-50%);display:none;align-items:center;justify-content:center;width:24px;height:24px;cursor:pointer;color:", ";"], (props) => props.theme.palette["red-600"]);
|
|
79
91
|
export {
|
|
80
|
-
|
|
92
|
+
Arrow,
|
|
93
|
+
Chip,
|
|
94
|
+
ChipLabel,
|
|
95
|
+
ChipRemove,
|
|
96
|
+
ClearWrapper,
|
|
97
|
+
DropdownWrapper,
|
|
98
|
+
Inner,
|
|
99
|
+
Loading,
|
|
100
|
+
MultiValueArea,
|
|
101
|
+
NotFound,
|
|
102
|
+
OptionItem,
|
|
103
|
+
Placeholder,
|
|
81
104
|
PrefixWrapper,
|
|
105
|
+
ScrollList,
|
|
106
|
+
SearchInputEl,
|
|
82
107
|
SelectContainer,
|
|
83
108
|
SelectWrapper,
|
|
109
|
+
SelectionItem,
|
|
110
|
+
Selector,
|
|
84
111
|
SuffixWrapper
|
|
85
112
|
};
|
package/dist/Select.types.d.ts
CHANGED
|
@@ -1,18 +1,144 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { SelectValue } from 'antd/lib/select';
|
|
3
|
-
import { ReactNode, ReactText } from 'react';
|
|
1
|
+
import { AriaAttributes, CSSProperties, FocusEvent, Key, KeyboardEvent, MouseEvent, ReactElement, ReactNode, UIEvent } from 'react';
|
|
4
2
|
import { CSSObject } from 'styled-components';
|
|
3
|
+
import { DropdownPlacement } from '@synerise/ds-dropdown';
|
|
5
4
|
import { FormFieldCommonProps } from '@synerise/ds-form-field';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The selected value. A plain value for single-select, an array for
|
|
7
|
+
* `mode="multiple"` / `mode="tags"`. Replaces antd's `SelectValue`.
|
|
8
|
+
*/
|
|
9
|
+
export type RawValueType = string | number;
|
|
10
|
+
export type SelectValue = RawValueType | RawValueType[] | undefined;
|
|
11
|
+
export type SelectMode = 'multiple' | 'tags';
|
|
12
|
+
/** Internal, normalised option shape (from `options` prop or `<Select.Option>` children). */
|
|
13
|
+
export type SelectOption = {
|
|
14
|
+
value: RawValueType;
|
|
15
|
+
/** antd-parity alias of `value`; some consumers read `option.key` in callbacks. */
|
|
16
|
+
key?: RawValueType;
|
|
17
|
+
/** Rendered content in the dropdown row (and the selector unless `optionLabelProp` picks another field). */
|
|
18
|
+
label?: ReactNode;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
/** Native `title` on the option (hover tooltip / accessible text), mirrors antd. */
|
|
21
|
+
title?: ReactNode;
|
|
22
|
+
/** antd parity: raw children of `<Select.Option>`; some consumers read `option.children`. */
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
/** antd parity: forwarded to the rendered option row. */
|
|
25
|
+
style?: CSSProperties;
|
|
26
|
+
/** Value used for client-side filtering when `optionFilterProp` is set. */
|
|
27
|
+
filterValue?: string;
|
|
28
|
+
/** antd parity: per-option `data-*` / `aria-*` attributes, forwarded to the rendered option row. */
|
|
29
|
+
[dataAttr: `data-${string}`]: unknown;
|
|
30
|
+
[ariaAttr: `aria-${string}`]: unknown;
|
|
31
|
+
};
|
|
32
|
+
/** Signature of a client-side option filter (antd-compatible). */
|
|
33
|
+
export type FilterOptionFn = (input: string, option: SelectOption) => boolean;
|
|
34
|
+
/** antd-parity: type for a select `onChange` / `onSelect` handler. */
|
|
35
|
+
export type SelectHandler<VT extends RawValueType = RawValueType> = (value: VT, option?: SelectOption | SelectOption[]) => void;
|
|
36
|
+
export type SelectProps<VT extends SelectValue = SelectValue> = {
|
|
37
|
+
value?: VT;
|
|
38
|
+
defaultValue?: VT;
|
|
39
|
+
onChange?: (value: VT, option?: SelectOption | SelectOption[]) => void;
|
|
40
|
+
onSelect?: (value: RawValueType, option?: SelectOption) => void;
|
|
41
|
+
onDeselect?: (value: RawValueType, option?: SelectOption) => void;
|
|
42
|
+
onBlur?: (event: FocusEvent<HTMLElement>) => void;
|
|
43
|
+
onFocus?: (event: FocusEvent<HTMLElement>) => void;
|
|
44
|
+
onDropdownVisibleChange?: (open: boolean) => void;
|
|
45
|
+
/** Remote-search callback (pair with `filterOption={false}`). */
|
|
46
|
+
onSearch?: (value: string) => void;
|
|
47
|
+
/** antd parity: fired as the dropdown option list scrolls (e.g. to page in more options). */
|
|
48
|
+
onPopupScroll?: (event: UIEvent<HTMLDivElement>) => void;
|
|
49
|
+
/** antd parity: fired when the clear affordance is used. */
|
|
50
|
+
onClear?: () => void;
|
|
51
|
+
/** antd parity: click handler on the selector box. */
|
|
52
|
+
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
|
|
53
|
+
/** antd parity: keydown on the inner search input. */
|
|
54
|
+
onInputKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
|
|
55
|
+
/** antd parity: keydown on the selector. */
|
|
56
|
+
onKeyDown?: (event: KeyboardEvent<HTMLElement>) => void;
|
|
57
|
+
/** `undefined` = single-select; `'multiple'` / `'tags'` = multi-value. */
|
|
58
|
+
mode?: SelectMode;
|
|
59
|
+
/** Options as data. When omitted, `<Select.Option>` children are read instead. */
|
|
60
|
+
options?: SelectOption[];
|
|
61
|
+
showSearch?: boolean;
|
|
62
|
+
/** Controlled search-input value (antd parity). Pairs with `onSearch`. */
|
|
63
|
+
searchValue?: string;
|
|
64
|
+
/** antd parity: max length of the search input. */
|
|
65
|
+
maxLength?: number;
|
|
66
|
+
/** `true`/`false` toggles built-in filtering; a function is a custom predicate. */
|
|
67
|
+
filterOption?: boolean | FilterOptionFn;
|
|
68
|
+
/** Which option field the built-in filter matches against (default: label). */
|
|
69
|
+
optionFilterProp?: string;
|
|
70
|
+
/** Which option field renders in the selector (default: label). */
|
|
71
|
+
optionLabelProp?: string;
|
|
72
|
+
placeholder?: ReactNode;
|
|
73
|
+
disabled?: boolean;
|
|
74
|
+
loading?: boolean;
|
|
75
|
+
allowClear?: boolean;
|
|
76
|
+
autoFocus?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* antd parity: accepted for back-compat but NOT implemented. The dropdown
|
|
79
|
+
* always highlights the selected (or first enabled) option on open, so this
|
|
80
|
+
* flag has no effect.
|
|
81
|
+
*/
|
|
82
|
+
defaultActiveFirstOption?: boolean;
|
|
83
|
+
showArrow?: boolean;
|
|
84
|
+
/** antd parity: custom icon replacing the dropdown arrow. */
|
|
85
|
+
suffixIcon?: ReactNode;
|
|
86
|
+
/** antd parity: tab index forwarded to the selector / search input. */
|
|
87
|
+
tabIndex?: number;
|
|
88
|
+
/** Max chips rendered in `multiple` / `tags` mode before the rest collapse into an overflow chip. */
|
|
89
|
+
maxTagCount?: number;
|
|
90
|
+
/** Max characters shown per chip label; longer labels are truncated with an ellipsis. */
|
|
91
|
+
maxTagTextLength?: number;
|
|
92
|
+
/**
|
|
93
|
+
* Content of the collapsed-overflow chip (antd parity). A node renders as-is;
|
|
94
|
+
* a function receives the omitted options and returns the node. Defaults to `+ N`.
|
|
95
|
+
*/
|
|
96
|
+
maxTagPlaceholder?: ReactNode | ((omittedValues: Array<{
|
|
97
|
+
value: RawValueType;
|
|
98
|
+
label: ReactNode;
|
|
99
|
+
}>) => ReactNode);
|
|
100
|
+
/** Characters that split typed text into tags in `mode="tags"`. */
|
|
101
|
+
tokenSeparators?: string[];
|
|
102
|
+
open?: boolean;
|
|
103
|
+
defaultOpen?: boolean;
|
|
104
|
+
/** Dropdown positioning / sizing. antd parity: consumers may return `parentNode` (`ParentNode | null`). */
|
|
105
|
+
getPopupContainer?: (trigger: HTMLElement) => HTMLElement | ParentNode | null;
|
|
106
|
+
placement?: DropdownPlacement;
|
|
107
|
+
dropdownClassName?: string;
|
|
108
|
+
/** antd v4 alias of `dropdownClassName`. */
|
|
109
|
+
popupClassName?: string;
|
|
110
|
+
/** antd parity: dropdown alignment config (accepted for compatibility). */
|
|
111
|
+
dropdownAlign?: Record<string, unknown>;
|
|
112
|
+
dropdownStyle?: CSSProperties;
|
|
113
|
+
/** Match the dropdown width to the selector (default true). A number fixes the dropdown width in px (antd parity). */
|
|
114
|
+
dropdownMatchSelectWidth?: boolean | number;
|
|
115
|
+
/** Wrap the rendered option menu (custom footer / scroll container). antd parity: receives a `ReactElement`. */
|
|
116
|
+
dropdownRender?: (menu: ReactElement) => ReactNode;
|
|
117
|
+
/** Max dropdown list height (px). */
|
|
118
|
+
listHeight?: number | string;
|
|
119
|
+
/** antd parity: fixed height per option row (px). Accepted for compatibility (non-virtualised list). */
|
|
120
|
+
listItemHeight?: number;
|
|
121
|
+
notFoundContent?: ReactNode;
|
|
122
|
+
clearIcon?: ReactNode;
|
|
123
|
+
/** Item key extractor for `<Select.Option>` children / options. */
|
|
124
|
+
rowKey?: (option: SelectOption) => Key;
|
|
9
125
|
prefixel?: ReactNode;
|
|
10
126
|
suffixel?: ReactNode;
|
|
11
|
-
listHeight?: ReactText;
|
|
12
127
|
grey?: boolean;
|
|
13
|
-
|
|
14
|
-
selectorStyle?: CSSObject;
|
|
128
|
+
/** Render just the selector (no FormField chrome). */
|
|
15
129
|
raw?: boolean;
|
|
16
130
|
readOnly?: boolean;
|
|
17
|
-
|
|
18
|
-
|
|
131
|
+
asFormElement?: boolean;
|
|
132
|
+
selectorStyle?: CSSObject;
|
|
133
|
+
clearTooltip?: string;
|
|
134
|
+
size?: 'middle' | 'large' | 'default';
|
|
135
|
+
error?: boolean;
|
|
136
|
+
id?: string;
|
|
137
|
+
className?: string;
|
|
138
|
+
style?: CSSProperties;
|
|
139
|
+
children?: ReactNode;
|
|
140
|
+
/** antd parity: forward native `data-*` attributes (e.g. `data-testid`) to the select root. */
|
|
141
|
+
[dataAttr: `data-${string}`]: unknown;
|
|
142
|
+
} & FormFieldCommonProps & AriaAttributes;
|
|
143
|
+
/** @deprecated internal alias kept for back-compat with the antd-era `Props` name. */
|
|
144
|
+
export type Props<VT extends SelectValue = SelectValue> = SelectProps<VT>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Key, ReactElement, ReactNode, UIEvent } from 'react';
|
|
2
|
+
import { RawValueType, SelectOption } from '../Select.types';
|
|
3
|
+
type OptionListProps = {
|
|
4
|
+
loading?: boolean;
|
|
5
|
+
/** Options to render (already filtered / with the tags create-row). */
|
|
6
|
+
options: SelectOption[];
|
|
7
|
+
notFoundContent?: ReactNode;
|
|
8
|
+
listHeight?: number | string;
|
|
9
|
+
isMultiple: boolean;
|
|
10
|
+
listboxId: string;
|
|
11
|
+
selectedValues: RawValueType[];
|
|
12
|
+
/** Keyboard-highlighted option index. */
|
|
13
|
+
activeIndex: number;
|
|
14
|
+
rowKey?: (option: SelectOption) => Key;
|
|
15
|
+
optionDomId: (index: number) => string;
|
|
16
|
+
onOptionActivate: (index: number) => void;
|
|
17
|
+
onOptionSelect: (option: SelectOption) => void;
|
|
18
|
+
/** antd parity: fired as the option list scrolls (e.g. to page in more options). */
|
|
19
|
+
onPopupScroll?: (event: UIEvent<HTMLDivElement>) => void;
|
|
20
|
+
};
|
|
21
|
+
/** The dropdown overlay: loading / empty / the scrollable listbox of options. */
|
|
22
|
+
export declare const OptionList: ({ loading, options, notFoundContent, listHeight, isMultiple, listboxId, selectedValues, activeIndex, rowKey, optionDomId, onOptionActivate, onOptionSelect, onPopupScroll, }: OptionListProps) => ReactElement;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import Loader from "@synerise/ds-loader";
|
|
3
|
+
import Scrollbar from "@synerise/ds-scrollbar";
|
|
4
|
+
import { DropdownWrapper, Loading, NotFound, ScrollList, Inner, OptionItem } from "../Select.styles.js";
|
|
5
|
+
import { DEFAULT_LIST_HEIGHT, cx } from "../utils/helpers.js";
|
|
6
|
+
const OptionList = ({
|
|
7
|
+
loading,
|
|
8
|
+
options,
|
|
9
|
+
notFoundContent,
|
|
10
|
+
listHeight,
|
|
11
|
+
isMultiple,
|
|
12
|
+
listboxId,
|
|
13
|
+
selectedValues,
|
|
14
|
+
activeIndex,
|
|
15
|
+
rowKey,
|
|
16
|
+
optionDomId,
|
|
17
|
+
onOptionActivate,
|
|
18
|
+
onOptionSelect,
|
|
19
|
+
onPopupScroll
|
|
20
|
+
}) => /* @__PURE__ */ jsx(DropdownWrapper, { onMouseDown: (event) => {
|
|
21
|
+
const target = event.target;
|
|
22
|
+
if (!target.closest('input, textarea, [contenteditable="true"]')) {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
}
|
|
25
|
+
}, children: loading ? /* @__PURE__ */ jsx(Loading, { className: "ds-select-loading", children: /* @__PURE__ */ jsx(Loader, { size: "M" }) }) : options.length === 0 ? /* @__PURE__ */ jsx(NotFound, { className: "ds-select-empty", children: notFoundContent }) : /* @__PURE__ */ jsx(ScrollList, { children: /* @__PURE__ */ jsx(Scrollbar, { absolute: true, maxHeight: Number(listHeight) || DEFAULT_LIST_HEIGHT, onScroll: onPopupScroll ? (event) => onPopupScroll(event) : void 0, children: /* @__PURE__ */ jsx(Inner, { role: "listbox", id: listboxId, "aria-multiselectable": isMultiple || void 0, children: options.map((option, index) => {
|
|
26
|
+
const isSelected = selectedValues.includes(option.value);
|
|
27
|
+
const optionAttrs = Object.fromEntries(Object.entries(option).filter(([key]) => key.startsWith("data-") || key.startsWith("aria-")));
|
|
28
|
+
return /* @__PURE__ */ jsx(OptionItem, { id: optionDomId(index), role: "option", className: cx("ds-select-item-option", isSelected && "ds-select-item-option-selected", index === activeIndex && "ds-select-item-option-active"), selected: isSelected, "aria-selected": isSelected, "data-testid": "select-option", title: typeof option.title === "string" ? option.title : void 0, text: option.label ?? option.value, style: option.style, disabled: option.disabled, onMouseEnter: () => onOptionActivate(index), onClick: () => onOptionSelect(option), ...optionAttrs }, rowKey ? rowKey(option) : option.value);
|
|
29
|
+
}) }) }) }) });
|
|
30
|
+
export {
|
|
31
|
+
OptionList
|
|
32
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ChangeEvent, KeyboardEvent, ReactElement, ReactNode, RefObject } from 'react';
|
|
2
|
+
import { RawValueType } from '../Select.types';
|
|
3
|
+
type SelectorContentProps = {
|
|
4
|
+
isMultiple: boolean;
|
|
5
|
+
showSearch?: boolean;
|
|
6
|
+
hasValue: boolean;
|
|
7
|
+
placeholder?: ReactNode;
|
|
8
|
+
placeholderStr?: string;
|
|
9
|
+
selectedValues: RawValueType[];
|
|
10
|
+
effectiveQuery: string;
|
|
11
|
+
labelFor: (value: RawValueType) => ReactNode;
|
|
12
|
+
onRemoveValue: (value: RawValueType) => void;
|
|
13
|
+
maxTagCount?: number;
|
|
14
|
+
maxTagTextLength?: number;
|
|
15
|
+
maxTagPlaceholder?: ReactNode | ((omittedValues: Array<{
|
|
16
|
+
value: RawValueType;
|
|
17
|
+
label: ReactNode;
|
|
18
|
+
}>) => ReactNode);
|
|
19
|
+
inputRef: RefObject<HTMLInputElement>;
|
|
20
|
+
isDisabled: boolean;
|
|
21
|
+
hasInput: boolean;
|
|
22
|
+
autoFocus?: boolean;
|
|
23
|
+
maxLength?: number;
|
|
24
|
+
id?: string;
|
|
25
|
+
isOpen: boolean;
|
|
26
|
+
listboxId: string;
|
|
27
|
+
activeIndex: number;
|
|
28
|
+
optionDomId: (index: number) => string;
|
|
29
|
+
onSearchChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
30
|
+
onSearchKeyDown: (event: KeyboardEvent<HTMLInputElement>) => void;
|
|
31
|
+
};
|
|
32
|
+
/** Content rendered inside the selector box: chips / selected label / placeholder + search input. */
|
|
33
|
+
export declare const SelectorContent: ({ isMultiple, showSearch, hasValue, placeholder, placeholderStr, selectedValues, effectiveQuery, labelFor, onRemoveValue, maxTagCount, maxTagTextLength, maxTagPlaceholder, inputRef, isDisabled, hasInput, autoFocus, maxLength, id, isOpen, listboxId, activeIndex, optionDomId, onSearchChange, onSearchKeyDown, }: SelectorContentProps) => ReactElement;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import Icon, { CloseS } from "@synerise/ds-icon";
|
|
3
|
+
import { SearchInputEl, MultiValueArea, Chip, ChipLabel, ChipRemove, SelectionItem, Placeholder } from "../Select.styles.js";
|
|
4
|
+
const SelectorContent = ({
|
|
5
|
+
isMultiple,
|
|
6
|
+
showSearch,
|
|
7
|
+
hasValue,
|
|
8
|
+
placeholder,
|
|
9
|
+
placeholderStr,
|
|
10
|
+
selectedValues,
|
|
11
|
+
effectiveQuery,
|
|
12
|
+
labelFor,
|
|
13
|
+
onRemoveValue,
|
|
14
|
+
maxTagCount,
|
|
15
|
+
maxTagTextLength,
|
|
16
|
+
maxTagPlaceholder,
|
|
17
|
+
inputRef,
|
|
18
|
+
isDisabled,
|
|
19
|
+
hasInput,
|
|
20
|
+
autoFocus,
|
|
21
|
+
maxLength,
|
|
22
|
+
id,
|
|
23
|
+
isOpen,
|
|
24
|
+
listboxId,
|
|
25
|
+
activeIndex,
|
|
26
|
+
optionDomId,
|
|
27
|
+
onSearchChange,
|
|
28
|
+
onSearchKeyDown
|
|
29
|
+
}) => {
|
|
30
|
+
const isSingleSearch = !!showSearch && !isMultiple;
|
|
31
|
+
const searchInput = /* @__PURE__ */ jsx(SearchInputEl, { ref: inputRef, $overlay: isSingleSearch, className: "ds-select-search", value: effectiveQuery, onChange: onSearchChange, onKeyDown: onSearchKeyDown, placeholder: hasValue ? void 0 : placeholderStr, disabled: isDisabled, autoFocus, maxLength, autoComplete: "off", readOnly: !hasInput, id, role: "combobox", "aria-autocomplete": "list", "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-controls": isOpen ? listboxId : void 0, "aria-activedescendant": isOpen && activeIndex >= 0 ? optionDomId(activeIndex) : void 0 });
|
|
32
|
+
if (isMultiple) {
|
|
33
|
+
const displayedValues = typeof maxTagCount === "number" ? selectedValues.slice(0, Math.max(maxTagCount, 0)) : selectedValues;
|
|
34
|
+
const omittedValues = selectedValues.slice(displayedValues.length);
|
|
35
|
+
const chipLabel = (v) => {
|
|
36
|
+
const label = labelFor(v);
|
|
37
|
+
if (typeof maxTagTextLength === "number" && typeof label === "string" && label.length > maxTagTextLength) {
|
|
38
|
+
return `${label.slice(0, maxTagTextLength)}...`;
|
|
39
|
+
}
|
|
40
|
+
return label;
|
|
41
|
+
};
|
|
42
|
+
const overflowContent = typeof maxTagPlaceholder === "function" ? maxTagPlaceholder(omittedValues.map((v) => ({
|
|
43
|
+
value: v,
|
|
44
|
+
label: labelFor(v)
|
|
45
|
+
}))) : maxTagPlaceholder ?? `+ ${omittedValues.length}`;
|
|
46
|
+
return /* @__PURE__ */ jsxs(MultiValueArea, { children: [
|
|
47
|
+
displayedValues.map((v) => /* @__PURE__ */ jsxs(Chip, { className: "ds-select-selection-item", children: [
|
|
48
|
+
/* @__PURE__ */ jsx(ChipLabel, { className: "ds-select-selection-item-label", children: chipLabel(v) }),
|
|
49
|
+
/* @__PURE__ */ jsx(ChipRemove, { className: "ds-select-selection-item-remove", onMouseDown: (e) => {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
e.stopPropagation();
|
|
52
|
+
onRemoveValue(v);
|
|
53
|
+
}, children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(CloseS, {}), size: 24 }) })
|
|
54
|
+
] }, v)),
|
|
55
|
+
omittedValues.length > 0 && /* @__PURE__ */ jsx(Chip, { className: "ds-select-selection-overflow", "data-testid": "select-tag-overflow", children: /* @__PURE__ */ jsx(ChipLabel, { className: "ds-select-selection-item-label", children: overflowContent }) }),
|
|
56
|
+
searchInput
|
|
57
|
+
] });
|
|
58
|
+
}
|
|
59
|
+
if (showSearch) {
|
|
60
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
61
|
+
hasValue && !effectiveQuery && /* @__PURE__ */ jsx(SelectionItem, { className: "ds-select-selection-item", children: labelFor(selectedValues[0]) }),
|
|
62
|
+
searchInput
|
|
63
|
+
] });
|
|
64
|
+
}
|
|
65
|
+
if (hasValue) {
|
|
66
|
+
return /* @__PURE__ */ jsx(SelectionItem, { className: "ds-select-selection-item", children: labelFor(selectedValues[0]) });
|
|
67
|
+
}
|
|
68
|
+
return /* @__PURE__ */ jsx(Placeholder, { className: "ds-select-selection-placeholder", children: placeholder });
|
|
69
|
+
};
|
|
70
|
+
export {
|
|
71
|
+
SelectorContent
|
|
72
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { FilterOptionFn, RawValueType, SelectOption } from '../Select.types';
|
|
3
|
+
type UseSelectOptionsParams = {
|
|
4
|
+
/** Options as data; when empty, `<Select.Option>` children are read instead. */
|
|
5
|
+
options?: SelectOption[];
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
/** `false` = remote search (never filter locally); a function is a custom predicate. */
|
|
8
|
+
filterOption?: boolean | FilterOptionFn;
|
|
9
|
+
optionFilterProp?: string;
|
|
10
|
+
/** Current search text (controlled `searchValue` or the internal query). */
|
|
11
|
+
effectiveQuery: string;
|
|
12
|
+
isTags: boolean;
|
|
13
|
+
/** Already-selected values (so tags mode doesn't offer an existing value as new). */
|
|
14
|
+
selectedValues: RawValueType[];
|
|
15
|
+
};
|
|
16
|
+
type UseSelectOptionsResult = {
|
|
17
|
+
/** Options from the `options` prop, or resolved from `<Select.Option>` children. */
|
|
18
|
+
resolvedOptions: SelectOption[];
|
|
19
|
+
/** `resolvedOptions` after client-side filtering + the tags create-row. */
|
|
20
|
+
displayedOptions: SelectOption[];
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Resolve a Select's option list: `options` prop → `<Select.Option>` children,
|
|
24
|
+
* then apply client-side filtering (`filterOption` / `optionFilterProp`) and, in
|
|
25
|
+
* `mode="tags"`, prepend the typed text as a create-able row.
|
|
26
|
+
*/
|
|
27
|
+
export declare const useSelectOptions: ({ options, children, filterOption, optionFilterProp, effectiveQuery, isTags, selectedValues, }: UseSelectOptionsParams) => UseSelectOptionsResult;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { getOptionsFromChildren } from "../utils/getOptionsFromChildren.js";
|
|
3
|
+
import { defaultFilter } from "../utils/helpers.js";
|
|
4
|
+
const useSelectOptions = ({
|
|
5
|
+
options,
|
|
6
|
+
children,
|
|
7
|
+
filterOption,
|
|
8
|
+
optionFilterProp,
|
|
9
|
+
effectiveQuery,
|
|
10
|
+
isTags,
|
|
11
|
+
selectedValues
|
|
12
|
+
}) => {
|
|
13
|
+
const resolvedOptions = useMemo(() => {
|
|
14
|
+
if (options && options.length > 0) {
|
|
15
|
+
return options;
|
|
16
|
+
}
|
|
17
|
+
return getOptionsFromChildren(children);
|
|
18
|
+
}, [options, children]);
|
|
19
|
+
const displayedOptions = useMemo(() => {
|
|
20
|
+
let list = resolvedOptions;
|
|
21
|
+
if (filterOption !== false && effectiveQuery) {
|
|
22
|
+
const match = typeof filterOption === "function" ? filterOption : defaultFilter(optionFilterProp);
|
|
23
|
+
list = resolvedOptions.filter((option) => match(effectiveQuery, option));
|
|
24
|
+
}
|
|
25
|
+
if (isTags && effectiveQuery && !resolvedOptions.some((option) => String(option.value) === effectiveQuery) && !selectedValues.some((v) => String(v) === effectiveQuery)) {
|
|
26
|
+
list = [{
|
|
27
|
+
value: effectiveQuery,
|
|
28
|
+
key: effectiveQuery,
|
|
29
|
+
label: effectiveQuery
|
|
30
|
+
}, ...list];
|
|
31
|
+
}
|
|
32
|
+
return list;
|
|
33
|
+
}, [resolvedOptions, filterOption, optionFilterProp, effectiveQuery, isTags, selectedValues]);
|
|
34
|
+
return {
|
|
35
|
+
resolvedOptions,
|
|
36
|
+
displayedOptions
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export {
|
|
40
|
+
useSelectOptions
|
|
41
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { default } from './Select';
|
|
2
|
-
export
|
|
2
|
+
export { Option, type OptionProps } from './Option';
|
|
3
|
+
export type { Props, SelectProps, SelectValue, SelectOption, SelectMode, SelectHandler, RawValueType, FilterOptionFn, } from './Select.types';
|
|
3
4
|
export * as SelectStyles from './Select.styles';
|
|
5
|
+
export { getOptionsFromChildren, findOption, } from './utils/getOptionsFromChildren';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { default as default2 } from "./Select.js";
|
|
2
|
+
import { Option } from "./Option.js";
|
|
2
3
|
import * as Select_styles from "./Select.styles.js";
|
|
4
|
+
import { findOption, getOptionsFromChildren } from "./utils/getOptionsFromChildren.js";
|
|
3
5
|
export {
|
|
6
|
+
Option,
|
|
4
7
|
Select_styles as SelectStyles,
|
|
5
|
-
default2 as default
|
|
8
|
+
default2 as default,
|
|
9
|
+
findOption,
|
|
10
|
+
getOptionsFromChildren
|
|
6
11
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { SelectOption } from '../Select.types';
|
|
3
|
+
/**
|
|
4
|
+
* Maps declarative `<Select.Option>` children to the internal option shape.
|
|
5
|
+
* Only direct `Option` children are considered; anything else is ignored.
|
|
6
|
+
* `label`/`title` are preserved so the selector display (`optionLabelProp`) and
|
|
7
|
+
* client-side filtering (`optionFilterProp`) can pick the right field.
|
|
8
|
+
*/
|
|
9
|
+
export declare const getOptionsFromChildren: (children: ReactNode) => SelectOption[];
|
|
10
|
+
/** Resolve the display node for a selected value from the options list. */
|
|
11
|
+
export declare const findOption: (options: SelectOption[], value: SelectOption["value"]) => SelectOption | undefined;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Children, isValidElement } from "react";
|
|
2
|
+
import { Option } from "../Option.js";
|
|
3
|
+
const getOptionsFromChildren = (children) => {
|
|
4
|
+
const options = [];
|
|
5
|
+
Children.forEach(children, (child) => {
|
|
6
|
+
if (!isValidElement(child) || child.type !== Option) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const optionProps = child.props;
|
|
10
|
+
const {
|
|
11
|
+
value,
|
|
12
|
+
disabled,
|
|
13
|
+
title,
|
|
14
|
+
label,
|
|
15
|
+
children: optionLabel,
|
|
16
|
+
style
|
|
17
|
+
} = optionProps;
|
|
18
|
+
const resolvedValue = value ?? child.key ?? void 0;
|
|
19
|
+
const option = {
|
|
20
|
+
value: resolvedValue,
|
|
21
|
+
key: resolvedValue,
|
|
22
|
+
disabled,
|
|
23
|
+
title,
|
|
24
|
+
label: label ?? optionLabel,
|
|
25
|
+
children: optionLabel,
|
|
26
|
+
style
|
|
27
|
+
};
|
|
28
|
+
Object.entries(optionProps).forEach(([key, val]) => {
|
|
29
|
+
if (key.startsWith("data-") || key.startsWith("aria-")) {
|
|
30
|
+
option[key] = val;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
options.push(option);
|
|
34
|
+
});
|
|
35
|
+
return options;
|
|
36
|
+
};
|
|
37
|
+
const findOption = (options, value) => options.find((option) => option.value === value);
|
|
38
|
+
export {
|
|
39
|
+
findOption,
|
|
40
|
+
getOptionsFromChildren
|
|
41
|
+
};
|