@mbao01/common 0.0.47 → 0.0.49
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/types/components/Command/Command.d.ts +6 -6
- package/dist/types/components/Form/MultiSelect/MultiSelect.d.ts +2 -2
- package/dist/types/components/Sidebar/Sidebar.d.ts +39 -0
- package/dist/types/components/Sidebar/SidebarContext.d.ts +2 -0
- package/dist/types/components/Sidebar/SidebarGroup.d.ts +13 -0
- package/dist/types/components/Sidebar/SidebarMenu.d.ts +30 -0
- package/dist/types/components/Sidebar/constants.d.ts +48 -0
- package/dist/types/components/Sidebar/hooks/index.d.ts +1 -0
- package/dist/types/components/Sidebar/hooks/useSidebar/index.d.ts +1 -0
- package/dist/types/components/Sidebar/hooks/useSidebar/useSidebar.d.ts +1 -0
- package/dist/types/components/Sidebar/index.d.ts +4 -0
- package/dist/types/components/Sidebar/stories/examples/Sidebar.example.d.ts +57 -0
- package/dist/types/components/Sidebar/stories/examples/components/AppMain.d.ts +3 -0
- package/dist/types/components/Sidebar/stories/examples/components/AppSidebar.d.ts +6 -0
- package/dist/types/components/Sidebar/stories/examples/components/SearchForm.d.ts +1 -0
- package/dist/types/components/Sidebar/stories/examples/components/VersionSwitcher.d.ts +4 -0
- package/dist/types/components/Sidebar/types.d.ts +55 -0
- package/dist/types/hooks/index.d.ts +1 -0
- package/dist/types/hooks/useIsMobile/index.d.ts +1 -0
- package/dist/types/hooks/useIsMobile/useIsMobile.d.ts +1 -0
- package/dist/types/index.d.ts +2 -0
- package/package.json +6 -6
- package/src/components/Anchor/Anchor.tsx +2 -2
- package/src/components/Calendar/Calendar.tsx +1 -1
- package/src/components/Carousel/Carousel.tsx +1 -1
- package/src/components/Chart/stories/examples/LineChart.tsx +2 -2
- package/src/components/Combobox/Combobox.tsx +2 -2
- package/src/components/Command/Command.tsx +2 -2
- package/src/components/DatePicker/DatePicker.tsx +2 -2
- package/src/components/DatePicker/DateRangePicker.tsx +2 -2
- package/src/components/DatePicker/MultipleDatesPicker.tsx +2 -2
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/FileUploader/FileUploader.tsx +2 -2
- package/src/components/Form/DatetimeInput/DatetimeCalendar.tsx +2 -2
- package/src/components/Form/MultiSelect/MultiSelect.tsx +2 -2
- package/src/components/Form/Select/Select.tsx +1 -1
- package/src/components/Form/TagsInput/TagsInput.tsx +2 -2
- package/src/components/Menu/ContextMenu/ContextMenu.tsx +2 -2
- package/src/components/Menu/DropdownMenu/DropdownMenu.tsx +2 -2
- package/src/components/Menu/Menubar/Menubar.tsx +2 -2
- package/src/components/Menu/NavigationMenu/NavigationMenu.tsx +1 -1
- package/src/components/Pagination/Pagination.tsx +2 -2
- package/src/components/Sidebar/Sidebar.tsx +326 -0
- package/src/components/Sidebar/SidebarContext.tsx +6 -0
- package/src/components/Sidebar/SidebarGroup.tsx +72 -0
- package/src/components/Sidebar/SidebarMenu.tsx +205 -0
- package/src/components/Sidebar/constants.ts +206 -0
- package/src/components/Sidebar/hooks/index.ts +1 -0
- package/src/components/Sidebar/hooks/useSidebar/index.ts +1 -0
- package/src/components/Sidebar/hooks/useSidebar/useSidebar.ts +11 -0
- package/src/components/Sidebar/index.ts +4 -0
- package/src/components/Sidebar/stories/examples/Sidebar.example.tsx +155 -0
- package/src/components/Sidebar/stories/examples/components/AppMain.tsx +36 -0
- package/src/components/Sidebar/stories/examples/components/AppSidebar.tsx +531 -0
- package/src/components/Sidebar/stories/examples/components/SearchForm.tsx +26 -0
- package/src/components/Sidebar/stories/examples/components/VersionSwitcher.tsx +45 -0
- package/src/components/Sidebar/types.ts +74 -0
- package/src/components/Widget/Widgets.example.tsx +2 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useIsMobile/index.ts +1 -0
- package/src/hooks/useIsMobile/useIsMobile.ts +19 -0
- package/src/index.ts +3 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { CSSProperties } from "react";
|
|
4
|
+
import { forwardRef, useMemo } from "react";
|
|
5
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
6
|
+
import type {
|
|
7
|
+
SidebarMenuActionProps,
|
|
8
|
+
SidebarMenuBadgeProps,
|
|
9
|
+
SidebarMenuButtonProps,
|
|
10
|
+
SidebarMenuItemProps,
|
|
11
|
+
SidebarMenuProps,
|
|
12
|
+
SidebarMenuSkeletonProps,
|
|
13
|
+
SidebarMenuSubButtonProps,
|
|
14
|
+
SidebarMenuSubItemProps,
|
|
15
|
+
SidebarMenuSubProps,
|
|
16
|
+
} from "./types";
|
|
17
|
+
import { cn } from "../../utilities";
|
|
18
|
+
import { Skeleton } from "../Skeleton";
|
|
19
|
+
import { Tooltip } from "../Tooltip";
|
|
20
|
+
import {
|
|
21
|
+
getSidebarMenuActionClasses,
|
|
22
|
+
getSidebarMenuBadgeClasses,
|
|
23
|
+
getSidebarMenuButtonClasses,
|
|
24
|
+
getSidebarMenuClasses,
|
|
25
|
+
getSidebarMenuItemClasses,
|
|
26
|
+
getSidebarMenuSkeletonClasses,
|
|
27
|
+
getSidebarMenuSubClasses,
|
|
28
|
+
getSidebarMenuSubItemClasses,
|
|
29
|
+
} from "./constants";
|
|
30
|
+
import { useSidebar } from "./hooks";
|
|
31
|
+
|
|
32
|
+
const SidebarMenu = ({ className, ...props }: SidebarMenuProps) => (
|
|
33
|
+
<ul data-sidebar="menu" className={cn(getSidebarMenuClasses(), className)} {...props} />
|
|
34
|
+
);
|
|
35
|
+
SidebarMenu.displayName = "SidebarMenu";
|
|
36
|
+
|
|
37
|
+
const SidebarMenuItem = forwardRef<HTMLLIElement, SidebarMenuItemProps>(
|
|
38
|
+
({ className, ...props }, ref) => (
|
|
39
|
+
<li
|
|
40
|
+
ref={ref}
|
|
41
|
+
data-sidebar="menu-item"
|
|
42
|
+
className={cn(getSidebarMenuItemClasses(), className)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
)
|
|
46
|
+
);
|
|
47
|
+
SidebarMenuItem.displayName = "SidebarMenuItem";
|
|
48
|
+
|
|
49
|
+
const SidebarMenuButton = forwardRef<HTMLButtonElement, SidebarMenuButtonProps>(
|
|
50
|
+
(
|
|
51
|
+
{
|
|
52
|
+
asChild = false,
|
|
53
|
+
isActive = false,
|
|
54
|
+
variant = "default",
|
|
55
|
+
size = "default",
|
|
56
|
+
tooltip,
|
|
57
|
+
className,
|
|
58
|
+
...props
|
|
59
|
+
},
|
|
60
|
+
ref
|
|
61
|
+
) => {
|
|
62
|
+
const Comp = asChild ? Slot : "button";
|
|
63
|
+
const { isMobile, state } = useSidebar();
|
|
64
|
+
|
|
65
|
+
const button = (
|
|
66
|
+
<Comp
|
|
67
|
+
ref={ref}
|
|
68
|
+
data-sidebar="menu-button"
|
|
69
|
+
data-size={size}
|
|
70
|
+
data-active={isActive}
|
|
71
|
+
className={cn(getSidebarMenuButtonClasses({ variant, size }), className)}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (!tooltip) {
|
|
77
|
+
return button;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (typeof tooltip === "string") {
|
|
81
|
+
tooltip = {
|
|
82
|
+
children: tooltip,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Tooltip>
|
|
88
|
+
<Tooltip.Trigger asChild>{button}</Tooltip.Trigger>
|
|
89
|
+
<Tooltip.Content
|
|
90
|
+
side="right"
|
|
91
|
+
align="center"
|
|
92
|
+
hidden={state !== "collapsed" || isMobile}
|
|
93
|
+
{...tooltip}
|
|
94
|
+
/>
|
|
95
|
+
</Tooltip>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
SidebarMenuButton.displayName = "SidebarMenuButton";
|
|
100
|
+
|
|
101
|
+
const SidebarMenuAction = forwardRef<HTMLButtonElement, SidebarMenuActionProps>(
|
|
102
|
+
({ className, asChild = false, showOnHover = false, ...props }, ref) => {
|
|
103
|
+
const Comp = asChild ? Slot : "button";
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<Comp
|
|
107
|
+
ref={ref}
|
|
108
|
+
data-sidebar="menu-action"
|
|
109
|
+
className={cn(getSidebarMenuActionClasses({ showOnHover }), className)}
|
|
110
|
+
{...props}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
SidebarMenuAction.displayName = "SidebarMenuAction";
|
|
116
|
+
|
|
117
|
+
const SidebarMenuBadge = forwardRef<HTMLDivElement, SidebarMenuBadgeProps>(
|
|
118
|
+
({ className, ...props }, ref) => (
|
|
119
|
+
<div
|
|
120
|
+
ref={ref}
|
|
121
|
+
data-sidebar="menu-badge"
|
|
122
|
+
className={cn(getSidebarMenuBadgeClasses(), className)}
|
|
123
|
+
{...props}
|
|
124
|
+
/>
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
SidebarMenuBadge.displayName = "SidebarMenuBadge";
|
|
128
|
+
|
|
129
|
+
const SidebarMenuSkeleton = forwardRef<HTMLDivElement, SidebarMenuSkeletonProps>(
|
|
130
|
+
({ className, showIcon = false, ...props }, ref) => {
|
|
131
|
+
// Random width between 50 to 90%.
|
|
132
|
+
const width = useMemo(() => {
|
|
133
|
+
return `${Math.floor(Math.random() * 40) + 50}%`;
|
|
134
|
+
}, []);
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<div
|
|
138
|
+
ref={ref}
|
|
139
|
+
data-sidebar="menu-skeleton"
|
|
140
|
+
className={cn(getSidebarMenuSkeletonClasses(), className)}
|
|
141
|
+
{...props}
|
|
142
|
+
>
|
|
143
|
+
{showIcon && (
|
|
144
|
+
<Skeleton width={4} height={4} className="rounded-md" data-sidebar="menu-skeleton-icon" />
|
|
145
|
+
)}
|
|
146
|
+
<Skeleton
|
|
147
|
+
className="h-4 flex-1 max-w-[--skeleton-width]"
|
|
148
|
+
data-sidebar="menu-skeleton-text"
|
|
149
|
+
style={
|
|
150
|
+
{
|
|
151
|
+
"--skeleton-width": width,
|
|
152
|
+
} as CSSProperties
|
|
153
|
+
}
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton";
|
|
160
|
+
|
|
161
|
+
const SidebarMenuSub = forwardRef<HTMLUListElement, SidebarMenuSubProps>(
|
|
162
|
+
({ className, ...props }, ref) => (
|
|
163
|
+
<ul
|
|
164
|
+
ref={ref}
|
|
165
|
+
data-sidebar="menu-sub"
|
|
166
|
+
className={cn(getSidebarMenuSubClasses(), className)}
|
|
167
|
+
{...props}
|
|
168
|
+
/>
|
|
169
|
+
)
|
|
170
|
+
);
|
|
171
|
+
SidebarMenuSub.displayName = "SidebarMenuSub";
|
|
172
|
+
|
|
173
|
+
const SidebarMenuSubItem = forwardRef<HTMLLIElement, SidebarMenuSubItemProps>(
|
|
174
|
+
({ ...props }, ref) => <li ref={ref} {...props} />
|
|
175
|
+
);
|
|
176
|
+
SidebarMenuSubItem.displayName = "SidebarMenuSubItem";
|
|
177
|
+
|
|
178
|
+
const SidebarMenuSubButton = forwardRef<HTMLAnchorElement, SidebarMenuSubButtonProps>(
|
|
179
|
+
({ asChild = false, size = "md", isActive, className, ...props }, ref) => {
|
|
180
|
+
const Comp = asChild ? Slot : "a";
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<Comp
|
|
184
|
+
ref={ref}
|
|
185
|
+
data-sidebar="menu-sub-button"
|
|
186
|
+
data-size={size}
|
|
187
|
+
data-active={isActive}
|
|
188
|
+
className={cn(getSidebarMenuSubItemClasses({ size }), className)}
|
|
189
|
+
{...props}
|
|
190
|
+
/>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
SidebarMenuSubButton.displayName = "SidebarMenuSubButton";
|
|
195
|
+
|
|
196
|
+
SidebarMenu.Item = SidebarMenuItem;
|
|
197
|
+
SidebarMenu.Button = SidebarMenuButton;
|
|
198
|
+
SidebarMenu.Action = SidebarMenuAction;
|
|
199
|
+
SidebarMenu.Badge = SidebarMenuBadge;
|
|
200
|
+
SidebarMenu.Skeleton = SidebarMenuSkeleton;
|
|
201
|
+
SidebarMenu.Sub = SidebarMenuSub;
|
|
202
|
+
SidebarMenu.SubItem = SidebarMenuSubItem;
|
|
203
|
+
SidebarMenu.SubButton = SidebarMenuSubButton;
|
|
204
|
+
|
|
205
|
+
export { SidebarMenu };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { cva } from "../../libs";
|
|
2
|
+
|
|
3
|
+
export const SIDEBAR_COOKIE_NAME = "sidebar:state";
|
|
4
|
+
|
|
5
|
+
export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
|
6
|
+
|
|
7
|
+
export const SIDEBAR_WIDTH = "16rem";
|
|
8
|
+
|
|
9
|
+
export const SIDEBAR_WIDTH_MOBILE = "18rem";
|
|
10
|
+
|
|
11
|
+
export const SIDEBAR_WIDTH_ICON = "3rem";
|
|
12
|
+
|
|
13
|
+
export const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
|
14
|
+
|
|
15
|
+
export const getSidebarClasses = cva([], {
|
|
16
|
+
variants: {
|
|
17
|
+
collapsible: {
|
|
18
|
+
none: "flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const getSidebarMobileClasses = cva([], {
|
|
24
|
+
variants: {
|
|
25
|
+
isMobile: {
|
|
26
|
+
true: "w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden",
|
|
27
|
+
false:
|
|
28
|
+
"flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const getSidebarOuterClasses = cva("group peer hidden md:block text-sidebar-foreground");
|
|
34
|
+
|
|
35
|
+
export const getSidebarInnerClasses = cva(
|
|
36
|
+
"duration-200 fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] ease-linear md:flex",
|
|
37
|
+
{
|
|
38
|
+
variants: {
|
|
39
|
+
side: {
|
|
40
|
+
left: "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]",
|
|
41
|
+
right: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
|
|
42
|
+
},
|
|
43
|
+
variant: {
|
|
44
|
+
floating:
|
|
45
|
+
"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]",
|
|
46
|
+
inset:
|
|
47
|
+
"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]",
|
|
48
|
+
sidebar:
|
|
49
|
+
"group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export const getSidebarGapClasses = cva(
|
|
56
|
+
[
|
|
57
|
+
"duration-200 relative h-svh w-[--sidebar-width] bg-transparent transition-[width] ease-linear",
|
|
58
|
+
"group-data-[collapsible=offcanvas]:w-0",
|
|
59
|
+
"group-data-[side=right]:rotate-180",
|
|
60
|
+
],
|
|
61
|
+
{
|
|
62
|
+
variants: {
|
|
63
|
+
variant: {
|
|
64
|
+
floating:
|
|
65
|
+
"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]",
|
|
66
|
+
inset:
|
|
67
|
+
"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]",
|
|
68
|
+
sidebar: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
compoundVariants: [
|
|
72
|
+
{
|
|
73
|
+
variant: null,
|
|
74
|
+
className: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
export const getSidebarTriggerClasses = cva("h-7 w-7 p-0");
|
|
81
|
+
|
|
82
|
+
export const getSidebarRailClasses = cva([
|
|
83
|
+
"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex",
|
|
84
|
+
"[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize",
|
|
85
|
+
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
|
86
|
+
"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar",
|
|
87
|
+
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
|
88
|
+
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
export const getSidebarInsetClasses = cva([
|
|
92
|
+
"relative flex min-h-svh flex-1 flex-col bg-background",
|
|
93
|
+
"peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
// TODO
|
|
97
|
+
export const getSidebarInputClasses = cva(
|
|
98
|
+
"h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring"
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
export const getSidebarHeaderClasses = cva("flex flex-col gap-2 p-2");
|
|
102
|
+
|
|
103
|
+
export const getSidebarFooterClasses = cva("flex flex-col gap-2 p-2");
|
|
104
|
+
|
|
105
|
+
export const getSidebarSeparatorClasses = cva("mx-2 w-auto bg-sidebar-border");
|
|
106
|
+
|
|
107
|
+
export const getSidebarContentClasses = cva(
|
|
108
|
+
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden"
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
export const getSidebarProviderClasses = cva(
|
|
112
|
+
"group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar"
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
export const getSidebarGroupClasses = cva("relative flex w-full min-w-0 flex-col p-2");
|
|
116
|
+
|
|
117
|
+
export const getSidebarGroupLabelClasses = cva([
|
|
118
|
+
"duration-200 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,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
119
|
+
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
|
|
120
|
+
]);
|
|
121
|
+
|
|
122
|
+
export const getSidebarGroupActionClasses = cva([
|
|
123
|
+
"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",
|
|
124
|
+
// Increases the hit area of the button on mobile.
|
|
125
|
+
"after:absolute after:-inset-2 after:md:hidden",
|
|
126
|
+
"group-data-[collapsible=icon]:hidden",
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
export const getSidebarGroupContentClasses = cva("w-full text-sm");
|
|
130
|
+
|
|
131
|
+
export const getSidebarMenuClasses = cva("flex w-full min-w-0 flex-col gap-1");
|
|
132
|
+
|
|
133
|
+
export const getSidebarMenuItemClasses = cva("group/menu-item relative");
|
|
134
|
+
|
|
135
|
+
export const getSidebarMenuButtonClasses = cva(
|
|
136
|
+
"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",
|
|
137
|
+
{
|
|
138
|
+
variants: {
|
|
139
|
+
variant: {
|
|
140
|
+
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
141
|
+
outline:
|
|
142
|
+
"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))]",
|
|
143
|
+
},
|
|
144
|
+
size: {
|
|
145
|
+
default: "h-8 text-sm",
|
|
146
|
+
sm: "h-7 text-xs",
|
|
147
|
+
lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0",
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
defaultVariants: {
|
|
151
|
+
variant: "default",
|
|
152
|
+
size: "default",
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
export const getSidebarMenuActionClasses = cva(
|
|
158
|
+
[
|
|
159
|
+
"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",
|
|
160
|
+
// Increases the hit area of the button on mobile.
|
|
161
|
+
"after:absolute after:-inset-2 after:md:hidden",
|
|
162
|
+
"peer-data-[size=sm]/menu-button:top-1",
|
|
163
|
+
"peer-data-[size=default]/menu-button:top-1.5",
|
|
164
|
+
"peer-data-[size=lg]/menu-button:top-2.5",
|
|
165
|
+
"group-data-[collapsible=icon]:hidden",
|
|
166
|
+
],
|
|
167
|
+
{
|
|
168
|
+
variants: {
|
|
169
|
+
showOnHover: {
|
|
170
|
+
true: "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0",
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
export const getSidebarMenuBadgeClasses = cva([
|
|
177
|
+
"absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground select-none pointer-events-none",
|
|
178
|
+
"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
|
|
179
|
+
"peer-data-[size=sm]/menu-button:top-1",
|
|
180
|
+
"peer-data-[size=default]/menu-button:top-1.5",
|
|
181
|
+
"peer-data-[size=lg]/menu-button:top-2.5",
|
|
182
|
+
"group-data-[collapsible=icon]:hidden",
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
export const getSidebarMenuSkeletonClasses = cva("rounded-md h-8 flex gap-2 px-2 items-center");
|
|
186
|
+
|
|
187
|
+
export const getSidebarMenuSubClasses = cva([
|
|
188
|
+
"mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5",
|
|
189
|
+
"group-data-[collapsible=icon]:hidden",
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
export const getSidebarMenuSubItemClasses = cva(
|
|
193
|
+
[
|
|
194
|
+
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring 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 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
|
|
195
|
+
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
|
|
196
|
+
"group-data-[collapsible=icon]:hidden",
|
|
197
|
+
],
|
|
198
|
+
{
|
|
199
|
+
variants: {
|
|
200
|
+
size: {
|
|
201
|
+
sm: "text-xs",
|
|
202
|
+
md: "text-sm",
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useSidebar } from "./useSidebar";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useSidebar } from "./useSidebar";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { SidebarContext } from "../../SidebarContext";
|
|
3
|
+
|
|
4
|
+
export const useSidebar = () => {
|
|
5
|
+
const context = useContext(SidebarContext);
|
|
6
|
+
if (!context) {
|
|
7
|
+
throw new Error("useSidebar must be used within a <Sidebar.Provider />");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return context;
|
|
11
|
+
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Button } from "../../../Button";
|
|
3
|
+
import { Dialog } from "../../../Dialog";
|
|
4
|
+
import { Sidebar } from "../../Sidebar";
|
|
5
|
+
import { SidebarProps } from "../../types";
|
|
6
|
+
import { AppMain } from "./components/AppMain";
|
|
7
|
+
import {
|
|
8
|
+
AppSidebar,
|
|
9
|
+
AppSidebarCollapsibleTree,
|
|
10
|
+
AppSidebarWithCollapsibleGroup,
|
|
11
|
+
AppSidebarWithSecondaryNavigation,
|
|
12
|
+
AppSidebarWithSubMenu,
|
|
13
|
+
} from "./components/AppSidebar";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Here's the basic style setup. In your css file e.g tailwind.css, add
|
|
17
|
+
*
|
|
18
|
+
```css
|
|
19
|
+
\@layer base {
|
|
20
|
+
:root {
|
|
21
|
+
--sidebar-background: 0 0% 98%;
|
|
22
|
+
--sidebar-foreground: 240 5.3% 26.1%;
|
|
23
|
+
--sidebar-primary: 240 5.9% 10%;
|
|
24
|
+
--sidebar-primary-foreground: 0 0% 98%;
|
|
25
|
+
--sidebar-accent: 240 4.8% 95.9%;
|
|
26
|
+
--sidebar-accent-foreground: 240 5.9% 10%;
|
|
27
|
+
--sidebar-border: 220 13% 91%;
|
|
28
|
+
--sidebar-ring: 217.2 91.2% 59.8%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.dark {
|
|
32
|
+
--sidebar-background: 240 5.9% 10%;
|
|
33
|
+
--sidebar-foreground: 240 4.8% 95.9%;
|
|
34
|
+
--sidebar-primary: 224.3 76.3% 48%;
|
|
35
|
+
--sidebar-primary-foreground: 0 0% 100%;
|
|
36
|
+
--sidebar-accent: 240 3.7% 15.9%;
|
|
37
|
+
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
|
38
|
+
--sidebar-border: 240 3.7% 15.9%;
|
|
39
|
+
--sidebar-ring: 217.2 91.2% 59.8%;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
*
|
|
44
|
+
* then add this config in tailwind.config.ts
|
|
45
|
+
*
|
|
46
|
+
```js
|
|
47
|
+
{
|
|
48
|
+
sidebar: {
|
|
49
|
+
DEFAULT: 'hsl(var(--sidebar-background))',
|
|
50
|
+
foreground: 'hsl(var(--sidebar-foreground))',
|
|
51
|
+
primary: 'hsl(var(--sidebar-primary))',
|
|
52
|
+
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
|
|
53
|
+
accent: 'hsl(var(--sidebar-accent))',
|
|
54
|
+
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
|
|
55
|
+
border: 'hsl(var(--sidebar-border))',
|
|
56
|
+
ring: 'hsl(var(--sidebar-ring))',
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
* @param props
|
|
61
|
+
* @returns
|
|
62
|
+
*/
|
|
63
|
+
export const SidebarExample = (props: SidebarProps) => {
|
|
64
|
+
return (
|
|
65
|
+
<Sidebar.Provider>
|
|
66
|
+
<AppSidebar {...props} />
|
|
67
|
+
|
|
68
|
+
<AppMain side={props.side} />
|
|
69
|
+
</Sidebar.Provider>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const SidebarWithCollapsibleGroupExample = (props: SidebarProps) => {
|
|
74
|
+
return (
|
|
75
|
+
<Sidebar.Provider>
|
|
76
|
+
<AppSidebarWithCollapsibleGroup {...props} />
|
|
77
|
+
|
|
78
|
+
<AppMain side={props.side} />
|
|
79
|
+
</Sidebar.Provider>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const SidebarWithSubMenuExample = (props: SidebarProps) => {
|
|
84
|
+
return (
|
|
85
|
+
<Sidebar.Provider>
|
|
86
|
+
<AppSidebarWithSubMenu {...props} />
|
|
87
|
+
|
|
88
|
+
<AppMain side={props.side} />
|
|
89
|
+
</Sidebar.Provider>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const SidebarWithSecondaryNavigationExample = (props: SidebarProps) => {
|
|
94
|
+
return (
|
|
95
|
+
<Sidebar.Provider>
|
|
96
|
+
<AppSidebarWithSecondaryNavigation {...props} />
|
|
97
|
+
|
|
98
|
+
<AppMain side={props.side} />
|
|
99
|
+
</Sidebar.Provider>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const SidebarWithCollapsibleTreeExample = (props: SidebarProps) => {
|
|
104
|
+
return (
|
|
105
|
+
<Sidebar.Provider>
|
|
106
|
+
<AppSidebarCollapsibleTree {...props} />
|
|
107
|
+
|
|
108
|
+
<AppMain side={props.side} />
|
|
109
|
+
</Sidebar.Provider>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const SidebarInADialogExample = (props: SidebarProps) => {
|
|
114
|
+
const [open, setOpen] = useState(false);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
118
|
+
<Dialog.Trigger asChild>
|
|
119
|
+
<Button size="sm">Open Dialog</Button>
|
|
120
|
+
</Dialog.Trigger>
|
|
121
|
+
<Dialog.Content className="overflow-hidden p-0 md:max-h-[500px] md:max-w-[700px] lg:max-w-[800px]">
|
|
122
|
+
<Dialog.Title className="sr-only">Settings</Dialog.Title>
|
|
123
|
+
<Dialog.Description className="sr-only">Customize your settings here.</Dialog.Description>
|
|
124
|
+
|
|
125
|
+
<Sidebar.Provider>
|
|
126
|
+
<AppSidebarWithSubMenu {...props} />
|
|
127
|
+
|
|
128
|
+
<AppMain side={props.side} />
|
|
129
|
+
</Sidebar.Provider>
|
|
130
|
+
</Dialog.Content>
|
|
131
|
+
</Dialog>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const SidebarOnTheRightExample = (props: SidebarProps) => {
|
|
136
|
+
return (
|
|
137
|
+
<Sidebar.Provider>
|
|
138
|
+
<AppMain side={props.side} />
|
|
139
|
+
|
|
140
|
+
<AppSidebarWithCollapsibleGroup {...props} />
|
|
141
|
+
</Sidebar.Provider>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export const SidebarLeftAndRightExample = (props: SidebarProps) => {
|
|
146
|
+
return (
|
|
147
|
+
<Sidebar.Provider>
|
|
148
|
+
<AppSidebar {...props} />
|
|
149
|
+
|
|
150
|
+
<AppMain side={props.side} />
|
|
151
|
+
|
|
152
|
+
<AppSidebarCollapsibleTree collapsible="none" side="right" />
|
|
153
|
+
</Sidebar.Provider>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { HTMLAttributes } from "react";
|
|
2
|
+
import { Breadcrumbs } from "../../../../Breadcrumbs";
|
|
3
|
+
import { Breadcrumb } from "../../../../Breadcrumbs/Breadcrumbs";
|
|
4
|
+
import { Sidebar } from "../../../Sidebar";
|
|
5
|
+
import { type SidebarProps } from "../../../types";
|
|
6
|
+
|
|
7
|
+
export const AppMain = ({
|
|
8
|
+
side,
|
|
9
|
+
...props
|
|
10
|
+
}: HTMLAttributes<HTMLDivElement> & Pick<SidebarProps, "side">) => {
|
|
11
|
+
return (
|
|
12
|
+
<Sidebar.Inset {...props}>
|
|
13
|
+
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
|
|
14
|
+
{side !== "right" ? <Sidebar.Trigger className="-ml-1" /> : null}
|
|
15
|
+
<Breadcrumbs>
|
|
16
|
+
<Breadcrumb className="hidden md:block">
|
|
17
|
+
<a href="#">Building Your Application</a>
|
|
18
|
+
</Breadcrumb>
|
|
19
|
+
|
|
20
|
+
<Breadcrumb>
|
|
21
|
+
<span>Data Fetching</span>
|
|
22
|
+
</Breadcrumb>
|
|
23
|
+
</Breadcrumbs>
|
|
24
|
+
{side === "right" ? <Sidebar.Trigger className="-mr-1 ml-auto !rotate-180" /> : null}
|
|
25
|
+
</header>
|
|
26
|
+
<div className="flex flex-1 flex-col gap-4 p-4">
|
|
27
|
+
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
|
|
28
|
+
<div className="aspect-video rounded-xl bg-base-200/50" />
|
|
29
|
+
<div className="aspect-video rounded-xl bg-base-200/50" />
|
|
30
|
+
<div className="aspect-video rounded-xl bg-base-200/50" />
|
|
31
|
+
</div>
|
|
32
|
+
<div className="min-h-[100vh] flex-1 rounded-xl bg-base-200/50 md:min-h-min" />
|
|
33
|
+
</div>
|
|
34
|
+
</Sidebar.Inset>
|
|
35
|
+
);
|
|
36
|
+
};
|