@snapdragonsnursery/react-components 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -1
- package/src/EmployeeSearchPage.test.jsx +6 -1
- package/src/components/app-sidebar.jsx +205 -0
- package/src/components/nav-main.jsx +60 -0
- package/src/components/nav-projects.jsx +76 -0
- package/src/components/nav-user.jsx +104 -0
- package/src/components/room-switcher.jsx +116 -0
- package/src/components/site-switcher.jsx +116 -0
- package/src/components/team-switcher.jsx +83 -0
- package/src/components/theme-mode-toggle.jsx +62 -0
- package/src/components/ui/avatar.jsx +47 -0
- package/src/components/ui/breadcrumb.jsx +112 -0
- package/src/components/ui/collapsible.jsx +23 -0
- package/src/components/ui/dropdown-menu.jsx +221 -0
- package/src/components/ui/separator.jsx +25 -0
- package/src/components/ui/sheet.jsx +140 -0
- package/src/components/ui/sidebar.jsx +681 -0
- package/src/components/ui/skeleton.jsx +15 -0
- package/src/components/ui/tooltip.jsx +53 -0
- package/src/hooks/use-current-site.js +61 -0
- package/src/hooks/use-mobile.js +19 -0
- package/src/index.css +44 -0
- package/src/index.d.ts +67 -0
- package/src/index.js +48 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { ChevronsUpDown, Building2 } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
DropdownMenu,
|
|
8
|
+
DropdownMenuContent,
|
|
9
|
+
DropdownMenuItem,
|
|
10
|
+
DropdownMenuLabel,
|
|
11
|
+
DropdownMenuSeparator,
|
|
12
|
+
DropdownMenuTrigger,
|
|
13
|
+
} from "./ui/dropdown-menu"
|
|
14
|
+
import {
|
|
15
|
+
SidebarMenu,
|
|
16
|
+
SidebarMenuButton,
|
|
17
|
+
SidebarMenuItem,
|
|
18
|
+
useSidebar,
|
|
19
|
+
} from "./ui/sidebar"
|
|
20
|
+
|
|
21
|
+
// SiteSwitcher: shows a list of sites with custom icons; no "Add" entry.
|
|
22
|
+
// Props:
|
|
23
|
+
// - items: Array<{ id?: string, name: string, icon?: React.ComponentType<any> }>
|
|
24
|
+
// - activeId?: string
|
|
25
|
+
// - onChange?: (item) => void
|
|
26
|
+
// - label?: string (default: "Sites")
|
|
27
|
+
export function SiteSwitcher({ items = [], activeId, onChange, label = "Sites", isLoading = false }) {
|
|
28
|
+
const { isMobile } = useSidebar()
|
|
29
|
+
const initial = React.useMemo(() => {
|
|
30
|
+
if (!items?.length) return null
|
|
31
|
+
return activeId
|
|
32
|
+
? items.find((i) => i.id === activeId) ?? items[0]
|
|
33
|
+
: items[0]
|
|
34
|
+
}, [items, activeId])
|
|
35
|
+
const [active, setActive] = React.useState(initial)
|
|
36
|
+
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
if (!items?.length) return
|
|
39
|
+
if (activeId) {
|
|
40
|
+
const next = items.find((i) => i.id === activeId)
|
|
41
|
+
if (next && next !== active) setActive(next)
|
|
42
|
+
}
|
|
43
|
+
}, [activeId, items])
|
|
44
|
+
|
|
45
|
+
if (isLoading) {
|
|
46
|
+
return (
|
|
47
|
+
<SidebarMenu>
|
|
48
|
+
<SidebarMenuItem>
|
|
49
|
+
<SidebarMenuButton size="lg" className="opacity-60">
|
|
50
|
+
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary/60 text-sidebar-primary-foreground" />
|
|
51
|
+
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
52
|
+
<span className="truncate font-semibold">Loading…</span>
|
|
53
|
+
<span className="truncate text-xs">{label}</span>
|
|
54
|
+
</div>
|
|
55
|
+
</SidebarMenuButton>
|
|
56
|
+
</SidebarMenuItem>
|
|
57
|
+
</SidebarMenu>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!active || !items?.length) return null
|
|
62
|
+
|
|
63
|
+
const ActiveIcon = active.icon || active.logo || Building2
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<SidebarMenu>
|
|
67
|
+
<SidebarMenuItem>
|
|
68
|
+
<DropdownMenu>
|
|
69
|
+
<DropdownMenuTrigger asChild>
|
|
70
|
+
<SidebarMenuButton
|
|
71
|
+
size="lg"
|
|
72
|
+
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground">
|
|
73
|
+
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
|
|
74
|
+
<ActiveIcon className="size-4" />
|
|
75
|
+
</div>
|
|
76
|
+
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
77
|
+
<span className="truncate font-semibold">{active.name}</span>
|
|
78
|
+
<span className="truncate text-xs">{label}</span>
|
|
79
|
+
</div>
|
|
80
|
+
<ChevronsUpDown className="ml-auto" />
|
|
81
|
+
</SidebarMenuButton>
|
|
82
|
+
</DropdownMenuTrigger>
|
|
83
|
+
<DropdownMenuContent
|
|
84
|
+
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
|
85
|
+
align="start"
|
|
86
|
+
side={isMobile ? "bottom" : "right"}
|
|
87
|
+
sideOffset={4}>
|
|
88
|
+
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
|
89
|
+
{label}
|
|
90
|
+
</DropdownMenuLabel>
|
|
91
|
+
{items.map((site) => {
|
|
92
|
+
const Icon = site.icon || site.logo || Building2
|
|
93
|
+
return (
|
|
94
|
+
<DropdownMenuItem
|
|
95
|
+
key={site.id ?? site.name}
|
|
96
|
+
onClick={() => {
|
|
97
|
+
setActive(site)
|
|
98
|
+
onChange?.(site)
|
|
99
|
+
}}
|
|
100
|
+
className="gap-2 p-2">
|
|
101
|
+
<div className="flex size-6 items-center justify-center rounded-sm border">
|
|
102
|
+
<Icon className="size-4 shrink-0" />
|
|
103
|
+
</div>
|
|
104
|
+
{site.name}
|
|
105
|
+
</DropdownMenuItem>
|
|
106
|
+
)
|
|
107
|
+
})}
|
|
108
|
+
<DropdownMenuSeparator />
|
|
109
|
+
</DropdownMenuContent>
|
|
110
|
+
</DropdownMenu>
|
|
111
|
+
</SidebarMenuItem>
|
|
112
|
+
</SidebarMenu>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export default SiteSwitcher
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { ChevronsUpDown, Plus } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
DropdownMenu,
|
|
8
|
+
DropdownMenuContent,
|
|
9
|
+
DropdownMenuItem,
|
|
10
|
+
DropdownMenuLabel,
|
|
11
|
+
DropdownMenuSeparator,
|
|
12
|
+
DropdownMenuShortcut,
|
|
13
|
+
DropdownMenuTrigger,
|
|
14
|
+
} from "./ui/dropdown-menu"
|
|
15
|
+
import {
|
|
16
|
+
SidebarMenu,
|
|
17
|
+
SidebarMenuButton,
|
|
18
|
+
SidebarMenuItem,
|
|
19
|
+
useSidebar,
|
|
20
|
+
} from "./ui/sidebar"
|
|
21
|
+
|
|
22
|
+
export function TeamSwitcher({
|
|
23
|
+
teams
|
|
24
|
+
}) {
|
|
25
|
+
const { isMobile } = useSidebar()
|
|
26
|
+
const [activeTeam, setActiveTeam] = React.useState(teams[0])
|
|
27
|
+
|
|
28
|
+
if (!activeTeam) {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<SidebarMenu>
|
|
34
|
+
<SidebarMenuItem>
|
|
35
|
+
<DropdownMenu>
|
|
36
|
+
<DropdownMenuTrigger asChild>
|
|
37
|
+
<SidebarMenuButton
|
|
38
|
+
size="lg"
|
|
39
|
+
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground">
|
|
40
|
+
<div
|
|
41
|
+
className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
|
|
42
|
+
<activeTeam.logo className="size-4" />
|
|
43
|
+
</div>
|
|
44
|
+
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
45
|
+
<span className="truncate font-semibold">
|
|
46
|
+
{activeTeam.name}
|
|
47
|
+
</span>
|
|
48
|
+
<span className="truncate text-xs">{activeTeam.plan}</span>
|
|
49
|
+
</div>
|
|
50
|
+
<ChevronsUpDown className="ml-auto" />
|
|
51
|
+
</SidebarMenuButton>
|
|
52
|
+
</DropdownMenuTrigger>
|
|
53
|
+
<DropdownMenuContent
|
|
54
|
+
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
|
55
|
+
align="start"
|
|
56
|
+
side={isMobile ? "bottom" : "right"}
|
|
57
|
+
sideOffset={4}>
|
|
58
|
+
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
|
59
|
+
Teams
|
|
60
|
+
</DropdownMenuLabel>
|
|
61
|
+
{teams.map((team, index) => (
|
|
62
|
+
<DropdownMenuItem key={team.name} onClick={() => setActiveTeam(team)} className="gap-2 p-2">
|
|
63
|
+
<div className="flex size-6 items-center justify-center rounded-sm border">
|
|
64
|
+
<team.logo className="size-4 shrink-0" />
|
|
65
|
+
</div>
|
|
66
|
+
{team.name}
|
|
67
|
+
<DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut>
|
|
68
|
+
</DropdownMenuItem>
|
|
69
|
+
))}
|
|
70
|
+
<DropdownMenuSeparator />
|
|
71
|
+
<DropdownMenuItem className="gap-2 p-2">
|
|
72
|
+
<div
|
|
73
|
+
className="flex size-6 items-center justify-center rounded-md border bg-background">
|
|
74
|
+
<Plus className="size-4" />
|
|
75
|
+
</div>
|
|
76
|
+
<div className="font-medium text-muted-foreground">Add team</div>
|
|
77
|
+
</DropdownMenuItem>
|
|
78
|
+
</DropdownMenuContent>
|
|
79
|
+
</DropdownMenu>
|
|
80
|
+
</SidebarMenuItem>
|
|
81
|
+
</SidebarMenu>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Laptop, Moon, Sun } from "lucide-react"
|
|
5
|
+
import {
|
|
6
|
+
SidebarMenuButton,
|
|
7
|
+
} from "./ui/sidebar"
|
|
8
|
+
|
|
9
|
+
// Simple theme manager that toggles the `dark` class on <html>.
|
|
10
|
+
// Persists the preference to localStorage under `ui-theme`.
|
|
11
|
+
// Modes: 'system' | 'light' | 'dark'
|
|
12
|
+
const STORAGE_KEY = "ui-theme"
|
|
13
|
+
|
|
14
|
+
function applyTheme(mode) {
|
|
15
|
+
if (typeof document === "undefined") return
|
|
16
|
+
const root = document.documentElement
|
|
17
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)")
|
|
18
|
+
const isDark = mode === "dark" || (mode === "system" && mq.matches)
|
|
19
|
+
root.classList.toggle("dark", isDark)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function ThemeModeToggle() {
|
|
23
|
+
const [mode, setMode] = React.useState("system")
|
|
24
|
+
|
|
25
|
+
// Initialize from storage and apply
|
|
26
|
+
React.useEffect(() => {
|
|
27
|
+
const stored = window.localStorage.getItem(STORAGE_KEY)
|
|
28
|
+
const initial = stored === "light" || stored === "dark" ? stored : "system"
|
|
29
|
+
setMode(initial)
|
|
30
|
+
applyTheme(initial)
|
|
31
|
+
}, [])
|
|
32
|
+
|
|
33
|
+
// React to OS changes when in system mode
|
|
34
|
+
React.useEffect(() => {
|
|
35
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)")
|
|
36
|
+
const handler = () => {
|
|
37
|
+
if (mode === "system") applyTheme("system")
|
|
38
|
+
}
|
|
39
|
+
mq.addEventListener("change", handler)
|
|
40
|
+
return () => mq.removeEventListener("change", handler)
|
|
41
|
+
}, [mode])
|
|
42
|
+
|
|
43
|
+
const cycle = React.useCallback(() => {
|
|
44
|
+
const next = mode === "system" ? "light" : mode === "light" ? "dark" : "system"
|
|
45
|
+
setMode(next)
|
|
46
|
+
window.localStorage.setItem(STORAGE_KEY, next)
|
|
47
|
+
applyTheme(next)
|
|
48
|
+
}, [mode])
|
|
49
|
+
|
|
50
|
+
const icon = mode === "system" ? <Laptop /> : mode === "light" ? <Sun /> : <Moon />
|
|
51
|
+
const label = mode === "system" ? "Auto" : mode === "light" ? "Light" : "Dark"
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<SidebarMenuButton tooltip="Theme" onClick={cycle}>
|
|
55
|
+
{icon}
|
|
56
|
+
<span>Theme: {label}</span>
|
|
57
|
+
</SidebarMenuButton>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default ThemeModeToggle
|
|
62
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
|
|
8
|
+
function Avatar({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<AvatarPrimitive.Root
|
|
14
|
+
data-slot="avatar"
|
|
15
|
+
className={cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", className)}
|
|
16
|
+
{...props} />
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function AvatarImage({
|
|
21
|
+
className,
|
|
22
|
+
...props
|
|
23
|
+
}) {
|
|
24
|
+
return (
|
|
25
|
+
<AvatarPrimitive.Image
|
|
26
|
+
data-slot="avatar-image"
|
|
27
|
+
className={cn("aspect-square size-full", className)}
|
|
28
|
+
{...props} />
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function AvatarFallback({
|
|
33
|
+
className,
|
|
34
|
+
...props
|
|
35
|
+
}) {
|
|
36
|
+
return (
|
|
37
|
+
<AvatarPrimitive.Fallback
|
|
38
|
+
data-slot="avatar-fallback"
|
|
39
|
+
className={cn(
|
|
40
|
+
"bg-muted flex size-full items-center justify-center rounded-full",
|
|
41
|
+
className
|
|
42
|
+
)}
|
|
43
|
+
{...props} />
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { Avatar, AvatarImage, AvatarFallback }
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
+
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
function Breadcrumb({
|
|
8
|
+
...props
|
|
9
|
+
}) {
|
|
10
|
+
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function BreadcrumbList({
|
|
14
|
+
className,
|
|
15
|
+
...props
|
|
16
|
+
}) {
|
|
17
|
+
return (
|
|
18
|
+
<ol
|
|
19
|
+
data-slot="breadcrumb-list"
|
|
20
|
+
className={cn(
|
|
21
|
+
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
{...props} />
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function BreadcrumbItem({
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}) {
|
|
32
|
+
return (
|
|
33
|
+
<li
|
|
34
|
+
data-slot="breadcrumb-item"
|
|
35
|
+
className={cn("inline-flex items-center gap-1.5", className)}
|
|
36
|
+
{...props} />
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function BreadcrumbLink({
|
|
41
|
+
asChild,
|
|
42
|
+
className,
|
|
43
|
+
...props
|
|
44
|
+
}) {
|
|
45
|
+
const Comp = asChild ? Slot : "a"
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<Comp
|
|
49
|
+
data-slot="breadcrumb-link"
|
|
50
|
+
className={cn("hover:text-foreground transition-colors", className)}
|
|
51
|
+
{...props} />
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function BreadcrumbPage({
|
|
56
|
+
className,
|
|
57
|
+
...props
|
|
58
|
+
}) {
|
|
59
|
+
return (
|
|
60
|
+
<span
|
|
61
|
+
data-slot="breadcrumb-page"
|
|
62
|
+
role="link"
|
|
63
|
+
aria-disabled="true"
|
|
64
|
+
aria-current="page"
|
|
65
|
+
className={cn("text-foreground font-normal", className)}
|
|
66
|
+
{...props} />
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function BreadcrumbSeparator({
|
|
71
|
+
children,
|
|
72
|
+
className,
|
|
73
|
+
...props
|
|
74
|
+
}) {
|
|
75
|
+
return (
|
|
76
|
+
<li
|
|
77
|
+
data-slot="breadcrumb-separator"
|
|
78
|
+
role="presentation"
|
|
79
|
+
aria-hidden="true"
|
|
80
|
+
className={cn("[&>svg]:size-3.5", className)}
|
|
81
|
+
{...props}>
|
|
82
|
+
{children ?? <ChevronRight />}
|
|
83
|
+
</li>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function BreadcrumbEllipsis({
|
|
88
|
+
className,
|
|
89
|
+
...props
|
|
90
|
+
}) {
|
|
91
|
+
return (
|
|
92
|
+
<span
|
|
93
|
+
data-slot="breadcrumb-ellipsis"
|
|
94
|
+
role="presentation"
|
|
95
|
+
aria-hidden="true"
|
|
96
|
+
className={cn("flex size-9 items-center justify-center", className)}
|
|
97
|
+
{...props}>
|
|
98
|
+
<MoreHorizontal className="size-4" />
|
|
99
|
+
<span className="sr-only">More</span>
|
|
100
|
+
</span>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export {
|
|
105
|
+
Breadcrumb,
|
|
106
|
+
BreadcrumbList,
|
|
107
|
+
BreadcrumbItem,
|
|
108
|
+
BreadcrumbLink,
|
|
109
|
+
BreadcrumbPage,
|
|
110
|
+
BreadcrumbSeparator,
|
|
111
|
+
BreadcrumbEllipsis,
|
|
112
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
|
|
4
|
+
|
|
5
|
+
function Collapsible({
|
|
6
|
+
...props
|
|
7
|
+
}) {
|
|
8
|
+
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function CollapsibleTrigger({
|
|
12
|
+
...props
|
|
13
|
+
}) {
|
|
14
|
+
return (<CollapsiblePrimitive.CollapsibleTrigger data-slot="collapsible-trigger" {...props} />);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function CollapsibleContent({
|
|
18
|
+
...props
|
|
19
|
+
}) {
|
|
20
|
+
return (<CollapsiblePrimitive.CollapsibleContent data-slot="collapsible-content" {...props} />);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { Collapsible, CollapsibleTrigger, CollapsibleContent }
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
|
3
|
+
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
function DropdownMenu({
|
|
8
|
+
...props
|
|
9
|
+
}) {
|
|
10
|
+
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function DropdownMenuPortal({
|
|
14
|
+
...props
|
|
15
|
+
}) {
|
|
16
|
+
return (<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function DropdownMenuTrigger({
|
|
20
|
+
...props
|
|
21
|
+
}) {
|
|
22
|
+
return (<DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function DropdownMenuContent({
|
|
26
|
+
className,
|
|
27
|
+
sideOffset = 4,
|
|
28
|
+
...props
|
|
29
|
+
}) {
|
|
30
|
+
return (
|
|
31
|
+
<DropdownMenuPrimitive.Portal>
|
|
32
|
+
<DropdownMenuPrimitive.Content
|
|
33
|
+
data-slot="dropdown-menu-content"
|
|
34
|
+
sideOffset={sideOffset}
|
|
35
|
+
className={cn(
|
|
36
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props} />
|
|
40
|
+
</DropdownMenuPrimitive.Portal>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function DropdownMenuGroup({
|
|
45
|
+
...props
|
|
46
|
+
}) {
|
|
47
|
+
return (<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function DropdownMenuItem({
|
|
51
|
+
className,
|
|
52
|
+
inset,
|
|
53
|
+
variant = "default",
|
|
54
|
+
...props
|
|
55
|
+
}) {
|
|
56
|
+
return (
|
|
57
|
+
<DropdownMenuPrimitive.Item
|
|
58
|
+
data-slot="dropdown-menu-item"
|
|
59
|
+
data-inset={inset}
|
|
60
|
+
data-variant={variant}
|
|
61
|
+
className={cn(
|
|
62
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
{...props} />
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function DropdownMenuCheckboxItem({
|
|
70
|
+
className,
|
|
71
|
+
children,
|
|
72
|
+
checked,
|
|
73
|
+
...props
|
|
74
|
+
}) {
|
|
75
|
+
return (
|
|
76
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
77
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
78
|
+
className={cn(
|
|
79
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
80
|
+
className
|
|
81
|
+
)}
|
|
82
|
+
checked={checked}
|
|
83
|
+
{...props}>
|
|
84
|
+
<span
|
|
85
|
+
className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
86
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
87
|
+
<CheckIcon className="size-4" />
|
|
88
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
89
|
+
</span>
|
|
90
|
+
{children}
|
|
91
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function DropdownMenuRadioGroup({
|
|
96
|
+
...props
|
|
97
|
+
}) {
|
|
98
|
+
return (<DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function DropdownMenuRadioItem({
|
|
102
|
+
className,
|
|
103
|
+
children,
|
|
104
|
+
...props
|
|
105
|
+
}) {
|
|
106
|
+
return (
|
|
107
|
+
<DropdownMenuPrimitive.RadioItem
|
|
108
|
+
data-slot="dropdown-menu-radio-item"
|
|
109
|
+
className={cn(
|
|
110
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
111
|
+
className
|
|
112
|
+
)}
|
|
113
|
+
{...props}>
|
|
114
|
+
<span
|
|
115
|
+
className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
116
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
117
|
+
<CircleIcon className="size-2 fill-current" />
|
|
118
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
119
|
+
</span>
|
|
120
|
+
{children}
|
|
121
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function DropdownMenuLabel({
|
|
126
|
+
className,
|
|
127
|
+
inset,
|
|
128
|
+
...props
|
|
129
|
+
}) {
|
|
130
|
+
return (
|
|
131
|
+
<DropdownMenuPrimitive.Label
|
|
132
|
+
data-slot="dropdown-menu-label"
|
|
133
|
+
data-inset={inset}
|
|
134
|
+
className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
|
|
135
|
+
{...props} />
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function DropdownMenuSeparator({
|
|
140
|
+
className,
|
|
141
|
+
...props
|
|
142
|
+
}) {
|
|
143
|
+
return (
|
|
144
|
+
<DropdownMenuPrimitive.Separator
|
|
145
|
+
data-slot="dropdown-menu-separator"
|
|
146
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
147
|
+
{...props} />
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function DropdownMenuShortcut({
|
|
152
|
+
className,
|
|
153
|
+
...props
|
|
154
|
+
}) {
|
|
155
|
+
return (
|
|
156
|
+
<span
|
|
157
|
+
data-slot="dropdown-menu-shortcut"
|
|
158
|
+
className={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
|
|
159
|
+
{...props} />
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function DropdownMenuSub({
|
|
164
|
+
...props
|
|
165
|
+
}) {
|
|
166
|
+
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function DropdownMenuSubTrigger({
|
|
170
|
+
className,
|
|
171
|
+
inset,
|
|
172
|
+
children,
|
|
173
|
+
...props
|
|
174
|
+
}) {
|
|
175
|
+
return (
|
|
176
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
177
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
178
|
+
data-inset={inset}
|
|
179
|
+
className={cn(
|
|
180
|
+
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
|
|
181
|
+
className
|
|
182
|
+
)}
|
|
183
|
+
{...props}>
|
|
184
|
+
{children}
|
|
185
|
+
<ChevronRightIcon className="ml-auto size-4" />
|
|
186
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function DropdownMenuSubContent({
|
|
191
|
+
className,
|
|
192
|
+
...props
|
|
193
|
+
}) {
|
|
194
|
+
return (
|
|
195
|
+
<DropdownMenuPrimitive.SubContent
|
|
196
|
+
data-slot="dropdown-menu-sub-content"
|
|
197
|
+
className={cn(
|
|
198
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
|
199
|
+
className
|
|
200
|
+
)}
|
|
201
|
+
{...props} />
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export {
|
|
206
|
+
DropdownMenu,
|
|
207
|
+
DropdownMenuPortal,
|
|
208
|
+
DropdownMenuTrigger,
|
|
209
|
+
DropdownMenuContent,
|
|
210
|
+
DropdownMenuGroup,
|
|
211
|
+
DropdownMenuLabel,
|
|
212
|
+
DropdownMenuItem,
|
|
213
|
+
DropdownMenuCheckboxItem,
|
|
214
|
+
DropdownMenuRadioGroup,
|
|
215
|
+
DropdownMenuRadioItem,
|
|
216
|
+
DropdownMenuSeparator,
|
|
217
|
+
DropdownMenuShortcut,
|
|
218
|
+
DropdownMenuSub,
|
|
219
|
+
DropdownMenuSubTrigger,
|
|
220
|
+
DropdownMenuSubContent,
|
|
221
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
|
3
|
+
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
|
|
6
|
+
function Separator({
|
|
7
|
+
className,
|
|
8
|
+
orientation = "horizontal",
|
|
9
|
+
decorative = true,
|
|
10
|
+
...props
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<SeparatorPrimitive.Root
|
|
14
|
+
data-slot="separator"
|
|
15
|
+
decorative={decorative}
|
|
16
|
+
orientation={orientation}
|
|
17
|
+
className={cn(
|
|
18
|
+
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
|
19
|
+
className
|
|
20
|
+
)}
|
|
21
|
+
{...props} />
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { Separator }
|