@rqdhw3n/react-admin-layout 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -10,7 +10,8 @@ Production-ready admin dashboard layout for React — sidebar, navbar, dark mode
10
10
  - Mobile drawer menu
11
11
  - Top navbar with search, notifications, user menu
12
12
  - Breadcrumbs + auto-generation from route path
13
- - Dark mode with `localStorage` persistence
13
+ - Dark mode with `localStorage` persistence
14
+ - Light, dark, and live system mode; custom palettes; modern, minimal, and compact styles
14
15
  - RBAC permission filtering on sidebar items
15
16
  - React Router support (optional peer)
16
17
  - Tailwind CSS — modern SaaS aesthetic
@@ -84,7 +85,281 @@ export default function App() {
84
85
  }
85
86
  ```
86
87
 
87
- ## React Router
88
+ ## Theming
89
+
90
+ The optional `theme` prop adds custom colors and three visual styles to the existing layout.
91
+ Keep using `sidebarItems`, `appName`, and the other existing props. Import the stylesheet once:
92
+
93
+ ```tsx
94
+ import {
95
+ AdminLayout,
96
+ type AdminLayoutTheme,
97
+ type AdminLayoutThemeMode,
98
+ type AdminLayoutStyle,
99
+ } from '@rqdhw3n/react-admin-layout'
100
+ import '@rqdhw3n/react-admin-layout/styles.css'
101
+ ```
102
+
103
+ The original `@rqdhw3n/react-admin-layout/style.css` import remains supported and points to the same file.
104
+ Consumers do not need Tailwind installed to use the shipped styles. Tailwind configuration is only
105
+ needed when generating additional utility classes in your own app.
106
+
107
+ ### Basic dark mode
108
+
109
+ ```tsx
110
+ <AdminLayout sidebarItems={sidebarItems} theme={{ mode: 'dark' }}>
111
+ <YourPage />
112
+ </AdminLayout>
113
+ ```
114
+
115
+ ### Built-in style
116
+
117
+ ```tsx
118
+ <AdminLayout sidebarItems={sidebarItems} theme={{ style: 'compact' }}>
119
+ <YourPage />
120
+ </AdminLayout>
121
+ ```
122
+
123
+ Every style supports every mode and palette:
124
+
125
+ | Style | Sidebar / collapsed | Header | Navigation height | Content spacing | Radius | Appearance |
126
+ | --- | --- | --- | --- | --- | --- | --- |
127
+ | `modern` (default) | 260 / 72 px | 64 px | 44 px | 24 px | 10 px | Rounded navigation and cards, subtle shadows |
128
+ | `minimal` | 260 / 72 px | 64 px | 46 px | 32 px | 4 px | Flat surfaces, borders, extra whitespace |
129
+ | `compact` | 220 / 60 px | 52 px | 34 px | 16 px | 6 px | Dense navigation and content, smaller header |
130
+
131
+ `adminLayoutPresets.modern`, `.minimal`, and `.compact` are immutable, optional convenience objects:
132
+
133
+ ```tsx
134
+ import { adminLayoutPresets } from '@rqdhw3n/react-admin-layout'
135
+
136
+ <AdminLayout sidebarItems={sidebarItems} theme={{
137
+ ...adminLayoutPresets.modern,
138
+ colors: { primary: '#15803d', activeText: '#ffffff', onPrimary: '#ffffff' },
139
+ }}>
140
+ <YourPage />
141
+ </AdminLayout>
142
+ ```
143
+
144
+ ### Custom colors
145
+
146
+ ```tsx
147
+ <AdminLayout sidebarItems={sidebarItems} theme={{
148
+ colors: {
149
+ primary: '#7c3aed',
150
+ activeText: '#ffffff',
151
+ onPrimary: '#ffffff',
152
+ sidebarBackground: '#111827',
153
+ sidebarText: '#cbd5e1',
154
+ },
155
+ }}>
156
+ <YourPage />
157
+ </AdminLayout>
158
+ ```
159
+
160
+ All colors accept CSS color values, including CSS variables. Default text/foreground pairs meet
161
+ 4.5:1 contrast. When providing custom backgrounds, choose readable matching foregrounds:
162
+ `sidebarText`, `headerText`, `hoverText`, `activeText`, `onPrimary`, and `onDanger`.
163
+ The library does not infer contrast for arbitrary CSS colors.
164
+
165
+ ### Custom light/dark
166
+
167
+ ```tsx
168
+ <AdminLayout sidebarItems={sidebarItems} theme={{
169
+ mode: 'system',
170
+ light: { primary: '#2563eb' },
171
+ dark: { primary: '#60a5fa', activeText: '#0f172a', onPrimary: '#0f172a' },
172
+ }}>
173
+ <YourPage />
174
+ </AdminLayout>
175
+ ```
176
+
177
+ Resolution order is **selected built-in light/dark palette → `colors` → selected `light` or `dark` overrides**.
178
+ Missing, `undefined`, and empty color values retain the earlier value. An overridden `primary` also
179
+ sets `activeBackground` unless an explicit `activeBackground` override exists. Other color slots
180
+ remain independent. Neither the input configuration nor the built-in defaults are mutated.
181
+
182
+ ### Runtime switching and the existing toggle
183
+
184
+ ```tsx
185
+ import { useState } from 'react'
186
+ import { AdminLayout, type AdminLayoutThemeMode } from '@rqdhw3n/react-admin-layout'
187
+
188
+ function App() {
189
+ const [mode, setMode] = useState<AdminLayoutThemeMode>('system')
190
+ return (
191
+ <AdminLayout
192
+ sidebarItems={sidebarItems}
193
+ theme={{ mode, style: 'modern' }}
194
+ onThemeModeChange={setMode}
195
+ >
196
+ <YourPage />
197
+ </AdminLayout>
198
+ )
199
+ }
200
+ ```
201
+
202
+ An explicit `theme.mode` is **controlled**: it always wins over storage and `initialDarkMode`.
203
+ Prop changes apply immediately. Connect `onThemeModeChange` to update parent state when the
204
+ built-in `ThemeToggle`, `useDarkMode()`, or new hook requests a mode. Without that callback,
205
+ the controlled mode stays fixed. Toggling from system selects the opposite of the effective mode.
206
+
207
+ When `theme.mode` is omitted, the layout manages and persists the mode internally under the
208
+ existing `storageKey`. Existing stored `darkMode` values and `initialDarkMode` still work;
209
+ the saved state now also supports `mode: 'system'`. Controlled modes do not overwrite the
210
+ saved uncontrolled preference. Use distinct storage keys for independent layout preferences.
211
+
212
+ ```tsx
213
+ import { useAdminLayoutTheme } from '@rqdhw3n/react-admin-layout'
214
+
215
+ function FollowSystemButton() {
216
+ const { mode, resolvedMode, style, colors, setMode } = useAdminLayoutTheme()
217
+ return <button onClick={() => setMode('system')}>Follow system ({resolvedMode})</button>
218
+ }
219
+ ```
220
+
221
+ Render this hook beneath `AdminLayout` or `LayoutProvider`. `mode` is the requested mode;
222
+ `resolvedMode` is always `light` or `dark`. The existing `useDarkMode()` API is unchanged.
223
+ System mode subscribes to `prefers-color-scheme` changes and cleans up its listener. Server
224
+ rendering uses light for system mode; hydration then reads the OS preference and stored state.
225
+ Blocked storage and unavailable `matchMedia` are handled safely.
226
+
227
+ ### Public TypeScript API
228
+
229
+ ```ts
230
+ export type AdminLayoutThemeMode = 'light' | 'dark' | 'system'
231
+ export type AdminLayoutResolvedMode = 'light' | 'dark'
232
+ export type AdminLayoutStyle = 'modern' | 'minimal' | 'compact'
233
+
234
+ export interface AdminLayoutColors {
235
+ primary?: string
236
+ secondary?: string
237
+ background?: string
238
+ surface?: string
239
+ text?: string
240
+ textSecondary?: string
241
+ sidebarBackground?: string
242
+ sidebarText?: string
243
+ headerBackground?: string
244
+ headerText?: string
245
+ hoverBackground?: string
246
+ hoverText?: string
247
+ activeBackground?: string
248
+ activeText?: string
249
+ border?: string
250
+ onPrimary?: string
251
+ danger?: string
252
+ onDanger?: string
253
+ overlay?: string
254
+ }
255
+
256
+ export interface AdminLayoutTheme {
257
+ mode?: AdminLayoutThemeMode
258
+ style?: AdminLayoutStyle
259
+ colors?: AdminLayoutColors
260
+ light?: AdminLayoutColors
261
+ dark?: AdminLayoutColors
262
+ }
263
+ ```
264
+
265
+ `AdminLayoutProps` and `LayoutProviderProps` add `theme?: AdminLayoutTheme` and
266
+ `onThemeModeChange?: (mode: AdminLayoutThemeMode) => void`. All existing props remain.
267
+ Also exported: `ResolvedAdminLayoutTheme`, `lightThemeColors`, `darkThemeColors`,
268
+ `adminLayoutPresets`, `resolveTheme(theme?, systemMode?)`, and `themeToCSSVariables(resolvedTheme)`.
269
+ The pure resolver defaults its system-mode argument to light; automatic OS tracking belongs to the provider.
270
+
271
+ ### CSS variables and scope
272
+
273
+ The root exposes `.ral-layout.admin-layout-root`, `data-theme="light|dark"`, and
274
+ `data-style="modern|minimal|compact"`. Palette variables are applied once at that boundary.
275
+ Layouts with an explicit theme have independent colors and density, including nested layouts.
276
+ The `.dark` class is also present on dark layout roots for existing descendant Tailwind utilities.
277
+ For backward compatibility, **only layouts without any `theme` prop** still synchronize the
278
+ document's `.dark` class. Use `theme={{}}` to opt into scoped theming with an uncontrolled toggle.
279
+
280
+ | Palette fields | CSS variables |
281
+ | --- | --- |
282
+ | `primary`, `secondary` | `--ral-primary`, `--ral-secondary` |
283
+ | `background`, `surface` | `--ral-background`, `--ral-surface` |
284
+ | `text`, `textSecondary` | `--ral-text`, `--ral-text-secondary` |
285
+ | `sidebarBackground`, `sidebarText` | `--ral-sidebar-bg`, `--ral-sidebar-text` |
286
+ | `headerBackground`, `headerText` | `--ral-header-bg`, `--ral-header-text` |
287
+ | `hoverBackground`, `hoverText` | `--ral-hover-bg`, `--ral-hover-text` |
288
+ | `activeBackground`, `activeText` | `--ral-active-bg`, `--ral-active-text` |
289
+ | `border`, `onPrimary` | `--ral-border`, `--ral-on-primary` |
290
+ | `danger`, `onDanger`, `overlay` | `--ral-danger`, `--ral-on-danger`, `--ral-overlay` |
291
+
292
+ Density tokens include `--ral-sidebar-width`, `--ral-sidebar-collapsed`, `--ral-header-height`,
293
+ `--ral-radius`, `--ral-spacing`, `--ral-nav-item-height`, and `--ral-shadow`.
294
+ Existing `--admin-*` variables remain as aliases. The optional `.ral-card` class gives consumer
295
+ cards the selected surface, border, radius, padding, and shadow. Other consumer components
296
+ must use these variables or their own theme integration; arbitrary child markup is not restyled.
297
+
298
+ ```css
299
+ .ral-layout.my-layout { --ral-sidebar-width: 280px; }
300
+ .my-card { background: var(--ral-surface); color: var(--ral-text); border: 1px solid var(--ral-border); }
301
+ ```
302
+
303
+ Use `className="my-layout"` on `AdminLayout` and load your overrides after the package CSS.
304
+ Transitions respect `prefers-reduced-motion`. Keyboard focus remains visible, nested navigation
305
+ works when collapsed, user menu supports arrow keys/Escape, and the mobile drawer traps and restores focus.
306
+
307
+ ### Demo and verification
308
+
309
+ Development scripts require Node.js 22.18+ (native TypeScript stripping for the CSS generator).
310
+ This is a contributor requirement; the published package contains compiled JavaScript.
311
+
312
+ ```bash
313
+ npm install
314
+ npm run build
315
+ npm run demo
316
+ ```
317
+
318
+ Open `http://127.0.0.1:4173`. The demo includes light/dark/system, modern/minimal/compact,
319
+ default/indigo/blue/green/rose/orange palettes, separate mode palettes, and custom sidebar/header
320
+ colors. It consumes public package exports. `/?legacy` exercises the old uncontrolled API;
321
+ `/?isolation` demonstrates independent nested layouts.
322
+
323
+ ```bash
324
+ npm run typecheck
325
+ npm test
326
+ npm run verify:package
327
+ npx playwright install chromium
328
+ npm run test:browser
329
+ ```
330
+
331
+ `verify:package` packs the local build, installs that tarball into an isolated Vite app under
332
+ `.tmp`, checks every export target, compiles ESM/CommonJS type consumers, and builds the demo.
333
+ The browser suite then runs that installed app, including screenshots in `test-results`.
334
+ It tests with React 18 and React Router 6; the library's development build uses Router 7.
335
+ On Windows, an existing Edge installation can be used instead of downloading Chromium:
336
+
337
+ ```powershell
338
+ $env:PLAYWRIGHT_CHANNEL = 'msedge'
339
+ npm.cmd run test:browser
340
+ ```
341
+
342
+ ### Publishing (manual)
343
+
344
+ No automatic publishing is configured. For the next minor release from the existing 1.0.1:
345
+
346
+ ```bash
347
+ npm version minor --no-git-tag-version
348
+ npm install
349
+ npm run build
350
+ npm run typecheck
351
+ npm test
352
+ npm run verify:package
353
+ npm run test:browser
354
+ npm pack --dry-run
355
+ npm login
356
+ npm publish --access public
357
+ ```
358
+
359
+ Use `npm.cmd` on Windows if PowerShell blocks `npm.ps1`. The package name remains
360
+ `@rqdhw3n/react-admin-layout`; the version is left unchanged until you run the release commands.
361
+
362
+ ## React Router
88
363
 
