@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/index.js CHANGED
@@ -531,15 +531,36 @@ var Sidebar = Object.assign(SidebarRoot, {
531
531
  });
532
532
 
533
533
  // src/v2/app-shell.tsx
534
- var import_shared_utils = require("@kopexa/shared-utils");
535
534
  var import_react3 = require("react");
536
535
  var import_react_dom = require("react-dom");
537
536
 
538
537
  // src/v2/context.tsx
539
538
  var import_react_utils2 = require("@kopexa/react-utils");
539
+ var import_theme2 = require("@kopexa/theme");
540
540
  var import_tooltip2 = require("@kopexa/tooltip");
541
541
  var import_use_is_mobile2 = require("@kopexa/use-is-mobile");
542
542
  var import_react2 = require("react");
543
+
544
+ // src/v2/types.ts
545
+ function normalizePath(href) {
546
+ const path = href.split("#")[0].split("?")[0].replace(/\/+$/, "");
547
+ return path === "" ? "/" : path;
548
+ }
549
+ function pathMatchLength(currentPath, href) {
550
+ const target = normalizePath(href);
551
+ if (currentPath === target) return target.length;
552
+ if (target !== "/" && currentPath.startsWith(`${target}/`))
553
+ return target.length;
554
+ return -1;
555
+ }
556
+ function panelItemHasChildren(item) {
557
+ return "children" in item && Array.isArray(item.children);
558
+ }
559
+ function panelItemIsSection(item) {
560
+ return "section" in item;
561
+ }
562
+
563
+ // src/v2/context.tsx
543
564
  var import_jsx_runtime2 = require("react/jsx-runtime");
544
565
  var PIN_COOKIE = "kpx_sidebar_v2_pinned";
545
566
  var PIN_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
@@ -576,6 +597,7 @@ function SidebarV2Provider({
576
597
  }, []);
577
598
  const panelOverrideActive = overrideCount > 0;
578
599
  const navPreviewActive = pinned ? selectedRail !== null : flyoutValue !== null;
