diff --git a/apps/cyberstorm-remix/app/root.tsx b/apps/cyberstorm-remix/app/root.tsx index f0a4c1939..6e2fa32ac 100644 --- a/apps/cyberstorm-remix/app/root.tsx +++ b/apps/cyberstorm-remix/app/root.tsx @@ -29,7 +29,7 @@ import { } from "cyberstorm/security/publicEnvVariables"; import { useEffect, useRef, useState } from "react"; import { useHydrated } from "remix-utils/use-hydrated"; -import Toast from "@thunderstore/cyberstorm/src/components/Toast"; +import Toast from "@thunderstore/cyberstorm/src/newComponents/Toast"; import { SessionProvider, useSession } from "@thunderstore/ts-api-react"; import { Footer } from "./commonComponents/Footer/Footer"; import { NavigationWrapper } from "cyberstorm/navigation/NavigationWrapper"; diff --git a/packages/cyberstorm-forms/src/useFormToaster.ts b/packages/cyberstorm-forms/src/useFormToaster.ts index b4bfcb4c6..3e07ce1d8 100644 --- a/packages/cyberstorm-forms/src/useFormToaster.ts +++ b/packages/cyberstorm-forms/src/useFormToaster.ts @@ -1,4 +1,4 @@ -import { useToast } from "@thunderstore/cyberstorm/src/components/Toast/Provider"; +import { useToast } from "@thunderstore/cyberstorm/src/newComponents/Toast/Provider"; export type UseFormToasterArgs = { successMessage: string; @@ -17,15 +17,15 @@ export function useFormToaster({ return { onSubmitSuccess: () => { toast.addToast({ - variant: "success", - message: successMessage, + csVariant: "success", + children: successMessage, duration: 30000, }); }, onSubmitError: () => { toast.addToast({ - variant: "danger", - message: errorMessage + csVariant: "danger", + children: errorMessage ? errorMessage : "Unknown error occurred. The error has been logged", }); diff --git a/packages/cyberstorm-theme/src/components.tsx b/packages/cyberstorm-theme/src/components.tsx index 8f387b64a..ac90d7c19 100644 --- a/packages/cyberstorm-theme/src/components.tsx +++ b/packages/cyberstorm-theme/src/components.tsx @@ -89,6 +89,12 @@ export { type AlertSizes, AlertSizesList, } from "./components/Alert/Alert"; +export { + type ToastVariants, + ToastVariantsList, + type ToastSizes, + ToastSizesList, +} from "./components/Toast/Toast"; export { type MetaItemVariants, MetaItemVariantsList, diff --git a/packages/cyberstorm-theme/src/components/Toast/Toast.css b/packages/cyberstorm-theme/src/components/Toast/Toast.css new file mode 100644 index 000000000..9070351c2 --- /dev/null +++ b/packages/cyberstorm-theme/src/components/Toast/Toast.css @@ -0,0 +1,102 @@ +@layer theme-components { + /* SIZES */ + .ts-toast:where(.ts-size--medium) { + --toast-gap: var(--toast-md-gap); + --toast-padding-block: var(--toast-md-padding-block); + --toast-padding-inline: var(--toast-md-padding-inline); + --toast-font-size: var(--toast-md-font-size); + --toast-font-weight: var(--toast-md-font-weight); + --toast-line-height: var(--toast-md-line-height); + --toast-icon-width: var(--toast-md-icon-width); + --toast-icon-height: var(--toast-md-icon-height); + --toast-border-radius: var(--toast-md-border-radius); + --toast-close-width: var(--toast-md-close-width); + --toast-close-height: var(--toast-md-close-height); + --toast-close-icon-width: var(--toast-md-close-icon-width); + --toast-close-icon-height: var(--toast-md-close-icon-height); + } + + /* VARIANTS */ + .ts-toast:where(.ts-variant--danger) { + --toast-background-color: var(--toast-danger-bg-color--default); + --toast-border-color: var(--toast-danger-border-color--default); + --toast-icon-color: var(--toast-danger-icon-color--default); + --toast-color: var(--toast-danger-text-color--default); + --toast-close-icon-color: var(--toast-danger-close-color--default); + --toast-close-icon-color-hover: var(--toast-danger-close-color--hover); + } + + .ts-toast:where(.ts-variant--info) { + --toast-background-color: var(--toast-info-bg-color--default); + --toast-border-color: var(--toast-info-border-color--default); + --toast-icon-color: var(--toast-info-icon-color--default); + --toast-color: var(--toast-info-text-color--default); + --toast-close-icon-color: var(--toast-info-close-color--default); + --toast-close-icon-color-hover: var(--toast-info-close-color--hover); + } + + .ts-toast:where(.ts-variant--success) { + --toast-background-color: var(--toast-success-bg-color--default); + --toast-border-color: var(--toast-success-border-color--default); + --toast-icon-color: var(--toast-success-icon-color--default); + --toast-color: var(--toast-success-text-color--default); + --toast-close-icon-color: var(--toast-success-close-color--default); + --toast-close-icon-color-hover: var(--toast-success-close-color--hover); + } + + .ts-toast:where(.ts-variant--warning) { + --toast-background-color: var(--toast-warning-bg-color--default); + --toast-border-color: var(--toast-warning-border-color--default); + --toast-icon-color: var(--toast-warning-icon-color--default); + --toast-color: var(--toast-warning-text-color--default); + --toast-close-icon-color: var(--toast-warning-close-color--default); + --toast-close-icon-color-hover: var(--toast-warning-close-color--hover); + } + + /* TOKENS */ + :root { + --toast-danger-bg-color--default: var(--color-accent-red-3); + --toast-danger-border-color--default: var(--color-accent-red-7); + --toast-danger-close-color--default: var(--color-accent-red-7); + --toast-danger-close-color--hover: var(--color-accent-red-10); + --toast-danger-icon-color--default: var(--color-accent-red-7); + --toast-danger-text-color--default: var(--color-accent-red-11); + --toast-default-bg-color--default: var(--color-surface-6); + --toast-default-border-color--default: #5d5a9f; + --toast-default-close-color--default: var(--color-text-tertiary); + --toast-default-close-color--hover: var(--color-text-secondary); + --toast-default-icon-color--default: var(--color-text-accent); + --toast-default-text-color--default: var(--color-text-secondary); + --toast-info-bg-color--default: var(--color-accent-blue-3); + --toast-info-border-color--default: var(--color-accent-blue-7); + --toast-info-close-color--default: var(--color-accent-blue-7); + --toast-info-close-color--hover: var(--color-accent-blue-10); + --toast-info-icon-color--default: var(--color-accent-blue-7); + --toast-info-text-color--default: var(--color-accent-blue-11); + --toast-success-bg-color--default: var(--color-accent-green-3); + --toast-success-border-color--default: var(--color-accent-green-7); + --toast-success-close-color--default: var(--color-accent-green-7); + --toast-success-close-color--hover: var(--color-accent-green-10); + --toast-success-icon-color--default: var(--color-accent-green-7); + --toast-success-text-color--default: var(--color-accent-green-11); + --toast-warning-bg-color--default: var(--color-accent-yellow-3); + --toast-warning-border-color--default: var(--color-accent-yellow-7); + --toast-warning-close-color--default: var(--color-accent-yellow-7); + --toast-warning-close-color--hover: var(--color-accent-yellow-10); + --toast-warning-icon-color--default: var(--color-accent-yellow-7); + --toast-warning-text-color--default: var(--color-accent-yellow-11); + --toast-md-gap: var(--space-16); + --toast-md-padding-block: var(--space-16); + --toast-md-padding-inline: var(--space-24); + --toast-md-font-size: var(--font-size-body-md); + --toast-md-font-weight: var(--font-weight-regular); + --toast-md-line-height: var(--line-height-md); + --toast-md-icon-width: 1.125rem; + --toast-md-icon-height: 1.375rem; + --toast-md-border-radius: var(--radius-sm); + --toast-md-close-width: 1.875rem; + --toast-md-close-height: 1.875rem; + --toast-md-close-icon-width: 0.875rem; + --toast-md-close-icon-height: 0.875rem; + } +} diff --git a/packages/cyberstorm-theme/src/components/Toast/Toast.ts b/packages/cyberstorm-theme/src/components/Toast/Toast.ts new file mode 100644 index 000000000..72904b520 --- /dev/null +++ b/packages/cyberstorm-theme/src/components/Toast/Toast.ts @@ -0,0 +1,12 @@ +// Variants +export const ToastVariantsList = [ + "info", + "success", + "warning", + "danger", +] as const; +export type ToastVariants = "info" | "success" | "warning" | "danger"; + +// Sizes +export const ToastSizesList = ["medium"] as const; +export type ToastSizes = "medium"; diff --git a/packages/cyberstorm-theme/src/index.tsx b/packages/cyberstorm-theme/src/index.tsx index 9c1a20abf..ebd097257 100644 --- a/packages/cyberstorm-theme/src/index.tsx +++ b/packages/cyberstorm-theme/src/index.tsx @@ -12,6 +12,7 @@ import "./components/BreadCrumbs/BreadCrumbs.css"; import "./components/Select/Select.css"; import "./components/Table/Table.css"; import "./components/Tag/Tag.css"; +import "./components/Toast/Toast.css"; import "./components/Alert/Alert.css"; import "./components/MetaItem/MetaItem.css"; import "./components/Link/Link.css"; diff --git a/packages/cyberstorm/src/components/Toast/Toast.module.css b/packages/cyberstorm/src/components/Toast/Toast.module.css deleted file mode 100644 index 25159f214..000000000 --- a/packages/cyberstorm/src/components/Toast/Toast.module.css +++ /dev/null @@ -1,183 +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(--radius-sm); - 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 0.875rem 1.5rem; -} - -.closeIconWrapper { - position: absolute; - top: 0.5rem; - right: 0.5rem; - width: 1.875em; - height: 1.875em; - background-color: transparent; -} - -.closeIcon { - display: flex; - align-items: center; - justify-content: center; - width: 0.875em; - height: 0.875em; - color: var(--text-color); -} - -.progress { - width: 100%; - height: 0.125rem; - background-color: rgb(0 0 0 / 0.3); -} - -.progressBar { - height: 100%; - background-color: var(--feature-color); - animation: progress-bar var(--bar-duration) 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-body-lg); - 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 */ -.viewport { - --viewport-padding: 1.5rem; - - position: fixed; - bottom: 0; - left: 0; - z-index: 2147483647; - display: flex; - flex-direction: column; - gap: 0.625rem; - width: 390px; - max-width: 100vw; - margin: 0; - padding: var(--viewport-padding); - list-style: none; - outline: none; - - --radix-toast-swipe-end-x: -1.5rem; -} - -.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/Toast/Toast.tsx b/packages/cyberstorm/src/components/Toast/Toast.tsx deleted file mode 100644 index a6354dc5c..000000000 --- a/packages/cyberstorm/src/components/Toast/Toast.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { CSSProperties, ReactNode } from "react"; -import styles from "./Toast.module.css"; -import { Icon } from "../Icon/Icon"; -import { classnames } from "../../utils/utils"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faCircleCheck, - faCircleExclamation, - faBomb, - faTriangleExclamation, - faXmark, -} from "@fortawesome/free-solid-svg-icons"; -import * as RadixToast from "@radix-ui/react-toast"; - -export type ToastProps = { - id: string; - variant?: "info" | "danger" | "warning" | "success"; - message?: ReactNode; - duration?: number; -}; - -type Props = ToastProps & - Omit, keyof ToastProps>; - -export function Toast(props: Props) { - const { message, variant = "info", duration = 10000 } = props; - const durationCSS = { - "--bar-duration": `${duration / 1000}s`, - } as CSSProperties; - return ( - -
-
- -
- {getIcon(variant)} -
{message}
-
-
-
-
-
- - - - - -
-
- - ); -} - -const getStyle = (scheme: Props["variant"] = "info") => { - return { - info: styles.toast__info, - danger: styles.toast__danger, - warning: styles.toast__warning, - success: styles.toast__success, - }[scheme]; -}; - -const getIcon = (scheme: Props["variant"] = "info") => { - return { - info: , - danger: , - warning: , - success: , - }[scheme]; -}; diff --git a/packages/cyberstorm/src/index.ts b/packages/cyberstorm/src/index.ts index 1b7b1c573..3451f57cb 100644 --- a/packages/cyberstorm/src/index.ts +++ b/packages/cyberstorm/src/index.ts @@ -62,7 +62,6 @@ export { export { Title, type TitleProps } from "./components/Title/Title"; export { Tooltip, type TooltipProps } from "./components/Tooltip/Tooltip"; export { Alert, type AlertProps } from "./components/Alert/Alert"; -export { Toast } from "./components/Toast/Toast"; export { Avatar } from "./components/Avatar/Avatar"; export { Table, Sort } from "./components/Table/Table"; export { CommunityCard } from "./components/CommunityCard/CommunityCard"; @@ -107,6 +106,7 @@ export { Link as NewLink } from "./newComponents/Link/Link/Link"; export { Button as NewButton } from "./newComponents/Button/Button"; export { CycleButton } from "./newComponents/CycleButton/CycleButton"; export { BreadCrumbs as NewBreadCrumbs } from "./newComponents/BreadCrumbs/BreadCrumbs"; +export { Toast } from "./newComponents/Toast/Toast"; export { TextInput as NewTextInput } from "./newComponents/TextInput/TextInput"; export { Select as NewSelect, diff --git a/packages/cyberstorm/src/components/Toast/Provider.tsx b/packages/cyberstorm/src/newComponents/Toast/Provider.tsx similarity index 100% rename from packages/cyberstorm/src/components/Toast/Provider.tsx rename to packages/cyberstorm/src/newComponents/Toast/Provider.tsx diff --git a/packages/cyberstorm/src/newComponents/Toast/Toast.css b/packages/cyberstorm/src/newComponents/Toast/Toast.css new file mode 100644 index 000000000..4e570aa53 --- /dev/null +++ b/packages/cyberstorm/src/newComponents/Toast/Toast.css @@ -0,0 +1,122 @@ +@layer components { + .ts-toast { + position: relative; + display: flex; + + gap: var(--toast-gap); + align-items: flex-start; + align-self: stretch; + + border-left: 3px solid var(--toast-border-color); + border-radius: var(--toast-border-radius); + background-color: var(--toast-background-color); + padding-block: var(--toast-padding-block); + padding-inline: var(--toast-padding-inline); + + > .ts-toast__icon { + width: var(--toast-icon-width); + height: var(--toast-icon-height); + + --icon-color: var(--toast-icon-color); + } + + > .ts-toast__close { + position: absolute; + top: 0.5rem; + right: 0.5rem; + width: var(--toast-close-width); + height: var(--toast-close-height); + background: transparent; + + & > .ts-toast__close__icon { + width: var(--toast-close-icon-width); + height: var(--toast-close-icon-height); + + --icon-color: var(--toast-close-icon-color); + } + + & > .ts-toast__close__icon:hover { + --icon-color: var(--toast-close-icon-color-hover); + } + } + + > .ts-toast__content { + min-width: 0; + color: var(--toast-color); + font-weight: var(--toast-font-weight); + font-size: var(--toast-font-size); + line-height: var(--toast-line-height); + } + } + + .ts-toast__viewport { + --viewport-padding: 1.5rem; + + position: fixed; + bottom: 0; + left: 0; + z-index: 2147483647; + display: flex; + flex-direction: column; + gap: 0.625rem; + width: 390px; + max-width: 100vw; + margin: 0; + padding: var(--viewport-padding); + list-style: none; + outline: none; + + --radix-toast-swipe-end-x: -1.5rem; + } + + .ts-toast[data-state="open"] { + animation: slide-in 600ms cubic-bezier(0.16, 1, 0.3, 1); + } + + .ts-toast[data-state="closed"] { + animation: hide 200ms ease-in; + } + + .ts-toast[data-swipe="move"] { + transform: translateX(var(--radix-toast-swipe-move-x)); + } + + .ts-toast[data-swipe="cancel"] { + transform: translateX(0); + transition: transform 200ms ease-out; + } + + .ts-toast[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/newComponents/Toast/Toast.tsx b/packages/cyberstorm/src/newComponents/Toast/Toast.tsx new file mode 100644 index 000000000..0e31c91f2 --- /dev/null +++ b/packages/cyberstorm/src/newComponents/Toast/Toast.tsx @@ -0,0 +1,88 @@ +import React from "react"; +import "./Toast.css"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import * as RadixToast from "@radix-ui/react-toast"; +import { NewIcon, PrimitiveComponentDefaultProps } from "../.."; +import { + ToastVariants, + ToastSizes, +} from "@thunderstore/cyberstorm-theme/src/components"; +import { classnames, componentClasses } from "../../utils/utils"; + +import { + faCheckCircle, + faExclamationCircle, + faExclamationTriangle, +} from "@fortawesome/free-solid-svg-icons"; +import { + faOctagonExclamation, + faXmarkLarge, +} from "@fortawesome/pro-solid-svg-icons"; + +// export type ToastProps = { +// variant?: "info" | "danger" | "warning" | "success"; +// }; + +export interface ToastProps extends PrimitiveComponentDefaultProps { + csVariant?: ToastVariants; + csSize?: ToastSizes; + id: string; + duration?: number; +} + +export const Toast = React.forwardRef( + (props: ToastProps, forwardedRef) => { + const { + duration = 10000, + children, + rootClasses, + csVariant = "info", + csSize = "medium", + ...forwardedProps + } = props; + // const durationCSS = { + // "--bar-duration": `${duration / 1000}s`, + // } as CSSProperties; + + const icon = getIcon(csVariant); + + return ( + +
+ + + + {children} + + + + + +
+
+ ); + } +); + +Toast.displayName = "Toast"; + +const getIcon = (scheme: ToastProps["csVariant"] = "info") => { + return { + info: faExclamationCircle, + success: faCheckCircle, + warning: faExclamationTriangle, + danger: faOctagonExclamation, + }[scheme]; +}; diff --git a/packages/cyberstorm/src/components/Toast/Viewport.tsx b/packages/cyberstorm/src/newComponents/Toast/Viewport.tsx similarity index 82% rename from packages/cyberstorm/src/components/Toast/Viewport.tsx rename to packages/cyberstorm/src/newComponents/Toast/Viewport.tsx index 95f553034..bf619664b 100644 --- a/packages/cyberstorm/src/components/Toast/Viewport.tsx +++ b/packages/cyberstorm/src/newComponents/Toast/Viewport.tsx @@ -1,5 +1,5 @@ import { Toast, ToastProps } from "./Toast"; -import styles from "./Toast.module.css"; +import "./Toast.css"; import * as RadixToast from "@radix-ui/react-toast"; export function Viewport(props: { toasts: ToastProps[] }) { @@ -7,7 +7,7 @@ export function Viewport(props: { toasts: ToastProps[] }) { return ( -
+
{toasts.map((toast) => ( ))} diff --git a/packages/cyberstorm/src/components/Toast/index.ts b/packages/cyberstorm/src/newComponents/Toast/index.ts similarity index 100% rename from packages/cyberstorm/src/components/Toast/index.ts rename to packages/cyberstorm/src/newComponents/Toast/index.ts