@spear-ai/spectral 1.16.3 → 1.16.5

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,374 @@
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, 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, { 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, 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;
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
+ setQuery(selectedOption?.label ?? "");
189
+ }, 150);
190
+ }, [selectedOption]);
191
+ const handleClear = useCallback(() => {
192
+ setValue("");
193
+ setQuery("");
194
+ inputRef.current?.focus();
195
+ }, [setValue]);
196
+ const showClearButton = !isLoading && !isDisabled && (value.length > 0 || isFocused && query.length > 0);
197
+ const { dropdownWidthMode, dropdownWidthStyle } = getDropdownWidthStyles({
198
+ dropdownWidth,
199
+ triggerWidth: "100%"
200
+ });
201
+ const inputClasses = cn(getInputClasses(state, className), "pe-12", showSearchIcon && "ps-11", showClearButton && "pe-10", state === "loading" && "cursor-wait");
202
+ return /* @__PURE__ */ jsxs("div", {
203
+ className: "flex w-full flex-col gap-1.5",
204
+ "data-testid": "spectral-input-search-container",
205
+ ref,
206
+ children: [
207
+ label && /* @__PURE__ */ jsx(Label, {
208
+ className: cn("mb-2 block", labelClassName, isDisabled && "cursor-not-allowed text-input-text--disabled"),
209
+ "data-testid": "spectral-input-search-label",
210
+ htmlFor: fieldId,
211
+ children: label
212
+ }),
213
+ /* @__PURE__ */ jsx("div", {
214
+ className: "relative",
215
+ "data-testid": "spectral-input-search-wrapper",
216
+ ref: triggerRef,
217
+ children: /* @__PURE__ */ jsxs("div", {
218
+ className: "relative",
219
+ children: [
220
+ showSearchIcon && /* @__PURE__ */ jsx("span", {
221
+ "aria-hidden": "true",
222
+ className: "left-4 text-input-icon absolute top-1/2 -translate-y-1/2",
223
+ "data-testid": "spectral-input-search-icon",
224
+ children: /* @__PURE__ */ jsx(SearchIcon, { size: 20 })
225
+ }),
226
+ /* @__PURE__ */ jsx("input", {
227
+ "aria-activedescendant": highlightedIndex >= 0 ? `${fieldId}-option-${highlightedIndex}` : void 0,
228
+ "aria-autocomplete": "list",
229
+ "aria-controls": listboxId,
230
+ "aria-expanded": showDropdown,
231
+ "aria-haspopup": "listbox",
232
+ "aria-label": ariaLabel ?? label,
233
+ autoComplete: "off",
234
+ className: inputClasses,
235
+ "data-state": state,
236
+ "data-testid": "spectral-input-search",
237
+ disabled: isDisabled || isLoading,
238
+ id: fieldId,
239
+ name,
240
+ onBlur: handleBlur,
241
+ onChange: (event) => {
242
+ setQuery(event.target.value);
243
+ if (!isFocused) setIsFocused(true);
244
+ },
245
+ onFocus: handleFocus,
246
+ onKeyDown: handleKeyDown,
247
+ placeholder,
248
+ ref: inputRef,
249
+ required,
250
+ role: "combobox",
251
+ style: getFormFieldCSSProperties(),
252
+ type: "text",
253
+ value: query,
254
+ ...ariaProps
255
+ }),
256
+ showClearButton ? /* @__PURE__ */ jsx("button", {
257
+ "aria-label": `Clear ${label ?? "search"}`,
258
+ 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",
259
+ "data-testid": "spectral-input-search-clear-button",
260
+ onClick: handleClear,
261
+ onMouseDown: (event) => event.preventDefault(),
262
+ type: "button",
263
+ children: /* @__PURE__ */ jsx(CloseCircleIcon, { size: 20 })
264
+ }) : state === "loading" ? /* @__PURE__ */ jsx("div", {
265
+ className: "right-4 text-input-icon absolute top-1/2 -translate-y-1/2",
266
+ "data-testid": "spectral-input-search-loading-icon",
267
+ children: /* @__PURE__ */ jsx(LoaderIcon, {
268
+ className: "motion-safe:animate-spin",
269
+ size: 20
270
+ })
271
+ }) : state === "error" ? /* @__PURE__ */ jsx("div", {
272
+ className: "right-4 text-danger-400 absolute top-1/2 -translate-y-1/2",
273
+ "data-testid": "spectral-input-search-error-icon",
274
+ children: /* @__PURE__ */ jsx(ErrorIcon, { size: 20 })
275
+ }) : state === "warning" ? /* @__PURE__ */ jsx("div", {
276
+ className: "right-4 text-warning-400 absolute top-1/2 -translate-y-1/2",
277
+ "data-testid": "spectral-input-search-warning-icon",
278
+ children: /* @__PURE__ */ jsx(WarningIcon, { size: 20 })
279
+ }) : null
280
+ ]
281
+ })
282
+ }),
283
+ showDropdown && dropdownPosition && typeof document !== "undefined" ? createPortal(/* @__PURE__ */ jsx("div", {
284
+ 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"),
285
+ "data-dropdown-width-mode": dropdownWidthMode,
286
+ "data-dropdown-width-value": dropdownWidthMode === "custom" ? dropdownWidth : void 0,
287
+ "data-testid": "spectral-input-search-content",
288
+ ref: setDropdownElement,
289
+ style: {
290
+ top: `${dropdownPosition.top}px`,
291
+ left: `${dropdownPosition.left}px`,
292
+ ...dropdownWidthMode === "trigger" ? { width: `${dropdownPosition.width}px` } : dropdownWidthStyle,
293
+ ...dropdownShiftStyle
294
+ },
295
+ children: /* @__PURE__ */ jsx("div", {
296
+ className: "max-h-[17.5rem] overflow-y-auto",
297
+ id: listboxId,
298
+ ref: listRef,
299
+ role: "listbox",
300
+ children: showEmptyState ? /* @__PURE__ */ jsx(EmptyState, { message: emptyMessage }) : /* @__PURE__ */ jsxs(Fragment, { children: [filteredOptions.map((option, index) => {
301
+ const isHighlighted = index === highlightedIndex;
302
+ const isSelected = option.value === value;
303
+ return /* @__PURE__ */ jsx("button", {
304
+ "aria-selected": isSelected,
305
+ className: cn(getOptionClasses(!!option.disabled, isHighlighted, isSelected), "text-left"),
306
+ "data-highlighted": isHighlighted ? "" : void 0,
307
+ "data-index": index,
308
+ "data-testid": "spectral-input-search-item",
309
+ disabled: option.disabled,
310
+ id: `${fieldId}-option-${index}`,
311
+ onMouseDown: (event) => {
312
+ event.preventDefault();
313
+ commitSelection(option);
314
+ },
315
+ onMouseEnter: () => setHighlightedIndex(index),
316
+ role: "option",
317
+ tabIndex: -1,
318
+ type: "button",
319
+ children: /* @__PURE__ */ jsx("span", {
320
+ className: "min-w-0 flex-1 truncate",
321
+ children: renderOption ? renderOption(option) : option.label
322
+ })
323
+ }, option.value);
324
+ }), showCreateOption && /* @__PURE__ */ jsx("button", {
325
+ "aria-selected": createOptionIndex === highlightedIndex,
326
+ className: cn(getOptionClasses(isCreating, createOptionIndex === highlightedIndex, false), "gap-2 text-left text-accent font-medium"),
327
+ "data-highlighted": createOptionIndex === highlightedIndex ? "" : void 0,
328
+ "data-index": createOptionIndex,
329
+ "data-testid": "spectral-input-search-create-option",
330
+ disabled: isCreating,
331
+ id: `${fieldId}-option-${createOptionIndex}`,
332
+ onMouseDown: (event) => {
333
+ event.preventDefault();
334
+ commitCreate();
335
+ },
336
+ onMouseEnter: () => setHighlightedIndex(createOptionIndex),
337
+ role: "option",
338
+ tabIndex: -1,
339
+ type: "button",
340
+ children: isCreating ? /* @__PURE__ */ jsxs("span", {
341
+ className: "gap-2 flex items-center",
342
+ children: [/* @__PURE__ */ jsx(LoaderIcon, {
343
+ className: "shrink-0 motion-safe:animate-spin",
344
+ size: 16
345
+ }), /* @__PURE__ */ jsx("span", { children: creatingLabel })]
346
+ }) : /* @__PURE__ */ jsx("span", {
347
+ className: "gap-2 flex items-center min-w-0",
348
+ children: createOptionLabel(trimmedQuery)
349
+ })
350
+ })] })
351
+ })
352
+ }), document.body) : null,
353
+ /* @__PURE__ */ jsx(ErrorMessage, {
354
+ dataTestId: "spectral-input-search-error-message",
355
+ id: errorMessageId,
356
+ message: isInvalid ? errorMessage ?? null : null,
357
+ messageReserveLines,
358
+ messageReserveSpace: messageReserveSpace && state === "error"
359
+ }),
360
+ /* @__PURE__ */ jsx(WarningMessage, {
361
+ dataTestId: "spectral-input-search-warning-message",
362
+ id: warningMessageId,
363
+ message: state === "warning" ? warningMessage ?? null : null,
364
+ messageReserveLines,
365
+ messageReserveSpace: messageReserveSpace && state === "warning"
366
+ })
367
+ ]
368
+ });
369
+ };
370
+ InputSearch.displayName = "InputSearch";
371
+
372
+ //#endregion
373
+ export { InputSearch };
374
+ //# 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 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 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\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 setIsFocused(false)\n setHighlightedIndex(-1)\n setQuery(selectedOption?.label ?? '')\n }, 150)\n }, [selectedOption])\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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"}
package/dist/index.d.ts CHANGED
@@ -56,6 +56,7 @@ import { KeyboardIcon } from "./Icons/KeyboardIcon.js";
56
56
  import { LabelIcon } from "./Icons/LabelIcon.js";