600
+ const styles = (0, import_react2.useMemo)(() => (0, import_theme2.sidebarV2)({ tone }), [tone]);
579
601
  const setPinned = (0, import_react2.useCallback)(
580
602
  (value2) => {
581
603
  onPinnedChange == null ? void 0 : onPinnedChange(value2);
@@ -605,12 +627,13 @@ function SidebarV2Provider({
605
627
  setOpenGroup((curr) => curr === key ? null : key);
606
628
  }, []);
607
629
  const isActive = (0, import_react2.useCallback)(
608
- (href) => activeHref === href || href !== "/" && activeHref.startsWith(`${href}/`),
630
+ (href) => pathMatchLength(normalizePath(activeHref), href) >= 0,
609
631
  [activeHref]
610
632
  );
611
633
  const value = (0, import_react2.useMemo)(
612
634
  () => ({
613
635
  tone,
636
+ styles,
614
637
  pinned,
615
638
  togglePin,
616
639
  setPinned,
@@ -639,6 +662,7 @@ function SidebarV2Provider({
639
662
  }),
640
663
  [
641
664
  tone,
665
+ styles,
642
666
  pinned,
643
667
  togglePin,
644
668
  setPinned,
@@ -681,17 +705,13 @@ function AppShellRoot({
681
705
  children,
682
706
  ...props
683
707
  }) {
684
- const { tone } = useSidebarV2();
708
+ const { tone, styles } = useSidebarV2();
685
709
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
686
710
  "div",
687
711
  {
688
712
  "data-slot": "app-shell",
689
713
  "data-tone": tone,
690
- className: (0, import_shared_utils.cn)(
691
- "relative isolate grid h-svh w-full overflow-hidden antialiased",
692
- tone === "light" ? "bg-muted text-foreground" : "bg-sidebar text-sidebar-foreground",
693
- className
694
- ),
714
+ className: styles.shell({ className }),
695
715
  style: {
696
716
  "--kpx-rail-width": RAIL_WIDTH,
697
717
  "--kpx-panel-width": PANEL_WIDTH,
@@ -710,26 +730,24 @@ function AppShellHeader({
710
730
  style,
711
731
  ...props
712
732
  }) {
733
+ const { styles } = useSidebarV2();
713
734
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
714
735
  "header",
715
736
  {
716
737
  "data-slot": "app-shell-header",
717
- className: (0, import_shared_utils.cn)("z-20 flex h-14 items-center gap-3 px-2", className),
738
+ className: styles.header({ className }),
718
739
  style: { gridArea: "header", ...style },
719
740
  ...props
720
741
  }
721
742
  );
722
743
  }
723
744
  function AppShellMain({ className, style, ...props }) {
745
+ const { styles } = useSidebarV2();
724
746
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
725
747
  "main",
726
748
  {
727
749
  "data-slot": "app-shell-main",
728
- className: (0, import_shared_utils.cn)(
729
- "relative flex min-h-0 min-w-0 flex-col overflow-hidden bg-background",
730
- "m-2 rounded-xl shadow-sm",
731
- className
732
- ),
750
+ className: styles.main({ className }),
733
751
  style: { gridArea: "main", ...style },
734
752
  ...props
735
753
  }
@@ -740,11 +758,12 @@ function AppShellAside({
740
758
  style,
741
759
  ...props
742
760
  }) {
761
+ const { styles } = useSidebarV2();
743
762
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
744
763
  "aside",
745
764
  {
746
765
  "data-slot": "app-shell-aside",
747
- className: (0, import_shared_utils.cn)("min-h-0 min-w-0 overflow-hidden", className),
766
+ className: styles.aside({ className }),
748
767
  style: { gridArea: "aside", ...style },
749
768
  ...props
750
769
  }
@@ -765,56 +784,32 @@ AppShell.PanelContent = AppShellPanelContent;
765
784
  // src/v2/components.tsx
766
785
  var import_button2 = require("@kopexa/button");
767
786
  var import_icons2 = require("@kopexa/icons");
768
- var import_shared_utils2 = require("@kopexa/shared-utils");
769
- var import_theme2 = require("@kopexa/theme");
787
+ var import_shared_utils = require("@kopexa/shared-utils");
788
+ var import_theme3 = require("@kopexa/theme");
770
789
  var import_tooltip3 = require("@kopexa/tooltip");
771
790
  var import_react4 = require("react");
772
-
773
- // src/v2/types.ts
774
- function panelItemHasChildren(item) {
775
- return "children" in item && Array.isArray(item.children);
776
- }
777
- function panelItemIsSection(item) {
778
- return "section" in item;
779
- }
780
-
781
- // src/v2/components.tsx
782
791
  var import_jsx_runtime4 = require("react/jsx-runtime");
783
792
  function SidebarV2Inset({
784
793
  className,
785
794
  ...props
786
795
  }) {
787
- const { tone } = useSidebarV2();
796
+ const { styles } = useSidebarV2();
788
797
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
789
798
  "main",
790
799
  {
791
800
  "data-slot": "sidebar-v2-inset",
792
- className: (0, import_shared_utils2.cn)(
793
- "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden bg-background",
794
- "md:m-2 md:rounded-xl md:shadow-sm",
795
- // On a light surround the white card needs a hairline to define its
796
- // edge; on the dark surround a border would read as a seam.
797
- tone === "light" && "md:border md:border-border/70",
798
- className
799
- ),
801
+ className: styles.inset({ className }),
800
802
  ...props
801
803
  }
802
804
  );
803
805
  }
804
806
  function SidebarV2Rail({ className, ...props }) {
805
- const { tone } = useSidebarV2();
806
- const light = tone === "light";
807
+ const { styles } = useSidebarV2();
807
808
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
808
809
  "nav",
809
810
  {
810
811
  "data-slot": "sidebar-v2-rail",
811
- className: (0, import_shared_utils2.cn)(
812
- "flex w-(--kpx-rail-width) shrink-0 flex-col overflow-hidden bg-sidebar py-2 text-sidebar-foreground",
813
- // Light tone: the dark rail floats as a rounded card on the light
814
- // surround. Dark tone: full-height, flush to the edge.
815
- light ? "m-2 rounded-2xl shadow-sm" : "h-full",
816
- className
817
- ),
812
+ className: styles.rail({ className }),
818
813
  ...props
819
814
  }
820
815
  );
@@ -823,6 +818,7 @@ function SidebarV2RailSpacer() {
823
818
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "aria-hidden": true, className: "flex-1" });
824
819
  }
825
820
  var SidebarV2Workspace = (0, import_react4.forwardRef)(({ name, role, logo, className, appearance = "rail", ...props }, ref) => {
821
+ const { styles } = useSidebarV2();
826
822
  if (appearance === "bar") {
827
823
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
828
824
  "button",
@@ -831,19 +827,15 @@ var SidebarV2Workspace = (0, import_react4.forwardRef)(({ name, role, logo, clas
831
827
  type: "button",
832
828
  "data-slot": "sidebar-v2-workspace",
833
829
  "aria-label": role ? `${name} \u2013 ${role}` : name,
834
- className: (0, import_shared_utils2.cn)(
835
- "group/ws flex cursor-pointer items-center gap-2 rounded-lg py-1 pr-2 pl-1",
836
- "outline-hidden ring-ring transition-colors hover:bg-foreground/5 focus-visible:ring-2",
837
- className
838
- ),
830
+ className: styles.workspaceBar({ className }),
839
831
  ...props,
840
832
  children: [
841
- /* @__PURE__ */ (0, import_jsx_runtime4.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() }),
842
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex min-w-0 flex-col text-left leading-tight", children: [
843
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate text-sm font-semibold text-foreground", children: name }),
844
- role && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: role })
833
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles.workspaceBarLogo(), children: logo != null ? logo : name.charAt(0).toUpperCase() }),
834
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: styles.workspaceBarText(), children: [
835
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles.workspaceBarName(), children: name }),
836
+ role && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles.workspaceBarRole(), children: role })
845
837
  ] }),
846
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons2.ChevronDownIcon, { className: "size-4 shrink-0 text-muted-foreground" })
838
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons2.ChevronDownIcon, { className: styles.workspaceBarChevron() })
847
839
  ]
