sweet-diagram
Version:
A modern and intuitive diagram editor component for React applications with advanced auto-connect features, Sankey diagrams, Stack functionality, vertical text support, animation effects, and comprehensive component library
445 lines (358 loc) β’ 13 kB
Markdown
π Sweet Diagram
[](https://badge.fury.io/js/sweet-diagram)
[](https://www.npmjs.com/package/sweet-diagram)
[](https://opensource.org/licenses/MIT)
[](https://reactjs.org/)
**νλμ μ΄κ³ μ§κ΄μ μΈ React λ€μ΄μ΄κ·Έλ¨ μλν° μ»΄ν¬λνΈ** with advanced auto-connect features, Sankey diagrams, Stack functionality, vertical text support, animation effects, and comprehensive component library.
π **Live Demo**: [https://sweetpotato-diagram.vercel.app](https://sweetpotato-diagram.vercel.app)

- π― **Complete Diagram Solution** - Box, Connector, Arrow, Triangle, Valve, Line, ImageBox components
- π **Sankey Diagrams** - Interactive flow diagrams with proportional connections and JSON import/export
- π **Stack Layout System** - Automatic stacking with priority-based positioning
- π **Auto-Connect** - Intelligent connection system with multiple algorithms
- π **Junction Points** - Advanced connection points for complex diagram layouts
- β©οΈ **Undo/Redo System** - Complete history management with keyboard shortcuts (Ctrl+Z/Ctrl+Y)
- π **Vertical Text Support** - Both horizontal and vertical text orientations
- π¨ **Modern Styling** - Built with TailwindCSS for beautiful designs
- π±οΈ **Interactive & Draggable** - Full mouse and touch support
- β‘ **High Performance** - Optimized for large diagrams
- π **Animation Support** - Smooth transitions and effects
- π± **Responsive Design** - Works on all screen sizes
- π§ **TypeScript Ready** - Full type definitions included
- πͺ **Zero Config** - Works out of the box
## π Quick Installation & Usage
### Step 1: Install Package
```bash
# NPMμΌλ‘ μ€μΉ
npm install sweet-diagram
# λλ YarnμΌλ‘ μ€μΉ
yarn add sweet-diagram
# λλ PNPMμΌλ‘ μ€μΉ
pnpm add sweet-diagram
```
### Step 2: Install Required Dependencies
Sweet Diagram uses peer dependencies for better flexibility:
```bash
# React (required)
npm install react react-dom
# TailwindCSS (highly recommended for styling)
npm install tailwindcss
# Additional peer dependencies (if using advanced features)
npm install @react-three/drei @react-three/fiber three lucide-react zustand
```
We moved heavy dependencies to peer dependencies to give you:
- **Flexibility** - Use your preferred versions
- **Smaller bundle** - Avoid duplicate dependencies
- **Better performance** - Shared dependencies across your app
### Step 3: Import CSS & Components
```jsx
import React from "react";
import { DiagramProvider, Box, Connector, Sankey, Triangle, Valve } from "sweet-diagram";
import "sweet-diagram/dist/sweet-diagram.css";
function MyApp() {
return (
<div className="w-full h-full">
<DiagramProvider width={800} height={600}>
<Box
id="box1"
x={100}
y={100}
width={120}
height={80}
text="μμμ "
className="bg-blue-500 text-white rounded-lg"
/>
<Box
id="box2"
x={300}
y={200}
width={120}
height={80}
text="λμ "
className="bg-green-500 text-white rounded-lg"
/>
<Connector
fromBox={{ id: "box1", position: "right" }}
toBox={{ id: "box2", position: "left" }}
connectionType="straight"
showArrow={true}
/>
</DiagramProvider>
</div>
);
}
export default MyApp;
```
```jsx
import React from "react";
import { Sankey } from "sweet-diagram";
import "sweet-diagram/dist/sweet-diagram.css";
function SankeyExample() {
const data = {
nodes: [
{ id: "A", name: "μμ€ A", layer: 0 },
{ id: "B", name: "μμ€ B", layer: 0 },
{ id: "C", name: "μ€κ° μ²λ¦¬", layer: 1 },
{ id: "D", name: "μ΅μ’
κ²°κ³Ό", layer: 2 },
],
links: [
{ id: "link1", source: "A", target: "C", value: 30 },
{ id: "link2", source: "B", target: "C", value: 20 },
{ id: "link3", source: "C", target: "D", value: 50 },
],
};
return (
<div className="w-full h-96">
<Sankey data={data} width={600} height={400} className="mx-auto border rounded-lg" />
</div>
);
}
```
```jsx
import React, { useState } from "react";
import { Sankey } from "sweet-diagram";
function SankeyWithImport() {
const [sankeyData, setSankeyData] = useState(null);
const handleImportJSON = (file) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = JSON.parse(e.target.result);
setSankeyData(data);
} catch (error) {
console.error("Invalid JSON file:", error);
}
};
reader.readAsText(file);
};
const handleExportJSON = () => {
if (sankeyData) {
const blob = new Blob([JSON.stringify(sankeyData, null, 2)], {
type: "application/json",
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "sankey-data.json";
a.click();
URL.revokeObjectURL(url);
}
};
return (
<div>
<input type="file" accept=".json" onChange={(e) => handleImportJSON(e.target.files[0])} />
<button onClick={handleExportJSON}>Export JSON</button>
{sankeyData && <Sankey data={sankeyData} width={600} height={400} />}
</div>
);
}
```
```jsx
import React from "react";
import { DiagramProvider, Box, Connector, Junction } from "sweet-diagram";
function JunctionExample() {
return (
<DiagramProvider width={800} height={600}>
<Box id="input1" x={50} y={100} width={100} height={60} text="Input 1" />
<Box id="input2" x={50} y={200} width={100} height={60} text="Input 2" />
<Box id="output" x={600} y={150} width={100} height={60} text="Output" />
{/* Junction Point for merging connections */}
<Junction id="junction1" x={400} y={150} size={8} className="fill-red-500 stroke-red-700" />
{/* Connections to junction */}
<Connector fromBox={{ id: "input1" }} toBox={{ id: "junction1" }} connectionType="orthogonal" />
<Connector fromBox={{ id: "input2" }} toBox={{ id: "junction1" }} connectionType="orthogonal" />
{/* Connection from junction to output */}
<Connector fromBox={{ id: "junction1" }} toBox={{ id: "output" }} connectionType="straight" />
</DiagramProvider>
);
}
```
```jsx
import React from "react";
import { DiagramProvider, DraggableBox, UndoRedoButtons, useDiagram } from "sweet-diagram";
function UndoRedoExample() {
const DebugInfo = () => {
const { getDiagramStats } = useDiagram();
const stats = getDiagramStats();
return (
<div className="absolute top-4 left-4 bg-white p-3 rounded shadow">
<div>Undo κ°λ₯: {stats.canUndo ? "β
" : "β"}</div>
<div>Redo κ°λ₯: {stats.canRedo ? "β
" : "β"}</div>
<div>νμ€ν 리: {stats.historyIndex + 1}κ°</div>
</div>
);
};
return (
<DiagramProvider width={800} height={600}>
{/* Draggable boxes that support undo/redo */}
<DraggableBox
id="box1"
initialX={100}
initialY={100}
width={120}
height={80}
title="λλκ·Έν΄λ³΄μΈμ"
color="blue"
/>
<DraggableBox id="box2" initialX={300} initialY={200} width={120} height={80} title="λ°μ€ 2" color="green" />
{/* Undo/Redo Buttons with various styles */}
<UndoRedoButtons position="top-right" variant="gradient" showLabels={true} enableKeyboardShortcuts={true} />
{/* Custom styled undo/redo buttons */}
<UndoRedoButtons
position="bottom-right"
customStyle={{
undo: "bg-red-500 hover:bg-red-600 text-white shadow-lg rounded-full",
redo: "bg-green-500 hover:bg-green-600 text-white shadow-lg rounded-full",
}}
customLabels={{ undo: "λλ리기", redo: "μμΌλ‘" }}
/>
<DebugInfo />
</DiagramProvider>
);
}
```
```jsx
import React from "react";
import {
DiagramProvider,
Box,
Connector,
DraggableBox,
Triangle,
Valve,
Arrow,
Line,
ImageBox,
Sankey,
useDiagram,
} from "sweet-diagram";
function MyDiagram() {
return (
<div className="w-full h-full absolute">
<DiagramProvider width={800} height={600}>
<Box
id="box1"
x={100}
y={100}
width={120}
height={80}
text="Start Point"
className="bg-blue-500 text-white border-blue-600 border-2 rounded-lg"
onClick={(event, data) => console.log("Box clicked:", data)}
/>
<Box
id="box2"
x={300}
y={200}
width={120}
height={80}
text="End Point"
className="bg-green-500 text-white border-green-600 border-2 rounded-lg"
/>
{/* Vertical Text Box */}
<Box
id="vertical-box"
x={500}
y={100}
width={60}
height={120}
text="Vertical Text"
textDirection="vertical"
verticalDirection="lr"
className="bg-purple-500 text-white border-purple-600 border-2 rounded-lg"
/>
<Connector
fromBox={{ id: "box1", position: "right" }}
toBox={{ id: "box2", position: "left" }}
connectionType="straight"
arrowDirection="forward"
strokeWidth={3}
className="text-black"
animated={true}
/>
<DraggableBox
id="draggable1"
initialX={500}
initialY={100}
width={100}
height={60}
title="Draggable"
color="purple"
onDrag={(position) => console.log("New position:", position)}
/>
<Triangle x={200} y={300} size={30} color="#ff6b6b" onClick={() => console.log("Triangle clicked")} />
<Valve x={400} y={150} size={25} isOpen={true} onClick={() => console.log("Valve clicked")} />
</DiagramProvider>
</div>
);
}
export default MyDiagram;
```
- **`DiagramProvider`** - Main context provider
- **`Box`** - Basic diagram box with text support
- **`Connector`** - Connection lines between components
- **`DraggableBox`** - Draggable box component
- **`Arrow`** - Arrow shapes and indicators
- **`Line`** - Simple line connections
- **`Triangle`** - Triangle shapes
- **`Valve`** - Valve indicators
- **`ImageBox`** - Image containers
- **`Sankey`** - Flow diagrams with proportional node heights
- **Stack Layout** - Automatic box stacking with priority system
- **`useDiagram`** - Access diagram context and state
Make sure your `package.json` includes:
```json
{
"dependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0",
"sweet-diagram": "^0.4.6"
},
"peerDependencies": {
"tailwindcss": "^3.0.0"
}
}
```
Sweet Diagram is designed to work perfectly with TailwindCSS. Add this to your `tailwind.config.js`:
```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}", "./node_modules/sweet-diagram/**/*.{js,jsx}"],
theme: {
extend: {},
},
plugins: [],
};
```
Visit our [comprehensive documentation](https://sweetpotato-diagram.vercel.app) for:
- π Complete API reference
- π― Interactive examples
- π¨ Styling guides
- β‘ Performance tips
- π οΈ Advanced usage patterns
Full TypeScript definitions are included:
```typescript
import { BoxProps, ConnectorProps, SankeyData, DiagramContextType } from "sweet-diagram";
```
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
MIT License - see [LICENSE](LICENSE) file for details.
Give us a βοΈ if this project helped you!
---
**Made with β€οΈ by the Sweet Diagram Team**