@antv/infographic 0.2.16 → 0.2.17

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 (59) hide show
  1. package/dist/infographic.min.js +114 -113
  2. package/dist/infographic.min.js.map +1 -1
  3. package/esm/constants/service.d.ts +1 -1
  4. package/esm/constants/service.js +1 -1
  5. package/esm/designs/structures/chart-line.js +2 -1
  6. package/esm/exporter/svg.js +167 -1
  7. package/esm/options/parser.js +8 -6
  8. package/esm/options/types.d.ts +3 -3
  9. package/esm/renderer/renderer.js +1 -1
  10. package/esm/resource/loaders/search.js +2 -3
  11. package/esm/runtime/options.js +1 -1
  12. package/esm/syntax/index.js +56 -10
  13. package/esm/syntax/mapper.js +20 -6
  14. package/esm/syntax/parser.js +9 -0
  15. package/esm/syntax/types.d.ts +1 -1
  16. package/esm/templates/registry.d.ts +1 -0
  17. package/esm/templates/registry.js +6 -0
  18. package/esm/templates/utils.d.ts +1 -0
  19. package/esm/templates/utils.js +63 -0
  20. package/esm/themes/built-in.js +3 -0
  21. package/esm/version.d.ts +1 -1
  22. package/esm/version.js +1 -1
  23. package/lib/constants/service.d.ts +1 -1
  24. package/lib/constants/service.js +1 -1
  25. package/lib/designs/structures/chart-line.js +2 -1
  26. package/lib/exporter/svg.js +167 -1
  27. package/lib/options/parser.js +7 -5
  28. package/lib/options/types.d.ts +3 -3
  29. package/lib/renderer/renderer.js +1 -1
  30. package/lib/resource/loaders/search.js +2 -3
  31. package/lib/runtime/options.js +1 -1
  32. package/lib/syntax/index.js +56 -10
  33. package/lib/syntax/mapper.js +20 -6
  34. package/lib/syntax/parser.js +9 -0
  35. package/lib/syntax/types.d.ts +1 -1
  36. package/lib/templates/registry.d.ts +1 -0
  37. package/lib/templates/registry.js +7 -0
  38. package/lib/templates/utils.d.ts +1 -0
  39. package/lib/templates/utils.js +66 -0
  40. package/lib/themes/built-in.js +3 -0
  41. package/lib/version.d.ts +1 -1
  42. package/lib/version.js +1 -1
  43. package/package.json +1 -1
  44. package/src/constants/service.ts +1 -1
  45. package/src/designs/structures/chart-line.tsx +3 -1
  46. package/src/exporter/svg.ts +195 -1
  47. package/src/options/parser.ts +7 -6
  48. package/src/options/types.ts +3 -3
  49. package/src/renderer/renderer.ts +1 -1
  50. package/src/resource/loaders/search.ts +2 -2
  51. package/src/runtime/options.ts +1 -1
  52. package/src/syntax/index.ts +71 -10
  53. package/src/syntax/mapper.ts +20 -6
  54. package/src/syntax/parser.ts +10 -0
  55. package/src/syntax/types.ts +1 -0
  56. package/src/templates/registry.ts +6 -0
  57. package/src/templates/utils.ts +87 -0
  58. package/src/themes/built-in.ts +4 -0
  59. package/src/version.ts +1 -1
@@ -13,6 +13,7 @@ exports.exportToSVGString = exportToSVGString;
13
13
  exports.exportToSVG = exportToSVG;
14
14
  const utils_1 = require("../utils");
15
15
  const font_1 = require("./font");