848
840
  }
849
841
  );
@@ -855,48 +847,25 @@ var SidebarV2Workspace = (0, import_react4.forwardRef)(({ name, role, logo, clas
855
847
  type: "button",
856
848
  "data-slot": "sidebar-v2-workspace",
857
849
  "aria-label": role ? `${name} \u2013 ${role}` : name,
858
- className: (0, import_shared_utils2.cn)(
859
- "group/ws relative mx-auto mt-1 flex cursor-pointer items-center justify-center rounded-lg p-1",
860
- "outline-hidden ring-sidebar-ring transition-colors",
861
- "hover:bg-sidebar-accent focus-visible:ring-2",
862
- className
863
- ),
850
+ className: styles.workspaceRail({ className }),
864
851
  ...props,
865
- children: /* @__PURE__ */ (0, import_jsx_runtime4.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: [
852
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: styles.workspaceRailLogo(), children: [
866
853
  logo != null ? logo : name.charAt(0).toUpperCase(),
867
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons2.ChevronDownIcon, { className: "absolute -right-1 -bottom-1 size-3.5 rounded-full bg-sidebar p-px text-sidebar-foreground/60" })
854
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons2.ChevronDownIcon, { className: styles.workspaceRailChevron() })
868
855
  ] })
869
856
  }
870
857
  );
871
858
  });
872
859
  SidebarV2Workspace.displayName = "SidebarV2Workspace";
873
- function railButtonClasses(active) {
874
- return (0, import_shared_utils2.cn)(
875
- "group/rail relative mx-auto flex size-11 shrink-0 cursor-pointer items-center justify-center rounded-xl",
876
- "outline-hidden ring-sidebar-ring transition-colors",
877
- "text-sidebar-foreground/85 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
878
- "focus-visible:ring-2",
879
- active && "bg-sidebar-accent text-sidebar-accent-foreground"
880
- );
881
- }
882
860
  function RailInner({
883
861
  icon: Icon,
884
- active,
885
862
  badge
886
863
  }) {
864
+ const { styles } = useSidebarV2();
887
865
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
888
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
889
- "span",
890
- {
891
- "aria-hidden": true,
892
- className: (0, import_shared_utils2.cn)(
893
- "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",
894
- active ? "opacity-100" : "opacity-0"
895
- )
896
- }
897
- ),
898
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { className: "size-5 shrink-0" }),
899
- badge != null && /* @__PURE__ */ (0, import_jsx_runtime4.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 })
866
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": true, className: styles.railIndicator() }),
867
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { className: styles.railIcon() }),
868
+ badge != null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles.railBadge(), children: badge })
900
869
  ] });
901
870
  }
