Skip to content

Commit

Permalink
Fix Toast PR issues
Browse files Browse the repository at this point in the history
refs TS-1939
  • Loading branch information
Oksamies committed Nov 15, 2023
1 parent 4fb4b20 commit 71f5708
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 171 deletions.
67 changes: 67 additions & 0 deletions apps/cyberstorm-storybook/stories/components/Toast.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { StoryFn, Meta } from "@storybook/react";
import { Toast, ToastProps } from "@thunderstore/cyberstorm";
import React from "react";
import { v4 as uuid } from "uuid";

const meta = {
title: "Cyberstorm/Components/Toast",
component: Toast,
} as Meta<typeof Toast>;

const defaultArgs: ToastProps = {
id: uuid(),
message: "-",
variant: "info",
};

const Template: StoryFn<typeof Toast> = (args) => (
<div style={{ width: "500px" }}>
<Toast {...args} />
</div>
);

const MinimalToast = Template.bind({});
MinimalToast.args = {
...defaultArgs,
};

const InfoToast = Template.bind({});
InfoToast.args = {
...defaultArgs,
content:
"Lorem ipsum dolor sit amet, lollero pollero long ass text right here ellipsis just kidding it’s not that long.",
variant: "info",
};

const DangerToast = Template.bind({});
DangerToast.args = {
...defaultArgs,
content:
"Lorem ipsum dolor sit amet, lollero pollero long ass text right here ellipsis just kidding it’s not that long.",
variant: "danger",
};

const WarningToast = Template.bind({});
WarningToast.args = {
...defaultArgs,
content:
"Lorem ipsum dolor sit amet, lollero pollero long ass text right here ellipsis just kidding it’s not that long.",
variant: "warning",
};

const SuccessToast = Template.bind({});
SuccessToast.args = {
...defaultArgs,
content:
"Lorem ipsum dolor sit amet, lollero pollero long ass text right here ellipsis just kidding it’s not that long.",
variant: "success",
};

export {
meta as default,
MinimalToast,
InfoToast,
DangerToast,
WarningToast,
SuccessToast,
};
14 changes: 10 additions & 4 deletions packages/cyberstorm/src/components/Providers.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
"use client";
import * as RadixTooltip from "@radix-ui/react-tooltip";
import { ReactNode } from "react";
import { ToastProvider } from "./Toast/ToastContext";
import Toast from "./Toast";

interface CyberstormProvidersProps {
children: ReactNode | ReactNode[];
tooltipDelay?: number;
toastDuration?: number;
}

export function CyberstormProviders(props: CyberstormProvidersProps) {
return (
<ToastProvider>
<RadixTooltip.Provider delayDuration={80}>
<Toast.Provider
toastDuration={props.toastDuration ? props.toastDuration : 10000}
>
<RadixTooltip.Provider
delayDuration={props.toastDuration ? props.toastDuration : 80}
>
{props.children}
</RadixTooltip.Provider>
</ToastProvider>
</Toast.Provider>
);
}
71 changes: 71 additions & 0 deletions packages/cyberstorm/src/components/Toast/Provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { createContext, PropsWithChildren, useReducer } from "react";
import { v4 as uuid } from "uuid";
import * as RadixToast from "@radix-ui/react-toast";
import Toast from ".";
import { ToastProps } from "./Toast";

const initState: {
toasts: ToastProps[];
} = { toasts: [] };

const toastReducer = (
state: {
toasts: ToastProps[];
},
action: {
type: "add" | "delete";
toast: ToastProps;
}
) => {
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: (props: Omit<ToastProps, "id">) => void;
remove: (id: string) => void;
}

const ToastContext = createContext<ContextInterface | null>(null);

export function Provider(props: { toastDuration: number } & PropsWithChildren) {
const [state, dispatch] = useReducer(toastReducer, initState);

const addToast = (props: Omit<ToastProps, "id">) => {
const id = uuid();
dispatch({ type: "add", toast: { id, ...props } });
};

const remove = (id: string) => {
dispatch({ type: "delete", toast: { id } });
};

const value = {
addToast,
remove,
};

return (
<ToastContext.Provider value={value}>
<RadixToast.Provider swipeDirection="left" duration={props.toastDuration}>
{props.children}
<Toast.Viewport toasts={state.toasts} />
</RadixToast.Provider>
</ToastContext.Provider>
);
}
20 changes: 8 additions & 12 deletions packages/cyberstorm/src/components/Toast/Toast.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
align-items: flex-start;
align-self: stretch;
width: 100%;
padding: 1rem 3.25rem calc(1rem - 0.125rem) 1.5rem;
padding: 1rem 3.25rem 0.875rem 1.5rem;
}

