From 5d6716a33df891c90b50d46a456acaec2895550d Mon Sep 17 00:00:00 2001 From: Alina Date: Sat, 16 Mar 2024 00:29:59 +0400 Subject: [PATCH] refactor: rename interfaces, separate toast file, move remove functionality to store --- src/shared/ui/Button/index.tsx | 4 +- src/shared/ui/Toast/Toast.tsx | 63 +++++++++++++ src/shared/ui/Toast/ToastContext.tsx | 47 +++++++++ src/shared/ui/Toast/index.tsx | 136 +-------------------------- src/shared/ui/Toast/toastStore.ts | 1 + 5 files changed, 114 insertions(+), 137 deletions(-) create mode 100644 src/shared/ui/Toast/Toast.tsx create mode 100644 src/shared/ui/Toast/ToastContext.tsx diff --git a/src/shared/ui/Button/index.tsx b/src/shared/ui/Button/index.tsx index 525eae2..6e94cf0 100644 --- a/src/shared/ui/Button/index.tsx +++ b/src/shared/ui/Button/index.tsx @@ -4,13 +4,13 @@ import { ComponentChild } from 'preact' import styles from './styles.module.scss' -export type IButtonColors = 'accent' | 'success' | 'error' | 'info' +export type TButtonColor = 'accent' | 'success' | 'error' | 'info' export interface Props extends JSXInternal.HTMLAttributes { startIcon?: ComponentChild endIcon?: ComponentChild variant?: 'primary' | 'secondary' - color?: IButtonColors + color?: TButtonColor } export const Button = ({ diff --git a/src/shared/ui/Toast/Toast.tsx b/src/shared/ui/Toast/Toast.tsx new file mode 100644 index 0000000..54107b6 --- /dev/null +++ b/src/shared/ui/Toast/Toast.tsx @@ -0,0 +1,63 @@ +import cn from 'classnames' +import { ComponentChildren } from 'preact' + +import { browser } from '@shared/browser' + +import { Button, TButtonColor } from '../Button' +import { ClearIcon } from '../icons/ClearIcon' +import styles from './styles.module.scss' + +export type TToastType = 'error' | 'info' | 'success' +export interface IToast { + title: string + message?: ComponentChildren + details?: ComponentChildren + id: string + type: TToastType +} + +interface Props { + toast: IToast + removeToast: () => void + openDetails: (details: ComponentChildren) => void +} + +const mapToastTypeToButtonColor: Record = { + error: 'error', + info: 'info', + success: 'success', +} + +export const Toast = ({ toast, removeToast, openDetails }: Props) => { + const { title, message, details, type } = toast + + const toastClass = cn(styles.toast, styles[`toast--variant-${type}`]) + + return ( +
  • +
    +

    {title}

    + + {typeof message === 'string' ?

    {message}

    : message} + + {!!details && ( + + )} +
    + +
  • + ) +} diff --git a/src/shared/ui/Toast/ToastContext.tsx b/src/shared/ui/Toast/ToastContext.tsx new file mode 100644 index 0000000..3aac13e --- /dev/null +++ b/src/shared/ui/Toast/ToastContext.tsx @@ -0,0 +1,47 @@ +import { ComponentChildren, createContext } from 'preact' +import { useContext, useState } from 'preact/hooks' + +import { Modal } from '../Modal' +import { Toast } from './Toast' +import styles from './styles.module.scss' +import { IAddToastProps, useToastsStore } from './toastStore' + +const ToastDispatchContext = createContext<{ + addToast: (toast: IAddToastProps) => void +}>({ + addToast: () => { + throw Error('Not implemented') + }, +}) + +export const ToastProvider = ({ + children, +}: { + children: ComponentChildren +}) => { + const { toasts, addToast, removeToast } = useToastsStore() + const [details, setDetails] = useState(null) + + return ( + +
      + {toasts.map((toast) => ( + removeToast(toast.id)} + openDetails={setDetails} + /> + ))} +
    + + {children} + + setDetails(null)}> + {details} + +
    + ) +} + +export const useToast = () => useContext(ToastDispatchContext) diff --git a/src/shared/ui/Toast/index.tsx b/src/shared/ui/Toast/index.tsx index 14455e2..04e3cac 100644 --- a/src/shared/ui/Toast/index.tsx +++ b/src/shared/ui/Toast/index.tsx @@ -1,135 +1 @@ -import cn from 'classnames' -import { ComponentChildren, createContext } from 'preact' -import { - useCallback, - useContext, - useEffect, - useRef, - useState, -} from 'preact/hooks' - -import { browser } from '@shared/browser' - -import { Button, IButtonColors } from '../Button' -import { Modal } from '../Modal' -import { ClearIcon } from '../icons/ClearIcon' -import styles from './styles.module.scss' - -interface IToast { - title: string - message?: ComponentChildren - details?: ComponentChildren - id: string - type: 'error' | 'info' | 'success' -} - -type IAddToastProps = Omit - -const ToastDispatchContext = createContext<{ - addToast: (toast: IAddToastProps) => void -}>({ - addToast: () => { - throw Error('Not implemented') - }, -}) - -export const ToastProvider = ({ - children, -}: { - children: ComponentChildren -}) => { - const [details, setDetails] = useState(null) - const [toasts, setToasts] = useState([]) - - const addToast = (toast: IAddToastProps) => { - setToasts((prevToasts) => [ - { - id: new Date().toISOString(), - ...toast, - }, - ...prevToasts, - ]) - } - - const removeToast = useCallback((id: string) => { - setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id)) - }, []) - - return ( - -
      - {toasts.map((toast) => ( - removeToast(toast.id)} - openDetails={setDetails} - /> - ))} -
    - - {children} - - setDetails(null)}> - {details} - -
    - ) -} - -export const useToast = () => useContext(ToastDispatchContext) - -interface ToastProps { - toast: IToast - removeToast: () => void - openDetails: (details: ComponentChildren) => void -} - -const mapToastTypeToButtonColor: Record = { - error: 'error', - info: 'info', - success: 'success', -} - -export const Toast = ({ toast, removeToast, openDetails }: ToastProps) => { - const timerRef = useRef() - useEffect(() => { - timerRef.current = setTimeout(() => { - removeToast() - }, 3000) - - return () => clearTimeout(timerRef.current) - }, []) - - const { title, message, details, type } = toast - - const toastClass = cn(styles.toast, styles[`toast--variant-${type}`]) - - return ( -
  • -
    -

    {title}

    - - {typeof message === 'string' ?

    {message}

    : message} - - {!!details && ( - - )} -
    - -
  • - ) -} +export { ToastProvider, useToast } from './ToastContext' diff --git a/src/shared/ui/Toast/toastStore.ts b/src/shared/ui/Toast/toastStore.ts index c7d7dcb..aee6680 100644 --- a/src/shared/ui/Toast/toastStore.ts +++ b/src/shared/ui/Toast/toastStore.ts @@ -22,6 +22,7 @@ export const useToastsStore = () => { const removeToast = useCallback((id: string) => { setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id)) + clearTimeout(timersRef.current[id]) delete timersRef.current[id] }, [])