902
871
  function SidebarV2RailLink({
@@ -905,18 +874,18 @@ function SidebarV2RailLink({
905
874
  href,
906
875
  badge
907
876
  }) {
908
- const { renderLink, isActive, resetPanelSelection } = useSidebarV2();
877
+ const { renderLink, isActive, resetPanelSelection, styles } = useSidebarV2();
909
878
  const active = isActive(href);
910
879
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_tooltip3.Tooltip, { content: label, side: "right", children: renderLink({
911
880
  href,
912
- className: railButtonClasses(active),
881
+ className: styles.railButton(),
913
882
  "data-active": active,
914
883
  "aria-current": active ? "page" : void 0,
915
884
  "aria-label": label,
916
885
  // Navigating to a destination link clears any open panel preview,
917
886
  // even when the route doesn't change (e.g. already on it).
918
887
  onClick: resetPanelSelection,
919
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RailInner, { icon, label, active, badge })
888
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RailInner, { icon, badge })
920
889
  }) });
921
890
  }
922
891
  function SidebarV2RailItem({
@@ -929,6 +898,7 @@ function SidebarV2RailItem({
929
898
  onMouseLeave,
930
899
  badge
931
900
  }) {
901
+ const { styles } = useSidebarV2();
932
902
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_tooltip3.Tooltip, { content: label, side: "right", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
933
903
  "button",
934
904
  {
@@ -939,8 +909,8 @@ function SidebarV2RailItem({
939
909
  onClick,
940
910
  onMouseEnter,
941
911
  onMouseLeave,
942
- className: railButtonClasses(active),
943
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RailInner, { icon, label, active, badge })
912
+ className: styles.railButton(),
913
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RailInner, { icon, badge })
944
914
  }
945
915
  ) });
946
916
  }
@@ -953,54 +923,18 @@ function SidebarV2Panel({
953
923
  children,
954
924
  className
955
925
  }) {
956
- const { togglePin, pinned, tone } = useSidebarV2();
957
- const light = tone === "light";
926
+ const { togglePin, pinned, styles } = useSidebarV2();
958
927
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
959
928
  "div",
960
929
  {
961
930
  "data-slot": "sidebar-v2-panel",
962
- "data-floating": floating,
963
- className: (0, import_shared_utils2.cn)(
964
- "flex h-full w-(--kpx-panel-width) shrink-0 flex-col",
965
- // Surface. The flyout is a distinct floating card (white on light),
966
- // so it reads as an overlay rather than a flat full-height slab.
967
- floating ? light ? "bg-background text-foreground" : "bg-sidebar text-sidebar-foreground" : light ? "bg-muted text-foreground" : "bg-sidebar text-sidebar-foreground",
968
- floating ? (
969
- // Floating card: rounded, soft layered shadow, hairline ring.
970
- (0, import_shared_utils2.cn)(
971
- "overflow-hidden rounded-2xl shadow-2xl shadow-black/20 ring-1",
972
- light ? "ring-black/5" : "ring-white/10"
973
- )
974
- ) : (
975
- // Pinned: light blends into the surround (no border); dark keeps a
976
- // hairline against the rail.
977
- light ? "" : "border-l border-sidebar-border/40"
978
- ),
979
- className
980
- ),
931
+ "data-floating": floating ? "true" : "false",
932
+ className: styles.panel({ className }),
981
933
  children: [
982
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-start gap-2 px-3 pt-3 pb-1.5", children: [
934
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: styles.panelHeader(), children: [
983
935
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "min-w-0 flex-1", children: [
984
- title && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
985
- "div",
986
- {
987
- className: (0, import_shared_utils2.cn)(
988
- "truncate text-sm font-semibold",
989
- light ? "text-foreground" : "text-sidebar-foreground"
990
- ),
991
- children: title
992
- }
993
- ),
994
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
995
- "div",
996
- {
997
- className: (0, import_shared_utils2.cn)(
998
- "truncate text-xs",
999
- light ? "text-muted-foreground" : "text-sidebar-foreground/70"
1000
- ),
1001
- children: subtitle
1002
- }
1003
- )
936
+ title && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles.panelTitle(), children: title }),
937
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles.panelSubtitle(), children: subtitle })
1004
938
  ] }),
1005
939
  action != null ? action : !hidePin && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1006
940
  import_tooltip3.Tooltip,