.closeIconWrapper {
Expand All @@ -36,23 +36,19 @@

.closeIcon {
display: flex;
align-items: center;
justify-content: center;
width: 1em;
height: 1em;
color: var(--color-text--tertiary);
}

.toastProgress {
.progress {
width: 100%;
height: 0.125rem;
background-color: rgb(0 0 0 / 0.3);
}

.toastProgressBar {
.progressBar {
height: 100%;
background-color: var(--feature-color);
animation: progress-bar var(--bar-timer) linear forwards;
animation: progress-bar var(--bar-duration) linear forwards;
}

/* Styles for Toast's text */
Expand Down Expand Up @@ -109,24 +105,24 @@
}

/* CSS for the Toast container */
.toastContainer {
--viewport-padding: 25px;
.viewport {
--viewport-padding: 1.5rem;

position: fixed;
bottom: 0;
left: 0;
z-index: 2147483647;
display: flex;
flex-direction: column;
gap: 10px;
gap: 0.625rem;
width: 390px;
max-width: 100vw;
margin: 0;
padding: var(--viewport-padding);
list-style: none;
outline: none;

--radix-toast-swipe-end-x: -25px;
--radix-toast-swipe-end-x: -1.5rem;
}

.root[data-state="open"] {
Expand Down
59 changes: 37 additions & 22 deletions packages/cyberstorm/src/components/Toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,48 @@ import styles from "./Toast.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 {
faCircleCheck,
faCircleExclamation,
faOctagonExclamation,
faTriangleExclamation,
faXmarkLarge,
} from "@fortawesome/pro-solid-svg-icons";
import * as RadixToast from "@radix-ui/react-toast";

type _ToastProps = {
export type ToastProps = {
id: string;
content: ReactNode;
icon?: JSX.Element;
variant?: "info" | "danger" | "warning" | "success";
timer?: number;
message?: ReactNode;
duration?: number;
};
export type ToastProps = _ToastProps &
Omit<React.HTMLProps<HTMLDivElement>, keyof _ToastProps>;

export function Toast(props: ToastProps) {
const { content, icon, variant = "info", timer = 10000 } = props;
const timerCSS = {
"--bar-timer": `${timer / 1000}s`,
} as CSSProperties;
type Props = ToastProps &
Omit<React.HTMLProps<HTMLDivElement>, keyof ToastProps>;

export function Toast(props: Props) {
const { message, variant = "info", duration = 10000 } = props;
const durationCSS = {
"--bar-duration": `${duration / 1000}s`,
} as CSSProperties;
return (
<RadixToast.Root asChild duration={timer}>
<RadixToast.Root asChild duration={duration}>
<div className={classnames(styles.root)}>
<div className={classnames(styles.contentWrapper, getStyle(variant))}>
<RadixToast.Description asChild>
<div className={styles.content}>
<Icon wrapperClasses={styles.icon}>{icon}</Icon>
<div className={styles.message}>{content}</div>
<Icon wrapperClasses={styles.icon}>{getIcon(variant)}</Icon>
<div className={styles.message}>{message}</div>
</div>
</RadixToast.Description>
<div className={styles.toastProgress}>
<div className={styles.toastProgressBar} style={timerCSS} />
<div className={styles.progress}>
<div className={styles.progressBar} style={durationCSS} />
</div>
<RadixToast.Close className={styles.closeIconWrapper}>
<Icon iconClasses={styles.closeIcon}>
<RadixToast.Close
className={styles.closeIconWrapper}
aria-label="Close"
>
<Icon iconClasses={styles.closeIcon} inline>
<FontAwesomeIcon icon={faXmarkLarge} />
</Icon>
</RadixToast.Close>
Expand All @@ -47,13 +55,20 @@ export function Toast(props: ToastProps) {
);
}

Toast.displayName = "Toast";

const getStyle = (scheme: ToastProps["variant"] = "info") => {
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: <FontAwesomeIcon icon={faCircleExclamation} />,
danger: <FontAwesomeIcon icon={faOctagonExclamation} />,
warning: <FontAwesomeIcon icon={faTriangleExclamation} />,
success: <FontAwesomeIcon icon={faCircleCheck} />,
}[scheme];
};
35 changes: 0 additions & 35 deletions packages/cyberstorm/src/components/Toast/ToastContainer.tsx

This file was deleted.

Loading

0 comments on commit 71f5708

Please sign in to comment.