@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.
- package/README.md +10 -10
- package/lib/helpers/createContext.d.ts +7 -2
- package/lib/helpers/createContext.d.ts.map +1 -1
- package/lib/index.cjs.js +1 -1
- package/lib/index.es.js +520 -493
- package/lib/output.css +1 -1
- package/lib/server.cjs.js +621 -413
- package/lib/server.es.js +625 -417
- package/lib/services/main.machine.d.ts +4857 -218
- package/lib/services/main.machine.d.ts.map +1 -1
- package/lib/{ui/components/FlowChart.data.d.ts → services/main.machine.data.d.ts} +4 -2
- package/lib/services/main.machine.data.d.ts.map +1 -0
- package/lib/services/main.machine.helpers.d.ts +32 -0
- package/lib/services/main.machine.helpers.d.ts.map +1 -0
- package/lib/services/main.machine.typings.d.ts +126 -0
- package/lib/services/main.machine.typings.d.ts.map +1 -0
- package/lib/ui/Flow.d.ts +2 -0
- package/lib/ui/Flow.d.ts.map +1 -1
- package/lib/ui/components/Bounds.d.ts +2 -0
- package/lib/ui/components/Bounds.d.ts.map +1 -1
- package/lib/ui/components/EdgeComponent.d.ts +3 -2
- package/lib/ui/components/EdgeComponent.d.ts.map +1 -1
- package/lib/ui/components/EdgesBoard.d.ts +2 -0
- package/lib/ui/components/EdgesBoard.d.ts.map +1 -1
- package/lib/ui/components/FlowChart.context.d.ts +5608 -996
- package/lib/ui/components/FlowChart.context.d.ts.map +1 -1
- package/lib/ui/components/FlowChart.d.ts +3 -1
- package/lib/ui/components/FlowChart.d.ts.map +1 -1
- package/lib/ui/components/NodeComponent.d.ts +2 -10
- package/lib/ui/components/NodeComponent.d.ts.map +1 -1
- package/lib/ui/components/NodesBoard.d.ts +2 -0
- package/lib/ui/components/NodesBoard.d.ts.map +1 -1
- package/package.json +10 -10
- package/src/helpers/createContext.ts +7 -2
- package/src/{ui/components/FlowChart.data.ts → services/main.machine.data.ts} +3 -1
- package/src/services/main.machine.helpers.ts +55 -0
- package/src/services/main.machine.ts +288 -70
- package/src/services/main.machine.typings.ts +98 -0
- package/src/ui/Flow.tsx +2 -0
- package/src/ui/components/Bounds.tsx +13 -8
- package/src/ui/components/EdgeComponent.tsx +30 -18
- package/src/ui/components/EdgesBoard.tsx +13 -36
- package/src/ui/components/FlowChart.context.ts +204 -310
- package/src/ui/components/FlowChart.tsx +16 -16
- package/src/ui/components/NodeComponent.tsx +59 -104
- package/src/ui/components/NodesBoard.tsx +74 -47
- package/src/ui/components/classes.ts +1 -1
- package/lib/services/main.typings.d.ts +0 -68
- package/lib/services/main.typings.d.ts.map +0 -1
- package/lib/ui/components/FlowChart.data.d.ts.map +0 -1
- package/src/services/main.typings.ts +0 -50
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-namespace */
|
|
2
|
-
import {
|
|
2
|
+
import { toArray } from '@bemedev/app';
|
|
3
|
+
import { createState } from '@bemedev/app-solidjs';
|
|
3
4
|
import { createDraggable } from '@thisbeyond/solid-dnd';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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 '
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
171
|
+
|
|
172
|
+
<Show when={node().content} keyed>
|
|
211
173
|
{content => <div class='p-3 select-none'>{content}</div>}
|
|
212
174
|
</Show>
|
|
213
|
-
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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 [
|
|
38
|
+
const [ref, setRef] = createSignal<HTMLDivElement | undefined>();
|
|
28
39
|
let percentX = 0;
|
|
29
40
|
let percentY = 0;
|
|
30
41
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
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
|
-
|
|
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
|
|
74
|
-
selector:
|
|
75
|
-
const list =
|
|
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:
|
|
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
|
-
|
|
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-
|
|
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={
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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'
|
|
@@ -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>;
|