banhaten 0.1.1 → 0.1.2

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 (37) hide show
  1. package/README.md +20 -8
  2. package/package.json +8 -2
  3. package/registry/components/autocomplete.tsx +637 -0
  4. package/registry/components/avatar.tsx +258 -22
  5. package/registry/components/badge.tsx +97 -35
  6. package/registry/components/date-picker-state.ts +253 -0
  7. package/registry/components/date-picker.tsx +115 -158
  8. package/registry/components/expanded/EmptyState.tsx +155 -0
  9. package/registry/components/expanded/emptyState.css +111 -0
  10. package/registry/components/expanded/slideout.css +1 -0
  11. package/registry/components/expanded/table.css +1 -0
  12. package/registry/components/input-otp.tsx +574 -0
  13. package/registry/components/input.tsx +21 -11
  14. package/registry/components/menu.tsx +371 -8
  15. package/registry/components/popover.tsx +840 -0
  16. package/registry/components/select.tsx +4 -0
  17. package/registry/components/skeleton.css +57 -0
  18. package/registry/components/skeleton.tsx +482 -0
  19. package/registry/components/spinner.tsx +79 -11
  20. package/registry/components/textarea.tsx +1 -1
  21. package/registry/components/tooltip.tsx +4 -0
  22. package/registry/examples/autocomplete-demo.tsx +109 -0
  23. package/registry/examples/avatar-demo.tsx +102 -47
  24. package/registry/examples/badge-demo.tsx +16 -0
  25. package/registry/examples/expanded/command-bar-demo.tsx +236 -0
  26. package/registry/examples/expanded/empty-state-demo.tsx +39 -0
  27. package/registry/examples/input-demo.tsx +1 -1
  28. package/registry/examples/input-otp-demo.tsx +72 -0
  29. package/registry/examples/menu-demo.tsx +101 -88
  30. package/registry/examples/popover-demo.tsx +546 -0
  31. package/registry/examples/select-demo.tsx +1 -1
  32. package/registry/examples/skeleton-demo.tsx +56 -0
  33. package/registry/examples/spinner-demo.tsx +23 -1
  34. package/registry/examples/textarea-demo.tsx +1 -1
  35. package/registry/index.json +240 -8
  36. package/registry/styles/globals.css +88 -0
  37. package/src/cli/index.js +997 -62
