@bronzelabs/oakma-ui 0.0.1

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 (121) hide show
  1. package/.prettierrc.cjs +25 -0
  2. package/.storybook/components/ActionButton.tsx +44 -0
  3. package/.storybook/components/DummyIcons.tsx +47 -0
  4. package/.storybook/components/index.ts +2 -0
  5. package/.storybook/docs/blocks/ImportStatement.tsx +52 -0
  6. package/.storybook/docs/blocks/index.ts +1 -0
  7. package/.storybook/docs/page.tsx +41 -0
  8. package/.storybook/main.ts +21 -0
  9. package/.storybook/postcss.config.cjs +8 -0
  10. package/.storybook/preview-body.html +20 -0
  11. package/.storybook/preview-head.html +6 -0
  12. package/.storybook/preview.tsx +30 -0
  13. package/.storybook/tailwind.css +6 -0
  14. package/.storybook/utils/index.ts +2 -0
  15. package/.storybook/utils/renderAsReact.tsx +30 -0
  16. package/.storybook/utils/renderDocsWithProps.tsx +22 -0
  17. package/@types/markdown.d.ts +4 -0
  18. package/README.md +3 -0
  19. package/eslint.config.js +91 -0
  20. package/package.json +63 -0
  21. package/postcss.config.cjs +8 -0
  22. package/scripts/release.sh +76 -0
  23. package/src/components/Button/Button.stories.tsx +314 -0
  24. package/src/components/Button/Button.tsx +132 -0
  25. package/src/components/Button/index.ts +2 -0
  26. package/src/components/Button/types.ts +19 -0
  27. package/src/components/Checkbox/Checkbox.stories.tsx +152 -0
  28. package/src/components/Checkbox/Checkbox.tsx +90 -0
  29. package/src/components/Checkbox/index.ts +2 -0
  30. package/src/components/Checkbox/types.ts +6 -0
  31. package/src/components/Chip/Chip.stories.tsx +146 -0
  32. package/src/components/Chip/Chip.tsx +59 -0
  33. package/src/components/Chip/index.ts +2 -0
  34. package/src/components/Chip/types.ts +6 -0
  35. package/src/components/Drawer/Drawer.docs.md +88 -0
  36. package/src/components/Drawer/Drawer.stories.tsx +239 -0
  37. package/src/components/Drawer/Drawer.tsx +194 -0
  38. package/src/components/Drawer/index.ts +3 -0
  39. package/src/components/Drawer/types.ts +3 -0
  40. package/src/components/Dropdown/AsyncDropdown.tsx +105 -0
  41. package/src/components/Dropdown/Dropdown.docs.md +33 -0
  42. package/src/components/Dropdown/Dropdown.stories.tsx +419 -0
  43. package/src/components/Dropdown/Dropdown.tsx +104 -0
  44. package/src/components/Dropdown/MultiValue.tsx +19 -0
  45. package/src/components/Dropdown/ValueContainer.tsx +114 -0
  46. package/src/components/Dropdown/index.ts +4 -0
  47. package/src/components/Dropdown/types.ts +29 -0
  48. package/src/components/Dropdown/useDropdown.tsx +257 -0
  49. package/src/components/Logo/Logo.stories.tsx +130 -0
  50. package/src/components/Logo/Logo.tsx +80 -0
  51. package/src/components/Logo/index.ts +2 -0
  52. package/src/components/Modal/Modal.docs.md +94 -0
  53. package/src/components/Modal/Modal.stories.tsx +318 -0
  54. package/src/components/Modal/Modal.tsx +217 -0
  55. package/src/components/Modal/index.ts +1 -0
  56. package/src/components/MultiSelect/AsyncMultiSelect.tsx +47 -0
  57. package/src/components/MultiSelect/MultiSelect.docs.md +37 -0
  58. package/src/components/MultiSelect/MultiSelect.stories.tsx +493 -0
  59. package/src/components/MultiSelect/MultiSelect.tsx +81 -0
  60. package/src/components/MultiSelect/index.ts +2 -0
  61. package/src/components/Notification/Notification.stories.tsx +158 -0
  62. package/src/components/Notification/Notification.tsx +110 -0
  63. package/src/components/Notification/index.ts +1 -0
  64. package/src/components/Notification/types.ts +11 -0
  65. package/src/components/Notifications/Notifications.docs.md +103 -0
  66. package/src/components/Notifications/Notifications.stories.tsx +159 -0
  67. package/src/components/Notifications/Notifications.tsx +90 -0
  68. package/src/components/Notifications/NotificationsContext.tsx +90 -0
  69. package/src/components/Notifications/index.ts +7 -0
  70. package/src/components/Select/Select.stories.tsx +234 -0
  71. package/src/components/Select/Select.tsx +129 -0
  72. package/src/components/Select/index.ts +2 -0
  73. package/src/components/Select/types.ts +1 -0
  74. package/src/components/Spinner/Spinner.stories.tsx +55 -0
  75. package/src/components/Spinner/Spinner.tsx +48 -0
  76. package/src/components/Spinner/index.ts +2 -0
  77. package/src/components/Spinner/types.ts +8 -0
  78. package/src/components/TextArea/TextArea.stories.tsx +243 -0
  79. package/src/components/TextArea/TextArea.tsx +133 -0
  80. package/src/components/TextArea/index.ts +2 -0
  81. package/src/components/TextArea/types.ts +4 -0
  82. package/src/components/TextField/Container.tsx +68 -0
  83. package/src/components/TextField/ErrorMessage.tsx +37 -0
  84. package/src/components/TextField/Icon.tsx +77 -0
  85. package/src/components/TextField/Label.tsx +56 -0
  86. package/src/components/TextField/NotchBorder.tsx +67 -0
  87. package/src/components/TextField/index.ts +14 -0
  88. package/src/components/TextField/types.ts +15 -0
  89. package/src/components/TextField/useInputKeyboardFocus.tsx +63 -0
  90. package/src/components/TextInput/TextInput.stories.tsx +384 -0
  91. package/src/components/TextInput/TextInput.tsx +255 -0
  92. package/src/components/TextInput/index.ts +2 -0
  93. package/src/components/TextInput/types.ts +4 -0
  94. package/src/components/Toggle/Toggle.stories.tsx +142 -0
  95. package/src/components/Toggle/Toggle.tsx +69 -0
  96. package/src/components/Toggle/index.ts +1 -0
  97. package/src/hooks/index.ts +6 -0
  98. package/src/hooks/useCombinedRefs.ts +37 -0
  99. package/src/hooks/useEventListener.ts +87 -0
  100. package/src/hooks/useFocusTrap/createAriaHider.ts +62 -0
  101. package/src/hooks/useFocusTrap/index.ts +1 -0
  102. package/src/hooks/useFocusTrap/scopeTab.ts +46 -0
  103. package/src/hooks/useFocusTrap/tabbable.ts +107 -0
  104. package/src/hooks/useFocusTrap/useFocusTrap.ts +97 -0
  105. package/src/hooks/useIsomorphicLayoutEffect.ts +14 -0
  106. package/src/hooks/useLockBodyScroll.ts +24 -0
  107. package/src/hooks/useOnClickOutside.ts +53 -0
  108. package/src/index.ts +22 -0
  109. package/src/tailwind.css +4 -0
  110. package/src/types/helpers.ts +11 -0
  111. package/src/types/polymorphic.ts +39 -0
  112. package/src/utils/animation/variants.ts +21 -0
  113. package/src/utils/array/index.ts +1 -0
  114. package/src/utils/array/uniqBy.ts +12 -0
  115. package/src/utils/common/index.ts +1 -0
  116. package/src/utils/common/isFunction.ts +17 -0
  117. package/src/utils/react/extractDisplayName.ts +15 -0
  118. package/src/utils/react/index.ts +1 -0
  119. package/tsconfig.json +16 -0
  120. package/tsconfig.production.json +19 -0
  121. package/tsup.config.ts +16 -0
