@wick-charts/vue 0.5.1 → 0.6.1

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 (82) hide show
  1. package/README.md +45 -6
  2. package/dist/BarSeries.vue.js +26 -20
  3. package/dist/CandlestickSeries.vue.js +30 -24
  4. package/dist/ChartContainer.vue.js +125 -72
  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/options.js +27 -16
  18. package/dist/core/dist/chart/y-target.js +13 -12
  19. package/dist/core/dist/chart.js +868 -441
  20. package/dist/core/dist/components/annotation-chip.js +28 -0
  21. package/dist/core/dist/components/crosshair.js +8 -6
  22. package/dist/core/dist/components/edge-indicator.js +58 -26
  23. package/dist/core/dist/components/loading-indicator.js +46 -0
  24. package/dist/core/dist/components/marker.js +49 -31
  25. package/dist/core/dist/components/reference-line.js +27 -36
  26. package/dist/core/dist/components/time-region.js +30 -28
  27. package/dist/core/dist/data/store.js +38 -19
  28. package/dist/core/dist/data/sync.js +60 -38
  29. package/dist/core/dist/heatmap-tooltip.js +16 -0
  30. package/dist/core/dist/interactions/handler.js +86 -46
  31. package/dist/core/dist/perf/perf-hud.js +5 -5
  32. package/dist/core/dist/scales/x-scale.js +85 -37
  33. package/dist/core/dist/scales/y-scale.js +38 -27
  34. package/dist/core/dist/series/bar-intro.js +18 -0
  35. package/dist/core/dist/series/bar.js +337 -205
  36. package/dist/core/dist/series/base-multi-layer.js +224 -96
  37. package/dist/core/dist/series/candlestick-intro.js +57 -0
  38. package/dist/core/dist/series/candlestick.js +454 -276
  39. package/dist/core/dist/series/heatmap.js +387 -0
  40. package/dist/core/dist/series/line-intro.js +85 -0
  41. package/dist/core/dist/series/line.js +590 -282
  42. package/dist/core/dist/series/painters/builtins.js +2 -2
  43. package/dist/core/dist/series/pie.js +396 -259
  44. package/dist/core/dist/series/register-builtins.js +8 -7
  45. package/dist/core/dist/series/types.js +6 -0
  46. package/dist/core/dist/series/wave-intro.js +26 -0
  47. package/dist/core/dist/theme/create.js +69 -63
  48. package/dist/core/dist/theme/themes/mint-breeze.js +2 -2
  49. package/dist/core/dist/theme/themes/sand-dune.js +2 -2
  50. package/dist/core/dist/utils/color.js +109 -34
  51. package/dist/core/dist/utils/deep-equal.js +18 -0
  52. package/dist/core/dist/utils/time.js +25 -19
  53. package/dist/index.d.ts +1494 -122
  54. package/dist/index.js +177 -130
  55. package/dist/ui/Crosshair.vue.js +20 -21
  56. package/dist/ui/HeatmapTooltip.vue.js +96 -0
  57. package/dist/ui/HeatmapTooltip.vue2.js +4 -0
  58. package/dist/ui/InfoBar.vue.js +46 -47
  59. package/dist/ui/Legend.vue.js +80 -79
  60. package/dist/ui/PieLegend.vue.js +5 -5
  61. package/dist/ui/PieTooltip.vue.js +77 -67
  62. package/dist/ui/Tooltip.vue.js +60 -60
  63. package/dist/ui/YAxis.vue.js +2 -2
  64. package/dist/ui/YLabel.vue.js +94 -61
  65. package/package.json +2 -2
  66. package/src/BarSeries.vue +23 -7
  67. package/src/CandlestickSeries.vue +23 -7
  68. package/src/ChartContainer.vue +250 -13
  69. package/src/HeatmapSeries.vue +69 -0
  70. package/src/LineSeries.vue +23 -7
  71. package/src/PieSeries.vue +22 -6
  72. package/src/index.ts +84 -3
  73. package/src/ui/Crosshair.vue +1 -2
  74. package/src/ui/HeatmapTooltip.vue +144 -0
  75. package/src/ui/InfoBar.vue +1 -2
  76. package/src/ui/Legend.vue +13 -0
  77. package/src/ui/Marker.vue +1 -1
  78. package/src/ui/PieTooltip.vue +10 -0
  79. package/src/ui/ReferenceLine.vue +2 -2
  80. package/src/ui/TimeRegion.vue +2 -2
  81. package/src/ui/Tooltip.vue +2 -2
  82. 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,44 @@
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, m = 500;
10
+ class T 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", this.syncPanZoomMode(), 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
+ /** Live read of the target's capability — series can be added or removed at any time. */
21
+ panZoomEnabled() {
22
+ return this.target.canPanZoom?.() ?? !0;
23
+ }
24
+ /**
25
+ * Reflect the target's pan/zoom capability into the canvas CSS. While
26
+ * pan/zoomable, `touch-action: none` hands every touch gesture to the
27
+ * chart; on a spatial-only target native scrolling stays enabled so the
28
+ * chart doesn't trap the page. The host re-invokes this whenever the
29
+ * series mix changes.
30
+ */
31
+ syncPanZoomMode() {
32
+ this.canvas.style.touchAction = this.panZoomEnabled() ? "none" : "auto";
33
+ }
34
+ onWheel = (e) => {
35
+ this.panZoomEnabled() && this.zoom.handleWheel(e);
21
36
  };
