@@ -3,7 +3,7 @@ import React from 'react';
33import { axisLeft , axisRight , line , select } from 'd3' ;
44import type { Axis , AxisDomain , AxisScale , BaseType , Selection } from 'd3' ;
55
6- import type { ChartScale , PreparedAxis , PreparedSplit } from '../../hooks' ;
6+ import type { ChartScale , PreparedAxis , PreparedAxisPlotLine , PreparedSplit } from '../../hooks' ;
77import {
88 block ,
99 calculateCos ,
@@ -12,6 +12,7 @@ import {
1212 getAxisHeight ,
1313 getAxisTitleRows ,
1414 getClosestPointsRange ,
15+ getLineDashArray ,
1516 getScaleTicks ,
1617 getTicksCount ,
1718 handleOverflowingText ,
@@ -30,6 +31,7 @@ type Props = {
3031 width : number ;
3132 height : number ;
3233 split : PreparedSplit ;
34+ plotRef ?: React . MutableRefObject < SVGGElement | null > ;
3335} ;
3436
3537function transformLabel ( args : { node : Element ; axis : PreparedAxis } ) {
@@ -130,8 +132,12 @@ function getTitlePosition(args: {axis: PreparedAxis; axisHeight: number; rowCoun
130132 return { x, y} ;
131133}
132134
135+ type PlotLineData = {
136+ transform : string ;
137+ } & PreparedAxisPlotLine ;
138+
133139export const AxisY = ( props : Props ) => {
134- const { axes, width, height : totalHeight , scale, split} = props ;
140+ const { axes, width, height : totalHeight , scale, split, plotRef } = props ;
135141 const height = getAxisHeight ( { split, boundsHeight : totalHeight } ) ;
136142 const ref = React . useRef < SVGGElement | null > ( null ) ;
137143
@@ -143,19 +149,36 @@ export const AxisY = (props: Props) => {
143149 const svgElement = select ( ref . current ) ;
144150 svgElement . selectAll ( '*' ) . remove ( ) ;
145151
152+ const getAxisPosition = ( axis : PreparedAxis ) => {
153+ const top = split . plots [ axis . plotIndex ] ?. top || 0 ;
154+ if ( axis . position === 'left' ) {
155+ return `translate(0, ${ top } px)` ;
156+ }
157+
158+ return `translate(${ width } px, 0)` ;
159+ } ;
160+
161+ const plotLines = axes . reduce < PlotLineData [ ] > ( ( acc , axis ) => {
162+ if ( axis . plotLines . length ) {
163+ acc . push (
164+ ...axis . plotLines . map ( ( plotLine ) => {
165+ return {
166+ ...plotLine ,
167+ transform : getAxisPosition ( axis ) ,
168+ } ;
169+ } ) ,
170+ ) ;
171+ }
172+
173+ return acc ;
174+ } , [ ] ) ;
175+
146176 const axisSelection = svgElement
147177 . selectAll ( 'axis' )
148178 . data ( axes )
149179 . join ( 'g' )
150180 . attr ( 'class' , b ( ) )
151- . style ( 'transform' , ( d ) => {
152- const top = split . plots [ d . plotIndex ] ?. top || 0 ;
153- if ( d . position === 'left' ) {
154- return `translate(0, ${ top } px)` ;
155- }
156-
157- return `translate(${ width } px, 0)` ;
158- } ) ;
181+ . style ( 'transform' , ( d ) => getAxisPosition ( d ) ) ;
159182
160183 axisSelection . each ( ( d , index , node ) => {
161184 const seriesScale = scale [ index ] ;
@@ -165,11 +188,9 @@ export const AxisY = (props: Props) => {
165188 BaseType ,
166189 unknown
167190 > ;
191+ const axisScale = seriesScale as AxisScale < AxisDomain > ;
168192 const yAxisGenerator = getAxisGenerator ( {
169- axisGenerator :
170- d . position === 'left'
171- ? axisLeft ( seriesScale as AxisScale < AxisDomain > )
172- : axisRight ( seriesScale as AxisScale < AxisDomain > ) ,
193+ axisGenerator : d . position === 'left' ? axisLeft ( axisScale ) : axisRight ( axisScale ) ,
173194 preparedAxis : d ,
174195 height,
175196 width,
@@ -215,6 +236,47 @@ export const AxisY = (props: Props) => {
215236 . remove ( ) ;
216237 }
217238
239+ if ( plotRef && d . plotLines . length > 0 ) {
240+ const plotLineClassName = b ( 'plotLine' ) ;
241+ const plotLineContainer = select ( plotRef . current ) ;
242+ plotLineContainer . selectAll ( `.${ plotLineClassName } ` ) . remove ( ) ;
243+
244+ const plotLinesSelection = plotLineContainer
245+ . selectAll ( `.${ plotLineClassName } ` )
246+ . data ( plotLines )
247+ . join ( 'g' )
248+ . attr ( 'class' , plotLineClassName )
249+ . style ( 'transform' , ( plotLine ) => plotLine . transform ) ;
250+
251+ plotLinesSelection
252+ . append ( 'path' )
253+ . attr ( 'd' , ( plotLine ) => {
254+ const plotLineValue = Number ( axisScale ( plotLine . value ) ) ;
255+ const points : [ number , number ] [ ] = [
256+ [ 0 , plotLineValue ] ,
257+ [ width , plotLineValue ] ,
258+ ] ;
259+
260+ return line ( ) ( points ) ;
261+ } )
262+ . attr ( 'stroke' , ( plotLine ) => plotLine . color )
263+ . attr ( 'stroke-width' , ( plotLine ) => plotLine . width )
264+ . attr ( 'stroke-dasharray' , ( plotLine ) =>
265+ getLineDashArray ( plotLine . dashStyle , plotLine . width ) ,
266+ )
267+ . attr ( 'opacity' , ( plotLine ) => plotLine . opacity ) ;
268+
269+ plotLinesSelection . each ( ( plotLineData , i , nodes ) => {
270+ const plotLineSelection = select ( nodes [ i ] ) ;
271+
272+ if ( plotLineData . layerPlacement === 'before' ) {
273+ plotLineSelection . lower ( ) ;
274+ } else {
275+ plotLineSelection . raise ( ) ;
276+ }
277+ } ) ;
278+ }
279+
218280 return axisItem ;
219281 } ) ;
220282
0 commit comments