@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.
@@ -39,23 +39,29 @@ module.exports = __toCommonJS(components_exports);
39
39
  var import_button = require("@kopexa/button");
40
40
  var import_icons = require("@kopexa/icons");
41
41
  var import_shared_utils = require("@kopexa/shared-utils");
42
- var import_theme = require("@kopexa/theme");
42
+ var import_theme2 = require("@kopexa/theme");
43
43
  var import_tooltip2 = require("@kopexa/tooltip");
44
44
  var import_react2 = require("react");
45
45
 
46
46
  // src/v2/context.tsx
47
47
  var import_react_utils = require("@kopexa/react-utils");
48
+ var import_theme = require("@kopexa/theme");
48
49
  var import_tooltip = require("@kopexa/tooltip");
49
50
  var import_use_is_mobile = require("@kopexa/use-is-mobile");
50
51
  var import_react = require("react");
51
- var import_jsx_runtime = require("react/jsx-runtime");
52
- var PIN_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
53
- var [Provider, useSidebarV2] = (0, import_react_utils.createContext)({
54
- name: "SidebarV2Context",
55
- errorMessage: "useSidebarV2 must be used within <SidebarV2> (the provider component)."
56
- });
57
52
 
58
53
  // src/v2/types.ts
54
+ function normalizePath(href) {
55
+ const path = href.split("#")[0].split("?")[0].replace(/\/+$/, "");
56
+ return path === "" ? "/" : path;
57
+ }
58
+ function pathMatchLength(currentPath, href) {
59
+ const target = normalizePath(href);
60
+ if (currentPath === target) return target.length;
61
+ if (target !== "/" && currentPath.startsWith(`${target}/`))
62
+ return target.length;
63
+ return -1;
64
+ }
59
65
  function panelItemHasChildren(item) {
60
66
  return "children" in item && Array.isArray(item.children);
61
67
  }
@@ -63,43 +69,37 @@ function panelItemIsSection(item) {
63
69
  return "section" in item;
64
70
  }
65
71
 
72
+ // src/v2/context.tsx
73
+ var import_jsx_runtime = require("react/jsx-runtime");
74
+ var PIN_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
75
+ var [Provider, useSidebarV2] = (0, import_react_utils.createContext)({
76
+ name: "SidebarV2Context",
77
+ errorMessage: "useSidebarV2 must be used within <SidebarV2> (the provider component)."
78
+ });
79
+
66
80
  // src/v2/components.tsx
67
81
  var import_jsx_runtime2 = require("react/jsx-runtime");
68
82
  function SidebarV2Inset({
69
83
  className,
70
84
  ...props
71
85
  }) {
72
- const { tone } = useSidebarV2();
86
+ const { styles } = useSidebarV2();
73
87
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
74
88
  "main",
75
89
  {
76
90
  "data-slot": "sidebar-v2-inset",
77
- className: (0, import_shared_utils.cn)(
78
- "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden bg-background",
79
- "md:m-2 md:rounded-xl md:shadow-sm",
80
- // On a light surround the white card needs a hairline to define its
81
- // edge; on the dark surround a border would read as a seam.
82
- tone === "light" && "md:border md:border-border/70",
83
- className
84
- ),
91
+ className: styles.inset({ className }),
85
92
  ...props
86
93
  }
87
94
  );
88
95
  }
89
96
  function SidebarV2Rail({ className, ...props }) {
90
- const { tone } = useSidebarV2();
91
- const light = tone === "light";
97
+ const { styles } = useSidebarV2();
92
98
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
93
99
  "nav",
94
100
  {
95
101
  "data-slot": "sidebar-v2-rail",
96
- className: (0, import_shared_utils.cn)(
97
- "flex w-(--kpx-rail-width) shrink-0 flex-col overflow-hidden bg-sidebar py-2 text-sidebar-foreground",
98
- // Light tone: the dark rail floats as a rounded card on the light
99
- // surround. Dark tone: full-height, flush to the edge.
100
- light ? "m-2 rounded-2xl shadow-sm" : "h-full",
101
- className
102
- ),
102
+ className: styles.rail({ className }),
103
103
  ...props
104
104
  }
105
105
  );
