@kopexa/sidebar 17.2.0 → 17.3.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/v2/index.js CHANGED
@@ -48,15 +48,36 @@ __export(v2_exports, {
48
48
  module.exports = __toCommonJS(v2_exports);
49
49
 
50
50
  // src/v2/app-shell.tsx
51
- var import_shared_utils = require("@kopexa/shared-utils");
52
51
  var import_react2 = require("react");
53
52
  var import_react_dom = require("react-dom");
54
53
 
55
54
  // src/v2/context.tsx
56
55
  var import_react_utils = require("@kopexa/react-utils");
56
+ var import_theme = require("@kopexa/theme");
57
57
  var import_tooltip = require("@kopexa/tooltip");
58
58
  var import_use_is_mobile = require("@kopexa/use-is-mobile");
59
59
  var import_react = require("react");
60
+
61
+ // src/v2/types.ts
62
+ function normalizePath(href) {
63
+ const path = href.split("#")[0].split("?")[0].replace(/\/+$/, "");
64
+ return path === "" ? "/" : path;
65
+ }
66
+ function pathMatchLength(currentPath, href) {
67
+ const target = normalizePath(href);
68
+ if (currentPath === target) return target.length;
69
+ if (target !== "/" && currentPath.startsWith(`${target}/`))
70
+ return target.length;
71
+ return -1;
72
+ }
73
+ function panelItemHasChildren(item) {
74
+ return "children" in item && Array.isArray(item.children);
75
+ }
76
+ function panelItemIsSection(item) {
77
+ return "section" in item;
78
+ }
79
+
80
+ // src/v2/context.tsx
60
81
  var import_jsx_runtime = require("react/jsx-runtime");
61
82
  var PIN_COOKIE = "kpx_sidebar_v2_pinned";
62
83
  var PIN_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
@@ -93,6 +114,7 @@ function SidebarV2Provider({
93
114
  }, []);
94
115
  const panelOverrideActive = overrideCount > 0;
95
116
  const navPreviewActive = pinned ? selectedRail !== null : flyoutValue !== null;
117
+ const styles = (0, import_react.useMemo)(() => (0, import_theme.sidebarV2)({ tone }), [tone]);
96
118
  const setPinned = (0, import_react.useCallback)(
97
119
  (value2) => {
98
120
  onPinnedChange == null ? void 0 : onPinnedChange(value2);
@@ -122,12 +144,13 @@ function SidebarV2Provider({
122
144
  setOpenGroup((curr) => curr === key ? null : key);
123
145
  }, []);
124
146
  const isActive = (0, import_react.useCallback)(
125
- (href) => activeHref === href || href !== "/" && activeHref.startsWith(`${href}/`),
147
+ (href) => pathMatchLength(normalizePath(activeHref), href) >= 0,
126
148
  [activeHref]
127
149
  );
128
150
  const value = (0, import_react.useMemo)(
129
151
  () => ({
130
152
  tone,
153
+ styles,
131
154
  pinned,
132
155
  togglePin,
133
156
  setPinned,
@@ -156,6 +179,7 @@ function SidebarV2Provider({
156
179
  }),
157
180
  [
158
181
  tone,
182
+ styles,
159
183
  pinned,
160
184
  togglePin,
161
185
  setPinned,
@@ -198,17 +222,13 @@ function AppShellRoot({
198
222
  children,
199
223
  ...props
200
224
  }) {
201
- const { tone } = useSidebarV2();
225
+ const { tone, styles } = useSidebarV2();
202
226
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
203
227
  "div",
204
228
  {
205
229
  "data-slot": "app-shell",
206
230
  "data-tone": tone,
207
- className: (0, import_shared_utils.cn)(
208
- "relative isolate grid h-svh w-full overflow-hidden antialiased",
209
- tone === "light" ? "bg-muted text-foreground" : "bg-sidebar text-sidebar-foreground",
210
- className
211
- ),
231
+ className: styles.shell({ className }),
212
232
  style: {
213
233
  "--kpx-rail-width": RAIL_WIDTH,
214
234
  "--kpx-panel-width": PANEL_WIDTH,
@@ -227,26 +247,24 @@ function AppShellHeader({
227
247
  style,
228
248
  ...props
229
249
  }) {
250
+ const { styles } = useSidebarV2();
230
251
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
231
252
  "header",
232
253
  {
233
254
  "data-slot": "app-shell-header",
234
- className: (0, import_shared_utils.cn)("z-20 flex h-14 items-center gap-3 px-2", className),
255
+ className: styles.header({ className }),
235
256
  style: { gridArea: "header", ...style },
236
257
  ...props
237
258
  }
238
259
  );
239
260
  }
240
261
  function AppShellMain({ className, style, ...props }) {
262
+ const { styles } = useSidebarV2();
241
263
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
242
264
  "main",
243
265
  {
244
266
  "data-slot": "app-shell-main",
245
- className: (0, import_shared_utils.cn)(
246
- "relative flex min-h-0 min-w-0 flex-col overflow-hidden bg-background",
247
- "m-2 rounded-xl shadow-sm",
248
- className
249
- ),
267
+ className: styles.main({ className }),
250
268
  style: { gridArea: "main", ...style },
251
269
  ...props
252
270
  }
@@ -257,11 +275,12 @@ function AppShellAside({
257
275
  style,
258
276
  ...props
259
277
  }) {
278
+ const { styles } = useSidebarV2();
260
279
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
261
280
  "aside",
262
281
  {
263
282
  "data-slot": "app-shell-aside",
264
- className: (0, import_shared_utils.cn)("min-h-0 min-w-0 overflow-hidden", className),
283
+ className: styles.aside({ className }),
265
284
  style: { gridArea: "aside", ...style },
266
285
  ...props
267
286
  }
@@ -282,56 +301,32 @@ AppShell.PanelContent = AppShellPanelContent;
282
301
  // src/v2/components.tsx
283
302
  var import_button = require("@kopexa/button");
284
303
  var import_icons = require("@kopexa/icons");
285
- var import_shared_utils2 = require("@kopexa/shared-utils");
286
- var import_theme = require("@kopexa/theme");
304
+ var import_shared_utils = require("@kopexa/shared-utils");
305
+ var import_theme2 = require("@kopexa/theme");
287
306
  var import_tooltip2 = require("@kopexa/tooltip");
288
307
  var import_react3 = require("react");
289
-
290
- // src/v2/types.ts
291
- function panelItemHasChildren(item) {
292
- return "children" in item && Array.isArray(item.children);
293
- }
294
- function panelItemIsSection(item) {
295
- return "section" in item;
296
- }
297
-
298
- // src/v2/components.tsx
299
308
  var import_jsx_runtime3 = require("react/jsx-runtime");
300
309
  function SidebarV2Inset({
301
310
  className,
302
311
  ...props
303
312
  }) {
304
- const { tone } = useSidebarV2();
313
+ const { styles } = useSidebarV2();
305
314
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
306
315
  "main",
307
316
  {
308
317
  "data-slot": "sidebar-v2-inset",
309
- className: (0, import_shared_utils2.cn)(
310
- "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden bg-background",
311
- "md:m-2 md:rounded-xl md:shadow-sm",
312
- // On a light surround the white card needs a hairline to define its
313
- // edge; on the dark surround a border would read as a seam.
314
- tone === "light" && "md:border md:border-border/70",
315
- className
316
- ),
318
+ className: styles.inset({ className }),
317
319
  ...props
318
320
  }
319
321
  );
320
322
  }
321
323
  function SidebarV2Rail({ className, ...props }) {
322
- const { tone } = useSidebarV2();
323
- const light = tone === "light";
324
+ const { styles } = useSidebarV2();
324
325
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
325
326
  "nav",
326
327
  {
327
328
  "data-slot": "sidebar-v2-rail",
328
- className: (0, import_shared_utils2.cn)(
329
- "flex w-(--kpx-rail-width) shrink-0 flex-col overflow-hidden bg-sidebar py-2 text-sidebar-foreground",
330
- // Light tone: the dark rail floats as a rounded card on the light
331
- // surround. Dark tone: full-height, flush to the edge.
332
- light ? "m-2 rounded-2xl shadow-sm" : "h-full",
333
- className
334
- ),
329
+ className: styles.rail({ className }),
335
330
  ...props
336
331
  }
337
332
  );
@@ -340,6 +335,7 @@ function SidebarV2RailSpacer() {
340
335
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "aria-hidden": true, className: "flex-1" });
341
336
  }
342
337
  var SidebarV2Workspace = (0, import_react3.forwardRef)(({ name, role, logo, className, appearance = "rail", ...props }, ref) => {
338
+ const { styles } = useSidebarV2();
343
339
  if (appearance === "bar") {
344
340
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
345
341
  "button",
@@ -348,19 +344,15 @@ var SidebarV2Workspace = (0, import_react3.forwardRef)(({ name, role, logo, clas
348
344
  type: "button",
349
345
  "data-slot": "sidebar-v2-workspace",
350
346
  "aria-label": role ? `${name} \u2013 ${role}` : name,
351
- className: (0, import_shared_utils2.cn)(
352
- "group/ws flex cursor-pointer items-center gap-2 rounded-lg py-1 pr-2 pl-1",
353
- "outline-hidden ring-ring transition-colors hover:bg-foreground/5 focus-visible:ring-2",
354
- className
355
- ),
347
+ className: styles.workspaceBar({ className }),
356
348
  ...props,
357
349
  children: [
358
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-primary text-xs font-semibold text-primary-foreground", children: logo != null ? logo : name.charAt(0).toUpperCase() }),
359
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "flex min-w-0 flex-col text-left leading-tight", children: [
360
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "truncate text-sm font-semibold text-foreground", children: name }),
361
- role && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: role })
350
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.workspaceBarLogo(), children: logo != null ? logo : name.charAt(0).toUpperCase() }),
351
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: styles.workspaceBarText(), children: [
352
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.workspaceBarName(), children: name }),
353
+ role && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.workspaceBarRole(), children: role })
362
354
  ] }),
363
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.ChevronDownIcon, { className: "size-4 shrink-0 text-muted-foreground" })
355
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.ChevronDownIcon, { className: styles.workspaceBarChevron() })
364
356
  ]
365
357
  }
366
358
  );
