@pixerate/editor 0.164.0 → 0.166.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/dist/agent/index.cjs +54 -39
- package/dist/agent/index.js +24 -9
- package/dist/animation/index.cjs +6 -5
- package/dist/animation/index.js +5 -4
- package/dist/animation-editor/index.cjs +65 -64
- package/dist/animation-editor/index.js +8 -7
- package/dist/bridge/cli.cjs +3 -3
- package/dist/bridge/cli.js +2 -2
- package/dist/bridge/index.cjs +3 -3
- package/dist/bridge/index.js +2 -2
- package/dist/canvas/index.cjs +89 -87
- package/dist/canvas/index.js +11 -9
- package/dist/chart/controller.d.cts +60 -2
- package/dist/chart/controller.d.ts +60 -2
- package/dist/chart/index.cjs +657 -163
- package/dist/chart/index.d.cts +3 -0
- package/dist/chart/index.d.ts +3 -0
- package/dist/chart/index.js +512 -18
- package/dist/chart/layout-families.d.cts +14 -0
- package/dist/chart/layout-families.d.ts +14 -0
- package/dist/chart/layout-shared.d.cts +5 -0
- package/dist/chart/layout-shared.d.ts +5 -0
- package/dist/chart/links.d.cts +34 -0
- package/dist/chart/links.d.ts +34 -0
- package/dist/chart/mermaid.d.cts +9 -6
- package/dist/chart/mermaid.d.ts +9 -6
- package/dist/chart/race.d.cts +26 -0
- package/dist/chart/race.d.ts +26 -0
- package/dist/chart/schemas.d.cts +5 -0
- package/dist/chart/schemas.d.ts +5 -0
- package/dist/chart/spec.d.cts +3 -2
- package/dist/chart/spec.d.ts +3 -2
- package/dist/chart/style.d.cts +33 -0
- package/dist/chart/style.d.ts +33 -0
- package/dist/chart/types.d.cts +28 -2
- package/dist/chart/types.d.ts +28 -2
- package/dist/{chunk-ZVNAMTFO.cjs → chunk-3FSUJIXN.cjs} +18 -174
- package/dist/{chunk-RQCMSEM5.cjs → chunk-4QUUHTGJ.cjs} +2 -2
- package/dist/{chunk-CIPIESX4.cjs → chunk-4ZC5QJE2.cjs} +3 -3
- package/dist/{chunk-S6X2QRVU.cjs → chunk-5UEFBM2M.cjs} +1 -138
- package/dist/{chunk-5FNGLV2D.js → chunk-5UNQDC3Q.js} +18 -3
- package/dist/{chunk-YSAGBIRW.cjs → chunk-6FYIFGBW.cjs} +16 -16
- package/dist/{chunk-QYZGBVNC.js → chunk-7272D3K2.js} +10 -8
- package/dist/{chunk-462WGGXX.cjs → chunk-AMX23CSN.cjs} +27 -16
- package/dist/{chunk-3VLOOO3G.js → chunk-BDIM6NVW.js} +1 -1
- package/dist/{chunk-R3P2M7HU.cjs → chunk-BPX4Z3N3.cjs} +355 -207
- package/dist/{chunk-D54JHQK2.cjs → chunk-CHLV7UFP.cjs} +51 -51
- package/dist/{chunk-OXERWBKE.js → chunk-DFCAR7AW.js} +3 -3
- package/dist/{chunk-GZ2FM3ZB.js → chunk-DKNEQX7T.js} +6 -162
- package/dist/{chunk-JCXMJUQO.cjs → chunk-EGZM52EO.cjs} +3 -3
- package/dist/{chunk-ES3UEVUT.cjs → chunk-EUIDTZNQ.cjs} +112 -110
- package/dist/{chunk-H3OEM3Q2.js → chunk-F6R67I3A.js} +1 -1
- package/dist/{chunk-3NEZJCBY.js → chunk-FR432ROA.js} +349 -201
- package/dist/chunk-HIDFL5LQ.js +484 -0
- package/dist/{chunk-22KLZZGH.cjs → chunk-HXOBOHH2.cjs} +8 -8
- package/dist/{chunk-MKTDELCD.js → chunk-I5MRJW7B.js} +1 -1
- package/dist/{chunk-SDHZG5TF.js → chunk-ILP77UD6.js} +160 -2
- package/dist/{chunk-Z4P2RBMJ.js → chunk-ITANXMZD.js} +177 -3
- package/dist/chunk-KGRZMHZB.js +204 -0
- package/dist/{chunk-SET6KJOP.js → chunk-LPIKXPBM.js} +1 -1
- package/dist/{chunk-BQWGN5WH.js → chunk-M2TPMYJE.js} +1 -1
- package/dist/{chunk-GTELFJRE.cjs → chunk-M5GLQQWG.cjs} +2 -2
- package/dist/{chunk-4VU4CP7R.js → chunk-MHLU6GRJ.js} +1 -138
- package/dist/{chunk-NZD2MZJC.cjs → chunk-MZFZSVUJ.cjs} +38 -23
- package/dist/{chunk-DAPQGA55.js → chunk-NQQDGGXY.js} +2 -2
- package/dist/chunk-O6OGNDND.js +164 -0
- package/dist/{chunk-PA3IMPPS.cjs → chunk-OVYQAYGH.cjs} +81 -81
- package/dist/{chunk-YXOW456B.js → chunk-PPEDZ5WR.js} +1 -1
- package/dist/{chunk-24M7VD6M.cjs → chunk-S4EIAIEW.cjs} +7 -7
- package/dist/{chunk-M4MIZPQI.cjs → chunk-SJ2CPTQC.cjs} +189 -15
- package/dist/{chunk-YGG7WDWT.cjs → chunk-SZWVMB3B.cjs} +24 -18
- package/dist/chunk-TFF2GTGN.cjs +153 -0
- package/dist/chunk-TTCJPX4U.cjs +204 -0
- package/dist/{chunk-SBQVEBSU.cjs → chunk-UUUTEVX6.cjs} +242 -68
- package/dist/{chunk-WI7QJA7A.cjs → chunk-UXETV2NC.cjs} +161 -3
- package/dist/{chunk-GKNEJ6EN.cjs → chunk-V5L2G22R.cjs} +321 -107
- package/dist/{chunk-TALQONZ5.js → chunk-VB2CY6EG.js} +3 -3
- package/dist/{chunk-QY4KUDMY.js → chunk-VRXXAXUT.js} +2 -2
- package/dist/chunk-VU6WQSMB.cjs +164 -0
- package/dist/{chunk-NHLYK3AX.js → chunk-XBQX76CT.js} +182 -8
- package/dist/{chunk-5AARIS3R.cjs → chunk-XCDGM7TD.cjs} +37 -37
- package/dist/{chunk-POR54B55.js → chunk-XNU45TYA.js} +7 -1
- package/dist/{chunk-EVXQNQND.js → chunk-XZB3TJ6K.js} +16 -5
- package/dist/chunk-YBAPT7NL.js +153 -0
- package/dist/{chunk-SZA42R4N.js → chunk-YUFTVNLX.js} +230 -16
- package/dist/chunk-Z2UQYMPS.cjs +484 -0
- package/dist/color/index.cjs +5 -3
- package/dist/color/index.js +12 -10
- package/dist/diagram/index.cjs +55 -243
- package/dist/diagram/index.js +12 -200
- package/dist/export/index.cjs +7 -6
- package/dist/export/index.js +6 -5
- package/dist/extensions/index.cjs +12 -9
- package/dist/extensions/index.js +11 -8
- package/dist/gradient/index.cjs +29 -27
- package/dist/gradient/index.js +6 -4
- package/dist/image-editor/index.cjs +644 -643
- package/dist/image-editor/index.js +7 -6
- package/dist/index.cjs +21 -18
- package/dist/index.js +14 -11
- package/dist/paint-editor/index.cjs +19 -18
- package/dist/paint-editor/index.js +7 -6
- package/dist/parameters/index.cjs +2 -2
- package/dist/parameters/index.js +1 -1
- package/dist/persistence/assetWrites.d.cts +22 -0
- package/dist/persistence/assetWrites.d.ts +22 -0
- package/dist/persistence/assets.d.cts +71 -0
- package/dist/persistence/assets.d.ts +71 -0
- package/dist/persistence/documentStore.d.cts +17 -1
- package/dist/persistence/documentStore.d.ts +17 -1
- package/dist/persistence/documentTools.d.cts +5 -1
- package/dist/persistence/documentTools.d.ts +5 -1
- package/dist/persistence/editorAutosave.d.cts +7 -0
- package/dist/persistence/editorAutosave.d.ts +7 -0
- package/dist/persistence/index.cjs +141 -21
- package/dist/persistence/index.js +130 -10
- package/dist/persistence/storage.d.cts +13 -1
- package/dist/persistence/storage.d.ts +13 -1
- package/dist/spreadsheet/index.cjs +3 -3
- package/dist/spreadsheet/index.js +2 -2
- package/dist/tokens/index.cjs +11 -3
- package/dist/tokens/index.d.cts +1 -0
- package/dist/tokens/index.d.ts +1 -0
- package/dist/tokens/index.js +10 -2
- package/dist/tokens/style.d.cts +36 -0
- package/dist/tokens/style.d.ts +36 -0
- package/dist/tools/index.cjs +15 -3
- package/dist/tools/index.d.cts +2 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +14 -2
- package/dist/tools/style-tools.d.cts +19 -0
- package/dist/tools/style-tools.d.ts +19 -0
- package/dist/tools/style.d.cts +212 -0
- package/dist/tools/style.d.ts +212 -0
- package/dist/tools/view.d.cts +6 -0
- package/dist/tools/view.d.ts +6 -0
- package/dist/typography/index.cjs +8 -6
- package/dist/typography/index.js +5 -3
- package/dist/vector/index.cjs +953 -952
- package/dist/vector/index.js +7 -6
- package/dist/video-editor/index.cjs +8 -7
- package/dist/video-editor/index.js +6 -5
- package/package.json +1 -1
- package/skills/pixerate-chart/SKILL.md +20 -2
- package/dist/chunk-ERAGQL2N.cjs +0 -68
- package/dist/chunk-KMU6QJB7.js +0 -68
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
simulateColorVision
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7272D3K2.js";
|
|
4
|
+
import {
|
|
5
|
+
deltaEOK
|
|
6
|
+
} from "./chunk-YBAPT7NL.js";
|
|
4
7
|
import {
|
|
5
|
-
deltaEOK,
|
|
6
8
|
rgbToOklab
|
|
7
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MHLU6GRJ.js";
|
|
8
10
|
|
|
9
11
|
// src/chart/color.ts
|
|
10
12
|
var NAMED = {
|
|
@@ -223,8 +225,208 @@ function parseChartAmountList(text2) {
|
|
|
223
225
|
return text2.split(/[;,\s]+/).filter((t) => t !== "").map((t) => t === "-" ? null : t);
|
|
224
226
|
}
|
|
225
227
|
|
|
228
|
+
// src/chart/tree.ts
|
|
229
|
+
var MAX_CHART_TREE_DEPTH = 6;
|
|
230
|
+
var MAX_CHART_TREE_NODES = 5e3;
|
|
231
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
232
|
+
function normalizeChartTree(value, issues = [], path = "tree") {
|
|
233
|
+
if (value === void 0 || value === null) return [];
|
|
234
|
+
if (!Array.isArray(value)) {
|
|
235
|
+
issues.push({ path, message: `${path} must be an array of nodes such as {"name": "Europe", "children": [{"name": "France", "value": 40}]}.` });
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
let count = 0;
|
|
239
|
+
const read = (list, at, depth) => {
|
|
240
|
+
const out = [];
|
|
241
|
+
const names = /* @__PURE__ */ new Set();
|
|
242
|
+
list.forEach((raw, i) => {
|
|
243
|
+
const where = `${at}[${i}]`;
|
|
244
|
+
if (!isRecord(raw)) {
|
|
245
|
+
issues.push({ path: where, message: `${where} must be an object with a name and a value or children.` });
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const name = typeof raw.name === "number" ? String(raw.name) : typeof raw.name === "string" ? raw.name.trim() : "";
|
|
249
|
+
if (!name) {
|
|
250
|
+
issues.push({ path: `${where}.name`, message: `${where} needs a name.` });
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (names.has(name)) {
|
|
254
|
+
issues.push({ path: `${where}.name`, message: `"${name}" appears twice under the same group in ${at}; sibling names must be unique, so the second was left out.` });
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (++count > MAX_CHART_TREE_NODES) {
|
|
258
|
+
if (count === MAX_CHART_TREE_NODES + 1) issues.push({ path, message: `The tree has more than ${MAX_CHART_TREE_NODES} nodes; the rest were left out.` });
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
names.add(name);
|
|
262
|
+
for (const k of Object.keys(raw)) if (!["name", "value", "children", "color"].includes(k)) issues.push({ path: `${where}.${k}`, message: `${where} has no "${k}"; a node takes name, value, children and color.` });
|
|
263
|
+
const node = { name };
|
|
264
|
+
if (typeof raw.color === "string" && raw.color.trim()) node.color = raw.color.trim();
|
|
265
|
+
if (Array.isArray(raw.children) && raw.children.length) {
|
|
266
|
+
if (depth >= MAX_CHART_TREE_DEPTH) {
|
|
267
|
+
issues.push({ path: `${where}.children`, message: `${where} is more than ${MAX_CHART_TREE_DEPTH} levels deep; its children were left out.` });
|
|
268
|
+
node.value = 0;
|
|
269
|
+
} else {
|
|
270
|
+
if (raw.value !== void 0 && raw.value !== null) issues.push({ path: `${where}.value`, message: `"${name}" is a group: its value is the sum of its children, so the value given was ignored.` });
|
|
271
|
+
const children = read(raw.children, `${where}.children`, depth + 1);
|
|
272
|
+
if (children.length) node.children = children;
|
|
273
|
+
else node.value = 0;
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
if (raw.children !== void 0 && raw.children !== null && !Array.isArray(raw.children)) issues.push({ path: `${where}.children`, message: `${where}.children must be an array of nodes.` });
|
|
277
|
+
const v = parseChartValue(raw.value);
|
|
278
|
+
if (raw.value === void 0 || raw.value === null) node.value = 0;
|
|
279
|
+
else if (typeof v === "number" && v >= 0) node.value = v;
|
|
280
|
+
else {
|
|
281
|
+
issues.push({ path: `${where}.value`, message: `"${name}" needs a value of 0 or more; got ${JSON.stringify(raw.value)}.` });
|
|
282
|
+
node.value = 0;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
out.push(node);
|
|
286
|
+
});
|
|
287
|
+
return out;
|
|
288
|
+
};
|
|
289
|
+
return read(value, path, 1);
|
|
290
|
+
}
|
|
291
|
+
function chartTreeTotal(node) {
|
|
292
|
+
return node.children ? node.children.reduce((t, c) => t + chartTreeTotal(c), 0) : node.value ?? 0;
|
|
293
|
+
}
|
|
294
|
+
function chartTreeDepth(tree) {
|
|
295
|
+
return tree.reduce((d, n) => Math.max(d, 1 + (n.children ? chartTreeDepth(n.children) : 0)), 0);
|
|
296
|
+
}
|
|
297
|
+
function findChartTreeNode(tree, path) {
|
|
298
|
+
let list = tree;
|
|
299
|
+
let node;
|
|
300
|
+
for (const name of path) {
|
|
301
|
+
node = list?.find((n) => n.name === name);
|
|
302
|
+
if (!node) return void 0;
|
|
303
|
+
list = node.children;
|
|
304
|
+
}
|
|
305
|
+
return node;
|
|
306
|
+
}
|
|
307
|
+
function chartTreeToRows(tree) {
|
|
308
|
+
const out = [];
|
|
309
|
+
const walk = (list, prefix) => {
|
|
310
|
+
for (const n of list) {
|
|
311
|
+
if (n.children) walk(n.children, [...prefix, n.name]);
|
|
312
|
+
else out.push({ path: [...prefix, n.name], value: n.value ?? 0 });
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
walk(tree, []);
|
|
316
|
+
return out;
|
|
317
|
+
}
|
|
318
|
+
function chartTreeFromRows(rows, issues = []) {
|
|
319
|
+
const tree = [];
|
|
320
|
+
rows.forEach((row, r) => {
|
|
321
|
+
const path = [];
|
|
322
|
+
for (const cell of row.path) {
|
|
323
|
+
const name = cell === null || cell === void 0 ? "" : String(cell).trim();
|
|
324
|
+
if (!name) break;
|
|
325
|
+
path.push(name);
|
|
326
|
+
}
|
|
327
|
+
if (!path.length) return;
|
|
328
|
+
if (path.length > MAX_CHART_TREE_DEPTH) {
|
|
329
|
+
issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} is ${path.length} levels deep; the tree keeps at most ${MAX_CHART_TREE_DEPTH}, so the row was left out.` });
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const v = parseChartValue(row.value);
|
|
333
|
+
if (row.value !== null && row.value !== void 0 && String(row.value).trim() !== "" && !(typeof v === "number" && v >= 0)) {
|
|
334
|
+
issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} (${path.join(" / ")}) needs a value of 0 or more; got ${JSON.stringify(row.value)}. It was counted as 0.` });
|
|
335
|
+
}
|
|
336
|
+
const value = typeof v === "number" && v >= 0 ? v : 0;
|
|
337
|
+
let list = tree;
|
|
338
|
+
for (let k = 0; k < path.length; k++) {
|
|
339
|
+
const last = k === path.length - 1;
|
|
340
|
+
let node = list.find((n) => n.name === path[k]);
|
|
341
|
+
if (!node) {
|
|
342
|
+
node = last ? { name: path[k], value } : { name: path[k], children: [] };
|
|
343
|
+
list.push(node);
|
|
344
|
+
if (last) return;
|
|
345
|
+
} else if (last) {
|
|
346
|
+
issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} (${path.join(" / ")}) ${node.children ? "names a group that already has items" : "repeats an earlier row"}; it was left out.` });
|
|
347
|
+
return;
|
|
348
|
+
} else if (!node.children) {
|
|
349
|
+
issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} puts items under "${path.slice(0, k + 1).join(" / ")}", which an earlier row made an item with its own value; it was left out.` });
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
list = node.children;
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
return tree;
|
|
356
|
+
}
|
|
357
|
+
function chartTreeFromTable(rows, options = {}, issues = []) {
|
|
358
|
+
const grid = rows.filter((r) => r.some((c) => c !== null && c !== void 0 && String(c).trim() !== ""));
|
|
359
|
+
if (!grid.length) return [];
|
|
360
|
+
const width = Math.max(...grid.map((r) => r.length));
|
|
361
|
+
if (width < 2) {
|
|
362
|
+
issues.push({ path: "rows", message: "A tree table needs at least two columns: names, then values." });
|
|
363
|
+
return [];
|
|
364
|
+
}
|
|
365
|
+
const header = options.header ?? "auto";
|
|
366
|
+
const skip = header === true || header === "auto" && typeof parseChartValue(grid[0][width - 1]) !== "number";
|
|
367
|
+
const body = skip ? grid.slice(1) : grid;
|
|
368
|
+
let above = [];
|
|
369
|
+
return chartTreeFromRows(
|
|
370
|
+
body.map((r) => {
|
|
371
|
+
const cells = Array.from({ length: width - 1 }, (_, k) => r[k] === null || r[k] === void 0 ? "" : String(r[k]).trim());
|
|
372
|
+
const first = cells.findIndex((c) => c !== "");
|
|
373
|
+
if (first < 0) return { path: [], value: r[width - 1] };
|
|
374
|
+
const own = [];
|
|
375
|
+
for (const c of cells.slice(first)) {
|
|
376
|
+
if (!c) break;
|
|
377
|
+
own.push(c);
|
|
378
|
+
}
|
|
379
|
+
const path = [...above.slice(0, first), ...own];
|
|
380
|
+
above = path;
|
|
381
|
+
return { path, value: r[width - 1] };
|
|
382
|
+
}),
|
|
383
|
+
issues
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
function chartTreeFromSeries(categories, values) {
|
|
387
|
+
const seen = /* @__PURE__ */ new Set();
|
|
388
|
+
const out = [];
|
|
389
|
+
categories.forEach((c, i) => {
|
|
390
|
+
const name = c.trim() || `${i + 1}`;
|
|
391
|
+
if (seen.has(name)) return;
|
|
392
|
+
seen.add(name);
|
|
393
|
+
const v = values[i];
|
|
394
|
+
out.push({ name, value: v !== null && v !== void 0 && Number.isFinite(v) && v > 0 ? v : 0 });
|
|
395
|
+
});
|
|
396
|
+
return out;
|
|
397
|
+
}
|
|
398
|
+
function cloneChartTree(tree) {
|
|
399
|
+
return tree.map((n) => ({ ...n, ...n.children ? { children: cloneChartTree(n.children) } : {} }));
|
|
400
|
+
}
|
|
401
|
+
function chartTreeColumnLabels(depth) {
|
|
402
|
+
if (depth <= 1) return ["Item"];
|
|
403
|
+
return Array.from({ length: depth }, (_, k) => k === 0 ? "Group" : k === depth - 1 ? "Item" : `Level ${k + 1}`);
|
|
404
|
+
}
|
|
405
|
+
function setChartTreeRowName(rows, row, level, name) {
|
|
406
|
+
return rows.map((r, i) => {
|
|
407
|
+
if (i !== row) return { path: [...r.path], value: r.value };
|
|
408
|
+
const path = [...r.path];
|
|
409
|
+
while (path.length <= level) path.push("");
|
|
410
|
+
path[level] = name.trim();
|
|
411
|
+
return { path, value: r.value };
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
function setChartTreeRowValue(rows, row, value) {
|
|
415
|
+
return rows.map((r, i) => ({ path: [...r.path], value: i === row ? value : r.value }));
|
|
416
|
+
}
|
|
417
|
+
function addChartTreeRow(rows) {
|
|
418
|
+
const parent = rows.length ? rows[rows.length - 1].path.slice(0, -1) : [];
|
|
419
|
+
const taken = new Set(rows.filter((r) => r.path.length === parent.length + 1 && parent.every((p, k) => r.path[k] === p)).map((r) => r.path[parent.length]));
|
|
420
|
+
let n = rows.length + 1;
|
|
421
|
+
while (taken.has(`Item ${n}`)) n++;
|
|
422
|
+
return [...rows.map((r) => ({ path: [...r.path], value: r.value })), { path: [...parent, `Item ${n}`], value: 0 }];
|
|
423
|
+
}
|
|
424
|
+
function removeChartTreeRow(rows, row) {
|
|
425
|
+
return rows.filter((_, i) => i !== row).map((r) => ({ path: [...r.path], value: r.value }));
|
|
426
|
+
}
|
|
427
|
+
|
|
226
428
|
// src/chart/spec.ts
|
|
227
|
-
var CHART_TYPES = ["bar", "line", "area", "scatter", "bubble", "pie", "donut", "radar", "waterfall", "histogram", "box", "funnel", "treemap", "sunburst", "slope", "likert", "parliament"];
|
|
429
|
+
var CHART_TYPES = ["bar", "line", "area", "scatter", "bubble", "pie", "donut", "radar", "waterfall", "histogram", "box", "funnel", "treemap", "sunburst", "sankey", "slope", "likert", "parliament", "barrace"];
|
|
228
430
|
var CHART_MARKERS = ["circle", "square", "diamond", "triangle", "cross", "none"];
|
|
229
431
|
var CHART_TRENDLINE_TYPES = ["linear", "exponential", "logarithmic", "power", "polynomial", "moving-average"];
|
|
230
432
|
var CHART_LABEL_POSITIONS = ["auto", "inside", "outside", "center"];
|
|
@@ -236,6 +438,7 @@ var CHART_CURVES = ["linear", "smooth", "step"];
|
|
|
236
438
|
var CHART_RADAR_GRATICULES = ["polygon", "circle"];
|
|
237
439
|
var CHART_LIKERT_NEUTRALS = ["split", "separate"];
|
|
238
440
|
var CHART_LIKERT_SORTS = ["none", "positive", "negative"];
|
|
441
|
+
var CHART_SANKEY_LINK_COLORS = ["source", "target", "gray"];
|
|
239
442
|
var CHART_REFERENCE_AXES = ["y", "y2", "x"];
|
|
240
443
|
var CHART_ORIENTATIONS = ["vertical", "horizontal"];
|
|
241
444
|
var CHART_LEGEND_POSITIONS = ["top", "bottom", "left", "right"];
|
|
@@ -257,7 +460,9 @@ var CHART_TYPE_LABELS = {
|
|
|
257
460
|
slope: "Slope",
|
|
258
461
|
likert: "Likert",
|
|
259
462
|
parliament: "Parliament",
|
|
260
|
-
sunburst: "Sunburst"
|
|
463
|
+
sunburst: "Sunburst",
|
|
464
|
+
sankey: "Sankey",
|
|
465
|
+
barrace: "Bar race"
|
|
261
466
|
};
|
|
262
467
|
var DEFAULT_CHART_PALETTE = ["#4269d0", "#efb118", "#ff725c", "#6cc5b0", "#3ca951", "#ff8ab7", "#a463f2", "#97bbf5", "#9c6b4e", "#9498a0"];
|
|
263
468
|
var DEFAULT_CHART_THEME = {
|
|
@@ -308,6 +513,8 @@ function chartFamily(spec) {
|
|
|
308
513
|
case "likert":
|
|
309
514
|
case "parliament":
|
|
310
515
|
case "sunburst":
|
|
516
|
+
case "sankey":
|
|
517
|
+
case "barrace":
|
|
311
518
|
return spec.type;
|
|
312
519
|
case "bubble":
|
|
313
520
|
return "xy";
|
|
@@ -316,7 +523,7 @@ function chartFamily(spec) {
|
|
|
316
523
|
}
|
|
317
524
|
}
|
|
318
525
|
function chartColorsByCategory(type) {
|
|
319
|
-
return type === "pie" || type === "donut" || type === "funnel" || type === "treemap" || type === "slope" || type === "parliament" || type === "sunburst";
|
|
526
|
+
return type === "pie" || type === "donut" || type === "funnel" || type === "treemap" || type === "slope" || type === "parliament" || type === "sunburst" || type === "sankey";
|
|
320
527
|
}
|
|
321
528
|
function chartSpecColorsByCategory(spec) {
|
|
322
529
|
return chartColorsByCategory(spec.type) || chartVariesColors(spec);
|
|
@@ -390,7 +597,7 @@ function parseChartValue(input) {
|
|
|
390
597
|
if (negative) value = -value;
|
|
391
598
|
return Number.isFinite(value) ? value : void 0;
|
|
392
599
|
}
|
|
393
|
-
var
|
|
600
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
394
601
|
function pick(value, options, fallback, path, issues) {
|
|
395
602
|
if (value === void 0) return fallback;
|
|
396
603
|
if (typeof value === "string" && options.includes(value)) return value;
|
|
@@ -419,7 +626,7 @@ function bound(value, path, issues) {
|
|
|
419
626
|
}
|
|
420
627
|
function normalizeFormat(value, path, issues) {
|
|
421
628
|
if (value === void 0 || value === null) return {};
|
|
422
|
-
if (!
|
|
629
|
+
if (!isRecord2(value)) {
|
|
423
630
|
issues.push({ path, message: `${path} must be an object.` });
|
|
424
631
|
return {};
|
|
425
632
|
}
|
|
@@ -437,7 +644,7 @@ function normalizeFormat(value, path, issues) {
|
|
|
437
644
|
}
|
|
438
645
|
function normalizeChartAxis(value, fallback, path, issues = []) {
|
|
439
646
|
if (value === void 0) return { ...fallback, format: { ...fallback.format } };
|
|
440
|
-
if (!
|
|
647
|
+
if (!isRecord2(value)) {
|
|
441
648
|
issues.push({ path, message: `${path} must be an object.` });
|
|
442
649
|
return { ...fallback, format: { ...fallback.format } };
|
|
443
650
|
}
|
|
@@ -487,7 +694,7 @@ function normalizeChartAxis(value, fallback, path, issues = []) {
|
|
|
487
694
|
function normalizeLegend(value, issues) {
|
|
488
695
|
const fallback = { show: true, position: "bottom" };
|
|
489
696
|
if (value === void 0) return fallback;
|
|
490
|
-
if (!
|
|
697
|
+
if (!isRecord2(value)) {
|
|
491
698
|
issues.push({ path: "legend", message: "legend must be an object." });
|
|
492
699
|
return fallback;
|
|
493
700
|
}
|
|
@@ -517,7 +724,7 @@ function normalizeChartValues(value, length, path, issues = []) {
|
|
|
517
724
|
}
|
|
518
725
|
function normalizeSeries(value, length, used, index, issues) {
|
|
519
726
|
const path = `series[${index}]`;
|
|
520
|
-
if (!
|
|
727
|
+
if (!isRecord2(value)) {
|
|
521
728
|
issues.push({ path, message: `${path} must be an object with a name and values.` });
|
|
522
729
|
return null;
|
|
523
730
|
}
|
|
@@ -607,7 +814,7 @@ var finiteNumber = (value) => {
|
|
|
607
814
|
return typeof n === "number" && Number.isFinite(n) ? n : null;
|
|
608
815
|
};
|
|
609
816
|
function normalizeChartReferenceLine(value, path, issues = []) {
|
|
610
|
-
if (!
|
|
817
|
+
if (!isRecord2(value)) {
|
|
611
818
|
issues.push({ path, message: `${path} must be an object with a value, e.g. {"value": 100, "label": "Target"}.` });
|
|
612
819
|
return null;
|
|
613
820
|
}
|
|
@@ -620,7 +827,7 @@ function normalizeChartReferenceLine(value, path, issues = []) {
|
|
|
620
827
|
return { value: at, ...referenceCommon(value, path, issues) };
|
|
621
828
|
}
|
|
622
829
|
function normalizeChartReferenceBand(value, path, issues = []) {
|
|
623
|
-
if (!
|
|
830
|
+
if (!isRecord2(value)) {
|
|
624
831
|
issues.push({ path, message: `${path} must be an object with from and to, e.g. {"from": 80, "to": 120, "label": "Target range"}.` });
|
|
625
832
|
return null;
|
|
626
833
|
}
|
|
@@ -634,7 +841,7 @@ function normalizeChartReferenceBand(value, path, issues = []) {
|
|
|
634
841
|
return { from: Math.min(from, to), to: Math.max(from, to), ...referenceCommon(value, path, issues) };
|
|
635
842
|
}
|
|
636
843
|
function normalizeChartErrorBars(value, length, path, issues = []) {
|
|
637
|
-
if (!
|
|
844
|
+
if (!isRecord2(value)) {
|
|
638
845
|
issues.push({ path, message: `${path} must be an object with a type (${CHART_ERROR_BAR_TYPES.join(", ")}), or null for none.` });
|
|
639
846
|
return null;
|
|
640
847
|
}
|
|
@@ -667,7 +874,7 @@ function normalizeChartErrorBars(value, length, path, issues = []) {
|
|
|
667
874
|
return issues.length > before ? null : bars;
|
|
668
875
|
}
|
|
669
876
|
function normalizeChartTrendline(value, path, issues = []) {
|
|
670
|
-
if (!
|
|
877
|
+
if (!isRecord2(value)) {
|
|
671
878
|
issues.push({ path, message: `${path} must be an object with a type.` });
|
|
672
879
|
return null;
|
|
673
880
|
}
|
|
@@ -706,8 +913,8 @@ function normalizeChartTrendline(value, path, issues = []) {
|
|
|
706
913
|
}
|
|
707
914
|
function normalizeChartSpec(input) {
|
|
708
915
|
const issues = [];
|
|
709
|
-
const value =
|
|
710
|
-
if (input !== void 0 && !
|
|
916
|
+
const value = isRecord2(input) ? input : {};
|
|
917
|
+
if (input !== void 0 && !isRecord2(input)) issues.push({ path: "", message: "A chart must be an object." });
|
|
711
918
|
const type = pick(value.type, CHART_TYPES, "bar", "type", issues);
|
|
712
919
|
const rawSeries = Array.isArray(value.series) ? value.series : [];
|
|
713
920
|
if (value.series !== void 0 && !Array.isArray(value.series)) issues.push({ path: "series", message: "series must be an array." });
|
|
@@ -715,16 +922,21 @@ function normalizeChartSpec(input) {
|
|
|
715
922
|
if (Array.isArray(value.categories)) categories = value.categories.map((c, i) => text(c, "", `categories[${i}]`, issues));
|
|
716
923
|
else {
|
|
717
924
|
if (value.categories !== void 0) issues.push({ path: "categories", message: "categories must be an array of labels." });
|
|
718
|
-
const longest = rawSeries.reduce((n, s) => Math.max(n,
|
|
925
|
+
const longest = rawSeries.reduce((n, s) => Math.max(n, isRecord2(s) && Array.isArray(s.values) ? s.values.length : 0), 0);
|
|
719
926
|
categories = Array.from({ length: longest }, (_, i) => `${i + 1}`);
|
|
720
927
|
}
|
|
721
928
|
const tree = normalizeChartTree(value.tree, issues);
|
|
722
|
-
|
|
929
|
+
const links = normalizeChartLinks(value.links, issues);
|
|
930
|
+
const sankeyNodes = chartSankeyNodes(links, Array.isArray(value.sankeyNodes) ? value.sankeyNodes.map(String) : [], issues);
|
|
931
|
+
if (value.sankeyNodes !== void 0 && value.sankeyNodes !== null && !Array.isArray(value.sankeyNodes)) issues.push({ path: "sankeyNodes", message: "sankeyNodes must be an array of node names." });
|
|
932
|
+
if (tree.length && links.length) issues.push({ path: "links", message: "A chart holds either grouped data (tree) or flows (links), not both; the flows were used." });
|
|
933
|
+
if (links.length) categories = [...sankeyNodes];
|
|
934
|
+
else if (tree.length) categories = tree.map((n) => n.name);
|
|
723
935
|
const used = /* @__PURE__ */ new Set();
|
|
724
936
|
const series = rawSeries.map((s, i) => normalizeSeries(s, categories.length, used, i, issues)).filter((s) => s !== null);
|
|
725
|
-
if (tree.length) {
|
|
937
|
+
if (tree.length || links.length) {
|
|
726
938
|
if (!series.length) series.push({ id: uniqueChartSeriesId(series), name: "Values", values: [] });
|
|
727
|
-
series[0] = { ...series[0], values: tree.map(chartTreeTotal) };
|
|
939
|
+
series[0] = { ...series[0], values: links.length ? chartSankeyNodeValues(links, sankeyNodes) : tree.map(chartTreeTotal) };
|
|
728
940
|
}
|
|
729
941
|
let palette = [...DEFAULT_CHART_PALETTE];
|
|
730
942
|
if (Array.isArray(value.palette)) {
|
|
@@ -774,6 +986,12 @@ function normalizeChartSpec(input) {
|
|
|
774
986
|
if (Number.isInteger(m) && m >= 1) parliamentMajority = m;
|
|
775
987
|
else issues.push({ path: "parliamentMajority", message: "parliamentMajority must be a whole number of seats, 1 or more, or null for half plus one." });
|
|
776
988
|
}
|
|
989
|
+
let raceTopN = 10;
|
|
990
|
+
if (value.raceTopN !== void 0) {
|
|
991
|
+
const n = Number(value.raceTopN);
|
|
992
|
+
if (value.raceTopN !== null && Number.isInteger(n) && n >= 1 && n <= 30) raceTopN = n;
|
|
993
|
+
else issues.push({ path: "raceTopN", message: "raceTopN must be a whole number of bars from 1 to 30." });
|
|
994
|
+
}
|
|
777
995
|
let radarTicks = null;
|
|
778
996
|
if (value.radarTicks !== void 0 && value.radarTicks !== null) {
|
|
779
997
|
const t = Number(value.radarTicks);
|
|
@@ -815,8 +1033,12 @@ function normalizeChartSpec(input) {
|
|
|
815
1033
|
parliamentArc: fraction("parliamentArc", 180, 90, 360),
|
|
816
1034
|
parliamentMajority,
|
|
817
1035
|
parliamentLabel: text(value.parliamentLabel, "", "parliamentLabel", issues),
|
|
818
|
-
tree,
|
|
819
|
-
sunburstLabel: text(value.sunburstLabel, "", "sunburstLabel", issues)
|
|
1036
|
+
tree: links.length ? [] : tree,
|
|
1037
|
+
sunburstLabel: text(value.sunburstLabel, "", "sunburstLabel", issues),
|
|
1038
|
+
links,
|
|
1039
|
+
sankeyNodes: links.length ? sankeyNodes : [],
|
|
1040
|
+
sankeyLinkColor: pick(value.sankeyLinkColor, CHART_SANKEY_LINK_COLORS, "source", "sankeyLinkColor", issues),
|
|
1041
|
+
raceTopN
|
|
820
1042
|
};
|
|
821
1043
|
if (spec.stacking === "percent")
|
|
822
1044
|
for (const name of ["yAxis", "yAxis2"]) {
|
|
@@ -840,7 +1062,9 @@ function cloneChartSpec(spec) {
|
|
|
840
1062
|
highlight: [...spec.highlight],
|
|
841
1063
|
referenceLines: spec.referenceLines.map((l) => ({ ...l })),
|
|
842
1064
|
referenceBands: spec.referenceBands.map((b) => ({ ...b })),
|
|
843
|
-
tree: cloneChartTree(spec.tree)
|
|
1065
|
+
tree: cloneChartTree(spec.tree),
|
|
1066
|
+
links: spec.links.map((l) => ({ ...l })),
|
|
1067
|
+
sankeyNodes: [...spec.sankeyNodes]
|
|
844
1068
|
};
|
|
845
1069
|
}
|
|
846
1070
|
function cloneChartSeries(series) {
|
|
@@ -909,204 +1133,120 @@ function chartColorInputValue(color) {
|
|
|
909
1133
|
return "#000000";
|
|
910
1134
|
}
|
|
911
1135
|
|
|
912
|
-
// src/chart/
|
|
913
|
-
var
|
|
914
|
-
var
|
|
915
|
-
var
|
|
916
|
-
function
|
|
1136
|
+
// src/chart/links.ts
|
|
1137
|
+
var MAX_CHART_LINKS = 2e3;
|
|
1138
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1139
|
+
var nameOf = (value) => typeof value === "number" ? String(value) : typeof value === "string" ? value.trim() : "";
|
|
1140
|
+
function normalizeChartLinks(value, issues = [], path = "links") {
|
|
917
1141
|
if (value === void 0 || value === null) return [];
|
|
918
1142
|
if (!Array.isArray(value)) {
|
|
919
|
-
issues.push({ path, message: `${path} must be an array of
|
|
1143
|
+
issues.push({ path, message: `${path} must be an array of flows such as {"source": "Website", "target": "Signup", "value": 120}.` });
|
|
920
1144
|
return [];
|
|
921
1145
|
}
|
|
922
|
-
let count = 0;
|
|
923
|
-
const read = (list, at, depth) => {
|
|
924
|
-
const out = [];
|
|
925
|
-
const names = /* @__PURE__ */ new Set();
|
|
926
|
-
list.forEach((raw, i) => {
|
|
927
|
-
const where = `${at}[${i}]`;
|
|
928
|
-
if (!isRecord2(raw)) {
|
|
929
|
-
issues.push({ path: where, message: `${where} must be an object with a name and a value or children.` });
|
|
930
|
-
return;
|
|
931
|
-
}
|
|
932
|
-
const name = typeof raw.name === "number" ? String(raw.name) : typeof raw.name === "string" ? raw.name.trim() : "";
|
|
933
|
-
if (!name) {
|
|
934
|
-
issues.push({ path: `${where}.name`, message: `${where} needs a name.` });
|
|
935
|
-
return;
|
|
936
|
-
}
|
|
937
|
-
if (names.has(name)) {
|
|
938
|
-
issues.push({ path: `${where}.name`, message: `"${name}" appears twice under the same group in ${at}; sibling names must be unique, so the second was left out.` });
|
|
939
|
-
return;
|
|
940
|
-
}
|
|
941
|
-
if (++count > MAX_CHART_TREE_NODES) {
|
|
942
|
-
if (count === MAX_CHART_TREE_NODES + 1) issues.push({ path, message: `The tree has more than ${MAX_CHART_TREE_NODES} nodes; the rest were left out.` });
|
|
943
|
-
return;
|
|
944
|
-
}
|
|
945
|
-
names.add(name);
|
|
946
|
-
for (const k of Object.keys(raw)) if (!["name", "value", "children", "color"].includes(k)) issues.push({ path: `${where}.${k}`, message: `${where} has no "${k}"; a node takes name, value, children and color.` });
|
|
947
|
-
const node = { name };
|
|
948
|
-
if (typeof raw.color === "string" && raw.color.trim()) node.color = raw.color.trim();
|
|
949
|
-
if (Array.isArray(raw.children) && raw.children.length) {
|
|
950
|
-
if (depth >= MAX_CHART_TREE_DEPTH) {
|
|
951
|
-
issues.push({ path: `${where}.children`, message: `${where} is more than ${MAX_CHART_TREE_DEPTH} levels deep; its children were left out.` });
|
|
952
|
-
node.value = 0;
|
|
953
|
-
} else {
|
|
954
|
-
if (raw.value !== void 0 && raw.value !== null) issues.push({ path: `${where}.value`, message: `"${name}" is a group: its value is the sum of its children, so the value given was ignored.` });
|
|
955
|
-
const children = read(raw.children, `${where}.children`, depth + 1);
|
|
956
|
-
if (children.length) node.children = children;
|
|
957
|
-
else node.value = 0;
|
|
958
|
-
}
|
|
959
|
-
} else {
|
|
960
|
-
if (raw.children !== void 0 && raw.children !== null && !Array.isArray(raw.children)) issues.push({ path: `${where}.children`, message: `${where}.children must be an array of nodes.` });
|
|
961
|
-
const v = parseChartValue(raw.value);
|
|
962
|
-
if (raw.value === void 0 || raw.value === null) node.value = 0;
|
|
963
|
-
else if (typeof v === "number" && v >= 0) node.value = v;
|
|
964
|
-
else {
|
|
965
|
-
issues.push({ path: `${where}.value`, message: `"${name}" needs a value of 0 or more; got ${JSON.stringify(raw.value)}.` });
|
|
966
|
-
node.value = 0;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
out.push(node);
|
|
970
|
-
});
|
|
971
|
-
return out;
|
|
972
|
-
};
|
|
973
|
-
return read(value, path, 1);
|
|
974
|
-
}
|
|
975
|
-
function chartTreeTotal(node) {
|
|
976
|
-
return node.children ? node.children.reduce((t, c) => t + chartTreeTotal(c), 0) : node.value ?? 0;
|
|
977
|
-
}
|
|
978
|
-
function chartTreeDepth(tree) {
|
|
979
|
-
return tree.reduce((d, n) => Math.max(d, 1 + (n.children ? chartTreeDepth(n.children) : 0)), 0);
|
|
980
|
-
}
|
|
981
|
-
function findChartTreeNode(tree, path) {
|
|
982
|
-
let list = tree;
|
|
983
|
-
let node;
|
|
984
|
-
for (const name of path) {
|
|
985
|
-
node = list?.find((n) => n.name === name);
|
|
986
|
-
if (!node) return void 0;
|
|
987
|
-
list = node.children;
|
|
988
|
-
}
|
|
989
|
-
return node;
|
|
990
|
-
}
|
|
991
|
-
function chartTreeToRows(tree) {
|
|
992
1146
|
const out = [];
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1147
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1148
|
+
const next = /* @__PURE__ */ new Map();
|
|
1149
|
+
const reaches = (to, from) => {
|
|
1150
|
+
const stack = [to];
|
|
1151
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1152
|
+
while (stack.length) {
|
|
1153
|
+
const n = stack.pop();
|
|
1154
|
+
if (n === from) return true;
|
|
1155
|
+
if (visited.has(n)) continue;
|
|
1156
|
+
visited.add(n);
|
|
1157
|
+
for (const m of next.get(n) ?? []) stack.push(m);
|
|
997
1158
|
}
|
|
1159
|
+
return false;
|
|
998
1160
|
};
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
rows.forEach((row, r) => {
|
|
1005
|
-
const path = [];
|
|
1006
|
-
for (const cell of row.path) {
|
|
1007
|
-
const name = cell === null || cell === void 0 ? "" : String(cell).trim();
|
|
1008
|
-
if (!name) break;
|
|
1009
|
-
path.push(name);
|
|
1161
|
+
value.forEach((raw, i) => {
|
|
1162
|
+
const where = `${path}[${i}]`;
|
|
1163
|
+
if (out.length >= MAX_CHART_LINKS) {
|
|
1164
|
+
if (out.length === MAX_CHART_LINKS && i === MAX_CHART_LINKS) issues.push({ path, message: `There are more than ${MAX_CHART_LINKS} flows; the rest were left out.` });
|
|
1165
|
+
return;
|
|
1010
1166
|
}
|
|
1011
|
-
if (!
|
|
1012
|
-
|
|
1013
|
-
issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} is ${path.length} levels deep; the tree keeps at most ${MAX_CHART_TREE_DEPTH}, so the row was left out.` });
|
|
1167
|
+
if (!isRecord3(raw)) {
|
|
1168
|
+
issues.push({ path: where, message: `${where} must be an object with source, target and value.` });
|
|
1014
1169
|
return;
|
|
1015
1170
|
}
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
|
|
1171
|
+
for (const k of Object.keys(raw)) if (!["source", "target", "value", "color"].includes(k)) issues.push({ path: `${where}.${k}`, message: `${where} has no "${k}"; a flow takes source, target, value and color.` });
|
|
1172
|
+
const source = nameOf(raw.source);
|
|
1173
|
+
const target = nameOf(raw.target);
|
|
1174
|
+
if (!source || !target) {
|
|
1175
|
+
issues.push({ path: where, message: `${where} needs a source and a target (node names).` });
|
|
1176
|
+
return;
|
|
1019
1177
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} puts items under "${path.slice(0, k + 1).join(" / ")}", which an earlier row made an item with its own value; it was left out.` });
|
|
1034
|
-
return;
|
|
1035
|
-
}
|
|
1036
|
-
list = node.children;
|
|
1178
|
+
if (source === target) {
|
|
1179
|
+
issues.push({ path: where, message: `${where} goes from "${source}" to itself; a flow must join two different nodes.` });
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
const v = parseChartValue(raw.value);
|
|
1183
|
+
if (!(typeof v === "number" && v > 0)) {
|
|
1184
|
+
issues.push({ path: `${where}.value`, message: `The flow from "${source}" to "${target}" needs a value above 0; got ${JSON.stringify(raw.value)}.` });
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
const key2 = `${source}\0${target}`;
|
|
1188
|
+
if (seen.has(key2)) {
|
|
1189
|
+
issues.push({ path: where, message: `The flow from "${source}" to "${target}" appears twice; add the values into one flow. The second was left out.` });
|
|
1190
|
+
return;
|
|
1037
1191
|
}
|
|
1192
|
+
if (reaches(target, source)) {
|
|
1193
|
+
issues.push({ path: where, message: `The flow from "${source}" to "${target}" would close a loop (${target} already leads back to ${source}); flows must only go forwards, so it was left out.` });
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
seen.add(key2);
|
|
1197
|
+
if (!next.has(source)) next.set(source, /* @__PURE__ */ new Set());
|
|
1198
|
+
next.get(source).add(target);
|
|
1199
|
+
const link = { source, target, value: v };
|
|
1200
|
+
if (typeof raw.color === "string" && raw.color.trim()) link.color = raw.color.trim();
|
|
1201
|
+
out.push(link);
|
|
1038
1202
|
});
|
|
1039
|
-
return
|
|
1203
|
+
return out;
|
|
1040
1204
|
}
|
|
1041
|
-
function
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
const
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
const header = options.header ?? "auto";
|
|
1050
|
-
const skip = header === true || header === "auto" && typeof parseChartValue(grid[0][width - 1]) !== "number";
|
|
1051
|
-
const body = skip ? grid.slice(1) : grid;
|
|
1052
|
-
let above = [];
|
|
1053
|
-
return chartTreeFromRows(
|
|
1054
|
-
body.map((r) => {
|
|
1055
|
-
const cells = Array.from({ length: width - 1 }, (_, k) => r[k] === null || r[k] === void 0 ? "" : String(r[k]).trim());
|
|
1056
|
-
const first = cells.findIndex((c) => c !== "");
|
|
1057
|
-
if (first < 0) return { path: [], value: r[width - 1] };
|
|
1058
|
-
const own = [];
|
|
1059
|
-
for (const c of cells.slice(first)) {
|
|
1060
|
-
if (!c) break;
|
|
1061
|
-
own.push(c);
|
|
1205
|
+
function chartSankeyNodes(links, order = [], issues = []) {
|
|
1206
|
+
const used = /* @__PURE__ */ new Set();
|
|
1207
|
+
const named = [];
|
|
1208
|
+
for (const l of links)
|
|
1209
|
+
for (const n of [l.source, l.target])
|
|
1210
|
+
if (!used.has(n)) {
|
|
1211
|
+
used.add(n);
|
|
1212
|
+
named.push(n);
|
|
1062
1213
|
}
|
|
1063
|
-
const path = [...above.slice(0, first), ...own];
|
|
1064
|
-
above = path;
|
|
1065
|
-
return { path, value: r[width - 1] };
|
|
1066
|
-
}),
|
|
1067
|
-
issues
|
|
1068
|
-
);
|
|
1069
|
-
}
|
|
1070
|
-
function chartTreeFromSeries(categories, values) {
|
|
1071
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1072
1214
|
const out = [];
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
out.
|
|
1079
|
-
}
|
|
1215
|
+
for (const n of order) {
|
|
1216
|
+
if (!used.has(n)) {
|
|
1217
|
+
if (links.length) issues.push({ path: "sankeyNodes", message: `sankeyNodes names "${n}", which no flow uses; it was left out.` });
|
|
1218
|
+
continue;
|
|
1219
|
+
}
|
|
1220
|
+
if (!out.includes(n)) out.push(n);
|
|
1221
|
+
}
|
|
1222
|
+
for (const n of named) if (!out.includes(n)) out.push(n);
|
|
1080
1223
|
return out;
|
|
1081
1224
|
}
|
|
1082
|
-
function
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
return rows.map((r, i) => {
|
|
1091
|
-
if (i !== row) return { path: [...r.path], value: r.value };
|
|
1092
|
-
const path = [...r.path];
|
|
1093
|
-
while (path.length <= level) path.push("");
|
|
1094
|
-
path[level] = name.trim();
|
|
1095
|
-
return { path, value: r.value };
|
|
1096
|
-
});
|
|
1225
|
+
function chartSankeyNodeValues(links, nodes) {
|
|
1226
|
+
const inflow = /* @__PURE__ */ new Map();
|
|
1227
|
+
const outflow = /* @__PURE__ */ new Map();
|
|
1228
|
+
for (const l of links) {
|
|
1229
|
+
outflow.set(l.source, (outflow.get(l.source) ?? 0) + l.value);
|
|
1230
|
+
inflow.set(l.target, (inflow.get(l.target) ?? 0) + l.value);
|
|
1231
|
+
}
|
|
1232
|
+
return nodes.map((n) => Math.max(inflow.get(n) ?? 0, outflow.get(n) ?? 0));
|
|
1097
1233
|
}
|
|
1098
|
-
function
|
|
1099
|
-
|
|
1234
|
+
function chartLinksFromTable(rows, options = {}) {
|
|
1235
|
+
const grid = rows.filter((r) => r.some((c) => c !== null && c !== void 0 && String(c).trim() !== ""));
|
|
1236
|
+
if (!grid.length) return [];
|
|
1237
|
+
const header = options.header ?? "auto";
|
|
1238
|
+
const skip = header === true || header === "auto" && typeof parseChartValue(grid[0][2]) !== "number";
|
|
1239
|
+
return (skip ? grid.slice(1) : grid).map((r) => ({ source: r[0], target: r[1], value: r[2] }));
|
|
1100
1240
|
}
|
|
1101
|
-
function
|
|
1102
|
-
|
|
1103
|
-
const taken = new Set(rows.filter((r) => r.path.length === parent.length + 1 && parent.every((p, k) => r.path[k] === p)).map((r) => r.path[parent.length]));
|
|
1104
|
-
let n = rows.length + 1;
|
|
1105
|
-
while (taken.has(`Item ${n}`)) n++;
|
|
1106
|
-
return [...rows.map((r) => ({ path: [...r.path], value: r.value })), { path: [...parent, `Item ${n}`], value: 0 }];
|
|
1241
|
+
function setChartLinkField(links, index, field, text2) {
|
|
1242
|
+
return links.map((l, i) => i === index ? { ...l, [field]: field === "value" ? text2 : text2.trim() } : { ...l });
|
|
1107
1243
|
}
|
|
1108
|
-
function
|
|
1109
|
-
|
|
1244
|
+
function addChartLink(links) {
|
|
1245
|
+
const last = links[links.length - 1];
|
|
1246
|
+
const names = new Set(links.flatMap((l) => [l.source, l.target]));
|
|
1247
|
+
let n = names.size + 1;
|
|
1248
|
+
while (names.has(`Step ${n}`)) n++;
|
|
1249
|
+
return [...links.map((l) => ({ ...l })), { source: last?.target ?? "Start", target: `Step ${n}`, value: last?.value ?? 1 }];
|
|
1110
1250
|
}
|
|
1111
1251
|
|
|
1112
1252
|
export {
|
|
@@ -1131,6 +1271,13 @@ export {
|
|
|
1131
1271
|
chartErrorBarsOfType,
|
|
1132
1272
|
formatChartAmountList,
|
|
1133
1273
|
parseChartAmountList,
|
|
1274
|
+
MAX_CHART_LINKS,
|
|
1275
|
+
normalizeChartLinks,
|
|
1276
|
+
chartSankeyNodes,
|
|
1277
|
+
chartSankeyNodeValues,
|
|
1278
|
+
chartLinksFromTable,
|
|
1279
|
+
setChartLinkField,
|
|
1280
|
+
addChartLink,
|
|
1134
1281
|
MAX_CHART_TREE_DEPTH,
|
|
1135
1282
|
MAX_CHART_TREE_NODES,
|
|
1136
1283
|
normalizeChartTree,
|
|
@@ -1159,6 +1306,7 @@ export {
|
|
|
1159
1306
|
CHART_RADAR_GRATICULES,
|
|
1160
1307
|
CHART_LIKERT_NEUTRALS,
|
|
1161
1308
|
CHART_LIKERT_SORTS,
|
|
1309
|
+
CHART_SANKEY_LINK_COLORS,
|
|
1162
1310
|
CHART_REFERENCE_AXES,
|
|
1163
1311
|
CHART_ORIENTATIONS,
|
|
1164
1312
|
CHART_LEGEND_POSITIONS,
|