@@ -108,6 +108,7 @@ function SidebarV2RailSpacer() {
108
108
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "aria-hidden": true, className: "flex-1" });
109
109
  }
110
110
  var SidebarV2Workspace = (0, import_react2.forwardRef)(({ name, role, logo, className, appearance = "rail", ...props }, ref) => {
111
+ const { styles } = useSidebarV2();
111
112
  if (appearance === "bar") {
112
113
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
113
114
  "button",
@@ -116,19 +117,15 @@ var SidebarV2Workspace = (0, import_react2.forwardRef)(({ name, role, logo, clas
116
117
  type: "button",
117
118
  "data-slot": "sidebar-v2-workspace",
118
119
  "aria-label": role ? `${name} \u2013 ${role}` : name,
119
- className: (0, import_shared_utils.cn)(
120
- "group/ws flex cursor-pointer items-center gap-2 rounded-lg py-1 pr-2 pl-1",
121
- "outline-hidden ring-ring transition-colors hover:bg-foreground/5 focus-visible:ring-2",
122
- className
123
- ),
120
+ className: styles.workspaceBar({ className }),
124
121
  ...props,
125
122
  children: [
126
- /* @__PURE__ */ (0, import_jsx_runtime2.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() }),
127
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "flex min-w-0 flex-col text-left leading-tight", children: [
128
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "truncate text-sm font-semibold text-foreground", children: name }),
129
- role && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: role })
123
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.workspaceBarLogo(), children: logo != null ? logo : name.charAt(0).toUpperCase() }),
124
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: styles.workspaceBarText(), children: [
125
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.workspaceBarName(), children: name }),
126
+ role && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.workspaceBarRole(), children: role })
130
127
  ] }),
131
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.ChevronDownIcon, { className: "size-4 shrink-0 text-muted-foreground" })
128
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.ChevronDownIcon, { className: styles.workspaceBarChevron() })
132
129
  ]
133
130
  }
134
131
  );
@@ -140,48 +137,25 @@ var SidebarV2Workspace = (0, import_react2.forwardRef)(({ name, role, logo, clas
140
137
  type: "button",
141
138
  "data-slot": "sidebar-v2-workspace",
142
139
  "aria-label": role ? `${name} \u2013 ${role}` : name,
143
- className: (0, import_shared_utils.cn)(
144
- "group/ws relative mx-auto mt-1 flex cursor-pointer items-center justify-center rounded-lg p-1",
145
- "outline-hidden ring-sidebar-ring transition-colors",
146
- "hover:bg-sidebar-accent focus-visible:ring-2",
147
- className
148
- ),
140
+ className: styles.workspaceRail({ className }),
149
141
  ...props,
150
- children: /* @__PURE__ */ (0, import_jsx_runtime2.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: [
142
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: styles.workspaceRailLogo(), children: [
151
143
  logo != null ? logo : name.charAt(0).toUpperCase(),
152
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.ChevronDownIcon, { className: "absolute -right-1 -bottom-1 size-3.5 rounded-full bg-sidebar p-px text-sidebar-foreground/60" })
144
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.ChevronDownIcon, { className: styles.workspaceRailChevron() })
153
145
  ] })
154
146
  }
155
147
  );
156
148
  });
157
149
  SidebarV2Workspace.displayName = "SidebarV2Workspace";
158
- function railButtonClasses(active) {
159
- return (0, import_shared_utils.cn)(
160
- "group/rail relative mx-auto flex size-11 shrink-0 cursor-pointer items-center justify-center rounded-xl",
161
- "outline-hidden ring-sidebar-ring transition-colors",
162
- "text-sidebar-foreground/85 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
163
- "focus-visible:ring-2",
164
- active && "bg-sidebar-accent text-sidebar-accent-foreground"
165
- );
166
- }
167
150
  function RailInner({
168
151
  icon: Icon,
169
- active,
170
152
  badge
171
153
  }) {
154
+ const { styles } = useSidebarV2();
172
155
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
173
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
174
- "span",
175
- {
176
- "aria-hidden": true,
177
- className: (0, import_shared_utils.cn)(
178
- "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",
179
- active ? "opacity-100" : "opacity-0"
180
- )
181
- }
182
- ),
183
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { className: "size-5 shrink-0" }),
184
- badge != null && /* @__PURE__ */ (0, import_jsx_runtime2.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 })
156
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": true, className: styles.railIndicator() }),
157
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { className: styles.railIcon() }),
158
+ badge != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.railBadge(), children: badge })
185
159
  ] });