@@ -372,48 +364,25 @@ var SidebarV2Workspace = (0, import_react3.forwardRef)(({ name, role, logo, clas
372
364
  type: "button",
373
365
  "data-slot": "sidebar-v2-workspace",
374
366
  "aria-label": role ? `${name} \u2013 ${role}` : name,
375
- className: (0, import_shared_utils2.cn)(
376
- "group/ws relative mx-auto mt-1 flex cursor-pointer items-center justify-center rounded-lg p-1",
377
- "outline-hidden ring-sidebar-ring transition-colors",
378
- "hover:bg-sidebar-accent focus-visible:ring-2",
379
- className
380
- ),
367
+ className: styles.workspaceRail({ className }),
381
368
  ...props,
382
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "relative flex size-9 shrink-0 items-center justify-center rounded-lg bg-primary text-sm font-semibold text-primary-foreground", children: [
369
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: styles.workspaceRailLogo(), children: [
383
370
  logo != null ? logo : name.charAt(0).toUpperCase(),
384
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.ChevronDownIcon, { className: "absolute -right-1 -bottom-1 size-3.5 rounded-full bg-sidebar p-px text-sidebar-foreground/60" })
371
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.ChevronDownIcon, { className: styles.workspaceRailChevron() })
385
372
  ] })
386
373
  }
