添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
善良的花卷  ·  Creatrip: ...·  4 月前    · 
高大的椅子  ·  LabVIEW ...·  1 年前    · 

React Integration

JsPlumb has several components to assist you to integrate with React. We recommend React 17 or later, but some users have reported that they have apps running with JsPlumb in React 16. JsPlumb has been tested against React 17, React 18 and NextJS 14.0.3.

Imports

"dependencies":{
"@jsplumbtoolkit/browser-ui-react":"6.71.0"
}

Quick start

The main component you will use is the SurfaceComponent . Let's take a quick look at how you'd use one:


import React from "react"

import {
MiniviewComponent,
SurfaceComponent,
ControlsComponent
}
from '@jsplumbtoolkit/browser-ui-react';

export default function DemoComponent({someProps}) {

const data = {
nodes:[
{ id:"1", label:"1", left:50, top:50},
{ id:"2", label:"TWO", left:250, top:250}
],
edges:[
{ source:"1", target:"2" }
]
}

return <div style={{width:"100%",height:"100%"}}>
<SurfaceComponent data={data}>
<ControlsComponent/>
<MiniviewComponent/>
</SurfaceComponent>
</div>

}

In the above example the only prop we set on the SurfaceComponent was data , meaning the component uses a default Absolute layout and some default JSX for rendering nodes. The SurfaceComponent , though, is highly configurable, mainly through its viewOptions and renderOptions props. Here's the same example from before but with a few more things configured:


import React from "react"

import {
MiniviewComponent,
SurfaceComponent,
ControlsComponent
}
from '@jsplumbtoolkit/browser-ui-react';

export default function DemoComponent({someProps}) {

const data = {
nodes:[
{ id:"1", label:"1", left:50, top:50},
{ id:"2", label:"TWO", left:250, top:250}
],
edges:[
{ source:"1", target:"2" }
]
}

const viewOptions = {
nodes:{
default:{
jsx:(ctx) => <div style={{width:"60px", height:"60px", backgroundColor:"white", border:"1px solid black", text-align:"center"}}><h3>{ctx.data.label}</h3></div>
}
}
}

const renderOptions = {
layout:{
type:"ForceDirected"
},
grid:{
size:{
w:50, h:50
}
}
}

return <div style={{width:"100%",height:"100%"}}>
<SurfaceComponent data={data} renderOptions={renderOptions} viewOptions={viewOptions}>
<ControlsComponent/>
<MiniviewComponent/>
</SurfaceComponent>
</div>

}

Rendering Nodes and Groups

At the core of your UI will be a SurfaceComponent , which provides the canvas onto which nodes, groups and edges are drawn. In the Quickstart section above we show a couple of examples of the SurfaceComponent in action.

Each node or group in your UI is rendered as an individual component. In the first example above, we rely on JsPlumb's default JSX to render each node, but in the second we provide some JSX ourselves, although its quite simple. In a real world app your JSX will likely be more complex. As an example, consider the component we use to render nodes in the Flowchart Builder :

import * as React from 'react';

import { ShapeComponent } from "@jsplumbtoolkit/browser-ui-react"

import anchorPositions from "./anchor-positions"

export default function NodeComponent({ctx, shapeLibrary}) {

const { vertex, toolkit } = ctx;
const data = vertex.data;

return <div style={{color:data.textColor}} className="flowchart-object" data-jtk-target="true">

<ShapeComponent obj={data} shapeLibrary={shapeLibrary} showLabels={true} labelProperty="text"/>

{anchorPositions.map(ap => <div className={"jtk-connect jtk-connect-" + ap.id} data-jtk-anchor-x={ap.x} data-jtk-anchor-y={ap.y} data-jtk-orientation-x={ap.ox} data-jtk-orientation-y={ap.oy} data-jtk-source="true" data-jtk-port-type="source" key={ap.id}></div>)}

<div className="node-delete node-action delete" onClick={() => toolkit.removeNode(vertex)}></div>
</div>
}

The contents of the JSX used to render each node/group are entirely up to you.

Mapping types to JSX

The viewOptions prop is used to map node/group types to JSX, and to certain behaviours. For instance, this is the nodes section of the viewOptions for the flowchart builder starter app:

