@bemedev/mind-flow 0.0.1 → 0.1.1

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 (45) hide show
  1. package/README.md +95 -0
  2. package/lib/helpers/createContext.d.ts +8 -11
  3. package/lib/helpers/createContext.d.ts.map +1 -1
  4. package/lib/index.cjs.js +1 -1
  5. package/lib/index.es.js +240 -235
  6. package/lib/server.cjs.js +181 -121
  7. package/lib/server.es.js +181 -121
  8. package/lib/services/main.machine.d.ts +7 -9
  9. package/lib/services/main.machine.d.ts.map +1 -1
  10. package/lib/services/main.typings.d.ts +3 -3
  11. package/lib/services/main.typings.d.ts.map +1 -1
  12. package/lib/ui/Flow.d.ts +2 -2
  13. package/lib/ui/Flow.d.ts.map +1 -1
  14. package/lib/ui/components/Bounds.d.ts +4 -4
  15. package/lib/ui/components/Bounds.d.ts.map +1 -1
  16. package/lib/ui/components/EdgeComponent.d.ts +2 -2
  17. package/lib/ui/components/EdgeComponent.d.ts.map +1 -1
  18. package/lib/ui/components/EdgesBoard.d.ts +2 -2
  19. package/lib/ui/components/EdgesBoard.d.ts.map +1 -1
  20. package/lib/ui/components/FlowChart.context.d.ts +10 -18
  21. package/lib/ui/components/FlowChart.context.d.ts.map +1 -1
  22. package/lib/ui/components/FlowChart.d.ts +4 -10
  23. package/lib/ui/components/FlowChart.d.ts.map +1 -1
  24. package/lib/ui/components/FlowChart.data.d.ts +10 -11
  25. package/lib/ui/components/FlowChart.data.d.ts.map +1 -1
  26. package/lib/ui/components/NodeComponent.d.ts +2 -2
  27. package/lib/ui/components/NodeComponent.d.ts.map +1 -1
  28. package/lib/ui/components/NodesBoard.d.ts +2 -2
  29. package/lib/ui/components/NodesBoard.d.ts.map +1 -1
  30. package/lib/ui/components/classes.d.ts +2 -2
  31. package/package.json +3 -9
  32. package/src/README.md +2 -2
  33. package/src/helpers/createContext.ts +8 -11
  34. package/src/services/main.machine.ts +18 -46
  35. package/src/services/main.typings.ts +3 -3
  36. package/src/ui/Flow.tsx +3 -2
  37. package/src/ui/components/Bounds.tsx +35 -44
  38. package/src/ui/components/EdgeComponent.tsx +7 -8
  39. package/src/ui/components/EdgesBoard.tsx +3 -2
  40. package/src/ui/components/FlowChart.context.ts +75 -82
  41. package/src/ui/components/FlowChart.data.ts +12 -15
  42. package/src/ui/components/FlowChart.tsx +6 -11
  43. package/src/ui/components/NodeComponent.tsx +13 -25
  44. package/src/ui/components/NodesBoard.tsx +120 -111
  45. package/src/ui/components/classes.ts +2 -2
package/lib/server.cjs.js CHANGED
@@ -11,8 +11,8 @@ let _thisbeyond_solid_dnd = require("@thisbeyond/solid-dnd");
11
11
  let dequal = require("dequal");
12
12
  //#region src/ui/components/classes.ts
13
13
  /**
14
- * List of Tailwind CSS class names used across flowchart UI components to
15
- * ensure compilation safelist.
14
+ * List of Tailwind CSS class names used across flowchart UI components to ensure
15
+ * compilation safelist.
16
16
  */
