Initial commit
Next.js + Express event management app for Hope Family Church.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import * as React from "react"
|
||||
import type { ToastActionElement, ToastProps } from "@/components/ui/toast"
|
||||
|
||||
// Based on shadcn/ui toast hook implementation
|
||||
export type Toast = Omit<ToastProps, "id"> & {
|
||||
id: string
|
||||
title?: React.ReactNode
|
||||
description?: React.ReactNode
|
||||
action?: ToastActionElement
|
||||
}
|
||||
|
||||
const TOAST_LIMIT = 5
|
||||
const TOAST_REMOVE_DELAY = 1000
|
||||
|
||||
type State = {
|
||||
toasts: Toast[]
|
||||
}
|
||||
|
||||
type ToastInput = Omit<Toast, "id">
|
||||
|
||||
type Listener = (state: State) => void
|
||||
|
||||
let count = 0
|
||||
function genId() {
|
||||
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
||||
return count.toString()
|
||||
}
|
||||
|
||||
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
||||
|
||||
const state: State = { toasts: [] }
|
||||
const listeners: Listener[] = []
|
||||
|
||||
function setState(newState: Partial<State>) {
|
||||
Object.assign(state, newState)
|
||||
listeners.forEach((l) => l(state))
|
||||
}
|
||||
|
||||
function addToast(toast: ToastInput) {
|
||||
const id = genId()
|
||||
|
||||
const newToast: Toast = {
|
||||
...toast,
|
||||
id,
|
||||
open: true as any,
|
||||
}
|
||||
|
||||
// Ensure limit
|
||||
const nextToasts = [newToast, ...state.toasts].slice(0, TOAST_LIMIT)
|
||||
|
||||
setState({ toasts: nextToasts })
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
function updateToast(id: string, update: Partial<Toast>) {
|
||||
setState({
|
||||
toasts: state.toasts.map((t) => (t.id === id ? { ...t, ...update } : t)),
|
||||
})
|
||||
}
|
||||
|
||||
function dismissToast(id?: string) {
|
||||
if (id) {
|
||||
queueRemoval(id)
|
||||
updateToast(id, { open: false } as any)
|
||||
} else {
|
||||
state.toasts.forEach((t) => {
|
||||
queueRemoval(t.id)
|
||||
updateToast(t.id, { open: false } as any)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function removeToast(id?: string) {
|
||||
if (id) {
|
||||
setState({ toasts: state.toasts.filter((t) => t.id !== id) })
|
||||
} else {
|
||||
setState({ toasts: [] })
|
||||
}
|
||||
}
|
||||
|
||||
function queueRemoval(id: string) {
|
||||
if (toastTimeouts.has(id)) return
|
||||
const timeout = setTimeout(() => {
|
||||
toastTimeouts.delete(id)
|
||||
removeToast(id)
|
||||
}, TOAST_REMOVE_DELAY)
|
||||
toastTimeouts.set(id, timeout)
|
||||
}
|
||||
|
||||
export function useToast() {
|
||||
const [localState, setLocalState] = React.useState<State>(state)
|
||||
|
||||
React.useEffect(() => {
|
||||
listeners.push(setLocalState)
|
||||
return () => {
|
||||
const index = listeners.indexOf(setLocalState)
|
||||
if (index > -1) listeners.splice(index, 1)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return {
|
||||
...localState,
|
||||
toast: ({ ...props }: ToastInput) => addToast(props),
|
||||
dismiss: (id?: string) => dismissToast(id),
|
||||
remove: (id?: string) => removeToast(id),
|
||||
}
|
||||
}
|
||||
|
||||
export type { ToastActionElement, ToastProps }
|
||||
Reference in New Issue
Block a user