@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,72 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Sidebar context, shared types, constants, and the `useSidebar` hook.
5
+ */
6
+ import {
7
+ createContext,
8
+ useContext,
9
+ useEffect,
10
+ useState,
11
+ } from 'react';
12
+
13
+ export type SidebarSide = 'left' | 'right';
14
+ export type SidebarVariant = 'sidebar' | 'floating' | 'inset';
15
+ export type SidebarCollapsible = 'offcanvas' | 'icon' | 'none';
16
+ export type SidebarPosition = 'contained' | 'fixed';
17
+
18
+ /* ── Constants ─────────────────────────────────────────────────────── */
19
+
20
+ export const SIDEBAR_COOKIE_NAME = 'sidebar_state';
21
+ export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; // 7 days
22
+ export const SIDEBAR_WIDTH = '16rem';
23
+ export const SIDEBAR_WIDTH_ICON = '3rem';
24
+ export const SIDEBAR_WIDTH_MOBILE = '18rem';
25
+ export const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
26
+ export const SIDEBAR_MOBILE_BREAKPOINT = 768;
27
+
28
+ /* ── useIsMobile ───────────────────────────────────────────────────── */
29
+
30
+ export function useIsMobile(breakpoint: number) {
31
+ const [isMobile, setIsMobile] = useState(false);
32
+
33
+ useEffect(() => {
34
+ if (typeof window.matchMedia !== 'function') {
35
+ return;
36
+ }
37
+
38
+ const mql = window.matchMedia(`(max-width: ${breakpoint}px)`);
39
+ const onChange = () => setIsMobile(mql.matches);
40
+ onChange();
41
+ mql.addEventListener('change', onChange);
42
+ return () => mql.removeEventListener('change', onChange);
43
+ }, [breakpoint]);
44
+
45
+ return isMobile;
46
+ }
47
+
48
+ /* ── Context ───────────────────────────────────────────────────────── */
49
+
50
+ export interface SidebarContextValue {
51
+ state: 'expanded' | 'collapsed';
52
+ open: boolean;
53
+ setOpen: (open: boolean | ((prev: boolean) => boolean)) => void;
54
+ openMobile: boolean;
55
+ setOpenMobile: (open: boolean | ((prev: boolean) => boolean)) => void;
56
+ isMobile: boolean;
57
+ side: SidebarSide;
58
+ variant: SidebarVariant;
59
+ collapsible: SidebarCollapsible;
60
+ position: SidebarPosition;
61
+ toggleSidebar: () => void;
62
+ }
63
+
64
+ export const SidebarContext = createContext<SidebarContextValue | null>(null);
65
+
66
+ export function useSidebar() {
67
+ const ctx = useContext(SidebarContext);
68
+ if (!ctx) {
69
+ throw new Error('useSidebar must be used within a <SidebarProvider>.');
70
+ }
71
+ return ctx;
72
+ }
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Group sections: SidebarGroup, SidebarGroupLabel, SidebarGroupAction, SidebarGroupContent.
5
+ */
6
+ import { forwardRef, type ButtonHTMLAttributes, type HTMLAttributes } from 'react';
7
+ import { cn } from '../internal/cn.js';
8
+
9
+ export const SidebarGroup = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
10
+ function SidebarGroup({ className, ...props }, ref) {
11
+ return (
12
+ <div
13
+ ref={ref}
14
+ data-sidebar="group"
15
+ className={cn('relative flex w-full min-w-0 flex-col p-2', className)}
16
+ {...props}
17
+ />
18
+ );
19
+ },
20
+ );
21
+
22
+ export const SidebarGroupLabel = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement> & { asChild?: boolean }>(
23
+ function SidebarGroupLabel({ className, asChild: _asChild, ...props }, ref) {
24
+ return (
25
+ <div
26
+ ref={ref}
27
+ data-sidebar="group-label"
28
+ className={cn(
29
+ 'flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2',
30
+ 'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
31
+ className,
32
+ )}
33
+ {...props}
34
+ />
35
+ );
36
+ },
37
+ );
38
+
39
+ export const SidebarGroupAction = forwardRef<HTMLButtonElement, ButtonHTMLAttributes<HTMLButtonElement> & { asChild?: boolean }>(
40
+ function SidebarGroupAction({ className, asChild: _asChild, ...props }, ref) {
41
+ return (
42
+ <button
43
+ ref={ref}
44
+ type="button"
45
+ data-sidebar="group-action"
46
+ className={cn(
47
+ 'absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
48
+ 'after:absolute after:-inset-2 after:md:hidden',
49
+ 'group-data-[collapsible=icon]:hidden',
50
+ className,
51
+ )}
52
+ {...props}
53
+ />
54
+ );
55
+ },
56
+ );
57
+
58
+ export const SidebarGroupContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
59
+ function SidebarGroupContent({ className, ...props }, ref) {
60
+ return (
61
+ <div
62
+ ref={ref}
63
+ data-sidebar="group-content"
64
+ className={cn('w-full text-sm', className)}
65
+ {...props}
66
+ />
67
+ );
68
+ },
69
+ );
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Inline trigger icons for the Sidebar. Kept local so the `@mihcm/ui`
3
+ * package doesn't pull in lucide-react just for two glyphs.
4
+ */
5
+
6
+ export function PanelLeftIcon() {
7
+ return (
8
+ <svg
9
+ aria-hidden="true"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ viewBox="0 0 24 24"
12
+ fill="none"
13
+ stroke="currentColor"
14
+ strokeWidth={2}
15
+ strokeLinecap="round"
16
+ strokeLinejoin="round"
17
+ className="h-4 w-4"
18
+ >
19
+ <rect width="18" height="18" x="3" y="3" rx="2" />
20
+ <path d="M9 3v18" />
21
+ </svg>
22
+ );
23
+ }
24
+
25
+ export function PanelRightIcon() {
26
+ return (
27
+ <svg
28
+ aria-hidden="true"
29
+ xmlns="http://www.w3.org/2000/svg"
30
+ viewBox="0 0 24 24"
31
+ fill="none"
32
+ stroke="currentColor"
33
+ strokeWidth={2}
34
+ strokeLinecap="round"
35
+ strokeLinejoin="round"
36
+ className="h-4 w-4"
37
+ >
38
+ <rect width="18" height="18" x="3" y="3" rx="2" />
39
+ <path d="M15 3v18" />
40
+ </svg>
41
+ );
42
+ }
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Layout sections: SidebarHeader, SidebarFooter, SidebarContent, SidebarSeparator.
5
+ */
6
+ import { forwardRef, type ComponentProps, type HTMLAttributes } from 'react';
7
+ import { cn } from '../internal/cn.js';
8
+ import { Separator } from '../Separator.js';
9
+
10
+ export const SidebarHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
11
+ function SidebarHeader({ className, ...props }, ref) {
12
+ return (
13
+ <div
14
+ ref={ref}
15
+ data-sidebar="header"
16
+ className={cn('flex flex-col gap-2 p-2', className)}
17
+ {...props}
18
+ />
19
+ );
20
+ },
21
+ );
22
+
23
+ export const SidebarFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
24
+ function SidebarFooter({ className, ...props }, ref) {
25
+ return (
26
+ <div
27
+ ref={ref}
28
+ data-sidebar="footer"
29
+ className={cn('flex flex-col gap-2 p-2', className)}
30
+ {...props}
31
+ />
32
+ );
33
+ },
34
+ );
35
+
36
+ export const SidebarContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
37
+ function SidebarContent({ className, ...props }, ref) {
38
+ return (
39
+ <div
40
+ ref={ref}
41
+ data-sidebar="content"
42
+ className={cn(
43
+ 'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
44
+ className,
45
+ )}
46
+ {...props}
47
+ />
48
+ );
49
+ },
50
+ );
51
+
52
+ export const SidebarSeparator = forwardRef<
53
+ HTMLDivElement,
54
+ ComponentProps<typeof Separator>
55
+ >(function SidebarSeparator({ className, ...props }, ref) {
56
+ return (
57
+ <Separator
58
+ ref={ref}
59
+ data-sidebar="separator"
60
+ className={cn('mx-2 w-auto bg-sidebar-border', className)}
61
+ {...props}
62
+ />
63
+ );
64
+ });
@@ -0,0 +1,171 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Menu list and item: SidebarMenu, SidebarMenuItem, SidebarMenuButton (+ CVA),
5
+ * SidebarMenuAction, SidebarMenuBadge, SidebarMenuSkeleton.
6
+ */
7
+ import {
8
+ forwardRef,
9
+ type ButtonHTMLAttributes,
10
+ type ComponentProps,
11
+ type HTMLAttributes,
12
+ } from 'react';
13
+ import { cva, type VariantProps } from 'class-variance-authority';
14
+ import { cn } from '../internal/cn.js';
15
+ import { Skeleton } from '../Skeleton.js';
16
+ import { Tooltip } from '../Tooltip.js';
17
+ import { useSidebar } from './context.js';
18
+
19
+ export const SidebarMenu = forwardRef<HTMLUListElement, HTMLAttributes<HTMLUListElement>>(
20
+ function SidebarMenu({ className, ...props }, ref) {
21
+ return (
22
+ <ul
23
+ ref={ref}
24
+ data-sidebar="menu"
25
+ className={cn('flex w-full min-w-0 flex-col gap-1', className)}
26
+ {...props}
27
+ />
28
+ );
29
+ },
30
+ );
31
+
32
+ export const SidebarMenuItem = forwardRef<HTMLLIElement, HTMLAttributes<HTMLLIElement>>(
33
+ function SidebarMenuItem({ className, ...props }, ref) {
34
+ return (
35
+ <li
36
+ ref={ref}
37
+ data-sidebar="menu-item"
38
+ className={cn('group/menu-item relative', className)}
39
+ {...props}
40
+ />
41
+ );
42
+ },
43
+ );
44
+
45
+ export const sidebarMenuButtonVariants = cva(
46
+ 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
47
+ {
48
+ variants: {
49
+ variant: {
50
+ default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
51
+ outline:
52
+ 'bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]',
53
+ },
54
+ size: {
55
+ default: 'h-8 text-sm',
56
+ sm: 'h-7 text-xs',
57
+ lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',
58
+ },
59
+ },
60
+ defaultVariants: {
61
+ variant: 'default',
62
+ size: 'default',
63
+ },
64
+ },
65
+ );
66
+
67
+ export interface SidebarMenuButtonProps
68
+ extends ButtonHTMLAttributes<HTMLButtonElement>,
69
+ VariantProps<typeof sidebarMenuButtonVariants> {
70
+ asChild?: boolean;
71
+ isActive?: boolean;
72
+ tooltip?: string | ComponentProps<typeof Tooltip>;
73
+ }
74
+
75
+ export const SidebarMenuButton = forwardRef<HTMLButtonElement, SidebarMenuButtonProps>(
76
+ function SidebarMenuButton(
77
+ { asChild: _asChild, isActive = false, variant, size, tooltip, className, ...props },
78
+ ref,
79
+ ) {
80
+ useSidebar();
81
+
82
+ const button = (
83
+ <button
84
+ ref={ref}
85
+ type="button"
86
+ data-sidebar="menu-button"
87
+ data-size={size}
88
+ data-active={isActive}
89
+ className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
90
+ {...props}
91
+ />
92
+ );
93
+
94
+ if (!tooltip) return button;
95
+
96
+ const tooltipProps: ComponentProps<typeof Tooltip> =
97
+ typeof tooltip === 'string' ? { content: tooltip } : tooltip;
98
+
99
+ return (
100
+ <Tooltip {...tooltipProps} side="right">
101
+ {button}
102
+ </Tooltip>
103
+ );
104
+ },
105
+ );
106
+
107
+ export const SidebarMenuAction = forwardRef<
108
+ HTMLButtonElement,
109
+ ButtonHTMLAttributes<HTMLButtonElement> & { asChild?: boolean; showOnHover?: boolean }
110
+ >(function SidebarMenuAction({ className, asChild: _asChild, showOnHover = false, ...props }, ref) {
111
+ return (
112
+ <button
113
+ ref={ref}
114
+ type="button"
115
+ data-sidebar="menu-action"
116
+ className={cn(
117
+ 'absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0',
118
+ 'after:absolute after:-inset-2 after:md:hidden',
119
+ 'group-data-[collapsible=icon]:hidden',
120
+ showOnHover &&
121
+ 'peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0',
122
+ className,
123
+ )}
124
+ {...props}
125
+ />
126
+ );
127
+ });
128
+
129
+ export const SidebarMenuBadge = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
130
+ function SidebarMenuBadge({ className, ...props }, ref) {
131
+ return (
132
+ <div
133
+ ref={ref}
134
+ data-sidebar="menu-badge"
135
+ className={cn(
136
+ 'pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground',
137
+ 'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
138
+ 'group-data-[collapsible=icon]:hidden',
139
+ className,
140
+ )}
141
+ {...props}
142
+ />
143
+ );
144
+ },
145
+ );
146
+
147
+ export interface SidebarMenuSkeletonProps extends HTMLAttributes<HTMLDivElement> {
148
+ showIcon?: boolean;
149
+ }
150
+
151
+ export const SidebarMenuSkeleton = forwardRef<HTMLDivElement, SidebarMenuSkeletonProps>(
152
+ function SidebarMenuSkeleton({ className, showIcon = false, ...props }, ref) {
153
+ const width = '70%';
154
+
155
+ return (
156
+ <div
157
+ ref={ref}
158
+ data-sidebar="menu-skeleton"
159
+ className={cn('flex h-8 items-center gap-2 rounded-md px-2', className)}
160
+ {...props}
161
+ >
162
+ {showIcon && <Skeleton className="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />}
163
+ <Skeleton
164
+ className="h-4 max-w-(--skeleton-width) flex-1"
165
+ data-sidebar="menu-skeleton-text"
166
+ style={{ '--skeleton-width': width } as React.CSSProperties}
167
+ />
168
+ </div>
169
+ );
170
+ },
171
+ );
@@ -0,0 +1,224 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * SidebarProvider — owns sidebar state, cookie persistence, keyboard shortcut,
5
+ * and feeds context values to descendant Sidebar pieces.
6
+ */
7
+ import {
8
+ forwardRef,
9
+ useCallback,
10
+ useEffect,
11
+ useMemo,
12
+ useState,
13
+ type HTMLAttributes,
14
+ } from 'react';
15
+ import { cn } from '../internal/cn.js';
16
+ import {
17
+ SIDEBAR_COOKIE_MAX_AGE,
18
+ SIDEBAR_COOKIE_NAME,
19
+ SIDEBAR_KEYBOARD_SHORTCUT,
20
+ SIDEBAR_MOBILE_BREAKPOINT,
21
+ SIDEBAR_WIDTH,
22
+ SIDEBAR_WIDTH_ICON,
23
+ SIDEBAR_WIDTH_MOBILE,
24
+ SidebarContext,
25
+ useIsMobile,
26
+ type SidebarCollapsible,
27
+ type SidebarContextValue,
28
+ type SidebarPosition,
29
+ type SidebarSide,
30
+ type SidebarVariant,
31
+ } from './context.js';
32
+
33
+ export interface SidebarProviderProps extends HTMLAttributes<HTMLDivElement> {
34
+ defaultOpen?: boolean;
35
+ open?: boolean;
36
+ onOpenChange?: (open: boolean) => void;
37
+ /** Initial mobile Sheet open state. Useful for examples and forced mobile previews. */
38
+ defaultOpenMobile?: boolean;
39
+ /** Controlled mobile Sheet open state. */
40
+ openMobile?: boolean;
41
+ /** Called when mobile Sheet open state changes. */
42
+ onOpenMobileChange?: (open: boolean) => void;
43
+ side?: SidebarSide;
44
+ variant?: SidebarVariant;
45
+ collapsible?: SidebarCollapsible;
46
+ position?: SidebarPosition;
47
+ /** Force mobile behavior for previews/tests. When omitted, matchMedia decides. */
48
+ mobile?: boolean;
49
+ /** Breakpoint in px where the sidebar switches to mobile Sheet mode. */
50
+ mobileBreakpoint?: number;
51
+ /** Expanded desktop width. */
52
+ width?: string;
53
+ /** Collapsed icon-rail width. */
54
+ iconWidth?: string;
55
+ /** Mobile Sheet width. */
56
+ mobileWidth?: string;
57
+ }
58
+
59
+ export const SidebarProvider = forwardRef<HTMLDivElement, SidebarProviderProps>(
60
+ function SidebarProvider(
61
+ {
62
+ defaultOpen = true,
63
+ open: openProp,
64
+ onOpenChange: setOpenProp,
65
+ defaultOpenMobile = false,
66
+ openMobile: openMobileProp,
67
+ onOpenMobileChange: setOpenMobileProp,
68
+ side = 'left',
69
+ variant = 'sidebar',
70
+ collapsible = 'offcanvas',
71
+ position = 'contained',
72
+ mobile,
73
+ mobileBreakpoint = SIDEBAR_MOBILE_BREAKPOINT,
74
+ width = SIDEBAR_WIDTH,
75
+ iconWidth = SIDEBAR_WIDTH_ICON,
76
+ mobileWidth = SIDEBAR_WIDTH_MOBILE,
77
+ className,
78
+ style,
79
+ children,
80
+ ...props
81
+ },
82
+ ref,
83
+ ) {
84
+ const detectedMobile = useIsMobile(mobileBreakpoint);
85
+ const isMobile = mobile ?? detectedMobile;
86
+ const [_openMobile, _setOpenMobile] = useState(defaultOpenMobile);
87
+
88
+ const [_open, _setOpen] = useState(defaultOpen);
89
+
90
+ const open = openProp ?? _open;
91
+ const openMobile = openMobileProp ?? _openMobile;
92
+
93
+ useEffect(() => {
94
+ if (openProp !== undefined) return;
95
+
96
+ const cookie = document.cookie
97
+ .split('; ')
98
+ .find((row) => row.startsWith(`${SIDEBAR_COOKIE_NAME}=`));
99
+ if (!cookie) return;
100
+
101
+ const nextOpen = cookie.split('=')[1] === 'true';
102
+ let cancelled = false;
103
+ queueMicrotask(() => {
104
+ if (!cancelled) {
105
+ _setOpen(nextOpen);
106
+ }
107
+ });
108
+
109
+ return () => {
110
+ cancelled = true;
111
+ };
112
+ }, [openProp]);
113
+
114
+ const setOpen = useCallback(
115
+ (value: boolean | ((prev: boolean) => boolean)) => {
116
+ const next = typeof value === 'function' ? value(open) : value;
117
+ if (setOpenProp) {
118
+ setOpenProp(next);
119
+ } else {
120
+ _setOpen(next);
121
+ }
122
+ if (typeof document !== 'undefined') {
123
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${next}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
124
+ }
125
+ },
126
+ [setOpenProp, open],
127
+ );
128
+
129
+ const setOpenMobile = useCallback(
130
+ (value: boolean | ((prev: boolean) => boolean)) => {
131
+ const next = typeof value === 'function' ? value(openMobile) : value;
132
+ if (setOpenMobileProp) {
133
+ setOpenMobileProp(next);
134
+ } else {
135
+ _setOpenMobile(next);
136
+ }
137
+ },
138
+ [openMobile, setOpenMobileProp],
139
+ );
140
+
141
+ const toggleSidebar = useCallback(() => {
142
+ if (collapsible === 'none') return;
143
+
144
+ if (isMobile) {
145
+ setOpenMobile((prev) => !prev);
146
+ } else {
147
+ setOpen((prev) => !prev);
148
+ }
149
+ }, [collapsible, isMobile, setOpen, setOpenMobile]);
150
+
151
+ useEffect(() => {
152
+ const handleKeyDown = (e: KeyboardEvent) => {
153
+ if (e.key === SIDEBAR_KEYBOARD_SHORTCUT && (e.metaKey || e.ctrlKey)) {
154
+ e.preventDefault();
155
+ toggleSidebar();
156
+ }
157
+ };
158
+ window.addEventListener('keydown', handleKeyDown);
159
+ return () => window.removeEventListener('keydown', handleKeyDown);
160
+ }, [toggleSidebar]);
161
+
162
+ const state = open ? 'expanded' : 'collapsed';
163
+
164
+ const value = useMemo<SidebarContextValue>(
165
+ () => ({
166
+ state,
167
+ open,
168
+ setOpen,
169
+ isMobile,
170
+ openMobile,
171
+ setOpenMobile,
172
+ side,
173
+ variant,
174
+ collapsible,
175
+ position,
176
+ toggleSidebar,
177
+ }),
178
+ [
179
+ state,
180
+ open,
181
+ setOpen,
182
+ isMobile,
183
+ openMobile,
184
+ setOpenMobile,
185
+ side,
186
+ variant,
187
+ collapsible,
188
+ position,
189
+ toggleSidebar,
190
+ ],
191
+ );
192
+
193
+ return (
194
+ <SidebarContext.Provider value={value}>
195
+ <div
196
+ ref={ref}
197
+ style={
198
+ {
199
+ '--sidebar-width': width,
200
+ '--sidebar-width-icon': iconWidth,
201
+ '--sidebar-width-mobile': mobileWidth,
202
+ ...style,
203
+ } as React.CSSProperties
204
+ }
205
+ className={cn(
206
+ 'group/sidebar-wrapper flex min-h-svh w-full',
207
+ side === 'right' && 'flex-row-reverse',
208
+ variant === 'inset' && 'bg-sidebar',
209
+ className,
210
+ )}
211
+ data-sidebar="provider"
212
+ data-state={state}
213
+ data-collapsible={state === 'collapsed' ? collapsible : ''}
214
+ data-variant={variant}
215
+ data-side={side}
216
+ data-position={position}
217
+ {...props}
218
+ >
219
+ {children}
220
+ </div>
221
+ </SidebarContext.Provider>
222
+ );
223
+ },
224
+ );