mirror of
https://github.com/penpot/penpot.git
synced 2026-08-24 13:48:51 +00:00
✨ Legend entries toggle node-table visibility
Clicking a legend entry hides/shows that table across the view (struck-through while hidden, kept listed for re-enabling; pure client-side id filter through filteredGraphData, edges drop with their endpoints, ghosts respect it). Also: setting fold >= depth above 0 now switches foldable containers on — a positive depth was silently inert without combos. Signed-off-by: Álvaro Tejero Cantero <alvorithm@teje.ro>
This commit is contained in:
parent
7df4de2d10
commit
ef189acf71
@ -450,6 +450,7 @@ Graph Console
|
|||||||
Object.keys(diffMarks.nodes).forEach(function (id) {
|
Object.keys(diffMarks.nodes).forEach(function (id) {
|
||||||
const m = liveMark(diffMarks.nodes, id);
|
const m = liveMark(diffMarks.nodes, id);
|
||||||
if (m && m.kind === "removed" && m.node && !present[id]
|
if (m && m.kind === "removed" && m.node && !present[id]
|
||||||
|
&& !hiddenTables.has(m.node.table)
|
||||||
&& (!graphFilterIds || graphFilterIds.has(id))) {
|
&& (!graphFilterIds || graphFilterIds.has(id))) {
|
||||||
nodes.push(m.node);
|
nodes.push(m.node);
|
||||||
present[id] = true;
|
present[id] = true;
|
||||||
@ -616,6 +617,8 @@ Graph Console
|
|||||||
let lastGraphSig = null;
|
let lastGraphSig = null;
|
||||||
let renderForced = false;
|
let renderForced = false;
|
||||||
let graphFilterIds = null;
|
let graphFilterIds = null;
|
||||||
|
// Node tables hidden via legend clicks (session-local, not persisted).
|
||||||
|
const hiddenTables = new Set();
|
||||||
|
|
||||||
function currentLayoutName() {
|
function currentLayoutName() {
|
||||||
const stored = localStorage.getItem("graph-layout");
|
const stored = localStorage.getItem("graph-layout");
|
||||||
@ -961,15 +964,27 @@ Graph Console
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Legend entries are clickable: toggling one hides/shows that table's
|
||||||
|
// nodes (struck-through while hidden; hidden tables stay listed so they
|
||||||
|
// can be re-enabled).
|
||||||
function renderGraphLegend(data) {
|
function renderGraphLegend(data) {
|
||||||
const el = document.getElementById("graph-legend");
|
const el = document.getElementById("graph-legend");
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
const tables = {};
|
const tables = {};
|
||||||
(data.nodes || []).forEach(function (n) { tables[n.table] = true; });
|
(data.nodes || []).forEach(function (n) { tables[n.table] = true; });
|
||||||
|
if (lastGraphData) {
|
||||||
|
lastGraphData.nodes.forEach(function (n) {
|
||||||
|
if (hiddenTables.has(n.table)) tables[n.table] = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
const rels = {};
|
const rels = {};
|
||||||
(data.edges || []).forEach(function (e) { rels[e.rel || "IsChildOf"] = true; });
|
(data.edges || []).forEach(function (e) { rels[e.rel || "IsChildOf"] = true; });
|
||||||
function nodeItem(table, s) {
|
function nodeItem(table, s) {
|
||||||
return '<span style="margin-right: 1em; white-space: nowrap;">'
|
const off = hiddenTables.has(table);
|
||||||
|
return '<span data-table="' + escapeHtml(table) + '"'
|
||||||
|
+ ' title="click to ' + (off ? "show" : "hide") + " " + escapeHtml(table) + ' nodes"'
|
||||||
|
+ ' style="margin-right: 1em; white-space: nowrap; cursor: pointer;'
|
||||||
|
+ (off ? " opacity: 0.35; text-decoration: line-through;" : "") + '">'
|
||||||
+ '<span style="color: ' + s.color + '; font-size: 14px;">'
|
+ '<span style="color: ' + s.color + '; font-size: 14px;">'
|
||||||
+ (s.hollow ? "⬡" : (GLYPH_CHARS[s.glyph] || "●")) + '</span> '
|
+ (s.hollow ? "⬡" : (GLYPH_CHARS[s.glyph] || "●")) + '</span> '
|
||||||
+ escapeHtml(table) + "</span>";
|
+ escapeHtml(table) + "</span>";
|
||||||
@ -1011,11 +1026,13 @@ Graph Console
|
|||||||
// by node ids found in the last Cypher result. No reconstruction from the
|
// by node ids found in the last Cypher result. No reconstruction from the
|
||||||
// query result is needed — ids are enough to slice the cached export.
|
// query result is needed — ids are enough to slice the cached export.
|
||||||
function filteredGraphData() {
|
function filteredGraphData() {
|
||||||
if (!graphFilterIds) return lastGraphData;
|
if (!graphFilterIds && !hiddenTables.size) return lastGraphData;
|
||||||
const keep = {};
|
const keep = {};
|
||||||
const nodes = lastGraphData.nodes.filter(function (n) {
|
const nodes = lastGraphData.nodes.filter(function (n) {
|
||||||
if (graphFilterIds.has(n.id)) { keep[n.id] = true; return true; }
|
if (hiddenTables.has(n.table)) return false;
|
||||||
return false;
|
if (graphFilterIds && !graphFilterIds.has(n.id)) return false;
|
||||||
|
keep[n.id] = true;
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
const edges = lastGraphData.edges.filter(function (e) {
|
const edges = lastGraphData.edges.filter(function (e) {
|
||||||
return keep[e.source] && keep[e.target];
|
return keep[e.source] && keep[e.target];
|
||||||
@ -1573,6 +1590,17 @@ Graph Console
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const legendEl = document.getElementById("graph-legend");
|
||||||
|
if (legendEl) {
|
||||||
|
legendEl.addEventListener("click", function (ev) {
|
||||||
|
const item = ev.target.closest("[data-table]");
|
||||||
|
if (!item) return;
|
||||||
|
const t = item.getAttribute("data-table");
|
||||||
|
if (hiddenTables.has(t)) hiddenTables.delete(t); else hiddenTables.add(t);
|
||||||
|
renderCurrent();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const diffFoldToggle = document.getElementById("graph-diff-fold");
|
const diffFoldToggle = document.getElementById("graph-diff-fold");
|
||||||
if (diffFoldToggle) {
|
if (diffFoldToggle) {
|
||||||
diffFoldToggle.checked = diffFoldEnabled();
|
diffFoldToggle.checked = diffFoldEnabled();
|
||||||
@ -1588,6 +1616,11 @@ Graph Console
|
|||||||
depthFoldInput.value = v == null ? "" : String(v);
|
depthFoldInput.value = v == null ? "" : String(v);
|
||||||
depthFoldInput.addEventListener("change", function () {
|
depthFoldInput.addEventListener("change", function () {
|
||||||
localStorage.setItem("graph-depth-fold", depthFoldInput.value);
|
localStorage.setItem("graph-depth-fold", depthFoldInput.value);
|
||||||
|
// A positive depth is meaningless without combos — switch them on.
|
||||||
|
if (parseInt(depthFoldInput.value, 10) > 0 && foldToggle && !foldToggle.checked) {
|
||||||
|
foldToggle.checked = true;
|
||||||
|
localStorage.setItem("graph-fold-containers", "1");
|
||||||
|
}
|
||||||
renderCurrent();
|
renderCurrent();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user