@antv/infographic 0.2.17 → 0.2.18

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 +99 -99
  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 +63 -3
  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 +63 -3
  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 +80 -3
  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
@@ -12,8 +12,10 @@ export const IconColor: EditItem<IconAttributes> = (
12
12
  selection,
13
13
  attrs,
14
14
  commander,
15
+ options,
15
16
  ) => {
16
- ensureIconColorStyles();
17
+ const root = options?.root;
18
+ ensureIconColorStyles(root);
17
19
 
18
20
  const color = normalizeColor(attrs.fill);
19
21
  const isMixed = attrs.fill === undefined && selection.length > 1;
@@ -24,6 +26,7 @@ export const IconColor: EditItem<IconAttributes> = (
24
26
  setButtonColor(button, color ?? DEFAULT_COLOR, isMixed);
25
27
 
26
28
  const picker = ColorPicker({
29
+ root,
27
30
  value: color ?? DEFAULT_COLOR,
28
31
  onChange: (nextColor) => {
29
32
  setButtonColor(button, nextColor, false);
@@ -41,6 +44,7 @@ export const IconColor: EditItem<IconAttributes> = (
41
44
  return Popover({
42
45
  target: button,
43
46
  content: picker,
47
+ getContainer: root,
44
48
  placement: ['top', 'bottom'],
45
49
  offset: 12,
46
50
  trigger: 'hover',
@@ -65,7 +69,7 @@ function setButtonColor(
65
69
  else button.removeAttribute('data-mixed');
66
70
  }
67
71
 
68
- function ensureIconColorStyles() {
72
+ function ensureIconColorStyles(target?: Node) {
69
73
  injectStyleOnce(
70
74
  ICON_COLOR_STYLE_ID,
71
75
  `
@@ -97,5 +101,6 @@ function ensureIconColorStyles() {
97
101
  );
98
102
  }
99
103
  `,
104
+ target,
100
105
  );
101
106
  }
@@ -1,9 +1,14 @@
1
1
  import type { BaseAttributes } from '../../../../types';
2
2
  import type { ICommandManager, Selection } from '../../../types';
3
3
 
4
+ export type EditItemOptions = {
5
+ root?: HTMLElement | ShadowRoot;
6
+ [key: string]: any;
7
+ };
8
+
4
9
  export type EditItem<T extends BaseAttributes = BaseAttributes> = (
5
10
  selection: Selection,
6
11
  attrs: T,
7
12
  commander: ICommandManager,
8
- options?: Record<string, any>,
13
+ options?: EditItemOptions,
9
14
  ) => HTMLElement;
@@ -9,6 +9,7 @@ import {
9
9
  } from '../../utils';
10
10
  import { UpdateOptionsCommand } from '../commands';
11
11
  import type { IPlugin, PluginInitOptions } from '../types';
12
+ import { getOverlayContainer } from '../utils';
12
13
  import { Plugin } from './base';
13
14
  import { IconButton } from './components';
14
15
  import { RESET_ICON } from './components/icons';
@@ -19,9 +20,11 @@ const BUTTON_SIZE = 40;
19
20
  export interface ResetViewBoxOptions {
20
21
  style?: Partial<CSSStyleDeclaration>;
21
22
  className?: string;
22
- getContainer?: HTMLElement | (() => HTMLElement);
23
+ getContainer?: OverlayRoot | (() => OverlayRoot);
23
24
  }
24
25
 
26
+ type OverlayRoot = HTMLElement | ShadowRoot;
27
+
25
28
  export class ResetViewBox extends Plugin implements IPlugin {
26
29
  name = 'reset-viewBox';
27
30
 
@@ -38,7 +41,7 @@ export class ResetViewBox extends Plugin implements IPlugin {
38
41
  super.init(options);
39
42
 
40
43
  // Initialize originViewBox
41
- this.ensureButtonStyle();
44
+ this.ensureButtonStyle(this.resolveOverlayRoot());
42
45
  this.updateOriginViewBox();
43
46
 
44
47
  this.unregisterSync = this.editor.registerSync(
@@ -91,10 +94,13 @@ export class ResetViewBox extends Plugin implements IPlugin {
91
94
  if (this.resetButton) return this.resetButton;
92
95
 
93
96
  const { style, className } = this.options || {};
97
+ const containerParent = this.resolveOverlayRoot();
98
+ this.ensureButtonStyle(containerParent);
94
99
 
95
100
  const button = IconButton({
96
101
  icon: RESET_ICON,
97
102
  onClick: this.resetViewBox,
103
+ root: containerParent,
98
104
  });
99
105
 
100
106
  button.classList.add(RESET_BUTTON_CLASS);
@@ -111,11 +117,6 @@ export class ResetViewBox extends Plugin implements IPlugin {
111
117
 
112
118
  this.resetButton = button;
113
119
 
114
- const { getContainer } = this.options || {};
115
- const resolvedContainer =
116
- typeof getContainer === 'function' ? getContainer() : getContainer;
117
- const containerParent = resolvedContainer ?? document.body;
118
-
119
120
  containerParent?.appendChild(button);
120
121
 
121
122
  return button;
@@ -229,7 +230,14 @@ export class ResetViewBox extends Plugin implements IPlugin {
229
230
  this.resetButton = undefined;
230
231
  };
231
232
 
232
- private ensureButtonStyle() {
233
+ private resolveOverlayRoot(): OverlayRoot {
234
+ const { getContainer } = this.options || {};
235
+ const resolvedContainer =
236
+ typeof getContainer === 'function' ? getContainer() : getContainer;
237
+ return resolvedContainer ?? getOverlayContainer(this.editor.getDocument());
238
+ }
239
+
240
+ private ensureButtonStyle(target?: Node) {
233
241
  injectStyleOnce(
234
242
  RESET_BUTTON_STYLE_ID,
235
243
  `
@@ -250,6 +258,7 @@ export class ResetViewBox extends Plugin implements IPlugin {
250
258
  cursor: pointer;
251
259
  }
252
260
  `,
261
+ target,
253
262
  );
254
263
  }
255
264
  }
@@ -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';
@@ -0,0 +1,26 @@
1
+ export type OverlayContainer = HTMLElement | ShadowRoot;
2
+
3
+ function getConnectedRoot(node?: Node | null): Document | ShadowRoot {
4
+ if (!node?.isConnected) return document;
5
+
6
+ const root = node.getRootNode();
7
+ return root instanceof ShadowRoot ? root : document;
8
+ }
9
+
10
+ export function getOverlayContainer(node?: Node | null): OverlayContainer {
11
+ const root = getConnectedRoot(node);
12
+ return root instanceof ShadowRoot ? root : document.body;
13
+ }
14
+
15
+ export function eventPathContains(event: Event, node: Node) {
16
+ const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
17
+ if (path.length > 0) {
18
+ return path.some(
19
+ (current) =>
20
+ current === node || (current instanceof Node && node.contains(current)),
21
+ );
22
+ }
23
+
24
+ const target = event.target;
25
+ return target instanceof Node && (target === node || node.contains(target));
26
+ }
@@ -26,7 +26,12 @@ function getExportViewBox(svg: SVGSVGElement) {
26
26
 
27
27
  const width = parseAbsoluteLength(svg.getAttribute('width'));
28
28
  const height = parseAbsoluteLength(svg.getAttribute('height'));
29
- if (width > 0 && height > 0) {
29
+ if (
30
+ !Number.isNaN(width) &&
31
+ width > 0 &&
32
+ !Number.isNaN(height) &&
33
+ height > 0
34
+ ) {
30
35
  return { x: 0, y: 0, width, height };
31
36
  }
32
37
 
@@ -253,7 +258,9 @@ async function embedIcons(svg: SVGSVGElement) {
253
258
 
254
259
  if (!existsSymbol) {
255
260
  const symbolElement = document.querySelector(href);
256
- if (symbolElement) defs.appendChild(symbolElement.cloneNode(true));
261
+ if (symbolElement) {
262
+ defs.appendChild(symbolElement.cloneNode(true));
263
+ }
257
264
  }
258
265
  });
259
266
  }
@@ -487,6 +494,7 @@ function collectDefElements(svg: SVGSVGElement, ids: Set<string>) {
487
494
 
488
495
  while (queue.length) {
489
496
  const id = queue.shift()!;
497
+ if (!id) continue;
490
498
  if (visited.has(id)) continue;
491
499
  visited.add(id);
492
500
 
@@ -503,11 +511,80 @@ function collectDefElements(svg: SVGSVGElement, ids: Set<string>) {
503
511
  return collected;
504
512
  }
505
513
 
514
+ // Fallback implementation based on the CSS.escape algorithm
515
+ function cssEscape(value: string): string {
516
+ const string = String(value);
517
+ const length = string.length;
518
+ let result = '';
519
+
520
+ if (length === 0) {
521
+ return '';
522
+ }
523
+
524
+ for (let i = 0; i < length; i++) {
525
+ const codeUnit = string.charCodeAt(i);
526
+
527
+ // Null character
528
+ if (codeUnit === 0x0000) {
529
+ result += '\uFFFD';
530
+ continue;
531
+ }
532
+
533
+ // Control characters or DEL
534
+ if ((codeUnit >= 0x0001 && codeUnit <= 0x001f) || codeUnit === 0x007f) {
535
+ result += '\\' + codeUnit.toString(16) + ' ';
536
+ continue;
537
+ }
538
+
539
+ // Escape if first character is a digit
540
+ if (i === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) {
541
+ result += '\\' + codeUnit.toString(16) + ' ';
542
+ continue;
543
+ }
544
+
545
+ // Escape if second character is a digit and first is a hyphen
546
+ if (
547
+ i === 1 &&
548
+ codeUnit >= 0x0030 &&
549
+ codeUnit <= 0x0039 &&
550
+ string.charCodeAt(0) === 0x002d
551
+ ) {
552
+ result += '\\' + codeUnit.toString(16) + ' ';
553
+ continue;
554
+ }
555
+
556
+ // If the character is the first and is a hyphen followed by end of string, escape it
557
+ if (i === 0 && length === 1 && codeUnit === 0x002d) {
558
+ result += '\\' + string.charAt(i);
559
+ continue;
560
+ }
561
+
562
+ // Characters that are safe to use unescaped
563
+ if (
564
+ codeUnit >= 0x0080 ||
565
+ (codeUnit >= 0x0030 && codeUnit <= 0x0039) || // 0-9
566
+ (codeUnit >= 0x0041 && codeUnit <= 0x005a) || // A-Z
567
+ (codeUnit >= 0x0061 && codeUnit <= 0x007a) || // a-z
568
+ codeUnit === 0x002d || // -
569
+ codeUnit === 0x005f // _
570
+ ) {
571
+ result += string.charAt(i);
572
+ continue;
573
+ }
574
+
575
+ // All other characters
576
+ result += '\\' + string.charAt(i);
577
+ }
578
+
579
+ return result;
580
+ }
581
+
506
582
  function escapeCssId(id: string) {
507
583
  if (globalThis.CSS && typeof globalThis.CSS.escape === 'function') {
508
584
  return globalThis.CSS.escape(id);
509
585
  }
510
- return id.replace(/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g, '\\$1');
586
+
587
+ return cssEscape(id);
511
588
  }
512
589
 
513
590
  function removeDefs(svg: SVGSVGElement) {
@@ -43,9 +43,6 @@ export async function loadSearchResource(query: string, format?: string) {
43
43
  const svgText = commaIndex >= 0 ? result.slice(commaIndex + 1) : result;
44
44
  return loadSVGResource(svgText);
45
45
  }
46
- if (mimeType === 'image/svg+xml' && format === 'svg' && isBase64) {
47
- return loadImageBase64Resource(result);
48
- }
49
46
  return loadImageBase64Resource(result);
50
47
  }
51
48
 
@@ -43,13 +43,34 @@ function getCommonPrefixLength(source: string, target: string): number {
43
43
  const limit = Math.min(source.length, target.length);
44
44
  let index = 0;
45
45
 
46
- while (index < limit && source[index] === target[index]) {
46
+ while (
47
+ index < limit &&
48
+ source.charCodeAt(index) === target.charCodeAt(index)
49
+ ) {
47
50
  index += 1;
48
51
  }
49
52
 
50
53
  return index;
51
54
  }
52
55
 
56
+ function isBetterMatch(
57
+ bestMatch: string | undefined,
58
+ bestDistance: number,
59
+ bestPrefixLength: number,
60
+ candidateKey: string,
61
+ candidateDistance: number,
62
+ candidatePrefixLength: number,
63
+ ): boolean {
64
+ return (
65
+ candidateDistance < bestDistance ||
66
+ (candidateDistance === bestDistance &&
67
+ candidatePrefixLength > bestPrefixLength) ||
68
+ (candidateDistance === bestDistance &&
69
+ candidatePrefixLength === bestPrefixLength &&
70
+ (!bestMatch || candidateKey < bestMatch))
71
+ );
72
+ }
73
+
53
74
  export function findClosestTemplateKey(
54
75
  type: string,
55
76
  keys: Iterable<string>,
@@ -71,11 +92,14 @@ export function findClosestTemplateKey(
71
92
  const prefixLength = getCommonPrefixLength(normalizedType, normalizedKey);
72
93
 
73
94
  if (
74
- distance < bestDistance ||
75
- (distance === bestDistance && prefixLength > bestPrefixLength) ||
76
- (distance === bestDistance &&
77
- prefixLength === bestPrefixLength &&
78
- (!bestMatch || key < bestMatch))
95
+ isBetterMatch(
96
+ bestMatch,
97
+ bestDistance,
98
+ bestPrefixLength,
99
+ key,
100
+ distance,
101
+ prefixLength,
102
+ )
79
103
  ) {
80
104
  bestMatch = key;
81
105
  bestDistance = distance;
@@ -27,7 +27,7 @@ export function setSVGPadding(svg: SVGSVGElement, padding: ParsedPadding) {
27
27
  if (isNode) {
28
28
  setSVGPaddingInNode(svg, padding);
29
29
  } else {
30
- if (document.contains(svg)) {
30
+ if (svg.isConnected) {
31
31
  setSVGPaddingInBrowser(svg, padding);
32
32
  } else {
33
33
  try {
@@ -1,8 +1,35 @@
1
- export function injectStyleOnce(id: string, styles: string) {
2
- if (document.getElementById(id)) return;
1
+ type StyleTarget = Document | ShadowRoot | Node | null | undefined;
3
2
 
4
- const style = document.createElement('style');
3
+ function resolveStyleRoot(target?: StyleTarget): Document | ShadowRoot {
4
+ if (!target) return document;
5
+ if (target instanceof Document || target instanceof ShadowRoot) return target;
6
+ if (!target.isConnected) return document;
7
+
8
+ const root = target.getRootNode();
9
+ return root instanceof ShadowRoot ? root : document;
10
+ }
11
+
12
+ function hasStyle(root: Document | ShadowRoot, id: string) {
13
+ if (root instanceof Document) return Boolean(root.getElementById(id));
14
+ return Boolean(root.querySelector(`#${id}`));
15
+ }
16
+
17
+ export function injectStyleOnce(
18
+ id: string,
19
+ styles: string,
20
+ target?: StyleTarget,
21
+ ) {
22
+ const root = resolveStyleRoot(target);
23
+ if (hasStyle(root, id)) return;
24
+
25
+ const doc = root instanceof Document ? root : (root.ownerDocument ?? document);
26
+ const style = doc.createElement('style');
5
27
  style.id = id;
6
28
  style.textContent = styles;
7
- document.head.appendChild(style);
29
+
30
+ if (root instanceof Document) {
31
+ root.head.appendChild(style);
32
+ } else {
33
+ root.appendChild(style);
34
+ }
8
35
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.2.17';
1
+ export const VERSION = '0.2.18';