@bemedev/mind-flow 0.1.1 → 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 +645 -418
- package/lib/server.es.js +649 -422
- 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} +4 -2
- 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 +8 -8
- package/src/helpers/createContext.ts +7 -2
- package/src/{ui/components/FlowChart.data.ts → services/main.machine.data.ts} +3 -1
- 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 -310
- package/src/ui/components/FlowChart.tsx +16 -16
- package/src/ui/components/NodeComponent.tsx +60 -104
- package/src/ui/components/NodesBoard.tsx +74 -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
package/lib/server.es.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { createComponent, escape, ssr, ssrAttribute, ssrHydrationKey, ssrStyleProperty } from "solid-js/web";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { type } from "@bemedev/app/bemedev";
|
|
2
|
+
import { createState } from "@bemedev/app-solidjs";
|
|
3
|
+
import { For, Show, createContext, createEffect, createSignal, on, onCleanup, onMount, useContext } from "solid-js";
|
|
4
|
+
import { createMachine, interpret, toArray } from "@bemedev/app";
|
|
5
|
+
import { toArray as toArray$1, type } from "@bemedev/app/bemedev";
|
|
7
6
|
import { nanoid } from "nanoid";
|
|
8
|
-
import { useState } from "@bemedev/app-solidjs";
|
|
9
7
|
import { DragDropProvider, DragDropSensors, DragOverlay, createDraggable, useDragDropContext } from "@thisbeyond/solid-dnd";
|
|
10
|
-
import { dequal
|
|
8
|
+
import { dequal } from "dequal";
|
|
11
9
|
//#region src/ui/components/classes.ts
|
|
12
10
|
/**
|
|
13
11
|
* List of Tailwind CSS class names used across flowchart UI components to ensure
|
|
@@ -34,7 +32,7 @@ var CLASSES = [
|
|
|
34
32
|
"w-full",
|
|
35
33
|
"h-full",
|
|
36
34
|
"overflow-hidden",
|
|
37
|
-
"overflow-
|
|
35
|
+
"overflow-auto",
|
|
38
36
|
"h-[150vh]",
|
|
39
37
|
"w-[2160px]",
|
|
40
38
|
"bg-size-[30px_30px]",
|
|
@@ -94,6 +92,50 @@ var CLASSES = [
|
|
|
94
92
|
"cursor-crosshair",
|
|
95
93
|
"mt-3"
|
|
96
94
|
];
|
|
95
|
+
/** X-axis center offset in pixels from the node container edge. */
|
|
96
|
+
var HANDLE_CENTER_X_OFFSET = 10.5;
|
|
97
|
+
/** Default 2D offset coordinates for input connection handles. */
|
|
98
|
+
var DEFAULT_INPUT_OFFSET = {
|
|
99
|
+
x: -10.5,
|
|
100
|
+
y: 18
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Computes default output handle offset coordinates given a node width.
|
|
104
|
+
*
|
|
105
|
+
* @param width - Node width in pixels, defaults to `0`.
|
|
106
|
+
*
|
|
107
|
+
* @returns 2D offset coordinates for the output handle.
|
|
108
|
+
*
|
|
109
|
+
* @see {@linkcode HANDLE_CENTER_X_OFFSET}, {@linkcode HANDLE_CENTER_Y}
|
|
110
|
+
*/
|
|
111
|
+
var getDefaultOutputOffset = (width = 0) => ({
|
|
112
|
+
x: width + HANDLE_CENTER_X_OFFSET,
|
|
113
|
+
y: 18
|
|
114
|
+
});
|
|
115
|
+
/**
|
|
116
|
+
* Boundary padding constraints for node dragging and positioning within the
|
|
117
|
+
* viewport.
|
|
118
|
+
*/
|
|
119
|
+
var BOUNDS_CONSTRAINTS = {
|
|
120
|
+
horizontal: 55,
|
|
121
|
+
vertical: 40
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Default node items of type {@linkcode NodeProps} provided when no initial
|
|
125
|
+
* configuration is given.
|
|
126
|
+
*/
|
|
127
|
+
var DEFAULT_NODES = [{
|
|
128
|
+
id: "node-0",
|
|
129
|
+
data: {
|
|
130
|
+
content: "Some text",
|
|
131
|
+
label: "Root node"
|
|
132
|
+
},
|
|
133
|
+
input: false,
|
|
134
|
+
position: {
|
|
135
|
+
x: 350,
|
|
136
|
+
y: 100
|
|
137
|
+
}
|
|
138
|
+
}];
|
|
97
139
|
//#endregion
|
|
98
140
|
//#region src/helpers/createContext.ts
|
|
99
141
|
/**
|
|
@@ -105,8 +147,10 @@ var CLASSES = [
|
|
|
105
147
|
* `NonNullable<T>`.
|
|
106
148
|
* @param options - Optional context configuration of type {@linkcode Options}.
|
|
107
149
|
*
|
|
108
|
-
* @returns A tuple containing the Provider component of
|
|
150
|
+
* @returns A tuple containing the Provider component of type
|
|
109
151
|
* {@linkcode ParentComponent}, the accessor hook, and the Solid context object.
|
|
152
|
+
*
|
|
153
|
+
* @see {@linkcode createSolidContext}, {@linkcode useContext}
|
|
110
154
|
*/
|
|
111
155
|
var createContext$1 = (context, options) => {
|
|
112
156
|
const _context = createContext(context(), options);
|
|
@@ -122,7 +166,66 @@ var createContext$1 = (context, options) => {
|
|
|
122
166
|
];
|
|
123
167
|
};
|
|
124
168
|
//#endregion
|
|
125
|
-
//#region src/services/main.
|
|
169
|
+
//#region src/services/main.machine.helpers.ts
|
|
170
|
+
/**
|
|
171
|
+
* Constructs a unique edge identifier string from source and destination node IDs.
|
|
172
|
+
*
|
|
173
|
+
* @param out - The source node ID string.
|
|
174
|
+
* @param _in - The destination node ID string.
|
|
175
|
+
*
|
|
176
|
+
* @returns Formatted edge identifier string.
|
|
177
|
+
*/
|
|
178
|
+
var buildEdgeId = (out, _in) => {
|
|
179
|
+
return `edge = ${out} => ${_in}`;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Constructs a formatted node identifier string from a generated ID.
|
|
183
|
+
*
|
|
184
|
+
* @param generated - The generated unique ID string or `null`/`undefined`.
|
|
185
|
+
*
|
|
186
|
+
* @returns Formatted node identifier string.
|
|
187
|
+
*/
|
|
188
|
+
var buildNodeID = (generated) => {
|
|
189
|
+
return `node-${generated}`;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Calculates node dimensions, connection points, and handle offsets from position
|
|
193
|
+
* and optional parent dimension.
|
|
194
|
+
*
|
|
195
|
+
* @param position - Node position in board coordinates of type {@linkcode Point}.
|
|
196
|
+
* @param parentDimension - Optional parent node dimension of type
|
|
197
|
+
* {@linkcode Dimension} to inherit sizing and offsets from.
|
|
198
|
+
*
|
|
199
|
+
* @returns Node layout dimension object of type {@linkcode Dimension}.
|
|
200
|
+
*
|
|
201
|
+
* @see {@linkcode getDefaultOutputOffset}, {@linkcode DEFAULT_INPUT_OFFSET}
|
|
202
|
+
*/
|
|
203
|
+
var calculateDimensions = (position, parentDimension = {
|
|
204
|
+
width: 192,
|
|
205
|
+
height: 50,
|
|
206
|
+
inputOffset: DEFAULT_INPUT_OFFSET
|
|
207
|
+
}) => {
|
|
208
|
+
const width = parentDimension.width;
|
|
209
|
+
const height = parentDimension.height;
|
|
210
|
+
const outputOffset = parentDimension.outputOffset ?? getDefaultOutputOffset(width);
|
|
211
|
+
const inputOffset = parentDimension.inputOffset;
|
|
212
|
+
return {
|
|
213
|
+
width,
|
|
214
|
+
height,
|
|
215
|
+
output: {
|
|
216
|
+
x: position.x + outputOffset.x,
|
|
217
|
+
y: position.y + outputOffset.y
|
|
218
|
+
},
|
|
219
|
+
input: {
|
|
220
|
+
x: position.x + inputOffset.x,
|
|
221
|
+
y: position.y + inputOffset.y
|
|
222
|
+
},
|
|
223
|
+
outputOffset,
|
|
224
|
+
inputOffset
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/services/main.machine.typings.ts
|
|
126
229
|
/** Schema definition for 2D coordinates `(x, y)`. */
|
|
127
230
|
var point = type({
|
|
128
231
|
x: "number",
|
|
@@ -137,7 +240,11 @@ var extremities = type({
|
|
|
137
240
|
from: "string",
|
|
138
241
|
to: "string"
|
|
139
242
|
});
|
|
140
|
-
/**
|
|
243
|
+
/**
|
|
244
|
+
* Schema definition for a serialized flowchart node entity.
|
|
245
|
+
*
|
|
246
|
+
* @see {@linkcode point}
|
|
247
|
+
*/
|
|
141
248
|
var nodeJSON = type(({ optional, use }) => ({
|
|
142
249
|
position: use(point),
|
|
143
250
|
data: {
|
|
@@ -146,50 +253,68 @@ var nodeJSON = type(({ optional, use }) => ({
|
|
|
146
253
|
},
|
|
147
254
|
input: "boolean"
|
|
148
255
|
}));
|
|
149
|
-
/**
|
|
256
|
+
/**
|
|
257
|
+
* Schema definition for a serialized flowchart edge entity.
|
|
258
|
+
*
|
|
259
|
+
* @see {@linkcode extremities}
|
|
260
|
+
*/
|
|
150
261
|
var edgeJSON = extremities;
|
|
151
|
-
|
|
262
|
+
/**
|
|
263
|
+
* Schema definition for layout dimensions and connection points of a node.
|
|
264
|
+
*
|
|
265
|
+
* @see {@linkcode point}
|
|
266
|
+
*/
|
|
267
|
+
var dimension = type(({ optional, use }) => ({
|
|
152
268
|
width: "number",
|
|
153
269
|
height: "number",
|
|
154
|
-
id: "string",
|
|
155
270
|
output: use(point),
|
|
156
|
-
input: optional(use(point))
|
|
271
|
+
input: optional(use(point)),
|
|
272
|
+
inputOffset: optional(use(point)),
|
|
273
|
+
outputOffset: optional(use(point))
|
|
157
274
|
}));
|
|
158
|
-
|
|
275
|
+
/**
|
|
276
|
+
* Schema definition for a 2D line vector representing edge coordinates `(x0, y0)` to
|
|
277
|
+
* `(x1, y1)`.
|
|
278
|
+
*/
|
|
279
|
+
var vector = type({
|
|
159
280
|
x0: "number",
|
|
160
281
|
y0: "number",
|
|
161
282
|
x1: "number",
|
|
162
283
|
y1: "number"
|
|
163
284
|
});
|
|
164
|
-
//#endregion
|
|
165
|
-
//#region src/services/main.machine.ts
|
|
166
|
-
/**
|
|
167
|
-
* Constructs a unique edge identifier string from source and destination node IDs.
|
|
168
|
-
*
|
|
169
|
-
* @param out - The source node ID string.
|
|
170
|
-
* @param _in - The destination node ID string.
|
|
171
|
-
*
|
|
172
|
-
* @returns Formatted edge identifier string.
|
|
173
|
-
*/
|
|
174
|
-
var buildEdgeId = (out, _in) => {
|
|
175
|
-
return `edge = ${out} => ${_in}`;
|
|
176
|
-
};
|
|
177
285
|
/**
|
|
178
|
-
*
|
|
286
|
+
* Schema definition for an ongoing new connection edge creation preview between
|
|
287
|
+
* source node and cursor position.
|
|
179
288
|
*
|
|
180
|
-
* @
|
|
181
|
-
*
|
|
182
|
-
* @returns Formatted node identifier string.
|
|
289
|
+
* @see {@linkcode vector}
|
|
183
290
|
*/
|
|
184
|
-
var
|
|
185
|
-
|
|
186
|
-
}
|
|
291
|
+
var newEdge = type(({ intersection, use }) => intersection({ from: "string" }, use(vector)));
|
|
292
|
+
/** Schema definition for flowchart board geometry and container scroll dimensions. */
|
|
293
|
+
var board = type(({ optional }) => ({
|
|
294
|
+
self: {
|
|
295
|
+
left: "number",
|
|
296
|
+
top: "number",
|
|
297
|
+
width: "number",
|
|
298
|
+
height: "number"
|
|
299
|
+
},
|
|
300
|
+
parent: optional({
|
|
301
|
+
scrollLeft: "number",
|
|
302
|
+
scrollTop: "number",
|
|
303
|
+
height: "number",
|
|
304
|
+
width: "number"
|
|
305
|
+
})
|
|
306
|
+
}));
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region src/services/main.machine.ts
|
|
187
309
|
/**
|
|
188
310
|
* State machine managing flowchart state transitions, nodes, edges, selection, and
|
|
189
311
|
* layout actions.
|
|
312
|
+
*
|
|
313
|
+
* @see {@linkcode calculateDimensions}, {@linkcode buildEdgeId}, {@linkcode buildNodeID}, {@linkcode getDefaultOutputOffset}, {@linkcode DEFAULT_INPUT_OFFSET}
|
|
190
314
|
*/
|
|
191
315
|
var machine = createMachine({
|
|
192
316
|
initial: "idle",
|
|
317
|
+
on: { SET_BOARD: { actions: ["setBoard"] } },
|
|
193
318
|
states: {
|
|
194
319
|
idle: { on: {
|
|
195
320
|
CONFIGURE: {
|
|
@@ -204,7 +329,7 @@ var machine = createMachine({
|
|
|
204
329
|
} },
|
|
205
330
|
working: { on: {
|
|
206
331
|
MOVE_IMMEDIATE: { actions: [{
|
|
207
|
-
name: "
|
|
332
|
+
name: "buildUI",
|
|
208
333
|
description: "Must be in the ui"
|
|
209
334
|
}] },
|
|
210
335
|
ADD_CHILD: {
|
|
@@ -229,6 +354,7 @@ var machine = createMachine({
|
|
|
229
354
|
],
|
|
230
355
|
target: "/construction"
|
|
231
356
|
},
|
|
357
|
+
ADD_DIMENSION: { actions: ["addDimension"] },
|
|
232
358
|
ADD_SIBLING: {
|
|
233
359
|
actions: [
|
|
234
360
|
"generateID",
|
|
@@ -248,12 +374,18 @@ var machine = createMachine({
|
|
|
248
374
|
actions: ["addEdge"],
|
|
249
375
|
target: "/construction"
|
|
250
376
|
},
|
|
377
|
+
START_NEW_EDGE: { actions: ["startNewEdge"] },
|
|
378
|
+
MOVE_NEW_EDGE: { actions: ["moveNewEdge"] },
|
|
379
|
+
CLEAR_NEW_EDGE: { actions: ["clearNewEdge"] },
|
|
251
380
|
DELETE: {
|
|
252
381
|
actions: ["delete"],
|
|
253
382
|
target: "/construction"
|
|
254
383
|
},
|
|
255
384
|
SELECT: { actions: ["select"] },
|
|
256
|
-
DESELECT: { actions: ["deselect"] }
|
|
385
|
+
DESELECT: { actions: ["deselect"] },
|
|
386
|
+
ZOOM: { actions: ["zoom"] },
|
|
387
|
+
TOGGLE_ZOOM: { actions: ["toggleZoom"] },
|
|
388
|
+
SET_BOARD: { actions: ["setBoard"] }
|
|
257
389
|
} }
|
|
258
390
|
}
|
|
259
391
|
}, {
|
|
@@ -262,6 +394,7 @@ var machine = createMachine({
|
|
|
262
394
|
nodes: array(intersection(use(nodeJSON), { id: "string" })),
|
|
263
395
|
edges: array(intersection(use(edgeJSON), { id: "string" }))
|
|
264
396
|
},
|
|
397
|
+
SET_BOARD: use(board),
|
|
265
398
|
CONFIGURE_EMPTY: "never",
|
|
266
399
|
MOVE: {
|
|
267
400
|
id: "string",
|
|
@@ -279,11 +412,27 @@ var machine = createMachine({
|
|
|
279
412
|
DELETE: "string",
|
|
280
413
|
SELECT: "string",
|
|
281
414
|
DESELECT: "never",
|
|
282
|
-
ADD_EDGE: use(extremities)
|
|
415
|
+
ADD_EDGE: use(extremities),
|
|
416
|
+
START_NEW_EDGE: "string",
|
|
417
|
+
MOVE_NEW_EDGE: use(point),
|
|
418
|
+
CLEAR_NEW_EDGE: "never",
|
|
419
|
+
ZOOM: "number",
|
|
420
|
+
TOGGLE_ZOOM: "never",
|
|
421
|
+
ADD_DIMENSION: {
|
|
422
|
+
id: "string",
|
|
423
|
+
dimension: use(dimension)
|
|
424
|
+
}
|
|
283
425
|
})),
|
|
284
426
|
sync: true,
|
|
285
|
-
pContext: type(({ union }) => ({
|
|
286
|
-
|
|
427
|
+
pContext: type(({ union, optional, record, use, custom }) => ({
|
|
428
|
+
generatedId: union("string", "null"),
|
|
429
|
+
previousZoom: optional("number"),
|
|
430
|
+
dimensions: record(use(dimension)),
|
|
431
|
+
getBoardPosition: custom(),
|
|
432
|
+
clampPosition: custom(),
|
|
433
|
+
calculateDimensions: custom()
|
|
434
|
+
})),
|
|
435
|
+
context: type(({ optional, use, array, record }) => ({
|
|
287
436
|
data: optional({
|
|
288
437
|
nodes: array({
|
|
289
438
|
...use(nodeJSON),
|
|
@@ -294,46 +443,145 @@ var machine = createMachine({
|
|
|
294
443
|
id: "string"
|
|
295
444
|
})
|
|
296
445
|
}),
|
|
446
|
+
board: optional(use(board)),
|
|
447
|
+
edgesPositions: record(use(vector)),
|
|
448
|
+
newEdge: optional(use(newEdge)),
|
|
297
449
|
selected: optional("string"),
|
|
298
|
-
updatingUI: optional("boolean")
|
|
450
|
+
updatingUI: optional("boolean"),
|
|
451
|
+
zoom: "number",
|
|
452
|
+
bounds: use(point)
|
|
299
453
|
}))
|
|
300
|
-
}).provideOptions(({ assign, batch, erase }) => ({ actions: {
|
|
301
|
-
configure: batch(assign("
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
454
|
+
}).provideOptions(({ assign, batch, erase, filter, action }) => ({ actions: {
|
|
455
|
+
configure: batch(assign("data", { CONFIGURE: ({ payload }) => payload }), assign("newEdge", () => void 0), assign("updatingUI", () => false), action(({ pContext }) => {
|
|
456
|
+
pContext.generatedId = null;
|
|
457
|
+
}), action({ CONFIGURE: ({ payload: { nodes }, pContext: { dimensions } }) => {
|
|
458
|
+
nodes.forEach(({ id, position }) => {
|
|
459
|
+
dimensions[id] = calculateDimensions(position);
|
|
460
|
+
});
|
|
461
|
+
} })),
|
|
462
|
+
startNewEdge: assign("newEdge", { START_NEW_EDGE: ({ payload: from, pContext: { dimensions } }) => {
|
|
463
|
+
const { x, y } = dimensions[from].output;
|
|
464
|
+
return {
|
|
465
|
+
from,
|
|
466
|
+
x0: x,
|
|
467
|
+
y0: y,
|
|
468
|
+
x1: x,
|
|
469
|
+
y1: y
|
|
470
|
+
};
|
|
471
|
+
} }),
|
|
472
|
+
setBoard: assign("board", { SET_BOARD: ({ payload }) => payload }),
|
|
473
|
+
buildUI: batch(assign("edgesPositions", {
|
|
474
|
+
MOVE: ({ context: { data, edgesPositions }, payload, pContext: { dimensions } }) => {
|
|
475
|
+
const edges = data?.edges;
|
|
476
|
+
const dimension = dimensions[payload.id];
|
|
477
|
+
const outputOffset = dimension?.outputOffset ?? getDefaultOutputOffset(dimension?.width);
|
|
478
|
+
const inputOffset = dimension?.inputOffset ?? DEFAULT_INPUT_OFFSET;
|
|
479
|
+
const output = {
|
|
480
|
+
x: payload.x + outputOffset.x,
|
|
481
|
+
y: payload.y + outputOffset.y
|
|
482
|
+
};
|
|
483
|
+
const input = {
|
|
484
|
+
x: payload.x + inputOffset.x,
|
|
485
|
+
y: payload.y + inputOffset.y
|
|
486
|
+
};
|
|
487
|
+
if (dimension) {
|
|
488
|
+
dimension.output = output;
|
|
489
|
+
dimension.input = input;
|
|
490
|
+
}
|
|
491
|
+
edges?.forEach(({ from, to, id }) => {
|
|
492
|
+
if (from === payload.id) {
|
|
493
|
+
edgesPositions[id].x0 = output.x;
|
|
494
|
+
edgesPositions[id].y0 = output.y;
|
|
495
|
+
}
|
|
496
|
+
if (to === payload.id) {
|
|
497
|
+
edgesPositions[id].x1 = input.x;
|
|
498
|
+
edgesPositions[id].y1 = input.y;
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
return edgesPositions;
|
|
502
|
+
},
|
|
503
|
+
MOVE_IMMEDIATE: ({ context: { data, edgesPositions }, payload, pContext: { dimensions } }) => {
|
|
504
|
+
(data?.edges)?.forEach(({ from, to, id }) => {
|
|
505
|
+
if (from === payload.id) {
|
|
506
|
+
const dimension = dimensions[payload.id];
|
|
507
|
+
const offset = dimension?.outputOffset ?? getDefaultOutputOffset(dimension?.width);
|
|
508
|
+
const x0 = payload.x + offset.x;
|
|
509
|
+
const y0 = payload.y + offset.y;
|
|
510
|
+
edgesPositions[id].x0 = x0;
|
|
511
|
+
edgesPositions[id].y0 = y0;
|
|
512
|
+
if (dimension) {
|
|
513
|
+
dimension.output.x = x0;
|
|
514
|
+
dimension.output.y = y0;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (to === payload.id) {
|
|
518
|
+
const dimension = dimensions[payload.id];
|
|
519
|
+
const offset = dimension?.inputOffset ?? DEFAULT_INPUT_OFFSET;
|
|
520
|
+
const x1 = payload.x + offset.x;
|
|
521
|
+
const y1 = payload.y + offset.y;
|
|
522
|
+
edgesPositions[id].x1 = x1;
|
|
523
|
+
edgesPositions[id].y1 = y1;
|
|
524
|
+
if (dimension) dimension.input = {
|
|
525
|
+
x: x1,
|
|
526
|
+
y: y1
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
return edgesPositions;
|
|
531
|
+
},
|
|
532
|
+
else: ({ context: { data, edgesPositions }, pContext: { dimensions } }) => {
|
|
533
|
+
const edges = data?.edges ?? [];
|
|
534
|
+
edgesPositions = {};
|
|
535
|
+
edges.forEach(({ from, id, to }) => {
|
|
536
|
+
const output = dimensions[from]?.output;
|
|
537
|
+
const input = dimensions[to]?.input;
|
|
538
|
+
if (output && input) edgesPositions[id] = {
|
|
539
|
+
x0: output.x,
|
|
540
|
+
y0: output.y,
|
|
541
|
+
x1: input.x,
|
|
542
|
+
y1: input.y
|
|
543
|
+
};
|
|
544
|
+
else delete edgesPositions[id];
|
|
545
|
+
});
|
|
546
|
+
return edgesPositions;
|
|
547
|
+
}
|
|
548
|
+
}), assign("updatingUI", () => true)),
|
|
549
|
+
addDimension: action({ ADD_DIMENSION: ({ payload: { id, dimension }, pContext: { dimensions } }) => {
|
|
550
|
+
dimensions[id] = dimension;
|
|
551
|
+
} }),
|
|
552
|
+
generateID: action(({ pContext }) => {
|
|
553
|
+
pContext.generatedId = nanoid();
|
|
554
|
+
}),
|
|
555
|
+
linkChild: batch(assign("data.edges", { ADD_CHILD: ({ context: { data }, pContext, payload }) => {
|
|
556
|
+
const edges = toArray$1.typed(data?.edges);
|
|
308
557
|
const from = payload;
|
|
309
558
|
const generatedId = pContext?.generatedId;
|
|
310
559
|
const to = buildNodeID(generatedId);
|
|
311
560
|
const id = buildEdgeId(from, to);
|
|
312
|
-
|
|
561
|
+
edges.push({
|
|
313
562
|
id,
|
|
314
563
|
from,
|
|
315
564
|
to
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
565
|
+
});
|
|
566
|
+
return edges;
|
|
567
|
+
} }), assign("selected", ({ pContext: { generatedId } }) => buildNodeID(generatedId))),
|
|
568
|
+
linkSibling: batch(assign("data.edges", { ADD_SIBLING: ({ pContext, payload, context: { data } }) => {
|
|
569
|
+
const edges = toArray$1.typed(data?.edges);
|
|
320
570
|
const generatedId = pContext?.generatedId;
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
if (!from) return out;
|
|
571
|
+
const from = edges.find(({ to }) => to === payload)?.from;
|
|
572
|
+
if (!from) return edges;
|
|
324
573
|
const to = buildNodeID(generatedId);
|
|
325
574
|
const id = buildEdgeId(from, to);
|
|
326
|
-
|
|
575
|
+
edges.push({
|
|
327
576
|
from,
|
|
328
577
|
to,
|
|
329
578
|
id
|
|
330
579
|
});
|
|
331
|
-
return
|
|
332
|
-
} }), assign("
|
|
333
|
-
selectParent: assign("
|
|
334
|
-
moveNode: assign("
|
|
580
|
+
return edges;
|
|
581
|
+
} }), assign("selected", ({ pContext: { generatedId } }) => buildNodeID(generatedId))),
|
|
582
|
+
selectParent: assign("selected", ({ pContext: { generatedId } }) => buildNodeID(generatedId)),
|
|
583
|
+
moveNode: assign("data.nodes", { MOVE: ({ context: { data }, payload }) => {
|
|
335
584
|
const { id, x, y } = payload;
|
|
336
|
-
if (!id) return data?.nodes ?? [];
|
|
337
585
|
return data?.nodes?.map((d) => {
|
|
338
586
|
if (d.id === id) return {
|
|
339
587
|
...d,
|
|
@@ -345,11 +593,11 @@ var machine = createMachine({
|
|
|
345
593
|
return d;
|
|
346
594
|
}) ?? [];
|
|
347
595
|
} }),
|
|
348
|
-
select: assign("
|
|
349
|
-
delete:
|
|
350
|
-
return
|
|
351
|
-
} }),
|
|
352
|
-
addEdge: assign("
|
|
596
|
+
select: assign("selected", { SELECT: ({ payload }) => payload }),
|
|
597
|
+
delete: batch(filter("data.edges", { DELETE: ({ id, from, to }, _, { payload }) => {
|
|
598
|
+
return id !== payload && from !== payload && to !== payload;
|
|
599
|
+
} }), filter("data.nodes", { DELETE: ({ id }, _, { payload }) => id !== payload })),
|
|
600
|
+
addEdge: batch(assign("data.edges", { ADD_EDGE: ({ context, payload: { from, to } }) => {
|
|
353
601
|
const edges = context.data?.edges ?? [];
|
|
354
602
|
const id = buildEdgeId(from, to);
|
|
355
603
|
if (edges.some((e) => e.id === id)) return edges;
|
|
@@ -358,304 +606,218 @@ var machine = createMachine({
|
|
|
358
606
|
from,
|
|
359
607
|
to
|
|
360
608
|
}];
|
|
609
|
+
} }), erase("newEdge")),
|
|
610
|
+
clearNewEdge: erase("newEdge"),
|
|
611
|
+
deselect: erase("selected"),
|
|
612
|
+
zoom: assign("zoom", { ZOOM: ({ context: { zoom }, payload, pContext }) => {
|
|
613
|
+
const next = zoom + payload;
|
|
614
|
+
const clamped = Math.min(Math.max(Number(next.toFixed(2)), .2), 3);
|
|
615
|
+
pContext.previousZoom = void 0;
|
|
616
|
+
return clamped;
|
|
361
617
|
} }),
|
|
362
|
-
|
|
618
|
+
toggleZoom: assign("zoom", { TOGGLE_ZOOM: ({ context: { zoom }, pContext }) => {
|
|
619
|
+
const previous = pContext.previousZoom;
|
|
620
|
+
if (previous !== void 0) {
|
|
621
|
+
pContext.previousZoom = void 0;
|
|
622
|
+
return previous;
|
|
623
|
+
}
|
|
624
|
+
pContext.previousZoom = zoom;
|
|
625
|
+
return 1;
|
|
626
|
+
} })
|
|
363
627
|
} }));
|
|
364
|
-
/** X-axis center offset in pixels from the node container edge. */
|
|
365
|
-
var HANDLE_CENTER_X_OFFSET = 10.5;
|
|
366
|
-
/** Default 2D offset coordinates for input connection handles. */
|
|
367
|
-
var DEFAULT_INPUT_OFFSET = {
|
|
368
|
-
x: -10.5,
|
|
369
|
-
y: 18
|
|
370
|
-
};
|
|
371
|
-
/**
|
|
372
|
-
* Computes default output handle offset coordinates given a node width.
|
|
373
|
-
*
|
|
374
|
-
* @param width - Node width in pixels, defaults to `0`.
|
|
375
|
-
*
|
|
376
|
-
* @returns 2D offset coordinates for the output handle.
|
|
377
|
-
*/
|
|
378
|
-
var getDefaultOutputOffset = (width = 0) => ({
|
|
379
|
-
x: width + HANDLE_CENTER_X_OFFSET,
|
|
380
|
-
y: 18
|
|
381
|
-
});
|
|
382
|
-
/**
|
|
383
|
-
* Boundary padding constraints for node dragging and positioning within the
|
|
384
|
-
* viewport.
|
|
385
|
-
*/
|
|
386
|
-
var BOUNDS_CONSTRAINTS = {
|
|
387
|
-
horizontal: 55,
|
|
388
|
-
vertical: 40
|
|
389
|
-
};
|
|
390
|
-
/**
|
|
391
|
-
* Default node items of type {@linkcode NodeProps} provided when no initial
|
|
392
|
-
* configuration is given.
|
|
393
|
-
*/
|
|
394
|
-
var DEFAULT_NODES = [{
|
|
395
|
-
id: "node-0",
|
|
396
|
-
data: {
|
|
397
|
-
content: "Some text",
|
|
398
|
-
label: "Root node"
|
|
399
|
-
},
|
|
400
|
-
input: false,
|
|
401
|
-
position: {
|
|
402
|
-
x: 350,
|
|
403
|
-
y: 100
|
|
404
|
-
}
|
|
405
|
-
}];
|
|
406
628
|
//#endregion
|
|
407
629
|
//#region src/ui/components/FlowChart.context.ts
|
|
408
630
|
/**
|
|
409
631
|
* Solid Context Provider component and hook for accessing flowchart board state,
|
|
410
632
|
* services, and zoom.
|
|
633
|
+
*
|
|
634
|
+
* @see {@linkcode machine}, {@linkcode createContext}
|
|
411
635
|
*/
|
|
412
636
|
var [Provider, useFlow] = createContext$1(() => {
|
|
413
|
-
/**
|
|
414
|
-
* Shared state machine interpreter service instance for flowchart state
|
|
415
|
-
* management.
|
|
416
|
-
*/
|
|
637
|
+
/** Shared service for flowchart state management. */
|
|
417
638
|
const service = interpret(machine, {
|
|
418
|
-
context: {
|
|
419
|
-
|
|
639
|
+
context: {
|
|
640
|
+
zoom: 1,
|
|
641
|
+
edgesPositions: {},
|
|
642
|
+
bounds: {
|
|
643
|
+
x: 0,
|
|
644
|
+
y: 0
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
pContext: {
|
|
648
|
+
generatedId: null,
|
|
649
|
+
dimensions: {},
|
|
650
|
+
/**
|
|
651
|
+
* Converts viewport screen coordinates to unscaled board coordinates.
|
|
652
|
+
*
|
|
653
|
+
* @param clientX - Client X coordinate in pixels.
|
|
654
|
+
* @param clientY - Client Y coordinate in pixels.
|
|
655
|
+
* @param el - Board layout details of type {@linkcode Board}.
|
|
656
|
+
*
|
|
657
|
+
* @returns Calculated 2D coordinate of type {@linkcode Point}.
|
|
658
|
+
*/
|
|
659
|
+
getBoardPosition(clientX, clientY, el) {
|
|
660
|
+
if (!el) return {
|
|
661
|
+
x: clientX,
|
|
662
|
+
y: clientY
|
|
663
|
+
};
|
|
664
|
+
const currentZoom = service.state.context.zoom;
|
|
665
|
+
return {
|
|
666
|
+
x: (clientX - el.self.left) / currentZoom,
|
|
667
|
+
y: (clientY - el.self.top) / currentZoom
|
|
668
|
+
};
|
|
669
|
+
},
|
|
670
|
+
/**
|
|
671
|
+
* Clamps given coordinates within board boundaries.
|
|
672
|
+
*
|
|
673
|
+
* @param board - Board layout and container details of type
|
|
674
|
+
* {@linkcode Board}.
|
|
675
|
+
* @param x - Desired X position in pixels.
|
|
676
|
+
* @param y - Desired Y position in pixels.
|
|
677
|
+
* @param nodeWidth - Node width in pixels, defaults to `192`.
|
|
678
|
+
* @param nodeHeight - Node height in pixels, defaults to `50`.
|
|
679
|
+
*
|
|
680
|
+
* @returns Clamped coordinate of type {@linkcode Point}.
|
|
681
|
+
*
|
|
682
|
+
* @see {@linkcode BOUNDS_CONSTRAINTS}
|
|
683
|
+
*/
|
|
684
|
+
clampPosition(board, x, y, nodeWidth = 192, nodeHeight = 50) {
|
|
685
|
+
const container = board?.parent;
|
|
686
|
+
if (!container) return {
|
|
687
|
+
x,
|
|
688
|
+
y
|
|
689
|
+
};
|
|
690
|
+
const currentZoom = service.state.context.zoom ?? 1;
|
|
691
|
+
const minX = container.scrollLeft / currentZoom + BOUNDS_CONSTRAINTS.horizontal;
|
|
692
|
+
const maxX = Math.max(minX, (container.scrollLeft + container.width) / currentZoom - BOUNDS_CONSTRAINTS.horizontal - nodeWidth);
|
|
693
|
+
const minY = container.scrollTop / currentZoom + BOUNDS_CONSTRAINTS.vertical;
|
|
694
|
+
const maxY = Math.max(minY, (container.scrollTop + container.height) / currentZoom - BOUNDS_CONSTRAINTS.vertical - nodeHeight);
|
|
695
|
+
return {
|
|
696
|
+
x: Math.min(Math.max(x, minX), maxX),
|
|
697
|
+
y: Math.min(Math.max(y, minY), maxY)
|
|
698
|
+
};
|
|
699
|
+
},
|
|
700
|
+
/**
|
|
701
|
+
* Computes node dimension, connection points, and handle offsets.
|
|
702
|
+
*
|
|
703
|
+
* @param position - Node position of type {@linkcode Point}.
|
|
704
|
+
* @param parentDimension - Optional sizing and offsets to inherit.
|
|
705
|
+
*
|
|
706
|
+
* @returns Calculated dimension object of type {@linkcode Dimension}.
|
|
707
|
+
*
|
|
708
|
+
* @see {@linkcode DEFAULT_INPUT_OFFSET}, {@linkcode getDefaultOutputOffset}
|
|
709
|
+
*/
|
|
710
|
+
calculateDimensions: (position, parentDimension = {
|
|
711
|
+
width: 192,
|
|
712
|
+
height: 50,
|
|
713
|
+
inputOffset: DEFAULT_INPUT_OFFSET
|
|
714
|
+
}) => {
|
|
715
|
+
const width = parentDimension.width;
|
|
716
|
+
const height = parentDimension.height;
|
|
717
|
+
const outputOffset = parentDimension.outputOffset ?? getDefaultOutputOffset(width);
|
|
718
|
+
const inputOffset = parentDimension.inputOffset;
|
|
719
|
+
return {
|
|
720
|
+
width,
|
|
721
|
+
height,
|
|
722
|
+
output: {
|
|
723
|
+
x: position.x + outputOffset.x,
|
|
724
|
+
y: position.y + outputOffset.y
|
|
725
|
+
},
|
|
726
|
+
input: {
|
|
727
|
+
x: position.x + inputOffset.x,
|
|
728
|
+
y: position.y + inputOffset.y
|
|
729
|
+
},
|
|
730
|
+
outputOffset,
|
|
731
|
+
inputOffset
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
}
|
|
420
735
|
});
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Converts client viewport coordinates to board canvas coordinates adjusted for
|
|
427
|
-
* zoom and scroll.
|
|
428
|
-
*
|
|
429
|
-
* @param clientX - Viewport horizontal coordinate.
|
|
430
|
-
* @param clientY - Viewport vertical coordinate.
|
|
431
|
-
*
|
|
432
|
-
* @returns Board coordinate point of type {@linkcode Point}.
|
|
433
|
-
*/
|
|
434
|
-
const getBoardPoint = (clientX, clientY) => {
|
|
435
|
-
const el = boardRef();
|
|
436
|
-
if (!el) return {
|
|
437
|
-
x: clientX,
|
|
438
|
-
y: clientY
|
|
439
|
-
};
|
|
440
|
-
const rect = el.getBoundingClientRect();
|
|
441
|
-
const currentZoom = zoom[0]();
|
|
442
|
-
return {
|
|
443
|
-
x: clientX - rect.left / currentZoom,
|
|
444
|
-
y: clientY - rect.top / currentZoom
|
|
445
|
-
};
|
|
446
|
-
};
|
|
447
|
-
const [edgesPositions, setEdgesPositions] = createSignal({}, { equals: false });
|
|
448
|
-
/**
|
|
449
|
-
* Clamps node coordinates within the visible container boundaries.
|
|
450
|
-
*
|
|
451
|
-
* @param x - Desired horizontal X coordinate.
|
|
452
|
-
* @param y - Desired vertical Y coordinate.
|
|
453
|
-
* @param nodeWidth - Width of the node element in pixels, defaults to `192`.
|
|
454
|
-
* @param nodeHeight - Height of the node element in pixels, defaults to `50`.
|
|
455
|
-
*
|
|
456
|
-
* @returns Clamped coordinate point of type {@linkcode Point}.
|
|
457
|
-
*/
|
|
458
|
-
const clampPosition = (x, y, nodeWidth = 192, nodeHeight = 50) => {
|
|
459
|
-
const container = boardRef()?.parentElement;
|
|
460
|
-
if (!container) return {
|
|
461
|
-
x,
|
|
462
|
-
y
|
|
463
|
-
};
|
|
464
|
-
const currentZoom = zoom[0]();
|
|
465
|
-
const minX = container.scrollLeft / currentZoom + BOUNDS_CONSTRAINTS.horizontal;
|
|
466
|
-
const maxX = Math.max(minX, (container.scrollLeft + container.clientWidth) / currentZoom - BOUNDS_CONSTRAINTS.horizontal - nodeWidth);
|
|
467
|
-
const minY = container.scrollTop / currentZoom + BOUNDS_CONSTRAINTS.vertical;
|
|
468
|
-
const maxY = Math.max(minY, (container.scrollTop + container.clientHeight) / currentZoom - BOUNDS_CONSTRAINTS.vertical - nodeHeight);
|
|
469
|
-
return {
|
|
470
|
-
x: Math.min(Math.max(x, minX), maxX),
|
|
471
|
-
y: Math.min(Math.max(y, minY), maxY)
|
|
472
|
-
};
|
|
473
|
-
};
|
|
474
|
-
service.addOptions(({ voidAction, batch, assign }) => ({ actions: {
|
|
475
|
-
placeChild: assign("context.data.nodes", { ADD_CHILD: ({ payload, context: { data }, pContext: { generatedId } }) => {
|
|
476
|
-
const nodes = data?.nodes ?? [];
|
|
736
|
+
service.addOptions(({ assign }) => ({ actions: {
|
|
737
|
+
placeChild: assign("data.nodes", { ADD_CHILD: ({ payload, context: { data, board }, pContext }) => {
|
|
738
|
+
if (!board) return data?.nodes;
|
|
739
|
+
const nodes = data?.nodes;
|
|
477
740
|
if (!payload) return nodes;
|
|
478
|
-
const parentNode = nodes
|
|
741
|
+
const parentNode = nodes?.find((node) => node.id === payload);
|
|
479
742
|
if (!parentNode) return nodes;
|
|
480
|
-
const
|
|
481
|
-
const
|
|
482
|
-
const
|
|
743
|
+
const parentDimension = pContext.dimensions[payload];
|
|
744
|
+
const id = `node-${pContext.generatedId}`;
|
|
745
|
+
const width = parentDimension?.width ?? 192;
|
|
746
|
+
const height = parentDimension?.height ?? 50;
|
|
483
747
|
const initialX = parentNode.position.x + width + 100;
|
|
484
748
|
const initialY = parentNode.position.y;
|
|
485
|
-
const position = clampPosition(initialX, initialY, width, height);
|
|
486
|
-
|
|
749
|
+
const position = pContext.clampPosition(board, initialX, initialY, width, height);
|
|
750
|
+
const dimension = pContext.calculateDimensions(position, parentDimension);
|
|
751
|
+
nodes?.push({
|
|
487
752
|
id,
|
|
488
753
|
data: { content: "<Nouveau nœud>" },
|
|
489
754
|
input: true,
|
|
490
755
|
position
|
|
491
|
-
}
|
|
756
|
+
});
|
|
757
|
+
pContext.dimensions[id] = dimension;
|
|
758
|
+
return nodes;
|
|
492
759
|
} }),
|
|
493
|
-
placeParent: assign("
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
const
|
|
760
|
+
placeParent: assign("data.nodes", { ADD_PARENT: ({ context: { data, zoom = 1, board }, pContext }) => {
|
|
761
|
+
if (!board) return data?.nodes;
|
|
762
|
+
const nodes = data?.nodes;
|
|
763
|
+
const id = `node-${pContext.generatedId}`;
|
|
764
|
+
const container = board.parent;
|
|
497
765
|
const scrollLeft = container?.scrollLeft ?? 0;
|
|
498
766
|
const scrollTop = container?.scrollTop ?? 0;
|
|
499
|
-
const width = container?.
|
|
500
|
-
const height = container?.
|
|
501
|
-
const currentZoom = zoom
|
|
767
|
+
const width = container?.width ?? 0;
|
|
768
|
+
const height = container?.height ?? 0;
|
|
769
|
+
const currentZoom = zoom;
|
|
502
770
|
const x = (scrollLeft + width / 2) / currentZoom;
|
|
503
771
|
const y = (scrollTop + height / 2) / currentZoom;
|
|
504
|
-
|
|
772
|
+
const position = pContext.clampPosition(board, x, y);
|
|
773
|
+
const dimension = pContext.calculateDimensions(position);
|
|
774
|
+
nodes?.push({
|
|
505
775
|
id,
|
|
506
776
|
data: { content: "<Nouveau nœud>" },
|
|
507
777
|
input: false,
|
|
508
|
-
position
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
}];
|
|
778
|
+
position
|
|
779
|
+
});
|
|
780
|
+
pContext.dimensions[id] = dimension;
|
|
781
|
+
return nodes;
|
|
513
782
|
} }),
|
|
514
|
-
placeSibling: assign("
|
|
515
|
-
|
|
516
|
-
const
|
|
517
|
-
const
|
|
783
|
+
placeSibling: assign("data.nodes", { ADD_SIBLING: ({ payload, context: { data, board }, pContext }) => {
|
|
784
|
+
if (!board) return data?.nodes;
|
|
785
|
+
const edges = data?.edges;
|
|
786
|
+
const nodes = data?.nodes;
|
|
787
|
+
const parentID = edges?.find((edge) => edge.to === payload)?.from;
|
|
518
788
|
if (!parentID) return nodes;
|
|
519
|
-
const parentNode = nodes
|
|
789
|
+
const parentNode = nodes?.find((node) => node.id === parentID);
|
|
520
790
|
if (!parentNode) return nodes;
|
|
521
|
-
const
|
|
522
|
-
const
|
|
523
|
-
const
|
|
791
|
+
const parentDimension = pContext.dimensions[parentID];
|
|
792
|
+
const id = `node-${pContext.generatedId}`;
|
|
793
|
+
const width = parentDimension?.width ?? 192;
|
|
794
|
+
const height = parentDimension?.height ?? 50;
|
|
524
795
|
const initialX = parentNode.position.x + width + 100;
|
|
525
796
|
const initialY = parentNode.position.y + 100;
|
|
526
|
-
const position = clampPosition(initialX, initialY, width, height);
|
|
527
|
-
|
|
797
|
+
const position = pContext.clampPosition(board, initialX, initialY, width, height);
|
|
798
|
+
const dimension = pContext.calculateDimensions(position, parentDimension);
|
|
799
|
+
nodes?.push({
|
|
528
800
|
id,
|
|
529
801
|
data: { content: "<Nouveau nœud>" },
|
|
530
802
|
input: true,
|
|
531
803
|
position
|
|
532
|
-
}];
|
|
533
|
-
} }),
|
|
534
|
-
buildUI: batch(voidAction(({ context: { data } }) => {
|
|
535
|
-
const edges = data?.edges ?? [];
|
|
536
|
-
setEdgesPositions((data) => {
|
|
537
|
-
const array = Object.entries({ ...data }).filter(([id]) => edges.some((edge) => edge.id === id));
|
|
538
|
-
return Object.fromEntries(array);
|
|
539
804
|
});
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
};
|
|
553
|
-
});
|
|
554
|
-
}));
|
|
555
|
-
},
|
|
556
|
-
MOVE: ({ context: { data }, payload }) => {
|
|
557
|
-
const edges = data?.edges ?? [];
|
|
558
|
-
setEdgesPositions(produce((next) => {
|
|
559
|
-
edges?.forEach(({ from, to, id }) => {
|
|
560
|
-
if (from === payload.id) {
|
|
561
|
-
const offset = dimensions()[payload.id]?.outputOffset ?? getDefaultOutputOffset(dimensions()[payload.id]?.width);
|
|
562
|
-
const x0 = payload.x + offset.x;
|
|
563
|
-
const y0 = payload.y + offset.y;
|
|
564
|
-
next[id] = {
|
|
565
|
-
...next[id],
|
|
566
|
-
x0,
|
|
567
|
-
y0
|
|
568
|
-
};
|
|
569
|
-
setDimensions(produce((data) => {
|
|
570
|
-
if (data[payload.id]) data[payload.id] = {
|
|
571
|
-
...data[payload.id],
|
|
572
|
-
output: {
|
|
573
|
-
x: x0,
|
|
574
|
-
y: y0
|
|
575
|
-
}
|
|
576
|
-
};
|
|
577
|
-
}));
|
|
578
|
-
}
|
|
579
|
-
if (to === payload.id) {
|
|
580
|
-
const offset = dimensions()[payload.id]?.inputOffset ?? DEFAULT_INPUT_OFFSET;
|
|
581
|
-
const x1 = payload.x + offset.x;
|
|
582
|
-
const y1 = payload.y + offset.y;
|
|
583
|
-
next[id] = {
|
|
584
|
-
...next[id],
|
|
585
|
-
x1,
|
|
586
|
-
y1
|
|
587
|
-
};
|
|
588
|
-
setDimensions(produce((data) => {
|
|
589
|
-
if (data[payload.id]) data[payload.id] = {
|
|
590
|
-
...data[payload.id],
|
|
591
|
-
input: {
|
|
592
|
-
x: x1,
|
|
593
|
-
y: y1
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
}));
|
|
597
|
-
}
|
|
598
|
-
});
|
|
599
|
-
}));
|
|
600
|
-
}
|
|
601
|
-
}), assign("context.updatingUI", () => true)),
|
|
602
|
-
buildImmediateUI: voidAction({ MOVE_IMMEDIATE: ({ context: { data }, payload }) => {
|
|
603
|
-
const edges = data?.edges ?? [];
|
|
604
|
-
setEdgesPositions(produce((next) => {
|
|
605
|
-
edges?.forEach(({ from, to, id }) => {
|
|
606
|
-
if (from === payload.id) {
|
|
607
|
-
const offset = dimensions()[payload.id]?.outputOffset ?? getDefaultOutputOffset(dimensions()[payload.id]?.width);
|
|
608
|
-
const x0 = payload.x + offset.x;
|
|
609
|
-
const y0 = payload.y + offset.y;
|
|
610
|
-
next[id] = {
|
|
611
|
-
...next[id],
|
|
612
|
-
x0,
|
|
613
|
-
y0
|
|
614
|
-
};
|
|
615
|
-
setDimensions(produce((data) => {
|
|
616
|
-
if (data[payload.id]) data[payload.id] = {
|
|
617
|
-
...data[payload.id],
|
|
618
|
-
output: {
|
|
619
|
-
x: x0,
|
|
620
|
-
y: y0
|
|
621
|
-
}
|
|
622
|
-
};
|
|
623
|
-
}));
|
|
624
|
-
}
|
|
625
|
-
if (to === payload.id) {
|
|
626
|
-
const offset = dimensions()[payload.id]?.inputOffset ?? DEFAULT_INPUT_OFFSET;
|
|
627
|
-
const x1 = payload.x + offset.x;
|
|
628
|
-
const y1 = payload.y + offset.y;
|
|
629
|
-
next[id] = {
|
|
630
|
-
...next[id],
|
|
631
|
-
x1,
|
|
632
|
-
y1
|
|
633
|
-
};
|
|
634
|
-
setDimensions(produce((data) => {
|
|
635
|
-
if (data[payload.id]) data[payload.id] = {
|
|
636
|
-
...data[payload.id],
|
|
637
|
-
input: {
|
|
638
|
-
x: x1,
|
|
639
|
-
y: y1
|
|
640
|
-
}
|
|
641
|
-
};
|
|
642
|
-
}));
|
|
643
|
-
}
|
|
644
|
-
});
|
|
645
|
-
}));
|
|
805
|
+
pContext.dimensions[id] = dimension;
|
|
806
|
+
return nodes;
|
|
807
|
+
} }),
|
|
808
|
+
moveNewEdge: assign("newEdge", { MOVE_NEW_EDGE: ({ context: { newEdge, board }, payload, pContext }) => {
|
|
809
|
+
if (!board) return void 0;
|
|
810
|
+
if (!newEdge) return void 0;
|
|
811
|
+
const { x: x1, y: y1 } = pContext.getBoardPosition(payload.x, payload.y, board);
|
|
812
|
+
return {
|
|
813
|
+
...newEdge,
|
|
814
|
+
x1,
|
|
815
|
+
y1
|
|
816
|
+
};
|
|
646
817
|
} })
|
|
647
818
|
} }));
|
|
648
819
|
service.start();
|
|
649
|
-
return
|
|
650
|
-
dimensions: [dimensions, setDimensions],
|
|
651
|
-
newEdge,
|
|
652
|
-
board: [boardRef, setBoardRef],
|
|
653
|
-
getBoardPoint,
|
|
654
|
-
edgesPositions: [edgesPositions, setEdgesPositions],
|
|
655
|
-
service,
|
|
656
|
-
zoom,
|
|
657
|
-
clampPosition
|
|
658
|
-
};
|
|
820
|
+
return service;
|
|
659
821
|
}, { name: "FlowContext" });
|
|
660
822
|
//#endregion
|
|
661
823
|
//#region src/ui/components/Bounds.tsx
|
|
@@ -665,24 +827,28 @@ var [Provider, useFlow] = createContext$1(() => {
|
|
|
665
827
|
*
|
|
666
828
|
* @returns `null` as this component performs side-effect transformer registrations
|
|
667
829
|
* only.
|
|
830
|
+
*
|
|
831
|
+
* @see {@linkcode useFlow}, {@linkcode BOUNDS_CONSTRAINTS}
|
|
668
832
|
*/
|
|
669
833
|
var DragBounds = () => {
|
|
670
|
-
const
|
|
834
|
+
const service = useFlow();
|
|
835
|
+
const zoom = createState(service, { selector: ({ context }) => context.zoom });
|
|
836
|
+
const board = createState(service, { selector: ({ context }) => context.board });
|
|
671
837
|
const [state, { addTransformer, removeTransformer, onDragStart, onDragEnd }] = useDragDropContext();
|
|
672
838
|
const transformer = {
|
|
673
839
|
id: "clamp-to-container",
|
|
674
840
|
order: 100,
|
|
675
841
|
callback: (transform) => {
|
|
676
|
-
const container =
|
|
842
|
+
const container = board()?.self;
|
|
677
843
|
const activeDraggable = state.active.draggable;
|
|
678
844
|
if (!container || !activeDraggable) return transform;
|
|
679
845
|
const node = activeDraggable.node;
|
|
680
846
|
if (!node) return transform;
|
|
681
847
|
const currentZoom = zoom();
|
|
682
848
|
const minBoardX = BOUNDS_CONSTRAINTS.horizontal;
|
|
683
|
-
const maxBoardX = Math.max(minBoardX, container.
|
|
849
|
+
const maxBoardX = Math.max(minBoardX, container.width / currentZoom - node.offsetWidth - BOUNDS_CONSTRAINTS.horizontal);
|
|
684
850
|
const minBoardY = BOUNDS_CONSTRAINTS.vertical;
|
|
685
|
-
const maxBoardY = Math.max(minBoardY, container.
|
|
851
|
+
const maxBoardY = Math.max(minBoardY, container.height / currentZoom - node.offsetHeight - BOUNDS_CONSTRAINTS.vertical);
|
|
686
852
|
const minTransformX = (minBoardX - node.offsetLeft) * currentZoom;
|
|
687
853
|
const maxTransformX = (maxBoardX - node.offsetLeft) * currentZoom;
|
|
688
854
|
const minTransformY = (minBoardY - node.offsetTop) * currentZoom;
|
|
@@ -731,6 +897,8 @@ var calculateOffset = (value) => value * 100 / 200;
|
|
|
731
897
|
* @param vector - Vector coordinates of type {@linkcode Vector}.
|
|
732
898
|
*
|
|
733
899
|
* @returns SVG cubic bezier path string.
|
|
900
|
+
*
|
|
901
|
+
* @see {@linkcode calculateOffset}
|
|
734
902
|
*/
|
|
735
903
|
var draw = ({ x0, y0, x1, y1 }) => {
|
|
736
904
|
return `M ${x0} ${y0} C ${x0 + calculateOffset(Math.abs(x1 - x0))} ${y0}, ${x1 - calculateOffset(Math.abs(x1 - x0))} ${y1}, ${x1} ${y1}`;
|
|
@@ -742,30 +910,53 @@ var draw = ({ x0, y0, x1, y1 }) => {
|
|
|
742
910
|
* @param props - Edge properties of type {@linkcode Props}.
|
|
743
911
|
*
|
|
744
912
|
* @returns The rendered SVG JSX elements.
|
|
913
|
+
*
|
|
914
|
+
* @see {@linkcode draw}, {@linkcode useFlow}
|
|
745
915
|
*/
|
|
746
916
|
var EdgeComponent = (props) => {
|
|
747
|
-
const
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
917
|
+
const service = useFlow();
|
|
918
|
+
const vector = createState(service, {
|
|
919
|
+
selector: ({ context }) => {
|
|
920
|
+
if (props.isNew) {
|
|
921
|
+
const edge = context.newEdge;
|
|
922
|
+
if (!edge) return void 0;
|
|
923
|
+
return {
|
|
924
|
+
x0: edge.x0,
|
|
925
|
+
y0: edge.y0,
|
|
926
|
+
x1: edge.x1,
|
|
927
|
+
y1: edge.y1
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
return context.edgesPositions[props.id];
|
|
931
|
+
},
|
|
932
|
+
equals: dequal
|
|
759
933
|
});
|
|
760
|
-
const selected =
|
|
761
|
-
|
|
934
|
+
const selected = createState(service, { selector: (s) => s.context.selected === props.id });
|
|
935
|
+
/** Computes the 2D midpoint coordinate of the edge curve for handle placement. */
|
|
936
|
+
const middlePoint = () => {
|
|
937
|
+
const v = vector();
|
|
938
|
+
if (!v) return {
|
|
939
|
+
x: 0,
|
|
940
|
+
y: 0
|
|
941
|
+
};
|
|
942
|
+
return {
|
|
943
|
+
x: v.x0 + (v.x1 - v.x0) / 2,
|
|
944
|
+
y: v.y0 + (v.y1 - v.y0) / 2
|
|
945
|
+
};
|
|
946
|
+
};
|
|
947
|
+
return createComponent(Show, {
|
|
762
948
|
get when() {
|
|
763
|
-
return
|
|
949
|
+
return vector();
|
|
764
950
|
},
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
951
|
+
children: (v) => [ssr(_tmpl$$4, ssrHydrationKey(), `relative cursor-pointer fill-transparent ${!!props.isNew ? "stroke-[rgba(168,168,168,0.4)] stroke-3 z-200" : ""} ${selected() && !props.isNew ? "stroke-[rgba(168,168,168,1)] stroke-4 z-100" : ""} ${!selected() && !props.isNew ? "stroke-[rgba(168,168,168,0.8)] stroke-3" : ""}`, ssrStyleProperty("pointer-events:", props.isNew ? "none" : "all"), ssrAttribute("d", escape(draw(v()), true), false)), createComponent(Show, {
|
|
952
|
+
get when() {
|
|
953
|
+
return selected();
|
|
954
|
+
},
|
|
955
|
+
get children() {
|
|
956
|
+
return ssr(_tmpl$2$2, ssrHydrationKey(), `translate(${escape(middlePoint().x, true)}, ${escape(middlePoint().y, true)})`, ssrStyleProperty("pointer-events:", "all") + ssrStyleProperty(";z-index:", "30"));
|
|
957
|
+
}
|
|
958
|
+
})]
|
|
959
|
+
});
|
|
769
960
|
};
|
|
770
961
|
//#endregion
|
|
771
962
|
//#region src/ui/components/EdgesBoard.tsx
|
|
@@ -780,44 +971,31 @@ var _tmpl$$3 = [
|
|
|
780
971
|
* edge creation previews.
|
|
781
972
|
*
|
|
782
973
|
* @returns The rendered SVG JSX element.
|
|
974
|
+
*
|
|
975
|
+
* @see {@linkcode EdgeComponent}, {@linkcode useFlow}
|
|
783
976
|
*/
|
|
784
977
|
var EdgesBoard = () => {
|
|
785
|
-
const
|
|
786
|
-
const
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
...vector
|
|
791
|
-
}));
|
|
792
|
-
});
|
|
793
|
-
createEffect(() => {
|
|
794
|
-
if (selected() && newEdge()) setSelected();
|
|
978
|
+
const service = useFlow();
|
|
979
|
+
const hasNewEdge = createState(service, { selector: (s) => !!s.context.newEdge });
|
|
980
|
+
const edgeIds = createState(service, {
|
|
981
|
+
selector: ({ context }) => Object.keys(context.edgesPositions),
|
|
982
|
+
equals: (prev, next) => prev.length === next.length
|
|
795
983
|
});
|
|
796
984
|
return ssr(_tmpl$$3, ssrHydrationKey(), escape(createComponent(Show, {
|
|
797
985
|
get when() {
|
|
798
|
-
return
|
|
986
|
+
return hasNewEdge();
|
|
799
987
|
},
|
|
800
|
-
children
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
get y0() {
|
|
807
|
-
return value().y0;
|
|
808
|
-
},
|
|
809
|
-
get x1() {
|
|
810
|
-
return value().x1;
|
|
811
|
-
},
|
|
812
|
-
get y1() {
|
|
813
|
-
return value().y1;
|
|
814
|
-
}
|
|
815
|
-
})
|
|
988
|
+
get children() {
|
|
989
|
+
return createComponent(EdgeComponent, {
|
|
990
|
+
id: "__#new-edge#__TEMP",
|
|
991
|
+
isNew: true
|
|
992
|
+
});
|
|
993
|
+
}
|
|
816
994
|
})), escape(createComponent(For, {
|
|
817
995
|
get each() {
|
|
818
|
-
return
|
|
996
|
+
return edgeIds();
|
|
819
997
|
},
|
|
820
|
-
children: EdgeComponent
|
|
998
|
+
children: (id) => createComponent(EdgeComponent, { id })
|
|
821
999
|
})));
|
|
822
1000
|
};
|
|
823
1001
|
//#endregion
|
|
@@ -837,11 +1015,12 @@ var _tmpl$3$1 = [
|
|
|
837
1015
|
"<div",
|
|
838
1016
|
" class=\"",
|
|
839
1017
|
"\" style=\"",
|
|
840
|
-
"\"
|
|
1018
|
+
"\"",
|
|
1019
|
+
"><div class=\"",
|
|
841
1020
|
"\"><svg class=\"cursor-pointer rounded-full fill-[#a11111] opacity-100 transition-all duration-200 ease-in-out\" fill=\"currentColor\" stroke-width=\"2\" viewBox=\"4 4 16 16\" style=\"",
|
|
842
1021
|
"\"><path d=\"M12 4c-4.419 0-8 3.582-8 8s3.581 8 8 8 8-3.582 8-8-3.581-8-8-8zm3.707 10.293a.999.999 0 11-1.414 1.414L12 13.414l-2.293 2.293a.997.997 0 01-1.414 0 .999.999 0 010-1.414L10.586 12 8.293 9.707a.999.999 0 111.414-1.414L12 10.586l2.293-2.293a.999.999 0 111.414 1.414L13.414 12l2.293 2.293z\"></path></svg><!--$-->",
|
|
843
|
-
"<!--/--><svg class=\"flex cursor-pointer items-center justify-center rounded-lg bg-blue-500 p-0.5 text-center font-bold text-white hover:bg-blue-600\" style=\"",
|
|
844
|
-
"\"
|
|
1022
|
+
"<!--/--><svg class=\"flex cursor-pointer items-center justify-center rounded-lg bg-blue-500 p-0.5 text-center font-bold text-white hover:bg-blue-600\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\" style=\"",
|
|
1023
|
+
"\"><path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"></path></svg></div><!--$-->",
|
|
845
1024
|
"<!--/--><!--$-->",
|
|
846
1025
|
"<!--/--><!--$-->",
|
|
847
1026
|
"<!--/--><div id=\"inputs\" class=\"pointer-events-none absolute top-0 z-10 flex flex-col\" style=\"",
|
|
@@ -865,22 +1044,34 @@ var _tmpl$5 = [
|
|
|
865
1044
|
* @param props - Node rendering properties of type {@linkcode Props}.
|
|
866
1045
|
*
|
|
867
1046
|
* @returns The rendered Solid component.
|
|
1047
|
+
*
|
|
1048
|
+
* @see {@linkcode useFlow}, {@linkcode HANDLE_CONTAINER_OFFSET_X}, {@linkcode HANDLE_MARGIN_TOP}, {@linkcode HANDLE_SIZE}
|
|
868
1049
|
*/
|
|
869
1050
|
var NodeComponent = (props) => {
|
|
870
|
-
const
|
|
871
|
-
const
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1051
|
+
const service = useFlow();
|
|
1052
|
+
const node = createState(service, {
|
|
1053
|
+
selector: ({ context }) => {
|
|
1054
|
+
const item = toArray.typed(context.data?.nodes).find((n) => n.id === props.id);
|
|
1055
|
+
if (!item) return void 0;
|
|
1056
|
+
return {
|
|
1057
|
+
x: item.position.x,
|
|
1058
|
+
y: item.position.y,
|
|
1059
|
+
label: item.data.label,
|
|
1060
|
+
content: item.data.content ?? "",
|
|
1061
|
+
input: item.input
|
|
1062
|
+
};
|
|
1063
|
+
},
|
|
1064
|
+
equals: dequal
|
|
876
1065
|
});
|
|
877
|
-
|
|
1066
|
+
createState(service, { selector: (s) => s.context.newEdge });
|
|
1067
|
+
const selected = createState(service, { selector: (s) => s.context.selected === props.id });
|
|
1068
|
+
const hasParent = createState(service, { selector: ({ context: { data } }) => {
|
|
878
1069
|
const edges = data?.edges;
|
|
879
1070
|
if (!edges) return false;
|
|
880
1071
|
return edges.some(({ to }) => to === props.id);
|
|
881
1072
|
} });
|
|
882
1073
|
createDraggable(props.id);
|
|
883
|
-
return ssr(_tmpl$3$1, ssrHydrationKey()
|
|
1074
|
+
return ssr(_tmpl$3$1, ssrHydrationKey(), `flex flex-col absolute cursor-grab bg-white rounded-md shadow-md select-none transition-[border,box-shadow] duration-200 ease-in-out hover:shadow-lg draggable ${selected() ? "border border-[#e38c29] z-[100]" : ""} ${!selected() ? "border border-[#e6d4be] z-[1]" : ""} min-w-48`, ssrStyleProperty("top:", `${escape(node()?.y ?? 0, true)}px`) + ssrStyleProperty(";left:", `${escape(node()?.x ?? 0, true)}px`), ssrAttribute("id", escape(props.id, true), false), `pointer-events-none absolute flex items-center justify-end -top-7.5 right-0 transition-all duration-200 ease-in-out space-x-2 ${selected() ? "w-full opacity-100" : ""} ${!selected() ? "w-0 -right-3 opacity-0 overflow-hidden" : ""}`, ssrStyleProperty("overflow:", "visible") + ssrStyleProperty(";pointer-events:", "all") + ssrStyleProperty(";width:", `${escape(12, true) * 2}px`) + ssrStyleProperty(";height:", `${escape(12, true) * 2}px`), escape(createComponent(Show, {
|
|
884
1075
|
get when() {
|
|
885
1076
|
return hasParent();
|
|
886
1077
|
},
|
|
@@ -889,19 +1080,19 @@ var NodeComponent = (props) => {
|
|
|
889
1080
|
}
|
|
890
1081
|
})), ssrStyleProperty("pointer-events:", "all") + ssrStyleProperty(";width:", `${escape(12, true) * 2}px`) + ssrStyleProperty(";height:", `${escape(12, true) * 2}px`), escape(createComponent(Show, {
|
|
891
1082
|
get when() {
|
|
892
|
-
return
|
|
1083
|
+
return node()?.label;
|
|
893
1084
|
},
|
|
894
1085
|
keyed: true,
|
|
895
1086
|
children: (label) => ssr(_tmpl$4, ssrHydrationKey(), escape(label))
|
|
896
1087
|
})), escape(createComponent(Show, {
|
|
897
1088
|
get when() {
|
|
898
|
-
return
|
|
1089
|
+
return node()?.content;
|
|
899
1090
|
},
|
|
900
1091
|
keyed: true,
|
|
901
1092
|
children: (content) => ssr(_tmpl$5, ssrHydrationKey(), escape(content))
|
|
902
1093
|
})), escape(createComponent(Show, {
|
|
903
1094
|
get when() {
|
|
904
|
-
return
|
|
1095
|
+
return node()?.input || hasParent();
|
|
905
1096
|
},
|
|
906
1097
|
get children() {
|
|
907
1098
|
return ssr(_tmpl$2$1, ssrHydrationKey(), ssrStyleProperty("left:", `-${escape(18, true)}px`), ssrStyleProperty("width:", `${escape(12, true)}px`) + ssrStyleProperty(";height:", `${escape(12, true)}px`) + ssrStyleProperty(";margin-top:", `${escape(12, true)}px`) + ssrStyleProperty(";pointer-events:", "all"));
|
|
@@ -912,7 +1103,7 @@ var NodeComponent = (props) => {
|
|
|
912
1103
|
//#region src/ui/components/NodesBoard.tsx
|
|
913
1104
|
var _tmpl$$1 = [
|
|
914
1105
|
"<div",
|
|
915
|
-
" class=\"relative h-full w-full overflow-
|
|
1106
|
+
" class=\"relative h-full w-full overflow-auto rounded-lg border-2 border-gray-600\"><!--$-->",
|
|
916
1107
|
"<!--/--><div class=\"",
|
|
917
1108
|
"\" style=\"",
|
|
918
1109
|
"\"><div class=\"relative h-full w-full\" style=\"",
|
|
@@ -936,6 +1127,8 @@ var _tmpl$3 = [
|
|
|
936
1127
|
* panning gestures, and rendered nodes/edges.
|
|
937
1128
|
*
|
|
938
1129
|
* @returns The rendered Solid component.
|
|
1130
|
+
*
|
|
1131
|
+
* @see {@linkcode DragBounds}, {@linkcode EdgesBoard}, {@linkcode NodeComponent}, {@linkcode useFlow}, {@linkcode CANVAS_FACTOR}, {@linkcode SCROLL_MULTIPLIER}
|
|
939
1132
|
*/
|
|
940
1133
|
var NodesBoard = () => {
|
|
941
1134
|
let containerRef;
|
|
@@ -945,17 +1138,51 @@ var NodesBoard = () => {
|
|
|
945
1138
|
y: 0
|
|
946
1139
|
});
|
|
947
1140
|
const [id, setId] = createSignal("");
|
|
948
|
-
const [
|
|
1141
|
+
const [ref, setRef] = createSignal();
|
|
949
1142
|
let percentX = 0;
|
|
950
1143
|
let percentY = 0;
|
|
951
|
-
const
|
|
1144
|
+
const service = useFlow();
|
|
1145
|
+
createState(service, {
|
|
1146
|
+
selector: (s) => s.context.newEdge,
|
|
1147
|
+
equals: dequal
|
|
1148
|
+
});
|
|
1149
|
+
const zoom = createState(service, { selector: (s) => s.context.zoom ?? 1 });
|
|
1150
|
+
/**
|
|
1151
|
+
* Dispatches the current board geometry and parent container dimensions to the
|
|
1152
|
+
* state machine service.
|
|
1153
|
+
*/
|
|
1154
|
+
const sendBoard = () => {
|
|
1155
|
+
const el = ref();
|
|
1156
|
+
if (!el) return;
|
|
1157
|
+
const rect = el.getBoundingClientRect();
|
|
1158
|
+
const parent = () => ref()?.parentElement;
|
|
1159
|
+
const payload = {
|
|
1160
|
+
self: {
|
|
1161
|
+
left: rect.left,
|
|
1162
|
+
top: rect.top,
|
|
1163
|
+
width: el.clientWidth,
|
|
1164
|
+
height: el.clientHeight
|
|
1165
|
+
},
|
|
1166
|
+
parent: parent() ? {
|
|
1167
|
+
scrollLeft: parent().scrollLeft,
|
|
1168
|
+
scrollTop: parent().scrollTop,
|
|
1169
|
+
height: parent().clientHeight,
|
|
1170
|
+
width: parent().clientWidth
|
|
1171
|
+
} : void 0
|
|
1172
|
+
};
|
|
1173
|
+
service.send({
|
|
1174
|
+
type: "SET_BOARD",
|
|
1175
|
+
payload
|
|
1176
|
+
});
|
|
1177
|
+
};
|
|
952
1178
|
createEffect(on(zoom, () => {
|
|
953
1179
|
const maxScrollX = containerRef.scrollWidth - containerRef.clientWidth;
|
|
954
1180
|
const maxScrollY = containerRef.scrollHeight - containerRef.clientHeight;
|
|
955
1181
|
if (maxScrollX > 0) containerRef.scrollLeft = percentX * maxScrollX;
|
|
956
1182
|
if (maxScrollY > 0) containerRef.scrollTop = percentY * maxScrollY;
|
|
957
1183
|
}, { defer: true }));
|
|
958
|
-
|
|
1184
|
+
onMount(sendBoard);
|
|
1185
|
+
const selectedId = createState(service, { selector: (s) => s.context?.selected });
|
|
959
1186
|
/**
|
|
960
1187
|
* Determines whether the given element ID matches the currently selected entity.
|
|
961
1188
|
*
|
|
@@ -964,23 +1191,18 @@ var NodesBoard = () => {
|
|
|
964
1191
|
* @returns `true` if the item is currently selected, otherwise `false`.
|
|
965
1192
|
*/
|
|
966
1193
|
const selected = (id) => selectedId() === id;
|
|
967
|
-
const
|
|
968
|
-
selector: (
|
|
969
|
-
return (
|
|
970
|
-
id: item.id,
|
|
971
|
-
x: item.position.x,
|
|
972
|
-
y: item.position.y,
|
|
973
|
-
label: item.data.label,
|
|
974
|
-
content: item.data.content ?? "",
|
|
975
|
-
input: item.input
|
|
976
|
-
}));
|
|
1194
|
+
const nodeIds = createState(service, {
|
|
1195
|
+
selector: ({ context }) => {
|
|
1196
|
+
return toArray.typed(context.data?.nodes).map((item) => item.id);
|
|
977
1197
|
},
|
|
978
|
-
equals:
|
|
1198
|
+
equals: (prev, next) => prev.length === next.length
|
|
979
1199
|
});
|
|
980
1200
|
const CANVAS_SIZE = 500;
|
|
981
1201
|
const MARGIN_X = 265;
|
|
982
1202
|
const MARGIN_Y = 425;
|
|
1203
|
+
/** Computes the dynamic canvas width string in CSS units adjusted for zoom. */
|
|
983
1204
|
const cWidth = () => `calc((${CANVAS_SIZE}vw - ${MARGIN_X}px) * ${zoom()})`;
|
|
1205
|
+
/** Computes the dynamic canvas height string in CSS units adjusted for zoom. */
|
|
984
1206
|
const cHeight = () => `calc((${CANVAS_SIZE}vh - ${MARGIN_Y}px) * ${zoom()})`;
|
|
985
1207
|
return ssr(_tmpl$3, ssrHydrationKey(), escape(createComponent(DragDropProvider, {
|
|
986
1208
|
onDragMove: ({ draggable: { transform: _transform, node, id } }) => {
|
|
@@ -1028,15 +1250,15 @@ var NodesBoard = () => {
|
|
|
1028
1250
|
node.style.setProperty("top", Y + "px");
|
|
1029
1251
|
node.style.setProperty("left", X + "px");
|
|
1030
1252
|
node.style.removeProperty("transform");
|
|
1253
|
+
service.send({
|
|
1254
|
+
type: "MOVE",
|
|
1255
|
+
payload: {
|
|
1256
|
+
id: `${id}`,
|
|
1257
|
+
x: X,
|
|
1258
|
+
y: Y
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1031
1261
|
setTimeout(() => {
|
|
1032
|
-
service.send({
|
|
1033
|
-
type: "MOVE",
|
|
1034
|
-
payload: {
|
|
1035
|
-
id: `${id}`,
|
|
1036
|
-
x: X,
|
|
1037
|
-
y: Y
|
|
1038
|
-
}
|
|
1039
|
-
});
|
|
1040
1262
|
setTransform({
|
|
1041
1263
|
x: 0,
|
|
1042
1264
|
y: 0
|
|
@@ -1047,9 +1269,9 @@ var NodesBoard = () => {
|
|
|
1047
1269
|
return [
|
|
1048
1270
|
ssr(_tmpl$$1, ssrHydrationKey(), escape(createComponent(DragDropSensors, {})), `relative cursor-crosshair overflow-hidden ${isPanning() ? "cursor-grabbing" : ""}`, ssrStyleProperty("height:", escape(cHeight(), true)) + ssrStyleProperty(";width:", escape(cWidth(), true)), ssrStyleProperty("scale:", escape(zoom(), true)) + ssrStyleProperty(";transform-origin:", "top left"), escape(createComponent(DragBounds, {})), escape(createComponent(EdgesBoard, {})), escape(createComponent(For, {
|
|
1049
1271
|
get each() {
|
|
1050
|
-
return
|
|
1272
|
+
return nodeIds();
|
|
1051
1273
|
},
|
|
1052
|
-
children: NodeComponent
|
|
1274
|
+
children: (id) => createComponent(NodeComponent, { id })
|
|
1053
1275
|
}))),
|
|
1054
1276
|
ssr(_tmpl$2, ssrHydrationKey(), escape(Math.round(zoom() * 100))),
|
|
1055
1277
|
createComponent(Show, {
|
|
@@ -1080,13 +1302,16 @@ var _tmpl$ = [
|
|
|
1080
1302
|
* {@linkcode FlowProps}.
|
|
1081
1303
|
*
|
|
1082
1304
|
* @returns The rendered Solid component.
|
|
1305
|
+
*
|
|
1306
|
+
* @see {@linkcode NodesBoard}, {@linkcode useFlow}, {@linkcode DEFAULT_NODES}
|
|
1083
1307
|
*/
|
|
1084
1308
|
var FlowChart = (props) => {
|
|
1085
1309
|
const primaryNodes = props.config?.nodes ?? DEFAULT_NODES;
|
|
1086
1310
|
const primaryEdges = props.config?.edges;
|
|
1087
|
-
const
|
|
1311
|
+
const service = useFlow();
|
|
1312
|
+
const hasNewEdge = createState(service, { selector: (s) => !!s.context.newEdge });
|
|
1088
1313
|
onMount(() => {
|
|
1089
|
-
service.
|
|
1314
|
+
service.resume();
|
|
1090
1315
|
service.send({
|
|
1091
1316
|
type: "CONFIGURE",
|
|
1092
1317
|
payload: {
|
|
@@ -1095,8 +1320,8 @@ var FlowChart = (props) => {
|
|
|
1095
1320
|
}
|
|
1096
1321
|
});
|
|
1097
1322
|
});
|
|
1098
|
-
onCleanup(service.
|
|
1099
|
-
return ssr(_tmpl$, ssrHydrationKey(), ssrStyleProperty("cursor:",
|
|
1323
|
+
onCleanup(service.pause);
|
|
1324
|
+
return ssr(_tmpl$, ssrHydrationKey(), ssrStyleProperty("cursor:", hasNewEdge() ? "inherit" : "crosshair") + ssrStyleProperty(";background-image:", "radial-gradient(circle, #b8b8b8bf 1px, rgba(0, 0, 0, 0) 1px)"), escape(createComponent(NodesBoard, {})));
|
|
1100
1325
|
};
|
|
1101
1326
|
//#endregion
|
|
1102
1327
|
//#region src/ui/Flow.tsx
|
|
@@ -1108,6 +1333,8 @@ var FlowChart = (props) => {
|
|
|
1108
1333
|
* {@linkcode FlowProps}.
|
|
1109
1334
|
*
|
|
1110
1335
|
* @returns The rendered Solid component.
|
|
1336
|
+
*
|
|
1337
|
+
* @see {@linkcode Provider}
|
|
1111
1338
|
*/
|
|
1112
1339
|
var Flow = (props) => createComponent(Provider, { get children() {
|
|
1113
1340
|
return createComponent(FlowChart, props);
|