387
374
  );
388
375
  });
389
376
  SidebarV2Workspace.displayName = "SidebarV2Workspace";
390
- function railButtonClasses(active) {
391
- return (0, import_shared_utils2.cn)(
392
- "group/rail relative mx-auto flex size-11 shrink-0 cursor-pointer items-center justify-center rounded-xl",
393
- "outline-hidden ring-sidebar-ring transition-colors",
394
- "text-sidebar-foreground/85 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
395
- "focus-visible:ring-2",
396
- active && "bg-sidebar-accent text-sidebar-accent-foreground"
397
- );
398
- }
399
377
  function RailInner({
400
378
  icon: Icon,
401
- active,
402
379
  badge
403
380
  }) {
381
+ const { styles } = useSidebarV2();
404
382
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
405
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
406
- "span",
407
- {
408
- "aria-hidden": true,
409
- className: (0, import_shared_utils2.cn)(
410
- "absolute top-1/2 left-0 h-5 w-0.5 -translate-x-[0.6rem] -translate-y-1/2 rounded-r bg-primary transition-opacity",
411
- active ? "opacity-100" : "opacity-0"
412
- )
413
- }
414
- ),
415
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: "size-5 shrink-0" }),
416
- badge != null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "absolute -top-0.5 -right-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[0.5625rem] font-semibold text-primary-foreground", children: badge })
383
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": true, className: styles.railIndicator() }),
384
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: styles.railIcon() }),
385
+ badge != null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.railBadge(), children: badge })
417
386
  ] });
