@@ -90,31 +90,19 @@ function syncQueryGraphView(el: HTMLElement, graph: Graph, host: ViewModel, targ
90
90
return '' ;
91
91
}
92
92
93
- function walk ( boxEl : HTMLElement | null , node : GraphNode , path : number [ ] , currentPath : number [ ] ) {
93
+ function walk ( layerEl : HTMLElement | null , node : GraphNode , path : number [ ] , currentPath : number [ ] ) {
94
94
if ( ! Array . isArray ( currentPath ) ) {
95
95
currentPath = [ ] ;
96
96
}
97
97
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 , [ ] ) ;
111
101
}
112
102
113
103
const isTarget = currentPath . length === 1 ;
114
104
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 ( ) ) ;
118
106
119
107
updateGraphNodeEl ( nodeEl , path , isTarget ? { ...node , ...targetNode } : node ) ;
120
108
nodeEl . classList . toggle ( 'target' , isTarget ) ;
@@ -134,7 +122,7 @@ function syncQueryGraphView(el: HTMLElement, graph: Graph, host: ViewModel, targ
134
122
if ( Array . isArray ( node . children ) ) {
135
123
for ( let i = 0 ; i < node . children . length ; i ++ ) {
136
124
const childEl = walk (
137
- boxEl . nextSibling as HTMLElement ,
125
+ layerEl . nextSibling as HTMLElement ,
138
126
node . children [ i ] ,
139
127
path . concat ( i ) ,
140
128
currentPath [ 1 ] === i ? currentPath . slice ( 1 ) : [ ]
@@ -147,35 +135,35 @@ function syncQueryGraphView(el: HTMLElement, graph: Graph, host: ViewModel, targ
147
135
return nodeEl ;
148
136
}
149
137
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
+ ) ;
151
143
const connections : [ from : HTMLElement , to : HTMLElement ] [ ] = [ ] ;
152
-
153
- el . querySelector ( ':scope > svg' ) ?. remove ( ) ;
144
+ const svgEl = el . querySelector ( ':scope > svg' ) || el . appendChild ( svg ( 'svg' ) ) ;
154
145
155
146
// create update nodes
156
147
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
+ ) ;
158
154
}
159
155
160
156
// 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 ( ) ;
173
160
}
174
161
}
175
162
176
163
requestAnimationFrame ( ( ) => {
177
- const svgEl = el . appendChild ( svg ( 'svg' ) ) ;
178
164
const gap = parseInt ( getComputedStyle ( el ) . gap ) ;
165
+ const defaultConnectionEls : SVGElement [ ] = [ ] ;
166
+ const currentConnectionEls : SVGElement [ ] = [ ] ;
179
167
180
168
for ( const [ fromEl , toEl ] of connections ) {
181
169
const fromBox = getBoundingRect ( fromEl , svgEl ) ;
@@ -189,28 +177,32 @@ function syncQueryGraphView(el: HTMLElement, graph: Graph, host: ViewModel, targ
189
177
const dx = ( x2 - x1 ) / 3 ;
190
178
191
179
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
+ } ) ;
209
195
210
196
if ( isCurrent ) {
197
+ currentConnectionEls . push ( connectionEl ) ;
211
198
connectionEl . classList . add ( 'current' ) ;
199
+ } else {
200
+ defaultConnectionEls . push ( connectionEl ) ;
212
201
}
202
+
213
203
}
204
+
205
+ svgEl . replaceChildren ( ...defaultConnectionEls , ...currentConnectionEls ) ;
214
206
} ) ;
215
207
}
216
208
@@ -543,7 +535,7 @@ export default function(host: ViewModel, updateHostParams: UpdateHostParams) {
543
535
}
544
536
545
537
function scheduleCompute ( fn : ( ) => void ) {
546
- const id = setTimeout ( fn , 16 ) ;
538
+ const id = setTimeout ( fn , 1 ) ;
547
539
return ( ) => clearTimeout ( id ) ;
548
540
}
549
541
@@ -782,6 +774,12 @@ export default function(host: ViewModel, updateHostParams: UpdateHostParams) {
782
774
delete graphNodeEl . dataset . state ;
783
775
}
784
776
}
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 ) ;
785
783
}
786
784
787
785
for ( let i = computeIndex ; i < currentGraph . current . length ; i ++ ) {
0 commit comments