@pactor-app/ui 1.1.0 → 1.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.
@@ -1,6 +1,8 @@
1
1
  import {
2
- Icon
3
- } from "./chunk-6JNCDPGS.js";
2
+ Icon,
3
+ Menu,
4
+ Sidebar
5
+ } from "./chunk-EVGIS2ZE.js";
4
6
  import {
5
7
  Accordion,
6
8
  AccordionContent,
@@ -13,10 +15,10 @@ import {
13
15
  Popover,
14
16
  PopoverContent,
15
17
  PopoverTrigger
16
- } from "./chunk-GL7IO22L.js";
18
+ } from "./chunk-L4Z7MNYY.js";
17
19
  import {
18
20
  cn
19
- } from "./chunk-RQHJBTEU.js";
21
+ } from "./chunk-WKJUCGV4.js";
20
22
 
21
23
  // src/layout/admin/menu-chain.ts
22
24
  function findMenuChain(registry, menuId) {
@@ -62,7 +64,7 @@ function collectDefaultOpenKeys(registry, activeMenuId) {
62
64
  return keys;
63
65
  }
64
66
 
65
- // src/layout/admin/ShadcnAdminLayout.tsx
67
+ // src/layout/admin/AdminLayout.tsx
66
68
  import { filterMenusByPermission } from "@pactor-app/permission";
67
69
  import {
68
70
  resolveRoute
@@ -103,7 +105,7 @@ function useBreakpointBelow(breakpoint) {
103
105
  return breakpoint !== void 0 && matches;
104
106
  }
105
107
 
106
- // src/layout/admin/ShadcnAdminLayout.tsx
108
+ // src/layout/admin/AdminLayout.tsx
107
109
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
108
110
  function buildVisibleMenuTree(registry, permissionChecker) {
109
111
  const tree = registry.tree();
@@ -119,7 +121,7 @@ function navigateMenuItem(menu, bridge) {
119
121
  }
120
122
  bridge.push(menu.path);
121
123
  }
122
- var menuItemClass = "flex w-full cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-left text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50";
124
+ var menuItemClass = "flex w-full cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-left text-sm text-muted-foreground transition-colors hover:bg-(--menu-hover-background) hover:text-(--menu-hover-foreground) disabled:pointer-events-none disabled:opacity-50";
123
125
  function CollapsedGroupFlyout({
124
126
  menu,
125
127
  registry,
@@ -225,7 +227,7 @@ function MenuTree({
225
227
  className: cn(
226
228
  menuItemClass,
227
229
  "mx-auto size-10 justify-center px-0 font-semibold",
228
- menu.id === activeMenuId && "bg-accent font-semibold text-accent-foreground"
230
+ menu.id === activeMenuId && "bg-(--menu-active-background) font-semibold text-(--menu-active-foreground)"
229
231
  ),
230
232
  disabled: menu.disabled,
231
233
  title: menu.title,
@@ -288,7 +290,7 @@ function MenuTree({
288
290
  "data-active": menu.id === activeMenuId || void 0,
289
291
  className: cn(
290
292
  menuItemClass,
291
- menu.id === activeMenuId && "bg-accent font-semibold text-accent-foreground"
293
+ menu.id === activeMenuId && "bg-(--menu-active-background) font-semibold text-(--menu-active-foreground)"
292
294
  ),
293
295
  disabled: menu.disabled,
294
296
  title: menu.title,
@@ -304,7 +306,7 @@ function MenuTree({
304
306
  }
305
307
  );
306
308
  }
307
- function ShadcnAdminLayout({
309
+ function AdminLayout({
308
310
  config = {},
309
311
  brand,
310
312
  registry,
@@ -394,7 +396,7 @@ function ShadcnAdminLayout({
394
396
  {
395
397
  "data-slot": "admin-header",
396
398
  className: cn(
397
- "flex h-14 items-center gap-4 border-b border-border bg-background px-6",
399
+ "flex h-14 items-center gap-4 border-b border-border bg-card px-6",
398
400
  fixedHeader && "sticky top-0 z-20"
399
401
  ),
400
402
  children: [
@@ -421,7 +423,7 @@ function ShadcnAdminLayout({
421
423
  className: cn(
422
424
  menuItemClass,
423
425
  "size-10 justify-center px-0 font-semibold",
424
- (menu.id === secondaryTopId || menu.id === activeTopId) && "bg-accent text-accent-foreground"
426
+ (menu.id === secondaryTopId || menu.id === activeTopId) && "bg-(--menu-active-background) text-(--menu-active-foreground)"
425
427
  ),
426
428
  disabled: menu.disabled,
427
429
  title: menu.title,
@@ -445,7 +447,7 @@ function ShadcnAdminLayout({
445
447
  "data-slot": "admin-sidebar",
446
448
  "data-collapsed": effectiveCollapsed || void 0,
447
449
  className: cn(
448
- "shrink-0 overflow-auto border-r border-border bg-background py-2",
450
+ "shrink-0 overflow-auto border-r border-border bg-sidebar py-2",
449
451
  effectiveCollapsed ? "w-16 px-1.5" : "w-52",
450
452
  fixedSider && (mode === "side-full" ? "sticky top-0 h-screen" : "sticky top-14 h-[calc(100vh-3.5rem)]")
451
453
  ),
@@ -492,7 +494,7 @@ function ShadcnAdminLayout({
492
494
  "data-slot": "admin-collapse-trigger",
493
495
  "aria-label": effectiveCollapsed ? t("pactor.layout.expandSidebar") : t("pactor.layout.collapseSidebar"),
494
496
  "aria-expanded": !effectiveCollapsed,
495
- className: "sticky bottom-0 flex w-full cursor-pointer items-center justify-center border-t border-border bg-background py-2 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
497
+ className: "sticky bottom-0 flex w-full cursor-pointer items-center justify-center border-t border-border bg-sidebar py-2 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
496
498
  onClick: () => setCollapsed((value) => !value),
497
499
  children: effectiveCollapsed ? "\xBB" : "\xAB"
498
500
  }
@@ -506,7 +508,7 @@ function ShadcnAdminLayout({
506
508
  {
507
509
  "data-slot": "admin-tabs",
508
510
  role: "tablist",
509
- className: "flex items-center gap-1 border-b border-border bg-background px-4 pt-2",
511
+ className: "flex items-center gap-1 border-b border-border bg-card px-4 pt-2",
510
512
  children: tabs.map((tab, index) => /* @__PURE__ */ jsxs(
511
513
  "div",
512
514
  {
@@ -515,7 +517,7 @@ function ShadcnAdminLayout({
515
517
  role: "presentation",
516
518
  className: cn(
517
519
  "inline-flex items-center gap-1 rounded-t-md border border-b-0 border-border px-3 py-1.5 text-sm",
518
- tab.key === currentPath ? "bg-background font-medium text-foreground" : "bg-muted text-muted-foreground"
520
+ tab.key === currentPath ? "bg-card font-medium text-foreground" : "bg-muted text-muted-foreground"
519
521
  ),
520
522
  children: [
521
523
  /* @__PURE__ */ jsx(
@@ -611,9 +613,9 @@ function ShadcnAdminLayout({
611
613
  );
612
614
  }
613
615
 
614
- // src/layout/basic/ShadcnBlankLayout.tsx
616
+ // src/layout/basic/BlankLayout.tsx
615
617
  import { jsx as jsx2 } from "react/jsx-runtime";
616
- function ShadcnBlankLayout({ children }) {
618
+ function BlankLayout({ children }) {
617
619
  return /* @__PURE__ */ jsx2(
618
620
  "div",
619
621
  {
@@ -624,17 +626,144 @@ function ShadcnBlankLayout({ children }) {
624
626
  );
625
627
  }
626
628
 
627
- // src/layout/basic/ShadcnLoginLayout.tsx
628
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
629
- function ShadcnLoginLayout({
629
+ // src/layout/chat/ChatLayout.tsx
630
+ import { filterMenusByPermission as filterMenusByPermission2 } from "@pactor-app/permission";
631
+ import { useEffect as useEffect3, useMemo as useMemo2, useState as useState3 } from "react";
632
+ import { jsx as jsx3 } from "react/jsx-runtime";
633
+ function ChatLayout({
634
+ brand,
635
+ menu,
636
+ registry,
637
+ bridge,
638
+ route: routeProp,
639
+ permissionChecker,
640
+ activeMenuKey,
641
+ onMenuSelect,
642
+ collapsible,
643
+ sidebar,
644
+ sidebarFooter,
645
+ children
646
+ }) {
647
+ const registryMenus = useMemo2(() => {
648
+ if (menu !== void 0 || registry === void 0) {
649
+ return [];
650
+ }
651
+ const tree = registry.tree();
652
+ const visible = permissionChecker !== void 0 ? filterMenusByPermission2(tree, permissionChecker) : tree;
653
+ return visible.flatMap((item) => menuItemToMenuData(item));
654
+ }, [menu, registry, permissionChecker]);
655
+ const menuItems = menu ?? registryMenus;
656
+ const [bridgePath, setBridgePath] = useState3(
657
+ () => bridge?.getLocation().pathname ?? "/"
658
+ );
659
+ useEffect3(() => {
660
+ if (bridge === void 0) {
661
+ return;
662
+ }
663
+ return bridge.subscribe((location) => {
664
+ setBridgePath(location.pathname);
665
+ });
666
+ }, [bridge]);
667
+ const activeKey = useMemo2(() => {
668
+ if (activeMenuKey !== void 0) {
669
+ return activeMenuKey;
670
+ }
671
+ const path = routeProp?.path ?? bridgePath;
672
+ return menuItems.find((item) => item.path === path)?.key;
673
+ }, [activeMenuKey, routeProp, bridgePath, menuItems]);
674
+ const handleSelect = (key) => {
675
+ const item = menuItems.find((m) => m.key === key);
676
+ if (item === void 0 || item.disabled === true) {
677
+ return;
678
+ }
679
+ if (onMenuSelect !== void 0) {
680
+ onMenuSelect(key, item);
681
+ return;
682
+ }
683
+ if (bridge !== void 0 && item.path !== void 0) {
684
+ bridge.push(item.path);
685
+ }
686
+ };
687
+ const sidebarContent = sidebar !== void 0 && sidebar !== null ? sidebar : menuItems.length > 0 ? /* @__PURE__ */ jsx3(
688
+ Menu,
689
+ {
690
+ items: menuItems.map((item) => chatMenuItemToUiMenuItem(item)),
691
+ selectedKey: activeKey,
692
+ onSelect: handleSelect
693
+ }
694
+ ) : void 0;
695
+ return /* @__PURE__ */ jsx3(
696
+ Sidebar,
697
+ {
698
+ brand,
699
+ content: sidebarContent,
700
+ sidebarFooter,
701
+ collapsible,
702
+ locationKey: routeProp?.path ?? bridgePath,
703
+ children: /* @__PURE__ */ jsx3(
704
+ "div",
705
+ {
706
+ "data-slot": "chat-layout-content",
707
+ className: "flex min-h-0 flex-1 flex-col",
708
+ children
709
+ }
710
+ )
711
+ }
712
+ );
713
+ }
714
+ function chatMenuItemToUiMenuItem(item) {
715
+ return {
716
+ key: item.key,
717
+ label: item.label,
718
+ icon: item.icon,
719
+ badge: item.badge,
720
+ dot: item.dot,
721
+ group: item.group,
722
+ disabled: item.disabled
723
+ };
724
+ }
725
+ function menuItemToMenuData(item) {
726
+ if (item.type === "group") {
727
+ const children = (item.children ?? []).flatMap(
728
+ (child) => menuItemToMenuData(child)
729
+ );
730
+ if (children.length === 0) {
731
+ return [];
732
+ }
733
+ return children.map((child, index) => ({
734
+ ...child,
735
+ group: index === 0 ? item.title : void 0
736
+ }));
737
+ }
738
+ if (item.hidden === true) {
739
+ return [];
740
+ }
741
+ const ext = item;
742
+ return [
743
+ {
744
+ key: item.id,
745
+ label: item.title,
746
+ icon: item.icon,
747
+ path: item.path,
748
+ badge: item.badge,
749
+ dot: ext.dot,
750
+ group: ext.group,
751
+ disabled: item.disabled
752
+ }
753
+ ];
754
+ }
755
+
756
+ // src/layout/basic/LoginLayout.tsx
757
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
758
+ function LoginLayout({
630
759
  variant = "center",
631
760
  brand,
632
761
  children
633
762
  }) {
634
763
  const brandBlock = /* @__PURE__ */ jsxs2("div", { "data-slot": "login-brand", className: "flex flex-col items-center gap-2", children: [
635
764
  brand.logo,
636
- /* @__PURE__ */ jsx3("h1", { className: "text-xl font-semibold", children: brand.title }),
637
- brand.description !== void 0 && /* @__PURE__ */ jsx3("div", { className: "text-sm text-muted-foreground", children: brand.description })
765
+ /* @__PURE__ */ jsx4("h1", { className: "text-xl font-semibold", children: brand.title }),
766
+ brand.description !== void 0 && /* @__PURE__ */ jsx4("div", { className: "text-sm text-muted-foreground", children: brand.description })
638
767
  ] });
639
768
  if (variant === "split") {
640
769
  return /* @__PURE__ */ jsxs2(
@@ -644,7 +773,7 @@ function ShadcnLoginLayout({
644
773
  "data-variant": "split",
645
774
  className: "grid min-h-screen grid-cols-2 bg-background text-foreground",
646
775
  children: [
647
- /* @__PURE__ */ jsx3(
776
+ /* @__PURE__ */ jsx4(
648
777
  "div",
649
778
  {
650
779
  "data-slot": "login-brand-panel",
@@ -652,7 +781,7 @@ function ShadcnLoginLayout({
652
781
  children: brandBlock
653
782
  }
654
783
  ),
655
- /* @__PURE__ */ jsx3(
784
+ /* @__PURE__ */ jsx4(
656
785
  "div",
657
786
  {
658
787
  "data-slot": "login-form-panel",
@@ -664,7 +793,7 @@ function ShadcnLoginLayout({
664
793
  }
665
794
  );
666
795
  }
667
- return /* @__PURE__ */ jsx3(
796
+ return /* @__PURE__ */ jsx4(
668
797
  "div",
669
798
  {
670
799
  "data-slot": "login-layout",
@@ -672,18 +801,18 @@ function ShadcnLoginLayout({
672
801
  className: "flex min-h-screen items-center justify-center bg-muted/40 text-foreground",
673
802
  children: /* @__PURE__ */ jsxs2(Card, { "data-slot": "login-card", className: "w-full max-w-sm gap-4 py-6", children: [
674
803
  /* @__PURE__ */ jsxs2(CardHeader, { className: "px-6", children: [
675
- /* @__PURE__ */ jsx3(CardTitle, { className: "sr-only", children: brand.title }),
804
+ /* @__PURE__ */ jsx4(CardTitle, { className: "sr-only", children: brand.title }),
676
805
  brandBlock
677
806
  ] }),
678
- /* @__PURE__ */ jsx3(CardContent, { className: "flex flex-col gap-4 px-6", children })
807
+ /* @__PURE__ */ jsx4(CardContent, { className: "flex flex-col gap-4 px-6", children })
679
808
  ] })
680
809
  }
681
810
  );
682
811
  }
683
812
 
684
- // src/layout/basic/ShadcnLandingLayout.tsx
685
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
686
- function ShadcnLandingLayout({
813
+ // src/layout/basic/LandingLayout.tsx
814
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
815
+ function LandingLayout({
687
816
  navbar,
688
817
  footer,
689
818
  children
@@ -694,16 +823,16 @@ function ShadcnLandingLayout({
694
823
  "data-slot": "landing-layout",
695
824
  className: "flex min-h-screen flex-col bg-background text-foreground",
696
825
  children: [
697
- navbar !== void 0 && /* @__PURE__ */ jsx4(
826
+ navbar !== void 0 && /* @__PURE__ */ jsx5(
698
827
  "header",
699
828
  {
700
829
  "data-slot": "landing-navbar",
701
- className: "border-b border-border bg-background",
830
+ className: "border-b border-border bg-card",
702
831
  children: navbar
703
832
  }
704
833
  ),
705
- /* @__PURE__ */ jsx4("main", { "data-slot": "landing-content", className: "flex-1", children }),
706
- footer !== void 0 && /* @__PURE__ */ jsx4(
834
+ /* @__PURE__ */ jsx5("main", { "data-slot": "landing-content", className: "flex-1", children }),
835
+ footer !== void 0 && /* @__PURE__ */ jsx5(
707
836
  "footer",
708
837
  {
709
838
  "data-slot": "landing-footer",
@@ -718,15 +847,16 @@ function ShadcnLandingLayout({
718
847
 
719
848
  // src/layout/registry.ts
720
849
  import { Registry } from "@pactor-app/core";
721
- function registerShadcnLayouts(registry) {
722
- registry.register("admin", ShadcnAdminLayout);
723
- registry.register("blank", ShadcnBlankLayout);
724
- registry.register("login", ShadcnLoginLayout);
725
- registry.register("landing", ShadcnLandingLayout);
850
+ function registerBuiltinLayouts(registry) {
851
+ registry.register("admin", AdminLayout);
852
+ registry.register("blank", BlankLayout);
853
+ registry.register("login", LoginLayout);
854
+ registry.register("landing", LandingLayout);
855
+ registry.register("chat", ChatLayout);
726
856
  }
727
- function createShadcnLayoutRegistry() {
857
+ function createLayoutRegistry() {
728
858
  const registry = new Registry("LayoutRegistry");
729
- registerShadcnLayouts(registry);
859
+ registerBuiltinLayouts(registry);
730
860
  return registry;
731
861
  }
732
862
  function resolveLayout(registry, type) {
@@ -735,7 +865,7 @@ function resolveLayout(registry, type) {
735
865
  console.warn(
736
866
  `[pactor-layout] unknown layout type "${type}", fallback to BlankLayout`
737
867
  );
738
- return ShadcnBlankLayout;
868
+ return BlankLayout;
739
869
  }
740
870
  return component;
741
871
  }
@@ -743,11 +873,12 @@ function resolveLayout(registry, type) {
743
873
  export {
744
874
  findMenuChain,
745
875
  collectDefaultOpenKeys,
746
- ShadcnAdminLayout,
747
- ShadcnBlankLayout,
748
- ShadcnLoginLayout,
749
- ShadcnLandingLayout,
750
- registerShadcnLayouts,
751
- createShadcnLayoutRegistry,
876
+ AdminLayout,
877
+ BlankLayout,
878
+ ChatLayout,
879
+ LoginLayout,
880
+ LandingLayout,
881
+ registerBuiltinLayouts,
882
+ createLayoutRegistry,
752
883
  resolveLayout
753
884
  };