Tree
Module Extensions
Section titled “Module Extensions”ColumnRegular (Extended from @revolist/revogrid)
Section titled “ColumnRegular (Extended from @revolist/revogrid)”interface ColumnRegular { /** * Enables tree template on cells */ tree?: boolean}HTMLRevoGridElement (Extended from global)
Section titled “HTMLRevoGridElement (Extended from global)”interface HTMLRevoGridElement { tree?: TreeConfig}AdditionalData (Extended from @revolist/revogrid)
Section titled “AdditionalData (Extended from @revolist/revogrid)”interface AdditionalData { /** * Additional data property tree * @deprecated Use `grid.tree` instead. * * @example * ```typescript * const grid = document.createElement('revo-grid'); * grid.additionalData = { * tree: { * idField: 'id', * parentIdField: 'parentId', * rootParentId: null, * expandAll: true, * expandedRowIds: new Set(), * }, * }; * ``` */ tree?: TreeConfig}HTMLRevoGridElementEventMap (Extended from global)
Section titled “HTMLRevoGridElementEventMap (Extended from global)”interface HTMLRevoGridElementEventMap { [TREE_ROW_SELECT_EVENT]: TreeRowSelectEvent; [TREE_TOGGLE_EVENT]: { rowId: any }; [TREE_EXPAND_ALL_EVENT]: undefined; [TREE_COLLAPSE_ALL_EVENT]: undefined; [TREE_STATE_CHANGED_EVENT]: { expandedRowIds: Set<any> }}Plugin API
Section titled “Plugin API”TreeDataPlugin
Section titled “TreeDataPlugin”The TreeDataPlugin now uses Revogrid’s row trimming mechanism without modifying the original data.
All tree metadata (level, expanded, hasChildren, visible) is computed separately and stored in a Map. The plugin builds a parent→children mapping from the original flat data and then recursively computes, for each row, its tree level and whether it should be visible based on the expanded/collapsed state of its ancestors.
The cell template uses a supplied “getMeta” function to look up computed tree metadata for rendering (indentation and expand/collapse icons) without changing the underlying row.
Usage Example:
import { TreeDataPlugin } from './tree/index';
const grid = document.createElement('revo-grid');grid.plugins = [TreeDataPlugin];
grid.columns = [ { prop: 'treeColumn', name: 'Hierarchy', tree: true, cellTemplate: TreeCellTemplate, // or you can let the plugin override it },];Dependencies
Section titled “Dependencies”- Event integration
row-order: Integrates with RowOrderPlugin row order events to reparent tree rows after drag and drop. - Event integration
row-select: Integrates with RowSelectPlugin row selection events for parent and descendant selection. - Event integration
DimensionAnimationPlugin: Auto-installs DimensionAnimationPlugin when tree.animation is enabled to animate tree collapse and expand trim changes.
class TreeDataPlugin { /** Whether tree configuration or tree-enabled columns currently activate this plugin. */ isActive(): boolean;
/** Active structural properties used by tree-safe editing integrations. */ getStructuralProps(): ReadonlySet<string>;
/** Resolve stable node identity and computed hierarchy state for a source row. */ getNodeContext<T extends DataType = DataType>( model: T, type: DimensionRows = 'rgRow', ): TreeNodeContext<T> | undefined;
/** Resolve whether a hierarchy mutation is valid without changing source state. */ getMutationState<T extends DataType = DataType>( request: TreeMutationRequest<T>, ): TreeMutationState<T>;
/** * Validate and commit one tree hierarchy mutation. Existing moved rows retain * object identity; row creation is completed and validated before any write. */ async mutate<T extends DataType = DataType>( request: TreeMutationRequest<T>, ): Promise<TreeMutationResult<T>>;
/** Capture stable ID-based hierarchy state for HistoryPlugin replay. */ getStructureSnapshot(type: DimensionRows = 'rgRow'): TreeStructureSnapshot;
/** Restore a stable ID-based hierarchy snapshot without replacing row objects. */ applyStructureSnapshot(snapshot: TreeStructureSnapshot): boolean;
/** * Recomputes the tree metadata for every row in the original data and builds a trimmed map. * Call this method after the original data/order/visibility/set changes * * This method builds a parent→children mapping (without modifying the original data) and then * recursively computes for each row: * - its level (indentation), * - whether it is expanded (based on the expandedRowIds set), * - whether it has children, * - and whether it should be visible (i.e. none of its ancestors are collapsed). * * The trimmed map is then applied via providers.data.setTrimmed(). */ async updateTree(type?: DimensionRows, animate = true);
/** * Toggle the expanded state for a row and then re-calculate tree metadata. */ toggleRowExpanded(row: DataType);
getExpandedRowIds();
toggleRowExpandedById(rowId: any);
expandAll();
collapseAll();
/** * Gets all descendant row IDs (children, grandchildren, etc.) for a given row * @param rowId - The ID of the row to get descendants for * @returns Array of physical indices of all descendant rows */ public getAllDescendantPhysicalIndices(rowId: any, type: DimensionRows = 'rgRow'): number[];}TreeMeta
Section titled “TreeMeta”This is the shape of computed tree metadata. Notice that nothing is stored on the row itself.
interface TreeMeta { level: number; expanded: boolean; hasChildren: boolean; visible: boolean; parentId: any}TreeConfig
Section titled “TreeConfig”interface TreeConfig { idField?: string; parentIdField?: string; rootParentId?: any; expandAll?: boolean; expandedRowIds?: Set<any>; /** * Mark tree parent rows as sticky through StickyCellsPlugin. * * Parent detection uses the row model fields configured by `idField` and * `parentIdField`, plus tree metadata computed by TreeDataPlugin. It does not * depend on virtual row indexes. * * This option only adds sticky behavior to tree-enabled columns. Register * StickyCellsPlugin separately to render those parent rows in pinned top rows. * * @default false */ stickyParents?: boolean; /** * Animate tree collapse and expand through DimensionAnimationPlugin. * * TreeDataPlugin auto-registers DimensionAnimationPlugin when this option is * enabled and the dimension animation plugin is not already registered. * * @default false */ animation?: boolean}TreeRowSelectEvent
Section titled “TreeRowSelectEvent”Event emitted when a tree row with children is selected. This event allows for custom handling of parent-child selection relationships.
interface TreeRowSelectEvent { /** The row type (e.g., 'rgRow') */ type: DimensionRows; /** The physical index of the parent row in the source data */ parentIndex: number; /** Array of physical indices of all descendant rows */ childrenIndices: number[]; /** The original row selection event that triggered this tree selection */ originalEvent?: any}TreeSelectionProvider
Section titled “TreeSelectionProvider”Runtime contract exposed by TreeDataPlugin for plugins that need tree descendant lookup without depending on the TreeDataPlugin class.
interface TreeSelectionProvider { getAllDescendantPhysicalIndices(rowId: unknown, type: DimensionRows): number[]}TreeMutationAction
Section titled “TreeMutationAction”export type TreeMutationAction = | 'moveUp' | 'moveDown' | 'indent' | 'outdent' | 'insertSiblingAbove' | 'insertSiblingBelow' | 'insertChild' | 'duplicate' | 'delete';TreeMutationUnavailableReason
Section titled “TreeMutationUnavailableReason”export type TreeMutationUnavailableReason = | 'inactive' | 'invalid-tree' | 'invalid-selection' | 'mixed-parent' | 'missing-factory' | 'first-sibling' | 'last-sibling' | 'no-previous-sibling' | 'root-level' | 'cancelled';TreeNodeContext (Extended from index.ts)
Section titled “TreeNodeContext (Extended from index.ts)”interface TreeNodeContext { readonly type: DimensionRows; readonly model: T; readonly physicalIndex: number; readonly id: unknown}TreeMutationCreateContext
Section titled “TreeMutationCreateContext”interface TreeMutationCreateContext { readonly action: Extract< TreeMutationAction, 'insertSiblingAbove' | 'insertSiblingBelow' | 'insertChild' | 'duplicate' >; readonly anchorRow: T; readonly sourceRow?: T; readonly parentRow?: T; readonly depth: number}TreeMutationRowFactory
Section titled “TreeMutationRowFactory”export type TreeMutationRowFactory<T extends DataType = DataType> = ( context: TreeMutationCreateContext<T>,) => T | null | undefined | Promise<T | null | undefined>;TreeMutationRequest
Section titled “TreeMutationRequest”interface TreeMutationRequest { readonly action: TreeMutationAction; readonly type: DimensionRows; readonly models: readonly T[]; readonly createRow?: TreeMutationRowFactory<T>}TreeMutationState
Section titled “TreeMutationState”interface TreeMutationState { readonly available: boolean; readonly reason?: TreeMutationUnavailableReason; readonly roots: readonly T[]}TreeMutationResult (Extended from index.ts)
Section titled “TreeMutationResult (Extended from index.ts)”interface TreeMutationResult { readonly applied: boolean; readonly createdRows?: readonly T[]; readonly deletedRows?: readonly T[]}TreeStructureSnapshot
Section titled “TreeStructureSnapshot”interface TreeStructureSnapshot { readonly type: DimensionRows; readonly orderedRowIds: readonly unknown[]; readonly parentByRowId: readonly (readonly [unknown, unknown])[]; readonly expandedRowIds: readonly unknown[]}composeTreeCellTemplate
Section titled “composeTreeCellTemplate”Compose the structural tree affordance with a replaceable value renderer. Other plugins may replace the value renderer without removing indentation or the expand/collapse control.
export function composeTreeCellTemplate( treeTemplate: CellTemplate, valueTemplate: CellTemplate,): CellTemplate;getTreeCellValueTemplate
Section titled “getTreeCellValueTemplate”Return the application/value renderer wrapped by a tree template.
export function getTreeCellValueTemplate( template: CellTemplate | undefined,): CellTemplate | undefined;replaceTreeCellValueTemplate
Section titled “replaceTreeCellValueTemplate”Replace only the value renderer when the supplied template is tree-composed.
export function replaceTreeCellValueTemplate( template: CellTemplate | undefined, valueTemplate: CellTemplate,): CellTemplate;TREE_CELL_TEMPLATE
Section titled “TREE_CELL_TEMPLATE”TREE_CELL_TEMPLATE: typeof TREE_CELL_TEMPLATE;TreeCellTemplate
Section titled “TreeCellTemplate”The TreeCellTemplate now receives an extra “getMeta” callback that provides the computed tree metadata for the given row. It does not assume that any tree information has been stored on the row itself.
TreeCellTemplate: (toggleRowExpanded: (data: DataType) => void, getMeta: (row: DataType) => TreeMeta) => CellTemplate<DataType<any, ColumnProp>>;analyzeTreeMutation
Section titled “analyzeTreeMutation”export function analyzeTreeMutation<T extends DataType>( options: TreeMutationOptions<T>,): TreeMutationState<T>;planTreeMutation
Section titled “planTreeMutation”export async function planTreeMutation<T extends DataType>( options: TreeMutationOptions<T>,): Promise<TreeMutationPlan<T>>;TreeMutationPlan (Extended from index.ts)
Section titled “TreeMutationPlan (Extended from index.ts)”interface TreeMutationPlan { readonly source: readonly T[]; readonly parentChanges: readonly { readonly model: T; readonly previousParentId: unknown; readonly parentId: unknown; }[]; readonly expandedRowIds: readonly unknown[]}TreeAnimationUpdate
Section titled “TreeAnimationUpdate”export type TreeAnimationUpdate = { cancelledIndexes: number[]; reused: boolean; version: number;};TreeAnimationCoordinator
Section titled “TreeAnimationCoordinator”class TreeAnimationCoordinator { beginUpdate({ type, nextTrimmed, source, proxyItems, }: BeginTreeAnimationUpdateOptions): TreeAnimationUpdate;
reset(type: DimensionRows): number[];
isCurrent(type: DimensionRows, version: number): boolean;
canAnimate(type: DimensionRows, dimensionAnimation?: DimensionAnimationPlugin): dimensionAnimation is DimensionAnimationPlugin;
markInitialized(type: DimensionRows, sourceLength: number);
hasTrimChanges( currentTrimmed: Record<number, boolean>, nextTrimmed: Record<number, boolean>, ): boolean;
async applyAnimatedTrim({ applyTrim, currentTrimmed, dimensionAnimation, nextTrimmed, proxyItems, source, type, version, }: { applyTrim: (trimmed: Record<number, boolean>) => void; currentTrimmed: Record<number, boolean>; dimensionAnimation: DimensionAnimationPlugin; nextTrimmed: Record<number, boolean>; proxyItems: number[]; source: DataType[]; type: DimensionRows; version: number; }): Promise<boolean>;}resolveTreeFilterTypes
Section titled “resolveTreeFilterTypes”export function resolveTreeFilterTypes(detail: TreeFilterEventDetail): string[];resolveTreeFilterTrim
Section titled “resolveTreeFilterTrim”Keeps complete hierarchy context around rows that match an active filter.
The filter plugin marks non-matching physical row indexes as trimmed. Positive filtering closes the matching set in both directions: ancestors remain as the path to a deep match, and descendants remain as the branch owned by a matching parent. Exclusion-only filters instead prune complete subtrees rooted at excluded rows. Unrelated branches keep the filter plugin’s trim decisions.
export function resolveTreeFilterTrim({ filterTrimmed, filterTypes, idField, parentIdField, rootParentId, source,}: TreeFilterTrimOptions): TrimmedEntity;TreeFilterTrimOptions
Section titled “TreeFilterTrimOptions”export type TreeFilterTrimOptions = { filterTrimmed: TrimmedEntity; filterTypes?: readonly string[]; idField: string; parentIdField: string; rootParentId: any; source: DataType[];};TreeFilterEventDetail
Section titled “TreeFilterEventDetail”interface TreeFilterEventDetail { collection?: Record<string, { type?: string }>; filterItems?: Record<string, Array<{ type?: string }>>}TreeRowOrderController
Section titled “TreeRowOrderController”class TreeRowOrderController { isParentUpdatePending(type: DimensionRows): boolean;
handleDragEnd();
handleDropValidation(context: RowOrderDropValidationContext): TreeDropValidation;
handleRowDrop(e: CustomEvent<RowOrderChange>);
highlightDropTarget(context: RowOrderDropStateChange);
clearDropTarget();}isTreeRowType
Section titled “isTreeRowType”export function isTreeRowType(type: unknown): type is DimensionRows;buildTreeChildIndex
Section titled “buildTreeChildIndex”export function buildTreeChildIndex({ idField, parentIdField, rootParentId, source,}: { idField: string; parentIdField: string; rootParentId: any; source: DataType[];}): TreeChildIndex;collectDescendantPhysicalIndices
Section titled “collectDescendantPhysicalIndices”export function collectDescendantPhysicalIndices( childrenByParent: TreeChildIndex, rowId: any,): number[];resolveTreeTrimState
Section titled “resolveTreeTrimState”export function resolveTreeTrimState( allTrimmed: Record<string, Record<number, boolean>> = {},): TreeTrimState;computeTreeState
Section titled “computeTreeState”export function computeTreeState({ expandedRowIds, idField, items, outsideTrimmed, parentIdField, rootParentId, source,}: { expandedRowIds: Set<any>; idField: string; items: number[]; outsideTrimmed: TrimmedEntity; parentIdField: string; rootParentId: any; source: DataType[];}): TreeState;collectExpandableRowIds
Section titled “collectExpandableRowIds”export function collectExpandableRowIds({ idField, parentIdField, rootParentId, source,}: { idField: string; parentIdField: string; rootParentId: any; source: DataType[];}): Set<any>;TRIMMED_COLLAPSED
Section titled “TRIMMED_COLLAPSED”TRIMMED_COLLAPSED: string;TREE_ROW_TYPES
Section titled “TREE_ROW_TYPES”TREE_ROW_TYPES: DimensionRows[];TreeState
Section titled “TreeState”export type TreeState = { itemParentOrder: number[]; metaData: Map<any, TreeMeta>; rowMetaEntries: Array<[DataType, TreeMeta]>; trimmed: Record<number, boolean>;};TreeChildIndex
Section titled “TreeChildIndex”export type TreeChildIndex = Map<any, Array<{ id: any; physicalIndex: number;}>>;TreeTrimState
Section titled “TreeTrimState”export type TreeTrimState = { currentTreeTrimmed: Record<number, boolean>; outsideTrimmed: TrimmedEntity;};TreeRuntimeState
Section titled “TreeRuntimeState”class TreeRuntimeState { setConfig(options: TreeConfig = {});
snapshotExpandedRowIds(): Set<any>;
toggleExpanded(rowId: any);
setExpandedRowIds(expandedRowIds: Set<any>);
clearExpandedRowIds();
setExpandAllPending(pending: boolean);}isNoopTreeDrop
Section titled “isNoopTreeDrop”export function isNoopTreeDrop({ dropPosition, from, itemCount, targetIndex, targetRow, visibleRowIndexes,}: { dropPosition?: RowOrderDropValidationContext['dropPosition']; from: number; itemCount: number; targetIndex: number; targetRow?: number; visibleRowIndexes?: number[];}): boolean;resolveTreeDropParentId
Section titled “resolveTreeDropParentId”export function resolveTreeDropParentId({ dropPosition, expandedRowIds, from, idField, itemCount, parentIdField, rootParentId, store, targetIndex, targetRow, visibleRowIndexes,}: { dropPosition?: RowOrderDropValidationContext['dropPosition']; expandedRowIds: Set<unknown>; from: number; idField: string; itemCount?: number; parentIdField: string; rootParentId: unknown; store: DataStore<DataType, DimensionRows>['store']; targetIndex: number; targetRow?: number; visibleRowIndexes?: number[];}): unknown;validateTreeRowDrop
Section titled “validateTreeRowDrop”export function validateTreeRowDrop({ context, idField, metaData, newParentId, rootParentId,}: { context: RowOrderDropValidationContext; idField: string; metaData: Map<unknown, { parentId?: unknown }>; newParentId: unknown; rootParentId: unknown;}): RowOrderDropValidationResult;resolveTreeDropValidation
Section titled “resolveTreeDropValidation”export function resolveTreeDropValidation({ context, expandedRowIds, idField, metaData, parentIdField, rootParentId, store,}: { context: RowOrderDropValidationContext; expandedRowIds: Set<unknown>; idField: string; metaData: Map<unknown, { parentId?: unknown }>; parentIdField: string; rootParentId: unknown; store: DataStore<DataType, DimensionRows>['store'];}): { newParentId: unknown; result: RowOrderDropValidationResult;} | null;moveProxySubtreesAfterTarget
Section titled “moveProxySubtreesAfterTarget”export function moveProxySubtreesAfterTarget({ idField, movedPhysicalIndexes, parentIdField, proxyItems, source, targetPhysicalIndex,}: { idField: string; movedPhysicalIndexes: number[]; parentIdField: string; proxyItems: number[]; source: DataType[]; targetPhysicalIndex: number;}): number[];moveProxySubtreesToTarget
Section titled “moveProxySubtreesToTarget”export function moveProxySubtreesToTarget({ idField, insert, movedPhysicalIndexes, parentIdField, proxyItems, source, targetPhysicalIndex,}: { idField: string; insert: 'before' | 'after' | 'after-subtree'; movedPhysicalIndexes: number[]; parentIdField: string; proxyItems: number[]; source: DataType[]; targetPhysicalIndex: number;}): number[];applyTreeStickyParentColumns
Section titled “applyTreeStickyParentColumns”export function applyTreeStickyParentColumns( columnsByType: ColumnCollection['columns'], resolveStickyCell: (model: CellTemplateProp, stickyCell: StickyCellPredicate | undefined) => boolean,);resolveTreeStickyCell
Section titled “resolveTreeStickyCell”export function resolveTreeStickyCell( model: CellTemplateProp, stickyCell: StickyCellPredicate | undefined, context: TreeStickyCellContext,): boolean;getStickyTreeConfig
Section titled “getStickyTreeConfig”export function getStickyTreeConfig(revogrid: HTMLRevoGridElement): TreeConfig | undefined;resolveActiveTreeStickyRows
Section titled “resolveActiveTreeStickyRows”export function resolveActiveTreeStickyRows({ rowIndex, maxRows, config, includeRow = true, rowStore, resolveStickyColumns,}: ActiveTreeStickyRowsContext): number[];TreeStickyCellContext
Section titled “TreeStickyCellContext”interface TreeStickyCellContext { config: TreeConfig | undefined; getMeta(row: DataType): TreeMeta}ActiveTreeStickyRowsContext
Section titled “ActiveTreeStickyRowsContext”interface ActiveTreeStickyRowsContext { rowIndex: number; maxRows: number; config: TreeConfig; includeRow?: boolean; rowStore: { get(key: 'source'): DataType[]; get(key: 'items'): number[]; }; resolveStickyColumns(rowIndex: number): boolean}