418
387
  }
419
388
  function SidebarV2RailLink({
@@ -422,18 +391,18 @@ function SidebarV2RailLink({
422
391
  href,
423
392
  badge
424
393
  }) {
425
- const { renderLink, isActive, resetPanelSelection } = useSidebarV2();
394
+ const { renderLink, isActive, resetPanelSelection, styles } = useSidebarV2();
426
395
  const active = isActive(href);
427
396
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_tooltip2.Tooltip, { content: label, side: "right", children: renderLink({
428
397
  href,
429
- className: railButtonClasses(active),
398
+ className: styles.railButton(),
430
399
  "data-active": active,
431
400
  "aria-current": active ? "page" : void 0,
432
401
  "aria-label": label,
433
402
  // Navigating to a destination link clears any open panel preview,
434
403
  // even when the route doesn't change (e.g. already on it).
435
404
  onClick: resetPanelSelection,
436
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RailInner, { icon, label, active, badge })
405
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RailInner, { icon, badge })
437
406
  }) });
438
407
  }
439
408
  function SidebarV2RailItem({
@@ -446,6 +415,7 @@ function SidebarV2RailItem({
446
415
  onMouseLeave,
447
416
  badge
448
417
  }) {
418
+ const { styles } = useSidebarV2();
449
419
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_tooltip2.Tooltip, { content: label, side: "right", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
450
420
  "button",
451
421
  {
@@ -456,8 +426,8 @@ function SidebarV2RailItem({
456
426
  onClick,
457
427
  onMouseEnter,
458
428
  onMouseLeave,
459
- className: railButtonClasses(active),
460
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RailInner, { icon, label, active, badge })
429
+ className: styles.railButton(),
430
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RailInner, { icon, badge })
461
431
  }
462
432
  ) });
463
433
  }
@@ -470,54 +440,18 @@ function SidebarV2Panel({
470
440
  children,
471
441
  className
472
442
  }) {
473
- const { togglePin, pinned, tone } = useSidebarV2();
474
- const light = tone === "light";
443
+ const { togglePin, pinned, styles } = useSidebarV2();
475
444
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
476
445
  "div",
477
446
  {
478
447
  "data-slot": "sidebar-v2-panel",
479
- "data-floating": floating,
480
- className: (0, import_shared_utils2.cn)(
481
- "flex h-full w-(--kpx-panel-width) shrink-0 flex-col",
482
- // Surface. The flyout is a distinct floating card (white on light),
483
- // so it reads as an overlay rather than a flat full-height slab.
484
- floating ? light ? "bg-background text-foreground" : "bg-sidebar text-sidebar-foreground" : light ? "bg-muted text-foreground" : "bg-sidebar text-sidebar-foreground",
485
- floating ? (
486
- // Floating card: rounded, soft layered shadow, hairline ring.
487
- (0, import_shared_utils2.cn)(
488
- "overflow-hidden rounded-2xl shadow-2xl shadow-black/20 ring-1",
489
- light ? "ring-black/5" : "ring-white/10"
490
- )
491
- ) : (
492
- // Pinned: light blends into the surround (no border); dark keeps a
493
- // hairline against the rail.
494
- light ? "" : "border-l border-sidebar-border/40"
495
- ),
496
- className
497
- ),
448
+ "data-floating": floating ? "true" : "false",
449
+ className: styles.panel({ className }),
498
450
  children: [
499
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-start gap-2 px-3 pt-3 pb-1.5", children: [
451
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: styles.panelHeader(), children: [
500
452
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "min-w-0 flex-1", children: [
501
- title && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
502
- "div",
503
- {
504
- className: (0, import_shared_utils2.cn)(
505
- "truncate text-sm font-semibold",
506
- light ? "text-foreground" : "text-sidebar-foreground"
507
- ),
508
- children: title
509
- }
510
- ),
511
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
512
- "div",
513
- {
514
- className: (0, import_shared_utils2.cn)(
515
- "truncate text-xs",
516
- light ? "text-muted-foreground" : "text-sidebar-foreground/70"
517
- ),
518
- children: subtitle
519
- }
520
- )
453
+ title && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: styles.panelTitle(), children: title }),
454
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: styles.panelSubtitle(), children: subtitle })
521
455
  ] }),
