Skip to content

Commit da509c1

Browse files
authored
refactor: Split OverLayer to several Layers (#49)
1 parent 096543a commit da509c1

File tree

23 files changed

+1442
-884
lines changed

23 files changed

+1442
-884
lines changed

docs/editing.md

Lines changed: 0 additions & 70 deletions
This file was deleted.

docs/layers.md

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Layers
2+
3+
## Core Concepts
4+
5+
Layers are essentially plugins that extend the graph library's functionality. Each layer can have its own canvas and/or HTML elements, allowing for flexible rendering and interaction capabilities. Layers are designed to:
6+
- Modify behavior of components
7+
- Add new features
8+
- Intercept events
9+
- Interact with other layers
10+
11+
The primary purpose of layers is to provide a modular way to extend the library's behavior without modifying its core code.
12+
13+
## Built-in Layers
14+
15+
The library includes several built-in layers that demonstrate how to extend functionality:
16+
17+
### NewBlockLayer
18+
19+
[NewBlockLayer](../src/components/canvas/layers/newBlockLayer/NewBlockLayer.md) is a good example that extends the library by enabling block creation:
20+
- Clone blocks with Alt + drag
21+
- Customize ghost block appearance
22+
- Handle block creation events
23+
24+
```typescript
25+
// Key events
26+
interface GraphEventsDefinitions {
27+
"block-add-start-from-shadow": (event: CustomEvent<{ block: Block }>) => void;
28+
"block-added-from-shadow": (event: CustomEvent<{ block: Block; coord: TPoint }>) => void;
29+
}
30+
```
31+
32+
### ConnectionLayer
33+
34+
[ConnectionLayer](../src/components/canvas/layers/connectionLayer/ConnectionLayer.md) is another usefull extension that manages connections:
35+
- Create block-to-block connections (Shift + drag)
36+
- Create anchor-to-anchor connections
37+
- Customize connection appearance
38+
39+
```typescript
40+
// Key events
41+
interface GraphEventsDefinitions {
42+
"connection-create-start": (event: CustomEvent<{ blockId: TBlockId; anchorId: string | undefined }>) => void;
43+
"connection-created": (event: CustomEvent<{
44+
sourceBlockId: TBlockId;
45+
sourceAnchorId?: string;
46+
targetBlockId: TBlockId;
47+
targetAnchorId?: string
48+
}>) => void;
49+
}
50+
```
51+
52+
## Using Layers
53+
54+
```typescript
55+
// Attach when creating a graph
56+
const graph = new Graph({
57+
layers: [
58+
[NewBlockLayer, { ghostBackground: "rgba(0, 0, 255, 0.3)" }],
59+
[ConnectionLayer, {}]
60+
]
61+
});
62+
63+
// Or add dynamically
64+
const newBlockLayer = graph.addLayer(NewBlockLayer, { ghostBackground: "rgba(0, 0, 255, 0.3)" });
65+
```
66+
67+
## Creating Custom Layers
68+
69+
### Basic Structure
70+
71+
Each layer can have its own canvas and/or HTML elements for rendering and interaction:
72+
73+
```typescript
74+
import { Layer, LayerContext, LayerProps } from "./services/Layer";
75+
76+
interface MyLayerProps extends LayerProps {
77+
customOption?: string;
78+
}
79+
80+
export class MyLayer extends Layer<MyLayerProps> {
81+
constructor(props: MyLayerProps) {
82+
super({
83+
// Canvas element for drawing
84+
canvas: {
85+
zIndex: 10,
86+
classNames: ["my-layer"]
87+
},
88+
// HTML element for DOM-based interactions
89+
html: {
90+
zIndex: 10,
91+
classNames: ["my-layer-html"],
92+
transformByCameraPosition: true
93+
},
94+
...props
95+
});
96+
97+
this.setContext({
98+
canvas: this.getCanvas(),
99+
ctx: this.getCanvas().getContext("2d"),
100+
camera: props.camera,
101+
graph: this.props.graph
102+
});
103+
104+
// Subscribe to events
105+
this.context.graph.on("camera-change", this.performRender);
106+
}
107+
108+
// Rendering method
109+
protected render() {
110+
const { ctx } = this.context;
111+
ctx.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
112+
// Custom rendering code
113+
}
114+
115+
// Resource cleanup
116+
protected unmount(): void {
117+
this.context.graph.off("camera-change", this.performRender);
118+
}
119+
}
120+
```
121+
122+
### Built-in CSS Classes
123+
124+
The library provides several built-in CSS classes that can be used with layers:
125+
126+
- **no-pointer-events**: Disables pointer events on the layer, making it transparent to mouse interactions. Useful for layers that only render visual elements but shouldn't capture mouse events.
127+
128+
```typescript
129+
canvas: {
130+
zIndex: 4,
131+
classNames: ["no-pointer-events"]
132+
}
133+
```
134+
135+
- **no-user-select**: Prevents text selection on the layer. Useful for layers with text content that shouldn't be selectable.
136+
137+
```typescript
138+
html: {
139+
zIndex: 5,
140+
classNames: ["no-user-select"]
141+
}
142+
```
143+
144+
- **layer-with-camera**: Automatically applied when `transformByCameraPosition: true` is set. Makes the HTML layer transform according to camera movements.
145+
146+
You can combine these classes as needed:
147+
148+
```typescript
149+
html: {
150+
zIndex: 3,
151+
classNames: ["my-custom-layer", "no-pointer-events", "no-user-select"]
152+
}
153+
```
154+
155+
### Layer Interaction
156+
157+
```typescript
158+
// Access another layer
159+
const connectionLayer = this.context.graph.layers.find(layer => layer instanceof ConnectionLayer);
160+
161+
// Use events
162+
this.context.graph.on("connection-created", (event) => {
163+
// React to connection creation
164+
});
165+
```
166+
167+
### Example: Grid Layer
168+
169+
```typescript
170+
export class GridLayer extends Layer {
171+
172+
protected ctx: CanvasRenderingContext2D;
173+
174+
constructor(props) {
175+
super({
176+
canvas: { zIndex: 1, classNames: ["grid-layer"] },
177+
...props
178+
});
179+
180+
this.ctx = this.getCanvas().getContext("2d");
181+
182+
this.context.graph.on("camera-change", this.performRender);
183+
}
184+
185+
protected render() {
186+
const { ctx, camera } = this.context;
187+
const { width, height, scale, x, y } = camera.getCameraState();
188+
189+
ctx.clearRect(0, 0, width, height);
190+
191+
// Draw grid
192+
ctx.beginPath();
193+
ctx.strokeStyle = "rgba(0, 0, 0, 0.1)";
194+
195+
const gridSize = 20 * scale;
196+
const offsetX = x % gridSize;
197+
const offsetY = y % gridSize;
198+
199+
for (let i = offsetX; i < width; i += gridSize) {
200+
ctx.moveTo(i, 0);
201+
ctx.lineTo(i, height);
202+
}
203+
204+
for (let i = offsetY; i < height; i += gridSize) {
205+
ctx.moveTo(0, i);
206+
ctx.lineTo(width, i);
207+
}
208+
209+
ctx.stroke();
210+
}
211+
212+
protected unmount(): void {
213+
this.context.graph.off("camera-change", this.performRender);
214+
}
215+
}
216+
```
217+

0 commit comments

Comments
 (0)