Skip to content

Commit 5805bf4

Browse files
authored
Export toast and make toast content vertical aligned (#617)
* Export toast and make toast content vertical aligned * Fix incorrect props for toast provider * Add toast container changes * Remove unwanted debug code
1 parent cc2d62f commit 5805bf4

File tree

3 files changed

+116
-20
lines changed

3 files changed

+116
-20
lines changed

src/App.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ import {
4343
GridContainer,
4444
TextField,
4545
Label,
46+
createToast,
47+
ToastProvider,
48+
Toast,
4649
} from "@/components";
4750
import { Dialog } from "@/components/Dialog/Dialog";
4851
import { ConfirmationDialog } from "@/components/ConfirmationDialog/ConfirmationDialog";
@@ -108,6 +111,78 @@ const App = () => {
108111
theme={currentTheme}
109112
config={{ tooltip: { delayDuration: 0 } }}
110113
>
114+
<Button
115+
onClick={() => {
116+
createToast({
117+
title: "Toast Title",
118+
description: "This is a toast description",
119+
type: "success",
120+
actions: [
121+
{
122+
label: "Action 1",
123+
altText: "Action 1 Alt Text",
124+
onClick: () => console.log("Action 1 clicked"),
125+
type: "secondary",
126+
},
127+
{
128+
label: "Action 2",
129+
altText: "Action 2 Alt Text",
130+
onClick: () => console.log("Action 2 clicked"),
131+
type: "secondary",
132+
},
133+
],
134+
});
135+
}}
136+
>
137+
Show Normal Toast
138+
</Button>
139+
<ToastProvider align="start">
140+
<Button
141+
onClick={() => {
142+
createToast({
143+
title: "Toast Title with Align Start",
144+
description: "This is a toast description with align start",
145+
type: "success",
146+
actions: [
147+
{
148+
label: "Action 1",
149+
altText: "Action 1 Alt Text",
150+
onClick: () => console.log("Action 1 clicked"),
151+
},
152+
{
153+
label: "Action 2",
154+
altText: "Action 2 Alt Text",
155+
onClick: () => console.log("Action 2 clicked"),
156+
},
157+
],
158+
align: "start",
159+
});
160+
}}
161+
>
162+
Show Toast with Align Start
163+
</Button>
164+
<Toast
165+
onClose={() => console.log("Toast closed")}
166+
title="Toast Title with Align Start without Button"
167+
duration={5000}
168+
description="This is a toast description with align start"
169+
type="success"
170+
actions={[
171+
{
172+
label: "Action 1",
173+
altText: "Action 1 Alt Text",
174+
onClick: () => console.log("Action 1 clicked"),
175+
type: "primary",
176+
},
177+
{
178+
label: "Action 2",
179+
altText: "Action 2 Alt Text",
180+
onClick: () => console.log("Action 2 clicked"),
181+
type: "secondary",
182+
},
183+
]}
184+
/>
185+
</ToastProvider>
111186
<BackgroundWrapper>
112187
<ProgressBar
113188
progress={100}

src/components/Toast/Toast.tsx

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import { keyframes, styled } from "styled-components";
55
import { toastsEventEmitter } from "./toastEmitter";
66

77
export interface ToastContextProps {
8-
createToast: (toast: ToastProps) => void;
8+
createToast: (toast: ToastProps, align?: ToastAlignment) => void;
99
}
1010
export const ToastContext = createContext<ToastContextProps>({
1111
createToast: () => null,
1212
});
1313

14+
export type ToastAlignment = "start" | "end";
1415
export type ToastType = "danger" | "warning" | "default" | "success";
1516
export interface ToastProps {
1617
id?: string;
@@ -20,6 +21,7 @@ export interface ToastProps {
2021
/** Time in milliseconds the toast will be visible */
2122
duration?: number;
2223
actions?: Array<ButtonProps & { altText: string }>;
24+
align?: ToastAlignment;
2325
}
2426

2527
const ToastIcon = styled(Icon)<{ $type?: ToastType }>`
@@ -99,6 +101,7 @@ const ToastHeader = styled(RadixUIToast.Title)`
99101

100102
const ToastDescriptionContainer = styled.div`
101103
display: flex;
104+
flex-direction: column;
102105
justify-content: space-between;
103106
width: 100%;
104107
align-items: flex-end;
@@ -123,7 +126,7 @@ const Title = styled.div`
123126
flex: 1;
124127
`;
125128

126-
const Toast = ({
129+
export const Toast = ({
127130
type,
128131
title,
129132
description,
@@ -167,8 +170,14 @@ const Toast = ({
167170
{actions.length > 0 && (
168171
<ToastDescriptionContent>
169172
{actions.map(({ altText, ...btnProps }) => (
170-
<RadixUIToast.Action altText={altText}>
171-
<Button {...btnProps} />
173+
<RadixUIToast.Action
174+
altText={altText}
175+
asChild
176+
key={altText}
177+
>
178+
<div>
179+
<Button {...btnProps} />
180+
</div>
172181
</RadixUIToast.Action>
173182
))}
174183
</ToastDescriptionContent>
@@ -179,7 +188,7 @@ const Toast = ({
179188
);
180189
};
181190

182-
const Viewport = styled(RadixUIToast.Viewport)<{ $align: "start" | "end" }>`
191+
const Viewport = styled(RadixUIToast.Viewport)<{ $align: ToastAlignment }>`
183192
--viewport-padding: 25px;
184193
position: fixed;
185194
bottom: 0;
@@ -203,23 +212,29 @@ const Viewport = styled(RadixUIToast.Viewport)<{ $align: "start" | "end" }>`
203212
outline: none;
204213
`;
205214

206-
export interface ToastProviderProps extends RadixUIToast.ToastProps {
207-
align?: "start" | "end";
215+
export interface ToastProviderProps extends RadixUIToast.ToastProviderProps {
216+
align?: ToastAlignment;
208217
}
209218

210219
export const ToastProvider = ({
211220
children,
212221
align = "end",
213222
...props
214223
}: ToastProviderProps) => {
215-
const [toasts, setToasts] = useState<Map<string, ToastProps>>(new Map());
224+
const [toasts, setToasts] = useState<Record<ToastAlignment, Map<string, ToastProps>>>({
225+
start: new Map(),
226+
end: new Map(),
227+
});
216228

217229
useEffect(() => {
218230
const listener = (toast: ToastProps) => {
219231
setToasts(currentToasts => {
220-
const newMap = new Map(currentToasts);
221-
newMap.set(toast?.id ?? String(Date.now()), toast);
222-
return newMap;
232+
const alignment = toast.align ?? "end";
233+
const newToasts = { ...currentToasts };
234+
const map = new Map(newToasts[alignment]);
235+
map.set(toast?.id ?? String(Date.now()), toast);
236+
newToasts[alignment] = map;
237+
return newToasts;
223238
});
224239
};
225240

@@ -231,30 +246,36 @@ export const ToastProvider = ({
231246
const onClose = (id: string) => (open: boolean) => {
232247
if (!open) {
233248
setToasts(currentToasts => {
234-
const newMap = new Map(currentToasts);
235-
newMap.delete(id);
236-
return newMap;
249+
const newToasts = { ...currentToasts };
250+
const map = new Map(newToasts[align]);
251+
map.delete(id);
252+
newToasts[align] = map;
253+
return newToasts;
237254
});
238255
}
239256
};
257+
240258
const value = {
241-
createToast: (toast: ToastProps) => {
259+
createToast: (toast: ToastProps, toastAlign: ToastAlignment = align) => {
242260
setToasts(currentToasts => {
243-
const newMap = new Map(currentToasts);
244-
newMap.set(toast?.id ?? String(Date.now()), toast);
245-
return newMap;
261+
const newToasts = { ...currentToasts };
262+
const map = new Map(newToasts[toastAlign]);
263+
map.set(toast?.id ?? String(Date.now()), toast);
264+
newToasts[toastAlign] = map;
265+
return newToasts;
246266
});
247267
},
248268
};
249269

250270
return (
251271
<RadixUIToast.Provider
252272
swipeDirection={align === "start" ? "left" : "right"}
273+
key={`toast-provider-${align}`}
253274
{...props}
254275
>
255276
<ToastContext.Provider value={value}>
256277
{children}
257-
{Array.from(toasts).map(([id, toast]) => (
278+
{Array.from(toasts[align]).map(([id, toast]) => (
258279
<Toast
259280
key={id}
260281
{...toast}

src/components/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,4 @@ export { createToast } from "./Toast/toastEmitter";
7171
export { UserIcon as ProfileIcon } from "./icons/UserIcon";
7272
export { default as VerticalStepper } from "./VerticalStepper/VerticalStepper";
7373
export { MultiAccordion } from "./MultiAccordion/MultiAccordion";
74-
export { ToastProvider } from "./Toast/Toast";
74+
export { ToastProvider, Toast } from "./Toast/Toast";

0 commit comments

Comments
 (0)