bezel-charts 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -9
- package/dist/bezel-charts.iife.js +9 -9
- package/dist/bezel-charts.js +775 -507
- package/dist/bezel-charts.min.js +9 -9
- package/dist/charts/bars.js +4 -0
- package/dist/charts/donut.js +2 -0
- package/dist/charts/gauge.js +3 -0
- package/dist/charts/heatmap.js +2 -0
- package/dist/charts/line.js +3 -0
- package/dist/charts/progress.js +2 -0
- package/dist/charts/ring.js +2 -0
- package/dist/charts/scatter.js +5 -0
- package/dist/charts/shared-L67BQ555.js +2 -0
- package/dist/charts/shared-PCFIMFLL.js +2 -0
- package/dist/charts/spark.js +2 -0
- package/package.json +12 -3
- package/src/charts/bars.js +17 -5
- package/src/charts/donut.js +9 -1
- package/src/charts/gauge.js +8 -0
- package/src/charts/heatmap.js +22 -5
- package/src/charts/line.js +28 -5
- package/src/charts/progress.js +9 -0
- package/src/charts/ring.js +7 -0
- package/src/charts/scatter.js +21 -4
- package/src/charts/spark.js +5 -0
- package/src/core/series.js +85 -0
- package/src/core/util.js +0 -32
- package/src/element.js +127 -16
- package/src/entries/bars.js +10 -0
- package/src/entries/donut.js +10 -0
- package/src/entries/gauge.js +10 -0
- package/src/entries/heatmap.js +10 -0
- package/src/entries/line.js +10 -0
- package/src/entries/progress.js +10 -0
- package/src/entries/ring.js +10 -0
- package/src/entries/scatter.js +10 -0
- package/src/entries/spark.js +10 -0
- package/src/index.js +13 -17
- package/src/render.js +27 -13
package/dist/bezel-charts.js
CHANGED
|
@@ -1,28 +1,4 @@
|
|
|
1
|
-
/*! Bezel Charts v1.
|
|
2
|
-
|
|
3
|
-
// src/core/themes.js
|
|
4
|
-
var THEMES = {
|
|
5
|
-
dark: {
|
|
6
|
-
tile: "#15151d",
|
|
7
|
-
bezel: "#000000",
|
|
8
|
-
fg: "#f2f1f7",
|
|
9
|
-
muted: "#8c8aa0",
|
|
10
|
-
grid: "#2a2935",
|
|
11
|
-
up: "#5EE6C8",
|
|
12
|
-
down: "#F675A8",
|
|
13
|
-
palette: ["#9FA1FF", "#F675A8", "#5EE6C8", "#FFC857", "#7CC4FF", "#FF8A5B"]
|
|
14
|
-
},
|
|
15
|
-
light: {
|
|
16
|
-
tile: "#ffffff",
|
|
17
|
-
bezel: "#000000",
|
|
18
|
-
fg: "#17161f",
|
|
19
|
-
muted: "#6b6980",
|
|
20
|
-
grid: "#e8e6ef",
|
|
21
|
-
up: "#12a383",
|
|
22
|
-
down: "#d63f7c",
|
|
23
|
-
palette: ["#6E70F0", "#E2508C", "#16A884", "#D99A0E", "#2F8FE0", "#EE6431"]
|
|
24
|
-
}
|
|
25
|
-
};
|
|
1
|
+
/*! Bezel Charts v1.3.0 | MIT | github.com/ShazMoghaddam/bezel-charts */
|
|
26
2
|
|
|
27
3
|
// src/core/util.js
|
|
28
4
|
var SANS = "ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif";
|
|
@@ -106,23 +82,6 @@ function fmt(n, o, short) {
|
|
|
106
82
|
}
|
|
107
83
|
return (n < 0 ? "\u2212" : "") + (o.prefix || "") + s + (o.suffix || "");
|
|
108
84
|
}
|
|
109
|
-
function niceScale(min, max, count) {
|
|
110
|
-
if (min === max) {
|
|
111
|
-
if (min === 0) max = 1;
|
|
112
|
-
else {
|
|
113
|
-
var p = Math.abs(min) * 0.1;
|
|
114
|
-
min -= p;
|
|
115
|
-
max += p;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
var step0 = (max - min) / (count || 4);
|
|
119
|
-
var mag = Math.pow(10, Math.floor(Math.log10(step0)));
|
|
120
|
-
var err = step0 / mag;
|
|
121
|
-
var step = (err <= 1 ? 1 : err <= 2 ? 2 : err <= 5 ? 5 : 10) * mag;
|
|
122
|
-
var lo = Math.floor(min / step) * step, hi = Math.ceil(max / step) * step, ticks = [];
|
|
123
|
-
for (var t = lo; t <= hi + step / 2; t += step) ticks.push(+t.toFixed(10));
|
|
124
|
-
return { lo, hi, ticks };
|
|
125
|
-
}
|
|
126
85
|
function pathOf(pts, straight) {
|
|
127
86
|
if (!pts.length) return "";
|
|
128
87
|
var d = "M" + r2(pts[0][0]) + "," + r2(pts[0][1]);
|
|
@@ -180,24 +139,6 @@ function statOf(v, o, fallback) {
|
|
|
180
139
|
if (k === "last" && v.length > 1 && o.delta !== "none") delta = v[v.length - 1] - v[0];
|
|
181
140
|
return { val: map[k], delta };
|
|
182
141
|
}
|
|
183
|
-
function legend(names, pal, th, x0, y0, w) {
|
|
184
|
-
var x = x0, y = y0, s = "";
|
|
185
|
-
names.forEach(function(nm, k) {
|
|
186
|
-
var wd = 13 + tw(nm, 12) + 16;
|
|
187
|
-
if (x > x0 && x + wd - 16 > x0 + w) {
|
|
188
|
-
x = x0;
|
|
189
|
-
y += 20;
|
|
190
|
-
}
|
|
191
|
-
s += '<circle cx="' + r2(x + 4) + '" cy="' + r2(y + 6) + '" r="4" fill="' + pal[k % pal.length] + '"/>' + text(x + 13, y + 10, nm, 'fill="' + th.fg + '" font-family="' + SANS + '" font-size="12"');
|
|
192
|
-
x += wd;
|
|
193
|
-
});
|
|
194
|
-
return { svg: s, height: y - y0 + 24 };
|
|
195
|
-
}
|
|
196
|
-
function axisWidth(ticks) {
|
|
197
|
-
return Math.max.apply(null, ticks.map(function(t) {
|
|
198
|
-
return tw(t, 10, true);
|
|
199
|
-
})) + 10;
|
|
200
|
-
}
|
|
201
142
|
|
|
202
143
|
// src/core/tile.js
|
|
203
144
|
function dims(o) {
|
|
@@ -295,65 +236,489 @@ function spark(o, th) {
|
|
|
295
236
|
return s;
|
|
296
237
|
}, { stat: st, summary: v.length ? "latest " + fmt(v[v.length - 1], o) : "no data" });
|
|
297
238
|
}
|
|
239
|
+
spark.table = function(o) {
|
|
240
|
+
var labels = list(o.labels);
|
|
241
|
+
return { head: ["Label", "Value"], rows: nums(o.values).map(function(n, i) {
|
|
242
|
+
return [labels[i] || "#" + (i + 1), fmt(n, o)];
|
|
243
|
+
}) };
|
|
244
|
+
};
|
|
298
245
|
|
|
299
|
-
// src/
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
246
|
+
// src/core/themes.js
|
|
247
|
+
var THEMES = {
|
|
248
|
+
dark: {
|
|
249
|
+
tile: "#15151d",
|
|
250
|
+
bezel: "#000000",
|
|
251
|
+
fg: "#f2f1f7",
|
|
252
|
+
muted: "#8c8aa0",
|
|
253
|
+
grid: "#2a2935",
|
|
254
|
+
up: "#5EE6C8",
|
|
255
|
+
down: "#F675A8",
|
|
256
|
+
palette: ["#9FA1FF", "#F675A8", "#5EE6C8", "#FFC857", "#7CC4FF", "#FF8A5B"]
|
|
257
|
+
},
|
|
258
|
+
light: {
|
|
259
|
+
tile: "#ffffff",
|
|
260
|
+
bezel: "#000000",
|
|
261
|
+
fg: "#17161f",
|
|
262
|
+
muted: "#6b6980",
|
|
263
|
+
grid: "#e8e6ef",
|
|
264
|
+
up: "#12a383",
|
|
265
|
+
down: "#d63f7c",
|
|
266
|
+
palette: ["#6E70F0", "#E2508C", "#16A884", "#D99A0E", "#2F8FE0", "#EE6431"]
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// src/render.js
|
|
271
|
+
var NAMES = ["spark", "line", "bars", "scatter", "ring", "donut", "gauge", "progress", "heatmap"];
|
|
272
|
+
var TYPES = {};
|
|
273
|
+
function register(type, fn) {
|
|
274
|
+
TYPES[type] = fn;
|
|
275
|
+
}
|
|
276
|
+
var camel = (k) => k.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
277
|
+
function prepare(type, options) {
|
|
278
|
+
if (!TYPES[type]) {
|
|
279
|
+
throw new Error(NAMES.includes(type) ? `Bezel Charts: the ${type} chart isn't loaded. Add import 'bezel-charts/${type}', or load dist/charts/${type}.js.` : `Bezel Charts: unknown chart type "${type}". Choose one of: ${NAMES.join(", ")}.`);
|
|
280
|
+
}
|
|
281
|
+
const o = {};
|
|
282
|
+
for (const [k, v] of Object.entries(options)) o[camel(k)] = v;
|
|
283
|
+
return o;
|
|
284
|
+
}
|
|
285
|
+
function render(type, options = {}) {
|
|
286
|
+
const o = prepare(type, options);
|
|
287
|
+
o.animate = bool(o.animate);
|
|
288
|
+
o.frame = o.frame === "none" ? "none" : "bezel";
|
|
289
|
+
const mode = o.theme === "light" || o.theme === "dark" ? o.theme : o.autoTheme || "dark";
|
|
290
|
+
return TYPES[type](o, THEMES[mode]);
|
|
291
|
+
}
|
|
292
|
+
function table(type, options = {}) {
|
|
293
|
+
return TYPES[type].table(prepare(type, options));
|
|
294
|
+
}
|
|
295
|
+
function toStatic(svg) {
|
|
296
|
+
return svg.replace(/<rect class="bzc-hit"[^>]*\/>/g, "").replace(/<circle[^>]* class="bzc-hit"[^>]*\/>/g, "").replace(/ data-tip="[^"]*"/g, "");
|
|
297
|
+
}
|
|
298
|
+
function toSVG(type, options = {}) {
|
|
299
|
+
return toStatic(render(type, { ...options, animate: false }));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/element.js
|
|
303
|
+
var ATTRIBUTES = [
|
|
304
|
+
"type",
|
|
305
|
+
"values",
|
|
306
|
+
"labels",
|
|
307
|
+
"series",
|
|
308
|
+
"heading",
|
|
309
|
+
"prefix",
|
|
310
|
+
"suffix",
|
|
311
|
+
"decimals",
|
|
312
|
+
"compact",
|
|
313
|
+
"color",
|
|
314
|
+
"theme",
|
|
315
|
+
"frame",
|
|
316
|
+
"width",
|
|
317
|
+
"height",
|
|
318
|
+
"goal",
|
|
319
|
+
"goal-label",
|
|
320
|
+
"notes",
|
|
321
|
+
"show-values",
|
|
322
|
+
"stat",
|
|
323
|
+
"delta",
|
|
324
|
+
"delta-label",
|
|
325
|
+
"max",
|
|
326
|
+
"min",
|
|
327
|
+
"value",
|
|
328
|
+
"zones",
|
|
329
|
+
"horizontal",
|
|
330
|
+
"straight",
|
|
331
|
+
"zero",
|
|
332
|
+
"start",
|
|
333
|
+
"lang",
|
|
334
|
+
"corners",
|
|
335
|
+
"background",
|
|
336
|
+
"center-label",
|
|
337
|
+
"highlight",
|
|
338
|
+
"x",
|
|
339
|
+
"y",
|
|
340
|
+
"x-label",
|
|
341
|
+
"y-label",
|
|
342
|
+
"trend",
|
|
343
|
+
"static",
|
|
344
|
+
"no-tooltips",
|
|
345
|
+
"stacked",
|
|
346
|
+
"angle-labels",
|
|
347
|
+
"dash",
|
|
348
|
+
"markers",
|
|
349
|
+
"patterns"
|
|
350
|
+
];
|
|
351
|
+
var HOST_CSS = `:host{display:block;position:relative;max-width:100%}:host([hidden]){display:none}svg{display:block;max-width:100%;height:auto;overflow:visible}.tip{position:absolute;left:0;top:0;pointer-events:none;background:#0b0b10;color:#f2f1f7;font:600 12px/1.5 ${MONO};padding:7px 10px;border-radius:10px;white-space:pre;opacity:0;transform:translate(-50%,calc(-100% - 12px));transition:opacity .12s;z-index:3;border:1px solid #2a2935;box-shadow:0 8px 24px rgba(0,0,0,.35)}.tip.on{opacity:1}.c{outline:none}.c:focus-visible{outline:2px solid #fff;outline-offset:3px;box-shadow:0 0 0 7px #000}.k{stroke:currentColor;stroke-width:2;stroke-opacity:1}.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media (forced-colors:active){.c:focus-visible{outline-color:Highlight}}`;
|
|
352
|
+
var STEPS = { ArrowLeft: -1, ArrowUp: -1, ArrowRight: 1, ArrowDown: 1 };
|
|
353
|
+
var darkQuery = () => typeof matchMedia === "function" ? matchMedia("(prefers-color-scheme: dark)") : null;
|
|
354
|
+
var pageTheme = () => {
|
|
355
|
+
const t = typeof document !== "undefined" && document.documentElement?.getAttribute?.("data-theme");
|
|
356
|
+
return t === "light" || t === "dark" ? t : null;
|
|
357
|
+
};
|
|
358
|
+
var live = /* @__PURE__ */ new Set();
|
|
359
|
+
if (typeof MutationObserver === "function" && typeof document !== "undefined" && document.documentElement) {
|
|
360
|
+
new MutationObserver(() => live.forEach((el) => el.refresh())).observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
|
|
361
|
+
}
|
|
362
|
+
var Base = typeof HTMLElement === "undefined" ? class {
|
|
363
|
+
} : HTMLElement;
|
|
364
|
+
var BezelChart = class extends Base {
|
|
365
|
+
static get observedAttributes() {
|
|
366
|
+
return ATTRIBUTES;
|
|
367
|
+
}
|
|
368
|
+
/** The default colour sets, so tools like the Studio can show them. */
|
|
369
|
+
static get themes() {
|
|
370
|
+
return THEMES;
|
|
371
|
+
}
|
|
372
|
+
/** Which theme the chart is drawing in right now: "dark" or "light". */
|
|
373
|
+
get resolvedTheme() {
|
|
374
|
+
const t = this.getAttribute("theme");
|
|
375
|
+
return t === "light" || t === "dark" ? t : this.options.autoTheme;
|
|
376
|
+
}
|
|
377
|
+
/** The chart type this tag draws. Subclasses fix it; <bezel-chart> reads its type attribute. */
|
|
378
|
+
get chartType() {
|
|
379
|
+
return this.getAttribute("type") || "spark";
|
|
380
|
+
}
|
|
381
|
+
connectedCallback() {
|
|
382
|
+
if (!this.shadowRoot) {
|
|
383
|
+
this.attachShadow({ mode: "open" }).innerHTML = `<style>${HOST_CSS}</style><div class="c" role="group" aria-roledescription="chart"></div><p class="sr" id="h">Use the arrow keys to step through the values.</p><div class="tip" aria-hidden="true"></div><div class="sr" aria-live="polite"></div><div class="sr"></div>`;
|
|
384
|
+
[, this.#box, , this.#tipEl, this.#live, this.#table] = this.shadowRoot.children;
|
|
385
|
+
this.shadowRoot.addEventListener("pointermove", (e) => this.#tip(e));
|
|
386
|
+
this.shadowRoot.addEventListener("pointerdown", (e) => this.#tip(e));
|
|
387
|
+
this.addEventListener("pointerleave", () => this.#tipEl.classList.remove("on"));
|
|
388
|
+
this.#box.addEventListener("keydown", (e) => this.#key(e));
|
|
389
|
+
this.#box.addEventListener("blur", () => this.#clear());
|
|
322
390
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
391
|
+
live.add(this);
|
|
392
|
+
this.#dark = darkQuery();
|
|
393
|
+
this.#dark?.addEventListener?.("change", this.#onScheme);
|
|
394
|
+
if (typeof ResizeObserver === "function") {
|
|
395
|
+
this.#ro = new ResizeObserver(([entry]) => {
|
|
396
|
+
const w = Math.round(entry.contentRect.width);
|
|
397
|
+
if (!this.#rendered || !this.hasAttribute("width") && w > 0 && Math.abs(w - this.#width) > 1) this.#render();
|
|
398
|
+
});
|
|
399
|
+
this.#ro.observe(this);
|
|
400
|
+
} else {
|
|
401
|
+
this.#render();
|
|
326
402
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
var Y = function(v) {
|
|
340
|
-
return top + ph - (v - sc.lo) / (sc.hi - sc.lo) * ph;
|
|
341
|
-
};
|
|
342
|
-
sc.ticks.forEach(function(t, i2) {
|
|
343
|
-
var y = Y(t);
|
|
344
|
-
s += '<line x1="' + r2(px) + '" x2="' + r2(px + pw) + '" y1="' + r2(y) + '" y2="' + r2(y) + '" stroke="' + th.grid + '"' + (t === 0 ? "" : ' stroke-dasharray="2 4"') + "/>" + text(px - 8, y + 3.5, tl[i2], small(th, 10, 'text-anchor="end"'));
|
|
403
|
+
}
|
|
404
|
+
disconnectedCallback() {
|
|
405
|
+
live.delete(this);
|
|
406
|
+
this.#ro?.disconnect();
|
|
407
|
+
this.#dark?.removeEventListener?.("change", this.#onScheme);
|
|
408
|
+
}
|
|
409
|
+
attributeChangedCallback() {
|
|
410
|
+
if (!this.#rendered || this.#queued) return;
|
|
411
|
+
this.#queued = true;
|
|
412
|
+
queueMicrotask(() => {
|
|
413
|
+
this.#queued = false;
|
|
414
|
+
this.#render();
|
|
345
415
|
});
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
416
|
+
}
|
|
417
|
+
/** Current settings as a plain object, the same shape BezelCharts.render() takes. */
|
|
418
|
+
get options() {
|
|
419
|
+
const o = {};
|
|
420
|
+
for (const name of ATTRIBUTES) if (this.hasAttribute(name)) o[name] = this.getAttribute(name);
|
|
421
|
+
if (!o.width) o.width = this.#width || 360;
|
|
422
|
+
o.autoTheme = pageTheme() || (this.#dark ? this.#dark.matches ? "dark" : "light" : "dark");
|
|
423
|
+
return o;
|
|
424
|
+
}
|
|
425
|
+
/** Redraws the chart without replaying its entrance animation. */
|
|
426
|
+
refresh() {
|
|
427
|
+
if (this.#rendered) this.#render();
|
|
428
|
+
}
|
|
429
|
+
/** The chart as a standalone static SVG string. */
|
|
430
|
+
toSVG(extra = {}) {
|
|
431
|
+
return toStatic(render(this.chartType, { ...this.options, ...extra, animate: false }));
|
|
432
|
+
}
|
|
433
|
+
/** Saves the chart as an .svg file. */
|
|
434
|
+
download(name) {
|
|
435
|
+
const url = URL.createObjectURL(new Blob([this.toSVG()], { type: "image/svg+xml" }));
|
|
436
|
+
const a = Object.assign(document.createElement("a"), {
|
|
437
|
+
href: url,
|
|
438
|
+
download: `${(name || this.getAttribute("heading") || this.chartType).replace(/[^\w-]+/g, "-").toLowerCase()}.svg`
|
|
439
|
+
});
|
|
440
|
+
document.body.append(a);
|
|
441
|
+
a.click();
|
|
442
|
+
a.remove();
|
|
443
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
444
|
+
}
|
|
445
|
+
#ro = null;
|
|
446
|
+
#box = null;
|
|
447
|
+
#tipEl = null;
|
|
448
|
+
#live = null;
|
|
449
|
+
#table = null;
|
|
450
|
+
#at = -1;
|
|
451
|
+
#dark = null;
|
|
452
|
+
#width = 0;
|
|
453
|
+
#rendered = false;
|
|
454
|
+
#queued = false;
|
|
455
|
+
#onScheme = () => {
|
|
456
|
+
const t = this.getAttribute("theme");
|
|
457
|
+
if (!t || t === "auto") this.refresh();
|
|
458
|
+
};
|
|
459
|
+
#render() {
|
|
460
|
+
if (!this.shadowRoot) return;
|
|
461
|
+
const w = Math.round(this.getBoundingClientRect?.().width || 0);
|
|
462
|
+
this.#width = w > 0 ? w : 360;
|
|
463
|
+
const o = this.options;
|
|
464
|
+
o.animate = !this.#rendered && !this.hasAttribute("static");
|
|
465
|
+
let svg, data = null;
|
|
466
|
+
try {
|
|
467
|
+
svg = render(this.chartType, o);
|
|
468
|
+
data = table(this.chartType, o);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
svg = `<p style="font:13px ${SANS};color:#d63f7c">${esc(err.message)}</p>`;
|
|
471
|
+
}
|
|
472
|
+
const box = this.#box;
|
|
473
|
+
box.innerHTML = svg.replace(/<title>[\s\S]*?<\/title>/, "");
|
|
474
|
+
const img = box.querySelector("svg");
|
|
475
|
+
if (img) {
|
|
476
|
+
box.setAttribute("aria-label", img.getAttribute("aria-label"));
|
|
477
|
+
img.removeAttribute("role");
|
|
478
|
+
img.removeAttribute("aria-label");
|
|
479
|
+
img.setAttribute("aria-hidden", "true");
|
|
480
|
+
} else {
|
|
481
|
+
box.removeAttribute("aria-label");
|
|
482
|
+
}
|
|
483
|
+
const keys = !!img && !this.hasAttribute("no-tooltips");
|
|
484
|
+
if (keys) {
|
|
485
|
+
box.setAttribute("tabindex", "0");
|
|
486
|
+
box.setAttribute("aria-describedby", "h");
|
|
487
|
+
} else {
|
|
488
|
+
box.removeAttribute("tabindex");
|
|
489
|
+
box.removeAttribute("aria-describedby");
|
|
490
|
+
}
|
|
491
|
+
box.style.color = THEMES[o.theme === "light" || o.theme === "dark" ? o.theme : o.autoTheme].fg;
|
|
492
|
+
box.style.borderRadius = `${o.frame === "none" ? 4 : clamp(+o.corners || 22, 0, 60)}px`;
|
|
493
|
+
this.#table.innerHTML = data && data.rows.length ? tableHTML(o.heading, data) : "";
|
|
494
|
+
if (this.#at >= 0 && keys && this.shadowRoot.activeElement === box) this.#show(this.#at);
|
|
495
|
+
else this.#at = -1;
|
|
496
|
+
this.#rendered = true;
|
|
497
|
+
this.dispatchEvent(new CustomEvent("bezel-render", { bubbles: true }));
|
|
498
|
+
}
|
|
499
|
+
#tip(e) {
|
|
500
|
+
if (this.hasAttribute("no-tooltips")) return;
|
|
501
|
+
const target = e.target?.closest?.("[data-tip]");
|
|
502
|
+
if (!target) {
|
|
503
|
+
this.#tipEl.classList.remove("on");
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
this.#place(target, e.clientX, e.clientY);
|
|
507
|
+
}
|
|
508
|
+
/** Shows the tooltip for a mark at a point on screen. */
|
|
509
|
+
#place(mark, x, y) {
|
|
510
|
+
const tip = this.#tipEl;
|
|
511
|
+
tip.textContent = mark.getAttribute("data-tip");
|
|
512
|
+
const box = this.getBoundingClientRect?.() || { left: 0, top: 0, width: 0 };
|
|
513
|
+
const half = tip.offsetWidth / 2;
|
|
514
|
+
tip.style.left = `${clamp(x - box.left, half, Math.max(half, box.width - half))}px`;
|
|
515
|
+
tip.style.top = `${y - box.top}px`;
|
|
516
|
+
tip.classList.add("on");
|
|
517
|
+
}
|
|
518
|
+
/** Every value the arrow keys visit, in order. Donuts mark their slices data-skip, as the legend rows repeat them. */
|
|
519
|
+
#marks() {
|
|
520
|
+
return [...this.#box.querySelectorAll("[data-tip]:not([data-skip])")];
|
|
521
|
+
}
|
|
522
|
+
#key(e) {
|
|
523
|
+
const marks = this.#marks(), n = marks.length;
|
|
524
|
+
if (!n) return;
|
|
525
|
+
let i = this.#at, step = STEPS[e.key];
|
|
526
|
+
if (step) {
|
|
527
|
+
if (this.chartType === "heatmap" && /Left|Right/.test(e.key)) step *= 7;
|
|
528
|
+
i = i < 0 ? step > 0 ? 0 : n - 1 : clamp(i + step, 0, n - 1);
|
|
529
|
+
} else if (e.key === "Home" || e.key === "End") {
|
|
530
|
+
i = e.key === "Home" ? 0 : n - 1;
|
|
531
|
+
} else {
|
|
532
|
+
if (e.key === "Escape") this.#clear();
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
e.preventDefault();
|
|
536
|
+
this.#show(i, true);
|
|
537
|
+
}
|
|
538
|
+
/** Outlines value i, shows its tooltip and, when say is set, reads it out. */
|
|
539
|
+
#show(i, say) {
|
|
540
|
+
const marks = this.#marks();
|
|
541
|
+
if (!marks.length) return;
|
|
542
|
+
i = clamp(i, 0, marks.length - 1);
|
|
543
|
+
this.#box.querySelector(".k")?.classList.remove("k");
|
|
544
|
+
const mark = marks[i], r = mark.getBoundingClientRect?.() || { left: 0, top: 0, width: 0 };
|
|
545
|
+
mark.classList.add("k");
|
|
546
|
+
this.#at = i;
|
|
547
|
+
this.#place(mark, r.left + r.width / 2, r.top);
|
|
548
|
+
if (say) this.#live.textContent = `${mark.getAttribute("data-tip").replace(/\n/g, ", ")}. ${i + 1} of ${marks.length}`;
|
|
549
|
+
}
|
|
550
|
+
#clear() {
|
|
551
|
+
this.#box.querySelector(".k")?.classList.remove("k");
|
|
552
|
+
this.#tipEl.classList.remove("on");
|
|
553
|
+
this.#live.textContent = "";
|
|
554
|
+
this.#at = -1;
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
function tableHTML(caption, { head, rows }) {
|
|
558
|
+
const cells = (row) => row.map((c, i) => i ? `<td>${esc(c)}</td>` : `<th scope="row">${esc(c)}</th>`).join("");
|
|
559
|
+
return `<table><caption>${esc(caption || "Chart data")}</caption><thead><tr>${head.map((h) => `<th scope="col">${esc(h)}</th>`).join("")}</tr></thead><tbody>${rows.map((r) => `<tr>${cells(r)}</tr>`).join("")}</tbody></table>`;
|
|
560
|
+
}
|
|
561
|
+
function define(type) {
|
|
562
|
+
if (typeof customElements === "undefined" || typeof HTMLElement === "undefined") return;
|
|
563
|
+
if (!customElements.get(`bezel-${type}`)) {
|
|
564
|
+
customElements.define(`bezel-${type}`, class extends BezelChart {
|
|
565
|
+
get chartType() {
|
|
566
|
+
return type;
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
if (!customElements.get("bezel-chart")) customElements.define("bezel-chart", class extends BezelChart {
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/entries/spark.js
|
|
575
|
+
register("spark", spark);
|
|
576
|
+
define("spark");
|
|
577
|
+
|
|
578
|
+
// src/core/series.js
|
|
579
|
+
function niceScale(min, max, count) {
|
|
580
|
+
if (min === max) {
|
|
581
|
+
if (min === 0) max = 1;
|
|
582
|
+
else {
|
|
583
|
+
var p = Math.abs(min) * 0.1;
|
|
584
|
+
min -= p;
|
|
585
|
+
max += p;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
var step0 = (max - min) / (count || 4);
|
|
589
|
+
var mag = Math.pow(10, Math.floor(Math.log10(step0)));
|
|
590
|
+
var err = step0 / mag;
|
|
591
|
+
var step = (err <= 1 ? 1 : err <= 2 ? 2 : err <= 5 ? 5 : 10) * mag;
|
|
592
|
+
var lo = Math.floor(min / step) * step, hi = Math.ceil(max / step) * step, ticks = [];
|
|
593
|
+
for (var t = lo; t <= hi + step / 2; t += step) ticks.push(+t.toFixed(10));
|
|
594
|
+
return { lo, hi, ticks };
|
|
595
|
+
}
|
|
596
|
+
function legend(names, pal, th, x0, y0, w, key, kw) {
|
|
597
|
+
var x = x0, y = y0, s = "", off = kw || 13;
|
|
598
|
+
names.forEach(function(nm, k) {
|
|
599
|
+
var wd = off + tw(nm, 12) + 16;
|
|
600
|
+
if (x > x0 && x + wd - 16 > x0 + w) {
|
|
601
|
+
x = x0;
|
|
602
|
+
y += 20;
|
|
603
|
+
}
|
|
604
|
+
s += (key ? key(k, x, y) : '<circle cx="' + r2(x + 4) + '" cy="' + r2(y + 6) + '" r="4" fill="' + pal[k % pal.length] + '"/>') + text(x + off, y + 10, nm, 'fill="' + th.fg + '" font-family="' + SANS + '" font-size="12"');
|
|
605
|
+
x += wd;
|
|
606
|
+
});
|
|
607
|
+
return { svg: s, height: y - y0 + 24 };
|
|
608
|
+
}
|
|
609
|
+
var DASHES = { solid: "", dashed: "7 5", dotted: "1 5", dashdot: "9 4 1 4" };
|
|
610
|
+
var SHAPES = ["circle", "square", "triangle", "diamond"];
|
|
611
|
+
var PATTERNS = ["solid", "stripes", "dots", "grid"];
|
|
612
|
+
function styleOf(v, names, off) {
|
|
613
|
+
if (v == null || v === false || v === "none" || v === "false") return null;
|
|
614
|
+
var l = v === true || v === "" || v === "auto" ? names : list(v).map(function(s) {
|
|
615
|
+
return names.indexOf(s) < 0 ? off : s;
|
|
616
|
+
});
|
|
617
|
+
if (!l.length) return null;
|
|
618
|
+
return function(k) {
|
|
619
|
+
return l[k % l.length];
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
function marker(kind, x, y, r, attrs) {
|
|
623
|
+
var a = r * 1.15, b = r * 0.85;
|
|
624
|
+
if (kind === "square") return '<rect x="' + r2(x - b) + '" y="' + r2(y - b) + '" width="' + r2(2 * b) + '" height="' + r2(2 * b) + '" rx="1"' + attrs + "/>";
|
|
625
|
+
if (kind === "triangle") return '<path d="M' + r2(x) + "," + r2(y - a) + "L" + r2(x + a) + "," + r2(y + b) + "L" + r2(x - a) + "," + r2(y + b) + 'Z"' + attrs + "/>";
|
|
626
|
+
if (kind === "diamond") return '<path d="M' + r2(x) + "," + r2(y - a * 1.1) + "L" + r2(x + a) + "," + r2(y) + "L" + r2(x) + "," + r2(y + a * 1.1) + "L" + r2(x - a) + "," + r2(y) + 'Z"' + attrs + "/>";
|
|
627
|
+
return '<circle cx="' + r2(x) + '" cy="' + r2(y) + '" r="' + r2(r) + '"' + attrs + "/>";
|
|
628
|
+
}
|
|
629
|
+
function pattern(id, kind, c, bg) {
|
|
630
|
+
var ink = ' stroke="' + bg + '" stroke-opacity=".55" stroke-width="1.5"';
|
|
631
|
+
return '<pattern id="' + id + '" width="6" height="6" patternUnits="userSpaceOnUse"' + (kind === "dots" ? "" : ' patternTransform="rotate(45)"') + '><rect width="6" height="6" fill="' + c + '"/>' + (kind === "dots" ? '<circle cx="3" cy="3" r="1.3" fill="' + bg + '" fill-opacity=".55"/>' : '<path d="M3,0V6' + (kind === "grid" ? "M0,3H6" : "") + '"' + ink + "/>") + "</pattern>";
|
|
632
|
+
}
|
|
633
|
+
function axisWidth(ticks) {
|
|
634
|
+
return Math.max.apply(null, ticks.map(function(t) {
|
|
635
|
+
return tw(t, 10, true);
|
|
636
|
+
})) + 10;
|
|
637
|
+
}
|
|
638
|
+
function seriesTable(o, total) {
|
|
639
|
+
var S = series(o.values), names = list(o.series), labels = list(o.labels), rows = [];
|
|
640
|
+
var n = Math.max.apply(null, [0].concat(S.map(function(s) {
|
|
641
|
+
return s.length;
|
|
642
|
+
})));
|
|
643
|
+
for (var i = 0; i < n; i++) {
|
|
644
|
+
var row = [labels[i] || "#" + (i + 1)], t = 0;
|
|
645
|
+
S.forEach(function(s) {
|
|
646
|
+
row.push(i < s.length ? fmt(s[i], o) : "");
|
|
647
|
+
t += i < s.length ? s[i] : 0;
|
|
648
|
+
});
|
|
649
|
+
if (total) row.push(fmt(t, o));
|
|
650
|
+
rows.push(row);
|
|
651
|
+
}
|
|
652
|
+
var head = S.length > 1 || names.length ? S.map(function(_, k) {
|
|
653
|
+
return names[k] || "Series " + (k + 1);
|
|
654
|
+
}) : ["Value"];
|
|
655
|
+
return { head: ["Label"].concat(head, total ? ["Total"] : []), rows };
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// src/charts/line.js
|
|
659
|
+
function line(o, th) {
|
|
660
|
+
var S = series(o.values), names = list(o.series), labels = list(o.labels), pal = palette(o, th);
|
|
661
|
+
var h = num(o.height, 170), n = Math.max.apply(null, [0].concat(S.map(function(s) {
|
|
662
|
+
return s.length;
|
|
663
|
+
})));
|
|
664
|
+
var notes = parseNotes(o, labels), multi = S.length > 1 || names.length > 0;
|
|
665
|
+
var st = statOf(S[0] || [], o, "");
|
|
666
|
+
var dashOf = styleOf(o.dash, Object.keys(DASHES), "solid"), markOf = styleOf(o.markers, SHAPES, "none");
|
|
667
|
+
var bg = o.background || th.tile;
|
|
668
|
+
var key = (dashOf || markOf) && function(k, x, y) {
|
|
669
|
+
var c = pal[k % pal.length], dk = dashOf ? DASHES[dashOf(k)] : "", mk = markOf ? markOf(k) : "none";
|
|
670
|
+
return '<line x1="' + r2(x) + '" x2="' + r2(x + 18) + '" y1="' + r2(y + 6) + '" y2="' + r2(y + 6) + '" stroke="' + c + '" stroke-width="2.5" stroke-linecap="round"' + (dk ? ' stroke-dasharray="' + dk + '"' : "") + "/>" + (mk !== "none" ? marker(mk, x + 9, y + 6, 3.5, ' fill="' + c + '" stroke="' + bg + '" stroke-width="1.5"') : "");
|
|
671
|
+
};
|
|
672
|
+
return tile(o, th, h, function(x0, y0, w, h2) {
|
|
673
|
+
if (!n) return emptyState(th, x0, y0, w, h2);
|
|
674
|
+
var s = "", top = y0;
|
|
675
|
+
if (multi) {
|
|
676
|
+
var lg = legend(S.map(function(_, k) {
|
|
677
|
+
return names[k] || "Series " + (k + 1);
|
|
678
|
+
}), pal, th, x0, top, w, key, key && 24);
|
|
679
|
+
s += lg.svg;
|
|
680
|
+
top += lg.height;
|
|
681
|
+
}
|
|
682
|
+
var all = [].concat.apply([], S);
|
|
683
|
+
var mn = Math.min.apply(null, all), mx = Math.max.apply(null, all);
|
|
684
|
+
if (has(o.goal)) {
|
|
685
|
+
mn = Math.min(mn, +o.goal);
|
|
686
|
+
mx = Math.max(mx, +o.goal);
|
|
687
|
+
}
|
|
688
|
+
if (bool(o.zero)) {
|
|
689
|
+
mn = Math.min(0, mn);
|
|
690
|
+
mx = Math.max(0, mx);
|
|
691
|
+
}
|
|
692
|
+
var sc = niceScale(mn, mx, 4);
|
|
693
|
+
var tl = sc.ticks.map(function(t) {
|
|
694
|
+
return fmt(t, o, true);
|
|
695
|
+
});
|
|
696
|
+
var axisW = Math.max.apply(null, tl.map(function(t) {
|
|
697
|
+
return tw(t, 10, true);
|
|
698
|
+
})) + 10;
|
|
699
|
+
var bottom = labels.length ? 20 : 4;
|
|
700
|
+
var px = x0 + axisW, pw = w - axisW, ph = h2 - (top - y0) - bottom;
|
|
701
|
+
var X = function(i2) {
|
|
702
|
+
return n === 1 ? px + pw / 2 : px + i2 * pw / (n - 1);
|
|
703
|
+
};
|
|
704
|
+
var Y = function(v) {
|
|
705
|
+
return top + ph - (v - sc.lo) / (sc.hi - sc.lo) * ph;
|
|
706
|
+
};
|
|
707
|
+
sc.ticks.forEach(function(t, i2) {
|
|
708
|
+
var y = Y(t);
|
|
709
|
+
s += '<line x1="' + r2(px) + '" x2="' + r2(px + pw) + '" y1="' + r2(y) + '" y2="' + r2(y) + '" stroke="' + th.grid + '"' + (t === 0 ? "" : ' stroke-dasharray="2 4"') + "/>" + text(px - 8, y + 3.5, tl[i2], small(th, 10, 'text-anchor="end"'));
|
|
710
|
+
});
|
|
711
|
+
if (labels.length) {
|
|
712
|
+
var every = Math.max(1, Math.ceil(n * (Math.max.apply(null, labels.map(function(l) {
|
|
713
|
+
return tw(l, 10, true);
|
|
714
|
+
})) + 10) / pw));
|
|
715
|
+
for (var i = 0; i < n; i += every) {
|
|
716
|
+
if (labels[i] == null) continue;
|
|
717
|
+
var anchor = n > 1 && i === 0 ? "start" : n > 1 && i === n - 1 ? "end" : "middle";
|
|
718
|
+
s += text(X(i), top + ph + 15, labels[i], small(th, 10, 'text-anchor="' + anchor + '"'));
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
notes.forEach(function(nt) {
|
|
357
722
|
if (nt.i >= n) return;
|
|
358
723
|
var x = X(nt.i);
|
|
359
724
|
s += '<line x1="' + r2(x) + '" x2="' + r2(x) + '" y1="' + r2(top + 12) + '" y2="' + r2(top + ph) + '" stroke="' + th.fg + '" stroke-opacity=".4" stroke-dasharray="3 3"/>' + text(clamp(x, px + 30, px + pw - 30), top + 8, nt.text, small(th, 10, 'text-anchor="middle"', th.fg));
|
|
@@ -369,9 +734,17 @@ function line(o, th) {
|
|
|
369
734
|
var g = newId("g"), last = P[P.length - 1];
|
|
370
735
|
s += '<defs><linearGradient id="' + g + '" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="' + c + '" stop-opacity=".26"/><stop offset="1" stop-color="' + c + '" stop-opacity="0"/></linearGradient></defs><path d="' + d + "L" + r2(last[0]) + "," + r2(top + ph) + "L" + r2(P[0][0]) + "," + r2(top + ph) + 'Z" fill="url(#' + g + ')"' + anim(o, "bzc-a") + "/>";
|
|
371
736
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
737
|
+
var dk = dashOf ? DASHES[dashOf(k)] : "", mk = markOf ? markOf(k) : "none";
|
|
738
|
+
s += dk ? '<path d="' + d + '" fill="none" stroke="' + c + '" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="' + dk + '"' + anim(o, "bzc-a", k * 0.15) + "/>" : '<path d="' + d + '" fill="none" stroke="' + c + '" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" pathLength="1" style="stroke-dasharray:1' + (o.animate ? ";animation-delay:" + k * 0.15 + "s" : "") + '"' + (o.animate ? ' class="bzc-d"' : "") + "/>";
|
|
739
|
+
if (mk !== "none") {
|
|
740
|
+
var every2 = Math.ceil(v.length / 40);
|
|
741
|
+
v.forEach(function(y, i2) {
|
|
742
|
+
if (i2 % every2 === 0 || i2 === v.length - 1) s += marker(mk, X(i2), Y(y), 3.5, ' fill="' + c + '" stroke="' + bg + '" stroke-width="1.5"' + anim(o, "bzc-a", 0.6 + k * 0.15));
|
|
743
|
+
});
|
|
744
|
+
} else {
|
|
745
|
+
var e = P[P.length - 1];
|
|
746
|
+
s += '<circle cx="' + r2(e[0]) + '" cy="' + r2(e[1]) + '" r="4" fill="' + c + '" stroke="' + bg + '" stroke-width="2"/>';
|
|
747
|
+
}
|
|
375
748
|
if (bool(o.showValues)) v.forEach(function(y, i2) {
|
|
376
749
|
s += text(X(i2), Y(y) - 8, fmt(y, o, true), small(th, 9, 'text-anchor="middle"'));
|
|
377
750
|
});
|
|
@@ -388,6 +761,13 @@ function line(o, th) {
|
|
|
388
761
|
return s;
|
|
389
762
|
}, { stat: st, summary: S.length + " series over " + n + " points" });
|
|
390
763
|
}
|
|
764
|
+
line.table = function(o) {
|
|
765
|
+
return seriesTable(o);
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
// src/entries/line.js
|
|
769
|
+
register("line", line);
|
|
770
|
+
define("line");
|
|
391
771
|
|
|
392
772
|
// src/charts/bars.js
|
|
393
773
|
var ANGLE = 40;
|
|
@@ -416,8 +796,18 @@ function bars(o, th) {
|
|
|
416
796
|
var nameOf = function(j) {
|
|
417
797
|
return names[j] || "Series " + (j + 1);
|
|
418
798
|
};
|
|
799
|
+
var patOf = grouped ? styleOf(o.patterns, PATTERNS, "solid") : null, defs = "", fills = [];
|
|
800
|
+
for (var p = 0; patOf && p < k; p++) {
|
|
801
|
+
if (patOf(p) === "solid") continue;
|
|
802
|
+
var pid = newId("g");
|
|
803
|
+
defs += pattern(pid, patOf(p), pal[p % pal.length], o.background || th.tile);
|
|
804
|
+
fills[p] = "url(#" + pid + ")";
|
|
805
|
+
}
|
|
419
806
|
var colourOf = function(j, i) {
|
|
420
|
-
return grouped ? pal[j % pal.length] : many ? pal[i % pal.length] : pal[0];
|
|
807
|
+
return grouped ? fills[j] || pal[j % pal.length] : many ? pal[i % pal.length] : pal[0];
|
|
808
|
+
};
|
|
809
|
+
var key = patOf && function(j, x, y) {
|
|
810
|
+
return '<rect x="' + r2(x) + '" y="' + r2(y + 1) + '" width="10" height="10" rx="2" fill="' + colourOf(j) + '"/>';
|
|
421
811
|
};
|
|
422
812
|
var opac = function(val) {
|
|
423
813
|
return grouped || many || o.highlight === "none" || val === top ? 1 : 0.5;
|
|
@@ -434,7 +824,7 @@ function bars(o, th) {
|
|
|
434
824
|
var d0 = dims(o);
|
|
435
825
|
var legendH = grouped || names.length ? legend(S.map(function(_, j) {
|
|
436
826
|
return nameOf(j);
|
|
437
|
-
}), pal, th, 0, 0, d0.w).height : 0;
|
|
827
|
+
}), pal, th, 0, 0, d0.w, key, key && 15).height : 0;
|
|
438
828
|
var rows = stacked ? 1 : k, thin = grouped && !stacked;
|
|
439
829
|
var subH = thin ? 10 : 14, subGap = 3;
|
|
440
830
|
var rowH = horiz ? rows * subH + (rows - 1) * subGap + (thin ? 14 : 16) : 0;
|
|
@@ -442,11 +832,11 @@ function bars(o, th) {
|
|
|
442
832
|
var best = v0.indexOf(Math.max.apply(null, v0.length ? v0 : [0]));
|
|
443
833
|
return tile(o, th, h, function(x0, y0, w, h2) {
|
|
444
834
|
if (!n) return emptyState(th, x0, y0, w, h2);
|
|
445
|
-
var s = "", top0 = y0;
|
|
835
|
+
var s = defs ? "<defs>" + defs + "</defs>" : "", top0 = y0;
|
|
446
836
|
if (legendH) {
|
|
447
837
|
s += legend(S.map(function(_, j) {
|
|
448
838
|
return nameOf(j);
|
|
449
|
-
}), pal, th, x0, y0, w).svg;
|
|
839
|
+
}), pal, th, x0, y0, w, key, key && 15).svg;
|
|
450
840
|
top0 += legendH;
|
|
451
841
|
}
|
|
452
842
|
var lo = Math.min(0, Math.min.apply(null, scale)), hi = Math.max(0, top);
|
|
@@ -597,33 +987,196 @@ function bars(o, th) {
|
|
|
597
987
|
summary: !n ? "no data" : stacked ? k + " series stacked across " + n + " groups" : grouped ? k + " series across " + n + " groups" : "highest " + (labels[best] || "") + " " + fmt(v0[best], o)
|
|
598
988
|
});
|
|
599
989
|
}
|
|
990
|
+
bars.table = function(o) {
|
|
991
|
+
return seriesTable(o, series(o.values).length > 1 && bool(o.stacked));
|
|
992
|
+
};
|
|
600
993
|
|
|
601
|
-
// src/
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
994
|
+
// src/entries/bars.js
|
|
995
|
+
register("bars", bars);
|
|
996
|
+
define("bars");
|
|
997
|
+
|
|
998
|
+
// src/charts/scatter.js
|
|
999
|
+
function fit(xs, ys) {
|
|
1000
|
+
var n = Math.min(xs.length, ys.length);
|
|
1001
|
+
if (n < 2) return null;
|
|
1002
|
+
var mx = 0, my = 0, i;
|
|
1003
|
+
for (i = 0; i < n; i++) {
|
|
1004
|
+
mx += xs[i];
|
|
1005
|
+
my += ys[i];
|
|
1006
|
+
}
|
|
1007
|
+
mx /= n;
|
|
1008
|
+
my /= n;
|
|
1009
|
+
var sxx = 0, syy = 0, sxy = 0;
|
|
1010
|
+
for (i = 0; i < n; i++) {
|
|
1011
|
+
sxx += (xs[i] - mx) * (xs[i] - mx);
|
|
1012
|
+
syy += (ys[i] - my) * (ys[i] - my);
|
|
1013
|
+
sxy += (xs[i] - mx) * (ys[i] - my);
|
|
1014
|
+
}
|
|
1015
|
+
if (!sxx) return null;
|
|
1016
|
+
var slope = sxy / sxx;
|
|
1017
|
+
return { slope, intercept: my - slope * mx, r: syy ? sxy / Math.sqrt(sxx * syy) : 0 };
|
|
1018
|
+
}
|
|
1019
|
+
function scatter(o, th) {
|
|
1020
|
+
var X = series(o.x), Y = series(o.y), names = list(o.series), labels = list(o.labels), pal = palette(o, th);
|
|
1021
|
+
var groups = [];
|
|
1022
|
+
for (var g = 0; g < Math.max(X.length, Y.length); g++) {
|
|
1023
|
+
var xs = X[g] || [], ys = Y[g] || [], m = Math.min(xs.length, ys.length);
|
|
1024
|
+
groups.push({ xs: xs.slice(0, m), ys: ys.slice(0, m) });
|
|
1025
|
+
}
|
|
1026
|
+
groups = groups.filter(function(gr) {
|
|
1027
|
+
return gr.xs.length;
|
|
1028
|
+
});
|
|
1029
|
+
var count = groups.reduce(function(a, gr) {
|
|
1030
|
+
return a + gr.xs.length;
|
|
1031
|
+
}, 0);
|
|
1032
|
+
var h = num(o.height, 210), multi = groups.length > 1 || names.length > 0, trend = bool(o.trend);
|
|
1033
|
+
var nameOf = function(j) {
|
|
1034
|
+
return names[j] || "Group " + (j + 1);
|
|
1035
|
+
};
|
|
1036
|
+
var xo = { decimals: o.decimals, compact: o.compact, lang: o.lang };
|
|
1037
|
+
var notes = parseNotes(o, labels);
|
|
1038
|
+
var markOf = styleOf(o.markers, SHAPES, "circle"), bg = o.background || th.tile;
|
|
1039
|
+
var shapeOf = function(j) {
|
|
1040
|
+
var m2 = markOf ? markOf(j) : "circle";
|
|
1041
|
+
return m2 === "none" ? "circle" : m2;
|
|
1042
|
+
};
|
|
1043
|
+
var key = markOf && function(j, x, y) {
|
|
1044
|
+
return marker(shapeOf(j), x + 4, y + 6, 4, ' fill="' + pal[j % pal.length] + '"');
|
|
608
1045
|
};
|
|
609
|
-
var st = statOf(v, o, "");
|
|
610
1046
|
return tile(o, th, h, function(x0, y0, w, h2) {
|
|
611
|
-
if (!
|
|
612
|
-
var s = "",
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
1047
|
+
if (!count) return emptyState(th, x0, y0, w, h2);
|
|
1048
|
+
var s = "", top = y0;
|
|
1049
|
+
if (multi) {
|
|
1050
|
+
var lg = legend(groups.map(function(_, j) {
|
|
1051
|
+
return nameOf(j);
|
|
1052
|
+
}), pal, th, x0, top, w, key);
|
|
1053
|
+
s += lg.svg;
|
|
1054
|
+
top += lg.height;
|
|
1055
|
+
}
|
|
1056
|
+
var ax = [].concat.apply([], groups.map(function(gr) {
|
|
1057
|
+
return gr.xs;
|
|
1058
|
+
}));
|
|
1059
|
+
var ay = [].concat.apply([], groups.map(function(gr) {
|
|
1060
|
+
return gr.ys;
|
|
1061
|
+
}));
|
|
1062
|
+
var ymin = Math.min.apply(null, ay), ymax = Math.max.apply(null, ay);
|
|
1063
|
+
if (has(o.goal)) {
|
|
1064
|
+
ymin = Math.min(ymin, +o.goal);
|
|
1065
|
+
ymax = Math.max(ymax, +o.goal);
|
|
1066
|
+
}
|
|
1067
|
+
if (bool(o.zero)) {
|
|
1068
|
+
ymin = Math.min(0, ymin);
|
|
1069
|
+
ymax = Math.max(0, ymax);
|
|
1070
|
+
}
|
|
1071
|
+
var sy = niceScale(ymin, ymax, 4), sx = niceScale(Math.min.apply(null, ax), Math.max.apply(null, ax), 5);
|
|
1072
|
+
var tly = sy.ticks.map(function(t) {
|
|
1073
|
+
return fmt(t, o, true);
|
|
1074
|
+
});
|
|
1075
|
+
var axisW = axisWidth(tly) + (has(o.yLabel) ? 14 : 0);
|
|
1076
|
+
var bottom = 20 + (has(o.xLabel) ? 16 : 0);
|
|
1077
|
+
var px = x0 + axisW, pw = w - axisW, ph = h2 - (top - y0) - bottom;
|
|
1078
|
+
var PX = function(v) {
|
|
1079
|
+
return px + 6 + (v - sx.lo) / (sx.hi - sx.lo) * (pw - 12);
|
|
1080
|
+
};
|
|
1081
|
+
var PY = function(v) {
|
|
1082
|
+
return top + 6 + ph - 6 - (v - sy.lo) / (sy.hi - sy.lo) * (ph - 6);
|
|
1083
|
+
};
|
|
1084
|
+
sy.ticks.forEach(function(t, i) {
|
|
1085
|
+
var y = PY(t);
|
|
1086
|
+
s += '<line x1="' + r2(px) + '" x2="' + r2(px + pw) + '" y1="' + r2(y) + '" y2="' + r2(y) + '" stroke="' + th.grid + '" stroke-dasharray="2 4"/>' + text(px - 8, y + 3.5, tly[i], small(th, 10, 'text-anchor="end"'));
|
|
1087
|
+
});
|
|
1088
|
+
var every = Math.max(1, Math.ceil(sx.ticks.length * 44 / pw));
|
|
1089
|
+
sx.ticks.forEach(function(t, i) {
|
|
1090
|
+
if (i % every) return;
|
|
1091
|
+
var x = PX(t);
|
|
1092
|
+
s += '<line x1="' + r2(x) + '" x2="' + r2(x) + '" y1="' + r2(top) + '" y2="' + r2(top + ph) + '" stroke="' + th.grid + '" stroke-dasharray="2 4"/>' + text(x, top + ph + 15, fmt(t, xo, true), small(th, 10, 'text-anchor="middle"'));
|
|
1093
|
+
});
|
|
1094
|
+
if (has(o.xLabel)) s += text(px + pw / 2, top + ph + 32, o.xLabel, small(th, 10, 'text-anchor="middle" font-weight="600"'));
|
|
1095
|
+
if (has(o.yLabel)) {
|
|
1096
|
+
var cy = top + ph / 2;
|
|
1097
|
+
s += '<text x="' + r2(x0 + 4) + '" y="' + r2(cy) + '" transform="rotate(-90 ' + r2(x0 + 4) + " " + r2(cy) + ')" text-anchor="middle" ' + small(th, 10, 'font-weight="600"') + ' dominant-baseline="hanging">' + String(o.yLabel).replace(/[&<>"]/g, function(c) {
|
|
1098
|
+
return { "&": "&", "<": "<", ">": ">", '"': """ }[c];
|
|
1099
|
+
}) + "</text>";
|
|
1100
|
+
}
|
|
1101
|
+
if (has(o.goal)) s += goalLine(o, th, px, px + pw, PY(+o.goal));
|
|
1102
|
+
if (trend) {
|
|
1103
|
+
groups.forEach(function(gr, j) {
|
|
1104
|
+
var f = fit(gr.xs, gr.ys);
|
|
1105
|
+
if (!f) return;
|
|
1106
|
+
var xa = Math.min.apply(null, gr.xs), xb = Math.max.apply(null, gr.xs);
|
|
1107
|
+
var ya = f.slope * xa + f.intercept, yb = f.slope * xb + f.intercept;
|
|
1108
|
+
var c = pal[j % pal.length];
|
|
1109
|
+
s += '<line x1="' + r2(PX(xa)) + '" y1="' + r2(clamp(PY(ya), top, top + ph)) + '" x2="' + r2(PX(xb)) + '" y2="' + r2(clamp(PY(yb), top, top + ph)) + '" stroke="' + c + '" stroke-width="2" stroke-dasharray="6 4" stroke-linecap="round" stroke-opacity=".9"/>';
|
|
1110
|
+
s += text(Math.min(PX(xb), px + pw) - 2, clamp(PY(yb), top + 10, top + ph) - 7, "r = " + f.r.toFixed(2), small(th, 10, 'text-anchor="end" font-weight="600"', c));
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
var idx = 0, r = count > 150 ? 3 : 4.5;
|
|
1114
|
+
groups.forEach(function(gr, j) {
|
|
1115
|
+
var c = pal[j % pal.length];
|
|
1116
|
+
gr.xs.forEach(function(xv, i) {
|
|
1117
|
+
var cx = PX(xv), cy2 = PY(gr.ys[i]);
|
|
1118
|
+
var tip = (labels[idx] ? labels[idx] + "\n" : multi ? nameOf(j) + "\n" : "") + (has(o.xLabel) ? o.xLabel : "x") + ": " + fmt(xv, xo) + "\n" + (has(o.yLabel) ? o.yLabel : "y") + ": " + fmt(gr.ys[i], o);
|
|
1119
|
+
s += marker(shapeOf(j), cx, cy2, r, ' fill="' + c + '" fill-opacity=".85" stroke="' + bg + '" stroke-width="1.5"' + (o.animate ? ' class="bzc-a" style="animation-delay:' + r2(Math.min(0.8, idx * 0.012)) + 's"' : ""));
|
|
1120
|
+
s += hit(cx - 8, cy2 - 8, 16, 16, tip);
|
|
1121
|
+
idx++;
|
|
1122
|
+
});
|
|
1123
|
+
});
|
|
1124
|
+
var flat = [];
|
|
1125
|
+
groups.forEach(function(gr) {
|
|
1126
|
+
gr.xs.forEach(function(xv, i) {
|
|
1127
|
+
flat.push([PX(xv), PY(gr.ys[i])]);
|
|
1128
|
+
});
|
|
1129
|
+
});
|
|
1130
|
+
notes.forEach(function(nt) {
|
|
1131
|
+
var p = flat[nt.i];
|
|
1132
|
+
if (!p) return;
|
|
1133
|
+
s += '<circle cx="' + r2(p[0]) + '" cy="' + r2(p[1]) + '" r="' + (r + 4) + '" fill="none" stroke="' + th.fg + '" stroke-width="1.5"/>' + text(clamp(p[0], px + 30, px + pw - 30), p[1] - r - 9, nt.text, small(th, 10, 'text-anchor="middle"', th.fg));
|
|
1134
|
+
});
|
|
1135
|
+
return s;
|
|
1136
|
+
}, { summary: count + " points" + (multi ? " in " + groups.length + " groups" : "") });
|
|
1137
|
+
}
|
|
1138
|
+
scatter.table = function(o) {
|
|
1139
|
+
var X = series(o.x), Y = series(o.y), names = list(o.series), labels = list(o.labels), multi = X.length > 1 || names.length > 0;
|
|
1140
|
+
var xo = { decimals: o.decimals, compact: o.compact, lang: o.lang }, rows = [], idx = 0;
|
|
1141
|
+
X.forEach(function(xs, g) {
|
|
1142
|
+
var ys = Y[g] || [];
|
|
1143
|
+
for (var i = 0; i < Math.min(xs.length, ys.length); i++, idx++) {
|
|
1144
|
+
rows.push([labels[idx] || "#" + (idx + 1)].concat(multi ? [names[g] || "Group " + (g + 1)] : [], [fmt(xs[i], xo), fmt(ys[i], o)]));
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
return { head: ["Point"].concat(multi ? ["Group"] : [], [has(o.xLabel) ? o.xLabel : "x", has(o.yLabel) ? o.yLabel : "y"]), rows };
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
// src/entries/scatter.js
|
|
1151
|
+
register("scatter", scatter);
|
|
1152
|
+
define("scatter");
|
|
1153
|
+
|
|
1154
|
+
// src/charts/ring.js
|
|
1155
|
+
function ring(o, th) {
|
|
1156
|
+
var v = nums(o.values), labels = list(o.labels), pal = palette(o, th);
|
|
1157
|
+
var max = num(o.max, 100) || 100, h = num(o.height, 140);
|
|
1158
|
+
var auto = !has(o.prefix) && !has(o.suffix) && max === 100;
|
|
1159
|
+
var show = function(n) {
|
|
1160
|
+
return fmt(n, o) + (auto ? "%" : "");
|
|
1161
|
+
};
|
|
1162
|
+
var st = statOf(v, o, "");
|
|
1163
|
+
return tile(o, th, h, function(x0, y0, w, h2) {
|
|
1164
|
+
if (!v.length) return emptyState(th, x0, y0, w, h2);
|
|
1165
|
+
var s = "", single = v.length === 1;
|
|
1166
|
+
var size = Math.min(h2, single ? w : w * 0.55);
|
|
1167
|
+
var cx = single ? x0 + w / 2 : x0 + size / 2, cy = y0 + h2 / 2;
|
|
1168
|
+
var sw = single ? size * 0.12 : clamp(size * 0.42 / (v.length * 1.35), 5, 16), gap = sw * 0.38;
|
|
1169
|
+
v.forEach(function(n, i) {
|
|
1170
|
+
var r = size / 2 - sw / 2 - i * (sw + gap);
|
|
1171
|
+
if (r <= sw / 2) return;
|
|
1172
|
+
var pct = clamp(n / max * 100, 0, 100), c = pal[i % pal.length];
|
|
1173
|
+
s += '<circle cx="' + r2(cx) + '" cy="' + r2(cy) + '" r="' + r2(r) + '" fill="none" stroke="' + c + '" stroke-opacity=".16" stroke-width="' + r2(sw) + '"/>';
|
|
1174
|
+
if (pct > 0) s += '<circle cx="' + r2(cx) + '" cy="' + r2(cy) + '" r="' + r2(r) + '" fill="none" stroke="' + c + '" stroke-width="' + r2(sw) + '" stroke-linecap="round" pathLength="100" transform="rotate(-90 ' + r2(cx) + " " + r2(cy) + ')" style="stroke-dasharray:' + r2(pct) + " 100" + (o.animate ? ";animation-delay:" + i * 0.12 + "s" : "") + '"' + (o.animate ? ' class="bzc-s"' : "") + "/>";
|
|
1175
|
+
s += '<circle cx="' + r2(cx) + '" cy="' + r2(cy) + '" r="' + r2(r + sw / 2) + '" fill="#000" fill-opacity="0" class="bzc-hit" data-tip="' + esc((labels[i] || "#" + (i + 1)) + ": " + show(n) + (auto ? "" : " of " + fmt(max, o))) + '"/>';
|
|
1176
|
+
});
|
|
1177
|
+
if (single) {
|
|
1178
|
+
s += text(cx, cy + size * 0.08, show(v[0]), 'text-anchor="middle" fill="' + th.fg + '" font-family="' + SANS + '" font-size="' + r2(size * 0.22) + '" font-weight="800"');
|
|
1179
|
+
if (labels[0]) s += text(cx, cy + size * 0.08 + 16, labels[0], small(th, 10, 'text-anchor="middle"'));
|
|
627
1180
|
} else {
|
|
628
1181
|
var lx = x0 + size + 22, rowH = Math.min(26, h2 / v.length), ly = cy - rowH * v.length / 2;
|
|
629
1182
|
v.forEach(function(n, i) {
|
|
@@ -636,6 +1189,16 @@ function ring(o, th) {
|
|
|
636
1189
|
return (labels[i] ? labels[i] + " " : "") + show(n);
|
|
637
1190
|
}).join(", ") });
|
|
638
1191
|
}
|
|
1192
|
+
ring.table = function(o) {
|
|
1193
|
+
var labels = list(o.labels), max = num(o.max, 100) || 100, auto = !has(o.prefix) && !has(o.suffix) && max === 100;
|
|
1194
|
+
return { head: ["Label", "Value"], rows: nums(o.values).map(function(n, i) {
|
|
1195
|
+
return [labels[i] || "#" + (i + 1), fmt(n, o) + (auto ? "%" : " of " + fmt(max, o))];
|
|
1196
|
+
}) };
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
// src/entries/ring.js
|
|
1200
|
+
register("ring", ring);
|
|
1201
|
+
define("ring");
|
|
639
1202
|
|
|
640
1203
|
// src/charts/donut.js
|
|
641
1204
|
function donut(o, th) {
|
|
@@ -688,12 +1251,24 @@ function donut(o, th) {
|
|
|
688
1251
|
v.forEach(function(n, i) {
|
|
689
1252
|
var len = n / total * 100;
|
|
690
1253
|
if (len <= 0) return;
|
|
691
|
-
s += '<circle cx="' + r2(cx) + '" cy="' + r2(cy) + '" r="' + r2(r) + '" fill="none" stroke="#000" stroke-opacity="0" stroke-width="' + r2(sw) + '" pathLength="100" stroke-dasharray="' + r2(len) + " " + r2(100 - len) + '" stroke-dashoffset="' + r2(-start) + '" transform="rotate(-90 ' + r2(cx) + " " + r2(cy) + ')" class="bzc-hit" data-tip="' + esc((labels[i] || "#" + (i + 1)) + ": " + fmt(n, o) + " (" + Math.round(len) + "%)") + '"/>';
|
|
1254
|
+
s += '<circle cx="' + r2(cx) + '" cy="' + r2(cy) + '" r="' + r2(r) + '" fill="none" stroke="#000" stroke-opacity="0" stroke-width="' + r2(sw) + '" pathLength="100" stroke-dasharray="' + r2(len) + " " + r2(100 - len) + '" stroke-dashoffset="' + r2(-start) + '" transform="rotate(-90 ' + r2(cx) + " " + r2(cy) + ')" class="bzc-hit" data-skip="" data-tip="' + esc((labels[i] || "#" + (i + 1)) + ": " + fmt(n, o) + " (" + Math.round(len) + "%)") + '"/>';
|
|
692
1255
|
start += len;
|
|
693
1256
|
});
|
|
694
1257
|
return s;
|
|
695
1258
|
}, { stat: st, summary: "total " + fmt(total, o) + " across " + v.length + " parts" });
|
|
696
1259
|
}
|
|
1260
|
+
donut.table = function(o) {
|
|
1261
|
+
var v = nums(o.values).map(function(n) {
|
|
1262
|
+
return Math.max(0, n);
|
|
1263
|
+
}), labels = list(o.labels), total = sum(v);
|
|
1264
|
+
return { head: ["Label", "Value", "Share"], rows: v.map(function(n, i) {
|
|
1265
|
+
return [labels[i] || "#" + (i + 1), fmt(n, o), (total ? Math.round(n / total * 100) : 0) + "%"];
|
|
1266
|
+
}) };
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
// src/entries/donut.js
|
|
1270
|
+
register("donut", donut);
|
|
1271
|
+
define("donut");
|
|
697
1272
|
|
|
698
1273
|
// src/charts/gauge.js
|
|
699
1274
|
function gauge(o, th) {
|
|
@@ -740,6 +1315,16 @@ function gauge(o, th) {
|
|
|
740
1315
|
return s;
|
|
741
1316
|
}, { summary: isFinite(val) ? show(val) + " on a scale of " + fmt(mn, o) + " to " + fmt(mx, o) : "no data" });
|
|
742
1317
|
}
|
|
1318
|
+
gauge.table = function(o) {
|
|
1319
|
+
var v = nums(o.values), val = v.length ? v[0] : num(o.value, NaN), labels = list(o.labels);
|
|
1320
|
+
var mn = num(o.min, 0), mx = num(o.max, 100), auto = !has(o.prefix) && !has(o.suffix) && mn === 0 && mx === 100;
|
|
1321
|
+
if (!isFinite(val)) return { head: ["Label", "Value"], rows: [] };
|
|
1322
|
+
return { head: ["Label", "Value"], rows: [[labels[0] || "Reading", fmt(val, o) + (auto ? "%" : "")], ["Lowest", fmt(mn, o)], ["Highest", fmt(mx, o)]].concat(has(o.goal) ? [["Goal", fmt(+o.goal, o)]] : []) };
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1325
|
+
// src/entries/gauge.js
|
|
1326
|
+
register("gauge", gauge);
|
|
1327
|
+
define("gauge");
|
|
743
1328
|
|
|
744
1329
|
// src/charts/progress.js
|
|
745
1330
|
function progress(o, th) {
|
|
@@ -802,16 +1387,40 @@ function progress(o, th) {
|
|
|
802
1387
|
return s;
|
|
803
1388
|
}, { stat: st, summary: single ? show(v[0]) + (auto ? "" : " of " + fmt(max, o)) : fmt(total, o) + " of " + fmt(max, o) });
|
|
804
1389
|
}
|
|
1390
|
+
progress.table = function(o) {
|
|
1391
|
+
var v = nums(o.values).map(function(n) {
|
|
1392
|
+
return Math.max(0, n);
|
|
1393
|
+
}), labels = list(o.labels), single = v.length === 1;
|
|
1394
|
+
var max = has(o.max) ? num(o.max, 100) : single ? 100 : sum(v);
|
|
1395
|
+
if (!(max > 0)) max = 1;
|
|
1396
|
+
var auto = !has(o.prefix) && !has(o.suffix) && max === 100 && single;
|
|
1397
|
+
if (single) return { head: ["Label", "Value"], rows: [[labels[0] || "Progress", fmt(v[0], o) + (auto ? "%" : " of " + fmt(max, o))]] };
|
|
1398
|
+
return { head: ["Label", "Value", "Share"], rows: v.map(function(n, i) {
|
|
1399
|
+
return [labels[i] || "#" + (i + 1), fmt(n, o), Math.round(n / max * 100) + "%"];
|
|
1400
|
+
}) };
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
// src/entries/progress.js
|
|
1404
|
+
register("progress", progress);
|
|
1405
|
+
define("progress");
|
|
805
1406
|
|
|
806
1407
|
// src/charts/heatmap.js
|
|
1408
|
+
var FULL = { weekday: "short", day: "numeric", month: "short", year: "numeric" };
|
|
1409
|
+
function startOf(o, len) {
|
|
1410
|
+
if (has(o.start) && /^\d{4}-\d{2}-\d{2}$/.test(String(o.start))) return /* @__PURE__ */ new Date(String(o.start) + "T00:00:00Z");
|
|
1411
|
+
var t = /* @__PURE__ */ new Date();
|
|
1412
|
+
return new Date(Date.UTC(t.getFullYear(), t.getMonth(), t.getDate()) - Math.max(0, len - 1) * 864e5);
|
|
1413
|
+
}
|
|
1414
|
+
function dates(lang, opt) {
|
|
1415
|
+
try {
|
|
1416
|
+
return new Intl.DateTimeFormat(lang, Object.assign({ timeZone: "UTC" }, opt));
|
|
1417
|
+
} catch (e) {
|
|
1418
|
+
return new Intl.DateTimeFormat("en-GB", Object.assign({ timeZone: "UTC" }, opt));
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
807
1421
|
function heatmap(o, th) {
|
|
808
1422
|
var v = nums(o.values), c = palette(o, th)[0], lang = o.lang || "en-GB";
|
|
809
|
-
var start;
|
|
810
|
-
if (has(o.start) && /^\d{4}-\d{2}-\d{2}$/.test(String(o.start))) start = /* @__PURE__ */ new Date(String(o.start) + "T00:00:00Z");
|
|
811
|
-
else {
|
|
812
|
-
var t = /* @__PURE__ */ new Date();
|
|
813
|
-
start = new Date(Date.UTC(t.getFullYear(), t.getMonth(), t.getDate()) - Math.max(0, v.length - 1) * 864e5);
|
|
814
|
-
}
|
|
1423
|
+
var start = startOf(o, v.length);
|
|
815
1424
|
var offset = (start.getUTCDay() + 6) % 7, cols = Math.max(1, Math.ceil((offset + v.length) / 7));
|
|
816
1425
|
var d0 = dims(o), leftW = 28, maxCols = Math.max(1, Math.floor((d0.w - leftW) / 10));
|
|
817
1426
|
if (cols > maxCols) {
|
|
@@ -825,13 +1434,9 @@ function heatmap(o, th) {
|
|
|
825
1434
|
var h = 16 + 7 * pitch + 26;
|
|
826
1435
|
var max = v.length ? Math.max.apply(null, v) : 0;
|
|
827
1436
|
var dfm = function(opt) {
|
|
828
|
-
|
|
829
|
-
return new Intl.DateTimeFormat(lang, Object.assign({ timeZone: "UTC" }, opt));
|
|
830
|
-
} catch (e) {
|
|
831
|
-
return new Intl.DateTimeFormat("en-GB", Object.assign({ timeZone: "UTC" }, opt));
|
|
832
|
-
}
|
|
1437
|
+
return dates(lang, opt);
|
|
833
1438
|
};
|
|
834
|
-
var monthF = dfm({ month: "short" }), dayF = dfm({ weekday: "short" }), fullF = dfm(
|
|
1439
|
+
var monthF = dfm({ month: "short" }), dayF = dfm({ weekday: "short" }), fullF = dfm(FULL);
|
|
835
1440
|
var st = statOf(v, o, "");
|
|
836
1441
|
return tile(o, th, h, function(x0, y0, w, h2) {
|
|
837
1442
|
if (!v.length) return emptyState(th, x0, y0, w, h2);
|
|
@@ -861,357 +1466,20 @@ function heatmap(o, th) {
|
|
|
861
1466
|
return s;
|
|
862
1467
|
}, { stat: st, summary: fmt(sum(v), o) + " across " + v.length + " days" });
|
|
863
1468
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
var mx = 0, my = 0, i;
|
|
870
|
-
for (i = 0; i < n; i++) {
|
|
871
|
-
mx += xs[i];
|
|
872
|
-
my += ys[i];
|
|
873
|
-
}
|
|
874
|
-
mx /= n;
|
|
875
|
-
my /= n;
|
|
876
|
-
var sxx = 0, syy = 0, sxy = 0;
|
|
877
|
-
for (i = 0; i < n; i++) {
|
|
878
|
-
sxx += (xs[i] - mx) * (xs[i] - mx);
|
|
879
|
-
syy += (ys[i] - my) * (ys[i] - my);
|
|
880
|
-
sxy += (xs[i] - mx) * (ys[i] - my);
|
|
881
|
-
}
|
|
882
|
-
if (!sxx) return null;
|
|
883
|
-
var slope = sxy / sxx;
|
|
884
|
-
return { slope, intercept: my - slope * mx, r: syy ? sxy / Math.sqrt(sxx * syy) : 0 };
|
|
885
|
-
}
|
|
886
|
-
function scatter(o, th) {
|
|
887
|
-
var X = series(o.x), Y = series(o.y), names = list(o.series), labels = list(o.labels), pal = palette(o, th);
|
|
888
|
-
var groups = [];
|
|
889
|
-
for (var g = 0; g < Math.max(X.length, Y.length); g++) {
|
|
890
|
-
var xs = X[g] || [], ys = Y[g] || [], m = Math.min(xs.length, ys.length);
|
|
891
|
-
groups.push({ xs: xs.slice(0, m), ys: ys.slice(0, m) });
|
|
892
|
-
}
|
|
893
|
-
groups = groups.filter(function(gr) {
|
|
894
|
-
return gr.xs.length;
|
|
895
|
-
});
|
|
896
|
-
var count = groups.reduce(function(a, gr) {
|
|
897
|
-
return a + gr.xs.length;
|
|
898
|
-
}, 0);
|
|
899
|
-
var h = num(o.height, 210), multi = groups.length > 1 || names.length > 0, trend = bool(o.trend);
|
|
900
|
-
var nameOf = function(j) {
|
|
901
|
-
return names[j] || "Group " + (j + 1);
|
|
902
|
-
};
|
|
903
|
-
var xo = { decimals: o.decimals, compact: o.compact, lang: o.lang };
|
|
904
|
-
var notes = parseNotes(o, labels);
|
|
905
|
-
return tile(o, th, h, function(x0, y0, w, h2) {
|
|
906
|
-
if (!count) return emptyState(th, x0, y0, w, h2);
|
|
907
|
-
var s = "", top = y0;
|
|
908
|
-
if (multi) {
|
|
909
|
-
var lg = legend(groups.map(function(_, j) {
|
|
910
|
-
return nameOf(j);
|
|
911
|
-
}), pal, th, x0, top, w);
|
|
912
|
-
s += lg.svg;
|
|
913
|
-
top += lg.height;
|
|
914
|
-
}
|
|
915
|
-
var ax = [].concat.apply([], groups.map(function(gr) {
|
|
916
|
-
return gr.xs;
|
|
917
|
-
}));
|
|
918
|
-
var ay = [].concat.apply([], groups.map(function(gr) {
|
|
919
|
-
return gr.ys;
|
|
920
|
-
}));
|
|
921
|
-
var ymin = Math.min.apply(null, ay), ymax = Math.max.apply(null, ay);
|
|
922
|
-
if (has(o.goal)) {
|
|
923
|
-
ymin = Math.min(ymin, +o.goal);
|
|
924
|
-
ymax = Math.max(ymax, +o.goal);
|
|
925
|
-
}
|
|
926
|
-
if (bool(o.zero)) {
|
|
927
|
-
ymin = Math.min(0, ymin);
|
|
928
|
-
ymax = Math.max(0, ymax);
|
|
929
|
-
}
|
|
930
|
-
var sy = niceScale(ymin, ymax, 4), sx = niceScale(Math.min.apply(null, ax), Math.max.apply(null, ax), 5);
|
|
931
|
-
var tly = sy.ticks.map(function(t) {
|
|
932
|
-
return fmt(t, o, true);
|
|
933
|
-
});
|
|
934
|
-
var axisW = axisWidth(tly) + (has(o.yLabel) ? 14 : 0);
|
|
935
|
-
var bottom = 20 + (has(o.xLabel) ? 16 : 0);
|
|
936
|
-
var px = x0 + axisW, pw = w - axisW, ph = h2 - (top - y0) - bottom;
|
|
937
|
-
var PX = function(v) {
|
|
938
|
-
return px + 6 + (v - sx.lo) / (sx.hi - sx.lo) * (pw - 12);
|
|
939
|
-
};
|
|
940
|
-
var PY = function(v) {
|
|
941
|
-
return top + 6 + ph - 6 - (v - sy.lo) / (sy.hi - sy.lo) * (ph - 6);
|
|
942
|
-
};
|
|
943
|
-
sy.ticks.forEach(function(t, i) {
|
|
944
|
-
var y = PY(t);
|
|
945
|
-
s += '<line x1="' + r2(px) + '" x2="' + r2(px + pw) + '" y1="' + r2(y) + '" y2="' + r2(y) + '" stroke="' + th.grid + '" stroke-dasharray="2 4"/>' + text(px - 8, y + 3.5, tly[i], small(th, 10, 'text-anchor="end"'));
|
|
946
|
-
});
|
|
947
|
-
var every = Math.max(1, Math.ceil(sx.ticks.length * 44 / pw));
|
|
948
|
-
sx.ticks.forEach(function(t, i) {
|
|
949
|
-
if (i % every) return;
|
|
950
|
-
var x = PX(t);
|
|
951
|
-
s += '<line x1="' + r2(x) + '" x2="' + r2(x) + '" y1="' + r2(top) + '" y2="' + r2(top + ph) + '" stroke="' + th.grid + '" stroke-dasharray="2 4"/>' + text(x, top + ph + 15, fmt(t, xo, true), small(th, 10, 'text-anchor="middle"'));
|
|
952
|
-
});
|
|
953
|
-
if (has(o.xLabel)) s += text(px + pw / 2, top + ph + 32, o.xLabel, small(th, 10, 'text-anchor="middle" font-weight="600"'));
|
|
954
|
-
if (has(o.yLabel)) {
|
|
955
|
-
var cy = top + ph / 2;
|
|
956
|
-
s += '<text x="' + r2(x0 + 4) + '" y="' + r2(cy) + '" transform="rotate(-90 ' + r2(x0 + 4) + " " + r2(cy) + ')" text-anchor="middle" ' + small(th, 10, 'font-weight="600"') + ' dominant-baseline="hanging">' + String(o.yLabel).replace(/[&<>"]/g, function(c) {
|
|
957
|
-
return { "&": "&", "<": "<", ">": ">", '"': """ }[c];
|
|
958
|
-
}) + "</text>";
|
|
959
|
-
}
|
|
960
|
-
if (has(o.goal)) s += goalLine(o, th, px, px + pw, PY(+o.goal));
|
|
961
|
-
if (trend) {
|
|
962
|
-
groups.forEach(function(gr, j) {
|
|
963
|
-
var f = fit(gr.xs, gr.ys);
|
|
964
|
-
if (!f) return;
|
|
965
|
-
var xa = Math.min.apply(null, gr.xs), xb = Math.max.apply(null, gr.xs);
|
|
966
|
-
var ya = f.slope * xa + f.intercept, yb = f.slope * xb + f.intercept;
|
|
967
|
-
var c = pal[j % pal.length];
|
|
968
|
-
s += '<line x1="' + r2(PX(xa)) + '" y1="' + r2(clamp(PY(ya), top, top + ph)) + '" x2="' + r2(PX(xb)) + '" y2="' + r2(clamp(PY(yb), top, top + ph)) + '" stroke="' + c + '" stroke-width="2" stroke-dasharray="6 4" stroke-linecap="round" stroke-opacity=".9"/>';
|
|
969
|
-
s += text(Math.min(PX(xb), px + pw) - 2, clamp(PY(yb), top + 10, top + ph) - 7, "r = " + f.r.toFixed(2), small(th, 10, 'text-anchor="end" font-weight="600"', c));
|
|
970
|
-
});
|
|
971
|
-
}
|
|
972
|
-
var idx = 0, r = count > 150 ? 3 : 4.5;
|
|
973
|
-
groups.forEach(function(gr, j) {
|
|
974
|
-
var c = pal[j % pal.length];
|
|
975
|
-
gr.xs.forEach(function(xv, i) {
|
|
976
|
-
var cx = PX(xv), cy2 = PY(gr.ys[i]);
|
|
977
|
-
var tip = (labels[idx] ? labels[idx] + "\n" : multi ? nameOf(j) + "\n" : "") + (has(o.xLabel) ? o.xLabel : "x") + ": " + fmt(xv, xo) + "\n" + (has(o.yLabel) ? o.yLabel : "y") + ": " + fmt(gr.ys[i], o);
|
|
978
|
-
s += '<circle cx="' + r2(cx) + '" cy="' + r2(cy2) + '" r="' + r + '" fill="' + c + '" fill-opacity=".85" stroke="' + (o.background || th.tile) + '" stroke-width="1.5"' + (o.animate ? ' class="bzc-a" style="animation-delay:' + r2(Math.min(0.8, idx * 0.012)) + 's"' : "") + "/>";
|
|
979
|
-
s += hit(cx - 8, cy2 - 8, 16, 16, tip);
|
|
980
|
-
idx++;
|
|
981
|
-
});
|
|
982
|
-
});
|
|
983
|
-
var flat = [];
|
|
984
|
-
groups.forEach(function(gr) {
|
|
985
|
-
gr.xs.forEach(function(xv, i) {
|
|
986
|
-
flat.push([PX(xv), PY(gr.ys[i])]);
|
|
987
|
-
});
|
|
988
|
-
});
|
|
989
|
-
notes.forEach(function(nt) {
|
|
990
|
-
var p = flat[nt.i];
|
|
991
|
-
if (!p) return;
|
|
992
|
-
s += '<circle cx="' + r2(p[0]) + '" cy="' + r2(p[1]) + '" r="' + (r + 4) + '" fill="none" stroke="' + th.fg + '" stroke-width="1.5"/>' + text(clamp(p[0], px + 30, px + pw - 30), p[1] - r - 9, nt.text, small(th, 10, 'text-anchor="middle"', th.fg));
|
|
993
|
-
});
|
|
994
|
-
return s;
|
|
995
|
-
}, { summary: count + " points" + (multi ? " in " + groups.length + " groups" : "") });
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
// src/render.js
|
|
999
|
-
var TYPES = { spark, line, bars, scatter, ring, donut, gauge, progress, heatmap };
|
|
1000
|
-
var camel = (k) => k.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1001
|
-
function render(type, options = {}) {
|
|
1002
|
-
if (!TYPES[type]) {
|
|
1003
|
-
throw new Error(`Bezel Charts: unknown chart type "${type}". Choose one of: ${Object.keys(TYPES).join(", ")}.`);
|
|
1004
|
-
}
|
|
1005
|
-
const o = {};
|
|
1006
|
-
for (const [k, v] of Object.entries(options)) o[camel(k)] = v;
|
|
1007
|
-
o.animate = bool(o.animate);
|
|
1008
|
-
o.frame = o.frame === "none" ? "none" : "bezel";
|
|
1009
|
-
const mode = o.theme === "light" || o.theme === "dark" ? o.theme : o.autoTheme || "dark";
|
|
1010
|
-
return TYPES[type](o, THEMES[mode]);
|
|
1011
|
-
}
|
|
1012
|
-
function toStatic(svg) {
|
|
1013
|
-
return svg.replace(/<rect class="bzc-hit"[^>]*\/>/g, "").replace(/<circle[^>]* class="bzc-hit"[^>]*\/>/g, "").replace(/ data-tip="[^"]*"/g, "");
|
|
1014
|
-
}
|
|
1015
|
-
function toSVG(type, options = {}) {
|
|
1016
|
-
return toStatic(render(type, { ...options, animate: false }));
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
// src/element.js
|
|
1020
|
-
var ATTRIBUTES = [
|
|
1021
|
-
"type",
|
|
1022
|
-
"values",
|
|
1023
|
-
"labels",
|
|
1024
|
-
"series",
|
|
1025
|
-
"heading",
|
|
1026
|
-
"prefix",
|
|
1027
|
-
"suffix",
|
|
1028
|
-
"decimals",
|
|
1029
|
-
"compact",
|
|
1030
|
-
"color",
|
|
1031
|
-
"theme",
|
|
1032
|
-
"frame",
|
|
1033
|
-
"width",
|
|
1034
|
-
"height",
|
|
1035
|
-
"goal",
|
|
1036
|
-
"goal-label",
|
|
1037
|
-
"notes",
|
|
1038
|
-
"show-values",
|
|
1039
|
-
"stat",
|
|
1040
|
-
"delta",
|
|
1041
|
-
"delta-label",
|
|
1042
|
-
"max",
|
|
1043
|
-
"min",
|
|
1044
|
-
"value",
|
|
1045
|
-
"zones",
|
|
1046
|
-
"horizontal",
|
|
1047
|
-
"straight",
|
|
1048
|
-
"zero",
|
|
1049
|
-
"start",
|
|
1050
|
-
"lang",
|
|
1051
|
-
"corners",
|
|
1052
|
-
"background",
|
|
1053
|
-
"center-label",
|
|
1054
|
-
"highlight",
|
|
1055
|
-
"x",
|
|
1056
|
-
"y",
|
|
1057
|
-
"x-label",
|
|
1058
|
-
"y-label",
|
|
1059
|
-
"trend",
|
|
1060
|
-
"static",
|
|
1061
|
-
"no-tooltips",
|
|
1062
|
-
"stacked",
|
|
1063
|
-
"angle-labels"
|
|
1064
|
-
];
|
|
1065
|
-
var HOST_CSS = `:host{display:block;position:relative;max-width:100%}:host([hidden]){display:none}svg{display:block;max-width:100%;height:auto;overflow:visible}.tip{position:absolute;left:0;top:0;pointer-events:none;background:#0b0b10;color:#f2f1f7;font:600 12px/1.5 ${MONO};padding:7px 10px;border-radius:10px;white-space:pre;opacity:0;transform:translate(-50%,calc(-100% - 12px));transition:opacity .12s;z-index:3;border:1px solid #2a2935;box-shadow:0 8px 24px rgba(0,0,0,.35)}.tip.on{opacity:1}`;
|
|
1066
|
-
var darkQuery = () => typeof matchMedia === "function" ? matchMedia("(prefers-color-scheme: dark)") : null;
|
|
1067
|
-
var pageTheme = () => {
|
|
1068
|
-
const t = typeof document !== "undefined" && document.documentElement?.getAttribute?.("data-theme");
|
|
1069
|
-
return t === "light" || t === "dark" ? t : null;
|
|
1070
|
-
};
|
|
1071
|
-
var live = /* @__PURE__ */ new Set();
|
|
1072
|
-
if (typeof MutationObserver === "function" && typeof document !== "undefined" && document.documentElement) {
|
|
1073
|
-
new MutationObserver(() => live.forEach((el) => el.refresh())).observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
|
|
1074
|
-
}
|
|
1075
|
-
var Base = typeof HTMLElement === "undefined" ? class {
|
|
1076
|
-
} : HTMLElement;
|
|
1077
|
-
var BezelChart = class extends Base {
|
|
1078
|
-
static get observedAttributes() {
|
|
1079
|
-
return ATTRIBUTES;
|
|
1080
|
-
}
|
|
1081
|
-
/** The default colour sets, so tools like the Studio can show them. */
|
|
1082
|
-
static get themes() {
|
|
1083
|
-
return THEMES;
|
|
1084
|
-
}
|
|
1085
|
-
/** Which theme the chart is drawing in right now: "dark" or "light". */
|
|
1086
|
-
get resolvedTheme() {
|
|
1087
|
-
const t = this.getAttribute("theme");
|
|
1088
|
-
return t === "light" || t === "dark" ? t : this.options.autoTheme;
|
|
1089
|
-
}
|
|
1090
|
-
/** The chart type this tag draws. Subclasses fix it; <bezel-chart> reads its type attribute. */
|
|
1091
|
-
get chartType() {
|
|
1092
|
-
return this.getAttribute("type") || "spark";
|
|
1093
|
-
}
|
|
1094
|
-
connectedCallback() {
|
|
1095
|
-
if (!this.shadowRoot) {
|
|
1096
|
-
this.attachShadow({ mode: "open" });
|
|
1097
|
-
this.shadowRoot.addEventListener("pointermove", (e) => this.#tip(e));
|
|
1098
|
-
this.shadowRoot.addEventListener("pointerdown", (e) => this.#tip(e));
|
|
1099
|
-
this.addEventListener("pointerleave", () => this.shadowRoot.querySelector(".tip")?.classList.remove("on"));
|
|
1100
|
-
}
|
|
1101
|
-
live.add(this);
|
|
1102
|
-
this.#dark = darkQuery();
|
|
1103
|
-
this.#dark?.addEventListener?.("change", this.#onScheme);
|
|
1104
|
-
if (typeof ResizeObserver === "function") {
|
|
1105
|
-
this.#ro = new ResizeObserver(([entry]) => {
|
|
1106
|
-
const w = Math.round(entry.contentRect.width);
|
|
1107
|
-
if (!this.#rendered || !this.hasAttribute("width") && w > 0 && Math.abs(w - this.#width) > 1) this.#render();
|
|
1108
|
-
});
|
|
1109
|
-
this.#ro.observe(this);
|
|
1110
|
-
} else {
|
|
1111
|
-
this.#render();
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
disconnectedCallback() {
|
|
1115
|
-
live.delete(this);
|
|
1116
|
-
this.#ro?.disconnect();
|
|
1117
|
-
this.#dark?.removeEventListener?.("change", this.#onScheme);
|
|
1118
|
-
}
|
|
1119
|
-
attributeChangedCallback() {
|
|
1120
|
-
if (!this.#rendered || this.#queued) return;
|
|
1121
|
-
this.#queued = true;
|
|
1122
|
-
queueMicrotask(() => {
|
|
1123
|
-
this.#queued = false;
|
|
1124
|
-
this.#render();
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
/** Current settings as a plain object, the same shape BezelCharts.render() takes. */
|
|
1128
|
-
get options() {
|
|
1129
|
-
const o = {};
|
|
1130
|
-
for (const name of ATTRIBUTES) if (this.hasAttribute(name)) o[name] = this.getAttribute(name);
|
|
1131
|
-
if (!o.width) o.width = this.#width || 360;
|
|
1132
|
-
o.autoTheme = pageTheme() || (this.#dark ? this.#dark.matches ? "dark" : "light" : "dark");
|
|
1133
|
-
return o;
|
|
1134
|
-
}
|
|
1135
|
-
/** Redraws the chart without replaying its entrance animation. */
|
|
1136
|
-
refresh() {
|
|
1137
|
-
if (this.#rendered) this.#render();
|
|
1138
|
-
}
|
|
1139
|
-
/** The chart as a standalone static SVG string. */
|
|
1140
|
-
toSVG(extra = {}) {
|
|
1141
|
-
return toStatic(render(this.chartType, { ...this.options, ...extra, animate: false }));
|
|
1142
|
-
}
|
|
1143
|
-
/** Saves the chart as an .svg file. */
|
|
1144
|
-
download(name) {
|
|
1145
|
-
const url = URL.createObjectURL(new Blob([this.toSVG()], { type: "image/svg+xml" }));
|
|
1146
|
-
const a = Object.assign(document.createElement("a"), {
|
|
1147
|
-
href: url,
|
|
1148
|
-
download: `${(name || this.getAttribute("heading") || this.chartType).replace(/[^\w-]+/g, "-").toLowerCase()}.svg`
|
|
1149
|
-
});
|
|
1150
|
-
document.body.append(a);
|
|
1151
|
-
a.click();
|
|
1152
|
-
a.remove();
|
|
1153
|
-
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
1154
|
-
}
|
|
1155
|
-
#ro = null;
|
|
1156
|
-
#dark = null;
|
|
1157
|
-
#width = 0;
|
|
1158
|
-
#rendered = false;
|
|
1159
|
-
#queued = false;
|
|
1160
|
-
#onScheme = () => {
|
|
1161
|
-
const t = this.getAttribute("theme");
|
|
1162
|
-
if (!t || t === "auto") this.refresh();
|
|
1163
|
-
};
|
|
1164
|
-
#render() {
|
|
1165
|
-
if (!this.shadowRoot) return;
|
|
1166
|
-
const w = Math.round(this.getBoundingClientRect?.().width || 0);
|
|
1167
|
-
this.#width = w > 0 ? w : 360;
|
|
1168
|
-
const o = this.options;
|
|
1169
|
-
o.animate = !this.#rendered && !this.hasAttribute("static");
|
|
1170
|
-
let svg;
|
|
1171
|
-
try {
|
|
1172
|
-
svg = render(this.chartType, o);
|
|
1173
|
-
} catch (err) {
|
|
1174
|
-
svg = `<p style="font:13px ${SANS};color:#d63f7c">${esc(err.message)}</p>`;
|
|
1175
|
-
}
|
|
1176
|
-
svg = svg.replace(/<title>[\s\S]*?<\/title>/, "");
|
|
1177
|
-
this.shadowRoot.innerHTML = `<style>${HOST_CSS}</style>${svg}<div class="tip" role="status"></div>`;
|
|
1178
|
-
this.#rendered = true;
|
|
1179
|
-
this.dispatchEvent(new CustomEvent("bezel-render", { bubbles: true }));
|
|
1180
|
-
}
|
|
1181
|
-
#tip(e) {
|
|
1182
|
-
const tip = this.shadowRoot.querySelector(".tip");
|
|
1183
|
-
if (!tip || this.hasAttribute("no-tooltips")) return;
|
|
1184
|
-
const target = e.target?.closest?.("[data-tip]");
|
|
1185
|
-
if (!target) {
|
|
1186
|
-
tip.classList.remove("on");
|
|
1187
|
-
return;
|
|
1188
|
-
}
|
|
1189
|
-
tip.textContent = target.getAttribute("data-tip");
|
|
1190
|
-
const box = this.getBoundingClientRect();
|
|
1191
|
-
const half = tip.offsetWidth / 2;
|
|
1192
|
-
tip.style.left = `${clamp(e.clientX - box.left, half, Math.max(half, box.width - half))}px`;
|
|
1193
|
-
tip.style.top = `${e.clientY - box.top}px`;
|
|
1194
|
-
tip.classList.add("on");
|
|
1195
|
-
}
|
|
1469
|
+
heatmap.table = function(o) {
|
|
1470
|
+
var v = nums(o.values), start = startOf(o, v.length), f = dates(o.lang || "en-GB", FULL);
|
|
1471
|
+
return { head: ["Date", "Value"], rows: v.map(function(n, i) {
|
|
1472
|
+
return [f.format(new Date(start.getTime() + i * 864e5)), fmt(n, o)];
|
|
1473
|
+
}) };
|
|
1196
1474
|
};
|
|
1197
1475
|
|
|
1476
|
+
// src/entries/heatmap.js
|
|
1477
|
+
register("heatmap", heatmap);
|
|
1478
|
+
define("heatmap");
|
|
1479
|
+
|
|
1198
1480
|
// src/index.js
|
|
1199
|
-
var version = "1.
|
|
1200
|
-
var types =
|
|
1201
|
-
if (typeof customElements !== "undefined" && typeof HTMLElement !== "undefined") {
|
|
1202
|
-
for (const type of types) {
|
|
1203
|
-
const tag = `bezel-${type}`;
|
|
1204
|
-
if (!customElements.get(tag)) {
|
|
1205
|
-
customElements.define(tag, class extends BezelChart {
|
|
1206
|
-
get chartType() {
|
|
1207
|
-
return type;
|
|
1208
|
-
}
|
|
1209
|
-
});
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
if (!customElements.get("bezel-chart")) customElements.define("bezel-chart", class extends BezelChart {
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1481
|
+
var version = "1.3.0";
|
|
1482
|
+
var types = NAMES.slice();
|
|
1215
1483
|
export {
|
|
1216
1484
|
BezelChart,
|
|
1217
1485
|
render,
|