Custom Tree Data Provider
In more complex scenarios, it's probably easiest to implement your own data provider. This provider must implement the TreeDataProvider interface, i.e.
export interface TreeDataProvider<T = any> {
onDidChangeTreeData?: (listener: (changedItemIds: TreeItemIndex[]) => void) => Disposable;
getTreeItem: (itemId: TreeItemIndex) => Promise<TreeItem<T>>;
getTreeItems?: (itemIds: TreeItemIndex[]) => Promise<TreeItem[]>;
onRenameItem?: (item: TreeItem<T>, name: string) => Promise<void>;
onChangeItemChildren?: (itemId: TreeItemIndex, newChildren: TreeItemIndex[]) => Promise<void>;
}
At least the getTreeItem
method must be implemented, to declare how data can be made available
to the tree structure. getTreeItems
allows you to make loading more efficient if multiple entries
need to be loaded at once. If you do not implement getTreeItems
, they are loaded sequentially
using getTreeItem
.
The methods onRenameItem
and onChangeItemChildren
allow you to declare how updates to the
tree structure should be handled, i.e. by renaming an item or moving items from one parent to
another. You still need to enable this functionality in the environment by providing the respective
flags. Look into the TreeCapabilities interface for more details
on the necessary flags.
You can use this implementation as baseline:
class CustomDataProviderImplementation implements TreeDataProvider {
private data: Record<TreeItemIndex, TreeItem> = { ...shortTree.items };
private treeChangeListeners: ((changedItemIds: TreeItemIndex[]) => void)[] =
[];
public async getTreeItem(itemId: TreeItemIndex) {
return this.data[itemId];
}
public async onChangeItemChildren(
itemId: TreeItemIndex,
newChildren: TreeItemIndex[]
) {
this.data[itemId].children = newChildren;
this.treeChangeListeners.forEach(listener => listener([itemId]));
}
public onDidChangeTreeData(
listener: (changedItemIds: TreeItemIndex[]) => void
): Disposable {
this.treeChangeListeners.push(listener);
return {
dispose: () =>
this.treeChangeListeners.splice(
this.treeChangeListeners.indexOf(listener),
1
),
};
}
public async onRenameItem(item: TreeItem<any>, name: string): Promise<void> {
this.data[item.index].data = name;
}
// custom handler for directly manipulating the tree data
public injectItem(name: string) {
const rand = `${Math.random()}`;
this.data[rand] = { data: name, index: rand } as TreeItem;
this.data.root.children?.push(rand);
this.treeChangeListeners.forEach(listener => listener(['root']));
}
}
Reacting to Drag Events
RCT will call onChangeItemChildren
when a drag operation is finished. You can use this directly
to update your data source. Note that, if you add or remove items, the affected item
is the parent item, not the added or removed items.
In the exemplary implementation above, this emits an event on the treeChangeListeners
listeners,
where you could register a custom listener to react to changes.
Reacting to Rename Events
RCT will call onRenameItem
when a rename operation is finished. Implement your rename logic there.