57
57
  import { LassoIcon } from "./Icons/LassoIcon.js";
58
58
  import { LineToolIcon } from "./Icons/LineToolIcon.js";
59
+ import { LinkIcon } from "./Icons/LinkIcon.js";
59
60
  import { LiveViewIcon } from "./Icons/LiveViewIcon.js";
60
61
  import { LoaderIcon } from "./Icons/LoaderIcon.js";
61
62
  import { LocationIcon } from "./Icons/LocationIcon.js";
@@ -93,6 +94,7 @@ import { SyncIcon } from "./Icons/SyncIcon.js";
93
94
  import { SyncOffIcon } from "./Icons/SyncOffIcon.js";
94
95
  import { TrashIcon } from "./Icons/TrashIcon.js";
95
96
  import { UndoIcon } from "./Icons/UndoIcon.js";
97
+ import { UnlinkIcon } from "./Icons/UnlinkIcon.js";
96
98
  import { UploadIcon } from "./Icons/UploadIcon.js";
97
99
  import { User2Icon } from "./Icons/User2Icon.js";
98
100
  import { UserIcon } from "./Icons/UserIcon.js";
@@ -103,10 +105,11 @@ import { ZoomYIcon } from "./Icons/ZoomYIcon.js";
103
105
  import { Input, InputProps } from "./Input.js";