186
160
  }
187
161
  function SidebarV2RailLink({
@@ -190,18 +164,18 @@ function SidebarV2RailLink({
190
164
  href,
191
165
  badge
192
166
  }) {
193
- const { renderLink, isActive, resetPanelSelection } = useSidebarV2();
167
+ const { renderLink, isActive, resetPanelSelection, styles } = useSidebarV2();
194
168
  const active = isActive(href);
195
169
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_tooltip2.Tooltip, { content: label, side: "right", children: renderLink({
196
170
  href,
197
- className: railButtonClasses(active),
171
+ className: styles.railButton(),
198
172
  "data-active": active,
199
173
  "aria-current": active ? "page" : void 0,
200
174
  "aria-label": label,
201
175
  // Navigating to a destination link clears any open panel preview,
202
176
  // even when the route doesn't change (e.g. already on it).
203
177
  onClick: resetPanelSelection,
204
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RailInner, { icon, label, active, badge })
178
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RailInner, { icon, badge })
205
179
  }) });
206
180
  }
207
181
  function SidebarV2RailItem({
@@ -214,6 +188,7 @@ function SidebarV2RailItem({
214
188
  onMouseLeave,
215
189
  badge
216
190
  }) {
191
+ const { styles } = useSidebarV2();
217
192
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_tooltip2.Tooltip, { content: label, side: "right", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
218
193
  "button",
219
194
  {
@@ -224,8 +199,8 @@ function SidebarV2RailItem({
224
199
  onClick,
225
200
  onMouseEnter,
226
201
  onMouseLeave,
227
- className: railButtonClasses(active),
228
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RailInner, { icon, label, active, badge })
202
+ className: styles.railButton(),
203
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RailInner, { icon, badge })
229
204
  }
230
205
  ) });
231
206
  }
@@ -238,54 +213,18 @@ function SidebarV2Panel({
238
213
  children,
239
214
  className
240
215
  }) {
241
- const { togglePin, pinned, tone } = useSidebarV2();
242
- const light = tone === "light";
216
+ const { togglePin, pinned, styles } = useSidebarV2();
243
217
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
244
218
  "div",
245
219
  {
246
220
  "data-slot": "sidebar-v2-panel",
247
- "data-floating": floating,
248
- className: (0, import_shared_utils.cn)(
249
- "flex h-full w-(--kpx-panel-width) shrink-0 flex-col",
250
- // Surface. The flyout is a distinct floating card (white on light),
251
- // so it reads as an overlay rather than a flat full-height slab.
252
- floating ? light ? "bg-background text-foreground" : "bg-sidebar text-sidebar-foreground" : light ? "bg-muted text-foreground" : "bg-sidebar text-sidebar-foreground",
253
- floating ? (
254
- // Floating card: rounded, soft layered shadow, hairline ring.
255
- (0, import_shared_utils.cn)(
256
- "overflow-hidden rounded-2xl shadow-2xl shadow-black/20 ring-1",
257
- light ? "ring-black/5" : "ring-white/10"
258
- )
259
- ) : (
260
- // Pinned: light blends into the surround (no border); dark keeps a
261
- // hairline against the rail.
262
- light ? "" : "border-l border-sidebar-border/40"
263
- ),
264
- className
265
- ),
221
+ "data-floating": floating ? "true" : "false",
222
+ className: styles.panel({ className }),
266
223
  children: [
267
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-start gap-2 px-3 pt-3 pb-1.5", children: [
224
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: styles.panelHeader(), children: [
268
225
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "min-w-0 flex-1", children: [
269
- title && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
270
- "div",
271
- {
272
- className: (0, import_shared_utils.cn)(
273
- "truncate text-sm font-semibold",
274
- light ? "text-foreground" : "text-sidebar-foreground"
275
- ),
276
- children: title
277
- }
278
- ),
279
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
280
- "div",
281
- {
282
- className: (0, import_shared_utils.cn)(
283
- "truncate text-xs",
284
- light ? "text-muted-foreground" : "text-sidebar-foreground/70"
285
- ),
286
- children: subtitle
287
- }
288
- )
226
+ title && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.panelTitle(), children: title }),
227
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.panelSubtitle(), children: subtitle })
289
228
  ] }),
