Varendo

FormFlow

Task-based steps, draft continuity, validation and reversible navigation before commit.

FamilycompoundLicenseApache-2.0
StandaloneAvailable

Use the web element directly without the semantic runtime.

Semantic bindingNo built-in binding

Use this component directly or compose it in application code.

Automatic adaptationNo standard recipe

The standard recipe resolver does not select this component automatically.

Import and live example

A two-step identity/review flow with a slotted field for each step.

Interactive preview requires JavaScript.

View the complete example source
import {registerAeliqoElements} from "@varendo/web";
import {
  AeliqoBreakdownElement,
  AeliqoComparisonElement,
  AeliqoExplorerElement,
  AeliqoFormFlowElement,
  AeliqoInvestigationElement,
  AeliqoQualityPanelElement,
  AeliqoRecordEditorElement,
  AeliqoSearchResultsElement,
  } from "@varendo/web/compound";

import {AeliqoDateRangeElement, AeliqoTextFieldElement} from "@varendo/web/inputs";
import type {ResultRef, Scalar} from "@varendo/core";
import type {VisualizationBindingContext, VisualizationSpec} from "@varendo/core/visualization";
import type {AeliqoDataColumn, AeliqoDataScope, AeliqoFieldOption} from "@varendo/web/data";
import type {VisualizationDataset} from "@varendo/web/visualization";

const catalogRef: ResultRef = {
  id: "aeliqo-catalog-example",
  revision: "1",
  outputId: "people",
  queryDigest: "catalog-query",
  scopeDigest: "catalog-scope",
};
const catalogRows: readonly Readonly<Record<string, Scalar>>[] = [
  {id: "ada", name: "Ada Lovelace", team: "Research", date: "2026-09-08", amount: 120},
  {id: "lin", name: "Lin Chen", team: "Product", date: "2026-09-09", amount: 96},
  {id: "grace", name: "Grace Hopper", team: "Research", date: "2026-09-10", amount: 144},
];
const catalogColumns: readonly AeliqoDataColumn[] = [
  {key: "id", label: "ID", type: "text", sortable: true},
  {key: "name", label: "Name", type: "text", sortable: true},
  {key: "team", label: "Team", type: "text"},
  {key: "date", label: "Date", type: "date", sortable: true},
  {key: "amount", label: "Amount", type: "integer", align: "end"},
];
const catalogFields: readonly AeliqoFieldOption[] = [
  {id: "name", label: "Name", type: "text"},
  {id: "team", label: "Team", type: "text"},
  {id: "amount", label: "Amount", type: "integer"},
];
const catalogScope: AeliqoDataScope = {loaded: catalogRows.length, filteredTotal: catalogRows.length, populationDigest: catalogRef.scopeDigest, kind: "filtered", label: "Authorized people"};
const catalogVisualizationContext: VisualizationBindingContext = {results: []};
const catalogVisualizationDataset: VisualizationDataset = {result: catalogRef, rows: catalogRows};
const catalogVisualizationSpecs: Readonly<Record<"trend", VisualizationSpec>> = {trend: {version: "1", view: "trend", plot: {version: "1", root: {kind: "unit", mark: "line", result: catalogRef, missing: "gap", encoding: {x: {field: "date", scale: "temporal"}, y: {field: "amount", scale: "linear"}}}}}};

registerAeliqoElements();

const host = document.querySelector<HTMLElement>("#aeliqo-example") ?? (() => {
  const created = document.createElement("div");
  created.id = "aeliqo-example";
  (document.body ?? document.documentElement).append(created);
  return created;
})();

function createCatalogRoot(container: HTMLElement): HTMLElement {
  const root = document.createElement("div");
  root.dataset.catalogSourceRoot = "true";
  root.style.display = "grid";
  root.style.gap = "12px";
  container.append(root);
  return root;
}

function createCatalogElement<T extends HTMLElement>(tagName: string, container: HTMLElement): T {
  const element = document.createElement(tagName) as T;
  element.setAttribute("data-aeliqo-theme", "light");
  container.append(element);
  return element;
}

function appendSlottedText(root: HTMLElement, slot: string, textContent: string): void {
  const content = document.createElement("span");
  content.slot = slot;
  content.textContent = textContent;
  root.append(content);
}

const root = createCatalogRoot(host);
(((root) => {
		const element = createCatalogElement<AeliqoFormFlowElement>("aeliqo-form-flow", root);
		element.steps = [{
			id: "identity",
			label: "Identity",
			fieldNames: ["name"]
		}, {
			id: "review",
			label: "Review",
			fieldNames: ["period"]
		}];
		element.activeStep = "identity";
		element.draft = { name: "Ada Lovelace" };
		const identity = document.createElement("div");
		identity.slot = "step-identity";
		const name = document.createElement("aeliqo-text-field") as AeliqoTextFieldElement;
		name.label = "Name";
		name.name = "name";
		name.value = "Ada Lovelace";
		identity.append(name);
		const review = document.createElement("div");
		review.slot = "step-review";
		review.textContent = "Review the authorized values before commit.";
		element.append(identity, review);
	}))(root);

