import { createContext, useCallback, useContext, useMemo, useRef, useState, type ReactNode } from 'react'; import { AlertCircle, AlertTriangle, CheckCircle2, Info, X } from 'lucide-react'; import { useAppStore } from '../../store/appStore'; import { LucentIconButton } from './LucentIconButton'; import './lucent-prompt.css'; type PromptTone = 'info' | 'success' | 'warning' | 'error'; interface NotifyOptions { title?: string; tone?: PromptTone; durationMs?: number; } interface ConfirmOptions { title?: string; message: string; tone?: PromptTone; confirmText?: string; cancelText?: string; } interface ToastItem { id: number; title?: string; message: string; tone: PromptTone; } interface ConfirmState { title?: string; message: string; tone: PromptTone; confirmText: string; cancelText: string; resolve: (value: boolean) => void; } interface LucentPromptApi { notify: (message: string, options?: NotifyOptions) => void; confirm: (options: ConfirmOptions) => Promise; } const LucentPromptContext = createContext(null); function ToneIcon({ tone }: { tone: PromptTone }) { if (tone === 'success') return ; if (tone === 'warning') return ; if (tone === 'error') return ; return ; } export function LucentPromptProvider({ children }: { children: ReactNode }) { const locale = useAppStore((s) => s.locale); const [toasts, setToasts] = useState([]); const [confirmState, setConfirmState] = useState(null); const idRef = useRef(1); const notify = useCallback((message: string, options?: NotifyOptions) => { const text = String(message || '').trim(); if (!text) return; const id = idRef.current++; const tone = options?.tone || 'info'; setToasts((prev) => [...prev, { id, title: options?.title, message: text, tone }]); const ttl = Math.max(1400, Math.min(options?.durationMs || 2600, 8000)); window.setTimeout(() => { setToasts((prev) => prev.filter((item) => item.id !== id)); }, ttl); }, []); const confirm = useCallback( (options: ConfirmOptions) => new Promise((resolve) => { const message = String(options?.message || '').trim(); if (!message) { resolve(false); return; } setConfirmState({ title: options?.title, message, tone: options?.tone || 'warning', confirmText: options?.confirmText || (locale === 'zh' ? '确定' : 'Confirm'), cancelText: options?.cancelText || (locale === 'zh' ? '取消' : 'Cancel'), resolve, }); }), [locale], ); const closeConfirm = useCallback( (value: boolean) => { setConfirmState((prev) => { if (prev) prev.resolve(value); return null; }); }, [], ); const value = useMemo( () => ({ notify, confirm, }), [notify, confirm], ); return ( {children}
{toasts.map((toast) => (
{toast.title ?
{toast.title}
: null}
{toast.message}
))}
{confirmState ? (
closeConfirm(false)}>
e.stopPropagation()}>
{confirmState.title || (locale === 'zh' ? '请确认操作' : 'Please Confirm')}
closeConfirm(false)} aria-label={locale === 'zh' ? '关闭' : 'Close'} tooltip={locale === 'zh' ? '关闭' : 'Close'} >
{confirmState.message}
) : null}
); } export function useLucentPrompt() { const ctx = useContext(LucentPromptContext); if (!ctx) { throw new Error('useLucentPrompt must be used inside LucentPromptProvider'); } return ctx; }