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