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. }