@ptengine/design-components 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/README.md +309 -0
  2. package/components.json +21 -0
  3. package/dist/components/ui/accordion.d.ts +8 -0
  4. package/dist/components/ui/accordion.d.ts.map +1 -0
  5. package/dist/components/ui/alert-dialog.d.ts +21 -0
  6. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  7. package/dist/components/ui/alert.d.ts +9 -0
  8. package/dist/components/ui/alert.d.ts.map +1 -0
  9. package/dist/components/ui/aspect-ratio.d.ts +4 -0
  10. package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
  11. package/dist/components/ui/attachment.d.ts +26 -0
  12. package/dist/components/ui/attachment.d.ts.map +1 -0
  13. package/dist/components/ui/avatar.d.ts +10 -0
  14. package/dist/components/ui/avatar.d.ts.map +1 -0
  15. package/dist/components/ui/badge.d.ts +10 -0
  16. package/dist/components/ui/badge.d.ts.map +1 -0
  17. package/dist/components/ui/breadcrumb.d.ts +22 -0
  18. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  19. package/dist/components/ui/bubble.d.ts +19 -0
  20. package/dist/components/ui/bubble.d.ts.map +1 -0
  21. package/dist/components/ui/button-group.d.ts +13 -0
  22. package/dist/components/ui/button-group.d.ts.map +1 -0
  23. package/dist/components/ui/button.d.ts +13 -0
  24. package/dist/components/ui/button.d.ts.map +1 -0
  25. package/dist/components/ui/calendar.d.ts +9 -0
  26. package/dist/components/ui/calendar.d.ts.map +1 -0
  27. package/dist/components/ui/card.d.ts +9 -0
  28. package/dist/components/ui/card.d.ts.map +1 -0
  29. package/dist/components/ui/carousel.d.ts +23 -0
  30. package/dist/components/ui/carousel.d.ts.map +1 -0
  31. package/dist/components/ui/chart.d.ts +63 -0
  32. package/dist/components/ui/chart.d.ts.map +1 -0
  33. package/dist/components/ui/checkbox.d.ts +5 -0
  34. package/dist/components/ui/checkbox.d.ts.map +1 -0
  35. package/dist/components/ui/collapsible.d.ts +6 -0
  36. package/dist/components/ui/collapsible.d.ts.map +1 -0
  37. package/dist/components/ui/command.d.ts +81 -0
  38. package/dist/components/ui/command.d.ts.map +1 -0
  39. package/dist/components/ui/context-menu.d.ts +28 -0
  40. package/dist/components/ui/context-menu.d.ts.map +1 -0
  41. package/dist/components/ui/dialog.d.ts +22 -0
  42. package/dist/components/ui/dialog.d.ts.map +1 -0
  43. package/dist/components/ui/direction.d.ts +8 -0
  44. package/dist/components/ui/direction.d.ts.map +1 -0
  45. package/dist/components/ui/drawer.d.ts +23 -0
  46. package/dist/components/ui/drawer.d.ts.map +1 -0
  47. package/dist/components/ui/dropdown-menu.d.ts +28 -0
  48. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  49. package/dist/components/ui/empty.d.ts +13 -0
  50. package/dist/components/ui/empty.d.ts.map +1 -0
  51. package/dist/components/ui/field.d.ts +25 -0
  52. package/dist/components/ui/field.d.ts.map +1 -0
  53. package/dist/components/ui/hover-card.d.ts +7 -0
  54. package/dist/components/ui/hover-card.d.ts.map +1 -0
  55. package/dist/components/ui/input-group.d.ts +13 -0
  56. package/dist/components/ui/input-group.d.ts.map +1 -0
  57. package/dist/components/ui/input-otp.d.ts +35 -0
  58. package/dist/components/ui/input-otp.d.ts.map +1 -0
  59. package/dist/components/ui/input.d.ts +5 -0
  60. package/dist/components/ui/input.d.ts.map +1 -0
  61. package/dist/components/ui/item.d.ts +24 -0
  62. package/dist/components/ui/item.d.ts.map +1 -0
  63. package/dist/components/ui/kbd.d.ts +5 -0
  64. package/dist/components/ui/kbd.d.ts.map +1 -0
  65. package/dist/components/ui/label.d.ts +6 -0
  66. package/dist/components/ui/label.d.ts.map +1 -0
  67. package/dist/components/ui/marker.d.ts +13 -0
  68. package/dist/components/ui/marker.d.ts.map +1 -0
  69. package/dist/components/ui/menubar.d.ts +29 -0
  70. package/dist/components/ui/menubar.d.ts.map +1 -0
  71. package/dist/components/ui/message-scroller.d.ts +26 -0
  72. package/dist/components/ui/message-scroller.d.ts.map +1 -0
  73. package/dist/components/ui/message.d.ts +11 -0
  74. package/dist/components/ui/message.d.ts.map +1 -0
  75. package/dist/components/ui/native-select.d.ts +4 -0
  76. package/dist/components/ui/native-select.d.ts.map +1 -0
  77. package/dist/components/ui/navigation-menu.d.ts +13 -0
  78. package/dist/components/ui/navigation-menu.d.ts.map +1 -0
  79. package/dist/components/ui/pagination.d.ts +35 -0
  80. package/dist/components/ui/pagination.d.ts.map +1 -0
  81. package/dist/components/ui/popover.d.ts +8 -0
  82. package/dist/components/ui/popover.d.ts.map +1 -0
  83. package/dist/components/ui/progress.d.ts +5 -0
  84. package/dist/components/ui/progress.d.ts.map +1 -0
  85. package/dist/components/ui/radio-group.d.ts +6 -0
  86. package/dist/components/ui/radio-group.d.ts.map +1 -0
  87. package/dist/components/ui/resizable.d.ts +24 -0
  88. package/dist/components/ui/resizable.d.ts.map +1 -0
  89. package/dist/components/ui/scroll-area.d.ts +6 -0
  90. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  91. package/dist/components/ui/search-input.d.ts +23 -0
  92. package/dist/components/ui/search-input.d.ts.map +1 -0
  93. package/dist/components/ui/select.d.ts +14 -0
  94. package/dist/components/ui/select.d.ts.map +1 -0
  95. package/dist/components/ui/separator.d.ts +5 -0
  96. package/dist/components/ui/separator.d.ts.map +1 -0
  97. package/dist/components/ui/sheet.d.ts +28 -0
  98. package/dist/components/ui/sheet.d.ts.map +1 -0
  99. package/dist/components/ui/sidebar.d.ts +70 -0
  100. package/dist/components/ui/sidebar.d.ts.map +1 -0
  101. package/dist/components/ui/skeleton.d.ts +4 -0
  102. package/dist/components/ui/skeleton.d.ts.map +1 -0
  103. package/dist/components/ui/slider.d.ts +5 -0
  104. package/dist/components/ui/slider.d.ts.map +1 -0
  105. package/dist/components/ui/sonner.d.ts +6 -0
  106. package/dist/components/ui/sonner.d.ts.map +1 -0
  107. package/dist/components/ui/spinner.d.ts +7 -0
  108. package/dist/components/ui/spinner.d.ts.map +1 -0
  109. package/dist/components/ui/switch.d.ts +5 -0
  110. package/dist/components/ui/switch.d.ts.map +1 -0
  111. package/dist/components/ui/table.d.ts +11 -0
  112. package/dist/components/ui/table.d.ts.map +1 -0
  113. package/dist/components/ui/tabs.d.ts +13 -0
  114. package/dist/components/ui/tabs.d.ts.map +1 -0
  115. package/dist/components/ui/textarea.d.ts +5 -0
  116. package/dist/components/ui/textarea.d.ts.map +1 -0
  117. package/dist/components/ui/toggle-group.d.ts +13 -0
  118. package/dist/components/ui/toggle-group.d.ts.map +1 -0
  119. package/dist/components/ui/toggle.d.ts +13 -0
  120. package/dist/components/ui/toggle.d.ts.map +1 -0
  121. package/dist/components/ui/tooltip.d.ts +8 -0
  122. package/dist/components/ui/tooltip.d.ts.map +1 -0
  123. package/dist/hooks/use-mobile.d.ts +2 -0
  124. package/dist/hooks/use-mobile.d.ts.map +1 -0
  125. package/dist/index.cjs +82 -0
  126. package/dist/index.d.ts +71 -0
  127. package/dist/index.d.ts.map +1 -0
  128. package/dist/index.js +20969 -0
  129. package/dist/lib/utils.d.ts +4 -0
  130. package/dist/lib/utils.d.ts.map +1 -0
  131. package/llms.txt +173 -0
  132. package/package.json +125 -0
  133. package/src/components/ui/accordion.tsx +51 -0
  134. package/src/components/ui/alert-dialog.tsx +115 -0
  135. package/src/components/ui/alert.tsx +48 -0
  136. package/src/components/ui/aspect-ratio.tsx +5 -0
  137. package/src/components/ui/attachment.tsx +186 -0
  138. package/src/components/ui/avatar.tsx +57 -0
  139. package/src/components/ui/badge.tsx +40 -0
  140. package/src/components/ui/breadcrumb.tsx +101 -0
  141. package/src/components/ui/bubble.tsx +123 -0
  142. package/src/components/ui/button-group.tsx +74 -0
  143. package/src/components/ui/button.tsx +64 -0
  144. package/src/components/ui/calendar.tsx +63 -0
  145. package/src/components/ui/card.tsx +55 -0
  146. package/src/components/ui/carousel.tsx +252 -0
  147. package/src/components/ui/chart.tsx +323 -0
  148. package/src/components/ui/checkbox.tsx +26 -0
  149. package/src/components/ui/collapsible.tsx +9 -0
  150. package/src/components/ui/command.tsx +136 -0
  151. package/src/components/ui/context-menu.tsx +187 -0
  152. package/src/components/ui/dialog.tsx +103 -0
  153. package/src/components/ui/direction.tsx +15 -0
  154. package/src/components/ui/drawer.tsx +98 -0
  155. package/src/components/ui/dropdown-menu.tsx +186 -0
  156. package/src/components/ui/empty.tsx +75 -0
  157. package/src/components/ui/field.tsx +175 -0
  158. package/src/components/ui/hover-card.tsx +27 -0
  159. package/src/components/ui/input-group.tsx +80 -0
  160. package/src/components/ui/input-otp.tsx +67 -0
  161. package/src/components/ui/input.tsx +24 -0
  162. package/src/components/ui/item.tsx +118 -0
  163. package/src/components/ui/kbd.tsx +21 -0
  164. package/src/components/ui/label.tsx +19 -0
  165. package/src/components/ui/marker.tsx +67 -0
  166. package/src/components/ui/menubar.tsx +218 -0
  167. package/src/components/ui/message-scroller.tsx +206 -0
  168. package/src/components/ui/message.tsx +85 -0
  169. package/src/components/ui/native-select.tsx +29 -0
  170. package/src/components/ui/navigation-menu.tsx +120 -0
  171. package/src/components/ui/pagination.tsx +108 -0
  172. package/src/components/ui/popover.tsx +31 -0
  173. package/src/components/ui/progress.tsx +23 -0
  174. package/src/components/ui/radio-group.tsx +36 -0
  175. package/src/components/ui/resizable.tsx +40 -0
  176. package/src/components/ui/scroll-area.tsx +44 -0
  177. package/src/components/ui/search-input.tsx +97 -0
  178. package/src/components/ui/select.tsx +151 -0
  179. package/src/components/ui/separator.tsx +24 -0
  180. package/src/components/ui/sheet.tsx +124 -0
  181. package/src/components/ui/sidebar.tsx +704 -0
  182. package/src/components/ui/skeleton.tsx +9 -0
  183. package/src/components/ui/slider.tsx +41 -0
  184. package/src/components/ui/sonner.tsx +45 -0
  185. package/src/components/ui/spinner.tsx +21 -0
  186. package/src/components/ui/switch.tsx +27 -0
  187. package/src/components/ui/table.tsx +95 -0
  188. package/src/components/ui/tabs.tsx +119 -0
  189. package/src/components/ui/textarea.tsx +23 -0
  190. package/src/components/ui/toggle-group.tsx +55 -0
  191. package/src/components/ui/toggle.tsx +40 -0
  192. package/src/components/ui/tooltip.tsx +30 -0
  193. package/src/hooks/use-mobile.ts +19 -0
  194. package/src/index.ts +414 -0
  195. package/src/lib/utils.ts +7 -0
  196. package/src/styles/tokens.css +360 -0
  197. package/tailwind.preset.js +235 -0
