@rovula/ui 0.1.41 → 0.1.43

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,76 @@
1
+ import React, { ReactNode } from "react";
2
+ import { type InputProps } from "../TextInput/TextInput";
3
+ export type AutoCompleteOption = {
4
+ value: string;
5
+ label: string;
6
+ };
7
+ /**
8
+ * InputProps that AutoComplete manages internally.
9
+ * These are excluded from the public API so callers cannot accidentally
10
+ * override internal event wiring or ARIA attributes.
11
+ */
12
+ type OmittedInputProps = "value" | "onChange" | "onKeyDown" | "onSelect" | "role" | "aria-expanded" | "aria-haspopup" | "aria-autocomplete" | "aria-activedescendant" | "aria-controls" | "autoComplete" | "hasClearIcon";
13
+ export type AutoCompleteProps<T extends AutoCompleteOption = AutoCompleteOption> = {
14
+ /** Options provided by caller (already filtered/fetched externally) */
15
+ options: T[];
16
+ /** Controlled value — the current input text */
17
+ value?: string;
18
+ /**
19
+ * Called on every change: typing or clearing.
20
+ * Parent should update `value` from this.
21
+ */
22
+ onChange?: (value: string) => void;
23
+ /**
24
+ * Called only when user explicitly selects an option from the list.
25
+ * Receives the full option object (including any domain-specific fields).
26
+ */
27
+ onSelect?: (option: T) => void;
28
+ onBlur?: () => void;
29
+ /** Called with the current query when the user types */
30
+ onSearch?: (query: string) => void;
31
+ /** Show a loading spinner inside the dropdown */
32
+ loading?: boolean;
33
+ /** Text shown when options is empty and not loading */
34
+ noOptionsText?: string;
35
+ /**
36
+ * When true, show the noOptionsText message when options is empty.
37
+ * Defaults to false (popover stays closed when no options).
38
+ */
39
+ showNoOptions?: boolean;
40
+ /**
41
+ * Custom render for each option item.
42
+ * Receives the option and whether it is currently selected.
43
+ * The wrapper button (styles + click handler) is provided by AutoComplete.
44
+ */
45
+ renderOption?: (option: T, isSelected: boolean) => ReactNode;
46
+ /**
47
+ * Override client-side filtering.
48
+ * Pass `(x) => x` to disable filtering entirely (when results come from an API).
49
+ * Defaults to identity (no filtering).
50
+ */
51
+ filterOptions?: (options: T[]) => T[];
52
+ /**
53
+ * Render the options list via a React portal so it escapes containers
54
+ * with `overflow: hidden/auto`.
55
+ * Set to false when inside a Dialog — portal content is blocked by
56
+ * Radix Dialog's focus trap and aria-modal, making items unclickable.
57
+ * Defaults to true.
58
+ */
59
+ portal?: boolean;
60
+ /** Extra className applied to the options list container */
61
+ listboxClassName?: string;
62
+ /** Extra inline styles applied to the options list container */
63
+ listboxStyle?: React.CSSProperties;
64
+ /** Extra inline styles applied to the Popover content wrapper (portal mode only) */
65
+ popoverStyle?: React.CSSProperties;
66
+ /** Extra className applied to each option item */
67
+ optionClassName?: string;
68
+ /** Extra inline styles applied to each option item */
69
+ optionStyle?: React.CSSProperties;
70
+ "data-testid"?: string;
71
+ } & Omit<InputProps, OmittedInputProps>;
72
+ declare function AutoCompleteInner<T extends AutoCompleteOption = AutoCompleteOption>({ options, value, onChange, onSelect, onBlur, onSearch, loading, noOptionsText, showNoOptions, renderOption, filterOptions, portal, listboxClassName, listboxStyle, popoverStyle, optionClassName, optionStyle, "data-testid": testId, id, label, fullwidth, size, rounded, variant, disabled, ...rest }: AutoCompleteProps<T>, ref: React.ForwardedRef<HTMLInputElement>): import("react/jsx-runtime").JSX.Element;
73
+ declare const AutoComplete: <T extends AutoCompleteOption = AutoCompleteOption>(props: AutoCompleteProps<T> & {
74
+ ref?: React.ForwardedRef<HTMLInputElement>;
75
+ }) => ReturnType<typeof AutoCompleteInner>;
76
+ export default AutoComplete;
@@ -0,0 +1,362 @@
1
+ import React from "react";
2
+ import type { StoryObj } from "@storybook/react";
3
+ import AutoComplete, { type AutoCompleteOption } from "./AutoComplete";
4
+ declare const meta: {
5
+ title: string;
6
+ component: <T extends AutoCompleteOption = AutoCompleteOption>(props: import("./AutoComplete").AutoCompleteProps<T> & {
7
+ ref?: React.ForwardedRef<HTMLInputElement>;
8
+ }) => ReturnType<(<T_1 extends AutoCompleteOption = AutoCompleteOption>({ options, value, onChange, onSelect, onBlur, onSearch, loading, noOptionsText, showNoOptions, renderOption, filterOptions, portal, listboxClassName, listboxStyle, popoverStyle, optionClassName, optionStyle, "data-testid": testId, id, label, fullwidth, size, rounded, variant, disabled, ...rest }: import("./AutoComplete").AutoCompleteProps<T_1>, ref: React.ForwardedRef<HTMLInputElement>) => import("react/jsx-runtime").JSX.Element)>;
9
+ tags: string[];
10
+ parameters: {
11
+ layout: string;
12
+ };
13
+ decorators: ((Story: import("@storybook/csf").PartialStoryFn<import("@storybook/react").ReactRenderer, {
14
+ options: AutoCompleteOption[];
15
+ value?: string | undefined;
16
+ onChange?: ((value: string) => void) | undefined;
17
+ onSelect?: ((option: AutoCompleteOption) => void) | undefined;
18
+ onBlur?: ((() => void) & React.FocusEventHandler<HTMLInputElement>) | undefined;
19
+ onSearch?: ((query: string) => void) | undefined;
20
+ loading?: boolean | undefined;
21
+ noOptionsText?: string | undefined;
22
+ showNoOptions?: boolean | undefined;
23
+ renderOption?: ((option: AutoCompleteOption, isSelected: boolean) => React.ReactNode) | undefined;
24
+ filterOptions?: ((options: AutoCompleteOption[]) => AutoCompleteOption[]) | undefined;
25
+ portal?: boolean | undefined;
26
+ listboxClassName?: string | undefined;
27
+ listboxStyle?: React.CSSProperties | undefined;
28
+ popoverStyle?: React.CSSProperties | undefined;
29
+ optionClassName?: string | undefined;
30
+ optionStyle?: React.CSSProperties | undefined;
31
+ "data-testid"?: string | undefined;
32
+ variant?: "flat" | "outline" | "underline" | undefined;
33
+ suppressHydrationWarning?: boolean | undefined | undefined;
34
+ className?: string | undefined;
35
+ color?: string | undefined | undefined;
36
+ height?: number | string | undefined | undefined;
37
+ id?: string | undefined;
38
+ lang?: string | undefined | undefined;
39
+ max?: number | string | undefined | undefined;
40
+ min?: number | string | undefined | undefined;
41
+ name?: string | undefined | undefined;
42
+ style?: React.CSSProperties | undefined;
43
+ type?: React.HTMLInputTypeAttribute | undefined;
44
+ width?: number | string | undefined | undefined;
45
+ tabIndex?: number | undefined | undefined;
46
+ format?: ((value: string) => string) | undefined;
47
+ "aria-atomic"?: (boolean | "true" | "false") | undefined;
48
+ "aria-braillelabel"?: string | undefined | undefined;
49
+ "aria-brailleroledescription"?: string | undefined | undefined;
50
+ "aria-busy"?: (boolean | "true" | "false") | undefined;
51
+ "aria-checked"?: boolean | "false" | "mixed" | "true" | undefined | undefined;
52
+ "aria-colcount"?: number | undefined | undefined;
53
+ "aria-colindex"?: number | undefined | undefined;
54
+ "aria-colindextext"?: string | undefined | undefined;
55
+ "aria-colspan"?: number | undefined | undefined;
56
+ "aria-current"?: boolean | "false" | "true" | "page" | "step" | "location" | "date" | "time" | undefined | undefined;
57
+ "aria-describedby"?: string | undefined | undefined;
58
+ "aria-description"?: string | undefined | undefined;
59
+ "aria-details"?: string | undefined | undefined;
60
+ "aria-disabled"?: (boolean | "true" | "false") | undefined;
61
+ "aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup" | undefined | undefined;
62
+ "aria-errormessage"?: string | undefined | undefined;
63
+ "aria-flowto"?: string | undefined | undefined;
64
+ "aria-grabbed"?: (boolean | "true" | "false") | undefined;
65
+ "aria-hidden"?: (boolean | "true" | "false") | undefined;
66
+ "aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling" | undefined | undefined;
67
+ "aria-keyshortcuts"?: string | undefined | undefined;
68
+ "aria-label"?: string | undefined | undefined;
69
+ "aria-labelledby"?: string | undefined | undefined;
70
+ "aria-level"?: number | undefined | undefined;
71
+ "aria-live"?: "off" | "assertive" | "polite" | undefined | undefined;
72
+ "aria-modal"?: (boolean | "true" | "false") | undefined;
73
+ "aria-multiline"?: (boolean | "true" | "false") | undefined;
74
+ "aria-multiselectable"?: (boolean | "true" | "false") | undefined;
75
+ "aria-orientation"?: "horizontal" | "vertical" | undefined | undefined;
76
+ "aria-owns"?: string | undefined | undefined;
77
+ "aria-placeholder"?: string | undefined | undefined;
78
+ "aria-posinset"?: number | undefined | undefined;
79
+ "aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined | undefined;
80
+ "aria-readonly"?: (boolean | "true" | "false") | undefined;
81
+ "aria-relevant"?: "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text" | "text additions" | "text removals" | undefined | undefined;
82
+ "aria-required"?: (boolean | "true" | "false") | undefined;
83
+ "aria-roledescription"?: string | undefined | undefined;
84
+ "aria-rowcount"?: number | undefined | undefined;
85
+ "aria-rowindex"?: number | undefined | undefined;
86
+ "aria-rowindextext"?: string | undefined | undefined;
87
+ "aria-rowspan"?: number | undefined | undefined;
88
+ "aria-selected"?: (boolean | "true" | "false") | undefined;
89
+ "aria-setsize"?: number | undefined | undefined;
90
+ "aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined | undefined;
91
+ "aria-valuemax"?: number | undefined | undefined;
92
+ "aria-valuemin"?: number | undefined | undefined;
93
+ "aria-valuenow"?: number | undefined | undefined;
94
+ "aria-valuetext"?: string | undefined | undefined;
95
+ children?: React.ReactNode;
96
+ dangerouslySetInnerHTML?: {
97
+ __html: string | TrustedHTML;
98
+ } | undefined | undefined;
99
+ onCopy?: React.ClipboardEventHandler<HTMLInputElement> | undefined;
100
+ onCopyCapture?: React.ClipboardEventHandler<HTMLInputElement> | undefined;
101
+ onCut?: React.ClipboardEventHandler<HTMLInputElement> | undefined;
102
+ onCutCapture?: React.ClipboardEventHandler<HTMLInputElement> | undefined;
103
+ onPaste?: React.ClipboardEventHandler<HTMLInputElement> | undefined;
104
+ onPasteCapture?: React.ClipboardEventHandler<HTMLInputElement> | undefined;
105
+ onCompositionEnd?: React.CompositionEventHandler<HTMLInputElement> | undefined;
106
+ onCompositionEndCapture?: React.CompositionEventHandler<HTMLInputElement> | undefined;
107
+ onCompositionStart?: React.CompositionEventHandler<HTMLInputElement> | undefined;
108
+ onCompositionStartCapture?: React.CompositionEventHandler<HTMLInputElement> | undefined;
109
+ onCompositionUpdate?: React.CompositionEventHandler<HTMLInputElement> | undefined;
110
+ onCompositionUpdateCapture?: React.CompositionEventHandler<HTMLInputElement> | undefined;
111
+ onFocus?: React.FocusEventHandler<HTMLInputElement> | undefined;
112
+ onFocusCapture?: React.FocusEventHandler<HTMLInputElement> | undefined;
113
+ onBlurCapture?: React.FocusEventHandler<HTMLInputElement> | undefined;
114
+ onChangeCapture?: React.FormEventHandler<HTMLInputElement> | undefined;
115
+ onBeforeInput?: React.FormEventHandler<HTMLInputElement> | undefined;
116
+ onBeforeInputCapture?: React.FormEventHandler<HTMLInputElement> | undefined;
117
+ onInput?: React.FormEventHandler<HTMLInputElement> | undefined;
118
+ onInputCapture?: React.FormEventHandler<HTMLInputElement> | undefined;
119
+ onReset?: React.FormEventHandler<HTMLInputElement> | undefined;
120
+ onResetCapture?: React.FormEventHandler<HTMLInputElement> | undefined;
121
+ onSubmit?: React.FormEventHandler<HTMLInputElement> | undefined;
122
+ onSubmitCapture?: React.FormEventHandler<HTMLInputElement> | undefined;
123
+ onInvalid?: React.FormEventHandler<HTMLInputElement> | undefined;
124
+ onInvalidCapture?: React.FormEventHandler<HTMLInputElement> | undefined;
125
+ onLoad?: React.ReactEventHandler<HTMLInputElement> | undefined;
126
+ onLoadCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
127
+ onError?: React.ReactEventHandler<HTMLInputElement> | undefined;
128
+ onErrorCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
129
+ onKeyDownCapture?: React.KeyboardEventHandler<HTMLInputElement> | undefined;
130
+ onKeyPress?: React.KeyboardEventHandler<HTMLInputElement> | undefined;
131
+ onKeyPressCapture?: React.KeyboardEventHandler<HTMLInputElement> | undefined;
132
+ onKeyUp?: React.KeyboardEventHandler<HTMLInputElement> | undefined;
133
+ onKeyUpCapture?: React.KeyboardEventHandler<HTMLInputElement> | undefined;
134
+ onAbort?: React.ReactEventHandler<HTMLInputElement> | undefined;
135
+ onAbortCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
136
+ onCanPlay?: React.ReactEventHandler<HTMLInputElement> | undefined;
137
+ onCanPlayCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
138
+ onCanPlayThrough?: React.ReactEventHandler<HTMLInputElement> | undefined;
139
+ onCanPlayThroughCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
140
+ onDurationChange?: React.ReactEventHandler<HTMLInputElement> | undefined;
141
+ onDurationChangeCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
142
+ onEmptied?: React.ReactEventHandler<HTMLInputElement> | undefined;
143
+ onEmptiedCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
144
+ onEncrypted?: React.ReactEventHandler<HTMLInputElement> | undefined;
145
+ onEncryptedCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
146
+ onEnded?: React.ReactEventHandler<HTMLInputElement> | undefined;
147
+ onEndedCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
148
+ onLoadedData?: React.ReactEventHandler<HTMLInputElement> | undefined;
149
+ onLoadedDataCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
150
+ onLoadedMetadata?: React.ReactEventHandler<HTMLInputElement> | undefined;
151
+ onLoadedMetadataCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
152
+ onLoadStart?: React.ReactEventHandler<HTMLInputElement> | undefined;
153
+ onLoadStartCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
154
+ onPause?: React.ReactEventHandler<HTMLInputElement> | undefined;
155
+ onPauseCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
156
+ onPlay?: React.ReactEventHandler<HTMLInputElement> | undefined;
157
+ onPlayCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
158
+ onPlaying?: React.ReactEventHandler<HTMLInputElement> | undefined;
159
+ onPlayingCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
160
+ onProgress?: React.ReactEventHandler<HTMLInputElement> | undefined;
161
+ onProgressCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
162
+ onRateChange?: React.ReactEventHandler<HTMLInputElement> | undefined;
163
+ onRateChangeCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
164
+ onResize?: React.ReactEventHandler<HTMLInputElement> | undefined;
165
+ onResizeCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
166
+ onSeeked?: React.ReactEventHandler<HTMLInputElement> | undefined;
167
+ onSeekedCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
168
+ onSeeking?: React.ReactEventHandler<HTMLInputElement> | undefined;
169
+ onSeekingCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
170
+ onStalled?: React.ReactEventHandler<HTMLInputElement> | undefined;
171
+ onStalledCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
172
+ onSuspend?: React.ReactEventHandler<HTMLInputElement> | undefined;
173
+ onSuspendCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
174
+ onTimeUpdate?: React.ReactEventHandler<HTMLInputElement> | undefined;
175
+ onTimeUpdateCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
176
+ onVolumeChange?: React.ReactEventHandler<HTMLInputElement> | undefined;
177
+ onVolumeChangeCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
178
+ onWaiting?: React.ReactEventHandler<HTMLInputElement> | undefined;
179
+ onWaitingCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
180
+ onAuxClick?: React.MouseEventHandler<HTMLInputElement> | undefined;
181
+ onAuxClickCapture?: React.MouseEventHandler<HTMLInputElement> | undefined;
182
+ onClick?: React.MouseEventHandler<HTMLInputElement> | undefined;
183
+ onClickCapture?: React.MouseEventHandler<HTMLInputElement> | undefined;
184
+ onContextMenu?: React.MouseEventHandler<HTMLInputElement> | undefined;
185
+ onContextMenuCapture?: React.MouseEventHandler<HTMLInputElement> | undefined;
186
+ onDoubleClick?: React.MouseEventHandler<HTMLInputElement> | undefined;
187
+ onDoubleClickCapture?: React.MouseEventHandler<HTMLInputElement> | undefined;
188
+ onDrag?: React.DragEventHandler<HTMLInputElement> | undefined;
189
+ onDragCapture?: React.DragEventHandler<HTMLInputElement> | undefined;
190
+ onDragEnd?: React.DragEventHandler<HTMLInputElement> | undefined;
191
+ onDragEndCapture?: React.DragEventHandler<HTMLInputElement> | undefined;
192
+ onDragEnter?: React.DragEventHandler<HTMLInputElement> | undefined;
193
+ onDragEnterCapture?: React.DragEventHandler<HTMLInputElement> | undefined;
194
+ onDragExit?: React.DragEventHandler<HTMLInputElement> | undefined;
195
+ onDragExitCapture?: React.DragEventHandler<HTMLInputElement> | undefined;
196
+ onDragLeave?: React.DragEventHandler<HTMLInputElement> | undefined;
197
+ onDragLeaveCapture?: React.DragEventHandler<HTMLInputElement> | undefined;
198
+ onDragOver?: React.DragEventHandler<HTMLInputElement> | undefined;
199
+ onDragOverCapture?: React.DragEventHandler<HTMLInputElement> | undefined;
200
+ onDragStart?: React.DragEventHandler<HTMLInputElement> | undefined;
201
+ onDragStartCapture?: React.DragEventHandler<HTMLInputElement> | undefined;
202
+ onDrop?: React.DragEventHandler<HTMLInputElement> | undefined;
203
+ onDropCapture?: React.DragEventHandler<HTMLInputElement> | undefined;
204
+ onMouseDown?: React.MouseEventHandler<HTMLInputElement> | undefined;
205
+ onMouseDownCapture?: React.MouseEventHandler<HTMLInputElement> | undefined;
206
+ onMouseEnter?: React.MouseEventHandler<HTMLInputElement> | undefined;
207
+ onMouseLeave?: React.MouseEventHandler<HTMLInputElement> | undefined;
208
+ onMouseMove?: React.MouseEventHandler<HTMLInputElement> | undefined;
209
+ onMouseMoveCapture?: React.MouseEventHandler<HTMLInputElement> | undefined;
210
+ onMouseOut?: React.MouseEventHandler<HTMLInputElement> | undefined;
211
+ onMouseOutCapture?: React.MouseEventHandler<HTMLInputElement> | undefined;
212
+ onMouseOver?: React.MouseEventHandler<HTMLInputElement> | undefined;
213
+ onMouseOverCapture?: React.MouseEventHandler<HTMLInputElement> | undefined;
214
+ onMouseUp?: React.MouseEventHandler<HTMLInputElement> | undefined;
215
+ onMouseUpCapture?: React.MouseEventHandler<HTMLInputElement> | undefined;
216
+ onSelectCapture?: React.ReactEventHandler<HTMLInputElement> | undefined;
217
+ onTouchCancel?: React.TouchEventHandler<HTMLInputElement> | undefined;
218
+ onTouchCancelCapture?: React.TouchEventHandler<HTMLInputElement> | undefined;
219
+ onTouchEnd?: React.TouchEventHandler<HTMLInputElement> | undefined;
220
+ onTouchEndCapture?: React.TouchEventHandler<HTMLInputElement> | undefined;
221
+ onTouchMove?: React.TouchEventHandler<HTMLInputElement> | undefined;
222
+ onTouchMoveCapture?: React.TouchEventHandler<HTMLInputElement> | undefined;
223
+ onTouchStart?: React.TouchEventHandler<HTMLInputElement> | undefined;
224
+ onTouchStartCapture?: React.TouchEventHandler<HTMLInputElement> | undefined;
225
+ onPointerDown?: React.PointerEventHandler<HTMLInputElement> | undefined;
226
+ onPointerDownCapture?: React.PointerEventHandler<HTMLInputElement> | undefined;
227
+ onPointerMove?: React.PointerEventHandler<HTMLInputElement> | undefined;
228
+ onPointerMoveCapture?: React.PointerEventHandler<HTMLInputElement> | undefined;
229
+ onPointerUp?: React.PointerEventHandler<HTMLInputElement> | undefined;
230
+ onPointerUpCapture?: React.PointerEventHandler<HTMLInputElement> | undefined;
231
+ onPointerCancel?: React.PointerEventHandler<HTMLInputElement> | undefined;
232
+ onPointerCancelCapture?: React.PointerEventHandler<HTMLInputElement> | undefined;
233
+ onPointerEnter?: React.PointerEventHandler<HTMLInputElement> | undefined;
234
+ onPointerLeave?: React.PointerEventHandler<HTMLInputElement> | undefined;
235
+ onPointerOver?: React.PointerEventHandler<HTMLInputElement> | undefined;
236
+ onPointerOverCapture?: React.PointerEventHandler<HTMLInputElement> | undefined;
237
+ onPointerOut?: React.PointerEventHandler<HTMLInputElement> | undefined;
238
+ onPointerOutCapture?: React.PointerEventHandler<HTMLInputElement> | undefined;
239
+ onGotPointerCapture?: React.PointerEventHandler<HTMLInputElement> | undefined;
240
+ onGotPointerCaptureCapture?: React.PointerEventHandler<HTMLInputElement> | undefined;
241
+ onLostPointerCapture?: React.PointerEventHandler<HTMLInputElement> | undefined;
242
+ onLostPointerCaptureCapture?: React.PointerEventHandler<HTMLInputElement> | undefined;
243
+ onScroll?: React.UIEventHandler<HTMLInputElement> | undefined;
244
+ onScrollCapture?: React.UIEventHandler<HTMLInputElement> | undefined;
245
+ onWheel?: React.WheelEventHandler<HTMLInputElement> | undefined;
246
+ onWheelCapture?: React.WheelEventHandler<HTMLInputElement> | undefined;
247
+ onAnimationStart?: React.AnimationEventHandler<HTMLInputElement> | undefined;
248
+ onAnimationStartCapture?: React.AnimationEventHandler<HTMLInputElement> | undefined;
249
+ onAnimationEnd?: React.AnimationEventHandler<HTMLInputElement> | undefined;
250
+ onAnimationEndCapture?: React.AnimationEventHandler<HTMLInputElement> | undefined;
251
+ onAnimationIteration?: React.AnimationEventHandler<HTMLInputElement> | undefined;
252
+ onAnimationIterationCapture?: React.AnimationEventHandler<HTMLInputElement> | undefined;
253
+ onTransitionEnd?: React.TransitionEventHandler<HTMLInputElement> | undefined;
254
+ onTransitionEndCapture?: React.TransitionEventHandler<HTMLInputElement> | undefined;
255
+ form?: string | undefined | undefined;
256
+ list?: string | undefined | undefined;
257
+ status?: "default" | "warning" | "error" | undefined;
258
+ step?: number | string | undefined | undefined;
259
+ normalize?: ((value: string) => string) | undefined;
260
+ warning?: boolean | undefined;
261
+ error?: boolean | undefined;
262
+ size?: "sm" | "md" | "lg" | undefined;
263
+ disabled?: boolean | undefined;
264
+ fullwidth?: boolean | undefined;
265
+ title?: string | undefined | undefined;
266
+ startIcon?: React.ReactNode;
267
+ endIcon?: React.ReactNode;
268
+ formAction?: string | undefined;
269
+ formEncType?: string | undefined | undefined;
270
+ formMethod?: string | undefined | undefined;
271
+ formNoValidate?: boolean | undefined | undefined;
272
+ formTarget?: string | undefined | undefined;
273
+ defaultChecked?: boolean | undefined | undefined;
274
+ defaultValue?: string | number | readonly string[] | undefined;
275
+ suppressContentEditableWarning?: boolean | undefined | undefined;
276
+ accessKey?: string | undefined | undefined;
277
+ autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters" | undefined | (string & {}) | undefined;
278
+ autoFocus?: boolean | undefined | undefined;
279
+ contentEditable?: "inherit" | (boolean | "true" | "false") | "plaintext-only" | undefined;
280
+ contextMenu?: string | undefined | undefined;
281
+ dir?: string | undefined | undefined;
282
+ draggable?: (boolean | "true" | "false") | undefined;
283
+ enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined | undefined;
284
+ hidden?: boolean | undefined | undefined;
285
+ nonce?: string | undefined | undefined;
286
+ slot?: string | undefined | undefined;
287
+ spellCheck?: (boolean | "true" | "false") | undefined;
288
+ translate?: "yes" | "no" | undefined | undefined;
289
+ radioGroup?: string | undefined | undefined;
290
+ about?: string | undefined | undefined;
291
+ content?: string | undefined | undefined;
292
+ datatype?: string | undefined | undefined;
293
+ inlist?: any;
294
+ prefix?: string | undefined | undefined;
295
+ property?: string | undefined | undefined;
296
+ rel?: string | undefined | undefined;
297
+ resource?: string | undefined | undefined;
298
+ rev?: string | undefined | undefined;
299
+ typeof?: string | undefined | undefined;
300
+ vocab?: string | undefined | undefined;
301
+ autoCorrect?: string | undefined | undefined;
302
+ autoSave?: string | undefined | undefined;
303
+ itemProp?: string | undefined | undefined;
304
+ itemScope?: boolean | undefined | undefined;
305
+ itemType?: string | undefined | undefined;
306
+ itemID?: string | undefined | undefined;
307
+ itemRef?: string | undefined | undefined;
308
+ results?: number | undefined | undefined;
309
+ security?: string | undefined | undefined;
310
+ unselectable?: "on" | "off" | undefined | undefined;
311
+ inputMode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined | undefined;
312
+ is?: string | undefined | undefined;
313
+ rounded?: "none" | "normal" | "full" | undefined;
314
+ hasSearchIcon?: boolean | undefined;
315
+ isFloatingLabel?: boolean | undefined;
316
+ accept?: string | undefined | undefined;
317
+ alt?: string | undefined | undefined;
318
+ capture?: boolean | "user" | "environment" | undefined | undefined;
319
+ checked?: boolean | undefined | undefined;
320
+ maxLength?: number | undefined | undefined;
321
+ minLength?: number | undefined | undefined;
322
+ multiple?: boolean | undefined | undefined;
323
+ pattern?: string | undefined | undefined;
324
+ placeholder?: string | undefined | undefined;
325
+ readOnly?: boolean | undefined | undefined;
326
+ required?: boolean | undefined;
327
+ src?: string | undefined | undefined;
328
+ label?: string | undefined;
329
+ iconMode?: "flat" | "solid" | undefined;
330
+ helperText?: string | undefined;
331
+ errorMessage?: string | undefined;
332
+ warningMessage?: string | undefined;
333
+ keepCloseIconOnValue?: boolean | undefined;
334
+ keepFooterSpace?: boolean | undefined;
335
+ labelClassName?: string | undefined;
336
+ classes?: {
337
+ iconWrapper?: string;
338
+ iconSearchWrapper?: string;
339
+ icon?: string;
340
+ startIconWrapper?: string;
341
+ endIconWrapper?: string;
342
+ } | undefined;
343
+ onClickStartIcon?: (() => void) | undefined;
344
+ onClickEndIcon?: (() => void) | undefined;
345
+ renderStartIcon?: (() => React.ReactNode) | undefined;
346
+ renderEndIcon?: (() => React.ReactNode) | undefined;
347
+ trimOnCommit?: boolean | undefined;
348
+ normalizeOnCommit?: ((value: string) => string) | undefined;
349
+ ref?: React.ForwardedRef<HTMLInputElement> | undefined;
350
+ }>) => import("react/jsx-runtime").JSX.Element)[];
351
+ };
352
+ export default meta;
353
+ export declare const Default: StoryObj<typeof AutoComplete>;
354
+ export declare const ShowNoOptionsMessage: StoryObj;
355
+ export declare const FreeSolo: StoryObj;
356
+ export declare const Async: StoryObj;
357
+ export declare const WithFormValidation: StoryObj;
358
+ export declare const InsideDialog: StoryObj;
359
+ export declare const Sizes: StoryObj;
360
+ export declare const States: StoryObj;
361
+ export declare const LoadingState: StoryObj;
362
+ export declare const KeyboardNavigation: StoryObj;
@@ -0,0 +1,2 @@
1
+ export { default } from "./AutoComplete";
2
+ export type { AutoCompleteProps, AutoCompleteOption } from "./AutoComplete";
@@ -9,6 +9,9 @@ export { default as TextArea } from "./components/TextArea/TextArea";
9
9
  export { default as Text } from "./components/Text/Text";
