introduce DataTree
This commit is contained in:
parent
5fae69221f
commit
12682daf54
60
src/vs/base/browser/ui/tree/dataTree.ts
Normal file
60
src/vs/base/browser/ui/tree/dataTree.ts
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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[]>;
|
||||
|
|
|
@ -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 });
|
||||
|
||||
|
|
Loading…
Reference in a new issue