22
- onMouseDown = (t) => {
23
- this.zoom.cancelPendingRebound(), this.pan.handleMouseDown(t);
37
+ onMouseDown = (e) => {
38
+ this.zoom.cancelPendingRebound(), this.panZoomEnabled() && this.pan.handleMouseDown(e), this.downClient = { x: e.clientX, y: e.clientY };
24
39
  };
25
- onMouseMove = (t) => {
26
- this.pan.isDragging() && this.pan.handleMouseMove(t), this.emitCrosshair(t.offsetX, t.offsetY);
40
+ onMouseMove = (e) => {
41
+ this.pan.isDragging() && this.pan.handleMouseMove(e), this.emitCrosshair(e.offsetX, e.offsetY);
27
42
  };
28
43
  onMouseUp = () => {
29
44
  this.pan.handleMouseUp();
@@ -31,54 +46,79 @@ class M extends c {
31
46
  onMouseLeave = () => {
32
47
  this.pan.handleMouseUp(), this.emit("crosshairMove", null);
33
48
  };
34
- onDblClick = () => {
49
+ // Pointer-down origin (mouse or the single touch that started a tap),
50
+ // used to tell a click/tap from a drag that happened to end over the canvas.
51
+ downClient = null;
52
+ onClick = (e) => {
53
+ this.movedBeyondClickTolerance(e.clientX, e.clientY) || e.detail > 1 || this.emit("click", this.posFromOffset(e.offsetX, e.offsetY));
54
+ };
55
+ onDblClick = (e) => {
56
+ this.emit("dblclick", this.posFromOffset(e.offsetX, e.offsetY));
35
57
  };
58
+ movedBeyondClickTolerance(e, t) {
59
+ return this.downClient ? Math.hypot(e - this.downClient.x, t - this.downClient.y) > d : !1;
60
+ }
36
61
  // Touch handling
37
62
  lastTouchDist = 0;
38
63
  touchCount = 0;
39
- onTouchStart = (t) => {
40
- t.preventDefault(), this.zoom.cancelPendingRebound(), this.touchCount = t.touches.length, t.touches.length === 1 ? this.pan.handleMouseDown({
64
+ touchDownTime = 0;
65
+ // True once a gesture has ever held two fingers (a pinch). A pinch is never a
66
+ // tap, even after it decays to one finger, so this gates tap synthesis in
67
+ // onTouchEnd — otherwise lifting both fingers in quick succession would
68
+ // synthesize a phantom 'click'.
69
+ gestureHadTwoFingers = !1;
70
+ onTouchStart = (e) => {
71
+ this.panZoomEnabled() && (e.preventDefault(), this.zoom.cancelPendingRebound(), this.touchCount = e.touches.length, e.touches.length === 1 ? (this.pan.handleMouseDown({
41
72
  button: 0,
42
- clientX: t.touches[0].clientX
43
- }) : t.touches.length === 2 && (this.lastTouchDist = i(t.touches[0], t.touches[1]));
73
+ clientX: e.touches[0].clientX
74
+ }), 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
75
  };
45
- onTouchMove = (t) => {
46
- if (t.preventDefault(), t.touches.length === 1 && this.touchCount === 1)
47
- this.pan.handleMouseMove({
48
- clientX: t.touches[0].clientX
49
- });
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());
76
+ onTouchMove = (e) => {
77
+ if (this.panZoomEnabled()) {
78
+ if (e.preventDefault(), e.touches.length === 1 && this.touchCount === 1)
79
+ this.pan.handleMouseMove({
80
+ clientX: e.touches[0].clientX
81
+ });
82
+ else if (e.touches.length === 2) {
83
+ const t = i(e.touches[0], e.touches[1]), s = (e.touches[0].clientX + e.touches[1].clientX) / 2, n = this.canvas.getBoundingClientRect();
84
+ if (t > 0 && this.lastTouchDist > 0) {
85
+ const h = l(this.lastTouchDist / t, 0.1, 10), c = this.timeScale.xToTime(s - n.left);
86
+ this.target.zoomAt(c, h, this.timeScale.getMediaWidth());
87
+ }
88
+ this.lastTouchDist = t;
55
89
  }
56
- this.lastTouchDist = e;
57
90
  }
58
91
  };
59
- onTouchEnd = (t) => {
60
- if (t.touches.length === 0) {
61
- this.pan.handleMouseUp(), this.touchCount = 0, this.lastTouchDist = 0, this.emit("crosshairMove", null);
92
+ onTouchEnd = (e) => {
93
+ if (e.touches.length === 0) {
94
+ const t = e.changedTouches[0];
95
+ if (this.touchCount === 1 && !this.gestureHadTwoFingers && t && !this.movedBeyondClickTolerance(t.clientX, t.clientY) && Date.now() - this.touchDownTime <= m) {
96
+ const s = this.canvas.getBoundingClientRect();
97
+ this.emit("click", this.posFromOffset(t.clientX - s.left, t.clientY - s.top));
98
+ }
99
+ this.pan.handleMouseUp(), this.touchCount = 0, this.lastTouchDist = 0, this.downClient = null, this.gestureHadTwoFingers = !1, this.emit("crosshairMove", null);
62
100
  return;
63
101
  }
64
- t.touches.length === 1 && (this.touchCount = 1, this.lastTouchDist = 0, this.pan.handleMouseDown({ button: 0, clientX: t.touches[0].clientX }));
102
+ e.touches.length === 1 && this.touchCount === 2 && (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
103
  };
66
104
  onTouchCancel = () => {
67
- this.pan.handleMouseUp(), this.touchCount = 0, this.lastTouchDist = 0, this.emit("crosshairMove", null);
105
+ this.pan.handleMouseUp(), this.touchCount = 0, this.lastTouchDist = 0, this.downClient = null, this.gestureHadTwoFingers = !1, this.emit("crosshairMove", null);
68
106
  };
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
- });
107
+ posFromOffset(e, t) {
108
+ return {
109
+ mediaX: e,
110
+ mediaY: t,
111
+ time: this.timeScale.xToTime(e),
112
+ y: this.yScale.yToValue(t)
113
+ };
114
+ }
115
+ emitCrosshair(e, t) {
116
+ this.emit("crosshairMove", this.posFromOffset(e, t));
77
117
  }
78
118
  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();
119
+ 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
120
  }
81
121
  }
82
122
  export {
83
- M as InteractionHandler
123
+ T as InteractionHandler
84
124
  };
@@ -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