290
229
  action != null ? action : !hidePin && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
291
230
  import_tooltip2.Tooltip,
@@ -299,41 +238,28 @@ function SidebarV2Panel({
299
238
  size: "sm",
300
239
  "aria-label": pinned ? "Panel l\xF6sen" : "Panel anheften",
301
240
  onClick: togglePin,
302
- className: (0, import_shared_utils.cn)(
303
- "-mr-1 shrink-0",
304
- light ? "text-muted-foreground hover:bg-foreground/5 hover:text-foreground" : "text-sidebar-foreground/60 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
305
- ),
241
+ className: styles.panelPin(),
306
242
  children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.PanelLeftIcon, { className: "size-4" })
307
243
  }
308
244
  )
309
245
  }
310
246
  )
311
247
  ] }),
312
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex min-h-0 flex-1 flex-col gap-0.5 overflow-auto p-2", children })
248
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.panelBody(), children })
313
249
  ]
314
250
  }
315
251
  );
316
252
  }
317
- function panelRowLight(active) {
318
- return (0, import_shared_utils.cn)(
319
- "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",
320
- active ? "bg-primary/10 font-medium text-primary" : "text-foreground/80 hover:bg-foreground/5"
321
- );
322
- }
323
253
  function SidebarV2PanelLabel({
324
254
  className,
325
255
  ...props
326
256
  }) {
327
- const { tone } = useSidebarV2();
257
+ const { styles } = useSidebarV2();
328
258
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
329
259
  "div",
330
260
  {
331
261
  "data-slot": "sidebar-v2-panel-label",
332
- className: (0, import_shared_utils.cn)(
333
- "px-2 pt-3 pb-1 text-[0.6875rem] font-medium uppercase tracking-wide",
334
- tone === "light" ? "text-muted-foreground" : "text-sidebar-foreground/50",
335
- className
336
- ),
262
+ className: styles.panelLabel({ className }),
337
263
  ...props
338
264
  }
339
265
  );
@@ -342,7 +268,7 @@ function SidebarV2PanelLeaf({
342
268
  item,
343
269
  active: activeProp
344
270
  }) {
345
- const { renderLink, isActive, tone } = useSidebarV2();
271
+ const { renderLink, isActive, tone, styles } = useSidebarV2();
346
272
  const light = tone === "light";
347
273
  const Icon = item.icon;
348
274
  const active = activeProp != null ? activeProp : isActive(item.href);
@@ -350,51 +276,40 @@ function SidebarV2PanelLeaf({
350
276
  href: item.href,
351
277
  "data-active": active,
352
278
  "aria-current": active ? "page" : void 0,
353
- className: light ? panelRowLight(active) : (0, import_theme.sidebarMenuButton)({ size: "md" }),
279
+ // Light tone uses the recipe's leaf slot (group/leaf + data-active);
280
+ // dark tone reuses the existing sidebarMenuButton recipe.
281
+ className: light ? styles.panelLeaf() : (0, import_theme2.sidebarMenuButton)({ size: "md" }),
354
282
  children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
355
- Icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
356
- Icon,
357
- {
358
- className: (0, import_shared_utils.cn)(
359
- light ? active ? void 0 : "text-muted-foreground" : "text-sidebar-foreground/75"
360
- )
361
- }
362
- ),
363
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
364
- item.badge != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
365
- "span",
366
- {
367
- className: (0, import_shared_utils.cn)(
368
- "ml-auto rounded-full px-1.5 text-[0.625rem] font-medium",
369
- light ? "bg-foreground/10 text-foreground/70" : "bg-sidebar-accent text-sidebar-accent-foreground"
370
- ),
371
- children: item.badge
372
- }
373
- )
283
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { className: styles.panelLeafIcon() }),
284
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.panelLeafLabel(), children: item.label }),
285
+ item.tag && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.panelLeafTag(), children: item.tag }),
286
+ item.badge != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.panelLeafBadge(), children: item.badge })
374
287
  ] })
