cynx-lib 1.3.91 → 1.3.92

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cynx-lib",
3
- "version": "1.3.91",
3
+ "version": "1.3.92",
4
4
  "type": "module",
5
5
  "main": "./index.cjs",
6
6
  "module": "./index.js",
@@ -1,44 +1,47 @@
1
1
  import { useState, useRef, useEffect, useCallback, useLayoutEffect } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
3
 
4
- function ActionSubmenu({ items, parentRect, closeAll, onMouseEnter, onMouseLeave }) {
4
+ function ActionSubmenu({ items, menuRect, itemRect, closeAll, onMouseEnter, onMouseLeave }) {
5
5
  const subRef = useRef(null);
6
- const [pos, setPos] = useState({
7
- top: parentRect ? parentRect.top - 5 : 0,
8
- left: parentRect ? parentRect.right + 4 : 0,
9
- transformOrigin: 'top left',
10
- });
11
6
 
12
7
  useLayoutEffect(() => {
13
- if (!subRef.current || !parentRect) return;
14
- const subWidth = subRef.current.offsetWidth || 190;
15
- const subHeight = subRef.current.offsetHeight || 150;
8
+ const sub = subRef.current;
9
+ if (!sub || !menuRect || !itemRect) return;
16
10
 
17
- const spaceRight = window.innerWidth - parentRect.right;
11
+ const subWidth = sub.offsetWidth || 190;
12
+ const subHeight = sub.offsetHeight || 150;
13
+
14
+ // Determine horizontal docking relative to PARENT MENU container
15
+ const spaceRight = window.innerWidth - menuRect.right;
18
16
  let left;
19
- let transformOriginX = 'left';
20
- if (spaceRight >= subWidth + 12) {
21
- left = parentRect.right + 4;
22
- transformOriginX = 'left';
17
+ let originX = 'left';
18
+
19
+ if (spaceRight >= subWidth + 8) {
20
+ // Dock cleanly to the right side of parent menu
21
+ left = menuRect.right + 4;
22
+ originX = 'left';
23
23
  } else {
24
- left = parentRect.left - subWidth - 4;
25
- transformOriginX = 'right';
24
+ // Dock cleanly to the left side of parent menu (never overlaps!)
25
+ left = menuRect.left - subWidth - 4;
26
+ originX = 'right';
26
27
  }
28
+
29
+ // Keep within viewport bounds
27
30
  left = Math.max(8, Math.min(left, window.innerWidth - subWidth - 8));
28
31
 
29
- let top = parentRect.top - 5;
30
- let transformOriginY = 'top';
31
- if (top + subHeight > window.innerHeight - 10) {
32
- top = Math.max(10, window.innerHeight - subHeight - 10);
33
- transformOriginY = 'bottom';
32
+ // Determine vertical docking relative to the active item
33
+ let top = itemRect.top - 5;
34
+ let originY = 'top';
35
+
36
+ if (top + subHeight > window.innerHeight - 8) {
37
+ top = Math.max(8, window.innerHeight - subHeight - 8);
38
+ originY = 'bottom';
34
39
  }
35
40
 
36
- setPos({
37
- top,
38
- left,
39
- transformOrigin: `${transformOriginY} ${transformOriginX}`,
40
- });
41
- }, [parentRect]);
41
+ sub.style.top = top + 'px';
42
+ sub.style.left = left + 'px';
43
+ sub.style.transformOrigin = `${originY} ${originX}`;
44
+ }, [menuRect, itemRect]);
42
45
 
43
46
  return createPortal(
44
47
  <div
@@ -51,9 +54,8 @@ function ActionSubmenu({ items, parentRect, closeAll, onMouseEnter, onMouseLeave
51
54
  zIndex: 100000,
52
55
  minWidth: 190,
53
56
  padding: 5,
54
- top: pos.top + 'px',
55
- left: pos.left + 'px',
56
- transformOrigin: pos.transformOrigin,
57
+ top: itemRect ? (itemRect.top - 5) + 'px' : '0px',
58
+ left: menuRect ? (menuRect.left - 200) + 'px' : '0px',
57
59
  background: 'var(--surface,#fff)',
58
60
  border: '1.5px solid var(--border-light,#e2e8f0)',
59
61
  borderRadius: 'var(--rads,8px)',
@@ -72,6 +74,7 @@ function ActionSubmenu({ items, parentRect, closeAll, onMouseEnter, onMouseLeave
72
74
  display: 'flex', alignItems: 'center', gap: 8,
73
75
  padding: '6px 10px', borderRadius: 'var(--rads,8px)',
74
76
  fontSize: 12, cursor: 'pointer', transition: 'all .14s',
77
+ whiteSpace: 'nowrap',
75
78
  color: (subItem.variant === 'danger' || subItem.danger) ? 'var(--danger, #ef4444)' : subItem.variant === 'success' ? 'var(--success, #16a34a)' : subItem.variant === 'warning' ? 'var(--warning, #f59e0b)' : 'var(--primary-text, #18181b)',
76
79
  fontWeight: 500,
77
80
  }}
@@ -99,7 +102,7 @@ function ActionSubmenu({ items, parentRect, closeAll, onMouseEnter, onMouseLeave
99
102
  export function ActionsMenu({ items, align = 'right' }) {
100
103
  const [open, setOpen] = useState(false);
101
104
  const [closing, setClosing] = useState(false);
102
- const [activeSub, setActiveSub] = useState(null); // { index, items, rect }
105
+ const [activeSub, setActiveSub] = useState(null); // { index, items, itemRect, menuRect }
103
106
  const wrapRef = useRef(null);
104
107
  const menuRef = useRef(null);
105
108
  const btnRef = useRef(null);
@@ -114,8 +117,9 @@ export function ActionsMenu({ items, align = 'right' }) {
114
117
 
115
118
  const openSub = (index, subItems, e) => {
116
119
  clearTimeout(subTimerRef.current);
117
- const rect = e.currentTarget.getBoundingClientRect();
118
- setActiveSub({ index, items: subItems, rect });
120
+ const itemRect = e.currentTarget.getBoundingClientRect();
121
+ const menuRect = menuRef.current ? menuRef.current.getBoundingClientRect() : itemRect;
122
+ setActiveSub({ index, items: subItems, itemRect, menuRect });
119
123
  };
120
124
 
121
125
  const scheduleCloseSub = () => {
@@ -280,6 +284,7 @@ export function ActionsMenu({ items, align = 'right' }) {
280
284
  display: 'flex', alignItems: 'center', gap: 8,
281
285
  padding: '6px 10px', borderRadius: 'var(--rads,8px)',
282
286
  fontSize: 12, cursor: 'pointer', transition: 'all .14s',
287
+ whiteSpace: 'nowrap',
283
288
  background: isSubActive ? 'var(--grey-1, #f4f4f5)' : 'transparent',
284
289
  color: (item.variant === 'danger' || item.danger)
285
290
  ? 'var(--danger, #ef4444)'
@@ -325,7 +330,8 @@ export function ActionsMenu({ items, align = 'right' }) {
325
330
  {open && activeSub && (
326
331
  <ActionSubmenu
327
332
  items={activeSub.items}
328
- parentRect={activeSub.rect}
333
+ menuRect={activeSub.menuRect}
334
+ itemRect={activeSub.itemRect}
329
335
  closeAll={close}
330
336
  onMouseEnter={cancelCloseSub}
331
337
  onMouseLeave={scheduleCloseSub}