diff --git a/QualityControl/public/object/ObjectTree.class.js b/QualityControl/public/object/ObjectTree.class.js index 4135b5e9b..a59f46b78 100644 --- a/QualityControl/public/object/ObjectTree.class.js +++ b/QualityControl/public/object/ObjectTree.class.js @@ -56,31 +56,19 @@ export default class ObjectTree extends Observable { } /** - * Open all or close all nodes of the tree - * @returns {undefined} - */ - toggleAll() { - this.open ? this.closeAll() : this.openAll(); - } - - /** - * Open all nodes of the tree - * @returns {undefined} + * Close all nodes of the tree */ - openAll() { - this.open = true; - this.children.forEach((child) => child.openAll()); + closeAll() { + this._closeAllRecursive(); this.notify(); } /** - * Close all nodes of the tree - * @returns {undefined} + * Recursively close all nodes without notifying. */ - closeAll() { + _closeAllRecursive() { this.open = false; - this.children.forEach((child) => child.closeAll()); - this.notify(); + this.children.forEach((child) => child._closeAllRecursive()); } /** diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js index f53b09d38..e3596cfe5 100644 --- a/QualityControl/public/object/objectTreePage.js +++ b/QualityControl/public/object/objectTreePage.js @@ -180,7 +180,7 @@ const treeRows = (model) => !model.object.tree ? model.object.tree.children.length === 0 ? h('.w-100.text-center', 'No objects found') - : model.object.tree.children.map((children) => treeRow(model, children, 0)); + : model.object.tree.children.map((children) => treeRow(model, children)); /** * Shows a line of object represented by parent node `tree`, also shows @@ -190,68 +190,71 @@ const treeRows = (model) => !model.object.tree ? * @param {Model} model - root model of the application * @param {ObjectTree} tree - data-structure containing an object per node * @param {number} level - used for indentation within recursive call of treeRow - * @returns {vnode} - virtual node element + * @returns {vnode[]} - virtual node element */ -function treeRow(model, tree, level) { - const padding = `${level}em`; - const levelDeeper = level + 1; - const children = tree.open ? tree.children.map((children) => treeRow(model, children, levelDeeper)) : []; - const path = tree.name; - const className = tree.object && tree.object === model.object.selected ? 'table-primary' : ''; +function treeRow(model, tree, level = 0) { + const { pathString, open, children, object, name } = tree; - if (model.object.searchInput) { - return []; - } else { - if (tree.object && tree.children.length === 0) { - return [leafRow(path, () => model.object.select(tree.object), className, padding, tree.name)]; - } else if (tree.object && tree.children.length > 0) { - return [ - leafRow(path, () => model.object.select(tree.object), className, padding, tree.name), - branchRow(path, tree, padding), - children, - ]; - } - return [ - branchRow(path, tree, padding), - children, - ]; + const childRow = open + ? children.flatMap((children) => treeRow(model, children, level + 1)) + : []; + + const rows = []; + + if (object) { + // Add a leaf row (final element; cannot be expanded further) + const className = object === model.object.selected ? 'table-primary' : ''; + const leaf = treeRowElement( + pathString, + name, + () => model.object.select(object), + iconBarChart, + className, + { + paddingLeft: `${level + 0.3}em`, + }, + ); + rows.push(leaf); + } + if (children.length > 0) { + // Add a branch row (expandable / collapsible element) + const branch = treeRowElement( + pathString, + name, + () => tree.toggle(), + open ? iconCaretBottom : iconCaretRight, + '', + { + paddingLeft: `${level + 0.3}em`, + }, + ); + rows.push(branch); } + + return [...rows, ...childRow]; } /** - * Creates a row containing specific visuals for leaf object and on selection - * it will plot the object with JSRoot - * @param {string} path - full name of the object - * @param {Action} selectItem - action for plotting the object - * @param {string} className - name of the row class - * @param {number} padding - space needed to be displayed so that leaf is within its parent - * @param {string} leafName - name of the object + * Creates a row containing specific visuals for either a branch or a leaf object + * and on click it will expand/collapse the branch or plot the leaf object with JSRoot + * @param {string} key - An unique identifier for this branch row element (table row) + * @param {string} title - The name of this tree object element + * @param {() => void} onclick - The action (callback) to perform upon clicking this branch row element (table row) + * @param {() => vnode} icon - Icon renderer for the row + * @param {string} className - Optional CSS class name(s) for the outer branch row element (table row) + * @param {object} style - Optional CSS styling for the inner branch row element (table data) * @returns {vnode} - virtual node element */ -const leafRow = (path, selectItem, className, padding, leafName) => +const treeRowElement = (key, title, onclick, icon, className = '', style = {}) => h('tr.object-selectable', { - key: path, title: path, onclick: selectItem, class: className, id: path, + key, + id: key, + title, + onclick, + class: className, }, [ - h('td.highlight', [ - h('span', { style: { paddingLeft: padding } }, iconBarChart()), - ' ', - leafName, - ]), - ]); - -/** - * Creates a row containing specific visuals for branch object and on selection - * it will open its children - * @param {string} path - full name of the object - * @param {ObjectTree} tree - current selected tree - * @param {number} padding - space needed to be displayed so that branch is within its parent - * @returns {vnode} - virtual node element - */ -const branchRow = (path, tree, padding) => - h('tr.object-selectable', { key: path, title: path, onclick: () => tree.toggle() }, [ - h('td.highlight', [ - h('span', { style: { paddingLeft: padding } }, tree.open ? iconCaretBottom() : iconCaretRight()), - ' ', - tree.name, + h('td.highlight.flex-row.items-center.g1', { style }, [ + icon(), + title, ]), ]);