@zag-js/editable
Version:
Core logic for the editable widget implemented as a state machine
212 lines (209 loc) • 6.31 kB
text/typescript
import { EventObject, Machine, Service } from '@zag-js/core';
import { InteractOutsideHandlers } from '@zag-js/interact-outside';
import { RequiredBy, DirectionProperty, CommonProperties, PropTypes } from '@zag-js/types';
interface ValueChangeDetails {
value: string;
}
interface EditChangeDetails {
edit: boolean;
}
type ActivationMode = "focus" | "dblclick" | "click" | "none";
type SubmitMode = "enter" | "blur" | "both" | "none";
type IntlTranslations = {
edit: string;
submit: string;
cancel: string;
input: string;
};
type ElementIds = Partial<{
root: string;
area: string;
label: string;
preview: string;
input: string;
control: string;
submitTrigger: string;
cancelTrigger: string;
editTrigger: string;
}>;
interface EditableProps extends DirectionProperty, CommonProperties, InteractOutsideHandlers {
/**
* The ids of the elements in the editable. Useful for composition.
*/
ids?: ElementIds | undefined;
/**
* Whether the input's value is invalid.
*/
invalid?: boolean | undefined;
/**
* The name attribute of the editable component. Used for form submission.
*/
name?: string | undefined;
/**
* The associate form of the underlying input.
*/
form?: string | undefined;
/**
* Whether the editable should auto-resize to fit the content.
*/
autoResize?: boolean | undefined;
/**
* The activation mode for the preview element.
*
* - "focus" - Enter edit mode when the preview is focused
* - "dblclick" - Enter edit mode when the preview is double-clicked
* - "click" - Enter edit mode when the preview is clicked
* - "none" - Edit can be triggered programmatically only
*
* @default "focus"
*/
activationMode?: ActivationMode | undefined;
/**
* The action that triggers submit in the edit mode:
*
* - "enter" - Trigger submit when the enter key is pressed
* - "blur" - Trigger submit when the editable is blurred
* - "none" - No action will trigger submit. You need to use the submit button
* - "both" - Pressing `Enter` and blurring the input will trigger submit
*
* @default "both"
*/
submitMode?: SubmitMode | undefined;
/**
* Whether to select the text in the input when it is focused.
* @default true
*/
selectOnFocus?: boolean | undefined;
/**
* Whether the editable is in edit mode.
*/
edit?: boolean | undefined;
/**
* Whether the editable is in edit mode by default.
*/
defaultEdit?: boolean | undefined;
/**
* Function to call when the edit mode changes.
*/
onEditChange?: ((details: EditChangeDetails) => void) | undefined;
/**
* The maximum number of characters allowed in the editable
*/
maxLength?: number | undefined;
/**
* Whether the editable is disabled.
*/
disabled?: boolean | undefined;
/**
* Whether the editable is read-only.
*/
readOnly?: boolean | undefined;
/**
* Whether the editable is required.
*/
required?: boolean | undefined;
/**
* The placeholder text for the editable.
*/
placeholder?: string | {
edit: string;
preview: string;
} | undefined;
/**
* The translations for the editable.
*/
translations?: IntlTranslations | undefined;
/**
* The element to receive focus when the editable is closed.
*/
finalFocusEl?: (() => HTMLElement | null) | undefined;
/**
* The controlled value of the editable.
*/
value?: string | undefined;
/**
* The initial value of the editable when rendered.
* Use when you don't need to control the value of the editable.
*/
defaultValue?: string | undefined;
/**
* Function to call when the value changes.
*/
onValueChange?: ((details: ValueChangeDetails) => void) | undefined;
/**
* Function to call when the value is reverted.
*/
onValueRevert?: ((details: ValueChangeDetails) => void) | undefined;
/**
* Function to call when the value is committed.
*/
onValueCommit?: ((details: ValueChangeDetails) => void) | undefined;
}
type PropsWithDefault = "activationMode" | "submitMode" | "selectOnFocus" | "translations";
interface EditableSchema {
props: RequiredBy<EditableProps, PropsWithDefault>;
state: "edit" | "preview";
context: {
value: string;
previousValue: string;
};
computed: {
isInteractive: boolean;
submitOnEnter: boolean;
submitOnBlur: boolean;
};
action: string;
effect: string;
event: EventObject;
guard: string;
}
type EditableService = Service<EditableSchema>;
type EditableMachine = Machine<EditableSchema>;
interface EditableApi<T extends PropTypes = PropTypes> {
/**
* Whether the editable is in edit mode
*/
editing: boolean;
/**
* Whether the editable value is empty
*/
empty: boolean;
/**
* The current value of the editable
*/
value: string;
/**
* The current value of the editable, or the placeholder if the value is empty
*/
valueText: string;
/**
* Function to set the value of the editable
*/
setValue: (value: string) => void;
/**
* Function to clear the value of the editable
*/
clearValue: VoidFunction;
/**
* Function to enter edit mode
*/
edit: VoidFunction;
/**
* Function to exit edit mode, and discard any changes
*/
cancel: VoidFunction;
/**
* Function to exit edit mode, and submit any changes
*/
submit: VoidFunction;
getRootProps: () => T["element"];
getAreaProps: () => T["element"];
getLabelProps: () => T["label"];
getInputProps: () => T["input"];
getPreviewProps: () => T["element"];
getEditTriggerProps: () => T["button"];
getControlProps: () => T["element"];
getSubmitTriggerProps: () => T["button"];
getCancelTriggerProps: () => T["button"];
}
export type { ActivationMode, EditChangeDetails, EditableApi, EditableMachine, EditableProps, EditableSchema, EditableService, ElementIds, IntlTranslations, SubmitMode, ValueChangeDetails };