@aurora-ds/components 1.6.0 → 1.7.1

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
@@ -972,13 +972,13 @@ const LINK_STYLES = theme.createStyles((theme) => ({
972
972
  * In that case the component stays accessible: it gets `role="link"`,
973
973
  * `tabIndex={0}` and keyboard Enter support automatically.
974
974
  *
975
- * @example <Link href='/about'>About</Link>
976
- * @example <Link href='https://example.com' external>External site</Link>
977
- * @example <Link href='/profile' underline='always' startIcon={UserIcon}>Profile</Link>
978
- * @example <Link href='/terms' underline='none'>Terms</Link>
979
- * @example <Link onClick={() => navigate('/about')}>About (SPA)</Link>
975
+ * @example <Link href='/about' label='About' />
976
+ * @example <Link href='https://example.com' label='External site' external />
977
+ * @example <Link href='/profile' underline='always' startIcon={UserIcon} label='Profile' />
978
+ * @example <Link href='/terms' underline='none' label='Terms' />
979
+ * @example <Link onClick={() => navigate('/about')} label='About (SPA)' />
980
980
  */
981
- const Link = ({ ref, underline = 'hover', color = 'default', external = false, disabled = false, startIcon: StartIcon, endIcon: EndIcon, children, className, href, onClick, onKeyDown, ...rest }) => {
981
+ const Link = ({ ref, label, fontSize = 'sm', underline = 'hover', color = 'default', external = false, disabled = false, startIcon: StartIcon, endIcon: EndIcon, className, href, onClick, onKeyDown, ...rest }) => {
982
982
  // An <a> without href has no implicit ARIA role and is not focusable.
983
983
  // When used for SPA navigation (onClick only), we restore both behaviours.
984
984
  const hasHref = !!href;
@@ -1004,7 +1004,7 @@ const Link = ({ ref, underline = 'hover', color = 'default', external = false, d
1004
1004
  // With href: the browser handles focusability natively (no tabIndex needed).
1005
1005
  tabIndex: disabled ? -1 : (!hasHref ? 0 : undefined),
1006
1006
  // Without href: <a> has no implicit ARIA role — add role="link" explicitly.
1007
- 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' }) }))] }));
1007
+ 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' }) })), jsxRuntime.jsx(Text, { as: 'span', fontSize: fontSize, children: label }), EndIcon && (jsxRuntime.jsx("span", { className: LINK_STYLES.icon, "aria-hidden": true, children: jsxRuntime.jsx(EndIcon, { width: '1em', height: '1em' }) }))] }));
1008
1008
  };
1009
1009
  Link.displayName = 'Link';
1010
1010
 
@@ -5672,7 +5672,13 @@ const DRAWER_STYLES = theme.createStyles((theme) => ({
5672
5672
  * Keep in sync with themeBreakpoints.
5673
5673
  */
5674
5674
  const BREAKPOINTS = {
5675
- sm: 640};
5675
+ xs: 480,
5676
+ sm: 640,
5677
+ md: 768,
5678
+ lg: 1024,
5679
+ xl: 1280,
5680
+ '2xl': 1536,
5681
+ };
5676
5682
  /** Max-width media query strings (max = breakpoint - 1px). */
5677
5683
  const MEDIA_MAX = {
5678
5684
  sm: `max-width: ${BREAKPOINTS.sm - 1}px`};
@@ -6571,6 +6577,13 @@ const AlertContext = React.createContext({
6571
6577
  });
6572
6578
  const useAlertContext = () => React.useContext(AlertContext);
6573
6579
 
6580
+ const VARIANT_DISMISS_COLOR = {
6581
+ default: 'neutral',
6582
+ success: 'success',
6583
+ error: 'error',
6584
+ warning: 'warning',
6585
+ info: 'info',
6586
+ };
6574
6587
  const VARIANT_ICONS = {
6575
6588
  success: AlertSuccessIcon,
6576
6589
  error: AlertErrorIcon,
@@ -6583,7 +6596,7 @@ const isSvgComponent = (value) => typeof value === 'function';
6583
6596
  * Alert title row: renders the variant icon alongside the title text.
6584
6597
  * Must be used inside an `<Alert>` component.
6585
6598
  */
6586
- const AlertTitle = ({ children, icon }) => {
6599
+ const AlertTitle = ({ children, icon, onDismiss }) => {
6587
6600
  const { variant, accentColor } = useAlertContext();
6588
6601
  // Resolve which icon to render:
6589
6602
  // - custom icon prop always takes precedence over the built-in variant icon
@@ -6593,7 +6606,7 @@ const AlertTitle = ({ children, icon }) => {
6593
6606
  const ResolvedIcon = icon && isSvgComponent(icon) ? icon : (icon === undefined ? (builtInIcon ?? null) : null);
6594
6607
  const customNode = icon && !isSvgComponent(icon) ? icon : null;
6595
6608
  const hasIcon = ResolvedIcon !== null || customNode !== null;
6596
- return (jsxRuntime.jsxs(Stack, { flexDirection: 'row', alignItems: 'center', gap: 'sm', children: [hasIcon && (jsxRuntime.jsx(Stack, { flexShrink: 0, alignItems: 'center', color: accentColor, width: '1.25rem', height: '1.25rem', "aria-hidden": true, children: ResolvedIcon ? (jsxRuntime.jsx(ResolvedIcon, { width: 20, height: 20 })) : (customNode) })), jsxRuntime.jsx(Text, { fontWeight: 'semibold', fontSize: 'sm', color: accentColor, children: children })] }));
6609
+ return (jsxRuntime.jsxs(Stack, { flexDirection: 'row', alignItems: 'center', gap: 'sm', justifyContent: onDismiss ? 'space-between' : undefined, children: [jsxRuntime.jsxs(Stack, { flexDirection: 'row', alignItems: 'center', gap: 'sm', children: [hasIcon && (jsxRuntime.jsx(Stack, { flexShrink: 0, alignItems: 'center', color: accentColor, width: '1.25rem', height: '1.25rem', "aria-hidden": true, children: ResolvedIcon ? (jsxRuntime.jsx(ResolvedIcon, { width: 20, height: 20 })) : (customNode) })), jsxRuntime.jsx(Text, { fontWeight: 'semibold', fontSize: 'sm', color: accentColor, children: children })] }), onDismiss && (jsxRuntime.jsx(IconButton, { icon: CloseIcon, ariaLabel: 'Dismiss', variant: 'text', color: VARIANT_DISMISS_COLOR[variant], size: 'sm', onClick: onDismiss }))] }));
6597
6610
  };
6598
6611
  AlertTitle.displayName = 'Alert.Title';
6599
6612
 
@@ -6603,6 +6616,22 @@ AlertTitle.displayName = 'Alert.Title';
6603
6616
  const AlertBody = ({ children }) => (jsxRuntime.jsx(Text, { as: 'p', fontSize: 'sm', color: 'textSecondary', children: children }));
6604
6617
  AlertBody.displayName = 'Alert.Body';
6605
6618
 
6619
+ /**
6620
+ * Alert actions row: renders action elements (buttons, links…) at the bottom of an `<Alert>`.
6621
+ * Must be used inside an `<Alert>` component.
6622
+ *
6623
+ * @example
6624
+ * <Alert variant="error">
6625
+ * <Alert.Title>Something went wrong</Alert.Title>
6626
+ * <Alert.Body>Please try again.</Alert.Body>
6627
+ * <Alert.Actions>
6628
+ * <Button size="sm" variant="outlined" color="error">Retry</Button>
6629
+ * </Alert.Actions>
6630
+ * </Alert>
6631
+ */
6632
+ const AlertActions = ({ children }) => (jsxRuntime.jsx(Stack, { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', justifyContent: 'end', gap: 'sm', paddingTop: 'xs', children: children }));
6633
+ AlertActions.displayName = 'Alert.Actions';
6634
+
6606
6635
  const VARIANT_TOKENS = {
6607
6636
  default: { backgroundColor: 'surfacePaper', borderColor: 'defaultMain', accentColor: 'defaultActive' },
6608
6637
  success: { backgroundColor: 'successSubtle', borderColor: 'successMain', accentColor: 'successActive' },
@@ -6644,6 +6673,7 @@ AlertBase.displayName = 'Alert';
6644
6673
  const Alert = AlertBase;
6645
6674
  Alert.Title = AlertTitle;
6646
6675
  Alert.Body = AlertBody;
6676
+ Alert.Actions = AlertActions;
6647
6677
 
6648
6678
  const TRANSITION = `${DEFAULT_TRANSITION_DURATION_MS}ms ease`;
6649
6679
  const DIALOG_STYLES = theme.createStyles((theme) => ({
@@ -6690,12 +6720,18 @@ const DIALOG_STYLES = theme.createStyles((theme) => ({
6690
6720
  transform: 'translateY(0)',
6691
6721
  },
6692
6722
  },
6693
- // Full-height variant on mobile (applied via `fullscreen` prop)
6723
+ // Full-viewport variant (applied via `fullscreen` prop — all screen sizes)
6694
6724
  panelFullscreen: {
6695
- [`@media (${MEDIA_MAX.sm})`]: {
6696
- height: '100dvh',
6697
- maxHeight: 'none',
6698
- },
6725
+ top: 0,
6726
+ left: 0,
6727
+ right: 0,
6728
+ bottom: 0,
6729
+ margin: 0,
6730
+ width: '100%',
6731
+ height: '100dvh',
6732
+ maxWidth: 'none',
6733
+ maxHeight: 'none',
6734
+ borderRadius: 0,
6699
6735
  },
6700
6736
  }), { id: 'dialog' });
6701
6737
 
@@ -6792,6 +6828,53 @@ const Dialog = DialogBase;
6792
6828
  Dialog.Header = DialogHeader;
6793
6829
  Dialog.Body = DialogBody;
6794
6830
 
6831
+ /**
6832
+ * Listens to a CSS media query string and returns whether it currently matches.
6833
+ *
6834
+ * @param query - A valid CSS media query string, e.g. `'(max-width: 639px)'`.
6835
+ * @returns `true` when the media query matches, `false` otherwise.
6836
+ *
6837
+ * @example
6838
+ * const isMobile = useMediaQuery('(max-width: 639px)')
6839
+ */
6840
+ const useMediaQuery = (query) => {
6841
+ const [matches, setMatches] = React.useState(() => {
6842
+ if (typeof window === 'undefined') {
6843
+ return false;
6844
+ }
6845
+ return window.matchMedia(query).matches;
6846
+ });
6847
+ React.useEffect(() => {
6848
+ if (typeof window === 'undefined') {
6849
+ return;
6850
+ }
6851
+ const mediaQueryList = window.matchMedia(query);
6852
+ setMatches(mediaQueryList.matches);
6853
+ const listener = (event) => setMatches(event.matches);
6854
+ mediaQueryList.addEventListener('change', listener);
6855
+ return () => mediaQueryList.removeEventListener('change', listener);
6856
+ }, [query]);
6857
+ return matches;
6858
+ };
6859
+ /**
6860
+ * Returns `true` when the viewport width is **below** the given breakpoint (mobile-first max).
6861
+ *
6862
+ * @param breakpoint - One of the Aurora breakpoints: `xs` | `sm` | `md` | `lg` | `xl` | `2xl`.
6863
+ *
6864
+ * @example
6865
+ * const isMobile = useBreakpointMax('sm') // true when width < 640px
6866
+ */
6867
+ const useBreakpointMax = (breakpoint) => useMediaQuery(`(max-width: ${BREAKPOINTS[breakpoint] - 1}px)`);
6868
+ /**
6869
+ * Returns `true` when the viewport width is **at or above** the given breakpoint.
6870
+ *
6871
+ * @param breakpoint - One of the Aurora breakpoints: `xs` | `sm` | `md` | `lg` | `xl` | `2xl`.
6872
+ *
6873
+ * @example
6874
+ * const isDesktop = useBreakpointMin('md') // true when width >= 768px
6875
+ */
6876
+ const useBreakpointMin = (breakpoint) => useMediaQuery(`(min-width: ${BREAKPOINTS[breakpoint]}px)`);
6877
+
6795
6878
  /**
6796
6879
  * Manages keyboard navigation for a listbox-style menu.
6797
6880
  *
@@ -7314,11 +7397,14 @@ exports.Tooltip = Tooltip;
7314
7397
  exports.darkTheme = darkTheme;
7315
7398
  exports.lightTheme = lightTheme;
7316
7399
  exports.useBodyScrollLock = useBodyScrollLock;
7400
+ exports.useBreakpointMax = useBreakpointMax;
7401
+ exports.useBreakpointMin = useBreakpointMin;
7317
7402
  exports.useControllableState = useControllableState;
7318
7403
  exports.useDrawerContext = useDrawerContext;
7319
7404
  exports.useFocusTrap = useFocusTrap;
7320
7405
  exports.useKeyPress = useKeyPress;
7321
7406
  exports.useListKeyNav = useListKeyNav;
7407
+ exports.useMediaQuery = useMediaQuery;
7322
7408
  exports.useMenuPosition = useMenuPosition;
7323
7409
  exports.useMergedRefs = useMergedRefs;
7324
7410
  exports.useTooltipPosition = useTooltipPosition;