@@ -91,6 +91,63 @@ export const transformInputData = <
9191 { } as TransformedData < RawData , XK , YK > [ "y" ] ,
9292 ) ;
9393
94+ const rawChartWidth = outputWindow . xMax - outputWindow . xMin ;
95+ const xTickValues = xAxis ?. tickValues ;
96+ const xTicks = xAxis ?. tickCount ;
97+
98+ const tickDomainsX = getDomainFromTicks ( xTickValues ) ;
99+ const ix = data . map ( ( datum ) => datum [ xKey ] ) as InputFields < RawData > [ XK ] [ ] ;
100+ const ixNum = ix . map ( ( val , i ) => ( isNumericalData ? ( val as number ) : i ) ) ;
101+
102+ // For non‐numeric (ordinal) data, use the index values
103+ // if user provides a domain- use that as our min/max
104+ // if tickValues are provided- we use that instead
105+ // if we find min/max of y values across all yKeys- and use that for yrange instead
106+ const ixMin = isNumericalData
107+ ? asNumber ( domain ?. x ?. [ 0 ] ?? tickDomainsX ?. [ 0 ] ?? ixNum . at ( 0 ) )
108+ : 0 ;
109+ const ixMax = isNumericalData
110+ ? asNumber ( domain ?. x ?. [ 1 ] ?? tickDomainsX ?. [ 1 ] ?? ixNum . at ( - 1 ) )
111+ : ixNum . length - 1 ;
112+
113+ const xTempScale = makeScale ( {
114+ inputBounds : ixMin === ixMax ? [ ixMin - 1 , ixMax + 1 ] : [ ixMin , ixMax ] ,
115+ outputBounds : [ 0 , rawChartWidth ] ,
116+ } ) ;
117+
118+ // normalize xTicks values either via the d3 scaleLinear ticks() function or our custom downSample function
119+ // 4consistency we do it here- so we have both y and x ticks to pass to the axis generator
120+ const xTicksNormalized = xTickValues
121+ ? downsampleTicks ( xTickValues , xTicks )
122+ : xTempScale . ticks ( xTicks ) ;
123+
124+ const maxXLabel = Math . max (
125+ ...xTicksNormalized . map ( ( xTick ) => {
126+ const labelValue = xAxis . formatXLabel
127+ ? xAxis . formatXLabel (
128+ xTick as unknown as Parameters < typeof xAxis . formatXLabel > [ 0 ] ,
129+ )
130+ : String ( xTick ) ;
131+ const labelStr = String ( labelValue ) ;
132+ if ( ! xAxis . font ) return 0 ;
133+ const glyphIDs = xAxis . font . getGlyphIDs ( labelStr ) ;
134+ const widths = xAxis . font . getGlyphWidths ?.( glyphIDs ) ?? [ ] ;
135+ return widths . reduce ( ( sum , w ) => sum + w , 0 ) ;
136+ } ) ,
137+ ) ;
138+
139+ // workt with adjustedoutputwindow isntead of directly
140+ // working with outpuwidnow
141+ const adjustedOutputWindow = { ...outputWindow } ;
142+
143+ if ( labelRotate && xAxis . labelPosition === "outset" ) {
144+ const rotateOffset = Math . abs ( maxXLabel * getOffsetFromAngle ( labelRotate ) ) ;
145+ if ( xAxis . axisSide === "bottom" ) {
146+ adjustedOutputWindow . yMax -= rotateOffset ;
147+ } else if ( xAxis . axisSide === "top" ) {
148+ adjustedOutputWindow . yMin += rotateOffset ;
149+ }
150+ }
94151 // 1. Set up our y axes first...
95152 // Transform data for each y-axis configuration
96153 const yAxesTransformed = ( yAxes ?? [ { } ] ) ?. map ( ( yAxis ) => {
@@ -141,24 +198,22 @@ export const transformInputData = <
141198 const xAxisSide = xAxis ?. axisSide ;
142199 const xLabelPosition = xAxis ?. labelPosition ;
143200
144- // bottom, outset
145201 if ( xAxisSide === "bottom" && xLabelPosition === "outset" ) {
146202 return [
147- outputWindow . yMin ,
148- outputWindow . yMax +
203+ adjustedOutputWindow . yMin ,
204+ adjustedOutputWindow . yMax +
149205 ( xTickCount > 0 ? - fontHeight - yLabelOffset * 2 : 0 ) ,
150206 ] ;
151207 }
152- // Top outset
153208 if ( xAxisSide === "top" && xLabelPosition === "outset" ) {
154209 return [
155- outputWindow . yMin +
210+ adjustedOutputWindow . yMin +
156211 ( xTickCount > 0 ? fontHeight + yLabelOffset * 2 : 0 ) ,
157- outputWindow . yMax ,
212+ adjustedOutputWindow . yMax ,
158213 ] ;
159214 }
160- // Inset labels don't need added offsets
161- return [ outputWindow . yMin , outputWindow . yMax ] ;
215+
216+ return [ adjustedOutputWindow . yMin , adjustedOutputWindow . yMax ] ;
162217 } ) ( ) ;
163218
164219 const yScale = makeScale ( {
@@ -244,6 +299,7 @@ export const transformInputData = <
244299 const labelWidth = yAxesTransformed [ index ] ?. maxYLabel ?? 0 ;
245300
246301 // Adjust xMin or xMax based on the axis side and label position
302+ // make ajdustments for label rotation here
247303 if ( yAxisSide === "left" && yLabelPosition === "outset" ) {
248304 xMinAdjustment += yTickCount > 0 ? labelWidth + yLabelOffset : 0 ;
249305 } else if ( yAxisSide === "right" && yLabelPosition === "outset" ) {
@@ -253,33 +309,11 @@ export const transformInputData = <
253309
254310 // Return the adjusted output range
255311 return [
256- outputWindow . xMin + xMinAdjustment ,
257- outputWindow . xMax + xMaxAdjustment ,
312+ adjustedOutputWindow . xMin + xMinAdjustment ,
313+ adjustedOutputWindow . xMax + xMaxAdjustment ,
258314 ] ;
259315 } ) ( ) ;
260316
261- const xTickValues = xAxis ?. tickValues ;
262-
263- // The user can specify either:
264- // custom X tick values
265-
266- // OR
267- // custom X tick count
268- const xTicks = xAxis ?. tickCount ;
269- // x tick domain of [number, number]
270- const tickDomainsX = getDomainFromTicks ( xTickValues ) ;
271-
272- // Input x is just extracting the xKey from each datum
273- const ix = data . map ( ( datum ) => datum [ xKey ] ) as InputFields < RawData > [ XK ] [ ] ;
274- const ixNum = ix . map ( ( val , i ) => ( isNumericalData ? ( val as number ) : i ) ) ;
275-
276- // Generate our x-scale
277- // If user provides a domain, use that as our min / max
278- // Else if, tickValues are provided, we use that instead
279- // Else, we find min / max of y values across all yKeys, and use that for y range instead.
280- const ixMin = asNumber ( domain ?. x ?. [ 0 ] ?? tickDomainsX ?. [ 0 ] ?? ixNum . at ( 0 ) ) ,
281- ixMax = asNumber ( domain ?. x ?. [ 1 ] ?? tickDomainsX ?. [ 1 ] ?? ixNum . at ( - 1 ) ) ;
282-
283317 const xInputBounds : [ number , number ] =
284318 ixMin === ixMax ? [ ixMin - 1 , ixMax + 1 ] : [ ixMin , ixMax ] ;
285319 const xScale = makeScale ( {
@@ -295,50 +329,11 @@ export const transformInputData = <
295329
296330 // Normalize xTicks values either via the d3 scaleLinear ticks() function or our custom downSample function
297331 // For consistency we do it here, so we have both y and x ticks to pass to the axis generator
298- const xTicksNormalized = xTickValues
299- ? downsampleTicks ( xTickValues , xTicks )
300- : xScale . ticks ( xTicks ) ;
301-
302- // If labelRotate is true, dynamically adjust yScale range to accommodate the maximum X label width
303- if ( labelRotate ) {
304- const maxXLabel = Math . max (
305- ...xTicksNormalized . map (
306- ( xTick ) =>
307- xAxis ?. font
308- ?. getGlyphWidths ?.(
309- xAxis . font . getGlyphIDs (
310- xAxis ?. formatXLabel ?.( xTick as never ) || String ( xTick ) ,
311- ) ,
312- )
313- . reduce ( ( sum , value ) => sum + value , 0 ) ?? 0 ,
314- ) ,
315- ) ;
316-
317- // First, we pass labelRotate as radian to Math.sin to get labelOffset multiplier based on maxLabel width
318- // We then use this multiplier to calculate labelOffset for rotated labels
319- const rotateLabelOffset = Math . abs (
320- maxXLabel * getOffsetFromAngle ( labelRotate ) ,
321- ) ;
322-
323- const yScaleRange0 = yAxesTransformed [ 0 ] ?. yScale . range ( ) . at ( 0 ) as number ;
324- const yScaleRange1 = yAxesTransformed [ 0 ] ?. yScale . range ( ) . at ( - 1 ) as number ;
325-
326- // bottom, outset
327- if ( xAxis ?. axisSide === "bottom" && xAxis ?. labelPosition === "outset" ) {
328- yAxesTransformed [ 0 ] ?. yScale . range ( [
329- yScaleRange0 ,
330- yScaleRange1 - rotateLabelOffset ,
331- ] ) ;
332- }
333-
334- // top, outset
335- if ( xAxis ?. axisSide === "top" && xAxis ?. labelPosition === "outset" ) {
336- yAxesTransformed [ 0 ] ?. yScale . range ( [
337- yScaleRange0 + rotateLabelOffset ,
338- yScaleRange1 ,
339- ] ) ;
340- }
341- }
332+ const finalXTicksNormalized = isNumericalData
333+ ? xTickValues
334+ ? downsampleTicks ( xTickValues , xTicks )
335+ : xScale . ticks ( xTicks )
336+ : ixNum ;
342337
343338 const ox = ixNum . map ( ( x ) => xScale ( x ) ! ) ;
344339
@@ -348,7 +343,7 @@ export const transformInputData = <
348343 isNumericalData,
349344 ox,
350345 xScale,
351- xTicksNormalized,
346+ xTicksNormalized : finalXTicksNormalized ,
352347 // conform to type NonEmptyArray<T>
353348 yAxes : [ yAxesTransformed [ 0 ] ! , ...yAxesTransformed . slice ( 1 ) ] ,
354349 } ;
0 commit comments