@antv/infographic 0.2.17 → 0.2.19

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 (97) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/infographic.min.js +110 -110
  4. package/dist/infographic.min.js.map +1 -1
  5. package/esm/constants/service.d.ts +1 -1
  6. package/esm/constants/service.js +1 -1
  7. package/esm/designs/structures/chart-line.js +5 -3
  8. package/esm/editor/interactions/dblclick-edit-text.js +3 -3
  9. package/esm/editor/managers/interaction.js +6 -4
  10. package/esm/editor/plugins/components/button.d.ts +2 -1
  11. package/esm/editor/plugins/components/button.js +4 -4
  12. package/esm/editor/plugins/components/color-picker.d.ts +1 -0
  13. package/esm/editor/plugins/components/color-picker.js +3 -3
  14. package/esm/editor/plugins/components/popover.d.ts +3 -1
  15. package/esm/editor/plugins/components/popover.js +29 -9
  16. package/esm/editor/plugins/edit-bar/edit-bar.d.ts +3 -1
  17. package/esm/editor/plugins/edit-bar/edit-bar.js +17 -7
  18. package/esm/editor/plugins/edit-bar/edit-items/align-elements.js +6 -4
  19. package/esm/editor/plugins/edit-bar/edit-items/font-align.js +8 -5
  20. package/esm/editor/plugins/edit-bar/edit-items/font-color.js +7 -4
  21. package/esm/editor/plugins/edit-bar/edit-items/font-family.js +11 -9
  22. package/esm/editor/plugins/edit-bar/edit-items/font-size.js +8 -5
  23. package/esm/editor/plugins/edit-bar/edit-items/icon-color.js +7 -4
  24. package/esm/editor/plugins/edit-bar/edit-items/types.d.ts +5 -1
  25. package/esm/editor/plugins/reset-viewbox.d.ts +4 -1
  26. package/esm/editor/plugins/reset-viewbox.js +12 -6
  27. package/esm/editor/utils/index.d.ts +1 -0
  28. package/esm/editor/utils/index.js +1 -0
  29. package/esm/editor/utils/root.d.ts +3 -0
  30. package/esm/editor/utils/root.js +18 -0
  31. package/esm/exporter/svg.js +192 -52
  32. package/esm/resource/loaders/search.js +0 -3
  33. package/esm/templates/utils.js +11 -6
  34. package/esm/utils/padding.js +1 -1
  35. package/esm/utils/style.d.ts +3 -1
  36. package/esm/utils/style.js +27 -4
  37. package/esm/version.d.ts +1 -1
  38. package/esm/version.js +1 -1
  39. package/lib/constants/service.d.ts +1 -1
  40. package/lib/constants/service.js +1 -1
  41. package/lib/designs/structures/chart-line.js +5 -3
  42. package/lib/editor/interactions/dblclick-edit-text.js +3 -3
  43. package/lib/editor/managers/interaction.js +7 -5
  44. package/lib/editor/plugins/components/button.d.ts +2 -1
  45. package/lib/editor/plugins/components/button.js +4 -4
  46. package/lib/editor/plugins/components/color-picker.d.ts +1 -0
  47. package/lib/editor/plugins/components/color-picker.js +3 -3
  48. package/lib/editor/plugins/components/popover.d.ts +3 -1
  49. package/lib/editor/plugins/components/popover.js +32 -12
  50. package/lib/editor/plugins/edit-bar/edit-bar.d.ts +3 -1
  51. package/lib/editor/plugins/edit-bar/edit-bar.js +17 -7
  52. package/lib/editor/plugins/edit-bar/edit-items/align-elements.js +6 -4
  53. package/lib/editor/plugins/edit-bar/edit-items/font-align.js +8 -5
  54. package/lib/editor/plugins/edit-bar/edit-items/font-color.js +7 -4
  55. package/lib/editor/plugins/edit-bar/edit-items/font-family.js +11 -9
  56. package/lib/editor/plugins/edit-bar/edit-items/font-size.js +8 -5
  57. package/lib/editor/plugins/edit-bar/edit-items/icon-color.js +7 -4
  58. package/lib/editor/plugins/edit-bar/edit-items/types.d.ts +5 -1
  59. package/lib/editor/plugins/reset-viewbox.d.ts +4 -1
  60. package/lib/editor/plugins/reset-viewbox.js +12 -6
  61. package/lib/editor/utils/index.d.ts +1 -0
  62. package/lib/editor/utils/index.js +1 -0
  63. package/lib/editor/utils/root.d.ts +3 -0
  64. package/lib/editor/utils/root.js +22 -0
  65. package/lib/exporter/svg.js +192 -52
  66. package/lib/resource/loaders/search.js +0 -3
  67. package/lib/templates/utils.js +11 -6
  68. package/lib/utils/padding.js +1 -1
  69. package/lib/utils/style.d.ts +3 -1
  70. package/lib/utils/style.js +27 -4
  71. package/lib/version.d.ts +1 -1
  72. package/lib/version.js +1 -1
  73. package/package.json +1 -1
  74. package/src/constants/service.ts +1 -1
  75. package/src/designs/structures/chart-line.tsx +5 -3
  76. package/src/editor/interactions/dblclick-edit-text.ts +3 -2
  77. package/src/editor/managers/interaction.ts +9 -7
  78. package/src/editor/plugins/components/button.ts +5 -2
  79. package/src/editor/plugins/components/color-picker.ts +4 -2
  80. package/src/editor/plugins/components/popover.ts +31 -12
  81. package/src/editor/plugins/edit-bar/edit-bar.ts +26 -11
  82. package/src/editor/plugins/edit-bar/edit-items/align-elements.ts +7 -2
  83. package/src/editor/plugins/edit-bar/edit-items/font-align.ts +8 -3
  84. package/src/editor/plugins/edit-bar/edit-items/font-color.ts +7 -2
  85. package/src/editor/plugins/edit-bar/edit-items/font-family.ts +11 -7
  86. package/src/editor/plugins/edit-bar/edit-items/font-size.ts +8 -3
  87. package/src/editor/plugins/edit-bar/edit-items/icon-color.ts +7 -2
  88. package/src/editor/plugins/edit-bar/edit-items/types.ts +6 -1
  89. package/src/editor/plugins/reset-viewbox.ts +17 -8
  90. package/src/editor/utils/index.ts +1 -0
  91. package/src/editor/utils/root.ts +26 -0
  92. package/src/exporter/svg.ts +267 -62
  93. package/src/resource/loaders/search.ts +0 -3
  94. package/src/templates/utils.ts +30 -6
  95. package/src/utils/padding.ts +1 -1
  96. package/src/utils/style.ts +31 -4
  97. package/src/version.ts +1 -1
