Skip to content

Commit 6c2516a

Browse files
committed
chore: fixes and adding draft1 of stories
1 parent 2a73f8a commit 6c2516a

File tree

9 files changed

+240
-75
lines changed

9 files changed

+240
-75
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import {ToastOptions, ToastQueue, useToastQueue} from "@react-stately/toast";
2-
import {ToastVariantProps} from "@nextui-org/theme";
32

43
import {ToastRegion} from "./toast-region";
5-
import {ToastType} from "./use-toast";
4+
import {ToastProps} from "./use-toast";
65

7-
let globalToastQueue: ToastQueue<ToastType> | null = null;
6+
let globalToastQueue: ToastQueue<ToastProps> | null = null;
87

98
interface ToastProviderProps {
109
maxVisibleToasts?: number;
@@ -30,31 +29,15 @@ export const ToastProvider = ({maxVisibleToasts = 5}: ToastProviderProps) => {
3029
return <ToastRegion toastQueue={toastQueue} />;
3130
};
3231

33-
export const addToast = ({
34-
title,
35-
description,
36-
priority,
37-
timeout,
38-
...config
39-
}: {
40-
title: string;
41-
description: string;
42-
} & ToastOptions &
43-
ToastVariantProps) => {
32+
export const addToast = ({...props}: ToastProps & ToastOptions) => {
4433
if (!globalToastQueue) {
4534
return;
4635
}
4736

48-
const content: ToastType = {
49-
title,
50-
description,
51-
config: config,
52-
};
53-
5437
const options: Partial<ToastOptions> = {
55-
timeout,
56-
priority,
38+
timeout: props?.timeout,
39+
priority: props?.priority,
5740
};
5841

59-
globalToastQueue.add(content, options);
42+
globalToastQueue.add(props, options);
6043
};

packages/components/toast/src/toast-region.tsx

+4-6
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import {useToastRegion, AriaToastRegionProps} from "@react-aria/toast";
33
import {QueuedToast, ToastState} from "@react-stately/toast";
44

55
import Toast from "./toast";
6-
import {ToastType} from "./use-toast";
6+
import {ToastProps} from "./use-toast";
77

88
interface ToastRegionProps<T> extends AriaToastRegionProps {
99
toastQueue: ToastState<T>;
1010
}
1111

12-
export function ToastRegion<T extends ToastType>({toastQueue, ...props}: ToastRegionProps<T>) {
12+
export function ToastRegion<T extends ToastProps>({toastQueue, ...props}: ToastRegionProps<T>) {
1313
const ref = useRef(null);
1414
const {regionProps} = useToastRegion(props, toastQueue, ref);
1515

@@ -20,10 +20,8 @@ export function ToastRegion<T extends ToastType>({toastQueue, ...props}: ToastRe
2020
ref={ref}
2121
className="fixed bottom-6 right-6 w-screen flex flex-col items-end justify-center"
2222
>
23-
{toastQueue.visibleToasts.map((toast: QueuedToast<ToastType>) => {
24-
return (
25-
<Toast key={toast.key} state={toastQueue} toast={toast} {...toast.content.config} />
26-
);
23+
{toastQueue.visibleToasts.map((toast: QueuedToast<ToastProps>) => {
24+
return <Toast key={toast.key} state={toastQueue} toast={toast} {...toast.content} />;
2725
})}
2826
</div>
2927
</>

packages/components/toast/src/toast.tsx

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,36 @@
11
import {forwardRef} from "@nextui-org/system";
22
import {Button, ButtonProps} from "@nextui-org/button";
3-
import {CloseIcon} from "@nextui-org/shared-icons";
3+
import {
4+
CloseIcon,
5+
DangerIcon,
6+
InfoFilledIcon,
7+
SuccessIcon,
8+
WarningIcon,
9+
} from "@nextui-org/shared-icons";
410
import {motion, AnimatePresence} from "framer-motion";
511
import {Progress} from "@nextui-org/progress";
12+
import {cloneElement, isValidElement} from "react";
613

714
import {UseToastProps, useToast} from "./use-toast";
815

916
export interface ToastProps extends UseToastProps {}
1017

18+
const iconMap = {
19+
primary: InfoFilledIcon,
20+
secondary: InfoFilledIcon,
21+
success: SuccessIcon,
22+
warning: WarningIcon,
23+
danger: DangerIcon,
24+
} as const;
25+
1126
const Toast = forwardRef<"div", ToastProps>((props, ref) => {
1227
const {
1328
Component,
14-
Icon,
29+
icon,
1530
domRef,
1631
endContent,
1732
closeProgressBarValue,
33+
color,
1834
getToastProps,
1935
getContentProps,
2036
getTitleProps,
@@ -33,6 +49,9 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => {
3349
exit: {opacity: 0, y: 50},
3450
};
3551

52+
const customIcon = icon && isValidElement(icon) ? cloneElement(icon, getIconProps()) : null;
53+
const IconComponent = iconMap[color] || iconMap.primary;
54+
3655
return (
3756
<AnimatePresence>
3857
<motion.div
@@ -44,7 +63,7 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => {
4463
>
4564
<Component ref={domRef} {...getToastProps()}>
4665
<main {...getContentProps()}>
47-
<Icon {...getIconProps()} />
66+
{customIcon || <IconComponent {...getIconProps()} />}
4867
<div>
4968
<div {...getTitleProps()}>{props.toast.content.title}</div>
5069
<div {...getDescriptionProps()}>{props.toast.content.description}</div>

packages/components/toast/src/use-toast.ts

+16-22
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,26 @@ import {ReactNode, useCallback, useEffect, useMemo, useState} from "react";
88
import {useToast as useToastAria, AriaToastProps} from "@react-aria/toast";
99
import {mergeProps} from "@react-aria/utils";
1010
import {QueuedToast, ToastState} from "@react-stately/toast";
11-
import {InfoFilledIcon} from "@nextui-org/shared-icons";
12-
13-
export type ToastType = {
14-
title: string;
15-
description: string;
16-
config: ToastVariantProps;
17-
};
18-
19-
interface Props<T> extends HTMLNextUIProps<"div"> {
20-
/**
21-
* Ref to the DOM node.
22-
*/
11+
12+
export interface ToastProps extends ToastVariantProps {
2313
ref?: ReactRef<HTMLElement | null>;
24-
toast: QueuedToast<T>;
25-
state: ToastState<T>;
14+
title?: string;
15+
description?: string;
2616
classNames?: SlotsToClasses<ToastSlots>;
27-
/**
28-
* Content to be displayed in the end side of the alert
29-
*/
3017
endContent?: ReactNode;
18+
icon?: ReactNode;
19+
}
20+
21+
interface Props<T> extends HTMLNextUIProps<"div">, ToastProps {
22+
toast: QueuedToast<T>;
23+
state: ToastState<T>;
3124
}
3225

33-
export type UseToastProps<T = ToastType> = Props<T> &
26+
export type UseToastProps<T = ToastProps> = Props<T> &
3427
ToastVariantProps &
3528
Omit<AriaToastProps<T>, "div">;
3629

37-
export function useToast<T extends ToastType>(originalProps: UseToastProps<T>) {
30+
export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>) {
3831
const [props, variantProps] = mapPropsVariants(originalProps, toastTheme.variantKeys);
3932

4033
const [closeProgressBarValue, setCloseProgressBarValue] = useState(0);
@@ -56,7 +49,7 @@ export function useToast<T extends ToastType>(originalProps: UseToastProps<T>) {
5649
const {ref, as, className, classNames, toast, endContent, ...otherProps} = props;
5750

5851
const Component = as || "div";
59-
let Icon = InfoFilledIcon;
52+
const icon: ReactNode = props.icon;
6053

6154
const domRef = useDOMRef(ref);
6255
const baseStyles = clsx(className, classNames?.base);
@@ -108,7 +101,7 @@ export function useToast<T extends ToastType>(originalProps: UseToastProps<T>) {
108101

109102
const getIconProps: PropGetter = useCallback(
110103
(props = {}) => ({
111-
className: slots.content({class: classNames?.icon}),
104+
className: slots.icon({class: classNames?.icon}),
112105
...props,
113106
}),
114107
[],
@@ -166,11 +159,12 @@ export function useToast<T extends ToastType>(originalProps: UseToastProps<T>) {
166159

167160
return {
168161
Component,
169-
Icon,
162+
icon,
170163
styles,
171164
domRef,
172165
classNames,
173166
closeProgressBarValue,
167+
color: variantProps["color"],
174168
getToastProps,
175169
getTitleProps,
176170
getContentProps,

packages/components/toast/stories/toast.stories.tsx

+140-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react";
22
import {Meta} from "@storybook/react";
3-
import {toast} from "@nextui-org/theme";
3+
import {cn, toast} from "@nextui-org/theme";
44
import {Button} from "@nextui-org/button";
55

66
import {Toast, ToastProps, ToastProvider, addToast} from "../src";
@@ -43,7 +43,7 @@ const Template = (args: ToastProps) => (
4343
<Button
4444
onPress={() => {
4545
addToast({
46-
title: "Title",
46+
title: "Toast Title",
4747
description: "Toast description",
4848
...args,
4949
});
@@ -54,17 +54,153 @@ const Template = (args: ToastProps) => (
5454
</>
5555
);
5656

57+
const TimeoutTemplate = (args: ToastProps) => {
58+
return (
59+
<>
60+
<ToastProvider />
61+
<Button
62+
onPress={() => {
63+
addToast({
64+
title: "Toast Title",
65+
description: "Toast Description",
66+
timeout: 3000,
67+
...args,
68+
});
69+
}}
70+
>
71+
Toast
72+
</Button>
73+
</>
74+
);
75+
};
76+
77+
const WithEndContentTemplate = (args) => {
78+
return (
79+
<>
80+
<ToastProvider />
81+
<Button
82+
onPress={() => {
83+
addToast({
84+
title: "Toast Title",
85+
description: "Toast Description",
86+
endContent: (
87+
<Button color="warning" size="sm" variant="flat">
88+
Upgrade
89+
</Button>
90+
),
91+
color: "warning",
92+
variant: "faded",
93+
...args,
94+
});
95+
}}
96+
>
97+
Toast
98+
</Button>
99+
</>
100+
);
101+
};
102+
103+
const CustomToastComponent = (args) => {
104+
const color = args.color;
105+
const colorMap = {
106+
primary: "before:bg-primary border-primary-200 dark:border-primary-100",
107+
secondary: "before:bg-secondary border-secondary-200 dark:border-secondary-100",
108+
success: "before:bg-success border-success-200 dark:border-success-100",
109+
warning: "before:bg-warning border-warning-200 dark:border-warning-100",
110+
danger: "before:bg-danger border-danger-200 dark:border-danger-100",
111+
};
112+
113+
return (
114+
<>
115+
<Button
116+
color={color}
117+
variant="bordered"
118+
onPress={() => {
119+
addToast({
120+
title: "Toast Title",
121+
description: "Toast Description",
122+
classNames: {
123+
base: cn([
124+
"bg-default-50 dark:bg-background shadow-sm",
125+
"border-1",
126+
"relative before:content-[''] before:absolute before:z-10",
127+
"before:left-0 before:top-[-1px] before:bottom-[-1px] before:w-1",
128+
"rounded-l-none border-l-0",
129+
colorMap[color],
130+
]),
131+
},
132+
color: color,
133+
});
134+
}}
135+
>
136+
Toast
137+
</Button>
138+
</>
139+
);
140+
};
141+
142+
const CustomToastTemplate = () => {
143+
const colors = ["primary", "secondary", "warning", "danger", "success"];
144+
145+
return (
146+
<>
147+
<ToastProvider />
148+
<div className="flex gap-2">
149+
{colors.map((color, idx) => (
150+
<CustomToastComponent key={idx} color={color} />
151+
))}
152+
</div>
153+
</>
154+
);
155+
};
156+
57157
export const Default = {
58158
render: Template,
59159
args: {
60160
...defaultProps,
61161
},
62162
};
63163

64-
export const WithTimeout = {
164+
export const WithIcon = {
65165
render: Template,
66166
args: {
67167
...defaultProps,
68-
timeout: 3000,
168+
title: "Custom Icon",
169+
icon: (
170+
<svg height={24} viewBox="0 0 24 24" width={24}>
171+
<g
172+
fill="none"
173+
stroke="currentColor"
174+
strokeLinecap="round"
175+
strokeLinejoin="round"
176+
strokeMiterlimit={10}
177+
strokeWidth={1.5}
178+
>
179+
<path
180+
d="M11.845 21.662C8.153 21.662 5 21.088 5 18.787s3.133-4.425 6.845-4.425c3.692 0 6.845 2.1 6.845 4.4s-3.134 2.9-6.845 2.9z"
181+
data-name="Stroke 1"
182+
/>
183+
<path d="M11.837 11.174a4.372 4.372 0 10-.031 0z" data-name="Stroke 3" />
184+
</g>
185+
</svg>
186+
),
187+
},
188+
};
189+
190+
export const WithTimeout = {
191+
render: TimeoutTemplate,
192+
args: {
193+
...defaultProps,
194+
},
195+
};
196+
197+
export const WithEndContent = {
198+
render: WithEndContentTemplate,
199+
args: {
200+
...defaultProps,
69201
},
70202
};
203+
204+
export const CustomTemplate = {
205+
render: CustomToastTemplate,
206+
};

0 commit comments

Comments
 (0)