@aurora-ds/components 1.5.0 → 1.7.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 +216 -34
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +205 -35
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +305 -13
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -379,8 +379,8 @@ const buildActionButtonRootStyle = (theme, variant, color, focusOptions) => {
|
|
|
379
379
|
backgroundColor: intent.main,
|
|
380
380
|
borderColor: intent.main,
|
|
381
381
|
color: intent.on,
|
|
382
|
-
':hover:not(:disabled)': { backgroundColor: intent.hover
|
|
383
|
-
':active:not(:disabled)': { backgroundColor: intent.active
|
|
382
|
+
':hover:not(:disabled)': { backgroundColor: intent.hover },
|
|
383
|
+
':active:not(:disabled)': { backgroundColor: intent.active },
|
|
384
384
|
}
|
|
385
385
|
: variant === 'outlined'
|
|
386
386
|
? {
|
|
@@ -388,7 +388,7 @@ const buildActionButtonRootStyle = (theme, variant, color, focusOptions) => {
|
|
|
388
388
|
borderColor: intent.border,
|
|
389
389
|
color: intent.fg,
|
|
390
390
|
':hover:not(:disabled)': { backgroundColor: intent.subtleHover, color: intent.fgHover },
|
|
391
|
-
':active:not(:disabled)': { backgroundColor: intent.subtleActive,
|
|
391
|
+
':active:not(:disabled)': { backgroundColor: intent.subtleActive, color: intent.active },
|
|
392
392
|
}
|
|
393
393
|
: {
|
|
394
394
|
backgroundColor: 'transparent',
|
|
@@ -410,7 +410,7 @@ const buildActionButtonRootStyle = (theme, variant, color, focusOptions) => {
|
|
|
410
410
|
height: DEFAULT_BUTTON_HEIGHT,
|
|
411
411
|
cursor: 'pointer',
|
|
412
412
|
outline: 'none',
|
|
413
|
-
transition: `background-color ${theme.transition.normal},
|
|
413
|
+
transition: `background-color ${theme.transition.normal}, color ${theme.transition.normal}`,
|
|
414
414
|
...colorVariantStyles,
|
|
415
415
|
':focus-visible': getFocusRingStyles(theme, focusOptions),
|
|
416
416
|
':disabled': { cursor: 'not-allowed', opacity: theme.opacity.high },
|
|
@@ -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'
|
|
976
|
-
* @example <Link href='https://example.com'
|
|
977
|
-
* @example <Link href='/profile' underline='always' startIcon={UserIcon}
|
|
978
|
-
* @example <Link href='/terms' underline='none'
|
|
979
|
-
* @example <Link onClick={() => navigate('/about')}
|
|
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,
|
|
981
|
+
const Link = ({ ref, label, fontSize = 'md', 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
|
|
|
@@ -3298,17 +3298,17 @@ const TEXTFIELD_WRAPPER_VARIANTS = theme.createVariants((theme) => {
|
|
|
3298
3298
|
return {
|
|
3299
3299
|
base: {
|
|
3300
3300
|
display: 'flex',
|
|
3301
|
-
|
|
3302
|
-
|
|
3301
|
+
// `stretch` lets the inner input fill the full height of the box so
|
|
3302
|
+
// clicking anywhere (including the vertical padding area) hits it.
|
|
3303
|
+
alignItems: 'stretch',
|
|
3303
3304
|
boxSizing: 'border-box',
|
|
3304
3305
|
borderWidth: '1px',
|
|
3305
3306
|
borderStyle: 'solid',
|
|
3306
3307
|
borderRadius: theme.radius.md,
|
|
3307
3308
|
backgroundColor: c.surfacePaper,
|
|
3309
|
+
// No focus ring on text inputs: focus is conveyed by the status
|
|
3310
|
+
// border colour change (see the `status` variants below).
|
|
3308
3311
|
transition: `border-color ${theme.transition.fast}`,
|
|
3309
|
-
// Full, always-visible focus ring when the inner input is focused
|
|
3310
|
-
// (single source of truth) — complements the status border colour.
|
|
3311
|
-
':focus-within': getFocusRingStyles(theme),
|
|
3312
3312
|
'&[data-disabled]': {
|
|
3313
3313
|
opacity: theme.opacity.high,
|
|
3314
3314
|
backgroundColor: c.disabledMain,
|
|
@@ -3317,10 +3317,12 @@ const TEXTFIELD_WRAPPER_VARIANTS = theme.createVariants((theme) => {
|
|
|
3317
3317
|
},
|
|
3318
3318
|
variants: {
|
|
3319
3319
|
size: {
|
|
3320
|
+
// Horizontal padding lives on the input itself (see TEXTFIELD_INPUT_VARIANTS),
|
|
3321
|
+
// not here, so the whole bordered area is part of the clickable input.
|
|
3320
3322
|
// Font-size is set on the wrapper so the native input inherits it via `fontSize: 'inherit'`.
|
|
3321
|
-
sm: { height: '2rem',
|
|
3322
|
-
md: { height: '2.5rem',
|
|
3323
|
-
lg: { height: '3rem',
|
|
3323
|
+
sm: { height: '2rem', fontSize: theme.fontSize.xs },
|
|
3324
|
+
md: { height: '2.5rem', fontSize: theme.fontSize.sm },
|
|
3325
|
+
lg: { height: '3rem', fontSize: theme.fontSize.md },
|
|
3324
3326
|
},
|
|
3325
3327
|
status: {
|
|
3326
3328
|
default: {
|
|
@@ -3345,11 +3347,17 @@ const TEXTFIELD_WRAPPER_VARIANTS = theme.createVariants((theme) => {
|
|
|
3345
3347
|
defaultVariants: { size: 'md', status: 'default' },
|
|
3346
3348
|
};
|
|
3347
3349
|
}, { id: 'textfield-wrapper' });
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3350
|
+
/**
|
|
3351
|
+
* The native input carries the horizontal padding and fills 100% of the
|
|
3352
|
+
* wrapper height, so the entire bordered box is part of the clickable input
|
|
3353
|
+
* (no dead zone between the text and the border).
|
|
3354
|
+
*/
|
|
3355
|
+
const TEXTFIELD_INPUT_VARIANTS = theme.createVariants((theme) => ({
|
|
3356
|
+
base: {
|
|
3351
3357
|
flex: 1,
|
|
3352
3358
|
minWidth: 0,
|
|
3359
|
+
height: '100%',
|
|
3360
|
+
boxSizing: 'border-box',
|
|
3353
3361
|
border: 'none',
|
|
3354
3362
|
outline: 'none',
|
|
3355
3363
|
background: 'transparent',
|
|
@@ -3357,15 +3365,25 @@ const TEXTFIELD_STYLES = theme.createStyles((theme) => ({
|
|
|
3357
3365
|
fontFamily: 'inherit',
|
|
3358
3366
|
fontSize: 'inherit',
|
|
3359
3367
|
lineHeight: 'normal',
|
|
3360
|
-
padding: '0',
|
|
3361
3368
|
'&::placeholder': { color: theme.colors.textTertiary },
|
|
3362
3369
|
'&:disabled': { cursor: 'not-allowed', color: theme.colors.textDisabled },
|
|
3363
3370
|
},
|
|
3371
|
+
variants: {
|
|
3372
|
+
size: {
|
|
3373
|
+
sm: { paddingLeft: theme.spacing.sm, paddingRight: theme.spacing.sm },
|
|
3374
|
+
md: { paddingLeft: theme.spacing.sm, paddingRight: theme.spacing.sm },
|
|
3375
|
+
lg: { paddingLeft: theme.spacing.md, paddingRight: theme.spacing.md },
|
|
3376
|
+
},
|
|
3377
|
+
},
|
|
3378
|
+
defaultVariants: { size: 'md' },
|
|
3379
|
+
}), { id: 'textfield-input' });
|
|
3380
|
+
const TEXTFIELD_STYLES = theme.createStyles((theme) => ({
|
|
3364
3381
|
/** Wrapper for the start icon — aligned with the input baseline. Clickable to focus the input. */
|
|
3365
3382
|
startIconWrap: {
|
|
3366
3383
|
display: 'flex',
|
|
3367
3384
|
alignItems: 'center',
|
|
3368
3385
|
flexShrink: 0,
|
|
3386
|
+
paddingLeft: theme.spacing.sm,
|
|
3369
3387
|
cursor: 'pointer',
|
|
3370
3388
|
},
|
|
3371
3389
|
/** Wrapper for end actions (custom content + optional password toggle). */
|
|
@@ -3373,6 +3391,7 @@ const TEXTFIELD_STYLES = theme.createStyles((theme) => ({
|
|
|
3373
3391
|
display: 'flex',
|
|
3374
3392
|
alignItems: 'center',
|
|
3375
3393
|
flexShrink: 0,
|
|
3394
|
+
paddingRight: theme.spacing.xs,
|
|
3376
3395
|
gap: theme.spacing['2xs'],
|
|
3377
3396
|
},
|
|
3378
3397
|
}), { id: 'textfield-extra' });
|
|
@@ -3433,7 +3452,11 @@ const useTextField = ({ id, ref, type, size, endAction, }) => {
|
|
|
3433
3452
|
*/
|
|
3434
3453
|
const TextField = ({ ref, label, helperText, size = 'md', status = 'default', startIcon: StartIcon, endAction, type, id, disabled, required, ...rest }) => {
|
|
3435
3454
|
const { fieldId, helperId, mergedRef, isPassword, showPassword, togglePassword, resolvedType, iconSize, iconButtonSize, hasEndSection, focusInput, } = useTextField({ id, ref, type, size, endAction });
|
|
3436
|
-
|
|
3455
|
+
// Associate the label with the input via `aria-labelledby` (and NOT `htmlFor`)
|
|
3456
|
+
// so the label stays accessible without natively focusing the input on click —
|
|
3457
|
+
// only clicking inside the bordered box should focus the field.
|
|
3458
|
+
const labelId = `${fieldId}-label`;
|
|
3459
|
+
return (jsxRuntime.jsxs(Stack, { flexDirection: 'column', gap: 'xs', children: [label !== undefined && (jsxRuntime.jsxs(Text, { variant: 'label', fontSize: 'sm', fontWeight: 'medium', color: 'textSecondary', id: labelId, 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-labelledby": label !== undefined ? labelId : undefined, "aria-describedby": helperText !== undefined ? helperId : undefined, "aria-errormessage": status === 'error' && helperText !== undefined ? helperId : undefined, className: TEXTFIELD_INPUT_VARIANTS({ size }), ...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 }))] }));
|
|
3437
3460
|
};
|
|
3438
3461
|
TextField.displayName = 'TextField';
|
|
3439
3462
|
|
|
@@ -4026,8 +4049,8 @@ const SELECT_TRIGGER_VARIANTS = theme.createVariants((theme) => {
|
|
|
4026
4049
|
transition: `border-color ${theme.transition.fast}`,
|
|
4027
4050
|
outline: 'none',
|
|
4028
4051
|
fontFamily: 'inherit',
|
|
4029
|
-
//
|
|
4030
|
-
|
|
4052
|
+
// No focus ring on the trigger: focus is conveyed by the border
|
|
4053
|
+
// colour change (see the `status` variants below).
|
|
4031
4054
|
'&[data-open]': {
|
|
4032
4055
|
borderColor: c.primaryMain,
|
|
4033
4056
|
},
|
|
@@ -4064,6 +4087,9 @@ const SELECT_TRIGGER_VARIANTS = theme.createVariants((theme) => {
|
|
|
4064
4087
|
':hover:not([data-disabled]):not([data-open])': {
|
|
4065
4088
|
borderColor: c.borderStrong,
|
|
4066
4089
|
},
|
|
4090
|
+
'&:focus-visible:not([data-open])': {
|
|
4091
|
+
borderColor: c.primaryMain,
|
|
4092
|
+
},
|
|
4067
4093
|
},
|
|
4068
4094
|
error: {
|
|
4069
4095
|
borderColor: c.errorMain,
|
|
@@ -5646,7 +5672,13 @@ const DRAWER_STYLES = theme.createStyles((theme) => ({
|
|
|
5646
5672
|
* Keep in sync with themeBreakpoints.
|
|
5647
5673
|
*/
|
|
5648
5674
|
const BREAKPOINTS = {
|
|
5649
|
-
|
|
5675
|
+
xs: 480,
|
|
5676
|
+
sm: 640,
|
|
5677
|
+
md: 768,
|
|
5678
|
+
lg: 1024,
|
|
5679
|
+
xl: 1280,
|
|
5680
|
+
'2xl': 1536,
|
|
5681
|
+
};
|
|
5650
5682
|
/** Max-width media query strings (max = breakpoint - 1px). */
|
|
5651
5683
|
const MEDIA_MAX = {
|
|
5652
5684
|
sm: `max-width: ${BREAKPOINTS.sm - 1}px`};
|
|
@@ -6545,6 +6577,13 @@ const AlertContext = React.createContext({
|
|
|
6545
6577
|
});
|
|
6546
6578
|
const useAlertContext = () => React.useContext(AlertContext);
|
|
6547
6579
|
|
|
6580
|
+
const VARIANT_DISMISS_COLOR = {
|
|
6581
|
+
default: 'neutral',
|
|
6582
|
+
success: 'success',
|
|
6583
|
+
error: 'error',
|
|
6584
|
+
warning: 'warning',
|
|
6585
|
+
info: 'info',
|
|
6586
|
+
};
|
|
6548
6587
|
const VARIANT_ICONS = {
|
|
6549
6588
|
success: AlertSuccessIcon,
|
|
6550
6589
|
error: AlertErrorIcon,
|
|
@@ -6557,7 +6596,7 @@ const isSvgComponent = (value) => typeof value === 'function';
|
|
|
6557
6596
|
* Alert title row: renders the variant icon alongside the title text.
|
|
6558
6597
|
* Must be used inside an `<Alert>` component.
|
|
6559
6598
|
*/
|
|
6560
|
-
const AlertTitle = ({ children, icon }) => {
|
|
6599
|
+
const AlertTitle = ({ children, icon, onDismiss }) => {
|
|
6561
6600
|
const { variant, accentColor } = useAlertContext();
|
|
6562
6601
|
// Resolve which icon to render:
|
|
6563
6602
|
// - custom icon prop always takes precedence over the built-in variant icon
|
|
@@ -6567,7 +6606,7 @@ const AlertTitle = ({ children, icon }) => {
|
|
|
6567
6606
|
const ResolvedIcon = icon && isSvgComponent(icon) ? icon : (icon === undefined ? (builtInIcon ?? null) : null);
|
|
6568
6607
|
const customNode = icon && !isSvgComponent(icon) ? icon : null;
|
|
6569
6608
|
const hasIcon = ResolvedIcon !== null || customNode !== null;
|
|
6570
|
-
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 }))] }));
|
|
6571
6610
|
};
|
|
6572
6611
|
AlertTitle.displayName = 'Alert.Title';
|
|
6573
6612
|
|
|
@@ -6577,6 +6616,22 @@ AlertTitle.displayName = 'Alert.Title';
|
|
|
6577
6616
|
const AlertBody = ({ children }) => (jsxRuntime.jsx(Text, { as: 'p', fontSize: 'sm', color: 'textSecondary', children: children }));
|
|
6578
6617
|
AlertBody.displayName = 'Alert.Body';
|
|
6579
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
|
+
|
|
6580
6635
|
const VARIANT_TOKENS = {
|
|
6581
6636
|
default: { backgroundColor: 'surfacePaper', borderColor: 'defaultMain', accentColor: 'defaultActive' },
|
|
6582
6637
|
success: { backgroundColor: 'successSubtle', borderColor: 'successMain', accentColor: 'successActive' },
|
|
@@ -6618,6 +6673,7 @@ AlertBase.displayName = 'Alert';
|
|
|
6618
6673
|
const Alert = AlertBase;
|
|
6619
6674
|
Alert.Title = AlertTitle;
|
|
6620
6675
|
Alert.Body = AlertBody;
|
|
6676
|
+
Alert.Actions = AlertActions;
|
|
6621
6677
|
|
|
6622
6678
|
const TRANSITION = `${DEFAULT_TRANSITION_DURATION_MS}ms ease`;
|
|
6623
6679
|
const DIALOG_STYLES = theme.createStyles((theme) => ({
|
|
@@ -6664,12 +6720,18 @@ const DIALOG_STYLES = theme.createStyles((theme) => ({
|
|
|
6664
6720
|
transform: 'translateY(0)',
|
|
6665
6721
|
},
|
|
6666
6722
|
},
|
|
6667
|
-
// Full-
|
|
6723
|
+
// Full-viewport variant (applied via `fullscreen` prop — all screen sizes)
|
|
6668
6724
|
panelFullscreen: {
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
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,
|
|
6673
6735
|
},
|
|
6674
6736
|
}), { id: 'dialog' });
|
|
6675
6737
|
|
|
@@ -6766,6 +6828,114 @@ const Dialog = DialogBase;
|
|
|
6766
6828
|
Dialog.Header = DialogHeader;
|
|
6767
6829
|
Dialog.Body = DialogBody;
|
|
6768
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
|
+
|
|
6878
|
+
/**
|
|
6879
|
+
* Manages keyboard navigation for a listbox-style menu.
|
|
6880
|
+
*
|
|
6881
|
+
* Binds ArrowDown, ArrowUp, Home, End, and Enter using `useKeyPress`.
|
|
6882
|
+
* Automatically skips disabled items and optionally wraps around (loop).
|
|
6883
|
+
* Resets focus when `enabled` toggles.
|
|
6884
|
+
*/
|
|
6885
|
+
const useListKeyNav = ({ itemCount, enabled, onSelect, isDisabled, loop = true, initialIndex = 0, }) => {
|
|
6886
|
+
const [focusedIndex, setFocusedIndex] = React.useState(-1);
|
|
6887
|
+
const initialIndexRef = React.useRef(initialIndex);
|
|
6888
|
+
React.useEffect(() => {
|
|
6889
|
+
initialIndexRef.current = initialIndex;
|
|
6890
|
+
}, [initialIndex]);
|
|
6891
|
+
React.useEffect(() => {
|
|
6892
|
+
setFocusedIndex(enabled ? initialIndexRef.current : -1);
|
|
6893
|
+
}, [enabled]);
|
|
6894
|
+
const getNextIndex = React.useCallback((current, direction) => {
|
|
6895
|
+
if (itemCount === 0) {
|
|
6896
|
+
return -1;
|
|
6897
|
+
}
|
|
6898
|
+
let next = current + direction;
|
|
6899
|
+
for (let i = 0; i < itemCount; i++) {
|
|
6900
|
+
if (next < 0) {
|
|
6901
|
+
next = loop ? itemCount - 1 : 0;
|
|
6902
|
+
}
|
|
6903
|
+
if (next >= itemCount) {
|
|
6904
|
+
next = loop ? 0 : itemCount - 1;
|
|
6905
|
+
}
|
|
6906
|
+
if (!isDisabled?.(next)) {
|
|
6907
|
+
return next;
|
|
6908
|
+
}
|
|
6909
|
+
next += direction;
|
|
6910
|
+
}
|
|
6911
|
+
return current;
|
|
6912
|
+
}, [itemCount, loop, isDisabled]);
|
|
6913
|
+
useKeyPress({
|
|
6914
|
+
ArrowDown: (e) => {
|
|
6915
|
+
e.preventDefault();
|
|
6916
|
+
setFocusedIndex((prev) => getNextIndex(prev, 1));
|
|
6917
|
+
},
|
|
6918
|
+
ArrowUp: (e) => {
|
|
6919
|
+
e.preventDefault();
|
|
6920
|
+
setFocusedIndex((prev) => getNextIndex(prev, -1));
|
|
6921
|
+
},
|
|
6922
|
+
Home: (e) => {
|
|
6923
|
+
e.preventDefault();
|
|
6924
|
+
setFocusedIndex(getNextIndex(-1, 1));
|
|
6925
|
+
},
|
|
6926
|
+
End: (e) => {
|
|
6927
|
+
e.preventDefault();
|
|
6928
|
+
setFocusedIndex(getNextIndex(itemCount, -1));
|
|
6929
|
+
},
|
|
6930
|
+
Enter: () => {
|
|
6931
|
+
if (focusedIndex >= 0) {
|
|
6932
|
+
onSelect(focusedIndex);
|
|
6933
|
+
}
|
|
6934
|
+
},
|
|
6935
|
+
}, { enabled });
|
|
6936
|
+
return { focusedIndex, setFocusedIndex };
|
|
6937
|
+
};
|
|
6938
|
+
|
|
6769
6939
|
const lightPalette = {
|
|
6770
6940
|
// Surface
|
|
6771
6941
|
surfaceBackground: '#f8fafc',
|
|
@@ -7226,5 +7396,17 @@ exports.ToggleIconButton = ToggleIconButton;
|
|
|
7226
7396
|
exports.Tooltip = Tooltip;
|
|
7227
7397
|
exports.darkTheme = darkTheme;
|
|
7228
7398
|
exports.lightTheme = lightTheme;
|
|
7399
|
+
exports.useBodyScrollLock = useBodyScrollLock;
|
|
7400
|
+
exports.useBreakpointMax = useBreakpointMax;
|
|
7401
|
+
exports.useBreakpointMin = useBreakpointMin;
|
|
7402
|
+
exports.useControllableState = useControllableState;
|
|
7229
7403
|
exports.useDrawerContext = useDrawerContext;
|
|
7404
|
+
exports.useFocusTrap = useFocusTrap;
|
|
7405
|
+
exports.useKeyPress = useKeyPress;
|
|
7406
|
+
exports.useListKeyNav = useListKeyNav;
|
|
7407
|
+
exports.useMediaQuery = useMediaQuery;
|
|
7408
|
+
exports.useMenuPosition = useMenuPosition;
|
|
7409
|
+
exports.useMergedRefs = useMergedRefs;
|
|
7410
|
+
exports.useTooltipPosition = useTooltipPosition;
|
|
7411
|
+
exports.useTransitionRender = useTransitionRender;
|
|
7230
7412
|
//# sourceMappingURL=index.js.map
|