@aurora-ds/components 1.8.1 → 1.8.3

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 CHANGED
@@ -3486,6 +3486,13 @@ width = '100%', minWidth = 0, maxWidth, flex, flexGrow, flexShrink, flexBasis, .
3486
3486
  };
3487
3487
  TextField.displayName = 'TextField';
3488
3488
 
3489
+ /**
3490
+ * Vertical padding (in px) applied to the top and bottom of the menu panel.
3491
+ * Matches `theme.spacing.xs` (0.25rem = 4px at 16px base font).
3492
+ * Exported so submenus can offset their position upward by this amount to align
3493
+ * their first item with the trigger item.
3494
+ */
3495
+ const MENU_PANEL_PADDING_Y_PX = 5;
3489
3496
  const MENU_PANEL_STYLES = theme.createStyles((theme) => ({
3490
3497
  backdrop: {
3491
3498
  position: 'fixed',
@@ -3559,7 +3566,7 @@ const MENU_MIN_WIDTH_PX = 224;
3559
3566
  * Uses a two-pass strategy: first render with `menuHeight = 0`, then a rAF
3560
3567
  * recompute with the actual rendered size to apply any overflow offset.
3561
3568
  */
3562
- const useMenuPosition = ({ anchorEl, open, menuRef, minWidth, gap = 4, placement = 'bottom', }) => {
3569
+ const useMenuPosition = ({ anchorEl, open, menuRef, minWidth, gap = 4, verticalOffset = 0, placement = 'bottom', }) => {
3563
3570
  const [style, setStyle] = React.useState({});
3564
3571
  // Stays false until the rAF second pass has run with the real panel dimensions.
3565
3572
  // Keeps the panel invisible during the initial style={} state and the first
@@ -3576,8 +3583,10 @@ const useMenuPosition = ({ anchorEl, open, menuRef, minWidth, gap = 4, placement
3576
3583
  const viewportRight = window.innerWidth - VIEWPORT_MARGIN_PX;
3577
3584
  const viewportBottom = window.innerHeight - VIEWPORT_MARGIN_PX;
3578
3585
  if (placement === 'right' || placement === 'left') {
3579
- // --- Vertical: align with the anchor top, clamp inside the viewport ---
3580
- let top = anchor.top;
3586
+ // --- Vertical: align with the anchor top, offset upward by verticalOffset
3587
+ // (e.g. the panel's paddingTop) so the first item lines up with the trigger,
3588
+ // then clamp inside the viewport. ---
3589
+ let top = anchor.top - verticalOffset;
3581
3590
  if (menuHeight > 0 && top + menuHeight > viewportBottom) {
3582
3591
  top = Math.max(VIEWPORT_MARGIN_PX, viewportBottom - menuHeight);
3583
3592
  }
@@ -3637,7 +3646,7 @@ const useMenuPosition = ({ anchorEl, open, menuRef, minWidth, gap = 4, placement
3637
3646
  const maxLeft = window.innerWidth - effectiveWidth - VIEWPORT_MARGIN_PX;
3638
3647
  const left = Math.max(VIEWPORT_MARGIN_PX, Math.min(anchor.left, maxLeft));
3639
3648
  setStyle({ top, left, minWidth: minWidth ?? fallbackMinWidth });
3640
- }, [anchorEl, menuRef, minWidth, gap, placement]);
3649
+ }, [anchorEl, menuRef, minWidth, gap, verticalOffset, placement]);
3641
3650
  // First pass: compute as soon as the menu opens (menuHeight = 0)
3642
3651
  React.useLayoutEffect(() => {
3643
3652
  if (open) {
@@ -3710,12 +3719,12 @@ const ITEM_SELECTOR = '[role^="menuitem"]:not([data-disabled]), [role="option"]:
3710
3719
  * `aria-activedescendant`. Item order is read from the DOM (read-only) so the
3711
3720
  * logic stays correct across groups and dynamic content.
3712
3721
  */
3713
- const useMenuPanel = ({ open, onClose, anchorEl, minWidth, placement = 'bottom', onArrowLeft, }) => {
3722
+ const useMenuPanel = ({ open, onClose, anchorEl, minWidth, placement = 'bottom', verticalOffset = 0, onArrowLeft, }) => {
3714
3723
  const panelRef = React.useRef(null);
3715
3724
  const [focusedId, setFocusedId] = React.useState(undefined);
3716
3725
  // True while a descendant submenu is open → pause this panel's keyboard nav.
3717
3726
  const [childOpen, setChildOpen] = React.useState(false);
3718
- const { style } = useMenuPosition({ anchorEl, open, menuRef: panelRef, minWidth, placement });
3727
+ const { style } = useMenuPosition({ anchorEl, open, menuRef: panelRef, minWidth, placement, verticalOffset });
3719
3728
  /** Read this panel's focusable items in DOM order (excluding nested submenus). */
3720
3729
  const getItems = React.useCallback(() => {
3721
3730
  const panel = panelRef.current;
@@ -3929,6 +3938,9 @@ const MenuPanel = ({ open, onClose, anchorEl, minWidth, maxHeight = '20rem', pla
3929
3938
  anchorEl,
3930
3939
  minWidth,
3931
3940
  placement,
3941
+ // Shift submenus upward by the panel's paddingTop so the first item aligns
3942
+ // visually with the trigger item in the parent menu.
3943
+ verticalOffset: isSubmenu ? MENU_PANEL_PADDING_Y_PX : 0,
3932
3944
  onArrowLeft,
3933
3945
  });
3934
3946
  const contextValue = React.useMemo(() => ({ setChildOpen, closeMenu }), [setChildOpen, closeMenu]);