@a-type/ui 4.1.0-beta.6 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/cjs/components/autocomplete/Autocomplete.d.ts +12 -9
  2. package/dist/cjs/components/autocomplete/Autocomplete.js +19 -24
  3. package/dist/cjs/components/autocomplete/Autocomplete.js.map +1 -1
  4. package/dist/cjs/components/combobox/Combobox.d.ts +202 -0
  5. package/dist/cjs/components/combobox/Combobox.js +311 -0
  6. package/dist/cjs/components/combobox/Combobox.js.map +1 -0
  7. package/dist/cjs/components/combobox/Combobox.stories.d.ts +53 -0
  8. package/dist/cjs/components/combobox/Combobox.stories.js +115 -0
  9. package/dist/cjs/components/combobox/Combobox.stories.js.map +1 -0
  10. package/dist/cjs/components/input/Input.js +1 -1
  11. package/dist/cjs/components/input/Input.js.map +1 -1
  12. package/dist/cjs/components/input/Input.stories.d.ts +1 -0
  13. package/dist/cjs/components/input/Input.stories.js +4 -1
  14. package/dist/cjs/components/input/Input.stories.js.map +1 -1
  15. package/dist/css/main.css +4 -4
  16. package/dist/esm/components/autocomplete/Autocomplete.d.ts +12 -9
  17. package/dist/esm/components/autocomplete/Autocomplete.js +18 -24
  18. package/dist/esm/components/autocomplete/Autocomplete.js.map +1 -1
  19. package/dist/esm/components/combobox/Combobox.d.ts +202 -0
  20. package/dist/esm/components/combobox/Combobox.js +271 -0
  21. package/dist/esm/components/combobox/Combobox.js.map +1 -0
  22. package/dist/esm/components/combobox/Combobox.stories.d.ts +53 -0
  23. package/dist/esm/components/combobox/Combobox.stories.js +112 -0
  24. package/dist/esm/components/combobox/Combobox.stories.js.map +1 -0
  25. package/dist/esm/components/input/Input.js +1 -1
  26. package/dist/esm/components/input/Input.js.map +1 -1
  27. package/dist/esm/components/input/Input.stories.d.ts +1 -0
  28. package/dist/esm/components/input/Input.stories.js +3 -0
  29. package/dist/esm/components/input/Input.stories.js.map +1 -1
  30. package/package.json +3 -2
  31. package/src/components/autocomplete/Autocomplete.tsx +46 -66
  32. package/src/components/combobox/Combobox.stories.tsx +289 -0
  33. package/src/components/combobox/Combobox.tsx +757 -0
  34. package/src/components/input/Input.stories.tsx +10 -0
  35. package/src/components/input/Input.tsx +2 -1
