-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WEB-570] chore: toast refactor (#3836)
* new toast setup * chore: new toast implementation. * chore: move toast component to ui package. * chore: replace `setToast` with `setPromiseToast` in required places for better UX. * chore: code cleanup. * chore: update theme. * fix: theme switching issue. * chore: remove toast from issue update operations. * chore: add promise toast for add/ remove issue to cycle/ module and remove local spinners. --------- Co-authored-by: rahulramesha <rahulramesham@gmail.com>
- Loading branch information
1 parent
c06ef4d
commit 53367a6
Showing
167 changed files
with
1,827 additions
and
1,896 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import * as React from "react"; | ||
|
||
interface ICircularBarSpinner extends React.SVGAttributes<SVGElement> { | ||
height?: string; | ||
width?: string; | ||
className?: string | undefined; | ||
} | ||
|
||
export const CircularBarSpinner: React.FC<ICircularBarSpinner> = ({ | ||
height = "16px", | ||
width = "16px", | ||
className = "", | ||
}) => ( | ||
<div role="status"> | ||
<svg xmlns="http://www.w3.org/2000/svg" width={width} height={height} viewBox="0 0 24 24" className={className}> | ||
<g> | ||
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.14} /> | ||
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.29} transform="rotate(30 12 12)" /> | ||
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.43} transform="rotate(60 12 12)" /> | ||
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.57} transform="rotate(90 12 12)" /> | ||
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.71} transform="rotate(120 12 12)" /> | ||
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.86} transform="rotate(150 12 12)" /> | ||
<rect width={2} height={5} x={11} y={1} fill="currentColor" transform="rotate(180 12 12)" /> | ||
<animateTransform | ||
attributeName="transform" | ||
calcMode="discrete" | ||
dur="0.75s" | ||
repeatCount="indefinite" | ||
type="rotate" | ||
values="0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12;360 12 12" | ||
/> | ||
</g> | ||
</svg> | ||
</div> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from "./circular-spinner"; | ||
export * from "./circular-bar-spinner"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
import * as React from "react"; | ||
import { Toaster, toast } from "sonner"; | ||
// icons | ||
import { AlertTriangle, CheckCircle2, X, XCircle } from "lucide-react"; | ||
// spinner | ||
import { CircularBarSpinner } from "../spinners"; | ||
// helper | ||
import { cn } from "../../helpers"; | ||
|
||
export enum TOAST_TYPE { | ||
SUCCESS = "success", | ||
ERROR = "error", | ||
INFO = "info", | ||
WARNING = "warning", | ||
LOADING = "loading", | ||
} | ||
|
||
type SetToastProps = | ||
| { | ||
type: TOAST_TYPE.LOADING; | ||
title?: string; | ||
} | ||
| { | ||
id?: string | number; | ||
type: Exclude<TOAST_TYPE, TOAST_TYPE.LOADING>; | ||
title: string; | ||
message?: string; | ||
}; | ||
|
||
type PromiseToastCallback<ToastData> = (data: ToastData) => string; | ||
|
||
type PromiseToastData<ToastData> = { | ||
title: string; | ||
message?: PromiseToastCallback<ToastData>; | ||
}; | ||
|
||
type PromiseToastOptions<ToastData> = { | ||
loading?: string; | ||
success: PromiseToastData<ToastData>; | ||
error: PromiseToastData<ToastData>; | ||
}; | ||
|
||
type ToastContentProps = { | ||
toastId: string | number; | ||
icon?: React.ReactNode; | ||
textColorClassName: string; | ||
backgroundColorClassName: string; | ||
borderColorClassName: string; | ||
}; | ||
|
||
type ToastProps = { | ||
theme: "light" | "dark" | "system"; | ||
}; | ||
|
||
export const Toast = (props: ToastProps) => { | ||
const { theme } = props; | ||
return <Toaster visibleToasts={5} gap={20} theme={theme} />; | ||
}; | ||
|
||
export const setToast = (props: SetToastProps) => { | ||
const renderToastContent = ({ | ||
toastId, | ||
icon, | ||
textColorClassName, | ||
backgroundColorClassName, | ||
borderColorClassName, | ||
}: ToastContentProps) => | ||
props.type === TOAST_TYPE.LOADING ? ( | ||
<div | ||
onMouseDown={(e) => { | ||
e.stopPropagation(); | ||
e.preventDefault(); | ||
}} | ||
className={cn("w-[350px] h-[67.3px] rounded-lg border shadow-sm p-2", backgroundColorClassName, borderColorClassName)} | ||
> | ||
<div className="w-full h-full flex items-center justify-center px-4 py-2"> | ||
{icon && <div className="flex items-center justify-center">{icon}</div>} | ||
<div className={cn("w-full flex items-center gap-0.5 pr-1", icon ? "pl-4" : "pl-1")}> | ||
<div className={cn("grow text-sm font-semibold", textColorClassName)}>{props.title ?? "Loading..."}</div> | ||
<div className="flex-shrink-0"> | ||
<X | ||
className="text-toast-text-secondary hover:text-toast-text-tertiary cursor-pointer" | ||
strokeWidth={1.5} | ||
width={14} | ||
height={14} | ||
onClick={() => toast.dismiss(toastId)} | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
) : ( | ||
<div | ||
onMouseDown={(e) => { | ||
e.stopPropagation(); | ||
e.preventDefault(); | ||
}} | ||
className={cn( | ||
"relative flex flex-col w-[350px] rounded-lg border shadow-sm p-2", | ||
backgroundColorClassName, | ||
borderColorClassName | ||
)} | ||
> | ||
<X | ||
className="fixed top-2 right-2.5 text-toast-text-secondary hover:text-toast-text-tertiary cursor-pointer" | ||
strokeWidth={1.5} | ||
width={14} | ||
height={14} | ||
onClick={() => toast.dismiss(toastId)} | ||
/> | ||
<div className="w-full flex items-center px-4 py-2"> | ||
{icon && <div className="flex items-center justify-center">{icon}</div>} | ||
<div className={cn("flex flex-col gap-0.5 pr-1", icon ? "pl-6" : "pl-1")}> | ||
<div className={cn("text-sm font-semibold", textColorClassName)}>{props.title}</div> | ||
{props.message && <div className="text-toast-text-secondary text-xs font-medium">{props.message}</div>} | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
|
||
switch (props.type) { | ||
case TOAST_TYPE.SUCCESS: | ||
return toast.custom( | ||
(toastId) => | ||
renderToastContent({ | ||
toastId, | ||
icon: <CheckCircle2 width={28} height={28} strokeWidth={1.5} className="text-toast-text-success" />, | ||
textColorClassName: "text-toast-text-success", | ||
backgroundColorClassName: "bg-toast-background-success", | ||
borderColorClassName: "border-toast-border-success", | ||
}), | ||
props.id ? { id: props.id } : {} | ||
); | ||
case TOAST_TYPE.ERROR: | ||
return toast.custom( | ||
(toastId) => | ||
renderToastContent({ | ||
toastId, | ||
icon: <XCircle width={28} height={28} strokeWidth={1.5} className="text-toast-text-error" />, | ||
textColorClassName: "text-toast-text-error", | ||
backgroundColorClassName: "bg-toast-background-error", | ||
borderColorClassName: "border-toast-border-error", | ||
}), | ||
props.id ? { id: props.id } : {} | ||
); | ||
case TOAST_TYPE.WARNING: | ||
return toast.custom( | ||
(toastId) => | ||
renderToastContent({ | ||
toastId, | ||
icon: <AlertTriangle width={28} height={28} strokeWidth={1.5} className="text-toast-text-warning" />, | ||
textColorClassName: "text-toast-text-warning", | ||
backgroundColorClassName: "bg-toast-background-warning", | ||
borderColorClassName: "border-toast-border-warning", | ||
}), | ||
props.id ? { id: props.id } : {} | ||
); | ||
case TOAST_TYPE.INFO: | ||
return toast.custom( | ||
(toastId) => | ||
renderToastContent({ | ||
toastId, | ||
textColorClassName: "text-toast-text-info", | ||
backgroundColorClassName: "bg-toast-background-info", | ||
borderColorClassName: "border-toast-border-info", | ||
}), | ||
props.id ? { id: props.id } : {} | ||
); | ||
|
||
case TOAST_TYPE.LOADING: | ||
return toast.custom((toastId) => | ||
renderToastContent({ | ||
toastId, | ||
icon: <CircularBarSpinner className="text-toast-text-tertiary" />, | ||
textColorClassName: "text-toast-text-loading", | ||
backgroundColorClassName: "bg-toast-background-loading", | ||
borderColorClassName: "border-toast-border-loading", | ||
}) | ||
); | ||
} | ||
}; | ||
|
||
export const setPromiseToast = <ToastData,>( | ||
promise: Promise<ToastData>, | ||
options: PromiseToastOptions<ToastData> | ||
): void => { | ||
const tId = setToast({ type: TOAST_TYPE.LOADING, title: options.loading }); | ||
|
||
promise | ||
.then((data: ToastData) => { | ||
setToast({ | ||
type: TOAST_TYPE.SUCCESS, | ||
id: tId, | ||
title: options.success.title, | ||
message: options.success.message?.(data), | ||
}); | ||
}) | ||
.catch((data: ToastData) => { | ||
setToast({ | ||
type: TOAST_TYPE.ERROR, | ||
id: tId, | ||
title: options.error.title, | ||
message: options.error.message?.(data), | ||
}); | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.