@wick-charts/vue 0.4.2 → 0.5.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 (144) hide show
  1. package/README.md +5 -3
  2. package/dist/BarSeries.vue.js +44 -0
  3. package/dist/BarSeries.vue2.js +4 -0
  4. package/dist/CandlestickSeries.vue.js +45 -0
  5. package/dist/CandlestickSeries.vue2.js +4 -0
  6. package/dist/ChartContainer.vue.js +168 -0
  7. package/dist/ChartContainer.vue2.js +4 -0
  8. package/dist/LineSeries.vue.js +44 -0
  9. package/dist/LineSeries.vue2.js +4 -0
  10. package/dist/PieSeries.vue.js +34 -0
  11. package/dist/PieSeries.vue2.js +4 -0
  12. package/dist/composables.js +44 -0
  13. package/dist/context.js +25 -0
  14. package/dist/core/dist/animation/animator.js +72 -0
  15. package/dist/core/dist/animation/config.js +145 -0
  16. package/dist/core/dist/animation/easing.js +4 -0
  17. package/dist/core/dist/animation/scalar-spring.js +57 -0
  18. package/dist/core/dist/animation/spring.js +11 -0
  19. package/dist/core/dist/animation/time.js +19 -0
  20. package/dist/core/dist/animation/viewport-engine.js +174 -0
  21. package/dist/core/dist/animation/visible-range-spring.js +64 -0
  22. package/dist/core/dist/animation/y-range-hermite.js +72 -0
  23. package/dist/core/dist/animation/y-range-snap.js +31 -0
  24. package/dist/core/dist/animation/y-range-spring.js +59 -0
  25. package/dist/core/dist/axis/dom-labels.js +60 -0
  26. package/dist/core/dist/canvas-manager.js +109 -0
  27. package/dist/core/dist/chart/edge-indicators.js +26 -0
  28. package/dist/core/dist/chart/fit-to-data.js +9 -0
  29. package/dist/core/dist/chart/last-value.js +31 -0
  30. package/dist/core/dist/chart/options.js +35 -0
  31. package/dist/core/dist/chart/pan-zoom-math.js +66 -0
  32. package/dist/core/dist/chart/streaming-cadence.js +70 -0
  33. package/dist/core/dist/chart/streaming-target.js +22 -0
  34. package/dist/core/dist/chart/viewport-padding.js +6 -0
  35. package/dist/core/dist/chart/y-target.js +24 -0
  36. package/dist/core/dist/chart.js +1495 -0
  37. package/dist/core/dist/components/crosshair.js +9 -0
  38. package/dist/core/dist/components/edge-indicator.js +36 -0
  39. package/dist/core/dist/components/grid.js +22 -0
  40. package/dist/core/dist/components/marker.js +41 -0
  41. package/dist/core/dist/components/reference-line.js +48 -0
  42. package/dist/core/dist/components/time-region.js +35 -0
  43. package/dist/core/dist/data/decimation.js +54 -0
  44. package/dist/core/dist/data/store.js +76 -0
  45. package/dist/core/dist/data/sync.js +47 -0
  46. package/dist/core/dist/events.js +19 -0
  47. package/dist/core/dist/interactions/handler.js +84 -0
  48. package/dist/core/dist/interactions/pan.js +26 -0
  49. package/dist/core/dist/interactions/zoom.js +21 -0
  50. package/dist/core/dist/navigator/controller.js +374 -0
  51. package/dist/core/dist/navigator/decimate.js +44 -0
  52. package/dist/core/dist/navigator/interactions.js +28 -0
  53. package/dist/core/dist/navigator/render.js +68 -0
  54. package/dist/core/dist/perf/counting-context.js +19 -0
  55. package/dist/core/dist/perf/perf-hud.js +453 -0
  56. package/dist/core/dist/perf/perf-monitor.js +132 -0
  57. package/dist/core/dist/render-scheduler.js +31 -0
  58. package/dist/core/dist/scales/tick-tracker.js +110 -0
  59. package/dist/core/dist/scales/x-scale.js +145 -0
  60. package/dist/core/dist/scales/y-scale.js +156 -0
  61. package/dist/core/dist/series/bar.js +341 -0
  62. package/dist/core/dist/series/base-multi-layer.js +438 -0
  63. package/dist/core/dist/series/candlestick.js +481 -0
  64. package/dist/core/dist/series/definition.js +11 -0
  65. package/dist/core/dist/series/line.js +446 -0
  66. package/dist/core/dist/series/painters/builtins.js +36 -0
  67. package/dist/core/dist/series/painters/canvas-path.js +79 -0
  68. package/dist/core/dist/series/painters/resolve.js +32 -0
  69. package/dist/core/dist/series/pie.js +545 -0
  70. package/dist/core/dist/series/register-builtins.js +11 -0
  71. package/dist/core/dist/series/stack-math.js +17 -0
  72. package/dist/core/dist/snapshots.js +107 -0
  73. package/dist/core/dist/theme/create.js +176 -0
  74. package/dist/core/dist/theme/resolve.js +14 -0
  75. package/dist/core/dist/theme/themes/andromeda.js +33 -0
  76. package/dist/core/dist/theme/themes/ayu-mirage.js +33 -0
  77. package/dist/core/dist/theme/themes/catppuccin.js +33 -0
  78. package/dist/core/dist/theme/themes/dracula.js +35 -0
  79. package/dist/core/dist/theme/themes/github-light.js +34 -0
  80. package/dist/core/dist/theme/themes/gruvbox.js +33 -0
  81. package/dist/core/dist/theme/themes/handwritten.js +101 -0
  82. package/dist/core/dist/theme/themes/high-contrast.js +33 -0
  83. package/dist/core/dist/theme/themes/lavender-mist.js +33 -0
  84. package/dist/core/dist/theme/themes/light-pink.js +34 -0
  85. package/dist/core/dist/theme/themes/material-palenight.js +33 -0
  86. package/dist/core/dist/theme/themes/minimal-light.js +34 -0
  87. package/dist/core/dist/theme/themes/mint-breeze.js +33 -0
  88. package/dist/core/dist/theme/themes/monokai-pro.js +33 -0
  89. package/dist/core/dist/theme/themes/night-owl.js +35 -0
  90. package/dist/core/dist/theme/themes/one-dark-pro.js +33 -0
  91. package/dist/core/dist/theme/themes/panda.js +33 -0
  92. package/dist/core/dist/theme/themes/peach-cream.js +33 -0
  93. package/dist/core/dist/theme/themes/quiet-light.js +37 -0
  94. package/dist/core/dist/theme/themes/rose-pine-dawn.js +33 -0
  95. package/dist/core/dist/theme/themes/sand-dune.js +33 -0
  96. package/dist/core/dist/theme/themes/solarized-light.js +34 -0
  97. package/dist/core/dist/tooltip-position.js +10 -0
  98. package/dist/core/dist/utils/color.js +37 -0
  99. package/dist/core/dist/utils/format.js +29 -0
  100. package/dist/core/dist/utils/math.js +26 -0
  101. package/dist/core/dist/utils/poisoned-data-reporter.js +15 -0
  102. package/dist/core/dist/utils/time.js +41 -0
  103. package/dist/index.d.ts +827 -63
  104. package/dist/index.js +142 -8025
  105. package/dist/ui/Crosshair.vue.js +53 -0
  106. package/dist/ui/Crosshair.vue2.js +4 -0
  107. package/dist/ui/InfoBar.vue.js +143 -0
  108. package/dist/ui/InfoBar.vue2.js +4 -0
  109. package/dist/ui/Legend.vue.js +212 -0
  110. package/dist/ui/Legend.vue2.js +4 -0
  111. package/dist/ui/Marker.vue.js +47 -0
  112. package/dist/ui/Marker.vue2.js +4 -0
  113. package/dist/ui/Navigator.vue.js +58 -0
  114. package/dist/ui/Navigator.vue2.js +4 -0
  115. package/dist/ui/NumberFlow.vue.js +69 -0
  116. package/dist/ui/NumberFlow.vue2.js +4 -0
  117. package/dist/ui/PieLegend.vue.js +132 -0
  118. package/dist/ui/PieLegend.vue2.js +4 -0
  119. package/dist/ui/PieTooltip.vue.js +135 -0
  120. package/dist/ui/PieTooltip.vue2.js +4 -0
  121. package/dist/ui/ReferenceLine.vue.js +44 -0
  122. package/dist/ui/ReferenceLine.vue2.js +4 -0
  123. package/dist/ui/TimeAxis.vue.js +43 -0
  124. package/dist/ui/TimeAxis.vue2.js +4 -0
  125. package/dist/ui/TimeRegion.vue.js +41 -0
  126. package/dist/ui/TimeRegion.vue2.js +4 -0
  127. package/dist/ui/Title.vue.js +54 -0
  128. package/dist/ui/Title.vue2.js +4 -0
  129. package/dist/ui/Tooltip.vue.js +195 -0
  130. package/dist/ui/Tooltip.vue2.js +4 -0
  131. package/dist/ui/YAxis.vue.js +42 -0
  132. package/dist/ui/YAxis.vue2.js +4 -0
  133. package/dist/ui/YLabel.vue.js +105 -0
  134. package/dist/ui/YLabel.vue2.js +4 -0
  135. package/package.json +2 -2
  136. package/src/BarSeries.vue +12 -13
  137. package/src/CandlestickSeries.vue +17 -6
  138. package/src/LineSeries.vue +12 -13
  139. package/src/PieSeries.vue +2 -1
  140. package/src/index.ts +23 -0
  141. package/src/ui/Legend.vue +1 -2
  142. package/src/ui/Marker.vue +62 -0
  143. package/src/ui/ReferenceLine.vue +58 -0
  144. package/src/ui/TimeRegion.vue +54 -0