@@ -1014,41 +948,28 @@ function SidebarV2Panel({
1014
948
  size: "sm",
1015
949
  "aria-label": pinned ? "Panel l\xF6sen" : "Panel anheften",
1016
950
  onClick: togglePin,
1017
- className: (0, import_shared_utils2.cn)(
1018
- "-mr-1 shrink-0",
1019
- light ? "text-muted-foreground hover:bg-foreground/5 hover:text-foreground" : "text-sidebar-foreground/60 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
1020
- ),
951
+ className: styles.panelPin(),
1021
952
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons2.PanelLeftIcon, { className: "size-4" })
1022
953
  }
1023
954
  )
1024
955
  }
1025
956
  )
1026
957
  ] }),
1027
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex min-h-0 flex-1 flex-col gap-0.5 overflow-auto p-2", children })
958
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles.panelBody(), children })
1028
959
  ]
1029
960
  }
1030
961
  );
1031
962
  }
1032
- function panelRowLight(active) {
1033
- return (0, import_shared_utils2.cn)(
1034
- "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",
1035
- active ? "bg-primary/10 font-medium text-primary" : "text-foreground/80 hover:bg-foreground/5"
1036
- );
1037
- }
1038
963
  function SidebarV2PanelLabel({
1039
964
  className,
1040
965
  ...props
1041
966
  }) {
1042
- const { tone } = useSidebarV2();
967
+ const { styles } = useSidebarV2();
1043
968
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1044
969
  "div",
1045
970
  {
1046
971
  "data-slot": "sidebar-v2-panel-label",
1047
- className: (0, import_shared_utils2.cn)(
1048
- "px-2 pt-3 pb-1 text-[0.6875rem] font-medium uppercase tracking-wide",
1049
- tone === "light" ? "text-muted-foreground" : "text-sidebar-foreground/50",
1050
- className
1051
- ),
972
+ className: styles.panelLabel({ className }),
1052
973
  ...props
1053
974
  }
1054
975
  );
@@ -1057,7 +978,7 @@ function SidebarV2PanelLeaf({
1057
978
  item,
1058
979
  active: activeProp
1059
980
  }) {
1060
- const { renderLink, isActive, tone } = useSidebarV2();
981
+ const { renderLink, isActive, tone, styles } = useSidebarV2();
1061
982
  const light = tone === "light";
1062
983
  const Icon = item.icon;
1063
984
  const active = activeProp != null ? activeProp : isActive(item.href);
@@ -1065,51 +986,40 @@ function SidebarV2PanelLeaf({
1065
986
  href: item.href,
1066
987
  "data-active": active,
1067
988
  "aria-current": active ? "page" : void 0,
1068
- className: light ? panelRowLight(active) : (0, import_theme2.sidebarMenuButton)({ size: "md" }),
989
+ // Light tone uses the recipe's leaf slot (group/leaf + data-active);
990
+ // dark tone reuses the existing sidebarMenuButton recipe.
991
+ className: light ? styles.panelLeaf() : (0, import_theme3.sidebarMenuButton)({ size: "md" }),
1069
992
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1070
- Icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1071
- Icon,
1072
- {
1073
- className: (0, import_shared_utils2.cn)(
1074
- light ? active ? void 0 : "text-muted-foreground" : "text-sidebar-foreground/75"
1075
- )
1076
- }
1077
- ),
1078
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
1079
- item.badge != null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1080
- "span",
1081
- {
1082
- className: (0, import_shared_utils2.cn)(
1083
- "ml-auto rounded-full px-1.5 text-[0.625rem] font-medium",
1084
- light ? "bg-foreground/10 text-foreground/70" : "bg-sidebar-accent text-sidebar-accent-foreground"
1085
- ),
1086
- children: item.badge
1087
- }
1088
- )
993
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { className: styles.panelLeafIcon() }),
994
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles.panelLeafLabel(), children: item.label }),
995
+ item.tag && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles.panelLeafTag(), children: item.tag }),
996
+ item.badge != null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles.panelLeafBadge(), children: item.badge })
1089
997
  ] })
1090
998
  }) });
1091
999
  }
