InputsReact
A text shown in place that turns into a field when pressed.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<Editable.Root>
<Editable.Preview />
<Editable.Input />
</Editable.Root>
// Not in this example: Editable.CancelTrigger, Editable.EditTrigger, Editable.SubmitTriggerExamples
Multiline
A bio in a textarea with autoResize: Enter types a line break, Control+Enter or Meta+Enter saves, and the Preview keeps the line breaks.
Triggers
A project title with activationMode="dblclick": a double-click or the EditTrigger starts an edit, and the SubmitTrigger and CancelTrigger end it.
Form
A username in a Form: required refuses an empty save, cancel() in onValueChange refuses other characters, and the hidden input submits the text.
API reference
25 props across 6 parts, in React syntax. className, style, render are listed once, under Shared props.
CancelTriggerEditable.CancelTrigger
A button that ends the edit without a commit, rendered while editing. Pressing it keeps focus in the Input until it acts. Named "Cancel" and the Field label. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
| Data attribute | Description |
|---|---|
data-disabled | Present when the trigger is disabled. |
EditTriggerEditable.EditTrigger
A button that starts an edit, rendered while not editing. While it is mounted it is the component's tab stop, and the Preview leaves the tab order. Named "Edit" and the Field label. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
| Data attribute | Description |
|---|---|
data-disabled | Present when the trigger is disabled. |
InputEditable.Input
The text field that edits the value, rendered while editing in place of the Preview. Enter commits and Escape cancels; render={<textarea />} makes it multiline, where Ctrl+Enter or Cmd+Enter commits. Renders an <input>.
| Data attribute | Description |
|---|---|
data-editing | Present while the Input is shown. |
data-empty | Present when the value is empty. |
data-disabled | Present when the component is disabled. |
data-readonly | Present when the component is read-only. |
data-required | Present when the value may not be empty. |
data-autoresize | Present when the Input is sized to its content. |
data-valid | Present when the field is valid. |
data-invalid | Present when the field is invalid. |
data-dirty | Present when the value has changed. |
data-touched | Present when focus has left the component. |
data-filled | Present when the value is not empty. |
data-focused | Present while focus is inside the component. |
data-rejected | Present from a refused commit until the text changes. |
PreviewEditable.Preview
The value shown as a button that starts an edit, or the placeholder while the value is empty. Rendered while not editing. With activationMode="none" it is plain text. Renders a <span>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | false |
| Data attribute | Description |
|---|---|
data-editing | Present while the Input is shown. |
data-empty | Present when the value is empty. |
data-disabled | Present when the component is disabled. |
data-readonly | Present when the component is read-only. |
data-required | Present when the value may not be empty. |
data-autoresize | Present when the Input is sized to its content. |
data-valid | Present when the field is valid. |
data-invalid | Present when the field is invalid. |
data-dirty | Present when the value has changed. |
data-touched | Present when focus has left the component. |
data-filled | Present when the value is not empty. |
data-focused | Present while focus is inside the component. |
RootEditable.Root
Groups all parts of the editable: a text shown as a button that turns into a text field to edit it in place. with the hidden inputs, followed by the status region. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
valueThe committed text. | string | — |
defaultValueThe committed text when the component is initially rendered. | string | — |
onValueChangeEvent handler called once per commit that changes the text. | ((value: string, eventDetails: EditableRootChangeEventDetails) => void) | — |
inputValueThe text in the Input while editing. | string | — |
onInputValueChangeEvent handler called when the text in the Input changes, and with the value when an edit | ((inputValue: string, eventDetails: EditableRootInputValueChangeEventDetails) => void) | — |
editingWhether the Input is shown. | boolean | — |
defaultEditingWhether the Input is shown when the component is initially rendered. | boolean | false |
onEditingChangeEvent handler called when an edit starts or ends. | ((editing: boolean, eventDetails: EditableRootEditingChangeEventDetails) => void) | — |
activationModeWhat starts an edit from the Preview: a click, a double-click, focus, or nothing (only the | EditableActivationMode | 'click' |
submitModeWhat commits the draft: Enter, focus leaving the component, both, or neither (only the | EditableSubmitMode | 'both' |
selectOnFocusWhether the text is selected when an edit starts; otherwise the caret goes to the end. | boolean | true |
placeholderThe Preview's text while the value is empty, and the Input's placeholder unless it sets | string | — |
autoResizeWhether the Input is sized to its content. | boolean | false |
finalFocusWhere focus goes after an edit ends by a key, a trigger or actionsRef: true the Preview | boolean | React.RefObject<HTMLElement | null> | (() => HTMLElement | null | undefined) | — |
disabledWhether the component should ignore user interaction. | boolean | false |
readOnlyWhether the user should be unable to start an edit. | boolean | false |
requiredWhether the value may not be empty. | boolean | false |
nameIdentifies the field when a form is submitted. | string | — |
formIdentifies the form that owns the hidden inputs. | string | — |
actionsRefA ref to start, commit or cancel an edit from the app. | React.RefObject<EditableRootActions | null> | — |
labelsThe texts, in English by default: the triggers' names and the refusal announcement. | Partial<EditableLabels> | — |
| Data attribute | Description |
|---|---|
data-editing | Present while the Input is shown. |
data-empty | Present when the value is empty. |
data-disabled | Present when the component is disabled. |
data-readonly | Present when the component is read-only. |
data-required | Present when the value may not be empty. |
data-autoresize | Present when the Input is sized to its content. |
data-valid | Present when the field is valid. |
data-invalid | Present when the field is invalid. |
data-dirty | Present when the value has changed. |
data-touched | Present when focus has left the component. |
data-filled | Present when the value is not empty. |
data-focused | Present while focus is inside the component. |
SubmitTriggerEditable.SubmitTrigger
A button that commits the draft, rendered while editing. Pressing it keeps focus in the Input until it acts. Named "Save" and the Field label. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
| Data attribute | Description |
|---|---|
data-disabled | Present when the trigger is disabled. |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| EnterSpace | Starts an edit, with the text selected | Preview |
| Enter | Saves the text once, and does not submit the form | Input |
| ControlEnter | In a textarea, saves the text; Enter alone types a line break | Input |
| Escape | Cancels the edit and restores the text | Input |
| Tab | Leaving the Root saves the text; moving to Save or Cancel does not | Input |
| EnterSpace | Starts an edit | EditTrigger |
| EnterSpace | Saves the text | SubmitTrigger |
| EnterSpace | Cancels the edit and restores the text | CancelTrigger |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Preview | a span with role="button", named by the Field label and its own text, and described by the Field's description and error | a reader hears "Display name Ada Lovelace, button" and knows a press edits it |
| Preview | with activationMode="none", no role and no tabindex | text that only the EditTrigger edits is read as text, not as a button that does nothing |
| Input | an input of type text, or a textarea, named by the Field label; aria-invalid while a save is refused | the field is read with its label, and a refused save is heard on the field |
| EditTrigger | a button named "Edit" and the Field label; while it is shown it is the Tab stop and the Preview is at tabindex="-1" | the component is one Tab stop, and the button says what it edits |
| SubmitTrigger | a button named "Save" and the Field label; a mousedown on it is prevented | a press saves without first moving focus out of the Input |
| CancelTrigger | a button named "Cancel" and the Field label; a mousedown on it is prevented | a press cancels without first saving on blur |
| Root | one polite role="status" region after the Root, and with a name a hidden input with the committed text | a refused save is announced ("Not saved", or the Input's own message), and the form gets it |
Focus order
- The Preview is the Tab stop, or the EditTrigger when there is one; Tab onto the Preview starts nothing unless
activationModeisfocus. - While editing, the Input takes focus with its text selected, then Save and Cancel follow it in the Tab order.
- Focus leaving the Root saves the text, and Enter, Escape and the triggers return focus to the Tab stop.