@@ -1,6 +1,7 @@
1
1
  import { COMPONENT_ROLE } from '../../constants/index.js';
2
2
  import { getBoundViewBox, getViewBox, injectStyleOnce, parsePadding, setElementRole, viewBoxToString, } from '../../utils/index.js';
3
3
  import { UpdateOptionsCommand } from '../commands/index.js';
4
+ import { getOverlayContainer } from '../utils/index.js';
4
5
  import { Plugin } from './base.js';
5
6
  import { IconButton } from './components/index.js';
6
7
  import { RESET_ICON } from './components/icons.js';
@@ -33,9 +34,12 @@ export class ResetViewBox extends Plugin {
33
34
  if (this.resetButton)
34
35
  return this.resetButton;
35
36
  const { style, className } = this.options || {};
37
+ const containerParent = this.resolveOverlayRoot();
38
+ this.ensureButtonStyle(containerParent);
36
39
  const button = IconButton({
37
40
  icon: RESET_ICON,
38
41
  onClick: this.resetViewBox,
42
+ root: containerParent,
39
43
  });
40
44
  button.classList.add(RESET_BUTTON_CLASS);
41
45
  if (className) {
@@ -46,9 +50,6 @@ export class ResetViewBox extends Plugin {
46
50
  }
47
51
  setElementRole(button, COMPONENT_ROLE);
48
52
  this.resetButton = button;
49
- const { getContainer } = this.options || {};
50
- const resolvedContainer = typeof getContainer === 'function' ? getContainer() : getContainer;
51
- const containerParent = resolvedContainer !== null && resolvedContainer !== void 0 ? resolvedContainer : document.body;
52
53
  containerParent === null || containerParent === void 0 ? void 0 : containerParent.appendChild(button);
53
54
  return button;
54
55
  };
@@ -139,7 +140,7 @@ export class ResetViewBox extends Plugin {
139
140
  init(options) {
140
141
  super.init(options);
141
142
  // Initialize originViewBox
142
- this.ensureButtonStyle();
143
+ this.ensureButtonStyle(this.resolveOverlayRoot());
143
144
  this.updateOriginViewBox();
144
145
  this.unregisterSync = this.editor.registerSync('viewBox', this.handleViewBoxChange);
145
146
  window.addEventListener('resize', this.handleResize);
@@ -161,7 +162,12 @@ export class ResetViewBox extends Plugin {
161
162
  const { padding } = this.state.getOptions();
162
163
  this.originViewBox = getBoundViewBox(svg, parsePadding(padding));
163
164
  }
164
- ensureButtonStyle() {
165
+ resolveOverlayRoot() {
166
+ const { getContainer } = this.options || {};
167
+ const resolvedContainer = typeof getContainer === 'function' ? getContainer() : getContainer;
168
+ return resolvedContainer !== null && resolvedContainer !== void 0 ? resolvedContainer : getOverlayContainer(this.editor.getDocument());
169
+ }
170
+ ensureButtonStyle(target) {
165
171
  injectStyleOnce(RESET_BUTTON_STYLE_ID, `
166
172
  button.${RESET_BUTTON_CLASS} {
167
173
  visibility: hidden;
@@ -179,7 +185,7 @@ export class ResetViewBox extends Plugin {
179
185
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
180
186
  cursor: pointer;
181
187
  }
182
- `);
188
+ `, target);
183
189
  }
184
190
  }
185
191
  const RESET_BUTTON_CLASS = 'infographic-reset-viewbox-btn';
@@ -6,3 +6,4 @@ export * from './event';
6
6
  export * from './extension';
7
7
  export * from './hotkey';
8
8
  export * from './object';
9
+ export * from './root';
@@ -6,3 +6,4 @@ export * from './event.js';
6
6
  export * from './extension.js';
7
7
  export * from './hotkey.js';
8
8
  export * from './object.js';
9
+ export * from './root.js';
@@ -0,0 +1,3 @@
1
+ export type OverlayContainer = HTMLElement | ShadowRoot;
2
+ export declare function getOverlayContainer(node?: Node | null): OverlayContainer;
3
+ export declare function eventPathContains(event: Event, node: Node): boolean;
@@ -0,0 +1,18 @@
1
+ function getConnectedRoot(node) {
2
+ if (!(node === null || node === void 0 ? void 0 : node.isConnected))
3
+ return document;
4
+ const root = node.getRootNode();
5
+ return root instanceof ShadowRoot ? root : document;
6
+ }
7
+ export function getOverlayContainer(node) {
8
+ const root = getConnectedRoot(node);
9
+ return root instanceof ShadowRoot ? root : document.body;
10
+ }
11
+ export function eventPathContains(event, node) {
12
+ const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
13
+ if (path.length > 0) {
14
+ return path.some((current) => current === node || (current instanceof Node && node.contains(current)));
15
+ }
16
+ const target = event.target;
17
+ return target instanceof Node && (target === node || node.contains(target));
18
+ }
@@ -22,7 +22,10 @@ function getExportViewBox(svg) {
22
22
  return getViewBox(svg);
23
23
  const width = parseAbsoluteLength(svg.getAttribute('width'));
24
24
  const height = parseAbsoluteLength(svg.getAttribute('height'));
25
- if (width > 0 && height > 0) {
25
+ if (!Number.isNaN(width) &&
26
+ width > 0 &&
27
+ !Number.isNaN(height) &&
28
+ height > 0) {
26
29
  return { x: 0, y: 0, width, height };
27
30
  }
28
31
  const rect = svg.getBoundingClientRect();
@@ -41,38 +44,69 @@ function parseAbsoluteLength(value) {
41
44
  return Number.NaN;
42
45
  return Number.parseFloat(trimmed);
43
46
  }
44
- function measureSpanContentHeight(span) {
47
+ function parseCoordinate(value) {
48
+ const parsed = parseAbsoluteLength(value);
49
+ return Number.isNaN(parsed) ? 0 : parsed;
50
+ }
51
+ function measureSpanContentDimensions(span, measureWidth) {
45
52
  const prevHeight = span.style.height;
53
+ const prevWidth = span.style.width;
46
54
  const prevOverflow = span.style.overflow;
47
55
  try {
48
56
  span.style.height = 'max-content';
49
57
  span.style.overflow = 'hidden';
50
58
  void span.offsetHeight; // force reflow
51
- return span.scrollHeight;
59
+ const scrollHeight = span.scrollHeight;
60
+ const rectHeight = span.getBoundingClientRect().height;
61
+ let width = span.scrollWidth;
62
+ if (measureWidth) {
63
+ span.style.width = 'max-content';
64
+ void span.offsetWidth; // force reflow
65
+ width = span.scrollWidth;
66
+ }
67
+ return {
68
+ height: Math.max(scrollHeight, rectHeight),
69
+ width,
70
+ };
52
71
  }
53
72
  finally {
54
73
  span.style.height = prevHeight;
55
- span.style.overflow = prevOverflow;
56
- }
57
- }
58
- function measureSpanContentWidth(span) {
59
- const prevWidth = span.style.width;
60
- const prevOverflow = span.style.overflow;
61
- try {
62
- span.style.width = 'max-content';
63
- span.style.overflow = 'hidden';
64
- void span.offsetWidth; // force reflow
65
- return span.scrollWidth;
66
- }
67
- finally {
68
74
  span.style.width = prevWidth;
69
75
  span.style.overflow = prevOverflow;
70
76
  }
71
77
  }
72
- // Returns [left, top, right, bottom] in SVG coordinates for a foreignObject,
78
+ function shouldKeepForeignObjectWidth(style) {
79
+ const whiteSpace = style.whiteSpace;
80
+ const flexWrap = style.flexWrap;
81
+ const wordBreak = style.wordBreak;
82
+ const overflowWrap = style.overflowWrap;
83
+ return (flexWrap === 'wrap' ||
84
+ flexWrap === 'wrap-reverse' ||
85
+ whiteSpace === 'pre-wrap' ||
86
+ whiteSpace === 'pre-line' ||
87
+ whiteSpace === 'normal' ||
88
+ overflowWrap === 'break-word' ||
89
+ wordBreak === 'break-word' ||
90
+ wordBreak === 'break-all');
91
+ }
92
+ function createCoordConverter(svg, element) {
93
+ if (typeof element.getScreenCTM !== 'function')
94
+ return null;
95
+ const screenCTM = element.getScreenCTM();
96
+ if (!screenCTM)
97
+ return null;
98
+ const inverseCTM = screenCTM.inverse();
99
+ return (clientX, clientY) => {
100
+ const pt = svg.createSVGPoint();
101
+ pt.x = clientX;
102
+ pt.y = clientY;
103
+ return pt.matrixTransform(inverseCTM);
104
+ };
105
+ }
106
+ // Returns [left, top, right, bottom] in target coordinates for a foreignObject,
73
107
  // accounting for flex alignment: bottom/center-aligned content can overflow,
74
108
  // and horizontally aligned content can overflow as well.
75
- function getFOContentBoundsInSVG(fo, content, toSVGCoord) {
109
+ function getFOContentBoundsInSVG(fo, toSVGCoord, { contentHeight, contentWidth, keepForeignObjectWidth, }) {
76
110
  const foRect = fo.getBoundingClientRect();
77
111
  const foTopLeft = toSVGCoord(foRect.left, foRect.top);
78
112
  const foBottomRight = toSVGCoord(foRect.right, foRect.bottom);
@@ -84,16 +118,15 @@ function getFOContentBoundsInSVG(fo, content, toSVGCoord) {
84
118
  const foHeightSVG = foBottomSVG - foTopSVG;
85
119
  const svgUnitsPerClientPxY = foRect.height > 0 ? foHeightSVG / foRect.height : 1;
86
120
  const svgUnitsPerClientPxX = foRect.width > 0 ? foWidthSVG / foRect.width : 1;
87
- // Measure actual content dimensions
88
- const realScrollHeight = measureSpanContentHeight(content);
89
- const contentHeightSVG = realScrollHeight > 0
90
- ? realScrollHeight * svgUnitsPerClientPxY
121
+ const contentHeightSVG = contentHeight > 0
122
+ ? contentHeight * svgUnitsPerClientPxY
91
123
  : foHeightSVG;
92
- const realScrollWidth = measureSpanContentWidth(content);
93
- const contentWidthSVG = realScrollWidth > 0 ? realScrollWidth * svgUnitsPerClientPxX : foWidthSVG;
94
- const computedStyle = window.getComputedStyle(content);
124
+ const computedStyle = window.getComputedStyle(fo.firstElementChild);
95
125
  const alignItems = computedStyle.alignItems;
96
126
  const justifyContent = computedStyle.justifyContent;
127
+ const contentWidthSVG = keepForeignObjectWidth
128
+ ? foWidthSVG
129
+ : Math.max(foWidthSVG, contentWidth * svgUnitsPerClientPxX);
97
130
  // Calculate vertical bounds
98
131
  let top, bottom;
99
132
  if (alignItems === 'flex-end' || alignItems === 'end') {
@@ -128,38 +161,75 @@ function getFOContentBoundsInSVG(fo, content, toSVGCoord) {
128
161
  }
129
162
  return [left, top, right, bottom];
130
163
  }
131
- /**
132
- * Computes a viewBox that fully covers all foreignObject text content,
133
- * accounting for overflow caused by flex alignment (bottom/center align
134
- * can push content outside the foreignObject bounds).
135
- */
136
- function computeFullViewBox(svg) {
164
+ function collectForeignObjectExportAdjustments(svg) {
165
+ const toSVGCoord = createCoordConverter(svg, svg);
166
+ if (!toSVGCoord)
167
+ return [];
168
+ return Array.from(svg.querySelectorAll('foreignObject')).map((fo) => {
169
+ const content = fo.firstElementChild;
170
+ if (!content)
171
+ return null;
172
+ const computedStyle = window.getComputedStyle(content);
173
+ const keepForeignObjectWidth = shouldKeepForeignObjectWidth(computedStyle);
174
+ const measuredContent = measureSpanContentDimensions(content, !keepForeignObjectWidth);
175
+ const parent = fo.parentElement instanceof SVGGraphicsElement ? fo.parentElement : svg;
176
+ const toParentCoord = createCoordConverter(svg, parent);
177
+ const toLocalCoord = createCoordConverter(svg, fo);
178
+ if (!toParentCoord)
179
+ return null;
180
+ const parentBounds = getFOContentBoundsInSVG(fo, toParentCoord, {
181
+ contentHeight: measuredContent.height,
182
+ contentWidth: measuredContent.width,
183
+ keepForeignObjectWidth,
184
+ });
185
+ const originalX = parseCoordinate(fo.getAttribute('x'));
186
+ const originalY = parseCoordinate(fo.getAttribute('y'));
187
+ const localBounds = toLocalCoord
188
+ ? getFOContentBoundsInSVG(fo, toLocalCoord, {
189
+ contentHeight: measuredContent.height,
190
+ contentWidth: measuredContent.width,
191
+ keepForeignObjectWidth,
192
+ })
193
+ : null;
194
+ const hasTransform = fo.hasAttribute('transform');
195
+ if (hasTransform && !localBounds)
196
+ return null;
197
+ const exportBounds = localBounds
198
+ ? {
199
+ x: originalX + localBounds[0],
200
+ y: originalY + localBounds[1],
201
+ width: localBounds[2] - localBounds[0],
202
+ height: localBounds[3] - localBounds[1],
203
+ }
204
+ : {
205
+ x: parentBounds[0],
206
+ y: parentBounds[1],
207
+ width: parentBounds[2] - parentBounds[0],
208
+ height: parentBounds[3] - parentBounds[1],
209
+ };
210
+ return {
211
+ rootBounds: getFOContentBoundsInSVG(fo, toSVGCoord, {
212
+ contentHeight: measuredContent.height,
213
+ contentWidth: measuredContent.width,
214
+ keepForeignObjectWidth,
215
+ }),
216
+ exportBounds,
217
+ };
218
+ });
219
+ }
220
+ function computeFullViewBox(svg, adjustments) {
137
221
  const viewBox = getExportViewBox(svg);
138
222
  if (!viewBox)
139
223
  return null;
140
- if (typeof svg.getScreenCTM !== 'function')
141
- return null;
142
- const screenCTM = svg.getScreenCTM();
143
- if (!screenCTM)
144
- return null;
145
- const inverseCTM = screenCTM.inverse();
146
- const toSVGCoord = (clientX, clientY) => {
147
- const pt = svg.createSVGPoint();
148
- pt.x = clientX;
149
- pt.y = clientY;
150
- return pt.matrixTransform(inverseCTM);
151
- };
152
224
  let minX = viewBox.x;
153
225
  let minY = viewBox.y;
154
226
  let maxX = viewBox.x + viewBox.width;
155
227
  let maxY = viewBox.y + viewBox.height;
156
- svg
157
- .querySelectorAll('foreignObject')
158
- .forEach((fo) => {
159
- const content = fo.firstElementChild;
160
- if (!content)
228
+ adjustments.forEach((adjustment) => {
229
+ if (!adjustment)
161
230
  return;
162
- const [left, top, right, bottom] = getFOContentBoundsInSVG(fo, content, toSVGCoord);
231
+ const { rootBounds } = adjustment;
232
+ const [left, top, right, bottom] = rootBounds;
163
233
  minX = Math.min(minX, left);
164
234
  minY = Math.min(minY, top);
165
235
  maxX = Math.max(maxX, right);
@@ -176,12 +246,25 @@ function computeFullViewBox(svg) {
176
246
  return null;
177
247
  return `${newX} ${newY} ${newWidth} ${newHeight}`;
178
248
  }
249
+ function applyForeignObjectExportAdjustments(svg, adjustments) {
250
+ const clonedForeignObjects = Array.from(svg.querySelectorAll('foreignObject'));
251
+ adjustments.forEach((adjustment, index) => {
252
+ if (!adjustment)
253
+ return;
254
+ const clonedForeignObject = clonedForeignObjects[index];
255
+ if (!clonedForeignObject)
256
+ return;
257
+ setAttributes(clonedForeignObject, adjustment.exportBounds);
258
+ });
259
+ }
179
260
  export function exportToSVG(svg_1) {
180
261
  return __awaiter(this, arguments, void 0, function* (svg, options = {}) {
181
262
  const { removeBackground = false, embedResources = true, removeIds = false, } = options;
182
263
  const clonedSVG = svg.cloneNode(true);
183
264
  if (typeof document !== 'undefined') {
184
- const fullViewBox = computeFullViewBox(svg);
265
+ const adjustments = collectForeignObjectExportAdjustments(svg);
266
+ applyForeignObjectExportAdjustments(clonedSVG, adjustments);
267
+ const fullViewBox = computeFullViewBox(svg, adjustments);
185
268
  if (fullViewBox) {
186
269
  clonedSVG.setAttribute('viewBox', fullViewBox);
187
270
  }
@@ -214,8 +297,9 @@ function embedIcons(svg) {
214
297
  const existsSymbol = svg.querySelector(href);
215
298
  if (!existsSymbol) {
216
299
  const symbolElement = document.querySelector(href);
217
- if (symbolElement)
300
+ if (symbolElement) {
218
301
  defs.appendChild(symbolElement.cloneNode(true));
302
+ }
219
303
  }
220
304
  });
221
305
  });
@@ -423,6 +507,8 @@ function collectDefElements(svg, ids) {
423
507
  };
424
508
  while (queue.length) {
425
509
  const id = queue.shift();
510
+ if (!id)
511
+ continue;
426
512
  if (visited.has(id))
427
513
  continue;
428
514
  visited.add(id);
@@ -437,11 +523,65 @@ function collectDefElements(svg, ids) {
437
523
  }
438
524
  return collected;
439
525
  }
526
+ // Fallback implementation based on the CSS.escape algorithm
527
+ function cssEscape(value) {
528
+ const string = String(value);
529
+ const length = string.length;
530
+ let result = '';
531
+ if (length === 0) {
532
+ return '';
533
+ }
534
+ for (let i = 0; i < length; i++) {
535
+ const codeUnit = string.charCodeAt(i);
536
+ // Null character
537
+ if (codeUnit === 0x0000) {
538
+ result += '\uFFFD';
539
+ continue;
540
+ }
541
+ // Control characters or DEL
542
+ if ((codeUnit >= 0x0001 && codeUnit <= 0x001f) || codeUnit === 0x007f) {
543
+ result += '\\' + codeUnit.toString(16) + ' ';
544
+ continue;
545
+ }
546
+ // Escape if first character is a digit
547
+ if (i === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) {
548
+ result += '\\' + codeUnit.toString(16) + ' ';
549
+ continue;
550
+ }
551
+ // Escape if second character is a digit and first is a hyphen
552
+ if (i === 1 &&
553
+ codeUnit >= 0x0030 &&
554
+ codeUnit <= 0x0039 &&
555
+ string.charCodeAt(0) === 0x002d) {
556
+ result += '\\' + codeUnit.toString(16) + ' ';
557
+ continue;
558
+ }
559
+ // If the character is the first and is a hyphen followed by end of string, escape it
560
+ if (i === 0 && length === 1 && codeUnit === 0x002d) {
561
+ result += '\\' + string.charAt(i);
562
+ continue;
563
+ }
564
+ // Characters that are safe to use unescaped
565
+ if (codeUnit >= 0x0080 ||
566
+ (codeUnit >= 0x0030 && codeUnit <= 0x0039) || // 0-9
567
+ (codeUnit >= 0x0041 && codeUnit <= 0x005a) || // A-Z
568
+ (codeUnit >= 0x0061 && codeUnit <= 0x007a) || // a-z
569
+ codeUnit === 0x002d || // -
570
+ codeUnit === 0x005f // _
571
+ ) {
572
+ result += string.charAt(i);
573
+ continue;
574
+ }
575
+ // All other characters
576
+ result += '\\' + string.charAt(i);
577
+ }
578
+ return result;
579
+ }
440
580
  function escapeCssId(id) {
441
581
  if (globalThis.CSS && typeof globalThis.CSS.escape === 'function') {
442
582
  return globalThis.CSS.escape(id);
443
583
  }
444
- return id.replace(/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g, '\\$1');
584
+ return cssEscape(id);
445
585
  }
446
586
  function removeDefs(svg) {
447
587
  const defsList = Array.from(svg.querySelectorAll('defs'));
@@ -54,9 +54,6 @@ export function loadSearchResource(query, format) {
54
54
  const svgText = commaIndex >= 0 ? result.slice(commaIndex + 1) : result;
55
55
  return loadSVGResource(svgText);
56
56
  }
57
- if (mimeType === 'image/svg+xml' && format === 'svg' && isBase64) {
58
- return loadImageBase64Resource(result);
59
- }
60
57
  return loadImageBase64Resource(result);
61
58
  }
62
59
  return loadRemoteResource(result, format);
@@ -30,11 +30,20 @@ function getLevenshteinDistance(source, target) {
30
30
  function getCommonPrefixLength(source, target) {
31
31
  const limit = Math.min(source.length, target.length);
32
32
  let index = 0;
33
- while (index < limit && source[index] === target[index]) {
33
+ while (index < limit &&
34
+ source.charCodeAt(index) === target.charCodeAt(index)) {
34
35
  index += 1;
35
36
  }
36
37
  return index;
37
38
  }
39
+ function isBetterMatch(bestMatch, bestDistance, bestPrefixLength, candidateKey, candidateDistance, candidatePrefixLength) {
40
+ return (candidateDistance < bestDistance ||
41
+ (candidateDistance === bestDistance &&
42
+ candidatePrefixLength > bestPrefixLength) ||
43
+ (candidateDistance === bestDistance &&
44
+ candidatePrefixLength === bestPrefixLength &&
45
+ (!bestMatch || candidateKey < bestMatch)));
46
+ }
38
47
  export function findClosestTemplateKey(type, keys) {
39
48
  const normalizedType = normalizeTemplateKey(type);
40
49
  if (!normalizedType)
@@ -49,11 +58,7 @@ export function findClosestTemplateKey(type, keys) {
49
58
  }
50
59
  const distance = getLevenshteinDistance(normalizedType, normalizedKey);
51
60
  const prefixLength = getCommonPrefixLength(normalizedType, normalizedKey);
52
- if (distance < bestDistance ||
53
- (distance === bestDistance && prefixLength > bestPrefixLength) ||
54
- (distance === bestDistance &&
55
- prefixLength === bestPrefixLength &&
56
- (!bestMatch || key < bestMatch))) {
61
+ if (isBetterMatch(bestMatch, bestDistance, bestPrefixLength, key, distance, prefixLength)) {
57
62
  bestMatch = key;
58
63
  bestDistance = distance;
59
64
  bestPrefixLength = prefixLength;
@@ -26,7 +26,7 @@ export function setSVGPadding(svg, padding) {
26
26
  setSVGPaddingInNode(svg, padding);
27
27
  }
28
28
  else {
29
- if (document.contains(svg)) {
29
+ if (svg.isConnected) {
30
30
  setSVGPaddingInBrowser(svg, padding);
31
31
  }
32
32
  else {
@@ -1 +1,3 @@
1
- export declare function injectStyleOnce(id: string, styles: string): void;
1
+ type StyleTarget = Document | ShadowRoot | Node | null | undefined;
2
+ export declare function injectStyleOnce(id: string, styles: string, target?: StyleTarget): void;
3
+ export {};
@@ -1,8 +1,31 @@
1
- export function injectStyleOnce(id, styles) {
2
- if (document.getElementById(id))
1
+ function resolveStyleRoot(target) {
2
+ if (!target)
3
+ return document;
4
+ if (target instanceof Document || target instanceof ShadowRoot)
5
+ return target;
6
+ if (!target.isConnected)
7
+ return document;
8
+ const root = target.getRootNode();
9
+ return root instanceof ShadowRoot ? root : document;
10
+ }
11
+ function hasStyle(root, id) {
12
+ if (root instanceof Document)
13
+ return Boolean(root.getElementById(id));
14
+ return Boolean(root.querySelector(`#${id}`));
15
+ }
16
+ export function injectStyleOnce(id, styles, target) {
17
+ var _a;
18
+ const root = resolveStyleRoot(target);
19
+ if (hasStyle(root, id))
3
20
  return;
4
- const style = document.createElement('style');
21
+ const doc = root instanceof Document ? root : ((_a = root.ownerDocument) !== null && _a !== void 0 ? _a : document);
22
+ const style = doc.createElement('style');
5
23
  style.id = id;
6
24
  style.textContent = styles;
7
- document.head.appendChild(style);
25
+ if (root instanceof Document) {
26
+ root.head.appendChild(style);
27
+ }
28
+ else {
29
+ root.appendChild(style);
30
+ }
8
31
  }
package/esm/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.2.17";
1
+ export declare const VERSION = "0.2.19";
package/esm/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.2.17';
1
+ export const VERSION = '0.2.19';
@@ -1 +1 @@
1
- export declare const ICON_SERVICE_URL = "https://lab.weavefox.cn/api/v1/infographic/icon";
1
+ export declare const ICON_SERVICE_URL = "https://www.weavefox.cn/api/v1/infographic/icon";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ICON_SERVICE_URL = void 0;
4
- exports.ICON_SERVICE_URL = 'https://lab.weavefox.cn/api/v1/infographic/icon';
4
+ exports.ICON_SERVICE_URL = 'https://www.weavefox.cn/api/v1/infographic/icon';
@@ -9,6 +9,8 @@ const components_1 = require("../components");
9
9
  const layouts_1 = require("../layouts");
10
10
  const utils_2 = require("../utils");
11
11
  const registry_1 = require("./registry");
12
+ const ITEM_POSITION_H = 'center';
13
+ const ITEM_POSITION_V = 'normal';
12
14
  const ChartLine = (props) => {
13
15
  const { Title, Item, data, width, height = 260, gap = 10, padding = 24, showValue = true, options, valueFormatter = (value) => value.toString(), } = props;
14
16
  const { title, desc, items = [], xTitle, yTitle } = data;
@@ -21,8 +23,8 @@ const ChartLine = (props) => {
21
23
  indexes: [0],
22
24
  datum: items[0],
23
25
  data,
24
- positionH: 'center',
25
- positionV: 'normal',
26
+ positionH: ITEM_POSITION_H,
27
+ positionV: ITEM_POSITION_V,
26
28
  };
27
29
  const sampleBounds = (0, jsx_1.getElementBounds)((0, jsx_runtime_1.jsx)(Item, Object.assign({}, itemProps)));
28
30
  const labelWidth = sampleBounds.width;
@@ -162,7 +164,7 @@ const ChartLine = (props) => {
162
164
  if (yTitle) {
163
165
  titleElements.push((0, jsx_runtime_1.jsx)(jsx_1.Text, { x: paddingLeft + yTitleSpace / 2, y: chartOriginY + height / 2, alignHorizontal: "center", alignVertical: "middle", fontSize: 14, fontWeight: "bold", fill: axisColor, children: yTitle }));
164
166
  }
165
- 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, { width: totalWidth, height: totalHeight, children: [(0, jsx_runtime_1.jsxs)(jsx_1.Defs, { children: [(0, jsx_runtime_1.jsx)("linearGradient", { id: gradientStrokeId, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: gradientStops }), (0, jsx_runtime_1.jsxs)("linearGradient", { id: gradientAreaId, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [areaStops, (0, jsx_runtime_1.jsx)("stop", { offset: "100%", stopColor: colorPrimary, stopOpacity: "0.04" })] })] }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: gridElements }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: [...axisElements, ...tickElements] }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: lineElements }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: pointElements }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: valueElements }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: titleElements }), (0, jsx_runtime_1.jsx)(components_1.ItemsGroup, { children: xLabels })] })] }));
167
+ 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, { width: totalWidth, height: totalHeight, children: [(0, jsx_runtime_1.jsxs)(jsx_1.Defs, { children: [(0, jsx_runtime_1.jsx)("linearGradient", { id: gradientStrokeId, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: gradientStops }), (0, jsx_runtime_1.jsx)("linearGradient", { id: gradientAreaId, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: areaStops })] }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: gridElements }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: [...axisElements, ...tickElements] }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: lineElements }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: pointElements }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: valueElements }), (0, jsx_runtime_1.jsx)(jsx_1.Group, { children: titleElements }), (0, jsx_runtime_1.jsx)(components_1.ItemsGroup, { children: xLabels })] })] }));
166
168
  };
