@spear-ai/spectral 1.16.4 → 1.16.6

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.
@@ -0,0 +1,373 @@
1
+ 'use client';
2
+ import { CloseCircleIcon } from "./Icons/CloseCircleIcon.js";
3
+ import { ErrorIcon } from "./Icons/ErrorIcon.js";
4
+ import { LoaderIcon } from "./Icons/LoaderIcon.js";
5
+ import { PlusIcon } from "./Icons/PlusIcon.js";
6
+ import { SearchIcon } from "./Icons/SearchIcon.js";
7
+ import { WarningIcon } from "./Icons/WarningIcon.js";
8
+ import { cn } from "./utils/twUtils.js";
9
+ import { ErrorMessage, WarningMessage } from "./FormFieldMessage.js";
10
+ import { EmptyState, getAriaProps, getDropdownSurfaceClasses, getDropdownWidthStyles, getErrorMessageId, getFormFieldCSSProperties, getInputClasses, getOptionClasses, useFormFieldId, useFormFieldState } from "./utils/formFieldUtils.js";
11
+ import { useUncontrolledState } from "./hooks/useUncontrolledState.js";
12
+ import { Label } from "./Label.js";
13
+ import { useAutoDropdownHorizontalShift } from "./utils/dropdownPositioning.js";
14
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
15
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
16
+ import { createPortal } from "react-dom";
17
+
18
+ //#region src/components/InputSearch/InputSearch.tsx
19
+ const defaultCreateOptionLabel = (query) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(PlusIcon, {
20
+ size: 16,
21
+ className: "shrink-0"
22
+ }), /* @__PURE__ */ jsxs("span", {
23
+ className: "truncate",
24
+ children: ["Create ", /* @__PURE__ */ jsxs("span", {
25
+ className: "font-semibold",
26
+ children: [
27
+ "“",
28
+ query,
29
+ "”"
30
+ ]
31
+ })]
32
+ })] });
33
+ const InputSearch = ({ className, createOptionLabel = defaultCreateOptionLabel, creatingLabel = "Creating…", defaultValue = "", disabled, dropdownWidth = "trigger", emptyMessage = "No options found", errorMessage, id, isCreating = false, label, labelClassName, messageReserveLines = 1, messageReserveSpace = false, name, onChange, onCreate, onValueChange, openOnFocus = false, options, placeholder = "Search…", ref, renderOption, required, showSearchIcon = true, state = "default", value: valueProp, warningMessage, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel }) => {
34
+ if (!label && !ariaLabel) console.warn("InputSearch: provide either `label` or `aria-label` for an accessible name.");
35
+ const fieldId = useFormFieldId(id, name);
36
+ const listboxId = `${fieldId}-listbox`;
37
+ const errorMessageId = getErrorMessageId(fieldId);
38
+ const warningMessageId = `${fieldId}-warning`;
39
+ const messageId = state === "error" ? errorMessageId : state === "warning" && warningMessage ? warningMessageId : void 0;
40
+ const { isDisabled, isLoading, isInvalid } = useFormFieldState(disabled, state);
41
+ const ariaProps = getAriaProps(state, ariaDescribedBy, required, messageId);
42
+ const [value, setValue] = useUncontrolledState({
43
+ value: valueProp,
44
+ defaultValue,
45
+ onChange: (nextValue) => {
46
+ if (onChange) onChange(nextValue);
47
+ else onValueChange?.(nextValue);
48
+ }
49
+ });
50
+ const [query, setQuery] = useState("");
51
+ const [isFocused, setIsFocused] = useState(false);
52
+ const [highlightedIndex, setHighlightedIndex] = useState(-1);
53
+ const [dropdownPosition, setDropdownPosition] = useState(null);
54
+ const inputRef = useRef(null);
55
+ const listRef = useRef(null);
56
+ const blurTimeoutRef = useRef(null);
57
+ const triggerRef = useRef(null);
58
+ const selectedOption = useMemo(() => options.find((option) => option.value === value) ?? null, [options, value]);
59
+ useEffect(() => {
60
+ if (!isFocused) setQuery(selectedOption?.label ?? "");
61
+ }, [selectedOption, isFocused]);
62
+ const trimmedQuery = query.trim();
63
+ const filteredOptions = useMemo(() => {
64
+ if (trimmedQuery.length === 0) return options;
65
+ const lower = trimmedQuery.toLowerCase();
66
+ return options.filter((option) => option.label.toLowerCase().includes(lower));
67
+ }, [trimmedQuery, options]);
68
+ const exactMatch = useMemo(() => options.some((option) => option.label.toLowerCase() === trimmedQuery.toLowerCase()), [options, trimmedQuery]);
69
+ const showCreateOption = onCreate !== void 0 && trimmedQuery.length > 0 && !exactMatch;
70
+ const createOptionIndex = showCreateOption ? filteredOptions.length : -1;
71
+ const totalItems = filteredOptions.length + (showCreateOption ? 1 : 0);
72
+ const showDropdown = isFocused && !isDisabled && !isLoading && (openOnFocus || trimmedQuery.length > 0);
73
+ const showEmptyState = showDropdown && totalItems === 0;
74
+ const { dropdownShiftStyle, recalculateDropdownPosition, setDropdownElement } = useAutoDropdownHorizontalShift(showDropdown);
75
+ const updateDropdownPosition = useCallback(() => {
76
+ const trigger = triggerRef.current;
77
+ if (!trigger) return;
78
+ const rect = trigger.getBoundingClientRect();
79
+ setDropdownPosition({
80
+ top: rect.bottom + 4,
81
+ left: rect.left,
82
+ width: rect.width
83
+ });
84
+ recalculateDropdownPosition();
85
+ }, [recalculateDropdownPosition]);
86
+ useLayoutEffect(() => {
87
+ if (!showDropdown) {
88
+ setDropdownPosition(null);
89
+ return;
90
+ }
91
+ updateDropdownPosition();
92
+ const handle = () => updateDropdownPosition();
93
+ window.addEventListener("scroll", handle, true);
94
+ window.addEventListener("resize", handle);
95
+ return () => {
96
+ window.removeEventListener("scroll", handle, true);
97
+ window.removeEventListener("resize", handle);
98
+ };
99
+ }, [showDropdown, updateDropdownPosition]);
100
+ useLayoutEffect(() => {
101
+ if (!showDropdown) return;
102
+ updateDropdownPosition();
103
+ }, [
104
+ showDropdown,
105
+ updateDropdownPosition,
106
+ filteredOptions.length,
107
+ showCreateOption
108
+ ]);
109
+ useEffect(() => {
110
+ setHighlightedIndex(-1);
111
+ }, [filteredOptions.length, showCreateOption]);
112
+ useEffect(() => {
113
+ if (highlightedIndex < 0 || !listRef.current) return;
114
+ listRef.current.querySelector(`[data-index='${highlightedIndex}']`)?.scrollIntoView({ block: "nearest" });
115
+ }, [highlightedIndex]);
116
+ useEffect(() => () => {
117
+ if (blurTimeoutRef.current !== null) clearTimeout(blurTimeoutRef.current);
118
+ }, []);
119
+ const commitSelection = useCallback((option) => {
120
+ if (option.disabled) return;
121
+ setValue(option.value);
122
+ setQuery(option.label);
123
+ setIsFocused(false);
124
+ setHighlightedIndex(-1);
125
+ inputRef.current?.blur();
126
+ }, [setValue]);
127
+ const commitCreate = useCallback(() => {
128
+ if (!onCreate || isCreating || trimmedQuery.length === 0) return;
129
+ onCreate(trimmedQuery);
130
+ }, [
131
+ onCreate,
132
+ isCreating,
133
+ trimmedQuery
134
+ ]);
135
+ const handleKeyDown = useCallback((event) => {
136
+ switch (event.key) {
137
+ case "ArrowDown":
138
+ if (totalItems === 0) return;
139
+ event.preventDefault();
140
+ setHighlightedIndex((prev) => prev < totalItems - 1 ? prev + 1 : prev);
141
+ break;
142
+ case "ArrowUp":
143
+ if (totalItems === 0) return;
144
+ event.preventDefault();
145
+ setHighlightedIndex((prev) => prev > 0 ? prev - 1 : 0);
146
+ break;
147
+ case "Enter":
148
+ if (totalItems === 0) return;
149
+ event.preventDefault();
150
+ if (highlightedIndex >= 0 && highlightedIndex < filteredOptions.length) {
151
+ const option = filteredOptions[highlightedIndex];
152
+ if (option) commitSelection(option);
153
+ } else if (highlightedIndex === createOptionIndex && !isCreating) commitCreate();
154
+ break;
155
+ case "Escape":
156
+ event.preventDefault();
157
+ setIsFocused(false);
158
+ setHighlightedIndex(-1);
159
+ if (selectedOption) setQuery(selectedOption.label);
160
+ inputRef.current?.blur();
161
+ break;
162
+ default: break;
163
+ }
164
+ }, [
165
+ totalItems,
166
+ filteredOptions,
167
+ highlightedIndex,
168
+ createOptionIndex,
169
+ commitSelection,
170
+ commitCreate,
171
+ isCreating,
172
+ selectedOption
173
+ ]);
174
+ const handleFocus = useCallback(() => {
175
+ if (blurTimeoutRef.current !== null) {
176
+ clearTimeout(blurTimeoutRef.current);
177
+ blurTimeoutRef.current = null;
178
+ }
179
+ setIsFocused(true);
180
+ setQuery("");
181
+ }, []);
182
+ const handleBlur = useCallback(() => {
183
+ if (blurTimeoutRef.current !== null) clearTimeout(blurTimeoutRef.current);
184
+ blurTimeoutRef.current = setTimeout(() => {
185
+ blurTimeoutRef.current = null;
186
+ setIsFocused(false);
187
+ setHighlightedIndex(-1);
188
+ }, 150);
189
+ }, []);
190
+ const handleClear = useCallback(() => {
191
+ setValue("");
192
+ setQuery("");
193
+ inputRef.current?.focus();
194
+ }, [setValue]);
195
+ const showClearButton = !isLoading && !isDisabled && (value.length > 0 || isFocused && query.length > 0);
196
+ const { dropdownWidthMode, dropdownWidthStyle } = getDropdownWidthStyles({
197
+ dropdownWidth,
198
+ triggerWidth: "100%"
199
+ });
200
+ const inputClasses = cn(getInputClasses(state, className), "pe-12", showSearchIcon && "ps-11", showClearButton && "pe-10", state === "loading" && "cursor-wait");
201
+ return /* @__PURE__ */ jsxs("div", {
202
+ className: "flex w-full flex-col gap-1.5",
203
+ "data-testid": "spectral-input-search-container",
204
+ ref,
205
+ children: [
206
+ label && /* @__PURE__ */ jsx(Label, {
207
+ className: cn("mb-2 block", labelClassName, isDisabled && "cursor-not-allowed text-input-text--disabled"),
208
+ "data-testid": "spectral-input-search-label",
209
+ htmlFor: fieldId,
210
+ children: label
211
+ }),
212
+ /* @__PURE__ */ jsx("div", {
213
+ className: "relative",
214
+ "data-testid": "spectral-input-search-wrapper",
215
+ ref: triggerRef,
216
+ children: /* @__PURE__ */ jsxs("div", {
217
+ className: "relative",
218
+ children: [
219
+ showSearchIcon && /* @__PURE__ */ jsx("span", {
220
+ "aria-hidden": "true",
221
+ className: "left-4 text-input-icon absolute top-1/2 -translate-y-1/2",
222
+ "data-testid": "spectral-input-search-icon",
223
+ children: /* @__PURE__ */ jsx(SearchIcon, { size: 20 })
224
+ }),
225
+ /* @__PURE__ */ jsx("input", {
226
+ "aria-activedescendant": highlightedIndex >= 0 ? `${fieldId}-option-${highlightedIndex}` : void 0,
227
+ "aria-autocomplete": "list",
228
+ "aria-controls": listboxId,
229
+ "aria-expanded": showDropdown,
230
+ "aria-haspopup": "listbox",
231
+ "aria-label": ariaLabel ?? label,
232
+ autoComplete: "off",
233
+ className: inputClasses,
234
+ "data-state": state,
235
+ "data-testid": "spectral-input-search",
236
+ disabled: isDisabled || isLoading,
237
+ id: fieldId,
238
+ name,
239
+ onBlur: handleBlur,
240
+ onChange: (event) => {
241
+ setQuery(event.target.value);
242
+ if (!isFocused) setIsFocused(true);
243
+ },
244
+ onFocus: handleFocus,
245
+ onKeyDown: handleKeyDown,
246
+ placeholder,
247
+ ref: inputRef,
248
+ required,
249
+ role: "combobox",
250
+ style: getFormFieldCSSProperties(),
251
+ type: "text",
252
+ value: query,
253
+ ...ariaProps
254
+ }),
255
+ showClearButton ? /* @__PURE__ */ jsx("button", {
256
+ "aria-label": `Clear ${label ?? "search"}`,
257
+ className: "right-4 text-input-icon hover:text-input-icon--hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-input-border--focus rounded-sm absolute top-1/2 -translate-y-1/2 cursor-pointer",
258
+ "data-testid": "spectral-input-search-clear-button",
259
+ onClick: handleClear,
260
+ onMouseDown: (event) => event.preventDefault(),
261
+ type: "button",
262
+ children: /* @__PURE__ */ jsx(CloseCircleIcon, { size: 20 })
263
+ }) : state === "loading" ? /* @__PURE__ */ jsx("div", {
264
+ className: "right-4 text-input-icon absolute top-1/2 -translate-y-1/2",
265
+ "data-testid": "spectral-input-search-loading-icon",
266
+ children: /* @__PURE__ */ jsx(LoaderIcon, {
267
+ className: "motion-safe:animate-spin",
268
+ size: 20
269
+ })
270
+ }) : state === "error" ? /* @__PURE__ */ jsx("div", {
271
+ className: "right-4 text-danger-400 absolute top-1/2 -translate-y-1/2",
272
+ "data-testid": "spectral-input-search-error-icon",
273
+ children: /* @__PURE__ */ jsx(ErrorIcon, { size: 20 })
274
+ }) : state === "warning" ? /* @__PURE__ */ jsx("div", {
275
+ className: "right-4 text-warning-400 absolute top-1/2 -translate-y-1/2",
276
+ "data-testid": "spectral-input-search-warning-icon",
277
+ children: /* @__PURE__ */ jsx(WarningIcon, { size: 20 })
278
+ }) : null
279
+ ]
280
+ })
281
+ }),
282
+ showDropdown && dropdownPosition && typeof document !== "undefined" ? createPortal(/* @__PURE__ */ jsx("div", {
283
+ className: cn("origin-top p-1 z-50 fixed", getDropdownSurfaceClasses(), "max-h-72 overflow-hidden", "motion-safe:animate-in motion-safe:fade-in-0 motion-safe:zoom-in-95 motion-safe:slide-in-from-top-2"),
284
+ "data-dropdown-width-mode": dropdownWidthMode,
285
+ "data-dropdown-width-value": dropdownWidthMode === "custom" ? dropdownWidth : void 0,
286
+ "data-testid": "spectral-input-search-content",
287
+ ref: setDropdownElement,
288
+ style: {
289
+ top: `${dropdownPosition.top}px`,
290
+ left: `${dropdownPosition.left}px`,
291
+ ...dropdownWidthMode === "trigger" ? { width: `${dropdownPosition.width}px` } : dropdownWidthStyle,
292
+ ...dropdownShiftStyle
293
+ },
294
+ children: /* @__PURE__ */ jsx("div", {
295
+ className: "max-h-[17.5rem] overflow-y-auto",
296
+ id: listboxId,
297
+ ref: listRef,
298
+ role: "listbox",
299
+ children: showEmptyState ? /* @__PURE__ */ jsx(EmptyState, { message: emptyMessage }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [filteredOptions.map((option, index) => {
300
+ const isHighlighted = index === highlightedIndex;
301
+ const isSelected = option.value === value;
302
+ return /* @__PURE__ */ jsx("button", {
303
+ "aria-selected": isSelected,
304
+ className: cn(getOptionClasses(!!option.disabled, isHighlighted, isSelected), "text-left"),
305
+ "data-highlighted": isHighlighted ? "" : void 0,
306
+ "data-index": index,
307
+ "data-testid": "spectral-input-search-item",
308
+ disabled: option.disabled,
309
+ id: `${fieldId}-option-${index}`,
310
+ onMouseDown: (event) => {
311
+ event.preventDefault();
312
+ commitSelection(option);
313
+ },
314
+ onMouseEnter: () => setHighlightedIndex(index),
315
+ role: "option",
316
+ tabIndex: -1,
317
+ type: "button",
318
+ children: /* @__PURE__ */ jsx("span", {
319
+ className: "min-w-0 flex-1 truncate",
320
+ children: renderOption ? renderOption(option) : option.label
321
+ })
322
+ }, option.value);
323
+ }), showCreateOption && /* @__PURE__ */ jsx("button", {
324
+ "aria-selected": createOptionIndex === highlightedIndex,
325
+ className: cn(getOptionClasses(isCreating, createOptionIndex === highlightedIndex, false), "gap-2 text-left text-accent font-medium"),
326
+ "data-highlighted": createOptionIndex === highlightedIndex ? "" : void 0,
327
+ "data-index": createOptionIndex,
328
+ "data-testid": "spectral-input-search-create-option",
329
+ disabled: isCreating,
330
+ id: `${fieldId}-option-${createOptionIndex}`,
331
+ onMouseDown: (event) => {
332
+ event.preventDefault();
333
+ commitCreate();
334
+ },
335
+ onMouseEnter: () => setHighlightedIndex(createOptionIndex),
336
+ role: "option",
337
+ tabIndex: -1,
338
+ type: "button",
339
+ children: isCreating ? /* @__PURE__ */ jsxs("span", {
340
+ className: "gap-2 flex items-center",
341
+ children: [/* @__PURE__ */ jsx(LoaderIcon, {
342
+ className: "shrink-0 motion-safe:animate-spin",
343
+ size: 16
344
+ }), /* @__PURE__ */ jsx("span", { children: creatingLabel })]
345
+ }) : /* @__PURE__ */ jsx("span", {
346
+ className: "gap-2 flex items-center min-w-0",
347
+ children: createOptionLabel(trimmedQuery)
348
+ })
349
+ })] })
350
+ })
351
+ }), document.body) : null,
352
+ /* @__PURE__ */ jsx(ErrorMessage, {
353
+ dataTestId: "spectral-input-search-error-message",
354
+ id: errorMessageId,
355
+ message: isInvalid ? errorMessage ?? null : null,
356
+ messageReserveLines,
357
+ messageReserveSpace: messageReserveSpace && state === "error"
358
+ }),
359
+ /* @__PURE__ */ jsx(WarningMessage, {
360
+ dataTestId: "spectral-input-search-warning-message",
361
+ id: warningMessageId,
362
+ message: state === "warning" ? warningMessage ?? null : null,
363
+ messageReserveLines,
364
+ messageReserveSpace: messageReserveSpace && state === "warning"
365
+ })
366
+ ]
367
+ });
368
+ };
369
+ InputSearch.displayName = "InputSearch";
370
+
371
+ //#endregion
372
+ export { InputSearch };
373
+ //# sourceMappingURL=InputSearch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputSearch.js","names":[],"sources":["../src/components/InputSearch/InputSearch.tsx"],"sourcesContent":["import { CloseCircleIcon, ErrorIcon, LoaderIcon, PlusIcon, SearchIcon, WarningIcon } from '@components/Icons'\nimport { Label } from '@components/Label/Label'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { useAutoDropdownHorizontalShift } from '@utils/dropdownPositioning'\nimport {\n EmptyState,\n ErrorMessage,\n WarningMessage,\n getAriaProps,\n getDropdownSurfaceClasses,\n getDropdownWidthStyles,\n getErrorMessageId,\n getFormFieldCSSProperties,\n getInputClasses,\n getOptionClasses,\n useFormFieldId,\n useFormFieldState,\n type BaseFormFieldProps,\n type BaseOption,\n type DropdownWidth,\n type FormFieldState,\n} from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type CSSProperties, type KeyboardEvent, type ReactNode, type Ref } from 'react'\nimport { createPortal } from 'react-dom'\n\nexport type InputSearchOption = BaseOption\n\nexport interface InputSearchProps extends Omit<BaseFormFieldProps, 'onChange' | 'state'> {\n className?: string\n createOptionLabel?: (query: string) => ReactNode\n creatingLabel?: string\n defaultValue?: string\n dropdownWidth?: DropdownWidth\n emptyMessage?: string\n isCreating?: boolean\n labelClassName?: string\n onChange?: (value: string) => void\n onCreate?: (query: string) => void\n onValueChange?: (value: string) => void\n openOnFocus?: boolean\n options: InputSearchOption[]\n placeholder?: string\n renderOption?: (option: InputSearchOption) => ReactNode\n showSearchIcon?: boolean\n state?: Exclude<FormFieldState, 'disabled'>\n value?: string\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nconst defaultCreateOptionLabel = (query: string): ReactNode => (\n <>\n <PlusIcon size={16} className='shrink-0' />\n <span className='truncate'>\n Create <span className='font-semibold'>&ldquo;{query}&rdquo;</span>\n </span>\n </>\n)\n\nexport const InputSearch = ({\n className,\n createOptionLabel = defaultCreateOptionLabel,\n creatingLabel = 'Creating…',\n defaultValue = '',\n disabled,\n dropdownWidth = 'trigger',\n emptyMessage = 'No options found',\n errorMessage,\n id,\n isCreating = false,\n label,\n labelClassName,\n messageReserveLines = 1,\n messageReserveSpace = false,\n name,\n onChange,\n onCreate,\n onValueChange,\n openOnFocus = false,\n options,\n placeholder = 'Search…',\n ref,\n renderOption,\n required,\n showSearchIcon = true,\n state = 'default',\n value: valueProp,\n warningMessage,\n 'aria-describedby': ariaDescribedBy,\n 'aria-label': ariaLabel,\n}: InputSearchProps & { ref?: Ref<HTMLDivElement> }) => {\n if (process.env.NODE_ENV !== 'production' && !label && !ariaLabel) {\n // eslint-disable-next-line no-console\n console.warn('InputSearch: provide either `label` or `aria-label` for an accessible name.')\n }\n\n const fieldId = useFormFieldId(id, name)\n const listboxId = `${fieldId}-listbox`\n const errorMessageId = getErrorMessageId(fieldId)\n const warningMessageId = `${fieldId}-warning`\n const messageId = state === 'error' ? errorMessageId : state === 'warning' && warningMessage ? warningMessageId : undefined\n const { isDisabled, isLoading, isInvalid } = useFormFieldState(disabled, state)\n const ariaProps = getAriaProps(state, ariaDescribedBy, required, messageId)\n\n const [value, setValue] = useUncontrolledState<string>({\n value: valueProp,\n defaultValue,\n onChange: (nextValue) => {\n if (onChange) {\n onChange(nextValue)\n } else {\n onValueChange?.(nextValue)\n }\n },\n })\n\n const [query, setQuery] = useState('')\n const [isFocused, setIsFocused] = useState(false)\n const [highlightedIndex, setHighlightedIndex] = useState(-1)\n const [dropdownPosition, setDropdownPosition] = useState<{ top: number; left: number; width: number } | null>(null)\n const inputRef = useRef<HTMLInputElement>(null)\n const listRef = useRef<HTMLDivElement>(null)\n const blurTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const triggerRef = useRef<HTMLDivElement>(null)\n\n const selectedOption = useMemo(() => options.find((option) => option.value === value) ?? null, [options, value])\n\n useEffect(() => {\n if (!isFocused) {\n setQuery(selectedOption?.label ?? '')\n }\n }, [selectedOption, isFocused])\n\n const trimmedQuery = query.trim()\n\n const filteredOptions = useMemo(() => {\n if (trimmedQuery.length === 0) return options\n const lower = trimmedQuery.toLowerCase()\n return options.filter((option) => option.label.toLowerCase().includes(lower))\n }, [trimmedQuery, options])\n\n const exactMatch = useMemo(() => options.some((option) => option.label.toLowerCase() === trimmedQuery.toLowerCase()), [options, trimmedQuery])\n\n const showCreateOption = onCreate !== undefined && trimmedQuery.length > 0 && !exactMatch\n const createOptionIndex = showCreateOption ? filteredOptions.length : -1\n const totalItems = filteredOptions.length + (showCreateOption ? 1 : 0)\n const showDropdown = isFocused && !isDisabled && !isLoading && (openOnFocus || trimmedQuery.length > 0)\n const showEmptyState = showDropdown && totalItems === 0\n\n const { dropdownShiftStyle, recalculateDropdownPosition, setDropdownElement } = useAutoDropdownHorizontalShift(showDropdown)\n\n const updateDropdownPosition = useCallback(() => {\n const trigger = triggerRef.current\n if (!trigger) return\n const rect = trigger.getBoundingClientRect()\n setDropdownPosition({\n top: rect.bottom + 4,\n left: rect.left,\n width: rect.width,\n })\n recalculateDropdownPosition()\n }, [recalculateDropdownPosition])\n\n useLayoutEffect(() => {\n if (!showDropdown) {\n setDropdownPosition(null)\n return\n }\n updateDropdownPosition()\n const handle = () => updateDropdownPosition()\n window.addEventListener('scroll', handle, true)\n window.addEventListener('resize', handle)\n return () => {\n window.removeEventListener('scroll', handle, true)\n window.removeEventListener('resize', handle)\n }\n }, [showDropdown, updateDropdownPosition])\n\n useLayoutEffect(() => {\n if (!showDropdown) return\n updateDropdownPosition()\n }, [showDropdown, updateDropdownPosition, filteredOptions.length, showCreateOption])\n\n useEffect(() => {\n setHighlightedIndex(-1)\n }, [filteredOptions.length, showCreateOption])\n\n useEffect(() => {\n if (highlightedIndex < 0 || !listRef.current) return\n const element = listRef.current.querySelector<HTMLElement>(`[data-index='${highlightedIndex}']`)\n element?.scrollIntoView({ block: 'nearest' })\n }, [highlightedIndex])\n\n useEffect(\n () => () => {\n if (blurTimeoutRef.current !== null) {\n clearTimeout(blurTimeoutRef.current)\n }\n },\n [],\n )\n\n const commitSelection = useCallback(\n (option: InputSearchOption) => {\n if (option.disabled) return\n setValue(option.value)\n setQuery(option.label)\n setIsFocused(false)\n setHighlightedIndex(-1)\n inputRef.current?.blur()\n },\n [setValue],\n )\n\n const commitCreate = useCallback(() => {\n if (!onCreate || isCreating || trimmedQuery.length === 0) return\n onCreate(trimmedQuery)\n }, [onCreate, isCreating, trimmedQuery])\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n switch (event.key) {\n case 'ArrowDown': {\n if (totalItems === 0) return\n event.preventDefault()\n setHighlightedIndex((prev) => (prev < totalItems - 1 ? prev + 1 : prev))\n break\n }\n case 'ArrowUp': {\n if (totalItems === 0) return\n event.preventDefault()\n setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : 0))\n break\n }\n case 'Enter': {\n if (totalItems === 0) return\n event.preventDefault()\n if (highlightedIndex >= 0 && highlightedIndex < filteredOptions.length) {\n const option = filteredOptions[highlightedIndex]\n if (option) commitSelection(option)\n } else if (highlightedIndex === createOptionIndex && !isCreating) {\n commitCreate()\n }\n break\n }\n case 'Escape': {\n event.preventDefault()\n setIsFocused(false)\n setHighlightedIndex(-1)\n if (selectedOption) setQuery(selectedOption.label)\n inputRef.current?.blur()\n break\n }\n default:\n break\n }\n },\n [totalItems, filteredOptions, highlightedIndex, createOptionIndex, commitSelection, commitCreate, isCreating, selectedOption],\n )\n\n const handleFocus = useCallback(() => {\n if (blurTimeoutRef.current !== null) {\n clearTimeout(blurTimeoutRef.current)\n blurTimeoutRef.current = null\n }\n setIsFocused(true)\n setQuery('')\n }, [])\n\n const handleBlur = useCallback(() => {\n if (blurTimeoutRef.current !== null) {\n clearTimeout(blurTimeoutRef.current)\n }\n blurTimeoutRef.current = setTimeout(() => {\n blurTimeoutRef.current = null\n // Don't write to `query` here — the useEffect tied to selectedOption + isFocused\n // is the single source of truth for the visible query when the field isn't focused.\n // Setting it from this stale closure causes off-by-one display after selection.\n setIsFocused(false)\n setHighlightedIndex(-1)\n }, 150)\n }, [])\n\n const handleClear = useCallback(() => {\n setValue('')\n setQuery('')\n inputRef.current?.focus()\n }, [setValue])\n\n const showClearButton = !isLoading && !isDisabled && (value.length > 0 || (isFocused && query.length > 0))\n\n const { dropdownWidthMode, dropdownWidthStyle } = getDropdownWidthStyles({\n dropdownWidth,\n triggerWidth: '100%',\n })\n\n const inputClasses = cn(getInputClasses(state, className), 'pe-12', showSearchIcon && 'ps-11', showClearButton && 'pe-10', state === 'loading' && 'cursor-wait')\n\n return (\n <div className='flex w-full flex-col gap-1.5' data-testid='spectral-input-search-container' ref={ref}>\n {label && (\n <Label className={cn('mb-2 block', labelClassName, isDisabled && 'cursor-not-allowed text-input-text--disabled')} data-testid='spectral-input-search-label' htmlFor={fieldId}>\n {label}\n </Label>\n )}\n\n <div className='relative' data-testid='spectral-input-search-wrapper' ref={triggerRef}>\n <div className='relative'>\n {showSearchIcon && (\n <span aria-hidden='true' className='left-4 text-input-icon absolute top-1/2 -translate-y-1/2' data-testid='spectral-input-search-icon'>\n <SearchIcon size={20} />\n </span>\n )}\n\n <input\n aria-activedescendant={highlightedIndex >= 0 ? `${fieldId}-option-${highlightedIndex}` : undefined}\n aria-autocomplete='list'\n aria-controls={listboxId}\n aria-expanded={showDropdown}\n // oxlint-disable-next-line jsx-a11y/role-supports-aria-props -- Valid per WAI-ARIA 1.2 Combobox pattern\n aria-haspopup='listbox'\n aria-label={ariaLabel ?? label}\n autoComplete='off'\n className={inputClasses}\n data-state={state}\n data-testid='spectral-input-search'\n disabled={isDisabled || isLoading}\n id={fieldId}\n name={name}\n onBlur={handleBlur}\n onChange={(event) => {\n setQuery(event.target.value)\n if (!isFocused) setIsFocused(true)\n }}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n ref={inputRef}\n required={required}\n role='combobox'\n style={getFormFieldCSSProperties() as CSSProperties}\n type='text'\n value={query}\n {...ariaProps}\n />\n\n {showClearButton ? (\n <button\n aria-label={`Clear ${label ?? 'search'}`}\n className='right-4 text-input-icon hover:text-input-icon--hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-input-border--focus rounded-sm absolute top-1/2 -translate-y-1/2 cursor-pointer'\n data-testid='spectral-input-search-clear-button'\n onClick={handleClear}\n // Prevent input blur from firing before click resolves\n onMouseDown={(event) => event.preventDefault()}\n type='button'\n >\n <CloseCircleIcon size={20} />\n </button>\n ) : state === 'loading' ? (\n <div className='right-4 text-input-icon absolute top-1/2 -translate-y-1/2' data-testid='spectral-input-search-loading-icon'>\n <LoaderIcon className='motion-safe:animate-spin' size={20} />\n </div>\n ) : state === 'error' ? (\n <div className='right-4 text-danger-400 absolute top-1/2 -translate-y-1/2' data-testid='spectral-input-search-error-icon'>\n <ErrorIcon size={20} />\n </div>\n ) : state === 'warning' ? (\n <div className='right-4 text-warning-400 absolute top-1/2 -translate-y-1/2' data-testid='spectral-input-search-warning-icon'>\n <WarningIcon size={20} />\n </div>\n ) : null}\n </div>\n\n </div>\n\n {showDropdown && dropdownPosition && typeof document !== 'undefined'\n ? createPortal(\n <div\n className={cn('origin-top p-1 z-50 fixed', getDropdownSurfaceClasses(), 'max-h-72 overflow-hidden', 'motion-safe:animate-in motion-safe:fade-in-0 motion-safe:zoom-in-95 motion-safe:slide-in-from-top-2')}\n data-dropdown-width-mode={dropdownWidthMode}\n data-dropdown-width-value={dropdownWidthMode === 'custom' ? dropdownWidth : undefined}\n data-testid='spectral-input-search-content'\n ref={setDropdownElement}\n style={{\n top: `${dropdownPosition.top}px`,\n left: `${dropdownPosition.left}px`,\n ...(dropdownWidthMode === 'trigger' ? { width: `${dropdownPosition.width}px` } : (dropdownWidthStyle as CSSProperties)),\n ...dropdownShiftStyle,\n }}\n >\n <div\n className='max-h-[17.5rem] overflow-y-auto'\n id={listboxId}\n ref={listRef}\n // oxlint-disable-next-line jsx-a11y/prefer-tag-over-role -- WAI-ARIA combobox pattern requires role='listbox' on the popup\n role='listbox'\n >\n {showEmptyState ? (\n <EmptyState message={emptyMessage} />\n ) : (\n <>\n {filteredOptions.map((option, index) => {\n const isHighlighted = index === highlightedIndex\n const isSelected = option.value === value\n return (\n <button\n aria-selected={isSelected}\n className={cn(getOptionClasses(!!option.disabled, isHighlighted, isSelected), 'text-left')}\n data-highlighted={isHighlighted ? '' : undefined}\n data-index={index}\n data-testid='spectral-input-search-item'\n disabled={option.disabled}\n id={`${fieldId}-option-${index}`}\n key={option.value}\n onMouseDown={(event) => {\n event.preventDefault()\n commitSelection(option)\n }}\n onMouseEnter={() => setHighlightedIndex(index)}\n role='option'\n tabIndex={-1}\n type='button'\n >\n <span className='min-w-0 flex-1 truncate'>{renderOption ? renderOption(option) : option.label}</span>\n </button>\n )\n })}\n\n {showCreateOption && (\n <button\n aria-selected={createOptionIndex === highlightedIndex}\n className={cn(getOptionClasses(isCreating, createOptionIndex === highlightedIndex, false), 'gap-2 text-left text-accent font-medium')}\n data-highlighted={createOptionIndex === highlightedIndex ? '' : undefined}\n data-index={createOptionIndex}\n data-testid='spectral-input-search-create-option'\n disabled={isCreating}\n id={`${fieldId}-option-${createOptionIndex}`}\n onMouseDown={(event) => {\n event.preventDefault()\n commitCreate()\n }}\n onMouseEnter={() => setHighlightedIndex(createOptionIndex)}\n role='option'\n tabIndex={-1}\n type='button'\n >\n {isCreating ? (\n <span className='gap-2 flex items-center'>\n <LoaderIcon className='shrink-0 motion-safe:animate-spin' size={16} />\n <span>{creatingLabel}</span>\n </span>\n ) : (\n <span className='gap-2 flex items-center min-w-0'>{createOptionLabel(trimmedQuery)}</span>\n )}\n </button>\n )}\n </>\n )}\n </div>\n </div>,\n document.body,\n )\n : null}\n\n <ErrorMessage\n dataTestId='spectral-input-search-error-message'\n id={errorMessageId}\n message={isInvalid ? (errorMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'error'}\n />\n <WarningMessage\n dataTestId='spectral-input-search-warning-message'\n id={warningMessageId}\n message={state === 'warning' ? (warningMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'warning'}\n />\n </div>\n )\n}\nInputSearch.displayName = 'InputSearch'\n"],"mappings":"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"}
@@ -10,7 +10,7 @@ import { useUncontrolledState } from "../hooks/useUncontrolledState.js";
10
10
  import { Label } from "../Label.js";