522
456
  action != null ? action : !hidePin && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
523
457
  import_tooltip2.Tooltip,
@@ -531,41 +465,28 @@ function SidebarV2Panel({
531
465
  size: "sm",
532
466
  "aria-label": pinned ? "Panel l\xF6sen" : "Panel anheften",
533
467
  onClick: togglePin,
534
- className: (0, import_shared_utils2.cn)(
535
- "-mr-1 shrink-0",
536
- light ? "text-muted-foreground hover:bg-foreground/5 hover:text-foreground" : "text-sidebar-foreground/60 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
537
- ),
468
+ className: styles.panelPin(),
538
469
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.PanelLeftIcon, { className: "size-4" })
539
470
  }
540
471
  )
541
472
  }
542
473
  )
543
474
  ] }),
544
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex min-h-0 flex-1 flex-col gap-0.5 overflow-auto p-2", children })
475
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: styles.panelBody(), children })
545
476
  ]
546
477
  }
547
478
  );
548
479
  }
549
- function panelRowLight(active) {
550
- return (0, import_shared_utils2.cn)(
551
- "flex h-8 w-full items-center gap-2.5 rounded-md px-2 text-sm outline-hidden ring-ring transition-colors focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
552
- active ? "bg-primary/10 font-medium text-primary" : "text-foreground/80 hover:bg-foreground/5"
553
- );
554
- }
555
480
  function SidebarV2PanelLabel({
556
481
  className,
557
482
  ...props
558
483
  }) {
559
- const { tone } = useSidebarV2();
484
+ const { styles } = useSidebarV2();
560
485
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
561
486
  "div",
562
487
  {
563
488
  "data-slot": "sidebar-v2-panel-label",
564
- className: (0, import_shared_utils2.cn)(
565
- "px-2 pt-3 pb-1 text-[0.6875rem] font-medium uppercase tracking-wide",
566
- tone === "light" ? "text-muted-foreground" : "text-sidebar-foreground/50",
567
- className
568
- ),
489
+ className: styles.panelLabel({ className }),
569
490
  ...props
570
491
  }
571
492
  );
@@ -574,7 +495,7 @@ function SidebarV2PanelLeaf({
574
495
  item,
575
496
  active: activeProp
576
497
  }) {
577
- const { renderLink, isActive, tone } = useSidebarV2();
498
+ const { renderLink, isActive, tone, styles } = useSidebarV2();
578
499
  const light = tone === "light";
579
500
  const Icon = item.icon;
580
501
  const active = activeProp != null ? activeProp : isActive(item.href);
@@ -582,51 +503,40 @@ function SidebarV2PanelLeaf({
582
503
  href: item.href,
583
504
  "data-active": active,
584
505
  "aria-current": active ? "page" : void 0,
585
- className: light ? panelRowLight(active) : (0, import_theme.sidebarMenuButton)({ size: "md" }),
506
+ // Light tone uses the recipe's leaf slot (group/leaf + data-active);
507
+ // dark tone reuses the existing sidebarMenuButton recipe.
508
+ className: light ? styles.panelLeaf() : (0, import_theme2.sidebarMenuButton)({ size: "md" }),
586
509
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
587
- Icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
588
- Icon,
589
- {
590
- className: (0, import_shared_utils2.cn)(
591
- light ? active ? void 0 : "text-muted-foreground" : "text-sidebar-foreground/75"
592
- )
593
- }
594
- ),
595
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
596
- item.badge != null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
597
- "span",
598
- {
599
- className: (0, import_shared_utils2.cn)(
600
- "ml-auto rounded-full px-1.5 text-[0.625rem] font-medium",
601
- light ? "bg-foreground/10 text-foreground/70" : "bg-sidebar-accent text-sidebar-accent-foreground"
602
- ),
603
- children: item.badge
604
- }
605
- )
510
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: styles.panelLeafIcon() }),
511
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.panelLeafLabel(), children: item.label }),
512
+ item.tag && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.panelLeafTag(), children: item.tag }),
513
+ item.badge != null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.panelLeafBadge(), children: item.badge })
606
514
  ] })
