@bemedev/mind-flow 0.1.1 → 0.3.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.
- 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 +556 -510
- package/lib/output.css +1 -1
- package/lib/server.cjs.js +645 -418
- package/lib/server.es.js +649 -422
- 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 +8 -8
- 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 +78 -56
- 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 +60 -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,35 @@ 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
|
+
if (!item) return undefined;
|
|
48
|
+
return {
|
|
49
|
+
x: item.position.x,
|
|
50
|
+
y: item.position.y,
|
|
51
|
+
label: item.data.label,
|
|
52
|
+
content: item.data.content ?? '',
|
|
53
|
+
input: item.input,
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
equals: dequal,
|
|
62
57
|
});
|
|
63
58
|
|
|
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 };
|
|
59
|
+
const newEdge = createState(service, { selector: s => s.context.newEdge });
|
|
92
60
|
|
|
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
|
-
);
|
|
61
|
+
const selected = createState(service, {
|
|
62
|
+
selector: s => s.context.selected === props.id,
|
|
107
63
|
});
|
|
108
64
|
|
|
109
|
-
const hasParent =
|
|
65
|
+
const hasParent = createState(service, {
|
|
110
66
|
selector: ({ context: { data } }) => {
|
|
111
67
|
const edges = data?.edges;
|
|
112
68
|
if (!edges) return false;
|
|
@@ -119,22 +75,21 @@ export const NodeComponent: Component<Props> = props => {
|
|
|
119
75
|
|
|
120
76
|
return (
|
|
121
77
|
<div
|
|
122
|
-
ref={setRef}
|
|
123
|
-
id={props.id}
|
|
124
78
|
classList={{
|
|
125
79
|
'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
80
|
'border border-[#e38c29] z-[100]': selected(),
|
|
127
81
|
'border border-[#e6d4be] z-[1]': !selected(),
|
|
128
82
|
}}
|
|
129
|
-
|
|
83
|
+
|
|
84
|
+
style={{ top: `${node()?.y ?? 0}px`, left: `${node()?.x ?? 0}px` }}
|
|
85
|
+
class='min-w-48'
|
|
86
|
+
use:draggable={{ skipTransform: true }}
|
|
87
|
+
id={props.id}
|
|
88
|
+
|
|
130
89
|
onMouseDown={e => {
|
|
131
90
|
e.stopPropagation();
|
|
132
|
-
e.stopImmediatePropagation();
|
|
133
91
|
service.send({ type: 'SELECT', payload: props.id });
|
|
134
92
|
}}
|
|
135
|
-
class='min-w-48'
|
|
136
|
-
|
|
137
|
-
use:draggable={{ skipTransform: true }}
|
|
138
93
|
>
|
|
139
94
|
<div
|
|
140
95
|
classList={{
|
|
@@ -145,13 +100,15 @@ export const NodeComponent: Component<Props> = props => {
|
|
|
145
100
|
>
|
|
146
101
|
<svg
|
|
147
102
|
class='cursor-pointer rounded-full fill-[#a11111] opacity-100 transition-all duration-200 ease-in-out'
|
|
103
|
+
fill='currentColor'
|
|
104
|
+
stroke-width='2'
|
|
105
|
+
viewBox='4 4 16 16'
|
|
106
|
+
|
|
148
107
|
onClick={e => {
|
|
149
108
|
e.stopPropagation();
|
|
150
109
|
service.send({ type: 'DELETE', payload: props.id });
|
|
151
110
|
}}
|
|
152
|
-
|
|
153
|
-
stroke-width='2'
|
|
154
|
-
viewBox='4 4 16 16'
|
|
111
|
+
|
|
155
112
|
style={{
|
|
156
113
|
overflow: 'visible',
|
|
157
114
|
'pointer-events': 'all',
|
|
@@ -161,6 +118,7 @@ export const NodeComponent: Component<Props> = props => {
|
|
|
161
118
|
>
|
|
162
119
|
<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
120
|
</svg>
|
|
121
|
+
|
|
164
122
|
<Show when={hasParent()}>
|
|
165
123
|
<svg
|
|
166
124
|
class='cursor-pointer overflow-visible rounded-full bg-green-500 p-0.5 text-center font-bold hover:bg-green-600'
|
|
@@ -173,6 +131,7 @@ export const NodeComponent: Component<Props> = props => {
|
|
|
173
131
|
width: `${HANDLE_SIZE * 2}px`,
|
|
174
132
|
height: `${HANDLE_SIZE * 2}px`,
|
|
175
133
|
}}
|
|
134
|
+
|
|
176
135
|
viewBox='0 0 1024 1024'
|
|
177
136
|
preserveAspectRatio='xMaxYMax'
|
|
178
137
|
xmlns='http://www.w3.org/2000/svg'
|
|
@@ -181,6 +140,7 @@ export const NodeComponent: Component<Props> = props => {
|
|
|
181
140
|
>
|
|
182
141
|
<g id='background'>
|
|
183
142
|
<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' />
|
|
143
|
+
|
|
184
144
|
<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
145
|
</g>
|
|
186
146
|
</svg>
|
|
@@ -188,55 +148,59 @@ export const NodeComponent: Component<Props> = props => {
|
|
|
188
148
|
<svg
|
|
189
149
|
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
150
|
onClick={() => service.send({ type: 'ADD_CHILD', payload: props.id })}
|
|
151
|
+
viewBox='0 0 24 24'
|
|
152
|
+
stroke='currentColor'
|
|
153
|
+
stroke-width='2'
|
|
154
|
+
|
|
191
155
|
style={{
|
|
192
156
|
'pointer-events': 'all',
|
|
193
157
|
width: `${HANDLE_SIZE * 2}px`,
|
|
194
158
|
height: `${HANDLE_SIZE * 2}px`,
|
|
195
159
|
}}
|
|
196
|
-
viewBox='0 0 24 24'
|
|
197
|
-
stroke='currentColor'
|
|
198
|
-
stroke-width='2'
|
|
199
160
|
>
|
|
200
161
|
<path d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z' />
|
|
201
162
|
</svg>
|
|
202
163
|
</div>
|
|
203
|
-
|
|
164
|
+
|
|
165
|
+
<Show when={node()?.label} keyed>
|
|
204
166
|
{label => (
|
|
205
167
|
<span class='min-w-max border-b border-[#f0f0f0] p-3 whitespace-nowrap text-red-600 select-none'>
|
|
206
168
|
{label}
|
|
207
169
|
</span>
|
|
208
170
|
)}
|
|
209
171
|
</Show>
|
|
210
|
-
|
|
172
|
+
|
|
173
|
+
<Show when={node()?.content} keyed>
|
|
211
174
|
{content => <div class='p-3 select-none'>{content}</div>}
|
|
212
175
|
</Show>
|
|
213
|
-
|
|
176
|
+
|
|
177
|
+
<Show when={node()?.input || hasParent()}>
|
|
214
178
|
<div
|
|
215
179
|
id='outputs'
|
|
216
180
|
class='pointer-events-none absolute top-0 z-10 flex cursor-default flex-col'
|
|
217
181
|
style={{ left: `-${HANDLE_CONTAINER_OFFSET_X}px` }}
|
|
218
182
|
>
|
|
219
183
|
<div
|
|
220
|
-
ref={el => (inputRef = el)}
|
|
221
184
|
class='cursor-default rounded-full bg-[#e38b29] shadow-md'
|
|
185
|
+
|
|
222
186
|
style={{
|
|
223
187
|
width: `${HANDLE_SIZE}px`,
|
|
224
188
|
height: `${HANDLE_SIZE}px`,
|
|
225
189
|
'margin-top': `${HANDLE_MARGIN_TOP}px`,
|
|
226
190
|
'pointer-events': 'all',
|
|
227
191
|
}}
|
|
192
|
+
|
|
228
193
|
onMouseDown={event => {
|
|
229
194
|
event.stopPropagation();
|
|
230
195
|
}}
|
|
196
|
+
|
|
231
197
|
onMouseUp={event => {
|
|
232
198
|
event.stopPropagation();
|
|
233
199
|
const from = newEdge()?.from;
|
|
234
200
|
|
|
235
201
|
if (from) {
|
|
236
202
|
service.send({ type: 'ADD_EDGE', payload: { from, to: props.id } });
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
setNewEdge();
|
|
203
|
+
} else service.send('CLEAR_NEW_EDGE');
|
|
240
204
|
}}
|
|
241
205
|
></div>
|
|
242
206
|
</div>
|
|
@@ -247,27 +211,19 @@ export const NodeComponent: Component<Props> = props => {
|
|
|
247
211
|
style={{ right: `-${HANDLE_CONTAINER_OFFSET_X}px` }}
|
|
248
212
|
>
|
|
249
213
|
<div
|
|
250
|
-
ref={el => (outputRef = el)}
|
|
251
214
|
class='cursor-crosshair rounded-full bg-[#e38b29] shadow-md'
|
|
215
|
+
|
|
252
216
|
style={{
|
|
253
217
|
width: `${HANDLE_SIZE}px`,
|
|
254
218
|
height: `${HANDLE_SIZE}px`,
|
|
255
219
|
'margin-top': `${HANDLE_MARGIN_TOP}px`,
|
|
256
220
|
'pointer-events': 'all',
|
|
257
221
|
}}
|
|
222
|
+
|
|
258
223
|
onMouseDown={event => {
|
|
259
224
|
event.stopPropagation();
|
|
260
225
|
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
|
-
});
|
|
226
|
+
service.send({ type: 'START_NEW_EDGE', payload: props.id });
|
|
271
227
|
}}
|
|
272
228
|
></div>
|
|
273
229
|
</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>;
|