main
js 407 lines 10.4 KB
Raw
1 import React from 'react';
2 import {Motion, spring} from 'react-motion';
3 import dagre from 'dagre';
4 // import prettyFormat from 'pretty-format';
5 // import reactElement from 'pretty-format/plugins/ReactElement';
6
7 function getFiberColor(fibers, id) {
8 if (fibers.currentIDs.indexOf(id) > -1) {
9 return 'lightgreen';
10 }
11 if (id === fibers.workInProgressID) {
12 return 'yellow';
13 }
14 return 'lightyellow';
15 }
16
17 function Graph(props) {
18 const {rankdir, trackActive} = props.settings;
19 var g = new dagre.graphlib.Graph();
20 g.setGraph({
21 width: 1000,
22 height: 1000,
23 nodesep: 50,
24 edgesep: 150,
25 ranksep: 100,
26 marginx: 100,
27 marginy: 100,
28 rankdir,
29 });
30
31 var edgeLabels = {};
32 React.Children.forEach(props.children, function (child) {
33 if (!child) {
34 return;
35 }
36 if (child.type.isVertex) {
37 g.setNode(child.key, {
38 label: child,
39 width: child.props.width,
40 height: child.props.height,
41 });
42 } else if (child.type.isEdge) {
43 const relationshipKey = child.props.source + ':' + child.props.target;
44 if (!edgeLabels[relationshipKey]) {
45 edgeLabels[relationshipKey] = [];
46 }
47 edgeLabels[relationshipKey].push(child);
48 }
49 });
50
51 Object.keys(edgeLabels).forEach(key => {
52 const children = edgeLabels[key];
53 const child = children[0];
54 g.setEdge(child.props.source, child.props.target, {
55 label: child,
56 allChildren: children.map(c => c.props.children),
57 weight: child.props.weight,
58 });
59 });
60
61 dagre.layout(g);
62
63 var activeNode = g
64 .nodes()
65 .map(v => g.node(v))
66 .find(node => node.label.props.isActive);
67 const [winX, winY] = [window.innerWidth / 2, window.innerHeight / 2];
68 var focusDx = trackActive && activeNode ? winX - activeNode.x : 0;
69 var focusDy = trackActive && activeNode ? winY - activeNode.y : 0;
70
71 var nodes = g.nodes().map(v => {
72 var node = g.node(v);
73 return (
74 <Motion
75 style={{
76 x: props.isDragging ? node.x + focusDx : spring(node.x + focusDx),
77 y: props.isDragging ? node.y + focusDy : spring(node.y + focusDy),
78 }}
79 key={node.label.key}>
80 {interpolatingStyle =>
81 React.cloneElement(node.label, {
82 x: interpolatingStyle.x + props.dx,
83 y: interpolatingStyle.y + props.dy,
84 vanillaX: node.x,
85 vanillaY: node.y,
86 })
87 }
88 </Motion>
89 );
90 });
91
92 var edges = g.edges().map(e => {
93 var edge = g.edge(e);
94 let idx = 0;
95 return (
96 <Motion
97 style={edge.points.reduce((bag, point) => {
98 bag[idx + ':x'] = props.isDragging
99 ? point.x + focusDx
100 : spring(point.x + focusDx);
101 bag[idx + ':y'] = props.isDragging
102 ? point.y + focusDy
103 : spring(point.y + focusDy);
104 idx++;
105 return bag;
106 }, {})}
107 key={edge.label.key}>
108 {interpolatedStyle => {
109 let points = [];
110 Object.keys(interpolatedStyle).forEach(key => {
111 const [idx, prop] = key.split(':');
112 if (!points[idx]) {
113 points[idx] = {x: props.dx, y: props.dy};
114 }
115 points[idx][prop] += interpolatedStyle[key];
116 });
117 return React.cloneElement(edge.label, {
118 points,
119 id: edge.label.key,
120 children: edge.allChildren.join(', '),
121 });
122 }}
123 </Motion>
124 );
125 });
126
127 return (
128 <div
129 style={{
130 position: 'relative',
131 height: '100%',
132 }}>
133 {edges}
134 {nodes}
135 </div>
136 );
137 }
138
139 function Vertex(props) {
140 if (Number.isNaN(props.x) || Number.isNaN(props.y)) {
141 return null;
142 }
143
144 return (
145 <div
146 style={{
147 position: 'absolute',
148 border: '1px solid black',
149 left: props.x - props.width / 2,
150 top: props.y - props.height / 2,
151 width: props.width,
152 height: props.height,
153 overflow: 'hidden',
154 padding: '4px',
155 wordWrap: 'break-word',
156 }}>
157 {props.children}
158 </div>
159 );
160 }
161 Vertex.isVertex = true;
162
163 const strokes = {
164 alt: 'blue',
165 child: 'green',
166 sibling: 'darkgreen',
167 return: 'red',
168 fx: 'purple',
169 };
170
171 function Edge(props) {
172 var points = props.points;
173 var path = 'M' + points[0].x + ' ' + points[0].y + ' ';
174
175 if (!points[0].x || !points[0].y) {
176 return null;
177 }
178
179 for (var i = 1; i < points.length; i++) {
180 path += 'L' + points[i].x + ' ' + points[i].y + ' ';
181 if (!points[i].x || !points[i].y) {
182 return null;
183 }
184 }
185
186 var lineID = props.id;
187
188 return (
189 <svg
190 width="100%"
191 height="100%"
192 style={{
193 position: 'absolute',
194 left: 0,
195 right: 0,
196 top: 0,
197 bottom: 0,
198 }}>
199 <defs>
200 <path d={path} id={lineID} />
201 <marker
202 id="markerCircle"
203 markerWidth="8"
204 markerHeight="8"
205 refX="5"
206 refY="5">
207 <circle cx="5" cy="5" r="3" style={{stroke: 'none', fill: 'black'}} />
208 </marker>
209 <marker
210 id="markerArrow"
211 markerWidth="13"
212 markerHeight="13"
213 refX="2"
214 refY="6"
215 orient="auto">
216 <path d="M2,2 L2,11 L10,6 L2,2" style={{fill: 'black'}} />
217 </marker>
218 </defs>
219
220 <use
221 xlinkHref={`#${lineID}`}
222 fill="none"
223 stroke={strokes[props.kind]}
224 style={{
225 markerStart: 'url(#markerCircle)',
226 markerEnd: 'url(#markerArrow)',
227 }}
228 />
229 <text>
230 <textPath xlinkHref={`#${lineID}`}>
231 {'     '}
232 {props.children}
233 </textPath>
234 </text>
235 </svg>
236 );
237 }
238 Edge.isEdge = true;
239
240 function formatPriority(priority) {
241 switch (priority) {
242 case 1:
243 return 'synchronous';
244 case 2:
245 return 'task';
246 case 3:
247 return 'hi-pri work';
248 case 4:
249 return 'lo-pri work';
250 case 5:
251 return 'offscreen work';
252 default:
253 throw new Error('Unknown priority.');
254 }
255 }
256
257 export default function Fibers({fibers, show, graphSettings, ...rest}) {
258 const items = Object.keys(fibers.descriptions).map(
259 id => fibers.descriptions[id]
260 );
261
262 const isDragging = rest.className.indexOf('dragging') > -1;
263 const [_, sdx, sdy] =
264 rest.style.transform.match(/translate\((-?\d+)px,(-?\d+)px\)/) || [];
265 const dx = Number(sdx);
266 const dy = Number(sdy);
267
268 return (
269 <div
270 {...rest}
271 style={{
272 width: '100%',
273 height: '100%',
274 position: 'absolute',
275 top: 0,
276 left: 0,
277 ...rest.style,
278 transform: null,
279 }}>
280 <Graph
281 className="graph"
282 dx={dx}
283 dy={dy}
284 isDragging={isDragging}
285 settings={graphSettings}>
286 {items.map(fiber => [
287 <Vertex
288 key={fiber.id}
289 width={150}
290 height={100}
291 isActive={fiber.id === fibers.workInProgressID}>
292 <div
293 style={{
294 width: '100%',
295 height: '100%',
296 backgroundColor: getFiberColor(fibers, fiber.id),
297 }}
298 title={
299 /*prettyFormat(fiber, { plugins: [reactElement ]})*/
300 'todo: this was hanging last time I tried to pretty print'
301 }>
302 <small>
303 {fiber.tag} #{fiber.id}
304 </small>
305 <br />
306 {fiber.type}
307 <br />
308 {fibers.currentIDs.indexOf(fiber.id) === -1 ? (
309 <small>
310 {fiber.pendingWorkPriority !== 0 && [
311 <span key="span">
312 Needs: {formatPriority(fiber.pendingWorkPriority)}
313 </span>,
314 <br key="br" />,
315 ]}
316 {fiber.memoizedProps !== null &&
317 fiber.pendingProps !== null && [
318 fiber.memoizedProps === fiber.pendingProps
319 ? 'Can reuse memoized.'
320 : 'Cannot reuse memoized.',
321 <br key="br" />,
322 ]}
323 </small>
324 ) : (
325 <small>Committed</small>
326 )}
327 {fiber.flags && [
328 <br key="br" />,
329 <small key="small">Effect: {fiber.flags}</small>,
330 ]}
331 </div>
332 </Vertex>,
333 fiber.child && show.child && (
334 <Edge
335 source={fiber.id}
336 target={fiber.child}
337 kind="child"
338 weight={1000}
339 key={`${fiber.id}-${fiber.child}-child`}>
340 child
341 </Edge>
342 ),
343 fiber.sibling && show.sibling && (
344 <Edge
345 source={fiber.id}
346 target={fiber.sibling}
347 kind="sibling"
348 weight={2000}
349 key={`${fiber.id}-${fiber.sibling}-sibling`}>
350 sibling
351 </Edge>
352 ),
353 fiber.return && show.return && (
354 <Edge
355 source={fiber.id}
356 target={fiber.return}
357 kind="return"
358 weight={1000}
359 key={`${fiber.id}-${fiber.return}-return`}>
360 return
361 </Edge>
362 ),
363 fiber.nextEffect && show.fx && (
364 <Edge
365 source={fiber.id}
366 target={fiber.nextEffect}
367 kind="fx"
368 weight={100}
369 key={`${fiber.id}-${fiber.nextEffect}-nextEffect`}>
370 nextFx
371 </Edge>
372 ),
373 fiber.firstEffect && show.fx && (
374 <Edge
375 source={fiber.id}
376 target={fiber.firstEffect}
377 kind="fx"
378 weight={100}
379 key={`${fiber.id}-${fiber.firstEffect}-firstEffect`}>
380 firstFx
381 </Edge>
382 ),
383 fiber.lastEffect && show.fx && (
384 <Edge
385 source={fiber.id}
386 target={fiber.lastEffect}
387 kind="fx"
388 weight={100}
389 key={`${fiber.id}-${fiber.lastEffect}-lastEffect`}>
390 lastFx
391 </Edge>
392 ),
393 fiber.alternate && show.alt && (
394 <Edge
395 source={fiber.id}
396 target={fiber.alternate}
397 kind="alt"
398 weight={10}
399 key={`${fiber.id}-${fiber.alternate}-alt`}>
400 alt
401 </Edge>
402 ),
403 ])}
404 </Graph>
405 </div>
406 );
407 }