375
288
  }) });
376
289
  }
377
290
  function SidebarV2PanelGroup({
378
- item
291
+ item,
292
+ winningHref
379
293
  }) {
380
294
  var _a;
381
- const { openGroup, toggleGroup, activeHref, renderLink, tone } = useSidebarV2();
295
+ const { openGroup, toggleGroup, activeHref, renderLink, tone, styles } = useSidebarV2();
382
296
  const light = tone === "light";
383
297
  const Icon = item.icon;
384
298
  const key = (_a = item.value) != null ? _a : item.label;
385
- let bestChildHref = null;
386
- let bestLen = -1;
387
- for (const c of item.children) {
388
- if (activeHref === c.href) {
389
- bestChildHref = c.href;
390
- break;
391
- }
392
- if (activeHref.startsWith(`${c.href}/`) && c.href.length > bestLen) {
393
- bestChildHref = c.href;
394
- bestLen = c.href.length;
299
+ let winner = winningHref;
300
+ if (winner === void 0) {
301
+ const current = normalizePath(activeHref);
302
+ let len = -1;
303
+ winner = null;
304
+ for (const c of item.children) {
305
+ const l = pathMatchLength(current, c.href);
306
+ if (l > len) {
307
+ len = l;
308
+ winner = normalizePath(c.href);
309
+ }
395
310
  }
396
311
  }
397
- const containsActive = bestChildHref !== null;
312
+ const containsActive = winner != null && item.children.some((c) => normalizePath(c.href) === winner);
398
313
  const open = openGroup === key || openGroup === null && containsActive;
399
314
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
400
315
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
@@ -402,71 +317,76 @@ function SidebarV2PanelGroup({
402
317
  {
403
318
  type: "button",
404
319
  "data-state": open ? "open" : "closed",
320
+ "data-contains-active": containsActive,
405
321
  "aria-expanded": open,
406
322
  onClick: () => toggleGroup(key),
407
323
  className: (0, import_shared_utils.cn)(
408
- light ? panelRowLight(false) : (0, import_theme.sidebarMenuButton)({ size: "md" }),
409
- "cursor-pointer",
410
- containsActive && (light ? "font-medium text-foreground" : "font-medium")
324
+ light ? styles.panelLeaf() : (0, import_theme2.sidebarMenuButton)({ size: "md" }),
325
+ styles.panelGroupButton()
411
326
  ),
412
327
  children: [
413
- Icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
414
- Icon,
415
- {
416
- className: light ? "text-muted-foreground" : "text-sidebar-foreground/75"
417
- }
418
- ),
419
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label }),
420
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
421
- import_icons.ChevronRightIcon,
422
- {
423
- className: (0, import_shared_utils.cn)(
424
- "size-3.5! shrink-0 transition-transform",
425
- light ? "text-muted-foreground" : "text-sidebar-foreground/60",
426
- open && "rotate-90"
427
- )
428
- }
429
- )
328
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { className: styles.panelGroupIcon() }),
329
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.panelGroupLabel(), children: item.label }),
330
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.ChevronRightIcon, { className: styles.panelGroupChevron() })
430
331
  ]
431
332
  }
432
333
  ),
