Modernize & simplify treeview component
parent
51879dc714
commit
394523c5b7
|
|
@ -7,9 +7,11 @@ module.exports = {
|
||||||
plugins: ["prettier"],
|
plugins: ["prettier"],
|
||||||
rules: {
|
rules: {
|
||||||
"class-methods-use-this": "off",
|
"class-methods-use-this": "off",
|
||||||
|
"max-classes-per-file": "off",
|
||||||
|
"no-console": "off",
|
||||||
|
"no-param-reassign": "off",
|
||||||
"prettier/prettier": "error",
|
"prettier/prettier": "error",
|
||||||
curly: ["error", "all"],
|
curly: ["error", "all"],
|
||||||
"no-console": "off",
|
|
||||||
},
|
},
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: "latest",
|
ecmaVersion: "latest",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* global gradioApp, TreeView, get_uiCurrentTabContent, onUiUpdate, onUiLoaded */
|
/* global gradioApp, get_uiCurrentTabContent, onUiUpdate, onUiLoaded */
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const SDDP_HELP_TEXTS = {
|
const SDDP_HELP_TEXTS = {
|
||||||
"sddp-disable-negative-prompt": "Don't use prompt magic on negative prompts.",
|
"sddp-disable-negative-prompt": "Don't use prompt magic on negative prompts.",
|
||||||
|
|
@ -15,6 +15,121 @@ const SDDP_HELP_TEXTS = {
|
||||||
"sddp-write-raw-template": "Write template into image metadata.",
|
"sddp-write-raw-template": "Write template into image metadata.",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SDDPTreeView {
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @property {object} handlers The attached event handlers
|
||||||
|
* @property {object} data The JSON object that represents the tree structure
|
||||||
|
* @property {Element} node The DOM element to render the tree in
|
||||||
|
*/
|
||||||
|
constructor(data, node) {
|
||||||
|
this.handlers = {};
|
||||||
|
this.node = node;
|
||||||
|
this.data = data;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the tree view in the DOM
|
||||||
|
*/
|
||||||
|
render = () => {
|
||||||
|
const container = this.node;
|
||||||
|
container.innerHTML = "";
|
||||||
|
this.data.forEach((item) => container.appendChild(this.renderNode(item)));
|
||||||
|
[...container.querySelectorAll(".tree-leaf-text,.tree-expando")].forEach(
|
||||||
|
(node) => node.addEventListener("click", this.handleClickEvent),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderNode = (item) => {
|
||||||
|
const leaf = document.createElement("div");
|
||||||
|
const content = document.createElement("div");
|
||||||
|
const text = document.createElement("div");
|
||||||
|
const expando = document.createElement("div");
|
||||||
|
leaf.setAttribute("class", "tree-leaf");
|
||||||
|
content.setAttribute("class", "tree-leaf-content");
|
||||||
|
text.setAttribute("class", "tree-leaf-text");
|
||||||
|
const { children, name, expanded } = item;
|
||||||
|
text.textContent = name;
|
||||||
|
expando.setAttribute("class", `tree-expando ${expanded ? "expanded" : ""}`);
|
||||||
|
expando.textContent = expanded ? "-" : "+";
|
||||||
|
content.appendChild(expando);
|
||||||
|
content.appendChild(text);
|
||||||
|
leaf.appendChild(content);
|
||||||
|
if (children?.length > 0) {
|
||||||
|
const childrenDiv = document.createElement("div");
|
||||||
|
childrenDiv.setAttribute("class", "tree-child-leaves");
|
||||||
|
children.forEach((child) => {
|
||||||
|
childrenDiv.appendChild(this.renderNode(child));
|
||||||
|
});
|
||||||
|
if (!expanded) {
|
||||||
|
childrenDiv.classList.add("hidden");
|
||||||
|
}
|
||||||
|
leaf.appendChild(childrenDiv);
|
||||||
|
} else {
|
||||||
|
expando.classList.add("hidden");
|
||||||
|
content.setAttribute("data-item", JSON.stringify(item));
|
||||||
|
}
|
||||||
|
return leaf;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClickEvent = (event) => {
|
||||||
|
const parent = (event.target || event.currentTarget).parentNode;
|
||||||
|
const leaves = parent.parentNode.querySelector(".tree-child-leaves");
|
||||||
|
if (leaves) {
|
||||||
|
this.setSubtreeVisibility(
|
||||||
|
parent,
|
||||||
|
leaves,
|
||||||
|
leaves.classList.contains("hidden"),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.emit("select", {
|
||||||
|
target: event,
|
||||||
|
data: JSON.parse(parent.getAttribute("data-item")),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expands/collapses by the expando or the leaf text
|
||||||
|
* @param {Element} node The parent node that contains the leaves
|
||||||
|
* @param {Element} leaves The leaves wrapper element
|
||||||
|
* @param {boolean} visible Expand or collapse?
|
||||||
|
* @param {boolean} skipEmit Skip emitting the event?
|
||||||
|
*/
|
||||||
|
setSubtreeVisibility(node, leaves, visible, skipEmit = false) {
|
||||||
|
leaves.classList.toggle("hidden", !visible);
|
||||||
|
node.querySelector(".tree-expando").textContent = visible ? "+" : "-";
|
||||||
|
if (skipEmit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.emit(visible ? "expand" : "collapse", {
|
||||||
|
target: node,
|
||||||
|
leaves,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
on(name, callback, context = null) {
|
||||||
|
const handlers = this.handlers[name] || [];
|
||||||
|
handlers.push({ callback, context });
|
||||||
|
this.handlers[name] = handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
off(name, callback) {
|
||||||
|
this.handlers[name] = (this.handlers[name] || []).filter(
|
||||||
|
(handle) => handle.callback !== callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(name, ...args) {
|
||||||
|
(this.handlers[name] || []).forEach((handle) => {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
handle.callback.apply(handle.context, args);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SDDP_UI {
|
class SDDP_UI {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.helpTextsConfigured = false;
|
this.helpTextsConfigured = false;
|
||||||
|
|
@ -86,7 +201,7 @@ class SDDP_UI {
|
||||||
if (!this.treeView) {
|
if (!this.treeView) {
|
||||||
const treeDiv = gradioApp().querySelector("#sddp-wildcard-tree");
|
const treeDiv = gradioApp().querySelector("#sddp-wildcard-tree");
|
||||||
if (treeDiv) {
|
if (treeDiv) {
|
||||||
treeView = new TreeView(content, treeDiv);
|
treeView = new SDDPTreeView(content, treeDiv);
|
||||||
treeView.on("select", this.onSelectNode.bind(this), null);
|
treeView.on("select", this.onSelectNode.bind(this), null);
|
||||||
this.treeView = treeView;
|
this.treeView = treeView;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,263 +0,0 @@
|
||||||
(function (define) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
(function (root, factory) {
|
|
||||||
if (typeof define === 'function' && define.amd) {
|
|
||||||
define(factory);
|
|
||||||
} else if (typeof exports === 'object') {
|
|
||||||
module.exports = factory();
|
|
||||||
} else {
|
|
||||||
root.TreeView = factory();
|
|
||||||
}
|
|
||||||
}(window, function () {
|
|
||||||
return (function () {
|
|
||||||
|
|
||||||
/** List of events supported by the tree view */
|
|
||||||
var events = [
|
|
||||||
'expand',
|
|
||||||
'expandAll',
|
|
||||||
'collapse',
|
|
||||||
'collapseAll',
|
|
||||||
'select'
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utilite function to check to see if something is a DOM object
|
|
||||||
* @param {object} Object to test
|
|
||||||
* @returns {boolean} If the object is a DOM object
|
|
||||||
*/
|
|
||||||
function isDOMElement(obj) {
|
|
||||||
try {
|
|
||||||
return obj instanceof HTMLElement;
|
|
||||||
} catch (e) {
|
|
||||||
// Some browsers don't support using the HTMLElement so some extra
|
|
||||||
// checks are needed.
|
|
||||||
return typeof obj === 'object' && obj.nodeType === 1 && typeof obj.style === 'object' && typeof obj.ownerDocument === 'object';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A forEach that will work with a NodeList and generic Arrays
|
|
||||||
* @param {array|NodeList} arr The array to iterate over
|
|
||||||
* @param {function} callback Function that executes for each element. First parameter is element, second is index
|
|
||||||
* @param {object} The context to execute callback with
|
|
||||||
*/
|
|
||||||
function forEach(arr, callback, scope) {
|
|
||||||
var i, len = arr.length;
|
|
||||||
for (i = 0; i < len; i += 1) {
|
|
||||||
callback.call(scope, arr[i], i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emit an event from the tree view
|
|
||||||
* @param {string} name The name of the event to emit
|
|
||||||
*/
|
|
||||||
function emit(instance, name) {
|
|
||||||
var args = [].slice.call(arguments, 2);
|
|
||||||
if (events.indexOf(name) > -1) {
|
|
||||||
if (instance.handlers[name] && instance.handlers[name] instanceof Array) {
|
|
||||||
forEach(instance.handlers[name], function (handle) {
|
|
||||||
window.setTimeout(function () {
|
|
||||||
handle.callback.apply(handle.context, args);
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error(name + ' event cannot be found on TreeView.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the tree view in the DOM
|
|
||||||
*/
|
|
||||||
function render(self) {
|
|
||||||
var container = isDOMElement(self.node) ? self.node : document.getElementById(self.node);
|
|
||||||
var leaves = [], click;
|
|
||||||
var renderLeaf = function (item) {
|
|
||||||
var leaf = document.createElement('div');
|
|
||||||
var content = document.createElement('div');
|
|
||||||
var text = document.createElement('div');
|
|
||||||
var expando = document.createElement('div');
|
|
||||||
|
|
||||||
leaf.setAttribute('class', 'tree-leaf');
|
|
||||||
content.setAttribute('class', 'tree-leaf-content');
|
|
||||||
content.setAttribute('data-item', JSON.stringify(item));
|
|
||||||
text.setAttribute('class', 'tree-leaf-text');
|
|
||||||
text.textContent = item.name;
|
|
||||||
expando.setAttribute('class', 'tree-expando ' + (item.expanded ? 'expanded' : ''));
|
|
||||||
expando.textContent = item.expanded ? '-' : '+';
|
|
||||||
content.appendChild(expando);
|
|
||||||
content.appendChild(text);
|
|
||||||
leaf.appendChild(content);
|
|
||||||
if (item.children && item.children.length > 0) {
|
|
||||||
var children = document.createElement('div');
|
|
||||||
children.setAttribute('class', 'tree-child-leaves');
|
|
||||||
forEach(item.children, function (child) {
|
|
||||||
var childLeaf = renderLeaf(child);
|
|
||||||
children.appendChild(childLeaf);
|
|
||||||
});
|
|
||||||
if (!item.expanded) {
|
|
||||||
children.classList.add('hidden');
|
|
||||||
}
|
|
||||||
leaf.appendChild(children);
|
|
||||||
} else {
|
|
||||||
expando.classList.add('hidden');
|
|
||||||
}
|
|
||||||
return leaf;
|
|
||||||
};
|
|
||||||
|
|
||||||
forEach(self.data, function (item) {
|
|
||||||
leaves.push(renderLeaf.call(self, item));
|
|
||||||
});
|
|
||||||
container.innerHTML = leaves.map(function (leaf) {
|
|
||||||
return leaf.outerHTML;
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
click = function (e) {
|
|
||||||
var parent = (e.target || e.currentTarget).parentNode;
|
|
||||||
var data = JSON.parse(parent.getAttribute('data-item'));
|
|
||||||
var leaves = parent.parentNode.querySelector('.tree-child-leaves');
|
|
||||||
if (leaves) {
|
|
||||||
if (leaves.classList.contains('hidden')) {
|
|
||||||
self.expand(parent, leaves);
|
|
||||||
} else {
|
|
||||||
self.collapse(parent, leaves);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
emit(self, 'select', {
|
|
||||||
target: e,
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
forEach(container.querySelectorAll('.tree-leaf-text'), function (node) {
|
|
||||||
node.onclick = click;
|
|
||||||
});
|
|
||||||
forEach(container.querySelectorAll('.tree-expando'), function (node) {
|
|
||||||
node.onclick = click;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @constructor
|
|
||||||
* @property {object} handlers The attached event handlers
|
|
||||||
* @property {object} data The JSON object that represents the tree structure
|
|
||||||
* @property {DOMElement} node The DOM element to render the tree in
|
|
||||||
*/
|
|
||||||
function TreeView(data, node) {
|
|
||||||
this.handlers = {};
|
|
||||||
this.node = node;
|
|
||||||
this.data = data;
|
|
||||||
render(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeView.prototype.render = function () {
|
|
||||||
render(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expands a leaflet by the expando or the leaf text
|
|
||||||
* @param {DOMElement} node The parent node that contains the leaves
|
|
||||||
* @param {DOMElement} leaves The leaves wrapper element
|
|
||||||
*/
|
|
||||||
TreeView.prototype.expand = function (node, leaves, skipEmit) {
|
|
||||||
var expando = node.querySelector('.tree-expando');
|
|
||||||
expando.textContent = '-';
|
|
||||||
leaves.classList.remove('hidden');
|
|
||||||
if (skipEmit) { return; }
|
|
||||||
emit(this, 'expand', {
|
|
||||||
target: node,
|
|
||||||
leaves: leaves
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
TreeView.prototype.expandAll = function () {
|
|
||||||
var self = this;
|
|
||||||
var nodes = document.getElementById(self.node).querySelectorAll('.tree-expando');
|
|
||||||
forEach(nodes, function (node) {
|
|
||||||
var parent = node.parentNode;
|
|
||||||
var leaves = parent.parentNode.querySelector('.tree-child-leaves');
|
|
||||||
if (parent && leaves && parent.hasAttribute('data-item')) {
|
|
||||||
self.expand(parent, leaves, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
emit(this, 'expandAll', {});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collapses a leaflet by the expando or the leaf text
|
|
||||||
* @param {DOMElement} node The parent node that contains the leaves
|
|
||||||
* @param {DOMElement} leaves The leaves wrapper element
|
|
||||||
*/
|
|
||||||
TreeView.prototype.collapse = function (node, leaves, skipEmit) {
|
|
||||||
var expando = node.querySelector('.tree-expando');
|
|
||||||
expando.textContent = '+';
|
|
||||||
leaves.classList.add('hidden');
|
|
||||||
if (skipEmit) { return; }
|
|
||||||
emit(this, 'collapse', {
|
|
||||||
target: node,
|
|
||||||
leaves: leaves
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
TreeView.prototype.collapseAll = function () {
|
|
||||||
var self = this;
|
|
||||||
var nodes = document.getElementById(self.node).querySelectorAll('.tree-expando');
|
|
||||||
forEach(nodes, function (node) {
|
|
||||||
var parent = node.parentNode;
|
|
||||||
var leaves = parent.parentNode.querySelector('.tree-child-leaves');
|
|
||||||
if (parent && leaves && parent.hasAttribute('data-item')) {
|
|
||||||
self.collapse(parent, leaves, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
emit(this, 'collapseAll', {});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach an event handler to the tree view
|
|
||||||
* @param {string} name Name of the event to attach
|
|
||||||
* @param {function} callback The callback to execute on the event
|
|
||||||
* @param {object} scope The context to call the callback with
|
|
||||||
*/
|
|
||||||
TreeView.prototype.on = function (name, callback, scope) {
|
|
||||||
if (events.indexOf(name) > -1) {
|
|
||||||
if (!this.handlers[name]) {
|
|
||||||
this.handlers[name] = [];
|
|
||||||
}
|
|
||||||
this.handlers[name].push({
|
|
||||||
callback: callback,
|
|
||||||
context: scope
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error(name + ' is not supported by TreeView.');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deattach an event handler from the tree view
|
|
||||||
* @param {string} name Name of the event to deattach
|
|
||||||
* @param {function} callback The function to deattach
|
|
||||||
*/
|
|
||||||
TreeView.prototype.off = function (name, callback) {
|
|
||||||
var index, found = false;
|
|
||||||
if (this.handlers[name] instanceof Array) {
|
|
||||||
this.handlers[name].forEach(function (handle, i) {
|
|
||||||
index = i;
|
|
||||||
if (handle.callback === callback && !found) {
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (found) {
|
|
||||||
this.handlers[name].splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return TreeView;
|
|
||||||
}());
|
|
||||||
}));
|
|
||||||
}(window.define));
|
|
||||||
Loading…
Reference in New Issue