@pactor-app/ui 1.44.0 → 1.47.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,4 +1,12 @@
1
1
  import {
2
+ DropdownMenu,
3
+ DropdownMenuContent,
4
+ DropdownMenuItem,
5
+ DropdownMenuSeparator,
6
+ DropdownMenuSub,
7
+ DropdownMenuSubContent,
8
+ DropdownMenuSubTrigger,
9
+ DropdownMenuTrigger,
2
10
  Popover,
3
11
  PopoverContent,
4
12
  PopoverTrigger,
@@ -16,7 +24,7 @@ import {
16
24
  TooltipTrigger,
17
25
  useIsMobile,
18
26
  useSidebar
19
- } from "./chunk-7OBUSV5S.js";
27
+ } from "./chunk-YHTRCBKF.js";
20
28
  import {
21
29
  cn
22
30
  } from "./chunk-WKJUCGV4.js";
@@ -85,10 +93,12 @@ import {
85
93
  LogOut,
86
94
  Mail,
87
95
  MapPin,
96
+ Maximize2,
88
97
  Menu,
89
98
  MessageCircle,
90
99
  MessageSquare,
91
100
  Mic,
101
+ Minimize2,
92
102
  Minus,
93
103
  Moon,
94
104
  Package,
@@ -255,6 +265,8 @@ var ICONS = {
255
265
  "chart-column": ChartColumn,
256
266
  "layout-grid": LayoutGrid,
257
267
  "layout-template": LayoutTemplate,
268
+ "maximize-2": Maximize2,
269
+ "minimize-2": Minimize2,
258
270
  palette: Palette,
259
271
  paintbrush: Paintbrush,
260
272
  funnel: Funnel,
@@ -321,15 +333,79 @@ function Icon({
321
333
  );
322
334
  }
323
335
 
336
+ // src/components/components/trigger-render.tsx
337
+ import { jsx as jsx2 } from "react/jsx-runtime";
338
+ function triggerWrapper(slot) {
339
+ return /* @__PURE__ */ jsx2("span", { "data-slot": slot });
340
+ }
341
+
342
+ // src/components/components/DropdownMenu/index.tsx
343
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
344
+ function renderDropdownItems(items, onSelect) {
345
+ const checkable = items.some((i) => i.selected);
346
+ return items.map((item, index) => {
347
+ const key = item.value ?? `item-${index}`;
348
+ if (item.separator) {
349
+ return /* @__PURE__ */ jsx3(DropdownMenuSeparator, {}, `separator-${index}`);
350
+ }
351
+ const leading = item.selected ? /* @__PURE__ */ jsx3(Icon, { name: "check", size: "sm" }) : item.icon ? /* @__PURE__ */ jsx3(Icon, { name: item.icon, size: "sm" }) : checkable ? /* @__PURE__ */ jsx3("span", { className: "size-4 shrink-0" }) : null;
352
+ if (item.children && item.children.length > 0) {
353
+ return /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
354
+ /* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { disabled: item.disabled, children: [
355
+ leading,
356
+ item.label,
357
+ item.trailing ? /* @__PURE__ */ jsx3("span", { className: "ml-auto pl-4 text-xs text-muted-foreground", children: item.trailing }) : null
358
+ ] }),
359
+ /* @__PURE__ */ jsx3(DropdownMenuSubContent, { children: renderDropdownItems(item.children, onSelect) })
360
+ ] }, key);
361
+ }
362
+ return /* @__PURE__ */ jsxs(
363
+ DropdownMenuItem,
364
+ {
365
+ variant: item.danger ? "destructive" : "default",
366
+ disabled: item.disabled,
367
+ onClick: () => item.value !== void 0 && onSelect?.(item.value),
368
+ children: [
369
+ leading,
370
+ item.label,
371
+ item.trailing ? /* @__PURE__ */ jsx3("span", { className: "ml-auto pl-4 text-xs text-muted-foreground", children: item.trailing }) : null
372
+ ]
373
+ },
374
+ key
375
+ );
376
+ });
377
+ }
378
+ function DropdownMenu2({
379
+ items = [],
380
+ onSelect,
381
+ children,
382
+ side,
383
+ align,
384
+ className,
385
+ style
386
+ }) {
387
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
388
+ /* @__PURE__ */ jsx3(
389
+ DropdownMenuTrigger,
390
+ {
391
+ nativeButton: false,
392
+ render: triggerWrapper("dropdown-menu-trigger-wrap"),
393
+ children
394
+ }
395
+ ),
396
+ /* @__PURE__ */ jsx3(DropdownMenuContent, { className, style, side, align, children: renderDropdownItems(items, onSelect) })
397
+ ] });
398
+ }
399
+
324
400
  // src/components/components/Sidebar/rail-tooltip.tsx
