@antv/infographic 0.2.14 → 0.2.15
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 +12 -5
- package/README.zh-CN.md +12 -5
- package/dist/infographic.min.js +155 -153
- package/dist/infographic.min.js.map +1 -1
- package/esm/designs/structures/index.d.ts +1 -0
- package/esm/designs/structures/index.js +1 -0
- package/esm/designs/structures/relation-dagre-flow.js +4 -139
- package/esm/designs/structures/sequence-interaction.d.ts +54 -0
- package/esm/designs/structures/sequence-interaction.js +440 -0
- package/esm/designs/utils/geometry.d.ts +44 -0
- package/esm/designs/utils/geometry.js +244 -0
- package/esm/designs/utils/index.d.ts +1 -0
- package/esm/designs/utils/index.js +1 -0
- package/esm/editor/managers/sync-registry.d.ts +2 -1
- package/esm/editor/types/editor.d.ts +2 -1
- package/esm/editor/types/sync.d.ts +2 -1
- package/esm/editor/utils/object.js +46 -39
- package/esm/options/types.d.ts +6 -0
- package/esm/runtime/Infographic.js +20 -7
- package/esm/syntax/index.js +40 -20
- package/esm/syntax/relations.js +26 -2
- package/esm/syntax/schema.js +1 -0
- package/esm/templates/built-in.js +3 -2
- package/esm/templates/sequence-interaction.d.ts +2 -0
- package/esm/templates/sequence-interaction.js +76 -0
- package/esm/types/data.d.ts +1 -0
- package/esm/utils/index.d.ts +1 -0
- package/esm/utils/index.js +1 -0
- package/esm/utils/measure-text.js +31 -3
- package/esm/utils/types.d.ts +16 -0
- package/esm/utils/types.js +12 -0
- package/esm/version.d.ts +1 -1
- package/esm/version.js +1 -1
- package/lib/designs/structures/index.d.ts +1 -0
- package/lib/designs/structures/index.js +1 -0
- package/lib/designs/structures/relation-dagre-flow.js +5 -140
- package/lib/designs/structures/sequence-interaction.d.ts +54 -0
- package/lib/designs/structures/sequence-interaction.js +444 -0
- package/lib/designs/utils/geometry.d.ts +44 -0
- package/lib/designs/utils/geometry.js +256 -0
- package/lib/designs/utils/index.d.ts +1 -0
- package/lib/designs/utils/index.js +1 -0
- package/lib/editor/managers/sync-registry.d.ts +2 -1
- package/lib/editor/types/editor.d.ts +2 -1
- package/lib/editor/types/sync.d.ts +2 -1
- package/lib/editor/utils/object.js +45 -38
- package/lib/options/types.d.ts +6 -0
- package/lib/runtime/Infographic.js +19 -6
- package/lib/syntax/index.js +40 -20
- package/lib/syntax/relations.js +26 -2
- package/lib/syntax/schema.js +1 -0
- package/lib/templates/built-in.js +3 -2
- package/lib/templates/sequence-interaction.d.ts +2 -0
- package/lib/templates/sequence-interaction.js +79 -0
- package/lib/types/data.d.ts +1 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/lib/utils/measure-text.js +30 -2
- package/lib/utils/types.d.ts +16 -0
- package/lib/utils/types.js +13 -0
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/src/designs/structures/index.ts +1 -0
- package/src/designs/structures/relation-dagre-flow.tsx +14 -178
- package/src/designs/structures/sequence-interaction.tsx +885 -0
- package/src/designs/utils/geometry.tsx +315 -0
- package/src/designs/utils/index.ts +1 -0
- package/src/editor/managers/sync-registry.ts +2 -1
- package/src/editor/types/editor.ts +2 -1
- package/src/editor/types/sync.ts +3 -1
- package/src/editor/utils/object.ts +50 -40
- package/src/options/types.ts +7 -0
- package/src/runtime/Infographic.tsx +27 -17
- package/src/syntax/index.ts +51 -18
- package/src/syntax/relations.ts +29 -2
- package/src/syntax/schema.ts +1 -0
- package/src/templates/built-in.ts +2 -0
- package/src/templates/sequence-interaction.ts +101 -0
- package/src/types/data.ts +1 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/measure-text.ts +35 -3
- package/src/utils/types.ts +61 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getEdgePathD = exports.getLabelPosition = exports.getPointAtT = exports.getTangentAngle = exports.getNodesAnchors = exports.createArrowElements = exports.createRoundedPath = exports.createStraightPath = exports.getMidPoint = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("@antv/infographic/jsx-runtime");
|
|
5
|
+
const jsx_1 = require("../../jsx");
|
|
6
|
+
const getMidPoint = (points) => {
|
|
7
|
+
if (points.length === 0)
|
|
8
|
+
return null;
|
|
9
|
+
if (points.length === 1)
|
|
10
|
+
return points[0];
|
|
11
|
+
let total = 0;
|
|
12
|
+
const segments = [];
|
|
13
|
+
for (let i = 0; i < points.length - 1; i += 1) {
|
|
14
|
+
const start = points[i];
|
|
15
|
+
const end = points[i + 1];
|
|
16
|
+
const length = Math.hypot(end[0] - start[0], end[1] - start[1]);
|
|
17
|
+
segments.push({ length, start, end });
|
|
18
|
+
total += length;
|
|
19
|
+
}
|
|
20
|
+
if (total === 0)
|
|
21
|
+
return points[0];
|
|
22
|
+
let target = total / 2;
|
|
23
|
+
for (let i = 0; i < segments.length; i += 1) {
|
|
24
|
+
const segment = segments[i];
|
|
25
|
+
if (target <= segment.length || i === segments.length - 1) {
|
|
26
|
+
const ratio = segment.length === 0
|
|
27
|
+
? 0
|
|
28
|
+
: Math.max(0, Math.min(1, target / segment.length));
|
|
29
|
+
return [
|
|
30
|
+
segment.start[0] + (segment.end[0] - segment.start[0]) * ratio,
|
|
31
|
+
segment.start[1] + (segment.end[1] - segment.start[1]) * ratio,
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
target -= segment.length;
|
|
35
|
+
}
|
|
36
|
+
return points[Math.floor(points.length / 2)];
|
|
37
|
+
};
|
|
38
|
+
exports.getMidPoint = getMidPoint;
|
|
39
|
+
const createStraightPath = (points, dx, dy) => points
|
|
40
|
+
.map(([x, y], index) => {
|
|
41
|
+
const prefix = index === 0 ? 'M' : 'L';
|
|
42
|
+
return `${prefix} ${x + dx} ${y + dy}`;
|
|
43
|
+
})
|
|
44
|
+
.join(' ');
|
|
45
|
+
exports.createStraightPath = createStraightPath;
|
|
46
|
+
const createRoundedPath = (points, radius, dx, dy) => {
|
|
47
|
+
if (points.length < 2)
|
|
48
|
+
return '';
|
|
49
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
50
|
+
const toPoint = ([x, y]) => ({
|
|
51
|
+
x: x + dx,
|
|
52
|
+
y: y + dy,
|
|
53
|
+
});
|
|
54
|
+
const output = [];
|
|
55
|
+
const first = toPoint(points[0]);
|
|
56
|
+
output.push(`M ${first.x} ${first.y}`);
|
|
57
|
+
if (points.length === 2) {
|
|
58
|
+
const last = toPoint(points[1]);
|
|
59
|
+
output.push(`L ${last.x} ${last.y}`);
|
|
60
|
+
return output.join(' ');
|
|
61
|
+
}
|
|
62
|
+
for (let i = 1; i < points.length - 1; i += 1) {
|
|
63
|
+
const prev = points[i - 1];
|
|
64
|
+
const curr = points[i];
|
|
65
|
+
const next = points[i + 1];
|
|
66
|
+
const v0x = curr[0] - prev[0];
|
|
67
|
+
const v0y = curr[1] - prev[1];
|
|
68
|
+
const v1x = next[0] - curr[0];
|
|
69
|
+
const v1y = next[1] - curr[1];
|
|
70
|
+
const d0 = Math.hypot(v0x, v0y);
|
|
71
|
+
const d1 = Math.hypot(v1x, v1y);
|
|
72
|
+
if (d0 === 0 || d1 === 0) {
|
|
73
|
+
const currPoint = toPoint(curr);
|
|
74
|
+
output.push(`L ${currPoint.x} ${currPoint.y}`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const r = clamp(radius, 0, Math.min(d0, d1) / 2);
|
|
78
|
+
if (r === 0) {
|
|
79
|
+
const currPoint = toPoint(curr);
|
|
80
|
+
output.push(`L ${currPoint.x} ${currPoint.y}`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const u0x = v0x / d0;
|
|
84
|
+
const u0y = v0y / d0;
|
|
85
|
+
const u1x = v1x / d1;
|
|
86
|
+
const u1y = v1y / d1;
|
|
87
|
+
const start = toPoint([curr[0] - u0x * r, curr[1] - u0y * r]);
|
|
88
|
+
const end = toPoint([curr[0] + u1x * r, curr[1] + u1y * r]);
|
|
89
|
+
output.push(`L ${start.x} ${start.y}`);
|
|
90
|
+
const currPoint = toPoint(curr);
|
|
91
|
+
output.push(`Q ${currPoint.x} ${currPoint.y} ${end.x} ${end.y}`);
|
|
92
|
+
}
|
|
93
|
+
const last = toPoint(points[points.length - 1]);
|
|
94
|
+
output.push(`L ${last.x} ${last.y}`);
|
|
95
|
+
return output.join(' ');
|
|
96
|
+
};
|
|
97
|
+
exports.createRoundedPath = createRoundedPath;
|
|
98
|
+
const createArrowElements = (x, y, angle, type, fillColor, edgeWidth, arrowSize) => {
|
|
99
|
+
const ux = Math.cos(angle);
|
|
100
|
+
const uy = Math.sin(angle);
|
|
101
|
+
const px = -uy;
|
|
102
|
+
const py = ux;
|
|
103
|
+
const length = arrowSize;
|
|
104
|
+
const halfWidth = arrowSize * 0.55;
|
|
105
|
+
if (type === 'arrow') {
|
|
106
|
+
const leftX = x - ux * length + px * halfWidth;
|
|
107
|
+
const leftY = y - uy * length + py * halfWidth;
|
|
108
|
+
const rightX = x - ux * length - px * halfWidth;
|
|
109
|
+
const rightY = y - uy * length - py * halfWidth;
|
|
110
|
+
return [
|
|
111
|
+
(0, jsx_runtime_1.jsx)(jsx_1.Path, { d: `M ${leftX} ${leftY} L ${x} ${y} L ${rightX} ${rightY}`, stroke: fillColor, strokeWidth: Math.max(1.5, edgeWidth), strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }),
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
if (type === 'diamond') {
|
|
115
|
+
const diamondLength = length * 1.25;
|
|
116
|
+
const diamondWidth = halfWidth * 0.75;
|
|
117
|
+
const midX = x - ux * diamondLength * 0.5;
|
|
118
|
+
const midY = y - uy * diamondLength * 0.5;
|
|
119
|
+
const diamondPoints = [
|
|
120
|
+
{ x, y },
|
|
121
|
+
{ x: midX + px * diamondWidth, y: midY + py * diamondWidth },
|
|
122
|
+
{ x: x - ux * diamondLength, y: y - uy * diamondLength },
|
|
123
|
+
{ x: midX - px * diamondWidth, y: midY - py * diamondWidth },
|
|
124
|
+
];
|
|
125
|
+
return [
|
|
126
|
+
(0, jsx_runtime_1.jsx)(jsx_1.Polygon, { points: diamondPoints, fill: fillColor, stroke: fillColor, strokeWidth: Math.max(1, edgeWidth * 0.8) }),
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
const trianglePoints = [
|
|
130
|
+
{ x, y },
|
|
131
|
+
{
|
|
132
|
+
x: x - ux * length + px * halfWidth,
|
|
133
|
+
y: y - uy * length + py * halfWidth,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
x: x - ux * length - px * halfWidth,
|
|
137
|
+
y: y - uy * length - py * halfWidth,
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
return [
|
|
141
|
+
(0, jsx_runtime_1.jsx)(jsx_1.Polygon, { points: trianglePoints, fill: fillColor, stroke: fillColor, strokeWidth: Math.max(1, edgeWidth * 0.8) }),
|
|
142
|
+
];
|
|
143
|
+
};
|
|
144
|
+
exports.createArrowElements = createArrowElements;
|
|
145
|
+
// LT: Left Top (radio), LC: Left Center (1/2), LB: Left Bottom (1 - radio)
|
|
146
|
+
// RT: Right Top (radio), RC: Right Center (1/2), RB: Right Bottom (1 - radio)
|
|
147
|
+
const getNodesAnchors = (node) => {
|
|
148
|
+
const { x, y, width, height, radio = 0.25 } = node;
|
|
149
|
+
const q1H = height * radio;
|
|
150
|
+
const halfH = height * 0.5;
|
|
151
|
+
const q3H = height * (1 - radio);
|
|
152
|
+
return {
|
|
153
|
+
LT: { x, y: y + q1H },
|
|
154
|
+
LC: { x, y: y + halfH },
|
|
155
|
+
LB: { x, y: y + q3H },
|
|
156
|
+
RT: { x: x + width, y: y + q1H },
|
|
157
|
+
RC: { x: x + width, y: y + halfH },
|
|
158
|
+
RB: { x: x + width, y: y + q3H },
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
exports.getNodesAnchors = getNodesAnchors;
|
|
162
|
+
const getTangentAngle = (points, t) => {
|
|
163
|
+
const len = points.length;
|
|
164
|
+
// Cubic Bezier (Self loop)
|
|
165
|
+
if (len === 4) {
|
|
166
|
+
const p0 = points[0], p1 = points[1], p2 = points[2], p3 = points[3];
|
|
167
|
+
if (t === 0) {
|
|
168
|
+
return Math.atan2(p1[1] - p0[1], p1[0] - p0[0]);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
return Math.atan2(p3[1] - p2[1], p3[0] - p2[0]);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Quad Bezier (Curved)
|
|
175
|
+
if (len === 3) {
|
|
176
|
+
const p0 = points[0], p1 = points[1], p2 = points[2];
|
|
177
|
+
if (t === 0) {
|
|
178
|
+
return Math.atan2(p1[1] - p0[1], p1[0] - p0[0]);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
return Math.atan2(p2[1] - p1[1], p2[0] - p1[0]);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Line
|
|
185
|
+
if (len === 2) {
|
|
186
|
+
const p0 = points[0], p1 = points[1];
|
|
187
|
+
const angle = Math.atan2(p1[1] - p0[1], p1[0] - p0[0]);
|
|
188
|
+
return angle;
|
|
189
|
+
}
|
|
190
|
+
return 0;
|
|
191
|
+
};
|
|
192
|
+
exports.getTangentAngle = getTangentAngle;
|
|
193
|
+
/**
|
|
194
|
+
* 计算贝塞尔曲线上任意 t (0-1) 位置的点
|
|
195
|
+
*/
|
|
196
|
+
const getPointAtT = (points, t = 0.5) => {
|
|
197
|
+
const len = points.length;
|
|
198
|
+
if (len === 4) {
|
|
199
|
+
const [p0, p1, p2, p3] = points;
|
|
200
|
+
const mt = 1 - t;
|
|
201
|
+
// B(t) = (1-t)^3*P0 + 3(1-t)^2*t*P1 + 3(1-t)*t^2*P2 + t^3*P3
|
|
202
|
+
return [
|
|
203
|
+
Math.pow(mt, 3) * p0[0] +
|
|
204
|
+
3 * Math.pow(mt, 2) * t * p1[0] +
|
|
205
|
+
3 * mt * Math.pow(t, 2) * p2[0] +
|
|
206
|
+
Math.pow(t, 3) * p3[0],
|
|
207
|
+
Math.pow(mt, 3) * p0[1] +
|
|
208
|
+
3 * Math.pow(mt, 2) * t * p1[1] +
|
|
209
|
+
3 * mt * Math.pow(t, 2) * p2[1] +
|
|
210
|
+
Math.pow(t, 3) * p3[1],
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
if (len === 3) {
|
|
214
|
+
const [p0, p1, p2] = points;
|
|
215
|
+
const mt = 1 - t;
|
|
216
|
+
// B(t) = (1-t)^2*P0 + 2(1-t)*t*P1 + t^2*P2
|
|
217
|
+
return [
|
|
218
|
+
Math.pow(mt, 2) * p0[0] + 2 * mt * t * p1[0] + Math.pow(t, 2) * p2[0],
|
|
219
|
+
Math.pow(mt, 2) * p0[1] + 2 * mt * t * p1[1] + Math.pow(t, 2) * p2[1],
|
|
220
|
+
];
|
|
221
|
+
}
|
|
222
|
+
if (len === 2) {
|
|
223
|
+
const [p0, p1] = points;
|
|
224
|
+
return [p0[0] + (p1[0] - p0[0]) * t, p0[1] + (p1[1] - p0[1]) * t];
|
|
225
|
+
}
|
|
226
|
+
return points[0] || [0, 0];
|
|
227
|
+
};
|
|
228
|
+
exports.getPointAtT = getPointAtT;
|
|
229
|
+
const getLabelPosition = (points, selfLoopOffset = 10) => {
|
|
230
|
+
const len = points.length;
|
|
231
|
+
// 默认取中点
|
|
232
|
+
const labelPoint = (0, exports.getPointAtT)(points);
|
|
233
|
+
if (len === 4) {
|
|
234
|
+
// 针对自连接(len=4)的特殊偏移处理
|
|
235
|
+
labelPoint[0] += selfLoopOffset;
|
|
236
|
+
}
|
|
237
|
+
return labelPoint;
|
|
238
|
+
};
|
|
239
|
+
exports.getLabelPosition = getLabelPosition;
|
|
240
|
+
const getEdgePathD = (points) => {
|
|
241
|
+
const len = points.length;
|
|
242
|
+
if (len === 4) {
|
|
243
|
+
const [p0, p1, p2, p3] = points;
|
|
244
|
+
return `M ${p0[0]} ${p0[1]} C ${p1[0]} ${p1[1]} ${p2[0]} ${p2[1]} ${p3[0]} ${p3[1]}`;
|
|
245
|
+
}
|
|
246
|
+
if (len === 3) {
|
|
247
|
+
const [p0, p1, p2] = points;
|
|
248
|
+
return `M ${p0[0]} ${p0[1]} Q ${p1[0]} ${p1[1]} ${p2[0]} ${p2[1]}`;
|
|
249
|
+
}
|
|
250
|
+
if (len === 2) {
|
|
251
|
+
const [p0, p1] = points;
|
|
252
|
+
return `M ${p0[0]} ${p0[1]} L ${p1[0]} ${p1[1]}`;
|
|
253
|
+
}
|
|
254
|
+
return '';
|
|
255
|
+
};
|
|
256
|
+
exports.getEdgePathD = getEdgePathD;
|
|
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./color"), exports);
|
|
18
|
+
__exportStar(require("./geometry"), exports);
|
|
18
19
|
__exportStar(require("./hierarchy-color"), exports);
|
|
19
20
|
__exportStar(require("./item"), exports);
|
|
20
21
|
__exportStar(require("./normalize-percent"), exports);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InfographicOptionPath } from '../../options';
|
|
1
2
|
import { ISyncRegistry, SyncHandler } from '../types';
|
|
2
3
|
type OptionsGetter = () => any;
|
|
3
4
|
export declare class SyncRegistry implements ISyncRegistry {
|
|
@@ -6,7 +7,7 @@ export declare class SyncRegistry implements ISyncRegistry {
|
|
|
6
7
|
private isDispatching;
|
|
7
8
|
private isDestroyed;
|
|
8
9
|
constructor(getOptions: OptionsGetter);
|
|
9
|
-
register(path: string, handler: SyncHandler, options?: {
|
|
10
|
+
register(path: InfographicOptionPath | (string & {}), handler: SyncHandler, options?: {
|
|
10
11
|
immediate?: boolean;
|
|
11
12
|
}): () => void;
|
|
12
13
|
trigger(path: string, newVal: any, oldVal: any): void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InfographicOptionPath } from '../../options';
|
|
1
2
|
import type { ICommandManager } from './command';
|
|
2
3
|
import type { IInteractionManager } from './interaction';
|
|
3
4
|
import type { IPluginManager } from './plugin';
|
|
@@ -10,7 +11,7 @@ export interface IEditor {
|
|
|
10
11
|
state: IStateManager;
|
|
11
12
|
syncRegistry: ISyncRegistry;
|
|
12
13
|
getDocument(): SVGSVGElement;
|
|
13
|
-
registerSync(path: string, handler: SyncHandler, options?: {
|
|
14
|
+
registerSync(path: InfographicOptionPath | (string & {}), handler: SyncHandler, options?: {
|
|
14
15
|
immediate?: boolean;
|
|
15
16
|
}): () => void;
|
|
16
17
|
destroy(): void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InfographicOptionPath } from '../../options';
|
|
1
2
|
/**
|
|
2
3
|
* Sync callback
|
|
3
4
|
* @param newValue The new value after modification
|
|
@@ -12,7 +13,7 @@ export interface ISyncRegistry {
|
|
|
12
13
|
* @param options.immediate Whether to execute immediately (used for view initialization)
|
|
13
14
|
* @returns unregister function
|
|
14
15
|
*/
|
|
15
|
-
register(path: string, handler: SyncHandler, options?: {
|
|
16
|
+
register(path: InfographicOptionPath | (string & {}), handler: SyncHandler, options?: {
|
|
16
17
|
immediate?: boolean;
|
|
17
18
|
}): () => void;
|
|
18
19
|
/**
|
|
@@ -12,62 +12,69 @@ const lodash_es_1 = require("lodash-es");
|
|
|
12
12
|
*/
|
|
13
13
|
function applyOptionUpdates(target, source, basePath = '', options) {
|
|
14
14
|
const { bubbleUp = false, collector } = options !== null && options !== void 0 ? options : {};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// Bubbling notification: from the deepest parent path to the shallowest
|
|
19
|
-
if (bubbleUp && collector && parentPathsToNotify.size > 0) {
|
|
20
|
-
// Sort by path depth in descending order (deepest first)
|
|
21
|
-
const sortedPaths = Array.from(parentPathsToNotify).sort((a, b) => {
|
|
22
|
-
const depthA = a === '' ? 0 : a.split('.').length;
|
|
23
|
-
const depthB = b === '' ? 0 : b.split('.').length;
|
|
24
|
-
return depthB - depthA;
|
|
25
|
-
});
|
|
26
|
-
for (const parentPath of sortedPaths) {
|
|
27
|
-
const newVal = parentPath ? (0, lodash_es_1.get)(target, parentPath) : target;
|
|
28
|
-
// For parent paths, we provide the cloned new value.
|
|
29
|
-
// oldVal is passed as undefined as tracking branch node state is complex.
|
|
30
|
-
collector(parentPath, (0, lodash_es_1.cloneDeep)(newVal), undefined);
|
|
31
|
-
}
|
|
15
|
+
const hasChange = applyOptionUpdatesInternal(target, source, basePath, collector, bubbleUp);
|
|
16
|
+
if (basePath === '' && hasChange && bubbleUp && collector) {
|
|
17
|
+
collector('', (0, lodash_es_1.cloneDeep)(target), undefined);
|
|
32
18
|
}
|
|
33
19
|
}
|
|
34
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Internal recursive function.
|
|
22
|
+
* Returns true if any change occurred within this branch (or its children).
|
|
23
|
+
*/
|
|
24
|
+
function applyOptionUpdatesInternal(target, source, basePath, collector, bubbleUp) {
|
|
25
|
+
let hasChange = false;
|
|
35
26
|
Object.keys(source).forEach((key) => {
|
|
27
|
+
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
36
30
|
const fullPath = basePath ? `${basePath}.${key}` : key;
|
|
37
31
|
const updateValue = source[key];
|
|
38
32
|
const oldValue = target[key];
|
|
33
|
+
let childChanged = false;
|
|
39
34
|
if (updateValue === undefined) {
|
|
40
|
-
delete
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
// Handle deletion: Only delete and notify if the key actually exists
|
|
36
|
+
if (key in target) {
|
|
37
|
+
delete target[key];
|
|
38
|
+
collector === null || collector === void 0 ? void 0 : collector(fullPath, undefined, oldValue);
|
|
39
|
+
childChanged = true;
|
|
40
|
+
}
|
|
44
41
|
}
|
|
45
42
|
else if ((0, lodash_es_1.isPlainObject)(updateValue)) {
|
|
46
|
-
|
|
43
|
+
// Handle nested object
|
|
44
|
+
const oldValueIsObject = (0, lodash_es_1.isPlainObject)(target[key]);
|
|
45
|
+
if (!oldValueIsObject) {
|
|
47
46
|
target[key] = {};
|
|
48
47
|
}
|
|
49
|
-
applyOptionUpdatesInternal(target[key], updateValue, fullPath, collector, bubbleUp
|
|
48
|
+
const grandChildChanged = applyOptionUpdatesInternal(target[key], updateValue, fullPath, collector, bubbleUp);
|
|
49
|
+
if (!oldValueIsObject) {
|
|
50
|
+
// Overwriting a primitive with an object is always a change.
|
|
51
|
+
childChanged = true;
|
|
52
|
+
// If the object was empty (grandChildChanged is false), we still need to report it.
|
|
53
|
+
if (!grandChildChanged) {
|
|
54
|
+
collector === null || collector === void 0 ? void 0 : collector(fullPath, target[key], oldValue);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
childChanged = grandChildChanged;
|
|
59
|
+
}
|
|
50
60
|
}
|
|
51
61
|
else {
|
|
62
|
+
// Handle primitive update
|
|
52
63
|
target[key] = updateValue;
|
|
53
64
|
if (!(0, lodash_es_1.isEqual)(updateValue, oldValue)) {
|
|
54
65
|
collector === null || collector === void 0 ? void 0 : collector(fullPath, updateValue, oldValue);
|
|
55
|
-
|
|
56
|
-
collectParentPaths(basePath, parentPathsToNotify);
|
|
66
|
+
childChanged = true;
|
|
57
67
|
}
|
|
58
68
|
}
|
|
69
|
+
if (childChanged) {
|
|
70
|
+
hasChange = true;
|
|
71
|
+
}
|
|
59
72
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!path) {
|
|
66
|
-
set.add(''); // Root path
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const parts = path.split('.');
|
|
70
|
-
for (let i = parts.length; i >= 0; i--) {
|
|
71
|
-
set.add(parts.slice(0, i).join('.'));
|
|
73
|
+
// Bubbling: Notify if any child changed in this branch
|
|
74
|
+
// The recursion naturally ensures this happens in "deepest-first" (post-order) sequence.
|
|
75
|
+
if (hasChange && bubbleUp && basePath !== '') {
|
|
76
|
+
// Current target is now fully updated for this scope.
|
|
77
|
+
collector === null || collector === void 0 ? void 0 : collector(basePath, (0, lodash_es_1.cloneDeep)(target), undefined);
|
|
72
78
|
}
|
|
79
|
+
return hasChange;
|
|
73
80
|
}
|
package/lib/options/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { DesignOptions, ParsedDesignsOptions } from '../designs';
|
|
|
2
2
|
import type { ElementProps, IInteraction, IPlugin } from '../editor';
|
|
3
3
|
import type { ThemeConfig } from '../themes';
|
|
4
4
|
import type { Data, Padding, ParsedData } from '../types';
|
|
5
|
+
import type { Path } from '../utils';
|
|
5
6
|
export interface InfographicOptions {
|
|
6
7
|
/** 容器,可以是选择器或者 HTMLElement */
|
|
7
8
|
container?: string | HTMLElement;
|
|
@@ -58,4 +59,9 @@ interface SVGOptions {
|
|
|
58
59
|
/** 是否启用背景 */
|
|
59
60
|
background?: boolean;
|
|
60
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* All valid property paths for Infographic Options.
|
|
64
|
+
* Use this to validate paths in SyncRegistry and other places.
|
|
65
|
+
*/
|
|
66
|
+
export type InfographicOptionPath = Path<UpdatableInfographicOptions>;
|
|
61
67
|
export {};
|
|
@@ -105,18 +105,31 @@ class Infographic {
|
|
|
105
105
|
* Compose the SVG template
|
|
106
106
|
*/
|
|
107
107
|
compose(parsedOptions) {
|
|
108
|
-
|
|
108
|
+
var _a, _b;
|
|
109
|
+
const { design, data, themeConfig } = parsedOptions;
|
|
109
110
|
const { title, item, items, structure } = design;
|
|
110
111
|
const { component: Structure, props: structureProps } = structure;
|
|
111
112
|
const Title = title.component;
|
|
112
113
|
const Item = item.component;
|
|
113
114
|
const Items = items.map((it) => it.component);
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
// Apply theme font-family before measurement so measureText uses the correct font
|
|
116
|
+
const themeFontFamily = (_b = (_a = themeConfig === null || themeConfig === void 0 ? void 0 : themeConfig.base) === null || _a === void 0 ? void 0 : _a.text) === null || _b === void 0 ? void 0 : _b['font-family'];
|
|
117
|
+
const previousDefaultFont = renderer_1.DEFAULT_FONT;
|
|
118
|
+
if (themeFontFamily)
|
|
119
|
+
(0, renderer_1.setDefaultFont)(themeFontFamily);
|
|
120
|
+
try {
|
|
121
|
+
const svg = (0, jsx_1.renderSVG)((0, jsx_runtime_1.jsx)(Structure, Object.assign({ data: data, Title: Title, Item: Item, Items: Items, options: parsedOptions }, structureProps)));
|
|
122
|
+
const template = (0, utils_1.parseSVG)(svg);
|
|
123
|
+
if (!template) {
|
|
124
|
+
throw new Error('Failed to parse SVG template');
|
|
125
|
+
}
|
|
126
|
+
return template;
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
// Restore previous default font
|
|
130
|
+
if (themeFontFamily)
|
|
131
|
+
(0, renderer_1.setDefaultFont)(previousDefaultFont);
|
|
118
132
|
}
|
|
119
|
-
return template;
|
|
120
133
|
}
|
|
121
134
|
getTypes() {
|
|
122
135
|
const parsedOptions = this.parsedOptions;
|
package/lib/syntax/index.js
CHANGED
|
@@ -114,27 +114,47 @@ function parseSyntax(input) {
|
|
|
114
114
|
const parsed = (0, relations_1.parseRelationsNode)(relationsNode, errors, 'data.relations');
|
|
115
115
|
if (parsed.relations.length > 0 || parsed.items.length > 0) {
|
|
116
116
|
const current = ((_a = options.data) !== null && _a !== void 0 ? _a : {});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
});
|
|
126
|
-
parsed.items.forEach((item) => {
|
|
127
|
-
const existing = itemMap.get(item.id);
|
|
128
|
-
if (existing) {
|
|
129
|
-
if (!existing.label && item.label)
|
|
130
|
-
existing.label = item.label;
|
|
117
|
+
// 优先使用已存在的数据列表 (sequences, lists, etc.)
|
|
118
|
+
const dataKeys = Object.keys(schema_1.DataSchema.fields).filter((key) => key !== 'items' && key !== 'relations');
|
|
119
|
+
let hasStructuredData = false;
|
|
120
|
+
// 尝试找到一个非空的数据源
|
|
121
|
+
for (const key of dataKeys) {
|
|
122
|
+
if (Array.isArray(current[key]) && current[key].length > 0) {
|
|
123
|
+
hasStructuredData = true;
|
|
124
|
+
break;
|
|
131
125
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
126
|
+
}
|
|
127
|
+
// 如果 items 已包含层级结构数据(带 children),也视为结构化数据
|
|
128
|
+
if (!hasStructuredData &&
|
|
129
|
+
Array.isArray(current.items) &&
|
|
130
|
+
current.items.length > 0 &&
|
|
131
|
+
current.items.some((item) => Array.isArray(item.children) && item.children.length > 0)) {
|
|
132
|
+
hasStructuredData = true;
|
|
133
|
+
}
|
|
134
|
+
// 如果没有找到其他数据源,才尝试合并 items
|
|
135
|
+
if (!hasStructuredData) {
|
|
136
|
+
const existingItems = Array.isArray(current.items)
|
|
137
|
+
? current.items
|
|
138
|
+
: [];
|
|
139
|
+
const normalizedItems = normalizeItems(existingItems);
|
|
140
|
+
const itemMap = new Map();
|
|
141
|
+
normalizedItems.forEach((item) => {
|
|
142
|
+
if (item.id)
|
|
143
|
+
itemMap.set(item.id, item);
|
|
144
|
+
});
|
|
145
|
+
parsed.items.forEach((item) => {
|
|
146
|
+
const existing = itemMap.get(item.id);
|
|
147
|
+
if (existing) {
|
|
148
|
+
if (!existing.label && item.label)
|
|
149
|
+
existing.label = item.label;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
normalizedItems.push(item);
|
|
153
|
+
itemMap.set(item.id, item);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
current.items = normalizedItems;
|
|
157
|
+
}
|
|
138
158
|
current.relations = parsed.relations;
|
|
139
159
|
options.data = current;
|
|
140
160
|
}
|
package/lib/syntax/relations.js
CHANGED
|
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.parseRelationsNode = parseRelationsNode;
|
|
4
4
|
const mapper_1 = require("./mapper");
|
|
5
5
|
const schema_1 = require("./schema");
|
|
6
|
-
const RELATION_TOKEN = /[
|
|
7
|
-
const ARROW_TOKEN = /[
|
|
6
|
+
const RELATION_TOKEN = /(?:[<>o.x-]{2,}|[<>=]{2,})/;
|
|
7
|
+
const ARROW_TOKEN = /(?:[<>o.x-]{2,}|[<>=]{2,})/g;
|
|
8
8
|
function normalizeLabel(text) {
|
|
9
9
|
let label = text.trim();
|
|
10
10
|
if (!label)
|
|
@@ -106,6 +106,30 @@ function readEdge(text, startIndex) {
|
|
|
106
106
|
let label = labelPrefix || undefined;
|
|
107
107
|
let directionToken = arrowToken;
|
|
108
108
|
let index = arrowEnd;
|
|
109
|
+
// Detect split bidirectional arrow pattern: <- label ->
|
|
110
|
+
{
|
|
111
|
+
const leftHasLeft = directionToken.includes('<');
|
|
112
|
+
const leftHasRight = directionToken.includes('>');
|
|
113
|
+
if (leftHasLeft && !leftHasRight) {
|
|
114
|
+
const lookahead = new RegExp(ARROW_TOKEN.source, 'g');
|
|
115
|
+
lookahead.lastIndex = arrowEnd;
|
|
116
|
+
const rightMatch = lookahead.exec(text);
|
|
117
|
+
if (rightMatch &&
|
|
118
|
+
rightMatch[0].includes('>') &&
|
|
119
|
+
!rightMatch[0].includes('<')) {
|
|
120
|
+
const middleText = text.slice(arrowEnd, rightMatch.index).trim();
|
|
121
|
+
if (middleText) {
|
|
122
|
+
const splitLabel = normalizeLabel(middleText);
|
|
123
|
+
return {
|
|
124
|
+
label: splitLabel || label,
|
|
125
|
+
direction: 'both',
|
|
126
|
+
reverse: false,
|
|
127
|
+
nextIndex: rightMatch.index + rightMatch[0].length,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
109
133
|
index = skipSpaces(text, index);
|
|
110
134
|
if (text[index] === '|') {
|
|
111
135
|
const pipeEnd = text.indexOf('|', index + 1);
|
package/lib/syntax/schema.js
CHANGED
|
@@ -53,6 +53,7 @@ exports.RelationSchema = object({
|
|
|
53
53
|
direction: enumOf(['forward', 'both', 'none']),
|
|
54
54
|
showArrow: enumOf(['true', 'false']),
|
|
55
55
|
arrowType: enumOf(['arrow', 'triangle', 'diamond']),
|
|
56
|
+
lineStyle: enumOf(['solid', 'dashed']),
|
|
56
57
|
}, { allowUnknown: true });
|
|
57
58
|
exports.ThemeSchema = object({
|
|
58
59
|
type: string(),
|
|
@@ -8,9 +8,10 @@ const hierarchy_tree_1 = require("./hierarchy-tree");
|
|
|
8
8
|
const list_zigzag_1 = require("./list-zigzag");
|
|
9
9
|
const registry_1 = require("./registry");
|
|
10
10
|
const relation_dagre_flow_1 = require("./relation-dagre-flow");
|
|
11
|
+
const sequence_interaction_1 = require("./sequence-interaction");
|
|
11
12
|
const sequence_stairs_1 = require("./sequence-stairs");
|
|
12
13
|
const word_cloud_1 = require("./word-cloud");
|
|
13
|
-
const BUILT_IN_TEMPLATES = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ 'compare-hierarchy-left-right-circle-node-pill-badge': {
|
|
14
|
+
const BUILT_IN_TEMPLATES = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ 'compare-hierarchy-left-right-circle-node-pill-badge': {
|
|
14
15
|
design: {
|
|
15
16
|
structure: {
|
|
16
17
|
type: 'compare-hierarchy-left-right',
|
|
@@ -650,7 +651,7 @@ const BUILT_IN_TEMPLATES = Object.assign(Object.assign(Object.assign(Object.assi
|
|
|
650
651
|
},
|
|
651
652
|
],
|
|
652
653
|
},
|
|
653
|
-
} }, chart_pie_1.chartPieTemplates), compare_quadrant_1.compareQuadrantTemplates), hierarchy_tree_1.hierarchyTreeTemplates), hierarchy_mindmap_1.hierarchyMindmapTemplates), sequence_stairs_1.sequenceStairsTemplates), word_cloud_1.wordCloudTemplate), list_zigzag_1.listZigzagTemplates), relation_dagre_flow_1.relationDagreFlowTemplates), hierarchy_structure_1.hierarchyStructureTemplates);
|
|
654
|
+
} }, chart_pie_1.chartPieTemplates), compare_quadrant_1.compareQuadrantTemplates), hierarchy_tree_1.hierarchyTreeTemplates), hierarchy_mindmap_1.hierarchyMindmapTemplates), sequence_stairs_1.sequenceStairsTemplates), word_cloud_1.wordCloudTemplate), list_zigzag_1.listZigzagTemplates), relation_dagre_flow_1.relationDagreFlowTemplates), sequence_interaction_1.sequenceInteractionTemplates), hierarchy_structure_1.hierarchyStructureTemplates);
|
|
654
655
|
Object.entries(BUILT_IN_TEMPLATES).forEach(([name, options]) => {
|
|
655
656
|
(0, registry_1.registerTemplate)(name, options);
|
|
656
657
|
});
|