@bemedev/mind-flow 0.1.1 → 0.3.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 (51) hide show
  1. package/README.md +10 -10
  2. package/lib/helpers/createContext.d.ts +7 -2
  3. package/lib/helpers/createContext.d.ts.map +1 -1
  4. package/lib/index.cjs.js +1 -1
  5. package/lib/index.es.js +520 -493
  6. package/lib/output.css +1 -1
  7. package/lib/server.cjs.js +621 -413
  8. package/lib/server.es.js +625 -417
  9. package/lib/services/main.machine.d.ts +4857 -218
  10. package/lib/services/main.machine.d.ts.map +1 -1
  11. package/lib/{ui/components/FlowChart.data.d.ts → services/main.machine.data.d.ts} +4 -2
  12. package/lib/services/main.machine.data.d.ts.map +1 -0
  13. package/lib/services/main.machine.helpers.d.ts +32 -0
  14. package/lib/services/main.machine.helpers.d.ts.map +1 -0
  15. package/lib/services/main.machine.typings.d.ts +126 -0
  16. package/lib/services/main.machine.typings.d.ts.map +1 -0
  17. package/lib/ui/Flow.d.ts +2 -0
  18. package/lib/ui/Flow.d.ts.map +1 -1
  19. package/lib/ui/components/Bounds.d.ts +2 -0
  20. package/lib/ui/components/Bounds.d.ts.map +1 -1
  21. package/lib/ui/components/EdgeComponent.d.ts +3 -2
  22. package/lib/ui/components/EdgeComponent.d.ts.map +1 -1
  23. package/lib/ui/components/EdgesBoard.d.ts +2 -0
  24. package/lib/ui/components/EdgesBoard.d.ts.map +1 -1
  25. package/lib/ui/components/FlowChart.context.d.ts +5608 -996
  26. package/lib/ui/components/FlowChart.context.d.ts.map +1 -1
  27. package/lib/ui/components/FlowChart.d.ts +3 -1
  28. package/lib/ui/components/FlowChart.d.ts.map +1 -1
  29. package/lib/ui/components/NodeComponent.d.ts +2 -10
  30. package/lib/ui/components/NodeComponent.d.ts.map +1 -1
  31. package/lib/ui/components/NodesBoard.d.ts +2 -0
  32. package/lib/ui/components/NodesBoard.d.ts.map +1 -1
  33. package/package.json +10 -10
  34. package/src/helpers/createContext.ts +7 -2
  35. package/src/{ui/components/FlowChart.data.ts → services/main.machine.data.ts} +3 -1
  36. package/src/services/main.machine.helpers.ts +55 -0
  37. package/src/services/main.machine.ts +288 -70
  38. package/src/services/main.machine.typings.ts +98 -0
  39. package/src/ui/Flow.tsx +2 -0
  40. package/src/ui/components/Bounds.tsx +13 -8
  41. package/src/ui/components/EdgeComponent.tsx +30 -18
  42. package/src/ui/components/EdgesBoard.tsx +13 -36
  43. package/src/ui/components/FlowChart.context.ts +204 -310
  44. package/src/ui/components/FlowChart.tsx +16 -16
  45. package/src/ui/components/NodeComponent.tsx +59 -104
  46. package/src/ui/components/NodesBoard.tsx +74 -47
  47. package/src/ui/components/classes.ts +1 -1
  48. package/lib/services/main.typings.d.ts +0 -68
  49. package/lib/services/main.typings.d.ts.map +0 -1
  50. package/lib/ui/components/FlowChart.data.d.ts.map +0 -1
  51. package/src/services/main.typings.ts +0 -50
@@ -1,17 +1,17 @@
1
1
  /* eslint-disable @typescript-eslint/no-namespace */
2
- import { useState } from '@bemedev/app-solidjs';
2
+ import { toArray } from '@bemedev/app';
3
+ import { createState } from '@bemedev/app-solidjs';
3
4
  import { createDraggable } from '@thisbeyond/solid-dnd';
4
- import { Component, createSignal, onMount, Show } from 'solid-js';
5
- import { produce } from 'solid-js/store';
5
+ import { dequal } from 'dequal';
6
+ import { Component, Show } from 'solid-js';
6
7
 
