@antv/infographic 0.2.7 → 0.2.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.
Files changed (142) hide show
  1. package/dist/infographic.min.js +191 -191
  2. package/dist/infographic.min.js.map +1 -1
  3. package/esm/designs/items/BadgeCard.js +6 -1
  4. package/esm/designs/items/SimpleCircleNode.d.ts +8 -0
  5. package/esm/designs/items/SimpleCircleNode.js +14 -0
  6. package/esm/designs/items/index.d.ts +1 -0
  7. package/esm/designs/items/index.js +1 -0
  8. package/esm/designs/structures/hierarchy-mindmap.js +19 -5
  9. package/esm/designs/structures/hierarchy-tree.d.ts +2 -1
  10. package/esm/designs/structures/hierarchy-tree.js +23 -20
  11. package/esm/designs/structures/index.d.ts +1 -0
  12. package/esm/designs/structures/index.js +1 -0
  13. package/esm/designs/structures/relation-dagre-flow.d.ts +21 -0
  14. package/esm/designs/structures/relation-dagre-flow.js +497 -0
  15. package/esm/designs/utils/hierarchy-color.d.ts +1 -1
  16. package/esm/editor/plugins/edit-bar/edit-bar.js +27 -9
  17. package/esm/index.js +1 -1
  18. package/esm/jsx/global.d.ts +1 -0
  19. package/esm/jsx/types/element.d.ts +5 -1
  20. package/esm/jsx/utils/svg.js +2 -0
  21. package/esm/renderer/composites/icon.js +2 -0
  22. package/esm/renderer/composites/illus.d.ts +1 -1
  23. package/esm/renderer/composites/illus.js +9 -4
  24. package/esm/renderer/composites/text.js +4 -2
  25. package/esm/renderer/fonts/loader.js +3 -1
  26. package/esm/renderer/fonts/registry.js +1 -1
  27. package/esm/renderer/renderer.js +28 -25
  28. package/esm/resource/loader.js +3 -1
  29. package/esm/runtime/Infographic.js +1 -1
  30. package/esm/ssr/dom-shim.d.ts +4 -0
  31. package/esm/ssr/dom-shim.js +107 -0
  32. package/esm/ssr/index.d.ts +1 -0
  33. package/esm/ssr/index.js +1 -0
  34. package/esm/ssr/renderer.d.ts +2 -0
  35. package/esm/ssr/renderer.js +60 -0
  36. package/esm/syntax/index.js +57 -1
  37. package/esm/syntax/parser.js +44 -0
  38. package/esm/syntax/relations.d.ts +6 -0
  39. package/esm/syntax/relations.js +251 -0
  40. package/esm/syntax/schema.d.ts +1 -0
  41. package/esm/syntax/schema.js +12 -0
  42. package/esm/templates/built-in.js +2 -0
  43. package/esm/templates/relation-dagre-flow.d.ts +2 -0
  44. package/esm/templates/relation-dagre-flow.js +68 -0
  45. package/esm/types/data.d.ts +24 -3
  46. package/esm/utils/data.js +1 -1
  47. package/esm/utils/index.d.ts +1 -0
  48. package/esm/utils/index.js +1 -0
  49. package/esm/utils/is-browser.js +5 -9
  50. package/esm/utils/measure-text.d.ts +2 -2
  51. package/esm/utils/measure-text.js +4 -4
  52. package/esm/utils/recognizer.js +8 -5
  53. package/esm/utils/text.js +27 -19
  54. package/lib/designs/items/BadgeCard.js +6 -1
  55. package/lib/designs/items/SimpleCircleNode.d.ts +8 -0
  56. package/lib/designs/items/SimpleCircleNode.js +18 -0
  57. package/lib/designs/items/index.d.ts +1 -0
  58. package/lib/designs/items/index.js +1 -0
  59. package/lib/designs/structures/hierarchy-mindmap.js +19 -5
  60. package/lib/designs/structures/hierarchy-tree.d.ts +2 -1
  61. package/lib/designs/structures/hierarchy-tree.js +23 -20
  62. package/lib/designs/structures/index.d.ts +1 -0
  63. package/lib/designs/structures/index.js +1 -0
  64. package/lib/designs/structures/relation-dagre-flow.d.ts +21 -0
  65. package/lib/designs/structures/relation-dagre-flow.js +501 -0
  66. package/lib/designs/utils/hierarchy-color.d.ts +1 -1
  67. package/lib/editor/plugins/edit-bar/edit-bar.js +27 -9
  68. package/lib/jsx/global.d.ts +1 -0
  69. package/lib/jsx/types/element.d.ts +5 -1
  70. package/lib/jsx/utils/svg.js +2 -0
  71. package/lib/renderer/composites/icon.js +2 -0
  72. package/lib/renderer/composites/illus.d.ts +1 -1
  73. package/lib/renderer/composites/illus.js +8 -3
  74. package/lib/renderer/composites/text.js +4 -2
  75. package/lib/renderer/fonts/loader.js +2 -0
  76. package/lib/renderer/fonts/registry.js +6 -6
  77. package/lib/renderer/renderer.js +27 -24
  78. package/lib/resource/loader.js +3 -1
  79. package/lib/runtime/Infographic.js +1 -1
  80. package/lib/ssr/dom-shim.d.ts +4 -0
  81. package/lib/ssr/dom-shim.js +110 -0
  82. package/lib/ssr/index.d.ts +1 -0
  83. package/lib/ssr/index.js +5 -0
  84. package/lib/ssr/renderer.d.ts +2 -0
  85. package/lib/ssr/renderer.js +63 -0
  86. package/lib/syntax/index.js +57 -1
  87. package/lib/syntax/parser.js +44 -0
  88. package/lib/syntax/relations.d.ts +6 -0
  89. package/lib/syntax/relations.js +254 -0
  90. package/lib/syntax/schema.d.ts +1 -0
  91. package/lib/syntax/schema.js +13 -1
  92. package/lib/templates/built-in.js +2 -0
  93. package/lib/templates/relation-dagre-flow.d.ts +2 -0
  94. package/lib/templates/relation-dagre-flow.js +71 -0
  95. package/lib/types/data.d.ts +24 -3
  96. package/lib/utils/data.js +2 -5
  97. package/lib/utils/index.d.ts +1 -0
  98. package/lib/utils/index.js +1 -0
  99. package/lib/utils/is-browser.js +5 -9
  100. package/lib/utils/measure-text.d.ts +2 -2
  101. package/lib/utils/measure-text.js +4 -4
  102. package/lib/utils/recognizer.js +8 -5
  103. package/lib/utils/text.js +28 -23
  104. package/package.json +19 -7
  105. package/src/designs/items/BadgeCard.tsx +9 -2
  106. package/src/designs/items/SimpleCircleNode.tsx +46 -0
  107. package/src/designs/items/index.ts +1 -0
  108. package/src/designs/structures/hierarchy-mindmap.tsx +15 -2
  109. package/src/designs/structures/hierarchy-tree.tsx +33 -31
  110. package/src/designs/structures/index.ts +1 -0
  111. package/src/designs/structures/relation-dagre-flow.tsx +782 -0
  112. package/src/designs/utils/hierarchy-color.ts +6 -1
  113. package/src/editor/plugins/edit-bar/edit-bar.ts +41 -17
  114. package/src/index.ts +1 -1
  115. package/src/jsx/global.ts +1 -0
  116. package/src/jsx/types/element.ts +15 -6
  117. package/src/jsx/utils/svg.ts +2 -0
  118. package/src/renderer/composites/icon.ts +2 -0
  119. package/src/renderer/composites/illus.ts +16 -3
  120. package/src/renderer/composites/text.ts +7 -2
  121. package/src/renderer/fonts/loader.ts +7 -1
  122. package/src/renderer/fonts/registry.ts +1 -1
  123. package/src/renderer/renderer.ts +42 -24
  124. package/src/resource/loader.ts +3 -1
  125. package/src/runtime/Infographic.tsx +1 -1
  126. package/src/ssr/dom-shim.ts +120 -0
  127. package/src/ssr/index.ts +1 -0
  128. package/src/ssr/renderer.ts +72 -0
  129. package/src/syntax/index.ts +58 -1
  130. package/src/syntax/parser.ts +49 -0
  131. package/src/syntax/relations.ts +291 -0
  132. package/src/syntax/schema.ts +16 -0
  133. package/src/templates/built-in.ts +4 -2
  134. package/src/templates/relation-dagre-flow.ts +73 -0
  135. package/src/types/data.ts +26 -3
  136. package/src/utils/data.ts +1 -1
  137. package/src/utils/index.ts +1 -0
  138. package/src/utils/is-browser.ts +3 -9
  139. package/src/utils/measure-text.ts +6 -7
  140. package/src/utils/recognizer.ts +9 -5
  141. package/src/utils/svg.ts +0 -1
  142. package/src/utils/text.ts +25 -19
