@xsolla/xui-context-menu 0.173.1 → 0.174.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.
package/web/index.mjs CHANGED
@@ -962,6 +962,7 @@ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
962
962
  var SUBMENU_GAP2 = 4;
963
963
  var OPEN_DELAY_MS = 200;
964
964
  var CLOSE_GRACE_MS = 100;
965
+ var _submenuIdCounter = 0;
965
966
  var ContextMenuSubmenu = ({
966
967
  label,
967
968
  icon,
@@ -976,6 +977,11 @@ var ContextMenuSubmenu = ({
976
977
  const size = propSize || context?.size || "md";
977
978
  const sizeStyles = xuiTheme.sizing.contextMenu(size);
978
979
  const borderRadius = xuiTheme.shape?.contextMenu?.[size]?.borderRadius ?? xuiTheme.radius?.button ?? 8;
980
+ const submenuIdRef = useRef(null);
981
+ if (submenuIdRef.current === null) {
982
+ submenuIdRef.current = `xui-cm-submenu-${_submenuIdCounter += 1}`;
983
+ }
984
+ const submenuId = submenuIdRef.current;
979
985
  const [isOpen, setIsOpen] = useState2(false);
980
986
  const [visible, setVisible] = useState2(false);
981
987
  const [openLeft, setOpenLeft] = useState2(false);
@@ -988,12 +994,19 @@ var ContextMenuSubmenu = ({
988
994
  if (openTimerRef.current) clearTimeout(openTimerRef.current);
989
995
  if (closeTimerRef.current) clearTimeout(closeTimerRef.current);
990
996
  }, []);
997
+ useEffect2(() => {
998
+ if (!context?.activeSubmenuId) return;
999
+ if (context.activeSubmenuId !== submenuId) {
1000
+ setIsOpen(false);
1001
+ }
1002
+ }, [context?.activeSubmenuId, submenuId]);
991
1003
  const calculatePlacement = useCallback(() => {
992
1004
  if (!triggerRef.current) return;
993
1005
  const triggerRect = triggerRef.current.getBoundingClientRect();
994
1006
  const estimatedWidth = sizeStyles.minWidth + 32;
995
1007
  const wouldOverflowRight = triggerRect.right + estimatedWidth + SUBMENU_GAP2 > window.innerWidth - 8;
996
- setOpenLeft(wouldOverflowRight);
1008
+ const hasSpaceOnLeft = triggerRect.left - SUBMENU_GAP2 - estimatedWidth >= 8;
1009
+ setOpenLeft(wouldOverflowRight && hasSpaceOnLeft);
997
1010
  setTopOffset(0);
998
1011
  }, [sizeStyles.minWidth]);
999
1012
  useLayoutEffect2(() => {
@@ -1001,7 +1014,8 @@ var ContextMenuSubmenu = ({
1001
1014
  const submenuRect = submenuRef.current.getBoundingClientRect();
1002
1015
  const triggerRect = triggerRef.current.getBoundingClientRect();
1003
1016
  const wouldOverflowRight = triggerRect.right + submenuRect.width + SUBMENU_GAP2 > window.innerWidth - 8;
1004
- setOpenLeft(wouldOverflowRight);
1017
+ const hasSpaceOnLeft = triggerRect.left - SUBMENU_GAP2 - submenuRect.width >= 8;
1018
+ setOpenLeft(wouldOverflowRight && hasSpaceOnLeft);
1005
1019
  const overflowBottom = triggerRect.top + submenuRect.height - (window.innerHeight - 8);
1006
1020
  if (overflowBottom > 0) {
1007
1021
  setTopOffset(-Math.min(overflowBottom, triggerRect.top - 8));
@@ -1024,16 +1038,27 @@ var ContextMenuSubmenu = ({
1024
1038
  openTimerRef.current = setTimeout(() => {
1025
1039
  calculatePlacement();
1026
1040
  setIsOpen(true);
1041
+ context?.setActiveSubmenuId(submenuId);
1027
1042
  }, OPEN_DELAY_MS);
1028
1043
  };
1029
1044
  const handleTriggerLeave = () => {
1030
1045
  clearTimers();
1031
- closeTimerRef.current = setTimeout(() => setIsOpen(false), CLOSE_GRACE_MS);
1046
+ closeTimerRef.current = setTimeout(() => {
1047
+ setIsOpen(false);
1048
+ if (context?.activeSubmenuId === submenuId) {
1049
+ context.setActiveSubmenuId(null);
1050
+ }
1051
+ }, CLOSE_GRACE_MS);
1032
1052
  };
1033
1053
  const handleSubmenuEnter = () => clearTimers();
1034
1054
  const handleSubmenuLeave = () => {
1035
1055
  clearTimers();
1036
- closeTimerRef.current = setTimeout(() => setIsOpen(false), CLOSE_GRACE_MS);
1056
+ closeTimerRef.current = setTimeout(() => {
1057
+ setIsOpen(false);
1058
+ if (context?.activeSubmenuId === submenuId) {
1059
+ context.setActiveSubmenuId(null);
1060
+ }
1061
+ }, CLOSE_GRACE_MS);
1037
1062
  };
1038
1063
  const submenuPositionStyle = openLeft ? { right: `calc(100% + ${SUBMENU_GAP2}px)` } : { left: `calc(100% + ${SUBMENU_GAP2}px)` };
1039
1064
  return /* @__PURE__ */ jsxs2(
@@ -1364,6 +1389,7 @@ var ContextMenuRoot = forwardRef(
1364
1389
  const [cellsVersion, setCellsVersion] = useState4(0);
1365
1390
  const [query, setQuery] = useState4("");
1366
1391
  const [debouncedQuery, setDebouncedQuery] = useState4("");
1392
+ const [activeSubmenuId, setActiveSubmenuId] = useState4(null);
1367
1393
  const containerRef = useRef2(null);
1368
1394
  const triggerRef = useRef2(null);
1369
1395
  const panelRef = useRef2(null);
@@ -1430,6 +1456,7 @@ var ContextMenuRoot = forwardRef(
1430
1456
  setCellsVersion((version) => version + 1);
1431
1457
  setQuery("");
1432
1458
  setDebouncedQuery("");
1459
+ setActiveSubmenuId(null);
1433
1460
  }
1434
1461
  }, [isOpen]);
1435
1462
  useEffect4(() => {
@@ -1484,7 +1511,9 @@ var ContextMenuRoot = forwardRef(
1484
1511
  getCellIndex,
1485
1512
  cellsVersion,
1486
1513
  query,
1487
- setQuery
1514
+ setQuery,
1515
+ activeSubmenuId,
1516
+ setActiveSubmenuId
1488
1517
  }),
1489
1518
  [
1490
1519
  size,
@@ -1495,7 +1524,8 @@ var ContextMenuRoot = forwardRef(
1495
1524
  unregisterCell,
1496
1525
  getCellIndex,
1497
1526
  cellsVersion,
1498
- query
1527
+ query,
1528
+ activeSubmenuId
1499
1529
  ]
1500
1530
  );
1501
1531
  const renderPresetItem = (item, index2) => {