@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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SelectItem — single option row with check indicator and keyboard support.
|
|
5
|
+
* Registers its label with the Select context so the trigger can display the
|
|
6
|
+
* selected option's visible label without re-deriving from `children`.
|
|
7
|
+
*/
|
|
8
|
+
import {
|
|
9
|
+
forwardRef,
|
|
10
|
+
useCallback,
|
|
11
|
+
useEffect,
|
|
12
|
+
type HTMLAttributes,
|
|
13
|
+
type KeyboardEvent,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
} from 'react';
|
|
16
|
+
import { cn } from '../internal/cn.js';
|
|
17
|
+
import { useSelectContext } from './context.js';
|
|
18
|
+
|
|
19
|
+
export interface SelectItemProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onClick'> {
|
|
20
|
+
value: string;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
|
|
26
|
+
function SelectItem({ className, value, disabled, children, ...props }, ref) {
|
|
27
|
+
const ctx = useSelectContext();
|
|
28
|
+
const { onValueChange, selectedLabel, setSelectedLabel, triggerRef } = ctx;
|
|
29
|
+
const isSelected = ctx.value === value;
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (
|
|
33
|
+
isSelected &&
|
|
34
|
+
typeof children === 'string' &&
|
|
35
|
+
(selectedLabel?.value !== value || selectedLabel.label !== children)
|
|
36
|
+
) {
|
|
37
|
+
setSelectedLabel({ value, label: children });
|
|
38
|
+
}
|
|
39
|
+
}, [isSelected, children, selectedLabel?.label, selectedLabel?.value, setSelectedLabel, value]);
|
|
40
|
+
|
|
41
|
+
const handleSelect = useCallback(() => {
|
|
42
|
+
if (disabled) return;
|
|
43
|
+
if (typeof children === 'string') {
|
|
44
|
+
setSelectedLabel({ value, label: children });
|
|
45
|
+
}
|
|
46
|
+
onValueChange(value);
|
|
47
|
+
triggerRef.current?.focus();
|
|
48
|
+
}, [children, disabled, onValueChange, setSelectedLabel, triggerRef, value]);
|
|
49
|
+
|
|
50
|
+
const handleKeyDown = useCallback(
|
|
51
|
+
(e: KeyboardEvent) => {
|
|
52
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
handleSelect();
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
[handleSelect],
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
ref={ref}
|
|
63
|
+
role="option"
|
|
64
|
+
aria-selected={isSelected}
|
|
65
|
+
aria-disabled={disabled || undefined}
|
|
66
|
+
tabIndex={disabled ? undefined : -1}
|
|
67
|
+
onClick={handleSelect}
|
|
68
|
+
onKeyDown={handleKeyDown}
|
|
69
|
+
className={cn(
|
|
70
|
+
'relative flex cursor-pointer items-center rounded-md px-2 py-1.5 text-body-sm outline-none',
|
|
71
|
+
'transition-colors duration-150',
|
|
72
|
+
'hover:bg-muted focus:bg-muted',
|
|
73
|
+
isSelected && 'font-medium',
|
|
74
|
+
disabled && 'pointer-events-none opacity-50',
|
|
75
|
+
className,
|
|
76
|
+
)}
|
|
77
|
+
{...props}
|
|
78
|
+
>
|
|
79
|
+
<span className="mr-2 inline-flex h-4 w-4 shrink-0 items-center justify-center">
|
|
80
|
+
{isSelected && (
|
|
81
|
+
<svg
|
|
82
|
+
aria-hidden="true"
|
|
83
|
+
viewBox="0 0 16 16"
|
|
84
|
+
fill="none"
|
|
85
|
+
stroke="currentColor"
|
|
86
|
+
strokeWidth={2}
|
|
87
|
+
className="h-4 w-4"
|
|
88
|
+
>
|
|
89
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M3 8l3 3 7-7" />
|
|
90
|
+
</svg>
|
|
91
|
+
)}
|
|
92
|
+
</span>
|
|
93
|
+
<span className="truncate">{children}</span>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
},
|
|
97
|
+
);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Small Select sub-parts: SelectGroup, SelectLabel, SelectSeparator.
|
|
5
|
+
*/
|
|
6
|
+
import { forwardRef, type HTMLAttributes } from 'react';
|
|
7
|
+
import { cn } from '../internal/cn.js';
|
|
8
|
+
|
|
9
|
+
export interface SelectGroupProps extends HTMLAttributes<HTMLDivElement> {}
|
|
10
|
+
|
|
11
|
+
export const SelectGroup = forwardRef<HTMLDivElement, SelectGroupProps>(
|
|
12
|
+
function SelectGroup({ className, ...props }, ref) {
|
|
13
|
+
return <div ref={ref} role="group" className={cn('py-1', className)} {...props} />;
|
|
14
|
+
},
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export interface SelectLabelProps extends HTMLAttributes<HTMLDivElement> {}
|
|
18
|
+
|
|
19
|
+
export const SelectLabel = forwardRef<HTMLDivElement, SelectLabelProps>(
|
|
20
|
+
function SelectLabel({ className, ...props }, ref) {
|
|
21
|
+
return (
|
|
22
|
+
<div
|
|
23
|
+
ref={ref}
|
|
24
|
+
className={cn('px-2 py-1.5 text-xs font-semibold text-muted-foreground', className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export interface SelectSeparatorProps extends HTMLAttributes<HTMLHRElement> {}
|
|
32
|
+
|
|
33
|
+
export const SelectSeparator = forwardRef<HTMLHRElement, SelectSeparatorProps>(
|
|
34
|
+
function SelectSeparator({ className, ...props }, ref) {
|
|
35
|
+
return (
|
|
36
|
+
<hr
|
|
37
|
+
ref={ref}
|
|
38
|
+
className={cn('-mx-1 my-1 border-t border-border', className)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
},
|
|
43
|
+
);
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* React Select-powered exports for `@mihcm/ui/Select`. Provides searchable,
|
|
5
|
+
* async, multi, creatable, grouped, and async-creatable selects with MiHCM
|
|
6
|
+
* field-surface tokens preserved through `classNames`.
|
|
7
|
+
*/
|
|
8
|
+
import ReactSelectBase, {
|
|
9
|
+
type ClassNamesConfig,
|
|
10
|
+
type GroupBase,
|
|
11
|
+
type Props as BaseReactSelectProps,
|
|
12
|
+
type SelectComponentsConfig,
|
|
13
|
+
} from 'react-select';
|
|
14
|
+
import ReactAsyncSelectBase, { type AsyncProps } from 'react-select/async';
|
|
15
|
+
import ReactCreatableSelectBase, { type CreatableProps } from 'react-select/creatable';
|
|
16
|
+
import ReactAsyncCreatableSelectBase, {
|
|
17
|
+
type AsyncCreatableProps,
|
|
18
|
+
} from 'react-select/async-creatable';
|
|
19
|
+
import makeAnimated from 'react-select/animated';
|
|
20
|
+
import { cn } from '../internal/cn.js';
|
|
21
|
+
|
|
22
|
+
interface ReactSelectDefaultableProps<
|
|
23
|
+
Option,
|
|
24
|
+
IsMulti extends boolean,
|
|
25
|
+
Group extends GroupBase<Option>,
|
|
26
|
+
> {
|
|
27
|
+
className?: string | undefined;
|
|
28
|
+
classNamePrefix?: string | null | undefined;
|
|
29
|
+
classNames?: ClassNamesConfig<Option, IsMulti, Group> | undefined;
|
|
30
|
+
unstyled?: boolean | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface DefaultReactSelectOption {
|
|
34
|
+
value: string;
|
|
35
|
+
label: string;
|
|
36
|
+
isDisabled?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type ReactSelectProps<
|
|
40
|
+
Option = DefaultReactSelectOption,
|
|
41
|
+
IsMulti extends boolean = false,
|
|
42
|
+
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
43
|
+
> = BaseReactSelectProps<Option, IsMulti, Group>;
|
|
44
|
+
|
|
45
|
+
export type ReactAsyncSelectProps<
|
|
46
|
+
Option = DefaultReactSelectOption,
|
|
47
|
+
IsMulti extends boolean = false,
|
|
48
|
+
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
49
|
+
> = AsyncProps<Option, IsMulti, Group>;
|
|
50
|
+
|
|
51
|
+
export type ReactCreatableSelectProps<
|
|
52
|
+
Option = DefaultReactSelectOption,
|
|
53
|
+
IsMulti extends boolean = false,
|
|
54
|
+
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
55
|
+
> = CreatableProps<Option, IsMulti, Group>;
|
|
56
|
+
|
|
57
|
+
export type ReactAsyncCreatableSelectProps<
|
|
58
|
+
Option = DefaultReactSelectOption,
|
|
59
|
+
IsMulti extends boolean = false,
|
|
60
|
+
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
61
|
+
> = AsyncCreatableProps<Option, IsMulti, Group>;
|
|
62
|
+
|
|
63
|
+
export function getReactSelectAnimatedComponents<
|
|
64
|
+
Option = DefaultReactSelectOption,
|
|
65
|
+
IsMulti extends boolean = false,
|
|
66
|
+
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
67
|
+
>(): SelectComponentsConfig<Option, IsMulti, Group> {
|
|
68
|
+
return makeAnimated() as unknown as SelectComponentsConfig<Option, IsMulti, Group>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const reactSelectAnimatedComponents: SelectComponentsConfig<
|
|
72
|
+
DefaultReactSelectOption,
|
|
73
|
+
false,
|
|
74
|
+
GroupBase<DefaultReactSelectOption>
|
|
75
|
+
> =
|
|
76
|
+
getReactSelectAnimatedComponents<DefaultReactSelectOption>();
|
|
77
|
+
|
|
78
|
+
const reactSelectClassNames = {
|
|
79
|
+
clearIndicator: ({ isFocused }) => cn(
|
|
80
|
+
'rounded-md p-1 text-muted-foreground transition-colors',
|
|
81
|
+
'hover:bg-muted hover:text-foreground',
|
|
82
|
+
isFocused && 'text-foreground',
|
|
83
|
+
),
|
|
84
|
+
control: ({ isDisabled, isFocused }) => cn(
|
|
85
|
+
'min-h-10 rounded-lg border bg-card px-2.5 text-body-sm shadow-mi-input transition-colors',
|
|
86
|
+
'hover:border-primary/60',
|
|
87
|
+
isFocused ? 'border-primary ring-2 ring-ring/20' : 'border-border',
|
|
88
|
+
isDisabled && 'cursor-not-allowed bg-muted opacity-50',
|
|
89
|
+
),
|
|
90
|
+
dropdownIndicator: ({ isFocused }) => cn(
|
|
91
|
+
'rounded-md p-1 text-muted-foreground transition-colors',
|
|
92
|
+
'hover:bg-muted hover:text-foreground',
|
|
93
|
+
isFocused && 'text-foreground',
|
|
94
|
+
),
|
|
95
|
+
group: () => 'py-1',
|
|
96
|
+
groupHeading: () => 'px-2 py-1 text-xs font-semibold text-muted-foreground',
|
|
97
|
+
indicatorsContainer: () => 'gap-1',
|
|
98
|
+
indicatorSeparator: () => 'mx-1 my-2 w-px self-stretch bg-border',
|
|
99
|
+
input: () => 'text-foreground',
|
|
100
|
+
loadingIndicator: () => 'text-muted-foreground',
|
|
101
|
+
loadingMessage: () => 'px-3 py-6 text-center text-body-sm text-muted-foreground',
|
|
102
|
+
menu: () => 'z-50 mt-1 overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-mi-input',
|
|
103
|
+
menuList: () => 'max-h-72 overflow-y-auto p-1',
|
|
104
|
+
multiValue: () => 'm-0.5 inline-flex items-center gap-1 rounded-md bg-muted text-foreground',
|
|
105
|
+
multiValueLabel: () => 'px-2 py-0.5 text-xs font-medium',
|
|
106
|
+
multiValueRemove: () => cn(
|
|
107
|
+
'rounded-r-md px-1 text-muted-foreground transition-colors',
|
|
108
|
+
'hover:bg-destructive hover:text-destructive-foreground',
|
|
109
|
+
),
|
|
110
|
+
noOptionsMessage: () => 'px-3 py-6 text-center text-body-sm text-muted-foreground',
|
|
111
|
+
option: ({ isDisabled, isFocused, isSelected }) => cn(
|
|
112
|
+
'cursor-pointer rounded-md px-2 py-1.5 text-body-sm outline-none transition-colors',
|
|
113
|
+
isFocused && 'bg-muted',
|
|
114
|
+
isSelected && 'bg-primary text-primary-foreground',
|
|
115
|
+
isDisabled && 'pointer-events-none cursor-not-allowed opacity-50',
|
|
116
|
+
),
|
|
117
|
+
placeholder: () => 'text-muted-foreground',
|
|
118
|
+
singleValue: () => 'text-foreground',
|
|
119
|
+
valueContainer: () => 'gap-1 py-1.5',
|
|
120
|
+
} satisfies ClassNamesConfig<unknown, boolean, GroupBase<unknown>>;
|
|
121
|
+
|
|
122
|
+
function withReactSelectDefaults<
|
|
123
|
+
Option,
|
|
124
|
+
IsMulti extends boolean,
|
|
125
|
+
Group extends GroupBase<Option>,
|
|
126
|
+
Props extends ReactSelectDefaultableProps<Option, IsMulti, Group>,
|
|
127
|
+
>(props: Props): Props {
|
|
128
|
+
const {
|
|
129
|
+
className,
|
|
130
|
+
classNamePrefix = 'mihcm-select',
|
|
131
|
+
classNames,
|
|
132
|
+
unstyled = true,
|
|
133
|
+
...rest
|
|
134
|
+
} = props;
|
|
135
|
+
const defaultClassNames = reactSelectClassNames as unknown as ClassNamesConfig<
|
|
136
|
+
Option,
|
|
137
|
+
IsMulti,
|
|
138
|
+
Group
|
|
139
|
+
>;
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
...rest,
|
|
143
|
+
className: cn('w-full min-w-0', className),
|
|
144
|
+
classNamePrefix,
|
|
145
|
+
classNames: {
|
|
146
|
+
...defaultClassNames,
|
|
147
|
+
...classNames,
|
|
148
|
+
},
|
|
149
|
+
unstyled,
|
|
150
|
+
} as Props;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function ReactSelect<
|
|
154
|
+
Option = DefaultReactSelectOption,
|
|
155
|
+
IsMulti extends boolean = false,
|
|
156
|
+
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
157
|
+
>(props: ReactSelectProps<Option, IsMulti, Group>) {
|
|
158
|
+
return (
|
|
159
|
+
<ReactSelectBase
|
|
160
|
+
{...withReactSelectDefaults<Option, IsMulti, Group, ReactSelectProps<Option, IsMulti, Group>>(
|
|
161
|
+
props,
|
|
162
|
+
)}
|
|
163
|
+
/>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function ReactAsyncSelect<
|
|
168
|
+
Option = DefaultReactSelectOption,
|
|
169
|
+
IsMulti extends boolean = false,
|
|
170
|
+
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
171
|
+
>(props: ReactAsyncSelectProps<Option, IsMulti, Group>) {
|
|
172
|
+
return (
|
|
173
|
+
<ReactAsyncSelectBase
|
|
174
|
+
{...withReactSelectDefaults<
|
|
175
|
+
Option,
|
|
176
|
+
IsMulti,
|
|
177
|
+
Group,
|
|
178
|
+
ReactAsyncSelectProps<Option, IsMulti, Group>
|
|
179
|
+
>(props)}
|
|
180
|
+
/>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function ReactCreatableSelect<
|
|
185
|
+
Option = DefaultReactSelectOption,
|
|
186
|
+
IsMulti extends boolean = false,
|
|
187
|
+
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
188
|
+
>(props: ReactCreatableSelectProps<Option, IsMulti, Group>) {
|
|
189
|
+
return (
|
|
190
|
+
<ReactCreatableSelectBase
|
|
191
|
+
{...withReactSelectDefaults<
|
|
192
|
+
Option,
|
|
193
|
+
IsMulti,
|
|
194
|
+
Group,
|
|
195
|
+
ReactCreatableSelectProps<Option, IsMulti, Group>
|
|
196
|
+
>(props)}
|
|
197
|
+
/>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function ReactAsyncCreatableSelect<
|
|
202
|
+
Option = DefaultReactSelectOption,
|
|
203
|
+
IsMulti extends boolean = false,
|
|
204
|
+
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
205
|
+
>(props: ReactAsyncCreatableSelectProps<Option, IsMulti, Group>) {
|
|
206
|
+
return (
|
|
207
|
+
<ReactAsyncCreatableSelectBase
|
|
208
|
+
{...withReactSelectDefaults<
|
|
209
|
+
Option,
|
|
210
|
+
IsMulti,
|
|
211
|
+
Group,
|
|
212
|
+
ReactAsyncCreatableSelectProps<Option, IsMulti, Group>
|
|
213
|
+
>(props)}
|
|
214
|
+
/>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Select root — owns open/close state, value, and the selected-label cache
|
|
5
|
+
* that downstream SelectTrigger reads to render the active option's label.
|
|
6
|
+
*/
|
|
7
|
+
import { useCallback, useRef, useState, type ReactNode } from 'react';
|
|
8
|
+
import { SelectContext, findSelectItemLabel } from './context.js';
|
|
9
|
+
|
|
10
|
+
export interface SelectProps {
|
|
11
|
+
value?: string | undefined;
|
|
12
|
+
onValueChange?: ((value: string) => void) | undefined;
|
|
13
|
+
open?: boolean | undefined;
|
|
14
|
+
onOpenChange?: ((open: boolean) => void) | undefined;
|
|
15
|
+
disabled?: boolean | undefined;
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function Select({
|
|
20
|
+
value,
|
|
21
|
+
onValueChange,
|
|
22
|
+
open: controlledOpen,
|
|
23
|
+
onOpenChange,
|
|
24
|
+
disabled,
|
|
25
|
+
children,
|
|
26
|
+
}: SelectProps) {
|
|
27
|
+
const [internalOpen, setInternalOpen] = useState(false);
|
|
28
|
+
const open = controlledOpen ?? internalOpen;
|
|
29
|
+
const setOpen = useCallback(
|
|
30
|
+
(next: boolean) => {
|
|
31
|
+
if (disabled && next) return;
|
|
32
|
+
onOpenChange?.(next);
|
|
33
|
+
setInternalOpen(next);
|
|
34
|
+
},
|
|
35
|
+
[disabled, onOpenChange],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
|
39
|
+
const setTriggerRef = useCallback((el: HTMLButtonElement | null) => {
|
|
40
|
+
triggerRef.current = el;
|
|
41
|
+
}, []);
|
|
42
|
+
const [selectedLabel, setSelectedLabel] = useState<{ value: string; label: string } | undefined>();
|
|
43
|
+
|
|
44
|
+
const handleValueChange = useCallback(
|
|
45
|
+
(v: string) => {
|
|
46
|
+
onValueChange?.(v);
|
|
47
|
+
setOpen(false);
|
|
48
|
+
},
|
|
49
|
+
[onValueChange, setOpen],
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const getLabelForValue = useCallback(
|
|
53
|
+
(nextValue: string | undefined) => findSelectItemLabel(children, nextValue),
|
|
54
|
+
[children],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<SelectContext.Provider
|
|
59
|
+
value={{
|
|
60
|
+
value,
|
|
61
|
+
onValueChange: handleValueChange,
|
|
62
|
+
open,
|
|
63
|
+
setOpen,
|
|
64
|
+
disabled: !!disabled,
|
|
65
|
+
triggerRef,
|
|
66
|
+
setTriggerRef,
|
|
67
|
+
selectedLabel,
|
|
68
|
+
setSelectedLabel,
|
|
69
|
+
getLabelForValue,
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
<div className="relative inline-block">{children}</div>
|
|
73
|
+
</SelectContext.Provider>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SelectTrigger — combobox button with chevron, displays the selected label.
|
|
5
|
+
*/
|
|
6
|
+
import {
|
|
7
|
+
forwardRef,
|
|
8
|
+
useCallback,
|
|
9
|
+
type HTMLAttributes,
|
|
10
|
+
type KeyboardEvent,
|
|
11
|
+
type MutableRefObject,
|
|
12
|
+
} from 'react';
|
|
13
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
14
|
+
import { cn } from '../internal/cn.js';
|
|
15
|
+
import { useSelectContext } from './context.js';
|
|
16
|
+
|
|
17
|
+
export const selectTriggerVariants = cva(
|
|
18
|
+
'inline-flex w-full items-center justify-between gap-2 rounded-lg border bg-card text-foreground shadow-mi-input ' +
|
|
19
|
+
'transition-colors duration-150 ' +
|
|
20
|
+
'focus-visible:outline-none focus-visible:border-primary ' +
|
|
21
|
+
'disabled:pointer-events-none disabled:opacity-50 ' +
|
|
22
|
+
'select-none whitespace-nowrap',
|
|
23
|
+
{
|
|
24
|
+
variants: {
|
|
25
|
+
variant: {
|
|
26
|
+
default: 'border-border',
|
|
27
|
+
destructive:
|
|
28
|
+
'border-destructive text-destructive focus-visible:border-destructive',
|
|
29
|
+
},
|
|
30
|
+
size: {
|
|
31
|
+
sm: 'h-9 px-3 text-body-sm',
|
|
32
|
+
md: 'h-10 px-3.5 text-body-sm',
|
|
33
|
+
lg: 'h-12 px-4 text-body-sm',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
defaultVariants: {
|
|
37
|
+
variant: 'default',
|
|
38
|
+
size: 'md',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
export interface SelectTriggerProps
|
|
44
|
+
extends Omit<HTMLAttributes<HTMLButtonElement>, 'children'>,
|
|
45
|
+
VariantProps<typeof selectTriggerVariants> {
|
|
46
|
+
placeholder?: string | undefined;
|
|
47
|
+
disabled?: boolean | undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const SelectTrigger = forwardRef<HTMLButtonElement, SelectTriggerProps>(
|
|
51
|
+
function SelectTrigger({ className, variant, size, placeholder, disabled, id, ...props }, ref) {
|
|
52
|
+
const ctx = useSelectContext();
|
|
53
|
+
const isDisabled = disabled ?? ctx.disabled;
|
|
54
|
+
const listboxId = id ? `${id}-listbox` : undefined;
|
|
55
|
+
|
|
56
|
+
const handleClick = useCallback(() => {
|
|
57
|
+
ctx.setOpen(!ctx.open);
|
|
58
|
+
}, [ctx]);
|
|
59
|
+
|
|
60
|
+
const handleKeyDown = useCallback(
|
|
61
|
+
(e: KeyboardEvent) => {
|
|
62
|
+
if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
if (!ctx.open) ctx.setOpen(true);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
[ctx],
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const setRef = useCallback(
|
|
71
|
+
(el: HTMLButtonElement | null) => {
|
|
72
|
+
ctx.setTriggerRef(el);
|
|
73
|
+
if (typeof ref === 'function') ref(el);
|
|
74
|
+
else if (ref) (ref as MutableRefObject<HTMLButtonElement | null>).current = el;
|
|
75
|
+
},
|
|
76
|
+
[ctx, ref],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const selectedLabel = ctx.selectedLabel;
|
|
80
|
+
const hasValue = ctx.value !== undefined && ctx.value !== '';
|
|
81
|
+
const displayText =
|
|
82
|
+
hasValue && selectedLabel && selectedLabel.value === ctx.value
|
|
83
|
+
? selectedLabel.label
|
|
84
|
+
: hasValue
|
|
85
|
+
? ctx.getLabelForValue(ctx.value) ?? ctx.value
|
|
86
|
+
: undefined;
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<button
|
|
90
|
+
ref={setRef}
|
|
91
|
+
type="button"
|
|
92
|
+
id={id}
|
|
93
|
+
role="combobox"
|
|
94
|
+
aria-expanded={ctx.open}
|
|
95
|
+
aria-haspopup="listbox"
|
|
96
|
+
aria-controls={ctx.open ? listboxId : undefined}
|
|
97
|
+
disabled={isDisabled}
|
|
98
|
+
onClick={handleClick}
|
|
99
|
+
onKeyDown={handleKeyDown}
|
|
100
|
+
className={cn(selectTriggerVariants({ variant, size }), className)}
|
|
101
|
+
{...props}
|
|
102
|
+
>
|
|
103
|
+
<span className={cn('truncate', !displayText && 'text-muted-foreground')}>
|
|
104
|
+
{displayText ?? placeholder ?? 'Select…'}
|
|
105
|
+
</span>
|
|
106
|
+
<svg
|
|
107
|
+
aria-hidden="true"
|
|
108
|
+
viewBox="0 0 16 16"
|
|
109
|
+
fill="none"
|
|
110
|
+
stroke="currentColor"
|
|
111
|
+
strokeWidth={2}
|
|
112
|
+
className={cn(
|
|
113
|
+
'h-4 w-4 shrink-0 opacity-50 transition-transform duration-200',
|
|
114
|
+
ctx.open && 'rotate-180',
|
|
115
|
+
)}
|
|
116
|
+
>
|
|
117
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M4 6l4 4 4-4" />
|
|
118
|
+
</svg>
|
|
119
|
+
</button>
|
|
120
|
+
);
|
|
121
|
+
},
|
|
122
|
+
);
|