import { DEFAULT, newInstance } from "@jsplumbtoolkit/browser-ui"
import NodeComponent from "./NodeComponent.jsx"

const shapeLibrary = ...
const toolkit = newInstance(....)

const view = {
nodes: {
[DEFAULT]: {
jsx: (ctx) => {
return <NodeComponent ctx={ctx} shapeLibrary={shapeLibrary}/>
},
// connections to/from this node can exist at any of the given anchorPositions
anchorPositions,
// node can support any number of connections.
maxConnections: -1,
events: {
[EVENT_TAP]: (params) => {
surface.current.stopEditingPath()
// if zero nodes currently selected, or the shift key wasnt pressed, make this node the only one in the selection.
if (toolkit.getSelection()._nodes.length < 1 || params.e.shiftKey !== true) {
toolkit.setSelection(params.obj)
} else {
// if multiple nodes already selected, or shift was pressed, add this node to the current selection.
toolkit.addToSelection(params.obj)
}
}
}
}
},
}

To map a node/group type to some JSX, you're expected to provide a function that takes ctx as argument, and returns JSX.

ctx is an object containing five things:

interface JsxContext {
vertex: Node | Group; // the vertex - node or group - that is being rendered by this JSX
surface: Surface; // the Surface widget that is rendering the vertex
toolkit: JsPlumbToolkit; // the underlying JsPlumbToolkit instance
data: Record<string, any>; // the vertex's backing data. This is just a convenience - it can be accessed via `vertex.data`
props: Record<string, any>; // these are any props that you passed in as the `childProps` argument to a `SurfaceComponent`. See below.
}

As mentioned above, you can pass back arbitrary JSX from these functions.

In this view you'll also see we pass a shapeLibrary in to each component - the demonstration uses a ShapeLibrary to draw the various flowchart shapes. What you pass in to your JSX is entirely your choice - JsPlumb gives you the ctx variable and you can use it how you like.

Passing props

You can pass props in to the components used to render your vertices by setting them on the childProps prop of a Surface component. For instance, in the , DemoComponent - the main app - declares a random color variable in its initial state:

class DemoComponent extends React.Component {

constructor(props) {
super(props);
this.toolkit = jsPlumbToolkit.newInstance();
this.state = { color:randomColor() };

}

}

In the render method of the DemoComponent , a surface component is created, and the color member of the main component's state is passed in:

render() {
return <div style={{width:"100%",height:"100%",display:"flex"}}>
<button onClick={this.changeColor.bind(this)} style={{backgroundColor:this.state.color}} className="colorButton">Change color</button>
<SurfaceComponent childProps={{color:this.state.color}} renderOptions={this.renderOptions} toolkit={this.toolkit} view={this.view} />
</div>
}

In the view for the DemoComponent , each of the node types references the Surface's childProps by way of the context they are given:

this.view = {
nodes: {
"shin":{
jsx: (ctx) => { return <ShinBoneComponent color={ctx.props.color} ctx={ctx}/> }
},
"knee":{
jsx: (ctx) => { return <KneeBoneComponent color={ctx.props.color} ctx={ctx}/> }
}
},
...
}

So, here, ctx.props.color is referencing this.state.color in DemoComponent . If we change DemoComponent 's state, the change propagates through to the vertex components:

changeColor() {
const current = this.state.color;
let col;
while (true) {
col = randomColor();
if (col !== current) {
break;
}
}
this.setState({ color:col } )
}

Context and Providers

JsPlumb React 6.40.0 has support for React's concepts of Context and Providers , and this offers a means for you to build apps with JsPlumb with far less boilerplate than was previously required. In the component discussions above you can see how MiniviewComponent and ControlsComponent can be nested inside a SurfaceComponent , and also how PaletteComponent was nested inside a SurfaceProvider - these are examples of providers and context in action.

For a primer on providers and context you can check out the React docs .

A Context is a means for some component to provide data to all of its descendants. JsPlumb React has two.

JsPlumbContext

This context contains an instance of the JsPlumb Toolkit - the underlying data model. A JsPlumbContext is created when a JsPlumbProvider is declared in your JSX. It's not as common to need a JsPlumbContext as it is to need a SurfaceContext : it's typically only needed in apps that wish to render one data model to more than one canvas.

