@wick-charts/vue 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +18 -3
  2. package/dist/BarSeries.vue.js +26 -20
  3. package/dist/CandlestickSeries.vue.js +30 -24
  4. package/dist/ChartContainer.vue.js +88 -65
  5. package/dist/HeatmapSeries.vue.js +40 -0
  6. package/dist/HeatmapSeries.vue2.js +4 -0
  7. package/dist/LineSeries.vue.js +26 -20
  8. package/dist/PieSeries.vue.js +24 -18
  9. package/dist/core/dist/animation/animator.js +21 -21
  10. package/dist/core/dist/animation/config.js +114 -87
  11. package/dist/core/dist/animation/easing.js +3 -1
  12. package/dist/core/dist/animation/intro-wave.js +68 -0
  13. package/dist/core/dist/axis/dom-labels.js +13 -14
  14. package/dist/core/dist/chart/badge-animator.js +56 -0
  15. package/dist/core/dist/chart/edge-indicators.js +28 -13
  16. package/dist/core/dist/chart/last-value.js +18 -17
  17. package/dist/core/dist/chart/y-target.js +13 -12
  18. package/dist/core/dist/chart.js +679 -452
  19. package/dist/core/dist/components/annotation-chip.js +28 -0
  20. package/dist/core/dist/components/edge-indicator.js +58 -26
  21. package/dist/core/dist/components/loading-indicator.js +46 -0
  22. package/dist/core/dist/components/marker.js +49 -31
  23. package/dist/core/dist/components/reference-line.js +27 -36
  24. package/dist/core/dist/components/time-region.js +30 -28
  25. package/dist/core/dist/data/store.js +38 -19
  26. package/dist/core/dist/data/sync.js +60 -38
  27. package/dist/core/dist/heatmap-tooltip.js +16 -0
  28. package/dist/core/dist/interactions/handler.js +67 -43
  29. package/dist/core/dist/perf/perf-hud.js +5 -5
  30. package/dist/core/dist/scales/x-scale.js +85 -37
  31. package/dist/core/dist/scales/y-scale.js +38 -27
  32. package/dist/core/dist/series/bar-intro.js +18 -0
  33. package/dist/core/dist/series/bar.js +337 -205
  34. package/dist/core/dist/series/base-multi-layer.js +224 -96
  35. package/dist/core/dist/series/candlestick-intro.js +57 -0
  36. package/dist/core/dist/series/candlestick.js +454 -276
  37. package/dist/core/dist/series/heatmap.js +387 -0
  38. package/dist/core/dist/series/line-intro.js +85 -0
  39. package/dist/core/dist/series/line.js +590 -282
  40. package/dist/core/dist/series/painters/builtins.js +2 -2
  41. package/dist/core/dist/series/pie.js +396 -259
  42. package/dist/core/dist/series/register-builtins.js +8 -7
  43. package/dist/core/dist/series/types.js +6 -0
  44. package/dist/core/dist/series/wave-intro.js +26 -0
  45. package/dist/core/dist/theme/create.js +69 -63
  46. package/dist/core/dist/theme/themes/mint-breeze.js +2 -2
  47. package/dist/core/dist/theme/themes/sand-dune.js +2 -2
  48. package/dist/core/dist/utils/color.js +109 -34
  49. package/dist/core/dist/utils/deep-equal.js +18 -0
  50. package/dist/core/dist/utils/time.js +25 -19
  51. package/dist/index.d.ts +1405 -123
  52. package/dist/index.js +177 -130
  53. package/dist/ui/Crosshair.vue.js +20 -21
  54. package/dist/ui/HeatmapTooltip.vue.js +96 -0
  55. package/dist/ui/HeatmapTooltip.vue2.js +4 -0
  56. package/dist/ui/InfoBar.vue.js +46 -47
  57. package/dist/ui/Legend.vue.js +80 -79
  58. package/dist/ui/PieLegend.vue.js +5 -5
  59. package/dist/ui/PieTooltip.vue.js +77 -67
  60. package/dist/ui/Tooltip.vue.js +60 -60
  61. package/dist/ui/YAxis.vue.js +2 -2
  62. package/dist/ui/YLabel.vue.js +94 -61
  63. package/package.json +2 -2
  64. package/src/BarSeries.vue +23 -7
  65. package/src/CandlestickSeries.vue +23 -7
  66. package/src/ChartContainer.vue +134 -12
  67. package/src/HeatmapSeries.vue +69 -0
  68. package/src/LineSeries.vue +23 -7
  69. package/src/PieSeries.vue +22 -6
  70. package/src/index.ts +84 -3
  71. package/src/ui/Crosshair.vue +1 -2
  72. package/src/ui/HeatmapTooltip.vue +144 -0
  73. package/src/ui/InfoBar.vue +1 -2
  74. package/src/ui/Legend.vue +13 -0
  75. package/src/ui/Marker.vue +1 -1
  76. package/src/ui/PieTooltip.vue +10 -0
  77. package/src/ui/ReferenceLine.vue +2 -2
  78. package/src/ui/TimeRegion.vue +2 -2
  79. package/src/ui/Tooltip.vue +2 -2
  80. package/src/ui/YLabel.vue +123 -8
