@pactor-app/ui 1.46.0 → 1.48.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";
@@ -325,15 +333,79 @@ function Icon({
325
333
  );
326
334
  }
327
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
+
328
400
  // src/components/components/Sidebar/rail-tooltip.tsx
329
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
401
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
330
402
  function RailTooltip({
331
403
  label,
332
404
  children
333
405
  }) {
334
- return /* @__PURE__ */ jsxs(Tooltip, { children: [
335
- /* @__PURE__ */ jsx2(TooltipTrigger, { render: children }),
336
- /* @__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 })
337
409
  ] });
338
410
  }
339
411
 
@@ -343,7 +415,7 @@ import {
343
415
  useEffect,
344
416
  useRef
345
417
  } from "react";
346
- 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";
347
419
  function useSidebarDrawer() {
348
420
  try {
349
421
  const ctx = useSidebar();
@@ -365,9 +437,9 @@ function useOptionalRenderer() {
365
437
  function SidebarContentSlot({ content }) {
366
438
  const renderer = useOptionalRenderer();
367
439
  if (renderer !== null && typeof content === "object" && content !== null && !("$$typeof" in content) && "type" in content) {
368
- return /* @__PURE__ */ jsx3(Fragment, { children: renderer.renderChildren([content]) });
440
+ return /* @__PURE__ */ jsx5(Fragment, { children: renderer.renderChildren([content]) });
369
441
  }
370
- return /* @__PURE__ */ jsx3(Fragment, { children: content });
442
+ return /* @__PURE__ */ jsx5(Fragment, { children: content });
371
443
  }
372
444
  function isBrandObject(brand) {
373
445
  return typeof brand === "object" && brand !== null && !("$$typeof" in brand) && "title" in brand;
@@ -377,8 +449,8 @@ function BrandNode({ brand, collapsed }) {
377
449
  return null;
378
450
  }
379
451
  if (isBrandObject(brand)) {
380
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
381
- brand.logo ? /* @__PURE__ */ jsx3(
452
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
453
+ brand.logo ? /* @__PURE__ */ jsx5(
382
454
  "img",
383
455
  {
384
456
  "data-slot": "sidebar-brand-logo",
@@ -387,27 +459,27 @@ function BrandNode({ brand, collapsed }) {
387
459
  className: "size-6 shrink-0"
388
460
  }
389
461
  ) : null,
390
- 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 })
391
463
  ] });
392
464
  }
393
- return /* @__PURE__ */ jsx3(Fragment, { children: brand });
465
+ return /* @__PURE__ */ jsx5(Fragment, { children: brand });
394
466
  }
395
467
  function SidebarNavItems({ items }) {
396
- 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)) });
397
469
  }
398
470
  function SidebarNavEntry({ item, depth }) {
399
- 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;
400
472
  const labelNode = item.label;
401
- 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: [
402
474
  iconNode,
403
- /* @__PURE__ */ jsx3("span", { children: labelNode })
404
- ] }) }) : /* @__PURE__ */ jsxs2(SidebarMenuButton, { tooltip: item.label, children: [
475
+ /* @__PURE__ */ jsx5("span", { children: labelNode })
476
+ ] }) }) : /* @__PURE__ */ jsxs3(SidebarMenuButton, { tooltip: item.label, children: [
405
477
  iconNode,
406
- /* @__PURE__ */ jsx3("span", { children: labelNode })
478
+ /* @__PURE__ */ jsx5("span", { children: labelNode })
407
479
  ] });
