@zag-js/steps
Version:
Core logic for the steps widget implemented as a state machine
202 lines (199 loc) • 5.52 kB
TypeScript
import { Machine, EventObject, Service } from '@zag-js/core';
import { PropTypes, RequiredBy, DirectionProperty, CommonProperties } from '@zag-js/types';
interface StepChangeDetails {
step: number;
}
interface StepInvalidDetails {
step: number;
action: "next" | "set";
targetStep?: number;
}
interface ElementIds {
root?: string | undefined;
list?: string | undefined;
triggerId?: ((index: number) => string) | undefined;
contentId?: ((index: number) => string) | undefined;
}
interface StepsProps extends DirectionProperty, CommonProperties {
/**
* The custom ids for the stepper elements
*/
ids?: ElementIds | undefined;
/**
* The controlled value of the stepper
*/
step?: number | undefined;
/**
* The initial value of the stepper when rendered.
* Use when you don't need to control the value of the stepper.
*/
defaultStep?: number | undefined;
/**
* Callback to be called when the value changes
*/
onStepChange?: ((details: StepChangeDetails) => void) | undefined;
/**
* Callback to be called when a step is completed
*/
onStepComplete?: VoidFunction | undefined;
/**
* If `true`, the stepper requires the user to complete the steps in order
*/
linear?: boolean | undefined;
/**
* The orientation of the stepper
* @default "horizontal"
*/
orientation?: "horizontal" | "vertical" | undefined;
/**
* The total number of steps
*/
count?: number | undefined;
/**
* Whether a step is valid. Invalid steps block forward navigation in linear mode.
* @default () => true
*/
isStepValid?: ((index: number) => boolean) | undefined;
/**
* Whether a step can be skipped during navigation.
* Skippable steps are bypassed when using next/prev.
* @default () => false
*/
isStepSkippable?: ((index: number) => boolean) | undefined;
/**
* Called when navigation is blocked due to an invalid step.
*/
onStepInvalid?: ((details: StepInvalidDetails) => void) | undefined;
}
type PropsWithDefault = "orientation" | "linear" | "count";
interface PrivateContext {
step: number;
}
type ComputedContext = Readonly<{
percent: number;
hasNextStep: boolean;
hasPrevStep: boolean;
completed: boolean;
}>;
interface StepsSchema {
props: RequiredBy<StepsProps, PropsWithDefault>;
context: PrivateContext;
computed: ComputedContext;
state: "idle";
event: EventObject;
action: string;
effect: string;
guard: string;
}
type StepsService = Service<StepsSchema>;
type StepsMachine = Machine<StepsSchema>;
interface ItemProps {
index: number;
}
interface ItemState {
/**
* The index of the step
*/
index: number;
/**
* The id of the trigger element
*/
triggerId: string;
/**
* The id of the content element
*/
contentId: string;
/**
* Whether the step is the current step
*/
current: boolean;
/**
* Whether the step is completed (index < current step)
*/
completed: boolean;
/**
* Whether the step is incomplete (index > current step)
*/
incomplete: boolean;
/**
* Whether the step is the last step
*/
last: boolean;
/**
* Whether the step is the first step
*/
first: boolean;
/**
* Whether the step can be skipped (based on `isStepSkippable` callback)
*/
skippable: boolean;
/**
* Lazy validation check - only evaluated when called
*/
isValid: () => boolean;
}
interface StepsApi<T extends PropTypes = PropTypes> {
/**
* The value of the stepper.
*/
value: number;
/**
* The percentage of the stepper.
*/
percent: number;
/**
* The total number of steps.
*/
count: number;
/**
* Whether the stepper has a next step.
*/
hasNextStep: boolean;
/**
* Whether the stepper has a previous step.
*/
hasPrevStep: boolean;
/**
* Whether the stepper is completed.
*/
isCompleted: boolean;
/**
* Check if a specific step is valid (lazy evaluation)
*/
isStepValid: (index: number) => boolean;
/**
* Check if a specific step can be skipped
*/
isStepSkippable: (index: number) => boolean;
/**
* Function to set the value of the stepper.
*/
setStep: (step: number) => void;
/**
* Function to go to the next step.
*/
goToNextStep: VoidFunction;
/**
* Function to go to the previous step.
*/
goToPrevStep: VoidFunction;
/**
* Function to go to reset the stepper.
*/
resetStep: VoidFunction;
/**
* Returns the state of the item at the given index.
*/
getItemState: (props: ItemProps) => ItemState;
getRootProps: () => T["element"];
getListProps: () => T["element"];
getItemProps: (props: ItemProps) => T["element"];
getTriggerProps: (props: ItemProps) => T["element"];
getContentProps: (props: ItemProps) => T["element"];
getNextTriggerProps: () => T["button"];
getPrevTriggerProps: () => T["button"];
getProgressProps: () => T["element"];
getIndicatorProps: (props: ItemProps) => T["element"];
getSeparatorProps: (props: ItemProps) => T["element"];
}
export type { ElementIds, ItemProps, ItemState, StepChangeDetails, StepInvalidDetails, StepsApi, StepsMachine, StepsProps, StepsSchema, StepsService };