Skip to content

Commit 989cd08

Browse files
committed
feat: add component alert
1 parent a20f37a commit 989cd08

File tree

11 files changed

+567
-0
lines changed

11 files changed

+567
-0
lines changed

packages/ui-variants/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from './shared';
33
export * from './types';
44

55
export * from './variants/accordion';
6+
export * from './variants/alert';
67
export * from './variants/badge';
78
export * from './variants/button';
89
export * from './variants/button-group';
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
import { tv } from 'tailwind-variants';
2+
import type { VariantProps } from 'tailwind-variants';
3+
4+
export const alertVariants = tv({
5+
compoundVariants: [
6+
{
7+
class: {
8+
close: 'text-primary-foreground',
9+
icon: 'text-primary-foreground',
10+
root: 'bg-primary text-primary-foreground'
11+
},
12+
color: 'primary',
13+
variant: 'solid'
14+
},
15+
{
16+
class: {
17+
close: 'text-destructive-foreground',
18+
icon: 'text-destructive-foreground',
19+
root: 'bg-destructive text-destructive-foreground'
20+
},
21+
color: 'destructive',
22+
variant: 'solid'
23+
},
24+
{
25+
class: {
26+
close: 'text-success-foreground',
27+
icon: 'text-success-foreground',
28+
root: 'bg-success text-success-foreground'
29+
},
30+
color: 'success',
31+
variant: 'solid'
32+
},
33+
{
34+
class: {
35+
close: 'text-warning-foreground',
36+
icon: 'text-warning-foreground',
37+
root: 'bg-warning text-warning-foreground'
38+
},
39+
color: 'warning',
40+
variant: 'solid'
41+
},
42+
{
43+
class: {
44+
close: 'text-info-foreground',
45+
icon: 'text-info-foreground',
46+
root: 'bg-info text-info-foreground'
47+
},
48+
color: 'info',
49+
variant: 'solid'
50+
},
51+
{
52+
class: {
53+
close: 'text-carbon-foreground',
54+
icon: 'text-carbon-foreground',
55+
root: 'bg-carbon text-carbon-foreground'
56+
},
57+
color: 'carbon',
58+
variant: 'solid'
59+
},
60+
{
61+
class: {
62+
close: 'text-secondary-foreground',
63+
icon: 'text-secondary-foreground',
64+
root: 'bg-secondary text-secondary-foreground'
65+
},
66+
color: 'secondary',
67+
variant: 'solid'
68+
},
69+
{
70+
class: {
71+
close: 'text-accent-foreground',
72+
icon: 'text-accent-foreground',
73+
root: 'bg-accent text-accent-foreground'
74+
},
75+
color: 'accent',
76+
variant: 'solid'
77+
},
78+
{
79+
class: {
80+
root: 'bg-primary/10'
81+
},
82+
color: 'primary',
83+
variant: ['soft', 'ghost']
84+
},
85+
{
86+
class: {
87+
root: 'bg-destructive/10'
88+
},
89+
color: 'destructive',
90+
variant: ['soft', 'ghost']
91+
},
92+
{
93+
class: {
94+
root: 'bg-success/10'
95+
},
96+
color: 'success',
97+
variant: ['soft', 'ghost']
98+
},
99+
{
100+
class: {
101+
root: 'bg-warning/10'
102+
},
103+
color: 'warning',
104+
variant: ['soft', 'ghost']
105+
},
106+
{
107+
class: {
108+
root: 'bg-info/10'
109+
},
110+
color: 'info',
111+
variant: ['soft', 'ghost']
112+
},
113+
{
114+
class: {
115+
root: 'bg-carbon/10'
116+
},
117+
color: 'carbon',
118+
variant: ['soft', 'ghost']
119+
},
120+
{
121+
class: {
122+
root: 'bg-secondary-foreground/5'
123+
},
124+
color: 'secondary',
125+
variant: ['soft', 'ghost']
126+
},
127+
{
128+
class: {
129+
root: 'bg-accent-foreground/5'
130+
},
131+
color: 'accent',
132+
variant: ['soft', 'ghost']
133+
}
134+
],
135+
defaultVariants: {
136+
color: 'primary',
137+
size: 'md',
138+
variant: 'ghost'
139+
},
140+
slots: {
141+
action: 'shrink-0',
142+
close: 'absolute',
143+
description: '[&_p]:leading-relaxed',
144+
icon: 'shrink-0',
145+
root: 'relative flex w-full rounded-lg border',
146+
title: 'font-medium tracking-tight',
147+
wrapper: 'flex-1 flex flex-col'
148+
},
149+
variants: {
150+
color: {
151+
accent: {
152+
icon: 'text-accent-foreground',
153+
root: 'border-accent-foreground/50 text-accent-foreground'
154+
},
155+
carbon: {
156+
icon: 'text-carbon',
157+
root: 'border-carbon text-carbon'
158+
},
159+
destructive: {
160+
icon: 'text-destructive',
161+
root: 'border-destructive text-destructive'
162+
},
163+
info: {
164+
icon: 'text-info',
165+
root: 'border-info text-info'
166+
},
167+
primary: {
168+
icon: 'text-primary',
169+
root: 'border-primary text-primary'
170+
},
171+
secondary: {
172+
icon: 'text-secondary-foreground',
173+
root: 'border-secondary-foreground/50 text-secondary-foreground'
174+
},
175+
success: {
176+
icon: 'text-success',
177+
root: 'border-success text-success'
178+
},
179+
warning: {
180+
icon: 'text-warning',
181+
root: 'border-warning text-warning'
182+
}
183+
},
184+
size: {
185+
'2xl': {
186+
close: 'top-4 right-7',
187+
root: 'gap-4.5 px-7 py-4.5 text-xl',
188+
title: 'text-2xl leading-6.25',
189+
wrapper: 'gap-2'
190+
},
191+
lg: {
192+
close: 'top-3 right-5',
193+
root: 'gap-3.5 px-5 py-3.5 text-base',
194+
title: 'text-lg leading-5',
195+
wrapper: 'gap-1.25'
196+
},
197+
md: {
198+
close: 'top-2.5 right-4',
199+
root: 'gap-3 px-4 py-3 text-sm',
200+
title: 'text-base leading-[calc(var(--spacing)*4.375)]',
201+
wrapper: 'gap-1'
202+
},
203+
sm: {
204+
close: 'top-2 right-3',
205+
root: 'gap-2.5 px-3 py-2.5 text-xs',
206+
title: 'text-sm leading-3.75',
207+
wrapper: 'gap-1'
208+
},
209+
xl: {
210+
close: 'top-3.5 right-6',
211+
root: 'gap-4 px-6 py-4 text-lg',
212+
title: 'text-xl leading-[var(--spacing)*5.625]',
213+
wrapper: 'gap-1.5'
214+
},
215+
xs: {
216+
close: 'top-1.25 right-2',
217+
root: 'gap-2 px-2 py-1.75 text-2xs',
218+
title: 'text-xs leading-[calc(var(--spacing)*3.125)]',
219+
wrapper: 'gap-0.75'
220+
}
221+
},
222+
variant: {
223+
ghost: {
224+
root: ''
225+
},
226+
outline: {
227+
root: 'bg-background'
228+
},
229+
pure: {
230+
root: 'bg-background text-foreground border-border'
231+
},
232+
soft: {
233+
root: 'border-0'
234+
},
235+
solid: {
236+
root: ''
237+
}
238+
}
239+
}
240+
});
241+
242+
type AlertVariants = VariantProps<typeof alertVariants>;
243+
244+
export type AlertVariant = NonNullable<AlertVariants['variant']>;
245+
246+
export type AlertSlots = keyof typeof alertVariants.slots;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export { default as Alert } from './source/Alert';
2+
export { default as AlertDescription } from './source/AlertDescription';
3+
export { default as AlertRoot } from './source/AlertRoot';
4+
export { default as AlertTitle } from './source/AlertTitle';
5+
export { default as AlertWrapper } from './source/AlertWrapper';
6+
7+
export * from './types';
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Slot } from '@radix-ui/react-slot';
2+
import { alertVariants, cn } from '@soybean-react-ui/variants';
3+
import { forwardRef } from 'react';
4+
5+
import type { AlertProps } from '../types';
6+
7+
import AlertDescription from './AlertDescription';
8+
import AlertRoot from './AlertRoot';
9+
import AlertTitle from './AlertTitle';
10+
import AlertWrapper from './AlertWrapper';
11+
12+
const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
13+
const {
14+
children,
15+
className,
16+
classNames,
17+
color,
18+
description,
19+
icon,
20+
leading,
21+
size,
22+
title,
23+
trailing,
24+
variant,
25+
...rest
26+
} = props;
27+
28+
const { icon: iconCls } = alertVariants({ color, size, variant });
29+
30+
const mergedCls = cn(iconCls(), classNames?.icon);
31+
return (
32+
<AlertRoot
33+
className={className || classNames?.root}
34+
color={color}
35+
size={size}
36+
variant={variant}
37+
{...rest}
38+
ref={ref}
39+
>
40+
{leading}
41+
<Slot className={mergedCls}>{icon}</Slot>
42+
43+
<AlertWrapper
44+
className={classNames?.wrapper}
45+
size={size}
46+
>
47+
{title && (
48+
<AlertTitle
49+
className={classNames?.title}
50+
size={size}
51+
>
52+
{title}
53+
</AlertTitle>
54+
)}
55+
56+
{description && (
57+
<AlertDescription
58+
className={classNames?.description}
59+
size={size}
60+
>
61+
{description}
62+
</AlertDescription>
63+
)}
64+
65+
{children}
66+
</AlertWrapper>
67+
68+
{trailing}
69+
</AlertRoot>
70+
);
71+
});
72+
73+
Alert.displayName = 'Alert';
74+
75+
export default Alert;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { alertVariants, cn } from '@soybean-react-ui/variants';
2+
import { forwardRef } from 'react';
3+
4+
import type { AlertDescriptionProps } from '../types';
5+
6+
const AlertDescription = forwardRef<HTMLDivElement, AlertDescriptionProps>((props, ref) => {
7+
const { className, size, ...rest } = props;
8+
9+
const { description } = alertVariants({ size });
10+
11+
const mergedCls = cn(description(), className);
12+
13+
return (
14+
<div
15+
className={mergedCls}
16+
{...rest}
17+
ref={ref}
18+
/>
19+
);
20+
});
21+
22+
AlertDescription.displayName = 'AlertDescription';
23+
24+
export default AlertDescription;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { alertVariants, cn } from '@soybean-react-ui/variants';
2+
import { forwardRef } from 'react';
3+
4+
import type { AlertRootProps } from '../types';
5+
6+
const AlertRoot = forwardRef<HTMLDivElement, AlertRootProps>((props, ref) => {
7+
const { className, color, size, variant, ...rest } = props;
8+
9+
const { root } = alertVariants({ color, size, variant });
10+
11+
const mergedCls = cn(root(), className);
12+
13+
return (
14+
<div
15+
className={mergedCls}
16+
role="alert"
17+
{...rest}
18+
ref={ref}
19+
/>
20+
);
21+
});
22+
23+
AlertRoot.displayName = 'AlertRoot';
24+
25+
export default AlertRoot;

0 commit comments

Comments
 (0)