@@ -0,0 +1,110 @@
1
+ import { DEFAULT_TICKS_MS as a } from "../animation/config.js";
2
+ import { Animator as h } from "../animation/animator.js";
3
+ const u = { entries: [], isAnimating: !1 }, c = (s, t, e) => s + (t - s) * e;
4
+ function f(s, t) {
5
+ if (s === t) return !0;
6
+ if (s.length !== t.length) return !1;
7
+ for (let e = 0; e < s.length; e++)
8
+ if (s[e] !== t[e]) return !1;
9
+ return !0;
10
+ }
11
+ class m {
12
+ #e = [];
13
+ #r = [];
14
+ #t = /* @__PURE__ */ new Map();
15
+ #i;
16
+ /**
17
+ * `false` during the initial mount phase: `setCurrentTicks` snaps each
18
+ * entering/exiting animator to its target (duration `0`) so the very
19
+ * first niceTickValues sets land at full opacity with no fade. The
20
+ * chart's renderMain flips this once the chart has reached a settled
21
+ * frame.
22
+ */
23
+ #s = !1;
24
+ constructor(t = {}) {
25
+ this.#i = t.fadeMs ?? a;
26
+ }
27
+ /**
28
+ * Record the latest tick set. Diffs against the current set and either
29
+ * starts a `0 → 1` fade-in for new values or a `1 → 0` fade-out for
30
+ * dropped ones. Idempotent on element-wise equal arrays.
31
+ *
32
+ * Callers must invoke {@link tick} every frame to advance the animators —
33
+ * `snapshot()` reads the current state but does not advance time.
34
+ */
35
+ setCurrentTicks(t) {
36
+ if (f(t, this.#e)) return;
37
+ const e = this.#s ? this.#i : 0, o = new Set(t), r = new Set(this.#e);
38
+ for (const i of t) {
39
+ if (r.has(i)) continue;
40
+ let n = this.#t.get(i);
41
+ n === void 0 && (n = new h({ initial: 0, duration: this.#i, lerp: c }), this.#t.set(i, n)), n.setTarget(1, { duration: e });
42
+ }
43
+ for (const i of this.#e) {
44
+ if (o.has(i)) continue;
45
+ const n = this.#t.get(i);
46
+ n !== void 0 && n.setTarget(0, { duration: e });
47
+ }
48
+ this.#r = this.#e, this.#e = t.slice();
49
+ }
50
+ /**
51
+ * Advance each tracked animator against `now`. Animators that settle
52
+ * at zero are dropped from the map; non-zero settled animators stay so
53
+ * a subsequent re-entering tick can pick them up without a flicker.
54
+ * Returns `true` while any animator is still in flight.
55
+ */
56
+ tick(t) {
57
+ let e = !1;
58
+ for (const [o, r] of this.#t) {
59
+ if (r.tick(t)) {
60
+ e = !0;
61
+ continue;
62
+ }
63
+ r.current === 0 && this.#t.delete(o);
64
+ }
65
+ return e;
66
+ }
67
+ /**
68
+ * Build a renderer-ready snapshot from the tracker's own animator state.
69
+ * No external opacity map is consulted — `setCurrentTicks` + `tick(now)`
70
+ * are the only inputs.
71
+ */
72
+ snapshot() {
73
+ if (this.#t.size === 0) return u;
74
+ const t = [];
75
+ let e = !1;
76
+ for (const [o, r] of this.#t) {
77
+ const i = r.current;
78
+ i <= 0 || (t.push({ value: o, opacity: i }), r.animating && (e = !0));
79
+ }
80
+ return { entries: t, isAnimating: e };
81
+ }
82
+ getCurrentTicks() {
83
+ return this.#e;
84
+ }
85
+ getPreviousTicks() {
86
+ return this.#r;
87
+ }
88
+ /**
89
+ * Reconfigure the cross-fade duration. Chart calls this after resolving
90
+ * `animations.axis.ticks` so the tracker honors the user's config
91
+ * without plumbing options through the scale constructors.
92
+ */
93
+ setFadeMs(t) {
94
+ this.#i = t;
95
+ }
96
+ /** Whether subsequent tick-set changes should fade-in or snap. */
97
+ markArmed() {
98
+ this.#s = !0;
99
+ }
100
+ get isArmed() {
101
+ return this.#s;
102
+ }
103
+ /** Drop all tracked ticks. Use after dataset swap so stale values don't linger. */
104
+ reset() {
105
+ this.#e = [], this.#r = [], this.#t.clear(), this.#s = !1;
106
+ }
107
+ }
108
+ export {
109
+ m as AxisTickTracker
110
+ };
@@ -0,0 +1,145 @@
1
+ import { niceTimeIntervals as u } from "../utils/time.js";
2
+ import { AxisTickTracker as m } from "./tick-tracker.js";
3
+ const c = 50, f = 80;
4
+ class p {
5
+ /**
6
+ * Shared fade state for time ticks. Grid lines (canvas) and DOM tick labels
7
+ * read opacity from the same tracker, so a tick fading in/out looks
8
+ * identical on both surfaces.
9
+ */
10
+ tickTracker = new m();
11
+ from = 0;
12
+ to = 0;
13
+ width = 1;
14
+ pixelRatio = 1;
15
+ dataInterval = null;
16
+ // Label density knobs — written by <TimeAxis labelCount=… minLabelSpacing=…>.
17
+ labelCountHintValue = null;
18
+ minSpacingValue = null;
19
+ resolvedInterval = null;
20
+ lastInterval = null;
21
+ /**
22
+ * "want" (desired interval post-floor) at the time lastInterval was snapped.
23
+ * Hysteresis band anchors to this so micro range drift back across a tier
24
+ * boundary doesn't flip the label density — mirrors YScale, see there for
25
+ * the full reasoning.
26
+ */
27
+ lastWant = null;
28
+ /** Identifies the dataInterval bucket whose tier list drove lastInterval. */
29
+ lastBucketKey = null;
30
+ get labelCountHint() {
31
+ return this.labelCountHintValue;
32
+ }
33
+ get minLabelSpacing() {
34
+ return this.minSpacingValue ?? f;
35
+ }
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;
40
+ }
41
+ this.resolveInterval();
42
+ }
43
+ setLabelCount(t) {
44
+ this.labelCountHintValue = v(t), this.resetHysteresis(), this.resolveInterval();
45
+ }
46
+ setMinSpacing(t) {
47
+ this.minSpacingValue = d(t), this.resetHysteresis(), this.resolveInterval();
48
+ }
49
+ resetHysteresis() {
50
+ this.lastInterval = null, this.lastWant = null;
51
+ }
52
+ timeToX(t) {
53
+ return this.to <= this.from ? 0 : (t - this.from) / (this.to - this.from) * this.width;
54
+ }
55
+ timeToBitmapX(t) {
56
+ return Math.round(this.timeToX(t) * this.pixelRatio);
57
+ }
58
+ xToTime(t) {
59
+ return this.to <= this.from ? this.from : this.from + t / this.width * (this.to - this.from);
60
+ }
61
+ pixelDeltaToTimeDelta(t) {
62
+ return this.to <= this.from ? 0 : t / this.width * (this.to - this.from);
63
+ }
64
+ barWidthMedia(t) {
65
+ return this.to <= this.from ? 0 : t / (this.to - this.from) * this.width;
66
+ }
67
+ barWidthBitmap(t) {
68
+ return Math.max(1, Math.round(this.barWidthMedia(t) * this.pixelRatio));
69
+ }
70
+ /**
71
+ * Evenly spaced "nice" tick times. Resolution uses the cached
72
+ * `dataInterval` set by `update()`; callers that bypass `update()` can
73
+ * still pass it here for back-compat.
74
+ */
75
+ niceTickValues(t) {
76
+ if (this.to <= this.from) return { ticks: [], tickInterval: 0 };
77
+ 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();
80
+ }
81
+ 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 };
85
+ }
86
+ getRange() {
87
+ return { from: this.from, to: this.to };
88
+ }
89
+ getMediaWidth() {
90
+ return this.width;
91
+ }
92
+ /**
93
+ * Resolve the next tick interval. Walks `niceTimeIntervals(dataInterval)`
94
+ * looking for the smallest tier ≥ desired spacing; retains the previous
95
+ * tier inside a ratio band so micro pan/zoom doesn't flip label density.
96
+ */
97
+ resolveInterval() {
98
+ if (this.to <= this.from || this.width <= 0 || this.dataInterval == null || this.dataInterval <= 0) {
99
+ this.resolvedInterval = null;
100
+ return;
101
+ }
102
+ const t = u(this.dataInterval), s = this.to - this.from, a = s / this.width * this.minLabelSpacing;
103
+ let e;
104
+ if (this.labelCountHint != null) {
105
+ const l = Math.max(1, this.labelCountHint);
106
+ e = Math.max(s / l, a);
107
+ } else
108
+ e = a;
109
+ 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) {
112
+ this.resolvedInterval = this.lastInterval;
113
+ return;
114
+ }
115
+ }
116
+ let n = t[t.length - 1], r = t.length - 1;
117
+ for (let l = 0; l < t.length; l++)
118
+ if (t[l] >= e) {
119
+ n = t[l], r = l;
120
+ break;
121
+ }
122
+ if (this.labelCountHint != null && r > 0 && this.countTicks(n) < this.labelCountHint) {
123
+ const l = t[r - 1];
124
+ l >= a && (n = l);
125
+ }
126
+ this.resolvedInterval = n, this.lastInterval = n, this.lastWant = e;
127
+ }
128
+ countTicks(t) {
129
+ 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);
132
+ }
133
+ }
134
+ function h(i) {
135
+ return i < 6e4 ? 0 : i < 36e5 ? 1 : i < 864e5 ? 2 : 3;
136
+ }
137
+ function v(i) {
138
+ return typeof i == "number" && Number.isFinite(i) && i >= 2 ? Math.floor(i) : null;
139
+ }
140
+ function d(i) {
141
+ return typeof i == "number" && Number.isFinite(i) && i > 0 ? i : null;
142
+ }
143
+ export {
144
+ p as XScale
145
+ };
@@ -0,0 +1,156 @@
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;
6
+ return (i <= 1 ? 1 : i <= 2 ? 2 : i <= 5 ? 5 : 10) * t;
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;
11
+ return (i >= 5 ? 5 : i >= 2 ? 2 : 1) * t;
12
+ }
13
+ const c = 50, v = 50;
14
+ class M {
15
+ /** Shared fade state for Y ticks; see {@link TimeScale.tickTracker}. */
16
+ tickTracker = new o();
17
+ min = 0;
18
+ max = 0;
19
+ height = 1;
20
+ pixelRatio = 1;
21
+ // Label density knobs — written by <YAxis labelCount=… minLabelSpacing=…>.
22
+ labelCountHintValue = null;
23
+ minSpacingValue = null;
24
+ /** Cached {1,2,5}×10^k interval resolved at update-time; null when degenerate. */
25
+ resolvedInterval = null;
26
+ /** Previous resolved interval — drives the ratio-band hysteresis. */
27
+ lastInterval = null;
28
+ /**
29
+ * rawInterval at the moment `lastInterval` was snapped. Anchoring the
30
+ * hysteresis band to *raw-at-snap* (not `lastInterval`) is what creates
31
+ * naturally asymmetric tier hysteresis: after an escalation the band
32
+ * starts just above the tier boundary, so live range drift back across
33
+ * the boundary doesn't instantly flip back.
34
+ */
35
+ lastRawInterval = null;
36
+ /** Custom formatter (driven by `<YAxis format=…>`). */
37
+ customFormat = null;
38
+ get labelCountHint() {
39
+ return this.labelCountHintValue;
40
+ }
41
+ get minLabelSpacing() {
42
+ return this.minSpacingValue ?? v;
43
+ }
44
+ /** Recalculate the scale with a new Y range, chart height, and device pixel ratio. */
45
+ update(t, i, a) {
46
+ this.min = t.min, this.max = t.max, this.height = i, this.pixelRatio = a, this.resolveInterval();
47
+ }
48
+ /** Desired label count. Invalid values (NaN, <2, Infinity) clear the hint. */
49
+ setLabelCount(t) {
50
+ this.labelCountHintValue = f(t), this.resetHysteresis(), this.resolveInterval();
51
+ }
52
+ /** Minimum pixel gap between adjacent labels. */
53
+ setMinSpacing(t) {
54
+ this.minSpacingValue = g(t), this.resetHysteresis(), this.resolveInterval();
55
+ }
56
+ resetHysteresis() {
57
+ this.lastInterval = null, this.lastRawInterval = null;
58
+ }
59
+ /** Install (or clear) a custom formatter. */
60
+ setFormat(t) {
61
+ this.customFormat = t;
62
+ }
63
+ /** Read back the currently installed formatter — null when the built-in is active. */
64
+ getFormat() {
65
+ return this.customFormat;
66
+ }
67
+ /** Convert a value to a Y position in CSS (media) pixels. */
68
+ valueToY(t) {
69
+ const i = this.max - this.min;
70
+ return i === 0 ? this.height / 2 : (1 - (t - this.min) / i) * this.height;
71
+ }
72
+ /** Convert a value to a Y position in physical (bitmap) pixels. */
73
+ valueToBitmapY(t) {
74
+ return Math.round(this.valueToY(t) * this.pixelRatio);
75
+ }
76
+ /** Convert a Y position in CSS pixels back to a value. */
77
+ yToValue(t) {
78
+ const i = this.max - this.min;
79
+ return i === 0 ? this.min : this.max - t / this.height * i;
80
+ }
81
+ /**
82
+ * Generate evenly spaced "nice" tick values for the current range and
83
+ * chart height. Pure read — resolution happens in `update()`.
84
+ */
85
+ niceTickValues() {
86
+ 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;
90
+ }
91
+ getRange() {
92
+ return { min: this.min, max: this.max };
93
+ }
94
+ getMediaHeight() {
95
+ return this.height;
96
+ }
97
+ /**
98
+ * Format a tick value for display. When a custom formatter is installed
99
+ * (via {@link setFormat} or the `format` prop on `<YAxis>`), it wins;
100
+ * otherwise decimals are driven by the resolved tick interval — so
101
+ * ticks at step 100 never render as "42000.0" just because the visible
102
+ * range happens to be < 1000.
103
+ */
104
+ formatY(t) {
105
+ if (this.customFormat) return this.customFormat(t);
106
+ if (this.max - this.min >= 1e6) return h(t);
107
+ const i = this.resolvedInterval, a = i != null && i > 0 ? Math.max(0, -Math.floor(Math.log10(i))) : 1;
108
+ return t.toFixed(a);
109
+ }
110
+ /**
111
+ * Resolve the interval to a {1,2,5}×10^k tick size that satisfies the pixel
112
+ * floor and (optionally) targets `labelCountHint` labels.
113
+ *
114
+ * Hysteresis: the band [0.8×, 1.25×] is anchored to **rawInterval at last
115
+ * snap**, not to `lastInterval`. After an escalation (e.g. 100 → 200),
116
+ * `lastRawInterval` sits just past the tier boundary, so typical live
117
+ * range drift back across the boundary stays inside the band and does
118
+ * *not* de-escalate. This prevents the 2-tier flicker users see when new
119
+ * candles nudge the Y range across a raw-interval threshold.
120
+ */
121
+ resolveInterval() {
122
+ if (this.max <= this.min || this.height <= 0) {
123
+ this.resolvedInterval = null;
124
+ return;
125
+ }
126
+ 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) {
131
+ this.resolvedInterval = this.lastInterval;
132
+ return;
133
+ }
134
+ }
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);
139
+ }
140
+ this.resolvedInterval = s, this.lastInterval = s, this.lastRawInterval = e;
141
+ }
142
+ countTicks(t) {
143
+ if (!(t > 0)) return 0;
144
+ const i = Math.ceil(this.min / t) * t;
145
+ return Math.max(0, Math.floor((this.max - i) / t) + 1);
146
+ }
147
+ }
148
+ function f(n) {
149
+ return typeof n == "number" && Number.isFinite(n) && n >= 2 ? Math.floor(n) : null;
150
+ }
151
+ function g(n) {
152
+ return typeof n == "number" && Number.isFinite(n) && n > 0 ? n : null;
153
+ }
154
+ export {
155
+ M as YScale
156
+ };