@@ -0,0 +1,493 @@
1
+ import React from "react"
2
+
3
+ // Components
4
+ import MultiSelect from "./MultiSelect"
5
+ import AsyncMultiSelect from "./AsyncMultiSelect"
6
+ import { DummyPhoneIcon, StorybookActionButton } from "../../../.storybook/components"
7
+
8
+ // Types
9
+ import type { StoryFn, Meta } from "@storybook/react-webpack5"
10
+
11
+ // Utils
12
+ import extraDocs from "./MultiSelect.docs.md"
13
+ import { renderDocsWithProps } from "../../../.storybook/utils"
14
+ import { TEXT_FIELD_SIZES } from "../TextField"
15
+
16
+ // Data
17
+ const mockOptions = {
18
+ default: [
19
+ {
20
+ label: "Item 1",
21
+ value: "item-1",
22
+ color: "blue",
23
+ },
24
+ {
25
+ label: "Item 2",
26
+ value: "item-2",
27
+ isDisabled: true,
28
+ },
29
+ {
30
+ label: "Item 3",
31
+ value: "item-3",
32
+ },
33
+ {
34
+ label: "Item 4",
35
+ value: "item-4",
36
+ },
37
+ ],
38
+ long: [
39
+ {
40
+ label: "This is a very long first item to have in a multiselect dropdown",
41
+ value: "item-1",
42
+ color: "blue",
43
+ },
44
+ {
45
+ label: "Long Item 2",
46
+ value: "item-2",
47
+ },
48
+ {
49
+ label:
50
+ "The third long multiselect item that should wrap onto multiple lines if the width of the dropdown is too narrow",
51
+ value: "item-3",
52
+ },
53
+ ],
54
+ subTitle: [
55
+ {
56
+ label: "Item 1",
57
+ value: "item-1",
58
+ subTitle: "Sub text for item 1",
59
+ },
60
+ {
61
+ label: "Item 2",
62
+ value: "item-2",
63
+ subTitle: "Sub text for item 2",
64
+ isDisabled: true,
65
+ },
66
+ {
67
+ label: "Item 3",
68
+ value: "item-3",
69
+ subTitle: "Sub text for item 3",
70
+ },
71
+ ],
72
+ icons: [
73
+ {
74
+ label: "Item 1",
75
+ value: "item-1",
76
+ icon: DummyPhoneIcon,
77
+ },
78
+ {
79
+ label: "Item 2",
80
+ value: "item-2",
81
+ icon: DummyPhoneIcon,
82
+ },
83
+ {
84
+ label: "Item 3",
85
+ value: "item-3",
86
+ icon: DummyPhoneIcon,
87
+ },
88
+ ],
89
+ iconsSubTitle: [
90
+ {
91
+ label: "Item 1",
92
+ value: "item-1",
93
+ icon: DummyPhoneIcon,
94
+ subTitle: "Sub text for item 1",
95
+ },
96
+ {
97
+ label: "Item 2",
98
+ value: "item-2",
99
+ icon: DummyPhoneIcon,
100
+ subTitle: "Sub text for item 2",
101
+ isDisabled: true,
102
+ },
103
+ {
104
+ label: "Item 3",
105
+ value: "item-3",
106
+ icon: DummyPhoneIcon,
107
+ subTitle: "Sub text for item 3",
108
+ },
109
+ ],
110
+ extra: [
111
+ {
112
+ label: "Item 1",
113
+ value: "item-1",
114
+ color: "blue",
115
+ },
116
+ {
117
+ label: "Item 2",
118
+ value: "item-2",
119
+ },
120
+ {
121
+ label: "Item 3",
122
+ value: "item-3",
123
+ },
124
+ {
125
+ label: "Item 4",
126
+ value: "item-4",
127
+ },
128
+ {
129
+ label: "Item 5",
130
+ value: "item-5",
131
+ },
132
+ {
133
+ label: "Item 6",
134
+ value: "item-6",
135
+ },
136
+ {
137
+ label: "Item 7",
138
+ value: "item-7",
139
+ },
140
+ {
141
+ label: "Item 8",
142
+ value: "item-8",
143
+ },
144
+ ],
145
+ users: [
146
+ { label: "John Doe", value: "john-doe" },
147
+ { label: "Jane Smith", value: "jane-smith" },
148
+ { label: "Bob Johnson", value: "bob-johnson" },
149
+ ],
150
+ }
151
+
152
+ /*
153
+
154
+
155
+
156
+
157
+ */
158
+
159
+ const meta: Meta<typeof MultiSelect> = {
160
+ title: "Components/MultiSelect",
161
+ component: MultiSelect,
162
+ parameters: {
163
+ docs: {
164
+ description: {
165
+ component:
166
+ "MultiSelect is a fancy select/combobox built on top of [react-select](https://react-select.com), that can be used to select multiple values from a list.\n\nThe main (and Oakma specific) props can be seen below, however, a full list of accepted props can be found in the react-select [docs](https://react-select.com/props). For details on how to use the component with `react-hook-form` see [here](#usage-with-react-hook-form).",
167
+ },
168
+ page: renderDocsWithProps({ extraDocs }),
169
+ },
170
+ controls: {
171
+ exclude: ["className", "ref", "children", "name"],
172
+ },
173
+ },
174
+ argTypes: {
175
+ options: {
176
+ type: {
177
+ name: "other",
178
+ value: "(Option | Group)[]",
179
+ },
180
+ description: "Array of options that populate the MultiSelect menu.",
181
+ table: {
182
+ type: {
183
+ summary: "(Option | Group)[]",
184
+ },
185
+ defaultValue: {
186
+ summary: "undefined",
187
+ },
188
+ },
189
+ },
190
+ label: {
191
+ type: "string",
192
+ description: "Visible label rendered above the MultiSelect.",
193
+ table: {
194
+ type: { summary: "string" },
195
+ defaultValue: { summary: "undefined" },
196
+ },
197
+ },
198
+ size: {
199
+ type: "string",
200
+ description: "Determines the size of the MultiSelect field.",
201
+ table: {
202
+ type: { summary: TEXT_FIELD_SIZES.map(size => `"${size}"`).join(" | ") },
203
+ defaultValue: { summary: '"md"' },
204
+ },
205
+ options: TEXT_FIELD_SIZES,
206
+ control: { type: "select" },
207
+ },
208
+ showOptional: {
209
+ type: "boolean",
210
+ description:
211
+ 'When `true`, and if the `required` prop is not set, an "Optional" tag will be displayed next to the label.',
212
+ table: {
213
+ type: { summary: "boolean" },
214
+ defaultValue: { summary: "true" },
215
+ },
216
+ },
217
+ leadingIcon: {
218
+ type: {
219
+ name: "other",
220
+ value: "{ icon: ReactNode }",
221
+ },
222
+ description:
223
+ "An object with an `icon` property containing a React node to render inside the field, aligned to the left.",
224
+ table: {
225
+ type: { summary: "{ icon: ReactNode }" },
226
+ defaultValue: { summary: "undefined" },
227
+ },
228
+ },
229
+ isLoading: {
230
+ type: "boolean",
231
+ description: "Is the MultiSelect in a state of loading (async).",
232
+ table: {
233
+ type: { summary: "boolean" },
234
+ defaultValue: { summary: "false" },
235
+ },
236
+ },
237
+ isDisabled: {
238
+ type: "boolean",
239
+ description: "Is the MultiSelect disabled.",
240
+ table: {
241
+ type: { summary: "boolean" },
242
+ defaultValue: { summary: "false" },
243
+ },
244
+ },
245
+ isClearable: {
246
+ type: "boolean",
247
+ description: "Is the MultiSelect value clearable.",
248
+ table: {
249
+ type: { summary: "boolean" },
250
+ defaultValue: { summary: "false" },
251
+ },
252
+ },
253
+ required: {
254
+ type: "boolean",
255
+ description: "Whether the Dropdown is required.",
256
+ table: {
257
+ type: { summary: "boolean" },
258
+ defaultValue: { summary: "false" },
259
+ },
260
+ },
261
+ tabSelectsValue: {
262
+ type: "boolean",
263
+ description: "Select the currently focused option when the user presses tab",
264
+ table: {
265
+ type: { summary: "boolean" },
266
+ defaultValue: { summary: "false" },
267
+ },
268
+ },
269
+ closeMenuOnSelect: {
270
+ type: "boolean",
271
+ description: "Close the dropdown menu when an option is selected.",
272
+ table: {
273
+ type: { summary: "boolean" },
274
+ defaultValue: { summary: "false" },
275
+ },
276
+ },
277
+ hideSelectedOptions: {
278
+ type: "boolean",
279
+ description: "Hide options that have already been selected from the dropdown menu.",
280
+ table: {
281
+ type: { summary: "boolean" },
282
+ defaultValue: { summary: "false" },
283
+ },
284
+ },
285
+ },
286
+ args: {
287
+ name: "multi-select",
288
+ label: "Label",
289
+ size: "md",
290
+ showOptional: true,
291
+ options: mockOptions.default,
292
+ required: false,
293
+ tabSelectsValue: false,
294
+ closeMenuOnSelect: false,
295
+ hideSelectedOptions: false,
296
+ isLoading: false,
297
+ isDisabled: false,
298
+ isClearable: false,
299
+ },
300
+ }
301
+
302
+ const mockLoadOptions = (inputValue: string) =>
303
+ new Promise<(typeof mockOptions.users)[number][]>(resolve => {
304
+ setTimeout(() => {
305
+ resolve(
306
+ mockOptions.users.filter(opt => opt.label.toLowerCase().includes(inputValue.toLowerCase())),
307
+ )
308
+ }, 600)
309
+ })
310
+
311
+ // Templates
312
+ const Template: StoryFn<typeof MultiSelect> = args => <MultiSelect {...args} />
313
+
314
+ const FormTemplate: StoryFn<typeof MultiSelect> = args => {
315
+ return (
316
+ <form
317
+ className="flex flex-col items-stretch gap-3"
318
+ onSubmit={e => {
319
+ e.preventDefault()
320
+ const formData = new FormData(e.currentTarget)
321
+ alert(`Selected values: ${JSON.stringify(formData.getAll("multi-select"))}`)
322
+ }}
323
+ >
324
+ <MultiSelect {...args} />
325
+ <StorybookActionButton type="submit">Submit</StorybookActionButton>
326
+ </form>
327
+ )
328
+ }
329
+
330
+ const ShowAllSelectedTemplate: StoryFn<typeof MultiSelect> = args => {
331
+ return (
332
+ <>
333
+ <MultiSelect {...args} />
334
+ <MultiSelect {...args} showSelectedCount={false} />
335
+ </>
336
+ )
337
+ }
338
+
339
+ const AsyncTemplate: StoryFn<typeof MultiSelect> = args => (
340
+ <AsyncMultiSelect
341
+ id="async-multi-select"
342
+ label={args.label}
343
+ showOptional={args.showOptional}
344
+ size={args.size}
345
+ loadOptions={mockLoadOptions}
346
+ defaultOptions={mockOptions.users}
347
+ placeholder="Search users…"
348
+ />
349
+ )
350
+
351
+ // Stories
352
+ const Default = Template.bind({})
353
+ const WithSubtitles = Template.bind({})
354
+ const WithIcons = Template.bind({})
355
+ const WithIconsAndSubtitles = Template.bind({})
356
+ const WithLongOptions = Template.bind({})
357
+ const Required = FormTemplate.bind({})
358
+ const ShowAllSelected = ShowAllSelectedTemplate.bind({})
359
+ const Async = AsyncTemplate.bind({})
360
+
361
+ // Args
362
+ WithSubtitles.args = {
363
+ options: mockOptions.subTitle,
364
+ }
365
+
366
+ WithIcons.args = {
367
+ options: mockOptions.icons,
368
+ }
369
+
370
+ WithIconsAndSubtitles.args = {
371
+ options: mockOptions.iconsSubTitle,
372
+ }
373
+
374
+ WithLongOptions.args = {
375
+ options: mockOptions.long,
376
+ }
377
+
378
+ Required.args = {
379
+ required: true,
380
+ }
381
+
382
+ ShowAllSelected.args = {
383
+ options: mockOptions.extra,
384
+ }
385
+
386
+ // Decorators
387
+ Default.decorators = [
388
+ Story => (
389
+ <div className="flex flex-col" style={{ width: 450, height: 300 }}>
390
+ <Story />
391
+ </div>
392
+ ),
393
+ ]
394
+
395
+ WithSubtitles.decorators = Default.decorators
396
+
397
+ WithIcons.decorators = Default.decorators
398
+
399
+ WithIconsAndSubtitles.decorators = Default.decorators
400
+
401
+ WithLongOptions.decorators = Default.decorators
402
+
403
+ Required.decorators = Default.decorators
404
+
405
+ ShowAllSelected.decorators = [
406
+ Story => (
407
+ <div className="flex flex-col gap-6" style={{ width: 450, height: 450 }}>
408
+ <Story />
409
+ </div>
410
+ ),
411
+ ]
412
+
413
+ Async.decorators = Default.decorators
414
+
415
+ Default.parameters = {
416
+ docs: {
417
+ description: {
418
+ story: "A basic MultiSelect with a flat list of options.",
419
+ },
420
+ },
421
+ }
422
+
423
+ WithSubtitles.parameters = {
424
+ docs: {
425
+ description: {
426
+ story:
427
+ "Options can include a `subTitle` string to provide additional context below the label.",
428
+ },
429
+ },
430
+ }
431
+
432
+ WithIcons.parameters = {
433
+ docs: {
434
+ description: {
435
+ story: "Options can include an `icon` component to render a visual alongside each label.",
436
+ },
437
+ },
438
+ }
439
+
440
+ WithIconsAndSubtitles.parameters = {
441
+ docs: {
442
+ description: {
443
+ story: "Icons and subtitles can be combined to provide richer option content.",
444
+ },
445
+ },
446
+ }
447
+
448
+ WithLongOptions.parameters = {
449
+ docs: {
450
+ description: {
451
+ story: "Long option labels wrap naturally within the menu without truncation.",
452
+ },
453
+ },
454
+ }
455
+
456
+ Required.parameters = {
457
+ docs: {
458
+ description: {
459
+ story:
460
+ 'Setting `required` hides the "Optional" tag and marks the field as required. The form will not submit until at least one value is selected.',
461
+ },
462
+ },
463
+ }
464
+
465
+ ShowAllSelected.parameters = {
466
+ docs: {
467
+ description: {
468
+ story:
469
+ "By default, selected options are summarised as a count badge once there are too many to display inline. Set `showSelectedCount={false}` to always show every selected option as an individual tag.",
470
+ },
471
+ },
472
+ }
473
+
474
+ Async.parameters = {
475
+ docs: {
476
+ description: {
477
+ story:
478
+ "Use `AsyncMultiSelect` when options are fetched remotely. Pass a `loadOptions` function that takes the current input string and returns a promise of options. `defaultOptions` populates the list before the user types.",
479
+ },
480
+ },
481
+ }
482
+
483
+ export {
484
+ Default,
485
+ WithSubtitles,
486
+ WithIcons,
487
+ WithIconsAndSubtitles,
488
+ WithLongOptions,
489
+ Required,
490
+ ShowAllSelected,
491
+ Async,
492
+ }
493
+ export default meta
@@ -0,0 +1,81 @@
1
+ "use client"
2
+
3
+ import React from "react"
4
+
5
+ // Components
6
+ import Dropdown, { type DropdownProps } from "../Dropdown"
7
+ import Checkbox from "../Checkbox"
8
+
9
+ // Utils
10
+ import { clsx } from "clsx"
11
+ import { components, type OptionProps } from "react-select"
12
+
13
+ // Props
14
+ interface MultiSelectProps extends Omit<DropdownProps, "isMulti"> {
15
+ cy?: string
16
+ }
17
+
18
+ const dataHasIcon = (check: any): check is { icon: React.ElementType } => {
19
+ return !!check?.icon && check?.icon !== null
20
+ }
21
+
22
+ const MultiSelectOption = (props: OptionProps<any>) => {
23
+ const Icon = dataHasIcon(props?.data) ? props.data.icon : undefined
24
+
25
+ return (
26
+ <components.Option
27
+ {...props}
28
+ className={clsx(props?.className, "flex! w-full items-center justify-between gap-3")}
29
+ >
30
+ {Icon && <Icon className="size-5" />}
31
+ <div className="flex grow flex-col">
32
+ <span>{props?.data?.label}</span>
33
+ {props?.data?.subTitle && <span>{props.data.subTitle}</span>}
34
+ </div>
35
+
36
+ <Checkbox checked={props?.isSelected} readOnly />
37
+ </components.Option>
38
+ )
39
+ }
40
+
41
+ const multiSelectComponents = { Option: MultiSelectOption }
42
+
43
+ /*
44
+
45
+
46
+
47
+
48
+
49
+ */
50
+
51
+ const MultiSelect: React.FC<MultiSelectProps> = ({
52
+ tabSelectsValue = false,
53
+ closeMenuOnSelect = false,
54
+ hideSelectedOptions = false,
55
+ isClearable = false,
56
+ ...rest
57
+ }) => {
58
+ // Functions
59
+ // const handleChange: typeof onChange = (newValue, data): void => {
60
+ // onChange?.(newValue, data)
61
+ // setTimeout(() => combinedRef.current?.focusInput(), 0)
62
+ // }
63
+
64
+ return (
65
+ <Dropdown
66
+ closeMenuOnSelect={closeMenuOnSelect}
67
+ hideSelectedOptions={hideSelectedOptions}
68
+ tabSelectsValue={tabSelectsValue}
69
+ // @ts-expect-error - The `isMulti` prop is not supported when used by external consumers, however it is required internally to render the correct components and behavior.
70
+ isMulti
71
+ isClearable={isClearable}
72
+ components={multiSelectComponents}
73
+ {...rest}
74
+ />
75
+ )
76
+ }
77
+
78
+ MultiSelect.displayName = "MultiSelect"
79
+ export default MultiSelect
80
+ export { MultiSelectOption }
81
+ export type { MultiSelectProps }
@@ -0,0 +1,2 @@
1
+ export { default, type MultiSelectProps } from "./MultiSelect"
2
+ export { default as AsyncMultiSelect, type AsyncMultiSelectProps } from "./AsyncMultiSelect"