0.1.1-alpha.1 • Published 6 months ago

@joint/react v0.1.1-alpha.1

Weekly downloads
-
License
MPL-2.0
Repository
github
Last release
6 months ago

JointJS React

React core components for working with JointJS.

!NOTE The goal of this project is to define a minimal basis for the various React components for JointJS. Please help us shape the package by reporting issues or proposing API changes.

!IMPORTANT This is an early stage product. The package may contain bugs and security issues. The API is subject to change.

Installation

yarn install jointjs @joint/react

API

Components

\<Paper />

The main component that allows you to draw nodes and edges on the canvas. In JointJS terminology, you draw cells (elements and links) on paper. The component requires the <GraphProvider /> to be its ancestor.

Props
PropertyTypeDescription
options?dia.Paper.OptionsThe options of the paper. It's async by default.
renderElement?(dia.Element) => React.JSX.Element \| nullA callback to render React components inside the paper element views. The components are rendered using the React.createPortal().
onReady?(dia.Paper) => voidA callback that is triggered after the paper is mounted and ready (cells may not be rendered).
onEvent?(dia.Paper, eventName, ...eventArgs) => voidA callback that allows you to listen to the dia.Paper events.
dataAttributes?string[]A list of model attributes that, if changed, will cause the renderElement function to be triggered. The default is ["data"].
portal?Element \| string \| (dia.ElementView) => string \| ElementAn HTMLElement or SVGElement (or a string selector) that serves as portal for rendering element's content. By default, the portal is selector "portal".
Rendering React component inside an Element view

By default, the content of the element view is rendered using JointJS. However, the content (SVG or HTML) can also be rendered using React.

import { dia, shapes } from 'jointjs';
import { GraphProvider, Paper } from '@joint/react';

// Assumes that the `portal` node is a foreign object descendant.
const renderHTMLElement = (element) => {
   const { label, value } = element.get('data');
   return (
       <div className="my-element">
           <h3>{label}</h3>
           <input
               type='text'
               value={value}
               onChange={(e) => element.prop('data/value', e.target.value)}
           />
       </div>
   );
};

export default function Diagram() {
   const graph = new dia.Graph({}, { cellNamespace: shapes });
   const paperProps = { /* ... */ };
   return (
       <GraphProvider graph={graph}>
           <Paper renderHTMLElement={renderHTMLElement} ...paperProps />
       </GraphProvider>
   )
}

Currently, the following drawbacks are known with this approach:

  • content rendered with React cannot be the source/target of any link (it's always a good idea to also render the SVG \ under the portal node).
  • <Paper /> currently replaces the default dia.Paper.prototype.options.elementView with a custom ElementView that triggers the portal:ready event ((elementView: dia.ElementView, portalEl: SVGElement | HTMLElement) => void) when the portal node is rendered (onRender() method). If you want to use custom views and you want to use renderElement with them, make sure you trigger the portal:ready event manually.
Paper provides context implicitly.

The <Paper /> context provides the paper context to its descendants implicitly. If you need to use the paper context outside of the <Paper />, use the <PaperProvider />.

<GraphProvider graph={graph}>
   <Paper>
       <MySelection/>{/* component is using JointJS `paper` */}
   </Paper>
</GraphProvider>

\<PaperProvider />

The <PaperProvider /> component is a context provider that makes it possible to access the JointJS paper outside of the <Paper /> component. Unlike the GraphProvider, the PaperProvider is not mandatory.

import { GraphProvider, PaperProvider, Paper } from '@joint/react';

export default function Diagram() {
    return (
        <GraphProvider graph={graph}>
            <PaperProvider>
                <Paper/>
                <MyZoomInButton/>
                <MyZoomOutButton/>
            </PaperProvider>
        </GraphProvider>
    )
}

\<GraphProvider />

The <GraphProvider /> component is a context provider that provides JointJS graph to <Paper /> components. You need use the <GraphProvider /> in order to render the <Paper /> component.

Here's an example of a GraphProvider providing a graph to two papers.

import { dia, shapes } from 'jointjs';
import { GraphProvider, Paper } from '@joint/react';

export default function Diagram() {
    const graph = new dia.Graph({}, { cellNamespace: shapes });
    const paperProps = { /* ... */ };
    const minimapProps = {  /* ... */ };
    return (
        <GraphProvider graph={graph}>
            <Paper ...paperProps className="canvas" />
            <Paper ...minimapProps className="minimap" />
        </GraphProvider>
    )
}
Props
PropertyTypeDescription
graphdia.GraphA graph instance to be provided to the descendants

Hooks

The package exposed several custom hooks.

usePaper

The usePaper is a hook that let you use the JointJS paper from your component.

import { usePaper } from '@joint/react';

export default function MyZoomInButton() {
    const paper = usePaper();
    const zoomIn = () => {
        if (!paper) return;
        const { sx, sy } = paper.scale();
        paper.scale(sx * 2, sy * 2);
    }
    return <button onClick={zoomIn}>Zoom In</button>
}

useGraph

The useGraph is a hook that let you use the JointJS graph from your component.

import { useGraph } from '@joint/react';

export default function MyDeleteAllButton() {
    const graph = useGraph();
    const deleteAll = () => {
        if (graph && confirm("Are you sure you want to delete all content?")) {
            graph.clear();
        }
    }
    return <button onClick={deleteAll}>Delete All</button>
}

What's next

The possible tasks ahead of us.

JointJS+

Define React Components for JointJS+.

// NOTE: This is fictional code
// It is just an example of possible API.
import { dia, mvc } from '@joint/core';
import { CommandManager } from '@joint/command-manager';
import { Paper } from '@joint/react';
import DiagramProvider from '@joint/diagram-provider';
import Toolbar from '@joint/toolbar-react';
import Stencil from '@joint/stencil-react';
import Scroller from '@joint/scroller-react';
import Inspector from '@joint/inspector-react';
import Selection from '@joint/selection-react';
import Snaplines from '@joint/snaplines-react';
import Grid from 'some-ui-lib';

export default function Diagram() {
    const graph = new dia.Graph();
    const cmd = new CommandManager({ graph });
    const selection = new mvc.Collection();
    return (
        <DiagramProvider graph={graph} commandManager={cmd} selection={selection}/>
            <Toolbar />
            <Grid>
                <Stencil />
                <Scroller />
                    <Paper >
                        <Selection />
                        <Snaplines />
                    </Paper>
                <Scroller>
                <Navigator />
            </Grid>
            <Navigator />
        <DiagramProvider />
    )
}

Higher-level Components

Define user-friendly higher-level components.

// Paper, Scroller, Toolbar, CommandManager as a single component
<Diagram preset="kitchen-sink" width={400} height={400} fitView={true} virtualRendering={true}></Diagram>
// Domain-specific diagram components
<OrgChartDiagram data={adjacencyList}></OrgChartDiagram>

License

Mozilla Public License 2.0

Copyright © 2013-2023 client IO