Appearance
ModelTreePlugin
Hierarchical model tree view built from StorageService. Provides filter, search, selection sync with the scene, per-node visibility toggle, and an optional built-in UI panel.
Usage
ts
import { ViewerCore, ModelTreePlugin } from "@optellix/xviewr-sdk";
const viewer = new ViewerCore(container, config);
await viewer.ready();
const tree = new ModelTreePlugin({
expandDepth: 2,
sortBy: "name",
ui: { enabled: true, container: "#tree-panel", theme: "dark" },
});
await viewer.loadPlugin(tree);Service dependencies: scene-hierarchy, selection, mesh. The plugin rebuilds tree data on storage events and keeps tree-side selection in sync with scene-side selection.
Constructor
ts
new ModelTreePlugin(config?: Partial<TreeConfig>)Public API
| Method | Description |
|---|---|
getTreeData() | Returns a shallow copy of the full TreeNodeData[]. |
getFilteredTreeData(filter?) | Returns the tree filtered by filter (or the current filter). |
setFilter(filter) | Replace the current TreeFilter. |
clearFilter() | Clear the active filter. |
getNodeById(nodeId) | Find a tree node by id. |
selectNode(nodeId) | Select a node in tree and scene. Returns false if the node has no mesh loaded. |
getSelectedNode() | Returns the currently selected tree node or null. |
expandNode(nodeId, expanded?) | Expand or collapse a node. |
isNodeExpanded(nodeId) | Boolean. |
toggleNodeVisibility(nodeId) | Flip the node and its descendants visible/hidden via MeshService. |
updateConfig(config) | Partial update of TreeConfig and rebuild. |
getConfig() | Returns the active TreeConfig. |
rebuildTreeData(reason?) | Rebuild tree from storage. Emits tree:rebuild-* events. |
getStats() | { totalNodes, expandedNodes, selectedNode, nodesByType }. |
enableUI(container?, config?) | Mount the built-in UI panel. |
disableUI() | Tear down the UI panel. |
getUIInstance() | Returns the ModelTreeUI instance or null. |
updateUITheme("dark" | "light") | Switch UI theme. |
setCustomStyles({ customCSS?, styleOverrides? }) | Inject custom CSS and CSS variable overrides. |
Types
TreeNodeData
ts
interface TreeNodeData {
id: string;
name: string;
type: NodeType;
path: string;
isVisible: boolean;
isLocked: boolean;
depth: number;
hasChildren: boolean;
children: TreeNodeData[];
metadata: {
partNumber?: string;
material?: string;
tags: string[];
customData: Record<string, any>;
datasetType?: string;
};
stats: {
childCount: number;
totalDescendants: number;
meshCount: number;
};
}TreeFilter
ts
interface TreeFilter {
nodeTypes?: NodeType[];
tags?: string[];
namePattern?: RegExp;
visibleOnly?: boolean;
maxDepth?: number;
customFilter?: (node: HierarchyNode) => boolean;
}TreeConfig
ts
interface TreeConfig {
expandDepth: number; // initial expansion depth
showStats: boolean;
showMetadata: boolean;
autoExpandSingleChild: boolean;
sortBy: "name" | "type" | "depth" | "custom";
sortOrder: "asc" | "desc";
customSortFn?: (a: TreeNodeData, b: TreeNodeData) => number;
ui?: ModelTreeUIConfig & {
enabled: boolean;
customCSS?: string;
customTheme?: Record<string, string>;
styleOverrides?: {
nodeHeight?: string;
fontSize?: string;
iconSize?: string;
indentSize?: string;
borderRadius?: string;
colors?: {
background?: string;
foreground?: string;
accent?: string;
border?: string;
hover?: string;
selected?: string;
};
};
};
}Events
| Event | Payload |
|---|---|
tree:data-updated | { rootNodes, totalNodes } |
tree:node-selected | { nodeId, nodeData } |
tree:node-expanded | { nodeId, expanded } |
tree:node-visibility-changed | { nodeId, visible } |
tree:filter-changed | { filter, resultCount } |
tree:rebuild-started | { reason } |
tree:rebuild-completed | { duration, nodeCount } |
Example
ts
const tree = viewer.pluginManager.getPlugin("modelTree") as ModelTreePlugin;
tree.setFilter({ namePattern: /bolt/i, visibleOnly: true });
const root = tree.getTreeData()[0];
if (root) tree.selectNode(root.id);
viewer.eventBus.on("tree:node-visibility-changed", ({ nodeId, visible }) => {
console.log(nodeId, visible);
});