Varendo

FilterBuilder

Typed predicates, AND/OR/null handling, visible inherited scope and explicit query application.

FamilydataLicenseApache-2.0
StandaloneAvailable

Use the web element directly without the semantic runtime.

Semantic bindingAvailable

A registered presentation adapter can bind this component to host-owned meaning and state.

Automatic adaptationNo standard recipe

The standard recipe resolver does not select this component automatically.

Import and live example

One unapplied team equals Research clause against authorized people fields.

Interactive preview requires JavaScript.

View the complete example source
import {registerAeliqoElements} from "@varendo/web";
import {
  AeliqoCardCollectionElement,
  AeliqoDeltaElement,
  AeliqoDetailElement,
  AeliqoFilterBuilderElement,
  AeliqoKeyValueElement,
  AeliqoMetricElement,
  AeliqoRecordListElement,
  AeliqoSelectionSummaryElement,
  AeliqoTableElement,
  type AeliqoKeyValueItem,
  type AeliqoTableColumn,
  type AeliqoTableRow,
  } from "@varendo/web/data";

import type {ResultRef,
  Scalar,
} from "@varendo/core";
import type {AeliqoDataColumn, AeliqoDataScope, AeliqoFieldOption} from "@varendo/web/data";

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 tableColumns: readonly AeliqoTableColumn[] = catalogColumns.map(({key, label, sortable, align}) => ({key, label, ...(sortable === undefined ? {} : {sortable}), ...(align === undefined ? {} : {align})}));
const tableRows: readonly AeliqoTableRow[] = catalogRows.map((row) => ({id: row.id ?? null, name: row.name ?? null, team: row.team ?? null, date: row.date ?? null, amount: row.amount ?? null}));
const personKeys = ["string:3:ada", "string:5:grace"] as const;

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<AeliqoFilterBuilderElement>("aeliqo-filter-builder", root);
		element.fields = catalogFields;
		element.entity = "person";
		element.scopeLabel = "Authorized people";
		element.autoApply = false;
		element.clauses = [{
			field: "team",
			operator: "eq",
			value: "Research"
		}];
		element.logical = "and";
	}))(root);

Purpose

Build filter clauses from allowed fields and operators. Apply emits a request; the application authorizes and runs the query.

When to use it

When to use a different component

Properties and defaults

Draft clauses are local until Apply; autoApply remains an explicit host choice and does not run a query itself.

Properties, ownership, and initial values
PropertyUse and ownershipDeclared typeInitial value
fieldsDefines the fields available to this control, including stable IDs, labels, and declared types.readonly AeliqoFieldOption[][]
predicateSupplies the validated filter predicate represented by the current control state.AeliqoFilterPredicate | undefinedundefined
inheritedSupplies read-only predicates inherited from an outer application scope.AeliqoFilterPredicate | undefinedundefined
entityNames the catalog entity whose records and identities are displayed.string''
scopeLabelProvides the visible text for the scope control or section.string'Current authorized scope'
applyLabelProvides the visible text for the apply control or section.string'Apply filter'
autoApplyEnables or disables auto apply as explicit host-controlled state.booleanfalse
clausesSupplies the current filter clauses. Application code owns query execution and authoritative filter state.readonly AeliqoFilterClause[][]
logicalChooses whether filter clauses are joined with AND or OR.'and' | 'or''and'
statusSupplies the explicit ready, loading, empty, partial, stale, or error state.AeliqoDataStatus'ready'
messageProvides the user-facing status, error, or recovery message.string''

Events

Events, payloads, timing, and application responsibilities
EventPayloadEmittedApplication responsibility
aeliqo-filter-changeThe typed predicate and filter state.After Apply, or after a valid change when auto-apply is enabled.Validate and evaluate the predicate, then update rows and Result evidence.

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

Listen for aeliqo-filter-change

TypeScript example
const element = document.querySelector('aeliqo-filter-builder');<p>element?.addEventListener('aeliqo-filter-change', (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 builder validates a typed predicate and emits it only after an explicit apply request.

Keyboard, focus, and accessibility

Keyboard behavior:

Exposed semantics:

Responsive behavior

Stack field, operator, and value controls when space is limited. Keep Apply reachable and preserve the active draft.

Style hooks

Generated TypeScript declaration

View generated declaration
export declare class AeliqoFilterBuilderElement extends LitElement {
    static readonly aeliqoVersion = "0.1.0";
    static readonly properties: {
        fields: {
            attribute: boolean;
        };
        predicate: {
            attribute: boolean;
        };
        inherited: {
            attribute: boolean;
        };
        entity: {
            type: StringConstructor;
        };
        scopeLabel: {
            attribute: string;
            type: StringConstructor;
        };
        applyLabel: {
            attribute: string;
            type: StringConstructor;
        };
        autoApply: {
            attribute: string;
            type: BooleanConstructor;
        };
        clauses: {
            attribute: boolean;
        };
        logical: {
            type: StringConstructor;
        };
        status: {
            type: StringConstructor;
        };
        message: {
            type: StringConstructor;
        };
    };
    fields: readonly AeliqoFieldOption[];
    predicate: AeliqoFilterPredicate | undefined;
    inherited: AeliqoFilterPredicate | undefined;
    entity: string;
    scopeLabel: string;
    applyLabel: string;
    autoApply: boolean;
    clauses: readonly AeliqoFilterClause[];
    logical: 'and' | 'or';
    status: AeliqoDataStatus;
    message: string;
    private clauseDrafts;
    private logicalMode;
    private unsupportedPredicate;
    private draftSourceSignature;
    private draftInitialized;
    private draftInteracted;
    private validationMessage;
    protected willUpdate(changed: Map<string, unknown>): void;
    protected render(): import("lit").TemplateResult<1>;
    private shouldProjectDraft;
    private currentProjection;
    private currentProjectionSignature;
    private replaceDraft;
    private seedDraftWhenFieldsArrive;
    private isUnavailable;
    private renderClauses;
    private renderLogicalControl;
    private renderAddCondition;
    private renderUnsupportedPredicate;
    private renderInheritedPredicate;
    private renderApplyButton;
    private renderValidationMessage;
    private renderStatus;
    private renderClause;
    private clauseNeedsValue;
    private clauseIndex;
    private updateClause;
    private readonly handleAddCondition;
    private readonly handleRemoveCondition;
    private readonly handleFieldChange;
    private readonly handleOperatorChange;
    private readonly handleValueInput;
    private readonly handleLogicalChange;
    private readonly handleSubmit;
    private readonly apply;
    private buildPredicate;
    static readonly styles: import("lit").CSSResult[];
}

Version

This page documents the Varendo 0.6 component contract.