@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.
@@ -21977,7 +21977,84 @@ var init_DashboardLayout = __esm({
21977
21977
  NavLinkBottom.displayName = "NavLinkBottom";
21978
21978
  }
21979
21979
  });
21980
- var Menu;
21980
+ function computeMenuStyle(position, triggerRect) {
21981
+ const isTop = position.startsWith("top");
21982
+ const isRight = position.endsWith("right") || position.endsWith("end");
21983
+ if (isTop) {
21984
+ return {
21985
+ top: triggerRect.top - MENU_GAP,
21986
+ transform: "translateY(-100%)",
21987
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
21988
+ };
21989
+ }
21990
+ return {
21991
+ top: triggerRect.bottom + MENU_GAP,
21992
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
21993
+ };
21994
+ }
21995
+ function SubMenu({
21996
+ items,
21997
+ itemRef,
21998
+ direction,
21999
+ eventBus
22000
+ }) {
22001
+ const [rect, setRect] = React80.useState(null);
22002
+ React80.useEffect(() => {
22003
+ if (itemRef) {
22004
+ setRect(itemRef.getBoundingClientRect());
22005
+ }
22006
+ }, [itemRef]);
22007
+ if (!rect) return null;
22008
+ const isRtl = direction === "rtl";
22009
+ const style = {
22010
+ top: rect.top,
22011
+ ...isRtl ? { right: window.innerWidth - rect.left } : { left: rect.right }
22012
+ };
22013
+ const panel = /* @__PURE__ */ jsxRuntime.jsx(
22014
+ "div",
22015
+ {
22016
+ className: cn("fixed z-50", menuContainerStyles),
22017
+ style,
22018
+ children: items.map((item, index) => {
22019
+ const isDivider = item.id === "divider" || item.label === "divider";
22020
+ const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
22021
+ const isDanger = item.variant === "danger";
22022
+ if (isDivider) {
22023
+ return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
22024
+ }
22025
+ return /* @__PURE__ */ jsxRuntime.jsxs(
22026
+ Box,
22027
+ {
22028
+ as: "button",
22029
+ onClick: () => {
22030
+ if (item.disabled) return;
22031
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
22032
+ item.onClick?.();
22033
+ },
22034
+ "aria-disabled": item.disabled || void 0,
22035
+ "data-testid": item.event ? `action-${item.event}` : void 0,
22036
+ className: cn(
22037
+ "w-full flex items-center gap-3 px-4 py-2 text-start",
22038
+ "text-sm transition-colors",
22039
+ "hover:bg-muted focus:outline-none focus:bg-muted",
22040
+ "disabled:opacity-50 disabled:cursor-not-allowed",
22041
+ item.disabled && "cursor-not-allowed",
22042
+ isDanger && "text-error hover:bg-error/10"
22043
+ ),
22044
+ children: [
22045
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
22046
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: cn("flex-1", isDanger && "text-red-600"), children: item.label }),
22047
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-xs font-medium", children: item.badge })
22048
+ ]
22049
+ },
22050
+ itemId
22051
+ );
22052
+ })
22053
+ }
22054
+ );
22055
+ return typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel;
22056
+ }
22057
+ var MENU_GAP, menuContainerStyles, Menu;
21981
22058
  var init_Menu = __esm({
21982
22059
  "components/core/molecules/Menu.tsx"() {
21983
22060
  "use client";
@@ -21988,6 +22065,14 @@ var init_Menu = __esm({
21988
22065
  init_Badge();
21989
22066
  init_cn();
21990
22067
  init_useEventBus();
22068
+ MENU_GAP = 4;
22069
+ menuContainerStyles = cn(
22070
+ "bg-card",
22071
+ "border-[length:var(--border-width)] border-border",
22072
+ "shadow-elevation-popover",
22073
+ "rounded-sm",
22074
+ "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
22075
+ );
21991
22076
  Menu = ({
21992
22077
  trigger,
21993
22078
  items,
@@ -21995,9 +22080,10 @@ var init_Menu = __esm({
21995
22080
  className
21996
22081
  }) => {
21997
22082
  const eventBus = useEventBus();
21998
- const { t, direction } = hooks.useTranslate();
22083
+ const { direction } = hooks.useTranslate();
21999
22084
  const [isOpen, setIsOpen] = React80.useState(false);
22000
22085
  const [activeSubMenu, setActiveSubMenu] = React80.useState(null);
22086
+ const [activeSubMenuRef, setActiveSubMenuRef] = React80.useState(null);
22001
22087
  const [triggerRect, setTriggerRect] = React80.useState(null);
22002
22088
  const triggerRef = React80.useRef(null);
22003
22089
  const menuRef = React80.useRef(null);
@@ -22012,13 +22098,14 @@ var init_Menu = __esm({
22012
22098
  }
22013
22099
  setIsOpen(!isOpen);
22014
22100
  setActiveSubMenu(null);
22101
+ setActiveSubMenuRef(null);
22015
22102
  };
22016
- const handleItemClick = (item) => {
22103
+ const handleItemClick = (item, itemId) => {
22017
22104
  if (item.disabled) return;
22018
22105
  if (item.subMenu && item.subMenu.length > 0) {
22019
- setActiveSubMenu(item.id ?? null);
22106
+ setActiveSubMenu(itemId);
22020
22107
  } else {
22021
- if (item.event) eventBus.emit(`UI:${item.event}`, { itemId: item.id, label: item.label });
22108
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
22022
22109
  item.onClick?.();
22023
22110
  setIsOpen(false);
22024
22111
  }
@@ -22033,22 +22120,12 @@ var init_Menu = __esm({
22033
22120
  if (isOpen && menuRef.current && !menuRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
22034
22121
  setIsOpen(false);
22035
22122
  setActiveSubMenu(null);
22123
+ setActiveSubMenuRef(null);
22036
22124
  }
22037
22125
  };
22038
22126
  document.addEventListener("mousedown", handleClickOutside);
22039
22127
  return () => document.removeEventListener("mousedown", handleClickOutside);
22040
22128
  }, [isOpen]);
22041
- const positionClasses = {
22042
- "top-left": "bottom-full left-0 mb-2",
22043
- "top-right": "bottom-full right-0 mb-2",
22044
- "bottom-left": "top-full left-0 mt-2",
22045
- "bottom-right": "top-full right-0 mt-2",
22046
- // Aliases for pattern compatibility
22047
- "top-start": "bottom-full left-0 mb-2",
22048
- "top-end": "bottom-full right-0 mb-2",
22049
- "bottom-start": "top-full left-0 mt-2",
22050
- "bottom-end": "top-full right-0 mt-2"
22051
- };
22052
22129
  const rtlMirror = {
22053
22130
  "top-left": "top-right",
22054
22131
  "top-right": "top-left",
@@ -22060,7 +22137,6 @@ var init_Menu = __esm({
22060
22137
  "bottom-end": "bottom-start"
22061
22138
  };
22062
22139
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
22063
- const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
22064
22140
  const triggerChild = React80__namespace.default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", as: "span", children: trigger });
22065
22141
  const triggerElement = React80__namespace.default.cloneElement(
22066
22142
  triggerChild,
@@ -22069,94 +22145,83 @@ var init_Menu = __esm({
22069
22145
  onClick: handleToggle
22070
22146
  }
22071
22147
  );
22072
- const menuContainerStyles = cn(
22073
- "bg-card",
22074
- "border-[length:var(--border-width)] border-border",
22075
- "shadow-elevation-popover",
22076
- "rounded-sm",
22077
- "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
22078
- );
22079
- const renderMenuItem = (item, hasSubMenu, index) => {
22148
+ const renderMenuItems = (menuItems) => menuItems.map((item, index) => {
22149
+ const isDivider = item.id === "divider" || item.label === "divider";
22080
22150
  const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
22151
+ const hasSubMenu = !!(item.subMenu && item.subMenu.length > 0);
22081
22152
  const isDanger = item.variant === "danger";
22082
- return /* @__PURE__ */ jsxRuntime.jsx(
22083
- Box,
22084
- {
22085
- as: "button",
22086
- onClick: () => !item.disabled && handleItemClick({ ...item, id: itemId }),
22087
- "aria-disabled": item.disabled || void 0,
22088
- onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
22089
- "data-testid": item.event ? `action-${item.event}` : void 0,
22090
- className: cn(
22091
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
22092
- "text-sm transition-colors",
22093
- "hover:bg-muted",
22094
- "focus:outline-none focus:bg-muted",
22095
- "disabled:opacity-50 disabled:cursor-not-allowed",
22096
- item.disabled && "cursor-not-allowed",
22097
- isDanger && "text-error hover:bg-error/10"
22098
- ),
22099
- children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
22100
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
22101
- /* @__PURE__ */ jsxRuntime.jsx(
22102
- Typography,
22103
- {
22104
- variant: "small",
22105
- className: cn("flex-1", isDanger && "text-red-600"),
22106
- children: item.label
22153
+ if (isDivider) {
22154
+ return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
22155
+ }
22156
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
22157
+ /* @__PURE__ */ jsxRuntime.jsx(
22158
+ Box,
22159
+ {
22160
+ as: "button",
22161
+ onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
22162
+ "aria-disabled": item.disabled || void 0,
22163
+ onMouseEnter: (e) => {
22164
+ if (hasSubMenu) {
22165
+ setActiveSubMenu(itemId);
22166
+ setActiveSubMenuRef(e.currentTarget);
22107
22167
  }
22168
+ },
22169
+ "data-testid": item.event ? `action-${item.event}` : void 0,
22170
+ className: cn(
22171
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
22172
+ "text-sm transition-colors",
22173
+ "hover:bg-muted",
22174
+ "focus:outline-none focus:bg-muted",
22175
+ "disabled:opacity-50 disabled:cursor-not-allowed",
22176
+ item.disabled && "cursor-not-allowed",
22177
+ isDanger && "text-error hover:bg-error/10"
22108
22178
  ),
22109
- item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
22110
- hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
22111
- ] })
22112
- },
22113
- itemId
22114
- );
22115
- };
22116
- const renderMenuItems = (menuItems) => {
22117
- return menuItems.map((item, index) => {
22118
- const hasSubMenu = item.subMenu && item.subMenu.length > 0;
22119
- const isDivider = item.id === "divider" || item.label === "divider";
22120
- const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
22121
- if (isDivider) {
22122
- return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
22123
- }
22124
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
22125
- renderMenuItem(item, !!hasSubMenu, index),
22126
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
22127
- Box,
22128
- {
22129
- className: cn(
22130
- "absolute top-0 z-50",
22131
- subMenuSideClass,
22132
- menuContainerStyles
22179
+ children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
22180
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
22181
+ /* @__PURE__ */ jsxRuntime.jsx(
22182
+ Typography,
22183
+ {
22184
+ variant: "small",
22185
+ className: cn("flex-1", isDanger && "text-red-600"),
22186
+ children: item.label
22187
+ }
22133
22188
  ),
22134
- children: renderMenuItems(item.subMenu)
22135
- }
22136
- )
22137
- ] }, itemId);
22138
- });
22139
- };
22140
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
22189
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
22190
+ hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(
22191
+ Icon,
22192
+ {
22193
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
22194
+ size: "sm",
22195
+ className: "flex-shrink-0"
22196
+ }
22197
+ )
22198
+ ] })
22199
+ }
22200
+ ),
22201
+ hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
22202
+ SubMenu,
22203
+ {
22204
+ items: item.subMenu,
22205
+ itemRef: activeSubMenuRef,
22206
+ direction,
22207
+ eventBus
22208
+ }
22209
+ )
22210
+ ] }, itemId);
22211
+ });
22212
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsx(
22213
+ "div",
22214
+ {
22215
+ ref: menuRef,
22216
+ className: cn("fixed z-50", menuContainerStyles, className),
22217
+ style: computeMenuStyle(effectivePosition, triggerRect),
22218
+ role: "menu",
22219
+ children: renderMenuItems(items)
22220
+ }
22221
+ ) : null;
22222
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
22141
22223
  triggerElement,
22142
- isOpen && triggerRect && /* @__PURE__ */ jsxRuntime.jsx(
22143
- Box,
22144
- {
22145
- ref: menuRef,
22146
- className: cn(
22147
- "absolute z-50",
22148
- menuContainerStyles,
22149
- positionClasses[effectivePosition],
22150
- className
22151
- ),
22152
- style: {
22153
- left: effectivePosition.includes("left") ? 0 : "auto",
22154
- right: effectivePosition.includes("right") ? 0 : "auto"
22155
- },
22156
- role: "menu",
22157
- children: renderMenuItems(items)
22158
- }
22159
- )
22224
+ panel && typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel
22160
22225
  ] });
22161
22226
  };
22162
22227
  Menu.displayName = "Menu";
@@ -36562,7 +36627,6 @@ var init_DocumentViewer = __esm({
36562
36627
  showPrint = false,
36563
36628
  actions,
36564
36629
  documents,
36565
- entity,
36566
36630
  isLoading = false,
36567
36631
  error,
36568
36632
  className
@@ -43358,7 +43422,7 @@ function TraitSlot({
43358
43422
  size = "md",
43359
43423
  showTooltip = true,
43360
43424
  categoryColors,
43361
- tooltipFrameUrl,
43425
+ tooltipFrameUrl = "",
43362
43426
  className,
43363
43427
  feedback,
43364
43428
  onItemDrop,