433
- open && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
434
- "div",
435
- {
436
- className: (0, import_shared_utils.cn)(
437
- "my-0.5 ml-[1.05rem] flex flex-col border-l pl-3",
438
- light ? "border-border" : "border-sidebar-border"
439
- ),
440
- children: item.children.map((child) => {
441
- const active = child.href === bestChildHref;
442
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: renderLink({
443
- href: child.href,
444
- "data-active": active,
445
- "aria-current": active ? "page" : void 0,
446
- className: (0, import_shared_utils.cn)(
447
- "relative block rounded-md px-2 py-1.5 text-[0.8125rem] outline-hidden transition-colors focus-visible:ring-2",
448
- "before:absolute before:top-1/2 before:-left-3 before:h-px before:w-2.5",
449
- 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"
450
- ),
451
- children: child.label
452
- }) }, child.href);
453
- })
454
- }
455
- )
334
+ open && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.panelTree(), children: item.children.map((child) => {
335
+ const active = normalizePath(child.href) === winner;
336
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: renderLink({
337
+ href: child.href,
338
+ "data-active": active,
339
+ "aria-current": active ? "page" : void 0,
340
+ className: styles.subItem(),
341
+ children: child.label
342
+ }) }, child.href);
343
+ }) })
456
344
  ] });
457
345
  }
458
346
  function SidebarV2PanelItems({
459
347
  items
460
348
  }) {
349
+ const { activeHref } = useSidebarV2();
350
+ const winningHref = (0, import_react2.useMemo)(() => {
351
+ const current = normalizePath(activeHref);
352
+ let href = null;
353
+ let len = -1;
354
+ for (const item of items) {
355
+ if (panelItemIsSection(item)) continue;
356
+ const hrefs = panelItemHasChildren(item) ? item.children.map((c) => c.href) : [item.href];
357
+ for (const h of hrefs) {
358
+ const l = pathMatchLength(current, h);
359
+ if (l > len) {
360
+ len = l;
361
+ href = normalizePath(h);
362
+ }
363
+ }
364
+ }
365
+ return href;
366
+ }, [items, activeHref]);
461
367
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: items.map((item, idx) => {
462
368
  var _a;
463
369
  if (panelItemIsSection(item)) {
464
370
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SidebarV2PanelLabel, { children: item.section }, `section-${item.section}-${idx}`);
465
371
  }
466
372
  if (panelItemHasChildren(item)) {
467
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SidebarV2PanelGroup, { item }, (_a = item.value) != null ? _a : item.label);
373
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
374
+ SidebarV2PanelGroup,
375
+ {
376
+ item,
377
+ winningHref
378
+ },
379
+ (_a = item.value) != null ? _a : item.label
380
+ );
468
381
  }
469
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SidebarV2PanelLeaf, { item }, item.href);
382
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
383
+ SidebarV2PanelLeaf,
384
+ {
385
+ item,
386
+ active: winningHref != null && normalizePath(item.href) === winningHref
387
+ },
388
+ item.href
389
+ );
470
390
  }) });
471
391
  }
