import { createContext, useCallback, useContext, useMemo, useState } from 'react' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import type { FC, ReactNode } from 'react' type DialogueVariant = 'default' | 'danger' type ConfirmOptions = { title: string description?: ReactNode confirmText?: string cancelText?: string variant?: DialogueVariant } type AlertOptions = { title: string description?: ReactNode okText?: string } type Choice = { value: T label: string variant?: DialogueVariant } type ChoiceOptions = { title: string description?: ReactNode choices: Choice[] cancelText?: string } type DialogueRequest = | { id: number kind: 'confirm' options: ConfirmOptions resolve: (value: boolean) => void } | { id: number kind: 'alert' options: AlertOptions resolve: () => void } | { id: number kind: 'choice' options: ChoiceOptions resolve: (value: string | null) => void } type DialogueAPI = { confirm: (options: ConfirmOptions) => Promise alert: (options: AlertOptions) => Promise choice: (options: ChoiceOptions) => Promise } const DialogueContext = createContext (null) let nextDialogueId = 1 type Props = { children: ReactNode } const DialogueProvider: FC = ({ children }) => { const [queue, setQueue] = useState ([]) const push = useCallback ((request: Omit) => { const id = nextDialogueId ++nextDialogueId setQueue (q => [...q, { ...request, id } as DialogueRequest]) }, []) const closeActive = useCallback ((result?: unknown) => { setQueue (q => { const [active, ...rest] = q if (!(active)) return rest switch (active.kind) { case 'confirm': active.resolve (Boolean (result)) break case 'alert': active.resolve () break case 'choice': active.resolve ((result ?? null) as string | null) break } return rest }) }, []) const api = useMemo (() => ({ confirm: options => new Promise (resolve => { push ({ kind: 'confirm', options, resolve }) }), alert: options => new Promise (resolve => { push ({ kind: 'alert', options, resolve }) }), choice: options => new Promise (resolve => { push ({ kind: 'choice', options: options as ChoiceOptions, resolve: resolve as (value: string | null) => void }) }) }), [push]) const active = queue[0] return ( {children} { if (!(open)) closeActive (active?.kind !== 'confirm' && null) }}> {active && ( {active.options.title} {active.options.description && (
{active.options.description}
)}
{active.kind === 'confirm' && ( <> )} {active.kind === 'alert' && ( )} {active.kind === 'choice' && ( <> {active.options.choices.map (choice => ( ))} )}
)}
) } export const useDialogue = () => { const dialogue = useContext (DialogueContext) if (!(dialogue)) throw new Error ('useDialogue must be used inside DialogueProvider') return dialogue } export default DialogueProvider