Skip to content

Commit 1baecb7

Browse files
committed
feat: add component alert-dialog
1 parent 634c04f commit 1baecb7

File tree

18 files changed

+803
-0
lines changed

18 files changed

+803
-0
lines changed

packages/ui-variants/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ export * from './types';
44

55
export * from './variants/accordion';
66
export * from './variants/alert';
7+
export * from './variants/alert-dialog';
78
export * from './variants/badge';
89
export * from './variants/button';
910
export * from './variants/button-group';
1011
export * from './variants/card';
1112
export * from './variants/divider';
1213
export * from './variants/scroll-area';
14+
1315
export * from './variants/tabs';
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { tv } from 'tailwind-variants';
2+
3+
export const dialogVariants = tv({
4+
defaultVariants: {
5+
size: 'md'
6+
},
7+
slots: {
8+
closeIcon: `absolute`,
9+
content: [
10+
`fixed left-[50%] top-[50%] z-50 flex flex-col w-full border bg-background shadow-lg translate-x-[-50%] translate-y-[-50%] md:w-full duration-200 sm:rounded-lg`,
11+
`data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]`,
12+
`data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]`
13+
],
14+
description: `text-muted-foreground`,
15+
footer: `flex flex-col-reverse sm:flex-row sm:justify-end`,
16+
header: `flex flex-col text-center sm:text-left`,
17+
overlay: [
18+
`fixed inset-0 z-50 bg-black/80`,
19+
`data-[state=open]:animate-in data-[state=open]:fade-in-0`,
20+
`data-[state=closed]:animate-out data-[state=closed]:fade-out-0`
21+
],
22+
title: `flex items-center font-semibold leading-none tracking-tight`
23+
},
24+
variants: {
25+
size: {
26+
'2xl': {
27+
closeIcon: 'right-6 top-6',
28+
content: `gap-y-6 max-w-3xl px-7 py-6 text-xl`,
29+
description: 'text-xl',
30+
footer: 'gap-6',
31+
header: 'gap-y-6',
32+
title: 'gap-x-3.5 text-2xl'
33+
},
34+
lg: {
35+
closeIcon: 'right-4 top-4',
36+
content: `gap-y-4 max-w-xl px-5 py-4 text-base`,
37+
description: 'text-base',
38+
footer: 'gap-4',
39+
header: 'gap-y-4',
40+
title: 'gap-x-2.5 text-lg'
41+
},
42+
md: {
43+
closeIcon: 'right-3 top-3',
44+
content: `gap-y-3 max-w-lg px-4 py-3 text-sm`,
45+
description: 'text-sm',
46+
footer: 'gap-3',
47+
header: 'gap-y-3',
48+
title: 'gap-x-2 text-base'
49+
},
50+
sm: {
51+
closeIcon: 'right-2 top-2',
52+
content: `gap-y-2 max-w-md px-3 py-2 text-xs`,
53+
description: 'text-xs',
54+
footer: 'gap-2',
55+
header: 'gap-y-2',
56+
title: 'gap-x-1.75 text-sm'
57+
},
58+
xl: {
59+
closeIcon: 'right-5 top-5',
60+
content: `gap-y-5 max-w-2xl px-6 py-5 text-lg`,
61+
description: 'text-lg',
62+
footer: 'gap-5',
63+
header: 'gap-y-5',
64+
title: 'gap-x-3 text-xl'
65+
},
66+
xs: {
67+
closeIcon: 'right-1.5 top-1.5',
68+
content: `gap-y-1.5 max-w-md px-2 py-1.5 text-2xs`,
69+
description: 'text-2xs',
70+
footer: 'gap-1.5',
71+
header: 'gap-y-1.5',
72+
title: 'gap-x-1.5 text-xs'
73+
}
74+
}
75+
}
76+
});
77+
78+
export type DialogSlots = keyof typeof dialogVariants.slots;