89
364
  ```tsx
90
365
  import { BrowserRouter, Routes, Route } from 'react-router-dom'
@@ -113,7 +388,8 @@ function App() {
113
388
  | Hook | Description |
114
389
  |------|-------------|
115
390
  | `useSidebar()` | Collapsed state, mobile drawer |
116
- | `useDarkMode()` | Theme toggle |
391
+ | `useDarkMode()` | Theme toggle |
392
+ | `useAdminLayoutTheme()` | Requested/effective mode, palette, style, mode setter |
117
393
  | `useLayoutState()` | Full layout context |
118
394
  | `useBreadcrumbs()` | Breadcrumb items |
119
395
 
@@ -134,7 +410,8 @@ permissions={{ users: ['view', 'create'], roles: ['view'] }}
134
410
 
135
411
  - [`examples/basic/App.example.tsx`](./examples/basic/App.example.tsx)
136
412
  - [`examples/dark-mode/App.example.tsx`](./examples/dark-mode/App.example.tsx)
137
- - [`examples/sidebar-collapse/App.example.tsx`](./examples/sidebar-collapse/App.example.tsx)
413
+ - [`examples/sidebar-collapse/App.example.tsx`](./examples/sidebar-collapse/App.example.tsx)
414
+ - [`examples/theming/App.tsx`](./examples/theming/App.tsx)
138
415
 
139
416
  ## Build (library)
140
417
 
@@ -147,7 +424,8 @@ Outputs:
147
424
 
148
425
  - `dist/index.js` (ESM)
149
426
  - `dist/index.cjs` (CJS)
150
- - `dist/index.d.ts`
427
+ - `dist/index.d.ts`
428
+ - `dist/index.d.cts` (CommonJS declarations)
151
429
  - `dist/style.css`
152
430
 
153
431
  ## Peer dependencies
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),u=require("react"),T=require("react-router-dom"),x=require("lucide-react");function re(t){if(typeof window>"u")return{};try{const a=localStorage.getItem(t);return a?JSON.parse(a):{}}catch{return{}}}function ne(t,a){if(!(typeof window>"u"))try{localStorage.setItem(t,JSON.stringify(a))}catch{}}const U=u.createContext(null);function $({children:t,storageKey:a="rqdhw3n-admin-layout",defaultCollapsed:s=!1,permissions:r={},initialBreadcrumbs:l=[],initialActivePath:i="/",initialDarkMode:n}){const d=re(a),[o,b]=u.useState(d.sidebarCollapsed??s),[f,v]=u.useState(!1),[h,c]=u.useState(d.darkMode??n??!1),[p,N]=u.useState(l),[S,g]=u.useState(i);u.useEffect(()=>{ne(a,{sidebarCollapsed:o,darkMode:h})},[a,o,h]),u.useEffect(()=>{const y=document.documentElement;h?y.classList.add("dark"):y.classList.remove("dark")},[h]);const k=u.useCallback(()=>b(y=>!y),[]),j=u.useCallback(()=>v(y=>!y),[]),C=u.useCallback(()=>c(y=>!y),[]),A=u.useMemo(()=>({sidebarCollapsed:o,mobileOpen:f,darkMode:h,toggleSidebar:k,setSidebarCollapsed:b,toggleMobile:j,setMobileOpen:v,toggleDarkMode:C,setDarkMode:c,permissions:r,breadcrumbs:p,setBreadcrumbs:N,activePath:S,setActivePath:g}),[o,f,h,k,j,C,r,p,S]);return e.jsx(U.Provider,{value:A,children:t})}function w(){const t=u.useContext(U);if(!t)throw new Error("useLayoutContext must be used within LayoutProvider / AdminLayout");return t}function M(){const{sidebarCollapsed:t,mobileOpen:a,toggleSidebar:s,setSidebarCollapsed:r,toggleMobile:l,setMobileOpen:i}=w();return{collapsed:t,mobileOpen:a,toggle:s,setCollapsed:r,toggleMobile:l,setMobileOpen:i,closeMobile:()=>i(!1)}}function P(t){const a=t.split("/").filter(Boolean);return a.length?a.map((s,r)=>{const l="/"+a.slice(0,r+1).join("/");return{label:s.replace(/-/g," ").replace(/\b\w/g,n=>n.toUpperCase()),path:r<a.length-1?l:void 0}}):[{label:"Home",path:"/"}]}function z(t){var a,s,r="";if(typeof t=="string"||typeof t=="number")r+=t;else if(typeof t=="object")if(Array.isArray(t)){var l=t.length;for(a=0;a<l;a++)t[a]&&(s=z(t[a]))&&(r&&(r+=" "),r+=s)}else for(s in t)t[s]&&(r&&(r+=" "),r+=s);return r}function ie(){for(var t,a,s=0,r="",l=arguments.length;s<l;s++)(t=arguments[s])&&(a=z(t))&&(r&&(r+=" "),r+=a);return r}function m(...t){return ie(t)}function G({children:t}){const{pathname:a}=T.useLocation(),{setActivePath:s,setBreadcrumbs:r,breadcrumbs:l}=w();return u.useEffect(()=>{s(a),l.length||r(P(a))},[a,s,r,l.length]),e.jsx(e.Fragment,{children:t})}function H({children:t,className:a}){return e.jsx("footer",{className:m("mt-auto border-t border-admin-border bg-admin-surface px-6 py-4 text-sm text-admin-muted",a),children:t??e.jsxs("p",{className:"text-center",children:["© ",new Date().getFullYear()," Admin Layout · Built with @rqdhw3n/react-admin-layout"]})})}function le(t){const a=t.indexOf(".");return a===-1?{resource:t,action:"view"}:{resource:t.slice(0,a),action:t.slice(a+1)}}function J(t,a){if(!a)return!0;const{resource:s,action:r}=le(a),l=t[s];return l!=null&&l.length?l.includes(r)||l.includes("*"):!1}function L(t,a){return t.filter(s=>J(a,s.permission)).map(s=>({...s,children:s.children?L(s.children,a):void 0})).filter(s=>{var r;return!((r=s.children)!=null&&r.length)||s.children.length>0||s.path})}function O({label:t,children:a,collapsed:s,className:r}){return e.jsxs("div",{className:m("mb-4",r),children:[t&&!s&&e.jsx("p",{className:"mb-2 px-3 text-[11px] font-semibold uppercase tracking-wider text-admin-muted",children:t}),e.jsx("ul",{className:"space-y-0.5",children:a})]})}function q(t,a){return t?a===t||a.startsWith(`${t}/`):!1}function B({item:t,collapsed:a=!1,depth:s=0,activePath:r="/",onNavigate:l,LinkComponent:i,onItemClick:n}){var g,k;const d=!!((g=t.children)!=null&&g.length),o=t.path?q(t.path,r):!1,b=(k=t.children)==null?void 0:k.some(j=>j.path&&q(j.path,r)),[f,v]=u.useState(o||b),h=t.icon,c=m("group flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary",o||b?"bg-admin-primary text-white shadow-sm":"text-admin-muted hover:bg-admin-bg hover:text-admin-text",t.disabled&&"pointer-events-none opacity-50",s>0&&"text-[13px] py-2"),p=e.jsxs(e.Fragment,{children:[h&&e.jsx(h,{className:m("h-[18px] w-[18px] shrink-0",o||b?"text-white":""),"aria-hidden":!0}),!a&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"flex-1 truncate text-left",children:t.label}),t.badge!=null&&e.jsx("span",{className:m("rounded-full px-2 py-0.5 text-[11px] font-semibold",o?"bg-white/20 text-white":"bg-admin-primary/10 text-admin-primary"),children:t.badge}),d&&e.jsx(x.ChevronDown,{className:m("h-4 w-4 shrink-0 transition-transform",f&&"rotate-180"),"aria-hidden":!0})]})]}),N=()=>{if(d){v(j=>!j);return}t.path&&(l==null||l(t.path),n==null||n())},S=t.path&&!d&&i?e.jsx(i,{to:t.path,className:c,onClick:()=>{n==null||n()},children:p}):e.jsx("button",{type:"button",className:c,onClick:N,disabled:t.disabled,children:p});return e.jsxs("li",{children:[S,d&&f&&!a&&e.jsx("ul",{className:"mt-1 space-y-0.5 border-l border-admin-border ml-5 pl-2",children:t.children.map(j=>e.jsx(B,{item:j,collapsed:a,depth:s+1,activePath:r,onNavigate:l,LinkComponent:i,onItemClick:n},j.label+(j.path??"")))})]})}function Q({items:t,appName:a="Admin",appLogo:s,onNavigate:r,activePath:l,LinkComponent:i}){const{mobileOpen:n,closeMobile:d}=M(),{permissions:o,activePath:b}=w(),f=l??b,v=L(t,o);if(u.useEffect(()=>{if(!n)return;const c=p=>{p.key==="Escape"&&d()};return document.addEventListener("keydown",c),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",c),document.body.style.overflow=""}},[n,d]),!n)return null;const h=c=>{r==null||r(c),d()};return e.jsxs("div",{className:"fixed inset-0 z-50 lg:hidden",role:"dialog","aria-modal":"true",children:[e.jsx("button",{type:"button",className:"absolute inset-0 bg-slate-900/50 backdrop-blur-sm animate-fade-in","aria-label":"Close menu",onClick:d}),e.jsxs("aside",{className:m("absolute left-0 top-0 flex h-full w-[min(280px,85vw)] flex-col","border-r border-admin-border bg-admin-surface shadow-admin-lg animate-slide-in"),children:[e.jsxs("div",{className:"flex h-16 items-center justify-between border-b border-admin-border px-4",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[s??e.jsx("div",{className:"flex h-9 w-9 items-center justify-center rounded-xl bg-admin-primary text-sm font-bold text-white",children:a.charAt(0)}),e.jsx("span",{className:"font-semibold text-admin-text",children:a})]}),e.jsx("button",{type:"button",onClick:d,className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg","aria-label":"Close sidebar",children:e.jsx(x.X,{className:"h-5 w-5"})})]}),e.jsx("nav",{className:"flex-1 overflow-y-auto p-3",children:e.jsx(O,{children:v.map(c=>e.jsx(B,{item:c,activePath:f,onNavigate:h,LinkComponent:i,onItemClick:d},c.label+(c.path??"")))})})]})]})}function W({items:t,className:a,LinkComponent:s,onNavigate:r}){if(!t.length)return null;const l=(i,n)=>{const d=m("text-sm transition-colors",n?"font-medium text-admin-text":"text-admin-muted hover:text-admin-text");return!i.path||n?e.jsx("span",{className:d,children:i.label}):s?e.jsx(s,{to:i.path,className:d,children:i.label}):e.jsx("button",{type:"button",className:d,onClick:()=>r==null?void 0:r(i.path),children:i.label})};return e.jsxs("nav",{"aria-label":"Breadcrumb",className:m("flex items-center gap-1.5",a),children:[e.jsx(x.Home,{className:"h-4 w-4 text-admin-muted shrink-0","aria-hidden":!0}),t.map((i,n)=>{const d=n===t.length-1;return e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(x.ChevronRight,{className:"h-3.5 w-3.5 text-admin-muted","aria-hidden":!0}),l(i,d)]},`${i.label}-${n}`)})]})}function X({placeholder:t="Search...",onSearch:a,className:s}){const[r,l]=u.useState(""),i=n=>{n.preventDefault(),a==null||a(r.trim())};return e.jsxs("form",{onSubmit:i,className:m("relative w-full max-w-md",s),children:[e.jsx(x.Search,{className:"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-admin-muted","aria-hidden":!0}),e.jsx("input",{type:"search",value:r,onChange:n=>l(n.target.value),placeholder:t,className:m("h-10 w-full rounded-xl border border-admin-border bg-admin-bg pl-10 pr-4 text-sm text-admin-text","placeholder:text-admin-muted focus:border-admin-primary focus:outline-none focus:ring-2 focus:ring-admin-primary/20"),"aria-label":"Search"})]})}function Y({notifications:t=[],className:a}){const[s,r]=u.useState(!1),l=u.useRef(null),i=t.filter(n=>!n.read).length;return u.useEffect(()=>{const n=d=>{l.current&&!l.current.contains(d.target)&&r(!1)};return document.addEventListener("mousedown",n),()=>document.removeEventListener("mousedown",n)},[]),e.jsxs("div",{ref:l,className:m("relative",a),children:[e.jsxs("button",{type:"button",onClick:()=>r(n=>!n),className:m("relative inline-flex h-9 w-9 items-center justify-center rounded-lg border border-admin-border","bg-admin-surface text-admin-muted transition-colors hover:bg-admin-bg hover:text-admin-text","focus-visible:ring-2 focus-visible:ring-admin-primary"),"aria-label":`Notifications${i?`, ${i} unread`:""}`,"aria-expanded":s,children:[e.jsx(x.Bell,{className:"h-4 w-4","aria-hidden":!0}),i>0&&e.jsx("span",{className:"absolute -right-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-red-500 px-1 text-[10px] font-bold text-white",children:i>9?"9+":i})]}),s&&e.jsxs("div",{className:"absolute right-0 top-full z-50 mt-2 w-80 rounded-xl border border-admin-border bg-admin-surface shadow-admin-lg animate-fade-in",children:[e.jsx("div",{className:"border-b border-admin-border px-4 py-3",children:e.jsx("p",{className:"text-sm font-semibold text-admin-text",children:"Notifications"})}),e.jsx("ul",{className:"max-h-72 overflow-y-auto p-2",children:t.length===0?e.jsx("li",{className:"px-3 py-6 text-center text-sm text-admin-muted",children:"No notifications"}):t.map(n=>e.jsxs("li",{className:m("rounded-lg px-3 py-2.5 transition-colors hover:bg-admin-bg",!n.read&&"bg-admin-primary/5"),children:[e.jsx("p",{className:"text-sm font-medium text-admin-text",children:n.title}),n.description&&e.jsx("p",{className:"mt-0.5 text-xs text-admin-muted line-clamp-2",children:n.description}),n.time&&e.jsx("p",{className:"mt-1 text-[11px] text-admin-muted",children:n.time})]},n.id))})]})]})}function _(){const{darkMode:t,toggleDarkMode:a,setDarkMode:s}=w();return{isDark:t,toggle:a,setDarkMode:s}}function K({className:t}){const{isDark:a,toggle:s}=_();return e.jsx("button",{type:"button",onClick:s,className:m("inline-flex h-9 w-9 items-center justify-center rounded-lg border border-admin-border","bg-admin-surface text-admin-muted transition-colors hover:bg-admin-bg hover:text-admin-text","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary focus-visible:ring-offset-2",t),"aria-label":a?"Switch to light mode":"Switch to dark mode",children:a?e.jsx(x.Sun,{className:"h-4 w-4","aria-hidden":!0}):e.jsx(x.Moon,{className:"h-4 w-4","aria-hidden":!0})})}function V({user:t,onLogout:a,onSettings:s,className:r}){const[l,i]=u.useState(!1),n=u.useRef(null);u.useEffect(()=>{const o=b=>{n.current&&!n.current.contains(b.target)&&i(!1)};return document.addEventListener("mousedown",o),()=>document.removeEventListener("mousedown",o)},[]);const d=t.name.split(" ").map(o=>o[0]).join("").slice(0,2).toUpperCase();return e.jsxs("div",{ref:n,className:m("relative",r),children:[e.jsxs("button",{type:"button",onClick:()=>i(o=>!o),className:m("flex items-center gap-2 rounded-xl border border-admin-border bg-admin-surface px-2 py-1.5","transition-colors hover:bg-admin-bg focus-visible:ring-2 focus-visible:ring-admin-primary"),"aria-expanded":l,"aria-haspopup":"menu",children:[t.avatar?e.jsx("img",{src:t.avatar,alt:"",className:"h-8 w-8 rounded-lg object-cover"}):e.jsx("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-admin-primary text-xs font-semibold text-white",children:d}),e.jsxs("div",{className:"hidden text-left sm:block",children:[e.jsx("p",{className:"text-sm font-medium text-admin-text leading-tight",children:t.name}),e.jsx("p",{className:"text-xs text-admin-muted leading-tight",children:t.email})]}),e.jsx(x.ChevronDown,{className:"hidden h-4 w-4 text-admin-muted sm:block","aria-hidden":!0})]}),l&&e.jsxs("div",{role:"menu",className:"absolute right-0 top-full z-50 mt-2 w-56 rounded-xl border border-admin-border bg-admin-surface p-1.5 shadow-admin-lg animate-fade-in",children:[e.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-admin-text hover:bg-admin-bg",onClick:()=>{s==null||s(),i(!1)},children:[e.jsx(x.Settings,{className:"h-4 w-4 text-admin-muted"}),"Settings"]}),e.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-admin-text hover:bg-admin-bg",children:[e.jsx(x.User,{className:"h-4 w-4 text-admin-muted"}),"Profile"]}),e.jsx("hr",{className:"my-1 border-admin-border"}),e.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-red-600 hover:bg-red-50 dark:hover:bg-red-950/30",onClick:()=>{a==null||a(),i(!1)},children:[e.jsx(x.LogOut,{className:"h-4 w-4"}),"Log out"]})]})]})}function Z({user:t,notifications:a=[],onSearch:s,searchPlaceholder:r="Search...",showSearch:l=!0,className:i,breadcrumbs:n=[],onMenuClick:d,sidebarCollapsed:o,onToggleSidebar:b}){return e.jsxs("header",{className:m("sticky top-0 z-40 flex h-16 shrink-0 items-center gap-4 border-b border-admin-border","admin-glass bg-admin-surface px-4 lg:px-6",i),children:[e.jsx("button",{type:"button",className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg lg:hidden",onClick:d,"aria-label":"Open menu",children:e.jsx(x.Menu,{className:"h-5 w-5"})}),e.jsx("button",{type:"button",className:"hidden rounded-lg p-2 text-admin-muted hover:bg-admin-bg lg:inline-flex",onClick:b,"aria-label":o?"Expand sidebar":"Collapse sidebar",children:o?e.jsx(x.PanelLeftOpen,{className:"h-5 w-5"}):e.jsx(x.PanelLeftClose,{className:"h-5 w-5"})}),e.jsx("div",{className:"hidden min-w-0 flex-1 md:block",children:e.jsx(W,{items:n})}),e.jsxs("div",{className:"flex flex-1 items-center justify-end gap-2 sm:gap-3",children:[l&&e.jsx("div",{className:"hidden flex-1 sm:flex sm:max-w-md",children:e.jsx(X,{placeholder:r,onSearch:s})}),e.jsx(K,{}),e.jsx(Y,{notifications:a}),t&&e.jsx(V,{user:t})]})]})}function I({items:t,collapsed:a,appName:s="Admin",appLogo:r,className:l,onNavigate:i,activePath:n,LinkComponent:d}){const{collapsed:o,toggle:b,closeMobile:f}=M(),{permissions:v,activePath:h}=w(),c=a??o,p=n??h,N=L(t,v),S=g=>{i==null||i(g),f()};return e.jsxs("aside",{className:m("hidden lg:flex flex-col border-r border-admin-border bg-admin-surface transition-all duration-300 ease-in-out",c?"w-[var(--admin-sidebar-collapsed)]":"w-[var(--admin-sidebar-width)]",l),"aria-label":"Sidebar navigation",children:[e.jsxs("div",{className:m("flex h-16 items-center border-b border-admin-border px-4",c?"justify-center":"justify-between gap-2"),children:[e.jsxs("div",{className:m("flex items-center gap-3 min-w-0",c&&"justify-center"),children:[r??e.jsx("div",{className:"flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-admin-primary text-sm font-bold text-white shadow-admin",children:s.charAt(0)}),!c&&e.jsx("span",{className:"truncate text-base font-semibold text-admin-text",children:s})]}),!c&&e.jsx("button",{type:"button",onClick:b,className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg hover:text-admin-text focus-visible:ring-2 focus-visible:ring-admin-primary","aria-label":"Collapse sidebar",children:e.jsx(x.PanelLeftClose,{className:"h-4 w-4"})})]}),c&&e.jsx("div",{className:"flex justify-center py-2 border-b border-admin-border",children:e.jsx("button",{type:"button",onClick:b,className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg","aria-label":"Expand sidebar",children:e.jsx(x.PanelLeftOpen,{className:"h-4 w-4"})})}),e.jsx("nav",{className:"flex-1 overflow-y-auto p-3",children:e.jsx(O,{collapsed:c,children:N.map(g=>e.jsx(B,{item:g,collapsed:c,activePath:p,onNavigate:S,LinkComponent:d},g.label+(g.path??"")))})})]})}function F({children:t,sidebarItems:a,appName:s="Admin",appLogo:r,user:l,notifications:i=[],footer:n,showFooter:d=!0,showSearch:o=!0,searchPlaceholder:b,onSearch:f,breadcrumbs:v,contentClassName:h,activePath:c,onNavigate:p,LinkComponent:N,enableRouter:S=!1}){const{sidebarCollapsed:g,toggleSidebar:k,toggleMobile:j,breadcrumbs:C,setBreadcrumbs:A,activePath:y,setActivePath:ee}=w(),{closeMobile:te}=M(),D=c??y,ae=v??C,R=E=>{ee(E),v||A(P(E)),p==null||p(E),te()},se=g?"lg:pl-[var(--admin-sidebar-collapsed)]":"lg:pl-[var(--admin-sidebar-width)]";return e.jsxs("div",{className:"admin-layout-root min-h-screen bg-admin-bg text-admin-text",children:[e.jsx(I,{items:a,appName:s,appLogo:r,activePath:D,onNavigate:R,LinkComponent:N}),e.jsx(Q,{items:a,appName:s,appLogo:r,activePath:D,onNavigate:R,LinkComponent:N}),e.jsxs("div",{className:m("flex min-h-screen flex-col transition-all duration-300",se),children:[e.jsx(Z,{user:l,notifications:i,onSearch:f,searchPlaceholder:b,showSearch:o,breadcrumbs:ae,onMenuClick:j,sidebarCollapsed:g,onToggleSidebar:k}),e.jsx("main",{className:m("flex-1 p-4 lg:p-6",h),children:t}),d&&e.jsx(H,{children:n})]})]})}function de(t){const{storageKey:a="rqdhw3n-admin-layout",defaultCollapsed:s=!1,initialDarkMode:r=!1,permissions:l={},breadcrumbs:i=[],activePath:n="/",enableRouter:d=!1,children:o,...b}=t,f=u.useMemo(()=>i.length?i:P(n),[i,n]);return e.jsx($,{storageKey:a,defaultCollapsed:s,permissions:l,initialBreadcrumbs:f,initialActivePath:n,initialDarkMode:r,children:d?e.jsx(G,{children:e.jsx(F,{...b,enableRouter:d,children:o})}):e.jsx(F,{...b,enableRouter:d,children:o})})}function oe({to:t,className:a,children:s,onClick:r}){return e.jsx(T.NavLink,{to:t,className:a,onClick:r,children:s})}function ce(){return w()}function ue(){const{breadcrumbs:t,setBreadcrumbs:a}=w(),s=u.useCallback(r=>a(r),[a]);return{items:t,setBreadcrumbs:s}}exports.AdminLayout=de;exports.AdminLayoutRouterSync=G;exports.Breadcrumbs=W;exports.Footer=H;exports.LayoutProvider=$;exports.MobileSidebar=Q;exports.Navbar=Z;exports.NavbarSearch=X;exports.NotificationMenu=Y;exports.RouterLink=oe;exports.Sidebar=I;exports.SidebarGroup=O;exports.SidebarItem=B;exports.ThemeToggle=K;exports.UserMenu=V;exports.cn=m;exports.filterSidebarByPermissions=L;exports.generateBreadcrumbsFromPath=P;exports.hasPermission=J;exports.useBreadcrumbs=ue;exports.useDarkMode=_;exports.useLayoutContext=w;exports.useLayoutState=ce;exports.useSidebar=M;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),c=require("react"),Q=require("react-router-dom"),g=require("lucide-react");function G(e){if(typeof window>"u")return{};try{const r=localStorage.getItem(e),n=r?JSON.parse(r):null;if(!n||typeof n!="object")return{};const a=n;return{sidebarCollapsed:typeof a.sidebarCollapsed=="boolean"?a.sidebarCollapsed:void 0,darkMode:typeof a.darkMode=="boolean"?a.darkMode:void 0,mode:a.mode==="light"||a.mode==="dark"||a.mode==="system"?a.mode:void 0}}catch{return{}}}function pe(e,r){if(!(typeof window>"u"))try{localStorage.setItem(e,JSON.stringify(r))}catch{}}const H=Object.freeze({primary:"#2563eb",secondary:"#7c3aed",background:"#f8fafc",surface:"#ffffff",text:"#0f172a",textSecondary:"#64748b",sidebarBackground:"#ffffff",sidebarText:"#64748b",headerBackground:"#ffffff",headerText:"#0f172a",hoverBackground:"#f1f5f9",hoverText:"#0f172a",activeBackground:"#2563eb",activeText:"#ffffff",border:"#e2e8f0",onPrimary:"#ffffff",danger:"#dc2626",onDanger:"#ffffff",overlay:"rgb(15 23 42 / 0.5)"}),W=Object.freeze({...H,primary:"#3b82f6",secondary:"#a78bfa",background:"#0b1220",surface:"#111827",text:"#f1f5f9",textSecondary:"#94a3b8",sidebarBackground:"#111827",sidebarText:"#cbd5e1",headerBackground:"#111827",headerText:"#f1f5f9",hoverBackground:"#1e293b",hoverText:"#ffffff",activeBackground:"#3b82f6",activeText:"#020617",onPrimary:"#020617",border:"#334155",danger:"#f87171",onDanger:"#020617",overlay:"rgb(2 6 23 / 0.7)"}),ve=Object.freeze({modern:Object.freeze({style:"modern"}),minimal:Object.freeze({style:"minimal"}),compact:Object.freeze({style:"compact"})}),J={primary:"--ral-primary",secondary:"--ral-secondary",background:"--ral-background",surface:"--ral-surface",text:"--ral-text",textSecondary:"--ral-text-secondary",sidebarBackground:"--ral-sidebar-bg",sidebarText:"--ral-sidebar-text",headerBackground:"--ral-header-bg",headerText:"--ral-header-text",hoverBackground:"--ral-hover-bg",hoverText:"--ral-hover-text",activeBackground:"--ral-active-bg",activeText:"--ral-active-text",border:"--ral-border",onPrimary:"--ral-on-primary",danger:"--ral-danger",onDanger:"--ral-on-danger",overlay:"--ral-overlay"};function X(e={},r="light"){const n=e.mode==="system"?r:e.mode??"light",a={...n==="dark"?W:H},i={};for(const d of[e.colors,e[n]])if(d)for(const s of Object.keys(a)){const o=d[s];typeof o=="string"&&o.trim()&&(i[s]=o)}return Object.assign(a,i),i.primary&&!i.activeBackground&&(a.activeBackground=i.primary),{mode:n,style:e.style??"modern",colors:a}}function Y(e){return Object.fromEntries(Object.keys(J).map(r=>[J[r],e.colors[r]]))}const _=c.createContext(null);function Z(){const e=c.useContext(_);if(!e)throw new Error("useAdminLayoutTheme must be used within LayoutProvider / AdminLayout");return{mode:e.requestedMode,resolvedMode:e.mode,style:e.style,colors:e.colors,setMode:e.setMode}}const K="(prefers-color-scheme: dark)",ye=()=>"light";function ge(e){const r=c.useCallback(a=>{if(!e||typeof window>"u"||!window.matchMedia)return()=>{};const i=window.matchMedia(K);return i.addEventListener?(i.addEventListener("change",a),()=>i.removeEventListener("change",a)):(i.addListener(a),()=>i.removeListener(a))},[e]),n=c.useCallback(()=>{var a;return e&&typeof window<"u"&&((a=window.matchMedia)!=null&&a.call(window,K).matches)?"dark":"light"},[e]);return c.useSyncExternalStore(r,n,ye)}const ee=c.createContext(null);function te({children:e,storageKey:r="rqdhw3n-admin-layout",defaultCollapsed:n=!1,permissions:a={},initialBreadcrumbs:i=[],initialActivePath:d="/",initialDarkMode:s,theme:o,onThemeModeChange:l}){const[u,p]=c.useState(n),[w,y]=c.useState(!1),[f,h]=c.useState(s?"dark":"light"),[k,S]=c.useState(null),v=(o==null?void 0:o.mode)??f,b=ge(v==="system"),A=X({...o,mode:v},b),N=A.mode==="dark",P=c.useCallback(x=>{(o==null?void 0:o.mode)===void 0&&h(x),l==null||l(x)},[o==null?void 0:o.mode,l]),L=c.useCallback(x=>P(x?"dark":"light"),[P]),[T,j]=c.useState(i),[C,M]=c.useState(d);c.useEffect(()=>{const x=G(r);p(x.sidebarCollapsed??n),h(x.mode??(x.darkMode??s?"dark":"light")),S(r)},[r]),c.useEffect(()=>{if(k!==r)return;const x=G(r);pe(r,(o==null?void 0:o.mode)===void 0?{sidebarCollapsed:u,darkMode:N,mode:v}:{...x,sidebarCollapsed:u})},[r,u,N,v,o==null?void 0:o.mode,k]),c.useEffect(()=>{if(o!==void 0)return;const x=document.documentElement,D=x.classList.contains("dark");return N?x.classList.add("dark"):x.classList.remove("dark"),()=>{x.classList.toggle("dark",D)}},[N,o]);const B=c.useCallback(()=>p(x=>!x),[]),q=c.useCallback(()=>y(x=>!x),[]),O=c.useCallback(()=>L(!N),[L,N]),$=c.useMemo(()=>({sidebarCollapsed:u,mobileOpen:w,darkMode:N,toggleSidebar:B,setSidebarCollapsed:p,toggleMobile:q,setMobileOpen:y,toggleDarkMode:O,setDarkMode:L,permissions:a,breadcrumbs:T,setBreadcrumbs:j,activePath:C,setActivePath:M}),[u,w,N,B,q,O,L,a,T,C]);return t.jsx(ee.Provider,{value:$,children:t.jsx(_.Provider,{value:{...A,requestedMode:v,setMode:P},children:e})})}function E(){const e=c.useContext(ee);if(!e)throw new Error("useLayoutContext must be used within LayoutProvider / AdminLayout");return e}function R(){const{sidebarCollapsed:e,mobileOpen:r,toggleSidebar:n,setSidebarCollapsed:a,toggleMobile:i,setMobileOpen:d}=E(),s=c.useCallback(()=>d(!1),[d]);return{collapsed:e,mobileOpen:r,toggle:n,setCollapsed:a,toggleMobile:i,setMobileOpen:d,closeMobile:s}}function z(e){const r=e.split("/").filter(Boolean);return r.length?r.map((n,a)=>{const i="/"+r.slice(0,a+1).join("/");return{label:n.replace(/-/g," ").replace(/\b\w/g,s=>s.toUpperCase()),path:a<r.length-1?i:void 0}}):[{label:"Home",path:"/"}]}function re(e){var r,n,a="";if(typeof e=="string"||typeof e=="number")a+=e;else if(typeof e=="object")if(Array.isArray(e)){var i=e.length;for(r=0;r<i;r++)e[r]&&(n=re(e[r]))&&(a&&(a+=" "),a+=n)}else for(n in e)e[n]&&(a&&(a+=" "),a+=n);return a}function je(){for(var e,r,n=0,a="",i=arguments.length;n<i;n++)(e=arguments[n])&&(r=re(e))&&(a&&(a+=" "),a+=r);return a}function m(...e){return je(e)}function ae({children:e}){const{pathname:r}=Q.useLocation(),{setActivePath:n,setBreadcrumbs:a,breadcrumbs:i}=E();return c.useEffect(()=>{n(r),i.length||a(z(r))},[r,n,a,i.length]),t.jsx(t.Fragment,{children:e})}function ne({children:e,className:r}){return t.jsx("footer",{className:m("mt-auto border-t border-admin-border bg-admin-surface px-6 py-4 text-sm text-admin-muted",r),children:e??t.jsxs("p",{className:"text-center",children:["© ",new Date().getFullYear()," Admin Layout · Built with @rqdhw3n/react-admin-layout"]})})}function we(e){const r=e.indexOf(".");return r===-1?{resource:e,action:"view"}:{resource:e.slice(0,r),action:e.slice(r+1)}}function se(e,r){if(!r)return!0;const{resource:n,action:a}=we(r),i=e[n];return i!=null&&i.length?i.includes(a)||i.includes("*"):!1}function F(e,r){return e.filter(n=>se(r,n.permission)).map(n=>({...n,children:n.children?F(n.children,r):void 0})).filter(n=>{var a;return!((a=n.children)!=null&&a.length)||n.children.length>0||n.path})}function V({label:e,children:r,collapsed:n,className:a}){return t.jsxs("div",{className:m("ral-sidebar-group mb-4",a),children:[e&&!n&&t.jsx("p",{className:"ral-sidebar-brand mb-2 px-3 text-[11px] font-semibold uppercase tracking-wider",children:e}),t.jsx("ul",{className:"space-y-0.5",children:r})]})}function ie(e,r){return e?r===e||r.startsWith(`${e}/`):!1}function oe(e,r){var n;return!!((n=e.children)!=null&&n.some(a=>ie(a.path,r)||oe(a,r)))}function U({item:e,collapsed:r=!1,depth:n=0,activePath:a="/",onNavigate:i,LinkComponent:d,onItemClick:s}){var v;const o=!!((v=e.children)!=null&&v.length),l=e.path?ie(e.path,a):!1,u=oe(e,a),[p,w]=c.useState(l||u);c.useEffect(()=>{u&&w(!0)},[a,u]);const y=e.icon,f=m("ral-nav-item group flex w-full items-center gap-3 px-3 text-sm font-medium","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary",e.disabled&&"pointer-events-none opacity-50",n>0&&"text-[13px]",r&&"justify-center",(l||u)&&"ral-nav-active"),h=t.jsxs(t.Fragment,{children:[y&&t.jsx(y,{className:"h-[18px] w-[18px] shrink-0","aria-hidden":!0}),r&&!y&&t.jsx("span",{"aria-hidden":!0,children:e.label.charAt(0)}),r&&t.jsx("span",{className:"sr-only",children:e.label}),!r&&t.jsxs(t.Fragment,{children:[t.jsx("span",{className:"flex-1 truncate text-left",children:e.label}),e.badge!=null&&t.jsx("span",{className:m("ral-nav-badge rounded-full px-2 py-0.5 text-[11px] font-semibold"),children:e.badge}),o&&t.jsx(g.ChevronDown,{className:m("h-4 w-4 shrink-0 transition-transform",p&&"rotate-180"),"aria-hidden":!0})]})]}),k=()=>{if(!e.disabled){if(o){w(b=>!b);return}e.path&&(i==null||i(e.path),s==null||s())}},S=e.path&&!o&&d&&!e.disabled?t.jsx(d,{to:e.path,className:f,onClick:k,children:h}):t.jsx("button",{type:"button",className:f,onClick:k,disabled:e.disabled,"data-active":l||u,"aria-label":r?e.label:void 0,title:r?e.label:void 0,"aria-expanded":o?!!p:void 0,"aria-current":l&&!o?"page":void 0,children:h});return t.jsxs("li",{"data-active":l||u,"aria-label":r?e.label:void 0,children:[S,o&&p&&t.jsx("ul",{className:m("mt-1 space-y-0.5",!r&&"border-l border-admin-border ml-5 pl-2"),children:e.children.map(b=>t.jsx(U,{item:b,collapsed:r,depth:n+1,activePath:a,onNavigate:i,LinkComponent:d,onItemClick:s},b.label+(b.path??"")))})]})}function de({items:e,appName:r="Admin",appLogo:n,onNavigate:a,activePath:i,LinkComponent:d}){const{mobileOpen:s,closeMobile:o}=R(),{permissions:l,activePath:u}=E(),p=i??u,w=F(e,l),y=c.useRef(null);if(c.useEffect(()=>{var N,P,L,T;if(!s)return;const h=document.activeElement,k=document.body.style.overflow,S=()=>{var j;return Array.from(((j=y.current)==null?void 0:j.querySelectorAll('button:not(:disabled), a[href], input, [tabindex="0"]'))??[]).filter(C=>C.getClientRects().length>0)};(P=(N=y.current)==null?void 0:N.querySelector('[aria-label="Close sidebar"]'))==null||P.focus();const v=j=>{if(j.key==="Escape"&&o(),j.key==="Tab"){const C=S(),M=C[0],B=C[C.length-1];j.shiftKey&&document.activeElement===M?(j.preventDefault(),B==null||B.focus()):!j.shiftKey&&document.activeElement===B&&(j.preventDefault(),M==null||M.focus())}},b=(L=window.matchMedia)==null?void 0:L.call(window,"(min-width: 1024px)"),A=()=>{b!=null&&b.matches&&o()};return(T=b==null?void 0:b.addEventListener)==null||T.call(b,"change",A),document.addEventListener("keydown",v),document.body.style.overflow="hidden",()=>{var j;document.removeEventListener("keydown",v),(j=b==null?void 0:b.removeEventListener)==null||j.call(b,"change",A),document.body.style.overflow=k,h==null||h.focus()}},[s,o]),!s)return null;const f=h=>{a==null||a(h),o()};return t.jsxs("div",{ref:y,className:"fixed inset-0 z-50 lg:hidden",role:"dialog","aria-modal":"true","aria-label":"Navigation menu",children:[t.jsx("button",{type:"button",className:"ral-overlay absolute inset-0 backdrop-blur-sm animate-fade-in","aria-label":"Close menu",onClick:o}),t.jsxs("aside",{className:m("ral-sidebar absolute left-0 top-0 flex h-full w-[min(var(--ral-sidebar-width),85vw)] flex-col","border-r border-admin-border shadow-admin-lg animate-slide-in"),children:[t.jsxs("div",{className:"ral-sidebar-top flex items-center justify-between border-b border-admin-border px-4",children:[t.jsxs("div",{className:"flex items-center gap-3",children:[n??t.jsx("div",{className:"flex h-9 w-9 items-center justify-center rounded-admin bg-admin-primary text-sm font-bold text-admin-on-primary",children:r.charAt(0)}),t.jsx("span",{className:"ral-sidebar-brand font-semibold",children:r})]}),t.jsx("button",{type:"button",onClick:o,className:"rounded-admin p-2 text-admin-muted hover:bg-admin-hover","aria-label":"Close sidebar",children:t.jsx(g.X,{className:"h-5 w-5"})})]}),t.jsx("nav",{className:"flex-1 overflow-y-auto p-3",children:t.jsx(V,{children:w.map(h=>t.jsx(U,{item:h,activePath:p,onNavigate:f,LinkComponent:d,onItemClick:o},h.label+(h.path??"")))})})]})]})}function le({items:e,className:r,LinkComponent:n,onNavigate:a}){if(!e.length)return null;const i=(d,s)=>{const o=m("text-sm transition-colors",s?"font-medium text-admin-text":"text-admin-muted hover:text-admin-hover-text");return!d.path||s?t.jsx("span",{className:o,children:d.label}):n?t.jsx(n,{to:d.path,className:o,children:d.label}):t.jsx("button",{type:"button",className:o,onClick:()=>a==null?void 0:a(d.path),children:d.label})};return t.jsxs("nav",{"aria-label":"Breadcrumb",className:m("flex items-center gap-1.5",r),children:[t.jsx(g.Home,{className:"h-4 w-4 text-admin-muted shrink-0","aria-hidden":!0}),e.map((d,s)=>{const o=s===e.length-1;return t.jsxs("span",{className:"flex items-center gap-1.5",children:[t.jsx(g.ChevronRight,{className:"h-3.5 w-3.5 text-admin-muted","aria-hidden":!0}),i(d,o)]},`${d.label}-${s}`)})]})}function ce({placeholder:e="Search...",onSearch:r,className:n}){const[a,i]=c.useState(""),d=s=>{s.preventDefault(),r==null||r(a.trim())};return t.jsxs("form",{onSubmit:d,className:m("relative w-full max-w-md",n),children:[t.jsx(g.Search,{className:"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-admin-muted","aria-hidden":!0}),t.jsx("input",{type:"search",value:a,onChange:s=>i(s.target.value),placeholder:e,className:m("h-10 w-full rounded-admin border border-admin-border bg-admin-bg pl-10 pr-4 text-sm text-admin-text","placeholder:text-admin-muted focus:border-admin-primary focus:outline-none focus:ring-2 focus:ring-admin-primary"),"aria-label":"Search"})]})}function ue({notifications:e=[],className:r}){const[n,a]=c.useState(!1),i=c.useRef(null),d=e.filter(s=>!s.read).length;return c.useEffect(()=>{const s=o=>{i.current&&!i.current.contains(o.target)&&a(!1)};return document.addEventListener("mousedown",s),()=>document.removeEventListener("mousedown",s)},[]),t.jsxs("div",{ref:i,className:m("relative",r),onKeyDown:s=>{var o,l;s.key==="Escape"&&(a(!1),(l=(o=i.current)==null?void 0:o.querySelector("button"))==null||l.focus())},children:[t.jsxs("button",{type:"button",onClick:()=>a(s=>!s),className:m("relative inline-flex h-9 w-9 items-center justify-center rounded-admin border border-admin-border","bg-admin-surface text-admin-muted transition-colors hover:bg-admin-hover hover:text-admin-hover-text","focus-visible:ring-2 focus-visible:ring-admin-primary"),"aria-label":`Notifications${d?`, ${d} unread`:""}`,"aria-expanded":n,children:[t.jsx(g.Bell,{className:"h-4 w-4","aria-hidden":!0}),d>0&&t.jsx("span",{className:"absolute -right-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-admin-danger px-1 text-[10px] font-bold text-admin-on-danger",children:d>9?"9+":d})]}),n&&t.jsxs("div",{className:"absolute right-0 top-full z-50 mt-2 w-80 max-w-[calc(100vw-6rem)] rounded-admin border border-admin-border bg-admin-surface shadow-admin-lg animate-fade-in",children:[t.jsx("div",{className:"border-b border-admin-border px-4 py-3",children:t.jsx("p",{className:"text-sm font-semibold text-admin-text",children:"Notifications"})}),t.jsx("ul",{className:"max-h-72 overflow-y-auto p-2",children:e.length===0?t.jsx("li",{className:"px-3 py-6 text-center text-sm text-admin-muted",children:"No notifications"}):e.map(s=>t.jsxs("li",{className:m("rounded-admin px-3 py-2.5 transition-colors hover:bg-admin-hover",!s.read&&"ral-unread"),children:[t.jsx("p",{className:"text-sm font-medium text-admin-text",children:s.title}),s.description&&t.jsx("p",{className:"mt-0.5 text-xs text-admin-muted line-clamp-2",children:s.description}),s.time&&t.jsx("p",{className:"mt-1 text-[11px] text-admin-muted",children:s.time})]},s.id))})]})]})}function me(){const{darkMode:e,toggleDarkMode:r,setDarkMode:n}=E();return{isDark:e,toggle:r,setDarkMode:n}}function fe({className:e}){const{isDark:r,toggle:n}=me();return t.jsx("button",{type:"button",onClick:n,className:m("inline-flex h-9 w-9 items-center justify-center rounded-admin border border-admin-border","bg-admin-surface text-admin-muted transition-colors hover:bg-admin-hover hover:text-admin-hover-text","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary focus-visible:ring-offset-2",e),"aria-label":r?"Switch to light mode":"Switch to dark mode",children:r?t.jsx(g.Sun,{className:"h-4 w-4","aria-hidden":!0}):t.jsx(g.Moon,{className:"h-4 w-4","aria-hidden":!0})})}function be({user:e,onLogout:r,onSettings:n,className:a}){const[i,d]=c.useState(!1),s=c.useRef(null);c.useEffect(()=>{const l=u=>{s.current&&!s.current.contains(u.target)&&d(!1)};return document.addEventListener("mousedown",l),()=>document.removeEventListener("mousedown",l)},[]),c.useEffect(()=>{var l,u;i&&((u=(l=s.current)==null?void 0:l.querySelector('[role="menuitem"]'))==null||u.focus())},[i]);const o=e.name.split(" ").map(l=>l[0]).join("").slice(0,2).toUpperCase();return t.jsxs("div",{ref:s,className:m("relative",a),onKeyDown:l=>{var p,w,y;const u=(p=s.current)==null?void 0:p.querySelector("[aria-haspopup]");if(l.key==="Escape"&&i&&(l.preventDefault(),d(!1),u==null||u.focus()),l.key==="Tab"&&d(!1),["ArrowDown","ArrowUp","Home","End"].includes(l.key)){if(l.preventDefault(),!i){d(!0);return}const f=Array.from(((w=s.current)==null?void 0:w.querySelectorAll('[role="menuitem"]'))??[]),h=f.indexOf(document.activeElement),k=l.key==="Home"?0:l.key==="End"?f.length-1:(h+(l.key==="ArrowDown"?1:-1)+f.length)%f.length;(y=f[k])==null||y.focus()}},children:[t.jsxs("button",{type:"button",onClick:()=>d(l=>!l),className:m("flex items-center gap-2 rounded-admin border border-admin-border bg-admin-surface px-2 py-1.5","transition-colors hover:bg-admin-hover focus-visible:ring-2 focus-visible:ring-admin-primary"),"aria-expanded":i,"aria-haspopup":"menu","aria-label":`User menu for ${e.name}`,children:[e.avatar?t.jsx("img",{src:e.avatar,alt:"",className:"h-8 w-8 rounded-admin object-cover"}):t.jsx("div",{className:"flex h-8 w-8 items-center justify-center rounded-admin bg-admin-primary text-xs font-semibold text-admin-on-primary",children:o}),t.jsxs("div",{className:"hidden text-left sm:block",children:[t.jsx("p",{className:"text-sm font-medium text-admin-text leading-tight",children:e.name}),t.jsx("p",{className:"text-xs text-admin-muted leading-tight",children:e.email})]}),t.jsx(g.ChevronDown,{className:"hidden h-4 w-4 text-admin-muted sm:block","aria-hidden":!0})]}),i&&t.jsxs("div",{role:"menu",className:"absolute right-0 top-full z-50 mt-2 w-56 rounded-admin border border-admin-border bg-admin-surface p-1.5 shadow-admin-lg animate-fade-in",children:[t.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-admin px-3 py-2 text-sm text-admin-text hover:bg-admin-hover",onClick:()=>{n==null||n(),d(!1)},children:[t.jsx(g.Settings,{className:"h-4 w-4 text-admin-muted"}),"Settings"]}),t.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-admin px-3 py-2 text-sm text-admin-text hover:bg-admin-hover",children:[t.jsx(g.User,{className:"h-4 w-4 text-admin-muted"}),"Profile"]}),t.jsx("hr",{className:"my-1 border-admin-border"}),t.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-admin px-3 py-2 text-sm ral-danger",onClick:()=>{r==null||r(),d(!1)},children:[t.jsx(g.LogOut,{className:"h-4 w-4"}),"Log out"]})]})]})}function xe({user:e,notifications:r=[],onSearch:n,searchPlaceholder:a="Search...",showSearch:i=!0,className:d,breadcrumbs:s=[],onMenuClick:o,sidebarCollapsed:l,onToggleSidebar:u}){return t.jsxs("header",{className:m("ral-header sticky top-0 z-40 flex shrink-0 items-center gap-4 border-b border-admin-border",d),children:[t.jsx("button",{type:"button",className:"rounded-admin p-2 text-admin-muted hover:bg-admin-hover lg:hidden",onClick:o,"aria-label":"Open menu",children:t.jsx(g.Menu,{className:"h-5 w-5"})}),t.jsx("button",{type:"button",className:"hidden rounded-admin p-2 text-admin-muted hover:bg-admin-hover lg:inline-flex",onClick:u,"aria-label":l?"Expand sidebar":"Collapse sidebar",children:l?t.jsx(g.PanelLeftOpen,{className:"h-5 w-5"}):t.jsx(g.PanelLeftClose,{className:"h-5 w-5"})}),t.jsx("div",{className:"hidden min-w-0 flex-1 md:block",children:t.jsx(le,{items:s})}),t.jsxs("div",{className:"flex flex-1 items-center justify-end gap-2 sm:gap-3",children:[i&&t.jsx("div",{className:"hidden flex-1 sm:flex sm:max-w-md",children:t.jsx(ce,{placeholder:a,onSearch:n})}),t.jsx(fe,{}),t.jsx(ue,{notifications:r}),e&&t.jsx(be,{user:e})]})]})}function he({items:e,collapsed:r,appName:n="Admin",appLogo:a,className:i,onNavigate:d,activePath:s,LinkComponent:o}){const{collapsed:l,toggle:u,closeMobile:p}=R(),{permissions:w,activePath:y}=E(),f=r??l,h=s??y,k=F(e,w),S=v=>{d==null||d(v),p()};return t.jsxs("aside",{className:m("ral-sidebar hidden lg:flex shrink-0 flex-col border-r border-admin-border","h-screen sticky top-0 transition-[width] duration-300 ease-in-out",f?"w-[var(--admin-sidebar-collapsed)]":"w-[var(--admin-sidebar-width)]",i),"aria-label":"Sidebar navigation",children:[t.jsxs("div",{className:m("ral-sidebar-top flex items-center border-b border-admin-border px-4",f?"justify-center":"justify-between gap-2"),children:[t.jsxs("div",{className:m("flex items-center gap-3 min-w-0",f&&"justify-center"),children:[a??t.jsx("div",{className:"flex h-9 w-9 shrink-0 items-center justify-center rounded-admin bg-admin-primary text-sm font-bold text-admin-on-primary shadow-admin",children:n.charAt(0)}),!f&&t.jsx("span",{className:"ral-sidebar-brand truncate text-base font-semibold",children:n})]}),!f&&t.jsx("button",{type:"button",onClick:u,className:"rounded-admin p-2 text-admin-muted hover:bg-admin-hover hover:text-admin-hover-text focus-visible:ring-2 focus-visible:ring-admin-primary","aria-label":"Collapse sidebar",children:t.jsx(g.PanelLeftClose,{className:"h-4 w-4"})})]}),f&&t.jsx("div",{className:"flex justify-center py-2 border-b border-admin-border",children:t.jsx("button",{type:"button",onClick:u,className:"rounded-admin p-2 text-admin-muted hover:bg-admin-hover","aria-label":"Expand sidebar",children:t.jsx(g.PanelLeftOpen,{className:"h-4 w-4"})})}),t.jsx("nav",{className:"flex-1 overflow-y-auto p-3",children:t.jsx(V,{collapsed:f,children:k.map(v=>t.jsx(U,{item:v,collapsed:f,activePath:h,onNavigate:S,LinkComponent:o},v.label+(v.path??"")))})})]})}function I({children:e,sidebarItems:r,appName:n="Admin",appLogo:a,user:i,notifications:d=[],footer:s,showFooter:o=!0,showSearch:l=!0,searchPlaceholder:u,onSearch:p,breadcrumbs:w,contentClassName:y,className:f,activePath:h,onNavigate:k,LinkComponent:S,enableRouter:v=!1}){const{sidebarCollapsed:b,toggleSidebar:A,toggleMobile:N,breadcrumbs:P,setBreadcrumbs:L,activePath:T,setActivePath:j}=E(),{closeMobile:C}=R(),{resolvedMode:M,style:B,colors:q}=Z(),O=h??T,$=w??P,x=D=>{j(D),w||L(z(D)),k==null||k(D),C()};return t.jsxs("div",{className:m("ral-layout admin-layout-root min-h-screen bg-admin-bg text-admin-text",M==="dark"&&"dark",f),"data-theme":M,"data-style":B,style:Y({colors:q}),children:[t.jsx(de,{items:r,appName:n,appLogo:a,activePath:O,onNavigate:x,LinkComponent:S}),t.jsxs("div",{className:"flex min-h-screen w-full",children:[t.jsx(he,{items:r,appName:n,appLogo:a,activePath:O,onNavigate:x,LinkComponent:S}),t.jsxs("div",{className:"flex min-h-screen min-w-0 flex-1 flex-col",children:[t.jsx(xe,{user:i,notifications:d,onSearch:p,searchPlaceholder:u,showSearch:l,breadcrumbs:$,onMenuClick:N,sidebarCollapsed:b,onToggleSidebar:A}),t.jsx("main",{className:m("ral-content flex-1",y),children:e}),o&&t.jsx(ne,{children:s})]})]})]})}function ke(e){const{storageKey:r="rqdhw3n-admin-layout",defaultCollapsed:n=!1,initialDarkMode:a=!1,permissions:i={},breadcrumbs:d=[],activePath:s="/",enableRouter:o=!1,children:l,...u}=e,p=c.useMemo(()=>d.length?d:z(s),[d,s]);return t.jsx(te,{storageKey:r,defaultCollapsed:n,permissions:i,initialBreadcrumbs:p,initialActivePath:s,initialDarkMode:a,theme:e.theme,onThemeModeChange:e.onThemeModeChange,children:o?t.jsx(ae,{children:t.jsx(I,{...u,enableRouter:o,children:l})}):t.jsx(I,{...u,enableRouter:o,children:l})})}function Ne({to:e,className:r,children:n,onClick:a}){return t.jsx(Q.NavLink,{to:e,className:r,onClick:a,children:n})}function Se(){return E()}function Ce(){const{breadcrumbs:e,setBreadcrumbs:r}=E(),n=c.useCallback(a=>r(a),[r]);return{items:e,setBreadcrumbs:n}}exports.AdminLayout=ke;exports.AdminLayoutRouterSync=ae;exports.Breadcrumbs=le;exports.Footer=ne;exports.LayoutProvider=te;exports.MobileSidebar=de;exports.Navbar=xe;exports.NavbarSearch=ce;exports.NotificationMenu=ue;exports.RouterLink=Ne;exports.Sidebar=he;exports.SidebarGroup=V;exports.SidebarItem=U;exports.ThemeToggle=fe;exports.UserMenu=be;exports.adminLayoutPresets=ve;exports.cn=m;exports.darkThemeColors=W;exports.filterSidebarByPermissions=F;exports.generateBreadcrumbsFromPath=z;exports.hasPermission=se;exports.lightThemeColors=H;exports.resolveTheme=X;exports.themeToCSSVariables=Y;exports.useAdminLayoutTheme=Z;exports.useBreadcrumbs=Ce;exports.useDarkMode=me;exports.useLayoutContext=E;exports.useLayoutState=Se;exports.useSidebar=R;
2
2
  //# sourceMappingURL=index.cjs.map