@antv/infographic 0.2.4 → 0.2.6
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 +2 -2
- package/README.zh-CN.md +2 -2
- package/dist/infographic.min.js +88 -78
- package/dist/infographic.min.js.map +1 -1
- package/esm/exporter/index.d.ts +1 -1
- package/esm/exporter/index.js +1 -1
- package/esm/exporter/svg.js +223 -2
- package/esm/exporter/types.d.ts +5 -0
- package/esm/index.d.ts +4 -2
- package/esm/index.js +3 -2
- package/esm/renderer/fonts/loader.js +63 -8
- package/esm/renderer/stylize/gradient.js +1 -1
- package/esm/resource/index.d.ts +1 -1
- package/esm/resource/index.js +1 -1
- package/esm/resource/load-tracker.d.ts +6 -0
- package/esm/resource/load-tracker.js +36 -0
- package/esm/resource/loader.d.ts +1 -0
- package/esm/resource/loader.js +27 -14
- package/esm/runtime/Infographic.js +13 -0
- package/esm/utils/is-browser.d.ts +1 -0
- package/esm/utils/is-browser.js +68 -0
- package/esm/utils/measure-text.d.ts +1 -0
- package/esm/utils/measure-text.js +9 -7
- package/lib/exporter/index.d.ts +1 -1
- package/lib/exporter/index.js +2 -1
- package/lib/exporter/svg.js +223 -2
- package/lib/exporter/types.d.ts +5 -0
- package/lib/index.d.ts +4 -2
- package/lib/index.js +6 -3
- package/lib/renderer/fonts/loader.js +63 -8
- package/lib/renderer/stylize/gradient.js +1 -1
- package/lib/resource/index.d.ts +1 -1
- package/lib/resource/index.js +3 -1
- package/lib/resource/load-tracker.d.ts +6 -0
- package/lib/resource/load-tracker.js +42 -0
- package/lib/resource/loader.d.ts +1 -0
- package/lib/resource/loader.js +30 -14
- package/lib/runtime/Infographic.js +13 -0
- package/lib/utils/is-browser.d.ts +1 -0
- package/lib/utils/is-browser.js +71 -0
- package/lib/utils/measure-text.d.ts +1 -0
- package/lib/utils/measure-text.js +11 -7
- package/package.json +1 -1
- package/src/exporter/index.ts +1 -1
- package/src/exporter/svg.ts +254 -2
- package/src/exporter/types.ts +5 -0
- package/src/index.ts +8 -3
- package/src/renderer/fonts/loader.ts +82 -9
- package/src/renderer/stylize/gradient.ts +1 -1
- package/src/resource/index.ts +1 -1
- package/src/resource/load-tracker.ts +51 -0
- package/src/resource/loader.ts +27 -12
- package/src/runtime/Infographic.tsx +12 -0
- package/src/utils/is-browser.ts +79 -0
- package/src/utils/measure-text.ts +11 -7
|
@@ -2,16 +2,18 @@ import { measureText as measure, registerFont } from 'measury';
|
|
|
2
2
|
import AlibabaPuHuiTi from 'measury/fonts/AlibabaPuHuiTi-Regular';
|
|
3
3
|
import { DEFAULT_FONT } from '../renderer';
|
|
4
4
|
import { encodeFontFamily } from './font';
|
|
5
|
+
import { isBrowser } from './is-browser';
|
|
5
6
|
import { isNode } from './is-node';
|
|
7
|
+
let FONT_EXTEND_FACTOR = 1.01;
|
|
8
|
+
export const setFontExtendFactor = (factor) => {
|
|
9
|
+
FONT_EXTEND_FACTOR = factor;
|
|
10
|
+
};
|
|
6
11
|
if (isNode) {
|
|
7
12
|
registerFont(AlibabaPuHuiTi);
|
|
8
13
|
}
|
|
9
|
-
const canUseDOM = !isNode && typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
10
14
|
let canvasContext = null;
|
|
11
15
|
let measureSpan = null;
|
|
12
16
|
function getCanvasContext() {
|
|
13
|
-
if (!canUseDOM)
|
|
14
|
-
return null;
|
|
15
17
|
if (canvasContext)
|
|
16
18
|
return canvasContext;
|
|
17
19
|
const canvas = document.createElement('canvas');
|
|
@@ -19,7 +21,7 @@ function getCanvasContext() {
|
|
|
19
21
|
return canvasContext;
|
|
20
22
|
}
|
|
21
23
|
function getMeasureSpan() {
|
|
22
|
-
if (!
|
|
24
|
+
if (!document.body)
|
|
23
25
|
return null;
|
|
24
26
|
if (measureSpan)
|
|
25
27
|
return measureSpan;
|
|
@@ -91,12 +93,12 @@ export function measureText(text = '', attrs) {
|
|
|
91
93
|
lineHeight,
|
|
92
94
|
};
|
|
93
95
|
const fallback = () => measure(content, options);
|
|
94
|
-
const metrics =
|
|
96
|
+
const metrics = isBrowser()
|
|
95
97
|
? (measureTextInBrowser(content, options) ?? fallback())
|
|
96
98
|
: fallback();
|
|
97
99
|
// 额外添加 1% 宽高
|
|
98
100
|
return {
|
|
99
|
-
width: Math.ceil(metrics.width *
|
|
100
|
-
height: Math.ceil(metrics.height *
|
|
101
|
+
width: Math.ceil(metrics.width * FONT_EXTEND_FACTOR),
|
|
102
|
+
height: Math.ceil(metrics.height * FONT_EXTEND_FACTOR),
|
|
101
103
|
};
|
|
102
104
|
}
|
package/lib/exporter/index.d.ts
CHANGED
package/lib/exporter/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.exportToSVGString = exports.exportToPNGString = void 0;
|
|
3
|
+
exports.exportToSVGString = exports.exportToSVG = exports.exportToPNGString = void 0;
|
|
4
4
|
var png_1 = require("./png");
|
|
5
5
|
Object.defineProperty(exports, "exportToPNGString", { enumerable: true, get: function () { return png_1.exportToPNGString; } });
|
|
6
6
|
var svg_1 = require("./svg");
|
|
7
|
+
Object.defineProperty(exports, "exportToSVG", { enumerable: true, get: function () { return svg_1.exportToSVG; } });
|
|
7
8
|
Object.defineProperty(exports, "exportToSVGString", { enumerable: true, get: function () { return svg_1.exportToSVGString; } });
|
package/lib/exporter/svg.js
CHANGED
|
@@ -10,11 +10,17 @@ async function exportToSVGString(svg, options = {}) {
|
|
|
10
10
|
return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(str);
|
|
11
11
|
}
|
|
12
12
|
async function exportToSVG(svg, options = {}) {
|
|
13
|
-
const { embedResources = true } = options;
|
|
13
|
+
const { embedResources = true, removeIds = false } = options;
|
|
14
14
|
const clonedSVG = svg.cloneNode(true);
|
|
15
15
|
const { width, height } = (0, utils_1.getViewBox)(svg);
|
|
16
16
|
(0, utils_1.setAttributes)(clonedSVG, { width, height });
|
|
17
|
-
|
|
17
|
+
if (removeIds) {
|
|
18
|
+
inlineUseElements(clonedSVG);
|
|
19
|
+
inlineDefsReferences(clonedSVG);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
await embedIcons(clonedSVG);
|
|
23
|
+
}
|
|
18
24
|
await (0, font_1.embedFonts)(clonedSVG, embedResources);
|
|
19
25
|
cleanSVG(clonedSVG);
|
|
20
26
|
return clonedSVG;
|
|
@@ -44,6 +50,221 @@ function getDefs(svg) {
|
|
|
44
50
|
svg.prepend(_defs);
|
|
45
51
|
return _defs;
|
|
46
52
|
}
|
|
53
|
+
function inlineUseElements(svg) {
|
|
54
|
+
const uses = Array.from(svg.querySelectorAll('use'));
|
|
55
|
+
if (!uses.length)
|
|
56
|
+
return;
|
|
57
|
+
uses.forEach((use) => {
|
|
58
|
+
const href = getUseHref(use);
|
|
59
|
+
if (!href || !href.startsWith('#'))
|
|
60
|
+
return;
|
|
61
|
+
const target = resolveUseTarget(svg, href);
|
|
62
|
+
if (!target || target === use)
|
|
63
|
+
return;
|
|
64
|
+
const replacement = createInlineElement(use, target);
|
|
65
|
+
if (!replacement)
|
|
66
|
+
return;
|
|
67
|
+
use.replaceWith(replacement);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function getUseHref(use) {
|
|
71
|
+
return use.getAttribute('href') ?? use.getAttribute('xlink:href');
|
|
72
|
+
}
|
|
73
|
+
function resolveUseTarget(svg, href) {
|
|
74
|
+
const localTarget = svg.querySelector(href);
|
|
75
|
+
if (localTarget)
|
|
76
|
+
return localTarget;
|
|
77
|
+
const docTarget = document.querySelector(href);
|
|
78
|
+
return docTarget;
|
|
79
|
+
}
|
|
80
|
+
function createInlineElement(use, target) {
|
|
81
|
+
const tag = target.tagName.toLowerCase();
|
|
82
|
+
if (tag === 'symbol') {
|
|
83
|
+
return materializeSymbol(use, target);
|
|
84
|
+
}
|
|
85
|
+
if (tag === 'svg') {
|
|
86
|
+
return materializeSVG(use, target);
|
|
87
|
+
}
|
|
88
|
+
return materializeElement(use, target);
|
|
89
|
+
}
|
|
90
|
+
function materializeSymbol(use, symbol) {
|
|
91
|
+
const symbolClone = symbol.cloneNode(true);
|
|
92
|
+
const svg = (0, utils_1.createElement)('svg');
|
|
93
|
+
applyAttributes(svg, symbolClone, new Set(['id']));
|
|
94
|
+
applyAttributes(svg, use, new Set(['href', 'xlink:href']));
|
|
95
|
+
while (symbolClone.firstChild) {
|
|
96
|
+
svg.appendChild(symbolClone.firstChild);
|
|
97
|
+
}
|
|
98
|
+
return svg;
|
|
99
|
+
}
|
|
100
|
+
function materializeSVG(use, source) {
|
|
101
|
+
const clone = source.cloneNode(true);
|
|
102
|
+
clone.removeAttribute('id');
|
|
103
|
+
applyAttributes(clone, use, new Set(['href', 'xlink:href']));
|
|
104
|
+
return clone;
|
|
105
|
+
}
|
|
106
|
+
function materializeElement(use, source) {
|
|
107
|
+
const clone = source.cloneNode(true);
|
|
108
|
+
clone.removeAttribute('id');
|
|
109
|
+
const wrapper = (0, utils_1.createElement)('g');
|
|
110
|
+
applyAttributes(wrapper, use, new Set(['href', 'xlink:href', 'x', 'y', 'width', 'height', 'transform']));
|
|
111
|
+
const transform = buildUseTransform(use);
|
|
112
|
+
if (transform) {
|
|
113
|
+
wrapper.setAttribute('transform', transform);
|
|
114
|
+
}
|
|
115
|
+
wrapper.appendChild(clone);
|
|
116
|
+
return wrapper;
|
|
117
|
+
}
|
|
118
|
+
function buildUseTransform(use) {
|
|
119
|
+
const x = use.getAttribute('x');
|
|
120
|
+
const y = use.getAttribute('y');
|
|
121
|
+
const translate = x || y ? `translate(${x ?? 0} ${y ?? 0})` : '';
|
|
122
|
+
const transform = use.getAttribute('transform') ?? '';
|
|
123
|
+
if (translate && transform)
|
|
124
|
+
return `${translate} ${transform}`;
|
|
125
|
+
return translate || transform || null;
|
|
126
|
+
}
|
|
127
|
+
function applyAttributes(target, source, exclude = new Set()) {
|
|
128
|
+
Array.from(source.attributes).forEach((attr) => {
|
|
129
|
+
if (exclude.has(attr.name))
|
|
130
|
+
return;
|
|
131
|
+
if (attr.name === 'style') {
|
|
132
|
+
mergeStyleAttribute(target, attr.value);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (attr.name === 'class') {
|
|
136
|
+
mergeClassAttribute(target, attr.value);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
target.setAttribute(attr.name, attr.value);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function mergeStyleAttribute(target, value) {
|
|
143
|
+
const current = target.getAttribute('style');
|
|
144
|
+
if (!current) {
|
|
145
|
+
target.setAttribute('style', value);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const separator = current.trim().endsWith(';') ? '' : ';';
|
|
149
|
+
target.setAttribute('style', `${current}${separator}${value}`);
|
|
150
|
+
}
|
|
151
|
+
function mergeClassAttribute(target, value) {
|
|
152
|
+
const current = target.getAttribute('class');
|
|
153
|
+
if (!current) {
|
|
154
|
+
target.setAttribute('class', value);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
target.setAttribute('class', `${current} ${value}`.trim());
|
|
158
|
+
}
|
|
159
|
+
const urlRefRegex = /url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/g;
|
|
160
|
+
function inlineDefsReferences(svg) {
|
|
161
|
+
const referencedIds = collectReferencedIds(svg);
|
|
162
|
+
if (referencedIds.size === 0) {
|
|
163
|
+
removeDefs(svg);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const defsDataUrl = createDefsDataUrl(svg, referencedIds);
|
|
167
|
+
if (!defsDataUrl)
|
|
168
|
+
return;
|
|
169
|
+
(0, utils_1.traverse)(svg, (node) => {
|
|
170
|
+
if (node.tagName.toLowerCase() === 'defs')
|
|
171
|
+
return false;
|
|
172
|
+
const attrs = Array.from(node.attributes);
|
|
173
|
+
attrs.forEach((attr) => {
|
|
174
|
+
const value = attr.value;
|
|
175
|
+
if (!value.includes('url('))
|
|
176
|
+
return;
|
|
177
|
+
const updated = value.replace(urlRefRegex, (_match, id) => {
|
|
178
|
+
const encodedId = encodeURIComponent(id);
|
|
179
|
+
return `url("${defsDataUrl}#${encodedId}")`;
|
|
180
|
+
});
|
|
181
|
+
if (updated !== value)
|
|
182
|
+
node.setAttribute(attr.name, updated);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
removeDefs(svg);
|
|
186
|
+
}
|
|
187
|
+
function collectReferencedIds(svg) {
|
|
188
|
+
const ids = new Set();
|
|
189
|
+
(0, utils_1.traverse)(svg, (node) => {
|
|
190
|
+
if (node.tagName.toLowerCase() === 'defs')
|
|
191
|
+
return false;
|
|
192
|
+
collectIdsFromAttributes(node, (id) => ids.add(id));
|
|
193
|
+
});
|
|
194
|
+
return ids;
|
|
195
|
+
}
|
|
196
|
+
function collectIdsFromAttributes(node, addId) {
|
|
197
|
+
for (const attr of Array.from(node.attributes)) {
|
|
198
|
+
const value = attr.value;
|
|
199
|
+
if (value.includes('url(')) {
|
|
200
|
+
for (const match of value.matchAll(urlRefRegex)) {
|
|
201
|
+
if (match[1])
|
|
202
|
+
addId(match[1]);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if ((attr.name === 'href' || attr.name === 'xlink:href') &&
|
|
206
|
+
value[0] === '#') {
|
|
207
|
+
addId(value.slice(1));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function createDefsDataUrl(svg, ids) {
|
|
212
|
+
if (ids.size === 0)
|
|
213
|
+
return null;
|
|
214
|
+
const collected = collectDefElements(svg, ids);
|
|
215
|
+
if (collected.size === 0)
|
|
216
|
+
return null;
|
|
217
|
+
const defsSvg = (0, utils_1.createElement)('svg', {
|
|
218
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
219
|
+
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
|
220
|
+
});
|
|
221
|
+
const defs = (0, utils_1.createElement)('defs');
|
|
222
|
+
collected.forEach((node) => {
|
|
223
|
+
defs.appendChild(node.cloneNode(true));
|
|
224
|
+
});
|
|
225
|
+
if (!defs.children.length)
|
|
226
|
+
return null;
|
|
227
|
+
defsSvg.appendChild(defs);
|
|
228
|
+
const serialized = new XMLSerializer().serializeToString(defsSvg);
|
|
229
|
+
return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(serialized);
|
|
230
|
+
}
|
|
231
|
+
function collectDefElements(svg, ids) {
|
|
232
|
+
const collected = new Map();
|
|
233
|
+
const queue = Array.from(ids);
|
|
234
|
+
const queued = new Set(queue);
|
|
235
|
+
const visited = new Set();
|
|
236
|
+
const enqueue = (id) => {
|
|
237
|
+
if (visited.has(id) || queued.has(id))
|
|
238
|
+
return;
|
|
239
|
+
queue.push(id);
|
|
240
|
+
queued.add(id);
|
|
241
|
+
};
|
|
242
|
+
while (queue.length) {
|
|
243
|
+
const id = queue.shift();
|
|
244
|
+
if (visited.has(id))
|
|
245
|
+
continue;
|
|
246
|
+
visited.add(id);
|
|
247
|
+
const selector = `#${escapeCssId(id)}`;
|
|
248
|
+
const target = svg.querySelector(selector);
|
|
249
|
+
if (!target)
|
|
250
|
+
continue;
|
|
251
|
+
collected.set(id, target);
|
|
252
|
+
(0, utils_1.traverse)(target, (node) => {
|
|
253
|
+
collectIdsFromAttributes(node, enqueue);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return collected;
|
|
257
|
+
}
|
|
258
|
+
function escapeCssId(id) {
|
|
259
|
+
if (globalThis.CSS && typeof globalThis.CSS.escape === 'function') {
|
|
260
|
+
return globalThis.CSS.escape(id);
|
|
261
|
+
}
|
|
262
|
+
return id.replace(/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g, '\\$1');
|
|
263
|
+
}
|
|
264
|
+
function removeDefs(svg) {
|
|
265
|
+
const defsList = Array.from(svg.querySelectorAll('defs'));
|
|
266
|
+
defsList.forEach((defs) => defs.remove());
|
|
267
|
+
}
|
|
47
268
|
function cleanSVG(svg) {
|
|
48
269
|
removeBtnGroup(svg);
|
|
49
270
|
removeTransientContainer(svg);
|
package/lib/exporter/types.d.ts
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -3,15 +3,17 @@ export * from './designs';
|
|
|
3
3
|
export { getItemProps, getThemeColors } from './designs/utils';
|
|
4
4
|
export { BrushSelect, ClickSelect, DblClickEditText, DragElement, HotkeyHistory, Interaction, SelectHighlight, ZoomWheel, } from './editor/interactions';
|
|
5
5
|
export { EditBar, Plugin, ResizeElement } from './editor/plugins';
|
|
6
|
+
export { exportToSVG } from './exporter';
|
|
6
7
|
export { Defs, Ellipse, Fragment, Group, Path, Polygon, Rect, Text, cloneElement, createFragment, createLayout, getCombinedBounds, getElementBounds, getElementsBounds, jsx, jsxDEV, jsxs, renderSVG, } from './jsx';
|
|
7
|
-
export { getFont, getFonts, getPalette,
|
|
8
|
+
export { getFont, getFonts, getPalette, getPaletteColor, getPalettes, registerFont, registerPalette, registerPattern, setDefaultFont, } from './renderer';
|
|
8
9
|
export { loadSVGResource, registerResourceLoader } from './resource';
|
|
9
10
|
export { Infographic } from './runtime';
|
|
10
11
|
export { parseSyntax } from './syntax';
|
|
11
12
|
export { getTemplate, getTemplates, registerTemplate } from './templates';
|
|
12
13
|
export { getTheme, getThemes, registerTheme } from './themes';
|
|
13
|
-
export { parseSVG } from './utils';
|
|
14
|
+
export { parseSVG, setFontExtendFactor } from './utils';
|
|
14
15
|
export type { EditBarOptions } from './editor';
|
|
16
|
+
export type { ExportOptions, PNGExportOptions, SVGExportOptions, } from './exporter';
|
|
15
17
|
export type { Bounds, ComponentType, DefsProps, EllipseProps, FragmentProps, GroupProps, JSXElement, JSXElementConstructor, JSXNode, PathProps, Point, PolygonProps, RectProps, RenderableNode, SVGAttributes, SVGProps, TextProps, WithChildren, } from './jsx';
|
|
16
18
|
export type { InfographicOptions, ParsedInfographicOptions } from './options';
|
|
17
19
|
export type { GradientConfig, IRenderer, LinearGradient, Palette, PatternConfig, PatternGenerator, PatternStyle, RadialGradient, RoughConfig, StylizeConfig, } from './renderer';
|
package/lib/index.js
CHANGED
|
@@ -17,8 +17,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.
|
|
21
|
-
exports.parseSVG = exports.registerTheme = void 0;
|
|
20
|
+
exports.getTheme = exports.registerTemplate = exports.getTemplates = exports.getTemplate = exports.parseSyntax = exports.Infographic = exports.registerResourceLoader = exports.loadSVGResource = exports.setDefaultFont = exports.registerPattern = exports.registerPalette = exports.registerFont = exports.getPalettes = exports.getPaletteColor = exports.getPalette = exports.getFonts = exports.getFont = exports.renderSVG = exports.jsxs = exports.jsxDEV = exports.jsx = exports.getElementsBounds = exports.getElementBounds = exports.getCombinedBounds = exports.createLayout = exports.createFragment = exports.cloneElement = exports.Text = exports.Rect = exports.Polygon = exports.Path = exports.Group = exports.Fragment = exports.Ellipse = exports.Defs = exports.exportToSVG = exports.ResizeElement = exports.Plugin = exports.EditBar = exports.ZoomWheel = exports.SelectHighlight = exports.Interaction = exports.HotkeyHistory = exports.DragElement = exports.DblClickEditText = exports.ClickSelect = exports.BrushSelect = exports.getThemeColors = exports.getItemProps = exports.VERSION = void 0;
|
|
21
|
+
exports.setFontExtendFactor = exports.parseSVG = exports.registerTheme = exports.getThemes = void 0;
|
|
22
22
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
23
23
|
exports.VERSION = package_json_1.default.version;
|
|
24
24
|
__exportStar(require("./designs"), exports);
|
|
@@ -38,6 +38,8 @@ var plugins_1 = require("./editor/plugins");
|
|
|
38
38
|
Object.defineProperty(exports, "EditBar", { enumerable: true, get: function () { return plugins_1.EditBar; } });
|
|
39
39
|
Object.defineProperty(exports, "Plugin", { enumerable: true, get: function () { return plugins_1.Plugin; } });
|
|
40
40
|
Object.defineProperty(exports, "ResizeElement", { enumerable: true, get: function () { return plugins_1.ResizeElement; } });
|
|
41
|
+
var exporter_1 = require("./exporter");
|
|
42
|
+
Object.defineProperty(exports, "exportToSVG", { enumerable: true, get: function () { return exporter_1.exportToSVG; } });
|
|
41
43
|
var jsx_1 = require("./jsx");
|
|
42
44
|
Object.defineProperty(exports, "Defs", { enumerable: true, get: function () { return jsx_1.Defs; } });
|
|
43
45
|
Object.defineProperty(exports, "Ellipse", { enumerable: true, get: function () { return jsx_1.Ellipse; } });
|
|
@@ -61,8 +63,8 @@ var renderer_1 = require("./renderer");
|
|
|
61
63
|
Object.defineProperty(exports, "getFont", { enumerable: true, get: function () { return renderer_1.getFont; } });
|
|
62
64
|
Object.defineProperty(exports, "getFonts", { enumerable: true, get: function () { return renderer_1.getFonts; } });
|
|
63
65
|
Object.defineProperty(exports, "getPalette", { enumerable: true, get: function () { return renderer_1.getPalette; } });
|
|
64
|
-
Object.defineProperty(exports, "getPalettes", { enumerable: true, get: function () { return renderer_1.getPalettes; } });
|
|
65
66
|
Object.defineProperty(exports, "getPaletteColor", { enumerable: true, get: function () { return renderer_1.getPaletteColor; } });
|
|
67
|
+
Object.defineProperty(exports, "getPalettes", { enumerable: true, get: function () { return renderer_1.getPalettes; } });
|
|
66
68
|
Object.defineProperty(exports, "registerFont", { enumerable: true, get: function () { return renderer_1.registerFont; } });
|
|
67
69
|
Object.defineProperty(exports, "registerPalette", { enumerable: true, get: function () { return renderer_1.registerPalette; } });
|
|
68
70
|
Object.defineProperty(exports, "registerPattern", { enumerable: true, get: function () { return renderer_1.registerPattern; } });
|
|
@@ -84,3 +86,4 @@ Object.defineProperty(exports, "getThemes", { enumerable: true, get: function ()
|
|
|
84
86
|
Object.defineProperty(exports, "registerTheme", { enumerable: true, get: function () { return themes_1.registerTheme; } });
|
|
85
87
|
var utils_2 = require("./utils");
|
|
86
88
|
Object.defineProperty(exports, "parseSVG", { enumerable: true, get: function () { return utils_2.parseSVG; } });
|
|
89
|
+
Object.defineProperty(exports, "setFontExtendFactor", { enumerable: true, get: function () { return utils_2.setFontExtendFactor; } });
|
|
@@ -4,6 +4,7 @@ exports.getFontURLs = getFontURLs;
|
|
|
4
4
|
exports.getWoff2BaseURL = getWoff2BaseURL;
|
|
5
5
|
exports.loadFont = loadFont;
|
|
6
6
|
exports.loadFonts = loadFonts;
|
|
7
|
+
const load_tracker_1 = require("../../resource/load-tracker");
|
|
7
8
|
const utils_1 = require("../../utils");
|
|
8
9
|
const registry_1 = require("./registry");
|
|
9
10
|
function getFontURLs(font) {
|
|
@@ -35,6 +36,52 @@ function getWoff2BaseURL(font, fontWeightName) {
|
|
|
35
36
|
return (0, utils_1.join)(config.baseUrl, path.replace(/\/result.css$/, ''));
|
|
36
37
|
}
|
|
37
38
|
const FONT_LOAD_MAP = new WeakMap();
|
|
39
|
+
const FONT_PROMISE_MAP = new WeakMap();
|
|
40
|
+
function trackFontPromise(target, id, promise) {
|
|
41
|
+
let map = FONT_PROMISE_MAP.get(target);
|
|
42
|
+
if (!map) {
|
|
43
|
+
map = new Map();
|
|
44
|
+
FONT_PROMISE_MAP.set(target, map);
|
|
45
|
+
}
|
|
46
|
+
map.set(id, promise);
|
|
47
|
+
promise.finally(() => {
|
|
48
|
+
const map = FONT_PROMISE_MAP.get(target);
|
|
49
|
+
if (!map)
|
|
50
|
+
return;
|
|
51
|
+
if (map.get(id) === promise)
|
|
52
|
+
map.delete(id);
|
|
53
|
+
if (map.size === 0)
|
|
54
|
+
FONT_PROMISE_MAP.delete(target);
|
|
55
|
+
});
|
|
56
|
+
return promise;
|
|
57
|
+
}
|
|
58
|
+
function isLinkLoaded(link) {
|
|
59
|
+
if (link.dataset.infographicFontLoaded === 'true')
|
|
60
|
+
return true;
|
|
61
|
+
try {
|
|
62
|
+
return !!link.sheet;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function getFontLoadPromise(target, id, link) {
|
|
69
|
+
const existing = FONT_PROMISE_MAP.get(target)?.get(id);
|
|
70
|
+
if (existing)
|
|
71
|
+
return existing;
|
|
72
|
+
if (!link || isLinkLoaded(link)) {
|
|
73
|
+
return trackFontPromise(target, id, Promise.resolve());
|
|
74
|
+
}
|
|
75
|
+
const promise = new Promise((resolve) => {
|
|
76
|
+
const done = () => {
|
|
77
|
+
link.dataset.infographicFontLoaded = 'true';
|
|
78
|
+
resolve();
|
|
79
|
+
};
|
|
80
|
+
link.addEventListener('load', done, { once: true });
|
|
81
|
+
link.addEventListener('error', done, { once: true });
|
|
82
|
+
});
|
|
83
|
+
return trackFontPromise(target, id, promise);
|
|
84
|
+
}
|
|
38
85
|
function loadFont(svg, font) {
|
|
39
86
|
const doc = svg.ownerDocument;
|
|
40
87
|
const target = doc?.head || document.head;
|
|
@@ -43,18 +90,26 @@ function loadFont(svg, font) {
|
|
|
43
90
|
if (!FONT_LOAD_MAP.has(target))
|
|
44
91
|
FONT_LOAD_MAP.set(target, new Map());
|
|
45
92
|
const map = FONT_LOAD_MAP.get(target);
|
|
46
|
-
const links = [];
|
|
47
93
|
const urls = getFontURLs(font);
|
|
94
|
+
if (!urls.length)
|
|
95
|
+
return;
|
|
96
|
+
const links = [];
|
|
48
97
|
urls.forEach((url) => {
|
|
49
98
|
const id = `${font}-${url}`;
|
|
50
|
-
|
|
99
|
+
const promiseKey = `font:${id}`;
|
|
100
|
+
if ((0, load_tracker_1.getSvgLoadPromise)(svg, promiseKey))
|
|
51
101
|
return;
|
|
52
|
-
|
|
53
|
-
link
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
102
|
+
let link = map.get(id);
|
|
103
|
+
if (!link) {
|
|
104
|
+
link = doc.createElement('link');
|
|
105
|
+
link.id = id;
|
|
106
|
+
link.rel = 'stylesheet';
|
|
107
|
+
link.href = url;
|
|
108
|
+
links.push(link);
|
|
109
|
+
map.set(id, link);
|
|
110
|
+
}
|
|
111
|
+
const promise = getFontLoadPromise(target, id, link);
|
|
112
|
+
(0, load_tracker_1.trackSvgLoadPromise)(svg, promiseKey, promise);
|
|
58
113
|
});
|
|
59
114
|
if (!links.length)
|
|
60
115
|
return;
|
|
@@ -26,7 +26,7 @@ function applyGradientStyle(node, svg, config, attr) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
|
-
const id = getGradientId(
|
|
29
|
+
const id = getGradientId(actualConfig);
|
|
30
30
|
if (type === 'linear-gradient') {
|
|
31
31
|
const { angle = 0 } = actualConfig;
|
|
32
32
|
const [[x1, y1], [x2, y2]] = angleToUnitVector(angle);
|
package/lib/resource/index.d.ts
CHANGED
package/lib/resource/index.js
CHANGED
|
@@ -14,9 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.parseResourceConfig = exports.getResourceId = exports.getResourceHref = exports.registerResourceLoader = exports.loadResource = void 0;
|
|
17
|
+
exports.parseResourceConfig = exports.getResourceId = exports.getResourceHref = exports.registerResourceLoader = exports.waitForSvgLoads = exports.loadResource = exports.getSvgLoadPromises = void 0;
|
|
18
18
|
var loader_1 = require("./loader");
|
|
19
|
+
Object.defineProperty(exports, "getSvgLoadPromises", { enumerable: true, get: function () { return loader_1.getSvgLoadPromises; } });
|
|
19
20
|
Object.defineProperty(exports, "loadResource", { enumerable: true, get: function () { return loader_1.loadResource; } });
|
|
21
|
+
Object.defineProperty(exports, "waitForSvgLoads", { enumerable: true, get: function () { return loader_1.waitForSvgLoads; } });
|
|
20
22
|
__exportStar(require("./loaders"), exports);
|
|
21
23
|
var registry_1 = require("./registry");
|
|
22
24
|
Object.defineProperty(exports, "registerResourceLoader", { enumerable: true, get: function () { return registry_1.registerResourceLoader; } });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type SvgLoadPromise = Promise<unknown>;
|
|
2
|
+
export declare function getSvgLoadPromises(svg: SVGSVGElement): SvgLoadPromise[];
|
|
3
|
+
export declare function getSvgLoadPromise<T = unknown>(svg: SVGSVGElement, key: string): Promise<T> | undefined;
|
|
4
|
+
export declare function trackSvgLoadPromise<T>(svg: SVGSVGElement, key: string, promise: Promise<T>): Promise<T>;
|
|
5
|
+
export declare function waitForSvgLoads(svg: SVGSVGElement): Promise<void>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSvgLoadPromises = getSvgLoadPromises;
|
|
4
|
+
exports.getSvgLoadPromise = getSvgLoadPromise;
|
|
5
|
+
exports.trackSvgLoadPromise = trackSvgLoadPromise;
|
|
6
|
+
exports.waitForSvgLoads = waitForSvgLoads;
|
|
7
|
+
const SVG_LOAD_PROMISE_MAP = new WeakMap();
|
|
8
|
+
function getSvgLoadPromises(svg) {
|
|
9
|
+
const map = SVG_LOAD_PROMISE_MAP.get(svg);
|
|
10
|
+
return map ? Array.from(map.values()) : [];
|
|
11
|
+
}
|
|
12
|
+
function getSvgLoadPromise(svg, key) {
|
|
13
|
+
return SVG_LOAD_PROMISE_MAP.get(svg)?.get(key);
|
|
14
|
+
}
|
|
15
|
+
function trackSvgLoadPromise(svg, key, promise) {
|
|
16
|
+
let map = SVG_LOAD_PROMISE_MAP.get(svg);
|
|
17
|
+
if (!map) {
|
|
18
|
+
map = new Map();
|
|
19
|
+
SVG_LOAD_PROMISE_MAP.set(svg, map);
|
|
20
|
+
}
|
|
21
|
+
map.set(key, promise);
|
|
22
|
+
promise.finally(() => {
|
|
23
|
+
const map = SVG_LOAD_PROMISE_MAP.get(svg);
|
|
24
|
+
if (!map)
|
|
25
|
+
return;
|
|
26
|
+
if (map.get(key) === promise)
|
|
27
|
+
map.delete(key);
|
|
28
|
+
if (map.size === 0)
|
|
29
|
+
SVG_LOAD_PROMISE_MAP.delete(svg);
|
|
30
|
+
});
|
|
31
|
+
return promise;
|
|
32
|
+
}
|
|
33
|
+
async function waitForSvgLoads(svg) {
|
|
34
|
+
await Promise.resolve();
|
|
35
|
+
while (true) {
|
|
36
|
+
const promises = getSvgLoadPromises(svg);
|
|
37
|
+
if (!promises.length)
|
|
38
|
+
break;
|
|
39
|
+
await Promise.allSettled(promises);
|
|
40
|
+
await Promise.resolve();
|
|
41
|
+
}
|
|
42
|
+
}
|
package/lib/resource/loader.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ import type { ResourceConfig, ResourceScene } from './types';
|
|
|
5
5
|
* @returns resource ref id
|
|
6
6
|
*/
|
|
7
7
|
export declare function loadResource(svg: SVGSVGElement | null, scene: ResourceScene, config: string | ResourceConfig, datum?: ItemDatum): Promise<string | null>;
|
|
8
|
+
export { getSvgLoadPromises, waitForSvgLoads } from './load-tracker';
|
package/lib/resource/loader.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitForSvgLoads = exports.getSvgLoadPromises = void 0;
|
|
3
4
|
exports.loadResource = loadResource;
|
|
4
5
|
const utils_1 = require("../utils");
|
|
6
|
+
const load_tracker_1 = require("./load-tracker");
|
|
5
7
|
const loaders_1 = require("./loaders");
|
|
6
8
|
const registry_1 = require("./registry");
|
|
7
9
|
const utils_2 = require("./utils");
|
|
@@ -60,22 +62,36 @@ async function loadResource(svg, scene, config, datum) {
|
|
|
60
62
|
if (!cfg)
|
|
61
63
|
return null;
|
|
62
64
|
const id = (0, utils_2.getResourceId)(cfg);
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
65
|
+
const promiseKey = `resource:${id}`;
|
|
66
|
+
const loadedMap = RESOURCE_LOAD_MAP.get(svg);
|
|
67
|
+
if (loadedMap?.has(id))
|
|
68
|
+
return id;
|
|
69
|
+
const existingPromise = (0, load_tracker_1.getSvgLoadPromise)(svg, promiseKey);
|
|
70
|
+
if (existingPromise)
|
|
71
|
+
return await existingPromise;
|
|
72
|
+
const loadPromise = (async () => {
|
|
73
|
+
const resource = RESOURCE_MAP.has(id)
|
|
74
|
+
? RESOURCE_MAP.get(id) || null
|
|
75
|
+
: await getResource(scene, cfg, datum);
|
|
76
|
+
if (!resource)
|
|
77
|
+
return null;
|
|
78
|
+
if (!RESOURCE_LOAD_MAP.has(svg))
|
|
79
|
+
RESOURCE_LOAD_MAP.set(svg, new Map());
|
|
80
|
+
const map = RESOURCE_LOAD_MAP.get(svg);
|
|
81
|
+
if (map.has(id))
|
|
82
|
+
return id;
|
|
83
|
+
const defs = (0, utils_1.getOrCreateDefs)(svg);
|
|
84
|
+
resource.id = id;
|
|
85
|
+
defs.appendChild(resource);
|
|
86
|
+
map.set(id, resource);
|
|
72
87
|
return id;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
map.set(id, resource);
|
|
77
|
-
return id;
|
|
88
|
+
})();
|
|
89
|
+
(0, load_tracker_1.trackSvgLoadPromise)(svg, promiseKey, loadPromise);
|
|
90
|
+
return await loadPromise;
|
|
78
91
|
}
|
|
92
|
+
var load_tracker_2 = require("./load-tracker");
|
|
93
|
+
Object.defineProperty(exports, "getSvgLoadPromises", { enumerable: true, get: function () { return load_tracker_2.getSvgLoadPromises; } });
|
|
94
|
+
Object.defineProperty(exports, "waitForSvgLoads", { enumerable: true, get: function () { return load_tracker_2.waitForSvgLoads; } });
|
|
79
95
|
function getFallbackQuery(cfg, scene, datum) {
|
|
80
96
|
const defaultQuery = scene === 'illus' ? 'illustration' : 'icon';
|
|
81
97
|
const datumQuery = normalizeQuery(datum?.label) || normalizeQuery(datum?.desc);
|
|
@@ -11,6 +11,7 @@ const exporter_1 = require("../exporter");
|
|
|
11
11
|
const jsx_1 = require("../jsx");
|
|
12
12
|
const options_1 = require("../options");
|
|
13
13
|
const renderer_1 = require("../renderer");
|
|
14
|
+
const resource_1 = require("../resource");
|
|
14
15
|
const syntax_1 = require("../syntax");
|
|
15
16
|
const utils_1 = require("../utils");
|
|
16
17
|
const options_2 = require("./options");
|
|
@@ -77,6 +78,18 @@ class Infographic {
|
|
|
77
78
|
}
|
|
78
79
|
this.rendered = true;
|
|
79
80
|
this.emitter.emit('rendered', { node: this.node, options: this.options });
|
|
81
|
+
const currentNode = this.node;
|
|
82
|
+
if (currentNode) {
|
|
83
|
+
void (0, resource_1.waitForSvgLoads)(currentNode).then(() => {
|
|
84
|
+
if (this.node !== currentNode)
|
|
85
|
+
return;
|
|
86
|
+
this.emitter.emit('loaded', {
|
|
87
|
+
node: currentNode,
|
|
88
|
+
options: this.options,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
80
93
|
}
|
|
81
94
|
/**
|
|
82
95
|
* Compose the SVG template
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isBrowser(): boolean;
|