import { useState } from "react"; import { Input } from "./Input"; // A name being typed in place of the name itself — a library file's in its row, a folder's in the rail // or the page title, a new folder's. Enter or leaving the field saves, Escape cancels: unmounting the // input fires no blur in React, so Escape really does cancel. The caller decides what an unchanged or // empty name means. // // Ended by a KEY, the field leaves with the keyboard's focus and nothing to give it to: the caller // puts it back on what stands there now (the row, the entry, the button that opened it) — or the // keyboard user is dropped on and loses their place (measured in Chrome, the playlist rail's // rename). Ended by a blur, the user has put the focus somewhere on purpose, and it stays there. export function RenameField({ initial, label, className = "w-full", onDone, }: { initial: string; /** The field's accessible name (it has no visible label: it stands where the name stood). */ label: string; className?: string; /** The typed name, or null when cancelled; `byKey`: Enter or Escape, so the focus is the caller's * to put back. */ onDone: (name: string | null, byKey: boolean) => void; }) { const [draft, setDraft] = useState(initial); return ( e.currentTarget.select()} onChange={(e) => setDraft(e.target.value)} onBlur={() => onDone(draft, false)} onKeyDown={(e) => { // preventDefault: a real Enter's own activation would otherwise press what focus lands on. if (e.key === "Enter") { e.preventDefault(); onDone(draft, true); } if (e.key === "Escape") { e.preventDefault(); // Only the edit is cancelled: a drawer (the rail on a phone) or a dialog round the field // must not close with it. e.stopPropagation(); onDone(null, true); } }} /> ); }