@aurora-ds/components 1.7.18 → 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/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): the menu opens below the anchor. If it would
3528
- * overflow the bottom of the viewport, `top` is shifted up so the menu bottom
3529
- * lands at `viewportHeight - margin` (never going off the top of the screen).
3530
- * - `placement="right"` (submenu): the menu opens to the right of the anchor and
3531
- * flips to the left when there is no horizontal room. Its `top` aligns with the
3532
- * anchor top and is clamped to the viewport.
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 right, flip to the left when no room ---
3561
- let left = anchor.right + gap;
3562
- if (menuWidth > 0 && left + menuWidth > viewportRight) {
3563
- const flippedLeft = anchor.left - gap - menuWidth;
3564
- left = flippedLeft >= VIEWPORT_MARGIN_PX
3565
- ? flippedLeft
3566
- : Math.max(VIEWPORT_MARGIN_PX, viewportRight - menuWidth);
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
- // --- Vertical (bottom) ---
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) {
@@ -4092,7 +4124,7 @@ const MenuItem = ({ ref, label, icon, iconColor, role = 'menuitem', checked, sel
4092
4124
  const isOption = role === 'option';
4093
4125
  const isHighlighted = isCheckable ? Boolean(checked) : Boolean(selected);
4094
4126
  const { liRef, mergedRef, submenuOpen, handleClick, scheduleOpen, scheduleClose, clearTimers, closeSubmenu, } = useMenuItem({ ref, role, hasSubmenu, disabled, onClick, closeOnClick });
4095
- 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 }))] }));
4096
4128
  };
4097
4129
  MenuItem.displayName = 'MenuItem';
4098
4130