@pixerate/editor 0.55.0 → 0.57.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.
Files changed (75) hide show
  1. package/dist/agent/index.cjs +18 -17
  2. package/dist/agent/index.cjs.map +1 -1
  3. package/dist/agent/index.js +3 -2
  4. package/dist/agent/index.js.map +1 -1
  5. package/dist/animation-editor/index.cjs +25 -7
  6. package/dist/animation-editor/index.cjs.map +1 -1
  7. package/dist/animation-editor/index.d.cts +21 -5
  8. package/dist/animation-editor/index.d.ts +21 -5
  9. package/dist/animation-editor/index.js +25 -7
  10. package/dist/animation-editor/index.js.map +1 -1
  11. package/dist/canvas/index.cjs +2105 -172
  12. package/dist/canvas/index.cjs.map +1 -1
  13. package/dist/canvas/index.d.cts +319 -23
  14. package/dist/canvas/index.d.ts +319 -23
  15. package/dist/canvas/index.js +2081 -148
  16. package/dist/canvas/index.js.map +1 -1
  17. package/dist/chart/index.cjs +32 -30
  18. package/dist/chart/index.cjs.map +1 -1
  19. package/dist/chart/index.d.cts +4 -2
  20. package/dist/chart/index.d.ts +4 -2
  21. package/dist/chart/index.js +20 -18
  22. package/dist/chart/index.js.map +1 -1
  23. package/dist/{chunk-C3JHII3M.cjs → chunk-3I6V7PI4.cjs} +45 -45
  24. package/dist/{chunk-C3JHII3M.cjs.map → chunk-3I6V7PI4.cjs.map} +1 -1
  25. package/dist/{chunk-P43HUQRX.cjs → chunk-AHCENCU4.cjs} +12 -10
  26. package/dist/chunk-AHCENCU4.cjs.map +1 -0
  27. package/dist/chunk-II77MLHN.cjs +11106 -0
  28. package/dist/chunk-II77MLHN.cjs.map +1 -0
  29. package/dist/{chunk-OIKYP7XC.js → chunk-LMUDHBWR.js} +2 -2
  30. package/dist/{chunk-VODBAO32.js → chunk-OEFCLWZ3.js} +4 -1425
  31. package/dist/chunk-OEFCLWZ3.js.map +1 -0
  32. package/dist/{chunk-NK3WL5TJ.cjs → chunk-P2CTDJNF.cjs} +27 -1448
  33. package/dist/chunk-P2CTDJNF.cjs.map +1 -0
  34. package/dist/chunk-TKPPIVW7.cjs +1443 -0
  35. package/dist/chunk-TKPPIVW7.cjs.map +1 -0
  36. package/dist/chunk-WJSCGBJ2.js +11106 -0
  37. package/dist/chunk-WJSCGBJ2.js.map +1 -0
  38. package/dist/{chunk-RIXTW3C4.js → chunk-WLVZ235X.js} +5 -3
  39. package/dist/{chunk-RIXTW3C4.js.map → chunk-WLVZ235X.js.map} +1 -1
  40. package/dist/chunk-ZOH6X3VR.js +1443 -0
  41. package/dist/chunk-ZOH6X3VR.js.map +1 -0
  42. package/dist/{controller-DscWQbjO.d.ts → controller-D2rj5umx.d.ts} +2 -362
  43. package/dist/{controller-CX0QfSv0.d.cts → controller-R2biKk_E.d.cts} +2 -362
  44. package/dist/diagram/index.cjs +1457 -47
  45. package/dist/diagram/index.cjs.map +1 -1
  46. package/dist/diagram/index.d.cts +1734 -15
  47. package/dist/diagram/index.d.ts +1734 -15
  48. package/dist/diagram/index.js +1436 -26
  49. package/dist/diagram/index.js.map +1 -1
  50. package/dist/extensions/index.cjs +5 -4
  51. package/dist/extensions/index.cjs.map +1 -1
  52. package/dist/extensions/index.d.cts +4 -3
  53. package/dist/extensions/index.d.ts +4 -3
  54. package/dist/extensions/index.js +4 -3
  55. package/dist/{index-DIWB6xMf.d.cts → index-BcHyG0Wq.d.cts} +2 -1
  56. package/dist/{index-CALDD35B.d.ts → index-CTCc3m3C.d.ts} +2 -1
  57. package/dist/index.cjs +10 -9
  58. package/dist/index.cjs.map +1 -1
  59. package/dist/index.d.cts +4 -3
  60. package/dist/index.d.ts +4 -3
  61. package/dist/index.js +4 -3
  62. package/dist/index.js.map +1 -1
  63. package/dist/types-BRp-6zkY.d.cts +362 -0
  64. package/dist/types-BRp-6zkY.d.ts +362 -0
  65. package/dist/{types-BULrFyuQ.d.cts → types-DYkjhzdz.d.cts} +1 -1
  66. package/dist/{types-BULrFyuQ.d.ts → types-DYkjhzdz.d.ts} +1 -1
  67. package/package.json +1 -1
  68. package/dist/chunk-H5K77C7Y.cjs +0 -3848
  69. package/dist/chunk-H5K77C7Y.cjs.map +0 -1
  70. package/dist/chunk-NK3WL5TJ.cjs.map +0 -1
  71. package/dist/chunk-P43HUQRX.cjs.map +0 -1
  72. package/dist/chunk-VODBAO32.js.map +0 -1
  73. package/dist/chunk-WWJNC7SX.js +0 -3848
  74. package/dist/chunk-WWJNC7SX.js.map +0 -1
  75. /package/dist/{chunk-OIKYP7XC.js.map → chunk-LMUDHBWR.js.map} +0 -0
@@ -1,17 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
15
2
 
16
3
 
17
4
 
@@ -25,393 +12,6 @@ var _chunkCKRNELPOcjs = require('./chunk-CKRNELPO.cjs');
25
12
 
26
13
  var _chunkXP5IUZUOcjs = require('./chunk-XP5IUZUO.cjs');
27
14
 
