@zoneflow/renderer-dom 0.0.7 → 0.0.8
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/dist/engines/debugDrawEngine.js +66 -26
- package/dist/engines/drawEngine.js +69 -29
- package/package.json +2 -2
|
@@ -5,6 +5,21 @@ const DEFAULT_DEBUG_LAYERS = [
|
|
|
5
5
|
"edges",
|
|
6
6
|
"anchors",
|
|
7
7
|
];
|
|
8
|
+
const EDGE_FLOW_CLASS = "zoneflow-debug-edge-flow";
|
|
9
|
+
const EDGE_FLOW_STYLE = `
|
|
10
|
+
@keyframes zoneflow-debug-edge-flow {
|
|
11
|
+
from { stroke-dashoffset: 18; }
|
|
12
|
+
to { stroke-dashoffset: 0; }
|
|
13
|
+
}
|
|
14
|
+
.${EDGE_FLOW_CLASS} {
|
|
15
|
+
animation: zoneflow-debug-edge-flow 900ms linear infinite;
|
|
16
|
+
}
|
|
17
|
+
@media (prefers-reduced-motion: reduce) {
|
|
18
|
+
.${EDGE_FLOW_CLASS} {
|
|
19
|
+
animation: none;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
8
23
|
const drawLayerMap = {
|
|
9
24
|
"graph-layout": drawGraphLayout,
|
|
10
25
|
density: drawDensity,
|
|
@@ -17,6 +32,11 @@ const drawLayerMap = {
|
|
|
17
32
|
function createSvgElement(tag) {
|
|
18
33
|
return document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
19
34
|
}
|
|
35
|
+
function appendEdgeFlowStyle(svg) {
|
|
36
|
+
const style = createSvgElement("style");
|
|
37
|
+
style.textContent = EDGE_FLOW_STYLE;
|
|
38
|
+
svg.appendChild(style);
|
|
39
|
+
}
|
|
20
40
|
function sortZoneVisualsForRender(pipeline) {
|
|
21
41
|
function getDepth(zoneId) {
|
|
22
42
|
let depth = 0;
|
|
@@ -41,22 +61,39 @@ function getEdgeColor(kind) {
|
|
|
41
61
|
}
|
|
42
62
|
function getBezierCurvePathD(params) {
|
|
43
63
|
const { source, target } = params;
|
|
64
|
+
const distanceX = Math.abs(target.x - source.x);
|
|
65
|
+
const distanceY = Math.abs(target.y - source.y);
|
|
66
|
+
if (distanceX <= 72 && distanceY <= 48) {
|
|
67
|
+
return `M ${source.x} ${source.y} L ${target.x} ${target.y}`;
|
|
68
|
+
}
|
|
44
69
|
const sourceLead = Math.min(Math.max(Math.abs(target.x - source.x) * 0.18, 18), 42);
|
|
45
70
|
const leadSourceX = source.x + sourceLead;
|
|
46
|
-
const
|
|
47
|
-
const
|
|
71
|
+
const targetLead = Math.min(Math.max(Math.abs(target.x - source.x) * 0.16, 18), 42);
|
|
72
|
+
const targetApproachX = target.x - targetLead;
|
|
73
|
+
const shouldRouteAround = targetApproachX - leadSourceX < 36;
|
|
74
|
+
if (shouldRouteAround) {
|
|
75
|
+
const bridgeDistance = Math.abs(leadSourceX - targetApproachX);
|
|
76
|
+
const midX = (leadSourceX + targetApproachX) / 2;
|
|
77
|
+
const sourceBendX = leadSourceX + Math.min(Math.max(bridgeDistance * 0.22, 28), 72);
|
|
78
|
+
const targetBendX = targetApproachX - Math.min(Math.max(bridgeDistance * 0.22, 28), 72);
|
|
79
|
+
const verticalGap = Math.abs(target.y - source.y);
|
|
80
|
+
const verticalDirection = target.y >= source.y ? 1 : -1;
|
|
81
|
+
const laneOffset = Math.min(Math.max(Math.abs(target.x - source.x) * 0.22 + 48, 56), 144);
|
|
82
|
+
const laneY = (source.y + target.y) / 2 +
|
|
83
|
+
(verticalGap < 36 ? verticalDirection * laneOffset : 0);
|
|
84
|
+
return [
|
|
85
|
+
`M ${source.x} ${source.y}`,
|
|
86
|
+
`L ${leadSourceX} ${source.y}`,
|
|
87
|
+
`C ${sourceBendX} ${source.y}, ${sourceBendX} ${laneY}, ${midX} ${laneY}`,
|
|
88
|
+
`C ${targetBendX} ${laneY}, ${targetBendX} ${target.y}, ${targetApproachX} ${target.y}`,
|
|
89
|
+
`L ${target.x} ${target.y}`,
|
|
90
|
+
].join(" ");
|
|
91
|
+
}
|
|
92
|
+
const dx = targetApproachX - leadSourceX;
|
|
48
93
|
const handle = Math.min(Math.max(Math.abs(dx) * 0.45, 28), 104);
|
|
49
|
-
const control1X = leadSourceX + handle
|
|
50
|
-
const control2X =
|
|
51
|
-
return `M ${source.x} ${source.y} L ${leadSourceX} ${source.y} C ${control1X} ${source.y}, ${control2X} ${target.y}, ${target.x} ${target.y}`;
|
|
52
|
-
}
|
|
53
|
-
function getChevronPathD(params) {
|
|
54
|
-
const { target, direction } = params;
|
|
55
|
-
const tipX = target.x - direction * 6;
|
|
56
|
-
const baseX = tipX - direction * 7;
|
|
57
|
-
const topY = target.y - 4;
|
|
58
|
-
const bottomY = target.y + 4;
|
|
59
|
-
return `M ${baseX} ${topY} L ${tipX} ${target.y} L ${baseX} ${bottomY}`;
|
|
94
|
+
const control1X = leadSourceX + handle;
|
|
95
|
+
const control2X = targetApproachX - handle;
|
|
96
|
+
return `M ${source.x} ${source.y} L ${leadSourceX} ${source.y} C ${control1X} ${source.y}, ${control2X} ${target.y}, ${targetApproachX} ${target.y} L ${target.x} ${target.y}`;
|
|
60
97
|
}
|
|
61
98
|
function filterPipelineForExclusion(input) {
|
|
62
99
|
const excludedZoneIds = new Set(input.exclusionState?.excludedZoneIds ?? []);
|
|
@@ -266,32 +303,35 @@ function drawEdges(root, pipeline) {
|
|
|
266
303
|
svg.style.height = "100%";
|
|
267
304
|
svg.style.overflow = "visible";
|
|
268
305
|
svg.style.pointerEvents = "none";
|
|
306
|
+
appendEdgeFlowStyle(svg);
|
|
269
307
|
Object.values(edgesByPathId)
|
|
270
308
|
.flatMap((edges) => edges)
|
|
271
309
|
.forEach((edge) => {
|
|
272
310
|
const path = createSvgElement("path");
|
|
273
311
|
const stroke = getEdgeColor(edge.kind);
|
|
274
|
-
|
|
312
|
+
const pathD = getBezierCurvePathD({
|
|
275
313
|
source: edge.source,
|
|
276
314
|
target: edge.target,
|
|
277
|
-
})
|
|
315
|
+
});
|
|
316
|
+
path.setAttribute("d", pathD);
|
|
278
317
|
path.setAttribute("fill", "none");
|
|
279
318
|
path.setAttribute("stroke", stroke);
|
|
280
319
|
path.setAttribute("stroke-width", edge.kind === "zone-to-path" ? "2" : "2.4");
|
|
281
320
|
path.setAttribute("stroke-linecap", "round");
|
|
282
321
|
path.setAttribute("stroke-linejoin", "round");
|
|
322
|
+
path.setAttribute("opacity", "0.42");
|
|
283
323
|
svg.appendChild(path);
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
svg.appendChild(
|
|
324
|
+
const flow = createSvgElement("path");
|
|
325
|
+
flow.setAttribute("d", pathD);
|
|
326
|
+
flow.setAttribute("fill", "none");
|
|
327
|
+
flow.setAttribute("stroke", stroke);
|
|
328
|
+
flow.setAttribute("stroke-width", edge.kind === "zone-to-path" ? "2.45" : "2.85");
|
|
329
|
+
flow.setAttribute("stroke-linecap", "round");
|
|
330
|
+
flow.setAttribute("stroke-linejoin", "round");
|
|
331
|
+
flow.setAttribute("stroke-dasharray", "1 11");
|
|
332
|
+
flow.setAttribute("stroke-dashoffset", "0");
|
|
333
|
+
flow.setAttribute("class", EDGE_FLOW_CLASS);
|
|
334
|
+
svg.appendChild(flow);
|
|
295
335
|
});
|
|
296
336
|
root.appendChild(svg);
|
|
297
337
|
}
|
|
@@ -9,6 +9,21 @@ const RENDER_Z_INDEX = {
|
|
|
9
9
|
edgeLayer: 20,
|
|
10
10
|
pathLayer: 30,
|
|
11
11
|
};
|
|
12
|
+
const EDGE_FLOW_CLASS = "zoneflow-edge-flow";
|
|
13
|
+
const EDGE_FLOW_STYLE = `
|
|
14
|
+
@keyframes zoneflow-edge-flow {
|
|
15
|
+
from { stroke-dashoffset: 18; }
|
|
16
|
+
to { stroke-dashoffset: 0; }
|
|
17
|
+
}
|
|
18
|
+
.${EDGE_FLOW_CLASS} {
|
|
19
|
+
animation: zoneflow-edge-flow 900ms linear infinite;
|
|
20
|
+
}
|
|
21
|
+
@media (prefers-reduced-motion: reduce) {
|
|
22
|
+
.${EDGE_FLOW_CLASS} {
|
|
23
|
+
animation: none;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
12
27
|
function applyStyles(el, styles) {
|
|
13
28
|
for (const [key, value] of Object.entries(styles)) {
|
|
14
29
|
// @ts-expect-error CSSStyleDeclaration index access
|
|
@@ -121,6 +136,11 @@ function getOpacity(emphasis) {
|
|
|
121
136
|
function createSvgElement(tag) {
|
|
122
137
|
return document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
123
138
|
}
|
|
139
|
+
function appendEdgeFlowStyle(svg) {
|
|
140
|
+
const style = createSvgElement("style");
|
|
141
|
+
style.textContent = EDGE_FLOW_STYLE;
|
|
142
|
+
svg.appendChild(style);
|
|
143
|
+
}
|
|
124
144
|
function getEdgeColor(params) {
|
|
125
145
|
return params.kind === "zone-to-path"
|
|
126
146
|
? params.theme.pathEdge
|
|
@@ -128,22 +148,39 @@ function getEdgeColor(params) {
|
|
|
128
148
|
}
|
|
129
149
|
function getBezierCurvePathD(params) {
|
|
130
150
|
const { source, target } = params;
|
|
151
|
+
const distanceX = Math.abs(target.x - source.x);
|
|
152
|
+
const distanceY = Math.abs(target.y - source.y);
|
|
153
|
+
if (distanceX <= 72 && distanceY <= 48) {
|
|
154
|
+
return `M ${source.x} ${source.y} L ${target.x} ${target.y}`;
|
|
155
|
+
}
|
|
131
156
|
const sourceLead = Math.min(Math.max(Math.abs(target.x - source.x) * 0.18, 18), 42);
|
|
132
157
|
const leadSourceX = source.x + sourceLead;
|
|
133
|
-
const
|
|
134
|
-
const
|
|
158
|
+
const targetLead = Math.min(Math.max(Math.abs(target.x - source.x) * 0.16, 18), 42);
|
|
159
|
+
const targetApproachX = target.x - targetLead;
|
|
160
|
+
const shouldRouteAround = targetApproachX - leadSourceX < 36;
|
|
161
|
+
if (shouldRouteAround) {
|
|
162
|
+
const bridgeDistance = Math.abs(leadSourceX - targetApproachX);
|
|
163
|
+
const midX = (leadSourceX + targetApproachX) / 2;
|
|
164
|
+
const sourceBendX = leadSourceX + Math.min(Math.max(bridgeDistance * 0.22, 28), 72);
|
|
165
|
+
const targetBendX = targetApproachX - Math.min(Math.max(bridgeDistance * 0.22, 28), 72);
|
|
166
|
+
const verticalGap = Math.abs(target.y - source.y);
|
|
167
|
+
const verticalDirection = target.y >= source.y ? 1 : -1;
|
|
168
|
+
const laneOffset = Math.min(Math.max(Math.abs(target.x - source.x) * 0.22 + 48, 56), 144);
|
|
169
|
+
const laneY = (source.y + target.y) / 2 +
|
|
170
|
+
(verticalGap < 36 ? verticalDirection * laneOffset : 0);
|
|
171
|
+
return [
|
|
172
|
+
`M ${source.x} ${source.y}`,
|
|
173
|
+
`L ${leadSourceX} ${source.y}`,
|
|
174
|
+
`C ${sourceBendX} ${source.y}, ${sourceBendX} ${laneY}, ${midX} ${laneY}`,
|
|
175
|
+
`C ${targetBendX} ${laneY}, ${targetBendX} ${target.y}, ${targetApproachX} ${target.y}`,
|
|
176
|
+
`L ${target.x} ${target.y}`,
|
|
177
|
+
].join(" ");
|
|
178
|
+
}
|
|
179
|
+
const dx = targetApproachX - leadSourceX;
|
|
135
180
|
const handle = Math.min(Math.max(Math.abs(dx) * 0.45, 28), 104);
|
|
136
|
-
const control1X = leadSourceX + handle
|
|
137
|
-
const control2X =
|
|
138
|
-
return `M ${source.x} ${source.y} L ${leadSourceX} ${source.y} C ${control1X} ${source.y}, ${control2X} ${target.y}, ${target.x} ${target.y}`;
|
|
139
|
-
}
|
|
140
|
-
function getChevronPathD(params) {
|
|
141
|
-
const { target, direction } = params;
|
|
142
|
-
const tipX = target.x - direction * 6;
|
|
143
|
-
const baseX = tipX - direction * 7;
|
|
144
|
-
const topY = target.y - 4;
|
|
145
|
-
const bottomY = target.y + 4;
|
|
146
|
-
return `M ${baseX} ${topY} L ${tipX} ${target.y} L ${baseX} ${bottomY}`;
|
|
181
|
+
const control1X = leadSourceX + handle;
|
|
182
|
+
const control2X = targetApproachX - handle;
|
|
183
|
+
return `M ${source.x} ${source.y} L ${leadSourceX} ${source.y} C ${control1X} ${source.y}, ${control2X} ${target.y}, ${targetApproachX} ${target.y} L ${target.x} ${target.y}`;
|
|
147
184
|
}
|
|
148
185
|
function computeSceneBounds(input) {
|
|
149
186
|
const { pipeline, viewportInfo, } = input;
|
|
@@ -423,6 +460,7 @@ function createPathSlotHost(params) {
|
|
|
423
460
|
}
|
|
424
461
|
function drawEdges(params) {
|
|
425
462
|
const { svg, input } = params;
|
|
463
|
+
appendEdgeFlowStyle(svg);
|
|
426
464
|
for (const [pathId, edges] of Object.entries(input.pipeline.graphLayout.edgesByPathId)) {
|
|
427
465
|
const visibility = input.pipeline.visibility.pathVisibilityById[pathId];
|
|
428
466
|
if (!visibility?.shouldRenderEdge)
|
|
@@ -432,30 +470,32 @@ function drawEdges(params) {
|
|
|
432
470
|
kind: edge.kind,
|
|
433
471
|
theme: input.theme,
|
|
434
472
|
});
|
|
435
|
-
const
|
|
436
|
-
path.setAttribute("d", getBezierCurvePathD({
|
|
473
|
+
const pathD = getBezierCurvePathD({
|
|
437
474
|
source: edge.source,
|
|
438
475
|
target: edge.target,
|
|
439
|
-
})
|
|
476
|
+
});
|
|
477
|
+
const opacity = getOpacity(visibility.emphasis);
|
|
478
|
+
const path = createSvgElement("path");
|
|
479
|
+
path.setAttribute("d", pathD);
|
|
440
480
|
path.setAttribute("fill", "none");
|
|
441
481
|
path.setAttribute("stroke", stroke);
|
|
442
482
|
path.setAttribute("stroke-width", edge.kind === "path-to-zone" ? "2.25" : "1.85");
|
|
443
483
|
path.setAttribute("stroke-linecap", "round");
|
|
444
484
|
path.setAttribute("stroke-linejoin", "round");
|
|
445
|
-
path.setAttribute("opacity", String(
|
|
485
|
+
path.setAttribute("opacity", String(opacity * 0.42));
|
|
446
486
|
svg.appendChild(path);
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
svg.appendChild(
|
|
487
|
+
const flow = createSvgElement("path");
|
|
488
|
+
flow.setAttribute("d", pathD);
|
|
489
|
+
flow.setAttribute("fill", "none");
|
|
490
|
+
flow.setAttribute("stroke", stroke);
|
|
491
|
+
flow.setAttribute("stroke-width", edge.kind === "path-to-zone" ? "2.8" : "2.35");
|
|
492
|
+
flow.setAttribute("stroke-linecap", "round");
|
|
493
|
+
flow.setAttribute("stroke-linejoin", "round");
|
|
494
|
+
flow.setAttribute("stroke-dasharray", "1 11");
|
|
495
|
+
flow.setAttribute("stroke-dashoffset", "0");
|
|
496
|
+
flow.setAttribute("opacity", String(opacity));
|
|
497
|
+
flow.setAttribute("class", EDGE_FLOW_CLASS);
|
|
498
|
+
svg.appendChild(flow);
|
|
459
499
|
}
|
|
460
500
|
}
|
|
461
501
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zoneflow/renderer-dom",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Low-level DOM renderer engines for Zoneflow.",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@zoneflow/core": "0.0.
|
|
22
|
+
"@zoneflow/core": "0.0.8"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc -p tsconfig.json",
|