607
515
  }) });
608
516
  }
609
517
  function SidebarV2PanelGroup({
610
- item
518
+ item,
519
+ winningHref
611
520
  }) {
612
521
  var _a;
613
- const { openGroup, toggleGroup, activeHref, renderLink, tone } = useSidebarV2();
522
+ const { openGroup, toggleGroup, activeHref, renderLink, tone, styles } = useSidebarV2();
614
523
  const light = tone === "light";
615
524
  const Icon = item.icon;
616
525
  const key = (_a = item.value) != null ? _a : item.label;
617
- let bestChildHref = null;
618
- let bestLen = -1;
619
- for (const c of item.children) {
620
- if (activeHref === c.href) {
621
- bestChildHref = c.href;
622
- break;
623
- }
624
- if (activeHref.startsWith(`${c.href}/`) && c.href.length > bestLen) {
625
- bestChildHref = c.href;
626
- bestLen = c.href.length;
526
+ let winner = winningHref;
527
+ if (winner === void 0) {
528
+ const current = normalizePath(activeHref);
529
+ let len = -1;
530
+ winner = null;
531
+ for (const c of item.children) {
532
+ const l = pathMatchLength(current, c.href);
533
+ if (l > len) {
534
+ len = l;
535
+ winner = normalizePath(c.href);
536
+ }
627
537
  }
628
538
  }
629
- const containsActive = bestChildHref !== null;
539
+ const containsActive = winner != null && item.children.some((c) => normalizePath(c.href) === winner);
630
540
  const open = openGroup === key || openGroup === null && containsActive;
631
541
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
632
542
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
@@ -634,71 +544,76 @@ function SidebarV2PanelGroup({
634
544
  {
635
545
  type: "button",
636
546
  "data-state": open ? "open" : "closed",
547
+ "data-contains-active": containsActive,
637
548
  "aria-expanded": open,
638
549
  onClick: () => toggleGroup(key),
639
- className: (0, import_shared_utils2.cn)(
640
- light ? panelRowLight(false) : (0, import_theme.sidebarMenuButton)({ size: "md" }),
641
- "cursor-pointer",
642
- containsActive && (light ? "font-medium text-foreground" : "font-medium")
550
+ className: (0, import_shared_utils.cn)(
551
+ light ? styles.panelLeaf() : (0, import_theme2.sidebarMenuButton)({ size: "md" }),
552
+ styles.panelGroupButton()
643
553
  ),
644
554
  children: [
645
- Icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
646
- Icon,
647
- {
648
- className: light ? "text-muted-foreground" : "text-sidebar-foreground/75"
649
- }
650
- ),
651
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label }),
652
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
653
- import_icons.ChevronRightIcon,
654
- {
655
- className: (0, import_shared_utils2.cn)(
656
- "size-3.5! shrink-0 transition-transform",
657
- light ? "text-muted-foreground" : "text-sidebar-foreground/60",
658
- open && "rotate-90"
659
- )
660
- }
661
- )
555
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: styles.panelGroupIcon() }),
556
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.panelGroupLabel(), children: item.label }),
557
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.ChevronRightIcon, { className: styles.panelGroupChevron() })
662
558
  ]
663
559
  }
664
560
  ),
665
- open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
666
- "div",
667
- {
668
- className: (0, import_shared_utils2.cn)(
669
- "my-0.5 ml-[1.05rem] flex flex-col border-l pl-3",
670
- light ? "border-border" : "border-sidebar-border"
671
- ),
672
- children: item.children.map((child) => {
673
- const active = child.href === bestChildHref;
674
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: renderLink({
675
- href: child.href,
676
- "data-active": active,
677
- "aria-current": active ? "page" : void 0,
678
- className: (0, import_shared_utils2.cn)(
679
- "relative block rounded-md px-2 py-1.5 text-[0.8125rem] outline-hidden transition-colors focus-visible:ring-2",
680
- "before:absolute before:top-1/2 before:-left-3 before:h-px before:w-2.5",
681
- light ? active ? "bg-primary/10 font-medium text-primary ring-ring before:bg-primary" : "text-muted-foreground ring-ring hover:bg-foreground/5 hover:text-foreground before:bg-border" : active ? "bg-sidebar-accent font-medium text-sidebar-accent-foreground ring-sidebar-ring before:bg-primary" : "text-sidebar-foreground/80 ring-sidebar-ring hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground before:bg-sidebar-foreground/30"
682
- ),
683
- children: child.label
684
- }) }, child.href);
685
- })
686
- }
687
- )
561
+ open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: styles.panelTree(), children: item.children.map((child) => {
562
+ const active = normalizePath(child.href) === winner;
563
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: renderLink({
564
+ href: child.href,
565
+ "data-active": active,
566
+ "aria-current": active ? "page" : void 0,
567
+ className: styles.subItem(),
568
+ children: child.label
569
+ }) }, child.href);
570
+ }) })
688
571
  ] });
