@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/native/index.d.mts +14 -1
- package/native/index.d.ts +14 -1
- package/native/index.js +48 -11
- package/native/index.js.map +1 -1
- package/native/index.mjs +57 -20
- package/native/index.mjs.map +1 -1
- package/package.json +10 -10
- package/web/index.d.mts +14 -1
- package/web/index.d.ts +14 -1
- package/web/index.js +48 -11
- package/web/index.js.map +1 -1
- package/web/index.mjs +57 -20
- package/web/index.mjs.map +1 -1
package/native/index.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
forwardRef,
|
|
5
5
|
isValidElement,
|
|
6
6
|
useCallback as useCallback3,
|
|
7
|
-
useEffect as
|
|
8
|
-
useLayoutEffect as
|
|
7
|
+
useEffect as useEffect3,
|
|
8
|
+
useLayoutEffect as useLayoutEffect4,
|
|
9
9
|
useMemo,
|
|
10
10
|
useRef as useRef2,
|
|
11
11
|
useState as useState4
|
|
@@ -353,6 +353,7 @@ var OptionCell = ({
|
|
|
353
353
|
const closeTimerRef = React.useRef(
|
|
354
354
|
null
|
|
355
355
|
);
|
|
356
|
+
const ownsSubmenu = Boolean(hasSubmenu && submenu != null);
|
|
356
357
|
const cancelClose = () => {
|
|
357
358
|
if (closeTimerRef.current) {
|
|
358
359
|
clearTimeout(closeTimerRef.current);
|
|
@@ -361,9 +362,22 @@ var OptionCell = ({
|
|
|
361
362
|
};
|
|
362
363
|
const scheduleClose = () => {
|
|
363
364
|
cancelClose();
|
|
364
|
-
closeTimerRef.current = setTimeout(() =>
|
|
365
|
+
closeTimerRef.current = setTimeout(() => {
|
|
366
|
+
setSubmenuOpen(false);
|
|
367
|
+
if (ownsSubmenu && ctx?.activeSubmenuId === id) {
|
|
368
|
+
ctx.setActiveSubmenuId?.(null);
|
|
369
|
+
}
|
|
370
|
+
}, 120);
|
|
365
371
|
};
|
|
366
372
|
useEffect(() => () => cancelClose(), []);
|
|
373
|
+
useEffect(() => {
|
|
374
|
+
if (!ownsSubmenu || !submenuOpen) return;
|
|
375
|
+
const activeId = ctx?.activeSubmenuId;
|
|
376
|
+
if (activeId != null && activeId !== id) {
|
|
377
|
+
cancelClose();
|
|
378
|
+
setSubmenuOpen(false);
|
|
379
|
+
}
|
|
380
|
+
}, [ctx?.activeSubmenuId, ownsSubmenu, submenuOpen, id]);
|
|
367
381
|
useEffect(() => {
|
|
368
382
|
if (!hasSubmenu || !submenuOpen) return;
|
|
369
383
|
const onMouseDown = (e) => {
|
|
@@ -454,6 +468,7 @@ var OptionCell = ({
|
|
|
454
468
|
if (ctx && index >= 0) ctx.setActiveIndex(index);
|
|
455
469
|
if (!ctx) setIsHovered(true);
|
|
456
470
|
if (hasSubmenu) setSubmenuOpen(true);
|
|
471
|
+
if (ownsSubmenu) ctx?.setActiveSubmenuId?.(id);
|
|
457
472
|
};
|
|
458
473
|
const handleLeave = () => {
|
|
459
474
|
if (!ctx) setIsHovered(false);
|
|
@@ -1050,7 +1065,7 @@ var ContextMenuSubmenu = ({
|
|
|
1050
1065
|
ContextMenuSubmenu.displayName = "ContextMenuSubmenu";
|
|
1051
1066
|
|
|
1052
1067
|
// src/hooks/useContextMenuPosition.ts
|
|
1053
|
-
import {
|
|
1068
|
+
import { useLayoutEffect as useLayoutEffect3, useState as useState3 } from "react";
|
|
1054
1069
|
var splitPlacement = (placement) => {
|
|
1055
1070
|
const [vertical, horizontal] = placement.split("-");
|
|
1056
1071
|
return { vertical, horizontal };
|
|
@@ -1059,12 +1074,13 @@ var joinPlacement = (vertical, horizontal) => `${vertical}-${horizontal}`;
|
|
|
1059
1074
|
var useContextMenuPosition = ({
|
|
1060
1075
|
triggerRef,
|
|
1061
1076
|
panelRef,
|
|
1077
|
+
containerRef,
|
|
1062
1078
|
isOpen,
|
|
1063
1079
|
placement = "bottom-start",
|
|
1064
1080
|
offset = 4
|
|
1065
1081
|
}) => {
|
|
1066
1082
|
const [resolved, setResolved] = useState3();
|
|
1067
|
-
|
|
1083
|
+
useLayoutEffect3(() => {
|
|
1068
1084
|
if (!isOpen) {
|
|
1069
1085
|
setResolved(void 0);
|
|
1070
1086
|
return;
|
|
@@ -1072,6 +1088,7 @@ var useContextMenuPosition = ({
|
|
|
1072
1088
|
const compute = () => {
|
|
1073
1089
|
const trigger = triggerRef.current;
|
|
1074
1090
|
const panel = panelRef.current;
|
|
1091
|
+
const container = containerRef.current;
|
|
1075
1092
|
if (!trigger || !panel) return;
|
|
1076
1093
|
const triggerRect = trigger.getBoundingClientRect();
|
|
1077
1094
|
const panelRect = panel.getBoundingClientRect();
|
|
@@ -1111,6 +1128,15 @@ var useContextMenuPosition = ({
|
|
|
1111
1128
|
}
|
|
1112
1129
|
}
|
|
1113
1130
|
}
|
|
1131
|
+
if (container) {
|
|
1132
|
+
const containerRect = container.getBoundingClientRect();
|
|
1133
|
+
const cOffsetW = container.offsetWidth;
|
|
1134
|
+
const cOffsetH = container.offsetHeight;
|
|
1135
|
+
const scaleX = cOffsetW > 0 ? containerRect.width / cOffsetW : 1;
|
|
1136
|
+
const scaleY = cOffsetH > 0 ? containerRect.height / cOffsetH : 1;
|
|
1137
|
+
left = (left - containerRect.left) / (scaleX || 1);
|
|
1138
|
+
top = (top - containerRect.top) / (scaleY || 1);
|
|
1139
|
+
}
|
|
1114
1140
|
const next = {
|
|
1115
1141
|
top,
|
|
1116
1142
|
left,
|
|
@@ -1120,14 +1146,13 @@ var useContextMenuPosition = ({
|
|
|
1120
1146
|
(prev) => prev && prev.top === next.top && prev.left === next.left && prev.placement === next.placement ? prev : next
|
|
1121
1147
|
);
|
|
1122
1148
|
};
|
|
1123
|
-
|
|
1149
|
+
compute();
|
|
1124
1150
|
const onResize = () => compute();
|
|
1125
1151
|
window.addEventListener("resize", onResize);
|
|
1126
1152
|
return () => {
|
|
1127
|
-
window.cancelAnimationFrame(rafId);
|
|
1128
1153
|
window.removeEventListener("resize", onResize);
|
|
1129
1154
|
};
|
|
1130
|
-
}, [isOpen, placement, offset, triggerRef, panelRef]);
|
|
1155
|
+
}, [isOpen, placement, offset, triggerRef, panelRef, containerRef]);
|
|
1131
1156
|
return resolved;
|
|
1132
1157
|
};
|
|
1133
1158
|
|
|
@@ -1259,6 +1284,7 @@ var SEARCH_DEBOUNCE_MS = 200;
|
|
|
1259
1284
|
var ARROW_SIZE = 10;
|
|
1260
1285
|
var ARROW_HALF = ARROW_SIZE / 2;
|
|
1261
1286
|
var ARROW_EDGE_OFFSET = 12;
|
|
1287
|
+
var ARROW_TIP_PROTRUSION = ARROW_HALF * Math.SQRT2;
|
|
1262
1288
|
var PANEL_SHADOW = "0 4px 12px rgba(0,0,0,0.15)";
|
|
1263
1289
|
var textFromNode = (node) => {
|
|
1264
1290
|
if (node === null || node === void 0 || typeof node === "boolean")
|
|
@@ -1308,7 +1334,9 @@ var ContextMenuRoot = forwardRef(
|
|
|
1308
1334
|
isOpen: propIsOpen,
|
|
1309
1335
|
onOpenChange,
|
|
1310
1336
|
trigger,
|
|
1337
|
+
triggerFullWidth = false,
|
|
1311
1338
|
placement = "bottom-start",
|
|
1339
|
+
offset = 4,
|
|
1312
1340
|
position,
|
|
1313
1341
|
width,
|
|
1314
1342
|
maxHeight = 300,
|
|
@@ -1342,11 +1370,14 @@ var ContextMenuRoot = forwardRef(
|
|
|
1342
1370
|
const sizeStyles = xuiTheme.sizing.contextMenu(size);
|
|
1343
1371
|
const borderRadius = xuiTheme.shape?.contextMenu?.[size]?.borderRadius ?? 8;
|
|
1344
1372
|
const shouldCloseOnSelect = closeOnSelect ?? (type === "checkbox" ? false : true);
|
|
1373
|
+
const positionOffset = withArrow ? offset + ARROW_TIP_PROTRUSION : offset;
|
|
1345
1374
|
const positioned = useContextMenuPosition({
|
|
1346
1375
|
triggerRef,
|
|
1347
1376
|
panelRef,
|
|
1377
|
+
containerRef,
|
|
1348
1378
|
isOpen: isOpen && !!trigger && !position,
|
|
1349
|
-
placement
|
|
1379
|
+
placement,
|
|
1380
|
+
offset: positionOffset
|
|
1350
1381
|
});
|
|
1351
1382
|
const resolvedPlacement = positioned?.placement ?? placement;
|
|
1352
1383
|
const getArrowStyle = (arrowPlacement) => {
|
|
@@ -1421,7 +1452,7 @@ var ContextMenuRoot = forwardRef(
|
|
|
1421
1452
|
onClose: closeMenu,
|
|
1422
1453
|
triggerRef
|
|
1423
1454
|
});
|
|
1424
|
-
|
|
1455
|
+
useEffect3(() => {
|
|
1425
1456
|
if (!isOpen) {
|
|
1426
1457
|
cellsRef.current = [];
|
|
1427
1458
|
setCellsVersion((version) => version + 1);
|
|
@@ -1430,14 +1461,14 @@ var ContextMenuRoot = forwardRef(
|
|
|
1430
1461
|
setActiveSubmenuId(null);
|
|
1431
1462
|
}
|
|
1432
1463
|
}, [isOpen]);
|
|
1433
|
-
|
|
1464
|
+
useEffect3(() => {
|
|
1434
1465
|
const timer = setTimeout(
|
|
1435
1466
|
() => setDebouncedQuery(query),
|
|
1436
1467
|
SEARCH_DEBOUNCE_MS
|
|
1437
1468
|
);
|
|
1438
1469
|
return () => clearTimeout(timer);
|
|
1439
1470
|
}, [query]);
|
|
1440
|
-
|
|
1471
|
+
useEffect3(() => {
|
|
1441
1472
|
if (!isOpen || !trigger) return;
|
|
1442
1473
|
const onMouseDown = (event) => {
|
|
1443
1474
|
const target = event.target;
|
|
@@ -1452,7 +1483,7 @@ var ContextMenuRoot = forwardRef(
|
|
|
1452
1483
|
window.removeEventListener("scroll", onScroll);
|
|
1453
1484
|
};
|
|
1454
1485
|
}, [isOpen, trigger, closeMenu]);
|
|
1455
|
-
|
|
1486
|
+
useLayoutEffect4(() => {
|
|
1456
1487
|
if (!isOpen || !panelRef.current) return;
|
|
1457
1488
|
const searchbox = panelRef.current.querySelector('[role="searchbox"]');
|
|
1458
1489
|
const firstOption = panelRef.current.querySelector(
|
|
@@ -1460,13 +1491,13 @@ var ContextMenuRoot = forwardRef(
|
|
|
1460
1491
|
);
|
|
1461
1492
|
(searchbox ?? firstOption)?.focus();
|
|
1462
1493
|
}, [isOpen]);
|
|
1463
|
-
|
|
1494
|
+
useLayoutEffect4(() => {
|
|
1464
1495
|
if (!isOpen || !panelRef.current) return;
|
|
1465
1496
|
const activeElement = document.activeElement;
|
|
1466
1497
|
if (activeElement && panelRef.current.contains(activeElement)) return;
|
|
1467
1498
|
panelRef.current.focus();
|
|
1468
1499
|
}, [isOpen, cellsVersion]);
|
|
1469
|
-
|
|
1500
|
+
useEffect3(() => {
|
|
1470
1501
|
if (isOpen) return;
|
|
1471
1502
|
triggerRef.current?.focus();
|
|
1472
1503
|
}, [isOpen]);
|
|
@@ -1602,7 +1633,10 @@ var ContextMenuRoot = forwardRef(
|
|
|
1602
1633
|
onClick: (event) => {
|
|
1603
1634
|
if (!event.defaultPrevented) toggleMenu();
|
|
1604
1635
|
},
|
|
1605
|
-
style: {
|
|
1636
|
+
style: {
|
|
1637
|
+
display: "inline-flex",
|
|
1638
|
+
width: triggerFullWidth ? "100%" : "auto"
|
|
1639
|
+
},
|
|
1606
1640
|
children: cloneElement(trigger, {
|
|
1607
1641
|
"aria-haspopup": "menu",
|
|
1608
1642
|
"aria-expanded": isOpen ? "true" : "false"
|
|
@@ -1624,10 +1658,13 @@ var ContextMenuRoot = forwardRef(
|
|
|
1624
1658
|
position: "fixed",
|
|
1625
1659
|
left: position.x,
|
|
1626
1660
|
top: position.y
|
|
1627
|
-
} : trigger ? {
|
|
1628
|
-
position: "
|
|
1629
|
-
left: positioned
|
|
1630
|
-
top: positioned
|
|
1661
|
+
} : trigger ? positioned ? {
|
|
1662
|
+
position: "absolute",
|
|
1663
|
+
left: positioned.left,
|
|
1664
|
+
top: positioned.top
|
|
1665
|
+
} : {
|
|
1666
|
+
position: "absolute",
|
|
1667
|
+
visibility: "hidden"
|
|
1631
1668
|
} : void 0;
|
|
1632
1669
|
return /* @__PURE__ */ jsx4(ContextMenuContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs3(
|
|
1633
1670
|
"div",
|