16
+ const VIEWBOX_CHANGE_TOLERANCE = 0.5;
16
17
  function exportToSVGString(svg_1) {
17
18
  return __awaiter(this, arguments, void 0, function* (svg, options = {}) {
18
19
  const node = yield exportToSVG(svg, options);
@@ -20,11 +21,176 @@ function exportToSVGString(svg_1) {
20
21
  return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(str);
21
22
  });
22
23
  }
24
+ function getExportViewBox(svg) {
25
+ if (svg.hasAttribute('viewBox'))
26
+ return (0, utils_1.getViewBox)(svg);
27
+ const width = parseAbsoluteLength(svg.getAttribute('width'));
28
+ const height = parseAbsoluteLength(svg.getAttribute('height'));
29
+ if (width > 0 && height > 0) {
30
+ return { x: 0, y: 0, width, height };
31
+ }
32
+ const rect = svg.getBoundingClientRect();
33
+ if (rect.width > 0 && rect.height > 0) {
34
+ return { x: 0, y: 0, width: rect.width, height: rect.height };
35
+ }
36
+ return null;
37
+ }
38
+ function parseAbsoluteLength(value) {
39
+ if (!value)
40
+ return Number.NaN;
41
+ const trimmed = value.trim();
42
+ if (!trimmed)
43
+ return Number.NaN;
44
+ if (!/^[-+]?(?:\d+\.?\d*|\.\d+)(?:px)?$/.test(trimmed))
45
+ return Number.NaN;
46
+ return Number.parseFloat(trimmed);
47
+ }
48
+ function measureSpanContentHeight(span) {
49
+ const prevHeight = span.style.height;
50
+ const prevOverflow = span.style.overflow;
51
+ try {
52
+ span.style.height = 'max-content';
53
+ span.style.overflow = 'hidden';
54
+ void span.offsetHeight; // force reflow
55
+ return span.scrollHeight;
56
+ }
57
+ finally {
58
+ span.style.height = prevHeight;
59
+ span.style.overflow = prevOverflow;
60
+ }
61
+ }
62
+ function measureSpanContentWidth(span) {
63
+ const prevWidth = span.style.width;
64
+ const prevOverflow = span.style.overflow;
65
+ try {
66
+ span.style.width = 'max-content';
67
+ span.style.overflow = 'hidden';
68
+ void span.offsetWidth; // force reflow
69
+ return span.scrollWidth;
70
+ }
71
+ finally {
72
+ span.style.width = prevWidth;
73
+ span.style.overflow = prevOverflow;
74
+ }
75
+ }
76
+ // Returns [left, top, right, bottom] in SVG coordinates for a foreignObject,
77
+ // accounting for flex alignment: bottom/center-aligned content can overflow,
78
+ // and horizontally aligned content can overflow as well.
79
+ function getFOContentBoundsInSVG(fo, content, toSVGCoord) {
80
+ const foRect = fo.getBoundingClientRect();
81
+ const foTopLeft = toSVGCoord(foRect.left, foRect.top);
82
+ const foBottomRight = toSVGCoord(foRect.right, foRect.bottom);
83
+ const foLeftSVG = foTopLeft.x;
84
+ const foTopSVG = foTopLeft.y;
85
+ const foRightSVG = foBottomRight.x;
86
+ const foBottomSVG = foBottomRight.y;
87
+ const foWidthSVG = foRightSVG - foLeftSVG;
88
+ const foHeightSVG = foBottomSVG - foTopSVG;
89
+ const svgUnitsPerClientPxY = foRect.height > 0 ? foHeightSVG / foRect.height : 1;
90
+ const svgUnitsPerClientPxX = foRect.width > 0 ? foWidthSVG / foRect.width : 1;
91
+ // Measure actual content dimensions
92
+ const realScrollHeight = measureSpanContentHeight(content);
93
+ const contentHeightSVG = realScrollHeight > 0
94
+ ? realScrollHeight * svgUnitsPerClientPxY
95
+ : foHeightSVG;
96
+ const realScrollWidth = measureSpanContentWidth(content);
97
+ const contentWidthSVG = realScrollWidth > 0 ? realScrollWidth * svgUnitsPerClientPxX : foWidthSVG;
98
+ const computedStyle = window.getComputedStyle(content);
99
+ const alignItems = computedStyle.alignItems;
100
+ const justifyContent = computedStyle.justifyContent;
101
+ // Calculate vertical bounds
102
+ let top, bottom;
103
+ if (alignItems === 'flex-end' || alignItems === 'end') {
104
+ top = foBottomSVG - contentHeightSVG;
105
+ bottom = foBottomSVG;
106
+ }
107
+ else if (alignItems === 'center') {
108
+ const overflowY = contentHeightSVG - foHeightSVG;
109
+ top = foTopSVG - overflowY / 2;
110
+ bottom = foBottomSVG + overflowY / 2;
111
+ }
112
+ else {
113
+ top = foTopSVG;
114
+ bottom = foTopSVG + contentHeightSVG;
115
+ }
116
+ // Calculate horizontal bounds
117
+ let left, right;
118
+ if (justifyContent === 'flex-end' ||
119
+ justifyContent === 'end' ||
120
+ justifyContent === 'right') {
121
+ left = foRightSVG - contentWidthSVG;
122
+ right = foRightSVG;
123
+ }
124
+ else if (justifyContent === 'center') {
125
+ const overflowX = contentWidthSVG - foWidthSVG;
126
+ left = foLeftSVG - overflowX / 2;
127
+ right = foRightSVG + overflowX / 2;
128
+ }
129
+ else {
130
+ left = foLeftSVG;
131
+ right = foLeftSVG + contentWidthSVG;
132
+ }
133
+ return [left, top, right, bottom];
134
+ }
135
+ /**
136
+ * Computes a viewBox that fully covers all foreignObject text content,
137
+ * accounting for overflow caused by flex alignment (bottom/center align
138
+ * can push content outside the foreignObject bounds).
139
+ */
140
+ function computeFullViewBox(svg) {
141
+ const viewBox = getExportViewBox(svg);
142
+ if (!viewBox)
143
+ return null;
144
+ if (typeof svg.getScreenCTM !== 'function')
145
+ return null;
146
+ const screenCTM = svg.getScreenCTM();
147
+ if (!screenCTM)
148
+ return null;
149
+ const inverseCTM = screenCTM.inverse();
150
+ const toSVGCoord = (clientX, clientY) => {
151
+ const pt = svg.createSVGPoint();
152
+ pt.x = clientX;
153
+ pt.y = clientY;
154
+ return pt.matrixTransform(inverseCTM);
155
+ };
156
+ let minX = viewBox.x;
157
+ let minY = viewBox.y;
158
+ let maxX = viewBox.x + viewBox.width;
159
+ let maxY = viewBox.y + viewBox.height;
160
+ svg
161
+ .querySelectorAll('foreignObject')
162
+ .forEach((fo) => {
163
+ const content = fo.firstElementChild;
164
+ if (!content)
165
+ return;
166
+ const [left, top, right, bottom] = getFOContentBoundsInSVG(fo, content, toSVGCoord);
167
+ minX = Math.min(minX, left);
168
+ minY = Math.min(minY, top);
169
+ maxX = Math.max(maxX, right);
170
+ maxY = Math.max(maxY, bottom);
171
+ });
172
+ const newX = minX;
173
+ const newY = minY;
174
+ const newWidth = maxX - newX;
175
+ const newHeight = maxY - newY;
176
+ if (newWidth <= viewBox.width + VIEWBOX_CHANGE_TOLERANCE &&
177
+ newHeight <= viewBox.height + VIEWBOX_CHANGE_TOLERANCE &&
178
+ newX >= viewBox.x - VIEWBOX_CHANGE_TOLERANCE &&
179
+ newY >= viewBox.y - VIEWBOX_CHANGE_TOLERANCE)
180
+ return null;
181
+ return `${newX} ${newY} ${newWidth} ${newHeight}`;
182
+ }
23
183
  function exportToSVG(svg_1) {
24
184
  return __awaiter(this, arguments, void 0, function* (svg, options = {}) {
25
185
  const { removeBackground = false, embedResources = true, removeIds = false, } = options;
26
186
  const clonedSVG = svg.cloneNode(true);
27
- const { width, height } = (0, utils_1.getViewBox)(svg);
187
+ if (typeof document !== 'undefined') {
188
+ const fullViewBox = computeFullViewBox(svg);
189
+ if (fullViewBox) {
190
+ clonedSVG.setAttribute('viewBox', fullViewBox);
191
+ }
192
+ }
193
+ const { width, height } = (0, utils_1.getViewBox)(clonedSVG);
28
194
  (0, utils_1.setAttributes)(clonedSVG, { width, height });
29
195
  if (removeIds) {
30
196
  inlineUseElements(clonedSVG);
@@ -16,15 +16,17 @@ exports.parseData = parseData;
16
16
  const lodash_es_1 = require("lodash-es");
17
17
  const designs_1 = require("../designs");
18
18
  const renderer_1 = require("../renderer");
19
+ const registry_1 = require("../templates/registry");
19
20
  const themes_1 = require("../themes");
20
21
  const utils_1 = require("../utils");
21
22
  function parseOptions(options) {
22
23
  const { container = '#container', padding = 0, template, design, theme, themeConfig, data } = options, restOptions = __rest(options, ["container", "padding", "template", "design", "theme", "themeConfig", "data"]);
24
+ const resolvedTemplate = template ? (0, registry_1.resolveTemplateKey)(template) : undefined;
23
25
  const parsedContainer = typeof container === 'string'
24
26
  ? document.querySelector(container) || document.createElement('div')
25
27
  : container;
26
- const templateOptions = template
27
- ? (0, designs_1.getTemplate)(template)
28
+ const templateOptions = resolvedTemplate
29
+ ? (0, registry_1.getTemplate)(resolvedTemplate)
28
30
  : undefined;
29
31
  const mergedThemeConfig = (0, lodash_es_1.merge)({}, templateOptions === null || templateOptions === void 0 ? void 0 : templateOptions.themeConfig, themeConfig);
30
32
  const resolvedThemeConfig = theme || themeConfig || (templateOptions === null || templateOptions === void 0 ? void 0 : templateOptions.themeConfig)
@@ -39,11 +41,11 @@ function parseOptions(options) {
39
41
  Object.assign(parsed, restTemplateOptions);
40
42
  }
41
43
  Object.assign(parsed, restOptions);
42
- const parsedData = parseData(data, options.template);
44
+ const parsedData = parseData(data, resolvedTemplate);
43
45
  if (parsedData)
44
46
  parsed.data = parsedData;
45
- if (template)
46
- parsed.template = template;
47
+ if (resolvedTemplate)
48
+ parsed.template = resolvedTemplate;
47
49
  if ((templateOptions === null || templateOptions === void 0 ? void 0 : templateOptions.design) || design) {
48
50
  const designOptions = Object.assign(Object.assign({}, (resolvedThemeConfig
49
51
  ? Object.assign(Object.assign({}, options), { themeConfig: resolvedThemeConfig }) : options)), { data: parsedData || data });
@@ -4,8 +4,8 @@ import type { ThemeConfig } from '../themes';
4
4
  import type { Data, Padding, ParsedData } from '../types';
5
5
  import type { Path } from '../utils';
6
6
  export interface InfographicOptions {
7
- /** 容器,可以是选择器或者 HTMLElement */
8
- container?: string | HTMLElement;
7
+ /** 容器,可以是选择器、Element ShadowRoot */
8
+ container?: string | Element | ShadowRoot;
9
9
  /** 宽度 */
10
10
  width?: number | string;
11
11
  /** 高度 */
@@ -34,7 +34,7 @@ export interface InfographicOptions {
34
34
  elements?: ElementProps[];
35
35
  }
36
36
  export interface ParsedInfographicOptions {
37
- container: HTMLElement;
37
+ container: Element | ShadowRoot;
38
38
  width?: number | string;
39
39
  height?: number | string;
40
40
  padding?: Padding;
@@ -50,7 +50,7 @@ class Renderer {
50
50
  });
51
51
  });
52
52
  try {
53
- observer.observe(document, {
53
+ observer.observe(this.options.container, {
54
54
  childList: true,
55
55
  subtree: true,
56
56
  });
@@ -16,7 +16,6 @@ const image_1 = require("./image");
16
16
  const remote_1 = require("./remote");
17
17
  const svg_1 = require("./svg");
18
18
  const queryIcon = (query) => __awaiter(void 0, void 0, void 0, function* () {
19
- var _a;
20
19
  try {
21
20
  const params = new URLSearchParams({ text: query, topK: '1' });
22
21
  const url = `${constants_1.ICON_SERVICE_URL}?${params.toString()}`;
@@ -24,9 +23,9 @@ const queryIcon = (query) => __awaiter(void 0, void 0, void 0, function* () {
24
23
  if (!response.ok)
25
24
  return null;
26
25
  const result = yield response.json();
27
- if (!(result === null || result === void 0 ? void 0 : result.status) || !Array.isArray((_a = result.data) === null || _a === void 0 ? void 0 : _a.data))
26
+ if (!(result === null || result === void 0 ? void 0 : result.success) || !Array.isArray(result.data))
28
27
  return null;
29
- return result.data.data[0] || null;
28
+ return result.data[0] || null;
30
29
  }
31
30
  catch (error) {
32
31
  console.error(`Failed to query icon for "${query}":`, error);
@@ -19,7 +19,7 @@ const createDefaultInteractions = () => [
19
19
  ];
20
20
  exports.DEFAULT_OPTIONS = {
21
21
  padding: 20,
22
- theme: 'light',
22
+ theme: 'default',
23
23
  themeConfig: {
24
24
  palette: 'antv',
25
25
  },
@@ -16,6 +16,15 @@ const mapper_1 = require("./mapper");
16
16
  const parser_1 = require("./parser");
17
17
  const relations_1 = require("./relations");
18
18
  const schema_1 = require("./schema");
19
+ const ALLOWED_ROOT_KEYS = new Set([
20
+ 'infographic',
21
+ 'template',
22
+ 'design',
23
+ 'data',
24
+ 'theme',
25
+ 'width',
26
+ 'height',
27
+ ]);
19
28
  function normalizeItems(items) {
20
29
  var _a;
21
30
  const seen = new Set();
@@ -36,6 +45,13 @@ function normalizeItems(items) {
36
45
  }
37
46
  return normalized.reverse();
38
47
  }
48
+ function assignMissingNodeIds(items) {
49
+ items.forEach((item) => {
50
+ if (!item.id && item.label) {
51
+ item.id = item.label;
52
+ }
53
+ });
54
+ }
39
55
  function resolveTemplate(node, errors) {
40
56
  if (!node)
41
57
  return undefined;
@@ -49,12 +65,42 @@ function resolveTemplate(node, errors) {
49
65
  }
50
66
  return undefined;
51
67
  }
68
+ function inferTemplateFromBareFirstLine(entries, warnings) {
69
+ if ('infographic' in entries || 'template' in entries) {
70
+ return undefined;
71
+ }
72
+ const [firstEntry] = Object.entries(entries);
73
+ if (!firstEntry)
74
+ return undefined;
75
+ const [key, node] = firstEntry;
76
+ if (ALLOWED_ROOT_KEYS.has(key) || node.kind !== 'object') {
77
+ return undefined;
78
+ }
79
+ if (node.value !== undefined || Object.keys(node.entries).length > 0) {
80
+ return undefined;
81
+ }
82
+ warnings.push({
83
+ path: 'template',
84
+ line: node.line,
85
+ code: 'implicit_template',
86
+ message: 'Inferred template from a bare first line. Prefix it with "infographic" or "template" to make the syntax explicit.',
87
+ raw: key,
88
+ });
89
+ return {
90
+ template: key,
91
+ inferredKey: key,
92
+ };
93
+ }
52
94
  function parseSyntax(input) {
53
95
  var _a;
54
96
  const { ast, errors } = (0, parser_1.parseSyntaxToAst)(input);
55
97
  const warnings = [];
56
98
  const options = {};
57
99
  const mergedEntries = Object.assign({}, ast.entries);
100
+ const inferredTemplate = inferTemplateFromBareFirstLine(ast.entries, warnings);
101
+ if (inferredTemplate) {
102
+ delete mergedEntries[inferredTemplate.inferredKey];
103
+ }
58
104
  const infographicNode = ast.entries.infographic;
59
105
  let templateFromInfographic;
60
106
  if (infographicNode && infographicNode.kind === 'object') {
@@ -65,17 +111,8 @@ function parseSyntax(input) {
65
111
  mergedEntries[key] = value;
66
112
  });
67
113
  }
68
- const allowedRootKeys = new Set([
69
- 'infographic',
70
- 'template',
71
- 'design',
72
- 'data',
73
- 'theme',
74
- 'width',
75
- 'height',
76
- ]);
77
114
  Object.keys(mergedEntries).forEach((key) => {
78
- if (!allowedRootKeys.has(key)) {
115
+ if (!ALLOWED_ROOT_KEYS.has(key)) {
79
116
  errors.push({
80
117
  path: key,
81
118
  line: mergedEntries[key].line,
@@ -92,6 +129,9 @@ function parseSyntax(input) {
92
129
  if (!options.template && templateFromInfographic) {
93
130
  options.template = templateFromInfographic;
94
131
  }
132
+ if (!options.template && inferredTemplate) {
133
+ options.template = inferredTemplate.template;
134
+ }
95
135
  const designNode = mergedEntries.design;
96
136
  if (designNode) {
97
137
  const design = (0, mapper_1.mapWithSchema)(designNode, schema_1.DesignSchema, 'design', errors);
@@ -114,6 +154,12 @@ function parseSyntax(input) {
114
154
  const parsed = (0, relations_1.parseRelationsNode)(relationsNode, errors, 'data.relations');
115
155
  if (parsed.relations.length > 0 || parsed.items.length > 0) {
116
156
  const current = ((_a = options.data) !== null && _a !== void 0 ? _a : {});
157
+ if (Array.isArray(current.items)) {
158
+ assignMissingNodeIds(current.items);
159
+ }
160
+ if (Array.isArray(current.nodes)) {
161
+ assignMissingNodeIds(current.nodes);
162
+ }
117
163
  // 优先使用已存在的数据列表 (sequences, lists, etc.)
118
164
  const dataKeys = Object.keys(schema_1.DataSchema.fields).filter((key) => key !== 'items' && key !== 'relations');
119
165
  let hasStructuredData = false;
@@ -12,11 +12,20 @@ function createValueNode(value, line) {
12
12
  }
13
13
  const HEX_COLOR_PATTERN = /^(#[0-9a-f]{8}|#[0-9a-f]{6}|#[0-9a-f]{4}|#[0-9a-f]{3})/i;
14
14
  const FUNCTION_COLOR_PATTERN = /^((?:rgb|rgba|hsl|hsla)\([^)]*\))/i;
15
+ function normalizeBooleanLiteral(value) {
16
+ const trimmed = value.trim();
17
+ if (/^true$/i.test(trimmed))
18
+ return 'true';
19
+ if (/^false$/i.test(trimmed))
20
+ return 'false';
21
+ return undefined;
22
+ }
15
23
  function parseScalar(value) {
16
24
  const trimmed = value.trim();
17
- if (trimmed === 'true')
25
+ const normalizedBoolean = normalizeBooleanLiteral(trimmed);
26
+ if (normalizedBoolean === 'true')
18
27
  return true;
19
- if (trimmed === 'false')
28
+ if (normalizedBoolean === 'false')
20
29
  return false;
21
30
  if (/^-?\d+(\.\d+)?$/.test(trimmed))
22
31
  return parseFloat(trimmed);
@@ -182,11 +191,12 @@ function mapWithSchema(node, schema, path, errors) {
182
191
  addError(errors, node, path, 'schema_mismatch', 'Expected boolean value.');
183
192
  return undefined;
184
193
  }
185
- if (value !== 'true' && value !== 'false') {
194
+ const normalizedBoolean = normalizeBooleanLiteral(value);
195
+ if (!normalizedBoolean) {
186
196
  addError(errors, node, path, 'invalid_value', 'Invalid boolean value.', value);
187
197
  return undefined;
188
198
  }
189
- return value === 'true';
199
+ return normalizedBoolean === 'true';
190
200
  }
191
201
  case 'enum': {
192
202
  const value = readScalar(node);
@@ -194,11 +204,15 @@ function mapWithSchema(node, schema, path, errors) {
194
204
  addError(errors, node, path, 'schema_mismatch', 'Expected enum value.');
195
205
  return undefined;
196
206
  }
197
- if (!schema.values.includes(value)) {
207
+ const normalizedBoolean = normalizeBooleanLiteral(value);
208
+ const enumValue = normalizedBoolean && schema.values.includes(normalizedBoolean)
209
+ ? normalizedBoolean
210
+ : value;
211
+ if (!schema.values.includes(enumValue)) {
198
212
  addError(errors, node, path, 'invalid_value', 'Invalid enum value.', value);
199
213
  return undefined;
200
214
  }
201
- return value;
215
+ return enumValue;
202
216
  }
203
217
  case 'array': {
204
218
  if (node.kind === 'array') {
@@ -27,6 +27,13 @@ function getIndentInfo(line) {
27
27
  function stripComments(content) {
28
28
  return content.trimEnd();
29
29
  }
30
+ function isCommentLine(content) {
31
+ const trimmed = content.trimStart();
32
+ return trimmed.startsWith('#') || trimmed.startsWith('//');
33
+ }
34
+ function isCodeFenceLine(content) {
35
+ return /^```[\w-]*\s*$/.test(content.trim());
36
+ }
30
37
  function looksLikeRelationExpression(text) {
31
38
  return /[<>=o.x-]{2,}/.test(text);
32
39
  }
@@ -137,6 +144,8 @@ function parseSyntaxToAst(input) {
137
144
  if (!line.trim())
138
145
  return;
139
146
  const { indent, content } = getIndentInfo(line);
147
+ if (isCommentLine(content) || isCodeFenceLine(content))
148
+ return;
140
149
  const stripped = stripComments(content);
141
150
  if (!stripped.trim())
142
151
  return;
@@ -16,7 +16,7 @@ export interface ArrayNode {
16
16
  line: number;
17
17
  items: SyntaxNode[];
18
18
  }
19
- export type SyntaxErrorCode = 'unknown_key' | 'schema_mismatch' | 'invalid_value' | 'bad_indent' | 'bad_list' | 'bad_syntax';
19
+ export type SyntaxErrorCode = 'implicit_template' | 'unknown_key' | 'schema_mismatch' | 'invalid_value' | 'bad_indent' | 'bad_list' | 'bad_syntax';
20
20
  export interface SyntaxError {
21
21
  path: string;
22
22
  line: number;
@@ -1,4 +1,5 @@
1
1
  import type { TemplateOptions } from './types';
2
2
  export declare function registerTemplate(type: string, template: TemplateOptions): void;
3
+ export declare function resolveTemplateKey(type: string): string | undefined;
3
4
  export declare function getTemplate(type: string): TemplateOptions | undefined;
4
5
  export declare function getTemplates(): string[];
@@ -1,12 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerTemplate = registerTemplate;
4
+ exports.resolveTemplateKey = resolveTemplateKey;
4
5
  exports.getTemplate = getTemplate;
5
6
  exports.getTemplates = getTemplates;
7
+ const utils_1 = require("./utils");
6
8
  const TEMPLATE_REGISTRY = new Map();
7
9
  function registerTemplate(type, template) {
8
10
  TEMPLATE_REGISTRY.set(type, template);
9
11
  }
12
+ function resolveTemplateKey(type) {
13
+ if (TEMPLATE_REGISTRY.has(type))
14
+ return type;
15
+ return (0, utils_1.findClosestTemplateKey)(type, TEMPLATE_REGISTRY.keys());
16
+ }
10
17
  function getTemplate(type) {
11
18
  return TEMPLATE_REGISTRY.get(type);
12
19
  }
@@ -0,0 +1 @@
1
+ export declare function findClosestTemplateKey(type: string, keys: Iterable<string>): string | undefined;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findClosestTemplateKey = findClosestTemplateKey;
4
+ function normalizeTemplateKey(type) {
5
+ return type
6
+ .trim()
7
+ .toLowerCase()
8
+ .replace(/[_\s]+/g, '-')
9
+ .replace(/-+/g, '-');
10
+ }
11
+ function getLevenshteinDistance(source, target) {
12
+ if (source === target)
13
+ return 0;
14
+ if (!source.length)
15
+ return target.length;
16
+ if (!target.length)
17
+ return source.length;
18
+ const previous = Array.from({ length: target.length + 1 }, (_, index) => index);
19
+ const current = new Array(target.length + 1);
20
+ for (let sourceIndex = 1; sourceIndex <= source.length; sourceIndex += 1) {
21
+ current[0] = sourceIndex;
22
+ const sourceCode = source.charCodeAt(sourceIndex - 1);
23
+ for (let targetIndex = 1; targetIndex <= target.length; targetIndex += 1) {
24
+ const replaceCost = sourceCode === target.charCodeAt(targetIndex - 1) ? 0 : 1;
25
+ current[targetIndex] = Math.min(previous[targetIndex] + 1, current[targetIndex - 1] + 1, previous[targetIndex - 1] + replaceCost);
26
+ }
27
+ for (let index = 0; index < current.length; index += 1) {
28
+ previous[index] = current[index];
29
+ }
30
+ }
31
+ return previous[target.length];
32
+ }
33
+ function getCommonPrefixLength(source, target) {
34
+ const limit = Math.min(source.length, target.length);
35
+ let index = 0;
36
+ while (index < limit && source[index] === target[index]) {
37
+ index += 1;
38
+ }
39
+ return index;
40
+ }
41
+ function findClosestTemplateKey(type, keys) {
42
+ const normalizedType = normalizeTemplateKey(type);
43
+ if (!normalizedType)
44
+ return undefined;
45
+ let bestMatch;
46
+ let bestDistance = Number.POSITIVE_INFINITY;
47
+ let bestPrefixLength = -1;
48
+ for (const key of keys) {
49
+ const normalizedKey = normalizeTemplateKey(key);
50
+ if (normalizedKey === normalizedType) {
51
+ return key;
52
+ }
53
+ const distance = getLevenshteinDistance(normalizedType, normalizedKey);
54
+ const prefixLength = getCommonPrefixLength(normalizedType, normalizedKey);
55
+ if (distance < bestDistance ||
56
+ (distance === bestDistance && prefixLength > bestPrefixLength) ||
57
+ (distance === bestDistance &&
58
+ prefixLength === bestPrefixLength &&
59
+ (!bestMatch || key < bestMatch))) {
60
+ bestMatch = key;
61
+ bestDistance = distance;
62
+ bestPrefixLength = prefixLength;
63
+ }
64
+ }
65
+ return bestMatch;
66
+ }
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const registry_1 = require("./registry");
4
+ (0, registry_1.registerTheme)('light', {
5
+ colorBg: '#ffffff',
6
+ });
4
7
  (0, registry_1.registerTheme)('dark', {
5
8
  colorBg: '#1F1F1F',
6
9
  base: {
package/lib/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.2.16";
1
+ export declare const VERSION = "0.2.17";
package/lib/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = '0.2.16';
4
+ exports.VERSION = '0.2.17';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antv/infographic",
3
- "version": "0.2.16",
3
+ "version": "0.2.17",
4
4
  "description": "An Infographic Generation and Rendering Framework, bring words to life!",
5
5
  "keywords": [
6
6
  "antv",
@@ -1 +1 @@
1
- export const ICON_SERVICE_URL = 'https://www.weavefox.cn/api/open/v1/icon';
1
+ export const ICON_SERVICE_URL = 'https://lab.weavefox.cn/api/v1/infographic/icon';
@@ -121,6 +121,8 @@ export const ChartLine: ComponentType<ChartLineProps> = (props) => {
121
121
  const tickElements: JSXElement[] = [];
122
122
 
123
123
  const ticksY = scaleY.ticks(6);
124
+ const formatTickY = scaleY.tickFormat(6);
125
+
124
126
  ticksY.forEach((tick) => {
125
127
  const yPos = chartOriginY + scaleY(tick);
126
128
  gridElements.push(
@@ -145,7 +147,7 @@ export const ChartLine: ComponentType<ChartLineProps> = (props) => {
145
147
  fontSize={12}
146
148
  fill={axisColor}
147
149
  >
148
- {Number.isInteger(tick) ? tick.toString() : tick.toFixed(1)}
150
+ {formatTickY(tick)}
149
151
  </Text>,
150
152
  );
151
153
  });