@bemedev/mind-flow 0.0.1 → 0.1.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 (44) hide show
  1. package/README.md +95 -0
  2. package/lib/helpers/createContext.d.ts +5 -8
  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 +148 -117
  7. package/lib/server.es.js +148 -117
  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 +2 -2
  11. package/lib/ui/Flow.d.ts +2 -2
  12. package/lib/ui/Flow.d.ts.map +1 -1
  13. package/lib/ui/components/Bounds.d.ts +4 -4
  14. package/lib/ui/components/Bounds.d.ts.map +1 -1
  15. package/lib/ui/components/EdgeComponent.d.ts +2 -2
  16. package/lib/ui/components/EdgeComponent.d.ts.map +1 -1
  17. package/lib/ui/components/EdgesBoard.d.ts +2 -2
  18. package/lib/ui/components/EdgesBoard.d.ts.map +1 -1
  19. package/lib/ui/components/FlowChart.context.d.ts +10 -18
  20. package/lib/ui/components/FlowChart.context.d.ts.map +1 -1
  21. package/lib/ui/components/FlowChart.d.ts +4 -10
  22. package/lib/ui/components/FlowChart.d.ts.map +1 -1
  23. package/lib/ui/components/FlowChart.data.d.ts +6 -10
  24. package/lib/ui/components/FlowChart.data.d.ts.map +1 -1
  25. package/lib/ui/components/NodeComponent.d.ts +2 -2
  26. package/lib/ui/components/NodeComponent.d.ts.map +1 -1
  27. package/lib/ui/components/NodesBoard.d.ts +2 -2
  28. package/lib/ui/components/NodesBoard.d.ts.map +1 -1
  29. package/lib/ui/components/classes.d.ts +2 -2
  30. package/package.json +2 -2
  31. package/src/README.md +2 -2
  32. package/src/helpers/createContext.ts +5 -8
  33. package/src/services/main.machine.ts +18 -46
  34. package/src/services/main.typings.ts +2 -2
  35. package/src/ui/Flow.tsx +3 -2
  36. package/src/ui/components/Bounds.tsx +35 -44
  37. package/src/ui/components/EdgeComponent.tsx +7 -8
  38. package/src/ui/components/EdgesBoard.tsx +3 -2
  39. package/src/ui/components/FlowChart.context.ts +56 -82
  40. package/src/ui/components/FlowChart.data.ts +8 -14
  41. package/src/ui/components/FlowChart.tsx +6 -11
  42. package/src/ui/components/NodeComponent.tsx +13 -25
  43. package/src/ui/components/NodesBoard.tsx +109 -111
  44. 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
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
109
  * @returns A tuple containing the Provider component of type
112
- * {@linkcode ParentComponent}, the accessor hook, and the Solid context
113
- * object.
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);
@@ -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,12 +381,12 @@ 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
391
  /** Default node items provided when no initial configuration is given. */