@@ -0,0 +1,704 @@
1
+ import * as React from 'react';
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+ import { PanelLeft } from 'lucide-react';
5
+
6
+ import { useIsMobile } from '../../hooks/use-mobile';
7
+ import { cn } from '../../lib/utils';
8
+ import { Button } from './button';
9
+ import { Input } from './input';
10
+ import { Separator } from './separator';
11
+ import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from './sheet';
12
+ import { Skeleton } from './skeleton';
13
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './tooltip';
14
+
15
+ const SIDEBAR_COOKIE_NAME = 'sidebar_state';
16
+ const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
17
+ const SIDEBAR_WIDTH = '16rem';
18
+ const SIDEBAR_WIDTH_MOBILE = '18rem';
19
+ const SIDEBAR_WIDTH_ICON = '3rem';
20
+ const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
21
+
22
+ type SidebarContextValue = {
23
+ state: 'expanded' | 'collapsed';
24
+ open: boolean;
25
+ setOpen: (open: boolean) => void;
26
+ openMobile: boolean;
27
+ setOpenMobile: (open: boolean) => void;
28
+ isMobile: boolean;
29
+ toggleSidebar: () => void;
30
+ };
31
+
32
+ const SidebarContext = React.createContext<SidebarContextValue | null>(null);
33
+
34
+ function useSidebar() {
35
+ const context = React.useContext(SidebarContext);
36
+ if (!context) {
37
+ throw new Error('useSidebar must be used within a SidebarProvider.');
38
+ }
39
+
40
+ return context;
41
+ }
42
+
43
+ const SidebarProvider = React.forwardRef<
44
+ HTMLDivElement,
45
+ React.ComponentProps<'div'> & {
46
+ defaultOpen?: boolean;
47
+ open?: boolean;
48
+ onOpenChange?: (open: boolean) => void;
49
+ }
50
+ >(
51
+ (
52
+ {
53
+ defaultOpen = true,
54
+ open: openProp,
55
+ onOpenChange: setOpenProp,
56
+ className,
57
+ style,
58
+ children,
59
+ ...props
60
+ },
61
+ ref
62
+ ) => {
63
+ const isMobile = useIsMobile();
64
+ const [openMobile, setOpenMobile] = React.useState(false);
65
+
66
+ const [_open, _setOpen] = React.useState(defaultOpen);
67
+ const open = openProp ?? _open;
68
+ const setOpen = React.useCallback(
69
+ (value: boolean | ((value: boolean) => boolean)) => {
70
+ const openState = typeof value === 'function' ? value(open) : value;
71
+ if (setOpenProp) {
72
+ setOpenProp(openState);
73
+ } else {
74
+ _setOpen(openState);
75
+ }
76
+
77
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
78
+ },
79
+ [setOpenProp, open]
80
+ );
81
+
82
+ const toggleSidebar = React.useCallback(() => {
83
+ return isMobile ? setOpenMobile(open => !open) : setOpen(open => !open);
84
+ }, [isMobile, setOpen, setOpenMobile]);
85
+
86
+ React.useEffect(() => {
87
+ const handleKeyDown = (event: KeyboardEvent) => {
88
+ if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
89
+ event.preventDefault();
90
+ toggleSidebar();
91
+ }
92
+ };
93
+
94
+ window.addEventListener('keydown', handleKeyDown);
95
+ return () => window.removeEventListener('keydown', handleKeyDown);
96
+ }, [toggleSidebar]);
97
+
98
+ const state = open ? 'expanded' : 'collapsed';
99
+
100
+ const contextValue = React.useMemo<SidebarContextValue>(
101
+ () => ({
102
+ state,
103
+ open,
104
+ setOpen,
105
+ isMobile,
106
+ openMobile,
107
+ setOpenMobile,
108
+ toggleSidebar
109
+ }),
110
+ [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
111
+ );
112
+
113
+ return (
114
+ <SidebarContext.Provider value={contextValue}>
115
+ <TooltipProvider delayDuration={0}>
116
+ <div
117
+ style={
118
+ {
119
+ '--pt-sidebar-width': SIDEBAR_WIDTH,
120
+ '--pt-sidebar-width-icon': SIDEBAR_WIDTH_ICON,
121
+ ...style
122
+ } as React.CSSProperties
123
+ }
124
+ className={cn(
125
+ 'group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar',
126
+ className
127
+ )}
128
+ ref={ref}
129
+ {...props}
130
+ >
131
+ {children}
132
+ </div>
133
+ </TooltipProvider>
134
+ </SidebarContext.Provider>
135
+ );
136
+ }
137
+ );
138
+ SidebarProvider.displayName = 'SidebarProvider';
139
+
140
+ const Sidebar = React.forwardRef<
141
+ HTMLDivElement,
142
+ React.ComponentProps<'div'> & {
143
+ side?: 'left' | 'right';
144
+ variant?: 'sidebar' | 'floating' | 'inset';
145
+ collapsible?: 'offcanvas' | 'icon' | 'none';
146
+ }
147
+ >(
148
+ (
149
+ { side = 'left', variant = 'sidebar', collapsible = 'offcanvas', className, children, ...props },
150
+ ref
151
+ ) => {
152
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
153
+
154
+ if (collapsible === 'none') {
155
+ return (
156
+ <div
157
+ className={cn(
158
+ 'flex h-full w-[--pt-sidebar-width] flex-col bg-sidebar text-sidebar-foreground',
159
+ className
160
+ )}
161
+ ref={ref}
162
+ {...props}
163
+ >
164
+ {children}
165
+ </div>
166
+ );
167
+ }
168
+
169
+ if (isMobile) {
170
+ return (
171
+ <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
172
+ <SheetContent
173
+ data-sidebar="sidebar"
174
+ data-mobile="true"
175
+ className="w-[--pt-sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
176
+ style={
177
+ {
178
+ '--pt-sidebar-width': SIDEBAR_WIDTH_MOBILE
179
+ } as React.CSSProperties
180
+ }
181
+ side={side}
182
+ >
183
+ <SheetHeader className="sr-only">
184
+ <SheetTitle>Sidebar</SheetTitle>
185
+ <SheetDescription>Displays the mobile sidebar.</SheetDescription>
186
+ </SheetHeader>
187
+ <div className="flex h-full w-full flex-col">{children}</div>
188
+ </SheetContent>
189
+ </Sheet>
190
+ );
191
+ }
192
+
193
+ return (
194
+ <div
195
+ ref={ref}
196
+ className="group peer hidden text-sidebar-foreground md:block"
197
+ data-state={state}
198
+ data-collapsible={state === 'collapsed' ? collapsible : ''}
199
+ data-variant={variant}
200
+ data-side={side}
201
+ >
202
+ <div
203
+ className={cn(
204
+ 'relative w-[--pt-sidebar-width] bg-transparent transition-[width] duration-200 ease-linear',
205
+ 'group-data-[collapsible=offcanvas]:w-0',
206
+ 'group-data-[side=right]:rotate-180',
207
+ variant === 'floating' || variant === 'inset'
208
+ ? 'group-data-[collapsible=icon]:w-[calc(var(--pt-sidebar-width-icon)_+_theme(spacing.4))]'
209
+ : 'group-data-[collapsible=icon]:w-[--pt-sidebar-width-icon]'
210
+ )}
211
+ />
212
+ <div
213
+ className={cn(
214
+ 'fixed inset-y-0 z-10 hidden h-svh w-[--pt-sidebar-width] transition-[left,right,width] duration-200 ease-linear md:flex',
215
+ side === 'left'
216
+ ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--pt-sidebar-width)*-1)]'
217
+ : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--pt-sidebar-width)*-1)]',
218
+ variant === 'floating' || variant === 'inset'
219
+ ? 'p-[8px] group-data-[collapsible=icon]:w-[calc(var(--pt-sidebar-width-icon)_+_theme(spacing.4)_+2px)]'
220
+ : 'group-data-[collapsible=icon]:w-[--pt-sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l',
221
+ className
222
+ )}
223
+ {...props}
224
+ data-sidebar="sidebar"
225
+ >
226
+ <div className="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-[var(--pt-radius)] group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border">
227
+ {children}
228
+ </div>
229
+ </div>
230
+ </div>
231
+ );
232
+ }
233
+ );
234
+ Sidebar.displayName = 'Sidebar';
235
+
236
+ /* label:开合钮读屏文案,库不做 i18n,消费方传译文(默认英文) */
237
+ const SidebarTrigger = React.forwardRef<
238
+ React.ElementRef<typeof Button>,
239
+ React.ComponentProps<typeof Button> & { label?: string }
240
+ >(({ className, onClick, label = 'Toggle sidebar', ...props }, ref) => {
241
+ const { toggleSidebar } = useSidebar();
242
+
243
+ return (
244
+ <Button
245
+ ref={ref}
246
+ data-sidebar="trigger"
247
+ variant="ghost"
248
+ size="icon"
249
+ className={cn('h-[28px] w-[28px]', className)}
250
+ onClick={event => {
251
+ onClick?.(event);
252
+ toggleSidebar();
253
+ }}
254
+ {...props}
255
+ >
256
+ <PanelLeft />
257
+ <span className="sr-only">{label}</span>
258
+ </Button>
259
+ );
260
+ });
261
+ SidebarTrigger.displayName = 'SidebarTrigger';
262
+
263
+ const SidebarRail = React.forwardRef<
264
+ HTMLButtonElement,
265
+ React.ComponentProps<'button'> & { label?: string }
266
+ >(({ className, label = 'Toggle sidebar', ...props }, ref) => {
267
+ const { toggleSidebar } = useSidebar();
268
+
269
+ return (
270
+ <button
271
+ ref={ref}
272
+ data-sidebar="rail"
273
+ aria-label={label}
274
+ tabIndex={-1}
275
+ onClick={toggleSidebar}
276
+ title={label}
277
+ className={cn(
278
+ 'absolute inset-y-0 z-20 hidden w-[16px] -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-[16px] group-data-[side=right]:left-0 sm:flex',
279
+ '[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize',
280
+ '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
281
+ 'group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar',
282
+ '[[data-side=left][data-collapsible=offcanvas]_&]:-right-[8px]',
283
+ '[[data-side=right][data-collapsible=offcanvas]_&]:-left-[8px]',
284
+ className
285
+ )}
286
+ {...props}
287
+ />
288
+ );
289
+ }
290
+ );
291
+ SidebarRail.displayName = 'SidebarRail';
292
+
293
+ const SidebarInset = React.forwardRef<HTMLDivElement, React.ComponentProps<'main'>>(
294
+ ({ className, ...props }, ref) => {
295
+ return (
296
+ <main
297
+ ref={ref}
298
+ className={cn(
299
+ 'relative flex w-full flex-1 flex-col bg-background',
300
+ 'md:peer-data-[variant=inset]:m-[8px] md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-[8px] md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-[12px]',
301
+ className
302
+ )}
303
+ {...props}
304
+ />
305
+ );
306
+ }
307
+ );
308
+ SidebarInset.displayName = 'SidebarInset';
309
+
310
+ const SidebarInput = React.forwardRef<
311
+ React.ElementRef<typeof Input>,
312
+ React.ComponentProps<typeof Input>
313
+ >(({ className, ...props }, ref) => {
314
+ return (
315
+ <Input
316
+ ref={ref}
317
+ data-sidebar="input"
318
+ className={cn('h-[32px] w-full bg-background', className)}
319
+ {...props}
320
+ />
321
+ );
322
+ });
323
+ SidebarInput.displayName = 'SidebarInput';
324
+
325
+ const SidebarHeader = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(
326
+ ({ className, ...props }, ref) => {
327
+ return (
328
+ <div
329
+ ref={ref}
330
+ data-sidebar="header"
331
+ className={cn('flex flex-col gap-[8px] p-[8px]', className)}
332
+ {...props}
333
+ />
334
+ );
335
+ }
336
+ );
337
+ SidebarHeader.displayName = 'SidebarHeader';
338
+
339
+ const SidebarFooter = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(
340
+ ({ className, ...props }, ref) => {
341
+ return (
342
+ <div
343
+ ref={ref}
344
+ data-sidebar="footer"
345
+ className={cn('flex flex-col gap-[8px] p-[8px]', className)}
346
+ {...props}
347
+ />
348
+ );
349
+ }
350
+ );
351
+ SidebarFooter.displayName = 'SidebarFooter';
352
+
353
+ const SidebarSeparator = React.forwardRef<
354
+ React.ElementRef<typeof Separator>,
355
+ React.ComponentProps<typeof Separator>
356
+ >(({ className, ...props }, ref) => {
357
+ return (
358
+ <Separator
359
+ ref={ref}
360
+ data-sidebar="separator"
361
+ className={cn('mx-[8px] w-auto bg-sidebar-border', className)}
362
+ {...props}
363
+ />
364
+ );
365
+ });
366
+ SidebarSeparator.displayName = 'SidebarSeparator';
367
+
368
+ const SidebarContent = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(
369
+ ({ className, ...props }, ref) => {
370
+ return (
371
+ <div
372
+ ref={ref}
373
+ data-sidebar="content"
374
+ className={cn(
375
+ 'flex min-h-0 flex-1 flex-col gap-[8px] overflow-auto group-data-[collapsible=icon]:overflow-hidden',
376
+ className
377
+ )}
378
+ {...props}
379
+ />
380
+ );
381
+ }
382
+ );
383
+ SidebarContent.displayName = 'SidebarContent';
384
+
385
+ const SidebarGroup = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(
386
+ ({ className, ...props }, ref) => {
387
+ return (
388
+ <div
389
+ ref={ref}
390
+ data-sidebar="group"
391
+ className={cn('relative flex w-full min-w-0 flex-col p-[8px]', className)}
392
+ {...props}
393
+ />
394
+ );
395
+ }
396
+ );
397
+ SidebarGroup.displayName = 'SidebarGroup';
398
+
399
+ const SidebarGroupLabel = React.forwardRef<
400
+ HTMLDivElement,
401
+ React.ComponentProps<'div'> & { asChild?: boolean }
402
+ >(({ className, asChild = false, ...props }, ref) => {
403
+ const Comp = asChild ? Slot : 'div';
404
+
405
+ return (
406
+ <Comp
407
+ ref={ref}
408
+ data-sidebar="group-label"
409
+ className={cn(
410
+ 'flex h-[32px] shrink-0 items-center rounded-[var(--pt-radius-md)] px-[8px] text-[13px] leading-[20px] font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-[16px] [&>svg]:shrink-0',
411
+ 'group-data-[collapsible=icon]:-mt-[32px] group-data-[collapsible=icon]:opacity-0',
412
+ className
413
+ )}
414
+ {...props}
415
+ />
416
+ );
417
+ });
418
+ SidebarGroupLabel.displayName = 'SidebarGroupLabel';
419
+
420
+ const SidebarGroupAction = React.forwardRef<
421
+ HTMLButtonElement,
422
+ React.ComponentProps<'button'> & { asChild?: boolean }
423
+ >(({ className, asChild = false, ...props }, ref) => {
424
+ const Comp = asChild ? Slot : 'button';
425
+
426
+ return (
427
+ <Comp
428
+ ref={ref}
429
+ data-sidebar="group-action"
430
+ className={cn(
431
+ 'absolute right-[12px] top-[14px] flex aspect-square w-[20px] items-center justify-center rounded-[var(--pt-radius-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-[16px] [&>svg]:shrink-0',
432
+ 'after:absolute after:-inset-[8px] after:md:hidden',
433
+ 'group-data-[collapsible=icon]:hidden',
434
+ className
435
+ )}
436
+ {...props}
437
+ />
438
+ );
439
+ });
440
+ SidebarGroupAction.displayName = 'SidebarGroupAction';
441
+
442
+ const SidebarGroupContent = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(
443
+ ({ className, ...props }, ref) => (
444
+ <div
445
+ ref={ref}
446
+ data-sidebar="group-content"
447
+ className={cn('w-full text-[14px] leading-[20px]', className)}
448
+ {...props}
449
+ />
450
+ )
451
+ );
452
+ SidebarGroupContent.displayName = 'SidebarGroupContent';
453
+
454
+ const SidebarMenu = React.forwardRef<HTMLUListElement, React.ComponentProps<'ul'>>(
455
+ ({ className, ...props }, ref) => (
456
+ <ul
457
+ ref={ref}
458
+ data-sidebar="menu"
459
+ className={cn('flex w-full min-w-0 flex-col gap-[4px]', className)}
460
+ {...props}
461
+ />
462
+ )
463
+ );
464
+ SidebarMenu.displayName = 'SidebarMenu';
465
+
466
+ const SidebarMenuItem = React.forwardRef<HTMLLIElement, React.ComponentProps<'li'>>(
467
+ ({ className, ...props }, ref) => (
468
+ <li
469
+ ref={ref}
470
+ data-sidebar="menu-item"
471
+ className={cn('group/menu-item relative', className)}
472
+ {...props}
473
+ />
474
+ )
475
+ );
476
+ SidebarMenuItem.displayName = 'SidebarMenuItem';
477
+
478
+ const sidebarMenuButtonVariants = cva(
479
+ 'peer/menu-button flex w-full items-center gap-[8px] overflow-hidden rounded-[var(--pt-radius-md)] p-[8px] text-left text-[14px] leading-[20px] text-sidebar-foreground 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-[32px] aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-selected 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-[32px] group-data-[collapsible=icon]:!p-[8px] [&>span:last-child]:truncate [&>svg]:size-[16px] [&>svg]:shrink-0',
480
+ {
481
+ variants: {
482
+ variant: {
483
+ default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
484
+ outline:
485
+ 'bg-background hover:bg-sidebar-accent hover:text-sidebar-accent-foreground'
486
+ },
487
+ size: {
488
+ default: 'h-[32px] text-[14px] leading-[20px]',
489
+ sm: 'h-[28px] text-[13px] leading-[20px]',
490
+ lg: 'h-[48px] text-[14px] leading-[20px] group-data-[collapsible=icon]:!p-0'
491
+ }
492
+ },
493
+ defaultVariants: {
494
+ variant: 'default',
495
+ size: 'default'
496
+ }
497
+ }
498
+ );
499
+
500
+ const SidebarMenuButton = React.forwardRef<
501
+ HTMLButtonElement,
502
+ React.ComponentProps<'button'> & {
503
+ asChild?: boolean;
504
+ isActive?: boolean;
505
+ tooltip?: string | React.ComponentProps<typeof TooltipContent>;
506
+ } & VariantProps<typeof sidebarMenuButtonVariants>
507
+ >(({ asChild = false, isActive = false, variant = 'default', size = 'default', tooltip, className, ...props }, ref) => {
508
+ const Comp = asChild ? Slot : 'button';
509
+ const { isMobile, state } = useSidebar();
510
+
511
+ const button = (
512
+ <Comp
513
+ ref={ref}
514
+ data-sidebar="menu-button"
515
+ data-size={size}
516
+ data-active={isActive}
517
+ className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
518
+ {...props}
519
+ />
520
+ );
521
+
522
+ if (!tooltip) {
523
+ return button;
524
+ }
525
+
526
+ if (typeof tooltip === 'string') {
527
+ tooltip = {
528
+ children: tooltip
529
+ };
530
+ }
531
+
532
+ return (
533
+ <Tooltip>
534
+ <TooltipTrigger asChild>{button}</TooltipTrigger>
535
+ <TooltipContent
536
+ side="right"
537
+ align="center"
538
+ hidden={state !== 'collapsed' || isMobile}
539
+ {...tooltip}
540
+ />
541
+ </Tooltip>
542
+ );
543
+ });
544
+ SidebarMenuButton.displayName = 'SidebarMenuButton';
545
+
546
+ const SidebarMenuAction = React.forwardRef<
547
+ HTMLButtonElement,
548
+ React.ComponentProps<'button'> & {
549
+ asChild?: boolean;
550
+ showOnHover?: boolean;
551
+ }
552
+ >(({ className, asChild = false, showOnHover = false, ...props }, ref) => {
553
+ const Comp = asChild ? Slot : 'button';
554
+
555
+ return (
556
+ <Comp
557
+ ref={ref}
558
+ data-sidebar="menu-action"
559
+ className={cn(
560
+ 'absolute right-[4px] top-[6px] flex aspect-square w-[20px] items-center justify-center rounded-[var(--pt-radius-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-[16px] [&>svg]:shrink-0',
561
+ 'after:absolute after:-inset-[8px] after:md:hidden',
562
+ 'peer-data-[size=sm]/menu-button:top-[4px]',
563
+ 'peer-data-[size=default]/menu-button:top-[6px]',
564
+ 'peer-data-[size=lg]/menu-button:top-[10px]',
565
+ 'group-data-[collapsible=icon]:hidden',
566
+ showOnHover &&
567
+ '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',
568
+ className
569
+ )}
570
+ {...props}
571
+ />
572
+ );
573
+ });
574
+ SidebarMenuAction.displayName = 'SidebarMenuAction';
575
+
576
+ const SidebarMenuBadge = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(
577
+ ({ className, ...props }, ref) => (
578
+ <div
579
+ ref={ref}
580
+ data-sidebar="menu-badge"
581
+ className={cn(
582
+ 'pointer-events-none absolute right-[4px] flex h-[20px] min-w-[20px] select-none items-center justify-center rounded-[var(--pt-radius-md)] px-[4px] text-[13px] leading-[20px] font-medium tabular-nums text-sidebar-foreground',
583
+ 'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
584
+ 'peer-data-[size=sm]/menu-button:top-[4px]',
585
+ 'peer-data-[size=default]/menu-button:top-[6px]',
586
+ 'peer-data-[size=lg]/menu-button:top-[10px]',
587
+ 'group-data-[collapsible=icon]:hidden',
588
+ className
589
+ )}
590
+ {...props}
591
+ />
592
+ )
593
+ );
594
+ SidebarMenuBadge.displayName = 'SidebarMenuBadge';
595
+
596
+ const SidebarMenuSkeleton = React.forwardRef<
597
+ HTMLDivElement,
598
+ React.ComponentProps<'div'> & {
599
+ showIcon?: boolean;
600
+ }
601
+ >(({ className, showIcon = false, ...props }, ref) => {
602
+ const width = React.useMemo(() => {
603
+ return `${Math.floor(Math.random() * 40) + 50}%`;
604
+ }, []);
605
+
606
+ return (
607
+ <div
608
+ ref={ref}
609
+ data-sidebar="menu-skeleton"
610
+ className={cn('flex h-[32px] items-center gap-[8px] rounded-[var(--pt-radius-md)] px-[8px]', className)}
611
+ {...props}
612
+ >
613
+ {showIcon && <Skeleton className="size-[16px] rounded-[var(--pt-radius-md)]" data-sidebar="menu-skeleton-icon" />}
614
+ <Skeleton
615
+ className="h-[16px] max-w-[--skeleton-width] flex-1"
616
+ data-sidebar="menu-skeleton-text"
617
+ style={
618
+ {
619
+ '--skeleton-width': width
620
+ } as React.CSSProperties
621
+ }
622
+ />
623
+ </div>
624
+ );
625
+ });
626
+ SidebarMenuSkeleton.displayName = 'SidebarMenuSkeleton';
627
+
628
+ const SidebarMenuSub = React.forwardRef<HTMLUListElement, React.ComponentProps<'ul'>>(
629
+ ({ className, ...props }, ref) => (
630
+ <ul
631
+ ref={ref}
632
+ data-sidebar="menu-sub"
633
+ className={cn(
634
+ 'mx-[14px] flex min-w-0 translate-x-px flex-col gap-[4px] border-l border-sidebar-border px-[10px] py-[2px]',
635
+ 'group-data-[collapsible=icon]:hidden',
636
+ className
637
+ )}
638
+ {...props}
639
+ />
640
+ )
641
+ );
642
+ SidebarMenuSub.displayName = 'SidebarMenuSub';
643
+
644
+ const SidebarMenuSubItem = React.forwardRef<HTMLLIElement, React.ComponentProps<'li'>>(
645
+ ({ ...props }, ref) => <li ref={ref} {...props} />
646
+ );
647
+ SidebarMenuSubItem.displayName = 'SidebarMenuSubItem';
648
+
649
+ const SidebarMenuSubButton = React.forwardRef<
650
+ HTMLAnchorElement,
651
+ React.ComponentProps<'a'> & {
652
+ asChild?: boolean;
653
+ size?: 'sm' | 'md';
654
+ isActive?: boolean;
655
+ }
656
+ >(({ asChild = false, size = 'md', isActive, className, ...props }, ref) => {
657
+ const Comp = asChild ? Slot : 'a';
658
+
659
+ return (
660
+ <Comp
661
+ ref={ref}
662
+ data-sidebar="menu-sub-button"
663
+ data-size={size}
664
+ data-active={isActive}
665
+ className={cn(
666
+ 'flex h-[28px] min-w-0 -translate-x-px items-center gap-[8px] overflow-hidden rounded-[var(--pt-radius-md)] px-[8px] 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-[16px] [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground',
667
+ 'data-[active=true]:bg-selected data-[active=true]:text-sidebar-accent-foreground',
668
+ size === 'sm' && 'text-[13px] leading-[20px]',
669
+ size === 'md' && 'text-[14px] leading-[20px]',
670
+ 'group-data-[collapsible=icon]:hidden',
671
+ className
672
+ )}
673
+ {...props}
674
+ />
675
+ );
676
+ });
677
+ SidebarMenuSubButton.displayName = 'SidebarMenuSubButton';
678
+
679
+ export {
680
+ Sidebar,
681
+ SidebarContent,
682
+ SidebarFooter,
683
+ SidebarGroup,
684
+ SidebarGroupAction,
685
+ SidebarGroupContent,
686
+ SidebarGroupLabel,
687
+ SidebarHeader,
688
+ SidebarInput,
689
+ SidebarInset,
690
+ SidebarMenu,
691
+ SidebarMenuAction,
692
+ SidebarMenuBadge,
693
+ SidebarMenuButton,
694
+ SidebarMenuItem,
695
+ SidebarMenuSkeleton,
696
+ SidebarMenuSub,
697
+ SidebarMenuSubButton,
698
+ SidebarMenuSubItem,
699
+ SidebarProvider,
700
+ SidebarRail,
701
+ SidebarSeparator,
702
+ SidebarTrigger,
703
+ useSidebar
704
+ };