10
10
  export { default as Tabs } from "./components/Tabs/Tabs";
11
11
  export { default as Dropdown } from "./components/Dropdown/Dropdown";
12
+ export { menuItemBaseStyles } from "./components/Dropdown/Dropdown";
13
+ export { default as AutoComplete } from "./components/AutoComplete/AutoComplete";
14
+ export type { AutoCompleteProps, AutoCompleteOption } from "./components/AutoComplete/AutoComplete";
12
15
  export { Checkbox } from "./components/Checkbox/Checkbox";
13
16
  export { Label } from "./components/Label/Label";
14
17
  export { Input } from "./components/Input/Input";
@@ -0,0 +1,103 @@
1
+ "use client";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ import { forwardRef, useCallback, useRef, useState, } from "react";
15
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
16
+ import TextInput from "../TextInput/TextInput";
17
+ import Loading from "../Loading/Loading";
18
+ import Text from "../Text/Text";
19
+ import Icon from "../Icon/Icon";
20
+ import { cn } from "@/utils/cn";
21
+ import { menuItemBaseStyles } from "../Dropdown/Dropdown";
22
+ // ---------------------------------------------------------------------------
23
+ // AutoComplete
24
+ // ---------------------------------------------------------------------------
25
+ function AutoCompleteInner(_a, ref) {
26
+ var _b;
27
+ var {
28
+ // AutoComplete-specific props
29
+ options, value = "", onChange, onSelect, onBlur, onSearch, loading = false, noOptionsText = "No results", showNoOptions = false, renderOption, filterOptions = (x) => x, portal = true, listboxClassName, listboxStyle, popoverStyle, optionClassName, optionStyle, "data-testid": testId,
30
+ // InputProps with explicit defaults (rest passes everything else through)
31
+ id, label, fullwidth = true, size = "md", rounded = "normal", variant = "outline", disabled } = _a, rest = __rest(_a, ["options", "value", "onChange", "onSelect", "onBlur", "onSearch", "loading", "noOptionsText", "showNoOptions", "renderOption", "filterOptions", "portal", "listboxClassName", "listboxStyle", "popoverStyle", "optionClassName", "optionStyle", "data-testid", "id", "label", "fullwidth", "size", "rounded", "variant", "disabled"]);
32
+ const [open, setOpen] = useState(false);
33
+ const [activeIndex, setActiveIndex] = useState(-1);
34
+ const inputRef = useRef(null);
35
+ const filteredOptions = filterOptions(options);
36
+ const showPopover = open && (loading || filteredOptions.length > 0 || showNoOptions);
37
+ const commitSelection = useCallback((option) => {
38
+ onChange === null || onChange === void 0 ? void 0 : onChange(option.value);
39
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(option);
40
+ setOpen(false);
41
+ setActiveIndex(-1);
42
+ }, [onChange, onSelect]);
43
+ const handleInputChange = useCallback((e) => {
44
+ const query = e.target.value;
45
+ onChange === null || onChange === void 0 ? void 0 : onChange(query);
46
+ onSearch === null || onSearch === void 0 ? void 0 : onSearch(query);
47
+ setOpen(true);
48
+ setActiveIndex(-1);
49
+ }, [onChange, onSearch]);
50
+ const handleFocus = useCallback((e) => {
51
+ var _a;
52
+ setOpen(true);
53
+ (_a = rest === null || rest === void 0 ? void 0 : rest.onFocus) === null || _a === void 0 ? void 0 : _a.call(rest, e);
54
+ }, [rest === null || rest === void 0 ? void 0 : rest.onFocus]);
55
+ const handleBlur = useCallback(() => {
56
+ // Delay so option button onClick fires before popover closes
57
+ setTimeout(() => {
58
+ setOpen(false);
59
+ setActiveIndex(-1);
60
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur();
61
+ }, 150);
62
+ }, [onBlur]);
63
+ const handleKeyDown = useCallback((e) => {
64
+ if (!open) {
65
+ if (e.key === "ArrowDown" || e.key === "ArrowUp")
66
+ setOpen(true);
67
+ return;
68
+ }
69
+ if (e.key === "ArrowDown") {
70
+ e.preventDefault();
71
+ setActiveIndex((i) => Math.min(i + 1, filteredOptions.length - 1));
72
+ }
73
+ else if (e.key === "ArrowUp") {
74
+ e.preventDefault();
75
+ setActiveIndex((i) => Math.max(i - 1, 0));
76
+ }
77
+ else if (e.key === "Enter") {
78
+ e.preventDefault();
79
+ if (activeIndex >= 0 && filteredOptions[activeIndex]) {
80
+ commitSelection(filteredOptions[activeIndex]);
81
+ }
82
+ }
83
+ else if (e.key === "Escape") {
84
+ setOpen(false);
85
+ setActiveIndex(-1);
86
+ }
87
+ }, [open, activeIndex, filteredOptions, commitSelection]);
88
+ const listContent = (_jsx("div", { role: "listbox", style: Object.assign({ boxShadow: "var(--dropdown-menu-shadow)" }, listboxStyle), className: cn("max-h-60 overflow-y-auto", "rounded-md border border-bg-stroke3", "bg-modal-dropdown-surface text-text-g-contrast-high", !portal && "absolute top-full left-0 w-full -mt-3 z-[51]", portal && "z-[51]", listboxClassName), "data-testid": testId ? `${testId}-listbox` : undefined, children: loading ? (_jsx("div", { className: "flex items-center justify-center py-6", children: _jsx(Loading, { size: 20 }) })) : filteredOptions.length === 0 ? (_jsx("div", { className: "px-4 py-6 text-center", children: _jsx(Text, { variant: "small1", className: "text-[var(--dropdown-menu-default-text)]", children: noOptionsText }) })) : (filteredOptions.map((option, index) => {
89
+ const isSelected = option.value === value;
90
+ const isActive = index === activeIndex;
91
+ return (_jsxs("button", { id: `autocomplete-option-${option.value}`, type: "button", role: "option", "aria-selected": isSelected, style: optionStyle, className: cn(menuItemBaseStyles, "w-full", isSelected &&
92
+ "bg-[var(--dropdown-menu-selected-bg)] text-[var(--dropdown-menu-selected-text)]", isActive &&
93
+ !isSelected &&
94
+ "bg-[var(--dropdown-menu-hover-bg)] text-[var(--dropdown-menu-hover-text)]", optionClassName), onMouseDown: (e) => e.preventDefault(), onClick: () => commitSelection(option), "data-testid": `autocomplete-option-${option.value}`, children: [_jsx("span", { className: "shrink-0 size-4 flex items-center justify-center", children: isSelected && (_jsx(Icon, { type: "heroicons", name: "check", className: "size-4 text-[var(--dropdown-menu-selected-text)]" })) }), renderOption ? renderOption(option, isSelected) : option.label] }, option.value));
95
+ })) }));
96
+ return (_jsxs(PopoverPrimitive.Root, { open: showPopover, children: [_jsx(PopoverPrimitive.Anchor, { asChild: true, children: _jsxs("div", { className: cn("relative", fullwidth && "w-full"), children: [_jsx(TextInput, Object.assign({}, rest, { ref: ref !== null && ref !== void 0 ? ref : inputRef, id: id, label: label, value: value, size: size, rounded: rounded, variant: variant, disabled: disabled, fullwidth: fullwidth, autoComplete: "off", role: "combobox", "aria-expanded": showPopover, "aria-autocomplete": "list", "aria-activedescendant": activeIndex >= 0
97
+ ? `autocomplete-option-${(_b = filteredOptions[activeIndex]) === null || _b === void 0 ? void 0 : _b.value}`
98
+ : undefined, hasClearIcon: true, onChange: handleInputChange, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, "data-testid": testId })), showPopover && !portal && listContent] }) }), portal && (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { onOpenAutoFocus: (e) => e.preventDefault(), onInteractOutside: (e) => e.preventDefault(), onFocusOutside: (e) => e.preventDefault(), side: "bottom", align: "start", sideOffset: -12, style: Object.assign({ width: "var(--radix-popover-trigger-width)", zIndex: 51 }, popoverStyle), children: listContent }) }))] }));
99
+ }
100
+ // forwardRef with generics requires a manual cast
101
+ const AutoComplete = forwardRef(AutoCompleteInner);
102
+ AutoComplete.displayName = "AutoComplete";
103
+ export default AutoComplete;