Skip to content

Commit 6f1bb7a

Browse files
committed
Refactor query graph view rendering logic
1 parent 1641025 commit 6f1bb7a

File tree

1 file changed

+51
-53
lines changed

1 file changed

+51
-53
lines changed

src/pages/discovery/editor-query.ts

Lines changed: 51 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -90,31 +90,19 @@ function syncQueryGraphView(el: HTMLElement, graph: Graph, host: ViewModel, targ
9090
return '';
9191
}
9292

93-
function walk(boxEl: HTMLElement | null, node: GraphNode, path: number[], currentPath: number[]) {
93+
function walk(layerEl: HTMLElement | null, node: GraphNode, path: number[], currentPath: number[]) {
9494
if (!Array.isArray(currentPath)) {
9595
currentPath = [];
9696
}
9797

98-
let nextLayerGraphNodeEl: HTMLElement | null = null;
99-
100-
if (!boxEl) {
101-
boxEl = createGraphLayer();
102-
nextGraphNodeElByLayer.set(boxEl, null);
103-
} else {
104-
let candidate = nextGraphNodeElByLayer.get(boxEl);
105-
106-
if (candidate === undefined) {
107-
nextGraphNodeElByLayer.set(boxEl, candidate = boxEl.firstElementChild as HTMLElement);
108-
}
109-
110-
nextLayerGraphNodeEl = candidate;
98+
if (!layerEl) {
99+
graphLayerEls.push(layerEl = createGraphLayer());
100+
nextGraphNodeElByLayer.set(layerEl, []);
111101
}
112102

113103
const isTarget = currentPath.length === 1;
114104
const isCurrent = currentPath.length > 1;
115-
const nodeEl = nextLayerGraphNodeEl || boxEl.appendChild(createGraphNode());
116-
117-
nextGraphNodeElByLayer.set(boxEl, nodeEl.nextElementSibling as HTMLElement);
105+
const nodeEl = nextGraphNodeElByLayer.get(layerEl)?.shift() || layerEl.appendChild(createGraphNode());
118106

119107
updateGraphNodeEl(nodeEl, path, isTarget ? { ...node, ...targetNode } : node);
120108
nodeEl.classList.toggle('target', isTarget);
@@ -134,7 +122,7 @@ function syncQueryGraphView(el: HTMLElement, graph: Graph, host: ViewModel, targ
134122
if (Array.isArray(node.children)) {
135123
for (let i = 0; i < node.children.length; i++) {
136124
const childEl = walk(
137-
boxEl.nextSibling as HTMLElement,
125+
layerEl.nextSibling as HTMLElement,
138126
node.children[i],
139127
path.concat(i),
140128
currentPath[1] === i ? currentPath.slice(1) : []
@@ -147,35 +135,35 @@ function syncQueryGraphView(el: HTMLElement, graph: Graph, host: ViewModel, targ
147135
return nodeEl;
148136
}
149137

150-
const nextGraphNodeElByLayer = new Map<HTMLElement, HTMLElement | null>();
138+
const graphLayerEls = [...el.querySelectorAll(':scope > .query-graph-layer')] as HTMLElement[];
139+
const nextGraphNodeElByLayer = graphLayerEls.reduce(
140+
(map, layerEl) => map.set(layerEl, [...layerEl.querySelectorAll(':scope > .query-graph-node')] as HTMLElement[]),
141+
new Map<HTMLElement, HTMLElement[]>()
142+
);
151143
const connections: [from: HTMLElement, to: HTMLElement][] = [];
152-
153-
el.querySelector(':scope > svg')?.remove();
144+
const svgEl = el.querySelector(':scope > svg') || el.appendChild(svg('svg'));
154145

155146
// create update nodes
156147
for (let i = 0; i < graph.children.length; i++) {
157-
walk(el.firstChild as HTMLElement, graph.children[i], [i], graph.current[0] === i ? graph.current : []);
148+
walk(
149+
graphLayerEls[0],
150+
graph.children[i],
151+
[i],
152+
graph.current[0] === i ? graph.current : []
153+
);
158154
}
159155

160156
// remove unused nodes
161-
for (const layerEl of el.querySelectorAll(':scope .query-graph-layer')) {
162-
let lastNodeEl = nextGraphNodeElByLayer.get(layerEl as HTMLElement);
163-
164-
if (lastNodeEl === undefined) {
165-
layerEl.remove();
166-
continue;
167-
} else {
168-
while (lastNodeEl !== null) {
169-
const tmpEl = lastNodeEl;
170-
lastNodeEl = lastNodeEl.nextElementSibling as HTMLElement | null;
171-
tmpEl?.remove();
172-
}
157+
for (const nodeEls of nextGraphNodeElByLayer.values()) {
158+
for (const nodeEl of nodeEls) {
159+
nodeEl.remove();
173160
}
174161
}
175162

176163
requestAnimationFrame(() => {
177-
const svgEl = el.appendChild(svg('svg'));
178164
const gap = parseInt(getComputedStyle(el).gap);
165+
const defaultConnectionEls: SVGElement[] = [];
166+
const currentConnectionEls: SVGElement[] = [];
179167

180168
for (const [fromEl, toEl] of connections) {
181169
const fromBox = getBoundingRect(fromEl, svgEl);
@@ -189,28 +177,32 @@ function syncQueryGraphView(el: HTMLElement, graph: Graph, host: ViewModel, targ
189177
const dx = (x2 - x1) / 3;
190178

191179
const isCurrent = toEl.classList.contains('current') || toEl.classList.contains('target');
192-
const connectionEl = svgEl.appendChild(
193-
y1 === y2
194-
? svg('line', {
195-
stroke: '#888',
196-
x1: x0,
197-
y1,
198-
x2,
199-
y2
200-
})
201-
: svg('path', {
202-
stroke: '#888',
203-
fill: 'none',
204-
d: `M ${x0} ${y1} H ${x1} C ${x1 + 2 * dx} ${y1} ${
205-
x2 - 2 * dx
206-
} ${y2} ${x2} ${y2}`
207-
})
208-
);
180+
const connectionEl = y1 === y2
181+
? svg('line', {
182+
stroke: '#888',
183+
x1: x0,
184+
y1,
185+
x2,
186+
y2
187+
})
188+
: svg('path', {
189+
stroke: '#888',
190+
fill: 'none',
191+
d: `M ${x0} ${y1} H ${x1} C ${x1 + 2 * dx} ${y1} ${
192+
x2 - 2 * dx
193+
} ${y2} ${x2} ${y2}`
194+
});
209195

210196
if (isCurrent) {
197+
currentConnectionEls.push(connectionEl);
211198
connectionEl.classList.add('current');
199+
} else {
200+
defaultConnectionEls.push(connectionEl);
212201
}
202+
213203
}
204+
205+
svgEl.replaceChildren(...defaultConnectionEls, ...currentConnectionEls);
214206
});
215207
}
216208

@@ -543,7 +535,7 @@ export default function(host: ViewModel, updateHostParams: UpdateHostParams) {
543535
}
544536

545537
function scheduleCompute(fn: () => void) {
546-
const id = setTimeout(fn, 16);
538+
const id = setTimeout(fn, 1);
547539
return () => clearTimeout(id);
548540
}
549541

@@ -782,6 +774,12 @@ export default function(host: ViewModel, updateHostParams: UpdateHostParams) {
782774
delete graphNodeEl.dataset.state;
783775
}
784776
}
777+
778+
// A trick to reset all transitions on graph nodes, as transitions
779+
// can freeze when the main thread is blocked and display an incorrect state of nodes
780+
const placeholder = document.createComment('');
781+
queryGraphEl.replaceWith(placeholder);
782+
placeholder.replaceWith(queryGraphEl);
785783
}
786784

787785
for (let i = computeIndex; i < currentGraph.current.length; i++) {

0 commit comments

Comments
 (0)