17
17
  var CLASSES = [
18
18
  "fill-transparent",
@@ -98,19 +98,16 @@ var CLASSES = [
98
98
  //#endregion
99
99
  //#region src/helpers/createContext.ts
100
100
  /**
101
- * Creates a Solid context provider component and a custom hook to consume
102
- * it.
101
+ * Creates a Solid context provider component and a custom hook to consume it.
103
102
  *
104
- * @template T - The type of the context value.
103
+ * @template `T` - The type of the context value.
105
104
  *
106
- * @param context - Factory function returning a non-nullable context value
107
- * of type `NonNullable<T>`.
108
- * @param options - Optional context configuration of type
109
- * {@linkcode Options}.
105
+ * @param context - Factory function returning a non-nullable context value of type
106
+ * `NonNullable<T>`.
107
+ * @param options - Optional context configuration of type {@linkcode Options}.
110
108
  *
111
- * @returns A tuple containing the Provider component of type
112
- * {@linkcode ParentComponent}, the accessor hook, and the Solid context
113
- * object.
109
+ * @returns A tuple containing the Provider component of interface
110
+ * {@linkcode ParentComponent}, the accessor hook, and the Solid context object.
114
111
  */
115
112
  var createContext = (context, options) => {
116
113
  const _context = (0, solid_js.createContext)(context(), options);
@@ -136,7 +133,7 @@ var point = (0, _bemedev_app_bemedev.type)({
136
133
  input: optional(use(point)),
137
134
  output: use(point)
138
135
  }));
139
- /** Schema definition for edge extremities */
136
+ /** Schema definition for edge extremities. */
140
137
  var extremities = (0, _bemedev_app_bemedev.type)({
141
138
  from: "string",
142
139
  to: "string"
@@ -168,8 +165,7 @@ var edgeJSON = extremities;
168
165
  //#endregion
169
166
  //#region src/services/main.machine.ts
170
167
  /**
171
- * Constructs a unique edge identifier string from source and destination
172
- * node IDs.
168
+ * Constructs a unique edge identifier string from source and destination node IDs.
173
169
  *
174
170
  * @param out - The source node ID string.
175
171
  * @param _in - The destination node ID string.
@@ -190,8 +186,8 @@ var buildNodeID = (generated) => {
190
186
  return `node-${generated}`;
191
187
  };
192
188
  /**
193
- * State machine managing flowchart state transitions, nodes, edges,
194
- * selection, and layout actions.
189
+ * State machine managing flowchart state transitions, nodes, edges, selection, and
190
+ * layout actions.
195
191
  */
196
192
  var machine = (0, _bemedev_app.createMachine)({
197
193
  initial: "idle",
@@ -208,15 +204,10 @@ var machine = (0, _bemedev_app.createMachine)({
208
204
  target: "/working"
209
205
  } },
210
206
  working: { on: {
211
- MOVE: {
212
- actions: ["moveNode", "buildUI"],
213
- target: "/construction"
214
- },
215
207
  MOVE_IMMEDIATE: { actions: [{
216
208
  name: "buildImmediateUI",
217
209
  description: "Must be in the ui"
218
210
  }] },
219
- UPDATE_UI: "/construction",
220
211
  ADD_CHILD: {
221
212
  actions: [
222
213
  "generateID",
@@ -250,6 +241,10 @@ var machine = (0, _bemedev_app.createMachine)({
250
241
  ],
251
242
  target: "/construction"
252
243
  },
244
+ MOVE: {
245
+ actions: ["moveNode", "buildUI"],
246
+ target: "/construction"
247
+ },
253
248
  ADD_EDGE: {
254
249
  actions: ["addEdge"],
255
250
  target: "/construction"
@@ -287,6 +282,7 @@ var machine = (0, _bemedev_app.createMachine)({
287
282
  DESELECT: "never",
288
283
  ADD_EDGE: use(extremities)
289
284
  })),
285
+ sync: true,
290
286
  pContext: (0, _bemedev_app_bemedev.type)(({ union }) => ({ generatedId: union("string", "null") })),
291
287
  context: (0, _bemedev_app_bemedev.type)(({ optional, use, array }) => ({
292
288
  data: optional({
@@ -301,8 +297,7 @@ var machine = (0, _bemedev_app.createMachine)({
301
297
  }),
302
298
  selected: optional("string"),
303
299
  updatingUI: optional("boolean")
304
- })),
305
- sync: true
300
+ }))
306
301
  }).provideOptions(({ assign, batch, erase }) => ({ actions: {
307
302
  configure: batch(assign("context.data", () => ({
308
303
  nodes: [],
@@ -386,14 +381,17 @@ var getDefaultOutputOffset = (width = 0) => ({
386
381
  y: 18
387
382
  });
388
383
  /**
389
- * Boundary padding constraints for node dragging and positioning within
390
- * the viewport.
384
+ * Boundary padding constraints for node dragging and positioning within the
385
+ * viewport.
391
386
  */
392
387
  var BOUNDS_CONSTRAINTS = {
393
- horizontal: 20,
394
- vertical: 35
388
+ horizontal: 55,
389
+ vertical: 40
395
390
  };
396
- /** Default node items provided when no initial configuration is given. */
391
+ /**
392
+ * Default node items of type {@linkcode NodeProps} provided when no initial
393
+ * configuration is given.
394
+ */
397
395
  var DEFAULT_NODES = [{
398
396
  id: "node-0",
399
397
  data: {
@@ -409,22 +407,31 @@ var DEFAULT_NODES = [{
409
407
  //#endregion
410
408
  //#region src/ui/components/FlowChart.context.ts
411
409
  /**
412
- * Shared state machine interpreter service instance for flowchart state
413
- * management.
414
- */
415
- var service = (0, _bemedev_app.interpret)(machine, {
416
- context: {},
417
- pContext: { generatedId: null }
418
- });
419
- /**
420
- * Solid Context Provider component and hook for accessing flowchart board
421
- * state, services, and zoom.
410
+ * Solid Context Provider component and hook for accessing flowchart board state,
411
+ * services, and zoom.
422
412
  */
423
413
  var [Provider, useFlow] = createContext(() => {
414
+ /**
415
+ * Shared state machine interpreter service instance for flowchart state
416
+ * management.
417
+ */
418
+ const service = (0, _bemedev_app.interpret)(machine, {
419
+ context: {},
420
+ pContext: { generatedId: null }
421
+ });
424
422
  const zoom = (0, solid_js.createSignal)(1);
425
423
  const newEdge = (0, solid_js.createSignal)();
426
424
  const [boardRef, setBoardRef] = (0, solid_js.createSignal)();
427
425
  const [dimensions, setDimensions] = (0, solid_js.createSignal)({}, { equals: dequal_lite.dequal });
426
+ /**
427
+ * Converts client viewport coordinates to board canvas coordinates adjusted for
428
+ * zoom and scroll.
429
+ *
430
+ * @param clientX - Viewport horizontal coordinate.
431
+ * @param clientY - Viewport vertical coordinate.
432
+ *
433
+ * @returns Board coordinate point of type {@linkcode Point}.
434
+ */
428
435
  const getBoardPoint = (clientX, clientY) => {
429
436
  const el = boardRef();
430
437
  if (!el) return {
@@ -434,11 +441,21 @@ var [Provider, useFlow] = createContext(() => {
434
441
  const rect = el.getBoundingClientRect();
435
442
  const currentZoom = zoom[0]();
436
443
  return {
437
- x: (clientX - rect.left) / currentZoom,
438
- y: (clientY - rect.top) / currentZoom
444
+ x: clientX - rect.left / currentZoom,
445
+ y: clientY - rect.top / currentZoom
439
446
  };
440
447
  };
441
448
  const [edgesPositions, setEdgesPositions] = (0, solid_js.createSignal)({}, { equals: false });
449
+ /**
450
+ * Clamps node coordinates within the visible container boundaries.
451
+ *
452
+ * @param x - Desired horizontal X coordinate.
453
+ * @param y - Desired vertical Y coordinate.
454
+ * @param nodeWidth - Width of the node element in pixels, defaults to `192`.
455
+ * @param nodeHeight - Height of the node element in pixels, defaults to `50`.
456
+ *
457
+ * @returns Clamped coordinate point of type {@linkcode Point}.
458
+ */
442
459
  const clampPosition = (x, y, nodeWidth = 192, nodeHeight = 50) => {
443
460
  const container = boardRef()?.parentElement;
444
461
  if (!container) return {
@@ -518,9 +535,7 @@ var [Provider, useFlow] = createContext(() => {
518
535
  buildUI: batch(voidAction(({ context: { data } }) => {
519
536
  const edges = data?.edges ?? [];
520
537
  setEdgesPositions((data) => {
521
- const array = Object.entries({ ...data }).filter(([id]) => {
522
- return edges?.some((edge) => edge.id === id);
523
- });
538
+ const array = Object.entries({ ...data }).filter(([id]) => edges.some((edge) => edge.id === id));
524
539
  return Object.fromEntries(array);
525
540
  });
526
541
  }), voidAction({
@@ -639,17 +654,18 @@ var [Provider, useFlow] = createContext(() => {
639
654
  getBoardPoint,
640
655
  edgesPositions: [edgesPositions, setEdgesPositions],
641
656
  service,
642
- zoom
657
+ zoom,
658
+ clampPosition
643
659
  };
644
660
  }, { name: "FlowContext" });
645
661
  //#endregion
646
662
  //#region src/ui/components/Bounds.tsx
647
663
  /**
648
- * Drag boundary transformer component that clamps draggable nodes within
649
- * container scroll bounds.
664
+ * Drag boundary transformer component that clamps draggable nodes within container
665
+ * scroll bounds.
650
666
  *
651
- * @returns `null` as this component performs side-effect transformer
652
- * registrations only.
667
+ * @returns `null` as this component performs side-effect transformer registrations
668
+ * only.
653
669
  */
654
670
  var DragBounds = () => {
655
671
  const { board: [ref], zoom: [zoom] } = useFlow();
@@ -658,23 +674,23 @@ var DragBounds = () => {
658
674
  id: "clamp-to-container",
659
675
  order: 100,
660
676
  callback: (transform) => {
661
- const container = ref()?.parentElement;
677
+ const container = ref();
662
678
  const activeDraggable = state.active.draggable;
663
679
  if (!container || !activeDraggable) return transform;
664
- const draggableLayout = activeDraggable.layout;
665
- const containerRect = container.getBoundingClientRect();
666
- const innerLeft = containerRect.left + container.clientLeft;
667
- const innerTop = containerRect.top + container.clientTop;
668
- const innerRight = innerLeft + container.clientWidth;
669
- const innerBottom = innerTop + container.clientHeight;
680
+ const node = activeDraggable.node;
681
+ if (!node) return transform;
670
682
  const currentZoom = zoom();
671
- const minX = innerLeft - draggableLayout.left + BOUNDS_CONSTRAINTS.horizontal * currentZoom;
672
- const maxX = Math.max(minX, innerRight - draggableLayout.right - BOUNDS_CONSTRAINTS.horizontal * currentZoom);
673
- const minY = innerTop - draggableLayout.top + BOUNDS_CONSTRAINTS.vertical * currentZoom;
674
- const maxY = Math.max(minY, innerBottom - draggableLayout.bottom - BOUNDS_CONSTRAINTS.vertical * currentZoom);
683
+ const minBoardX = BOUNDS_CONSTRAINTS.horizontal;
684
+ const maxBoardX = Math.max(minBoardX, container.clientWidth / currentZoom - node.offsetWidth - BOUNDS_CONSTRAINTS.horizontal);
685
+ const minBoardY = BOUNDS_CONSTRAINTS.vertical;
686
+ const maxBoardY = Math.max(minBoardY, container.clientHeight / currentZoom - node.offsetHeight - BOUNDS_CONSTRAINTS.vertical);
687
+ const minTransformX = (minBoardX - node.offsetLeft) * currentZoom;
688
+ const maxTransformX = (maxBoardX - node.offsetLeft) * currentZoom;
689
+ const minTransformY = (minBoardY - node.offsetTop) * currentZoom;
690
+ const maxTransformY = (maxBoardY - node.offsetTop) * currentZoom;
675
691
  return {
676
- x: Math.min(Math.max(transform.x, minX), maxX),
677
- y: Math.min(Math.max(transform.y, minY), maxY)
692
+ x: Math.min(Math.max(transform.x, minTransformX), maxTransformX),
693
+ y: Math.min(Math.max(transform.y, minTransformY), maxTransformY)
678
694
  };
679
695
  }
680
696
  };
@@ -695,7 +711,7 @@ var _tmpl$$4 = [
695
711
  "\"",
696
712
  "></path>"
697
713
  ];
698
- var _tmpl$2$1 = [
714
+ var _tmpl$2$2 = [
699
715
  "<g",
700
716
  " cursor=\"pointer\" transform=\"",
701
717
  "\" style=\"",
@@ -710,8 +726,8 @@ var _tmpl$2$1 = [
710
726
  */
711
727
  var calculateOffset = (value) => value * 100 / 200;
712
728
  /**
713
- * Generates an SVG cubic bezier path `d` string between coordinates `(x0,
714
- * y0)` and `(x1, y1)`.
729
+ * Generates an SVG cubic bezier path `d` string between coordinates `(x0, y0)` and
730
+ * `(x1, y1)`.
715
731
  *
716
732
  * @param vector - Vector coordinates of type {@linkcode Vector}.
717
733
  *
@@ -721,8 +737,8 @@ var draw = ({ x0, y0, x1, y1 }) => {
721
737
  return `M ${x0} ${y0} C ${x0 + calculateOffset(Math.abs(x1 - x0))} ${y0}, ${x1 - calculateOffset(Math.abs(x1 - x0))} ${y1}, ${x1} ${y1}`;
722
738
  };
723
739
  /**
724
- * SVG edge component rendering bezier curves and delete interaction
725
- * handles for node connections.
740
+ * SVG edge component rendering bezier curves and delete interaction handles for node
741
+ * connections.
726
742
  *
727
743
  * @param props - Edge properties of type {@linkcode Props}.
728
744
  *
@@ -748,7 +764,7 @@ var EdgeComponent = (props) => {
748
764
  return selected();
749
765
  },
750
766
  get children() {
751
- return (0, solid_js_web.ssr)(_tmpl$2$1, (0, solid_js_web.ssrHydrationKey)(), `translate(${(0, solid_js_web.escape)(middlePoint().x, true)}, ${(0, solid_js_web.escape)(middlePoint().y, true)})`, (0, solid_js_web.ssrStyleProperty)("pointer-events:", "all") + (0, solid_js_web.ssrStyleProperty)(";z-index:", "30"));
767
+ return (0, solid_js_web.ssr)(_tmpl$2$2, (0, solid_js_web.ssrHydrationKey)(), `translate(${(0, solid_js_web.escape)(middlePoint().x, true)}, ${(0, solid_js_web.escape)(middlePoint().y, true)})`, (0, solid_js_web.ssrStyleProperty)("pointer-events:", "all") + (0, solid_js_web.ssrStyleProperty)(";z-index:", "30"));
752
768
  }
753
769
  })];
754
770
  };
@@ -761,8 +777,8 @@ var _tmpl$$3 = [
761
777
  "<!--/--></svg>"
762
778
  ];
763
779
  /**
764
- * SVG board overlay component that renders all active connecting edges and
765
- * ongoing edge creation previews.
780
+ * SVG board overlay component that renders all active connecting edges and ongoing
781
+ * edge creation previews.
766
782
  *
767
783
  * @returns The rendered SVG JSX element.
768
784
  */
@@ -812,13 +828,13 @@ var _tmpl$$2 = [
812
828
  " class=\"cursor-pointer overflow-visible rounded-full bg-green-500 p-0.5 text-center font-bold hover:bg-green-600\" style=\"",
813
829
  "\" 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>"
814
830
  ];
815
- var _tmpl$2 = [
831
+ var _tmpl$2$1 = [
816
832
  "<div",
817
833
  " id=\"outputs\" class=\"pointer-events-none absolute top-0 z-10 flex cursor-default flex-col\" style=\"",
818
834
  "\"><div class=\"cursor-default rounded-full bg-[#e38b29] shadow-md\" style=\"",
819
835
  "\"></div></div>"
820
836
  ];
821
- var _tmpl$3 = [
837
+ var _tmpl$3$1 = [
822
838
  "<div",
823
839
  " class=\"",
824
840
  "\" style=\"",
@@ -844,8 +860,8 @@ var _tmpl$5 = [
844
860
  "</div>"
845
861
  ];
846
862
  /**
847
- * Interactive flowchart node component supporting dragging, selection,
848
- * handle connections, and child/sibling creation.
863
+ * Interactive flowchart node component supporting dragging, selection, handle
864
+ * connections, and child/sibling creation.
849
865
  *
850
866
  * @param props - Node rendering properties of type {@linkcode Props}.
851
867
  *
@@ -855,17 +871,17 @@ var NodeComponent = (props) => {
855
871
  const [ref, setRef] = (0, solid_js.createSignal)();
856
872
  const { dimensions: [dimensions, setDimensions], newEdge: [newEdge, setNewEdge], getBoardPoint, service, zoom: [zoom] } = useFlow();
857
873
  const selected = (0, _bemedev_app_solidjs.useState)(service, { selector: (s) => s.context.selected === props.id });
858
- (0, solid_js.createEffect)(() => {
874
+ (0, solid_js.onMount)(() => {
859
875
  ref();
860
876
  zoom();
861
877
  });
862
878
  const hasParent = (0, _bemedev_app_solidjs.useState)(service, { selector: ({ context: { data } }) => {
863
879
  const edges = data?.edges;
864
880
  if (!edges) return false;
865
- return Object.values(edges).some((edge) => edge.to === props.id);
881
+ return edges.some(({ to }) => to === props.id);
866
882
  } });
867
883
  (0, _thisbeyond_solid_dnd.createDraggable)(props.id);
868
- return (0, solid_js_web.ssr)(_tmpl$3, (0, solid_js_web.ssrHydrationKey)() + (0, solid_js_web.ssrAttribute)("id", (0, solid_js_web.escape)(props.id, true), false), `flex flex-col absolute cursor-grab bg-white rounded-md shadow-md select-none transition-[border,box-shadow] duration-200 ease-in-out hover:shadow-lg draggable ${selected() ? "border border-[#e38c29] z-[100]" : ""} ${!selected() ? "border border-[#e6d4be] z-[1]" : ""} min-w-48`, (0, solid_js_web.ssrStyleProperty)("top:", `${(0, solid_js_web.escape)(props.y, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";left:", `${(0, solid_js_web.escape)(props.x, true)}px`), `pointer-events-none absolute flex items-center justify-end -top-7.5 right-0 transition-all duration-200 ease-in-out space-x-2 ${selected() ? "w-full opacity-100" : ""} ${!selected() ? "w-0 -right-3 opacity-0 overflow-hidden" : ""}`, (0, solid_js_web.ssrStyleProperty)("overflow:", "visible") + (0, solid_js_web.ssrStyleProperty)(";pointer-events:", "all") + (0, solid_js_web.ssrStyleProperty)(";width:", `${(0, solid_js_web.escape)(12, true) * 2}px`) + (0, solid_js_web.ssrStyleProperty)(";height:", `${(0, solid_js_web.escape)(12, true) * 2}px`), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(solid_js.Show, {
884
+ return (0, solid_js_web.ssr)(_tmpl$3$1, (0, solid_js_web.ssrHydrationKey)() + (0, solid_js_web.ssrAttribute)("id", (0, solid_js_web.escape)(props.id, true), false), `flex flex-col absolute cursor-grab bg-white rounded-md shadow-md select-none transition-[border,box-shadow] duration-200 ease-in-out hover:shadow-lg draggable ${selected() ? "border border-[#e38c29] z-[100]" : ""} ${!selected() ? "border border-[#e6d4be] z-[1]" : ""} min-w-48`, (0, solid_js_web.ssrStyleProperty)("top:", `${(0, solid_js_web.escape)(props.y, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";left:", `${(0, solid_js_web.escape)(props.x, true)}px`), `pointer-events-none absolute flex items-center justify-end -top-7.5 right-0 transition-all duration-200 ease-in-out space-x-2 ${selected() ? "w-full opacity-100" : ""} ${!selected() ? "w-0 -right-3 opacity-0 overflow-hidden" : ""}`, (0, solid_js_web.ssrStyleProperty)("overflow:", "visible") + (0, solid_js_web.ssrStyleProperty)(";pointer-events:", "all") + (0, solid_js_web.ssrStyleProperty)(";width:", `${(0, solid_js_web.escape)(12, true) * 2}px`) + (0, solid_js_web.ssrStyleProperty)(";height:", `${(0, solid_js_web.escape)(12, true) * 2}px`), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(solid_js.Show, {
869
885
  get when() {
870
886
  return hasParent();
871
887
  },
@@ -889,7 +905,7 @@ var NodeComponent = (props) => {
889
905
  return props.input || hasParent();
890
906
  },
891
907
  get children() {
892
- return (0, solid_js_web.ssr)(_tmpl$2, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.ssrStyleProperty)("left:", `-${(0, solid_js_web.escape)(18, true)}px`), (0, solid_js_web.ssrStyleProperty)("width:", `${(0, solid_js_web.escape)(12, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";height:", `${(0, solid_js_web.escape)(12, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";margin-top:", `${(0, solid_js_web.escape)(12, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";pointer-events:", "all"));
908
+ return (0, solid_js_web.ssr)(_tmpl$2$1, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.ssrStyleProperty)("left:", `-${(0, solid_js_web.escape)(18, true)}px`), (0, solid_js_web.ssrStyleProperty)("width:", `${(0, solid_js_web.escape)(12, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";height:", `${(0, solid_js_web.escape)(12, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";margin-top:", `${(0, solid_js_web.escape)(12, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";pointer-events:", "all"));
893
909
  }
894
910
  })), (0, solid_js_web.ssrStyleProperty)("right:", `-${(0, solid_js_web.escape)(18, true)}px`), (0, solid_js_web.ssrStyleProperty)("width:", `${(0, solid_js_web.escape)(12, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";height:", `${(0, solid_js_web.escape)(12, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";margin-top:", `${(0, solid_js_web.escape)(12, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";pointer-events:", "all"));
895
911
  };
@@ -897,22 +913,33 @@ var NodeComponent = (props) => {
897
913
  //#region src/ui/components/NodesBoard.tsx
898
914
  var _tmpl$$1 = [
899
915
  "<div",
900
- " class=\"relative mx-auto h-[calc(100vh-64px)] w-[calc(100vw-32px)]\"><div class=\"relative h-full w-full overflow-scroll rounded-lg border-2 border-gray-600\"><!--$-->",
901
- "<!--/--><!--$-->",
916
+ " class=\"relative h-full w-full overflow-scroll rounded-lg border-2 border-gray-600\"><!--$-->",
902
917
  "<!--/--><div class=\"",
903
918
  "\" style=\"",
919
+ "\"><div class=\"relative h-full w-full\" style=\"",
904
920
  "\"><!--$-->",
905
921
  "<!--/--><!--$-->",
906
- "<!--/--></div></div><div 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\"><!--$-->",
907
- "<!--/-->%</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></div>"
922
+ "<!--/--><!--$-->",
923
+ "<!--/--></div></div></div>"
924
+ ];
925
+ var _tmpl$2 = [
926
+ "<div",
927
+ " 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\"><!--$-->",
928
+ "<!--/-->%</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>"
929
+ ];
930
+ var _tmpl$3 = [
931
+ "<div",
932
+ " class=\"relative mx-auto h-[calc(100vh-64px)] w-[calc(100vw-32px)] overflow-hidden\">",
933
+ "</div>"
908
934
  ];
909
935
  /**
910
- * Interactive board component containing the drag-drop viewport, zoom
911
- * controls, panning gestures, and rendered nodes/edges.
936
+ * Interactive board component containing the drag-drop viewport, zoom controls,
937
+ * panning gestures, and rendered nodes/edges.
912
938
  *
913
939
  * @returns The rendered Solid component.
914
940
  */
915
941
  var NodesBoard = () => {
942
+ let containerRef;
916
943
  const [isPanning, setIsPanning] = (0, solid_js.createSignal)(false);
917
944
  const [transform, setTransform] = (0, solid_js.createSignal)({
918
945
  x: 0,
@@ -920,11 +947,23 @@ var NodesBoard = () => {
920
947
  });
921
948
  const [id, setId] = (0, solid_js.createSignal)("");
922
949
  const [previousZoom, setPreviousZoom] = (0, solid_js.createSignal)();
923
- let cleanupPanning = () => {};
924
- (0, solid_js.onCleanup)(cleanupPanning);
950
+ let percentX = 0;
951
+ let percentY = 0;
925
952
  const { board: [, setRef], service, newEdge: [newEdge], zoom: [zoom, setZoom] } = useFlow();
926
- (0, solid_js.createEffect)((0, solid_js.on)(zoom, () => {}, { defer: true }));
953
+ (0, solid_js.createEffect)((0, solid_js.on)(zoom, () => {
954
+ const maxScrollX = containerRef.scrollWidth - containerRef.clientWidth;
955
+ const maxScrollY = containerRef.scrollHeight - containerRef.clientHeight;
956
+ if (maxScrollX > 0) containerRef.scrollLeft = percentX * maxScrollX;
957
+ if (maxScrollY > 0) containerRef.scrollTop = percentY * maxScrollY;
958
+ }, { defer: true }));
927
959
  const selectedId = (0, _bemedev_app_solidjs.useState)(service, { selector: (s) => s.context?.selected });
960
+ /**
961
+ * Determines whether the given element ID matches the currently selected entity.
962
+ *
963
+ * @param id - The node or edge identifier to check.
964
+ *
965
+ * @returns `true` if the item is currently selected, otherwise `false`.
966
+ */
928
967
  const selected = (id) => selectedId() === id;
929
968
  const nodes = (0, _bemedev_app_solidjs.useState)(service, {
930
969
  selector: (s) => {
@@ -939,21 +978,27 @@ var NodesBoard = () => {
939
978
  },
940
979
  equals: dequal.dequal
941
980
  });
942
- const cDim = () => {
943
- const _zoom = zoom();
944
- if (_zoom < 1) return 500 * _zoom;
945
- return 500;
946
- };
947
- return (0, solid_js_web.createComponent)(_thisbeyond_solid_dnd.DragDropProvider, {
948
- onDragMove: ({ draggable: { transform: _transform, node, id }, overlay }) => {
981
+ const CANVAS_SIZE = 500;
982
+ const MARGIN_X = 265;
983
+ const MARGIN_Y = 425;
984
+ const cWidth = () => `calc((${CANVAS_SIZE}vw - ${MARGIN_X}px) * ${zoom()})`;
985
+ const cHeight = () => `calc((${CANVAS_SIZE}vh - ${MARGIN_Y}px) * ${zoom()})`;
986
+ return (0, solid_js_web.ssr)(_tmpl$3, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(_thisbeyond_solid_dnd.DragDropProvider, {
987
+ onDragMove: ({ draggable: { transform: _transform, node, id } }) => {
949
988
  setId(id);
950
989
  if (selected(id)) {
951
- const x = node.offsetLeft + _transform.x / zoom();
952
- const y = node.offsetTop + _transform.y / zoom();
953
- overlay?.node.style.setProperty("top", y * 2 + "px");
954
- overlay?.node.style.setProperty("left", x * 2 + "px");
955
- const deltaX = _transform.x / zoom();
956
- const deltaY = _transform.y / zoom();
990
+ const grandParent = node.parentElement?.parentElement;
991
+ const currentZoom = zoom();
992
+ const minX = 0;
993
+ const maxX = grandParent ? Math.max(0, grandParent.clientWidth / currentZoom - node.offsetWidth) : Infinity;
994
+ const minY = 0;
995
+ const maxY = grandParent ? Math.max(0, grandParent.clientHeight / currentZoom - node.offsetHeight) : Infinity;
996
+ const targetX = node.offsetLeft + _transform.x / currentZoom;
997
+ const targetY = node.offsetTop + _transform.y / currentZoom;
998
+ const x = Math.min(Math.max(targetX, minX), maxX);
999
+ const y = Math.min(Math.max(targetY, minY), maxY);
1000
+ const deltaX = x - node.offsetLeft;
1001
+ const deltaY = y - node.offsetTop;
957
1002
  node.style.setProperty("transform", `translate3d(${deltaX}px, ${deltaY}px, 0)`);
958
1003
  service.send({
959
1004
  type: "MOVE_IMMEDIATE",
@@ -963,13 +1008,24 @@ var NodesBoard = () => {
963
1008
  y
964
1009
  }
965
1010
  });
966
- setTransform({ ..._transform });
1011
+ setTransform({
1012
+ x: deltaX * currentZoom,
1013
+ y: deltaY * currentZoom
1014
+ });
967
1015
  }
968
1016
  },
969
1017
  onDragEnd: ({ draggable: { node, id } }) => {
970
1018
  if (!selected(id)) return;
971
- const X = node.offsetLeft + transform().x / zoom();
972
- const Y = node.offsetTop + transform().y / zoom();
1019
+ const grandParent = node.parentElement?.parentElement;
1020
+ const currentZoom = zoom();
1021
+ const minX = 0;
1022
+ const maxX = grandParent ? Math.max(0, grandParent.clientWidth / currentZoom - node.offsetWidth) : Infinity;
1023
+ const minY = 0;
1024
+ const maxY = grandParent ? Math.max(0, grandParent.clientHeight / currentZoom - node.offsetHeight) : Infinity;
1025
+ const rawX = node.offsetLeft + transform().x / currentZoom;
1026
+ const rawY = node.offsetTop + transform().y / currentZoom;
1027
+ const X = Math.min(Math.max(rawX, minX), maxX);
1028
+ const Y = Math.min(Math.max(rawY, minY), maxY);
973
1029
  node.style.setProperty("top", Y + "px");
974
1030
  node.style.setProperty("left", X + "px");
975
1031
  node.style.removeProperty("transform");
@@ -989,21 +1045,25 @@ var NodesBoard = () => {
989
1045
  }, 0);
990
1046
  },
991
1047
  get children() {
992
- return [(0, solid_js_web.ssr)(_tmpl$$1, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(_thisbeyond_solid_dnd.DragDropSensors, {})), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(DragBounds, {})), `relative cursor-crosshair ${isPanning() ? "cursor-grabbing" : ""}`, (0, solid_js_web.ssrStyleProperty)("height:", `calc(${(0, solid_js_web.escape)(cDim(), true)}vh - 85px)`) + (0, solid_js_web.ssrStyleProperty)(";width:", `calc(${(0, solid_js_web.escape)(cDim(), true)}vw - 53px)`) + (0, solid_js_web.ssrStyleProperty)(";scale:", (0, solid_js_web.escape)(zoom(), true)) + (0, solid_js_web.ssrStyleProperty)(";transform-origin:", "top left"), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(EdgesBoard, {})), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(solid_js.For, {
993
- get each() {
994
- return nodes();
995
- },
996
- children: NodeComponent
997
- })), (0, solid_js_web.escape)(Math.round(zoom() * 100))), (0, solid_js_web.createComponent)(solid_js.Show, {
998
- get when() {
999
- return !id() || !selected(id());
1000
- },
1001
- get children() {
1002
- return (0, solid_js_web.createComponent)(_thisbeyond_solid_dnd.DragOverlay, { children: "" });
1003
- }
1004
- })];
1048
+ return [
1049
+ (0, solid_js_web.ssr)(_tmpl$$1, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(_thisbeyond_solid_dnd.DragDropSensors, {})), `relative cursor-crosshair overflow-hidden ${isPanning() ? "cursor-grabbing" : ""}`, (0, solid_js_web.ssrStyleProperty)("height:", (0, solid_js_web.escape)(cHeight(), true)) + (0, solid_js_web.ssrStyleProperty)(";width:", (0, solid_js_web.escape)(cWidth(), true)), (0, solid_js_web.ssrStyleProperty)("scale:", (0, solid_js_web.escape)(zoom(), true)) + (0, solid_js_web.ssrStyleProperty)(";transform-origin:", "top left"), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(DragBounds, {})), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(EdgesBoard, {})), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(solid_js.For, {
1050
+ get each() {
1051
+ return nodes();
1052
+ },
1053
+ children: NodeComponent
1054
+ }))),
1055
+ (0, solid_js_web.ssr)(_tmpl$2, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.escape)(Math.round(zoom() * 100))),
1056
+ (0, solid_js_web.createComponent)(solid_js.Show, {
1057
+ get when() {
1058
+ return !id() || !selected(id());
1059
+ },
1060
+ get children() {
1061
+ return (0, solid_js_web.createComponent)(_thisbeyond_solid_dnd.DragOverlay, { children: "" });
1062
+ }
1063
+ })
1064
+ ];
1005
1065
  }
1006
- });
1066
+ })));
1007
1067
  };
1008
1068
  //#endregion
1009
1069
  //#region src/ui/components/FlowChart.tsx
@@ -1014,8 +1074,8 @@ var _tmpl$ = [
1014
1074
  "</div></div>"
1015
1075
  ];
1016
1076
  /**
1017
- * Flowchart board canvas component that renders interactive nodes, edges,
1018
- * pan/zoom, and toolbar controls.
1077
+ * Flowchart board canvas component that renders interactive nodes, edges, pan/zoom,
1078
+ * and toolbar controls.
1019
1079
  *
1020
1080
  * @param props - Flowchart configuration and event handlers of type
1021
1081
  * {@linkcode FlowProps}.
@@ -1042,8 +1102,8 @@ var FlowChart = (props) => {
1042
1102
  //#endregion
1043
1103
  //#region src/ui/Flow.tsx
1044
1104
  /**
1045
- * Root Flow component wrapping the {@linkcode FlowChart} inside the flow
1046
- * context provider.
1105
+ * Root Flow component wrapping the {@linkcode FlowChart} inside the flow context
1106
+ * provider.
1047
1107
  *
1048
1108
  * @param props - Flow chart configuration and callbacks of type
1049
1109
  * {@linkcode FlowProps}.