@srcroot/ui 0.0.44 → 0.0.45
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/package.json +1 -1
- package/src/registry/analytics/google-tag-manager.tsx +1 -1
- package/src/registry/themes/v4/blue.css +34 -36
- package/src/registry/themes/v4/gray.css +36 -38
- package/src/registry/themes/v4/green.css +34 -36
- package/src/registry/themes/v4/neutral.css +36 -38
- package/src/registry/themes/v4/orange.css +34 -36
- package/src/registry/themes/v4/rose.css +34 -36
- package/src/registry/themes/v4/slate.css +36 -38
- package/src/registry/themes/v4/stone.css +36 -38
- package/src/registry/themes/v4/violet.css +36 -38
- package/src/registry/themes/v4/zinc.css +34 -36
- package/src/registry/ui/chart.tsx +17 -15
- package/src/registry/ui/dropdown-menu.tsx +92 -15
- package/src/registry/ui/sidebar.tsx +159 -0
- package/src/registry/ui/theme-switcher.tsx +66 -0
|
@@ -492,6 +492,159 @@ const SidebarMenuButton = React.forwardRef<
|
|
|
492
492
|
)
|
|
493
493
|
SidebarMenuButton.displayName = "SidebarMenuButton"
|
|
494
494
|
|
|
495
|
+
const SidebarMenuAction = React.forwardRef<
|
|
496
|
+
HTMLButtonElement,
|
|
497
|
+
React.ButtonHTMLAttributes<HTMLButtonElement> & { asChild?: boolean; showOnHover?: boolean }
|
|
498
|
+
>(({ className, asChild = false, showOnHover = false, ...props }, ref) => {
|
|
499
|
+
if (asChild) {
|
|
500
|
+
const child = React.Children.only(props.children) as React.ReactElement<any>
|
|
501
|
+
return React.cloneElement(child, {
|
|
502
|
+
ref,
|
|
503
|
+
"data-sidebar": "menu-action",
|
|
504
|
+
className: cn(
|
|
505
|
+
"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",
|
|
506
|
+
// Increases the hit area of the button on mobile.
|
|
507
|
+
"after:absolute after:-inset-2 after:md:hidden",
|
|
508
|
+
"peer-data-[size=sm]/menu-button:top-1",
|
|
509
|
+
"peer-data-[size=default]/menu-button:top-1.5",
|
|
510
|
+
"peer-data-[size=lg]/menu-button:top-2.5",
|
|
511
|
+
"group-data-[collapsible=icon]:hidden",
|
|
512
|
+
showOnHover &&
|
|
513
|
+
"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",
|
|
514
|
+
className,
|
|
515
|
+
child.props.className
|
|
516
|
+
),
|
|
517
|
+
...props,
|
|
518
|
+
children: child.props.children
|
|
519
|
+
})
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return (
|
|
523
|
+
<button
|
|
524
|
+
ref={ref}
|
|
525
|
+
data-sidebar="menu-action"
|
|
526
|
+
className={cn(
|
|
527
|
+
"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",
|
|
528
|
+
// Increases the hit area of the button on mobile.
|
|
529
|
+
"after:absolute after:-inset-2 after:md:hidden",
|
|
530
|
+
"peer-data-[size=sm]/menu-button:top-1",
|
|
531
|
+
"peer-data-[size=default]/menu-button:top-1.5",
|
|
532
|
+
"peer-data-[size=lg]/menu-button:top-2.5",
|
|
533
|
+
"group-data-[collapsible=icon]:hidden",
|
|
534
|
+
showOnHover &&
|
|
535
|
+
"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",
|
|
536
|
+
className
|
|
537
|
+
)}
|
|
538
|
+
{...props}
|
|
539
|
+
/>
|
|
540
|
+
)
|
|
541
|
+
})
|
|
542
|
+
SidebarMenuAction.displayName = "SidebarMenuAction"
|
|
543
|
+
|
|
544
|
+
const SidebarMenuSub = React.forwardRef<
|
|
545
|
+
HTMLUListElement,
|
|
546
|
+
React.HTMLAttributes<HTMLUListElement>
|
|
547
|
+
>(({ className, ...props }, ref) => (
|
|
548
|
+
<ul
|
|
549
|
+
ref={ref}
|
|
550
|
+
data-sidebar="menu-sub"
|
|
551
|
+
className={cn(
|
|
552
|
+
"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",
|
|
553
|
+
"group-data-[collapsible=icon]:hidden",
|
|
554
|
+
className
|
|
555
|
+
)}
|
|
556
|
+
{...props}
|
|
557
|
+
/>
|
|
558
|
+
))
|
|
559
|
+
SidebarMenuSub.displayName = "SidebarMenuSub"
|
|
560
|
+
|
|
561
|
+
const SidebarMenuSubItem = React.forwardRef<
|
|
562
|
+
HTMLLIElement,
|
|
563
|
+
React.LiHTMLAttributes<HTMLLIElement>
|
|
564
|
+
>(({ ...props }, ref) => <li ref={ref} {...props} />)
|
|
565
|
+
SidebarMenuSubItem.displayName = "SidebarMenuSubItem"
|
|
566
|
+
|
|
567
|
+
const SidebarMenuSubButton = React.forwardRef<
|
|
568
|
+
HTMLAnchorElement,
|
|
569
|
+
React.AnchorHTMLAttributes<HTMLAnchorElement> & {
|
|
570
|
+
asChild?: boolean
|
|
571
|
+
size?: "sm" | "md"
|
|
572
|
+
isActive?: boolean
|
|
573
|
+
}
|
|
574
|
+
>(({ asChild = false, size = "md", isActive, className, ...props }, ref) => {
|
|
575
|
+
if (asChild) {
|
|
576
|
+
const child = React.Children.only(props.children) as React.ReactElement<any>
|
|
577
|
+
return React.cloneElement(child, {
|
|
578
|
+
ref,
|
|
579
|
+
"data-sidebar": "menu-sub-button",
|
|
580
|
+
"data-size": size,
|
|
581
|
+
"data-active": isActive,
|
|
582
|
+
className: cn(
|
|
583
|
+
"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 group-data-[collapsible=icon]:hidden",
|
|
584
|
+
size === "sm" && "text-xs",
|
|
585
|
+
size === "md" && "text-sm",
|
|
586
|
+
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
|
|
587
|
+
className,
|
|
588
|
+
child.props.className
|
|
589
|
+
),
|
|
590
|
+
...props,
|
|
591
|
+
children: child.props.children
|
|
592
|
+
})
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return (
|
|
596
|
+
<a
|
|
597
|
+
ref={ref}
|
|
598
|
+
data-sidebar="menu-sub-button"
|
|
599
|
+
data-size={size}
|
|
600
|
+
data-active={isActive}
|
|
601
|
+
className={cn(
|
|
602
|
+
"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 group-data-[collapsible=icon]:hidden",
|
|
603
|
+
size === "sm" && "text-xs",
|
|
604
|
+
size === "md" && "text-sm",
|
|
605
|
+
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
|
|
606
|
+
className
|
|
607
|
+
)}
|
|
608
|
+
{...props}
|
|
609
|
+
/>
|
|
610
|
+
)
|
|
611
|
+
})
|
|
612
|
+
SidebarMenuSubButton.displayName = "SidebarMenuSubButton"
|
|
613
|
+
|
|
614
|
+
const SidebarInput = React.forwardRef<
|
|
615
|
+
HTMLInputElement,
|
|
616
|
+
React.InputHTMLAttributes<HTMLInputElement>
|
|
617
|
+
>(({ className, ...props }, ref) => {
|
|
618
|
+
return (
|
|
619
|
+
<input
|
|
620
|
+
ref={ref}
|
|
621
|
+
data-sidebar="input"
|
|
622
|
+
className={cn(
|
|
623
|
+
"flex h-8 w-full bg-background rounded-md px-3 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 group-data-[collapsible=icon]:w-0 group-data-[collapsible=icon]:p-0 group-data-[collapsible=icon]:border-0 group-data-[collapsible=icon]:hidden",
|
|
624
|
+
className
|
|
625
|
+
)}
|
|
626
|
+
{...props}
|
|
627
|
+
/>
|
|
628
|
+
)
|
|
629
|
+
})
|
|
630
|
+
SidebarInput.displayName = "SidebarInput"
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
const SidebarSeparator = React.forwardRef<
|
|
634
|
+
HTMLDivElement,
|
|
635
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
636
|
+
>(({ className, ...props }, ref) => {
|
|
637
|
+
return (
|
|
638
|
+
<div
|
|
639
|
+
ref={ref}
|
|
640
|
+
data-sidebar="separator"
|
|
641
|
+
className={cn("mx-2 w-auto bg-sidebar-border h-px", className)}
|
|
642
|
+
{...props}
|
|
643
|
+
/>
|
|
644
|
+
)
|
|
645
|
+
})
|
|
646
|
+
SidebarSeparator.displayName = "SidebarSeparator"
|
|
647
|
+
|
|
495
648
|
export {
|
|
496
649
|
Sidebar,
|
|
497
650
|
SidebarContent,
|
|
@@ -500,12 +653,18 @@ export {
|
|
|
500
653
|
SidebarGroupContent,
|
|
501
654
|
SidebarGroupLabel,
|
|
502
655
|
SidebarHeader,
|
|
656
|
+
SidebarInput,
|
|
503
657
|
SidebarInset,
|
|
504
658
|
SidebarMenu,
|
|
659
|
+
SidebarMenuAction,
|
|
505
660
|
SidebarMenuButton,
|
|
506
661
|
SidebarMenuItem,
|
|
662
|
+
SidebarMenuSub,
|
|
663
|
+
SidebarMenuSubButton,
|
|
664
|
+
SidebarMenuSubItem,
|
|
507
665
|
SidebarProvider,
|
|
508
666
|
SidebarRail,
|
|
667
|
+
SidebarSeparator,
|
|
509
668
|
SidebarTrigger,
|
|
510
669
|
useSidebar,
|
|
511
670
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { useTheme } from "next-themes"
|
|
5
|
+
import { FiSun, FiMoon, FiMonitor } from "react-icons/fi"
|
|
6
|
+
|
|
7
|
+
import { DropdownMenuItem } from "@/components/ui/dropdown-menu"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ThemeSwitcher component for use within a DropdownMenu
|
|
11
|
+
* Toggles between light, dark, and system themes
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <DropdownMenu>
|
|
15
|
+
* <DropdownMenuContent>
|
|
16
|
+
* <ThemeSwitcher />
|
|
17
|
+
* </DropdownMenuContent>
|
|
18
|
+
* </DropdownMenu>
|
|
19
|
+
*/
|
|
20
|
+
export function ThemeSwitcher() {
|
|
21
|
+
const { setTheme, theme, resolvedTheme } = useTheme()
|
|
22
|
+
const [mounted, setMounted] = React.useState(false)
|
|
23
|
+
|
|
24
|
+
// Avoid hydration mismatch
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
setMounted(true)
|
|
27
|
+
}, [])
|
|
28
|
+
|
|
29
|
+
const toggleTheme = () => {
|
|
30
|
+
// Cycle: light -> dark -> system -> light
|
|
31
|
+
if (theme === "light") {
|
|
32
|
+
setTheme("dark")
|
|
33
|
+
} else if (theme === "dark") {
|
|
34
|
+
setTheme("system")
|
|
35
|
+
} else {
|
|
36
|
+
setTheme("light")
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const getIcon = () => {
|
|
41
|
+
if (!mounted) {
|
|
42
|
+
return <FiSun className="mr-2 h-4 w-4" />
|
|
43
|
+
}
|
|
44
|
+
if (theme === "system") {
|
|
45
|
+
return <FiMonitor className="mr-2 h-4 w-4" />
|
|
46
|
+
}
|
|
47
|
+
if (resolvedTheme === "dark") {
|
|
48
|
+
return <FiMoon className="mr-2 h-4 w-4" />
|
|
49
|
+
}
|
|
50
|
+
return <FiSun className="mr-2 h-4 w-4" />
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const getLabel = () => {
|
|
54
|
+
if (!mounted) return "Theme"
|
|
55
|
+
if (theme === "system") return "System"
|
|
56
|
+
if (theme === "dark") return "Dark"
|
|
57
|
+
return "Light"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<DropdownMenuItem onClick={toggleTheme} closeOnSelect={false}>
|
|
62
|
+
{getIcon()}
|
|
63
|
+
<span>Theme: {getLabel()}</span>
|
|
64
|
+
</DropdownMenuItem>
|
|
65
|
+
)
|
|
66
|
+
}
|