@antv/infographic 0.2.16 → 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 (131) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/infographic.min.js +121 -120
  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 +7 -4
  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 +229 -3
  32. package/esm/options/parser.js +8 -6
  33. package/esm/options/types.d.ts +3 -3
  34. package/esm/renderer/renderer.js +1 -1
  35. package/esm/resource/loaders/search.js +2 -6
  36. package/esm/runtime/options.js +1 -1
  37. package/esm/syntax/index.js +56 -10
  38. package/esm/syntax/mapper.js +20 -6
  39. package/esm/syntax/parser.js +9 -0
  40. package/esm/syntax/types.d.ts +1 -1
  41. package/esm/templates/registry.d.ts +1 -0
  42. package/esm/templates/registry.js +6 -0
  43. package/esm/templates/utils.d.ts +1 -0
  44. package/esm/templates/utils.js +68 -0
  45. package/esm/themes/built-in.js +3 -0
  46. package/esm/utils/padding.js +1 -1
  47. package/esm/utils/style.d.ts +3 -1
  48. package/esm/utils/style.js +27 -4
  49. package/esm/version.d.ts +1 -1
  50. package/esm/version.js +1 -1
  51. package/lib/constants/service.d.ts +1 -1
  52. package/lib/constants/service.js +1 -1
  53. package/lib/designs/structures/chart-line.js +7 -4
  54. package/lib/editor/interactions/dblclick-edit-text.js +3 -3
  55. package/lib/editor/managers/interaction.js +7 -5
  56. package/lib/editor/plugins/components/button.d.ts +2 -1
  57. package/lib/editor/plugins/components/button.js +4 -4
  58. package/lib/editor/plugins/components/color-picker.d.ts +1 -0
  59. package/lib/editor/plugins/components/color-picker.js +3 -3
  60. package/lib/editor/plugins/components/popover.d.ts +3 -1
  61. package/lib/editor/plugins/components/popover.js +32 -12
  62. package/lib/editor/plugins/edit-bar/edit-bar.d.ts +3 -1
  63. package/lib/editor/plugins/edit-bar/edit-bar.js +17 -7
  64. package/lib/editor/plugins/edit-bar/edit-items/align-elements.js +6 -4
  65. package/lib/editor/plugins/edit-bar/edit-items/font-align.js +8 -5
  66. package/lib/editor/plugins/edit-bar/edit-items/font-color.js +7 -4
  67. package/lib/editor/plugins/edit-bar/edit-items/font-family.js +11 -9
  68. package/lib/editor/plugins/edit-bar/edit-items/font-size.js +8 -5
  69. package/lib/editor/plugins/edit-bar/edit-items/icon-color.js +7 -4
  70. package/lib/editor/plugins/edit-bar/edit-items/types.d.ts +5 -1
  71. package/lib/editor/plugins/reset-viewbox.d.ts +4 -1
  72. package/lib/editor/plugins/reset-viewbox.js +12 -6
  73. package/lib/editor/utils/index.d.ts +1 -0
  74. package/lib/editor/utils/index.js +1 -0
  75. package/lib/editor/utils/root.d.ts +3 -0
  76. package/lib/editor/utils/root.js +22 -0
  77. package/lib/exporter/svg.js +229 -3
  78. package/lib/options/parser.js +7 -5
  79. package/lib/options/types.d.ts +3 -3
  80. package/lib/renderer/renderer.js +1 -1
  81. package/lib/resource/loaders/search.js +2 -6
  82. package/lib/runtime/options.js +1 -1
  83. package/lib/syntax/index.js +56 -10
  84. package/lib/syntax/mapper.js +20 -6
  85. package/lib/syntax/parser.js +9 -0
  86. package/lib/syntax/types.d.ts +1 -1
  87. package/lib/templates/registry.d.ts +1 -0
  88. package/lib/templates/registry.js +7 -0
  89. package/lib/templates/utils.d.ts +1 -0
  90. package/lib/templates/utils.js +71 -0
  91. package/lib/themes/built-in.js +3 -0
  92. package/lib/utils/padding.js +1 -1
  93. package/lib/utils/style.d.ts +3 -1
  94. package/lib/utils/style.js +27 -4
  95. package/lib/version.d.ts +1 -1
  96. package/lib/version.js +1 -1
  97. package/package.json +1 -1
  98. package/src/constants/service.ts +1 -1
  99. package/src/designs/structures/chart-line.tsx +8 -4
  100. package/src/editor/interactions/dblclick-edit-text.ts +3 -2
  101. package/src/editor/managers/interaction.ts +9 -7
  102. package/src/editor/plugins/components/button.ts +5 -2
  103. package/src/editor/plugins/components/color-picker.ts +4 -2
  104. package/src/editor/plugins/components/popover.ts +31 -12
  105. package/src/editor/plugins/edit-bar/edit-bar.ts +26 -11
  106. package/src/editor/plugins/edit-bar/edit-items/align-elements.ts +7 -2
  107. package/src/editor/plugins/edit-bar/edit-items/font-align.ts +8 -3
  108. package/src/editor/plugins/edit-bar/edit-items/font-color.ts +7 -2
  109. package/src/editor/plugins/edit-bar/edit-items/font-family.ts +11 -7
  110. package/src/editor/plugins/edit-bar/edit-items/font-size.ts +8 -3
  111. package/src/editor/plugins/edit-bar/edit-items/icon-color.ts +7 -2
  112. package/src/editor/plugins/edit-bar/edit-items/types.ts +6 -1
  113. package/src/editor/plugins/reset-viewbox.ts +17 -8
  114. package/src/editor/utils/index.ts +1 -0
  115. package/src/editor/utils/root.ts +26 -0
  116. package/src/exporter/svg.ts +274 -3
  117. package/src/options/parser.ts +7 -6
  118. package/src/options/types.ts +3 -3
  119. package/src/renderer/renderer.ts +1 -1
  120. package/src/resource/loaders/search.ts +2 -5
  121. package/src/runtime/options.ts +1 -1
  122. package/src/syntax/index.ts +71 -10
  123. package/src/syntax/mapper.ts +20 -6
  124. package/src/syntax/parser.ts +10 -0
  125. package/src/syntax/types.ts +1 -0
  126. package/src/templates/registry.ts +6 -0
  127. package/src/templates/utils.ts +111 -0
  128. package/src/themes/built-in.ts +4 -0
  129. package/src/utils/padding.ts +1 -1
  130. package/src/utils/style.ts +31 -4
  131. package/src/version.ts +1 -1
