CoolFace
Apppublic

WaledRashed24/comics

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
use-toast.ts193 linesDownload Raw Back to ui
1// Inspired by react-hot-toast library2import * as React from "react"3 4import type {5  ToastActionElement,6  ToastProps,7} from "@/components/ui/toast"8 9const TOAST_LIMIT = 110const TOAST_REMOVE_DELAY = 100000011 12type ToasterToast = ToastProps & {13  id: string14  title?: React.ReactNode15  description?: React.ReactNode16  action?: ToastActionElement17}18 19const actionTypes = {20  ADD_TOAST: "ADD_TOAST",21  UPDATE_TOAST: "UPDATE_TOAST",22  DISMISS_TOAST: "DISMISS_TOAST",23  REMOVE_TOAST: "REMOVE_TOAST",24} as const25 26let count = 027 28function genId() {29  count = (count + 1) % Number.MAX_VALUE30  return count.toString()31}32 33type ActionType = typeof actionTypes34 35type Action =36  | {37      type: ActionType["ADD_TOAST"]38      toast: ToasterToast39    }40  | {41      type: ActionType["UPDATE_TOAST"]42      toast: Partial<ToasterToast>43    }44  | {45      type: ActionType["DISMISS_TOAST"]46      toastId?: ToasterToast["id"]47    }48  | {49      type: ActionType["REMOVE_TOAST"]50      toastId?: ToasterToast["id"]51    }52 53interface State {54  toasts: ToasterToast[]55}56 57const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()58 59const addToRemoveQueue = (toastId: string) => {60  if (toastTimeouts.has(toastId)) {61    return62  }63 64  const timeout = setTimeout(() => {65    toastTimeouts.delete(toastId)66    dispatch({67      type: "REMOVE_TOAST",68      toastId: toastId,69    })70  }, TOAST_REMOVE_DELAY)71 72  toastTimeouts.set(toastId, timeout)73}74 75export const reducer = (state: State, action: Action): State => {76  switch (action.type) {77    case "ADD_TOAST":78      return {79        ...state,80        toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),81      }82 83    case "UPDATE_TOAST":84      return {85        ...state,86        toasts: state.toasts.map((t) =>87          t.id === action.toast.id ? { ...t, ...action.toast } : t88        ),89      }90 91    case "DISMISS_TOAST": {92      const { toastId } = action93 94      // ! Side effects ! - This could be extracted into a dismissToast() action,95      // but I'll keep it here for simplicity96      if (toastId) {97        addToRemoveQueue(toastId)98      } else {99        state.toasts.forEach((toast) => {100          addToRemoveQueue(toast.id)101        })102      }103 104      return {105        ...state,106        toasts: state.toasts.map((t) =>107          t.id === toastId || toastId === undefined108            ? {109                ...t,110                open: false,111              }112            : t113        ),114      }115    }116    case "REMOVE_TOAST":117      if (action.toastId === undefined) {118        return {119          ...state,120          toasts: [],121        }122      }123      return {124        ...state,125        toasts: state.toasts.filter((t) => t.id !== action.toastId),126      }127  }128}129 130const listeners: Array<(state: State) => void> = []131 132let memoryState: State = { toasts: [] }133 134function dispatch(action: Action) {135  memoryState = reducer(memoryState, action)136  listeners.forEach((listener) => {137    listener(memoryState)138  })139}140 141type Toast = Omit<ToasterToast, "id">142 143function toast({ ...props }: Toast) {144  const id = genId()145 146  const update = (props: ToasterToast) =>147    dispatch({148      type: "UPDATE_TOAST",149      toast: { ...props, id },150    })151  const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })152 153  dispatch({154    type: "ADD_TOAST",155    toast: {156      ...props,157      id,158      open: true,159      onOpenChange: (open) => {160        if (!open) dismiss()161      },162    },163  })164 165  return {166    id: id,167    dismiss,168    update,169  }170}171 172function useToast() {173  const [state, setState] = React.useState<State>(memoryState)174 175  React.useEffect(() => {176    listeners.push(setState)177    return () => {178      const index = listeners.indexOf(setState)179      if (index > -1) {180        listeners.splice(index, 1)181      }182    }183  }, [state])184 185  return {186    ...state,187    toast,188    dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),189  }190}191 192export { useToast, toast }193