104
106
  import { InputNumeric, InputNumericProps } from "./InputNumeric.js";
105
107
  import { InputOTP, InputOTPBaseProps, InputOTPProps } from "./InputOTP.js";
108
+ import { InputSearch, InputSearchOption, InputSearchProps } from "./InputSearch.js";
106
109
  import { Kbd, KbdGroup, KbdGroupProps, KbdProps, KbdSymbol } from "./Kbd.js";
107
110
  import { Label, LabelProps } from "./Label.js";
108
111
  import { MultiSelect, MultiSelectProps } from "./MultiSelect.js";
109
- import { Popover, PopoverContent, PopoverContentProps, PopoverTrigger } from "./Popover.js";
112
+ import { Popover, PopoverAnchor, PopoverContent, PopoverContentProps, PopoverTrigger } from "./Popover.js";
110
113
  import { RadioButton, RadioButtonProps } from "./RadioButton.js";
111
114
  import { RadioButtonGroup, RadioButtonGroupItem, RadioButtonGroupItemProps, RadioButtonGroupProps } from "./RadioButtonGroup.js";
112
115
  import { RadioGroup, RadioGroupItem, RadioGroupItemProps, RadioGroupProps } from "./RadioGroup.js";
@@ -129,4 +132,4 @@ import { useControllableState } from "./hooks/useControllableState.js";
129
132
  import { useUncontrolledState } from "./hooks/useUncontrolledState.js";
