diff --git a/packages/cyberstorm/src/components/Forms/CreateTeamForm/CreateTeamForm.tsx b/packages/cyberstorm/src/components/Forms/CreateTeamForm/CreateTeamForm.tsx index 0fec6fc16..1659e1cfb 100644 --- a/packages/cyberstorm/src/components/Forms/CreateTeamForm/CreateTeamForm.tsx +++ b/packages/cyberstorm/src/components/Forms/CreateTeamForm/CreateTeamForm.tsx @@ -12,7 +12,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faArrowsRotate } from "@fortawesome/free-solid-svg-icons"; import { useContext } from "react"; -import { ToastContext } from "../../NewToast/NewToastContext"; +import { ToastContext } from "../../Toast/ToastContext"; import { faCircleCheck, faCircleExclamation, diff --git a/packages/cyberstorm/src/components/NewToast/NewToast.module.css b/packages/cyberstorm/src/components/NewToast/NewToast.module.css deleted file mode 100644 index 5d7b440ee..000000000 --- a/packages/cyberstorm/src/components/NewToast/NewToast.module.css +++ /dev/null @@ -1,181 +0,0 @@ -.root { - position: relative; - display: flex; - flex-direction: column; - box-shadow: 0 12px 24px 5px rgb(0 0 0 / 0.65); -} - -.contentWrapper { - display: flex; - flex-direction: column; - width: 100%; - border-left: 3px solid var(--feature-color); - border-radius: var(--border-radius--4); - background-color: var(--background-color); - - --background-color: var(--alert-info-bg-color); - --feature-color: var(--alert-info-accent-color); - --text-color: var(--alert-info-text-color); -} - -.content { - display: flex; - gap: var(--space--16); - align-items: flex-start; - align-self: stretch; - width: 100%; - padding: 1rem 3.25rem calc(1rem - 0.125rem) 1.5rem; -} - -.closeIconWrapper { - position: absolute; - top: 0.5rem; - right: 0.5rem; - background-color: transparent; -} - -.closeIcon { - display: flex; - align-items: center; - justify-content: center; - width: 1em; - height: 1em; - color: var(--color-text--tertiary); -} - -.toastProgress { - width: 100%; - height: 0.125rem; - background-color: rgb(0 0 0 / 0.3); -} - -.toastProgressBar { - height: 100%; - background-color: var(--feature-color); - animation: progress-bar var(--bar-timer) linear forwards; -} - -/* Styles for Toast's text */ -.message { - display: flex; - flex: 1 0 0; - min-height: calc(1rem * var(--line-height--m)); - color: var(--text-color); - font-weight: var(--font-weight-regular); - font-size: var(--font-size--m); - line-height: var(--line-height--m); -} - -/* Styles for Toast's Icon */ -.icon { - width: var(--space--18); - height: var(--space--18); - color: var(--feature-color); -} - -/* Different Toast variants */ -.toast__info { - --background-color: var(--alert-info-bg-color); - --feature-color: var(--alert-info-accent-color); - --text-color: var(--alert-info-text-color); -} - -.toast__success { - --background-color: var(--alert-success-bg-color); - --feature-color: var(--alert-success-accent-color); - --text-color: var(--alert-success-text-color); -} - -.toast__warning { - --background-color: var(--alert-warning-bg-color); - --feature-color: var(--alert-warning-accent-color); - --text-color: var(--alert-warning-text-color); -} - -.toast__danger { - --background-color: var(--alert-danger-bg-color); - --feature-color: var(--alert-danger-accent-color); - --text-color: var(--alert-danger-text-color); -} - -@keyframes progress-bar { - 0% { - width: 100%; - } - - 100% { - width: 0%; - } -} - -/* CSS for the Toast container */ -.toastContainer { - --viewport-padding: 25px; - - position: fixed; - bottom: 0; - left: 0; - z-index: 2147483647; - display: flex; - flex-direction: column; - gap: 10px; - width: 390px; - max-width: 100vw; - margin: 0; - padding: var(--viewport-padding); - list-style: none; - outline: none; - - --radix-toast-swipe-end-x: -25px; -} - -.root[data-state="open"] { - animation: slide-in 600ms cubic-bezier(0.16, 1, 0.3, 1); -} - -.root[data-state="closed"] { - animation: hide 200ms ease-in; -} - -.root[data-swipe="move"] { - transform: translateX(var(--radix-toast-swipe-move-x)); -} - -.root[data-swipe="cancel"] { - transform: translateX(0); - transition: transform 200ms ease-out; -} - -.root[data-swipe="end"] { - animation: swipe-out 600ms ease-out; -} - -@keyframes hide { - from { - opacity: 1; - } - - to { - opacity: 0; - } -} - -@keyframes slide-in { - from { - transform: translateX(calc(-100% - var(--viewport-padding))); - } - - to { - transform: translateX(0); - } -} - -@keyframes swipe-out { - from { - transform: translateX(var(--radix-toast-swipe-end-x)); - } - - to { - transform: translateX(calc(-100% - var(--viewport-padding))); - } -} diff --git a/packages/cyberstorm/src/components/NewToast/NewToast.tsx b/packages/cyberstorm/src/components/NewToast/NewToast.tsx deleted file mode 100644 index df2c9aad5..000000000 --- a/packages/cyberstorm/src/components/NewToast/NewToast.tsx +++ /dev/null @@ -1,59 +0,0 @@ -"use client"; -import React, { CSSProperties, ReactNode } from "react"; -import styles from "./NewToast.module.css"; -import { Icon } from "../Icon/Icon"; -import { classnames } from "../../utils/utils"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons"; -import * as RadixToast from "@radix-ui/react-toast"; - -type _ToastProps = { - id: string; - content: ReactNode; - icon?: JSX.Element; - variant?: "info" | "danger" | "warning" | "success"; - timer?: number; -}; -export type ToastProps = _ToastProps & - Omit, keyof _ToastProps>; - -export function Toast(props: ToastProps) { - const { content, icon, variant = "info", timer = 10000 } = props; - const timerCSS = { - "--bar-timer": `${timer / 1000}s`, - } as CSSProperties; - - return ( - -
-
- -
- {icon} -
{content}
-
-
-
-
-
- - - - - -
-
- - ); -} - -Toast.displayName = "Toast"; - -const getStyle = (scheme: ToastProps["variant"] = "info") => { - return { - info: styles.toast__info, - danger: styles.toast__danger, - warning: styles.toast__warning, - success: styles.toast__success, - }[scheme]; -}; diff --git a/packages/cyberstorm/src/components/NewToast/NewToastContainer.tsx b/packages/cyberstorm/src/components/NewToast/NewToastContainer.tsx deleted file mode 100644 index 35233e58f..000000000 --- a/packages/cyberstorm/src/components/NewToast/NewToastContainer.tsx +++ /dev/null @@ -1,35 +0,0 @@ -"use client"; -import { Toast } from "./NewToast"; -import styles from "./NewToast.module.css"; -import * as RadixToast from "@radix-ui/react-toast"; - -export function ToastContainer(props: { - toasts: { - id: string; - variant?: "info" | "danger" | "warning" | "success"; - icon?: JSX.Element; - message?: string; - timer?: number; - }[]; -}) { - const { toasts } = props; - - return ( - -
- {toasts.map((toast) => ( - - ))} -
-
- ); -} - -ToastContainer.displayName = "ToastContainer"; diff --git a/packages/cyberstorm/src/components/NewToast/NewToastContext.tsx b/packages/cyberstorm/src/components/NewToast/NewToastContext.tsx deleted file mode 100644 index 198b57a45..000000000 --- a/packages/cyberstorm/src/components/NewToast/NewToastContext.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { createContext, PropsWithChildren, useReducer } from "react"; -import { ToastContainer } from "./NewToastContainer"; -import { v4 as uuid } from "uuid"; -import * as RadixToast from "@radix-ui/react-toast"; - -const initState: { - toasts: { - id: string; - variant?: "info" | "danger" | "warning" | "success"; - icon?: JSX.Element; - message?: string; - timer?: number; - }[]; -} = { toasts: [] }; - -const toastReducer = ( - state: { - toasts: { - id: string; - variant?: "info" | "danger" | "warning" | "success"; - icon?: JSX.Element; - message?: string; - timer?: number; - }[]; - }, - action: { - type: "add" | "delete"; - toast: { - id: string; - variant?: "info" | "danger" | "warning" | "success"; - icon?: JSX.Element; - message?: string; - timer?: number; - }; - } -) => { - switch (action.type) { - case "add": { - return { - toasts: [...state.toasts, action.toast], - }; - } - case "delete": { - const updatedToasts = state.toasts.filter( - (toast) => toast.id !== action.toast.id - ); - return { - toasts: updatedToasts, - }; - } - default: - throw new Error(`Unhandled action: ${action.type}`); - } -}; - -interface ContextInterface { - addToast: ( - variant?: "info" | "danger" | "warning" | "success", - icon?: JSX.Element, - message?: string, - timer?: number - ) => void; - remove: (id: string) => void; -} - -export const ToastContext = createContext(null); - -export function ToastProvider(props: PropsWithChildren) { - const [state, dispatch] = useReducer(toastReducer, initState); - - const addToast = ( - variant?: "info" | "danger" | "warning" | "success", - icon?: JSX.Element, - message?: string, - timer?: number - ) => { - const id = uuid(); - dispatch({ type: "add", toast: { id, variant, icon, message, timer } }); - }; - - const remove = (id: string) => { - dispatch({ type: "delete", toast: { id } }); - }; - - const value = { - addToast, - remove, - }; - - return ( - - - {props.children} - - - - ); -} diff --git a/packages/cyberstorm/src/components/Providers.tsx b/packages/cyberstorm/src/components/Providers.tsx index 56a140f03..99cf7f543 100644 --- a/packages/cyberstorm/src/components/Providers.tsx +++ b/packages/cyberstorm/src/components/Providers.tsx @@ -1,7 +1,7 @@ "use client"; import * as RadixTooltip from "@radix-ui/react-tooltip"; import { ReactNode } from "react"; -import { ToastProvider } from "./NewToast/NewToastContext"; +import { ToastProvider } from "./Toast/ToastContext"; interface CyberstormProvidersProps { children: ReactNode | ReactNode[]; diff --git a/packages/cyberstorm/src/components/Toast/Toast.module.css b/packages/cyberstorm/src/components/Toast/Toast.module.css index c3c92d415..5d7b440ee 100644 --- a/packages/cyberstorm/src/components/Toast/Toast.module.css +++ b/packages/cyberstorm/src/components/Toast/Toast.module.css @@ -27,6 +27,34 @@ padding: 1rem 3.25rem calc(1rem - 0.125rem) 1.5rem; } +.closeIconWrapper { + position: absolute; + top: 0.5rem; + right: 0.5rem; + background-color: transparent; +} + +.closeIcon { + display: flex; + align-items: center; + justify-content: center; + width: 1em; + height: 1em; + color: var(--color-text--tertiary); +} + +.toastProgress { + width: 100%; + height: 0.125rem; + background-color: rgb(0 0 0 / 0.3); +} + +.toastProgressBar { + height: 100%; + background-color: var(--feature-color); + animation: progress-bar var(--bar-timer) linear forwards; +} + /* Styles for Toast's text */ .message { display: flex; @@ -70,35 +98,6 @@ --text-color: var(--alert-danger-text-color); } -.closeIconWrapper { - position: absolute; - top: 0.5rem; - right: 0.5rem; -} - -.closeIcon { - display: flex; - align-items: center; - justify-content: center; - width: 1.875rem; - height: 1.875rem; -} - -.toastProgress { - width: 100%; - height: 0.125rem; - background-color: rgb(0 0 0 / 0.3); -} - -.toastProgressBar { - height: 100%; - background-color: var(--feature-color); -} - -.toastProgressBarTimer { - animation: progress-bar 10s linear forwards; -} - @keyframes progress-bar { 0% { width: 100%; @@ -109,40 +108,74 @@ } } +/* CSS for the Toast container */ .toastContainer { + --viewport-padding: 25px; + position: fixed; - bottom: 1rem; - left: 1rem; - z-index: 9999; + bottom: 0; + left: 0; + z-index: 2147483647; display: flex; flex-direction: column; - row-gap: 1rem; + gap: 10px; + width: 390px; + max-width: 100vw; + margin: 0; + padding: var(--viewport-padding); + list-style: none; + outline: none; + + --radix-toast-swipe-end-x: -25px; +} + +.root[data-state="open"] { + animation: slide-in 600ms cubic-bezier(0.16, 1, 0.3, 1); } -.rootFadeIn { - animation: fadein 0.3s linear forwards; +.root[data-state="closed"] { + animation: hide 200ms ease-in; } -.rootFadeOut { - animation: fadeout 0.3s linear forwards; +.root[data-swipe="move"] { + transform: translateX(var(--radix-toast-swipe-move-x)); } -@keyframes fadein { - 0% { opacity: 0; } - 100% { opacity: 1; } +.root[data-swipe="cancel"] { + transform: translateX(0); + transition: transform 200ms ease-out; } -@keyframes fadein { - 0% { opacity: 0; } - 100% { opacity: 1; } +.root[data-swipe="end"] { + animation: swipe-out 600ms ease-out; } -@keyframes fadeout { - 0% { opacity: 1; } - 100% { opacity: 0; } +@keyframes hide { + from { + opacity: 1; + } + + to { + opacity: 0; + } } -@keyframes fadeout { - 0% { opacity: 1; } - 100% { opacity: 0; } +@keyframes slide-in { + from { + transform: translateX(calc(-100% - var(--viewport-padding))); + } + + to { + transform: translateX(0); + } +} + +@keyframes swipe-out { + from { + transform: translateX(var(--radix-toast-swipe-end-x)); + } + + to { + transform: translateX(calc(-100% - var(--viewport-padding))); + } } diff --git a/packages/cyberstorm/src/components/Toast/Toast.tsx b/packages/cyberstorm/src/components/Toast/Toast.tsx index 76c57aaf1..80d914d43 100644 --- a/packages/cyberstorm/src/components/Toast/Toast.tsx +++ b/packages/cyberstorm/src/components/Toast/Toast.tsx @@ -1,83 +1,49 @@ "use client"; -import React, { ReactNode, useContext, useState } from "react"; +import React, { CSSProperties, ReactNode } from "react"; import styles from "./Toast.module.css"; import { Icon } from "../Icon/Icon"; import { classnames } from "../../utils/utils"; -import { Button } from "../.."; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons"; -import { ToastContext } from "./ToastContext"; +import * as RadixToast from "@radix-ui/react-toast"; type _ToastProps = { id: string; content: ReactNode; icon?: JSX.Element; variant?: "info" | "danger" | "warning" | "success"; - noTimer?: boolean; + timer?: number; }; export type ToastProps = _ToastProps & Omit, keyof _ToastProps>; export function Toast(props: ToastProps) { - const { id, content, icon, variant = "info", noTimer = false } = props; - const useToast = () => useContext(ToastContext); - const toast = useToast(); - const [isFadingOut, setIsFadingOut] = useState(false); - const [isFadingIn, setIsFadingIn] = useState(true); - - // Fade in - setTimeout(() => setIsFadingIn(false), 300); - - const removeToast = () => { - toast?.remove(id); - setIsFadingOut(false); - }; - const fadeOut = () => { - setIsFadingOut(true); - setTimeout(() => removeToast(), 300); - }; - - // Auto remove after 10 seconds - if (!noTimer) { - setTimeout(() => fadeOut(), 10000); - } + const { content, icon, variant = "info", timer = 10000 } = props; + const timerCSS = { + "--bar-timer": `${timer / 1000}s`, + } as CSSProperties; return ( -
-
-
- {icon} -
{content}
-
- - - - - + +
+
+ +
+ {icon} +
{content}
+
+
+
+
-
-
-
+ + + + +
-
+ ); } diff --git a/packages/cyberstorm/src/components/Toast/ToastContainer.tsx b/packages/cyberstorm/src/components/Toast/ToastContainer.tsx index d3532cda9..fb8160f41 100644 --- a/packages/cyberstorm/src/components/Toast/ToastContainer.tsx +++ b/packages/cyberstorm/src/components/Toast/ToastContainer.tsx @@ -1,6 +1,7 @@ "use client"; -import { Toast } from "../.."; +import { Toast } from "./Toast"; import styles from "./Toast.module.css"; +import * as RadixToast from "@radix-ui/react-toast"; export function ToastContainer(props: { toasts: { @@ -8,24 +9,26 @@ export function ToastContainer(props: { variant?: "info" | "danger" | "warning" | "success"; icon?: JSX.Element; message?: string; - noTimer?: boolean; + timer?: number; }[]; }) { const { toasts } = props; return ( -
- {toasts.map((toast) => ( - - ))} -
+ +
+ {toasts.map((toast) => ( + + ))} +
+
); } diff --git a/packages/cyberstorm/src/components/Toast/ToastContext.tsx b/packages/cyberstorm/src/components/Toast/ToastContext.tsx index 148a33510..386f1a569 100644 --- a/packages/cyberstorm/src/components/Toast/ToastContext.tsx +++ b/packages/cyberstorm/src/components/Toast/ToastContext.tsx @@ -1,6 +1,7 @@ import { createContext, PropsWithChildren, useReducer } from "react"; import { ToastContainer } from "./ToastContainer"; import { v4 as uuid } from "uuid"; +import * as RadixToast from "@radix-ui/react-toast"; const initState: { toasts: { @@ -8,7 +9,7 @@ const initState: { variant?: "info" | "danger" | "warning" | "success"; icon?: JSX.Element; message?: string; - noTimer?: boolean; + timer?: number; }[]; } = { toasts: [] }; @@ -19,7 +20,7 @@ const toastReducer = ( variant?: "info" | "danger" | "warning" | "success"; icon?: JSX.Element; message?: string; - noTimer?: boolean; + timer?: number; }[]; }, action: { @@ -29,7 +30,7 @@ const toastReducer = ( variant?: "info" | "danger" | "warning" | "success"; icon?: JSX.Element; message?: string; - noTimer?: boolean; + timer?: number; }; } ) => { @@ -57,7 +58,7 @@ interface ContextInterface { variant?: "info" | "danger" | "warning" | "success", icon?: JSX.Element, message?: string, - noTimer?: boolean + timer?: number ) => void; remove: (id: string) => void; } @@ -71,10 +72,10 @@ export function ToastProvider(props: PropsWithChildren) { variant?: "info" | "danger" | "warning" | "success", icon?: JSX.Element, message?: string, - noTimer?: boolean + timer?: number ) => { const id = uuid(); - dispatch({ type: "add", toast: { id, variant, icon, message, noTimer } }); + dispatch({ type: "add", toast: { id, variant, icon, message, timer } }); }; const remove = (id: string) => { @@ -88,8 +89,10 @@ export function ToastProvider(props: PropsWithChildren) { return ( - - {props.children} + + {props.children} + + ); }