From b516235507319ef6c6e594ca01d4722d5b93d881 Mon Sep 17 00:00:00 2001 From: James Xu Date: Fri, 1 May 2026 15:09:41 -0700 Subject: [PATCH] feat(BarChart): anchor non-stacked bars at value 0 when 0 is in domain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Non-stacked bar charts on signed data (e.g. net = inflow - outflow) used to draw every bar from the bottom of the plot area up to its value, so negative values produced large bars hugging the lower edge instead of short bars hanging below the zero line. Anchor each bar at clamp(0, yMin, yMax) so: - All-positive data: anchor = yMin (bottom) — same as before. - Mixed signs: anchor = 0; positive bars grow up, negative bars grow down. - All-negative data: anchor = yMax (top); bars hang down to value. Same treatment applied to the horizontal orientation. Stacked path is unchanged (cumulative semantics already differ). --- src/components/Chart/BarChart.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/Chart/BarChart.tsx b/src/components/Chart/BarChart.tsx index 42f24d8..ca7428d 100644 --- a/src/components/Chart/BarChart.tsx +++ b/src/components/Chart/BarChart.tsx @@ -581,16 +581,22 @@ export const Bar = React.forwardRef( const v = Number(d[s.key]) || 0; const barFill = getBarColor?.(d, di, s.key) ?? s.color; const barOffset = slotStart + si * (barThickness + BAR_ITEM_GAP); + const anchor = Math.min(yMax, Math.max(yMin, 0)); if (isHorizontal) { - const barW = ((v - yMin) / (yMax - yMin)) * plotWidth; + const xAnchor = linearScale(anchor, yMin, yMax, 0, plotWidth); + const xVal = linearScale(v, yMin, yMax, 0, plotWidth); + const barX = Math.min(xAnchor, xVal); + const barW = Math.abs(xVal - xAnchor); return ( - ); } - const barH = ((v - yMin) / (yMax - yMin)) * plotHeight; - const barY = plotHeight - barH; + const yAnchor = linearScale(anchor, yMin, yMax, plotHeight, 0); + const yVal = linearScale(v, yMin, yMax, plotHeight, 0); + const barY = Math.min(yAnchor, yVal); + const barH = Math.abs(yVal - yAnchor); return (