@aurora-ds/components 1.7.17 → 1.7.19
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/dist/cjs/index.js +57 -19
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +57 -19
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +26 -11
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -3524,12 +3524,12 @@ const MENU_MIN_WIDTH_PX = 224;
|
|
|
3524
3524
|
* Computes and continuously updates the `position: fixed` style for a menu panel.
|
|
3525
3525
|
*
|
|
3526
3526
|
* Positioning strategy:
|
|
3527
|
-
* - `placement="bottom"` (default):
|
|
3528
|
-
*
|
|
3529
|
-
*
|
|
3530
|
-
* - `placement="
|
|
3531
|
-
*
|
|
3532
|
-
*
|
|
3527
|
+
* - `placement="bottom"` (default): opens below the anchor; overflows are shifted up.
|
|
3528
|
+
* - `placement="top"`: opens above the anchor; flips to bottom when no room.
|
|
3529
|
+
* - `placement="right"`: opens to the right of the anchor; flips to the left when no room.
|
|
3530
|
+
* - `placement="left"`: opens to the left of the anchor; flips to the right when no room.
|
|
3531
|
+
*
|
|
3532
|
+
* In all cases the vertical position is clamped inside the viewport.
|
|
3533
3533
|
*
|
|
3534
3534
|
* Scroll handling: the page scroll is locked while the menu is open (see
|
|
3535
3535
|
* `MenuPanel`), so the anchor cannot move out from under the menu. As a safety
|
|
@@ -3551,24 +3551,56 @@ const useMenuPosition = ({ anchorEl, open, menuRef, minWidth, gap = 4, placement
|
|
|
3551
3551
|
const menuWidth = menuEl?.offsetWidth ?? 0;
|
|
3552
3552
|
const viewportRight = window.innerWidth - VIEWPORT_MARGIN_PX;
|
|
3553
3553
|
const viewportBottom = window.innerHeight - VIEWPORT_MARGIN_PX;
|
|
3554
|
-
if (placement === 'right') {
|
|
3554
|
+
if (placement === 'right' || placement === 'left') {
|
|
3555
3555
|
// --- Vertical: align with the anchor top, clamp inside the viewport ---
|
|
3556
3556
|
let top = anchor.top;
|
|
3557
3557
|
if (menuHeight > 0 && top + menuHeight > viewportBottom) {
|
|
3558
3558
|
top = Math.max(VIEWPORT_MARGIN_PX, viewportBottom - menuHeight);
|
|
3559
3559
|
}
|
|
3560
|
-
// --- Horizontal: open to the
|
|
3561
|
-
let left
|
|
3562
|
-
if (
|
|
3563
|
-
|
|
3564
|
-
left
|
|
3565
|
-
|
|
3566
|
-
|
|
3560
|
+
// --- Horizontal: open to the preferred side, flip to the opposite when no room ---
|
|
3561
|
+
let left;
|
|
3562
|
+
if (placement === 'right') {
|
|
3563
|
+
left = anchor.right + gap;
|
|
3564
|
+
if (menuWidth > 0 && left + menuWidth > viewportRight) {
|
|
3565
|
+
const flippedLeft = anchor.left - gap - menuWidth;
|
|
3566
|
+
left = flippedLeft >= VIEWPORT_MARGIN_PX
|
|
3567
|
+
? flippedLeft
|
|
3568
|
+
: Math.max(VIEWPORT_MARGIN_PX, viewportRight - menuWidth);
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
else {
|
|
3572
|
+
// placement === 'left'
|
|
3573
|
+
left = anchor.left - gap - menuWidth;
|
|
3574
|
+
if (menuWidth > 0 && left < VIEWPORT_MARGIN_PX) {
|
|
3575
|
+
const flippedLeft = anchor.right + gap;
|
|
3576
|
+
left = flippedLeft + menuWidth <= viewportRight
|
|
3577
|
+
? flippedLeft
|
|
3578
|
+
: Math.max(VIEWPORT_MARGIN_PX, viewportRight - menuWidth);
|
|
3579
|
+
}
|
|
3567
3580
|
}
|
|
3568
3581
|
setStyle({ top, left, minWidth: minWidth ?? MENU_MIN_WIDTH_PX });
|
|
3569
3582
|
return;
|
|
3570
3583
|
}
|
|
3571
|
-
|
|
3584
|
+
if (placement === 'top') {
|
|
3585
|
+
// --- Vertical: open above, flip to bottom when no room ---
|
|
3586
|
+
let top = anchor.top - gap - menuHeight;
|
|
3587
|
+
if (menuHeight > 0 && top < VIEWPORT_MARGIN_PX) {
|
|
3588
|
+
const flippedTop = anchor.bottom + gap;
|
|
3589
|
+
top = flippedTop + menuHeight <= viewportBottom
|
|
3590
|
+
? flippedTop
|
|
3591
|
+
: Math.max(VIEWPORT_MARGIN_PX, viewportBottom - menuHeight);
|
|
3592
|
+
}
|
|
3593
|
+
// --- Horizontal ---
|
|
3594
|
+
const fallbackMinWidth = Math.max(anchor.width, MENU_MIN_WIDTH_PX);
|
|
3595
|
+
const resolvedMinWidth = typeof minWidth === 'number' ? minWidth : fallbackMinWidth;
|
|
3596
|
+
const effectiveWidth = Math.max(menuWidth, resolvedMinWidth);
|
|
3597
|
+
const maxLeft = window.innerWidth - effectiveWidth - VIEWPORT_MARGIN_PX;
|
|
3598
|
+
const left = Math.max(VIEWPORT_MARGIN_PX, Math.min(anchor.left, maxLeft));
|
|
3599
|
+
setStyle({ top, left, minWidth: minWidth ?? fallbackMinWidth });
|
|
3600
|
+
return;
|
|
3601
|
+
}
|
|
3602
|
+
// --- placement === 'bottom' ---
|
|
3603
|
+
// --- Vertical ---
|
|
3572
3604
|
const preferredTop = anchor.bottom + gap;
|
|
3573
3605
|
let top = preferredTop;
|
|
3574
3606
|
if (menuHeight > 0 && preferredTop + menuHeight > viewportBottom) {
|
|
@@ -3645,7 +3677,9 @@ const useMenuPanel = ({ open, onClose, anchorEl, minWidth, placement = 'bottom',
|
|
|
3645
3677
|
}
|
|
3646
3678
|
return Array.from(panelRef.current.querySelectorAll('[role^="menuitem"]:not([data-disabled]), [role="option"]:not([data-disabled])')).filter((el) => el.closest('[data-menu-panel]') === panelRef.current);
|
|
3647
3679
|
}, []);
|
|
3648
|
-
// On open: focus panel,
|
|
3680
|
+
// On open: focus panel, pre-select a checked/selected item if any.
|
|
3681
|
+
// When nothing is pre-selected, leave focusedIndex at -1 so no item appears
|
|
3682
|
+
// highlighted on mouse-open — the first ArrowDown/Up will start navigation.
|
|
3649
3683
|
useEffect(() => {
|
|
3650
3684
|
if (!open) {
|
|
3651
3685
|
setFocusedIndex(-1);
|
|
@@ -3655,7 +3689,7 @@ const useMenuPanel = ({ open, onClose, anchorEl, minWidth, placement = 'bottom',
|
|
|
3655
3689
|
panelRef.current?.focus();
|
|
3656
3690
|
const options = getOptions();
|
|
3657
3691
|
const selectedIdx = options.findIndex((el) => el.getAttribute('aria-checked') === 'true' || el.getAttribute('data-selected') !== null);
|
|
3658
|
-
setFocusedIndex(selectedIdx >= 0 ? selectedIdx :
|
|
3692
|
+
setFocusedIndex(selectedIdx >= 0 ? selectedIdx : -1);
|
|
3659
3693
|
});
|
|
3660
3694
|
return () => cancelAnimationFrame(raf);
|
|
3661
3695
|
}, [open, getOptions]);
|
|
@@ -3700,7 +3734,11 @@ const useMenuPanel = ({ open, onClose, anchorEl, minWidth, placement = 'bottom',
|
|
|
3700
3734
|
e.preventDefault();
|
|
3701
3735
|
setFocusedIndex((prev) => {
|
|
3702
3736
|
const count = getOptions().length;
|
|
3703
|
-
|
|
3737
|
+
if (count === 0) {
|
|
3738
|
+
return prev;
|
|
3739
|
+
}
|
|
3740
|
+
// prev === -1 means no item focused yet → jump to last item
|
|
3741
|
+
return prev <= 0 ? count - 1 : prev - 1;
|
|
3704
3742
|
});
|
|
3705
3743
|
},
|
|
3706
3744
|
ArrowRight: (e) => {
|
|
@@ -4086,7 +4124,7 @@ const MenuItem = ({ ref, label, icon, iconColor, role = 'menuitem', checked, sel
|
|
|
4086
4124
|
const isOption = role === 'option';
|
|
4087
4125
|
const isHighlighted = isCheckable ? Boolean(checked) : Boolean(selected);
|
|
4088
4126
|
const { liRef, mergedRef, submenuOpen, handleClick, scheduleOpen, scheduleClose, clearTimers, closeSubmenu, } = useMenuItem({ ref, role, hasSubmenu, disabled, onClick, closeOnClick });
|
|
4089
|
-
return (jsxs(Fragment$1, { children: [jsxs("li", { ref: mergedRef, role: role, "aria-checked": isCheckable ? Boolean(checked) : undefined, "aria-selected": isOption ? Boolean(selected) : undefined, "aria-disabled": disabled, "aria-haspopup": hasSubmenu ? 'menu' : undefined, "aria-expanded": hasSubmenu ? submenuOpen : undefined, "data-selected": isHighlighted || undefined, "data-focused": focused || undefined, "data-disabled": disabled || undefined, className: MENU_ITEM_STYLES.root({ size }), onClick: handleClick, onMouseEnter: hasSubmenu && !disabled ? scheduleOpen : undefined, onMouseLeave: hasSubmenu ? scheduleClose : undefined, ...rest, children: [isCheckable && (jsxs("span", { className: MENU_ITEM_STYLES.indicator, "aria-hidden": true, children: [checked && role === 'menuitemcheckbox' && (jsx(Icon, { icon: CheckIcon, size: 'sm', strokeColor: 'primaryMain' })), checked && role === 'menuitemradio' && (jsx("span", { className: MENU_ITEM_STYLES.radioDot }))] })), icon !== undefined && (jsx(Icon, { icon: icon, size: 'sm', strokeColor: iconColor ?? (isHighlighted ? 'primaryMain' : 'textSecondary') })), jsx(Text, { variant: 'span', fontSize: 'sm', className: MENU_ITEM_STYLES.label, children: label }), hasSubmenu && (jsx("span", { className: MENU_ITEM_STYLES.submenuChevron, "aria-hidden": true, children: jsx(Icon, { icon: ChevronRightIcon, size: 'sm', strokeColor: 'textTertiary' }) }))] }), hasSubmenu && (jsx(MenuPanel, { open: submenuOpen, onClose: () => closeSubmenu(true), onArrowLeft: () => closeSubmenu(true), anchorEl: liRef.current, placement: submenuPlacement, isSubmenu: true, "aria-label": label, onMouseEnter: clearTimers, onMouseLeave: scheduleClose, children: submenu }))] }));
|
|
4127
|
+
return (jsxs(Fragment$1, { children: [jsxs("li", { ref: mergedRef, role: role, "aria-checked": isCheckable ? Boolean(checked) : undefined, "aria-selected": isOption ? Boolean(selected) : undefined, "aria-disabled": disabled, "aria-haspopup": hasSubmenu ? 'menu' : undefined, "aria-expanded": hasSubmenu ? submenuOpen : undefined, "data-selected": isHighlighted || undefined, "data-focused": focused || undefined, "data-disabled": disabled || undefined, className: MENU_ITEM_STYLES.root({ size }), onClick: handleClick, onMouseEnter: hasSubmenu && !disabled ? scheduleOpen : undefined, onMouseLeave: hasSubmenu ? scheduleClose : undefined, ...rest, children: [isCheckable && (jsxs("span", { className: MENU_ITEM_STYLES.indicator, "aria-hidden": true, children: [checked && role === 'menuitemcheckbox' && (jsx(Icon, { icon: CheckIcon, size: size === 'default' ? 'sm' : 'md', strokeColor: 'primaryMain' })), checked && role === 'menuitemradio' && (jsx("span", { className: MENU_ITEM_STYLES.radioDot }))] })), icon !== undefined && (jsx(Icon, { icon: icon, size: 'sm', strokeColor: iconColor ?? (isHighlighted ? 'primaryMain' : 'textSecondary') })), jsx(Text, { variant: 'span', fontSize: 'sm', className: MENU_ITEM_STYLES.label, children: label }), hasSubmenu && (jsx("span", { className: MENU_ITEM_STYLES.submenuChevron, "aria-hidden": true, children: jsx(Icon, { icon: ChevronRightIcon, size: 'sm', strokeColor: 'textTertiary' }) }))] }), hasSubmenu && (jsx(MenuPanel, { open: submenuOpen, onClose: () => closeSubmenu(true), onArrowLeft: () => closeSubmenu(true), anchorEl: liRef.current, placement: submenuPlacement, isSubmenu: true, "aria-label": label, onMouseEnter: clearTimers, onMouseLeave: scheduleClose, children: submenu }))] }));
|
|
4090
4128
|
};
|
|
4091
4129
|
MenuItem.displayName = 'MenuItem';
|
|
4092
4130
|
|