130
133
  import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea } from "./primitives/input-group.js";
131
134
  import { cn } from "./utils/twUtils.js";
132
- export { Accordion, type AccordionProps, AdjustmentsIcon, Alert, type AlertProps, AnalyzeIcon, AnnotationsIcon, ApprovedIcon, ArrowDownIcon, ArrowUpIcon, Avatar, type AvatarProps, Badge, type BadgeProps, BoxToolIcon, Button, ButtonGroup, ButtonGroupItem, type ButtonGroupProps, ButtonGroupSeparator, ButtonIcon, type ButtonIconProps, ButtonIconSlideout, type ButtonIconSlideoutProps, type ButtonProps, CalendarIcon, CheckCircleIcon, CheckSquareIcon, Checkbox, type CheckboxProps, CheckmarkIcon, ChevronDownIcon, ChevronUpIcon, ClockIcon, CloseCircleIcon, CloseIcon, Combobox, type ComboboxOption, type ComboboxProps, ControlGroupSelect, type ControlGroupSelectCaptionLayout, type ControlGroupSelectProps, Crosshairs2Icon, CrosshairsIcon, DashboardIcon, DataCard, type DataCardProps, DatabaseIcon, DateTimePicker, type DateTimePickerProps, DeleteIcon, Dialog, Drawer, type DrawerProps, DurationIcon, EditIcon, EmailIcon, EraserIcon, ErrorIcon, ErrorMessage, EyeClosedIcon, EyeClosedIcon2, EyeOpenIcon, FileDownloadIcon, type FormFieldMessageValue, GoToFirstIcon, GoToLastIcon, HarmonicCursorsIcon, HoverCard, HoverCardContent, type HoverCardProps, HoverCardTrigger, IconBase, InfoIcon, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputNumeric, type InputNumericProps, InputOTP, type InputOTPBaseProps, type InputOTPProps, type InputProps, Kbd, KbdGroup, type KbdGroupProps, type KbdProps, type KbdSymbol, KeyboardIcon, Label, LabelIcon, type LabelProps, LassoIcon, LineToolIcon, LiveViewIcon, LoaderIcon, LocationIcon, LogoutIcon, MaximizeIcon, MeasureIcon, MenuDotsIcon, MenuIcon, MessagesIcon, MetadataIcon, MinimizeIcon, MinusIcon, MultiSelect, type MultiSelectProps, OntologyIcon, PanelIconClose, PanelIconOpen, PauseIcon, PlayIcon, PlusIcon, PolygonIcon, Popover, PopoverContent, type PopoverContentProps, PopoverTrigger, PrinterIcon, ProgressCheckIcon, RadioButton, RadioButtonGroup, RadioButtonGroupItem, type RadioButtonGroupItemProps, type RadioButtonGroupProps, type RadioButtonProps, RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RadioGroupProps, ResetIcon, ReviewedIcon, ScissorsIcon, SearchIcon, Select, type SelectExtendedProps, type SelectProps, Separator, type SeparatorProps, SettingsIcon, Skeleton, Slider, type SliderProps, SortAscendingIcon, SortAtoZIcon, SortDescendingIcon, SortZtoAIcon, SparklesIcon, SpectralProvider, type SpectralProviderProps, StackIcon, StarIcon, SvgIdContext, Switch, type SwitchProps, SyncIcon, SyncOffIcon, Tabs, type TabsProps, Textarea, type TextareaProps, Toast, type ToastProps, Toggle, ToggleGroup, ToggleGroupItem, type ToggleGroupItemProps, type ToggleGroupProps, ToggleGroupSplitMenuItem, type ToggleGroupSplitMenuItemProps, type ToggleProps, Tooltip, TooltipContent, TooltipTrigger, TrashIcon, Tray, type TrayBaseProps, type TrayBodyProps, type TrayContentProps, UndoIcon, UploadIcon, User2Icon, UserIcon, WarningIcon, WarningMessage, ZoomAllIcon, ZoomXIcon, ZoomYIcon, cn, toast, useAccordionAutoScroll, useControllableState, useUncontrolledState };
135
+ export { Accordion, type AccordionProps, AdjustmentsIcon, Alert, type AlertProps, AnalyzeIcon, AnnotationsIcon, ApprovedIcon, ArrowDownIcon, ArrowUpIcon, Avatar, type AvatarProps, Badge, type BadgeProps, BoxToolIcon, Button, ButtonGroup, ButtonGroupItem, type ButtonGroupProps, ButtonGroupSeparator, ButtonIcon, type ButtonIconProps, ButtonIconSlideout, type ButtonIconSlideoutProps, type ButtonProps, CalendarIcon, CheckCircleIcon, CheckSquareIcon, Checkbox, type CheckboxProps, CheckmarkIcon, ChevronDownIcon, ChevronUpIcon, ClockIcon, CloseCircleIcon, CloseIcon, Combobox, type ComboboxOption, type ComboboxProps, ControlGroupSelect, type ControlGroupSelectCaptionLayout, type ControlGroupSelectProps, Crosshairs2Icon, CrosshairsIcon, DashboardIcon, DataCard, type DataCardProps, DatabaseIcon, DateTimePicker, type DateTimePickerProps, DeleteIcon, Dialog, Drawer, type DrawerProps, DurationIcon, EditIcon, EmailIcon, EraserIcon, ErrorIcon, ErrorMessage, EyeClosedIcon, EyeClosedIcon2, EyeOpenIcon, FileDownloadIcon, type FormFieldMessageValue, GoToFirstIcon, GoToLastIcon, HarmonicCursorsIcon, HoverCard, HoverCardContent, type HoverCardProps, HoverCardTrigger, IconBase, InfoIcon, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputNumeric, type InputNumericProps, InputOTP, type InputOTPBaseProps, type InputOTPProps, type InputProps, InputSearch, type InputSearchOption, type InputSearchProps, Kbd, KbdGroup, type KbdGroupProps, type KbdProps, type KbdSymbol, KeyboardIcon, Label, LabelIcon, type LabelProps, LassoIcon, LineToolIcon, LinkIcon, LiveViewIcon, LoaderIcon, LocationIcon, LogoutIcon, MaximizeIcon, MeasureIcon, MenuDotsIcon, MenuIcon, MessagesIcon, MetadataIcon, MinimizeIcon, MinusIcon, MultiSelect, type MultiSelectProps, OntologyIcon, PanelIconClose, PanelIconOpen, PauseIcon, PlayIcon, PlusIcon, PolygonIcon, Popover, PopoverAnchor, PopoverContent, type PopoverContentProps, PopoverTrigger, PrinterIcon, ProgressCheckIcon, RadioButton, RadioButtonGroup, RadioButtonGroupItem, type RadioButtonGroupItemProps, type RadioButtonGroupProps, type RadioButtonProps, RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RadioGroupProps, ResetIcon, ReviewedIcon, ScissorsIcon, SearchIcon, Select, type SelectExtendedProps, type SelectProps, Separator, type SeparatorProps, SettingsIcon, Skeleton, Slider, type SliderProps, SortAscendingIcon, SortAtoZIcon, SortDescendingIcon, SortZtoAIcon, SparklesIcon, SpectralProvider, type SpectralProviderProps, StackIcon, StarIcon, SvgIdContext, Switch, type SwitchProps, SyncIcon, SyncOffIcon, Tabs, type TabsProps, Textarea, type TextareaProps, Toast, type ToastProps, Toggle, ToggleGroup, ToggleGroupItem, type ToggleGroupItemProps, type ToggleGroupProps, ToggleGroupSplitMenuItem, type ToggleGroupSplitMenuItemProps, type ToggleProps, Tooltip, TooltipContent, TooltipTrigger, TrashIcon, Tray, type TrayBaseProps, type TrayBodyProps, type TrayContentProps, UndoIcon, UnlinkIcon, UploadIcon, User2Icon, UserIcon, WarningIcon, WarningMessage, ZoomAllIcon, ZoomXIcon, ZoomYIcon, cn, toast, useAccordionAutoScroll, useControllableState, useUncontrolledState };
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ import { KeyboardIcon } from "./Icons/KeyboardIcon.js";
38
38
  import { LabelIcon } from "./Icons/LabelIcon.js";
