@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.
@@ -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";