@activecollab/components
Version:
ActiveCollab Components
68 lines • 3.58 kB
TypeScript
import { ChangeEvent, FocusEvent, KeyboardEvent } from "react";
/**
* useInlineEdit — story-local click-to-edit state machine for StackedCard
* (task 4/8). Presentation-only; not exported from the design-system barrel.
*
* A card in preview/placeholder mode enters an EDITING state on click: a
* card-centered text field with confirm/cancel keyboard rules, an optional
* validator, and a single-line ↔ multi-line mode switch. The hook owns only the
* editing lifecycle (mode, working value, error, keyboard + outside-click); the
* card chrome and the async handoff after commit (e.g. the link unfurl ladder
* from task 3/8) live in the story that consumes it.
*
* ── The decided rules (AC task #454744) ──
* Single-line Enter confirms · click-outside confirms · Esc cancels + reverts
* Multi-line Enter = newline · Cmd/Ctrl+Enter saves · click-outside saves ·
* Esc cancels + reverts
* Validator validate(value) => true | errorMessage, run before every accept
* pass → commit + exit editing
* fail via a key → STAY editing, surface the message (retry)
* fail via outside → REVERT (acts as cancel) — user never trapped
*
* Keyboard is handled on the field's own keydown (never a global listener) and
* every keydown stops propagation, so the card focus model (task 2/8) can't
* swallow the Enter/Esc that belong to the editor.
*/
export type InlineEditValidator = (value: string) => true | string;
export interface UseInlineEditConfig {
/** Seed value for the first edit; also the revert target until first commit. */
initialValue?: string;
/** Multi-line mode: Enter inserts a newline, Cmd/Ctrl+Enter saves. */
multiline?: boolean;
/** Optional validator, run before every accept. `true` passes; a string fails. */
validate?: InlineEditValidator;
/** Called with the committed value when an accept passes validation. */
onCommit?: (value: string) => void;
}
/** Props to spread onto the `<input>` or `<textarea>` while editing. */
export interface InlineEditBind {
/** Callback ref — assignable to both an input and a textarea. */
ref: (el: HTMLInputElement | HTMLTextAreaElement | null) => void;
value: string;
onChange: (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
onKeyDown: (e: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
onBlur: (e: FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
"aria-invalid": boolean;
}
export interface InlineEditController {
/** Whether the field is currently shown. */
editing: boolean;
/** The working value (the draft being typed). */
value: string;
/** The last committed value — what a revert restores. */
committed: string;
/** The current validation message, or null when valid / untouched. */
error: string | null;
/** Whether this editor is in multi-line mode. */
multiline: boolean;
/** Enter editing, loading the last committed value. */
begin: () => void;
/** Cancel + revert to the committed value (the Esc / invalid-outside path). */
cancel: () => void;
/** Force a committed value without going through the field (repeat demos). */
reset: (next?: string) => void;
/** Spread onto the field element while `editing`. */
bind: InlineEditBind;
}
export declare const useInlineEdit: ({ initialValue, multiline, validate, onCommit, }?: UseInlineEditConfig) => InlineEditController;
//# sourceMappingURL=useInlineEdit.d.ts.map