39
39
  import { LassoIcon } from "./Icons/LassoIcon.js";
40
40
  import { LineToolIcon } from "./Icons/LineToolIcon.js";
41
+ import { LinkIcon } from "./Icons/LinkIcon.js";
41
42
  import { LiveViewIcon } from "./Icons/LiveViewIcon.js";
42
43
  import { LoaderIcon } from "./Icons/LoaderIcon.js";
43
44
  import { LocationIcon } from "./Icons/LocationIcon.js";
@@ -75,6 +76,7 @@ import { SyncIcon } from "./Icons/SyncIcon.js";
75
76
  import { SyncOffIcon } from "./Icons/SyncOffIcon.js";
76
77
  import { TrashIcon } from "./Icons/TrashIcon.js";
77
78
  import { UndoIcon } from "./Icons/UndoIcon.js";
79
+ import { UnlinkIcon } from "./Icons/UnlinkIcon.js";
78
80
  import { UploadIcon } from "./Icons/UploadIcon.js";
79
81
  import { User2Icon } from "./Icons/User2Icon.js";
80
82
  import { UserIcon } from "./Icons/UserIcon.js";
@@ -102,7 +104,7 @@ import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGr
102
104
  import { Combobox } from "./Combobox.js";
103
105
  import { ControlGroupSelect } from "./ControlGroup/ControlGroupSelect.js";