325
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
401
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
326
402
  function RailTooltip({
327
403
  label,
328
404
  children
329
405
  }) {
330
- return /* @__PURE__ */ jsxs(Tooltip, { children: [
331
- /* @__PURE__ */ jsx2(TooltipTrigger, { render: children }),
332
- /* @__PURE__ */ jsx2(TooltipContent, { side: "right", sideOffset: 8, children: label })
406
+ return /* @__PURE__ */ jsxs2(Tooltip, { children: [
407
+ /* @__PURE__ */ jsx4(TooltipTrigger, { render: children }),
408
+ /* @__PURE__ */ jsx4(TooltipContent, { side: "right", sideOffset: 8, children: label })
333
409
  ] });
334
410
  }
335
411
 
@@ -339,7 +415,7 @@ import {
339
415
  useEffect,
340
416
  useRef
341
417
  } from "react";
342
- import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
418
+ import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
343
419
  function useSidebarDrawer() {
344
420
  try {
345
421
  const ctx = useSidebar();
@@ -361,9 +437,9 @@ function useOptionalRenderer() {
361
437
  function SidebarContentSlot({ content }) {
362
438
  const renderer = useOptionalRenderer();
363
439
  if (renderer !== null && typeof content === "object" && content !== null && !("$$typeof" in content) && "type" in content) {
364
- return /* @__PURE__ */ jsx3(Fragment, { children: renderer.renderChildren([content]) });
440
+ return /* @__PURE__ */ jsx5(Fragment, { children: renderer.renderChildren([content]) });
365
441
  }
366
- return /* @__PURE__ */ jsx3(Fragment, { children: content });
442
+ return /* @__PURE__ */ jsx5(Fragment, { children: content });
367
443
  }
368
444
  function isBrandObject(brand) {
369
445
  return typeof brand === "object" && brand !== null && !("$$typeof" in brand) && "title" in brand;
@@ -373,8 +449,8 @@ function BrandNode({ brand, collapsed }) {
373
449
  return null;
374
450
  }
375
451
  if (isBrandObject(brand)) {
376
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
377
- brand.logo ? /* @__PURE__ */ jsx3(
452
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
453
+ brand.logo ? /* @__PURE__ */ jsx5(
378
454
  "img",
379
455
  {
380
456
  "data-slot": "sidebar-brand-logo",
@@ -383,27 +459,27 @@ function BrandNode({ brand, collapsed }) {
383
459
  className: "size-6 shrink-0"
384
460
  }
385
461
  ) : null,
386
- collapsed ? null : /* @__PURE__ */ jsx3("span", { className: "truncate text-sm font-semibold", children: brand.title })
462
+ collapsed ? null : /* @__PURE__ */ jsx5("span", { className: "truncate text-sm font-semibold", children: brand.title })
387
463
  ] });
388
464
  }
389
- return /* @__PURE__ */ jsx3(Fragment, { children: brand });
465
+ return /* @__PURE__ */ jsx5(Fragment, { children: brand });
390
466
  }
391
467
  function SidebarNavItems({ items }) {
392
- return /* @__PURE__ */ jsx3(SidebarMenu, { children: items.map((item, index) => /* @__PURE__ */ jsx3(SidebarNavEntry, { item, depth: 0 }, item.href ?? item.label ?? index)) });
468
+ return /* @__PURE__ */ jsx5(SidebarMenu, { children: items.map((item, index) => /* @__PURE__ */ jsx5(SidebarNavEntry, { item, depth: 0 }, item.href ?? item.label ?? index)) });
393
469
  }
394
470
  function SidebarNavEntry({ item, depth }) {
395
- const iconNode = item.icon ? /* @__PURE__ */ jsx3(Icon, { name: item.icon, size: "sm" }) : null;
471
+ const iconNode = item.icon ? /* @__PURE__ */ jsx5(Icon, { name: item.icon, size: "sm" }) : null;
396
472
  const labelNode = item.label;
397
- const button = item.href ? /* @__PURE__ */ jsx3(SidebarMenuButton, { asChild: true, tooltip: item.label, children: /* @__PURE__ */ jsxs2("a", { href: item.href, children: [
473
+ const button = item.href ? /* @__PURE__ */ jsx5(SidebarMenuButton, { asChild: true, tooltip: item.label, children: /* @__PURE__ */ jsxs3("a", { href: item.href, children: [
398
474
  iconNode,
399
- /* @__PURE__ */ jsx3("span", { children: labelNode })
400
- ] }) }) : /* @__PURE__ */ jsxs2(SidebarMenuButton, { tooltip: item.label, children: [
475
+ /* @__PURE__ */ jsx5("span", { children: labelNode })
476
+ ] }) }) : /* @__PURE__ */ jsxs3(SidebarMenuButton, { tooltip: item.label, children: [
401
477
  iconNode,
402
- /* @__PURE__ */ jsx3("span", { children: labelNode })
478
+ /* @__PURE__ */ jsx5("span", { children: labelNode })
403
479
  ] });
404
- return /* @__PURE__ */ jsxs2(SidebarMenuItem, { children: [
480
+ return /* @__PURE__ */ jsxs3(SidebarMenuItem, { children: [
405
481
  button,
406
- item.children && item.children.length > 0 && /* @__PURE__ */ jsx3("div", { className: cn("ml-4"), children: item.children.map((child, index) => /* @__PURE__ */ jsx3(
482
+ item.children && item.children.length > 0 && /* @__PURE__ */ jsx5("div", { className: cn("ml-4"), children: item.children.map((child, index) => /* @__PURE__ */ jsx5(
407
483
  SidebarNavEntry,
408
484
  {
409
485
  item: child,
@@ -427,13 +503,13 @@ function Sidebar({
427
503
  style,
428
504
  children
429
505
  }) {
430
- return /* @__PURE__ */ jsx3(
506
+ return /* @__PURE__ */ jsx5(
431
507
  SidebarProvider,
432
508
  {
433
509
  defaultOpen: !defaultCollapsed,
434
510
  className,
435
511
  style,
436
- children: /* @__PURE__ */ jsx3(
512
+ children: /* @__PURE__ */ jsx5(
437
513
  SidebarInner,
438
514
  {
439
515
  brand,
@@ -478,20 +554,20 @@ function SidebarInner({
478
554
  }
479
555
  }, [locationKey, setOpenMobile]);
480
556
  const collapsedEffective = collapsible === true && collapsed && !isMobile;
481
- const brandNode = /* @__PURE__ */ jsx3(BrandNode, { brand, collapsed: collapsedEffective });
557
+ const brandNode = /* @__PURE__ */ jsx5(BrandNode, { brand, collapsed: collapsedEffective });
482
558
  const collapsedBrandLogo = collapsedEffective && isBrandObject(brand) && brand.logo ? brand.logo : null;
483
- const sidebarContent = content !== void 0 && content !== null ? /* @__PURE__ */ jsx3(SidebarContentSlot, { content }) : items.length > 0 ? /* @__PURE__ */ jsx3(SidebarNavItems, { items }) : null;
484
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
485
- /* @__PURE__ */ jsxs2(
559
+ const sidebarContent = content !== void 0 && content !== null ? /* @__PURE__ */ jsx5(SidebarContentSlot, { content }) : items.length > 0 ? /* @__PURE__ */ jsx5(SidebarNavItems, { items }) : null;
560
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
561
+ /* @__PURE__ */ jsxs3(
486
562
  SidebarRoot,
487
563
  {
488
564
  collapsible: collapsible ? "icon" : "offcanvas",
489
565
  children: [
490
- (brand !== void 0 || collapsible) && /* @__PURE__ */ jsx3(
566
+ (brand !== void 0 || collapsible) && /* @__PURE__ */ jsx5(
491
567
  SidebarHeader,
492
568
  {
493
569
  className: brand === void 0 ? "h-9" : void 0,
494
- children: /* @__PURE__ */ jsxs2(
570
+ children: /* @__PURE__ */ jsxs3(
495
571
  "div",
496
572
  {
497
573
  "data-slot": "sidebar-brand",
@@ -502,7 +578,7 @@ function SidebarInner({
502
578
  collapsedBrandLogo && "h-auto flex-col gap-1 py-1.5"
503
579
  ),
504
580
  children: [
505
- collapsedEffective ? collapsedBrandLogo && isBrandObject(brand) ? /* @__PURE__ */ jsx3(
581
+ collapsedEffective ? collapsedBrandLogo && isBrandObject(brand) ? /* @__PURE__ */ jsx5(
506
582
  "img",
507
583
  {
508
584
  "data-slot": "sidebar-brand-logo",
@@ -510,8 +586,8 @@ function SidebarInner({
510
586
  alt: brand.title,
511
587
  className: "size-6 shrink-0"
512
588
  }
513
- ) : null : /* @__PURE__ */ jsx3("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: brandNode }),
514
- collapsible && !isMobile && /* @__PURE__ */ jsx3(
589
+ ) : null : /* @__PURE__ */ jsx5("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: brandNode }),
590
+ collapsible && !isMobile && /* @__PURE__ */ jsx5(
515
591
  "button",
516
592
  {
517
593
  type: "button",
@@ -520,7 +596,7 @@ function SidebarInner({
520
596
  "aria-expanded": !collapsedEffective,
521
597
  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",
522
598
  onClick: toggleSidebar,
523
- children: /* @__PURE__ */ jsx3(Icon, { name: collapsedEffective ? "chevron-right" : "chevron-left", size: 16 })
599
+ children: /* @__PURE__ */ jsx5(Icon, { name: collapsedEffective ? "chevron-right" : "chevron-left", size: 16 })
524
600
  }
525
601
  )
526
602
  ]
@@ -528,27 +604,27 @@ function SidebarInner({
528
604
  )
529
605
  }
530
606
  ),
531
- /* @__PURE__ */ jsx3(SidebarContent, { children: sidebarContent }),
532
- sidebarFooter !== void 0 && sidebarFooter !== null && /* @__PURE__ */ jsx3(
607
+ /* @__PURE__ */ jsx5(SidebarContent, { children: sidebarContent }),
608
+ sidebarFooter !== void 0 && sidebarFooter !== null && /* @__PURE__ */ jsx5(
533
609
  SidebarFooter,
534
610
  {
535
611
  className: cn(
536
612
  collapsedEffective && "items-center px-0 [&_[data-slot=flex]]:justify-center [&_[data-slot=icon]]:hidden [&_[data-slot=text]]:hidden"
537
613
  ),
538
- children: /* @__PURE__ */ jsx3(SidebarContentSlot, { content: sidebarFooter })
614
+ children: /* @__PURE__ */ jsx5(SidebarContentSlot, { content: sidebarFooter })
539
615
  }
540
616
  )
541
617
  ]
542
618
  }
543
619
  ),
544
- /* @__PURE__ */ jsxs2(SidebarInset, { children: [
545
- drawer && /* @__PURE__ */ jsxs2(
620
+ /* @__PURE__ */ jsxs3(SidebarInset, { children: [
621
+ drawer && /* @__PURE__ */ jsxs3(
546
622
  "div",
547
623
  {
548
624
  "data-slot": "sidebar-topbar",
549
625
  className: "flex h-14 items-center gap-2 border-b border-sidebar-border bg-sidebar px-3 md:hidden",
550
626
  children: [
551
- /* @__PURE__ */ jsx3(
627
+ /* @__PURE__ */ jsx5(
552
628
  "button",
553
629
  {
554
630
  type: "button",
@@ -556,7 +632,7 @@ function SidebarInner({
556
632
  "aria-label": t("pactor.layout.openNavigation"),
557
633
  className: "flex size-8 items-center justify-center rounded-md text-sidebar-foreground/60 transition-colors hover:bg-sidebar-accent hover:text-sidebar-foreground",
558
634
  onClick: toggleSidebar,
559
- children: /* @__PURE__ */ jsx3(Icon, { name: "menu", size: 18 })
635
+ children: /* @__PURE__ */ jsx5(Icon, { name: "menu", size: 18 })
560
636
  }
561
637
  ),
562
638
  brandNode
@@ -568,9 +644,18 @@ function SidebarInner({
568
644
  ] });
569
645
  }
570
646
 
647
+ // src/components/components/Tooltip/index.tsx
648
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
649
+ function Tooltip2({ content, side, children, className, style }) {
650
+ return /* @__PURE__ */ jsxs4(Tooltip, { children: [
651
+ /* @__PURE__ */ jsx6(TooltipTrigger, { render: triggerWrapper("tooltip-trigger-wrap"), children }),
652
+ /* @__PURE__ */ jsx6(TooltipContent, { side, className, style, children: content })
653
+ ] });
654
+ }
655
+
571
656
  // src/components/components/Menu/index.tsx
572
657
  import { useEffect as useEffect2, useRef as useRef2, useState } from "react";
573
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
658
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
574
659
  function collectAncestors(items, target, into, trail = []) {
575
660
  for (const item of items) {
576
661
  if (item.key === target) {
@@ -590,22 +675,32 @@ function Menu2({
590
675
  collapsible = false,
591
676
  collapsed: collapsedProp,
592
677
  defaultExpandedKeys,
678
+ defaultExpandAll = false,
679
+ chevronPlacement = "right",
593
680
  onSelect,
681
+ onItemAction,
594
682
  className,
595
683
  style
596
684
  }) {
597
685
  const drawer = useSidebarDrawer();
598
686
  const collapsed = collapsedProp ?? (mode === "inline" && drawer?.collapsed === true);
599
687
  const collapsibleOn = collapsible && mode === "inline" && !collapsed;
600
- const [expandedKeys, setExpandedKeys] = useState(() => {
688
+ const [baseExpandedKeys] = useState(() => {
601
689
  const initial = new Set(defaultExpandedKeys ?? []);
602
690
  if (selectedKey !== void 0) {
603
691
  collectAncestors(items, selectedKey, initial);
604
692
  }
605
693
  return initial;
606
694
  });
695
+ const [toggledKeys, setToggledKeys] = useState(
696
+ () => /* @__PURE__ */ new Set()
697
+ );
698
+ const isExpanded = (key) => {
699
+ const base = defaultExpandAll || baseExpandedKeys.has(key);
700
+ return toggledKeys.has(key) ? !base : base;
701
+ };
607
702
  const toggleExpanded = (key) => {
608
- setExpandedKeys((prev) => {
703
+ setToggledKeys((prev) => {
609
704
  const next = new Set(prev);
610
705
  if (next.has(key)) {
611
706
  next.delete(key);
@@ -616,7 +711,7 @@ function Menu2({
616
711
  });
617
712
  };
618
713
  let previousGroup;
619
- return /* @__PURE__ */ jsx4(
714
+ return /* @__PURE__ */ jsx7(
620
715
  "nav",
621
716
  {
622
717
  "data-slot": "menu",
@@ -633,8 +728,8 @@ function Menu2({
633
728
  groupLabel = item.group;
634
729
  }
635
730
  previousGroup = item.group;
636
- return /* @__PURE__ */ jsxs3("div", { "data-slot": "menu-group-entry", children: [
637
- groupLabel !== void 0 && /* @__PURE__ */ jsx4(
731
+ return /* @__PURE__ */ jsxs5("div", { "data-slot": "menu-group-entry", children: [
732
+ groupLabel !== void 0 && /* @__PURE__ */ jsx7(
638
733
  "div",
639
734
  {
640
735
  "data-slot": "menu-group",
@@ -642,7 +737,7 @@ function Menu2({
642
737
  children: groupLabel
643
738
  }
644
739
  ),
645
- /* @__PURE__ */ jsx4(
740
+ /* @__PURE__ */ jsx7(
646
741
  MenuEntry,
647
742
  {
648
743
  item,
@@ -650,9 +745,11 @@ function Menu2({
650
745
  depth: 0,
651
746
  selectedKey,
652
747
  onSelect,
748
+ onItemAction,
653
749
  collapsed,
654
750
  collapsible: collapsibleOn,
655
- expandedKeys,
751
+ chevronPlacement,
752
+ isExpanded,
656
753
  onToggle: toggleExpanded
657
754
  }
658
755
  )
@@ -667,16 +764,20 @@ function MenuEntry({
667
764
  depth,
668
765
  selectedKey,
669
766
  onSelect,
767
+ onItemAction,
670
768
  collapsed = false,
671
769
  collapsible = false,
672
- expandedKeys,
770
+ chevronPlacement = "right",
771
+ isExpanded,
673
772
  onToggle
674
773
  }) {
675
- const selected = item.key === selectedKey;
676
774
  const hasChildren = (item.children?.length ?? 0) > 0;
677
- const isDirectory = collapsible && hasChildren;
678
- const expanded = isDirectory && expandedKeys?.has(item.key) === true;
775
+ const isDirectory = item.children !== void 0 && mode === "inline" && !collapsed;
776
+ const canToggle = collapsible && isDirectory && hasChildren;
777
+ const expanded = canToggle && isExpanded?.(item.key) === true;
778
+ const selected = !isDirectory && item.key === selectedKey;
679
779
  const railDirectory = collapsed && hasChildren;
780
+ const hasRowExtras = !collapsed && (item.trailing !== void 0 || (item.actions?.length ?? 0) > 0);
680
781
  const [flyoutOpen, setFlyoutOpen] = useState(false);
681
782
  const flyoutTimer = useRef2(void 0);
682
783
  const flyoutOpenNow = () => {
@@ -688,20 +789,37 @@ function MenuEntry({
688
789
  flyoutTimer.current = window.setTimeout(() => setFlyoutOpen(false), 200);
689
790
  };
690
791
  useEffect2(() => () => window.clearTimeout(flyoutTimer.current), []);
691
- const button = /* @__PURE__ */ jsxs3(
792
+ const chevron = canToggle ? /* @__PURE__ */ jsx7(
793
+ "span",
794
+ {
795
+ "data-slot": "menu-item-chevron",
796
+ className: cn(
797
+ "shrink-0 transition-transform",
798
+ expanded && "rotate-180"
799
+ ),
800
+ children: /* @__PURE__ */ jsx7(Icon, { name: "chevron-down", size: "sm" })
801
+ }
802
+ ) : null;
803
+ const button = /* @__PURE__ */ jsxs5(
692
804
  "button",
693
805
  {
694
806
  type: "button",
695
807
  "data-slot": "menu-item",
696
808
  "aria-current": selected ? "page" : void 0,
697
- "aria-expanded": isDirectory ? expanded : railDirectory ? flyoutOpen : void 0,
809
+ "aria-expanded": canToggle ? expanded : railDirectory ? flyoutOpen : void 0,
698
810
  disabled: item.disabled,
699
811
  className: cn(
700
- "flex w-full items-center gap-2 rounded-md text-sm transition-colors",
812
+ // w-full 兜底非 flex 父容器(rail / flyout);flex-1 min-w-0 供
813
+ // 行容器(trailing / actions 与按钮同级)内收缩
814
+ "flex w-full min-w-0 flex-1 items-center gap-2 text-sm transition-colors",
701
815
  mode === "horizontal" ? "px-3 py-2" : "px-3 py-2 text-left",
702
816
  collapsed && "justify-center px-0",
703
- selected ? "bg-(--menu-active-background) font-medium text-(--menu-active-foreground)" : "cursor-pointer text-foreground hover:bg-(--menu-hover-background)/50",
704
- item.disabled && "cursor-not-allowed opacity-50 hover:bg-transparent hover:text-muted-foreground"
817
+ // 行容器形态下圆角与悬浮/选中底色由 menu-item-row 承担,按钮本体透明
818
+ hasRowExtras ? item.disabled ? "cursor-not-allowed" : "cursor-pointer" : cn(
819
+ "rounded-md",
820
+ selected ? "bg-(--menu-active-background) font-medium text-(--menu-active-foreground)" : "cursor-pointer text-foreground hover:bg-(--menu-hover-background)/50",
821
+ item.disabled && "cursor-not-allowed opacity-50 hover:bg-transparent hover:text-muted-foreground"
822
+ )
705
823
  ),
706
824
  style: mode === "inline" && depth > 0 && !collapsed ? { paddingLeft: 12 + depth * 16 } : void 0,
707
825
  onMouseEnter: railDirectory ? flyoutOpenNow : void 0,
@@ -710,16 +828,17 @@ function MenuEntry({
710
828
  if (item.disabled) return;
711
829
  if (railDirectory) return;
712
830
  if (isDirectory) {
713
- onToggle?.(item.key);
714
- } else {
715
- onSelect?.(item.key);
831
+ if (canToggle) onToggle?.(item.key);
832
+ return;
716
833
  }
834
+ onSelect?.(item.key);
717
835
  },
718
836
  children: [
719
- item.icon ? /* @__PURE__ */ jsx4(Icon, { name: item.icon, size: "sm" }) : null,
720
- collapsed ? null : /* @__PURE__ */ jsx4("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label }),
721
- item.dot && !collapsed ? /* @__PURE__ */ jsx4("span", { "data-slot": "menu-item-dot", className: "size-1.5 shrink-0 rounded-full bg-destructive" }) : null,
722
- item.badge !== void 0 && !collapsed ? /* @__PURE__ */ jsx4(
837
+ chevronPlacement === "left" ? chevron : null,
838
+ item.icon ? /* @__PURE__ */ jsx7(Icon, { name: item.icon, size: "sm" }) : null,
839
+ collapsed ? null : /* @__PURE__ */ jsx7("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label }),
840
+ item.dot && !collapsed ? /* @__PURE__ */ jsx7("span", { "data-slot": "menu-item-dot", className: "size-1.5 shrink-0 rounded-full bg-destructive" }) : null,
841
+ item.badge !== void 0 && !collapsed ? /* @__PURE__ */ jsx7(
723
842
  "span",
724
843
  {
725
844
  "data-slot": "menu-item-badge",
@@ -727,24 +846,14 @@ function MenuEntry({
727
846
  children: item.badge
728
847
  }
729
848
  ) : null,
730
- isDirectory ? /* @__PURE__ */ jsx4(
731
- "span",
732
- {
733
- "data-slot": "menu-item-chevron",
734
- className: cn(
735
- "shrink-0 transition-transform",
736
- expanded && "rotate-180"
737
- ),
738
- children: /* @__PURE__ */ jsx4(Icon, { name: "chevron-down", size: "sm" })
739
- }
740
- ) : null
849
+ chevronPlacement === "left" ? null : chevron
741
850
  ]
742
851
  }
743
852
  );
744
- return /* @__PURE__ */ jsxs3("div", { "data-slot": "menu-entry", "data-key": item.key, children: [
745
- railDirectory ? /* @__PURE__ */ jsxs3(Popover, { open: flyoutOpen, onOpenChange: setFlyoutOpen, children: [
746
- /* @__PURE__ */ jsx4(PopoverTrigger, { render: button }),
747
- /* @__PURE__ */ jsxs3(
853
+ return /* @__PURE__ */ jsxs5("div", { "data-slot": "menu-entry", "data-key": item.key, children: [
854
+ railDirectory ? /* @__PURE__ */ jsxs5(Popover, { open: flyoutOpen, onOpenChange: setFlyoutOpen, children: [
855
+ /* @__PURE__ */ jsx7(PopoverTrigger, { render: button }),
856
+ /* @__PURE__ */ jsxs5(
748
857
  PopoverContent,
749
858
  {
750
859
  "data-slot": "menu-flyout",
@@ -761,23 +870,67 @@ function MenuEntry({
761
870
  }
762
871
  },
763
872
  children: [
764
- /* @__PURE__ */ jsx4("div", { className: "px-3 pt-2 pb-1 text-xs font-semibold text-muted-foreground", children: item.label }),
765
- item.children?.map((child) => /* @__PURE__ */ jsx4(
873
+ /* @__PURE__ */ jsx7("div", { className: "px-3 pt-2 pb-1 text-xs font-semibold text-muted-foreground", children: item.label }),
874
+ item.children?.map((child) => /* @__PURE__ */ jsx7(
766
875
  MenuEntry,
767
876
  {
768
877
  item: child,
769
878
  mode: "inline",
770
879
  depth: 0,
771
880
  selectedKey,
772
- onSelect
881
+ onSelect,
882
+ onItemAction
773
883
  },
774
884
  child.key
775
885
  ))
776
886
  ]
777
887
  }
778
888
  )
779
- ] }) : collapsed ? /* @__PURE__ */ jsx4(RailTooltip, { label: item.label, children: button }) : button,
780
- railDirectory || isDirectory && !expanded ? null : item.children?.map((child) => /* @__PURE__ */ jsx4(
889
+ ] }) : collapsed ? /* @__PURE__ */ jsx7(RailTooltip, { label: item.label, children: button }) : hasRowExtras ? (
890
+ // 行容器:按钮主体 + 右端弱提示文本 / 操作按钮(同级节点,避免按钮
891
+ // 嵌套按钮;操作点击不冒泡到 onSelect / 目录 toggle)。悬浮/选中
892
+ // 底色覆盖整行,文字与右端操作不产生割裂感
893
+ /* @__PURE__ */ jsxs5(
894
+ "div",
895
+ {
896
+ "data-slot": "menu-item-row",
897
+ className: cn(
898
+ "flex w-full items-center gap-1 rounded-md pr-1.5 transition-colors",
899
+ selected ? "bg-(--menu-active-background) font-medium text-(--menu-active-foreground)" : "text-foreground hover:bg-(--menu-hover-background)/50",
900
+ item.disabled && "opacity-50"
901
+ ),
902
+ children: [
903
+ button,
904
+ item.trailing !== void 0 ? /* @__PURE__ */ jsx7(
905
+ "span",
906
+ {
907
+ "data-slot": "menu-item-trailing",
908
+ className: "shrink-0 text-xs text-muted-foreground",
909
+ children: item.trailing
910
+ }
911
+ ) : null,
912
+ (item.actions?.length ?? 0) > 0 ? /* @__PURE__ */ jsx7(
913
+ "span",
914
+ {
915
+ "data-slot": "menu-item-actions",
916
+ className: "flex shrink-0 items-center gap-0.5",
917
+ children: item.actions.map((action) => /* @__PURE__ */ jsx7(
918
+ MenuItemActionButton,
919
+ {
920
+ itemKey: item.key,
921
+ action,
922
+ disabled: item.disabled,
923
+ onItemAction
924
+ },
925
+ action.key
926
+ ))
927
+ }
928
+ ) : null
929
+ ]
930
+ }
931
+ )
932
+ ) : button,
933
+ railDirectory || canToggle && !expanded ? null : item.children?.map((child) => /* @__PURE__ */ jsx7(
781
934
  MenuEntry,
782
935
  {
783
936
  item: child,
@@ -785,22 +938,70 @@ function MenuEntry({
785
938
  depth: depth + 1,
786
939
  selectedKey,
787
940
  onSelect,
941
+ onItemAction,
788
942
  collapsed,
789
943
  collapsible,
790
- expandedKeys,
944
+ chevronPlacement,
945
+ isExpanded,
791
946
  onToggle
792
947
  },
793
948
  child.key
794
949
  ))
795
950
  ] });
796
951
  }
952
+ function MenuItemActionButton({
953
+ itemKey,
954
+ action,
955
+ disabled,
956
+ onItemAction
957
+ }) {
958
+ const button = /* @__PURE__ */ jsx7(
959
+ "button",
960
+ {
961
+ type: "button",
962
+ "data-slot": "menu-item-action",
963
+ "data-action": action.key,
964
+ disabled,
965
+ className: "shrink-0 cursor-pointer rounded p-0.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
966
+ onClick: (event) => {
967
+ if (action.menu) return;
968
+ event.stopPropagation();
969
+ if (disabled) return;
970
+ onItemAction?.({ key: itemKey, action: action.key });
971
+ },
972
+ children: /* @__PURE__ */ jsx7(Icon, { name: action.icon, size: "sm" })
973
+ }
974
+ );
975
+ if (!action.menu) {
976
+ return action.tooltip ? /* @__PURE__ */ jsx7(Tooltip2, { content: action.tooltip, children: button }) : button;
977
+ }
978
+ if (disabled) return button;
979
+ return /* @__PURE__ */ jsxs5(DropdownMenu, { children: [
980
+ /* @__PURE__ */ jsx7(
981
+ DropdownMenuTrigger,
982
+ {
983
+ nativeButton: false,
984
+ render: triggerWrapper("menu-item-action-trigger"),
985
+ children: button
986
+ }
987
+ ),
988
+ /* @__PURE__ */ jsx7(DropdownMenuContent, { side: "bottom", align: "end", className: "min-w-32", children: renderDropdownItems(
989
+ action.menu,
990
+ (value) => onItemAction?.({ key: itemKey, action: `${action.key}:${value}` })
991
+ ) })
992
+ ] });
993
+ }
797
994
 
798
995
  export {
799
996
  ICONS,
800
997
  normalizeIconName,
801
998
  Icon,
999
+ triggerWrapper,
1000
+ renderDropdownItems,
1001
+ DropdownMenu2 as DropdownMenu,
802
1002
  RailTooltip,
803
1003
  useSidebarDrawer,
804
1004
  Sidebar,
1005
+ Tooltip2 as Tooltip,
805
1006
  Menu2 as Menu
806
1007
  };