77 niceTicks ,
88 thinIndices ,
99 dynamicTickTarget ,
10+ xAxisTickTarget ,
11+ applyEdgeLabels ,
1012 measureLabelWidth ,
1113 axisPadForLabels ,
1214 formatChartDatumValue ,
@@ -30,6 +32,7 @@ import {
3032 resolveSeries ,
3133 resolveTooltipMode ,
3234 axisTickTarget ,
35+ type XAxisLabelProps ,
3336} from "./types" ;
3437import { ChartWrapper } from "./ChartWrapper" ;
3538import { useTrackedCallback } from "../Analytics/useTrackedCallback" ;
@@ -39,7 +42,9 @@ const EMPTY_TICKS = { min: 0, max: 1, ticks: [0, 1] } as const;
3942
4043const clickIndexMeta = ( index : number ) => ( { index } ) ;
4144
42- export interface BarChartProps extends React . ComponentPropsWithoutRef < "div" > {
45+ export interface BarChartProps
46+ extends React . ComponentPropsWithoutRef < "div" > ,
47+ XAxisLabelProps {
4348 data : ChartDatum [ ] ;
4449 /**
4550 * Pre-measurement width in pixels. Used as a fallback before
@@ -65,6 +70,22 @@ export interface BarChartProps extends React.ComponentPropsWithoutRef<"div"> {
6570 formatValue ?: ( value : number ) => string ;
6671 formatXLabel ?: ( value : ChartDatumValue ) => string ;
6772 formatYLabel ?: ( value : number ) => string ;
73+ /**
74+ * How vertical-bar x-axis (category) labels are thinned to avoid overlap.
75+ * Has no effect on horizontal bar charts.
76+ * - `"fixed"` (default): roughly one label per 60px, regardless of width.
77+ * - `"measured"`: spacing based on the measured pixel width of the labels,
78+ * so wide labels (dates, currency) get more room and short labels pack in.
79+ */
80+ xAxisLabels ?: "fixed" | "measured" ;
81+ /**
82+ * Whether the first and last x-axis (category) labels are shown on vertical
83+ * bar charts. Has no effect on horizontal bar charts.
84+ * - `"show"` (default): keep the edge labels.
85+ * - `"hide"`: drop the first and last labels (useful when they collide with
86+ * the y-axis or chart edges).
87+ */
88+ xAxisEdgeLabels ?: "show" | "hide" ;
6889 /** Fixed Y-axis domain. Overrides auto-computed domain from data. */
6990 yDomain ?: [ number , number ] ;
7091 /** Show legend below chart. */
@@ -111,6 +132,8 @@ export const Bar = React.forwardRef<HTMLDivElement, BarChartProps>(function Bar(
111132 formatValue,
112133 formatXLabel,
113134 formatYLabel,
135+ xAxisLabels = "fixed" ,
136+ xAxisEdgeLabels = "show" ,
114137 yDomain,
115138 legend,
116139 loading,
@@ -234,6 +257,30 @@ export const Bar = React.forwardRef<HTMLDivElement, BarChartProps>(function Bar(
234257 const padRight = isHorizontal && showValueAxis ? 40 : PAD_RIGHT ;
235258 const plotWidth = Math . max ( 0 , width - padLeft - padRight ) ;
236259
260+ const categoryAxisLabels = React . useMemo ( ( ) => {
261+ if ( ! xKey ) return [ ] ;
262+ const labels = data . map ( ( d ) =>
263+ formatXLabel ? formatXLabel ( d [ xKey ] ) : formatChartDatumValue ( d [ xKey ] ) ,
264+ ) ;
265+ const maxLabels = isHorizontal
266+ ? Math . max ( 2 , Math . floor ( plotHeight / 24 ) )
267+ : xAxisTickTarget ( xAxisLabels , plotWidth , ( ) => labels ) ;
268+ const indices = thinIndices ( data . length , maxLabels ) ;
269+ const visibleIndices = isHorizontal
270+ ? indices
271+ : applyEdgeLabels ( xAxisEdgeLabels , indices ) ;
272+ return visibleIndices . map ( ( index ) => ( { index, label : labels [ index ] } ) ) ;
273+ } , [
274+ data ,
275+ formatXLabel ,
276+ isHorizontal ,
277+ plotHeight ,
278+ plotWidth ,
279+ xKey ,
280+ xAxisLabels ,
281+ xAxisEdgeLabels ,
282+ ] ) ;
283+
237284 const tickTarget = React . useMemo ( ( ) => {
238285 if ( ! isHorizontal ) return verticalTickTarget ;
239286 const fmt = formatYLabel ?? ( ( v : number ) => String ( v ) ) ;
@@ -915,11 +962,7 @@ export const Bar = React.forwardRef<HTMLDivElement, BarChartProps>(function Bar(
915962 { /* Category axis labels (thinned to avoid overlap) */ }
916963 { xKey &&
917964 ( ( ) => {
918- const maxLabels = isHorizontal
919- ? Math . max ( 2 , Math . floor ( plotHeight / 24 ) )
920- : Math . max ( 2 , Math . floor ( plotWidth / 60 ) ) ;
921- const indices = thinIndices ( data . length , maxLabels ) ;
922- return indices . map ( ( i ) =>
965+ return categoryAxisLabels . map ( ( { index : i , label } ) =>
923966 isHorizontal ? (
924967 < text
925968 key = { i }
@@ -929,9 +972,7 @@ export const Bar = React.forwardRef<HTMLDivElement, BarChartProps>(function Bar(
929972 textAnchor = "end"
930973 dominantBaseline = "middle"
931974 >
932- { formatXLabel
933- ? formatXLabel ( data [ i ] [ xKey ] )
934- : formatChartDatumValue ( data [ i ] [ xKey ] ) }
975+ { label }
935976 </ text >
936977 ) : (
937978 < text
@@ -942,9 +983,7 @@ export const Bar = React.forwardRef<HTMLDivElement, BarChartProps>(function Bar(
942983 textAnchor = "middle"
943984 dominantBaseline = "auto"
944985 >
945- { formatXLabel
946- ? formatXLabel ( data [ i ] [ xKey ] )
947- : formatChartDatumValue ( data [ i ] [ xKey ] ) }
986+ { label }
948987 </ text >
949988 ) ,
950989 ) ;
0 commit comments