7
- import { useFlow } from './FlowChart.context';
8
8
  import {
9
- DEFAULT_INPUT_OFFSET_X,
10
- DEFAULT_INPUT_OFFSET_Y,
11
9
  HANDLE_CONTAINER_OFFSET_X,
12
10
  HANDLE_MARGIN_TOP,
13
11
  HANDLE_SIZE,
14
- } from './FlowChart.data';
12
+ } from '#services/main.machine.data';
13
+
14
+ import { useFlow } from './FlowChart.context';
15
15
 
16
16
  declare module 'solid-js' {
17
17
  namespace JSX {
@@ -25,16 +25,6 @@ declare module 'solid-js' {
25
25
  type Props = {
26
26
  /** Unique identifier of the node. */
27
27
  id: string;
28
- /** Horizontal position of the node in board coordinates. */
29
- x: number;
30
- /** Vertical position of the node in board coordinates. */
31
- y: number;
32
- /** Optional header label for the node. */
33
- label?: string;
34
- /** Body content text of the node. */
35
- content: string;
36
- /** Whether the node has an input handle. */
37
- input: boolean;
38
28
  };
39
29
 
40
30
  /**
@@ -44,69 +34,34 @@ type Props = {
44
34
  * @param props - Node rendering properties of type {@linkcode Props}.
45
35
  *
46
36
  * @returns The rendered Solid component.
37
+ *
38
+ * @see {@linkcode useFlow}, {@linkcode HANDLE_CONTAINER_OFFSET_X}, {@linkcode HANDLE_MARGIN_TOP}, {@linkcode HANDLE_SIZE}
47
39
  */
48
40
  export const NodeComponent: Component<Props> = props => {
49
- let inputRef: HTMLDivElement | undefined;
50
- let outputRef: HTMLDivElement | undefined;
51
- const [ref, setRef] = createSignal<HTMLDivElement>();
52
- const {
53
- dimensions: [dimensions, setDimensions],
54
- newEdge: [newEdge, setNewEdge],
55
- getBoardPoint,
56
- service,
57
- zoom: [zoom],
58
- } = useFlow();
59
-
60
- const selected = useState(service, {
61
- selector: s => s.context.selected === props.id,
41
+ const service = useFlow();
42
+
43
+ const node = createState(service, {
44
+ selector: ({ context }) => {
45
+ const list = toArray.typed(context.data?.nodes);
46
+ const item = list.find(n => n.id === props.id)!;
47
+ return {
48
+ x: item.position.x,
49
+ y: item.position.y,
50
+ label: item.data.label,
51
+ content: item.data.content ?? '',
52
+ input: item.input,
53
+ };
54
+ },
55
+ equals: dequal,
62
56
  });
63
57
 
64
- onMount(() => {
65
- const _inputRef = inputRef;
66
- const _outputRef = outputRef;
67
- const _rootRef = ref();
68
- const currentZoom = zoom();
69
-
70
- if (!_outputRef || !_rootRef) return;
71
-
72
- const rootRect = _rootRef.getBoundingClientRect();
73
- const outputRect = _outputRef.getBoundingClientRect();
74
- const inputRect = _inputRef?.getBoundingClientRect();
75
-
76
- const outputOffsetX =
77
- (outputRect.left - rootRect.left + outputRect.width / 2) / currentZoom;
78
- const outputOffsetY =
79
- (outputRect.top - rootRect.top + outputRect.height / 2) / currentZoom;
80
-
81
- const inputOffsetX = inputRect
82
- ? (inputRect.left - rootRect.left + inputRect.width / 2) / currentZoom
83
- : DEFAULT_INPUT_OFFSET_X;
84
- const inputOffsetY = inputRect
85
- ? (inputRect.top - rootRect.top + inputRect.height / 2) / currentZoom
86
- : DEFAULT_INPUT_OFFSET_Y;
87
-
88
- const width = rootRect.width / currentZoom;
89
- const height = rootRect.height / currentZoom;
90
-
91
- const output = { x: props.x + outputOffsetX, y: props.y + outputOffsetY };
58
+ const newEdge = createState(service, { selector: s => s.context.newEdge });
92
59
 
93
- const input = { x: props.x + inputOffsetX, y: props.y + inputOffsetY };
94
-
95
- setDimensions(
96
- produce(data => {
97
- data[props.id] = {
98
- width,
99
- height,
100
- output,
101
- input,
102
- outputOffset: { x: outputOffsetX, y: outputOffsetY },
103
- inputOffset: { x: inputOffsetX, y: inputOffsetY },
104
- };
105
- }),
106
- );
60
+ const selected = createState(service, {
61
+ selector: s => s.context.selected === props.id,
107
62
  });
108
63
 
109
- const hasParent = useState(service, {
64
+ const hasParent = createState(service, {
110
65
  selector: ({ context: { data } }) => {
111
66
  const edges = data?.edges;
112
67
  if (!edges) return false;
@@ -119,22 +74,21 @@ export const NodeComponent: Component<Props> = props => {
119
74
 
120
75
  return (
121
76
  <div
122
- ref={setRef}
123
- id={props.id}
124
77
  classList={{
125
78
  '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': true,
126
79
  'border border-[#e38c29] z-[100]': selected(),
127
80
  'border border-[#e6d4be] z-[1]': !selected(),
128
81
  }}
129
- style={{ top: `${props.y}px`, left: `${props.x}px` }}
82
+
83
+ style={{ top: `${node().y}px`, left: `${node().x}px` }}
84
+ class='min-w-48'
85
+ use:draggable={{ skipTransform: true }}
86
+ id={props.id}
87
+
130
88
  onMouseDown={e => {
131
89
  e.stopPropagation();
132
- e.stopImmediatePropagation();
133
90
  service.send({ type: 'SELECT', payload: props.id });
134
91
  }}
135
- class='min-w-48'
136
-
137
- use:draggable={{ skipTransform: true }}
138
92
  >
139
93
  <div
140
94
  classList={{
@@ -145,13 +99,15 @@ export const NodeComponent: Component<Props> = props => {
145
99
  >
146
100
  <svg
147
101
  class='cursor-pointer rounded-full fill-[#a11111] opacity-100 transition-all duration-200 ease-in-out'
102
+ fill='currentColor'
103
+ stroke-width='2'
104
+ viewBox='4 4 16 16'
105
+
148
106
  onClick={e => {
149
107
  e.stopPropagation();
150
108
  service.send({ type: 'DELETE', payload: props.id });
151
109
  }}
152
- fill='currentColor'
153
- stroke-width='2'
154
- viewBox='4 4 16 16'
110
+
155
111
  style={{
156
112
  overflow: 'visible',
157
113
  'pointer-events': 'all',
@@ -161,6 +117,7 @@ export const NodeComponent: Component<Props> = props => {
161
117
  >
162
118
  <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' />
163
119
  </svg>
120
+
164
121
  <Show when={hasParent()}>
165
122
  <svg
166
123
  class='cursor-pointer overflow-visible rounded-full bg-green-500 p-0.5 text-center font-bold hover:bg-green-600'
@@ -173,6 +130,7 @@ export const NodeComponent: Component<Props> = props => {
173
130
  width: `${HANDLE_SIZE * 2}px`,
174
131
  height: `${HANDLE_SIZE * 2}px`,
175
132
  }}
133
+
176
134
  viewBox='0 0 1024 1024'
177
135
  preserveAspectRatio='xMaxYMax'
178
136
  xmlns='http://www.w3.org/2000/svg'
@@ -181,6 +139,7 @@ export const NodeComponent: Component<Props> = props => {
181
139
  >
182
140
  <g id='background'>
183
141
  <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' />
142
+
184
143
  <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' />
185
144
  </g>
186
145
  </svg>
@@ -188,55 +147,59 @@ export const NodeComponent: Component<Props> = props => {
188
147
  <svg
189
148
  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'
190
149
  onClick={() => service.send({ type: 'ADD_CHILD', payload: props.id })}
150
+ viewBox='0 0 24 24'
151
+ stroke='currentColor'
152
+ stroke-width='2'
153
+
191
154
  style={{
192
155
  'pointer-events': 'all',
193
156
  width: `${HANDLE_SIZE * 2}px`,
194
157
  height: `${HANDLE_SIZE * 2}px`,
195
158
  }}
196
- viewBox='0 0 24 24'
197
- stroke='currentColor'
198
- stroke-width='2'
199
159
  >
200
160
  <path d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z' />
201
161
  </svg>
202
162
  </div>
203
- <Show when={props.label} keyed>
163
+
164
+ <Show when={node().label} keyed>
204
165
  {label => (
205
166
  <span class='min-w-max border-b border-[#f0f0f0] p-3 whitespace-nowrap text-red-600 select-none'>
206
167
  {label}
207
168
  </span>
208
169
  )}
209
170
  </Show>
210
- <Show when={props.content} keyed>
171
+
172
+ <Show when={node().content} keyed>
211
173
  {content => <div class='p-3 select-none'>{content}</div>}
212
174
  </Show>
213
- <Show when={props.input || hasParent()}>
175
+
176
+ <Show when={node().input || hasParent()}>
214
177
  <div
215
178
  id='outputs'
216
179
  class='pointer-events-none absolute top-0 z-10 flex cursor-default flex-col'
217
180
  style={{ left: `-${HANDLE_CONTAINER_OFFSET_X}px` }}
218
181
  >
219
182
  <div
220
- ref={el => (inputRef = el)}
221
183
  class='cursor-default rounded-full bg-[#e38b29] shadow-md'
184
+
222
185
  style={{
223
186
  width: `${HANDLE_SIZE}px`,
224
187
  height: `${HANDLE_SIZE}px`,
225
188
  'margin-top': `${HANDLE_MARGIN_TOP}px`,
226
189
  'pointer-events': 'all',
227
190
  }}
191
+
228
192
  onMouseDown={event => {
229
193
  event.stopPropagation();
230
194
  }}
195
+
231
196
  onMouseUp={event => {
232
197
  event.stopPropagation();
233
198
  const from = newEdge()?.from;
234
199
 
235
200
  if (from) {
236
201
  service.send({ type: 'ADD_EDGE', payload: { from, to: props.id } });
237
- }
238
-
239
- setNewEdge();
202
+ } else service.send('CLEAR_NEW_EDGE');
240
203
  }}
241
204
  ></div>
242
205
  </div>
@@ -247,27 +210,19 @@ export const NodeComponent: Component<Props> = props => {
247
210
  style={{ right: `-${HANDLE_CONTAINER_OFFSET_X}px` }}
248
211
  >
249
212
  <div
250
- ref={el => (outputRef = el)}
251
213
  class='cursor-crosshair rounded-full bg-[#e38b29] shadow-md'
214
+
252
215
  style={{
253
216
  width: `${HANDLE_SIZE}px`,
254
217
  height: `${HANDLE_SIZE}px`,
255
218
  'margin-top': `${HANDLE_MARGIN_TOP}px`,
256
219
  'pointer-events': 'all',
257
220
  }}
221
+
258
222
  onMouseDown={event => {
259
223
  event.stopPropagation();
260
224
  service.send('DESELECT');
261
- const output = dimensions()[props.id]?.output;
262
- const boardPoint = getBoardPoint(event.clientX, event.clientY);
263
- if (output)
264
- setNewEdge({
265
- x0: output.x,
266
- y0: output.y,
267
- x1: boardPoint.x,
268
- y1: boardPoint.y,
269
- from: props.id,
270
- });
225
+ service.send({ type: 'START_NEW_EDGE', payload: props.id });
271
226
  }}
272
227
  ></div>
273
228
  </div>
@@ -1,16 +1,25 @@
1
- import { useState } from '@bemedev/app-solidjs';
1
+ import { toArray } from '@bemedev/app';
2
+ import { createState } from '@bemedev/app-solidjs';
2
3
  import {
3
4
  DragDropProvider,
4
5
  DragDropSensors,
5
6
  DragOverlay,
6
7
  } from '@thisbeyond/solid-dnd';
7
8
  import { dequal } from 'dequal';
8
- import { Component, createEffect, createSignal, For, on, Show } from 'solid-js';
9
+ import {
10
+ Component,
11
+ createEffect,
12
+ createSignal,
13
+ For,
14
+ on,
15
+ onMount,
16
+ Show,
17
+ } from 'solid-js';
9
18
 
19
+ import { CANVAS_FACTOR, SCROLL_MULTIPLIER } from '../../services/main.machine.data';
10
20
  import { DragBounds } from './Bounds';
11
21
  import { EdgesBoard } from './EdgesBoard';
12
22
  import { useFlow } from './FlowChart.context';
13
- import { CANVAS_FACTOR, SCROLL_MULTIPLIER } from './FlowChart.data';
14
23
  import { NodeComponent } from './NodeComponent';
15
24
 
16
25
  /**
@@ -18,22 +27,56 @@ import { NodeComponent } from './NodeComponent';
18
27
  * panning gestures, and rendered nodes/edges.
19
28
  *
20
29
  * @returns The rendered Solid component.
30
+ *
31
+ * @see {@linkcode DragBounds}, {@linkcode EdgesBoard}, {@linkcode NodeComponent}, {@linkcode useFlow}, {@linkcode CANVAS_FACTOR}, {@linkcode SCROLL_MULTIPLIER}
21
32
  */
22
33
  export const NodesBoard: Component = () => {
23
34
  let containerRef: HTMLDivElement;
24
35
  const [isPanning, setIsPanning] = createSignal(false);
25
36
  const [transform, setTransform] = createSignal({ x: 0, y: 0 });
26
37
  const [id, setId] = createSignal<string | number>('');
27
- const [previousZoom, setPreviousZoom] = createSignal<number>();
38
+ const [ref, setRef] = createSignal<HTMLDivElement | undefined>();
28
39
  let percentX = 0;
29
40
  let percentY = 0;
30
41
 
31
- const {
32
- board: [, setRef],
33
- service,
34
- newEdge: [newEdge],
35
- zoom: [zoom, setZoom],
36
- } = useFlow();
42
+ const service = useFlow();
43
+
44
+ const newEdge = createState(service, {
45
+ selector: s => s.context.newEdge,
46
+ equals: dequal,
47
+ });
48
+
49
+ const zoom = createState(service, { selector: s => s.context.zoom ?? 1 });
50
+
51
+ /**
52
+ * Dispatches the current board geometry and parent container dimensions to the
53
+ * state machine service.
54
+ */
55
+ const sendBoard = () => {
56
+ const el = ref();
57
+ if (!el) return;
58
+ const rect = el.getBoundingClientRect();
59
+ const parent = () => ref()?.parentElement;
60
+
61
+ const payload = {
62
+ self: {
63
+ left: rect.left,
64
+ top: rect.top,
65
+ width: el.clientWidth,
66
+ height: el.clientHeight,
67
+ },
68
+ parent: parent()
69
+ ? {
70
+ scrollLeft: parent()!.scrollLeft,
71
+ scrollTop: parent()!.scrollTop,
72
+ height: parent()!.clientHeight,
73
+ width: parent()!.clientWidth,
74
+ }
75
+ : undefined,
76
+ };
77
+
78
+ service.send({ type: 'SET_BOARD', payload });
79
+ };
37
80
 
38
81
  /**
39
82
  * Calculates and saves the normalized scroll percentages across X and Y axes for
@@ -44,6 +87,7 @@ export const NodesBoard: Component = () => {
44
87
  const maxScrollY = containerRef.scrollHeight - containerRef.clientHeight;
45
88
  percentX = maxScrollX > 0 ? containerRef.scrollLeft / maxScrollX : 0;
46
89
  percentY = maxScrollY > 0 ? containerRef.scrollTop / maxScrollY : 0;
90
+ sendBoard();
47
91
  };
48
92
 
49
93
  createEffect(
@@ -59,7 +103,9 @@ export const NodesBoard: Component = () => {
59
103
  ),
60
104
  );
61
105
 
62
- const selectedId = useState(service, { selector: s => s.context?.selected });
106
+ onMount(sendBoard);
107
+
108
+ const selectedId = createState(service, { selector: s => s.context?.selected });
63
109
 
64
110
  /**
65
111
  * Determines whether the given element ID matches the currently selected entity.
@@ -70,25 +116,22 @@ export const NodesBoard: Component = () => {
70
116
  */
71
117
  const selected = (id: string | number) => selectedId() === id;
72
118
 
73
- const nodes = useState(service, {
74
- selector: s => {
75
- const list = s.context.data?.nodes ?? [];
76
- return list.map(item => ({
77
- id: item.id,
78
- x: item.position.x,
79
- y: item.position.y,
80
- label: item.data.label,
81
- content: item.data.content ?? '',
82
- input: item.input,
83
- }));
119
+ const nodeIds = createState(service, {
120
+ selector: ({ context }) => {
121
+ const list = toArray.typed(context.data?.nodes);
122
+ return list.map(item => item.id);
84
123
  },
85
- equals: dequal,
124
+ equals: (prev, next) => prev.length === next.length,
86
125
  });
87
126
 
88
127
  const CANVAS_SIZE = CANVAS_FACTOR * 100;
89
128
  const MARGIN_X = 53 * CANVAS_FACTOR;
90
129
  const MARGIN_Y = 85 * CANVAS_FACTOR;
130
+
131
+ /** Computes the dynamic canvas width string in CSS units adjusted for zoom. */
91
132
  const cWidth = () => `calc((${CANVAS_SIZE}vw - ${MARGIN_X}px) * ${zoom()})`;
133
+
134
+ /** Computes the dynamic canvas height string in CSS units adjusted for zoom. */
92
135
  const cHeight = () => `calc((${CANVAS_SIZE}vh - ${MARGIN_Y}px) * ${zoom()})`;
93
136
 
94
137
  return (
@@ -98,9 +141,7 @@ export const NodesBoard: Component = () => {
98
141
  e.preventDefault();
99
142
  updateScrollPercentages();
100
143
  const delta = e.deltaY < 0 ? 0.1 : -0.1;
101
- setZoom(prev =>
102
- Math.min(Math.max(Number((prev + delta).toFixed(2)), 0.2), 3),
103
- );
144
+ service.send({ type: 'ZOOM', payload: delta });
104
145
  }
105
146
  }}
106
147
 
@@ -167,8 +208,8 @@ export const NodesBoard: Component = () => {
167
208
  node.style.setProperty('left', X + 'px');
168
209
  node.style.removeProperty('transform');
169
210
 
211
+ service.send({ type: 'MOVE', payload: { id: `${id}`, x: X, y: Y } });
170
212
  setTimeout(() => {
171
- service.send({ type: 'MOVE', payload: { id: `${id}`, x: X, y: Y } });
172
213
  setTransform({ x: 0, y: 0 });
173
214
  }, 0);
174
215
  }}
@@ -178,7 +219,7 @@ export const NodesBoard: Component = () => {
178
219
  return (containerRef = el);
179
220
  }}
180
221
  onScroll={updateScrollPercentages}
181
- class='relative h-full w-full overflow-scroll rounded-lg border-2 border-gray-600'
222
+ class='relative h-full w-full overflow-auto rounded-lg border-2 border-gray-600'
182
223
  >
183
224
  <DragDropSensors />
184
225
  <div
@@ -186,6 +227,7 @@ export const NodesBoard: Component = () => {
186
227
  class='relative cursor-crosshair overflow-hidden'
187
228
  classList={{ 'cursor-grabbing': isPanning() }}
188
229
  style={{ height: cHeight(), width: cWidth() }}
230
+ onScroll={() => {}}
189
231
 
190
232
  onMouseDown={e => {
191
233
  if (newEdge() || e.button !== 0) return;
@@ -226,7 +268,7 @@ export const NodesBoard: Component = () => {
226
268
  >
227
269
  <DragBounds />
228
270
  <EdgesBoard />
229
- <For each={nodes()} children={NodeComponent} />
271
+ <For each={nodeIds()}>{id => <NodeComponent id={id} />}</For>
230
272
  </div>
231
273
  </div>
232
274
  </div>
@@ -238,10 +280,7 @@ export const NodesBoard: Component = () => {
238
280
  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'
239
281
  onClick={() => {
240
282
  updateScrollPercentages();
241
- setPreviousZoom(undefined);
242
- setZoom(prev =>
243
- Math.max(1 / CANVAS_FACTOR, Number((prev - 0.1).toFixed(2))),
244
- );
283
+ service.send({ type: 'ZOOM', payload: -0.1 });
245
284
  }}
246
285
  title='Zoom out'
247
286
  aria-label='Zoom out'
@@ -253,13 +292,7 @@ export const NodesBoard: Component = () => {
253
292
  class='h-9 cursor-pointer rounded-lg px-2 text-xs font-semibold text-gray-700 transition-colors hover:bg-gray-100'
254
293
  onClick={() => {
255
294
  updateScrollPercentages();
256
- if (previousZoom()) {
257
- setZoom(previousZoom()!);
258
- setPreviousZoom(undefined);
259
- } else {
260
- setPreviousZoom(zoom());
261
- setZoom(1);
262
- }
295
+ service.send('TOGGLE_ZOOM');
263
296
  }}
264
297
  title='Reset zoom'
265
298
  aria-label='Reset zoom'
@@ -271,13 +304,7 @@ export const NodesBoard: Component = () => {
271
304
  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'
272
305
  onClick={() => {
273
306
  updateScrollPercentages();
274
- setPreviousZoom(undefined);
275
- setZoom(prev =>
276
- Math.min(
277
- Math.min(3, CANVAS_FACTOR),
278
- Number((prev + 0.1).toFixed(2)),
279
- ),
280
- );
307
+ service.send({ type: 'ZOOM', payload: 0.1 });
281
308
  }}
282
309
  title='Zoom in'
283
310
  aria-label='Zoom in'
@@ -23,7 +23,7 @@ export const CLASSES = [
23
23
  'w-full',
24
24
  'h-full',
25
25
  'overflow-hidden',
26
- 'overflow-scroll',
26
+ 'overflow-auto',
27
27
  'h-[150vh]',
28
28
  'w-[2160px]',
29
29
  'bg-size-[30px_30px]',
@@ -1,68 +0,0 @@
1
- import { inferT } from '@bemedev/app/typings';
2
- /** Schema definition for 2D coordinates `(x, y)`. */
3
- export declare const point: import('@bemedev/typings').inferSh<{
4
- x: "number";
5
- y: "number";
6
- }>;
7
- /** 2D coordinate point type inferred from schema {@linkcode point}. */
8
- export type Point = inferT<typeof point>;
9
- /** Schema definition for node handle offsets (input and output). */
10
- export declare const nodeOffset: import('@bemedev/typings').inferSh<{
11
- input: import('@bemedev/typings').Optional<{
12
- x: "number";
13
- y: "number";
14
- }>;
15
- output: {
16
- x: "number";
17
- y: "number";
18
- };
19
- }>;
20
- /** Schema definition for edge extremities. */
21
- export declare const extremities: import('@bemedev/typings').inferSh<{
22
- from: "string";
23
- to: "string";
24
- }>;
25
- /** Schema definition for a serialized flowchart node entity. */
26
- export declare const nodeJSON: import('@bemedev/typings').inferSh<{
27
- position: {
28
- x: "number";
29
- y: "number";
30
- };
31
- data: {
32
- label: import('@bemedev/typings').Optional<"string">;
33
- content: "string";
34
- };
35
- input: "boolean";
36
- }>;
37
- /** Schema definition for a serialized flowchart edge entity. */
38
- export declare const edgeJSON: import('@bemedev/typings').inferSh<{
39
- from: "string";
40
- to: "string";
41
- }>;
42
- /** Schema definition for layout dimensions and connection points of a node. */
43
- export declare const dimensions: import('@bemedev/typings').inferSh<{
44
- width: "number";
45
- height: "number";
46
- id: "string";
47
- output: {
48
- x: "number";
49
- y: "number";
50
- };
51
- input: import('@bemedev/typings').Optional<{
52
- x: "number";
53
- y: "number";
54
- }>;
55
- }>;
56
- /**
57
- * Schema definition for a 2D line vector representing edge coordinates `(x0, y0)` to
58
- * `(x1, y1)`.
59
- */
60
- export declare const vector: import('@bemedev/typings').inferSh<{
61
- x0: "number";
62
- y0: "number";
63
- x1: "number";
64
- y1: "number";
65
- }>;
66
- /** 2D vector coordinate type inferred from schema {@linkcode vector}. */
67
- export type Vector = inferT<typeof vector>;
68
- //# sourceMappingURL=main.typings.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"main.typings.d.ts","sourceRoot":"","sources":["../../src/services/main.typings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD,qDAAqD;AACrD,eAAO,MAAM,KAAK;;;EAAqC,CAAC;AAExD,uEAAuE;AACvE,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC;AAEzC,oEAAoE;AACpE,eAAO,MAAM,UAAU;;;;;;;;;EAGpB,CAAC;AAEJ,8CAA8C;AAC9C,eAAO,MAAM,WAAW;;;EAAyC,CAAC;AAElE,gEAAgE;AAChE,eAAO,MAAM,QAAQ;;;;;;;;;;EAIlB,CAAC;AAEJ,gEAAgE;AAChE,eAAO,MAAM,QAAQ;;;EAAc,CAAC;AAEpC,+EAA+E;AAC/E,eAAO,MAAM,UAAU;;;;;;;;;;;;EAMpB,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,MAAM;;;;;EAKjB,CAAC;AAEH,yEAAyE;AACzE,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"FlowChart.data.d.ts","sourceRoot":"","sources":["../../../src/ui/components/FlowChart.data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAG1C,mDAAmD;AACnD,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,sDAAsD;AACtD,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,oDAAoD;AACpD,eAAO,MAAM,aAAa,QAAkB,CAAC;AAE7C,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,kEAAkE;AAClE,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,sDAAsD;AACtD,eAAO,MAAM,eAAe,QAAoC,CAAC;AAEjE,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,QAC4B,CAAC;AAEhE,+DAA+D;AAC/D,eAAO,MAAM,sBAAsB,QAA0B,CAAC;AAE9D,+DAA+D;AAC/D,eAAO,MAAM,sBAAsB,QAAkB,CAAC;AAEtD,kEAAkE;AAClE,eAAO,MAAM,oBAAoB;;;CAGhC,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,oBAAoB;;;CAAgC,CAAC;AAElE;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GAAI,cAAS;;;CAG9C,CAAC;AAEH,qDAAqD;AACrD,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,yDAAyD;AACzD,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;CAAmC,CAAC;AAEnE,0EAA0E;AAC1E,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAGnC;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,CAAC,SAAS,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,EAOvD,CAAC"}
@@ -1,50 +0,0 @@
1
- import { type } from '@bemedev/app/bemedev';
2
- import type { inferT } from '@bemedev/app/typings';
3
-
4
- /** Schema definition for 2D coordinates `(x, y)`. */
5
- export const point = type({ x: 'number', y: 'number' });
6
-
7
- /** 2D coordinate point type inferred from schema {@linkcode point}. */
8
- export type Point = inferT<typeof point>;
9
-
10
- /** Schema definition for node handle offsets (input and output). */
11
- export const nodeOffset = type(({ optional, use }) => ({
12
- input: optional(use(point)),
13
- output: use(point),
14
- }));
15
-
16
- /** Schema definition for edge extremities. */
17
- export const extremities = type({ from: 'string', to: 'string' });
18
-
19
- /** Schema definition for a serialized flowchart node entity. */
20
- export const nodeJSON = type(({ optional, use }) => ({
21
- position: use(point),
22
- data: { label: optional('string'), content: 'string' },
23
- input: 'boolean',
24
- }));
25
-
26
- /** Schema definition for a serialized flowchart edge entity. */
27
- export const edgeJSON = extremities;
28
-
29
- /** Schema definition for layout dimensions and connection points of a node. */
30
- export const dimensions = type(({ optional, use }) => ({
31
- width: 'number',
32
- height: 'number',
33
- id: 'string',
34
- output: use(point),
35
- input: optional(use(point)),
36
- }));
37
-
38
- /**
39
- * Schema definition for a 2D line vector representing edge coordinates `(x0, y0)` to
40
- * `(x1, y1)`.
41
- */
42
- export const vector = type({
43
- x0: 'number',
44
- y0: 'number',
45
- x1: 'number',
46
- y1: 'number',
47
- });
48
-
49
- /** 2D vector coordinate type inferred from schema {@linkcode vector}. */
50
- export type Vector = inferT<typeof vector>;