@bemedev/mind-flow 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/lib/helpers/createContext.d.ts +7 -2
- package/lib/helpers/createContext.d.ts.map +1 -1
- package/lib/index.cjs.js +1 -1
- package/lib/index.es.js +556 -510
- package/lib/output.css +1 -1
- package/lib/server.cjs.js +653 -397
- package/lib/server.es.js +657 -401
- package/lib/services/main.machine.d.ts +4857 -218
- package/lib/services/main.machine.d.ts.map +1 -1
- package/lib/{ui/components/FlowChart.data.d.ts → services/main.machine.data.d.ts} +8 -3
- package/lib/services/main.machine.data.d.ts.map +1 -0
- package/lib/services/main.machine.helpers.d.ts +32 -0
- package/lib/services/main.machine.helpers.d.ts.map +1 -0
- package/lib/services/main.machine.typings.d.ts +126 -0
- package/lib/services/main.machine.typings.d.ts.map +1 -0
- package/lib/ui/Flow.d.ts +2 -0
- package/lib/ui/Flow.d.ts.map +1 -1
- package/lib/ui/components/Bounds.d.ts +2 -0
- package/lib/ui/components/Bounds.d.ts.map +1 -1
- package/lib/ui/components/EdgeComponent.d.ts +3 -2
- package/lib/ui/components/EdgeComponent.d.ts.map +1 -1
- package/lib/ui/components/EdgesBoard.d.ts +2 -0
- package/lib/ui/components/EdgesBoard.d.ts.map +1 -1
- package/lib/ui/components/FlowChart.context.d.ts +5608 -996
- package/lib/ui/components/FlowChart.context.d.ts.map +1 -1
- package/lib/ui/components/FlowChart.d.ts +3 -1
- package/lib/ui/components/FlowChart.d.ts.map +1 -1
- package/lib/ui/components/NodeComponent.d.ts +2 -10
- package/lib/ui/components/NodeComponent.d.ts.map +1 -1
- package/lib/ui/components/NodesBoard.d.ts +2 -0
- package/lib/ui/components/NodesBoard.d.ts.map +1 -1
- package/package.json +9 -15
- package/src/helpers/createContext.ts +7 -2
- package/src/{ui/components/FlowChart.data.ts → services/main.machine.data.ts} +7 -2
- package/src/services/main.machine.helpers.ts +55 -0
- package/src/services/main.machine.ts +288 -70
- package/src/services/main.machine.typings.ts +98 -0
- package/src/ui/Flow.tsx +2 -0
- package/src/ui/components/Bounds.tsx +13 -8
- package/src/ui/components/EdgeComponent.tsx +78 -56
- package/src/ui/components/EdgesBoard.tsx +13 -36
- package/src/ui/components/FlowChart.context.ts +204 -291
- package/src/ui/components/FlowChart.tsx +16 -16
- package/src/ui/components/NodeComponent.tsx +60 -104
- package/src/ui/components/NodesBoard.tsx +85 -47
- package/src/ui/components/classes.ts +1 -1
- package/lib/services/main.typings.d.ts +0 -68
- package/lib/services/main.typings.d.ts.map +0 -1
- package/lib/ui/components/FlowChart.data.d.ts.map +0 -1
- package/src/services/main.typings.ts +0 -50
|
@@ -1,347 +1,260 @@
|
|
|
1
1
|
import { interpret } from '@bemedev/app';
|
|
2
|
-
import { dequal } from 'dequal/lite';
|
|
3
|
-
import { createSignal } from 'solid-js';
|
|
4
|
-
import { produce } from 'solid-js/store';
|
|
5
2
|
|
|
6
|
-
import { createContext } from '
|
|
7
|
-
import { machine } from '
|
|
8
|
-
import type { Point, Vector } from '../../services/main.typings';
|
|
3
|
+
import { createContext } from '#helpers/createContext';
|
|
4
|
+
import { machine } from '#main-machine';
|
|
9
5
|
import {
|
|
10
6
|
BOUNDS_CONSTRAINTS,
|
|
11
7
|
DEFAULT_INPUT_OFFSET,
|
|
12
8
|
getDefaultOutputOffset,
|
|
13
9
|
PARENT_CHILD_GAP_WIDTH,
|
|
14
|
-
} from '
|
|
15
|
-
|
|
16
|
-
/** Layout dimensions and handle offset coordinates for a flowchart node. */
|
|
17
|
-
type Dimensions = {
|
|
18
|
-
/** Node width in pixels. */
|
|
19
|
-
width: number;
|
|
20
|
-
/** Node height in pixels. */
|
|
21
|
-
height: number;
|
|
22
|
-
/** Output handle point coordinates of type {@linkcode Point}. */
|
|
23
|
-
output: Point;
|
|
24
|
-
/** Input handle point coordinates of type {@linkcode Point}. */
|
|
25
|
-
input?: Point;
|
|
26
|
-
/** Output handle relative offset point of type {@linkcode Point}. */
|
|
27
|
-
outputOffset?: Point;
|
|
28
|
-
/** Input handle relative offset point of type {@linkcode Point}. */
|
|
29
|
-
inputOffset?: Point;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/** Connection edge coordinate representation between two endpoints. */
|
|
33
|
-
type Edge = {
|
|
34
|
-
/** Source node identifier. */
|
|
35
|
-
from: string;
|
|
36
|
-
} & Vector;
|
|
10
|
+
} from '#services/main.machine.data';
|
|
11
|
+
import type { Dimension, Point } from '#services/main.machine.typings';
|
|
37
12
|
|
|
38
13
|
/**
|
|
39
14
|
* Solid Context Provider component and hook for accessing flowchart board state,
|
|
40
15
|
* services, and zoom.
|
|
16
|
+
*
|
|
17
|
+
* @see {@linkcode machine}, {@linkcode createContext}
|
|
41
18
|
*/
|
|
42
19
|
export const [Provider, useFlow] = createContext(
|
|
43
20
|
() => {
|
|
44
|
-
/**
|
|
45
|
-
* Shared state machine interpreter service instance for flowchart state
|
|
46
|
-
* management.
|
|
47
|
-
*/
|
|
21
|
+
/** Shared service for flowchart state management. */
|
|
48
22
|
const service = interpret(machine, {
|
|
49
|
-
context: {},
|
|
50
|
-
pContext: {
|
|
23
|
+
context: { zoom: 1, edgesPositions: {}, bounds: { x: 0, y: 0 } },
|
|
24
|
+
pContext: {
|
|
25
|
+
generatedId: null,
|
|
26
|
+
dimensions: {},
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Converts viewport screen coordinates to unscaled board coordinates.
|
|
30
|
+
*
|
|
31
|
+
* @param clientX - Client X coordinate in pixels.
|
|
32
|
+
* @param clientY - Client Y coordinate in pixels.
|
|
33
|
+
* @param el - Board layout details of type {@linkcode Board}.
|
|
34
|
+
*
|
|
35
|
+
* @returns Calculated 2D coordinate of type {@linkcode Point}.
|
|
36
|
+
*/
|
|
37
|
+
getBoardPosition(clientX, clientY, el): Point {
|
|
38
|
+
if (!el) return { x: clientX, y: clientY };
|
|
39
|
+
const currentZoom = service.state.context.zoom;
|
|
40
|
+
const x = (clientX - el.self.left) / currentZoom;
|
|
41
|
+
const y = (clientY - el.self.top) / currentZoom;
|
|
42
|
+
return { x, y };
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Clamps given coordinates within board boundaries.
|
|
47
|
+
*
|
|
48
|
+
* @param board - Board layout and container details of type
|
|
49
|
+
* {@linkcode Board}.
|
|
50
|
+
* @param x - Desired X position in pixels.
|
|
51
|
+
* @param y - Desired Y position in pixels.
|
|
52
|
+
* @param nodeWidth - Node width in pixels, defaults to `192`.
|
|
53
|
+
* @param nodeHeight - Node height in pixels, defaults to `50`.
|
|
54
|
+
*
|
|
55
|
+
* @returns Clamped coordinate of type {@linkcode Point}.
|
|
56
|
+
*
|
|
57
|
+
* @see {@linkcode BOUNDS_CONSTRAINTS}
|
|
58
|
+
*/
|
|
59
|
+
clampPosition(board, x, y, nodeWidth = 192, nodeHeight = 50): Point {
|
|
60
|
+
const container = board?.parent;
|
|
61
|
+
if (!container) return { x, y };
|
|
62
|
+
|
|
63
|
+
const currentZoom = service.state.context.zoom ?? 1;
|
|
64
|
+
|
|
65
|
+
const minX =
|
|
66
|
+
container.scrollLeft / currentZoom + BOUNDS_CONSTRAINTS.horizontal;
|
|
67
|
+
|
|
68
|
+
const maxX = Math.max(
|
|
69
|
+
minX,
|
|
70
|
+
(container.scrollLeft + container.width) / currentZoom -
|
|
71
|
+
BOUNDS_CONSTRAINTS.horizontal -
|
|
72
|
+
nodeWidth,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const minY =
|
|
76
|
+
container.scrollTop / currentZoom + BOUNDS_CONSTRAINTS.vertical;
|
|
77
|
+
|
|
78
|
+
const maxY = Math.max(
|
|
79
|
+
minY,
|
|
80
|
+
(container.scrollTop + container.height) / currentZoom -
|
|
81
|
+
BOUNDS_CONSTRAINTS.vertical -
|
|
82
|
+
nodeHeight,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
x: Math.min(Math.max(x, minX), maxX),
|
|
87
|
+
y: Math.min(Math.max(y, minY), maxY),
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Computes node dimension, connection points, and handle offsets.
|
|
93
|
+
*
|
|
94
|
+
* @param position - Node position of type {@linkcode Point}.
|
|
95
|
+
* @param parentDimension - Optional sizing and offsets to inherit.
|
|
96
|
+
*
|
|
97
|
+
* @returns Calculated dimension object of type {@linkcode Dimension}.
|
|
98
|
+
*
|
|
99
|
+
* @see {@linkcode DEFAULT_INPUT_OFFSET}, {@linkcode getDefaultOutputOffset}
|
|
100
|
+
*/
|
|
101
|
+
calculateDimensions: (
|
|
102
|
+
position: Point,
|
|
103
|
+
parentDimension: Pick<
|
|
104
|
+
Dimension,
|
|
105
|
+
'width' | 'height' | 'outputOffset' | 'inputOffset'
|
|
106
|
+
> = { width: 192, height: 50, inputOffset: DEFAULT_INPUT_OFFSET },
|
|
107
|
+
): Dimension => {
|
|
108
|
+
const width = parentDimension.width;
|
|
109
|
+
const height = parentDimension.height;
|
|
110
|
+
const outputOffset =
|
|
111
|
+
parentDimension.outputOffset ?? getDefaultOutputOffset(width);
|
|
112
|
+
const inputOffset = parentDimension.inputOffset!;
|
|
113
|
+
|
|
114
|
+
const output = {
|
|
115
|
+
x: position.x + outputOffset.x,
|
|
116
|
+
y: position.y + outputOffset.y,
|
|
117
|
+
};
|
|
118
|
+
const input = {
|
|
119
|
+
x: position.x + inputOffset.x,
|
|
120
|
+
y: position.y + inputOffset.y,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return { width, height, output, input, outputOffset, inputOffset };
|
|
124
|
+
},
|
|
125
|
+
},
|
|
51
126
|
});
|
|
52
127
|
|
|
53
|
-
|
|
54
|
-
const newEdge = createSignal<Edge>();
|
|
55
|
-
const [boardRef, setBoardRef] = createSignal<HTMLDivElement>();
|
|
56
|
-
|
|
57
|
-
const [dimensions, setDimensions] = createSignal<Record<string, Dimensions>>(
|
|
58
|
-
{},
|
|
59
|
-
{ equals: dequal },
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const getBoardPoint = (clientX: number, clientY: number): Point => {
|
|
63
|
-
const el = boardRef();
|
|
64
|
-
if (!el) return { x: clientX, y: clientY };
|
|
65
|
-
|
|
66
|
-
const rect = el.getBoundingClientRect();
|
|
67
|
-
const currentZoom = zoom[0]();
|
|
68
|
-
const x = clientX - rect.left / currentZoom;
|
|
69
|
-
const y = clientY - rect.top / currentZoom;
|
|
70
|
-
return { x, y };
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const [edgesPositions, setEdgesPositions] = createSignal<Record<string, Vector>>(
|
|
74
|
-
{},
|
|
75
|
-
{ equals: false },
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
const clampPosition = (
|
|
79
|
-
x: number,
|
|
80
|
-
y: number,
|
|
81
|
-
nodeWidth = 192,
|
|
82
|
-
nodeHeight = 50,
|
|
83
|
-
): Point => {
|
|
84
|
-
const container = boardRef()?.parentElement;
|
|
85
|
-
if (!container) return { x, y };
|
|
86
|
-
|
|
87
|
-
const currentZoom = zoom[0]();
|
|
88
|
-
|
|
89
|
-
const minX =
|
|
90
|
-
container.scrollLeft / currentZoom + BOUNDS_CONSTRAINTS.horizontal;
|
|
91
|
-
|
|
92
|
-
const maxX = Math.max(
|
|
93
|
-
minX,
|
|
94
|
-
(container.scrollLeft + container.clientWidth) / currentZoom -
|
|
95
|
-
BOUNDS_CONSTRAINTS.horizontal -
|
|
96
|
-
nodeWidth,
|
|
97
|
-
);
|
|
128
|
+
// #endregion
|
|
98
129
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const maxY = Math.max(
|
|
102
|
-
minY,
|
|
103
|
-
(container.scrollTop + container.clientHeight) / currentZoom -
|
|
104
|
-
BOUNDS_CONSTRAINTS.vertical -
|
|
105
|
-
nodeHeight,
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
x: Math.min(Math.max(x, minX), maxX),
|
|
110
|
-
y: Math.min(Math.max(y, minY), maxY),
|
|
111
|
-
};
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
service.addOptions(({ voidAction, batch, assign }) => ({
|
|
130
|
+
service.addOptions(({ assign }) => ({
|
|
115
131
|
actions: {
|
|
116
|
-
placeChild: assign('
|
|
117
|
-
ADD_CHILD: ({ payload, context: { data }, pContext
|
|
118
|
-
|
|
132
|
+
placeChild: assign('data.nodes', {
|
|
133
|
+
ADD_CHILD: ({ payload, context: { data, board }, pContext }) => {
|
|
134
|
+
if (!board) return data?.nodes;
|
|
135
|
+
const nodes = data?.nodes;
|
|
119
136
|
if (!payload) return nodes;
|
|
120
137
|
|
|
121
|
-
const parentNode = nodes
|
|
138
|
+
const parentNode = nodes?.find(node => node.id === payload);
|
|
122
139
|
if (!parentNode) return nodes;
|
|
123
140
|
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
const
|
|
141
|
+
const parentDimension = pContext.dimensions[payload];
|
|
142
|
+
|
|
143
|
+
const id = `node-${pContext.generatedId}`;
|
|
144
|
+
const width = parentDimension?.width ?? 192;
|
|
145
|
+
const height = parentDimension?.height ?? 50;
|
|
127
146
|
const initialX = parentNode.position.x + width + PARENT_CHILD_GAP_WIDTH;
|
|
128
147
|
const initialY = parentNode.position.y;
|
|
129
|
-
const position = clampPosition(
|
|
148
|
+
const position = pContext.clampPosition(
|
|
149
|
+
board,
|
|
150
|
+
initialX,
|
|
151
|
+
initialY,
|
|
152
|
+
width,
|
|
153
|
+
height,
|
|
154
|
+
);
|
|
155
|
+
const dimension = pContext.calculateDimensions(
|
|
156
|
+
position,
|
|
157
|
+
parentDimension,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
nodes?.push({
|
|
161
|
+
id,
|
|
162
|
+
data: { content: '<Nouveau nœud>' },
|
|
163
|
+
input: true,
|
|
164
|
+
position,
|
|
165
|
+
});
|
|
166
|
+
pContext.dimensions[id] = dimension;
|
|
130
167
|
|
|
131
|
-
return
|
|
132
|
-
...nodes,
|
|
133
|
-
{ id, data: { content: '<Nouveau nœud>' }, input: true, position },
|
|
134
|
-
];
|
|
168
|
+
return nodes;
|
|
135
169
|
},
|
|
136
170
|
}),
|
|
137
171
|
|
|
138
|
-
placeParent: assign('
|
|
139
|
-
ADD_PARENT: ({ context: { data
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
const
|
|
172
|
+
placeParent: assign('data.nodes', {
|
|
173
|
+
ADD_PARENT: ({ context: { data, zoom = 1, board }, pContext }) => {
|
|
174
|
+
if (!board) return data?.nodes;
|
|
175
|
+
const nodes = data?.nodes;
|
|
176
|
+
const id = `node-${pContext.generatedId}`;
|
|
177
|
+
const container = board.parent;
|
|
143
178
|
const scrollLeft = container?.scrollLeft ?? 0;
|
|
144
179
|
const scrollTop = container?.scrollTop ?? 0;
|
|
145
|
-
const width = container?.
|
|
146
|
-
const height = container?.
|
|
147
|
-
const currentZoom = zoom
|
|
180
|
+
const width = container?.width ?? 0;
|
|
181
|
+
const height = container?.height ?? 0;
|
|
182
|
+
const currentZoom = zoom;
|
|
148
183
|
const x = (scrollLeft + width / 2) / currentZoom;
|
|
149
184
|
const y = (scrollTop + height / 2) / currentZoom;
|
|
185
|
+
const position = pContext.clampPosition(board, x, y);
|
|
186
|
+
const dimension = pContext.calculateDimensions(position);
|
|
187
|
+
|
|
188
|
+
nodes?.push({
|
|
189
|
+
id,
|
|
190
|
+
data: { content: '<Nouveau nœud>' },
|
|
191
|
+
input: false,
|
|
192
|
+
position,
|
|
193
|
+
});
|
|
150
194
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
{
|
|
154
|
-
id,
|
|
155
|
-
data: { content: '<Nouveau nœud>' },
|
|
156
|
-
input: false,
|
|
157
|
-
position: { x, y },
|
|
158
|
-
},
|
|
159
|
-
];
|
|
195
|
+
pContext.dimensions[id] = dimension;
|
|
196
|
+
return nodes;
|
|
160
197
|
},
|
|
161
198
|
}),
|
|
162
199
|
|
|
163
|
-
placeSibling: assign('
|
|
164
|
-
ADD_SIBLING: ({
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const edges = data?.edges ?? [];
|
|
170
|
-
const nodes = data?.nodes ?? [];
|
|
171
|
-
const parentID = edges.find(edge => edge.to === payload)?.from;
|
|
200
|
+
placeSibling: assign('data.nodes', {
|
|
201
|
+
ADD_SIBLING: ({ payload, context: { data, board }, pContext }) => {
|
|
202
|
+
if (!board) return data?.nodes;
|
|
203
|
+
const edges = data?.edges;
|
|
204
|
+
const nodes = data?.nodes;
|
|
205
|
+
const parentID = edges?.find(edge => edge.to === payload)?.from;
|
|
172
206
|
if (!parentID) return nodes;
|
|
173
207
|
|
|
174
|
-
const parentNode = nodes
|
|
208
|
+
const parentNode = nodes?.find(node => node.id === parentID);
|
|
175
209
|
if (!parentNode) return nodes;
|
|
176
210
|
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
const
|
|
211
|
+
const parentDimension = pContext.dimensions[parentID];
|
|
212
|
+
const id = `node-${pContext.generatedId}`;
|
|
213
|
+
const width = parentDimension?.width ?? 192;
|
|
214
|
+
const height = parentDimension?.height ?? 50;
|
|
180
215
|
const initialX = parentNode.position.x + width + PARENT_CHILD_GAP_WIDTH;
|
|
181
|
-
const initialY = parentNode.position.y +
|
|
182
|
-
const position = clampPosition(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const edges = data?.edges ?? [];
|
|
194
|
-
setEdgesPositions(data => {
|
|
195
|
-
const array = Object.entries({ ...data }).filter(([id]) =>
|
|
196
|
-
edges.some(edge => edge.id === id),
|
|
197
|
-
);
|
|
216
|
+
const initialY = parentNode.position.y + PARENT_CHILD_GAP_WIDTH;
|
|
217
|
+
const position = pContext.clampPosition(
|
|
218
|
+
board,
|
|
219
|
+
initialX,
|
|
220
|
+
initialY,
|
|
221
|
+
width,
|
|
222
|
+
height,
|
|
223
|
+
);
|
|
224
|
+
const dimension = pContext.calculateDimensions(
|
|
225
|
+
position,
|
|
226
|
+
parentDimension,
|
|
227
|
+
);
|
|
198
228
|
|
|
199
|
-
|
|
229
|
+
nodes?.push({
|
|
230
|
+
id,
|
|
231
|
+
data: { content: '<Nouveau nœud>' },
|
|
232
|
+
input: true,
|
|
233
|
+
position,
|
|
200
234
|
});
|
|
201
|
-
}),
|
|
202
|
-
voidAction({
|
|
203
|
-
else: ({ context: { data } }) => {
|
|
204
|
-
const edges = data?.edges ?? [];
|
|
205
|
-
setEdgesPositions(
|
|
206
|
-
produce(next => {
|
|
207
|
-
edges?.forEach(({ from, id, to }) => {
|
|
208
|
-
const output = dimensions()[from]?.output;
|
|
209
|
-
const input = dimensions()[to]?.input;
|
|
210
|
-
|
|
211
|
-
if (output && input) {
|
|
212
|
-
next[id] = {
|
|
213
|
-
x0: output.x,
|
|
214
|
-
y0: output.y,
|
|
215
|
-
x1: input.x,
|
|
216
|
-
y1: input.y,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
}),
|
|
221
|
-
);
|
|
222
|
-
},
|
|
223
|
-
MOVE: ({ context: { data }, payload }) => {
|
|
224
|
-
const edges = data?.edges ?? [];
|
|
225
|
-
|
|
226
|
-
setEdgesPositions(
|
|
227
|
-
produce(next => {
|
|
228
|
-
edges?.forEach(({ from, to, id }) => {
|
|
229
|
-
if (from === payload.id) {
|
|
230
|
-
const offset =
|
|
231
|
-
dimensions()[payload.id]?.outputOffset ??
|
|
232
|
-
getDefaultOutputOffset(dimensions()[payload.id]?.width);
|
|
233
|
-
|
|
234
|
-
const x0 = payload.x + offset.x;
|
|
235
|
-
const y0 = payload.y + offset.y;
|
|
236
|
-
next[id] = { ...next[id], x0, y0 };
|
|
237
|
-
|
|
238
|
-
setDimensions(
|
|
239
|
-
produce(data => {
|
|
240
|
-
if (data[payload.id]) {
|
|
241
|
-
data[payload.id] = {
|
|
242
|
-
...data[payload.id],
|
|
243
|
-
output: { x: x0, y: y0 },
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
}),
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
if (to === payload.id) {
|
|
250
|
-
const offset =
|
|
251
|
-
dimensions()[payload.id]?.inputOffset ??
|
|
252
|
-
DEFAULT_INPUT_OFFSET;
|
|
253
|
-
|
|
254
|
-
const x1 = payload.x + offset.x;
|
|
255
|
-
const y1 = payload.y + offset.y;
|
|
256
|
-
next[id] = { ...next[id], x1, y1 };
|
|
257
235
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
...data[payload.id],
|
|
263
|
-
input: { x: x1, y: y1 },
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
}),
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
}),
|
|
271
|
-
);
|
|
272
|
-
},
|
|
273
|
-
}),
|
|
274
|
-
|
|
275
|
-
assign('context.updatingUI', () => true),
|
|
276
|
-
),
|
|
277
|
-
|
|
278
|
-
buildImmediateUI: voidAction({
|
|
279
|
-
MOVE_IMMEDIATE: ({ context: { data }, payload }) => {
|
|
280
|
-
const edges = data?.edges ?? [];
|
|
281
|
-
|
|
282
|
-
setEdgesPositions(
|
|
283
|
-
produce(next => {
|
|
284
|
-
edges?.forEach(({ from, to, id }) => {
|
|
285
|
-
if (from === payload.id) {
|
|
286
|
-
const offset =
|
|
287
|
-
dimensions()[payload.id]?.outputOffset ??
|
|
288
|
-
getDefaultOutputOffset(dimensions()[payload.id]?.width);
|
|
289
|
-
|
|
290
|
-
const x0 = payload.x + offset.x;
|
|
291
|
-
const y0 = payload.y + offset.y;
|
|
292
|
-
next[id] = { ...next[id], x0, y0 };
|
|
293
|
-
|
|
294
|
-
setDimensions(
|
|
295
|
-
produce(data => {
|
|
296
|
-
if (data[payload.id]) {
|
|
297
|
-
data[payload.id] = {
|
|
298
|
-
...data[payload.id],
|
|
299
|
-
output: { x: x0, y: y0 },
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
}),
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (to === payload.id) {
|
|
307
|
-
const offset =
|
|
308
|
-
dimensions()[payload.id]?.inputOffset ?? DEFAULT_INPUT_OFFSET;
|
|
309
|
-
|
|
310
|
-
const x1 = payload.x + offset.x;
|
|
311
|
-
const y1 = payload.y + offset.y;
|
|
312
|
-
next[id] = { ...next[id], x1, y1 };
|
|
236
|
+
pContext.dimensions[id] = dimension;
|
|
237
|
+
return nodes;
|
|
238
|
+
},
|
|
239
|
+
}),
|
|
313
240
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}),
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
}),
|
|
241
|
+
moveNewEdge: assign('newEdge', {
|
|
242
|
+
MOVE_NEW_EDGE: ({ context: { newEdge, board }, payload, pContext }) => {
|
|
243
|
+
if (!board) return undefined;
|
|
244
|
+
if (!newEdge) return undefined;
|
|
245
|
+
const { x: x1, y: y1 } = pContext.getBoardPosition(
|
|
246
|
+
payload.x,
|
|
247
|
+
payload.y,
|
|
248
|
+
board,
|
|
327
249
|
);
|
|
250
|
+
return { ...newEdge, x1, y1 };
|
|
328
251
|
},
|
|
329
252
|
}),
|
|
330
253
|
},
|
|
331
254
|
}));
|
|
332
255
|
|
|
333
256
|
service.start();
|
|
334
|
-
|
|
335
|
-
return {
|
|
336
|
-
dimensions: [dimensions, setDimensions] as const,
|
|
337
|
-
newEdge,
|
|
338
|
-
board: [boardRef, setBoardRef] as const,
|
|
339
|
-
getBoardPoint,
|
|
340
|
-
edgesPositions: [edgesPositions, setEdgesPositions] as const,
|
|
341
|
-
service,
|
|
342
|
-
zoom,
|
|
343
|
-
clampPosition,
|
|
344
|
-
};
|
|
257
|
+
return service;
|
|
345
258
|
},
|
|
346
259
|
{ name: 'FlowContext' },
|
|
347
260
|
);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { createState } from '@bemedev/app-solidjs';
|
|
1
2
|
import type { inferT } from '@bemedev/app/typings';
|
|
2
3
|
import { Component, onCleanup, onMount } from 'solid-js';
|
|
3
4
|
|
|
4
|
-
import
|
|
5
|
+
import { DEFAULT_NODES } from '../../services/main.machine.data';
|
|
6
|
+
import type { edgeJSON, nodeJSON } from '../../services/main.machine.typings';
|
|
5
7
|
import { useFlow } from './FlowChart.context';
|
|
6
|
-
import { DEFAULT_NODES } from './FlowChart.data';
|
|
7
8
|
import { NodesBoard } from './NodesBoard';
|
|
8
9
|
|
|
9
10
|
/** Serialized node properties type inferred from schema {@linkcode nodeJSON}. */
|
|
@@ -58,44 +59,43 @@ export type FlowProps = {
|
|
|
58
59
|
* {@linkcode FlowProps}.
|
|
59
60
|
*
|
|
60
61
|
* @returns The rendered Solid component.
|
|
62
|
+
*
|
|
63
|
+
* @see {@linkcode NodesBoard}, {@linkcode useFlow}, {@linkcode DEFAULT_NODES}
|
|
61
64
|
*/
|
|
62
65
|
export const FlowChart: Component<FlowProps> = props => {
|
|
63
66
|
const primaryNodes = props.config?.nodes ?? DEFAULT_NODES;
|
|
64
67
|
const primaryEdges = props.config?.edges;
|
|
65
68
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
getBoardPoint,
|
|
70
|
-
} = useFlow();
|
|
69
|
+
const service = useFlow();
|
|
70
|
+
|
|
71
|
+
const hasNewEdge = createState(service, { selector: s => !!s.context.newEdge });
|
|
71
72
|
|
|
72
73
|
onMount(() => {
|
|
73
|
-
service.
|
|
74
|
+
service.resume();
|
|
74
75
|
service.send({
|
|
75
76
|
type: 'CONFIGURE',
|
|
76
77
|
payload: { nodes: primaryNodes, edges: primaryEdges ?? [] },
|
|
77
78
|
});
|
|
78
79
|
});
|
|
79
80
|
|
|
80
|
-
onCleanup(service.
|
|
81
|
+
onCleanup(service.pause);
|
|
81
82
|
|
|
82
83
|
return (
|
|
83
84
|
<div
|
|
84
85
|
class='relative h-full w-full'
|
|
85
|
-
onMouseUp={() =>
|
|
86
|
+
onMouseUp={() => service.send('CLEAR_NEW_EDGE')}
|
|
86
87
|
onMouseMove={event => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
88
|
+
service.send({
|
|
89
|
+
type: 'MOVE_NEW_EDGE',
|
|
90
|
+
payload: { x: event.clientX, y: event.clientY },
|
|
91
|
+
});
|
|
92
92
|
}}
|
|
93
93
|
style={{}}
|
|
94
94
|
>
|
|
95
95
|
<div
|
|
96
96
|
class='relative h-full w-full bg-white bg-size-[30px_30px]'
|
|
97
97
|
style={{
|
|
98
|
-
cursor:
|
|
98
|
+
cursor: hasNewEdge() ? 'inherit' : 'crosshair',
|
|
99
99
|
'background-image':
|
|
100
100
|
'radial-gradient(circle, #b8b8b8bf 1px, rgba(0, 0, 0, 0) 1px)',
|
|
101
101
|
}}
|