@mihcm/ui 0.14.1 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +150 -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 +166 -0
- package/src/MainSidebar/hover.tsx +334 -0
- package/src/MainSidebar/index.tsx +191 -0
- package/src/MainSidebar/mobile.tsx +117 -0
- package/src/MainSidebar/motion.ts +64 -0
- package/src/MainSidebar/rail.tsx +137 -0
- package/src/MainSidebar/search.tsx +99 -0
- package/src/MainSidebar/types.ts +208 -0
- package/src/MainSidebar.tsx +15 -4
- package/src/NavigationMenu.tsx +1 -1
- package/src/RichTextEditor/theme.ts +43 -0
- package/src/RichTextEditor/toolbar-icons.tsx +40 -0
- package/src/RichTextEditor/toolbar.tsx +271 -0
- package/src/RichTextEditor.tsx +23 -371
- package/src/Select/content.tsx +111 -0
- package/src/Select/context.tsx +66 -0
- package/src/Select/item.tsx +97 -0
- package/src/Select/parts.tsx +43 -0
- package/src/Select/react-select.tsx +216 -0
- package/src/Select/root.tsx +75 -0
- package/src/Select/trigger.tsx +122 -0
- package/src/Select.tsx +34 -692
- package/src/Sidebar/context.tsx +72 -0
- package/src/Sidebar/group.tsx +69 -0
- package/src/Sidebar/icons.tsx +42 -0
- package/src/Sidebar/layout.tsx +64 -0
- package/src/Sidebar/menu.tsx +171 -0
- package/src/Sidebar/provider.tsx +224 -0
- package/src/Sidebar/sidebar.tsx +178 -0
- package/src/Sidebar/submenu.tsx +58 -0
- package/src/Sidebar/trigger.tsx +104 -0
- package/src/Sidebar.tsx +44 -927
- package/src/StatCard.tsx +365 -20
- package/src/TransferList.native.tsx +2 -1
- package/dist/TiptapEditor.d.ts +0 -24
- package/dist/TiptapEditor.d.ts.map +0 -1
- package/dist/TiptapEditor.js +0 -84
- package/dist/TiptapEditor.js.map +0 -1
package/src/IconSidebar.tsx
CHANGED
|
@@ -1,448 +1,26 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* IconSidebar — legacy alias.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* Wiki: docs/components/IconSidebar.md
|
|
6
|
+
* The implementation moved to `@mihcm/ui/MainSidebar` and gained five
|
|
7
|
+
* interaction-model variants. This file remains so existing
|
|
8
|
+
* `import { IconSidebar } from '@mihcm/ui/IconSidebar'` consumers keep
|
|
9
|
+
* working without changes. New code should import from `MainSidebar`.
|
|
12
10
|
*/
|
|
13
|
-
import { forwardRef, useMemo, useState, type HTMLAttributes, type ReactNode } from 'react';
|
|
14
|
-
import { cn } from './internal/cn.js';
|
|
15
|
-
|
|
16
|
-
export interface MainSidebarItem {
|
|
17
|
-
key: string;
|
|
18
|
-
icon: ReactNode;
|
|
19
|
-
label: string;
|
|
20
|
-
href?: string;
|
|
21
|
-
description?: string;
|
|
22
|
-
badge?: ReactNode;
|
|
23
|
-
disabled?: boolean;
|
|
24
|
-
children?: MainSidebarItem[];
|
|
25
|
-
panel?: ReactNode;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface MainSidebarProps extends Omit<HTMLAttributes<HTMLElement>, 'onSelect'> {
|
|
29
|
-
items: MainSidebarItem[];
|
|
30
|
-
activeKey?: string;
|
|
31
|
-
expanded?: boolean;
|
|
32
|
-
defaultExpanded?: boolean;
|
|
33
|
-
onExpandedChange?: (expanded: boolean) => void;
|
|
34
|
-
onItemSelect?: (key: string, item: MainSidebarItem) => void;
|
|
35
|
-
header?: ReactNode;
|
|
36
|
-
footer?: ReactNode;
|
|
37
|
-
panelHeader?: ReactNode;
|
|
38
|
-
panelFooter?: ReactNode;
|
|
39
|
-
railClassName?: string;
|
|
40
|
-
panelClassName?: string;
|
|
41
|
-
itemClassName?: string;
|
|
42
|
-
activeItemClassName?: string;
|
|
43
|
-
collapsedLabel?: string;
|
|
44
|
-
expandedLabel?: string;
|
|
45
|
-
backLabel?: string;
|
|
46
|
-
showLabelsWhenExpanded?: boolean;
|
|
47
|
-
side?: 'left' | 'right';
|
|
48
|
-
/** Force the bottom mobile navigation pattern for docs, tests, or mobile-only shells. */
|
|
49
|
-
mobile?: boolean;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type IconSidebarItem = MainSidebarItem;
|
|
53
|
-
export type IconSidebarProps = MainSidebarProps;
|
|
54
|
-
|
|
55
|
-
function ChevronLeftIcon() {
|
|
56
|
-
return (
|
|
57
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden>
|
|
58
|
-
<path d="m15 18-6-6 6-6" strokeLinecap="round" strokeLinejoin="round" />
|
|
59
|
-
</svg>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function ChevronRightIcon() {
|
|
64
|
-
return (
|
|
65
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden>
|
|
66
|
-
<path d="m9 18 6-6-6-6" strokeLinecap="round" strokeLinejoin="round" />
|
|
67
|
-
</svg>
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function MenuIcon() {
|
|
72
|
-
return (
|
|
73
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden>
|
|
74
|
-
<path d="M4 7h16M4 12h16M4 17h16" strokeLinecap="round" />
|
|
75
|
-
</svg>
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function findItem(items: MainSidebarItem[], key?: string): MainSidebarItem | undefined {
|
|
80
|
-
if (!key) return undefined;
|
|
81
|
-
|
|
82
|
-
for (const item of items) {
|
|
83
|
-
if (item.key === key) return item;
|
|
84
|
-
const child = findItem(item.children ?? [], key);
|
|
85
|
-
if (child) return child;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function firstPanelItem(items: MainSidebarItem[], activeKey?: string) {
|
|
92
|
-
const active = findItem(items, activeKey);
|
|
93
|
-
if (active?.children?.length || active?.panel) return active;
|
|
94
|
-
return items.find((item) => item.children?.length || item.panel) ?? items[0];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function opensPanel(item: MainSidebarItem) {
|
|
98
|
-
return Boolean(item.children?.length || item.panel);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export const MainSidebar = forwardRef<HTMLElement, MainSidebarProps>(function MainSidebar(
|
|
102
|
-
{
|
|
103
|
-
className,
|
|
104
|
-
items,
|
|
105
|
-
activeKey,
|
|
106
|
-
expanded,
|
|
107
|
-
defaultExpanded = false,
|
|
108
|
-
onExpandedChange,
|
|
109
|
-
onItemSelect,
|
|
110
|
-
header,
|
|
111
|
-
footer,
|
|
112
|
-
panelHeader,
|
|
113
|
-
panelFooter,
|
|
114
|
-
railClassName,
|
|
115
|
-
panelClassName,
|
|
116
|
-
itemClassName,
|
|
117
|
-
activeItemClassName,
|
|
118
|
-
collapsedLabel = 'Expand main sidebar',
|
|
119
|
-
expandedLabel = 'Collapse main sidebar',
|
|
120
|
-
backLabel = 'Back',
|
|
121
|
-
showLabelsWhenExpanded = true,
|
|
122
|
-
side = 'left',
|
|
123
|
-
mobile = false,
|
|
124
|
-
...props
|
|
125
|
-
},
|
|
126
|
-
ref,
|
|
127
|
-
) {
|
|
128
|
-
const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);
|
|
129
|
-
const isExpanded = expanded ?? internalExpanded;
|
|
130
|
-
const rootItem = useMemo(() => firstPanelItem(items, activeKey), [activeKey, items]);
|
|
131
|
-
const [pathKeys, setPathKeys] = useState<string[]>([]);
|
|
132
|
-
const path = useMemo(() => {
|
|
133
|
-
const resolved = pathKeys
|
|
134
|
-
.map((key) => findItem(items, key))
|
|
135
|
-
.filter((item): item is MainSidebarItem => Boolean(item));
|
|
136
|
-
return resolved.length ? resolved : rootItem ? [rootItem] : [];
|
|
137
|
-
}, [items, pathKeys, rootItem]);
|
|
138
|
-
|
|
139
|
-
const current = path.at(-1);
|
|
140
|
-
const currentItems = current?.children ?? items;
|
|
141
|
-
const isRightSide = side === 'right';
|
|
142
|
-
|
|
143
|
-
function setExpanded(next: boolean) {
|
|
144
|
-
if (expanded === undefined) setInternalExpanded(next);
|
|
145
|
-
if (!next) setPathKeys([]);
|
|
146
|
-
onExpandedChange?.(next);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function selectRailItem(item: MainSidebarItem) {
|
|
150
|
-
if (item.disabled) return;
|
|
151
|
-
onItemSelect?.(item.key, item);
|
|
152
|
-
|
|
153
|
-
if (opensPanel(item)) {
|
|
154
|
-
setPathKeys([item.key]);
|
|
155
|
-
setExpanded(true);
|
|
156
|
-
} else {
|
|
157
|
-
setPathKeys([]);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function selectPanelItem(item: MainSidebarItem) {
|
|
162
|
-
if (item.disabled) return;
|
|
163
|
-
onItemSelect?.(item.key, item);
|
|
164
|
-
|
|
165
|
-
if (opensPanel(item)) {
|
|
166
|
-
setExpanded(true);
|
|
167
|
-
setPathKeys((previous) =>
|
|
168
|
-
previous.at(-1) === item.key ? previous : [...previous, item.key],
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const panel = isExpanded ? (
|
|
174
|
-
<div
|
|
175
|
-
className={cn(
|
|
176
|
-
'min-w-0 bg-card text-card-foreground shadow-mi-card',
|
|
177
|
-
isRightSide ? 'border-l border-border' : 'border-r border-border',
|
|
178
|
-
'w-full md:w-72 md:flex-none',
|
|
179
|
-
isRightSide
|
|
180
|
-
? 'animate-in fade-in slide-in-from-right-2 duration-300'
|
|
181
|
-
: 'animate-in fade-in slide-in-from-left-2 duration-300',
|
|
182
|
-
panelClassName,
|
|
183
|
-
)}
|
|
184
|
-
>
|
|
185
|
-
<div className="flex h-full flex-col">
|
|
186
|
-
<div className="border-b border-border px-5 py-4">
|
|
187
|
-
{panelHeader ?? (
|
|
188
|
-
<div className="flex items-center justify-between gap-3">
|
|
189
|
-
<div className="min-w-0">
|
|
190
|
-
<div className="text-sm font-semibold">{current?.label ?? 'Main Sidebar'}</div>
|
|
191
|
-
{current?.description ? (
|
|
192
|
-
<p className="mt-1 text-xs text-muted-foreground">{current.description}</p>
|
|
193
|
-
) : null}
|
|
194
|
-
</div>
|
|
195
|
-
<button
|
|
196
|
-
type="button"
|
|
197
|
-
aria-label={expandedLabel}
|
|
198
|
-
onClick={() => setExpanded(false)}
|
|
199
|
-
className="grid size-8 shrink-0 place-items-center rounded-md text-muted-foreground transition hover:bg-muted hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
200
|
-
>
|
|
201
|
-
{isRightSide ? <ChevronRightIcon /> : <ChevronLeftIcon />}
|
|
202
|
-
</button>
|
|
203
|
-
</div>
|
|
204
|
-
)}
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
<div className="min-h-0 flex-1 overflow-y-auto px-3 py-4">
|
|
208
|
-
{path.length > 1 ? (
|
|
209
|
-
<button
|
|
210
|
-
type="button"
|
|
211
|
-
onClick={() => setPathKeys((previous) => previous.slice(0, -1))}
|
|
212
|
-
className="mb-3 inline-flex items-center gap-2 rounded-md px-2 py-1.5 text-xs font-medium text-muted-foreground transition hover:bg-muted hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
213
|
-
>
|
|
214
|
-
<span className="[&_svg]:size-4">
|
|
215
|
-
<ChevronLeftIcon />
|
|
216
|
-
</span>
|
|
217
|
-
{backLabel}
|
|
218
|
-
</button>
|
|
219
|
-
) : null}
|
|
220
|
-
|
|
221
|
-
{current?.panel ? (
|
|
222
|
-
<div className="animate-in fade-in slide-in-from-left-2 duration-200">
|
|
223
|
-
{current.panel}
|
|
224
|
-
</div>
|
|
225
|
-
) : (
|
|
226
|
-
<div className="grid gap-1 animate-in fade-in slide-in-from-right-2 duration-200">
|
|
227
|
-
{currentItems.map((item) => {
|
|
228
|
-
const isActive = item.key === activeKey;
|
|
229
|
-
const hasChildren = opensPanel(item);
|
|
230
|
-
return (
|
|
231
|
-
<button
|
|
232
|
-
key={item.key}
|
|
233
|
-
type="button"
|
|
234
|
-
disabled={item.disabled}
|
|
235
|
-
onClick={() => selectPanelItem(item)}
|
|
236
|
-
className={cn(
|
|
237
|
-
'group flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left transition',
|
|
238
|
-
'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
239
|
-
item.disabled && 'cursor-not-allowed opacity-40',
|
|
240
|
-
isActive ? 'bg-accent text-primary-foreground' : 'hover:bg-muted',
|
|
241
|
-
)}
|
|
242
|
-
>
|
|
243
|
-
<span className="grid size-8 shrink-0 place-items-center rounded-md bg-muted text-foreground group-hover:bg-background [&_svg]:size-4">
|
|
244
|
-
{item.icon}
|
|
245
|
-
</span>
|
|
246
|
-
<span className="min-w-0 flex-1">
|
|
247
|
-
<span className="block truncate text-sm font-medium">{item.label}</span>
|
|
248
|
-
{item.description ? (
|
|
249
|
-
<span
|
|
250
|
-
className={cn(
|
|
251
|
-
'mt-0.5 block text-xs',
|
|
252
|
-
isActive ? 'text-primary-foreground/80' : 'text-muted-foreground',
|
|
253
|
-
)}
|
|
254
|
-
>
|
|
255
|
-
{item.description}
|
|
256
|
-
</span>
|
|
257
|
-
) : null}
|
|
258
|
-
</span>
|
|
259
|
-
{item.badge ? <span className="shrink-0">{item.badge}</span> : null}
|
|
260
|
-
{hasChildren ? (
|
|
261
|
-
<span className="shrink-0 text-muted-foreground [&_svg]:size-4">
|
|
262
|
-
<ChevronRightIcon />
|
|
263
|
-
</span>
|
|
264
|
-
) : null}
|
|
265
|
-
</button>
|
|
266
|
-
);
|
|
267
|
-
})}
|
|
268
|
-
</div>
|
|
269
|
-
)}
|
|
270
|
-
</div>
|
|
271
|
-
|
|
272
|
-
{showLabelsWhenExpanded ? (
|
|
273
|
-
<div className="border-t border-border px-5 py-4 text-xs text-muted-foreground">
|
|
274
|
-
{currentItems.length} navigation {currentItems.length === 1 ? 'item' : 'items'}
|
|
275
|
-
</div>
|
|
276
|
-
) : null}
|
|
277
|
-
{panelFooter ? <div className="border-t border-border px-5 py-4">{panelFooter}</div> : null}
|
|
278
|
-
</div>
|
|
279
|
-
</div>
|
|
280
|
-
) : null;
|
|
281
|
-
|
|
282
|
-
return (
|
|
283
|
-
<>
|
|
284
|
-
<nav
|
|
285
|
-
aria-label="Main Sidebar"
|
|
286
|
-
data-expanded={isExpanded ? 'true' : 'false'}
|
|
287
|
-
data-mobile={mobile ? 'true' : undefined}
|
|
288
|
-
className={cn('absolute inset-x-0 bottom-0 z-30', !mobile && 'md:hidden', className)}
|
|
289
|
-
>
|
|
290
|
-
{isExpanded ? (
|
|
291
|
-
<div className="absolute inset-x-3 bottom-full mb-3 max-h-96 overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-mi-popover">
|
|
292
|
-
{panel}
|
|
293
|
-
</div>
|
|
294
|
-
) : null}
|
|
295
|
-
<div
|
|
296
|
-
className={cn(
|
|
297
|
-
'flex items-stretch border-t border-primary/80 bg-primary text-primary-foreground',
|
|
298
|
-
railClassName,
|
|
299
|
-
)}
|
|
300
|
-
>
|
|
301
|
-
<div className="flex min-w-0 flex-1 overflow-x-auto">
|
|
302
|
-
{items.map((item) => {
|
|
303
|
-
const isActive = item.key === activeKey;
|
|
304
|
-
const mobileItemClassName = cn(
|
|
305
|
-
'grid min-w-18 flex-1 place-items-center gap-1 px-3 py-2 text-caption transition',
|
|
306
|
-
'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-foreground/70',
|
|
307
|
-
item.disabled && 'cursor-not-allowed opacity-40',
|
|
308
|
-
isActive ? 'bg-accent text-primary-foreground' : 'text-primary-foreground/75',
|
|
309
|
-
itemClassName,
|
|
310
|
-
isActive && activeItemClassName,
|
|
311
|
-
);
|
|
312
|
-
const mobileItemContent = (
|
|
313
|
-
<>
|
|
314
|
-
<span className="[&_svg]:size-5">{item.icon}</span>
|
|
315
|
-
<span className="max-w-16 truncate">{item.label}</span>
|
|
316
|
-
</>
|
|
317
|
-
);
|
|
318
|
-
|
|
319
|
-
if (item.href && !item.disabled && !item.children?.length && !item.panel) {
|
|
320
|
-
return (
|
|
321
|
-
<a
|
|
322
|
-
key={item.key}
|
|
323
|
-
href={item.href}
|
|
324
|
-
aria-label={item.label}
|
|
325
|
-
aria-current={isActive ? 'page' : undefined}
|
|
326
|
-
aria-disabled={item.disabled || undefined}
|
|
327
|
-
className={mobileItemClassName}
|
|
328
|
-
>
|
|
329
|
-
{mobileItemContent}
|
|
330
|
-
</a>
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return (
|
|
335
|
-
<button
|
|
336
|
-
key={item.key}
|
|
337
|
-
type="button"
|
|
338
|
-
disabled={item.disabled}
|
|
339
|
-
aria-label={item.label}
|
|
340
|
-
aria-current={isActive ? 'page' : undefined}
|
|
341
|
-
aria-disabled={item.disabled || undefined}
|
|
342
|
-
onClick={() => selectRailItem(item)}
|
|
343
|
-
className={mobileItemClassName}
|
|
344
|
-
>
|
|
345
|
-
{mobileItemContent}
|
|
346
|
-
</button>
|
|
347
|
-
);
|
|
348
|
-
})}
|
|
349
|
-
</div>
|
|
350
|
-
<button
|
|
351
|
-
type="button"
|
|
352
|
-
aria-label={isExpanded ? expandedLabel : collapsedLabel}
|
|
353
|
-
aria-expanded={isExpanded}
|
|
354
|
-
onClick={() => setExpanded(!isExpanded)}
|
|
355
|
-
className="grid min-w-16 place-items-center gap-1 border-l border-primary-foreground/15 px-3 py-2 text-caption text-primary-foreground/85 transition hover:bg-primary-foreground/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-foreground/70"
|
|
356
|
-
>
|
|
357
|
-
<span className="[&_svg]:size-5">
|
|
358
|
-
<MenuIcon />
|
|
359
|
-
</span>
|
|
360
|
-
Menu
|
|
361
|
-
</button>
|
|
362
|
-
</div>
|
|
363
|
-
</nav>
|
|
364
|
-
|
|
365
|
-
<aside
|
|
366
|
-
ref={ref}
|
|
367
|
-
aria-label="Main Sidebar"
|
|
368
|
-
data-expanded={isExpanded ? 'true' : 'false'}
|
|
369
|
-
data-side={side}
|
|
370
|
-
data-mobile={mobile ? 'false' : undefined}
|
|
371
|
-
className={cn(
|
|
372
|
-
'h-full flex-shrink-0 overflow-hidden bg-primary text-primary-foreground',
|
|
373
|
-
mobile ? 'hidden' : 'hidden md:flex',
|
|
374
|
-
isRightSide && 'flex-row-reverse',
|
|
375
|
-
'transition-[width] duration-300 ease-out',
|
|
376
|
-
isExpanded ? 'w-[20.875rem]' : 'w-11.5',
|
|
377
|
-
className,
|
|
378
|
-
)}
|
|
379
|
-
{...props}
|
|
380
|
-
>
|
|
381
|
-
<div
|
|
382
|
-
className={cn(
|
|
383
|
-
'flex w-11.5 shrink-0 flex-col items-center gap-9 overflow-hidden bg-primary pb-20',
|
|
384
|
-
railClassName,
|
|
385
|
-
)}
|
|
386
|
-
>
|
|
387
|
-
{header ? <div className="flex w-full justify-center">{header}</div> : null}
|
|
388
|
-
<nav className="flex flex-col items-center" aria-label="Primary">
|
|
389
|
-
{items.map((item) => {
|
|
390
|
-
const isActive = item.key === activeKey;
|
|
391
|
-
const shared = {
|
|
392
|
-
'aria-label': item.label,
|
|
393
|
-
'aria-current': isActive ? ('page' as const) : undefined,
|
|
394
|
-
'aria-disabled': item.disabled || undefined,
|
|
395
|
-
title: item.label,
|
|
396
|
-
className: cn(
|
|
397
|
-
'grid h-11.5 w-11.5 shrink-0 place-items-center transition-colors duration-150',
|
|
398
|
-
'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-foreground/70',
|
|
399
|
-
item.disabled ? 'cursor-not-allowed opacity-40' : 'cursor-pointer',
|
|
400
|
-
isActive
|
|
401
|
-
? 'bg-accent text-primary-foreground opacity-100'
|
|
402
|
-
: 'text-primary-foreground opacity-70 hover:opacity-100 focus-visible:opacity-100',
|
|
403
|
-
itemClassName,
|
|
404
|
-
isActive && activeItemClassName,
|
|
405
|
-
),
|
|
406
|
-
};
|
|
407
|
-
const content = <span className="[&_svg]:size-5">{item.icon}</span>;
|
|
408
|
-
|
|
409
|
-
if (item.href && !item.disabled && !item.children?.length && !item.panel) {
|
|
410
|
-
return (
|
|
411
|
-
<a key={item.key} href={item.href} {...shared}>
|
|
412
|
-
{content}
|
|
413
|
-
</a>
|
|
414
|
-
);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return (
|
|
418
|
-
<button
|
|
419
|
-
key={item.key}
|
|
420
|
-
type="button"
|
|
421
|
-
disabled={item.disabled}
|
|
422
|
-
onClick={() => selectRailItem(item)}
|
|
423
|
-
{...shared}
|
|
424
|
-
>
|
|
425
|
-
{content}
|
|
426
|
-
</button>
|
|
427
|
-
);
|
|
428
|
-
})}
|
|
429
|
-
</nav>
|
|
430
|
-
{footer ? <div className="mt-auto flex w-full justify-center">{footer}</div> : null}
|
|
431
|
-
<button
|
|
432
|
-
type="button"
|
|
433
|
-
aria-label={isExpanded ? expandedLabel : collapsedLabel}
|
|
434
|
-
aria-expanded={isExpanded}
|
|
435
|
-
onClick={() => setExpanded(!isExpanded)}
|
|
436
|
-
className="mb-3 grid size-8 place-items-center rounded-md text-primary-foreground/80 transition hover:bg-primary-foreground/10 hover:text-primary-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-foreground/70"
|
|
437
|
-
>
|
|
438
|
-
<MenuIcon />
|
|
439
|
-
</button>
|
|
440
|
-
</div>
|
|
441
|
-
|
|
442
|
-
{panel}
|
|
443
|
-
</aside>
|
|
444
|
-
</>
|
|
445
|
-
);
|
|
446
|
-
});
|
|
447
11
|
|
|
448
|
-
export
|
|
12
|
+
export {
|
|
13
|
+
IconSidebar,
|
|
14
|
+
MainSidebar,
|
|
15
|
+
type IconSidebarItem,
|
|
16
|
+
type IconSidebarProps,
|
|
17
|
+
type MainSidebarCollapsible,
|
|
18
|
+
type MainSidebarColorScheme,
|
|
19
|
+
type MainSidebarDensity,
|
|
20
|
+
type MainSidebarItem,
|
|
21
|
+
type MainSidebarMotionPreset,
|
|
22
|
+
type MainSidebarProps,
|
|
23
|
+
type MainSidebarSearchConfig,
|
|
24
|
+
type MainSidebarSide,
|
|
25
|
+
type MainSidebarVariant,
|
|
26
|
+
} from './MainSidebar/index.js';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared back / chevron button. Used by drilldown + columns + floating
|
|
5
|
+
* panel headers.
|
|
6
|
+
*/
|
|
7
|
+
import { cn } from '../internal/cn.js';
|
|
8
|
+
|
|
9
|
+
interface BackButtonProps {
|
|
10
|
+
onClick: () => void;
|
|
11
|
+
label: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function BackButton({ onClick, label, className }: BackButtonProps) {
|
|
16
|
+
return (
|
|
17
|
+
<button
|
|
18
|
+
type="button"
|
|
19
|
+
onClick={onClick}
|
|
20
|
+
className={cn(
|
|
21
|
+
'inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-xs font-medium text-card-foreground/70 transition-colors',
|
|
22
|
+
'hover:bg-card-foreground/10 hover:text-card-foreground',
|
|
23
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-card-foreground/40',
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
>
|
|
27
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="size-3.5" aria-hidden="true">
|
|
28
|
+
<path d="m15 18-6-6 6-6" strokeLinecap="round" strokeLinejoin="round" />
|
|
29
|
+
</svg>
|
|
30
|
+
{label}
|
|
31
|
+
</button>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface CloseButtonProps {
|
|
36
|
+
onClick: () => void;
|
|
37
|
+
label: string;
|
|
38
|
+
className?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function CloseButton({ onClick, label, className }: CloseButtonProps) {
|
|
42
|
+
return (
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
onClick={onClick}
|
|
46
|
+
aria-label={label}
|
|
47
|
+
className={cn(
|
|
48
|
+
'grid size-8 shrink-0 place-items-center rounded-md text-card-foreground/70 transition hover:bg-card-foreground/10 hover:text-card-foreground',
|
|
49
|
+
'focus:outline-none focus-visible:ring-2 focus-visible:ring-card-foreground/40',
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
>
|
|
53
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="size-4" aria-hidden="true">
|
|
54
|
+
<path d="M6 6l12 12M18 6 6 18" strokeLinecap="round" />
|
|
55
|
+
</svg>
|
|
56
|
+
</button>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Path breadcrumb shared by drilldown / floating / columns / command / hover.
|
|
5
|
+
*
|
|
6
|
+
* Renders the drilled-into ancestors as clickable text crumbs so the user can
|
|
7
|
+
* jump back to any level in one click. Used in the panel header when the
|
|
8
|
+
* `path` is deeper than the root.
|
|
9
|
+
*
|
|
10
|
+
* <PathBreadcrumb path={[people, onboarding]} onJump={(idx) => ...} />
|
|
11
|
+
* ⇒ People › Onboarding
|
|
12
|
+
*
|
|
13
|
+
* `path` is the full ancestor list (including the current leaf). The leaf
|
|
14
|
+
* itself renders as the panel title, so the breadcrumb stops at
|
|
15
|
+
* `path.length - 1`.
|
|
16
|
+
*/
|
|
17
|
+
import { cn } from '../internal/cn.js';
|
|
18
|
+
import type { MainSidebarItem } from './types.js';
|
|
19
|
+
|
|
20
|
+
interface PathBreadcrumbProps {
|
|
21
|
+
path: MainSidebarItem[];
|
|
22
|
+
/** Click handler — receives the depth index to jump to. */
|
|
23
|
+
onJump: (depth: number) => void;
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function PathBreadcrumb({ path, onJump, className }: PathBreadcrumbProps) {
|
|
28
|
+
if (path.length <= 1) return null;
|
|
29
|
+
const ancestors = path.slice(0, -1);
|
|
30
|
+
return (
|
|
31
|
+
<nav
|
|
32
|
+
aria-label="Breadcrumb"
|
|
33
|
+
className={cn('flex flex-wrap items-center gap-1 text-xs text-card-foreground/70', className)}
|
|
34
|
+
>
|
|
35
|
+
<ol className="flex flex-wrap items-center gap-1">
|
|
36
|
+
{ancestors.map((node, idx) => (
|
|
37
|
+
<li key={node.key} className="flex items-center gap-1">
|
|
38
|
+
<button
|
|
39
|
+
type="button"
|
|
40
|
+
onClick={() => onJump(idx)}
|
|
41
|
+
className="rounded px-1 py-0.5 text-xs font-medium text-card-foreground/70 transition-colors hover:bg-card-foreground/10 hover:text-card-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-card-foreground/40"
|
|
42
|
+
>
|
|
43
|
+
{node.label}
|
|
44
|
+
</button>
|
|
45
|
+
<span aria-hidden="true" className="text-card-foreground/50">
|
|
46
|
+
›
|
|
47
|
+
</span>
|
|
48
|
+
</li>
|
|
49
|
+
))}
|
|
50
|
+
</ol>
|
|
51
|
+
</nav>
|
|
52
|
+
);
|
|
53
|
+
}
|