28
-
29
-
30
- var _chunkTNNJXF46cjs = require('./chunk-TNNJXF46.cjs');
31
-
32
-
33
- var _chunkQKLRAA4Tcjs = require('./chunk-QKLRAA4T.cjs');
34
-
35
- // src/chart/mermaid.ts
36
- var CHART_MERMAID_META_PREFIX = "%% @pixerate ";
37
- var quote = (text) => `"${text.replace(/"/g, "#quot;")}"`;
38
- var unquote = (text) => {
39
- const t = text.trim();
40
- return (t.length >= 2 && t.startsWith('"') && t.endsWith('"') ? t.slice(1, -1) : t).replace(/#quot;/g, '"');
41
- };
42
- var word = (text) => /^[A-Za-z0-9_]+$/.test(text) ? text : quote(text);
43
- var num = (value) => Number.isInteger(value) ? String(value) : String(Number(value.toPrecision(10)));
44
- function splitOutside(text, sep) {
45
- const out = [];
46
- let cur = "";
47
- let quoted = false;
48
- let depth = 0;
49
- for (const ch of text) {
50
- if (ch === '"') quoted = !quoted;
51
- else if (!quoted && (ch === "[" || ch === "{")) depth++;
52
- else if (!quoted && (ch === "]" || ch === "}")) depth--;
53
- if (ch === sep && !quoted && depth === 0) {
54
- out.push(cur);
55
- cur = "";
56
- } else cur += ch;
57
- }
58
- out.push(cur);
59
- return out;
60
- }
61
- function chartMermaidFamily(spec) {
62
- const family = _chunkCKRNELPOcjs.chartFamily.call(void 0, spec);
63
- if (family === "pie" || family === "funnel" || family === "treemap") return "pie";
64
- if (family === "radar") return "radar";
65
- return "xychart";
66
- }
67
- function slug(label, i, taken) {
68
- let base = label.normalize("NFKD").replace(/[^\w-]+/g, "_").replace(/^_+|_+$/g, "") || `a${i + 1}`;
69
- if (!/^[A-Za-z_]/.test(base)) base = `a_${base}`;
70
- let id = base;
71
- for (let k = 2; taken.has(id); k++) id = `${base}_${k}`;
72
- taken.add(id);
73
- return id;
74
- }
75
- function metaFor(spec, text) {
76
- const base = chartFromMermaid(text, { ignoreMeta: true }).spec;
77
- const meta = {};
78
- const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
79
- for (const key of Object.keys(spec)) {
80
- if (key === "series" || key === "version") continue;
81
- if (!same(spec[key], base[key])) meta[key] = spec[key];
82
- }
83
- const series = spec.series.map((s, i) => {
84
- const b = base.series[i];
85
- const out = {};
86
- for (const [k, v] of Object.entries(s)) {
87
- if (k === "values") {
88
- if (b && !same(v, b.values)) out.values = v;
89
- continue;
90
- }
91
- if (!b || !same(v, b[k])) out[k] = v;
92
- }
93
- return out;
94
- });
95
- if (series.some((s) => Object.keys(s).length) || spec.series.length !== base.series.length) meta.series = series;
96
- return Object.keys(meta).length ? meta : null;
97
- }
98
- function chartToMermaid(spec, options = {}) {
99
- const ind = _nullishCoalesce(options.indent, () => ( " "));
100
- const family = chartMermaidFamily(spec);
101
- const visible = spec.series.filter((s) => !s.hidden);
102
- const lines = [];
103
- const v = (x) => x === null || !Number.isFinite(x) ? "0" : num(x);
104
- if (family === "pie") {
105
- const s = _nullishCoalesce(visible[0], () => ( spec.series[0]));
106
- lines.push(`pie${spec.dataLabels ? " showData" : ""}`);
107
- if (spec.title) lines.push(`${ind}title ${spec.title}`);
108
- spec.categories.forEach((c, i) => {
109
- const value = _optionalChain([s, 'optionalAccess', _2 => _2.values, 'access', _3 => _3[i]]);
110
- if (value !== null && value !== void 0 && Number.isFinite(value) && value > 0) lines.push(`${ind}${quote(c)} : ${num(value)}`);
111
- });
112
- } else if (family === "radar") {
113
- lines.push("radar-beta");
114
- if (spec.title) lines.push(`${ind}title ${spec.title}`);
115
- const taken = /* @__PURE__ */ new Set();
116
- const ids = spec.categories.map((c, i) => slug(c, i, taken));
117
- lines.push(`${ind}axis ${ids.map((id, i) => id === spec.categories[i] ? id : `${id}["${spec.categories[i].replace(/"/g, "'")}"]`).join(", ")}`);
118
- const seriesTaken = new Set(ids);
119
- for (const [i, s] of spec.series.entries()) {
120
- const id = slug(s.id, i, seriesTaken);
121
- lines.push(`${ind}curve ${id}["${s.name.replace(/"/g, "'")}"]{${s.values.map(v).join(", ")}}`);
122
- }
123
- if (spec.yAxis.max !== null) lines.push(`${ind}max ${num(spec.yAxis.max)}`);
124
- if (spec.yAxis.min !== null) lines.push(`${ind}min ${num(spec.yAxis.min)}`);
125
- if (!spec.legend.show) lines.push(`${ind}showLegend false`);
126
- } else {
127
- lines.push(`xychart-beta${spec.orientation === "horizontal" ? " horizontal" : ""}`);
128
- if (spec.title) lines.push(`${ind}title ${quote(spec.title)}`);
129
- const xTitle = spec.xAxis.title ? ` ${quote(spec.xAxis.title)}` : "";
130
- lines.push(`${ind}x-axis${xTitle} [${spec.categories.map(word).join(", ")}]`);
131
- const y = spec.yAxis;
132
- if (y.title || y.min !== null || y.max !== null) {
133
- let out = `${ind}y-axis`;
134
- if (y.title) out += ` ${quote(y.title)}`;
135
- if (y.min !== null || y.max !== null) {
136
- const all = spec.series.flatMap((s) => s.values).filter((x) => x !== null && Number.isFinite(x));
137
- const lo = _nullishCoalesce(y.min, () => ( Math.min(0, ...all)));
138
- const hi = _nullishCoalesce(y.max, () => ( Math.max(lo + 1, ...all)));
139
- out += ` ${num(lo)} --> ${num(hi)}`;
140
- }
141
- lines.push(out);
142
- }
143
- for (const s of spec.series) {
144
- const kind = (_nullishCoalesce(s.type, () => ( (spec.type === "bar" ? "bar" : "line")))) === "bar" ? "bar" : "line";
145
- lines.push(`${ind}${kind} ${quote(s.name)} [${s.values.map(v).join(", ")}]`);
146
- }
147
- }
148
- let text = `${lines.join("\n")}
149
- `;
150
- if (options.meta !== false) {
151
- const meta = metaFor(spec, text);
152
- if (meta) {
153
- const [header, ...rest] = lines;
154
- text = `${[header, `${CHART_MERMAID_META_PREFIX}${JSON.stringify({ v: 1, ...meta })}`, ...rest].join("\n")}
155
- `;
156
- }
157
- }
158
- return text;
159
- }
160
- var RANGE = /^(-?\d+(?:\.\d+)?)\s*-->\s*(-?\d+(?:\.\d+)?)$/;
161
- function takeTitle(text) {
162
- const t = text.trim();
163
- if (t.startsWith('"')) {
164
- const end = t.indexOf('"', 1);
165
- if (end > 0) return { title: unquote(t.slice(0, end + 1)), rest: t.slice(end + 1).trim() };
166
- }
167
- if (!t || t.startsWith("[") || RANGE.test(t) || /^-?\d/.test(t)) return { rest: t };
168
- const m = /^(\S+)\s*(.*)$/.exec(t);
169
- return { title: m[1], rest: m[2].trim() };
170
- }
171
- function parseList(text, partial) {
172
- const t = text.trim();
173
- let m = /^\[(.*)\]$/s.exec(t);
174
- if (!m && partial) m = /^\[(.*)$/s.exec(t);
175
- if (!m) return void 0;
176
- if (!m[1].trim()) return [];
177
- const items = splitOutside(m[1], ",").map((x) => x.trim());
178
- if (partial && !/\]$/.test(t)) {
179
- const last = items[items.length - 1];
180
- if (!last || last.startsWith('"') && !/"$/.test(last.slice(1))) items.pop();
181
- }
182
- return items.map(unquote);
183
- }
184
- function chartFromMermaid(text, options = {}) {
185
- const issues = [];
186
- const partial = !!options.partial;
187
- const all = text.replace(/^/, "").split(/\r?\n/);
188
- let start = 0;
189
- let frontTitle;
190
- if (_optionalChain([all, 'access', _4 => _4[0], 'optionalAccess', _5 => _5.trim, 'call', _6 => _6()]) === "---") {
191
- const end = all.findIndex((l, i) => i > 0 && l.trim() === "---");
192
- if (end > 0) {
193
- for (const l of all.slice(1, end)) {
194
- const m = /^title:\s*(.+)$/.exec(l.trim());
195
- if (m) frontTitle = unquote(m[1]);
196
- }
197
- start = end + 1;
198
- }
199
- }
200
- let family = null;
201
- let headerLine = -1;
202
- let horizontal = false;
203
- let showData = false;
204
- let meta = null;
205
- for (let i = start; i < all.length; i++) {
206
- const t = all[i].trim();
207
- if (!t || t.startsWith("%%")) continue;
208
- let m;
209
- if (m = /^xychart(?:-beta)?(?:\s+(horizontal|vertical))?\s*$/i.exec(t)) {
210
- family = "xychart";
211
- horizontal = _optionalChain([m, 'access', _7 => _7[1], 'optionalAccess', _8 => _8.toLowerCase, 'call', _9 => _9()]) === "horizontal";
212
- } else if (m = /^pie(\s+showData)?(?:\s+title\s+(.*))?\s*$/i.exec(t)) {
213
- family = "pie";
214
- showData = !!m[1];
215
- if (m[2]) frontTitle = m[2].trim();
216
- } else if (/^radar(?:-beta)?\s*$/i.test(t)) family = "radar";
217
- else issues.push({ path: "header", line: i + 1, message: `Line ${i + 1}: expected a chart header (xychart-beta, pie or radar-beta), got "${t.slice(0, 40)}".` });
218
- headerLine = i;
219
- break;
220
- }
221
- let title = _nullishCoalesce(frontTitle, () => ( ""));
222
- const categories = [];
223
- const series = [];
224
- const xAxis = _chunkCKRNELPOcjs.defaultChartAxis.call(void 0, false);
225
- const yAxis = _chunkCKRNELPOcjs.defaultChartAxis.call(void 0, true);
226
- const radarAxes = [];
227
- const pieSlices = [];
228
- let legendShow = true;
229
- const counts = { bar: 0, line: 0 };
230
- const lineIssue = (i, message) => issues.push({ path: `line ${i + 1}`, line: i + 1, message: `Line ${i + 1}: ${message}` });
231
- for (let i = headerLine + 1; family && i < all.length; i++) {
232
- const raw = all[i];
233
- const t = raw.trim();
234
- if (!t) continue;
235
- if (t.startsWith(CHART_MERMAID_META_PREFIX.trim())) {
236
- if (options.ignoreMeta) continue;
237
- try {
238
- const parsed = JSON.parse(t.slice(CHART_MERMAID_META_PREFIX.trim().length).trim());
239
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) meta = parsed;
240
- else lineIssue(i, "the @pixerate metadata is not a JSON object; it was ignored.");
241
- } catch (e2) {
242
- if (!partial) lineIssue(i, "the @pixerate metadata is not valid JSON; it was ignored.");
243
- }
244
- continue;
245
- }
246
- if (t.startsWith("%%")) continue;
247
- const isLast = i === all.length - 1 || all.slice(i + 1).every((l) => !l.trim());
248
- let m;
249
- if (m = /^title\s+(.+)$/i.exec(t)) {
250
- title = unquote(m[1]);
251
- continue;
252
- }
253
- if (family === "xychart") {
254
- if (m = /^x-axis\b(.*)$/i.exec(t)) {
255
- const { title: xt, rest } = takeTitle(m[1]);
256
- if (xt) xAxis.title = xt;
257
- const list = parseList(rest, partial && isLast);
258
- if (list) categories.splice(0, categories.length, ...list);
259
- else if (RANGE.test(rest)) {
260
- const r = RANGE.exec(rest);
261
- xAxis.min = Number(r[1]);
262
- xAxis.max = Number(r[2]);
263
- } else if (rest) lineIssue(i, `expected x-axis [a, b, \u2026] or a range "min --> max", got "${rest.slice(0, 40)}".`);
264
- continue;
265
- }
266
- if (m = /^y-axis\b(.*)$/i.exec(t)) {
267
- const { title: yt, rest } = takeTitle(m[1]);
268
- if (yt) yAxis.title = yt;
269
- const r = RANGE.exec(rest);
270
- if (r) {
271
- yAxis.min = Number(r[1]);
272
- yAxis.max = Number(r[2]);
273
- } else if (rest) lineIssue(i, `expected y-axis "min --> max", got "${rest.slice(0, 40)}".`);
274
- continue;
275
- }
276
- if (m = /^(bar|line)\b(.*)$/i.exec(t)) {
277
- const kind = m[1].toLowerCase();
278
- const { title: st, rest } = takeTitle(m[2]);
279
- const list = parseList(rest, partial && isLast);
280
- if (!list) {
281
- if (!(partial && isLast)) lineIssue(i, `expected ${kind} [1, 2, 3], got "${rest.slice(0, 40)}".`);
282
- continue;
283
- }
284
- const values = list.map((x) => x.trim() === "" ? null : Number(x));
285
- if (values.some((x) => x !== null && !Number.isFinite(x))) {
286
- lineIssue(i, `${kind} values must be numbers.`);
287
- continue;
288
- }
289
- counts[kind]++;
290
- series.push({ id: `series-${series.length + 1}`, name: _nullishCoalesce(st, () => ( `${kind === "bar" ? "Bar" : "Line"} ${counts[kind]}`)), values, type: kind });
291
- continue;
292
- }
293
- } else if (family === "pie") {
294
- if (m = /^"((?:[^"\\]|\\.)*)"\s*:\s*(-?\d+(?:\.\d+)?)\s*$/.exec(t)) {
295
- pieSlices.push({ label: m[1].replace(/#quot;/g, '"'), value: Number(m[2]) });
296
- continue;
297
- }
298
- if (/^showData\s*$/i.test(t)) {
299
- showData = true;
300
- continue;
301
- }
302
- } else if (family === "radar") {
303
- if (m = /^axis\s+(.+)$/.exec(t)) {
304
- for (const part of splitOutside(m[1], ",")) {
305
- const a = /^([A-Za-z_][\w-]*)\s*(?:\[\s*"((?:[^"\\]|\\.)*)"\s*\])?$/.exec(part.trim());
306
- if (a) radarAxes.push({ id: a[1], label: _nullishCoalesce(a[2], () => ( a[1])) });
307
- else lineIssue(i, `could not read the axis "${part.trim()}".`);
308
- }
309
- continue;
310
- }
311
- if (m = /^curve\s+([A-Za-z_][\w-]*)\s*(?:\[\s*"((?:[^"\\]|\\.)*)"\s*\])?\s*\{(.*)\}?\s*$/.exec(t)) {
312
- const body = m[3].replace(/\}\s*$/, "");
313
- const entries = body.trim() ? splitOutside(body, ",").map((x) => x.trim()).filter(Boolean) : [];
314
- const ids = radarAxes.map((a) => a.id);
315
- const values = ids.map(() => null);
316
- let k = 0;
317
- for (const e of entries) {
318
- const keyed = /^([A-Za-z_][\w-]*)\s*:\s*(-?\d+(?:\.\d+)?)$/.exec(e);
319
- if (keyed) {
320
- const at = ids.indexOf(keyed[1]);
321
- if (at >= 0) values[at] = Number(keyed[2]);
322
- else lineIssue(i, `curve ${m[1]} names an unknown axis "${keyed[1]}".`);
323
- } else if (/^-?\d+(?:\.\d+)?$/.test(e)) {
324
- if (k < values.length) values[k] = Number(e);
325
- else values.push(Number(e));
326
- k++;
327
- } else lineIssue(i, `could not read "${e}" in curve ${m[1]}.`);
328
- }
329
- series.push({ id: m[1], name: _nullishCoalesce(m[2], () => ( m[1])), values });
330
- continue;
331
- }
332
- if (m = /^(max|min|ticks|graticule|showLegend)\s+(\S+)$/.exec(t)) {
333
- if (m[1] === "max") yAxis.max = Number(m[2]);
334
- else if (m[1] === "min") yAxis.min = Number(m[2]);
335
- else if (m[1] === "showLegend") legendShow = m[2] !== "false";
336
- continue;
337
- }
338
- }
339
- if (!(partial && isLast)) lineIssue(i, `could not read "${t.slice(0, 60)}"; it was skipped.`);
340
- }
341
- let input;
342
- if (family === "pie") {
343
- input = {
344
- type: "pie",
345
- title,
346
- dataLabels: showData,
347
- categories: pieSlices.map((s) => s.label),
348
- series: [{ id: "series-1", name: title || "Values", values: pieSlices.map((s) => s.value) }]
349
- };
350
- } else if (family === "radar") {
351
- input = { type: "radar", title, categories: radarAxes.map((a) => a.label), series, yAxis, legend: { show: legendShow, position: "bottom" } };
352
- } else {
353
- const allBar = series.length > 0 && series.every((s) => s.type === "bar");
354
- const allLine = series.length > 0 && series.every((s) => s.type === "line");
355
- const type = allLine ? "line" : "bar";
356
- const longest = Math.max(0, ...series.map((s) => s.values.length));
357
- const cats = categories.length ? categories : Array.from({ length: longest }, (_, i) => `${i + 1}`);
358
- input = {
359
- type,
360
- title,
361
- orientation: horizontal ? "horizontal" : "vertical",
362
- categories: cats,
363
- xAxis,
364
- yAxis,
365
- // Series that match the chart type do not need their own type.
366
- series: series.map((s) => allBar || allLine || s.type === type ? { ...s, type: void 0 } : s)
367
- };
368
- }
369
- if (meta) {
370
- const { v: _v, series: metaSeries, ...rest } = meta;
371
- Object.assign(input, rest);
372
- if (Array.isArray(metaSeries)) {
373
- const base = _nullishCoalesce(input.series, () => ( []));
374
- input.series = metaSeries.map((extra, i) => ({ ..._nullishCoalesce(base[i], () => ( {})), ..._nullishCoalesce(extra, () => ( {})) }));
375
- }
376
- }
377
- const { spec, issues: specIssues } = _chunkCKRNELPOcjs.normalizeChartSpec.call(void 0, input);
378
- if (!family && !partial && !issues.length) issues.push({ path: "header", message: "The text is empty: expected a chart header (xychart-beta, pie or radar-beta)." });
379
- return { spec, issues: [...issues, ...specIssues], family, hasMeta: !!meta };
380
- }
381
- function findMermaidCharts(text) {
382
- const lines = text.replace(/^/, "").split(/\r?\n/);
383
- const found = [];
384
- const familyOf = (body) => {
385
- const header = body.split(/\r?\n/).find((l) => l.trim() && !l.trim().startsWith("%%") && l.trim() !== "---");
386
- if (!header) return null;
387
- if (/^\s*xychart(-beta)?\b/.test(header)) return "xychart";
388
- if (/^\s*pie\b/.test(header)) return "pie";
389
- if (/^\s*radar(-beta)?\b/.test(header)) return "radar";
390
- return null;
391
- };
392
- for (let i = 0; i < lines.length; i++) {
393
- const open = /^ {0,3}(`{3,}|~{3,})\s*([\w-]*)/.exec(lines[i]);
394
- if (!open) continue;
395
- const fence = open[1];
396
- const info = open[2].toLowerCase();
397
- let end = i + 1;
398
- while (end < lines.length && !new RegExp(`^ {0,3}${fence[0]}{${fence.length},}\\s*$`).test(lines[end])) end++;
399
- const body = lines.slice(i + 1, end).join("\n");
400
- if (!info || info === "mermaid") {
401
- const family = familyOf(body);
402
- if (family) found.push({ source: `${body.trimEnd()}
403
- `, family, line: i + 2 });
404
- }
405
- i = end;
406
- }
407
- if (!found.length) {
408
- const family = familyOf(text);
409
- if (family) found.push({ source: `${text.trim()}
410
- `, family, line: 1 });
411
- }
412
- return found;
413
- }
414
-
415
15
  // src/chart/date.ts
416
16
  var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
417
17
  function parseChartDate(label) {
@@ -1227,8 +827,8 @@ var MAX_CHART_DATA_LABELS = 200;
1227
827
  var MAX_CHART_LINE_POINTS = 2e3;
1228
828
  var MAX_CHART_MARKERS = 1e3;
1229
829
  function scaleInfo(scale, range, format, axis) {
1230
- const logBase = _optionalChain([axis, 'optionalAccess', _10 => _10.scale]) === "log" ? _nullishCoalesce(axis.logBase, () => ( 10)) : void 0;
1231
- const r = _optionalChain([axis, 'optionalAccess', _11 => _11.reverse]) ? [range[1], range[0]] : range;
830
+ const logBase = _optionalChain([axis, 'optionalAccess', _2 => _2.scale]) === "log" ? _nullishCoalesce(axis.logBase, () => ( 10)) : void 0;
831
+ const r = _optionalChain([axis, 'optionalAccess', _3 => _3.reverse]) ? [range[1], range[0]] : range;
1232
832
  const vs = { domain: [scale.min, scale.max], range: r, logBase };
1233
833
  const raw = (v) => chartScaleToPixel(v, vs);
1234
834
  const px = (v) => logBase && !(v > 0) ? r[0] : raw(Math.min(scale.max, Math.max(scale.min, v)));
@@ -1249,7 +849,7 @@ function categoryFrame(ctx, area, input) {
1249
849
  };
1250
850
  const valueLen0 = horizontal ? area.width : area.height;
1251
851
  const valueLabelWidth = primary.axis.show ? estimate(primary, valueLen0) : 0;
1252
- const valueLabelWidth2 = _optionalChain([secondary, 'optionalAccess', _12 => _12.axis, 'access', _13 => _13.show]) ? estimate(secondary, valueLen0) : 0;
852
+ const valueLabelWidth2 = _optionalChain([secondary, 'optionalAccess', _4 => _4.axis, 'access', _5 => _5.show]) ? estimate(secondary, valueLen0) : 0;
1253
853
  const catLabelWidth = Math.max(0, ...labels.map((c) => measure(c, fs)));
1254
854
  let left;
1255
855
  let bottom;
@@ -1373,7 +973,7 @@ function categoryFrame(ctx, area, input) {
1373
973
  marks.push({ kind: "text", key: `axis-title-${horizontal ? "y" : "x"}`, role: "axis-title", x: plot.x + plot.width / 2, y: area.y + area.height, text: bottomTitle, anchor: "middle", baseline: "bottom", size: fs + 1, weight: 500, fill: theme.text, target: horizontal ? yTarget : xTarget });
1374
974
  if (leftTitle)
1375
975
  marks.push({ kind: "text", key: `axis-title-${horizontal ? "x" : "y"}`, role: "axis-title", x: area.x, y: plot.y + plot.height / 2, text: leftTitle, anchor: "middle", baseline: "top", size: fs + 1, weight: 500, fill: theme.text, rotate: -90, target: horizontal ? xTarget : yTarget });
1376
- if (_optionalChain([secondary, 'optionalAccess', _14 => _14.axis, 'access', _15 => _15.title])) {
976
+ if (_optionalChain([secondary, 'optionalAccess', _6 => _6.axis, 'access', _7 => _7.title])) {
1377
977
  const target = { kind: "axis", axis: "y2" };
1378
978
  if (horizontal) marks.push({ kind: "text", key: "axis-title-y2", role: "axis-title", x: plot.x + plot.width / 2, y: area.y, text: secondary.axis.title, anchor: "middle", baseline: "top", size: fs + 1, weight: 500, fill: theme.text, target });
1379
979
  else marks.push({ kind: "text", key: "axis-title-y2", role: "axis-title", x: area.x + area.width, y: plot.y + plot.height / 2, text: secondary.axis.title, anchor: "middle", baseline: "top", size: fs + 1, weight: 500, fill: theme.text, rotate: 90, target });
@@ -1513,7 +1113,7 @@ function layoutCartesian(ctx, area, visible) {
1513
1113
  s.id,
1514
1114
  _chunkCKRNELPOcjs.chartErrorAmounts.call(void 0, s.errorBars, s.values).map((e, i) => {
1515
1115
  const raw = s.values[i];
1516
- const seg = _optionalChain([stack, 'optionalAccess', _16 => _16.segments, 'access', _17 => _17[i]]);
1116
+ const seg = _optionalChain([stack, 'optionalAccess', _8 => _8.segments, 'access', _9 => _9[i]]);
1517
1117
  if (!e || raw === null || stack && !seg) return null;
1518
1118
  const k = seg && percent ? raw ? (seg[1] - seg[0]) / raw : 0 : 1;
1519
1119
  const center = (seg ? seg[1] : raw) + (e.center - raw) * k;
@@ -1535,7 +1135,7 @@ function layoutCartesian(ctx, area, visible) {
1535
1135
  const stack = stacks.get(s.id);
1536
1136
  if (stack) stack.segments.forEach((seg) => seg && (see(seg[0]), see(seg[1])));
1537
1137
  else s.values.forEach((v) => v !== null && see(v));
1538
- _optionalChain([errors, 'access', _18 => _18.get, 'call', _19 => _19(s.id), 'optionalAccess', _20 => _20.forEach, 'call', _21 => _21((e) => e && (see(e.lo), see(e.hi)))]);
1138
+ _optionalChain([errors, 'access', _10 => _10.get, 'call', _11 => _11(s.id), 'optionalAccess', _12 => _12.forEach, 'call', _13 => _13((e) => e && (see(e.lo), see(e.hi)))]);
1539
1139
  }
1540
1140
  if (log) {
1541
1141
  const skipped = group.reduce((t, s) => t + s.values.filter((v) => v !== null && Number.isFinite(v) && v <= 0).length, 0);
@@ -1632,7 +1232,7 @@ function layoutCartesian(ctx, area, visible) {
1632
1232
  } else {
1633
1233
  const curve = _nullishCoalesce(s.curve, () => ( spec.curve));
1634
1234
  const pointValue = (i) => {
1635
- const v = stack ? _nullishCoalesce(_optionalChain([stack, 'access', _22 => _22.segments, 'access', _23 => _23[i], 'optionalAccess', _24 => _24[1]]), () => ( null)) : s.values[i];
1235
+ const v = stack ? _nullishCoalesce(_optionalChain([stack, 'access', _14 => _14.segments, 'access', _15 => _15[i], 'optionalAccess', _16 => _16[1]]), () => ( null)) : s.values[i];
1636
1236
  return v !== null && Number.isFinite(v) && !(log && (v <= 0 || (_nullishCoalesce(s.values[i], () => ( 0))) <= 0)) ? v : null;
1637
1237
  };
1638
1238
  const runs = [];
@@ -1656,7 +1256,7 @@ function layoutCartesian(ctx, area, visible) {
1656
1256
  const drawn = runs.map(thin);
1657
1257
  if (type === "area") {
1658
1258
  const parts = drawn.map((r) => {
1659
- const baseline = stack ? r.map((pt) => ({ c: pt.c, v: valuePx(_nullishCoalesce(_optionalChain([stack, 'access', _25 => _25.segments, 'access', _26 => _26[pt.i], 'optionalAccess', _27 => _27[0]]), () => ( 0))) })).reverse() : [
1259
+ const baseline = stack ? r.map((pt) => ({ c: pt.c, v: valuePx(_nullishCoalesce(_optionalChain([stack, 'access', _17 => _17.segments, 'access', _18 => _18[pt.i], 'optionalAccess', _19 => _19[0]]), () => ( 0))) })).reverse() : [
1660
1260
  { c: r[r.length - 1].c, v: info.zero },
1661
1261
  { c: r[0].c, v: info.zero }
1662
1262
  ];
@@ -1720,7 +1320,7 @@ function layoutCartesian(ctx, area, visible) {
1720
1320
  if (d) errorMarks.push({ kind: "path", key: `error-bar-${s.id}-${i}`, role: "error-bar", d, fill: "none", stroke: type === "bar" ? theme.text : color, strokeWidth: selSeries ? 2 : 1.25, target: seriesTarget, selected: selSeries });
1721
1321
  });
1722
1322
  }
1723
- if (trendsAllowed && _optionalChain([s, 'access', _28 => _28.trendlines, 'optionalAccess', _29 => _29.length])) {
1323
+ if (trendsAllowed && _optionalChain([s, 'access', _20 => _20.trendlines, 'optionalAccess', _21 => _21.length])) {
1724
1324
  const points = [];
1725
1325
  s.values.forEach((v, i) => v !== null && Number.isFinite(v) && points.push([i + 1, v]));
1726
1326
  const toPx = (x, y) => map({ c: catCenter(x - 1), v: info.raw(y) });
@@ -1745,7 +1345,7 @@ function layoutXy(ctx, area, visible) {
1745
1345
  const xLog = xAxis.scale === "log";
1746
1346
  const yLog = yAxis.scale === "log";
1747
1347
  let skipped = 0;
1748
- const pointsOf = (s) => s.values.map((y, i) => ({ i, x: _nullishCoalesce(_optionalChain([s, 'access', _30 => _30.x, 'optionalAccess', _31 => _31[i]]), () => ( (bubble || !s.x ? i + 1 : null))), y, size: _nullishCoalesce(_optionalChain([s, 'access', _32 => _32.sizes, 'optionalAccess', _33 => _33[i]]), () => ( null)) })).filter((p) => {
1348
+ const pointsOf = (s) => s.values.map((y, i) => ({ i, x: _nullishCoalesce(_optionalChain([s, 'access', _22 => _22.x, 'optionalAccess', _23 => _23[i]]), () => ( (bubble || !s.x ? i + 1 : null))), y, size: _nullishCoalesce(_optionalChain([s, 'access', _24 => _24.sizes, 'optionalAccess', _25 => _25[i]]), () => ( null)) })).filter((p) => {
1749
1349
  if (p.x === null || p.y === null || !Number.isFinite(p.x) || !Number.isFinite(p.y)) return false;
1750
1350
  if (xLog && p.x <= 0 || yLog && p.y <= 0) return skipped++, false;
1751
1351
  return true;
@@ -1756,7 +1356,7 @@ function layoutXy(ctx, area, visible) {
1756
1356
  const xErrors = /* @__PURE__ */ new Map();
1757
1357
  for (const { s } of all) {
1758
1358
  if (s.errorBars) yErrors.set(s.id, _chunkCKRNELPOcjs.chartErrorAmounts.call(void 0, s.errorBars, s.values));
1759
- if (s.xErrorBars) xErrors.set(s.id, _chunkCKRNELPOcjs.chartErrorAmounts.call(void 0, s.xErrorBars, s.values.map((_, i) => _nullishCoalesce(_optionalChain([s, 'access', _34 => _34.x, 'optionalAccess', _35 => _35[i]]), () => ( (bubble || !s.x ? i + 1 : null))))));
1359
+ if (s.xErrorBars) xErrors.set(s.id, _chunkCKRNELPOcjs.chartErrorAmounts.call(void 0, s.xErrorBars, s.values.map((_, i) => _nullishCoalesce(_optionalChain([s, 'access', _26 => _26.x, 'optionalAccess', _27 => _27[i]]), () => ( (bubble || !s.x ? i + 1 : null))))));
1760
1360
  }
1761
1361
  let xlo = Infinity;
1762
1362
  let xhi = -Infinity;
@@ -1778,9 +1378,9 @@ function layoutXy(ctx, area, visible) {
1778
1378
  seeX(p.x);
1779
1379
  seeY(p.y);
1780
1380
  if (p.size !== null && p.size > maxSize) maxSize = p.size;
1781
- const ey = _optionalChain([yErrors, 'access', _36 => _36.get, 'call', _37 => _37(s.id), 'optionalAccess', _38 => _38[p.i]]);
1381
+ const ey = _optionalChain([yErrors, 'access', _28 => _28.get, 'call', _29 => _29(s.id), 'optionalAccess', _30 => _30[p.i]]);
1782
1382
  if (ey) seeY(ey.center - ey.minus), seeY(ey.center + ey.plus);
1783
- const ex = _optionalChain([xErrors, 'access', _39 => _39.get, 'call', _40 => _40(s.id), 'optionalAccess', _41 => _41[p.i]]);
1383
+ const ex = _optionalChain([xErrors, 'access', _31 => _31.get, 'call', _32 => _32(s.id), 'optionalAccess', _33 => _33[p.i]]);
1784
1384
  if (ex) seeX(ex.center - ex.minus), seeX(ex.center + ex.plus);
1785
1385
  }
1786
1386
  for (const t of _nullishCoalesce(s.trendlines, () => ( []))) {
@@ -1871,14 +1471,14 @@ function layoutXy(ctx, area, visible) {
1871
1471
  const stroke = color;
1872
1472
  const errorBar = (key, d) => d && errorMarks.push({ kind: "path", key, role: "error-bar", d, fill: "none", stroke, strokeWidth: selSeries ? 2 : 1.25, target: seriesTarget, selected: selSeries });
1873
1473
  for (const p of pts) {
1874
- const ey = _optionalChain([yErrors, 'access', _42 => _42.get, 'call', _43 => _43(s.id), 'optionalAccess', _44 => _44[p.i]]);
1474
+ const ey = _optionalChain([yErrors, 'access', _34 => _34.get, 'call', _35 => _35(s.id), 'optionalAccess', _36 => _36[p.i]]);
1875
1475
  if (ey) {
1876
1476
  const lo = ey.center - ey.minus;
1877
1477
  const hi = ey.center + ey.plus;
1878
1478
  const cap = s.errorBars.cap !== false;
1879
1479
  errorBar(`error-bar-${s.id}-${p.i}`, errorBarPath((q) => [q.c, q.v], xPx(p.x), yPx(lo), yPx(hi), 8, cap && lo < ey.center, cap && hi > ey.center));
1880
1480
  }
1881
- const ex = _optionalChain([xErrors, 'access', _45 => _45.get, 'call', _46 => _46(s.id), 'optionalAccess', _47 => _47[p.i]]);
1481
+ const ex = _optionalChain([xErrors, 'access', _37 => _37.get, 'call', _38 => _38(s.id), 'optionalAccess', _39 => _39[p.i]]);
1882
1482
  if (ex) {
1883
1483
  const lo = ex.center - ex.minus;
1884
1484
  const hi = ex.center + ex.plus;
@@ -1886,7 +1486,7 @@ function layoutXy(ctx, area, visible) {
1886
1486
  errorBar(`error-bar-x-${s.id}-${p.i}`, errorBarPath((q) => [q.v, q.c], yPx(p.y), xPx(lo), xPx(hi), 8, cap && lo < ex.center, cap && hi > ex.center));
1887
1487
  }
1888
1488
  }
1889
- if (_optionalChain([s, 'access', _48 => _48.trendlines, 'optionalAccess', _49 => _49.length])) drawTrendlines(ctx, plot, s, color, pts.map((p) => [p.x, p.y]), (x, y) => [xRaw(x), yRaw(y)], trendLabels, xLog || yLog);
1489
+ if (_optionalChain([s, 'access', _40 => _40.trendlines, 'optionalAccess', _41 => _41.length])) drawTrendlines(ctx, plot, s, color, pts.map((p) => [p.x, p.y]), (x, y) => [xRaw(x), yRaw(y)], trendLabels, xLog || yLog);
1890
1490
  }
1891
1491
  marks.push(...errorMarks, ...labelMarks);
1892
1492
  drawTrendLabels(ctx, plot, trendLabels);
@@ -1904,7 +1504,7 @@ function trendlineLegendItems(spec, color) {
1904
1504
  }
1905
1505
 
1906
1506
  // src/chart/layout-families.ts
1907
- var sliceColor = (ctx, series, i) => _optionalChain([series, 'access', _50 => _50.pointColors, 'optionalAccess', _51 => _51[i]]) || _chunkCKRNELPOcjs.chartSliceColor.call(void 0, ctx.spec, i);
1507
+ var sliceColor = (ctx, series, i) => _optionalChain([series, 'access', _42 => _42.pointColors, 'optionalAccess', _43 => _43[i]]) || _chunkCKRNELPOcjs.chartSliceColor.call(void 0, ctx.spec, i);
1908
1508
  function layoutPie(ctx, area, series) {
1909
1509
  const { spec, theme, marks, fs, selection } = ctx;
1910
1510
  const values = spec.categories.map((_, i) => {
@@ -1934,7 +1534,7 @@ function layoutPie(ctx, area, series) {
1934
1534
  angle = end;
1935
1535
  const target = { kind: "point", seriesId: series.id, index: i };
1936
1536
  const selected = ctx.isSelected(target) || ctx.isSelected({ kind: "category", index: i });
1937
- const seriesSel = _optionalChain([selection, 'optionalAccess', _52 => _52.kind]) === "series" && selection.seriesId === series.id;
1537
+ const seriesSel = _optionalChain([selection, 'optionalAccess', _44 => _44.kind]) === "series" && selection.seriesId === series.id;
1938
1538
  const mid = (start + end) / 2;
1939
1539
  const off = selected ? 6 : 0;
1940
1540
  const ox = cx + off * Math.sin(mid);
@@ -2243,7 +1843,7 @@ function legendItems(spec, ctx) {
2243
1843
  const first = spec.series.find((s) => !s.hidden);
2244
1844
  return spec.categories.map((c, i) => ({
2245
1845
  label: c,
2246
- color: _optionalChain([first, 'optionalAccess', _53 => _53.pointColors, 'optionalAccess', _54 => _54[i]]) || _chunkCKRNELPOcjs.chartSliceColor.call(void 0, spec, i),
1846
+ color: _optionalChain([first, 'optionalAccess', _45 => _45.pointColors, 'optionalAccess', _46 => _46[i]]) || _chunkCKRNELPOcjs.chartSliceColor.call(void 0, spec, i),
2247
1847
  target: { kind: "category", index: i },
2248
1848
  faded: false,
2249
1849
  selected: ctx.isSelected({ kind: "category", index: i }),
@@ -2296,7 +1896,7 @@ function layoutChart(spec, options) {
2296
1896
  warnings,
2297
1897
  selection,
2298
1898
  isSelected: (target) => sameChartSelection(selection, target),
2299
- seriesSelected: (id) => _optionalChain([selection, 'optionalAccess', _55 => _55.kind]) === "series" && selection.seriesId === id,
1899
+ seriesSelected: (id) => _optionalChain([selection, 'optionalAccess', _47 => _47.kind]) === "series" && selection.seriesId === id,
2300
1900
  outline: (key, box) => marks.push({ kind: "rect", key, role: "selection", ...box, fill: "none", stroke: theme.selection, strokeWidth: 1, radius: 3 }),
2301
1901
  warn: (code, message) => {
2302
1902
  if (!warnings.some((w) => w.code === code && w.message === message)) warnings.push({ code, message });
@@ -2453,9 +2053,9 @@ function layoutLegend(items, position, area, ctx, legendSelected) {
2453
2053
  function addReadout(ctx, readout, plot) {
2454
2054
  const { spec, marks, theme, fs, measure } = ctx;
2455
2055
  const series = spec.series.find((s) => s.id === readout.seriesId);
2456
- const value = _optionalChain([series, 'optionalAccess', _56 => _56.values, 'access', _57 => _57[readout.index]]);
2056
+ const value = _optionalChain([series, 'optionalAccess', _48 => _48.values, 'access', _49 => _49[readout.index]]);
2457
2057
  if (!series || value === null || value === void 0) return;
2458
- const mark = marks.find((m) => (m.role === "point" || m.role === "bar") && _optionalChain([m, 'access', _58 => _58.target, 'optionalAccess', _59 => _59.kind]) === "point" && m.target.seriesId === readout.seriesId && m.target.index === readout.index);
2058
+ const mark = marks.find((m) => (m.role === "point" || m.role === "bar") && _optionalChain([m, 'access', _50 => _50.target, 'optionalAccess', _51 => _51.kind]) === "point" && m.target.seriesId === readout.seriesId && m.target.index === readout.index);
2459
2059
  if (!mark) return;
2460
2060
  let ax;
2461
2061
  let ay;
@@ -2467,7 +2067,7 @@ function addReadout(ctx, readout, plot) {
2467
2067
  ay = mark.cy - mark.r;
2468
2068
  } else return;
2469
2069
  const axis = series.axis === "secondary" ? spec.yAxis2 : spec.yAxis;
2470
- const x = _optionalChain([series, 'access', _60 => _60.x, 'optionalAccess', _61 => _61[readout.index]]);
2070
+ const x = _optionalChain([series, 'access', _52 => _52.x, 'optionalAccess', _53 => _53[readout.index]]);
2471
2071
  const text = x !== null && x !== void 0 && _chunkCKRNELPOcjs.chartFamily.call(void 0, spec) === "xy" ? `${formatChartNumber(x, spec.xAxis.format)}, ${formatChartNumber(value, axis.format)}` : `${_nullishCoalesce(spec.categories[readout.index], () => ( ""))}: ${formatChartNumber(value, _nullishCoalesce(series.format, () => ( axis.format)))}`;
2472
2072
  const w = measure(text, fs, 600) + 10;
2473
2073
  const h = fs + 8;
@@ -2517,7 +2117,7 @@ function chartXAtPoint(scene, point) {
2517
2117
  if (!scale) return void 0;
2518
2118
  return chartScaleFromPixel(point.x, scale);
2519
2119
  }
2520
- function snapChartValue(scene, value, step, ticks = _nullishCoalesce(_optionalChain([scene, 'access', _62 => _62.valueScale, 'optionalAccess', _63 => _63.ticks]), () => ( [])), logBase) {
2120
+ function snapChartValue(scene, value, step, ticks = _nullishCoalesce(_optionalChain([scene, 'access', _54 => _54.valueScale, 'optionalAccess', _55 => _55.ticks]), () => ( [])), logBase) {
2521
2121
  if (logBase && step === void 0) return value > 0 && Number.isFinite(value) ? Number(value.toPrecision(2)) : value;
2522
2122
  const unit = _nullishCoalesce(step, () => ( (ticks.length > 1 ? (ticks[1] - ticks[0]) / 10 : 1)));
2523
2123
  if (!(unit > 0)) return value;
@@ -2525,1027 +2125,6 @@ function snapChartValue(scene, value, step, ticks = _nullishCoalesce(_optionalCh
2525
2125
  return Number(snapped.toPrecision(12));
2526
2126
  }
2527
2127
 
2528
- // src/chart/summary.ts
2529
- function summarizeChart(spec, size = { width: 640, height: 400 }, scene = layoutChart(spec, { ...size, selection: null })) {
2530
- const visible = spec.series.filter((s) => !s.hidden);
2531
- const series = visible.map((s) => summarizeChartSeries(s, spec.categories));
2532
- const colors = _chunkCKRNELPOcjs.chartColorsByCategory.call(void 0, spec.type) ? spec.categories.map((_, i) => _optionalChain([visible, 'access', _64 => _64[0], 'optionalAccess', _65 => _65.pointColors, 'optionalAccess', _66 => _66[i]]) || _chunkCKRNELPOcjs.chartSliceColor.call(void 0, spec, i)) : visible.map((s) => _chunkCKRNELPOcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s)));
2533
- const check = _chunkCKRNELPOcjs.checkChartColors.call(void 0, colors, { surface: scene.theme.surface.startsWith("var(") ? "#ffffff" : scene.theme.surface });
2534
- const axis = (s, a) => s ? { min: s.domain[0], max: s.domain[1], ticks: [...s.ticks], ...s.logBase ? { logBase: s.logBase } : {}, ...a.reverse ? { reversed: true } : {} } : void 0;
2535
- return {
2536
- type: spec.type,
2537
- title: spec.title,
2538
- categories: spec.categories.length,
2539
- series,
2540
- ...scene.valueScale ? { valueAxis: axis(scene.valueScale, spec.yAxis) } : {},
2541
- ...scene.valueScale2 ? { secondaryAxis: axis(scene.valueScale2, spec.yAxis2) } : {},
2542
- ...scene.xScale ? { xAxis: axis(scene.xScale, spec.xAxis) } : {},
2543
- warnings: scene.warnings,
2544
- colors: { ok: check.ok, issues: check.issues },
2545
- description: describeChart(spec, series)
2546
- };
2547
- }
2548
- function describeChart(spec, series = spec.series.filter((s) => !s.hidden).map((s) => summarizeChartSeries(s, spec.categories))) {
2549
- const kind = _chunkCKRNELPOcjs.CHART_TYPE_LABELS[spec.type].toLowerCase();
2550
- const parts = [];
2551
- const family = _chunkCKRNELPOcjs.chartFamily.call(void 0, spec);
2552
- const format = spec.yAxis.format;
2553
- const fmt = (v) => v === null ? "blank" : formatChartNumber(v, format);
2554
- const head = spec.title ? `${spec.title}: a ${kind} chart` : `A ${kind} chart`;
2555
- if (!series.length || series.every((s) => s.count === 0)) return `${head} with no data.`;
2556
- if (family === "pie" || family === "funnel" || family === "treemap") {
2557
- const s = series[0];
2558
- const total = _nullishCoalesce(s.sum, () => ( 0));
2559
- const share = (v) => total && v !== null ? ` (${Math.round(v / total * 100)}%)` : "";
2560
- parts.push(`${head} of ${s.name} across ${spec.categories.length} categories, totalling ${fmt(s.sum)}.`);
2561
- parts.push(`The largest is ${s.maxAt} at ${fmt(s.max)}${share(s.max)}; the smallest is ${s.minAt} at ${fmt(s.min)}${share(s.min)}.`);
2562
- return parts.join(" ");
2563
- }
2564
- if (family === "waterfall") {
2565
- const s = spec.series.find((x) => !x.hidden);
2566
- const steps = s.values.filter((v, i) => v !== null && Number.isFinite(v) && !/\b(sub)?total\b/i.test(_nullishCoalesce(spec.categories[i], () => ( ""))));
2567
- const total = steps.reduce((t, v) => t + v, 0);
2568
- const up = steps.filter((v) => v > 0).length;
2569
- const down = steps.filter((v) => v < 0).length;
2570
- return `${head} of ${s.name}: ${up} increase${up === 1 ? "" : "s"} and ${down} decrease${down === 1 ? "" : "s"} over ${spec.categories.length} steps, ending at a total of ${fmt(total)}.`;
2571
- }
2572
- if (family === "histogram" || family === "box") {
2573
- parts.push(`${head} of ${series.map((s) => s.name).join(", ")}.`);
2574
- for (const s of series) parts.push(`${s.name}: ${s.count} values from ${fmt(s.min)} to ${fmt(s.max)}, mean ${fmt(s.mean)}.`);
2575
- return parts.join(" ");
2576
- }
2577
- const what = family === "xy" ? `${series.length} series of points` : `${series.length} series over ${spec.categories.length} categories${spec.categories.length ? ` (${spec.categories[0]} to ${spec.categories[spec.categories.length - 1]})` : ""}`;
2578
- parts.push(`${head} showing ${what}.`);
2579
- for (const s of series.slice(0, 8)) {
2580
- if (!s.count) {
2581
- parts.push(`${s.name} has no values.`);
2582
- continue;
2583
- }
2584
- const pct = s.change !== null ? Math.round(Math.abs(s.change) * 100) : 0;
2585
- const trend = s.trend && s.trend !== "flat" ? `, ${s.trend}${pct ? `, ${s.change > 0 ? "up" : "down"} ${pct}% from first to last` : ""}` : s.trend === "flat" ? ", roughly flat" : "";
2586
- parts.push(`${s.name} ranges from ${fmt(s.min)} (${s.minAt}) to ${fmt(s.max)} (${s.maxAt})${trend}.`);
2587
- }
2588
- if (series.length > 8) parts.push(`${series.length - 8} more series are not described.`);
2589
- const axes = [];
2590
- for (const [a, name] of [
2591
- [spec.yAxis, "value axis"],
2592
- [spec.yAxis2, "secondary value axis"],
2593
- [spec.xAxis, family === "xy" ? "x axis" : "category axis"]
2594
- ]) {
2595
- if (name === "secondary value axis" && !spec.series.some((s) => !s.hidden && s.axis === "secondary")) continue;
2596
- const log = a.scale === "log" && name !== "category axis";
2597
- if (log || a.reverse) axes.push(`The ${name} is ${[log ? `logarithmic (base ${_nullishCoalesce(a.logBase, () => ( 10))})` : "", a.reverse ? "reversed" : ""].filter(Boolean).join(" and ")}.`);
2598
- }
2599
- parts.push(...axes);
2600
- for (const s of spec.series.filter((x) => !x.hidden && (x.errorBars || x.xErrorBars)).slice(0, 8)) {
2601
- const bars = [s.errorBars ? _chunkCKRNELPOcjs.describeChartErrorBars.call(void 0, s.errorBars) : "", s.xErrorBars ? `x ${_chunkCKRNELPOcjs.describeChartErrorBars.call(void 0, s.xErrorBars)}` : ""].filter(Boolean).join(" and ");
2602
- parts.push(`${s.name} has ${bars}.`);
2603
- }
2604
- return parts.join(" ");
2605
- }
2606
-
2607
- // src/chart/svg.ts
2608
- function chartTextBaseline(baseline) {
2609
- return baseline === "top" ? "hanging" : baseline === "middle" ? "central" : "text-after-edge";
2610
- }
2611
- function chartTextTransform(mark) {
2612
- return mark.rotate ? `rotate(${mark.rotate} ${mark.x} ${mark.y})` : void 0;
2613
- }
2614
- var escape = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2615
- function markParts(mark, fontFamily) {
2616
- const common = { opacity: mark.opacity, "data-role": mark.role };
2617
- const parts = (tag, values, text) => ({
2618
- tag,
2619
- attrs: Object.fromEntries(Object.entries(values).filter(([, v]) => v !== void 0 && v !== "").map(([k, v]) => [k, String(v)])),
2620
- text
2621
- });
2622
- switch (mark.kind) {
2623
- case "rect":
2624
- return parts("rect", { x: mark.x, y: mark.y, width: mark.width, height: mark.height, rx: mark.radius, fill: mark.fill, stroke: mark.stroke, "stroke-width": mark.strokeWidth, ...common });
2625
- case "path":
2626
- return parts("path", { d: mark.d, fill: _nullishCoalesce(mark.fill, () => ( "none")), stroke: mark.stroke, "stroke-width": mark.strokeWidth, "stroke-dasharray": mark.dash, "stroke-linejoin": "round", "stroke-linecap": "round", ...common });
2627
- case "circle":
2628
- return parts("circle", { cx: mark.cx, cy: mark.cy, r: mark.r, fill: mark.fill, stroke: mark.stroke, "stroke-width": mark.strokeWidth, ...common });
2629
- case "line":
2630
- return parts("line", { x1: mark.x1, y1: mark.y1, x2: mark.x2, y2: mark.y2, stroke: mark.stroke, "stroke-width": mark.strokeWidth, "stroke-dasharray": mark.dash, ...common });
2631
- case "text":
2632
- return parts(
2633
- "text",
2634
- {
2635
- x: mark.x,
2636
- y: mark.y,
2637
- "text-anchor": mark.anchor,
2638
- "dominant-baseline": chartTextBaseline(mark.baseline),
2639
- "font-size": mark.size,
2640
- "font-weight": mark.weight,
2641
- "font-family": fontFamily,
2642
- fill: mark.fill,
2643
- transform: chartTextTransform(mark),
2644
- ...common
2645
- },
2646
- mark.text
2647
- );
2648
- }
2649
- }
2650
- function markToSvg(mark, fontFamily) {
2651
- const { tag, attrs, text } = markParts(mark, fontFamily);
2652
- const attrText = Object.entries(attrs).map(([k, v]) => ` ${k}="${escape(v)}"`).join("");
2653
- return text === void 0 ? `<${tag}${attrText}/>` : `<${tag}${attrText}>${escape(text)}</${tag}>`;
2654
- }
2655
- function chartSceneToSvg(scene, options = {}) {
2656
- const hide = _nullishCoalesce(options.hideSelection, () => ( true));
2657
- const font = scene.theme.fontFamily;
2658
- const body = scene.marks.filter((m) => !(hide && m.role === "selection")).map((m) => markToSvg(m, font)).join("");
2659
- const label = options.ariaLabel;
2660
- const desc = options.description;
2661
- const labelled = [label ? "chart-title" : "", desc ? "chart-desc" : ""].filter(Boolean).join(" ");
2662
- const head = `${label ? `<title id="chart-title">${escape(label)}</title>` : ""}${desc ? `<desc id="chart-desc">${escape(desc)}</desc>` : ""}`;
2663
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${scene.width}" height="${scene.height}" viewBox="0 0 ${scene.width} ${scene.height}" role="img"${label ? ` aria-label="${escape(label)}"` : ""}${labelled ? ` aria-labelledby="${labelled}"` : ""}>${head}${body}</svg>`;
2664
- }
2665
- function chartToSvg(spec, options) {
2666
- const scene = layoutChart(spec, { ...options, selection: null });
2667
- return chartSceneToSvg(scene, { ...options, ariaLabel: _nullishCoalesce(options.ariaLabel, () => ( (spec.title || void 0))), description: _nullishCoalesce(options.description, () => ( describeChart(spec))) });
2668
- }
2669
- var SVG_NS = "http://www.w3.org/2000/svg";
2670
- function chartSceneToDomSpec(scene, options = {}) {
2671
- const hide = _nullishCoalesce(options.hideSelection, () => ( true));
2672
- const font = scene.theme.fontFamily;
2673
- const tag = (name) => `${SVG_NS} ${name}`;
2674
- const children = [];
2675
- if (options.ariaLabel) children.push([tag("title"), {}, options.ariaLabel]);
2676
- if (options.description) children.push([tag("desc"), {}, options.description]);
2677
- for (const mark of scene.marks) {
2678
- if (hide && mark.role === "selection") continue;
2679
- const { tag: name, attrs: attrs2, text } = markParts(mark, font);
2680
- children.push(text === void 0 ? [tag(name), attrs2] : [tag(name), attrs2, text]);
2681
- }
2682
- const attrs = {
2683
- width: String(scene.width),
2684
- height: String(scene.height),
2685
- viewBox: `0 0 ${scene.width} ${scene.height}`,
2686
- role: "img",
2687
- ...options.ariaLabel ? { "aria-label": options.ariaLabel } : {},
2688
- ...options.attrs
2689
- };
2690
- return [tag("svg"), attrs, ...children];
2691
- }
2692
-
2693
- // src/chart/presets.ts
2694
- var CHART_LAYOUT_PRESETS = [
2695
- { id: "standard", label: "Standard", description: "Legend at the bottom, value grid lines, no data labels.", patch: { legend: { show: true, position: "bottom" }, dataLabels: false, xAxis: { gridlines: false, show: true }, yAxis: { gridlines: true, show: true } } },
2696
- { id: "minimal", label: "Minimal", description: "No legend, grid lines or axis titles; the data speaks.", patch: { legend: { show: false }, dataLabels: false, xAxis: { gridlines: false, title: "" }, yAxis: { gridlines: false, title: "" } } },
2697
- { id: "labelled", label: "Labelled", description: "Values written on the marks, no value axis or grid lines.", patch: { dataLabels: true, labelPosition: "auto", yAxis: { show: false, gridlines: false }, xAxis: { gridlines: false } } },
2698
- { id: "legend-right", label: "Legend on the right", description: "Legend beside the plot, value grid lines.", patch: { legend: { show: true, position: "right" }, yAxis: { gridlines: true } } },
2699
- { id: "presentation", label: "Presentation", description: "Legend on top, data labels, light grid.", patch: { legend: { show: true, position: "top" }, dataLabels: true, yAxis: { gridlines: true }, xAxis: { gridlines: false } } },
2700
- { id: "analysis", label: "Analysis", description: "Grid lines on both axes, point markers, legend at the bottom.", patch: { legend: { show: true, position: "bottom" }, points: true, dataLabels: false, xAxis: { gridlines: true }, yAxis: { gridlines: true } } }
2701
- ];
2702
- function monochromeChartPalette(base, count = 6) {
2703
- const steps = count <= 1 ? [0] : Array.from({ length: count }, (_, i) => -0.45 + 1.05 * i / (count - 1));
2704
- return steps.map((t) => _chunkCKRNELPOcjs.shadeChartColor.call(void 0, base, t));
2705
- }
2706
- var CHART_COLOR_SCHEMES = [
2707
- { id: "default", label: "Default", description: "Ten distinct colours, readable on light and dark backgrounds.", palette: _chunkCKRNELPOcjs.DEFAULT_CHART_PALETTE },
2708
- { id: "okabe-ito", label: "Okabe\u2013Ito", description: "Eight colours chosen to stay distinct with colour-blindness.", palette: ["#e69f00", "#56b4e9", "#009e73", "#f0e442", "#0072b2", "#d55e00", "#cc79a7", "#000000"] },
2709
- { id: "office", label: "Office", description: "The six accent colours of the default Office theme.", palette: ["#4472c4", "#ed7d31", "#a5a5a5", "#ffc000", "#5b9bd5", "#70ad47"] },
2710
- { id: "warm", label: "Warm", description: "Reds, oranges and yellows.", palette: ["#b2182b", "#d6604d", "#f4a582", "#e08214", "#fdb863", "#8c510a"] },
2711
- { id: "cool", label: "Cool", description: "Blues, teals and purples.", palette: ["#2166ac", "#4393c3", "#35978f", "#80cdc1", "#762a83", "#9970ab"] },
2712
- { id: "monochrome-blue", label: "Monochrome blue", description: "Shades of one blue.", palette: monochromeChartPalette("#4269d0") },
2713
- { id: "monochrome-green", label: "Monochrome green", description: "Shades of one green.", palette: monochromeChartPalette("#3ca951") },
2714
- { id: "monochrome-orange", label: "Monochrome orange", description: "Shades of one orange.", palette: monochromeChartPalette("#e8710a") },
2715
- { id: "monochrome-gray", label: "Monochrome grey", description: "Shades of grey.", palette: monochromeChartPalette("#6b7280") }
2716
- ];
2717
- function chartPresetPatch(options) {
2718
- const patch = {};
2719
- if (options.layout !== void 0) {
2720
- const preset = CHART_LAYOUT_PRESETS.find((p) => p.id === options.layout);
2721
- if (!preset) return { ok: false, message: `There is no layout "${options.layout}"; expected one of ${CHART_LAYOUT_PRESETS.map((p) => `"${p.id}"`).join(", ")}.` };
2722
- Object.assign(patch, preset.patch);
2723
- }
2724
- if (options.colors !== void 0) {
2725
- const scheme = CHART_COLOR_SCHEMES.find((p) => p.id === options.colors);
2726
- if (!scheme) return { ok: false, message: `There is no colour scheme "${options.colors}"; expected one of ${CHART_COLOR_SCHEMES.map((p) => `"${p.id}"`).join(", ")}.` };
2727
- patch.palette = [...scheme.palette];
2728
- }
2729
- if (options.layout === void 0 && options.colors === void 0) return { ok: false, message: "Name a layout, a colour scheme or both." };
2730
- return { ok: true, patch };
2731
- }
2732
-
2733
- // src/chart/controller.ts
2734
- function sameChartJson(a, b) {
2735
- if (a === b) return true;
2736
- if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return Number.isNaN(a) && Number.isNaN(b);
2737
- if (Array.isArray(a) !== Array.isArray(b)) return false;
2738
- if (Array.isArray(a)) {
2739
- const bb = b;
2740
- return a.length === bb.length && a.every((v, i) => sameChartJson(v, bb[i]));
2741
- }
2742
- const ao = a;
2743
- const bo = b;
2744
- const keys = Object.keys(ao).filter((k) => ao[k] !== void 0);
2745
- if (keys.length !== Object.keys(bo).filter((k) => bo[k] !== void 0).length) return false;
2746
- return keys.every((k) => Object.prototype.hasOwnProperty.call(bo, k) && sameChartJson(ao[k], bo[k]));
2747
- }
2748
- var fail = (message, issues) => ({ ok: false, message, ...issues ? { issues } : {} });
2749
- var ChartController = (_class = class {
2750
-
2751
-
2752
- __init() {this.selection = null}
2753
- __init2() {this.issues = []}
2754
- __init3() {this.gestureKey = null}
2755
- __init4() {this.gestureCount = 0}
2756
- __init5() {this.gestureStart = null}
2757
- __init6() {this.revision = 0}
2758
- __init7() {this.applying = false}
2759
-
2760
- __init8() {this.listeners = /* @__PURE__ */ new Set()}
2761
-
2762
-
2763
- constructor(options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);
2764
- this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
2765
- this.onChange = options.onChange;
2766
- const { spec, issues } = _chunkCKRNELPOcjs.normalizeChartSpec.call(void 0, _nullishCoalesce(options.spec, () => ( {})));
2767
- this.spec = spec;
2768
- this.issues = issues;
2769
- this.state = this.computeState();
2770
- this.connect();
2771
- }
2772
- connect() {
2773
- _optionalChain([this, 'access', _67 => _67.unsubscribeHistory, 'optionalCall', _68 => _68()]);
2774
- this.unsubscribeHistory = this.history.subscribe((h) => {
2775
- const s = this.state;
2776
- if (this.applying || s.canUndo === h.canUndo && s.canRedo === h.canRedo && s.undoLabel === h.undoName && s.redoLabel === h.redoName) return;
2777
- this.notify(false);
2778
- });
2779
- }
2780
- // -------------------------------------------------------------------------
2781
- // State
2782
- // -------------------------------------------------------------------------
2783
- /** The current snapshot (stable until the next change). */
2784
- __init9() {this.getState = () => this.state}
2785
- /** Calls `listener` after every change. Returns an unsubscribe function. */
2786
- __init10() {this.subscribe = (listener) => {
2787
- if (!this.unsubscribeHistory) this.connect();
2788
- this.listeners.add(listener);
2789
- return () => {
2790
- this.listeners.delete(listener);
2791
- };
2792
- }}
2793
- /** The current spec (do not mutate; edit through the controller). */
2794
- getSpec() {
2795
- return this.spec;
2796
- }
2797
- getSeries(id) {
2798
- return this.spec.series.find((s) => s.id === id);
2799
- }
2800
- computeState() {
2801
- const h = this.history.getState();
2802
- const sel = this.selection;
2803
- const seriesId = _optionalChain([sel, 'optionalAccess', _69 => _69.kind]) === "series" || _optionalChain([sel, 'optionalAccess', _70 => _70.kind]) === "point" || _optionalChain([sel, 'optionalAccess', _71 => _71.kind]) === "trendline" ? sel.seriesId : null;
2804
- return {
2805
- spec: this.spec,
2806
- selection: sel,
2807
- selectedSeries: seriesId ? _nullishCoalesce(this.spec.series.find((s) => s.id === seriesId), () => ( null)) : null,
2808
- selectedIndex: _optionalChain([sel, 'optionalAccess', _72 => _72.kind]) === "point" || _optionalChain([sel, 'optionalAccess', _73 => _73.kind]) === "category" ? sel.index : null,
2809
- issues: this.issues,
2810
- gesture: this.gestureKey,
2811
- canUndo: h.canUndo,
2812
- canRedo: h.canRedo,
2813
- undoLabel: h.undoName,
2814
- redoLabel: h.redoName,
2815
- revision: this.revision
2816
- };
2817
- }
2818
- notify(specChanged) {
2819
- this.revision++;
2820
- this.state = this.computeState();
2821
- if (specChanged) _optionalChain([this, 'access', _74 => _74.onChange, 'optionalCall', _75 => _75(this.spec)]);
2822
- const state = this.state;
2823
- this.listeners.forEach((listener) => {
2824
- try {
2825
- listener(state);
2826
- } catch (error) {
2827
- console.error("[ChartController] Error in state listener:", error);
2828
- }
2829
- });
2830
- }
2831
- // -------------------------------------------------------------------------
2832
- // Changes: every edit records a before / after snapshot of the spec
2833
- // -------------------------------------------------------------------------
2834
- /** Drops a selection that no longer points at anything. */
2835
- validSelection(sel, spec) {
2836
- if (!sel) return null;
2837
- switch (sel.kind) {
2838
- case "series":
2839
- return spec.series.some((s) => s.id === sel.seriesId) ? sel : null;
2840
- case "point":
2841
- return spec.series.some((s) => s.id === sel.seriesId) && sel.index < spec.categories.length ? sel : null;
2842
- case "category":
2843
- return sel.index < spec.categories.length ? sel : null;
2844
- case "trendline":
2845
- return _optionalChain([spec, 'access', _76 => _76.series, 'access', _77 => _77.find, 'call', _78 => _78((s) => s.id === sel.seriesId), 'optionalAccess', _79 => _79.trendlines, 'optionalAccess', _80 => _80[sel.index]]) ? sel : null;
2846
- case "title":
2847
- return spec.title ? sel : null;
2848
- case "subtitle":
2849
- return spec.subtitle ? sel : null;
2850
- default:
2851
- return sel;
2852
- }
2853
- }
2854
- applySpec(spec) {
2855
- this.applying = true;
2856
- try {
2857
- this.spec = spec;
2858
- this.selection = this.validSelection(this.selection, spec);
2859
- } finally {
2860
- this.applying = false;
2861
- }
2862
- this.notify(true);
2863
- }
2864
- commit(label, next, select) {
2865
- const before = this.spec;
2866
- const hadIssues = this.issues.length > 0;
2867
- this.issues = [];
2868
- if (sameChartJson(before, next)) {
2869
- const reselect = select !== void 0 && !sameChartSelection(this.selection, select) && !(select === null && this.selection === null);
2870
- if (reselect) this.selection = select;
2871
- if (reselect || hadIssues) this.notify(false);
2872
- return;
2873
- }
2874
- const command = {
2875
- name: label,
2876
- tag: "chart",
2877
- execute: () => this.applySpec(next),
2878
- undo: () => this.applySpec(before),
2879
- redo: () => this.applySpec(next)
2880
- };
2881
- if (select !== void 0) this.selection = select;
2882
- if (this.gestureKey !== null) this.history.executeMerged(command, { key: `chart.gesture:${this.gestureKey}:${this.gestureCount}` });
2883
- else this.history.execute(command);
2884
- }
2885
- reject(message, issues = []) {
2886
- this.issues = issues.length ? issues : [{ path: "", message }];
2887
- this.notify(false);
2888
- return fail(message, issues.length ? issues : void 0);
2889
- }
2890
- /** A copy of the spec with `edit` applied (series and categories copied as needed). */
2891
- edited(edit) {
2892
- const draft = _chunkCKRNELPOcjs.cloneChartSpec.call(void 0, this.spec);
2893
- edit(draft);
2894
- return draft;
2895
- }
2896
- unknownSeries(id) {
2897
- const ids = this.spec.series.map((s) => `"${s.id}"`);
2898
- return fail(`There is no series "${id}"; ${ids.length ? `expected one of ${ids.join(", ")}` : "the chart has no series yet"}.`);
2899
- }
2900
- badIndex(index) {
2901
- const n = this.spec.categories.length;
2902
- return fail(n ? `Category index ${index} is out of range; expected 0 to ${n - 1}.` : "The chart has no categories yet.");
2903
- }
2904
- // -------------------------------------------------------------------------
2905
- // Presentation
2906
- // -------------------------------------------------------------------------
2907
- /**
2908
- * Changes any presentation or data fields as one undo step. All-or-nothing: if anything
2909
- * is invalid, nothing changes and every problem is returned (and shown in `state.issues`).
2910
- */
2911
- update(patch, options = {}) {
2912
- const s = this.spec;
2913
- const merged = {
2914
- ...s,
2915
- ...patch,
2916
- xAxis: patch.xAxis ? { ...s.xAxis, ...patch.xAxis } : s.xAxis,
2917
- yAxis: patch.yAxis ? { ...s.yAxis, ...patch.yAxis } : s.yAxis,
2918
- yAxis2: patch.yAxis2 ? { ...s.yAxis2, ...patch.yAxis2 } : s.yAxis2,
2919
- legend: patch.legend ? { ...s.legend, ...patch.legend } : s.legend,
2920
- version: 1
2921
- };
2922
- const { spec, issues } = _chunkCKRNELPOcjs.normalizeChartSpec.call(void 0, merged);
2923
- if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
2924
- if (JSON.stringify(spec) === JSON.stringify(s)) return { ok: true };
2925
- this.commit(_nullishCoalesce(options.label, () => ( updateLabel(patch))), spec);
2926
- return { ok: true };
2927
- }
2928
- setType(type) {
2929
- return this.update({ type }, { label: `Change chart type` });
2930
- }
2931
- setTitle(title) {
2932
- return this.update({ title }, { label: "Edit title" });
2933
- }
2934
- setSubtitle(subtitle) {
2935
- return this.update({ subtitle }, { label: "Edit subtitle" });
2936
- }
2937
- /** Changes the category (`x`), value (`y`) or secondary value (`y2`) axis. */
2938
- setAxis(axis, patch) {
2939
- const key = axis === "x" ? "xAxis" : axis === "y" ? "yAxis" : "yAxis2";
2940
- return this.update({ [key]: patch }, { label: `Change ${axis === "x" ? "category" : axis === "y" ? "value" : "secondary value"} axis` });
2941
- }
2942
- /**
2943
- * Applies a named quick layout and / or colour scheme (see `CHART_LAYOUT_PRESETS` and
2944
- * `CHART_COLOR_SCHEMES`) as one undo step.
2945
- */
2946
- applyPreset(options) {
2947
- const preset = chartPresetPatch(options);
2948
- if (!preset.ok) return this.reject(preset.message);
2949
- const label = options.layout && options.colors ? "Apply chart style" : options.layout ? "Apply quick layout" : "Change colours";
2950
- return this.update(preset.patch, { label });
2951
- }
2952
- setLegend(patch) {
2953
- return this.update({ legend: patch }, { label: "Change legend" });
2954
- }
2955
- // -------------------------------------------------------------------------
2956
- // Data
2957
- // -------------------------------------------------------------------------
2958
- /**
2959
- * Replaces categories and / or series as one undo step. Values are validated and padded
2960
- * to the categories; all-or-nothing like `update`.
2961
- */
2962
- setData(data, options = {}) {
2963
- return this.update({ ...data.categories ? { categories: data.categories } : {}, ...data.series ? { series: data.series } : {} }, { label: _nullishCoalesce(options.label, () => ( "Edit chart data")) });
2964
- }
2965
- /**
2966
- * Sets one value (numbers, numeric text such as `1,200` or `12%`, or blank for a gap).
2967
- * One undo step, or part of the current gesture.
2968
- */
2969
- setValue(seriesId, index, value, options = {}) {
2970
- const series = this.getSeries(seriesId);
2971
- if (!series) return this.unknownSeries(seriesId);
2972
- if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
2973
- const parsed = _chunkCKRNELPOcjs.parseChartValue.call(void 0, value);
2974
- if (parsed === void 0) return this.reject(`"${String(value)}" is not a number; enter a number or leave the cell blank.`, [{ path: `${seriesId}[${index}]`, message: `"${String(value)}" is not a number.` }]);
2975
- if (series.values[index] === parsed) {
2976
- if (this.issues.length) {
2977
- this.issues = [];
2978
- this.notify(false);
2979
- }
2980
- return { ok: true };
2981
- }
2982
- const next = this.edited((d) => {
2983
- d.series.find((s) => s.id === seriesId).values[index] = parsed;
2984
- });
2985
- this.commit(_nullishCoalesce(options.label, () => ( `Edit ${series.name} \xB7 ${this.spec.categories[index]}`)), next);
2986
- return { ok: true };
2987
- }
2988
- /** Several values as one undo step (all-or-nothing). */
2989
- setValues(cells, options = {}) {
2990
- const issues = [];
2991
- const parsed = [];
2992
- for (const cell of cells) {
2993
- if (!this.getSeries(cell.seriesId)) issues.push({ path: cell.seriesId, message: this.unknownSeries(cell.seriesId).message });
2994
- else if (!Number.isInteger(cell.index) || cell.index < 0 || cell.index >= this.spec.categories.length) issues.push({ path: `${cell.seriesId}[${cell.index}]`, message: this.badIndex(cell.index).message });
2995
- else {
2996
- const value = _chunkCKRNELPOcjs.parseChartValue.call(void 0, cell.value);
2997
- if (value === void 0) issues.push({ path: `${cell.seriesId}[${cell.index}]`, message: `"${String(cell.value)}" is not a number.` });
2998
- else parsed.push({ seriesId: cell.seriesId, index: cell.index, value });
2999
- }
3000
- }
3001
- if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
3002
- const next = this.edited((d) => {
3003
- for (const cell of parsed) d.series.find((s) => s.id === cell.seriesId).values[cell.index] = cell.value;
3004
- });
3005
- if (JSON.stringify(next.series) !== JSON.stringify(this.spec.series)) this.commit(_nullishCoalesce(options.label, () => ( "Edit values")), next);
3006
- return { ok: true };
3007
- }
3008
- /** Adds a series (values padded to the categories) and selects it. Returns its id. */
3009
- addSeries(input = {}, options = {}) {
3010
- const id = input.id && !this.getSeries(input.id) ? input.id : _chunkCKRNELPOcjs.uniqueChartSeriesId.call(void 0, this.spec.series);
3011
- const name = _nullishCoalesce(input.name, () => ( `Series ${this.spec.series.length + 1}`));
3012
- const at = Math.max(0, Math.min(this.spec.series.length, _nullishCoalesce(options.at, () => ( this.spec.series.length))));
3013
- const series = [...this.spec.series];
3014
- series.splice(at, 0, { ...input, id, name, values: _nullishCoalesce(input.values, () => ( [])) });
3015
- const { spec, issues } = _chunkCKRNELPOcjs.normalizeChartSpec.call(void 0, { ...this.spec, series });
3016
- if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
3017
- this.commit(`Add series "${name}"`, spec, { kind: "series", seriesId: id });
3018
- return { ok: true, id };
3019
- }
3020
- removeSeries(id) {
3021
- const series = this.getSeries(id);
3022
- if (!series) return this.unknownSeries(id);
3023
- this.commit(
3024
- `Delete series "${series.name}"`,
3025
- this.edited((d) => {
3026
- d.series = d.series.filter((s) => s.id !== id);
3027
- })
3028
- );
3029
- return { ok: true };
3030
- }
3031
- updateSeries(id, patch) {
3032
- const series = this.getSeries(id);
3033
- if (!series) return this.unknownSeries(id);
3034
- const next = _chunkCKRNELPOcjs.cloneChartSeries.call(void 0, series);
3035
- if (patch.name !== void 0) next.name = String(patch.name);
3036
- if (patch.color === null || patch.color === "") delete next.color;
3037
- else if (patch.color !== void 0) next.color = patch.color;
3038
- if (patch.type === null) delete next.type;
3039
- else if (patch.type !== void 0) next.type = patch.type;
3040
- if (patch.hidden !== void 0) {
3041
- if (patch.hidden) next.hidden = true;
3042
- else delete next.hidden;
3043
- }
3044
- const resettable = ["axis", "x", "sizes", "pointColors", "marker", "curve", "dataLabels", "format", "errorBars", "xErrorBars"];
3045
- for (const key of resettable) {
3046
- if (!(key in patch) || patch[key] === void 0) continue;
3047
- if (patch[key] === null) delete next[key];
3048
- else next[key] = patch[key];
3049
- }
3050
- if (next.axis === "primary") delete next.axis;
3051
- const series2 = this.spec.series.map((s) => s.id === id ? next : s);
3052
- const { spec, issues } = _chunkCKRNELPOcjs.normalizeChartSpec.call(void 0, { ...this.spec, series: series2 });
3053
- if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
3054
- if (JSON.stringify(spec.series) === JSON.stringify(this.spec.series)) return { ok: true };
3055
- const label = patch.hidden !== void 0 && Object.keys(patch).length === 1 ? `${patch.hidden ? "Hide" : "Show"} "${series.name}"` : patch.name !== void 0 && Object.keys(patch).length === 1 ? "Rename series" : `Change series "${series.name}"`;
3056
- this.commit(label, spec);
3057
- return { ok: true };
3058
- }
3059
- toggleSeries(id) {
3060
- const series = this.getSeries(id);
3061
- if (!series) return this.unknownSeries(id);
3062
- return this.updateSeries(id, { hidden: !series.hidden });
3063
- }
3064
- /** Moves a series to position `to` (drawing and legend order). */
3065
- moveSeries(id, to) {
3066
- const from = this.spec.series.findIndex((s) => s.id === id);
3067
- if (from < 0) return this.unknownSeries(id);
3068
- const target = Math.max(0, Math.min(this.spec.series.length - 1, to));
3069
- if (target === from) return { ok: true };
3070
- this.commit(
3071
- "Reorder series",
3072
- this.edited((d) => {
3073
- const [s] = d.series.splice(from, 1);
3074
- d.series.splice(target, 0, s);
3075
- })
3076
- );
3077
- return { ok: true };
3078
- }
3079
- /** Inserts a category (with gaps in every series) at `at` (default: the end). Returns its index. */
3080
- addCategory(label, options = {}) {
3081
- const at = Math.max(0, Math.min(this.spec.categories.length, _nullishCoalesce(options.at, () => ( this.spec.categories.length))));
3082
- const name = _nullishCoalesce(label, () => ( `${this.spec.categories.length + 1}`));
3083
- this.commit(
3084
- `Add category "${name}"`,
3085
- this.edited((d) => {
3086
- d.categories.splice(at, 0, name);
3087
- for (const s of d.series) s.values.splice(at, 0, null);
3088
- }),
3089
- { kind: "category", index: at }
3090
- );
3091
- return { ok: true, index: at };
3092
- }
3093
- removeCategory(index) {
3094
- if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
3095
- this.commit(
3096
- `Delete category "${this.spec.categories[index]}"`,
3097
- this.edited((d) => {
3098
- d.categories.splice(index, 1);
3099
- for (const s of d.series) s.values.splice(index, 1);
3100
- })
3101
- );
3102
- return { ok: true };
3103
- }
3104
- renameCategory(index, label) {
3105
- if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
3106
- if (this.spec.categories[index] === label) return { ok: true };
3107
- this.commit(
3108
- "Rename category",
3109
- this.edited((d) => {
3110
- d.categories[index] = String(label);
3111
- })
3112
- );
3113
- return { ok: true };
3114
- }
3115
- moveCategory(from, to) {
3116
- if (!Number.isInteger(from) || from < 0 || from >= this.spec.categories.length) return this.badIndex(from);
3117
- const target = Math.max(0, Math.min(this.spec.categories.length - 1, to));
3118
- if (target === from) return { ok: true };
3119
- const move = (list) => {
3120
- const [item] = list.splice(from, 1);
3121
- list.splice(target, 0, item);
3122
- };
3123
- this.commit(
3124
- "Reorder categories",
3125
- this.edited((d) => {
3126
- move(d.categories);
3127
- for (const s of d.series) move(s.values);
3128
- })
3129
- );
3130
- return { ok: true };
3131
- }
3132
- /**
3133
- * Pastes a block of cells (TSV / CSV text or rows) into the data grid with its top-left at
3134
- * `row` (category index) and `column` (series index; `-1` is the category column). The
3135
- * grid grows to fit. Non-numeric value cells become gaps and are reported in `issues`.
3136
- */
3137
- pasteCells(row, column, cells) {
3138
- const rows = typeof cells === "string" ? cells.includes(" ") ? _chunkTNNJXF46cjs.parseTsv.call(void 0, cells) : _chunkTNNJXF46cjs.parseCsv.call(void 0, cells) : cells.map((r) => [...r]);
3139
- if (!rows.length) return { ok: true };
3140
- const issues = [];
3141
- const next = this.edited((d) => {
3142
- const width = Math.max(...rows.map((r) => r.length));
3143
- const lastSeries = column + width - 1;
3144
- while (d.series.length <= lastSeries) {
3145
- const id = _chunkCKRNELPOcjs.uniqueChartSeriesId.call(void 0, d.series);
3146
- d.series.push({ id, name: `Series ${d.series.length + 1}`, values: d.categories.map(() => null) });
3147
- }
3148
- const lastRow = row + rows.length - 1;
3149
- while (d.categories.length <= lastRow) {
3150
- d.categories.push(`${d.categories.length + 1}`);
3151
- for (const s of d.series) s.values.push(null);
3152
- }
3153
- rows.forEach((cellsInRow, r) => {
3154
- cellsInRow.forEach((cell, c) => {
3155
- const col = column + c;
3156
- const index = row + r;
3157
- if (col < 0) d.categories[index] = cell === null || cell === void 0 ? "" : String(cell).trim();
3158
- else {
3159
- const parsed = _chunkCKRNELPOcjs.parseChartValue.call(void 0, cell);
3160
- if (parsed === void 0) issues.push({ path: `${d.series[col].id}[${index}]`, message: `"${String(cell)}" is not a number; it was left blank.` });
3161
- d.series[col].values[index] = _nullishCoalesce(parsed, () => ( null));
3162
- }
3163
- });
3164
- });
3165
- });
3166
- this.commit("Paste cells", next);
3167
- if (issues.length) {
3168
- this.issues = issues;
3169
- this.notify(false);
3170
- }
3171
- return issues.length ? { ok: true, issues } : { ok: true };
3172
- }
3173
- /**
3174
- * Replaces the data with a grid of cells (see `chartDataFromTable`): one undo step. Series
3175
- * colours and types are kept by name.
3176
- */
3177
- importTable(rows, options = {}) {
3178
- const data = _chunkCKRNELPOcjs.chartDataFromTable.call(void 0, rows, options);
3179
- if (!data.categories.length || !data.series.length) return this.reject("There is no data to import: expected a header row and at least one row of values.");
3180
- const byName = new Map(this.spec.series.map((s) => [s.name, s]));
3181
- const series = data.series.map((s) => {
3182
- const old = byName.get(s.name);
3183
- return { ...s, ..._optionalChain([old, 'optionalAccess', _81 => _81.color]) ? { color: old.color } : {}, ..._optionalChain([old, 'optionalAccess', _82 => _82.type]) ? { type: old.type } : {}, ...old ? { id: old.id } : {} };
3184
- });
3185
- const { spec } = _chunkCKRNELPOcjs.normalizeChartSpec.call(void 0, { ...this.spec, categories: data.categories, series });
3186
- this.commit("Import data", spec);
3187
- if (data.issues.length) {
3188
- this.issues = data.issues;
3189
- this.notify(false);
3190
- return { ok: true, issues: data.issues };
3191
- }
3192
- return { ok: true };
3193
- }
3194
- /** Imports CSV (or TSV, detected by tabs) text; see `importTable`. */
3195
- importCsv(text, options = {}) {
3196
- const delimiter = _nullishCoalesce(options.delimiter, () => ( (text.includes(" ") ? " " : ",")));
3197
- return this.importTable(_chunkTNNJXF46cjs.parseCsv.call(void 0, text, { delimiter }), options);
3198
- }
3199
- // -------------------------------------------------------------------------
3200
- // Trendlines
3201
- // -------------------------------------------------------------------------
3202
- /** Adds a trendline to a series and selects it. Returns its index (and the fit, when it can be computed). */
3203
- addTrendline(seriesId, input) {
3204
- const series = this.getSeries(seriesId);
3205
- if (!series) return this.unknownSeries(seriesId);
3206
- if (!chartSupportsTrendlines(this.spec.type, this.spec.stacking))
3207
- return this.reject(`Trendlines need an unstacked bar, line, area, scatter or bubble chart; this is a ${this.spec.stacking !== "none" ? `${this.spec.stacking} ` : ""}${this.spec.type} chart.`);
3208
- const issues = [];
3209
- const line = _chunkCKRNELPOcjs.normalizeChartTrendline.call(void 0, input, "trendline", issues);
3210
- if (!line || issues.length) return this.reject(issues.map((i) => i.message).join(" ") || "The trendline is not valid.", issues);
3211
- const index = _nullishCoalesce(_optionalChain([series, 'access', _83 => _83.trendlines, 'optionalAccess', _84 => _84.length]), () => ( 0));
3212
- const next = this.edited((d) => {
3213
- const s = d.series.find((x) => x.id === seriesId);
3214
- s.trendlines = [..._nullishCoalesce(s.trendlines, () => ( [])), line];
3215
- });
3216
- this.commit(`Add trendline to "${series.name}"`, next, { kind: "trendline", seriesId, index });
3217
- return { ok: true, index, fit: this.trendlineFit(seriesId, index) };
3218
- }
3219
- /** Changes a trendline (all-or-nothing). */
3220
- updateTrendline(seriesId, index, patch) {
3221
- const series = this.getSeries(seriesId);
3222
- if (!series) return this.unknownSeries(seriesId);
3223
- const current = _optionalChain([series, 'access', _85 => _85.trendlines, 'optionalAccess', _86 => _86[index]]);
3224
- if (!current) return fail(`"${series.name}" has no trendline ${index}; ${_optionalChain([series, 'access', _87 => _87.trendlines, 'optionalAccess', _88 => _88.length]) ? `expected 0 to ${series.trendlines.length - 1}` : "it has none yet"}.`);
3225
- const issues = [];
3226
- const merged = { ...current, ...patch };
3227
- for (const [k, v] of Object.entries(patch)) if (v === null) delete merged[k];
3228
- const line = _chunkCKRNELPOcjs.normalizeChartTrendline.call(void 0, merged, "trendline", issues);
3229
- if (!line || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
3230
- this.commit(
3231
- "Change trendline",
3232
- this.edited((d) => {
3233
- d.series.find((x) => x.id === seriesId).trendlines[index] = line;
3234
- })
3235
- );
3236
- return { ok: true };
3237
- }
3238
- removeTrendline(seriesId, index) {
3239
- const series = this.getSeries(seriesId);
3240
- if (!series) return this.unknownSeries(seriesId);
3241
- if (!_optionalChain([series, 'access', _89 => _89.trendlines, 'optionalAccess', _90 => _90[index]])) return fail(`"${series.name}" has no trendline ${index}.`);
3242
- this.commit(
3243
- "Delete trendline",
3244
- this.edited((d) => {
3245
- const s = d.series.find((x) => x.id === seriesId);
3246
- s.trendlines = s.trendlines.filter((_, i) => i !== index);
3247
- if (!s.trendlines.length) delete s.trendlines;
3248
- })
3249
- );
3250
- return { ok: true };
3251
- }
3252
- /** The points a series' trendlines are fitted to: (x, y) with x = 1, 2, 3 … on category charts. */
3253
- trendlinePoints(seriesId) {
3254
- const series = this.getSeries(seriesId);
3255
- if (!series) return [];
3256
- const xy = _chunkCKRNELPOcjs.chartFamily.call(void 0, this.spec) === "xy";
3257
- const out = [];
3258
- series.values.forEach((v, i) => {
3259
- const x = xy ? _nullishCoalesce(_optionalChain([series, 'access', _91 => _91.x, 'optionalAccess', _92 => _92[i]]), () => ( (this.spec.type === "bubble" ? i + 1 : null))) : i + 1;
3260
- if (v !== null && x !== null && Number.isFinite(v) && Number.isFinite(x)) out.push([x, v]);
3261
- });
3262
- return out;
3263
- }
3264
- /** The fitted coefficients, R² and equation of a trendline (null for moving averages or unfittable data). */
3265
- trendlineFit(seriesId, index) {
3266
- const line = _optionalChain([this, 'access', _93 => _93.getSeries, 'call', _94 => _94(seriesId), 'optionalAccess', _95 => _95.trendlines, 'optionalAccess', _96 => _96[index]]);
3267
- if (!line) return null;
3268
- return fitChartTrendline(line, this.trendlinePoints(seriesId));
3269
- }
3270
- /** The moving-average points of a trendline (empty for other kinds). */
3271
- trendlineMovingAverage(seriesId, index) {
3272
- const line = _optionalChain([this, 'access', _97 => _97.getSeries, 'call', _98 => _98(seriesId), 'optionalAccess', _99 => _99.trendlines, 'optionalAccess', _100 => _100[index]]);
3273
- if (!line || line.type !== "moving-average") return [];
3274
- return chartMovingAverage(this.trendlinePoints(seriesId), _nullishCoalesce(line.period, () => ( 2)));
3275
- }
3276
- // -------------------------------------------------------------------------
3277
- // Reshaping
3278
- // -------------------------------------------------------------------------
3279
- /**
3280
- * Switches rows and columns: series become categories and categories become series (one
3281
- * undo step). Series colours, types and other per-series settings are dropped.
3282
- */
3283
- transpose() {
3284
- const s = this.spec;
3285
- if (!s.series.length || !s.categories.length) return this.reject("There is no data to switch: the chart needs at least one series and one category.");
3286
- const used = /* @__PURE__ */ new Set();
3287
- const series = s.categories.map((name, i) => {
3288
- let id = `series-${i + 1}`;
3289
- for (let k = i + 2; used.has(id); k++) id = `series-${k}`;
3290
- used.add(id);
3291
- return { id, name, values: s.series.map((x) => _nullishCoalesce(x.values[i], () => ( null))) };
3292
- });
3293
- const { spec } = _chunkCKRNELPOcjs.normalizeChartSpec.call(void 0, { ...s, categories: s.series.map((x) => x.name), series });
3294
- this.commit("Switch rows and columns", spec, null);
3295
- return { ok: true };
3296
- }
3297
- // -------------------------------------------------------------------------
3298
- // Selection and gestures
3299
- // -------------------------------------------------------------------------
3300
- /** Selects a chart element (view state; not an undo step). */
3301
- select(selection) {
3302
- const next = this.validSelection(selection, this.spec);
3303
- if (sameChartSelection(next, this.selection) || next === null && this.selection === null) return;
3304
- this.selection = next;
3305
- this.notify(false);
3306
- }
3307
- /**
3308
- * Deletes what is selected: a series or category is removed, a point becomes a gap, a
3309
- * title is cleared, the legend or an axis is hidden.
3310
- */
3311
- deleteSelection() {
3312
- const sel = this.selection;
3313
- if (!sel) return fail("Nothing is selected.");
3314
- switch (sel.kind) {
3315
- case "series":
3316
- return this.removeSeries(sel.seriesId);
3317
- case "category":
3318
- return this.removeCategory(sel.index);
3319
- case "point":
3320
- return this.setValue(sel.seriesId, sel.index, null, { label: "Clear value" });
3321
- case "title":
3322
- return this.setTitle("");
3323
- case "subtitle":
3324
- return this.setSubtitle("");
3325
- case "legend":
3326
- return this.setLegend({ show: false });
3327
- case "axis":
3328
- return this.setAxis(sel.axis, { show: false });
3329
- case "trendline":
3330
- return this.removeTrendline(sel.seriesId, sel.index);
3331
- default:
3332
- return fail("The chart itself cannot be deleted.");
3333
- }
3334
- }
3335
- /** Adds `delta` to a value (a gap counts as 0). */
3336
- nudgeValue(seriesId, index, delta) {
3337
- const series = this.getSeries(seriesId);
3338
- if (!series) return this.unknownSeries(seriesId);
3339
- const current = _nullishCoalesce(series.values[index], () => ( 0));
3340
- return this.setValue(seriesId, index, Number((current + delta).toPrecision(12)), { label: `Edit ${series.name} \xB7 ${_nullishCoalesce(this.spec.categories[index], () => ( index))}` });
3341
- }
3342
- /** Starts a continuous edit (a drag): every change until `endGesture()` is one undo step. */
3343
- beginGesture(key) {
3344
- if (this.gestureKey === key) return;
3345
- this.history.endGesture();
3346
- this.gestureKey = key;
3347
- this.gestureCount++;
3348
- this.gestureStart = { spec: this.spec, mark: this.history.mark() };
3349
- this.notify(false);
3350
- }
3351
- /**
3352
- * Cancels the gesture in progress (Escape during a drag): everything it changed is taken
3353
- * back and leaves no undo or redo step.
3354
- */
3355
- cancelGesture() {
3356
- if (this.gestureKey === null) return;
3357
- const start = this.gestureStart;
3358
- this.gestureKey = null;
3359
- this.gestureStart = null;
3360
- this.history.endGesture();
3361
- if (start) this.history.revertTo(start.mark);
3362
- this.notify(false);
3363
- }
3364
- endGesture() {
3365
- if (this.gestureKey === null) return;
3366
- this.gestureKey = null;
3367
- this.history.endGesture();
3368
- const start = this.gestureStart;
3369
- this.gestureStart = null;
3370
- if (start && sameChartJson(start.spec, this.spec)) this.history.revertTo(start.mark);
3371
- this.notify(false);
3372
- }
3373
- // -------------------------------------------------------------------------
3374
- // Persistence and history
3375
- // -------------------------------------------------------------------------
3376
- /** The spec, for saving (a copy). */
3377
- toJSON() {
3378
- return _chunkCKRNELPOcjs.cloneChartSpec.call(void 0, this.spec);
3379
- }
3380
- /**
3381
- * Replaces the chart without an undo step (e.g. when opening a document). Problems are
3382
- * fixed with defaults and reported in `state.issues` (and returned).
3383
- */
3384
- load(input, options = {}) {
3385
- const { spec, issues } = _chunkCKRNELPOcjs.normalizeChartSpec.call(void 0, input);
3386
- this.spec = spec;
3387
- this.issues = issues;
3388
- this.selection = this.validSelection(this.selection, spec);
3389
- if (options.clearHistory) {
3390
- this.applying = true;
3391
- try {
3392
- this.history.clear();
3393
- } finally {
3394
- this.applying = false;
3395
- }
3396
- }
3397
- this.notify(true);
3398
- return issues;
3399
- }
3400
- undo() {
3401
- this.endGesture();
3402
- return this.history.undo();
3403
- }
3404
- redo() {
3405
- this.endGesture();
3406
- return this.history.redo();
3407
- }
3408
- /** Stops listening to the history (reconnected by the next `subscribe`). */
3409
- destroy() {
3410
- _optionalChain([this, 'access', _101 => _101.unsubscribeHistory, 'optionalCall', _102 => _102()]);
3411
- this.unsubscribeHistory = void 0;
3412
- this.listeners.clear();
3413
- }
3414
- }, _class);
3415
- function createChartController(options = {}) {
3416
- return new ChartController(options);
3417
- }
3418
- function updateLabel(patch) {
3419
- const keys = Object.keys(patch);
3420
- if (keys.length !== 1) return "Change chart";
3421
- const labels = {
3422
- type: "Change chart type",
3423
- title: "Edit title",
3424
- subtitle: "Edit subtitle",
3425
- categories: "Edit categories",
3426
- series: "Edit series",
3427
- xAxis: "Change category axis",
3428
- yAxis: "Change value axis",
3429
- yAxis2: "Change secondary value axis",
3430
- labelPosition: "Change label position",
3431
- gapWidth: "Change bar spacing",
3432
- overlap: "Change bar overlap",
3433
- bins: "Change histogram bins",
3434
- showTotal: "Toggle waterfall total",
3435
- legend: "Change legend",
3436
- stacking: "Change stacking",
3437
- orientation: "Change orientation",
3438
- curve: "Change line style",
3439
- points: "Toggle points",
3440
- dataLabels: "Toggle data labels",
3441
- palette: "Change colours",
3442
- innerRadius: "Change donut hole"
3443
- };
3444
- return _nullishCoalesce(labels[keys[0]], () => ( "Change chart"));
3445
- }
3446
-
3447
- // src/chart/sparkline.ts
3448
- var MAX_POINTS = 1e5;
3449
- function layoutSparkline(values, options) {
3450
- const kind = _nullishCoalesce(options.kind, () => ( "line"));
3451
- const w = Number.isFinite(options.width) ? Math.max(1, options.width) : 1;
3452
- const h = Number.isFinite(options.height) ? Math.max(1, options.height) : 1;
3453
- const color = _nullishCoalesce(options.color, () => ( "#4269d0"));
3454
- const negative = _nullishCoalesce(options.negativeColor, () => ( "#e5484d"));
3455
- const theme = { ..._chunkCKRNELPOcjs.DEFAULT_CHART_THEME, background: "transparent" };
3456
- const marks = [];
3457
- const scene = { width: w, height: h, plot: { x: 0, y: 0, width: w, height: h }, marks, percent: false, theme, warnings: [] };
3458
- const vals = values.slice(0, MAX_POINTS).map((v) => v !== null && Number.isFinite(v) ? Math.max(-1e300, Math.min(1e300, v)) : null);
3459
- const n = vals.length;
3460
- if (!n) return scene;
3461
- const pad = Math.max(0.5, Math.min(2, h * 0.1));
3462
- const x0 = pad;
3463
- const y0 = pad;
3464
- const iw = Math.max(0.5, w - 2 * pad);
3465
- const ih = Math.max(0.5, h - 2 * pad);
3466
- let lo = Infinity;
3467
- let hi = -Infinity;
3468
- for (const v of vals) if (v !== null) lo = Math.min(lo, v), hi = Math.max(hi, v);
3469
- if (!Number.isFinite(lo)) return scene;
3470
- if (options.min !== void 0 && Number.isFinite(options.min)) lo = options.min;
3471
- if (options.max !== void 0 && Number.isFinite(options.max)) hi = options.max;
3472
- let hiAt = -1;
3473
- let loAt = -1;
3474
- vals.forEach((v, i) => {
3475
- if (v === null) return;
3476
- if (hiAt < 0 || v > vals[hiAt]) hiAt = i;
3477
- if (loAt < 0 || v < vals[loAt]) loAt = i;
3478
- });
3479
- if (kind === "line") {
3480
- const span2 = hi - lo;
3481
- const fy2 = (v) => y0 + ih - (span2 > 0 ? (v - lo) / span2 : 0.5) * ih;
3482
- const fx = (j) => n > 1 ? x0 + j / (n - 1) * iw : x0 + iw / 2;
3483
- let d = "";
3484
- let run = [];
3485
- const flush = () => {
3486
- if (run.length >= 2) {
3487
- const pts = run.length > 4e3 ? run.filter((_, i) => i % (Math.floor(run.length / 2e3) + 1) === 0) : run;
3488
- d += pts.map(([x, y], i) => `${i ? "L" : "M"}${r2(x)},${r2(y)}`).join("");
3489
- }
3490
- run = [];
3491
- };
3492
- vals.forEach((v, j) => v === null ? flush() : run.push([fx(j), fy2(v)]));
3493
- flush();
3494
- if (d) marks.push({ kind: "path", key: "sparkline", role: "line", d, fill: "none", stroke: color, strokeWidth: 1.25 });
3495
- const r = Math.max(1, Math.min(3, h * 0.08));
3496
- if (options.markers || n === 1) {
3497
- const step = Math.max(1, Math.floor(n / 500));
3498
- vals.forEach((v, j) => {
3499
- if (v !== null && j % step === 0) marks.push({ kind: "circle", key: `point-${j}`, role: "point", cx: fx(j), cy: fy2(v), r, fill: color });
3500
- });
3501
- }
3502
- if (options.highLow && n > 1) {
3503
- if (hiAt >= 0) marks.push({ kind: "circle", key: "high", role: "point", cx: fx(hiAt), cy: fy2(vals[hiAt]), r: r + 0.5, fill: color });
3504
- if (loAt >= 0 && loAt !== hiAt) marks.push({ kind: "circle", key: "low", role: "point", cx: fx(loAt), cy: fy2(vals[loAt]), r: r + 0.5, fill: negative });
3505
- }
3506
- return scene;
3507
- }
3508
- const slot = iw / n;
3509
- const bw = Math.max(0.25, slot * 0.8);
3510
- const win = kind === "win-loss";
3511
- const lo0 = win ? -1 : Math.min(lo, 0);
3512
- const hi0 = win ? 1 : Math.max(hi, 0);
3513
- const span = hi0 - lo0;
3514
- const fy = (v) => span > 0 ? y0 + ih - (v - lo0) / span * ih : y0 + ih;
3515
- const base = fy(0);
3516
- vals.forEach((raw, j) => {
3517
- if (raw === null) return;
3518
- const v = win ? Math.sign(raw) : raw;
3519
- if (win && v === 0) return;
3520
- const top = fy(v);
3521
- const highlight = options.highLow && !win && (j === hiAt || j === loAt);
3522
- marks.push({ kind: "rect", key: `bar-${j}`, role: "bar", x: x0 + j * slot + (slot - bw) / 2, y: Math.min(top, base), width: bw, height: Math.max(0.5, Math.abs(top - base)), fill: v < 0 ? negative : color, opacity: options.highLow && !win && !highlight ? 0.6 : void 0 });
3523
- });
3524
- return scene;
3525
- }
3526
- function sparklineToSvg(values, options) {
3527
- return chartSceneToSvg(layoutSparkline(values, options), { ariaLabel: options.ariaLabel });
3528
- }
3529
-
3530
-
3531
-
3532
-
3533
-
3534
-
3535
-
3536
-
3537
-
3538
-
3539
-
3540
-
3541
-
3542
-
3543
-
3544
-
3545
-
3546
-
3547
-
3548
-
3549
2128
 
3550
2129
 
3551
2130
 
@@ -3585,5 +2164,5 @@ function sparklineToSvg(values, options) {
3585
2164
 
3586
2165
 
3587
2166
 
3588
- exports.CHART_MERMAID_META_PREFIX = CHART_MERMAID_META_PREFIX; exports.chartMermaidFamily = chartMermaidFamily; exports.chartToMermaid = chartToMermaid; exports.chartFromMermaid = chartFromMermaid; exports.findMermaidCharts = findMermaidCharts; exports.parseChartDate = parseChartDate; exports.CHART_DATE_STEPS = CHART_DATE_STEPS; exports.formatChartDate = formatChartDate; exports.chartDateLabels = chartDateLabels; exports.compileChartFormatCode = compileChartFormatCode; exports.formatChartValueWithCode = formatChartValueWithCode; exports.chartSerialToDate = chartSerialToDate; exports.chartDateToSerial = chartDateToSerial; exports.niceStep = niceStep; exports.niceScale = niceScale; exports.stepDecimals = stepDecimals; exports.formatChartNumber = formatChartNumber; exports.linearMap = linearMap; exports.niceLogScale = niceLogScale; exports.chartScaleToPixel = chartScaleToPixel; exports.chartScaleFromPixel = chartScaleFromPixel; exports.estimateChartTextWidth = estimateChartTextWidth; exports.sameChartSelection = sameChartSelection; exports.contrastingTextColor = contrastingTextColor; exports.chartHistogram = chartHistogram; exports.chartQuantile = chartQuantile; exports.chartBoxStats = chartBoxStats; exports.chartSquarify = chartSquarify; exports.decimateChartPoints = decimateChartPoints; exports.summarizeChartSeries = summarizeChartSeries; exports.fitChartTrendline = fitChartTrendline; exports.trendNumberText = trendNumberText; exports.chartMovingAverage = chartMovingAverage; exports.chartTrendlineName = chartTrendlineName; exports.chartSupportsTrendlines = chartSupportsTrendlines; exports.MAX_CHART_DATA_LABELS = MAX_CHART_DATA_LABELS; exports.MAX_CHART_LINE_POINTS = MAX_CHART_LINE_POINTS; exports.MAX_CHART_MARKERS = MAX_CHART_MARKERS; exports.layoutChart = layoutChart; exports.chartValueAtPoint = chartValueAtPoint; exports.chartXAtPoint = chartXAtPoint; exports.snapChartValue = snapChartValue; exports.summarizeChart = summarizeChart; exports.describeChart = describeChart; exports.chartTextBaseline = chartTextBaseline; exports.chartTextTransform = chartTextTransform; exports.chartSceneToSvg = chartSceneToSvg; exports.chartToSvg = chartToSvg; exports.chartSceneToDomSpec = chartSceneToDomSpec; exports.CHART_LAYOUT_PRESETS = CHART_LAYOUT_PRESETS; exports.monochromeChartPalette = monochromeChartPalette; exports.CHART_COLOR_SCHEMES = CHART_COLOR_SCHEMES; exports.chartPresetPatch = chartPresetPatch; exports.ChartController = ChartController; exports.createChartController = createChartController; exports.layoutSparkline = layoutSparkline; exports.sparklineToSvg = sparklineToSvg;
3589
- //# sourceMappingURL=chunk-NK3WL5TJ.cjs.map
2167
+ exports.parseChartDate = parseChartDate; exports.CHART_DATE_STEPS = CHART_DATE_STEPS; exports.formatChartDate = formatChartDate; exports.chartDateLabels = chartDateLabels; exports.compileChartFormatCode = compileChartFormatCode; exports.formatChartValueWithCode = formatChartValueWithCode; exports.chartSerialToDate = chartSerialToDate; exports.chartDateToSerial = chartDateToSerial; exports.niceStep = niceStep; exports.niceScale = niceScale; exports.stepDecimals = stepDecimals; exports.formatChartNumber = formatChartNumber; exports.linearMap = linearMap; exports.niceLogScale = niceLogScale; exports.chartScaleToPixel = chartScaleToPixel; exports.chartScaleFromPixel = chartScaleFromPixel; exports.estimateChartTextWidth = estimateChartTextWidth; exports.sameChartSelection = sameChartSelection; exports.contrastingTextColor = contrastingTextColor; exports.r2 = r2; exports.chartHistogram = chartHistogram; exports.chartQuantile = chartQuantile; exports.chartBoxStats = chartBoxStats; exports.chartSquarify = chartSquarify; exports.decimateChartPoints = decimateChartPoints; exports.summarizeChartSeries = summarizeChartSeries; exports.fitChartTrendline = fitChartTrendline; exports.trendNumberText = trendNumberText; exports.chartMovingAverage = chartMovingAverage; exports.chartTrendlineName = chartTrendlineName; exports.chartSupportsTrendlines = chartSupportsTrendlines; exports.MAX_CHART_DATA_LABELS = MAX_CHART_DATA_LABELS; exports.MAX_CHART_LINE_POINTS = MAX_CHART_LINE_POINTS; exports.MAX_CHART_MARKERS = MAX_CHART_MARKERS; exports.layoutChart = layoutChart; exports.chartValueAtPoint = chartValueAtPoint; exports.chartXAtPoint = chartXAtPoint; exports.snapChartValue = snapChartValue;
2168
+ //# sourceMappingURL=chunk-P2CTDJNF.cjs.map