@aurora-ds/components 1.1.7 → 1.2.0
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 +59 -20
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +59 -20
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +241 -54
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -594,7 +594,7 @@ const ICON_SIZE$2 = {
|
|
|
594
594
|
* @example <Button label='Delete' variant='outlined' color='error' startIcon={IconRegistry.CloseIcon} />
|
|
595
595
|
* @example <Button label='Submitting…' color='success' isLoading width='100%' />
|
|
596
596
|
*/
|
|
597
|
-
const Button = ({ ref, variant = 'contained', color = 'primary', size = 'md', width, flexGrow, flexShrink, isLoading = false, startIcon: StartIcon, endIcon: EndIcon, label, className, type = 'button', disabled, style, ...rest }) => {
|
|
597
|
+
const Button = ({ ref, variant = 'contained', color = 'primary', size = 'md', width, flexGrow, flexShrink, isLoading = false, startIcon: StartIcon, endIcon: EndIcon, label, children, className, type = 'button', disabled, style, ...rest }) => {
|
|
598
598
|
const isDisabled = disabled || isLoading;
|
|
599
599
|
const iconSize = ICON_SIZE$2[size];
|
|
600
600
|
const rootClassName = theme.cx(BUTTON_STYLES.root({ variant, color, size }), className);
|
|
@@ -604,7 +604,7 @@ const Button = ({ ref, variant = 'contained', color = 'primary', size = 'md', wi
|
|
|
604
604
|
...(flexGrow !== undefined ? { flexGrow } : {}),
|
|
605
605
|
...(flexShrink !== undefined ? { flexShrink } : {}),
|
|
606
606
|
};
|
|
607
|
-
return (jsxRuntime.jsxs("button", { ref: ref, type: type, className: rootClassName, disabled: isDisabled, "aria-busy": isLoading || undefined, style: mergedStyle, ...rest, children: [isLoading && (jsxRuntime.jsx("span", { className: BUTTON_STYLES.spinnerWrap, children: jsxRuntime.jsx(Icon, { icon: SpinnerIcon, size: iconSize, className: BUTTON_STYLES.spinnerIcon }) })), jsxRuntime.jsxs("span", { className: theme.cx(BUTTON_STYLES.content, isLoading && BUTTON_STYLES.contentHidden), children: [StartIcon && (jsxRuntime.jsx(Icon, { icon: StartIcon, size: iconSize })), label && (jsxRuntime.jsx(Text, { variant: 'span', fontSize: LABEL_FONT_SIZE$1[size], fontWeight: 'medium', lineHeight: 'none', children: label })), EndIcon && (jsxRuntime.jsx(Icon, { icon: EndIcon, size: iconSize }))] })] }));
|
|
607
|
+
return (jsxRuntime.jsxs("button", { ref: ref, type: type, className: rootClassName, disabled: isDisabled, "aria-busy": isLoading || undefined, style: mergedStyle, ...rest, children: [isLoading && (jsxRuntime.jsx("span", { className: BUTTON_STYLES.spinnerWrap, children: jsxRuntime.jsx(Icon, { icon: SpinnerIcon, size: iconSize, className: BUTTON_STYLES.spinnerIcon }) })), jsxRuntime.jsxs("span", { className: theme.cx(BUTTON_STYLES.content, isLoading && BUTTON_STYLES.contentHidden), children: [StartIcon && (jsxRuntime.jsx(Icon, { icon: StartIcon, size: iconSize })), (label !== undefined || children !== undefined) && (jsxRuntime.jsx(Text, { variant: 'span', fontSize: LABEL_FONT_SIZE$1[size], fontWeight: 'medium', lineHeight: 'none', children: label ?? children })), EndIcon && (jsxRuntime.jsx(Icon, { icon: EndIcon, size: iconSize }))] })] }));
|
|
608
608
|
};
|
|
609
609
|
Button.displayName = 'Button';
|
|
610
610
|
|
|
@@ -703,12 +703,20 @@ const LINK_STYLES = theme.createStyles((theme) => ({
|
|
|
703
703
|
/**
|
|
704
704
|
* Theme-aware anchor element with optional icons and underline control.
|
|
705
705
|
*
|
|
706
|
+
* Supports SPA navigation (e.g. React Router) via `onClick` without `href`.
|
|
707
|
+
* In that case the component stays accessible: it gets `role="link"`,
|
|
708
|
+
* `tabIndex={0}` and keyboard Enter support automatically.
|
|
709
|
+
*
|
|
706
710
|
* @example <Link href='/about'>About</Link>
|
|
707
711
|
* @example <Link href='https://example.com' external>External site</Link>
|
|
708
712
|
* @example <Link href='/profile' underline='always' startIcon={UserIcon}>Profile</Link>
|
|
709
713
|
* @example <Link href='/terms' underline='none'>Terms</Link>
|
|
714
|
+
* @example <Link onClick={() => navigate('/about')}>About (SPA)</Link>
|
|
710
715
|
*/
|
|
711
|
-
const Link = ({ ref, underline = 'hover', external = false, disabled = false, startIcon: StartIcon, endIcon: EndIcon, children, className, onClick, onKeyDown, ...rest }) => {
|
|
716
|
+
const Link = ({ ref, underline = 'hover', external = false, disabled = false, startIcon: StartIcon, endIcon: EndIcon, children, className, href, onClick, onKeyDown, ...rest }) => {
|
|
717
|
+
// An <a> without href has no implicit ARIA role and is not focusable.
|
|
718
|
+
// When used for SPA navigation (onClick only), we restore both behaviours.
|
|
719
|
+
const hasHref = !!href;
|
|
712
720
|
const handleClick = (e) => {
|
|
713
721
|
if (disabled) {
|
|
714
722
|
e.preventDefault();
|
|
@@ -716,14 +724,22 @@ const Link = ({ ref, underline = 'hover', external = false, disabled = false, st
|
|
|
716
724
|
}
|
|
717
725
|
onClick?.(e);
|
|
718
726
|
};
|
|
719
|
-
// Prevents Enter navigation when disabled; satisfies jsx-a11y/click-events-have-key-events.
|
|
720
727
|
const handleKeyDown = (e) => {
|
|
721
728
|
if (disabled && e.key === 'Enter') {
|
|
722
729
|
e.preventDefault();
|
|
723
730
|
}
|
|
731
|
+
// Without href, the browser does not fire a click on Enter natively.
|
|
732
|
+
if (!hasHref && !disabled && e.key === 'Enter') {
|
|
733
|
+
e.currentTarget.click();
|
|
734
|
+
}
|
|
724
735
|
onKeyDown?.(e);
|
|
725
736
|
};
|
|
726
|
-
return (jsxRuntime.jsxs("a", { ref: ref, className: theme.cx(LINK_STYLES.root({ underline }), className), "aria-disabled": disabled || undefined,
|
|
737
|
+
return (jsxRuntime.jsxs("a", { ref: ref, href: href, className: theme.cx(LINK_STYLES.root({ underline }), className), "aria-disabled": disabled || undefined,
|
|
738
|
+
// Without href: must be explicitly put in the tab order.
|
|
739
|
+
// With href: the browser handles focusability natively (no tabIndex needed).
|
|
740
|
+
tabIndex: disabled ? -1 : (!hasHref ? 0 : undefined),
|
|
741
|
+
// Without href: <a> has no implicit ARIA role — add role="link" explicitly.
|
|
742
|
+
role: !hasHref ? 'link' : undefined, target: external ? '_blank' : undefined, rel: external ? 'noopener noreferrer' : undefined, onClick: handleClick, onKeyDown: handleKeyDown, ...rest, children: [StartIcon && (jsxRuntime.jsx("span", { className: LINK_STYLES.icon, "aria-hidden": true, children: jsxRuntime.jsx(StartIcon, { width: '1em', height: '1em' }) })), children, EndIcon && (jsxRuntime.jsx("span", { className: LINK_STYLES.icon, "aria-hidden": true, children: jsxRuntime.jsx(EndIcon, { width: '1em', height: '1em' }) }))] }));
|
|
727
743
|
};
|
|
728
744
|
Link.displayName = 'Link';
|
|
729
745
|
|
|
@@ -1779,7 +1795,7 @@ const useTextField = ({ id, ref, type, size, endAction, }) => {
|
|
|
1779
1795
|
*/
|
|
1780
1796
|
const TextField = ({ ref, label, helperText, size = 'md', status = 'default', startIcon: StartIcon, endAction, type, id, disabled, required, ...rest }) => {
|
|
1781
1797
|
const { fieldId, helperId, mergedRef, isPassword, showPassword, togglePassword, resolvedType, iconSize, iconButtonSize, hasEndSection, focusInput, } = useTextField({ id, ref, type, size, endAction });
|
|
1782
|
-
return (jsxRuntime.jsxs(Stack, { flexDirection: 'column', gap: 'xs', children: [label !== undefined && (jsxRuntime.jsxs(Text, { variant: 'label', fontSize: 'sm', fontWeight: 'medium', color: 'textSecondary', htmlFor: fieldId, children: [label, required && (jsxRuntime.jsx(Text, { variant: 'span', color: 'errorMain', "aria-hidden": true, children: ' *' }))] })), jsxRuntime.jsxs("div", { className: TEXTFIELD_WRAPPER_VARIANTS({ size, status }), "data-disabled": disabled || undefined, children: [StartIcon && (jsxRuntime.jsx("span", { className: TEXTFIELD_STYLES.startIconWrap, onClick: focusInput, children: jsxRuntime.jsx(Icon, { icon: StartIcon, size: iconSize, strokeColor: 'textSecondary' }) })), jsxRuntime.jsx("input", { ref: mergedRef, id: fieldId, type: resolvedType, disabled: disabled, required: required, "aria-required": required || undefined, "aria-invalid": status === 'error' || undefined, "aria-describedby": helperText !== undefined ? helperId : undefined, className: TEXTFIELD_STYLES.input, ...rest }), hasEndSection && (jsxRuntime.jsxs("span", { className: TEXTFIELD_STYLES.endActionWrap, children: [endAction, isPassword && (jsxRuntime.jsx(IconButton, { icon: showPassword ? EyeSlashIcon : EyeIcon, ariaLabel: showPassword ? 'Hide password' : 'Show password', variant: 'text', color: 'neutral', size: iconButtonSize, type: 'button',
|
|
1798
|
+
return (jsxRuntime.jsxs(Stack, { flexDirection: 'column', gap: 'xs', children: [label !== undefined && (jsxRuntime.jsxs(Text, { variant: 'label', fontSize: 'sm', fontWeight: 'medium', color: 'textSecondary', htmlFor: fieldId, children: [label, required && (jsxRuntime.jsx(Text, { variant: 'span', color: 'errorMain', "aria-hidden": true, children: ' *' }))] })), jsxRuntime.jsxs("div", { className: TEXTFIELD_WRAPPER_VARIANTS({ size, status }), "data-disabled": disabled || undefined, children: [StartIcon && (jsxRuntime.jsx("span", { className: TEXTFIELD_STYLES.startIconWrap, onClick: focusInput, "aria-hidden": true, children: jsxRuntime.jsx(Icon, { icon: StartIcon, size: iconSize, strokeColor: 'textSecondary' }) })), jsxRuntime.jsx("input", { ref: mergedRef, id: fieldId, type: resolvedType, disabled: disabled, required: required, "aria-required": required || undefined, "aria-invalid": status === 'error' || undefined, "aria-describedby": helperText !== undefined ? helperId : undefined, "aria-errormessage": status === 'error' && helperText !== undefined ? helperId : undefined, className: TEXTFIELD_STYLES.input, ...rest }), hasEndSection && (jsxRuntime.jsxs("span", { className: TEXTFIELD_STYLES.endActionWrap, children: [endAction, isPassword && (jsxRuntime.jsx(IconButton, { icon: showPassword ? EyeSlashIcon : EyeIcon, ariaLabel: showPassword ? 'Hide password' : 'Show password', variant: 'text', color: 'neutral', size: iconButtonSize, type: 'button', onClick: togglePassword }))] }))] }), helperText !== undefined && (jsxRuntime.jsx(FormHelperText, { id: helperId, status: status, children: helperText }))] }));
|
|
1783
1799
|
};
|
|
1784
1800
|
TextField.displayName = 'TextField';
|
|
1785
1801
|
|
|
@@ -1914,7 +1930,9 @@ const useMenuPosition = ({ anchorEl, open, menuRef, minWidth, gap = 4, }) => {
|
|
|
1914
1930
|
*/
|
|
1915
1931
|
const useMenu = ({ open, onClose, anchorEl, minWidth }) => {
|
|
1916
1932
|
const panelRef = React.useRef(null);
|
|
1933
|
+
const baseId = React.useId();
|
|
1917
1934
|
const [focusedIndex, setFocusedIndex] = React.useState(-1);
|
|
1935
|
+
const [activeDescendant, setActiveDescendant] = React.useState(undefined);
|
|
1918
1936
|
const { style } = useMenuPosition({ anchorEl, open, menuRef: panelRef, minWidth });
|
|
1919
1937
|
/** Returns all non-disabled option elements inside the panel. */
|
|
1920
1938
|
const getOptions = React.useCallback(() => {
|
|
@@ -1937,22 +1955,31 @@ const useMenu = ({ open, onClose, anchorEl, minWidth }) => {
|
|
|
1937
1955
|
});
|
|
1938
1956
|
return () => cancelAnimationFrame(raf);
|
|
1939
1957
|
}, [open, getOptions]);
|
|
1940
|
-
// Keep data-focused
|
|
1958
|
+
// Keep data-focused (visual highlight) and aria-activedescendant (screen reader
|
|
1959
|
+
// announcement) in sync with focusedIndex. Each option is assigned a stable id
|
|
1960
|
+
// so the listbox can reference the active one via aria-activedescendant.
|
|
1941
1961
|
React.useEffect(() => {
|
|
1942
1962
|
if (!open) {
|
|
1963
|
+
setActiveDescendant(undefined);
|
|
1943
1964
|
return;
|
|
1944
1965
|
}
|
|
1945
1966
|
const options = getOptions();
|
|
1967
|
+
let activeId;
|
|
1946
1968
|
options.forEach((el, idx) => {
|
|
1969
|
+
if (!el.id) {
|
|
1970
|
+
el.id = `${baseId}-option-${idx}`;
|
|
1971
|
+
}
|
|
1947
1972
|
if (idx === focusedIndex) {
|
|
1948
1973
|
el.setAttribute('data-focused', 'true');
|
|
1949
1974
|
el.scrollIntoView({ block: 'nearest' });
|
|
1975
|
+
activeId = el.id;
|
|
1950
1976
|
}
|
|
1951
1977
|
else {
|
|
1952
1978
|
el.removeAttribute('data-focused');
|
|
1953
1979
|
}
|
|
1954
1980
|
});
|
|
1955
|
-
|
|
1981
|
+
setActiveDescendant(activeId);
|
|
1982
|
+
}, [focusedIndex, open, getOptions, baseId]);
|
|
1956
1983
|
useKeyPress({
|
|
1957
1984
|
Escape: onClose,
|
|
1958
1985
|
ArrowDown: (e) => {
|
|
@@ -1990,7 +2017,7 @@ const useMenu = ({ open, onClose, anchorEl, minWidth }) => {
|
|
|
1990
2017
|
}
|
|
1991
2018
|
},
|
|
1992
2019
|
}, { enabled: open });
|
|
1993
|
-
return { panelRef, style };
|
|
2020
|
+
return { panelRef, style, activeDescendant };
|
|
1994
2021
|
};
|
|
1995
2022
|
|
|
1996
2023
|
const MENU_GROUP_STYLES = theme.createStyles((theme) => {
|
|
@@ -2031,7 +2058,8 @@ const MENU_GROUP_STYLES = theme.createStyles((theme) => {
|
|
|
2031
2058
|
}, { id: 'menu-group' });
|
|
2032
2059
|
|
|
2033
2060
|
const MenuGroup = ({ label, divider, children, }) => {
|
|
2034
|
-
|
|
2061
|
+
const labelId = React.useId();
|
|
2062
|
+
return (jsxRuntime.jsxs("div", { className: MENU_GROUP_STYLES.root, children: [divider && (jsxRuntime.jsx("div", { className: MENU_GROUP_STYLES.divider, role: 'separator', "aria-hidden": true })), label !== undefined && (jsxRuntime.jsx("span", { id: labelId, className: MENU_GROUP_STYLES.label, "aria-hidden": true, children: label })), jsxRuntime.jsx("ul", { className: MENU_GROUP_STYLES.list, role: 'group', "aria-labelledby": label !== undefined ? labelId : undefined, children: children })] }));
|
|
2035
2063
|
};
|
|
2036
2064
|
MenuGroup.displayName = 'MenuGroup';
|
|
2037
2065
|
|
|
@@ -2082,12 +2110,12 @@ const MenuItem = ({ ref, label, icon, selected, focused, disabled, onClick, ...r
|
|
|
2082
2110
|
};
|
|
2083
2111
|
MenuItem.displayName = 'MenuItem';
|
|
2084
2112
|
|
|
2085
|
-
const MenuBase = ({ open, onClose, anchorEl, minWidth, maxHeight = '20rem', id, children, }) => {
|
|
2086
|
-
const { panelRef, style } = useMenu({ open, onClose, anchorEl, minWidth });
|
|
2113
|
+
const MenuBase = ({ open, onClose, anchorEl, minWidth, maxHeight = '20rem', id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, children, }) => {
|
|
2114
|
+
const { panelRef, style, activeDescendant } = useMenu({ open, onClose, anchorEl, minWidth });
|
|
2087
2115
|
if (!open) {
|
|
2088
2116
|
return null;
|
|
2089
2117
|
}
|
|
2090
|
-
return reactDom.createPortal(jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: MENU_STYLES.backdrop, onClick: onClose, "aria-hidden": true }), jsxRuntime.jsx("div", { ref: panelRef, id: id, role: 'listbox', tabIndex: -1, className: MENU_STYLES.panel, style: { ...style, maxHeight, outline: 'none' }, children: children })] }), document.body);
|
|
2118
|
+
return reactDom.createPortal(jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: MENU_STYLES.backdrop, onClick: onClose, "aria-hidden": true }), jsxRuntime.jsx("div", { ref: panelRef, id: id, role: 'listbox', tabIndex: -1, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-activedescendant": activeDescendant, className: MENU_STYLES.panel, style: { ...style, maxHeight, outline: 'none' }, children: children })] }), document.body);
|
|
2091
2119
|
};
|
|
2092
2120
|
MenuBase.displayName = 'Menu';
|
|
2093
2121
|
const Menu = MenuBase;
|
|
@@ -2209,9 +2237,9 @@ const ICON_SIZE_MAP = {
|
|
|
2209
2237
|
md: 'md',
|
|
2210
2238
|
lg: 'lg',
|
|
2211
2239
|
};
|
|
2212
|
-
const SelectTrigger = ({ ref, size = 'md', status = 'default', open, hasValue, startIcon, disabled, children, ...rest }) => {
|
|
2240
|
+
const SelectTrigger = ({ ref, size = 'md', status = 'default', open, hasValue, startIcon, disabled, children, 'aria-expanded': ariaExpanded, 'aria-controls': ariaControls, ...rest }) => {
|
|
2213
2241
|
const iconSize = ICON_SIZE_MAP[size];
|
|
2214
|
-
return (jsxRuntime.jsxs("button", { type: 'button', ref: ref, className: SELECT_TRIGGER_VARIANTS({ size, status }), "data-open": open || undefined, "data-disabled": disabled || undefined, disabled: disabled, ...rest, children: [startIcon !== undefined && (jsxRuntime.jsx(Icon, { icon: startIcon, size: iconSize, strokeColor: 'textSecondary' })), jsxRuntime.jsx("span", { className: hasValue ? SELECT_TRIGGER_STYLES.value : SELECT_TRIGGER_STYLES.placeholder, children: children }), jsxRuntime.jsx(Icon, { icon: ChevronDownIcon, size: iconSize, className: theme.cx(SELECT_TRIGGER_STYLES.chevron, open ? SELECT_TRIGGER_STYLES.chevronOpen : undefined), strokeColor: 'textSecondary' })] }));
|
|
2242
|
+
return (jsxRuntime.jsxs("button", { type: 'button', role: 'combobox', ref: ref, className: SELECT_TRIGGER_VARIANTS({ size, status }), "data-open": open || undefined, "data-disabled": disabled || undefined, disabled: disabled, "aria-expanded": ariaExpanded, "aria-controls": ariaControls, ...rest, children: [startIcon !== undefined && (jsxRuntime.jsx(Icon, { icon: startIcon, size: iconSize, strokeColor: 'textSecondary' })), jsxRuntime.jsx("span", { className: hasValue ? SELECT_TRIGGER_STYLES.value : SELECT_TRIGGER_STYLES.placeholder, children: children }), jsxRuntime.jsx(Icon, { icon: ChevronDownIcon, size: iconSize, className: theme.cx(SELECT_TRIGGER_STYLES.chevron, open ? SELECT_TRIGGER_STYLES.chevronOpen : undefined), strokeColor: 'textSecondary' })] }));
|
|
2215
2243
|
};
|
|
2216
2244
|
SelectTrigger.displayName = 'SelectTrigger';
|
|
2217
2245
|
|
|
@@ -2225,6 +2253,7 @@ const useSelect = ({ id, ref, value, defaultValue, onChange, options, disabled,
|
|
|
2225
2253
|
const fieldId = id ?? generatedId;
|
|
2226
2254
|
const helperId = `${fieldId}-helper`;
|
|
2227
2255
|
const menuId = `${fieldId}-menu`;
|
|
2256
|
+
const labelId = `${fieldId}-label`;
|
|
2228
2257
|
const triggerRef = React.useRef(null);
|
|
2229
2258
|
const mergedRef = useMergedRefs(ref, triggerRef);
|
|
2230
2259
|
const [open, setOpen] = React.useState(false);
|
|
@@ -2268,6 +2297,7 @@ const useSelect = ({ id, ref, value, defaultValue, onChange, options, disabled,
|
|
|
2268
2297
|
const close = React.useCallback(() => setOpen(false), []);
|
|
2269
2298
|
return {
|
|
2270
2299
|
fieldId,
|
|
2300
|
+
labelId,
|
|
2271
2301
|
helperId,
|
|
2272
2302
|
menuId,
|
|
2273
2303
|
triggerRef,
|
|
@@ -2283,8 +2313,8 @@ const useSelect = ({ id, ref, value, defaultValue, onChange, options, disabled,
|
|
|
2283
2313
|
};
|
|
2284
2314
|
|
|
2285
2315
|
const Select = ({ ref, value, defaultValue, onChange, options, label, helperText, placeholder, size = 'md', status = 'default', disabled, required, width, id, }) => {
|
|
2286
|
-
const { fieldId, helperId, menuId, triggerRef, mergedRef, open, toggle, close, currentValue, selectedOption, groupedOptions, handleSelect, } = useSelect({ id, ref, value, defaultValue, onChange, options, disabled });
|
|
2287
|
-
return (jsxRuntime.jsxs(Stack, { flexDirection: 'column', gap: 'xs', style: { width: width ?? '100%' }, children: [label !== undefined && (jsxRuntime.jsxs(Text, { variant: 'label', fontSize: 'sm', fontWeight: 'medium', color: 'textSecondary', htmlFor: fieldId, children: [label, required && (jsxRuntime.jsx(Text, { variant: 'span', color: 'errorMain', "aria-hidden": true, children: ' *' }))] })), jsxRuntime.jsx(SelectTrigger, { ref: mergedRef, id: fieldId, size: size, status: status, open: open, hasValue: selectedOption !== undefined, disabled: disabled,
|
|
2316
|
+
const { fieldId, labelId, helperId, menuId, triggerRef, mergedRef, open, toggle, close, currentValue, selectedOption, groupedOptions, handleSelect, } = useSelect({ id, ref, value, defaultValue, onChange, options, disabled });
|
|
2317
|
+
return (jsxRuntime.jsxs(Stack, { flexDirection: 'column', gap: 'xs', style: { width: width ?? '100%' }, children: [label !== undefined && (jsxRuntime.jsxs(Text, { variant: 'label', fontSize: 'sm', fontWeight: 'medium', color: 'textSecondary', htmlFor: fieldId, id: labelId, children: [label, required && (jsxRuntime.jsx(Text, { variant: 'span', color: 'errorMain', "aria-hidden": true, children: ' *' }))] })), jsxRuntime.jsx(SelectTrigger, { ref: mergedRef, id: fieldId, size: size, status: status, open: open, hasValue: selectedOption !== undefined, disabled: disabled, "aria-haspopup": 'listbox', "aria-expanded": open, "aria-controls": menuId, "aria-labelledby": label !== undefined ? `${labelId} ${fieldId}` : undefined, "aria-required": required || undefined, "aria-invalid": status === 'error' || undefined, "aria-errormessage": status === 'error' && helperText !== undefined ? helperId : undefined, "aria-describedby": helperText !== undefined ? helperId : undefined, onClick: toggle, children: selectedOption !== undefined ? selectedOption.label : placeholder }), jsxRuntime.jsx(Menu, { open: open, onClose: close, anchorEl: triggerRef.current, id: menuId, "aria-labelledby": label !== undefined ? labelId : undefined, "aria-label": label === undefined ? placeholder : undefined, children: Array.from(groupedOptions.entries()).map(([groupKey, groupOpts], groupIndex) => {
|
|
2288
2318
|
const items = groupOpts.map((opt) => (jsxRuntime.jsx(Menu.Item, { label: opt.label, icon: opt.icon, selected: opt.value === currentValue, disabled: opt.disabled, onClick: () => handleSelect(opt.value) }, opt.value)));
|
|
2289
2319
|
return groupKey !== undefined ? (jsxRuntime.jsx(Menu.Group, { label: groupKey, divider: groupIndex > 0, children: items }, groupKey)) : (jsxRuntime.jsx(Menu.Group, { divider: groupIndex > 0, children: items }, '__ungrouped'));
|
|
2290
2320
|
}) }), helperText !== undefined && (jsxRuntime.jsx(FormHelperText, { id: helperId, status: status, children: helperText }))] }));
|
|
@@ -2452,7 +2482,7 @@ const useCheckbox = ({ id, ref, indeterminate = false }) => {
|
|
|
2452
2482
|
const Checkbox = ({ ref, label, helperText, size = 'md', status = 'default', indeterminate = false, error, id, disabled, required, ...rest }) => {
|
|
2453
2483
|
const resolvedStatus = error ? 'error' : status;
|
|
2454
2484
|
const { checkboxId, helperId, mergedRef } = useCheckbox({ id, ref, indeterminate });
|
|
2455
|
-
return (jsxRuntime.jsxs("div", { className: CHECKBOX_STYLES.wrapper, children: [jsxRuntime.jsxs("label", { htmlFor: checkboxId, className: CHECKBOX_ROOT_VARIANTS({ disabled: disabled ? 'true' : 'false' }), children: [jsxRuntime.jsx("input", { ref: mergedRef, id: checkboxId, type: 'checkbox', disabled: disabled, required: required, "aria-required": required || undefined, "aria-invalid": resolvedStatus === 'error' || undefined, "aria-describedby": helperText !== undefined ? helperId : undefined, className: CHECKBOX_INPUT_VARIANTS({
|
|
2485
|
+
return (jsxRuntime.jsxs("div", { className: CHECKBOX_STYLES.wrapper, children: [jsxRuntime.jsxs("label", { htmlFor: checkboxId, className: CHECKBOX_ROOT_VARIANTS({ disabled: disabled ? 'true' : 'false' }), children: [jsxRuntime.jsx("input", { ref: mergedRef, id: checkboxId, type: 'checkbox', disabled: disabled, required: required, "aria-required": required || undefined, "aria-invalid": resolvedStatus === 'error' || undefined, "aria-describedby": helperText !== undefined ? helperId : undefined, "aria-errormessage": resolvedStatus === 'error' && helperText !== undefined ? helperId : undefined, className: CHECKBOX_INPUT_VARIANTS({
|
|
2456
2486
|
size,
|
|
2457
2487
|
status: resolvedStatus,
|
|
2458
2488
|
disabled: disabled ? 'true' : 'false',
|
|
@@ -2950,6 +2980,15 @@ const VARIANT_ARIA_LIVE = {
|
|
|
2950
2980
|
warning: 'polite',
|
|
2951
2981
|
info: 'polite',
|
|
2952
2982
|
};
|
|
2983
|
+
/** role="alert" implies aria-live="assertive" — reserve it for errors.
|
|
2984
|
+
* Other variants use role="status" (implies aria-live="polite"). */
|
|
2985
|
+
const VARIANT_ROLE = {
|
|
2986
|
+
default: 'status',
|
|
2987
|
+
success: 'status',
|
|
2988
|
+
error: 'alert',
|
|
2989
|
+
warning: 'status',
|
|
2990
|
+
info: 'status',
|
|
2991
|
+
};
|
|
2953
2992
|
/**
|
|
2954
2993
|
* Inline alert banner with 5 visual variants: default, success, error, warning, info.
|
|
2955
2994
|
* Use `Alert.Title` (icon + heading) and `Alert.Body` (message) as children.
|
|
@@ -2962,7 +3001,7 @@ const VARIANT_ARIA_LIVE = {
|
|
|
2962
3001
|
*/
|
|
2963
3002
|
const AlertBase = ({ variant = 'default', children, width = '100%', outline = false, shadow = 'none', }) => {
|
|
2964
3003
|
const { backgroundColor, borderColor, accentColor } = VARIANT_TOKENS[variant];
|
|
2965
|
-
return (jsxRuntime.jsx(AlertContext.Provider, { value: { variant, accentColor }, children: jsxRuntime.jsx(Stack, { role:
|
|
3004
|
+
return (jsxRuntime.jsx(AlertContext.Provider, { value: { variant, accentColor }, children: jsxRuntime.jsx(Stack, { role: VARIANT_ROLE[variant], "aria-live": VARIANT_ARIA_LIVE[variant], flexDirection: 'column', gap: 'xs', padding: 'md', borderRadius: 'lg', backgroundColor: backgroundColor, borderColor: outline ? borderColor : undefined, borderWidth: outline ? '1px' : undefined, borderStyle: outline ? 'solid' : undefined, boxShadow: shadow, width: width, children: children }) }));
|
|
2966
3005
|
};
|
|
2967
3006
|
AlertBase.displayName = 'Alert';
|
|
2968
3007
|
const Alert = AlertBase;
|
|
@@ -3129,7 +3168,7 @@ const useDialog = ({ open, onClose, closeOnBackdropClick, maxWidth, maxHeight, m
|
|
|
3129
3168
|
*/
|
|
3130
3169
|
const DialogHeader = ({ title, onClose }) => {
|
|
3131
3170
|
const { titleId, CloseIconComponent } = React.useContext(DialogContext);
|
|
3132
|
-
return (jsxRuntime.jsxs(Stack, { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 'md', paddingTop: 'md', paddingBottom: 'md', paddingLeft: 'lg', paddingRight: 'md', flexShrink: 0, children: [jsxRuntime.jsx(Text, { id: titleId, variant: 'span', fontSize: 'md', fontWeight: 'semibold', color: 'textPrimary', children: title }), jsxRuntime.jsx(IconButton, { icon: CloseIconComponent, ariaLabel: 'Close dialog', variant: 'text', color: 'neutral', size: 'sm', type: 'button', onClick: onClose })] }));
|
|
3171
|
+
return (jsxRuntime.jsxs(Stack, { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 'md', paddingTop: 'md', paddingBottom: 'md', paddingLeft: 'lg', paddingRight: 'md', flexShrink: 0, children: [jsxRuntime.jsx(Text, { id: titleId, variant: 'span', as: 'h2', fontSize: 'md', fontWeight: 'semibold', color: 'textPrimary', children: title }), jsxRuntime.jsx(IconButton, { icon: CloseIconComponent, ariaLabel: 'Close dialog', variant: 'text', color: 'neutral', size: 'sm', type: 'button', onClick: onClose })] }));
|
|
3133
3172
|
};
|
|
3134
3173
|
DialogHeader.displayName = 'Dialog.Header';
|
|
3135
3174
|
|