introduce DataTree

This commit is contained in:
Joao Moreno 2018-12-17 11:02:02 +01:00
parent 5fae69221f
commit 12682daf54
3 changed files with 138 additions and 1 deletions

View file

@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
import { ISpliceable } from 'vs/base/common/sequence';
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, IDataSource } from 'vs/base/browser/ui/tree/tree';
import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { Iterator } from 'vs/base/common/iterator';
export interface IDataTreeOptions<T, TFilterData = void> extends IAbstractTreeOptions<T, TFilterData> {
sorter?: ITreeSorter<T>;
}
export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | null, TFilterData, TInput | T> {
protected model: ObjectTreeModel<T | null, TFilterData>;
private _input: TInput | undefined;
get input(): TInput | undefined {
return this._input;
}
set input(input: TInput | undefined) {
this._input = input;
this.refresh(input);
}
constructor(
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
private dataSource: IDataSource<TInput, T>,
options: IDataTreeOptions<T, TFilterData> = {}
) {
super(container, delegate, renderers, options);
}
refresh(element: TInput | T): void {
if (!this._input) {
throw new Error('Tree input not set');
}
this.model.setChildren((element === this.input ? null : element) as T, this.createIterator(element));
}
private createIterator(element: TInput | T): Iterator<ITreeElement<T>> {
return Iterator.map(Iterator.fromArray(this.dataSource.getChildren(element)), element => ({
element,
children: this.createIterator(element)
}));
}
protected createModel(view: ISpliceable<ITreeNode<T, TFilterData>>, options: IDataTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
return new ObjectTreeModel(view, options);
}
}

View file

@ -145,6 +145,10 @@ export interface ITreeNavigator<T> {
next(): T | null;
}
export interface IDataSource<TInput, T> {
getChildren(element: TInput | T): T[];
}
export interface IAsyncDataSource<T extends NonNullable<any>> {
hasChildren(element: T | null): boolean;
getChildren(element: T | null): T[] | Promise<T[]>;

View file

@ -44,7 +44,7 @@
require.config({ baseUrl: '/static' });
require(['vs/base/browser/ui/tree/indexTree', 'vs/base/browser/ui/tree/asyncDataTree', 'vs/base/browser/ui/tree/tree', 'vs/base/common/iterator'], ({ IndexTree }, { AsyncDataTree }, { TreeVisibility }, { iter }) => {
require(['vs/base/browser/ui/tree/indexTree', 'vs/base/browser/ui/tree/asyncDataTree', 'vs/base/browser/ui/tree/dataTree', 'vs/base/browser/ui/tree/tree', 'vs/base/common/iterator'], ({ IndexTree }, { AsyncDataTree }, { DataTree }, { TreeVisibility }, { iter }) => {
function createIndexTree(opts) {
opts = opts || {};
@ -182,6 +182,69 @@
return { tree, treeFilter };
}
function createDataTree() {
const delegate = {
getHeight() { return 22; },
getTemplateId() { return 'template'; }
};
const renderer = {
templateId: 'template',
renderTemplate(container) { return container; },
renderElement(node, index, container) { container.textContent = node.element.name; },
disposeElement() { },
disposeTemplate() { }
};
const treeFilter = new class {
constructor() {
this.pattern = null;
let timeout;
filter.oninput = () => {
clearTimeout(timeout);
timeout = setTimeout(() => this.updatePattern(), 300);
};
}
updatePattern() {
if (!filter.value) {
this.pattern = null;
} else {
this.pattern = new RegExp(filter.value, 'i');
}
perf('refilter', () => tree.refilter());
}
filter(el) {
return (this.pattern ? this.pattern.test(el.name) : true) ? TreeVisibility.Visible : TreeVisibility.Recurse;
}
};
const dataSource = new class {
getChildren(element) {
return element.children || [];
}
};
const identityProvider = {
getId(node) {
return node.name;
}
};
const tree = new DataTree(container, delegate, [renderer], dataSource, { filter: treeFilter, identityProvider });
tree.input = {
children: [
{ name: 'A', children: [{ name: 'AA' }, { name: 'AB' }] },
{ name: 'B', children: [{ name: 'BA', children: [{ name: 'BAA' }] }, { name: 'BB' }] },
{ name: 'C' }
]
};
return { tree, treeFilter };
}
switch (location.search) {
case '?problems': {
const { tree, treeFilter } = createIndexTree();
@ -216,6 +279,16 @@
break;
}
case '?objectdata': {
const { tree, treeFilter } = createDataTree();
expandall.onclick = () => perf('expand all', () => tree.expandAll());
collapseall.onclick = () => perf('collapse all', () => tree.collapseAll());
renderwidth.onclick = () => perf('renderwidth', () => tree.layoutWidth(Math.random()));
refresh.onclick = () => perf('refresh', () => tree.refresh(null, true));
break;
}
case '?height': {
const { tree, treeFilter } = createIndexTree({ supportDynamicHeights: true });