@aurora-ds/components 1.7.19 → 1.7.21

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
@@ -3541,6 +3541,10 @@ const MENU_MIN_WIDTH_PX = 224;
3541
3541
  */
3542
3542
  const useMenuPosition = ({ anchorEl, open, menuRef, minWidth, gap = 4, placement = 'bottom', }) => {
3543
3543
  const [style, setStyle] = useState({});
3544
+ // Stays false until the rAF second pass has run with the real panel dimensions.
3545
+ // Keeps the panel invisible during the initial style={} state and the first
3546
+ // pass (menuHeight = 0) to prevent the position-jump flickering.
3547
+ const [isPositioned, setIsPositioned] = useState(false);
3544
3548
  const computePosition = useCallback(() => {
3545
3549
  if (!anchorEl) {
3546
3550
  return;
@@ -3621,14 +3625,19 @@ const useMenuPosition = ({ anchorEl, open, menuRef, minWidth, gap = 4, placement
3621
3625
  }
3622
3626
  else {
3623
3627
  setStyle({});
3628
+ setIsPositioned(false);
3624
3629
  }
3625
3630
  }, [open, computePosition]);
3626
- // Second pass: recompute after the panel renders to get actual height/width
3631
+ // Second pass: recompute after the panel renders to get actual height/width,
3632
+ // then mark as positioned so the panel becomes visible.
3627
3633
  useEffect(() => {
3628
3634
  if (!open) {
3629
3635
  return;
3630
3636
  }
3631
- const id = requestAnimationFrame(computePosition);
3637
+ const id = requestAnimationFrame(() => {
3638
+ computePosition();
3639
+ setIsPositioned(true);
3640
+ });
3632
3641
  return () => cancelAnimationFrame(id);
3633
3642
  }, [open, computePosition]);
3634
3643
  // Keep the menu glued to its anchor on scroll (nested containers) and resize.
@@ -3643,7 +3652,7 @@ const useMenuPosition = ({ anchorEl, open, menuRef, minWidth, gap = 4, placement
3643
3652
  window.removeEventListener('resize', computePosition);
3644
3653
  };
3645
3654
  }, [open, computePosition]);
3646
- return { style };
3655
+ return { style: { ...style, visibility: isPositioned ? 'visible' : 'hidden' } };
3647
3656
  };
3648
3657
 
3649
3658
  /** Custom DOM event fired on a submenu trigger item to request it opens. */
@@ -3967,7 +3976,7 @@ const HOVER_CLOSE_DELAY_MS = 160;
3967
3976
  * focus restoration, parent-keyboard pausing and the configurable close-on-click
3968
3977
  * behaviour.
3969
3978
  */
3970
- const useMenuItem = ({ ref, role, hasSubmenu, disabled, onClick, closeOnClick, }) => {
3979
+ const useMenuItem = ({ ref, role, hasSubmenu, disabled, onClick, closeOnClick, submenuTrigger = 'hover', }) => {
3971
3980
  const { setChildOpen, closeMenu } = useContext(MenuContext);
3972
3981
  const liRef = useRef(null);
3973
3982
  const mergedRef = useMergedRefs(ref, liRef);
@@ -4027,14 +4036,20 @@ const useMenuItem = ({ ref, role, hasSubmenu, disabled, onClick, closeOnClick, }
4027
4036
  return;
4028
4037
  }
4029
4038
  if (hasSubmenu) {
4030
- openSubmenu();
4039
+ if (submenuTrigger === 'click') {
4040
+ // Toggle: click again closes the submenu.
4041
+ setSubmenuOpen((prev) => !prev);
4042
+ }
4043
+ else {
4044
+ openSubmenu();
4045
+ }
4031
4046
  return;
4032
4047
  }
4033
4048
  onClick?.(event);
4034
4049
  if (shouldCloseOnClick) {
4035
4050
  closeMenu?.();
4036
4051
  }
4037
- }, [disabled, hasSubmenu, openSubmenu, onClick, shouldCloseOnClick, closeMenu]);
4052
+ }, [disabled, hasSubmenu, submenuTrigger, openSubmenu, onClick, shouldCloseOnClick, closeMenu]);
4038
4053
  return {
4039
4054
  liRef,
4040
4055
  mergedRef,
@@ -4044,6 +4059,7 @@ const useMenuItem = ({ ref, role, hasSubmenu, disabled, onClick, closeOnClick, }
4044
4059
  scheduleClose,
4045
4060
  clearTimers,
4046
4061
  closeSubmenu,
4062
+ submenuTrigger,
4047
4063
  };
4048
4064
  };
4049
4065
 
@@ -4118,13 +4134,14 @@ const MENU_ITEM_STYLES = createStyles((theme) => {
4118
4134
  };
4119
4135
  }, { id: 'menu-item' });
4120
4136
 
4121
- const MenuItem = ({ ref, label, icon, iconColor, role = 'menuitem', checked, selected, focused, disabled, size = 'default', closeOnClick, submenu, submenuPlacement = 'right', onClick, ...rest }) => {
4137
+ const MenuItem = ({ ref, label, icon, iconColor, role = 'menuitem', checked, selected, focused, disabled, size = 'default', closeOnClick, submenu, submenuTrigger = 'click', submenuPlacement = 'right', onClick, ...rest }) => {
4122
4138
  const hasSubmenu = submenu !== undefined;
4123
4139
  const isCheckable = role === 'menuitemcheckbox' || role === 'menuitemradio';
4124
4140
  const isOption = role === 'option';
4125
4141
  const isHighlighted = isCheckable ? Boolean(checked) : Boolean(selected);
4126
- const { liRef, mergedRef, submenuOpen, handleClick, scheduleOpen, scheduleClose, clearTimers, closeSubmenu, } = useMenuItem({ ref, role, hasSubmenu, disabled, onClick, closeOnClick });
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 }))] }));
4142
+ const { liRef, mergedRef, submenuOpen, handleClick, scheduleOpen, scheduleClose, clearTimers, closeSubmenu, } = useMenuItem({ ref, role, hasSubmenu, disabled, onClick, closeOnClick, submenuTrigger });
4143
+ const isHoverTrigger = submenuTrigger === 'hover';
4144
+ 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 && isHoverTrigger ? scheduleOpen : undefined, onMouseLeave: hasSubmenu && isHoverTrigger ? 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: isHoverTrigger ? clearTimers : undefined, onMouseLeave: isHoverTrigger ? scheduleClose : undefined, children: submenu }))] }));
4128
4145
  };
4129
4146
  MenuItem.displayName = 'MenuItem';
4130
4147