@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.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/infographic.min.js +110 -110
- package/dist/infographic.min.js.map +1 -1
- package/esm/constants/service.d.ts +1 -1
- package/esm/constants/service.js +1 -1
- package/esm/designs/structures/chart-line.js +5 -3
- package/esm/editor/interactions/dblclick-edit-text.js +3 -3
- package/esm/editor/managers/interaction.js +6 -4
- package/esm/editor/plugins/components/button.d.ts +2 -1
- package/esm/editor/plugins/components/button.js +4 -4
- package/esm/editor/plugins/components/color-picker.d.ts +1 -0
- package/esm/editor/plugins/components/color-picker.js +3 -3
- package/esm/editor/plugins/components/popover.d.ts +3 -1
- package/esm/editor/plugins/components/popover.js +29 -9
- package/esm/editor/plugins/edit-bar/edit-bar.d.ts +3 -1
- package/esm/editor/plugins/edit-bar/edit-bar.js +17 -7
- package/esm/editor/plugins/edit-bar/edit-items/align-elements.js +6 -4
- package/esm/editor/plugins/edit-bar/edit-items/font-align.js +8 -5
- package/esm/editor/plugins/edit-bar/edit-items/font-color.js +7 -4
- package/esm/editor/plugins/edit-bar/edit-items/font-family.js +11 -9
- package/esm/editor/plugins/edit-bar/edit-items/font-size.js +8 -5
- package/esm/editor/plugins/edit-bar/edit-items/icon-color.js +7 -4
- package/esm/editor/plugins/edit-bar/edit-items/types.d.ts +5 -1
- package/esm/editor/plugins/reset-viewbox.d.ts +4 -1
- package/esm/editor/plugins/reset-viewbox.js +12 -6
- package/esm/editor/utils/index.d.ts +1 -0
- package/esm/editor/utils/index.js +1 -0
- package/esm/editor/utils/root.d.ts +3 -0
- package/esm/editor/utils/root.js +18 -0
- package/esm/exporter/svg.js +192 -52
- package/esm/resource/loaders/search.js +0 -3
- package/esm/templates/utils.js +11 -6
- package/esm/utils/padding.js +1 -1
- package/esm/utils/style.d.ts +3 -1
- package/esm/utils/style.js +27 -4
- package/esm/version.d.ts +1 -1
- package/esm/version.js +1 -1
- package/lib/constants/service.d.ts +1 -1
- package/lib/constants/service.js +1 -1
- package/lib/designs/structures/chart-line.js +5 -3
- package/lib/editor/interactions/dblclick-edit-text.js +3 -3
- package/lib/editor/managers/interaction.js +7 -5
- package/lib/editor/plugins/components/button.d.ts +2 -1
- package/lib/editor/plugins/components/button.js +4 -4
- package/lib/editor/plugins/components/color-picker.d.ts +1 -0
- package/lib/editor/plugins/components/color-picker.js +3 -3
- package/lib/editor/plugins/components/popover.d.ts +3 -1
- package/lib/editor/plugins/components/popover.js +32 -12
- package/lib/editor/plugins/edit-bar/edit-bar.d.ts +3 -1
- package/lib/editor/plugins/edit-bar/edit-bar.js +17 -7
- package/lib/editor/plugins/edit-bar/edit-items/align-elements.js +6 -4
- package/lib/editor/plugins/edit-bar/edit-items/font-align.js +8 -5
- package/lib/editor/plugins/edit-bar/edit-items/font-color.js +7 -4
- package/lib/editor/plugins/edit-bar/edit-items/font-family.js +11 -9
- package/lib/editor/plugins/edit-bar/edit-items/font-size.js +8 -5
- package/lib/editor/plugins/edit-bar/edit-items/icon-color.js +7 -4
- package/lib/editor/plugins/edit-bar/edit-items/types.d.ts +5 -1
- package/lib/editor/plugins/reset-viewbox.d.ts +4 -1
- package/lib/editor/plugins/reset-viewbox.js +12 -6
- package/lib/editor/utils/index.d.ts +1 -0
- package/lib/editor/utils/index.js +1 -0
- package/lib/editor/utils/root.d.ts +3 -0
- package/lib/editor/utils/root.js +22 -0
- package/lib/exporter/svg.js +192 -52
- package/lib/resource/loaders/search.js +0 -3
- package/lib/templates/utils.js +11 -6
- package/lib/utils/padding.js +1 -1
- package/lib/utils/style.d.ts +3 -1
- package/lib/utils/style.js +27 -4
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/src/constants/service.ts +1 -1
- package/src/designs/structures/chart-line.tsx +5 -3
- package/src/editor/interactions/dblclick-edit-text.ts +3 -2
- package/src/editor/managers/interaction.ts +9 -7
- package/src/editor/plugins/components/button.ts +5 -2
- package/src/editor/plugins/components/color-picker.ts +4 -2
- package/src/editor/plugins/components/popover.ts +31 -12
- package/src/editor/plugins/edit-bar/edit-bar.ts +26 -11
- package/src/editor/plugins/edit-bar/edit-items/align-elements.ts +7 -2
- package/src/editor/plugins/edit-bar/edit-items/font-align.ts +8 -3
- package/src/editor/plugins/edit-bar/edit-items/font-color.ts +7 -2
- package/src/editor/plugins/edit-bar/edit-items/font-family.ts +11 -7
- package/src/editor/plugins/edit-bar/edit-items/font-size.ts +8 -3
- package/src/editor/plugins/edit-bar/edit-items/icon-color.ts +7 -2
- package/src/editor/plugins/edit-bar/edit-items/types.ts +6 -1
- package/src/editor/plugins/reset-viewbox.ts +17 -8
- package/src/editor/utils/index.ts +1 -0
- package/src/editor/utils/root.ts +26 -0
- package/src/exporter/svg.ts +267 -62
- package/src/resource/loaders/search.ts +0 -3
- package/src/templates/utils.ts +30 -6
- package/src/utils/padding.ts +1 -1
- package/src/utils/style.ts +31 -4
- 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?:
|
|
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 ??
|
|
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
|
-
|
|
174
|
-
|
|
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
|
|
221
|
-
|
|
222
|
-
|
|
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?:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
29
|
+
const fontOptions = fonts.map((font) => ({
|
|
28
30
|
label: font.name || font.fontFamily,
|
|
29
31
|
value: font.fontFamily,
|
|
30
32
|
}));
|
|
31
33
|
if (
|
|
32
|
-
!
|
|
34
|
+
!fontOptions.some((option) => normalizeFontFamily(option.value) === current)
|
|
33
35
|
) {
|
|
34
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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?:
|
|
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?:
|
|
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
|
|
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
|
}
|
|
@@ -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
|
+
}
|