@@ -1,47 +1,69 @@
1
- import { normalizeTime as h } from "../utils/time.js";
2
- const y = { len: 0, firstTime: null, lastTime: null }, d = 20;
3
- function f(a) {
4
- return typeof a == "object" && a !== null && !Array.isArray(a) && Array.isArray(a.data);
1
+ import { normalizeTime as s } from "../utils/time.js";
2
+ const g = { len: 0, firstTime: null, lastTime: null, data: null }, d = 20;
3
+ function c(t, a) {
4
+ if (s(t.time) !== s(a.time)) return !1;
5
+ const r = Object.keys(t), e = Object.keys(a);
6
+ return r.length !== e.length ? !1 : r.every((n) => n === "time" || t[n] === a[n]);
5
7
  }
6
- function p(a) {
7
- if (!Array.isArray(a)) return [a];
8
- if (a.length === 0) return [{ data: [] }];
9
- const r = a[0];
10
- return Array.isArray(r) || f(r) ? a.map((t) => Array.isArray(t) ? { data: t } : t) : [{ data: a }];
8
+ function y(t, a) {
9
+ if (!t || t.length !== a.length || t === a) return !1;
10
+ for (let r = 0; r < a.length - 1; r++)
11
+ if (t[r] !== a[r] && !c(t[r], a[r]))
12
+ return !1;
13
+ return !0;
11
14
  }
12
- function A(a) {
13
- const { chart: r, id: t } = a, e = p(a.data), l = e.length > 1 ? e.map((n, i) => n.label ?? `Series ${i + 1}`) : [e[0].label];
14
- r.setSeriesLabels(t, l), r.setLayerColors(
15
- t,
16
- e.map((n) => n.color)
15
+ function p(t, a, r) {
16
+ if (!t || t.length === 0 || r <= 0 || t.length !== a.length - r) return !1;
17
+ for (let e = 1; e < r; e++)
18
+ if (s(a[e - 1].time) >= s(a[e].time)) return !1;
19
+ if (s(a[r - 1].time) >= s(t[0].time)) return !1;
20
+ for (let e = 0; e < t.length; e++)
21
+ if (!c(t[e], a[r + e])) return !1;
22
+ return !0;
23
+ }
24
+ function T(t) {
25
+ return typeof t == "object" && t !== null && !Array.isArray(t) && Array.isArray(t.data);
26
+ }
27
+ function A(t) {
28
+ if (!Array.isArray(t)) return [t];
29
+ if (t.length === 0) return [{ data: [] }];
30
+ const a = t[0];
31
+ return Array.isArray(a) || T(a) ? t.map((r) => Array.isArray(r) ? { data: r } : r) : [{ data: t }];
32
+ }
33
+ function D(t) {
34
+ const { chart: a, id: r } = t, e = A(t.data), n = e.length > 1 ? e.map((l, f) => l.label ?? `Series ${f + 1}`) : [e[0].label];
35
+ a.setSeriesLabels(r, n), a.setLayerColors(
36
+ r,
37
+ e.map((l) => l.color)
17
38
  );
18
- const s = [];
19
- return r.batch(() => {
20
- for (let n = 0; n < e.length; n++)
21
- s[n] = T({
22
- chart: r,
23
- id: t,
24
- data: e[n].data,
25
- prev: a.prev[n] ?? y,
26
- layerIndex: n
39
+ const i = [];
40
+ return a.batch(() => {
41
+ for (let l = 0; l < e.length; l++)
42
+ i[l] = b({
43
+ chart: a,
44
+ id: r,
45
+ data: e[l].data,
46
+ prev: t.prev[l] ?? g,
47
+ layerIndex: l
27
48
  });
28
- }), s;
49
+ }), i;
29
50
  }
30
- function T(a) {
31
- const { chart: r, id: t, data: e, prev: l, layerIndex: s } = a;
51
+ function b(t) {
52
+ const { chart: a, id: r, data: e, prev: n, layerIndex: i } = t;
32
53
  if (e.length === 0)
33
- return r.setSeriesData(t, [], s), { len: 0, firstTime: null, lastTime: null };
34
- const n = h(e[0].time), i = h(e[e.length - 1].time), c = l.firstTime !== null && l.firstTime !== n, u = e.length - l.len, m = l.lastTime !== null && l.lastTime !== i;
35
- return c && u === 0 && m ? r.batch(() => {
36
- r.appendData(t, e[e.length - 1], s), r.keepLast(t, e.length, s);
37
- }) : l.len === 0 || e.length < l.len || u > d || c ? r.setSeriesData(t, e, s) : e.length === l.len ? r.updateData(t, e[e.length - 1], s) : r.batch(() => {
38
- for (let o = l.len; o < e.length; o++)
39
- r.appendData(t, e[o], s);
40
- }), { len: e.length, firstTime: n, lastTime: i };
54
+ return a.setSeriesData(r, [], i), { len: 0, firstTime: null, lastTime: null, data: [] };
55
+ if (n.data === e && n.len === e.length) return n;
56
+ const l = s(e[0].time), f = s(e[e.length - 1].time), h = n.firstTime !== null && n.firstTime !== l, u = e.length - n.len, m = n.lastTime !== null && n.lastTime !== f;
57
+ return h && u === 0 && m ? a.batch(() => {
58
+ a.appendData(r, e[e.length - 1], i), a.keepLast(r, e.length, i);
59
+ }) : p(n.data, e, u) ? a.prependData(r, e.slice(0, u), i) : n.len === 0 || e.length < n.len || u > d || h ? a.setSeriesData(r, e, i) : e.length === n.len ? y(n.data, e) ? a.updateData(r, e[e.length - 1], i) : a.setSeriesData(r, e, i) : a.batch(() => {
60
+ for (let o = n.len; o < e.length; o++)
61
+ a.appendData(r, e[o], i);
62
+ }), { len: e.length, firstTime: l, lastTime: f, data: e };
41
63
  }
42
64
  export {
43
- y as EMPTY_SYNC_STATE,
44
- A as syncLayers,
45
- T as syncSeriesLayer,
46
- p as toLayers
65
+ g as EMPTY_SYNC_STATE,
66
+ D as syncLayers,
67
+ b as syncSeriesLayer,
68
+ A as toLayers
47
69
  };
@@ -0,0 +1,16 @@
1
+ import { formatCompact as a } from "./utils/format.js";
2
+ const s = 170, o = 78;
3
+ function i(e) {
4
+ return Number.isInteger(e) ? e.toString() : a(e);
5
+ }
6
+ function m(e, t) {
7
+ if (t !== void 0) return t;
8
+ const r = e.getSeriesIdsByType("heatmap", { visibleOnly: !0 });
9
+ return r.length > 0 ? r[0] : null;
10
+ }
11
+ export {
12
+ o as HEATMAP_TOOLTIP_HEIGHT,
13
+ s as HEATMAP_TOOLTIP_WIDTH,
14
+ i as defaultHeatmapFormat,
15
+ m as resolveHeatmapSeriesId
16
+ };
@@ -1,29 +1,30 @@
1
- import { EventEmitter as c } from "../events.js";
2
- import { clamp as u } from "../utils/math.js";
3
- import { PanHandler as l } from "./pan.js";
1
+ import { EventEmitter as a } from "../events.js";
2
+ import { clamp as l } from "../utils/math.js";
3
+ import { PanHandler as u } from "./pan.js";
4
4
  import { ZoomHandler as r } from "./zoom.js";
5
- function i(n, t) {
6
- const e = n.clientX - t.clientX, s = n.clientY - t.clientY;
7
- return Math.hypot(e, s);
5
+ function i(o, e) {
6
+ const t = o.clientX - e.clientX, s = o.clientY - e.clientY;
7
+ return Math.hypot(t, s);
8
8
  }
9
- class M extends c {
9
+ const d = 5, v = 500;
10
+ class M extends a {
10
11
  zoom;
11
12
  pan;
12
13
  canvas;
13
14
  timeScale;
14
15
  yScale;
15
16
  target;
16
- constructor(t, e, s, o) {
17
- super(), this.canvas = t, this.target = e, this.timeScale = s, this.yScale = o, this.zoom = new r(e, s), this.pan = new l(e, s, t), t.style.cursor = "crosshair", t.style.touchAction = "none", t.addEventListener("wheel", this.onWheel, { passive: !1 }), t.addEventListener("mousedown", this.onMouseDown), t.addEventListener("mousemove", this.onMouseMove), t.addEventListener("mouseup", this.onMouseUp), t.addEventListener("mouseleave", this.onMouseLeave), t.addEventListener("dblclick", this.onDblClick), t.addEventListener("touchstart", this.onTouchStart, { passive: !1 }), t.addEventListener("touchmove", this.onTouchMove, { passive: !1 }), t.addEventListener("touchend", this.onTouchEnd), t.addEventListener("touchcancel", this.onTouchCancel);
17
+ constructor(e, t, s, n) {
18
+ super(), this.canvas = e, this.target = t, this.timeScale = s, this.yScale = n, this.zoom = new r(t, s), this.pan = new u(t, s, e), e.style.cursor = "crosshair", e.style.touchAction = "none", e.addEventListener("wheel", this.onWheel, { passive: !1 }), e.addEventListener("mousedown", this.onMouseDown), e.addEventListener("mousemove", this.onMouseMove), e.addEventListener("mouseup", this.onMouseUp), e.addEventListener("mouseleave", this.onMouseLeave), e.addEventListener("click", this.onClick), e.addEventListener("dblclick", this.onDblClick), e.addEventListener("touchstart", this.onTouchStart, { passive: !1 }), e.addEventListener("touchmove", this.onTouchMove, { passive: !1 }), e.addEventListener("touchend", this.onTouchEnd), e.addEventListener("touchcancel", this.onTouchCancel);
18
19
  }
19
- onWheel = (t) => {
20
- this.zoom.handleWheel(t);
20
+ onWheel = (e) => {
21
+ this.zoom.handleWheel(e);
21
22
  };
22
- onMouseDown = (t) => {
23
- this.zoom.cancelPendingRebound(), this.pan.handleMouseDown(t);
23
+ onMouseDown = (e) => {
24
+ this.zoom.cancelPendingRebound(), this.pan.handleMouseDown(e), this.downClient = { x: e.clientX, y: e.clientY };
24
25
  };
25
- onMouseMove = (t) => {
26
- this.pan.isDragging() && this.pan.handleMouseMove(t), this.emitCrosshair(t.offsetX, t.offsetY);
26
+ onMouseMove = (e) => {
27
+ this.pan.isDragging() && this.pan.handleMouseMove(e), this.emitCrosshair(e.offsetX, e.offsetY);
27
28
  };
28
29
  onMouseUp = () => {
29
30
  this.pan.handleMouseUp();
@@ -31,52 +32,75 @@ class M extends c {
31
32
  onMouseLeave = () => {
32
33
  this.pan.handleMouseUp(), this.emit("crosshairMove", null);
33
34
  };
34
- onDblClick = () => {
35
+ // Pointer-down origin (mouse or the single touch that started a tap),
36
+ // used to tell a click/tap from a drag that happened to end over the canvas.
37
+ downClient = null;
38
+ onClick = (e) => {
39
+ this.movedBeyondClickTolerance(e.clientX, e.clientY) || e.detail > 1 || this.emit("click", this.posFromOffset(e.offsetX, e.offsetY));
35
40
  };
41
+ onDblClick = (e) => {
42
+ this.emit("dblclick", this.posFromOffset(e.offsetX, e.offsetY));
43
+ };
44
+ movedBeyondClickTolerance(e, t) {
45
+ return this.downClient ? Math.hypot(e - this.downClient.x, t - this.downClient.y) > d : !1;
46
+ }
36
47
  // Touch handling
37
48
  lastTouchDist = 0;
38
49
  touchCount = 0;
39
- onTouchStart = (t) => {
40
- t.preventDefault(), this.zoom.cancelPendingRebound(), this.touchCount = t.touches.length, t.touches.length === 1 ? this.pan.handleMouseDown({
50
+ touchDownTime = 0;
51
+ // True once a gesture has ever held two fingers (a pinch). A pinch is never a
52
+ // tap, even after it decays to one finger, so this gates tap synthesis in
53
+ // onTouchEnd — otherwise lifting both fingers in quick succession would
54
+ // synthesize a phantom 'click'.
55
+ gestureHadTwoFingers = !1;
56
+ onTouchStart = (e) => {
57
+ e.preventDefault(), this.zoom.cancelPendingRebound(), this.touchCount = e.touches.length, e.touches.length === 1 ? (this.pan.handleMouseDown({
41
58
  button: 0,
42
- clientX: t.touches[0].clientX
43
- }) : t.touches.length === 2 && (this.lastTouchDist = i(t.touches[0], t.touches[1]));
59
+ clientX: e.touches[0].clientX
60
+ }), this.downClient = { x: e.touches[0].clientX, y: e.touches[0].clientY }, this.touchDownTime = Date.now(), this.gestureHadTwoFingers = !1) : e.touches.length === 2 && (this.lastTouchDist = i(e.touches[0], e.touches[1]), this.gestureHadTwoFingers = !0);
44
61
  };
45
- onTouchMove = (t) => {
46
- if (t.preventDefault(), t.touches.length === 1 && this.touchCount === 1)
62
+ onTouchMove = (e) => {
63
+ if (e.preventDefault(), e.touches.length === 1 && this.touchCount === 1)
47
64
  this.pan.handleMouseMove({
48
- clientX: t.touches[0].clientX
65
+ clientX: e.touches[0].clientX
49
66
  });
50
- else if (t.touches.length === 2) {
51
- const e = i(t.touches[0], t.touches[1]), s = (t.touches[0].clientX + t.touches[1].clientX) / 2, o = this.canvas.getBoundingClientRect();
52
- if (e > 0 && this.lastTouchDist > 0) {
53
- const h = u(this.lastTouchDist / e, 0.1, 10), a = this.timeScale.xToTime(s - o.left);
54
- this.target.zoomAt(a, h, this.timeScale.getMediaWidth());
67
+ else if (e.touches.length === 2) {
68
+ const t = i(e.touches[0], e.touches[1]), s = (e.touches[0].clientX + e.touches[1].clientX) / 2, n = this.canvas.getBoundingClientRect();
69
+ if (t > 0 && this.lastTouchDist > 0) {
70
+ const h = l(this.lastTouchDist / t, 0.1, 10), c = this.timeScale.xToTime(s - n.left);
71
+ this.target.zoomAt(c, h, this.timeScale.getMediaWidth());
55
72
  }
56
- this.lastTouchDist = e;
73
+ this.lastTouchDist = t;
57
74
  }
58
75
  };
59
- onTouchEnd = (t) => {
60
- if (t.touches.length === 0) {
61
- this.pan.handleMouseUp(), this.touchCount = 0, this.lastTouchDist = 0, this.emit("crosshairMove", null);
76
+ onTouchEnd = (e) => {
77
+ if (e.touches.length === 0) {
78
+ const t = e.changedTouches[0];
79
+ if (this.touchCount === 1 && !this.gestureHadTwoFingers && t && !this.movedBeyondClickTolerance(t.clientX, t.clientY) && Date.now() - this.touchDownTime <= v) {
80
+ const s = this.canvas.getBoundingClientRect();
81
+ this.emit("click", this.posFromOffset(t.clientX - s.left, t.clientY - s.top));
82
+ }
83
+ this.pan.handleMouseUp(), this.touchCount = 0, this.lastTouchDist = 0, this.downClient = null, this.gestureHadTwoFingers = !1, this.emit("crosshairMove", null);
62
84
  return;
63
85
  }
64
- t.touches.length === 1 && (this.touchCount = 1, this.lastTouchDist = 0, this.pan.handleMouseDown({ button: 0, clientX: t.touches[0].clientX }));
86
+ e.touches.length === 1 && (this.touchCount = 1, this.lastTouchDist = 0, this.pan.handleMouseDown({ button: 0, clientX: e.touches[0].clientX }), this.downClient = { x: e.touches[0].clientX, y: e.touches[0].clientY }, this.touchDownTime = Date.now());
65
87
  };
66
88
  onTouchCancel = () => {
67
- this.pan.handleMouseUp(), this.touchCount = 0, this.lastTouchDist = 0, this.emit("crosshairMove", null);
89
+ this.pan.handleMouseUp(), this.touchCount = 0, this.lastTouchDist = 0, this.downClient = null, this.gestureHadTwoFingers = !1, this.emit("crosshairMove", null);
68
90
  };
69
- emitCrosshair(t, e) {
70
- const s = this.timeScale.xToTime(t), o = this.yScale.yToValue(e);
71
- this.emit("crosshairMove", {
72
- mediaX: t,
73
- mediaY: e,
74
- time: s,
75
- y: o
76
- });
91
+ posFromOffset(e, t) {
92
+ return {
93
+ mediaX: e,
94
+ mediaY: t,
95
+ time: this.timeScale.xToTime(e),
96
+ y: this.yScale.yToValue(t)
97
+ };
98
+ }
99
+ emitCrosshair(e, t) {
100
+ this.emit("crosshairMove", this.posFromOffset(e, t));
77
101
  }
78
102
  destroy() {
79
- this.zoom.cancelPendingRebound(), this.canvas.removeEventListener("wheel", this.onWheel), this.canvas.removeEventListener("mousedown", this.onMouseDown), this.canvas.removeEventListener("mousemove", this.onMouseMove), this.canvas.removeEventListener("mouseup", this.onMouseUp), this.canvas.removeEventListener("mouseleave", this.onMouseLeave), this.canvas.removeEventListener("dblclick", this.onDblClick), this.canvas.removeEventListener("touchstart", this.onTouchStart), this.canvas.removeEventListener("touchmove", this.onTouchMove), this.canvas.removeEventListener("touchend", this.onTouchEnd), this.canvas.removeEventListener("touchcancel", this.onTouchCancel), this.removeAllListeners();
103
+ this.zoom.cancelPendingRebound(), this.canvas.removeEventListener("wheel", this.onWheel), this.canvas.removeEventListener("mousedown", this.onMouseDown), this.canvas.removeEventListener("mousemove", this.onMouseMove), this.canvas.removeEventListener("mouseup", this.onMouseUp), this.canvas.removeEventListener("mouseleave", this.onMouseLeave), this.canvas.removeEventListener("click", this.onClick), this.canvas.removeEventListener("dblclick", this.onDblClick), this.canvas.removeEventListener("touchstart", this.onTouchStart), this.canvas.removeEventListener("touchmove", this.onTouchMove), this.canvas.removeEventListener("touchend", this.onTouchEnd), this.canvas.removeEventListener("touchcancel", this.onTouchCancel), this.removeAllListeners();
80
104
  }
81
105
  }
82
106
  export {
@@ -10,14 +10,14 @@ function N(i) {
10
10
  ownsMonitor: !e
11
11
  };
12
12
  }
13
- const P = 100, z = 600, f = 250, F = 1e3 / 60, H = 1e3 / 120, m = 60, b = 4, O = 34, S = 64, j = 24;
13
+ const P = 100, z = 600, f = 250, F = 1e3 / 60, H = 1e3 / 120, m = 60, b = 4, j = 34, S = 64, L = 24;
14
14
  function I(i) {
15
15
  let e = 0;
16
16
  for (const t of i)
17
17
  t > e && (e = t);
18
18
  return Math.max(10, Math.ceil(e / 10) * 10);
19
19
  }
20
- function L(i) {
20
+ function O(i) {
21
21
  return -I(i) / 12;
22
22
  }
23
23
  const B = "#9aa6b6", c = "#5d6877", C = "#e8eef7", x = "#cdd6e2", y = "#3fd68f", V = "#f5c451", $ = "#f4756a", Y = "#5d6877", X = {
@@ -233,7 +233,7 @@ class D {
233
233
  }), this.element.insertBefore(this.sparkHost, this.element.firstChild)) : (Object.assign(this.sparkHost.style, {
234
234
  position: "relative",
235
235
  inset: "auto",
236
- height: `${O}px`,
236
+ height: `${j}px`,
237
237
  marginTop: "7px",
238
238
  borderRadius: "4px",
239
239
  opacity: "1",
@@ -271,7 +271,7 @@ class D {
271
271
  const t = performance.now(), n = new R(e, {
272
272
  theme: T({ background: "#10141c" }).theme,
273
273
  axis: {
274
- y: { visible: !1, width: 0, min: L, max: I },
274
+ y: { visible: !1, width: 0, min: O, max: I },
275
275
  x: { visible: !1, height: 0 }
276
276
  },
277
277
  // Bottom padding keeps the zero baseline clearly on canvas — at 0 the
@@ -308,7 +308,7 @@ class D {
308
308
  }
309
309
  /** Keeps the plateau below the FPS number (mini) or the Y tick (strip). */
310
310
  sparkPaddingTop() {
311
- return this.mode === "mini" ? j : 12;
311
+ return this.mode === "mini" ? L : 12;
312
312
  }
313
313
  /** Append one FPS sample to the sparkline chart and roll its window. */
314
314
  appendSpark(e) {
@@ -1,13 +1,13 @@
1
- import { niceTimeIntervals as u } from "../utils/time.js";
2
- import { AxisTickTracker as m } from "./tick-tracker.js";
3
- const c = 50, f = 80;
1
+ import { formatTime as m, niceTimeIntervals as c } from "../utils/time.js";
2
+ import { AxisTickTracker as f } from "./tick-tracker.js";
3
+ const o = 50, v = 80;
4
4
  class p {
5
5
  /**
6
6
  * Shared fade state for time ticks. Grid lines (canvas) and DOM tick labels
7
7
  * read opacity from the same tracker, so a tick fading in/out looks
8
8
  * identical on both surfaces.
9
9
  */
10
- tickTracker = new m();
10
+ tickTracker = new f();
11
11
  from = 0;
12
12
  to = 0;
13
13
  width = 1;
@@ -16,6 +16,12 @@ class p {
16
16
  // Label density knobs — written by <TimeAxis labelCount=… minLabelSpacing=…>.
17
17
  labelCountHintValue = null;
18
18
  minSpacingValue = null;
19
+ /** Custom formatter (driven by `setFormat` / a future `<TimeAxis format=…>`). */
20
+ customFormat = null;
21
+ /** Custom tick generator — bypasses tier resolution in `niceTickValues` when set. */
22
+ customTickGenerator = null;
23
+ localeValue;
24
+ timeZoneValue;
19
25
  resolvedInterval = null;
20
26
  lastInterval = null;
21
27
  /**
@@ -31,20 +37,58 @@ class p {
31
37
  return this.labelCountHintValue;
32
38
  }
33
39
  get minLabelSpacing() {
34
- return this.minSpacingValue ?? f;
40
+ return this.minSpacingValue ?? v;
35
41
  }
36
- update(t, s, a, e) {
37
- if (this.from = t.from, this.to = t.to, this.width = s, this.pixelRatio = a, e !== void 0 && e > 0) {
38
- const n = h(e);
39
- this.lastBucketKey !== null && this.lastBucketKey !== n && this.resetHysteresis(), this.lastBucketKey = n, this.dataInterval = e;
42
+ update(t, e, n, r) {
43
+ if (this.from = t.from, this.to = t.to, this.width = e, this.pixelRatio = n, r !== void 0 && r > 0) {
44
+ const a = h(r);
45
+ this.lastBucketKey !== null && this.lastBucketKey !== a && this.resetHysteresis(), this.lastBucketKey = a, this.dataInterval = r;
40
46
  }
41
47
  this.resolveInterval();
42
48
  }
43
49
  setLabelCount(t) {
44
- this.labelCountHintValue = v(t), this.resetHysteresis(), this.resolveInterval();
50
+ this.labelCountHintValue = d(t), this.resetHysteresis(), this.resolveInterval();
45
51
  }
46
52
  setMinSpacing(t) {
47
- this.minSpacingValue = d(t), this.resetHysteresis(), this.resolveInterval();
53
+ this.minSpacingValue = I(t), this.resetHysteresis(), this.resolveInterval();
54
+ }
55
+ /** Install (or clear) a custom tick-label formatter. */
56
+ setFormat(t) {
57
+ this.customFormat = t;
58
+ }
59
+ /** Read back the currently installed formatter — null when the built-in is active. */
60
+ getFormat() {
61
+ return this.customFormat;
62
+ }
63
+ /**
64
+ * Install (or clear) a custom tick-value generator. When set, it replaces
65
+ * `niceTimeIntervals` tier resolution entirely — `niceTickValues` calls it
66
+ * directly with the current range and reports its own tick spacing.
67
+ */
68
+ setTickGenerator(t) {
69
+ this.customTickGenerator = t, this.resetHysteresis();
70
+ }
71
+ /** BCP 47 locale applied to the built-in formatter. Ignored while a custom `setFormat` is installed. */
72
+ setLocale(t) {
73
+ this.localeValue = t;
74
+ }
75
+ getLocale() {
76
+ return this.localeValue;
77
+ }
78
+ /** IANA timezone applied to the built-in formatter. Ignored while a custom `setFormat` is installed. */
79
+ setTimeZone(t) {
80
+ this.timeZoneValue = t;
81
+ }
82
+ getTimeZone() {
83
+ return this.timeZoneValue;
84
+ }
85
+ /**
86
+ * Format a tick timestamp for display. A custom formatter (via
87
+ * {@link setFormat}) wins; otherwise falls back to the built-in
88
+ * `formatTime`, applying the installed locale/timezone.
89
+ */
90
+ formatX(t, e) {
91
+ return this.customFormat ? this.customFormat(t, e) : m(t, e, { locale: this.localeValue, timeZone: this.timeZoneValue });
48
92
  }
49
93
  resetHysteresis() {
50
94
  this.lastInterval = null, this.lastWant = null;
@@ -74,14 +118,18 @@ class p {
74
118
  */
75
119
  niceTickValues(t) {
76
120
  if (this.to <= this.from) return { ticks: [], tickInterval: 0 };
121
+ if (this.customTickGenerator) {
122
+ const i = this.customTickGenerator({ from: this.from, to: this.to }, t).slice(0, o), l = i.length >= 2 ? i[1] - i[0] : t;
123
+ return { ticks: i, tickInterval: l };
124
+ }
77
125
  if (this.dataInterval !== t) {
78
- const r = h(t);
79
- this.lastBucketKey !== null && this.lastBucketKey !== r && this.resetHysteresis(), this.lastBucketKey = r, this.dataInterval = t, this.resolveInterval();
126
+ const i = h(t);
127
+ this.lastBucketKey !== null && this.lastBucketKey !== i && this.resetHysteresis(), this.lastBucketKey = i, this.dataInterval = t, this.resolveInterval();
80
128
  }
81
129
  if (this.resolvedInterval == null) return { ticks: [], tickInterval: 0 };
82
- const s = this.resolvedInterval, a = Math.ceil(this.from / s) * s, e = Math.max(0, Math.min(c, Math.floor((this.to - a) / s) + 1)), n = [];
83
- for (let r = 0; r < e; r++) n.push(a + r * s);
84
- return { ticks: n, tickInterval: s };
130
+ const e = this.resolvedInterval, n = Math.ceil(this.from / e) * e, r = Math.max(0, Math.min(o, Math.floor((this.to - n) / e) + 1)), a = [];
131
+ for (let i = 0; i < r; i++) a.push(n + i * e);
132
+ return { ticks: a, tickInterval: e };
85
133
  }
86
134
  getRange() {
87
135
  return { from: this.from, to: this.to };
@@ -99,46 +147,46 @@ class p {
99
147
  this.resolvedInterval = null;
100
148
  return;
101
149
  }
102
- const t = u(this.dataInterval), s = this.to - this.from, a = s / this.width * this.minLabelSpacing;
103
- let e;
150
+ const t = c(this.dataInterval), e = this.to - this.from, n = e / this.width * this.minLabelSpacing;
151
+ let r;
104
152
  if (this.labelCountHint != null) {
105
153
  const l = Math.max(1, this.labelCountHint);
106
- e = Math.max(s / l, a);
154
+ r = Math.max(e / l, n);
107
155
  } else
108
- e = a;
156
+ r = n;
109
157
  if (this.lastInterval != null && this.lastWant != null && t.includes(this.lastInterval)) {
110
- const l = e >= this.lastWant * 0.8 && e <= this.lastWant * 1.25, o = this.lastInterval >= a;
111
- if (l && o) {
158
+ const l = r >= this.lastWant * 0.8 && r <= this.lastWant * 1.25, u = this.lastInterval >= n;
159
+ if (l && u) {
112
160
  this.resolvedInterval = this.lastInterval;
113
161
  return;
114
162
  }
115
163
  }
116
- let n = t[t.length - 1], r = t.length - 1;
164
+ let a = t[t.length - 1], i = t.length - 1;
117
165
  for (let l = 0; l < t.length; l++)
118
- if (t[l] >= e) {
119
- n = t[l], r = l;
166
+ if (t[l] >= r) {
167
+ a = t[l], i = l;
120
168
  break;
121
169
  }
122
- if (this.labelCountHint != null && r > 0 && this.countTicks(n) < this.labelCountHint) {
123
- const l = t[r - 1];
124
- l >= a && (n = l);
170
+ if (this.labelCountHint != null && i > 0 && this.countTicks(a) < this.labelCountHint) {
171
+ const l = t[i - 1];
172
+ l >= n && (a = l);
125
173
  }
126
- this.resolvedInterval = n, this.lastInterval = n, this.lastWant = e;
174
+ this.resolvedInterval = a, this.lastInterval = a, this.lastWant = r;
127
175
  }
128
176
  countTicks(t) {
129
177
  if (!(t > 0)) return 0;
130
- const s = Math.ceil(this.from / t) * t;
131
- return Math.max(0, Math.floor((this.to - s) / t) + 1);
178
+ const e = Math.ceil(this.from / t) * t;
179
+ return Math.max(0, Math.floor((this.to - e) / t) + 1);
132
180
  }
133
181
  }
134
- function h(i) {
135
- return i < 6e4 ? 0 : i < 36e5 ? 1 : i < 864e5 ? 2 : 3;
182
+ function h(s) {
183
+ return s < 6e4 ? 0 : s < 36e5 ? 1 : s < 864e5 ? 2 : 3;
136
184
  }
137
- function v(i) {
138
- return typeof i == "number" && Number.isFinite(i) && i >= 2 ? Math.floor(i) : null;
185
+ function d(s) {
186
+ return typeof s == "number" && Number.isFinite(s) && s >= 2 ? Math.floor(s) : null;
139
187
  }
140
- function d(i) {
141
- return typeof i == "number" && Number.isFinite(i) && i > 0 ? i : null;
188
+ function I(s) {
189
+ return typeof s == "number" && Number.isFinite(s) && s > 0 ? s : null;
142
190
  }
143
191
  export {
144
192
  p as XScale
@@ -1,19 +1,19 @@
1
- import { formatCompact as h } from "../utils/format.js";
2
- import { AxisTickTracker as o } from "./tick-tracker.js";
3
- function u(n) {
4
- if (!Number.isFinite(n) || n <= 0) return 1;
5
- const t = 10 ** Math.floor(Math.log10(n)), i = n / t;
1
+ import { formatCompact as o } from "../utils/format.js";
2
+ import { AxisTickTracker as u } from "./tick-tracker.js";
3
+ function m(e) {
4
+ if (!Number.isFinite(e) || e <= 0) return 1;
5
+ const t = 10 ** Math.floor(Math.log10(e)), i = e / t;
6
6
  return (i <= 1 ? 1 : i <= 2 ? 2 : i <= 5 ? 5 : 10) * t;
7
7
  }
8
- function m(n) {
9
- if (!Number.isFinite(n) || n <= 0) return 1;
10
- const t = 10 ** Math.floor(Math.log10(n)), i = n / t;
8
+ function c(e) {
9
+ if (!Number.isFinite(e) || e <= 0) return 1;
10
+ const t = 10 ** Math.floor(Math.log10(e)), i = e / t;
11
11
  return (i >= 5 ? 5 : i >= 2 ? 2 : 1) * t;
12
12
  }
13
- const c = 50, v = 50;
13
+ const r = 50, v = 50;
14
14
  class M {
15
15
  /** Shared fade state for Y ticks; see {@link TimeScale.tickTracker}. */
16
- tickTracker = new o();
16
+ tickTracker = new u();
17
17
  min = 0;
18
18
  max = 0;
19
19
  height = 1;
@@ -35,6 +35,8 @@ class M {
35
35
  lastRawInterval = null;
36
36
  /** Custom formatter (driven by `<YAxis format=…>`). */
37
37
  customFormat = null;
38
+ /** Custom tick generator — bypasses {1,2,5}×10^k resolution in `niceTickValues` when set. */
39
+ customTickGenerator = null;
38
40
  get labelCountHint() {
39
41
  return this.labelCountHintValue;
40
42
  }
@@ -64,6 +66,14 @@ class M {
64
66
  getFormat() {
65
67
  return this.customFormat;
66
68
  }
69
+ /**
70
+ * Install (or clear) a custom tick-value generator. When set, it replaces
71
+ * {1,2,5}×10^k resolution entirely — `niceTickValues` calls it directly
72
+ * with the current range.
73
+ */
74
+ setTickGenerator(t) {
75
+ this.customTickGenerator = t, this.resetHysteresis();
76
+ }
67
77
  /** Convert a value to a Y position in CSS (media) pixels. */
68
78
  valueToY(t) {
69
79
  const i = this.max - this.min;
@@ -83,10 +93,11 @@ class M {
83
93
  * chart height. Pure read — resolution happens in `update()`.
84
94
  */
85
95
  niceTickValues() {
96
+ if (this.customTickGenerator) return this.customTickGenerator({ min: this.min, max: this.max }).slice(0, r);
86
97
  if (this.resolvedInterval == null) return [];
87
- const t = this.resolvedInterval, i = Math.ceil(this.min / t) * t, a = Math.max(0, Math.min(c, Math.floor((this.max - i) / t) + 1)), e = [];
88
- for (let s = 0; s < a; s++) e.push(i + s * t);
89
- return e;
98
+ const t = this.resolvedInterval, i = Math.ceil(this.min / t) * t, a = Math.max(0, Math.min(r, Math.floor((this.max - i) / t) + 1)), s = [];
99
+ for (let n = 0; n < a; n++) s.push(i + n * t);
100
+ return s;
90
101
  }
91
102
  getRange() {
92
103
  return { min: this.min, max: this.max };
@@ -103,7 +114,7 @@ class M {
103
114
  */
104
115
  formatY(t) {
105
116
  if (this.customFormat) return this.customFormat(t);
106
- if (this.max - this.min >= 1e6) return h(t);
117
+ if (this.max - this.min >= 1e6) return o(t);
107
118
  const i = this.resolvedInterval, a = i != null && i > 0 ? Math.max(0, -Math.floor(Math.log10(i))) : 1;
108
119
  return t.toFixed(a);
109
120
  }
@@ -124,20 +135,20 @@ class M {
124
135
  return;
125
136
  }
126
137
  const t = this.max - this.min, i = t * this.minLabelSpacing / this.height, a = this.labelCountHint != null ? Math.max(1, this.labelCountHint) : Math.max(1, Math.floor(this.height / this.minLabelSpacing));
127
- let e = t / a;
128
- if (e < i && (e = i), this.lastInterval != null && this.lastRawInterval != null) {
129
- const l = e >= this.lastRawInterval * 0.8 && e <= this.lastRawInterval * 1.25, r = this.lastInterval >= i;
130
- if (l && r) {
138
+ let s = t / a;
139
+ if (s < i && (s = i), this.lastInterval != null && this.lastRawInterval != null) {
140
+ const l = s >= this.lastRawInterval * 0.8 && s <= this.lastRawInterval * 1.25, h = this.lastInterval >= i;
141
+ if (l && h) {
131
142
  this.resolvedInterval = this.lastInterval;
132
143
  return;
133
144
  }
134
145
  }
135
- let s = u(e);
136
- if (this.labelCountHint != null && this.countTicks(s) < this.labelCountHint) {
137
- const l = m(e);
138
- l >= i && l > 0 && (s = l);
146
+ let n = m(s);
147
+ if (this.labelCountHint != null && this.countTicks(n) < this.labelCountHint) {
148
+ const l = c(s);
149
+ l >= i && l > 0 && (n = l);
139
150
  }
140
- this.resolvedInterval = s, this.lastInterval = s, this.lastRawInterval = e;
151
+ this.resolvedInterval = n, this.lastInterval = n, this.lastRawInterval = s;
141
152
  }
142
153
  countTicks(t) {
143
154
  if (!(t > 0)) return 0;
@@ -145,11 +156,11 @@ class M {
145
156
  return Math.max(0, Math.floor((this.max - i) / t) + 1);
146
157
  }
147
158
  }
148
- function f(n) {
149
- return typeof n == "number" && Number.isFinite(n) && n >= 2 ? Math.floor(n) : null;
159
+ function f(e) {
160
+ return typeof e == "number" && Number.isFinite(e) && e >= 2 ? Math.floor(e) : null;
150
161
  }
151
- function g(n) {
152
- return typeof n == "number" && Number.isFinite(n) && n > 0 ? n : null;
162
+ function g(e) {
163
+ return typeof e == "number" && Number.isFinite(e) && e > 0 ? e : null;
153
164
  }
154
165
  export {
155
166
  M as YScale