import { reactive, readonly } from 'vue'; export type DialogResult = { status: boolean; value: T }; export type DialogOption = { key: string; label: string; checked?: boolean; }; type BaseOptions = { /** * Title of the dialog */ title?: string; /** * Message to display in the dialog */ message?: string; /** * Text for the confirm button */ confirmText?: string; /** * Color for the confirm button. */ confirmColor?: 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'error' | 'neutral'; }; export type PromptOptions = BaseOptions & { /** * Text for the input field */ initial?: string; /** * Placeholder text for the input field */ placeholder?: string; /** * Text for the cancel button */ cancelText?: string; /** * Function to validate the input value * @returns true if valid, or an error message string if invalid */ validate?: (v: string) => true | string; }; export type ConfirmOptions = BaseOptions & { /** * Text for the confirm button */ cancelText?: string; /** * Optional checkbox-style options to return with the confirmation result. */ options?: DialogOption[]; }; export type AlertOptions = BaseOptions & {}; type QueueItem = { type: 'prompt' | 'confirm' | 'alert'; opts: PromptOptions | ConfirmOptions | AlertOptions; resolve: (r: DialogResult) => void; }; type DialogState = { current: QueueItem | null; queue: QueueItem[]; errorMsg: string | null; input: string; }; export const useDialog = () => { const raw = useState('dialog:state', () => reactive({ current: null, queue: [], errorMsg: null, input: '', } as DialogState), ); const state = raw.value; const _dequeue = () => { if (!state.current && state.queue.length) { state.current = state.queue.shift()!; state.errorMsg = null; state.input = state.current.type === 'prompt' ? ((state.current.opts as PromptOptions).initial ?? '') : ''; } }; const promptDialog = (opts: PromptOptions) => new Promise>((resolve) => { state.queue.push({ type: 'prompt', opts, resolve }); _dequeue(); }); const confirmDialog = (opts: ConfirmOptions) => new Promise | null>>((resolve) => { state.queue.push({ type: 'confirm', opts, resolve }); _dequeue(); }); const alertDialog = (opts: AlertOptions) => new Promise>((resolve) => { state.queue.push({ type: 'alert', opts, resolve }); _dequeue(); }); const confirm = (value?: string | Record) => { if (!state.current) return; if (state.current.type === 'prompt') { const val = 'string' === typeof value ? value : state.input; const v = (state.current.opts as PromptOptions).validate?.(val); if (v && v !== true) { state.errorMsg = v; return; } state.current.resolve({ status: true, value: val }); } else if (state.current.type === 'confirm') { state.current.resolve({ status: true, value: 'object' === typeof value && null !== value ? value : null, }); } else { state.current.resolve({ status: true, value: null }); } state.current = null; _dequeue(); }; const cancel = () => { if (!state.current) return; state.current.resolve({ status: false, value: null }); state.current = null; _dequeue(); }; return { promptDialog, confirmDialog, alertDialog, confirm, cancel, state: readonly(state) as Readonly, }; };