@bensdev/react-sidebar 0.1.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/dist/index.js ADDED
@@ -0,0 +1,1287 @@
1
+ "use client";
2
+ import * as React21 from 'react';
3
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
+ import { createPortal } from 'react-dom';
5
+
6
+ // src/Sidebar.tsx
7
+
8
+ // src/lib/cx.ts
9
+ function cx(...values) {
10
+ let out = "";
11
+ for (const v of values) {
12
+ if (!v && v !== 0) continue;
13
+ out += (out ? " " : "") + v;
14
+ }
15
+ return out;
16
+ }
17
+
18
+ // src/lib/themeToStyle.ts
19
+ var kebab = (s) => s.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
20
+ var NUMERIC_PX = /* @__PURE__ */ new Set([
21
+ "width",
22
+ "widthCollapsed",
23
+ "drawerWidth",
24
+ "headerHeight",
25
+ "itemHeight",
26
+ "iconSize",
27
+ "accentWidth",
28
+ "toggleSize",
29
+ "toggleOffsetY",
30
+ "logoSize",
31
+ "avatarSize"
32
+ ]);
33
+ function themeToStyle(theme, scheme) {
34
+ if (!theme) return {};
35
+ const { light, dark, ...base } = theme;
36
+ const merged = {
37
+ ...base,
38
+ ...scheme === "dark" ? dark : light
39
+ };
40
+ const out = {};
41
+ for (const [key, value] of Object.entries(merged)) {
42
+ if (value == null) continue;
43
+ out[`--bsb-${kebab(key)}`] = typeof value === "number" && NUMERIC_PX.has(key) ? `${value}px` : String(value);
44
+ }
45
+ return out;
46
+ }
47
+ function useMediaQuery(query) {
48
+ const subscribe = React21.useCallback(
49
+ (cb) => {
50
+ if (typeof window === "undefined" || !window.matchMedia) return () => {
51
+ };
52
+ const mql = window.matchMedia(query);
53
+ mql.addEventListener("change", cb);
54
+ return () => mql.removeEventListener("change", cb);
55
+ },
56
+ [query]
57
+ );
58
+ const getSnapshot = React21.useCallback(() => {
59
+ if (typeof window === "undefined" || !window.matchMedia) return false;
60
+ return window.matchMedia(query).matches;
61
+ }, [query]);
62
+ const getServerSnapshot = React21.useCallback(() => false, []);
63
+ return React21.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
64
+ }
65
+ function toBreakpointQuery(breakpoint) {
66
+ return typeof breakpoint === "number" ? `(max-width: ${breakpoint - 1}px)` : breakpoint;
67
+ }
68
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React21.useLayoutEffect : React21.useEffect;
69
+
70
+ // src/hooks/useColorScheme.ts
71
+ function useColorScheme(pref, ref) {
72
+ const [resolved, setResolved] = React21.useState(pref === "dark" ? "dark" : "light");
73
+ useIsomorphicLayoutEffect(() => {
74
+ if (pref !== "auto") {
75
+ setResolved(pref);
76
+ return;
77
+ }
78
+ const el = ref.current;
79
+ if (!el || typeof window === "undefined") return;
80
+ const read = () => {
81
+ const darkAncestor = el.closest('.dark, [data-theme="dark"], [data-color-scheme="dark"]');
82
+ const lightAncestor = el.closest('[data-theme="light"], [data-color-scheme="light"]');
83
+ if (darkAncestor && (!lightAncestor || darkAncestor.contains(lightAncestor))) {
84
+ setResolved("dark");
85
+ return;
86
+ }
87
+ if (lightAncestor) {
88
+ setResolved("light");
89
+ return;
90
+ }
91
+ setResolved(window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light");
92
+ };
93
+ read();
94
+ const mo = new MutationObserver(read);
95
+ mo.observe(document.documentElement, {
96
+ attributes: true,
97
+ attributeFilter: ["class", "data-theme", "data-color-scheme"],
98
+ subtree: true
99
+ });
100
+ const mql = window.matchMedia?.("(prefers-color-scheme: dark)");
101
+ mql?.addEventListener("change", read);
102
+ return () => {
103
+ mo.disconnect();
104
+ mql?.removeEventListener("change", read);
105
+ };
106
+ }, [pref, ref]);
107
+ return resolved;
108
+ }
109
+ function useControllableState({
110
+ value,
111
+ defaultValue,
112
+ onChange
113
+ }) {
114
+ const isControlled = value !== void 0;
115
+ const [internal, setInternal] = React21.useState(defaultValue);
116
+ const current = isControlled ? value : internal;
117
+ const setState = React21.useCallback(
118
+ (next) => {
119
+ const resolved = typeof next === "function" ? next(current) : next;
120
+ if (!isControlled) setInternal(resolved);
121
+ onChange?.(resolved);
122
+ },
123
+ [isControlled, current, onChange]
124
+ );
125
+ return [current, setState];
126
+ }
127
+
128
+ // src/lib/storage.ts
129
+ function safeLocalStorage() {
130
+ if (typeof window === "undefined") return null;
131
+ try {
132
+ const testKey = "__bsb_storage_test__";
133
+ window.localStorage.setItem(testKey, "1");
134
+ window.localStorage.removeItem(testKey);
135
+ } catch {
136
+ return null;
137
+ }
138
+ return {
139
+ getItem: (key) => {
140
+ try {
141
+ return window.localStorage.getItem(key);
142
+ } catch {
143
+ return null;
144
+ }
145
+ },
146
+ setItem: (key, value) => {
147
+ try {
148
+ window.localStorage.setItem(key, value);
149
+ } catch {
150
+ }
151
+ }
152
+ };
153
+ }
154
+ function isStorageAdapter(value) {
155
+ return typeof value === "object" && value !== null && typeof value.getItem === "function" && typeof value.setItem === "function";
156
+ }
157
+
158
+ // src/hooks/usePersistedCollapse.ts
159
+ var DEFAULT_KEY = "sidebar-collapsed";
160
+ function usePersistedCollapse(persist) {
161
+ const key = typeof persist === "string" ? persist : DEFAULT_KEY;
162
+ const store = React21.useMemo(() => {
163
+ if (!persist) return null;
164
+ if (isStorageAdapter(persist)) return persist;
165
+ return safeLocalStorage();
166
+ }, [persist]);
167
+ const subscribe = React21.useCallback(
168
+ (cb) => {
169
+ if (!store) return () => {
170
+ };
171
+ if (store.subscribe) return store.subscribe(cb);
172
+ if (typeof window === "undefined") return () => {
173
+ };
174
+ const handler = (e) => {
175
+ if (e.key === key) cb();
176
+ };
177
+ window.addEventListener("storage", handler);
178
+ return () => window.removeEventListener("storage", handler);
179
+ },
180
+ [store, key]
181
+ );
182
+ const getSnapshot = React21.useCallback(() => {
183
+ if (!store) return null;
184
+ const raw = store.getItem(key);
185
+ return raw === null ? null : raw === "true";
186
+ }, [store, key]);
187
+ const getServerSnapshot = React21.useCallback(() => null, []);
188
+ const persisted = React21.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
189
+ const setPersisted = React21.useCallback(
190
+ (value) => {
191
+ store?.setItem(key, String(value));
192
+ },
193
+ [store, key]
194
+ );
195
+ return [persist ? persisted : null, setPersisted];
196
+ }
197
+ function useCurrentPath(explicit) {
198
+ const subscribe = React21.useCallback((cb) => {
199
+ if (typeof window === "undefined") return () => {
200
+ };
201
+ window.addEventListener("popstate", cb);
202
+ window.addEventListener("hashchange", cb);
203
+ return () => {
204
+ window.removeEventListener("popstate", cb);
205
+ window.removeEventListener("hashchange", cb);
206
+ };
207
+ }, []);
208
+ const getSnapshot = React21.useCallback(() => {
209
+ if (typeof window === "undefined") return "/";
210
+ return window.location.pathname;
211
+ }, []);
212
+ const getServerSnapshot = React21.useCallback(() => "/", []);
213
+ const tracked = React21.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
214
+ return explicit ?? tracked;
215
+ }
216
+ var NavContext = React21.createContext(null);
217
+ function useNavContext() {
218
+ const ctx = React21.useContext(NavContext);
219
+ if (!ctx) {
220
+ throw new Error("@bensdev/react-sidebar: nav components must be rendered inside <Sidebar>.");
221
+ }
222
+ return ctx;
223
+ }
224
+ function isShapedBadge(value) {
225
+ return typeof value === "object" && value !== null && "content" in value;
226
+ }
227
+ function Badge({
228
+ value,
229
+ className
230
+ }) {
231
+ if (value == null) return null;
232
+ const shaped = isShapedBadge(value);
233
+ const content = shaped ? value.content : value;
234
+ const tone = shaped ? value.tone ?? "default" : "default";
235
+ return /* @__PURE__ */ jsx(
236
+ "span",
237
+ {
238
+ className: cx("bsb-badge", `bsb-badge--${tone}`, shaped ? value.className : void 0, className),
239
+ children: content
240
+ }
241
+ );
242
+ }
243
+ function isBrandConfig(value) {
244
+ return typeof value === "object" && value !== null && !React21.isValidElement(value) && ("logo" in value || "title" in value || "subtitle" in value || "badge" in value || "logoNode" in value);
245
+ }
246
+ function SidebarHeader({ brand, header, collapsed, classNames, renderCtx, toggle }) {
247
+ if (header !== void 0) {
248
+ const content = typeof header === "function" ? header(renderCtx) : header;
249
+ return /* @__PURE__ */ jsxs("div", { className: cx("bsb-header", classNames.header), children: [
250
+ content,
251
+ toggle
252
+ ] });
253
+ }
254
+ if (brand === void 0) return null;
255
+ const resolved = typeof brand === "function" ? brand(renderCtx) : brand;
256
+ if (!isBrandConfig(resolved)) {
257
+ return /* @__PURE__ */ jsxs("div", { className: cx("bsb-header", classNames.header), children: [
258
+ resolved,
259
+ toggle
260
+ ] });
261
+ }
262
+ const config = resolved;
263
+ const TitleTag = config.href ? "a" : config.onClick ? "button" : "div";
264
+ return /* @__PURE__ */ jsxs("div", { className: cx("bsb-header", collapsed && "bsb-header--collapsed", classNames.header), children: [
265
+ /* @__PURE__ */ jsxs(
266
+ TitleTag,
267
+ {
268
+ className: cx("bsb-brand", classNames.brand),
269
+ href: config.href,
270
+ type: TitleTag === "button" ? "button" : void 0,
271
+ onClick: config.onClick,
272
+ children: [
273
+ config.logoNode ?? /* @__PURE__ */ jsx("span", { className: cx("bsb-logo", classNames.logo), "aria-hidden": !!config.title, children: config.logo }),
274
+ !collapsed && (config.title || config.subtitle || config.badge != null) && /* @__PURE__ */ jsxs("span", { className: "bsb-brand__text", children: [
275
+ /* @__PURE__ */ jsxs("span", { className: "bsb-brand__title-row", children: [
276
+ config.title != null && /* @__PURE__ */ jsx("span", { className: cx("bsb-title", classNames.title), children: config.title }),
277
+ config.badge != null && /* @__PURE__ */ jsx(Badge, { value: config.badge, className: classNames.headerBadge })
278
+ ] }),
279
+ config.subtitle != null && /* @__PURE__ */ jsx("span", { className: cx("bsb-subtitle", classNames.subtitle), children: config.subtitle })
280
+ ] })
281
+ ]
282
+ }
283
+ ),
284
+ toggle
285
+ ] });
286
+ }
287
+
288
+ // src/lib/isActive.ts
289
+ function normalizePath(path) {
290
+ const stripped = (path || "/").split("?")[0].split("#")[0];
291
+ return stripped.length > 1 && stripped.endsWith("/") ? stripped.slice(0, -1) : stripped;
292
+ }
293
+ function defaultIsActive(href, currentPath, end = false) {
294
+ const target = normalizePath(href);
295
+ const current = normalizePath(currentPath);
296
+ if (end || target === "/") return target === current;
297
+ return current === target || current.startsWith(target + "/");
298
+ }
299
+ function resolveIsActive(item, currentPath, exact, isItemActive) {
300
+ const href = item.href;
301
+ const fallback = href ? defaultIsActive(href, currentPath, item.end ?? exact) : false;
302
+ if (typeof item.isActive === "function") {
303
+ return item.isActive({ currentPath, item, defaultIsActive: fallback });
304
+ }
305
+ if (typeof item.isActive === "boolean") return item.isActive;
306
+ if (item.type !== "group" && isItemActive) {
307
+ const custom = isItemActive(item, { currentPath, item, defaultIsActive: fallback });
308
+ if (custom !== void 0) return custom;
309
+ }
310
+ return fallback;
311
+ }
312
+ function groupHasActiveDescendant(item, currentPath, exact, isItemActive) {
313
+ if (item.href && resolveIsActive(item, currentPath, exact, isItemActive)) return true;
314
+ return item.items.some((child) => itemHasActiveDescendant(child, currentPath, exact, isItemActive));
315
+ }
316
+ function itemHasActiveDescendant(item, currentPath, exact, isItemActive) {
317
+ if (item.type === "group") return groupHasActiveDescendant(item, currentPath, exact, isItemActive);
318
+ if (item.type === "link" || item.type === void 0) {
319
+ return resolveIsActive(item, currentPath, exact, isItemActive);
320
+ }
321
+ return false;
322
+ }
323
+ function Tooltip({ id, children, className }) {
324
+ return /* @__PURE__ */ jsx("div", { id, role: "tooltip", className: cx("bsb-tooltip", className), "aria-hidden": "true", children });
325
+ }
326
+ var uid = 0;
327
+ function useStableId(prefix) {
328
+ const ref = React21.useRef(null);
329
+ if (!ref.current) ref.current = `${prefix}-${++uid}`;
330
+ return ref.current;
331
+ }
332
+ function SidebarLinkRow({ item, depth, active: activeOverride }) {
333
+ const ctx = useNavContext();
334
+ const tooltipId = useStableId("bsb-tooltip");
335
+ const isLink = item.type !== "action";
336
+ const linkItem = isLink ? item : null;
337
+ const actionItem = !isLink ? item : null;
338
+ const isActive = activeOverride ?? (linkItem ? resolveIsActive(linkItem, ctx.currentPath, ctx.exact, ctx.isItemActive) : !!actionItem?.selected);
339
+ const showTooltip = ctx.collapsed && ctx.tooltips && item.tooltip !== false && !ctx.isMobile;
340
+ const tooltipContent = item.tooltip ?? item.label;
341
+ const handleClick = (e) => {
342
+ if (item.disabled) {
343
+ e.preventDefault();
344
+ return;
345
+ }
346
+ if (linkItem?.onClick) linkItem.onClick(e);
347
+ if (actionItem) actionItem.onSelect(e);
348
+ ctx.handleNavigate(item, e);
349
+ };
350
+ const className = cx(
351
+ "bsb-item",
352
+ ctx.classNames.item,
353
+ isActive && "bsb-item--active",
354
+ isActive && ctx.classNames.itemActive,
355
+ item.disabled && "bsb-item--disabled",
356
+ item.disabled && ctx.classNames.itemDisabled,
357
+ item.className
358
+ );
359
+ const style = depth > 0 ? { paddingInlineStart: `calc(var(--bsb-item-padding-x) + ${depth} * var(--bsb-indent-step))` } : void 0;
360
+ const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
361
+ item.icon != null && /* @__PURE__ */ jsx("span", { className: cx("bsb-item__icon", ctx.classNames.itemIcon), "aria-hidden": "true", children: item.icon }),
362
+ /* @__PURE__ */ jsx("span", { className: cx("bsb-item__label", ctx.classNames.itemLabel), children: item.label }),
363
+ item.badge != null && /* @__PURE__ */ jsx(Badge, { value: item.badge, className: ctx.classNames.badge }),
364
+ showTooltip && /* @__PURE__ */ jsx(Tooltip, { id: tooltipId, className: ctx.classNames.tooltip, children: tooltipContent })
365
+ ] });
366
+ const commonProps = {
367
+ className,
368
+ style,
369
+ onClick: handleClick,
370
+ "aria-disabled": item.disabled || void 0,
371
+ "aria-describedby": showTooltip ? tooltipId : void 0
372
+ };
373
+ let node;
374
+ if (actionItem) {
375
+ node = /* @__PURE__ */ jsx(
376
+ "button",
377
+ {
378
+ type: "button",
379
+ ...commonProps,
380
+ disabled: item.disabled,
381
+ "aria-pressed": actionItem.selected,
382
+ children: inner
383
+ }
384
+ );
385
+ } else if (linkItem && !item.disabled) {
386
+ const href = linkItem.href;
387
+ const rowProps = {
388
+ ...commonProps,
389
+ ...linkItem.linkProps,
390
+ target: linkItem.target,
391
+ rel: linkItem.rel,
392
+ "aria-current": isActive ? "page" : void 0
393
+ };
394
+ if (ctx.renderLink) {
395
+ node = ctx.renderLink({
396
+ item: linkItem,
397
+ href,
398
+ isActive,
399
+ className,
400
+ children: inner,
401
+ props: rowProps,
402
+ ctx: ctx.renderCtx
403
+ });
404
+ } else {
405
+ const Comp = ctx.linkComponent ?? "a";
406
+ const hrefKey = ctx.hrefProp || "href";
407
+ node = /* @__PURE__ */ jsx(Comp, { ...rowProps, ...{ [hrefKey]: href }, children: inner });
408
+ }
409
+ } else {
410
+ node = /* @__PURE__ */ jsx("span", { role: "link", tabIndex: -1, ...commonProps, children: inner });
411
+ }
412
+ return /* @__PURE__ */ jsx("li", { className: "bsb-list-item", children: node });
413
+ }
414
+ function baseProps(size, rest) {
415
+ return {
416
+ xmlns: "http://www.w3.org/2000/svg",
417
+ width: size ?? 18,
418
+ height: size ?? 18,
419
+ viewBox: "0 0 24 24",
420
+ fill: "none",
421
+ stroke: "currentColor",
422
+ strokeWidth: 2,
423
+ strokeLinecap: "round",
424
+ strokeLinejoin: "round",
425
+ "aria-hidden": true,
426
+ ...rest
427
+ };
428
+ }
429
+ function ChevronLeftIcon({ size, ...rest }) {
430
+ return /* @__PURE__ */ jsx("svg", { ...baseProps(size, rest), children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) });
431
+ }
432
+ function ChevronRightIcon({ size, ...rest }) {
433
+ return /* @__PURE__ */ jsx("svg", { ...baseProps(size, rest), children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) });
434
+ }
435
+ function ChevronDownIcon({ size, ...rest }) {
436
+ return /* @__PURE__ */ jsx("svg", { ...baseProps(size, rest), children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) });
437
+ }
438
+ function CloseIcon({ size, ...rest }) {
439
+ return /* @__PURE__ */ jsx("svg", { ...baseProps(size, rest), children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) });
440
+ }
441
+ function MenuIcon({ size, ...rest }) {
442
+ return /* @__PURE__ */ jsx("svg", { ...baseProps(size, rest), children: /* @__PURE__ */ jsx("path", { d: "M4 12h16M4 6h16M4 18h16" }) });
443
+ }
444
+ var uid2 = 0;
445
+ function useStableId2(prefix) {
446
+ const ref = React21.useRef(null);
447
+ if (!ref.current) ref.current = `${prefix}-${++uid2}`;
448
+ return ref.current;
449
+ }
450
+ function SidebarGroupRow({ item, depth }) {
451
+ const ctx = useNavContext();
452
+ const fallbackGroupId = useStableId2("bsb-group");
453
+ const groupId = item.id ?? fallbackGroupId;
454
+ const panelId = `${groupId}-panel`;
455
+ const tooltipId = `${groupId}-tooltip`;
456
+ const collapsible = item.collapsible ?? true;
457
+ const hasActiveChild = groupHasActiveDescendant(item, ctx.currentPath, ctx.exact, ctx.isItemActive);
458
+ const selfActive = item.href ? resolveIsActive(item, ctx.currentPath, ctx.exact) : false;
459
+ const controlledOpen = item.open;
460
+ const isOpen = controlledOpen ?? (!collapsible || ctx.openGroups.has(groupId) || ctx.autoOpenActiveGroup && hasActiveChild);
461
+ const toggle = () => {
462
+ if (item.onOpenChange) {
463
+ item.onOpenChange(!isOpen);
464
+ return;
465
+ }
466
+ ctx.toggleGroup(groupId);
467
+ };
468
+ const showTooltip = ctx.collapsed && ctx.tooltips && item.tooltip !== false && !ctx.isMobile;
469
+ const isRail = ctx.collapsed && !ctx.isMobile;
470
+ const behavior = ctx.collapsedGroupBehavior;
471
+ const triggerClassName = cx(
472
+ "bsb-item",
473
+ "bsb-group__trigger",
474
+ ctx.classNames.item,
475
+ ctx.classNames.groupTrigger,
476
+ (hasActiveChild || selfActive) && "bsb-item--active",
477
+ (hasActiveChild || selfActive) && ctx.classNames.itemActive,
478
+ item.className
479
+ );
480
+ const style = depth > 0 ? { paddingInlineStart: `calc(var(--bsb-item-padding-x) + ${depth} * var(--bsb-indent-step))` } : void 0;
481
+ const trigger = /* @__PURE__ */ jsxs(
482
+ "button",
483
+ {
484
+ type: "button",
485
+ className: triggerClassName,
486
+ style,
487
+ onClick: collapsible ? toggle : void 0,
488
+ "aria-expanded": collapsible ? isOpen : void 0,
489
+ "aria-controls": collapsible ? panelId : void 0,
490
+ "aria-describedby": showTooltip ? tooltipId : void 0,
491
+ children: [
492
+ item.icon != null && /* @__PURE__ */ jsx("span", { className: cx("bsb-item__icon", ctx.classNames.itemIcon), "aria-hidden": "true", children: item.icon }),
493
+ /* @__PURE__ */ jsx("span", { className: cx("bsb-item__label", ctx.classNames.itemLabel), children: item.label }),
494
+ item.badge != null && /* @__PURE__ */ jsx(Badge, { value: item.badge, className: ctx.classNames.badge }),
495
+ collapsible && !isRail && /* @__PURE__ */ jsx(
496
+ "span",
497
+ {
498
+ className: cx("bsb-group__chevron", isOpen && "bsb-group__chevron--open", ctx.classNames.groupChevron),
499
+ "aria-hidden": "true",
500
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 14 })
501
+ }
502
+ ),
503
+ showTooltip && behavior !== "flyout" && /* @__PURE__ */ jsx(Tooltip, { id: tooltipId, className: ctx.classNames.tooltip, children: item.tooltip ?? item.label })
504
+ ]
505
+ }
506
+ );
507
+ if (isRail && behavior === "flyout") {
508
+ return /* @__PURE__ */ jsxs("li", { className: cx("bsb-list-item", "bsb-group", "bsb-group--flyout"), children: [
509
+ trigger,
510
+ /* @__PURE__ */ jsxs(
511
+ "div",
512
+ {
513
+ className: cx("bsb-group__flyout", ctx.classNames.groupPanel),
514
+ role: "group",
515
+ "aria-label": typeof item.label === "string" ? item.label : void 0,
516
+ children: [
517
+ item.label != null && /* @__PURE__ */ jsx("div", { className: "bsb-group__flyout-label", children: item.label }),
518
+ /* @__PURE__ */ jsx(NavContext.Provider, { value: { ...ctx, collapsed: false, tooltips: false }, children: /* @__PURE__ */ jsx(SidebarNodeList, { items: item.items, depth: 0 }) })
519
+ ]
520
+ }
521
+ )
522
+ ] });
523
+ }
524
+ if (isRail && behavior === "ignore") {
525
+ return /* @__PURE__ */ jsx("li", { className: "bsb-list-item", children: trigger });
526
+ }
527
+ return /* @__PURE__ */ jsxs("li", { className: cx("bsb-list-item", "bsb-group"), children: [
528
+ trigger,
529
+ (isOpen || !collapsible) && /* @__PURE__ */ jsx("ul", { id: panelId, className: cx("bsb-group__panel", ctx.classNames.groupPanel), children: /* @__PURE__ */ jsx(SidebarNodeList, { items: item.items, depth: depth + 1 }) })
530
+ ] });
531
+ }
532
+ function SidebarNode({ item, depth }) {
533
+ const ctx = useNavContext();
534
+ if ("hidden" in item && item.hidden) return null;
535
+ const defaultNode = (() => {
536
+ switch (item.type) {
537
+ case "divider":
538
+ return /* @__PURE__ */ jsx("li", { className: cx("bsb-list-item", "bsb-divider-item"), role: "separator", children: /* @__PURE__ */ jsx("hr", { className: cx("bsb-divider", ctx.classNames.divider, item.className) }) });
539
+ case "heading":
540
+ return /* @__PURE__ */ jsx("li", { className: cx("bsb-list-item", "bsb-heading-item"), children: /* @__PURE__ */ jsx("div", { className: cx("bsb-heading", ctx.classNames.heading, item.className), children: item.label }) });
541
+ case "custom":
542
+ return /* @__PURE__ */ jsx("li", { className: "bsb-list-item bsb-custom-item", children: item.render(ctx.renderCtx) });
543
+ case "group":
544
+ return /* @__PURE__ */ jsx(SidebarGroupRow, { item, depth });
545
+ case "action":
546
+ case "link":
547
+ default:
548
+ return /* @__PURE__ */ jsx(SidebarLinkRow, { item, depth });
549
+ }
550
+ })();
551
+ if (ctx.renderItem) {
552
+ const custom = ctx.renderItem(item, ctx.renderCtx, defaultNode);
553
+ if (custom !== void 0) return /* @__PURE__ */ jsx(Fragment, { children: custom });
554
+ }
555
+ return /* @__PURE__ */ jsx(Fragment, { children: defaultNode });
556
+ }
557
+ function SidebarNodeList({ items, depth }) {
558
+ return /* @__PURE__ */ jsx(Fragment, { children: items.map((item, i) => /* @__PURE__ */ jsx(SidebarNode, { item, depth }, item.id ?? ("href" in item ? item.href : void 0) ?? `${item.type ?? "link"}-${i}`)) });
559
+ }
560
+ function SidebarNav({ items, ariaLabel }) {
561
+ const ctx = useNavContext();
562
+ return /* @__PURE__ */ jsx("nav", { className: cx("bsb-nav", ctx.classNames.nav), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx("ul", { className: cx("bsb-list", ctx.classNames.list), children: /* @__PURE__ */ jsx(SidebarNodeList, { items, depth: 0 }) }) });
563
+ }
564
+
565
+ // src/lib/initials.ts
566
+ function initialsFromName(name, fallback = "") {
567
+ if (typeof name !== "string" || !name.trim()) return fallback;
568
+ return name.trim().split(/\s+/).map((part) => part[0]).join("").slice(0, 2).toUpperCase();
569
+ }
570
+ function SidebarFooter({
571
+ user,
572
+ footerAction,
573
+ footer,
574
+ collapsed,
575
+ collapsedFooter,
576
+ classNames,
577
+ renderCtx
578
+ }) {
579
+ if (footer !== void 0) {
580
+ const content = typeof footer === "function" ? footer(renderCtx) : footer;
581
+ if (!content) return null;
582
+ return /* @__PURE__ */ jsx("div", { className: cx("bsb-footer", classNames.footer), children: content });
583
+ }
584
+ if (!user && !footerAction) return null;
585
+ const actions = footerAction ? Array.isArray(footerAction) ? footerAction : [footerAction] : [];
586
+ const initials = user ? initialsFromName(user.name, user.fallbackInitials ?? "") || user.fallbackInitials || "" : "";
587
+ if (collapsed && collapsedFooter === "hidden") return null;
588
+ const avatar = user && /* @__PURE__ */ jsx("span", { className: cx("bsb-avatar", classNames.avatar), children: user.avatarUrl ? (
589
+ // eslint-disable-next-line @next/next/no-img-element
590
+ /* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "bsb-avatar__img" })
591
+ ) : user.avatar ? user.avatar : /* @__PURE__ */ jsx("span", { className: "bsb-avatar__initials", children: initials }) });
592
+ if (collapsed && collapsedFooter === "avatar") {
593
+ return /* @__PURE__ */ jsx("div", { className: cx("bsb-footer", "bsb-footer--collapsed", classNames.footer), children: /* @__PURE__ */ jsx("div", { className: cx("bsb-user", "bsb-user--centered", classNames.user), children: avatar }) });
594
+ }
595
+ if (collapsed && collapsedFooter === "stack") {
596
+ return /* @__PURE__ */ jsxs("div", { className: cx("bsb-footer", "bsb-footer--collapsed", "bsb-footer--stacked", classNames.footer), children: [
597
+ /* @__PURE__ */ jsx("div", { className: cx("bsb-user", "bsb-user--centered", classNames.user), children: avatar }),
598
+ actions.map((action, i) => /* @__PURE__ */ jsx(
599
+ "button",
600
+ {
601
+ type: "button",
602
+ className: cx("bsb-footer-action", classNames.footerAction),
603
+ title: action.label,
604
+ "aria-label": action.label,
605
+ onClick: action.onClick,
606
+ children: action.icon
607
+ },
608
+ i
609
+ ))
610
+ ] });
611
+ }
612
+ return /* @__PURE__ */ jsx("div", { className: cx("bsb-footer", classNames.footer), children: /* @__PURE__ */ jsxs("div", { className: cx("bsb-user", classNames.user), children: [
613
+ avatar,
614
+ (user?.name != null || user?.email != null) && /* @__PURE__ */ jsxs("span", { className: "bsb-user__text", children: [
615
+ user?.name != null && /* @__PURE__ */ jsx("span", { className: cx("bsb-user__name", classNames.userName), children: user.name }),
616
+ user?.email != null && /* @__PURE__ */ jsx("span", { className: cx("bsb-user__meta", classNames.userMeta), children: user.email })
617
+ ] }),
618
+ actions.map((action, i) => /* @__PURE__ */ jsx(
619
+ "button",
620
+ {
621
+ type: "button",
622
+ className: cx("bsb-footer-action", classNames.footerAction),
623
+ title: action.label,
624
+ "aria-label": action.label,
625
+ onClick: action.onClick,
626
+ children: action.icon
627
+ },
628
+ i
629
+ ))
630
+ ] }) });
631
+ }
632
+ function CollapseToggle({
633
+ collapsed,
634
+ onToggle,
635
+ className,
636
+ position,
637
+ strategy,
638
+ labels,
639
+ icon
640
+ }) {
641
+ const label = collapsed ? labels.expand : labels.collapse;
642
+ const showRightChevron = position === "left" ? collapsed : !collapsed;
643
+ return /* @__PURE__ */ jsx(
644
+ "button",
645
+ {
646
+ type: "button",
647
+ onClick: onToggle,
648
+ title: label,
649
+ "aria-label": label,
650
+ "data-toggle-strategy": strategy,
651
+ className: cx("bsb-toggle", className),
652
+ children: icon ? icon(collapsed) : showRightChevron ? /* @__PURE__ */ jsx(ChevronRightIcon, { size: 13 }) : /* @__PURE__ */ jsx(ChevronLeftIcon, { size: 13 })
653
+ }
654
+ );
655
+ }
656
+ function SidebarShell({
657
+ items,
658
+ ariaLabel,
659
+ brand,
660
+ header,
661
+ footer,
662
+ user,
663
+ footerAction,
664
+ collapsed,
665
+ collapsedFooter,
666
+ showToggle,
667
+ onToggle,
668
+ togglePosition,
669
+ toggleStrategy,
670
+ toggleLabels,
671
+ classNames,
672
+ slots,
673
+ renderCtx,
674
+ extraHeader
675
+ }) {
676
+ const navTop = typeof slots.navTop === "function" ? slots.navTop(renderCtx) : slots.navTop;
677
+ const navBottom = typeof slots.navBottom === "function" ? slots.navBottom(renderCtx) : slots.navBottom;
678
+ return /* @__PURE__ */ jsxs("div", { className: "bsb-shell", children: [
679
+ /* @__PURE__ */ jsx(SidebarHeader, { brand, header, collapsed, classNames, renderCtx }),
680
+ extraHeader,
681
+ navTop,
682
+ /* @__PURE__ */ jsx(SidebarNav, { items, ariaLabel }),
683
+ navBottom,
684
+ /* @__PURE__ */ jsx(
685
+ SidebarFooter,
686
+ {
687
+ user,
688
+ footerAction,
689
+ footer,
690
+ collapsed,
691
+ collapsedFooter,
692
+ classNames,
693
+ renderCtx
694
+ }
695
+ ),
696
+ showToggle && /* @__PURE__ */ jsx(
697
+ CollapseToggle,
698
+ {
699
+ collapsed,
700
+ onToggle,
701
+ className: classNames.toggle,
702
+ position: togglePosition,
703
+ strategy: toggleStrategy,
704
+ labels: toggleLabels,
705
+ icon: slots.collapseIcon
706
+ }
707
+ )
708
+ ] });
709
+ }
710
+ function useMountTransition(open, durationMs) {
711
+ const [mounted, setMounted] = React21.useState(open);
712
+ const [state, setState] = React21.useState(open ? "open" : "closed");
713
+ React21.useEffect(() => {
714
+ if (open) {
715
+ setMounted(true);
716
+ const raf1 = requestAnimationFrame(() => {
717
+ requestAnimationFrame(() => setState("open"));
718
+ });
719
+ return () => cancelAnimationFrame(raf1);
720
+ }
721
+ setState("closed");
722
+ const timer = setTimeout(() => setMounted(false), durationMs + 60);
723
+ return () => clearTimeout(timer);
724
+ }, [open, durationMs]);
725
+ const onPanelTransitionEnd = React21.useCallback(
726
+ (e) => {
727
+ if (e.propertyName === "transform" && !open) setMounted(false);
728
+ },
729
+ [open]
730
+ );
731
+ return { mounted, state, onPanelTransitionEnd };
732
+ }
733
+ var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
734
+ function useFocusTrap(containerRef, { active, onEscape, restoreFocus = true }) {
735
+ const previouslyFocused = React21.useRef(null);
736
+ React21.useEffect(() => {
737
+ if (!active || typeof document === "undefined") return;
738
+ previouslyFocused.current = document.activeElement;
739
+ const container = containerRef.current;
740
+ const focusFirst = () => {
741
+ if (!container) return;
742
+ const focusable = container.querySelectorAll(FOCUSABLE_SELECTOR);
743
+ (focusable[0] ?? container).focus();
744
+ };
745
+ const id = requestAnimationFrame(focusFirst);
746
+ const onKeyDown = (e) => {
747
+ if (e.key === "Escape") {
748
+ onEscape?.();
749
+ return;
750
+ }
751
+ if (e.key !== "Tab" || !container) return;
752
+ const focusable = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));
753
+ if (focusable.length === 0) {
754
+ e.preventDefault();
755
+ return;
756
+ }
757
+ const first = focusable[0];
758
+ const last = focusable[focusable.length - 1];
759
+ const activeEl = document.activeElement;
760
+ if (e.shiftKey && activeEl === first) {
761
+ e.preventDefault();
762
+ last.focus();
763
+ } else if (!e.shiftKey && activeEl === last) {
764
+ e.preventDefault();
765
+ first.focus();
766
+ }
767
+ };
768
+ document.addEventListener("keydown", onKeyDown);
769
+ return () => {
770
+ cancelAnimationFrame(id);
771
+ document.removeEventListener("keydown", onKeyDown);
772
+ if (restoreFocus) previouslyFocused.current?.focus?.();
773
+ };
774
+ }, [active, containerRef, onEscape, restoreFocus]);
775
+ }
776
+ var lockCount = 0;
777
+ var savedOverflow = "";
778
+ var savedPaddingRight = "";
779
+ function lock() {
780
+ if (typeof document === "undefined") return;
781
+ if (lockCount === 0) {
782
+ const body = document.body;
783
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
784
+ savedOverflow = body.style.overflow;
785
+ savedPaddingRight = body.style.paddingRight;
786
+ body.style.overflow = "hidden";
787
+ if (scrollbarWidth > 0) {
788
+ const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
789
+ body.style.paddingRight = `${current + scrollbarWidth}px`;
790
+ }
791
+ }
792
+ lockCount++;
793
+ }
794
+ function unlock() {
795
+ if (typeof document === "undefined") return;
796
+ lockCount = Math.max(0, lockCount - 1);
797
+ if (lockCount === 0) {
798
+ document.body.style.overflow = savedOverflow;
799
+ document.body.style.paddingRight = savedPaddingRight;
800
+ }
801
+ }
802
+ function useBodyScrollLock(active) {
803
+ React21.useEffect(() => {
804
+ if (!active) return;
805
+ lock();
806
+ return unlock;
807
+ }, [active]);
808
+ }
809
+ function Portal({ target, disabled, children }) {
810
+ const [mounted, setMounted] = React21.useState(false);
811
+ React21.useEffect(() => setMounted(true), []);
812
+ if (disabled) return /* @__PURE__ */ jsx(Fragment, { children });
813
+ if (!mounted || typeof document === "undefined") return null;
814
+ const node = typeof target === "function" ? target() : target;
815
+ return createPortal(children, node ?? document.body);
816
+ }
817
+ function MobileDrawer({
818
+ open,
819
+ onClose,
820
+ ariaLabel,
821
+ width,
822
+ position,
823
+ closeOnEscape,
824
+ closeOnBackdropClick,
825
+ trapFocus,
826
+ lockScroll,
827
+ restoreFocus,
828
+ portalTarget,
829
+ disablePortal,
830
+ closeLabel,
831
+ classNames,
832
+ durationMs = 400,
833
+ children
834
+ }) {
835
+ const { mounted, state, onPanelTransitionEnd } = useMountTransition(open, durationMs);
836
+ const panelRef = React21.useRef(null);
837
+ useFocusTrap(panelRef, {
838
+ active: trapFocus && open,
839
+ onEscape: closeOnEscape ? onClose : void 0,
840
+ restoreFocus
841
+ });
842
+ useBodyScrollLock(lockScroll && open);
843
+ if (!mounted) return null;
844
+ const widthValue = typeof width === "number" ? `${width}px` : width;
845
+ return /* @__PURE__ */ jsxs(Portal, { target: portalTarget, disabled: disablePortal, children: [
846
+ /* @__PURE__ */ jsx(
847
+ "div",
848
+ {
849
+ className: cx("bsb-backdrop", classNames.backdrop),
850
+ "data-state": state,
851
+ "aria-hidden": "true",
852
+ onClick: closeOnBackdropClick ? onClose : void 0
853
+ }
854
+ ),
855
+ /* @__PURE__ */ jsxs(
856
+ "div",
857
+ {
858
+ ref: panelRef,
859
+ className: cx("bsb-drawer", classNames.drawer),
860
+ "data-state": state,
861
+ "data-position": position,
862
+ style: { "--bsb-drawer-current-width": widthValue },
863
+ role: "dialog",
864
+ "aria-modal": "true",
865
+ "aria-label": ariaLabel,
866
+ tabIndex: -1,
867
+ onTransitionEnd: onPanelTransitionEnd,
868
+ children: [
869
+ /* @__PURE__ */ jsx(
870
+ "button",
871
+ {
872
+ type: "button",
873
+ className: cx("bsb-drawer__close", classNames.drawerClose),
874
+ onClick: onClose,
875
+ "aria-label": closeLabel,
876
+ title: closeLabel,
877
+ children: /* @__PURE__ */ jsx(CloseIcon, { size: 18 })
878
+ }
879
+ ),
880
+ children
881
+ ]
882
+ }
883
+ )
884
+ ] });
885
+ }
886
+ function widthToCss(value) {
887
+ if (value == null) return void 0;
888
+ return typeof value === "number" ? `${value}px` : value;
889
+ }
890
+ var Sidebar = React21.forwardRef(function Sidebar2(props, ref) {
891
+ const {
892
+ items,
893
+ renderItem,
894
+ slots = {},
895
+ classNames = {},
896
+ currentPath: currentPathProp,
897
+ linkComponent,
898
+ hrefProp = "href",
899
+ renderLink,
900
+ isItemActive,
901
+ exact = false,
902
+ onItemClick,
903
+ closeOnNavigate = true,
904
+ collapsible = true,
905
+ collapsed: collapsedProp,
906
+ defaultCollapsed = false,
907
+ onCollapsedChange,
908
+ persistCollapse,
909
+ showCollapseToggle,
910
+ collapsedFooter = "avatar",
911
+ openGroups: openGroupsProp,
912
+ defaultOpenGroups = [],
913
+ onOpenGroupsChange,
914
+ accordion = false,
915
+ autoOpenActiveGroup = true,
916
+ collapsedGroupBehavior = "flyout",
917
+ mobileOpen: mobileOpenProp,
918
+ onMobileClose,
919
+ onMobileOpenChange,
920
+ breakpoint = 1024,
921
+ responsive = true,
922
+ drawerWidth = 280,
923
+ closeOnEscape = true,
924
+ closeOnBackdropClick = true,
925
+ trapFocus = true,
926
+ lockScroll = true,
927
+ restoreFocus = true,
928
+ portalTarget,
929
+ disablePortal,
930
+ brand,
931
+ user,
932
+ footerAction,
933
+ header,
934
+ footer,
935
+ theme,
936
+ colorScheme: colorSchemeProp = "auto",
937
+ width,
938
+ collapsedWidth,
939
+ position = "left",
940
+ bordered = true,
941
+ tooltips = true,
942
+ reduceMotion = "auto",
943
+ toggleStrategy = "absolute",
944
+ as: RootTag = "aside",
945
+ id,
946
+ className,
947
+ style,
948
+ "aria-label": ariaLabel = "Main navigation"
949
+ } = props;
950
+ const rootRef = React21.useRef(null);
951
+ const [internalCollapsed, setInternalCollapsed] = useControllableState({
952
+ value: collapsedProp,
953
+ defaultValue: defaultCollapsed,
954
+ onChange: onCollapsedChange
955
+ });
956
+ const isCollapseControlled = collapsedProp !== void 0;
957
+ const [persistedCollapsed, setPersistedCollapsed] = usePersistedCollapse(
958
+ isCollapseControlled ? void 0 : persistCollapse
959
+ );
960
+ const collapsed = !collapsible ? false : isCollapseControlled ? collapsedProp : persistedCollapsed ?? internalCollapsed;
961
+ const setCollapsed = React21.useCallback(
962
+ (next) => {
963
+ setInternalCollapsed(next);
964
+ if (persistCollapse) setPersistedCollapsed(next);
965
+ },
966
+ [setInternalCollapsed, setPersistedCollapsed, persistCollapse]
967
+ );
968
+ const toggleCollapsed = React21.useCallback(() => setCollapsed(!collapsed), [setCollapsed, collapsed]);
969
+ const mediaIsMobile = useMediaQuery(toBreakpointQuery(breakpoint));
970
+ const isMobile = responsive === false ? false : mediaIsMobile;
971
+ const [mobileOpen, setMobileOpenInternal] = useControllableState({
972
+ value: mobileOpenProp,
973
+ defaultValue: false,
974
+ onChange: (open) => {
975
+ onMobileOpenChange?.(open);
976
+ if (!open) onMobileClose?.();
977
+ }
978
+ });
979
+ const closeMobile = React21.useCallback(() => setMobileOpenInternal(false), [setMobileOpenInternal]);
980
+ const openMobile = React21.useCallback(() => setMobileOpenInternal(true), [setMobileOpenInternal]);
981
+ const [openGroupsArr, setOpenGroupsArr] = useControllableState({
982
+ value: openGroupsProp,
983
+ defaultValue: defaultOpenGroups,
984
+ onChange: onOpenGroupsChange
985
+ });
986
+ const openGroups = React21.useMemo(() => new Set(openGroupsArr), [openGroupsArr]);
987
+ const toggleGroup = React21.useCallback(
988
+ (id2) => {
989
+ setOpenGroupsArr((prev) => {
990
+ const has = prev.includes(id2);
991
+ if (accordion) return has ? [] : [id2];
992
+ return has ? prev.filter((x) => x !== id2) : [...prev, id2];
993
+ });
994
+ },
995
+ [setOpenGroupsArr, accordion]
996
+ );
997
+ const currentPath = useCurrentPath(currentPathProp);
998
+ const handleNavigate = React21.useCallback(
999
+ (item, e) => {
1000
+ onItemClick?.(item, e);
1001
+ const type = item.type ?? "link";
1002
+ if (isMobile && closeOnNavigate && type !== "group") closeMobile();
1003
+ },
1004
+ [onItemClick, isMobile, closeOnNavigate, closeMobile]
1005
+ );
1006
+ const colorScheme = useColorScheme(colorSchemeProp, rootRef);
1007
+ const themeStyle = themeToStyle(theme, colorScheme);
1008
+ const widthStyle = {};
1009
+ if (width != null) widthStyle["--bsb-width"] = widthToCss(width);
1010
+ if (collapsedWidth != null)
1011
+ widthStyle["--bsb-width-collapsed"] = widthToCss(collapsedWidth);
1012
+ if (drawerWidth != null)
1013
+ widthStyle["--bsb-drawer-width"] = widthToCss(drawerWidth);
1014
+ const mergedStyle = { ...themeStyle, ...widthStyle, ...style };
1015
+ React21.useImperativeHandle(
1016
+ ref,
1017
+ () => ({
1018
+ collapse: () => setCollapsed(true),
1019
+ expand: () => setCollapsed(false),
1020
+ toggleCollapsed,
1021
+ openMobile,
1022
+ closeMobile,
1023
+ get element() {
1024
+ return rootRef.current;
1025
+ }
1026
+ }),
1027
+ [setCollapsed, toggleCollapsed, openMobile, closeMobile]
1028
+ );
1029
+ const renderCtx = React21.useMemo(
1030
+ () => ({ collapsed, isMobile, colorScheme, currentPath, toggleCollapsed, closeMobile }),
1031
+ [collapsed, isMobile, colorScheme, currentPath, toggleCollapsed, closeMobile]
1032
+ );
1033
+ const navContextValue = React21.useMemo(
1034
+ () => ({
1035
+ currentPath,
1036
+ linkComponent,
1037
+ hrefProp,
1038
+ renderLink,
1039
+ isItemActive,
1040
+ exact,
1041
+ handleNavigate,
1042
+ collapsed,
1043
+ isMobile,
1044
+ colorScheme,
1045
+ openGroups,
1046
+ toggleGroup,
1047
+ accordion,
1048
+ autoOpenActiveGroup,
1049
+ collapsedGroupBehavior,
1050
+ tooltips,
1051
+ classNames,
1052
+ slots,
1053
+ renderItem,
1054
+ renderCtx
1055
+ }),
1056
+ [
1057
+ currentPath,
1058
+ linkComponent,
1059
+ hrefProp,
1060
+ renderLink,
1061
+ isItemActive,
1062
+ exact,
1063
+ handleNavigate,
1064
+ collapsed,
1065
+ isMobile,
1066
+ colorScheme,
1067
+ openGroups,
1068
+ toggleGroup,
1069
+ accordion,
1070
+ autoOpenActiveGroup,
1071
+ collapsedGroupBehavior,
1072
+ tooltips,
1073
+ classNames,
1074
+ slots,
1075
+ renderItem,
1076
+ renderCtx
1077
+ ]
1078
+ );
1079
+ const rootClassName = cx(
1080
+ "bsb-root",
1081
+ bordered && "bsb-root--bordered",
1082
+ classNames.root,
1083
+ className
1084
+ );
1085
+ const rootDataAttrs = {
1086
+ "data-collapsed": collapsed,
1087
+ "data-position": position,
1088
+ ...colorSchemeProp !== "auto" ? { "data-color-scheme": colorScheme } : {},
1089
+ ...reduceMotion === true ? { "data-reduce-motion": "true" } : {}
1090
+ };
1091
+ const shellProps = {
1092
+ items,
1093
+ ariaLabel,
1094
+ brand,
1095
+ header,
1096
+ footer,
1097
+ user,
1098
+ footerAction,
1099
+ collapsedFooter,
1100
+ classNames,
1101
+ slots,
1102
+ renderCtx
1103
+ };
1104
+ const desktopToggle = collapsible && (showCollapseToggle ?? true);
1105
+ return /* @__PURE__ */ jsxs(NavContext.Provider, { value: navContextValue, children: [
1106
+ !isMobile && /* @__PURE__ */ jsx(
1107
+ RootTag,
1108
+ {
1109
+ ref: rootRef,
1110
+ id,
1111
+ className: rootClassName,
1112
+ style: mergedStyle,
1113
+ ...rootDataAttrs,
1114
+ children: /* @__PURE__ */ jsx(
1115
+ SidebarShell,
1116
+ {
1117
+ ...shellProps,
1118
+ collapsed,
1119
+ showToggle: desktopToggle,
1120
+ onToggle: toggleCollapsed,
1121
+ togglePosition: position,
1122
+ toggleStrategy,
1123
+ toggleLabels: { expand: "Expand sidebar", collapse: "Collapse sidebar" }
1124
+ }
1125
+ )
1126
+ }
1127
+ ),
1128
+ isMobile && /* @__PURE__ */ jsx(
1129
+ "div",
1130
+ {
1131
+ ref: rootRef,
1132
+ className: rootClassName,
1133
+ style: mergedStyle,
1134
+ ...rootDataAttrs,
1135
+ children: /* @__PURE__ */ jsx(
1136
+ MobileDrawer,
1137
+ {
1138
+ open: mobileOpen,
1139
+ onClose: closeMobile,
1140
+ ariaLabel,
1141
+ width: drawerWidth,
1142
+ position,
1143
+ closeOnEscape,
1144
+ closeOnBackdropClick,
1145
+ trapFocus,
1146
+ lockScroll,
1147
+ restoreFocus,
1148
+ portalTarget,
1149
+ disablePortal,
1150
+ closeLabel: "Close navigation",
1151
+ classNames,
1152
+ children: /* @__PURE__ */ jsx(SidebarShell, { ...shellProps, collapsed: false, showToggle: false, onToggle: toggleCollapsed, togglePosition: position, toggleStrategy, toggleLabels: { expand: "Expand sidebar", collapse: "Collapse sidebar" } })
1153
+ }
1154
+ )
1155
+ }
1156
+ )
1157
+ ] });
1158
+ });
1159
+ Sidebar.displayName = "Sidebar";
1160
+
1161
+ // src/lib/themes.ts
1162
+ var slate = {};
1163
+ var greenMist = {
1164
+ fontDisplay: `'Plus Jakarta Sans', system-ui, sans-serif`,
1165
+ radiusItem: "16px",
1166
+ radiusLogo: "10px",
1167
+ radiusTooltip: "10px",
1168
+ radiusAction: "4px",
1169
+ ease: "cubic-bezier(0.16, 1, 0.3, 1)",
1170
+ shadowLogo: "0 1px 2px rgba(10,37,16,0.05)",
1171
+ shadowToggle: "0 4px 12px rgba(10,37,16,0.08)",
1172
+ shadowTooltip: "0 12px 32px rgba(10,37,16,0.12)",
1173
+ shadowDrawer: "0 24px 48px rgba(10,37,16,0.16)",
1174
+ badgeFontSize: "9px",
1175
+ light: {
1176
+ bg: "#0f3a18",
1177
+ border: "rgba(23,84,35,0.6)",
1178
+ divider: "rgba(23,84,35,0.4)",
1179
+ itemFg: "#b3ddb6",
1180
+ itemHoverBg: "rgba(23,84,35,0.7)",
1181
+ itemHoverFg: "#ffffff",
1182
+ itemActiveBg: "rgba(31,110,46,0.6)",
1183
+ itemActiveFg: "#ffffff",
1184
+ itemAccent: "#7cc485",
1185
+ headingFg: "#4faa5a",
1186
+ logoBg: "#4faa5a",
1187
+ logoFg: "#ffffff",
1188
+ titleFg: "#ffffff",
1189
+ subtitleFg: "#4faa5a",
1190
+ tooltipBg: "#0a2510",
1191
+ tooltipFg: "#d9eed9",
1192
+ tooltipBorder: "#175423",
1193
+ toggleBg: "#2d8a3e",
1194
+ toggleHoverBg: "#4faa5a",
1195
+ toggleFg: "#ffffff",
1196
+ toggleBorder: "#175423",
1197
+ avatarBg: "#2d8a3e",
1198
+ avatarFg: "#ffffff",
1199
+ avatarRing: "rgba(79,170,90,0.4)",
1200
+ userNameFg: "#d9eed9",
1201
+ userMetaFg: "#4faa5a",
1202
+ actionFg: "#4faa5a",
1203
+ actionHoverFg: "#ffffff",
1204
+ actionHoverBg: "rgba(23,84,35,0.5)",
1205
+ badgeBg: "rgba(251,191,36,0.2)",
1206
+ badgeFg: "#fcd34d",
1207
+ badgeBorder: "rgba(251,191,36,0.3)",
1208
+ scrollbarThumb: "#b3ddb6",
1209
+ scrollbarThumbHover: "#7cc485"
1210
+ },
1211
+ dark: {
1212
+ bg: "#18181b",
1213
+ border: "#2d2d2d",
1214
+ divider: "#2d2d2d",
1215
+ itemFg: "#8a8a8a",
1216
+ itemHoverBg: "rgba(255,255,255,0.1)",
1217
+ itemHoverFg: "#f5f5f5",
1218
+ itemActiveBg: "rgba(255,255,255,0.1)",
1219
+ itemActiveFg: "#e2e2e2",
1220
+ itemAccent: "#8a8a8a",
1221
+ headingFg: "#8a8a8a",
1222
+ logoBg: "#27272a",
1223
+ logoFg: "#e2e2e2",
1224
+ titleFg: "#e2e2e2",
1225
+ subtitleFg: "#8a8a8a",
1226
+ tooltipBg: "#0f0f0f",
1227
+ tooltipFg: "#c8c8c8",
1228
+ tooltipBorder: "#2d2d2d",
1229
+ toggleBg: "#27272a",
1230
+ toggleHoverBg: "#333333",
1231
+ toggleFg: "#e2e2e2",
1232
+ toggleBorder: "#3d3d3d",
1233
+ avatarBg: "#27272a",
1234
+ avatarFg: "#e2e2e2",
1235
+ avatarRing: "rgba(255,255,255,0.1)",
1236
+ userNameFg: "#e5e5e5",
1237
+ userMetaFg: "#8a8a8a",
1238
+ actionFg: "#8a8a8a",
1239
+ actionHoverFg: "#ffffff",
1240
+ actionHoverBg: "rgba(255,255,255,0.1)",
1241
+ scrollbarThumb: "#3a3a3a",
1242
+ scrollbarThumbHover: "#4d4d4d"
1243
+ }
1244
+ };
1245
+ var midnight = {
1246
+ light: {
1247
+ bg: "#18181b",
1248
+ border: "rgba(255,255,255,0.08)",
1249
+ divider: "rgba(255,255,255,0.06)",
1250
+ itemFg: "#a1a1aa",
1251
+ itemHoverBg: "rgba(255,255,255,0.08)",
1252
+ itemHoverFg: "#ffffff",
1253
+ itemActiveBg: "rgba(255,255,255,0.12)",
1254
+ itemActiveFg: "#ffffff",
1255
+ itemAccent: "#a78bfa",
1256
+ headingFg: "#71717a",
1257
+ logoBg: "#3f3f46",
1258
+ logoFg: "#ffffff",
1259
+ titleFg: "#ffffff",
1260
+ subtitleFg: "#a1a1aa",
1261
+ tooltipBg: "#09090b",
1262
+ tooltipFg: "#e4e4e7",
1263
+ tooltipBorder: "#3f3f46",
1264
+ toggleBg: "#3f3f46",
1265
+ toggleHoverBg: "#52525b",
1266
+ toggleFg: "#ffffff",
1267
+ toggleBorder: "#18181b",
1268
+ avatarBg: "#3f3f46",
1269
+ avatarFg: "#ffffff",
1270
+ avatarRing: "rgba(255,255,255,0.15)",
1271
+ userNameFg: "#e4e4e7",
1272
+ userMetaFg: "#a1a1aa",
1273
+ actionFg: "#a1a1aa",
1274
+ actionHoverFg: "#ffffff",
1275
+ actionHoverBg: "rgba(255,255,255,0.08)",
1276
+ scrollbarThumb: "rgba(255,255,255,0.15)",
1277
+ scrollbarThumbHover: "rgba(255,255,255,0.25)"
1278
+ }
1279
+ };
1280
+ var themes = { slate, greenMist, midnight };
1281
+ function defineTheme(theme) {
1282
+ return theme;
1283
+ }
1284
+
1285
+ export { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, CloseIcon, MenuIcon, Sidebar, defaultIsActive, defineTheme, greenMist, midnight, normalizePath, resolveIsActive, slate, themeToStyle, themes };
1286
+ //# sourceMappingURL=index.js.map
1287
+ //# sourceMappingURL=index.js.map