analytica-frontend-lib 1.0.50 → 1.0.51
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/dist/Select/index.d.mts +10 -3
- package/dist/Select/index.d.ts +10 -3
- package/dist/Select/index.js +72 -24
- package/dist/Select/index.js.map +1 -1
- package/dist/Select/index.mjs +75 -26
- package/dist/Select/index.mjs.map +1 -1
- package/dist/index.css +11 -4
- package/dist/index.css.map +1 -1
- package/dist/index.js +72 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +75 -26
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +11 -4
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
package/dist/Select/index.d.mts
CHANGED
|
@@ -10,9 +10,10 @@ interface SelectStore {
|
|
|
10
10
|
setValue: (value: string) => void;
|
|
11
11
|
selectedLabel: ReactNode;
|
|
12
12
|
setSelectedLabel: (label: ReactNode) => void;
|
|
13
|
+
onValueChange?: (value: string) => void;
|
|
13
14
|
}
|
|
14
15
|
type SelectStoreApi = StoreApi<SelectStore>;
|
|
15
|
-
declare function createSelectStore(): SelectStoreApi;
|
|
16
|
+
declare function createSelectStore(onValueChange?: (value: string) => void): SelectStoreApi;
|
|
16
17
|
declare const useSelectStore: (externalStore?: SelectStoreApi) => SelectStoreApi;
|
|
17
18
|
declare function getLabelAsNode(children: ReactNode): ReactNode;
|
|
18
19
|
interface SelectProps {
|
|
@@ -20,9 +21,13 @@ interface SelectProps {
|
|
|
20
21
|
defaultValue?: string;
|
|
21
22
|
value?: string;
|
|
22
23
|
onValueChange?: (value: string) => void;
|
|
23
|
-
size?: 'small' | 'medium' | 'large';
|
|
24
|
+
size?: 'small' | 'medium' | 'large' | 'extra-large';
|
|
25
|
+
label?: string;
|
|
26
|
+
helperText?: string;
|
|
27
|
+
errorMessage?: string;
|
|
28
|
+
id?: string;
|
|
24
29
|
}
|
|
25
|
-
declare const Select: ({ children, defaultValue, value: propValue, onValueChange, size, }: SelectProps) => react_jsx_runtime.JSX.Element;
|
|
30
|
+
declare const Select: ({ children, defaultValue, value: propValue, onValueChange, size, label, helperText, errorMessage, id, }: SelectProps) => react_jsx_runtime.JSX.Element;
|
|
26
31
|
declare const SelectValue: ({ placeholder, store: externalStore, }: {
|
|
27
32
|
placeholder?: string;
|
|
28
33
|
store?: SelectStoreApi;
|
|
@@ -32,6 +37,8 @@ interface SelectTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
32
37
|
invalid?: boolean;
|
|
33
38
|
variant?: 'outlined' | 'underlined' | 'rounded';
|
|
34
39
|
store?: SelectStoreApi;
|
|
40
|
+
size?: 'small' | 'medium' | 'large' | 'extra-large';
|
|
41
|
+
selectId?: string;
|
|
35
42
|
}
|
|
36
43
|
declare const SelectTrigger: react.ForwardRefExoticComponent<SelectTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
37
44
|
interface SelectContentProps extends HTMLAttributes<HTMLDivElement> {
|
package/dist/Select/index.d.ts
CHANGED
|
@@ -10,9 +10,10 @@ interface SelectStore {
|
|
|
10
10
|
setValue: (value: string) => void;
|
|
11
11
|
selectedLabel: ReactNode;
|
|
12
12
|
setSelectedLabel: (label: ReactNode) => void;
|
|
13
|
+
onValueChange?: (value: string) => void;
|
|
13
14
|
}
|
|
14
15
|
type SelectStoreApi = StoreApi<SelectStore>;
|
|
15
|
-
declare function createSelectStore(): SelectStoreApi;
|
|
16
|
+
declare function createSelectStore(onValueChange?: (value: string) => void): SelectStoreApi;
|
|
16
17
|
declare const useSelectStore: (externalStore?: SelectStoreApi) => SelectStoreApi;
|
|
17
18
|
declare function getLabelAsNode(children: ReactNode): ReactNode;
|
|
18
19
|
interface SelectProps {
|
|
@@ -20,9 +21,13 @@ interface SelectProps {
|
|
|
20
21
|
defaultValue?: string;
|
|
21
22
|
value?: string;
|
|
22
23
|
onValueChange?: (value: string) => void;
|
|
23
|
-
size?: 'small' | 'medium' | 'large';
|
|
24
|
+
size?: 'small' | 'medium' | 'large' | 'extra-large';
|
|
25
|
+
label?: string;
|
|
26
|
+
helperText?: string;
|
|
27
|
+
errorMessage?: string;
|
|
28
|
+
id?: string;
|
|
24
29
|
}
|
|
25
|
-
declare const Select: ({ children, defaultValue, value: propValue, onValueChange, size, }: SelectProps) => react_jsx_runtime.JSX.Element;
|
|
30
|
+
declare const Select: ({ children, defaultValue, value: propValue, onValueChange, size, label, helperText, errorMessage, id, }: SelectProps) => react_jsx_runtime.JSX.Element;
|
|
26
31
|
declare const SelectValue: ({ placeholder, store: externalStore, }: {
|
|
27
32
|
placeholder?: string;
|
|
28
33
|
store?: SelectStoreApi;
|
|
@@ -32,6 +37,8 @@ interface SelectTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
32
37
|
invalid?: boolean;
|
|
33
38
|
variant?: 'outlined' | 'underlined' | 'rounded';
|
|
34
39
|
store?: SelectStoreApi;
|
|
40
|
+
size?: 'small' | 'medium' | 'large' | 'extra-large';
|
|
41
|
+
selectId?: string;
|
|
35
42
|
}
|
|
36
43
|
declare const SelectTrigger: react.ForwardRefExoticComponent<SelectTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
37
44
|
interface SelectContentProps extends HTMLAttributes<HTMLDivElement> {
|
package/dist/Select/index.js
CHANGED
|
@@ -35,14 +35,27 @@ var import_react = require("react");
|
|
|
35
35
|
var import_phosphor_react = require("phosphor-react");
|
|
36
36
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
37
37
|
var VARIANT_CLASSES = {
|
|
38
|
-
outlined: "border rounded-
|
|
38
|
+
outlined: "border rounded-lg focus:border-primary-950",
|
|
39
39
|
underlined: "border-b focus:border-primary-950",
|
|
40
|
-
rounded: "border rounded-
|
|
40
|
+
rounded: "border rounded-full focus:border-primary-950"
|
|
41
41
|
};
|
|
42
42
|
var SIZE_CLASSES = {
|
|
43
43
|
small: "text-sm",
|
|
44
44
|
medium: "text-md",
|
|
45
|
-
large: "text-lg"
|
|
45
|
+
large: "text-lg",
|
|
46
|
+
"extra-large": "text-lg"
|
|
47
|
+
};
|
|
48
|
+
var HEIGHT_CLASSES = {
|
|
49
|
+
small: "h-8",
|
|
50
|
+
medium: "h-9",
|
|
51
|
+
large: "h-10",
|
|
52
|
+
"extra-large": "h-12"
|
|
53
|
+
};
|
|
54
|
+
var PADDING_CLASSES = {
|
|
55
|
+
small: "px-2 py-1",
|
|
56
|
+
medium: "px-3 py-2",
|
|
57
|
+
large: "px-4 py-3",
|
|
58
|
+
"extra-large": "px-5 py-4"
|
|
46
59
|
};
|
|
47
60
|
var SIDE_CLASSES = {
|
|
48
61
|
top: "bottom-full -translate-y-1",
|
|
@@ -55,14 +68,15 @@ var ALIGN_CLASSES = {
|
|
|
55
68
|
center: "left-1/2 -translate-x-1/2",
|
|
56
69
|
end: "right-0"
|
|
57
70
|
};
|
|
58
|
-
function createSelectStore() {
|
|
71
|
+
function createSelectStore(onValueChange) {
|
|
59
72
|
return (0, import_zustand.create)((set) => ({
|
|
60
73
|
open: false,
|
|
61
74
|
setOpen: (open) => set({ open }),
|
|
62
75
|
value: "",
|
|
63
76
|
setValue: (value) => set({ value }),
|
|
64
77
|
selectedLabel: "",
|
|
65
|
-
setSelectedLabel: (label) => set({ selectedLabel: label })
|
|
78
|
+
setSelectedLabel: (label) => set({ selectedLabel: label }),
|
|
79
|
+
onValueChange
|
|
66
80
|
}));
|
|
67
81
|
}
|
|
68
82
|
var useSelectStore = (externalStore) => {
|
|
@@ -81,15 +95,24 @@ function getLabelAsNode(children) {
|
|
|
81
95
|
if (flattened.length === 1) return flattened[0];
|
|
82
96
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: flattened });
|
|
83
97
|
}
|
|
84
|
-
var injectStore = (children, store) => {
|
|
98
|
+
var injectStore = (children, store, size, selectId) => {
|
|
85
99
|
return import_react.Children.map(children, (child) => {
|
|
86
100
|
if ((0, import_react.isValidElement)(child)) {
|
|
87
101
|
const typedChild = child;
|
|
88
102
|
const newProps = {
|
|
89
103
|
store
|
|
90
104
|
};
|
|
105
|
+
if (typedChild.type === SelectTrigger) {
|
|
106
|
+
newProps.size = size;
|
|
107
|
+
newProps.selectId = selectId;
|
|
108
|
+
}
|
|
91
109
|
if (typedChild.props.children) {
|
|
92
|
-
newProps.children = injectStore(
|
|
110
|
+
newProps.children = injectStore(
|
|
111
|
+
typedChild.props.children,
|
|
112
|
+
store,
|
|
113
|
+
size,
|
|
114
|
+
selectId
|
|
115
|
+
);
|
|
93
116
|
}
|
|
94
117
|
return (0, import_react.cloneElement)(typedChild, newProps);
|
|
95
118
|
}
|
|
@@ -101,16 +124,19 @@ var Select = ({
|
|
|
101
124
|
defaultValue = "",
|
|
102
125
|
value: propValue,
|
|
103
126
|
onValueChange,
|
|
104
|
-
size = "small"
|
|
127
|
+
size = "small",
|
|
128
|
+
label,
|
|
129
|
+
helperText,
|
|
130
|
+
errorMessage,
|
|
131
|
+
id
|
|
105
132
|
}) => {
|
|
106
133
|
const storeRef = (0, import_react.useRef)(null);
|
|
107
|
-
storeRef.current ??= createSelectStore();
|
|
134
|
+
storeRef.current ??= createSelectStore(onValueChange);
|
|
108
135
|
const store = storeRef.current;
|
|
109
136
|
const selectRef = (0, import_react.useRef)(null);
|
|
110
|
-
const { open, setOpen, setValue,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
);
|
|
137
|
+
const { open, setOpen, setValue, selectedLabel } = (0, import_zustand.useStore)(store, (s) => s);
|
|
138
|
+
const generatedId = (0, import_react.useId)();
|
|
139
|
+
const selectId = id ?? `select-${generatedId}`;
|
|
114
140
|
const findLabelForValue = (children2, targetValue) => {
|
|
115
141
|
let found = null;
|
|
116
142
|
const search = (nodes) => {
|
|
@@ -130,8 +156,8 @@ var Select = ({
|
|
|
130
156
|
};
|
|
131
157
|
(0, import_react.useEffect)(() => {
|
|
132
158
|
if (!selectedLabel && defaultValue) {
|
|
133
|
-
const
|
|
134
|
-
if (
|
|
159
|
+
const label2 = findLabelForValue(children, defaultValue);
|
|
160
|
+
if (label2) store.setState({ selectedLabel: label2 });
|
|
135
161
|
}
|
|
136
162
|
}, [children, defaultValue, selectedLabel]);
|
|
137
163
|
(0, import_react.useEffect)(() => {
|
|
@@ -169,19 +195,33 @@ var Select = ({
|
|
|
169
195
|
document.removeEventListener("keydown", handleArrowKeys);
|
|
170
196
|
};
|
|
171
197
|
}, [open]);
|
|
172
|
-
(0, import_react.useEffect)(() => {
|
|
173
|
-
setValue(value);
|
|
174
|
-
onValueChange?.(value);
|
|
175
|
-
}, [value, onValueChange]);
|
|
176
198
|
(0, import_react.useEffect)(() => {
|
|
177
199
|
if (propValue) {
|
|
178
200
|
setValue(propValue);
|
|
179
|
-
const
|
|
180
|
-
if (
|
|
201
|
+
const label2 = findLabelForValue(children, propValue);
|
|
202
|
+
if (label2) store.setState({ selectedLabel: label2 });
|
|
181
203
|
}
|
|
182
204
|
}, [propValue]);
|
|
183
205
|
const sizeClasses = SIZE_CLASSES[size];
|
|
184
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
206
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "w-full", children: [
|
|
207
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
208
|
+
"label",
|
|
209
|
+
{
|
|
210
|
+
htmlFor: selectId,
|
|
211
|
+
className: `block font-bold text-text-900 mb-1.5 ${sizeClasses}`,
|
|
212
|
+
children: label
|
|
213
|
+
}
|
|
214
|
+
),
|
|
215
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `relative ${sizeClasses}`, ref: selectRef, children: injectStore(children, store, size, selectId) }),
|
|
216
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mt-1.5 gap-1.5", children: [
|
|
217
|
+
helperText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-text-500", children: helperText }),
|
|
218
|
+
errorMessage && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "flex gap-1 items-center text-sm text-indicator-error", children: [
|
|
219
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_phosphor_react.WarningCircle, { size: 16 }),
|
|
220
|
+
" ",
|
|
221
|
+
errorMessage
|
|
222
|
+
] })
|
|
223
|
+
] })
|
|
224
|
+
] });
|
|
185
225
|
};
|
|
186
226
|
var SelectValue = ({
|
|
187
227
|
placeholder,
|
|
@@ -199,18 +239,24 @@ var SelectTrigger = (0, import_react.forwardRef)(
|
|
|
199
239
|
variant = "outlined",
|
|
200
240
|
store: externalStore,
|
|
201
241
|
disabled,
|
|
242
|
+
size = "medium",
|
|
243
|
+
selectId,
|
|
202
244
|
...props
|
|
203
245
|
}, ref) => {
|
|
204
246
|
const store = useSelectStore(externalStore);
|
|
205
247
|
const open = (0, import_zustand.useStore)(store, (s) => s.open);
|
|
206
248
|
const toggleOpen = () => store.setState({ open: !open });
|
|
207
249
|
const variantClasses = VARIANT_CLASSES[variant];
|
|
250
|
+
const heightClasses = HEIGHT_CLASSES[size];
|
|
251
|
+
const paddingClasses = PADDING_CLASSES[size];
|
|
208
252
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
209
253
|
"button",
|
|
210
254
|
{
|
|
211
255
|
ref,
|
|
256
|
+
id: selectId,
|
|
212
257
|
className: `
|
|
213
|
-
flex
|
|
258
|
+
flex min-w-[220px] w-full items-center justify-between border-border-300
|
|
259
|
+
${heightClasses} ${paddingClasses}
|
|
214
260
|
${invalid && `${variant == "underlined" ? "border-b-2" : "border-2"} border-indicator-error text-text-600`}
|
|
215
261
|
${disabled ? "cursor-not-allowed text-text-400 pointer-events-none opacity-50" : "cursor-pointer hover:bg-background-50 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground"}
|
|
216
262
|
${!invalid && !disabled ? "text-text-700" : ""}
|
|
@@ -276,7 +322,8 @@ var SelectItem = (0, import_react.forwardRef)(
|
|
|
276
322
|
value: selectedValue,
|
|
277
323
|
setValue,
|
|
278
324
|
setOpen,
|
|
279
|
-
setSelectedLabel
|
|
325
|
+
setSelectedLabel,
|
|
326
|
+
onValueChange
|
|
280
327
|
} = (0, import_zustand.useStore)(store, (s) => s);
|
|
281
328
|
const handleClick = (e) => {
|
|
282
329
|
const labelNode = getLabelAsNode(children);
|
|
@@ -284,6 +331,7 @@ var SelectItem = (0, import_react.forwardRef)(
|
|
|
284
331
|
setValue(value);
|
|
285
332
|
setSelectedLabel(labelNode);
|
|
286
333
|
setOpen(false);
|
|
334
|
+
onValueChange?.(value);
|
|
287
335
|
}
|
|
288
336
|
props.onClick?.(e);
|
|
289
337
|
};
|
package/dist/Select/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Select/Select.tsx"],"sourcesContent":["import { create, StoreApi, useStore } from 'zustand';\nimport {\n ReactNode,\n useEffect,\n useRef,\n ButtonHTMLAttributes,\n forwardRef,\n HTMLAttributes,\n KeyboardEvent,\n MouseEvent,\n ReactElement,\n isValidElement,\n Children,\n cloneElement,\n} from 'react';\nimport { CaretDown, Check } from 'phosphor-react';\n\nconst VARIANT_CLASSES = {\n outlined: 'border rounded-sm focus:border-primary-950',\n underlined: 'border-b focus:border-primary-950',\n rounded: 'border rounded-4xl focus:border-primary-950',\n} as const;\n\nconst SIZE_CLASSES = {\n small: 'text-sm',\n medium: 'text-md',\n large: 'text-lg',\n} as const;\n\nconst SIDE_CLASSES = {\n top: 'bottom-full -translate-y-1',\n right: 'top-full translate-y-1',\n bottom: 'top-full translate-y-1',\n left: 'top-full translate-y-1',\n};\nconst ALIGN_CLASSES = {\n start: 'left-0',\n center: 'left-1/2 -translate-x-1/2',\n end: 'right-0',\n};\n\ninterface SelectStore {\n open: boolean;\n setOpen: (open: boolean) => void;\n value: string;\n setValue: (value: string) => void;\n selectedLabel: ReactNode;\n setSelectedLabel: (label: ReactNode) => void;\n}\n\ntype SelectStoreApi = StoreApi<SelectStore>;\n\nexport function createSelectStore(): SelectStoreApi {\n return create<SelectStore>((set) => ({\n open: false,\n setOpen: (open) => set({ open }),\n value: '',\n setValue: (value) => set({ value }),\n selectedLabel: '',\n setSelectedLabel: (label) => set({ selectedLabel: label }),\n }));\n}\n\nexport const useSelectStore = (externalStore?: SelectStoreApi) => {\n if (!externalStore) {\n throw new Error(\n 'Component must be used within a Select (store is missing)'\n );\n }\n\n return externalStore;\n};\n\nexport function getLabelAsNode(children: ReactNode): ReactNode {\n if (typeof children === 'string' || typeof children === 'number') {\n return children;\n }\n const flattened = Children.toArray(children);\n\n if (flattened.length === 1) return flattened[0];\n\n return <>{flattened}</>;\n}\n\ninterface SelectProps {\n children: ReactNode;\n defaultValue?: string;\n value?: string;\n onValueChange?: (value: string) => void;\n size?: 'small' | 'medium' | 'large';\n}\n\nconst injectStore = (children: ReactNode, store: SelectStoreApi): ReactNode => {\n return Children.map(children, (child) => {\n if (isValidElement(child)) {\n const typedChild = child as ReactElement<{\n store?: SelectStoreApi;\n children?: ReactNode;\n }>;\n\n const newProps: Partial<{ store: SelectStoreApi; children: ReactNode }> =\n {\n store,\n };\n\n if (typedChild.props.children) {\n newProps.children = injectStore(typedChild.props.children, store);\n }\n\n return cloneElement(typedChild, newProps);\n }\n return child;\n });\n};\n\nconst Select = ({\n children,\n defaultValue = '',\n value: propValue,\n onValueChange,\n size = 'small',\n}: SelectProps) => {\n const storeRef = useRef<SelectStoreApi | null>(null);\n storeRef.current ??= createSelectStore();\n const store = storeRef.current;\n\n const selectRef = useRef<HTMLDivElement>(null);\n const { open, setOpen, setValue, value, selectedLabel } = useStore(\n store,\n (s) => s\n );\n\n const findLabelForValue = (\n children: ReactNode,\n targetValue: string\n ): string | null => {\n let found: string | null = null;\n const search = (nodes: ReactNode) => {\n Children.forEach(nodes, (child) => {\n if (!isValidElement(child)) return;\n const typedChild = child as ReactElement<{\n value?: string;\n children?: ReactNode;\n }>;\n if (\n typedChild.type === SelectItem &&\n typedChild.props.value === targetValue\n ) {\n if (typeof typedChild.props.children === 'string')\n found = typedChild.props.children;\n }\n if (typedChild.props.children && !found)\n search(typedChild.props.children);\n });\n };\n search(children);\n return found;\n };\n\n useEffect(() => {\n if (!selectedLabel && defaultValue) {\n const label = findLabelForValue(children, defaultValue);\n if (label) store.setState({ selectedLabel: label });\n }\n }, [children, defaultValue, selectedLabel]);\n\n useEffect(() => {\n const handleClickOutside = (event: globalThis.MouseEvent) => {\n if (\n selectRef.current &&\n !selectRef.current.contains(event.target as Node)\n ) {\n setOpen(false);\n }\n };\n\n const handleArrowKeys = (event: globalThis.KeyboardEvent) => {\n const selectContent = selectRef.current?.querySelector('[role=\"menu\"]');\n if (selectContent) {\n event.preventDefault();\n const items = Array.from(\n selectContent.querySelectorAll(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])'\n )\n ).filter((el): el is HTMLElement => el instanceof HTMLElement);\n\n const focused = document.activeElement as HTMLElement;\n const currentIndex = items.findIndex((item) => item === focused);\n\n let nextIndex = 0;\n if (event.key === 'ArrowDown') {\n nextIndex =\n currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;\n } else {\n nextIndex =\n currentIndex === -1\n ? items.length - 1\n : (currentIndex - 1 + items.length) % items.length;\n }\n items[nextIndex]?.focus();\n }\n };\n\n if (open) {\n document.addEventListener('mousedown', handleClickOutside);\n document.addEventListener('keydown', handleArrowKeys);\n }\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n document.removeEventListener('keydown', handleArrowKeys);\n };\n }, [open]);\n\n useEffect(() => {\n setValue(value);\n onValueChange?.(value);\n }, [value, onValueChange]);\n\n useEffect(() => {\n if (propValue) {\n setValue(propValue);\n const label = findLabelForValue(children, propValue);\n if (label) store.setState({ selectedLabel: label });\n }\n }, [propValue]);\n\n const sizeClasses = SIZE_CLASSES[size];\n\n return (\n <div className={`relative ${sizeClasses}`} ref={selectRef}>\n {injectStore(children, store)}\n </div>\n );\n};\n\nconst SelectValue = ({\n placeholder,\n store: externalStore,\n}: {\n placeholder?: string;\n store?: SelectStoreApi;\n}) => {\n const store = useSelectStore(externalStore);\n\n const selectedLabel = useStore(store, (s) => s.selectedLabel);\n const value = useStore(store, (s) => s.value);\n return (\n <span className=\"text-inherit\">\n {selectedLabel || placeholder || value}\n </span>\n );\n};\n\ninterface SelectTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n className?: string;\n invalid?: boolean;\n variant?: 'outlined' | 'underlined' | 'rounded';\n store?: SelectStoreApi;\n}\n\nconst SelectTrigger = forwardRef<HTMLButtonElement, SelectTriggerProps>(\n (\n {\n className,\n invalid = false,\n variant = 'outlined',\n store: externalStore,\n disabled,\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const toggleOpen = () => store.setState({ open: !open });\n\n const variantClasses = VARIANT_CLASSES[variant];\n\n return (\n <button\n ref={ref}\n className={`\n flex h-9 min-w-[220px] w-full items-center justify-between border-border-300 px-3 py-2\n ${invalid && `${variant == 'underlined' ? 'border-b-2' : 'border-2'} border-indicator-error text-text-600`}\n ${\n disabled\n ? 'cursor-not-allowed text-text-400 pointer-events-none opacity-50'\n : 'cursor-pointer hover:bg-background-50 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground'\n }\n ${!invalid && !disabled ? 'text-text-700' : ''}\n ${variantClasses}\n ${className}\n `}\n onClick={toggleOpen}\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-controls={open ? 'select-content' : undefined}\n {...props}\n >\n {props.children}\n <CaretDown\n className={`h-[1em] w-[1em] opacity-50 transition-transform ${open ? 'rotate-180' : ''}`}\n />\n </button>\n );\n }\n);\nSelectTrigger.displayName = 'SelectTrigger';\n\ninterface SelectContentProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n align?: 'start' | 'center' | 'end';\n side?: 'top' | 'right' | 'bottom' | 'left';\n store?: SelectStoreApi;\n}\n\nconst SelectContent = forwardRef<HTMLDivElement, SelectContentProps>(\n (\n {\n children,\n className,\n align = 'start',\n side = 'bottom',\n store: externalStore,\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n\n const open = useStore(store, (s) => s.open);\n if (!open) return null;\n\n const getPositionClasses = () =>\n `w-full min-w-full absolute ${SIDE_CLASSES[side]} ${ALIGN_CLASSES[align]}`;\n\n return (\n <div\n role=\"menu\"\n ref={ref}\n className={`bg-background z-50 min-w-[210px] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md border-border-100 ${getPositionClasses()} ${className}`}\n {...props}\n >\n {children}\n </div>\n );\n }\n);\nSelectContent.displayName = 'SelectContent';\n\ninterface SelectItemProps extends HTMLAttributes<HTMLDivElement> {\n value: string;\n disabled?: boolean;\n store?: SelectStoreApi;\n}\n\nconst SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(\n (\n {\n className,\n children,\n value,\n disabled = false,\n store: externalStore,\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n const {\n value: selectedValue,\n setValue,\n setOpen,\n setSelectedLabel,\n } = useStore(store, (s) => s);\n\n const handleClick = (\n e: MouseEvent<HTMLDivElement> | KeyboardEvent<HTMLDivElement>\n ) => {\n const labelNode = getLabelAsNode(children);\n if (!disabled) {\n setValue(value);\n setSelectedLabel(labelNode);\n setOpen(false);\n }\n props.onClick?.(e as MouseEvent<HTMLDivElement>);\n };\n\n return (\n <div\n role=\"menuitem\"\n aria-disabled={disabled}\n ref={ref}\n className={`\n focus-visible:bg-background-50\n relative flex select-none items-center gap-2 rounded-sm p-3 outline-none transition-colors [&>svg]:size-4 [&>svg]:shrink-0\n ${className}\n ${\n disabled\n ? 'cursor-not-allowed text-text-400 pointer-events-none opacity-50'\n : 'cursor-pointer hover:bg-background-50 text-text-700 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground'\n }\n ${selectedValue === value && 'bg-background-50'}\n `}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') handleClick(e);\n }}\n tabIndex={disabled ? -1 : 0}\n {...props}\n >\n <span className=\"absolute right-2 flex h-3.5 w-3.5 items-center justify-center\">\n {selectedValue === value && <Check className=\"\" />}\n </span>\n {children}\n </div>\n );\n }\n);\n\nSelectItem.displayName = 'SelectItem';\n\nexport default Select;\nexport { SelectTrigger, SelectContent, SelectItem, SelectValue };\n"],"mappings":"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","names":["children"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Select/Select.tsx"],"sourcesContent":["import { create, StoreApi, useStore } from 'zustand';\nimport {\n ReactNode,\n useEffect,\n useRef,\n ButtonHTMLAttributes,\n forwardRef,\n HTMLAttributes,\n KeyboardEvent,\n MouseEvent,\n ReactElement,\n isValidElement,\n Children,\n cloneElement,\n useId,\n} from 'react';\nimport { CaretDown, Check, WarningCircle } from 'phosphor-react';\n\nconst VARIANT_CLASSES = {\n outlined: 'border rounded-lg focus:border-primary-950',\n underlined: 'border-b focus:border-primary-950',\n rounded: 'border rounded-full focus:border-primary-950',\n} as const;\n\nconst SIZE_CLASSES = {\n small: 'text-sm',\n medium: 'text-md',\n large: 'text-lg',\n 'extra-large': 'text-lg',\n} as const;\n\nconst HEIGHT_CLASSES = {\n small: 'h-8',\n medium: 'h-9',\n large: 'h-10',\n 'extra-large': 'h-12',\n} as const;\n\nconst PADDING_CLASSES = {\n small: 'px-2 py-1',\n medium: 'px-3 py-2',\n large: 'px-4 py-3',\n 'extra-large': 'px-5 py-4',\n} as const;\n\nconst SIDE_CLASSES = {\n top: 'bottom-full -translate-y-1',\n right: 'top-full translate-y-1',\n bottom: 'top-full translate-y-1',\n left: 'top-full translate-y-1',\n};\nconst ALIGN_CLASSES = {\n start: 'left-0',\n center: 'left-1/2 -translate-x-1/2',\n end: 'right-0',\n};\n\ninterface SelectStore {\n open: boolean;\n setOpen: (open: boolean) => void;\n value: string;\n setValue: (value: string) => void;\n selectedLabel: ReactNode;\n setSelectedLabel: (label: ReactNode) => void;\n onValueChange?: (value: string) => void;\n}\n\ntype SelectStoreApi = StoreApi<SelectStore>;\n\nexport function createSelectStore(\n onValueChange?: (value: string) => void\n): SelectStoreApi {\n return create<SelectStore>((set) => ({\n open: false,\n setOpen: (open) => set({ open }),\n value: '',\n setValue: (value) => set({ value }),\n selectedLabel: '',\n setSelectedLabel: (label) => set({ selectedLabel: label }),\n onValueChange,\n }));\n}\n\nexport const useSelectStore = (externalStore?: SelectStoreApi) => {\n if (!externalStore) {\n throw new Error(\n 'Component must be used within a Select (store is missing)'\n );\n }\n\n return externalStore;\n};\n\nexport function getLabelAsNode(children: ReactNode): ReactNode {\n if (typeof children === 'string' || typeof children === 'number') {\n return children;\n }\n const flattened = Children.toArray(children);\n\n if (flattened.length === 1) return flattened[0];\n\n return <>{flattened}</>;\n}\n\ninterface SelectProps {\n children: ReactNode;\n defaultValue?: string;\n value?: string;\n onValueChange?: (value: string) => void;\n size?: 'small' | 'medium' | 'large' | 'extra-large';\n label?: string;\n helperText?: string;\n errorMessage?: string;\n id?: string;\n}\n\nconst injectStore = (\n children: ReactNode,\n store: SelectStoreApi,\n size: string,\n selectId: string\n): ReactNode => {\n return Children.map(children, (child) => {\n if (isValidElement(child)) {\n const typedChild = child as ReactElement<{\n store?: SelectStoreApi;\n children?: ReactNode;\n size?: string;\n selectId?: string;\n }>;\n\n const newProps: Partial<{\n store: SelectStoreApi;\n children: ReactNode;\n size: string;\n selectId: string;\n }> = {\n store,\n };\n\n // Only pass size and selectId to SelectTrigger\n if (typedChild.type === SelectTrigger) {\n newProps.size = size;\n newProps.selectId = selectId;\n }\n\n if (typedChild.props.children) {\n newProps.children = injectStore(\n typedChild.props.children,\n store,\n size,\n selectId\n );\n }\n\n return cloneElement(typedChild, newProps);\n }\n return child;\n });\n};\n\nconst Select = ({\n children,\n defaultValue = '',\n value: propValue,\n onValueChange,\n size = 'small',\n label,\n helperText,\n errorMessage,\n id,\n}: SelectProps) => {\n const storeRef = useRef<SelectStoreApi | null>(null);\n storeRef.current ??= createSelectStore(onValueChange);\n const store = storeRef.current;\n\n const selectRef = useRef<HTMLDivElement>(null);\n const { open, setOpen, setValue, selectedLabel } = useStore(store, (s) => s);\n\n // Generate unique ID if not provided\n const generatedId = useId();\n const selectId = id ?? `select-${generatedId}`;\n\n const findLabelForValue = (\n children: ReactNode,\n targetValue: string\n ): string | null => {\n let found: string | null = null;\n const search = (nodes: ReactNode) => {\n Children.forEach(nodes, (child) => {\n if (!isValidElement(child)) return;\n const typedChild = child as ReactElement<{\n value?: string;\n children?: ReactNode;\n }>;\n if (\n typedChild.type === SelectItem &&\n typedChild.props.value === targetValue\n ) {\n if (typeof typedChild.props.children === 'string')\n found = typedChild.props.children;\n }\n if (typedChild.props.children && !found)\n search(typedChild.props.children);\n });\n };\n search(children);\n return found;\n };\n\n useEffect(() => {\n if (!selectedLabel && defaultValue) {\n const label = findLabelForValue(children, defaultValue);\n if (label) store.setState({ selectedLabel: label });\n }\n }, [children, defaultValue, selectedLabel]);\n\n useEffect(() => {\n const handleClickOutside = (event: globalThis.MouseEvent) => {\n if (\n selectRef.current &&\n !selectRef.current.contains(event.target as Node)\n ) {\n setOpen(false);\n }\n };\n\n const handleArrowKeys = (event: globalThis.KeyboardEvent) => {\n const selectContent = selectRef.current?.querySelector('[role=\"menu\"]');\n if (selectContent) {\n event.preventDefault();\n const items = Array.from(\n selectContent.querySelectorAll(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])'\n )\n ).filter((el): el is HTMLElement => el instanceof HTMLElement);\n\n const focused = document.activeElement as HTMLElement;\n const currentIndex = items.findIndex((item) => item === focused);\n\n let nextIndex = 0;\n if (event.key === 'ArrowDown') {\n nextIndex =\n currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;\n } else {\n nextIndex =\n currentIndex === -1\n ? items.length - 1\n : (currentIndex - 1 + items.length) % items.length;\n }\n items[nextIndex]?.focus();\n }\n };\n\n if (open) {\n document.addEventListener('mousedown', handleClickOutside);\n document.addEventListener('keydown', handleArrowKeys);\n }\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n document.removeEventListener('keydown', handleArrowKeys);\n };\n }, [open]);\n\n useEffect(() => {\n if (propValue) {\n setValue(propValue);\n const label = findLabelForValue(children, propValue);\n if (label) store.setState({ selectedLabel: label });\n }\n }, [propValue]);\n\n const sizeClasses = SIZE_CLASSES[size];\n\n return (\n <div className=\"w-full\">\n {/* Label */}\n {label && (\n <label\n htmlFor={selectId}\n className={`block font-bold text-text-900 mb-1.5 ${sizeClasses}`}\n >\n {label}\n </label>\n )}\n\n {/* Select Container */}\n <div className={`relative ${sizeClasses}`} ref={selectRef}>\n {injectStore(children, store, size, selectId)}\n </div>\n\n {/* Helper Text or Error Message */}\n <div className=\"mt-1.5 gap-1.5\">\n {helperText && <p className=\"text-sm text-text-500\">{helperText}</p>}\n {errorMessage && (\n <p className=\"flex gap-1 items-center text-sm text-indicator-error\">\n <WarningCircle size={16} /> {errorMessage}\n </p>\n )}\n </div>\n </div>\n );\n};\n\nconst SelectValue = ({\n placeholder,\n store: externalStore,\n}: {\n placeholder?: string;\n store?: SelectStoreApi;\n}) => {\n const store = useSelectStore(externalStore);\n\n const selectedLabel = useStore(store, (s) => s.selectedLabel);\n const value = useStore(store, (s) => s.value);\n return (\n <span className=\"text-inherit\">\n {selectedLabel || placeholder || value}\n </span>\n );\n};\n\ninterface SelectTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n className?: string;\n invalid?: boolean;\n variant?: 'outlined' | 'underlined' | 'rounded';\n store?: SelectStoreApi;\n size?: 'small' | 'medium' | 'large' | 'extra-large';\n selectId?: string;\n}\n\nconst SelectTrigger = forwardRef<HTMLButtonElement, SelectTriggerProps>(\n (\n {\n className,\n invalid = false,\n variant = 'outlined',\n store: externalStore,\n disabled,\n size = 'medium',\n selectId,\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const toggleOpen = () => store.setState({ open: !open });\n\n const variantClasses = VARIANT_CLASSES[variant];\n const heightClasses = HEIGHT_CLASSES[size];\n const paddingClasses = PADDING_CLASSES[size];\n\n return (\n <button\n ref={ref}\n id={selectId}\n className={`\n flex min-w-[220px] w-full items-center justify-between border-border-300\n ${heightClasses} ${paddingClasses}\n ${invalid && `${variant == 'underlined' ? 'border-b-2' : 'border-2'} border-indicator-error text-text-600`}\n ${\n disabled\n ? 'cursor-not-allowed text-text-400 pointer-events-none opacity-50'\n : 'cursor-pointer hover:bg-background-50 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground'\n }\n ${!invalid && !disabled ? 'text-text-700' : ''}\n ${variantClasses}\n ${className}\n `}\n onClick={toggleOpen}\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-controls={open ? 'select-content' : undefined}\n {...props}\n >\n {props.children}\n <CaretDown\n className={`h-[1em] w-[1em] opacity-50 transition-transform ${open ? 'rotate-180' : ''}`}\n />\n </button>\n );\n }\n);\nSelectTrigger.displayName = 'SelectTrigger';\n\ninterface SelectContentProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n align?: 'start' | 'center' | 'end';\n side?: 'top' | 'right' | 'bottom' | 'left';\n store?: SelectStoreApi;\n}\n\nconst SelectContent = forwardRef<HTMLDivElement, SelectContentProps>(\n (\n {\n children,\n className,\n align = 'start',\n side = 'bottom',\n store: externalStore,\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n\n const open = useStore(store, (s) => s.open);\n if (!open) return null;\n\n const getPositionClasses = () =>\n `w-full min-w-full absolute ${SIDE_CLASSES[side]} ${ALIGN_CLASSES[align]}`;\n\n return (\n <div\n role=\"menu\"\n ref={ref}\n className={`bg-background z-50 min-w-[210px] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md border-border-100 ${getPositionClasses()} ${className}`}\n {...props}\n >\n {children}\n </div>\n );\n }\n);\nSelectContent.displayName = 'SelectContent';\n\ninterface SelectItemProps extends HTMLAttributes<HTMLDivElement> {\n value: string;\n disabled?: boolean;\n store?: SelectStoreApi;\n}\n\nconst SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(\n (\n {\n className,\n children,\n value,\n disabled = false,\n store: externalStore,\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n const {\n value: selectedValue,\n setValue,\n setOpen,\n setSelectedLabel,\n onValueChange,\n } = useStore(store, (s) => s);\n\n const handleClick = (\n e: MouseEvent<HTMLDivElement> | KeyboardEvent<HTMLDivElement>\n ) => {\n const labelNode = getLabelAsNode(children);\n if (!disabled) {\n setValue(value);\n setSelectedLabel(labelNode);\n setOpen(false);\n onValueChange?.(value);\n }\n props.onClick?.(e as MouseEvent<HTMLDivElement>);\n };\n\n return (\n <div\n role=\"menuitem\"\n aria-disabled={disabled}\n ref={ref}\n className={`\n focus-visible:bg-background-50\n relative flex select-none items-center gap-2 rounded-sm p-3 outline-none transition-colors [&>svg]:size-4 [&>svg]:shrink-0\n ${className}\n ${\n disabled\n ? 'cursor-not-allowed text-text-400 pointer-events-none opacity-50'\n : 'cursor-pointer hover:bg-background-50 text-text-700 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground'\n }\n ${selectedValue === value && 'bg-background-50'}\n `}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') handleClick(e);\n }}\n tabIndex={disabled ? -1 : 0}\n {...props}\n >\n <span className=\"absolute right-2 flex h-3.5 w-3.5 items-center justify-center\">\n {selectedValue === value && <Check className=\"\" />}\n </span>\n {children}\n </div>\n );\n }\n);\n\nSelectItem.displayName = 'SelectItem';\n\nexport default Select;\nexport { SelectTrigger, SelectContent, SelectItem, SelectValue };\n"],"mappings":"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","names":["children","label"]}
|
package/dist/Select/index.mjs
CHANGED
|
@@ -6,19 +6,33 @@ import {
|
|
|
6
6
|
forwardRef,
|
|
7
7
|
isValidElement,
|
|
8
8
|
Children,
|
|
9
|
-
cloneElement
|
|
9
|
+
cloneElement,
|
|
10
|
+
useId
|
|
10
11
|
} from "react";
|
|
11
|
-
import { CaretDown, Check } from "phosphor-react";
|
|
12
|
+
import { CaretDown, Check, WarningCircle } from "phosphor-react";
|
|
12
13
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
14
|
var VARIANT_CLASSES = {
|
|
14
|
-
outlined: "border rounded-
|
|
15
|
+
outlined: "border rounded-lg focus:border-primary-950",
|
|
15
16
|
underlined: "border-b focus:border-primary-950",
|
|
16
|
-
rounded: "border rounded-
|
|
17
|
+
rounded: "border rounded-full focus:border-primary-950"
|
|
17
18
|
};
|
|
18
19
|
var SIZE_CLASSES = {
|
|
19
20
|
small: "text-sm",
|
|
20
21
|
medium: "text-md",
|
|
21
|
-
large: "text-lg"
|
|
22
|
+
large: "text-lg",
|
|
23
|
+
"extra-large": "text-lg"
|
|
24
|
+
};
|
|
25
|
+
var HEIGHT_CLASSES = {
|
|
26
|
+
small: "h-8",
|
|
27
|
+
medium: "h-9",
|
|
28
|
+
large: "h-10",
|
|
29
|
+
"extra-large": "h-12"
|
|
30
|
+
};
|
|
31
|
+
var PADDING_CLASSES = {
|
|
32
|
+
small: "px-2 py-1",
|
|
33
|
+
medium: "px-3 py-2",
|
|
34
|
+
large: "px-4 py-3",
|
|
35
|
+
"extra-large": "px-5 py-4"
|
|
22
36
|
};
|
|
23
37
|
var SIDE_CLASSES = {
|
|
24
38
|
top: "bottom-full -translate-y-1",
|
|
@@ -31,14 +45,15 @@ var ALIGN_CLASSES = {
|
|
|
31
45
|
center: "left-1/2 -translate-x-1/2",
|
|
32
46
|
end: "right-0"
|
|
33
47
|
};
|
|
34
|
-
function createSelectStore() {
|
|
48
|
+
function createSelectStore(onValueChange) {
|
|
35
49
|
return create((set) => ({
|
|
36
50
|
open: false,
|
|
37
51
|
setOpen: (open) => set({ open }),
|
|
38
52
|
value: "",
|
|
39
53
|
setValue: (value) => set({ value }),
|
|
40
54
|
selectedLabel: "",
|
|
41
|
-
setSelectedLabel: (label) => set({ selectedLabel: label })
|
|
55
|
+
setSelectedLabel: (label) => set({ selectedLabel: label }),
|
|
56
|
+
onValueChange
|
|
42
57
|
}));
|
|
43
58
|
}
|
|
44
59
|
var useSelectStore = (externalStore) => {
|
|
@@ -57,15 +72,24 @@ function getLabelAsNode(children) {
|
|
|
57
72
|
if (flattened.length === 1) return flattened[0];
|
|
58
73
|
return /* @__PURE__ */ jsx(Fragment, { children: flattened });
|
|
59
74
|
}
|
|
60
|
-
var injectStore = (children, store) => {
|
|
75
|
+
var injectStore = (children, store, size, selectId) => {
|
|
61
76
|
return Children.map(children, (child) => {
|
|
62
77
|
if (isValidElement(child)) {
|
|
63
78
|
const typedChild = child;
|
|
64
79
|
const newProps = {
|
|
65
80
|
store
|
|
66
81
|
};
|
|
82
|
+
if (typedChild.type === SelectTrigger) {
|
|
83
|
+
newProps.size = size;
|
|
84
|
+
newProps.selectId = selectId;
|
|
85
|
+
}
|
|
67
86
|
if (typedChild.props.children) {
|
|
68
|
-
newProps.children = injectStore(
|
|
87
|
+
newProps.children = injectStore(
|
|
88
|
+
typedChild.props.children,
|
|
89
|
+
store,
|
|
90
|
+
size,
|
|
91
|
+
selectId
|
|
92
|
+
);
|
|
69
93
|
}
|
|
70
94
|
return cloneElement(typedChild, newProps);
|
|
71
95
|
}
|
|
@@ -77,16 +101,19 @@ var Select = ({
|
|
|
77
101
|
defaultValue = "",
|
|
78
102
|
value: propValue,
|
|
79
103
|
onValueChange,
|
|
80
|
-
size = "small"
|
|
104
|
+
size = "small",
|
|
105
|
+
label,
|
|
106
|
+
helperText,
|
|
107
|
+
errorMessage,
|
|
108
|
+
id
|
|
81
109
|
}) => {
|
|
82
110
|
const storeRef = useRef(null);
|
|
83
|
-
storeRef.current ??= createSelectStore();
|
|
111
|
+
storeRef.current ??= createSelectStore(onValueChange);
|
|
84
112
|
const store = storeRef.current;
|
|
85
113
|
const selectRef = useRef(null);
|
|
86
|
-
const { open, setOpen, setValue,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
);
|
|
114
|
+
const { open, setOpen, setValue, selectedLabel } = useStore(store, (s) => s);
|
|
115
|
+
const generatedId = useId();
|
|
116
|
+
const selectId = id ?? `select-${generatedId}`;
|
|
90
117
|
const findLabelForValue = (children2, targetValue) => {
|
|
91
118
|
let found = null;
|
|
92
119
|
const search = (nodes) => {
|
|
@@ -106,8 +133,8 @@ var Select = ({
|
|
|
106
133
|
};
|
|
107
134
|
useEffect(() => {
|
|
108
135
|
if (!selectedLabel && defaultValue) {
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
136
|
+
const label2 = findLabelForValue(children, defaultValue);
|
|
137
|
+
if (label2) store.setState({ selectedLabel: label2 });
|
|
111
138
|
}
|
|
112
139
|
}, [children, defaultValue, selectedLabel]);
|
|
113
140
|
useEffect(() => {
|
|
@@ -145,19 +172,33 @@ var Select = ({
|
|
|
145
172
|
document.removeEventListener("keydown", handleArrowKeys);
|
|
146
173
|
};
|
|
147
174
|
}, [open]);
|
|
148
|
-
useEffect(() => {
|
|
149
|
-
setValue(value);
|
|
150
|
-
onValueChange?.(value);
|
|
151
|
-
}, [value, onValueChange]);
|
|
152
175
|
useEffect(() => {
|
|
153
176
|
if (propValue) {
|
|
154
177
|
setValue(propValue);
|
|
155
|
-
const
|
|
156
|
-
if (
|
|
178
|
+
const label2 = findLabelForValue(children, propValue);
|
|
179
|
+
if (label2) store.setState({ selectedLabel: label2 });
|
|
157
180
|
}
|
|
158
181
|
}, [propValue]);
|
|
159
182
|
const sizeClasses = SIZE_CLASSES[size];
|
|
160
|
-
return /* @__PURE__ */
|
|
183
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
|
|
184
|
+
label && /* @__PURE__ */ jsx(
|
|
185
|
+
"label",
|
|
186
|
+
{
|
|
187
|
+
htmlFor: selectId,
|
|
188
|
+
className: `block font-bold text-text-900 mb-1.5 ${sizeClasses}`,
|
|
189
|
+
children: label
|
|
190
|
+
}
|
|
191
|
+
),
|
|
192
|
+
/* @__PURE__ */ jsx("div", { className: `relative ${sizeClasses}`, ref: selectRef, children: injectStore(children, store, size, selectId) }),
|
|
193
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1.5 gap-1.5", children: [
|
|
194
|
+
helperText && /* @__PURE__ */ jsx("p", { className: "text-sm text-text-500", children: helperText }),
|
|
195
|
+
errorMessage && /* @__PURE__ */ jsxs("p", { className: "flex gap-1 items-center text-sm text-indicator-error", children: [
|
|
196
|
+
/* @__PURE__ */ jsx(WarningCircle, { size: 16 }),
|
|
197
|
+
" ",
|
|
198
|
+
errorMessage
|
|
199
|
+
] })
|
|
200
|
+
] })
|
|
201
|
+
] });
|
|
161
202
|
};
|
|
162
203
|
var SelectValue = ({
|
|
163
204
|
placeholder,
|
|
@@ -175,18 +216,24 @@ var SelectTrigger = forwardRef(
|
|
|
175
216
|
variant = "outlined",
|
|
176
217
|
store: externalStore,
|
|
177
218
|
disabled,
|
|
219
|
+
size = "medium",
|
|
220
|
+
selectId,
|
|
178
221
|
...props
|
|
179
222
|
}, ref) => {
|
|
180
223
|
const store = useSelectStore(externalStore);
|
|
181
224
|
const open = useStore(store, (s) => s.open);
|
|
182
225
|
const toggleOpen = () => store.setState({ open: !open });
|
|
183
226
|
const variantClasses = VARIANT_CLASSES[variant];
|
|
227
|
+
const heightClasses = HEIGHT_CLASSES[size];
|
|
228
|
+
const paddingClasses = PADDING_CLASSES[size];
|
|
184
229
|
return /* @__PURE__ */ jsxs(
|
|
185
230
|
"button",
|
|
186
231
|
{
|
|
187
232
|
ref,
|
|
233
|
+
id: selectId,
|
|
188
234
|
className: `
|
|
189
|
-
flex
|
|
235
|
+
flex min-w-[220px] w-full items-center justify-between border-border-300
|
|
236
|
+
${heightClasses} ${paddingClasses}
|
|
190
237
|
${invalid && `${variant == "underlined" ? "border-b-2" : "border-2"} border-indicator-error text-text-600`}
|
|
191
238
|
${disabled ? "cursor-not-allowed text-text-400 pointer-events-none opacity-50" : "cursor-pointer hover:bg-background-50 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground"}
|
|
192
239
|
${!invalid && !disabled ? "text-text-700" : ""}
|
|
@@ -252,7 +299,8 @@ var SelectItem = forwardRef(
|
|
|
252
299
|
value: selectedValue,
|
|
253
300
|
setValue,
|
|
254
301
|
setOpen,
|
|
255
|
-
setSelectedLabel
|
|
302
|
+
setSelectedLabel,
|
|
303
|
+
onValueChange
|
|
256
304
|
} = useStore(store, (s) => s);
|
|
257
305
|
const handleClick = (e) => {
|
|
258
306
|
const labelNode = getLabelAsNode(children);
|
|
@@ -260,6 +308,7 @@ var SelectItem = forwardRef(
|
|
|
260
308
|
setValue(value);
|
|
261
309
|
setSelectedLabel(labelNode);
|
|
262
310
|
setOpen(false);
|
|
311
|
+
onValueChange?.(value);
|
|
263
312
|
}
|
|
264
313
|
props.onClick?.(e);
|
|
265
314
|
};
|