689
572
  }
690
573
  function SidebarV2PanelItems({
691
574
  items
692
575
  }) {
576
+ const { activeHref } = useSidebarV2();
577
+ const winningHref = (0, import_react3.useMemo)(() => {
578
+ const current = normalizePath(activeHref);
579
+ let href = null;
580
+ let len = -1;
581
+ for (const item of items) {
582
+ if (panelItemIsSection(item)) continue;
583
+ const hrefs = panelItemHasChildren(item) ? item.children.map((c) => c.href) : [item.href];
584
+ for (const h of hrefs) {
585
+ const l = pathMatchLength(current, h);
586
+ if (l > len) {
587
+ len = l;
588
+ href = normalizePath(h);
589
+ }
590
+ }
591
+ }
592
+ return href;
593
+ }, [items, activeHref]);
693
594
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: items.map((item, idx) => {
694
595
  var _a;
695
596
  if (panelItemIsSection(item)) {
696
597
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SidebarV2PanelLabel, { children: item.section }, `section-${item.section}-${idx}`);
697
598
  }
698
599
  if (panelItemHasChildren(item)) {
699
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SidebarV2PanelGroup, { item }, (_a = item.value) != null ? _a : item.label);
600
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
601
+ SidebarV2PanelGroup,
602
+ {
603
+ item,
604
+ winningHref
605
+ },
606
+ (_a = item.value) != null ? _a : item.label
607
+ );
700
608
  }
701
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SidebarV2PanelLeaf, { item }, item.href);
609
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
610
+ SidebarV2PanelLeaf,
611
+ {
612
+ item,
613
+ active: winningHref != null && normalizePath(item.href) === winningHref
614
+ },
615
+ item.href
616
+ );
702
617
  }) });
703
618
  }