packages/ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
},
1313
"dependencies": {
1414
"@radix-ui/react-accordion": "^1.2.11",
15+
"@radix-ui/react-alert-dialog": "^1.1.14",
1516
"@radix-ui/react-compose-refs": "1.1.2",
1617
"@radix-ui/react-scroll-area": "^1.2.9",
1718
"@radix-ui/react-separator": "^1.1.7",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export { default as AlertDialog } from './source/AlertDialog';
2+
export { default as AlertDialogAction } from './source/AlertDialogAction';
3+
export { default as AlertDialogCancel } from './source/AlertDialogCancel';
4+
export { default as AlertDialogContent } from './source/AlertDialogContent';
5+
export { default as AlertDialogDescription } from './source/AlertDialogDescription';
6+
export { default as AlertDialogFooter } from './source/AlertDialogFooter';
7+
export { default as AlertDialogHeader } from './source/AlertDialogHeader';
8+
export { default as AlertDialogOverlay } from './source/AlertDialogOverlay';
9+
export { default as AlertDialogTitle } from './source/AlertDialogTitle';
10+
11+
export * from './types';
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import type { Content } from '@radix-ui/react-alert-dialog';
2+
import { AlertDialog as AlertDialogRoot, Portal, Trigger } from '@radix-ui/react-alert-dialog';
3+
import { Slot } from '@radix-ui/react-slot';
4+
import { CircleAlert, CircleCheck, CircleX, Info } from 'lucide-react';
5+
import { forwardRef } from 'react';
6+
7+
import type { AlertDialogProps, AlertType } from '../types';
8+
9+
import AlertDialogContent from './AlertDialogContent';
10+
import AlertDialogDescription from './AlertDialogDescription';
11+
import AlertDialogFooter from './AlertDialogFooter';
12+
import AlertDialogHeader from './AlertDialogHeader';
13+
import AlertDialogOverlay from './AlertDialogOverlay';
14+
import AlertDialogTitle from './AlertDialogTitle';
15+
16+
const iconRecord: Record<AlertType, React.ReactNode> = {
17+
destructive: <CircleX className="text-destructive" />,
18+
info: <Info className="text-info" />,
19+
success: <CircleCheck className="text-success" />,
20+
warning: <CircleAlert className="text-warning" />
21+
};
22+
23+
const AlertDialog = forwardRef<React.ElementRef<typeof Content>, AlertDialogProps>((props, ref) => {
24+
const {
25+
children,
26+
className,
27+
classNames,
28+
description,
29+
footer,
30+
forceMountOverlay,
31+
forceMountPortal,
32+
icon,
33+
size,
34+
title,
35+
trigger,
36+
type,
37+
...rest
38+
} = props;
39+
40+
return (
41+
<AlertDialogRoot {...props}>
42+
<Trigger asChild>{trigger}</Trigger>
43+
44+
<Portal forceMount={forceMountPortal}>
45+
<AlertDialogOverlay
46+
className={classNames?.overlay}
47+
forceMount={forceMountOverlay}
48+
/>
49+
50+
<AlertDialogContent
51+
{...rest}
52+
className={className || classNames?.content}
53+
ref={ref}
54+
size={size}
55+
>
56+
<AlertDialogHeader
57+
className={classNames?.header}
58+
size={size}
59+
>
60+
<AlertDialogTitle
61+
className={classNames?.title}
62+
size={size}
63+
>
64+
{icon || (type && <Slot className={classNames?.icon || ''}>{iconRecord[type]}</Slot>)}
65+
{title}
66+
</AlertDialogTitle>
67+
68+
{description && (
69+
<AlertDialogDescription
70+
className={classNames?.description}
71+
size={size}
72+
>
73+
{description}
74+
</AlertDialogDescription>
75+
)}
76+
</AlertDialogHeader>
77+
78+
{children}
79+
80+
{footer && (
81+
<AlertDialogFooter
82+
className={classNames?.footer}
83+
size={size}
84+
>
85+
{footer}
86+
</AlertDialogFooter>
87+
)}
88+
</AlertDialogContent>
89+
</Portal>
90+
</AlertDialogRoot>
91+
);
92+
});
93+
94+
AlertDialog.displayName = 'AlertDialog';
95+
96+
export default AlertDialog;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Action } from '@radix-ui/react-alert-dialog';
2+
import { forwardRef } from 'react';
3+
4+
import { Button, type ButtonProps } from '../../button';
5+
6+
const AlertDialogAction = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
7+
return (
8+
<Action asChild>
9+
<Button
10+
{...props}
11+
ref={ref}
12+
/>
13+
</Action>
14+
);
15+
});
16+
17+
AlertDialogAction.displayName = 'AlertDialogAction';
18+
19+
export default AlertDialogAction;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Cancel } from '@radix-ui/react-alert-dialog';
2+
import { forwardRef } from 'react';
3+
4+
import { Button, type ButtonProps } from '../../button';
5+
6+
const AlertDialogCancel = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
7+
const { variant = 'plain', ...rest } = props;
8+
9+
return (
10+
<Cancel asChild>
11+
<Button
12+
{...rest}
13+
ref={ref}
14+
variant={variant}
15+
/>
16+
</Cancel>
17+
);
18+
});
19+
20+
AlertDialogCancel.displayName = 'AlertDialogCancel';
21+
22+
export default AlertDialogCancel;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Content } from '@radix-ui/react-alert-dialog';
2+
import { cn, dialogVariants } from '@soybean-react-ui/variants';
3+
import { forwardRef } from 'react';
4+
5+
import type { AlertDialogContentProps } from '../types';
6+
7+
const AlertDialogContent = forwardRef<React.ElementRef<typeof Content>, AlertDialogContentProps>((props, ref) => {
8+
const { className, size, ...rest } = props;
9+
10+
const { content } = dialogVariants({ size });
11+
12+
const mergedClass = cn(content(), className);
13+
return (
14+
<Content
15+
{...rest}
16+
className={mergedClass}
17+
ref={ref}
18+
/>
19+
);
20+
});
21+
22+
AlertDialogContent.displayName = 'AlertDialogContent';
23+
24+
export default AlertDialogContent;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Description } from '@radix-ui/react-alert-dialog';
2+
import { cn, dialogVariants } from '@soybean-react-ui/variants';
3+
import { forwardRef } from 'react';
4+
5+
import type { AlertDialogDescriptionProps } from '../types';
6+
7+
const AlertDialogDescription = forwardRef<React.ElementRef<typeof Description>, AlertDialogDescriptionProps>(
8+
(props, ref) => {
9+
const { className, size, ...rest } = props;
10+
11+
const { description } = dialogVariants({ size });
12+
13+
const mergedClass = cn(description(), className);
14+
return (
15+
<Description
16+
{...rest}
17+
className={mergedClass}
18+
ref={ref}
19+
/>
20+
);
21+
}
22+
);
23+
24+
AlertDialogDescription.displayName = 'AlertDialogDescription';
25+
26+
export default AlertDialogDescription;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { cn, dialogVariants } from '@soybean-react-ui/variants';
2+
import { forwardRef } from 'react';
3+
4+
import type { AlertDialogFooterProps } from '../types';
5+
6+
const AlertDialogFooter = forwardRef<HTMLDivElement, AlertDialogFooterProps>((props, ref) => {
7+
const { className, size, ...rest } = props;
8+
9+
const { footer } = dialogVariants({ size });
10+
11+
const mergedClass = cn(footer(), className);
12+
return (
13+
<div
14+
{...rest}
15+
className={mergedClass}
16+
ref={ref}
17+
/>
18+
);
19+
});
20+
21+
AlertDialogFooter.displayName = 'AlertDialogFooter';
22+
23+
export default AlertDialogFooter;

0 commit comments

Comments
 (0)