@antv/infographic 0.2.7 → 0.2.9
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/dist/infographic.min.js +191 -191
- package/dist/infographic.min.js.map +1 -1
- package/esm/designs/items/BadgeCard.js +6 -1
- package/esm/designs/items/SimpleCircleNode.d.ts +8 -0
- package/esm/designs/items/SimpleCircleNode.js +14 -0
- package/esm/designs/items/index.d.ts +1 -0
- package/esm/designs/items/index.js +1 -0
- package/esm/designs/structures/hierarchy-mindmap.js +19 -5
- package/esm/designs/structures/hierarchy-tree.d.ts +2 -1
- package/esm/designs/structures/hierarchy-tree.js +23 -20
- package/esm/designs/structures/index.d.ts +1 -0
- package/esm/designs/structures/index.js +1 -0
- package/esm/designs/structures/relation-dagre-flow.d.ts +21 -0
- package/esm/designs/structures/relation-dagre-flow.js +497 -0
- package/esm/designs/utils/hierarchy-color.d.ts +1 -1
- package/esm/editor/plugins/edit-bar/edit-bar.js +27 -9
- package/esm/index.d.ts +1 -1
- package/esm/index.js +1 -2
- package/esm/jsx/global.d.ts +1 -0
- package/esm/jsx/types/element.d.ts +5 -1
- package/esm/jsx/utils/svg.js +2 -0
- package/esm/renderer/composites/icon.js +2 -0
- package/esm/renderer/composites/illus.d.ts +1 -1
- package/esm/renderer/composites/illus.js +9 -4
- package/esm/renderer/composites/text.js +4 -2
- package/esm/renderer/fonts/loader.js +3 -1
- package/esm/renderer/fonts/registry.js +1 -1
- package/esm/renderer/renderer.js +28 -25
- package/esm/resource/loader.js +3 -1
- package/esm/runtime/Infographic.js +1 -1
- package/esm/ssr/dom-shim.d.ts +4 -0
- package/esm/ssr/dom-shim.js +107 -0
- package/esm/ssr/index.d.ts +1 -0
- package/esm/ssr/index.js +1 -0
- package/esm/ssr/renderer.d.ts +2 -0
- package/esm/ssr/renderer.js +60 -0
- package/esm/syntax/index.js +57 -1
- package/esm/syntax/parser.js +44 -0
- package/esm/syntax/relations.d.ts +6 -0
- package/esm/syntax/relations.js +251 -0
- package/esm/syntax/schema.d.ts +1 -0
- package/esm/syntax/schema.js +12 -0
- package/esm/templates/built-in.js +2 -0
- package/esm/templates/relation-dagre-flow.d.ts +2 -0
- package/esm/templates/relation-dagre-flow.js +68 -0
- package/esm/types/data.d.ts +24 -3
- package/esm/utils/data.js +1 -1
- package/esm/utils/index.d.ts +1 -0
- package/esm/utils/index.js +1 -0
- package/esm/utils/is-browser.js +5 -9
- package/esm/utils/measure-text.d.ts +2 -2
- package/esm/utils/measure-text.js +4 -4
- package/esm/utils/recognizer.js +8 -5
- package/esm/utils/text.js +27 -19
- package/esm/version.d.ts +1 -0
- package/esm/version.js +1 -0
- package/lib/designs/items/BadgeCard.js +6 -1
- package/lib/designs/items/SimpleCircleNode.d.ts +8 -0
- package/lib/designs/items/SimpleCircleNode.js +18 -0
- package/lib/designs/items/index.d.ts +1 -0
- package/lib/designs/items/index.js +1 -0
- package/lib/designs/structures/hierarchy-mindmap.js +19 -5
- package/lib/designs/structures/hierarchy-tree.d.ts +2 -1
- package/lib/designs/structures/hierarchy-tree.js +23 -20
- package/lib/designs/structures/index.d.ts +1 -0
- package/lib/designs/structures/index.js +1 -0
- package/lib/designs/structures/relation-dagre-flow.d.ts +21 -0
- package/lib/designs/structures/relation-dagre-flow.js +501 -0
- package/lib/designs/utils/hierarchy-color.d.ts +1 -1
- package/lib/editor/plugins/edit-bar/edit-bar.js +27 -9
- package/lib/index.d.ts +1 -1
- package/lib/index.js +4 -7
- package/lib/jsx/global.d.ts +1 -0
- package/lib/jsx/types/element.d.ts +5 -1
- package/lib/jsx/utils/svg.js +2 -0
- package/lib/renderer/composites/icon.js +2 -0
- package/lib/renderer/composites/illus.d.ts +1 -1
- package/lib/renderer/composites/illus.js +8 -3
- package/lib/renderer/composites/text.js +4 -2
- package/lib/renderer/fonts/loader.js +2 -0
- package/lib/renderer/fonts/registry.js +6 -6
- package/lib/renderer/renderer.js +27 -24
- package/lib/resource/loader.js +3 -1
- package/lib/runtime/Infographic.js +1 -1
- package/lib/ssr/dom-shim.d.ts +4 -0
- package/lib/ssr/dom-shim.js +110 -0
- package/lib/ssr/index.d.ts +1 -0
- package/lib/ssr/index.js +5 -0
- package/lib/ssr/renderer.d.ts +2 -0
- package/lib/ssr/renderer.js +63 -0
- package/lib/syntax/index.js +57 -1
- package/lib/syntax/parser.js +44 -0
- package/lib/syntax/relations.d.ts +6 -0
- package/lib/syntax/relations.js +254 -0
- package/lib/syntax/schema.d.ts +1 -0
- package/lib/syntax/schema.js +13 -1
- package/lib/templates/built-in.js +2 -0
- package/lib/templates/relation-dagre-flow.d.ts +2 -0
- package/lib/templates/relation-dagre-flow.js +71 -0
- package/lib/types/data.d.ts +24 -3
- package/lib/utils/data.js +2 -5
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/lib/utils/is-browser.js +5 -9
- package/lib/utils/measure-text.d.ts +2 -2
- package/lib/utils/measure-text.js +4 -4
- package/lib/utils/recognizer.js +8 -5
- package/lib/utils/text.js +28 -23
- package/lib/version.d.ts +1 -0
- package/lib/version.js +4 -0
- package/package.json +21 -8
- package/src/designs/items/BadgeCard.tsx +9 -2
- package/src/designs/items/SimpleCircleNode.tsx +46 -0
- package/src/designs/items/index.ts +1 -0
- package/src/designs/structures/hierarchy-mindmap.tsx +15 -2
- package/src/designs/structures/hierarchy-tree.tsx +33 -31
- package/src/designs/structures/index.ts +1 -0
- package/src/designs/structures/relation-dagre-flow.tsx +782 -0
- package/src/designs/utils/hierarchy-color.ts +6 -1
- package/src/editor/plugins/edit-bar/edit-bar.ts +41 -17
- package/src/index.ts +1 -3
- package/src/jsx/global.ts +1 -0
- package/src/jsx/types/element.ts +15 -6
- package/src/jsx/utils/svg.ts +2 -0
- package/src/renderer/composites/icon.ts +2 -0
- package/src/renderer/composites/illus.ts +16 -3
- package/src/renderer/composites/text.ts +7 -2
- package/src/renderer/fonts/loader.ts +7 -1
- package/src/renderer/fonts/registry.ts +1 -1
- package/src/renderer/renderer.ts +42 -24
- package/src/resource/loader.ts +3 -1
- package/src/runtime/Infographic.tsx +1 -1
- package/src/ssr/dom-shim.ts +120 -0
- package/src/ssr/index.ts +1 -0
- package/src/ssr/renderer.ts +72 -0
- package/src/syntax/index.ts +58 -1
- package/src/syntax/parser.ts +49 -0
- package/src/syntax/relations.ts +291 -0
- package/src/syntax/schema.ts +16 -0
- package/src/templates/built-in.ts +4 -2
- package/src/templates/relation-dagre-flow.ts +73 -0
- package/src/types/data.ts +26 -3
- package/src/utils/data.ts +1 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/is-browser.ts +3 -9
- package/src/utils/measure-text.ts +6 -7
- package/src/utils/recognizer.ts +9 -5
- package/src/utils/svg.ts +0 -1
- package/src/utils/text.ts +25 -19
- package/src/version.ts +1 -0
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import { getResourceHref, getResourceId, loadResource, parseResourceConfig, } from '../../resource';
|
|
2
|
-
import { createElement, getAttributes, getOrCreateDefs, removeAttributes, uuid, } from '../../utils';
|
|
3
|
-
export function renderIllus(svg, node, value, datum) {
|
|
2
|
+
import { createElement, getAttributes, getOrCreateDefs, removeAttributes, setAttributes, uuid, } from '../../utils';
|
|
3
|
+
export function renderIllus(svg, node, value, datum, attrs = {}) {
|
|
4
4
|
if (!value)
|
|
5
5
|
return null;
|
|
6
6
|
const config = parseResourceConfig(value);
|
|
7
7
|
if (!config)
|
|
8
8
|
return null;
|
|
9
9
|
const id = getResourceId(config);
|
|
10
|
+
if (attrs && Object.keys(attrs).length > 0) {
|
|
11
|
+
setAttributes(node, attrs);
|
|
12
|
+
}
|
|
10
13
|
const clipPathId = createClipPath(svg, node, id);
|
|
11
14
|
loadResource(svg, 'illus', config, datum);
|
|
12
15
|
const { data, color } = config;
|
|
13
16
|
return createIllusElement(id, {
|
|
14
17
|
...parseIllusBounds(node),
|
|
15
|
-
'clip-path': `url(#${clipPathId})`,
|
|
16
18
|
...(color ? { color } : {}),
|
|
19
|
+
...attrs,
|
|
20
|
+
'clip-path': `url(#${clipPathId})`,
|
|
17
21
|
}, data);
|
|
18
22
|
}
|
|
19
23
|
export function renderItemIllus(svg, node, datum) {
|
|
20
24
|
const value = datum.illus;
|
|
21
|
-
|
|
25
|
+
const attrs = datum.attributes?.illus;
|
|
26
|
+
return renderIllus(svg, node, value, datum, attrs);
|
|
22
27
|
}
|
|
23
28
|
function createClipPath(svg, node, id) {
|
|
24
29
|
const clipPathId = `clip-${id}-${uuid()}`;
|
|
@@ -19,8 +19,10 @@ export function renderItemText(type, node, options) {
|
|
|
19
19
|
return null;
|
|
20
20
|
const { data, themeConfig } = options;
|
|
21
21
|
const indexes = getItemIndexes(node.dataset.indexes || '0');
|
|
22
|
-
const
|
|
23
|
-
const
|
|
22
|
+
const datum = getDatumByIndexes(data, indexes);
|
|
23
|
+
const text = String(get(datum, type, ''));
|
|
24
|
+
const dataAttrs = datum?.attributes?.[type];
|
|
25
|
+
const attrs = Object.assign({}, themeConfig.base?.text, themeConfig.item?.[type], dataAttrs);
|
|
24
26
|
const staticAttrs = parseDynamicAttributes(textShape, attrs);
|
|
25
27
|
return renderText(node, node.textContent || text, staticAttrs);
|
|
26
28
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSvgLoadPromise, trackSvgLoadPromise, } from '../../resource/load-tracker';
|
|
2
|
-
import { join, normalizeFontWeightName, splitFontFamily } from '../../utils';
|
|
2
|
+
import { isNode, join, normalizeFontWeightName, splitFontFamily, } from '../../utils';
|
|
3
3
|
import { getFont, getFonts } from './registry';
|
|
4
4
|
export function getFontURLs(font) {
|
|
5
5
|
const urls = splitFontFamily(font).flatMap((family) => {
|
|
@@ -112,6 +112,8 @@ export function loadFont(svg, font) {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
export function loadFonts(svg) {
|
|
115
|
+
if (isNode)
|
|
116
|
+
return;
|
|
115
117
|
const fonts = getFonts();
|
|
116
118
|
fonts.forEach((font) => loadFont(svg, font.fontFamily));
|
|
117
119
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decodeFontFamily, encodeFontFamily, splitFontFamily, } from '../../utils';
|
|
1
|
+
import { decodeFontFamily, encodeFontFamily, splitFontFamily, } from '../../utils/font';
|
|
2
2
|
const FONT_REGISTRY = new Map();
|
|
3
3
|
export let DEFAULT_FONT = 'Alibaba PuHuiTi';
|
|
4
4
|
export function getFont(font) {
|
package/esm/renderer/renderer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDatumByIndexes, getItemIndexes, isBtnsGroup, isDesc, isGroup, isIllus, isItemDesc, isItemIcon, isItemIllus, isItemLabel, isItemValue, isShape, isShapesGroup, isText, isTitle, parsePadding, setAttributes, setSVGPadding, } from '../utils';
|
|
1
|
+
import { getDatumByIndexes, getItemIndexes, isBtnsGroup, isDesc, isGroup, isIllus, isItemDesc, isItemIcon, isItemIllus, isItemLabel, isItemValue, isNode, isShape, isShapesGroup, isText, isTitle, parsePadding, setAttributes, setSVGPadding, } from '../utils';
|
|
2
2
|
import { renderBackground, renderBaseElement, renderButtonsGroup, renderIllus, renderItemIcon, renderItemIllus, renderItemText, renderShape, renderStaticShape, renderStaticText, renderSVG, renderText, } from './composites';
|
|
3
3
|
import { loadFonts } from './fonts';
|
|
4
4
|
const upsert = (original, modified) => {
|
|
@@ -30,30 +30,33 @@ export class Renderer {
|
|
|
30
30
|
const postRender = () => {
|
|
31
31
|
setView(this.template, this.options);
|
|
32
32
|
loadFonts(this.template);
|
|
33
|
-
svg.style.visibility
|
|
33
|
+
svg.style.removeProperty('visibility');
|
|
34
34
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
if (isNode) {
|
|
36
|
+
postRender();
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const observer = new MutationObserver((mutations) => {
|
|
40
|
+
mutations.forEach((mutation) => {
|
|
41
|
+
mutation.addedNodes.forEach((node) => {
|
|
42
|
+
if (node === svg || node.contains(svg)) {
|
|
43
|
+
postRender();
|
|
44
|
+
observer.disconnect();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
44
47
|
});
|
|
45
48
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
try {
|
|
50
|
+
observer.observe(document, {
|
|
51
|
+
childList: true,
|
|
52
|
+
subtree: true,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// Fallback for micro-app environments that proxy document.
|
|
57
|
+
postRender();
|
|
58
|
+
console.error(error);
|
|
59
|
+
}
|
|
57
60
|
}
|
|
58
61
|
this.rendered = true;
|
|
59
62
|
return svg;
|
|
@@ -71,15 +74,15 @@ function fill(svg, options) {
|
|
|
71
74
|
elements.forEach((element) => {
|
|
72
75
|
const id = element.id || '';
|
|
73
76
|
if (isTitle(element)) {
|
|
74
|
-
const modified = renderText(element, data.title || '', Object.assign({}, themeConfig.base?.text, themeConfig.title));
|
|
77
|
+
const modified = renderText(element, data.title || '', Object.assign({}, themeConfig.base?.text, themeConfig.title, data.attributes?.title));
|
|
75
78
|
return upsert(element, modified);
|
|
76
79
|
}
|
|
77
80
|
if (isDesc(element)) {
|
|
78
|
-
const modified = renderText(element, data.desc || '', Object.assign({}, themeConfig.base?.text, themeConfig.desc));
|
|
81
|
+
const modified = renderText(element, data.desc || '', Object.assign({}, themeConfig.base?.text, themeConfig.desc, data.attributes?.desc));
|
|
79
82
|
return upsert(element, modified);
|
|
80
83
|
}
|
|
81
84
|
if (isIllus(element)) {
|
|
82
|
-
const modified = renderIllus(svg, element, data.illus?.[id]);
|
|
85
|
+
const modified = renderIllus(svg, element, data.illus?.[id], undefined, data.attributes?.illus);
|
|
83
86
|
return upsert(element, modified);
|
|
84
87
|
}
|
|
85
88
|
if (isShapesGroup(element)) {
|
package/esm/resource/loader.js
CHANGED
|
@@ -88,7 +88,9 @@ export async function loadResource(svg, scene, config, datum) {
|
|
|
88
88
|
export { getSvgLoadPromises, waitForSvgLoads } from './load-tracker';
|
|
89
89
|
function getFallbackQuery(cfg, scene, datum) {
|
|
90
90
|
const defaultQuery = scene === 'illus' ? 'illustration' : 'icon';
|
|
91
|
-
const datumQuery = normalizeQuery(
|
|
91
|
+
const datumQuery = normalizeQuery(cfg.data) ||
|
|
92
|
+
normalizeQuery(datum?.label) ||
|
|
93
|
+
normalizeQuery(datum?.desc);
|
|
92
94
|
if (datumQuery)
|
|
93
95
|
return datumQuery;
|
|
94
96
|
const data = normalizeQuery(cfg.data);
|
|
@@ -24,7 +24,7 @@ export class Infographic {
|
|
|
24
24
|
setOptions(options, mode = 'replace', isInitial = false) {
|
|
25
25
|
const { options: parsedOptions, errors, warnings, } = parseSyntaxOptions(options);
|
|
26
26
|
if (isInitial) {
|
|
27
|
-
this.initialOptions =
|
|
27
|
+
this.initialOptions = parsedOptions;
|
|
28
28
|
}
|
|
29
29
|
const base = mode === 'replace'
|
|
30
30
|
? mergeOptions(cloneOptions(this.initialOptions || {}), parsedOptions)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { DOMParser, parseHTML } from 'linkedom';
|
|
2
|
+
export function setupDOM() {
|
|
3
|
+
const { document, window } = parseHTML('<!DOCTYPE html><html><body><div id="container"></div></body></html>');
|
|
4
|
+
Object.assign(globalThis, {
|
|
5
|
+
window,
|
|
6
|
+
document,
|
|
7
|
+
DOMParser,
|
|
8
|
+
});
|
|
9
|
+
const classes = [
|
|
10
|
+
'HTMLElement',
|
|
11
|
+
'HTMLDivElement',
|
|
12
|
+
'HTMLSpanElement',
|
|
13
|
+
'HTMLImageElement',
|
|
14
|
+
'HTMLCanvasElement',
|
|
15
|
+
'HTMLInputElement',
|
|
16
|
+
'HTMLButtonElement',
|
|
17
|
+
'Element',
|
|
18
|
+
'Node',
|
|
19
|
+
'Text',
|
|
20
|
+
'Comment',
|
|
21
|
+
'DocumentFragment',
|
|
22
|
+
'Document',
|
|
23
|
+
'XMLSerializer',
|
|
24
|
+
'MutationObserver',
|
|
25
|
+
// SVG
|
|
26
|
+
'SVGElement',
|
|
27
|
+
'SVGSVGElement',
|
|
28
|
+
'SVGGraphicsElement',
|
|
29
|
+
'SVGGElement',
|
|
30
|
+
'SVGPathElement',
|
|
31
|
+
'SVGRectElement',
|
|
32
|
+
'SVGCircleElement',
|
|
33
|
+
'SVGTextElement',
|
|
34
|
+
'SVGLineElement',
|
|
35
|
+
'SVGPolygonElement',
|
|
36
|
+
'SVGPolylineElement',
|
|
37
|
+
'SVGEllipseElement',
|
|
38
|
+
'SVGImageElement',
|
|
39
|
+
'SVGDefsElement',
|
|
40
|
+
'SVGUseElement',
|
|
41
|
+
'SVGClipPathElement',
|
|
42
|
+
'SVGLinearGradientElement',
|
|
43
|
+
'SVGRadialGradientElement',
|
|
44
|
+
'SVGStopElement',
|
|
45
|
+
'SVGPatternElement',
|
|
46
|
+
'SVGMaskElement',
|
|
47
|
+
'SVGForeignObjectElement',
|
|
48
|
+
'Image',
|
|
49
|
+
];
|
|
50
|
+
classes.forEach((name) => {
|
|
51
|
+
if (window[name])
|
|
52
|
+
globalThis[name] = window[name];
|
|
53
|
+
});
|
|
54
|
+
if (!document.fonts) {
|
|
55
|
+
const fontSet = new Set();
|
|
56
|
+
Object.defineProperty(document, 'fonts', {
|
|
57
|
+
value: {
|
|
58
|
+
add: (font) => fontSet.add(font),
|
|
59
|
+
delete: (font) => fontSet.delete(font),
|
|
60
|
+
has: (font) => fontSet.has(font),
|
|
61
|
+
clear: () => fontSet.clear(),
|
|
62
|
+
forEach: (callback) => fontSet.forEach(callback),
|
|
63
|
+
entries: () => fontSet.entries(),
|
|
64
|
+
keys: () => fontSet.keys(),
|
|
65
|
+
values: () => fontSet.values(),
|
|
66
|
+
[Symbol.iterator]: () => fontSet[Symbol.iterator](),
|
|
67
|
+
get size() {
|
|
68
|
+
return fontSet.size;
|
|
69
|
+
},
|
|
70
|
+
get ready() {
|
|
71
|
+
return Promise.resolve(this);
|
|
72
|
+
},
|
|
73
|
+
check: () => true,
|
|
74
|
+
load: () => Promise.resolve([]),
|
|
75
|
+
get status() {
|
|
76
|
+
return 'loaded';
|
|
77
|
+
},
|
|
78
|
+
onloading: null,
|
|
79
|
+
onloadingdone: null,
|
|
80
|
+
onloadingerror: null,
|
|
81
|
+
addEventListener: () => { },
|
|
82
|
+
removeEventListener: () => { },
|
|
83
|
+
dispatchEvent: () => true,
|
|
84
|
+
},
|
|
85
|
+
configurable: true,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const rafIds = new Map();
|
|
89
|
+
let nextRafId = 0;
|
|
90
|
+
globalThis.requestAnimationFrame = (cb) => {
|
|
91
|
+
const id = ++nextRafId;
|
|
92
|
+
const immediate = setImmediate(() => {
|
|
93
|
+
rafIds.delete(id);
|
|
94
|
+
cb(performance.now());
|
|
95
|
+
});
|
|
96
|
+
rafIds.set(id, immediate);
|
|
97
|
+
return id;
|
|
98
|
+
};
|
|
99
|
+
globalThis.cancelAnimationFrame = (id) => {
|
|
100
|
+
const immediate = rafIds.get(id);
|
|
101
|
+
if (immediate) {
|
|
102
|
+
clearImmediate(immediate);
|
|
103
|
+
rafIds.delete(id);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
return { window, document };
|
|
107
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { renderToString } from './renderer';
|
package/esm/ssr/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { renderToString } from './renderer';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { exportToSVG } from '../exporter';
|
|
2
|
+
import { getFontURLs } from '../renderer';
|
|
3
|
+
import { Infographic } from '../runtime';
|
|
4
|
+
import { decodeFontFamily } from '../utils';
|
|
5
|
+
import { setupDOM } from './dom-shim';
|
|
6
|
+
export async function renderToString(options, init) {
|
|
7
|
+
const { document } = setupDOM();
|
|
8
|
+
const container = document.getElementById('container');
|
|
9
|
+
let infographic;
|
|
10
|
+
let timeoutId;
|
|
11
|
+
try {
|
|
12
|
+
infographic = new Infographic({
|
|
13
|
+
...init,
|
|
14
|
+
container,
|
|
15
|
+
editable: false,
|
|
16
|
+
});
|
|
17
|
+
const renderPromise = new Promise((resolve, reject) => {
|
|
18
|
+
infographic.on('loaded', async ({ node }) => {
|
|
19
|
+
try {
|
|
20
|
+
const svg = await exportToSVG(node, { embedResources: true });
|
|
21
|
+
resolve(svg.outerHTML);
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
reject(e);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
29
|
+
timeoutId = setTimeout(() => {
|
|
30
|
+
reject(new Error('SSR render timeout'));
|
|
31
|
+
}, 10000);
|
|
32
|
+
});
|
|
33
|
+
infographic.render(options);
|
|
34
|
+
const svg = await Promise.race([renderPromise, timeoutPromise]);
|
|
35
|
+
return injectXMLStylesheet(svg);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
clearTimeout(timeoutId);
|
|
39
|
+
if (infographic) {
|
|
40
|
+
infographic.destroy();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function injectXMLStylesheet(svg) {
|
|
45
|
+
const matched = svg.matchAll(/font-family="([\S ]+?)"/g);
|
|
46
|
+
const fonts = Array.from(matched, (match) => match[1]);
|
|
47
|
+
const set = new Set();
|
|
48
|
+
fonts.forEach((font) => {
|
|
49
|
+
const decoded = decodeFontFamily(font);
|
|
50
|
+
decoded.split(',').forEach((f) => set.add(f.trim()));
|
|
51
|
+
});
|
|
52
|
+
const urls = Array.from(set)
|
|
53
|
+
.map((font) => getFontURLs(font))
|
|
54
|
+
.flat();
|
|
55
|
+
if (urls.length === 0)
|
|
56
|
+
return svg;
|
|
57
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
58
|
+
${urls.map((url) => `<?xml-stylesheet href="${url}" type="text/css"?>`).join('\n')}
|
|
59
|
+
${svg}`;
|
|
60
|
+
}
|
package/esm/syntax/index.js
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import { mapWithSchema } from './mapper';
|
|
2
2
|
import { parseSyntaxToAst } from './parser';
|
|
3
|
+
import { parseRelationsNode } from './relations';
|
|
3
4
|
import { DataSchema, DesignSchema, RootSchema, TemplateSchema, ThemeSchema, } from './schema';
|
|
5
|
+
function normalizeItems(items) {
|
|
6
|
+
const seen = new Set();
|
|
7
|
+
const normalized = [];
|
|
8
|
+
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
9
|
+
const item = items[index];
|
|
10
|
+
const id = item.id ?? item.label;
|
|
11
|
+
if (!id) {
|
|
12
|
+
normalized.push(item);
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (seen.has(id))
|
|
16
|
+
continue;
|
|
17
|
+
seen.add(id);
|
|
18
|
+
if (!item.id)
|
|
19
|
+
item.id = id;
|
|
20
|
+
normalized.push(item);
|
|
21
|
+
}
|
|
22
|
+
return normalized.reverse();
|
|
23
|
+
}
|
|
4
24
|
function resolveTemplate(node, errors) {
|
|
5
25
|
if (!node)
|
|
6
26
|
return undefined;
|
|
@@ -64,9 +84,45 @@ export function parseSyntax(input) {
|
|
|
64
84
|
}
|
|
65
85
|
const dataNode = mergedEntries.data;
|
|
66
86
|
if (dataNode) {
|
|
67
|
-
|
|
87
|
+
let relationsNode;
|
|
88
|
+
let dataNodeForMapping = dataNode;
|
|
89
|
+
if (dataNode.kind === 'object') {
|
|
90
|
+
const { relations, ...rest } = dataNode.entries;
|
|
91
|
+
relationsNode = relations;
|
|
92
|
+
dataNodeForMapping = { ...dataNode, entries: rest };
|
|
93
|
+
}
|
|
94
|
+
const data = mapWithSchema(dataNodeForMapping, DataSchema, 'data', errors);
|
|
68
95
|
if (data)
|
|
69
96
|
options.data = data;
|
|
97
|
+
if (relationsNode) {
|
|
98
|
+
const parsed = parseRelationsNode(relationsNode, errors, 'data.relations');
|
|
99
|
+
if (parsed.relations.length > 0 || parsed.items.length > 0) {
|
|
100
|
+
const current = (options.data ?? {});
|
|
101
|
+
const existingItems = Array.isArray(current.items)
|
|
102
|
+
? current.items
|
|
103
|
+
: [];
|
|
104
|
+
const normalizedItems = normalizeItems(existingItems);
|
|
105
|
+
const itemMap = new Map();
|
|
106
|
+
normalizedItems.forEach((item) => {
|
|
107
|
+
if (item.id)
|
|
108
|
+
itemMap.set(item.id, item);
|
|
109
|
+
});
|
|
110
|
+
parsed.items.forEach((item) => {
|
|
111
|
+
const existing = itemMap.get(item.id);
|
|
112
|
+
if (existing) {
|
|
113
|
+
if (!existing.label && item.label)
|
|
114
|
+
existing.label = item.label;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
normalizedItems.push(item);
|
|
118
|
+
itemMap.set(item.id, item);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
current.items = normalizedItems;
|
|
122
|
+
current.relations = parsed.relations;
|
|
123
|
+
options.data = current;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
70
126
|
}
|
|
71
127
|
const themeNode = mergedEntries.theme;
|
|
72
128
|
if (themeNode) {
|
package/esm/syntax/parser.js
CHANGED
|
@@ -23,6 +23,9 @@ function getIndentInfo(line) {
|
|
|
23
23
|
function stripComments(content) {
|
|
24
24
|
return content.trimEnd();
|
|
25
25
|
}
|
|
26
|
+
function looksLikeRelationExpression(text) {
|
|
27
|
+
return /[<>=o.x-]{2,}/.test(text);
|
|
28
|
+
}
|
|
26
29
|
function parseKeyValue(raw) {
|
|
27
30
|
const text = raw.trim();
|
|
28
31
|
if (!text)
|
|
@@ -105,6 +108,47 @@ export function parseSyntaxToAst(input) {
|
|
|
105
108
|
});
|
|
106
109
|
return;
|
|
107
110
|
}
|
|
111
|
+
if (parentFrame.key === 'relations' &&
|
|
112
|
+
!trimmed.startsWith('-') &&
|
|
113
|
+
looksLikeRelationExpression(trimmed)) {
|
|
114
|
+
if (parentNode.kind !== 'array') {
|
|
115
|
+
if (parentNode.kind === 'object' &&
|
|
116
|
+
Object.keys(parentNode.entries).length === 0 &&
|
|
117
|
+
parentNode.value === undefined &&
|
|
118
|
+
parentFrame.parent &&
|
|
119
|
+
parentFrame.key) {
|
|
120
|
+
const arrayNode = createArrayNode(parentNode.line);
|
|
121
|
+
if (parentFrame.parent.kind === 'object') {
|
|
122
|
+
parentFrame.parent.entries[parentFrame.key] = arrayNode;
|
|
123
|
+
}
|
|
124
|
+
else if (parentFrame.parent.kind === 'array') {
|
|
125
|
+
const indexInParent = parentFrame.parent.items.indexOf(parentNode);
|
|
126
|
+
if (indexInParent >= 0)
|
|
127
|
+
parentFrame.parent.items[indexInParent] = arrayNode;
|
|
128
|
+
}
|
|
129
|
+
parentFrame.node = arrayNode;
|
|
130
|
+
parentNode = arrayNode;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
errors.push({
|
|
134
|
+
path: '',
|
|
135
|
+
line: lineNumber,
|
|
136
|
+
code: 'bad_list',
|
|
137
|
+
message: 'List item is not under an array container.',
|
|
138
|
+
raw: trimmed,
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const itemNode = createObjectNode(lineNumber, trimmed);
|
|
144
|
+
parentNode.items.push(itemNode);
|
|
145
|
+
stack.push({
|
|
146
|
+
indent,
|
|
147
|
+
node: itemNode,
|
|
148
|
+
parent: parentNode,
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
108
152
|
const parsed = parseKeyValue(trimmed);
|
|
109
153
|
if (!parsed) {
|
|
110
154
|
errors.push({
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ItemDatum, RelationDatum } from '../types';
|
|
2
|
+
import type { SyntaxError, SyntaxNode } from './types';
|
|
3
|
+
export declare function parseRelationsNode(node: SyntaxNode, errors: SyntaxError[], path: string): {
|
|
4
|
+
relations: RelationDatum[];
|
|
5
|
+
items: ItemDatum[];
|
|
6
|
+
};
|