408
- return /* @__PURE__ */ jsxs2(SidebarMenuItem, { children: [
480
+ return /* @__PURE__ */ jsxs3(SidebarMenuItem, { children: [
409
481
  button,
410
- 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(
411
483
  SidebarNavEntry,
412
484
  {
413
485
  item: child,
@@ -431,13 +503,13 @@ function Sidebar({
431
503
  style,
432
504
  children
433
505
  }) {
434
- return /* @__PURE__ */ jsx3(
506
+ return /* @__PURE__ */ jsx5(
435
507
  SidebarProvider,
436
508
  {
437
509
  defaultOpen: !defaultCollapsed,
438
510
  className,
439
511
  style,
440
- children: /* @__PURE__ */ jsx3(
512
+ children: /* @__PURE__ */ jsx5(
441
513
  SidebarInner,
442
514
  {
443
515
  brand,
@@ -482,20 +554,20 @@ function SidebarInner({
482
554
  }
483
555
  }, [locationKey, setOpenMobile]);
484
556
  const collapsedEffective = collapsible === true && collapsed && !isMobile;
485
- const brandNode = /* @__PURE__ */ jsx3(BrandNode, { brand, collapsed: collapsedEffective });
557
+ const brandNode = /* @__PURE__ */ jsx5(BrandNode, { brand, collapsed: collapsedEffective });
486
558
  const collapsedBrandLogo = collapsedEffective && isBrandObject(brand) && brand.logo ? brand.logo : null;
487
- const sidebarContent = content !== void 0 && content !== null ? /* @__PURE__ */ jsx3(SidebarContentSlot, { content }) : items.length > 0 ? /* @__PURE__ */ jsx3(SidebarNavItems, { items }) : null;
488
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
489
- /* @__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(
490
562
  SidebarRoot,
491
563
  {
492
564
  collapsible: collapsible ? "icon" : "offcanvas",
493
565
  children: [
494
- (brand !== void 0 || collapsible) && /* @__PURE__ */ jsx3(
566
+ (brand !== void 0 || collapsible) && /* @__PURE__ */ jsx5(
495
567
  SidebarHeader,
496
568
  {
497
569
  className: brand === void 0 ? "h-9" : void 0,
498
- children: /* @__PURE__ */ jsxs2(
570
+ children: /* @__PURE__ */ jsxs3(
499
571
  "div",
500
572
  {
501
573
  "data-slot": "sidebar-brand",
@@ -506,7 +578,7 @@ function SidebarInner({
506
578
  collapsedBrandLogo && "h-auto flex-col gap-1 py-1.5"
507
579
  ),
508
580
  children: [
509
- collapsedEffective ? collapsedBrandLogo && isBrandObject(brand) ? /* @__PURE__ */ jsx3(
581
+ collapsedEffective ? collapsedBrandLogo && isBrandObject(brand) ? /* @__PURE__ */ jsx5(
510
582
  "img",
511
583
  {
512
584
  "data-slot": "sidebar-brand-logo",
@@ -514,8 +586,8 @@ function SidebarInner({
514
586
  alt: brand.title,
515
587
  className: "size-6 shrink-0"
516
588
  }
517
- ) : null : /* @__PURE__ */ jsx3("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: brandNode }),
518
- 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(
519
591
  "button",
520
592
  {
521
593
  type: "button",
@@ -524,7 +596,7 @@ function SidebarInner({
524
596
  "aria-expanded": !collapsedEffective,
525
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",
526
598
  onClick: toggleSidebar,
527
- 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 })
528
600
  }
529
601
  )
530
602
  ]
@@ -532,27 +604,27 @@ function SidebarInner({
532
604
  )
533
605
  }
534
606
  ),
535
- /* @__PURE__ */ jsx3(SidebarContent, { children: sidebarContent }),
536
- sidebarFooter !== void 0 && sidebarFooter !== null && /* @__PURE__ */ jsx3(
607
+ /* @__PURE__ */ jsx5(SidebarContent, { children: sidebarContent }),
608
+ sidebarFooter !== void 0 && sidebarFooter !== null && /* @__PURE__ */ jsx5(
537
609
  SidebarFooter,
538
610
  {
539
611
  className: cn(
540
612
  collapsedEffective && "items-center px-0 [&_[data-slot=flex]]:justify-center [&_[data-slot=icon]]:hidden [&_[data-slot=text]]:hidden"
541
613
  ),
542
- children: /* @__PURE__ */ jsx3(SidebarContentSlot, { content: sidebarFooter })
614
+ children: /* @__PURE__ */ jsx5(SidebarContentSlot, { content: sidebarFooter })
543
615
  }
544
616
  )
545
617
  ]
546
618
  }
547
619
  ),
548
- /* @__PURE__ */ jsxs2(SidebarInset, { children: [
549
- drawer && /* @__PURE__ */ jsxs2(
620
+ /* @__PURE__ */ jsxs3(SidebarInset, { children: [
621
+ drawer && /* @__PURE__ */ jsxs3(
550
622
  "div",
551
623
  {
552
624
  "data-slot": "sidebar-topbar",
553
625
  className: "flex h-14 items-center gap-2 border-b border-sidebar-border bg-sidebar px-3 md:hidden",
554
626
  children: [
555
- /* @__PURE__ */ jsx3(
627
+ /* @__PURE__ */ jsx5(
556
628
  "button",
557
629
  {
558
630
  type: "button",
@@ -560,7 +632,7 @@ function SidebarInner({
560
632
  "aria-label": t("pactor.layout.openNavigation"),
561
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",
562
634
  onClick: toggleSidebar,
563
- children: /* @__PURE__ */ jsx3(Icon, { name: "menu", size: 18 })
635
+ children: /* @__PURE__ */ jsx5(Icon, { name: "menu", size: 18 })
564
636
  }
565
637
  ),
566
638
  brandNode
@@ -572,9 +644,18 @@ function SidebarInner({
572
644
  ] });
573
645
  }
574
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
+
575
656
  // src/components/components/Menu/index.tsx
576
657
  import { useEffect as useEffect2, useRef as useRef2, useState } from "react";
577
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
658
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
578
659
  function collectAncestors(items, target, into, trail = []) {
579
660
  for (const item of items) {
580
661
  if (item.key === target) {
@@ -594,22 +675,32 @@ function Menu2({
594
675
  collapsible = false,
595
676
  collapsed: collapsedProp,
596
677
  defaultExpandedKeys,
678
+ defaultExpandAll = false,
679
+ chevronPlacement = "right",
597
680
  onSelect,
681
+ onItemAction,
598
682
  className,
599
683
  style
600
684
  }) {
601
685
  const drawer = useSidebarDrawer();
602
686
  const collapsed = collapsedProp ?? (mode === "inline" && drawer?.collapsed === true);
603
687
  const collapsibleOn = collapsible && mode === "inline" && !collapsed;
604
- const [expandedKeys, setExpandedKeys] = useState(() => {
688
+ const [baseExpandedKeys] = useState(() => {
605
689
  const initial = new Set(defaultExpandedKeys ?? []);
606
690
  if (selectedKey !== void 0) {
607
691
  collectAncestors(items, selectedKey, initial);
608
692
  }
609
693
  return initial;
610
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
+ };
611
702
  const toggleExpanded = (key) => {
612
- setExpandedKeys((prev) => {
703
+ setToggledKeys((prev) => {
613
704
  const next = new Set(prev);
614
705
  if (next.has(key)) {
615
706
  next.delete(key);
@@ -620,7 +711,7 @@ function Menu2({
620
711
  });
621
712
  };
622
713
  let previousGroup;
623
- return /* @__PURE__ */ jsx4(
714
+ return /* @__PURE__ */ jsx7(
624
715
  "nav",
625
716
  {
626
717
  "data-slot": "menu",
@@ -637,8 +728,8 @@ function Menu2({
637
728
  groupLabel = item.group;
638
729
  }
639
730
  previousGroup = item.group;
640
- return /* @__PURE__ */ jsxs3("div", { "data-slot": "menu-group-entry", children: [
641
- groupLabel !== void 0 && /* @__PURE__ */ jsx4(
731
+ return /* @__PURE__ */ jsxs5("div", { "data-slot": "menu-group-entry", children: [
732
+ groupLabel !== void 0 && /* @__PURE__ */ jsx7(
642
733
  "div",
643
734
  {
644
735
  "data-slot": "menu-group",
@@ -646,7 +737,7 @@ function Menu2({
646
737
  children: groupLabel
647
738
  }
648
739
  ),
649
- /* @__PURE__ */ jsx4(
740
+ /* @__PURE__ */ jsx7(
650
741
  MenuEntry,
651
742
  {
652
743
  item,
@@ -654,9 +745,11 @@ function Menu2({
654
745
  depth: 0,
655
746
  selectedKey,
656
747
  onSelect,
748
+ onItemAction,
657
749
  collapsed,
658
750
  collapsible: collapsibleOn,
659
- expandedKeys,
751
+ chevronPlacement,
752
+ isExpanded,
660
753
  onToggle: toggleExpanded
661
754
  }
662
755
  )
@@ -671,16 +764,20 @@ function MenuEntry({
671
764
  depth,
672
765
  selectedKey,
673
766
  onSelect,
767
+ onItemAction,
674
768
  collapsed = false,
675
769
  collapsible = false,
676
- expandedKeys,
770
+ chevronPlacement = "right",
771
+ isExpanded,
677
772
  onToggle
678
773
  }) {
679
- const selected = item.key === selectedKey;
680
774
  const hasChildren = (item.children?.length ?? 0) > 0;
681
- const isDirectory = collapsible && hasChildren;
682
- 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;
683
779
  const railDirectory = collapsed && hasChildren;
780
+ const hasRowExtras = !collapsed && (item.trailing !== void 0 || (item.actions?.length ?? 0) > 0);
684
781
  const [flyoutOpen, setFlyoutOpen] = useState(false);
685
782
  const flyoutTimer = useRef2(void 0);
686
783
  const flyoutOpenNow = () => {
@@ -692,20 +789,37 @@ function MenuEntry({
692
789
  flyoutTimer.current = window.setTimeout(() => setFlyoutOpen(false), 200);
693
790
  };
694
791
  useEffect2(() => () => window.clearTimeout(flyoutTimer.current), []);
695
- 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(
696
804
  "button",
697
805
  {
698
806
  type: "button",
699
807
  "data-slot": "menu-item",
700
808
  "aria-current": selected ? "page" : void 0,
701
- "aria-expanded": isDirectory ? expanded : railDirectory ? flyoutOpen : void 0,
809
+ "aria-expanded": canToggle ? expanded : railDirectory ? flyoutOpen : void 0,
702
810
  disabled: item.disabled,
703
811
  className: cn(
704
- "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",
705
815
  mode === "horizontal" ? "px-3 py-2" : "px-3 py-2 text-left",
706
816
  collapsed && "justify-center px-0",
707
- selected ? "bg-(--menu-active-background) font-medium text-(--menu-active-foreground)" : "cursor-pointer text-foreground hover:bg-(--menu-hover-background)/50",
708
- 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
+ )
709
823
  ),
710
824
  style: mode === "inline" && depth > 0 && !collapsed ? { paddingLeft: 12 + depth * 16 } : void 0,
711
825
  onMouseEnter: railDirectory ? flyoutOpenNow : void 0,
@@ -714,16 +828,17 @@ function MenuEntry({
714
828
  if (item.disabled) return;
715
829
  if (railDirectory) return;
716
830
  if (isDirectory) {
717
- onToggle?.(item.key);
718
- } else {
719
- onSelect?.(item.key);
831
+ if (canToggle) onToggle?.(item.key);
832
+ return;
720
833
  }
834
+ onSelect?.(item.key);
721
835
  },
722
836
  children: [
723
- item.icon ? /* @__PURE__ */ jsx4(Icon, { name: item.icon, size: "sm" }) : null,
724
- collapsed ? null : /* @__PURE__ */ jsx4("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label }),
725
- item.dot && !collapsed ? /* @__PURE__ */ jsx4("span", { "data-slot": "menu-item-dot", className: "size-1.5 shrink-0 rounded-full bg-destructive" }) : null,
726
- 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(
727
842
  "span",
728
843
  {
729
844
  "data-slot": "menu-item-badge",
@@ -731,24 +846,14 @@ function MenuEntry({
731
846
  children: item.badge
732
847
  }
733
848
  ) : null,
734
- isDirectory ? /* @__PURE__ */ jsx4(
735
- "span",
736
- {
737
- "data-slot": "menu-item-chevron",
738
- className: cn(
739
- "shrink-0 transition-transform",
740
- expanded && "rotate-180"
741
- ),
742
- children: /* @__PURE__ */ jsx4(Icon, { name: "chevron-down", size: "sm" })
743
- }
744
- ) : null
849
+ chevronPlacement === "left" ? null : chevron
745
850
  ]
746
851
  }
747
852
  );
748
- return /* @__PURE__ */ jsxs3("div", { "data-slot": "menu-entry", "data-key": item.key, children: [
749
- railDirectory ? /* @__PURE__ */ jsxs3(Popover, { open: flyoutOpen, onOpenChange: setFlyoutOpen, children: [
750
- /* @__PURE__ */ jsx4(PopoverTrigger, { render: button }),
751
- /* @__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(
752
857
  PopoverContent,
753
858
  {
754
859
  "data-slot": "menu-flyout",
@@ -765,23 +870,67 @@ function MenuEntry({
765
870
  }
766
871
  },
767
872
  children: [
768
- /* @__PURE__ */ jsx4("div", { className: "px-3 pt-2 pb-1 text-xs font-semibold text-muted-foreground", children: item.label }),
769
- 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(
770
875
  MenuEntry,
771
876
  {
772
877
  item: child,
773
878
  mode: "inline",
774
879
  depth: 0,
775
880
  selectedKey,
776
- onSelect
881
+ onSelect,
882
+ onItemAction
777
883
  },
778
884
  child.key
779
885
  ))
780
886
  ]
781
887
  }
782
888
  )
783
- ] }) : collapsed ? /* @__PURE__ */ jsx4(RailTooltip, { label: item.label, children: button }) : button,
784
- 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(
785
934
  MenuEntry,
786
935
  {
787
936
  item: child,
@@ -789,22 +938,70 @@ function MenuEntry({
789
938
  depth: depth + 1,
790
939
  selectedKey,
791
940
  onSelect,
941
+ onItemAction,
792
942
  collapsed,
793
943
  collapsible,
794
- expandedKeys,
944
+ chevronPlacement,
945
+ isExpanded,
795
946
  onToggle
796
947
  },
797
948
  child.key
798
949
  ))
799
950
  ] });
800
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
+ }
801
994
 
802
995
  export {
803
996
  ICONS,
804
997
  normalizeIconName,
805
998
  Icon,
999
+ triggerWrapper,
1000
+ renderDropdownItems,
1001
+ DropdownMenu2 as DropdownMenu,
806
1002
  RailTooltip,
807
1003
  useSidebarDrawer,
808
1004
  Sidebar,
1005
+ Tooltip2 as Tooltip,
809
1006
  Menu2 as Menu
810
1007
  };