Skip to content

Commit 12682da

Browse files
committed
introduce DataTree
1 parent 5fae692 commit 12682da

3 files changed

Lines changed: 138 additions & 1 deletion

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
7+
import { ISpliceable } from 'vs/base/common/sequence';
8+
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, IDataSource } from 'vs/base/browser/ui/tree/tree';
9+
import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
10+
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
11+
import { Iterator } from 'vs/base/common/iterator';
12+
13+
export interface IDataTreeOptions<T, TFilterData = void> extends IAbstractTreeOptions<T, TFilterData> {
14+
sorter?: ITreeSorter<T>;
15+
}
16+
17+
export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | null, TFilterData, TInput | T> {
18+
19+
protected model: ObjectTreeModel<T | null, TFilterData>;
20+
21+
private _input: TInput | undefined;
22+
23+
get input(): TInput | undefined {
24+
return this._input;
25+
}
26+
27+
set input(input: TInput | undefined) {
28+
this._input = input;
29+
this.refresh(input);
30+
}
31+
32+
constructor(
33+
container: HTMLElement,
34+
delegate: IListVirtualDelegate<T>,
35+
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
36+
private dataSource: IDataSource<TInput, T>,
37+
options: IDataTreeOptions<T, TFilterData> = {}
38+
) {
39+
super(container, delegate, renderers, options);
40+
}
41+
42+
refresh(element: TInput | T): void {
43+
if (!this._input) {
44+
throw new Error('Tree input not set');
45+
}
46+
47+
this.model.setChildren((element === this.input ? null : element) as T, this.createIterator(element));
48+
}
49+
50+
private createIterator(element: TInput | T): Iterator<ITreeElement<T>> {
51+
return Iterator.map(Iterator.fromArray(this.dataSource.getChildren(element)), element => ({
52+
element,
53+
children: this.createIterator(element)
54+
}));
55+
}
56+
57+
protected createModel(view: ISpliceable<ITreeNode<T, TFilterData>>, options: IDataTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
58+
return new ObjectTreeModel(view, options);
59+
}
60+
}

src/vs/base/browser/ui/tree/tree.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ export interface ITreeNavigator<T> {
145145
next(): T | null;
146146
}
147147

148+
export interface IDataSource<TInput, T> {
149+
getChildren(element: TInput | T): T[];
150+
}
151+
148152
export interface IAsyncDataSource<T extends NonNullable<any>> {
149153
hasChildren(element: T | null): boolean;
150154
getChildren(element: T | null): T[] | Promise<T[]>;

test/tree/public/index.html

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444

4545
require.config({ baseUrl: '/static' });
4646

47-
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 }) => {
47+
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 }) => {
4848
function createIndexTree(opts) {
4949
opts = opts || {};
5050

@@ -182,6 +182,69 @@
182182
return { tree, treeFilter };
183183
}
184184

185+
function createDataTree() {
186+
const delegate = {
187+
getHeight() { return 22; },
188+
getTemplateId() { return 'template'; }
189+
};
190+
191+
const renderer = {
192+
templateId: 'template',
193+
renderTemplate(container) { return container; },
194+
renderElement(node, index, container) { container.textContent = node.element.name; },
195+
disposeElement() { },
196+
disposeTemplate() { }
197+
};
198+
199+
const treeFilter = new class {
200+
constructor() {
201+
this.pattern = null;
202+
let timeout;
203+
filter.oninput = () => {
204+
clearTimeout(timeout);
205+
timeout = setTimeout(() => this.updatePattern(), 300);
206+
};
207+
}
208+
209+
updatePattern() {
210+
if (!filter.value) {
211+
this.pattern = null;
212+
} else {
213+
this.pattern = new RegExp(filter.value, 'i');
214+
}
215+
216+
perf('refilter', () => tree.refilter());
217+
}
218+
filter(el) {
219+
return (this.pattern ? this.pattern.test(el.name) : true) ? TreeVisibility.Visible : TreeVisibility.Recurse;
220+
}
221+
};
222+
223+
const dataSource = new class {
224+
getChildren(element) {
225+
return element.children || [];
226+
}
227+
};
228+
229+
const identityProvider = {
230+
getId(node) {
231+
return node.name;
232+
}
233+
};
234+
235+
const tree = new DataTree(container, delegate, [renderer], dataSource, { filter: treeFilter, identityProvider });
236+
237+
tree.input = {
238+
children: [
239+
{ name: 'A', children: [{ name: 'AA' }, { name: 'AB' }] },
240+
{ name: 'B', children: [{ name: 'BA', children: [{ name: 'BAA' }] }, { name: 'BB' }] },
241+
{ name: 'C' }
242+
]
243+
};
244+
245+
return { tree, treeFilter };
246+
}
247+
185248
switch (location.search) {
186249
case '?problems': {
187250
const { tree, treeFilter } = createIndexTree();
@@ -216,6 +279,16 @@
216279

217280
break;
218281
}
282+
case '?objectdata': {
283+
const { tree, treeFilter } = createDataTree();
284+
285+
expandall.onclick = () => perf('expand all', () => tree.expandAll());
286+
collapseall.onclick = () => perf('collapse all', () => tree.collapseAll());
287+
renderwidth.onclick = () => perf('renderwidth', () => tree.layoutWidth(Math.random()));
288+
refresh.onclick = () => perf('refresh', () => tree.refresh(null, true));
289+
290+
break;
291+
}
219292
case '?height': {
220293
const { tree, treeFilter } = createIndexTree({ supportDynamicHeights: true });
221294

0 commit comments

Comments
 (0)