@xsolla/xui-context-menu 0.184.0 → 0.185.1

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.
package/web/index.js CHANGED
@@ -456,6 +456,7 @@ var OptionCell = ({
456
456
  const closeTimerRef = import_react4.default.useRef(
457
457
  null
458
458
  );
459
+ const ownsSubmenu = Boolean(hasSubmenu && submenu != null);
459
460
  const cancelClose = () => {
460
461
  if (closeTimerRef.current) {
461
462
  clearTimeout(closeTimerRef.current);
@@ -464,9 +465,22 @@ var OptionCell = ({
464
465
  };
465
466
  const scheduleClose = () => {
466
467
  cancelClose();
467
- closeTimerRef.current = setTimeout(() => setSubmenuOpen(false), 120);
468
+ closeTimerRef.current = setTimeout(() => {
469
+ setSubmenuOpen(false);
470
+ if (ownsSubmenu && ctx?.activeSubmenuId === id) {
471
+ ctx.setActiveSubmenuId?.(null);
472
+ }
473
+ }, 120);
468
474
  };
469
475
  (0, import_react4.useEffect)(() => () => cancelClose(), []);
476
+ (0, import_react4.useEffect)(() => {
477
+ if (!ownsSubmenu || !submenuOpen) return;
478
+ const activeId = ctx?.activeSubmenuId;
479
+ if (activeId != null && activeId !== id) {
480
+ cancelClose();
481
+ setSubmenuOpen(false);
482
+ }
483
+ }, [ctx?.activeSubmenuId, ownsSubmenu, submenuOpen, id]);
470
484
  (0, import_react4.useEffect)(() => {
471
485
  if (!hasSubmenu || !submenuOpen) return;
472
486
  const onMouseDown = (e) => {
@@ -557,6 +571,7 @@ var OptionCell = ({
557
571
  if (ctx && index2 >= 0) ctx.setActiveIndex(index2);
558
572
  if (!ctx) setIsHovered(true);
559
573
  if (hasSubmenu) setSubmenuOpen(true);
574
+ if (ownsSubmenu) ctx?.setActiveSubmenuId?.(id);
560
575
  };
561
576
  const handleLeave = () => {
562
577
  if (!ctx) setIsHovered(false);
@@ -1156,12 +1171,13 @@ var joinPlacement = (vertical, horizontal) => `${vertical}-${horizontal}`;
1156
1171
  var useContextMenuPosition = ({
1157
1172
  triggerRef,
1158
1173
  panelRef,
1174
+ containerRef,
1159
1175
  isOpen,
1160
1176
  placement = "bottom-start",
1161
1177
  offset = 4
1162
1178
  }) => {
1163
1179
  const [resolved, setResolved] = (0, import_react6.useState)();
1164
- (0, import_react6.useEffect)(() => {
1180
+ (0, import_react6.useLayoutEffect)(() => {
1165
1181
  if (!isOpen) {
1166
1182
  setResolved(void 0);
1167
1183
  return;
@@ -1169,6 +1185,7 @@ var useContextMenuPosition = ({
1169
1185
  const compute = () => {
1170
1186
  const trigger = triggerRef.current;
1171
1187
  const panel = panelRef.current;
1188
+ const container = containerRef.current;
1172
1189
  if (!trigger || !panel) return;
1173
1190
  const triggerRect = trigger.getBoundingClientRect();
1174
1191
  const panelRect = panel.getBoundingClientRect();
@@ -1208,6 +1225,15 @@ var useContextMenuPosition = ({
1208
1225
  }
1209
1226
  }
1210
1227
  }
1228
+ if (container) {
1229
+ const containerRect = container.getBoundingClientRect();
1230
+ const cOffsetW = container.offsetWidth;
1231
+ const cOffsetH = container.offsetHeight;
1232
+ const scaleX = cOffsetW > 0 ? containerRect.width / cOffsetW : 1;
1233
+ const scaleY = cOffsetH > 0 ? containerRect.height / cOffsetH : 1;
1234
+ left = (left - containerRect.left) / (scaleX || 1);
1235
+ top = (top - containerRect.top) / (scaleY || 1);
1236
+ }
1211
1237
  const next = {
1212
1238
  top,
1213
1239
  left,
@@ -1217,14 +1243,13 @@ var useContextMenuPosition = ({
1217
1243
  (prev) => prev && prev.top === next.top && prev.left === next.left && prev.placement === next.placement ? prev : next
1218
1244
  );
1219
1245
  };
1220
- const rafId = window.requestAnimationFrame(compute);
1246
+ compute();
1221
1247
  const onResize = () => compute();
1222
1248
  window.addEventListener("resize", onResize);
1223
1249
  return () => {
1224
- window.cancelAnimationFrame(rafId);
1225
1250
  window.removeEventListener("resize", onResize);
1226
1251
  };
1227
- }, [isOpen, placement, offset, triggerRef, panelRef]);
1252
+ }, [isOpen, placement, offset, triggerRef, panelRef, containerRef]);
1228
1253
  return resolved;
1229
1254
  };
1230
1255
 
@@ -1356,6 +1381,7 @@ var SEARCH_DEBOUNCE_MS = 200;
1356
1381
  var ARROW_SIZE = 10;
1357
1382
  var ARROW_HALF = ARROW_SIZE / 2;
1358
1383
  var ARROW_EDGE_OFFSET = 12;
1384
+ var ARROW_TIP_PROTRUSION = ARROW_HALF * Math.SQRT2;
1359
1385
  var PANEL_SHADOW = "0 4px 12px rgba(0,0,0,0.15)";
1360
1386
  var textFromNode = (node) => {
1361
1387
  if (node === null || node === void 0 || typeof node === "boolean")
@@ -1405,7 +1431,9 @@ var ContextMenuRoot = (0, import_react8.forwardRef)(
1405
1431
  isOpen: propIsOpen,
1406
1432
  onOpenChange,
1407
1433
  trigger,
1434
+ triggerFullWidth = false,
1408
1435
  placement = "bottom-start",
1436
+ offset = 4,
1409
1437
  position,
1410
1438
  width,
1411
1439
  maxHeight = 300,
@@ -1439,11 +1467,14 @@ var ContextMenuRoot = (0, import_react8.forwardRef)(
1439
1467
  const sizeStyles = xuiTheme.sizing.contextMenu(size);
1440
1468
  const borderRadius = xuiTheme.shape?.contextMenu?.[size]?.borderRadius ?? 8;
1441
1469
  const shouldCloseOnSelect = closeOnSelect ?? (type === "checkbox" ? false : true);
1470
+ const positionOffset = withArrow ? offset + ARROW_TIP_PROTRUSION : offset;
1442
1471
  const positioned = useContextMenuPosition({
1443
1472
  triggerRef,
1444
1473
  panelRef,
1474
+ containerRef,
1445
1475
  isOpen: isOpen && !!trigger && !position,
1446
- placement
1476
+ placement,
1477
+ offset: positionOffset
1447
1478
  });
1448
1479
  const resolvedPlacement = positioned?.placement ?? placement;
1449
1480
  const getArrowStyle = (arrowPlacement) => {
@@ -1699,7 +1730,10 @@ var ContextMenuRoot = (0, import_react8.forwardRef)(
1699
1730
  onClick: (event) => {
1700
1731
  if (!event.defaultPrevented) toggleMenu();
1701
1732
  },
1702
- style: { display: "inline-flex" },
1733
+ style: {
1734
+ display: "inline-flex",
1735
+ width: triggerFullWidth ? "100%" : "auto"
1736
+ },
1703
1737
  children: (0, import_react8.cloneElement)(trigger, {
1704
1738
  "aria-haspopup": "menu",
1705
1739
  "aria-expanded": isOpen ? "true" : "false"
@@ -1721,10 +1755,13 @@ var ContextMenuRoot = (0, import_react8.forwardRef)(
1721
1755
  position: "fixed",
1722
1756
  left: position.x,
1723
1757
  top: position.y
1724
- } : trigger ? {
1725
- position: "fixed",
1726
- left: positioned?.left ?? 0,
1727
- top: positioned?.top ?? 0
1758
+ } : trigger ? positioned ? {
1759
+ position: "absolute",
1760
+ left: positioned.left,
1761
+ top: positioned.top
1762
+ } : {
1763
+ position: "absolute",
1764
+ visibility: "hidden"
1728
1765
  } : void 0;
1729
1766
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ContextMenuContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1730
1767
  "div",