@aurora-ds/components 1.8.0 → 1.8.2
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 +30 -9
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +30 -9
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/package.json +1 -1
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 = 6;
|
|
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,
|
|
3580
|
-
|
|
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]);
|
|
@@ -4094,17 +4106,18 @@ const MENU_ITEM_STYLES = theme.createStyles((theme) => {
|
|
|
4094
4106
|
display: 'flex',
|
|
4095
4107
|
alignItems: 'center',
|
|
4096
4108
|
gap: theme.spacing.sm,
|
|
4097
|
-
paddingLeft: theme.spacing.md
|
|
4109
|
+
paddingLeft: `calc(${theme.spacing.md} - 2px)`,
|
|
4098
4110
|
paddingRight: theme.spacing.md,
|
|
4099
4111
|
...(size === 'default'
|
|
4100
4112
|
? { height: DEFAULT_DRAWER_ITEM_SIZE }
|
|
4101
4113
|
: { paddingTop: theme.spacing.xs, paddingBottom: theme.spacing.xs }),
|
|
4114
|
+
borderLeft: '2px solid transparent',
|
|
4102
4115
|
cursor: 'pointer',
|
|
4103
4116
|
userSelect: 'none',
|
|
4104
4117
|
color: c.textPrimary,
|
|
4105
4118
|
fontSize: theme.fontSize.sm,
|
|
4106
4119
|
listStyle: 'none',
|
|
4107
|
-
transition: `background-color ${theme.transition.fast}`,
|
|
4120
|
+
transition: `background-color ${theme.transition.fast}, border-color ${theme.transition.fast}`,
|
|
4108
4121
|
'&[data-selected]': {
|
|
4109
4122
|
backgroundColor: c.primarySubtle,
|
|
4110
4123
|
color: c.primaryMain,
|
|
@@ -4116,6 +4129,14 @@ const MENU_ITEM_STYLES = theme.createStyles((theme) => {
|
|
|
4116
4129
|
'&[data-focused][data-selected]': {
|
|
4117
4130
|
backgroundColor: c.primarySubtleHover,
|
|
4118
4131
|
},
|
|
4132
|
+
'&[data-submenu-open]': {
|
|
4133
|
+
backgroundColor: c.primarySubtle,
|
|
4134
|
+
color: c.primaryMain,
|
|
4135
|
+
borderLeftColor: c.primaryMain,
|
|
4136
|
+
},
|
|
4137
|
+
'&[data-submenu-open]:hover:not([data-disabled])': {
|
|
4138
|
+
backgroundColor: c.primarySubtleHover,
|
|
4139
|
+
},
|
|
4119
4140
|
'&[data-disabled]': {
|
|
4120
4141
|
cursor: 'not-allowed',
|
|
4121
4142
|
opacity: theme.opacity.high,
|
|
@@ -4173,7 +4194,7 @@ const MenuItem = ({ ref, id, label, icon, iconColor, role = 'menuitem', checked,
|
|
|
4173
4194
|
const isFocused = focused || focusedId === itemId;
|
|
4174
4195
|
const { liRef, mergedRef, submenuOpen, handleClick, scheduleOpen, scheduleClose, clearTimers, closeSubmenu, } = useMenuItem({ ref, role, hasSubmenu, disabled, onClick, closeOnClick, submenuTrigger });
|
|
4175
4196
|
const isHoverTrigger = submenuTrigger === 'hover';
|
|
4176
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("li", { ref: mergedRef, id: itemId, 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": isFocused || 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 && (jsxRuntime.jsxs("span", { className: MENU_ITEM_STYLES.indicator, "aria-hidden": true, children: [checked && role === 'menuitemcheckbox' && (jsxRuntime.jsx(Icon, { icon: CheckIcon, size: size === 'default' ? 'sm' : 'md', strokeColor: 'primaryMain' })), checked && role === 'menuitemradio' && (jsxRuntime.jsx("span", { className: MENU_ITEM_STYLES.radioDot }))] })), icon !== undefined && (jsxRuntime.jsx(Icon, { icon: icon, size: 'sm', strokeColor: iconColor ?? (isHighlighted ? 'primaryMain' : 'textSecondary') })), jsxRuntime.jsx(Text, { variant: 'span', fontSize: 'sm', className: MENU_ITEM_STYLES.label, children: label }), hasSubmenu && (jsxRuntime.jsx("span", { className: MENU_ITEM_STYLES.submenuChevron, "aria-hidden": true, children: jsxRuntime.jsx(Icon, { icon: ChevronRightIcon, size: 'sm', strokeColor: 'textTertiary' }) }))] }), hasSubmenu && (jsxRuntime.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 }))] }));
|
|
4197
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("li", { ref: mergedRef, id: itemId, 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": isFocused || undefined, "data-disabled": disabled || undefined, "data-submenu-open": hasSubmenu && submenuOpen || undefined, className: MENU_ITEM_STYLES.root({ size }), onClick: handleClick, onMouseEnter: hasSubmenu && !disabled && isHoverTrigger ? scheduleOpen : undefined, onMouseLeave: hasSubmenu && isHoverTrigger ? scheduleClose : undefined, ...rest, children: [isCheckable && (jsxRuntime.jsxs("span", { className: MENU_ITEM_STYLES.indicator, "aria-hidden": true, children: [checked && role === 'menuitemcheckbox' && (jsxRuntime.jsx(Icon, { icon: CheckIcon, size: size === 'default' ? 'sm' : 'md', strokeColor: 'primaryMain' })), checked && role === 'menuitemradio' && (jsxRuntime.jsx("span", { className: MENU_ITEM_STYLES.radioDot }))] })), icon !== undefined && (jsxRuntime.jsx(Icon, { icon: icon, size: 'sm', strokeColor: iconColor ?? (isHighlighted ? 'primaryMain' : 'textSecondary') })), jsxRuntime.jsx(Text, { variant: 'span', fontSize: 'sm', className: MENU_ITEM_STYLES.label, children: label }), hasSubmenu && (jsxRuntime.jsx("span", { className: MENU_ITEM_STYLES.submenuChevron, "aria-hidden": true, children: jsxRuntime.jsx(Icon, { icon: ChevronRightIcon, size: 'sm', strokeColor: 'textTertiary' }) }))] }), hasSubmenu && (jsxRuntime.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 }))] }));
|
|
4177
4198
|
};
|
|
4178
4199
|
MenuItem.displayName = 'MenuItem';
|
|
4179
4200
|
|