@pixerate/editor 0.164.0 → 0.165.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 +31 -2
- package/dist/chart/controller.d.ts +31 -2
- package/dist/chart/index.cjs +504 -132
- package/dist/chart/index.d.cts +2 -0
- package/dist/chart/index.d.ts +2 -0
- package/dist/chart/index.js +386 -14
- package/dist/chart/layout-families.d.cts +7 -0
- package/dist/chart/layout-families.d.ts +7 -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/schemas.d.cts +3 -0
- package/dist/chart/schemas.d.ts +3 -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 +24 -2
- package/dist/chart/types.d.ts +24 -2
- package/dist/{chunk-TALQONZ5.js → chunk-2J5VLJHC.js} +3 -3
- package/dist/{chunk-3NEZJCBY.js → chunk-2ZRN43IN.js} +340 -201
- 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-YSAGBIRW.cjs → chunk-64VWWR5U.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-POR54B55.js → chunk-CRFSQWKQ.js} +4 -1
- 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-5FNGLV2D.js → chunk-FEU2XVQH.js} +15 -3
- package/dist/{chunk-SZA42R4N.js → chunk-FRBRLKNQ.js} +154 -16
- package/dist/chunk-HIDFL5LQ.js +484 -0
- package/dist/{chunk-MKTDELCD.js → chunk-I5MRJW7B.js} +1 -1
- package/dist/{chunk-SDHZG5TF.js → chunk-ILP77UD6.js} +160 -2
- package/dist/{chunk-GKNEJ6EN.cjs → chunk-IMRXC7OA.cjs} +241 -103
- 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-D54JHQK2.cjs → chunk-MKXK6E4O.cjs} +51 -51
- package/dist/{chunk-DAPQGA55.js → chunk-N7NBGT2Y.js} +2 -2
- package/dist/chunk-O6OGNDND.js +164 -0
- package/dist/{chunk-22KLZZGH.cjs → chunk-ODFCS7HF.cjs} +8 -8
- package/dist/{chunk-PA3IMPPS.cjs → chunk-OVYQAYGH.cjs} +81 -81
- package/dist/{chunk-YXOW456B.js → chunk-PPEDZ5WR.js} +1 -1
- package/dist/{chunk-YGG7WDWT.cjs → chunk-Q25WAFTD.cjs} +21 -18
- package/dist/{chunk-SBQVEBSU.cjs → chunk-QO2T6KA5.cjs} +165 -67
- package/dist/{chunk-24M7VD6M.cjs → chunk-S4EIAIEW.cjs} +7 -7
- package/dist/{chunk-M4MIZPQI.cjs → chunk-SJ2CPTQC.cjs} +189 -15
- package/dist/chunk-TFF2GTGN.cjs +153 -0
- package/dist/chunk-TTCJPX4U.cjs +204 -0
- package/dist/{chunk-NZD2MZJC.cjs → chunk-TVQW26AV.cjs} +35 -23
- package/dist/{chunk-R3P2M7HU.cjs → chunk-UVXTOZ2B.cjs} +346 -207
- package/dist/{chunk-WI7QJA7A.cjs → chunk-UXETV2NC.cjs} +161 -3
- package/dist/{chunk-Z4P2RBMJ.js → chunk-V5GSY3NC.js} +101 -3
- package/dist/{chunk-QY4KUDMY.js → chunk-VRXXAXUT.js} +2 -2
- package/dist/chunk-VU6WQSMB.cjs +164 -0
- package/dist/{chunk-OXERWBKE.js → chunk-X5W645RI.js} +3 -3
- package/dist/{chunk-NHLYK3AX.js → chunk-XBQX76CT.js} +182 -8
- package/dist/{chunk-5AARIS3R.cjs → chunk-XCDGM7TD.cjs} +37 -37
- package/dist/{chunk-EVXQNQND.js → chunk-XZB3TJ6K.js} +16 -5
- package/dist/chunk-YBAPT7NL.js +153 -0
- 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/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"];
|
|
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,8 @@ 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"
|
|
261
465
|
};
|
|
262
466
|
var DEFAULT_CHART_PALETTE = ["#4269d0", "#efb118", "#ff725c", "#6cc5b0", "#3ca951", "#ff8ab7", "#a463f2", "#97bbf5", "#9c6b4e", "#9498a0"];
|
|
263
467
|
var DEFAULT_CHART_THEME = {
|
|
@@ -308,6 +512,7 @@ function chartFamily(spec) {
|
|
|
308
512
|
case "likert":
|
|
309
513
|
case "parliament":
|
|
310
514
|
case "sunburst":
|
|
515
|
+
case "sankey":
|
|
311
516
|
return spec.type;
|
|
312
517
|
case "bubble":
|
|
313
518
|
return "xy";
|
|
@@ -316,7 +521,7 @@ function chartFamily(spec) {
|
|
|
316
521
|
}
|
|
317
522
|
}
|
|
318
523
|
function chartColorsByCategory(type) {
|
|
319
|
-
return type === "pie" || type === "donut" || type === "funnel" || type === "treemap" || type === "slope" || type === "parliament" || type === "sunburst";
|
|
524
|
+
return type === "pie" || type === "donut" || type === "funnel" || type === "treemap" || type === "slope" || type === "parliament" || type === "sunburst" || type === "sankey";
|
|
320
525
|
}
|
|
321
526
|
function chartSpecColorsByCategory(spec) {
|
|
322
527
|
return chartColorsByCategory(spec.type) || chartVariesColors(spec);
|
|
@@ -390,7 +595,7 @@ function parseChartValue(input) {
|
|
|
390
595
|
if (negative) value = -value;
|
|
391
596
|
return Number.isFinite(value) ? value : void 0;
|
|
392
597
|
}
|
|
393
|
-
var
|
|
598
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
394
599
|
function pick(value, options, fallback, path, issues) {
|
|
395
600
|
if (value === void 0) return fallback;
|
|
396
601
|
if (typeof value === "string" && options.includes(value)) return value;
|
|
@@ -419,7 +624,7 @@ function bound(value, path, issues) {
|
|
|
419
624
|
}
|
|
420
625
|
function normalizeFormat(value, path, issues) {
|
|
421
626
|
if (value === void 0 || value === null) return {};
|
|
422
|
-
if (!
|
|
627
|
+
if (!isRecord2(value)) {
|
|
423
628
|
issues.push({ path, message: `${path} must be an object.` });
|
|
424
629
|
return {};
|
|
425
630
|
}
|
|
@@ -437,7 +642,7 @@ function normalizeFormat(value, path, issues) {
|
|
|
437
642
|
}
|
|
438
643
|
function normalizeChartAxis(value, fallback, path, issues = []) {
|
|
439
644
|
if (value === void 0) return { ...fallback, format: { ...fallback.format } };
|
|
440
|
-
if (!
|
|
645
|
+
if (!isRecord2(value)) {
|
|
441
646
|
issues.push({ path, message: `${path} must be an object.` });
|
|
442
647
|
return { ...fallback, format: { ...fallback.format } };
|
|
443
648
|
}
|
|
@@ -487,7 +692,7 @@ function normalizeChartAxis(value, fallback, path, issues = []) {
|
|
|
487
692
|
function normalizeLegend(value, issues) {
|
|
488
693
|
const fallback = { show: true, position: "bottom" };
|
|
489
694
|
if (value === void 0) return fallback;
|
|
490
|
-
if (!
|
|
695
|
+
if (!isRecord2(value)) {
|
|
491
696
|
issues.push({ path: "legend", message: "legend must be an object." });
|
|
492
697
|
return fallback;
|
|
493
698
|
}
|
|
@@ -517,7 +722,7 @@ function normalizeChartValues(value, length, path, issues = []) {
|
|
|
517
722
|
}
|
|
518
723
|
function normalizeSeries(value, length, used, index, issues) {
|
|
519
724
|
const path = `series[${index}]`;
|
|
520
|
-
if (!
|
|
725
|
+
if (!isRecord2(value)) {
|
|
521
726
|
issues.push({ path, message: `${path} must be an object with a name and values.` });
|
|
522
727
|
return null;
|
|
523
728
|
}
|
|
@@ -607,7 +812,7 @@ var finiteNumber = (value) => {
|
|
|
607
812
|
return typeof n === "number" && Number.isFinite(n) ? n : null;
|
|
608
813
|
};
|
|
609
814
|
function normalizeChartReferenceLine(value, path, issues = []) {
|
|
610
|
-
if (!
|
|
815
|
+
if (!isRecord2(value)) {
|
|
611
816
|
issues.push({ path, message: `${path} must be an object with a value, e.g. {"value": 100, "label": "Target"}.` });
|
|
612
817
|
return null;
|
|
613
818
|
}
|
|
@@ -620,7 +825,7 @@ function normalizeChartReferenceLine(value, path, issues = []) {
|
|
|
620
825
|
return { value: at, ...referenceCommon(value, path, issues) };
|
|
621
826
|
}
|
|
622
827
|
function normalizeChartReferenceBand(value, path, issues = []) {
|
|
623
|
-
if (!
|
|
828
|
+
if (!isRecord2(value)) {
|
|
624
829
|
issues.push({ path, message: `${path} must be an object with from and to, e.g. {"from": 80, "to": 120, "label": "Target range"}.` });
|
|
625
830
|
return null;
|
|
626
831
|
}
|
|
@@ -634,7 +839,7 @@ function normalizeChartReferenceBand(value, path, issues = []) {
|
|
|
634
839
|
return { from: Math.min(from, to), to: Math.max(from, to), ...referenceCommon(value, path, issues) };
|
|
635
840
|
}
|
|
636
841
|
function normalizeChartErrorBars(value, length, path, issues = []) {
|
|
637
|
-
if (!
|
|
842
|
+
if (!isRecord2(value)) {
|
|
638
843
|
issues.push({ path, message: `${path} must be an object with a type (${CHART_ERROR_BAR_TYPES.join(", ")}), or null for none.` });
|
|
639
844
|
return null;
|
|
640
845
|
}
|
|
@@ -667,7 +872,7 @@ function normalizeChartErrorBars(value, length, path, issues = []) {
|
|
|
667
872
|
return issues.length > before ? null : bars;
|
|
668
873
|
}
|
|
669
874
|
function normalizeChartTrendline(value, path, issues = []) {
|
|
670
|
-
if (!
|
|
875
|
+
if (!isRecord2(value)) {
|
|
671
876
|
issues.push({ path, message: `${path} must be an object with a type.` });
|
|
672
877
|
return null;
|
|
673
878
|
}
|
|
@@ -706,8 +911,8 @@ function normalizeChartTrendline(value, path, issues = []) {
|
|
|
706
911
|
}
|
|
707
912
|
function normalizeChartSpec(input) {
|
|
708
913
|
const issues = [];
|
|
709
|
-
const value =
|
|
710
|
-
if (input !== void 0 && !
|
|
914
|
+
const value = isRecord2(input) ? input : {};
|
|
915
|
+
if (input !== void 0 && !isRecord2(input)) issues.push({ path: "", message: "A chart must be an object." });
|
|
711
916
|
const type = pick(value.type, CHART_TYPES, "bar", "type", issues);
|
|
712
917
|
const rawSeries = Array.isArray(value.series) ? value.series : [];
|
|
713
918
|
if (value.series !== void 0 && !Array.isArray(value.series)) issues.push({ path: "series", message: "series must be an array." });
|
|
@@ -715,16 +920,21 @@ function normalizeChartSpec(input) {
|
|
|
715
920
|
if (Array.isArray(value.categories)) categories = value.categories.map((c, i) => text(c, "", `categories[${i}]`, issues));
|
|
716
921
|
else {
|
|
717
922
|
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,
|
|
923
|
+
const longest = rawSeries.reduce((n, s) => Math.max(n, isRecord2(s) && Array.isArray(s.values) ? s.values.length : 0), 0);
|
|
719
924
|
categories = Array.from({ length: longest }, (_, i) => `${i + 1}`);
|
|
720
925
|
}
|
|
721
926
|
const tree = normalizeChartTree(value.tree, issues);
|
|
722
|
-
|
|
927
|
+
const links = normalizeChartLinks(value.links, issues);
|
|
928
|
+
const sankeyNodes = chartSankeyNodes(links, Array.isArray(value.sankeyNodes) ? value.sankeyNodes.map(String) : [], issues);
|
|
929
|
+
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." });
|
|
930
|
+
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." });
|
|
931
|
+
if (links.length) categories = [...sankeyNodes];
|
|
932
|
+
else if (tree.length) categories = tree.map((n) => n.name);
|
|
723
933
|
const used = /* @__PURE__ */ new Set();
|
|
724
934
|
const series = rawSeries.map((s, i) => normalizeSeries(s, categories.length, used, i, issues)).filter((s) => s !== null);
|
|
725
|
-
if (tree.length) {
|
|
935
|
+
if (tree.length || links.length) {
|
|
726
936
|
if (!series.length) series.push({ id: uniqueChartSeriesId(series), name: "Values", values: [] });
|
|
727
|
-
series[0] = { ...series[0], values: tree.map(chartTreeTotal) };
|
|
937
|
+
series[0] = { ...series[0], values: links.length ? chartSankeyNodeValues(links, sankeyNodes) : tree.map(chartTreeTotal) };
|
|
728
938
|
}
|
|
729
939
|
let palette = [...DEFAULT_CHART_PALETTE];
|
|
730
940
|
if (Array.isArray(value.palette)) {
|
|
@@ -815,8 +1025,11 @@ function normalizeChartSpec(input) {
|
|
|
815
1025
|
parliamentArc: fraction("parliamentArc", 180, 90, 360),
|
|
816
1026
|
parliamentMajority,
|
|
817
1027
|
parliamentLabel: text(value.parliamentLabel, "", "parliamentLabel", issues),
|
|
818
|
-
tree,
|
|
819
|
-
sunburstLabel: text(value.sunburstLabel, "", "sunburstLabel", issues)
|
|
1028
|
+
tree: links.length ? [] : tree,
|
|
1029
|
+
sunburstLabel: text(value.sunburstLabel, "", "sunburstLabel", issues),
|
|
1030
|
+
links,
|
|
1031
|
+
sankeyNodes: links.length ? sankeyNodes : [],
|
|
1032
|
+
sankeyLinkColor: pick(value.sankeyLinkColor, CHART_SANKEY_LINK_COLORS, "source", "sankeyLinkColor", issues)
|
|
820
1033
|
};
|
|
821
1034
|
if (spec.stacking === "percent")
|
|
822
1035
|
for (const name of ["yAxis", "yAxis2"]) {
|
|
@@ -840,7 +1053,9 @@ function cloneChartSpec(spec) {
|
|
|
840
1053
|
highlight: [...spec.highlight],
|
|
841
1054
|
referenceLines: spec.referenceLines.map((l) => ({ ...l })),
|
|
842
1055
|
referenceBands: spec.referenceBands.map((b) => ({ ...b })),
|
|
843
|
-
tree: cloneChartTree(spec.tree)
|
|
1056
|
+
tree: cloneChartTree(spec.tree),
|
|
1057
|
+
links: spec.links.map((l) => ({ ...l })),
|
|
1058
|
+
sankeyNodes: [...spec.sankeyNodes]
|
|
844
1059
|
};
|
|
845
1060
|
}
|
|
846
1061
|
function cloneChartSeries(series) {
|
|
@@ -909,204 +1124,120 @@ function chartColorInputValue(color) {
|
|
|
909
1124
|
return "#000000";
|
|
910
1125
|
}
|
|
911
1126
|
|
|
912
|
-
// src/chart/
|
|
913
|
-
var
|
|
914
|
-
var
|
|
915
|
-
var
|
|
916
|
-
function
|
|
1127
|
+
// src/chart/links.ts
|
|
1128
|
+
var MAX_CHART_LINKS = 2e3;
|
|
1129
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1130
|
+
var nameOf = (value) => typeof value === "number" ? String(value) : typeof value === "string" ? value.trim() : "";
|
|
1131
|
+
function normalizeChartLinks(value, issues = [], path = "links") {
|
|
917
1132
|
if (value === void 0 || value === null) return [];
|
|
918
1133
|
if (!Array.isArray(value)) {
|
|
919
|
-
issues.push({ path, message: `${path} must be an array of
|
|
1134
|
+
issues.push({ path, message: `${path} must be an array of flows such as {"source": "Website", "target": "Signup", "value": 120}.` });
|
|
920
1135
|
return [];
|
|
921
1136
|
}
|
|
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
1137
|
const out = [];
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1138
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1139
|
+
const next = /* @__PURE__ */ new Map();
|
|
1140
|
+
const reaches = (to, from) => {
|
|
1141
|
+
const stack = [to];
|
|
1142
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1143
|
+
while (stack.length) {
|
|
1144
|
+
const n = stack.pop();
|
|
1145
|
+
if (n === from) return true;
|
|
1146
|
+
if (visited.has(n)) continue;
|
|
1147
|
+
visited.add(n);
|
|
1148
|
+
for (const m of next.get(n) ?? []) stack.push(m);
|
|
997
1149
|
}
|
|
1150
|
+
return false;
|
|
998
1151
|
};
|
|
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);
|
|
1152
|
+
value.forEach((raw, i) => {
|
|
1153
|
+
const where = `${path}[${i}]`;
|
|
1154
|
+
if (out.length >= MAX_CHART_LINKS) {
|
|
1155
|
+
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.` });
|
|
1156
|
+
return;
|
|
1010
1157
|
}
|
|
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.` });
|
|
1158
|
+
if (!isRecord3(raw)) {
|
|
1159
|
+
issues.push({ path: where, message: `${where} must be an object with source, target and value.` });
|
|
1014
1160
|
return;
|
|
1015
1161
|
}
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
|
|
1162
|
+
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.` });
|
|
1163
|
+
const source = nameOf(raw.source);
|
|
1164
|
+
const target = nameOf(raw.target);
|
|
1165
|
+
if (!source || !target) {
|
|
1166
|
+
issues.push({ path: where, message: `${where} needs a source and a target (node names).` });
|
|
1167
|
+
return;
|
|
1019
1168
|
}
|
|
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;
|
|
1169
|
+
if (source === target) {
|
|
1170
|
+
issues.push({ path: where, message: `${where} goes from "${source}" to itself; a flow must join two different nodes.` });
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
const v = parseChartValue(raw.value);
|
|
1174
|
+
if (!(typeof v === "number" && v > 0)) {
|
|
1175
|
+
issues.push({ path: `${where}.value`, message: `The flow from "${source}" to "${target}" needs a value above 0; got ${JSON.stringify(raw.value)}.` });
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
const key2 = `${source}\0${target}`;
|
|
1179
|
+
if (seen.has(key2)) {
|
|
1180
|
+
issues.push({ path: where, message: `The flow from "${source}" to "${target}" appears twice; add the values into one flow. The second was left out.` });
|
|
1181
|
+
return;
|
|
1037
1182
|
}
|
|
1183
|
+
if (reaches(target, source)) {
|
|
1184
|
+
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.` });
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
seen.add(key2);
|
|
1188
|
+
if (!next.has(source)) next.set(source, /* @__PURE__ */ new Set());
|
|
1189
|
+
next.get(source).add(target);
|
|
1190
|
+
const link = { source, target, value: v };
|
|
1191
|
+
if (typeof raw.color === "string" && raw.color.trim()) link.color = raw.color.trim();
|
|
1192
|
+
out.push(link);
|
|
1038
1193
|
});
|
|
1039
|
-
return
|
|
1194
|
+
return out;
|
|
1040
1195
|
}
|
|
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);
|
|
1196
|
+
function chartSankeyNodes(links, order = [], issues = []) {
|
|
1197
|
+
const used = /* @__PURE__ */ new Set();
|
|
1198
|
+
const named = [];
|
|
1199
|
+
for (const l of links)
|
|
1200
|
+
for (const n of [l.source, l.target])
|
|
1201
|
+
if (!used.has(n)) {
|
|
1202
|
+
used.add(n);
|
|
1203
|
+
named.push(n);
|
|
1062
1204
|
}
|
|
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
1205
|
const out = [];
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
out.
|
|
1079
|
-
}
|
|
1206
|
+
for (const n of order) {
|
|
1207
|
+
if (!used.has(n)) {
|
|
1208
|
+
if (links.length) issues.push({ path: "sankeyNodes", message: `sankeyNodes names "${n}", which no flow uses; it was left out.` });
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
if (!out.includes(n)) out.push(n);
|
|
1212
|
+
}
|
|
1213
|
+
for (const n of named) if (!out.includes(n)) out.push(n);
|
|
1080
1214
|
return out;
|
|
1081
1215
|
}
|
|
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
|
-
});
|
|
1216
|
+
function chartSankeyNodeValues(links, nodes) {
|
|
1217
|
+
const inflow = /* @__PURE__ */ new Map();
|
|
1218
|
+
const outflow = /* @__PURE__ */ new Map();
|
|
1219
|
+
for (const l of links) {
|
|
1220
|
+
outflow.set(l.source, (outflow.get(l.source) ?? 0) + l.value);
|
|
1221
|
+
inflow.set(l.target, (inflow.get(l.target) ?? 0) + l.value);
|
|
1222
|
+
}
|
|
1223
|
+
return nodes.map((n) => Math.max(inflow.get(n) ?? 0, outflow.get(n) ?? 0));
|
|
1097
1224
|
}
|
|
1098
|
-
function
|
|
1099
|
-
|
|
1225
|
+
function chartLinksFromTable(rows, options = {}) {
|
|
1226
|
+
const grid = rows.filter((r) => r.some((c) => c !== null && c !== void 0 && String(c).trim() !== ""));
|
|
1227
|
+
if (!grid.length) return [];
|
|
1228
|
+
const header = options.header ?? "auto";
|
|
1229
|
+
const skip = header === true || header === "auto" && typeof parseChartValue(grid[0][2]) !== "number";
|
|
1230
|
+
return (skip ? grid.slice(1) : grid).map((r) => ({ source: r[0], target: r[1], value: r[2] }));
|
|
1100
1231
|
}
|
|
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 }];
|
|
1232
|
+
function setChartLinkField(links, index, field, text2) {
|
|
1233
|
+
return links.map((l, i) => i === index ? { ...l, [field]: field === "value" ? text2 : text2.trim() } : { ...l });
|
|
1107
1234
|
}
|
|
1108
|
-
function
|
|
1109
|
-
|
|
1235
|
+
function addChartLink(links) {
|
|
1236
|
+
const last = links[links.length - 1];
|
|
1237
|
+
const names = new Set(links.flatMap((l) => [l.source, l.target]));
|
|
1238
|
+
let n = names.size + 1;
|
|
1239
|
+
while (names.has(`Step ${n}`)) n++;
|
|
1240
|
+
return [...links.map((l) => ({ ...l })), { source: last?.target ?? "Start", target: `Step ${n}`, value: last?.value ?? 1 }];
|
|
1110
1241
|
}
|
|
1111
1242
|
|
|
1112
1243
|
export {
|
|
@@ -1131,6 +1262,13 @@ export {
|
|
|
1131
1262
|
chartErrorBarsOfType,
|
|
1132
1263
|
formatChartAmountList,
|
|
1133
1264
|
parseChartAmountList,
|
|
1265
|
+
MAX_CHART_LINKS,
|
|
1266
|
+
normalizeChartLinks,
|
|
1267
|
+
chartSankeyNodes,
|
|
1268
|
+
chartSankeyNodeValues,
|
|
1269
|
+
chartLinksFromTable,
|
|
1270
|
+
setChartLinkField,
|
|
1271
|
+
addChartLink,
|
|
1134
1272
|
MAX_CHART_TREE_DEPTH,
|
|
1135
1273
|
MAX_CHART_TREE_NODES,
|
|
1136
1274
|
normalizeChartTree,
|
|
@@ -1159,6 +1297,7 @@ export {
|
|
|
1159
1297
|
CHART_RADAR_GRATICULES,
|
|
1160
1298
|
CHART_LIKERT_NEUTRALS,
|
|
1161
1299
|
CHART_LIKERT_SORTS,
|
|
1300
|
+
CHART_SANKEY_LINK_COLORS,
|
|
1162
1301
|
CHART_REFERENCE_AXES,
|
|
1163
1302
|
CHART_ORIENTATIONS,
|
|
1164
1303
|
CHART_LEGEND_POSITIONS,
|