@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.
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 +653 -397
  8. package/lib/server.es.js +657 -401
  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} +8 -3
  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 +9 -15
  34. package/src/helpers/createContext.ts +7 -2
  35. package/src/{ui/components/FlowChart.data.ts → services/main.machine.data.ts} +7 -2
  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 -291
  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 +85 -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,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.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",
@@ -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
- /** 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
286
  /**
168
- * Constructs a unique edge identifier string from source and destination node IDs.
287
+ * Schema definition for an ongoing new connection edge creation preview between
288
+ * source node and cursor position.
169
289
  *
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
- /**
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 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,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
- 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
- /** 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
- 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
+ }
418
736
  });
419
- const zoom = (0, solid_js.createSignal)(1);
420
- const newEdge = (0, solid_js.createSignal)();
421
- const [boardRef, setBoardRef] = (0, solid_js.createSignal)();
422
- const [dimensions, setDimensions] = (0, solid_js.createSignal)({}, { equals: dequal_lite.dequal });
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.find((node) => node.id === payload);
742
+ const parentNode = nodes?.find((node) => node.id === payload);
458
743
  if (!parentNode) return nodes;
459
- const id = `node-${generatedId}`;
460
- const width = dimensions()[payload]?.width ?? 0;
461
- 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;
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
- return [...nodes, {
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("context.data.nodes", { ADD_PARENT: ({ context: { data }, pContext: { generatedId } }) => {
473
- const nodes = data?.nodes ?? [];
474
- const id = `node-${generatedId}`;
475
- 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;
476
766
  const scrollLeft = container?.scrollLeft ?? 0;
477
767
  const scrollTop = container?.scrollTop ?? 0;
478
- const width = container?.clientWidth ?? 0;
479
- const height = container?.clientHeight ?? 0;
480
- const currentZoom = zoom[0]();
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
- return [...nodes, {
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
- x,
489
- y
490
- }
491
- }];
779
+ position
780
+ });
781
+ pContext.dimensions[id] = dimension;
782
+ return nodes;
492
783
  } }),
493
- placeSibling: assign("context.data.nodes", { ADD_SIBLING: ({ payload, context: { data }, pContext: { generatedId } }) => {
494
- const edges = data?.edges ?? [];
495
- const nodes = data?.nodes ?? [];
496
- 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;
497
789
  if (!parentID) return nodes;
498
- const parentNode = nodes.find((node) => node.id === parentID);
790
+ const parentNode = nodes?.find((node) => node.id === parentID);
499
791
  if (!parentNode) return nodes;
500
- const id = `node-${generatedId}`;
501
- const width = dimensions()[parentID]?.width ?? 0;
502
- 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;
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
- return [...nodes, {
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
- }), voidAction({
520
- else: ({ context: { data } }) => {
521
- const edges = data?.edges ?? [];
522
- setEdgesPositions((0, solid_js_store.produce)((next) => {
523
- edges?.forEach(({ from, id, to }) => {
524
- const output = dimensions()[from]?.output;
525
- const input = dimensions()[to]?.input;
526
- if (output && input) next[id] = {
527
- x0: output.x,
528
- y0: output.y,
529
- x1: input.x,
530
- y1: input.y
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 { 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 });
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 = ref();
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.clientWidth / currentZoom - node.offsetWidth - BOUNDS_CONSTRAINTS.horizontal);
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.clientHeight / currentZoom - node.offsetHeight - BOUNDS_CONSTRAINTS.vertical);
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 [middlePoint, setMiddlePoint] = (0, solid_js.createSignal)({
727
- x: props.x0 + (props.x1 - props.x0) / 2,
728
- y: props.y0 + (props.y1 - props.y0) / 2
729
- });
730
- const { service } = useFlow();
731
- (0, solid_js.createEffect)(() => {
732
- const middleX = props.x0 + (props.x1 - props.x0) / 2;
733
- const middleY = props.y0 + (props.y1 - props.y0) / 2;
734
- setMiddlePoint({
735
- x: middleX,
736
- y: middleY
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.useState)(service, { selector: (s) => s.context.selected === props.id });
740
- 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, {
741
949
  get when() {
742
- return selected();
950
+ return vector();
743
951
  },
744
- get children() {
745
- 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"));
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 [selected, setSelected] = (0, solid_js.createSignal)();
765
- const { newEdge: [newEdge], edgesPositions: [edgesPositions] } = useFlow();
766
- const datas = (0, solid_js.createMemo)(() => {
767
- return Object.entries(edgesPositions()).map(([id, vector]) => ({
768
- id,
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 newEdge();
987
+ return hasNewEdge();
778
988
  },
779
- children: (value) => (0, solid_js_web.createComponent)(EdgeComponent, {
780
- id: "__#new-edge#__TEMP",
781
- isNew: true,
782
- get x0() {
783
- return value().x0;
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 datas();
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
- "\"><div class=\"",
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
- "\" 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><!--$-->",
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 [ref, setRef] = (0, solid_js.createSignal)();
850
- const { dimensions: [dimensions, setDimensions], newEdge: [newEdge, setNewEdge], getBoardPoint, service, zoom: [zoom] } = useFlow();
851
- const selected = (0, _bemedev_app_solidjs.useState)(service, { selector: (s) => s.context.selected === props.id });
852
- (0, solid_js.onMount)(() => {
853
- ref();
854
- 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
855
1066
  });
856
- 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 } }) => {
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)() + (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, {
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 props.label;
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 props.content;
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 props.input || hasParent();
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-scroll rounded-lg border-2 border-gray-600\"><!--$-->",
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 [previousZoom, setPreviousZoom] = (0, solid_js.createSignal)();
1142
+ const [ref, setRef] = (0, solid_js.createSignal)();
928
1143
  let percentX = 0;
929
1144
  let percentY = 0;
930
- 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
+ };
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
- 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 });
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 nodes = (0, _bemedev_app_solidjs.useState)(service, {
940
- selector: (s) => {
941
- return (s.context.data?.nodes ?? []).map((item) => ({
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: dequal.dequal
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 nodes();
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 { service, newEdge: [newEdge, setNewEdge], getBoardPoint } = useFlow();
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.start();
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.stop);
1071
- 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, {})));
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);