@@ -0,0 +1,757 @@
1
+ import { ButtonProps } from '@base-ui/react/button';
2
+ import * as BaseCombobox from '@base-ui/react/combobox';
3
+ import clsx from 'clsx';
4
+ import {
5
+ createContext,
6
+ ReactNode,
7
+ Ref,
8
+ RefObject,
9
+ useContext,
10
+ useRef,
11
+ useState,
12
+ } from 'react';
13
+ import { withClassName } from '../../hooks.js';
14
+ import { PaletteName } from '../../uno/index.js';
15
+ import { Button } from '../button/Button.js';
16
+ import { Chip, ChipProps } from '../chip/Chip.js';
17
+ import { Icon } from '../icon/Icon.js';
18
+ import { Input, InputProps } from '../input/Input.js';
19
+ import {
20
+ arrowClassName,
21
+ itemClassName,
22
+ itemListClassName,
23
+ popupClassName,
24
+ separatorClassName,
25
+ } from '../primitives/menus.js';
26
+ import { ArrowSvg } from '../utility/ArrowSvg.js';
27
+ import { SlotDiv, SlotDivProps } from '../utility/SlotDiv.js';
28
+
29
+ export const comboboxPopupClassName = clsx(
30
+ popupClassName,
31
+ 'layer-components:w-[--anchor-width]',
32
+ );
33
+
34
+ export const comboboxBackdropClassName = clsx(
35
+ 'layer-components:(fixed inset-0)',
36
+ );
37
+
38
+ export const comboboxListClassName = clsx(
39
+ itemListClassName,
40
+ 'layer-components:(flex flex-col overscroll-contain outline-none overflow-y-auto overflow-unstable)',
41
+ 'layer-components:empty:(p-0)',
42
+ );
43
+
44
+ export const comboboxIconClassName = clsx(
45
+ 'icon',
46
+ 'layer-components:(flex shrink-0 items-center justify-center transition-transform)',
47
+ 'layer-components:data-[open]:(rotate-180)',
48
+ );
49
+
50
+ export const comboboxEmptyClassName = clsx(
51
+ 'layer-components:[&:not(:empty)]:(p-sm text-sm color-gray-dark)',
52
+ );
53
+
54
+ export const comboboxGroupClassName = clsx(
55
+ 'layer-components:(flex flex-col gap-xs overflow-hidden p-sm)',
56
+ );
57
+
58
+ export const comboboxGroupItemListClassName = clsx(
59
+ 'layer-components:(flex flex-row flex-wrap gap-xs)',
60
+ );
61
+
62
+ export const comboboxGroupLabelClassName = clsx(
63
+ 'layer-components:(w-full px-xs text-xs font-medium uppercase color-gray-dark)',
64
+ );
65
+
66
+ export const comboboxRowClassName = clsx(
67
+ 'layer-components:(flex items-center gap-xs)',
68
+ );
69
+
70
+ export const comboboxGroupItemClassName = clsx(
71
+ 'layer-composed-2:(bg-white)',
72
+ 'layer-composed-2:data-[highlighted]:(ring-2 bg-main-wash ring-primary)',
73
+ );
74
+
75
+ export interface ComboboxInputProps
76
+ extends Omit<BaseCombobox.ComboboxInputProps, 'className' | 'render'> {
77
+ ref?: React.Ref<HTMLInputElement>;
78
+ icon?: ReactNode;
79
+ disableCaret?: boolean;
80
+ disableClear?: boolean;
81
+ children?: ReactNode;
82
+ className?: string;
83
+ }
84
+
85
+ export function ComboboxComposedInput({
86
+ open,
87
+ ref,
88
+ className,
89
+ icon,
90
+ children,
91
+ disableCaret,
92
+ disableClear,
93
+ ...props
94
+ }: ComboboxInputProps & {
95
+ className?: string;
96
+ ref?: React.Ref<HTMLInputElement>;
97
+ open: boolean;
98
+ disableCaret?: boolean;
99
+ disableClear?: boolean;
100
+ children?: React.ReactNode;
101
+ icon?: React.ReactNode;
102
+ render?: InputProps['render'];
103
+ }) {
104
+ const hasValue = !!useContext(ComboboxValueContext);
105
+ const isInChips = useContext(ComboboxChipsContext);
106
+
107
+ const clearAndCaret = ((!disableClear && hasValue) || !disableCaret) && (
108
+ <div className="flex flex-shrink-0 items-center">
109
+ {!disableClear && hasValue && <ComboboxClear />}
110
+ {!disableCaret && <ComboboxTrigger open={open} />}
111
+ </div>
112
+ );
113
+
114
+ if (isInChips) {
115
+ return (
116
+ <div
117
+ className={clsx(
118
+ 'max-w-full min-w-12ch flex flex-1 flex-row items-center gap-xs',
119
+ className,
120
+ )}
121
+ >
122
+ <Input.Input
123
+ autoComplete="off"
124
+ ref={ref}
125
+ className={clsx('layer-components:(min-w-3ch flex-1)')}
126
+ minLength={3}
127
+ {...props}
128
+ />
129
+ {clearAndCaret}
130
+ {children}
131
+ </div>
132
+ );
133
+ }
134
+
135
+ return (
136
+ <Input.Border ref={ref} className={className}>
137
+ {icon}
138
+ <Input.Input autoComplete="off" minLength={3} {...props} />
139
+ {clearAndCaret}
140
+ {children}
141
+ </Input.Border>
142
+ );
143
+ }
144
+
145
+ export const ComboboxValueContext = createContext<any>(null);
146
+
147
+ const ComboboxCreatableContext = createContext<{
148
+ onInputEnter?: (value: string) => void;
149
+ inputValue: string;
150
+ showCreatableItem?: boolean;
151
+ }>({
152
+ inputValue: '',
153
+ });
154
+
155
+ const createableSymbol = Symbol('combobox-creatable');
156
+ function makeCreatableItem(input: string) {
157
+ const item: any = {
158
+ [createableSymbol]: true,
159
+ value: input,
160
+ label: input,
161
+ key: input,
162
+ id: input,
163
+ };
164
+ return item;
165
+ }
166
+ function isCreatableItem(item: any): item is { value: string; label: string } {
167
+ return !!item?.[createableSymbol];
168
+ }
169
+ function getCreateableItem(value: any) {
170
+ if (isCreatableItem(value)) {
171
+ return value;
172
+ }
173
+ if (Array.isArray(value)) {
174
+ return value.find((v) => isCreatableItem(v));
175
+ }
176
+ return null;
177
+ }
178
+ function matchItem(
179
+ items: readonly any[],
180
+ itemToStringLabel: ((item: any) => string) | undefined,
181
+ input: string | number | readonly string[],
182
+ ) {
183
+ return items.find((item) => {
184
+ const label = itemToStringLabel
185
+ ? itemToStringLabel(item)
186
+ : item?.label || item?.value || '';
187
+ return label.toLowerCase() === input.toString().trim().toLowerCase();
188
+ });
189
+ }
190
+
191
+ export interface ComboboxProps<
192
+ TItem,
193
+ Multiple extends boolean | undefined = false,
194
+ > extends BaseCombobox.ComboboxRootProps<TItem, Multiple> {
195
+ onCreate?: (value: string) => void;
196
+ showCreatableItem?: boolean;
197
+ }
198
+ const ComboboxRoot = ({
199
+ onCreate,
200
+ items: userItems,
201
+ onItemHighlighted,
202
+ itemToStringLabel: userItemToStringLabel,
203
+ inputValue: userInputValue,
204
+ onInputValueChange: userOnInputValueChange,
205
+ onValueChange: userOnValueChange,
206
+ showCreatableItem,
207
+ ...props
208
+ }: ComboboxProps<any, any>) => {
209
+ const highlightedItemRef = useRef<any>(null);
210
+ const handleItemHighlighted = (
211
+ item: any,
212
+ ev: BaseCombobox.ComboboxRootHighlightEventDetails,
213
+ ) => {
214
+ highlightedItemRef.current = item;
215
+ onItemHighlighted?.(item, ev);
216
+ };
217
+ const onInputEnter = onCreate
218
+ ? (value: string) => {
219
+ if (highlightedItemRef.current) return;
220
+ onCreate(value);
221
+ }
222
+ : undefined;
223
+
224
+ const [internalInputValue, setInternalInputValue] = useState(
225
+ userInputValue || props.defaultInputValue || '',
226
+ );
227
+
228
+ const inputValue = (
229
+ userInputValue === undefined ? internalInputValue : userInputValue
230
+ ).toString();
231
+ const canCreate = !!(
232
+ showCreatableItem &&
233
+ onCreate &&
234
+ inputValue &&
235
+ !matchItem(userItems ?? [], userItemToStringLabel, inputValue)
236
+ );
237
+ const items = canCreate
238
+ ? userItems
239
+ ? [...userItems, makeCreatableItem(inputValue)]
240
+ : [makeCreatableItem(inputValue)]
241
+ : userItems;
242
+
243
+ const anchorRef = useRef<HTMLDivElement>(null);
244
+
245
+ return (
246
+ <ComboboxAnchorContext.Provider value={anchorRef}>
247
+ <ComboboxCreatableContext.Provider
248
+ value={{ inputValue, onInputEnter, showCreatableItem: canCreate }}
249
+ >
250
+ <ComboboxValueContext.Provider value={props.value || null}>
251
+ <BaseCombobox.Combobox.Root
252
+ {...props}
253
+ items={items}
254
+ inputValue={userInputValue}
255
+ itemToStringLabel={userItemToStringLabel}
256
+ onInputValueChange={(value, ev) => {
257
+ if (userInputValue === undefined) {
258
+ setInternalInputValue(value);
259
+ }
260
+ userOnInputValueChange?.(value, ev);
261
+ }}
262
+ onValueChange={(value, ev) => {
263
+ const creatable = getCreateableItem(value);
264
+ if (creatable) {
265
+ onCreate?.(creatable.value);
266
+ if (value === creatable) {
267
+ return;
268
+ }
269
+ if (Array.isArray(value)) {
270
+ value = value.filter((v) => v !== creatable);
271
+ }
272
+ }
273
+
274
+ userOnValueChange?.(value, ev);
275
+ }}
276
+ onItemHighlighted={handleItemHighlighted}
277
+ />
278
+ </ComboboxValueContext.Provider>
279
+ </ComboboxCreatableContext.Provider>
280
+ </ComboboxAnchorContext.Provider>
281
+ );
282
+ };
283
+
284
+ function ComboboxTrigger({
285
+ children,
286
+ open,
287
+ ...props
288
+ }: BaseCombobox.ComboboxTriggerProps & {
289
+ open?: boolean;
290
+ }) {
291
+ return (
292
+ <BaseCombobox.Combobox.Trigger
293
+ render={<Button emphasis="ghost" size="small" />}
294
+ {...props}
295
+ >
296
+ {children ?? <ComboboxIcon data-open={open} />}
297
+ </BaseCombobox.Combobox.Trigger>
298
+ );
299
+ }
300
+
301
+ function ComboboxInput({
302
+ disableCaret,
303
+ disableClear,
304
+ icon,
305
+ children,
306
+ onKeyDown: userOnKeyDown,
307
+ ...outerProps
308
+ }: ComboboxInputProps) {
309
+ const { onInputEnter } = useContext(ComboboxCreatableContext);
310
+ const handleKeyDown: ComboboxInputProps['onKeyDown'] = (ev) => {
311
+ if (ev.key === 'Enter' && onInputEnter) {
312
+ onInputEnter(ev.currentTarget.value);
313
+ }
314
+ userOnKeyDown?.(ev);
315
+ };
316
+ return (
317
+ <BaseCombobox.Combobox.Input
318
+ onKeyDown={handleKeyDown}
319
+ render={(props, state) => (
320
+ <ComboboxComposedInput
321
+ {...props}
322
+ open={state.open}
323
+ disableCaret={disableCaret}
324
+ disableClear={disableClear}
325
+ icon={icon}
326
+ >
327
+ {children}
328
+ </ComboboxComposedInput>
329
+ )}
330
+ {...outerProps}
331
+ />
332
+ );
333
+ }
334
+
335
+ const ComboboxAnchorContext =
336
+ createContext<RefObject<HTMLDivElement | null> | null>(null);
337
+
338
+ const ComboboxChipsContext = createContext(false);
339
+
340
+ function ComboboxChips({
341
+ className,
342
+ ...props
343
+ }: BaseCombobox.ComboboxChipsProps) {
344
+ const anchorRef = useContext(ComboboxAnchorContext);
345
+ return (
346
+ <ComboboxChipsContext.Provider value={true}>
347
+ <BaseCombobox.Combobox.Chips
348
+ ref={anchorRef || undefined}
349
+ className={clsx(
350
+ 'layer-components:(flex flex-row items-center gap-xs)',
351
+ className,
352
+ )}
353
+ render={<Input.Border />}
354
+ {...props}
355
+ />
356
+ </ComboboxChipsContext.Provider>
357
+ );
358
+ }
359
+
360
+ const ComboboxChipsList = withClassName(
361
+ SlotDiv,
362
+ 'layer-components:(flex flex-row flex-wrap gap-xs p-xs)',
363
+ 'layer-components:empty:hidden',
364
+ );
365
+
366
+ function ComboboxChip({
367
+ className,
368
+ children,
369
+ ...props
370
+ }: BaseCombobox.ComboboxChipProps) {
371
+ return (
372
+ <BaseCombobox.Combobox.Chip
373
+ render={<Chip />}
374
+ className={clsx(
375
+ 'layer-composed-2:(my-auto flex flex-row items-center gap-xs px-sm bg-white)',
376
+ className,
377
+ )}
378
+ {...props}
379
+ >
380
+ {children}
381
+ <BaseCombobox.Combobox.ChipRemove
382
+ render={
383
+ <Button
384
+ size="small"
385
+ emphasis="ghost"
386
+ className="min-h-0 min-w-0 p-0 leading-1"
387
+ >
388
+ <Icon name="x" size={10} />
389
+ </Button>
390
+ }
391
+ />
392
+ </BaseCombobox.Combobox.Chip>
393
+ );
394
+ }
395
+
396
+ function ComboboxIcon({ className, ...props }: BaseCombobox.ComboboxIconProps) {
397
+ return (
398
+ <BaseCombobox.Combobox.Icon
399
+ {...props}
400
+ className={clsx(comboboxIconClassName, className)}
401
+ >
402
+ <Icon name="chevron" />
403
+ </BaseCombobox.Combobox.Icon>
404
+ );
405
+ }
406
+
407
+ const ComboboxPopup = withClassName(
408
+ BaseCombobox.Combobox.Popup,
409
+ comboboxPopupClassName,
410
+ );
411
+
412
+ const ComboboxBackdrop = withClassName(
413
+ BaseCombobox.Combobox.Backdrop,
414
+ comboboxBackdropClassName,
415
+ );
416
+
417
+ function ComboboxArrow({
418
+ className,
419
+ ...props
420
+ }: BaseCombobox.ComboboxArrowProps) {
421
+ return (
422
+ <BaseCombobox.Combobox.Arrow
423
+ {...props}
424
+ className={clsx(arrowClassName, className)}
425
+ >
426
+ <ArrowSvg />
427
+ </BaseCombobox.Combobox.Arrow>
428
+ );
429
+ }
430
+
431
+ export interface ComboboxPopupProps extends BaseCombobox.ComboboxPopupProps {
432
+ positioner?: BaseCombobox.ComboboxPositionerProps;
433
+ ref?: Ref<HTMLDivElement>;
434
+ arrow?: boolean;
435
+ }
436
+ function ComboboxContent({
437
+ positioner,
438
+ arrow,
439
+ children,
440
+ ...props
441
+ }: ComboboxPopupProps) {
442
+ const anchorRef = useContext(ComboboxAnchorContext);
443
+ return (
444
+ <BaseCombobox.Combobox.Portal>
445
+ <ComboboxBackdrop />
446
+ <BaseCombobox.Combobox.Positioner
447
+ sideOffset={8}
448
+ anchor={anchorRef}
449
+ {...positioner}
450
+ >
451
+ <ComboboxPopup {...props}>
452
+ {arrow && <ComboboxArrow />}
453
+ {children}
454
+ </ComboboxPopup>
455
+ </BaseCombobox.Combobox.Positioner>
456
+ </BaseCombobox.Combobox.Portal>
457
+ );
458
+ }
459
+
460
+ function ComboboxList({ className, ...props }: BaseCombobox.ComboboxListProps) {
461
+ return (
462
+ <BaseCombobox.Combobox.List
463
+ className={clsx(comboboxListClassName, className)}
464
+ {...props}
465
+ />
466
+ );
467
+ }
468
+
469
+ const ComboboxEmpty = withClassName(
470
+ BaseCombobox.Combobox.Empty,
471
+ comboboxEmptyClassName,
472
+ );
473
+
474
+ export interface ComboboxItemProps extends BaseCombobox.ComboboxItemProps {
475
+ ref?: Ref<HTMLDivElement>;
476
+ color?: PaletteName;
477
+ }
478
+ function ComboboxItem({
479
+ className,
480
+ color = 'gray',
481
+ children,
482
+ ...props
483
+ }: ComboboxItemProps) {
484
+ return (
485
+ <BaseCombobox.Combobox.Item
486
+ {...props}
487
+ className={clsx(
488
+ color && `palette-${color}`,
489
+ itemClassName,
490
+ 'layer-components:data-[selected]:color-gray-dark',
491
+ className,
492
+ )}
493
+ >
494
+ {children}
495
+ <ComboboxItemIndicator />
496
+ </BaseCombobox.Combobox.Item>
497
+ );
498
+ }
499
+
500
+ const ComboboxItemIndicator = withClassName(
501
+ (props: BaseCombobox.ComboboxItemIndicatorProps) => (
502
+ <BaseCombobox.Combobox.ItemIndicator
503
+ {...props}
504
+ render={({ children: _, ...rest }) => <Icon name="check" {...rest} />}
505
+ />
506
+ ),
507
+ 'layer-components:(ml-auto)',
508
+ );
509
+
510
+ const ComboboxGroup = withClassName(
511
+ BaseCombobox.Combobox.Group,
512
+ comboboxGroupClassName,
513
+ );
514
+
515
+ const ComboboxGroupItemList = withClassName(
516
+ SlotDiv,
517
+ comboboxGroupItemListClassName,
518
+ );
519
+
520
+ const ComboboxGroupLabel = withClassName(
521
+ BaseCombobox.Combobox.GroupLabel,
522
+ comboboxGroupLabelClassName,
523
+ );
524
+
525
+ const ComboboxRow = withClassName(
526
+ BaseCombobox.Combobox.Row,
527
+ comboboxRowClassName,
528
+ );
529
+
530
+ const ComboboxSeparator = withClassName(
531
+ BaseCombobox.Combobox.Separator,
532
+ separatorClassName,
533
+ );
534
+
535
+ export interface ComboboxGroupItemProps
536
+ extends Omit<BaseCombobox.ComboboxItemProps, 'render'> {
537
+ ref?: Ref<HTMLDivElement>;
538
+ replace?: BaseCombobox.ComboboxItemProps['render'];
539
+ render?: ChipProps['render'];
540
+ color?: ChipProps['color'];
541
+ }
542
+ function ComboboxGroupItem({
543
+ className,
544
+ replace,
545
+ render,
546
+ color,
547
+ children,
548
+ ...props
549
+ }: ComboboxGroupItemProps) {
550
+ return (
551
+ <BaseCombobox.Combobox.Item
552
+ render={
553
+ replace ?? <Button render={<Chip render={render} color={color} />} />
554
+ }
555
+ {...props}
556
+ className={clsx(
557
+ comboboxGroupItemClassName,
558
+ 'layer-components:data-[selected]:color-gray-dark',
559
+ className,
560
+ )}
561
+ >
562
+ {children}
563
+ <ComboboxItemIndicator className="layer-components:(h-10px w-10px)" />
564
+ </BaseCombobox.Combobox.Item>
565
+ );
566
+ }
567
+
568
+ export interface ComboboxMultiValueProps
569
+ extends BaseCombobox.ComboboxValueProps,
570
+ Omit<SlotDivProps, 'children'> {}
571
+ function ComboboxMultiValue({
572
+ children,
573
+ className,
574
+ ...props
575
+ }: ComboboxMultiValueProps) {
576
+ return (
577
+ <SlotDiv
578
+ className={clsx('max-w-full flex flex-row flex-wrap gap-xs', className)}
579
+ {...props}
580
+ >
581
+ <BaseCombobox.Combobox.Value>{children}</BaseCombobox.Combobox.Value>
582
+ </SlotDiv>
583
+ );
584
+ }
585
+
586
+ export type ComboboxClearProps = ButtonProps & {
587
+ ref?: Ref<HTMLButtonElement>;
588
+ children?: ReactNode;
589
+ };
590
+ function ComboboxClear({ children, ...props }: ComboboxClearProps) {
591
+ return (
592
+ <BaseCombobox.Combobox.Clear
593
+ render={<Button emphasis="ghost" size="small" />}
594
+ {...props}
595
+ >
596
+ {children ?? <Icon name="x" />}
597
+ </BaseCombobox.Combobox.Clear>
598
+ );
599
+ }
600
+
601
+ const baseSubComponents = {
602
+ useFilter: BaseCombobox.Combobox.useFilter,
603
+
604
+ makeCreatableItem,
605
+ isCreatableItem,
606
+
607
+ Input: ComboboxInput,
608
+ Content: ComboboxContent,
609
+ Empty: ComboboxEmpty,
610
+ List: ComboboxList,
611
+ Item: ComboboxItem,
612
+ Group: ComboboxGroup,
613
+ GroupItemList: ComboboxGroupItemList,
614
+ GroupLabel: ComboboxGroupLabel,
615
+ Row: ComboboxRow,
616
+ Separator: ComboboxSeparator,
617
+ ItemGroup: ComboboxGroupItem,
618
+ Icon: ComboboxIcon,
619
+ Clear: ComboboxClear,
620
+ Chips: ComboboxChips,
621
+ Chip: ComboboxChip,
622
+ ChipsList: ComboboxChipsList,
623
+
624
+ Value: BaseCombobox.Combobox.Value,
625
+ MultiValue: ComboboxMultiValue,
626
+ Positioner: BaseCombobox.Combobox.Positioner,
627
+ Portal: BaseCombobox.Combobox.Portal,
628
+ Backdrop: ComboboxBackdrop,
629
+ Arrow: ComboboxArrow,
630
+ Popup: ComboboxPopup,
631
+
632
+ ListItem: ComboboxItem,
633
+ GroupItem: ComboboxGroupItem,
634
+
635
+ Unstyled: BaseCombobox.Combobox,
636
+ };
637
+
638
+ function createCombobox<TItem>() {
639
+ function TypedRoot(
640
+ props: Omit<ComboboxProps<TItem, false>, 'items'> & {
641
+ items?: readonly TItem[];
642
+ },
643
+ ) {
644
+ return <ComboboxRoot {...props} />;
645
+ }
646
+ function TypedMultiRoot(
647
+ props: Omit<ComboboxProps<TItem, true>, 'items'> & {
648
+ items?: readonly TItem[];
649
+ },
650
+ ) {
651
+ return <ComboboxRoot multiple {...props} />;
652
+ }
653
+ function TypedList(
654
+ props: Omit<BaseCombobox.ComboboxListProps, 'children'> & {
655
+ children?: ReactNode | ((item: TItem, index: number) => ReactNode);
656
+ },
657
+ ) {
658
+ return <ComboboxList {...props} />;
659
+ }
660
+ function TypedItem(
661
+ props: Omit<ComboboxItemProps, 'value'> & {
662
+ value: TItem;
663
+ },
664
+ ) {
665
+ return <ComboboxItem {...props} />;
666
+ }
667
+ function TypedValue(
668
+ props: Omit<BaseCombobox.ComboboxValueProps, 'children'> & {
669
+ children?: ReactNode | ((item: TItem | null) => ReactNode);
670
+ },
671
+ ) {
672
+ return <BaseCombobox.Combobox.Value {...props} />;
673
+ }
674
+ function TypedMultiValue(
675
+ props: Omit<ComboboxMultiValueProps, 'children'> & {
676
+ children?: ReactNode | ((items: TItem[]) => ReactNode);
677
+ },
678
+ ) {
679
+ return <ComboboxMultiValue {...props} />;
680
+ }
681
+ return Object.assign(TypedRoot, {
682
+ ...baseSubComponents,
683
+ Multi: TypedMultiRoot,
684
+ Item: TypedItem,
685
+ List: TypedList,
686
+ Value: TypedValue,
687
+ MultiValue: TypedMultiValue,
688
+ });
689
+ }
690
+
691
+ function createComboboxGrouped<TItemGroup extends { items: readonly any[] }>() {
692
+ function TypedRoot(
693
+ props: Omit<ComboboxProps<TItemGroup['items'][number], false>, 'items'> & {
694
+ items?: readonly TItemGroup[];
695
+ },
696
+ ) {
697
+ return <ComboboxRoot {...(props as any)} />;
698
+ }
699
+ function TypedMultiRoot(
700
+ props: Omit<ComboboxProps<TItemGroup['items'][number], true>, 'items'> & {
701
+ items?: readonly TItemGroup[];
702
+ },
703
+ ) {
704
+ return <ComboboxRoot multiple {...(props as any)} />;
705
+ }
706
+ function TypedList(
707
+ props: Omit<BaseCombobox.ComboboxListProps, 'children'> & {
708
+ children?: ReactNode | ((item: TItemGroup, index: number) => ReactNode);
709
+ },
710
+ ) {
711
+ return <ComboboxList {...(props as any)} />;
712
+ }
713
+ function TypedItem(
714
+ props: Omit<ComboboxGroupItemProps, 'value'> & {
715
+ value: TItemGroup['items'][number];
716
+ },
717
+ ) {
718
+ return <ComboboxGroupItem {...(props as any)} />;
719
+ }
720
+ function TypedValue(
721
+ props: Omit<BaseCombobox.ComboboxValueProps, 'children'> & {
722
+ children?:
723
+ | ReactNode
724
+ | ((item: TItemGroup['items'][number] | null) => ReactNode);
725
+ },
726
+ ) {
727
+ return <BaseCombobox.Combobox.Value {...props} />;
728
+ }
729
+ function TypedMultiValue(
730
+ props: Omit<ComboboxMultiValueProps, 'children'> & {
731
+ children?:
732
+ | ReactNode
733
+ | ((items: TItemGroup['items'][number][]) => ReactNode);
734
+ },
735
+ ) {
736
+ return <ComboboxMultiValue {...props} />;
737
+ }
738
+ return Object.assign(TypedRoot, {
739
+ ...baseSubComponents,
740
+ Multi: TypedMultiRoot,
741
+ Item: TypedItem,
742
+ List: TypedList,
743
+ Group: ComboboxGroup,
744
+ GroupList: ComboboxGroupItemList,
745
+ GroupLabel: ComboboxGroupLabel,
746
+ Row: ComboboxRow,
747
+ Value: TypedValue,
748
+ MultiValue: TypedMultiValue,
749
+ });
750
+ }
751
+
752
+ export const Combobox = Object.assign(ComboboxRoot, {
753
+ create: createCombobox,
754
+ createGrouped: createComboboxGrouped,
755
+
756
+ ...baseSubComponents,
757
+ });