import { memo, useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { toast } from "sonner"; import { ClipboardPaste } from "lucide-react"; type Props = { open: boolean; onOpenChange: (v: boolean) => void; position: "above" | "below"; onConfirm: (name: string, values?: Record) => void | Promise; validate?: (name: string) => string | null; availableLanguages: string[]; }; function AddEntryModalImpl({ open, onOpenChange, position, onConfirm, validate, availableLanguages }: Props) { const [name, setName] = useState(""); const [err, setErr] = useState(null); const [saving, setSaving] = useState(false); const [clipboardValues, setClipboardValues] = useState | null>(null); const [useClipboard, setUseClipboard] = useState(false); useEffect(() => { if (open) { setName(""); setErr(null); setSaving(false); setUseClipboard(false); // 尝试读取剪贴板内容 checkClipboard(); } }, [open]); async function checkClipboard() { try { const text = await navigator.clipboard.readText(); const parsed = JSON.parse(text); // 验证是否是有效的语言值对象 if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) { const keys = Object.keys(parsed); const allStrings = keys.every(key => typeof parsed[key] === 'string'); if (allStrings && keys.length > 0) { setClipboardValues(parsed); setUseClipboard(true); return; } } } catch { // 剪贴板内容不是有效的 JSON 或无法访问,忽略 } setClipboardValues(null); setUseClipboard(false); } async function handleSubmit(e: React.FormEvent) { e.preventDefault(); const trimmed = name.trim(); if (!trimmed) return setErr("名称不能为空"); if (trimmed.includes(".")) return setErr("名称不能包含 '.'"); if (validate) { const msg = validate(trimmed); if (msg) return setErr(msg); } setSaving(true); try { const values = useClipboard && clipboardValues ? clipboardValues : undefined; await onConfirm(trimmed, values); onOpenChange(false); } catch (e) { setErr((e as Error)?.message ?? "操作失败"); } finally { setSaving(false); } } const title = position === "above" ? "在上方新增条目" : "在下方新增条目"; return ( { if (!saving) onOpenChange(v); }}> {title}
setName(e.target.value)} aria-label="名称" />
{clipboardValues && (
setUseClipboard(!!checked)} />
{useClipboard && (
检测到以下语言的翻译:
{Object.entries(clipboardValues).map(([lang, value]) => { const isAvailable = availableLanguages.includes(lang); return (
{lang} {!isAvailable && ( (项目中不存在) )}
{value}
); })}
{Object.keys(clipboardValues).some(lang => !availableLanguages.includes(lang)) && (
注意:部分语言在当前项目中不存在,这些值将被忽略
)}
)}
)} {err &&
{err}
}
); } export const AddEntryModal = memo(AddEntryModalImpl);