Purpose

FormFlow walks users through named steps while retaining field drafts across navigation and validating each step before commit. The host supplies draft and activeStep and owns durable persistence.

When to use it

When to use a different component

Properties and defaults

Draft and validation are host-visible; step transitions and commit are typed requests with preserved focus.

Properties, ownership, and initial values
PropertyUse and ownershipDeclared typeInitial value
stepsSupplies the ordered form-flow steps with stable IDs and labels.readonly AeliqoFormFlowStep[][]
activeStepSupplies active step to the component as application-owned input.string''
draftSupplies the current multi-step form draft. The host remains the source of truth across steps.Readonly<Record<string, FormDraftValue>>{}
validationSupplies authoritative validation results for the current form-flow draft.Readonly<Record<string, string | undefined>>{}
statusSupplies the explicit ready, loading, empty, partial, stale, or error state.AeliqoCompoundStatus'ready'
messageProvides the user-facing status, error, or recovery message.string''
titleProvides the visible title for the current data or compound surface.string'Form'
nextLabelProvides the visible text for the next control or section.string'Next'
backLabelProvides the visible text for the back control or section.string'Back'
commitLabelProvides the visible text for the commit control or section.string'Commit'

Events

Events, payloads, timing, and application responsibilities
EventPayloadEmittedApplication responsibility
aeliqo-form-flow-stepThe proposed next step ID and current draft.After Back or Next is activated.Validate the draft and update activeStep if the transition is accepted.
aeliqo-form-flow-commitThe current draft, validation evidence, and step context.After the user commits the final valid step.Authorize and execute the registered action; report pending, success, or failure.

Events cross the component boundary as user proposals. Validate their detail and current authority before applying a business effect.

Listen for aeliqo-form-flow-step

TypeScript example
const element = document.querySelector('aeliqo-form-flow');<p>element?.addEventListener('aeliqo-form-flow-step', (event) => {
  const proposal = (event as CustomEvent<unknown>).detail;
  // Validate the proposal, update host state, then assign controlled properties.
  applyProposal(proposal);
});

Listen for aeliqo-form-flow-commit

TypeScript example
const element = document.querySelector('aeliqo-form-flow');</p>
<p>element?.addEventListener('aeliqo-form-flow-commit', (event) => {
  const proposal = (event as CustomEvent<unknown>).detail;
  // Validate the proposal, update host state, then assign controlled properties.
  applyProposal(proposal);
});

States and failure handling

Relevant states:

Expected result: The flow preserves drafts and focus while emitting step or commit proposals after validation.

Keyboard, focus, and accessibility

Keyboard behavior:

Exposed semantics:

Responsive behavior

Keep the step label, validation, draft, and navigation in DOM order. Let the flow become one column as space decreases.

Style hooks

Generated TypeScript declaration

View generated declaration
export declare class AeliqoFormFlowElement extends AeliqoCompoundElement {
    static readonly properties: {
        steps: {
            attribute: boolean;
        };
        activeStep: {
            attribute: string;
            type: StringConstructor;
        };
        draft: {
            attribute: boolean;
        };
        validation: {
            attribute: boolean;
        };
        status: {
            type: StringConstructor;
        };
        message: {
            type: StringConstructor;
        };
        title: {
            type: StringConstructor;
        };
        nextLabel: {
            attribute: string;
            type: StringConstructor;
        };
        backLabel: {
            attribute: string;
            type: StringConstructor;
        };
        commitLabel: {
            attribute: string;
            type: StringConstructor;
        };
    };
    static readonly styles: import("lit").CSSResult[];
    steps: readonly AeliqoFormFlowStep[];
    activeStep: string;
    draft: Readonly<Record<string, FormDraftValue>>;
    validation: Readonly<Record<string, string | undefined>>;
    status: AeliqoCompoundStatus;
    message: string;
    title: string;
    nextLabel: string;
    backLabel: string;
    commitLabel: string;
    private transientDraft;
    private transientDraftSource;
    private pendingFocus;
    private focusStepAfterUpdate;
    protected willUpdate(changes: PropertyValues): void;
    protected updated(changes: PropertyValues): void;
    protected render(): import("lit").TemplateResult<1>;
    private renderStepStatus;
    private hasNamedStepSlots;
    private controlsForStep;
    private stepValid;
    private collectDraft;
    private resetTransientDraft;
    private replaceCurrentValue;
    private collectControlDraft;
    private skipDraftControl;
    private collectToggleDraft;
    private collectMultipleSelectDraft;
    private collectDateRangeDraft;
    private captureDraft;
    private moveRelative;
    private moveTo;
    private stepTransition;
    private canMove;
    private stepsValidBetween;
    private dispatchStepChange;
    private readonly commit;
}

Version

This page documents the Varendo 0.6 component contract.