@pixerate/editor 0.47.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.cjs +246 -29
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +18 -6
- package/dist/agent/index.d.ts +18 -6
- package/dist/agent/index.js +226 -9
- package/dist/agent/index.js.map +1 -1
- package/dist/{builtins-xs7rK6ys.d.cts → builtins-1kqwLUnb.d.cts} +1 -1
- package/dist/{builtins-D2nMtw2W.d.ts → builtins-BKYlHH1s.d.ts} +1 -1
- package/dist/chart/index.cjs +214 -3541
- package/dist/chart/index.cjs.map +1 -1
- package/dist/chart/index.d.cts +4 -494
- package/dist/chart/index.d.ts +4 -494
- package/dist/chart/index.js +135 -3462
- package/dist/chart/index.js.map +1 -1
- package/dist/{chunk-5VXI5GED.js → chunk-2CJ3LPYQ.js} +3 -3
- package/dist/{chunk-ZFBBVGCI.js → chunk-EKYHKQPG.js} +243 -243
- package/dist/{chunk-ZFBBVGCI.js.map → chunk-EKYHKQPG.js.map} +1 -1
- package/dist/chunk-HXUL3LS7.js +113 -0
- package/dist/chunk-HXUL3LS7.js.map +1 -0
- package/dist/{chunk-REAGGLXS.js → chunk-JQ3BB3E6.js} +2 -2
- package/dist/chunk-LTUM52X6.js +4074 -0
- package/dist/chunk-LTUM52X6.js.map +1 -0
- package/dist/{chunk-J3R2OWOE.cjs → chunk-MUCYWI6W.cjs} +608 -15
- package/dist/chunk-MUCYWI6W.cjs.map +1 -0
- package/dist/chunk-PIMCHMZE.cjs +521 -0
- package/dist/chunk-PIMCHMZE.cjs.map +1 -0
- package/dist/{chunk-TK2XZK6Z.js → chunk-PKBWLEL7.js} +594 -1
- package/dist/chunk-PKBWLEL7.js.map +1 -0
- package/dist/chunk-PRSJJZBS.cjs +4074 -0
- package/dist/chunk-PRSJJZBS.cjs.map +1 -0
- package/dist/chunk-U6IZKC7B.cjs +113 -0
- package/dist/chunk-U6IZKC7B.cjs.map +1 -0
- package/dist/{chunk-SQC26VQV.cjs → chunk-UN33YOXW.cjs} +191 -191
- package/dist/{chunk-SQC26VQV.cjs.map → chunk-UN33YOXW.cjs.map} +1 -1
- package/dist/{chunk-PQFAHT5O.js → chunk-VKPEPWKZ.js} +27 -94
- package/dist/chunk-VKPEPWKZ.js.map +1 -0
- package/dist/{chunk-H4PICYNL.cjs → chunk-WXEHCVJQ.cjs} +113 -180
- package/dist/chunk-WXEHCVJQ.cjs.map +1 -0
- package/dist/{chunk-LYGEEDNR.cjs → chunk-XP5IUZUO.cjs} +242 -242
- package/dist/chunk-XP5IUZUO.cjs.map +1 -0
- package/dist/{chunk-C3NWJE3K.js → chunk-XUUD43OV.js} +11 -672
- package/dist/chunk-XUUD43OV.js.map +1 -0
- package/dist/{chunk-BBRK6MOI.cjs → chunk-YU4WQE5M.cjs} +100 -100
- package/dist/{chunk-BBRK6MOI.cjs.map → chunk-YU4WQE5M.cjs.map} +1 -1
- package/dist/commands/index.d.cts +1 -1
- package/dist/commands/index.d.ts +1 -1
- package/dist/controller-ChjCCMLl.d.cts +822 -0
- package/dist/controller-DtbEo3om.d.ts +822 -0
- package/dist/extensions/index.cjs +32 -4
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +4 -2
- package/dist/extensions/index.d.ts +4 -2
- package/dist/extensions/index.js +31 -3
- package/dist/{index-QrqMmZ75.d.cts → index-DoHqT1mq.d.cts} +203 -7
- package/dist/{index-CSUCQYAH.d.ts → index-YLZ0fKqE.d.ts} +203 -7
- package/dist/index.cjs +37 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -7
- package/dist/index.d.ts +9 -7
- package/dist/index.js +31 -6
- package/dist/index.js.map +1 -1
- package/dist/spreadsheet/index.cjs +4 -4
- package/dist/spreadsheet/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-C3NWJE3K.js.map +0 -1
- package/dist/chunk-H4PICYNL.cjs.map +0 -1
- package/dist/chunk-J3R2OWOE.cjs.map +0 -1
- package/dist/chunk-LYGEEDNR.cjs.map +0 -1
- package/dist/chunk-NEOO6QJD.cjs +0 -1182
- package/dist/chunk-NEOO6QJD.cjs.map +0 -1
- package/dist/chunk-PQFAHT5O.js.map +0 -1
- package/dist/chunk-TK2XZK6Z.js.map +0 -1
- package/dist/types-BVqWm3d1.d.cts +0 -320
- package/dist/types-BVqWm3d1.d.ts +0 -320
- /package/dist/{chunk-5VXI5GED.js.map → chunk-2CJ3LPYQ.js.map} +0 -0
- /package/dist/{chunk-REAGGLXS.js.map → chunk-JQ3BB3E6.js.map} +0 -0
package/dist/chart/index.cjs
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
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; var _class2; var _class3;
|
|
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; var _class2; var _class3;
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
var _chunkU6IZKC7Bcjs = require('../chunk-U6IZKC7B.cjs');
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
var _chunkPIMCHMZEcjs = require('../chunk-PIMCHMZE.cjs');
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
2
24
|
|
|
3
25
|
|
|
4
26
|
|
|
@@ -53,2233 +75,65 @@
|
|
|
53
75
|
|
|
54
76
|
|
|
55
77
|
|
|
56
|
-
var _chunkNEOO6QJDcjs = require('../chunk-NEOO6QJD.cjs');
|
|
57
78
|
|
|
58
79
|
|
|
59
|
-
var _chunkLYGEEDNRcjs = require('../chunk-LYGEEDNR.cjs');
|
|
60
80
|
|
|
61
81
|
|
|
62
82
|
|
|
63
|
-
var _chunkTNNJXF46cjs = require('../chunk-TNNJXF46.cjs');
|
|
64
83
|
|
|
65
84
|
|
|
66
|
-
var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
|
|
67
85
|
|
|
68
|
-
// src/chart/numfmt.ts
|
|
69
|
-
function compileChartFormatCode(code) {
|
|
70
|
-
const fmt = _chunkLYGEEDNRcjs.NumberFormat.parseCached(code);
|
|
71
|
-
return {
|
|
72
|
-
isDate: fmt.isDate(),
|
|
73
|
-
isPercent: fmt.isPercent(),
|
|
74
|
-
format: (value) => Number.isFinite(value) ? fmt.formatNumber(value).text : ""
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
function formatChartValueWithCode(value, code) {
|
|
78
|
-
return compileChartFormatCode(code).format(value);
|
|
79
|
-
}
|
|
80
|
-
function chartSerialToDate(serial) {
|
|
81
|
-
const origin = serial < 61 ? Date.UTC(1899, 11, 31) : Date.UTC(1899, 11, 30);
|
|
82
|
-
return new Date(origin + Math.round(serial * 864e5));
|
|
83
|
-
}
|
|
84
|
-
function chartDateToSerial(date) {
|
|
85
|
-
const serial = (date.getTime() - Date.UTC(1899, 11, 30)) / 864e5;
|
|
86
|
-
return serial < 61 ? serial - 1 : serial;
|
|
87
|
-
}
|
|
88
86
|
|
|
89
|
-
// src/chart/scale.ts
|
|
90
|
-
function niceStep(span, count) {
|
|
91
|
-
if (!(span > 0) || !(count > 0)) return 1;
|
|
92
|
-
const raw = span / count;
|
|
93
|
-
const power = Math.pow(10, Math.floor(Math.log10(raw)));
|
|
94
|
-
const fraction = raw / power;
|
|
95
|
-
const nice = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 2.5 ? 2.5 : fraction <= 5 ? 5 : 10;
|
|
96
|
-
return nice * power;
|
|
97
|
-
}
|
|
98
|
-
var clean = (n) => Math.abs(n) < 1e-12 ? 0 : Number(n.toPrecision(12));
|
|
99
|
-
function niceScale(min, max, count = 5, fixedMin = null, fixedMax = null) {
|
|
100
|
-
if (fixedMin !== null && !Number.isFinite(fixedMin)) fixedMin = null;
|
|
101
|
-
if (fixedMax !== null && !Number.isFinite(fixedMax)) fixedMax = null;
|
|
102
|
-
let lo = _nullishCoalesce(fixedMin, () => ( min));
|
|
103
|
-
let hi = _nullishCoalesce(fixedMax, () => ( max));
|
|
104
|
-
if (!Number.isFinite(lo) || !Number.isFinite(hi)) {
|
|
105
|
-
fixedMin = fixedMax = null;
|
|
106
|
-
lo = 0;
|
|
107
|
-
hi = 1;
|
|
108
|
-
}
|
|
109
|
-
if (lo > hi) [lo, hi] = [hi, lo];
|
|
110
|
-
if (lo === hi) {
|
|
111
|
-
const pad = lo === 0 ? 1 : Math.abs(lo) * 0.1;
|
|
112
|
-
if (fixedMin === null && lo !== 0) lo -= pad;
|
|
113
|
-
if (fixedMax === null || lo === hi) hi += pad;
|
|
114
|
-
}
|
|
115
|
-
const span = hi - lo;
|
|
116
|
-
const step = Number.isFinite(span) ? niceStep(span, count) : niceStep(hi / 2 - lo / 2, count) * 2;
|
|
117
|
-
const finiteOr = (n2, fallback) => Number.isFinite(n2) ? n2 : fallback;
|
|
118
|
-
const niceMin = _nullishCoalesce(fixedMin, () => ( finiteOr(snap(Math.floor(lo / step + 1e-9) * step, step), lo)));
|
|
119
|
-
const niceMax = _nullishCoalesce(fixedMax, () => ( finiteOr(snap(Math.ceil(hi / step - 1e-9) * step, step), hi)));
|
|
120
|
-
const firstIndex = Math.ceil(niceMin / step - 1e-9);
|
|
121
|
-
const lastIndex = Math.floor(niceMax / step + 1e-9);
|
|
122
|
-
const n = Math.min(999, Math.max(-1, lastIndex - firstIndex));
|
|
123
|
-
const ticks = [];
|
|
124
|
-
for (let i = 0; i <= n; i++) {
|
|
125
|
-
const t = snap((firstIndex + i) * step, step);
|
|
126
|
-
if (Number.isFinite(t) && t !== ticks[ticks.length - 1]) ticks.push(t);
|
|
127
|
-
}
|
|
128
|
-
return { min: niceMin, max: niceMax, step, ticks };
|
|
129
|
-
}
|
|
130
|
-
function snap(n, step) {
|
|
131
|
-
const c = clean(n);
|
|
132
|
-
return Math.abs(c - n) <= step * 1e-6 ? c : n;
|
|
133
|
-
}
|
|
134
|
-
function stepDecimals(step) {
|
|
135
|
-
if (!(step > 0) || !Number.isFinite(step)) return 0;
|
|
136
|
-
for (let d = 0; d < 6; d++) {
|
|
137
|
-
const scaled = step * Math.pow(10, d);
|
|
138
|
-
if (Math.abs(scaled - Math.round(scaled)) < 1e-9) return d;
|
|
139
|
-
}
|
|
140
|
-
return 6;
|
|
141
|
-
}
|
|
142
|
-
function magnitudeDecimals(value) {
|
|
143
|
-
if (!(value > 0) || value >= 1) return 0;
|
|
144
|
-
return Math.min(6, Math.max(0, Math.ceil(-Math.log10(value) - 1e-9)));
|
|
145
|
-
}
|
|
146
|
-
var COMPACT = [
|
|
147
|
-
{ value: 1e12, suffix: "T" },
|
|
148
|
-
{ value: 1e9, suffix: "B" },
|
|
149
|
-
{ value: 1e6, suffix: "M" },
|
|
150
|
-
{ value: 1e3, suffix: "K" }
|
|
151
|
-
];
|
|
152
|
-
function grouped(value, decimals) {
|
|
153
|
-
const fixed = Math.abs(value).toFixed(decimals);
|
|
154
|
-
const [int, frac] = fixed.split(".");
|
|
155
|
-
const withCommas = int.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
156
|
-
return `${value < 0 && Number(fixed) !== 0 ? "-" : ""}${withCommas}${frac ? `.${frac}` : ""}`;
|
|
157
|
-
}
|
|
158
|
-
var CURRENCY_SYMBOLS = { USD: "$", EUR: "\u20AC", GBP: "\xA3", JPY: "\xA5", CNY: "\xA5", INR: "\u20B9", AUD: "A$", CAD: "C$" };
|
|
159
|
-
function formatChartNumber(value, format = {}, fallbackDecimals) {
|
|
160
|
-
if (value === null || value === void 0 || !Number.isFinite(value)) return "";
|
|
161
|
-
if (format.code) return `${_nullishCoalesce(format.prefix, () => ( ""))}${formatChartValueWithCode(value, format.code)}${_nullishCoalesce(format.suffix, () => ( ""))}`;
|
|
162
|
-
const style = _nullishCoalesce(format.style, () => ( "number"));
|
|
163
|
-
let body;
|
|
164
|
-
const decimalsFor = (v) => {
|
|
165
|
-
if (format.decimals !== void 0) return format.decimals;
|
|
166
|
-
if (fallbackDecimals !== void 0) return fallbackDecimals;
|
|
167
|
-
return Number.isInteger(v) ? 0 : Math.abs(v) < 1 ? Math.min(4, Math.max(2, magnitudeDecimals(Math.abs(v)) + 1)) : 2;
|
|
168
|
-
};
|
|
169
|
-
const trim = (s) => format.decimals === void 0 && fallbackDecimals === void 0 && s.includes(".") ? s.replace(/\.?0+$/, "") : s;
|
|
170
|
-
if (style === "percent") {
|
|
171
|
-
const v = value * 100;
|
|
172
|
-
const d = _nullishCoalesce(format.decimals, () => ( (fallbackDecimals !== void 0 ? Math.max(0, fallbackDecimals - 2) : Number.isInteger(clean(v)) ? 0 : 1)));
|
|
173
|
-
body = `${grouped(clean(v), d)}%`;
|
|
174
|
-
} else if (style === "compact") {
|
|
175
|
-
const unit = COMPACT.find((u) => Math.abs(value) >= u.value);
|
|
176
|
-
if (unit) {
|
|
177
|
-
const v = value / unit.value;
|
|
178
|
-
const d = _nullishCoalesce(format.decimals, () => ( (Math.abs(v) >= 100 || Number.isInteger(clean(v)) ? 0 : 1)));
|
|
179
|
-
body = `${trim(grouped(v, d))}${unit.suffix}`;
|
|
180
|
-
} else body = trim(grouped(value, decimalsFor(value)));
|
|
181
|
-
} else if (style === "currency") {
|
|
182
|
-
const code = (format.currency || "USD").toUpperCase();
|
|
183
|
-
const symbol = _nullishCoalesce(CURRENCY_SYMBOLS[code], () => ( `${code} `));
|
|
184
|
-
const d = _nullishCoalesce(format.decimals, () => ( (fallbackDecimals !== void 0 ? fallbackDecimals : Number.isInteger(value) ? 0 : 2)));
|
|
185
|
-
const s = grouped(Math.abs(value), d);
|
|
186
|
-
body = `${value < 0 ? "-" : ""}${symbol}${s}`;
|
|
187
|
-
} else body = trim(grouped(value, decimalsFor(value)));
|
|
188
|
-
return `${_nullishCoalesce(format.prefix, () => ( ""))}${body}${_nullishCoalesce(format.suffix, () => ( ""))}`;
|
|
189
|
-
}
|
|
190
|
-
function linearMap(value, domain, range) {
|
|
191
|
-
const [d0, d1] = domain;
|
|
192
|
-
const [r0, r1] = range;
|
|
193
|
-
if (d1 === d0) return (r0 + r1) / 2;
|
|
194
|
-
return r0 + (value - d0) / (d1 - d0) * (r1 - r0);
|
|
195
|
-
}
|
|
196
87
|
|
|
197
|
-
// src/chart/date.ts
|
|
198
|
-
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
199
|
-
function parseChartDate(label) {
|
|
200
|
-
const text = label.trim();
|
|
201
|
-
let m = /^(\d{4})$/.exec(text);
|
|
202
|
-
if (m) return Date.UTC(Number(m[1]), 0, 1);
|
|
203
|
-
m = /^(\d{4})[-/](\d{1,2})$/.exec(text);
|
|
204
|
-
if (m) return valid(Number(m[1]), Number(m[2]) - 1, 1);
|
|
205
|
-
m = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})(?:[T ](\d{1,2}):(\d{2})(?::(\d{2}))?(?:\.\d+)?(Z|[+-]\d{2}:?\d{2})?)?$/.exec(text);
|
|
206
|
-
if (m) {
|
|
207
|
-
if (m[4] !== void 0) {
|
|
208
|
-
const t = Date.parse(text.replace(" ", "T").replace(/\//g, "-") + (m[7] ? "" : "Z"));
|
|
209
|
-
return Number.isFinite(t) ? t : null;
|
|
210
|
-
}
|
|
211
|
-
return valid(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
|
|
212
|
-
}
|
|
213
|
-
m = /^(?:(\d{1,2})\s+)?([A-Za-z]{3})[a-z]*\.?,?\s+(\d{4})$/.exec(text);
|
|
214
|
-
if (m) {
|
|
215
|
-
const month = MONTHS.findIndex((x) => x.toLowerCase() === m[2].toLowerCase());
|
|
216
|
-
if (month >= 0) return valid(Number(m[3]), month, m[1] ? Number(m[1]) : 1);
|
|
217
|
-
}
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
function valid(y, mo, d) {
|
|
221
|
-
const t = Date.UTC(y, mo, d);
|
|
222
|
-
const date = new Date(t);
|
|
223
|
-
return date.getUTCFullYear() === y && date.getUTCMonth() === mo && date.getUTCDate() === d ? t : null;
|
|
224
|
-
}
|
|
225
|
-
var CHART_DATE_STEPS = [
|
|
226
|
-
{ unit: "day", count: 1 },
|
|
227
|
-
{ unit: "day", count: 2 },
|
|
228
|
-
{ unit: "week", count: 1 },
|
|
229
|
-
{ unit: "week", count: 2 },
|
|
230
|
-
{ unit: "month", count: 1 },
|
|
231
|
-
{ unit: "month", count: 3 },
|
|
232
|
-
{ unit: "month", count: 6 },
|
|
233
|
-
{ unit: "year", count: 1 },
|
|
234
|
-
{ unit: "year", count: 2 },
|
|
235
|
-
{ unit: "year", count: 5 },
|
|
236
|
-
{ unit: "year", count: 10 },
|
|
237
|
-
{ unit: "year", count: 25 },
|
|
238
|
-
{ unit: "year", count: 50 },
|
|
239
|
-
{ unit: "year", count: 100 }
|
|
240
|
-
];
|
|
241
|
-
function bucket(t, step) {
|
|
242
|
-
const d = new Date(t);
|
|
243
|
-
const y = d.getUTCFullYear();
|
|
244
|
-
switch (step.unit) {
|
|
245
|
-
case "day":
|
|
246
|
-
return Math.floor(Math.floor(t / 864e5) / step.count);
|
|
247
|
-
case "week":
|
|
248
|
-
return Math.floor(Math.floor((t / 864e5 - 4) / 7) / step.count);
|
|
249
|
-
case "month":
|
|
250
|
-
return Math.floor((y * 12 + d.getUTCMonth()) / step.count);
|
|
251
|
-
case "year":
|
|
252
|
-
return Math.floor(y / step.count);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
function formatChartDate(t, step, previous = null) {
|
|
256
|
-
const d = new Date(t);
|
|
257
|
-
const y = d.getUTCFullYear();
|
|
258
|
-
const mo = MONTHS[d.getUTCMonth()];
|
|
259
|
-
const sameYear = previous !== null && new Date(previous).getUTCFullYear() === y;
|
|
260
|
-
if (step.unit === "year") return String(y);
|
|
261
|
-
if (step.unit === "month") return sameYear ? mo : `${mo} ${y}`;
|
|
262
|
-
return sameYear ? `${d.getUTCDate()} ${mo}` : `${d.getUTCDate()} ${mo} ${y}`;
|
|
263
|
-
}
|
|
264
|
-
function chartDateLabels(categories, band, measure, gap = 10) {
|
|
265
|
-
const times = categories.map(parseChartDate);
|
|
266
|
-
if (times.filter((t) => t !== null).length < Math.max(1, categories.length / 2)) return null;
|
|
267
|
-
const present = times.filter((t) => t !== null).sort((x, y) => x - y);
|
|
268
|
-
const gaps = present.slice(1).map((t, i) => t - present[i]).filter((g) => g > 0).sort((x, y) => x - y);
|
|
269
|
-
const spacing = gaps.length ? gaps[Math.floor(gaps.length / 2)] : 0;
|
|
270
|
-
const DAY = 864e5;
|
|
271
|
-
const length = (step) => step.count * (step.unit === "day" ? DAY : step.unit === "week" ? 7 * DAY : step.unit === "month" ? 30 * DAY : 365 * DAY);
|
|
272
|
-
for (const step of CHART_DATE_STEPS) {
|
|
273
|
-
if (length(step) < spacing * 0.5 && step !== CHART_DATE_STEPS[CHART_DATE_STEPS.length - 1]) continue;
|
|
274
|
-
const indexes = [];
|
|
275
|
-
const labels = /* @__PURE__ */ new Map();
|
|
276
|
-
let last = null;
|
|
277
|
-
let lastTime = null;
|
|
278
|
-
times.forEach((t, i) => {
|
|
279
|
-
if (t === null) return;
|
|
280
|
-
const b = bucket(t, step);
|
|
281
|
-
if (b === last) return;
|
|
282
|
-
last = b;
|
|
283
|
-
indexes.push(i);
|
|
284
|
-
labels.set(i, formatChartDate(t, step, lastTime));
|
|
285
|
-
lastTime = t;
|
|
286
|
-
});
|
|
287
|
-
let fits = true;
|
|
288
|
-
for (let k = 1; k < indexes.length && fits; k++) {
|
|
289
|
-
const a = labels.get(indexes[k - 1]);
|
|
290
|
-
const b = labels.get(indexes[k]);
|
|
291
|
-
if ((indexes[k] - indexes[k - 1]) * band < (measure(a) + measure(b)) / 2 + gap) fits = false;
|
|
292
|
-
}
|
|
293
|
-
if (fits || step === CHART_DATE_STEPS[CHART_DATE_STEPS.length - 1]) return { step, indexes, labels };
|
|
294
|
-
}
|
|
295
|
-
return null;
|
|
296
|
-
}
|
|
297
88
|
|
|
298
|
-
// src/chart/stats.ts
|
|
299
|
-
function chartHistogram(values, bins = null) {
|
|
300
|
-
const vals = values.filter((v) => v !== null && Number.isFinite(v));
|
|
301
|
-
if (!vals.length) return [];
|
|
302
|
-
let lo = Infinity;
|
|
303
|
-
let hi = -Infinity;
|
|
304
|
-
for (const v of vals) {
|
|
305
|
-
lo = Math.min(lo, v);
|
|
306
|
-
hi = Math.max(hi, v);
|
|
307
|
-
}
|
|
308
|
-
const span = hi - lo;
|
|
309
|
-
let count = _nullishCoalesce(bins, () => ( Math.max(1, Math.min(100, Math.ceil(Math.sqrt(vals.length))))));
|
|
310
|
-
if (!(span > 0)) count = 1;
|
|
311
|
-
const width = count === 1 && !(span > 0) ? 1 : span / count;
|
|
312
|
-
const edges = Array.from({ length: count }, (_, b) => b + 1 === count ? Math.max(hi, lo + b * width) : lo + (b + 1) * width);
|
|
313
|
-
const counts = Array(count).fill(0);
|
|
314
|
-
for (const v of vals) {
|
|
315
|
-
let a = 0;
|
|
316
|
-
let z = count;
|
|
317
|
-
while (a < z) {
|
|
318
|
-
const mid = a + z >> 1;
|
|
319
|
-
if (edges[mid] < v) a = mid + 1;
|
|
320
|
-
else z = mid;
|
|
321
|
-
}
|
|
322
|
-
counts[Math.min(a, count - 1)]++;
|
|
323
|
-
}
|
|
324
|
-
return counts.map((c, b) => ({ from: clean2(lo + b * width), to: clean2(edges[b]), count: c }));
|
|
325
|
-
}
|
|
326
|
-
var clean2 = (n) => Math.abs(n) < 1e-12 ? 0 : Number(n.toPrecision(12));
|
|
327
|
-
function chartQuantile(sorted, q) {
|
|
328
|
-
const n = sorted.length;
|
|
329
|
-
if (!n) return 0;
|
|
330
|
-
const pos = q * (n - 1);
|
|
331
|
-
const i = Math.floor(pos);
|
|
332
|
-
const a = sorted[i];
|
|
333
|
-
const b = _nullishCoalesce(sorted[i + 1], () => ( a));
|
|
334
|
-
return a + (b - a) * (pos - i);
|
|
335
|
-
}
|
|
336
|
-
function chartBoxStats(values) {
|
|
337
|
-
const v = values.filter((x) => x !== null && Number.isFinite(x)).sort((a, b) => a - b);
|
|
338
|
-
if (!v.length) return null;
|
|
339
|
-
const q1 = chartQuantile(v, 0.25);
|
|
340
|
-
const median = chartQuantile(v, 0.5);
|
|
341
|
-
const q3 = chartQuantile(v, 0.75);
|
|
342
|
-
const iqr = q3 - q1;
|
|
343
|
-
const lo = q1 - 1.5 * iqr;
|
|
344
|
-
const hi = q3 + 1.5 * iqr;
|
|
345
|
-
return {
|
|
346
|
-
min: v[0],
|
|
347
|
-
max: v[v.length - 1],
|
|
348
|
-
q1,
|
|
349
|
-
median,
|
|
350
|
-
q3,
|
|
351
|
-
mean: v.reduce((t, x) => t + x, 0) / v.length,
|
|
352
|
-
lowerWhisker: _nullishCoalesce(v.find((x) => x >= lo), () => ( q1)),
|
|
353
|
-
upperWhisker: _nullishCoalesce([...v].reverse().find((x) => x <= hi), () => ( q3)),
|
|
354
|
-
outliers: v.filter((x) => x < lo || x > hi),
|
|
355
|
-
count: v.length
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
function chartSquarify(values, rect) {
|
|
359
|
-
const out = values.map(() => ({ x: rect.x, y: rect.y, width: 0, height: 0 }));
|
|
360
|
-
const items = values.map((v, i2) => [i2, v]).filter(([, v]) => v > 0 && Number.isFinite(v)).sort((a, b) => b[1] - a[1]);
|
|
361
|
-
const total = items.reduce((t, [, v]) => t + v, 0);
|
|
362
|
-
const area = rect.width * rect.height;
|
|
363
|
-
if (!(total > 0 && area > 0)) return out;
|
|
364
|
-
const scale = area / total;
|
|
365
|
-
const rem = { ...rect };
|
|
366
|
-
let row = [];
|
|
367
|
-
const worst = (r, side) => {
|
|
368
|
-
const s = r.reduce((t, v) => t + v, 0);
|
|
369
|
-
if (s <= 0 || side <= 0) return Infinity;
|
|
370
|
-
const mn = Math.min(...r);
|
|
371
|
-
const mx = Math.max(...r);
|
|
372
|
-
return Math.max(side * side * mx / (s * s), s * s / (side * side * mn));
|
|
373
|
-
};
|
|
374
|
-
const layoutRow = (r) => {
|
|
375
|
-
const s = r.reduce((t, [, a]) => t + a, 0);
|
|
376
|
-
if (s <= 0) return;
|
|
377
|
-
if (rem.width >= rem.height) {
|
|
378
|
-
const cw = s / Math.max(1e-6, rem.height);
|
|
379
|
-
let y = rem.y;
|
|
380
|
-
for (const [j, a] of r) {
|
|
381
|
-
const h = a / Math.max(1e-6, cw);
|
|
382
|
-
out[j] = { x: rem.x, y, width: cw, height: h };
|
|
383
|
-
y += h;
|
|
384
|
-
}
|
|
385
|
-
rem.x += cw;
|
|
386
|
-
rem.width = Math.max(0, rem.width - cw);
|
|
387
|
-
} else {
|
|
388
|
-
const rh = s / Math.max(1e-6, rem.width);
|
|
389
|
-
let x = rem.x;
|
|
390
|
-
for (const [j, a] of r) {
|
|
391
|
-
const w = a / Math.max(1e-6, rh);
|
|
392
|
-
out[j] = { x, y: rem.y, width: w, height: rh };
|
|
393
|
-
x += w;
|
|
394
|
-
}
|
|
395
|
-
rem.y += rh;
|
|
396
|
-
rem.height = Math.max(0, rem.height - rh);
|
|
397
|
-
}
|
|
398
|
-
};
|
|
399
|
-
let i = 0;
|
|
400
|
-
while (i < items.length) {
|
|
401
|
-
const [j, v] = items[i];
|
|
402
|
-
const a = v * scale;
|
|
403
|
-
const side = Math.min(rem.width, rem.height);
|
|
404
|
-
const cur = row.map(([, x]) => x);
|
|
405
|
-
if (!row.length || worst([...cur, a], side) <= worst(cur, side)) {
|
|
406
|
-
row.push([j, a]);
|
|
407
|
-
i++;
|
|
408
|
-
} else {
|
|
409
|
-
layoutRow(row);
|
|
410
|
-
row = [];
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
if (row.length) layoutRow(row);
|
|
414
|
-
return out;
|
|
415
|
-
}
|
|
416
|
-
function decimateChartPoints(points, max = 2e3) {
|
|
417
|
-
if (points.length <= max) return [...points];
|
|
418
|
-
const out = [];
|
|
419
|
-
let i = 0;
|
|
420
|
-
while (i < points.length) {
|
|
421
|
-
const col = Math.floor(points[i].x);
|
|
422
|
-
const start = i;
|
|
423
|
-
let mn = i;
|
|
424
|
-
let mx = i;
|
|
425
|
-
let k = i + 1;
|
|
426
|
-
while (k < points.length && Math.floor(points[k].x) === col) {
|
|
427
|
-
if (points[k].y < points[mn].y) mn = k;
|
|
428
|
-
if (points[k].y > points[mx].y) mx = k;
|
|
429
|
-
k++;
|
|
430
|
-
}
|
|
431
|
-
const end = k - 1;
|
|
432
|
-
for (const idx of [.../* @__PURE__ */ new Set([start, mn, mx, end])].sort((a, b) => a - b)) out.push(points[idx]);
|
|
433
|
-
i = k;
|
|
434
|
-
}
|
|
435
|
-
return out;
|
|
436
|
-
}
|
|
437
|
-
var round = (n) => Number(n.toPrecision(6));
|
|
438
|
-
function summarizeChartSeries(series, categories) {
|
|
439
|
-
const present = series.values.map((v, i) => [i, v]).filter((p) => p[1] !== null && Number.isFinite(p[1]));
|
|
440
|
-
const base = { id: series.id, name: series.name, count: present.length, gaps: series.values.length - present.length, min: null, max: null, mean: null, sum: null, minAt: null, maxAt: null, trend: null, change: null };
|
|
441
|
-
if (!present.length) return base;
|
|
442
|
-
let lo = present[0];
|
|
443
|
-
let hi = present[0];
|
|
444
|
-
let sum = 0;
|
|
445
|
-
for (const p of present) {
|
|
446
|
-
if (p[1] < lo[1]) lo = p;
|
|
447
|
-
if (p[1] > hi[1]) hi = p;
|
|
448
|
-
sum += p[1];
|
|
449
|
-
}
|
|
450
|
-
const mean = sum / present.length;
|
|
451
|
-
let trend = null;
|
|
452
|
-
if (present.length >= 2) {
|
|
453
|
-
const mx = present.reduce((t, p) => t + p[0], 0) / present.length;
|
|
454
|
-
let sxy = 0;
|
|
455
|
-
let sxx = 0;
|
|
456
|
-
for (const [i, v] of present) {
|
|
457
|
-
sxy += (i - mx) * (v - mean);
|
|
458
|
-
sxx += (i - mx) ** 2;
|
|
459
|
-
}
|
|
460
|
-
const slope = sxx ? sxy / sxx : 0;
|
|
461
|
-
const spread = hi[1] - lo[1];
|
|
462
|
-
const travel = Math.abs(slope * (present[present.length - 1][0] - present[0][0]));
|
|
463
|
-
trend = !spread || travel < spread * 0.05 ? "flat" : slope > 0 ? "rising" : "falling";
|
|
464
|
-
}
|
|
465
|
-
const first = present[0][1];
|
|
466
|
-
const last = present[present.length - 1][1];
|
|
467
|
-
return {
|
|
468
|
-
...base,
|
|
469
|
-
min: round(lo[1]),
|
|
470
|
-
max: round(hi[1]),
|
|
471
|
-
mean: round(mean),
|
|
472
|
-
sum: round(sum),
|
|
473
|
-
minAt: _nullishCoalesce(categories[lo[0]], () => ( String(lo[0]))),
|
|
474
|
-
maxAt: _nullishCoalesce(categories[hi[0]], () => ( String(hi[0]))),
|
|
475
|
-
trend,
|
|
476
|
-
change: present.length >= 2 && first !== 0 ? round((last - first) / Math.abs(first)) : null
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
89
|
|
|
480
|
-
// src/chart/trend.ts
|
|
481
|
-
var degree = (t) => t.type === "polynomial" ? Math.max(2, Math.min(6, _nullishCoalesce(t.order, () => ( 2)))) : 1;
|
|
482
|
-
var logX = (type) => type === "logarithmic" || type === "power";
|
|
483
|
-
var logY = (type) => type === "exponential" || type === "power";
|
|
484
|
-
function fitChartTrendline(trendline, points) {
|
|
485
|
-
const type = trendline.type;
|
|
486
|
-
if (type === "moving-average") return null;
|
|
487
|
-
const d = degree(trendline);
|
|
488
|
-
const data = points.filter(([x, y]) => Number.isFinite(x) && Number.isFinite(y) && (!logX(type) || x > 0) && (!logY(type) || y > 0)).map(([x, y]) => [logX(type) ? Math.log(x) : x, logY(type) ? Math.log(y) : y]);
|
|
489
|
-
if (data.length < 2 || data.length <= d) return null;
|
|
490
|
-
let fixed = null;
|
|
491
|
-
const b = trendline.intercept;
|
|
492
|
-
if (b !== void 0 && b !== null && Number.isFinite(b)) {
|
|
493
|
-
if (type === "linear" || type === "polynomial") fixed = b;
|
|
494
|
-
else if (type === "exponential" && b > 0) fixed = Math.log(b);
|
|
495
|
-
}
|
|
496
|
-
const n = data.length;
|
|
497
|
-
const center = fixed !== null ? 0 : data.reduce((t, p) => t + p[0], 0) / n;
|
|
498
|
-
const scale = data.reduce((m2, p) => Math.max(m2, Math.abs(p[0] - center)), 0);
|
|
499
|
-
if (!(Number.isFinite(scale) && scale > 0)) return null;
|
|
500
|
-
const first = fixed !== null ? 1 : 0;
|
|
501
|
-
const b0 = _nullishCoalesce(fixed, () => ( 0));
|
|
502
|
-
const m = d + 1 - first;
|
|
503
|
-
const ata = Array.from({ length: m }, () => Array(m).fill(0));
|
|
504
|
-
const aty = Array(m).fill(0);
|
|
505
|
-
for (const [x, y] of data) {
|
|
506
|
-
const u = (x - center) / scale;
|
|
507
|
-
const pows = Array.from({ length: m }, (_, k) => Math.pow(u, k + first));
|
|
508
|
-
for (let i = 0; i < m; i++) {
|
|
509
|
-
aty[i] += pows[i] * (y - b0);
|
|
510
|
-
for (let j = 0; j < m; j++) ata[i][j] += pows[i] * pows[j];
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
const sol = solve(ata, aty);
|
|
514
|
-
if (!sol) return null;
|
|
515
|
-
const a = Array(d + 1).fill(b0);
|
|
516
|
-
sol.forEach((v, k) => a[k + first] = v);
|
|
517
|
-
const coefficients = expand(a, center, scale);
|
|
518
|
-
if (!coefficients) return null;
|
|
519
|
-
const poly = (x) => {
|
|
520
|
-
const u = (x - center) / scale;
|
|
521
|
-
return a.reduceRight((acc, c) => acc * u + c, 0);
|
|
522
|
-
};
|
|
523
|
-
const mean = data.reduce((t, p) => t + p[1], 0) / n;
|
|
524
|
-
let ssRes = 0;
|
|
525
|
-
let ssTot = 0;
|
|
526
|
-
for (const [x, y] of data) {
|
|
527
|
-
ssRes += (y - poly(x)) ** 2;
|
|
528
|
-
ssTot += fixed !== null ? (y - b0) ** 2 : (y - mean) ** 2;
|
|
529
|
-
}
|
|
530
|
-
const r22 = ssTot > 0 && Number.isFinite(ssTot) && Number.isFinite(ssRes) ? 1 - ssRes / ssTot : null;
|
|
531
|
-
return {
|
|
532
|
-
type,
|
|
533
|
-
coefficients,
|
|
534
|
-
r2: r22,
|
|
535
|
-
equation: equationText(type, coefficients),
|
|
536
|
-
points: n,
|
|
537
|
-
evaluate(x) {
|
|
538
|
-
let xx = x;
|
|
539
|
-
if (logX(type)) {
|
|
540
|
-
if (!(x > 0)) return null;
|
|
541
|
-
xx = Math.log(x);
|
|
542
|
-
}
|
|
543
|
-
const p = poly(xx);
|
|
544
|
-
const y = logY(type) ? Math.exp(p) : p;
|
|
545
|
-
return Number.isFinite(y) ? y : null;
|
|
546
|
-
}
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
function solve(a, b) {
|
|
550
|
-
const n = b.length;
|
|
551
|
-
const big = a.flat().reduce((m, v) => Math.max(m, Math.abs(v)), 0);
|
|
552
|
-
if (!(Number.isFinite(big) && big > 0)) return null;
|
|
553
|
-
for (let col = 0; col < n; col++) {
|
|
554
|
-
let piv = col;
|
|
555
|
-
for (let r = col + 1; r < n; r++) if (Math.abs(a[r][col]) > Math.abs(a[piv][col])) piv = r;
|
|
556
|
-
[a[col], a[piv]] = [a[piv], a[col]];
|
|
557
|
-
[b[col], b[piv]] = [b[piv], b[col]];
|
|
558
|
-
const p = a[col][col];
|
|
559
|
-
if (!Number.isFinite(p) || Math.abs(p) <= big * 1e-13) return null;
|
|
560
|
-
for (let r = col + 1; r < n; r++) {
|
|
561
|
-
const f = a[r][col] / p;
|
|
562
|
-
for (let c = col; c < n; c++) a[r][c] -= f * a[col][c];
|
|
563
|
-
b[r] -= f * b[col];
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
const x = Array(n).fill(0);
|
|
567
|
-
for (let r = n - 1; r >= 0; r--) {
|
|
568
|
-
let s = 0;
|
|
569
|
-
for (let c = r + 1; c < n; c++) s += a[r][c] * x[c];
|
|
570
|
-
x[r] = (b[r] - s) / a[r][r];
|
|
571
|
-
}
|
|
572
|
-
return x.every(Number.isFinite) ? x : null;
|
|
573
|
-
}
|
|
574
|
-
function expand(a, c, s) {
|
|
575
|
-
const out = Array(a.length).fill(0);
|
|
576
|
-
a.forEach((ak, k) => {
|
|
577
|
-
const lead = ak / Math.pow(s, k);
|
|
578
|
-
let binom = 1;
|
|
579
|
-
for (let j = 0; j <= k; j++) {
|
|
580
|
-
out[j] += lead * binom * Math.pow(-c, k - j);
|
|
581
|
-
binom = binom * (k - j) / (j + 1);
|
|
582
|
-
}
|
|
583
|
-
});
|
|
584
|
-
return out.every(Number.isFinite) ? out : null;
|
|
585
|
-
}
|
|
586
|
-
var POWERS = ["", "x", "x\xB2", "x\xB3", "x\u2074", "x\u2075", "x\u2076"];
|
|
587
|
-
function equationText(type, coef) {
|
|
588
|
-
const c0 = _nullishCoalesce(coef[0], () => ( 0));
|
|
589
|
-
const c1 = _nullishCoalesce(coef[1], () => ( 0));
|
|
590
|
-
if (type === "exponential") return `y = ${trendNumberText(Math.exp(c0))}e^(${trendNumberText(c1)}x)`;
|
|
591
|
-
if (type === "power") return `y = ${trendNumberText(Math.exp(c0))}x^${trendNumberText(c1)}`;
|
|
592
|
-
if (type === "logarithmic") return terms([[c1, "ln(x)"], [c0, ""]]);
|
|
593
|
-
return terms(coef.map((c, k) => [c, _nullishCoalesce(POWERS[k], () => ( "x"))]).reverse());
|
|
594
|
-
}
|
|
595
|
-
function terms(parts) {
|
|
596
|
-
let s = "y = ";
|
|
597
|
-
let first = true;
|
|
598
|
-
for (const [c, t] of parts) {
|
|
599
|
-
const txt = trendNumberText(Math.abs(c));
|
|
600
|
-
if (txt === "0") continue;
|
|
601
|
-
const neg = c < 0;
|
|
602
|
-
if (first) s += neg ? "-" : "";
|
|
603
|
-
else s += neg ? " - " : " + ";
|
|
604
|
-
if (txt !== "1" || !t) s += txt;
|
|
605
|
-
s += t;
|
|
606
|
-
first = false;
|
|
607
|
-
}
|
|
608
|
-
return first ? `${s}0` : s;
|
|
609
|
-
}
|
|
610
|
-
function trendNumberText(v) {
|
|
611
|
-
if (!Number.isFinite(v)) return "#NUM!";
|
|
612
|
-
const a = Math.abs(v);
|
|
613
|
-
if (a === 0) return "0";
|
|
614
|
-
if (a < 1e-4 || a >= 1e11) {
|
|
615
|
-
const [mant, exp] = v.toExponential(4).split("e");
|
|
616
|
-
const e = Number(exp);
|
|
617
|
-
return `${trimZeros(mant)}E${e < 0 ? "-" : "+"}${String(Math.abs(e)).padStart(2, "0")}`;
|
|
618
|
-
}
|
|
619
|
-
const s = trimZeros(v.toFixed(4));
|
|
620
|
-
return s === "-0" ? "0" : s;
|
|
621
|
-
}
|
|
622
|
-
var trimZeros = (s) => s.includes(".") ? s.replace(/0+$/, "").replace(/\.$/, "") : s;
|
|
623
|
-
function chartMovingAverage(points, period) {
|
|
624
|
-
const p = Math.max(2, Math.min(1e3, Math.round(period) || 2));
|
|
625
|
-
const pts = points.filter(([x, y]) => Number.isFinite(x) && Number.isFinite(y));
|
|
626
|
-
const out = [];
|
|
627
|
-
let sum = 0;
|
|
628
|
-
pts.forEach(([x, y], i) => {
|
|
629
|
-
sum += y;
|
|
630
|
-
if (i >= p) sum -= pts[i - p][1];
|
|
631
|
-
if (i >= p - 1) out.push([x, sum / p]);
|
|
632
|
-
});
|
|
633
|
-
return out;
|
|
634
|
-
}
|
|
635
|
-
function chartTrendlineName(trendline, seriesName) {
|
|
636
|
-
if (trendline.name) return trendline.name;
|
|
637
|
-
const kind = {
|
|
638
|
-
linear: "Linear",
|
|
639
|
-
exponential: "Expon.",
|
|
640
|
-
logarithmic: "Log.",
|
|
641
|
-
polynomial: "Poly.",
|
|
642
|
-
power: "Power",
|
|
643
|
-
"moving-average": `${Math.max(2, _nullishCoalesce(trendline.period, () => ( 2)))} per. Mov. Avg.`
|
|
644
|
-
};
|
|
645
|
-
return `${kind[trendline.type]} (${seriesName})`;
|
|
646
|
-
}
|
|
647
|
-
function chartSupportsTrendlines(type, stacking) {
|
|
648
|
-
return (type === "bar" || type === "line" || type === "area" || type === "scatter" || type === "bubble") && stacking === "none";
|
|
649
|
-
}
|
|
650
90
|
|
|
651
|
-
// src/chart/layout-shared.ts
|
|
652
|
-
var estimateChartTextWidth = (text, size, weight = 400) => text.length * size * (weight >= 600 ? 0.6 : 0.56);
|
|
653
|
-
function sameChartSelection(a, b) {
|
|
654
|
-
if (!a || !b || a.kind !== b.kind) return false;
|
|
655
|
-
switch (a.kind) {
|
|
656
|
-
case "axis":
|
|
657
|
-
return a.axis === b.axis;
|
|
658
|
-
case "series":
|
|
659
|
-
return a.seriesId === b.seriesId;
|
|
660
|
-
case "point":
|
|
661
|
-
case "trendline":
|
|
662
|
-
return a.seriesId === b.seriesId && a.index === b.index;
|
|
663
|
-
case "category":
|
|
664
|
-
return a.index === b.index;
|
|
665
|
-
default:
|
|
666
|
-
return true;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
function contrastingTextColor(fill) {
|
|
670
|
-
let rgb = null;
|
|
671
|
-
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(fill.trim());
|
|
672
|
-
if (hex) {
|
|
673
|
-
const h = hex[1].length === 3 ? [...hex[1]].map((c) => c + c).join("") : hex[1];
|
|
674
|
-
rgb = [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16));
|
|
675
|
-
} else {
|
|
676
|
-
const fn = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i.exec(fill.trim());
|
|
677
|
-
if (fn) rgb = fn.slice(1, 4).map(Number);
|
|
678
|
-
}
|
|
679
|
-
if (!rgb) return "#ffffff";
|
|
680
|
-
const luminance = (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255;
|
|
681
|
-
return luminance > 0.5 ? "#0f172a" : "#ffffff";
|
|
682
|
-
}
|
|
683
|
-
var r2 = (n) => Math.round(n * 100) / 100;
|
|
684
|
-
function curvePath(points, curve, map, moveFirst = true) {
|
|
685
|
-
if (!points.length) return "";
|
|
686
|
-
const xy = points.map(map);
|
|
687
|
-
let d = `${moveFirst ? "M" : "L"}${r2(xy[0][0])},${r2(xy[0][1])}`;
|
|
688
|
-
if (points.length === 1) return d;
|
|
689
|
-
if (curve === "step") {
|
|
690
|
-
for (let i = 1; i < points.length; i++) {
|
|
691
|
-
const mid = (points[i - 1].c + points[i].c) / 2;
|
|
692
|
-
const a = map({ c: mid, v: points[i - 1].v });
|
|
693
|
-
const b = map({ c: mid, v: points[i].v });
|
|
694
|
-
d += `L${r2(a[0])},${r2(a[1])}L${r2(b[0])},${r2(b[1])}L${r2(xy[i][0])},${r2(xy[i][1])}`;
|
|
695
|
-
}
|
|
696
|
-
return d;
|
|
697
|
-
}
|
|
698
|
-
if (curve === "smooth" && points.length > 2) {
|
|
699
|
-
const n = points.length;
|
|
700
|
-
const dc = points.slice(1).map((p, i) => p.c - points[i].c);
|
|
701
|
-
const slope = points.slice(1).map((p, i) => dc[i] === 0 ? 0 : (p.v - points[i].v) / dc[i]);
|
|
702
|
-
const tangent = points.map((_, i) => {
|
|
703
|
-
if (i === 0) return slope[0];
|
|
704
|
-
if (i === n - 1) return slope[n - 2];
|
|
705
|
-
const a = slope[i - 1];
|
|
706
|
-
const b = slope[i];
|
|
707
|
-
if (a * b <= 0) return 0;
|
|
708
|
-
const wa = 2 * dc[i] + dc[i - 1];
|
|
709
|
-
const wb = dc[i] + 2 * dc[i - 1];
|
|
710
|
-
return (wa + wb) / (wa / a + wb / b);
|
|
711
|
-
});
|
|
712
|
-
for (let i = 1; i < n; i++) {
|
|
713
|
-
const h = dc[i - 1] / 3;
|
|
714
|
-
const p0 = points[i - 1];
|
|
715
|
-
const p1 = points[i];
|
|
716
|
-
const a = map({ c: p0.c + h, v: p0.v + tangent[i - 1] * h });
|
|
717
|
-
const b = map({ c: p1.c - h, v: p1.v - tangent[i] * h });
|
|
718
|
-
d += `C${r2(a[0])},${r2(a[1])} ${r2(b[0])},${r2(b[1])} ${r2(xy[i][0])},${r2(xy[i][1])}`;
|
|
719
|
-
}
|
|
720
|
-
return d;
|
|
721
|
-
}
|
|
722
|
-
for (let i = 1; i < xy.length; i++) d += `L${r2(xy[i][0])},${r2(xy[i][1])}`;
|
|
723
|
-
return d;
|
|
724
|
-
}
|
|
725
|
-
function arcPath(cx, cy, outer, inner, start, end) {
|
|
726
|
-
const full = end - start >= Math.PI * 2 - 1e-9;
|
|
727
|
-
if (full) end = start + Math.PI * 2 - 1e-4;
|
|
728
|
-
const p = (r, a) => `${r2(cx + r * Math.sin(a))},${r2(cy - r * Math.cos(a))}`;
|
|
729
|
-
const large = end - start > Math.PI ? 1 : 0;
|
|
730
|
-
let d = `M${p(outer, start)}A${r2(outer)},${r2(outer)} 0 ${large} 1 ${p(outer, end)}`;
|
|
731
|
-
if (inner > 0) d += `L${p(inner, end)}A${r2(inner)},${r2(inner)} 0 ${large} 0 ${p(inner, start)}Z`;
|
|
732
|
-
else d += `L${r2(cx)},${r2(cy)}Z`;
|
|
733
|
-
return d;
|
|
734
|
-
}
|
|
735
|
-
function truncate(text, maxWidth, size, measure) {
|
|
736
|
-
if (measure(text, size) <= maxWidth) return text;
|
|
737
|
-
let out = text;
|
|
738
|
-
while (out.length > 1 && measure(`${out}\u2026`, size) > maxWidth) out = out.slice(0, -1);
|
|
739
|
-
return `${out}\u2026`;
|
|
740
|
-
}
|
|
741
|
-
function markerPath(shape, cx, cy, r) {
|
|
742
|
-
switch (shape) {
|
|
743
|
-
case "square":
|
|
744
|
-
return `M${r2(cx - r)},${r2(cy - r)}H${r2(cx + r)}V${r2(cy + r)}H${r2(cx - r)}Z`;
|
|
745
|
-
case "diamond": {
|
|
746
|
-
const d = r * 1.3;
|
|
747
|
-
return `M${r2(cx)},${r2(cy - d)}L${r2(cx + d)},${r2(cy)}L${r2(cx)},${r2(cy + d)}L${r2(cx - d)},${r2(cy)}Z`;
|
|
748
|
-
}
|
|
749
|
-
case "triangle": {
|
|
750
|
-
const d = r * 1.3;
|
|
751
|
-
return `M${r2(cx)},${r2(cy - d)}L${r2(cx + d * 0.87)},${r2(cy + d * 0.5)}L${r2(cx - d * 0.87)},${r2(cy + d * 0.5)}Z`;
|
|
752
|
-
}
|
|
753
|
-
case "cross": {
|
|
754
|
-
const d = r;
|
|
755
|
-
return `M${r2(cx - d)},${r2(cy - d)}L${r2(cx + d)},${r2(cy + d)}M${r2(cx - d)},${r2(cy + d)}L${r2(cx + d)},${r2(cy - d)}`;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
var tickCount = (length) => Math.max(2, Math.min(10, Math.round(length / 56)));
|
|
760
|
-
function axisScale(lo, hi, length, axis, frozen, headroom = false) {
|
|
761
|
-
const fixedMin = frozen && Number.isFinite(frozen.min) ? frozen.min : axis.min;
|
|
762
|
-
const fixedMax = frozen && Number.isFinite(frozen.max) ? frozen.max : axis.max;
|
|
763
|
-
const step = axis.step;
|
|
764
|
-
if (step && step > 0 && Number.isFinite(lo) && Number.isFinite(hi)) {
|
|
765
|
-
let min = _nullishCoalesce(fixedMin, () => ( Math.floor(lo / step + 1e-9) * step));
|
|
766
|
-
let max = _nullishCoalesce(fixedMax, () => ( Math.ceil(hi / step - 1e-9) * step));
|
|
767
|
-
if (!(max > min)) max = min + step;
|
|
768
|
-
const first = Math.ceil(min / step - 1e-9);
|
|
769
|
-
const last = Math.floor(max / step + 1e-9);
|
|
770
|
-
if (last - first <= 200) {
|
|
771
|
-
const clean3 = (n) => Math.abs(n) < 1e-12 ? 0 : Number(n.toPrecision(12));
|
|
772
|
-
min = clean3(min);
|
|
773
|
-
max = clean3(max);
|
|
774
|
-
const ticks = [];
|
|
775
|
-
for (let i = first; i <= last; i++) ticks.push(clean3(i * step));
|
|
776
|
-
return { min, max, step, ticks };
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
let scale = niceScale(lo, hi, tickCount(length), fixedMin, fixedMax);
|
|
780
|
-
const roomy = hi + (hi - lo) * 0.05;
|
|
781
|
-
if (headroom && fixedMax === null && hi !== 0 && hi > lo && scale.max === hi && Number.isFinite(roomy)) scale = niceScale(lo, roomy, tickCount(length), fixedMin, fixedMax);
|
|
782
|
-
return scale;
|
|
783
|
-
}
|
|
784
|
-
function clipSegment(a, b, rect) {
|
|
785
|
-
let t0 = 0;
|
|
786
|
-
let t1 = 1;
|
|
787
|
-
const dx = b[0] - a[0];
|
|
788
|
-
const dy = b[1] - a[1];
|
|
789
|
-
const checks = [
|
|
790
|
-
[-dx, a[0] - rect.x],
|
|
791
|
-
[dx, rect.x + rect.width - a[0]],
|
|
792
|
-
[-dy, a[1] - rect.y],
|
|
793
|
-
[dy, rect.y + rect.height - a[1]]
|
|
794
|
-
];
|
|
795
|
-
for (const [p, q] of checks) {
|
|
796
|
-
if (p === 0) {
|
|
797
|
-
if (q < 0) return null;
|
|
798
|
-
continue;
|
|
799
|
-
}
|
|
800
|
-
const r = q / p;
|
|
801
|
-
if (p < 0) {
|
|
802
|
-
if (r > t1) return null;
|
|
803
|
-
if (r > t0) t0 = r;
|
|
804
|
-
} else {
|
|
805
|
-
if (r < t0) return null;
|
|
806
|
-
if (r < t1) t1 = r;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
return [
|
|
810
|
-
[a[0] + t0 * dx, a[1] + t0 * dy],
|
|
811
|
-
[a[0] + t1 * dx, a[1] + t1 * dy]
|
|
812
|
-
];
|
|
813
|
-
}
|
|
814
|
-
function clippedPolyline(points, rect) {
|
|
815
|
-
let d = "";
|
|
816
|
-
let open = false;
|
|
817
|
-
let last = null;
|
|
818
|
-
for (let i = 1; i < points.length; i++) {
|
|
819
|
-
const seg = clipSegment(points[i - 1], points[i], rect);
|
|
820
|
-
if (!seg) {
|
|
821
|
-
open = false;
|
|
822
|
-
continue;
|
|
823
|
-
}
|
|
824
|
-
const [p, q] = seg;
|
|
825
|
-
if (!open || !last || Math.abs(last[0] - p[0]) > 0.01 || Math.abs(last[1] - p[1]) > 0.01) d += `M${r2(p[0])},${r2(p[1])}`;
|
|
826
|
-
d += `L${r2(q[0])},${r2(q[1])}`;
|
|
827
|
-
last = q;
|
|
828
|
-
open = true;
|
|
829
|
-
}
|
|
830
|
-
return d;
|
|
831
|
-
}
|
|
832
91
|
|
|
833
|
-
// src/chart/layout-cartesian.ts
|
|
834
|
-
var MAX_CHART_DATA_LABELS = 200;
|
|
835
|
-
var MAX_CHART_LINE_POINTS = 2e3;
|
|
836
|
-
var MAX_CHART_MARKERS = 1e3;
|
|
837
|
-
function scaleInfo(scale, range, format) {
|
|
838
|
-
const domain = [scale.min, scale.max];
|
|
839
|
-
const raw = (v) => linearMap(v, domain, range);
|
|
840
|
-
const px = (v) => raw(Math.min(scale.max, Math.max(scale.min, v)));
|
|
841
|
-
return { scale, range, px, raw, zero: px(0), format, tickText: (t) => formatChartNumber(t, format, _nullishCoalesce(format.decimals, () => ( stepDecimals(scale.step)))) };
|
|
842
|
-
}
|
|
843
|
-
function categoryFrame(ctx, area, input) {
|
|
844
|
-
const { spec, theme, measure, fs, marks } = ctx;
|
|
845
|
-
const { labels, horizontal, primary, secondary } = input;
|
|
846
|
-
const xAxis = _nullishCoalesce(input.categoryAxis, () => ( spec.xAxis));
|
|
847
|
-
const n = Math.max(1, labels.length);
|
|
848
|
-
const titleBand = fs * 1.3 + 6;
|
|
849
|
-
const labelTarget = _nullishCoalesce(input.labelTarget, () => ( ((i) => ({ kind: "category", index: i }))));
|
|
850
|
-
const estimate = (v, length) => {
|
|
851
|
-
const s = axisScale(v.lo, v.hi, length, v.axis, v.frozen, v.headroom);
|
|
852
|
-
const info = scaleInfo(s, [0, 1], v.format);
|
|
853
|
-
return Math.max(0, ...s.ticks.map((t) => measure(info.tickText(t), fs)));
|
|
854
|
-
};
|
|
855
|
-
const valueLen0 = horizontal ? area.width : area.height;
|
|
856
|
-
const valueLabelWidth = primary.axis.show ? estimate(primary, valueLen0) : 0;
|
|
857
|
-
const valueLabelWidth2 = _optionalChain([secondary, 'optionalAccess', _2 => _2.axis, 'access', _3 => _3.show]) ? estimate(secondary, valueLen0) : 0;
|
|
858
|
-
const catLabelWidth = Math.max(0, ...labels.map((c) => measure(c, fs)));
|
|
859
|
-
let left;
|
|
860
|
-
let bottom;
|
|
861
|
-
let right = 8;
|
|
862
|
-
let top = horizontal ? 4 : fs / 2 + 2;
|
|
863
|
-
let rotate = 0;
|
|
864
|
-
let dateLabels = null;
|
|
865
|
-
let rotatedMax = 0;
|
|
866
|
-
if (horizontal) {
|
|
867
|
-
left = (xAxis.show ? Math.min(catLabelWidth, area.width * 0.35) + 8 : 2) + (xAxis.title ? titleBand : 0);
|
|
868
|
-
bottom = (primary.axis.show ? fs * 1.6 : 2) + (primary.axis.title ? titleBand : 0);
|
|
869
|
-
right = Math.max(8, valueLabelWidth / 2);
|
|
870
|
-
if (secondary) top = (secondary.axis.show ? fs * 1.6 : 4) + (secondary.axis.title ? titleBand : 0);
|
|
871
|
-
} else {
|
|
872
|
-
left = (primary.axis.show ? valueLabelWidth + 8 : 2) + (primary.axis.title ? titleBand : 0);
|
|
873
|
-
if (secondary) right = (secondary.axis.show ? valueLabelWidth2 + 8 : 8) + (secondary.axis.title ? titleBand : 0);
|
|
874
|
-
const band2 = (area.width - left - right) / n;
|
|
875
|
-
if (xAxis.show && xAxis.kind === "date") dateLabels = chartDateLabels(labels, band2, (t) => measure(t, fs));
|
|
876
|
-
if (xAxis.show && !dateLabels && catLabelWidth > band2 * 0.92) rotate = -40;
|
|
877
|
-
const sin = Math.sin(Math.PI / 4.5);
|
|
878
|
-
const catHeight = rotate ? Math.min(catLabelWidth * sin + fs, area.height * 0.4) : fs * 1.6;
|
|
879
|
-
if (rotate) rotatedMax = (catHeight - fs) / sin;
|
|
880
|
-
bottom = (xAxis.show ? catHeight : 2) + (xAxis.title ? titleBand : 0);
|
|
881
|
-
}
|
|
882
|
-
const plot = {
|
|
883
|
-
x: area.x + left,
|
|
884
|
-
y: area.y + top,
|
|
885
|
-
width: Math.max(1, area.width - left - right),
|
|
886
|
-
height: Math.max(1, area.height - bottom - top)
|
|
887
|
-
};
|
|
888
|
-
const valueLen = horizontal ? plot.width : plot.height;
|
|
889
|
-
const range = horizontal ? [plot.x, plot.x + plot.width] : [plot.y + plot.height, plot.y];
|
|
890
|
-
const value = scaleInfo(axisScale(primary.lo, primary.hi, valueLen, primary.axis, primary.frozen, primary.headroom), range, primary.format);
|
|
891
|
-
const value2 = secondary ? scaleInfo(axisScale(secondary.lo, secondary.hi, valueLen, secondary.axis, secondary.frozen, secondary.headroom), range, secondary.format) : void 0;
|
|
892
|
-
const catStart = horizontal ? plot.y : plot.x;
|
|
893
|
-
const catLen = horizontal ? plot.height : plot.width;
|
|
894
|
-
const band = catLen / n;
|
|
895
|
-
const catCenter = (i) => catStart + band * (i + 0.5);
|
|
896
|
-
const map = (p) => horizontal ? [p.v, p.c] : [p.c, p.v];
|
|
897
|
-
const gridAt = (info, key) => info.scale.ticks.forEach((t, i) => {
|
|
898
|
-
const p = info.px(t);
|
|
899
|
-
const [x1, y1] = map({ c: catStart, v: p });
|
|
900
|
-
const [x2, y2] = map({ c: catStart + catLen, v: p });
|
|
901
|
-
marks.push({ kind: "line", key: `grid-${key}-${i}`, role: "grid", x1, y1, x2, y2, stroke: theme.grid, strokeWidth: 1 });
|
|
902
|
-
});
|
|
903
|
-
if (primary.axis.gridlines) gridAt(value, "y");
|
|
904
|
-
if (value2 && secondary.axis.gridlines) gridAt(value2, "y2");
|
|
905
|
-
if (xAxis.gridlines) {
|
|
906
|
-
for (let i = 1; i < n; i++) {
|
|
907
|
-
const c = catStart + band * i;
|
|
908
|
-
const [x1, y1] = map({ c, v: range[0] });
|
|
909
|
-
const [x2, y2] = map({ c, v: range[1] });
|
|
910
|
-
marks.push({ kind: "line", key: `grid-x-${i}`, role: "grid", x1, y1, x2, y2, stroke: theme.grid, strokeWidth: 1 });
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
const ticksFor = (info, v, far) => {
|
|
914
|
-
if (!v.axis.show) return;
|
|
915
|
-
const target = { kind: "axis", axis: v.key };
|
|
916
|
-
info.scale.ticks.forEach((t, i) => {
|
|
917
|
-
const p = info.px(t);
|
|
918
|
-
const label = info.tickText(t);
|
|
919
|
-
const key = `tick-${v.key}-${i}`;
|
|
920
|
-
if (horizontal) {
|
|
921
|
-
if (far) marks.push({ kind: "text", key, role: "tick-label", x: p, y: plot.y - 6, text: label, anchor: "middle", baseline: "bottom", size: fs, fill: theme.mutedText, target });
|
|
922
|
-
else marks.push({ kind: "text", key, role: "tick-label", x: p, y: plot.y + plot.height + 6, text: label, anchor: "middle", baseline: "top", size: fs, fill: theme.mutedText, target });
|
|
923
|
-
} else if (far) marks.push({ kind: "text", key, role: "tick-label", x: plot.x + plot.width + 8, y: p, text: label, anchor: "start", baseline: "middle", size: fs, fill: theme.mutedText, target });
|
|
924
|
-
else marks.push({ kind: "text", key, role: "tick-label", x: plot.x - 8, y: p, text: label, anchor: "end", baseline: "middle", size: fs, fill: theme.mutedText, target });
|
|
925
|
-
});
|
|
926
|
-
};
|
|
927
|
-
ticksFor(value, primary, false);
|
|
928
|
-
if (value2) ticksFor(value2, secondary, true);
|
|
929
|
-
const xTarget = { kind: "axis", axis: "x" };
|
|
930
|
-
if (xAxis.show) {
|
|
931
|
-
const [x1, y1] = map({ c: catStart, v: value.zero });
|
|
932
|
-
const [x2, y2] = map({ c: catStart + catLen, v: value.zero });
|
|
933
|
-
marks.push({ kind: "line", key: "axis-x", role: "axis", x1, y1, x2, y2, stroke: theme.axis, strokeWidth: 1, target: xTarget });
|
|
934
|
-
const every = dateLabels ? 1 : horizontal ? Math.max(1, Math.ceil(fs * 1.3 / band)) : rotate ? Math.max(1, Math.ceil(fs * 1.4 / band)) : 1;
|
|
935
|
-
let hidden = 0;
|
|
936
|
-
let truncated = 0;
|
|
937
|
-
labels.forEach((c, i) => {
|
|
938
|
-
const text0 = dateLabels ? dateLabels.labels.get(i) : i % every ? void 0 : c;
|
|
939
|
-
if (text0 === void 0) {
|
|
940
|
-
hidden++;
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
const target = labelTarget(i);
|
|
944
|
-
const selected = target ? ctx.isSelected(target) : false;
|
|
945
|
-
const fill = selected ? theme.selection : theme.mutedText;
|
|
946
|
-
const extra = target ? { target, selected } : {};
|
|
947
|
-
if (horizontal) {
|
|
948
|
-
const maxW = left - (xAxis.title ? titleBand : 0) - 8;
|
|
949
|
-
const text = truncate(text0, maxW, fs, measure);
|
|
950
|
-
if (text !== text0) truncated++;
|
|
951
|
-
marks.push({ kind: "text", key: `tick-x-${i}`, role: "tick-label", x: plot.x - 8, y: catCenter(i), text, anchor: "end", baseline: "middle", size: fs, fill, ...extra });
|
|
952
|
-
} else if (rotate) {
|
|
953
|
-
const room = (catCenter(i) - 2) / Math.cos(Math.PI / 4.5);
|
|
954
|
-
const text = truncate(text0, Math.max(fs, Math.min(rotatedMax, room)), fs, measure);
|
|
955
|
-
if (text !== text0) truncated++;
|
|
956
|
-
marks.push({ kind: "text", key: `tick-x-${i}`, role: "tick-label", x: catCenter(i), y: plot.y + plot.height + 8, text, anchor: "end", baseline: "middle", size: fs, fill, rotate, ...extra });
|
|
957
|
-
} else {
|
|
958
|
-
const text = dateLabels ? text0 : truncate(text0, Math.max(band - 4, fs * 2), fs, measure);
|
|
959
|
-
if (text !== text0) truncated++;
|
|
960
|
-
marks.push({ kind: "text", key: `tick-x-${i}`, role: "tick-label", x: catCenter(i), y: plot.y + plot.height + 6, text, anchor: dateLabels ? "start" : "middle", baseline: "top", size: fs, fill, ...extra });
|
|
961
|
-
}
|
|
962
|
-
});
|
|
963
|
-
if (dateLabels) {
|
|
964
|
-
for (const i of dateLabels.indexes) {
|
|
965
|
-
const x = catCenter(i);
|
|
966
|
-
marks.push({ kind: "line", key: `tick-mark-x-${i}`, role: "tick", x1: x, y1: plot.y + plot.height, x2: x, y2: plot.y + plot.height + 4, stroke: theme.axis, strokeWidth: 1 });
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
if (hidden && !dateLabels) ctx.warn("labels-hidden", `${hidden} of ${labels.length} category labels are hidden so the rest do not overlap.`);
|
|
970
|
-
if (truncated) ctx.warn("labels-truncated", `${truncated} category label${truncated === 1 ? " was" : "s were"} shortened to fit.`);
|
|
971
|
-
}
|
|
972
|
-
const yTarget = { kind: "axis", axis: "y" };
|
|
973
|
-
const valueTitle = primary.axis.title;
|
|
974
|
-
const catTitle = xAxis.title;
|
|
975
|
-
const bottomTitle = horizontal ? valueTitle : catTitle;
|
|
976
|
-
const leftTitle = horizontal ? catTitle : valueTitle;
|
|
977
|
-
if (bottomTitle)
|
|
978
|
-
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 });
|
|
979
|
-
if (leftTitle)
|
|
980
|
-
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 });
|
|
981
|
-
if (_optionalChain([secondary, 'optionalAccess', _4 => _4.axis, 'access', _5 => _5.title])) {
|
|
982
|
-
const target = { kind: "axis", axis: "y2" };
|
|
983
|
-
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 });
|
|
984
|
-
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 });
|
|
985
|
-
}
|
|
986
|
-
for (const axis of ["x", "y", "y2"]) {
|
|
987
|
-
if (!ctx.isSelected({ kind: "axis", axis })) continue;
|
|
988
|
-
if (axis === "y2" && !secondary) continue;
|
|
989
|
-
let box;
|
|
990
|
-
if (axis === "y2") box = horizontal ? { x: plot.x - 4, y: area.y, width: plot.width + 8, height: plot.y - area.y - 2 } : { x: plot.x + plot.width + 2, y: plot.y - fs / 2, width: area.x + area.width - plot.x - plot.width - 2, height: plot.height + fs };
|
|
991
|
-
else {
|
|
992
|
-
const onLeft = axis === "y" !== horizontal;
|
|
993
|
-
box = onLeft ? { x: area.x, y: plot.y - fs / 2, width: left - 2, height: plot.height + fs } : { x: plot.x - 4, y: plot.y + plot.height + 2, width: plot.width + 8, height: area.y + area.height - plot.y - plot.height - 2 };
|
|
994
|
-
}
|
|
995
|
-
ctx.outline(`axis-${axis}-selection`, box);
|
|
996
|
-
}
|
|
997
|
-
return { plot, horizontal, n, band, catStart, catLen, catCenter, map, value, value2 };
|
|
998
|
-
}
|
|
999
|
-
var toValueScale = (info, axis) => ({ axis, domain: [info.scale.min, info.scale.max], range: info.range, ticks: info.scale.ticks });
|
|
1000
|
-
var order = (t) => t === "area" ? 0 : t === "bar" ? 1 : t === "line" ? 2 : 3;
|
|
1001
|
-
function pointMark(base, shape, cx, cy, r, fill, stroke, strokeWidth) {
|
|
1002
|
-
if (shape === "circle" || shape === "hit" || shape === "none") return { kind: "circle", role: "point", ...base, cx, cy, r, fill, ...stroke ? { stroke, strokeWidth } : {} };
|
|
1003
|
-
const closed = shape !== "cross";
|
|
1004
|
-
return { kind: "path", role: "point", ...base, d: markerPath(shape, cx, cy, r), fill: closed ? fill : "none", stroke: _nullishCoalesce(stroke, () => ( fill)), strokeWidth: _nullishCoalesce(strokeWidth, () => ( 2)) };
|
|
1005
|
-
}
|
|
1006
|
-
function drawTrendlines(ctx, plot, series, color, points, toPx, labels) {
|
|
1007
|
-
(_nullishCoalesce(series.trendlines, () => ( []))).forEach((t, ti) => {
|
|
1008
|
-
const target = { kind: "trendline", seriesId: series.id, index: ti };
|
|
1009
|
-
const selected = ctx.isSelected(target);
|
|
1010
|
-
const stroke = _nullishCoalesce(t.color, () => ( color));
|
|
1011
|
-
let px = [];
|
|
1012
|
-
if (t.type === "moving-average") px = chartMovingAverage(points, _nullishCoalesce(t.period, () => ( 2))).map(([x, y]) => toPx(x, y));
|
|
1013
|
-
else {
|
|
1014
|
-
const fit = fitChartTrendline(t, points);
|
|
1015
|
-
if (!fit) {
|
|
1016
|
-
ctx.warn("values-skipped", `The ${t.type} trendline on "${series.name}" could not be fitted (too few usable points).`);
|
|
1017
|
-
return;
|
|
1018
|
-
}
|
|
1019
|
-
const xs = points.map((p) => p[0]);
|
|
1020
|
-
const lo = Math.min(...xs) - (_nullishCoalesce(t.backward, () => ( 0)));
|
|
1021
|
-
const hi = Math.max(...xs) + (_nullishCoalesce(t.forward, () => ( 0)));
|
|
1022
|
-
const steps = t.type === "linear" ? 1 : 200;
|
|
1023
|
-
for (let k = 0; k <= steps; k++) {
|
|
1024
|
-
const x = lo + (hi - lo) * k / steps;
|
|
1025
|
-
const y = fit.evaluate(x);
|
|
1026
|
-
if (y !== null) px.push(toPx(x, y));
|
|
1027
|
-
}
|
|
1028
|
-
if (t.showEquation) labels.push({ text: fit.equation, color: stroke, target });
|
|
1029
|
-
if (t.showR2) labels.push({ text: fit.r2 === null ? "R\xB2 = #N/A" : `R\xB2 = ${trendNumberText(fit.r2)}`, color: stroke, target });
|
|
1030
|
-
}
|
|
1031
|
-
const d = clippedPolyline(px, plot);
|
|
1032
|
-
if (!d) return;
|
|
1033
|
-
ctx.marks.push({ kind: "path", key: `trendline-${series.id}-${ti}`, role: "trendline", d, fill: "none", stroke, strokeWidth: selected ? 3 : 1.5, dash: "6 4", target, selected });
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
function drawTrendLabels(ctx, plot, labels) {
|
|
1037
|
-
labels.forEach(
|
|
1038
|
-
(l, i) => ctx.marks.push({ kind: "text", key: `trendline-label-${i}`, role: "trendline-label", x: plot.x + plot.width - 4, y: plot.y + 4 + i * (ctx.fs + 3), text: l.text, anchor: "end", baseline: "top", size: ctx.fs - 1, fill: l.color, target: l.target })
|
|
1039
|
-
);
|
|
1040
|
-
}
|
|
1041
|
-
function barLabel(ctx, text, rect, horizontal, positive, stacked, fill, plot) {
|
|
1042
|
-
const { measure, fs, spec, theme } = ctx;
|
|
1043
|
-
const size = fs - 1;
|
|
1044
|
-
const tw = measure(text, size);
|
|
1045
|
-
const th = size + 2;
|
|
1046
|
-
const along = horizontal ? rect.width : rect.height;
|
|
1047
|
-
const across = horizontal ? rect.height : rect.width;
|
|
1048
|
-
const need = horizontal ? tw + 6 : th + 2;
|
|
1049
|
-
const fitsInside = along >= need && across >= (horizontal ? th : Math.min(tw, across + 1));
|
|
1050
|
-
let position = spec.labelPosition;
|
|
1051
|
-
if (position === "auto") position = stacked ? "center" : "outside";
|
|
1052
|
-
const cx = rect.x + rect.width / 2;
|
|
1053
|
-
const cy = rect.y + rect.height / 2;
|
|
1054
|
-
const inner = contrastingTextColor(fill);
|
|
1055
|
-
const centre = () => ({ x: cx, y: cy, anchor: "middle", baseline: "middle", fill: inner });
|
|
1056
|
-
const insideEnd = () => horizontal ? { x: positive ? rect.x + rect.width - 4 : rect.x + 4, y: cy, anchor: positive ? "end" : "start", baseline: "middle", fill: inner } : { x: cx, y: positive ? rect.y + 3 : rect.y + rect.height - 3, anchor: "middle", baseline: positive ? "top" : "bottom", fill: inner };
|
|
1057
|
-
const outside = () => horizontal ? { x: positive ? rect.x + rect.width + 4 : rect.x - 4, y: cy, anchor: positive ? "start" : "end", baseline: "middle", fill: theme.text } : { x: cx, y: positive ? rect.y - 4 : rect.y + rect.height + 4, anchor: "middle", baseline: positive ? "bottom" : "top", fill: theme.text };
|
|
1058
|
-
const outsideFits = () => {
|
|
1059
|
-
if (horizontal) return positive ? rect.x + rect.width + 4 + tw <= plot.x + plot.width + 8 : rect.x - 4 - tw >= plot.x - 8;
|
|
1060
|
-
return positive ? rect.y - 4 - th >= plot.y - fs : rect.y + rect.height + 4 + th <= plot.y + plot.height + fs;
|
|
1061
|
-
};
|
|
1062
|
-
if (position === "center") return fitsInside ? centre() : null;
|
|
1063
|
-
if (position === "inside") return fitsInside ? insideEnd() : stacked ? null : outside();
|
|
1064
|
-
if (stacked) return fitsInside ? centre() : null;
|
|
1065
|
-
if (outsideFits()) return outside();
|
|
1066
|
-
return fitsInside ? insideEnd() : null;
|
|
1067
|
-
}
|
|
1068
|
-
function layoutCartesian(ctx, area, visible) {
|
|
1069
|
-
const { spec, theme, marks, measure, fs } = ctx;
|
|
1070
|
-
const horizontal = spec.orientation === "horizontal";
|
|
1071
|
-
const n = spec.categories.length;
|
|
1072
|
-
const types = new Map(visible.map((s) => [s.id, _chunkNEOO6QJDcjs.chartSeriesType.call(void 0, spec, s)]));
|
|
1073
|
-
const stacked = spec.stacking !== "none";
|
|
1074
|
-
const percent = spec.stacking === "percent";
|
|
1075
|
-
const onSecondary = (s) => s.axis === "secondary";
|
|
1076
|
-
const useSecondary = visible.some(onSecondary);
|
|
1077
|
-
const stacks = /* @__PURE__ */ new Map();
|
|
1078
|
-
if (stacked) {
|
|
1079
|
-
for (const kind of ["bar", "area"]) {
|
|
1080
|
-
for (const secondary of [false, true]) {
|
|
1081
|
-
const group = visible.filter((s) => types.get(s.id) === kind && onSecondary(s) === secondary);
|
|
1082
|
-
if (!group.length) continue;
|
|
1083
|
-
const totals = Array.from({ length: n }, (_, i) => group.reduce((t, s) => t + Math.abs(_nullishCoalesce(s.values[i], () => ( 0))), 0));
|
|
1084
|
-
const pos = Array(n).fill(0);
|
|
1085
|
-
const neg = Array(n).fill(0);
|
|
1086
|
-
for (const s of group) {
|
|
1087
|
-
const segments = s.values.map((raw, i) => {
|
|
1088
|
-
if (raw === null && kind === "bar") return null;
|
|
1089
|
-
let v = _nullishCoalesce(raw, () => ( 0));
|
|
1090
|
-
if (percent) v = totals[i] ? v / totals[i] : 0;
|
|
1091
|
-
const base = v >= 0 ? pos[i] : neg[i];
|
|
1092
|
-
const seg = [base, base + v];
|
|
1093
|
-
if (v >= 0) pos[i] += v;
|
|
1094
|
-
else neg[i] += v;
|
|
1095
|
-
return seg;
|
|
1096
|
-
});
|
|
1097
|
-
stacks.set(s.id, { segments });
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
const range = (secondary) => {
|
|
1103
|
-
let lo = Infinity;
|
|
1104
|
-
let hi = -Infinity;
|
|
1105
|
-
const see = (v) => {
|
|
1106
|
-
lo = Math.min(lo, v);
|
|
1107
|
-
hi = Math.max(hi, v);
|
|
1108
|
-
};
|
|
1109
|
-
const group = visible.filter((s) => onSecondary(s) === secondary);
|
|
1110
|
-
for (const s of group) {
|
|
1111
|
-
const stack = stacks.get(s.id);
|
|
1112
|
-
if (stack) stack.segments.forEach((seg) => seg && (see(seg[0]), see(seg[1])));
|
|
1113
|
-
else s.values.forEach((v) => v !== null && Number.isFinite(v) && see(v));
|
|
1114
|
-
}
|
|
1115
|
-
if (!Number.isFinite(lo)) {
|
|
1116
|
-
lo = 0;
|
|
1117
|
-
hi = 1;
|
|
1118
|
-
}
|
|
1119
|
-
if (group.some((s) => types.get(s.id) === "bar" || types.get(s.id) === "area")) {
|
|
1120
|
-
lo = Math.min(lo, 0);
|
|
1121
|
-
hi = Math.max(hi, 0);
|
|
1122
|
-
}
|
|
1123
|
-
const lineLike = group.some((s) => types.get(s.id) === "line" || types.get(s.id) === "scatter");
|
|
1124
|
-
return { lo, hi, lineLike };
|
|
1125
|
-
};
|
|
1126
|
-
const pctFormat = (f) => percent && !f.style && !f.code ? { ...f, style: "percent" } : f;
|
|
1127
|
-
const p = range(false);
|
|
1128
|
-
const s2 = useSecondary ? range(true) : null;
|
|
1129
|
-
const frame = categoryFrame(ctx, area, {
|
|
1130
|
-
labels: spec.categories,
|
|
1131
|
-
horizontal,
|
|
1132
|
-
primary: { lo: p.lo, hi: p.hi, axis: spec.yAxis, format: pctFormat(spec.yAxis.format), frozen: ctx.options.valueDomain, headroom: p.lineLike, key: "y" },
|
|
1133
|
-
secondary: s2 ? { lo: s2.lo, hi: s2.hi, axis: spec.yAxis2, format: pctFormat(spec.yAxis2.format), frozen: ctx.options.valueDomain2, headroom: s2.lineLike, key: "y2" } : void 0
|
|
1134
|
-
});
|
|
1135
|
-
const { plot, band, catCenter, map, catStart } = frame;
|
|
1136
|
-
const infoOf = (s) => onSecondary(s) && frame.value2 ? frame.value2 : frame.value;
|
|
1137
|
-
const bars = visible.filter((s) => types.get(s.id) === "bar");
|
|
1138
|
-
const barAxes = [...new Set(bars.map((s) => onSecondary(s)))];
|
|
1139
|
-
const slots = stacked ? barAxes.length : bars.length;
|
|
1140
|
-
const groupWidth = band * (1 - spec.gapWidth);
|
|
1141
|
-
const o = Math.max(-1, Math.min(0.99, spec.overlap));
|
|
1142
|
-
const barWidth = slots ? groupWidth / (slots - (slots - 1) * o) : 0;
|
|
1143
|
-
const slotOf = (s) => stacked ? barAxes.indexOf(onSecondary(s)) : bars.indexOf(s);
|
|
1144
|
-
const labelMarks = [];
|
|
1145
|
-
const trendLabels = [];
|
|
1146
|
-
let hiddenLabels = 0;
|
|
1147
|
-
const trendsAllowed = chartSupportsTrendlines(spec.type, spec.stacking);
|
|
1148
|
-
const drawOrder = [...visible].sort((a, b) => order(types.get(a.id)) - order(types.get(b.id)));
|
|
1149
|
-
for (const s of drawOrder) {
|
|
1150
|
-
const si = spec.series.indexOf(s);
|
|
1151
|
-
const color = _chunkNEOO6QJDcjs.chartSeriesColor.call(void 0, spec, si);
|
|
1152
|
-
const type = types.get(s.id);
|
|
1153
|
-
const stack = stacks.get(s.id);
|
|
1154
|
-
const selSeries = ctx.seriesSelected(s.id);
|
|
1155
|
-
const seriesTarget = { kind: "series", seriesId: s.id };
|
|
1156
|
-
const info = infoOf(s);
|
|
1157
|
-
const valuePx = info.px;
|
|
1158
|
-
const labelsOn = (_nullishCoalesce(s.dataLabels, () => ( spec.dataLabels))) && s.values.length <= MAX_CHART_DATA_LABELS;
|
|
1159
|
-
if ((_nullishCoalesce(s.dataLabels, () => ( spec.dataLabels))) && !labelsOn) ctx.warn("data-labels-hidden", `Data labels are off for "${s.name}": it has more than ${MAX_CHART_DATA_LABELS} points.`);
|
|
1160
|
-
const labelFormat = _nullishCoalesce(s.format, () => ( info.format));
|
|
1161
|
-
const dataLabel = (v) => formatChartNumber(v, labelFormat, _nullishCoalesce(labelFormat.decimals, () => ( void 0)));
|
|
1162
|
-
if (type === "bar") {
|
|
1163
|
-
const slot = slotOf(s);
|
|
1164
|
-
s.values.forEach((raw, i) => {
|
|
1165
|
-
const seg = stack ? stack.segments[i] : raw === null ? null : [0, raw];
|
|
1166
|
-
if (!seg || raw === null || !Number.isFinite(raw)) return;
|
|
1167
|
-
const a = valuePx(seg[0]);
|
|
1168
|
-
const b = valuePx(seg[1]);
|
|
1169
|
-
const c0 = catCenter(i) - groupWidth / 2 + slot * barWidth * (1 - o);
|
|
1170
|
-
const cw = barWidth;
|
|
1171
|
-
const target = { kind: "point", seriesId: s.id, index: i };
|
|
1172
|
-
const selected = ctx.isSelected(target);
|
|
1173
|
-
const [x, y] = map({ c: c0, v: Math.min(a, b) });
|
|
1174
|
-
const rect = horizontal ? { x: Math.min(a, b), y: c0, width: Math.abs(b - a), height: cw } : { x, y, width: cw, height: Math.abs(b - a) };
|
|
1175
|
-
const fill = _chunkNEOO6QJDcjs.chartPointColor.call(void 0, spec, si, i);
|
|
1176
|
-
marks.push({
|
|
1177
|
-
kind: "rect",
|
|
1178
|
-
key: `bar-${s.id}-${i}`,
|
|
1179
|
-
role: "bar",
|
|
1180
|
-
...rect,
|
|
1181
|
-
fill,
|
|
1182
|
-
radius: Math.min(2, cw / 4),
|
|
1183
|
-
target,
|
|
1184
|
-
selected: selected || selSeries,
|
|
1185
|
-
...selSeries ? { stroke: theme.selection, strokeWidth: 1.5 } : {}
|
|
1186
|
-
});
|
|
1187
|
-
if (selected) ctx.outline(`bar-${s.id}-${i}-selection`, { x: rect.x - 2, y: rect.y - 2, width: rect.width + 4, height: rect.height + 4 });
|
|
1188
|
-
if (labelsOn) {
|
|
1189
|
-
const v = percent ? seg[1] - seg[0] : raw;
|
|
1190
|
-
const text = dataLabel(v);
|
|
1191
|
-
const at = barLabel(ctx, text, rect, horizontal, seg[1] >= seg[0], stacked, fill, plot);
|
|
1192
|
-
if (!at) {
|
|
1193
|
-
hiddenLabels++;
|
|
1194
|
-
return;
|
|
1195
|
-
}
|
|
1196
|
-
labelMarks.push({ kind: "text", key: `label-${s.id}-${i}`, role: "data-label", ...at, text, size: fs - 1, target });
|
|
1197
|
-
}
|
|
1198
|
-
});
|
|
1199
|
-
} else {
|
|
1200
|
-
const curve = _nullishCoalesce(s.curve, () => ( spec.curve));
|
|
1201
|
-
const pointValue = (i) => {
|
|
1202
|
-
const v = stack ? _nullishCoalesce(_optionalChain([stack, 'access', _6 => _6.segments, 'access', _7 => _7[i], 'optionalAccess', _8 => _8[1]]), () => ( null)) : s.values[i];
|
|
1203
|
-
return v !== null && Number.isFinite(v) ? v : null;
|
|
1204
|
-
};
|
|
1205
|
-
const runs = [];
|
|
1206
|
-
let run = [];
|
|
1207
|
-
for (let i = 0; i < n; i++) {
|
|
1208
|
-
const v = pointValue(i);
|
|
1209
|
-
if (v === null) {
|
|
1210
|
-
if (run.length) runs.push(run);
|
|
1211
|
-
run = [];
|
|
1212
|
-
continue;
|
|
1213
|
-
}
|
|
1214
|
-
run.push({ c: catCenter(i), v: valuePx(v) });
|
|
1215
|
-
}
|
|
1216
|
-
if (run.length) runs.push(run);
|
|
1217
|
-
const thin = (r) => {
|
|
1218
|
-
if (r.length <= MAX_CHART_LINE_POINTS) return r;
|
|
1219
|
-
const pts = decimateChartPoints(r.map((p2) => ({ x: p2.c, y: p2.v, p: p2 })), MAX_CHART_LINE_POINTS).map((q) => q.p);
|
|
1220
|
-
ctx.warn("points-decimated", `"${s.name}" has ${r.length} points; its line is drawn with ${pts.length} (one column of pixels each).`);
|
|
1221
|
-
return pts;
|
|
1222
|
-
};
|
|
1223
|
-
const drawn = runs.map(thin);
|
|
1224
|
-
if (type === "area") {
|
|
1225
|
-
const parts = drawn.map((r) => {
|
|
1226
|
-
const baseline = stack ? r.map((pt) => ({ c: pt.c, v: valuePx(_nullishCoalesce(_optionalChain([stack, 'access', _9 => _9.segments, 'access', _10 => _10[Math.round((pt.c - catStart) / band - 0.5)], 'optionalAccess', _11 => _11[0]]), () => ( 0))) })).reverse() : [
|
|
1227
|
-
{ c: r[r.length - 1].c, v: info.zero },
|
|
1228
|
-
{ c: r[0].c, v: info.zero }
|
|
1229
|
-
];
|
|
1230
|
-
return `${curvePath(r, curve, map)}${stack ? curvePath(baseline, curve, map, false) : baseline.map((pt) => `L${map(pt).map(r2).join(",")}`).join("")}Z`;
|
|
1231
|
-
});
|
|
1232
|
-
marks.push({ kind: "path", key: `area-${s.id}`, role: "area", d: parts.join(""), fill: color, opacity: 0.22, target: seriesTarget, selected: selSeries });
|
|
1233
|
-
}
|
|
1234
|
-
if (type === "line" || type === "area") {
|
|
1235
|
-
marks.push({ kind: "path", key: `line-${s.id}`, role: "line", d: drawn.map((r) => curvePath(r, curve, map)).join(""), fill: "none", stroke: color, strokeWidth: selSeries ? 3.5 : 2, target: seriesTarget, selected: selSeries });
|
|
1236
|
-
}
|
|
1237
|
-
const marker = _nullishCoalesce(s.marker, () => ( (type === "scatter" || spec.points ? "circle" : "none")));
|
|
1238
|
-
const showDots = marker !== "none";
|
|
1239
|
-
const thinMarkers = n > MAX_CHART_MARKERS;
|
|
1240
|
-
const cells = /* @__PURE__ */ new Set();
|
|
1241
|
-
for (let i = 0; i < n; i++) {
|
|
1242
|
-
const v = pointValue(i);
|
|
1243
|
-
if (v === null || s.values[i] === null) continue;
|
|
1244
|
-
const [cx, cy] = map({ c: catCenter(i), v: valuePx(v) });
|
|
1245
|
-
if (thinMarkers) {
|
|
1246
|
-
const cell = `${Math.round(cx / 2)},${Math.round(cy / 2)}`;
|
|
1247
|
-
if (cells.has(cell)) continue;
|
|
1248
|
-
cells.add(cell);
|
|
1249
|
-
}
|
|
1250
|
-
const target = { kind: "point", seriesId: s.id, index: i };
|
|
1251
|
-
const selected = ctx.isSelected(target);
|
|
1252
|
-
const r = type === "scatter" ? 4.5 : 3.5;
|
|
1253
|
-
const pointColor = _chunkNEOO6QJDcjs.chartPointColor.call(void 0, spec, si, i);
|
|
1254
|
-
const fill = showDots ? type === "scatter" ? pointColor : theme.surface : "transparent";
|
|
1255
|
-
marks.push(
|
|
1256
|
-
pointMark({ key: `point-${s.id}-${i}`, target, selected: selected || selSeries, opacity: type === "scatter" ? 0.85 : void 0 }, showDots ? marker : "hit", cx, cy, showDots ? r : 6, fill, showDots ? pointColor : void 0, 2)
|
|
1257
|
-
);
|
|
1258
|
-
if (selected) marks.push({ kind: "circle", key: `point-${s.id}-${i}-selection`, role: "selection", cx, cy, r: r + 3.5, fill: "none", stroke: theme.selection, strokeWidth: 1.5 });
|
|
1259
|
-
if (labelsOn) {
|
|
1260
|
-
const raw = s.values[i];
|
|
1261
|
-
const shown = percent && stack ? stack.segments[i][1] - stack.segments[i][0] : raw;
|
|
1262
|
-
labelMarks.push({
|
|
1263
|
-
kind: "text",
|
|
1264
|
-
key: `label-${s.id}-${i}`,
|
|
1265
|
-
role: "data-label",
|
|
1266
|
-
x: horizontal ? cx + 8 : cx,
|
|
1267
|
-
y: horizontal ? cy : cy - 8,
|
|
1268
|
-
text: dataLabel(shown),
|
|
1269
|
-
anchor: horizontal ? "start" : "middle",
|
|
1270
|
-
baseline: horizontal ? "middle" : "bottom",
|
|
1271
|
-
size: fs - 1,
|
|
1272
|
-
fill: theme.text,
|
|
1273
|
-
target
|
|
1274
|
-
});
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
if (trendsAllowed && _optionalChain([s, 'access', _12 => _12.trendlines, 'optionalAccess', _13 => _13.length])) {
|
|
1279
|
-
const points = [];
|
|
1280
|
-
s.values.forEach((v, i) => v !== null && Number.isFinite(v) && points.push([i + 1, v]));
|
|
1281
|
-
const toPx = (x, y) => map({ c: catStart + band * (x - 0.5), v: info.raw(y) });
|
|
1282
|
-
drawTrendlines(ctx, plot, s, color, points, toPx, trendLabels);
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
marks.push(...labelMarks);
|
|
1286
|
-
drawTrendLabels(ctx, plot, trendLabels);
|
|
1287
|
-
if (hiddenLabels) ctx.warn("data-labels-hidden", `${hiddenLabels} data label${hiddenLabels === 1 ? "" : "s"} did not fit and ${hiddenLabels === 1 ? "is" : "are"} hidden.`);
|
|
1288
|
-
return {
|
|
1289
|
-
plot,
|
|
1290
|
-
valueScale: toValueScale(frame.value, horizontal ? "x" : "y"),
|
|
1291
|
-
...frame.value2 ? { valueScale2: toValueScale(frame.value2, horizontal ? "x" : "y") } : {},
|
|
1292
|
-
percent
|
|
1293
|
-
};
|
|
1294
|
-
}
|
|
1295
|
-
function layoutXy(ctx, area, visible) {
|
|
1296
|
-
const { spec, theme, marks, measure, fs } = ctx;
|
|
1297
|
-
const bubble = spec.type === "bubble";
|
|
1298
|
-
const pointsOf = (s) => s.values.map((y, i) => ({ i, x: _nullishCoalesce(_optionalChain([s, 'access', _14 => _14.x, 'optionalAccess', _15 => _15[i]]), () => ( (bubble || !s.x ? i + 1 : null))), y, size: _nullishCoalesce(_optionalChain([s, 'access', _16 => _16.sizes, 'optionalAccess', _17 => _17[i]]), () => ( null)) })).filter((p) => p.x !== null && p.y !== null && Number.isFinite(p.x) && Number.isFinite(p.y));
|
|
1299
|
-
const all = visible.map((s) => ({ s, pts: pointsOf(s) }));
|
|
1300
|
-
let xlo = Infinity;
|
|
1301
|
-
let xhi = -Infinity;
|
|
1302
|
-
let ylo = Infinity;
|
|
1303
|
-
let yhi = -Infinity;
|
|
1304
|
-
let maxSize = 0;
|
|
1305
|
-
for (const { s, pts } of all) {
|
|
1306
|
-
for (const p of pts) {
|
|
1307
|
-
xlo = Math.min(xlo, p.x);
|
|
1308
|
-
xhi = Math.max(xhi, p.x);
|
|
1309
|
-
ylo = Math.min(ylo, p.y);
|
|
1310
|
-
yhi = Math.max(yhi, p.y);
|
|
1311
|
-
if (p.size !== null && p.size > maxSize) maxSize = p.size;
|
|
1312
|
-
}
|
|
1313
|
-
for (const t of _nullishCoalesce(s.trendlines, () => ( []))) {
|
|
1314
|
-
if (!pts.length) continue;
|
|
1315
|
-
xlo = Math.min(xlo, Math.min(...pts.map((p) => p.x)) - (_nullishCoalesce(t.backward, () => ( 0))));
|
|
1316
|
-
xhi = Math.max(xhi, Math.max(...pts.map((p) => p.x)) + (_nullishCoalesce(t.forward, () => ( 0))));
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
if (!Number.isFinite(xlo)) [xlo, xhi, ylo, yhi] = [0, 1, 0, 1];
|
|
1320
|
-
const titleBand = fs * 1.3 + 6;
|
|
1321
|
-
const yAxis = spec.yAxis;
|
|
1322
|
-
const xAxis = spec.xAxis;
|
|
1323
|
-
const yEst = axisScale(ylo, yhi, area.height, yAxis, ctx.options.valueDomain, true);
|
|
1324
|
-
const yText = (t, step) => formatChartNumber(t, yAxis.format, _nullishCoalesce(yAxis.format.decimals, () => ( stepDecimals(step))));
|
|
1325
|
-
const left = (yAxis.show ? Math.max(0, ...yEst.ticks.map((t) => measure(yText(t, yEst.step), fs))) + 8 : 2) + (yAxis.title ? titleBand : 0);
|
|
1326
|
-
const bottom = (xAxis.show ? fs * 1.6 : 2) + (xAxis.title ? titleBand : 0);
|
|
1327
|
-
const plot = { x: area.x + left, y: area.y + fs / 2 + 2, width: Math.max(1, area.width - left - 12), height: Math.max(1, area.height - bottom - fs / 2 - 2) };
|
|
1328
|
-
const maxR = bubble ? Math.max(4, Math.min(plot.width, plot.height) * 0.08) : 0;
|
|
1329
|
-
const inset = bubble ? maxR : 0;
|
|
1330
|
-
const yScale = axisScale(ylo, yhi, plot.height, yAxis, ctx.options.valueDomain, true);
|
|
1331
|
-
const xScale = axisScale(xlo, xhi, plot.width, xAxis, ctx.options.xDomain, true);
|
|
1332
|
-
const xRange = [plot.x + inset, plot.x + plot.width - inset];
|
|
1333
|
-
const yRange = [plot.y + plot.height - inset, plot.y + inset];
|
|
1334
|
-
const xRaw = (x) => linearMap(x, [xScale.min, xScale.max], xRange);
|
|
1335
|
-
const yRaw = (y) => linearMap(y, [yScale.min, yScale.max], yRange);
|
|
1336
|
-
const xPx = (x) => xRaw(Math.min(xScale.max, Math.max(xScale.min, x)));
|
|
1337
|
-
const yPx = (y) => yRaw(Math.min(yScale.max, Math.max(yScale.min, y)));
|
|
1338
|
-
const xTarget = { kind: "axis", axis: "x" };
|
|
1339
|
-
const yTarget = { kind: "axis", axis: "y" };
|
|
1340
|
-
if (yAxis.gridlines) yScale.ticks.forEach((t, i) => marks.push({ kind: "line", key: `grid-y-${i}`, role: "grid", x1: plot.x, y1: yPx(t), x2: plot.x + plot.width, y2: yPx(t), stroke: theme.grid, strokeWidth: 1 }));
|
|
1341
|
-
if (xAxis.gridlines) xScale.ticks.forEach((t, i) => marks.push({ kind: "line", key: `grid-x-${i}`, role: "grid", x1: xPx(t), y1: plot.y, x2: xPx(t), y2: plot.y + plot.height, stroke: theme.grid, strokeWidth: 1 }));
|
|
1342
|
-
if (yAxis.show) yScale.ticks.forEach((t, i) => marks.push({ kind: "text", key: `tick-y-${i}`, role: "tick-label", x: plot.x - 8, y: yPx(t), text: yText(t, yScale.step), anchor: "end", baseline: "middle", size: fs, fill: theme.mutedText, target: yTarget }));
|
|
1343
|
-
if (xAxis.show) {
|
|
1344
|
-
const y0 = yScale.min <= 0 && yScale.max >= 0 ? yPx(0) : plot.y + plot.height;
|
|
1345
|
-
marks.push({ kind: "line", key: "axis-x", role: "axis", x1: plot.x, y1: y0, x2: plot.x + plot.width, y2: y0, stroke: theme.axis, strokeWidth: 1, target: xTarget });
|
|
1346
|
-
const texts = xScale.ticks.map((t) => formatChartNumber(t, xAxis.format, _nullishCoalesce(xAxis.format.decimals, () => ( stepDecimals(xScale.step)))));
|
|
1347
|
-
const widest = Math.max(0, ...texts.map((t) => measure(t, fs)));
|
|
1348
|
-
const spacing = xScale.ticks.length > 1 ? Math.abs(xPx(xScale.ticks[1]) - xPx(xScale.ticks[0])) : Infinity;
|
|
1349
|
-
const every = Math.max(1, Math.ceil((widest + 8) / spacing));
|
|
1350
|
-
xScale.ticks.forEach((t, i) => {
|
|
1351
|
-
if (i % every) return;
|
|
1352
|
-
marks.push({ kind: "text", key: `tick-x-${i}`, role: "tick-label", x: xPx(t), y: plot.y + plot.height + 6, text: texts[i], anchor: "middle", baseline: "top", size: fs, fill: theme.mutedText, target: xTarget });
|
|
1353
|
-
});
|
|
1354
|
-
}
|
|
1355
|
-
if (xAxis.title) marks.push({ kind: "text", key: "axis-title-x", role: "axis-title", x: plot.x + plot.width / 2, y: area.y + area.height, text: xAxis.title, anchor: "middle", baseline: "bottom", size: fs + 1, weight: 500, fill: theme.text, target: xTarget });
|
|
1356
|
-
if (yAxis.title) marks.push({ kind: "text", key: "axis-title-y", role: "axis-title", x: area.x, y: plot.y + plot.height / 2, text: yAxis.title, anchor: "middle", baseline: "top", size: fs + 1, weight: 500, fill: theme.text, rotate: -90, target: yTarget });
|
|
1357
|
-
for (const axis of ["x", "y"]) {
|
|
1358
|
-
if (!ctx.isSelected({ kind: "axis", axis })) continue;
|
|
1359
|
-
ctx.outline(`axis-${axis}-selection`, axis === "y" ? { x: area.x, y: plot.y - fs / 2, width: left - 2, height: plot.height + fs } : { x: plot.x - 4, y: plot.y + plot.height + 2, width: plot.width + 8, height: area.y + area.height - plot.y - plot.height - 2 });
|
|
1360
|
-
}
|
|
1361
|
-
const labelMarks = [];
|
|
1362
|
-
const trendLabels = [];
|
|
1363
|
-
for (const { s, pts } of all) {
|
|
1364
|
-
const si = spec.series.indexOf(s);
|
|
1365
|
-
const color = _chunkNEOO6QJDcjs.chartSeriesColor.call(void 0, spec, si);
|
|
1366
|
-
const type = _chunkNEOO6QJDcjs.chartSeriesType.call(void 0, spec, s);
|
|
1367
|
-
const selSeries = ctx.seriesSelected(s.id);
|
|
1368
|
-
const seriesTarget = { kind: "series", seriesId: s.id };
|
|
1369
|
-
if (type === "line" || type === "area") {
|
|
1370
|
-
const xy = pts.map((p) => ({ x: xPx(p.x), y: yPx(p.y) }));
|
|
1371
|
-
const thinned = xy.length > MAX_CHART_LINE_POINTS ? decimateChartPoints(xy, MAX_CHART_LINE_POINTS) : xy;
|
|
1372
|
-
if (thinned.length < xy.length) ctx.warn("points-decimated", `"${s.name}" has ${xy.length} points; its line is drawn with ${thinned.length}.`);
|
|
1373
|
-
const d = curvePath(thinned.map((p) => ({ c: p.x, v: p.y })), _nullishCoalesce(s.curve, () => ( spec.curve)), (p) => [p.c, p.v]);
|
|
1374
|
-
if (d) marks.push({ kind: "path", key: `line-${s.id}`, role: "line", d, fill: "none", stroke: color, strokeWidth: selSeries ? 3.5 : 2, target: seriesTarget, selected: selSeries });
|
|
1375
|
-
}
|
|
1376
|
-
const marker = bubble ? "circle" : _nullishCoalesce(s.marker, () => ( (type === "scatter" || spec.points ? "circle" : "none")));
|
|
1377
|
-
const ordered = bubble ? [...pts].sort((a, b) => (_nullishCoalesce(b.size, () => ( 0))) - (_nullishCoalesce(a.size, () => ( 0)))) : pts;
|
|
1378
|
-
const labelsOn = (_nullishCoalesce(s.dataLabels, () => ( spec.dataLabels))) && pts.length <= MAX_CHART_DATA_LABELS;
|
|
1379
|
-
const labelFormat = _nullishCoalesce(s.format, () => ( yAxis.format));
|
|
1380
|
-
const cells = /* @__PURE__ */ new Set();
|
|
1381
|
-
for (const p of ordered) {
|
|
1382
|
-
const cx = xPx(p.x);
|
|
1383
|
-
const cy = yPx(p.y);
|
|
1384
|
-
if (pts.length > MAX_CHART_MARKERS) {
|
|
1385
|
-
const cell = `${Math.round(cx / 2)},${Math.round(cy / 2)}`;
|
|
1386
|
-
if (cells.has(cell)) continue;
|
|
1387
|
-
cells.add(cell);
|
|
1388
|
-
}
|
|
1389
|
-
const target = { kind: "point", seriesId: s.id, index: p.i };
|
|
1390
|
-
const selected = ctx.isSelected(target);
|
|
1391
|
-
const pointColor = _chunkNEOO6QJDcjs.chartPointColor.call(void 0, spec, si, p.i);
|
|
1392
|
-
const r = bubble ? maxSize > 0 && p.size !== null && p.size > 0 ? Math.max(2, Math.sqrt(p.size / maxSize) * maxR) : 2 : 4.5;
|
|
1393
|
-
if (bubble) marks.push({ kind: "circle", key: `point-${s.id}-${p.i}`, role: "point", cx, cy, r, fill: pointColor, stroke: pointColor, strokeWidth: 1, opacity: 0.6, target, selected: selected || selSeries });
|
|
1394
|
-
else marks.push(pointMark({ key: `point-${s.id}-${p.i}`, target, selected: selected || selSeries, opacity: 0.85 }, marker === "none" ? "hit" : marker, cx, cy, marker === "none" ? 6 : r, marker === "none" ? "transparent" : pointColor, marker === "none" ? void 0 : pointColor, 2));
|
|
1395
|
-
if (selected) marks.push({ kind: "circle", key: `point-${s.id}-${p.i}-selection`, role: "selection", cx, cy, r: r + 3.5, fill: "none", stroke: theme.selection, strokeWidth: 1.5 });
|
|
1396
|
-
if (labelsOn) labelMarks.push({ kind: "text", key: `label-${s.id}-${p.i}`, role: "data-label", x: cx, y: cy - r - 4, text: formatChartNumber(p.y, labelFormat), anchor: "middle", baseline: "bottom", size: fs - 1, fill: theme.text, target });
|
|
1397
|
-
}
|
|
1398
|
-
if (_optionalChain([s, 'access', _18 => _18.trendlines, 'optionalAccess', _19 => _19.length])) drawTrendlines(ctx, plot, s, color, pts.map((p) => [p.x, p.y]), (x, y) => [xRaw(x), yRaw(y)], trendLabels);
|
|
1399
|
-
}
|
|
1400
|
-
marks.push(...labelMarks);
|
|
1401
|
-
drawTrendLabels(ctx, plot, trendLabels);
|
|
1402
|
-
return {
|
|
1403
|
-
plot,
|
|
1404
|
-
valueScale: { axis: "y", domain: [yScale.min, yScale.max], range: yRange, ticks: yScale.ticks },
|
|
1405
|
-
xScale: { axis: "x", domain: [xScale.min, xScale.max], range: xRange, ticks: xScale.ticks }
|
|
1406
|
-
};
|
|
1407
|
-
}
|
|
1408
|
-
function trendlineLegendItems(spec, color) {
|
|
1409
|
-
if (!chartSupportsTrendlines(spec.type, spec.stacking)) return [];
|
|
1410
|
-
return spec.series.flatMap(
|
|
1411
|
-
(s, si) => s.hidden ? [] : (_nullishCoalesce(s.trendlines, () => ( []))).map((t, ti) => ({ label: chartTrendlineName(t, s.name), color: _nullishCoalesce(t.color, () => ( color(si))), target: { kind: "trendline", seriesId: s.id, index: ti }, after: s.id }))
|
|
1412
|
-
);
|
|
1413
|
-
}
|
|
1414
92
|
|
|
1415
|
-
// src/chart/layout-families.ts
|
|
1416
|
-
var sliceColor = (ctx, series, i) => _optionalChain([series, 'access', _20 => _20.pointColors, 'optionalAccess', _21 => _21[i]]) || _chunkNEOO6QJDcjs.chartSliceColor.call(void 0, ctx.spec, i);
|
|
1417
|
-
function layoutPie(ctx, area, series) {
|
|
1418
|
-
const { spec, theme, marks, fs, selection } = ctx;
|
|
1419
|
-
const values = spec.categories.map((_, i) => {
|
|
1420
|
-
const v = series.values[i];
|
|
1421
|
-
return v !== null && Number.isFinite(v) ? Math.max(0, v) : 0;
|
|
1422
|
-
});
|
|
1423
|
-
const total = values.reduce((a, b) => a + b, 0);
|
|
1424
|
-
const labelsOn = _nullishCoalesce(series.dataLabels, () => ( spec.dataLabels));
|
|
1425
|
-
const labelRoom = labelsOn ? fs * 2.2 : 4;
|
|
1426
|
-
const outer = Math.max(4, Math.min(area.width, area.height) / 2 - labelRoom);
|
|
1427
|
-
const inner = spec.type === "donut" ? outer * spec.innerRadius : 0;
|
|
1428
|
-
const cx = area.x + area.width / 2;
|
|
1429
|
-
const cy = area.y + area.height / 2;
|
|
1430
|
-
const plot = { x: cx - outer, y: cy - outer, width: outer * 2, height: outer * 2 };
|
|
1431
|
-
if (total <= 0) {
|
|
1432
|
-
marks.push({ kind: "text", key: "empty", role: "empty", x: cx, y: cy, text: "No positive values", anchor: "middle", baseline: "middle", size: fs + 1, fill: theme.mutedText });
|
|
1433
|
-
return { plot };
|
|
1434
|
-
}
|
|
1435
|
-
let angle = 0;
|
|
1436
|
-
const format = _nullishCoalesce(series.format, () => ( spec.yAxis.format));
|
|
1437
|
-
let hidden = 0;
|
|
1438
|
-
values.forEach((v, i) => {
|
|
1439
|
-
if (v <= 0) return;
|
|
1440
|
-
const sweep = v / total * Math.PI * 2;
|
|
1441
|
-
const start = angle;
|
|
1442
|
-
const end = angle + sweep;
|
|
1443
|
-
angle = end;
|
|
1444
|
-
const target = { kind: "point", seriesId: series.id, index: i };
|
|
1445
|
-
const selected = ctx.isSelected(target) || ctx.isSelected({ kind: "category", index: i });
|
|
1446
|
-
const seriesSel = _optionalChain([selection, 'optionalAccess', _22 => _22.kind]) === "series" && selection.seriesId === series.id;
|
|
1447
|
-
const mid = (start + end) / 2;
|
|
1448
|
-
const off = selected ? 6 : 0;
|
|
1449
|
-
const ox = cx + off * Math.sin(mid);
|
|
1450
|
-
const oy = cy - off * Math.cos(mid);
|
|
1451
|
-
marks.push({
|
|
1452
|
-
kind: "path",
|
|
1453
|
-
key: `slice-${i}`,
|
|
1454
|
-
role: "slice",
|
|
1455
|
-
d: arcPath(ox, oy, outer, inner, start, end),
|
|
1456
|
-
fill: sliceColor(ctx, series, i),
|
|
1457
|
-
stroke: selected || seriesSel ? theme.selection : theme.surface,
|
|
1458
|
-
strokeWidth: selected || seriesSel ? 2 : 1,
|
|
1459
|
-
target,
|
|
1460
|
-
selected: selected || seriesSel
|
|
1461
|
-
});
|
|
1462
|
-
if (!labelsOn) return;
|
|
1463
|
-
if (sweep <= 0.12) {
|
|
1464
|
-
hidden++;
|
|
1465
|
-
return;
|
|
1466
|
-
}
|
|
1467
|
-
const share = v / total;
|
|
1468
|
-
const text = format.style || format.code ? formatChartNumber(v, format) : `${Math.round(share * 100)}%`;
|
|
1469
|
-
const lr = outer + fs * 1.1;
|
|
1470
|
-
const lx = ox + lr * Math.sin(mid);
|
|
1471
|
-
const ly = oy - lr * Math.cos(mid);
|
|
1472
|
-
const anchor = Math.abs(Math.sin(mid)) < 0.2 ? "middle" : Math.sin(mid) > 0 ? "start" : "end";
|
|
1473
|
-
marks.push({ kind: "text", key: `label-slice-${i}`, role: "data-label", x: lx, y: ly, text, anchor, baseline: "middle", size: fs, fill: theme.text, target });
|
|
1474
|
-
});
|
|
1475
|
-
if (hidden) ctx.warn("data-labels-hidden", `${hidden} slice label${hidden === 1 ? " is" : "s are"} hidden: the slices are too thin.`);
|
|
1476
|
-
if (spec.type === "donut" && inner > fs * 2) {
|
|
1477
|
-
marks.push({ kind: "text", key: "donut-total", role: "data-label", x: cx, y: cy, text: formatChartNumber(total, format), anchor: "middle", baseline: "middle", size: Math.min(theme.titleSize + 4, inner / 2), weight: 600, fill: theme.text, target: { kind: "series", seriesId: series.id } });
|
|
1478
|
-
}
|
|
1479
|
-
return { plot };
|
|
1480
|
-
}
|
|
1481
|
-
function layoutRadar(ctx, area, visible) {
|
|
1482
|
-
const { spec, theme, marks, fs, measure } = ctx;
|
|
1483
|
-
const n = spec.categories.length;
|
|
1484
|
-
const labelW = Math.min(area.width * 0.25, Math.max(0, ...spec.categories.map((c) => measure(c, fs))));
|
|
1485
|
-
const radius = Math.max(8, Math.min(area.width / 2 - labelW - 8, area.height / 2 - fs * 1.6));
|
|
1486
|
-
const cx = area.x + area.width / 2;
|
|
1487
|
-
const cy = area.y + area.height / 2;
|
|
1488
|
-
let lo = 0;
|
|
1489
|
-
let hi = -Infinity;
|
|
1490
|
-
for (const s of visible) for (const v of s.values) if (v !== null && Number.isFinite(v)) lo = Math.min(lo, v), hi = Math.max(hi, v);
|
|
1491
|
-
if (!Number.isFinite(hi)) hi = 1;
|
|
1492
|
-
const scale = axisScale(lo, hi, radius, spec.yAxis, ctx.options.valueDomain);
|
|
1493
|
-
const rOf = (v) => linearMap(Math.min(scale.max, Math.max(scale.min, v)), [scale.min, scale.max], [0, radius]);
|
|
1494
|
-
const angle = (i) => i / n * Math.PI * 2;
|
|
1495
|
-
const at = (i, r) => [cx + r * Math.sin(angle(i)), cy - r * Math.cos(angle(i))];
|
|
1496
|
-
const yTarget = { kind: "axis", axis: "y" };
|
|
1497
|
-
if (spec.yAxis.gridlines)
|
|
1498
|
-
scale.ticks.forEach((t, k) => {
|
|
1499
|
-
const r = rOf(t);
|
|
1500
|
-
if (r <= 0) return;
|
|
1501
|
-
const d = Array.from({ length: n }, (_, i) => at(i, r)).map(([x, y], i) => `${i ? "L" : "M"}${r2(x)},${r2(y)}`).join("") + "Z";
|
|
1502
|
-
marks.push({ kind: "path", key: `grid-ring-${k}`, role: "grid", d, fill: "none", stroke: theme.grid, strokeWidth: 1 });
|
|
1503
|
-
});
|
|
1504
|
-
for (let i = 0; i < n; i++) {
|
|
1505
|
-
const [x, y] = at(i, radius);
|
|
1506
|
-
marks.push({ kind: "line", key: `spoke-${i}`, role: "axis", x1: cx, y1: cy, x2: x, y2: y, stroke: theme.grid, strokeWidth: 1, target: { kind: "axis", axis: "x" } });
|
|
1507
|
-
if (spec.xAxis.show) {
|
|
1508
|
-
const [lx, ly] = at(i, radius + 8);
|
|
1509
|
-
const s = Math.sin(angle(i));
|
|
1510
|
-
const c = Math.cos(angle(i));
|
|
1511
|
-
const target = { kind: "category", index: i };
|
|
1512
|
-
const selected = ctx.isSelected(target);
|
|
1513
|
-
marks.push({ kind: "text", key: `tick-x-${i}`, role: "tick-label", x: lx, y: ly, text: truncate(spec.categories[i], labelW, fs, measure), anchor: Math.abs(s) < 0.2 ? "middle" : s > 0 ? "start" : "end", baseline: Math.abs(c) < 0.2 ? "middle" : c > 0 ? "bottom" : "top", size: fs, fill: selected ? theme.selection : theme.mutedText, target, selected });
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
if (spec.yAxis.show)
|
|
1517
|
-
scale.ticks.forEach((t, k) => {
|
|
1518
|
-
const r = rOf(t);
|
|
1519
|
-
marks.push({ kind: "text", key: `tick-y-${k}`, role: "tick-label", x: cx + 4, y: cy - r, text: formatChartNumber(t, spec.yAxis.format, _nullishCoalesce(spec.yAxis.format.decimals, () => ( stepDecimals(scale.step)))), anchor: "start", baseline: "bottom", size: fs - 1, fill: theme.mutedText, target: yTarget });
|
|
1520
|
-
});
|
|
1521
|
-
for (const s of visible) {
|
|
1522
|
-
const si = spec.series.indexOf(s);
|
|
1523
|
-
const color = _chunkNEOO6QJDcjs.chartSeriesColor.call(void 0, spec, si);
|
|
1524
|
-
const selSeries = ctx.seriesSelected(s.id);
|
|
1525
|
-
const pts = s.values.map((v, i) => v !== null && Number.isFinite(v) ? at(i, rOf(v)) : null);
|
|
1526
|
-
const present = pts.filter((p) => p !== null);
|
|
1527
|
-
if (!present.length) continue;
|
|
1528
|
-
const d = present.map(([x, y], i) => `${i ? "L" : "M"}${r2(x)},${r2(y)}`).join("") + "Z";
|
|
1529
|
-
marks.push({ kind: "path", key: `area-${s.id}`, role: "area", d, fill: color, opacity: s.type === "line" ? 0 : 0.15, target: { kind: "series", seriesId: s.id }, selected: selSeries });
|
|
1530
|
-
marks.push({ kind: "path", key: `line-${s.id}`, role: "line", d, fill: "none", stroke: color, strokeWidth: selSeries ? 3.5 : 2, target: { kind: "series", seriesId: s.id }, selected: selSeries });
|
|
1531
|
-
pts.forEach((p, i) => {
|
|
1532
|
-
if (!p) return;
|
|
1533
|
-
const target = { kind: "point", seriesId: s.id, index: i };
|
|
1534
|
-
const selected = ctx.isSelected(target);
|
|
1535
|
-
marks.push({ kind: "circle", key: `point-${s.id}-${i}`, role: "point", cx: p[0], cy: p[1], r: spec.points ? 3.5 : 6, fill: spec.points ? theme.surface : "transparent", ...spec.points ? { stroke: color, strokeWidth: 2 } : {}, target, selected: selected || selSeries });
|
|
1536
|
-
if (selected) marks.push({ kind: "circle", key: `point-${s.id}-${i}-selection`, role: "selection", cx: p[0], cy: p[1], r: 7, fill: "none", stroke: theme.selection, strokeWidth: 1.5 });
|
|
1537
|
-
if (_nullishCoalesce(s.dataLabels, () => ( spec.dataLabels))) marks.push({ kind: "text", key: `label-${s.id}-${i}`, role: "data-label", x: p[0], y: p[1] - 6, text: formatChartNumber(s.values[i], _nullishCoalesce(s.format, () => ( spec.yAxis.format))), anchor: "middle", baseline: "bottom", size: fs - 1, fill: theme.text, target });
|
|
1538
|
-
});
|
|
1539
|
-
}
|
|
1540
|
-
return { plot: { x: cx - radius, y: cy - radius, width: radius * 2, height: radius * 2 } };
|
|
1541
|
-
}
|
|
1542
|
-
function layoutWaterfall(ctx, area, series) {
|
|
1543
|
-
const { spec, theme, marks, fs } = ctx;
|
|
1544
|
-
const isTotal = (label) => /\b(sub)?total\b/i.test(label);
|
|
1545
|
-
const steps = [];
|
|
1546
|
-
let running = 0;
|
|
1547
|
-
spec.categories.forEach((label, i) => {
|
|
1548
|
-
const v = series.values[i];
|
|
1549
|
-
if (isTotal(label)) {
|
|
1550
|
-
steps.push({ label, index: i, from: 0, to: running, total: true });
|
|
1551
|
-
return;
|
|
1552
|
-
}
|
|
1553
|
-
const delta = v !== null && Number.isFinite(v) ? v : 0;
|
|
1554
|
-
steps.push({ label, index: i, from: running, to: running + delta, total: false });
|
|
1555
|
-
running += delta;
|
|
1556
|
-
});
|
|
1557
|
-
if (spec.showTotal && !(steps.length && steps[steps.length - 1].total)) steps.push({ label: "Total", index: null, from: 0, to: running, total: true });
|
|
1558
|
-
let lo = 0;
|
|
1559
|
-
let hi = 0;
|
|
1560
|
-
for (const s of steps) {
|
|
1561
|
-
lo = Math.min(lo, s.from, s.to);
|
|
1562
|
-
hi = Math.max(hi, s.from, s.to);
|
|
1563
|
-
}
|
|
1564
|
-
const horizontal = spec.orientation === "horizontal";
|
|
1565
|
-
const frame = categoryFrame(ctx, area, {
|
|
1566
|
-
labels: steps.map((s) => s.label),
|
|
1567
|
-
horizontal,
|
|
1568
|
-
primary: { lo, hi, axis: spec.yAxis, format: spec.yAxis.format, frozen: ctx.options.valueDomain, key: "y" },
|
|
1569
|
-
labelTarget: (k) => steps[k].index !== null ? { kind: "category", index: steps[k].index } : void 0
|
|
1570
|
-
});
|
|
1571
|
-
const { band, catCenter, map, value } = frame;
|
|
1572
|
-
const w = band * (1 - spec.gapWidth);
|
|
1573
|
-
const color = _chunkNEOO6QJDcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(series));
|
|
1574
|
-
const format = _nullishCoalesce(series.format, () => ( spec.yAxis.format));
|
|
1575
|
-
const labelsOn = _nullishCoalesce(series.dataLabels, () => ( spec.dataLabels));
|
|
1576
|
-
let prevEnd = null;
|
|
1577
|
-
steps.forEach((s, k) => {
|
|
1578
|
-
const a = value.px(s.from);
|
|
1579
|
-
const b = value.px(s.to);
|
|
1580
|
-
const c0 = catCenter(k) - w / 2;
|
|
1581
|
-
const [x, y] = map({ c: c0, v: Math.min(a, b) });
|
|
1582
|
-
const rect = horizontal ? { x: Math.min(a, b), y: c0, width: Math.max(1, Math.abs(b - a)), height: w } : { x, y, width: w, height: Math.max(1, Math.abs(b - a)) };
|
|
1583
|
-
const fill = s.total ? color : s.to >= s.from ? theme.positive : theme.negative;
|
|
1584
|
-
const target = s.index !== null ? { kind: "point", seriesId: series.id, index: s.index } : { kind: "series", seriesId: series.id };
|
|
1585
|
-
const selected = ctx.isSelected(target);
|
|
1586
|
-
marks.push({ kind: "rect", key: `bar-${series.id}-${_nullishCoalesce(s.index, () => ( "total"))}`, role: "bar", ...rect, fill, radius: Math.min(2, w / 4), target, selected });
|
|
1587
|
-
if (selected) ctx.outline(`bar-${series.id}-${_nullishCoalesce(s.index, () => ( "total"))}-selection`, { x: rect.x - 2, y: rect.y - 2, width: rect.width + 4, height: rect.height + 4 });
|
|
1588
|
-
if (prevEnd !== null) {
|
|
1589
|
-
const [x1, y1] = map({ c: catCenter(k - 1) + w / 2, v: value.px(prevEnd) });
|
|
1590
|
-
const [x2, y2] = map({ c: c0, v: value.px(prevEnd) });
|
|
1591
|
-
marks.push({ kind: "line", key: `connector-${k}`, role: "connector", x1, y1, x2, y2, stroke: theme.axis, strokeWidth: 1, dash: "3 3" });
|
|
1592
|
-
}
|
|
1593
|
-
prevEnd = s.to;
|
|
1594
|
-
if (labelsOn) {
|
|
1595
|
-
const text = formatChartNumber(s.total ? s.to : s.to - s.from, format);
|
|
1596
|
-
const up = s.to >= s.from;
|
|
1597
|
-
const [lx, ly] = map({ c: catCenter(k), v: horizontal ? Math.max(a, b) + 4 : up ? Math.min(a, b) - 4 : Math.max(a, b) + 4 });
|
|
1598
|
-
marks.push({ kind: "text", key: `label-${series.id}-${_nullishCoalesce(s.index, () => ( "total"))}`, role: "data-label", x: lx, y: ly, text, anchor: horizontal ? "start" : "middle", baseline: horizontal ? "middle" : up ? "bottom" : "top", size: fs - 1, fill: theme.text, target });
|
|
1599
|
-
}
|
|
1600
|
-
});
|
|
1601
|
-
return { plot: frame.plot, valueScale: toValueScale(value, horizontal ? "x" : "y") };
|
|
1602
|
-
}
|
|
1603
|
-
function layoutHistogram(ctx, area, visible) {
|
|
1604
|
-
const { spec, marks, fs, theme } = ctx;
|
|
1605
|
-
const pooled = visible.flatMap((s) => s.values);
|
|
1606
|
-
const bins = chartHistogram(pooled, spec.bins);
|
|
1607
|
-
if (!bins.length) return { plot: area };
|
|
1608
|
-
const edgeFormat = spec.xAxis.format;
|
|
1609
|
-
const edges = (b, k2) => `${k2 === 0 ? "[" : "("}${formatChartNumber(b.from, edgeFormat)}, ${formatChartNumber(b.to, edgeFormat)}]`;
|
|
1610
|
-
const counts = visible.map((s) => {
|
|
1611
|
-
const c = bins.map(() => 0);
|
|
1612
|
-
for (const v of s.values) {
|
|
1613
|
-
if (v === null || !Number.isFinite(v)) continue;
|
|
1614
|
-
let k2 = bins.findIndex((b) => v <= b.to);
|
|
1615
|
-
if (k2 < 0) k2 = bins.length - 1;
|
|
1616
|
-
c[k2]++;
|
|
1617
|
-
}
|
|
1618
|
-
return c;
|
|
1619
|
-
});
|
|
1620
|
-
const maxCount = Math.max(1, ...counts.flat());
|
|
1621
|
-
const frame = categoryFrame(ctx, area, {
|
|
1622
|
-
labels: bins.map(edges),
|
|
1623
|
-
horizontal: false,
|
|
1624
|
-
primary: { lo: 0, hi: maxCount, axis: { ...spec.yAxis, format: spec.yAxis.format.code || spec.yAxis.format.style ? spec.yAxis.format : { decimals: 0 } }, format: spec.yAxis.format.code || spec.yAxis.format.style ? spec.yAxis.format : { decimals: 0 }, frozen: null, key: "y" },
|
|
1625
|
-
labelTarget: () => void 0
|
|
1626
|
-
});
|
|
1627
|
-
const { band, catCenter, value, plot } = frame;
|
|
1628
|
-
const k = visible.length;
|
|
1629
|
-
const gap = k > 1 ? band * spec.gapWidth * 0.5 : 0;
|
|
1630
|
-
const w = (band - gap) / k;
|
|
1631
|
-
visible.forEach((s, si) => {
|
|
1632
|
-
const color = _chunkNEOO6QJDcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s));
|
|
1633
|
-
const target = { kind: "series", seriesId: s.id };
|
|
1634
|
-
const selected = ctx.seriesSelected(s.id);
|
|
1635
|
-
counts[si].forEach((c, b) => {
|
|
1636
|
-
if (!c) return;
|
|
1637
|
-
const top = value.px(c);
|
|
1638
|
-
const x = catCenter(b) - band / 2 + gap / 2 + si * w;
|
|
1639
|
-
marks.push({ kind: "rect", key: `bar-${s.id}-${b}`, role: "bar", x, y: top, width: w, height: value.zero - top, fill: color, stroke: theme.surface, strokeWidth: 0.75, opacity: k > 1 ? 0.85 : void 0, target, selected });
|
|
1640
|
-
if (_nullishCoalesce(s.dataLabels, () => ( spec.dataLabels))) marks.push({ kind: "text", key: `label-${s.id}-${b}`, role: "data-label", x: x + w / 2, y: top - 3, text: String(c), anchor: "middle", baseline: "bottom", size: fs - 1, fill: theme.text, target });
|
|
1641
|
-
});
|
|
1642
|
-
});
|
|
1643
|
-
return { plot };
|
|
1644
|
-
}
|
|
1645
|
-
function layoutBox(ctx, area, visible) {
|
|
1646
|
-
const { spec, marks, theme, fs } = ctx;
|
|
1647
|
-
const stats = visible.map((s) => chartBoxStats(s.values));
|
|
1648
|
-
let lo = Infinity;
|
|
1649
|
-
let hi = -Infinity;
|
|
1650
|
-
for (const st of stats) if (st) lo = Math.min(lo, st.min), hi = Math.max(hi, st.max);
|
|
1651
|
-
if (!Number.isFinite(lo)) [lo, hi] = [0, 1];
|
|
1652
|
-
const frame = categoryFrame(ctx, area, {
|
|
1653
|
-
labels: visible.map((s) => s.name),
|
|
1654
|
-
horizontal: false,
|
|
1655
|
-
primary: { lo, hi, axis: spec.yAxis, format: spec.yAxis.format, frozen: ctx.options.valueDomain, headroom: true, key: "y" },
|
|
1656
|
-
labelTarget: (i) => ({ kind: "series", seriesId: visible[i].id })
|
|
1657
|
-
});
|
|
1658
|
-
const { band, catCenter, value, plot } = frame;
|
|
1659
|
-
const bw = Math.min(80, band * (1 - spec.gapWidth) * 0.6);
|
|
1660
|
-
visible.forEach((s, i) => {
|
|
1661
|
-
const st = stats[i];
|
|
1662
|
-
if (!st) return;
|
|
1663
|
-
const color = _chunkNEOO6QJDcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s));
|
|
1664
|
-
const target = { kind: "series", seriesId: s.id };
|
|
1665
|
-
const selected = ctx.seriesSelected(s.id);
|
|
1666
|
-
const c = catCenter(i);
|
|
1667
|
-
const y = value.px;
|
|
1668
|
-
const line = (key, x1, y1, x2, y2, width = 1.25, stroke = color) => marks.push({ kind: "line", key, role: "whisker", x1, y1, x2, y2, stroke, strokeWidth: width, target });
|
|
1669
|
-
line(`whisker-hi-${s.id}`, c, y(st.upperWhisker), c, y(st.q3));
|
|
1670
|
-
line(`whisker-lo-${s.id}`, c, y(st.q1), c, y(st.lowerWhisker));
|
|
1671
|
-
line(`cap-hi-${s.id}`, c - bw / 4, y(st.upperWhisker), c + bw / 4, y(st.upperWhisker));
|
|
1672
|
-
line(`cap-lo-${s.id}`, c - bw / 4, y(st.lowerWhisker), c + bw / 4, y(st.lowerWhisker));
|
|
1673
|
-
const top = y(st.q3);
|
|
1674
|
-
const bottom = y(st.q1);
|
|
1675
|
-
marks.push({ kind: "rect", key: `box-${s.id}`, role: "box", x: c - bw / 2, y: top, width: bw, height: Math.max(0.5, bottom - top), fill: color, opacity: 0.3, stroke: selected ? theme.selection : color, strokeWidth: selected ? 2.5 : 1.5, target, selected });
|
|
1676
|
-
line(`median-${s.id}`, c - bw / 2, y(st.median), c + bw / 2, y(st.median), 2.5);
|
|
1677
|
-
const my = y(st.mean);
|
|
1678
|
-
marks.push({ kind: "path", key: `mean-${s.id}`, role: "whisker", d: `M${r2(c - 3)},${r2(my - 3)}L${r2(c + 3)},${r2(my + 3)}M${r2(c - 3)},${r2(my + 3)}L${r2(c + 3)},${r2(my - 3)}`, stroke: color, strokeWidth: 1.25, fill: "none", target });
|
|
1679
|
-
st.outliers.slice(0, 500).forEach((o, k) => marks.push({ kind: "circle", key: `outlier-${s.id}-${k}`, role: "outlier", cx: c, cy: y(o), r: 2.5, fill: "none", stroke: color, strokeWidth: 1, target }));
|
|
1680
|
-
if (_nullishCoalesce(s.dataLabels, () => ( spec.dataLabels))) marks.push({ kind: "text", key: `label-${s.id}-median`, role: "data-label", x: c + bw / 2 + 4, y: y(st.median), text: formatChartNumber(st.median, _nullishCoalesce(s.format, () => ( spec.yAxis.format))), anchor: "start", baseline: "middle", size: fs - 1, fill: theme.text, target });
|
|
1681
|
-
});
|
|
1682
|
-
return { plot, valueScale: toValueScale(value, "y") };
|
|
1683
|
-
}
|
|
1684
|
-
function layoutFunnel(ctx, area, series) {
|
|
1685
|
-
const { spec, marks, theme, fs, measure } = ctx;
|
|
1686
|
-
const n = spec.categories.length;
|
|
1687
|
-
const labelW = spec.xAxis.show ? Math.min(area.width * 0.3, Math.max(0, ...spec.categories.map((c) => measure(c, fs)))) + 10 : 0;
|
|
1688
|
-
const plot = { x: area.x + labelW, y: area.y, width: Math.max(1, area.width - labelW), height: area.height };
|
|
1689
|
-
const max = Math.max(0, ...series.values.map((v) => v !== null && Number.isFinite(v) ? Math.abs(v) : 0));
|
|
1690
|
-
const band = plot.height / Math.max(1, n);
|
|
1691
|
-
const h = band * (1 - Math.min(0.5, spec.gapWidth * 0.5));
|
|
1692
|
-
const format = _nullishCoalesce(series.format, () => ( spec.yAxis.format));
|
|
1693
|
-
const labelsOn = _nullishCoalesce(series.dataLabels, () => ( true));
|
|
1694
|
-
const cx = plot.x + plot.width / 2;
|
|
1695
|
-
spec.categories.forEach((label, i) => {
|
|
1696
|
-
const v = series.values[i];
|
|
1697
|
-
const cy = plot.y + band * (i + 0.5);
|
|
1698
|
-
const target = { kind: "point", seriesId: series.id, index: i };
|
|
1699
|
-
const selected = ctx.isSelected(target);
|
|
1700
|
-
if (spec.xAxis.show) {
|
|
1701
|
-
const catTarget = { kind: "category", index: i };
|
|
1702
|
-
marks.push({ kind: "text", key: `tick-x-${i}`, role: "tick-label", x: plot.x - 8, y: cy, text: truncate(label, labelW - 10, fs, measure), anchor: "end", baseline: "middle", size: fs, fill: ctx.isSelected(catTarget) ? theme.selection : theme.mutedText, target: catTarget });
|
|
1703
|
-
}
|
|
1704
|
-
if (v === null || !Number.isFinite(v) || max <= 0) return;
|
|
1705
|
-
const w = Math.max(1, Math.abs(v) / max * plot.width);
|
|
1706
|
-
const fill = sliceColor(ctx, series, i);
|
|
1707
|
-
marks.push({ kind: "rect", key: `bar-${series.id}-${i}`, role: "bar", x: cx - w / 2, y: cy - h / 2, width: w, height: h, fill, radius: 2, target, selected });
|
|
1708
|
-
if (selected) ctx.outline(`bar-${series.id}-${i}-selection`, { x: cx - w / 2 - 2, y: cy - h / 2 - 2, width: w + 4, height: h + 4 });
|
|
1709
|
-
if (labelsOn) {
|
|
1710
|
-
const text = formatChartNumber(v, format);
|
|
1711
|
-
const fits = measure(text, fs) + 8 <= w && fs + 2 <= h;
|
|
1712
|
-
marks.push({ kind: "text", key: `label-${series.id}-${i}`, role: "data-label", x: fits ? cx : cx + w / 2 + 4, y: cy, text, anchor: fits ? "middle" : "start", baseline: "middle", size: fs, fill: fits ? contrastingTextColor(fill) : theme.text, target });
|
|
1713
|
-
}
|
|
1714
|
-
});
|
|
1715
|
-
return { plot };
|
|
1716
|
-
}
|
|
1717
|
-
function layoutTreemap(ctx, area, series) {
|
|
1718
|
-
const { spec, marks, theme, fs, measure } = ctx;
|
|
1719
|
-
const values = spec.categories.map((_, i) => {
|
|
1720
|
-
const v = series.values[i];
|
|
1721
|
-
return v !== null && Number.isFinite(v) ? v : 0;
|
|
1722
|
-
});
|
|
1723
|
-
const rects = chartSquarify(values, area);
|
|
1724
|
-
const format = _nullishCoalesce(series.format, () => ( spec.yAxis.format));
|
|
1725
|
-
const labelsOn = _nullishCoalesce(series.dataLabels, () => ( true));
|
|
1726
|
-
let unlabelled = 0;
|
|
1727
|
-
rects.forEach((r, i) => {
|
|
1728
|
-
if (r.width <= 0 || r.height <= 0) return;
|
|
1729
|
-
const target = { kind: "point", seriesId: series.id, index: i };
|
|
1730
|
-
const selected = ctx.isSelected(target) || ctx.isSelected({ kind: "category", index: i });
|
|
1731
|
-
const fill = sliceColor(ctx, series, i);
|
|
1732
|
-
marks.push({ kind: "rect", key: `cell-${i}`, role: "cell", ...r, fill, stroke: selected ? theme.selection : theme.surface, strokeWidth: selected ? 2.5 : 1.5, target, selected });
|
|
1733
|
-
if (!labelsOn) return;
|
|
1734
|
-
const room = r.width - 8;
|
|
1735
|
-
if (room < fs * 2 || r.height < fs * 1.5) {
|
|
1736
|
-
unlabelled++;
|
|
1737
|
-
return;
|
|
1738
|
-
}
|
|
1739
|
-
const color = contrastingTextColor(fill);
|
|
1740
|
-
marks.push({ kind: "text", key: `label-cell-${i}`, role: "data-label", x: r.x + 4, y: r.y + 4, text: truncate(spec.categories[i], room, fs, measure), anchor: "start", baseline: "top", size: fs, weight: 600, fill: color, target });
|
|
1741
|
-
if (r.height >= fs * 3) marks.push({ kind: "text", key: `label-value-${i}`, role: "data-label", x: r.x + 4, y: r.y + 6 + fs * 1.2, text: truncate(formatChartNumber(values[i], format), room, fs - 1, measure), anchor: "start", baseline: "top", size: fs - 1, fill: color, target });
|
|
1742
|
-
});
|
|
1743
|
-
if (unlabelled) ctx.warn("data-labels-hidden", `${unlabelled} treemap cell${unlabelled === 1 ? " is" : "s are"} too small for a label.`);
|
|
1744
|
-
return { plot: area };
|
|
1745
|
-
}
|
|
1746
93
|
|
|
1747
|
-
// src/chart/layout.ts
|
|
1748
|
-
function legendItems(spec, ctx) {
|
|
1749
|
-
const family = _chunkNEOO6QJDcjs.chartFamily.call(void 0, spec);
|
|
1750
|
-
if (spec.type === "funnel") return [];
|
|
1751
|
-
if (_chunkNEOO6QJDcjs.chartColorsByCategory.call(void 0, spec.type)) {
|
|
1752
|
-
const first = spec.series.find((s) => !s.hidden);
|
|
1753
|
-
return spec.categories.map((c, i) => ({
|
|
1754
|
-
label: c,
|
|
1755
|
-
color: _optionalChain([first, 'optionalAccess', _23 => _23.pointColors, 'optionalAccess', _24 => _24[i]]) || _chunkNEOO6QJDcjs.chartSliceColor.call(void 0, spec, i),
|
|
1756
|
-
target: { kind: "category", index: i },
|
|
1757
|
-
faded: false,
|
|
1758
|
-
selected: ctx.isSelected({ kind: "category", index: i }),
|
|
1759
|
-
shape: "square"
|
|
1760
|
-
}));
|
|
1761
|
-
}
|
|
1762
|
-
if (family === "waterfall") {
|
|
1763
|
-
const first = spec.series.find((s) => !s.hidden);
|
|
1764
|
-
if (!first) return [];
|
|
1765
|
-
const target = { kind: "series", seriesId: first.id };
|
|
1766
|
-
return [
|
|
1767
|
-
{ label: "Increase", color: ctx.theme.positive, target, faded: false, selected: false, shape: "square" },
|
|
1768
|
-
{ label: "Decrease", color: ctx.theme.negative, target, faded: false, selected: false, shape: "square" },
|
|
1769
|
-
{ label: "Total", color: _chunkNEOO6QJDcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(first)), target, faded: false, selected: false, shape: "square" }
|
|
1770
|
-
];
|
|
1771
|
-
}
|
|
1772
|
-
if (family === "box") return [];
|
|
1773
|
-
const trends = trendlineLegendItems(spec, (i) => _chunkNEOO6QJDcjs.chartSeriesColor.call(void 0, spec, i));
|
|
1774
|
-
const items = [];
|
|
1775
|
-
spec.series.forEach((s, i) => {
|
|
1776
|
-
const type = _chunkNEOO6QJDcjs.chartSeriesType.call(void 0, spec, s);
|
|
1777
|
-
items.push({
|
|
1778
|
-
label: s.name,
|
|
1779
|
-
color: _chunkNEOO6QJDcjs.chartSeriesColor.call(void 0, spec, i),
|
|
1780
|
-
target: { kind: "series", seriesId: s.id },
|
|
1781
|
-
faded: !!s.hidden,
|
|
1782
|
-
selected: ctx.seriesSelected(s.id),
|
|
1783
|
-
shape: family === "cartesian" || family === "xy" || family === "radar" ? type === "line" ? "line" : "square" : "square"
|
|
1784
|
-
});
|
|
1785
|
-
for (const t of trends.filter((t2) => t2.after === s.id)) items.push({ label: t.label, color: t.color, target: t.target, faded: false, selected: ctx.isSelected(t.target), shape: "dash" });
|
|
1786
|
-
});
|
|
1787
|
-
return items;
|
|
1788
|
-
}
|
|
1789
|
-
function layoutChart(spec, options) {
|
|
1790
|
-
const width = Math.max(1, Number.isFinite(options.width) ? options.width : 1);
|
|
1791
|
-
const height = Math.max(1, Number.isFinite(options.height) ? options.height : 1);
|
|
1792
|
-
const theme = { ..._chunkNEOO6QJDcjs.DEFAULT_CHART_THEME, ...options.theme };
|
|
1793
|
-
const selection = _nullishCoalesce(options.selection, () => ( null));
|
|
1794
|
-
const marks = [];
|
|
1795
|
-
const warnings = [];
|
|
1796
|
-
const ctx = {
|
|
1797
|
-
spec,
|
|
1798
|
-
options,
|
|
1799
|
-
theme,
|
|
1800
|
-
measure: _nullishCoalesce(options.measureText, () => ( estimateChartTextWidth)),
|
|
1801
|
-
fs: theme.fontSize,
|
|
1802
|
-
width,
|
|
1803
|
-
height,
|
|
1804
|
-
marks,
|
|
1805
|
-
warnings,
|
|
1806
|
-
selection,
|
|
1807
|
-
isSelected: (target) => sameChartSelection(selection, target),
|
|
1808
|
-
seriesSelected: (id) => _optionalChain([selection, 'optionalAccess', _25 => _25.kind]) === "series" && selection.seriesId === id,
|
|
1809
|
-
outline: (key, box) => marks.push({ kind: "rect", key, role: "selection", ...box, fill: "none", stroke: theme.selection, strokeWidth: 1, radius: 3 }),
|
|
1810
|
-
warn: (code, message) => {
|
|
1811
|
-
if (!warnings.some((w) => w.code === code && w.message === message)) warnings.push({ code, message });
|
|
1812
|
-
}
|
|
1813
|
-
};
|
|
1814
|
-
const pad = 12;
|
|
1815
|
-
const fs = theme.fontSize;
|
|
1816
|
-
marks.push({ kind: "rect", key: "background", role: "background", x: 0, y: 0, width, height, fill: theme.background, target: { kind: "chart" } });
|
|
1817
|
-
let top = pad;
|
|
1818
|
-
const titleMark = (kind, label, size, weight, fill) => {
|
|
1819
|
-
const target = { kind };
|
|
1820
|
-
const selected = ctx.isSelected(target);
|
|
1821
|
-
marks.push({ kind: "text", key: kind, role: kind, x: width / 2, y: top, text: label, anchor: "middle", baseline: "top", size, weight, fill, target, selected });
|
|
1822
|
-
if (selected) {
|
|
1823
|
-
const w = ctx.measure(label, size, weight);
|
|
1824
|
-
ctx.outline(`${kind}-selection`, { x: width / 2 - w / 2 - 4, y: top - 3, width: w + 8, height: size * 1.25 + 6 });
|
|
1825
|
-
}
|
|
1826
|
-
top += size * 1.35;
|
|
1827
|
-
};
|
|
1828
|
-
if (spec.title) titleMark("title", spec.title, theme.titleSize, 600, theme.text);
|
|
1829
|
-
if (spec.subtitle) titleMark("subtitle", spec.subtitle, fs + 1, 400, theme.mutedText);
|
|
1830
|
-
if (spec.title || spec.subtitle) top += 6;
|
|
1831
|
-
const family = _chunkNEOO6QJDcjs.chartFamily.call(void 0, spec);
|
|
1832
|
-
const visible = spec.series.filter((s) => !s.hidden);
|
|
1833
|
-
const items = legendItems(spec, ctx);
|
|
1834
|
-
let area = { x: pad, y: top, width: width - pad * 2, height: height - top - pad };
|
|
1835
|
-
const seriesLegend = family === "cartesian" || family === "xy" || family === "radar" || family === "histogram";
|
|
1836
|
-
const hasTrend = items.some((i) => i.shape === "dash");
|
|
1837
|
-
const showLegend = spec.legend.show && items.length > 0 && (!seriesLegend || spec.series.length > 1 || spec.series.some((s) => s.hidden) || hasTrend);
|
|
1838
|
-
if (showLegend) area = layoutLegend(items, spec.legend.position, area, ctx, ctx.isSelected({ kind: "legend" }));
|
|
1839
|
-
const finish = (result) => {
|
|
1840
|
-
if (options.readout) addReadout(ctx, options.readout, result.plot);
|
|
1841
|
-
const kept = finalizeMarks(marks, width, height);
|
|
1842
|
-
if (kept.length < marks.length) ctx.warn("values-skipped", `${marks.length - kept.length} mark${marks.length - kept.length === 1 ? " was" : "s were"} left out: their values are not finite numbers.`);
|
|
1843
|
-
return {
|
|
1844
|
-
width,
|
|
1845
|
-
height,
|
|
1846
|
-
plot: result.plot,
|
|
1847
|
-
marks: kept,
|
|
1848
|
-
percent: _nullishCoalesce(result.percent, () => ( false)),
|
|
1849
|
-
theme,
|
|
1850
|
-
warnings,
|
|
1851
|
-
...result.valueScale ? { valueScale: result.valueScale } : {},
|
|
1852
|
-
...result.valueScale2 ? { valueScale2: result.valueScale2 } : {},
|
|
1853
|
-
...result.xScale ? { xScale: result.xScale } : {}
|
|
1854
|
-
};
|
|
1855
|
-
};
|
|
1856
|
-
const empty = (message) => {
|
|
1857
|
-
marks.push({ kind: "text", key: "empty", role: "empty", x: area.x + area.width / 2, y: area.y + area.height / 2, text: message, anchor: "middle", baseline: "middle", size: fs + 1, fill: theme.mutedText });
|
|
1858
|
-
return finish({ plot: { x: area.x, y: area.y, width: 0, height: 0 } });
|
|
1859
|
-
};
|
|
1860
|
-
const hasData = visible.some((s) => s.values.some((v) => v !== null && Number.isFinite(v)));
|
|
1861
|
-
const needsCategories = family !== "histogram" && family !== "box" && family !== "xy";
|
|
1862
|
-
if (needsCategories && !spec.categories.length || !visible.length || !hasData) return empty(_nullishCoalesce(options.emptyMessage, () => ( "No data")));
|
|
1863
|
-
switch (family) {
|
|
1864
|
-
case "pie":
|
|
1865
|
-
return finish(layoutPie(ctx, area, visible[0]));
|
|
1866
|
-
case "radar":
|
|
1867
|
-
if (spec.categories.length < 3) {
|
|
1868
|
-
ctx.warn("type-fallback", "A radar chart needs at least 3 categories; it is drawn as a line chart until then.");
|
|
1869
|
-
return finish(layoutCartesian(ctx, area, visible));
|
|
1870
|
-
}
|
|
1871
|
-
return finish(layoutRadar(ctx, area, visible));
|
|
1872
|
-
case "waterfall":
|
|
1873
|
-
return finish(layoutWaterfall(ctx, area, visible[0]));
|
|
1874
|
-
case "histogram":
|
|
1875
|
-
return finish(layoutHistogram(ctx, area, visible));
|
|
1876
|
-
case "box":
|
|
1877
|
-
return finish(layoutBox(ctx, area, visible));
|
|
1878
|
-
case "funnel":
|
|
1879
|
-
return finish(layoutFunnel(ctx, area, visible[0]));
|
|
1880
|
-
case "treemap":
|
|
1881
|
-
return finish(layoutTreemap(ctx, area, visible[0]));
|
|
1882
|
-
case "xy":
|
|
1883
|
-
return finish(layoutXy(ctx, area, visible));
|
|
1884
|
-
default:
|
|
1885
|
-
return finish(layoutCartesian(ctx, area, visible));
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
function layoutLegend(items, position, area, ctx, legendSelected) {
|
|
1889
|
-
const { theme, measure, marks } = ctx;
|
|
1890
|
-
const fs = theme.fontSize;
|
|
1891
|
-
const swatch = 10;
|
|
1892
|
-
const rowH = fs * 1.7;
|
|
1893
|
-
const itemW = (item) => swatch + 6 + measure(item.label, fs) + 14;
|
|
1894
|
-
const placed = [];
|
|
1895
|
-
let box;
|
|
1896
|
-
let rest;
|
|
1897
|
-
if (position === "left" || position === "right") {
|
|
1898
|
-
const w = Math.min(area.width * 0.35, Math.max(...items.map(itemW)));
|
|
1899
|
-
const fit = Math.max(1, Math.floor((area.height + 0.5) / rowH));
|
|
1900
|
-
const shown = items.slice(0, fit);
|
|
1901
|
-
const h = Math.min(shown.length * rowH, area.height);
|
|
1902
|
-
const x = position === "left" ? area.x : area.x + area.width - w;
|
|
1903
|
-
const y = area.y + (area.height - h) / 2;
|
|
1904
|
-
shown.forEach((item, i) => placed.push({ item, x, y: y + i * rowH + rowH / 2 }));
|
|
1905
|
-
if (shown.length < items.length) ctx.warn("legend-truncated", `The legend shows ${shown.length} of ${items.length} entries; the chart is too short for the rest.`);
|
|
1906
|
-
box = { x, y, width: w, height: h };
|
|
1907
|
-
rest = position === "left" ? { ...area, x: area.x + w + 8, width: area.width - w - 8 } : { ...area, width: area.width - w - 8 };
|
|
1908
|
-
} else {
|
|
1909
|
-
const rows = [[]];
|
|
1910
|
-
let used = 0;
|
|
1911
|
-
for (const item of items) {
|
|
1912
|
-
const w = itemW(item);
|
|
1913
|
-
if (used + w > area.width && rows[rows.length - 1].length) {
|
|
1914
|
-
rows.push([]);
|
|
1915
|
-
used = 0;
|
|
1916
|
-
}
|
|
1917
|
-
rows[rows.length - 1].push(item);
|
|
1918
|
-
used += w;
|
|
1919
|
-
}
|
|
1920
|
-
const maxRows = Math.max(1, Math.floor(ctx.height * 0.3 / rowH));
|
|
1921
|
-
const shown = rows.slice(0, maxRows);
|
|
1922
|
-
const hiddenCount = rows.slice(maxRows).reduce((t, r) => t + r.length, 0);
|
|
1923
|
-
if (hiddenCount) ctx.warn("legend-truncated", `The legend shows ${items.length - hiddenCount} of ${items.length} entries; the rest do not fit.`);
|
|
1924
|
-
const h = shown.length * rowH;
|
|
1925
|
-
const y0 = position === "top" ? area.y : area.y + area.height - h;
|
|
1926
|
-
shown.forEach((row, r) => {
|
|
1927
|
-
const total = row.reduce((t, item) => t + itemW(item), 0) - 14;
|
|
1928
|
-
let x = area.x + (area.width - total) / 2;
|
|
1929
|
-
for (const item of row) {
|
|
1930
|
-
placed.push({ item, x, y: y0 + r * rowH + rowH / 2 });
|
|
1931
|
-
x += itemW(item);
|
|
1932
|
-
}
|
|
1933
|
-
});
|
|
1934
|
-
box = { x: area.x, y: y0, width: area.width, height: h };
|
|
1935
|
-
rest = position === "top" ? { ...area, y: area.y + h + 6, height: area.height - h - 6 } : { ...area, height: area.height - h - 6 };
|
|
1936
|
-
}
|
|
1937
|
-
placed.forEach(({ item, x, y }, i) => {
|
|
1938
|
-
const opacity = item.faded ? 0.4 : void 0;
|
|
1939
|
-
if (item.shape === "line" || item.shape === "dash")
|
|
1940
|
-
marks.push({ kind: "line", key: `legend-swatch-${i}`, role: "legend-swatch", x1: x, y1: y, x2: x + swatch, y2: y, stroke: item.color, strokeWidth: item.shape === "dash" ? 1.5 : 2.5, ...item.shape === "dash" ? { dash: "3 2" } : {}, opacity, target: item.target, selected: item.selected });
|
|
1941
|
-
else marks.push({ kind: "rect", key: `legend-swatch-${i}`, role: "legend-swatch", x, y: y - swatch / 2, width: swatch, height: swatch, radius: 2, fill: item.color, opacity, target: item.target, selected: item.selected });
|
|
1942
|
-
marks.push({
|
|
1943
|
-
kind: "text",
|
|
1944
|
-
key: `legend-label-${i}`,
|
|
1945
|
-
role: "legend-label",
|
|
1946
|
-
x: x + swatch + 6,
|
|
1947
|
-
y,
|
|
1948
|
-
text: item.label,
|
|
1949
|
-
anchor: "start",
|
|
1950
|
-
baseline: "middle",
|
|
1951
|
-
size: fs,
|
|
1952
|
-
fill: item.selected ? theme.selection : theme.text,
|
|
1953
|
-
weight: item.selected ? 600 : void 0,
|
|
1954
|
-
opacity,
|
|
1955
|
-
target: item.target,
|
|
1956
|
-
selected: item.selected
|
|
1957
|
-
});
|
|
1958
|
-
});
|
|
1959
|
-
if (legendSelected) marks.push({ kind: "rect", key: "legend-selection", role: "selection", x: box.x - 4, y: box.y - 2, width: box.width + 8, height: box.height + 4, fill: "none", stroke: theme.selection, strokeWidth: 1, radius: 3 });
|
|
1960
|
-
return rest;
|
|
1961
|
-
}
|
|
1962
|
-
function addReadout(ctx, readout, plot) {
|
|
1963
|
-
const { spec, marks, theme, fs, measure } = ctx;
|
|
1964
|
-
const series = spec.series.find((s) => s.id === readout.seriesId);
|
|
1965
|
-
const value = _optionalChain([series, 'optionalAccess', _26 => _26.values, 'access', _27 => _27[readout.index]]);
|
|
1966
|
-
if (!series || value === null || value === void 0) return;
|
|
1967
|
-
const mark = marks.find((m) => (m.role === "point" || m.role === "bar") && _optionalChain([m, 'access', _28 => _28.target, 'optionalAccess', _29 => _29.kind]) === "point" && m.target.seriesId === readout.seriesId && m.target.index === readout.index);
|
|
1968
|
-
if (!mark) return;
|
|
1969
|
-
let ax;
|
|
1970
|
-
let ay;
|
|
1971
|
-
if (mark.kind === "rect") {
|
|
1972
|
-
ax = mark.x + mark.width / 2;
|
|
1973
|
-
ay = mark.y;
|
|
1974
|
-
} else if (mark.kind === "circle") {
|
|
1975
|
-
ax = mark.cx;
|
|
1976
|
-
ay = mark.cy - mark.r;
|
|
1977
|
-
} else return;
|
|
1978
|
-
const axis = series.axis === "secondary" ? spec.yAxis2 : spec.yAxis;
|
|
1979
|
-
const x = _optionalChain([series, 'access', _30 => _30.x, 'optionalAccess', _31 => _31[readout.index]]);
|
|
1980
|
-
const text = x !== null && x !== void 0 && _chunkNEOO6QJDcjs.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)))}`;
|
|
1981
|
-
const w = measure(text, fs, 600) + 10;
|
|
1982
|
-
const h = fs + 8;
|
|
1983
|
-
const bx = Math.max(plot.x, Math.min(plot.x + plot.width - w, ax - w / 2));
|
|
1984
|
-
const by = Math.max(plot.y, Math.min(plot.y + plot.height - h, ay - h - 6));
|
|
1985
|
-
marks.push({ kind: "rect", key: "drag-readout-box", role: "drag-readout", x: bx, y: by, width: w, height: h, fill: theme.text, radius: 3, opacity: 0.9 });
|
|
1986
|
-
marks.push({ kind: "text", key: "drag-readout", role: "drag-readout", x: bx + w / 2, y: by + h / 2, text, anchor: "middle", baseline: "middle", size: fs, weight: 600, fill: theme.surface });
|
|
1987
|
-
}
|
|
1988
|
-
var finite = (...n) => n.every((v) => v === void 0 || Number.isFinite(v));
|
|
1989
|
-
function finalizeMarks(marks, width, height) {
|
|
1990
|
-
const lim = (v, size) => Math.max(-size * 4, Math.min(size * 5, v));
|
|
1991
|
-
const out = [];
|
|
1992
|
-
for (const m of marks) {
|
|
1993
|
-
switch (m.kind) {
|
|
1994
|
-
case "rect":
|
|
1995
|
-
if (!finite(m.x, m.y, m.width, m.height)) continue;
|
|
1996
|
-
out.push({ ...m, x: lim(m.x, width), y: lim(m.y, height), width: Math.max(0, Math.min(m.width, width * 9)), height: Math.max(0, Math.min(m.height, height * 9)) });
|
|
1997
|
-
break;
|
|
1998
|
-
case "circle":
|
|
1999
|
-
if (!finite(m.cx, m.cy, m.r)) continue;
|
|
2000
|
-
out.push({ ...m, cx: lim(m.cx, width), cy: lim(m.cy, height) });
|
|
2001
|
-
break;
|
|
2002
|
-
case "line":
|
|
2003
|
-
if (!finite(m.x1, m.y1, m.x2, m.y2)) continue;
|
|
2004
|
-
out.push({ ...m, x1: lim(m.x1, width), y1: lim(m.y1, height), x2: lim(m.x2, width), y2: lim(m.y2, height) });
|
|
2005
|
-
break;
|
|
2006
|
-
case "text":
|
|
2007
|
-
if (!finite(m.x, m.y, m.size)) continue;
|
|
2008
|
-
out.push(m);
|
|
2009
|
-
break;
|
|
2010
|
-
case "path":
|
|
2011
|
-
if (/NaN|Infinity/.test(m.d)) continue;
|
|
2012
|
-
out.push(m);
|
|
2013
|
-
break;
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
|
-
return out;
|
|
2017
|
-
}
|
|
2018
|
-
function chartValueAtPoint(scene, point, secondary = false) {
|
|
2019
|
-
const scale = secondary ? _nullishCoalesce(scene.valueScale2, () => ( scene.valueScale)) : scene.valueScale;
|
|
2020
|
-
if (!scale || scene.percent) return void 0;
|
|
2021
|
-
const px = scale.axis === "y" ? point.y : point.x;
|
|
2022
|
-
return linearMap(px, scale.range, scale.domain);
|
|
2023
|
-
}
|
|
2024
|
-
function chartXAtPoint(scene, point) {
|
|
2025
|
-
const scale = scene.xScale;
|
|
2026
|
-
if (!scale) return void 0;
|
|
2027
|
-
return linearMap(point.x, scale.range, scale.domain);
|
|
2028
|
-
}
|
|
2029
|
-
function snapChartValue(scene, value, step, ticks = _nullishCoalesce(_optionalChain([scene, 'access', _32 => _32.valueScale, 'optionalAccess', _33 => _33.ticks]), () => ( []))) {
|
|
2030
|
-
const unit = _nullishCoalesce(step, () => ( (ticks.length > 1 ? (ticks[1] - ticks[0]) / 10 : 1)));
|
|
2031
|
-
if (!(unit > 0)) return value;
|
|
2032
|
-
const snapped = Math.round(value / unit) * unit;
|
|
2033
|
-
return Number(snapped.toPrecision(12));
|
|
2034
|
-
}
|
|
2035
94
|
|
|
2036
|
-
// src/chart/summary.ts
|
|
2037
|
-
function summarizeChart(spec, size = { width: 640, height: 400 }, scene = layoutChart(spec, { ...size, selection: null })) {
|
|
2038
|
-
const visible = spec.series.filter((s) => !s.hidden);
|
|
2039
|
-
const series = visible.map((s) => summarizeChartSeries(s, spec.categories));
|
|
2040
|
-
const colors = _chunkNEOO6QJDcjs.chartColorsByCategory.call(void 0, spec.type) ? spec.categories.map((_, i) => _optionalChain([visible, 'access', _34 => _34[0], 'optionalAccess', _35 => _35.pointColors, 'optionalAccess', _36 => _36[i]]) || _chunkNEOO6QJDcjs.chartSliceColor.call(void 0, spec, i)) : visible.map((s) => _chunkNEOO6QJDcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s)));
|
|
2041
|
-
const check2 = _chunkNEOO6QJDcjs.checkChartColors.call(void 0, colors, { surface: scene.theme.surface.startsWith("var(") ? "#ffffff" : scene.theme.surface });
|
|
2042
|
-
const axis = (s) => s ? { min: s.domain[0], max: s.domain[1], ticks: [...s.ticks] } : void 0;
|
|
2043
|
-
return {
|
|
2044
|
-
type: spec.type,
|
|
2045
|
-
title: spec.title,
|
|
2046
|
-
categories: spec.categories.length,
|
|
2047
|
-
series,
|
|
2048
|
-
...scene.valueScale ? { valueAxis: axis(scene.valueScale) } : {},
|
|
2049
|
-
...scene.valueScale2 ? { secondaryAxis: axis(scene.valueScale2) } : {},
|
|
2050
|
-
...scene.xScale ? { xAxis: axis(scene.xScale) } : {},
|
|
2051
|
-
warnings: scene.warnings,
|
|
2052
|
-
colors: { ok: check2.ok, issues: check2.issues },
|
|
2053
|
-
description: describeChart(spec, series)
|
|
2054
|
-
};
|
|
2055
|
-
}
|
|
2056
|
-
function describeChart(spec, series = spec.series.filter((s) => !s.hidden).map((s) => summarizeChartSeries(s, spec.categories))) {
|
|
2057
|
-
const kind = _chunkNEOO6QJDcjs.CHART_TYPE_LABELS[spec.type].toLowerCase();
|
|
2058
|
-
const parts = [];
|
|
2059
|
-
const family = _chunkNEOO6QJDcjs.chartFamily.call(void 0, spec);
|
|
2060
|
-
const format = spec.yAxis.format;
|
|
2061
|
-
const fmt = (v) => v === null ? "blank" : formatChartNumber(v, format);
|
|
2062
|
-
const head = spec.title ? `${spec.title}: a ${kind} chart` : `A ${kind} chart`;
|
|
2063
|
-
if (!series.length || series.every((s) => s.count === 0)) return `${head} with no data.`;
|
|
2064
|
-
if (family === "pie" || family === "funnel" || family === "treemap") {
|
|
2065
|
-
const s = series[0];
|
|
2066
|
-
const total = _nullishCoalesce(s.sum, () => ( 0));
|
|
2067
|
-
const share = (v) => total && v !== null ? ` (${Math.round(v / total * 100)}%)` : "";
|
|
2068
|
-
parts.push(`${head} of ${s.name} across ${spec.categories.length} categories, totalling ${fmt(s.sum)}.`);
|
|
2069
|
-
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)}.`);
|
|
2070
|
-
return parts.join(" ");
|
|
2071
|
-
}
|
|
2072
|
-
if (family === "waterfall") {
|
|
2073
|
-
const s = spec.series.find((x) => !x.hidden);
|
|
2074
|
-
const steps = s.values.filter((v, i) => v !== null && Number.isFinite(v) && !/\b(sub)?total\b/i.test(_nullishCoalesce(spec.categories[i], () => ( ""))));
|
|
2075
|
-
const total = steps.reduce((t, v) => t + v, 0);
|
|
2076
|
-
const up = steps.filter((v) => v > 0).length;
|
|
2077
|
-
const down = steps.filter((v) => v < 0).length;
|
|
2078
|
-
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)}.`;
|
|
2079
|
-
}
|
|
2080
|
-
if (family === "histogram" || family === "box") {
|
|
2081
|
-
parts.push(`${head} of ${series.map((s) => s.name).join(", ")}.`);
|
|
2082
|
-
for (const s of series) parts.push(`${s.name}: ${s.count} values from ${fmt(s.min)} to ${fmt(s.max)}, mean ${fmt(s.mean)}.`);
|
|
2083
|
-
return parts.join(" ");
|
|
2084
|
-
}
|
|
2085
|
-
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]})` : ""}`;
|
|
2086
|
-
parts.push(`${head} showing ${what}.`);
|
|
2087
|
-
for (const s of series.slice(0, 8)) {
|
|
2088
|
-
if (!s.count) {
|
|
2089
|
-
parts.push(`${s.name} has no values.`);
|
|
2090
|
-
continue;
|
|
2091
|
-
}
|
|
2092
|
-
const pct = s.change !== null ? Math.round(Math.abs(s.change) * 100) : 0;
|
|
2093
|
-
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" : "";
|
|
2094
|
-
parts.push(`${s.name} ranges from ${fmt(s.min)} (${s.minAt}) to ${fmt(s.max)} (${s.maxAt})${trend}.`);
|
|
2095
|
-
}
|
|
2096
|
-
if (series.length > 8) parts.push(`${series.length - 8} more series are not described.`);
|
|
2097
|
-
return parts.join(" ");
|
|
2098
|
-
}
|
|
2099
95
|
|
|
2100
|
-
// src/chart/svg.ts
|
|
2101
|
-
function chartTextBaseline(baseline) {
|
|
2102
|
-
return baseline === "top" ? "hanging" : baseline === "middle" ? "central" : "text-after-edge";
|
|
2103
|
-
}
|
|
2104
|
-
function chartTextTransform(mark) {
|
|
2105
|
-
return mark.rotate ? `rotate(${mark.rotate} ${mark.x} ${mark.y})` : void 0;
|
|
2106
|
-
}
|
|
2107
|
-
var escape = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2108
|
-
function attrs(values) {
|
|
2109
|
-
return Object.entries(values).filter(([, v]) => v !== void 0 && v !== "").map(([k, v]) => ` ${k}="${escape(String(v))}"`).join("");
|
|
2110
|
-
}
|
|
2111
|
-
function markToSvg(mark, fontFamily) {
|
|
2112
|
-
const common = { opacity: mark.opacity, "data-role": mark.role };
|
|
2113
|
-
switch (mark.kind) {
|
|
2114
|
-
case "rect":
|
|
2115
|
-
return `<rect${attrs({ 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 })}/>`;
|
|
2116
|
-
case "path":
|
|
2117
|
-
return `<path${attrs({ 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 })}/>`;
|
|
2118
|
-
case "circle":
|
|
2119
|
-
return `<circle${attrs({ cx: mark.cx, cy: mark.cy, r: mark.r, fill: mark.fill, stroke: mark.stroke, "stroke-width": mark.strokeWidth, ...common })}/>`;
|
|
2120
|
-
case "line":
|
|
2121
|
-
return `<line${attrs({ x1: mark.x1, y1: mark.y1, x2: mark.x2, y2: mark.y2, stroke: mark.stroke, "stroke-width": mark.strokeWidth, "stroke-dasharray": mark.dash, ...common })}/>`;
|
|
2122
|
-
case "text":
|
|
2123
|
-
return `<text${attrs({
|
|
2124
|
-
x: mark.x,
|
|
2125
|
-
y: mark.y,
|
|
2126
|
-
"text-anchor": mark.anchor,
|
|
2127
|
-
"dominant-baseline": chartTextBaseline(mark.baseline),
|
|
2128
|
-
"font-size": mark.size,
|
|
2129
|
-
"font-weight": mark.weight,
|
|
2130
|
-
"font-family": fontFamily,
|
|
2131
|
-
fill: mark.fill,
|
|
2132
|
-
transform: chartTextTransform(mark),
|
|
2133
|
-
...common
|
|
2134
|
-
})}>${escape(mark.text)}</text>`;
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2137
|
-
function chartSceneToSvg(scene, options = {}) {
|
|
2138
|
-
const hide = _nullishCoalesce(options.hideSelection, () => ( true));
|
|
2139
|
-
const font = scene.theme.fontFamily;
|
|
2140
|
-
const body = scene.marks.filter((m) => !(hide && m.role === "selection")).map((m) => markToSvg(m, font)).join("");
|
|
2141
|
-
const label = options.ariaLabel;
|
|
2142
|
-
const desc = options.description;
|
|
2143
|
-
const labelled = [label ? "chart-title" : "", desc ? "chart-desc" : ""].filter(Boolean).join(" ");
|
|
2144
|
-
const head = `${label ? `<title id="chart-title">${escape(label)}</title>` : ""}${desc ? `<desc id="chart-desc">${escape(desc)}</desc>` : ""}`;
|
|
2145
|
-
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>`;
|
|
2146
|
-
}
|
|
2147
|
-
function chartToSvg(spec, options) {
|
|
2148
|
-
const scene = layoutChart(spec, { ...options, selection: null });
|
|
2149
|
-
return chartSceneToSvg(scene, { ...options, ariaLabel: _nullishCoalesce(options.ariaLabel, () => ( (spec.title || void 0))), description: _nullishCoalesce(options.description, () => ( describeChart(spec))) });
|
|
2150
|
-
}
|
|
2151
96
|
|
|
2152
|
-
// src/chart/sparkline.ts
|
|
2153
|
-
var MAX_POINTS = 1e5;
|
|
2154
|
-
function layoutSparkline(values, options) {
|
|
2155
|
-
const kind = _nullishCoalesce(options.kind, () => ( "line"));
|
|
2156
|
-
const w = Number.isFinite(options.width) ? Math.max(1, options.width) : 1;
|
|
2157
|
-
const h = Number.isFinite(options.height) ? Math.max(1, options.height) : 1;
|
|
2158
|
-
const color = _nullishCoalesce(options.color, () => ( "#4269d0"));
|
|
2159
|
-
const negative = _nullishCoalesce(options.negativeColor, () => ( "#e5484d"));
|
|
2160
|
-
const theme = { ..._chunkNEOO6QJDcjs.DEFAULT_CHART_THEME, background: "transparent" };
|
|
2161
|
-
const marks = [];
|
|
2162
|
-
const scene = { width: w, height: h, plot: { x: 0, y: 0, width: w, height: h }, marks, percent: false, theme, warnings: [] };
|
|
2163
|
-
const vals = values.slice(0, MAX_POINTS).map((v) => v !== null && Number.isFinite(v) ? Math.max(-1e300, Math.min(1e300, v)) : null);
|
|
2164
|
-
const n = vals.length;
|
|
2165
|
-
if (!n) return scene;
|
|
2166
|
-
const pad = Math.max(0.5, Math.min(2, h * 0.1));
|
|
2167
|
-
const x0 = pad;
|
|
2168
|
-
const y0 = pad;
|
|
2169
|
-
const iw = Math.max(0.5, w - 2 * pad);
|
|
2170
|
-
const ih = Math.max(0.5, h - 2 * pad);
|
|
2171
|
-
let lo = Infinity;
|
|
2172
|
-
let hi = -Infinity;
|
|
2173
|
-
for (const v of vals) if (v !== null) lo = Math.min(lo, v), hi = Math.max(hi, v);
|
|
2174
|
-
if (!Number.isFinite(lo)) return scene;
|
|
2175
|
-
if (options.min !== void 0 && Number.isFinite(options.min)) lo = options.min;
|
|
2176
|
-
if (options.max !== void 0 && Number.isFinite(options.max)) hi = options.max;
|
|
2177
|
-
let hiAt = -1;
|
|
2178
|
-
let loAt = -1;
|
|
2179
|
-
vals.forEach((v, i) => {
|
|
2180
|
-
if (v === null) return;
|
|
2181
|
-
if (hiAt < 0 || v > vals[hiAt]) hiAt = i;
|
|
2182
|
-
if (loAt < 0 || v < vals[loAt]) loAt = i;
|
|
2183
|
-
});
|
|
2184
|
-
if (kind === "line") {
|
|
2185
|
-
const span2 = hi - lo;
|
|
2186
|
-
const fy2 = (v) => y0 + ih - (span2 > 0 ? (v - lo) / span2 : 0.5) * ih;
|
|
2187
|
-
const fx = (j) => n > 1 ? x0 + j / (n - 1) * iw : x0 + iw / 2;
|
|
2188
|
-
let d = "";
|
|
2189
|
-
let run = [];
|
|
2190
|
-
const flush = () => {
|
|
2191
|
-
if (run.length >= 2) {
|
|
2192
|
-
const pts = run.length > 4e3 ? run.filter((_, i) => i % (Math.floor(run.length / 2e3) + 1) === 0) : run;
|
|
2193
|
-
d += pts.map(([x, y], i) => `${i ? "L" : "M"}${r2(x)},${r2(y)}`).join("");
|
|
2194
|
-
}
|
|
2195
|
-
run = [];
|
|
2196
|
-
};
|
|
2197
|
-
vals.forEach((v, j) => v === null ? flush() : run.push([fx(j), fy2(v)]));
|
|
2198
|
-
flush();
|
|
2199
|
-
if (d) marks.push({ kind: "path", key: "sparkline", role: "line", d, fill: "none", stroke: color, strokeWidth: 1.25 });
|
|
2200
|
-
const r = Math.max(1, Math.min(3, h * 0.08));
|
|
2201
|
-
if (options.markers || n === 1) {
|
|
2202
|
-
const step = Math.max(1, Math.floor(n / 500));
|
|
2203
|
-
vals.forEach((v, j) => {
|
|
2204
|
-
if (v !== null && j % step === 0) marks.push({ kind: "circle", key: `point-${j}`, role: "point", cx: fx(j), cy: fy2(v), r, fill: color });
|
|
2205
|
-
});
|
|
2206
|
-
}
|
|
2207
|
-
if (options.highLow && n > 1) {
|
|
2208
|
-
if (hiAt >= 0) marks.push({ kind: "circle", key: "high", role: "point", cx: fx(hiAt), cy: fy2(vals[hiAt]), r: r + 0.5, fill: color });
|
|
2209
|
-
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 });
|
|
2210
|
-
}
|
|
2211
|
-
return scene;
|
|
2212
|
-
}
|
|
2213
|
-
const slot = iw / n;
|
|
2214
|
-
const bw = Math.max(0.25, slot * 0.8);
|
|
2215
|
-
const win = kind === "win-loss";
|
|
2216
|
-
const lo0 = win ? -1 : Math.min(lo, 0);
|
|
2217
|
-
const hi0 = win ? 1 : Math.max(hi, 0);
|
|
2218
|
-
const span = hi0 - lo0;
|
|
2219
|
-
const fy = (v) => span > 0 ? y0 + ih - (v - lo0) / span * ih : y0 + ih;
|
|
2220
|
-
const base = fy(0);
|
|
2221
|
-
vals.forEach((raw, j) => {
|
|
2222
|
-
if (raw === null) return;
|
|
2223
|
-
const v = win ? Math.sign(raw) : raw;
|
|
2224
|
-
if (win && v === 0) return;
|
|
2225
|
-
const top = fy(v);
|
|
2226
|
-
const highlight = options.highLow && !win && (j === hiAt || j === loAt);
|
|
2227
|
-
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 });
|
|
2228
|
-
});
|
|
2229
|
-
return scene;
|
|
2230
|
-
}
|
|
2231
|
-
function sparklineToSvg(values, options) {
|
|
2232
|
-
return chartSceneToSvg(layoutSparkline(values, options), { ariaLabel: options.ariaLabel });
|
|
2233
|
-
}
|
|
2234
97
|
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
}
|
|
2266
|
-
if (options.colors !== void 0) {
|
|
2267
|
-
const scheme = CHART_COLOR_SCHEMES.find((p) => p.id === options.colors);
|
|
2268
|
-
if (!scheme) return { ok: false, message: `There is no colour scheme "${options.colors}"; expected one of ${CHART_COLOR_SCHEMES.map((p) => `"${p.id}"`).join(", ")}.` };
|
|
2269
|
-
patch.palette = [...scheme.palette];
|
|
2270
|
-
}
|
|
2271
|
-
if (options.layout === void 0 && options.colors === void 0) return { ok: false, message: "Name a layout, a colour scheme or both." };
|
|
2272
|
-
return { ok: true, patch };
|
|
2273
|
-
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
var _chunkPRSJJZBScjs = require('../chunk-PRSJJZBS.cjs');
|
|
123
|
+
require('../chunk-XP5IUZUO.cjs');
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
var _chunkTNNJXF46cjs = require('../chunk-TNNJXF46.cjs');
|
|
127
|
+
require('../chunk-QKLRAA4T.cjs');
|
|
2274
128
|
|
|
2275
129
|
// src/chart/recommend.ts
|
|
2276
130
|
var blank = (c) => c === null || c === void 0 || String(c).trim() === "";
|
|
2277
|
-
var isText = (c) => blank(c) ||
|
|
131
|
+
var isText = (c) => blank(c) || _chunkPRSJJZBScjs.parseChartValue.call(void 0, c) === void 0;
|
|
2278
132
|
function analyzeChartTable(rows) {
|
|
2279
133
|
const grid = rows.filter((r) => r.some((c) => !blank(c)));
|
|
2280
134
|
const height = grid.length;
|
|
2281
135
|
const width = Math.max(0, ...grid.map((r) => r.length));
|
|
2282
|
-
const cell = (r, c) => _optionalChain([grid, 'access',
|
|
136
|
+
const cell = (r, c) => _optionalChain([grid, 'access', _2 => _2[r], 'optionalAccess', _3 => _3[c]]);
|
|
2283
137
|
const headerRow = height > 1 && grid[0].some((c) => !blank(c) && isText(c)) && Array.from({ length: width - 1 }, (_, i) => i + 1).every((c) => isText(cell(0, c)));
|
|
2284
138
|
const blankCorner = headerRow && blank(cell(0, 0));
|
|
2285
139
|
const labelColumn = width > 1 && (blankCorner || Array.from({ length: height - (headerRow ? 1 : 0) }, (_, i) => i + (headerRow ? 1 : 0)).every((r) => isText(cell(r, 0))));
|
|
@@ -2292,7 +146,7 @@ function analyzeChartTable(rows) {
|
|
|
2292
146
|
let anyPositive = false;
|
|
2293
147
|
for (let r = headerRow ? 1 : 0; r < height; r++)
|
|
2294
148
|
for (let c = labelColumn ? 1 : 0; c < width; c++) {
|
|
2295
|
-
const v =
|
|
149
|
+
const v = _chunkPRSJJZBScjs.parseChartValue.call(void 0, cell(r, c));
|
|
2296
150
|
if (typeof v !== "number") continue;
|
|
2297
151
|
anyNumber = true;
|
|
2298
152
|
if (v < 0) {
|
|
@@ -2300,8 +154,8 @@ function analyzeChartTable(rows) {
|
|
|
2300
154
|
anyNegative = true;
|
|
2301
155
|
} else if (v > 0) anyPositive = true;
|
|
2302
156
|
}
|
|
2303
|
-
const labels = byRows ? _nullishCoalesce(_optionalChain([grid, 'access',
|
|
2304
|
-
const dates = (byRows ? headerRow : labelColumn) && labels.length > 1 && labels.filter((l) => !blank(l) && parseChartDate(String(l)) !== null).length >= labels.length * 0.8;
|
|
157
|
+
const labels = byRows ? _nullishCoalesce(_optionalChain([grid, 'access', _4 => _4[0], 'optionalAccess', _5 => _5.slice, 'call', _6 => _6(labelColumn ? 1 : 0)]), () => ( [])) : grid.slice(headerRow ? 1 : 0).map((r) => r[0]);
|
|
158
|
+
const dates = (byRows ? headerRow : labelColumn) && labels.length > 1 && labels.filter((l) => !blank(l) && _chunkPRSJJZBScjs.parseChartDate.call(void 0, String(l)) !== null).length >= labels.length * 0.8;
|
|
2305
159
|
return {
|
|
2306
160
|
headerRow,
|
|
2307
161
|
labelColumn,
|
|
@@ -2377,8 +231,8 @@ function recommendForShape(shape) {
|
|
|
2377
231
|
function chartFromTable(rows, options = {}) {
|
|
2378
232
|
const shape = analyzeChartTable(rows);
|
|
2379
233
|
const rec = (_nullishCoalesce(options.type, () => ( "auto"))) === "auto" ? recommendForShape(shape)[0] : void 0;
|
|
2380
|
-
const type = _nullishCoalesce(_optionalChain([rec, 'optionalAccess',
|
|
2381
|
-
const stacking = _nullishCoalesce(_optionalChain([rec, 'optionalAccess',
|
|
234
|
+
const type = _nullishCoalesce(_optionalChain([rec, 'optionalAccess', _7 => _7.type]), () => ( (options.type === "auto" || !options.type ? "bar" : options.type)));
|
|
235
|
+
const stacking = _nullishCoalesce(_optionalChain([rec, 'optionalAccess', _8 => _8.stacking]), () => ( "none"));
|
|
2382
236
|
const grid = rows.filter((r) => r.some((c) => !blank(c))).map((r) => [...r]);
|
|
2383
237
|
const width = Math.max(0, ...grid.map((r) => r.length));
|
|
2384
238
|
let table = grid;
|
|
@@ -2388,1198 +242,16 @@ function chartFromTable(rows, options = {}) {
|
|
|
2388
242
|
if (xFromFirst) {
|
|
2389
243
|
table = table.map((r) => [r[1], ...r.slice(2)]);
|
|
2390
244
|
}
|
|
2391
|
-
const data =
|
|
245
|
+
const data = _chunkPRSJJZBScjs.chartDataFromTable.call(void 0, table, { seriesIn: shape.byRows ? "rows" : "columns", header: true, xFromFirstColumn: xFromFirst });
|
|
2392
246
|
return { type, stacking, categories: data.categories, series: data.series, issues: data.issues, shape };
|
|
2393
247
|
}
|
|
2394
248
|
|
|
2395
|
-
// src/chart/controller.ts
|
|
2396
|
-
function sameChartJson(a, b) {
|
|
2397
|
-
if (a === b) return true;
|
|
2398
|
-
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return Number.isNaN(a) && Number.isNaN(b);
|
|
2399
|
-
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
2400
|
-
if (Array.isArray(a)) {
|
|
2401
|
-
const bb = b;
|
|
2402
|
-
return a.length === bb.length && a.every((v, i) => sameChartJson(v, bb[i]));
|
|
2403
|
-
}
|
|
2404
|
-
const ao = a;
|
|
2405
|
-
const bo = b;
|
|
2406
|
-
const keys = Object.keys(ao).filter((k) => ao[k] !== void 0);
|
|
2407
|
-
if (keys.length !== Object.keys(bo).filter((k) => bo[k] !== void 0).length) return false;
|
|
2408
|
-
return keys.every((k) => Object.prototype.hasOwnProperty.call(bo, k) && sameChartJson(ao[k], bo[k]));
|
|
2409
|
-
}
|
|
2410
|
-
var fail = (message, issues) => ({ ok: false, message, ...issues ? { issues } : {} });
|
|
2411
|
-
var ChartController = (_class = class {
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
__init() {this.selection = null}
|
|
2415
|
-
__init2() {this.issues = []}
|
|
2416
|
-
__init3() {this.gestureKey = null}
|
|
2417
|
-
__init4() {this.gestureCount = 0}
|
|
2418
|
-
__init5() {this.gestureStart = null}
|
|
2419
|
-
__init6() {this.revision = 0}
|
|
2420
|
-
__init7() {this.applying = false}
|
|
2421
|
-
|
|
2422
|
-
__init8() {this.listeners = /* @__PURE__ */ new Set()}
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
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);
|
|
2426
|
-
this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
|
|
2427
|
-
this.onChange = options.onChange;
|
|
2428
|
-
const { spec, issues } = _chunkNEOO6QJDcjs.normalizeChartSpec.call(void 0, _nullishCoalesce(options.spec, () => ( {})));
|
|
2429
|
-
this.spec = spec;
|
|
2430
|
-
this.issues = issues;
|
|
2431
|
-
this.state = this.computeState();
|
|
2432
|
-
this.connect();
|
|
2433
|
-
}
|
|
2434
|
-
connect() {
|
|
2435
|
-
_optionalChain([this, 'access', _44 => _44.unsubscribeHistory, 'optionalCall', _45 => _45()]);
|
|
2436
|
-
this.unsubscribeHistory = this.history.subscribe((h) => {
|
|
2437
|
-
const s = this.state;
|
|
2438
|
-
if (this.applying || s.canUndo === h.canUndo && s.canRedo === h.canRedo && s.undoLabel === h.undoName && s.redoLabel === h.redoName) return;
|
|
2439
|
-
this.notify(false);
|
|
2440
|
-
});
|
|
2441
|
-
}
|
|
2442
|
-
// -------------------------------------------------------------------------
|
|
2443
|
-
// State
|
|
2444
|
-
// -------------------------------------------------------------------------
|
|
2445
|
-
/** The current snapshot (stable until the next change). */
|
|
2446
|
-
__init9() {this.getState = () => this.state}
|
|
2447
|
-
/** Calls `listener` after every change. Returns an unsubscribe function. */
|
|
2448
|
-
__init10() {this.subscribe = (listener) => {
|
|
2449
|
-
if (!this.unsubscribeHistory) this.connect();
|
|
2450
|
-
this.listeners.add(listener);
|
|
2451
|
-
return () => {
|
|
2452
|
-
this.listeners.delete(listener);
|
|
2453
|
-
};
|
|
2454
|
-
}}
|
|
2455
|
-
/** The current spec (do not mutate; edit through the controller). */
|
|
2456
|
-
getSpec() {
|
|
2457
|
-
return this.spec;
|
|
2458
|
-
}
|
|
2459
|
-
getSeries(id) {
|
|
2460
|
-
return this.spec.series.find((s) => s.id === id);
|
|
2461
|
-
}
|
|
2462
|
-
computeState() {
|
|
2463
|
-
const h = this.history.getState();
|
|
2464
|
-
const sel = this.selection;
|
|
2465
|
-
const seriesId = _optionalChain([sel, 'optionalAccess', _46 => _46.kind]) === "series" || _optionalChain([sel, 'optionalAccess', _47 => _47.kind]) === "point" || _optionalChain([sel, 'optionalAccess', _48 => _48.kind]) === "trendline" ? sel.seriesId : null;
|
|
2466
|
-
return {
|
|
2467
|
-
spec: this.spec,
|
|
2468
|
-
selection: sel,
|
|
2469
|
-
selectedSeries: seriesId ? _nullishCoalesce(this.spec.series.find((s) => s.id === seriesId), () => ( null)) : null,
|
|
2470
|
-
selectedIndex: _optionalChain([sel, 'optionalAccess', _49 => _49.kind]) === "point" || _optionalChain([sel, 'optionalAccess', _50 => _50.kind]) === "category" ? sel.index : null,
|
|
2471
|
-
issues: this.issues,
|
|
2472
|
-
gesture: this.gestureKey,
|
|
2473
|
-
canUndo: h.canUndo,
|
|
2474
|
-
canRedo: h.canRedo,
|
|
2475
|
-
undoLabel: h.undoName,
|
|
2476
|
-
redoLabel: h.redoName,
|
|
2477
|
-
revision: this.revision
|
|
2478
|
-
};
|
|
2479
|
-
}
|
|
2480
|
-
notify(specChanged) {
|
|
2481
|
-
this.revision++;
|
|
2482
|
-
this.state = this.computeState();
|
|
2483
|
-
if (specChanged) _optionalChain([this, 'access', _51 => _51.onChange, 'optionalCall', _52 => _52(this.spec)]);
|
|
2484
|
-
const state = this.state;
|
|
2485
|
-
this.listeners.forEach((listener) => {
|
|
2486
|
-
try {
|
|
2487
|
-
listener(state);
|
|
2488
|
-
} catch (error) {
|
|
2489
|
-
console.error("[ChartController] Error in state listener:", error);
|
|
2490
|
-
}
|
|
2491
|
-
});
|
|
2492
|
-
}
|
|
2493
|
-
// -------------------------------------------------------------------------
|
|
2494
|
-
// Changes: every edit records a before / after snapshot of the spec
|
|
2495
|
-
// -------------------------------------------------------------------------
|
|
2496
|
-
/** Drops a selection that no longer points at anything. */
|
|
2497
|
-
validSelection(sel, spec) {
|
|
2498
|
-
if (!sel) return null;
|
|
2499
|
-
switch (sel.kind) {
|
|
2500
|
-
case "series":
|
|
2501
|
-
return spec.series.some((s) => s.id === sel.seriesId) ? sel : null;
|
|
2502
|
-
case "point":
|
|
2503
|
-
return spec.series.some((s) => s.id === sel.seriesId) && sel.index < spec.categories.length ? sel : null;
|
|
2504
|
-
case "category":
|
|
2505
|
-
return sel.index < spec.categories.length ? sel : null;
|
|
2506
|
-
case "trendline":
|
|
2507
|
-
return _optionalChain([spec, 'access', _53 => _53.series, 'access', _54 => _54.find, 'call', _55 => _55((s) => s.id === sel.seriesId), 'optionalAccess', _56 => _56.trendlines, 'optionalAccess', _57 => _57[sel.index]]) ? sel : null;
|
|
2508
|
-
case "title":
|
|
2509
|
-
return spec.title ? sel : null;
|
|
2510
|
-
case "subtitle":
|
|
2511
|
-
return spec.subtitle ? sel : null;
|
|
2512
|
-
default:
|
|
2513
|
-
return sel;
|
|
2514
|
-
}
|
|
2515
|
-
}
|
|
2516
|
-
applySpec(spec) {
|
|
2517
|
-
this.applying = true;
|
|
2518
|
-
try {
|
|
2519
|
-
this.spec = spec;
|
|
2520
|
-
this.selection = this.validSelection(this.selection, spec);
|
|
2521
|
-
} finally {
|
|
2522
|
-
this.applying = false;
|
|
2523
|
-
}
|
|
2524
|
-
this.notify(true);
|
|
2525
|
-
}
|
|
2526
|
-
commit(label, next, select) {
|
|
2527
|
-
const before = this.spec;
|
|
2528
|
-
const hadIssues = this.issues.length > 0;
|
|
2529
|
-
this.issues = [];
|
|
2530
|
-
if (sameChartJson(before, next)) {
|
|
2531
|
-
const reselect = select !== void 0 && !sameChartSelection(this.selection, select) && !(select === null && this.selection === null);
|
|
2532
|
-
if (reselect) this.selection = select;
|
|
2533
|
-
if (reselect || hadIssues) this.notify(false);
|
|
2534
|
-
return;
|
|
2535
|
-
}
|
|
2536
|
-
const command = {
|
|
2537
|
-
name: label,
|
|
2538
|
-
tag: "chart",
|
|
2539
|
-
execute: () => this.applySpec(next),
|
|
2540
|
-
undo: () => this.applySpec(before),
|
|
2541
|
-
redo: () => this.applySpec(next)
|
|
2542
|
-
};
|
|
2543
|
-
if (select !== void 0) this.selection = select;
|
|
2544
|
-
if (this.gestureKey !== null) this.history.executeMerged(command, { key: `chart.gesture:${this.gestureKey}:${this.gestureCount}` });
|
|
2545
|
-
else this.history.execute(command);
|
|
2546
|
-
}
|
|
2547
|
-
reject(message, issues = []) {
|
|
2548
|
-
this.issues = issues.length ? issues : [{ path: "", message }];
|
|
2549
|
-
this.notify(false);
|
|
2550
|
-
return fail(message, issues.length ? issues : void 0);
|
|
2551
|
-
}
|
|
2552
|
-
/** A copy of the spec with `edit` applied (series and categories copied as needed). */
|
|
2553
|
-
edited(edit) {
|
|
2554
|
-
const draft = _chunkNEOO6QJDcjs.cloneChartSpec.call(void 0, this.spec);
|
|
2555
|
-
edit(draft);
|
|
2556
|
-
return draft;
|
|
2557
|
-
}
|
|
2558
|
-
unknownSeries(id) {
|
|
2559
|
-
const ids = this.spec.series.map((s) => `"${s.id}"`);
|
|
2560
|
-
return fail(`There is no series "${id}"; ${ids.length ? `expected one of ${ids.join(", ")}` : "the chart has no series yet"}.`);
|
|
2561
|
-
}
|
|
2562
|
-
badIndex(index) {
|
|
2563
|
-
const n = this.spec.categories.length;
|
|
2564
|
-
return fail(n ? `Category index ${index} is out of range; expected 0 to ${n - 1}.` : "The chart has no categories yet.");
|
|
2565
|
-
}
|
|
2566
|
-
// -------------------------------------------------------------------------
|
|
2567
|
-
// Presentation
|
|
2568
|
-
// -------------------------------------------------------------------------
|
|
2569
|
-
/**
|
|
2570
|
-
* Changes any presentation or data fields as one undo step. All-or-nothing: if anything
|
|
2571
|
-
* is invalid, nothing changes and every problem is returned (and shown in `state.issues`).
|
|
2572
|
-
*/
|
|
2573
|
-
update(patch, options = {}) {
|
|
2574
|
-
const s = this.spec;
|
|
2575
|
-
const merged = {
|
|
2576
|
-
...s,
|
|
2577
|
-
...patch,
|
|
2578
|
-
xAxis: patch.xAxis ? { ...s.xAxis, ...patch.xAxis } : s.xAxis,
|
|
2579
|
-
yAxis: patch.yAxis ? { ...s.yAxis, ...patch.yAxis } : s.yAxis,
|
|
2580
|
-
yAxis2: patch.yAxis2 ? { ...s.yAxis2, ...patch.yAxis2 } : s.yAxis2,
|
|
2581
|
-
legend: patch.legend ? { ...s.legend, ...patch.legend } : s.legend,
|
|
2582
|
-
version: 1
|
|
2583
|
-
};
|
|
2584
|
-
const { spec, issues } = _chunkNEOO6QJDcjs.normalizeChartSpec.call(void 0, merged);
|
|
2585
|
-
if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
2586
|
-
if (JSON.stringify(spec) === JSON.stringify(s)) return { ok: true };
|
|
2587
|
-
this.commit(_nullishCoalesce(options.label, () => ( updateLabel(patch))), spec);
|
|
2588
|
-
return { ok: true };
|
|
2589
|
-
}
|
|
2590
|
-
setType(type) {
|
|
2591
|
-
return this.update({ type }, { label: `Change chart type` });
|
|
2592
|
-
}
|
|
2593
|
-
setTitle(title) {
|
|
2594
|
-
return this.update({ title }, { label: "Edit title" });
|
|
2595
|
-
}
|
|
2596
|
-
setSubtitle(subtitle) {
|
|
2597
|
-
return this.update({ subtitle }, { label: "Edit subtitle" });
|
|
2598
|
-
}
|
|
2599
|
-
/** Changes the category (`x`), value (`y`) or secondary value (`y2`) axis. */
|
|
2600
|
-
setAxis(axis, patch) {
|
|
2601
|
-
const key = axis === "x" ? "xAxis" : axis === "y" ? "yAxis" : "yAxis2";
|
|
2602
|
-
return this.update({ [key]: patch }, { label: `Change ${axis === "x" ? "category" : axis === "y" ? "value" : "secondary value"} axis` });
|
|
2603
|
-
}
|
|
2604
|
-
/**
|
|
2605
|
-
* Applies a named quick layout and / or colour scheme (see `CHART_LAYOUT_PRESETS` and
|
|
2606
|
-
* `CHART_COLOR_SCHEMES`) as one undo step.
|
|
2607
|
-
*/
|
|
2608
|
-
applyPreset(options) {
|
|
2609
|
-
const preset = chartPresetPatch(options);
|
|
2610
|
-
if (!preset.ok) return this.reject(preset.message);
|
|
2611
|
-
const label = options.layout && options.colors ? "Apply chart style" : options.layout ? "Apply quick layout" : "Change colours";
|
|
2612
|
-
return this.update(preset.patch, { label });
|
|
2613
|
-
}
|
|
2614
|
-
setLegend(patch) {
|
|
2615
|
-
return this.update({ legend: patch }, { label: "Change legend" });
|
|
2616
|
-
}
|
|
2617
|
-
// -------------------------------------------------------------------------
|
|
2618
|
-
// Data
|
|
2619
|
-
// -------------------------------------------------------------------------
|
|
2620
|
-
/**
|
|
2621
|
-
* Replaces categories and / or series as one undo step. Values are validated and padded
|
|
2622
|
-
* to the categories; all-or-nothing like `update`.
|
|
2623
|
-
*/
|
|
2624
|
-
setData(data, options = {}) {
|
|
2625
|
-
return this.update({ ...data.categories ? { categories: data.categories } : {}, ...data.series ? { series: data.series } : {} }, { label: _nullishCoalesce(options.label, () => ( "Edit chart data")) });
|
|
2626
|
-
}
|
|
2627
|
-
/**
|
|
2628
|
-
* Sets one value (numbers, numeric text such as `1,200` or `12%`, or blank for a gap).
|
|
2629
|
-
* One undo step, or part of the current gesture.
|
|
2630
|
-
*/
|
|
2631
|
-
setValue(seriesId, index, value, options = {}) {
|
|
2632
|
-
const series = this.getSeries(seriesId);
|
|
2633
|
-
if (!series) return this.unknownSeries(seriesId);
|
|
2634
|
-
if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
|
|
2635
|
-
const parsed = _chunkNEOO6QJDcjs.parseChartValue.call(void 0, value);
|
|
2636
|
-
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.` }]);
|
|
2637
|
-
if (series.values[index] === parsed) {
|
|
2638
|
-
if (this.issues.length) {
|
|
2639
|
-
this.issues = [];
|
|
2640
|
-
this.notify(false);
|
|
2641
|
-
}
|
|
2642
|
-
return { ok: true };
|
|
2643
|
-
}
|
|
2644
|
-
const next = this.edited((d) => {
|
|
2645
|
-
d.series.find((s) => s.id === seriesId).values[index] = parsed;
|
|
2646
|
-
});
|
|
2647
|
-
this.commit(_nullishCoalesce(options.label, () => ( `Edit ${series.name} \xB7 ${this.spec.categories[index]}`)), next);
|
|
2648
|
-
return { ok: true };
|
|
2649
|
-
}
|
|
2650
|
-
/** Several values as one undo step (all-or-nothing). */
|
|
2651
|
-
setValues(cells, options = {}) {
|
|
2652
|
-
const issues = [];
|
|
2653
|
-
const parsed = [];
|
|
2654
|
-
for (const cell of cells) {
|
|
2655
|
-
if (!this.getSeries(cell.seriesId)) issues.push({ path: cell.seriesId, message: this.unknownSeries(cell.seriesId).message });
|
|
2656
|
-
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 });
|
|
2657
|
-
else {
|
|
2658
|
-
const value = _chunkNEOO6QJDcjs.parseChartValue.call(void 0, cell.value);
|
|
2659
|
-
if (value === void 0) issues.push({ path: `${cell.seriesId}[${cell.index}]`, message: `"${String(cell.value)}" is not a number.` });
|
|
2660
|
-
else parsed.push({ seriesId: cell.seriesId, index: cell.index, value });
|
|
2661
|
-
}
|
|
2662
|
-
}
|
|
2663
|
-
if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
2664
|
-
const next = this.edited((d) => {
|
|
2665
|
-
for (const cell of parsed) d.series.find((s) => s.id === cell.seriesId).values[cell.index] = cell.value;
|
|
2666
|
-
});
|
|
2667
|
-
if (JSON.stringify(next.series) !== JSON.stringify(this.spec.series)) this.commit(_nullishCoalesce(options.label, () => ( "Edit values")), next);
|
|
2668
|
-
return { ok: true };
|
|
2669
|
-
}
|
|
2670
|
-
/** Adds a series (values padded to the categories) and selects it. Returns its id. */
|
|
2671
|
-
addSeries(input = {}, options = {}) {
|
|
2672
|
-
const id = input.id && !this.getSeries(input.id) ? input.id : _chunkNEOO6QJDcjs.uniqueChartSeriesId.call(void 0, this.spec.series);
|
|
2673
|
-
const name = _nullishCoalesce(input.name, () => ( `Series ${this.spec.series.length + 1}`));
|
|
2674
|
-
const at = Math.max(0, Math.min(this.spec.series.length, _nullishCoalesce(options.at, () => ( this.spec.series.length))));
|
|
2675
|
-
const series = [...this.spec.series];
|
|
2676
|
-
series.splice(at, 0, { ...input, id, name, values: _nullishCoalesce(input.values, () => ( [])) });
|
|
2677
|
-
const { spec, issues } = _chunkNEOO6QJDcjs.normalizeChartSpec.call(void 0, { ...this.spec, series });
|
|
2678
|
-
if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
2679
|
-
this.commit(`Add series "${name}"`, spec, { kind: "series", seriesId: id });
|
|
2680
|
-
return { ok: true, id };
|
|
2681
|
-
}
|
|
2682
|
-
removeSeries(id) {
|
|
2683
|
-
const series = this.getSeries(id);
|
|
2684
|
-
if (!series) return this.unknownSeries(id);
|
|
2685
|
-
this.commit(
|
|
2686
|
-
`Delete series "${series.name}"`,
|
|
2687
|
-
this.edited((d) => {
|
|
2688
|
-
d.series = d.series.filter((s) => s.id !== id);
|
|
2689
|
-
})
|
|
2690
|
-
);
|
|
2691
|
-
return { ok: true };
|
|
2692
|
-
}
|
|
2693
|
-
updateSeries(id, patch) {
|
|
2694
|
-
const series = this.getSeries(id);
|
|
2695
|
-
if (!series) return this.unknownSeries(id);
|
|
2696
|
-
const next = _chunkNEOO6QJDcjs.cloneChartSeries.call(void 0, series);
|
|
2697
|
-
if (patch.name !== void 0) next.name = String(patch.name);
|
|
2698
|
-
if (patch.color === null || patch.color === "") delete next.color;
|
|
2699
|
-
else if (patch.color !== void 0) next.color = patch.color;
|
|
2700
|
-
if (patch.type === null) delete next.type;
|
|
2701
|
-
else if (patch.type !== void 0) next.type = patch.type;
|
|
2702
|
-
if (patch.hidden !== void 0) {
|
|
2703
|
-
if (patch.hidden) next.hidden = true;
|
|
2704
|
-
else delete next.hidden;
|
|
2705
|
-
}
|
|
2706
|
-
const resettable = ["axis", "x", "sizes", "pointColors", "marker", "curve", "dataLabels", "format"];
|
|
2707
|
-
for (const key of resettable) {
|
|
2708
|
-
if (!(key in patch) || patch[key] === void 0) continue;
|
|
2709
|
-
if (patch[key] === null) delete next[key];
|
|
2710
|
-
else next[key] = patch[key];
|
|
2711
|
-
}
|
|
2712
|
-
if (next.axis === "primary") delete next.axis;
|
|
2713
|
-
const series2 = this.spec.series.map((s) => s.id === id ? next : s);
|
|
2714
|
-
const { spec, issues } = _chunkNEOO6QJDcjs.normalizeChartSpec.call(void 0, { ...this.spec, series: series2 });
|
|
2715
|
-
if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
2716
|
-
if (JSON.stringify(spec.series) === JSON.stringify(this.spec.series)) return { ok: true };
|
|
2717
|
-
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}"`;
|
|
2718
|
-
this.commit(label, spec);
|
|
2719
|
-
return { ok: true };
|
|
2720
|
-
}
|
|
2721
|
-
toggleSeries(id) {
|
|
2722
|
-
const series = this.getSeries(id);
|
|
2723
|
-
if (!series) return this.unknownSeries(id);
|
|
2724
|
-
return this.updateSeries(id, { hidden: !series.hidden });
|
|
2725
|
-
}
|
|
2726
|
-
/** Moves a series to position `to` (drawing and legend order). */
|
|
2727
|
-
moveSeries(id, to) {
|
|
2728
|
-
const from = this.spec.series.findIndex((s) => s.id === id);
|
|
2729
|
-
if (from < 0) return this.unknownSeries(id);
|
|
2730
|
-
const target = Math.max(0, Math.min(this.spec.series.length - 1, to));
|
|
2731
|
-
if (target === from) return { ok: true };
|
|
2732
|
-
this.commit(
|
|
2733
|
-
"Reorder series",
|
|
2734
|
-
this.edited((d) => {
|
|
2735
|
-
const [s] = d.series.splice(from, 1);
|
|
2736
|
-
d.series.splice(target, 0, s);
|
|
2737
|
-
})
|
|
2738
|
-
);
|
|
2739
|
-
return { ok: true };
|
|
2740
|
-
}
|
|
2741
|
-
/** Inserts a category (with gaps in every series) at `at` (default: the end). Returns its index. */
|
|
2742
|
-
addCategory(label, options = {}) {
|
|
2743
|
-
const at = Math.max(0, Math.min(this.spec.categories.length, _nullishCoalesce(options.at, () => ( this.spec.categories.length))));
|
|
2744
|
-
const name = _nullishCoalesce(label, () => ( `${this.spec.categories.length + 1}`));
|
|
2745
|
-
this.commit(
|
|
2746
|
-
`Add category "${name}"`,
|
|
2747
|
-
this.edited((d) => {
|
|
2748
|
-
d.categories.splice(at, 0, name);
|
|
2749
|
-
for (const s of d.series) s.values.splice(at, 0, null);
|
|
2750
|
-
}),
|
|
2751
|
-
{ kind: "category", index: at }
|
|
2752
|
-
);
|
|
2753
|
-
return { ok: true, index: at };
|
|
2754
|
-
}
|
|
2755
|
-
removeCategory(index) {
|
|
2756
|
-
if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
|
|
2757
|
-
this.commit(
|
|
2758
|
-
`Delete category "${this.spec.categories[index]}"`,
|
|
2759
|
-
this.edited((d) => {
|
|
2760
|
-
d.categories.splice(index, 1);
|
|
2761
|
-
for (const s of d.series) s.values.splice(index, 1);
|
|
2762
|
-
})
|
|
2763
|
-
);
|
|
2764
|
-
return { ok: true };
|
|
2765
|
-
}
|
|
2766
|
-
renameCategory(index, label) {
|
|
2767
|
-
if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
|
|
2768
|
-
if (this.spec.categories[index] === label) return { ok: true };
|
|
2769
|
-
this.commit(
|
|
2770
|
-
"Rename category",
|
|
2771
|
-
this.edited((d) => {
|
|
2772
|
-
d.categories[index] = String(label);
|
|
2773
|
-
})
|
|
2774
|
-
);
|
|
2775
|
-
return { ok: true };
|
|
2776
|
-
}
|
|
2777
|
-
moveCategory(from, to) {
|
|
2778
|
-
if (!Number.isInteger(from) || from < 0 || from >= this.spec.categories.length) return this.badIndex(from);
|
|
2779
|
-
const target = Math.max(0, Math.min(this.spec.categories.length - 1, to));
|
|
2780
|
-
if (target === from) return { ok: true };
|
|
2781
|
-
const move = (list) => {
|
|
2782
|
-
const [item] = list.splice(from, 1);
|
|
2783
|
-
list.splice(target, 0, item);
|
|
2784
|
-
};
|
|
2785
|
-
this.commit(
|
|
2786
|
-
"Reorder categories",
|
|
2787
|
-
this.edited((d) => {
|
|
2788
|
-
move(d.categories);
|
|
2789
|
-
for (const s of d.series) move(s.values);
|
|
2790
|
-
})
|
|
2791
|
-
);
|
|
2792
|
-
return { ok: true };
|
|
2793
|
-
}
|
|
2794
|
-
/**
|
|
2795
|
-
* Pastes a block of cells (TSV / CSV text or rows) into the data grid with its top-left at
|
|
2796
|
-
* `row` (category index) and `column` (series index; `-1` is the category column). The
|
|
2797
|
-
* grid grows to fit. Non-numeric value cells become gaps and are reported in `issues`.
|
|
2798
|
-
*/
|
|
2799
|
-
pasteCells(row, column, cells) {
|
|
2800
|
-
const rows = typeof cells === "string" ? cells.includes(" ") ? _chunkTNNJXF46cjs.parseTsv.call(void 0, cells) : _chunkTNNJXF46cjs.parseCsv.call(void 0, cells) : cells.map((r) => [...r]);
|
|
2801
|
-
if (!rows.length) return { ok: true };
|
|
2802
|
-
const issues = [];
|
|
2803
|
-
const next = this.edited((d) => {
|
|
2804
|
-
const width = Math.max(...rows.map((r) => r.length));
|
|
2805
|
-
const lastSeries = column + width - 1;
|
|
2806
|
-
while (d.series.length <= lastSeries) {
|
|
2807
|
-
const id = _chunkNEOO6QJDcjs.uniqueChartSeriesId.call(void 0, d.series);
|
|
2808
|
-
d.series.push({ id, name: `Series ${d.series.length + 1}`, values: d.categories.map(() => null) });
|
|
2809
|
-
}
|
|
2810
|
-
const lastRow = row + rows.length - 1;
|
|
2811
|
-
while (d.categories.length <= lastRow) {
|
|
2812
|
-
d.categories.push(`${d.categories.length + 1}`);
|
|
2813
|
-
for (const s of d.series) s.values.push(null);
|
|
2814
|
-
}
|
|
2815
|
-
rows.forEach((cellsInRow, r) => {
|
|
2816
|
-
cellsInRow.forEach((cell, c) => {
|
|
2817
|
-
const col = column + c;
|
|
2818
|
-
const index = row + r;
|
|
2819
|
-
if (col < 0) d.categories[index] = cell === null || cell === void 0 ? "" : String(cell).trim();
|
|
2820
|
-
else {
|
|
2821
|
-
const parsed = _chunkNEOO6QJDcjs.parseChartValue.call(void 0, cell);
|
|
2822
|
-
if (parsed === void 0) issues.push({ path: `${d.series[col].id}[${index}]`, message: `"${String(cell)}" is not a number; it was left blank.` });
|
|
2823
|
-
d.series[col].values[index] = _nullishCoalesce(parsed, () => ( null));
|
|
2824
|
-
}
|
|
2825
|
-
});
|
|
2826
|
-
});
|
|
2827
|
-
});
|
|
2828
|
-
this.commit("Paste cells", next);
|
|
2829
|
-
if (issues.length) {
|
|
2830
|
-
this.issues = issues;
|
|
2831
|
-
this.notify(false);
|
|
2832
|
-
}
|
|
2833
|
-
return issues.length ? { ok: true, issues } : { ok: true };
|
|
2834
|
-
}
|
|
2835
|
-
/**
|
|
2836
|
-
* Replaces the data with a grid of cells (see `chartDataFromTable`): one undo step. Series
|
|
2837
|
-
* colours and types are kept by name.
|
|
2838
|
-
*/
|
|
2839
|
-
importTable(rows, options = {}) {
|
|
2840
|
-
const data = _chunkNEOO6QJDcjs.chartDataFromTable.call(void 0, rows, options);
|
|
2841
|
-
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.");
|
|
2842
|
-
const byName = new Map(this.spec.series.map((s) => [s.name, s]));
|
|
2843
|
-
const series = data.series.map((s) => {
|
|
2844
|
-
const old = byName.get(s.name);
|
|
2845
|
-
return { ...s, ..._optionalChain([old, 'optionalAccess', _58 => _58.color]) ? { color: old.color } : {}, ..._optionalChain([old, 'optionalAccess', _59 => _59.type]) ? { type: old.type } : {}, ...old ? { id: old.id } : {} };
|
|
2846
|
-
});
|
|
2847
|
-
const { spec } = _chunkNEOO6QJDcjs.normalizeChartSpec.call(void 0, { ...this.spec, categories: data.categories, series });
|
|
2848
|
-
this.commit("Import data", spec);
|
|
2849
|
-
if (data.issues.length) {
|
|
2850
|
-
this.issues = data.issues;
|
|
2851
|
-
this.notify(false);
|
|
2852
|
-
return { ok: true, issues: data.issues };
|
|
2853
|
-
}
|
|
2854
|
-
return { ok: true };
|
|
2855
|
-
}
|
|
2856
|
-
/** Imports CSV (or TSV, detected by tabs) text; see `importTable`. */
|
|
2857
|
-
importCsv(text, options = {}) {
|
|
2858
|
-
const delimiter = _nullishCoalesce(options.delimiter, () => ( (text.includes(" ") ? " " : ",")));
|
|
2859
|
-
return this.importTable(_chunkTNNJXF46cjs.parseCsv.call(void 0, text, { delimiter }), options);
|
|
2860
|
-
}
|
|
2861
|
-
// -------------------------------------------------------------------------
|
|
2862
|
-
// Trendlines
|
|
2863
|
-
// -------------------------------------------------------------------------
|
|
2864
|
-
/** Adds a trendline to a series and selects it. Returns its index (and the fit, when it can be computed). */
|
|
2865
|
-
addTrendline(seriesId, input) {
|
|
2866
|
-
const series = this.getSeries(seriesId);
|
|
2867
|
-
if (!series) return this.unknownSeries(seriesId);
|
|
2868
|
-
if (!chartSupportsTrendlines(this.spec.type, this.spec.stacking))
|
|
2869
|
-
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.`);
|
|
2870
|
-
const issues = [];
|
|
2871
|
-
const line = _chunkNEOO6QJDcjs.normalizeChartTrendline.call(void 0, input, "trendline", issues);
|
|
2872
|
-
if (!line || issues.length) return this.reject(issues.map((i) => i.message).join(" ") || "The trendline is not valid.", issues);
|
|
2873
|
-
const index = _nullishCoalesce(_optionalChain([series, 'access', _60 => _60.trendlines, 'optionalAccess', _61 => _61.length]), () => ( 0));
|
|
2874
|
-
const next = this.edited((d) => {
|
|
2875
|
-
const s = d.series.find((x) => x.id === seriesId);
|
|
2876
|
-
s.trendlines = [..._nullishCoalesce(s.trendlines, () => ( [])), line];
|
|
2877
|
-
});
|
|
2878
|
-
this.commit(`Add trendline to "${series.name}"`, next, { kind: "trendline", seriesId, index });
|
|
2879
|
-
return { ok: true, index, fit: this.trendlineFit(seriesId, index) };
|
|
2880
|
-
}
|
|
2881
|
-
/** Changes a trendline (all-or-nothing). */
|
|
2882
|
-
updateTrendline(seriesId, index, patch) {
|
|
2883
|
-
const series = this.getSeries(seriesId);
|
|
2884
|
-
if (!series) return this.unknownSeries(seriesId);
|
|
2885
|
-
const current = _optionalChain([series, 'access', _62 => _62.trendlines, 'optionalAccess', _63 => _63[index]]);
|
|
2886
|
-
if (!current) return fail(`"${series.name}" has no trendline ${index}; ${_optionalChain([series, 'access', _64 => _64.trendlines, 'optionalAccess', _65 => _65.length]) ? `expected 0 to ${series.trendlines.length - 1}` : "it has none yet"}.`);
|
|
2887
|
-
const issues = [];
|
|
2888
|
-
const merged = { ...current, ...patch };
|
|
2889
|
-
for (const [k, v] of Object.entries(patch)) if (v === null) delete merged[k];
|
|
2890
|
-
const line = _chunkNEOO6QJDcjs.normalizeChartTrendline.call(void 0, merged, "trendline", issues);
|
|
2891
|
-
if (!line || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
2892
|
-
this.commit(
|
|
2893
|
-
"Change trendline",
|
|
2894
|
-
this.edited((d) => {
|
|
2895
|
-
d.series.find((x) => x.id === seriesId).trendlines[index] = line;
|
|
2896
|
-
})
|
|
2897
|
-
);
|
|
2898
|
-
return { ok: true };
|
|
2899
|
-
}
|
|
2900
|
-
removeTrendline(seriesId, index) {
|
|
2901
|
-
const series = this.getSeries(seriesId);
|
|
2902
|
-
if (!series) return this.unknownSeries(seriesId);
|
|
2903
|
-
if (!_optionalChain([series, 'access', _66 => _66.trendlines, 'optionalAccess', _67 => _67[index]])) return fail(`"${series.name}" has no trendline ${index}.`);
|
|
2904
|
-
this.commit(
|
|
2905
|
-
"Delete trendline",
|
|
2906
|
-
this.edited((d) => {
|
|
2907
|
-
const s = d.series.find((x) => x.id === seriesId);
|
|
2908
|
-
s.trendlines = s.trendlines.filter((_, i) => i !== index);
|
|
2909
|
-
if (!s.trendlines.length) delete s.trendlines;
|
|
2910
|
-
})
|
|
2911
|
-
);
|
|
2912
|
-
return { ok: true };
|
|
2913
|
-
}
|
|
2914
|
-
/** The points a series' trendlines are fitted to: (x, y) with x = 1, 2, 3 … on category charts. */
|
|
2915
|
-
trendlinePoints(seriesId) {
|
|
2916
|
-
const series = this.getSeries(seriesId);
|
|
2917
|
-
if (!series) return [];
|
|
2918
|
-
const xy = _chunkNEOO6QJDcjs.chartFamily.call(void 0, this.spec) === "xy";
|
|
2919
|
-
const out = [];
|
|
2920
|
-
series.values.forEach((v, i) => {
|
|
2921
|
-
const x = xy ? _nullishCoalesce(_optionalChain([series, 'access', _68 => _68.x, 'optionalAccess', _69 => _69[i]]), () => ( (this.spec.type === "bubble" ? i + 1 : null))) : i + 1;
|
|
2922
|
-
if (v !== null && x !== null && Number.isFinite(v) && Number.isFinite(x)) out.push([x, v]);
|
|
2923
|
-
});
|
|
2924
|
-
return out;
|
|
2925
|
-
}
|
|
2926
|
-
/** The fitted coefficients, R² and equation of a trendline (null for moving averages or unfittable data). */
|
|
2927
|
-
trendlineFit(seriesId, index) {
|
|
2928
|
-
const line = _optionalChain([this, 'access', _70 => _70.getSeries, 'call', _71 => _71(seriesId), 'optionalAccess', _72 => _72.trendlines, 'optionalAccess', _73 => _73[index]]);
|
|
2929
|
-
if (!line) return null;
|
|
2930
|
-
return fitChartTrendline(line, this.trendlinePoints(seriesId));
|
|
2931
|
-
}
|
|
2932
|
-
/** The moving-average points of a trendline (empty for other kinds). */
|
|
2933
|
-
trendlineMovingAverage(seriesId, index) {
|
|
2934
|
-
const line = _optionalChain([this, 'access', _74 => _74.getSeries, 'call', _75 => _75(seriesId), 'optionalAccess', _76 => _76.trendlines, 'optionalAccess', _77 => _77[index]]);
|
|
2935
|
-
if (!line || line.type !== "moving-average") return [];
|
|
2936
|
-
return chartMovingAverage(this.trendlinePoints(seriesId), _nullishCoalesce(line.period, () => ( 2)));
|
|
2937
|
-
}
|
|
2938
|
-
// -------------------------------------------------------------------------
|
|
2939
|
-
// Reshaping
|
|
2940
|
-
// -------------------------------------------------------------------------
|
|
2941
|
-
/**
|
|
2942
|
-
* Switches rows and columns: series become categories and categories become series (one
|
|
2943
|
-
* undo step). Series colours, types and other per-series settings are dropped.
|
|
2944
|
-
*/
|
|
2945
|
-
transpose() {
|
|
2946
|
-
const s = this.spec;
|
|
2947
|
-
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.");
|
|
2948
|
-
const used = /* @__PURE__ */ new Set();
|
|
2949
|
-
const series = s.categories.map((name, i) => {
|
|
2950
|
-
let id = `series-${i + 1}`;
|
|
2951
|
-
for (let k = i + 2; used.has(id); k++) id = `series-${k}`;
|
|
2952
|
-
used.add(id);
|
|
2953
|
-
return { id, name, values: s.series.map((x) => _nullishCoalesce(x.values[i], () => ( null))) };
|
|
2954
|
-
});
|
|
2955
|
-
const { spec } = _chunkNEOO6QJDcjs.normalizeChartSpec.call(void 0, { ...s, categories: s.series.map((x) => x.name), series });
|
|
2956
|
-
this.commit("Switch rows and columns", spec, null);
|
|
2957
|
-
return { ok: true };
|
|
2958
|
-
}
|
|
2959
|
-
// -------------------------------------------------------------------------
|
|
2960
|
-
// Selection and gestures
|
|
2961
|
-
// -------------------------------------------------------------------------
|
|
2962
|
-
/** Selects a chart element (view state; not an undo step). */
|
|
2963
|
-
select(selection) {
|
|
2964
|
-
const next = this.validSelection(selection, this.spec);
|
|
2965
|
-
if (sameChartSelection(next, this.selection) || next === null && this.selection === null) return;
|
|
2966
|
-
this.selection = next;
|
|
2967
|
-
this.notify(false);
|
|
2968
|
-
}
|
|
2969
|
-
/**
|
|
2970
|
-
* Deletes what is selected: a series or category is removed, a point becomes a gap, a
|
|
2971
|
-
* title is cleared, the legend or an axis is hidden.
|
|
2972
|
-
*/
|
|
2973
|
-
deleteSelection() {
|
|
2974
|
-
const sel = this.selection;
|
|
2975
|
-
if (!sel) return fail("Nothing is selected.");
|
|
2976
|
-
switch (sel.kind) {
|
|
2977
|
-
case "series":
|
|
2978
|
-
return this.removeSeries(sel.seriesId);
|
|
2979
|
-
case "category":
|
|
2980
|
-
return this.removeCategory(sel.index);
|
|
2981
|
-
case "point":
|
|
2982
|
-
return this.setValue(sel.seriesId, sel.index, null, { label: "Clear value" });
|
|
2983
|
-
case "title":
|
|
2984
|
-
return this.setTitle("");
|
|
2985
|
-
case "subtitle":
|
|
2986
|
-
return this.setSubtitle("");
|
|
2987
|
-
case "legend":
|
|
2988
|
-
return this.setLegend({ show: false });
|
|
2989
|
-
case "axis":
|
|
2990
|
-
return this.setAxis(sel.axis, { show: false });
|
|
2991
|
-
case "trendline":
|
|
2992
|
-
return this.removeTrendline(sel.seriesId, sel.index);
|
|
2993
|
-
default:
|
|
2994
|
-
return fail("The chart itself cannot be deleted.");
|
|
2995
|
-
}
|
|
2996
|
-
}
|
|
2997
|
-
/** Adds `delta` to a value (a gap counts as 0). */
|
|
2998
|
-
nudgeValue(seriesId, index, delta) {
|
|
2999
|
-
const series = this.getSeries(seriesId);
|
|
3000
|
-
if (!series) return this.unknownSeries(seriesId);
|
|
3001
|
-
const current = _nullishCoalesce(series.values[index], () => ( 0));
|
|
3002
|
-
return this.setValue(seriesId, index, Number((current + delta).toPrecision(12)), { label: `Edit ${series.name} \xB7 ${_nullishCoalesce(this.spec.categories[index], () => ( index))}` });
|
|
3003
|
-
}
|
|
3004
|
-
/** Starts a continuous edit (a drag): every change until `endGesture()` is one undo step. */
|
|
3005
|
-
beginGesture(key) {
|
|
3006
|
-
if (this.gestureKey === key) return;
|
|
3007
|
-
this.history.endGesture();
|
|
3008
|
-
this.gestureKey = key;
|
|
3009
|
-
this.gestureCount++;
|
|
3010
|
-
this.gestureStart = { spec: this.spec, mark: this.history.mark() };
|
|
3011
|
-
this.notify(false);
|
|
3012
|
-
}
|
|
3013
|
-
/**
|
|
3014
|
-
* Cancels the gesture in progress (Escape during a drag): everything it changed is taken
|
|
3015
|
-
* back and leaves no undo or redo step.
|
|
3016
|
-
*/
|
|
3017
|
-
cancelGesture() {
|
|
3018
|
-
if (this.gestureKey === null) return;
|
|
3019
|
-
const start = this.gestureStart;
|
|
3020
|
-
this.gestureKey = null;
|
|
3021
|
-
this.gestureStart = null;
|
|
3022
|
-
this.history.endGesture();
|
|
3023
|
-
if (start) this.history.revertTo(start.mark);
|
|
3024
|
-
this.notify(false);
|
|
3025
|
-
}
|
|
3026
|
-
endGesture() {
|
|
3027
|
-
if (this.gestureKey === null) return;
|
|
3028
|
-
this.gestureKey = null;
|
|
3029
|
-
this.history.endGesture();
|
|
3030
|
-
const start = this.gestureStart;
|
|
3031
|
-
this.gestureStart = null;
|
|
3032
|
-
if (start && sameChartJson(start.spec, this.spec)) this.history.revertTo(start.mark);
|
|
3033
|
-
this.notify(false);
|
|
3034
|
-
}
|
|
3035
|
-
// -------------------------------------------------------------------------
|
|
3036
|
-
// Persistence and history
|
|
3037
|
-
// -------------------------------------------------------------------------
|
|
3038
|
-
/** The spec, for saving (a copy). */
|
|
3039
|
-
toJSON() {
|
|
3040
|
-
return _chunkNEOO6QJDcjs.cloneChartSpec.call(void 0, this.spec);
|
|
3041
|
-
}
|
|
3042
|
-
/**
|
|
3043
|
-
* Replaces the chart without an undo step (e.g. when opening a document). Problems are
|
|
3044
|
-
* fixed with defaults and reported in `state.issues` (and returned).
|
|
3045
|
-
*/
|
|
3046
|
-
load(input, options = {}) {
|
|
3047
|
-
const { spec, issues } = _chunkNEOO6QJDcjs.normalizeChartSpec.call(void 0, input);
|
|
3048
|
-
this.spec = spec;
|
|
3049
|
-
this.issues = issues;
|
|
3050
|
-
this.selection = this.validSelection(this.selection, spec);
|
|
3051
|
-
if (options.clearHistory) {
|
|
3052
|
-
this.applying = true;
|
|
3053
|
-
try {
|
|
3054
|
-
this.history.clear();
|
|
3055
|
-
} finally {
|
|
3056
|
-
this.applying = false;
|
|
3057
|
-
}
|
|
3058
|
-
}
|
|
3059
|
-
this.notify(true);
|
|
3060
|
-
return issues;
|
|
3061
|
-
}
|
|
3062
|
-
undo() {
|
|
3063
|
-
this.endGesture();
|
|
3064
|
-
return this.history.undo();
|
|
3065
|
-
}
|
|
3066
|
-
redo() {
|
|
3067
|
-
this.endGesture();
|
|
3068
|
-
return this.history.redo();
|
|
3069
|
-
}
|
|
3070
|
-
/** Stops listening to the history (reconnected by the next `subscribe`). */
|
|
3071
|
-
destroy() {
|
|
3072
|
-
_optionalChain([this, 'access', _78 => _78.unsubscribeHistory, 'optionalCall', _79 => _79()]);
|
|
3073
|
-
this.unsubscribeHistory = void 0;
|
|
3074
|
-
this.listeners.clear();
|
|
3075
|
-
}
|
|
3076
|
-
}, _class);
|
|
3077
|
-
function createChartController(options = {}) {
|
|
3078
|
-
return new ChartController(options);
|
|
3079
|
-
}
|
|
3080
|
-
function updateLabel(patch) {
|
|
3081
|
-
const keys = Object.keys(patch);
|
|
3082
|
-
if (keys.length !== 1) return "Change chart";
|
|
3083
|
-
const labels = {
|
|
3084
|
-
type: "Change chart type",
|
|
3085
|
-
title: "Edit title",
|
|
3086
|
-
subtitle: "Edit subtitle",
|
|
3087
|
-
categories: "Edit categories",
|
|
3088
|
-
series: "Edit series",
|
|
3089
|
-
xAxis: "Change category axis",
|
|
3090
|
-
yAxis: "Change value axis",
|
|
3091
|
-
yAxis2: "Change secondary value axis",
|
|
3092
|
-
labelPosition: "Change label position",
|
|
3093
|
-
gapWidth: "Change bar spacing",
|
|
3094
|
-
overlap: "Change bar overlap",
|
|
3095
|
-
bins: "Change histogram bins",
|
|
3096
|
-
showTotal: "Toggle waterfall total",
|
|
3097
|
-
legend: "Change legend",
|
|
3098
|
-
stacking: "Change stacking",
|
|
3099
|
-
orientation: "Change orientation",
|
|
3100
|
-
curve: "Change line style",
|
|
3101
|
-
points: "Toggle points",
|
|
3102
|
-
dataLabels: "Toggle data labels",
|
|
3103
|
-
palette: "Change colours",
|
|
3104
|
-
innerRadius: "Change donut hole"
|
|
3105
|
-
};
|
|
3106
|
-
return _nullishCoalesce(labels[keys[0]], () => ( "Change chart"));
|
|
3107
|
-
}
|
|
3108
|
-
|
|
3109
|
-
// src/chart/mermaid.ts
|
|
3110
|
-
var CHART_MERMAID_META_PREFIX = "%% @pixerate ";
|
|
3111
|
-
var quote = (text) => `"${text.replace(/"/g, "#quot;")}"`;
|
|
3112
|
-
var unquote = (text) => {
|
|
3113
|
-
const t = text.trim();
|
|
3114
|
-
return (t.length >= 2 && t.startsWith('"') && t.endsWith('"') ? t.slice(1, -1) : t).replace(/#quot;/g, '"');
|
|
3115
|
-
};
|
|
3116
|
-
var word = (text) => /^[A-Za-z0-9_]+$/.test(text) ? text : quote(text);
|
|
3117
|
-
var num = (value) => Number.isInteger(value) ? String(value) : String(Number(value.toPrecision(10)));
|
|
3118
|
-
function splitOutside(text, sep) {
|
|
3119
|
-
const out = [];
|
|
3120
|
-
let cur = "";
|
|
3121
|
-
let quoted = false;
|
|
3122
|
-
let depth = 0;
|
|
3123
|
-
for (const ch of text) {
|
|
3124
|
-
if (ch === '"') quoted = !quoted;
|
|
3125
|
-
else if (!quoted && (ch === "[" || ch === "{")) depth++;
|
|
3126
|
-
else if (!quoted && (ch === "]" || ch === "}")) depth--;
|
|
3127
|
-
if (ch === sep && !quoted && depth === 0) {
|
|
3128
|
-
out.push(cur);
|
|
3129
|
-
cur = "";
|
|
3130
|
-
} else cur += ch;
|
|
3131
|
-
}
|
|
3132
|
-
out.push(cur);
|
|
3133
|
-
return out;
|
|
3134
|
-
}
|
|
3135
|
-
function chartMermaidFamily(spec) {
|
|
3136
|
-
const family = _chunkNEOO6QJDcjs.chartFamily.call(void 0, spec);
|
|
3137
|
-
if (family === "pie" || family === "funnel" || family === "treemap") return "pie";
|
|
3138
|
-
if (family === "radar") return "radar";
|
|
3139
|
-
return "xychart";
|
|
3140
|
-
}
|
|
3141
|
-
function slug(label, i, taken) {
|
|
3142
|
-
let base = label.normalize("NFKD").replace(/[^\w-]+/g, "_").replace(/^_+|_+$/g, "") || `a${i + 1}`;
|
|
3143
|
-
if (!/^[A-Za-z_]/.test(base)) base = `a_${base}`;
|
|
3144
|
-
let id = base;
|
|
3145
|
-
for (let k = 2; taken.has(id); k++) id = `${base}_${k}`;
|
|
3146
|
-
taken.add(id);
|
|
3147
|
-
return id;
|
|
3148
|
-
}
|
|
3149
|
-
function metaFor(spec, text) {
|
|
3150
|
-
const base = chartFromMermaid(text, { ignoreMeta: true }).spec;
|
|
3151
|
-
const meta = {};
|
|
3152
|
-
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
3153
|
-
for (const key of Object.keys(spec)) {
|
|
3154
|
-
if (key === "series" || key === "version") continue;
|
|
3155
|
-
if (!same(spec[key], base[key])) meta[key] = spec[key];
|
|
3156
|
-
}
|
|
3157
|
-
const series = spec.series.map((s, i) => {
|
|
3158
|
-
const b = base.series[i];
|
|
3159
|
-
const out = {};
|
|
3160
|
-
for (const [k, v] of Object.entries(s)) {
|
|
3161
|
-
if (k === "values") {
|
|
3162
|
-
if (b && !same(v, b.values)) out.values = v;
|
|
3163
|
-
continue;
|
|
3164
|
-
}
|
|
3165
|
-
if (!b || !same(v, b[k])) out[k] = v;
|
|
3166
|
-
}
|
|
3167
|
-
return out;
|
|
3168
|
-
});
|
|
3169
|
-
if (series.some((s) => Object.keys(s).length) || spec.series.length !== base.series.length) meta.series = series;
|
|
3170
|
-
return Object.keys(meta).length ? meta : null;
|
|
3171
|
-
}
|
|
3172
|
-
function chartToMermaid(spec, options = {}) {
|
|
3173
|
-
const ind = _nullishCoalesce(options.indent, () => ( " "));
|
|
3174
|
-
const family = chartMermaidFamily(spec);
|
|
3175
|
-
const visible = spec.series.filter((s) => !s.hidden);
|
|
3176
|
-
const lines = [];
|
|
3177
|
-
const v = (x) => x === null || !Number.isFinite(x) ? "0" : num(x);
|
|
3178
|
-
if (family === "pie") {
|
|
3179
|
-
const s = _nullishCoalesce(visible[0], () => ( spec.series[0]));
|
|
3180
|
-
lines.push(`pie${spec.dataLabels ? " showData" : ""}`);
|
|
3181
|
-
if (spec.title) lines.push(`${ind}title ${spec.title}`);
|
|
3182
|
-
spec.categories.forEach((c, i) => {
|
|
3183
|
-
const value = _optionalChain([s, 'optionalAccess', _80 => _80.values, 'access', _81 => _81[i]]);
|
|
3184
|
-
if (value !== null && value !== void 0 && Number.isFinite(value) && value > 0) lines.push(`${ind}${quote(c)} : ${num(value)}`);
|
|
3185
|
-
});
|
|
3186
|
-
} else if (family === "radar") {
|
|
3187
|
-
lines.push("radar-beta");
|
|
3188
|
-
if (spec.title) lines.push(`${ind}title ${spec.title}`);
|
|
3189
|
-
const taken = /* @__PURE__ */ new Set();
|
|
3190
|
-
const ids = spec.categories.map((c, i) => slug(c, i, taken));
|
|
3191
|
-
lines.push(`${ind}axis ${ids.map((id, i) => id === spec.categories[i] ? id : `${id}["${spec.categories[i].replace(/"/g, "'")}"]`).join(", ")}`);
|
|
3192
|
-
const seriesTaken = new Set(ids);
|
|
3193
|
-
for (const [i, s] of spec.series.entries()) {
|
|
3194
|
-
const id = slug(s.id, i, seriesTaken);
|
|
3195
|
-
lines.push(`${ind}curve ${id}["${s.name.replace(/"/g, "'")}"]{${s.values.map(v).join(", ")}}`);
|
|
3196
|
-
}
|
|
3197
|
-
if (spec.yAxis.max !== null) lines.push(`${ind}max ${num(spec.yAxis.max)}`);
|
|
3198
|
-
if (spec.yAxis.min !== null) lines.push(`${ind}min ${num(spec.yAxis.min)}`);
|
|
3199
|
-
if (!spec.legend.show) lines.push(`${ind}showLegend false`);
|
|
3200
|
-
} else {
|
|
3201
|
-
lines.push(`xychart-beta${spec.orientation === "horizontal" ? " horizontal" : ""}`);
|
|
3202
|
-
if (spec.title) lines.push(`${ind}title ${quote(spec.title)}`);
|
|
3203
|
-
const xTitle = spec.xAxis.title ? ` ${quote(spec.xAxis.title)}` : "";
|
|
3204
|
-
lines.push(`${ind}x-axis${xTitle} [${spec.categories.map(word).join(", ")}]`);
|
|
3205
|
-
const y = spec.yAxis;
|
|
3206
|
-
if (y.title || y.min !== null || y.max !== null) {
|
|
3207
|
-
let out = `${ind}y-axis`;
|
|
3208
|
-
if (y.title) out += ` ${quote(y.title)}`;
|
|
3209
|
-
if (y.min !== null || y.max !== null) {
|
|
3210
|
-
const all = spec.series.flatMap((s) => s.values).filter((x) => x !== null && Number.isFinite(x));
|
|
3211
|
-
const lo = _nullishCoalesce(y.min, () => ( Math.min(0, ...all)));
|
|
3212
|
-
const hi = _nullishCoalesce(y.max, () => ( Math.max(lo + 1, ...all)));
|
|
3213
|
-
out += ` ${num(lo)} --> ${num(hi)}`;
|
|
3214
|
-
}
|
|
3215
|
-
lines.push(out);
|
|
3216
|
-
}
|
|
3217
|
-
for (const s of spec.series) {
|
|
3218
|
-
const kind = (_nullishCoalesce(s.type, () => ( (spec.type === "bar" ? "bar" : "line")))) === "bar" ? "bar" : "line";
|
|
3219
|
-
lines.push(`${ind}${kind} ${quote(s.name)} [${s.values.map(v).join(", ")}]`);
|
|
3220
|
-
}
|
|
3221
|
-
}
|
|
3222
|
-
let text = `${lines.join("\n")}
|
|
3223
|
-
`;
|
|
3224
|
-
if (options.meta !== false) {
|
|
3225
|
-
const meta = metaFor(spec, text);
|
|
3226
|
-
if (meta) {
|
|
3227
|
-
const [header, ...rest] = lines;
|
|
3228
|
-
text = `${[header, `${CHART_MERMAID_META_PREFIX}${JSON.stringify({ v: 1, ...meta })}`, ...rest].join("\n")}
|
|
3229
|
-
`;
|
|
3230
|
-
}
|
|
3231
|
-
}
|
|
3232
|
-
return text;
|
|
3233
|
-
}
|
|
3234
|
-
var RANGE = /^(-?\d+(?:\.\d+)?)\s*-->\s*(-?\d+(?:\.\d+)?)$/;
|
|
3235
|
-
function takeTitle(text) {
|
|
3236
|
-
const t = text.trim();
|
|
3237
|
-
if (t.startsWith('"')) {
|
|
3238
|
-
const end = t.indexOf('"', 1);
|
|
3239
|
-
if (end > 0) return { title: unquote(t.slice(0, end + 1)), rest: t.slice(end + 1).trim() };
|
|
3240
|
-
}
|
|
3241
|
-
if (!t || t.startsWith("[") || RANGE.test(t) || /^-?\d/.test(t)) return { rest: t };
|
|
3242
|
-
const m = /^(\S+)\s*(.*)$/.exec(t);
|
|
3243
|
-
return { title: m[1], rest: m[2].trim() };
|
|
3244
|
-
}
|
|
3245
|
-
function parseList(text, partial) {
|
|
3246
|
-
const t = text.trim();
|
|
3247
|
-
let m = /^\[(.*)\]$/s.exec(t);
|
|
3248
|
-
if (!m && partial) m = /^\[(.*)$/s.exec(t);
|
|
3249
|
-
if (!m) return void 0;
|
|
3250
|
-
if (!m[1].trim()) return [];
|
|
3251
|
-
const items = splitOutside(m[1], ",").map((x) => x.trim());
|
|
3252
|
-
if (partial && !/\]$/.test(t)) {
|
|
3253
|
-
const last = items[items.length - 1];
|
|
3254
|
-
if (!last || last.startsWith('"') && !/"$/.test(last.slice(1))) items.pop();
|
|
3255
|
-
}
|
|
3256
|
-
return items.map(unquote);
|
|
3257
|
-
}
|
|
3258
|
-
function chartFromMermaid(text, options = {}) {
|
|
3259
|
-
const issues = [];
|
|
3260
|
-
const partial = !!options.partial;
|
|
3261
|
-
const all = text.replace(/^/, "").split(/\r?\n/);
|
|
3262
|
-
let start = 0;
|
|
3263
|
-
let frontTitle;
|
|
3264
|
-
if (_optionalChain([all, 'access', _82 => _82[0], 'optionalAccess', _83 => _83.trim, 'call', _84 => _84()]) === "---") {
|
|
3265
|
-
const end = all.findIndex((l, i) => i > 0 && l.trim() === "---");
|
|
3266
|
-
if (end > 0) {
|
|
3267
|
-
for (const l of all.slice(1, end)) {
|
|
3268
|
-
const m = /^title:\s*(.+)$/.exec(l.trim());
|
|
3269
|
-
if (m) frontTitle = unquote(m[1]);
|
|
3270
|
-
}
|
|
3271
|
-
start = end + 1;
|
|
3272
|
-
}
|
|
3273
|
-
}
|
|
3274
|
-
let family = null;
|
|
3275
|
-
let headerLine = -1;
|
|
3276
|
-
let horizontal = false;
|
|
3277
|
-
let showData = false;
|
|
3278
|
-
let meta = null;
|
|
3279
|
-
for (let i = start; i < all.length; i++) {
|
|
3280
|
-
const t = all[i].trim();
|
|
3281
|
-
if (!t || t.startsWith("%%")) continue;
|
|
3282
|
-
let m;
|
|
3283
|
-
if (m = /^xychart(?:-beta)?(?:\s+(horizontal|vertical))?\s*$/i.exec(t)) {
|
|
3284
|
-
family = "xychart";
|
|
3285
|
-
horizontal = _optionalChain([m, 'access', _85 => _85[1], 'optionalAccess', _86 => _86.toLowerCase, 'call', _87 => _87()]) === "horizontal";
|
|
3286
|
-
} else if (m = /^pie(\s+showData)?(?:\s+title\s+(.*))?\s*$/i.exec(t)) {
|
|
3287
|
-
family = "pie";
|
|
3288
|
-
showData = !!m[1];
|
|
3289
|
-
if (m[2]) frontTitle = m[2].trim();
|
|
3290
|
-
} else if (/^radar(?:-beta)?\s*$/i.test(t)) family = "radar";
|
|
3291
|
-
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)}".` });
|
|
3292
|
-
headerLine = i;
|
|
3293
|
-
break;
|
|
3294
|
-
}
|
|
3295
|
-
let title = _nullishCoalesce(frontTitle, () => ( ""));
|
|
3296
|
-
const categories = [];
|
|
3297
|
-
const series = [];
|
|
3298
|
-
const xAxis = _chunkNEOO6QJDcjs.defaultChartAxis.call(void 0, false);
|
|
3299
|
-
const yAxis = _chunkNEOO6QJDcjs.defaultChartAxis.call(void 0, true);
|
|
3300
|
-
const radarAxes = [];
|
|
3301
|
-
const pieSlices = [];
|
|
3302
|
-
let legendShow = true;
|
|
3303
|
-
const counts = { bar: 0, line: 0 };
|
|
3304
|
-
const lineIssue = (i, message) => issues.push({ path: `line ${i + 1}`, line: i + 1, message: `Line ${i + 1}: ${message}` });
|
|
3305
|
-
for (let i = headerLine + 1; family && i < all.length; i++) {
|
|
3306
|
-
const raw = all[i];
|
|
3307
|
-
const t = raw.trim();
|
|
3308
|
-
if (!t) continue;
|
|
3309
|
-
if (t.startsWith(CHART_MERMAID_META_PREFIX.trim())) {
|
|
3310
|
-
if (options.ignoreMeta) continue;
|
|
3311
|
-
try {
|
|
3312
|
-
const parsed = JSON.parse(t.slice(CHART_MERMAID_META_PREFIX.trim().length).trim());
|
|
3313
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) meta = parsed;
|
|
3314
|
-
else lineIssue(i, "the @pixerate metadata is not a JSON object; it was ignored.");
|
|
3315
|
-
} catch (e2) {
|
|
3316
|
-
if (!partial) lineIssue(i, "the @pixerate metadata is not valid JSON; it was ignored.");
|
|
3317
|
-
}
|
|
3318
|
-
continue;
|
|
3319
|
-
}
|
|
3320
|
-
if (t.startsWith("%%")) continue;
|
|
3321
|
-
const isLast = i === all.length - 1 || all.slice(i + 1).every((l) => !l.trim());
|
|
3322
|
-
let m;
|
|
3323
|
-
if (m = /^title\s+(.+)$/i.exec(t)) {
|
|
3324
|
-
title = unquote(m[1]);
|
|
3325
|
-
continue;
|
|
3326
|
-
}
|
|
3327
|
-
if (family === "xychart") {
|
|
3328
|
-
if (m = /^x-axis\b(.*)$/i.exec(t)) {
|
|
3329
|
-
const { title: xt, rest } = takeTitle(m[1]);
|
|
3330
|
-
if (xt) xAxis.title = xt;
|
|
3331
|
-
const list = parseList(rest, partial && isLast);
|
|
3332
|
-
if (list) categories.splice(0, categories.length, ...list);
|
|
3333
|
-
else if (RANGE.test(rest)) {
|
|
3334
|
-
const r = RANGE.exec(rest);
|
|
3335
|
-
xAxis.min = Number(r[1]);
|
|
3336
|
-
xAxis.max = Number(r[2]);
|
|
3337
|
-
} else if (rest) lineIssue(i, `expected x-axis [a, b, \u2026] or a range "min --> max", got "${rest.slice(0, 40)}".`);
|
|
3338
|
-
continue;
|
|
3339
|
-
}
|
|
3340
|
-
if (m = /^y-axis\b(.*)$/i.exec(t)) {
|
|
3341
|
-
const { title: yt, rest } = takeTitle(m[1]);
|
|
3342
|
-
if (yt) yAxis.title = yt;
|
|
3343
|
-
const r = RANGE.exec(rest);
|
|
3344
|
-
if (r) {
|
|
3345
|
-
yAxis.min = Number(r[1]);
|
|
3346
|
-
yAxis.max = Number(r[2]);
|
|
3347
|
-
} else if (rest) lineIssue(i, `expected y-axis "min --> max", got "${rest.slice(0, 40)}".`);
|
|
3348
|
-
continue;
|
|
3349
|
-
}
|
|
3350
|
-
if (m = /^(bar|line)\b(.*)$/i.exec(t)) {
|
|
3351
|
-
const kind = m[1].toLowerCase();
|
|
3352
|
-
const { title: st, rest } = takeTitle(m[2]);
|
|
3353
|
-
const list = parseList(rest, partial && isLast);
|
|
3354
|
-
if (!list) {
|
|
3355
|
-
if (!(partial && isLast)) lineIssue(i, `expected ${kind} [1, 2, 3], got "${rest.slice(0, 40)}".`);
|
|
3356
|
-
continue;
|
|
3357
|
-
}
|
|
3358
|
-
const values = list.map((x) => x.trim() === "" ? null : Number(x));
|
|
3359
|
-
if (values.some((x) => x !== null && !Number.isFinite(x))) {
|
|
3360
|
-
lineIssue(i, `${kind} values must be numbers.`);
|
|
3361
|
-
continue;
|
|
3362
|
-
}
|
|
3363
|
-
counts[kind]++;
|
|
3364
|
-
series.push({ id: `series-${series.length + 1}`, name: _nullishCoalesce(st, () => ( `${kind === "bar" ? "Bar" : "Line"} ${counts[kind]}`)), values, type: kind });
|
|
3365
|
-
continue;
|
|
3366
|
-
}
|
|
3367
|
-
} else if (family === "pie") {
|
|
3368
|
-
if (m = /^"((?:[^"\\]|\\.)*)"\s*:\s*(-?\d+(?:\.\d+)?)\s*$/.exec(t)) {
|
|
3369
|
-
pieSlices.push({ label: m[1].replace(/#quot;/g, '"'), value: Number(m[2]) });
|
|
3370
|
-
continue;
|
|
3371
|
-
}
|
|
3372
|
-
if (/^showData\s*$/i.test(t)) {
|
|
3373
|
-
showData = true;
|
|
3374
|
-
continue;
|
|
3375
|
-
}
|
|
3376
|
-
} else if (family === "radar") {
|
|
3377
|
-
if (m = /^axis\s+(.+)$/.exec(t)) {
|
|
3378
|
-
for (const part of splitOutside(m[1], ",")) {
|
|
3379
|
-
const a = /^([A-Za-z_][\w-]*)\s*(?:\[\s*"((?:[^"\\]|\\.)*)"\s*\])?$/.exec(part.trim());
|
|
3380
|
-
if (a) radarAxes.push({ id: a[1], label: _nullishCoalesce(a[2], () => ( a[1])) });
|
|
3381
|
-
else lineIssue(i, `could not read the axis "${part.trim()}".`);
|
|
3382
|
-
}
|
|
3383
|
-
continue;
|
|
3384
|
-
}
|
|
3385
|
-
if (m = /^curve\s+([A-Za-z_][\w-]*)\s*(?:\[\s*"((?:[^"\\]|\\.)*)"\s*\])?\s*\{(.*)\}?\s*$/.exec(t)) {
|
|
3386
|
-
const body = m[3].replace(/\}\s*$/, "");
|
|
3387
|
-
const entries = body.trim() ? splitOutside(body, ",").map((x) => x.trim()).filter(Boolean) : [];
|
|
3388
|
-
const ids = radarAxes.map((a) => a.id);
|
|
3389
|
-
const values = ids.map(() => null);
|
|
3390
|
-
let k = 0;
|
|
3391
|
-
for (const e of entries) {
|
|
3392
|
-
const keyed = /^([A-Za-z_][\w-]*)\s*:\s*(-?\d+(?:\.\d+)?)$/.exec(e);
|
|
3393
|
-
if (keyed) {
|
|
3394
|
-
const at = ids.indexOf(keyed[1]);
|
|
3395
|
-
if (at >= 0) values[at] = Number(keyed[2]);
|
|
3396
|
-
else lineIssue(i, `curve ${m[1]} names an unknown axis "${keyed[1]}".`);
|
|
3397
|
-
} else if (/^-?\d+(?:\.\d+)?$/.test(e)) {
|
|
3398
|
-
if (k < values.length) values[k] = Number(e);
|
|
3399
|
-
else values.push(Number(e));
|
|
3400
|
-
k++;
|
|
3401
|
-
} else lineIssue(i, `could not read "${e}" in curve ${m[1]}.`);
|
|
3402
|
-
}
|
|
3403
|
-
series.push({ id: m[1], name: _nullishCoalesce(m[2], () => ( m[1])), values });
|
|
3404
|
-
continue;
|
|
3405
|
-
}
|
|
3406
|
-
if (m = /^(max|min|ticks|graticule|showLegend)\s+(\S+)$/.exec(t)) {
|
|
3407
|
-
if (m[1] === "max") yAxis.max = Number(m[2]);
|
|
3408
|
-
else if (m[1] === "min") yAxis.min = Number(m[2]);
|
|
3409
|
-
else if (m[1] === "showLegend") legendShow = m[2] !== "false";
|
|
3410
|
-
continue;
|
|
3411
|
-
}
|
|
3412
|
-
}
|
|
3413
|
-
if (!(partial && isLast)) lineIssue(i, `could not read "${t.slice(0, 60)}"; it was skipped.`);
|
|
3414
|
-
}
|
|
3415
|
-
let input;
|
|
3416
|
-
if (family === "pie") {
|
|
3417
|
-
input = {
|
|
3418
|
-
type: "pie",
|
|
3419
|
-
title,
|
|
3420
|
-
dataLabels: showData,
|
|
3421
|
-
categories: pieSlices.map((s) => s.label),
|
|
3422
|
-
series: [{ id: "series-1", name: title || "Values", values: pieSlices.map((s) => s.value) }]
|
|
3423
|
-
};
|
|
3424
|
-
} else if (family === "radar") {
|
|
3425
|
-
input = { type: "radar", title, categories: radarAxes.map((a) => a.label), series, yAxis, legend: { show: legendShow, position: "bottom" } };
|
|
3426
|
-
} else {
|
|
3427
|
-
const allBar = series.length > 0 && series.every((s) => s.type === "bar");
|
|
3428
|
-
const allLine = series.length > 0 && series.every((s) => s.type === "line");
|
|
3429
|
-
const type = allLine ? "line" : "bar";
|
|
3430
|
-
const longest = Math.max(0, ...series.map((s) => s.values.length));
|
|
3431
|
-
const cats = categories.length ? categories : Array.from({ length: longest }, (_, i) => `${i + 1}`);
|
|
3432
|
-
input = {
|
|
3433
|
-
type,
|
|
3434
|
-
title,
|
|
3435
|
-
orientation: horizontal ? "horizontal" : "vertical",
|
|
3436
|
-
categories: cats,
|
|
3437
|
-
xAxis,
|
|
3438
|
-
yAxis,
|
|
3439
|
-
// Series that match the chart type do not need their own type.
|
|
3440
|
-
series: series.map((s) => allBar || allLine || s.type === type ? { ...s, type: void 0 } : s)
|
|
3441
|
-
};
|
|
3442
|
-
}
|
|
3443
|
-
if (meta) {
|
|
3444
|
-
const { v: _v, series: metaSeries, ...rest } = meta;
|
|
3445
|
-
Object.assign(input, rest);
|
|
3446
|
-
if (Array.isArray(metaSeries)) {
|
|
3447
|
-
const base = _nullishCoalesce(input.series, () => ( []));
|
|
3448
|
-
input.series = metaSeries.map((extra, i) => ({ ..._nullishCoalesce(base[i], () => ( {})), ..._nullishCoalesce(extra, () => ( {})) }));
|
|
3449
|
-
}
|
|
3450
|
-
}
|
|
3451
|
-
const { spec, issues: specIssues } = _chunkNEOO6QJDcjs.normalizeChartSpec.call(void 0, input);
|
|
3452
|
-
if (!family && !partial && !issues.length) issues.push({ path: "header", message: "The text is empty: expected a chart header (xychart-beta, pie or radar-beta)." });
|
|
3453
|
-
return { spec, issues: [...issues, ...specIssues], family, hasMeta: !!meta };
|
|
3454
|
-
}
|
|
3455
|
-
function findMermaidCharts(text) {
|
|
3456
|
-
const lines = text.replace(/^/, "").split(/\r?\n/);
|
|
3457
|
-
const found = [];
|
|
3458
|
-
const familyOf = (body) => {
|
|
3459
|
-
const header = body.split(/\r?\n/).find((l) => l.trim() && !l.trim().startsWith("%%") && l.trim() !== "---");
|
|
3460
|
-
if (!header) return null;
|
|
3461
|
-
if (/^\s*xychart(-beta)?\b/.test(header)) return "xychart";
|
|
3462
|
-
if (/^\s*pie\b/.test(header)) return "pie";
|
|
3463
|
-
if (/^\s*radar(-beta)?\b/.test(header)) return "radar";
|
|
3464
|
-
return null;
|
|
3465
|
-
};
|
|
3466
|
-
for (let i = 0; i < lines.length; i++) {
|
|
3467
|
-
const open = /^ {0,3}(`{3,}|~{3,})\s*([\w-]*)/.exec(lines[i]);
|
|
3468
|
-
if (!open) continue;
|
|
3469
|
-
const fence = open[1];
|
|
3470
|
-
const info = open[2].toLowerCase();
|
|
3471
|
-
let end = i + 1;
|
|
3472
|
-
while (end < lines.length && !new RegExp(`^ {0,3}${fence[0]}{${fence.length},}\\s*$`).test(lines[end])) end++;
|
|
3473
|
-
const body = lines.slice(i + 1, end).join("\n");
|
|
3474
|
-
if (!info || info === "mermaid") {
|
|
3475
|
-
const family = familyOf(body);
|
|
3476
|
-
if (family) found.push({ source: `${body.trimEnd()}
|
|
3477
|
-
`, family, line: i + 2 });
|
|
3478
|
-
}
|
|
3479
|
-
i = end;
|
|
3480
|
-
}
|
|
3481
|
-
if (!found.length) {
|
|
3482
|
-
const family = familyOf(text);
|
|
3483
|
-
if (family) found.push({ source: `${text.trim()}
|
|
3484
|
-
`, family, line: 1 });
|
|
3485
|
-
}
|
|
3486
|
-
return found;
|
|
3487
|
-
}
|
|
3488
|
-
|
|
3489
|
-
// src/chart/schemas.ts
|
|
3490
|
-
var VALUE = { type: ["number", "string", "null"], description: 'A number, numeric text ("1,200", "12%", "$40", "(3)") or null for a gap.' };
|
|
3491
|
-
var SERIES_REF = { type: "string", description: "Series id (from get_chart) or exact series name." };
|
|
3492
|
-
var CATEGORY_REF = { type: ["string", "integer"], description: "Category index (0-based) or exact category label." };
|
|
3493
|
-
var WIDTH = { type: "number", minimum: 50, maximum: 4096, description: "Width in pixels (default 640)." };
|
|
3494
|
-
var HEIGHT = { type: "number", minimum: 50, maximum: 4096, description: "Height in pixels (default 400)." };
|
|
3495
|
-
var FORMAT = {
|
|
3496
|
-
type: "object",
|
|
3497
|
-
description: "How numbers are written on this axis or series.",
|
|
3498
|
-
properties: {
|
|
3499
|
-
style: { type: "string", enum: [..._chunkNEOO6QJDcjs.CHART_NUMBER_STYLES], description: "number (default), percent (0.25 \u2192 25%), currency or compact (1.2K)." },
|
|
3500
|
-
decimals: { type: "integer", minimum: 0, maximum: 10, description: "Fixed decimals (default: what the ticks need)." },
|
|
3501
|
-
currency: { type: "string", description: 'ISO currency code for style "currency", e.g. "EUR" (default USD).' },
|
|
3502
|
-
prefix: { type: "string", description: "Text before every number." },
|
|
3503
|
-
suffix: { type: "string", description: "Text after every number." },
|
|
3504
|
-
code: { type: "string", description: 'Spreadsheet format code such as "#,##0.0,,\\"M\\"" or "0.0%"; wins over the other fields.' }
|
|
3505
|
-
},
|
|
3506
|
-
additionalProperties: false
|
|
3507
|
-
};
|
|
3508
|
-
var AXIS = (what) => ({
|
|
3509
|
-
type: "object",
|
|
3510
|
-
description: what,
|
|
3511
|
-
properties: {
|
|
3512
|
-
show: { type: "boolean", description: "Draw the axis labels (default true)." },
|
|
3513
|
-
title: { type: "string", description: 'Axis title ("" for none).' },
|
|
3514
|
-
min: { type: ["number", "null"], description: "Fixed lower bound, or null for automatic." },
|
|
3515
|
-
max: { type: ["number", "null"], description: "Fixed upper bound, or null for automatic." },
|
|
3516
|
-
step: { type: ["number", "null"], exclusiveMinimum: 0, description: "Fixed distance between ticks, or null for automatic." },
|
|
3517
|
-
gridlines: { type: "boolean", description: "Grid lines at each tick." },
|
|
3518
|
-
format: FORMAT,
|
|
3519
|
-
kind: { type: ["string", "null"], enum: [..._chunkNEOO6QJDcjs.CHART_AXIS_KINDS, null], description: 'Category axis only: "date" reads categories as dates and labels them by calendar steps.' }
|
|
3520
|
-
},
|
|
3521
|
-
additionalProperties: false
|
|
3522
|
-
});
|
|
3523
|
-
var TRENDLINE_PROPS = {
|
|
3524
|
-
type: { type: "string", enum: [..._chunkNEOO6QJDcjs.CHART_TRENDLINE_TYPES], description: "Fit: linear, exponential, logarithmic, power, polynomial or moving-average." },
|
|
3525
|
-
order: { type: "integer", minimum: 2, maximum: 6, description: "Polynomial order (default 2)." },
|
|
3526
|
-
period: { type: "integer", minimum: 2, description: "Moving-average period in points (default 2)." },
|
|
3527
|
-
forward: { type: "number", minimum: 0, description: "Extend the line this many x units past the last point (default 0)." },
|
|
3528
|
-
backward: { type: "number", minimum: 0, description: "Extend the line this many x units before the first point (default 0)." },
|
|
3529
|
-
intercept: { type: ["number", "null"], description: "Force the fit through this y at x = 0 (linear, polynomial, exponential)." },
|
|
3530
|
-
showEquation: { type: "boolean", description: "Write the fitted equation on the chart." },
|
|
3531
|
-
showR2: { type: "boolean", description: "Write R\xB2 on the chart." },
|
|
3532
|
-
name: { type: "string", description: 'Legend name (default e.g. "Linear (Sales)").' },
|
|
3533
|
-
color: { type: "string", description: "CSS colour (default: the series colour)." }
|
|
3534
|
-
};
|
|
3535
|
-
var SERIES_PROPS = {
|
|
3536
|
-
name: { type: "string", description: "Series name (legend and data grid header)." },
|
|
3537
|
-
values: { type: "array", items: VALUE, description: "One value per category, in category order (padded with gaps)." },
|
|
3538
|
-
color: { type: ["string", "null"], description: "CSS colour, or null for the palette colour." },
|
|
3539
|
-
type: { type: ["string", "null"], enum: [..._chunkNEOO6QJDcjs.CHART_SERIES_TYPES, null], description: "Draw this series as bar, line, area or scatter (a combo chart); null follows the chart type." },
|
|
3540
|
-
hidden: { type: "boolean", description: "Hide the series (its data is kept)." },
|
|
3541
|
-
axis: { type: ["string", "null"], enum: ["primary", "secondary", null], description: "Value axis: secondary is drawn on the right with its own scale." },
|
|
3542
|
-
x: { type: ["array", "null"], items: VALUE, description: "Numeric x per category, for scatter and bubble charts." },
|
|
3543
|
-
sizes: { type: ["array", "null"], items: VALUE, description: "Bubble sizes per category (area is proportional to size)." },
|
|
3544
|
-
pointColors: { type: ["array", "null"], items: { type: ["string", "null"] }, description: "Colour per category (null keeps the series colour)." },
|
|
3545
|
-
marker: { type: ["string", "null"], enum: [..._chunkNEOO6QJDcjs.CHART_MARKERS, null], description: "Point marker shape for line, area and scatter series." },
|
|
3546
|
-
curve: { type: ["string", "null"], enum: [..._chunkNEOO6QJDcjs.CHART_CURVES, null], description: "Line shape for this series: linear, smooth or step." },
|
|
3547
|
-
dataLabels: { type: ["boolean", "null"], description: "Value labels for this series (null follows the chart)." },
|
|
3548
|
-
format: { ...FORMAT, type: ["object", "null"], description: "How this series' labels are written (null follows the value axis)." }
|
|
3549
|
-
};
|
|
3550
|
-
var CHART_UPDATE_PROPERTIES = {
|
|
3551
|
-
type: { type: "string", enum: [..._chunkNEOO6QJDcjs.CHART_TYPES], description: "Chart type." },
|
|
3552
|
-
title: { type: "string", description: 'Title ("" for none).' },
|
|
3553
|
-
subtitle: { type: "string", description: 'Subtitle ("" for none).' },
|
|
3554
|
-
xAxis: AXIS("Category axis (or numeric x axis of scatter / bubble charts)."),
|
|
3555
|
-
yAxis: AXIS("Value axis."),
|
|
3556
|
-
yAxis2: AXIS('Secondary value axis (for series with axis "secondary").'),
|
|
3557
|
-
legend: {
|
|
3558
|
-
type: "object",
|
|
3559
|
-
description: "Legend.",
|
|
3560
|
-
properties: { show: { type: "boolean", description: "Show the legend." }, position: { type: "string", enum: [..._chunkNEOO6QJDcjs.CHART_LEGEND_POSITIONS], description: "Where the legend goes." } },
|
|
3561
|
-
additionalProperties: false
|
|
3562
|
-
},
|
|
3563
|
-
stacking: { type: "string", enum: [..._chunkNEOO6QJDcjs.CHART_STACKINGS], description: "none, stacked, or percent (each category scaled to 100%)." },
|
|
3564
|
-
orientation: { type: "string", enum: [..._chunkNEOO6QJDcjs.CHART_ORIENTATIONS], description: "horizontal draws bars sideways." },
|
|
3565
|
-
curve: { type: "string", enum: [..._chunkNEOO6QJDcjs.CHART_CURVES], description: "Line shape: linear, smooth or step." },
|
|
3566
|
-
points: { type: "boolean", description: "Dots on line and area series." },
|
|
3567
|
-
dataLabels: { type: "boolean", description: "Value labels on bars, points and slices." },
|
|
3568
|
-
labelPosition: { type: "string", enum: [..._chunkNEOO6QJDcjs.CHART_LABEL_POSITIONS], description: "Bar label position; auto drops labels that do not fit." },
|
|
3569
|
-
palette: { type: "array", items: { type: "string" }, minItems: 1, description: "Series colours in order (CSS colours)." },
|
|
3570
|
-
innerRadius: { type: "number", minimum: 0, maximum: 0.9, description: "Donut hole as a fraction of the radius." },
|
|
3571
|
-
gapWidth: { type: "number", minimum: 0, maximum: 0.9, description: "Space between bar groups as a fraction of the category (default 0.28)." },
|
|
3572
|
-
overlap: { type: "number", minimum: -1, maximum: 1, description: "Bar overlap within a group as a fraction of a bar (negative: a gap; default -0.12)." },
|
|
3573
|
-
bins: { type: ["integer", "null"], minimum: 1, maximum: 500, description: "Histogram bin count, or null for \u221An." },
|
|
3574
|
-
showTotal: { type: "boolean", description: "Waterfall: append a total bar." }
|
|
3575
|
-
};
|
|
3576
|
-
|
|
3577
249
|
// src/chart/commands.ts
|
|
3578
|
-
var
|
|
250
|
+
var fail = (message) => {
|
|
3579
251
|
throw new Error(message);
|
|
3580
252
|
};
|
|
3581
253
|
var check = (result) => {
|
|
3582
|
-
if (!result.ok)
|
|
254
|
+
if (!result.ok) fail(_nullishCoalesce(result.message, () => ( "The change was rejected.")));
|
|
3583
255
|
};
|
|
3584
256
|
function createChartCommands(controller, options = {}) {
|
|
3585
257
|
const prefix = _nullishCoalesce(options.prefix, () => ( "chart."));
|
|
@@ -3588,13 +260,13 @@ function createChartCommands(controller, options = {}) {
|
|
|
3588
260
|
const resolveSeries = (ref) => {
|
|
3589
261
|
const spec = controller.getSpec();
|
|
3590
262
|
const series = _nullishCoalesce(spec.series.find((s) => s.id === ref), () => ( spec.series.find((s) => s.name === ref)));
|
|
3591
|
-
if (!series)
|
|
263
|
+
if (!series) fail(`There is no series "${ref}"; expected one of ${seriesIds().map((id) => `"${id}"`).join(", ") || "(none yet)"}.`);
|
|
3592
264
|
return series;
|
|
3593
265
|
};
|
|
3594
266
|
const resolveCategory = (ref) => {
|
|
3595
267
|
const categories = controller.getSpec().categories;
|
|
3596
268
|
const index = typeof ref === "number" ? ref : categories.indexOf(ref);
|
|
3597
|
-
if (index < 0 || index >= categories.length || !Number.isInteger(index))
|
|
269
|
+
if (index < 0 || index >= categories.length || !Number.isInteger(index)) fail(`There is no category ${JSON.stringify(ref)}; expected an index from 0 to ${categories.length - 1} or one of the labels.`);
|
|
3598
270
|
return index;
|
|
3599
271
|
};
|
|
3600
272
|
const object = (properties, required) => ({ type: "object", properties, ...required ? { required } : {}, additionalProperties: false });
|
|
@@ -3606,10 +278,10 @@ function createChartCommands(controller, options = {}) {
|
|
|
3606
278
|
readOnly: true,
|
|
3607
279
|
keywords: ["chart", "data", "spec", "get", "summary"],
|
|
3608
280
|
description: "Returns the chart spec, its data as a table (series names in the header row) and a summary: per-series min / max / mean / trend, axes as drawn, layout warnings and a colour check.",
|
|
3609
|
-
params: object({ width: WIDTH, height: HEIGHT }),
|
|
281
|
+
params: object({ width: _chunkU6IZKC7Bcjs.WIDTH, height: _chunkU6IZKC7Bcjs.HEIGHT }),
|
|
3610
282
|
run: (_ctx, params = {}) => {
|
|
3611
283
|
const spec = controller.getSpec();
|
|
3612
|
-
return { spec: controller.toJSON(), table:
|
|
284
|
+
return { spec: controller.toJSON(), table: _chunkPRSJJZBScjs.chartDataToTable.call(void 0, spec), summary: _chunkPRSJJZBScjs.summarizeChart.call(void 0, controller.toJSON(), { width: _nullishCoalesce(_optionalChain([params, 'optionalAccess', _9 => _9.width]), () => ( 640)), height: _nullishCoalesce(_optionalChain([params, 'optionalAccess', _10 => _10.height]), () => ( 400)) }), issues: controller.getState().issues };
|
|
3613
285
|
}
|
|
3614
286
|
},
|
|
3615
287
|
{
|
|
@@ -3618,7 +290,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3618
290
|
label: "Change Chart\u2026",
|
|
3619
291
|
keywords: ["chart", "title", "axis", "legend", "stack", "style", "format", "type"],
|
|
3620
292
|
description: "Changes chart presentation as one undo step: type, titles, axes (xAxis = categories, yAxis = values, yAxis2 = secondary values), legend, stacking, orientation, curve, points, data labels, palette, donut hole, bar spacing, histogram bins or waterfall total. Invalid values change nothing and return why.",
|
|
3621
|
-
params: object(CHART_UPDATE_PROPERTIES),
|
|
293
|
+
params: object(_chunkU6IZKC7Bcjs.CHART_UPDATE_PROPERTIES),
|
|
3622
294
|
run: (_ctx, params) => {
|
|
3623
295
|
check(controller.update(params));
|
|
3624
296
|
return { spec: controller.toJSON() };
|
|
@@ -3632,11 +304,11 @@ function createChartCommands(controller, options = {}) {
|
|
|
3632
304
|
description: "Replaces the categories and series as one undo step. Each series has one value per category (padded with gaps).",
|
|
3633
305
|
params: object({
|
|
3634
306
|
categories: { type: "array", items: { type: "string" }, description: "Category labels in order." },
|
|
3635
|
-
series: { type: "array", description: "The new series.", items: object({ id: { type: "string", description: "Stable id (optional)." }, ...SERIES_PROPS }, ["name", "values"]) }
|
|
307
|
+
series: { type: "array", description: "The new series.", items: object({ id: { type: "string", description: "Stable id (optional)." }, ..._chunkU6IZKC7Bcjs.SERIES_PROPS }, ["name", "values"]) }
|
|
3636
308
|
}),
|
|
3637
309
|
run: (_ctx, params) => {
|
|
3638
310
|
check(controller.setData(params));
|
|
3639
|
-
return { table:
|
|
311
|
+
return { table: _chunkPRSJJZBScjs.chartDataToTable.call(void 0, controller.getSpec()) };
|
|
3640
312
|
}
|
|
3641
313
|
},
|
|
3642
314
|
{
|
|
@@ -3645,10 +317,10 @@ function createChartCommands(controller, options = {}) {
|
|
|
3645
317
|
label: "Set Chart Values\u2026",
|
|
3646
318
|
keywords: ["chart", "value", "cell", "edit"],
|
|
3647
319
|
description: "Sets individual values as one undo step. Series by id or name; category by index or label.",
|
|
3648
|
-
params: object({ cells: { type: "array", minItems: 1, description: "Values to set.", items: object({ series: SERIES_REF, category: CATEGORY_REF, value: VALUE }, ["series", "category", "value"]) } }, ["cells"]),
|
|
320
|
+
params: object({ cells: { type: "array", minItems: 1, description: "Values to set.", items: object({ series: _chunkU6IZKC7Bcjs.SERIES_REF, category: _chunkU6IZKC7Bcjs.CATEGORY_REF, value: _chunkU6IZKC7Bcjs.VALUE }, ["series", "category", "value"]) } }, ["cells"]),
|
|
3649
321
|
run: (_ctx, params) => {
|
|
3650
322
|
check(controller.setValues(params.cells.map((c) => ({ seriesId: resolveSeries(c.series).id, index: resolveCategory(c.category), value: c.value }))));
|
|
3651
|
-
return { table:
|
|
323
|
+
return { table: _chunkPRSJJZBScjs.chartDataToTable.call(void 0, controller.getSpec()) };
|
|
3652
324
|
}
|
|
3653
325
|
},
|
|
3654
326
|
{
|
|
@@ -3657,11 +329,11 @@ function createChartCommands(controller, options = {}) {
|
|
|
3657
329
|
label: "Add Series",
|
|
3658
330
|
keywords: ["chart", "series", "new"],
|
|
3659
331
|
description: "Adds a series (default: an empty one at the end). Returns its id.",
|
|
3660
|
-
params: object({ ...SERIES_PROPS, id: { type: "string", description: "Id to use (default series-N)." }, at: { type: "integer", minimum: 0, description: "Position (default: last)." } }),
|
|
332
|
+
params: object({ ..._chunkU6IZKC7Bcjs.SERIES_PROPS, id: { type: "string", description: "Id to use (default series-N)." }, at: { type: "integer", minimum: 0, description: "Position (default: last)." } }),
|
|
3661
333
|
run: (_ctx, params = {}) => {
|
|
3662
334
|
const { at, ...input } = _nullishCoalesce(params, () => ( {}));
|
|
3663
|
-
const
|
|
3664
|
-
const result = controller.addSeries(
|
|
335
|
+
const clean = Object.fromEntries(Object.entries(input).filter(([, v]) => v !== null));
|
|
336
|
+
const result = controller.addSeries(clean, { at });
|
|
3665
337
|
check(result);
|
|
3666
338
|
return { id: result.id };
|
|
3667
339
|
}
|
|
@@ -3672,7 +344,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3672
344
|
label: "Change Series\u2026",
|
|
3673
345
|
keywords: ["chart", "series", "rename", "colour", "color", "hide", "axis", "marker"],
|
|
3674
346
|
description: "Renames, recolours, hides, moves to the secondary axis or changes how a series is drawn (null resets a field).",
|
|
3675
|
-
params: object({ series: SERIES_REF, ...Object.fromEntries(Object.entries(SERIES_PROPS).filter(([k]) => k !== "values")) }, ["series"]),
|
|
347
|
+
params: object({ series: _chunkU6IZKC7Bcjs.SERIES_REF, ...Object.fromEntries(Object.entries(_chunkU6IZKC7Bcjs.SERIES_PROPS).filter(([k]) => k !== "values")) }, ["series"]),
|
|
3676
348
|
run: (_ctx, params) => {
|
|
3677
349
|
const { series, ...patch } = params;
|
|
3678
350
|
const id = resolveSeries(series).id;
|
|
@@ -3686,7 +358,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3686
358
|
label: "Delete Series",
|
|
3687
359
|
keywords: ["chart", "series", "remove"],
|
|
3688
360
|
description: "Deletes a series by id or name.",
|
|
3689
|
-
params: object({ series: SERIES_REF }, ["series"]),
|
|
361
|
+
params: object({ series: _chunkU6IZKC7Bcjs.SERIES_REF }, ["series"]),
|
|
3690
362
|
run: (_ctx, params) => {
|
|
3691
363
|
check(controller.removeSeries(resolveSeries(params.series).id));
|
|
3692
364
|
return { series: seriesIds() };
|
|
@@ -3699,7 +371,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3699
371
|
keywords: ["chart", "category", "row", "label"],
|
|
3700
372
|
description: "Inserts a category (gaps in every series) at `at` (default: the end).",
|
|
3701
373
|
params: object({ label: { type: "string", description: "Category label (default: its number)." }, at: { type: "integer", minimum: 0, description: "Index to insert at (default: the end)." } }),
|
|
3702
|
-
run: (_ctx, params = {}) => controller.addCategory(_optionalChain([params, 'optionalAccess',
|
|
374
|
+
run: (_ctx, params = {}) => controller.addCategory(_optionalChain([params, 'optionalAccess', _11 => _11.label]), { at: _optionalChain([params, 'optionalAccess', _12 => _12.at]) })
|
|
3703
375
|
},
|
|
3704
376
|
{
|
|
3705
377
|
...base,
|
|
@@ -3707,7 +379,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3707
379
|
label: "Delete Category",
|
|
3708
380
|
keywords: ["chart", "category", "row", "remove"],
|
|
3709
381
|
description: "Deletes a category (and its values) by index or label.",
|
|
3710
|
-
params: object({ category: CATEGORY_REF }, ["category"]),
|
|
382
|
+
params: object({ category: _chunkU6IZKC7Bcjs.CATEGORY_REF }, ["category"]),
|
|
3711
383
|
run: (_ctx, params) => {
|
|
3712
384
|
check(controller.removeCategory(resolveCategory(params.category)));
|
|
3713
385
|
return { categories: controller.getSpec().categories };
|
|
@@ -3719,7 +391,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3719
391
|
label: "Add Trendline\u2026",
|
|
3720
392
|
keywords: ["chart", "trend", "regression", "fit", "forecast", "moving average"],
|
|
3721
393
|
description: "Fits a trendline to a series (linear, exponential, logarithmic, power, polynomial or moving average). Returns its index and the fit.",
|
|
3722
|
-
params: object({ series: SERIES_REF, ...TRENDLINE_PROPS }, ["series", "type"]),
|
|
394
|
+
params: object({ series: _chunkU6IZKC7Bcjs.SERIES_REF, ..._chunkU6IZKC7Bcjs.TRENDLINE_PROPS }, ["series", "type"]),
|
|
3723
395
|
run: (_ctx, params) => {
|
|
3724
396
|
const { series, ...line } = params;
|
|
3725
397
|
const result = controller.addTrendline(resolveSeries(series).id, line);
|
|
@@ -3734,10 +406,10 @@ function createChartCommands(controller, options = {}) {
|
|
|
3734
406
|
label: "Delete Trendline",
|
|
3735
407
|
keywords: ["chart", "trend", "remove"],
|
|
3736
408
|
description: "Deletes a trendline from a series.",
|
|
3737
|
-
params: object({ series: SERIES_REF, index: { type: "integer", minimum: 0, description: "Trendline index (0 = first)." } }, ["series", "index"]),
|
|
409
|
+
params: object({ series: _chunkU6IZKC7Bcjs.SERIES_REF, index: { type: "integer", minimum: 0, description: "Trendline index (0 = first)." } }, ["series", "index"]),
|
|
3738
410
|
run: (_ctx, params) => {
|
|
3739
411
|
check(controller.removeTrendline(resolveSeries(params.series).id, params.index));
|
|
3740
|
-
return { trendlines: _nullishCoalesce(_optionalChain([controller, 'access',
|
|
412
|
+
return { trendlines: _nullishCoalesce(_optionalChain([controller, 'access', _13 => _13.getSeries, 'call', _14 => _14(resolveSeries(params.series).id), 'optionalAccess', _15 => _15.trendlines]), () => ( [])) };
|
|
3741
413
|
}
|
|
3742
414
|
},
|
|
3743
415
|
{
|
|
@@ -3747,8 +419,8 @@ function createChartCommands(controller, options = {}) {
|
|
|
3747
419
|
keywords: ["chart", "quick layout", "style", "colours", "colors", "monochrome", "theme"],
|
|
3748
420
|
description: "Applies a quick layout and / or colour scheme as one undo step.",
|
|
3749
421
|
params: object({
|
|
3750
|
-
layout: { type: "string", enum: CHART_LAYOUT_PRESETS.map((p) => p.id), description: "Quick layout id." },
|
|
3751
|
-
colors: { type: "string", enum: CHART_COLOR_SCHEMES.map((p) => p.id), description: "Colour scheme id." }
|
|
422
|
+
layout: { type: "string", enum: _chunkPRSJJZBScjs.CHART_LAYOUT_PRESETS.map((p) => p.id), description: "Quick layout id." },
|
|
423
|
+
colors: { type: "string", enum: _chunkPRSJJZBScjs.CHART_COLOR_SCHEMES.map((p) => p.id), description: "Colour scheme id." }
|
|
3752
424
|
}),
|
|
3753
425
|
run: (_ctx, params = {}) => {
|
|
3754
426
|
check(controller.applyPreset(_nullishCoalesce(params, () => ( {}))));
|
|
@@ -3763,7 +435,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3763
435
|
description: "Swaps series and categories as one undo step.",
|
|
3764
436
|
run: () => {
|
|
3765
437
|
check(controller.transpose());
|
|
3766
|
-
return { table:
|
|
438
|
+
return { table: _chunkPRSJJZBScjs.chartDataToTable.call(void 0, controller.getSpec()) };
|
|
3767
439
|
}
|
|
3768
440
|
},
|
|
3769
441
|
{
|
|
@@ -3783,7 +455,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3783
455
|
run: (_ctx, params) => {
|
|
3784
456
|
const result = controller.importCsv(params.csv, { seriesIn: params.seriesIn, header: params.header });
|
|
3785
457
|
check(result);
|
|
3786
|
-
return { table:
|
|
458
|
+
return { table: _chunkPRSJJZBScjs.chartDataToTable.call(void 0, controller.getSpec()), issues: _nullishCoalesce(result.issues, () => ( [])) };
|
|
3787
459
|
}
|
|
3788
460
|
},
|
|
3789
461
|
{
|
|
@@ -3794,8 +466,8 @@ function createChartCommands(controller, options = {}) {
|
|
|
3794
466
|
description: "Replaces the chart with Mermaid xychart-beta, pie or radar-beta text (one undo step). Unreadable lines are listed with line numbers.",
|
|
3795
467
|
params: object({ mermaid: { type: "string", description: "Mermaid text." } }, ["mermaid"]),
|
|
3796
468
|
run: (_ctx, params) => {
|
|
3797
|
-
const read = chartFromMermaid(params.mermaid);
|
|
3798
|
-
if (!read.family)
|
|
469
|
+
const read = _chunkPRSJJZBScjs.chartFromMermaid.call(void 0, params.mermaid);
|
|
470
|
+
if (!read.family) fail(read.issues.map((i) => i.message).join(" ") || "Expected Mermaid starting with xychart-beta, pie or radar-beta.");
|
|
3799
471
|
check(controller.update({ ...read.spec }, { label: "Import Mermaid chart" }));
|
|
3800
472
|
return { spec: controller.toJSON(), issues: read.issues };
|
|
3801
473
|
}
|
|
@@ -3808,7 +480,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3808
480
|
keywords: ["chart", "mermaid", "markdown", "export"],
|
|
3809
481
|
description: "Returns the chart as Mermaid text (with a round-trip comment unless meta is false).",
|
|
3810
482
|
params: object({ meta: { type: "boolean", description: "Include the %% @pixerate round-trip comment (default true)." } }),
|
|
3811
|
-
run: (_ctx, params = {}) => ({ mermaid: chartToMermaid(controller.toJSON(), { meta: _optionalChain([params, 'optionalAccess',
|
|
483
|
+
run: (_ctx, params = {}) => ({ mermaid: _chunkPRSJJZBScjs.chartToMermaid.call(void 0, controller.toJSON(), { meta: _optionalChain([params, 'optionalAccess', _16 => _16.meta]) }) })
|
|
3812
484
|
},
|
|
3813
485
|
{
|
|
3814
486
|
...base,
|
|
@@ -3817,7 +489,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3817
489
|
readOnly: true,
|
|
3818
490
|
keywords: ["chart", "office", "word", "powerpoint", "docx", "pptx", "xml", "export"],
|
|
3819
491
|
description: "Returns the chart as a DrawingML chart part (c:chartSpace) and what could not be written.",
|
|
3820
|
-
run: () =>
|
|
492
|
+
run: () => _chunkPIMCHMZEcjs.chartToOoxml.call(void 0, controller.toJSON())
|
|
3821
493
|
},
|
|
3822
494
|
{
|
|
3823
495
|
...base,
|
|
@@ -3826,9 +498,9 @@ function createChartCommands(controller, options = {}) {
|
|
|
3826
498
|
readOnly: true,
|
|
3827
499
|
keywords: ["chart", "svg", "export", "image"],
|
|
3828
500
|
description: "Returns the chart drawn as a standalone, accessible SVG string (with <title> and <desc>).",
|
|
3829
|
-
params: object({ width: WIDTH, height: HEIGHT }),
|
|
501
|
+
params: object({ width: _chunkU6IZKC7Bcjs.WIDTH, height: _chunkU6IZKC7Bcjs.HEIGHT }),
|
|
3830
502
|
run: (_ctx, params = {}) => ({
|
|
3831
|
-
svg: chartToSvg(controller.toJSON(), { width: _nullishCoalesce(_optionalChain([params, 'optionalAccess',
|
|
503
|
+
svg: _chunkPRSJJZBScjs.chartToSvg.call(void 0, controller.toJSON(), { width: _nullishCoalesce(_optionalChain([params, 'optionalAccess', _17 => _17.width]), () => ( 640)), height: _nullishCoalesce(_optionalChain([params, 'optionalAccess', _18 => _18.height]), () => ( 400)) })
|
|
3832
504
|
})
|
|
3833
505
|
},
|
|
3834
506
|
{
|
|
@@ -3840,7 +512,7 @@ function createChartCommands(controller, options = {}) {
|
|
|
3840
512
|
description: "Suggests chart types for the current data (or CSV), best first, each with a reason.",
|
|
3841
513
|
params: object({ csv: { type: "string", description: "CSV text to analyse instead of the current data." } }),
|
|
3842
514
|
run: (_ctx, params = {}) => ({
|
|
3843
|
-
recommendations: recommendChartTypes(_optionalChain([params, 'optionalAccess',
|
|
515
|
+
recommendations: recommendChartTypes(_optionalChain([params, 'optionalAccess', _19 => _19.csv]) !== void 0 ? _chunkTNNJXF46cjs.parseCsv.call(void 0, params.csv) : _chunkPRSJJZBScjs.chartDataToTable.call(void 0, controller.getSpec()))
|
|
3844
516
|
})
|
|
3845
517
|
},
|
|
3846
518
|
{
|
|
@@ -3853,8 +525,8 @@ function createChartCommands(controller, options = {}) {
|
|
|
3853
525
|
run: () => {
|
|
3854
526
|
const s = controller.getSpec();
|
|
3855
527
|
const visible = s.series.filter((x) => !x.hidden);
|
|
3856
|
-
const colors =
|
|
3857
|
-
return { colors, ...
|
|
528
|
+
const colors = _chunkPRSJJZBScjs.chartColorsByCategory.call(void 0, s.type) ? s.categories.map((_, i) => _optionalChain([visible, 'access', _20 => _20[0], 'optionalAccess', _21 => _21.pointColors, 'optionalAccess', _22 => _22[i]]) || _chunkPRSJJZBScjs.chartSliceColor.call(void 0, s, i)) : visible.map((x) => _chunkPRSJJZBScjs.chartSeriesColor.call(void 0, s, s.series.indexOf(x)));
|
|
529
|
+
return { colors, ..._chunkPRSJJZBScjs.checkChartColors.call(void 0, colors) };
|
|
3858
530
|
}
|
|
3859
531
|
},
|
|
3860
532
|
{
|
|
@@ -3883,9 +555,9 @@ function chartClientToScene(scene, box, clientX, clientY) {
|
|
|
3883
555
|
return { x: (clientX - box.left) * sx, y: (clientY - box.top) * sy };
|
|
3884
556
|
}
|
|
3885
557
|
function canDragChartPoint(controller, scene, target) {
|
|
3886
|
-
if (_optionalChain([target, 'optionalAccess',
|
|
558
|
+
if (_optionalChain([target, 'optionalAccess', _23 => _23.kind]) !== "point" || !scene.valueScale || scene.percent) return false;
|
|
3887
559
|
const spec = controller.getSpec();
|
|
3888
|
-
const family =
|
|
560
|
+
const family = _chunkPRSJJZBScjs.chartFamily.call(void 0, spec);
|
|
3889
561
|
if (family !== "cartesian" && family !== "xy" && family !== "waterfall") return false;
|
|
3890
562
|
const series = controller.getSeries(target.seriesId);
|
|
3891
563
|
return !!series && !series.hidden && series.values[target.index] !== null;
|
|
@@ -3894,7 +566,7 @@ function snapChartDragValue(scene, value, options = {}) {
|
|
|
3894
566
|
const scale = _nullishCoalesce(options.scale, () => ( scene.valueScale));
|
|
3895
567
|
const tolerance = _nullishCoalesce(options.tolerancePx, () => ( 6));
|
|
3896
568
|
if (scale) {
|
|
3897
|
-
const px = (v) => linearMap(v, scale.domain, scale.range);
|
|
569
|
+
const px = (v) => _chunkPRSJJZBScjs.linearMap.call(void 0, v, scale.domain, scale.range);
|
|
3898
570
|
const at = px(value);
|
|
3899
571
|
let best = null;
|
|
3900
572
|
let bestDist = tolerance;
|
|
@@ -3908,7 +580,7 @@ function snapChartDragValue(scene, value, options = {}) {
|
|
|
3908
580
|
}
|
|
3909
581
|
if (best !== null) return best;
|
|
3910
582
|
}
|
|
3911
|
-
return snapChartValue(scene, value, options.step, _optionalChain([scale, 'optionalAccess',
|
|
583
|
+
return _chunkPRSJJZBScjs.snapChartValue.call(void 0, scene, value, options.step, _optionalChain([scale, 'optionalAccess', _24 => _24.ticks]));
|
|
3912
584
|
}
|
|
3913
585
|
function startChartPointDrag(controller, scene, target, start, options = {}) {
|
|
3914
586
|
if (!canDragChartPoint(controller, scene, target)) return null;
|
|
@@ -3917,11 +589,11 @@ function startChartPointDrag(controller, scene, target, start, options = {}) {
|
|
|
3917
589
|
const series = controller.getSeries(seriesId);
|
|
3918
590
|
const secondary = series.axis === "secondary" && !!scene.valueScale2;
|
|
3919
591
|
const scale = secondary ? scene.valueScale2 : scene.valueScale;
|
|
3920
|
-
const xy =
|
|
592
|
+
const xy = _chunkPRSJJZBScjs.chartFamily.call(void 0, spec) === "xy" && !!scene.xScale;
|
|
3921
593
|
const startValue = _nullishCoalesce(series.values[index], () => ( 0));
|
|
3922
|
-
const startX = _nullishCoalesce(_optionalChain([series, 'access',
|
|
3923
|
-
const startAt = chartValueAtPoint(scene, start, secondary);
|
|
3924
|
-
const startAtX = xy ? chartXAtPoint(scene, start) : void 0;
|
|
594
|
+
const startX = _nullishCoalesce(_optionalChain([series, 'access', _25 => _25.x, 'optionalAccess', _26 => _26[index]]), () => ( null));
|
|
595
|
+
const startAt = _chunkPRSJJZBScjs.chartValueAtPoint.call(void 0, scene, start, secondary);
|
|
596
|
+
const startAtX = xy ? _chunkPRSJJZBScjs.chartXAtPoint.call(void 0, scene, start) : void 0;
|
|
3925
597
|
if (startAt === void 0) return null;
|
|
3926
598
|
const candidates = [0, startValue];
|
|
3927
599
|
if (!xy) {
|
|
@@ -3939,34 +611,34 @@ function startChartPointDrag(controller, scene, target, start, options = {}) {
|
|
|
3939
611
|
target: { seriesId, index },
|
|
3940
612
|
move(point, moveOptions = {}) {
|
|
3941
613
|
if (ended) return;
|
|
3942
|
-
const at = chartValueAtPoint(scene, point, secondary);
|
|
614
|
+
const at = _chunkPRSJJZBScjs.chartValueAtPoint.call(void 0, scene, point, secondary);
|
|
3943
615
|
if (at === void 0) return;
|
|
3944
616
|
const factor = moveOptions.fine ? 0.1 : 1;
|
|
3945
617
|
let dy = (at - startAt) * factor;
|
|
3946
618
|
let dx = 0;
|
|
3947
619
|
if (xy && startX !== null && startAtX !== void 0) {
|
|
3948
|
-
const atX = chartXAtPoint(scene, point);
|
|
620
|
+
const atX = _chunkPRSJJZBScjs.chartXAtPoint.call(void 0, scene, point);
|
|
3949
621
|
if (atX !== void 0) dx = (atX - startAtX) * factor;
|
|
3950
622
|
if (moveOptions.lockAxis) {
|
|
3951
623
|
if (Math.abs(point.x - start.x) >= Math.abs(point.y - start.y)) dy = 0;
|
|
3952
624
|
else dx = 0;
|
|
3953
625
|
}
|
|
3954
626
|
}
|
|
3955
|
-
const
|
|
627
|
+
const snap = (v, s, c) => {
|
|
3956
628
|
if (moveOptions.noSnap) return Number(v.toPrecision(12));
|
|
3957
629
|
if (moveOptions.fine) {
|
|
3958
630
|
const unit = options.step ? options.step / 10 : s.ticks.length > 1 ? (s.ticks[1] - s.ticks[0]) / 100 : void 0;
|
|
3959
|
-
return snapChartValue(scene, v, unit, s.ticks);
|
|
631
|
+
return _chunkPRSJJZBScjs.snapChartValue.call(void 0, scene, v, unit, s.ticks);
|
|
3960
632
|
}
|
|
3961
633
|
return snapChartDragValue(scene, v, { candidates: c, tolerancePx: options.tolerancePx, step: options.step, scale: s });
|
|
3962
634
|
};
|
|
3963
|
-
const y =
|
|
635
|
+
const y = snap(startValue + dy, scale, candidates);
|
|
3964
636
|
if (xy && startX !== null) {
|
|
3965
|
-
const x = dx !== 0 ?
|
|
637
|
+
const x = dx !== 0 ? snap(startX + dx, scene.xScale, [0, startX]) : startX;
|
|
3966
638
|
const xs = [..._nullishCoalesce(series.x, () => ( []))];
|
|
3967
639
|
xs[index] = x;
|
|
3968
640
|
controller.setValue(seriesId, index, y, { label });
|
|
3969
|
-
if (x !== _optionalChain([controller, 'access',
|
|
641
|
+
if (x !== _optionalChain([controller, 'access', _27 => _27.getSeries, 'call', _28 => _28(seriesId), 'optionalAccess', _29 => _29.x, 'optionalAccess', _30 => _30[index]])) controller.updateSeries(seriesId, { x: xs });
|
|
3970
642
|
} else controller.setValue(seriesId, index, y, { label });
|
|
3971
643
|
},
|
|
3972
644
|
end() {
|
|
@@ -4000,7 +672,7 @@ function handleChartKey(controller, scene, event) {
|
|
|
4000
672
|
return true;
|
|
4001
673
|
case "ArrowLeft":
|
|
4002
674
|
case "ArrowRight": {
|
|
4003
|
-
if (event.altKey || _optionalChain([selection, 'optionalAccess',
|
|
675
|
+
if (event.altKey || _optionalChain([selection, 'optionalAccess', _31 => _31.kind]) !== "point" && _optionalChain([selection, 'optionalAccess', _32 => _32.kind]) !== "category") return false;
|
|
4004
676
|
const n = spec.categories.length;
|
|
4005
677
|
const index = Math.max(0, Math.min(n - 1, selection.index + (event.key === "ArrowRight" ? 1 : -1)));
|
|
4006
678
|
controller.select({ ...selection, index });
|
|
@@ -4008,9 +680,9 @@ function handleChartKey(controller, scene, event) {
|
|
|
4008
680
|
}
|
|
4009
681
|
case "ArrowUp":
|
|
4010
682
|
case "ArrowDown": {
|
|
4011
|
-
if (_optionalChain([selection, 'optionalAccess',
|
|
4012
|
-
const secondary = _optionalChain([controller, 'access',
|
|
4013
|
-
const ticks = _nullishCoalesce(_nullishCoalesce(_optionalChain([(secondary ? _optionalChain([scene, 'optionalAccess',
|
|
683
|
+
if (_optionalChain([selection, 'optionalAccess', _33 => _33.kind]) !== "point" || _optionalChain([scene, 'optionalAccess', _34 => _34.percent])) return false;
|
|
684
|
+
const secondary = _optionalChain([controller, 'access', _35 => _35.getSeries, 'call', _36 => _36(selection.seriesId), 'optionalAccess', _37 => _37.axis]) === "secondary";
|
|
685
|
+
const ticks = _nullishCoalesce(_nullishCoalesce(_optionalChain([(secondary ? _optionalChain([scene, 'optionalAccess', _38 => _38.valueScale2]) : null), 'optionalAccess', _39 => _39.ticks]), () => ( _optionalChain([scene, 'optionalAccess', _40 => _40.valueScale, 'optionalAccess', _41 => _41.ticks]))), () => ( []));
|
|
4014
686
|
const step = ticks.length > 1 ? ticks[1] - ticks[0] : 1;
|
|
4015
687
|
const amount = event.shiftKey ? step : event.altKey ? step / 100 : step / 10;
|
|
4016
688
|
controller.nudgeValue(selection.seriesId, selection.index, amount * (event.key === "ArrowUp" ? 1 : -1));
|
|
@@ -4033,22 +705,22 @@ function describeChartSelection(controller, selection = controller.getState().se
|
|
|
4033
705
|
case "legend":
|
|
4034
706
|
return "Legend";
|
|
4035
707
|
case "axis":
|
|
4036
|
-
return selection.axis === "x" ?
|
|
708
|
+
return selection.axis === "x" ? _chunkPRSJJZBScjs.chartFamily.call(void 0, spec) === "xy" ? "X axis" : "Category axis" : selection.axis === "y" ? "Value axis" : "Secondary value axis";
|
|
4037
709
|
case "series":
|
|
4038
|
-
return `Series "${_nullishCoalesce(_optionalChain([controller, 'access',
|
|
710
|
+
return `Series "${_nullishCoalesce(_optionalChain([controller, 'access', _42 => _42.getSeries, 'call', _43 => _43(selection.seriesId), 'optionalAccess', _44 => _44.name]), () => ( selection.seriesId))}"`;
|
|
4039
711
|
case "category":
|
|
4040
712
|
return `Category "${_nullishCoalesce(spec.categories[selection.index], () => ( selection.index))}"`;
|
|
4041
713
|
case "trendline": {
|
|
4042
714
|
const series = controller.getSeries(selection.seriesId);
|
|
4043
|
-
const line = _optionalChain([series, 'optionalAccess',
|
|
4044
|
-
return `${line ? line.type.replace("-", " ") : "Trendline"} trendline on "${_nullishCoalesce(_optionalChain([series, 'optionalAccess',
|
|
715
|
+
const line = _optionalChain([series, 'optionalAccess', _45 => _45.trendlines, 'optionalAccess', _46 => _46[selection.index]]);
|
|
716
|
+
return `${line ? line.type.replace("-", " ") : "Trendline"} trendline on "${_nullishCoalesce(_optionalChain([series, 'optionalAccess', _47 => _47.name]), () => ( selection.seriesId))}"`;
|
|
4045
717
|
}
|
|
4046
718
|
case "point": {
|
|
4047
719
|
const series = controller.getSeries(selection.seriesId);
|
|
4048
|
-
const value = _optionalChain([series, 'optionalAccess',
|
|
4049
|
-
const x = _optionalChain([series, 'optionalAccess',
|
|
4050
|
-
if (x !== void 0 && x !== null &&
|
|
4051
|
-
return `${_nullishCoalesce(_optionalChain([series, 'optionalAccess',
|
|
720
|
+
const value = _optionalChain([series, 'optionalAccess', _48 => _48.values, 'access', _49 => _49[selection.index]]);
|
|
721
|
+
const x = _optionalChain([series, 'optionalAccess', _50 => _50.x, 'optionalAccess', _51 => _51[selection.index]]);
|
|
722
|
+
if (x !== void 0 && x !== null && _chunkPRSJJZBScjs.chartFamily.call(void 0, spec) === "xy") return `${_nullishCoalesce(_optionalChain([series, 'optionalAccess', _52 => _52.name]), () => ( selection.seriesId))}: (${x}, ${_nullishCoalesce(value, () => ( "blank"))})`;
|
|
723
|
+
return `${_nullishCoalesce(_optionalChain([series, 'optionalAccess', _53 => _53.name]), () => ( selection.seriesId))} \xB7 ${_nullishCoalesce(spec.categories[selection.index], () => ( selection.index))}: ${_nullishCoalesce(value, () => ( "blank"))}`;
|
|
4052
724
|
}
|
|
4053
725
|
}
|
|
4054
726
|
}
|
|
@@ -4093,11 +765,11 @@ var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, o
|
|
|
4093
765
|
var WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
4094
766
|
var WRITE_IDEMPOTENT = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
4095
767
|
var DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false };
|
|
4096
|
-
var
|
|
768
|
+
var fail2 = (message) => {
|
|
4097
769
|
throw new Error(message);
|
|
4098
770
|
};
|
|
4099
771
|
function resolve(result) {
|
|
4100
|
-
if (!result.ok)
|
|
772
|
+
if (!result.ok) fail2(_nullishCoalesce(result.message, () => ( "The change was rejected.")));
|
|
4101
773
|
return result;
|
|
4102
774
|
}
|
|
4103
775
|
function createChartToolProvider(controller, options = {}) {
|
|
@@ -4110,23 +782,23 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4110
782
|
const spec = () => controller.getSpec();
|
|
4111
783
|
const seriesById = (ref) => {
|
|
4112
784
|
const s = _nullishCoalesce(spec().series.find((x) => x.id === ref), () => ( spec().series.find((x) => x.name === ref)));
|
|
4113
|
-
if (!s)
|
|
785
|
+
if (!s) fail2(`There is no series "${ref}"; expected one of ${spec().series.map((x) => `"${x.id}" (${x.name})`).join(", ") || "(none yet; add one with add_chart_series)"}.`);
|
|
4114
786
|
return s;
|
|
4115
787
|
};
|
|
4116
788
|
const categoryIndex = (ref) => {
|
|
4117
789
|
const categories = spec().categories;
|
|
4118
790
|
const index = typeof ref === "number" ? ref : categories.indexOf(ref);
|
|
4119
791
|
if (!Number.isInteger(index) || index < 0 || index >= categories.length)
|
|
4120
|
-
|
|
792
|
+
fail2(`There is no category ${JSON.stringify(ref)}; expected an index from 0 to ${categories.length - 1} or one of ${categories.slice(0, 20).map((c) => JSON.stringify(c)).join(", ")}.`);
|
|
4121
793
|
return index;
|
|
4122
794
|
};
|
|
4123
|
-
const table = () =>
|
|
795
|
+
const table = () => _chunkPRSJJZBScjs.chartDataToTable.call(void 0, spec());
|
|
4124
796
|
const Parser = () => _nullishCoalesce(options.DOMParser, () => ( globalThis.DOMParser));
|
|
4125
797
|
const getChart = {
|
|
4126
798
|
name: "get_chart",
|
|
4127
799
|
title: "Read the chart",
|
|
4128
800
|
description: "Returns the chart spec (type, titles, axes, legend, series with ids and settings), its data as a table (header row of series names, then one row per category), the current selection, and a summary at the given size: per-series min / max / mean / trend, the axes as drawn, layout warnings (hidden labels, thinned lines), a colour check and a plain-language description. Use the series ids and category indexes it returns with the write tools.",
|
|
4129
|
-
inputSchema: { type: "object", properties: { width: WIDTH, height: HEIGHT }, additionalProperties: false },
|
|
801
|
+
inputSchema: { type: "object", properties: { width: _chunkU6IZKC7Bcjs.WIDTH, height: _chunkU6IZKC7Bcjs.HEIGHT }, additionalProperties: false },
|
|
4130
802
|
annotations: { title: "Read the chart", ...READ },
|
|
4131
803
|
handler: ({ width = size.width, height = size.height }) => {
|
|
4132
804
|
const reason = readOnlyReason();
|
|
@@ -4134,7 +806,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4134
806
|
spec: controller.toJSON(),
|
|
4135
807
|
table: table(),
|
|
4136
808
|
selection: controller.getState().selection,
|
|
4137
|
-
summary: summarizeChart(controller.toJSON(), { width, height }),
|
|
809
|
+
summary: _chunkPRSJJZBScjs.summarizeChart.call(void 0, controller.toJSON(), { width, height }),
|
|
4138
810
|
issues: controller.getState().issues,
|
|
4139
811
|
...reason ? { readOnly: reason } : {}
|
|
4140
812
|
};
|
|
@@ -4146,14 +818,14 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4146
818
|
description: 'Draws the chart without changing it, to check it visually: SVG text by default, or a PNG image (format "png") when the host can rasterize. Look for overlapping or hidden labels; get_chart lists layout warnings without a render.',
|
|
4147
819
|
inputSchema: {
|
|
4148
820
|
type: "object",
|
|
4149
|
-
properties: { width: WIDTH, height: HEIGHT, format: { type: "string", enum: ["svg", "png"], description: "svg (default) or png (only when the host supports it)." } },
|
|
821
|
+
properties: { width: _chunkU6IZKC7Bcjs.WIDTH, height: _chunkU6IZKC7Bcjs.HEIGHT, format: { type: "string", enum: ["svg", "png"], description: "svg (default) or png (only when the host supports it)." } },
|
|
4150
822
|
additionalProperties: false
|
|
4151
823
|
},
|
|
4152
824
|
annotations: { title: "Render the chart", ...READ },
|
|
4153
825
|
handler: async ({ width = size.width, height = size.height, format = "svg" }) => {
|
|
4154
|
-
const svg = chartToSvg(controller.toJSON(), { width, height });
|
|
826
|
+
const svg = _chunkPRSJJZBScjs.chartToSvg.call(void 0, controller.toJSON(), { width, height });
|
|
4155
827
|
if (format === "png") {
|
|
4156
|
-
if (!options.rasterize)
|
|
828
|
+
if (!options.rasterize) fail2('This host cannot rasterize charts; call render_chart with format "svg".');
|
|
4157
829
|
const image = await options.rasterize(svg, { width, height });
|
|
4158
830
|
return { content: [{ type: "image", data: image.data, mimeType: image.mimeType }], structuredContent: { width, height, mimeType: image.mimeType } };
|
|
4159
831
|
}
|
|
@@ -4176,8 +848,8 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4176
848
|
handler: ({ colors, surface }) => {
|
|
4177
849
|
const s = spec();
|
|
4178
850
|
const visible = s.series.filter((x) => !x.hidden);
|
|
4179
|
-
const own =
|
|
4180
|
-
return { colors: _nullishCoalesce(colors, () => ( own)), ...
|
|
851
|
+
const own = _chunkPRSJJZBScjs.chartColorsByCategory.call(void 0, s.type) ? s.categories.map((_, i) => _optionalChain([visible, 'access', _54 => _54[0], 'optionalAccess', _55 => _55.pointColors, 'optionalAccess', _56 => _56[i]]) || _chunkPRSJJZBScjs.chartSliceColor.call(void 0, s, i)) : visible.map((x) => _chunkPRSJJZBScjs.chartSeriesColor.call(void 0, s, s.series.indexOf(x)));
|
|
852
|
+
return { colors: _nullishCoalesce(colors, () => ( own)), ..._chunkPRSJJZBScjs.checkChartColors.call(void 0, _nullishCoalesce(colors, () => ( own)), { surface }) };
|
|
4181
853
|
}
|
|
4182
854
|
};
|
|
4183
855
|
const recommend = {
|
|
@@ -4189,7 +861,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4189
861
|
handler: ({ csv }) => {
|
|
4190
862
|
const rows = csv !== void 0 ? _chunkTNNJXF46cjs.parseCsv.call(void 0, csv, { delimiter: csv.includes(" ") ? " " : "," }) : table();
|
|
4191
863
|
const recommendations = recommendChartTypes(rows);
|
|
4192
|
-
if (!recommendations.length)
|
|
864
|
+
if (!recommendations.length) fail2("There are no numbers to chart: give CSV with at least one numeric column.");
|
|
4193
865
|
return { recommendations };
|
|
4194
866
|
}
|
|
4195
867
|
};
|
|
@@ -4199,18 +871,18 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4199
871
|
description: "Returns a trendline's fitted coefficients (in x for category charts, x = 1, 2, 3, \u2026), R\xB2, equation and the number of points, or the moving-average points.",
|
|
4200
872
|
inputSchema: {
|
|
4201
873
|
type: "object",
|
|
4202
|
-
properties: { series: SERIES_REF, index: { type: "integer", minimum: 0, description: "Trendline index on the series (0 = first)." } },
|
|
874
|
+
properties: { series: _chunkU6IZKC7Bcjs.SERIES_REF, index: { type: "integer", minimum: 0, description: "Trendline index on the series (0 = first)." } },
|
|
4203
875
|
required: ["series", "index"],
|
|
4204
876
|
additionalProperties: false
|
|
4205
877
|
},
|
|
4206
878
|
annotations: { title: "Read a trendline fit", ...READ },
|
|
4207
879
|
handler: ({ series, index }) => {
|
|
4208
880
|
const s = seriesById(series);
|
|
4209
|
-
const line = _optionalChain([s, 'access',
|
|
4210
|
-
if (!line)
|
|
881
|
+
const line = _optionalChain([s, 'access', _57 => _57.trendlines, 'optionalAccess', _58 => _58[index]]);
|
|
882
|
+
if (!line) fail2(`"${s.name}" has no trendline ${index}; it has ${_nullishCoalesce(_optionalChain([s, 'access', _59 => _59.trendlines, 'optionalAccess', _60 => _60.length]), () => ( 0))}.`);
|
|
4211
883
|
if (line.type === "moving-average") return { trendline: line, points: controller.trendlineMovingAverage(s.id, index) };
|
|
4212
884
|
const fit = controller.trendlineFit(s.id, index);
|
|
4213
|
-
if (!fit)
|
|
885
|
+
if (!fit) fail2(`The ${line.type} trendline on "${s.name}" cannot be fitted: it needs more points (and positive values for log, exponential and power fits).`);
|
|
4214
886
|
return { trendline: line, coefficients: fit.coefficients, r2: fit.r2, equation: fit.equation, points: fit.points };
|
|
4215
887
|
}
|
|
4216
888
|
};
|
|
@@ -4221,8 +893,8 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4221
893
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
4222
894
|
annotations: { title: "List chart presets", ...READ },
|
|
4223
895
|
handler: () => ({
|
|
4224
|
-
layouts: CHART_LAYOUT_PRESETS.map(({ id, label, description }) => ({ id, label, description })),
|
|
4225
|
-
colorSchemes: CHART_COLOR_SCHEMES.map(({ id, label, description, palette }) => ({ id, label, description, palette }))
|
|
896
|
+
layouts: _chunkPRSJJZBScjs.CHART_LAYOUT_PRESETS.map(({ id, label, description }) => ({ id, label, description })),
|
|
897
|
+
colorSchemes: _chunkPRSJJZBScjs.CHART_COLOR_SCHEMES.map(({ id, label, description, palette }) => ({ id, label, description, palette }))
|
|
4226
898
|
})
|
|
4227
899
|
};
|
|
4228
900
|
const exportMermaid = {
|
|
@@ -4231,7 +903,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4231
903
|
description: 'Returns the chart as Mermaid text (xychart-beta, pie or radar-beta) for Markdown or chat. With meta (default true) one "%% @pixerate {\u2026}" comment keeps what Mermaid cannot express, so import_chart_mermaid gets the chart back exactly.',
|
|
4232
904
|
inputSchema: { type: "object", properties: { meta: { type: "boolean", description: "Include the round-trip comment (default true)." } }, additionalProperties: false },
|
|
4233
905
|
annotations: { title: "Export chart as Mermaid", ...READ },
|
|
4234
|
-
handler: ({ meta = true }) => ({ mermaid: chartToMermaid(controller.toJSON(), { meta }) })
|
|
906
|
+
handler: ({ meta = true }) => ({ mermaid: _chunkPRSJJZBScjs.chartToMermaid.call(void 0, controller.toJSON(), { meta }) })
|
|
4235
907
|
};
|
|
4236
908
|
const exportOoxml = {
|
|
4237
909
|
name: "export_chart_ooxml",
|
|
@@ -4239,7 +911,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4239
911
|
description: "Returns the chart as a DrawingML chart part (c:chartSpace) for Word or PowerPoint files, with what could not be written as sentences.",
|
|
4240
912
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
4241
913
|
annotations: { title: "Export chart as Office XML", ...READ },
|
|
4242
|
-
handler: () =>
|
|
914
|
+
handler: () => _chunkPIMCHMZEcjs.chartToOoxml.call(void 0, controller.toJSON())
|
|
4243
915
|
};
|
|
4244
916
|
const updateChart = {
|
|
4245
917
|
name: "update_chart",
|
|
@@ -4247,12 +919,12 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4247
919
|
description: "Changes how the chart looks, as one undo step: type, titles, axes (xAxis = categories or numeric x, yAxis = values, yAxis2 = secondary values), legend, stacking, orientation, curve, points, data labels and their position, palette, donut hole, bar spacing, histogram bins, waterfall total. All-or-nothing: invalid values change nothing and come back as sentences. Returns the new spec.",
|
|
4248
920
|
inputSchema: {
|
|
4249
921
|
type: "object",
|
|
4250
|
-
properties: CHART_UPDATE_PROPERTIES,
|
|
922
|
+
properties: _chunkU6IZKC7Bcjs.CHART_UPDATE_PROPERTIES,
|
|
4251
923
|
additionalProperties: false
|
|
4252
924
|
},
|
|
4253
925
|
annotations: { title: "Change chart presentation", ...WRITE_IDEMPOTENT },
|
|
4254
926
|
handler: (args) => {
|
|
4255
|
-
if (!Object.keys(args).length)
|
|
927
|
+
if (!Object.keys(args).length) fail2('Nothing to change: pass at least one field, e.g. {"type": "line"}.');
|
|
4256
928
|
resolve(controller.update(args));
|
|
4257
929
|
return { spec: controller.toJSON() };
|
|
4258
930
|
}
|
|
@@ -4268,14 +940,14 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4268
940
|
series: {
|
|
4269
941
|
type: "array",
|
|
4270
942
|
description: "The new series (replaces all).",
|
|
4271
|
-
items: { type: "object", properties: { id: { type: "string", description: "Stable id (optional)." }, ...SERIES_PROPS }, required: ["name", "values"], additionalProperties: false }
|
|
943
|
+
items: { type: "object", properties: { id: { type: "string", description: "Stable id (optional)." }, ..._chunkU6IZKC7Bcjs.SERIES_PROPS }, required: ["name", "values"], additionalProperties: false }
|
|
4272
944
|
}
|
|
4273
945
|
},
|
|
4274
946
|
additionalProperties: false
|
|
4275
947
|
},
|
|
4276
948
|
annotations: { title: "Replace chart data", ...DESTRUCTIVE },
|
|
4277
949
|
handler: (args) => {
|
|
4278
|
-
if (!args.categories && !args.series)
|
|
950
|
+
if (!args.categories && !args.series) fail2("Pass categories, series or both.");
|
|
4279
951
|
resolve(controller.setData(args));
|
|
4280
952
|
return { table: table(), series: spec().series.map((s) => ({ id: s.id, name: s.name })) };
|
|
4281
953
|
}
|
|
@@ -4291,7 +963,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4291
963
|
type: "array",
|
|
4292
964
|
minItems: 1,
|
|
4293
965
|
description: "Values to set.",
|
|
4294
|
-
items: { type: "object", properties: { series: SERIES_REF, category: CATEGORY_REF, value: VALUE }, required: ["series", "category", "value"], additionalProperties: false }
|
|
966
|
+
items: { type: "object", properties: { series: _chunkU6IZKC7Bcjs.SERIES_REF, category: _chunkU6IZKC7Bcjs.CATEGORY_REF, value: _chunkU6IZKC7Bcjs.VALUE }, required: ["series", "category", "value"], additionalProperties: false }
|
|
4295
967
|
}
|
|
4296
968
|
},
|
|
4297
969
|
required: ["cells"],
|
|
@@ -4300,13 +972,13 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4300
972
|
annotations: { title: "Set chart values", ...WRITE_IDEMPOTENT },
|
|
4301
973
|
handler: async ({ cells }) => {
|
|
4302
974
|
const resolved = cells.map((c) => ({ seriesId: seriesById(c.series).id, index: categoryIndex(c.category), value: c.value }));
|
|
4303
|
-
const parsed = resolved.map((c) =>
|
|
975
|
+
const parsed = resolved.map((c) => _chunkPRSJJZBScjs.parseChartValue.call(void 0, c.value));
|
|
4304
976
|
const bad = resolved.find((_, i) => parsed[i] === void 0);
|
|
4305
|
-
if (bad)
|
|
977
|
+
if (bad) fail2(`"${String(bad.value)}" is not a number; pass a number, numeric text or null for a gap. Nothing was changed.`);
|
|
4306
978
|
if (options.director && parsed.every((v) => typeof v === "number")) {
|
|
4307
979
|
const results = await options.director.perform(_nullishCoalesce(options.actor, () => ( { id: "agent", name: "Agent" })), resolved.map((c, i) => ({ kind: "setValue", seriesId: c.seriesId, index: c.index, value: parsed[i] })));
|
|
4308
980
|
const failed = results.find((r) => !r.ok);
|
|
4309
|
-
if (failed && !failed.ok)
|
|
981
|
+
if (failed && !failed.ok) fail2(failed.message);
|
|
4310
982
|
} else resolve(controller.setValues(resolved));
|
|
4311
983
|
return { cells: resolved.map((c) => ({ series: c.seriesId, category: spec().categories[c.index], index: c.index, value: controller.getSeries(c.seriesId).values[c.index] })) };
|
|
4312
984
|
}
|
|
@@ -4317,14 +989,14 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4317
989
|
description: "Adds a series (values padded to the categories) and selects it. Returns its id and stored values.",
|
|
4318
990
|
inputSchema: {
|
|
4319
991
|
type: "object",
|
|
4320
|
-
properties: { ...SERIES_PROPS, id: { type: "string", description: "Id to use (default: series-N)." }, at: { type: "integer", minimum: 0, description: "Position (default: last)." } },
|
|
992
|
+
properties: { ..._chunkU6IZKC7Bcjs.SERIES_PROPS, id: { type: "string", description: "Id to use (default: series-N)." }, at: { type: "integer", minimum: 0, description: "Position (default: last)." } },
|
|
4321
993
|
required: ["name"],
|
|
4322
994
|
additionalProperties: false
|
|
4323
995
|
},
|
|
4324
996
|
annotations: { title: "Add a series", ...WRITE },
|
|
4325
997
|
handler: ({ at, ...input }) => {
|
|
4326
|
-
const
|
|
4327
|
-
const result = resolve(controller.addSeries(
|
|
998
|
+
const clean = Object.fromEntries(Object.entries(input).filter(([, v]) => v !== null));
|
|
999
|
+
const result = resolve(controller.addSeries(clean, { at }));
|
|
4328
1000
|
const s = controller.getSeries(result.id);
|
|
4329
1001
|
return { id: s.id, name: s.name, values: s.values };
|
|
4330
1002
|
}
|
|
@@ -4335,14 +1007,14 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4335
1007
|
description: "Renames, recolours, hides, moves to the secondary axis or changes how a series is drawn (marker, curve, labels, format, x values, bubble sizes, point colours). null resets a field. Values change with set_chart_values. Returns the series.",
|
|
4336
1008
|
inputSchema: {
|
|
4337
1009
|
type: "object",
|
|
4338
|
-
properties: { series: SERIES_REF, ...Object.fromEntries(Object.entries(SERIES_PROPS).filter(([k]) => k !== "values")) },
|
|
1010
|
+
properties: { series: _chunkU6IZKC7Bcjs.SERIES_REF, ...Object.fromEntries(Object.entries(_chunkU6IZKC7Bcjs.SERIES_PROPS).filter(([k]) => k !== "values")) },
|
|
4339
1011
|
required: ["series"],
|
|
4340
1012
|
additionalProperties: false
|
|
4341
1013
|
},
|
|
4342
1014
|
annotations: { title: "Change a series", ...WRITE_IDEMPOTENT },
|
|
4343
1015
|
handler: ({ series, ...patch }) => {
|
|
4344
1016
|
const id = seriesById(series).id;
|
|
4345
|
-
if (!Object.keys(patch).length)
|
|
1017
|
+
if (!Object.keys(patch).length) fail2('Nothing to change: pass a field such as {"color": "#e8710a"} or {"axis": "secondary"}.');
|
|
4346
1018
|
resolve(controller.updateSeries(id, patch));
|
|
4347
1019
|
return { series: controller.getSeries(id) };
|
|
4348
1020
|
}
|
|
@@ -4351,7 +1023,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4351
1023
|
name: "remove_chart_series",
|
|
4352
1024
|
title: "Delete a series",
|
|
4353
1025
|
description: "Deletes a series and its values. Returns the remaining series ids.",
|
|
4354
|
-
inputSchema: { type: "object", properties: { series: SERIES_REF }, required: ["series"], additionalProperties: false },
|
|
1026
|
+
inputSchema: { type: "object", properties: { series: _chunkU6IZKC7Bcjs.SERIES_REF }, required: ["series"], additionalProperties: false },
|
|
4355
1027
|
annotations: { title: "Delete a series", ...DESTRUCTIVE },
|
|
4356
1028
|
handler: ({ series }) => {
|
|
4357
1029
|
resolve(controller.removeSeries(seriesById(series).id));
|
|
@@ -4377,7 +1049,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4377
1049
|
name: "remove_chart_category",
|
|
4378
1050
|
title: "Delete a category",
|
|
4379
1051
|
description: "Deletes a category and its value in every series. Returns the remaining categories.",
|
|
4380
|
-
inputSchema: { type: "object", properties: { category: CATEGORY_REF }, required: ["category"], additionalProperties: false },
|
|
1052
|
+
inputSchema: { type: "object", properties: { category: _chunkU6IZKC7Bcjs.CATEGORY_REF }, required: ["category"], additionalProperties: false },
|
|
4381
1053
|
annotations: { title: "Delete a category", ...DESTRUCTIVE },
|
|
4382
1054
|
handler: ({ category }) => {
|
|
4383
1055
|
resolve(controller.removeCategory(categoryIndex(category)));
|
|
@@ -4404,7 +1076,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4404
1076
|
if (chartType === "auto") {
|
|
4405
1077
|
const rows = _chunkTNNJXF46cjs.parseCsv.call(void 0, csv, { delimiter: csv.includes(" ") ? " " : "," });
|
|
4406
1078
|
const built = chartFromTable(rows);
|
|
4407
|
-
if (!built.series.length || !built.categories.length)
|
|
1079
|
+
if (!built.series.length || !built.categories.length) fail2("There is no data to import: expected at least one row of numbers.");
|
|
4408
1080
|
resolve(controller.update({ type: built.type, stacking: built.stacking, categories: built.categories, series: built.series }, { label: "Import data" }));
|
|
4409
1081
|
return { type: built.type, table: table(), issues: built.issues };
|
|
4410
1082
|
}
|
|
@@ -4420,8 +1092,8 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4420
1092
|
annotations: { title: "Import a Mermaid chart", ...DESTRUCTIVE },
|
|
4421
1093
|
handler: ({ mermaid }) => {
|
|
4422
1094
|
const fenced = /```(?:mermaid)?\s*\n([\s\S]*?)```/.exec(mermaid);
|
|
4423
|
-
const read = chartFromMermaid(fenced ? fenced[1] : mermaid);
|
|
4424
|
-
if (!read.family)
|
|
1095
|
+
const read = _chunkPRSJJZBScjs.chartFromMermaid.call(void 0, fenced ? fenced[1] : mermaid);
|
|
1096
|
+
if (!read.family) fail2(read.issues.map((i) => i.message).join(" ") || "Expected Mermaid starting with xychart-beta, pie or radar-beta.");
|
|
4425
1097
|
resolve(controller.update({ ...read.spec }, { label: "Import Mermaid chart" }));
|
|
4426
1098
|
return { spec: controller.toJSON(), issues: read.issues };
|
|
4427
1099
|
}
|
|
@@ -4433,8 +1105,8 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4433
1105
|
inputSchema: { type: "object", properties: { xml: { type: "string", description: "The chart part XML, e.g. word/charts/chart1.xml." } }, required: ["xml"], additionalProperties: false },
|
|
4434
1106
|
annotations: { title: "Import an Office chart", ...DESTRUCTIVE },
|
|
4435
1107
|
handler: ({ xml }) => {
|
|
4436
|
-
const read =
|
|
4437
|
-
if (read.issues.some((i) => i.path === "" || i.path === "plotArea"))
|
|
1108
|
+
const read = _chunkPIMCHMZEcjs.chartFromOoxml.call(void 0, xml, { DOMParser: Parser() });
|
|
1109
|
+
if (read.issues.some((i) => i.path === "" || i.path === "plotArea")) fail2(read.issues.map((i) => i.message).join(" "));
|
|
4438
1110
|
resolve(controller.update({ ...read.spec }, { label: "Import Office chart" }));
|
|
4439
1111
|
return { spec: controller.toJSON(), issues: read.issues.map((i) => i.message), unsupported: read.unsupported, lossless: read.lossless };
|
|
4440
1112
|
}
|
|
@@ -4443,7 +1115,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4443
1115
|
name: "add_chart_trendline",
|
|
4444
1116
|
title: "Add a trendline",
|
|
4445
1117
|
description: "Fits a trendline to a series and draws it (unstacked bar, line, area, scatter and bubble charts). Returns its index and the fit: coefficients, R\xB2 and equation.",
|
|
4446
|
-
inputSchema: { type: "object", properties: { series: SERIES_REF, ...TRENDLINE_PROPS }, required: ["series", "type"], additionalProperties: false },
|
|
1118
|
+
inputSchema: { type: "object", properties: { series: _chunkU6IZKC7Bcjs.SERIES_REF, ..._chunkU6IZKC7Bcjs.TRENDLINE_PROPS }, required: ["series", "type"], additionalProperties: false },
|
|
4447
1119
|
annotations: { title: "Add a trendline", ...WRITE },
|
|
4448
1120
|
handler: ({ series, ...line }) => {
|
|
4449
1121
|
const s = seriesById(series);
|
|
@@ -4457,7 +1129,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4457
1129
|
description: "Deletes a trendline from a series. Returns the series' remaining trendlines.",
|
|
4458
1130
|
inputSchema: {
|
|
4459
1131
|
type: "object",
|
|
4460
|
-
properties: { series: SERIES_REF, index: { type: "integer", minimum: 0, description: "Trendline index on the series (0 = first)." } },
|
|
1132
|
+
properties: { series: _chunkU6IZKC7Bcjs.SERIES_REF, index: { type: "integer", minimum: 0, description: "Trendline index on the series (0 = first)." } },
|
|
4461
1133
|
required: ["series", "index"],
|
|
4462
1134
|
additionalProperties: false
|
|
4463
1135
|
},
|
|
@@ -4465,7 +1137,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4465
1137
|
handler: ({ series, index }) => {
|
|
4466
1138
|
const s = seriesById(series);
|
|
4467
1139
|
resolve(controller.removeTrendline(s.id, index));
|
|
4468
|
-
return { series: s.id, trendlines: _nullishCoalesce(_optionalChain([controller, 'access',
|
|
1140
|
+
return { series: s.id, trendlines: _nullishCoalesce(_optionalChain([controller, 'access', _61 => _61.getSeries, 'call', _62 => _62(s.id), 'optionalAccess', _63 => _63.trendlines]), () => ( [])) };
|
|
4469
1141
|
}
|
|
4470
1142
|
};
|
|
4471
1143
|
const applyPreset = {
|
|
@@ -4475,8 +1147,8 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4475
1147
|
inputSchema: {
|
|
4476
1148
|
type: "object",
|
|
4477
1149
|
properties: {
|
|
4478
|
-
layout: { type: "string", enum: CHART_LAYOUT_PRESETS.map((p) => p.id), description: "Quick layout id." },
|
|
4479
|
-
colors: { type: "string", enum: CHART_COLOR_SCHEMES.map((p) => p.id), description: "Colour scheme id." }
|
|
1150
|
+
layout: { type: "string", enum: _chunkPRSJJZBScjs.CHART_LAYOUT_PRESETS.map((p) => p.id), description: "Quick layout id." },
|
|
1151
|
+
colors: { type: "string", enum: _chunkPRSJJZBScjs.CHART_COLOR_SCHEMES.map((p) => p.id), description: "Colour scheme id." }
|
|
4480
1152
|
},
|
|
4481
1153
|
additionalProperties: false
|
|
4482
1154
|
},
|
|
@@ -4505,8 +1177,8 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4505
1177
|
type: "object",
|
|
4506
1178
|
properties: {
|
|
4507
1179
|
kind: { type: "string", enum: ["none", "chart", "title", "subtitle", "legend", "axis", "series", "point", "category", "trendline"], description: "What to select." },
|
|
4508
|
-
series: { ...SERIES_REF, description: "Series id or name (series, point, trendline)." },
|
|
4509
|
-
category: { ...CATEGORY_REF, description: "Category index or label (point, category)." },
|
|
1180
|
+
series: { ..._chunkU6IZKC7Bcjs.SERIES_REF, description: "Series id or name (series, point, trendline)." },
|
|
1181
|
+
category: { ..._chunkU6IZKC7Bcjs.CATEGORY_REF, description: "Category index or label (point, category)." },
|
|
4510
1182
|
axis: { type: "string", enum: ["x", "y", "y2"], description: "Axis (axis)." },
|
|
4511
1183
|
index: { type: "integer", minimum: 0, description: "Trendline index (trendline)." }
|
|
4512
1184
|
},
|
|
@@ -4516,7 +1188,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4516
1188
|
annotations: { title: "Select a chart element", ...WRITE_IDEMPOTENT },
|
|
4517
1189
|
handler: async ({ kind, series, category, axis, index }) => {
|
|
4518
1190
|
let sel;
|
|
4519
|
-
const need = (v, what) => v === void 0 &&
|
|
1191
|
+
const need = (v, what) => v === void 0 && fail2(`kind "${kind}" needs ${what}.`);
|
|
4520
1192
|
switch (kind) {
|
|
4521
1193
|
case "none":
|
|
4522
1194
|
sel = null;
|
|
@@ -4549,7 +1221,7 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4549
1221
|
if (options.director) await options.director.perform(_nullishCoalesce(options.actor, () => ( { id: "agent", name: "Agent" })), [{ kind: "select", selection: sel }]);
|
|
4550
1222
|
else controller.select(sel);
|
|
4551
1223
|
const now = controller.getState().selection;
|
|
4552
|
-
if (sel && !now)
|
|
1224
|
+
if (sel && !now) fail2(`Nothing to select there (e.g. the chart has no ${kind} of that kind).`);
|
|
4553
1225
|
return { selection: now };
|
|
4554
1226
|
}
|
|
4555
1227
|
};
|
|
@@ -4564,22 +1236,22 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4564
1236
|
to: { type: "object", description: "End, as fractions of the chart.", properties: { x: { type: "number", minimum: 0, maximum: 1, description: "Fraction of the width." }, y: { type: "number", minimum: 0, maximum: 1, description: "Fraction of the height." } }, required: ["x", "y"], additionalProperties: false },
|
|
4565
1237
|
fine: { type: "boolean", description: "Move a tenth as far (Alt)." },
|
|
4566
1238
|
lockAxis: { type: "boolean", description: "Scatter charts: change only the axis moved along most (Shift)." },
|
|
4567
|
-
width: WIDTH,
|
|
4568
|
-
height: HEIGHT
|
|
1239
|
+
width: _chunkU6IZKC7Bcjs.WIDTH,
|
|
1240
|
+
height: _chunkU6IZKC7Bcjs.HEIGHT
|
|
4569
1241
|
},
|
|
4570
1242
|
required: ["from", "to"],
|
|
4571
1243
|
additionalProperties: false
|
|
4572
1244
|
},
|
|
4573
1245
|
annotations: { title: "Drag a chart point", ...WRITE },
|
|
4574
1246
|
handler: ({ from, to, fine, lockAxis, width = size.width, height = size.height }) => {
|
|
4575
|
-
const scene = layoutChart(controller.toJSON(), { width, height });
|
|
1247
|
+
const scene = _chunkPRSJJZBScjs.layoutChart.call(void 0, controller.toJSON(), { width, height });
|
|
4576
1248
|
const start = { x: from.x * width, y: from.y * height };
|
|
4577
1249
|
const target = chartHitTest(scene, start);
|
|
4578
|
-
if (_optionalChain([target, 'optionalAccess',
|
|
1250
|
+
if (_optionalChain([target, 'optionalAccess', _64 => _64.kind]) !== "point") fail2(`There is no bar or point at (${from.x}, ${from.y}); ${target ? `that is the ${target.kind}` : "that is outside the chart"}. Use render_chart to find marks.`);
|
|
4579
1251
|
const t = target;
|
|
4580
|
-
const before = _nullishCoalesce(_optionalChain([controller, 'access',
|
|
1252
|
+
const before = _nullishCoalesce(_optionalChain([controller, 'access', _65 => _65.getSeries, 'call', _66 => _66(t.seriesId), 'optionalAccess', _67 => _67.values, 'access', _68 => _68[t.index]]), () => ( null));
|
|
4581
1253
|
const session = startChartPointDrag(controller, scene, t, start);
|
|
4582
|
-
if (!session)
|
|
1254
|
+
if (!session) fail2("That point cannot be dragged here (percent-stacked charts, pies and gaps cannot be dragged).");
|
|
4583
1255
|
session.move({ x: to.x * width, y: to.y * height }, { fine, lockAxis });
|
|
4584
1256
|
session.end();
|
|
4585
1257
|
const s = controller.getSeries(t.seriesId);
|
|
@@ -4602,10 +1274,10 @@ function createChartToolProvider(controller, options = {}) {
|
|
|
4602
1274
|
},
|
|
4603
1275
|
annotations: { title: "Press a key on the chart", ...DESTRUCTIVE },
|
|
4604
1276
|
handler: ({ key: k, shift, alt }) => {
|
|
4605
|
-
const scene = layoutChart(controller.toJSON(), size);
|
|
1277
|
+
const scene = _chunkPRSJJZBScjs.layoutChart.call(void 0, controller.toJSON(), size);
|
|
4606
1278
|
const used = handleChartKey(controller, scene, { key: k, shiftKey: shift, altKey: alt });
|
|
4607
1279
|
const sel = controller.getState().selection;
|
|
4608
|
-
const value = _optionalChain([sel, 'optionalAccess',
|
|
1280
|
+
const value = _optionalChain([sel, 'optionalAccess', _69 => _69.kind]) === "point" ? _nullishCoalesce(_optionalChain([controller, 'access', _70 => _70.getSeries, 'call', _71 => _71(sel.seriesId), 'optionalAccess', _72 => _72.values, 'access', _73 => _73[sel.index]]), () => ( null)) : void 0;
|
|
4609
1281
|
return { used, selection: sel, ...value !== void 0 ? { value } : {} };
|
|
4610
1282
|
}
|
|
4611
1283
|
};
|
|
@@ -4661,7 +1333,7 @@ function parsePartialChartJson(text) {
|
|
|
4661
1333
|
if (!src) return void 0;
|
|
4662
1334
|
try {
|
|
4663
1335
|
return JSON.parse(src);
|
|
4664
|
-
} catch (
|
|
1336
|
+
} catch (e2) {
|
|
4665
1337
|
}
|
|
4666
1338
|
const stack = [];
|
|
4667
1339
|
let inString = false;
|
|
@@ -4685,7 +1357,7 @@ function parsePartialChartJson(text) {
|
|
|
4685
1357
|
const candidate = trimmed + [...stack].reverse().join("");
|
|
4686
1358
|
try {
|
|
4687
1359
|
return JSON.parse(candidate);
|
|
4688
|
-
} catch (
|
|
1360
|
+
} catch (e3) {
|
|
4689
1361
|
const next = trimmed.replace(/,\s*$/, "").replace(/(?:,|\{)\s*"[^"]*"\s*:?\s*$/, (m) => m.trim().startsWith("{") ? "{" : "").replace(/:\s*$/, ": null").replace(/(?<=[\[,:]\s*)(-|tr?u?|fa?l?s?|nu?l?)$/, "").replace(/(?<=[\[,:]\s*)(-?\d+\.)$/, (m) => m.slice(0, -1));
|
|
4690
1362
|
if (next === trimmed) break;
|
|
4691
1363
|
out = next;
|
|
@@ -4693,14 +1365,14 @@ function parsePartialChartJson(text) {
|
|
|
4693
1365
|
}
|
|
4694
1366
|
return void 0;
|
|
4695
1367
|
}
|
|
4696
|
-
var ChartStream = (
|
|
4697
|
-
constructor(format = "auto") {;
|
|
1368
|
+
var ChartStream = (_class = class {
|
|
1369
|
+
constructor(format = "auto") {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);
|
|
4698
1370
|
this.format = format;
|
|
4699
1371
|
}
|
|
4700
1372
|
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
1373
|
+
__init() {this.lo = Infinity}
|
|
1374
|
+
__init2() {this.hi = -Infinity}
|
|
1375
|
+
__init3() {this.last = null}
|
|
4704
1376
|
/** Reads the source so far. `complete` when nothing more will arrive. */
|
|
4705
1377
|
update(source, complete = false) {
|
|
4706
1378
|
const text = source.replace(/^\s*```(?:mermaid|json)?\s*\n?/, "").replace(/```\s*$/, "");
|
|
@@ -4710,12 +1382,12 @@ var ChartStream = (_class2 = class {
|
|
|
4710
1382
|
if (format === "json") {
|
|
4711
1383
|
const value = complete ? safeParse(text) : parsePartialChartJson(text);
|
|
4712
1384
|
if (value !== void 0) {
|
|
4713
|
-
const read =
|
|
1385
|
+
const read = _chunkPRSJJZBScjs.normalizeChartSpec.call(void 0, value);
|
|
4714
1386
|
spec = read.spec;
|
|
4715
1387
|
issues = complete ? read.issues : [];
|
|
4716
1388
|
}
|
|
4717
1389
|
} else {
|
|
4718
|
-
const read = chartFromMermaid(text, { partial: !complete });
|
|
1390
|
+
const read = _chunkPRSJJZBScjs.chartFromMermaid.call(void 0, text, { partial: !complete });
|
|
4719
1391
|
if (read.family) {
|
|
4720
1392
|
spec = read.spec;
|
|
4721
1393
|
issues = complete ? read.issues : [];
|
|
@@ -4735,11 +1407,11 @@ var ChartStream = (_class2 = class {
|
|
|
4735
1407
|
this.hi = -Infinity;
|
|
4736
1408
|
this.last = null;
|
|
4737
1409
|
}
|
|
4738
|
-
},
|
|
1410
|
+
}, _class);
|
|
4739
1411
|
function safeParse(text) {
|
|
4740
1412
|
try {
|
|
4741
1413
|
return JSON.parse(text);
|
|
4742
|
-
} catch (
|
|
1414
|
+
} catch (e4) {
|
|
4743
1415
|
return parsePartialChartJson(text);
|
|
4744
1416
|
}
|
|
4745
1417
|
}
|
|
@@ -4788,11 +1460,11 @@ function blendChartScenes(from, to, t) {
|
|
|
4788
1460
|
var easeChartTween = (t) => 1 - Math.pow(1 - Math.max(0, Math.min(1, t)), 3);
|
|
4789
1461
|
|
|
4790
1462
|
// src/chart/presence.ts
|
|
4791
|
-
var ChartPresence = (
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
1463
|
+
var ChartPresence = (_class2 = class {constructor() { _class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this); }
|
|
1464
|
+
__init4() {this.state = { actors: [] }}
|
|
1465
|
+
__init5() {this.listeners = /* @__PURE__ */ new Set()}
|
|
1466
|
+
__init6() {this.getState = () => this.state}
|
|
1467
|
+
__init7() {this.subscribe = (listener) => {
|
|
4796
1468
|
this.listeners.add(listener);
|
|
4797
1469
|
return () => {
|
|
4798
1470
|
this.listeners.delete(listener);
|
|
@@ -4810,7 +1482,7 @@ var ChartPresence = (_class3 = class {constructor() { _class3.prototype.__init14
|
|
|
4810
1482
|
this.state = { actors: this.state.actors.filter((a) => a.id !== id) };
|
|
4811
1483
|
this.listeners.forEach((l) => l(this.state));
|
|
4812
1484
|
}
|
|
4813
|
-
},
|
|
1485
|
+
}, _class2);
|
|
4814
1486
|
var CHART_PRESENCE_COLORS = ["#7c3aed", "#db2777", "#0891b2", "#ea580c", "#16a34a"];
|
|
4815
1487
|
var realChartClock = {
|
|
4816
1488
|
now: () => Date.now(),
|
|
@@ -4827,7 +1499,7 @@ function createInstantChartClock(start = 0) {
|
|
|
4827
1499
|
};
|
|
4828
1500
|
}
|
|
4829
1501
|
function chartPointAnchor(scene, seriesId, index) {
|
|
4830
|
-
const mark = scene.marks.find((m) => (m.role === "bar" || m.role === "point") && _optionalChain([m, 'access',
|
|
1502
|
+
const mark = scene.marks.find((m) => (m.role === "bar" || m.role === "point") && _optionalChain([m, 'access', _74 => _74.target, 'optionalAccess', _75 => _75.kind]) === "point" && m.target.seriesId === seriesId && m.target.index === index);
|
|
4831
1503
|
if (!mark) return null;
|
|
4832
1504
|
if (mark.kind === "rect") return { x: mark.x + mark.width / 2, y: mark.y };
|
|
4833
1505
|
if (mark.kind === "circle") return { x: mark.cx, y: mark.cy };
|
|
@@ -4853,10 +1525,10 @@ function planChartCommand(spec, scene, command, speed = 1) {
|
|
|
4853
1525
|
case "setValue": {
|
|
4854
1526
|
const at = chartPointAnchor(scene, command.seriesId, command.index);
|
|
4855
1527
|
const series = spec.series.find((s) => s.id === command.seriesId);
|
|
4856
|
-
const from = _nullishCoalesce(_optionalChain([series, 'optionalAccess',
|
|
4857
|
-
const scale = _optionalChain([series, 'optionalAccess',
|
|
1528
|
+
const from = _nullishCoalesce(_optionalChain([series, 'optionalAccess', _76 => _76.values, 'access', _77 => _77[command.index]]), () => ( 0));
|
|
1529
|
+
const scale = _optionalChain([series, 'optionalAccess', _78 => _78.axis]) === "secondary" ? _nullishCoalesce(scene.valueScale2, () => ( scene.valueScale)) : scene.valueScale;
|
|
4858
1530
|
if (at && scale) {
|
|
4859
|
-
const px = linearMap(command.value, scale.domain, scale.range);
|
|
1531
|
+
const px = _chunkPRSJJZBScjs.linearMap.call(void 0, command.value, scale.domain, scale.range);
|
|
4860
1532
|
const to = scale.axis === "y" ? { x: at.x, y: px } : { x: px, y: at.y };
|
|
4861
1533
|
steps.push({ type: "move", to: at, ms: ms(380) }, { type: "press" }, { type: "drag", to, ms: ms(520), from, value: command.value }, { type: "release" });
|
|
4862
1534
|
} else steps.push({ type: "drag", to: { x: scene.plot.x + scene.plot.width / 2, y: scene.plot.y + scene.plot.height / 2 }, ms: ms(200), from, value: command.value });
|
|
@@ -4866,12 +1538,12 @@ function planChartCommand(spec, scene, command, speed = 1) {
|
|
|
4866
1538
|
case "select": {
|
|
4867
1539
|
const sel = command.selection;
|
|
4868
1540
|
let to = null;
|
|
4869
|
-
if (_optionalChain([sel, 'optionalAccess',
|
|
1541
|
+
if (_optionalChain([sel, 'optionalAccess', _79 => _79.kind]) === "point") to = chartPointAnchor(scene, sel.seriesId, sel.index);
|
|
4870
1542
|
else if (sel) {
|
|
4871
1543
|
const m = scene.marks.find((x) => x.target && JSON.stringify(x.target) === JSON.stringify(sel));
|
|
4872
|
-
if (_optionalChain([m, 'optionalAccess',
|
|
4873
|
-
else if (_optionalChain([m, 'optionalAccess',
|
|
4874
|
-
else if (_optionalChain([m, 'optionalAccess',
|
|
1544
|
+
if (_optionalChain([m, 'optionalAccess', _80 => _80.kind]) === "text") to = { x: m.x, y: m.y };
|
|
1545
|
+
else if (_optionalChain([m, 'optionalAccess', _81 => _81.kind]) === "rect") to = { x: m.x + m.width / 2, y: m.y + m.height / 2 };
|
|
1546
|
+
else if (_optionalChain([m, 'optionalAccess', _82 => _82.kind]) === "circle") to = { x: m.cx, y: m.cy };
|
|
4875
1547
|
}
|
|
4876
1548
|
if (to) steps.push({ type: "move", to, ms: ms(380) }, { type: "press" }, { type: "release" });
|
|
4877
1549
|
steps.push({ type: "commit" });
|
|
@@ -4886,8 +1558,8 @@ function planChartCommand(spec, scene, command, speed = 1) {
|
|
|
4886
1558
|
}
|
|
4887
1559
|
}
|
|
4888
1560
|
}
|
|
4889
|
-
var ChartDirector = (
|
|
4890
|
-
constructor(controller, presence, options) {;
|
|
1561
|
+
var ChartDirector = (_class3 = class {
|
|
1562
|
+
constructor(controller, presence, options) {;_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);
|
|
4891
1563
|
this.controller = controller;
|
|
4892
1564
|
this.presence = presence;
|
|
4893
1565
|
this.options = options;
|
|
@@ -4896,9 +1568,9 @@ var ChartDirector = (_class4 = class {
|
|
|
4896
1568
|
|
|
4897
1569
|
|
|
4898
1570
|
|
|
4899
|
-
|
|
1571
|
+
__init8() {this.queues = /* @__PURE__ */ new Map()}
|
|
4900
1572
|
|
|
4901
|
-
|
|
1573
|
+
__init9() {this.calls = 0}
|
|
4902
1574
|
/** Acts out `commands` in order for `actor`. Resolves with one result per command. */
|
|
4903
1575
|
perform(actor, commands) {
|
|
4904
1576
|
const prev = _nullishCoalesce(this.queues.get(actor.id), () => ( Promise.resolve()));
|
|
@@ -4938,7 +1610,7 @@ var ChartDirector = (_class4 = class {
|
|
|
4938
1610
|
const t = f / frames;
|
|
4939
1611
|
const e = 1 - Math.pow(1 - t, 3);
|
|
4940
1612
|
this.presence.set({ id, x: from.x + (to.x - from.x) * e, y: from.y + (to.y - from.y) * e });
|
|
4941
|
-
_optionalChain([each, 'optionalCall',
|
|
1613
|
+
_optionalChain([each, 'optionalCall', _83 => _83(e)]);
|
|
4942
1614
|
await this.clock.wait(ms / frames);
|
|
4943
1615
|
}
|
|
4944
1616
|
}
|
|
@@ -4970,7 +1642,7 @@ var ChartDirector = (_class4 = class {
|
|
|
4970
1642
|
if (command.kind !== "setValue") break;
|
|
4971
1643
|
const stale2 = this.stale(command);
|
|
4972
1644
|
if (stale2) return { ok: false, command, message: stale2 };
|
|
4973
|
-
const label = `${_nullishCoalesce(_optionalChain([this, 'access',
|
|
1645
|
+
const label = `${_nullishCoalesce(_optionalChain([this, 'access', _84 => _84.presence, 'access', _85 => _85.getState, 'call', _86 => _86(), 'access', _87 => _87.actors, 'access', _88 => _88.find, 'call', _89 => _89((a) => a.id === id), 'optionalAccess', _90 => _90.name]), () => ( "Agent"))}: edit value`;
|
|
4974
1646
|
await this.move(id, where(), step.to, step.ms, (e) => {
|
|
4975
1647
|
const v = step.from + (step.value - step.from) * e;
|
|
4976
1648
|
this.controller.setValue(command.seriesId, command.index, Number(v.toPrecision(12)), { label });
|
|
@@ -4991,7 +1663,8 @@ var ChartDirector = (_class4 = class {
|
|
|
4991
1663
|
return { ok: true, command };
|
|
4992
1664
|
}
|
|
4993
1665
|
}
|
|
4994
|
-
},
|
|
1666
|
+
}, _class3);
|
|
1667
|
+
|
|
4995
1668
|
|
|
4996
1669
|
|
|
4997
1670
|
|
|
@@ -5124,5 +1797,5 @@ var ChartDirector = (_class4 = class {
|
|
|
5124
1797
|
|
|
5125
1798
|
|
|
5126
1799
|
|
|
5127
|
-
exports.CHART_AXIS_KINDS =
|
|
1800
|
+
exports.CHART_AXIS_KINDS = _chunkPRSJJZBScjs.CHART_AXIS_KINDS; exports.CHART_COLOR_SCHEMES = _chunkPRSJJZBScjs.CHART_COLOR_SCHEMES; exports.CHART_CURVES = _chunkPRSJJZBScjs.CHART_CURVES; exports.CHART_DATE_STEPS = _chunkPRSJJZBScjs.CHART_DATE_STEPS; exports.CHART_LABEL_POSITIONS = _chunkPRSJJZBScjs.CHART_LABEL_POSITIONS; exports.CHART_LAYOUT_PRESETS = _chunkPRSJJZBScjs.CHART_LAYOUT_PRESETS; exports.CHART_LEGEND_POSITIONS = _chunkPRSJJZBScjs.CHART_LEGEND_POSITIONS; exports.CHART_MARKERS = _chunkPRSJJZBScjs.CHART_MARKERS; exports.CHART_MERMAID_META_PREFIX = _chunkPRSJJZBScjs.CHART_MERMAID_META_PREFIX; exports.CHART_NUMBER_STYLES = _chunkPRSJJZBScjs.CHART_NUMBER_STYLES; exports.CHART_OOXML_CONTENT_TYPE = _chunkPIMCHMZEcjs.CHART_OOXML_CONTENT_TYPE; exports.CHART_OOXML_NS = _chunkPIMCHMZEcjs.CHART_OOXML_NS; exports.CHART_OOXML_RELATIONSHIP = _chunkPIMCHMZEcjs.CHART_OOXML_RELATIONSHIP; exports.CHART_ORIENTATIONS = _chunkPRSJJZBScjs.CHART_ORIENTATIONS; exports.CHART_PALETTE_SHADES = _chunkPRSJJZBScjs.CHART_PALETTE_SHADES; exports.CHART_PRESENCE_COLORS = CHART_PRESENCE_COLORS; exports.CHART_SERIES_TYPES = _chunkPRSJJZBScjs.CHART_SERIES_TYPES; exports.CHART_STACKINGS = _chunkPRSJJZBScjs.CHART_STACKINGS; exports.CHART_TRENDLINE_TYPES = _chunkPRSJJZBScjs.CHART_TRENDLINE_TYPES; exports.CHART_TYPES = _chunkPRSJJZBScjs.CHART_TYPES; exports.CHART_TYPE_LABELS = _chunkPRSJJZBScjs.CHART_TYPE_LABELS; exports.CSS_VARIABLE_CHART_THEME = _chunkPRSJJZBScjs.CSS_VARIABLE_CHART_THEME; exports.ChartController = _chunkPRSJJZBScjs.ChartController; exports.ChartDirector = ChartDirector; exports.ChartPresence = ChartPresence; exports.ChartStream = ChartStream; exports.DEFAULT_CHART_PALETTE = _chunkPRSJJZBScjs.DEFAULT_CHART_PALETTE; exports.DEFAULT_CHART_THEME = _chunkPRSJJZBScjs.DEFAULT_CHART_THEME; exports.MAX_CHART_DATA_LABELS = _chunkPRSJJZBScjs.MAX_CHART_DATA_LABELS; exports.MAX_CHART_LINE_POINTS = _chunkPRSJJZBScjs.MAX_CHART_LINE_POINTS; exports.MAX_CHART_MARKERS = _chunkPRSJJZBScjs.MAX_CHART_MARKERS; exports.analyzeChartTable = analyzeChartTable; exports.blendChartScenes = blendChartScenes; exports.canDragChartPoint = canDragChartPoint; exports.chartBoxStats = _chunkPRSJJZBScjs.chartBoxStats; exports.chartClientToScene = chartClientToScene; exports.chartColorDistance = _chunkPRSJJZBScjs.chartColorDistance; exports.chartColorInputValue = _chunkPRSJJZBScjs.chartColorInputValue; exports.chartColorsByCategory = _chunkPRSJJZBScjs.chartColorsByCategory; exports.chartContrastRatio = _chunkPRSJJZBScjs.chartContrastRatio; exports.chartDataFromTable = _chunkPRSJJZBScjs.chartDataFromTable; exports.chartDataToTable = _chunkPRSJJZBScjs.chartDataToTable; exports.chartDateLabels = _chunkPRSJJZBScjs.chartDateLabels; exports.chartDateToSerial = _chunkPRSJJZBScjs.chartDateToSerial; exports.chartFamily = _chunkPRSJJZBScjs.chartFamily; exports.chartFormatToCode = _chunkPIMCHMZEcjs.chartFormatToCode; exports.chartFromMermaid = _chunkPRSJJZBScjs.chartFromMermaid; exports.chartFromOoxml = _chunkPIMCHMZEcjs.chartFromOoxml; exports.chartFromTable = chartFromTable; exports.chartHistogram = _chunkPRSJJZBScjs.chartHistogram; exports.chartHitTest = chartHitTest; exports.chartLuminance = _chunkPRSJJZBScjs.chartLuminance; exports.chartMermaidFamily = _chunkPRSJJZBScjs.chartMermaidFamily; exports.chartMovingAverage = _chunkPRSJJZBScjs.chartMovingAverage; exports.chartPointAnchor = chartPointAnchor; exports.chartPointColor = _chunkPRSJJZBScjs.chartPointColor; exports.chartPresetPatch = _chunkPRSJJZBScjs.chartPresetPatch; exports.chartQuantile = _chunkPRSJJZBScjs.chartQuantile; exports.chartRgbToHex = _chunkPRSJJZBScjs.chartRgbToHex; exports.chartRgbToOklab = _chunkPRSJJZBScjs.chartRgbToOklab; exports.chartSceneToDomSpec = _chunkPRSJJZBScjs.chartSceneToDomSpec; exports.chartSceneToSvg = _chunkPRSJJZBScjs.chartSceneToSvg; exports.chartSerialToDate = _chunkPRSJJZBScjs.chartSerialToDate; exports.chartSeriesColor = _chunkPRSJJZBScjs.chartSeriesColor; exports.chartSeriesType = _chunkPRSJJZBScjs.chartSeriesType; exports.chartSliceColor = _chunkPRSJJZBScjs.chartSliceColor; exports.chartSquarify = _chunkPRSJJZBScjs.chartSquarify; exports.chartSupportsTrendlines = _chunkPRSJJZBScjs.chartSupportsTrendlines; exports.chartTextBaseline = _chunkPRSJJZBScjs.chartTextBaseline; exports.chartTextOn = _chunkPRSJJZBScjs.chartTextOn; exports.chartTextTransform = _chunkPRSJJZBScjs.chartTextTransform; exports.chartToMermaid = _chunkPRSJJZBScjs.chartToMermaid; exports.chartToOoxml = _chunkPIMCHMZEcjs.chartToOoxml; exports.chartToSvg = _chunkPRSJJZBScjs.chartToSvg; exports.chartTrendlineName = _chunkPRSJJZBScjs.chartTrendlineName; exports.chartValueAtPoint = _chunkPRSJJZBScjs.chartValueAtPoint; exports.chartXAtPoint = _chunkPRSJJZBScjs.chartXAtPoint; exports.checkChartColors = _chunkPRSJJZBScjs.checkChartColors; exports.cloneChartSeries = _chunkPRSJJZBScjs.cloneChartSeries; exports.cloneChartSpec = _chunkPRSJJZBScjs.cloneChartSpec; exports.compileChartFormatCode = _chunkPRSJJZBScjs.compileChartFormatCode; exports.contrastingTextColor = _chunkPRSJJZBScjs.contrastingTextColor; exports.createChartCommands = createChartCommands; exports.createChartController = _chunkPRSJJZBScjs.createChartController; exports.createChartSpec = _chunkPRSJJZBScjs.createChartSpec; exports.createChartToolProvider = createChartToolProvider; exports.createInstantChartClock = createInstantChartClock; exports.cyclePaletteColor = _chunkPRSJJZBScjs.cyclePaletteColor; exports.decimateChartPoints = _chunkPRSJJZBScjs.decimateChartPoints; exports.defaultChartAxis = _chunkPRSJJZBScjs.defaultChartAxis; exports.describeChart = _chunkPRSJJZBScjs.describeChart; exports.describeChartSelection = describeChartSelection; exports.easeChartTween = easeChartTween; exports.estimateChartTextWidth = _chunkPRSJJZBScjs.estimateChartTextWidth; exports.findMermaidCharts = _chunkPRSJJZBScjs.findMermaidCharts; exports.fitChartTrendline = _chunkPRSJJZBScjs.fitChartTrendline; exports.formatChartDate = _chunkPRSJJZBScjs.formatChartDate; exports.formatChartNumber = _chunkPRSJJZBScjs.formatChartNumber; exports.formatChartValueWithCode = _chunkPRSJJZBScjs.formatChartValueWithCode; exports.handleChartKey = handleChartKey; exports.isCartesianChart = _chunkPRSJJZBScjs.isCartesianChart; exports.isXyChart = _chunkPRSJJZBScjs.isXyChart; exports.layoutChart = _chunkPRSJJZBScjs.layoutChart; exports.layoutSparkline = _chunkPRSJJZBScjs.layoutSparkline; exports.linearMap = _chunkPRSJJZBScjs.linearMap; exports.monochromeChartPalette = _chunkPRSJJZBScjs.monochromeChartPalette; exports.niceScale = _chunkPRSJJZBScjs.niceScale; exports.niceStep = _chunkPRSJJZBScjs.niceStep; exports.normalizeChartAxis = _chunkPRSJJZBScjs.normalizeChartAxis; exports.normalizeChartSpec = _chunkPRSJJZBScjs.normalizeChartSpec; exports.normalizeChartTrendline = _chunkPRSJJZBScjs.normalizeChartTrendline; exports.normalizeChartValues = _chunkPRSJJZBScjs.normalizeChartValues; exports.parseChartColor = _chunkPRSJJZBScjs.parseChartColor; exports.parseChartDate = _chunkPRSJJZBScjs.parseChartDate; exports.parseChartValue = _chunkPRSJJZBScjs.parseChartValue; exports.parsePartialChartJson = parsePartialChartJson; exports.planChartCommand = planChartCommand; exports.realChartClock = realChartClock; exports.recommendChartTypes = recommendChartTypes; exports.recommendForShape = recommendForShape; exports.sameChartSelection = _chunkPRSJJZBScjs.sameChartSelection; exports.shadeChartColor = _chunkPRSJJZBScjs.shadeChartColor; exports.simulateChartVision = _chunkPRSJJZBScjs.simulateChartVision; exports.snapChartDragValue = snapChartDragValue; exports.snapChartValue = _chunkPRSJJZBScjs.snapChartValue; exports.sparklineToSvg = _chunkPRSJJZBScjs.sparklineToSvg; exports.startChartPointDrag = startChartPointDrag; exports.stepDecimals = _chunkPRSJJZBScjs.stepDecimals; exports.summarizeChart = _chunkPRSJJZBScjs.summarizeChart; exports.summarizeChartSeries = _chunkPRSJJZBScjs.summarizeChartSeries; exports.trendNumberText = _chunkPRSJJZBScjs.trendNumberText; exports.uniqueChartSeriesId = _chunkPRSJJZBScjs.uniqueChartSeriesId;
|
|
5128
1801
|
//# sourceMappingURL=index.cjs.map
|