1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-07-18 20:59:39 +02:00

Dynamic y-axis baseline value for chart scales
Some checks failed
Publish Docker image / ci (push) Has been cancelled
Publish Docker image / Build docker image (push) Has been cancelled

This commit is contained in:
Zach Gollwitzer 2025-06-27 12:57:23 -04:00
parent e4a82d85e8
commit 869462a9a5

View file

@ -510,29 +510,55 @@ export default class extends Controller {
get _d3YScale() { get _d3YScale() {
const dataMin = d3.min(this._normalDataPoints, this._getDatumValue); const dataMin = d3.min(this._normalDataPoints, this._getDatumValue);
const dataMax = d3.max(this._normalDataPoints, this._getDatumValue); const dataMax = d3.max(this._normalDataPoints, this._getDatumValue);
// Use 0 as baseline, but allow negative values if they exist // Handle edge case where all values are the same
const yMin = Math.min(0, dataMin); if (dataMin === dataMax) {
// Handle edge case where all values are the same (including all zeros)
const range = dataMax - yMin;
if (range === 0) {
// If all values are 0, show 0-100 scale. Otherwise center the value with padding.
const padding = dataMax === 0 ? 100 : Math.abs(dataMax) * 0.5; const padding = dataMax === 0 ? 100 : Math.abs(dataMax) * 0.5;
return d3 return d3
.scaleLinear() .scaleLinear()
.rangeRound([this._d3ContainerHeight, 0]) .rangeRound([this._d3ContainerHeight, 0])
.domain([yMin - padding, dataMax + padding]); .domain([dataMin - padding, dataMax + padding]);
}
const dataRange = dataMax - dataMin;
const avgValue = (dataMax + dataMin) / 2;
// Calculate relative change as a percentage
const relativeChange = avgValue !== 0 ? dataRange / Math.abs(avgValue) : 1;
// Dynamic baseline calculation
let yMin;
let yMax;
// For small relative changes (< 10%), use a tighter scale
if (relativeChange < 0.1 && dataMin > 0) {
// Start axis at a percentage below the minimum, not at 0
const baselinePadding = dataRange * 2; // Show 2x the data range below min
yMin = Math.max(0, dataMin - baselinePadding);
yMax = dataMax + dataRange * 0.5; // Add 50% padding above
} else {
// For larger changes or when data crosses zero, use more context
// Always include 0 when data is negative or close to 0
if (dataMin < 0 || (dataMin >= 0 && dataMin < avgValue * 0.1)) {
yMin = Math.min(0, dataMin * 1.1);
} else {
// Otherwise use dynamic baseline
yMin = dataMin - dataRange * 0.3;
}
yMax = dataMax + dataRange * 0.1;
}
// Adjust padding for labels if needed
if (this.useLabelsValue) {
const extraPadding = (yMax - yMin) * 0.1;
yMin -= extraPadding;
yMax += extraPadding;
} }
// Add padding to prevent overlapping with labels and for visual breathing room
const topPadding = range * 0.1;
const bottomPadding = range * (this.useLabelsValue ? 0.15 : 0.05);
return d3 return d3
.scaleLinear() .scaleLinear()
.rangeRound([this._d3ContainerHeight, 0]) .rangeRound([this._d3ContainerHeight, 0])
.domain([yMin - bottomPadding, dataMax + topPadding]); .domain([yMin, yMax]);
} }
_setupResizeObserver() { _setupResizeObserver() {