Skip to content

feat(joint-react): paper provider #2971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions packages/joint-react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Changelog

All notable changes to @joint/react will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.0.1] - 2025-27-05

### Added
- Initial release of @joint/react
- Core components: GraphProvider, Paper
- Element and link rendering system
- Accessibility features
- TypeScript support
- Testing utilities
- Error boundaries
- Hooks for state management

### Changed
- N/A (initial release)

### Deprecated
- N/A (initial release)

### Removed
- N/A (initial release)

### Fixed
- N/A (initial release)

### Security
- N/A (initial release)

180 changes: 155 additions & 25 deletions packages/joint-react/README.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,190 @@
<div style="display:flex;align-items:center;background:#131E29;padding:1.5rem 1rem 1.5rem 1rem;margin-bottom:2rem;">
<img src="https://cdn.prod.website-files.com/63061d4ee85b5a18644f221c/633045c1d726c7116dcbe582_JJS_logo.svg" alt="JointJS Logo" height="56" style="margin-right:1.5rem;display:inline-block;" />
</div>

# @joint/react

A React library for creating and managing interactive diagrams and graphs using JointJS.
A React library for building interactive diagrams, flowcharts, and graph-based visualizations. This library provides React components and hooks that wrap JointJS, making it easy to create powerful diagramming applications.

## What can you build with @joint/react?

- 📊 Flowcharts and process diagrams
- 🌐 Network topology visualizations
- 🧠 Mind maps and organization charts
- ⚙️ State machines and workflow editors
- 📈 Any interactive connected graphs

## Overview
## Why @joint/react?

