@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/web/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
|
|
@@ -426,6 +426,7 @@ var OptionCell = ({
|
|
|
426
426
|
const closeTimerRef = React3.useRef(
|
|
427
427
|
null
|
|
428
428
|
);
|
|
429
|
+
const ownsSubmenu = Boolean(hasSubmenu && submenu != null);
|
|
429
430
|
const cancelClose = () => {
|
|
430
431
|
if (closeTimerRef.current) {
|
|
431
432
|
clearTimeout(closeTimerRef.current);
|
|
@@ -434,9 +435,22 @@ var OptionCell = ({
|
|
|
434
435
|
};
|
|
435
436
|
const scheduleClose = () => {
|
|
436
437
|
cancelClose();
|
|
437
|
-
closeTimerRef.current = setTimeout(() =>
|
|
438
|
+
closeTimerRef.current = setTimeout(() => {
|
|
439
|
+
setSubmenuOpen(false);
|
|
440
|
+
if (ownsSubmenu && ctx?.activeSubmenuId === id) {
|
|
441
|
+
ctx.setActiveSubmenuId?.(null);
|
|
442
|
+
}
|
|
443
|
+
}, 120);
|
|
438
444
|
};
|
|
439
445
|
useEffect(() => () => cancelClose(), []);
|
|
446
|
+
useEffect(() => {
|
|
447
|
+
if (!ownsSubmenu || !submenuOpen) return;
|
|
448
|
+
const activeId = ctx?.activeSubmenuId;
|
|
449
|
+
if (activeId != null && activeId !== id) {
|
|
450
|
+
cancelClose();
|
|
451
|
+
setSubmenuOpen(false);
|
|
452
|
+
}
|
|
453
|
+
}, [ctx?.activeSubmenuId, ownsSubmenu, submenuOpen, id]);
|
|
440
454
|
useEffect(() => {
|
|
441
455
|
if (!hasSubmenu || !submenuOpen) return;
|
|
442
456
|
const onMouseDown = (e) => {
|
|
@@ -527,6 +541,7 @@ var OptionCell = ({
|
|
|
527
541
|
if (ctx && index2 >= 0) ctx.setActiveIndex(index2);
|
|
528
542
|
if (!ctx) setIsHovered(true);
|
|
529
543
|
if (hasSubmenu) setSubmenuOpen(true);
|
|
544
|
+
if (ownsSubmenu) ctx?.setActiveSubmenuId?.(id);
|
|
530
545
|
};
|
|
531
546
|
const handleLeave = () => {
|
|
532
547
|
if (!ctx) setIsHovered(false);
|
|
@@ -1123,7 +1138,7 @@ var ContextMenuSubmenu = ({
|
|
|
1123
1138
|
ContextMenuSubmenu.displayName = "ContextMenuSubmenu";
|
|
1124
1139
|
|
|
1125
1140
|
// src/hooks/useContextMenuPosition.ts
|
|
1126
|
-
import {
|
|
1141
|
+
import { useLayoutEffect as useLayoutEffect3, useState as useState3 } from "react";
|
|
1127
1142
|
var splitPlacement = (placement) => {
|
|
1128
1143
|
const [vertical, horizontal] = placement.split("-");
|
|
1129
1144
|
return { vertical, horizontal };
|
|
@@ -1132,12 +1147,13 @@ var joinPlacement = (vertical, horizontal) => `${vertical}-${horizontal}`;
|
|
|
1132
1147
|
var useContextMenuPosition = ({
|
|
1133
1148
|
triggerRef,
|
|
1134
1149
|
panelRef,
|
|
1150
|
+
containerRef,
|
|
1135
1151
|
isOpen,
|
|
1136
1152
|
placement = "bottom-start",
|
|
1137
1153
|
offset = 4
|
|
1138
1154
|
}) => {
|
|
1139
1155
|
const [resolved, setResolved] = useState3();
|
|
1140
|
-
|
|
1156
|
+
useLayoutEffect3(() => {
|
|
1141
1157
|
if (!isOpen) {
|
|
1142
1158
|
setResolved(void 0);
|
|
1143
1159
|
return;
|
|
@@ -1145,6 +1161,7 @@ var useContextMenuPosition = ({
|
|
|
1145
1161
|
const compute = () => {
|
|
1146
1162
|
const trigger = triggerRef.current;
|
|
1147
1163
|
const panel = panelRef.current;
|
|
1164
|
+
const container = containerRef.current;
|
|
1148
1165
|
if (!trigger || !panel) return;
|
|
1149
1166
|
const triggerRect = trigger.getBoundingClientRect();
|
|
1150
1167
|
const panelRect = panel.getBoundingClientRect();
|
|
@@ -1184,6 +1201,15 @@ var useContextMenuPosition = ({
|
|
|
1184
1201
|
}
|
|
1185
1202
|
}
|
|
1186
1203
|
}
|
|
1204
|
+
if (container) {
|
|
1205
|
+
const containerRect = container.getBoundingClientRect();
|
|
1206
|
+
const cOffsetW = container.offsetWidth;
|
|
1207
|
+
const cOffsetH = container.offsetHeight;
|
|
1208
|
+
const scaleX = cOffsetW > 0 ? containerRect.width / cOffsetW : 1;
|
|
1209
|
+
const scaleY = cOffsetH > 0 ? containerRect.height / cOffsetH : 1;
|
|
1210
|
+
left = (left - containerRect.left) / (scaleX || 1);
|
|
1211
|
+
top = (top - containerRect.top) / (scaleY || 1);
|
|
1212
|
+
}
|
|
1187
1213
|
const next = {
|
|
1188
1214
|
top,
|
|
1189
1215
|
left,
|
|
@@ -1193,14 +1219,13 @@ var useContextMenuPosition = ({
|
|
|
1193
1219
|
(prev) => prev && prev.top === next.top && prev.left === next.left && prev.placement === next.placement ? prev : next
|
|
1194
1220
|
);
|
|
1195
1221
|
};
|
|
1196
|
-
|
|
1222
|
+
compute();
|
|
1197
1223
|
const onResize = () => compute();
|
|
1198
1224
|
window.addEventListener("resize", onResize);
|
|
1199
1225
|
return () => {
|
|
1200
|
-
window.cancelAnimationFrame(rafId);
|
|
1201
1226
|
window.removeEventListener("resize", onResize);
|
|
1202
1227
|
};
|
|
1203
|
-
}, [isOpen, placement, offset, triggerRef, panelRef]);
|
|
1228
|
+
}, [isOpen, placement, offset, triggerRef, panelRef, containerRef]);
|
|
1204
1229
|
return resolved;
|
|
1205
1230
|
};
|
|
1206
1231
|
|
|
@@ -1332,6 +1357,7 @@ var SEARCH_DEBOUNCE_MS = 200;
|
|
|
1332
1357
|
var ARROW_SIZE = 10;
|
|
1333
1358
|
var ARROW_HALF = ARROW_SIZE / 2;
|
|
1334
1359
|
var ARROW_EDGE_OFFSET = 12;
|
|
1360
|
+
var ARROW_TIP_PROTRUSION = ARROW_HALF * Math.SQRT2;
|
|
1335
1361
|
var PANEL_SHADOW = "0 4px 12px rgba(0,0,0,0.15)";
|
|
1336
1362
|
var textFromNode = (node) => {
|
|
1337
1363
|
if (node === null || node === void 0 || typeof node === "boolean")
|
|
@@ -1381,7 +1407,9 @@ var ContextMenuRoot = forwardRef(
|
|
|
1381
1407
|
isOpen: propIsOpen,
|
|
1382
1408
|
onOpenChange,
|
|
1383
1409
|
trigger,
|
|
1410
|
+
triggerFullWidth = false,
|
|
1384
1411
|
placement = "bottom-start",
|
|
1412
|
+
offset = 4,
|
|
1385
1413
|
position,
|
|
1386
1414
|
width,
|
|
1387
1415
|
maxHeight = 300,
|
|
@@ -1415,11 +1443,14 @@ var ContextMenuRoot = forwardRef(
|
|
|
1415
1443
|
const sizeStyles = xuiTheme.sizing.contextMenu(size);
|
|
1416
1444
|
const borderRadius = xuiTheme.shape?.contextMenu?.[size]?.borderRadius ?? 8;
|
|
1417
1445
|
const shouldCloseOnSelect = closeOnSelect ?? (type === "checkbox" ? false : true);
|
|
1446
|
+
const positionOffset = withArrow ? offset + ARROW_TIP_PROTRUSION : offset;
|
|
1418
1447
|
const positioned = useContextMenuPosition({
|
|
1419
1448
|
triggerRef,
|
|
1420
1449
|
panelRef,
|
|
1450
|
+
containerRef,
|
|
1421
1451
|
isOpen: isOpen && !!trigger && !position,
|
|
1422
|
-
placement
|
|
1452
|
+
placement,
|
|
1453
|
+
offset: positionOffset
|
|
1423
1454
|
});
|
|
1424
1455
|
const resolvedPlacement = positioned?.placement ?? placement;
|
|
1425
1456
|
const getArrowStyle = (arrowPlacement) => {
|
|
@@ -1494,7 +1525,7 @@ var ContextMenuRoot = forwardRef(
|
|
|
1494
1525
|
onClose: closeMenu,
|
|
1495
1526
|
triggerRef
|
|
1496
1527
|
});
|
|
1497
|
-
|
|
1528
|
+
useEffect3(() => {
|
|
1498
1529
|
if (!isOpen) {
|
|
1499
1530
|
cellsRef.current = [];
|
|
1500
1531
|
setCellsVersion((version) => version + 1);
|
|
@@ -1503,14 +1534,14 @@ var ContextMenuRoot = forwardRef(
|
|
|
1503
1534
|
setActiveSubmenuId(null);
|
|
1504
1535
|
}
|
|
1505
1536
|
}, [isOpen]);
|
|
1506
|
-
|
|
1537
|
+
useEffect3(() => {
|
|
1507
1538
|
const timer = setTimeout(
|
|
1508
1539
|
() => setDebouncedQuery(query),
|
|
1509
1540
|
SEARCH_DEBOUNCE_MS
|
|
1510
1541
|
);
|
|
1511
1542
|
return () => clearTimeout(timer);
|
|
1512
1543
|
}, [query]);
|
|
1513
|
-
|
|
1544
|
+
useEffect3(() => {
|
|
1514
1545
|
if (!isOpen || !trigger) return;
|
|
1515
1546
|
const onMouseDown = (event) => {
|
|
1516
1547
|
const target = event.target;
|
|
@@ -1525,7 +1556,7 @@ var ContextMenuRoot = forwardRef(
|
|
|
1525
1556
|
window.removeEventListener("scroll", onScroll);
|
|
1526
1557
|
};
|
|
1527
1558
|
}, [isOpen, trigger, closeMenu]);
|
|
1528
|
-
|
|
1559
|
+
useLayoutEffect4(() => {
|
|
1529
1560
|
if (!isOpen || !panelRef.current) return;
|
|
1530
1561
|
const searchbox = panelRef.current.querySelector('[role="searchbox"]');
|
|
1531
1562
|
const firstOption = panelRef.current.querySelector(
|
|
@@ -1533,13 +1564,13 @@ var ContextMenuRoot = forwardRef(
|
|
|
1533
1564
|
);
|
|
1534
1565
|
(searchbox ?? firstOption)?.focus();
|
|
1535
1566
|
}, [isOpen]);
|
|
1536
|
-
|
|
1567
|
+
useLayoutEffect4(() => {
|
|
1537
1568
|
if (!isOpen || !panelRef.current) return;
|
|
1538
1569
|
const activeElement = document.activeElement;
|
|
1539
1570
|
if (activeElement && panelRef.current.contains(activeElement)) return;
|
|
1540
1571
|
panelRef.current.focus();
|
|
1541
1572
|
}, [isOpen, cellsVersion]);
|
|
1542
|
-
|
|
1573
|
+
useEffect3(() => {
|
|
1543
1574
|
if (isOpen) return;
|
|
1544
1575
|
triggerRef.current?.focus();
|
|
1545
1576
|
}, [isOpen]);
|
|
@@ -1675,7 +1706,10 @@ var ContextMenuRoot = forwardRef(
|
|
|
1675
1706
|
onClick: (event) => {
|
|
1676
1707
|
if (!event.defaultPrevented) toggleMenu();
|
|
1677
1708
|
},
|
|
1678
|
-
style: {
|
|
1709
|
+
style: {
|
|
1710
|
+
display: "inline-flex",
|
|
1711
|
+
width: triggerFullWidth ? "100%" : "auto"
|
|
1712
|
+
},
|
|
1679
1713
|
children: cloneElement(trigger, {
|
|
1680
1714
|
"aria-haspopup": "menu",
|
|
1681
1715
|
"aria-expanded": isOpen ? "true" : "false"
|
|
@@ -1697,10 +1731,13 @@ var ContextMenuRoot = forwardRef(
|
|
|
1697
1731
|
position: "fixed",
|
|
1698
1732
|
left: position.x,
|
|
1699
1733
|
top: position.y
|
|
1700
|
-
} : trigger ? {
|
|
1701
|
-
position: "
|
|
1702
|
-
left: positioned
|
|
1703
|
-
top: positioned
|
|
1734
|
+
} : trigger ? positioned ? {
|
|
1735
|
+
position: "absolute",
|
|
1736
|
+
left: positioned.left,
|
|
1737
|
+
top: positioned.top
|
|
1738
|
+
} : {
|
|
1739
|
+
position: "absolute",
|
|
1740
|
+
visibility: "hidden"
|
|
1704
1741
|
} : void 0;
|
|
1705
1742
|
return /* @__PURE__ */ jsx4(ContextMenuContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs3(
|
|
1706
1743
|
"div",
|