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