@@ -1,3 +1,4 @@
1
+ import { eventPathContains, getOverlayContainer } from '../../utils';
1
2
  import { COMPONENT_ROLE } from '../../../constants';
2
3
  import { injectStyleOnce, setElementRole } from '../../../utils';
3
4
 
@@ -7,7 +8,7 @@ export type PopoverPlacementPreference = PopoverPlacement | PopoverPlacement[];
7
8
  export interface PopoverProps {
8
9
  content: HTMLElement | string | (() => HTMLElement | string);
9
10
  target?: HTMLElement;
10
- getContainer?: HTMLElement | (() => HTMLElement);
11
+ getContainer?: OverlayRoot | (() => OverlayRoot);
11
12
  placement?: PopoverPlacementPreference;
12
13
  padding?: number | string;
13
14
  open?: boolean;
@@ -18,6 +19,8 @@ export interface PopoverProps {
18
19
  offset?: number;
19
20
  }
20
21
 
22
+ type OverlayRoot = HTMLElement | ShadowRoot;
23
+
21
24
  export interface PopoverHandle {
22
25
  setOpen: (open: boolean) => void;
23
26
  toggle: () => void;
@@ -32,8 +35,6 @@ const POPOVER_ARROW_CLASS = `${POPOVER_CLASS}__arrow`;
32
35
  const POPOVER_STYLE_ID = 'infographic-edit-popover-style';
33
36
 
34
37
  export function Popover(props: PopoverProps): HTMLDivElement & PopoverHandle {
35
- ensurePopoverStyle();
36
-
37
38
  const placement = props.placement ?? 'top';
38
39
  const closeOnOutsideClick = props.closeOnOutsideClick ?? true;
39
40
  const triggerActions = Array.isArray(props.trigger)
@@ -60,7 +61,7 @@ export function Popover(props: PopoverProps): HTMLDivElement & PopoverHandle {
60
61
  typeof props.getContainer === 'function'
61
62
  ? props.getContainer()
62
63
  : props.getContainer;
63
- return next ?? document.body;
64
+ return next ?? getOverlayContainer(trigger);
64
65
  };
65
66
 
66
67
  const content = document.createElement('div');
@@ -78,8 +79,17 @@ export function Popover(props: PopoverProps): HTMLDivElement & PopoverHandle {
78
79
  );
79
80
 
80
81
  const contentContainer = getContentContainer();
82
+ ensurePopoverStyle(contentContainer);
81
83
  const isPortal = contentContainer !== container;
82
84
 
85
+ const getPortalOffsetParent = () => {
86
+ const offsetParent = content.offsetParent as HTMLElement | null;
87
+ if (offsetParent) return offsetParent;
88
+ if (contentContainer instanceof ShadowRoot) return contentContainer.host;
89
+ if (contentContainer instanceof HTMLElement) return contentContainer;
90
+ return document.documentElement as HTMLElement;
91
+ };
92
+
83
93
  const arrow = document.createElement('div');
84
94
  arrow.classList.add(POPOVER_ARROW_CLASS);
85
95
  content.appendChild(arrow);
@@ -170,8 +180,18 @@ export function Popover(props: PopoverProps): HTMLDivElement & PopoverHandle {
170
180
  if (!isPortal) return;
171
181
 
172
182
  ({ left, top } = position);
173
- content.style.left = `${left}px`;
174
- content.style.top = `${top}px`;
183
+ const offsetParent = getPortalOffsetParent();
184
+ if (
185
+ offsetParent === document.body ||
186
+ offsetParent === document.documentElement
187
+ ) {
188
+ content.style.left = `${left}px`;
189
+ content.style.top = `${top}px`;
190
+ } else {
191
+ const parentRect = offsetParent.getBoundingClientRect();
192
+ content.style.left = `${left - parentRect.left + offsetParent.scrollLeft}px`;
193
+ content.style.top = `${top - parentRect.top + offsetParent.scrollTop}px`;
194
+ }
175
195
  content.style.right = 'auto';
176
196
  content.style.bottom = 'auto';
177
197
  content.style.transform = 'translate(0, 0)';
@@ -217,11 +237,9 @@ export function Popover(props: PopoverProps): HTMLDivElement & PopoverHandle {
217
237
  const toggle = () => setOpen(!open);
218
238
 
219
239
  const handleOutsideClick = (event: MouseEvent) => {
220
- const targetNode = event.target as Node;
221
- if (
222
- !container.contains(targetNode) &&
223
- (isPortal ? !content.contains(targetNode) : true)
224
- ) {
240
+ const insideTrigger = eventPathContains(event, container);
241
+ const insideContent = eventPathContains(event, content);
242
+ if (!insideTrigger && !insideContent) {
225
243
  setOpen(false);
226
244
  }
227
245
  };
@@ -296,7 +314,7 @@ export function Popover(props: PopoverProps): HTMLDivElement & PopoverHandle {
296
314
  return Object.assign(container, api);
297
315
  }
298
316
 
299
- function ensurePopoverStyle() {
317
+ function ensurePopoverStyle(target?: Node) {
300
318
  injectStyleOnce(
301
319
  POPOVER_STYLE_ID,
302
320
  `
@@ -442,5 +460,6 @@ function ensurePopoverStyle() {
442
460
  transform: translate(0, -50%);
443
461
  }
444
462
  `,
463
+ target,
445
464
  );
446
465
  }
@@ -10,6 +10,7 @@ import {
10
10
  isIconElement,
11
11
  setElementRole,
12
12
  } from '../../../utils';
13
+ import { getOverlayContainer } from '../../utils';
13
14
  import type {
14
15
  IPlugin,
15
16
  PluginInitOptions,
@@ -29,9 +30,11 @@ import {
29
30
  export interface EditBarOptions {
30
31
  style?: Partial<CSSStyleDeclaration>;
31
32
  className?: string;
32
- getContainer?: HTMLElement | (() => HTMLElement);
33
+ getContainer?: OverlayRoot | (() => OverlayRoot);
33
34
  }
34
35
 
36
+ type OverlayRoot = HTMLElement | ShadowRoot;
37
+
35
38
  type EditItem = HTMLElement;
36
39
 
37
40
  export class EditBar extends Plugin implements IPlugin {
@@ -169,12 +172,7 @@ export class EditBar extends Plugin implements IPlugin {
169
172
  setElementRole(container, COMPONENT_ROLE);
170
173
 
171
174
  this.container = container;
172
-
173
- const { getContainer } = this.options || {};
174
- const resolvedContainer =
175
- typeof getContainer === 'function' ? getContainer() : getContainer;
176
- const containerParent = resolvedContainer ?? document.body;
177
-
175
+ const containerParent = this.resolveOverlayRoot();
178
176
  containerParent?.appendChild(container);
179
177
 
180
178
  return container;
@@ -182,8 +180,9 @@ export class EditBar extends Plugin implements IPlugin {
182
180
 
183
181
  protected getTextEditItems(text: TextElement): EditItem[] {
184
182
  const { attributes = {} } = getTextElementProps(text);
183
+ const root = this.resolveOverlayRoot();
185
184
  return [FontColor, FontSize, FontAlign, FontFamily].map((item) =>
186
- item([text], attributes, this.commander),
185
+ item([text], attributes, this.commander, { root }),
187
186
  );
188
187
  }
189
188
 
@@ -191,8 +190,9 @@ export class EditBar extends Plugin implements IPlugin {
191
190
  const attrs = getCommonAttrs(
192
191
  selection.map((text) => getTextElementProps(text).attributes || {}),
193
192
  );
193
+ const root = this.resolveOverlayRoot();
194
194
  const items = [FontColor, FontSize, FontAlign, FontFamily].map((item) =>
195
- item(selection, attrs, this.commander),
195
+ item(selection, attrs, this.commander, { root }),
196
196
  );
197
197
  const commonItems = this.getElementCollectionEditItems(selection);
198
198
  return [...items, ...commonItems];
@@ -200,13 +200,19 @@ export class EditBar extends Plugin implements IPlugin {
200
200
 
201
201
  protected getIconEditItems(selection: Selection): EditItem[] {
202
202
  const attrs = getIconAttrs(selection[0] as IconElement);
203
- return [IconColor].map((item) => item(selection, attrs, this.commander));
203
+ const root = this.resolveOverlayRoot();
204
+ return [IconColor].map((item) =>
205
+ item(selection, attrs, this.commander, { root }),
206
+ );
204
207
  }
205
208
  protected getIconCollectionEditItems(selection: Selection): EditItem[] {
206
209
  const attrs = getCommonAttrs(
207
210
  selection.map((icon) => getIconAttrs(icon as IconElement)),
208
211
  );
209
- return [IconColor].map((item) => item(selection, attrs, this.commander));
212
+ const root = this.resolveOverlayRoot();
213
+ return [IconColor].map((item) =>
214
+ item(selection, attrs, this.commander, { root }),
215
+ );
210
216
  }
211
217
 
212
218
  protected getGeometryEditItems(_selection: Selection): EditItem[] {
@@ -220,13 +226,22 @@ export class EditBar extends Plugin implements IPlugin {
220
226
 
221
227
  protected getElementCollectionEditItems(selection: Selection): EditItem[] {
222
228
  if (selection.length <= 1) return [];
229
+ const root = this.resolveOverlayRoot();
223
230
  return [
224
231
  ElementAlign(selection, {}, this.commander, {
225
232
  enableDistribution: selection.length > 2,
233
+ root,
226
234
  }),
227
235
  ];
228
236
  }
229
237
 
238
+ private resolveOverlayRoot(): OverlayRoot {
239
+ const { getContainer } = this.options || {};
240
+ const resolvedContainer =
241
+ typeof getContainer === 'function' ? getContainer() : getContainer;
242
+ return resolvedContainer ?? getOverlayContainer(this.editor.getDocument());
243
+ }
244
+
230
245
  private placeEditBar(container: HTMLDivElement, selection: Selection) {
231
246
  if (selection.length === 0) return;
232
247
 
@@ -40,6 +40,7 @@ type AlignAction =
40
40
 
41
41
  type AlignOptions = {
42
42
  enableDistribution?: boolean;
43
+ root?: HTMLElement | ShadowRoot;
43
44
  };
44
45
 
45
46
  type TransformParts = {
@@ -79,17 +80,19 @@ export const ElementAlign: EditItem = (
79
80
  commander: ICommandManager,
80
81
  options?: AlignOptions,
81
82
  ) => {
82
- injectStyleOnce(GRID_STYLE_ID, GRID_STYLES);
83
+ injectStyleOnce(GRID_STYLE_ID, GRID_STYLES, options?.root);
83
84
  const enableDistribution = options?.enableDistribution ?? true;
84
85
 
85
86
  const content = createAlignContent(
86
87
  (action) => alignSelection(selection, action, commander),
87
88
  enableDistribution,
89
+ options?.root,
88
90
  );
89
91
 
90
92
  return Popover({
91
- target: IconButton({ icon: ELEMENT_ICONS.align }),
93
+ target: IconButton({ icon: ELEMENT_ICONS.align, root: options?.root }),
92
94
  content,
95
+ getContainer: options?.root,
93
96
  placement: 'top',
94
97
  offset: 12,
95
98
  });
@@ -98,6 +101,7 @@ export const ElementAlign: EditItem = (
98
101
  function createAlignContent(
99
102
  onSelect: (action: AlignAction) => void,
100
103
  enableDistribution: boolean,
104
+ root?: Node,
101
105
  ) {
102
106
  const content = document.createElement('div');
103
107
  content.classList.add(GRID_CLASS);
@@ -111,6 +115,7 @@ function createAlignContent(
111
115
 
112
116
  visibleOptions.forEach(({ icon, action }) => {
113
117
  const button = IconButton({
118
+ root,
114
119
  icon,
115
120
  onClick: () => onSelect(action),
116
121
  });
@@ -48,15 +48,17 @@ export const FontAlign: EditItem<TextAttributes> = (
48
48
  selection,
49
49
  attrs,
50
50
  commander,
51
+ options,
51
52
  ) => {
52
- injectStyleOnce(GRID_STYLE_ID, GRID_STYLES);
53
+ const root = options?.root;
54
+ injectStyleOnce(GRID_STYLE_ID, GRID_STYLES, root);
53
55
 
54
56
  const state: AlignState = {
55
57
  horizontal: attrs['data-horizontal-align'],
56
58
  vertical: attrs['data-vertical-align'],
57
59
  };
58
60
 
59
- const button = IconButton({ icon: TEXT_ICONS.align });
61
+ const button = IconButton({ icon: TEXT_ICONS.align, root });
60
62
 
61
63
  const content = createAlignContent(state, (align) => {
62
64
  const attributes: Partial<TextAttributes> = {};
@@ -74,11 +76,12 @@ export const FontAlign: EditItem<TextAttributes> = (
74
76
  }),
75
77
  ),
76
78
  );
77
- });
79
+ }, root);
78
80
 
79
81
  return Popover({
80
82
  target: button,
81
83
  content,
84
+ getContainer: root,
82
85
  placement: 'top',
83
86
  offset: 12,
84
87
  });
@@ -87,6 +90,7 @@ export const FontAlign: EditItem<TextAttributes> = (
87
90
  function createAlignContent(
88
91
  state: AlignState,
89
92
  onAlignChange: (align: AlignState) => void,
93
+ root?: Node,
90
94
  ) {
91
95
  const content = document.createElement('div');
92
96
  content.classList.add(GRID_CLASS);
@@ -106,6 +110,7 @@ function createAlignContent(
106
110
  ) => {
107
111
  options.forEach(({ icon, align }) => {
108
112
  const button = IconButton({
113
+ root,
109
114
  icon,
110
115
  onClick: () => {
111
116
  if (state[stateKey] === align) return;
@@ -12,8 +12,10 @@ export const FontColor: EditItem<TextAttributes> = (
12
12
  selection,
13
13
  attrs,
14
14
  commander,
15
+ options,
15
16
  ) => {
16
- ensureFontColorStyles();
17
+ const root = options?.root;
18
+ ensureFontColorStyles(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 FontColor: EditItem<TextAttributes> = (
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 FontColor: EditItem<TextAttributes> = (
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 ensureFontColorStyles() {
72
+ function ensureFontColorStyles(target?: Node) {
69
73
  injectStyleOnce(
70
74
  FONT_COLOR_STYLE_ID,
71
75
  `
@@ -97,5 +101,6 @@ function ensureFontColorStyles() {
97
101
  );
98
102
  }
99
103
  `,
104
+ target,
100
105
  );
101
106
  }
@@ -18,27 +18,29 @@ export const FontFamily: EditItem<TextAttributes> = (
18
18
  selection,
19
19
  attrs,
20
20
  commander,
21
+ editItemOptions,
21
22
  ) => {
22
- ensureFontFamilyListStyle();
23
+ const root = editItemOptions?.root;
24
+ ensureFontFamilyListStyle(root);
23
25
 
24
26
  const fonts = getFonts();
25
27
  const current = normalizeFontFamily(attrs['font-family']);
26
28
 
27
- const options = fonts.map((font) => ({
29
+ const fontOptions = fonts.map((font) => ({
28
30
  label: font.name || font.fontFamily,
29
31
  value: font.fontFamily,
30
32
  }));
31
33
  if (
32
- !options.some((option) => normalizeFontFamily(option.value) === current)
34
+ !fontOptions.some((option) => normalizeFontFamily(option.value) === current)
33
35
  ) {
34
- options.unshift({
36
+ fontOptions.unshift({
35
37
  label: DEFAULT_FONT_LABEL,
36
38
  value: current,
37
39
  });
38
40
  }
39
41
 
40
42
  let selected = current;
41
- const content = createFontList(options, selected, (value) => {
43
+ const content = createFontList(fontOptions, selected, (value) => {
42
44
  if (selected === value) return;
43
45
  selected = value;
44
46
  commander.executeBatch(
@@ -51,11 +53,12 @@ export const FontFamily: EditItem<TextAttributes> = (
51
53
  );
52
54
  });
53
55
 
54
- const button = IconButton({ icon: TEXT_ICONS.fontFamily });
56
+ const button = IconButton({ icon: TEXT_ICONS.fontFamily, root });
55
57
 
56
58
  const popover = Popover({
57
59
  target: button,
58
60
  content,
61
+ getContainer: root,
59
62
  placement: ['top', 'bottom'],
60
63
  offset: 12,
61
64
  trigger: 'hover',
@@ -110,7 +113,7 @@ function normalizeFontFamily(font: TextAttributes['font-family']) {
110
113
  return encodeFontFamily(String(font));
111
114
  }
112
115
 
113
- function ensureFontFamilyListStyle() {
116
+ function ensureFontFamilyListStyle(target?: Node) {
114
117
  injectStyleOnce(
115
118
  FONT_LIST_STYLE_ID,
116
119
  `
@@ -143,5 +146,6 @@ function ensureFontFamilyListStyle() {
143
146
  color: #0958d9;
144
147
  }
145
148
  `,
149
+ target,
146
150
  );
147
151
  }
@@ -30,10 +30,12 @@ export const FontSize: EditItem<TextAttributes> = (
30
30
  selection,
31
31
  attrs,
32
32
  commander,
33
+ options,
33
34
  ) => {
34
- injectStyleOnce(FONT_SIZE_STYLE_ID, FONT_SIZE_STYLES);
35
+ const root = options?.root;
36
+ injectStyleOnce(FONT_SIZE_STYLE_ID, FONT_SIZE_STYLES, root);
35
37
 
36
- const button = IconButton({ icon: TEXT_ICONS.fontSize });
38
+ const button = IconButton({ icon: TEXT_ICONS.fontSize, root });
37
39
  const currentSize = normalizeFontSize(attrs['font-size']);
38
40
  const content = createFontSizeContent(currentSize, (size) => {
39
41
  commander.executeBatch(
@@ -44,11 +46,12 @@ export const FontSize: EditItem<TextAttributes> = (
44
46
  }),
45
47
  ),
46
48
  );
47
- });
49
+ }, root);
48
50
 
49
51
  return Popover({
50
52
  target: button,
51
53
  content,
54
+ getContainer: root,
52
55
  placement: 'top',
53
56
  offset: 12,
54
57
  });
@@ -57,6 +60,7 @@ export const FontSize: EditItem<TextAttributes> = (
57
60
  function createFontSizeContent(
58
61
  defaultSize: number,
59
62
  onSizeChange: (size: number) => void,
63
+ root?: Node,
60
64
  ) {
61
65
  const content = document.createElement('div');
62
66
  content.classList.add(FONT_SIZE_CLASS);
@@ -73,6 +77,7 @@ function createFontSizeContent(
73
77
 
74
78
  FONT_SIZE_OPTIONS.forEach(({ label, value }) => {
75
79
  const button = IconButton({
80
+ root,
76
81
  icon: createLabelIcon(label),
77
82
  onClick: () => {
78
83
  if (selected === value) return;
@@ -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
+ }