Core logic for the dialog widget implemented as a state machine
The dialog state machine manages all dialog behaviors including state transitions, effects, and accessibility features. It provides a framework-agnostic foundation that can be connected to any UI framework.
Creates a configured dialog state machine with all necessary behaviors and effects.
/**
* Creates a dialog state machine with configuration and behaviors
* @param props - Configuration props for the dialog machine
* @returns Configured state machine instance
*/
function machine(props: Props): Machine;
interface Machine extends StateMachine<DialogSchema> {
/** Current state of the machine */
state: State;
/** Send events to the machine */
send: (event: MachineEvent) => void;
/** Access machine context */
context: Context;
/** Get prop values */
prop: (key: keyof Props) => any;
/** DOM scope for element access */
scope: Scope;
}Usage Example:
import { machine } from "@zag-js/dialog";
const dialogMachine = machine({
id: "my-dialog",
modal: true,
trapFocus: true,
onOpenChange: (details) => {
console.log("Dialog state changed:", details.open);
},
closeOnEscape: true,
closeOnInteractOutside: true
});Events that can be sent to the dialog machine to trigger state changes.
interface MachineEvent {
type:
| "OPEN" // Open the dialog
| "CLOSE" // Close the dialog
| "TOGGLE" // Toggle dialog state
| "CONTROLLED.OPEN" // Open via controlled prop
| "CONTROLLED.CLOSE"; // Close via controlled prop
/** Optional source information */
src?: string;
/** Previous event reference */
previousEvent?: MachineEvent;
}Usage Examples:
// Open dialog programmatically
machine.send({ type: "OPEN" });
// Close dialog programmatically
machine.send({ type: "CLOSE" });
// Toggle dialog state
machine.send({ type: "TOGGLE" });
// Close via interaction outside (internal use)
machine.send({ type: "CLOSE", src: "interact-outside" });The dialog machine has two primary states with different behaviors and effects.
type DialogState = "open" | "closed";
interface StateConfig {
/** State name */
value: DialogState;
/** Whether state matches given state(s) */
matches: (state: DialogState | DialogState[]) => boolean;
/** Active effects in this state */
effects?: string[];
/** Entry actions when entering state */
entry?: string[];
/** Available transitions from this state */
on?: Record<string, TransitionConfig>;
}State Behaviors:
Effects that run when the dialog is in the "open" state, providing accessibility and interaction features.
interface MachineEffects {
/** Track dismissible element for outside interactions */
trackDismissableElement: Effect;
/** Prevent background scrolling */
preventScroll: Effect;
/** Trap focus within dialog */
trapFocus: Effect;
/** Hide content below modal */
hideContentBelow: Effect;
}
interface Effect {
/** Effect cleanup function */
(): (() => void) | void;
}Effect Details:
preventScroll: true)trapFocus: true)aria-hidden (if modal: true)Actions that execute during state transitions to handle side effects and updates.
interface MachineActions {
/** Check if title and description elements are rendered */
checkRenderedElements: Action;
/** Synchronize z-index between positioner and backdrop */
syncZIndex: Action;
/** Invoke onOpenChange callback with open: false */
invokeOnClose: Action;
/** Invoke onOpenChange callback with open: true */
invokeOnOpen: Action;
/** Toggle visibility based on controlled open prop */
toggleVisibility: Action;
}
interface Action {
/** Action execution context */
(context: ActionContext): void;
}
interface ActionContext {
/** Machine context */
context: Context;
/** DOM scope */
scope: Scope;
/** Get prop value */
prop: (key: string) => any;
/** Send event to machine */
send: (event: MachineEvent) => void;
/** Current event being processed */
event: MachineEvent;
}Guards that determine whether state transitions should occur based on current conditions.
interface MachineGuards {
/** Check if open state is controlled via props */
isOpenControlled: Guard;
}
interface Guard {
/** Guard evaluation */
(context: GuardContext): boolean;
}
interface GuardContext {
/** Machine context */
context: Context;
/** Get prop value */
prop: (key: string) => any;
/** Current event */
event: MachineEvent;
}Guard Logic:
true if open prop is defined, indicating controlled modeInternal context maintained by the machine for tracking rendered elements and other state.
interface MachineContext {
/** Tracks which elements are currently rendered in DOM */
rendered: {
title: boolean;
description: boolean;
};
}The context is automatically updated by machine actions and used for proper ARIA labeling and descriptions.
Complete schema definition for the dialog state machine, used for type safety and validation.
interface DialogSchema {
/** Props type for the machine */
props: Props;
/** Available states */
state: "open" | "closed";
/** Machine context */
context: MachineContext;
/** Available guards */
guard: "isOpenControlled";
/** Available effects */
effect: "trackDismissableElement" | "preventScroll" | "trapFocus" | "hideContentBelow";
/** Available actions */
action: "checkRenderedElements" | "syncZIndex" | "invokeOnClose" | "invokeOnOpen" | "toggleVisibility";
/** Event types */
event: MachineEvent;
}Install with Tessl CLI
npx tessl i tessl/npm-zag-js--dialog