athameui 0.0.5 → 0.0.6-c
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/components/Avatar/Avatar.tsx +51 -0
- package/components/Avatar/index.ts +1 -0
- package/components/Button/Button.tsx +6 -18
- package/components/Button/button.variants.ts +0 -9
- package/components/Button/index.ts +0 -1
- package/components/{Button → ButtonGroup}/ButtonGroup.tsx +3 -13
- package/components/ButtonGroup/ButtonGroupProps.ts +12 -0
- package/components/ButtonGroup/buttonGroup.variants.ts +8 -0
- package/components/ButtonGroup/index.ts +1 -0
- package/components/FormElements/FormInput.tsx +7 -1
- package/components/List/List.tsx +12 -32
- package/components/Modal/Modal.tsx +206 -0
- package/components/Modal/index.ts +1 -0
- package/components/Modals/Modal.tsx +206 -0
- package/components/index.ts +7 -6
- package/dist/athameui.cjs +393 -235
- package/dist/athameui.cjs.map +1 -1
- package/dist/athameui.mjs +393 -237
- package/dist/athameui.mjs.map +1 -1
- package/dist/components/Avatar/Avatar.d.ts +17 -0
- package/dist/components/Avatar/Avatar.d.ts.map +1 -0
- package/dist/components/Avatar/index.d.ts +2 -0
- package/dist/components/Avatar/index.d.ts.map +1 -0
- package/dist/components/Button/Button.d.ts +2 -4
- package/dist/components/Button/Button.d.ts.map +1 -1
- package/dist/components/Button/button.variants.d.ts +0 -7
- package/dist/components/Button/button.variants.d.ts.map +1 -1
- package/dist/components/Button/index.d.ts +0 -1
- package/dist/components/Button/index.d.ts.map +1 -1
- package/dist/components/ButtonGroup/ButtonGroup.d.ts +3 -0
- package/dist/components/ButtonGroup/ButtonGroup.d.ts.map +1 -0
- package/dist/components/{Button/ButtonGroup.d.ts → ButtonGroup/ButtonGroupProps.d.ts} +2 -4
- package/dist/components/ButtonGroup/ButtonGroupProps.d.ts.map +1 -0
- package/dist/components/ButtonGroup/buttonGroup.variants.d.ts +8 -0
- package/dist/components/ButtonGroup/buttonGroup.variants.d.ts.map +1 -0
- package/dist/components/ButtonGroup/index.d.ts +2 -0
- package/dist/components/ButtonGroup/index.d.ts.map +1 -0
- package/dist/components/FormElements/FormInput.d.ts +2 -1
- package/dist/components/FormElements/FormInput.d.ts.map +1 -1
- package/dist/components/List/List.d.ts +8 -8
- package/dist/components/List/List.d.ts.map +1 -1
- package/dist/components/Modal/Modal.d.ts +37 -0
- package/dist/components/Modal/Modal.d.ts.map +1 -0
- package/dist/components/Modal/index.d.ts +2 -0
- package/dist/components/Modal/index.d.ts.map +1 -0
- package/dist/components/Modals/Modal.d.ts +37 -0
- package/dist/components/Modals/Modal.d.ts.map +1 -0
- package/dist/components/index.d.ts +7 -4
- package/dist/components/index.d.ts.map +1 -1
- package/dist/styles/components/avatar.css +22 -0
- package/dist/styles/components/button-group.css +16 -0
- package/dist/styles/components/button.css +0 -14
- package/dist/styles/components/form-elements.css +1 -1
- package/dist/styles/components/modals.css +60 -0
- package/dist/styles/theme.css +7 -4
- package/package.json +1 -1
- package/styles/components/avatar.css +22 -0
- package/styles/components/button-group.css +16 -0
- package/styles/components/button.css +0 -14
- package/styles/components/form-elements.css +1 -1
- package/styles/components/modals.css +60 -0
- package/styles/theme.css +7 -4
- package/dist/components/Button/ButtonGroup.d.ts.map +0 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { CSSObject } from "@emotion/react";
|
|
3
|
+
import { cx } from "../../utils/cx";
|
|
4
|
+
|
|
5
|
+
type AvatarProps = {
|
|
6
|
+
displayName: string;
|
|
7
|
+
imageUrl?: string;
|
|
8
|
+
className?: {
|
|
9
|
+
avatar?: string;
|
|
10
|
+
initials?: string;
|
|
11
|
+
// image?: string;
|
|
12
|
+
};
|
|
13
|
+
size?: number;
|
|
14
|
+
sx?: {
|
|
15
|
+
avatar?: CSSObject;
|
|
16
|
+
initials?: CSSObject;
|
|
17
|
+
// image?: CSSObject;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// TODO: Add images later
|
|
22
|
+
export const Avatar = ({
|
|
23
|
+
className,
|
|
24
|
+
displayName = "Display Name",
|
|
25
|
+
sx,
|
|
26
|
+
size = 40,
|
|
27
|
+
}: AvatarProps) => {
|
|
28
|
+
const initials = displayName.split(" ").map((n) => n[0].toUpperCase());
|
|
29
|
+
const avatarClasses = cx(
|
|
30
|
+
".ath-avatar",
|
|
31
|
+
`width-${size}px`,
|
|
32
|
+
`height-${size}px`,
|
|
33
|
+
`line-height-${size}px`,
|
|
34
|
+
className?.avatar,
|
|
35
|
+
);
|
|
36
|
+
const initialsClasses = cx(
|
|
37
|
+
".ath-avatar-initials",
|
|
38
|
+
`font-size-${size / 2}px`,
|
|
39
|
+
className?.initials,
|
|
40
|
+
);
|
|
41
|
+
// const imageClasses = cx(".ath-avatar-image", className?.image);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div css={sx?.avatar} className={avatarClasses}>
|
|
45
|
+
<div css={sx?.initials} className={initialsClasses}>
|
|
46
|
+
{initials[0]}
|
|
47
|
+
{initials[1]}
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Avatar';
|
|
@@ -14,14 +14,14 @@ import {
|
|
|
14
14
|
import { CSSObject } from "@emotion/react";
|
|
15
15
|
|
|
16
16
|
export type ButtonProps = {
|
|
17
|
-
children?:
|
|
17
|
+
children?: React.ReactNode | string;
|
|
18
18
|
className?: {
|
|
19
19
|
button?: string | false | null | undefined;
|
|
20
20
|
icon?: string | false | null | undefined;
|
|
21
21
|
};
|
|
22
22
|
dark?: boolean;
|
|
23
23
|
disabled?: boolean;
|
|
24
|
-
icon?:
|
|
24
|
+
icon?: ReactElement | string;
|
|
25
25
|
size?: ButtonSize;
|
|
26
26
|
sx?: CSSObject | { button?: CSSObject; icon?: CSSObject };
|
|
27
27
|
title?: string;
|
|
@@ -43,7 +43,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
43
43
|
className,
|
|
44
44
|
dark = false,
|
|
45
45
|
disabled = false,
|
|
46
|
-
icon
|
|
46
|
+
icon,
|
|
47
47
|
size = "medium",
|
|
48
48
|
sx,
|
|
49
49
|
tabIndex,
|
|
@@ -59,7 +59,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
59
59
|
},
|
|
60
60
|
ref,
|
|
61
61
|
) => {
|
|
62
|
-
const isIconOnly =
|
|
62
|
+
const isIconOnly = icon && !children;
|
|
63
63
|
|
|
64
64
|
const sharedClasses = cx(
|
|
65
65
|
"ath-button",
|
|
@@ -77,14 +77,6 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
77
77
|
: className,
|
|
78
78
|
);
|
|
79
79
|
|
|
80
|
-
const iconClasses = cx(
|
|
81
|
-
sharedClasses,
|
|
82
|
-
"ath-button-icon",
|
|
83
|
-
typeof className === "object" && className !== null
|
|
84
|
-
? className.icon
|
|
85
|
-
: undefined,
|
|
86
|
-
);
|
|
87
|
-
|
|
88
80
|
const onClickHandler: MouseEventHandler<HTMLButtonElement> = (e) => {
|
|
89
81
|
onClick?.(e);
|
|
90
82
|
};
|
|
@@ -115,13 +107,9 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
115
107
|
onBlur={onBlurHandler}
|
|
116
108
|
{...rest}
|
|
117
109
|
>
|
|
118
|
-
{
|
|
110
|
+
{icon ? (
|
|
119
111
|
<>
|
|
120
|
-
|
|
121
|
-
className={iconClasses}
|
|
122
|
-
css={sx?.icon ? sx.icon : undefined}
|
|
123
|
-
/>
|
|
124
|
-
|
|
112
|
+
{icon}
|
|
125
113
|
{!isIconOnly && children}
|
|
126
114
|
</>
|
|
127
115
|
) : (
|
|
@@ -20,19 +20,10 @@ export const buttonVariants = {
|
|
|
20
20
|
outline: "ath-button-outline",
|
|
21
21
|
ghost: "ath-button-ghost",
|
|
22
22
|
},
|
|
23
|
-
|
|
24
23
|
dark: "ath-button-dark",
|
|
25
24
|
} as const;
|
|
26
25
|
|
|
27
|
-
export const ButtonGroupVariants = {
|
|
28
|
-
direction: {
|
|
29
|
-
row: "ath-button-group-direction-row",
|
|
30
|
-
column: "ath-button-group-direction-column",
|
|
31
|
-
},
|
|
32
|
-
} as const;
|
|
33
|
-
|
|
34
26
|
export type ButtonSize = keyof typeof buttonVariants.size;
|
|
35
27
|
export type ButtonVariant = keyof typeof buttonVariants.variant;
|
|
36
28
|
export type ButtonDark = typeof buttonVariants.dark;
|
|
37
29
|
export type ButtonTextPosition = keyof typeof buttonVariants.textPosition;
|
|
38
|
-
export type ButtonGroupDirection = keyof typeof ButtonGroupVariants.direction;
|
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
import { CSSObject } from "@emotion/react";
|
|
2
1
|
import { cx } from "../../utils";
|
|
3
|
-
|
|
4
|
-
type ButtonGroupProps
|
|
5
|
-
children: React.ReactNode;
|
|
6
|
-
className?: {
|
|
7
|
-
buttonGroup?: string | false | null | undefined;
|
|
8
|
-
};
|
|
9
|
-
direction?: "row" | "column";
|
|
10
|
-
sx?: {
|
|
11
|
-
buttonGroup?: CSSObject | string;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
2
|
+
import { ButtonGroupVariants } from "./buttonGroup.variants";
|
|
3
|
+
import type { ButtonGroupProps } from "./ButtonGroupProps";
|
|
14
4
|
|
|
15
5
|
export const ButtonGroup = ({
|
|
16
6
|
children,
|
|
@@ -20,7 +10,7 @@ export const ButtonGroup = ({
|
|
|
20
10
|
}: ButtonGroupProps) => {
|
|
21
11
|
const buttonGroupClasses = cx(
|
|
22
12
|
"ath-button-group",
|
|
23
|
-
|
|
13
|
+
ButtonGroupVariants.direction[direction],
|
|
24
14
|
className?.buttonGroup ? className.buttonGroup : undefined,
|
|
25
15
|
);
|
|
26
16
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CSSObject } from "@emotion/react";
|
|
2
|
+
|
|
3
|
+
export type ButtonGroupProps = {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
className?: {
|
|
6
|
+
buttonGroup?: string | false | null | undefined;
|
|
7
|
+
};
|
|
8
|
+
direction?: "row" | "column";
|
|
9
|
+
sx?: {
|
|
10
|
+
buttonGroup?: CSSObject | string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./ButtonGroup";
|
|
@@ -4,6 +4,7 @@ import { cx } from "../../utils/cx";
|
|
|
4
4
|
import { FormInputLabel } from "./FormInputLabel";
|
|
5
5
|
|
|
6
6
|
export type FormInputProps = {
|
|
7
|
+
autoComplete?: string;
|
|
7
8
|
autoFocus?: boolean;
|
|
8
9
|
autoResize?: boolean;
|
|
9
10
|
className?: {
|
|
@@ -48,7 +49,9 @@ export type FormInputProps = {
|
|
|
48
49
|
value?: string | number;
|
|
49
50
|
|
|
50
51
|
onChange: (
|
|
51
|
-
e:
|
|
52
|
+
e:
|
|
53
|
+
| React.ChangeEvent<HTMLInputElement>
|
|
54
|
+
| React.ChangeEvent<HTMLTextAreaElement>,
|
|
52
55
|
) => void;
|
|
53
56
|
onKeyDown?: React.KeyboardEventHandler<
|
|
54
57
|
HTMLInputElement | HTMLTextAreaElement
|
|
@@ -56,6 +59,7 @@ export type FormInputProps = {
|
|
|
56
59
|
};
|
|
57
60
|
|
|
58
61
|
export const FormInput: React.FC<FormInputProps> = ({
|
|
62
|
+
autoComplete,
|
|
59
63
|
autoFocus = false,
|
|
60
64
|
autoResize = false,
|
|
61
65
|
className,
|
|
@@ -159,6 +163,7 @@ export const FormInput: React.FC<FormInputProps> = ({
|
|
|
159
163
|
const inputElement =
|
|
160
164
|
type === "textarea" ? (
|
|
161
165
|
<textarea
|
|
166
|
+
autoComplete={autoComplete}
|
|
162
167
|
autoFocus={autoFocus}
|
|
163
168
|
ref={textAreaRef}
|
|
164
169
|
name={name}
|
|
@@ -175,6 +180,7 @@ export const FormInput: React.FC<FormInputProps> = ({
|
|
|
175
180
|
/>
|
|
176
181
|
) : (
|
|
177
182
|
<input
|
|
183
|
+
autoComplete={autoComplete}
|
|
178
184
|
autoFocus={autoFocus}
|
|
179
185
|
name={name}
|
|
180
186
|
type={type}
|
package/components/List/List.tsx
CHANGED
|
@@ -1,52 +1,32 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
+
import { CSSObject } from "@emotion/react";
|
|
2
3
|
import { cx } from "../../utils/cx";
|
|
3
4
|
|
|
4
5
|
type ListProps = {
|
|
5
6
|
children?: ReactElement[] | string[];
|
|
6
|
-
className?:
|
|
7
|
-
|
|
|
8
|
-
|
|
9
|
-
list?: string | false | null | undefined;
|
|
10
|
-
}
|
|
11
|
-
| false
|
|
12
|
-
| undefined
|
|
13
|
-
| null;
|
|
14
|
-
ariaLabel?: string;
|
|
15
|
-
ariaLabelledBy?: string;
|
|
16
|
-
ariaDescribedBy?: string;
|
|
17
|
-
role?: "list" | "menu" | "menubar" | "tablist" | "tree" | "grid";
|
|
18
|
-
dataTestId?: string;
|
|
7
|
+
className?: {
|
|
8
|
+
list?: string | false | null | undefined;
|
|
9
|
+
};
|
|
19
10
|
ordered?: boolean;
|
|
11
|
+
role?: "list" | "menu" | "menubar" | "tablist" | "tree" | "grid";
|
|
12
|
+
sx?: {
|
|
13
|
+
list?: CSSObject;
|
|
14
|
+
};
|
|
20
15
|
};
|
|
21
16
|
|
|
22
17
|
export const List = ({
|
|
23
18
|
className,
|
|
24
19
|
children,
|
|
25
|
-
ariaLabel,
|
|
26
|
-
ariaLabelledBy,
|
|
27
|
-
ariaDescribedBy,
|
|
28
|
-
role = "list",
|
|
29
|
-
dataTestId,
|
|
30
20
|
ordered = false,
|
|
21
|
+
role = "list",
|
|
22
|
+
sx,
|
|
31
23
|
}: ListProps) => {
|
|
32
24
|
const ListType = ordered ? "ol" : "ul";
|
|
33
25
|
|
|
34
|
-
const classes = cx(
|
|
35
|
-
"ath-list",
|
|
36
|
-
typeof className === "object" && className !== null
|
|
37
|
-
? className.list
|
|
38
|
-
: className,
|
|
39
|
-
);
|
|
26
|
+
const classes = cx("ath-list", className?.list);
|
|
40
27
|
|
|
41
28
|
return (
|
|
42
|
-
<ListType
|
|
43
|
-
className={classes}
|
|
44
|
-
role={role}
|
|
45
|
-
aria-label={ariaLabel}
|
|
46
|
-
aria-labelledby={ariaLabelledBy}
|
|
47
|
-
aria-describedby={ariaDescribedBy}
|
|
48
|
-
data-testid={dataTestId}
|
|
49
|
-
>
|
|
29
|
+
<ListType className={classes} role={role} css={sx?.list}>
|
|
50
30
|
{children}
|
|
51
31
|
</ListType>
|
|
52
32
|
);
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { CSSObject } from "@emotion/react";
|
|
3
|
+
import { HugeiconsIcon } from "@hugeicons/react";
|
|
4
|
+
import { CancelCircleIcon } from "@hugeicons/core-free-icons";
|
|
5
|
+
import { Button } from "../Button/Button";
|
|
6
|
+
import { cx } from "../../main";
|
|
7
|
+
|
|
8
|
+
export type ModalProps = {
|
|
9
|
+
isOpen: boolean;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
title?: string;
|
|
13
|
+
maxWidth?: string;
|
|
14
|
+
showCloseButton?: boolean;
|
|
15
|
+
dataTestId?: string;
|
|
16
|
+
className?: {
|
|
17
|
+
backdrop?: string;
|
|
18
|
+
modal?: string;
|
|
19
|
+
header?: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
content?: string;
|
|
22
|
+
closeButton?: string;
|
|
23
|
+
};
|
|
24
|
+
closeOnBackdropClick?: boolean;
|
|
25
|
+
closeOnEscape?: boolean;
|
|
26
|
+
trapFocus?: boolean;
|
|
27
|
+
initialFocusRef?: React.RefObject<HTMLElement>;
|
|
28
|
+
finalFocusRef?: React.RefObject<HTMLElement>;
|
|
29
|
+
ariaLabel?: string;
|
|
30
|
+
ariaLabelledBy?: string;
|
|
31
|
+
ariaDescribedBy?: string;
|
|
32
|
+
sx?: {
|
|
33
|
+
backdrop?: CSSObject;
|
|
34
|
+
modal?: CSSObject;
|
|
35
|
+
header?: CSSObject;
|
|
36
|
+
title?: CSSObject;
|
|
37
|
+
content?: CSSObject;
|
|
38
|
+
closeButton?: CSSObject;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const Modal: React.FC<ModalProps> = ({
|
|
43
|
+
isOpen,
|
|
44
|
+
children,
|
|
45
|
+
title,
|
|
46
|
+
showCloseButton = true,
|
|
47
|
+
dataTestId,
|
|
48
|
+
className,
|
|
49
|
+
closeOnBackdropClick = true,
|
|
50
|
+
closeOnEscape = true,
|
|
51
|
+
trapFocus = true,
|
|
52
|
+
initialFocusRef,
|
|
53
|
+
finalFocusRef,
|
|
54
|
+
sx,
|
|
55
|
+
|
|
56
|
+
onClose,
|
|
57
|
+
...rest
|
|
58
|
+
}) => {
|
|
59
|
+
const [previouslyFocusedElement, setPreviouslyFocusedElement] =
|
|
60
|
+
React.useState<HTMLElement | null>(null);
|
|
61
|
+
const modalRef = React.useRef<HTMLDivElement>(null);
|
|
62
|
+
|
|
63
|
+
const backdropClasses = cx("ath-modal-backdrop", className?.backdrop);
|
|
64
|
+
const modalClasses = cx("ath-modal", className?.modal);
|
|
65
|
+
const headerClasses = cx("ath-modal-header", className?.header);
|
|
66
|
+
const titleClasses = cx("ath-modal-title", className?.title);
|
|
67
|
+
const contentClasses = cx("ath-modal-content", className?.content);
|
|
68
|
+
const closeButtonClasses = cx(
|
|
69
|
+
"ath-modal-close-button",
|
|
70
|
+
className?.closeButton,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const handleBackdropClick = (e: React.MouseEvent) => {
|
|
74
|
+
if (closeOnBackdropClick && e.target === e.currentTarget) {
|
|
75
|
+
onClose();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Focus trap implementation
|
|
80
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
81
|
+
if (!trapFocus || e.key !== "Tab") return;
|
|
82
|
+
|
|
83
|
+
const modalElement = modalRef.current;
|
|
84
|
+
if (!modalElement) return;
|
|
85
|
+
|
|
86
|
+
const focusableElements = modalElement.querySelectorAll(
|
|
87
|
+
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled])',
|
|
88
|
+
);
|
|
89
|
+
const firstElement = focusableElements[0] as HTMLElement;
|
|
90
|
+
const lastElement = focusableElements[
|
|
91
|
+
focusableElements.length - 1
|
|
92
|
+
] as HTMLElement;
|
|
93
|
+
|
|
94
|
+
if (e.shiftKey) {
|
|
95
|
+
if (document.activeElement === firstElement) {
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
lastElement?.focus();
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
if (document.activeElement === lastElement) {
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
firstElement?.focus();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
React.useEffect(() => {
|
|
108
|
+
if (isOpen) {
|
|
109
|
+
setPreviouslyFocusedElement(document.activeElement as HTMLElement);
|
|
110
|
+
|
|
111
|
+
document.body.style.overflow = "hidden";
|
|
112
|
+
|
|
113
|
+
setTimeout(() => {
|
|
114
|
+
if (initialFocusRef?.current) {
|
|
115
|
+
initialFocusRef.current.focus();
|
|
116
|
+
} else {
|
|
117
|
+
const modalElement = modalRef.current;
|
|
118
|
+
if (modalElement) {
|
|
119
|
+
const firstFocusable = modalElement.querySelector(
|
|
120
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
|
|
121
|
+
) as HTMLElement;
|
|
122
|
+
if (firstFocusable) {
|
|
123
|
+
firstFocusable.focus();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}, 0);
|
|
128
|
+
} else {
|
|
129
|
+
document.body.style.overflow = "";
|
|
130
|
+
|
|
131
|
+
if (finalFocusRef?.current) {
|
|
132
|
+
finalFocusRef.current.focus();
|
|
133
|
+
} else if (previouslyFocusedElement) {
|
|
134
|
+
previouslyFocusedElement.focus();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const handleEscape = (e: KeyboardEvent) => {
|
|
139
|
+
if (closeOnEscape && e.key === "Escape") {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
onClose();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
if (isOpen) {
|
|
146
|
+
document.addEventListener("keydown", handleEscape);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return () => {
|
|
150
|
+
document.removeEventListener("keydown", handleEscape);
|
|
151
|
+
document.body.style.overflow = "";
|
|
152
|
+
};
|
|
153
|
+
}, [
|
|
154
|
+
isOpen,
|
|
155
|
+
onClose,
|
|
156
|
+
closeOnEscape,
|
|
157
|
+
initialFocusRef,
|
|
158
|
+
finalFocusRef,
|
|
159
|
+
previouslyFocusedElement,
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
if (!isOpen) return null;
|
|
163
|
+
|
|
164
|
+
const titleId = title ? `${dataTestId || "modal"}-title` : undefined;
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<div
|
|
168
|
+
className={backdropClasses}
|
|
169
|
+
onClick={handleBackdropClick}
|
|
170
|
+
onKeyDown={handleKeyDown}
|
|
171
|
+
css={sx?.backdrop}
|
|
172
|
+
>
|
|
173
|
+
<div
|
|
174
|
+
ref={modalRef}
|
|
175
|
+
css={sx?.modal}
|
|
176
|
+
className={modalClasses}
|
|
177
|
+
role="dialog"
|
|
178
|
+
{...rest}
|
|
179
|
+
>
|
|
180
|
+
{(title || showCloseButton) && (
|
|
181
|
+
<div css={sx?.header} className={headerClasses}>
|
|
182
|
+
{title && (
|
|
183
|
+
<h1 css={sx?.title} className={titleClasses} id={titleId}>
|
|
184
|
+
{title}
|
|
185
|
+
</h1>
|
|
186
|
+
)}
|
|
187
|
+
{showCloseButton && (
|
|
188
|
+
<Button
|
|
189
|
+
className={{ button: closeButtonClasses }}
|
|
190
|
+
sx={{ button: sx?.closeButton }}
|
|
191
|
+
onClick={onClose}
|
|
192
|
+
aria-label="Close modal"
|
|
193
|
+
variant="ghost"
|
|
194
|
+
size="medium"
|
|
195
|
+
icon={<HugeiconsIcon icon={CancelCircleIcon} size={24} />}
|
|
196
|
+
/>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
)}
|
|
200
|
+
<div css={sx?.content} className={contentClasses}>
|
|
201
|
+
{children}
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Modal";
|