104
106
  import { DataCard } from "./DataCard.js";
105
- import { Popover, PopoverContent, PopoverTrigger } from "./Popover.js";
107
+ import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "./Popover.js";
106
108
  import { DateTimePicker } from "./DateTimePicker.js";
107
109
  import { Dialog } from "./Dialog.js";
108
110
  import { SpectralProvider } from "./components/SpectralProvider/SpectralProvider.js";
@@ -111,6 +113,7 @@ import { HoverCard, HoverCardContent, HoverCardTrigger } from "./HoverCard.js";
111
113
  import { Input } from "./Input.js";
112
114
  import { InputNumeric } from "./InputNumeric.js";
113
115
  import { InputOTP } from "./InputOTP.js";
116
+ import { InputSearch } from "./InputSearch.js";
114
117
  import { Kbd, KbdGroup } from "./Kbd.js";
115
118
  import { MultiSelect } from "./MultiSelect.js";
116
119
  import { RadioButtonGroup, RadioButtonGroupItem } from "./RadioButtonGroup.js";
@@ -130,4 +133,4 @@ import { ToggleGroup } from "./ToggleGroup.js";
130
133
  import { Tooltip, TooltipContent, TooltipTrigger } from "./Tooltip.js";
131
134
  import { Tray } from "./Tray.js";
132
135
 