**@joint/react** is a React wrapper for [JointJS](https://www.jointjs.com/), designed to simplify the creation and management of interactive diagrams and graphs in React applications. It provides intuitive React components, hooks, and utilities to efficiently manage nodes, links, and user interactions.
- 🎯 **React-First**: React components and hooks for modern React applications
- 🔌 **Easy Integration**: Simple drop-in components with minimal setup
- 🎨 **Customizable**: Full control over node and link appearances
- ⚡ **Interactive**: Built-in support for dragging, connecting, and editing
- 🎭 **Type-Safe**: Written in TypeScript with full type definitions

## Prerequisites

Before installing @joint/react, ensure you have:
- React 16.8+ (for Hooks support)
- Node.js 14+
- A modern browser (Chrome, Firefox, Safari, Edge)

## Installation

Install the library using your preferred package manager:

### Yarn
```sh
yarn add @joint/react
```

### Npm
```sh
# Using npm
npm install @joint/react
```

### Bun
```sh
# Using yarn
yarn add @joint/react

# Using bun
bun add @joint/react
```

## Core Concepts

Before diving into the code, let's understand the basic building blocks:

- **Elements**: Nodes in your diagram (boxes, circles, or custom shapes)
- **Links**: Connections between elements (lines, arrows, or custom connectors)
- **Paper**: The canvas (UI) where your diagram is rendered
- **Graph**: The data model that holds your diagram's structure

## Quick Start

Heres a simple example to get started:
Here's a complete example of a simple diagram with two connected nodes:

```tsx
import React, { useCallback } from 'react';
import { GraphProvider, Paper, createElements, createLinks } from '@joint/react';

// Define your diagram elements (nodes)
const initialElements = createElements([
{ id: '1', label: 'Node 1', x: 100, y: 0, width: 100, height: 50 },
{ id: '2', label: 'Node 2', x: 100, y: 200, width: 100, height: 50 },
{
id: '1',
label: 'Start',
x: 100, // Position from left
y: 50, // Position from top
width: 120,
height: 60
},
{
id: '2',
label: 'End',
x: 100,
y: 200,
width: 120,
height: 60
},
]);

const initialLinks = createLinks([{ id: 'e1-2', source: '1', target: '2' }]);
// Define connections between elements
const initialLinks = createLinks([
{
id: 'link1',
source: '1', // ID of source element
target: '2' // ID of target element
}
]);

function Main() {
const renderElement = useCallback(
(element) => <div className="node">{element.label}</div>,
[]
);
// Main component that renders the diagram
function DiagramExample() {
// Define how each element should look
const renderElement = useCallback((element) => (
<div style={{
padding: '10px',
border: '2px solid #3498db',
borderRadius: '8px',
background: 'white'
}}>
{element.label}
</div>
), []);

return (
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Paper width="100%" renderElement={renderElement} />
<div style={{ height: '400px', border: '1px solid #ccc' }}>
<Paper
initialElements={initialElements}
width="100%"
height="100%"
renderElement={renderElement}
useHTMLOverlay
/>
</div>
);
}

// Wrap your app with GraphProvider
export default function App() {
return (
<GraphProvider initialLinks={initialLinks} initialElements={initialElements}>
<Main />
<GraphProvider
initialElements={initialElements}
initialLinks={initialLinks}
>
<DiagramExample />
</GraphProvider>
);
}
```

## Event Handling

@joint/react provides various events to handle user interactions:

```tsx
function DiagramExample() {
const handleElementClick = useCallback((element) => {
console.log('Element clicked:', element);
}, []);

return (
<Paper
width="100%"
height="100%"
onElementPointerClick={handleElementClick}
/>
);
}
```

## TypeScript Support

@joint/react is written in TypeScript and includes comprehensive type definitions. Here's an example of using types:

```tsx
import { InferElement } from '@joint/react';

const elements = createElements([
{ id: '1', label: 'Node', x: 0, y: 0, width: 100, height: 40 }
]);

type CustomElement = InferElement<typeof elements>;

const renderElement = (element: CustomElement) => (
<div>{element.label}</div>
);
```

## Performance Considerations

To ensure optimal performance:

1. **Memoization**
```tsx
// Memoize render functions
const renderElement = useCallback((element) => {
return <CustomNode element={element} />;
}, []);

// Memoize event handlers
const handleElementClick = useCallback((element) => {
// Handle click
}, []);
```

## 📌 Core Components

### 1. **GraphProvider**
Expand Down Expand Up @@ -109,6 +232,12 @@ const renderElement = ({ width, height }) => (

### 🔹 Modifying Elements
- `useUpdateElement()`: Update existing elements in the diagram.
- `useCreateElement()`: Create new elements in the diagram.
- `useRemoveElement()`: Remove elements from the diagram.

- `useCreateLink()`: Create new elements in the diagram.
- `useRemoveLink()`: Remove elements from the diagram.


### 🔹 Graph and Paper Instances
- `useGraph()`: Access the [dia.Graph](https://docs.jointjs.com/api/dia/Graph/) instance directly.
Expand All @@ -117,6 +246,7 @@ const renderElement = ({ width, height }) => (
### 🔹 Creating Nodes and Links
- `createElements()`: Utility for creating nodes.


```ts
import { createElements } from '@joint/react';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ exports[`GraphProvider "Default" 1`] = `
style="opacity: 1; pointer-events: all; position: relative; overflow: hidden; width: 100%; height: 100%;"
>
<div
class="joint-paper joint-theme-default"
class="joint-border-1 joint-border-gray-300 joint-rounded-lg joint-shadow-md joint-overflow-hidden joint-p-2 joint-mr-2 joint-theme-default"
style="position: relative; width: 100%; height: 600px;"
>
<div
Expand Down Expand Up @@ -72,7 +72,7 @@ exports[`GraphProvider "Default" 1`] = `
style="opacity: 1; pointer-events: all; position: relative; overflow: hidden; width: 100%; height: 100%;"
>
<div
class="joint-paper joint-theme-default"
class="joint-border-1 joint-border-gray-300 joint-rounded-lg joint-shadow-md joint-overflow-hidden joint-p-2 joint-mr-2 joint-theme-default"
style="position: relative; width: 100%; height: 600px;"
>
<div
Expand Down Expand Up @@ -191,7 +191,7 @@ exports[`GraphProvider "WithExternalGraph" 1`] = `
style="opacity: 1; pointer-events: all; position: relative; overflow: hidden; width: 100%; height: 100%;"
>
<div
class="joint-paper joint-theme-default"
class="joint-border-1 joint-border-gray-300 joint-rounded-lg joint-shadow-md joint-overflow-hidden joint-p-2 joint-mr-2 joint-theme-default"
style="position: relative; width: 100%; height: 600px;"
>
<div
Expand Down Expand Up @@ -253,7 +253,7 @@ exports[`GraphProvider "WithExternalGraph" 1`] = `
style="opacity: 1; pointer-events: all; position: relative; overflow: hidden; width: 100%; height: 100%;"
>
<div
class="joint-paper joint-theme-default"
class="joint-border-1 joint-border-gray-300 joint-rounded-lg joint-shadow-md joint-overflow-hidden joint-p-2 joint-mr-2 joint-theme-default"
style="position: relative; width: 100%; height: 600px;"
>
<div
Expand Down Expand Up @@ -377,7 +377,7 @@ exports[`GraphProvider "WithExternalGraphAndLayout" 1`] = `
style="opacity: 1; pointer-events: all; position: relative; overflow: hidden; width: 100%; height: 100%;"
>
<div
class="joint-paper joint-theme-default"
class="joint-border-1 joint-border-gray-300 joint-rounded-lg joint-shadow-md joint-overflow-hidden joint-p-2 joint-mr-2 joint-theme-default"
style="position: relative; width: 100%; height: 600px;"
>
<div
Expand Down Expand Up @@ -444,7 +444,7 @@ exports[`GraphProvider "WithExternalGraphAndLayout" 1`] = `
style="opacity: 1; pointer-events: all; position: relative; overflow: hidden; width: 100%; height: 100%;"
>
<div
class="joint-paper joint-theme-default"
class="joint-border-1 joint-border-gray-300 joint-rounded-lg joint-shadow-md joint-overflow-hidden joint-p-2 joint-mr-2 joint-theme-default"
style="position: relative; width: 100%; height: 600px;"
>
<div
Expand Down Expand Up @@ -563,7 +563,7 @@ exports[`GraphProvider "WithLink" 1`] = `
style="opacity: 1; pointer-events: all; position: relative; overflow: hidden; width: 100%; height: 100%;"
>
<div
class="joint-paper joint-theme-default"
class="joint-border-1 joint-border-gray-300 joint-rounded-lg joint-shadow-md joint-overflow-hidden joint-p-2 joint-mr-2 joint-theme-default"
style="position: relative; width: 100%; height: 600px;"
>
<div
Expand Down Expand Up @@ -625,7 +625,7 @@ exports[`GraphProvider "WithLink" 1`] = `
style="opacity: 1; pointer-events: all; position: relative; overflow: hidden; width: 100%; height: 100%;"
>
<div
class="joint-paper joint-theme-default"
class="joint-border-1 joint-border-gray-300 joint-rounded-lg joint-shadow-md joint-overflow-hidden joint-p-2 joint-mr-2 joint-theme-default"
style="position: relative; width: 100%; height: 600px;"
>
<div
Expand Down Expand Up @@ -744,7 +744,7 @@ exports[`GraphProvider "WithoutSizeDefinedInElements" 1`] = `
style="opacity: 0; pointer-events: none; position: relative; overflow: hidden; width: 100%; height: 100%;"
>
<div
class="joint-paper joint-theme-default"
class="joint-border-1 joint-border-gray-300 joint-rounded-lg joint-shadow-md joint-overflow-hidden joint-p-2 joint-mr-2 joint-theme-default"
style="position: relative; width: 100%; height: 600px;"
>
<div
Expand Down Expand Up @@ -806,7 +806,7 @@ exports[`GraphProvider "WithoutSizeDefinedInElements" 1`] = `
style="opacity: 0; pointer-events: none; position: relative; overflow: hidden; width: 100%; height: 100%;"
>
<div
class="joint-paper joint-theme-default"
class="joint-border-1 joint-border-gray-300 joint-rounded-lg joint-shadow-md joint-overflow-hidden joint-p-2 joint-mr-2 joint-theme-default"
style="position: relative; width: 100%; height: 600px;"
>
<div
Expand Down
Loading