@antv/infographic 0.1.3 → 0.1.4

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 (171) hide show
  1. package/dist/infographic.min.js +110 -105
  2. package/dist/infographic.min.js.map +1 -1
  3. package/esm/constants/element.d.ts +1 -1
  4. package/esm/constants/index.d.ts +1 -0
  5. package/esm/constants/index.js +1 -0
  6. package/esm/constants/service.d.ts +1 -0
  7. package/esm/constants/service.js +1 -0
  8. package/esm/designs/components/Illus.js +1 -1
  9. package/esm/designs/structures/chart-wordcloud.d.ts +11 -0
  10. package/esm/designs/structures/chart-wordcloud.js +156 -0
  11. package/esm/designs/structures/hierarchy-tree.d.ts +2 -0
  12. package/esm/designs/structures/hierarchy-tree.js +179 -50
  13. package/esm/designs/structures/index.d.ts +2 -0
  14. package/esm/designs/structures/index.js +2 -0
  15. package/esm/designs/structures/sequence-stairs-front.d.ts +8 -0
  16. package/esm/designs/structures/sequence-stairs-front.js +116 -0
  17. package/esm/designs/types.d.ts +8 -0
  18. package/esm/editor/managers/state.js +1 -1
  19. package/esm/index.d.ts +2 -0
  20. package/esm/index.js +1 -0
  21. package/esm/options/parser.d.ts +1 -1
  22. package/esm/options/parser.js +33 -15
  23. package/esm/renderer/composites/icon.js +1 -1
  24. package/esm/renderer/composites/illus.js +1 -1
  25. package/esm/resource/loader.d.ts +2 -2
  26. package/esm/resource/loader.js +22 -11
  27. package/esm/resource/loaders/index.d.ts +1 -0
  28. package/esm/resource/loaders/index.js +1 -0
  29. package/esm/resource/loaders/remote.d.ts +1 -1
  30. package/esm/resource/loaders/remote.js +10 -2
  31. package/esm/resource/loaders/search.d.ts +1 -0
  32. package/esm/resource/loaders/search.js +51 -0
  33. package/esm/resource/types/index.d.ts +1 -0
  34. package/esm/resource/types/resource.d.ts +8 -1
  35. package/esm/resource/types/scene.d.ts +1 -0
  36. package/esm/resource/utils/data-uri.js +20 -11
  37. package/esm/resource/utils/parser.js +92 -1
  38. package/esm/resource/utils/ref.js +2 -2
  39. package/esm/runtime/Infographic.d.ts +7 -6
  40. package/esm/runtime/Infographic.js +48 -17
  41. package/esm/runtime/utils.d.ts +4 -2
  42. package/esm/runtime/utils.js +33 -13
  43. package/esm/syntax/index.d.ts +3 -0
  44. package/esm/syntax/index.js +101 -0
  45. package/esm/syntax/mapper.d.ts +3 -0
  46. package/esm/syntax/mapper.js +238 -0
  47. package/esm/syntax/parser.d.ts +14 -0
  48. package/esm/syntax/parser.js +142 -0
  49. package/esm/syntax/schema.d.ts +6 -0
  50. package/esm/syntax/schema.js +74 -0
  51. package/esm/syntax/types.d.ts +61 -0
  52. package/esm/syntax/types.js +1 -0
  53. package/esm/templates/built-in.js +4 -0
  54. package/esm/templates/hierarchy-tree.js +25 -11
  55. package/esm/templates/sequence-stairs.d.ts +2 -0
  56. package/esm/templates/sequence-stairs.js +42 -0
  57. package/esm/templates/word-cloud.d.ts +2 -0
  58. package/esm/templates/word-cloud.js +19 -0
  59. package/esm/themes/types.d.ts +1 -1
  60. package/esm/utils/design.d.ts +2 -0
  61. package/esm/utils/design.js +10 -0
  62. package/esm/utils/font.js +11 -1
  63. package/esm/utils/index.d.ts +1 -0
  64. package/esm/utils/index.js +1 -0
  65. package/lib/constants/element.d.ts +1 -1
  66. package/lib/constants/index.d.ts +1 -0
  67. package/lib/constants/index.js +1 -0
  68. package/lib/constants/service.d.ts +1 -0
  69. package/lib/constants/service.js +4 -0
  70. package/lib/designs/components/Illus.js +1 -1
  71. package/lib/designs/structures/chart-wordcloud.d.ts +11 -0
  72. package/lib/designs/structures/chart-wordcloud.js +160 -0
  73. package/lib/designs/structures/hierarchy-tree.d.ts +2 -0
  74. package/lib/designs/structures/hierarchy-tree.js +179 -50
  75. package/lib/designs/structures/index.d.ts +2 -0
  76. package/lib/designs/structures/index.js +2 -0
  77. package/lib/designs/structures/sequence-stairs-front.d.ts +8 -0
  78. package/lib/designs/structures/sequence-stairs-front.js +120 -0
  79. package/lib/designs/types.d.ts +8 -0
  80. package/lib/editor/managers/state.js +1 -1
  81. package/lib/index.d.ts +2 -0
  82. package/lib/index.js +4 -1
  83. package/lib/options/parser.d.ts +1 -1
  84. package/lib/options/parser.js +32 -14
  85. package/lib/renderer/composites/icon.js +1 -1
  86. package/lib/renderer/composites/illus.js +1 -1
  87. package/lib/resource/loader.d.ts +2 -2
  88. package/lib/resource/loader.js +21 -10
  89. package/lib/resource/loaders/index.d.ts +1 -0
  90. package/lib/resource/loaders/index.js +1 -0
  91. package/lib/resource/loaders/remote.d.ts +1 -1
  92. package/lib/resource/loaders/remote.js +10 -2
  93. package/lib/resource/loaders/search.d.ts +1 -0
  94. package/lib/resource/loaders/search.js +54 -0
  95. package/lib/resource/types/index.d.ts +1 -0
  96. package/lib/resource/types/resource.d.ts +8 -1
  97. package/lib/resource/types/scene.d.ts +1 -0
  98. package/lib/resource/utils/data-uri.js +20 -11
  99. package/lib/resource/utils/parser.js +92 -1
  100. package/lib/resource/utils/ref.js +2 -2
  101. package/lib/runtime/Infographic.d.ts +7 -6
  102. package/lib/runtime/Infographic.js +47 -16
  103. package/lib/runtime/utils.d.ts +4 -2
  104. package/lib/runtime/utils.js +35 -13
  105. package/lib/syntax/index.d.ts +3 -0
  106. package/lib/syntax/index.js +104 -0
  107. package/lib/syntax/mapper.d.ts +3 -0
  108. package/lib/syntax/mapper.js +242 -0
  109. package/lib/syntax/parser.d.ts +14 -0
  110. package/lib/syntax/parser.js +146 -0
  111. package/lib/syntax/schema.d.ts +6 -0
  112. package/lib/syntax/schema.js +77 -0
  113. package/lib/syntax/types.d.ts +61 -0
  114. package/lib/syntax/types.js +2 -0
  115. package/lib/templates/built-in.js +4 -0
  116. package/lib/templates/hierarchy-tree.js +25 -11
  117. package/lib/templates/sequence-stairs.d.ts +2 -0
  118. package/lib/templates/sequence-stairs.js +45 -0
  119. package/lib/templates/word-cloud.d.ts +2 -0
  120. package/lib/templates/word-cloud.js +22 -0
  121. package/lib/themes/types.d.ts +1 -1
  122. package/lib/utils/design.d.ts +2 -0
  123. package/lib/utils/design.js +13 -0
  124. package/lib/utils/font.js +11 -1
  125. package/lib/utils/index.d.ts +1 -0
  126. package/lib/utils/index.js +1 -0
  127. package/package.json +1 -1
  128. package/src/constants/element.ts +1 -1
  129. package/src/constants/index.ts +1 -0
  130. package/src/constants/service.ts +1 -0
  131. package/src/designs/components/Illus.tsx +1 -1
  132. package/src/designs/structures/chart-wordcloud.tsx +278 -0
  133. package/src/designs/structures/hierarchy-tree.tsx +212 -59
  134. package/src/designs/structures/index.ts +2 -0
  135. package/src/designs/structures/sequence-stairs-front.tsx +291 -0
  136. package/src/designs/types.ts +9 -0
  137. package/src/editor/managers/state.ts +1 -1
  138. package/src/index.ts +2 -0
  139. package/src/options/parser.ts +57 -28
  140. package/src/renderer/composites/icon.ts +1 -1
  141. package/src/renderer/composites/illus.ts +1 -1
  142. package/src/resource/loader.ts +22 -8
  143. package/src/resource/loaders/index.ts +1 -0
  144. package/src/resource/loaders/remote.ts +9 -2
  145. package/src/resource/loaders/search.ts +52 -0
  146. package/src/resource/types/index.ts +2 -1
  147. package/src/resource/types/resource.ts +12 -1
  148. package/src/resource/types/scene.ts +1 -0
  149. package/src/resource/utils/data-uri.ts +20 -11
  150. package/src/resource/utils/parser.ts +103 -2
  151. package/src/resource/utils/ref.ts +2 -2
  152. package/src/runtime/Infographic.tsx +74 -22
  153. package/src/runtime/utils.ts +38 -16
  154. package/src/syntax/index.ts +124 -0
  155. package/src/syntax/mapper.ts +362 -0
  156. package/src/syntax/parser.ts +171 -0
  157. package/src/syntax/schema.ts +98 -0
  158. package/src/syntax/types.ts +89 -0
  159. package/src/templates/built-in.ts +4 -0
  160. package/src/templates/hierarchy-tree.ts +34 -11
  161. package/src/templates/sequence-stairs.ts +44 -0
  162. package/src/templates/word-cloud.ts +21 -0
  163. package/src/themes/types.ts +1 -1
  164. package/src/utils/design.ts +14 -0
  165. package/src/utils/font.ts +11 -1
  166. package/src/utils/index.ts +1 -0
  167. package/esm/resource/types/font.d.ts +0 -12
  168. package/lib/resource/types/font.d.ts +0 -12
  169. package/src/resource/types/font.ts +0 -23
  170. /package/esm/resource/types/{font.js → scene.js} +0 -0
  171. /package/lib/resource/types/{font.js → scene.js} +0 -0
