preface

Hi, everyone. Today I’m going to show you how to inject the React component into the Topology.

The effect is as follows:

Encapsulate a third-party UI library in a canvas

The default Topology support is: rectangle, Circle, triangle, and so on. For non-built-in nodes, you need to register your custom node to the canvas by calling the registerNode method.

To give you an intuition, let’s analyze the source code involved in @Topology/Core.

// registerNode: Register a custom node.
// name - The name of node.
// drawFn - How to draw.
// anchorsFn - How to get the anchors.
// iconRectFn - How to get the icon rect.
// textRectFn - How to get the text rect.
// force - Overwirte the node if exists.
 export function registerNode(
   name: string,
   drawFn: (ctx: CanvasRenderingContext2D, node: Node) => void, anchorsFn? : (node: Node) =>void, iconRectFn? : (node: Node) =>void, textRectFn? : (node: Node) =>void, protect? : boolean) {
   // Exist
   if (drawNodeFns[name] && protect) {
     return false;
   }

   drawNodeFns[name] = drawFn;
   anchorsFns[name] = anchorsFn;
   iconRectFns[name] = iconRectFn;
   textRectFns[name] = textRectFn;

   return true;
 }
Copy the code

We can find from the source code that the most important input parameters of registerNode function are name and drawFn, and we can easily know from the comments that name is the name of the node and drawFn represents how to draw the corresponding node.

So the idea is pretty clear, just rewrite drawFn.

Add drawFn function

import React from 'react';
import ReactDOM from 'react-dom';
import { s8, createDiv, rectangle } from '@topology/core';
// Store the native DOM node
const reactNodesData = {};

const reactNodes = (ReactComponent) = > (ctx, node) = > {
 // Draw a base map, similar to a placeholder.
 rectangle(ctx, node);

 // If the component is unknown, return directly
 if(! ReactComponent) {return;
 }

 // You need to set a unique ID for the painting engine to recognize
 if(! node.elementId) { node.elementId = s8(); }// elementLoaded is used to determine whether the third-party graphics library is being loaded for the first time and needs to be initialized
 // This is an auxiliary variable that can be assigned by the user or not
 if(! node.elementLoaded) {// Create a div container
   reactNodesData[node.id] = {
     div: createDiv(node)
   };
   node.elementLoaded = true;
   document.body.appendChild(reactNodesData[node.id].div);
   
   // Add the current node to the div layer, otherwise it cannot be displayed
   node.addToDiv();

   // Initialize the react component
   if(node && node.data && node.data.props) {
     reactNodesData[node.id].component = ReactDOM.render(
       <ReactComponent {. node.data.props} / >,
       reactNodesData[node.id].div
     );
   }

   node.elementRendered = false;
 }

 // The node elementRendered is used to determine whether a third-party graphics library needs to be redrawn
 // The paint engine sets this property to false when it needs to redraw nodes
 if(! node.elementRendered) {// When initializing, wait for the parent div to finish rendering first to avoid making the initial chart control too large.
   setTimeout(() = > {
     // Complete the redrawing to avoid unnecessary redrawing
     node.elementRendered = true; }); }};export default reactNodes;
Copy the code

Registers a custom node

  import { registerNode } from '@topology/core';
  import { Modal, Tabs, Button, DatePicker, Result, Table } from 'antd';

  registerNode('button', reactNodes(Button), null.null.null);
  registerNode('datePicker', reactNodes(DatePicker), null.null.null);
  registerNode('result', reactNodes(Result), null.null.null);
  registerNode('table', reactNodes(Table), null.null.null);
Copy the code

Import the ANTD component library and register buttons, time pickers, result pages, and tables into the canvas.

Defining configuration items

Since we have already registered the custom nodes to the canvas, we can create the corresponding nodes by writing the corresponding configuration items and dragging them directly.

The props of the component can be stored in the custom data data.

{
    group: 'the react components'.children: [{text: 'button'.icon: 'icon-rectangle'.name: 'button'.data: {
          autoRect: true.strokeStyle: '#fff'.rect: {
            x: 100.y: 200.width: 100.height: 200
          },
          name: 'button'.data: {
            props: {
              type: 'primary'.children: 'query'}}}}, {text: 'Date component'.icon: 'icon-diamond'.name: 'datePicker'.data: {
          strokeStyle: '#fff'.rect: {
            x: 100.y: 200.width: 300.height: 200
          },
          name: 'datePicker'.data: {
            props: {}}}}, {text: 'Result page'.icon: 'icon-pentagon'.name: 'result'.data: {
          strokeStyle: '#fff'.rect: {
            x: 100.y: 200.width: 200.height: 200
          },
          name: 'result'.data: {
            props: {
              status: '403'.title: '403'.subTitle: 'Sorry, you are not authorized to access this page.'}}}}, {text: 'form'.icon: 'icon-triangle'.name: 'table'.data: {
          strokeStyle: '#fff'.rect: {
            x: 100.y: 200.width: 600.height: 400
          },
          name: 'table'.data: {
            props: {
              columns: columns,
              dataSource: data
            }
          }
        }
      }
    ]
  },
Copy the code

Problems that need to be solved

We are only able to display THE UI components on the canvas, but in the actual scene there needs to be events to interact with the components and so on. I am still working on this. If you have good ideas, please leave them in the comments section