@bemedev/mind-flow 0.3.1 → 1.0.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 (115) hide show
  1. package/README.md +55 -12
  2. package/lib/helpers/clamp.d.ts +2 -0
  3. package/lib/helpers/clamp.d.ts.map +1 -0
  4. package/lib/helpers/createContext.d.ts +1 -1
  5. package/lib/helpers/createContext.d.ts.map +1 -1
  6. package/lib/helpers/index.d.ts +3 -0
  7. package/lib/helpers/index.d.ts.map +1 -0
  8. package/lib/index.cjs.js +1 -1
  9. package/lib/index.d.ts +9 -1
  10. package/lib/index.d.ts.map +1 -1
  11. package/lib/index.es.js +2543 -448
  12. package/lib/output.css +1 -1
  13. package/lib/server.cjs.js +1129 -397
  14. package/lib/server.es.js +1095 -407
  15. package/lib/services/main.machine.d.ts +2453 -576
  16. package/lib/services/main.machine.d.ts.map +1 -1
  17. package/lib/services/main.machine.data.d.ts +14 -1
  18. package/lib/services/main.machine.data.d.ts.map +1 -1
  19. package/lib/services/main.machine.helpers.d.ts +1 -1
  20. package/lib/services/main.machine.helpers.d.ts.map +1 -1
  21. package/lib/services/main.machine.test.d.ts +2 -0
  22. package/lib/services/main.machine.test.d.ts.map +1 -0
  23. package/lib/services/main.machine.typings.d.ts +26 -8
  24. package/lib/services/main.machine.typings.d.ts.map +1 -1
  25. package/lib/ui/Flow.d.ts +8 -4
  26. package/lib/ui/Flow.d.ts.map +1 -1
  27. package/lib/ui/components/Bounds.d.ts.map +1 -1
  28. package/lib/ui/components/EdgeComponent.d.ts.map +1 -1
  29. package/lib/ui/components/EdgesBoard.d.ts.map +1 -1
  30. package/lib/ui/components/EditPanel.d.ts +19 -0
  31. package/lib/ui/components/EditPanel.d.ts.map +1 -0
  32. package/lib/ui/components/EditPanel.hooks.d.ts +29 -0
  33. package/lib/ui/components/EditPanel.hooks.d.ts.map +1 -0
  34. package/lib/ui/components/EditPanel.types.d.ts +47 -0
  35. package/lib/ui/components/EditPanel.types.d.ts.map +1 -0
  36. package/lib/ui/components/FlowChart.context.d.ts +2452 -575
  37. package/lib/ui/components/FlowChart.context.d.ts.map +1 -1
  38. package/lib/ui/components/FlowChart.d.ts +8 -47
  39. package/lib/ui/components/FlowChart.d.ts.map +1 -1
  40. package/lib/ui/components/FlowChart.types.d.ts +87 -0
  41. package/lib/ui/components/FlowChart.types.d.ts.map +1 -0
  42. package/lib/ui/components/NodeComponent.d.ts +10 -12
  43. package/lib/ui/components/NodeComponent.d.ts.map +1 -1
  44. package/lib/ui/components/NodesBoard.d.ts +17 -2
  45. package/lib/ui/components/NodesBoard.d.ts.map +1 -1
  46. package/lib/ui/components/Panels.d.ts +12 -0
  47. package/lib/ui/components/Panels.d.ts.map +1 -0
  48. package/lib/ui/globals/components/atoms/TypingText.d.ts +29 -0
  49. package/lib/ui/globals/components/atoms/TypingText.d.ts.map +1 -0
  50. package/lib/ui/globals/components/molecules/FadingDots.d.ts +21 -0
  51. package/lib/ui/globals/components/molecules/FadingDots.d.ts.map +1 -0
  52. package/lib/ui/globals/components/organisms/LoadingFallback.d.ts +46 -0
  53. package/lib/ui/globals/components/organisms/LoadingFallback.d.ts.map +1 -0
  54. package/lib/ui/globals/constants/index.d.ts +3 -0
  55. package/lib/ui/globals/constants/index.d.ts.map +1 -0
  56. package/lib/ui/globals/constants/links.d.ts +4 -0
  57. package/lib/ui/globals/constants/links.d.ts.map +1 -0
  58. package/lib/ui/globals/constants/strings.d.ts +5 -0
  59. package/lib/ui/globals/constants/strings.d.ts.map +1 -0
  60. package/lib/ui/globals/directives/clickOutside.d.ts +9 -0
  61. package/lib/ui/globals/directives/clickOutside.d.ts.map +1 -0
  62. package/lib/ui/globals/directives/index.d.ts +16 -0
  63. package/lib/ui/globals/directives/index.d.ts.map +1 -0
  64. package/lib/ui/globals/directives/mouseOut.d.ts +33 -0
  65. package/lib/ui/globals/directives/mouseOut.d.ts.map +1 -0
  66. package/lib/ui/globals/directives/mouseOut.test.d.ts +2 -0
  67. package/lib/ui/globals/directives/mouseOut.test.d.ts.map +1 -0
  68. package/lib/ui/globals/directives/resize.d.ts +11 -0
  69. package/lib/ui/globals/directives/resize.d.ts.map +1 -0
  70. package/lib/ui/globals/helpers/espace.d.ts +17 -0
  71. package/lib/ui/globals/helpers/espace.d.ts.map +1 -0
  72. package/lib/ui/globals/signals/createTyping.d.ts +27 -0
  73. package/lib/ui/globals/signals/createTyping.d.ts.map +1 -0
  74. package/lib/ui/globals/types.d.ts +54 -0
  75. package/lib/ui/globals/types.d.ts.map +1 -0
  76. package/lib/ui/utils.d.ts +11 -0
  77. package/lib/ui/utils.d.ts.map +1 -0
  78. package/package.json +2 -3
  79. package/src/helpers/clamp.ts +3 -0
  80. package/src/helpers/createContext.ts +20 -4
  81. package/src/helpers/index.ts +2 -0
  82. package/src/index.ts +9 -1
  83. package/src/services/main.machine.data.ts +13 -2
  84. package/src/services/main.machine.helpers.ts +12 -4
  85. package/src/services/main.machine.test.ts +205 -0
  86. package/src/services/main.machine.ts +253 -75
  87. package/src/services/main.machine.typings.ts +31 -6
  88. package/src/ui/Flow.tsx +47 -8
  89. package/src/ui/components/Bounds.tsx +0 -2
  90. package/src/ui/components/EdgeComponent.tsx +60 -46
  91. package/src/ui/components/EdgesBoard.tsx +1 -2
  92. package/src/ui/components/EditPanel.hooks.ts +69 -0
  93. package/src/ui/components/EditPanel.tsx +89 -0
  94. package/src/ui/components/EditPanel.types.ts +49 -0
  95. package/src/ui/components/FlowChart.context.ts +22 -141
  96. package/src/ui/components/FlowChart.tsx +27 -64
  97. package/src/ui/components/FlowChart.types.ts +92 -0
  98. package/src/ui/components/NodeComponent.tsx +88 -65
  99. package/src/ui/components/NodesBoard.tsx +61 -56
  100. package/src/ui/components/Panels.tsx +40 -0
  101. package/src/ui/globals/components/atoms/TypingText.tsx +63 -0
  102. package/src/ui/globals/components/molecules/FadingDots.tsx +45 -0
  103. package/src/ui/globals/components/organisms/LoadingFallback.tsx +116 -0
  104. package/src/ui/globals/constants/index.ts +2 -0
  105. package/src/ui/globals/constants/links.ts +15 -0
  106. package/src/ui/globals/constants/strings.ts +5 -0
  107. package/src/ui/globals/directives/clickOutside.ts +18 -0
  108. package/src/ui/globals/directives/index.ts +16 -0
  109. package/src/ui/globals/directives/mouseOut.test.ts +115 -0
  110. package/src/ui/globals/directives/mouseOut.ts +98 -0
  111. package/src/ui/globals/directives/resize.ts +33 -0
  112. package/src/ui/globals/helpers/espace.ts +26 -0
  113. package/src/ui/globals/signals/createTyping.ts +82 -0
  114. package/src/ui/globals/types.ts +83 -0
  115. package/src/ui/utils.ts +14 -0
package/lib/server.es.js CHANGED
@@ -1,11 +1,189 @@
1
- import { createComponent, escape, ssr, ssrAttribute, ssrHydrationKey, ssrStyleProperty } from "solid-js/web";
1
+ import { isDefined, toArray, type } from "@bemedev/app/bemedev";
2
+ import { Dynamic, createComponent, escape, memo, mergeProps, ssr, ssrAttribute, ssrClassList, ssrElement, ssrHydrationKey, ssrStyle, ssrStyleProperty } from "solid-js/web";
2
3
  import { createState } from "@bemedev/app-solidjs";
3
- import { For, Show, createContext, createEffect, createSignal, on, onCleanup, onMount, useContext } from "solid-js";
4
- import { createMachine, interpret, toArray } from "@bemedev/app";
5
- import { toArray as toArray$1, type } from "@bemedev/app/bemedev";
4
+ import { For, Show, createComponent as createComponent$1, createContext as createContext$1, createEffect, createMemo, createSignal, on, onCleanup, onMount, splitProps, useContext } from "solid-js";
5
+ import { createMachine, deepEqual, interpret, toArray as toArray$1 } from "@bemedev/app";
6
6
  import { nanoid } from "nanoid";
7
7
  import { DragDropProvider, DragDropSensors, DragOverlay, createDraggable, useDragDropContext } from "@thisbeyond/solid-dnd";
8
8
  import { dequal } from "dequal";
9
+ import { clsx } from "clsx";
10
+ import { twMerge } from "tailwind-merge";
11
+ //#region src/services/main.machine.data.ts
12
+ /** Horizontal gap in pixels between a parent node and newly created child node. */
13
+ var PARENT_CHILD_GAP_WIDTH = 100;
14
+ /** Default width and height dimensions for newly created flowchart nodes. */
15
+ var DEFAULT_SIZE = {
16
+ width: 192,
17
+ height: 50
18
+ };
19
+ /** Default node width in pixels. */
20
+ var DEFAULT_NODE_WIDTH = DEFAULT_SIZE.width;
21
+ /** Default node height in pixels. */
22
+ var DEFAULT_NODE_HEIGHT = DEFAULT_SIZE.height;
23
+ /** Default node data payload applied when creating new nodes. */
24
+ var DEFAULT_DATA = { content: "<Nouveau nœud>" };
25
+ /** Border width in pixels for a node container. */
26
+ var NODE_BORDER_WIDTH = 1.5;
27
+ /** Diameter in pixels of a node connection handle. */
28
+ var HANDLE_SIZE = 12;
29
+ /** Radius in pixels of a node connection handle. */
30
+ var HANDLE_RADIUS = 6;
31
+ /** Top margin in pixels for positioning connection handles. */
32
+ var HANDLE_MARGIN_TOP = 12;
33
+ /** Horizontal container offset in pixels for handle placement. */
34
+ var HANDLE_CONTAINER_OFFSET_X = 18;
35
+ /** Y-axis center coordinate in pixels of a handle. */
36
+ var HANDLE_CENTER_Y = 18;
37
+ /** X-axis center offset in pixels from the node container edge. */
38
+ var HANDLE_CENTER_X_OFFSET = 10.5;
39
+ /** Default X offset in pixels for input connection handles. */
40
+ var DEFAULT_INPUT_OFFSET_X = -10.5;
41
+ /** Default Y offset in pixels for input connection handles. */
42
+ var DEFAULT_INPUT_OFFSET_Y = 18;
43
+ /** Default 2D offset coordinates for input connection handles. */
44
+ var DEFAULT_INPUT_OFFSET = {
45
+ x: DEFAULT_INPUT_OFFSET_X,
46
+ y: 18
47
+ };
48
+ /** Dimensions representing width and height of the flow chart container canvas. */
49
+ var CONTAINER_DIMENSIONS = {
50
+ WIDTH: 5e3,
51
+ HEIGHT: 3500
52
+ };
53
+ /**
54
+ * Computes default output handle offset coordinates given a node width.
55
+ *
56
+ * @param width - Node width in pixels, defaults to `0`.
57
+ *
58
+ * @returns 2D offset coordinates for the output handle.
59
+ *
60
+ * @see {@linkcode HANDLE_CENTER_X_OFFSET}, {@linkcode HANDLE_CENTER_Y}
61
+ */
62
+ var getDefaultOutputOffset = (width = 0) => ({
63
+ x: width + HANDLE_CENTER_X_OFFSET,
64
+ y: 18
65
+ });
66
+ /** Top offset in pixels for node action toolbars. */
67
+ var TOOLBAR_TOP_OFFSET = 30;
68
+ /** Safety buffer in pixels applied above the toolbar. */
69
+ var TOOLBAR_BUFFER = 5;
70
+ /**
71
+ * Boundary padding constraints for node dragging and positioning within the
72
+ * viewport.
73
+ */
74
+ var BOUNDS_CONSTRAINTS = {
75
+ horizontal: 55,
76
+ vertical: 40
77
+ };
78
+ /** Multiplier factor used to compute canvas virtual scroll dimensions. */
79
+ var CANVAS_FACTOR = 5;
80
+ /** Multiplier factor used to compute canvas scroll dimensions. */
81
+ var SCROLL_MULTIPLIER = 3;
82
+ /**
83
+ * Default node items of type {@linkcode NodeProps} provided when no initial
84
+ * configuration is given.
85
+ */
86
+ var DEFAULT_NODES = [{
87
+ id: "node-0",
88
+ data: {
89
+ content: "Some text",
90
+ label: "Root node"
91
+ },
92
+ position: {
93
+ x: 350,
94
+ y: 100
95
+ }
96
+ }];
97
+ //#endregion
98
+ //#region src/services/main.machine.typings.ts
99
+ /** Schema definition for 2D coordinates `(x, y)`. */
100
+ var point = type({
101
+ x: "number",
102
+ y: "number"
103
+ });
104
+ /**
105
+ * Schema definition for node handle offsets (input and output).
106
+ *
107
+ * @see {@linkcode point}
108
+ */
109
+ var nodeOffset = type(({ use }) => ({
110
+ input: use(point),
111
+ output: use(point)
112
+ }));
113
+ /** Schema definition for edge extremities. */
114
+ var extremities = type({
115
+ from: "string",
116
+ to: "string"
117
+ });
118
+ /** Schema definition for primitive values allowed in node data. */
119
+ var nodeDataValue = type(({ union }) => union("string", "boolean", "number"));
120
+ /**
121
+ * Schema definition for serialized node data dictionary.
122
+ *
123
+ * @see {@linkcode nodeDataValue}
124
+ */
125
+ var data = type(({ record, use }) => record(use(nodeDataValue)));
126
+ /**
127
+ * Schema definition for a serialized flowchart node entity.
128
+ *
129
+ * @see {@linkcode point}, {@linkcode data}
130
+ */
131
+ var nodeJSON = type(({ use }) => ({
132
+ position: use(point),
133
+ data: use(data)
134
+ }));
135
+ /**
136
+ * Schema definition for a serialized flowchart edge entity.
137
+ *
138
+ * @see {@linkcode extremities}
139
+ */
140
+ var edgeJSON = extremities;
141
+ /**
142
+ * Schema definition for layout dimensions and connection points of a node.
143
+ *
144
+ * @see {@linkcode point}
145
+ */
146
+ var dimension = type(({ optional, use }) => ({
147
+ width: "number",
148
+ height: "number",
149
+ output: use(point),
150
+ input: optional(use(point)),
151
+ inputOffset: optional(use(point)),
152
+ outputOffset: optional(use(point))
153
+ }));
154
+ /**
155
+ * Schema definition for a 2D line vector representing edge coordinates `(x0, y0)` to
156
+ * `(x1, y1)`.
157
+ */
158
+ var vector = type({
159
+ x0: "number",
160
+ y0: "number",
161
+ x1: "number",
162
+ y1: "number"
163
+ });
164
+ /**
165
+ * Schema definition for an ongoing new connection edge creation preview between
166
+ * source node and cursor position.
167
+ *
168
+ * @see {@linkcode vector}
169
+ */
170
+ var newEdge = type(({ intersection, use }) => intersection({ from: "string" }, use(vector)));
171
+ /** Schema definition for flowchart board geometry and container scroll dimensions. */
172
+ var board = type(({ optional }) => ({
173
+ self: {
174
+ left: "number",
175
+ top: "number",
176
+ width: "number",
177
+ height: "number"
178
+ },
179
+ parent: optional({
180
+ scrollLeft: "number",
181
+ scrollTop: "number",
182
+ height: "number",
183
+ width: "number"
184
+ })
185
+ }));
186
+ //#endregion
9
187
  //#region src/ui/components/classes.ts
