@pactor-app/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/dist/chunk-57NRUZ5G.js +95 -0
- package/dist/chunk-DMK6RSTK.js +746 -0
- package/dist/chunk-OH3U6PCM.js +536 -0
- package/dist/chunk-OUTSIJMO.js +442 -0
- package/dist/chunk-QDTVOJ5P.js +147 -0
- package/dist/chunk-QWZ23QLS.js +68 -0
- package/dist/chunk-RQHJBTEU.js +10 -0
- package/dist/chunk-SNFDI77B.js +136 -0
- package/dist/chunk-XGOTHOI6.js +619 -0
- package/dist/components/index.cjs +1111 -0
- package/dist/components/index.d.cts +307 -0
- package/dist/components/index.d.ts +307 -0
- package/dist/components/index.js +40 -0
- package/dist/index.cjs +2626 -0
- package/dist/index.d.cts +41 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +87 -0
- package/dist/interaction/index.cjs +752 -0
- package/dist/interaction/index.d.cts +178 -0
- package/dist/interaction/index.d.ts +178 -0
- package/dist/interaction/index.js +19 -0
- package/dist/layout/index.cjs +938 -0
- package/dist/layout/index.d.cts +158 -0
- package/dist/layout/index.d.ts +158 -0
- package/dist/layout/index.js +25 -0
- package/dist/ui/index.cjs +1076 -0
- package/dist/ui/index.d.cts +166 -0
- package/dist/ui/index.d.ts +166 -0
- package/dist/ui/index.js +233 -0
- package/package.json +127 -0
- package/styles.css +1672 -0
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Accordion,
|
|
3
|
+
AccordionContent,
|
|
4
|
+
AccordionItem,
|
|
5
|
+
AccordionTrigger,
|
|
6
|
+
Popover,
|
|
7
|
+
PopoverContent,
|
|
8
|
+
PopoverTrigger
|
|
9
|
+
} from "./chunk-SNFDI77B.js";
|
|
10
|
+
import {
|
|
11
|
+
Card,
|
|
12
|
+
CardContent,
|
|
13
|
+
CardHeader,
|
|
14
|
+
CardTitle
|
|
15
|
+
} from "./chunk-57NRUZ5G.js";
|
|
16
|
+
import {
|
|
17
|
+
cn
|
|
18
|
+
} from "./chunk-RQHJBTEU.js";
|
|
19
|
+
|
|
20
|
+
// src/layout/admin/menu-chain.ts
|
|
21
|
+
function findMenuChain(registry, menuId) {
|
|
22
|
+
const walk = (menus, ancestors) => {
|
|
23
|
+
for (const menu of menus) {
|
|
24
|
+
const chain = [...ancestors, menu];
|
|
25
|
+
if (menu.id === menuId) {
|
|
26
|
+
return chain;
|
|
27
|
+
}
|
|
28
|
+
if (menu.children !== void 0) {
|
|
29
|
+
const hit = walk(menu.children, chain);
|
|
30
|
+
if (hit !== null) {
|
|
31
|
+
return hit;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
return walk(registry.tree(), []) ?? [];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/layout/admin/menu-items.ts
|
|
41
|
+
function collectDefaultOpenKeys(registry, activeMenuId) {
|
|
42
|
+
const keys = [];
|
|
43
|
+
const walk = (menus) => {
|
|
44
|
+
for (const menu of menus) {
|
|
45
|
+
if (menu.type === "group" && menu.defaultOpen === true) {
|
|
46
|
+
keys.push(menu.id);
|
|
47
|
+
}
|
|
48
|
+
if (menu.children !== void 0) {
|
|
49
|
+
walk(menu.children);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
walk(registry.tree());
|
|
54
|
+
if (activeMenuId !== void 0) {
|
|
55
|
+
for (const menu of findMenuChain(registry, activeMenuId)) {
|
|
56
|
+
if (menu.type === "group" && !keys.includes(menu.id)) {
|
|
57
|
+
keys.push(menu.id);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return keys;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/layout/admin/ShadcnAdminLayout.tsx
|
|
65
|
+
import { filterMenusByPermission } from "@pactor-app/permission";
|
|
66
|
+
import {
|
|
67
|
+
resolveRoute
|
|
68
|
+
} from "@pactor-app/router";
|
|
69
|
+
import { useI18n } from "@pactor-app/runtime";
|
|
70
|
+
import { useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
|
|
71
|
+
|
|
72
|
+
// src/layout/admin/use-media-query.ts
|
|
73
|
+
import { useEffect, useState } from "react";
|
|
74
|
+
var BREAKPOINT_PX = {
|
|
75
|
+
xs: 480,
|
|
76
|
+
sm: 576,
|
|
77
|
+
md: 768,
|
|
78
|
+
lg: 992,
|
|
79
|
+
xl: 1200,
|
|
80
|
+
xxl: 1600
|
|
81
|
+
};
|
|
82
|
+
function useMediaQuery(query) {
|
|
83
|
+
const [matches, setMatches] = useState(
|
|
84
|
+
() => typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(query).matches : false
|
|
85
|
+
);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const mql = window.matchMedia(query);
|
|
91
|
+
const onChange = (event) => setMatches(event.matches);
|
|
92
|
+
setMatches(mql.matches);
|
|
93
|
+
mql.addEventListener("change", onChange);
|
|
94
|
+
return () => mql.removeEventListener("change", onChange);
|
|
95
|
+
}, [query]);
|
|
96
|
+
return matches;
|
|
97
|
+
}
|
|
98
|
+
function useBreakpointBelow(breakpoint) {
|
|
99
|
+
const matches = useMediaQuery(
|
|
100
|
+
breakpoint ? `(max-width: ${BREAKPOINT_PX[breakpoint] - 1}px)` : "(min-width: 0px)"
|
|
101
|
+
);
|
|
102
|
+
return breakpoint !== void 0 && matches;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/layout/admin/ShadcnAdminLayout.tsx
|
|
106
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
107
|
+
function buildVisibleMenuTree(registry, permissionChecker) {
|
|
108
|
+
const tree = registry.tree();
|
|
109
|
+
return permissionChecker === void 0 ? [...tree] : filterMenusByPermission(tree, permissionChecker);
|
|
110
|
+
}
|
|
111
|
+
function navigateMenuItem(menu, bridge) {
|
|
112
|
+
if (menu === void 0 || menu.type === "group" || menu.path === void 0) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (menu.external === true || menu.type === "link") {
|
|
116
|
+
window.open(menu.path, menu.target ?? "_blank");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
bridge.push(menu.path);
|
|
120
|
+
}
|
|
121
|
+
var menuItemClass = "flex w-full cursor-pointer items-center 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";
|
|
122
|
+
function CollapsedGroupFlyout({
|
|
123
|
+
menu,
|
|
124
|
+
registry,
|
|
125
|
+
bridge,
|
|
126
|
+
activeMenuId,
|
|
127
|
+
defaultOpenKeys
|
|
128
|
+
}) {
|
|
129
|
+
const [open, setOpen] = useState2(false);
|
|
130
|
+
const closeTimer = useRef(void 0);
|
|
131
|
+
const openNow = () => {
|
|
132
|
+
window.clearTimeout(closeTimer.current);
|
|
133
|
+
setOpen(true);
|
|
134
|
+
};
|
|
135
|
+
const closeLater = () => {
|
|
136
|
+
window.clearTimeout(closeTimer.current);
|
|
137
|
+
closeTimer.current = window.setTimeout(() => setOpen(false), 200);
|
|
138
|
+
};
|
|
139
|
+
useEffect2(() => () => window.clearTimeout(closeTimer.current), []);
|
|
140
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
141
|
+
/* @__PURE__ */ jsx(
|
|
142
|
+
PopoverTrigger,
|
|
143
|
+
{
|
|
144
|
+
render: /* @__PURE__ */ jsx(
|
|
145
|
+
"button",
|
|
146
|
+
{
|
|
147
|
+
type: "button",
|
|
148
|
+
"data-slot": "menu-item",
|
|
149
|
+
className: cn(
|
|
150
|
+
menuItemClass,
|
|
151
|
+
"mx-auto size-10 justify-center px-0 font-semibold"
|
|
152
|
+
),
|
|
153
|
+
disabled: menu.disabled,
|
|
154
|
+
title: menu.title,
|
|
155
|
+
onMouseEnter: openNow,
|
|
156
|
+
onMouseLeave: closeLater,
|
|
157
|
+
children: menu.title.slice(0, 1)
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
),
|
|
162
|
+
/* @__PURE__ */ jsxs(
|
|
163
|
+
PopoverContent,
|
|
164
|
+
{
|
|
165
|
+
"data-slot": "menu-flyout",
|
|
166
|
+
side: "right",
|
|
167
|
+
align: "start",
|
|
168
|
+
sideOffset: 8,
|
|
169
|
+
className: "max-h-[70vh] w-40 overflow-auto p-1",
|
|
170
|
+
initialFocus: false,
|
|
171
|
+
onMouseEnter: openNow,
|
|
172
|
+
onMouseLeave: closeLater,
|
|
173
|
+
onClick: (event) => {
|
|
174
|
+
if (event.target.closest("[data-slot=menu-item]")) {
|
|
175
|
+
setOpen(false);
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
children: [
|
|
179
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 pt-2 pb-1 text-xs font-semibold text-muted-foreground", children: menu.title }),
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
MenuTree,
|
|
182
|
+
{
|
|
183
|
+
menus: menu.children ?? [],
|
|
184
|
+
registry,
|
|
185
|
+
bridge,
|
|
186
|
+
activeMenuId,
|
|
187
|
+
defaultOpenKeys
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
] });
|
|
194
|
+
}
|
|
195
|
+
function MenuTree({
|
|
196
|
+
menus,
|
|
197
|
+
registry,
|
|
198
|
+
bridge,
|
|
199
|
+
activeMenuId,
|
|
200
|
+
defaultOpenKeys,
|
|
201
|
+
collapsed = false
|
|
202
|
+
}) {
|
|
203
|
+
if (collapsed) {
|
|
204
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "menu", "data-collapsed": "true", className: "flex flex-col gap-1", children: menus.map((menu) => {
|
|
205
|
+
if (menu.type === "group") {
|
|
206
|
+
return /* @__PURE__ */ jsx(
|
|
207
|
+
CollapsedGroupFlyout,
|
|
208
|
+
{
|
|
209
|
+
menu,
|
|
210
|
+
registry,
|
|
211
|
+
bridge,
|
|
212
|
+
activeMenuId,
|
|
213
|
+
defaultOpenKeys
|
|
214
|
+
},
|
|
215
|
+
menu.id
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return /* @__PURE__ */ jsx(
|
|
219
|
+
"button",
|
|
220
|
+
{
|
|
221
|
+
type: "button",
|
|
222
|
+
"data-slot": "menu-item",
|
|
223
|
+
"data-active": menu.id === activeMenuId || void 0,
|
|
224
|
+
className: cn(
|
|
225
|
+
menuItemClass,
|
|
226
|
+
"mx-auto size-10 justify-center px-0 font-semibold",
|
|
227
|
+
menu.id === activeMenuId && "bg-accent font-semibold text-accent-foreground"
|
|
228
|
+
),
|
|
229
|
+
disabled: menu.disabled,
|
|
230
|
+
title: menu.title,
|
|
231
|
+
onClick: () => navigateMenuItem(registry.findById(menu.id), bridge),
|
|
232
|
+
children: menu.title.slice(0, 1)
|
|
233
|
+
},
|
|
234
|
+
menu.id
|
|
235
|
+
);
|
|
236
|
+
}) });
|
|
237
|
+
}
|
|
238
|
+
return /* @__PURE__ */ jsx(
|
|
239
|
+
Accordion,
|
|
240
|
+
{
|
|
241
|
+
type: "multiple",
|
|
242
|
+
defaultValue: defaultOpenKeys,
|
|
243
|
+
"data-slot": "menu",
|
|
244
|
+
className: "flex flex-col gap-0.5 px-2",
|
|
245
|
+
children: menus.map((menu) => {
|
|
246
|
+
if (menu.type === "group") {
|
|
247
|
+
return /* @__PURE__ */ jsxs(
|
|
248
|
+
AccordionItem,
|
|
249
|
+
{
|
|
250
|
+
value: menu.id,
|
|
251
|
+
"data-slot": "menu-group",
|
|
252
|
+
className: "border-b-0",
|
|
253
|
+
children: [
|
|
254
|
+
/* @__PURE__ */ jsx(
|
|
255
|
+
AccordionTrigger,
|
|
256
|
+
{
|
|
257
|
+
"data-slot": "menu-group-trigger",
|
|
258
|
+
className: cn(menuItemClass, "py-2 font-medium"),
|
|
259
|
+
disabled: menu.disabled,
|
|
260
|
+
title: menu.title,
|
|
261
|
+
children: menu.title
|
|
262
|
+
}
|
|
263
|
+
),
|
|
264
|
+
/* @__PURE__ */ jsx(AccordionContent, { "data-slot": "menu-group-content", className: "pb-1", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5 pl-3", children: /* @__PURE__ */ jsx(
|
|
265
|
+
MenuTree,
|
|
266
|
+
{
|
|
267
|
+
menus: menu.children ?? [],
|
|
268
|
+
registry,
|
|
269
|
+
bridge,
|
|
270
|
+
activeMenuId,
|
|
271
|
+
defaultOpenKeys
|
|
272
|
+
}
|
|
273
|
+
) }) })
|
|
274
|
+
]
|
|
275
|
+
},
|
|
276
|
+
menu.id
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
return /* @__PURE__ */ jsx(
|
|
280
|
+
"button",
|
|
281
|
+
{
|
|
282
|
+
type: "button",
|
|
283
|
+
"data-slot": "menu-item",
|
|
284
|
+
"data-active": menu.id === activeMenuId || void 0,
|
|
285
|
+
className: cn(
|
|
286
|
+
menuItemClass,
|
|
287
|
+
menu.id === activeMenuId && "bg-accent font-semibold text-accent-foreground"
|
|
288
|
+
),
|
|
289
|
+
disabled: menu.disabled,
|
|
290
|
+
title: menu.title,
|
|
291
|
+
onClick: () => navigateMenuItem(registry.findById(menu.id), bridge),
|
|
292
|
+
children: menu.title
|
|
293
|
+
},
|
|
294
|
+
menu.id
|
|
295
|
+
);
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
function ShadcnAdminLayout({
|
|
301
|
+
config = {},
|
|
302
|
+
brand,
|
|
303
|
+
registry,
|
|
304
|
+
bridge,
|
|
305
|
+
route: routeProp,
|
|
306
|
+
permissionChecker,
|
|
307
|
+
headerExtra,
|
|
308
|
+
children
|
|
309
|
+
}) {
|
|
310
|
+
const { t } = useI18n();
|
|
311
|
+
const mode = config.mode ?? "top-side";
|
|
312
|
+
const showHeader = config.header ?? true;
|
|
313
|
+
const showSidebar = (config.sidebar ?? true) && mode !== "top-bottom";
|
|
314
|
+
const showTabs = config.tabs ?? false;
|
|
315
|
+
const showBreadcrumb = config.breadcrumb ?? false;
|
|
316
|
+
const showFooter = config.footer ?? false;
|
|
317
|
+
const [collapsed, setCollapsed] = useState2(config.collapsed ?? false);
|
|
318
|
+
const broken = useBreakpointBelow(config.breakpoint);
|
|
319
|
+
const effectiveCollapsed = collapsed || broken;
|
|
320
|
+
const [bridgeRoute, setBridgeRoute] = useState2(
|
|
321
|
+
() => resolveRoute(registry, bridge.getLocation())
|
|
322
|
+
);
|
|
323
|
+
const [tabs, setTabs] = useState2([]);
|
|
324
|
+
const tabsRef = useRef(tabs);
|
|
325
|
+
tabsRef.current = tabs;
|
|
326
|
+
useEffect2(() => {
|
|
327
|
+
const handleLocation = (location) => {
|
|
328
|
+
const next = resolveRoute(registry, location);
|
|
329
|
+
setBridgeRoute(next);
|
|
330
|
+
if (next?.menuId !== void 0) {
|
|
331
|
+
const menu = registry.findById(next.menuId);
|
|
332
|
+
if (menu !== void 0 && menu.type === "page" && menu.path !== void 0) {
|
|
333
|
+
setTabs((prev) => {
|
|
334
|
+
if (prev.some((tab) => tab.key === location.pathname)) {
|
|
335
|
+
return prev;
|
|
336
|
+
}
|
|
337
|
+
return [...prev, { key: location.pathname, title: menu.title }];
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
handleLocation(bridge.getLocation());
|
|
343
|
+
return bridge.subscribe(handleLocation);
|
|
344
|
+
}, [registry, bridge]);
|
|
345
|
+
const route = routeProp ?? bridgeRoute;
|
|
346
|
+
const activeMenuId = route?.activeMenu;
|
|
347
|
+
const visibleMenus = useMemo(
|
|
348
|
+
() => buildVisibleMenuTree(registry, permissionChecker),
|
|
349
|
+
[registry, permissionChecker]
|
|
350
|
+
);
|
|
351
|
+
const defaultOpenKeysRef = useRef(null);
|
|
352
|
+
if (defaultOpenKeysRef.current === null) {
|
|
353
|
+
defaultOpenKeysRef.current = collectDefaultOpenKeys(registry, activeMenuId);
|
|
354
|
+
}
|
|
355
|
+
const defaultOpenKeys = defaultOpenKeysRef.current;
|
|
356
|
+
const activeTopId = useMemo(() => {
|
|
357
|
+
if (activeMenuId !== void 0) {
|
|
358
|
+
const chain = findMenuChain(registry, activeMenuId);
|
|
359
|
+
if (chain.length > 0) {
|
|
360
|
+
return chain[0].id;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return void 0;
|
|
364
|
+
}, [registry, activeMenuId]);
|
|
365
|
+
const [selectedTopId, setSelectedTopId] = useState2();
|
|
366
|
+
const secondaryTopId = selectedTopId ?? activeTopId ?? visibleMenus.find((m) => m.type === "group")?.id ?? visibleMenus[0]?.id;
|
|
367
|
+
const secondaryTop = visibleMenus.find((m) => m.id === secondaryTopId);
|
|
368
|
+
const hasSecondary = (secondaryTop?.children?.length ?? 0) > 0;
|
|
369
|
+
const breadcrumbItems = useMemo(() => {
|
|
370
|
+
if (!showBreadcrumb || activeMenuId === void 0) {
|
|
371
|
+
return [];
|
|
372
|
+
}
|
|
373
|
+
return findMenuChain(registry, activeMenuId).map((menu) => menu.title);
|
|
374
|
+
}, [showBreadcrumb, registry, activeMenuId]);
|
|
375
|
+
const currentPath = route?.path;
|
|
376
|
+
const closeTab = (targetKey) => {
|
|
377
|
+
const remaining = tabsRef.current.filter((tab) => tab.key !== targetKey);
|
|
378
|
+
setTabs(remaining);
|
|
379
|
+
if (targetKey === currentPath && remaining.length > 0) {
|
|
380
|
+
bridge.push(remaining[remaining.length - 1].key);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
const fixedHeader = config.fixedHeader ?? true;
|
|
384
|
+
const fixedSider = config.fixedSider ?? true;
|
|
385
|
+
const header = showHeader ? /* @__PURE__ */ jsxs(
|
|
386
|
+
"header",
|
|
387
|
+
{
|
|
388
|
+
"data-slot": "admin-header",
|
|
389
|
+
className: cn(
|
|
390
|
+
"flex h-14 items-center gap-4 border-b border-border bg-background px-6",
|
|
391
|
+
fixedHeader && "sticky top-0 z-20"
|
|
392
|
+
),
|
|
393
|
+
children: [
|
|
394
|
+
mode !== "side-full" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
395
|
+
brand.logo,
|
|
396
|
+
/* @__PURE__ */ jsx("span", { "data-slot": "admin-brand", className: "text-lg font-semibold", children: brand.title })
|
|
397
|
+
] }),
|
|
398
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
399
|
+
headerExtra
|
|
400
|
+
]
|
|
401
|
+
}
|
|
402
|
+
) : null;
|
|
403
|
+
const primaryColumn = /* @__PURE__ */ jsx(
|
|
404
|
+
"div",
|
|
405
|
+
{
|
|
406
|
+
"data-slot": "admin-primary-col",
|
|
407
|
+
className: "flex w-12 shrink-0 flex-col items-center gap-1 border-r border-border py-2",
|
|
408
|
+
children: visibleMenus.map((menu) => /* @__PURE__ */ jsx(
|
|
409
|
+
"button",
|
|
410
|
+
{
|
|
411
|
+
type: "button",
|
|
412
|
+
"data-slot": "admin-primary-item",
|
|
413
|
+
"data-active": menu.id === secondaryTopId || menu.id === activeTopId || void 0,
|
|
414
|
+
className: cn(
|
|
415
|
+
menuItemClass,
|
|
416
|
+
"size-10 justify-center px-0 font-semibold",
|
|
417
|
+
(menu.id === secondaryTopId || menu.id === activeTopId) && "bg-accent text-accent-foreground"
|
|
418
|
+
),
|
|
419
|
+
disabled: menu.disabled,
|
|
420
|
+
title: menu.title,
|
|
421
|
+
onClick: () => {
|
|
422
|
+
if (menu.type === "group") {
|
|
423
|
+
setSelectedTopId(menu.id);
|
|
424
|
+
} else {
|
|
425
|
+
navigateMenuItem(menu, bridge);
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
children: menu.title.slice(0, 1)
|
|
429
|
+
},
|
|
430
|
+
menu.id
|
|
431
|
+
))
|
|
432
|
+
}
|
|
433
|
+
);
|
|
434
|
+
const showTrigger = config.collapsible && mode !== "two-column";
|
|
435
|
+
const sidebar = showSidebar ? /* @__PURE__ */ jsxs(
|
|
436
|
+
"aside",
|
|
437
|
+
{
|
|
438
|
+
"data-slot": "admin-sidebar",
|
|
439
|
+
"data-collapsed": effectiveCollapsed || void 0,
|
|
440
|
+
className: cn(
|
|
441
|
+
"shrink-0 overflow-auto border-r border-border bg-background py-2",
|
|
442
|
+
effectiveCollapsed ? "w-16 px-1.5" : "w-52",
|
|
443
|
+
fixedSider && (mode === "side-full" ? "sticky top-0 h-screen" : "sticky top-14 h-[calc(100vh-3.5rem)]")
|
|
444
|
+
),
|
|
445
|
+
style: mode === "two-column" && !hasSecondary ? { width: 56 } : void 0,
|
|
446
|
+
children: [
|
|
447
|
+
mode === "side-full" && /* @__PURE__ */ jsxs(
|
|
448
|
+
"div",
|
|
449
|
+
{
|
|
450
|
+
"data-slot": "admin-sidebar-brand",
|
|
451
|
+
className: "flex items-center gap-2 border-b border-border px-4 py-4 font-semibold",
|
|
452
|
+
children: [
|
|
453
|
+
brand.logo,
|
|
454
|
+
/* @__PURE__ */ jsx("span", { children: brand.title })
|
|
455
|
+
]
|
|
456
|
+
}
|
|
457
|
+
),
|
|
458
|
+
mode === "two-column" ? /* @__PURE__ */ jsxs("div", { "data-slot": "admin-two-col", className: "flex h-full", children: [
|
|
459
|
+
primaryColumn,
|
|
460
|
+
hasSecondary && /* @__PURE__ */ jsx("div", { "data-slot": "admin-secondary-col", className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
|
|
461
|
+
MenuTree,
|
|
462
|
+
{
|
|
463
|
+
menus: secondaryTop?.children ?? [],
|
|
464
|
+
registry,
|
|
465
|
+
bridge,
|
|
466
|
+
activeMenuId,
|
|
467
|
+
defaultOpenKeys
|
|
468
|
+
}
|
|
469
|
+
) })
|
|
470
|
+
] }) : /* @__PURE__ */ jsx(
|
|
471
|
+
MenuTree,
|
|
472
|
+
{
|
|
473
|
+
menus: visibleMenus,
|
|
474
|
+
registry,
|
|
475
|
+
bridge,
|
|
476
|
+
activeMenuId,
|
|
477
|
+
defaultOpenKeys,
|
|
478
|
+
collapsed: effectiveCollapsed
|
|
479
|
+
}
|
|
480
|
+
),
|
|
481
|
+
showTrigger && /* @__PURE__ */ jsx(
|
|
482
|
+
"button",
|
|
483
|
+
{
|
|
484
|
+
type: "button",
|
|
485
|
+
"data-slot": "admin-collapse-trigger",
|
|
486
|
+
"aria-label": effectiveCollapsed ? t("pactor.layout.expandSidebar") : t("pactor.layout.collapseSidebar"),
|
|
487
|
+
"aria-expanded": !effectiveCollapsed,
|
|
488
|
+
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",
|
|
489
|
+
onClick: () => setCollapsed((value) => !value),
|
|
490
|
+
children: effectiveCollapsed ? "\xBB" : "\xAB"
|
|
491
|
+
}
|
|
492
|
+
)
|
|
493
|
+
]
|
|
494
|
+
}
|
|
495
|
+
) : null;
|
|
496
|
+
const body = /* @__PURE__ */ jsxs("div", { "data-slot": "admin-body", className: "flex min-w-0 flex-1 flex-col", children: [
|
|
497
|
+
showTabs && /* @__PURE__ */ jsx(
|
|
498
|
+
"div",
|
|
499
|
+
{
|
|
500
|
+
"data-slot": "admin-tabs",
|
|
501
|
+
role: "tablist",
|
|
502
|
+
className: "flex items-center gap-1 border-b border-border bg-background px-4 pt-2",
|
|
503
|
+
children: tabs.map((tab, index) => /* @__PURE__ */ jsxs(
|
|
504
|
+
"div",
|
|
505
|
+
{
|
|
506
|
+
"data-slot": "admin-tab",
|
|
507
|
+
"data-active": tab.key === currentPath || void 0,
|
|
508
|
+
role: "presentation",
|
|
509
|
+
className: cn(
|
|
510
|
+
"inline-flex items-center gap-1 rounded-t-md border border-b-0 border-border px-3 py-1.5 text-sm",
|
|
511
|
+
tab.key === currentPath ? "bg-background font-medium text-foreground" : "bg-muted text-muted-foreground"
|
|
512
|
+
),
|
|
513
|
+
children: [
|
|
514
|
+
/* @__PURE__ */ jsx(
|
|
515
|
+
"button",
|
|
516
|
+
{
|
|
517
|
+
type: "button",
|
|
518
|
+
role: "tab",
|
|
519
|
+
"aria-selected": tab.key === currentPath,
|
|
520
|
+
className: "cursor-pointer",
|
|
521
|
+
onClick: () => bridge.push(tab.key),
|
|
522
|
+
children: tab.title
|
|
523
|
+
}
|
|
524
|
+
),
|
|
525
|
+
index > 0 && /* @__PURE__ */ jsx(
|
|
526
|
+
"button",
|
|
527
|
+
{
|
|
528
|
+
type: "button",
|
|
529
|
+
"data-slot": "admin-tab-close",
|
|
530
|
+
"aria-label": t("pactor.layout.closeTab", { title: tab.title }),
|
|
531
|
+
className: "cursor-pointer text-muted-foreground hover:text-foreground",
|
|
532
|
+
onClick: () => closeTab(tab.key),
|
|
533
|
+
children: "\xD7"
|
|
534
|
+
}
|
|
535
|
+
)
|
|
536
|
+
]
|
|
537
|
+
},
|
|
538
|
+
tab.key
|
|
539
|
+
))
|
|
540
|
+
}
|
|
541
|
+
),
|
|
542
|
+
/* @__PURE__ */ jsxs(
|
|
543
|
+
"main",
|
|
544
|
+
{
|
|
545
|
+
"data-slot": "admin-content",
|
|
546
|
+
className: "flex-1 bg-muted/40 p-6",
|
|
547
|
+
children: [
|
|
548
|
+
showBreadcrumb && breadcrumbItems.length > 0 && /* @__PURE__ */ jsx(
|
|
549
|
+
"nav",
|
|
550
|
+
{
|
|
551
|
+
"data-slot": "admin-breadcrumb",
|
|
552
|
+
"aria-label": t("pactor.layout.breadcrumb"),
|
|
553
|
+
className: "mb-4",
|
|
554
|
+
children: /* @__PURE__ */ jsx("ol", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: breadcrumbItems.map((title, index) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2", children: [
|
|
555
|
+
index > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "/" }),
|
|
556
|
+
/* @__PURE__ */ jsx("span", { "data-slot": "breadcrumb-link", children: title })
|
|
557
|
+
] }, index)) })
|
|
558
|
+
}
|
|
559
|
+
),
|
|
560
|
+
children
|
|
561
|
+
]
|
|
562
|
+
}
|
|
563
|
+
)
|
|
564
|
+
] });
|
|
565
|
+
const footer = showFooter ? /* @__PURE__ */ jsx(
|
|
566
|
+
"footer",
|
|
567
|
+
{
|
|
568
|
+
"data-slot": "admin-footer",
|
|
569
|
+
className: "bg-muted p-4 text-center text-sm text-muted-foreground",
|
|
570
|
+
children: brand.title
|
|
571
|
+
}
|
|
572
|
+
) : null;
|
|
573
|
+
return /* @__PURE__ */ jsx(
|
|
574
|
+
"div",
|
|
575
|
+
{
|
|
576
|
+
"data-slot": "admin-layout",
|
|
577
|
+
"data-mode": mode,
|
|
578
|
+
"data-fixed-header": fixedHeader || void 0,
|
|
579
|
+
"data-fixed-sider": fixedSider || void 0,
|
|
580
|
+
className: "flex min-h-screen flex-col bg-background text-foreground",
|
|
581
|
+
children: mode === "side-full" ? /* @__PURE__ */ jsxs("div", { "data-slot": "admin-side-full", className: "flex min-h-screen flex-1", children: [
|
|
582
|
+
sidebar,
|
|
583
|
+
/* @__PURE__ */ jsxs(
|
|
584
|
+
"div",
|
|
585
|
+
{
|
|
586
|
+
"data-slot": "admin-side-full-main",
|
|
587
|
+
className: "flex min-w-0 flex-1 flex-col",
|
|
588
|
+
children: [
|
|
589
|
+
header,
|
|
590
|
+
body,
|
|
591
|
+
footer
|
|
592
|
+
]
|
|
593
|
+
}
|
|
594
|
+
)
|
|
595
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
596
|
+
header,
|
|
597
|
+
/* @__PURE__ */ jsxs("div", { "data-slot": "admin-main", className: "flex min-h-0 flex-1", children: [
|
|
598
|
+
sidebar,
|
|
599
|
+
body
|
|
600
|
+
] }),
|
|
601
|
+
footer
|
|
602
|
+
] })
|
|
603
|
+
}
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// src/layout/basic/ShadcnBlankLayout.tsx
|
|
608
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
609
|
+
function ShadcnBlankLayout({ children }) {
|
|
610
|
+
return /* @__PURE__ */ jsx2(
|
|
611
|
+
"div",
|
|
612
|
+
{
|
|
613
|
+
"data-slot": "blank-layout",
|
|
614
|
+
className: "min-h-screen bg-background text-foreground",
|
|
615
|
+
children
|
|
616
|
+
}
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// src/layout/basic/ShadcnLoginLayout.tsx
|
|
621
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
622
|
+
function ShadcnLoginLayout({
|
|
623
|
+
variant = "center",
|
|
624
|
+
brand,
|
|
625
|
+
children
|
|
626
|
+
}) {
|
|
627
|
+
const brandBlock = /* @__PURE__ */ jsxs2("div", { "data-slot": "login-brand", className: "flex flex-col items-center gap-2", children: [
|
|
628
|
+
brand.logo,
|
|
629
|
+
/* @__PURE__ */ jsx3("h1", { className: "text-xl font-semibold", children: brand.title }),
|
|
630
|
+
brand.description !== void 0 && /* @__PURE__ */ jsx3("div", { className: "text-sm text-muted-foreground", children: brand.description })
|
|
631
|
+
] });
|
|
632
|
+
if (variant === "split") {
|
|
633
|
+
return /* @__PURE__ */ jsxs2(
|
|
634
|
+
"div",
|
|
635
|
+
{
|
|
636
|
+
"data-slot": "login-layout",
|
|
637
|
+
"data-variant": "split",
|
|
638
|
+
className: "grid min-h-screen grid-cols-2 bg-background text-foreground",
|
|
639
|
+
children: [
|
|
640
|
+
/* @__PURE__ */ jsx3(
|
|
641
|
+
"div",
|
|
642
|
+
{
|
|
643
|
+
"data-slot": "login-brand-panel",
|
|
644
|
+
className: "flex items-center justify-center bg-muted/40 p-10",
|
|
645
|
+
children: brandBlock
|
|
646
|
+
}
|
|
647
|
+
),
|
|
648
|
+
/* @__PURE__ */ jsx3(
|
|
649
|
+
"div",
|
|
650
|
+
{
|
|
651
|
+
"data-slot": "login-form-panel",
|
|
652
|
+
className: "flex items-center justify-center p-10",
|
|
653
|
+
children
|
|
654
|
+
}
|
|
655
|
+
)
|
|
656
|
+
]
|
|
657
|
+
}
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
return /* @__PURE__ */ jsx3(
|
|
661
|
+
"div",
|
|
662
|
+
{
|
|
663
|
+
"data-slot": "login-layout",
|
|
664
|
+
"data-variant": "center",
|
|
665
|
+
className: "flex min-h-screen items-center justify-center bg-muted/40 text-foreground",
|
|
666
|
+
children: /* @__PURE__ */ jsxs2(Card, { "data-slot": "login-card", className: "w-full max-w-sm gap-4 py-6", children: [
|
|
667
|
+
/* @__PURE__ */ jsxs2(CardHeader, { className: "px-6", children: [
|
|
668
|
+
/* @__PURE__ */ jsx3(CardTitle, { className: "sr-only", children: brand.title }),
|
|
669
|
+
brandBlock
|
|
670
|
+
] }),
|
|
671
|
+
/* @__PURE__ */ jsx3(CardContent, { className: "flex flex-col gap-4 px-6", children })
|
|
672
|
+
] })
|
|
673
|
+
}
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// src/layout/basic/ShadcnLandingLayout.tsx
|
|
678
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
679
|
+
function ShadcnLandingLayout({
|
|
680
|
+
navbar,
|
|
681
|
+
footer,
|
|
682
|
+
children
|
|
683
|
+
}) {
|
|
684
|
+
return /* @__PURE__ */ jsxs3(
|
|
685
|
+
"div",
|
|
686
|
+
{
|
|
687
|
+
"data-slot": "landing-layout",
|
|
688
|
+
className: "flex min-h-screen flex-col bg-background text-foreground",
|
|
689
|
+
children: [
|
|
690
|
+
navbar !== void 0 && /* @__PURE__ */ jsx4(
|
|
691
|
+
"header",
|
|
692
|
+
{
|
|
693
|
+
"data-slot": "landing-navbar",
|
|
694
|
+
className: "border-b border-border bg-background",
|
|
695
|
+
children: navbar
|
|
696
|
+
}
|
|
697
|
+
),
|
|
698
|
+
/* @__PURE__ */ jsx4("main", { "data-slot": "landing-content", className: "flex-1", children }),
|
|
699
|
+
footer !== void 0 && /* @__PURE__ */ jsx4(
|
|
700
|
+
"footer",
|
|
701
|
+
{
|
|
702
|
+
"data-slot": "landing-footer",
|
|
703
|
+
className: "border-t border-border text-sm text-muted-foreground",
|
|
704
|
+
children: footer
|
|
705
|
+
}
|
|
706
|
+
)
|
|
707
|
+
]
|
|
708
|
+
}
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// src/layout/registry.ts
|
|
713
|
+
import { Registry } from "@pactor-app/core";
|
|
714
|
+
function registerShadcnLayouts(registry) {
|
|
715
|
+
registry.register("admin", ShadcnAdminLayout);
|
|
716
|
+
registry.register("blank", ShadcnBlankLayout);
|
|
717
|
+
registry.register("login", ShadcnLoginLayout);
|
|
718
|
+
registry.register("landing", ShadcnLandingLayout);
|
|
719
|
+
}
|
|
720
|
+
function createShadcnLayoutRegistry() {
|
|
721
|
+
const registry = new Registry("LayoutRegistry");
|
|
722
|
+
registerShadcnLayouts(registry);
|
|
723
|
+
return registry;
|
|
724
|
+
}
|
|
725
|
+
function resolveLayout(registry, type) {
|
|
726
|
+
const component = registry.get(type);
|
|
727
|
+
if (component === void 0) {
|
|
728
|
+
console.warn(
|
|
729
|
+
`[pactor-layout] unknown layout type "${type}", fallback to BlankLayout`
|
|
730
|
+
);
|
|
731
|
+
return ShadcnBlankLayout;
|
|
732
|
+
}
|
|
733
|
+
return component;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
export {
|
|
737
|
+
findMenuChain,
|
|
738
|
+
collectDefaultOpenKeys,
|
|
739
|
+
ShadcnAdminLayout,
|
|
740
|
+
ShadcnBlankLayout,
|
|
741
|
+
ShadcnLoginLayout,
|
|
742
|
+
ShadcnLandingLayout,
|
|
743
|
+
registerShadcnLayouts,
|
|
744
|
+
createShadcnLayoutRegistry,
|
|
745
|
+
resolveLayout
|
|
746
|
+
};
|