import type { ReactNode } from "react"; /** * WHO OWNS THE DRAFT: a form (controlled, committing in its footer) or the field (self-committing). * Read off which callback was passed; `?: never` refuses both. Never flip one instance's mode: the * field remounts under the reader. */ export type CommitControls = "blur" | "buttons"; export interface Controlled { value: T; onValueChange: (next: T) => void; onSave?: never; controls?: never; actions?: never; } /** Async save, saving and error in place, Escape reverts, blur or Enter commits. */ export interface SelfCommitting { value: T; /** A throw shows the message and keeps the draft; an `AbortError` silently reverts to `value`. */ onSave: (next: T) => void | Promise; /** "blur" (default): blur or Enter commits, Escape reverts. "buttons": ✓/✕ own the exit. */ controls?: CommitControls; /** Rendered in both states so the width never changes; siblings of the control, never nested. */ actions?: ReactNode; onValueChange?: never; } export type Commit = Controlled | SelfCommitting; /** A form's field that carries verbs about its value beside its control — a stamp of now, a value offered * off another row — as a self-committing one does. */ export interface ControlledActing extends Omit, "actions"> { /** Siblings of the control, in the box the field then paints. Pass unconditionally and `disabled`, or the * slot's arrival shifts layout. */ actions?: ReactNode; } /** Either mode, each taking verbs beside the control. */ export type CommitActing = ControlledActing | SelfCommitting;