@@ -26,8 +26,8 @@ type MainGraphResponse = {
2626 meta : {
2727 time_labels : string [ ]
2828 time_label_result_indices : ( number | null ) [ ]
29- comparison_time_labels : string [ ]
30- comparison_time_label_result_indices : ( number | null ) [ ]
29+ comparison_time_labels ? : string [ ]
30+ comparison_time_label_result_indices ? : ( number | null ) [ ]
3131 }
3232 query : {
3333 interval : string
@@ -47,7 +47,7 @@ type GraphDatum = {
4747
4848type XPos = number
4949type YPos = number
50- type Point = [ XPos , YPos [ ] ]
50+ type Point = [ XPos , { yMain : YPos | null ; yComparison : YPos | null } ]
5151
5252type MainGraphData = MainGraphResponse & { period : DashboardPeriod }
5353
@@ -59,7 +59,7 @@ export const MainGraph = ({
5959 data : MainGraphData
6060} ) => {
6161 const { mode } = useTheme ( )
62- const { primaryGradient } = paletteByTheme [ mode ]
62+ const { primaryGradient, secondaryGradient } = paletteByTheme [ mode ]
6363 const svgRef = useRef < SVGSVGElement | null > ( null )
6464
6565 useEffect ( ( ) => {
@@ -101,12 +101,11 @@ export const MainGraph = ({
101101
102102 const points : Point [ ] = remappedData . map ( ( d , index ) => [
103103 x ( index ) ,
104- [
105- [ d . timeLabel , d . value ] as const ,
106- [ d . comparisonTimeLabel , d . comparisonValue ] as const
107- ]
108- . filter ( ( [ label , _v ] ) => label !== null )
109- . map ( ( [ _label , v ] ) => y ( v ! ) )
104+ {
105+ yMain : d . timeLabel !== null ? y ( d . value ! ) : null ,
106+ yComparison :
107+ d . comparisonTimeLabel !== null ? y ( d . comparisonValue ! ) : null
108+ }
110109 ] )
111110
112111 // Create the SVG container.
@@ -173,8 +172,15 @@ export const MainGraph = ({
173172 . attr ( 'class' , tickLineClass )
174173 )
175174
176- const addGradient = ( ) : string => {
177- const id = 'areaGradient'
175+ const addGradient = ( {
176+ id,
177+ stopTop,
178+ stopBottom
179+ } : {
180+ id : string
181+ stopTop : [ string , number ]
182+ stopBottom : [ string , number ]
183+ } ) : string => {
178184 const grad = svg
179185 . append ( 'defs' )
180186 . append ( 'linearGradient' )
@@ -187,14 +193,14 @@ export const MainGraph = ({
187193 grad
188194 . append ( 'stop' )
189195 . attr ( 'offset' , '0%' )
190- . attr ( 'stop-color' , primaryGradient [ 0 ] [ 0 ] )
191- . attr ( 'stop-opacity' , primaryGradient [ 0 ] [ 1 ] )
196+ . attr ( 'stop-color' , stopTop [ 0 ] )
197+ . attr ( 'stop-opacity' , stopTop [ 1 ] )
192198
193199 grad
194200 . append ( 'stop' )
195201 . attr ( 'offset' , '100%' )
196- . attr ( 'stop-color' , primaryGradient [ 1 ] [ 0 ] )
197- . attr ( 'stop-opacity' , primaryGradient [ 1 ] [ 1 ] )
202+ . attr ( 'stop-color' , stopBottom [ 0 ] )
203+ . attr ( 'stop-opacity' , stopBottom [ 1 ] )
198204 return id
199205 }
200206
@@ -221,7 +227,8 @@ export const MainGraph = ({
221227 const drawLine = (
222228 dataset : GraphDatum [ ] ,
223229 isDefined : ( d : GraphDatum ) => boolean ,
224- yAccessor : ( d : GraphDatum , index : number ) => number
230+ yAccessor : ( d : GraphDatum , index : number ) => number ,
231+ className ?: string
225232 ) => {
226233 const line = d3
227234 . line < GraphDatum > ( )
@@ -232,32 +239,58 @@ export const MainGraph = ({
232239 svg
233240 . append ( 'path' )
234241 . attr ( 'fill' , 'none' )
235- . attr ( 'class' , pathClass )
242+ . attr ( 'class' , classNames ( sharedPathClass , className ) )
236243 . attr ( 'stroke-linejoin' , 'round' )
237244 . attr ( 'stroke-linecap' , 'round' )
238245 . datum ( dataset )
239246 . attr ( 'd' , line )
240247 }
241248
242- const drawDot = ( ) => {
249+ const drawDot = ( className : string ) => {
243250 const dot = svg . append ( 'g' ) . attr ( 'display' , 'none' )
244- dot . append ( 'circle' ) . attr ( 'r' , 2.5 ) . attr ( 'class' , dotClass )
251+ dot . append ( 'circle' ) . attr ( 'r' , 2.5 ) . attr ( 'class' , className )
245252 return dot
246253 }
247254
248- const gradientId = addGradient ( )
255+ const mainGradientId = addGradient ( {
256+ id : 'main' ,
257+ stopTop : primaryGradient [ 0 ] ,
258+ stopBottom : primaryGradient [ 1 ]
259+ } )
260+ const comparisonGradientId = addGradient ( {
261+ id : 'comparisonGradient' ,
262+ stopTop : secondaryGradient [ 0 ] ,
263+ stopBottom : secondaryGradient [ 1 ]
264+ } )
265+
266+ paintUnderLine (
267+ mainGradientId ,
268+ ( d ) => d . timeLabel !== null ,
269+ ( d ) => y ( d . value ! )
270+ )
271+
249272 paintUnderLine (
250- gradientId ,
251- ( { timeLabel } ) => timeLabel !== null ,
252- ( { value } ) => y ( value ! )
273+ comparisonGradientId ,
274+ ( d ) => d . comparisonTimeLabel !== null ,
275+ ( d ) => y ( d . comparisonValue ! )
253276 )
277+
254278 drawLine (
255279 remappedData ,
256280 ( d ) => d . timeLabel !== null ,
257- ( d ) => y ( d . value ! )
281+ ( d ) => y ( d . value ! ) ,
282+ mainPathClass
258283 )
259-
260- const dot = drawDot ( )
284+
285+ drawLine (
286+ remappedData ,
287+ ( d ) => d . comparisonTimeLabel !== null ,
288+ ( d ) => y ( d . comparisonValue ! ) ,
289+ comparisonPathClass
290+ )
291+
292+ const dot = drawDot ( mainDotClass )
293+ const comparisonDot = drawDot ( comparisonDotClass )
261294
262295 svg
263296 . on ( 'pointermove' , ( event ) => {
@@ -266,12 +299,24 @@ export const MainGraph = ({
266299 . bisector ( ( dataPoint : Point ) => dataPoint [ 0 ] )
267300 . center ( points , xPointer )
268301 const [ x , yValues ] = points [ closestIndexToPointer ]
269- dot
270- . attr ( 'transform' , `translate(${ x } ,${ yValues [ 0 ] } )` )
271- . attr ( 'display' , null )
302+ if ( yValues . yMain ) {
303+ dot
304+ . attr ( 'transform' , `translate(${ x } ,${ yValues . yMain } )` )
305+ . attr ( 'display' , null )
306+ } else {
307+ dot . attr ( 'display' , 'none' )
308+ }
309+ if ( yValues . yComparison ) {
310+ comparisonDot
311+ . attr ( 'transform' , `translate(${ x } ,${ yValues . yComparison } )` )
312+ . attr ( 'display' , null )
313+ } else {
314+ comparisonDot . attr ( 'display' , 'none' )
315+ }
272316 } )
273317 . on ( 'pointerleave' , ( ) => {
274318 dot . attr ( 'display' , 'none' )
319+ comparisonDot . attr ( 'display' , 'none' )
275320 } )
276321 . on ( 'touchstart' , ( event ) => event . preventDefault ( ) )
277322
@@ -391,9 +436,13 @@ const remapToGraphData = (
391436 // where to get the main result - the main graph is defined only
392437 data . meta . time_label_result_indices [ index ] ?? null ,
393438 // comparison label
394- data . meta . comparison_time_labels [ index ] ?? null ,
439+ ( data . meta . comparison_time_labels &&
440+ data . meta . comparison_time_labels [ index ] ) ??
441+ null ,
395442 // where to get the comparison result - the comparison graph is defined only where not null
396- data . meta . comparison_time_label_result_indices [ index ] ?? null
443+ ( data . meta . comparison_time_label_result_indices &&
444+ data . meta . comparison_time_label_result_indices [ index ] ) ??
445+ null
397446 ]
398447
399448 const mainResultDefined = typeof timeLabel === 'string'
@@ -493,7 +542,11 @@ const paletteByTheme = {
493542const tickLineClass =
494543 'stroke-gray-150 dark:stroke-gray-800/75 group-first:stroke-gray-300 dark:group-first:stroke-gray-700'
495544const tickClass = 'fill-gray-500 dark:fill-gray-400 text-xs'
496- // const dotClass = 'fill-[#6366f1]' // custom color like indigo-400
497- const dotClass = 'fill-indigo-400'
545+
546+ const mainDotClass = 'fill-indigo-500 dark:fill-indigo-400'
547+ const comparisonDotClass = 'fill-indigo-500/20 dark:fill-indigo-400/20'
548+
498549// const pathClass = 'stroke-[#6366f1] stroke-2 z-1' // custom color like indigo-400
499- const pathClass = 'stroke-indigo-500 dark:stroke-indigo-400 stroke-2 z-1'
550+ const sharedPathClass = 'stroke-2'
551+ const mainPathClass = 'stroke-indigo-500 dark:stroke-indigo-400 z-2'
552+ const comparisonPathClass = 'stroke-indigo-500/20 dark:stroke-indigo-400/20 z-1'
0 commit comments