import { type Field } from '@byline/core'; import type { DocumentPatch } from '@byline/core/patches'; /** * Payload emitted by the form on Save. Carries the content (field data + * patches) alongside the document-grain system fields (path / advertised * locales) and per-bucket dirty flags so the host can route each piece to the * right write path — versioned for content, immediate/non-versioned for the * system fields. See docs/08-internationalization/index.md. */ export interface SystemFieldsSubmitPayload { data: any; patches: DocumentPatch[]; contentDirty: boolean; pathDirty: boolean; systemPath?: string | null; availableLocalesDirty: boolean; systemAvailableLocales?: string[]; } /** * Where a save has got to. Mutually exclusive by construction: the form cannot * be validating and uploading at once, which two independent booleans allowed. */ export type SubmissionPhase = { kind: 'idle'; } | { kind: 'validating'; } | { kind: 'uploading'; } | { kind: 'confirmingSystemFields'; payload: SystemFieldsSubmitPayload; } | { kind: 'submitting'; }; export interface UseFormSubmissionOptions { mode: 'create' | 'edit'; fields: Field[]; /** Persisted document id (edit mode only); feeds server-side upload hooks. */ documentId?: string; /** * Only collections that opted into the advertised-locale widget emit the * locale set, so the write path never touches * `byline_document_available_locales` for the others. */ advertiseLocales?: boolean; onSubmit: (payload: SystemFieldsSubmitPayload) => void | Promise; /** Mutations blocked (stale/lock/discarding). Read synchronously, per call. */ isBlocked: () => boolean; /** * Called immediately before the form becomes `inert` — once before uploads * and once before submission — so the caller can record the focused element * and restore it when busy ends. The DOM refs stay in the component; the * hook only signals the transition. */ onBeforeBusy?: () => void; } export interface UseFormSubmissionResult { phase: SubmissionPhase; isBusy: boolean; submit: () => Promise; confirmSystemFields: () => Promise; cancelSystemFields: () => void; } /** * Owns one save: validate → upload → (confirm) → submit. * * Admission is decided by a ref, not by `phase`. Rendered state is not a * re-entry guard: two `submit()` calls in the same turn both observe * `phase.kind === 'idle'` because React has not rerendered between them. The * ref is set synchronously at the top of `submit()` and cleared in `finally`, * spanning validation through submission — the whole window, not just the * final request. `phase` drives the UI; the ref decides admission. */ export declare function useFormSubmission(options: UseFormSubmissionOptions): UseFormSubmissionResult;