@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.
@@ -0,0 +1,614 @@
1
+ import {
2
+ SidebarContent,
3
+ SidebarFooter,
4
+ SidebarHeader,
5
+ SidebarInset,
6
+ SidebarMenu,
7
+ SidebarMenuButton,
8
+ SidebarMenuItem,
9
+ SidebarProvider,
10
+ SidebarRoot,
11
+ Tooltip,
12
+ TooltipContent,
13
+ TooltipTrigger,
14
+ useIsMobile,
15
+ useSidebar
16
+ } from "./chunk-L4Z7MNYY.js";
17
+ import {
18
+ cn
19
+ } from "./chunk-WKJUCGV4.js";
20
+
21
+ // src/components/components/Icon/index.tsx
22
+ import {
23
+ ArrowDown,
24
+ ArrowLeft,
25
+ ArrowRight,
26
+ ArrowUp,
27
+ Bell,
28
+ Bookmark,
29
+ Brain,
30
+ Briefcase,
31
+ Building2,
32
+ Calendar,
33
+ Camera,
34
+ ChartColumn,
35
+ Check,
36
+ ChevronDown,
37
+ ChevronLeft,
38
+ ChevronRight,
39
+ ChevronsUpDown,
40
+ ChevronUp,
41
+ CircleAlert,
42
+ CircleCheck,
43
+ CircleQuestionMark,
44
+ CircleX,
45
+ Clock,
46
+ Cloud,
47
+ Code,
48
+ Copy,
49
+ CreditCard,
50
+ Database,
51
+ Download,
52
+ Ellipsis,
53
+ EllipsisVertical,
54
+ ExternalLink,
55
+ Eye,
56
+ File,
57
+ FileText,
58
+ Flag,
59
+ Folder,
60
+ Funnel,
61
+ Globe,
62
+ Heart,
63
+ History,
64
+ House,
65
+ Image,
66
+ Info,
67
+ Key,
68
+ Languages,
69
+ LayoutGrid,
70
+ Link,
71
+ List,
72
+ ListFilter,
73
+ LoaderCircle,
74
+ Lock,
75
+ LockOpen,
76
+ LogIn,
77
+ LogOut,
78
+ Mail,
79
+ MapPin,
80
+ Menu,
81
+ MessageCircle,
82
+ MessageSquare,
83
+ Minus,
84
+ Moon,
85
+ Package,
86
+ PanelLeft,
87
+ PanelRightOpen,
88
+ Paperclip,
89
+ Pencil,
90
+ Phone,
91
+ Plus,
92
+ Quote,
93
+ RefreshCcw,
94
+ RotateCcw,
95
+ Save,
96
+ Search,
97
+ Send,
98
+ Settings,
99
+ Share2,
100
+ Shield,
101
+ ShoppingCart,
102
+ Square,
103
+ SquarePen,
104
+ Star,
105
+ Sun,
106
+ Table,
107
+ Tag,
108
+ Terminal,
109
+ ThumbsDown,
110
+ ThumbsUp,
111
+ Trash2,
112
+ TriangleAlert,
113
+ Upload,
114
+ User,
115
+ Users,
116
+ X,
117
+ Zap
118
+ } from "lucide-react";
119
+ import { jsx } from "react/jsx-runtime";
120
+ var ICONS = {
121
+ "arrow-left": ArrowLeft,
122
+ "arrow-right": ArrowRight,
123
+ "arrow-up": ArrowUp,
124
+ "arrow-down": ArrowDown,
125
+ "chevron-left": ChevronLeft,
126
+ "chevron-right": ChevronRight,
127
+ "chevron-up": ChevronUp,
128
+ "chevron-down": ChevronDown,
129
+ check: Check,
130
+ x: X,
131
+ plus: Plus,
132
+ minus: Minus,
133
+ search: Search,
134
+ settings: Settings,
135
+ user: User,
136
+ users: Users,
137
+ home: House,
138
+ menu: Menu,
139
+ bell: Bell,
140
+ calendar: Calendar,
141
+ clock: Clock,
142
+ download: Download,
143
+ upload: Upload,
144
+ pencil: Pencil,
145
+ edit: SquarePen,
146
+ trash: Trash2,
147
+ eye: Eye,
148
+ info: Info,
149
+ warning: TriangleAlert,
150
+ error: CircleAlert,
151
+ "check-circle": CircleCheck,
152
+ "x-circle": CircleX,
153
+ "help-circle": CircleQuestionMark,
154
+ loading: LoaderCircle,
155
+ spinner: LoaderCircle,
156
+ star: Star,
157
+ heart: Heart,
158
+ bookmark: Bookmark,
159
+ share: Share2,
160
+ copy: Copy,
161
+ "external-link": ExternalLink,
162
+ link: Link,
163
+ mail: Mail,
164
+ phone: Phone,
165
+ lock: Lock,
166
+ unlock: LockOpen,
167
+ "log-in": LogIn,
168
+ "log-out": LogOut,
169
+ sun: Sun,
170
+ moon: Moon,
171
+ globe: Globe,
172
+ filter: Funnel,
173
+ "list-filter": ListFilter,
174
+ "more-horizontal": Ellipsis,
175
+ "more-vertical": EllipsisVertical,
176
+ refresh: RefreshCcw,
177
+ save: Save,
178
+ file: File,
179
+ folder: Folder,
180
+ image: Image,
181
+ camera: Camera,
182
+ chart: ChartColumn,
183
+ table: Table,
184
+ list: List,
185
+ grid: LayoutGrid,
186
+ tag: Tag,
187
+ "shopping-cart": ShoppingCart,
188
+ "credit-card": CreditCard,
189
+ package: Package,
190
+ "map-pin": MapPin,
191
+ building: Building2,
192
+ briefcase: Briefcase,
193
+ send: Send,
194
+ message: MessageCircle,
195
+ "message-square": MessageSquare,
196
+ paperclip: Paperclip,
197
+ "thumbs-up": ThumbsUp,
198
+ "thumbs-down": ThumbsDown,
199
+ zap: Zap,
200
+ flag: Flag,
201
+ key: Key,
202
+ shield: Shield,
203
+ terminal: Terminal,
204
+ code: Code,
205
+ database: Database,
206
+ cloud: Cloud,
207
+ languages: Languages,
208
+ // @pactor-app/chat 收敛(spec: chat-icon-convergence):chat 既有图标与
209
+ // 契约图标名(menu.icon / messageActions[].icon 的既有取值)直通
210
+ "chevrons-up-down": ChevronsUpDown,
211
+ "file-text": FileText,
212
+ "panel-left": PanelLeft,
213
+ "panel-right-open": PanelRightOpen,
214
+ square: Square,
215
+ history: History,
216
+ "rotate-ccw": RotateCcw,
217
+ quote: Quote,
218
+ chat: MessageSquare,
219
+ brain: Brain
220
+ };
221
+ function normalizeIconName(name) {
222
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[_\s]+/g, "-").toLowerCase();
223
+ }
224
+ var sizePx = {
225
+ sm: 16,
226
+ default: 20,
227
+ lg: 24
228
+ };
229
+ function Icon({
230
+ name,
231
+ size = "default",
232
+ color,
233
+ strokeWidth,
234
+ className,
235
+ style
236
+ }) {
237
+ const key = name ? normalizeIconName(name) : "";
238
+ const Lucide = ICONS[key];
239
+ const px = typeof size === "number" ? size : sizePx[size];
240
+ if (!Lucide) {
241
+ return /* @__PURE__ */ jsx(
242
+ CircleQuestionMark,
243
+ {
244
+ "data-slot": "icon",
245
+ "data-unknown": name,
246
+ size: px,
247
+ color,
248
+ strokeWidth,
249
+ className: cn("shrink-0 text-muted-foreground", className),
250
+ style,
251
+ "aria-hidden": "true"
252
+ }
253
+ );
254
+ }
255
+ return /* @__PURE__ */ jsx(
256
+ Lucide,
257
+ {
258
+ "data-slot": "icon",
259
+ size: px,
260
+ color,
261
+ strokeWidth,
262
+ className: cn("shrink-0", className),
263
+ style,
264
+ "aria-hidden": "true"
265
+ }
266
+ );
267
+ }
268
+
269
+ // src/components/components/Sidebar/rail-tooltip.tsx
270
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
271
+ function RailTooltip({
272
+ label,
273
+ children
274
+ }) {
275
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
276
+ /* @__PURE__ */ jsx2(TooltipTrigger, { render: children }),
277
+ /* @__PURE__ */ jsx2(TooltipContent, { side: "right", sideOffset: 8, children: label })
278
+ ] });
279
+ }
280
+
281
+ // src/components/components/Sidebar/index.tsx
282
+ import { useI18n, useRenderer } from "@pactor-app/runtime";
283
+ import {
284
+ useEffect,
285
+ useRef
286
+ } from "react";
287
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
288
+ function useSidebarDrawer() {
289
+ try {
290
+ const ctx = useSidebar();
291
+ return {
292
+ close: () => ctx.setOpenMobile(false),
293
+ collapsed: ctx.state === "collapsed"
294
+ };
295
+ } catch {
296
+ return null;
297
+ }
298
+ }
299
+ function useOptionalRenderer() {
300
+ try {
301
+ return useRenderer();
302
+ } catch {
303
+ return null;
304
+ }
305
+ }
306
+ function SidebarContentSlot({ content }) {
307
+ const renderer = useOptionalRenderer();
308
+ if (renderer !== null && typeof content === "object" && content !== null && !("$$typeof" in content) && "type" in content) {
309
+ return /* @__PURE__ */ jsx3(Fragment, { children: renderer.renderChildren([content]) });
310
+ }
311
+ return /* @__PURE__ */ jsx3(Fragment, { children: content });
312
+ }
313
+ function isBrandObject(brand) {
314
+ return typeof brand === "object" && brand !== null && !("$$typeof" in brand) && "title" in brand;
315
+ }
316
+ function BrandNode({ brand, collapsed }) {
317
+ if (brand === void 0 || brand === null) {
318
+ return null;
319
+ }
320
+ if (isBrandObject(brand)) {
321
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
322
+ brand.logo ? /* @__PURE__ */ jsx3(
323
+ "img",
324
+ {
325
+ "data-slot": "sidebar-brand-logo",
326
+ src: brand.logo,
327
+ alt: brand.title,
328
+ className: "size-6 shrink-0"
329
+ }
330
+ ) : null,
331
+ collapsed ? null : /* @__PURE__ */ jsx3("span", { className: "truncate text-sm font-semibold", children: brand.title })
332
+ ] });
333
+ }
334
+ return /* @__PURE__ */ jsx3(Fragment, { children: brand });
335
+ }
336
+ function SidebarNavItems({ items }) {
337
+ return /* @__PURE__ */ jsx3(SidebarMenu, { children: items.map((item, index) => /* @__PURE__ */ jsx3(SidebarNavEntry, { item, depth: 0 }, item.href ?? item.label ?? index)) });
338
+ }
339
+ function SidebarNavEntry({ item, depth }) {
340
+ const iconNode = item.icon ? /* @__PURE__ */ jsx3(Icon, { name: item.icon, size: "sm" }) : null;
341
+ const labelNode = item.label;
342
+ const button = item.href ? /* @__PURE__ */ jsx3(SidebarMenuButton, { asChild: true, tooltip: item.label, children: /* @__PURE__ */ jsxs2("a", { href: item.href, children: [
343
+ iconNode,
344
+ /* @__PURE__ */ jsx3("span", { children: labelNode })
345
+ ] }) }) : /* @__PURE__ */ jsxs2(SidebarMenuButton, { tooltip: item.label, children: [
346
+ iconNode,
347
+ /* @__PURE__ */ jsx3("span", { children: labelNode })
348
+ ] });
349
+ return /* @__PURE__ */ jsxs2(SidebarMenuItem, { children: [
350
+ button,
351
+ item.children && item.children.length > 0 && /* @__PURE__ */ jsx3("div", { className: cn("ml-4"), children: item.children.map((child, index) => /* @__PURE__ */ jsx3(
352
+ SidebarNavEntry,
353
+ {
354
+ item: child,
355
+ depth: depth + 1
356
+ },
357
+ child.href ?? child.label ?? index
358
+ )) })
359
+ ] });
360
+ }
361
+ function Sidebar({
362
+ brand,
363
+ items = [],
364
+ content,
365
+ sidebarFooter,
366
+ collapsible,
367
+ onCollapse,
368
+ locationKey,
369
+ drawer = true,
370
+ className,
371
+ style,
372
+ children
373
+ }) {
374
+ return /* @__PURE__ */ jsx3(
375
+ SidebarProvider,
376
+ {
377
+ defaultOpen: true,
378
+ className,
379
+ style,
380
+ children: /* @__PURE__ */ jsx3(
381
+ SidebarInner,
382
+ {
383
+ brand,
384
+ items,
385
+ content,
386
+ sidebarFooter,
387
+ collapsible,
388
+ onCollapse,
389
+ locationKey,
390
+ drawer,
391
+ children
392
+ }
393
+ )
394
+ }
395
+ );
396
+ }
397
+ function SidebarInner({
398
+ brand,
399
+ items = [],
400
+ content,
401
+ sidebarFooter,
402
+ collapsible,
403
+ onCollapse,
404
+ locationKey,
405
+ drawer = true,
406
+ children
407
+ }) {
408
+ const { t } = useI18n();
409
+ const isMobile = useIsMobile();
410
+ const { state, setOpenMobile, toggleSidebar } = useSidebar();
411
+ const collapsed = state === "collapsed";
412
+ const prevCollapsed = useRef(collapsed);
413
+ useEffect(() => {
414
+ if (prevCollapsed.current !== collapsed) {
415
+ prevCollapsed.current = collapsed;
416
+ onCollapse?.(collapsed);
417
+ }
418
+ }, [collapsed, onCollapse]);
419
+ useEffect(() => {
420
+ if (locationKey !== void 0) {
421
+ setOpenMobile(false);
422
+ }
423
+ }, [locationKey, setOpenMobile]);
424
+ const collapsedEffective = collapsible === true && collapsed && !isMobile;
425
+ const brandNode = /* @__PURE__ */ jsx3(BrandNode, { brand, collapsed: collapsedEffective });
426
+ const sidebarContent = content !== void 0 && content !== null ? /* @__PURE__ */ jsx3(SidebarContentSlot, { content }) : items.length > 0 ? /* @__PURE__ */ jsx3(SidebarNavItems, { items }) : null;
427
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
428
+ /* @__PURE__ */ jsxs2(
429
+ SidebarRoot,
430
+ {
431
+ collapsible: collapsible ? "icon" : "offcanvas",
432
+ children: [
433
+ (brand !== void 0 || collapsible) && /* @__PURE__ */ jsx3(SidebarHeader, { children: /* @__PURE__ */ jsxs2(
434
+ "div",
435
+ {
436
+ "data-slot": "sidebar-brand",
437
+ className: cn(
438
+ "flex h-14 shrink-0 items-center gap-2 px-2",
439
+ collapsedEffective && "justify-center px-0"
440
+ ),
441
+ children: [
442
+ collapsedEffective ? null : /* @__PURE__ */ jsx3("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: brandNode }),
443
+ collapsible && !isMobile && /* @__PURE__ */ jsx3(
444
+ "button",
445
+ {
446
+ type: "button",
447
+ "data-slot": "sidebar-collapse-trigger",
448
+ "aria-label": collapsedEffective ? t("pactor.layout.expandSidebar") : t("pactor.layout.collapseSidebar"),
449
+ "aria-expanded": !collapsedEffective,
450
+ className: "flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-sidebar-foreground/60 transition-colors hover:bg-sidebar-accent hover:text-sidebar-foreground",
451
+ onClick: toggleSidebar,
452
+ children: /* @__PURE__ */ jsx3(Icon, { name: collapsedEffective ? "chevron-right" : "chevron-left", size: 16 })
453
+ }
454
+ )
455
+ ]
456
+ }
457
+ ) }),
458
+ /* @__PURE__ */ jsx3(SidebarContent, { children: sidebarContent }),
459
+ sidebarFooter !== void 0 && sidebarFooter !== null && !collapsedEffective && /* @__PURE__ */ jsx3(SidebarFooter, { children: sidebarFooter })
460
+ ]
461
+ }
462
+ ),
463
+ /* @__PURE__ */ jsxs2(SidebarInset, { children: [
464
+ drawer && /* @__PURE__ */ jsxs2(
465
+ "div",
466
+ {
467
+ "data-slot": "sidebar-topbar",
468
+ className: "flex h-14 items-center gap-2 border-b border-sidebar-border bg-sidebar px-3 md:hidden",
469
+ children: [
470
+ /* @__PURE__ */ jsx3(
471
+ "button",
472
+ {
473
+ type: "button",
474
+ "data-slot": "sidebar-menu-trigger",
475
+ "aria-label": t("pactor.layout.openNavigation"),
476
+ className: "flex size-8 items-center justify-center rounded-md text-sidebar-foreground/60 transition-colors hover:bg-sidebar-accent hover:text-sidebar-foreground",
477
+ onClick: toggleSidebar,
478
+ children: /* @__PURE__ */ jsx3(Icon, { name: "menu", size: 18 })
479
+ }
480
+ ),
481
+ brandNode
482
+ ]
483
+ }
484
+ ),
485
+ children
486
+ ] })
487
+ ] });
488
+ }
489
+
490
+ // src/components/components/Menu/index.tsx
491
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
492
+ function Menu2({
493
+ items = [],
494
+ mode = "inline",
495
+ selectedKey,
496
+ onSelect,
497
+ className,
498
+ style
499
+ }) {
500
+ const drawer = useSidebarDrawer();
501
+ const collapsed = mode === "inline" && drawer?.collapsed === true;
502
+ let previousGroup;
503
+ return /* @__PURE__ */ jsx4(
504
+ "nav",
505
+ {
506
+ "data-slot": "menu",
507
+ "data-mode": mode,
508
+ "data-collapsed": collapsed || void 0,
509
+ className: cn(
510
+ mode === "horizontal" ? "flex items-center gap-1" : "flex flex-col gap-0.5 p-2",
511
+ className
512
+ ),
513
+ style,
514
+ children: items.map((item) => {
515
+ let groupLabel;
516
+ if (!collapsed && mode === "inline" && item.group !== void 0 && item.group !== previousGroup) {
517
+ groupLabel = item.group;
518
+ }
519
+ previousGroup = item.group;
520
+ return /* @__PURE__ */ jsxs3("div", { "data-slot": "menu-group-entry", children: [
521
+ groupLabel !== void 0 && /* @__PURE__ */ jsx4(
522
+ "div",
523
+ {
524
+ "data-slot": "menu-group",
525
+ className: "px-3 pb-1 pt-3 text-xs font-medium text-muted-foreground first:pt-1",
526
+ children: groupLabel
527
+ }
528
+ ),
529
+ /* @__PURE__ */ jsx4(
530
+ MenuEntry,
531
+ {
532
+ item,
533
+ mode,
534
+ depth: 0,
535
+ selectedKey,
536
+ onSelect,
537
+ collapsed
538
+ }
539
+ )
540
+ ] }, item.key);
541
+ })
542
+ }
543
+ );
544
+ }
545
+ function MenuEntry({
546
+ item,
547
+ mode,
548
+ depth,
549
+ selectedKey,
550
+ onSelect,
551
+ collapsed = false
552
+ }) {
553
+ const selected = item.key === selectedKey;
554
+ const button = /* @__PURE__ */ jsxs3(
555
+ "button",
556
+ {
557
+ type: "button",
558
+ "data-slot": "menu-item",
559
+ "aria-current": selected ? "page" : void 0,
560
+ disabled: item.disabled,
561
+ className: cn(
562
+ "flex w-full items-center gap-2 rounded-md text-sm transition-colors",
563
+ mode === "horizontal" ? "px-3 py-2" : "px-3 py-2 text-left",
564
+ collapsed && "justify-center px-0",
565
+ selected ? "bg-(--menu-active-background) font-medium text-(--menu-active-foreground)" : "cursor-pointer text-muted-foreground hover:bg-(--menu-hover-background)/50 hover:text-foreground",
566
+ item.disabled && "cursor-not-allowed opacity-50 hover:bg-transparent hover:text-muted-foreground"
567
+ ),
568
+ style: mode === "inline" && depth > 0 && !collapsed ? { paddingLeft: 12 + depth * 16 } : void 0,
569
+ onClick: () => {
570
+ if (!item.disabled) {
571
+ onSelect?.(item.key);
572
+ }
573
+ },
574
+ children: [
575
+ item.icon ? /* @__PURE__ */ jsx4(Icon, { name: item.icon, size: "sm" }) : null,
576
+ collapsed ? null : /* @__PURE__ */ jsx4("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label }),
577
+ item.dot && !collapsed ? /* @__PURE__ */ jsx4("span", { "data-slot": "menu-item-dot", className: "size-1.5 shrink-0 rounded-full bg-destructive" }) : null,
578
+ item.badge !== void 0 && !collapsed ? /* @__PURE__ */ jsx4(
579
+ "span",
580
+ {
581
+ "data-slot": "menu-item-badge",
582
+ className: "shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-xs text-muted-foreground",
583
+ children: item.badge
584
+ }
585
+ ) : null
586
+ ]
587
+ }
588
+ );
589
+ return /* @__PURE__ */ jsxs3("div", { "data-slot": "menu-entry", "data-key": item.key, children: [
590
+ collapsed ? /* @__PURE__ */ jsx4(RailTooltip, { label: item.label, children: button }) : button,
591
+ item.children?.map((child) => /* @__PURE__ */ jsx4(
592
+ MenuEntry,
593
+ {
594
+ item: child,
595
+ mode,
596
+ depth: depth + 1,
597
+ selectedKey,
598
+ onSelect,
599
+ collapsed
600
+ },
601
+ child.key
602
+ ))
603
+ ] });
604
+ }
605
+
606
+ export {
607
+ ICONS,
608
+ normalizeIconName,
609
+ Icon,
610
+ RailTooltip,
611
+ useSidebarDrawer,
612
+ Sidebar,
613
+ Menu2 as Menu
614
+ };