167
169
  exports.ChartLine = ChartLine;
168
170
  (0, registry_1.registerStructure)('chart-line', {
@@ -76,7 +76,7 @@ function editText(text, options) {
76
76
  const entity = (0, utils_1.getTextEntity)(text);
77
77
  if (!entity)
78
78
  return;
79
- ensureEditorStyles();
79
+ ensureEditorStyles(entity);
80
80
  new InlineTextEditor(entity, options).start();
81
81
  }
82
82
  class InlineTextEditor {
@@ -207,7 +207,7 @@ class InlineTextEditor {
207
207
  return this.entity.textContent || '';
208
208
  }
209
209
  }
210
- function ensureEditorStyles() {
210
+ function ensureEditorStyles(target) {
211
211
  (0, utils_1.injectStyleOnce)(EDITOR_STYLE_ID, `
212
212
  .${EDITOR_BASE_CLASS} {
213
213
  margin: 0;
@@ -220,5 +220,5 @@ function ensureEditorStyles() {
220
220
  .${EDITOR_BASE_CLASS}::selection {
221
221
  background-color: #b3d4fc;
222
222
  }
223
- `);
223
+ `, target);
224
224
  }
@@ -12,9 +12,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.InteractionManager = void 0;
13
13
  const utils_1 = require("../../utils");
14
14
  const utils_2 = require("../utils");
15
+ const utils_3 = require("../utils");
15
16
  class InteractionManager {
16
17
  constructor() {
17
- this.extensions = new utils_2.Extension();
18
+ this.extensions = new utils_3.Extension();
18
19
  this.interactions = [];
19
20
  this.active = false;
20
21
  this.running = null;
@@ -22,14 +23,15 @@ class InteractionManager {
22
23
  this.selection = new Set();
23
24
  this.handleClick = (event) => {
24
25
  const doc = this.editor.getDocument();
25
- const target = event.target;
26
- if (!target) {
26
+ const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
27
+ const insideInfographic = (0, utils_2.eventPathContains)(event, doc) ||
28
+ path.some((node) => node instanceof HTMLElement && (0, utils_1.isInfographicComponent)(node));
29
+ if (!event.target) {
27
30
  this.deactivate();
28
31
  return;
29
32
  }
30
33
  // 点击画布 SVG 或者标记为组件的元素
31
- if (doc.contains(target) ||
32
- (0, utils_1.isInfographicComponent)(target))
34
+ if (insideInfographic)
33
35
  this.activate();
34
36
  else
35
37
  this.deactivate();
@@ -4,9 +4,10 @@ export interface IconButtonProps {
4
4
  icon: Icon;
5
5
  onClick?: () => void;
6
6
  activate?: boolean;
7
+ root?: Node;
7
8
  }
8
9
  export interface IconButtonHandle {
9
10
  activate: boolean;
10
11
  setActivate: (activate: boolean) => void;
11
12
  }
12
- export declare const IconButton: ({ icon, onClick, activate, }: IconButtonProps) => Button;
13
+ export declare const IconButton: ({ icon, onClick, activate, root, }: IconButtonProps) => Button;