@@ -0,0 +1,501 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RelationDagreFlow = void 0;
4
+ const jsx_runtime_1 = require("@antv/infographic/jsx-runtime");
5
+ const layout_1 = require("@antv/layout");
6
+ const jsx_1 = require("../../jsx");
7
+ const components_1 = require("../components");
8
+ const layouts_1 = require("../layouts");
9
+ const utils_1 = require("../utils");
10
+ const registry_1 = require("./registry");
11
+ const DEFAULT_NODE_SEP = 50;
12
+ const DEFAULT_RANK_SEP = 70;
13
+ const DEFAULT_EDGE_SEP = 10;
14
+ const DEFAULT_EDGE_WIDTH = 2;
15
+ const DEFAULT_PADDING = 30;
16
+ const checkUndirectedCycle = (nodeIds, edges) => {
17
+ const adj = new Map();
18
+ nodeIds.forEach((id) => adj.set(id, []));
19
+ for (const edge of edges) {
20
+ if (edge.source === edge.target)
21
+ return true;
22
+ adj.get(edge.source)?.push({ target: edge.target, edgeId: edge.id });
23
+ adj.get(edge.target)?.push({ target: edge.source, edgeId: edge.id });
24
+ }
25
+ const visited = new Set();
26
+ const dfs = (u, parentEdgeId) => {
27
+ visited.add(u);
28
+ const neighbors = adj.get(u) || [];
29
+ for (const { target: v, edgeId } of neighbors) {
30
+ if (edgeId === parentEdgeId)
31
+ continue;
32
+ if (visited.has(v))
33
+ return true;
34
+ if (dfs(v, edgeId))
35
+ return true;
36
+ }
37
+ return false;
38
+ };
39
+ for (const node of nodeIds) {
40
+ if (!visited.has(node)) {
41
+ if (dfs(node, null))
42
+ return true;
43
+ }
44
+ }
45
+ return false;
46
+ };
47
+ const RelationDagreFlow = (props) => {
48
+ const { Title, Item, data, rankdir = 'TB', nodesep = DEFAULT_NODE_SEP, ranksep = DEFAULT_RANK_SEP, edgesep = DEFAULT_EDGE_SEP, edgeWidth = DEFAULT_EDGE_WIDTH, showConnections = true, edgeColorMode = 'gradient', edgeStyle = 'solid', edgeDashPattern = '5,5', edgeCornerRadius = 12, edgeRouting = 'orth', showArrow = true, arrowType = 'triangle', padding = DEFAULT_PADDING, edgeAnimation = 'none', edgeAnimationSpeed = 1, options, } = props;
49
+ const { title, desc, items = [] } = data;
50
+ const titleContent = Title ? (0, jsx_runtime_1.jsx)(Title, { title: title, desc: desc }) : null;
51
+ if (!Item || items.length === 0) {
52
+ return ((0, jsx_runtime_1.jsxs)(layouts_1.FlexLayout, { id: "infographic-container", flexDirection: "column", justifyContent: "center", alignItems: "center", children: [titleContent, (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: (0, jsx_runtime_1.jsx)(components_1.BtnAdd, { indexes: [0], x: 0, y: 0 }) })] }));
53
+ }
54
+ const nodeMetaMap = new Map();
55
+ const nodeSizeMap = new Map();
56
+ const nodeColorMap = new Map();
57
+ const nodeIdsByIndex = new Map();
58
+ const nodeIdSet = new Set();
59
+ const colorGroupIndexMap = new Map();
60
+ let nextColorGroupIndex = 0;
61
+ const nodes = items.map((item, index) => {
62
+ const datum = item;
63
+ const id = String(datum.id ?? index);
64
+ const indexes = [index];
65
+ let primary;
66
+ const groupKey = String(datum.group ?? '');
67
+ if (groupKey) {
68
+ let groupIndex = colorGroupIndexMap.get(groupKey);
69
+ if (groupIndex == null) {
70
+ groupIndex = nextColorGroupIndex;
71
+ colorGroupIndexMap.set(groupKey, groupIndex);
72
+ nextColorGroupIndex += 1;
73
+ }
74
+ primary = (0, utils_1.getPaletteColor)(options, [groupIndex]);
75
+ }
76
+ else {
77
+ primary = (0, utils_1.getPaletteColor)(options, indexes);
78
+ }
79
+ const themeColors = primary
80
+ ? (0, utils_1.getThemeColors)({ colorPrimary: primary }, options)
81
+ : undefined;
82
+ if (primary) {
83
+ nodeColorMap.set(id, primary);
84
+ }
85
+ const bounds = (0, jsx_1.getElementBounds)((0, jsx_runtime_1.jsx)(Item, { indexes: indexes, data: data, datum: datum, positionH: "center", positionV: "middle", themeColors: themeColors }));
86
+ nodeSizeMap.set(id, bounds);
87
+ nodeMetaMap.set(id, { id, indexes, datum, themeColors });
88
+ nodeIdsByIndex.set(index, id);
89
+ nodeIdSet.add(id);
90
+ return { id, parentId: datum.parentId };
91
+ });
92
+ const relations = data.relations ?? [];
93
+ const resolveNodeId = (value) => {
94
+ if (value == null)
95
+ return null;
96
+ const direct = String(value);
97
+ if (nodeIdSet.has(direct))
98
+ return direct;
99
+ const asIndex = Number(value);
100
+ if (!Number.isNaN(asIndex)) {
101
+ const mapped = nodeIdsByIndex.get(asIndex);
102
+ if (mapped)
103
+ return mapped;
104
+ }
105
+ return null;
106
+ };
107
+ const edges = relations
108
+ .map((relation, index) => {
109
+ const source = resolveNodeId(relation.from);
110
+ const target = resolveNodeId(relation.to);
111
+ if (!source || !target)
112
+ return null;
113
+ return {
114
+ id: relation.id ? String(relation.id) : `edge-${index}`,
115
+ source,
116
+ target,
117
+ relation,
118
+ };
119
+ })
120
+ .filter(Boolean);
121
+ const hasCycle = checkUndirectedCycle(Array.from(nodeIdSet), edges);
122
+ const finalEdgeRouting = hasCycle ? 'dagre' : edgeRouting;
123
+ const layout = new layout_1.DagreLayout({
124
+ rankdir,
125
+ nodesep,
126
+ ranksep,
127
+ edgesep,
128
+ controlPoints: true,
129
+ nodeSize: (node) => {
130
+ const id = String(node.id ?? '');
131
+ const bounds = nodeSizeMap.get(id);
132
+ return bounds ? [bounds.width, bounds.height] : [0, 0];
133
+ },
134
+ });
135
+ layout.execute({ nodes, edges });
136
+ const nodeLayouts = [];
137
+ layout.forEachNode((node) => {
138
+ const id = String(node.id);
139
+ const meta = nodeMetaMap.get(id);
140
+ if (!meta)
141
+ return;
142
+ const bounds = nodeSizeMap.get(id);
143
+ const width = bounds?.width ?? 0;
144
+ const height = bounds?.height ?? 0;
145
+ const x = (node.x ?? 0) - width / 2;
146
+ const y = (node.y ?? 0) - height / 2;
147
+ nodeLayouts.push({
148
+ ...meta,
149
+ x,
150
+ y,
151
+ width,
152
+ height,
153
+ centerX: x + width / 2,
154
+ centerY: y + height / 2,
155
+ });
156
+ });
157
+ if (nodeLayouts.length === 0) {
158
+ return ((0, jsx_runtime_1.jsxs)(layouts_1.FlexLayout, { id: "infographic-container", flexDirection: "column", justifyContent: "center", alignItems: "center", children: [titleContent, (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: (0, jsx_runtime_1.jsx)(components_1.BtnAdd, { indexes: [0], x: 0, y: 0 }) })] }));
159
+ }
160
+ const minX = Math.min(...nodeLayouts.map((node) => node.x));
161
+ const minY = Math.min(...nodeLayouts.map((node) => node.y));
162
+ const offsetX = padding - minX;
163
+ const offsetY = padding - minY;
164
+ const nodeLayoutById = new Map();
165
+ const itemElements = [];
166
+ nodeLayouts.forEach((node) => {
167
+ const displayX = node.x + offsetX;
168
+ const displayY = node.y + offsetY;
169
+ const positionH = rankdir === 'LR' ? 'normal' : rankdir === 'RL' ? 'flipped' : 'center';
170
+ const positionV = rankdir === 'TB' ? 'normal' : rankdir === 'BT' ? 'flipped' : 'middle';
171
+ itemElements.push((0, jsx_runtime_1.jsx)(Item, { indexes: node.indexes, datum: node.datum, data: data, x: displayX, y: displayY, positionH: positionH, positionV: positionV, themeColors: node.themeColors }));
172
+ nodeLayoutById.set(node.id, {
173
+ ...node,
174
+ x: displayX,
175
+ y: displayY,
176
+ centerX: displayX + node.width / 2,
177
+ centerY: displayY + node.height / 2,
178
+ });
179
+ });
180
+ const defsElements = [];
181
+ const decorElements = [];
182
+ if (showConnections) {
183
+ const defaultStroke = (0, utils_1.getColorPrimary)(options);
184
+ const themeColors = (0, utils_1.getThemeColors)(options.themeConfig, options);
185
+ const labelBackground = themeColors?.colorBg ?? '#ffffff';
186
+ const labelTextColor = themeColors?.colorText ?? defaultStroke;
187
+ const arrowSize = Math.max(10, edgeWidth * 4);
188
+ const isVertical = rankdir === 'TB' || rankdir === 'BT';
189
+ const enableAnimation = edgeAnimation === 'ant-line';
190
+ const animationDashArray = enableAnimation ? edgeDashPattern : '';
191
+ const staticDashArray = !enableAnimation && edgeStyle === 'dashed' ? edgeDashPattern : '';
192
+ const actualDashArray = enableAnimation
193
+ ? animationDashArray
194
+ : staticDashArray;
195
+ const dashPatternLength = enableAnimation
196
+ ? animationDashArray
197
+ .split(',')
198
+ .reduce((sum, val) => sum + parseFloat(val.trim() || '0'), 0)
199
+ : 0;
200
+ const animationDuration = enableAnimation && dashPatternLength > 0
201
+ ? `${dashPatternLength / (edgeAnimationSpeed * 10)}s`
202
+ : '1s';
203
+ const straightCornerRadius = edgeCornerRadius;
204
+ const createStraightPath = (points, dx, dy) => points
205
+ .map(([x, y], index) => {
206
+ const prefix = index === 0 ? 'M' : 'L';
207
+ return `${prefix} ${x + dx} ${y + dy}`;
208
+ })
209
+ .join(' ');
210
+ const createRoundedPath = (points, radius, dx, dy) => {
211
+ if (points.length < 2)
212
+ return '';
213
+ const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
214
+ const toPoint = ([x, y]) => ({
215
+ x: x + dx,
216
+ y: y + dy,
217
+ });
218
+ const output = [];
219
+ const first = toPoint(points[0]);
220
+ output.push(`M ${first.x} ${first.y}`);
221
+ if (points.length === 2) {
222
+ const last = toPoint(points[1]);
223
+ output.push(`L ${last.x} ${last.y}`);
224
+ return output.join(' ');
225
+ }
226
+ for (let i = 1; i < points.length - 1; i += 1) {
227
+ const prev = points[i - 1];
228
+ const curr = points[i];
229
+ const next = points[i + 1];
230
+ const v0x = curr[0] - prev[0];
231
+ const v0y = curr[1] - prev[1];
232
+ const v1x = next[0] - curr[0];
233
+ const v1y = next[1] - curr[1];
234
+ const d0 = Math.hypot(v0x, v0y);
235
+ const d1 = Math.hypot(v1x, v1y);
236
+ if (d0 === 0 || d1 === 0) {
237
+ const currPoint = toPoint(curr);
238
+ output.push(`L ${currPoint.x} ${currPoint.y}`);
239
+ continue;
240
+ }
241
+ const r = clamp(radius, 0, Math.min(d0, d1) / 2);
242
+ if (r === 0) {
243
+ const currPoint = toPoint(curr);
244
+ output.push(`L ${currPoint.x} ${currPoint.y}`);
245
+ continue;
246
+ }
247
+ const u0x = v0x / d0;
248
+ const u0y = v0y / d0;
249
+ const u1x = v1x / d1;
250
+ const u1y = v1y / d1;
251
+ const start = toPoint([curr[0] - u0x * r, curr[1] - u0y * r]);
252
+ const end = toPoint([curr[0] + u1x * r, curr[1] + u1y * r]);
253
+ output.push(`L ${start.x} ${start.y}`);
254
+ const currPoint = toPoint(curr);
255
+ output.push(`Q ${currPoint.x} ${currPoint.y} ${end.x} ${end.y}`);
256
+ }
257
+ const last = toPoint(points[points.length - 1]);
258
+ output.push(`L ${last.x} ${last.y}`);
259
+ return output.join(' ');
260
+ };
261
+ const createArrowElements = (x, y, angle, type, fillColor) => {
262
+ const ux = Math.cos(angle);
263
+ const uy = Math.sin(angle);
264
+ const px = -uy;
265
+ const py = ux;
266
+ const length = arrowSize;
267
+ const halfWidth = arrowSize * 0.55;
268
+ if (type === 'arrow') {
269
+ const leftX = x - ux * length + px * halfWidth;
270
+ const leftY = y - uy * length + py * halfWidth;
271
+ const rightX = x - ux * length - px * halfWidth;
272
+ const rightY = y - uy * length - py * halfWidth;
273
+ return [
274
+ (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" }),
275
+ ];
276
+ }
277
+ if (type === 'diamond') {
278
+ const diamondLength = length * 1.25;
279
+ const diamondWidth = halfWidth * 0.75;
280
+ const midX = x - ux * diamondLength * 0.5;
281
+ const midY = y - uy * diamondLength * 0.5;
282
+ const diamondPoints = [
283
+ { x, y },
284
+ { x: midX + px * diamondWidth, y: midY + py * diamondWidth },
285
+ { x: x - ux * diamondLength, y: y - uy * diamondLength },
286
+ { x: midX - px * diamondWidth, y: midY - py * diamondWidth },
287
+ ];
288
+ return [
289
+ (0, jsx_runtime_1.jsx)(jsx_1.Polygon, { points: diamondPoints, fill: fillColor, stroke: fillColor, strokeWidth: Math.max(1, edgeWidth * 0.8) }),
290
+ ];
291
+ }
292
+ const trianglePoints = [
293
+ { x, y },
294
+ {
295
+ x: x - ux * length + px * halfWidth,
296
+ y: y - uy * length + py * halfWidth,
297
+ },
298
+ {
299
+ x: x - ux * length - px * halfWidth,
300
+ y: y - uy * length - py * halfWidth,
301
+ },
302
+ ];
303
+ return [
304
+ (0, jsx_runtime_1.jsx)(jsx_1.Polygon, { points: trianglePoints, fill: fillColor, stroke: fillColor, strokeWidth: Math.max(1, edgeWidth * 0.8) }),
305
+ ];
306
+ };
307
+ const getMidPoint = (points) => {
308
+ if (points.length === 0)
309
+ return null;
310
+ if (points.length === 1)
311
+ return points[0];
312
+ let total = 0;
313
+ const segments = [];
314
+ for (let i = 0; i < points.length - 1; i += 1) {
315
+ const start = points[i];
316
+ const end = points[i + 1];
317
+ const length = Math.hypot(end[0] - start[0], end[1] - start[1]);
318
+ segments.push({ length, start, end });
319
+ total += length;
320
+ }
321
+ if (total === 0)
322
+ return points[0];
323
+ let target = total / 2;
324
+ for (let i = 0; i < segments.length; i += 1) {
325
+ const segment = segments[i];
326
+ if (target <= segment.length || i === segments.length - 1) {
327
+ const ratio = segment.length === 0
328
+ ? 0
329
+ : Math.max(0, Math.min(1, target / segment.length));
330
+ return [
331
+ segment.start[0] + (segment.end[0] - segment.start[0]) * ratio,
332
+ segment.start[1] + (segment.end[1] - segment.start[1]) * ratio,
333
+ ];
334
+ }
335
+ target -= segment.length;
336
+ }
337
+ return points[Math.floor(points.length / 2)];
338
+ };
339
+ const getOrthEdgeEndpoints = (sourceId, targetId) => {
340
+ const source = nodeLayoutById.get(sourceId);
341
+ const target = nodeLayoutById.get(targetId);
342
+ if (!source || !target)
343
+ return null;
344
+ if (rankdir === 'TB') {
345
+ return {
346
+ start: [source.centerX, source.y + source.height],
347
+ end: [target.centerX, target.y],
348
+ };
349
+ }
350
+ if (rankdir === 'BT') {
351
+ return {
352
+ start: [source.centerX, source.y],
353
+ end: [target.centerX, target.y + target.height],
354
+ };
355
+ }
356
+ if (rankdir === 'LR') {
357
+ return {
358
+ start: [source.x + source.width, source.centerY],
359
+ end: [target.x, target.centerY],
360
+ };
361
+ }
362
+ return {
363
+ start: [source.x, source.centerY],
364
+ end: [target.x + target.width, target.centerY],
365
+ };
366
+ };
367
+ const getOrthEdgePoints = (sourceId, targetId) => {
368
+ const endpoints = getOrthEdgeEndpoints(sourceId, targetId);
369
+ if (!endpoints)
370
+ return null;
371
+ const { start, end } = endpoints;
372
+ if (isVertical) {
373
+ const midY = start[1] + (end[1] - start[1]) / 2;
374
+ return {
375
+ start,
376
+ end,
377
+ points: [start, [start[0], midY], [end[0], midY], end],
378
+ };
379
+ }
380
+ const midX = start[0] + (end[0] - start[0]) / 2;
381
+ return {
382
+ start,
383
+ end,
384
+ points: [start, [midX, start[1]], [midX, end[1]], end],
385
+ };
386
+ };
387
+ layout.forEachEdge((edge) => {
388
+ const normalizePoints = (rawPoints) => {
389
+ if (!Array.isArray(rawPoints))
390
+ return [];
391
+ return rawPoints
392
+ .map((point) => {
393
+ if (!point)
394
+ return null;
395
+ if (Array.isArray(point) && point.length >= 2) {
396
+ return [Number(point[0]), Number(point[1])];
397
+ }
398
+ return null;
399
+ })
400
+ .filter((point) => !!point && Number.isFinite(point[0]) && Number.isFinite(point[1]));
401
+ };
402
+ const fallbackPoints = () => {
403
+ const source = nodeLayoutById.get(String(edge.source));
404
+ const target = nodeLayoutById.get(String(edge.target));
405
+ if (!source || !target)
406
+ return [];
407
+ return [
408
+ [source.centerX - offsetX, source.centerY - offsetY],
409
+ [target.centerX - offsetX, target.centerY - offsetY],
410
+ ];
411
+ };
412
+ const useOrthRouting = finalEdgeRouting === 'orth';
413
+ const orthEdge = useOrthRouting
414
+ ? getOrthEdgePoints(String(edge.source), String(edge.target))
415
+ : null;
416
+ const normalized = useOrthRouting ? [] : normalizePoints(edge.points);
417
+ const points = useOrthRouting
418
+ ? (orthEdge?.points ?? [])
419
+ : normalized.length
420
+ ? normalized
421
+ : fallbackPoints();
422
+ if (!points.length)
423
+ return;
424
+ const pointsOffsetX = useOrthRouting ? 0 : offsetX;
425
+ const pointsOffsetY = useOrthRouting ? 0 : offsetY;
426
+ const startPoint = useOrthRouting
427
+ ? (orthEdge?.start ?? points[0])
428
+ : points[0];
429
+ const endPoint = useOrthRouting
430
+ ? (orthEdge?.end ?? points[points.length - 1])
431
+ : points[points.length - 1];
432
+ const relation = edge
433
+ ._original?.relation;
434
+ const sourceColor = nodeColorMap.get(String(edge.source)) ?? defaultStroke;
435
+ const targetColor = nodeColorMap.get(String(edge.target)) ?? defaultStroke;
436
+ const gradientKey = `edge-gradient-${String(sourceColor)}-${String(targetColor)}`.replace(/[^a-zA-Z0-9_-]/g, '');
437
+ const edgeStroke = edgeColorMode === 'gradient' ? `url(#${gradientKey})` : defaultStroke;
438
+ let pathD = '';
439
+ if (straightCornerRadius > 0) {
440
+ pathD = createRoundedPath(points, straightCornerRadius, pointsOffsetX, pointsOffsetY);
441
+ }
442
+ else {
443
+ pathD = createStraightPath(points, pointsOffsetX, pointsOffsetY);
444
+ }
445
+ if (!pathD)
446
+ return;
447
+ const pathElement = ((0, jsx_runtime_1.jsx)(jsx_1.Path, { d: pathD, stroke: edgeStroke, strokeWidth: edgeWidth, strokeDasharray: actualDashArray, fill: "none", "data-element-type": "shape", children: enableAnimation && ((0, jsx_runtime_1.jsx)("animate", { attributeName: "stroke-dashoffset", from: String(dashPatternLength), to: "0", dur: animationDuration, repeatCount: "indefinite" })) }));
448
+ decorElements.push(pathElement);
449
+ if (edgeColorMode === 'gradient') {
450
+ const start = startPoint;
451
+ const end = endPoint;
452
+ defsElements.push((0, jsx_runtime_1.jsxs)("linearGradient", { id: gradientKey, gradientUnits: "userSpaceOnUse", x1: start[0] + pointsOffsetX, y1: start[1] + pointsOffsetY, x2: end[0] + pointsOffsetX, y2: end[1] + pointsOffsetY, children: [(0, jsx_runtime_1.jsx)("stop", { offset: "0%", stopColor: sourceColor }), (0, jsx_runtime_1.jsx)("stop", { offset: "100%", stopColor: targetColor })] }));
453
+ }
454
+ if (relation?.label) {
455
+ let labelPoint = null;
456
+ const midPoint = getMidPoint(points);
457
+ if (midPoint) {
458
+ labelPoint = [
459
+ midPoint[0] + pointsOffsetX,
460
+ midPoint[1] + pointsOffsetY,
461
+ ];
462
+ }
463
+ if (labelPoint) {
464
+ const labelText = String(relation.label);
465
+ const labelBounds = (0, jsx_1.getElementBounds)((0, jsx_runtime_1.jsx)(jsx_1.Text, { fontSize: 14, fontWeight: "normal", children: labelText }));
466
+ const labelX = labelPoint[0] - labelBounds.width / 2;
467
+ const labelY = labelPoint[1] - labelBounds.height / 2;
468
+ decorElements.push((0, jsx_runtime_1.jsx)(jsx_1.Text, { x: labelX, y: labelY, width: labelBounds.width, height: labelBounds.height, fontSize: 14, fontWeight: "normal", alignHorizontal: "center", alignVertical: "middle", fill: labelTextColor, backgroundColor: labelBackground, children: labelText }));
469
+ }
470
+ }
471
+ const effectiveShowArrow = relation?.showArrow ?? showArrow;
472
+ const direction = relation?.direction ?? 'forward';
473
+ const edgeArrowType = relation?.arrowType ?? arrowType;
474
+ const lastIndex = points.length - 1;
475
+ if (effectiveShowArrow && points.length > 1) {
476
+ if (direction === 'forward' || direction === 'both') {
477
+ const head = points[lastIndex];
478
+ const tail = points[lastIndex - 1];
479
+ const angle = Math.atan2(head[1] - tail[1], head[0] - tail[0]);
480
+ const arrowFill = edgeColorMode === 'gradient' ? targetColor : defaultStroke;
481
+ const arrowElements = createArrowElements(head[0] + pointsOffsetX, head[1] + pointsOffsetY, angle, edgeArrowType, arrowFill);
482
+ decorElements.push(...arrowElements);
483
+ }
484
+ if (direction === 'both') {
485
+ const head = points[0];
486
+ const tail = points[1];
487
+ const angle = Math.atan2(head[1] - tail[1], head[0] - tail[0]);
488
+ const arrowFill = edgeColorMode === 'gradient' ? sourceColor : defaultStroke;
489
+ const arrowElements = createArrowElements(head[0] + pointsOffsetX, head[1] + pointsOffsetY, angle, edgeArrowType, arrowFill);
490
+ decorElements.push(...arrowElements);
491
+ }
492
+ }
493
+ });
494
+ }
495
+ return ((0, jsx_runtime_1.jsxs)(layouts_1.FlexLayout, { id: "infographic-container", flexDirection: "column", justifyContent: "center", alignItems: "center", children: [titleContent, (0, jsx_runtime_1.jsxs)(jsx_1.Group, { children: [(0, jsx_runtime_1.jsx)(jsx_1.Defs, { children: defsElements }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { width: 0, height: 0, children: decorElements }), (0, jsx_runtime_1.jsx)(components_1.ItemsGroup, { children: itemElements }), (0, jsx_runtime_1.jsx)(components_1.BtnsGroup, {})] })] }));
496
+ };
497
+ exports.RelationDagreFlow = RelationDagreFlow;
498
+ (0, registry_1.registerStructure)('relation-dagre-flow', {
499
+ component: exports.RelationDagreFlow,
500
+ composites: ['title', 'item'],
501
+ });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 层级结构着色模式类型
3
3
  */
