@snapdragonsnursery/react-components 1.7.0 → 1.8.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/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,53 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
|
3
|
+
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
|
|
6
|
+
function TooltipProvider({
|
|
7
|
+
delayDuration = 0,
|
|
8
|
+
...props
|
|
9
|
+
}) {
|
|
10
|
+
return (<TooltipPrimitive.Provider data-slot="tooltip-provider" delayDuration={delayDuration} {...props} />);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function Tooltip({
|
|
14
|
+
...props
|
|
15
|
+
}) {
|
|
16
|
+
return (
|
|
17
|
+
<TooltipProvider>
|
|
18
|
+
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
19
|
+
</TooltipProvider>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function TooltipTrigger({
|
|
24
|
+
...props
|
|
25
|
+
}) {
|
|
26
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function TooltipContent({
|
|
30
|
+
className,
|
|
31
|
+
sideOffset = 0,
|
|
32
|
+
children,
|
|
33
|
+
...props
|
|
34
|
+
}) {
|
|
35
|
+
return (
|
|
36
|
+
<TooltipPrimitive.Portal>
|
|
37
|
+
<TooltipPrimitive.Content
|
|
38
|
+
data-slot="tooltip-content"
|
|
39
|
+
sideOffset={sideOffset}
|
|
40
|
+
className={cn(
|
|
41
|
+
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
{...props}>
|
|
45
|
+
{children}
|
|
46
|
+
<TooltipPrimitive.Arrow
|
|
47
|
+
className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
|
48
|
+
</TooltipPrimitive.Content>
|
|
49
|
+
</TooltipPrimitive.Portal>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
export const SITE_STORAGE_KEY = "current_site_id"
|
|
4
|
+
export const SITE_EVENT_NAME = "siteChanged"
|
|
5
|
+
|
|
6
|
+
export function readCurrentSiteId(storageKey = SITE_STORAGE_KEY) {
|
|
7
|
+
if (typeof window === "undefined") return null
|
|
8
|
+
try {
|
|
9
|
+
const v = window.localStorage.getItem(storageKey)
|
|
10
|
+
if (v == null) return null
|
|
11
|
+
const n = Number(v)
|
|
12
|
+
return Number.isNaN(n) ? v : n
|
|
13
|
+
} catch {
|
|
14
|
+
return null
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function writeCurrentSiteId(siteId, storageKey = SITE_STORAGE_KEY) {
|
|
19
|
+
if (typeof window === "undefined") return
|
|
20
|
+
try {
|
|
21
|
+
if (siteId == null) {
|
|
22
|
+
window.localStorage.removeItem(storageKey)
|
|
23
|
+
} else {
|
|
24
|
+
window.localStorage.setItem(storageKey, String(siteId))
|
|
25
|
+
}
|
|
26
|
+
} catch {}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const numeric = typeof siteId === "number" ? siteId : Number(siteId)
|
|
30
|
+
const detail = { siteId: Number.isFinite(numeric) ? numeric : siteId }
|
|
31
|
+
window.dispatchEvent(new CustomEvent(SITE_EVENT_NAME, { detail }))
|
|
32
|
+
} catch {}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function useCurrentSiteId({ storageKey = SITE_STORAGE_KEY, eventName = SITE_EVENT_NAME } = {}) {
|
|
36
|
+
const [currentSiteId, setCurrentSiteId] = React.useState(null)
|
|
37
|
+
|
|
38
|
+
React.useEffect(() => {
|
|
39
|
+
// Initialize from storage on mount
|
|
40
|
+
const initial = readCurrentSiteId(storageKey)
|
|
41
|
+
setCurrentSiteId(initial)
|
|
42
|
+
|
|
43
|
+
// Listen for global site changes
|
|
44
|
+
const handler = (e) => {
|
|
45
|
+
try {
|
|
46
|
+
const id = e?.detail?.siteId
|
|
47
|
+
if (typeof id !== "undefined") setCurrentSiteId(id)
|
|
48
|
+
} catch {}
|
|
49
|
+
}
|
|
50
|
+
window.addEventListener(eventName, handler)
|
|
51
|
+
return () => window.removeEventListener(eventName, handler)
|
|
52
|
+
}, [storageKey, eventName])
|
|
53
|
+
|
|
54
|
+
const update = React.useCallback((id) => {
|
|
55
|
+
setCurrentSiteId(id)
|
|
56
|
+
writeCurrentSiteId(id, storageKey)
|
|
57
|
+
}, [storageKey])
|
|
58
|
+
|
|
59
|
+
return [currentSiteId, update]
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState(undefined)
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
12
|
+
}
|
|
13
|
+
mql.addEventListener("change", onChange)
|
|
14
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
15
|
+
return () => mql.removeEventListener("change", onChange);
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
return !!isMobile
|
|
19
|
+
}
|
package/src/index.css
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
@tailwind base;
|
|
2
|
+
|
|
3
|
+
@custom-variant dark (&:is(.dark *));
|
|
2
4
|
@tailwind components;
|
|
3
5
|
@tailwind utilities;
|
|
4
6
|
|
|
@@ -56,4 +58,46 @@
|
|
|
56
58
|
body {
|
|
57
59
|
@apply bg-background text-foreground;
|
|
58
60
|
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
:root {
|
|
64
|
+
--sidebar: hsl(0 0% 98%);
|
|
65
|
+
--sidebar-foreground: hsl(240 5.3% 26.1%);
|
|
66
|
+
--sidebar-primary: hsl(240 5.9% 10%);
|
|
67
|
+
--sidebar-primary-foreground: hsl(0 0% 98%);
|
|
68
|
+
--sidebar-accent: hsl(240 4.8% 95.9%);
|
|
69
|
+
--sidebar-accent-foreground: hsl(240 5.9% 10%);
|
|
70
|
+
--sidebar-border: hsl(220 13% 91%);
|
|
71
|
+
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.dark {
|
|
75
|
+
--sidebar: hsl(240 5.9% 10%);
|
|
76
|
+
--sidebar-foreground: hsl(240 4.8% 95.9%);
|
|
77
|
+
--sidebar-primary: hsl(224.3 76.3% 48%);
|
|
78
|
+
--sidebar-primary-foreground: hsl(0 0% 100%);
|
|
79
|
+
--sidebar-accent: hsl(240 3.7% 15.9%);
|
|
80
|
+
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
|
|
81
|
+
--sidebar-border: hsl(240 3.7% 15.9%);
|
|
82
|
+
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@theme inline {
|
|
86
|
+
--color-sidebar: var(--sidebar);
|
|
87
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
88
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
89
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
90
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
91
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
92
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
93
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@layer base {
|
|
97
|
+
* {
|
|
98
|
+
@apply border-border outline-ring/50;
|
|
99
|
+
}
|
|
100
|
+
body {
|
|
101
|
+
@apply bg-background text-foreground;
|
|
102
|
+
}
|
|
59
103
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -65,3 +65,70 @@ export const StatCard: React.ComponentType<StatCardProps>
|
|
|
65
65
|
export function configureTelemetry(...args: any[]): any
|
|
66
66
|
|
|
67
67
|
|
|
68
|
+
// Sidebar + UI exports
|
|
69
|
+
export const AppSidebar: React.ComponentType<any>
|
|
70
|
+
export const Sidebar: React.ComponentType<any>
|
|
71
|
+
export const SidebarContent: React.ComponentType<any>
|
|
72
|
+
export const SidebarFooter: React.ComponentType<any>
|
|
73
|
+
export const SidebarGroup: React.ComponentType<any>
|
|
74
|
+
export const SidebarGroupAction: React.ComponentType<any>
|
|
75
|
+
export const SidebarGroupContent: React.ComponentType<any>
|
|
76
|
+
export const SidebarGroupLabel: React.ComponentType<any>
|
|
77
|
+
export const SidebarHeader: React.ComponentType<any>
|
|
78
|
+
export const SidebarInput: React.ComponentType<any>
|
|
79
|
+
export const SidebarInset: React.ComponentType<any>
|
|
80
|
+
export const SidebarMenu: React.ComponentType<any>
|
|
81
|
+
export const SidebarMenuAction: React.ComponentType<any>
|
|
82
|
+
export const SidebarMenuBadge: React.ComponentType<any>
|
|
83
|
+
export const SidebarMenuButton: React.ComponentType<any>
|
|
84
|
+
export const SidebarMenuItem: React.ComponentType<any>
|
|
85
|
+
export const SidebarMenuSkeleton: React.ComponentType<any>
|
|
86
|
+
export const SidebarMenuSub: React.ComponentType<any>
|
|
87
|
+
export const SidebarMenuSubButton: React.ComponentType<any>
|
|
88
|
+
export const SidebarMenuSubItem: React.ComponentType<any>
|
|
89
|
+
export const SidebarProvider: React.ComponentType<any>
|
|
90
|
+
export const SidebarRail: React.ComponentType<any>
|
|
91
|
+
export const SidebarSeparator: React.ComponentType<any>
|
|
92
|
+
export const SidebarTrigger: React.ComponentType<any>
|
|
93
|
+
export function useSidebar(): any
|
|
94
|
+
|
|
95
|
+
export const Separator: React.ComponentType<any>
|
|
96
|
+
export const Breadcrumb: React.ComponentType<any>
|
|
97
|
+
export const BreadcrumbItem: React.ComponentType<any>
|
|
98
|
+
export const BreadcrumbLink: React.ComponentType<any>
|
|
99
|
+
export const BreadcrumbList: React.ComponentType<any>
|
|
100
|
+
export const BreadcrumbPage: React.ComponentType<any>
|
|
101
|
+
export const BreadcrumbSeparator: React.ComponentType<any>
|
|
102
|
+
export const BreadcrumbEllipsis: React.ComponentType<any>
|
|
103
|
+
|
|
104
|
+
// Switchers
|
|
105
|
+
export interface SwitcherItem {
|
|
106
|
+
id?: string | number
|
|
107
|
+
name: string
|
|
108
|
+
icon?: React.ComponentType<any>
|
|
109
|
+
logo?: React.ComponentType<any>
|
|
110
|
+
}
|
|
111
|
+
export interface SiteSwitcherProps {
|
|
112
|
+
items?: SwitcherItem[]
|
|
113
|
+
activeId?: string | number
|
|
114
|
+
onChange?: (item: SwitcherItem) => void
|
|
115
|
+
label?: string
|
|
116
|
+
isLoading?: boolean
|
|
117
|
+
}
|
|
118
|
+
export interface RoomSwitcherProps {
|
|
119
|
+
items?: SwitcherItem[]
|
|
120
|
+
activeId?: string | number
|
|
121
|
+
onChange?: (item: SwitcherItem) => void
|
|
122
|
+
label?: string
|
|
123
|
+
isLoading?: boolean
|
|
124
|
+
baseColor?: string
|
|
125
|
+
}
|
|
126
|
+
export const SiteSwitcher: React.ComponentType<SiteSwitcherProps>
|
|
127
|
+
export const RoomSwitcher: React.ComponentType<RoomSwitcherProps>
|
|
128
|
+
|
|
129
|
+
// Site selection helpers
|
|
130
|
+
export const SITE_STORAGE_KEY: string
|
|
131
|
+
export const SITE_EVENT_NAME: string
|
|
132
|
+
export function readCurrentSiteId(storageKey?: string): string | number | null
|
|
133
|
+
export function writeCurrentSiteId(siteId: string | number | null, storageKey?: string): void
|
|
134
|
+
export function useCurrentSiteId(options?: { storageKey?: string; eventName?: string }): [string | number | null, (id: string | number | null) => void]
|
package/src/index.js
CHANGED
|
@@ -26,3 +26,51 @@ export { SimpleCalendar } from "./components/ui/simple-calendar.jsx";
|
|
|
26
26
|
export { Popover, PopoverContent, PopoverTrigger } from "./components/ui/popover.jsx";
|
|
27
27
|
export { default as SoftWarningAlert } from "./components/ui/soft-warning-alert.jsx";
|
|
28
28
|
export { default as StatCard } from "./components/ui/stat-card.jsx";
|
|
29
|
+
|
|
30
|
+
// Shadcn Sidebar + related UI
|
|
31
|
+
export { AppSidebar } from "./components/app-sidebar.jsx";
|
|
32
|
+
export { SiteSwitcher } from "./components/site-switcher.jsx";
|
|
33
|
+
export { RoomSwitcher } from "./components/room-switcher.jsx";
|
|
34
|
+
export {
|
|
35
|
+
useCurrentSiteId,
|
|
36
|
+
readCurrentSiteId,
|
|
37
|
+
writeCurrentSiteId,
|
|
38
|
+
SITE_STORAGE_KEY,
|
|
39
|
+
SITE_EVENT_NAME,
|
|
40
|
+
} from "./hooks/use-current-site.js";
|
|
41
|
+
export {
|
|
42
|
+
Sidebar,
|
|
43
|
+
SidebarContent,
|
|
44
|
+
SidebarFooter,
|
|
45
|
+
SidebarGroup,
|
|
46
|
+
SidebarGroupAction,
|
|
47
|
+
SidebarGroupContent,
|
|
48
|
+
SidebarGroupLabel,
|
|
49
|
+
SidebarHeader,
|
|
50
|
+
SidebarInput,
|
|
51
|
+
SidebarInset,
|
|
52
|
+
SidebarMenu,
|
|
53
|
+
SidebarMenuAction,
|
|
54
|
+
SidebarMenuBadge,
|
|
55
|
+
SidebarMenuButton,
|
|
56
|
+
SidebarMenuItem,
|
|
57
|
+
SidebarMenuSkeleton,
|
|
58
|
+
SidebarMenuSub,
|
|
59
|
+
SidebarMenuSubButton,
|
|
60
|
+
SidebarMenuSubItem,
|
|
61
|
+
SidebarProvider,
|
|
62
|
+
SidebarRail,
|
|
63
|
+
SidebarSeparator,
|
|
64
|
+
SidebarTrigger,
|
|
65
|
+
useSidebar,
|
|
66
|
+
} from "./components/ui/sidebar.jsx";
|
|
67
|
+
export { Separator } from "./components/ui/separator.jsx";
|
|
68
|
+
export {
|
|
69
|
+
Breadcrumb,
|
|
70
|
+
BreadcrumbItem,
|
|
71
|
+
BreadcrumbLink,
|
|
72
|
+
BreadcrumbList,
|
|
73
|
+
BreadcrumbPage,
|
|
74
|
+
BreadcrumbSeparator,
|
|
75
|
+
BreadcrumbEllipsis,
|
|
76
|
+
} from "./components/ui/breadcrumb.jsx";
|