From cd0f8afd30d62df6d62d81727072078894e2ca40 Mon Sep 17 00:00:00 2001
From: hehoon <100522372+hehoon@users.noreply.github.com>
Date: Sat, 13 Dec 2025 13:21:16 +0100
Subject: [PATCH 1/9] Fix tree table rendering by flattening recursive rows
with flatMap
---
.../public/object/objectTreePage.js | 111 +++++++++++-------
1 file changed, 69 insertions(+), 42 deletions(-)
diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js
index f53b09d38..10df6ab2d 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
@@ -192,49 +192,68 @@ const treeRows = (model) => !model.object.tree ?
* @param {number} level - used for indentation within recursive call of treeRow
* @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 && object === model.object.selected ? 'table-primary' : '';
+ const leaf = leafRow(
+ pathString,
+ name,
+ () => model.object.select(object),
+ className,
+ {
+ paddingLeft: `${level}em`,
+ },
+ );
+ rows.push(leaf);
+ }
+ if (children.length > 0) {
+ // Add a branch row (expandable / collapsible element)
+ const branch = branchRow(
+ pathString,
+ open,
+ name,
+ () => tree.toggle(),
+ {
+ paddingLeft: `${level}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
+ * @param {string} key - An unique identifier for this branch row element (table row)
+ * @param {string} leafName - The name of this tree object element
+ * @param {() => void} onClick - The action (callback) to perform upon clicking this branch row element (table row)
+ * @param {string} className - Optional CSS class name(s) for the outer branch row element (table row)
+ * @param {object} styling - Optional CSS styling for the inner branch row element (table data)
* @returns {vnode} - virtual node element
*/
-const leafRow = (path, selectItem, className, padding, leafName) =>
+const leafRow = (key, leafName, onClick, className = '', styling = {}) =>
h('tr.object-selectable', {
- key: path, title: path, onclick: selectItem, class: className, id: path,
+ key: key,
+ id: key,
+ title: leafName,
+ onclick: onClick,
+ class: className,
}, [
- h('td.highlight', [
- h('span', { style: { paddingLeft: padding } }, iconBarChart()),
- ' ',
+ h('td.highlight.flex-row.items-center.g1', {
+ style: styling,
+ }, [
+ iconBarChart(),
leafName,
]),
]);
@@ -242,16 +261,24 @@ const leafRow = (path, selectItem, className, padding, 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
+ * @param {string} key - An unique identifier for this branch row element (table row)
+ * @param {boolean} isTreeOpen - Whether the current tree object is open (expanded)
+ * @param {string} branchName - The name of this tree object element
+ * @param {() => void} onClick - The action (callback) to perform upon clicking this branch row element (table row)
+ * @param {object} styling - Optional CSS styling for the inner branch row element (table data)
* @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,
+const branchRow = (key, isTreeOpen, branchName, onClick, styling = {}) =>
+ h('tr.object-selectable', {
+ key: key,
+ id: key,
+ title: branchName,
+ onclick: onClick,
+ }, [
+ h('td.highlight.flex-row.items-center.g1', {
+ style: styling,
+ }, [
+ isTreeOpen ? iconCaretBottom() : iconCaretRight(),
+ branchName,
]),
]);
From c973d8fd65d9f7443fad29094e4769abbbc67b2c Mon Sep 17 00:00:00 2001
From: hehoon <100522372+hehoon@users.noreply.github.com>
Date: Sat, 13 Dec 2025 13:22:09 +0100
Subject: [PATCH 2/9] Optimize tree open/close operations by only calling
notify() at the end
---
.../public/object/ObjectTree.class.js | 24 ++++++++++++++-----
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/QualityControl/public/object/ObjectTree.class.js b/QualityControl/public/object/ObjectTree.class.js
index 4135b5e9b..d2d474432 100644
--- a/QualityControl/public/object/ObjectTree.class.js
+++ b/QualityControl/public/object/ObjectTree.class.js
@@ -65,24 +65,36 @@ export default class ObjectTree extends Observable {
/**
* Open all nodes of the tree
- * @returns {undefined}
*/
openAll() {
- this.open = true;
- this.children.forEach((child) => child.openAll());
+ this._openAllRecursive();
this.notify();
}
+ /**
+ * Recursively open all nodes without notifying.
+ */
+ _openAllRecursive() {
+ this.open = true;
+ this.children.forEach((child) => child._openAllRecursive());
+ }
+
/**
* Close all nodes of the tree
- * @returns {undefined}
*/
closeAll() {
- this.open = false;
- this.children.forEach((child) => child.closeAll());
+ this._closeAllRecursive();
this.notify();
}
+ /**
+ * Recursively close all nodes without notifying.
+ */
+ _closeAllRecursive() {
+ this.open = false;
+ this.children.forEach((child) => child._closeAllRecursive());
+ }
+
/**
* Add recursively an object inside a tree
* @param {object} object - The object to be inserted, property name must exist
From c7f8ca1e75a675511ec349e28a4544d5c97b2189 Mon Sep 17 00:00:00 2001
From: hehoon <100522372+hehoon@users.noreply.github.com>
Date: Sat, 13 Dec 2025 13:33:46 +0100
Subject: [PATCH 3/9] Fix useless conditional
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
---
QualityControl/public/object/objectTreePage.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js
index 10df6ab2d..77f04fd5c 100644
--- a/QualityControl/public/object/objectTreePage.js
+++ b/QualityControl/public/object/objectTreePage.js
@@ -203,7 +203,7 @@ function treeRow(model, tree, level = 0) {
if (object) {
// Add a leaf row (final element; cannot be expanded further)
- const className = object && object === model.object.selected ? 'table-primary' : '';
+ const className = object === model.object.selected ? 'table-primary' : '';
const leaf = leafRow(
pathString,
name,
From 1e2518e1f42d85d85531a2ee460b50932dfd58ed Mon Sep 17 00:00:00 2001
From: hehoon <100522372+hehoon@users.noreply.github.com>
Date: Tue, 16 Dec 2025 21:23:00 +0100
Subject: [PATCH 4/9] Remove the `toggleAll` and `openAll` and
`_openAllRecursive` methods
---
.../public/object/ObjectTree.class.js | 24 -------------------
1 file changed, 24 deletions(-)
diff --git a/QualityControl/public/object/ObjectTree.class.js b/QualityControl/public/object/ObjectTree.class.js
index d2d474432..a59f46b78 100644
--- a/QualityControl/public/object/ObjectTree.class.js
+++ b/QualityControl/public/object/ObjectTree.class.js
@@ -55,30 +55,6 @@ export default class ObjectTree extends Observable {
this.notify();
}
- /**
- * Open all or close all nodes of the tree
- * @returns {undefined}
- */
- toggleAll() {
- this.open ? this.closeAll() : this.openAll();
- }
-
- /**
- * Open all nodes of the tree
- */
- openAll() {
- this._openAllRecursive();
- this.notify();
- }
-
- /**
- * Recursively open all nodes without notifying.
- */
- _openAllRecursive() {
- this.open = true;
- this.children.forEach((child) => child._openAllRecursive());
- }
-
/**
* Close all nodes of the tree
*/
From 5b4b606bb94fe67b732ee209b1a229e47cba6b6e Mon Sep 17 00:00:00 2001
From: hehoon <100522372+hehoon@users.noreply.github.com>
Date: Tue, 16 Dec 2025 21:31:22 +0100
Subject: [PATCH 5/9] Refactor `branchRow` and `leafRow` into one
`treeRowElement` function
---
.../public/object/objectTreePage.js | 56 ++++++-------------
1 file changed, 17 insertions(+), 39 deletions(-)
diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js
index 77f04fd5c..e87d57a6c 100644
--- a/QualityControl/public/object/objectTreePage.js
+++ b/QualityControl/public/object/objectTreePage.js
@@ -190,7 +190,7 @@ 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 = 0) {
const { pathString, open, children, object, name } = tree;
@@ -204,10 +204,11 @@ function treeRow(model, tree, level = 0) {
if (object) {
// Add a leaf row (final element; cannot be expanded further)
const className = object === model.object.selected ? 'table-primary' : '';
- const leaf = leafRow(
+ const leaf = treeRowElement(
pathString,
name,
() => model.object.select(object),
+ iconBarChart,
className,
{
paddingLeft: `${level}em`,
@@ -217,11 +218,12 @@ function treeRow(model, tree, level = 0) {
}
if (children.length > 0) {
// Add a branch row (expandable / collapsible element)
- const branch = branchRow(
+ const branch = treeRowElement(
pathString,
- open,
name,
() => tree.toggle(),
+ open ? iconCaretBottom : iconCaretRight,
+ '',
{
paddingLeft: `${level}em`,
},
@@ -233,52 +235,28 @@ function treeRow(model, tree, level = 0) {
}
/**
- * Creates a row containing specific visuals for leaf object and on selection
- * it will plot the object with JSRoot
+ * 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} leafName - The name of this tree object element
- * @param {() => void} onClick - The action (callback) to perform upon clicking this branch row element (table row)
+ * @param {string} name - 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} styling - Optional CSS styling for the inner branch row element (table data)
* @returns {vnode} - virtual node element
*/
-const leafRow = (key, leafName, onClick, className = '', styling = {}) =>
+const treeRowElement = (key, name, onclick, icon, className = '', styling = {}) =>
h('tr.object-selectable', {
- key: key,
+ key,
id: key,
- title: leafName,
- onclick: onClick,
+ title: name,
+ onclick,
class: className,
}, [
h('td.highlight.flex-row.items-center.g1', {
style: styling,
}, [
- iconBarChart(),
- leafName,
- ]),
- ]);
-
-/**
- * Creates a row containing specific visuals for branch object and on selection
- * it will open its children
- * @param {string} key - An unique identifier for this branch row element (table row)
- * @param {boolean} isTreeOpen - Whether the current tree object is open (expanded)
- * @param {string} branchName - The name of this tree object element
- * @param {() => void} onClick - The action (callback) to perform upon clicking this branch row element (table row)
- * @param {object} styling - Optional CSS styling for the inner branch row element (table data)
- * @returns {vnode} - virtual node element
- */
-const branchRow = (key, isTreeOpen, branchName, onClick, styling = {}) =>
- h('tr.object-selectable', {
- key: key,
- id: key,
- title: branchName,
- onclick: onClick,
- }, [
- h('td.highlight.flex-row.items-center.g1', {
- style: styling,
- }, [
- isTreeOpen ? iconCaretBottom() : iconCaretRight(),
- branchName,
+ icon(),
+ name,
]),
]);
From b5634c964b6f3165daec013bd3e48f788a926191 Mon Sep 17 00:00:00 2001
From: hehoon <100522372+hehoon@users.noreply.github.com>
Date: Tue, 16 Dec 2025 21:38:04 +0100
Subject: [PATCH 6/9] Add `.25rem` left padding (`--space-xs`) to each tree row
element
---
QualityControl/public/object/objectTreePage.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js
index e87d57a6c..e754aa12f 100644
--- a/QualityControl/public/object/objectTreePage.js
+++ b/QualityControl/public/object/objectTreePage.js
@@ -211,7 +211,7 @@ function treeRow(model, tree, level = 0) {
iconBarChart,
className,
{
- paddingLeft: `${level}em`,
+ paddingLeft: `${level + 0.25}em`,
},
);
rows.push(leaf);
@@ -225,7 +225,7 @@ function treeRow(model, tree, level = 0) {
open ? iconCaretBottom : iconCaretRight,
'',
{
- paddingLeft: `${level}em`,
+ paddingLeft: `${level + 0.25}em`,
},
);
rows.push(branch);
From ae946727beec78453f5c0b6e0669bb9acf7a4669 Mon Sep 17 00:00:00 2001
From: hehoon <100522372+hehoon@users.noreply.github.com>
Date: Wed, 17 Dec 2025 15:39:29 +0100
Subject: [PATCH 7/9] Refactor of treeRowElement params
---
QualityControl/public/object/objectTreePage.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js
index e754aa12f..c6fd69371 100644
--- a/QualityControl/public/object/objectTreePage.js
+++ b/QualityControl/public/object/objectTreePage.js
@@ -238,25 +238,25 @@ function treeRow(model, tree, level = 0) {
* 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} name - The name of this tree object element
+ * @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} styling - Optional CSS styling for the inner branch row element (table data)
+ * @param {object} style - Optional CSS styling for the inner branch row element (table data)
* @returns {vnode} - virtual node element
*/
-const treeRowElement = (key, name, onclick, icon, className = '', styling = {}) =>
+const treeRowElement = (key, title, onclick, icon, className = '', style = {}) =>
h('tr.object-selectable', {
key,
id: key,
- title: name,
+ title,
onclick,
class: className,
}, [
h('td.highlight.flex-row.items-center.g1', {
- style: styling,
+ style,
}, [
icon(),
- name,
+ title,
]),
]);
From c4bed33a5474a3a2b241e17b4409d4c69de0d5b0 Mon Sep 17 00:00:00 2001
From: hehoon <100522372+hehoon@users.noreply.github.com>
Date: Wed, 17 Dec 2025 15:40:32 +0100
Subject: [PATCH 8/9] Change default padding from `0.25em` to `0.3em`
---
QualityControl/public/object/objectTreePage.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js
index c6fd69371..178e1dd6a 100644
--- a/QualityControl/public/object/objectTreePage.js
+++ b/QualityControl/public/object/objectTreePage.js
@@ -211,7 +211,7 @@ function treeRow(model, tree, level = 0) {
iconBarChart,
className,
{
- paddingLeft: `${level + 0.25}em`,
+ paddingLeft: `${level + 0.3}em`,
},
);
rows.push(leaf);
@@ -225,7 +225,7 @@ function treeRow(model, tree, level = 0) {
open ? iconCaretBottom : iconCaretRight,
'',
{
- paddingLeft: `${level + 0.25}em`,
+ paddingLeft: `${level + 0.3}em`,
},
);
rows.push(branch);
From f72ec637999346956d66361d37a6ded6c561b60f Mon Sep 17 00:00:00 2001
From: hehoon <100522372+hehoon@users.noreply.github.com>
Date: Wed, 17 Dec 2025 15:44:22 +0100
Subject: [PATCH 9/9] Use the short version `{ style }`
---
QualityControl/public/object/objectTreePage.js | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js
index 178e1dd6a..e3596cfe5 100644
--- a/QualityControl/public/object/objectTreePage.js
+++ b/QualityControl/public/object/objectTreePage.js
@@ -253,9 +253,7 @@ const treeRowElement = (key, title, onclick, icon, className = '', style = {}) =
onclick,
class: className,
}, [
- h('td.highlight.flex-row.items-center.g1', {
- style,
- }, [
+ h('td.highlight.flex-row.items-center.g1', { style }, [
icon(),
title,
]),