@@ -0,0 +1,637 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { SearchIcon } from "lucide-react"
5
+
6
+ import { Input, type InputSize, type InputVariant, type InputVisualState } from "./input"
7
+ import {
8
+ SelectHelperText,
9
+ SelectLabel,
10
+ SelectMenu,
11
+ SelectMenuItem,
12
+ SelectRoot,
13
+ type SelectItemType,
14
+ } from "./select"
15
+ import { Tag, type TagSize } from "./tag"
16
+
17
+ import { cn } from "@/lib/utils"
18
+
19
+ type AutocompleteSelectionMode = "single" | "multiple"
20
+ type AutocompleteValue = string | string[] | undefined
21
+
22
+ type AutocompleteOption = {
23
+ addonText?: React.ReactNode
24
+ disabled?: boolean
25
+ itemType?: SelectItemType
26
+ keywords?: string[]
27
+ label: React.ReactNode
28
+ media?: React.ReactNode | false
29
+ value: string
30
+ }
31
+
32
+ type AutocompleteFilter = (
33
+ options: AutocompleteOption[],
34
+ inputValue: string
35
+ ) => AutocompleteOption[]
36
+
37
+ type AutocompleteInputProps = Omit<
38
+ React.ComponentProps<"input">,
39
+ | "className"
40
+ | "defaultValue"
41
+ | "dir"
42
+ | "disabled"
43
+ | "id"
44
+ | "size"
45
+ | "value"
46
+ >
47
+
48
+ type AutocompleteProps = Omit<
49
+ React.ComponentProps<"div">,
50
+ "defaultValue" | "dir" | "onChange"
51
+ > & {
52
+ clearInputOnSelect?: boolean
53
+ defaultInputValue?: string
54
+ defaultOpen?: boolean
55
+ defaultValue?: string | string[]
56
+ dir?: "ltr" | "rtl" | "auto"
57
+ disabled?: boolean
58
+ emptyText?: React.ReactNode
59
+ errorMessage?: React.ReactNode
60
+ filterOptions?: AutocompleteFilter
61
+ hasHelperText?: boolean
62
+ hasInformationIcon?: boolean
63
+ hasLabel?: boolean
64
+ helperText?: React.ReactNode
65
+ inputClassName?: string
66
+ inputId?: string
67
+ inputProps?: AutocompleteInputProps
68
+ inputValue?: string
69
+ isOptional?: boolean
70
+ isRequired?: boolean
71
+ label?: React.ReactNode
72
+ menuClassName?: string
73
+ name?: string
74
+ onInputValueChange?: (value: string) => void
75
+ onOpenChange?: (open: boolean) => void
76
+ onValueChange?: (
77
+ value: AutocompleteValue,
78
+ option: AutocompleteOption | AutocompleteOption[] | null
79
+ ) => void
80
+ open?: boolean
81
+ optionalText?: React.ReactNode
82
+ options: AutocompleteOption[]
83
+ placeholder?: string
84
+ selectionMode?: AutocompleteSelectionMode
85
+ size?: InputSize
86
+ state?: InputVisualState
87
+ tagListClassName?: string
88
+ tagSize?: TagSize
89
+ variant?: InputVariant
90
+ value?: string | string[]
91
+ }
92
+
93
+ const Autocomplete = React.forwardRef<HTMLDivElement, AutocompleteProps>(
94
+ function Autocomplete(
95
+ {
96
+ className,
97
+ clearInputOnSelect,
98
+ defaultInputValue,
99
+ defaultOpen = false,
100
+ defaultValue,
101
+ dir,
102
+ disabled = false,
103
+ emptyText = "No results found",
104
+ errorMessage,
105
+ filterOptions = defaultFilterOptions,
106
+ hasHelperText,
107
+ hasInformationIcon = false,
108
+ hasLabel,
109
+ helperText,
110
+ inputClassName,
111
+ inputId,
112
+ inputProps,
113
+ inputValue,
114
+ isOptional = false,
115
+ isRequired = false,
116
+ label,
117
+ menuClassName,
118
+ name,
119
+ onInputValueChange,
120
+ onOpenChange,
121
+ onBlur: onRootBlur,
122
+ onValueChange,
123
+ open,
124
+ optionalText,
125
+ options,
126
+ placeholder,
127
+ selectionMode = "single",
128
+ size = "lg",
129
+ state,
130
+ tagListClassName,
131
+ tagSize,
132
+ value,
133
+ variant = "default",
134
+ ...props
135
+ },
136
+ ref
137
+ ) {
138
+ const generatedInputId = React.useId()
139
+ const generatedListboxId = React.useId()
140
+ const resolvedInputId = inputId || generatedInputId
141
+ const listboxId = `${resolvedInputId}-${generatedListboxId}-listbox`
142
+ const rootRef = React.useRef<HTMLDivElement | null>(null)
143
+ const isMultiple = selectionMode === "multiple"
144
+ const isValueControlled = value !== undefined
145
+ const isInputControlled = inputValue !== undefined
146
+ const isOpenControlled = open !== undefined
147
+ const [uncontrolledValue, setUncontrolledValue] =
148
+ React.useState<AutocompleteValue>(defaultValue)
149
+ const [uncontrolledInputValue, setUncontrolledInputValue] =
150
+ React.useState(
151
+ defaultInputValue ??
152
+ getInitialInputValue(defaultValue, selectionMode, options)
153
+ )
154
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)
155
+ const [activeIndex, setActiveIndex] = React.useState(-1)
156
+
157
+ const selectedValue = isValueControlled ? value : uncontrolledValue
158
+ const selectedValues = normalizeSelectedValues(selectedValue, isMultiple)
159
+ const query = isInputControlled ? inputValue : uncontrolledInputValue
160
+ const isDisabled = disabled || state === "disabled"
161
+ const isError = state === "error" && !isDisabled
162
+ const selectedOptions = React.useMemo(
163
+ () => getSelectedOptions(options, selectedValues),
164
+ [options, selectedValues]
165
+ )
166
+ const filteredOptions = React.useMemo(
167
+ () => filterOptions(options, query),
168
+ [filterOptions, options, query]
169
+ )
170
+ const hasSelection = selectedValues.length > 0
171
+ const visualState: InputVisualState = isDisabled
172
+ ? "disabled"
173
+ : isError
174
+ ? "error"
175
+ : state ?? (hasSelection || query ? "filled" : "default")
176
+ const shouldRenderLabel = hasLabel ?? hasRenderableContent(label)
177
+ const helperContent = isError ? errorMessage : helperText
178
+ const shouldRenderHelper =
179
+ hasHelperText ?? hasRenderableContent(helperContent)
180
+ const menuOpen =
181
+ !isDisabled &&
182
+ (isOpenControlled ? Boolean(open) : uncontrolledOpen) &&
183
+ (filteredOptions.length > 0 || hasRenderableContent(emptyText))
184
+ const activeOption = filteredOptions[activeIndex]
185
+ const activeOptionId =
186
+ menuOpen && activeOption ? getOptionId(listboxId, activeIndex) : undefined
187
+ const resolvedTagSize =
188
+ tagSize ?? (size === "md" ? ("xs" as const) : ("md" as const))
189
+ const clearOnSelect = clearInputOnSelect ?? isMultiple
190
+
191
+ React.useEffect(() => {
192
+ if (!menuOpen) {
193
+ setActiveIndex(-1)
194
+ return
195
+ }
196
+
197
+ setActiveIndex((currentIndex) => {
198
+ if (currentIndex >= 0 && !filteredOptions[currentIndex]?.disabled) {
199
+ return currentIndex
200
+ }
201
+
202
+ return getFirstEnabledIndex(filteredOptions)
203
+ })
204
+ }, [filteredOptions, menuOpen])
205
+
206
+ function setRootRef(node: HTMLDivElement | null) {
207
+ rootRef.current = node
208
+
209
+ if (typeof ref === "function") {
210
+ ref(node)
211
+ } else if (ref) {
212
+ ref.current = node
213
+ }
214
+ }
215
+
216
+ function setMenuOpen(nextOpen: boolean) {
217
+ if (!isOpenControlled) {
218
+ setUncontrolledOpen(nextOpen)
219
+ }
220
+
221
+ onOpenChange?.(nextOpen)
222
+ }
223
+
224
+ function setQuery(nextQuery: string) {
225
+ if (!isInputControlled) {
226
+ setUncontrolledInputValue(nextQuery)
227
+ }
228
+
229
+ onInputValueChange?.(nextQuery)
230
+ }
231
+
232
+ function setSelectedValue(
233
+ nextValue: AutocompleteValue,
234
+ option: AutocompleteOption | AutocompleteOption[] | null
235
+ ) {
236
+ if (!isValueControlled) {
237
+ setUncontrolledValue(nextValue)
238
+ }
239
+
240
+ onValueChange?.(nextValue, option)
241
+ }
242
+
243
+ function selectOption(option: AutocompleteOption) {
244
+ if (option.disabled) return
245
+
246
+ if (isMultiple) {
247
+ const nextValues = selectedValues.includes(option.value)
248
+ ? selectedValues.filter((item) => item !== option.value)
249
+ : [...selectedValues, option.value]
250
+
251
+ setSelectedValue(nextValues, getSelectedOptions(options, nextValues))
252
+ setQuery(clearOnSelect ? "" : query)
253
+ setMenuOpen(true)
254
+ return
255
+ }
256
+
257
+ setSelectedValue(option.value, option)
258
+ setQuery(clearOnSelect ? "" : getOptionText(option))
259
+ setMenuOpen(false)
260
+ }
261
+
262
+ function removeSelectedValue(valueToRemove: string) {
263
+ const nextValues = selectedValues.filter((item) => item !== valueToRemove)
264
+ setSelectedValue(
265
+ isMultiple ? nextValues : nextValues[0],
266
+ getSelectedOptions(options, nextValues)
267
+ )
268
+ }
269
+
270
+ function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
271
+ inputProps?.onChange?.(event)
272
+ if (event.defaultPrevented) return
273
+
274
+ setQuery(event.currentTarget.value)
275
+ setMenuOpen(true)
276
+ }
277
+
278
+ function handleInputFocus(event: React.FocusEvent<HTMLInputElement>) {
279
+ inputProps?.onFocus?.(event)
280
+ if (!event.defaultPrevented) setMenuOpen(true)
281
+ }
282
+
283
+ function handleRootBlur(event: React.FocusEvent<HTMLDivElement>) {
284
+ onRootBlur?.(event)
285
+ if (event.defaultPrevented) return
286
+
287
+ if (
288
+ event.relatedTarget instanceof Node &&
289
+ event.currentTarget.contains(event.relatedTarget)
290
+ ) {
291
+ return
292
+ }
293
+
294
+ setMenuOpen(false)
295
+ }
296
+
297
+ function handleInputClick(event: React.MouseEvent<HTMLInputElement>) {
298
+ inputProps?.onClick?.(event)
299
+ if (!event.defaultPrevented) setMenuOpen(true)
300
+ }
301
+
302
+ function handleInputKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {
303
+ inputProps?.onKeyDown?.(event)
304
+ if (event.defaultPrevented) return
305
+
306
+ if (event.key === "ArrowDown") {
307
+ event.preventDefault()
308
+ setMenuOpen(true)
309
+ setActiveIndex((currentIndex) =>
310
+ getNextEnabledIndex(filteredOptions, currentIndex, 1)
311
+ )
312
+ return
313
+ }
314
+
315
+ if (event.key === "ArrowUp") {
316
+ event.preventDefault()
317
+ setMenuOpen(true)
318
+ setActiveIndex((currentIndex) =>
319
+ getNextEnabledIndex(filteredOptions, currentIndex, -1)
320
+ )
321
+ return
322
+ }
323
+
324
+ if (event.key === "Home" && menuOpen) {
325
+ event.preventDefault()
326
+ setActiveIndex(getFirstEnabledIndex(filteredOptions))
327
+ return
328
+ }
329
+
330
+ if (event.key === "End" && menuOpen) {
331
+ event.preventDefault()
332
+ setActiveIndex(getLastEnabledIndex(filteredOptions))
333
+ return
334
+ }
335
+
336
+ if (event.key === "Enter" && menuOpen && activeOption) {
337
+ event.preventDefault()
338
+ selectOption(activeOption)
339
+ return
340
+ }
341
+
342
+ if (event.key === "Escape") {
343
+ setMenuOpen(false)
344
+ return
345
+ }
346
+
347
+ if (
348
+ event.key === "Backspace" &&
349
+ isMultiple &&
350
+ query.length === 0 &&
351
+ selectedValues.length > 0
352
+ ) {
353
+ removeSelectedValue(selectedValues[selectedValues.length - 1])
354
+ }
355
+ }
356
+
357
+ return (
358
+ <SelectRoot
359
+ data-selection-mode={selectionMode}
360
+ data-slot="autocomplete-root"
361
+ data-state={visualState}
362
+ data-variant={variant}
363
+ dir={dir}
364
+ ref={setRootRef}
365
+ className={cn(
366
+ "[--bh-select-width:var(--bh-input-width)] [--bh-select-menu-width:var(--bh-select-width)]",
367
+ className
368
+ )}
369
+ {...props}
370
+ onBlur={handleRootBlur}
371
+ >
372
+ {shouldRenderLabel ? (
373
+ <SelectLabel
374
+ hasInformationIcon={hasInformationIcon}
375
+ htmlFor={resolvedInputId}
376
+ isOptional={isOptional}
377
+ isRequired={isRequired}
378
+ optionalText={optionalText}
379
+ >
380
+ {label}
381
+ </SelectLabel>
382
+ ) : null}
383
+
384
+ {isMultiple && selectedOptions.length > 0 ? (
385
+ <div
386
+ data-slot="autocomplete-tags"
387
+ className={cn(
388
+ "flex min-w-0 flex-wrap gap-[var(--bh-space-xs-4)]",
389
+ tagListClassName
390
+ )}
391
+ >
392
+ {selectedOptions.map((option) => (
393
+ <Tag
394
+ closeLabel={formatRemoveLabel(option)}
395
+ disabled={isDisabled}
396
+ key={option.value}
397
+ showCloseButton
398
+ size={resolvedTagSize}
399
+ onClose={() => removeSelectedValue(option.value)}
400
+ >
401
+ {option.label}
402
+ </Tag>
403
+ ))}
404
+ </div>
405
+ ) : null}
406
+
407
+ <div data-slot="autocomplete-control" className="relative w-full">
408
+ <Input
409
+ aria-activedescendant={activeOptionId}
410
+ aria-autocomplete="list"
411
+ aria-controls={menuOpen ? listboxId : undefined}
412
+ aria-describedby={shouldRenderHelper ? `${resolvedInputId}-helper` : undefined}
413
+ aria-expanded={menuOpen}
414
+ aria-haspopup="listbox"
415
+ aria-invalid={isError || undefined}
416
+ disabled={isDisabled}
417
+ hasHelperText={false}
418
+ hasInformationIcon={false}
419
+ hasLabel={false}
420
+ id={resolvedInputId}
421
+ kind="default"
422
+ leadingIcon={
423
+ <SearchIcon
424
+ aria-hidden="true"
425
+ className="size-[var(--bh-input-icon-size)]"
426
+ focusable="false"
427
+ strokeWidth={2.25}
428
+ />
429
+ }
430
+ placeholder={placeholder}
431
+ role="combobox"
432
+ size={size}
433
+ state={visualState}
434
+ value={query}
435
+ variant={variant}
436
+ className={cn("[--bh-input-width:100%]", inputClassName)}
437
+ {...inputProps}
438
+ onChange={handleInputChange}
439
+ onClick={handleInputClick}
440
+ onFocus={handleInputFocus}
441
+ onKeyDown={handleInputKeyDown}
442
+ />
443
+
444
+ {menuOpen ? (
445
+ <SelectMenu
446
+ aria-multiselectable={isMultiple || undefined}
447
+ id={listboxId}
448
+ className={cn(
449
+ "absolute start-0 top-[calc(100%+var(--bh-select-menu-offset))] z-[var(--bh-z-popover)] max-h-[var(--bh-autocomplete-menu-max-height,var(--bh-space-18xl-320))] overflow-y-auto",
450
+ menuClassName
451
+ )}
452
+ >
453
+ {filteredOptions.length > 0 ? (
454
+ filteredOptions.map((option, index) => (
455
+ <SelectMenuItem
456
+ aria-selected={selectedValues.includes(option.value)}
457
+ data-active={activeIndex === index ? "true" : undefined}
458
+ disabled={option.disabled}
459
+ id={getOptionId(listboxId, index)}
460
+ itemType={option.itemType}
461
+ key={option.value}
462
+ label={option.label}
463
+ media={option.media}
464
+ addonText={option.addonText}
465
+ selected={selectedValues.includes(option.value)}
466
+ selectionType="default"
467
+ value={option.value}
468
+ className={cn(
469
+ activeIndex === index &&
470
+ "[&>[data-slot=select-menu-item-content]]:bg-[var(--bh-select-item-hover-bg)]"
471
+ )}
472
+ onClick={() => selectOption(option)}
473
+ onMouseDown={(event) => event.preventDefault()}
474
+ onMouseEnter={() => {
475
+ if (!option.disabled) setActiveIndex(index)
476
+ }}
477
+ />
478
+ ))
479
+ ) : (
480
+ <div
481
+ data-slot="autocomplete-empty"
482
+ role="status"
483
+ className="px-[var(--bh-select-item-padding-x)] py-[var(--bh-space-md-8)] text-start text-[length:var(--bh-text-body-sm-regular-font-size)] font-[var(--bh-text-body-sm-regular-font-weight)] leading-[var(--bh-text-body-sm-regular-line-height)] tracking-[var(--bh-text-body-sm-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
484
+ >
485
+ <span dir="auto">{emptyText}</span>
486
+ </div>
487
+ )}
488
+ </SelectMenu>
489
+ ) : null}
490
+ </div>
491
+
492
+ {shouldRenderHelper ? (
493
+ <SelectHelperText
494
+ id={`${resolvedInputId}-helper`}
495
+ type={isError ? "error" : "default"}
496
+ >
497
+ {helperContent}
498
+ </SelectHelperText>
499
+ ) : null}
500
+
501
+ {name
502
+ ? selectedValues.map((selectedItem) => (
503
+ <input
504
+ key={selectedItem}
505
+ name={name}
506
+ type="hidden"
507
+ value={selectedItem}
508
+ />
509
+ ))
510
+ : null}
511
+ </SelectRoot>
512
+ )
513
+ }
514
+ )
515
+
516
+ function defaultFilterOptions(
517
+ options: AutocompleteOption[],
518
+ inputValue: string
519
+ ) {
520
+ const normalizedInput = inputValue.trim().toLocaleLowerCase()
521
+
522
+ if (!normalizedInput) return options
523
+
524
+ return options.filter((option) =>
525
+ getSearchText(option).toLocaleLowerCase().includes(normalizedInput)
526
+ )
527
+ }
528
+
529
+ function getSearchText(option: AutocompleteOption) {
530
+ return [getOptionText(option), option.value, ...(option.keywords || [])]
531
+ .filter(Boolean)
532
+ .join(" ")
533
+ }
534
+
535
+ function getOptionText(option: AutocompleteOption) {
536
+ if (
537
+ typeof option.label === "string" ||
538
+ typeof option.label === "number" ||
539
+ typeof option.label === "bigint"
540
+ ) {
541
+ return String(option.label)
542
+ }
543
+
544
+ return option.value
545
+ }
546
+
547
+ function normalizeSelectedValues(
548
+ value: AutocompleteValue,
549
+ multiple: boolean
550
+ ) {
551
+ if (Array.isArray(value)) return value
552
+ if (!value) return []
553
+
554
+ return multiple ? [value] : [value]
555
+ }
556
+
557
+ function getSelectedOptions(
558
+ options: AutocompleteOption[],
559
+ values: string[]
560
+ ) {
561
+ const selectedValues = new Set(values)
562
+ return options.filter((option) => selectedValues.has(option.value))
563
+ }
564
+
565
+ function getInitialInputValue(
566
+ defaultValue: string | string[] | undefined,
567
+ selectionMode: AutocompleteSelectionMode,
568
+ options: AutocompleteOption[]
569
+ ) {
570
+ if (selectionMode === "multiple" || Array.isArray(defaultValue) || !defaultValue) {
571
+ return ""
572
+ }
573
+
574
+ const option = options.find((item) => item.value === defaultValue)
575
+ return option ? getOptionText(option) : ""
576
+ }
577
+
578
+ function hasRenderableContent(content: React.ReactNode) {
579
+ return (
580
+ content !== undefined &&
581
+ content !== null &&
582
+ content !== false &&
583
+ content !== ""
584
+ )
585
+ }
586
+
587
+ function getEnabledIndexes(options: AutocompleteOption[]) {
588
+ return options
589
+ .map((option, index) => (option.disabled ? -1 : index))
590
+ .filter((index) => index >= 0)
591
+ }
592
+
593
+ function getFirstEnabledIndex(options: AutocompleteOption[]) {
594
+ return getEnabledIndexes(options)[0] ?? -1
595
+ }
596
+
597
+ function getLastEnabledIndex(options: AutocompleteOption[]) {
598
+ const enabledIndexes = getEnabledIndexes(options)
599
+ return enabledIndexes[enabledIndexes.length - 1] ?? -1
600
+ }
601
+
602
+ function getNextEnabledIndex(
603
+ options: AutocompleteOption[],
604
+ currentIndex: number,
605
+ offset: 1 | -1
606
+ ) {
607
+ const enabledIndexes = getEnabledIndexes(options)
608
+ if (enabledIndexes.length === 0) return -1
609
+
610
+ const currentPosition = enabledIndexes.indexOf(currentIndex)
611
+ if (currentPosition === -1) {
612
+ return offset > 0
613
+ ? enabledIndexes[0]
614
+ : enabledIndexes[enabledIndexes.length - 1]
615
+ }
616
+
617
+ const nextPosition =
618
+ (currentPosition + offset + enabledIndexes.length) % enabledIndexes.length
619
+ return enabledIndexes[nextPosition]
620
+ }
621
+
622
+ function getOptionId(listboxId: string, index: number) {
623
+ return `${listboxId}-option-${index}`
624
+ }
625
+
626
+ function formatRemoveLabel(option: AutocompleteOption) {
627
+ const optionText = getOptionText(option)
628
+ return optionText ? `Remove ${optionText}` : "Remove option"
629
+ }
630
+
631
+ export { Autocomplete, defaultFilterOptions }
632
+ export type {
633
+ AutocompleteFilter,
634
+ AutocompleteOption,
635
+ AutocompleteProps,
636
+ AutocompleteSelectionMode,
637
+ }