704
619
  function SidebarV2Trigger({
@@ -722,7 +637,7 @@ function SidebarV2Trigger({
722
637
 
723
638
  // src/v2/from-config.tsx
724
639
  var import_drawer = require("@kopexa/drawer");
725
- var import_shared_utils3 = require("@kopexa/shared-utils");
640
+ var import_shared_utils2 = require("@kopexa/shared-utils");
726
641
  var import_react4 = require("motion/react");
727
642
  var import_react5 = require("react");
728
643
  var import_jsx_runtime4 = require("react/jsx-runtime");
@@ -751,13 +666,14 @@ function SidebarV2FromConfig({
751
666
  closeFlyout,
752
667
  selectedRail,
753
668
  setSelectedRail,
754
- isActive,
669
+ activeHref,
755
670
  isMobile,
756
671
  drawerOpen,
757
672
  setDrawerOpen,
758
673
  setPanelHost,
759
674
  panelOverrideActive,
760
- navPreviewActive
675
+ navPreviewActive,
676
+ styles
761
677
  } = useSidebarV2();
762
678
  const hoverMode = flyoutTrigger === "hover" && !pinned && !isMobile;
763
679
  const closeTimer = (0, import_react5.useRef)(null);
@@ -796,14 +712,21 @@ function SidebarV2FromConfig({
796
712
  [items]
797
713
  );
798
714
  const activeRailValue = (0, import_react5.useMemo)(() => {
799
- const match = panels.find(
800
- (p) => p.items.some((item) => {
801
- if (panelItemIsSection(item)) return false;
802
- return panelItemHasChildren(item) ? item.children.some((c) => isActive(c.href)) : isActive(item.href);
803
- })
804
- );
805
- return match ? entryValue(match) : null;
806
- }, [panels, isActive]);
715
+ var _a2, _b2;
716
+ const current = normalizePath(activeHref);
717
+ let best = null;
718
+ for (const p of panels) {
719
+ for (const item of p.items) {
720
+ if (panelItemIsSection(item)) continue;
721
+ const hrefs = panelItemHasChildren(item) ? item.children.map((c) => c.href) : [item.href];
722
+ for (const href of hrefs) {
723
+ const len = pathMatchLength(current, href);
724
+ if (len > ((_a2 = best == null ? void 0 : best.len) != null ? _a2 : -1)) best = { value: entryValue(p), len };
725
+ }
726
+ }
727
+ }
728
+ return (_b2 = best == null ? void 0 : best.value) != null ? _b2 : null;
729
+ }, [panels, activeHref]);
807
730
  const shownValue = pinned ? selectedRail != null ? selectedRail : activeRailValue : flyoutValue;
808
731
  const shownPanel = (_a = panels.find((p) => entryValue(p) === shownValue)) != null ? _a : null;
809
732
  const topEntries = items.filter((e) => e.slot !== "bottom");
@@ -814,7 +737,7 @@ function SidebarV2FromConfig({
814
737
  "div",
815
738
  {
816
739
  "aria-hidden": true,
817
- className: "mx-3 my-1.5 h-px shrink-0 bg-sidebar-border/70"
740
+ className: styles.railDivider()
818
741
  },
819
742
  `rail-divider-${index}`
820
743
  );
@@ -866,14 +789,14 @@ function SidebarV2FromConfig({
866
789
  );
867
790
  }
868
791
  const railContent = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
869
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: [
792
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: styles.railScroll(), children: [
870
793
  header && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
871
794
  header,
872
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mx-3 my-1 h-px shrink-0 bg-sidebar-border/60" })
795
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles.railHeaderDivider() })
873
796
  ] }),
874
797
  topEntries.map(renderRailEntry)
875
798
  ] }),
876
- bottomEntries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex shrink-0 flex-col gap-1 pt-1", children: bottomEntries.map(renderRailEntry) })
799
+ bottomEntries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles.railBottom(), children: bottomEntries.map(renderRailEntry) })
877
800
  ] });
878
801
  const navData = pinned && shownPanel && (navPreviewActive || !panelOverrideActive) ? shownPanel : null;
879
802
  const [bufferedPanel, setBufferedPanel] = (0, import_react5.useState)(navData);
@@ -905,7 +828,7 @@ function SidebarV2FromConfig({
905
828
  open: drawerOpen,
906
829
  onOpenChange: setDrawerOpen,
907
830
  placement: "left",
908
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_drawer.Drawer.Content, { className: "w-auto max-w-[85vw] border-0 bg-sidebar p-0 [&>button]:hidden", children: [
831
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_drawer.Drawer.Content, { className: styles.mobileDrawer(), children: [
909
832
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_drawer.Drawer.Header, { className: "sr-only", children: [
910
833
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_drawer.Drawer.Title, { children: "Navigation" }),
911
834
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_drawer.Drawer.Description, { children: "Hauptnavigation" })
@@ -933,10 +856,7 @@ function SidebarV2FromConfig({
933
856
  {
934
857
  ref: setPanelHost,
935
858
  "data-slot": "sidebar-v2-panel-zone",
936
- className: (0, import_shared_utils3.cn)(
937
- "relative shrink-0 transition-[width] duration-200 ease-out motion-reduce:transition-none",
938
- pinned && "overflow-hidden"
939
- ),
859
+ className: (0, import_shared_utils2.cn)(styles.panelZone(), pinned && "overflow-hidden"),
940
860
  style: { gridArea: "panel", width: docked ? PANEL_WIDTH2 : "0px" },
941
861
  children: [
942
862
  panelContent,
@@ -944,7 +864,7 @@ function SidebarV2FromConfig({
944
864
  import_react4.motion.div,
945
865
  {
946
866
  "data-floating": "true",
947
- className: "absolute inset-y-2 left-1 z-30",
867
+ className: styles.flyout(),
948
868
  initial: { x: -14, opacity: 0 },
949
869
  animate: { x: 0, opacity: 1 },
950
870
  exit: { x: -14, opacity: 0 },