@mihcm/ui 0.14.1 → 0.15.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.
- package/dist/CheckboxGrid.native.d.ts.map +1 -1
- package/dist/CheckboxGrid.native.js +2 -1
- package/dist/CheckboxGrid.native.js.map +1 -1
- package/dist/Combobox.native.d.ts.map +1 -1
- package/dist/Combobox.native.js +2 -1
- package/dist/Combobox.native.js.map +1 -1
- package/dist/DataTable/column-filter.d.ts +8 -0
- package/dist/DataTable/column-filter.d.ts.map +1 -0
- package/dist/DataTable/column-filter.js +67 -0
- package/dist/DataTable/column-filter.js.map +1 -0
- package/dist/DataTable/column-header.d.ts +16 -0
- package/dist/DataTable/column-header.d.ts.map +1 -0
- package/dist/DataTable/column-header.js +11 -0
- package/dist/DataTable/column-header.js.map +1 -0
- package/dist/DataTable/column-visibility.d.ts +7 -0
- package/dist/DataTable/column-visibility.d.ts.map +1 -0
- package/dist/DataTable/column-visibility.js +35 -0
- package/dist/DataTable/column-visibility.js.map +1 -0
- package/dist/DataTable/index.d.ts +5 -0
- package/dist/DataTable/index.d.ts.map +1 -0
- package/dist/DataTable/index.js +5 -0
- package/dist/DataTable/index.js.map +1 -0
- package/dist/DataTable/pinning.d.ts +13 -0
- package/dist/DataTable/pinning.d.ts.map +1 -0
- package/dist/DataTable/pinning.js +29 -0
- package/dist/DataTable/pinning.js.map +1 -0
- package/dist/DataTable.d.ts +3 -7
- package/dist/DataTable.d.ts.map +1 -1
- package/dist/DataTable.js +7 -126
- package/dist/DataTable.js.map +1 -1
- package/dist/Dialog.native.d.ts +3 -1
- package/dist/Dialog.native.d.ts.map +1 -1
- package/dist/Dialog.native.js +2 -2
- package/dist/Dialog.native.js.map +1 -1
- package/dist/Form/building-blocks.d.ts +26 -0
- package/dist/Form/building-blocks.d.ts.map +1 -0
- package/dist/Form/building-blocks.js +29 -0
- package/dist/Form/building-blocks.js.map +1 -0
- package/dist/Form/fields-choice.d.ts +72 -0
- package/dist/Form/fields-choice.d.ts.map +1 -0
- package/dist/Form/fields-choice.js +69 -0
- package/dist/Form/fields-choice.js.map +1 -0
- package/dist/Form/fields-complex.d.ts +28 -0
- package/dist/Form/fields-complex.d.ts.map +1 -0
- package/dist/Form/fields-complex.js +38 -0
- package/dist/Form/fields-complex.js.map +1 -0
- package/dist/Form/fields-date.d.ts +46 -0
- package/dist/Form/fields-date.d.ts.map +1 -0
- package/dist/Form/fields-date.js +41 -0
- package/dist/Form/fields-date.js.map +1 -0
- package/dist/Form/fields-text.d.ts +47 -0
- package/dist/Form/fields-text.d.ts.map +1 -0
- package/dist/Form/fields-text.js +46 -0
- package/dist/Form/fields-text.js.map +1 -0
- package/dist/Form/fields-toggle.d.ts +24 -0
- package/dist/Form/fields-toggle.d.ts.map +1 -0
- package/dist/Form/fields-toggle.js +32 -0
- package/dist/Form/fields-toggle.js.map +1 -0
- package/dist/Form/helpers.d.ts +66 -0
- package/dist/Form/helpers.d.ts.map +1 -0
- package/dist/Form/helpers.js +44 -0
- package/dist/Form/helpers.js.map +1 -0
- package/dist/Form/types.d.ts +25 -0
- package/dist/Form/types.d.ts.map +1 -0
- package/dist/Form/types.js +8 -0
- package/dist/Form/types.js.map +1 -0
- package/dist/Form.d.ts +24 -298
- package/dist/Form.d.ts.map +1 -1
- package/dist/Form.js +30 -246
- package/dist/Form.js.map +1 -1
- package/dist/IconSidebar.d.ts +6 -46
- package/dist/IconSidebar.d.ts.map +1 -1
- package/dist/IconSidebar.js +6 -116
- package/dist/IconSidebar.js.map +1 -1
- package/dist/MainSidebar/back-button.d.ts +14 -0
- package/dist/MainSidebar/back-button.d.ts.map +1 -0
- package/dist/MainSidebar/back-button.js +14 -0
- package/dist/MainSidebar/back-button.js.map +1 -0
- package/dist/MainSidebar/breadcrumb.d.ts +10 -0
- package/dist/MainSidebar/breadcrumb.d.ts.map +1 -0
- package/dist/MainSidebar/breadcrumb.js +24 -0
- package/dist/MainSidebar/breadcrumb.js.map +1 -0
- package/dist/MainSidebar/columns.d.ts +3 -0
- package/dist/MainSidebar/columns.d.ts.map +1 -0
- package/dist/MainSidebar/columns.js +198 -0
- package/dist/MainSidebar/columns.js.map +1 -0
- package/dist/MainSidebar/command.d.ts +3 -0
- package/dist/MainSidebar/command.d.ts.map +1 -0
- package/dist/MainSidebar/command.js +193 -0
- package/dist/MainSidebar/command.js.map +1 -0
- package/dist/MainSidebar/drilldown.d.ts +3 -0
- package/dist/MainSidebar/drilldown.d.ts.map +1 -0
- package/dist/MainSidebar/drilldown.js +154 -0
- package/dist/MainSidebar/drilldown.js.map +1 -0
- package/dist/MainSidebar/expanded.d.ts +7 -0
- package/dist/MainSidebar/expanded.d.ts.map +1 -0
- package/dist/MainSidebar/expanded.js +102 -0
- package/dist/MainSidebar/expanded.js.map +1 -0
- package/dist/MainSidebar/floating.d.ts +3 -0
- package/dist/MainSidebar/floating.d.ts.map +1 -0
- package/dist/MainSidebar/floating.js +116 -0
- package/dist/MainSidebar/floating.js.map +1 -0
- package/dist/MainSidebar/helpers.d.ts +50 -0
- package/dist/MainSidebar/helpers.d.ts.map +1 -0
- package/dist/MainSidebar/helpers.js +148 -0
- package/dist/MainSidebar/helpers.js.map +1 -0
- package/dist/MainSidebar/hover.d.ts +3 -0
- package/dist/MainSidebar/hover.d.ts.map +1 -0
- package/dist/MainSidebar/hover.js +177 -0
- package/dist/MainSidebar/hover.js.map +1 -0
- package/dist/MainSidebar/index.d.ts +6 -0
- package/dist/MainSidebar/index.d.ts.map +1 -0
- package/dist/MainSidebar/index.js +108 -0
- package/dist/MainSidebar/index.js.map +1 -0
- package/dist/MainSidebar/mobile.d.ts +29 -0
- package/dist/MainSidebar/mobile.d.ts.map +1 -0
- package/dist/MainSidebar/mobile.js +38 -0
- package/dist/MainSidebar/mobile.js.map +1 -0
- package/dist/MainSidebar/motion.d.ts +23 -0
- package/dist/MainSidebar/motion.d.ts.map +1 -0
- package/dist/MainSidebar/motion.js +40 -0
- package/dist/MainSidebar/motion.js.map +1 -0
- package/dist/MainSidebar/rail.d.ts +24 -0
- package/dist/MainSidebar/rail.d.ts.map +1 -0
- package/dist/MainSidebar/rail.js +29 -0
- package/dist/MainSidebar/rail.js.map +1 -0
- package/dist/MainSidebar/search.d.ts +19 -0
- package/dist/MainSidebar/search.d.ts.map +1 -0
- package/dist/MainSidebar/search.js +33 -0
- package/dist/MainSidebar/search.js.map +1 -0
- package/dist/MainSidebar/types.d.ts +161 -0
- package/dist/MainSidebar/types.d.ts.map +1 -0
- package/dist/MainSidebar/types.js +2 -0
- package/dist/MainSidebar/types.js.map +1 -0
- package/dist/MainSidebar.d.ts +6 -1
- package/dist/MainSidebar.d.ts.map +1 -1
- package/dist/MainSidebar.js +6 -1
- package/dist/MainSidebar.js.map +1 -1
- package/dist/NavigationMenu.js +1 -1
- package/dist/NavigationMenu.js.map +1 -1
- package/dist/RichTextEditor/theme.d.ts +44 -0
- package/dist/RichTextEditor/theme.d.ts.map +1 -0
- package/dist/RichTextEditor/theme.js +41 -0
- package/dist/RichTextEditor/theme.js.map +1 -0
- package/dist/RichTextEditor/toolbar-icons.d.ts +21 -0
- package/dist/RichTextEditor/toolbar-icons.d.ts.map +1 -0
- package/dist/RichTextEditor/toolbar-icons.js +21 -0
- package/dist/RichTextEditor/toolbar-icons.js.map +1 -0
- package/dist/RichTextEditor/toolbar.d.ts +5 -0
- package/dist/RichTextEditor/toolbar.d.ts.map +1 -0
- package/dist/RichTextEditor/toolbar.js +116 -0
- package/dist/RichTextEditor/toolbar.js.map +1 -0
- package/dist/RichTextEditor.d.ts +16 -9
- package/dist/RichTextEditor.d.ts.map +1 -1
- package/dist/RichTextEditor.js +18 -164
- package/dist/RichTextEditor.js.map +1 -1
- package/dist/Select/content.d.ts +9 -0
- package/dist/Select/content.d.ts.map +1 -0
- package/dist/Select/content.js +80 -0
- package/dist/Select/content.js.map +1 -0
- package/dist/Select/context.d.ts +27 -0
- package/dist/Select/context.d.ts.map +1 -0
- package/dist/Select/context.js +35 -0
- package/dist/Select/context.js.map +1 -0
- package/dist/Select/item.d.ts +13 -0
- package/dist/Select/item.d.ts.map +1 -0
- package/dist/Select/item.js +39 -0
- package/dist/Select/item.js.map +1 -0
- package/dist/Select/parts.d.ts +14 -0
- package/dist/Select/parts.d.ts.map +1 -0
- package/dist/Select/parts.js +17 -0
- package/dist/Select/parts.js.map +1 -0
- package/dist/Select/react-select.d.ts +25 -0
- package/dist/Select/react-select.d.ts.map +1 -0
- package/dist/Select/react-select.js +66 -0
- package/dist/Select/react-select.js.map +1 -0
- package/dist/Select/root.d.ts +15 -0
- package/dist/Select/root.d.ts.map +1 -0
- package/dist/Select/root.js +41 -0
- package/dist/Select/root.js.map +1 -0
- package/dist/Select/trigger.d.ts +15 -0
- package/dist/Select/trigger.d.ts.map +1 -0
- package/dist/Select/trigger.js +61 -0
- package/dist/Select/trigger.js.map +1 -0
- package/dist/Select.d.ts +14 -62
- package/dist/Select.d.ts.map +1 -1
- package/dist/Select.js +14 -293
- package/dist/Select.js.map +1 -1
- package/dist/Sidebar/context.d.ts +28 -0
- package/dist/Sidebar/context.d.ts.map +1 -0
- package/dist/Sidebar/context.js +37 -0
- package/dist/Sidebar/context.js.map +1 -0
- package/dist/Sidebar/group.d.ts +13 -0
- package/dist/Sidebar/group.d.ts.map +1 -0
- package/dist/Sidebar/group.js +20 -0
- package/dist/Sidebar/group.js.map +1 -0
- package/dist/Sidebar/icons.d.ts +7 -0
- package/dist/Sidebar/icons.d.ts.map +1 -0
- package/dist/Sidebar/icons.js +12 -0
- package/dist/Sidebar/icons.js.map +1 -0
- package/dist/Sidebar/layout.d.ts +9 -0
- package/dist/Sidebar/layout.d.ts.map +1 -0
- package/dist/Sidebar/layout.js +21 -0
- package/dist/Sidebar/layout.js.map +1 -0
- package/dist/Sidebar/menu.d.ts +29 -0
- package/dist/Sidebar/menu.d.ts.map +1 -0
- package/dist/Sidebar/menu.js +55 -0
- package/dist/Sidebar/menu.js.map +1 -0
- package/dist/Sidebar/provider.d.ts +33 -0
- package/dist/Sidebar/provider.d.ts.map +1 -0
- package/dist/Sidebar/provider.js +110 -0
- package/dist/Sidebar/provider.js.map +1 -0
- package/dist/Sidebar/sidebar.d.ts +17 -0
- package/dist/Sidebar/sidebar.d.ts.map +1 -0
- package/dist/Sidebar/sidebar.js +51 -0
- package/dist/Sidebar/sidebar.js.map +1 -0
- package/dist/Sidebar/submenu.d.ts +13 -0
- package/dist/Sidebar/submenu.d.ts.map +1 -0
- package/dist/Sidebar/submenu.js +17 -0
- package/dist/Sidebar/submenu.js.map +1 -0
- package/dist/Sidebar/trigger.d.ts +9 -0
- package/dist/Sidebar/trigger.d.ts.map +1 -0
- package/dist/Sidebar/trigger.js +33 -0
- package/dist/Sidebar/trigger.js.map +1 -0
- package/dist/Sidebar.d.ts +14 -104
- package/dist/Sidebar.d.ts.map +1 -1
- package/dist/Sidebar.js +14 -300
- package/dist/Sidebar.js.map +1 -1
- package/dist/StatCard.d.ts +67 -9
- package/dist/StatCard.d.ts.map +1 -1
- package/dist/StatCard.js +111 -9
- package/dist/StatCard.js.map +1 -1
- package/dist/TransferList.native.d.ts.map +1 -1
- package/dist/TransferList.native.js +2 -1
- package/dist/TransferList.native.js.map +1 -1
- package/package.json +2 -2
- package/src/CheckboxGrid.native.tsx +2 -1
- package/src/Combobox.native.tsx +2 -1
- package/src/DataTable/column-filter.tsx +134 -0
- package/src/DataTable/column-header.tsx +67 -0
- package/src/DataTable/column-visibility.tsx +87 -0
- package/src/DataTable/index.ts +4 -0
- package/src/DataTable/pinning.ts +40 -0
- package/src/DataTable.tsx +14 -297
- package/src/Dialog.native.tsx +4 -2
- package/src/Form/building-blocks.tsx +97 -0
- package/src/Form/fields-choice.tsx +312 -0
- package/src/Form/fields-complex.tsx +195 -0
- package/src/Form/fields-date.tsx +195 -0
- package/src/Form/fields-text.tsx +218 -0
- package/src/Form/fields-toggle.tsx +123 -0
- package/src/Form/helpers.tsx +189 -0
- package/src/Form/types.ts +26 -0
- package/src/Form.tsx +91 -1308
- package/src/IconSidebar.tsx +20 -442
- package/src/MainSidebar/back-button.tsx +58 -0
- package/src/MainSidebar/breadcrumb.tsx +53 -0
- package/src/MainSidebar/columns.tsx +350 -0
- package/src/MainSidebar/command.tsx +404 -0
- package/src/MainSidebar/drilldown.tsx +373 -0
- package/src/MainSidebar/expanded.tsx +414 -0
- package/src/MainSidebar/floating.tsx +268 -0
- package/src/MainSidebar/helpers.ts +164 -0
- package/src/MainSidebar/hover.tsx +334 -0
- package/src/MainSidebar/index.tsx +191 -0
- package/src/MainSidebar/mobile.tsx +117 -0
- package/src/MainSidebar/motion.ts +64 -0
- package/src/MainSidebar/rail.tsx +137 -0
- package/src/MainSidebar/search.tsx +99 -0
- package/src/MainSidebar/types.ts +208 -0
- package/src/MainSidebar.tsx +15 -4
- package/src/NavigationMenu.tsx +1 -1
- package/src/RichTextEditor/theme.ts +43 -0
- package/src/RichTextEditor/toolbar-icons.tsx +40 -0
- package/src/RichTextEditor/toolbar.tsx +271 -0
- package/src/RichTextEditor.tsx +23 -371
- package/src/Select/content.tsx +111 -0
- package/src/Select/context.tsx +66 -0
- package/src/Select/item.tsx +97 -0
- package/src/Select/parts.tsx +43 -0
- package/src/Select/react-select.tsx +216 -0
- package/src/Select/root.tsx +75 -0
- package/src/Select/trigger.tsx +122 -0
- package/src/Select.tsx +34 -692
- package/src/Sidebar/context.tsx +72 -0
- package/src/Sidebar/group.tsx +69 -0
- package/src/Sidebar/icons.tsx +42 -0
- package/src/Sidebar/layout.tsx +64 -0
- package/src/Sidebar/menu.tsx +171 -0
- package/src/Sidebar/provider.tsx +224 -0
- package/src/Sidebar/sidebar.tsx +178 -0
- package/src/Sidebar/submenu.tsx +58 -0
- package/src/Sidebar/trigger.tsx +104 -0
- package/src/Sidebar.tsx +44 -927
- package/src/StatCard.tsx +365 -20
- package/src/TransferList.native.tsx +2 -1
- package/dist/TiptapEditor.d.ts +0 -24
- package/dist/TiptapEditor.d.ts.map +0 -1
- package/dist/TiptapEditor.js +0 -84
- package/dist/TiptapEditor.js.map +0 -1
package/src/Select.tsx
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Select (web variant — React DOM).
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Composable select dropdown built on top of native focus management — no
|
|
7
|
+
* external listbox engine, no native `<select>`. Plus a `react-select`-backed
|
|
8
|
+
* family for searchable / async / multi / creatable cases.
|
|
7
9
|
*
|
|
8
|
-
* Usage:
|
|
9
10
|
* <Select value={val} onValueChange={setVal}>
|
|
10
11
|
* <SelectTrigger placeholder="Pick a fruit" />
|
|
11
12
|
* <SelectContent>
|
|
@@ -14,697 +15,38 @@
|
|
|
14
15
|
* </SelectContent>
|
|
15
16
|
* </Select>
|
|
16
17
|
*
|
|
17
|
-
* Controlled-only: pass `value` / `onValueChange`.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* Controlled-only: pass `value` / `onValueChange`. Keyboard: ArrowDown/Up to
|
|
19
|
+
* navigate, Enter to select, Escape to close. WCAG 2.1 AA: role="listbox",
|
|
20
|
+
* role="option", aria-expanded, aria-selected.
|
|
21
|
+
*
|
|
22
|
+
* Public barrel — implementation split across `./Select/*` per CLAUDE.md §6.
|
|
20
23
|
*
|
|
21
24
|
* Wiki: docs/components/Select.md
|
|
22
25
|
*/
|
|
23
|
-
import {
|
|
24
|
-
forwardRef,
|
|
25
|
-
Children,
|
|
26
|
-
createContext,
|
|
27
|
-
isValidElement,
|
|
28
|
-
useContext,
|
|
29
|
-
useCallback,
|
|
30
|
-
useEffect,
|
|
31
|
-
useRef,
|
|
32
|
-
useState,
|
|
33
|
-
type HTMLAttributes,
|
|
34
|
-
type ReactNode,
|
|
35
|
-
} from 'react';
|
|
36
|
-
import { cva, type VariantProps } from 'class-variance-authority';
|
|
37
|
-
import ReactSelectBase, {
|
|
38
|
-
type ClassNamesConfig,
|
|
39
|
-
type GroupBase,
|
|
40
|
-
type Props as BaseReactSelectProps,
|
|
41
|
-
type SelectComponentsConfig,
|
|
42
|
-
} from 'react-select';
|
|
43
|
-
import ReactAsyncSelectBase, { type AsyncProps } from 'react-select/async';
|
|
44
|
-
import ReactCreatableSelectBase, { type CreatableProps } from 'react-select/creatable';
|
|
45
|
-
import ReactAsyncCreatableSelectBase, {
|
|
46
|
-
type AsyncCreatableProps,
|
|
47
|
-
} from 'react-select/async-creatable';
|
|
48
|
-
import makeAnimated from 'react-select/animated';
|
|
49
|
-
import { cn } from './internal/cn.js';
|
|
50
|
-
|
|
51
|
-
interface ReactSelectDefaultableProps<
|
|
52
|
-
Option,
|
|
53
|
-
IsMulti extends boolean,
|
|
54
|
-
Group extends GroupBase<Option>,
|
|
55
|
-
> {
|
|
56
|
-
className?: string | undefined;
|
|
57
|
-
classNamePrefix?: string | null | undefined;
|
|
58
|
-
classNames?: ClassNamesConfig<Option, IsMulti, Group> | undefined;
|
|
59
|
-
unstyled?: boolean | undefined;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export interface DefaultReactSelectOption {
|
|
63
|
-
value: string;
|
|
64
|
-
label: string;
|
|
65
|
-
isDisabled?: boolean;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export type ReactSelectProps<
|
|
69
|
-
Option = DefaultReactSelectOption,
|
|
70
|
-
IsMulti extends boolean = false,
|
|
71
|
-
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
72
|
-
> = BaseReactSelectProps<Option, IsMulti, Group>;
|
|
73
|
-
|
|
74
|
-
export type ReactAsyncSelectProps<
|
|
75
|
-
Option = DefaultReactSelectOption,
|
|
76
|
-
IsMulti extends boolean = false,
|
|
77
|
-
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
78
|
-
> = AsyncProps<Option, IsMulti, Group>;
|
|
79
|
-
|
|
80
|
-
export type ReactCreatableSelectProps<
|
|
81
|
-
Option = DefaultReactSelectOption,
|
|
82
|
-
IsMulti extends boolean = false,
|
|
83
|
-
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
84
|
-
> = CreatableProps<Option, IsMulti, Group>;
|
|
85
|
-
|
|
86
|
-
export type ReactAsyncCreatableSelectProps<
|
|
87
|
-
Option = DefaultReactSelectOption,
|
|
88
|
-
IsMulti extends boolean = false,
|
|
89
|
-
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
90
|
-
> = AsyncCreatableProps<Option, IsMulti, Group>;
|
|
91
|
-
|
|
92
|
-
export function getReactSelectAnimatedComponents<
|
|
93
|
-
Option = DefaultReactSelectOption,
|
|
94
|
-
IsMulti extends boolean = false,
|
|
95
|
-
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
96
|
-
>(): SelectComponentsConfig<Option, IsMulti, Group> {
|
|
97
|
-
return makeAnimated() as unknown as SelectComponentsConfig<Option, IsMulti, Group>;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export const reactSelectAnimatedComponents: SelectComponentsConfig<
|
|
101
|
-
DefaultReactSelectOption,
|
|
102
|
-
false,
|
|
103
|
-
GroupBase<DefaultReactSelectOption>
|
|
104
|
-
> =
|
|
105
|
-
getReactSelectAnimatedComponents<DefaultReactSelectOption>();
|
|
106
|
-
|
|
107
|
-
const reactSelectClassNames = {
|
|
108
|
-
clearIndicator: ({ isFocused }) => cn(
|
|
109
|
-
'rounded-md p-1 text-muted-foreground transition-colors',
|
|
110
|
-
'hover:bg-muted hover:text-foreground',
|
|
111
|
-
isFocused && 'text-foreground',
|
|
112
|
-
),
|
|
113
|
-
control: ({ isDisabled, isFocused }) => cn(
|
|
114
|
-
'min-h-10 rounded-lg border bg-card px-2.5 text-body-sm shadow-mi-input transition-colors',
|
|
115
|
-
'hover:border-primary/60',
|
|
116
|
-
isFocused ? 'border-primary ring-2 ring-ring/20' : 'border-border',
|
|
117
|
-
isDisabled && 'cursor-not-allowed bg-muted opacity-50',
|
|
118
|
-
),
|
|
119
|
-
dropdownIndicator: ({ isFocused }) => cn(
|
|
120
|
-
'rounded-md p-1 text-muted-foreground transition-colors',
|
|
121
|
-
'hover:bg-muted hover:text-foreground',
|
|
122
|
-
isFocused && 'text-foreground',
|
|
123
|
-
),
|
|
124
|
-
group: () => 'py-1',
|
|
125
|
-
groupHeading: () => 'px-2 py-1 text-xs font-semibold text-muted-foreground',
|
|
126
|
-
indicatorsContainer: () => 'gap-1',
|
|
127
|
-
indicatorSeparator: () => 'mx-1 my-2 w-px self-stretch bg-border',
|
|
128
|
-
input: () => 'text-foreground',
|
|
129
|
-
loadingIndicator: () => 'text-muted-foreground',
|
|
130
|
-
loadingMessage: () => 'px-3 py-6 text-center text-body-sm text-muted-foreground',
|
|
131
|
-
menu: () => 'z-50 mt-1 overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-mi-input',
|
|
132
|
-
menuList: () => 'max-h-72 overflow-y-auto p-1',
|
|
133
|
-
multiValue: () => 'm-0.5 inline-flex items-center gap-1 rounded-md bg-muted text-foreground',
|
|
134
|
-
multiValueLabel: () => 'px-2 py-0.5 text-xs font-medium',
|
|
135
|
-
multiValueRemove: () => cn(
|
|
136
|
-
'rounded-r-md px-1 text-muted-foreground transition-colors',
|
|
137
|
-
'hover:bg-destructive hover:text-destructive-foreground',
|
|
138
|
-
),
|
|
139
|
-
noOptionsMessage: () => 'px-3 py-6 text-center text-body-sm text-muted-foreground',
|
|
140
|
-
option: ({ isDisabled, isFocused, isSelected }) => cn(
|
|
141
|
-
'cursor-pointer rounded-md px-2 py-1.5 text-body-sm outline-none transition-colors',
|
|
142
|
-
isFocused && 'bg-muted',
|
|
143
|
-
isSelected && 'bg-primary text-primary-foreground',
|
|
144
|
-
isDisabled && 'pointer-events-none cursor-not-allowed opacity-50',
|
|
145
|
-
),
|
|
146
|
-
placeholder: () => 'text-muted-foreground',
|
|
147
|
-
singleValue: () => 'text-foreground',
|
|
148
|
-
valueContainer: () => 'gap-1 py-1.5',
|
|
149
|
-
} satisfies ClassNamesConfig<unknown, boolean, GroupBase<unknown>>;
|
|
150
|
-
|
|
151
|
-
function withReactSelectDefaults<
|
|
152
|
-
Option,
|
|
153
|
-
IsMulti extends boolean,
|
|
154
|
-
Group extends GroupBase<Option>,
|
|
155
|
-
Props extends ReactSelectDefaultableProps<Option, IsMulti, Group>,
|
|
156
|
-
>(props: Props): Props {
|
|
157
|
-
const {
|
|
158
|
-
className,
|
|
159
|
-
classNamePrefix = 'mihcm-select',
|
|
160
|
-
classNames,
|
|
161
|
-
unstyled = true,
|
|
162
|
-
...rest
|
|
163
|
-
} = props;
|
|
164
|
-
const defaultClassNames = reactSelectClassNames as unknown as ClassNamesConfig<
|
|
165
|
-
Option,
|
|
166
|
-
IsMulti,
|
|
167
|
-
Group
|
|
168
|
-
>;
|
|
169
|
-
|
|
170
|
-
return {
|
|
171
|
-
...rest,
|
|
172
|
-
className: cn('w-full min-w-0', className),
|
|
173
|
-
classNamePrefix,
|
|
174
|
-
classNames: {
|
|
175
|
-
...defaultClassNames,
|
|
176
|
-
...classNames,
|
|
177
|
-
},
|
|
178
|
-
unstyled,
|
|
179
|
-
} as Props;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/* ── React Select powered exports ──────────────────────────────────── */
|
|
183
|
-
|
|
184
|
-
export function ReactSelect<
|
|
185
|
-
Option = DefaultReactSelectOption,
|
|
186
|
-
IsMulti extends boolean = false,
|
|
187
|
-
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
188
|
-
>(props: ReactSelectProps<Option, IsMulti, Group>) {
|
|
189
|
-
return (
|
|
190
|
-
<ReactSelectBase
|
|
191
|
-
{...withReactSelectDefaults<Option, IsMulti, Group, ReactSelectProps<Option, IsMulti, Group>>(
|
|
192
|
-
props,
|
|
193
|
-
)}
|
|
194
|
-
/>
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export function ReactAsyncSelect<
|
|
199
|
-
Option = DefaultReactSelectOption,
|
|
200
|
-
IsMulti extends boolean = false,
|
|
201
|
-
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
202
|
-
>(props: ReactAsyncSelectProps<Option, IsMulti, Group>) {
|
|
203
|
-
return (
|
|
204
|
-
<ReactAsyncSelectBase
|
|
205
|
-
{...withReactSelectDefaults<
|
|
206
|
-
Option,
|
|
207
|
-
IsMulti,
|
|
208
|
-
Group,
|
|
209
|
-
ReactAsyncSelectProps<Option, IsMulti, Group>
|
|
210
|
-
>(props)}
|
|
211
|
-
/>
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export function ReactCreatableSelect<
|
|
216
|
-
Option = DefaultReactSelectOption,
|
|
217
|
-
IsMulti extends boolean = false,
|
|
218
|
-
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
219
|
-
>(props: ReactCreatableSelectProps<Option, IsMulti, Group>) {
|
|
220
|
-
return (
|
|
221
|
-
<ReactCreatableSelectBase
|
|
222
|
-
{...withReactSelectDefaults<
|
|
223
|
-
Option,
|
|
224
|
-
IsMulti,
|
|
225
|
-
Group,
|
|
226
|
-
ReactCreatableSelectProps<Option, IsMulti, Group>
|
|
227
|
-
>(props)}
|
|
228
|
-
/>
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
export function ReactAsyncCreatableSelect<
|
|
233
|
-
Option = DefaultReactSelectOption,
|
|
234
|
-
IsMulti extends boolean = false,
|
|
235
|
-
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
236
|
-
>(props: ReactAsyncCreatableSelectProps<Option, IsMulti, Group>) {
|
|
237
|
-
return (
|
|
238
|
-
<ReactAsyncCreatableSelectBase
|
|
239
|
-
{...withReactSelectDefaults<
|
|
240
|
-
Option,
|
|
241
|
-
IsMulti,
|
|
242
|
-
Group,
|
|
243
|
-
ReactAsyncCreatableSelectProps<Option, IsMulti, Group>
|
|
244
|
-
>(props)}
|
|
245
|
-
/>
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/* ── Context ───────────────────────────────────────────────────────── */
|
|
250
|
-
|
|
251
|
-
interface SelectContextValue {
|
|
252
|
-
value: string | undefined;
|
|
253
|
-
onValueChange: (value: string) => void;
|
|
254
|
-
open: boolean;
|
|
255
|
-
setOpen: (open: boolean) => void;
|
|
256
|
-
disabled: boolean;
|
|
257
|
-
triggerRef: React.RefObject<HTMLButtonElement | null>;
|
|
258
|
-
setTriggerRef: (el: HTMLButtonElement | null) => void;
|
|
259
|
-
/** Label text of the currently selected item (set by SelectItem). */
|
|
260
|
-
selectedLabel: { value: string; label: string } | undefined;
|
|
261
|
-
setSelectedLabel: (selected: { value: string; label: string } | undefined) => void;
|
|
262
|
-
getLabelForValue: (value: string | undefined) => string | undefined;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const SelectContext = createContext<SelectContextValue | null>(null);
|
|
266
|
-
|
|
267
|
-
function useSelectContext() {
|
|
268
|
-
const ctx = useContext(SelectContext);
|
|
269
|
-
if (!ctx) throw new Error('Select sub-components must be used within <Select>.');
|
|
270
|
-
return ctx;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function getTextLabel(children: ReactNode): string | undefined {
|
|
274
|
-
if (typeof children === 'string' || typeof children === 'number') {
|
|
275
|
-
return String(children);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return undefined;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function findSelectItemLabel(children: ReactNode, value: string | undefined): string | undefined {
|
|
282
|
-
if (!value) return undefined;
|
|
283
|
-
|
|
284
|
-
let match: string | undefined;
|
|
285
|
-
|
|
286
|
-
Children.forEach(children, (child) => {
|
|
287
|
-
if (match || !isValidElement(child)) return;
|
|
288
|
-
|
|
289
|
-
const props = child.props as {
|
|
290
|
-
value?: unknown;
|
|
291
|
-
children?: ReactNode;
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
if (props.value === value) {
|
|
295
|
-
match = getTextLabel(props.children);
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
match = findSelectItemLabel(props.children, value);
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
return match;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/* ── Root ───────────────────────────────────────────────────────────── */
|
|
306
|
-
|
|
307
|
-
export interface SelectProps {
|
|
308
|
-
value?: string | undefined;
|
|
309
|
-
onValueChange?: ((value: string) => void) | undefined;
|
|
310
|
-
open?: boolean | undefined;
|
|
311
|
-
onOpenChange?: ((open: boolean) => void) | undefined;
|
|
312
|
-
disabled?: boolean | undefined;
|
|
313
|
-
children: ReactNode;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export function Select({
|
|
317
|
-
value,
|
|
318
|
-
onValueChange,
|
|
319
|
-
open: controlledOpen,
|
|
320
|
-
onOpenChange,
|
|
321
|
-
disabled,
|
|
322
|
-
children,
|
|
323
|
-
}: SelectProps) {
|
|
324
|
-
const [internalOpen, setInternalOpen] = useState(false);
|
|
325
|
-
const open = controlledOpen ?? internalOpen;
|
|
326
|
-
const setOpen = useCallback(
|
|
327
|
-
(next: boolean) => {
|
|
328
|
-
if (disabled && next) return;
|
|
329
|
-
onOpenChange?.(next);
|
|
330
|
-
setInternalOpen(next);
|
|
331
|
-
},
|
|
332
|
-
[disabled, onOpenChange],
|
|
333
|
-
);
|
|
334
|
-
|
|
335
|
-
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
|
336
|
-
const setTriggerRef = useCallback((el: HTMLButtonElement | null) => {
|
|
337
|
-
triggerRef.current = el;
|
|
338
|
-
}, []);
|
|
339
|
-
const [selectedLabel, setSelectedLabel] = useState<{ value: string; label: string } | undefined>();
|
|
340
|
-
|
|
341
|
-
const handleValueChange = useCallback(
|
|
342
|
-
(v: string) => {
|
|
343
|
-
onValueChange?.(v);
|
|
344
|
-
setOpen(false);
|
|
345
|
-
},
|
|
346
|
-
[onValueChange, setOpen],
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
const getLabelForValue = useCallback(
|
|
350
|
-
(nextValue: string | undefined) => findSelectItemLabel(children, nextValue),
|
|
351
|
-
[children],
|
|
352
|
-
);
|
|
353
|
-
|
|
354
|
-
return (
|
|
355
|
-
<SelectContext.Provider
|
|
356
|
-
value={{
|
|
357
|
-
value,
|
|
358
|
-
onValueChange: handleValueChange,
|
|
359
|
-
open,
|
|
360
|
-
setOpen,
|
|
361
|
-
disabled: !!disabled,
|
|
362
|
-
triggerRef,
|
|
363
|
-
setTriggerRef,
|
|
364
|
-
selectedLabel,
|
|
365
|
-
setSelectedLabel,
|
|
366
|
-
getLabelForValue,
|
|
367
|
-
}}
|
|
368
|
-
>
|
|
369
|
-
<div className="relative inline-block">{children}</div>
|
|
370
|
-
</SelectContext.Provider>
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/* ── Trigger ───────────────────────────────────────────────────────── */
|
|
375
|
-
|
|
376
|
-
export const selectTriggerVariants = cva(
|
|
377
|
-
'inline-flex w-full items-center justify-between gap-2 rounded-lg border bg-card text-foreground shadow-mi-input ' +
|
|
378
|
-
'transition-colors duration-150 ' +
|
|
379
|
-
'focus-visible:outline-none focus-visible:border-primary ' +
|
|
380
|
-
'disabled:pointer-events-none disabled:opacity-50 ' +
|
|
381
|
-
'select-none whitespace-nowrap',
|
|
382
|
-
{
|
|
383
|
-
variants: {
|
|
384
|
-
variant: {
|
|
385
|
-
default: 'border-border',
|
|
386
|
-
destructive:
|
|
387
|
-
'border-destructive text-destructive focus-visible:border-destructive',
|
|
388
|
-
},
|
|
389
|
-
size: {
|
|
390
|
-
sm: 'h-9 px-3 text-body-sm',
|
|
391
|
-
md: 'h-10 px-3.5 text-body-sm',
|
|
392
|
-
lg: 'h-12 px-4 text-body-sm',
|
|
393
|
-
},
|
|
394
|
-
},
|
|
395
|
-
defaultVariants: {
|
|
396
|
-
variant: 'default',
|
|
397
|
-
size: 'md',
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
export interface SelectTriggerProps
|
|
403
|
-
extends Omit<HTMLAttributes<HTMLButtonElement>, 'children'>,
|
|
404
|
-
VariantProps<typeof selectTriggerVariants> {
|
|
405
|
-
placeholder?: string | undefined;
|
|
406
|
-
disabled?: boolean | undefined;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
export const SelectTrigger = forwardRef<HTMLButtonElement, SelectTriggerProps>(
|
|
410
|
-
function SelectTrigger({ className, variant, size, placeholder, disabled, id, ...props }, ref) {
|
|
411
|
-
const ctx = useSelectContext();
|
|
412
|
-
const isDisabled = disabled ?? ctx.disabled;
|
|
413
|
-
const listboxId = id ? `${id}-listbox` : undefined;
|
|
414
|
-
|
|
415
|
-
const handleClick = useCallback(() => {
|
|
416
|
-
ctx.setOpen(!ctx.open);
|
|
417
|
-
}, [ctx]);
|
|
418
|
-
|
|
419
|
-
const handleKeyDown = useCallback(
|
|
420
|
-
(e: React.KeyboardEvent) => {
|
|
421
|
-
if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {
|
|
422
|
-
e.preventDefault();
|
|
423
|
-
if (!ctx.open) ctx.setOpen(true);
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
|
-
[ctx],
|
|
427
|
-
);
|
|
428
|
-
|
|
429
|
-
// Merge refs
|
|
430
|
-
const setRef = useCallback(
|
|
431
|
-
(el: HTMLButtonElement | null) => {
|
|
432
|
-
ctx.setTriggerRef(el);
|
|
433
|
-
if (typeof ref === 'function') ref(el);
|
|
434
|
-
else if (ref) (ref as React.MutableRefObject<HTMLButtonElement | null>).current = el;
|
|
435
|
-
},
|
|
436
|
-
[ctx, ref],
|
|
437
|
-
);
|
|
438
|
-
|
|
439
|
-
const selectedLabel = ctx.selectedLabel;
|
|
440
|
-
const hasValue = ctx.value !== undefined && ctx.value !== '';
|
|
441
|
-
const displayText =
|
|
442
|
-
hasValue && selectedLabel && selectedLabel.value === ctx.value
|
|
443
|
-
? selectedLabel.label
|
|
444
|
-
: hasValue
|
|
445
|
-
? ctx.getLabelForValue(ctx.value) ?? ctx.value
|
|
446
|
-
: undefined;
|
|
447
|
-
|
|
448
|
-
return (
|
|
449
|
-
<button
|
|
450
|
-
ref={setRef}
|
|
451
|
-
type="button"
|
|
452
|
-
id={id}
|
|
453
|
-
role="combobox"
|
|
454
|
-
aria-expanded={ctx.open}
|
|
455
|
-
aria-haspopup="listbox"
|
|
456
|
-
aria-controls={ctx.open ? listboxId : undefined}
|
|
457
|
-
disabled={isDisabled}
|
|
458
|
-
onClick={handleClick}
|
|
459
|
-
onKeyDown={handleKeyDown}
|
|
460
|
-
className={cn(selectTriggerVariants({ variant, size }), className)}
|
|
461
|
-
{...props}
|
|
462
|
-
>
|
|
463
|
-
<span className={cn('truncate', !displayText && 'text-muted-foreground')}>
|
|
464
|
-
{displayText ?? placeholder ?? 'Select…'}
|
|
465
|
-
</span>
|
|
466
|
-
{/* Chevron-down icon */}
|
|
467
|
-
<svg
|
|
468
|
-
aria-hidden
|
|
469
|
-
viewBox="0 0 16 16"
|
|
470
|
-
fill="none"
|
|
471
|
-
stroke="currentColor"
|
|
472
|
-
strokeWidth={2}
|
|
473
|
-
className={cn(
|
|
474
|
-
'h-4 w-4 shrink-0 opacity-50 transition-transform duration-200',
|
|
475
|
-
ctx.open && 'rotate-180',
|
|
476
|
-
)}
|
|
477
|
-
>
|
|
478
|
-
<path strokeLinecap="round" strokeLinejoin="round" d="M4 6l4 4 4-4" />
|
|
479
|
-
</svg>
|
|
480
|
-
</button>
|
|
481
|
-
);
|
|
482
|
-
},
|
|
483
|
-
);
|
|
484
|
-
|
|
485
|
-
/* ── Content ───────────────────────────────────────────────────────── */
|
|
486
|
-
|
|
487
|
-
export interface SelectContentProps extends HTMLAttributes<HTMLDivElement> {}
|
|
488
|
-
|
|
489
|
-
export const SelectContent = forwardRef<HTMLDivElement, SelectContentProps>(
|
|
490
|
-
function SelectContent({ className, children, ...props }, ref) {
|
|
491
|
-
const ctx = useSelectContext();
|
|
492
|
-
const contentRef = useRef<HTMLDivElement | null>(null);
|
|
493
|
-
const focusedIndexRef = useRef(-1);
|
|
494
|
-
|
|
495
|
-
// Close on click outside
|
|
496
|
-
useEffect(() => {
|
|
497
|
-
if (!ctx.open) return;
|
|
498
|
-
function handleClickOutside(e: MouseEvent) {
|
|
499
|
-
const target = e.target as Node;
|
|
500
|
-
if (
|
|
501
|
-
contentRef.current &&
|
|
502
|
-
!contentRef.current.contains(target) &&
|
|
503
|
-
ctx.triggerRef.current &&
|
|
504
|
-
!ctx.triggerRef.current.contains(target)
|
|
505
|
-
) {
|
|
506
|
-
ctx.setOpen(false);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
510
|
-
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
511
|
-
}, [ctx]);
|
|
512
|
-
|
|
513
|
-
// Keyboard navigation within the list
|
|
514
|
-
useEffect(() => {
|
|
515
|
-
if (!ctx.open) return;
|
|
516
|
-
function handleKeyDown(e: KeyboardEvent) {
|
|
517
|
-
const options = contentRef.current?.querySelectorAll<HTMLElement>('[role="option"]');
|
|
518
|
-
if (!options?.length) return;
|
|
519
|
-
|
|
520
|
-
if (e.key === 'Escape') {
|
|
521
|
-
e.preventDefault();
|
|
522
|
-
ctx.setOpen(false);
|
|
523
|
-
ctx.triggerRef.current?.focus();
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
if (e.key === 'ArrowDown') {
|
|
528
|
-
e.preventDefault();
|
|
529
|
-
focusedIndexRef.current = Math.min(focusedIndexRef.current + 1, options.length - 1);
|
|
530
|
-
options[focusedIndexRef.current]?.focus();
|
|
531
|
-
} else if (e.key === 'ArrowUp') {
|
|
532
|
-
e.preventDefault();
|
|
533
|
-
focusedIndexRef.current = Math.max(focusedIndexRef.current - 1, 0);
|
|
534
|
-
options[focusedIndexRef.current]?.focus();
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
538
|
-
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
539
|
-
}, [ctx]);
|
|
540
|
-
|
|
541
|
-
// Focus first option (or selected) on open
|
|
542
|
-
useEffect(() => {
|
|
543
|
-
if (!ctx.open || !contentRef.current) return;
|
|
544
|
-
focusedIndexRef.current = -1;
|
|
545
|
-
const selected = contentRef.current.querySelector<HTMLElement>('[aria-selected="true"]');
|
|
546
|
-
if (selected) {
|
|
547
|
-
selected.focus();
|
|
548
|
-
} else {
|
|
549
|
-
const first = contentRef.current.querySelector<HTMLElement>('[role="option"]');
|
|
550
|
-
first?.focus();
|
|
551
|
-
}
|
|
552
|
-
}, [ctx.open]);
|
|
553
|
-
|
|
554
|
-
// Merge refs
|
|
555
|
-
const setRef = useCallback(
|
|
556
|
-
(el: HTMLDivElement | null) => {
|
|
557
|
-
contentRef.current = el;
|
|
558
|
-
if (typeof ref === 'function') ref(el);
|
|
559
|
-
else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = el;
|
|
560
|
-
},
|
|
561
|
-
[ref],
|
|
562
|
-
);
|
|
563
|
-
|
|
564
|
-
if (!ctx.open) return null;
|
|
565
|
-
|
|
566
|
-
return (
|
|
567
|
-
<div
|
|
568
|
-
ref={setRef}
|
|
569
|
-
id={ctx.triggerRef.current?.id ? `${ctx.triggerRef.current.id}-listbox` : undefined}
|
|
570
|
-
role="listbox"
|
|
571
|
-
aria-labelledby={ctx.triggerRef.current?.id || undefined}
|
|
572
|
-
className={cn(
|
|
573
|
-
'absolute left-0 top-full z-50 mt-1 w-full min-w-[8rem] overflow-hidden rounded-lg',
|
|
574
|
-
'border border-border bg-card text-card-foreground shadow-mi-input',
|
|
575
|
-
'animate-dropdown-in',
|
|
576
|
-
className,
|
|
577
|
-
)}
|
|
578
|
-
{...props}
|
|
579
|
-
>
|
|
580
|
-
<div className="max-h-60 overflow-y-auto p-1">{children}</div>
|
|
581
|
-
</div>
|
|
582
|
-
);
|
|
583
|
-
},
|
|
584
|
-
);
|
|
585
|
-
|
|
586
|
-
/* ── Item ──────────────────────────────────────────────────────────── */
|
|
587
|
-
|
|
588
|
-
export interface SelectItemProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onClick'> {
|
|
589
|
-
value: string;
|
|
590
|
-
disabled?: boolean;
|
|
591
|
-
children: ReactNode;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
export const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
|
|
595
|
-
function SelectItem({ className, value, disabled, children, ...props }, ref) {
|
|
596
|
-
const ctx = useSelectContext();
|
|
597
|
-
const { onValueChange, selectedLabel, setSelectedLabel, triggerRef } = ctx;
|
|
598
|
-
const isSelected = ctx.value === value;
|
|
599
|
-
|
|
600
|
-
// Register label of selected item
|
|
601
|
-
useEffect(() => {
|
|
602
|
-
if (
|
|
603
|
-
isSelected &&
|
|
604
|
-
typeof children === 'string' &&
|
|
605
|
-
(selectedLabel?.value !== value || selectedLabel.label !== children)
|
|
606
|
-
) {
|
|
607
|
-
setSelectedLabel({ value, label: children });
|
|
608
|
-
}
|
|
609
|
-
}, [isSelected, children, selectedLabel?.label, selectedLabel?.value, setSelectedLabel, value]);
|
|
610
|
-
|
|
611
|
-
const handleSelect = useCallback(() => {
|
|
612
|
-
if (disabled) return;
|
|
613
|
-
if (typeof children === 'string') {
|
|
614
|
-
setSelectedLabel({ value, label: children });
|
|
615
|
-
}
|
|
616
|
-
onValueChange(value);
|
|
617
|
-
triggerRef.current?.focus();
|
|
618
|
-
}, [children, disabled, onValueChange, setSelectedLabel, triggerRef, value]);
|
|
619
|
-
|
|
620
|
-
const handleKeyDown = useCallback(
|
|
621
|
-
(e: React.KeyboardEvent) => {
|
|
622
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
623
|
-
e.preventDefault();
|
|
624
|
-
handleSelect();
|
|
625
|
-
}
|
|
626
|
-
},
|
|
627
|
-
[handleSelect],
|
|
628
|
-
);
|
|
629
|
-
|
|
630
|
-
return (
|
|
631
|
-
<div
|
|
632
|
-
ref={ref}
|
|
633
|
-
role="option"
|
|
634
|
-
aria-selected={isSelected}
|
|
635
|
-
aria-disabled={disabled || undefined}
|
|
636
|
-
tabIndex={disabled ? undefined : -1}
|
|
637
|
-
onClick={handleSelect}
|
|
638
|
-
onKeyDown={handleKeyDown}
|
|
639
|
-
className={cn(
|
|
640
|
-
'relative flex cursor-pointer items-center rounded-md px-2 py-1.5 text-body-sm outline-none',
|
|
641
|
-
'transition-colors duration-150',
|
|
642
|
-
'hover:bg-muted focus:bg-muted',
|
|
643
|
-
isSelected && 'font-medium',
|
|
644
|
-
disabled && 'pointer-events-none opacity-50',
|
|
645
|
-
className,
|
|
646
|
-
)}
|
|
647
|
-
{...props}
|
|
648
|
-
>
|
|
649
|
-
{/* Check icon */}
|
|
650
|
-
<span className="mr-2 inline-flex h-4 w-4 shrink-0 items-center justify-center">
|
|
651
|
-
{isSelected && (
|
|
652
|
-
<svg
|
|
653
|
-
aria-hidden
|
|
654
|
-
viewBox="0 0 16 16"
|
|
655
|
-
fill="none"
|
|
656
|
-
stroke="currentColor"
|
|
657
|
-
strokeWidth={2}
|
|
658
|
-
className="h-4 w-4"
|
|
659
|
-
>
|
|
660
|
-
<path strokeLinecap="round" strokeLinejoin="round" d="M3 8l3 3 7-7" />
|
|
661
|
-
</svg>
|
|
662
|
-
)}
|
|
663
|
-
</span>
|
|
664
|
-
<span className="truncate">{children}</span>
|
|
665
|
-
</div>
|
|
666
|
-
);
|
|
667
|
-
},
|
|
668
|
-
);
|
|
669
|
-
|
|
670
|
-
/* ── Group ─────────────────────────────────────────────────────────── */
|
|
671
|
-
|
|
672
|
-
export interface SelectGroupProps extends HTMLAttributes<HTMLDivElement> {}
|
|
673
|
-
|
|
674
|
-
export const SelectGroup = forwardRef<HTMLDivElement, SelectGroupProps>(
|
|
675
|
-
function SelectGroup({ className, ...props }, ref) {
|
|
676
|
-
return <div ref={ref} role="group" className={cn('py-1', className)} {...props} />;
|
|
677
|
-
},
|
|
678
|
-
);
|
|
679
|
-
|
|
680
|
-
/* ── Label ─────────────────────────────────────────────────────────── */
|
|
681
|
-
|
|
682
|
-
export interface SelectLabelProps extends HTMLAttributes<HTMLDivElement> {}
|
|
683
|
-
|
|
684
|
-
export const SelectLabel = forwardRef<HTMLDivElement, SelectLabelProps>(
|
|
685
|
-
function SelectLabel({ className, ...props }, ref) {
|
|
686
|
-
return (
|
|
687
|
-
<div
|
|
688
|
-
ref={ref}
|
|
689
|
-
className={cn('px-2 py-1.5 text-xs font-semibold text-muted-foreground', className)}
|
|
690
|
-
{...props}
|
|
691
|
-
/>
|
|
692
|
-
);
|
|
693
|
-
},
|
|
694
|
-
);
|
|
695
|
-
|
|
696
|
-
/* ── Separator ─────────────────────────────────────────────────────── */
|
|
697
|
-
|
|
698
|
-
export interface SelectSeparatorProps extends HTMLAttributes<HTMLHRElement> {}
|
|
699
26
|
|
|
700
|
-
export
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
27
|
+
export {
|
|
28
|
+
getReactSelectAnimatedComponents,
|
|
29
|
+
reactSelectAnimatedComponents,
|
|
30
|
+
ReactAsyncCreatableSelect,
|
|
31
|
+
ReactAsyncSelect,
|
|
32
|
+
ReactCreatableSelect,
|
|
33
|
+
ReactSelect,
|
|
34
|
+
type DefaultReactSelectOption,
|
|
35
|
+
type ReactAsyncCreatableSelectProps,
|
|
36
|
+
type ReactAsyncSelectProps,
|
|
37
|
+
type ReactCreatableSelectProps,
|
|
38
|
+
type ReactSelectProps,
|
|
39
|
+
} from './Select/react-select.js';
|
|
40
|
+
|
|
41
|
+
export { Select, type SelectProps } from './Select/root.js';
|
|
42
|
+
export { SelectTrigger, selectTriggerVariants, type SelectTriggerProps } from './Select/trigger.js';
|
|
43
|
+
export { SelectContent, type SelectContentProps } from './Select/content.js';
|
|
44
|
+
export { SelectItem, type SelectItemProps } from './Select/item.js';
|
|
45
|
+
export {
|
|
46
|
+
SelectGroup,
|
|
47
|
+
SelectLabel,
|
|
48
|
+
SelectSeparator,
|
|
49
|
+
type SelectGroupProps,
|
|
50
|
+
type SelectLabelProps,
|
|
51
|
+
type SelectSeparatorProps,
|
|
52
|
+
} from './Select/parts.js';
|