@@ -0,0 +1,116 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@antv/infographic/jsx-runtime";
2
+ import { getElementBounds, Group, Path, Polygon, Rect, Text } from '../../jsx';
3
+ import { BtnAdd, BtnRemove, BtnsGroup, ItemsGroup } from '../components';
4
+ import { FlexLayout } from '../layouts';
5
+ import { getColorPrimary, getPaletteColor } from '../utils';
6
+ import { registerStructure } from './registry';
7
+ export const SequenceStairsFront = (props) => {
8
+ const { Title, Item, data, options, gap = 16, perspectiveFactor = 0.2, width = 720, } = props;
9
+ const { title, desc, items = [] } = data;
10
+ const TitleComponent = Title;
11
+ const titleContent = TitleComponent ? (_jsx(TitleComponent, { title: title, desc: desc })) : null;
12
+ if (items.length === 0) {
13
+ const btnAddElement = _jsx(BtnAdd, { indexes: [0], x: 0, y: 0 });
14
+ return (_jsxs(FlexLayout, { id: "infographic-container", flexDirection: "column", justifyContent: "center", alignItems: "center", children: [titleContent, _jsx(Group, { children: _jsx(BtnsGroup, { children: btnAddElement }) })] }));
15
+ }
16
+ const colorPrimary = getColorPrimary(options);
17
+ const btnBounds = getElementBounds(_jsx(BtnAdd, { indexes: [0] }));
18
+ const itemBounds = getElementBounds(_jsx(Item, { indexes: [0], data: data, datum: items[0] || {}, positionH: "center" }));
19
+ const connectorGap = 12;
20
+ const baseConnectorWidth = Math.min(width * 0.1, 100);
21
+ const bottomStepWidth = Math.min(width * 0.45, Math.max(80, width - itemBounds.width - connectorGap - baseConnectorWidth));
22
+ const baseDepth = Math.max(24, bottomStepWidth * perspectiveFactor * 0.35);
23
+ const baseStepHeight = 56;
24
+ const minStepHeight = baseStepHeight * 0.7;
25
+ const heightStep = items.length > 1
26
+ ? (baseStepHeight - minStepHeight) / (items.length - 1)
27
+ : 0;
28
+ const topMargin = Math.max(btnBounds.height + baseDepth + 20, baseDepth + 28);
29
+ const centerX = bottomStepWidth / 2;
30
+ const itemX = bottomStepWidth + baseConnectorWidth + connectorGap;
31
+ const stepHeights = items.map((_, index) => baseStepHeight - heightStep * index);
32
+ const stepDepths = items.map((_, index) => Math.max(baseDepth * (1 - index * 0.05), baseDepth * 0.7));
33
+ const stepYs = [];
34
+ const depthFromTop = [];
35
+ const lastIndex = items.length - 1;
36
+ stepYs[lastIndex] = topMargin;
37
+ depthFromTop[lastIndex] = 0;
38
+ for (let i = lastIndex - 1; i >= 0; i -= 1) {
39
+ depthFromTop[i] = depthFromTop[i + 1] + stepDepths[i] + gap;
40
+ stepYs[i] = stepYs[i + 1] + stepDepths[i] + stepHeights[i + 1] + gap;
41
+ }
42
+ const totalDepthSpan = depthFromTop[0] || stepDepths[0] || baseDepth;
43
+ const minStepWidth = bottomStepWidth * (0.55 + perspectiveFactor * 0.1);
44
+ const widthShrink = bottomStepWidth - minStepWidth;
45
+ const stepWidths = items.map((_, index) => {
46
+ const ratio = totalDepthSpan === 0 ? 1 : depthFromTop[index] / totalDepthSpan;
47
+ return minStepWidth + widthShrink * ratio;
48
+ });
49
+ const connectorWidths = stepWidths.map((stepWidth) => {
50
+ const connectorStartX = centerX + stepWidth / 2;
51
+ return Math.max(0, itemX - connectorGap - connectorStartX);
52
+ });
53
+ const stepElements = [];
54
+ const itemElements = [];
55
+ const btnElements = [];
56
+ const connectorElements = [];
57
+ const spineElements = [];
58
+ const spineTop = 0;
59
+ const spineBottom = (stepYs[0] || topMargin) + (stepHeights[0] || baseStepHeight);
60
+ const arrowHeight = spineBottom - spineTop;
61
+ const arrowHeadHeight = 35;
62
+ const topStepWidth = stepWidths[lastIndex] || minStepWidth;
63
+ const arrowTopWidth = topStepWidth * 0.8;
64
+ const arrowNeckWidth = arrowTopWidth * 0.65;
65
+ const arrowBottomWidth = (stepWidths[0] || bottomStepWidth) * 0.9;
66
+ spineElements.push(_jsx(Polygon, { points: [
67
+ { x: centerX, y: spineTop },
68
+ { x: centerX + arrowTopWidth / 2, y: spineTop + arrowHeadHeight },
69
+ { x: centerX + arrowNeckWidth / 2, y: spineTop + arrowHeadHeight },
70
+ { x: centerX + arrowBottomWidth / 2, y: spineBottom },
71
+ { x: centerX - arrowBottomWidth / 2, y: spineBottom },
72
+ { x: centerX - arrowNeckWidth / 2, y: spineTop + arrowHeadHeight },
73
+ { x: centerX - arrowTopWidth / 2, y: spineTop + arrowHeadHeight },
74
+ ], fill: "rgba(0,0,0,0.12)", width: arrowBottomWidth, height: arrowHeight, "data-element-type": "shape" }));
75
+ let previousCenterY = stepYs[lastIndex] + stepHeights[lastIndex] / 2;
76
+ items.forEach((item, index) => {
77
+ const indexes = [index];
78
+ const stepWidth = stepWidths[index];
79
+ const stepDepth = stepDepths[index];
80
+ const stepX = centerX - stepWidth / 2;
81
+ const stepHeight = stepHeights[index];
82
+ const stepY = stepYs[index];
83
+ const topY = stepY - stepDepth;
84
+ const rectCenterY = stepY + stepHeight / 2;
85
+ const stepColor = getPaletteColor(options, indexes) || colorPrimary;
86
+ stepElements.push(_jsx(Rect, { x: stepX, y: stepY, width: stepWidth, height: stepHeight, fill: stepColor, "data-element-type": "shape" }), _jsx(Polygon, { points: [
87
+ { x: stepX, y: stepY },
88
+ { x: stepX + stepWidth, y: stepY },
89
+ { x: stepX + stepWidth - stepDepth / 2, y: topY },
90
+ { x: stepX + stepDepth / 2, y: topY },
91
+ ], fill: stepColor, opacity: "0.3", width: stepWidth, height: stepDepth, "data-element-type": "shape" }), _jsx(Text, { x: stepX, y: stepY, width: stepWidth, height: stepHeight, fontSize: stepHeight / 2, fontWeight: "bold", alignHorizontal: "center", alignVertical: "middle", fill: "#ffffff", children: String(index + 1).padStart(2, '0') }));
92
+ const connectorStartX = stepX + stepWidth;
93
+ const connectorEndY = rectCenterY;
94
+ const connectorWidth = connectorWidths[index];
95
+ const lineEndX = connectorStartX + connectorWidth;
96
+ connectorElements.push(_jsx(Path, { d: `M ${connectorStartX} ${connectorEndY} L ${lineEndX} ${connectorEndY}`, stroke: stepColor, strokeWidth: 2, fill: "none", width: connectorWidth, height: 2, "data-element-type": "shape" }), _jsx(Path, { d: `M ${lineEndX} ${connectorEndY} L ${lineEndX} ${connectorEndY}`, stroke: stepColor, strokeWidth: 6, strokeLinecap: "round", width: 1, height: 1, "data-element-type": "shape" }));
97
+ const itemY = rectCenterY - itemBounds.height / 2;
98
+ itemElements.push(_jsx(Item, { indexes: indexes, datum: item, data: data, x: itemX, y: itemY, positionV: "middle" }));
99
+ btnElements.push(_jsx(BtnRemove, { indexes: indexes, x: itemX + itemBounds.width + 10, y: itemY + itemBounds.height / 2 - btnBounds.height / 2 }));
100
+ if (index === 0) {
101
+ btnElements.push(_jsx(BtnAdd, { indexes: [0], x: itemX + (itemBounds.width - btnBounds.width) / 2, y: itemY - btnBounds.height - 12 }));
102
+ }
103
+ else {
104
+ btnElements.push(_jsx(BtnAdd, { indexes: indexes, x: itemX + (itemBounds.width - btnBounds.width) / 2, y: (previousCenterY + rectCenterY) / 2 - btnBounds.height / 2 }));
105
+ }
106
+ previousCenterY = rectCenterY;
107
+ });
108
+ const lastCenterY = previousCenterY;
109
+ const lastItemY = lastCenterY - itemBounds.height / 2;
110
+ btnElements.push(_jsx(BtnAdd, { indexes: [items.length], x: itemX + (itemBounds.width - btnBounds.width) / 2, y: lastItemY + itemBounds.height + 12 }));
111
+ return (_jsxs(FlexLayout, { id: "infographic-container", flexDirection: "column", justifyContent: "center", alignItems: "center", children: [titleContent, _jsxs(Group, { children: [_jsx(Group, { children: spineElements }), _jsx(Group, { children: stepElements }), _jsx(Group, { children: connectorElements }), _jsx(ItemsGroup, { children: itemElements }), _jsx(BtnsGroup, { children: btnElements })] })] }));
112
+ };
113
+ registerStructure('sequence-stairs-front', {
114
+ component: SequenceStairsFront,
115
+ composites: ['title', 'item'],
116
+ });
@@ -20,6 +20,14 @@ export interface ParsedDesignsOptions {
20
20
  item: WithProps<Item>;
21
21
  items: WithProps<Item>[];
22
22
  }
