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