133
- export { Accordion, AdjustmentsIcon, Alert, AnalyzeIcon, AnnotationsIcon, ApprovedIcon, ArrowDownIcon, ArrowUpIcon, Avatar, Badge, BoxToolIcon, Button, ButtonGroup, ButtonGroupItem, ButtonGroupSeparator, ButtonIcon, ButtonIconSlideout, CalendarIcon, CheckCircleIcon, CheckSquareIcon, Checkbox, CheckmarkIcon, ChevronDownIcon, ChevronUpIcon, ClockIcon, CloseCircleIcon, CloseIcon, Combobox, ControlGroupSelect, Crosshairs2Icon, CrosshairsIcon, DashboardIcon, DataCard, DatabaseIcon, DateTimePicker, DeleteIcon, Dialog, Drawer, DurationIcon, EditIcon, EmailIcon, EraserIcon, ErrorIcon, ErrorMessage, EyeClosedIcon, EyeClosedIcon2, EyeOpenIcon, FileDownloadIcon, GoToFirstIcon, GoToLastIcon, HarmonicCursorsIcon, HoverCard, HoverCardContent, HoverCardTrigger, IconBase, InfoIcon, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputNumeric, InputOTP, Kbd, KbdGroup, KeyboardIcon, Label, LabelIcon, LassoIcon, LineToolIcon, LiveViewIcon, LoaderIcon, LocationIcon, LogoutIcon, MaximizeIcon, MeasureIcon, MenuDotsIcon, MenuIcon, MessagesIcon, MetadataIcon, MinimizeIcon, MinusIcon, MultiSelect, OntologyIcon, PanelIconClose, PanelIconOpen, PauseIcon, PlayIcon, PlusIcon, PolygonIcon, Popover, PopoverContent, PopoverTrigger, PrinterIcon, ProgressCheckIcon, RadioButton, RadioButtonGroup, RadioButtonGroupItem, RadioGroup, RadioGroupItem, ResetIcon, ReviewedIcon, ScissorsIcon, SearchIcon, Select, Separator, SettingsIcon, Skeleton, Slider, SortAscendingIcon, SortAtoZIcon, SortDescendingIcon, SortZtoAIcon, SparklesIcon, SpectralProvider, StackIcon, StarIcon, SvgIdContext, Switch, SyncIcon, SyncOffIcon, Tabs, Textarea, Toast, Toggle, ToggleGroup, ToggleGroupItem, ToggleGroupSplitMenuItem, Tooltip, TooltipContent, TooltipTrigger, TrashIcon, Tray, UndoIcon, UploadIcon, User2Icon, UserIcon, WarningIcon, WarningMessage, ZoomAllIcon, ZoomXIcon, ZoomYIcon, cn, toast, useAccordionAutoScroll, useControllableState, useUncontrolledState };
136
+ export { Accordion, AdjustmentsIcon, Alert, AnalyzeIcon, AnnotationsIcon, ApprovedIcon, ArrowDownIcon, ArrowUpIcon, Avatar, Badge, BoxToolIcon, Button, ButtonGroup, ButtonGroupItem, ButtonGroupSeparator, ButtonIcon, ButtonIconSlideout, CalendarIcon, CheckCircleIcon, CheckSquareIcon, Checkbox, CheckmarkIcon, ChevronDownIcon, ChevronUpIcon, ClockIcon, CloseCircleIcon, CloseIcon, Combobox, ControlGroupSelect, Crosshairs2Icon, CrosshairsIcon, DashboardIcon, DataCard, DatabaseIcon, DateTimePicker, DeleteIcon, Dialog, Drawer, DurationIcon, EditIcon, EmailIcon, EraserIcon, ErrorIcon, ErrorMessage, EyeClosedIcon, EyeClosedIcon2, EyeOpenIcon, FileDownloadIcon, GoToFirstIcon, GoToLastIcon, HarmonicCursorsIcon, HoverCard, HoverCardContent, HoverCardTrigger, IconBase, InfoIcon, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputNumeric, InputOTP, InputSearch, Kbd, KbdGroup, KeyboardIcon, Label, LabelIcon, LassoIcon, LineToolIcon, LinkIcon, LiveViewIcon, LoaderIcon, LocationIcon, LogoutIcon, MaximizeIcon, MeasureIcon, MenuDotsIcon, MenuIcon, MessagesIcon, MetadataIcon, MinimizeIcon, MinusIcon, MultiSelect, OntologyIcon, PanelIconClose, PanelIconOpen, PauseIcon, PlayIcon, PlusIcon, PolygonIcon, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PrinterIcon, ProgressCheckIcon, RadioButton, RadioButtonGroup, RadioButtonGroupItem, RadioGroup, RadioGroupItem, ResetIcon, ReviewedIcon, ScissorsIcon, SearchIcon, Select, Separator, SettingsIcon, Skeleton, Slider, SortAscendingIcon, SortAtoZIcon, SortDescendingIcon, SortZtoAIcon, SparklesIcon, SpectralProvider, StackIcon, StarIcon, SvgIdContext, Switch, SyncIcon, SyncOffIcon, Tabs, Textarea, Toast, Toggle, ToggleGroup, ToggleGroupItem, ToggleGroupSplitMenuItem, Tooltip, TooltipContent, TooltipTrigger, TrashIcon, Tray, UndoIcon, UnlinkIcon, UploadIcon, User2Icon, UserIcon, WarningIcon, WarningMessage, ZoomAllIcon, ZoomXIcon, ZoomYIcon, cn, toast, useAccordionAutoScroll, useControllableState, useUncontrolledState };