1092
1000
  function SidebarV2PanelGroup({
1093
- item
1001
+ item,
1002
+ winningHref
1094
1003
  }) {
1095
1004
  var _a;
1096
- const { openGroup, toggleGroup, activeHref, renderLink, tone } = useSidebarV2();
1005
+ const { openGroup, toggleGroup, activeHref, renderLink, tone, styles } = useSidebarV2();
1097
1006
  const light = tone === "light";
1098
1007
  const Icon = item.icon;
1099
1008
  const key = (_a = item.value) != null ? _a : item.label;
1100
- let bestChildHref = null;
1101
- let bestLen = -1;
1102
- for (const c of item.children) {
1103
- if (activeHref === c.href) {
1104
- bestChildHref = c.href;
1105
- break;
1106
- }
1107
- if (activeHref.startsWith(`${c.href}/`) && c.href.length > bestLen) {
1108
- bestChildHref = c.href;
1109
- bestLen = c.href.length;
1009
+ let winner = winningHref;
1010
+ if (winner === void 0) {
1011
+ const current = normalizePath(activeHref);
1012
+ let len = -1;
1013
+ winner = null;
1014
+ for (const c of item.children) {
1015
+ const l = pathMatchLength(current, c.href);
1016
+ if (l > len) {
1017
+ len = l;
1018
+ winner = normalizePath(c.href);
1019
+ }
1110
1020
  }
1111
1021
  }
1112
- const containsActive = bestChildHref !== null;
1022
+ const containsActive = winner != null && item.children.some((c) => normalizePath(c.href) === winner);
1113
1023
  const open = openGroup === key || openGroup === null && containsActive;
1114
1024
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
1115
1025
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
@@ -1117,71 +1027,76 @@ function SidebarV2PanelGroup({
1117
1027
  {
1118
1028
  type: "button",
1119
1029
  "data-state": open ? "open" : "closed",
1030
+ "data-contains-active": containsActive,
1120
1031
  "aria-expanded": open,
1121
1032
  onClick: () => toggleGroup(key),
1122
- className: (0, import_shared_utils2.cn)(
1123
- light ? panelRowLight(false) : (0, import_theme2.sidebarMenuButton)({ size: "md" }),
1124
- "cursor-pointer",
1125
- containsActive && (light ? "font-medium text-foreground" : "font-medium")
1033
+ className: (0, import_shared_utils.cn)(
1034
+ light ? styles.panelLeaf() : (0, import_theme3.sidebarMenuButton)({ size: "md" }),
1035
+ styles.panelGroupButton()
1126
1036
  ),
1127
1037
  children: [
1128
- Icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1129
- Icon,
1130
- {
1131
- className: light ? "text-muted-foreground" : "text-sidebar-foreground/75"
1132
- }
1133
- ),
1134
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label }),
1135
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1136
- import_icons2.ChevronRightIcon,
1137
- {
1138
- className: (0, import_shared_utils2.cn)(
1139
- "size-3.5! shrink-0 transition-transform",
1140
- light ? "text-muted-foreground" : "text-sidebar-foreground/60",
1141
- open && "rotate-90"
1142
- )
1143
- }
1144
- )
1038
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { className: styles.panelGroupIcon() }),
1039
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles.panelGroupLabel(), children: item.label }),
1040
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons2.ChevronRightIcon, { className: styles.panelGroupChevron() })
1145
1041
  ]
1146
1042
  }
1147
1043
  ),
1148
- open && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1149
- "div",
1150
- {
1151
- className: (0, import_shared_utils2.cn)(
1152
- "my-0.5 ml-[1.05rem] flex flex-col border-l pl-3",
1153
- light ? "border-border" : "border-sidebar-border"
1154
- ),
1155
- children: item.children.map((child) => {
1156
- const active = child.href === bestChildHref;
1157
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: renderLink({
1158
- href: child.href,
1159
- "data-active": active,
1160
- "aria-current": active ? "page" : void 0,
1161
- className: (0, import_shared_utils2.cn)(
1162
- "relative block rounded-md px-2 py-1.5 text-[0.8125rem] outline-hidden transition-colors focus-visible:ring-2",
1163
- "before:absolute before:top-1/2 before:-left-3 before:h-px before:w-2.5",
1164
- 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"
1165
- ),
1166
- children: child.label
1167
- }) }, child.href);
1168
- })
1169
- }
1170
- )
1044
+ open && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles.panelTree(), children: item.children.map((child) => {
1045
+ const active = normalizePath(child.href) === winner;
1046
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: renderLink({
1047
+ href: child.href,
1048
+ "data-active": active,
1049
+ "aria-current": active ? "page" : void 0,
1050
+ className: styles.subItem(),
1051
+ children: child.label
1052
+ }) }, child.href);
1053
+ }) })
1171
1054
  ] });