10
188
  /**
11
189
  * List of Tailwind CSS class names used across flowchart UI components to ensure
@@ -92,50 +270,11 @@ var CLASSES = [
92
270
  "cursor-crosshair",
93
271
  "mt-3"
94
272
  ];
95
- /** X-axis center offset in pixels from the node container edge. */
96
- var HANDLE_CENTER_X_OFFSET = 10.5;
97
- /** Default 2D offset coordinates for input connection handles. */
98
- var DEFAULT_INPUT_OFFSET = {
99
- x: -10.5,
100
- y: 18
101
- };
102
- /**
103
- * Computes default output handle offset coordinates given a node width.
104
- *
105
- * @param width - Node width in pixels, defaults to `0`.
106
- *
107
- * @returns 2D offset coordinates for the output handle.
108
- *
109
- * @see {@linkcode HANDLE_CENTER_X_OFFSET}, {@linkcode HANDLE_CENTER_Y}
110
- */
111
- var getDefaultOutputOffset = (width = 0) => ({
112
- x: width + HANDLE_CENTER_X_OFFSET,
113
- y: 18
114
- });
115
- /**
116
- * Boundary padding constraints for node dragging and positioning within the
117
- * viewport.
118
- */
119
- var BOUNDS_CONSTRAINTS = {
120
- horizontal: 55,
121
- vertical: 40
273
+ //#endregion
274
+ //#region src/helpers/clamp.ts
275
+ var clamp = (value, min, max) => {
276
+ return Math.min(Math.max(value, min), max);
122
277
  };
123
- /**
124
- * Default node items of type {@linkcode NodeProps} provided when no initial
125
- * configuration is given.
126
- */
127
- var DEFAULT_NODES = [{
128
- id: "node-0",
129
- data: {
130
- content: "Some text",
131
- label: "Root node"
132
- },
133
- input: false,
134
- position: {
135
- x: 350,
136
- y: 100
137
- }
138
- }];
139
278
  //#endregion
140
279
  //#region src/helpers/createContext.ts
141
280
  /**
@@ -152,13 +291,21 @@ var DEFAULT_NODES = [{
152
291
  *
153
292
  * @see {@linkcode createSolidContext}, {@linkcode useContext}
154
293
  */
155
- var createContext$1 = (context, options) => {
156
- const _context = createContext(context(), options);
157
- const Provider = ({ children }) => _context.Provider({
158
- value: _context.defaultValue,
159
- children
160
- });
161
- const _useContext = () => useContext(_context);
294
+ var createContext = (context, options) => {
295
+ const _context = createContext$1(void 0, options);
296
+ const Provider = (props) => {
297
+ return createComponent$1(_context.Provider, {
298
+ value: context(),
299
+ get children() {
300
+ return props.children;
301
+ }
302
+ });
303
+ };
304
+ const _useContext = () => {
305
+ const __context = useContext(_context);
306
+ if (!__context) throw new Error("useContext must be used within a Provider");
307
+ return __context;
308
+ };
162
309
  return [
163
310
  Provider,
164
311
  _useContext,
@@ -198,17 +345,17 @@ var buildNodeID = (generated) => {
198
345
  *
199
346
  * @returns Node layout dimension object of type {@linkcode Dimension}.
200
347
  *
201
- * @see {@linkcode getDefaultOutputOffset}, {@linkcode DEFAULT_INPUT_OFFSET}
348
+ * @see {@linkcode getDefaultOutputOffset}, {@linkcode DEFAULT_INPUT_OFFSET}, {@linkcode DEFAULT_SIZE}
202
349
  */
203
350
  var calculateDimensions = (position, parentDimension = {
204
- width: 192,
205
- height: 50,
351
+ width: DEFAULT_SIZE.width,
352
+ height: DEFAULT_SIZE.height,
206
353
  inputOffset: DEFAULT_INPUT_OFFSET
207
354
  }) => {
208
355
  const width = parentDimension.width;
209
356
  const height = parentDimension.height;
210
357
  const outputOffset = parentDimension.outputOffset ?? getDefaultOutputOffset(width);
211
- const inputOffset = parentDimension.inputOffset;
358
+ const inputOffset = parentDimension.inputOffset ?? DEFAULT_INPUT_OFFSET;
212
359
  return {
213
360
  width,
214
361
  height,
@@ -225,92 +372,12 @@ var calculateDimensions = (position, parentDimension = {
225
372
  };
226
373
  };
227
374
  //#endregion
228
- //#region src/services/main.machine.typings.ts
229
- /** Schema definition for 2D coordinates `(x, y)`. */
230
- var point = type({
231
- x: "number",
232
- y: "number"
233
- });
234
- type(({ optional, use }) => ({
235
- input: optional(use(point)),
236
- output: use(point)
237
- }));
238
- /** Schema definition for edge extremities. */
239
- var extremities = type({
240
- from: "string",
241
- to: "string"
242
- });
243
- /**
244
- * Schema definition for a serialized flowchart node entity.
245
- *
246
- * @see {@linkcode point}
247
- */
248
- var nodeJSON = type(({ optional, use }) => ({
249
- position: use(point),
250
- data: {
251
- label: optional("string"),
252
- content: "string"
253
- },
254
- input: "boolean"
255
- }));
256
- /**
257
- * Schema definition for a serialized flowchart edge entity.
258
- *
259
- * @see {@linkcode extremities}
260
- */
261
- var edgeJSON = extremities;
262
- /**
263
- * Schema definition for layout dimensions and connection points of a node.
264
- *
265
- * @see {@linkcode point}
266
- */
267
- var dimension = type(({ optional, use }) => ({
268
- width: "number",
269
- height: "number",
270
- output: use(point),
271
- input: optional(use(point)),
272
- inputOffset: optional(use(point)),
273
- outputOffset: optional(use(point))
274
- }));
275
- /**
276
- * Schema definition for a 2D line vector representing edge coordinates `(x0, y0)` to
277
- * `(x1, y1)`.
278
- */
279
- var vector = type({
280
- x0: "number",
281
- y0: "number",
282
- x1: "number",
283
- y1: "number"
284
- });
285
- /**
286
- * Schema definition for an ongoing new connection edge creation preview between
287
- * source node and cursor position.
288
- *
289
- * @see {@linkcode vector}
290
- */
291
- var newEdge = type(({ intersection, use }) => intersection({ from: "string" }, use(vector)));
292
- /** Schema definition for flowchart board geometry and container scroll dimensions. */
293
- var board = type(({ optional }) => ({
294
- self: {
295
- left: "number",
296
- top: "number",
297
- width: "number",
298
- height: "number"
299
- },
300
- parent: optional({
301
- scrollLeft: "number",
302
- scrollTop: "number",
303
- height: "number",
304
- width: "number"
305
- })
306
- }));
307
- //#endregion
308
375
  //#region src/services/main.machine.ts
309
376
  /**
310
377
  * State machine managing flowchart state transitions, nodes, edges, selection, and
311
378
  * layout actions.
312
379
  *
313
- * @see {@linkcode calculateDimensions}, {@linkcode buildEdgeId}, {@linkcode buildNodeID}, {@linkcode getDefaultOutputOffset}, {@linkcode DEFAULT_INPUT_OFFSET}
380
+ * @see {@linkcode calculateDimensions}, {@linkcode buildEdgeId}, {@linkcode buildNodeID}, {@linkcode getDefaultOutputOffset}, {@linkcode DEFAULT_INPUT_OFFSET}, {@linkcode DEFAULT_SIZE}, {@linkcode DEFAULT_DATA}
314
381
  */
315
382
  var machine = createMachine({
316
383
  initial: "idle",
@@ -319,7 +386,7 @@ var machine = createMachine({
319
386
  idle: { on: {
320
387
  CONFIGURE: {
321
388
  actions: ["configure"],
322
- target: "/working"
389
+ target: "/construction"
323
390
  },
324
391
  CONFIGURE_EMPTY: "/working"
325
392
  } },
@@ -328,6 +395,10 @@ var machine = createMachine({
328
395
  target: "/working"
329
396
  } },
330
397
  working: { on: {
398
+ CONFIGURE: {
399
+ actions: ["configure"],
400
+ target: "/construction"
401
+ },
331
402
  MOVE_IMMEDIATE: { actions: [{
332
403
  name: "buildUI",
333
404
  description: "Must be in the ui"
@@ -354,7 +425,6 @@ var machine = createMachine({
354
425
  ],
355
426
  target: "/construction"
356
427
  },
357
- ADD_DIMENSION: { actions: ["addDimension"] },
358
428
  ADD_SIBLING: {
359
429
  actions: [
360
430
  "generateID",
@@ -366,6 +436,7 @@ var machine = createMachine({
366
436
  ],
367
437
  target: "/construction"
368
438
  },
439
+ RESIZE: { actions: ["resize", "buildUI"] },
369
440
  MOVE: {
370
441
  actions: ["moveNode", "buildUI"],
371
442
  target: "/construction"
@@ -385,14 +456,18 @@ var machine = createMachine({
385
456
  DESELECT: { actions: ["deselect"] },
386
457
  ZOOM: { actions: ["zoom"] },
387
458
  TOGGLE_ZOOM: { actions: ["toggleZoom"] },
388
- SET_BOARD: { actions: ["setBoard"] }
459
+ SET_BOARD: { actions: ["setBoard"] },
460
+ SET_NODE_DATA: { actions: ["setNodeData"] },
461
+ EDIT: { actions: ["edit"] },
462
+ STOP_EDIT: { actions: ["stopEdit"] }
389
463
  } }
390
464
  }
391
465
  }, {
392
- eventsMap: type(({ intersection, use, array }) => ({
466
+ eventsMap: type(({ intersection, use, array, optional }) => ({
393
467
  CONFIGURE: {
394
468
  nodes: array(intersection(use(nodeJSON), { id: "string" })),
395
- edges: array(intersection(use(edgeJSON), { id: "string" }))
469
+ edges: array(intersection(use(edgeJSON), { id: "string" })),
470
+ defaultData: optional(use(data))
396
471
  },
397
472
  SET_BOARD: use(board),
398
473
  CONFIGURE_EMPTY: "never",
@@ -412,15 +487,24 @@ var machine = createMachine({
412
487
  DELETE: "string",
413
488
  SELECT: "string",
414
489
  DESELECT: "never",
490
+ EDIT: "string",
491
+ STOP_EDIT: "never",
415
492
  ADD_EDGE: use(extremities),
416
493
  START_NEW_EDGE: "string",
417
494
  MOVE_NEW_EDGE: use(point),
418
495
  CLEAR_NEW_EDGE: "never",
419
496
  ZOOM: "number",
420
497
  TOGGLE_ZOOM: "never",
421
- ADD_DIMENSION: {
498
+ RESIZE: {
499
+ id: "string",
500
+ size: {
501
+ width: "number",
502
+ height: "number"
503
+ }
504
+ },
505
+ SET_NODE_DATA: {
422
506
  id: "string",
423
- dimension: use(dimension)
507
+ data: use(data)
424
508
  }
425
509
  })),
426
510
  sync: true,
@@ -428,6 +512,7 @@ var machine = createMachine({
428
512
  generatedId: union("string", "null"),
429
513
  previousZoom: optional("number"),
430
514
  dimensions: record(use(dimension)),
515
+ defaultData: optional(use(data)),
431
516
  getBoardPosition: custom(),
432
517
  clampPosition: custom(),
433
518
  calculateDimensions: custom()
@@ -447,18 +532,42 @@ var machine = createMachine({
447
532
  edgesPositions: record(use(vector)),
448
533
  newEdge: optional(use(newEdge)),
449
534
  selected: optional("string"),
535
+ editing: optional("string"),
450
536
  updatingUI: optional("boolean"),
451
537
  zoom: "number",
452
538
  bounds: use(point)
453
539
  }))
454
540
  }).provideOptions(({ assign, batch, erase, filter, action }) => ({ actions: {
455
- configure: batch(assign("data", { CONFIGURE: ({ payload }) => payload }), assign("newEdge", () => void 0), assign("updatingUI", () => false), action(({ pContext }) => {
541
+ configure: batch(assign("data", { CONFIGURE: ({ payload: { nodes, edges } }) => ({
542
+ nodes,
543
+ edges
544
+ }) }), assign("newEdge", () => void 0), assign("updatingUI", () => false), action(({ pContext }) => {
456
545
  pContext.generatedId = null;
457
- }), action({ CONFIGURE: ({ payload: { nodes }, pContext: { dimensions } }) => {
546
+ }), action({ CONFIGURE: ({ payload: { nodes, defaultData }, pContext }) => {
547
+ pContext.defaultData = defaultData;
458
548
  nodes.forEach(({ id, position }) => {
459
- dimensions[id] = calculateDimensions(position);
549
+ pContext.dimensions[id] = calculateDimensions(position);
460
550
  });
461
551
  } })),
552
+ setNodeData: assign("data.nodes", { SET_NODE_DATA: ({ context: { data }, payload: { id, data: newData } }) => {
553
+ return data?.nodes?.map((node) => {
554
+ if (node.id === id) return {
555
+ ...node,
556
+ data: {
557
+ ...node.data,
558
+ ...newData
559
+ }
560
+ };
561
+ return node;
562
+ });
563
+ } }),
564
+ setBoard: assign("board", { SET_BOARD: ({ payload }) => payload }),
565
+ generateID: action(({ pContext }) => pContext.generatedId = nanoid()),
566
+ select: assign("selected", { SELECT: ({ payload }) => payload }),
567
+ clearNewEdge: erase("newEdge"),
568
+ deselect: batch(erase("selected"), erase("editing")),
569
+ edit: batch(assign("editing", { EDIT: ({ payload }) => payload }), assign("selected", { EDIT: ({ payload }) => payload })),
570
+ stopEdit: erase("editing"),
462
571
  startNewEdge: assign("newEdge", { START_NEW_EDGE: ({ payload: from, pContext: { dimensions } }) => {
463
572
  const { x, y } = dimensions[from].output;
464
573
  return {
@@ -469,13 +578,13 @@ var machine = createMachine({
469
578
  y1: y
470
579
  };
471
580
  } }),
472
- setBoard: assign("board", { SET_BOARD: ({ payload }) => payload }),
473
581
  buildUI: batch(assign("edgesPositions", {
474
582
  MOVE: ({ context: { data, edgesPositions }, payload, pContext: { dimensions } }) => {
475
583
  const edges = data?.edges;
476
584
  const dimension = dimensions[payload.id];
477
- const outputOffset = dimension?.outputOffset ?? getDefaultOutputOffset(dimension?.width);
478
- const inputOffset = dimension?.inputOffset ?? DEFAULT_INPUT_OFFSET;
585
+ if (!dimension) return;
586
+ const outputOffset = dimension.outputOffset ?? getDefaultOutputOffset(dimension.width);
587
+ const inputOffset = dimension.inputOffset ?? DEFAULT_INPUT_OFFSET;
479
588
  const output = {
480
589
  x: payload.x + outputOffset.x,
481
590
  y: payload.y + outputOffset.y
@@ -484,44 +593,45 @@ var machine = createMachine({
484
593
  x: payload.x + inputOffset.x,
485
594
  y: payload.y + inputOffset.y
486
595
  };
487
- if (dimension) {
488
- dimension.output = output;
489
- dimension.input = input;
490
- }
596
+ dimension.output = output;
597
+ dimension.input = input;
491
598
  edges?.forEach(({ from, to, id }) => {
599
+ const edgePosition = edgesPositions[id];
600
+ if (!edgePosition) return;
492
601
  if (from === payload.id) {
493
- edgesPositions[id].x0 = output.x;
494
- edgesPositions[id].y0 = output.y;
602
+ edgePosition.x0 = output.x;
603
+ edgePosition.y0 = output.y;
495
604
  }
496
605
  if (to === payload.id) {
497
- edgesPositions[id].x1 = input.x;
498
- edgesPositions[id].y1 = input.y;
606
+ edgePosition.x1 = input.x;
607
+ edgePosition.y1 = input.y;
499
608
  }
500
609
  });
501
610
  return edgesPositions;
502
611
  },
503
612
  MOVE_IMMEDIATE: ({ context: { data, edgesPositions }, payload, pContext: { dimensions } }) => {
504
- (data?.edges)?.forEach(({ from, to, id }) => {
613
+ data?.edges?.forEach(({ from, to, id }) => {
614
+ const dimension = dimensions[payload.id];
615
+ const edgePosition = edgesPositions[id];
616
+ if (!edgePosition || !dimension) return;
505
617
  if (from === payload.id) {
506
- const dimension = dimensions[payload.id];
507
- const offset = dimension?.outputOffset ?? getDefaultOutputOffset(dimension?.width);
618
+ const offset = dimension.outputOffset ?? getDefaultOutputOffset(dimension.width);
508
619
  const x0 = payload.x + offset.x;
509
620
  const y0 = payload.y + offset.y;
510
- edgesPositions[id].x0 = x0;
511
- edgesPositions[id].y0 = y0;
512
- if (dimension) {
513
- dimension.output.x = x0;
514
- dimension.output.y = y0;
515
- }
621
+ edgePosition.x0 = x0;
622
+ edgePosition.y0 = y0;
623
+ dimension.output = {
624
+ x: x0,
625
+ y: y0
626
+ };
516
627
  }
517
628
  if (to === payload.id) {
518
- const dimension = dimensions[payload.id];
519
629
  const offset = dimension?.inputOffset ?? DEFAULT_INPUT_OFFSET;
520
630
  const x1 = payload.x + offset.x;
521
631
  const y1 = payload.y + offset.y;
522
- edgesPositions[id].x1 = x1;
523
- edgesPositions[id].y1 = y1;
524
- if (dimension) dimension.input = {
632
+ edgePosition.x1 = x1;
633
+ edgePosition.y1 = y1;
634
+ dimension.input = {
525
635
  x: x1,
526
636
  y: y1
527
637
  };
@@ -529,32 +639,47 @@ var machine = createMachine({
529
639
  });
530
640
  return edgesPositions;
531
641
  },
532
- else: ({ context: { data, edgesPositions }, pContext: { dimensions } }) => {
533
- const edges = data?.edges ?? [];
534
- edgesPositions = {};
535
- edges.forEach(({ from, id, to }) => {
642
+ else: ({ context: { data }, pContext: { dimensions } }) => {
643
+ const nextEdgesPositions = {};
644
+ data?.edges?.forEach(({ from, id, to }) => {
536
645
  const output = dimensions[from]?.output;
537
646
  const input = dimensions[to]?.input;
538
- if (output && input) edgesPositions[id] = {
647
+ if (output && input) nextEdgesPositions[id] = {
539
648
  x0: output.x,
540
649
  y0: output.y,
541
650
  x1: input.x,
542
651
  y1: input.y
543
652
  };
544
- else delete edgesPositions[id];
545
653
  });
546
- return edgesPositions;
654
+ return nextEdgesPositions;
547
655
  }
548
656
  }), assign("updatingUI", () => true)),
549
- addDimension: action({ ADD_DIMENSION: ({ payload: { id, dimension }, pContext: { dimensions } }) => {
550
- dimensions[id] = dimension;
657
+ resize: action({ RESIZE: ({ payload: { id, size: { width, height } }, context: { data }, pContext: { dimensions } }) => {
658
+ const dimension = dimensions[id];
659
+ if (!dimension) return;
660
+ dimension.width = width;
661
+ dimension.height = height;
662
+ const node = data?.nodes?.find((n) => n.id === id);
663
+ const outputOffset = getDefaultOutputOffset(width);
664
+ const inputOffset = dimension.inputOffset ?? DEFAULT_INPUT_OFFSET;
665
+ dimension.outputOffset = outputOffset;
666
+ dimension.inputOffset = inputOffset;
667
+ if (node) {
668
+ dimension.output = {
669
+ x: node.position.x + outputOffset.x,
670
+ y: node.position.y + outputOffset.y
671
+ };
672
+ dimension.input = {
673
+ x: node.position.x + inputOffset.x,
674
+ y: node.position.y + inputOffset.y
675
+ };
676
+ } else dimension.output = {
677
+ x: dimension.output.x,
678
+ y: dimension.output.y
679
+ };
551
680
  } }),
552
- generateID: action(({ pContext }) => {
553
- pContext.generatedId = nanoid();
554
- }),
555
- linkChild: batch(assign("data.edges", { ADD_CHILD: ({ context: { data }, pContext, payload }) => {
556
- const edges = toArray$1.typed(data?.edges);
557
- const from = payload;
681
+ linkChild: batch(assign("data.edges", { ADD_CHILD: ({ context: { data }, pContext, payload: from }) => {
682
+ const edges = toArray.typed(data?.edges);
558
683
  const generatedId = pContext?.generatedId;
559
684
  const to = buildNodeID(generatedId);
560
685
  const id = buildEdgeId(from, to);
@@ -566,7 +691,7 @@ var machine = createMachine({
566
691
  return edges;
567
692
  } }), assign("selected", ({ pContext: { generatedId } }) => buildNodeID(generatedId))),
568
693
  linkSibling: batch(assign("data.edges", { ADD_SIBLING: ({ pContext, payload, context: { data } }) => {
569
- const edges = toArray$1.typed(data?.edges);
694
+ const edges = toArray.typed(data?.edges);
570
695
  const generatedId = pContext?.generatedId;
571
696
  const from = edges.find(({ to }) => to === payload)?.from;
572
697
  if (!from) return edges;
@@ -580,23 +705,21 @@ var machine = createMachine({
580
705
  return edges;
581
706
  } }), assign("selected", ({ pContext: { generatedId } }) => buildNodeID(generatedId))),
582
707
  selectParent: assign("selected", ({ pContext: { generatedId } }) => buildNodeID(generatedId)),
583
- moveNode: assign("data.nodes", { MOVE: ({ context: { data }, payload }) => {
584
- const { id, x, y } = payload;
585
- return data?.nodes?.map((d) => {
586
- if (d.id === id) return {
587
- ...d,
708
+ moveNode: assign("data.nodes", { MOVE: ({ context: { data }, payload: { id, x, y } }) => {
709
+ return data?.nodes?.map((node) => {
710
+ if (node.id === id) return {
711
+ ...node,
588
712
  position: {
589
713
  x,
590
714
  y
591
715
  }
592
716
  };
593
- return d;
594
- }) ?? [];
717
+ return node;
718
+ });
595
719
  } }),
596
- select: assign("selected", { SELECT: ({ payload }) => payload }),
597
720
  delete: batch(filter("data.edges", { DELETE: ({ id, from, to }, _, { payload }) => {
598
721
  return id !== payload && from !== payload && to !== payload;
599
- } }), filter("data.nodes", { DELETE: ({ id }, _, { payload }) => id !== payload })),
722
+ } }), filter("data.nodes", { DELETE: ({ id }, _, { payload }) => id !== payload }), erase("editing")),
600
723
  addEdge: batch(assign("data.edges", { ADD_EDGE: ({ context, payload: { from, to } }) => {
601
724
  const edges = context.data?.edges ?? [];
602
725
  const id = buildEdgeId(from, to);
@@ -607,11 +730,8 @@ var machine = createMachine({
607
730
  to
608
731
  }];
609
732
  } }), erase("newEdge")),
610
- clearNewEdge: erase("newEdge"),
611
- deselect: erase("selected"),
612
733
  zoom: assign("zoom", { ZOOM: ({ context: { zoom }, payload, pContext }) => {
613
- const next = zoom + payload;
614
- const clamped = Math.min(Math.max(Number(next.toFixed(2)), .2), 3);
734
+ const clamped = clamp(zoom + payload, .1, 3);
615
735
  pContext.previousZoom = void 0;
616
736
  return clamped;
617
737
  } }),
@@ -623,6 +743,84 @@ var machine = createMachine({
623
743
  }
624
744
  pContext.previousZoom = zoom;
625
745
  return 1;
746
+ } }),
747
+ placeChild: assign("data.nodes", { ADD_CHILD: ({ payload, context: { data, board }, pContext }) => {
748
+ if (!board) return data?.nodes;
749
+ const nodes = data?.nodes;
750
+ if (!payload) return nodes;
751
+ const parentNode = nodes?.find((node) => node.id === payload);
752
+ if (!parentNode) return nodes;
753
+ const parentDimension = pContext.dimensions[payload];
754
+ const id = `node-${pContext.generatedId}`;
755
+ const width = parentDimension?.width ?? DEFAULT_SIZE.width;
756
+ const height = parentDimension?.height ?? DEFAULT_SIZE.height;
757
+ const initialX = parentNode.position.x + width + 100;
758
+ const initialY = parentNode.position.y;
759
+ const position = pContext.clampPosition(board, initialX, initialY, width, height);
760
+ const defaultData = pContext.defaultData ?? DEFAULT_DATA;
761
+ nodes?.push({
762
+ id,
763
+ data: { ...defaultData },
764
+ position
765
+ });
766
+ pContext.dimensions[id] = pContext.calculateDimensions(position, parentDimension ?? DEFAULT_SIZE);
767
+ return nodes;
768
+ } }),
769
+ placeParent: assign("data.nodes", { ADD_PARENT: ({ context: { data, zoom = 1, board }, pContext }) => {
770
+ if (!board) return data?.nodes;
771
+ const nodes = data?.nodes;
772
+ const id = `node-${pContext.generatedId}`;
773
+ const container = board.parent;
774
+ const scrollLeft = container?.scrollLeft ?? 0;
775
+ const scrollTop = container?.scrollTop ?? 0;
776
+ const width = container?.width ?? 0;
777
+ const height = container?.height ?? 0;
778
+ const currentZoom = zoom;
779
+ const x = (scrollLeft + width / 2) / currentZoom;
780
+ const y = (scrollTop + height / 2) / currentZoom;
781
+ const position = pContext.clampPosition(board, x, y, DEFAULT_SIZE.width, DEFAULT_SIZE.height);
782
+ const defaultData = pContext.defaultData ?? DEFAULT_DATA;
783
+ nodes?.push({
784
+ id,
785
+ data: { ...defaultData },
786
+ position
787
+ });
788
+ pContext.dimensions[id] = pContext.calculateDimensions(position, DEFAULT_SIZE);
789
+ return nodes;
790
+ } }),
791
+ placeSibling: assign("data.nodes", { ADD_SIBLING: ({ payload, context: { data, board }, pContext }) => {
792
+ if (!board) return data?.nodes;
793
+ const edges = data?.edges;
794
+ const nodes = data?.nodes;
795
+ const parentID = edges?.find((edge) => edge.to === payload)?.from;
796
+ if (!parentID) return nodes;
797
+ const parentNode = nodes?.find((node) => node.id === parentID);
798
+ if (!parentNode) return nodes;
799
+ const parentDimension = pContext.dimensions[parentID];
800
+ const id = `node-${pContext.generatedId}`;
801
+ const width = parentDimension?.width ?? DEFAULT_SIZE.width;
802
+ const height = parentDimension?.height ?? DEFAULT_SIZE.height;
803
+ const initialX = parentNode.position.x + width + 100;
804
+ const initialY = parentNode.position.y + 100;
805
+ const position = pContext.clampPosition(board, initialX, initialY, width, height);
806
+ const defaultData = pContext.defaultData ?? DEFAULT_DATA;
807
+ nodes?.push({
808
+ id,
809
+ data: { ...defaultData },
810
+ position
811
+ });
812
+ pContext.dimensions[id] = pContext.calculateDimensions(position, parentDimension ?? DEFAULT_SIZE);
813
+ return nodes;
814
+ } }),
815
+ moveNewEdge: assign("newEdge", { MOVE_NEW_EDGE: ({ context: { newEdge, board }, payload, pContext }) => {
816
+ if (!board) return void 0;
817
+ if (!newEdge) return void 0;
818
+ const { x: x1, y: y1 } = pContext.getBoardPosition(payload.x, payload.y, board);
819
+ return {
820
+ ...newEdge,
821
+ x1,
822
+ y1
823
+ };
626
824
  } })
627
825
  } }));
628
826
  //#endregion
@@ -633,7 +831,7 @@ var machine = createMachine({
633
831
  *
634
832
  * @see {@linkcode machine}, {@linkcode createContext}
635
833
  */
636
- var [Provider, useFlow] = createContext$1(() => {
834
+ var [Provider, useFlow] = createContext(() => {
637
835
  /** Shared service for flowchart state management. */
638
836
  const service = interpret(machine, {
639
837
  context: {
@@ -656,7 +854,7 @@ var [Provider, useFlow] = createContext$1(() => {
656
854
  *
657
855
  * @returns Calculated 2D coordinate of type {@linkcode Point}.
658
856
  */
659
- getBoardPosition(clientX, clientY, el) {
857
+ getBoardPosition: (clientX, clientY, el) => {
660
858
  if (!el) return {
661
859
  x: clientX,
662
860
  y: clientY
@@ -674,14 +872,15 @@ var [Provider, useFlow] = createContext$1(() => {
674
872
  * {@linkcode Board}.
675
873
  * @param x - Desired X position in pixels.
676
874
  * @param y - Desired Y position in pixels.
677
- * @param nodeWidth - Node width in pixels, defaults to `192`.
678
- * @param nodeHeight - Node height in pixels, defaults to `50`.
875
+ * @param nodeWidth - Node width in pixels, defaults to `DEFAULT_SIZE.width`.
876
+ * @param nodeHeight - Node height in pixels, defaults to
877
+ * `DEFAULT_SIZE.height`.
679
878
  *
680
879
  * @returns Clamped coordinate of type {@linkcode Point}.
681
880
  *
682
- * @see {@linkcode BOUNDS_CONSTRAINTS}
881
+ * @see {@linkcode BOUNDS_CONSTRAINTS}, {@linkcode DEFAULT_SIZE}
683
882
  */
684
- clampPosition(board, x, y, nodeWidth = 192, nodeHeight = 50) {
883
+ clampPosition: (board, x, y, nodeWidth = DEFAULT_SIZE.width, nodeHeight = DEFAULT_SIZE.height) => {
685
884
  const container = board?.parent;
686
885
  if (!container) return {
687
886
  x,
@@ -693,8 +892,8 @@ var [Provider, useFlow] = createContext$1(() => {
693
892
  const minY = container.scrollTop / currentZoom + BOUNDS_CONSTRAINTS.vertical;
694
893
  const maxY = Math.max(minY, (container.scrollTop + container.height) / currentZoom - BOUNDS_CONSTRAINTS.vertical - nodeHeight);
695
894
  return {
696
- x: Math.min(Math.max(x, minX), maxX),
697
- y: Math.min(Math.max(y, minY), maxY)
895
+ x: clamp(x, minX, maxX),
896
+ y: clamp(y, minY, maxY)
698
897
  };
699
898
  },
700
899
  /**
@@ -705,17 +904,17 @@ var [Provider, useFlow] = createContext$1(() => {
705
904
  *
706
905
  * @returns Calculated dimension object of type {@linkcode Dimension}.
707
906
  *
708
- * @see {@linkcode DEFAULT_INPUT_OFFSET}, {@linkcode getDefaultOutputOffset}
907
+ * @see {@linkcode DEFAULT_INPUT_OFFSET}, {@linkcode getDefaultOutputOffset}, {@linkcode DEFAULT_SIZE}
709
908
  */
710
909
  calculateDimensions: (position, parentDimension = {
711
- width: 192,
712
- height: 50,
910
+ width: DEFAULT_SIZE.width,
911
+ height: DEFAULT_SIZE.height,
713
912
  inputOffset: DEFAULT_INPUT_OFFSET
714
913
  }) => {
715
914
  const width = parentDimension.width;
716
915
  const height = parentDimension.height;
717
916
  const outputOffset = parentDimension.outputOffset ?? getDefaultOutputOffset(width);
718
- const inputOffset = parentDimension.inputOffset;
917
+ const inputOffset = parentDimension.inputOffset ?? DEFAULT_INPUT_OFFSET;
719
918
  return {
720
919
  width,
721
920
  height,
@@ -733,89 +932,6 @@ var [Provider, useFlow] = createContext$1(() => {
733
932
  }
734
933
  }
735
934
  });
736
- service.addOptions(({ assign }) => ({ actions: {
737
- placeChild: assign("data.nodes", { ADD_CHILD: ({ payload, context: { data, board }, pContext }) => {
738
- if (!board) return data?.nodes;
739
- const nodes = data?.nodes;
740
- if (!payload) return nodes;
741
- const parentNode = nodes?.find((node) => node.id === payload);
742
- if (!parentNode) return nodes;
743
- const parentDimension = pContext.dimensions[payload];
744
- const id = `node-${pContext.generatedId}`;
745
- const width = parentDimension?.width ?? 192;
746
- const height = parentDimension?.height ?? 50;
747
- const initialX = parentNode.position.x + width + 100;
748
- const initialY = parentNode.position.y;
749
- const position = pContext.clampPosition(board, initialX, initialY, width, height);
750
- const dimension = pContext.calculateDimensions(position, parentDimension);
751
- nodes?.push({
752
- id,
753
- data: { content: "<Nouveau nœud>" },
754
- input: true,
755
- position
756
- });
757
- pContext.dimensions[id] = dimension;
758
- return nodes;
759
- } }),
760
- placeParent: assign("data.nodes", { ADD_PARENT: ({ context: { data, zoom = 1, board }, pContext }) => {
761
- if (!board) return data?.nodes;
762
- const nodes = data?.nodes;
763
- const id = `node-${pContext.generatedId}`;
764
- const container = board.parent;
765
- const scrollLeft = container?.scrollLeft ?? 0;
766
- const scrollTop = container?.scrollTop ?? 0;
767
- const width = container?.width ?? 0;
768
- const height = container?.height ?? 0;
769
- const currentZoom = zoom;
770
- const x = (scrollLeft + width / 2) / currentZoom;
771
- const y = (scrollTop + height / 2) / currentZoom;
772
- const position = pContext.clampPosition(board, x, y);
773
- const dimension = pContext.calculateDimensions(position);
774
- nodes?.push({
775
- id,
776
- data: { content: "<Nouveau nœud>" },
777
- input: false,
778
- position
779
- });
780
- pContext.dimensions[id] = dimension;
781
- return nodes;
782
- } }),
783
- placeSibling: assign("data.nodes", { ADD_SIBLING: ({ payload, context: { data, board }, pContext }) => {
784
- if (!board) return data?.nodes;
785
- const edges = data?.edges;
786
- const nodes = data?.nodes;
787
- const parentID = edges?.find((edge) => edge.to === payload)?.from;
788
- if (!parentID) return nodes;
789
- const parentNode = nodes?.find((node) => node.id === parentID);
790
- if (!parentNode) return nodes;
791
- const parentDimension = pContext.dimensions[parentID];
792
- const id = `node-${pContext.generatedId}`;
793
- const width = parentDimension?.width ?? 192;
794
- const height = parentDimension?.height ?? 50;
795
- const initialX = parentNode.position.x + width + 100;
796
- const initialY = parentNode.position.y + 100;
797
- const position = pContext.clampPosition(board, initialX, initialY, width, height);
798
- const dimension = pContext.calculateDimensions(position, parentDimension);
799
- nodes?.push({
800
- id,
801
- data: { content: "<Nouveau nœud>" },
802
- input: true,
803
- position
804
- });
805
- pContext.dimensions[id] = dimension;
806
- return nodes;
807
- } }),
808
- moveNewEdge: assign("newEdge", { MOVE_NEW_EDGE: ({ context: { newEdge, board }, payload, pContext }) => {
809
- if (!board) return void 0;
810
- if (!newEdge) return void 0;
811
- const { x: x1, y: y1 } = pContext.getBoardPosition(payload.x, payload.y, board);
812
- return {
813
- ...newEdge,
814
- x1,
815
- y1
816
- };
817
- } })
818
- } }));
819
935
  service.start();
820
936
  return service;
821
937
  }, { name: "FlowContext" });
@@ -869,14 +985,14 @@ var DragBounds = () => {
869
985
  };
870
986
  //#endregion
871
987
  //#region src/ui/components/EdgeComponent.tsx
872
- var _tmpl$$4 = [
988
+ var _tmpl$$10 = [
873
989
  "<path",
874
990
  " class=\"",
875
991
  "\" style=\"",
876
992
  "\"",
877
993
  "></path>"
878
994
  ];
879
- var _tmpl$2$2 = [
995
+ var _tmpl$2$5 = [
880
996
  "<g",
881
997
  " cursor=\"pointer\" transform=\"",
882
998
  "\" style=\"",
@@ -889,6 +1005,13 @@ var _tmpl$2$2 = [
889
1005
  *
890
1006
  * @returns Offset distance in pixels for bezier curvature.
891
1007
  */
1008
+ /**
1009
+ * Computes bezier curve control point offset based on horizontal distance.
1010
+ *
1011
+ * @param value - Absolute horizontal delta between start and end points.
1012
+ *
1013
+ * @returns Offset distance in pixels for bezier curvature.
1014
+ */
892
1015
  var calculateOffset = (value) => value * 100 / 200;
893
1016
  /**
894
1017
  * Generates an SVG cubic bezier path `d` string between coordinates `(x0, y0)` and
@@ -900,7 +1023,9 @@ var calculateOffset = (value) => value * 100 / 200;
900
1023
  *
901
1024
  * @see {@linkcode calculateOffset}
902
1025
  */
903
- var draw = ({ x0, y0, x1, y1 }) => {
1026
+ var draw = (vector) => {
1027
+ if (!vector) return "";
1028
+ const { x0, y0, x1, y1 } = vector;
904
1029
  return `M ${x0} ${y0} C ${x0 + calculateOffset(Math.abs(x1 - x0))} ${y0}, ${x1 - calculateOffset(Math.abs(x1 - x0))} ${y1}, ${x1} ${y1}`;
905
1030
  };
906
1031
  /**
@@ -926,23 +1051,32 @@ var EdgeComponent = (props) => {
926
1051
  /** Computes the 2D midpoint coordinate of the edge curve for handle placement. */
927
1052
  const middlePoint = () => {
928
1053
  const v = vector();
1054
+ if (!v) return {
1055
+ x: 0,
1056
+ y: 0
1057
+ };
929
1058
  return {
930
1059
  x: v.x0 + (v.x1 - v.x0) / 2,
931
1060
  y: v.y0 + (v.y1 - v.y0) / 2
932
1061
  };
933
1062
  };
934
- return [ssr(_tmpl$$4, ssrHydrationKey(), `relative cursor-pointer fill-transparent ${!!props.isNew ? "stroke-[rgba(168,168,168,0.4)] stroke-3 z-200" : ""} ${selected() && !props.isNew ? "stroke-[rgba(168,168,168,1)] stroke-4 z-100" : ""} ${!selected() && !props.isNew ? "stroke-[rgba(168,168,168,0.8)] stroke-3" : ""}`, ssrStyleProperty("pointer-events:", props.isNew ? "none" : "all"), ssrAttribute("d", escape(draw(vector()), true), false)), createComponent(Show, {
1063
+ return createComponent(Show, {
935
1064
  get when() {
936
- return selected();
1065
+ return vector();
937
1066
  },
938
- get children() {
939
- return ssr(_tmpl$2$2, ssrHydrationKey(), `translate(${escape(middlePoint().x, true)}, ${escape(middlePoint().y, true)})`, ssrStyleProperty("pointer-events:", "all") + ssrStyleProperty(";z-index:", "30"));
940
- }
941
- })];
1067
+ children: (v) => [ssr(_tmpl$$10, ssrHydrationKey(), `relative cursor-pointer fill-transparent ${!!props.isNew ? "stroke-[rgba(168,168,168,0.4)] stroke-3 z-200" : ""} ${selected() && !props.isNew ? "stroke-[rgba(168,168,168,1)] stroke-4 z-100" : ""} ${!selected() && !props.isNew ? "stroke-[rgba(168,168,168,0.8)] stroke-3" : ""}`, ssrStyleProperty("pointer-events:", props.isNew ? "none" : "all"), ssrAttribute("d", escape(draw(v()), true), false)), createComponent(Show, {
1068
+ get when() {
1069
+ return selected();
1070
+ },
1071
+ get children() {
1072
+ return ssr(_tmpl$2$5, ssrHydrationKey(), `translate(${escape(middlePoint().x, true)}, ${escape(middlePoint().y, true)})`, ssrStyleProperty("pointer-events:", "all") + ssrStyleProperty(";z-index:", "30"));
1073
+ }
1074
+ })]
1075
+ });
942
1076
  };
943
1077
  //#endregion
944
1078
  //#region src/ui/components/EdgesBoard.tsx
945
- var _tmpl$$3 = [
1079
+ var _tmpl$$9 = [
946
1080
  "<svg",
947
1081
  " class=\"pointer-events-none h-full w-full overflow-visible\"><!--$-->",
948
1082
  "<!--/--><!--$-->",
@@ -960,10 +1094,10 @@ var EdgesBoard = () => {
960
1094
  const service = useFlow();
961
1095
  const hasNewEdge = createState(service, { selector: (s) => !!s.context.newEdge });
962
1096
  const edgeIds = createState(service, {
963
- selector: ({ context }) => Object.keys(context.edgesPositions),
1097
+ selector: ({ context }) => Object.keys(context.edgesPositions ?? {}),
964
1098
  equals: (prev, next) => prev.length === next.length
965
1099
  });
966
- return ssr(_tmpl$$3, ssrHydrationKey(), escape(createComponent(Show, {
1100
+ return ssr(_tmpl$$9, ssrHydrationKey(), escape(createComponent(Show, {
967
1101
  get when() {
968
1102
  return hasNewEdge();
969
1103
  },
@@ -982,18 +1116,12 @@ var EdgesBoard = () => {
982
1116
  };
983
1117
  //#endregion
984
1118
  //#region src/ui/components/NodeComponent.tsx
985
- var _tmpl$$2 = [
1119
+ var _tmpl$$8 = [
986
1120
  "<svg",
987
1121
  " class=\"cursor-pointer overflow-visible rounded-full bg-green-500 p-0.5 text-center font-bold hover:bg-green-600\" style=\"",
988
1122
  "\" viewBox=\"0 0 1024 1024\" preserveAspectRatio=\"xMaxYMax\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"white\"><g id=\"background\"><path d=\"M467.40667,277.66696c-0.05948,-14.53055 5.75527,-22.95613 -8.62044,-20.90487c-112.55699,16.0607 -222.1609,112.14558 -245.06161,239.85765c-46.52056,259.43466 231.33083,443.06705 449.51209,316.97506c117.31668,-67.80002 160.95215,-190.43324 151.34416,-288.29849c-5.92276,-60.32819 -27.80273,-107.95668 -53.44246,-144.25469l59.39269,-42.05363c111.72214,156.309 73.11535,351.55635 -25.06953,459.45565c-184.18877,202.4124 -470.46624,145.52064 -592.95027,-32.92123c-156.18269,-227.53604 -27.15324,-543.64371 261.18883,-582.44416c5.0579,-0.68061 3.56556,-7.04079 3.56442,-8.58985c-0.05594,-76.3354 -0.11021,-76.7687 1.10909,-77.24589c2.06886,-0.80969 151.41433,118.4561 151.92482,118.95524c4.65592,4.55233 -0.99548,7.829 -29.07828,30.50907c-120.49369,97.31245 -120.4977,98.55675 -123.0691,97.87586c-0.43639,-0.11555 -0.80698,-0.31322 -0.74442,-66.91571Z\"></path><path d=\"M316.61562,611.03414c0.11517,-90.16257 -0.25516,-99.92912 0.64739,-101.78856c1.56486,-3.22393 131.99102,0.91032 134.6959,-1.89763c2.21336,-2.2977 -0.59362,-129.97807 1.1376,-132.37034c0.7253,-1.00225 11.10552,-0.99175 12.07474,-0.99077c104.50336,0.10564 104.90098,-0.37811 105.967,0.85765c1.56461,1.81373 0.25596,114.18436 0.67852,129.81412c0.1322,4.88975 3.22386,3.28152 99.72028,3.4563c33.0841,0.05992 36.14259,-1.40368 36.21852,4.50462c0.11713,9.11348 1.41954,110.45369 -0.40274,113.92715c-1.81106,3.45208 -130.39967,-0.42618 -134.48289,1.62075c-2.29035,1.14816 -0.36989,101.12392 -1.11542,130.51904c-0.09548,3.76459 -2.13506,3.20617 -47.84854,3.17365c-69.30253,-0.0493 -69.31099,-0.25627 -69.65762,-0.42191c-3.77927,-1.80595 0.16287,-129.33555 -2.24266,-132.58994c-1.79931,-2.43424 -124.06108,-0.29118 -132.80252,-0.97392c-3.81102,-0.29766 -2.58229,-14.8847 -2.58758,-16.8402Z\"></path></g></svg>"
989
1123
  ];
990
- var _tmpl$2$1 = [
991
- "<div",
992
- " id=\"outputs\" class=\"pointer-events-none absolute top-0 z-10 flex cursor-default flex-col\" style=\"",
993
- "\"><div class=\"cursor-default rounded-full bg-[#e38b29] shadow-md\" style=\"",
994
- "\"></div></div>"
995
- ];
996
- var _tmpl$3$1 = [
1124
+ var _tmpl$2$4 = [
997
1125
  "<div",
998
1126
  " class=\"",
999
1127
  "\" style=\"",
@@ -1002,28 +1130,44 @@ var _tmpl$3$1 = [
1002
1130
  "\"><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=\"",
1003
1131
  "\"><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><!--$-->",
1004
1132
  "<!--/--><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=\"",
1005
- "\"><path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"></path></svg></div><!--$-->",
1006
- "<!--/--><!--$-->",
1007
- "<!--/--><!--$-->",
1008
- "<!--/--><div id=\"inputs\" class=\"pointer-events-none absolute top-0 z-10 flex flex-col\" style=\"",
1133
+ "\"><path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"></path></svg></div><div>",
1134
+ "</div><div id=\"inputs\" class=\"pointer-events-none absolute top-0 z-10 flex cursor-default flex-col\" style=\"",
1135
+ "\"><div class=\"cursor-default rounded-full bg-[#e38b29] shadow-md\" style=\"",
1136
+ "\"></div></div><div id=\"outputs\" class=\"pointer-events-none absolute top-0 z-10 flex flex-col\" style=\"",
1009
1137
  "\"><div class=\"cursor-crosshair rounded-full bg-[#e38b29] shadow-md\" style=\"",
1010
1138
  "\"></div></div></div>"
1011
1139
  ];
1140
+ var _tmpl$3$2 = [
1141
+ "<div",
1142
+ " class=\"p-3 select-none\"><!--$-->",
1143
+ "<!--/--><!--$-->",
1144
+ "<!--/--><!--$-->",
1145
+ "<!--/--></div>"
1146
+ ];
1012
1147
  var _tmpl$4 = [
1013
1148
  "<span",
1014
- " class=\"min-w-max border-b border-[#f0f0f0] p-3 whitespace-nowrap text-red-600 select-none\">",
1149
+ " class=\"mb-2 block min-w-max border-b border-[#f0f0f0] pb-2 font-semibold whitespace-nowrap text-red-600 select-none\">",
1015
1150
  "</span>"
1016
1151
  ];
1017
1152
  var _tmpl$5 = [
1018
1153
  "<div",
1019
- " class=\"p-3 select-none\">",
1154
+ " class=\"select-none\">",
1020
1155
  "</div>"
1021
1156
  ];
1157
+ var _tmpl$6 = [
1158
+ "<div",
1159
+ "><span class=\"font-semibold\"><!--$-->",
1160
+ "<!--/-->: </span><span>",
1161
+ "</span></div>"
1162
+ ];
1022
1163
  /**
1023
1164
  * Interactive flowchart node component supporting dragging, selection, handle
1024
1165
  * connections, and child/sibling creation.
1025
1166
  *
1026
- * @param props - Node rendering properties of type {@linkcode Props}.
1167
+ * @template | {@linkcode NodeData} `D` - Custom node data dictionary type extending
1168
+ * {@linkcode NodeData}.
1169
+ *
1170
+ * @param props - Node rendering properties of type {@linkcode NodeComponentProps}.
1027
1171
  *
1028
1172
  * @returns The rendered Solid component.
1029
1173
  *
@@ -1031,60 +1175,134 @@ var _tmpl$5 = [
1031
1175
  */
1032
1176
  var NodeComponent = (props) => {
1033
1177
  const service = useFlow();
1178
+ createState(service, { selector: (s) => s.context.newEdge });
1179
+ createDraggable(props.id);
1034
1180
  const node = createState(service, {
1035
1181
  selector: ({ context }) => {
1036
- const item = toArray.typed(context.data?.nodes).find((n) => n.id === props.id);
1182
+ const item = toArray$1.typed(context.data?.nodes).find((n) => n.id === props.id);
1037
1183
  return {
1038
- x: item.position.x,
1039
- y: item.position.y,
1040
- label: item.data.label,
1041
- content: item.data.content ?? "",
1042
- input: item.input
1184
+ x: item?.position.x ?? 0,
1185
+ y: item?.position.y ?? 0,
1186
+ data: item?.data ?? {}
1043
1187
  };
1044
1188
  },
1045
1189
  equals: dequal
1046
1190
  });
1047
- createState(service, { selector: (s) => s.context.newEdge });
1048
- const selected = createState(service, { selector: (s) => s.context.selected === props.id });
1191
+ const selected = createState(service, { selector: ({ context }) => context.selected === props.id });
1049
1192
  const hasParent = createState(service, { selector: ({ context: { data } }) => {
1050
1193
  const edges = data?.edges;
1051
1194
  if (!edges) return false;
1052
1195
  return edges.some(({ to }) => to === props.id);
1053
1196
  } });
1054
- createDraggable(props.id);
1055
- return ssr(_tmpl$3$1, ssrHydrationKey(), `flex flex-col absolute cursor-grab bg-white rounded-md shadow-md select-none transition-[border,box-shadow] duration-200 ease-in-out hover:shadow-lg draggable ${selected() ? "border border-[#e38c29] z-[100]" : ""} ${!selected() ? "border border-[#e6d4be] z-[1]" : ""} min-w-48`, ssrStyleProperty("top:", `${escape(node().y, true)}px`) + ssrStyleProperty(";left:", `${escape(node().x, true)}px`), ssrAttribute("id", escape(props.id, true), false), `pointer-events-none absolute flex items-center justify-end -top-7.5 right-0 transition-all duration-200 ease-in-out space-x-2 ${selected() ? "w-full opacity-100" : ""} ${!selected() ? "w-0 -right-3 opacity-0 overflow-hidden" : ""}`, ssrStyleProperty("overflow:", "visible") + ssrStyleProperty(";pointer-events:", "all") + ssrStyleProperty(";width:", `${escape(12, true) * 2}px`) + ssrStyleProperty(";height:", `${escape(12, true) * 2}px`), escape(createComponent(Show, {
1197
+ return ssr(_tmpl$2$4, ssrHydrationKey(), `flex flex-col absolute cursor-grab bg-white rounded-md shadow-md select-none transition-[border,box-shadow] duration-200 ease-in-out hover:shadow-lg draggable ${selected() ? "border border-[#e38c29] z-100" : ""} ${!selected() ? "border border-[#e6d4be] z-1" : ""} min-w-48`, ssrStyleProperty("top:", `${escape(node().y, true)}px`) + ssrStyleProperty(";left:", `${escape(node().x, true)}px`), ssrAttribute("id", escape(props.id, true), false), `pointer-events-none absolute flex items-center justify-end -top-7.5 right-0 transition-all duration-200 ease-in-out space-x-2 ${selected() ? "w-full opacity-100" : ""} ${!selected() ? "w-0 -right-3 opacity-0 overflow-hidden" : ""}`, ssrStyleProperty("overflow:", "visible") + ssrStyleProperty(";pointer-events:", "all") + ssrStyleProperty(";width:", `${escape(12, true) * 2}px`) + ssrStyleProperty(";height:", `${escape(12, true) * 2}px`), escape(createComponent(Show, {
1056
1198
  get when() {
1057
1199
  return hasParent();
1058
1200
  },
1059
1201
  get children() {
1060
- return ssr(_tmpl$$2, ssrHydrationKey(), ssrStyleProperty("pointer-events:", "all") + ssrStyleProperty(";fill-rule:", "evenodd") + ssrStyleProperty(";clip-rule:", "evenodd") + ssrStyleProperty(";stroke-linejoin:", "round") + ssrStyleProperty(";stroke-miterlimit:", "2") + ssrStyleProperty(";width:", `${escape(12, true) * 2}px`) + ssrStyleProperty(";height:", `${escape(12, true) * 2}px`));
1202
+ return ssr(_tmpl$$8, ssrHydrationKey(), ssrStyleProperty("pointer-events:", "all") + ssrStyleProperty(";fill-rule:", "evenodd") + ssrStyleProperty(";clip-rule:", "evenodd") + ssrStyleProperty(";stroke-linejoin:", "round") + ssrStyleProperty(";stroke-miterlimit:", "2") + ssrStyleProperty(";width:", `${escape(12, true) * 2}px`) + ssrStyleProperty(";height:", `${escape(12, true) * 2}px`));
1061
1203
  }
1062
1204
  })), ssrStyleProperty("pointer-events:", "all") + ssrStyleProperty(";width:", `${escape(12, true) * 2}px`) + ssrStyleProperty(";height:", `${escape(12, true) * 2}px`), escape(createComponent(Show, {
1063
1205
  get when() {
1064
- return node().label;
1065
- },
1066
- keyed: true,
1067
- children: (label) => ssr(_tmpl$4, ssrHydrationKey(), escape(label))
1068
- })), escape(createComponent(Show, {
1069
- get when() {
1070
- return node().content;
1206
+ return props.children;
1071
1207
  },
1072
- keyed: true,
1073
- children: (content) => ssr(_tmpl$5, ssrHydrationKey(), escape(content))
1074
- })), escape(createComponent(Show, {
1075
- get when() {
1076
- return node().input || hasParent();
1208
+ get fallback() {
1209
+ return ssr(_tmpl$3$2, ssrHydrationKey(), escape(createComponent(Show, {
1210
+ get when() {
1211
+ return node().data.label;
1212
+ },
1213
+ keyed: true,
1214
+ children: (label) => ssr(_tmpl$4, ssrHydrationKey(), escape(String(label)))
1215
+ })), escape(createComponent(Show, {
1216
+ get when() {
1217
+ return node().data.content;
1218
+ },
1219
+ keyed: true,
1220
+ children: (content) => ssr(_tmpl$5, ssrHydrationKey(), escape(String(content)))
1221
+ })), escape(createComponent(Show, {
1222
+ get when() {
1223
+ return memo(() => !!!node().data.label)() && !node().data.content;
1224
+ },
1225
+ get children() {
1226
+ return createComponent(For, {
1227
+ get each() {
1228
+ return Object.entries(node().data);
1229
+ },
1230
+ children: ([key, val]) => ssr(_tmpl$6, ssrHydrationKey(), escape(key), escape(String(val)))
1231
+ });
1232
+ }
1233
+ })));
1077
1234
  },
1078
1235
  get children() {
1079
- return ssr(_tmpl$2$1, ssrHydrationKey(), ssrStyleProperty("left:", `-${escape(18, true)}px`), ssrStyleProperty("width:", `${escape(12, true)}px`) + ssrStyleProperty(";height:", `${escape(12, true)}px`) + ssrStyleProperty(";margin-top:", `${escape(12, true)}px`) + ssrStyleProperty(";pointer-events:", "all"));
1236
+ return createComponent(Dynamic, mergeProps({ get component() {
1237
+ return props.children;
1238
+ } }, () => node().data));
1080
1239
  }
1081
- })), ssrStyleProperty("right:", `-${escape(18, true)}px`), ssrStyleProperty("width:", `${escape(12, true)}px`) + ssrStyleProperty(";height:", `${escape(12, true)}px`) + ssrStyleProperty(";margin-top:", `${escape(12, true)}px`) + ssrStyleProperty(";pointer-events:", "all"));
1240
+ })), ssrStyleProperty("left:", `-${escape(18, true)}px`), ssrStyleProperty("width:", `${escape(12, true)}px`) + ssrStyleProperty(";height:", `${escape(12, true)}px`) + ssrStyleProperty(";margin-top:", `${escape(12, true)}px`) + ssrStyleProperty(";pointer-events:", "all"), ssrStyleProperty("right:", `-${escape(18, true)}px`), ssrStyleProperty("width:", `${escape(12, true)}px`) + ssrStyleProperty(";height:", `${escape(12, true)}px`) + ssrStyleProperty(";margin-top:", `${escape(12, true)}px`) + ssrStyleProperty(";pointer-events:", "all"));
1241
+ };
1242
+ //#endregion
1243
+ //#region src/ui/components/Panels.tsx
1244
+ var _tmpl$$7 = [
1245
+ "<div",
1246
+ " class=\"absolute top-4 left-4 z-50\">",
1247
+ "</div>"
1248
+ ];
1249
+ var _tmpl$2$3 = [
1250
+ "<div",
1251
+ " class=\"absolute top-4 right-4 z-50\">",
1252
+ "</div>"
1253
+ ];
1254
+ var _tmpl$3$1 = [
1255
+ "<div",
1256
+ " class=\"absolute bottom-4 left-4 z-50\">",
1257
+ "</div>"
1258
+ ];
1259
+ /**
1260
+ * Overlay container rendering custom panel slots positioned around the flowchart
1261
+ * canvas.
1262
+ *
1263
+ * @param props - Flow panels layout properties of type {@linkcode FlowPanels}.
1264
+ *
1265
+ * @returns Rendered overlay panels JSX elements.
1266
+ */
1267
+ var Panels = (props) => {
1268
+ return [
1269
+ createComponent(Show, {
1270
+ get when() {
1271
+ return props.topLeft;
1272
+ },
1273
+ keyed: true,
1274
+ children: (Panel) => {
1275
+ const Component = typeof Panel === "function" ? createComponent(Panel, {}) : Panel;
1276
+ return ssr(_tmpl$$7, ssrHydrationKey(), escape(Component));
1277
+ }
1278
+ }),
1279
+ createComponent(Show, {
1280
+ get when() {
1281
+ return props.topRight;
1282
+ },
1283
+ keyed: true,
1284
+ children: (Panel) => {
1285
+ const Component = typeof Panel === "function" ? createComponent(Panel, {}) : Panel;
1286
+ return ssr(_tmpl$2$3, ssrHydrationKey(), escape(Component));
1287
+ }
1288
+ }),
1289
+ createComponent(Show, {
1290
+ get when() {
1291
+ return props.bottomLeft;
1292
+ },
1293
+ keyed: true,
1294
+ children: (Panel) => {
1295
+ const Component = typeof Panel === "function" ? createComponent(Panel, {}) : Panel;
1296
+ return ssr(_tmpl$3$1, ssrHydrationKey(), escape(Component));
1297
+ }
1298
+ })
1299
+ ];
1082
1300
  };
1083
1301
  //#endregion
1084
1302
  //#region src/ui/components/NodesBoard.tsx
1085
- var _tmpl$$1 = [
1303
+ var _tmpl$$6 = [
1086
1304
  "<div",
1087
- " class=\"relative h-full w-full overflow-auto rounded-lg border-2 border-gray-600\"><!--$-->",
1305
+ " class=\"relative h-full w-full overflow-auto\"><!--$-->",
1088
1306
  "<!--/--><div class=\"",
1089
1307
  "\" style=\"",
1090
1308
  "\"><div class=\"relative h-full w-full\" style=\"",
@@ -1093,32 +1311,36 @@ var _tmpl$$1 = [
1093
1311
  "<!--/--><!--$-->",
1094
1312
  "<!--/--></div></div></div>"
1095
1313
  ];
1096
- var _tmpl$2 = [
1314
+ var _tmpl$2$2 = [
1097
1315
  "<div",
1098
1316
  " class=\"absolute right-4 bottom-4 z-50 flex items-center gap-2 rounded-xl border border-gray-200 bg-white/90 p-2 shadow-lg backdrop-blur-md\"><button type=\"button\" class=\"flex size-9 cursor-pointer items-center justify-center rounded-lg bg-gray-100 text-lg font-bold text-gray-700 shadow-sm transition-all duration-150 hover:bg-gray-200 active:scale-95\" title=\"Zoom out\" aria-label=\"Zoom out\">-</button><button type=\"button\" class=\"h-9 cursor-pointer rounded-lg px-2 text-xs font-semibold text-gray-700 transition-colors hover:bg-gray-100\" title=\"Reset zoom\" aria-label=\"Reset zoom\"><!--$-->",
1099
1317
  "<!--/-->%</button><button type=\"button\" class=\"flex size-9 cursor-pointer items-center justify-center rounded-lg bg-gray-100 text-lg font-bold text-gray-700 shadow-sm transition-all duration-150 hover:bg-gray-200 active:scale-95\" title=\"Zoom in\" aria-label=\"Zoom in\">+</button><div class=\"h-5 w-px bg-gray-300\"></div><button type=\"button\" class=\"flex size-9 cursor-pointer items-center justify-center rounded-lg bg-blue-600 text-white shadow transition-all duration-150 hover:bg-blue-700 active:scale-95\" title=\"Add parent node\" aria-label=\"Add parent node\"><svg class=\"size-5\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"></path></svg></button></div>"
1100
1318
  ];
1101
1319
  var _tmpl$3 = [
1102
1320
  "<div",
1103
- " class=\"relative mx-auto h-[calc(100vh-64px)] w-[calc(100vw-32px)] overflow-hidden\">",
1321
+ " class=\"relative mx-auto h-full w-full overflow-hidden\">",
1104
1322
  "</div>"
1105
1323
  ];
1106
1324
  /**
1107
1325
  * Interactive board component containing the drag-drop viewport, zoom controls,
1108
1326
  * panning gestures, and rendered nodes/edges.
1109
1327
  *
1328
+ * @template | {@linkcode NodeData} `D` - Custom node data dictionary type extending
1329
+ * {@linkcode NodeData}.
1330
+ *
1331
+ * @param props - Board component properties of type {@linkcode Props}.
1332
+ *
1110
1333
  * @returns The rendered Solid component.
1111
1334
  *
1112
1335
  * @see {@linkcode DragBounds}, {@linkcode EdgesBoard}, {@linkcode NodeComponent}, {@linkcode useFlow}, {@linkcode CANVAS_FACTOR}, {@linkcode SCROLL_MULTIPLIER}
1113
1336
  */
1114
- var NodesBoard = () => {
1337
+ var NodesBoard = (props) => {
1115
1338
  let containerRef;
1116
1339
  const [isPanning, setIsPanning] = createSignal(false);
1117
1340
  const [transform, setTransform] = createSignal({
1118
1341
  x: 0,
1119
1342
  y: 0
1120
1343
  });
1121
- const [id, setId] = createSignal("");
1122
1344
  const [ref, setRef] = createSignal();
1123
1345
  let percentX = 0;
1124
1346
  let percentY = 0;
@@ -1164,17 +1386,9 @@ var NodesBoard = () => {
1164
1386
  }, { defer: true }));
1165
1387
  onMount(sendBoard);
1166
1388
  const selectedId = createState(service, { selector: (s) => s.context?.selected });
1167
- /**
1168
- * Determines whether the given element ID matches the currently selected entity.
1169
- *
1170
- * @param id - The node or edge identifier to check.
1171
- *
1172
- * @returns `true` if the item is currently selected, otherwise `false`.
1173
- */
1174
- const selected = (id) => selectedId() === id;
1175
1389
  const nodeIds = createState(service, {
1176
1390
  selector: ({ context }) => {
1177
- return toArray.typed(context.data?.nodes).map((item) => item.id);
1391
+ return toArray$1.typed(context.data?.nodes).map((item) => item.id);
1178
1392
  },
1179
1393
  equals: (prev, next) => prev.length === next.length
1180
1394
  });
@@ -1187,37 +1401,33 @@ var NodesBoard = () => {
1187
1401
  const cHeight = () => `calc((${CANVAS_SIZE}vh - ${MARGIN_Y}px) * ${zoom()})`;
1188
1402
  return ssr(_tmpl$3, ssrHydrationKey(), escape(createComponent(DragDropProvider, {
1189
1403
  onDragMove: ({ draggable: { transform: _transform, node, id } }) => {
1190
- setId(id);
1191
- if (selected(id)) {
1192
- const grandParent = node.parentElement?.parentElement;
1193
- const currentZoom = zoom();
1194
- const minX = 0;
1195
- const maxX = grandParent ? Math.max(0, grandParent.clientWidth / currentZoom - node.offsetWidth) : Infinity;
1196
- const minY = 0;
1197
- const maxY = grandParent ? Math.max(0, grandParent.clientHeight / currentZoom - node.offsetHeight) : Infinity;
1198
- const targetX = node.offsetLeft + _transform.x / currentZoom;
1199
- const targetY = node.offsetTop + _transform.y / currentZoom;
1200
- const x = Math.min(Math.max(targetX, minX), maxX);
1201
- const y = Math.min(Math.max(targetY, minY), maxY);
1202
- const deltaX = x - node.offsetLeft;
1203
- const deltaY = y - node.offsetTop;
1204
- node.style.setProperty("transform", `translate3d(${deltaX}px, ${deltaY}px, 0)`);
1205
- service.send({
1206
- type: "MOVE_IMMEDIATE",
1207
- payload: {
1208
- id: `${id}`,
1209
- x,
1210
- y
1211
- }
1212
- });
1213
- setTransform({
1214
- x: deltaX * currentZoom,
1215
- y: deltaY * currentZoom
1216
- });
1217
- }
1404
+ const grandParent = node.parentElement?.parentElement;
1405
+ const currentZoom = zoom();
1406
+ const minX = 0;
1407
+ const maxX = grandParent ? Math.max(0, grandParent.clientWidth / currentZoom - node.offsetWidth) : Infinity;
1408
+ const minY = 0;
1409
+ const maxY = grandParent ? Math.max(0, grandParent.clientHeight / currentZoom - node.offsetHeight) : Infinity;
1410
+ const targetX = node.offsetLeft + _transform.x / currentZoom;
1411
+ const targetY = node.offsetTop + _transform.y / currentZoom;
1412
+ const x = Math.min(Math.max(targetX, minX), maxX);
1413
+ const y = Math.min(Math.max(targetY, minY), maxY);
1414
+ const deltaX = x - node.offsetLeft;
1415
+ const deltaY = y - node.offsetTop;
1416
+ node.style.setProperty("transform", `translate3d(${deltaX}px, ${deltaY}px, 0)`);
1417
+ service.send({
1418
+ type: "MOVE_IMMEDIATE",
1419
+ payload: {
1420
+ id: `${id}`,
1421
+ x,
1422
+ y
1423
+ }
1424
+ });
1425
+ setTransform({
1426
+ x: deltaX * currentZoom,
1427
+ y: deltaY * currentZoom
1428
+ });
1218
1429
  },
1219
1430
  onDragEnd: ({ draggable: { node, id } }) => {
1220
- if (!selected(id)) return;
1221
1431
  const grandParent = node.parentElement?.parentElement;
1222
1432
  const currentZoom = zoom();
1223
1433
  const minX = 0;
@@ -1248,16 +1458,22 @@ var NodesBoard = () => {
1248
1458
  },
1249
1459
  get children() {
1250
1460
  return [
1251
- ssr(_tmpl$$1, ssrHydrationKey(), escape(createComponent(DragDropSensors, {})), `relative cursor-crosshair overflow-hidden ${isPanning() ? "cursor-grabbing" : ""}`, ssrStyleProperty("height:", escape(cHeight(), true)) + ssrStyleProperty(";width:", escape(cWidth(), true)), ssrStyleProperty("scale:", escape(zoom(), true)) + ssrStyleProperty(";transform-origin:", "top left"), escape(createComponent(DragBounds, {})), escape(createComponent(EdgesBoard, {})), escape(createComponent(For, {
1461
+ ssr(_tmpl$$6, ssrHydrationKey(), escape(createComponent(DragDropSensors, {})), `relative cursor-crosshair overflow-hidden ${isPanning() ? "cursor-grabbing" : ""}`, ssrStyleProperty("height:", escape(cHeight(), true)) + ssrStyleProperty(";width:", escape(cWidth(), true)), ssrStyleProperty("scale:", escape(zoom(), true)) + ssrStyleProperty(";transform-origin:", "top left"), escape(createComponent(DragBounds, {})), escape(createComponent(EdgesBoard, {})), escape(createComponent(For, {
1252
1462
  get each() {
1253
1463
  return nodeIds();
1254
1464
  },
1255
- children: (id) => createComponent(NodeComponent, { id })
1465
+ children: (id) => createComponent(NodeComponent, {
1466
+ id,
1467
+ get children() {
1468
+ return props.component;
1469
+ }
1470
+ })
1256
1471
  }))),
1257
- ssr(_tmpl$2, ssrHydrationKey(), escape(Math.round(zoom() * 100))),
1472
+ createComponent(Panels, mergeProps(() => props.panels)),
1473
+ ssr(_tmpl$2$2, ssrHydrationKey(), escape(Math.round(zoom() * 100))),
1258
1474
  createComponent(Show, {
1259
1475
  get when() {
1260
- return !id() || !selected(id());
1476
+ return !selectedId();
1261
1477
  },
1262
1478
  get children() {
1263
1479
  return createComponent(DragOverlay, { children: "" });
@@ -1269,9 +1485,9 @@ var NodesBoard = () => {
1269
1485
  };
1270
1486
  //#endregion
1271
1487
  //#region src/ui/components/FlowChart.tsx
1272
- var _tmpl$ = [
1488
+ var _tmpl$$5 = [
1273
1489
  "<div",
1274
- " class=\"relative h-full w-full\"><div class=\"relative h-full w-full bg-white bg-size-[30px_30px]\" style=\"",
1490
+ " class=\"relative h-full w-full\"><div class=\"h-full w-full\" style=\"",
1275
1491
  "\">",
1276
1492
  "</div></div>"
1277
1493
  ];
@@ -1279,6 +1495,9 @@ var _tmpl$ = [
1279
1495
  * Flowchart board canvas component that renders interactive nodes, edges, pan/zoom,
1280
1496
  * and toolbar controls.
1281
1497
  *
1498
+ * @template | {@linkcode NodeData} `D` - Custom node data dictionary type extending
1499
+ * {@linkcode NodeData}.
1500
+ *
1282
1501
  * @param props - Flowchart configuration and event handlers of type
1283
1502
  * {@linkcode FlowProps}.
1284
1503
  *
@@ -1291,34 +1510,503 @@ var FlowChart = (props) => {
1291
1510
  const primaryEdges = props.config?.edges;
1292
1511
  const service = useFlow();
1293
1512
  const hasNewEdge = createState(service, { selector: (s) => !!s.context.newEdge });
1513
+ onCleanup(service.pause);
1294
1514
  onMount(() => {
1295
1515
  service.resume();
1296
1516
  service.send({
1297
1517
  type: "CONFIGURE",
1298
1518
  payload: {
1299
1519
  nodes: primaryNodes,
1300
- edges: primaryEdges ?? []
1520
+ edges: primaryEdges ?? [],
1521
+ defaultData: props.defaultData
1301
1522
  }
1302
1523
  });
1303
1524
  });
1304
- onCleanup(service.pause);
1305
- return ssr(_tmpl$, ssrHydrationKey(), ssrStyleProperty("cursor:", hasNewEdge() ? "inherit" : "crosshair") + ssrStyleProperty(";background-image:", "radial-gradient(circle, #b8b8b8bf 1px, rgba(0, 0, 0, 0) 1px)"), escape(createComponent(NodesBoard, {})));
1525
+ return ssr(_tmpl$$5, ssrHydrationKey(), ssrStyleProperty("cursor:", hasNewEdge() ? "inherit" : "crosshair"), escape(createComponent(NodesBoard, {
1526
+ get panels() {
1527
+ return props.panels;
1528
+ },
1529
+ get component() {
1530
+ return props.component;
1531
+ }
1532
+ })));
1533
+ };
1534
+ //#endregion
1535
+ //#region src/ui/utils.ts
1536
+ /**
1537
+ * Merges conditional CSS class names using `clsx` and resolves Tailwind CSS class
1538
+ * conflicts with `twMerge`.
1539
+ *
1540
+ * @param inputs - Variable list of class values, objects, or arrays.
1541
+ *
1542
+ * @returns The resolved single class name string.
1543
+ */
1544
+ function cn(...inputs) {
1545
+ return twMerge(clsx(inputs));
1546
+ }
1547
+ //#endregion
1548
+ //#region src/ui/globals/helpers/espace.ts
1549
+ /**
1550
+ * Returns a zero-width space if string is empty, preserving layout flow.
1551
+ *
1552
+ * @param newText - The input text string.
1553
+ *
1554
+ * @returns The input text or zero-width visible space placeholder.
1555
+ */
1556
+ var espace = (newText) => {
1557
+ if (newText === "") return "​";
1558
+ return newText;
1559
+ };
1560
+ //#endregion
1561
+ //#region src/ui/globals/signals/createTyping.ts
1562
+ /**
1563
+ * Creates a reactive typewriter effect signal with oscillatory typing delay and
1564
+ * optional rewind.
1565
+ *
1566
+ * @param props - Configuration properties of type {@linkcode Props}.
1567
+ *
1568
+ * @returns An object containing the current text signal, type trigger, and text
1569
+ * setter.
1570
+ */
1571
+ var createTyping = ({ content, min, ...props }) => {
1572
+ const rewind = props.rewind ?? false;
1573
+ const rewindDelay = props.rewindDelay ?? 500;
1574
+ const OSCILLATION = Math.min(min / 10 + content.length / 10, 10);
1575
+ const [text, setText] = createSignal("​");
1576
+ let timeoutId;
1577
+ let index = 0;
1578
+ let isTyping = true;
1579
+ const max = min + OSCILLATION;
1580
+ let interval = min;
1581
+ let intervalDirection = 1;
1582
+ const type = () => {
1583
+ if (isTyping) {
1584
+ if (index < content.length) {
1585
+ setText((prev) => prev + content[index]);
1586
+ index++;
1587
+ timeoutId = setTimeout(type, interval);
1588
+ } else if (rewind) {
1589
+ isTyping = false;
1590
+ intervalDirection = 1;
1591
+ timeoutId = setTimeout(type, rewindDelay);
1592
+ }
1593
+ } else if (index > 0) {
1594
+ index--;
1595
+ setText(espace(content.substring(0, index)));
1596
+ timeoutId = setTimeout(type, interval);
1597
+ } else {
1598
+ isTyping = true;
1599
+ intervalDirection = 1;
1600
+ timeoutId = setTimeout(type, rewindDelay);
1601
+ }
1602
+ interval += intervalDirection;
1603
+ if (interval >= max) intervalDirection = -1;
1604
+ else if (interval <= min) intervalDirection = 1;
1605
+ };
1606
+ onCleanup(() => {
1607
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
1608
+ });
1609
+ return {
1610
+ text,
1611
+ type,
1612
+ setText
1613
+ };
1614
+ };
1615
+ //#endregion
1616
+ //#region src/ui/globals/components/atoms/TypingText.tsx
1617
+ var _tmpl$$4 = [
1618
+ "<div",
1619
+ ">",
1620
+ "</div>"
1621
+ ];
1622
+ /**
1623
+ * Renders animated text with a typewriter keystroke animation effect.
1624
+ *
1625
+ * @param props - Component configuration properties of type {@linkcode Props}.
1626
+ *
1627
+ * @returns Rendered animated typing text JSX container.
1628
+ */
1629
+ var TypingText = (props) => {
1630
+ const min = props.interval ?? 62;
1631
+ const rewind = props.rewind ?? false;
1632
+ const rewindDelay = props.rewindDelay ?? 500;
1633
+ const content = props.children;
1634
+ const disabled = createMemo(() => props.disabled ? props.disabled() : false, false);
1635
+ const { text, type, setText } = createTyping({
1636
+ content,
1637
+ min,
1638
+ rewind,
1639
+ rewindDelay
1640
+ });
1641
+ createEffect(on(disabled, () => setText("​")));
1642
+ createEffect(() => {
1643
+ if (disabled()) setText(content);
1644
+ else type();
1645
+ });
1646
+ return ssr(_tmpl$$4, ssrHydrationKey() + ssrAttribute("class", escape(props.class, true), false), escape(text()));
1647
+ };
1648
+ //#endregion
1649
+ //#region src/ui/globals/components/molecules/FadingDots.tsx
1650
+ var _tmpl$$3 = [
1651
+ "<div",
1652
+ " class=\"mt-4 flex items-center justify-center space-x-4\">",
1653
+ "</div>"
1654
+ ];
1655
+ /**
1656
+ * Loading animation component displaying animated pulsing dots.
1657
+ *
1658
+ * @param props - Fading dots configuration properties.
1659
+ * @param props.count - The number of dots to display.
1660
+ * @param props.duration - Animation duration in milliseconds. Defaults to 500.
1661
+ * @param props.innerProps - Additional properties and CSS styles applied to dot
1662
+ * elements.
1663
+ *
1664
+ * @returns Rendered animated dots container JSX element.
1665
+ */
1666
+ var FadingDots = ({ count: length, innerProps = {}, duration = 500 }) => {
1667
+ const [local, rest] = splitProps(innerProps, ["class", "style"]);
1668
+ return ssr(_tmpl$$3, ssrHydrationKey(), escape(Array.from({ length }, (_, i) => ssrElement("span", mergeProps({
1669
+ get ["class"]() {
1670
+ return cn("rounded-full animate-out direction-alternate repeat-infinite fade-out-5 size-4 bg-gray-600", local.class);
1671
+ },
1672
+ get style() {
1673
+ return {
1674
+ ...local.style,
1675
+ "animation-duration": `${duration}ms`,
1676
+ "animation-delay": `${duration / i}ms`
1677
+ };
1678
+ },
1679
+ "aria-hidden": "true"
1680
+ }, rest), void 0, true))));
1681
+ };
1682
+ //#endregion
1683
+ //#region src/ui/globals/components/organisms/LoadingFallback.tsx
1684
+ var _tmpl$$2 = [
1685
+ "<div",
1686
+ " role=\"status\" aria-live=\"polite\" aria-label=\"Chargement en cours\"><div class=\"relative\"><div",
1687
+ " aria-hidden=\"true\"></div><div",
1688
+ " aria-hidden=\"true\"></div></div><div class=\"text-center\"",
1689
+ "><!--$-->",
1690
+ "<!--/--><!--$-->",
1691
+ "<!--/--></div></div>"
1692
+ ];
1693
+ /**
1694
+ * Fallback loading screen component displaying an animated spinner and typewriter
1695
+ * text.
1696
+ *
1697
+ * @example
1698
+ * ```tsx
1699
+ * <Show when={data()} fallback={<LoadingFallback />}>
1700
+ * <DataComponent data={data()!} />
1701
+ * </Show>;
1702
+ * ```;
1703
+ *
1704
+ * @param props - Loading fallback properties of type
1705
+ * {@linkcode LoadingFallbackProps}.
1706
+ *
1707
+ * @returns Rendered full-screen loading fallback JSX element.
1708
+ */
1709
+ var LoadingFallback = (props) => {
1710
+ const message = props.message ?? "Chargement en cours...";
1711
+ const spinnerSizes = {
1712
+ sm: "w-8 h-8",
1713
+ md: "w-12 h-12",
1714
+ lg: "w-16 h-16",
1715
+ xl: "w-24 h-24"
1716
+ };
1717
+ const size = props.size ?? "lg";
1718
+ return ssr(_tmpl$$2, ssrHydrationKey() + ssrAttribute("class", escape(cn("flex flex-col items-center justify-center gap-6", "h-screen w-screen overflow-hidden p-4", props.class), true), false), ssrAttribute("class", escape(cn(spinnerSizes[size], "rounded-full border-4 border-[#4B9CAD]/20", "animate-spin", "border-t-[#4B9CAD]"), true), false), ssrAttribute("class", escape(cn("absolute inset-2", "rounded-full border-4 border-transparent", "border-b-[#4B9CAD]/40", "animate-spin", "animation-duration-[1.5s]", "direction-[reverse]"), true), false), ssrAttribute("aria-label", escape(message, true), false), escape(createComponent(TypingText, {
1719
+ "class": "text-lg font-medium text-gray-700 md:text-xl dark:text-gray-300",
1720
+ get interval() {
1721
+ return props.interval ?? 80;
1722
+ },
1723
+ get rewind() {
1724
+ return props.rewind ?? true;
1725
+ },
1726
+ get rewindDelay() {
1727
+ return props.rewindDelay ?? 2e3;
1728
+ },
1729
+ children: message
1730
+ })), escape(createComponent(FadingDots, {
1731
+ count: 5,
1732
+ innerProps: { class: "bg-[#3d8091]" }
1733
+ })));
1306
1734
  };
1307
1735
  //#endregion
1308
1736
  //#region src/ui/Flow.tsx
1737
+ var _tmpl$$1 = [
1738
+ "<div",
1739
+ " style=\"",
1740
+ "\" class=\"relative flex h-full w-full rounded-lg border-2 border-gray-600 bg-white bg-size-[30px_30px]\">",
1741
+ "</div>"
1742
+ ];
1743
+ var _tmpl$2$1 = [
1744
+ "<div",
1745
+ " class=\"relative flex flex-1 items-center justify-center\">",
1746
+ "</div>"
1747
+ ];
1309
1748
  /**
1310
1749
  * Root Flow component wrapping the {@linkcode FlowChart} inside the flow context
1311
1750
  * provider.
1312
1751
  *
1752
+ * @template | {@linkcode NodeData} `D` - Custom node data dictionary type extending
1753
+ * {@linkcode NodeData}.
1754
+ *
1313
1755
  * @param props - Flow chart configuration and callbacks of type
1314
1756
  * {@linkcode FlowProps}.
1315
1757
  *
1316
1758
  * @returns The rendered Solid component.
1317
1759
  *
1318
- * @see {@linkcode Provider}
1760
+ * @see {@linkcode Provider}, {@linkcode FlowChart}
1761
+ */
1762
+ var Flow = (props) => {
1763
+ const [isMounted, setIsMounted] = createSignal(false);
1764
+ onMount(() => setTimeout(() => setIsMounted(true), props.delay));
1765
+ return ssr(_tmpl$$1, ssrHydrationKey(), ssrStyleProperty("background-image:", "radial-gradient(circle, #b8b8b8bf 1px, rgba(0, 0, 0, 0) 1px)"), escape(createComponent(Show, {
1766
+ get when() {
1767
+ return isMounted();
1768
+ },
1769
+ keyed: true,
1770
+ get fallback() {
1771
+ return ssr(_tmpl$2$1, ssrHydrationKey(), escape(createComponent(LoadingFallback, {
1772
+ message: "Chart is loading...",
1773
+ interval: 35,
1774
+ rewind: true,
1775
+ rewindDelay: 1e3
1776
+ })));
1777
+ },
1778
+ get children() {
1779
+ return createComponent(Provider, { get children() {
1780
+ return createComponent(FlowChart, props);
1781
+ } });
1782
+ }
1783
+ })));
1784
+ };
1785
+ //#endregion
1786
+ //#region src/ui/globals/directives/clickOutside.ts
1787
+ /**
1788
+ * Solid custom directive that triggers a callback when a click occurs outside the
1789
+ * bound element.
1790
+ *
1791
+ * @param el - The bound DOM element.
1792
+ * @param accessor - Accessor returning the callback function to execute.
1793
+ */
1794
+ var clickOutside = (el, accessor) => {
1795
+ const onClick = (e) => {
1796
+ if (!el.contains(e.target)) accessor()?.();
1797
+ };
1798
+ document.body.addEventListener("click", onClick);
1799
+ onCleanup(() => document.body.removeEventListener("click", onClick));
1800
+ };
1801
+ //#endregion
1802
+ //#region src/ui/globals/directives/mouseOut.ts
1803
+ /**
1804
+ * Solid custom directive that triggers a callback when the mouse leaves the bound
1805
+ * element after a specified timer duration. If the user re-enters the component
1806
+ * before the timer expires, the timer is reset/cancelled.
1807
+ *
1808
+ * @param el - The bound DOM element.
1809
+ * @param accessor - Accessor returning a callback, tuple `[callback, delay]`, or
1810
+ * type {@linkcode MouseOutOptions}.
1811
+ */
1812
+ var mouseOut = (el, accessor) => {
1813
+ let timer;
1814
+ const clear = () => {
1815
+ if (timer !== void 0) {
1816
+ clearTimeout(timer);
1817
+ timer = void 0;
1818
+ }
1819
+ };
1820
+ const onMouseEnter = () => {
1821
+ clear();
1822
+ };
1823
+ const onMouseLeave = (event) => {
1824
+ clear();
1825
+ const param = accessor();
1826
+ if (!param) return;
1827
+ let callback;
1828
+ let delay = 0;
1829
+ if (typeof param === "function") callback = param;
1830
+ else if (Array.isArray(param)) {
1831
+ callback = param[0];
1832
+ delay = param[1] ?? 0;
1833
+ } else if (typeof param === "object") {
1834
+ callback = param.callback ?? param.onMouseOut;
1835
+ delay = param.delay ?? 0;
1836
+ }
1837
+ if (!callback) return;
1838
+ if (delay > 0) timer = setTimeout(() => {
1839
+ callback?.(event);
1840
+ timer = void 0;
1841
+ }, delay);
1842
+ else callback(event);
1843
+ };
1844
+ el.addEventListener("mouseleave", onMouseLeave);
1845
+ el.addEventListener("mouseenter", onMouseEnter);
1846
+ onCleanup(() => {
1847
+ clear();
1848
+ el.removeEventListener("mouseleave", onMouseLeave);
1849
+ el.removeEventListener("mouseenter", onMouseEnter);
1850
+ });
1851
+ };
1852
+ //#endregion
1853
+ //#region src/ui/globals/directives/resize.ts
1854
+ /**
1855
+ * Creates a ResizeObserver hook attached to the node content container element,
1856
+ * dispatching RESIZE events to update node dimensions and edge positioning in
1857
+ * real-time.
1858
+ *
1859
+ * @param id - Unique identifier of the flowchart node.
1860
+ *
1861
+ * @returns Ref callback attaching and cleaning up the observer on unmount.
1862
+ */
1863
+ var resize = (id) => (el) => {
1864
+ const service = useFlow();
1865
+ const resize = new ResizeObserver((entries) => {
1866
+ const entry = entries[0].target;
1867
+ if (!entry) return;
1868
+ const width = entry.clientWidth;
1869
+ const height = entry.clientHeight;
1870
+ if (width > 0 && height > 0) service.send({
1871
+ type: "RESIZE",
1872
+ payload: {
1873
+ id,
1874
+ size: {
1875
+ width,
1876
+ height
1877
+ }
1878
+ }
1879
+ });
1880
+ });
1881
+ resize.observe(el);
1882
+ return () => {
1883
+ resize.unobserve(el);
1884
+ resize.disconnect();
1885
+ };
1886
+ };
1887
+ //#endregion
1888
+ //#region src/ui/components/EditPanel.hooks.ts
1889
+ /**
1890
+ * Hook providing reactive state and mutation helpers for editing flowchart node
1891
+ * data.
1892
+ *
1893
+ * @template | {@linkcode NodeData} `D` - Custom node data dictionary type extending
1894
+ * {@linkcode NodeData}.
1895
+ *
1896
+ * @param timeout - Transition delay in milliseconds before closing the panel.
1897
+ * Defaults to `270`.
1898
+ *
1899
+ * @returns An object containing reactive accessors and mutation callbacks for the
1900
+ * active node.
1901
+ *
1902
+ * @see {@linkcode useFlow}
1903
+ */
1904
+ var useHook = (timeout = 270) => {
1905
+ const service = useFlow();
1906
+ const directClose = () => service.send("STOP_EDIT");
1907
+ const [closing, setClosing] = createSignal(false);
1908
+ const senderData = service.sender("SET_NODE_DATA");
1909
+ const _editing = createState(service, {
1910
+ selector: ({ context }) => {
1911
+ const editingId = context.editing;
1912
+ if (!isDefined(editingId)) return;
1913
+ const item = context.data?.nodes?.find((n) => n.id === editingId);
1914
+ if (!isDefined(item)) return;
1915
+ return {
1916
+ id: item.id,
1917
+ data: item.data
1918
+ };
1919
+ },
1920
+ equals: deepEqual
1921
+ });
1922
+ const close = () => {
1923
+ setClosing(true);
1924
+ setTimeout(() => {
1925
+ directClose();
1926
+ setClosing(false);
1927
+ }, timeout);
1928
+ };
1929
+ const updateField = (field, value) => {
1930
+ const current = _editing();
1931
+ if (!current) return;
1932
+ return senderData({
1933
+ ...current,
1934
+ data: {
1935
+ ...current.data,
1936
+ [field]: value
1937
+ }
1938
+ });
1939
+ };
1940
+ const updateData = (data) => {
1941
+ const current = _editing();
1942
+ if (!current) return;
1943
+ return senderData({
1944
+ ...current,
1945
+ data: {
1946
+ ...current.data,
1947
+ ...data
1948
+ }
1949
+ });
1950
+ };
1951
+ return {
1952
+ editing: _editing,
1953
+ updateField,
1954
+ updateData,
1955
+ close,
1956
+ closing,
1957
+ directClose
1958
+ };
1959
+ };
1960
+ //#endregion
1961
+ //#region src/ui/components/EditPanel.tsx
1962
+ var _tmpl$ = [
1963
+ "<div",
1964
+ " class=\"",
1965
+ "\" style=\"",
1966
+ "\"><div class=\"flex flex-col gap-3\"><!--$-->",
1967
+ "<!--/--><!--$-->",
1968
+ "<!--/--></div></div>"
1969
+ ];
1970
+ var _tmpl$2 = [
1971
+ "<div",
1972
+ " class=\"flex items-center justify-between border-b border-gray-100 pb-2\"><div class=\"flex items-center gap-2\"><h3 class=\"text-sm font-bold text-gray-800\">Edit</h3><span class=\"font-mono text-[11px] text-gray-400\">",
1973
+ "</span></div><button type=\"button\" title=\"Close editor\" aria-label=\"Close editor\" class=\"cursor-pointer rounded-full p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-700\"><svg class=\"size-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M18 6L6 18M6 6l12 12\"></path></svg></button></div>"
1974
+ ];
1975
+ /**
1976
+ * Generic overlay edit panel component for modifying the data of the currently
1977
+ * active/double-clicked flowchart node.
1978
+ *
1979
+ * @template | {@linkcode NodeData} `D` - Custom node data dictionary type extending
1980
+ * {@linkcode NodeData}.
1981
+ *
1982
+ * @param props - Edit panel configuration properties of type
1983
+ * {@linkcode EditPanelProps}.
1984
+ *
1985
+ * @returns The rendered Solid component or `null` when no node is being edited.
1986
+ *
1987
+ * @see {@linkcode useHook}, {@linkcode clickOutside}
1319
1988
  */
1320
- var Flow = (props) => createComponent(Provider, { get children() {
1321
- return createComponent(FlowChart, props);
1322
- } });
1989
+ var EditPanel = (props) => {
1990
+ const hooks = useHook(props.timeout);
1991
+ const classList = () => props.classList instanceof Function ? props.classList(hooks) : props.classList;
1992
+ return createComponent(Show, {
1993
+ get when() {
1994
+ return hooks.editing();
1995
+ },
1996
+ children: (node) => ssr(_tmpl$, ssrHydrationKey(), `${escape(cn(`w-80 cursor-default rounded-2xl border border-gray-200 bg-white/95 p-4 shadow-xl backdrop-blur-md transition-all ${props.class ?? ""}`) || "", true)} ${escape(ssrClassList(classList()) || "", true)}`, ssrStyle(props.style), escape(createComponent(Show, {
1997
+ get when() {
1998
+ return props.header;
1999
+ },
2000
+ keyed: true,
2001
+ get fallback() {
2002
+ return ssr(_tmpl$2, ssrHydrationKey(), escape(node().id));
2003
+ },
2004
+ children: (header) => header({
2005
+ id: node().id,
2006
+ close: hooks.close
2007
+ })
2008
+ })), escape(props.children(hooks)))
2009
+ });
2010
+ };
1323
2011
  //#endregion
1324
- export { CLASSES, Flow };
2012
+ export { BOUNDS_CONSTRAINTS, CANVAS_FACTOR, CLASSES, CONTAINER_DIMENSIONS, DEFAULT_DATA, DEFAULT_INPUT_OFFSET, DEFAULT_INPUT_OFFSET_X, DEFAULT_INPUT_OFFSET_Y, DEFAULT_NODES, DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH, DEFAULT_SIZE, EditPanel, Flow, FlowChart, HANDLE_CENTER_X_OFFSET, HANDLE_CENTER_Y, HANDLE_CONTAINER_OFFSET_X, HANDLE_MARGIN_TOP, HANDLE_RADIUS, HANDLE_SIZE, NODE_BORDER_WIDTH, PARENT_CHILD_GAP_WIDTH, Panels, Provider, SCROLL_MULTIPLIER, TOOLBAR_BUFFER, TOOLBAR_TOP_OFFSET, board, clamp, clickOutside, createContext, data, dimension, edgeJSON, extremities, getDefaultOutputOffset, mouseOut, newEdge, nodeDataValue, nodeJSON, nodeOffset, point, resize, useFlow, vector };