Skip to content

Instantly share code, notes, and snippets.

@gszauer
Last active September 30, 2024 06:32
Show Gist options
  • Save gszauer/506ec829ad09eeff8e44cb6b96ed171f to your computer and use it in GitHub Desktop.
Save gszauer/506ec829ad09eeff8e44cb6b96ed171f to your computer and use it in GitHub Desktop.
TreeTest
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test Layout</title>
<style>
@import 'https://fonts.googleapis.com/css?family=Open+Sans';
* {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
font-family: 'Open Sans', sans-serif;
line-height: 1.75em;
font-size: 16px;
background-color: #222;
color: #aaa;
}
p {
font-size: 16px;
}
h1 {
font-size: 30px;
line-height: 34px;
}
h2 {
font-size: 20px;
line-height: 25px;
}
h3 {
font-size: 16px;
line-height: 27px;
padding-top: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #D8D8D8;
border-top: 1px solid #D8D8D8;
}
hr {
height: 1px;
background-color: #d8d8d8;
border: none;
width: 100%;
margin: 0px;
}
a[href] {
color: #1e8ad6;
}
a[href]:hover {
color: #3ba0e6;
}
img {
max-width: 100%;
}
li {
line-height: 1.5em;
}
aside,
[class *= "sidebar"],
[id *= "sidebar"] {
max-width: 90%;
margin: 0 auto;
border: 1px solid lightgrey;
padding: 5px 15px;
}
@media (min-width: 1921px) {
body {
font-size: 18px;
}
}
</style>
</head>
<body>
<script type="text/javascript" src="UITree.js"></script>
<script>
const tree = new UITree();
document.body.appendChild(tree.div);
const node1 = tree.CreateNode(null);
node1.text = "Parent";
node1.MakeFolder();
const node2 = node1.CreateChild(null);
node2.text = "Child [folder]";
node2.canBeCollapsed = true;
node2.MakeFolder();
node2.CreateChild(null).text = "Foo";
const node3 = node1.CreateChild(null);
node3.text = "Bar [File]";
node3.MakeFile();
node3.canBeCollapsed = false;
node1.CreateChild(null).text = "Zig";
node1.CreateChild(null).text = "Zag";
</script>
</body>
</html>
if (document.GenUUID === undefined) {
document.GenUUID = () => {
const guid = "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
(+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16)
);
return guid;
};
}
class UITreeNode {
static IndentSizeInPixels = 20;
onTextEdited = null; // function(self : UITreeNode, text: string);
onDestroy = null; // function(self : UITreeNode)
onParentChanged = null; // function(self : UITreeNode)
_uiTree = null;
get tree() { return this._uiTree; }
_uuid = null;
get uuid() { return this._uuid; }
_userData = null;
get userData() { return this._userData; }
_parent = null;
get parent() { return this._parent; }
_child = null;
get firstChild() { return this._child; }
_sibling = null;
get nextSibling() { return this._sibling; }
_canBeCollapsed = null; // true (for folder), false (for files), or null (for default behavior)
get canBeCollapsed() {
return this._canBeCollapsed;
}
set canBeCollapsed(v) {
if (v === true) {
this._canBeCollapsed = true;
}
else if (v === null) {
this._canBeCollapsed = null;
}
else {
this._canBeCollapsed = false;
}
}
get text() {
return this._html.content.innerHTML;
}
set text(value) {
if (this._html.editor !== null) {
throw new Error("UITree:setText: Can't set text while being edited");
}
this._html.content.innerHTML = "" + value;
}
_forceOpen = false;
_isOpen = false;
get isOpen() {
if (this._forceOpen) {
return true;
}
return this._isOpen;
}
set isOpen(value) {
if (value) {
this._isOpen = true;
this._ShowDownArrow();
}
else { // false on falsy
this._isOpen = false;
this._ShowRightArrow();
}
}
get isVisible() {
for (let iter = this._parent; iter != null; iter = iter._parent) {
if (!iter.isOpen) {
return false;
}
}
return true;
}
_html = {
root: null,
spacer: null,
content: null,
expander: null,
controlls: null,
edit: null,
delete: null,
editor: null,
icon: null
};
MakeFile() {
this.canBeCollapsed = false;
this.ShowIcon(UITree.FileIconHtml);
}
MakeFolder() {
this.canBeCollapsed = true;
this.ShowIcon(UITree.FolderIconHtml);
}
ShowIcon(innerHtml) {
if (this._html.icon === null) {
return;
}
if (innerHtml === null || innerHtml === undefined || innerHtml.length === 0) {
this._html.icon.innerHTML = "";
this._html.icon.classList.add("UITreeNode-Hide");
}
else {
this._html.icon.classList.remove("UITreeNode-Hide");
this._html.icon.innerHTML = "" + innerHtml;
}
}
_NullOutHtml() {
this._html.root = null;
this._html.spacer = null;
this._html.content = null;
this._html.expander = null;
this._html.controlls = null;
this._html.edit = null;
this._html.delete = null;
this._html.editor = null;
this._html.icon = null;
}
_GetRootElement(e) {
// Make sure something is passed in
if (e === null || e === undefined) {
return null;
}
// and that it has a class list
if (e.classList === undefined) {
return null;
}
{ // Validate that one of the UITreeNode styles is present
let isValid = false;
const styles = UITree._GetTreeNodeStyles();
const classList = e.classList;
for (let i = 0, len = styles.length; i < len; ++i) {
if (classList.contains(styles[i])) {
isValid = true;
}
}
if (e.namespaceURI == 'http://www.w3.org/2000/svg') {
isValid = true;
}
if (!isValid) {
return null;
}
}
while(!e.classList.contains("UITreeNode")) {
e = e.parentElement;
if (e === null) {
return null;
}
}
return e;
}
constructor(owner, userdata = null) {
this._uuid = document.GenUUID();
if (owner instanceof UITree) {
this._uiTree = owner;
}
else {
throw new Error("UITreeNode::constructor: Invalid first argument")
}
this._userData = userdata;
this._CreateHTMLElements()
owner.div.appendChild(this._html.root);
this.text = this._uuid;
this._AddContentControls();
this._AddDragDropSupport();
this.isOpen = false; // setter also updates icon!
if (owner.onNodeCreated != null) {
owner.onNodeCreated(this);
}
owner.all.set(this._uuid, this);
this._uiTree._UpdateNodesVisually();
}
IsDescendantOf(otherNode) {
if (otherNode === null || otherNode === undefined || otherNode == this) {
return false;
}
for (let iter = this; iter != null; iter = iter.parent) {
if (iter === otherNode) {
return true;
}
}
return false;
}
ForEachDepthFirst(callback) {
const root = this;
let itr = root;
let traversing = true;
while (traversing) {
callback(itr);
if (itr._child) {
itr = itr._child;
}
else {
while (itr._sibling === null) {
if (itr === root) {
traversing = false;
break;
}
itr = itr._parent;
}
if (itr === root) { // Prevent stepping to the roots sibling
traversing = false;
break;
}
itr = itr._sibling;
}
}
}
CreateChild(userdata) {
const childNode = new UITreeNode(this._uiTree, userdata);
this.AddChild(childNode);
return childNode;
}
AddChild(child) {
if (child._parent === this) {
return; // Early out
}
if (child._parent !== null) {
if (!child._parent._RemoveChild(child)) {
throw new Error("UITreeNode::AddChild: Failed to remove child from old parent");
}
}
if (child._sibling !== null) {
throw new Error("UITreeNode::AddChild: Expected child.nextSibling to be null");
}
if (this._child === null) {
this._child = child;
}
else {
for (let iter = this._child; iter !== null; iter = iter._sibling) {
if (iter._sibling === null) {
iter._sibling = child;
break;
}
}
}
child._sibling = null;
child._parent = this;
if (this.onParentChanged !== null) {
this.onParentChanged(this);
}
if (this._uiTree.onNodeParentChanged !== null) {
this._uiTree.onNodeParentChanged(this);
}
this.isOpen = true;
this._uiTree._UpdateNodesVisually();
}
_RemoveChild(child) {
let result = false;
let last = null;
for (let iter = this._child; iter !== null; iter = iter._sibling) {
if (iter === child) {
if (last === null) { // Removing head
this._child = child._sibling;
}
else {
last._sibling = child._sibling;
}
child._sibling = null;
child._parent = null;
result = true;
break;
}
last = iter;
}
//child._UpdateIndentationStyle();
//this._UpdateIndentationStyle();
return result;
}
Destroy() {
if (this._parent !== null) {
this._parent._RemoveChild(this);
}
if (this._sibling !== null) {
throw new Error("Can't destroy a node with active siblings");
}
const cleanup = [];
this.ForEachDepthFirst((node) => {
cleanup.push(node);
});
for (let i = 0, len = cleanup.length; i < len; ++i) {
cleanup[i]._DestroyInstance();
}
}
_DestroyInstance() {
if (this.onDestroy !== null) {
this.onDestroy(this);
}
if (this._uiTree.onNodeDestroyed !== null) {
this._uiTree.onNodeDestroyed(this);
}
this._uiTree.all.delete(this._uuid);
if (this._uiTree.selected === this) {
this._uiTree.selected = null;
}
this._html.root.remove();
this._NullOutHtml();
}
_ShowDownArrow() {
// https://iconmonstr.com/caret-down-filled-svg/
if (this._html.expander != null) {
this._html.expander.innerHTML = `<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m16.843 10.211c.108-.141.157-.3.157-.456 0-.389-.306-.755-.749-.755h-8.501c-.445 0-.75.367-.75.755 0 .157.05.316.159.457 1.203 1.554 3.252 4.199 4.258 5.498.142.184.36.29.592.29.23 0 .449-.107.591-.291 1.002-1.299 3.044-3.945 4.243-5.498z"/></svg>`;
}
}
_ShowRightArrow() {
if (this._html.expander != null) {
// https://iconmonstr.com/caret-right-lined-svg/
this._html.expander.innerHTML = `<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m10.211 7.155c-.141-.108-.3-.157-.456-.157-.389 0-.755.306-.755.749v8.501c0 .445.367.75.755.75.157 0 .316-.05.457-.159 1.554-1.203 4.199-3.252 5.498-4.258.184-.142.29-.36.29-.592 0-.23-.107-.449-.291-.591zm.289 7.563v-5.446l3.522 2.719z" fill-rule="nonzero"/></svg>`;
}
}
_ShowEditIcon() {
if (this._html.edit !== null) {
//https://iconmonstr.com/pencil-square-filled-svg/
this._html.edit.innerHTML = `<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m11.25 6c.398 0 .75.352.75.75 0 .414-.336.75-.75.75-1.505 0-7.75 0-7.75 0v12h17v-8.75c0-.414.336-.75.75-.75s.75.336.75.75v9.25c0 .621-.522 1-1 1h-18c-.48 0-1-.379-1-1v-13c0-.481.38-1 1-1zm-2.011 6.526c-1.045 3.003-1.238 3.45-1.238 3.84 0 .441.385.626.627.626.272 0 1.108-.301 3.829-1.249zm.888-.889 3.22 3.22 8.408-8.4c.163-.163.245-.377.245-.592 0-.213-.082-.427-.245-.591-.58-.578-1.458-1.457-2.039-2.036-.163-.163-.377-.245-.591-.245-.213 0-.428.082-.592.245z" fill-rule="nonzero"/></svg>`;
}
}
_ShowCommitIcon() {
if (this._html.edit !== null) {
this._html.edit.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/></svg>`;
}
}
_CommitEditIfAvailable() {
if (this._html.editor !== null) {
const dispalyString = "" + this._html.editor.value;
this._html.controlls.style.display = "";
this._ShowEditIcon();
this._html.editor.remove();
this._html.editor = null;
this._html.content.innerHTML = dispalyString;
if (this.onTextEdited !== null) {
this.onTextEdited(this, dispalyString);
}
if (this._uiTree.onNodeTextEdited !== null) {
this._uiTree.onNodeTextEdited(this, dispalyString);
}
}
}
_CreateHTMLElements() {
this._html.root = document.createElement("div");
this._html.root.classList.add("UITreeNode");
this._html.root.id = this._uuid;
this._html.root.onclick = () => {
this._uiTree.selected = this;
};
this._html.spacer = document.createElement("div");
this._html.spacer.classList.add("UITreeNode-Spacer");
this._html.root.appendChild(this._html.spacer);
this._html.expander = document.createElement("button");
this._html.expander.classList.add("UITreeNode-Expander");
this._html.root.appendChild(this._html.expander);
this._html.icon = document.createElement("div");
this._html.icon.classList.add("UITreeNode-Icon");
//this._html.icon.classList.add("UITreeNode-Hide");
this._html.root.appendChild(this._html.icon);
this._html.expander.onclick = (e) => {
this.isOpen = !this.isOpen;
this._uiTree._UpdateNodesVisually();
};
this._html.content = document.createElement("div");
this._html.content.classList.add("UITreeNode-Content");
this._html.root.appendChild(this._html.content);
}
_AddContentControls() {
this._html.controlls = document.createElement("div");
this._html.controlls.classList.add("UITreeNode-Controls");
this._html.root.appendChild(this._html.controlls);
this._html.edit = document.createElement("button");
this._html.edit.classList.add("UITreeNode-Control");
this._ShowEditIcon();
this._html.controlls.appendChild(this._html.edit);
this._html.delete = document.createElement("button");
this._html.delete.classList.add("UITreeNode-Control");
// https://iconmonstr.com/trash-can-filled-svg/
this._html.delete.innerHTML = `<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m20.015 6.506h-16v14.423c0 .591.448 1.071 1 1.071h14c.552 0 1-.48 1-1.071 0-3.905 0-14.423 0-14.423zm-5.75 2.494c.414 0 .75.336.75.75v8.5c0 .414-.336.75-.75.75s-.75-.336-.75-.75v-8.5c0-.414.336-.75.75-.75zm-4.5 0c.414 0 .75.336.75.75v8.5c0 .414-.336.75-.75.75s-.75-.336-.75-.75v-8.5c0-.414.336-.75.75-.75zm-.75-5v-1c0-.535.474-1 1-1h4c.526 0 1 .465 1 1v1h5.254c.412 0 .746.335.746.747s-.334.747-.746.747h-16.507c-.413 0-.747-.335-.747-.747s.334-.747.747-.747zm4.5 0v-.5h-3v.5z" fill-rule="nonzero"/></svg>`;
this._html.controlls.appendChild(this._html.delete);
this._html.delete.onclick = () => {
setTimeout(() => {
this._CommitEditIfAvailable();
this.Destroy();
}, 1);
};
this._html.edit.onclick = () => {
setTimeout(() => {
const commit = () => {
this._CommitEditIfAvailable();
}
if (this._html.controlls.style.display != "flex") {
const dispalyString = this.text;
this._html.controlls.style.display = "flex"
this._ShowCommitIcon();
this._html.content.innerHTML = "";
this._html.editor = document.createElement("input");
this._html.editor.type = "text";
this._html.editor.classList.add("UITreeNode-Editor");
this._html.editor.value = dispalyString;
this._html.content.appendChild(this._html.editor);
this._html.editor.onkeydown = (e) => {
if (e.key === "Enter" || e.key == "Tab") {
commit();
}
};
}
else {
commit();
}
}, 1);
}
}
_AddDragDropSupport() {
const isString = value => typeof value === 'string' || value instanceof String;
const getTargetNodeFromDragEvent = (ev) =>{
const targetElement = this._GetRootElement(ev.target);
if (targetElement !== null) {
const targetNode = this._uiTree.all.get(targetElement.id);
if (targetNode !== null && targetNode !== undefined) {
return targetNode;
}
}
return null;
}
const validateDragDrop = (srcNode, dstNode) => {
if (srcNode == dstNode) {
return false;
}
if (srcNode._uiTree !== dstNode._uiTree) {
console.log("Dragging between disimilar trees");
return false;
}
if (srcNode.parent == dstNode) {
return false;
}
if (dstNode.canBeCollapsed === false) { // null and true are ok
return false;
}
if (dstNode.IsDescendantOf(srcNode)) {
return false;
}
return true;
};
this._html.root.draggable = true;
this._html.root.addEventListener("dragstart", (ev) => {
const srcNode = getTargetNodeFromDragEvent(ev);
if (srcNode !== null) {
const dragData = srcNode.uuid;
ev.dataTransfer.clearData();
ev.dataTransfer.setData("uitreenode", dragData);
this._uiTree.selected = this;
srcNode._html.root.classList.add("UITreeNode-Dragging");
}
});
this._html.root.addEventListener("dragend", (ev) => {
const srcNode = getTargetNodeFromDragEvent(ev);
if (srcNode !== null) {
srcNode._html.root.classList.remove("UITreeNode-Dragging");
}
});
this._html.root.addEventListener("dragover", (ev) => {
const types = ev.dataTransfer.types;
if (!types.includes("uitreenode")) {
return; // Only allow the drag over here
}
ev.preventDefault();
const dstNode = getTargetNodeFromDragEvent(ev);
});
this._html.root.addEventListener("drop", (ev) => {
const data = ev.dataTransfer.getData("uitreenode");
let dropped = false;
if (isString(data) && data.length > 0) {
if (this._uiTree.all.has(data)) {
const srcNode = this._uiTree.all.get(data);
const dstNode = getTargetNodeFromDragEvent(ev);
let valid = true;
if (srcNode !== null && srcNode !== undefined) {
srcNode._html.root.classList.remove("UITreeNode-Dragging");
if (dstNode !== null && dstNode !== undefined) {
if (validateDragDrop(srcNode, dstNode)) {
srcNode._CommitEditIfAvailable();
dstNode._CommitEditIfAvailable();
dstNode.AddChild(srcNode); // Triggers OnParentChanged
//console.log("drop(uitreenode) - from: " + srcNode.uuid + ", to: " + dstNode.uuid);
dropped = true;
}
}
}
}
}
/*if (!dropped) {
console.log("drop(uitreenode): invalid drop")
}*/
if (this._html.root !== null) {
this._html.root.classList.remove("UITreeNode-Highlighted");
}
ev.preventDefault();
});
this._html.root.addEventListener("dragenter", (ev) => {
const data = ev.dataTransfer.getData("uitreenode");
if (this._html.root !== null) {
if (this.canBeCollapsed !== false) {
this._html.root.classList.add("UITreeNode-Highlighted");
}
}
});
this._html.root.addEventListener("dragleave", (ev) => {
if (this._html.root !== null) {
this._html.root.classList.remove("UITreeNode-Highlighted");
}
});
}
}
class UITree {
onNodeCreated = null; // fubction(newNode : UITreeNode)
onNodeDestroyed = null; // function(node : UITreeNode)
onNodeTextEdited = null; // function(node : UITreeNode, text : string)
onNodeParentChanged = null; // function(node : UITreeNode)
onSelectionChanged = null; // function(node : UITreeNode)
_uuid = null;
get uuid() { return this._uuid; }
_div = null;
get div() { return this._div; }
_all = null;
get all() { return this._all; }
_root = null;
get root() { return this._root; }
_selected = null;
get selected() {
return this._selected;
}
set selected(node) {
const prev = this._selected;
if (prev !== node) {
if (prev !== null) {
if (prev._html.root != null) {
prev._html.root.classList.remove("UITreeNode-Selected");
}
prev._CommitEditIfAvailable();
}
if (node !== null) {
if (node._html.root != null) {
node._html.root.classList.add("UITreeNode-Selected");
}
}
if (this.onSelectionChanged !== null) {
this.onSelectionChanged(node);
}
}
this._selected = node;
}
// arg1: name of a div on the page to use, or null
constructor(divName = null) {
UITree._CreateStyles();
this._uuid = document.GenUUID();
if (typeof divName === "string") {
this._div = document.getElementById(divName);
}
else if (divName === null) {
this._div = document.createElement("div");
}
else {
throw new Error("UITree::constructor Invalid first argument");
}
this._all = new Map();
this._div.classList.className = "";
this._div.classList.add("UITree");
this._div.id = this._uuid;
this._root = new UITreeNode(this, null);
this._root._html.root.remove();
this._root._NullOutHtml();
this._root._forceOpen = true;
}
CreateNode(userdata) {
return this._root.CreateChild(userdata);
}
_UpdateNodesVisually() {
if (this._root === null) {
return;
}
let index = 0;
this._root.ForEachDepthFirst((node) => {
// Sort
if (node._html.root !== null) {
node._html.root.style.order = index++;
}
// Update visibility
if (!node.isVisible) {
if (node._html.root !== null) {
node._html.root.style.display = "none";
}
}
else {
if (node._html.root !== null) {
node._html.root.style.display = "flex";
}
}
// Update indentation
let indent = 0;
for (let iter = node.parent; iter != null; iter = iter.parent) {
indent += 1;
}
if (node._html.spacer != null) {
node._html.spacer.style.width = Math.floor(indent * UITreeNode.IndentSizeInPixels) + "px";
}
// Show / hide arrow
if (node._html.expander != null) {
if (node.canBeCollapsed === true) {
node._html.expander.style.display = "inline-block";
}
else if (node.canBeCollapsed === false) {
node._html.expander.style.display = "none";
}
// canBeCollapsed is null if we're here
else if (node._child === null) {
node._html.expander.style.display = "none";
}
else {
node._html.expander.style.display = "inline-block";
}
}
});
}
static _stylesCreated = false;
static _CreateStyles() {
if (UITree._stylesCreated) {
return;
}
{
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = `
.UITree {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.UITreeNode {
display: flex;
flex-direction: row;
height: 24px;
}
.UITreeNode-Spacer {
flex-grow: 0;
flex-shrink: 0;
height: 24px;
}
.UITreeNode-Content {
flex-grow: 1;
overflow: hidden;
cursor: default;
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard */
}
.UITreeNode-Expander {
flex-grow: 0;
flex-shrink: 0;
width: 24px;
height: 24px;
padding: 0;
margin: 0;
border: 0;
align-self: center;
}
.UITreeNode-Controls {
/*display:flex;*/
display:none;
flex-grow: 0;
flex-shrink: 0;
}
.UITreeNode:hover .UITreeNode-Controls {
display:flex;
}
.UITreeNode-Control {
flex-grow: 0;
flex-shrink: 0;
width: 24px;
height: 24px;
padding: 0;
margin: 0;
border: 0;
align-self: center;
margin-left: 5px;
}
.UITreeNode-Editor {
width: 100%;
margin-left: 5px;
align-self: center;
height: 24px;
}
.UITreeNode-Icon {
flex-grow: 0;
flex-shrink: 0;
height: 24px;
layout: flow;
}
.UITreeNode-Hide {
display: none;
}
.UITreeNode-Selected {
background-color: #ff0000;
}
.UITreeNode-Highlighted {
background-color: #00ff00;
}
.UITreeNode-Dragging {
background-color: #0000ff;
}`;
document.head.appendChild(style);
}
UITree._stylesCreated = true;
}
static _GetTreeNodeStyles() {
return [
"UITreeNode-Editor",
"UITreeNode-Controls",
"UITreeNode-Control",
"UITreeNode-Expander",
"UITreeNode-Content",
"UITreeNode-Spacer",
"UITreeNode",
"UITreeNode-Icon"
];
}
static get FileIconHtml() {
return `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path fill="#ffffff" d="M22 24h-20v-24h14l6 6v18zm-7-23h-12v22h18v-16h-6v-6zm3 15v1h-12v-1h12zm0-3v1h-12v-1h12zm0-3v1h-12v-1h12zm-2-4h4.586l-4.586-4.586v4.586z"/></svg>`;
}
static get FolderIconHtml() {
return `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path fill="#ffffff" d="M0 2h8l3 3h10v4h3l-4 13h-20v-20zm22.646 8h-17.907l-3.385 11h17.907l3.385-11zm-2.646-1v-3h-9.414l-3-3h-6.586v15.75l3-9.75h16z"/></svg>`;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment