@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.
Files changed (51) hide show
  1. package/README.md +10 -10
  2. package/lib/helpers/createContext.d.ts +7 -2
  3. package/lib/helpers/createContext.d.ts.map +1 -1
  4. package/lib/index.cjs.js +1 -1
  5. package/lib/index.es.js +556 -510
  6. package/lib/output.css +1 -1
  7. package/lib/server.cjs.js +645 -418
  8. package/lib/server.es.js +649 -422
  9. package/lib/services/main.machine.d.ts +4857 -218
  10. package/lib/services/main.machine.d.ts.map +1 -1
  11. package/lib/{ui/components/FlowChart.data.d.ts → services/main.machine.data.d.ts} +4 -2
  12. package/lib/services/main.machine.data.d.ts.map +1 -0
  13. package/lib/services/main.machine.helpers.d.ts +32 -0
  14. package/lib/services/main.machine.helpers.d.ts.map +1 -0
  15. package/lib/services/main.machine.typings.d.ts +126 -0
  16. package/lib/services/main.machine.typings.d.ts.map +1 -0
  17. package/lib/ui/Flow.d.ts +2 -0
  18. package/lib/ui/Flow.d.ts.map +1 -1
  19. package/lib/ui/components/Bounds.d.ts +2 -0
  20. package/lib/ui/components/Bounds.d.ts.map +1 -1
  21. package/lib/ui/components/EdgeComponent.d.ts +3 -2
  22. package/lib/ui/components/EdgeComponent.d.ts.map +1 -1
  23. package/lib/ui/components/EdgesBoard.d.ts +2 -0
  24. package/lib/ui/components/EdgesBoard.d.ts.map +1 -1
  25. package/lib/ui/components/FlowChart.context.d.ts +5608 -996
  26. package/lib/ui/components/FlowChart.context.d.ts.map +1 -1
  27. package/lib/ui/components/FlowChart.d.ts +3 -1
  28. package/lib/ui/components/FlowChart.d.ts.map +1 -1
  29. package/lib/ui/components/NodeComponent.d.ts +2 -10
  30. package/lib/ui/components/NodeComponent.d.ts.map +1 -1
  31. package/lib/ui/components/NodesBoard.d.ts +2 -0
  32. package/lib/ui/components/NodesBoard.d.ts.map +1 -1
  33. package/package.json +8 -8
  34. package/src/helpers/createContext.ts +7 -2
  35. package/src/{ui/components/FlowChart.data.ts → services/main.machine.data.ts} +3 -1
  36. package/src/services/main.machine.helpers.ts +55 -0
  37. package/src/services/main.machine.ts +288 -70
  38. package/src/services/main.machine.typings.ts +98 -0
  39. package/src/ui/Flow.tsx +2 -0
  40. package/src/ui/components/Bounds.tsx +13 -8
  41. package/src/ui/components/EdgeComponent.tsx +78 -56
  42. package/src/ui/components/EdgesBoard.tsx +13 -36
  43. package/src/ui/components/FlowChart.context.ts +204 -310
  44. package/src/ui/components/FlowChart.tsx +16 -16
  45. package/src/ui/components/NodeComponent.tsx +60 -104
  46. package/src/ui/components/NodesBoard.tsx +74 -47
  47. package/src/ui/components/classes.ts +1 -1
  48. package/lib/services/main.typings.d.ts +0 -68
  49. package/lib/services/main.typings.d.ts.map +0 -1
  50. package/lib/ui/components/FlowChart.data.d.ts.map +0 -1
  51. 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-scroll",
36
+ "overflow-auto",
39
37
  "h-[150vh]",
40
38
  "w-[2160px]",
41
39
  "bg-size-[30px_30px]",
@@ -95,6 +93,50 @@ 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
  /**
@@ -106,8 +148,10 @@ var CLASSES = [
106
148
  * `NonNullable<T>`.
107
149
  * @param options - Optional context configuration of type {@linkcode Options}.
108
150
  *
109
- * @returns A tuple containing the Provider component of interface
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.typings.ts
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",
@@ -138,7 +241,11 @@ var extremities = (0, _bemedev_app_bemedev.type)({
138
241
  from: "string",
139
242
  to: "string"
140
243
  });
141
- /** Schema definition for a serialized flowchart node entity. */
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
- /** Schema definition for a serialized flowchart edge entity. */
257
+ /**
258
+ * Schema definition for a serialized flowchart edge entity.
259
+ *
260
+ * @see {@linkcode extremities}
261
+ */
151
262
  var edgeJSON = extremities;
