@almadar/ui 5.29.0 → 5.30.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.
@@ -21495,7 +21495,84 @@ var init_DashboardLayout = __esm({
21495
21495
  NavLinkBottom.displayName = "NavLinkBottom";
21496
21496
  }
21497
21497
  });
21498
- var Menu;
21498
+ function computeMenuStyle(position, triggerRect) {
21499
+ const isTop = position.startsWith("top");
21500
+ const isRight = position.endsWith("right") || position.endsWith("end");
21501
+ if (isTop) {
21502
+ return {
21503
+ top: triggerRect.top - MENU_GAP,
21504
+ transform: "translateY(-100%)",
21505
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
21506
+ };
21507
+ }
21508
+ return {
21509
+ top: triggerRect.bottom + MENU_GAP,
21510
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
21511
+ };
21512
+ }
21513
+ function SubMenu({
21514
+ items,
21515
+ itemRef,
21516
+ direction,
21517
+ eventBus
21518
+ }) {
21519
+ const [rect, setRect] = useState(null);
21520
+ useEffect(() => {
21521
+ if (itemRef) {
21522
+ setRect(itemRef.getBoundingClientRect());
21523
+ }
21524
+ }, [itemRef]);
21525
+ if (!rect) return null;
21526
+ const isRtl = direction === "rtl";
21527
+ const style = {
21528
+ top: rect.top,
21529
+ ...isRtl ? { right: window.innerWidth - rect.left } : { left: rect.right }
21530
+ };
21531
+ const panel = /* @__PURE__ */ jsx(
21532
+ "div",
21533
+ {
21534
+ className: cn("fixed z-50", menuContainerStyles),
21535
+ style,
21536
+ children: items.map((item, index) => {
21537
+ const isDivider = item.id === "divider" || item.label === "divider";
21538
+ const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
21539
+ const isDanger = item.variant === "danger";
21540
+ if (isDivider) {
21541
+ return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
21542
+ }
21543
+ return /* @__PURE__ */ jsxs(
21544
+ Box,
21545
+ {
21546
+ as: "button",
21547
+ onClick: () => {
21548
+ if (item.disabled) return;
21549
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
21550
+ item.onClick?.();
21551
+ },
21552
+ "aria-disabled": item.disabled || void 0,
21553
+ "data-testid": item.event ? `action-${item.event}` : void 0,
21554
+ className: cn(
21555
+ "w-full flex items-center gap-3 px-4 py-2 text-start",
21556
+ "text-sm transition-colors",
21557
+ "hover:bg-muted focus:outline-none focus:bg-muted",
21558
+ "disabled:opacity-50 disabled:cursor-not-allowed",
21559
+ item.disabled && "cursor-not-allowed",
21560
+ isDanger && "text-error hover:bg-error/10"
21561
+ ),
21562
+ children: [
21563
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
21564
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: cn("flex-1", isDanger && "text-red-600"), children: item.label }),
21565
+ item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs font-medium", children: item.badge })
21566
+ ]
21567
+ },
21568
+ itemId
21569
+ );
21570
+ })
21571
+ }
21572
+ );
21573
+ return typeof document !== "undefined" ? createPortal(panel, document.body) : panel;
21574
+ }
21575
+ var MENU_GAP, menuContainerStyles, Menu;
21499
21576
  var init_Menu = __esm({
21500
21577
  "components/core/molecules/Menu.tsx"() {
21501
21578
  "use client";
@@ -21506,6 +21583,14 @@ var init_Menu = __esm({
21506
21583
  init_Badge();
21507
21584
  init_cn();
21508
21585
  init_useEventBus();
21586
+ MENU_GAP = 4;
21587
+ menuContainerStyles = cn(
21588
+ "bg-card",
21589
+ "border-[length:var(--border-width)] border-border",
21590
+ "shadow-elevation-popover",
21591
+ "rounded-sm",
21592
+ "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
21593
+ );
21509
21594
  Menu = ({
21510
21595
  trigger,
21511
21596
  items,
@@ -21513,9 +21598,10 @@ var init_Menu = __esm({
21513
21598
  className
21514
21599
  }) => {
21515
21600
  const eventBus = useEventBus();
21516
- const { t, direction } = useTranslate();
21601
+ const { direction } = useTranslate();
21517
21602
  const [isOpen, setIsOpen] = useState(false);
21518
21603
  const [activeSubMenu, setActiveSubMenu] = useState(null);
21604
+ const [activeSubMenuRef, setActiveSubMenuRef] = useState(null);
21519
21605
  const [triggerRect, setTriggerRect] = useState(null);
21520
21606
  const triggerRef = useRef(null);
21521
21607
  const menuRef = useRef(null);
@@ -21530,13 +21616,14 @@ var init_Menu = __esm({
21530
21616
  }
21531
21617
  setIsOpen(!isOpen);
21532
21618
  setActiveSubMenu(null);
21619
+ setActiveSubMenuRef(null);
21533
21620
  };
21534
- const handleItemClick = (item) => {
21621
+ const handleItemClick = (item, itemId) => {
21535
21622
  if (item.disabled) return;
21536
21623
  if (item.subMenu && item.subMenu.length > 0) {
21537
- setActiveSubMenu(item.id ?? null);
21624
+ setActiveSubMenu(itemId);
21538
21625
  } else {
21539
- if (item.event) eventBus.emit(`UI:${item.event}`, { itemId: item.id, label: item.label });
21626
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
21540
21627
  item.onClick?.();
21541
21628
  setIsOpen(false);
21542
21629
  }
@@ -21551,22 +21638,12 @@ var init_Menu = __esm({
21551
21638
  if (isOpen && menuRef.current && !menuRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
21552
21639
  setIsOpen(false);
21553
21640
  setActiveSubMenu(null);
21641
+ setActiveSubMenuRef(null);
21554
21642
  }
21555
21643
  };
21556
21644
  document.addEventListener("mousedown", handleClickOutside);
21557
21645
  return () => document.removeEventListener("mousedown", handleClickOutside);
21558
21646
  }, [isOpen]);
21559
- const positionClasses = {
21560
- "top-left": "bottom-full left-0 mb-2",
21561
- "top-right": "bottom-full right-0 mb-2",
21562
- "bottom-left": "top-full left-0 mt-2",
21563
- "bottom-right": "top-full right-0 mt-2",
21564
- // Aliases for pattern compatibility
21565
- "top-start": "bottom-full left-0 mb-2",
21566
- "top-end": "bottom-full right-0 mb-2",
21567
- "bottom-start": "top-full left-0 mt-2",
21568
- "bottom-end": "top-full right-0 mt-2"
21569
- };
21570
21647
  const rtlMirror = {
21571
21648
  "top-left": "top-right",
21572
21649
  "top-right": "top-left",
@@ -21578,7 +21655,6 @@ var init_Menu = __esm({
21578
21655
  "bottom-end": "bottom-start"
21579
21656
  };
21580
21657
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
21581
- const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
21582
21658
  const triggerChild = React79__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
21583
21659
  const triggerElement = React79__default.cloneElement(
21584
21660
  triggerChild,
@@ -21587,94 +21663,83 @@ var init_Menu = __esm({
21587
21663
  onClick: handleToggle
21588
21664
  }
21589
21665
  );
21590
- const menuContainerStyles = cn(
21591
- "bg-card",
21592
- "border-[length:var(--border-width)] border-border",
21593
- "shadow-elevation-popover",
21594
- "rounded-sm",
21595
- "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
21596
- );
21597
- const renderMenuItem = (item, hasSubMenu, index) => {
21666
+ const renderMenuItems = (menuItems) => menuItems.map((item, index) => {
21667
+ const isDivider = item.id === "divider" || item.label === "divider";
21598
21668
  const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
21669
+ const hasSubMenu = !!(item.subMenu && item.subMenu.length > 0);
21599
21670
  const isDanger = item.variant === "danger";
21600
- return /* @__PURE__ */ jsx(
21601
- Box,
21602
- {
21603
- as: "button",
21604
- onClick: () => !item.disabled && handleItemClick({ ...item, id: itemId }),
21605
- "aria-disabled": item.disabled || void 0,
21606
- onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
21607
- "data-testid": item.event ? `action-${item.event}` : void 0,
21608
- className: cn(
21609
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
21610
- "text-sm transition-colors",
21611
- "hover:bg-muted",
21612
- "focus:outline-none focus:bg-muted",
21613
- "disabled:opacity-50 disabled:cursor-not-allowed",
21614
- item.disabled && "cursor-not-allowed",
21615
- isDanger && "text-error hover:bg-error/10"
21616
- ),
21617
- children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
21618
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
21619
- /* @__PURE__ */ jsx(
21620
- Typography,
21621
- {
21622
- variant: "small",
21623
- className: cn("flex-1", isDanger && "text-red-600"),
21624
- children: item.label
21671
+ if (isDivider) {
21672
+ return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
21673
+ }
21674
+ return /* @__PURE__ */ jsxs(Box, { children: [
21675
+ /* @__PURE__ */ jsx(
21676
+ Box,
21677
+ {
21678
+ as: "button",
21679
+ onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
21680
+ "aria-disabled": item.disabled || void 0,
21681
+ onMouseEnter: (e) => {
21682
+ if (hasSubMenu) {
21683
+ setActiveSubMenu(itemId);
21684
+ setActiveSubMenuRef(e.currentTarget);
21625
21685
  }
21686
+ },
21687
+ "data-testid": item.event ? `action-${item.event}` : void 0,
21688
+ className: cn(
21689
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
21690
+ "text-sm transition-colors",
21691
+ "hover:bg-muted",
21692
+ "focus:outline-none focus:bg-muted",
21693
+ "disabled:opacity-50 disabled:cursor-not-allowed",
21694
+ item.disabled && "cursor-not-allowed",
21695
+ isDanger && "text-error hover:bg-error/10"
21626
21696
  ),
21627
- item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
21628
- hasSubMenu && /* @__PURE__ */ jsx(Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
21629
- ] })
21630
- },
21631
- itemId
21632
- );
21633
- };
21634
- const renderMenuItems = (menuItems) => {
21635
- return menuItems.map((item, index) => {
21636
- const hasSubMenu = item.subMenu && item.subMenu.length > 0;
21637
- const isDivider = item.id === "divider" || item.label === "divider";
21638
- const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
21639
- if (isDivider) {
21640
- return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
21641
- }
21642
- return /* @__PURE__ */ jsxs(Box, { children: [
21643
- renderMenuItem(item, !!hasSubMenu, index),
21644
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsx(
21645
- Box,
21646
- {
21647
- className: cn(
21648
- "absolute top-0 z-50",
21649
- subMenuSideClass,
21650
- menuContainerStyles
21697
+ children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
21698
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
21699
+ /* @__PURE__ */ jsx(
21700
+ Typography,
21701
+ {
21702
+ variant: "small",
21703
+ className: cn("flex-1", isDanger && "text-red-600"),
21704
+ children: item.label
21705
+ }
21651
21706
  ),
21652
- children: renderMenuItems(item.subMenu)
21653
- }
21654
- )
21655
- ] }, itemId);
21656
- });
21657
- };
21658
- return /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
21707
+ item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
21708
+ hasSubMenu && /* @__PURE__ */ jsx(
21709
+ Icon,
21710
+ {
21711
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
21712
+ size: "sm",
21713
+ className: "flex-shrink-0"
21714
+ }
21715
+ )
21716
+ ] })
21717
+ }
21718
+ ),
21719
+ hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsx(
21720
+ SubMenu,
21721
+ {
21722
+ items: item.subMenu,
21723
+ itemRef: activeSubMenuRef,
21724
+ direction,
21725
+ eventBus
21726
+ }
21727
+ )
21728
+ ] }, itemId);
21729
+ });
21730
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsx(
21731
+ "div",
21732
+ {
21733
+ ref: menuRef,
21734
+ className: cn("fixed z-50", menuContainerStyles, className),
21735
+ style: computeMenuStyle(effectivePosition, triggerRect),
21736
+ role: "menu",
21737
+ children: renderMenuItems(items)
21738
+ }
21739
+ ) : null;
21740
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
21659
21741
  triggerElement,
21660
- isOpen && triggerRect && /* @__PURE__ */ jsx(
21661
- Box,
21662
- {
21663
- ref: menuRef,
21664
- className: cn(
21665
- "absolute z-50",
21666
- menuContainerStyles,
21667
- positionClasses[effectivePosition],
21668
- className
21669
- ),
21670
- style: {
21671
- left: effectivePosition.includes("left") ? 0 : "auto",
21672
- right: effectivePosition.includes("right") ? 0 : "auto"
21673
- },
21674
- role: "menu",
21675
- children: renderMenuItems(items)
21676
- }
21677
- )
21742
+ panel && typeof document !== "undefined" ? createPortal(panel, document.body) : panel
21678
21743
  ] });
21679
21744
  };
21680
21745
  Menu.displayName = "Menu";
@@ -36080,7 +36145,6 @@ var init_DocumentViewer = __esm({
36080
36145
  showPrint = false,
36081
36146
  actions,
36082
36147
  documents,
36083
- entity,
36084
36148
  isLoading = false,
36085
36149
  error,
36086
36150
  className
@@ -42895,7 +42959,7 @@ function TraitSlot({
42895
42959
  size = "md",
42896
42960
  showTooltip = true,
42897
42961
  categoryColors,
42898
- tooltipFrameUrl,
42962
+ tooltipFrameUrl = "",
42899
42963
  className,
42900
42964
  feedback,
42901
42965
  onItemDrop,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.29.0",
3
+ "version": "5.30.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [