Skip to content

Commit 1a8ef53

Browse files
fix: address PR #56 review — extract shared lineage utils, CSS variable, exec_order
- Extract getRelatedNodeIds and applyScopedStyles into shared lineage-utils.js - Remove duplicate functions from lineage-tab.jsx and no-code-model.jsx - Move hardcoded #1677ff to --lineage-selected-border CSS variable (light: #1677ff, dark: #69b1ff) - Add comment clarifying exec_order fallback uses backend's topological order
1 parent 835752d commit 1a8ef53

5 files changed

Lines changed: 107 additions & 147 deletions

File tree

frontend/src/ide/editor/lineage-tab/lineage-tab.jsx

Lines changed: 1 addition & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { THEME } from "../../../common/constants.js";
4040
import { SpinnerLoader } from "../../../widgets/spinner_loader/index.js";
4141
import { useNotificationService } from "../../../service/notification-service.js";
4242
import { Tech } from "../../../base/icons/index.js";
43+
import { applyScopedStyles } from "../lineage-utils.js";
4344

4445
import "reactflow/dist/style.css";
4546
import "./lineage-tab.css";
@@ -289,80 +290,6 @@ const transformLineageData = (data) => {
289290
return data;
290291
};
291292

292-
// Find all ancestor and descendant node IDs for a given model
293-
const getRelatedNodeIds = (allEdges, selectedLabel, allNodes) => {
294-
const nodeByLabel = {};
295-
allNodes.forEach((n) => {
296-
nodeByLabel[n.data.originalLabel || n.data.label] = n.id;
297-
});
298-
const selectedId = nodeByLabel[selectedLabel];
299-
if (!selectedId) return null;
300-
301-
const related = new Set([selectedId]);
302-
const findAncestors = (id) => {
303-
allEdges.forEach((e) => {
304-
if (e.target === id && !related.has(e.source)) {
305-
related.add(e.source);
306-
findAncestors(e.source);
307-
}
308-
});
309-
};
310-
const findDescendants = (id) => {
311-
allEdges.forEach((e) => {
312-
if (e.source === id && !related.has(e.target)) {
313-
related.add(e.target);
314-
findDescendants(e.target);
315-
}
316-
});
317-
};
318-
findAncestors(selectedId);
319-
findDescendants(selectedId);
320-
return related;
321-
};
322-
323-
const applyScopedStyles = (layoutedNodes, layoutedEdges, selectedLabel) => {
324-
const rawEdges = layoutedEdges.map((e) => ({
325-
source: e.source,
326-
target: e.target,
327-
}));
328-
const related = getRelatedNodeIds(rawEdges, selectedLabel, layoutedNodes);
329-
if (!related) return { nodes: layoutedNodes, edges: layoutedEdges };
330-
331-
const styledNodes = layoutedNodes.map((node) => {
332-
const nodeLabel = node.data.originalLabel || node.data.label;
333-
const isSelected = nodeLabel === selectedLabel;
334-
const isRelated = related.has(node.id);
335-
return {
336-
...node,
337-
style: {
338-
...node.style,
339-
opacity: isRelated ? 1 : 0.25,
340-
border: isSelected
341-
? "2px dashed #1677ff"
342-
: node.style?.border || "1px solid var(--black)",
343-
},
344-
};
345-
});
346-
347-
const relatedEdgeSet = new Set();
348-
layoutedEdges.forEach((e) => {
349-
if (related.has(e.source) && related.has(e.target)) {
350-
relatedEdgeSet.add(e.id);
351-
}
352-
});
353-
354-
const styledEdges = layoutedEdges.map((edge) => ({
355-
...edge,
356-
style: {
357-
...edge.style,
358-
opacity: relatedEdgeSet.has(edge.id) ? 1 : 0.15,
359-
stroke: relatedEdgeSet.has(edge.id) ? "#1677ff" : undefined,
360-
},
361-
}));
362-
363-
return { nodes: styledNodes, edges: styledEdges };
364-
};
365-
366293
function LineageTab({ nodeData, selectedModelName }) {
367294
const axios = useAxiosPrivate();
368295
const { selectedOrgId } = orgStore();
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* Shared utility functions for lineage scoping.
3+
* Used by both lineage-tab.jsx (standalone) and no-code-model.jsx (bottom section).
4+
*/
5+
6+
/**
7+
* Find all ancestor and descendant node IDs for a given model.
8+
* @param {Array} allEdges - Array of { source, target } edge objects
9+
* @param {string} selectedLabel - The label of the selected model
10+
* @param {Array} allNodes - Array of node objects with data.originalLabel or data.label
11+
* @return {Set|null} Set of related node IDs, or null if selected model not found
12+
*/
13+
export const getRelatedNodeIds = (allEdges, selectedLabel, allNodes) => {
14+
const nodeByLabel = {};
15+
allNodes.forEach((n) => {
16+
nodeByLabel[n.data.originalLabel || n.data.label] = n.id;
17+
});
18+
const selectedId = nodeByLabel[selectedLabel];
19+
if (!selectedId) return null;
20+
21+
const related = new Set([selectedId]);
22+
const findAncestors = (id) => {
23+
allEdges.forEach((e) => {
24+
if (e.target === id && !related.has(e.source)) {
25+
related.add(e.source);
26+
findAncestors(e.source);
27+
}
28+
});
29+
};
30+
const findDescendants = (id) => {
31+
allEdges.forEach((e) => {
32+
if (e.source === id && !related.has(e.target)) {
33+
related.add(e.target);
34+
findDescendants(e.target);
35+
}
36+
});
37+
};
38+
findAncestors(selectedId);
39+
findDescendants(selectedId);
40+
return related;
41+
};
42+
43+
/**
44+
* Apply scoped styles to nodes and edges based on the selected model's lineage chain.
45+
* Related nodes stay full opacity, unrelated nodes are faded.
46+
* @param {Array} layoutedNodes - Array of positioned node objects
47+
* @param {Array} layoutedEdges - Array of edge objects
48+
* @param {string} selectedLabel - The label of the selected model
49+
* @return {Object} { nodes, edges } with scoped styles applied
50+
*/
51+
export const applyScopedStyles = (
52+
layoutedNodes,
53+
layoutedEdges,
54+
selectedLabel
55+
) => {
56+
const rawEdges = layoutedEdges.map((e) => ({
57+
source: e.source,
58+
target: e.target,
59+
}));
60+
const related = getRelatedNodeIds(rawEdges, selectedLabel, layoutedNodes);
61+
if (!related) return { nodes: layoutedNodes, edges: layoutedEdges };
62+
63+
const styledNodes = layoutedNodes.map((node) => {
64+
const nodeLabel = node.data.originalLabel || node.data.label;
65+
const isSelected = nodeLabel === selectedLabel;
66+
const isRelated = related.has(node.id);
67+
return {
68+
...node,
69+
style: {
70+
...node.style,
71+
opacity: isRelated ? 1 : 0.25,
72+
border: isSelected
73+
? "2px dashed var(--lineage-selected-border)"
74+
: node.style?.border || "1px solid var(--black)",
75+
},
76+
};
77+
});
78+
79+
const relatedEdgeSet = new Set();
80+
layoutedEdges.forEach((e) => {
81+
if (related.has(e.source) && related.has(e.target)) {
82+
relatedEdgeSet.add(e.id);
83+
}
84+
});
85+
86+
const styledEdges = layoutedEdges.map((edge) => ({
87+
...edge,
88+
style: {
89+
...edge.style,
90+
opacity: relatedEdgeSet.has(edge.id) ? 1 : 0.15,
91+
stroke: relatedEdgeSet.has(edge.id)
92+
? "var(--lineage-selected-border)"
93+
: undefined,
94+
},
95+
}));
96+
97+
return { nodes: styledNodes, edges: styledEdges };
98+
};

frontend/src/ide/editor/no-code-model/no-code-model.jsx

Lines changed: 1 addition & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import dagre from "dagre";
6969
import { useAxiosPrivate } from "../../../service/axios-service.js";
7070
import { NoCodeToolbar } from "../no-code-toolbar/no-code-toolbar.jsx";
7171
import { NoCodeTopbar } from "../no-code-topbar/no-code-topbar.jsx";
72+
import { applyScopedStyles } from "../lineage-utils.js";
7273
import { ConfigureSourceDestination } from "../no-code-configuration/configure-source-destination.jsx";
7374
import { ConfigureJoins } from "../no-code-configuration/configure-joins.jsx";
7475
import { useProjectStore } from "../../../store/project-store.js";
@@ -2171,79 +2172,6 @@ function NoCodeModel({ nodeData }) {
21712172
};
21722173

21732174
// Find all ancestor and descendant node IDs for a given model
2174-
const getRelatedNodeIds = (allEdges, selectedLabel, allNodes) => {
2175-
const nodeByLabel = {};
2176-
allNodes.forEach((n) => {
2177-
nodeByLabel[n.data.originalLabel || n.data.label] = n.id;
2178-
});
2179-
const selectedId = nodeByLabel[selectedLabel];
2180-
if (!selectedId) return null;
2181-
2182-
const related = new Set([selectedId]);
2183-
const findAncestors = (id) => {
2184-
allEdges.forEach((e) => {
2185-
if (e.target === id && !related.has(e.source)) {
2186-
related.add(e.source);
2187-
findAncestors(e.source);
2188-
}
2189-
});
2190-
};
2191-
const findDescendants = (id) => {
2192-
allEdges.forEach((e) => {
2193-
if (e.source === id && !related.has(e.target)) {
2194-
related.add(e.target);
2195-
findDescendants(e.target);
2196-
}
2197-
});
2198-
};
2199-
findAncestors(selectedId);
2200-
findDescendants(selectedId);
2201-
return related;
2202-
};
2203-
2204-
const applyScopedStyles = (layoutedNodes, layoutedEdges, selectedLabel) => {
2205-
const rawEdges = layoutedEdges.map((e) => ({
2206-
source: e.source,
2207-
target: e.target,
2208-
}));
2209-
const related = getRelatedNodeIds(rawEdges, selectedLabel, layoutedNodes);
2210-
if (!related) return { nodes: layoutedNodes, edges: layoutedEdges };
2211-
2212-
const styledNodes = layoutedNodes.map((node) => {
2213-
const nodeLabel = node.data.originalLabel || node.data.label;
2214-
const isSelected = nodeLabel === selectedLabel;
2215-
const isRelated = related.has(node.id);
2216-
return {
2217-
...node,
2218-
style: {
2219-
...node.style,
2220-
opacity: isRelated ? 1 : 0.25,
2221-
border: isSelected
2222-
? "2px dashed #1677ff"
2223-
: node.style?.border || "1px solid var(--black)",
2224-
},
2225-
};
2226-
});
2227-
2228-
const relatedEdgeSet = new Set();
2229-
layoutedEdges.forEach((e) => {
2230-
if (related.has(e.source) && related.has(e.target)) {
2231-
relatedEdgeSet.add(e.id);
2232-
}
2233-
});
2234-
2235-
const styledEdges = layoutedEdges.map((edge) => ({
2236-
...edge,
2237-
style: {
2238-
...edge.style,
2239-
opacity: relatedEdgeSet.has(edge.id) ? 1 : 0.15,
2240-
stroke: relatedEdgeSet.has(edge.id) ? "#1677ff" : undefined,
2241-
},
2242-
}));
2243-
2244-
return { nodes: styledNodes, edges: styledEdges };
2245-
};
2246-
22472175
const getLineageData = (callSample = false) => {
22482176
if (!projectId) return;
22492177
setLineageData();

frontend/src/ide/explorer/explorer-component.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ const IdeExplorer = ({
244244
});
245245
return result;
246246
}
247+
// "exec_order" and default: keep original backend order (topological/execution)
247248
return [...models];
248249
};
249250

frontend/src/variables.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@
8181
--node-color-blue: #b0e3f9;
8282
--node-color-yellow: #ffdd8a;
8383
--node-color-pink: #ffc8d2;
84+
85+
/* Lineage scoping */
86+
--lineage-selected-border: #1677ff;
8487
}
8588

8689
.dark {
@@ -156,4 +159,7 @@
156159
--node-color-blue: #1a5a6e;
157160
--node-color-yellow: #6e5a1a;
158161
--node-color-pink: #6e3a4a;
162+
163+
/* Lineage scoping */
164+
--lineage-selected-border: #69b1ff;
159165
}

0 commit comments

Comments
 (0)