397
392
  var DEFAULT_NODES = [{
@@ -409,18 +404,18 @@ var DEFAULT_NODES = [{
409
404
  //#endregion
410
405
  //#region src/ui/components/FlowChart.context.ts
411
406
  /**
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.
407
+ * Solid Context Provider component and hook for accessing flowchart board state,
408
+ * services, and zoom.
422
409
  */
423
410
  var [Provider, useFlow] = createContext(() => {
411
+ /**
412
+ * Shared state machine interpreter service instance for flowchart state
413
+ * management.
414
+ */
415
+ const service = (0, _bemedev_app.interpret)(machine, {
416
+ context: {},
417
+ pContext: { generatedId: null }
418
+ });
424
419
  const zoom = (0, solid_js.createSignal)(1);
425
420
  const newEdge = (0, solid_js.createSignal)();
426
421
  const [boardRef, setBoardRef] = (0, solid_js.createSignal)();
@@ -434,8 +429,8 @@ var [Provider, useFlow] = createContext(() => {
434
429
  const rect = el.getBoundingClientRect();
435
430
  const currentZoom = zoom[0]();
436
431
  return {
437
- x: (clientX - rect.left) / currentZoom,
438
- y: (clientY - rect.top) / currentZoom
432
+ x: clientX - rect.left / currentZoom,
433
+ y: clientY - rect.top / currentZoom
439
434
  };
440
435
  };
441
436
  const [edgesPositions, setEdgesPositions] = (0, solid_js.createSignal)({}, { equals: false });
@@ -518,9 +513,7 @@ var [Provider, useFlow] = createContext(() => {
518
513
  buildUI: batch(voidAction(({ context: { data } }) => {
519
514
  const edges = data?.edges ?? [];
520
515
  setEdgesPositions((data) => {
521
- const array = Object.entries({ ...data }).filter(([id]) => {
522
- return edges?.some((edge) => edge.id === id);
523
- });
516
+ const array = Object.entries({ ...data }).filter(([id]) => edges.some((edge) => edge.id === id));
524
517
  return Object.fromEntries(array);
525
518
  });
526
519
  }), voidAction({
@@ -639,17 +632,18 @@ var [Provider, useFlow] = createContext(() => {
639
632
  getBoardPoint,
640
633
  edgesPositions: [edgesPositions, setEdgesPositions],
641
634
  service,
642
- zoom
635
+ zoom,
636
+ clampPosition
643
637
  };
644
638
  }, { name: "FlowContext" });
645
639
  //#endregion
646
640
  //#region src/ui/components/Bounds.tsx
647
641
  /**
648
- * Drag boundary transformer component that clamps draggable nodes within
649
- * container scroll bounds.
642
+ * Drag boundary transformer component that clamps draggable nodes within container
643
+ * scroll bounds.
650
644
  *
651
- * @returns `null` as this component performs side-effect transformer
652
- * registrations only.
645
+ * @returns `null` as this component performs side-effect transformer registrations
646
+ * only.
653
647
  */
654
648
  var DragBounds = () => {
655
649
  const { board: [ref], zoom: [zoom] } = useFlow();
@@ -658,23 +652,23 @@ var DragBounds = () => {
658
652
  id: "clamp-to-container",
659
653
  order: 100,
660
654
  callback: (transform) => {
661
- const container = ref()?.parentElement;
655
+ const container = ref();
662
656
  const activeDraggable = state.active.draggable;
663
657
  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;
658
+ const node = activeDraggable.node;
659
+ if (!node) return transform;
670
660
  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);
661
+ const minBoardX = BOUNDS_CONSTRAINTS.horizontal;
662
+ const maxBoardX = Math.max(minBoardX, container.clientWidth / currentZoom - node.offsetWidth - BOUNDS_CONSTRAINTS.horizontal);
663
+ const minBoardY = BOUNDS_CONSTRAINTS.vertical;
664
+ const maxBoardY = Math.max(minBoardY, container.clientHeight / currentZoom - node.offsetHeight - BOUNDS_CONSTRAINTS.vertical);
665
+ const minTransformX = (minBoardX - node.offsetLeft) * currentZoom;
666
+ const maxTransformX = (maxBoardX - node.offsetLeft) * currentZoom;
667
+ const minTransformY = (minBoardY - node.offsetTop) * currentZoom;
668
+ const maxTransformY = (maxBoardY - node.offsetTop) * currentZoom;
675
669
  return {
676
- x: Math.min(Math.max(transform.x, minX), maxX),
677
- y: Math.min(Math.max(transform.y, minY), maxY)
670
+ x: Math.min(Math.max(transform.x, minTransformX), maxTransformX),
671
+ y: Math.min(Math.max(transform.y, minTransformY), maxTransformY)
678
672
  };
679
673
  }
680
674
  };
@@ -695,7 +689,7 @@ var _tmpl$$4 = [
695
689
  "\"",
696
690
  "></path>"
697
691
  ];
698
- var _tmpl$2$1 = [
692
+ var _tmpl$2$2 = [
699
693
  "<g",
700
694
  " cursor=\"pointer\" transform=\"",
701
695
  "\" style=\"",
@@ -710,8 +704,8 @@ var _tmpl$2$1 = [
710
704
  */
711
705
  var calculateOffset = (value) => value * 100 / 200;
712
706
  /**
713
- * Generates an SVG cubic bezier path `d` string between coordinates `(x0,
714
- * y0)` and `(x1, y1)`.
707
+ * Generates an SVG cubic bezier path `d` string between coordinates `(x0, y0)` and
708
+ * `(x1, y1)`.
715
709
  *
716
710
  * @param vector - Vector coordinates of type {@linkcode Vector}.
717
711
  *
@@ -721,8 +715,8 @@ var draw = ({ x0, y0, x1, y1 }) => {
721
715
  return `M ${x0} ${y0} C ${x0 + calculateOffset(Math.abs(x1 - x0))} ${y0}, ${x1 - calculateOffset(Math.abs(x1 - x0))} ${y1}, ${x1} ${y1}`;
722
716
  };
723
717
  /**
724
- * SVG edge component rendering bezier curves and delete interaction
725
- * handles for node connections.
718
+ * SVG edge component rendering bezier curves and delete interaction handles for node
719
+ * connections.
726
720
  *
727
721
  * @param props - Edge properties of type {@linkcode Props}.
728
722
  *
@@ -748,7 +742,7 @@ var EdgeComponent = (props) => {
748
742
  return selected();
749
743
  },
750
744
  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"));
745
+ return (0, solid_js_web.ssr)(_tmpl$2$2, (0, solid_js_web.ssrHydrationKey)(), `translate(${(0, solid_js_web.escape)(middlePoint().x, true)}, ${(0, solid_js_web.escape)(middlePoint().y, true)})`, (0, solid_js_web.ssrStyleProperty)("pointer-events:", "all") + (0, solid_js_web.ssrStyleProperty)(";z-index:", "30"));
752
746
  }
753
747
  })];
754
748
  };
@@ -761,8 +755,8 @@ var _tmpl$$3 = [
761
755
  "<!--/--></svg>"
762
756
  ];
763
757
  /**
764
- * SVG board overlay component that renders all active connecting edges and
765
- * ongoing edge creation previews.
758
+ * SVG board overlay component that renders all active connecting edges and ongoing
759
+ * edge creation previews.
766
760
  *
767
761
  * @returns The rendered SVG JSX element.
768
762
  */
@@ -812,13 +806,13 @@ var _tmpl$$2 = [
812
806
  " class=\"cursor-pointer overflow-visible rounded-full bg-green-500 p-0.5 text-center font-bold hover:bg-green-600\" style=\"",
813
807
  "\" 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
808
  ];
815
- var _tmpl$2 = [
809
+ var _tmpl$2$1 = [
816
810
  "<div",
817
811
  " id=\"outputs\" class=\"pointer-events-none absolute top-0 z-10 flex cursor-default flex-col\" style=\"",
818
812
  "\"><div class=\"cursor-default rounded-full bg-[#e38b29] shadow-md\" style=\"",
819
813
  "\"></div></div>"
820
814
  ];
821
- var _tmpl$3 = [
815
+ var _tmpl$3$1 = [
822
816
  "<div",
823
817
  " class=\"",
824
818
  "\" style=\"",
@@ -844,8 +838,8 @@ var _tmpl$5 = [
844
838
  "</div>"
845
839
  ];
846
840
  /**
847
- * Interactive flowchart node component supporting dragging, selection,
848
- * handle connections, and child/sibling creation.
841
+ * Interactive flowchart node component supporting dragging, selection, handle
842
+ * connections, and child/sibling creation.
849
843
  *
850
844
  * @param props - Node rendering properties of type {@linkcode Props}.
851
845
  *
@@ -855,17 +849,17 @@ var NodeComponent = (props) => {
855
849
  const [ref, setRef] = (0, solid_js.createSignal)();
856
850
  const { dimensions: [dimensions, setDimensions], newEdge: [newEdge, setNewEdge], getBoardPoint, service, zoom: [zoom] } = useFlow();
857
851
  const selected = (0, _bemedev_app_solidjs.useState)(service, { selector: (s) => s.context.selected === props.id });
858
- (0, solid_js.createEffect)(() => {
852
+ (0, solid_js.onMount)(() => {
859
853
  ref();
860
854
  zoom();
861
855
  });
862
856
  const hasParent = (0, _bemedev_app_solidjs.useState)(service, { selector: ({ context: { data } }) => {
863
857
  const edges = data?.edges;
864
858
  if (!edges) return false;
865
- return Object.values(edges).some((edge) => edge.to === props.id);
859
+ return edges.some(({ to }) => to === props.id);
866
860
  } });
867
861
  (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, {
862
+ return (0, solid_js_web.ssr)(_tmpl$3$1, (0, solid_js_web.ssrHydrationKey)() + (0, solid_js_web.ssrAttribute)("id", (0, solid_js_web.escape)(props.id, true), false), `flex flex-col absolute cursor-grab bg-white rounded-md shadow-md select-none transition-[border,box-shadow] duration-200 ease-in-out hover:shadow-lg draggable ${selected() ? "border border-[#e38c29] z-[100]" : ""} ${!selected() ? "border border-[#e6d4be] z-[1]" : ""} min-w-48`, (0, solid_js_web.ssrStyleProperty)("top:", `${(0, solid_js_web.escape)(props.y, true)}px`) + (0, solid_js_web.ssrStyleProperty)(";left:", `${(0, solid_js_web.escape)(props.x, true)}px`), `pointer-events-none absolute flex items-center justify-end -top-7.5 right-0 transition-all duration-200 ease-in-out space-x-2 ${selected() ? "w-full opacity-100" : ""} ${!selected() ? "w-0 -right-3 opacity-0 overflow-hidden" : ""}`, (0, solid_js_web.ssrStyleProperty)("overflow:", "visible") + (0, solid_js_web.ssrStyleProperty)(";pointer-events:", "all") + (0, solid_js_web.ssrStyleProperty)(";width:", `${(0, solid_js_web.escape)(12, true) * 2}px`) + (0, solid_js_web.ssrStyleProperty)(";height:", `${(0, solid_js_web.escape)(12, true) * 2}px`), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(solid_js.Show, {
869
863
  get when() {
870
864
  return hasParent();
871
865
  },
@@ -889,7 +883,7 @@ var NodeComponent = (props) => {
889
883
  return props.input || hasParent();
890
884
  },
891
885
  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"));
886
+ 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
887
  }
894
888
  })), (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
889
  };
@@ -897,22 +891,33 @@ var NodeComponent = (props) => {
897
891
  //#region src/ui/components/NodesBoard.tsx
898
892
  var _tmpl$$1 = [
899
893
  "<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
- "<!--/--><!--$-->",
894
+ " class=\"relative h-full w-full overflow-scroll rounded-lg border-2 border-gray-600\"><!--$-->",
902
895
  "<!--/--><div class=\"",
903
896
  "\" style=\"",
897
+ "\"><div class=\"relative h-full w-full\" style=\"",
904
898
  "\"><!--$-->",
905
899
  "<!--/--><!--$-->",
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>"
900
+ "<!--/--><!--$-->",
901
+ "<!--/--></div></div></div>"
902
+ ];
903
+ var _tmpl$2 = [
904
+ "<div",
905
+ " 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\"><!--$-->",
906
+ "<!--/-->%</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>"
907
+ ];
908
+ var _tmpl$3 = [
909
+ "<div",
910
+ " class=\"relative mx-auto h-[calc(100vh-64px)] w-[calc(100vw-32px)] overflow-hidden\">",
911
+ "</div>"
908
912
  ];
909
913
  /**
910
- * Interactive board component containing the drag-drop viewport, zoom
911
- * controls, panning gestures, and rendered nodes/edges.
914
+ * Interactive board component containing the drag-drop viewport, zoom controls,
915
+ * panning gestures, and rendered nodes/edges.
912
916
  *
913
917
  * @returns The rendered Solid component.
914
918
  */
915
919
  var NodesBoard = () => {
920
+ let containerRef;
916
921
  const [isPanning, setIsPanning] = (0, solid_js.createSignal)(false);
917
922
  const [transform, setTransform] = (0, solid_js.createSignal)({
918
923
  x: 0,
@@ -920,10 +925,15 @@ var NodesBoard = () => {
920
925
  });
921
926
  const [id, setId] = (0, solid_js.createSignal)("");
922
927
  const [previousZoom, setPreviousZoom] = (0, solid_js.createSignal)();
923
- let cleanupPanning = () => {};
924
- (0, solid_js.onCleanup)(cleanupPanning);
928
+ let percentX = 0;
929
+ let percentY = 0;
925
930
  const { board: [, setRef], service, newEdge: [newEdge], zoom: [zoom, setZoom] } = useFlow();
926
- (0, solid_js.createEffect)((0, solid_js.on)(zoom, () => {}, { defer: true }));
931
+ (0, solid_js.createEffect)((0, solid_js.on)(zoom, () => {
932
+ const maxScrollX = containerRef.scrollWidth - containerRef.clientWidth;
933
+ const maxScrollY = containerRef.scrollHeight - containerRef.clientHeight;
934
+ if (maxScrollX > 0) containerRef.scrollLeft = percentX * maxScrollX;
935
+ if (maxScrollY > 0) containerRef.scrollTop = percentY * maxScrollY;
936
+ }, { defer: true }));
927
937
  const selectedId = (0, _bemedev_app_solidjs.useState)(service, { selector: (s) => s.context?.selected });
928
938
  const selected = (id) => selectedId() === id;
929
939
  const nodes = (0, _bemedev_app_solidjs.useState)(service, {
@@ -939,21 +949,27 @@ var NodesBoard = () => {
939
949
  },
940
950
  equals: dequal.dequal
941
951
  });
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 }) => {
952
+ const CANVAS_SIZE = 500;
953
+ const MARGIN_X = 265;
954
+ const MARGIN_Y = 425;
955
+ const cWidth = () => `calc((${CANVAS_SIZE}vw - ${MARGIN_X}px) * ${zoom()})`;
956
+ const cHeight = () => `calc((${CANVAS_SIZE}vh - ${MARGIN_Y}px) * ${zoom()})`;
957
+ return (0, solid_js_web.ssr)(_tmpl$3, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(_thisbeyond_solid_dnd.DragDropProvider, {
958
+ onDragMove: ({ draggable: { transform: _transform, node, id } }) => {
949
959
  setId(id);
950
960
  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();
961
+ const grandParent = node.parentElement?.parentElement;
962
+ const currentZoom = zoom();
963
+ const minX = 0;
964
+ const maxX = grandParent ? Math.max(0, grandParent.clientWidth / currentZoom - node.offsetWidth) : Infinity;
965
+ const minY = 0;
966
+ const maxY = grandParent ? Math.max(0, grandParent.clientHeight / currentZoom - node.offsetHeight) : Infinity;
967
+ const targetX = node.offsetLeft + _transform.x / currentZoom;
968
+ const targetY = node.offsetTop + _transform.y / currentZoom;
969
+ const x = Math.min(Math.max(targetX, minX), maxX);
970
+ const y = Math.min(Math.max(targetY, minY), maxY);
971
+ const deltaX = x - node.offsetLeft;
972
+ const deltaY = y - node.offsetTop;
957
973
  node.style.setProperty("transform", `translate3d(${deltaX}px, ${deltaY}px, 0)`);
958
974
  service.send({
959
975
  type: "MOVE_IMMEDIATE",
@@ -963,13 +979,24 @@ var NodesBoard = () => {
963
979
  y
964
980
  }
965
981
  });
966
- setTransform({ ..._transform });
982
+ setTransform({
983
+ x: deltaX * currentZoom,
984
+ y: deltaY * currentZoom
985
+ });
967
986
  }
968
987
  },
969
988
  onDragEnd: ({ draggable: { node, id } }) => {
970
989
  if (!selected(id)) return;
971
- const X = node.offsetLeft + transform().x / zoom();
972
- const Y = node.offsetTop + 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 rawX = node.offsetLeft + transform().x / currentZoom;
997
+ const rawY = node.offsetTop + transform().y / currentZoom;
998
+ const X = Math.min(Math.max(rawX, minX), maxX);
999
+ const Y = Math.min(Math.max(rawY, minY), maxY);
973
1000
  node.style.setProperty("top", Y + "px");
974
1001
  node.style.setProperty("left", X + "px");
975
1002
  node.style.removeProperty("transform");
@@ -989,21 +1016,25 @@ var NodesBoard = () => {
989
1016
  }, 0);
990
1017
  },
991
1018
  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
- })];
1019
+ return [
1020
+ (0, solid_js_web.ssr)(_tmpl$$1, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(_thisbeyond_solid_dnd.DragDropSensors, {})), `relative cursor-crosshair overflow-hidden ${isPanning() ? "cursor-grabbing" : ""}`, (0, solid_js_web.ssrStyleProperty)("height:", (0, solid_js_web.escape)(cHeight(), true)) + (0, solid_js_web.ssrStyleProperty)(";width:", (0, solid_js_web.escape)(cWidth(), true)), (0, solid_js_web.ssrStyleProperty)("scale:", (0, solid_js_web.escape)(zoom(), true)) + (0, solid_js_web.ssrStyleProperty)(";transform-origin:", "top left"), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(DragBounds, {})), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(EdgesBoard, {})), (0, solid_js_web.escape)((0, solid_js_web.createComponent)(solid_js.For, {
1021
+ get each() {
1022
+ return nodes();
1023
+ },
1024
+ children: NodeComponent
1025
+ }))),
1026
+ (0, solid_js_web.ssr)(_tmpl$2, (0, solid_js_web.ssrHydrationKey)(), (0, solid_js_web.escape)(Math.round(zoom() * 100))),
1027
+ (0, solid_js_web.createComponent)(solid_js.Show, {
1028
+ get when() {
1029
+ return !id() || !selected(id());
1030
+ },
1031
+ get children() {
1032
+ return (0, solid_js_web.createComponent)(_thisbeyond_solid_dnd.DragOverlay, { children: "" });
1033
+ }
1034
+ })
1035
+ ];
1005
1036
  }
1006
- });
1037
+ })));
1007
1038
  };
1008
1039
  //#endregion
1009
1040
  //#region src/ui/components/FlowChart.tsx
@@ -1014,8 +1045,8 @@ var _tmpl$ = [
1014
1045
  "</div></div>"
1015
1046
  ];
1016
1047
  /**
1017
- * Flowchart board canvas component that renders interactive nodes, edges,
1018
- * pan/zoom, and toolbar controls.
1048
+ * Flowchart board canvas component that renders interactive nodes, edges, pan/zoom,
1049
+ * and toolbar controls.
1019
1050
  *
1020
1051
  * @param props - Flowchart configuration and event handlers of type
1021
1052
  * {@linkcode FlowProps}.
@@ -1042,8 +1073,8 @@ var FlowChart = (props) => {
1042
1073
  //#endregion
1043
1074
  //#region src/ui/Flow.tsx
1044
1075
  /**
1045
- * Root Flow component wrapping the {@linkcode FlowChart} inside the flow
1046
- * context provider.
1076
+ * Root Flow component wrapping the {@linkcode FlowChart} inside the flow context
1077
+ * provider.
1047
1078
  *
1048
1079
  * @param props - Flow chart configuration and callbacks of type
1049
1080
  * {@linkcode FlowProps}.