1172
1055
  }
1173
1056
  function SidebarV2PanelItems({
1174
1057
  items
1175
1058
  }) {
1059
+ const { activeHref } = useSidebarV2();
1060
+ const winningHref = (0, import_react4.useMemo)(() => {
1061
+ const current = normalizePath(activeHref);
1062
+ let href = null;
1063
+ let len = -1;
1064
+ for (const item of items) {
1065
+ if (panelItemIsSection(item)) continue;
1066
+ const hrefs = panelItemHasChildren(item) ? item.children.map((c) => c.href) : [item.href];
1067
+ for (const h of hrefs) {
1068
+ const l = pathMatchLength(current, h);
1069
+ if (l > len) {
1070
+ len = l;
1071
+ href = normalizePath(h);
1072
+ }
1073
+ }
1074
+ }
1075
+ return href;
1076
+ }, [items, activeHref]);
1176
1077
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: items.map((item, idx) => {
1177
1078
  var _a;
1178
1079
  if (panelItemIsSection(item)) {
1179
1080
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SidebarV2PanelLabel, { children: item.section }, `section-${item.section}-${idx}`);
1180
1081
  }
1181
1082
  if (panelItemHasChildren(item)) {
1182
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SidebarV2PanelGroup, { item }, (_a = item.value) != null ? _a : item.label);
1083
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1084
+ SidebarV2PanelGroup,
1085
+ {
1086
+ item,
1087
+ winningHref
1088
+ },
1089
+ (_a = item.value) != null ? _a : item.label
1090
+ );
1183
1091
  }
1184
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SidebarV2PanelLeaf, { item }, item.href);
1092
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1093
+ SidebarV2PanelLeaf,
1094
+ {
1095
+ item,
1096
+ active: winningHref != null && normalizePath(item.href) === winningHref
1097
+ },
1098
+ item.href
1099
+ );
1185
1100
  }) });
1186
1101
  }
1187
1102
  function SidebarV2Trigger({
@@ -1205,7 +1120,7 @@ function SidebarV2Trigger({
1205
1120
 
1206
1121
  // src/v2/from-config.tsx
1207
1122
  var import_drawer2 = require("@kopexa/drawer");
1208
- var import_shared_utils3 = require("@kopexa/shared-utils");
1123
+ var import_shared_utils2 = require("@kopexa/shared-utils");
1209
1124
  var import_react5 = require("motion/react");
1210
1125
  var import_react6 = require("react");
1211
1126
  var import_jsx_runtime5 = require("react/jsx-runtime");
@@ -1234,13 +1149,14 @@ function SidebarV2FromConfig({
1234
1149
  closeFlyout,
1235
1150
  selectedRail,
1236
1151
  setSelectedRail,
1237
- isActive,
1152
+ activeHref,
1238
1153
  isMobile,
1239
1154
  drawerOpen,
1240
1155
  setDrawerOpen,
1241
1156
  setPanelHost,
1242
1157
  panelOverrideActive,
1243
- navPreviewActive
1158
+ navPreviewActive,
1159
+ styles
1244
1160
  } = useSidebarV2();
1245
1161
  const hoverMode = flyoutTrigger === "hover" && !pinned && !isMobile;
1246
1162
  const closeTimer = (0, import_react6.useRef)(null);
@@ -1279,14 +1195,21 @@ function SidebarV2FromConfig({
1279
1195
  [items]
1280
1196
  );
1281
1197
  const activeRailValue = (0, import_react6.useMemo)(() => {
1282
- const match = panels.find(
1283
- (p) => p.items.some((item) => {
1284
- if (panelItemIsSection(item)) return false;
1285
- return panelItemHasChildren(item) ? item.children.some((c) => isActive(c.href)) : isActive(item.href);
1286
- })
1287
- );
1288
- return match ? entryValue(match) : null;
1289
- }, [panels, isActive]);
1198
+ var _a2, _b2;
1199
+ const current = normalizePath(activeHref);
1200
+ let best = null;
1201
+ for (const p of panels) {
1202
+ for (const item of p.items) {
1203
+ if (panelItemIsSection(item)) continue;
1204
+ const hrefs = panelItemHasChildren(item) ? item.children.map((c) => c.href) : [item.href];
1205
+ for (const href of hrefs) {
1206
+ const len = pathMatchLength(current, href);
1207
+ if (len > ((_a2 = best == null ? void 0 : best.len) != null ? _a2 : -1)) best = { value: entryValue(p), len };
1208
+ }
1209
+ }
1210
+ }
1211
+ return (_b2 = best == null ? void 0 : best.value) != null ? _b2 : null;
1212
+ }, [panels, activeHref]);
1290
1213
  const shownValue = pinned ? selectedRail != null ? selectedRail : activeRailValue : flyoutValue;
1291
1214
  const shownPanel = (_a = panels.find((p) => entryValue(p) === shownValue)) != null ? _a : null;
1292
1215
  const topEntries = items.filter((e) => e.slot !== "bottom");
@@ -1297,7 +1220,7 @@ function SidebarV2FromConfig({
1297
1220
  "div",
1298
1221
  {
1299
1222
  "aria-hidden": true,
1300
- className: "mx-3 my-1.5 h-px shrink-0 bg-sidebar-border/70"
1223
+ className: styles.railDivider()
1301
1224
  },
1302
1225
  `rail-divider-${index}`
1303
1226
  );
@@ -1349,14 +1272,14 @@ function SidebarV2FromConfig({
1349
1272
  );
1350
1273
  }
1351
1274
  const railContent = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1352
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: [
1275
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: styles.railScroll(), children: [
1353
1276
  header && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1354
1277
  header,
1355
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mx-3 my-1 h-px shrink-0 bg-sidebar-border/60" })
1278
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: styles.railHeaderDivider() })
1356
1279
  ] }),
1357
1280
  topEntries.map(renderRailEntry)
1358
1281
  ] }),