152
- (0, _bemedev_app_bemedev.type)(({ optional, use }) => ({
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
- (0, _bemedev_app_bemedev.type)({
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
- /**
168
- * Constructs a unique edge identifier string from source and destination node IDs.
169
- *
170
- * @param out - The source node ID string.
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
286
  /**
179
- * Constructs a formatted node identifier string from a generated ID.
287
+ * Schema definition for an ongoing new connection edge creation preview between
288
+ * source node and cursor position.
180
289
  *
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 buildNodeID = (generated) => {
186
- return `node-${generated}`;
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: "buildImmediateUI",
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 }) => ({ generatedId: union("string", "null") })),
287
- context: (0, _bemedev_app_bemedev.type)(({ optional, use, array }) => ({
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("context.data", () => ({
303
- nodes: [],
304
- edges: []
305
- })), assign("context.data.nodes", { CONFIGURE: ({ payload: { nodes } }) => nodes }), assign("context.data.edges", { CONFIGURE: ({ payload: { edges } }) => edges }), assign("context.updatingUI", () => false), assign("pContext.generatedId", () => null)),
306
- generateID: assign("pContext.generatedId", () => (0, nanoid.nanoid)()),
307
- linkChild: batch(assign("context.data.edges", { ADD_CHILD: ({ context, pContext, payload }) => {
308
- const data = context.data;
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
- return [...data?.edges ?? [], {
562
+ edges.push({
314
563
  id,
315
564
  from,
316
565
  to
317
- }];
318
- } }), assign("context.selected", ({ pContext: { generatedId } }) => buildNodeID(generatedId))),
319
- linkSibling: batch(assign("context.data.edges", { ADD_SIBLING: ({ pContext, payload, context }) => {
320
- const edges = context.data?.edges;
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 out = [...edges ?? []];
323
- const from = edges?.find(({ to }) => to === payload)?.from;
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
- out.push({
576
+ edges.push({
328
577
  from,
329
578
  to,
330
579
  id
331
580
  });
332
- return out;
333
- } }), assign("context.selected", ({ pContext: { generatedId } }) => buildNodeID(generatedId))),
334
- selectParent: assign("context.selected", ({ pContext: { generatedId } }) => buildNodeID(generatedId)),
335
- moveNode: assign("context.data.nodes", { MOVE: ({ context: { data }, payload }) => {
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("context.selected", { SELECT: ({ payload }) => payload }),
350
- delete: assign(["context.data.nodes", "context.data.edges"], { DELETE: ({ context: { data }, payload }) => {
351
- return [data?.nodes?.filter(({ id }) => id !== payload), data?.edges?.filter(({ id, from, to }) => id !== payload && from !== payload && to !== payload)];
352
- } }),
353
- addEdge: assign("context.data.edges", { ADD_EDGE: ({ context, payload: { from, to } }) => {
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,304 +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
- deselect: erase("context.selected")
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
- /**
392
- * Default node items of type {@linkcode NodeProps} provided when no initial
393
- * configuration is given.
394
- */
395
- var DEFAULT_NODES = [{
396
- id: "node-0",
397
- data: {
398
- content: "Some text",
399
- label: "Root node"
400
- },
401
- input: false,
402
- position: {
403
- x: 350,
404
- y: 100
405
- }
406
- }];
407
629
  //#endregion
408
630
  //#region src/ui/components/FlowChart.context.ts
409
631
  /**
410
632
  * Solid Context Provider component and hook for accessing flowchart board state,
411
633
  * services, and zoom.
634
+ *
635
+ * @see {@linkcode machine}, {@linkcode createContext}
412
636
  */
413
637
  var [Provider, useFlow] = createContext(() => {
414
- /**
415
- * Shared state machine interpreter service instance for flowchart state
416
- * management.
417
- */
638
+ /** Shared service for flowchart state management. */
418
639
  const service = (0, _bemedev_app.interpret)(machine, {
419
- context: {},
420
- pContext: { generatedId: null }
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
+ }
421
736
  });
422
- const zoom = (0, solid_js.createSignal)(1);
423
- const newEdge = (0, solid_js.createSignal)();
424
- const [boardRef, setBoardRef] = (0, solid_js.createSignal)();
425
- const [dimensions, setDimensions] = (0, solid_js.createSignal)({}, { equals: dequal_lite.dequal });
426
- /**
427
- * Converts client viewport coordinates to board canvas coordinates adjusted for
428
- * zoom and scroll.
429
- *
430
- * @param clientX - Viewport horizontal coordinate.
431
- * @param clientY - Viewport vertical coordinate.
432
- *
433
- * @returns Board coordinate point of type {@linkcode Point}.
434
- */
435
- const getBoardPoint = (clientX, clientY) => {
436
- const el = boardRef();
437
- if (!el) return {
438
- x: clientX,
439
- y: clientY
440
- };
441
- const rect = el.getBoundingClientRect();
442
- const currentZoom = zoom[0]();
443
- return {
444
- x: clientX - rect.left / currentZoom,
445
- y: clientY - rect.top / currentZoom
446
- };
447
- };
448
- const [edgesPositions, setEdgesPositions] = (0, solid_js.createSignal)({}, { equals: false });
449
- /**
450
- * Clamps node coordinates within the visible container boundaries.
451
- *
452
- * @param x - Desired horizontal X coordinate.
453
- * @param y - Desired vertical Y coordinate.
454
- * @param nodeWidth - Width of the node element in pixels, defaults to `192`.
455
- * @param nodeHeight - Height of the node element in pixels, defaults to `50`.
456
- *
457
- * @returns Clamped coordinate point of type {@linkcode Point}.
458
- */
459
- const clampPosition = (x, y, nodeWidth = 192, nodeHeight = 50) => {
460
- const container = boardRef()?.parentElement;
461
- if (!container) return {
462
- x,
463
- y
464
- };
465
- const currentZoom = zoom[0]();
466
- const minX = container.scrollLeft / currentZoom + BOUNDS_CONSTRAINTS.horizontal;
467
- const maxX = Math.max(minX, (container.scrollLeft + container.clientWidth) / currentZoom - BOUNDS_CONSTRAINTS.horizontal - nodeWidth);
468
- const minY = container.scrollTop / currentZoom + BOUNDS_CONSTRAINTS.vertical;
469
- const maxY = Math.max(minY, (container.scrollTop + container.clientHeight) / currentZoom - BOUNDS_CONSTRAINTS.vertical - nodeHeight);
470
- return {
471
- x: Math.min(Math.max(x, minX), maxX),
472
- y: Math.min(Math.max(y, minY), maxY)
473
- };
474
- };
475
- service.addOptions(({ voidAction, batch, assign }) => ({ actions: {
476
- placeChild: assign("context.data.nodes", { ADD_CHILD: ({ payload, context: { data }, pContext: { generatedId } }) => {
477
- 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;
478
741
  if (!payload) return nodes;
479
- const parentNode = nodes.find((node) => node.id === payload);
742
+ const parentNode = nodes?.find((node) => node.id === payload);
480
743
  if (!parentNode) return nodes;
481
- const id = `node-${generatedId}`;
482
- const width = dimensions()[payload]?.width ?? 0;
483
- const height = dimensions()[payload]?.height ?? 0;
744
+ const parentDimension = pContext.dimensions[payload];
745
+ const id = `node-${pContext.generatedId}`;
746
+ const width = parentDimension?.width ?? 192;
747
+ const height = parentDimension?.height ?? 50;
484
748
  const initialX = parentNode.position.x + width + 100;
485
749
  const initialY = parentNode.position.y;
486
- const position = clampPosition(initialX, initialY, width, height);
487
- return [...nodes, {
750
+ const position = pContext.clampPosition(board, initialX, initialY, width, height);
751
+ const dimension = pContext.calculateDimensions(position, parentDimension);
752
+ nodes?.push({
488
753
  id,
489
754
  data: { content: "<Nouveau nœud>" },
490
755
  input: true,
491
756
  position
492
- }];
757
+ });
758
+ pContext.dimensions[id] = dimension;
759
+ return nodes;
493
760
  } }),
494
- placeParent: assign("context.data.nodes", { ADD_PARENT: ({ context: { data }, pContext: { generatedId } }) => {
495
- const nodes = data?.nodes ?? [];
496
- const id = `node-${generatedId}`;
497
- const container = boardRef()?.parentElement;
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;
498
766
  const scrollLeft = container?.scrollLeft ?? 0;
499
767
  const scrollTop = container?.scrollTop ?? 0;
500
- const width = container?.clientWidth ?? 0;
501
- const height = container?.clientHeight ?? 0;
502
- const currentZoom = zoom[0]();
768
+ const width = container?.width ?? 0;
769
+ const height = container?.height ?? 0;
770
+ const currentZoom = zoom;
503
771
  const x = (scrollLeft + width / 2) / currentZoom;
504
772
  const y = (scrollTop + height / 2) / currentZoom;
505
- return [...nodes, {
773
+ const position = pContext.clampPosition(board, x, y);
774
+ const dimension = pContext.calculateDimensions(position);
775
+ nodes?.push({
506
776
  id,
507
777
  data: { content: "<Nouveau nœud>" },
508
778
  input: false,
509
- position: {
510
- x,
511
- y
512
- }
513
- }];
779
+ position
780
+ });
781
+ pContext.dimensions[id] = dimension;
782
+ return nodes;
514
783
  } }),
515
- placeSibling: assign("context.data.nodes", { ADD_SIBLING: ({ payload, context: { data }, pContext: { generatedId } }) => {
516
- const edges = data?.edges ?? [];
517
- const nodes = data?.nodes ?? [];
518
- const parentID = edges.find((edge) => edge.to === payload)?.from;
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;
519
789
  if (!parentID) return nodes;
520
- const parentNode = nodes.find((node) => node.id === parentID);
790
+ const parentNode = nodes?.find((node) => node.id === parentID);
521
791
  if (!parentNode) return nodes;
522
- const id = `node-${generatedId}`;
523
- const width = dimensions()[parentID]?.width ?? 0;
524
- const height = dimensions()[parentID]?.height ?? 0;
792
+ const parentDimension = pContext.dimensions[parentID];
793
+ const id = `node-${pContext.generatedId}`;
794
+ const width = parentDimension?.width ?? 192;
795
+ const height = parentDimension?.height ?? 50;
525
796
  const initialX = parentNode.position.x + width + 100;
526
797
  const initialY = parentNode.position.y + 100;
527
- const position = clampPosition(initialX, initialY, width, height);
528
- return [...nodes, {
798
+ const position = pContext.clampPosition(board, initialX, initialY, width, height);
799
+ const dimension = pContext.calculateDimensions(position, parentDimension);
800
+ nodes?.push({
529
801
  id,
530
802
  data: { content: "<Nouveau nœud>" },
531
803
  input: true,
532
804
  position
533
- }];
534
- } }),
535
- buildUI: batch(voidAction(({ context: { data } }) => {
536
- const edges = data?.edges ?? [];
537
- setEdgesPositions((data) => {
538
- const array = Object.entries({ ...data }).filter(([id]) => edges.some((edge) => edge.id === id));
539
- return Object.fromEntries(array);
540
805
  });
541
- }), voidAction({
542
- else: ({ context: { data } }) => {
543
- const edges = data?.edges ?? [];
544
- setEdgesPositions((0, solid_js_store.produce)((next) => {
545
- edges?.forEach(({ from, id, to }) => {
546
- const output = dimensions()[from]?.output;
547
- const input = dimensions()[to]?.input;
548
- if (output && input) next[id] = {
549
- x0: output.x,
550
- y0: output.y,
551
- x1: input.x,
552
- y1: input.y
553
- };
554
- });
555
- }));
556
- },
557
- MOVE: ({ context: { data }, payload }) => {
558
- const edges = data?.edges ?? [];
559
- setEdgesPositions((0, solid_js_store.produce)((next) => {
560
- edges?.forEach(({ from, to, id }) => {
561
- if (from === payload.id) {
562
- const offset = dimensions()[payload.id]?.outputOffset ?? getDefaultOutputOffset(dimensions()[payload.id]?.width);
563
- const x0 = payload.x + offset.x;
564
- const y0 = payload.y + offset.y;
565
- next[id] = {
566
- ...next[id],
567
- x0,
568
- y0
569
- };
570
- setDimensions((0, solid_js_store.produce)((data) => {
571
- if (data[payload.id]) data[payload.id] = {
572
- ...data[payload.id],
573
- output: {
574
- x: x0,
575
- y: y0
576
- }
577
- };
578
- }));
579
- }
580
- if (to === payload.id) {
581
- const offset = dimensions()[payload.id]?.inputOffset ?? DEFAULT_INPUT_OFFSET;
582
- const x1 = payload.x + offset.x;
583
- const y1 = payload.y + offset.y;
584
- next[id] = {
585
- ...next[id],
586
- x1,
587
- y1
588
- };
589
- setDimensions((0, solid_js_store.produce)((data) => {
590
- if (data[payload.id]) data[payload.id] = {
591
- ...data[payload.id],
592
- input: {
593
- x: x1,
594
- y: y1
595
- }
596
- };
597
- }));
598
- }
599
- });
600
- }));
601
- }
602
- }), assign("context.updatingUI", () => true)),
603
- buildImmediateUI: voidAction({ MOVE_IMMEDIATE: ({ context: { data }, payload }) => {
604
- const edges = data?.edges ?? [];
605
- setEdgesPositions((0, solid_js_store.produce)((next) => {
606
- edges?.forEach(({ from, to, id }) => {
607
- if (from === payload.id) {
608
- const offset = dimensions()[payload.id]?.outputOffset ?? getDefaultOutputOffset(dimensions()[payload.id]?.width);
609
- const x0 = payload.x + offset.x;
610
- const y0 = payload.y + offset.y;
611
- next[id] = {
612
- ...next[id],
613
- x0,
614
- y0
615
- };
616
- setDimensions((0, solid_js_store.produce)((data) => {
617
- if (data[payload.id]) data[payload.id] = {
618
- ...data[payload.id],
619
- output: {
620
- x: x0,
621
- y: y0
622
- }
623
- };
624
- }));
625
- }
626
- if (to === payload.id) {
627
- const offset = dimensions()[payload.id]?.inputOffset ?? DEFAULT_INPUT_OFFSET;
628
- const x1 = payload.x + offset.x;
629
- const y1 = payload.y + offset.y;
630
- next[id] = {
631
- ...next[id],
632
- x1,
633
- y1
634
- };
635
- setDimensions((0, solid_js_store.produce)((data) => {
636
- if (data[payload.id]) data[payload.id] = {
637
- ...data[payload.id],
638
- input: {
639
- x: x1,
640
- y: y1
641
- }
642
- };
643
- }));
644
- }
645
- });
646
- }));
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
+ };
647
818
  } })
648
819
  } }));
649
820
  service.start();
650
- return {
651
- dimensions: [dimensions, setDimensions],
652
- newEdge,
653
- board: [boardRef, setBoardRef],
654
- getBoardPoint,
655
- edgesPositions: [edgesPositions, setEdgesPositions],
656
- service,
657
- zoom,
658
- clampPosition
659
- };
821
+ return service;
660
822
  }, { name: "FlowContext" });
661
823
  //#endregion
662
824
  //#region src/ui/components/Bounds.tsx
@@ -666,24 +828,28 @@ var [Provider, useFlow] = createContext(() => {
666
828
  *
667
829
  * @returns `null` as this component performs side-effect transformer registrations
668
830
  * only.
831
+ *
832
+ * @see {@linkcode useFlow}, {@linkcode BOUNDS_CONSTRAINTS}
669
833
  */
670
834
  var DragBounds = () => {
671
- const { board: [ref], zoom: [zoom] } = useFlow();
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 });
672
838
  const [state, { addTransformer, removeTransformer, onDragStart, onDragEnd }] = (0, _thisbeyond_solid_dnd.useDragDropContext)();
673
839
  const transformer = {
674
840
  id: "clamp-to-container",
675
841
  order: 100,
676
842
  callback: (transform) => {
677
- const container = ref();
843
+ const container = board()?.self;
678
844
  const activeDraggable = state.active.draggable;
679
845
  if (!container || !activeDraggable) return transform;
680
846
  const node = activeDraggable.node;
681
847
  if (!node) return transform;
682
848
  const currentZoom = zoom();
683
849
  const minBoardX = BOUNDS_CONSTRAINTS.horizontal;
684
- const maxBoardX = Math.max(minBoardX, container.clientWidth / currentZoom - node.offsetWidth - BOUNDS_CONSTRAINTS.horizontal);
850
+ const maxBoardX = Math.max(minBoardX, container.width / currentZoom - node.offsetWidth - BOUNDS_CONSTRAINTS.horizontal);
685
851
  const minBoardY = BOUNDS_CONSTRAINTS.vertical;
686
- const maxBoardY = Math.max(minBoardY, container.clientHeight / currentZoom - node.offsetHeight - BOUNDS_CONSTRAINTS.vertical);
852
+ const maxBoardY = Math.max(minBoardY, container.height / currentZoom - node.offsetHeight - BOUNDS_CONSTRAINTS.vertical);
687
853
  const minTransformX = (minBoardX - node.offsetLeft) * currentZoom;
688
854
  const maxTransformX = (maxBoardX - node.offsetLeft) * currentZoom;
689
855
  const minTransformY = (minBoardY - node.offsetTop) * currentZoom;
@@ -732,6 +898,8 @@ var calculateOffset = (value) => value * 100 / 200;
732
898
  * @param vector - Vector coordinates of type {@linkcode Vector}.
733
899
  *
734
900
  * @returns SVG cubic bezier path string.
901
+ *
902
+ * @see {@linkcode calculateOffset}
735
903
  */
736
904
  var draw = ({ x0, y0, x1, y1 }) => {
737
905
  return `M ${x0} ${y0} C ${x0 + calculateOffset(Math.abs(x1 - x0))} ${y0}, ${x1 - calculateOffset(Math.abs(x1 - x0))} ${y1}, ${x1} ${y1}`;
@@ -743,30 +911,53 @@ var draw = ({ x0, y0, x1, y1 }) => {
743
911
  * @param props - Edge properties of type {@linkcode Props}.
744
912
  *
745
913
  * @returns The rendered SVG JSX elements.
914
+ *
915
+ * @see {@linkcode draw}, {@linkcode useFlow}
746
916
  */
747
917
  var EdgeComponent = (props) => {
748
- const [middlePoint, setMiddlePoint] = (0, solid_js.createSignal)({
749
- x: props.x0 + (props.x1 - props.x0) / 2,
750
- y: props.y0 + (props.y1 - props.y0) / 2
751
- });
752
- const { service } = useFlow();
753
- (0, solid_js.createEffect)(() => {
754
- const middleX = props.x0 + (props.x1 - props.x0) / 2;
755
- const middleY = props.y0 + (props.y1 - props.y0) / 2;
756
- setMiddlePoint({
757
- x: middleX,
758
- y: middleY
759
- });
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
760
934
  });
761
- const selected = (0, _bemedev_app_solidjs.useState)(service, { selector: (s) => s.context.selected === props.id });
762
- return [(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(props), true), false)), (0, solid_js_web.createComponent)(solid_js.Show, {
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, {
763
949
  get when() {
764
- return selected();
950
+ return vector();
765
951
  },
766
- get children() {
767
- 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"));
768
- }
769
- })];
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
+ });
770
961
  };
771
962
  //#endregion
772
963
  //#region src/ui/components/EdgesBoard.tsx
@@ -781,44 +972,31 @@ var _tmpl$$3 = [
781
972
  * edge creation previews.
782
973
  *
783
974
  * @returns The rendered SVG JSX element.
975
+ *
976
+ * @see {@linkcode EdgeComponent}, {@linkcode useFlow}
784
977
  */
785
978
  var EdgesBoard = () => {
786
- const [selected, setSelected] = (0, solid_js.createSignal)();
787
- const { newEdge: [newEdge], edgesPositions: [edgesPositions] } = useFlow();
788
- const datas = (0, solid_js.createMemo)(() => {
789
- return Object.entries(edgesPositions()).map(([id, vector]) => ({
790
- id,
791
- ...vector
792
- }));
793
- });
794
- (0, solid_js.createEffect)(() => {
795
- 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
796
984
  });
797
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, {
798
986
  get when() {
799
- return newEdge();
987
+ return hasNewEdge();
800
988
  },
801
- children: (value) => (0, solid_js_web.createComponent)(EdgeComponent, {
802
- id: "__#new-edge#__TEMP",
803
- isNew: true,
804
- get x0() {
805
- return value().x0;
806
- },
807
- get y0() {
808
- return value().y0;
809
- },
810
- get x1() {
811
- return value().x1;
812
- },
813
- get y1() {
814
- return value().y1;
815
- }
816
- })
989
+ get children() {
990
+ return (0, solid_js_web.createComponent)(EdgeComponent, {
991
+ id: "__#new-edge#__TEMP",
992
+ isNew: true
993
+ });
994
+ }
817
995
  })), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(solid_js.For, {
818
996
  get each() {
819
- return datas();
997
+ return edgeIds();
820
998
  },
821
- children: EdgeComponent
999
+ children: (id) => (0, solid_js_web.createComponent)(EdgeComponent, { id })
822
1000
  })));
823
1001
  };
824
1002
  //#endregion
@@ -838,11 +1016,12 @@ var _tmpl$3$1 = [
838
1016
  "<div",
839
1017
  " class=\"",
840
1018
  "\" style=\"",
841
- "\"><div class=\"",
1019
+ "\"",
1020
+ "><div class=\"",
842
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=\"",
843
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><!--$-->",
844
- "<!--/--><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=\"",
845
- "\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"></path></svg></div><!--$-->",
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><!--$-->",
846
1025
  "<!--/--><!--$-->",
847
1026
  "<!--/--><!--$-->",
848
1027
  "<!--/--><div id=\"inputs\" class=\"pointer-events-none absolute top-0 z-10 flex flex-col\" style=\"",
@@ -866,22 +1045,34 @@ var _tmpl$5 = [
866
1045
  * @param props - Node rendering properties of type {@linkcode Props}.
867
1046
  *
868
1047
  * @returns The rendered Solid component.
1048
+ *
1049
+ * @see {@linkcode useFlow}, {@linkcode HANDLE_CONTAINER_OFFSET_X}, {@linkcode HANDLE_MARGIN_TOP}, {@linkcode HANDLE_SIZE}
869
1050
  */
870
1051
  var NodeComponent = (props) => {
871
- const [ref, setRef] = (0, solid_js.createSignal)();
872
- const { dimensions: [dimensions, setDimensions], newEdge: [newEdge, setNewEdge], getBoardPoint, service, zoom: [zoom] } = useFlow();
873
- const selected = (0, _bemedev_app_solidjs.useState)(service, { selector: (s) => s.context.selected === props.id });
874
- (0, solid_js.onMount)(() => {
875
- ref();
876
- zoom();
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
877
1066
  });
878
- const hasParent = (0, _bemedev_app_solidjs.useState)(service, { selector: ({ context: { data } }) => {
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 } }) => {
879
1070
  const edges = data?.edges;
880
1071
  if (!edges) return false;
881
1072
  return edges.some(({ to }) => to === props.id);
882
1073
  } });
883
1074
  (0, _thisbeyond_solid_dnd.createDraggable)(props.id);
884
- return (0, solid_js_web.ssr)(_tmpl$3$1, (0, solid_js_web.ssrHydrationKey)() + (0, solid_js_web.ssrAttribute)("id", (0, solid_js_web.escape)(props.id, true), false), `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)(props.y, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";left:", `${(0, solid_js_web.escape)(props.x, true)}px`), `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, {
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, {
885
1076
  get when() {
886
1077
  return hasParent();
887
1078
  },
@@ -890,19 +1081,19 @@ var NodeComponent = (props) => {
890
1081
  }
891
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, {
892
1083
  get when() {
893
- return props.label;
1084
+ return node()?.label;
894
1085
  },
895
1086
  keyed: true,
896
1087
  children: (label) => (0, solid_js_web.ssr)(_tmpl$4, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.escape)(label))
897
1088
  })), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(solid_js.Show, {
898
1089
  get when() {
899
- return props.content;
1090
+ return node()?.content;
900
1091
  },
901
1092
  keyed: true,
902
1093
  children: (content) => (0, solid_js_web.ssr)(_tmpl$5, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.escape)(content))
903
1094
  })), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(solid_js.Show, {
904
1095
  get when() {
905
- return props.input || hasParent();
1096
+ return node()?.input || hasParent();
906
1097
  },
907
1098
  get children() {
908
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"));
@@ -913,7 +1104,7 @@ var NodeComponent = (props) => {
913
1104
  //#region src/ui/components/NodesBoard.tsx
914
1105
  var _tmpl$$1 = [
915
1106
  "<div",
916
- " class=\"relative h-full w-full overflow-scroll rounded-lg border-2 border-gray-600\"><!--$-->",
1107
+ " class=\"relative h-full w-full overflow-auto rounded-lg border-2 border-gray-600\"><!--$-->",
917
1108
  "<!--/--><div class=\"",
918
1109
  "\" style=\"",
919
1110
  "\"><div class=\"relative h-full w-full\" style=\"",
@@ -937,6 +1128,8 @@ var _tmpl$3 = [
937
1128
  * panning gestures, and rendered nodes/edges.
938
1129
  *
939
1130
  * @returns The rendered Solid component.
1131
+ *
1132
+ * @see {@linkcode DragBounds}, {@linkcode EdgesBoard}, {@linkcode NodeComponent}, {@linkcode useFlow}, {@linkcode CANVAS_FACTOR}, {@linkcode SCROLL_MULTIPLIER}
940
1133
  */
941
1134
  var NodesBoard = () => {
942
1135
  let containerRef;
@@ -946,17 +1139,51 @@ var NodesBoard = () => {
946
1139
  y: 0
947
1140
  });
948
1141
  const [id, setId] = (0, solid_js.createSignal)("");
949
- const [previousZoom, setPreviousZoom] = (0, solid_js.createSignal)();
1142
+ const [ref, setRef] = (0, solid_js.createSignal)();
950
1143
  let percentX = 0;
951
1144
  let percentY = 0;
952
- const { board: [, setRef], service, newEdge: [newEdge], zoom: [zoom, setZoom] } = useFlow();
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
+ };
953
1179
  (0, solid_js.createEffect)((0, solid_js.on)(zoom, () => {
954
1180
  const maxScrollX = containerRef.scrollWidth - containerRef.clientWidth;
955
1181
  const maxScrollY = containerRef.scrollHeight - containerRef.clientHeight;
956
1182
  if (maxScrollX > 0) containerRef.scrollLeft = percentX * maxScrollX;
957
1183
  if (maxScrollY > 0) containerRef.scrollTop = percentY * maxScrollY;
958
1184
  }, { defer: true }));
959
- const selectedId = (0, _bemedev_app_solidjs.useState)(service, { selector: (s) => s.context?.selected });
1185
+ (0, solid_js.onMount)(sendBoard);
1186
+ const selectedId = (0, _bemedev_app_solidjs.createState)(service, { selector: (s) => s.context?.selected });
960
1187
  /**
961
1188
  * Determines whether the given element ID matches the currently selected entity.
962
1189
  *
@@ -965,23 +1192,18 @@ var NodesBoard = () => {
965
1192
  * @returns `true` if the item is currently selected, otherwise `false`.
966
1193
  */
967
1194
  const selected = (id) => selectedId() === id;
968
- const nodes = (0, _bemedev_app_solidjs.useState)(service, {
969
- selector: (s) => {
970
- return (s.context.data?.nodes ?? []).map((item) => ({
971
- id: item.id,
972
- x: item.position.x,
973
- y: item.position.y,
974
- label: item.data.label,
975
- content: item.data.content ?? "",
976
- input: item.input
977
- }));
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);
978
1198
  },
979
- equals: dequal.dequal
1199
+ equals: (prev, next) => prev.length === next.length
980
1200
  });
981
1201
  const CANVAS_SIZE = 500;
982
1202
  const MARGIN_X = 265;
983
1203
  const MARGIN_Y = 425;
1204
+ /** Computes the dynamic canvas width string in CSS units adjusted for zoom. */
984
1205
  const cWidth = () => `calc((${CANVAS_SIZE}vw - ${MARGIN_X}px) * ${zoom()})`;
1206
+ /** Computes the dynamic canvas height string in CSS units adjusted for zoom. */
985
1207
  const cHeight = () => `calc((${CANVAS_SIZE}vh - ${MARGIN_Y}px) * ${zoom()})`;
986
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, {
987
1209
  onDragMove: ({ draggable: { transform: _transform, node, id } }) => {
@@ -1029,15 +1251,15 @@ var NodesBoard = () => {
1029
1251
  node.style.setProperty("top", Y + "px");
1030
1252
  node.style.setProperty("left", X + "px");
1031
1253
  node.style.removeProperty("transform");
1254
+ service.send({
1255
+ type: "MOVE",
1256
+ payload: {
1257
+ id: `${id}`,
1258
+ x: X,
1259
+ y: Y
1260
+ }
1261
+ });
1032
1262
  setTimeout(() => {
1033
- service.send({
1034
- type: "MOVE",
1035
- payload: {
1036
- id: `${id}`,
1037
- x: X,
1038
- y: Y
1039
- }
1040
- });
1041
1263
  setTransform({
1042
1264
  x: 0,
1043
1265
  y: 0
@@ -1048,9 +1270,9 @@ var NodesBoard = () => {
1048
1270
  return [
1049
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, {
1050
1272
  get each() {
1051
- return nodes();
1273
+ return nodeIds();
1052
1274
  },
1053
- children: NodeComponent
1275
+ children: (id) => (0, solid_js_web.createComponent)(NodeComponent, { id })
1054
1276
  }))),
1055
1277
  (0, solid_js_web.ssr)(_tmpl$2, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.escape)(Math.round(zoom() * 100))),
1056
1278
  (0, solid_js_web.createComponent)(solid_js.Show, {
@@ -1081,13 +1303,16 @@ var _tmpl$ = [
1081
1303
  * {@linkcode FlowProps}.
1082
1304
  *
1083
1305
  * @returns The rendered Solid component.
1306
+ *
1307
+ * @see {@linkcode NodesBoard}, {@linkcode useFlow}, {@linkcode DEFAULT_NODES}
1084
1308
  */
1085
1309
  var FlowChart = (props) => {
1086
1310
  const primaryNodes = props.config?.nodes ?? DEFAULT_NODES;
1087
1311
  const primaryEdges = props.config?.edges;
1088
- const { service, newEdge: [newEdge, setNewEdge], getBoardPoint } = useFlow();
1312
+ const service = useFlow();
1313
+ const hasNewEdge = (0, _bemedev_app_solidjs.createState)(service, { selector: (s) => !!s.context.newEdge });
1089
1314
  (0, solid_js.onMount)(() => {
1090
- service.start();
1315
+ service.resume();
1091
1316
  service.send({
1092
1317
  type: "CONFIGURE",
1093
1318
  payload: {
@@ -1096,8 +1321,8 @@ var FlowChart = (props) => {
1096
1321
  }
1097
1322
  });
1098
1323
  });
1099
- (0, solid_js.onCleanup)(service.stop);
1100
- return (0, solid_js_web.ssr)(_tmpl$, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.ssrStyleProperty)("cursor:", newEdge() ? "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, {})));
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, {})));
1101
1326
  };
1102
1327
  //#endregion
1103
1328
  //#region src/ui/Flow.tsx
@@ -1109,6 +1334,8 @@ var FlowChart = (props) => {
1109
1334
  * {@linkcode FlowProps}.
1110
1335
  *
1111
1336
  * @returns The rendered Solid component.
1337
+ *
1338
+ * @see {@linkcode Provider}
1112
1339
  */
1113
1340
  var Flow = (props) => (0, solid_js_web.createComponent)(Provider, { get children() {
1114
1341
  return (0, solid_js_web.createComponent)(FlowChart, props);