SurfaceContext

This context contains a Surface. A SurfaceContext is created when:

  • a SurfaceProvider is declared in your JSX (see below); or
  • a SurfaceComponent is created and it is not inside an existing SurfaceContext
  • In the component examples above, we see the MiniviewComponent and ControlsComponent declared without any props, as children of a SurfaceComponent . The SurfaceComponent populates a SurfaceContext with the surface it has created, and the MiniviewComponent and ControlsComponent discover the surface via the SurfaceContext :

    <SurfaceComponent renderOptions={renderOptions}>
    <ControlsComponent/>
    <MiniviewComponent/>
    </SurfaceComponent>

    SurfaceProvider

    This provider offers a means for you to share a surface located somewhere in your UI with other components that are not necessarily descendants of the surface component. A common use case is for Inspectors and Palettes - often you'll find they do not exist as children of the surface element but rather elsewhere in the DOM, for instance:

    <div className="row">
    <SurfaceProvider>
    <div className="col-9">
    <SurfaceComponent/>
    </div>
    <div className="col-3 d-flex flex-column">
    <PaletteComponent selector="li" dataGenerator={dataGenerator} typeExtractor={typeExtractor}>
    <ul>
    <li data-node-type="foo" jtk-is-group="true">FOO</li>
    <li data-node-type="bar">BAR</li>
    </ul>
    </PaletteComponent>
    <InspectorComponent/>
    </div>
    </SurfaceProvider>
    </div>

    Here we see a UI using Bootstrap , consisting of a surface across 9 columns, and then a right hand pane containing a palette and an inspector. The SurfaceProvider is a common parent for all of the JsPlumb components.

    JsPlumbProvider

    This provides a JsPlumbContext - a typical use case is when you want to render some data model to more than one place.

    <div className="row">
    <JsPlumbProvider>
    <div className="col-6">
    <SurfaceComponent/>
    </div>
    <div className="col-6">
    <SurfaceComponent/>
    </div>
    </JsPlumbProvider>
    </div>

    Shape libraries

    A shape library is a set of named shapes that you will use to render SVG inside the vertices in your application. These are discussed in detail in the shape libraries and shape-sets pages; here we provide an overview of the available React integration with these modules.

    Creating a shape library

    In React, a shape library is created the same way as in vanilla js. In the code below we create a shape library with flowchart and basic shapes, which we then inject into our SurfaceComponent :

    import React from 'react';
    import { FLOWCHART_SHAPES, BASIC_SHAPES, ShapeLibraryImpl, DEFAULT } from "@jsplumbtoolkit/browser-ui"

    import NodeComponent from './node-component'

    export default function FlowchartComponent() {

    const shapeLibrary = new ShapeLibraryImpl([FLOWCHART_SHAPES, BASIC_SHAPES])

    const viewOptions = {
    nodes: {
    [DEFAULT]: {
    jsx: (ctx) => {
    return <NodeComponent ctx={ctx} />
    }
    }
    }
    }

    return <div>
    <SurfaceComponent viewOptions shapeLibrary={shapeLibrary}/>
    </div>

    Rendering an SVG shape

    Shapes can be rendered with a ShapeComponent . This takes the ctx that JsPlumb passes into your JSX inside the view as a prop, from which it can extract the current vertex and the underlying surface, plus the shape library the surface is using:

    import * as React from 'react';

    import { ShapeComponent } from "@jsplumbtoolkit/browser-ui-react"

    export default function NodeComponent({ctx}) {

    return <div className="my-class">
    <ShapeComponent ctx={ctx} />
    </div>
    }

    By default the shape component will render just the shape. If you want to add labels you can do so like this:

    import * as React from 'react';

    import { ShapeComponent } from "@jsplumbtoolkit/browser-ui-react"

    export default function NodeComponent({ctx}) {

    return <div className="my-class">
    <ShapeComponent ctx={ctx} showLabels={true} labelProperty="text"/>
    </div>
    }

    The full list of props is available below .

    Adding your own shapes

    To add your own shapes, you'll need to create a shape set. These are discussed on a separate page .

    Dragging new nodes/groups

    Palettes offer a means for your users to drag new nodes/groups onto your canvas. The PaletteComponent is the main component you'll use; there's also a ShapeLibraryPaletteComponent which is a specialised version of the PaletteComponent that sources its draggable nodes from a ShapeLibrary .

    HTML elements

    To configure your UI to drag and drop HTML elements onto your canvas, you'll use a PaletteComponent . Here's how to set one up:


    import { PaletteComponent, SurfaceComponent, SurfaceProvder } from '@jsplumbtoolkit/browser-ui-react';

    export default function MyApp() {

    const dataGenerator = function (type) { return { w:120, h:80, type:el.getAttribute("data-node-type") } };

    return <div className="row">
    <SurfaceProvider>
    <div className="col-9">
    <SurfaceComponent/>
    </div>
    <div className="col-3">
    <PaletteComponent selector="li" dataGenerator={dataGenerator}>
    <ul>
    <li data-node-type="foo" jtk-is-group="true">FOO</li>
    <li data-node-type="bar">BAR</li>
    </ul>
    </PaletteComponent>
    </div>
    </SurfaceProvider>
    </div>
    }

    The basic contract is that you declare a PaletteComponent in your JSX, and you provide - at the minimum - a selector , which tells the palette component how to find which children of the component are draggable. You then write out the children however you like - in this case we use a ul and an li for each draggable type.

    The PaletteComponent needs to know which surface it is going to be attached to, and it does this by being context aware - in the code above we have a SurfaceProvider wrapping both the surface component and the palette component. When the surface component is instantiated, it populates the surface provider's context, and the palette component is notified of the surface to use.

    In the code above we also see a dataGenerator prop: dataGenerator provides a means for the component to generate an initial data payload for some node that is being dragged. You can read about the full list of available props in the component list below .

    SVG shapes

    To drag and drop SVG shapes, you can use a ShapeLibraryPaletteComponent . This is a specialised version of the PaletteComponent which sources its draggable nodes from a ShapeLibrary .

    import React, { useEffect, useRef} from 'react';
    import { FLOWCHART_SHAPES, BASIC_SHAPES, ShapeLibraryImpl } from "@jsplumbtoolkit/browser-ui"
    import { ShapeLibraryPaletteComponent, SurfaceComponent, SurfaceProvider } from "@jsplumbtoolkit/browser-ui-react"

    export default function FlowchartComponent() {

    const shapeLibrary = new ShapeLibraryImpl([FLOWCHART_SHAPES, BASIC_SHAPES])
    const dataGenerator = function (type) { return { w:120, h:80, type:el.getAttribute("data-node-type") } };

    return <div>
    <SurfaceProvider>
    <SurfaceComponent viewOptions={viewOptions} shapeLibrary={shapeLibrary}/>
    <ShapeLibraryPaletteComponent className="node-palette"
    dataGenerator={dataGenerator} initialSet={FLOWCHART_SHAPES.id}/>
    </SurfaceProvider>

    </div>

    As with PaletteComponent , the ShapeLibraryPaletteComponent is context aware and can locate the surface to use from a SurfaceProvider .

    Component List

    This is a full list of the components that are available in JsPlumb React. For each we provide a sample usage, which does not necessarily use all of the props available for the given component. We then provide the full list of available props.

    SurfaceComponent

    The key UI component you'll use to render your UI.

    <SurfaceComponent renderOptions={someRenderOptions} 
    viewOptions={someViewOptions}/>

    Interface SurfaceComponentProps

    Name Type Description
    childProps ? any Any props to pass to children.
    className ? string Optional class name to append to the root element's class list.
    data ? any Optional initial data
    modelOptions ? JsPlumbToolkitOptions Options for the underlying JsPlumb Toolkit instance. If you use this component as a child of a JsPlumbComponent then the underlying JsPlumb instance will be sourced from the JsPlumbComponent. Otherwise, this component will instantiate a JsPlumb instance itself, optionally using these params.
    renderOptions ? ReactSurfaceRenderOptions Render options such as layout, drag options etc
    shapeLibrary ? ShapeLibraryImpl Optional shape library for the underlying surface to use. You can provide this or you can also provide a shapes render option to the surface.
    surfaceId ? string Optional ID to assign to the surface that is created.
    toolkit ? BrowserUIReact JsPlumb instance to use - optional. The SurfaceComponent will create find a JsPlumb instance in an enclosing JsPlumbContext, if applicable, or otherwise will create its own JsPlumb instance (for which you can provide modelOptions if you wish)
    url ? string Optional URL to load initial data from.
    viewOptions ? ReactSurfaceViewOptions Mappings of vertex types to components/jsx and edge types.

    MiniviewComponent

    Provides a Miniview of the contents of some surface. This component can be nested inside a SurfaceComponent , from which it will find the surface to attach to, or it can also exist as a descendant of a SurfaceProvider - the approach you choose will depend on the structure of your page.

    Example


    export default function MyComponent() {
    return <SurfaceComponent renderOptions={renderOptions}>
    <MiniviewComponent/>
    </SurfaceComponent>
    }

    Interface MiniviewComponentProps

    Name Type Description
    activeTracking ? boolean Whether or not to move miniview elements at the same time as their related surface element is being dragged. Defaults to true.
    className ? string Optional class name to append to the root element's class list.
    clickToCenter ? boolean Defaults to true, meaning a click on a node/group in the miniview will cause that node/group to be centered in the related surface.
    elementFilter ? Optional filter to decide which elements to show in the miniview.
    typeFunction ? Optional function to use to decorate miniview elements with a jtk-miniview-type attribute. Can be used for simple styling.

    ControlsComponent

    Provides a component that offers a set of controls for some surface. If there is a lasso plugin installed on the given surface this component will show buttons for pan/select mode, otherwise it will not. This component can be nested inside a SurfaceComponent , from which it will find the surface to attach to, or it can also exist as a descendant of a SurfaceProvider - the approach you choose will depend on the structure of your page.

    Example


    export default function MyComponent() {
    return <SurfaceComponent renderOptions={renderOptions}>
    <ControlsComponent/>
    </SurfaceComponent>
    }

    Props

    Interface ControlsComponentProps

    Name Type Description
    buttons ? ControlsComponentButtons Optional extra buttons to add to the controls component.
    className ? string Optional class name to append to the root element's class list.
    clear ? boolean Whether or not to show the clear button, defaults to true.
    clearMessage ? string Optional message for the alert that the clear button shows by default. Defaults to Clear dataset? .
    onMaybeClear ? Optional callback to invoke when the user presses the clear button. If you provide this, the component will not show an alert and instead call this method, passing in a function you can invoke if you wish to continue with the clear operation.
    orientation ? Optional orientation for the controls. Defaults to 'row'.
    surfaceId ? string The ID of surface to attach to. Optional.
    undoRedo ? boolean Whether or not to show undo/redo buttons, defaults to true
    zoomToExtents ? boolean Whether or not to show the zoom to extents button, defaults to true

    ExportControlsComponent

    Provides a component that offers a set of buttons users can click to generate SVG, PNG or JPG output of the canvas.

    Example


    export default function MyComponent() {
    return <SurfaceComponent renderOptions={renderOptions}>
    <ExportControlsComponent/>
    </SurfaceComponent>
    }

    Props

    Interface ExportControlsComponentProps

    Name Type Description
    allowJpgExport ? boolean Defaults to true.
    allowPngExport ? boolean Defaults to true.
    allowSvgExport ? boolean Defaults to true.
    imageOptions ? ImageExportUIOptions Options for image exports.
    label ? string What to show in the label, if visible. Defaults to "Export:".
    margins ? PointXY Optional margins to apply to both SVG and image exports. Will not override any margins specified in svgOptions or imageOptions.
    showLabel ? boolean Whether or not to show a label in front of the buttons. Defaults to true.
    surfaceId ? string The ID of surface to attach to. Optional.
    svgOptions ? SvgExportUIOptions Options for SVG exports.

    PaletteComponent

    Provides a means to implement drag/drop of new Nodes/Groups onto your Surface. This component can be nested inside a SurfaceComponent , from which it will find the surface to attach to, or it can also exist as a descendant of a SurfaceProvider - the approach you choose will depend on the structure of your page.

    Example


    import { PaletteComponent, SurfaceComponent, SurfaceProvder } from '@jsplumbtoolkit/browser-ui-react';

    export default function MyApp() {

    const typeExtractor = function(el) { return el.getAttribute("data-node-type") };
    const dataGenerator = function (type) { return { w:120, h:80 }; };

    return <div className="row">
    <SurfaceProvider>
    <div className="col-9">
    <SurfaceComponent/>
    </div>
    <div className="col-3">
    <PaletteComponent selector="li" dataGenerator={dataGenerator} typeExtractor={typeExtractor}>
    <ul>
    <li data-node-type="foo" jtk-is-group="true">FOO</li>
    <li data-node-type="bar">BAR</li>
    </ul>
    </PaletteComponent>
    </div>
    </SurfaceProvider>
    </div>
    }

    Props

    Interface PaletteComponentProps

    Name Type Description
    allowDropOnCanvas ? string Whether or not to allow drop on whitespace in the canvas. Defaults to true.
    allowDropOnEdge ? string Whether or not to allow drop on edge. Defaults to false.
    allowDropOnGroup ? string Whether or not to allow drop on existing vertices. Defaults to true.
    children ? Child elements. Although this is optional it is expected there will be some - otherwise the palette does nothing.
    className ? string Optional extra css classes to set on the element
    dataGenerator ? DataGeneratorFunction Function to use to get a dataset for an item that is being dragged.
    groupIdentifier ? GroupIdentifierFunction Function to use to determine whether an item that is being dragged represents a group.
    selector string CSS 3 selector identifying what child elements of container are draggable.
    surface ? Surface The surface to attach to. Optional: this component can derive a surface from a SurfaceProvider or SurfaceComponent.
    typeGenerator ? TypeGeneratorFunction Function to use to get the type of an item that is being dragged.

    ShapeLibraryPaletteComponent

    This is a specialised version of the PaletteComponent which sources its draggable nodes from a ShapeLibrary .

    Example

    import React, { useEffect, useRef} from 'react';
    import { FLOWCHART_SHAPES, BASIC_SHAPES, ShapeLibraryImpl } from "@jsplumbtoolkit/browser-ui"
    import { ShapeLibraryPaletteComponent, SurfaceComponent, SurfaceProvider } from "@jsplumbtoolkit/browser-ui-react"

    export default function FlowchartComponent() {

    const shapeLibrary = new ShapeLibraryImpl([FLOWCHART_SHAPES, BASIC_SHAPES])
    const dataGenerator = function (type) { return { w:120, h:80, type:el.getAttribute("data-node-type") } };

    return <div>
    <SurfaceProvider>
    <SurfaceComponent viewOptions={viewOptions}/>
    <ShapeLibraryPaletteComponent className="node-palette"
    shapeLibrary={shapeLibrary} dataGenerator={dataGenerator} initialSet={FLOWCHART_SHAPES.id}/>
    </SurfaceProvider>

    </div>

    Props

    Interface ShapeLibraryPaletteComponentProps

    Name Type Description
    canvasStrokeWidth ? number Stroke width to use for shapes dropped on canvas. Defaults to 2.
    dataGenerator ? DataGeneratorFunction Optional data generator to allow you to specify initial data for some element to be dragged. Note that you cannot override the object's type with this function. The palette will set the new object's type to match the type of the element that the user is dragging from the palette.
    dragSize ? Size Optional size to use for dragged elements.
    fill ? string Optional fill color to use for dragged elements. This should be in RGB format, _not_ a color like 'white' or 'cyan' etc.
    iconSize ? Size Optional size to use for icons.
    initialSet ? string ID of the initial set to show, if any. When you have multiple shapes in a set you may want the palette to open up showing just one set.
    outline ? string Optional color to use for outline of dragged elements. Should be in RGB format.
    paletteStrokeWidth ? number Stroke width to use for shapes in palette. Defaults to 1.
    selectAfterDrop ? boolean When true (which is the default), a newly dropped vertex will be set as the underlying Toolkit's selection.
    shapeLibrary ? ShapeLibraryImpl The shape library to render.
    showAllMessage ? string Message to use for the 'show all' option in the shape set drop down when there is more than one set of shapes. Defaults to Show all .
    surface ? Surface Surface to attach the drag/drop to. Optional; this component can extract the Surface from a SurfaceProvider, or from being a child of a SurfaceComponent.

    ShapeComponent

    This component is used in conjunction with a ShapeLibrary to render SVG shapes.