1359
- bottomEntries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex shrink-0 flex-col gap-1 pt-1", children: bottomEntries.map(renderRailEntry) })
1282
+ bottomEntries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: styles.railBottom(), children: bottomEntries.map(renderRailEntry) })
1360
1283
  ] });
1361
1284
  const navData = pinned && shownPanel && (navPreviewActive || !panelOverrideActive) ? shownPanel : null;
1362
1285
  const [bufferedPanel, setBufferedPanel] = (0, import_react6.useState)(navData);
@@ -1388,7 +1311,7 @@ function SidebarV2FromConfig({
1388
1311
  open: drawerOpen,
1389
1312
  onOpenChange: setDrawerOpen,
1390
1313
  placement: "left",
1391
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_drawer2.Drawer.Content, { className: "w-auto max-w-[85vw] border-0 bg-sidebar p-0 [&>button]:hidden", children: [
1314
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_drawer2.Drawer.Content, { className: styles.mobileDrawer(), children: [
1392
1315
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_drawer2.Drawer.Header, { className: "sr-only", children: [
1393
1316
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_drawer2.Drawer.Title, { children: "Navigation" }),
1394
1317
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_drawer2.Drawer.Description, { children: "Hauptnavigation" })
@@ -1416,10 +1339,7 @@ function SidebarV2FromConfig({
1416
1339
  {
1417
1340
  ref: setPanelHost,
1418
1341
  "data-slot": "sidebar-v2-panel-zone",
1419
- className: (0, import_shared_utils3.cn)(
1420
- "relative shrink-0 transition-[width] duration-200 ease-out motion-reduce:transition-none",
1421
- pinned && "overflow-hidden"
1422
- ),
1342
+ className: (0, import_shared_utils2.cn)(styles.panelZone(), pinned && "overflow-hidden"),
1423
1343
  style: { gridArea: "panel", width: docked ? PANEL_WIDTH2 : "0px" },
1424
1344
  children: [
1425
1345
  panelContent,
@@ -1427,7 +1347,7 @@ function SidebarV2FromConfig({
1427
1347
  import_react5.motion.div,
1428
1348
  {
1429
1349
  "data-floating": "true",
1430
- className: "absolute inset-y-2 left-1 z-30",
1350
+ className: styles.flyout(),
1431
1351
  initial: { x: -14, opacity: 0 },
1432
1352
  animate: { x: 0, opacity: 1 },
1433
1353
  exit: { x: -14, opacity: 0 },