@diagrammo/dgmo 0.1.7 → 0.2.0
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 +19 -19
- package/dist/cli.cjs +1498 -3
- package/dist/index.cjs +453 -475
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -16
- package/dist/index.d.ts +20 -16
- package/dist/index.js +450 -473
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
- package/src/chart.ts +231 -0
- package/src/cli.ts +19 -5
- package/src/dgmo-router.ts +12 -13
- package/src/echarts.ts +590 -0
- package/src/index.ts +6 -7
- package/src/chartjs.ts +0 -784
package/dist/cli.cjs
CHANGED
|
@@ -5066,6 +5066,1491 @@ async function renderD3ForExport(content, theme, palette) {
|
|
|
5066
5066
|
}
|
|
5067
5067
|
}
|
|
5068
5068
|
|
|
5069
|
+
// src/echarts.ts
|
|
5070
|
+
var echarts = __toESM(require("echarts"), 1);
|
|
5071
|
+
init_fonts();
|
|
5072
|
+
init_colors();
|
|
5073
|
+
init_palettes();
|
|
5074
|
+
|
|
5075
|
+
// src/chart.ts
|
|
5076
|
+
init_colors();
|
|
5077
|
+
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
5078
|
+
"bar",
|
|
5079
|
+
"line",
|
|
5080
|
+
"pie",
|
|
5081
|
+
"doughnut",
|
|
5082
|
+
"area",
|
|
5083
|
+
"polar-area",
|
|
5084
|
+
"radar",
|
|
5085
|
+
"bar-stacked"
|
|
5086
|
+
]);
|
|
5087
|
+
var TYPE_ALIASES = {
|
|
5088
|
+
"multi-line": "line"
|
|
5089
|
+
};
|
|
5090
|
+
function parseChart(content, palette) {
|
|
5091
|
+
const lines = content.split("\n");
|
|
5092
|
+
const result = {
|
|
5093
|
+
type: "bar",
|
|
5094
|
+
data: []
|
|
5095
|
+
};
|
|
5096
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5097
|
+
const trimmed = lines[i].trim();
|
|
5098
|
+
const lineNumber = i + 1;
|
|
5099
|
+
if (!trimmed) continue;
|
|
5100
|
+
if (/^#{2,}\s+/.test(trimmed)) continue;
|
|
5101
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
|
|
5102
|
+
const colonIndex = trimmed.indexOf(":");
|
|
5103
|
+
if (colonIndex === -1) continue;
|
|
5104
|
+
const key = trimmed.substring(0, colonIndex).trim().toLowerCase();
|
|
5105
|
+
const value = trimmed.substring(colonIndex + 1).trim();
|
|
5106
|
+
if (key === "chart") {
|
|
5107
|
+
const raw = value.toLowerCase();
|
|
5108
|
+
const chartType = TYPE_ALIASES[raw] ?? raw;
|
|
5109
|
+
if (VALID_TYPES.has(chartType)) {
|
|
5110
|
+
result.type = chartType;
|
|
5111
|
+
} else {
|
|
5112
|
+
result.error = `Unsupported chart type: ${value}. Supported types: ${[...VALID_TYPES].join(", ")}.`;
|
|
5113
|
+
return result;
|
|
5114
|
+
}
|
|
5115
|
+
continue;
|
|
5116
|
+
}
|
|
5117
|
+
if (key === "title") {
|
|
5118
|
+
result.title = value;
|
|
5119
|
+
continue;
|
|
5120
|
+
}
|
|
5121
|
+
if (key === "xlabel") {
|
|
5122
|
+
result.xlabel = value;
|
|
5123
|
+
continue;
|
|
5124
|
+
}
|
|
5125
|
+
if (key === "ylabel") {
|
|
5126
|
+
result.ylabel = value;
|
|
5127
|
+
continue;
|
|
5128
|
+
}
|
|
5129
|
+
if (key === "label") {
|
|
5130
|
+
result.label = value;
|
|
5131
|
+
continue;
|
|
5132
|
+
}
|
|
5133
|
+
if (key === "orientation") {
|
|
5134
|
+
const v = value.toLowerCase();
|
|
5135
|
+
if (v === "horizontal" || v === "vertical") {
|
|
5136
|
+
result.orientation = v;
|
|
5137
|
+
}
|
|
5138
|
+
continue;
|
|
5139
|
+
}
|
|
5140
|
+
if (key === "color") {
|
|
5141
|
+
result.color = resolveColor(value.trim(), palette);
|
|
5142
|
+
continue;
|
|
5143
|
+
}
|
|
5144
|
+
if (key === "series") {
|
|
5145
|
+
result.series = value;
|
|
5146
|
+
const rawNames = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
5147
|
+
const names = [];
|
|
5148
|
+
const nameColors = [];
|
|
5149
|
+
for (const raw of rawNames) {
|
|
5150
|
+
const colorMatch = raw.match(/\(([^)]+)\)\s*$/);
|
|
5151
|
+
if (colorMatch) {
|
|
5152
|
+
const resolved = resolveColor(colorMatch[1].trim(), palette);
|
|
5153
|
+
nameColors.push(resolved);
|
|
5154
|
+
names.push(raw.substring(0, colorMatch.index).trim());
|
|
5155
|
+
} else {
|
|
5156
|
+
nameColors.push(void 0);
|
|
5157
|
+
names.push(raw);
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
if (names.length === 1) {
|
|
5161
|
+
result.series = names[0];
|
|
5162
|
+
}
|
|
5163
|
+
if (names.length > 1) {
|
|
5164
|
+
result.seriesNames = names;
|
|
5165
|
+
}
|
|
5166
|
+
if (nameColors.some(Boolean)) result.seriesNameColors = nameColors;
|
|
5167
|
+
continue;
|
|
5168
|
+
}
|
|
5169
|
+
const parts = value.split(",").map((s) => s.trim());
|
|
5170
|
+
const numValue = parseFloat(parts[0]);
|
|
5171
|
+
if (!isNaN(numValue)) {
|
|
5172
|
+
let rawLabel = trimmed.substring(0, colonIndex).trim();
|
|
5173
|
+
let pointColor;
|
|
5174
|
+
const colorMatch = rawLabel.match(/\(([^)]+)\)\s*$/);
|
|
5175
|
+
if (colorMatch) {
|
|
5176
|
+
const resolved = resolveColor(colorMatch[1].trim(), palette);
|
|
5177
|
+
pointColor = resolved;
|
|
5178
|
+
rawLabel = rawLabel.substring(0, colorMatch.index).trim();
|
|
5179
|
+
}
|
|
5180
|
+
const extra = parts.slice(1).map((s) => parseFloat(s)).filter((n) => !isNaN(n));
|
|
5181
|
+
result.data.push({
|
|
5182
|
+
label: rawLabel,
|
|
5183
|
+
value: numValue,
|
|
5184
|
+
...extra.length > 0 && { extraValues: extra },
|
|
5185
|
+
...pointColor && { color: pointColor },
|
|
5186
|
+
lineNumber
|
|
5187
|
+
});
|
|
5188
|
+
}
|
|
5189
|
+
}
|
|
5190
|
+
if (!result.error && result.data.length === 0) {
|
|
5191
|
+
result.error = "No data points found. Add data in format: Label: 123";
|
|
5192
|
+
}
|
|
5193
|
+
if (!result.error && result.type === "bar-stacked" && !result.seriesNames) {
|
|
5194
|
+
result.error = `Chart type "bar-stacked" requires multiple series names. Use: series: Name1, Name2, Name3`;
|
|
5195
|
+
}
|
|
5196
|
+
if (!result.error && result.seriesNames) {
|
|
5197
|
+
const expectedCount = result.seriesNames.length;
|
|
5198
|
+
for (const dp of result.data) {
|
|
5199
|
+
const actualCount = 1 + (dp.extraValues?.length ?? 0);
|
|
5200
|
+
if (actualCount !== expectedCount) {
|
|
5201
|
+
result.error = `Data point "${dp.label}" has ${actualCount} value(s), but ${expectedCount} series defined. Each row must have ${expectedCount} comma-separated values.`;
|
|
5202
|
+
break;
|
|
5203
|
+
}
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
return result;
|
|
5207
|
+
}
|
|
5208
|
+
|
|
5209
|
+
// src/echarts.ts
|
|
5210
|
+
function parseEChart(content, palette) {
|
|
5211
|
+
const lines = content.split("\n");
|
|
5212
|
+
const result = {
|
|
5213
|
+
type: "scatter",
|
|
5214
|
+
data: []
|
|
5215
|
+
};
|
|
5216
|
+
let currentCategory = "Default";
|
|
5217
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5218
|
+
const trimmed = lines[i].trim();
|
|
5219
|
+
const lineNumber = i + 1;
|
|
5220
|
+
if (!trimmed) continue;
|
|
5221
|
+
const mdCategoryMatch = trimmed.match(/^#{2,}\s+(.+)$/);
|
|
5222
|
+
if (mdCategoryMatch) {
|
|
5223
|
+
let catName = mdCategoryMatch[1].trim();
|
|
5224
|
+
const catColorMatch = catName.match(/\(([^)]+)\)\s*$/);
|
|
5225
|
+
if (catColorMatch) {
|
|
5226
|
+
const resolved = resolveColor(catColorMatch[1].trim(), palette);
|
|
5227
|
+
if (!result.categoryColors) result.categoryColors = {};
|
|
5228
|
+
catName = catName.substring(0, catColorMatch.index).trim();
|
|
5229
|
+
result.categoryColors[catName] = resolved;
|
|
5230
|
+
}
|
|
5231
|
+
currentCategory = catName;
|
|
5232
|
+
continue;
|
|
5233
|
+
}
|
|
5234
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
|
|
5235
|
+
const categoryMatch = trimmed.match(/^\[(.+)\]$/);
|
|
5236
|
+
if (categoryMatch) {
|
|
5237
|
+
currentCategory = categoryMatch[1].trim();
|
|
5238
|
+
continue;
|
|
5239
|
+
}
|
|
5240
|
+
const colonIndex = trimmed.indexOf(":");
|
|
5241
|
+
if (colonIndex === -1) continue;
|
|
5242
|
+
const key = trimmed.substring(0, colonIndex).trim().toLowerCase();
|
|
5243
|
+
const value = trimmed.substring(colonIndex + 1).trim();
|
|
5244
|
+
if (key === "chart") {
|
|
5245
|
+
const chartType = value.toLowerCase();
|
|
5246
|
+
if (chartType === "sankey" || chartType === "chord" || chartType === "function" || chartType === "scatter" || chartType === "heatmap" || chartType === "funnel") {
|
|
5247
|
+
result.type = chartType;
|
|
5248
|
+
} else {
|
|
5249
|
+
result.error = `Unsupported chart type: ${value}. Supported types: scatter, sankey, chord, function, heatmap, funnel.`;
|
|
5250
|
+
return result;
|
|
5251
|
+
}
|
|
5252
|
+
continue;
|
|
5253
|
+
}
|
|
5254
|
+
if (key === "title") {
|
|
5255
|
+
result.title = value;
|
|
5256
|
+
continue;
|
|
5257
|
+
}
|
|
5258
|
+
if (key === "series") {
|
|
5259
|
+
result.series = value;
|
|
5260
|
+
const rawNames = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
5261
|
+
const names = [];
|
|
5262
|
+
const nameColors = [];
|
|
5263
|
+
for (const raw of rawNames) {
|
|
5264
|
+
const colorMatch = raw.match(/\(([^)]+)\)\s*$/);
|
|
5265
|
+
if (colorMatch) {
|
|
5266
|
+
nameColors.push(resolveColor(colorMatch[1].trim(), palette));
|
|
5267
|
+
names.push(raw.substring(0, colorMatch.index).trim());
|
|
5268
|
+
} else {
|
|
5269
|
+
nameColors.push(void 0);
|
|
5270
|
+
names.push(raw);
|
|
5271
|
+
}
|
|
5272
|
+
}
|
|
5273
|
+
if (names.length === 1) {
|
|
5274
|
+
result.series = names[0];
|
|
5275
|
+
}
|
|
5276
|
+
if (nameColors.some(Boolean)) result.seriesNameColors = nameColors;
|
|
5277
|
+
continue;
|
|
5278
|
+
}
|
|
5279
|
+
if (key === "xlabel") {
|
|
5280
|
+
result.xlabel = value;
|
|
5281
|
+
continue;
|
|
5282
|
+
}
|
|
5283
|
+
if (key === "ylabel") {
|
|
5284
|
+
result.ylabel = value;
|
|
5285
|
+
continue;
|
|
5286
|
+
}
|
|
5287
|
+
if (key === "sizelabel") {
|
|
5288
|
+
result.sizelabel = value;
|
|
5289
|
+
continue;
|
|
5290
|
+
}
|
|
5291
|
+
if (key === "labels") {
|
|
5292
|
+
result.showLabels = value.toLowerCase() === "on" || value.toLowerCase() === "true";
|
|
5293
|
+
continue;
|
|
5294
|
+
}
|
|
5295
|
+
if (key === "columns") {
|
|
5296
|
+
result.columns = value.split(",").map((s) => s.trim());
|
|
5297
|
+
continue;
|
|
5298
|
+
}
|
|
5299
|
+
if (key === "rows") {
|
|
5300
|
+
result.rows = value.split(",").map((s) => s.trim());
|
|
5301
|
+
continue;
|
|
5302
|
+
}
|
|
5303
|
+
if (key === "x") {
|
|
5304
|
+
const rangeMatch = value.match(/^(-?[\d.]+)\s+to\s+(-?[\d.]+)$/);
|
|
5305
|
+
if (rangeMatch) {
|
|
5306
|
+
result.xRange = {
|
|
5307
|
+
min: parseFloat(rangeMatch[1]),
|
|
5308
|
+
max: parseFloat(rangeMatch[2])
|
|
5309
|
+
};
|
|
5310
|
+
}
|
|
5311
|
+
continue;
|
|
5312
|
+
}
|
|
5313
|
+
const arrowMatch = trimmed.match(/^(.+?)\s*->\s*(.+?):\s*(\d+(?:\.\d+)?)$/);
|
|
5314
|
+
if (arrowMatch) {
|
|
5315
|
+
const [, source, target, val] = arrowMatch;
|
|
5316
|
+
if (!result.links) result.links = [];
|
|
5317
|
+
result.links.push({
|
|
5318
|
+
source: source.trim(),
|
|
5319
|
+
target: target.trim(),
|
|
5320
|
+
value: parseFloat(val),
|
|
5321
|
+
lineNumber
|
|
5322
|
+
});
|
|
5323
|
+
continue;
|
|
5324
|
+
}
|
|
5325
|
+
if (result.type === "function") {
|
|
5326
|
+
let fnName = trimmed.substring(0, colonIndex).trim();
|
|
5327
|
+
let fnColor;
|
|
5328
|
+
const colorMatch = fnName.match(/\(([^)]+)\)\s*$/);
|
|
5329
|
+
if (colorMatch) {
|
|
5330
|
+
fnColor = resolveColor(colorMatch[1].trim(), palette);
|
|
5331
|
+
fnName = fnName.substring(0, colorMatch.index).trim();
|
|
5332
|
+
}
|
|
5333
|
+
if (!result.functions) result.functions = [];
|
|
5334
|
+
result.functions.push({
|
|
5335
|
+
name: fnName,
|
|
5336
|
+
expression: value,
|
|
5337
|
+
...fnColor && { color: fnColor },
|
|
5338
|
+
lineNumber
|
|
5339
|
+
});
|
|
5340
|
+
continue;
|
|
5341
|
+
}
|
|
5342
|
+
if (result.type === "scatter") {
|
|
5343
|
+
const scatterMatch = value.match(
|
|
5344
|
+
/^(-?[\d.]+)\s*,\s*(-?[\d.]+)(?:\s*,\s*(-?[\d.]+))?$/
|
|
5345
|
+
);
|
|
5346
|
+
if (scatterMatch) {
|
|
5347
|
+
let scatterName = trimmed.substring(0, colonIndex).trim();
|
|
5348
|
+
let scatterColor;
|
|
5349
|
+
const colorMatch = scatterName.match(/\(([^)]+)\)\s*$/);
|
|
5350
|
+
if (colorMatch) {
|
|
5351
|
+
scatterColor = resolveColor(colorMatch[1].trim(), palette);
|
|
5352
|
+
scatterName = scatterName.substring(0, colorMatch.index).trim();
|
|
5353
|
+
}
|
|
5354
|
+
if (!result.scatterPoints) result.scatterPoints = [];
|
|
5355
|
+
result.scatterPoints.push({
|
|
5356
|
+
name: scatterName,
|
|
5357
|
+
x: parseFloat(scatterMatch[1]),
|
|
5358
|
+
y: parseFloat(scatterMatch[2]),
|
|
5359
|
+
size: scatterMatch[3] ? parseFloat(scatterMatch[3]) : void 0,
|
|
5360
|
+
...scatterColor && { color: scatterColor },
|
|
5361
|
+
...currentCategory !== "Default" && { category: currentCategory },
|
|
5362
|
+
lineNumber
|
|
5363
|
+
});
|
|
5364
|
+
}
|
|
5365
|
+
continue;
|
|
5366
|
+
}
|
|
5367
|
+
if (result.type === "heatmap") {
|
|
5368
|
+
const values = value.split(",").map((v) => parseFloat(v.trim()));
|
|
5369
|
+
if (values.length > 0 && values.every((v) => !isNaN(v))) {
|
|
5370
|
+
const originalKey = trimmed.substring(0, colonIndex).trim();
|
|
5371
|
+
if (!result.heatmapRows) result.heatmapRows = [];
|
|
5372
|
+
result.heatmapRows.push({ label: originalKey, values, lineNumber });
|
|
5373
|
+
}
|
|
5374
|
+
continue;
|
|
5375
|
+
}
|
|
5376
|
+
const numValue = parseFloat(value);
|
|
5377
|
+
if (!isNaN(numValue)) {
|
|
5378
|
+
let rawLabel = trimmed.substring(0, colonIndex).trim();
|
|
5379
|
+
let pointColor;
|
|
5380
|
+
const colorMatch = rawLabel.match(/\(([^)]+)\)\s*$/);
|
|
5381
|
+
if (colorMatch) {
|
|
5382
|
+
pointColor = resolveColor(colorMatch[1].trim(), palette);
|
|
5383
|
+
rawLabel = rawLabel.substring(0, colorMatch.index).trim();
|
|
5384
|
+
}
|
|
5385
|
+
result.data.push({
|
|
5386
|
+
label: rawLabel,
|
|
5387
|
+
value: numValue,
|
|
5388
|
+
...pointColor && { color: pointColor },
|
|
5389
|
+
lineNumber
|
|
5390
|
+
});
|
|
5391
|
+
}
|
|
5392
|
+
}
|
|
5393
|
+
if (!result.error) {
|
|
5394
|
+
if (result.type === "sankey") {
|
|
5395
|
+
if (!result.links || result.links.length === 0) {
|
|
5396
|
+
result.error = "No links found. Add links in format: Source -> Target: 123";
|
|
5397
|
+
}
|
|
5398
|
+
} else if (result.type === "chord") {
|
|
5399
|
+
if (!result.links || result.links.length === 0) {
|
|
5400
|
+
result.error = "No links found. Add links in format: Source -> Target: 123";
|
|
5401
|
+
}
|
|
5402
|
+
} else if (result.type === "function") {
|
|
5403
|
+
if (!result.functions || result.functions.length === 0) {
|
|
5404
|
+
result.error = "No functions found. Add functions in format: Name: expression";
|
|
5405
|
+
}
|
|
5406
|
+
if (!result.xRange) {
|
|
5407
|
+
result.xRange = { min: -10, max: 10 };
|
|
5408
|
+
}
|
|
5409
|
+
} else if (result.type === "scatter") {
|
|
5410
|
+
if (!result.scatterPoints || result.scatterPoints.length === 0) {
|
|
5411
|
+
result.error = "No scatter points found. Add points in format: Name: x, y or Name: x, y, size";
|
|
5412
|
+
}
|
|
5413
|
+
} else if (result.type === "heatmap") {
|
|
5414
|
+
if (!result.heatmapRows || result.heatmapRows.length === 0) {
|
|
5415
|
+
result.error = "No heatmap data found. Add data in format: RowLabel: val1, val2, val3";
|
|
5416
|
+
}
|
|
5417
|
+
if (!result.columns || result.columns.length === 0) {
|
|
5418
|
+
result.error = "No columns defined. Add columns in format: columns: Col1, Col2, Col3";
|
|
5419
|
+
}
|
|
5420
|
+
} else if (result.type === "funnel") {
|
|
5421
|
+
if (result.data.length === 0) {
|
|
5422
|
+
result.error = "No data found. Add data in format: Label: value";
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5426
|
+
return result;
|
|
5427
|
+
}
|
|
5428
|
+
function buildEChartsOption(parsed, palette, _isDark) {
|
|
5429
|
+
const textColor = palette.text;
|
|
5430
|
+
const axisLineColor = palette.border;
|
|
5431
|
+
const colors = getSeriesColors(palette);
|
|
5432
|
+
if (parsed.error) {
|
|
5433
|
+
return {};
|
|
5434
|
+
}
|
|
5435
|
+
const titleConfig = parsed.title ? {
|
|
5436
|
+
text: parsed.title,
|
|
5437
|
+
left: "center",
|
|
5438
|
+
textStyle: {
|
|
5439
|
+
color: textColor,
|
|
5440
|
+
fontSize: 18,
|
|
5441
|
+
fontWeight: "bold",
|
|
5442
|
+
fontFamily: FONT_FAMILY
|
|
5443
|
+
}
|
|
5444
|
+
} : void 0;
|
|
5445
|
+
const tooltipTheme = {
|
|
5446
|
+
backgroundColor: palette.surface,
|
|
5447
|
+
borderColor: palette.border,
|
|
5448
|
+
textStyle: { color: palette.text }
|
|
5449
|
+
};
|
|
5450
|
+
if (parsed.type === "sankey") {
|
|
5451
|
+
return buildSankeyOption(
|
|
5452
|
+
parsed,
|
|
5453
|
+
textColor,
|
|
5454
|
+
colors,
|
|
5455
|
+
titleConfig,
|
|
5456
|
+
tooltipTheme
|
|
5457
|
+
);
|
|
5458
|
+
}
|
|
5459
|
+
if (parsed.type === "chord") {
|
|
5460
|
+
return buildChordOption(
|
|
5461
|
+
parsed,
|
|
5462
|
+
textColor,
|
|
5463
|
+
colors,
|
|
5464
|
+
titleConfig,
|
|
5465
|
+
tooltipTheme
|
|
5466
|
+
);
|
|
5467
|
+
}
|
|
5468
|
+
if (parsed.type === "function") {
|
|
5469
|
+
return buildFunctionOption(
|
|
5470
|
+
parsed,
|
|
5471
|
+
palette,
|
|
5472
|
+
textColor,
|
|
5473
|
+
axisLineColor,
|
|
5474
|
+
colors,
|
|
5475
|
+
titleConfig,
|
|
5476
|
+
tooltipTheme
|
|
5477
|
+
);
|
|
5478
|
+
}
|
|
5479
|
+
if (parsed.type === "scatter") {
|
|
5480
|
+
return buildScatterOption(
|
|
5481
|
+
parsed,
|
|
5482
|
+
palette,
|
|
5483
|
+
textColor,
|
|
5484
|
+
axisLineColor,
|
|
5485
|
+
colors,
|
|
5486
|
+
titleConfig,
|
|
5487
|
+
tooltipTheme
|
|
5488
|
+
);
|
|
5489
|
+
}
|
|
5490
|
+
if (parsed.type === "funnel") {
|
|
5491
|
+
return buildFunnelOption(
|
|
5492
|
+
parsed,
|
|
5493
|
+
textColor,
|
|
5494
|
+
colors,
|
|
5495
|
+
titleConfig,
|
|
5496
|
+
tooltipTheme
|
|
5497
|
+
);
|
|
5498
|
+
}
|
|
5499
|
+
return buildHeatmapOption(
|
|
5500
|
+
parsed,
|
|
5501
|
+
palette,
|
|
5502
|
+
textColor,
|
|
5503
|
+
axisLineColor,
|
|
5504
|
+
titleConfig,
|
|
5505
|
+
tooltipTheme
|
|
5506
|
+
);
|
|
5507
|
+
}
|
|
5508
|
+
function buildSankeyOption(parsed, textColor, colors, titleConfig, tooltipTheme) {
|
|
5509
|
+
const nodeSet = /* @__PURE__ */ new Set();
|
|
5510
|
+
if (parsed.links) {
|
|
5511
|
+
for (const link of parsed.links) {
|
|
5512
|
+
nodeSet.add(link.source);
|
|
5513
|
+
nodeSet.add(link.target);
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
const nodes = Array.from(nodeSet).map((name, index) => ({
|
|
5517
|
+
name,
|
|
5518
|
+
itemStyle: {
|
|
5519
|
+
color: colors[index % colors.length]
|
|
5520
|
+
}
|
|
5521
|
+
}));
|
|
5522
|
+
return {
|
|
5523
|
+
backgroundColor: "transparent",
|
|
5524
|
+
animation: false,
|
|
5525
|
+
title: titleConfig,
|
|
5526
|
+
tooltip: {
|
|
5527
|
+
show: false,
|
|
5528
|
+
...tooltipTheme
|
|
5529
|
+
},
|
|
5530
|
+
series: [
|
|
5531
|
+
{
|
|
5532
|
+
type: "sankey",
|
|
5533
|
+
emphasis: {
|
|
5534
|
+
focus: "adjacency"
|
|
5535
|
+
},
|
|
5536
|
+
nodeAlign: "left",
|
|
5537
|
+
nodeGap: 12,
|
|
5538
|
+
nodeWidth: 20,
|
|
5539
|
+
data: nodes,
|
|
5540
|
+
links: parsed.links ?? [],
|
|
5541
|
+
lineStyle: {
|
|
5542
|
+
color: "gradient",
|
|
5543
|
+
curveness: 0.5
|
|
5544
|
+
},
|
|
5545
|
+
label: {
|
|
5546
|
+
color: textColor,
|
|
5547
|
+
fontSize: 12
|
|
5548
|
+
}
|
|
5549
|
+
}
|
|
5550
|
+
]
|
|
5551
|
+
};
|
|
5552
|
+
}
|
|
5553
|
+
function buildChordOption(parsed, textColor, colors, titleConfig, tooltipTheme) {
|
|
5554
|
+
const nodeSet = /* @__PURE__ */ new Set();
|
|
5555
|
+
if (parsed.links) {
|
|
5556
|
+
for (const link of parsed.links) {
|
|
5557
|
+
nodeSet.add(link.source);
|
|
5558
|
+
nodeSet.add(link.target);
|
|
5559
|
+
}
|
|
5560
|
+
}
|
|
5561
|
+
const nodeNames = Array.from(nodeSet);
|
|
5562
|
+
const nodeCount = nodeNames.length;
|
|
5563
|
+
const matrix = Array(nodeCount).fill(null).map(() => Array(nodeCount).fill(0));
|
|
5564
|
+
if (parsed.links) {
|
|
5565
|
+
for (const link of parsed.links) {
|
|
5566
|
+
const sourceIndex = nodeNames.indexOf(link.source);
|
|
5567
|
+
const targetIndex = nodeNames.indexOf(link.target);
|
|
5568
|
+
if (sourceIndex !== -1 && targetIndex !== -1) {
|
|
5569
|
+
matrix[sourceIndex][targetIndex] = link.value;
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
const categories = nodeNames.map((name, index) => ({
|
|
5574
|
+
name,
|
|
5575
|
+
itemStyle: {
|
|
5576
|
+
color: colors[index % colors.length]
|
|
5577
|
+
}
|
|
5578
|
+
}));
|
|
5579
|
+
return {
|
|
5580
|
+
backgroundColor: "transparent",
|
|
5581
|
+
animation: false,
|
|
5582
|
+
title: titleConfig,
|
|
5583
|
+
tooltip: {
|
|
5584
|
+
trigger: "item",
|
|
5585
|
+
...tooltipTheme,
|
|
5586
|
+
formatter: (params) => {
|
|
5587
|
+
const p = params;
|
|
5588
|
+
if (p.data && p.data.source && p.data.target) {
|
|
5589
|
+
return `${p.data.source} \u2192 ${p.data.target}: ${p.data.value}`;
|
|
5590
|
+
}
|
|
5591
|
+
return "";
|
|
5592
|
+
}
|
|
5593
|
+
},
|
|
5594
|
+
legend: {
|
|
5595
|
+
data: nodeNames,
|
|
5596
|
+
bottom: 10,
|
|
5597
|
+
textStyle: {
|
|
5598
|
+
color: textColor
|
|
5599
|
+
}
|
|
5600
|
+
},
|
|
5601
|
+
series: [
|
|
5602
|
+
{
|
|
5603
|
+
type: "graph",
|
|
5604
|
+
layout: "circular",
|
|
5605
|
+
circular: {
|
|
5606
|
+
rotateLabel: true
|
|
5607
|
+
},
|
|
5608
|
+
center: ["50%", "55%"],
|
|
5609
|
+
width: "60%",
|
|
5610
|
+
height: "60%",
|
|
5611
|
+
data: categories.map((cat) => ({
|
|
5612
|
+
name: cat.name,
|
|
5613
|
+
symbolSize: 20,
|
|
5614
|
+
itemStyle: cat.itemStyle,
|
|
5615
|
+
label: {
|
|
5616
|
+
show: true,
|
|
5617
|
+
color: textColor
|
|
5618
|
+
}
|
|
5619
|
+
})),
|
|
5620
|
+
links: (parsed.links ?? []).map((link) => ({
|
|
5621
|
+
source: link.source,
|
|
5622
|
+
target: link.target,
|
|
5623
|
+
value: link.value,
|
|
5624
|
+
lineStyle: {
|
|
5625
|
+
width: Math.max(1, Math.min(link.value / 20, 10)),
|
|
5626
|
+
color: colors[nodeNames.indexOf(link.source) % colors.length],
|
|
5627
|
+
curveness: 0.3,
|
|
5628
|
+
opacity: 0.6
|
|
5629
|
+
}
|
|
5630
|
+
})),
|
|
5631
|
+
roam: true,
|
|
5632
|
+
label: {
|
|
5633
|
+
position: "right",
|
|
5634
|
+
formatter: "{b}"
|
|
5635
|
+
},
|
|
5636
|
+
emphasis: {
|
|
5637
|
+
focus: "adjacency",
|
|
5638
|
+
lineStyle: {
|
|
5639
|
+
width: 5,
|
|
5640
|
+
opacity: 1
|
|
5641
|
+
}
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
]
|
|
5645
|
+
};
|
|
5646
|
+
}
|
|
5647
|
+
function evaluateExpression(expr, x) {
|
|
5648
|
+
try {
|
|
5649
|
+
const processed = expr.replace(/\bpi\b/gi, String(Math.PI)).replace(/\be\b/g, String(Math.E)).replace(/\bsin\s*\(/gi, "Math.sin(").replace(/\bcos\s*\(/gi, "Math.cos(").replace(/\btan\s*\(/gi, "Math.tan(").replace(/\bln\s*\(/gi, "Math.log(").replace(/\blog\s*\(/gi, "Math.log10(").replace(/\bexp\s*\(/gi, "Math.exp(").replace(/\bsqrt\s*\(/gi, "Math.sqrt(").replace(/\babs\s*\(/gi, "Math.abs(").replace(/\bx\b/gi, `(${x})`).replace(/\^/g, "**");
|
|
5650
|
+
const result = new Function(`return ${processed}`)();
|
|
5651
|
+
return typeof result === "number" && isFinite(result) ? result : NaN;
|
|
5652
|
+
} catch {
|
|
5653
|
+
return NaN;
|
|
5654
|
+
}
|
|
5655
|
+
}
|
|
5656
|
+
function buildFunctionOption(parsed, palette, textColor, axisLineColor, colors, titleConfig, tooltipTheme) {
|
|
5657
|
+
const xRange = parsed.xRange ?? { min: -10, max: 10 };
|
|
5658
|
+
const samples = 200;
|
|
5659
|
+
const step = (xRange.max - xRange.min) / samples;
|
|
5660
|
+
const xValues = [];
|
|
5661
|
+
for (let i = 0; i <= samples; i++) {
|
|
5662
|
+
xValues.push(xRange.min + i * step);
|
|
5663
|
+
}
|
|
5664
|
+
const series = (parsed.functions ?? []).map((fn, index) => {
|
|
5665
|
+
const data = xValues.map((x) => {
|
|
5666
|
+
const y = evaluateExpression(fn.expression, x);
|
|
5667
|
+
return [x, y];
|
|
5668
|
+
});
|
|
5669
|
+
const fnColor = fn.color ?? colors[index % colors.length];
|
|
5670
|
+
return {
|
|
5671
|
+
name: fn.name,
|
|
5672
|
+
type: "line",
|
|
5673
|
+
showSymbol: false,
|
|
5674
|
+
smooth: true,
|
|
5675
|
+
data,
|
|
5676
|
+
lineStyle: {
|
|
5677
|
+
width: 2,
|
|
5678
|
+
color: fnColor
|
|
5679
|
+
},
|
|
5680
|
+
itemStyle: {
|
|
5681
|
+
color: fnColor
|
|
5682
|
+
}
|
|
5683
|
+
};
|
|
5684
|
+
});
|
|
5685
|
+
return {
|
|
5686
|
+
backgroundColor: "transparent",
|
|
5687
|
+
animation: false,
|
|
5688
|
+
title: titleConfig,
|
|
5689
|
+
tooltip: {
|
|
5690
|
+
trigger: "axis",
|
|
5691
|
+
...tooltipTheme,
|
|
5692
|
+
axisPointer: {
|
|
5693
|
+
type: "cross"
|
|
5694
|
+
}
|
|
5695
|
+
},
|
|
5696
|
+
legend: {
|
|
5697
|
+
data: (parsed.functions ?? []).map((fn) => fn.name),
|
|
5698
|
+
bottom: 10,
|
|
5699
|
+
textStyle: {
|
|
5700
|
+
color: textColor
|
|
5701
|
+
}
|
|
5702
|
+
},
|
|
5703
|
+
grid: {
|
|
5704
|
+
left: "3%",
|
|
5705
|
+
right: "4%",
|
|
5706
|
+
bottom: "15%",
|
|
5707
|
+
top: parsed.title ? "15%" : "5%",
|
|
5708
|
+
containLabel: true
|
|
5709
|
+
},
|
|
5710
|
+
xAxis: {
|
|
5711
|
+
type: "value",
|
|
5712
|
+
min: xRange.min,
|
|
5713
|
+
max: xRange.max,
|
|
5714
|
+
axisLine: {
|
|
5715
|
+
lineStyle: { color: axisLineColor }
|
|
5716
|
+
},
|
|
5717
|
+
axisLabel: {
|
|
5718
|
+
color: textColor
|
|
5719
|
+
},
|
|
5720
|
+
splitLine: {
|
|
5721
|
+
lineStyle: {
|
|
5722
|
+
color: palette.overlay
|
|
5723
|
+
}
|
|
5724
|
+
}
|
|
5725
|
+
},
|
|
5726
|
+
yAxis: {
|
|
5727
|
+
type: "value",
|
|
5728
|
+
axisLine: {
|
|
5729
|
+
lineStyle: { color: axisLineColor }
|
|
5730
|
+
},
|
|
5731
|
+
axisLabel: {
|
|
5732
|
+
color: textColor
|
|
5733
|
+
},
|
|
5734
|
+
splitLine: {
|
|
5735
|
+
lineStyle: {
|
|
5736
|
+
color: palette.overlay
|
|
5737
|
+
}
|
|
5738
|
+
}
|
|
5739
|
+
},
|
|
5740
|
+
series
|
|
5741
|
+
};
|
|
5742
|
+
}
|
|
5743
|
+
function buildScatterOption(parsed, palette, textColor, axisLineColor, colors, titleConfig, tooltipTheme) {
|
|
5744
|
+
const points = parsed.scatterPoints ?? [];
|
|
5745
|
+
const defaultSize = 15;
|
|
5746
|
+
const hasCategories = points.some((p) => p.category !== void 0);
|
|
5747
|
+
const hasSize = points.some((p) => p.size !== void 0);
|
|
5748
|
+
const labelConfig = {
|
|
5749
|
+
show: parsed.showLabels ?? false,
|
|
5750
|
+
formatter: "{b}",
|
|
5751
|
+
position: "top",
|
|
5752
|
+
color: textColor,
|
|
5753
|
+
fontSize: 11
|
|
5754
|
+
};
|
|
5755
|
+
const emphasisConfig = {
|
|
5756
|
+
focus: "self",
|
|
5757
|
+
itemStyle: {
|
|
5758
|
+
shadowBlur: 10,
|
|
5759
|
+
shadowColor: "rgba(0, 0, 0, 0.3)"
|
|
5760
|
+
}
|
|
5761
|
+
};
|
|
5762
|
+
let series;
|
|
5763
|
+
let legendData;
|
|
5764
|
+
if (hasCategories) {
|
|
5765
|
+
const categories = [
|
|
5766
|
+
...new Set(points.map((p) => p.category).filter(Boolean))
|
|
5767
|
+
];
|
|
5768
|
+
legendData = categories;
|
|
5769
|
+
series = categories.map((category, catIndex) => {
|
|
5770
|
+
const categoryPoints = points.filter((p) => p.category === category);
|
|
5771
|
+
const catColor = parsed.categoryColors?.[category] ?? colors[catIndex % colors.length];
|
|
5772
|
+
const data = categoryPoints.map((p) => ({
|
|
5773
|
+
name: p.name,
|
|
5774
|
+
value: hasSize ? [p.x, p.y, p.size ?? 0] : [p.x, p.y],
|
|
5775
|
+
...p.color && { itemStyle: { color: p.color } }
|
|
5776
|
+
}));
|
|
5777
|
+
return {
|
|
5778
|
+
name: category,
|
|
5779
|
+
type: "scatter",
|
|
5780
|
+
data,
|
|
5781
|
+
...hasSize ? { symbolSize: (val) => val[2] } : { symbolSize: defaultSize },
|
|
5782
|
+
itemStyle: { color: catColor },
|
|
5783
|
+
label: labelConfig,
|
|
5784
|
+
emphasis: emphasisConfig
|
|
5785
|
+
};
|
|
5786
|
+
});
|
|
5787
|
+
} else {
|
|
5788
|
+
const data = points.map((p, index) => ({
|
|
5789
|
+
name: p.name,
|
|
5790
|
+
value: hasSize ? [p.x, p.y, p.size ?? 0] : [p.x, p.y],
|
|
5791
|
+
...hasSize ? { symbolSize: p.size ?? defaultSize } : { symbolSize: defaultSize },
|
|
5792
|
+
itemStyle: {
|
|
5793
|
+
color: p.color ?? colors[index % colors.length]
|
|
5794
|
+
}
|
|
5795
|
+
}));
|
|
5796
|
+
series = [
|
|
5797
|
+
{
|
|
5798
|
+
type: "scatter",
|
|
5799
|
+
data,
|
|
5800
|
+
label: labelConfig,
|
|
5801
|
+
emphasis: emphasisConfig
|
|
5802
|
+
}
|
|
5803
|
+
];
|
|
5804
|
+
}
|
|
5805
|
+
const tooltip = {
|
|
5806
|
+
trigger: "item",
|
|
5807
|
+
...tooltipTheme,
|
|
5808
|
+
formatter: (params) => {
|
|
5809
|
+
const p = params;
|
|
5810
|
+
const xLabel = parsed.xlabel || "x";
|
|
5811
|
+
const yLabel = parsed.ylabel || "y";
|
|
5812
|
+
let html = `<strong>${p.name}</strong>`;
|
|
5813
|
+
if (hasCategories) html += `<br/>${p.seriesName}`;
|
|
5814
|
+
html += `<br/>${xLabel}: ${p.value[0]}<br/>${yLabel}: ${p.value[1]}`;
|
|
5815
|
+
if (hasSize) html += `<br/>${parsed.sizelabel || "size"}: ${p.value[2]}`;
|
|
5816
|
+
return html;
|
|
5817
|
+
}
|
|
5818
|
+
};
|
|
5819
|
+
return {
|
|
5820
|
+
backgroundColor: "transparent",
|
|
5821
|
+
animation: false,
|
|
5822
|
+
title: titleConfig,
|
|
5823
|
+
tooltip,
|
|
5824
|
+
...legendData && {
|
|
5825
|
+
legend: {
|
|
5826
|
+
data: legendData,
|
|
5827
|
+
bottom: 10,
|
|
5828
|
+
textStyle: { color: textColor }
|
|
5829
|
+
}
|
|
5830
|
+
},
|
|
5831
|
+
grid: {
|
|
5832
|
+
left: "3%",
|
|
5833
|
+
right: "4%",
|
|
5834
|
+
bottom: hasCategories ? "15%" : "3%",
|
|
5835
|
+
top: parsed.title ? "15%" : "5%",
|
|
5836
|
+
containLabel: true
|
|
5837
|
+
},
|
|
5838
|
+
xAxis: {
|
|
5839
|
+
type: "value",
|
|
5840
|
+
name: parsed.xlabel,
|
|
5841
|
+
nameLocation: "middle",
|
|
5842
|
+
nameGap: 30,
|
|
5843
|
+
nameTextStyle: {
|
|
5844
|
+
color: textColor,
|
|
5845
|
+
fontSize: 12
|
|
5846
|
+
},
|
|
5847
|
+
axisLine: {
|
|
5848
|
+
lineStyle: { color: axisLineColor }
|
|
5849
|
+
},
|
|
5850
|
+
axisLabel: {
|
|
5851
|
+
color: textColor
|
|
5852
|
+
},
|
|
5853
|
+
splitLine: {
|
|
5854
|
+
lineStyle: {
|
|
5855
|
+
color: palette.overlay
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5858
|
+
},
|
|
5859
|
+
yAxis: {
|
|
5860
|
+
type: "value",
|
|
5861
|
+
name: parsed.ylabel,
|
|
5862
|
+
nameLocation: "middle",
|
|
5863
|
+
nameGap: 40,
|
|
5864
|
+
nameTextStyle: {
|
|
5865
|
+
color: textColor,
|
|
5866
|
+
fontSize: 12
|
|
5867
|
+
},
|
|
5868
|
+
axisLine: {
|
|
5869
|
+
lineStyle: { color: axisLineColor }
|
|
5870
|
+
},
|
|
5871
|
+
axisLabel: {
|
|
5872
|
+
color: textColor
|
|
5873
|
+
},
|
|
5874
|
+
splitLine: {
|
|
5875
|
+
lineStyle: {
|
|
5876
|
+
color: palette.overlay
|
|
5877
|
+
}
|
|
5878
|
+
}
|
|
5879
|
+
},
|
|
5880
|
+
series
|
|
5881
|
+
};
|
|
5882
|
+
}
|
|
5883
|
+
function buildHeatmapOption(parsed, palette, textColor, axisLineColor, titleConfig, tooltipTheme) {
|
|
5884
|
+
const heatmapRows = parsed.heatmapRows ?? [];
|
|
5885
|
+
const columns = parsed.columns ?? [];
|
|
5886
|
+
const rowLabels = heatmapRows.map((r) => r.label);
|
|
5887
|
+
const data = [];
|
|
5888
|
+
let minValue = Infinity;
|
|
5889
|
+
let maxValue = -Infinity;
|
|
5890
|
+
heatmapRows.forEach((row, rowIndex) => {
|
|
5891
|
+
row.values.forEach((value, colIndex) => {
|
|
5892
|
+
data.push([colIndex, rowIndex, value]);
|
|
5893
|
+
minValue = Math.min(minValue, value);
|
|
5894
|
+
maxValue = Math.max(maxValue, value);
|
|
5895
|
+
});
|
|
5896
|
+
});
|
|
5897
|
+
return {
|
|
5898
|
+
backgroundColor: "transparent",
|
|
5899
|
+
animation: false,
|
|
5900
|
+
title: titleConfig,
|
|
5901
|
+
tooltip: {
|
|
5902
|
+
trigger: "item",
|
|
5903
|
+
...tooltipTheme,
|
|
5904
|
+
formatter: (params) => {
|
|
5905
|
+
const p = params;
|
|
5906
|
+
const colName = columns[p.data[0]] ?? p.data[0];
|
|
5907
|
+
const rowName = rowLabels[p.data[1]] ?? p.data[1];
|
|
5908
|
+
return `${rowName} / ${colName}: <strong>${p.data[2]}</strong>`;
|
|
5909
|
+
}
|
|
5910
|
+
},
|
|
5911
|
+
grid: {
|
|
5912
|
+
left: "3%",
|
|
5913
|
+
right: "10%",
|
|
5914
|
+
bottom: "3%",
|
|
5915
|
+
top: parsed.title ? "15%" : "5%",
|
|
5916
|
+
containLabel: true
|
|
5917
|
+
},
|
|
5918
|
+
xAxis: {
|
|
5919
|
+
type: "category",
|
|
5920
|
+
data: columns,
|
|
5921
|
+
splitArea: {
|
|
5922
|
+
show: true
|
|
5923
|
+
},
|
|
5924
|
+
axisLine: {
|
|
5925
|
+
lineStyle: { color: axisLineColor }
|
|
5926
|
+
},
|
|
5927
|
+
axisLabel: {
|
|
5928
|
+
color: textColor
|
|
5929
|
+
}
|
|
5930
|
+
},
|
|
5931
|
+
yAxis: {
|
|
5932
|
+
type: "category",
|
|
5933
|
+
data: rowLabels,
|
|
5934
|
+
splitArea: {
|
|
5935
|
+
show: true
|
|
5936
|
+
},
|
|
5937
|
+
axisLine: {
|
|
5938
|
+
lineStyle: { color: axisLineColor }
|
|
5939
|
+
},
|
|
5940
|
+
axisLabel: {
|
|
5941
|
+
color: textColor
|
|
5942
|
+
}
|
|
5943
|
+
},
|
|
5944
|
+
visualMap: {
|
|
5945
|
+
min: minValue,
|
|
5946
|
+
max: maxValue,
|
|
5947
|
+
calculable: true,
|
|
5948
|
+
orient: "vertical",
|
|
5949
|
+
right: "2%",
|
|
5950
|
+
top: "center",
|
|
5951
|
+
inRange: {
|
|
5952
|
+
color: [
|
|
5953
|
+
palette.bg,
|
|
5954
|
+
palette.primary,
|
|
5955
|
+
palette.colors.cyan,
|
|
5956
|
+
palette.colors.yellow,
|
|
5957
|
+
palette.colors.orange
|
|
5958
|
+
]
|
|
5959
|
+
},
|
|
5960
|
+
textStyle: {
|
|
5961
|
+
color: textColor
|
|
5962
|
+
}
|
|
5963
|
+
},
|
|
5964
|
+
series: [
|
|
5965
|
+
{
|
|
5966
|
+
type: "heatmap",
|
|
5967
|
+
data,
|
|
5968
|
+
label: {
|
|
5969
|
+
show: true,
|
|
5970
|
+
color: textColor
|
|
5971
|
+
},
|
|
5972
|
+
emphasis: {
|
|
5973
|
+
itemStyle: {
|
|
5974
|
+
shadowBlur: 10,
|
|
5975
|
+
shadowColor: "rgba(0, 0, 0, 0.5)"
|
|
5976
|
+
}
|
|
5977
|
+
}
|
|
5978
|
+
}
|
|
5979
|
+
]
|
|
5980
|
+
};
|
|
5981
|
+
}
|
|
5982
|
+
function buildFunnelOption(parsed, textColor, colors, titleConfig, tooltipTheme) {
|
|
5983
|
+
const sorted = [...parsed.data].sort((a, b) => b.value - a.value);
|
|
5984
|
+
const topValue = sorted.length > 0 ? sorted[0].value : 1;
|
|
5985
|
+
const data = sorted.map((d) => ({
|
|
5986
|
+
name: d.label,
|
|
5987
|
+
value: d.value,
|
|
5988
|
+
itemStyle: {
|
|
5989
|
+
color: d.color ?? colors[parsed.data.indexOf(d) % colors.length],
|
|
5990
|
+
borderWidth: 0
|
|
5991
|
+
}
|
|
5992
|
+
}));
|
|
5993
|
+
const prevValueMap = /* @__PURE__ */ new Map();
|
|
5994
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
5995
|
+
prevValueMap.set(
|
|
5996
|
+
sorted[i].label,
|
|
5997
|
+
i > 0 ? sorted[i - 1].value : sorted[i].value
|
|
5998
|
+
);
|
|
5999
|
+
}
|
|
6000
|
+
const funnelTop = parsed.title ? 60 : 20;
|
|
6001
|
+
const funnelLayout = {
|
|
6002
|
+
left: "20%",
|
|
6003
|
+
top: funnelTop,
|
|
6004
|
+
bottom: 20,
|
|
6005
|
+
width: "60%",
|
|
6006
|
+
sort: "descending",
|
|
6007
|
+
gap: 2,
|
|
6008
|
+
minSize: "8%"
|
|
6009
|
+
};
|
|
6010
|
+
return {
|
|
6011
|
+
backgroundColor: "transparent",
|
|
6012
|
+
animation: false,
|
|
6013
|
+
title: titleConfig,
|
|
6014
|
+
tooltip: {
|
|
6015
|
+
trigger: "item",
|
|
6016
|
+
...tooltipTheme,
|
|
6017
|
+
formatter: (params) => {
|
|
6018
|
+
const p = params;
|
|
6019
|
+
const val = p.value;
|
|
6020
|
+
const prev = prevValueMap.get(p.name) ?? val;
|
|
6021
|
+
const isFirst = p.dataIndex === 0;
|
|
6022
|
+
let html = `<strong>${p.name}</strong>: ${val}`;
|
|
6023
|
+
if (!isFirst) {
|
|
6024
|
+
const stepDrop = ((1 - val / prev) * 100).toFixed(1);
|
|
6025
|
+
html += `<br/>Step drop-off: ${stepDrop}%`;
|
|
6026
|
+
}
|
|
6027
|
+
if (!isFirst && topValue > 0) {
|
|
6028
|
+
const totalDrop = ((1 - val / topValue) * 100).toFixed(1);
|
|
6029
|
+
html += `<br/>Overall drop-off: ${totalDrop}%`;
|
|
6030
|
+
}
|
|
6031
|
+
return html;
|
|
6032
|
+
}
|
|
6033
|
+
},
|
|
6034
|
+
series: [
|
|
6035
|
+
{
|
|
6036
|
+
type: "funnel",
|
|
6037
|
+
...funnelLayout,
|
|
6038
|
+
label: {
|
|
6039
|
+
show: true,
|
|
6040
|
+
position: "left",
|
|
6041
|
+
formatter: "{b}",
|
|
6042
|
+
color: textColor,
|
|
6043
|
+
fontSize: 13
|
|
6044
|
+
},
|
|
6045
|
+
labelLine: {
|
|
6046
|
+
show: true,
|
|
6047
|
+
length: 10,
|
|
6048
|
+
lineStyle: { color: textColor, opacity: 0.3 }
|
|
6049
|
+
},
|
|
6050
|
+
emphasis: {
|
|
6051
|
+
label: {
|
|
6052
|
+
fontSize: 15
|
|
6053
|
+
}
|
|
6054
|
+
},
|
|
6055
|
+
data
|
|
6056
|
+
},
|
|
6057
|
+
{
|
|
6058
|
+
type: "funnel",
|
|
6059
|
+
...funnelLayout,
|
|
6060
|
+
silent: true,
|
|
6061
|
+
itemStyle: { color: "transparent", borderWidth: 0 },
|
|
6062
|
+
label: {
|
|
6063
|
+
show: true,
|
|
6064
|
+
position: "right",
|
|
6065
|
+
formatter: "{c}",
|
|
6066
|
+
color: textColor,
|
|
6067
|
+
fontSize: 13
|
|
6068
|
+
},
|
|
6069
|
+
labelLine: {
|
|
6070
|
+
show: true,
|
|
6071
|
+
length: 10,
|
|
6072
|
+
lineStyle: { color: textColor, opacity: 0.3 }
|
|
6073
|
+
},
|
|
6074
|
+
emphasis: { disabled: true },
|
|
6075
|
+
data: data.map((d) => ({
|
|
6076
|
+
...d,
|
|
6077
|
+
itemStyle: { color: "transparent", borderWidth: 0 }
|
|
6078
|
+
}))
|
|
6079
|
+
}
|
|
6080
|
+
]
|
|
6081
|
+
};
|
|
6082
|
+
}
|
|
6083
|
+
function resolveAxisLabels(parsed) {
|
|
6084
|
+
const isHorizontal = parsed.orientation === "horizontal";
|
|
6085
|
+
return {
|
|
6086
|
+
xLabel: parsed.xlabel ?? (isHorizontal ? parsed.label : void 0),
|
|
6087
|
+
yLabel: parsed.ylabel ?? (isHorizontal ? void 0 : parsed.label)
|
|
6088
|
+
};
|
|
6089
|
+
}
|
|
6090
|
+
function makeGridAxis(type, textColor, axisLineColor, splitLineColor, label, data) {
|
|
6091
|
+
return {
|
|
6092
|
+
type,
|
|
6093
|
+
...data && { data },
|
|
6094
|
+
axisLine: { lineStyle: { color: axisLineColor } },
|
|
6095
|
+
axisLabel: { color: textColor, fontFamily: FONT_FAMILY },
|
|
6096
|
+
splitLine: { lineStyle: { color: splitLineColor } },
|
|
6097
|
+
...label && {
|
|
6098
|
+
name: label,
|
|
6099
|
+
nameLocation: "middle",
|
|
6100
|
+
nameGap: 30,
|
|
6101
|
+
nameTextStyle: { color: textColor, fontSize: 12, fontFamily: FONT_FAMILY }
|
|
6102
|
+
}
|
|
6103
|
+
};
|
|
6104
|
+
}
|
|
6105
|
+
function buildEChartsOptionFromChart(parsed, palette, _isDark) {
|
|
6106
|
+
if (parsed.error) return {};
|
|
6107
|
+
const textColor = palette.text;
|
|
6108
|
+
const axisLineColor = palette.border;
|
|
6109
|
+
const splitLineColor = palette.overlay;
|
|
6110
|
+
const colors = getSeriesColors(palette);
|
|
6111
|
+
const titleConfig = parsed.title ? {
|
|
6112
|
+
text: parsed.title,
|
|
6113
|
+
left: "center",
|
|
6114
|
+
textStyle: {
|
|
6115
|
+
color: textColor,
|
|
6116
|
+
fontSize: 18,
|
|
6117
|
+
fontWeight: "bold",
|
|
6118
|
+
fontFamily: FONT_FAMILY
|
|
6119
|
+
}
|
|
6120
|
+
} : void 0;
|
|
6121
|
+
const tooltipTheme = {
|
|
6122
|
+
backgroundColor: palette.surface,
|
|
6123
|
+
borderColor: palette.border,
|
|
6124
|
+
textStyle: { color: palette.text }
|
|
6125
|
+
};
|
|
6126
|
+
switch (parsed.type) {
|
|
6127
|
+
case "bar":
|
|
6128
|
+
return buildBarOption(parsed, textColor, axisLineColor, splitLineColor, colors, titleConfig, tooltipTheme);
|
|
6129
|
+
case "bar-stacked":
|
|
6130
|
+
return buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, colors, titleConfig, tooltipTheme);
|
|
6131
|
+
case "line":
|
|
6132
|
+
return parsed.seriesNames ? buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, colors, titleConfig, tooltipTheme) : buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, titleConfig, tooltipTheme);
|
|
6133
|
+
case "area":
|
|
6134
|
+
return buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, titleConfig, tooltipTheme);
|
|
6135
|
+
case "pie":
|
|
6136
|
+
return buildPieOption(parsed, textColor, colors, titleConfig, tooltipTheme, false);
|
|
6137
|
+
case "doughnut":
|
|
6138
|
+
return buildPieOption(parsed, textColor, colors, titleConfig, tooltipTheme, true);
|
|
6139
|
+
case "radar":
|
|
6140
|
+
return buildRadarOption(parsed, palette, textColor, colors, titleConfig, tooltipTheme);
|
|
6141
|
+
case "polar-area":
|
|
6142
|
+
return buildPolarAreaOption(parsed, textColor, colors, titleConfig, tooltipTheme);
|
|
6143
|
+
}
|
|
6144
|
+
}
|
|
6145
|
+
function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, colors, titleConfig, tooltipTheme) {
|
|
6146
|
+
const { xLabel, yLabel } = resolveAxisLabels(parsed);
|
|
6147
|
+
const isHorizontal = parsed.orientation === "horizontal";
|
|
6148
|
+
const labels = parsed.data.map((d) => d.label);
|
|
6149
|
+
const data = parsed.data.map((d, i) => ({
|
|
6150
|
+
value: d.value,
|
|
6151
|
+
itemStyle: { color: d.color ?? colors[i % colors.length] }
|
|
6152
|
+
}));
|
|
6153
|
+
const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, isHorizontal ? yLabel : xLabel, labels);
|
|
6154
|
+
const valueAxis = makeGridAxis("value", textColor, axisLineColor, splitLineColor, isHorizontal ? xLabel : yLabel);
|
|
6155
|
+
return {
|
|
6156
|
+
backgroundColor: "transparent",
|
|
6157
|
+
animation: false,
|
|
6158
|
+
title: titleConfig,
|
|
6159
|
+
tooltip: {
|
|
6160
|
+
trigger: "axis",
|
|
6161
|
+
...tooltipTheme,
|
|
6162
|
+
axisPointer: { type: "shadow" }
|
|
6163
|
+
},
|
|
6164
|
+
grid: {
|
|
6165
|
+
left: "3%",
|
|
6166
|
+
right: "4%",
|
|
6167
|
+
bottom: "3%",
|
|
6168
|
+
top: parsed.title ? "15%" : "5%",
|
|
6169
|
+
containLabel: true
|
|
6170
|
+
},
|
|
6171
|
+
xAxis: isHorizontal ? valueAxis : categoryAxis,
|
|
6172
|
+
yAxis: isHorizontal ? categoryAxis : valueAxis,
|
|
6173
|
+
series: [
|
|
6174
|
+
{
|
|
6175
|
+
type: "bar",
|
|
6176
|
+
data
|
|
6177
|
+
}
|
|
6178
|
+
]
|
|
6179
|
+
};
|
|
6180
|
+
}
|
|
6181
|
+
function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, titleConfig, tooltipTheme) {
|
|
6182
|
+
const { xLabel, yLabel } = resolveAxisLabels(parsed);
|
|
6183
|
+
const lineColor = parsed.color ?? parsed.seriesNameColors?.[0] ?? palette.primary;
|
|
6184
|
+
const labels = parsed.data.map((d) => d.label);
|
|
6185
|
+
const values = parsed.data.map((d) => d.value);
|
|
6186
|
+
return {
|
|
6187
|
+
backgroundColor: "transparent",
|
|
6188
|
+
animation: false,
|
|
6189
|
+
title: titleConfig,
|
|
6190
|
+
tooltip: {
|
|
6191
|
+
trigger: "axis",
|
|
6192
|
+
...tooltipTheme,
|
|
6193
|
+
axisPointer: { type: "line" }
|
|
6194
|
+
},
|
|
6195
|
+
grid: {
|
|
6196
|
+
left: "3%",
|
|
6197
|
+
right: "4%",
|
|
6198
|
+
bottom: "3%",
|
|
6199
|
+
top: parsed.title ? "15%" : "5%",
|
|
6200
|
+
containLabel: true
|
|
6201
|
+
},
|
|
6202
|
+
xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, xLabel, labels),
|
|
6203
|
+
yAxis: makeGridAxis("value", textColor, axisLineColor, splitLineColor, yLabel),
|
|
6204
|
+
series: [
|
|
6205
|
+
{
|
|
6206
|
+
type: "line",
|
|
6207
|
+
data: values,
|
|
6208
|
+
smooth: false,
|
|
6209
|
+
symbolSize: 8,
|
|
6210
|
+
lineStyle: { color: lineColor, width: 3 },
|
|
6211
|
+
itemStyle: { color: lineColor }
|
|
6212
|
+
}
|
|
6213
|
+
]
|
|
6214
|
+
};
|
|
6215
|
+
}
|
|
6216
|
+
function buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, colors, titleConfig, tooltipTheme) {
|
|
6217
|
+
const { xLabel, yLabel } = resolveAxisLabels(parsed);
|
|
6218
|
+
const seriesNames = parsed.seriesNames ?? [];
|
|
6219
|
+
const labels = parsed.data.map((d) => d.label);
|
|
6220
|
+
const series = seriesNames.map((name, idx) => {
|
|
6221
|
+
const color = parsed.seriesNameColors?.[idx] ?? colors[idx % colors.length];
|
|
6222
|
+
const data = parsed.data.map(
|
|
6223
|
+
(dp) => idx === 0 ? dp.value : dp.extraValues?.[idx - 1] ?? 0
|
|
6224
|
+
);
|
|
6225
|
+
return {
|
|
6226
|
+
name,
|
|
6227
|
+
type: "line",
|
|
6228
|
+
data,
|
|
6229
|
+
smooth: false,
|
|
6230
|
+
symbolSize: 8,
|
|
6231
|
+
lineStyle: { color, width: 3 },
|
|
6232
|
+
itemStyle: { color }
|
|
6233
|
+
};
|
|
6234
|
+
});
|
|
6235
|
+
return {
|
|
6236
|
+
backgroundColor: "transparent",
|
|
6237
|
+
animation: false,
|
|
6238
|
+
title: titleConfig,
|
|
6239
|
+
tooltip: {
|
|
6240
|
+
trigger: "axis",
|
|
6241
|
+
...tooltipTheme,
|
|
6242
|
+
axisPointer: { type: "line" }
|
|
6243
|
+
},
|
|
6244
|
+
legend: {
|
|
6245
|
+
data: seriesNames,
|
|
6246
|
+
bottom: 10,
|
|
6247
|
+
textStyle: { color: textColor }
|
|
6248
|
+
},
|
|
6249
|
+
grid: {
|
|
6250
|
+
left: "3%",
|
|
6251
|
+
right: "4%",
|
|
6252
|
+
bottom: "15%",
|
|
6253
|
+
top: parsed.title ? "15%" : "5%",
|
|
6254
|
+
containLabel: true
|
|
6255
|
+
},
|
|
6256
|
+
xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, xLabel, labels),
|
|
6257
|
+
yAxis: makeGridAxis("value", textColor, axisLineColor, splitLineColor, yLabel),
|
|
6258
|
+
series
|
|
6259
|
+
};
|
|
6260
|
+
}
|
|
6261
|
+
function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, titleConfig, tooltipTheme) {
|
|
6262
|
+
const { xLabel, yLabel } = resolveAxisLabels(parsed);
|
|
6263
|
+
const lineColor = parsed.color ?? parsed.seriesNameColors?.[0] ?? palette.primary;
|
|
6264
|
+
const labels = parsed.data.map((d) => d.label);
|
|
6265
|
+
const values = parsed.data.map((d) => d.value);
|
|
6266
|
+
return {
|
|
6267
|
+
backgroundColor: "transparent",
|
|
6268
|
+
animation: false,
|
|
6269
|
+
title: titleConfig,
|
|
6270
|
+
tooltip: {
|
|
6271
|
+
trigger: "axis",
|
|
6272
|
+
...tooltipTheme,
|
|
6273
|
+
axisPointer: { type: "line" }
|
|
6274
|
+
},
|
|
6275
|
+
grid: {
|
|
6276
|
+
left: "3%",
|
|
6277
|
+
right: "4%",
|
|
6278
|
+
bottom: "3%",
|
|
6279
|
+
top: parsed.title ? "15%" : "5%",
|
|
6280
|
+
containLabel: true
|
|
6281
|
+
},
|
|
6282
|
+
xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, xLabel, labels),
|
|
6283
|
+
yAxis: makeGridAxis("value", textColor, axisLineColor, splitLineColor, yLabel),
|
|
6284
|
+
series: [
|
|
6285
|
+
{
|
|
6286
|
+
type: "line",
|
|
6287
|
+
data: values,
|
|
6288
|
+
smooth: false,
|
|
6289
|
+
symbolSize: 8,
|
|
6290
|
+
lineStyle: { color: lineColor, width: 3 },
|
|
6291
|
+
itemStyle: { color: lineColor },
|
|
6292
|
+
areaStyle: { opacity: 0.25 }
|
|
6293
|
+
}
|
|
6294
|
+
]
|
|
6295
|
+
};
|
|
6296
|
+
}
|
|
6297
|
+
function buildPieOption(parsed, textColor, colors, titleConfig, tooltipTheme, isDoughnut) {
|
|
6298
|
+
const data = parsed.data.map((d, i) => ({
|
|
6299
|
+
name: d.label,
|
|
6300
|
+
value: d.value,
|
|
6301
|
+
itemStyle: { color: d.color ?? colors[i % colors.length] }
|
|
6302
|
+
}));
|
|
6303
|
+
return {
|
|
6304
|
+
backgroundColor: "transparent",
|
|
6305
|
+
animation: false,
|
|
6306
|
+
title: titleConfig,
|
|
6307
|
+
tooltip: {
|
|
6308
|
+
trigger: "item",
|
|
6309
|
+
...tooltipTheme
|
|
6310
|
+
},
|
|
6311
|
+
series: [
|
|
6312
|
+
{
|
|
6313
|
+
type: "pie",
|
|
6314
|
+
radius: isDoughnut ? ["40%", "70%"] : ["0%", "70%"],
|
|
6315
|
+
data,
|
|
6316
|
+
label: {
|
|
6317
|
+
position: "outside",
|
|
6318
|
+
formatter: "{b}",
|
|
6319
|
+
color: textColor,
|
|
6320
|
+
fontFamily: FONT_FAMILY
|
|
6321
|
+
},
|
|
6322
|
+
labelLine: { show: true }
|
|
6323
|
+
}
|
|
6324
|
+
]
|
|
6325
|
+
};
|
|
6326
|
+
}
|
|
6327
|
+
function buildRadarOption(parsed, palette, textColor, colors, titleConfig, tooltipTheme) {
|
|
6328
|
+
const radarColor = parsed.color ?? parsed.seriesNameColors?.[0] ?? palette.primary;
|
|
6329
|
+
const values = parsed.data.map((d) => d.value);
|
|
6330
|
+
const maxValue = Math.max(...values) * 1.15;
|
|
6331
|
+
const gridOpacity = 0.6;
|
|
6332
|
+
const indicator = parsed.data.map((d) => ({
|
|
6333
|
+
name: d.label,
|
|
6334
|
+
max: maxValue
|
|
6335
|
+
}));
|
|
6336
|
+
return {
|
|
6337
|
+
backgroundColor: "transparent",
|
|
6338
|
+
animation: false,
|
|
6339
|
+
title: titleConfig,
|
|
6340
|
+
tooltip: {
|
|
6341
|
+
trigger: "item",
|
|
6342
|
+
...tooltipTheme
|
|
6343
|
+
},
|
|
6344
|
+
radar: {
|
|
6345
|
+
indicator,
|
|
6346
|
+
axisName: {
|
|
6347
|
+
color: textColor,
|
|
6348
|
+
fontFamily: FONT_FAMILY
|
|
6349
|
+
},
|
|
6350
|
+
splitLine: {
|
|
6351
|
+
lineStyle: { color: palette.border, opacity: gridOpacity }
|
|
6352
|
+
},
|
|
6353
|
+
axisLine: {
|
|
6354
|
+
lineStyle: { color: palette.border, opacity: gridOpacity }
|
|
6355
|
+
},
|
|
6356
|
+
splitArea: { show: false }
|
|
6357
|
+
},
|
|
6358
|
+
series: [
|
|
6359
|
+
{
|
|
6360
|
+
type: "radar",
|
|
6361
|
+
data: [
|
|
6362
|
+
{
|
|
6363
|
+
value: values,
|
|
6364
|
+
name: parsed.series ?? "Value",
|
|
6365
|
+
areaStyle: { color: radarColor, opacity: 0.25 },
|
|
6366
|
+
lineStyle: { color: radarColor },
|
|
6367
|
+
itemStyle: { color: radarColor },
|
|
6368
|
+
symbol: "circle",
|
|
6369
|
+
symbolSize: 8,
|
|
6370
|
+
label: {
|
|
6371
|
+
show: true,
|
|
6372
|
+
formatter: "{c}",
|
|
6373
|
+
color: textColor,
|
|
6374
|
+
fontSize: 11,
|
|
6375
|
+
fontFamily: FONT_FAMILY
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
]
|
|
6379
|
+
}
|
|
6380
|
+
]
|
|
6381
|
+
};
|
|
6382
|
+
}
|
|
6383
|
+
function buildPolarAreaOption(parsed, textColor, colors, titleConfig, tooltipTheme) {
|
|
6384
|
+
const data = parsed.data.map((d, i) => ({
|
|
6385
|
+
name: d.label,
|
|
6386
|
+
value: d.value,
|
|
6387
|
+
itemStyle: { color: d.color ?? colors[i % colors.length] }
|
|
6388
|
+
}));
|
|
6389
|
+
return {
|
|
6390
|
+
backgroundColor: "transparent",
|
|
6391
|
+
animation: false,
|
|
6392
|
+
title: titleConfig,
|
|
6393
|
+
tooltip: {
|
|
6394
|
+
trigger: "item",
|
|
6395
|
+
...tooltipTheme
|
|
6396
|
+
},
|
|
6397
|
+
series: [
|
|
6398
|
+
{
|
|
6399
|
+
type: "pie",
|
|
6400
|
+
roseType: "radius",
|
|
6401
|
+
radius: ["10%", "70%"],
|
|
6402
|
+
data,
|
|
6403
|
+
label: {
|
|
6404
|
+
position: "outside",
|
|
6405
|
+
formatter: "{b}",
|
|
6406
|
+
color: textColor,
|
|
6407
|
+
fontFamily: FONT_FAMILY
|
|
6408
|
+
},
|
|
6409
|
+
labelLine: { show: true }
|
|
6410
|
+
}
|
|
6411
|
+
]
|
|
6412
|
+
};
|
|
6413
|
+
}
|
|
6414
|
+
function buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, colors, titleConfig, tooltipTheme) {
|
|
6415
|
+
const { xLabel, yLabel } = resolveAxisLabels(parsed);
|
|
6416
|
+
const isHorizontal = parsed.orientation === "horizontal";
|
|
6417
|
+
const seriesNames = parsed.seriesNames ?? [];
|
|
6418
|
+
const labels = parsed.data.map((d) => d.label);
|
|
6419
|
+
const series = seriesNames.map((name, idx) => {
|
|
6420
|
+
const color = parsed.seriesNameColors?.[idx] ?? colors[idx % colors.length];
|
|
6421
|
+
const data = parsed.data.map(
|
|
6422
|
+
(dp) => idx === 0 ? dp.value : dp.extraValues?.[idx - 1] ?? 0
|
|
6423
|
+
);
|
|
6424
|
+
return {
|
|
6425
|
+
name,
|
|
6426
|
+
type: "bar",
|
|
6427
|
+
stack: "total",
|
|
6428
|
+
data,
|
|
6429
|
+
itemStyle: { color }
|
|
6430
|
+
};
|
|
6431
|
+
});
|
|
6432
|
+
const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, isHorizontal ? yLabel : xLabel, labels);
|
|
6433
|
+
const valueAxis = makeGridAxis("value", textColor, axisLineColor, splitLineColor, isHorizontal ? xLabel : yLabel);
|
|
6434
|
+
return {
|
|
6435
|
+
backgroundColor: "transparent",
|
|
6436
|
+
animation: false,
|
|
6437
|
+
title: titleConfig,
|
|
6438
|
+
tooltip: {
|
|
6439
|
+
trigger: "axis",
|
|
6440
|
+
...tooltipTheme,
|
|
6441
|
+
axisPointer: { type: "shadow" }
|
|
6442
|
+
},
|
|
6443
|
+
legend: {
|
|
6444
|
+
data: seriesNames,
|
|
6445
|
+
bottom: 10,
|
|
6446
|
+
textStyle: { color: textColor }
|
|
6447
|
+
},
|
|
6448
|
+
grid: {
|
|
6449
|
+
left: "3%",
|
|
6450
|
+
right: "4%",
|
|
6451
|
+
bottom: "15%",
|
|
6452
|
+
top: parsed.title ? "15%" : "5%",
|
|
6453
|
+
containLabel: true
|
|
6454
|
+
},
|
|
6455
|
+
xAxis: isHorizontal ? valueAxis : categoryAxis,
|
|
6456
|
+
yAxis: isHorizontal ? categoryAxis : valueAxis,
|
|
6457
|
+
series
|
|
6458
|
+
};
|
|
6459
|
+
}
|
|
6460
|
+
var ECHART_EXPORT_WIDTH = 1200;
|
|
6461
|
+
var ECHART_EXPORT_HEIGHT = 800;
|
|
6462
|
+
var STANDARD_CHART_TYPES = /* @__PURE__ */ new Set([
|
|
6463
|
+
"bar",
|
|
6464
|
+
"line",
|
|
6465
|
+
"multi-line",
|
|
6466
|
+
"area",
|
|
6467
|
+
"pie",
|
|
6468
|
+
"doughnut",
|
|
6469
|
+
"radar",
|
|
6470
|
+
"polar-area",
|
|
6471
|
+
"bar-stacked"
|
|
6472
|
+
]);
|
|
6473
|
+
async function renderEChartsForExport(content, theme, palette) {
|
|
6474
|
+
const isDark = theme === "dark";
|
|
6475
|
+
const { getPalette: getPalette2 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
|
|
6476
|
+
const effectivePalette = palette ?? (isDark ? getPalette2("nord").dark : getPalette2("nord").light);
|
|
6477
|
+
const chartLine = content.match(/^chart\s*:\s*(.+)/im);
|
|
6478
|
+
const chartType = chartLine?.[1]?.trim().toLowerCase();
|
|
6479
|
+
let option;
|
|
6480
|
+
if (chartType && STANDARD_CHART_TYPES.has(chartType)) {
|
|
6481
|
+
const parsed = parseChart(content, effectivePalette);
|
|
6482
|
+
if (parsed.error) return "";
|
|
6483
|
+
option = buildEChartsOptionFromChart(parsed, effectivePalette, isDark);
|
|
6484
|
+
} else {
|
|
6485
|
+
const parsed = parseEChart(content, effectivePalette);
|
|
6486
|
+
if (parsed.error) return "";
|
|
6487
|
+
option = buildEChartsOption(parsed, effectivePalette, isDark);
|
|
6488
|
+
}
|
|
6489
|
+
if (!option || Object.keys(option).length === 0) return "";
|
|
6490
|
+
const chart = echarts.init(null, null, {
|
|
6491
|
+
renderer: "svg",
|
|
6492
|
+
ssr: true,
|
|
6493
|
+
width: ECHART_EXPORT_WIDTH,
|
|
6494
|
+
height: ECHART_EXPORT_HEIGHT
|
|
6495
|
+
});
|
|
6496
|
+
try {
|
|
6497
|
+
chart.setOption(option);
|
|
6498
|
+
const svgString = chart.renderToSVGString();
|
|
6499
|
+
if (!svgString) return "";
|
|
6500
|
+
return svgString.replace(
|
|
6501
|
+
/^<svg /,
|
|
6502
|
+
`<svg style="font-family: ${FONT_FAMILY}" `
|
|
6503
|
+
);
|
|
6504
|
+
} finally {
|
|
6505
|
+
chart.dispose();
|
|
6506
|
+
}
|
|
6507
|
+
}
|
|
6508
|
+
|
|
6509
|
+
// src/dgmo-router.ts
|
|
6510
|
+
init_parser();
|
|
6511
|
+
var DGMO_CHART_TYPE_MAP = {
|
|
6512
|
+
// Standard charts (via ECharts)
|
|
6513
|
+
bar: "echart",
|
|
6514
|
+
line: "echart",
|
|
6515
|
+
"multi-line": "echart",
|
|
6516
|
+
area: "echart",
|
|
6517
|
+
pie: "echart",
|
|
6518
|
+
doughnut: "echart",
|
|
6519
|
+
radar: "echart",
|
|
6520
|
+
"polar-area": "echart",
|
|
6521
|
+
"bar-stacked": "echart",
|
|
6522
|
+
// ECharts
|
|
6523
|
+
scatter: "echart",
|
|
6524
|
+
sankey: "echart",
|
|
6525
|
+
chord: "echart",
|
|
6526
|
+
function: "echart",
|
|
6527
|
+
heatmap: "echart",
|
|
6528
|
+
funnel: "echart",
|
|
6529
|
+
// D3
|
|
6530
|
+
slope: "d3",
|
|
6531
|
+
wordcloud: "d3",
|
|
6532
|
+
arc: "d3",
|
|
6533
|
+
timeline: "d3",
|
|
6534
|
+
venn: "d3",
|
|
6535
|
+
quadrant: "d3",
|
|
6536
|
+
sequence: "d3"
|
|
6537
|
+
};
|
|
6538
|
+
function getDgmoFramework(chartType) {
|
|
6539
|
+
return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
|
|
6540
|
+
}
|
|
6541
|
+
function parseDgmoChartType(content) {
|
|
6542
|
+
const lines = content.split("\n");
|
|
6543
|
+
for (const line2 of lines) {
|
|
6544
|
+
const trimmed = line2.trim();
|
|
6545
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
|
|
6546
|
+
continue;
|
|
6547
|
+
const match = trimmed.match(/^chart\s*:\s*(.+)/i);
|
|
6548
|
+
if (match) return match[1].trim().toLowerCase();
|
|
6549
|
+
}
|
|
6550
|
+
if (looksLikeSequence(content)) return "sequence";
|
|
6551
|
+
return null;
|
|
6552
|
+
}
|
|
6553
|
+
|
|
5069
6554
|
// src/cli.ts
|
|
5070
6555
|
init_registry();
|
|
5071
6556
|
init_fonts();
|
|
@@ -5247,13 +6732,23 @@ async function main() {
|
|
|
5247
6732
|
} else {
|
|
5248
6733
|
noInput();
|
|
5249
6734
|
}
|
|
5250
|
-
setupDom();
|
|
5251
6735
|
const isDark = opts.theme === "dark";
|
|
5252
6736
|
const paletteColors = isDark ? getPalette(opts.palette).dark : getPalette(opts.palette).light;
|
|
5253
|
-
const
|
|
6737
|
+
const chartType = parseDgmoChartType(content);
|
|
6738
|
+
const framework = chartType ? getDgmoFramework(chartType) : null;
|
|
6739
|
+
let svg;
|
|
6740
|
+
if (framework === "echart") {
|
|
6741
|
+
svg = await renderEChartsForExport(content, opts.theme, paletteColors);
|
|
6742
|
+
} else if (framework === "d3" || framework === null) {
|
|
6743
|
+
setupDom();
|
|
6744
|
+
svg = await renderD3ForExport(content, opts.theme, paletteColors);
|
|
6745
|
+
} else {
|
|
6746
|
+
console.error(`Error: Unknown chart framework "${framework}".`);
|
|
6747
|
+
process.exit(1);
|
|
6748
|
+
}
|
|
5254
6749
|
if (!svg) {
|
|
5255
6750
|
console.error(
|
|
5256
|
-
"Error: Failed to render diagram. The input may be empty, invalid, or use an unsupported chart type
|
|
6751
|
+
"Error: Failed to render diagram. The input may be empty, invalid, or use an unsupported chart type."
|
|
5257
6752
|
);
|
|
5258
6753
|
process.exit(1);
|
|
5259
6754
|
}
|