23
+ export interface NullableParsedDesignsOptions {
24
+ structure: WithProps<Structure> | null;
25
+ title: {
26
+ component: ComponentType<any> | null;
27
+ };
28
+ item: WithProps<Item> | null;
29
+ items: (WithProps<Item> | null)[];
30
+ }
23
31
  type WithType<T> = T & {
24
32
  type: string;
25
33
  };
@@ -105,7 +105,7 @@ export class StateManager {
105
105
  "item-label" /* ElementTypeEnum.ItemLabel */ === role ||
106
106
  "item-desc" /* ElementTypeEnum.ItemDesc */ === role ||
107
107
  "item-value" /* ElementTypeEnum.ItemValue */ === role ||
108
- "items-illus" /* ElementTypeEnum.ItemsIllus */ === role;
108
+ "item-illus" /* ElementTypeEnum.ItemIllus */ === role;
109
109
  const indexes = isItemElement ? getIndexesFromElement(element) : undefined;
110
110
  if (isItemElement) {
111
111
  const datum = getDatumByIndexes(data, indexes);
package/esm/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export { Defs, Ellipse, Fragment, Group, Path, Polygon, Rect, Text, cloneElement
7
7
  export { getFont, getFonts, getPalette, getPaletteColor, registerFont, registerPalette, registerPattern, setDefaultFont, } from './renderer';
8
8
  export { loadSVGResource, registerResourceLoader } from './resource';
9
9
  export { Infographic } from './runtime';
10
+ export { parseSyntax } from './syntax';
10
11
  export { getTemplate, getTemplates, registerTemplate } from './templates';
11
12
  export { getTheme, getThemes, registerTheme } from './themes';
12
13
  export { parseSVG } from './utils';
@@ -14,6 +15,7 @@ export type { EditBarOptions } from './editor';
14
15
  export type { Bounds, ComponentType, DefsProps, EllipseProps, FragmentProps, GroupProps, JSXElement, JSXElementConstructor, JSXNode, PathProps, Point, PolygonProps, RectProps, RenderableNode, SVGAttributes, SVGProps, TextProps, WithChildren, } from './jsx';
15
16
  export type { InfographicOptions, ParsedInfographicOptions } from './options';
16
17
  export type { GradientConfig, IRenderer, LinearGradient, Palette, PatternConfig, PatternGenerator, PatternStyle, RadialGradient, RoughConfig, StylizeConfig, } from './renderer';
18
+ export type { SyntaxError, SyntaxParseResult } from './syntax';
17
19
  export type { ParsedTemplateOptions, TemplateOptions } from './templates';
18
20
  export type { ThemeColors, ThemeConfig, ThemeSeed } from './themes';
19
21
  export type { Data, Font, FontWeightName, ImageResource, ItemDatum, TextHorizontalAlign, TextVerticalAlign, } from './types';
package/esm/index.js CHANGED
@@ -8,6 +8,7 @@ export { Defs, Ellipse, Fragment, Group, Path, Polygon, Rect, Text, cloneElement
8
8
  export { getFont, getFonts, getPalette, getPaletteColor, registerFont, registerPalette, registerPattern, setDefaultFont, } from './renderer';
9
9
  export { loadSVGResource, registerResourceLoader } from './resource';
10
10
  export { Infographic } from './runtime';
11
+ export { parseSyntax } from './syntax';
11
12
  export { getTemplate, getTemplates, registerTemplate } from './templates';
12
13
  export { getTheme, getThemes, registerTheme } from './themes';
13
14
  export { parseSVG } from './utils';
@@ -1,2 +1,2 @@
1
1
  import type { InfographicOptions, ParsedInfographicOptions } from './types';
2
- export declare function parseOptions(options: InfographicOptions): ParsedInfographicOptions;
2
+ export declare function parseOptions(options: Partial<InfographicOptions>): Partial<ParsedInfographicOptions>;
@@ -2,26 +2,44 @@ import { merge } from 'lodash-es';
2
2
  import { getItem, getStructure, getTemplate, Title, } from '../designs';
3
3
  import { getPaletteColor } from '../renderer';
4
4
  import { generateThemeColors, getTheme } from '../themes';
5
- import { isDarkColor, parsePadding } from '../utils';
5
+ import { isDarkColor, isNonNullableParsedDesignsOptions, parsePadding, } from '../utils';
6
6
  export function parseOptions(options) {
7
7
  const { container = '#container', padding = 0, template, design, theme, themeConfig, ...restOptions } = options;
8
8
  const parsedContainer = typeof container === 'string'
9
9
  ? document.querySelector(container) || document.createElement('div')
10
10
  : container;
11
11
  const templateOptions = template
12
- ? getTemplate(template) || {}
13
- : {};
14
- const { design: templateDesign, ...restTemplateOptions } = templateOptions;
15
- return {
16
- ...restTemplateOptions,
17
- ...restOptions,
12
+ ? getTemplate(template)
13
+ : undefined;
14
+ const mergedThemeConfig = merge({}, templateOptions?.themeConfig, themeConfig);
15
+ const resolvedThemeConfig = theme || themeConfig || templateOptions?.themeConfig
16
+ ? parseTheme(theme, mergedThemeConfig)
17
+ : undefined;
18
+ const parsed = {
18
19
  container: parsedContainer,
19
20
  padding: parsePadding(padding),
20
- template,
21
- design: parseDesign({ ...templateDesign, ...design }, options),
22
- theme,
23
- themeConfig: parseTheme(theme, merge({ ...restTemplateOptions?.themeConfig }, themeConfig)),
24
21
  };
22
+ if (templateOptions) {
23
+ const { design: templateDesign, ...restTemplateOptions } = templateOptions;
24
+ Object.assign(parsed, restTemplateOptions);
25
+ }
26
+ Object.assign(parsed, restOptions);
27
+ if (template)
28
+ parsed.template = template;
29
+ if (templateOptions?.design || design) {
30
+ const parsedDesign = parseDesign({ ...templateOptions?.design, ...design }, resolvedThemeConfig
31
+ ? { ...options, themeConfig: resolvedThemeConfig }
32
+ : options);
33
+ if (isNonNullableParsedDesignsOptions(parsedDesign)) {
34
+ parsed.design = parsedDesign;
35
+ }
36
+ }
37
+ if (theme)
38
+ parsed.theme = theme;
39
+ if (resolvedThemeConfig) {
40
+ parsed.themeConfig = resolvedThemeConfig;
41
+ }
42
+ return parsed;
25
43
  }
26
44
  function normalizeWithType(obj) {
27
45
  if (typeof obj === 'string')
@@ -44,11 +62,11 @@ function parseDesign(config, options) {
44
62
  }
45
63
  function parseDesignStructure(config) {
46
64
  if (!config)
47
- throw new Error('Structure is required in design or template');
65
+ return null;
48
66
  const { type, ...userProps } = normalizeWithType(config);
49
67
  const structure = getStructure(type);
50
68
  if (!structure)
51
- throw new Error(`Structure ${type} not found`);
69
+ return null;
52
70
  const { component } = structure;
53
71
  return {
54
72
  ...structure,
@@ -69,11 +87,11 @@ function parseDesignTitle(config, options) {
69
87
  }
70
88
  function parseDesignItem(config, options) {
71
89
  if (!config)
72
- throw new Error('Item is required in design or template');
90
+ return null;
73
91
  const { type, ...userProps } = normalizeWithType(config);
74
92
  const item = getItem(type);
75
93
  if (!item)
76
- throw new Error(`Item ${type} not found`);
94
+ return null;
77
95
  const { component, options: itemOptions } = item;
78
96
  return {
79
97
  ...item,
@@ -19,7 +19,7 @@ export function renderItemIcon(svg, node, value, options) {
19
19
  }
20
20
  function createIcon(svg, node, value, attrs) {
21
21
  // load async
22
- loadResource(svg, value);
22
+ loadResource(svg, 'icon', value);
23
23
  return createIconElement(value, {
24
24
  ...getAttributes(node, [
25
25
  'id',
@@ -8,7 +8,7 @@ export function renderIllus(svg, node, value) {
8
8
  return null;
9
9
  const id = getResourceId(config);
10
10
  const clipPathId = createClipPath(svg, node, id);
11
- loadResource(svg, config);
11
+ loadResource(svg, 'illus', config);
12
12
  const { data, color } = config;
13
13
  return createIllusElement(id, {
14
14
  ...parseIllusBounds(node),
@@ -1,6 +1,6 @@
1
- import type { ResourceConfig } from './types';
1
+ import type { ResourceConfig, ResourceScene } from './types';
2
2
  /**
3
3
  * load resource into svg defs
4
4
  * @returns resource ref id
5
5
  */
6
- export declare function loadResource(svg: SVGSVGElement | null, config: string | ResourceConfig): Promise<string | null>;
6
+ export declare function loadResource(svg: SVGSVGElement | null, scene: ResourceScene, config: string | ResourceConfig): Promise<string | null>;
@@ -1,20 +1,31 @@
1
1
  import { getOrCreateDefs } from '../utils';
2
- import { loadImageBase64Resource, loadRemoteResource, loadSVGResource, } from './loaders';
2
+ import { loadImageBase64Resource, loadRemoteResource, loadSearchResource, loadSVGResource, } from './loaders';
3
3
  import { getCustomResourceLoader } from './registry';
4
4
  import { getResourceId, parseResourceConfig } from './utils';
5
- async function getResource(config) {
5
+ async function getResource(scene, config) {
6
6
  const cfg = parseResourceConfig(config);
7
7
  if (!cfg)
8
8
  return null;
9
- const { type, data } = cfg;
10
- if (type === 'image') {
11
- return await loadImageBase64Resource(data);
12
- }
13
- else if (type === 'svg') {
9
+ cfg.scene || (cfg.scene = scene);
10
+ const { source, data, format, encoding } = cfg;
11
+ if (source === 'inline') {
12
+ const isDataURI = data.startsWith('data:');
13
+ if (format === 'svg' && encoding === 'raw') {
14
+ return loadSVGResource(data);
15
+ }
16
+ if (format === 'svg' && isDataURI) {
17
+ return await loadImageBase64Resource(data);
18
+ }
19
+ if (isDataURI || format === 'image') {
20
+ return await loadImageBase64Resource(data);
21
+ }
14
22
  return loadSVGResource(data);
15
23
  }
16
- else if (type === 'remote') {
17
- return await loadRemoteResource(data);
24
+ else if (source === 'remote') {
25
+ return await loadRemoteResource(data, format);
26
+ }
27
+ else if (source === 'search') {
28
+ return await loadSearchResource(data, format);
18
29
  }
19
30
  else {
20
31
  const customLoader = getCustomResourceLoader();
@@ -29,7 +40,7 @@ const RESOURCE_LOAD_MAP = new WeakMap();
29
40
  * load resource into svg defs
30
41
  * @returns resource ref id
31
42
  */
32
- export async function loadResource(svg, config) {
43
+ export async function loadResource(svg, scene, config) {
33
44
  if (!svg)
34
45
  return null;
35
46
  const cfg = parseResourceConfig(config);
@@ -38,7 +49,7 @@ export async function loadResource(svg, config) {
38
49
  const id = getResourceId(cfg);
39
50
  const resource = RESOURCE_MAP.has(id)
40
51
  ? RESOURCE_MAP.get(id) || null
41
- : await getResource(cfg);
52
+ : await getResource(scene, cfg);
42
53
  if (!resource)
43
54
  return null;
44
55
  if (!RESOURCE_LOAD_MAP.has(svg))
@@ -1,3 +1,4 @@
1
1
  export * from './image';
2
2
  export * from './remote';
3
+ export * from './search';
3
4
  export * from './svg';
@@ -1,3 +1,4 @@
1
1
  export * from './image';
2
2
  export * from './remote';
3
+ export * from './search';
3
4
  export * from './svg';
@@ -1 +1 @@
1
- export declare function loadRemoteResource(resource: string): Promise<SVGSymbolElement | null>;
1
+ export declare function loadRemoteResource(resource: string, format?: string): Promise<SVGSymbolElement | null>;
@@ -8,14 +8,22 @@ function isRemoteResource(resource) {
8
8
  return false;
9
9
  }
10
10
  }
11
- export async function loadRemoteResource(resource) {
11
+ function shouldParseAsSVG(contentType, format) {
12
+ const normalized = contentType.toLowerCase();
13
+ if (normalized.includes('image/svg'))
14
+ return true;
15
+ if (!contentType && format === 'svg')
16
+ return true;
17
+ return false;
18
+ }
19
+ export async function loadRemoteResource(resource, format) {
12
20
  if (!resource || !isRemoteResource(resource))
13
21
  return null;
14
22
  const response = await fetch(resource);
15
23
  if (!response.ok)
16
24
  throw new Error('Failed to load resource');
17
25
  const contentType = response.headers.get('Content-Type') || '';
18
- if (contentType.includes('image/svg+xml')) {
26
+ if (shouldParseAsSVG(contentType, format)) {
19
27
  const svgText = await response.text();
20
28
  return loadSVGResource(svgText);
21
29
  }
@@ -0,0 +1 @@
1
+ export declare function loadSearchResource(query: string, format?: string): Promise<SVGSymbolElement | null>;
@@ -0,0 +1,51 @@
1
+ import { ICON_SERVICE_URL } from '../../constants';
2
+ import { loadImageBase64Resource } from './image';
3
+ import { loadRemoteResource } from './remote';
4
+ import { loadSVGResource } from './svg';
5
+ const queryIcon = async (query) => {
6
+ try {
7
+ const params = new URLSearchParams({ text: query, topK: '1' });
8
+ const url = `${ICON_SERVICE_URL}?${params.toString()}`;
9
+ const response = await fetch(url);
10
+ if (!response.ok)
11
+ return null;
12
+ const result = await response.json();
13
+ if (!result?.status || !Array.isArray(result.data?.data))
14
+ return null;
15
+ return result.data.data[0] || null;
16
+ }
17
+ catch (error) {
18
+ console.error(`Failed to query icon for "${query}":`, error);
19
+ return null;
20
+ }
21
+ };
22
+ function isDataURI(resource) {
23
+ return resource.startsWith('data:');
24
+ }
25
+ function looksLikeSVG(resource) {
26
+ const str = resource.trim();
27
+ return str.startsWith('<svg') || str.startsWith('<symbol');
28
+ }
29
+ export async function loadSearchResource(query, format) {
30
+ if (!query)
31
+ return null;
32
+ const result = await queryIcon(query);
33
+ if (!result)
34
+ return null;
35
+ if (looksLikeSVG(result))
36
+ return loadSVGResource(result);
37
+ if (isDataURI(result)) {
38
+ const mimeType = result.match(/^data:([^;]+)/)?.[1] || '';
39
+ const isBase64 = result.includes(';base64,');
40
+ if (mimeType === 'image/svg+xml' && !isBase64) {
41
+ const commaIndex = result.indexOf(',');
42
+ const svgText = commaIndex >= 0 ? result.slice(commaIndex + 1) : result;
43
+ return loadSVGResource(svgText);
44
+ }
45
+ if (mimeType === 'image/svg+xml' && format === 'svg' && isBase64) {
46
+ return loadImageBase64Resource(result);
47
+ }
48
+ return loadImageBase64Resource(result);
49
+ }
50
+ return loadRemoteResource(result, format);
51
+ }
@@ -1 +1,2 @@
1
1
  export type * from './resource';
2
+ export type * from './scene';
@@ -1,6 +1,13 @@
1
+ import { ResourceScene } from './scene';
2
+ export type ResourceSource = 'inline' | 'remote' | 'search' | 'custom';
3
+ export type ResourceFormat = 'svg' | 'image' | 'json' | 'binary' | string;
4
+ export type ResourceEncoding = 'raw' | 'data-uri' | 'base64' | string;
1
5
  export interface ResourceConfig {
2
- type: 'image' | 'svg' | 'remote' | 'custom';
6
+ source: ResourceSource;
7
+ format?: ResourceFormat;
8
+ encoding?: ResourceEncoding;
3
9
  data: string;
10
+ scene?: ResourceScene;
4
11
  [key: string]: any;
5
12
  }
6
13
  export type Resource = SVGSymbolElement;
@@ -0,0 +1 @@
1
+ export type ResourceScene = 'icon' | 'illus';
@@ -1,6 +1,6 @@
1
1
  export function parseDataURI(resource) {
2
2
  if (!resource.startsWith('data:'))
3
- return { type: 'custom', data: resource };
3
+ return null;
4
4
  const commaIndex = resource.indexOf(',');
5
5
  if (commaIndex === -1)
6
6
  return null;
@@ -8,15 +8,24 @@ export function parseDataURI(resource) {
8
8
  const data = resource.slice(commaIndex + 1);
9
9
  const parts = header.split(';');
10
10
  const mimeType = parts[0];
11
- if (parts.includes('base64')) {
12
- return { type: "image" /* DataURITypeEnum.Image */, data: resource };
11
+ const isBase64 = parts.includes('base64');
12
+ if (mimeType === 'image/svg+xml' && !isBase64) {
13
+ const decoded = data.startsWith('%3C') ? decodeURIComponent(data) : data;
14
+ return {
15
+ source: 'inline',
16
+ format: 'svg',
17
+ encoding: 'raw',
18
+ data: decoded,
19
+ };
20
+ }
21
+ if (mimeType.startsWith('image/')) {
22
+ const format = mimeType === 'image/svg+xml' ? 'svg' : 'image';
23
+ return {
24
+ source: 'inline',
25
+ format,
26
+ encoding: isBase64 ? 'base64' : 'data-uri',
27
+ data: resource,
28
+ };
13
29
  }
14
- const typeMap = {
15
- 'text/url': "remote" /* DataURITypeEnum.Remote */,
16
- 'image/svg+xml': "svg" /* DataURITypeEnum.SVG */,
17
- };
18
- const type = typeMap[mimeType];
19
- if (type)
20
- return { type, data };
21
- return { type: 'custom', data: resource };
30
+ return null;
22
31
  }
@@ -1,6 +1,97 @@
1
1
  import { parseDataURI } from './data-uri';
2
+ const KNOWN_FORMATS = new Set(['svg', 'png', 'jpg', 'jpeg', 'webp', 'gif']);
3
+ function looksLikeSVG(resource) {
4
+ const str = resource.trim();
5
+ return str.startsWith('<svg') || str.startsWith('<symbol');
6
+ }
7
+ function inferFormatFromUrl(url) {
8
+ const lower = url.toLowerCase();
9
+ if (lower.endsWith('.svg') || lower.includes('.svg?'))
10
+ return 'svg';
11
+ if (lower.endsWith('.png') ||
12
+ lower.endsWith('.jpg') ||
13
+ lower.endsWith('.jpeg') ||
14
+ lower.endsWith('.webp') ||
15
+ lower.endsWith('.gif')) {
16
+ return 'image';
17
+ }
18
+ return undefined;
19
+ }
20
+ function parseRefResource(resource) {
21
+ if (!resource.startsWith('ref:'))
22
+ return null;
23
+ const rest = resource.slice(4);
24
+ const [source, ...restParts] = rest.split(':');
25
+ if (!source || restParts.length === 0)
26
+ return null;
27
+ let format;
28
+ if (restParts.length > 1 && KNOWN_FORMATS.has(restParts[0].toLowerCase())) {
29
+ format = restParts.shift()?.toLowerCase();
30
+ }
31
+ const payload = restParts.join(':');
32
+ if (!payload)
33
+ return null;
34
+ const normalizedSource = source === 'url' ? 'remote' : source;
35
+ if (normalizedSource === 'remote') {
36
+ return {
37
+ source: 'remote',
38
+ format: format || inferFormatFromUrl(payload) || undefined,
39
+ data: payload,
40
+ };
41
+ }
42
+ if (normalizedSource === 'search') {
43
+ return {
44
+ source: 'search',
45
+ format: format || 'svg',
46
+ data: payload,
47
+ };
48
+ }
49
+ if (normalizedSource === 'svg') {
50
+ return { source: 'inline', format: 'svg', data: payload, encoding: 'raw' };
51
+ }
52
+ return { source: 'custom', data: resource, format };
53
+ }
2
54
  export function parseResourceConfig(config) {
3
55
  if (!config)
4
56
  return null;
5
- return typeof config === 'string' ? parseDataURI(config) : config;
57
+ if (typeof config !== 'string') {
58
+ if (config.source)
59
+ return config;
60
+ const legacy = config;
61
+ if (legacy.type === 'image') {
62
+ return { source: 'inline', format: 'image', data: legacy.data };
63
+ }
64
+ if (legacy.type === 'svg') {
65
+ return {
66
+ source: 'inline',
67
+ format: 'svg',
68
+ encoding: 'raw',
69
+ data: legacy.data,
70
+ };
71
+ }
72
+ if (legacy.type === 'remote') {
73
+ return { source: 'remote', format: legacy.format, data: legacy.data };
74
+ }
75
+ if (legacy.type === 'search') {
76
+ return {
77
+ source: 'search',
78
+ format: legacy.format || 'svg',
79
+ data: legacy.data,
80
+ };
81
+ }
82
+ if (legacy.type === 'custom') {
83
+ return { source: 'custom', data: legacy.data };
84
+ }
85
+ return null;
86
+ }
87
+ const dataURIConfig = parseDataURI(config);
88
+ if (dataURIConfig)
89
+ return dataURIConfig;
90
+ const refConfig = parseRefResource(config);
91
+ if (refConfig)
92
+ return refConfig;
93
+ if (looksLikeSVG(config)) {
94
+ return { source: 'inline', format: 'svg', encoding: 'raw', data: config };
95
+ }
96
+ return { source: 'custom', data: config };
6
97
  }
@@ -1,7 +1,7 @@
1
1
  import { getSimpleHash } from '../../utils';
2
- import { parseDataURI } from './data-uri';
2
+ import { parseResourceConfig } from './parser';
3
3
  export function getResourceId(config) {
4
- const cfg = typeof config === 'string' ? parseDataURI(config) : config;
4
+ const cfg = typeof config === 'string' ? parseResourceConfig(config) : config;
5
5
  if (!cfg)
6
6
  return null;
7
7
  return 'rsc-' + getSimpleHash(JSON.stringify(cfg));
@@ -1,5 +1,5 @@
1
1
  import { type ExportOptions } from '../exporter';
2
- import { InfographicOptions } from '../options';
2
+ import { InfographicOptions, ParsedInfographicOptions } from '../options';
3
3
  export declare class Infographic {
4
4
  rendered: boolean;
5
5
  private emitter;
@@ -7,17 +7,18 @@ export declare class Infographic {
7
7
  private editor?;
8
8
  private options;
9
9
  private parsedOptions;
10
- constructor(options: InfographicOptions);
11
- getOptions(): InfographicOptions;
10
+ constructor(options: string | Partial<InfographicOptions>);
11
+ getOptions(): Partial<InfographicOptions>;
12
+ private setOptions;
12
13
  /**
13
14
  * Render the infographic into the container
14
15
  */
15
- render(): void;
16
+ render(options?: string | Partial<InfographicOptions>): void;
16
17
  /**
17
18
  * Compose the SVG template
18
19
  */
19
- compose(): SVGSVGElement;
20
- getTypes(): string;
20
+ compose(parsedOptions: ParsedInfographicOptions): SVGSVGElement;
21
+ getTypes(): string | undefined;
21
22
  /**
22
23
  * Export the infographic to data URL
23
24
  * @param options Export option