@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.
Files changed (300) hide show
  1. package/dist/CheckboxGrid.native.d.ts.map +1 -1
  2. package/dist/CheckboxGrid.native.js +2 -1
  3. package/dist/CheckboxGrid.native.js.map +1 -1
  4. package/dist/Combobox.native.d.ts.map +1 -1
  5. package/dist/Combobox.native.js +2 -1
  6. package/dist/Combobox.native.js.map +1 -1
  7. package/dist/DataTable/column-filter.d.ts +8 -0
  8. package/dist/DataTable/column-filter.d.ts.map +1 -0
  9. package/dist/DataTable/column-filter.js +67 -0
  10. package/dist/DataTable/column-filter.js.map +1 -0
  11. package/dist/DataTable/column-header.d.ts +16 -0
  12. package/dist/DataTable/column-header.d.ts.map +1 -0
  13. package/dist/DataTable/column-header.js +11 -0
  14. package/dist/DataTable/column-header.js.map +1 -0
  15. package/dist/DataTable/column-visibility.d.ts +7 -0
  16. package/dist/DataTable/column-visibility.d.ts.map +1 -0
  17. package/dist/DataTable/column-visibility.js +35 -0
  18. package/dist/DataTable/column-visibility.js.map +1 -0
  19. package/dist/DataTable/index.d.ts +5 -0
  20. package/dist/DataTable/index.d.ts.map +1 -0
  21. package/dist/DataTable/index.js +5 -0
  22. package/dist/DataTable/index.js.map +1 -0
  23. package/dist/DataTable/pinning.d.ts +13 -0
  24. package/dist/DataTable/pinning.d.ts.map +1 -0
  25. package/dist/DataTable/pinning.js +29 -0
  26. package/dist/DataTable/pinning.js.map +1 -0
  27. package/dist/DataTable.d.ts +3 -7
  28. package/dist/DataTable.d.ts.map +1 -1
  29. package/dist/DataTable.js +7 -126
  30. package/dist/DataTable.js.map +1 -1
  31. package/dist/Dialog.native.d.ts +3 -1
  32. package/dist/Dialog.native.d.ts.map +1 -1
  33. package/dist/Dialog.native.js +2 -2
  34. package/dist/Dialog.native.js.map +1 -1
  35. package/dist/Form/building-blocks.d.ts +26 -0
  36. package/dist/Form/building-blocks.d.ts.map +1 -0
  37. package/dist/Form/building-blocks.js +29 -0
  38. package/dist/Form/building-blocks.js.map +1 -0
  39. package/dist/Form/fields-choice.d.ts +72 -0
  40. package/dist/Form/fields-choice.d.ts.map +1 -0
  41. package/dist/Form/fields-choice.js +69 -0
  42. package/dist/Form/fields-choice.js.map +1 -0
  43. package/dist/Form/fields-complex.d.ts +28 -0
  44. package/dist/Form/fields-complex.d.ts.map +1 -0
  45. package/dist/Form/fields-complex.js +38 -0
  46. package/dist/Form/fields-complex.js.map +1 -0
  47. package/dist/Form/fields-date.d.ts +46 -0
  48. package/dist/Form/fields-date.d.ts.map +1 -0
  49. package/dist/Form/fields-date.js +41 -0
  50. package/dist/Form/fields-date.js.map +1 -0
  51. package/dist/Form/fields-text.d.ts +47 -0
  52. package/dist/Form/fields-text.d.ts.map +1 -0
  53. package/dist/Form/fields-text.js +46 -0
  54. package/dist/Form/fields-text.js.map +1 -0
  55. package/dist/Form/fields-toggle.d.ts +24 -0
  56. package/dist/Form/fields-toggle.d.ts.map +1 -0
  57. package/dist/Form/fields-toggle.js +32 -0
  58. package/dist/Form/fields-toggle.js.map +1 -0
  59. package/dist/Form/helpers.d.ts +66 -0
  60. package/dist/Form/helpers.d.ts.map +1 -0
  61. package/dist/Form/helpers.js +44 -0
  62. package/dist/Form/helpers.js.map +1 -0
  63. package/dist/Form/types.d.ts +25 -0
  64. package/dist/Form/types.d.ts.map +1 -0
  65. package/dist/Form/types.js +8 -0
  66. package/dist/Form/types.js.map +1 -0
  67. package/dist/Form.d.ts +24 -298
  68. package/dist/Form.d.ts.map +1 -1
  69. package/dist/Form.js +30 -246
  70. package/dist/Form.js.map +1 -1
  71. package/dist/IconSidebar.d.ts +6 -46
  72. package/dist/IconSidebar.d.ts.map +1 -1
  73. package/dist/IconSidebar.js +6 -116
  74. package/dist/IconSidebar.js.map +1 -1
  75. package/dist/MainSidebar/back-button.d.ts +14 -0
  76. package/dist/MainSidebar/back-button.d.ts.map +1 -0
  77. package/dist/MainSidebar/back-button.js +14 -0
  78. package/dist/MainSidebar/back-button.js.map +1 -0
  79. package/dist/MainSidebar/breadcrumb.d.ts +10 -0
  80. package/dist/MainSidebar/breadcrumb.d.ts.map +1 -0
  81. package/dist/MainSidebar/breadcrumb.js +24 -0
  82. package/dist/MainSidebar/breadcrumb.js.map +1 -0
  83. package/dist/MainSidebar/columns.d.ts +3 -0
  84. package/dist/MainSidebar/columns.d.ts.map +1 -0
  85. package/dist/MainSidebar/columns.js +198 -0
  86. package/dist/MainSidebar/columns.js.map +1 -0
  87. package/dist/MainSidebar/command.d.ts +3 -0
  88. package/dist/MainSidebar/command.d.ts.map +1 -0
  89. package/dist/MainSidebar/command.js +193 -0
  90. package/dist/MainSidebar/command.js.map +1 -0
  91. package/dist/MainSidebar/drilldown.d.ts +3 -0
  92. package/dist/MainSidebar/drilldown.d.ts.map +1 -0
  93. package/dist/MainSidebar/drilldown.js +154 -0
  94. package/dist/MainSidebar/drilldown.js.map +1 -0
  95. package/dist/MainSidebar/expanded.d.ts +7 -0
  96. package/dist/MainSidebar/expanded.d.ts.map +1 -0
  97. package/dist/MainSidebar/expanded.js +102 -0
  98. package/dist/MainSidebar/expanded.js.map +1 -0
  99. package/dist/MainSidebar/floating.d.ts +3 -0
  100. package/dist/MainSidebar/floating.d.ts.map +1 -0
  101. package/dist/MainSidebar/floating.js +116 -0
  102. package/dist/MainSidebar/floating.js.map +1 -0
  103. package/dist/MainSidebar/helpers.d.ts +50 -0
  104. package/dist/MainSidebar/helpers.d.ts.map +1 -0
  105. package/dist/MainSidebar/helpers.js +148 -0
  106. package/dist/MainSidebar/helpers.js.map +1 -0
  107. package/dist/MainSidebar/hover.d.ts +3 -0
  108. package/dist/MainSidebar/hover.d.ts.map +1 -0
  109. package/dist/MainSidebar/hover.js +177 -0
  110. package/dist/MainSidebar/hover.js.map +1 -0
  111. package/dist/MainSidebar/index.d.ts +6 -0
  112. package/dist/MainSidebar/index.d.ts.map +1 -0
  113. package/dist/MainSidebar/index.js +108 -0
  114. package/dist/MainSidebar/index.js.map +1 -0
  115. package/dist/MainSidebar/mobile.d.ts +29 -0
  116. package/dist/MainSidebar/mobile.d.ts.map +1 -0
  117. package/dist/MainSidebar/mobile.js +38 -0
  118. package/dist/MainSidebar/mobile.js.map +1 -0
  119. package/dist/MainSidebar/motion.d.ts +23 -0
  120. package/dist/MainSidebar/motion.d.ts.map +1 -0
  121. package/dist/MainSidebar/motion.js +40 -0
  122. package/dist/MainSidebar/motion.js.map +1 -0
  123. package/dist/MainSidebar/rail.d.ts +24 -0
  124. package/dist/MainSidebar/rail.d.ts.map +1 -0
  125. package/dist/MainSidebar/rail.js +29 -0
  126. package/dist/MainSidebar/rail.js.map +1 -0
  127. package/dist/MainSidebar/search.d.ts +19 -0
  128. package/dist/MainSidebar/search.d.ts.map +1 -0
  129. package/dist/MainSidebar/search.js +33 -0
  130. package/dist/MainSidebar/search.js.map +1 -0
  131. package/dist/MainSidebar/types.d.ts +161 -0
  132. package/dist/MainSidebar/types.d.ts.map +1 -0
  133. package/dist/MainSidebar/types.js +2 -0
  134. package/dist/MainSidebar/types.js.map +1 -0
  135. package/dist/MainSidebar.d.ts +6 -1
  136. package/dist/MainSidebar.d.ts.map +1 -1
  137. package/dist/MainSidebar.js +6 -1
  138. package/dist/MainSidebar.js.map +1 -1
  139. package/dist/NavigationMenu.js +1 -1
  140. package/dist/NavigationMenu.js.map +1 -1
  141. package/dist/RichTextEditor/theme.d.ts +44 -0
  142. package/dist/RichTextEditor/theme.d.ts.map +1 -0
  143. package/dist/RichTextEditor/theme.js +41 -0
  144. package/dist/RichTextEditor/theme.js.map +1 -0
  145. package/dist/RichTextEditor/toolbar-icons.d.ts +21 -0
  146. package/dist/RichTextEditor/toolbar-icons.d.ts.map +1 -0
  147. package/dist/RichTextEditor/toolbar-icons.js +21 -0
  148. package/dist/RichTextEditor/toolbar-icons.js.map +1 -0
  149. package/dist/RichTextEditor/toolbar.d.ts +5 -0
  150. package/dist/RichTextEditor/toolbar.d.ts.map +1 -0
  151. package/dist/RichTextEditor/toolbar.js +116 -0
  152. package/dist/RichTextEditor/toolbar.js.map +1 -0
  153. package/dist/RichTextEditor.d.ts +16 -9
  154. package/dist/RichTextEditor.d.ts.map +1 -1
  155. package/dist/RichTextEditor.js +18 -164
  156. package/dist/RichTextEditor.js.map +1 -1
  157. package/dist/Select/content.d.ts +9 -0
  158. package/dist/Select/content.d.ts.map +1 -0
  159. package/dist/Select/content.js +80 -0
  160. package/dist/Select/content.js.map +1 -0
  161. package/dist/Select/context.d.ts +27 -0
  162. package/dist/Select/context.d.ts.map +1 -0
  163. package/dist/Select/context.js +35 -0
  164. package/dist/Select/context.js.map +1 -0
  165. package/dist/Select/item.d.ts +13 -0
  166. package/dist/Select/item.d.ts.map +1 -0
  167. package/dist/Select/item.js +39 -0
  168. package/dist/Select/item.js.map +1 -0
  169. package/dist/Select/parts.d.ts +14 -0
  170. package/dist/Select/parts.d.ts.map +1 -0
  171. package/dist/Select/parts.js +17 -0
  172. package/dist/Select/parts.js.map +1 -0
  173. package/dist/Select/react-select.d.ts +25 -0
  174. package/dist/Select/react-select.d.ts.map +1 -0
  175. package/dist/Select/react-select.js +66 -0
  176. package/dist/Select/react-select.js.map +1 -0
  177. package/dist/Select/root.d.ts +15 -0
  178. package/dist/Select/root.d.ts.map +1 -0
  179. package/dist/Select/root.js +41 -0
  180. package/dist/Select/root.js.map +1 -0
  181. package/dist/Select/trigger.d.ts +15 -0
  182. package/dist/Select/trigger.d.ts.map +1 -0
  183. package/dist/Select/trigger.js +61 -0
  184. package/dist/Select/trigger.js.map +1 -0
  185. package/dist/Select.d.ts +14 -62
  186. package/dist/Select.d.ts.map +1 -1
  187. package/dist/Select.js +14 -293
  188. package/dist/Select.js.map +1 -1
  189. package/dist/Sidebar/context.d.ts +28 -0
  190. package/dist/Sidebar/context.d.ts.map +1 -0
  191. package/dist/Sidebar/context.js +37 -0
  192. package/dist/Sidebar/context.js.map +1 -0
  193. package/dist/Sidebar/group.d.ts +13 -0
  194. package/dist/Sidebar/group.d.ts.map +1 -0
  195. package/dist/Sidebar/group.js +20 -0
  196. package/dist/Sidebar/group.js.map +1 -0
  197. package/dist/Sidebar/icons.d.ts +7 -0
  198. package/dist/Sidebar/icons.d.ts.map +1 -0
  199. package/dist/Sidebar/icons.js +12 -0
  200. package/dist/Sidebar/icons.js.map +1 -0
  201. package/dist/Sidebar/layout.d.ts +9 -0
  202. package/dist/Sidebar/layout.d.ts.map +1 -0
  203. package/dist/Sidebar/layout.js +21 -0
  204. package/dist/Sidebar/layout.js.map +1 -0
  205. package/dist/Sidebar/menu.d.ts +29 -0
  206. package/dist/Sidebar/menu.d.ts.map +1 -0
  207. package/dist/Sidebar/menu.js +55 -0
  208. package/dist/Sidebar/menu.js.map +1 -0
  209. package/dist/Sidebar/provider.d.ts +33 -0
  210. package/dist/Sidebar/provider.d.ts.map +1 -0
  211. package/dist/Sidebar/provider.js +110 -0
  212. package/dist/Sidebar/provider.js.map +1 -0
  213. package/dist/Sidebar/sidebar.d.ts +17 -0
  214. package/dist/Sidebar/sidebar.d.ts.map +1 -0
  215. package/dist/Sidebar/sidebar.js +51 -0
  216. package/dist/Sidebar/sidebar.js.map +1 -0
  217. package/dist/Sidebar/submenu.d.ts +13 -0
  218. package/dist/Sidebar/submenu.d.ts.map +1 -0
  219. package/dist/Sidebar/submenu.js +17 -0
  220. package/dist/Sidebar/submenu.js.map +1 -0
  221. package/dist/Sidebar/trigger.d.ts +9 -0
  222. package/dist/Sidebar/trigger.d.ts.map +1 -0
  223. package/dist/Sidebar/trigger.js +33 -0
  224. package/dist/Sidebar/trigger.js.map +1 -0
  225. package/dist/Sidebar.d.ts +14 -104
  226. package/dist/Sidebar.d.ts.map +1 -1
  227. package/dist/Sidebar.js +14 -300
  228. package/dist/Sidebar.js.map +1 -1
  229. package/dist/StatCard.d.ts +67 -9
  230. package/dist/StatCard.d.ts.map +1 -1
  231. package/dist/StatCard.js +111 -9
  232. package/dist/StatCard.js.map +1 -1
  233. package/dist/TransferList.native.d.ts.map +1 -1
  234. package/dist/TransferList.native.js +2 -1
  235. package/dist/TransferList.native.js.map +1 -1
  236. package/package.json +2 -2
  237. package/src/CheckboxGrid.native.tsx +2 -1
  238. package/src/Combobox.native.tsx +2 -1
  239. package/src/DataTable/column-filter.tsx +134 -0
  240. package/src/DataTable/column-header.tsx +67 -0
  241. package/src/DataTable/column-visibility.tsx +87 -0
  242. package/src/DataTable/index.ts +4 -0
  243. package/src/DataTable/pinning.ts +40 -0
  244. package/src/DataTable.tsx +14 -297
  245. package/src/Dialog.native.tsx +4 -2
  246. package/src/Form/building-blocks.tsx +97 -0
  247. package/src/Form/fields-choice.tsx +312 -0
  248. package/src/Form/fields-complex.tsx +195 -0
  249. package/src/Form/fields-date.tsx +195 -0
  250. package/src/Form/fields-text.tsx +218 -0
  251. package/src/Form/fields-toggle.tsx +123 -0
  252. package/src/Form/helpers.tsx +189 -0
  253. package/src/Form/types.ts +26 -0
  254. package/src/Form.tsx +91 -1308
  255. package/src/IconSidebar.tsx +20 -442
  256. package/src/MainSidebar/back-button.tsx +58 -0
  257. package/src/MainSidebar/breadcrumb.tsx +53 -0
  258. package/src/MainSidebar/columns.tsx +350 -0
  259. package/src/MainSidebar/command.tsx +404 -0
  260. package/src/MainSidebar/drilldown.tsx +373 -0
  261. package/src/MainSidebar/expanded.tsx +414 -0
  262. package/src/MainSidebar/floating.tsx +268 -0
  263. package/src/MainSidebar/helpers.ts +164 -0
  264. package/src/MainSidebar/hover.tsx +334 -0
  265. package/src/MainSidebar/index.tsx +191 -0
  266. package/src/MainSidebar/mobile.tsx +117 -0
  267. package/src/MainSidebar/motion.ts +64 -0
  268. package/src/MainSidebar/rail.tsx +137 -0
  269. package/src/MainSidebar/search.tsx +99 -0
  270. package/src/MainSidebar/types.ts +208 -0
  271. package/src/MainSidebar.tsx +15 -4
  272. package/src/NavigationMenu.tsx +1 -1
  273. package/src/RichTextEditor/theme.ts +43 -0
  274. package/src/RichTextEditor/toolbar-icons.tsx +40 -0
  275. package/src/RichTextEditor/toolbar.tsx +271 -0
  276. package/src/RichTextEditor.tsx +23 -371
  277. package/src/Select/content.tsx +111 -0
  278. package/src/Select/context.tsx +66 -0
  279. package/src/Select/item.tsx +97 -0
  280. package/src/Select/parts.tsx +43 -0
  281. package/src/Select/react-select.tsx +216 -0
  282. package/src/Select/root.tsx +75 -0
  283. package/src/Select/trigger.tsx +122 -0
  284. package/src/Select.tsx +34 -692
  285. package/src/Sidebar/context.tsx +72 -0
  286. package/src/Sidebar/group.tsx +69 -0
  287. package/src/Sidebar/icons.tsx +42 -0
  288. package/src/Sidebar/layout.tsx +64 -0
  289. package/src/Sidebar/menu.tsx +171 -0
  290. package/src/Sidebar/provider.tsx +224 -0
  291. package/src/Sidebar/sidebar.tsx +178 -0
  292. package/src/Sidebar/submenu.tsx +58 -0
  293. package/src/Sidebar/trigger.tsx +104 -0
  294. package/src/Sidebar.tsx +44 -927
  295. package/src/StatCard.tsx +365 -20
  296. package/src/TransferList.native.tsx +2 -1
  297. package/dist/TiptapEditor.d.ts +0 -24
  298. package/dist/TiptapEditor.d.ts.map +0 -1
  299. package/dist/TiptapEditor.js +0 -84
  300. 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
+ );