@auto-engineer/generate-react-client 1.63.0 → 1.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/dist/starter/.storybook/main.ts +17 -22
- package/dist/starter/.storybook/manager-head.html +31 -31
- package/dist/starter/.storybook/manager.ts +133 -133
- package/dist/starter/.storybook/preview-head.html +12 -12
- package/dist/starter/.storybook/preview.tsx +79 -79
- package/dist/starter/biome.json +126 -0
- package/dist/starter/codegen.ts +11 -11
- package/dist/starter/components.json +27 -27
- package/dist/starter/package.json +86 -80
- package/dist/starter/public/mockServiceWorker.js +261 -261
- package/dist/starter/scripts/build-component-db.ts +17 -20
- package/dist/starter/src/App.tsx +15 -17
- package/dist/starter/src/components/ui/Accordion.stories.tsx +35 -35
- package/dist/starter/src/components/ui/Accordion.tsx +33 -33
- package/dist/starter/src/components/ui/Alert.stories.tsx +15 -15
- package/dist/starter/src/components/ui/Alert.tsx +32 -32
- package/dist/starter/src/components/ui/AlertDialog.stories.tsx +50 -50
- package/dist/starter/src/components/ui/AlertDialog.tsx +114 -115
- package/dist/starter/src/components/ui/AspectRatio.stories.tsx +20 -20
- package/dist/starter/src/components/ui/AspectRatio.tsx +1 -1
- package/dist/starter/src/components/ui/Avatar.stories.tsx +27 -27
- package/dist/starter/src/components/ui/Avatar.tsx +63 -63
- package/dist/starter/src/components/ui/Badge.stories.tsx +14 -14
- package/dist/starter/src/components/ui/Badge.tsx +27 -27
- package/dist/starter/src/components/ui/Breadcrumb.stories.tsx +38 -38
- package/dist/starter/src/components/ui/Breadcrumb.tsx +63 -62
- package/dist/starter/src/components/ui/Button.stories.tsx +55 -55
- package/dist/starter/src/components/ui/Button.tsx +49 -49
- package/dist/starter/src/components/ui/ButtonGroup.stories.tsx +17 -17
- package/dist/starter/src/components/ui/ButtonGroup.tsx +52 -53
- package/dist/starter/src/components/ui/Calendar.stories.tsx +20 -19
- package/dist/starter/src/components/ui/Calendar.tsx +142 -143
- package/dist/starter/src/components/ui/Card.stories.tsx +29 -29
- package/dist/starter/src/components/ui/Card.tsx +31 -31
- package/dist/starter/src/components/ui/Carousel.stories.tsx +41 -41
- package/dist/starter/src/components/ui/Carousel.tsx +171 -172
- package/dist/starter/src/components/ui/Chart.stories.tsx +21 -21
- package/dist/starter/src/components/ui/Chart.tsx +244 -247
- package/dist/starter/src/components/ui/Checkbox.stories.tsx +11 -11
- package/dist/starter/src/components/ui/Checkbox.tsx +18 -18
- package/dist/starter/src/components/ui/Collapsible.stories.tsx +40 -40
- package/dist/starter/src/components/ui/Collapsible.tsx +3 -3
- package/dist/starter/src/components/ui/Combobox.stories.tsx +48 -48
- package/dist/starter/src/components/ui/Combobox.tsx +204 -205
- package/dist/starter/src/components/ui/Command.stories.tsx +55 -55
- package/dist/starter/src/components/ui/Command.tsx +102 -103
- package/dist/starter/src/components/ui/ContextMenu.stories.tsx +52 -52
- package/dist/starter/src/components/ui/ContextMenu.tsx +151 -151
- package/dist/starter/src/components/ui/DesignSystem-Colors.stories.tsx +92 -92
- package/dist/starter/src/components/ui/DesignSystem-Layout.stories.tsx +139 -139
- package/dist/starter/src/components/ui/DesignSystem-Overview.stories.tsx +676 -657
- package/dist/starter/src/components/ui/DesignSystem-Typography.stories.tsx +59 -59
- package/dist/starter/src/components/ui/Dialog.stories.tsx +56 -56
- package/dist/starter/src/components/ui/Dialog.tsx +97 -98
- package/dist/starter/src/components/ui/Direction.stories.tsx +20 -20
- package/dist/starter/src/components/ui/Direction.tsx +7 -7
- package/dist/starter/src/components/ui/Drawer.stories.tsx +54 -54
- package/dist/starter/src/components/ui/Drawer.tsx +70 -70
- package/dist/starter/src/components/ui/DropdownMenu.stories.tsx +58 -58
- package/dist/starter/src/components/ui/DropdownMenu.tsx +157 -157
- package/dist/starter/src/components/ui/Empty.stories.tsx +22 -22
- package/dist/starter/src/components/ui/Empty.tsx +58 -58
- package/dist/starter/src/components/ui/Field.stories.tsx +31 -31
- package/dist/starter/src/components/ui/Field.tsx +180 -181
- package/dist/starter/src/components/ui/Form.stories.tsx +29 -29
- package/dist/starter/src/components/ui/Form.tsx +93 -96
- package/dist/starter/src/components/ui/HoverCard.stories.tsx +34 -34
- package/dist/starter/src/components/ui/HoverCard.tsx +21 -21
- package/dist/starter/src/components/ui/Input.stories.tsx +18 -18
- package/dist/starter/src/components/ui/Input.tsx +14 -14
- package/dist/starter/src/components/ui/InputGroup.stories.tsx +34 -34
- package/dist/starter/src/components/ui/InputGroup.tsx +110 -111
- package/dist/starter/src/components/ui/InputOTP.stories.tsx +28 -28
- package/dist/starter/src/components/ui/InputOTP.tsx +43 -43
- package/dist/starter/src/components/ui/Item.stories.tsx +45 -45
- package/dist/starter/src/components/ui/Item.tsx +113 -114
- package/dist/starter/src/components/ui/Kbd.stories.tsx +31 -31
- package/dist/starter/src/components/ui/Kbd.tsx +11 -11
- package/dist/starter/src/components/ui/Label.stories.tsx +62 -62
- package/dist/starter/src/components/ui/Label.tsx +26 -25
- package/dist/starter/src/components/ui/Menubar.stories.tsx +62 -62
- package/dist/starter/src/components/ui/Menubar.tsx +173 -173
- package/dist/starter/src/components/ui/NativeSelect.stories.tsx +26 -26
- package/dist/starter/src/components/ui/NativeSelect.tsx +29 -29
- package/dist/starter/src/components/ui/NavigationMenu.stories.tsx +64 -64
- package/dist/starter/src/components/ui/NavigationMenu.tsx +103 -103
- package/dist/starter/src/components/ui/Pagination.stories.tsx +61 -61
- package/dist/starter/src/components/ui/Pagination.tsx +69 -71
- package/dist/starter/src/components/ui/Popover.stories.tsx +38 -38
- package/dist/starter/src/components/ui/Popover.tsx +25 -25
- package/dist/starter/src/components/ui/Progress.stories.tsx +9 -9
- package/dist/starter/src/components/ui/Progress.tsx +14 -14
- package/dist/starter/src/components/ui/RadioGroup.stories.tsx +35 -35
- package/dist/starter/src/components/ui/RadioGroup.tsx +19 -19
- package/dist/starter/src/components/ui/Resizable.stories.tsx +54 -54
- package/dist/starter/src/components/ui/Resizable.tsx +29 -29
- package/dist/starter/src/components/ui/ScrollArea.stories.tsx +27 -27
- package/dist/starter/src/components/ui/ScrollArea.tsx +34 -34
- package/dist/starter/src/components/ui/Select.stories.tsx +43 -43
- package/dist/starter/src/components/ui/Select.tsx +120 -120
- package/dist/starter/src/components/ui/Separator.stories.tsx +27 -27
- package/dist/starter/src/components/ui/Separator.tsx +17 -17
- package/dist/starter/src/components/ui/Sheet.stories.tsx +53 -53
- package/dist/starter/src/components/ui/Sheet.tsx +69 -69
- package/dist/starter/src/components/ui/Sidebar.stories.tsx +77 -77
- package/dist/starter/src/components/ui/Sidebar.tsx +563 -564
- package/dist/starter/src/components/ui/Skeleton.stories.tsx +25 -25
- package/dist/starter/src/components/ui/Skeleton.tsx +1 -1
- package/dist/starter/src/components/ui/Slider.stories.tsx +5 -5
- package/dist/starter/src/components/ui/Slider.tsx +45 -44
- package/dist/starter/src/components/ui/Sonner.stories.tsx +32 -32
- package/dist/starter/src/components/ui/Sonner.tsx +23 -23
- package/dist/starter/src/components/ui/Spinner.stories.tsx +8 -8
- package/dist/starter/src/components/ui/Spinner.tsx +1 -1
- package/dist/starter/src/components/ui/Switch.stories.tsx +16 -17
- package/dist/starter/src/components/ui/Switch.tsx +24 -24
- package/dist/starter/src/components/ui/Table.stories.tsx +50 -50
- package/dist/starter/src/components/ui/Table.tsx +45 -45
- package/dist/starter/src/components/ui/Tabs.stories.tsx +39 -39
- package/dist/starter/src/components/ui/Tabs.tsx +47 -47
- package/dist/starter/src/components/ui/Textarea.stories.tsx +9 -9
- package/dist/starter/src/components/ui/Textarea.tsx +11 -11
- package/dist/starter/src/components/ui/Toast.stories.tsx +77 -77
- package/dist/starter/src/components/ui/Toast.tsx +75 -75
- package/dist/starter/src/components/ui/Toaster.tsx +17 -19
- package/dist/starter/src/components/ui/Toggle.stories.tsx +20 -20
- package/dist/starter/src/components/ui/Toggle.tsx +26 -26
- package/dist/starter/src/components/ui/ToggleGroup.stories.tsx +41 -41
- package/dist/starter/src/components/ui/ToggleGroup.tsx +61 -62
- package/dist/starter/src/components/ui/Tooltip.stories.tsx +26 -26
- package/dist/starter/src/components/ui/Tooltip.tsx +24 -24
- package/dist/starter/src/gql/execute.ts +1 -1
- package/dist/starter/src/gql/fragment-masking.ts +1 -1
- package/dist/starter/src/gql/graphql.ts +3 -0
- package/dist/starter/src/hooks/use-mobile.ts +11 -11
- package/dist/starter/src/hooks/use-toast.ts +135 -135
- package/dist/starter/src/index.css +105 -105
- package/dist/starter/src/lib/utils.ts +1 -1
- package/dist/starter/src/main.tsx +4 -1
- package/dist/starter/tsconfig.app.json +24 -24
- package/dist/starter/tsconfig.json +8 -8
- package/dist/starter/vite.config.ts +38 -37
- package/package.json +3 -3
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
1
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
2
|
import { PanelLeftIcon } from 'lucide-react';
|
|
4
3
|
import { Slot } from 'radix-ui';
|
|
5
|
-
|
|
6
|
-
import { useIsMobile } from '@/hooks/use-mobile';
|
|
7
|
-
import { cn } from '@/lib/utils';
|
|
4
|
+
import * as React from 'react';
|
|
8
5
|
import { Button } from '@/components/ui/Button';
|
|
9
6
|
import { Input } from '@/components/ui/Input';
|
|
10
7
|
import { Separator } from '@/components/ui/Separator';
|
|
11
8
|
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '@/components/ui/Sheet';
|
|
12
9
|
import { Skeleton } from '@/components/ui/Skeleton';
|
|
13
10
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/Tooltip';
|
|
11
|
+
import { useIsMobile } from '@/hooks/use-mobile';
|
|
12
|
+
import { cn } from '@/lib/utils';
|
|
14
13
|
|
|
15
14
|
const SIDEBAR_COOKIE_NAME = 'sidebar_state';
|
|
16
15
|
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
|
@@ -20,24 +19,24 @@ const SIDEBAR_WIDTH_ICON = '3rem';
|
|
|
20
19
|
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
|
|
21
20
|
|
|
22
21
|
type SidebarContextProps = {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
state: 'expanded' | 'collapsed';
|
|
23
|
+
open: boolean;
|
|
24
|
+
setOpen: (open: boolean) => void;
|
|
25
|
+
openMobile: boolean;
|
|
26
|
+
setOpenMobile: (open: boolean) => void;
|
|
27
|
+
isMobile: boolean;
|
|
28
|
+
toggleSidebar: () => void;
|
|
30
29
|
};
|
|
31
30
|
|
|
32
31
|
const SidebarContext = React.createContext<SidebarContextProps | null>(null);
|
|
33
32
|
|
|
34
33
|
function useSidebar() {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
const context = React.useContext(SidebarContext);
|
|
35
|
+
if (!context) {
|
|
36
|
+
throw new Error('useSidebar must be used within a SidebarProvider.');
|
|
37
|
+
}
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
return context;
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
/**
|
|
@@ -45,95 +44,95 @@ function useSidebar() {
|
|
|
45
44
|
* Wrap your layout with this to enable sidebar state management for all child Sidebar components.
|
|
46
45
|
*/
|
|
47
46
|
function SidebarProvider({
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
defaultOpen = true,
|
|
48
|
+
open: openProp,
|
|
49
|
+
onOpenChange: setOpenProp,
|
|
50
|
+
className,
|
|
51
|
+
style,
|
|
52
|
+
children,
|
|
53
|
+
...props
|
|
55
54
|
}: React.ComponentProps<'div'> & {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
defaultOpen?: boolean;
|
|
56
|
+
open?: boolean;
|
|
57
|
+
onOpenChange?: (open: boolean) => void;
|
|
59
58
|
}) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
59
|
+
const isMobile = useIsMobile();
|
|
60
|
+
const [openMobile, setOpenMobile] = React.useState(false);
|
|
61
|
+
|
|
62
|
+
// This is the internal state of the sidebar.
|
|
63
|
+
// We use openProp and setOpenProp for control from outside the component.
|
|
64
|
+
const [_open, _setOpen] = React.useState(defaultOpen);
|
|
65
|
+
const open = openProp ?? _open;
|
|
66
|
+
const setOpen = React.useCallback(
|
|
67
|
+
(value: boolean | ((value: boolean) => boolean)) => {
|
|
68
|
+
const openState = typeof value === 'function' ? value(open) : value;
|
|
69
|
+
if (setOpenProp) {
|
|
70
|
+
setOpenProp(openState);
|
|
71
|
+
} else {
|
|
72
|
+
_setOpen(openState);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// biome-ignore lint/suspicious/noDocumentCookie: Persisting sidebar state via cookie is intentional for SSR hydration
|
|
76
|
+
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
|
77
|
+
},
|
|
78
|
+
[setOpenProp, open],
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Helper to toggle the sidebar.
|
|
82
|
+
const toggleSidebar = React.useCallback(() => {
|
|
83
|
+
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
|
|
84
|
+
}, [isMobile, setOpen]);
|
|
85
|
+
|
|
86
|
+
// Adds a keyboard shortcut to toggle the sidebar.
|
|
87
|
+
React.useEffect(() => {
|
|
88
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
89
|
+
if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
toggleSidebar();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
96
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
97
|
+
}, [toggleSidebar]);
|
|
98
|
+
|
|
99
|
+
// We add a state so that we can do data-state="expanded" or "collapsed".
|
|
100
|
+
// This makes it easier to style the sidebar with Tailwind classes.
|
|
101
|
+
const state = open ? 'expanded' : 'collapsed';
|
|
102
|
+
|
|
103
|
+
const contextValue = React.useMemo<SidebarContextProps>(
|
|
104
|
+
() => ({
|
|
105
|
+
state,
|
|
106
|
+
open,
|
|
107
|
+
setOpen,
|
|
108
|
+
isMobile,
|
|
109
|
+
openMobile,
|
|
110
|
+
setOpenMobile,
|
|
111
|
+
toggleSidebar,
|
|
112
|
+
}),
|
|
113
|
+
[state, open, setOpen, isMobile, openMobile, toggleSidebar],
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<SidebarContext.Provider value={contextValue}>
|
|
118
|
+
<TooltipProvider delayDuration={0}>
|
|
119
|
+
<div
|
|
120
|
+
data-slot="sidebar-wrapper"
|
|
121
|
+
style={
|
|
122
|
+
{
|
|
123
|
+
'--sidebar-width': SIDEBAR_WIDTH,
|
|
124
|
+
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
|
|
125
|
+
...style,
|
|
126
|
+
} as React.CSSProperties
|
|
127
|
+
}
|
|
128
|
+
className={cn('group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full', className)}
|
|
129
|
+
{...props}
|
|
130
|
+
>
|
|
131
|
+
{children}
|
|
132
|
+
</div>
|
|
133
|
+
</TooltipProvider>
|
|
134
|
+
</SidebarContext.Provider>
|
|
135
|
+
);
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
/**
|
|
@@ -141,555 +140,555 @@ function SidebarProvider({
|
|
|
141
140
|
* Supports left/right placement, multiple visual variants (sidebar, floating, inset), and collapsible modes (offcanvas, icon, none).
|
|
142
141
|
*/
|
|
143
142
|
function Sidebar({
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
143
|
+
/** Which edge of the viewport the sidebar is placed on. */
|
|
144
|
+
side = 'left',
|
|
145
|
+
/** Visual style: "sidebar" (bordered), "floating" (rounded with shadow), or "inset" (embedded). */
|
|
146
|
+
variant = 'sidebar',
|
|
147
|
+
/** Collapse behavior: "offcanvas" (slides off-screen), "icon" (collapses to icon width), or "none" (always visible). */
|
|
148
|
+
collapsible = 'offcanvas',
|
|
149
|
+
className,
|
|
150
|
+
children,
|
|
151
|
+
...props
|
|
153
152
|
}: React.ComponentProps<'div'> & {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
side?: 'left' | 'right';
|
|
154
|
+
variant?: 'sidebar' | 'floating' | 'inset';
|
|
155
|
+
collapsible?: 'offcanvas' | 'icon' | 'none';
|
|
157
156
|
}) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
157
|
+
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
158
|
+
|
|
159
|
+
if (collapsible === 'none') {
|
|
160
|
+
return (
|
|
161
|
+
<div
|
|
162
|
+
data-slot="sidebar"
|
|
163
|
+
className={cn('bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col', className)}
|
|
164
|
+
{...props}
|
|
165
|
+
>
|
|
166
|
+
{children}
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (isMobile) {
|
|
172
|
+
return (
|
|
173
|
+
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
|
|
174
|
+
<SheetContent
|
|
175
|
+
data-sidebar="sidebar"
|
|
176
|
+
data-slot="sidebar"
|
|
177
|
+
data-mobile="true"
|
|
178
|
+
className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
|
|
179
|
+
style={
|
|
180
|
+
{
|
|
181
|
+
'--sidebar-width': SIDEBAR_WIDTH_MOBILE,
|
|
182
|
+
} as React.CSSProperties
|
|
183
|
+
}
|
|
184
|
+
side={side}
|
|
185
|
+
>
|
|
186
|
+
<SheetHeader className="sr-only">
|
|
187
|
+
<SheetTitle>Sidebar</SheetTitle>
|
|
188
|
+
<SheetDescription>Displays the mobile sidebar.</SheetDescription>
|
|
189
|
+
</SheetHeader>
|
|
190
|
+
<div className="flex h-full w-full flex-col">{children}</div>
|
|
191
|
+
</SheetContent>
|
|
192
|
+
</Sheet>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<div
|
|
198
|
+
className="group peer text-sidebar-foreground hidden md:block"
|
|
199
|
+
data-state={state}
|
|
200
|
+
data-collapsible={state === 'collapsed' ? collapsible : ''}
|
|
201
|
+
data-variant={variant}
|
|
202
|
+
data-side={side}
|
|
203
|
+
data-slot="sidebar"
|
|
204
|
+
>
|
|
205
|
+
{/* This is what handles the sidebar gap on desktop */}
|
|
206
|
+
<div
|
|
207
|
+
data-slot="sidebar-gap"
|
|
208
|
+
className={cn(
|
|
209
|
+
'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',
|
|
210
|
+
'group-data-[collapsible=offcanvas]:w-0',
|
|
211
|
+
'group-data-[side=right]:rotate-180',
|
|
212
|
+
variant === 'floating' || variant === 'inset'
|
|
213
|
+
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'
|
|
214
|
+
: 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',
|
|
215
|
+
)}
|
|
216
|
+
/>
|
|
217
|
+
<div
|
|
218
|
+
data-slot="sidebar-container"
|
|
219
|
+
className={cn(
|
|
220
|
+
'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex',
|
|
221
|
+
side === 'left'
|
|
222
|
+
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
|
|
223
|
+
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
|
|
224
|
+
// Adjust the padding for floating and inset variants.
|
|
225
|
+
variant === 'floating' || variant === 'inset'
|
|
226
|
+
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
|
|
227
|
+
: 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',
|
|
228
|
+
className,
|
|
229
|
+
)}
|
|
230
|
+
{...props}
|
|
231
|
+
>
|
|
232
|
+
<div
|
|
233
|
+
data-sidebar="sidebar"
|
|
234
|
+
data-slot="sidebar-inner"
|
|
235
|
+
className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm"
|
|
236
|
+
>
|
|
237
|
+
{children}
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
243
242
|
}
|
|
244
243
|
|
|
245
244
|
/** A button that toggles the sidebar open/closed state. */
|
|
246
245
|
function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<typeof Button>) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
246
|
+
const { toggleSidebar } = useSidebar();
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<Button
|
|
250
|
+
data-sidebar="trigger"
|
|
251
|
+
data-slot="sidebar-trigger"
|
|
252
|
+
variant="ghost"
|
|
253
|
+
size="icon"
|
|
254
|
+
className={cn('size-7', className)}
|
|
255
|
+
onClick={(event) => {
|
|
256
|
+
onClick?.(event);
|
|
257
|
+
toggleSidebar();
|
|
258
|
+
}}
|
|
259
|
+
{...props}
|
|
260
|
+
>
|
|
261
|
+
<PanelLeftIcon />
|
|
262
|
+
<span className="sr-only">Toggle Sidebar</span>
|
|
263
|
+
</Button>
|
|
264
|
+
);
|
|
266
265
|
}
|
|
267
266
|
|
|
268
267
|
/** A thin hover-activated rail along the sidebar edge that toggles collapse on click. */
|
|
269
268
|
function SidebarRail({ className, ...props }: React.ComponentProps<'button'>) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
269
|
+
const { toggleSidebar } = useSidebar();
|
|
270
|
+
|
|
271
|
+
return (
|
|
272
|
+
<button
|
|
273
|
+
data-sidebar="rail"
|
|
274
|
+
data-slot="sidebar-rail"
|
|
275
|
+
aria-label="Toggle Sidebar"
|
|
276
|
+
tabIndex={-1}
|
|
277
|
+
onClick={toggleSidebar}
|
|
278
|
+
title="Toggle Sidebar"
|
|
279
|
+
className={cn(
|
|
280
|
+
'hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex',
|
|
281
|
+
'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',
|
|
282
|
+
'[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
|
|
283
|
+
'hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full',
|
|
284
|
+
'[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
|
|
285
|
+
'[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
|
|
286
|
+
className,
|
|
287
|
+
)}
|
|
288
|
+
{...props}
|
|
289
|
+
/>
|
|
290
|
+
);
|
|
292
291
|
}
|
|
293
292
|
|
|
294
293
|
/** The main content area that sits alongside the sidebar, adjusting its margin based on sidebar state. */
|
|
295
294
|
function SidebarInset({ className, ...props }: React.ComponentProps<'main'>) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
295
|
+
return (
|
|
296
|
+
<main
|
|
297
|
+
data-slot="sidebar-inset"
|
|
298
|
+
className={cn(
|
|
299
|
+
'bg-background relative flex w-full flex-1 flex-col',
|
|
300
|
+
'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2',
|
|
301
|
+
className,
|
|
302
|
+
)}
|
|
303
|
+
{...props}
|
|
304
|
+
/>
|
|
305
|
+
);
|
|
307
306
|
}
|
|
308
307
|
|
|
309
308
|
function SidebarInput({ className, ...props }: React.ComponentProps<typeof Input>) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
309
|
+
return (
|
|
310
|
+
<Input
|
|
311
|
+
data-slot="sidebar-input"
|
|
312
|
+
data-sidebar="input"
|
|
313
|
+
className={cn('bg-background h-8 w-full shadow-none', className)}
|
|
314
|
+
{...props}
|
|
315
|
+
/>
|
|
316
|
+
);
|
|
318
317
|
}
|
|
319
318
|
|
|
320
319
|
function SidebarHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
320
|
+
return (
|
|
321
|
+
<div
|
|
322
|
+
data-slot="sidebar-header"
|
|
323
|
+
data-sidebar="header"
|
|
324
|
+
className={cn('flex flex-col gap-2 p-2', className)}
|
|
325
|
+
{...props}
|
|
326
|
+
/>
|
|
327
|
+
);
|
|
329
328
|
}
|
|
330
329
|
|
|
331
330
|
function SidebarFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
331
|
+
return (
|
|
332
|
+
<div
|
|
333
|
+
data-slot="sidebar-footer"
|
|
334
|
+
data-sidebar="footer"
|
|
335
|
+
className={cn('flex flex-col gap-2 p-2', className)}
|
|
336
|
+
{...props}
|
|
337
|
+
/>
|
|
338
|
+
);
|
|
340
339
|
}
|
|
341
340
|
|
|
342
341
|
function SidebarSeparator({ className, ...props }: React.ComponentProps<typeof Separator>) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
342
|
+
return (
|
|
343
|
+
<Separator
|
|
344
|
+
data-slot="sidebar-separator"
|
|
345
|
+
data-sidebar="separator"
|
|
346
|
+
className={cn('bg-sidebar-border mx-2 w-auto', className)}
|
|
347
|
+
{...props}
|
|
348
|
+
/>
|
|
349
|
+
);
|
|
351
350
|
}
|
|
352
351
|
|
|
353
352
|
/** The scrollable main body area of the sidebar that holds SidebarGroup sections. */
|
|
354
353
|
function SidebarContent({ className, ...props }: React.ComponentProps<'div'>) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
354
|
+
return (
|
|
355
|
+
<div
|
|
356
|
+
data-slot="sidebar-content"
|
|
357
|
+
data-sidebar="content"
|
|
358
|
+
className={cn(
|
|
359
|
+
'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
|
|
360
|
+
className,
|
|
361
|
+
)}
|
|
362
|
+
{...props}
|
|
363
|
+
/>
|
|
364
|
+
);
|
|
366
365
|
}
|
|
367
366
|
|
|
368
367
|
/** A logical grouping of sidebar menu items, optionally with a label and action button. */
|
|
369
368
|
function SidebarGroup({ className, ...props }: React.ComponentProps<'div'>) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
369
|
+
return (
|
|
370
|
+
<div
|
|
371
|
+
data-slot="sidebar-group"
|
|
372
|
+
data-sidebar="group"
|
|
373
|
+
className={cn('relative flex w-full min-w-0 flex-col p-2', className)}
|
|
374
|
+
{...props}
|
|
375
|
+
/>
|
|
376
|
+
);
|
|
378
377
|
}
|
|
379
378
|
|
|
380
379
|
/** A small heading label for a SidebarGroup. Hidden when the sidebar is collapsed to icon mode. */
|
|
381
380
|
function SidebarGroupLabel({
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
381
|
+
className,
|
|
382
|
+
asChild = false,
|
|
383
|
+
...props
|
|
385
384
|
}: React.ComponentProps<'div'> & { asChild?: boolean }) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
385
|
+
const Comp = asChild ? Slot.Root : 'div';
|
|
386
|
+
|
|
387
|
+
return (
|
|
388
|
+
<Comp
|
|
389
|
+
data-slot="sidebar-group-label"
|
|
390
|
+
data-sidebar="group-label"
|
|
391
|
+
className={cn(
|
|
392
|
+
'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
|
393
|
+
'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
|
|
394
|
+
className,
|
|
395
|
+
)}
|
|
396
|
+
{...props}
|
|
397
|
+
/>
|
|
398
|
+
);
|
|
400
399
|
}
|
|
401
400
|
|
|
402
401
|
function SidebarGroupAction({
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
402
|
+
className,
|
|
403
|
+
asChild = false,
|
|
404
|
+
...props
|
|
406
405
|
}: React.ComponentProps<'button'> & { asChild?: boolean }) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
406
|
+
const Comp = asChild ? Slot.Root : 'button';
|
|
407
|
+
|
|
408
|
+
return (
|
|
409
|
+
<Comp
|
|
410
|
+
data-slot="sidebar-group-action"
|
|
411
|
+
data-sidebar="group-action"
|
|
412
|
+
className={cn(
|
|
413
|
+
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
|
414
|
+
// Increases the hit area of the button on mobile.
|
|
415
|
+
'after:absolute after:-inset-2 md:after:hidden',
|
|
416
|
+
'group-data-[collapsible=icon]:hidden',
|
|
417
|
+
className,
|
|
418
|
+
)}
|
|
419
|
+
{...props}
|
|
420
|
+
/>
|
|
421
|
+
);
|
|
423
422
|
}
|
|
424
423
|
|
|
425
424
|
function SidebarGroupContent({ className, ...props }: React.ComponentProps<'div'>) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
425
|
+
return (
|
|
426
|
+
<div
|
|
427
|
+
data-slot="sidebar-group-content"
|
|
428
|
+
data-sidebar="group-content"
|
|
429
|
+
className={cn('w-full text-sm', className)}
|
|
430
|
+
{...props}
|
|
431
|
+
/>
|
|
432
|
+
);
|
|
434
433
|
}
|
|
435
434
|
|
|
436
435
|
function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
436
|
+
return (
|
|
437
|
+
<ul
|
|
438
|
+
data-slot="sidebar-menu"
|
|
439
|
+
data-sidebar="menu"
|
|
440
|
+
className={cn('flex w-full min-w-0 flex-col gap-1', className)}
|
|
441
|
+
{...props}
|
|
442
|
+
/>
|
|
443
|
+
);
|
|
445
444
|
}
|
|
446
445
|
|
|
447
446
|
function SidebarMenuItem({ className, ...props }: React.ComponentProps<'li'>) {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
447
|
+
return (
|
|
448
|
+
<li
|
|
449
|
+
data-slot="sidebar-menu-item"
|
|
450
|
+
data-sidebar="menu-item"
|
|
451
|
+
className={cn('group/menu-item relative', className)}
|
|
452
|
+
{...props}
|
|
453
|
+
/>
|
|
454
|
+
);
|
|
456
455
|
}
|
|
457
456
|
|
|
458
457
|
const sidebarMenuButtonVariants = cva(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
458
|
+
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden 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',
|
|
459
|
+
{
|
|
460
|
+
variants: {
|
|
461
|
+
variant: {
|
|
462
|
+
default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
|
|
463
|
+
outline:
|
|
464
|
+
'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))]',
|
|
465
|
+
},
|
|
466
|
+
size: {
|
|
467
|
+
default: 'h-8 text-sm',
|
|
468
|
+
sm: 'h-7 text-xs',
|
|
469
|
+
lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
defaultVariants: {
|
|
473
|
+
variant: 'default',
|
|
474
|
+
size: 'default',
|
|
475
|
+
},
|
|
476
|
+
},
|
|
478
477
|
);
|
|
479
478
|
|
|
480
479
|
/** An interactive menu item button with optional tooltip support when collapsed. Supports active state highlighting and size variants. */
|
|
481
480
|
function SidebarMenuButton({
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
481
|
+
asChild = false,
|
|
482
|
+
isActive = false,
|
|
483
|
+
variant = 'default',
|
|
484
|
+
size = 'default',
|
|
485
|
+
tooltip,
|
|
486
|
+
className,
|
|
487
|
+
...props
|
|
489
488
|
}: React.ComponentProps<'button'> & {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
489
|
+
asChild?: boolean;
|
|
490
|
+
isActive?: boolean;
|
|
491
|
+
tooltip?: string | React.ComponentProps<typeof TooltipContent>;
|
|
493
492
|
} & VariantProps<typeof sidebarMenuButtonVariants>) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
493
|
+
const Comp = asChild ? Slot.Root : 'button';
|
|
494
|
+
const { isMobile, state } = useSidebar();
|
|
495
|
+
|
|
496
|
+
const button = (
|
|
497
|
+
<Comp
|
|
498
|
+
data-slot="sidebar-menu-button"
|
|
499
|
+
data-sidebar="menu-button"
|
|
500
|
+
data-size={size}
|
|
501
|
+
data-active={isActive}
|
|
502
|
+
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
|
|
503
|
+
{...props}
|
|
504
|
+
/>
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
if (!tooltip) {
|
|
508
|
+
return button;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (typeof tooltip === 'string') {
|
|
512
|
+
tooltip = {
|
|
513
|
+
children: tooltip,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return (
|
|
518
|
+
<Tooltip>
|
|
519
|
+
<TooltipTrigger asChild>{button}</TooltipTrigger>
|
|
520
|
+
<TooltipContent side="right" align="center" hidden={state !== 'collapsed' || isMobile} {...tooltip} />
|
|
521
|
+
</Tooltip>
|
|
522
|
+
);
|
|
524
523
|
}
|
|
525
524
|
|
|
526
525
|
/** An action button (e.g., delete, settings) positioned to the right of a SidebarMenuButton. */
|
|
527
526
|
function SidebarMenuAction({
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
527
|
+
className,
|
|
528
|
+
asChild = false,
|
|
529
|
+
showOnHover = false,
|
|
530
|
+
...props
|
|
532
531
|
}: React.ComponentProps<'button'> & {
|
|
533
|
-
|
|
534
|
-
|
|
532
|
+
asChild?: boolean;
|
|
533
|
+
showOnHover?: boolean;
|
|
535
534
|
}) {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
535
|
+
const Comp = asChild ? Slot.Root : 'button';
|
|
536
|
+
|
|
537
|
+
return (
|
|
538
|
+
<Comp
|
|
539
|
+
data-slot="sidebar-menu-action"
|
|
540
|
+
data-sidebar="menu-action"
|
|
541
|
+
className={cn(
|
|
542
|
+
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
|
543
|
+
// Increases the hit area of the button on mobile.
|
|
544
|
+
'after:absolute after:-inset-2 md:after:hidden',
|
|
545
|
+
'peer-data-[size=sm]/menu-button:top-1',
|
|
546
|
+
'peer-data-[size=default]/menu-button:top-1.5',
|
|
547
|
+
'peer-data-[size=lg]/menu-button:top-2.5',
|
|
548
|
+
'group-data-[collapsible=icon]:hidden',
|
|
549
|
+
showOnHover &&
|
|
550
|
+
'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',
|
|
551
|
+
className,
|
|
552
|
+
)}
|
|
553
|
+
{...props}
|
|
554
|
+
/>
|
|
555
|
+
);
|
|
557
556
|
}
|
|
558
557
|
|
|
559
558
|
function SidebarMenuBadge({ className, ...props }: React.ComponentProps<'div'>) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
559
|
+
return (
|
|
560
|
+
<div
|
|
561
|
+
data-slot="sidebar-menu-badge"
|
|
562
|
+
data-sidebar="menu-badge"
|
|
563
|
+
className={cn(
|
|
564
|
+
'text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none',
|
|
565
|
+
'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
|
|
566
|
+
'peer-data-[size=sm]/menu-button:top-1',
|
|
567
|
+
'peer-data-[size=default]/menu-button:top-1.5',
|
|
568
|
+
'peer-data-[size=lg]/menu-button:top-2.5',
|
|
569
|
+
'group-data-[collapsible=icon]:hidden',
|
|
570
|
+
className,
|
|
571
|
+
)}
|
|
572
|
+
{...props}
|
|
573
|
+
/>
|
|
574
|
+
);
|
|
576
575
|
}
|
|
577
576
|
|
|
578
577
|
function SidebarMenuSkeleton({
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
578
|
+
className,
|
|
579
|
+
showIcon = false,
|
|
580
|
+
...props
|
|
582
581
|
}: React.ComponentProps<'div'> & {
|
|
583
|
-
|
|
582
|
+
showIcon?: boolean;
|
|
584
583
|
}) {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
584
|
+
// Random width between 50 to 90%.
|
|
585
|
+
const width = React.useMemo(() => {
|
|
586
|
+
return `${Math.floor(Math.random() * 40) + 50}%`;
|
|
587
|
+
}, []);
|
|
588
|
+
|
|
589
|
+
return (
|
|
590
|
+
<div
|
|
591
|
+
data-slot="sidebar-menu-skeleton"
|
|
592
|
+
data-sidebar="menu-skeleton"
|
|
593
|
+
className={cn('flex h-8 items-center gap-2 rounded-md px-2', className)}
|
|
594
|
+
{...props}
|
|
595
|
+
>
|
|
596
|
+
{showIcon && <Skeleton className="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />}
|
|
597
|
+
<Skeleton
|
|
598
|
+
className="h-4 max-w-(--skeleton-width) flex-1"
|
|
599
|
+
data-sidebar="menu-skeleton-text"
|
|
600
|
+
style={
|
|
601
|
+
{
|
|
602
|
+
'--skeleton-width': width,
|
|
603
|
+
} as React.CSSProperties
|
|
604
|
+
}
|
|
605
|
+
/>
|
|
606
|
+
</div>
|
|
607
|
+
);
|
|
609
608
|
}
|
|
610
609
|
|
|
611
610
|
/** A nested sub-menu list indented under a parent menu item with a left border. */
|
|
612
611
|
function SidebarMenuSub({ className, ...props }: React.ComponentProps<'ul'>) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
612
|
+
return (
|
|
613
|
+
<ul
|
|
614
|
+
data-slot="sidebar-menu-sub"
|
|
615
|
+
data-sidebar="menu-sub"
|
|
616
|
+
className={cn(
|
|
617
|
+
'border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5',
|
|
618
|
+
'group-data-[collapsible=icon]:hidden',
|
|
619
|
+
className,
|
|
620
|
+
)}
|
|
621
|
+
{...props}
|
|
622
|
+
/>
|
|
623
|
+
);
|
|
625
624
|
}
|
|
626
625
|
|
|
627
626
|
function SidebarMenuSubItem({ className, ...props }: React.ComponentProps<'li'>) {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
627
|
+
return (
|
|
628
|
+
<li
|
|
629
|
+
data-slot="sidebar-menu-sub-item"
|
|
630
|
+
data-sidebar="menu-sub-item"
|
|
631
|
+
className={cn('group/menu-sub-item relative', className)}
|
|
632
|
+
{...props}
|
|
633
|
+
/>
|
|
634
|
+
);
|
|
636
635
|
}
|
|
637
636
|
|
|
638
637
|
function SidebarMenuSubButton({
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
638
|
+
asChild = false,
|
|
639
|
+
size = 'md',
|
|
640
|
+
isActive = false,
|
|
641
|
+
className,
|
|
642
|
+
...props
|
|
644
643
|
}: React.ComponentProps<'a'> & {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
644
|
+
asChild?: boolean;
|
|
645
|
+
size?: 'sm' | 'md';
|
|
646
|
+
isActive?: boolean;
|
|
648
647
|
}) {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
648
|
+
const Comp = asChild ? Slot.Root : 'a';
|
|
649
|
+
|
|
650
|
+
return (
|
|
651
|
+
<Comp
|
|
652
|
+
data-slot="sidebar-menu-sub-button"
|
|
653
|
+
data-sidebar="menu-sub-button"
|
|
654
|
+
data-size={size}
|
|
655
|
+
data-active={isActive}
|
|
656
|
+
className={cn(
|
|
657
|
+
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
|
|
658
|
+
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
|
|
659
|
+
size === 'sm' && 'text-xs',
|
|
660
|
+
size === 'md' && 'text-sm',
|
|
661
|
+
'group-data-[collapsible=icon]:hidden',
|
|
662
|
+
className,
|
|
663
|
+
)}
|
|
664
|
+
{...props}
|
|
665
|
+
/>
|
|
666
|
+
);
|
|
668
667
|
}
|
|
669
668
|
|
|
670
669
|
export {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
670
|
+
Sidebar,
|
|
671
|
+
SidebarContent,
|
|
672
|
+
SidebarFooter,
|
|
673
|
+
SidebarGroup,
|
|
674
|
+
SidebarGroupAction,
|
|
675
|
+
SidebarGroupContent,
|
|
676
|
+
SidebarGroupLabel,
|
|
677
|
+
SidebarHeader,
|
|
678
|
+
SidebarInput,
|
|
679
|
+
SidebarInset,
|
|
680
|
+
SidebarMenu,
|
|
681
|
+
SidebarMenuAction,
|
|
682
|
+
SidebarMenuBadge,
|
|
683
|
+
SidebarMenuButton,
|
|
684
|
+
SidebarMenuItem,
|
|
685
|
+
SidebarMenuSkeleton,
|
|
686
|
+
SidebarMenuSub,
|
|
687
|
+
SidebarMenuSubButton,
|
|
688
|
+
SidebarMenuSubItem,
|
|
689
|
+
SidebarProvider,
|
|
690
|
+
SidebarRail,
|
|
691
|
+
SidebarSeparator,
|
|
692
|
+
SidebarTrigger,
|
|
693
|
+
useSidebar,
|
|
695
694
|
};
|