11
11
  import { useAutoDropdownHorizontalShift } from "../utils/dropdownPositioning.js";
12
12
  import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
13
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
14
14
  import * as Popover from "@radix-ui/react-popover";
15
15
 
16
16
  //#region src/components/MultiSelect/MultiSelectBase.tsx
@@ -393,7 +393,7 @@ const MultiSelectBase = ({ className, clearAllLabel = "Clear all", closeOnSelect
393
393
  className: "text-sm",
394
394
  "data-testid": "spectral-multiselect-empty-message",
395
395
  message: emptyMessage
396
- }) : /* @__PURE__ */ jsxs(Fragment, { children: [
396
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
397
397
  showSelectAll && /* @__PURE__ */ jsxs("div", {
398
398
  className: "mb-1",
399
399
  children: [/* @__PURE__ */ jsx("button", {
package/dist/Select.js CHANGED
@@ -9,7 +9,7 @@ import { Label } from "./Label.js";
9
9
  import { useAutoDropdownHorizontalShift } from "./utils/dropdownPositioning.js";
10
10
  import { SelectValue } from "./primitives/select.js";
11
11
  import { useState } from "react";
12
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
13
  import * as SelectPrimitive from "@radix-ui/react-select";
14
14
 
15
15
  //#region src/components/Select/Select.tsx
@@ -73,7 +73,7 @@ const Select = (allProps) => {
73
73
  })]
74
74
  }, option.value);
75
75
  };
76
- return /* @__PURE__ */ jsxs(Fragment, { children: [ungrouped.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [ungrouped.map(renderOption), Object.keys(groups).length > 0 && /* @__PURE__ */ jsx(SelectPrimitive.Separator, {
76
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [ungrouped.length > 0 && /* @__PURE__ */ jsxs(Fragment$1, { children: [ungrouped.map(renderOption), Object.keys(groups).length > 0 && /* @__PURE__ */ jsx(SelectPrimitive.Separator, {
77
77
  className: "-mx-1 my-1 h-px bg-border-secondary",
78
78
  "data-testid": "spectral-select-separator"
79
79
  })] }), Object.entries(groups).map(([groupName, groupOptions], groupIndex) => /* @__PURE__ */ jsxs(SelectPrimitive.Group, {
@@ -1 +1 @@
1
- {"version":3,"file":"Select.js","names":[],"sources":["../src/components/Select/Select.tsx"],"sourcesContent":["import { CheckmarkIcon, ChevronDownIcon, LoaderIcon } from '@components/Icons'\nimport { Label } from '@components/Label/Label'\nimport { SelectValue } from '@primitives/select'\nimport * as SelectPrimitive from '@radix-ui/react-select'\nimport { useAutoDropdownHorizontalShift } from '@utils/dropdownPositioning'\nimport {\n EmptyState,\n ErrorMessage,\n getAriaProps,\n getDropdownWidthStyles,\n getDropdownSurfaceClasses,\n getErrorMessageId,\n getFormFieldCSSProperties,\n getOptionClasses,\n getTriggerClasses,\n groupOptions,\n LoadingState,\n WarningMessage,\n useFormFieldId,\n type BaseFormFieldProps,\n type BaseOption,\n type DropdownWidth,\n type FormFieldState,\n} from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useState, type ComponentPropsWithoutRef, type CSSProperties, type ReactNode, type Ref } from 'react'\n\ntype SelectOption = BaseOption\ntype Align = 'start' | 'center' | 'end'\ntype Side = 'top' | 'bottom' | 'left' | 'right'\n\nexport interface SelectProps extends Omit<ComponentPropsWithoutRef<'button'>, 'value' | 'onChange' | 'aria-disabled' | 'aria-invalid' | 'aria-required' | 'aria-describedby' | 'aria-label'>, Omit<BaseFormFieldProps, 'state'> {\n defaultValue?: string\n dropdownWidth?: DropdownWidth\n emptyMessage?: ReactNode\n id?: string\n label?: string\n labelClassName?: string\n loadingMessage?: string\n onChange?: (value: string) => void\n onValueChange?: (value: string) => void\n options: SelectOption[]\n placeholder?: string\n state?: Exclude<FormFieldState, 'disabled'>\n value?: string\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nexport interface SelectExtendedProps extends SelectProps {\n align?: Align\n alignOffset?: number\n avoidCollisions?: boolean\n collisionBoundary?: Element | Element[] | null\n collisionPadding?: number | Partial<Record<Side, number>>\n position?: 'popper' | 'item-aligned'\n side?: Side\n sideOffset?: number\n}\n\nexport const Select = (\n allProps: SelectExtendedProps & {\n ref?: Ref<HTMLButtonElement>\n },\n) => {\n const isControlled = 'value' in allProps\n const {\n align = 'start',\n alignOffset = 0,\n avoidCollisions = true,\n className,\n collisionBoundary,\n collisionPadding = 10,\n defaultValue,\n dropdownWidth = 'trigger',\n emptyMessage = 'No options found',\n errorMessage,\n disabled,\n id,\n label,\n labelClassName,\n loadingMessage = 'Loading…',\n messageReserveLines = 1,\n messageReserveSpace = false,\n name,\n onChange,\n onValueChange,\n options,\n placeholder = 'Select an option',\n position = 'popper',\n ref,\n required,\n side = 'bottom',\n sideOffset = 4,\n state = 'default',\n value: valueProp,\n warningMessage,\n 'aria-label': ariaLabel,\n 'aria-describedby': ariaDescribedBy,\n ...props\n } = allProps\n const value = isControlled ? (valueProp ?? '') : valueProp\n const [open, setOpen] = useState(false)\n const { dropdownShiftStyle, setDropdownElement } = useAutoDropdownHorizontalShift(open)\n const selectId = useFormFieldId(id, name)\n const listboxId = `${selectId}-listbox`\n const errorMessageId = getErrorMessageId(selectId)\n const warningMessageId = `${selectId}-warning`\n const messageId = state === 'error' && errorMessage ? errorMessageId : state === 'warning' && warningMessage ? warningMessageId : undefined\n const { dropdownWidthMode, dropdownWidthStyle, resolvedDropdownWidth } = getDropdownWidthStyles({\n dropdownWidth,\n triggerWidth: 'var(--radix-select-trigger-width)',\n })\n const selectContentStyle = {\n '--spectral-select-content-width': resolvedDropdownWidth,\n ...(position === 'item-aligned' ? { width: resolvedDropdownWidth } : {}),\n ...dropdownWidthStyle,\n ...dropdownShiftStyle,\n } as CSSProperties\n const isDisabled = Boolean(disabled)\n const isLoading = state === 'loading'\n const isInvalid = state === 'error'\n const ariaProps = getAriaProps(state, ariaDescribedBy, required, messageId)\n const { groups, ungrouped } = groupOptions(options)\n const handleValueChange = (nextValue: string) => {\n onChange?.(nextValue)\n onValueChange?.(nextValue)\n }\n\n const renderOptions = () => {\n if (isLoading) {\n return <LoadingState data-testid='spectral-select-loading' message={loadingMessage} />\n }\n\n if (options.length === 0) {\n return <EmptyState data-testid='spectral-select-empty' message={emptyMessage} />\n }\n\n const renderOption = (option: SelectOption) => {\n const isSelected = value === option.value\n\n return (\n <SelectPrimitive.Item className={cn(getOptionClasses(!!option.disabled, false, isSelected), 'relative flex w-full cursor-pointer items-center')} data-testid='spectral-select-item' disabled={option.disabled} key={option.value} value={option.value}>\n <SelectPrimitive.ItemText data-testid='spectral-select-item-text' className='block truncate'>\n {option.label}\n </SelectPrimitive.ItemText>\n <SelectPrimitive.ItemIndicator data-testid='spectral-select-item-selected-indicator' asChild>\n <span className='right-2 h-4 w-4 absolute flex items-center justify-center'>\n <CheckmarkIcon size={16} />\n </span>\n </SelectPrimitive.ItemIndicator>\n </SelectPrimitive.Item>\n )\n }\n\n return (\n <>\n {ungrouped.length > 0 && (\n <>\n {ungrouped.map(renderOption)}\n {Object.keys(groups).length > 0 && <SelectPrimitive.Separator className='-mx-1 my-1 h-px bg-border-secondary' data-testid='spectral-select-separator' />}\n </>\n )}\n\n {Object.entries(groups).map(([groupName, groupOptions], groupIndex) => (\n <SelectPrimitive.Group key={groupName} data-testid='spectral-select-group'>\n {groupIndex > 0 && <SelectPrimitive.Separator className='-mx-1 my-1 h-px bg-border-secondary' data-testid='spectral-select-group-separator' />}\n <Label className={cn('px-2 py-1.5 text-base font-semibold text-text-primary', labelClassName)} data-testid='spectral-select-group-label'>\n {groupName}\n </Label>\n {groupOptions.map((option: BaseOption) => renderOption(option))}\n </SelectPrimitive.Group>\n ))}\n </>\n )\n }\n\n return (\n <div className='w-full'>\n {label && (\n <Label className={cn('mb-2 block text-text-primary', labelClassName, isDisabled && 'text-text-secondary')} data-testid='spectral-select-label' htmlFor={selectId}>\n {label}\n </Label>\n )}\n <SelectPrimitive.Root data-testid='spectral-select' defaultValue={defaultValue} disabled={isDisabled} name={name} onOpenChange={setOpen} onValueChange={handleValueChange} open={open} required={required} value={value}>\n <SelectPrimitive.Trigger\n aria-controls={listboxId}\n aria-expanded={open}\n aria-label={ariaLabel ?? label}\n asChild\n className={cn(getTriggerClasses(open, state), 'text-input-text data-placeholder:text-input-text-placeholder!', className)}\n data-slot='select-trigger'\n data-state={state}\n data-testid='spectral-select-trigger'\n id={selectId}\n ref={ref}\n style={getFormFieldCSSProperties() as CSSProperties}\n {...ariaProps}\n {...props}\n >\n <button\n className='min-w-0 gap-2 [&>span]:min-w-0 grid w-full cursor-pointer grid-cols-[minmax(0,1fr)_auto] items-center overflow-hidden text-left whitespace-nowrap text-input-text! data-placeholder:text-input-text-placeholder! [&>span]:block [&>span]:overflow-hidden [&>span]:text-ellipsis [&>span]:whitespace-nowrap [&>span[data-placeholder]]:text-input-text-placeholder!'\n type='button'\n disabled={isDisabled}\n >\n <SelectValue data-testid='spectral-select-value' placeholder={placeholder} />\n <SelectPrimitive.Icon asChild>\n <div className='flex shrink-0 cursor-pointer items-center'>{isLoading ? <LoaderIcon size={20} /> : <ChevronDownIcon className={cn('transition-transform duration-200', open && 'rotate-180')} size={20} />}</div>\n </SelectPrimitive.Icon>\n </button>\n </SelectPrimitive.Trigger>\n\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n align={align}\n alignOffset={alignOffset}\n avoidCollisions={avoidCollisions}\n className={cn(\n 'relative z-50 motion-safe:data-[state=closed]:animate-out motion-safe:data-[state=open]:animate-in',\n getDropdownSurfaceClasses(),\n 'motion-safe:data-[state=closed]:fade-out-0 motion-safe:data-[state=closed]:zoom-out-95 motion-safe:data-[state=open]:fade-in-0 motion-safe:data-[state=open]:zoom-in-95',\n 'max-h-[min(var(--radix-select-content-available-height),300px)] motion-safe:data-[side=bottom]:slide-in-from-top-2 motion-safe:data-[side=top]:slide-in-from-bottom-2',\n 'min-w-32 origin-(--radix-select-content-transform-origin) overflow-hidden',\n position === 'popper' && 'data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1',\n )}\n collisionBoundary={collisionBoundary}\n collisionPadding={collisionPadding}\n data-dropdown-width-mode={dropdownWidthMode}\n data-dropdown-width-value={dropdownWidthMode === 'custom' ? dropdownWidth : undefined}\n id={listboxId}\n data-slot='select-content'\n data-testid='spectral-select-content'\n position={position}\n ref={setDropdownElement}\n side={side}\n sideOffset={sideOffset}\n style={selectContentStyle}\n >\n <SelectPrimitive.ScrollUpButton className='py-1 flex cursor-default items-center justify-center' data-testid='spectral-select-scroll-up-button'>\n <ChevronDownIcon aria-hidden='true' className='rotate-180' size={18} />\n </SelectPrimitive.ScrollUpButton>\n\n <SelectPrimitive.Viewport asChild>\n <div\n className={cn(\n 'p-1 overflow-x-hidden overflow-y-auto',\n position === 'popper' && (dropdownWidth === 'trigger' ? 'scroll-my-1 h-(--radix-select-trigger-height) w-(--spectral-select-content-width) min-w-(--spectral-select-content-width)' : 'scroll-my-1 h-(--radix-select-trigger-height)'),\n )}\n data-testid='spectral-select-items'\n >\n {renderOptions()}\n </div>\n </SelectPrimitive.Viewport>\n\n <SelectPrimitive.ScrollDownButton className='py-1 flex cursor-default items-center justify-center' data-testid='spectral-select-scroll-down-button'>\n <ChevronDownIcon aria-hidden='true' size={18} />\n </SelectPrimitive.ScrollDownButton>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n </SelectPrimitive.Root>\n\n <ErrorMessage\n dataTestId='spectral-select-error-message'\n id={errorMessageId}\n message={isInvalid ? (errorMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'error'}\n />\n <WarningMessage\n dataTestId='spectral-select-warning-message'\n id={warningMessageId}\n message={state === 'warning' ? (warningMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'warning'}\n />\n </div>\n )\n}\nSelect.displayName = 'Select'\n"],"mappings":";;;;;;;;;;;;;;;AA2DA,MAAa,UACX,aAGG;CACH,MAAM,eAAe,WAAW;CAChC,MAAM,EACJ,QAAQ,SACR,cAAc,GACd,kBAAkB,MAClB,WACA,mBACA,mBAAmB,IACnB,cACA,gBAAgB,WAChB,eAAe,oBACf,cACA,UACA,IACA,OACA,gBACA,iBAAiB,YACjB,sBAAsB,GACtB,sBAAsB,OACtB,MACA,UACA,eACA,SACA,cAAc,oBACd,WAAW,UACX,KACA,UACA,OAAO,UACP,aAAa,GACb,QAAQ,WACR,OAAO,WACP,gBACA,cAAc,WACd,oBAAoB,iBACpB,GAAG,UACD;CACJ,MAAM,QAAQ,eAAgB,aAAa,KAAM;CACjD,MAAM,CAAC,MAAM,WAAW,SAAS,MAAM;CACvC,MAAM,EAAE,oBAAoB,uBAAuB,+BAA+B,KAAK;CACvF,MAAM,WAAW,eAAe,IAAI,KAAK;CACzC,MAAM,YAAY,GAAG,SAAS;CAC9B,MAAM,iBAAiB,kBAAkB,SAAS;CAClD,MAAM,mBAAmB,GAAG,SAAS;CACrC,MAAM,YAAY,UAAU,WAAW,eAAe,iBAAiB,UAAU,aAAa,iBAAiB,mBAAmB;CAClI,MAAM,EAAE,mBAAmB,oBAAoB,0BAA0B,uBAAuB;EAC9F;EACA,cAAc;EACf,CAAC;CACF,MAAM,qBAAqB;EACzB,mCAAmC;EACnC,GAAI,aAAa,iBAAiB,EAAE,OAAO,uBAAuB,GAAG,EAAE;EACvE,GAAG;EACH,GAAG;EACJ;CACD,MAAM,aAAa,QAAQ,SAAS;CACpC,MAAM,YAAY,UAAU;CAC5B,MAAM,YAAY,UAAU;CAC5B,MAAM,YAAY,aAAa,OAAO,iBAAiB,UAAU,UAAU;CAC3E,MAAM,EAAE,QAAQ,cAAc,aAAa,QAAQ;CACnD,MAAM,qBAAqB,cAAsB;AAC/C,aAAW,UAAU;AACrB,kBAAgB,UAAU;;CAG5B,MAAM,sBAAsB;AAC1B,MAAI,UACF,QAAO,oBAAC,cAAD;GAAc,eAAY;GAA0B,SAAS;GAAkB;AAGxF,MAAI,QAAQ,WAAW,EACrB,QAAO,oBAAC,YAAD;GAAY,eAAY;GAAwB,SAAS;GAAgB;EAGlF,MAAM,gBAAgB,WAAyB;GAC7C,MAAM,aAAa,UAAU,OAAO;AAEpC,UACE,qBAAC,gBAAgB,MAAjB;IAAsB,WAAW,GAAG,iBAAiB,CAAC,CAAC,OAAO,UAAU,OAAO,WAAW,EAAE,mDAAmD;IAAE,eAAY;IAAuB,UAAU,OAAO;IAA6B,OAAO,OAAO;cAAhP,CACE,oBAAC,gBAAgB,UAAjB;KAA0B,eAAY;KAA4B,WAAU;eACzE,OAAO;KACiB,GAC3B,oBAAC,gBAAgB,eAAjB;KAA+B,eAAY;KAA0C;eACnF,oBAAC,QAAD;MAAM,WAAU;gBACd,oBAAC,eAAD,EAAe,MAAM,IAAM;MACtB;KACuB,EACX;MAT6L,OAAO,MASpM;;AAI3B,SACE,4CACG,UAAU,SAAS,KAClB,4CACG,UAAU,IAAI,aAAa,EAC3B,OAAO,KAAK,OAAO,CAAC,SAAS,KAAK,oBAAC,gBAAgB,WAAjB;GAA2B,WAAU;GAAsC,eAAY;GAA8B,EACvJ,KAGJ,OAAO,QAAQ,OAAO,CAAC,KAAK,CAAC,WAAW,eAAe,eACtD,qBAAC,gBAAgB,OAAjB;GAAuC,eAAY;aAAnD;IACG,aAAa,KAAK,oBAAC,gBAAgB,WAAjB;KAA2B,WAAU;KAAsC,eAAY;KAAoC;IAC9I,oBAAC,OAAD;KAAO,WAAW,GAAG,yDAAyD,eAAe;KAAE,eAAY;eACxG;KACK;IACP,aAAa,KAAK,WAAuB,aAAa,OAAO,CAAC;IACzC;KANI,UAMJ,CACxB,CACD;;AAIP,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GACG,SACC,oBAAC,OAAD;IAAO,WAAW,GAAG,gCAAgC,gBAAgB,cAAc,sBAAsB;IAAE,eAAY;IAAwB,SAAS;cACrJ;IACK;GAEV,qBAAC,gBAAgB,MAAjB;IAAsB,eAAY;IAAgC;IAAc,UAAU;IAAkB;IAAM,cAAc;IAAS,eAAe;IAAyB;IAAgB;IAAiB;cAAlN,CACE,oBAAC,gBAAgB,SAAjB;KACE,iBAAe;KACf,iBAAe;KACf,cAAY,aAAa;KACzB;KACA,WAAW,GAAG,kBAAkB,MAAM,MAAM,EAAE,iEAAiE,UAAU;KACzH,aAAU;KACV,cAAY;KACZ,eAAY;KACZ,IAAI;KACC;KACL,OAAO,2BAA2B;KAClC,GAAI;KACJ,GAAI;eAEJ,qBAAC,UAAD;MACE,WAAU;MACV,MAAK;MACL,UAAU;gBAHZ,CAKE,oBAAC,aAAD;OAAa,eAAY;OAAqC;OAAe,GAC7E,oBAAC,gBAAgB,MAAjB;OAAsB;iBACpB,oBAAC,OAAD;QAAK,WAAU;kBAA6C,YAAY,oBAAC,YAAD,EAAY,MAAM,IAAM,IAAG,oBAAC,iBAAD;SAAiB,WAAW,GAAG,qCAAqC,QAAQ,aAAa;SAAE,MAAM;SAAM;QAAO;OAC5L,EAChB;;KACe,GAE1B,oBAAC,gBAAgB,QAAjB,YACE,qBAAC,gBAAgB,SAAjB;KACS;KACM;KACI;KACjB,WAAW,GACT,sGACA,2BAA2B,EAC3B,2KACA,yKACA,6EACA,aAAa,YAAY,kEAC1B;KACkB;KACD;KAClB,4BAA0B;KAC1B,6BAA2B,sBAAsB,WAAW,gBAAgB;KAC5E,IAAI;KACJ,aAAU;KACV,eAAY;KACF;KACV,KAAK;KACC;KACM;KACZ,OAAO;eAvBT;MAyBE,oBAAC,gBAAgB,gBAAjB;OAAgC,WAAU;OAAuD,eAAY;iBAC3G,oBAAC,iBAAD;QAAiB,eAAY;QAAO,WAAU;QAAa,MAAM;QAAM;OACxC;MAEjC,oBAAC,gBAAgB,UAAjB;OAA0B;iBACxB,oBAAC,OAAD;QACE,WAAW,GACT,yCACA,aAAa,aAAa,kBAAkB,YAAY,8HAA8H,iDACvL;QACD,eAAY;kBAEX,eAAe;QACZ;OACmB;MAE3B,oBAAC,gBAAgB,kBAAjB;OAAkC,WAAU;OAAuD,eAAY;iBAC7G,oBAAC,iBAAD;QAAiB,eAAY;QAAO,MAAM;QAAM;OACf;MACX;QACH,EACJ;;GAEvB,oBAAC,cAAD;IACE,YAAW;IACX,IAAI;IACJ,SAAS,YAAa,gBAAgB,OAAQ;IACzB;IACrB,qBAAqB,uBAAuB,UAAU;IACtD;GACF,oBAAC,gBAAD;IACE,YAAW;IACX,IAAI;IACJ,SAAS,UAAU,YAAa,kBAAkB,OAAQ;IACrC;IACrB,qBAAqB,uBAAuB,UAAU;IACtD;GACE;;;AAGV,OAAO,cAAc"}
1
+ {"version":3,"file":"Select.js","names":[],"sources":["../src/components/Select/Select.tsx"],"sourcesContent":["import { CheckmarkIcon, ChevronDownIcon, LoaderIcon } from '@components/Icons'\nimport { Label } from '@components/Label/Label'\nimport { SelectValue } from '@primitives/select'\nimport * as SelectPrimitive from '@radix-ui/react-select'\nimport { useAutoDropdownHorizontalShift } from '@utils/dropdownPositioning'\nimport {\n EmptyState,\n ErrorMessage,\n getAriaProps,\n getDropdownWidthStyles,\n getDropdownSurfaceClasses,\n getErrorMessageId,\n getFormFieldCSSProperties,\n getOptionClasses,\n getTriggerClasses,\n groupOptions,\n LoadingState,\n WarningMessage,\n useFormFieldId,\n type BaseFormFieldProps,\n type BaseOption,\n type DropdownWidth,\n type FormFieldState,\n} from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useState, type ComponentPropsWithoutRef, type CSSProperties, type ReactNode, type Ref } from 'react'\n\ntype SelectOption = BaseOption\ntype Align = 'start' | 'center' | 'end'\ntype Side = 'top' | 'bottom' | 'left' | 'right'\n\nexport interface SelectProps extends Omit<ComponentPropsWithoutRef<'button'>, 'value' | 'onChange' | 'aria-disabled' | 'aria-invalid' | 'aria-required' | 'aria-describedby' | 'aria-label'>, Omit<BaseFormFieldProps, 'state'> {\n defaultValue?: string\n dropdownWidth?: DropdownWidth\n emptyMessage?: ReactNode\n id?: string\n label?: string\n labelClassName?: string\n loadingMessage?: string\n onChange?: (value: string) => void\n onValueChange?: (value: string) => void\n options: SelectOption[]\n placeholder?: string\n state?: Exclude<FormFieldState, 'disabled'>\n value?: string\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nexport interface SelectExtendedProps extends SelectProps {\n align?: Align\n alignOffset?: number\n avoidCollisions?: boolean\n collisionBoundary?: Element | Element[] | null\n collisionPadding?: number | Partial<Record<Side, number>>\n position?: 'popper' | 'item-aligned'\n side?: Side\n sideOffset?: number\n}\n\nexport const Select = (\n allProps: SelectExtendedProps & {\n ref?: Ref<HTMLButtonElement>\n },\n) => {\n const isControlled = 'value' in allProps\n const {\n align = 'start',\n alignOffset = 0,\n avoidCollisions = true,\n className,\n collisionBoundary,\n collisionPadding = 10,\n defaultValue,\n dropdownWidth = 'trigger',\n emptyMessage = 'No options found',\n errorMessage,\n disabled,\n id,\n label,\n labelClassName,\n loadingMessage = 'Loading…',\n messageReserveLines = 1,\n messageReserveSpace = false,\n name,\n onChange,\n onValueChange,\n options,\n placeholder = 'Select an option',\n position = 'popper',\n ref,\n required,\n side = 'bottom',\n sideOffset = 4,\n state = 'default',\n value: valueProp,\n warningMessage,\n 'aria-label': ariaLabel,\n 'aria-describedby': ariaDescribedBy,\n ...props\n } = allProps\n const value = isControlled ? (valueProp ?? '') : valueProp\n const [open, setOpen] = useState(false)\n const { dropdownShiftStyle, setDropdownElement } = useAutoDropdownHorizontalShift(open)\n const selectId = useFormFieldId(id, name)\n const listboxId = `${selectId}-listbox`\n const errorMessageId = getErrorMessageId(selectId)\n const warningMessageId = `${selectId}-warning`\n const messageId = state === 'error' && errorMessage ? errorMessageId : state === 'warning' && warningMessage ? warningMessageId : undefined\n const { dropdownWidthMode, dropdownWidthStyle, resolvedDropdownWidth } = getDropdownWidthStyles({\n dropdownWidth,\n triggerWidth: 'var(--radix-select-trigger-width)',\n })\n const selectContentStyle = {\n '--spectral-select-content-width': resolvedDropdownWidth,\n ...(position === 'item-aligned' ? { width: resolvedDropdownWidth } : {}),\n ...dropdownWidthStyle,\n ...dropdownShiftStyle,\n } as CSSProperties\n const isDisabled = Boolean(disabled)\n const isLoading = state === 'loading'\n const isInvalid = state === 'error'\n const ariaProps = getAriaProps(state, ariaDescribedBy, required, messageId)\n const { groups, ungrouped } = groupOptions(options)\n const handleValueChange = (nextValue: string) => {\n onChange?.(nextValue)\n onValueChange?.(nextValue)\n }\n\n const renderOptions = () => {\n if (isLoading) {\n return <LoadingState data-testid='spectral-select-loading' message={loadingMessage} />\n }\n\n if (options.length === 0) {\n return <EmptyState data-testid='spectral-select-empty' message={emptyMessage} />\n }\n\n const renderOption = (option: SelectOption) => {\n const isSelected = value === option.value\n\n return (\n <SelectPrimitive.Item className={cn(getOptionClasses(!!option.disabled, false, isSelected), 'relative flex w-full cursor-pointer items-center')} data-testid='spectral-select-item' disabled={option.disabled} key={option.value} value={option.value}>\n <SelectPrimitive.ItemText data-testid='spectral-select-item-text' className='block truncate'>\n {option.label}\n </SelectPrimitive.ItemText>\n <SelectPrimitive.ItemIndicator data-testid='spectral-select-item-selected-indicator' asChild>\n <span className='right-2 h-4 w-4 absolute flex items-center justify-center'>\n <CheckmarkIcon size={16} />\n </span>\n </SelectPrimitive.ItemIndicator>\n </SelectPrimitive.Item>\n )\n }\n\n return (\n <>\n {ungrouped.length > 0 && (\n <>\n {ungrouped.map(renderOption)}\n {Object.keys(groups).length > 0 && <SelectPrimitive.Separator className='-mx-1 my-1 h-px bg-border-secondary' data-testid='spectral-select-separator' />}\n </>\n )}\n\n {Object.entries(groups).map(([groupName, groupOptions], groupIndex) => (\n <SelectPrimitive.Group key={groupName} data-testid='spectral-select-group'>\n {groupIndex > 0 && <SelectPrimitive.Separator className='-mx-1 my-1 h-px bg-border-secondary' data-testid='spectral-select-group-separator' />}\n <Label className={cn('px-2 py-1.5 text-base font-semibold text-text-primary', labelClassName)} data-testid='spectral-select-group-label'>\n {groupName}\n </Label>\n {groupOptions.map((option: BaseOption) => renderOption(option))}\n </SelectPrimitive.Group>\n ))}\n </>\n )\n }\n\n return (\n <div className='w-full'>\n {label && (\n <Label className={cn('mb-2 block text-text-primary', labelClassName, isDisabled && 'text-text-secondary')} data-testid='spectral-select-label' htmlFor={selectId}>\n {label}\n </Label>\n )}\n <SelectPrimitive.Root data-testid='spectral-select' defaultValue={defaultValue} disabled={isDisabled} name={name} onOpenChange={setOpen} onValueChange={handleValueChange} open={open} required={required} value={value}>\n <SelectPrimitive.Trigger\n aria-controls={listboxId}\n aria-expanded={open}\n aria-label={ariaLabel ?? label}\n asChild\n className={cn(getTriggerClasses(open, state), 'text-input-text data-placeholder:text-input-text-placeholder!', className)}\n data-slot='select-trigger'\n data-state={state}\n data-testid='spectral-select-trigger'\n id={selectId}\n ref={ref}\n style={getFormFieldCSSProperties() as CSSProperties}\n {...ariaProps}\n {...props}\n >\n <button\n className='min-w-0 gap-2 [&>span]:min-w-0 grid w-full cursor-pointer grid-cols-[minmax(0,1fr)_auto] items-center overflow-hidden text-left whitespace-nowrap text-input-text! data-placeholder:text-input-text-placeholder! [&>span]:block [&>span]:overflow-hidden [&>span]:text-ellipsis [&>span]:whitespace-nowrap [&>span[data-placeholder]]:text-input-text-placeholder!'\n type='button'\n disabled={isDisabled}\n >\n <SelectValue data-testid='spectral-select-value' placeholder={placeholder} />\n <SelectPrimitive.Icon asChild>\n <div className='flex shrink-0 cursor-pointer items-center'>{isLoading ? <LoaderIcon size={20} /> : <ChevronDownIcon className={cn('transition-transform duration-200', open && 'rotate-180')} size={20} />}</div>\n </SelectPrimitive.Icon>\n </button>\n </SelectPrimitive.Trigger>\n\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n align={align}\n alignOffset={alignOffset}\n avoidCollisions={avoidCollisions}\n className={cn(\n 'relative z-50 motion-safe:data-[state=closed]:animate-out motion-safe:data-[state=open]:animate-in',\n getDropdownSurfaceClasses(),\n 'motion-safe:data-[state=closed]:fade-out-0 motion-safe:data-[state=closed]:zoom-out-95 motion-safe:data-[state=open]:fade-in-0 motion-safe:data-[state=open]:zoom-in-95',\n 'max-h-[min(var(--radix-select-content-available-height),300px)] motion-safe:data-[side=bottom]:slide-in-from-top-2 motion-safe:data-[side=top]:slide-in-from-bottom-2',\n 'min-w-32 origin-(--radix-select-content-transform-origin) overflow-hidden',\n position === 'popper' && 'data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1',\n )}\n collisionBoundary={collisionBoundary}\n collisionPadding={collisionPadding}\n data-dropdown-width-mode={dropdownWidthMode}\n data-dropdown-width-value={dropdownWidthMode === 'custom' ? dropdownWidth : undefined}\n id={listboxId}\n data-slot='select-content'\n data-testid='spectral-select-content'\n position={position}\n ref={setDropdownElement}\n side={side}\n sideOffset={sideOffset}\n style={selectContentStyle}\n >\n <SelectPrimitive.ScrollUpButton className='py-1 flex cursor-default items-center justify-center' data-testid='spectral-select-scroll-up-button'>\n <ChevronDownIcon aria-hidden='true' className='rotate-180' size={18} />\n </SelectPrimitive.ScrollUpButton>\n\n <SelectPrimitive.Viewport asChild>\n <div\n className={cn(\n 'p-1 overflow-x-hidden overflow-y-auto',\n position === 'popper' && (dropdownWidth === 'trigger' ? 'scroll-my-1 h-(--radix-select-trigger-height) w-(--spectral-select-content-width) min-w-(--spectral-select-content-width)' : 'scroll-my-1 h-(--radix-select-trigger-height)'),\n )}\n data-testid='spectral-select-items'\n >\n {renderOptions()}\n </div>\n </SelectPrimitive.Viewport>\n\n <SelectPrimitive.ScrollDownButton className='py-1 flex cursor-default items-center justify-center' data-testid='spectral-select-scroll-down-button'>\n <ChevronDownIcon aria-hidden='true' size={18} />\n </SelectPrimitive.ScrollDownButton>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n </SelectPrimitive.Root>\n\n <ErrorMessage\n dataTestId='spectral-select-error-message'\n id={errorMessageId}\n message={isInvalid ? (errorMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'error'}\n />\n <WarningMessage\n dataTestId='spectral-select-warning-message'\n id={warningMessageId}\n message={state === 'warning' ? (warningMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'warning'}\n />\n </div>\n )\n}\nSelect.displayName = 'Select'\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  import { Slot } from "../primitives/slot.js";
3
3
  import { useUncontrolledState } from "../hooks/useUncontrolledState.js";
4
4
  import { createContext, useCallback, useContext, useEffect, useId, useRef } from "react";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
6
 
7
7
  //#region src/components/Switch/SwitchBase.tsx
8
8
  const SwitchCtx = createContext(null);
@@ -59,7 +59,7 @@ const Switch = ({ asChild, checked, children, className, defaultChecked = false,
59
59
  checked: isChecked,
60
60
  disabled
61
61
  },
62
- children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Comp, {
62
+ children: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Comp, {
63
63
  "aria-checked": isChecked,
64
64
  className,
65
65
  "data-disabled": disabled ?? void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"SwitchBase.js","names":[],"sources":["../../src/components/Switch/SwitchBase.tsx"],"sourcesContent":["import { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { Slot, type AsChildProp } from '@primitives/slot'\nimport { createContext, useCallback, useContext, useEffect, useId, useRef, type ButtonHTMLAttributes, type ElementType, type HTMLAttributes, type KeyboardEvent, type MouseEvent, type Ref } from 'react'\n\ninterface Ctx {\n checked: boolean\n disabled: boolean | undefined\n}\n\nconst SwitchCtx = createContext<Ctx | null>(null)\n\ntype BaseButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onChange' | 'value'>\n\nexport interface SwitchProps extends BaseButtonProps, AsChildProp {\n checked?: boolean\n defaultChecked?: boolean\n form?: string\n name?: string\n onCheckedChange?: (checked: boolean) => void\n required?: boolean\n value?: string\n}\n\nexport const Switch = ({\n asChild,\n checked,\n children,\n className,\n defaultChecked = false,\n disabled,\n form,\n id,\n name,\n onCheckedChange,\n onClick,\n onKeyDown,\n ref,\n required,\n value = 'on',\n ...rest\n}: SwitchProps & {\n ref?: Ref<HTMLButtonElement>\n}) => {\n const inputRef = useRef<HTMLInputElement | null>(null)\n const autoId = useId()\n const resolvedId = id ?? `swt-${autoId}`\n\n const [isChecked, setIsChecked] = useUncontrolledState<boolean>({\n value: checked,\n defaultValue: defaultChecked,\n onChange: onCheckedChange,\n })\n\n // form reset -> restore default\n useEffect(() => {\n const formEl = inputRef.current?.form ?? null\n if (!formEl) return\n const handleReset = () => setIsChecked(defaultChecked)\n formEl.addEventListener('reset', handleReset)\n return () => formEl.removeEventListener('reset', handleReset)\n }, [defaultChecked, setIsChecked])\n\n const emitFormChange = useCallback((checkedValue: boolean) => {\n const el = inputRef.current\n if (!el) return\n el.checked = checkedValue\n const ev = new Event('change', { bubbles: true })\n el.dispatchEvent(ev)\n }, [])\n\n const toggle = useCallback(() => {\n if (disabled) return\n const newChecked = !isChecked\n setIsChecked(newChecked)\n queueMicrotask(() => emitFormChange(newChecked))\n }, [disabled, emitFormChange, isChecked, setIsChecked])\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n onClick?.(e)\n if (e.defaultPrevented) return\n toggle()\n },\n [onClick, toggle],\n )\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n onKeyDown?.(e)\n if (e.defaultPrevented) return\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault()\n toggle()\n }\n },\n [onKeyDown, toggle],\n )\n\n const Comp: ElementType = asChild ? Slot : 'button'\n\n return (\n <SwitchCtx.Provider value={{ checked: isChecked, disabled }}>\n <>\n <Comp aria-checked={isChecked} className={className} data-disabled={disabled ?? undefined} data-state={isChecked ? 'checked' : 'unchecked'} disabled={disabled} id={resolvedId} onClick={handleClick} onKeyDown={handleKeyDown} ref={ref} role='switch' type='button' {...rest}>\n {children}\n </Comp>\n {/* hidden input outside <button> — <input> inside <button> is invalid HTML.\n The form attribute handles cross-form association without nesting. */}\n <input\n aria-hidden='true'\n checked={isChecked}\n disabled={disabled}\n form={form}\n name={name}\n readOnly\n ref={inputRef}\n required={required}\n tabIndex={-1}\n type='checkbox'\n value={value}\n style={{\n height: 0,\n opacity: 0,\n pointerEvents: 'none',\n position: 'absolute',\n width: 0,\n }}\n />\n </>\n </SwitchCtx.Provider>\n )\n}\nSwitch.displayName = 'Switch'\n\nexport type SwitchThumbProps = HTMLAttributes<HTMLSpanElement> & AsChildProp\n\nexport const SwitchThumb = ({\n asChild,\n className,\n ref,\n ...props\n}: SwitchThumbProps & {\n ref?: Ref<HTMLSpanElement>\n}) => {\n const ctx = useContext(SwitchCtx)\n if (!ctx) throw new Error('SwitchThumb must be used within Switch')\n\n const Comp: ElementType = asChild ? Slot : 'span'\n\n return <Comp className={className} data-disabled={ctx.disabled ?? undefined} data-state={ctx.checked ? 'checked' : 'unchecked'} ref={ref} {...props} />\n}\nSwitchThumb.displayName = 'SwitchThumb'\n"],"mappings":";;;;;;;AASA,MAAM,YAAY,cAA0B,KAAK;AAcjD,MAAa,UAAU,EACrB,SACA,SACA,UACA,WACA,iBAAiB,OACjB,UACA,MACA,IACA,MACA,iBACA,SACA,WACA,KACA,UACA,QAAQ,MACR,GAAG,WAGC;CACJ,MAAM,WAAW,OAAgC,KAAK;CACtD,MAAM,SAAS,OAAO;CACtB,MAAM,aAAa,MAAM,OAAO;CAEhC,MAAM,CAAC,WAAW,gBAAgB,qBAA8B;EAC9D,OAAO;EACP,cAAc;EACd,UAAU;EACX,CAAC;AAGF,iBAAgB;EACd,MAAM,SAAS,SAAS,SAAS,QAAQ;AACzC,MAAI,CAAC,OAAQ;EACb,MAAM,oBAAoB,aAAa,eAAe;AACtD,SAAO,iBAAiB,SAAS,YAAY;AAC7C,eAAa,OAAO,oBAAoB,SAAS,YAAY;IAC5D,CAAC,gBAAgB,aAAa,CAAC;CAElC,MAAM,iBAAiB,aAAa,iBAA0B;EAC5D,MAAM,KAAK,SAAS;AACpB,MAAI,CAAC,GAAI;AACT,KAAG,UAAU;EACb,MAAM,KAAK,IAAI,MAAM,UAAU,EAAE,SAAS,MAAM,CAAC;AACjD,KAAG,cAAc,GAAG;IACnB,EAAE,CAAC;CAEN,MAAM,SAAS,kBAAkB;AAC/B,MAAI,SAAU;EACd,MAAM,aAAa,CAAC;AACpB,eAAa,WAAW;AACxB,uBAAqB,eAAe,WAAW,CAAC;IAC/C;EAAC;EAAU;EAAgB;EAAW;EAAa,CAAC;CAEvD,MAAM,cAAc,aACjB,MAAqC;AACpC,YAAU,EAAE;AACZ,MAAI,EAAE,iBAAkB;AACxB,UAAQ;IAEV,CAAC,SAAS,OAAO,CAClB;CAED,MAAM,gBAAgB,aACnB,MAAwC;AACvC,cAAY,EAAE;AACd,MAAI,EAAE,iBAAkB;AACxB,MAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,SAAS;AACtC,KAAE,gBAAgB;AAClB,WAAQ;;IAGZ,CAAC,WAAW,OAAO,CACpB;CAED,MAAM,OAAoB,UAAU,OAAO;AAE3C,QACE,oBAAC,UAAU,UAAX;EAAoB,OAAO;GAAE,SAAS;GAAW;GAAU;YACzD,4CACE,oBAAC,MAAD;GAAM,gBAAc;GAAsB;GAAW,iBAAe,YAAY;GAAW,cAAY,YAAY,YAAY;GAAuB;GAAU,IAAI;GAAY,SAAS;GAAa,WAAW;GAAoB;GAAK,MAAK;GAAS,MAAK;GAAS,GAAI;GACvQ;GACI,GAGP,oBAAC,SAAD;GACE,eAAY;GACZ,SAAS;GACC;GACJ;GACA;GACN;GACA,KAAK;GACK;GACV,UAAU;GACV,MAAK;GACE;GACP,OAAO;IACL,QAAQ;IACR,SAAS;IACT,eAAe;IACf,UAAU;IACV,OAAO;IACR;GACD,EACD;EACgB;;AAGzB,OAAO,cAAc;AAIrB,MAAa,eAAe,EAC1B,SACA,WACA,KACA,GAAG,YAGC;CACJ,MAAM,MAAM,WAAW,UAAU;AACjC,KAAI,CAAC,IAAK,OAAM,IAAI,MAAM,yCAAyC;AAInE,QAAO,oBAFmB,UAAU,OAAO,QAEpC;EAAiB;EAAW,iBAAe,IAAI,YAAY;EAAW,cAAY,IAAI,UAAU,YAAY;EAAkB;EAAK,GAAI;EAAS;;AAEzJ,YAAY,cAAc"}
1
+ {"version":3,"file":"SwitchBase.js","names":[],"sources":["../../src/components/Switch/SwitchBase.tsx"],"sourcesContent":["import { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { Slot, type AsChildProp } from '@primitives/slot'\nimport { createContext, useCallback, useContext, useEffect, useId, useRef, type ButtonHTMLAttributes, type ElementType, type HTMLAttributes, type KeyboardEvent, type MouseEvent, type Ref } from 'react'\n\ninterface Ctx {\n checked: boolean\n disabled: boolean | undefined\n}\n\nconst SwitchCtx = createContext<Ctx | null>(null)\n\ntype BaseButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onChange' | 'value'>\n\nexport interface SwitchProps extends BaseButtonProps, AsChildProp {\n checked?: boolean\n defaultChecked?: boolean\n form?: string\n name?: string\n onCheckedChange?: (checked: boolean) => void\n required?: boolean\n value?: string\n}\n\nexport const Switch = ({\n asChild,\n checked,\n children,\n className,\n defaultChecked = false,\n disabled,\n form,\n id,\n name,\n onCheckedChange,\n onClick,\n onKeyDown,\n ref,\n required,\n value = 'on',\n ...rest\n}: SwitchProps & {\n ref?: Ref<HTMLButtonElement>\n}) => {\n const inputRef = useRef<HTMLInputElement | null>(null)\n const autoId = useId()\n const resolvedId = id ?? `swt-${autoId}`\n\n const [isChecked, setIsChecked] = useUncontrolledState<boolean>({\n value: checked,\n defaultValue: defaultChecked,\n onChange: onCheckedChange,\n })\n\n // form reset -> restore default\n useEffect(() => {\n const formEl = inputRef.current?.form ?? null\n if (!formEl) return\n const handleReset = () => setIsChecked(defaultChecked)\n formEl.addEventListener('reset', handleReset)\n return () => formEl.removeEventListener('reset', handleReset)\n }, [defaultChecked, setIsChecked])\n\n const emitFormChange = useCallback((checkedValue: boolean) => {\n const el = inputRef.current\n if (!el) return\n el.checked = checkedValue\n const ev = new Event('change', { bubbles: true })\n el.dispatchEvent(ev)\n }, [])\n\n const toggle = useCallback(() => {\n if (disabled) return\n const newChecked = !isChecked\n setIsChecked(newChecked)\n queueMicrotask(() => emitFormChange(newChecked))\n }, [disabled, emitFormChange, isChecked, setIsChecked])\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n onClick?.(e)\n if (e.defaultPrevented) return\n toggle()\n },\n [onClick, toggle],\n )\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n onKeyDown?.(e)\n if (e.defaultPrevented) return\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault()\n toggle()\n }\n },\n [onKeyDown, toggle],\n )\n\n const Comp: ElementType = asChild ? Slot : 'button'\n\n return (\n <SwitchCtx.Provider value={{ checked: isChecked, disabled }}>\n <>\n <Comp aria-checked={isChecked} className={className} data-disabled={disabled ?? undefined} data-state={isChecked ? 'checked' : 'unchecked'} disabled={disabled} id={resolvedId} onClick={handleClick} onKeyDown={handleKeyDown} ref={ref} role='switch' type='button' {...rest}>\n {children}\n </Comp>\n {/* hidden input outside <button> — <input> inside <button> is invalid HTML.\n The form attribute handles cross-form association without nesting. */}\n <input\n aria-hidden='true'\n checked={isChecked}\n disabled={disabled}\n form={form}\n name={name}\n readOnly\n ref={inputRef}\n required={required}\n tabIndex={-1}\n type='checkbox'\n value={value}\n style={{\n height: 0,\n opacity: 0,\n pointerEvents: 'none',\n position: 'absolute',\n width: 0,\n }}\n />\n </>\n </SwitchCtx.Provider>\n )\n}\nSwitch.displayName = 'Switch'\n\nexport type SwitchThumbProps = HTMLAttributes<HTMLSpanElement> & AsChildProp\n\nexport const SwitchThumb = ({\n asChild,\n className,\n ref,\n ...props\n}: SwitchThumbProps & {\n ref?: Ref<HTMLSpanElement>\n}) => {\n const ctx = useContext(SwitchCtx)\n if (!ctx) throw new Error('SwitchThumb must be used within Switch')\n\n const Comp: ElementType = asChild ? Slot : 'span'\n\n return <Comp className={className} data-disabled={ctx.disabled ?? undefined} data-state={ctx.checked ? 'checked' : 'unchecked'} ref={ref} {...props} />\n}\nSwitchThumb.displayName = 'SwitchThumb'\n"],"mappings":";;;;;;;AASA,MAAM,YAAY,cAA0B,KAAK;AAcjD,MAAa,UAAU,EACrB,SACA,SACA,UACA,WACA,iBAAiB,OACjB,UACA,MACA,IACA,MACA,iBACA,SACA,WACA,KACA,UACA,QAAQ,MACR,GAAG,WAGC;CACJ,MAAM,WAAW,OAAgC,KAAK;CACtD,MAAM,SAAS,OAAO;CACtB,MAAM,aAAa,MAAM,OAAO;CAEhC,MAAM,CAAC,WAAW,gBAAgB,qBAA8B;EAC9D,OAAO;EACP,cAAc;EACd,UAAU;EACX,CAAC;AAGF,iBAAgB;EACd,MAAM,SAAS,SAAS,SAAS,QAAQ;AACzC,MAAI,CAAC,OAAQ;EACb,MAAM,oBAAoB,aAAa,eAAe;AACtD,SAAO,iBAAiB,SAAS,YAAY;AAC7C,eAAa,OAAO,oBAAoB,SAAS,YAAY;IAC5D,CAAC,gBAAgB,aAAa,CAAC;CAElC,MAAM,iBAAiB,aAAa,iBAA0B;EAC5D,MAAM,KAAK,SAAS;AACpB,MAAI,CAAC,GAAI;AACT,KAAG,UAAU;EACb,MAAM,KAAK,IAAI,MAAM,UAAU,EAAE,SAAS,MAAM,CAAC;AACjD,KAAG,cAAc,GAAG;IACnB,EAAE,CAAC;CAEN,MAAM,SAAS,kBAAkB;AAC/B,MAAI,SAAU;EACd,MAAM,aAAa,CAAC;AACpB,eAAa,WAAW;AACxB,uBAAqB,eAAe,WAAW,CAAC;IAC/C;EAAC;EAAU;EAAgB;EAAW;EAAa,CAAC;CAEvD,MAAM,cAAc,aACjB,MAAqC;AACpC,YAAU,EAAE;AACZ,MAAI,EAAE,iBAAkB;AACxB,UAAQ;IAEV,CAAC,SAAS,OAAO,CAClB;CAED,MAAM,gBAAgB,aACnB,MAAwC;AACvC,cAAY,EAAE;AACd,MAAI,EAAE,iBAAkB;AACxB,MAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,SAAS;AACtC,KAAE,gBAAgB;AAClB,WAAQ;;IAGZ,CAAC,WAAW,OAAO,CACpB;CAED,MAAM,OAAoB,UAAU,OAAO;AAE3C,QACE,oBAAC,UAAU,UAAX;EAAoB,OAAO;GAAE,SAAS;GAAW;GAAU;YACzD,8CACE,oBAAC,MAAD;GAAM,gBAAc;GAAsB;GAAW,iBAAe,YAAY;GAAW,cAAY,YAAY,YAAY;GAAuB;GAAU,IAAI;GAAY,SAAS;GAAa,WAAW;GAAoB;GAAK,MAAK;GAAS,MAAK;GAAS,GAAI;GACvQ;GACI,GAGP,oBAAC,SAAD;GACE,eAAY;GACZ,SAAS;GACC;GACJ;GACA;GACN;GACA,KAAK;GACK;GACV,UAAU;GACV,MAAK;GACE;GACP,OAAO;IACL,QAAQ;IACR,SAAS;IACT,eAAe;IACf,UAAU;IACV,OAAO;IACR;GACD,EACD;EACgB;;AAGzB,OAAO,cAAc;AAIrB,MAAa,eAAe,EAC1B,SACA,WACA,KACA,GAAG,YAGC;CACJ,MAAM,MAAM,WAAW,UAAU;AACjC,KAAI,CAAC,IAAK,OAAM,IAAI,MAAM,yCAAyC;AAInE,QAAO,oBAFmB,UAAU,OAAO,QAEpC;EAAiB;EAAW,iBAAe,IAAI,YAAY;EAAW,cAAY,IAAI,UAAU,YAAY;EAAkB;EAAK,GAAI;EAAS;;AAEzJ,YAAY,cAAc"}
@@ -7,7 +7,7 @@ import { ToggleGroupContext } from "../components/ToggleGroup/ToggleGroup.contex
7
7
  import { ToggleGroupItem } from "./ToggleGroupItem.js";
8
8
  import { DropdownMenu } from "../DropdownMenu.js";
9
9
  import { useContext, useRef, useState } from "react";
10
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
11
 
12
12
  //#region src/components/ToggleGroup/ToggleGroupSplitMenuItem.tsx
13
13
  const ToggleGroupSplitMenuItem = ({ activeColor, activeTextColor, children, className, dropdownAlign = "end", dropdownAriaLabel, dropdownButtonClassName, dropdownButtonStyle, dropdownCollisionPadding = 12, dropdownOptions, dropdownSide = "bottom", dropdownValue, dropdownValueLabel, layout, onDropdownValueChange, onKeyDown, size, style, triggerIcon = /* @__PURE__ */ jsx(ChevronDownIcon, { size: 16 }), value, variant, ...props }) => {
@@ -87,7 +87,7 @@ const ToggleGroupSplitMenuItem = ({ activeColor, activeTextColor, children, clas
87
87
  }
88
88
  if (onKeyDown) onKeyDown(event);
89
89
  };
90
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ToggleGroupItem, {
90
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ToggleGroupItem, {
91
91
  activeColor: resolvedActiveColor,
92
92
  activeTextColor: resolvedActiveTextColor,
93
93
  "aria-label": props["aria-label"] ? `${props["aria-label"]}${dropdownValueLabel ? `, ${dropdownValueLabel}` : ""}` : void 0,