472
392
  function SidebarV2Trigger({
@@ -13,9 +13,9 @@ import {
13
13
  SidebarV2RailSpacer,
14
14
  SidebarV2Trigger,
15
15
  SidebarV2Workspace
16
- } from "../chunk-EIXUCY5M.mjs";
17
- import "../chunk-SDMGFB6V.mjs";
18
- import "../chunk-3L2F566G.mjs";
16
+ } from "../chunk-5VE25P3M.mjs";
17
+ import "../chunk-XBTONQ3L.mjs";
18
+ import "../chunk-BFZFZSUC.mjs";
19
19
  export {
20
20
  SidebarV2Inset,
21
21
  SidebarV2Panel,
@@ -1,4 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { sidebarV2 } from '@kopexa/theme';
2
3
  import { ReactNode } from 'react';
3
4
  import { SidebarV2LinkProps } from './types.mjs';
4
5
 
@@ -13,6 +14,8 @@ type SidebarV2FlyoutTrigger = "click" | "hover";
13
14
  type SidebarV2ContextValue = {
14
15
  /** Visual tone of the panel + surround. */
15
16
  tone: SidebarV2Tone;
17
+ /** Resolved tailwind-variants styles (from @kopexa/theme) for the tone. */
18
+ styles: ReturnType<typeof sidebarV2>;
16
19
  /** Pinned = persistent two-column. Unpinned = rail only + flyout panel. */
17
20
  pinned: boolean;
18
21
  togglePin: () => void;
@@ -1,4 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { sidebarV2 } from '@kopexa/theme';
2
3
  import { ReactNode } from 'react';
3
4
  import { SidebarV2LinkProps } from './types.js';
4
5
 
@@ -13,6 +14,8 @@ type SidebarV2FlyoutTrigger = "click" | "hover";
13
14
  type SidebarV2ContextValue = {
14
15
  /** Visual tone of the panel + surround. */
15
16
  tone: SidebarV2Tone;
17
+ /** Resolved tailwind-variants styles (from @kopexa/theme) for the tone. */
18
+ styles: ReturnType<typeof sidebarV2>;
16
19
  /** Pinned = persistent two-column. Unpinned = rail only + flyout panel. */
17
20
  pinned: boolean;
18
21
  togglePin: () => void;
@@ -27,9 +27,25 @@ __export(context_exports, {
27
27
  });
28
28
  module.exports = __toCommonJS(context_exports);
29
29
  var import_react_utils = require("@kopexa/react-utils");
30
+ var import_theme = require("@kopexa/theme");
30
31
  var import_tooltip = require("@kopexa/tooltip");
31
32
  var import_use_is_mobile = require("@kopexa/use-is-mobile");
32
33
  var import_react = require("react");
34
+
35
+ // src/v2/types.ts
36
+ function normalizePath(href) {
37
+ const path = href.split("#")[0].split("?")[0].replace(/\/+$/, "");
38
+ return path === "" ? "/" : path;
39
+ }
40
+ function pathMatchLength(currentPath, href) {
41
+ const target = normalizePath(href);
42
+ if (currentPath === target) return target.length;
43
+ if (target !== "/" && currentPath.startsWith(`${target}/`))
44
+ return target.length;
45
+ return -1;
46
+ }
47
+
48
+ // src/v2/context.tsx
33
49
  var import_jsx_runtime = require("react/jsx-runtime");
34
50
  var PIN_COOKIE = "kpx_sidebar_v2_pinned";
35
51
  var PIN_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
@@ -66,6 +82,7 @@ function SidebarV2Provider({
66
82
  }, []);
67
83
  const panelOverrideActive = overrideCount > 0;
68
84
  const navPreviewActive = pinned ? selectedRail !== null : flyoutValue !== null;
85
+ const styles = (0, import_react.useMemo)(() => (0, import_theme.sidebarV2)({ tone }), [tone]);
69
86
  const setPinned = (0, import_react.useCallback)(
70
87
  (value2) => {
71
88
  onPinnedChange == null ? void 0 : onPinnedChange(value2);
@@ -95,12 +112,13 @@ function SidebarV2Provider({
95
112
  setOpenGroup((curr) => curr === key ? null : key);
96
113
  }, []);
97
114
  const isActive = (0, import_react.useCallback)(
98
- (href) => activeHref === href || href !== "/" && activeHref.startsWith(`${href}/`),
115
+ (href) => pathMatchLength(normalizePath(activeHref), href) >= 0,
99
116
  [activeHref]
100
117
  );
101
118
  const value = (0, import_react.useMemo)(
102
119
  () => ({
103
120
  tone,
121
+ styles,
104
122
  pinned,
105
123
  togglePin,
106
124
  setPinned,
@@ -129,6 +147,7 @@ function SidebarV2Provider({
129
147
  }),
130
148
  [
131
149
  tone,
150
+ styles,
132
151
  pinned,
133
152
  togglePin,
134
153
  setPinned,
@@ -3,7 +3,8 @@
3
3
  import {
4
4
  SidebarV2Provider,
5
5
  useSidebarV2
6
- } from "../chunk-3L2F566G.mjs";
6
+ } from "../chunk-XBTONQ3L.mjs";
7
+ import "../chunk-BFZFZSUC.mjs";
7
8
  export {
8
9
  SidebarV2Provider,
9
10
  useSidebarV2