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