@aurora-ds/components 1.1.7 → 1.4.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 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
 
@@ -661,36 +661,42 @@ const IconButton = ({ ref, icon: IconComponent, ariaLabel, variant = 'contained'
661
661
  IconButton.displayName = 'IconButton';
662
662
 
663
663
  const LINK_STYLES = theme.createStyles((theme) => ({
664
- root: ({ underline = 'hover' }) => ({
665
- display: 'inline-flex',
666
- alignItems: 'center',
667
- gap: '0.25em',
668
- color: theme.colors.linkMain,
669
- fontFamily: 'inherit',
670
- fontSize: 'inherit',
671
- lineHeight: 'inherit',
672
- fontWeight: 'inherit',
673
- textDecoration: underline === 'always' ? 'underline' : 'none',
674
- cursor: 'pointer',
675
- borderRadius: theme.radius.xs,
676
- transition: `color ${theme.transition.fast}`,
677
- ':hover:not([aria-disabled="true"])': {
678
- color: theme.colors.linkHover,
679
- textDecoration: underline !== 'none' ? 'underline' : 'none',
680
- },
681
- ':active:not([aria-disabled="true"])': {
682
- color: theme.colors.linkActive,
683
- },
684
- ':focus-visible': {
685
- outline: `2px solid ${theme.colors.linkMain}`,
686
- outlineOffset: '2px',
687
- },
688
- '&[aria-disabled="true"]': {
689
- color: theme.colors.linkDisabled,
690
- cursor: 'not-allowed',
691
- textDecoration: 'none',
692
- },
693
- }),
664
+ root: ({ underline = 'hover', color = 'default' }) => {
665
+ const mainColor = color === 'secondary' ? theme.colors.textSecondary : theme.colors.linkMain;
666
+ const hoverColor = color === 'secondary' ? theme.colors.textTertiary : theme.colors.linkHover;
667
+ const activeColor = color === 'secondary' ? theme.colors.textPrimary : theme.colors.linkActive;
668
+ const disabledColor = color === 'secondary' ? theme.colors.textDisabled : theme.colors.linkDisabled;
669
+ return {
670
+ display: 'inline-flex',
671
+ alignItems: 'center',
672
+ gap: '0.25em',
673
+ color: mainColor,
674
+ fontFamily: 'inherit',
675
+ fontSize: 'inherit',
676
+ lineHeight: 'inherit',
677
+ fontWeight: 'inherit',
678
+ textDecoration: underline === 'always' ? 'underline' : 'none',
679
+ cursor: 'pointer',
680
+ borderRadius: theme.radius.xs,
681
+ transition: `color ${theme.transition.fast}`,
682
+ ':hover:not([aria-disabled="true"])': {
683
+ color: hoverColor,
684
+ textDecoration: underline !== 'none' ? 'underline' : 'none',
685
+ },
686
+ ':active:not([aria-disabled="true"])': {
687
+ color: activeColor,
688
+ },
689
+ ':focus-visible': {
690
+ outline: `2px solid ${mainColor}`,
691
+ outlineOffset: '2px',
692
+ },
693
+ '&[aria-disabled="true"]': {
694
+ color: disabledColor,
695
+ cursor: 'not-allowed',
696
+ textDecoration: 'none',
697
+ },
698
+ };
699
+ },
694
700
  icon: {
695
701
  display: 'inline-flex',
696
702
  alignItems: 'center',
@@ -703,12 +709,20 @@ const LINK_STYLES = theme.createStyles((theme) => ({
703
709
  /**
704
710
  * Theme-aware anchor element with optional icons and underline control.
705
711
  *
712
+ * Supports SPA navigation (e.g. React Router) via `onClick` without `href`.
713
+ * In that case the component stays accessible: it gets `role="link"`,
714
+ * `tabIndex={0}` and keyboard Enter support automatically.
715
+ *
706
716
  * @example <Link href='/about'>About</Link>
707
717
  * @example <Link href='https://example.com' external>External site</Link>
708
718
  * @example <Link href='/profile' underline='always' startIcon={UserIcon}>Profile</Link>
709
719
  * @example <Link href='/terms' underline='none'>Terms</Link>
720
+ * @example <Link onClick={() => navigate('/about')}>About (SPA)</Link>
710
721
  */
711
- const Link = ({ ref, underline = 'hover', external = false, disabled = false, startIcon: StartIcon, endIcon: EndIcon, children, className, onClick, onKeyDown, ...rest }) => {
722
+ const Link = ({ ref, underline = 'hover', color = 'default', external = false, disabled = false, startIcon: StartIcon, endIcon: EndIcon, children, className, href, onClick, onKeyDown, ...rest }) => {
723
+ // An <a> without href has no implicit ARIA role and is not focusable.
724
+ // When used for SPA navigation (onClick only), we restore both behaviours.
725
+ const hasHref = !!href;
712
726
  const handleClick = (e) => {
713
727
  if (disabled) {
714
728
  e.preventDefault();
@@ -716,14 +730,22 @@ const Link = ({ ref, underline = 'hover', external = false, disabled = false, st
716
730
  }
717
731
  onClick?.(e);
718
732
  };
719
- // Prevents Enter navigation when disabled; satisfies jsx-a11y/click-events-have-key-events.
720
733
  const handleKeyDown = (e) => {
721
734
  if (disabled && e.key === 'Enter') {
722
735
  e.preventDefault();
723
736
  }
737
+ // Without href, the browser does not fire a click on Enter natively.
738
+ if (!hasHref && !disabled && e.key === 'Enter') {
739
+ e.currentTarget.click();
740
+ }
724
741
  onKeyDown?.(e);
725
742
  };
726
- return (jsxRuntime.jsxs("a", { ref: ref, className: theme.cx(LINK_STYLES.root({ underline }), className), "aria-disabled": disabled || undefined, tabIndex: disabled ? -1 : 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' }) }))] }));
743
+ return (jsxRuntime.jsxs("a", { ref: ref, href: href, className: theme.cx(LINK_STYLES.root({ underline, color }), className), "aria-disabled": disabled || undefined,
744
+ // Without href: must be explicitly put in the tab order.
745
+ // With href: the browser handles focusability natively (no tabIndex needed).
746
+ tabIndex: disabled ? -1 : (!hasHref ? 0 : undefined),
747
+ // Without href: <a> has no implicit ARIA role — add role="link" explicitly.
748
+ 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
749
  };
728
750
  Link.displayName = 'Link';
729
751
 
@@ -1779,7 +1801,7 @@ const useTextField = ({ id, ref, type, size, endAction, }) => {
1779
1801
  */
1780
1802
  const TextField = ({ ref, label, helperText, size = 'md', status = 'default', startIcon: StartIcon, endAction, type, id, disabled, required, ...rest }) => {
1781
1803
  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', tabIndex: -1, onClick: togglePassword }))] }))] }), helperText !== undefined && (jsxRuntime.jsx(FormHelperText, { id: helperId, status: status, children: helperText }))] }));
1804
+ 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
1805
  };
1784
1806
  TextField.displayName = 'TextField';
1785
1807
 
@@ -1914,7 +1936,9 @@ const useMenuPosition = ({ anchorEl, open, menuRef, minWidth, gap = 4, }) => {
1914
1936
  */
1915
1937
  const useMenu = ({ open, onClose, anchorEl, minWidth }) => {
1916
1938
  const panelRef = React.useRef(null);
1939
+ const baseId = React.useId();
1917
1940
  const [focusedIndex, setFocusedIndex] = React.useState(-1);
1941
+ const [activeDescendant, setActiveDescendant] = React.useState(undefined);
1918
1942
  const { style } = useMenuPosition({ anchorEl, open, menuRef: panelRef, minWidth });
1919
1943
  /** Returns all non-disabled option elements inside the panel. */
1920
1944
  const getOptions = React.useCallback(() => {
@@ -1937,22 +1961,31 @@ const useMenu = ({ open, onClose, anchorEl, minWidth }) => {
1937
1961
  });
1938
1962
  return () => cancelAnimationFrame(raf);
1939
1963
  }, [open, getOptions]);
1940
- // Keep data-focused in sync with focusedIndex
1964
+ // Keep data-focused (visual highlight) and aria-activedescendant (screen reader
1965
+ // announcement) in sync with focusedIndex. Each option is assigned a stable id
1966
+ // so the listbox can reference the active one via aria-activedescendant.
1941
1967
  React.useEffect(() => {
1942
1968
  if (!open) {
1969
+ setActiveDescendant(undefined);
1943
1970
  return;
1944
1971
  }
1945
1972
  const options = getOptions();
1973
+ let activeId;
1946
1974
  options.forEach((el, idx) => {
1975
+ if (!el.id) {
1976
+ el.id = `${baseId}-option-${idx}`;
1977
+ }
1947
1978
  if (idx === focusedIndex) {
1948
1979
  el.setAttribute('data-focused', 'true');
1949
1980
  el.scrollIntoView({ block: 'nearest' });
1981
+ activeId = el.id;
1950
1982
  }
1951
1983
  else {
1952
1984
  el.removeAttribute('data-focused');
1953
1985
  }
1954
1986
  });
1955
- }, [focusedIndex, open, getOptions]);
1987
+ setActiveDescendant(activeId);
1988
+ }, [focusedIndex, open, getOptions, baseId]);
1956
1989
  useKeyPress({
1957
1990
  Escape: onClose,
1958
1991
  ArrowDown: (e) => {
@@ -1990,7 +2023,7 @@ const useMenu = ({ open, onClose, anchorEl, minWidth }) => {
1990
2023
  }
1991
2024
  },
1992
2025
  }, { enabled: open });
1993
- return { panelRef, style };
2026
+ return { panelRef, style, activeDescendant };
1994
2027
  };
1995
2028
 
1996
2029
  const MENU_GROUP_STYLES = theme.createStyles((theme) => {
@@ -2031,7 +2064,8 @@ const MENU_GROUP_STYLES = theme.createStyles((theme) => {
2031
2064
  }, { id: 'menu-group' });
2032
2065
 
2033
2066
  const MenuGroup = ({ label, divider, children, }) => {
2034
- 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", { className: MENU_GROUP_STYLES.label, children: label })), jsxRuntime.jsx("ul", { className: MENU_GROUP_STYLES.list, role: 'group', "aria-label": label, children: children })] }));
2067
+ const labelId = React.useId();
2068
+ 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
2069
  };
2036
2070
  MenuGroup.displayName = 'MenuGroup';
2037
2071
 
@@ -2082,12 +2116,12 @@ const MenuItem = ({ ref, label, icon, selected, focused, disabled, onClick, ...r
2082
2116
  };
2083
2117
  MenuItem.displayName = 'MenuItem';
2084
2118
 
2085
- const MenuBase = ({ open, onClose, anchorEl, minWidth, maxHeight = '20rem', id, children, }) => {
2086
- const { panelRef, style } = useMenu({ open, onClose, anchorEl, minWidth });
2119
+ const MenuBase = ({ open, onClose, anchorEl, minWidth, maxHeight = '20rem', id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, children, }) => {
2120
+ const { panelRef, style, activeDescendant } = useMenu({ open, onClose, anchorEl, minWidth });
2087
2121
  if (!open) {
2088
2122
  return null;
2089
2123
  }
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);
2124
+ 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
2125
  };
2092
2126
  MenuBase.displayName = 'Menu';
2093
2127
  const Menu = MenuBase;
@@ -2209,9 +2243,9 @@ const ICON_SIZE_MAP = {
2209
2243
  md: 'md',
2210
2244
  lg: 'lg',
2211
2245
  };
2212
- const SelectTrigger = ({ ref, size = 'md', status = 'default', open, hasValue, startIcon, disabled, children, ...rest }) => {
2246
+ const SelectTrigger = ({ ref, size = 'md', status = 'default', open, hasValue, startIcon, disabled, children, 'aria-expanded': ariaExpanded, 'aria-controls': ariaControls, ...rest }) => {
2213
2247
  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' })] }));
2248
+ 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
2249
  };
2216
2250
  SelectTrigger.displayName = 'SelectTrigger';
2217
2251
 
@@ -2225,6 +2259,7 @@ const useSelect = ({ id, ref, value, defaultValue, onChange, options, disabled,
2225
2259
  const fieldId = id ?? generatedId;
2226
2260
  const helperId = `${fieldId}-helper`;
2227
2261
  const menuId = `${fieldId}-menu`;
2262
+ const labelId = `${fieldId}-label`;
2228
2263
  const triggerRef = React.useRef(null);
2229
2264
  const mergedRef = useMergedRefs(ref, triggerRef);
2230
2265
  const [open, setOpen] = React.useState(false);
@@ -2268,6 +2303,7 @@ const useSelect = ({ id, ref, value, defaultValue, onChange, options, disabled,
2268
2303
  const close = React.useCallback(() => setOpen(false), []);
2269
2304
  return {
2270
2305
  fieldId,
2306
+ labelId,
2271
2307
  helperId,
2272
2308
  menuId,
2273
2309
  triggerRef,
@@ -2283,8 +2319,8 @@ const useSelect = ({ id, ref, value, defaultValue, onChange, options, disabled,
2283
2319
  };
2284
2320
 
2285
2321
  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, required: required, "aria-haspopup": 'listbox', "aria-expanded": open, "aria-controls": menuId, "aria-invalid": status === 'error' || 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, children: Array.from(groupedOptions.entries()).map(([groupKey, groupOpts], groupIndex) => {
2322
+ const { fieldId, labelId, helperId, menuId, triggerRef, mergedRef, open, toggle, close, currentValue, selectedOption, groupedOptions, handleSelect, } = useSelect({ id, ref, value, defaultValue, onChange, options, disabled });
2323
+ 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
2324
  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
2325
  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
2326
  }) }), helperText !== undefined && (jsxRuntime.jsx(FormHelperText, { id: helperId, status: status, children: helperText }))] }));
@@ -2452,7 +2488,7 @@ const useCheckbox = ({ id, ref, indeterminate = false }) => {
2452
2488
  const Checkbox = ({ ref, label, helperText, size = 'md', status = 'default', indeterminate = false, error, id, disabled, required, ...rest }) => {
2453
2489
  const resolvedStatus = error ? 'error' : status;
2454
2490
  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({
2491
+ 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
2492
  size,
2457
2493
  status: resolvedStatus,
2458
2494
  disabled: disabled ? 'true' : 'false',
@@ -2543,6 +2579,78 @@ const Grid = ({ display = 'grid', columns, rows, autoFlow, autoColumns, autoRows
2543
2579
  };
2544
2580
  Grid.displayName = 'Grid';
2545
2581
 
2582
+ const BREADCRUMB_STYLES = theme.createStyles((_theme) => ({
2583
+ nav: {
2584
+ display: 'block',
2585
+ },
2586
+ list: {
2587
+ display: 'flex',
2588
+ flexWrap: 'wrap',
2589
+ alignItems: 'center',
2590
+ listStyle: 'none',
2591
+ margin: 0,
2592
+ padding: 0,
2593
+ },
2594
+ }), { id: 'breadcrumb' });
2595
+
2596
+ const BreadcrumbContext = React.createContext({
2597
+ separator: '/',
2598
+ });
2599
+ const useBreadcrumbContext = () => React.useContext(BreadcrumbContext);
2600
+
2601
+ const BREADCRUMB_ITEM_STYLES = theme.createStyles((theme) => ({
2602
+ item: {
2603
+ display: 'inline-flex',
2604
+ alignItems: 'center',
2605
+ fontSize: theme.fontSize.sm,
2606
+ lineHeight: theme.lineHeight.normal,
2607
+ },
2608
+ separator: {
2609
+ display: 'inline-flex',
2610
+ alignItems: 'center',
2611
+ marginLeft: theme.spacing.sm,
2612
+ marginRight: theme.spacing.sm,
2613
+ color: theme.colors.textTertiary,
2614
+ userSelect: 'none',
2615
+ },
2616
+ }), { id: 'breadcrumb-item' });
2617
+
2618
+ const BreadcrumbItem = ({ label, href, onClick, current = false, isFirst = false, }) => {
2619
+ const { separator } = useBreadcrumbContext();
2620
+ return (jsxRuntime.jsxs("li", { className: BREADCRUMB_ITEM_STYLES.item, "aria-current": current ? 'page' : undefined, children: [!isFirst && (jsxRuntime.jsx("span", { "aria-hidden": true, className: BREADCRUMB_ITEM_STYLES.separator, children: separator })), current ? (jsxRuntime.jsx(Text, { variant: 'span', fontWeight: 'semibold', color: 'textPrimary', children: label })) : (jsxRuntime.jsx(Link, { href: href, onClick: onClick, color: 'secondary', underline: 'hover', children: label }))] }));
2621
+ };
2622
+ BreadcrumbItem.displayName = 'Breadcrumb.Item';
2623
+
2624
+ /**
2625
+ * WAI-ARIA compliant breadcrumb navigation using a compound component API.
2626
+ *
2627
+ * ```tsx
2628
+ * <Breadcrumb separator='/'>
2629
+ * <Breadcrumb.Item label='Home' href='/' />
2630
+ * <Breadcrumb.Item label='Products' href='/products' />
2631
+ * <Breadcrumb.Item label='Laptop Pro X' current />
2632
+ * </Breadcrumb>
2633
+ * ```
2634
+ *
2635
+ * The last item is typically marked as `current` — it renders as bold text (non-clickable)
2636
+ * and exposes `aria-current="page"` for assistive technologies.
2637
+ */
2638
+ const BreadcrumbBase = ({ children, separator = '/', ariaLabel = 'Breadcrumb', }) => {
2639
+ const contextValue = React.useMemo(() => ({ separator }), [separator]);
2640
+ const items = React.Children.map(children, (child, index) => {
2641
+ if (!React.isValidElement(child)) {
2642
+ return child;
2643
+ }
2644
+ return React.cloneElement(child, {
2645
+ isFirst: index === 0,
2646
+ });
2647
+ });
2648
+ return (jsxRuntime.jsx(BreadcrumbContext.Provider, { value: contextValue, children: jsxRuntime.jsx("nav", { "aria-label": ariaLabel, className: BREADCRUMB_STYLES.nav, children: jsxRuntime.jsx("ol", { className: BREADCRUMB_STYLES.list, children: items }) }) }));
2649
+ };
2650
+ BreadcrumbBase.displayName = 'Breadcrumb';
2651
+ const Breadcrumb = BreadcrumbBase;
2652
+ Breadcrumb.Item = BreadcrumbItem;
2653
+
2546
2654
  const DrawerContext = React.createContext({
2547
2655
  isExpanded: true,
2548
2656
  });
@@ -2898,6 +3006,234 @@ Drawer.Body = DrawerBody;
2898
3006
  Drawer.Footer = DrawerFooter;
2899
3007
  Drawer.Item = DrawerItem;
2900
3008
 
3009
+ const TABS_LIST_STYLES = theme.createStyles(() => ({
3010
+ root: {
3011
+ display: 'flex',
3012
+ flexDirection: 'row',
3013
+ alignItems: 'center',
3014
+ overflowX: 'auto',
3015
+ scrollbarWidth: 'none',
3016
+ width: 'fit-content',
3017
+ '::-webkit-scrollbar': { display: 'none' },
3018
+ },
3019
+ }));
3020
+
3021
+ const TabsContext = React.createContext(null);
3022
+
3023
+ /**
3024
+ * Internal hook — consumes the Tabs context and throws if used outside a `<Tabs>` provider.
3025
+ * @internal
3026
+ */
3027
+ const useTabsContext = () => {
3028
+ const ctx = React.useContext(TabsContext);
3029
+ if (!ctx) {
3030
+ throw new Error('This component must be used inside a <Tabs> provider.');
3031
+ }
3032
+ return ctx;
3033
+ };
3034
+
3035
+ const TabsList = ({ children, ariaLabel, ariaLabelledBy }) => {
3036
+ const { value, setValue, baseId, getTabValues } = useTabsContext();
3037
+ /**
3038
+ * Returns true if the tab at the given index is aria-disabled.
3039
+ * We check the DOM attribute because disabled state lives in TabItem,
3040
+ * not in the shared context, and we want to avoid adding it to the context.
3041
+ */
3042
+ const isTabDisabled = (tabValue) => {
3043
+ const el = document.getElementById(`${baseId}-tab-${tabValue}`);
3044
+ return el?.getAttribute('aria-disabled') === 'true';
3045
+ };
3046
+ /**
3047
+ * Moves focus to the nearest non-disabled tab starting from `startIndex`
3048
+ * and stepping in the given `direction` (+1 = right, -1 = left).
3049
+ * Stops after a full loop if all tabs are disabled.
3050
+ */
3051
+ const focusNextEnabled = (startIndex, direction) => {
3052
+ const values = getTabValues();
3053
+ for (let i = 1; i <= values.length; i++) {
3054
+ const index = (startIndex + direction * i + values.length) % values.length;
3055
+ const nextValue = values[index];
3056
+ if (!isTabDisabled(nextValue)) {
3057
+ document.getElementById(`${baseId}-tab-${nextValue}`)?.focus();
3058
+ setValue(nextValue);
3059
+ return;
3060
+ }
3061
+ }
3062
+ };
3063
+ const handleKeyDown = (event) => {
3064
+ const values = getTabValues();
3065
+ const currentIndex = values.indexOf(value);
3066
+ if (currentIndex === -1) {
3067
+ return;
3068
+ }
3069
+ switch (event.key) {
3070
+ case 'ArrowRight':
3071
+ event.preventDefault();
3072
+ focusNextEnabled(currentIndex, 1);
3073
+ break;
3074
+ case 'ArrowLeft':
3075
+ event.preventDefault();
3076
+ focusNextEnabled(currentIndex, -1);
3077
+ break;
3078
+ case 'Home': {
3079
+ event.preventDefault();
3080
+ // Focus the first non-disabled tab
3081
+ const firstIdx = values.findIndex((v) => !isTabDisabled(v));
3082
+ if (firstIdx !== -1) {
3083
+ document.getElementById(`${baseId}-tab-${values[firstIdx]}`)?.focus();
3084
+ setValue(values[firstIdx]);
3085
+ }
3086
+ break;
3087
+ }
3088
+ case 'End': {
3089
+ event.preventDefault();
3090
+ // Focus the last non-disabled tab
3091
+ const lastIdx = [...values].reverse().findIndex((v) => !isTabDisabled(v));
3092
+ if (lastIdx !== -1) {
3093
+ const realIndex = values.length - 1 - lastIdx;
3094
+ document.getElementById(`${baseId}-tab-${values[realIndex]}`)?.focus();
3095
+ setValue(values[realIndex]);
3096
+ }
3097
+ break;
3098
+ }
3099
+ }
3100
+ };
3101
+ return (jsxRuntime.jsx("div", { role: 'tablist', "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className: TABS_LIST_STYLES.root, onKeyDown: handleKeyDown, children: children }));
3102
+ };
3103
+ TabsList.displayName = 'Tabs.List';
3104
+
3105
+ const TABS_PANEL_STYLES = theme.createStyles(() => ({
3106
+ root: {
3107
+ width: '100%',
3108
+ },
3109
+ }));
3110
+
3111
+ const TabsPanel = ({ value, children, keepMounted = false }) => {
3112
+ const { value: activeValue, baseId } = useTabsContext();
3113
+ const isActive = activeValue === value;
3114
+ if (!isActive && !keepMounted) {
3115
+ return null;
3116
+ }
3117
+ return (jsxRuntime.jsx("div", { role: 'tabpanel', id: `${baseId}-panel-${value}`, "aria-labelledby": `${baseId}-tab-${value}`,
3118
+ // WAI-ARIA APG: tabIndex={0} allows keyboard scrolling when the panel has no focusable child.
3119
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
3120
+ tabIndex: 0, hidden: !isActive, className: TABS_PANEL_STYLES.root, children: children }));
3121
+ };
3122
+ TabsPanel.displayName = 'Tabs.Panel';
3123
+
3124
+ const TAB_ITEM_STYLES = theme.createStyles((theme) => ({
3125
+ root: ({ isActive, disabled }) => ({
3126
+ display: 'inline-flex',
3127
+ alignItems: 'center',
3128
+ justifyContent: 'center',
3129
+ boxSizing: 'border-box',
3130
+ paddingTop: theme.spacing.xs,
3131
+ paddingBottom: theme.spacing.xs,
3132
+ paddingLeft: theme.spacing.md,
3133
+ paddingRight: theme.spacing.md,
3134
+ border: 'none',
3135
+ borderBottom: `2px solid ${isActive ? theme.colors.primaryMain : 'transparent'}`,
3136
+ background: 'transparent',
3137
+ cursor: disabled ? 'not-allowed' : 'pointer',
3138
+ fontFamily: 'inherit',
3139
+ whiteSpace: 'nowrap',
3140
+ transition: `border-color ${theme.transition.fast}`,
3141
+ ...(disabled ? {} : {
3142
+ ':hover': {
3143
+ borderBottomColor: isActive ? theme.colors.primaryMain : theme.colors.textTertiary,
3144
+ },
3145
+ }),
3146
+ ':focus-visible': {
3147
+ outline: `2px solid ${theme.colors.primaryMain}`,
3148
+ outlineOffset: '2px',
3149
+ },
3150
+ }),
3151
+ }));
3152
+
3153
+ /**
3154
+ * Handles tab registration, active state derivation and interaction props
3155
+ * for a single `Tabs.Tab` (TabItem) button.
3156
+ *
3157
+ * Registers the tab value into the shared ordered registry on mount so that
3158
+ * `TabsList` can perform correct ArrowLeft/Right/Home/End keyboard navigation.
3159
+ */
3160
+ const useTabItem = ({ value, disabled }) => {
3161
+ const { value: activeValue, setValue, baseId, registerTab } = useTabsContext();
3162
+ // Maintains the ordered tab registry used by TabsList for keyboard navigation.
3163
+ React.useEffect(() => registerTab(value), [registerTab, value]);
3164
+ const isActive = activeValue === value;
3165
+ return {
3166
+ isActive,
3167
+ buttonProps: {
3168
+ id: `${baseId}-tab-${value}`,
3169
+ 'aria-selected': isActive,
3170
+ 'aria-controls': `${baseId}-panel-${value}`,
3171
+ 'aria-disabled': disabled || undefined,
3172
+ tabIndex: isActive ? 0 : -1,
3173
+ onClick: () => {
3174
+ if (!disabled) {
3175
+ setValue(value);
3176
+ }
3177
+ },
3178
+ },
3179
+ };
3180
+ };
3181
+
3182
+ const TabItem = ({ value, label, disabled = false }) => {
3183
+ const { isActive, buttonProps } = useTabItem({ value, disabled });
3184
+ return (jsxRuntime.jsx("button", { type: 'button', role: 'tab', className: TAB_ITEM_STYLES.root({ isActive, disabled }), ...buttonProps, children: jsxRuntime.jsx(Text, { variant: 'span', fontSize: 'sm', fontWeight: isActive ? 'semibold' : 'medium', color: disabled ? 'textDisabled' : isActive ? 'textPrimary' : 'textSecondary', children: label }) }));
3185
+ };
3186
+ TabItem.displayName = 'Tabs.Tab';
3187
+
3188
+ /**
3189
+ * WAI-ARIA compliant tab interface using a compound component API.
3190
+ *
3191
+ * ```tsx
3192
+ * <Tabs defaultValue={'active'} onChange={handleChange}>
3193
+ * <Tabs.List ariaLabel={'Subscriptions'}>
3194
+ * <Tabs.Tab value={'active'} label={'Active'} />
3195
+ * <Tabs.Tab value={'cancelled'} label={'Cancelled'} />
3196
+ * </Tabs.List>
3197
+ * <Tabs.Panel value={'active'}>...</Tabs.Panel>
3198
+ * <Tabs.Panel value={'cancelled'}>...</Tabs.Panel>
3199
+ * </Tabs>
3200
+ * ```
3201
+ *
3202
+ * Supports controlled (`value` + `onChange`) and uncontrolled (`defaultValue`) modes.
3203
+ * Keyboard navigation: `ArrowLeft/Right`, `Home`, `End`.
3204
+ * Only the active panel is mounted — use `keepMounted` on `Tabs.Panel` to preserve state across switches.
3205
+ */
3206
+ const TabsBase = ({ children, value: controlledValue, defaultValue, onChange, id, }) => {
3207
+ const reactId = React.useId();
3208
+ const baseId = id ?? `tabs-${reactId}`;
3209
+ const [internalValue, setInternalValue] = React.useState(defaultValue ?? '');
3210
+ const isControlled = controlledValue !== undefined;
3211
+ const value = isControlled ? controlledValue : internalValue;
3212
+ const tabsRef = React.useRef([]);
3213
+ const registerTab = React.useCallback((tabValue) => {
3214
+ if (!tabsRef.current.includes(tabValue)) {
3215
+ tabsRef.current.push(tabValue);
3216
+ }
3217
+ return () => {
3218
+ tabsRef.current = tabsRef.current.filter((v) => v !== tabValue);
3219
+ };
3220
+ }, []);
3221
+ const getTabValues = React.useCallback(() => tabsRef.current, []);
3222
+ const setValue = React.useCallback((next) => {
3223
+ if (!isControlled) {
3224
+ setInternalValue(next);
3225
+ }
3226
+ onChange?.(next);
3227
+ }, [isControlled, onChange]);
3228
+ const contextValue = React.useMemo(() => ({ value, setValue, baseId, registerTab, getTabValues }), [value, setValue, baseId, registerTab, getTabValues]);
3229
+ return (jsxRuntime.jsx(TabsContext.Provider, { value: contextValue, children: children }));
3230
+ };
3231
+ TabsBase.displayName = 'Tabs';
3232
+ const Tabs = TabsBase;
3233
+ Tabs.List = TabsList;
3234
+ Tabs.Tab = TabItem;
3235
+ Tabs.Panel = TabsPanel;
3236
+
2901
3237
  const AlertContext = React.createContext({
2902
3238
  variant: 'default',
2903
3239
  accentColor: 'defaultActive',
@@ -2950,6 +3286,15 @@ const VARIANT_ARIA_LIVE = {
2950
3286
  warning: 'polite',
2951
3287
  info: 'polite',
2952
3288
  };
3289
+ /** role="alert" implies aria-live="assertive" — reserve it for errors.
3290
+ * Other variants use role="status" (implies aria-live="polite"). */
3291
+ const VARIANT_ROLE = {
3292
+ default: 'status',
3293
+ success: 'status',
3294
+ error: 'alert',
3295
+ warning: 'status',
3296
+ info: 'status',
3297
+ };
2953
3298
  /**
2954
3299
  * Inline alert banner with 5 visual variants: default, success, error, warning, info.
2955
3300
  * Use `Alert.Title` (icon + heading) and `Alert.Body` (message) as children.
@@ -2962,7 +3307,7 @@ const VARIANT_ARIA_LIVE = {
2962
3307
  */
2963
3308
  const AlertBase = ({ variant = 'default', children, width = '100%', outline = false, shadow = 'none', }) => {
2964
3309
  const { backgroundColor, borderColor, accentColor } = VARIANT_TOKENS[variant];
2965
- return (jsxRuntime.jsx(AlertContext.Provider, { value: { variant, accentColor }, children: jsxRuntime.jsx(Stack, { role: 'alert', "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 }) }));
3310
+ 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
3311
  };
2967
3312
  AlertBase.displayName = 'Alert';
2968
3313
  const Alert = AlertBase;
@@ -3129,7 +3474,7 @@ const useDialog = ({ open, onClose, closeOnBackdropClick, maxWidth, maxHeight, m
3129
3474
  */
3130
3475
  const DialogHeader = ({ title, onClose }) => {
3131
3476
  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 })] }));
3477
+ 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
3478
  };
3134
3479
  DialogHeader.displayName = 'Dialog.Header';
3135
3480
 
@@ -3561,6 +3906,7 @@ exports.Alert = Alert;
3561
3906
  exports.Backdrop = Backdrop;
3562
3907
  exports.Badge = Badge;
3563
3908
  exports.Box = Box;
3909
+ exports.Breadcrumb = Breadcrumb;
3564
3910
  exports.Button = Button;
3565
3911
  exports.Card = Card;
3566
3912
  exports.Checkbox = Checkbox;
@@ -3577,6 +3923,7 @@ exports.Select = Select;
3577
3923
  exports.Skeleton = Skeleton;
3578
3924
  exports.Stack = Stack;
3579
3925
  exports.Switch = Switch;
3926
+ exports.Tabs = Tabs;
3580
3927
  exports.Text = Text;
3581
3928
  exports.TextField = TextField;
3582
3929
  exports.Tooltip = Tooltip;