import { useState, useRef, useEffect } from "react"; import type { Author, Affiliation } from "./frontmatter-store"; /** * Add/Edit form for a single author, rendered as a native modal dialog. * * Affiliations are selected by their 1-based index (matching the store's * `author.affiliations` model). A free-text "new affiliation" field lets the * user create + attach an affiliation in one go; the caller persists it via * `onSubmit(author, newAffiliation)`. * * Extracted from FrontmatterHero so both the hero shortcut and the * AuthorsManager modal share one tested form. */ export function AuthorInlineForm({ open, initial, affiliations, onSubmit, onCancel, }: { open: boolean; initial?: Author; affiliations: Affiliation[]; onSubmit: (author: Author, newAffiliation?: Affiliation) => void; onCancel: () => void; }) { const [name, setName] = useState(initial?.name || ""); const [url, setUrl] = useState(initial?.url || ""); const [affIndices, setAffIndices] = useState(initial?.affiliations || []); const [newAffName, setNewAffName] = useState(""); const nameRef = useRef(null); const dialogRef = useRef(null); useEffect(() => { const dialog = dialogRef.current; if (!dialog) return; if (open && !dialog.open) { setName(initial?.name || ""); setUrl(initial?.url || ""); setAffIndices(initial?.affiliations || []); setNewAffName(""); dialog.showModal(); setTimeout(() => nameRef.current?.focus(), 50); } else if (!open && dialog.open) { dialog.close(); } }, [open, initial]); useEffect(() => { const dialog = dialogRef.current; if (!dialog) return; const onClose = () => onCancel(); dialog.addEventListener("close", onClose); return () => dialog.removeEventListener("close", onClose); }, [onCancel]); const handleSubmit = () => { if (!name.trim()) return; const newAff = newAffName.trim() ? { name: newAffName.trim() } : undefined; onSubmit( { name: name.trim(), url: url.trim() || undefined, affiliations: affIndices }, newAff, ); }; const handleCancel = () => { dialogRef.current?.close(); onCancel(); }; const toggleAff = (idx: number) => { setAffIndices((prev) => prev.includes(idx) ? prev.filter((i) => i !== idx) : [...prev, idx], ); }; return (

{initial ? "Edit author" : "Add author"}

setName(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleSubmit()} /> setUrl(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleSubmit()} />
{affiliations.length > 0 && (
{affiliations.map((aff, i) => ( toggleAff(i + 1)} > {i + 1}. {aff.name} ))}
)} setNewAffName(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleSubmit()} style={{ marginTop: 12 }} />
); }