404 lines
11 KiB
HTML
404 lines
11 KiB
HTML
|
<html>
|
||
|
|
||
|
<head>
|
||
|
<meta charset="utf-8">
|
||
|
<title>Tree</title>
|
||
|
<style>
|
||
|
#container {
|
||
|
width: 400;
|
||
|
height: 600;
|
||
|
border: 1px solid black;
|
||
|
}
|
||
|
|
||
|
.monaco-scrollable-element>.scrollbar>.slider {
|
||
|
background: rgba(100, 100, 100, .4);
|
||
|
}
|
||
|
|
||
|
.tl-contents {
|
||
|
flex: 1;
|
||
|
}
|
||
|
|
||
|
.monaco-list-row:hover:not(.selected):not(.focused) {
|
||
|
background: gainsboro !important;
|
||
|
}
|
||
|
</style>
|
||
|
</head>
|
||
|
|
||
|
<body>
|
||
|
<input type="text" id="filter" />
|
||
|
<button id="expandall">Expand All</button>
|
||
|
<button id="collapseall">Collapse All</button>
|
||
|
<button id="renderwidth">Render Width</button>
|
||
|
<button id="refresh">Refresh</button>
|
||
|
<div id="container"></div>
|
||
|
|
||
|
<script src="/static/vs/loader.js"></script>
|
||
|
<script>
|
||
|
function perf(name, fn) {
|
||
|
performance.mark('before ' + name);
|
||
|
const start = performance.now();
|
||
|
fn();
|
||
|
console.log(name + ' took', performance.now() - start);
|
||
|
performance.mark('after ' + name);
|
||
|
}
|
||
|
|
||
|
require.config({ baseUrl: '/static' });
|
||
|
|
||
|
require(['vs/base/browser/ui/tree/indexTree', 'vs/base/browser/ui/tree/objectTree', 'vs/base/browser/ui/tree/asyncDataTree', 'vs/base/browser/ui/tree/dataTree', 'vs/base/browser/ui/tree/tree', 'vs/base/common/iterator'], ({ IndexTree }, { CompressibleObjectTree }, { AsyncDataTree }, { DataTree }, { TreeVisibility }, { iter }) => {
|
||
|
function createIndexTree(opts) {
|
||
|
opts = opts || {};
|
||
|
|
||
|
const delegate = {
|
||
|
getHeight() { return 22; },
|
||
|
getTemplateId() { return 'template'; },
|
||
|
hasDynamicHeight() { return true; }
|
||
|
};
|
||
|
|
||
|
const renderer = {
|
||
|
templateId: 'template',
|
||
|
renderTemplate(container) { return container; },
|
||
|
renderElement(element, index, container) {
|
||
|
if (opts.supportDynamicHeights) {
|
||
|
let v = [];
|
||
|
for (let i = 1; i <= 5; i++) {
|
||
|
v.push(element.element);
|
||
|
}
|
||
|
container.innerHTML = v.join('<br />');
|
||
|
} else {
|
||
|
container.innerHTML = element.element;
|
||
|
}
|
||
|
},
|
||
|
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) : true) ? TreeVisibility.Visible : TreeVisibility.Recurse;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const tree = new IndexTree('test', container, delegate, [renderer], null, { ...opts, filter: treeFilter, setRowLineHeight: false });
|
||
|
|
||
|
return { tree, treeFilter };
|
||
|
}
|
||
|
|
||
|
function createCompressedObjectTree(opts) {
|
||
|
opts = opts || {};
|
||
|
|
||
|
const delegate = {
|
||
|
getHeight() { return 22; },
|
||
|
getTemplateId() { return 'template'; },
|
||
|
hasDynamicHeight() { return true; }
|
||
|
};
|
||
|
|
||
|
const renderer = {
|
||
|
templateId: 'template',
|
||
|
renderTemplate(container) { return container; },
|
||
|
renderElement(element, index, container) {
|
||
|
container.innerHTML = element.element.name;
|
||
|
},
|
||
|
renderCompressedElements(node, index, container, height) {
|
||
|
container.innerHTML = `🙈 ${node.element.elements.map(el => el.name).join('/')}`;
|
||
|
},
|
||
|
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) : true) ? TreeVisibility.Visible : TreeVisibility.Recurse;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const tree = new CompressibleObjectTree('test', container, delegate, [renderer], { ...opts, filter: treeFilter, setRowLineHeight: false, collapseByDefault: true, setRowLineHeight: true });
|
||
|
|
||
|
return { tree, treeFilter };
|
||
|
}
|
||
|
|
||
|
function createAsyncDataTree() {
|
||
|
const delegate = {
|
||
|
getHeight() { return 22; },
|
||
|
getTemplateId() { return 'template'; }
|
||
|
};
|
||
|
|
||
|
const renderer = {
|
||
|
templateId: 'template',
|
||
|
renderTemplate(container) { return container; },
|
||
|
renderElement(node, index, container) { container.textContent = node.element.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 sorter = new class {
|
||
|
compare(a, b) {
|
||
|
if (a.collapsible === b.collapsible) {
|
||
|
return a.name < b.name ? -1 : 1;
|
||
|
}
|
||
|
|
||
|
return a.collapsible ? -1 : 1;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const dataSource = new class {
|
||
|
hasChildren(element) {
|
||
|
return element === null || element.element.type === 'dir';
|
||
|
}
|
||
|
getChildren(element) {
|
||
|
return new Promise((c, e) => {
|
||
|
const xhr = new XMLHttpRequest();
|
||
|
xhr.open('GET', element ? `/api/readdir?path=${element.element.path}` : '/api/readdir');
|
||
|
xhr.send();
|
||
|
xhr.onreadystatechange = function () {
|
||
|
if (this.readyState == 4 && this.status == 200) {
|
||
|
const els = JSON.parse(this.responseText).map(element => ({
|
||
|
element,
|
||
|
collapsible: element.type === 'dir'
|
||
|
}));
|
||
|
|
||
|
c(els);
|
||
|
}
|
||
|
};
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const identityProvider = {
|
||
|
getId(node) {
|
||
|
return node.element.path;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const tree = new AsyncDataTree('test', container, delegate, [renderer], dataSource, { filter: treeFilter, sorter, identityProvider });
|
||
|
|
||
|
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('test', container, delegate, [renderer], dataSource, { filter: treeFilter, identityProvider });
|
||
|
|
||
|
tree.setInput({
|
||
|
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();
|
||
|
|
||
|
expandall.onclick = () => perf('expand all', () => tree.expandAll());
|
||
|
collapseall.onclick = () => perf('collapse all', () => tree.collapseAll());
|
||
|
renderwidth.onclick = () => perf('renderwidth', () => tree.layoutWidth(Math.random()));
|
||
|
|
||
|
const files = [];
|
||
|
for (let i = 0; i < 100000; i++) {
|
||
|
const errors = [];
|
||
|
|
||
|
for (let j = 1; j <= 3; j++) {
|
||
|
errors.push({ element: `error #${j} ` });
|
||
|
}
|
||
|
|
||
|
files.push({ element: `file #${i}`, children: errors });
|
||
|
}
|
||
|
|
||
|
perf('splice', () => tree.splice([0], 0, files));
|
||
|
break;
|
||
|
}
|
||
|
case '?data': {
|
||
|
const { tree, treeFilter } = createAsyncDataTree();
|
||
|
|
||
|
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.updateChildren());
|
||
|
|
||
|
tree.setInput(null);
|
||
|
|
||
|
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.updateChildren());
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case '?compressed': {
|
||
|
const { tree, treeFilter } = createCompressedObjectTree();
|
||
|
|
||
|
expandall.onclick = () => perf('expand all', () => tree.expandAll());
|
||
|
collapseall.onclick = () => perf('collapse all', () => tree.collapseAll());
|
||
|
|
||
|
const xhr = new XMLHttpRequest();
|
||
|
xhr.open('GET', '/compressed.json');
|
||
|
xhr.send();
|
||
|
xhr.onreadystatechange = function () {
|
||
|
if (this.readyState == 4 && this.status == 200) {
|
||
|
tree.setChildren(null, JSON.parse(this.responseText));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case '?height': {
|
||
|
const { tree, treeFilter } = createIndexTree({ supportDynamicHeights: true });
|
||
|
|
||
|
expandall.onclick = () => perf('expand all', () => tree.expandAll());
|
||
|
collapseall.onclick = () => perf('collapse all', () => tree.collapseAll());
|
||
|
renderwidth.onclick = () => perf('renderwidth', () => tree.layoutWidth(Math.random()));
|
||
|
|
||
|
const xhr = new XMLHttpRequest();
|
||
|
xhr.open('GET', '/api/ls?path=');
|
||
|
xhr.send();
|
||
|
xhr.onreadystatechange = function () {
|
||
|
if (this.readyState == 4 && this.status == 200) {
|
||
|
perf('splice', () => tree.splice([0], 0, [JSON.parse(this.responseText)]));
|
||
|
treeFilter.updatePattern();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// container.
|
||
|
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
default: {
|
||
|
const { tree, treeFilter } = createIndexTree();
|
||
|
|
||
|
expandall.onclick = () => perf('expand all', () => tree.expandAll());
|
||
|
collapseall.onclick = () => perf('collapse all', () => tree.collapseAll());
|
||
|
renderwidth.onclick = () => perf('renderwidth', () => tree.layoutWidth(Math.random()));
|
||
|
|
||
|
const xhr = new XMLHttpRequest();
|
||
|
xhr.open('GET', '/api/ls?path=');
|
||
|
xhr.send();
|
||
|
xhr.onreadystatechange = function () {
|
||
|
if (this.readyState == 4 && this.status == 200) {
|
||
|
perf('splice', () => tree.splice([0], 0, [JSON.parse(this.responseText)]));
|
||
|
treeFilter.updatePattern();
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
</script>
|
||
|
</body>
|
||
|
|
||
|
</html>
|