@westpac/ui 0.53.2 → 0.55.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/CHANGELOG.md +26 -0
- package/dist/component-type.json +1 -1
- package/dist/components/accordion/components/accordion-item/accordion-item.styles.js +9 -1
- package/dist/components/autocomplete/components/autocomplete-list-box/autocomplete-list-box.component.js +1 -1
- package/dist/components/autocomplete/components/autocomplete-popover/autocomplete-popover.component.js +1 -1
- package/dist/components/button/button.component.d.ts +1 -0
- package/dist/components/button/button.component.js +3 -2
- package/dist/components/button/button.styles.d.ts +9 -0
- package/dist/components/button/button.styles.js +12 -1
- package/dist/components/button/button.types.d.ts +5 -0
- package/dist/components/button-dropdown/button-dropdown.component.d.ts +1 -1
- package/dist/components/button-dropdown/button-dropdown.component.js +2 -2
- package/dist/components/button-dropdown/button-dropdown.types.d.ts +5 -0
- package/dist/components/button-group/button-group.component.js +1 -0
- package/dist/components/checkbox-group/checkbox-group.component.js +1 -0
- package/dist/components/header/header.component.js +2 -1
- package/dist/components/header/header.types.d.ts +2 -1
- package/dist/components/modal/components/modal-backdrop/modal-backdrop.component.d.ts +1 -1
- package/dist/components/modal/components/modal-backdrop/modal-backdrop.component.js +3 -3
- package/dist/components/modal/components/modal-backdrop/modal-backdrop.styles.d.ts +39 -20
- package/dist/components/modal/components/modal-backdrop/modal-backdrop.styles.js +52 -11
- package/dist/components/modal/components/modal-backdrop/modal-backdrop.types.d.ts +5 -0
- package/dist/components/modal/components/modal-dialog/components/modal-dialog-body/modal-dialog-body.component.js +8 -2
- package/dist/components/modal/components/modal-dialog/components/modal-dialog-body/modal-dialog-body.styles.d.ts +54 -0
- package/dist/components/modal/components/modal-dialog/components/modal-dialog-body/modal-dialog-body.styles.js +64 -4
- package/dist/components/modal/components/modal-dialog/components/modal-dialog-footer/modal-dialog-footer.component.js +9 -3
- package/dist/components/modal/components/modal-dialog/components/modal-dialog-footer/modal-dialog-footer.styles.d.ts +12 -0
- package/dist/components/modal/components/modal-dialog/components/modal-dialog-footer/modal-dialog-footer.styles.js +25 -8
- package/dist/components/modal/components/modal-dialog/modal-dialog.component.d.ts +1 -1
- package/dist/components/modal/components/modal-dialog/modal-dialog.component.js +49 -4
- package/dist/components/modal/components/modal-dialog/modal-dialog.styles.d.ts +27 -0
- package/dist/components/modal/components/modal-dialog/modal-dialog.styles.js +39 -6
- package/dist/components/modal/components/modal-dialog/modal-dialog.types.d.ts +33 -0
- package/dist/components/modal/modal.component.d.ts +1 -1
- package/dist/components/modal/modal.component.js +4 -2
- package/dist/components/popover/components/panel/panel.styles.js +1 -1
- package/dist/components/radio-group/radio-group.component.js +1 -0
- package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.component.js +1 -0
- package/dist/components/selector/components/selector-radio-group/selector-radio-group.component.js +1 -0
- package/dist/css/westpac-ui.css +226 -25
- package/dist/css/westpac-ui.min.css +226 -25
- package/package.json +6 -6
- package/src/components/accordion/components/accordion-item/accordion-item.styles.ts +7 -1
- package/src/components/autocomplete/components/autocomplete-list-box/autocomplete-list-box.component.tsx +1 -1
- package/src/components/autocomplete/components/autocomplete-popover/autocomplete-popover.component.tsx +1 -4
- package/src/components/button/button.component.tsx +2 -0
- package/src/components/button/button.styles.ts +10 -1
- package/src/components/button/button.types.ts +5 -0
- package/src/components/button-dropdown/button-dropdown.component.tsx +2 -1
- package/src/components/button-dropdown/button-dropdown.types.ts +5 -0
- package/src/components/button-group/button-group.component.tsx +1 -0
- package/src/components/checkbox-group/checkbox-group.component.tsx +1 -0
- package/src/components/header/header.component.tsx +3 -1
- package/src/components/header/header.types.ts +2 -1
- package/src/components/modal/components/modal-backdrop/modal-backdrop.component.tsx +5 -2
- package/src/components/modal/components/modal-backdrop/modal-backdrop.styles.ts +35 -15
- package/src/components/modal/components/modal-backdrop/modal-backdrop.types.ts +5 -0
- package/src/components/modal/components/modal-dialog/components/modal-dialog-body/modal-dialog-body.component.tsx +7 -3
- package/src/components/modal/components/modal-dialog/components/modal-dialog-body/modal-dialog-body.styles.ts +45 -3
- package/src/components/modal/components/modal-dialog/components/modal-dialog-footer/modal-dialog-footer.component.tsx +8 -3
- package/src/components/modal/components/modal-dialog/components/modal-dialog-footer/modal-dialog-footer.styles.ts +22 -6
- package/src/components/modal/components/modal-dialog/modal-dialog.component.tsx +51 -5
- package/src/components/modal/components/modal-dialog/modal-dialog.styles.ts +28 -5
- package/src/components/modal/components/modal-dialog/modal-dialog.types.ts +33 -0
- package/src/components/modal/modal.component.tsx +4 -2
- package/src/components/popover/components/panel/panel.styles.ts +1 -1
- package/src/components/radio-group/radio-group.component.tsx +1 -0
- package/src/components/selector/components/selector-checkbox-group/selector-checkbox-group.component.tsx +1 -0
- package/src/components/selector/components/selector-radio-group/selector-radio-group.component.tsx +1 -0
|
@@ -37,10 +37,7 @@ export function AutocompletePopover(props: AutocompletePopoverProps) {
|
|
|
37
37
|
{...popoverProps}
|
|
38
38
|
style={{ ...popoverProps.style, width: width ? `${width}px` : undefined }}
|
|
39
39
|
ref={popoverRef}
|
|
40
|
-
className={clsx(
|
|
41
|
-
'z-10 mt-1 max-h-[400px] overflow-auto rounded border border-border bg-white shadow-lg',
|
|
42
|
-
className,
|
|
43
|
-
)}
|
|
40
|
+
className={clsx('z-10 mt-1 max-h-[400px] rounded border border-border bg-white shadow-lg', className)}
|
|
44
41
|
>
|
|
45
42
|
{!isNonModal && <DismissButton onDismiss={() => state.close()} />}
|
|
46
43
|
{children}
|
|
@@ -22,6 +22,7 @@ function BaseButton(
|
|
|
22
22
|
iconColor,
|
|
23
23
|
iconSize,
|
|
24
24
|
children,
|
|
25
|
+
removeLinkPadding,
|
|
25
26
|
...props
|
|
26
27
|
}: ButtonProps,
|
|
27
28
|
ref: Ref<ButtonRef>,
|
|
@@ -36,6 +37,7 @@ function BaseButton(
|
|
|
36
37
|
justify,
|
|
37
38
|
isFocusVisible,
|
|
38
39
|
hasChildren: !!children,
|
|
40
|
+
removeLinkPadding,
|
|
39
41
|
});
|
|
40
42
|
|
|
41
43
|
return (
|
|
@@ -33,7 +33,7 @@ export const styles = tv(
|
|
|
33
33
|
},
|
|
34
34
|
hero: { base: 'border border-hero bg-hero text-white hover:bg-hero-70 active:bg-hero-50' },
|
|
35
35
|
faint: { base: 'border border-borderDark bg-light text-muted hover:bg-white active:bg-white' },
|
|
36
|
-
link: { base: '
|
|
36
|
+
link: { base: 'text-link underline' },
|
|
37
37
|
unstyled: { base: 'p-0 text-left' },
|
|
38
38
|
},
|
|
39
39
|
soft: {
|
|
@@ -53,6 +53,9 @@ export const styles = tv(
|
|
|
53
53
|
true: { base: 'focus-outline' },
|
|
54
54
|
false: { base: 'outline-none' },
|
|
55
55
|
},
|
|
56
|
+
removeLinkPadding: {
|
|
57
|
+
true: '',
|
|
58
|
+
},
|
|
56
59
|
},
|
|
57
60
|
compoundSlots: [
|
|
58
61
|
{
|
|
@@ -73,6 +76,12 @@ export const styles = tv(
|
|
|
73
76
|
soft: true,
|
|
74
77
|
className: 'hover:bg-light active:bg-light',
|
|
75
78
|
},
|
|
79
|
+
{
|
|
80
|
+
slots: ['base'],
|
|
81
|
+
look: 'link',
|
|
82
|
+
removeLinkPadding: true,
|
|
83
|
+
className: 'px-0',
|
|
84
|
+
},
|
|
76
85
|
{
|
|
77
86
|
slots: ['iconBefore'],
|
|
78
87
|
hasChildren: true,
|
|
@@ -54,6 +54,11 @@ export type ButtonProps = {
|
|
|
54
54
|
* @default hero
|
|
55
55
|
*/
|
|
56
56
|
look?: Variants['look'];
|
|
57
|
+
/**
|
|
58
|
+
* Removes horizontal padding from the 'link' look button
|
|
59
|
+
* @default false
|
|
60
|
+
*/
|
|
61
|
+
removeLinkPadding?: boolean;
|
|
57
62
|
/**
|
|
58
63
|
* Size of the button
|
|
59
64
|
* @default medium
|
|
@@ -25,6 +25,7 @@ export function ButtonDropdown({
|
|
|
25
25
|
soft = false,
|
|
26
26
|
block = false,
|
|
27
27
|
dropDownIcon: Icon = DropDownIcon,
|
|
28
|
+
placement = 'bottom start',
|
|
28
29
|
shouldFlip,
|
|
29
30
|
}: ButtonDropdownProps) {
|
|
30
31
|
const ref = useRef<HTMLButtonElement & HTMLAnchorElement & HTMLSpanElement & HTMLDivElement>(null);
|
|
@@ -73,7 +74,7 @@ export function ButtonDropdown({
|
|
|
73
74
|
{state.isOpen && (
|
|
74
75
|
<ButtonDropdownPanel
|
|
75
76
|
className={styles.panel({ className })}
|
|
76
|
-
placement=
|
|
77
|
+
placement={placement}
|
|
77
78
|
triggerRef={ref}
|
|
78
79
|
state={state}
|
|
79
80
|
block={block}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ButtonHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { Placement } from 'react-aria';
|
|
2
3
|
import { type VariantProps } from 'tailwind-variants';
|
|
3
4
|
|
|
4
5
|
import { ButtonProps } from '../button/index.js';
|
|
@@ -26,6 +27,10 @@ export type ButtonDropdownProps = {
|
|
|
26
27
|
* State of whether the Popover is open
|
|
27
28
|
*/
|
|
28
29
|
open?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* placement of the popover
|
|
32
|
+
*/
|
|
33
|
+
placement?: Placement;
|
|
29
34
|
/**
|
|
30
35
|
* Soft look button
|
|
31
36
|
*/
|
|
@@ -17,6 +17,7 @@ export const ButtonGroupContext = createContext<ButtonGroupContextState>({
|
|
|
17
17
|
state: {
|
|
18
18
|
// TODO: Remove deprecated name prop once React Aria removes it from RadioGroupState
|
|
19
19
|
name: '',
|
|
20
|
+
defaultSelectedValue: null,
|
|
20
21
|
isDisabled: false,
|
|
21
22
|
isReadOnly: false,
|
|
22
23
|
isRequired: false,
|
|
@@ -105,6 +105,8 @@ export function Header({
|
|
|
105
105
|
</>
|
|
106
106
|
);
|
|
107
107
|
|
|
108
|
+
const defaultAssistiveText = leftIcon === 'arrow' ? 'Back' : 'Menu';
|
|
109
|
+
|
|
108
110
|
return (
|
|
109
111
|
<header className={styles.base({ className })} {...props}>
|
|
110
112
|
<div className={styles.inner()} style={{ maxWidth: fixed ? fixedMaxWidth : undefined }}>
|
|
@@ -116,7 +118,7 @@ export function Header({
|
|
|
116
118
|
iconAfter={ButtonIcon}
|
|
117
119
|
iconSize={leftIcon === 'arrow' ? 'medium' : 'small'}
|
|
118
120
|
onClick={leftOnClick}
|
|
119
|
-
aria-label={leftAssistiveText}
|
|
121
|
+
aria-label={leftAssistiveText ?? defaultAssistiveText}
|
|
120
122
|
className={styles.leftButton()}
|
|
121
123
|
iconColor="text"
|
|
122
124
|
/>
|
|
@@ -8,11 +8,14 @@ import { type ModalBackdropProps } from './modal-backdrop.types.js';
|
|
|
8
8
|
/**
|
|
9
9
|
* @private
|
|
10
10
|
*/
|
|
11
|
-
export function ModalBackdrop({ zIndex = 100, portalContainer, size, ...props }: ModalBackdropProps) {
|
|
11
|
+
export function ModalBackdrop({ zIndex = 100, portalContainer, size, compact, ...props }: ModalBackdropProps) {
|
|
12
12
|
const { children, state, className } = props;
|
|
13
13
|
|
|
14
14
|
const ref = useRef(null);
|
|
15
|
-
const styles = backdropStyles({
|
|
15
|
+
const styles = backdropStyles({
|
|
16
|
+
size,
|
|
17
|
+
compact: compact && (size === 'md' || size === 'lg'),
|
|
18
|
+
});
|
|
16
19
|
|
|
17
20
|
const { modalProps, underlayProps } = useModalOverlay(props, state, ref);
|
|
18
21
|
|
|
@@ -1,21 +1,41 @@
|
|
|
1
1
|
import { tv } from 'tailwind-variants';
|
|
2
2
|
|
|
3
|
-
export const styles = tv(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
variants: {
|
|
9
|
-
fullscreen: {
|
|
10
|
-
true: {
|
|
11
|
-
modal: 'top-0 flex flex-1 flex-col',
|
|
12
|
-
base: 'flex flex-col p-0',
|
|
13
|
-
},
|
|
3
|
+
export const styles = tv(
|
|
4
|
+
{
|
|
5
|
+
slots: {
|
|
6
|
+
base: 'fixed inset-0 flex animate-fadeIn justify-center bg-black/50 px-4',
|
|
7
|
+
modal: 'relative top-[5vh] z-10 size-fit max-w-full animate-fadeInDown',
|
|
14
8
|
},
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
variants: {
|
|
10
|
+
size: {
|
|
11
|
+
fluid: { base: 'px-4' },
|
|
12
|
+
full: {
|
|
13
|
+
modal: '!top-0 flex w-full flex-1 flex-col p-0',
|
|
14
|
+
base: 'flex flex-col p-0',
|
|
15
|
+
},
|
|
16
|
+
lg: '',
|
|
17
|
+
md: '',
|
|
18
|
+
sm: '',
|
|
19
|
+
},
|
|
20
|
+
compact: {
|
|
21
|
+
true: '',
|
|
22
|
+
false: '',
|
|
18
23
|
},
|
|
19
24
|
},
|
|
25
|
+
compoundSlots: [
|
|
26
|
+
{
|
|
27
|
+
slots: ['base'],
|
|
28
|
+
size: ['sm', 'md', 'lg', 'fluid'],
|
|
29
|
+
compact: false,
|
|
30
|
+
className: 'overflow-y-auto',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
slots: ['modal'],
|
|
34
|
+
size: ['sm', 'md', 'lg', 'fluid'],
|
|
35
|
+
compact: false,
|
|
36
|
+
className: 'pb-[5vh]',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
20
39
|
},
|
|
21
|
-
}
|
|
40
|
+
{ responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
|
|
41
|
+
);
|
|
@@ -12,6 +12,11 @@ export type ModalBackdropProps = {
|
|
|
12
12
|
* Clasname
|
|
13
13
|
*/
|
|
14
14
|
className?: string;
|
|
15
|
+
/**
|
|
16
|
+
* For medium and large sizes.
|
|
17
|
+
* Keeps entire modal in view by adding internal scrolling.
|
|
18
|
+
*/
|
|
19
|
+
compact?: boolean;
|
|
15
20
|
/**
|
|
16
21
|
* Element where backdrop will be placed
|
|
17
22
|
*/
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
1
3
|
import React from 'react';
|
|
2
4
|
|
|
3
5
|
import { useModalDialogContext } from '../../modal-dialog.component.js';
|
|
@@ -6,10 +8,12 @@ import { styles as modalBodyStyles } from './modal-dialog-body.styles.js';
|
|
|
6
8
|
import { type ModalDialogBodyProps } from './modal-dialog-body.types.js';
|
|
7
9
|
|
|
8
10
|
export function ModalDialogBody({ className, children, ...props }: ModalDialogBodyProps) {
|
|
9
|
-
const { size } = useModalDialogContext();
|
|
10
|
-
|
|
11
|
+
const { size, scrollingRef, canScroll, compact, footerPresent, scrollAtBottom } = useModalDialogContext();
|
|
12
|
+
|
|
13
|
+
const styles = modalBodyStyles({ size, canScroll, scrollAtBottom, compact, footerPresent });
|
|
14
|
+
|
|
11
15
|
return (
|
|
12
|
-
<div className={styles.base({ className })} {...props}>
|
|
16
|
+
<div className={styles.base({ className })} ref={scrollingRef} {...props}>
|
|
13
17
|
{children}
|
|
14
18
|
</div>
|
|
15
19
|
);
|
|
@@ -2,16 +2,58 @@ import { tv } from 'tailwind-variants';
|
|
|
2
2
|
|
|
3
3
|
export const styles = tv(
|
|
4
4
|
{
|
|
5
|
-
slots: { base: 'flex-1
|
|
5
|
+
slots: { base: 'flex-1 transition-shadow delay-0 duration-200 ease-[ease]' },
|
|
6
6
|
variants: {
|
|
7
7
|
size: {
|
|
8
8
|
full: { base: 'px-4 py-3' },
|
|
9
9
|
lg: { base: 'px-12 pb-12' },
|
|
10
10
|
md: { base: 'px-7 pb-7' },
|
|
11
|
-
sm: { base: 'px-5 pb-
|
|
12
|
-
fluid: { base: 'px-5 pb-
|
|
11
|
+
sm: { base: 'px-5 pb-5' },
|
|
12
|
+
fluid: { base: 'px-5 pb-5' },
|
|
13
|
+
},
|
|
14
|
+
canScroll: {
|
|
15
|
+
true: {
|
|
16
|
+
base: 'shadow-[0px_-4px_5px_-2px_inset] shadow-black/30',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
scrollAtBottom: {
|
|
20
|
+
true: { base: 'shadow-[0_0_0_0_inset]' },
|
|
21
|
+
},
|
|
22
|
+
compact: {
|
|
23
|
+
true: '',
|
|
24
|
+
false: '',
|
|
25
|
+
},
|
|
26
|
+
footerPresent: {
|
|
27
|
+
true: '',
|
|
28
|
+
false: '',
|
|
13
29
|
},
|
|
14
30
|
},
|
|
31
|
+
compoundSlots: [
|
|
32
|
+
{
|
|
33
|
+
slots: ['base'],
|
|
34
|
+
size: ['lg'],
|
|
35
|
+
compact: true,
|
|
36
|
+
className: 'overflow-y-auto px-5 pb-3',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
slots: ['base'],
|
|
40
|
+
size: ['md'],
|
|
41
|
+
compact: true,
|
|
42
|
+
className: 'overflow-y-auto px-5 pb-2',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
slots: ['base'],
|
|
46
|
+
size: ['md'],
|
|
47
|
+
footerPresent: true,
|
|
48
|
+
className: 'pb-5',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
slots: ['base'],
|
|
52
|
+
size: ['lg'],
|
|
53
|
+
footerPresent: true,
|
|
54
|
+
className: 'pb-6',
|
|
55
|
+
},
|
|
56
|
+
],
|
|
15
57
|
},
|
|
16
58
|
{ responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
|
|
17
59
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import React, { useEffect } from 'react';
|
|
4
4
|
|
|
5
5
|
import { Button } from '../../../../../button/index.js';
|
|
6
6
|
import { useModalDialogContext } from '../../modal-dialog.component.js';
|
|
@@ -16,8 +16,13 @@ export function ModalDialogFooter({
|
|
|
16
16
|
secondaryOnClick,
|
|
17
17
|
...props
|
|
18
18
|
}: ModalDialogFooterProps) {
|
|
19
|
-
const { size } = useModalDialogContext();
|
|
20
|
-
const styles = modalFooterStyles({ size });
|
|
19
|
+
const { size, compact, setFooterPresent } = useModalDialogContext();
|
|
20
|
+
const styles = modalFooterStyles({ size, compact });
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setFooterPresent?.(true);
|
|
24
|
+
}, [setFooterPresent]);
|
|
25
|
+
|
|
21
26
|
return (
|
|
22
27
|
<div className={styles.base({ className })} {...props}>
|
|
23
28
|
<Button look="primary" size="large" className={styles.primaryBtn()} onClick={primaryOnClick}>
|
|
@@ -2,16 +2,32 @@ import { tv } from 'tailwind-variants';
|
|
|
2
2
|
|
|
3
3
|
export const styles = tv(
|
|
4
4
|
{
|
|
5
|
-
slots: {
|
|
5
|
+
slots: {
|
|
6
|
+
base: 'flex gap-1 rounded-b-[3px] bg-white',
|
|
7
|
+
primaryBtn: '',
|
|
8
|
+
secondaryBtn: '',
|
|
9
|
+
},
|
|
6
10
|
variants: {
|
|
7
11
|
size: {
|
|
8
|
-
full: { base: 'px-4
|
|
9
|
-
lg: { base: '
|
|
10
|
-
md: { base: '
|
|
11
|
-
sm: { base: '
|
|
12
|
-
fluid: { base: '
|
|
12
|
+
full: { base: 'px-4 pb-3' },
|
|
13
|
+
lg: { base: 'px-12 pb-12' },
|
|
14
|
+
md: { base: 'px-7 pb-7' },
|
|
15
|
+
sm: { base: 'flex-col px-5 pb-5 ' },
|
|
16
|
+
fluid: { base: 'px-5 pb-5 max-md:flex-col' },
|
|
17
|
+
},
|
|
18
|
+
compact: {
|
|
19
|
+
true: '',
|
|
20
|
+
false: '',
|
|
13
21
|
},
|
|
14
22
|
},
|
|
23
|
+
compoundSlots: [
|
|
24
|
+
{
|
|
25
|
+
slots: ['base'],
|
|
26
|
+
size: ['lg', 'md'],
|
|
27
|
+
compact: true,
|
|
28
|
+
className: 'min-h-[90px] px-5 pb-5 pt-3',
|
|
29
|
+
},
|
|
30
|
+
],
|
|
15
31
|
},
|
|
16
32
|
{ responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
|
|
17
33
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { createContext, useContext, useRef } from 'react';
|
|
3
|
+
import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
|
|
4
4
|
import { useDialog, useFocusRing } from 'react-aria';
|
|
5
5
|
|
|
6
6
|
import { CloseIcon } from '../../../../components/icon/index.js';
|
|
@@ -12,19 +12,63 @@ import { ModalDialogContextValue, type ModalDialogProps } from './modal-dialog.t
|
|
|
12
12
|
|
|
13
13
|
const ModalDialogContext = createContext<ModalDialogContextValue>({ size: 'md' });
|
|
14
14
|
|
|
15
|
+
const SCROLL_BUFFER = 10;
|
|
16
|
+
|
|
15
17
|
export const useModalDialogContext = () => useContext(ModalDialogContext);
|
|
18
|
+
|
|
16
19
|
/**
|
|
17
20
|
* @private
|
|
18
21
|
*/
|
|
19
|
-
export function ModalDialog({ className, body, onClose, size
|
|
22
|
+
export function ModalDialog({ className, body, onClose, size, compact, ...props }: ModalDialogProps) {
|
|
20
23
|
const { children } = props;
|
|
21
24
|
const { isFocusVisible, focusProps } = useFocusRing();
|
|
22
|
-
const
|
|
25
|
+
const [scrolled, setScrolled] = useState(false);
|
|
26
|
+
const [scrollAtBottom, setScrollAtBottom] = useState(false);
|
|
27
|
+
const styles = dialogStyles({ size, isFocusVisible, compact, scrolled });
|
|
28
|
+
const [canScroll, setCanScroll] = useState(false);
|
|
29
|
+
const [footerPresent, setFooterPresent] = useState<boolean>(false);
|
|
23
30
|
|
|
24
31
|
const ref = useRef(null);
|
|
32
|
+
const bodyRef = useRef<HTMLDivElement>(null);
|
|
25
33
|
|
|
26
34
|
const { dialogProps, titleProps } = useDialog(props, ref);
|
|
27
35
|
|
|
36
|
+
const handleScroll = useCallback(() => {
|
|
37
|
+
if (bodyRef?.current) {
|
|
38
|
+
const { scrollTop, scrollHeight, clientHeight } = bodyRef.current;
|
|
39
|
+
setScrolled(scrollTop > SCROLL_BUFFER);
|
|
40
|
+
setScrollAtBottom(scrollTop + clientHeight >= scrollHeight - SCROLL_BUFFER);
|
|
41
|
+
}
|
|
42
|
+
}, [bodyRef]);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const bodyElement = bodyRef.current;
|
|
46
|
+
|
|
47
|
+
if (!bodyElement) {
|
|
48
|
+
setCanScroll(false);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
bodyElement.addEventListener('scroll', handleScroll);
|
|
53
|
+
|
|
54
|
+
const updateCanScroll = () => {
|
|
55
|
+
setCanScroll(bodyElement.scrollHeight > bodyElement.clientHeight);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
updateCanScroll();
|
|
59
|
+
|
|
60
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
61
|
+
updateCanScroll();
|
|
62
|
+
});
|
|
63
|
+
resizeObserver.observe(bodyElement);
|
|
64
|
+
|
|
65
|
+
return () => {
|
|
66
|
+
resizeObserver.disconnect();
|
|
67
|
+
bodyElement.removeEventListener('scroll', handleScroll);
|
|
68
|
+
};
|
|
69
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
|
+
}, [bodyRef]);
|
|
71
|
+
|
|
28
72
|
return (
|
|
29
73
|
<div {...dialogProps} ref={ref} className={styles.base({ className })}>
|
|
30
74
|
{onClose && (
|
|
@@ -37,12 +81,14 @@ export function ModalDialog({ className, body, onClose, size = 'md', ...props }:
|
|
|
37
81
|
{props.title}
|
|
38
82
|
</h3>
|
|
39
83
|
)}
|
|
40
|
-
|
|
84
|
+
|
|
85
|
+
<ModalDialogContext.Provider
|
|
86
|
+
value={{ size, scrollingRef: bodyRef, canScroll, compact, footerPresent, setFooterPresent, scrollAtBottom }}
|
|
87
|
+
>
|
|
41
88
|
{body ? <ModalDialogBody>{children}</ModalDialogBody> : children}
|
|
42
89
|
</ModalDialogContext.Provider>
|
|
43
90
|
</div>
|
|
44
91
|
);
|
|
45
92
|
}
|
|
46
|
-
|
|
47
93
|
ModalDialog.Body = ModalDialogBody;
|
|
48
94
|
ModalDialog.Footer = ModalDialogFooter;
|
|
@@ -3,14 +3,14 @@ import { tv } from 'tailwind-variants';
|
|
|
3
3
|
export const styles = tv(
|
|
4
4
|
{
|
|
5
5
|
slots: {
|
|
6
|
-
base: 'relative
|
|
7
|
-
title: 'typography-body-7 pb-4 pt-9 font-bold text-text',
|
|
6
|
+
base: 'relative flex max-h-full max-w-full flex-col rounded bg-white text-text outline-none',
|
|
7
|
+
title: 'typography-body-7 pb-4 pt-9 font-bold text-text transition-shadow delay-0 duration-200 ease-[ease]',
|
|
8
8
|
close: 'absolute right-0 top-0 block p-3',
|
|
9
9
|
},
|
|
10
10
|
variants: {
|
|
11
11
|
size: {
|
|
12
12
|
full: {
|
|
13
|
-
base: 'max-h-screen w-full flex-1',
|
|
13
|
+
base: 'h-screen max-h-screen w-full flex-1 rounded-none',
|
|
14
14
|
close: 'p-2',
|
|
15
15
|
title: 'px-4 py-3',
|
|
16
16
|
},
|
|
@@ -20,14 +20,14 @@ export const styles = tv(
|
|
|
20
20
|
},
|
|
21
21
|
md: {
|
|
22
22
|
base: 'w-[37.5rem]',
|
|
23
|
-
title: 'px-7
|
|
23
|
+
title: 'px-7',
|
|
24
24
|
},
|
|
25
25
|
sm: {
|
|
26
26
|
base: 'w-[25rem]',
|
|
27
27
|
title: 'px-5',
|
|
28
28
|
},
|
|
29
29
|
fluid: {
|
|
30
|
-
base: 'w-
|
|
30
|
+
base: 'w-screen',
|
|
31
31
|
title: 'px-5',
|
|
32
32
|
},
|
|
33
33
|
},
|
|
@@ -35,7 +35,30 @@ export const styles = tv(
|
|
|
35
35
|
true: { close: '!outline-offset-[-3px] focus-outline' },
|
|
36
36
|
false: { close: 'outline-none' },
|
|
37
37
|
},
|
|
38
|
+
compact: {
|
|
39
|
+
true: '',
|
|
40
|
+
false: '',
|
|
41
|
+
},
|
|
42
|
+
scrolled: {
|
|
43
|
+
true: {
|
|
44
|
+
title: 'shadow-[0px_2px_5px_0px] shadow-black/30',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
38
47
|
},
|
|
48
|
+
compoundSlots: [
|
|
49
|
+
{
|
|
50
|
+
slots: ['base'],
|
|
51
|
+
size: ['md', 'lg'],
|
|
52
|
+
compact: true,
|
|
53
|
+
className: 'max-h-[80vh] overflow-hidden',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
slots: ['title'],
|
|
57
|
+
size: ['lg', 'md'],
|
|
58
|
+
compact: true,
|
|
59
|
+
className: 'min-h-[90px] px-5 pb-4 pt-6',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
39
62
|
},
|
|
40
63
|
{ responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
|
|
41
64
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
1
2
|
import { type AriaDialogProps } from 'react-aria';
|
|
2
3
|
import { type VariantProps } from 'tailwind-variants';
|
|
3
4
|
|
|
@@ -18,6 +19,12 @@ export type ModalDialogProps = {
|
|
|
18
19
|
* Additional className for Dialog
|
|
19
20
|
*/
|
|
20
21
|
className?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Alternate styling for **medium** and **large** sizes. Other sizes will continue to scroll within the backdrop.
|
|
24
|
+
*
|
|
25
|
+
* Keeps entire modal in view by adding internal scrolling and reducing internal padding.
|
|
26
|
+
*/
|
|
27
|
+
compact?: boolean;
|
|
21
28
|
/**
|
|
22
29
|
* Full screen
|
|
23
30
|
*/
|
|
@@ -37,8 +44,34 @@ export type ModalDialogProps = {
|
|
|
37
44
|
} & AriaDialogProps;
|
|
38
45
|
|
|
39
46
|
export type ModalDialogContextValue = {
|
|
47
|
+
/**
|
|
48
|
+
* Whether container can scroll
|
|
49
|
+
*/
|
|
50
|
+
canScroll?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Alternate styling for **medium** and **large** sizes. Other sizes will continue to scroll within the backdrop.
|
|
53
|
+
*
|
|
54
|
+
* Keeps entire modal in view by adding internal scrolling and reducing internal padding.
|
|
55
|
+
*/
|
|
56
|
+
compact?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether footer is present for styling
|
|
59
|
+
*/
|
|
60
|
+
footerPresent?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Sets whether footer is present for styling
|
|
63
|
+
*/
|
|
64
|
+
setFooterPresent?: (present: boolean) => void;
|
|
40
65
|
/**
|
|
41
66
|
* Size of dialog
|
|
42
67
|
*/
|
|
43
68
|
size?: Variants['size'];
|
|
69
|
+
/**
|
|
70
|
+
* Whether scroll is at bottom of the scrollable area
|
|
71
|
+
*/
|
|
72
|
+
scrollAtBottom?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Ref to use for scrolling animations
|
|
75
|
+
*/
|
|
76
|
+
scrollingRef?: RefObject<HTMLDivElement>;
|
|
44
77
|
};
|
|
@@ -11,13 +11,14 @@ export function Modal({
|
|
|
11
11
|
title,
|
|
12
12
|
role,
|
|
13
13
|
body,
|
|
14
|
-
size,
|
|
14
|
+
size = 'md',
|
|
15
15
|
className,
|
|
16
16
|
fullscreen,
|
|
17
|
+
compact = false,
|
|
17
18
|
...props
|
|
18
19
|
}: ModalProps) {
|
|
19
20
|
return (
|
|
20
|
-
<ModalBackdrop size={size} className={backdropStyle} {...props}>
|
|
21
|
+
<ModalBackdrop size={size} className={backdropStyle} compact={compact} {...props}>
|
|
21
22
|
<ModalDialog
|
|
22
23
|
fullscreen={fullscreen}
|
|
23
24
|
onClose={props.isDismissable ? () => props.state.close() : undefined}
|
|
@@ -26,6 +27,7 @@ export function Modal({
|
|
|
26
27
|
body={body}
|
|
27
28
|
size={size}
|
|
28
29
|
className={className}
|
|
30
|
+
compact={compact}
|
|
29
31
|
>
|
|
30
32
|
{children}
|
|
31
33
|
</ModalDialog>
|
|
@@ -10,7 +10,7 @@ export const styles = tv(
|
|
|
10
10
|
after:left-[1.5px] after:top-0 after:size-0 after:border-x-[6.5px] after:border-t-[11px] after:border-x-[transparent] after:border-t-white
|
|
11
11
|
`,
|
|
12
12
|
closeBtn: 'absolute right-1 top-1 h-3 p-0 hover:opacity-80',
|
|
13
|
-
content: 'w-[
|
|
13
|
+
content: 'w-[18.75rem] rounded-[3px] bg-white py-4 pl-3 pr-5',
|
|
14
14
|
heading: 'typography-body-9 mb-2 font-medium text-text focus-visible:focus-outline',
|
|
15
15
|
body: 'typography-body-10 text-text focus-visible:focus-outline',
|
|
16
16
|
},
|
package/src/components/selector/components/selector-radio-group/selector-radio-group.component.tsx
CHANGED
|
@@ -15,6 +15,7 @@ export const SelectorRadioGroupContext = createContext<SelectorRadioGroupContext
|
|
|
15
15
|
state: {
|
|
16
16
|
// TODO: Remove deprecated name prop once React Aria removes it from RadioGroupState
|
|
17
17
|
name: '',
|
|
18
|
+
defaultSelectedValue: null,
|
|
18
19
|
isDisabled: false,
|
|
19
20
|
isReadOnly: false,
|
|
20
21
|
isRequired: false,
|