4
- export type HierarchyColorMode = 'level' | 'branch' | 'node' | 'node-flat';
4
+ export type HierarchyColorMode = 'level' | 'branch' | 'node' | 'node-flat' | 'group';
5
5
  /**
6
6
  * 层级节点信息接口
7
7
  */
@@ -4,7 +4,6 @@ exports.EditBar = void 0;
4
4
  const constants_1 = require("../../../constants");
5
5
  const jsx_1 = require("../../../jsx");
6
6
  const utils_1 = require("../../../utils");
7
- const utils_2 = require("../../utils");
8
7
  const base_1 = require("../base");
9
8
  const edit_items_1 = require("./edit-items");
10
9
  class EditBar extends base_1.Plugin {
@@ -174,24 +173,43 @@ class EditBar extends base_1.Plugin {
174
173
  placeEditBar(container, selection) {
175
174
  if (selection.length === 0)
176
175
  return;
177
- const svg = this.editor.getDocument();
178
- const combinedBounds = (0, jsx_1.getCombinedBounds)(selection.map((element) => (0, utils_2.getElementViewportBounds)(svg, element)));
176
+ const combinedBounds = (0, jsx_1.getCombinedBounds)(selection.map((element) => element.getBoundingClientRect()));
179
177
  const offsetParent = container.offsetParent ??
180
178
  document.documentElement;
181
- const parentRect = offsetParent.getBoundingClientRect();
182
179
  const viewportHeight = document.documentElement.clientHeight;
180
+ const viewportWidth = document.documentElement.clientWidth;
183
181
  const containerRect = container.getBoundingClientRect();
184
182
  const offset = 8;
185
- const matrix = (0, utils_2.getScreenCTM)(svg);
186
- const anchorTop = new DOMPoint(combinedBounds.x + combinedBounds.width / 2, combinedBounds.y).matrixTransform(matrix);
187
- const anchorBottom = new DOMPoint(combinedBounds.x + combinedBounds.width / 2, combinedBounds.y + combinedBounds.height).matrixTransform(matrix);
183
+ const anchorTop = {
184
+ x: combinedBounds.x + combinedBounds.width / 2,
185
+ y: combinedBounds.y,
186
+ };
187
+ const anchorBottom = {
188
+ x: anchorTop.x,
189
+ y: combinedBounds.y + combinedBounds.height,
190
+ };
188
191
  const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
189
- let left = anchorTop.x - parentRect.left - containerRect.width / 2;
190
- left = clamp(left, 0, Math.max(parentRect.width - containerRect.width, 0));
191
192
  // Use viewport space, not container space, to decide whether we have enough room above.
192
193
  const spaceAbove = anchorTop.y - offset;
193
194
  const spaceBelow = viewportHeight - anchorBottom.y - offset;
194
195
  const shouldPlaceAbove = spaceAbove >= containerRect.height || spaceAbove >= spaceBelow;
196
+ if (offsetParent === document.body ||
197
+ offsetParent === document.documentElement) {
198
+ const scrollX = window.scrollX || document.documentElement.scrollLeft;
199
+ const scrollY = window.scrollY || document.documentElement.scrollTop;
200
+ let left = scrollX + anchorTop.x - containerRect.width / 2;
201
+ left = clamp(left, scrollX, scrollX + Math.max(viewportWidth - containerRect.width, 0));
202
+ let top = shouldPlaceAbove
203
+ ? scrollY + anchorTop.y - containerRect.height - offset
204
+ : scrollY + anchorBottom.y + offset;
205
+ top = clamp(top, scrollY, scrollY + Math.max(viewportHeight - containerRect.height, 0));
206
+ container.style.left = `${left}px`;
207
+ container.style.top = `${top}px`;
208
+ return;
209
+ }
210
+ const parentRect = offsetParent.getBoundingClientRect();
211
+ let left = anchorTop.x - parentRect.left - containerRect.width / 2;
212
+ left = clamp(left, 0, Math.max(parentRect.width - containerRect.width, 0));
195
213
  let top = shouldPlaceAbove
196
214
  ? anchorTop.y - parentRect.top - containerRect.height - offset
197
215
  : anchorBottom.y - parentRect.top + offset;
@@ -65,6 +65,7 @@ declare global {
65
65
  symbol: SVGAttributes<SVGSymbolElement>;
66
66
  text: SVGAttributes<SVGTextElement>;
67
67
  textPath: SVGAttributes<SVGTextPathElement>;
68
+ title: SVGAttributes<SVGTitleElement>;
68
69
  tspan: SVGAttributes<SVGTSpanElement>;
69
70
  use: SVGAttributes<SVGUseElement>;
70
71
  view: SVGAttributes<SVGViewElement>;
@@ -21,8 +21,10 @@ export interface GroupProps extends BaseGeometryProps {
21
21
  children?: JSXNode;
22
22
  }
23
23
  export interface RectProps extends BaseGeometryProps {
24
+ children?: JSXNode;
24
25
  }
25
26
  export interface EllipseProps extends BaseGeometryProps {
27
+ children?: JSXNode;
26
28
  }
27
29
  export interface TextProps extends BaseGeometryProps {
28
30
  lineHeight?: number;
@@ -32,10 +34,12 @@ export interface TextProps extends BaseGeometryProps {
32
34
  backgroundColor?: string;
33
35
  backgroundOpacity?: number;
34
36
  backgroundRadius?: number;
35
- children?: string | number;
37
+ children?: string | number | JSXNode;
36
38
  }
37
39
  export interface PathProps extends BaseGeometryProps {
40
+ children?: JSXNode;
38
41
  }
39
42
  export interface PolygonProps extends Omit<BaseGeometryProps, 'points'> {
40
43
  points?: Point[];
44
+ children?: JSXNode;
41
45
  }
@@ -39,6 +39,8 @@ const SPECIFIC_ATTRS_MAP = {
39
39
  textLength: 'textLength',
40
40
  lengthAdjust: 'lengthAdjust',
41
41
  // Animation
42
+ attributeName: 'attributeName',
43
+ attributeType: 'attributeType',
42
44
  repeatCount: 'repeatCount',
43
45
  repeatDur: 'repeatDur',
44
46
  calcMode: 'calcMode',
@@ -9,8 +9,10 @@ function renderItemIcon(svg, node, datum, options) {
9
9
  if (!value)
10
10
  return null;
11
11
  const { themeConfig } = options;
12
+ const dataAttrs = datum.attributes?.icon;
12
13
  const attrs = {
13
14
  ...themeConfig.item?.icon,
15
+ ...dataAttrs,
14
16
  };
15
17
  const parsedAttrs = (0, utils_2.parseDynamicAttributes)(node, attrs);
16
18
  return createIcon(svg, node, value, parsedAttrs, datum);
@@ -1,4 +1,4 @@
1
1
  import { type ResourceConfig } from '../../resource';
2
2
  import type { IllusElement, ItemDatum } from '../../types';
3
- export declare function renderIllus(svg: SVGSVGElement, node: SVGElement, value: string | ResourceConfig | undefined, datum?: ItemDatum): IllusElement | null;
3
+ export declare function renderIllus(svg: SVGSVGElement, node: SVGElement, value: string | ResourceConfig | undefined, datum?: ItemDatum, attrs?: Record<string, any>): IllusElement | null;
4
4
  export declare function renderItemIllus(svg: SVGSVGElement, node: SVGElement, datum: ItemDatum): SVGGElement | null;
@@ -4,25 +4,30 @@ exports.renderIllus = renderIllus;
4
4
  exports.renderItemIllus = renderItemIllus;
5
5
  const resource_1 = require("../../resource");
6
6
  const utils_1 = require("../../utils");
7
- function renderIllus(svg, node, value, datum) {
7
+ function renderIllus(svg, node, value, datum, attrs = {}) {
8
8
  if (!value)
9
9
  return null;
10
10
  const config = (0, resource_1.parseResourceConfig)(value);
11
11
  if (!config)
12
12
  return null;
13
13
  const id = (0, resource_1.getResourceId)(config);
14
+ if (attrs && Object.keys(attrs).length > 0) {
15
+ (0, utils_1.setAttributes)(node, attrs);
16
+ }
14
17
  const clipPathId = createClipPath(svg, node, id);
15
18
  (0, resource_1.loadResource)(svg, 'illus', config, datum);
16
19
  const { data, color } = config;
17
20
  return createIllusElement(id, {
18
21
  ...parseIllusBounds(node),
19
- 'clip-path': `url(#${clipPathId})`,
20
22
  ...(color ? { color } : {}),
23
+ ...attrs,
24
+ 'clip-path': `url(#${clipPathId})`,
21
25
  }, data);
22
26
  }
23
27
  function renderItemIllus(svg, node, datum) {
24
28
  const value = datum.illus;
25
- return renderIllus(svg, node, value, datum);
29
+ const attrs = datum.attributes?.illus;
30
+ return renderIllus(svg, node, value, datum, attrs);
26
31
  }
27
32
  function createClipPath(svg, node, id) {
28
33
  const clipPathId = `clip-${id}-${(0, utils_1.uuid)()}`;