1
1
import { groups , sort , sum } from 'd3-array'
2
- import { stack , stackOffsetNone } from 'd3-shape'
3
2
import React , { ComponentPropsWithoutRef } from 'react'
4
3
5
4
import useGetLatest from '../hooks/useGetLatest'
@@ -26,13 +25,14 @@ import {
26
25
materializeStyles ,
27
26
getSeriesStatus ,
28
27
getDatumStatus ,
28
+ sortDatums ,
29
29
} from '../utils/Utils'
30
30
import buildAxisLinear from '../utils/buildAxis.linear'
31
31
import { ChartContextProvider } from '../utils/chartContext'
32
32
import AxisLinear from './AxisLinear'
33
33
// import Brush from './Brush'
34
34
import Cursors from './Cursors'
35
- import Tooltip from './Tooltip'
35
+ import Tooltip , { defaultTooltip } from './Tooltip'
36
36
import Voronoi from './Voronoi'
37
37
38
38
//
@@ -69,13 +69,12 @@ function defaultChartOptions<TDatum>(
69
69
initialHeight : options . initialHeight ?? 200 ,
70
70
getSeriesOrder :
71
71
options . getSeriesOrder ?? ( ( series : Series < TDatum > [ ] ) => series ) ,
72
- groupingMode : options . groupingMode ?? 'primary' ,
72
+ interactionMode : options . interactionMode ?? 'primary' ,
73
73
showVoronoi : options . showVoronoi ?? false ,
74
74
defaultColors : options . defaultColors ?? defaultColorScheme ,
75
75
useIntersectionObserver : options . useIntersectionObserver ?? true ,
76
76
intersectionObserverRootMargin :
77
77
options . intersectionObserverRootMargin ?? '1000px' ,
78
- tooltip : options . tooltip ?? true ,
79
78
primaryCursor : options . primaryCursor ?? true ,
80
79
secondaryCursor : options . secondaryCursor ?? true ,
81
80
padding : options . padding ?? defaultPadding ,
@@ -275,8 +274,30 @@ function ChartInner<TDatum>({
275
274
)
276
275
} , [ options . data , options . secondaryAxes , primaryAxisOptions ] )
277
276
277
+ // Resolve Tooltip Option
278
+ const tooltipOptions = React . useMemo ( ( ) => {
279
+ const tooltipOptions = defaultTooltip ( options ?. tooltip )
280
+ tooltipOptions . groupingMode =
281
+ tooltipOptions . groupingMode ??
282
+ ( ( ) => {
283
+ if ( options . interactionMode === 'closest' ) {
284
+ return 'single'
285
+ }
286
+ return 'primary'
287
+ } ) ( )
288
+
289
+ return tooltipOptions
290
+ } , [ options . interactionMode , options ?. tooltip ] )
291
+
292
+ options = {
293
+ ...options ,
294
+ tooltip : tooltipOptions ,
295
+ }
296
+
297
+ //
298
+
278
299
const svgRef = React . useRef < SVGSVGElement > ( null )
279
- const getOptions = useGetLatest ( options )
300
+ const getOptions = useGetLatest ( { ... options , tooltip : tooltipOptions } )
280
301
281
302
const axisDimensionsState = React . useState < AxisDimensions > ( {
282
303
left : { } ,
@@ -400,99 +421,92 @@ function ChartInner<TDatum>({
400
421
}
401
422
}
402
423
403
- if ( secondaryAxesOptions . some ( axisOptions => axisOptions . stacked ) ) {
404
- secondaryAxesOptions
405
- . filter ( d => d . stacked )
406
- . forEach ( secondaryAxis => {
407
- const axisSeries = series . filter (
408
- s => s . secondaryAxisId === secondaryAxis . id
409
- )
410
- const seriesIndices = Object . keys ( axisSeries )
411
- const stacker = stack ( )
412
- . keys ( seriesIndices )
413
- . value ( ( _ , seriesIndex , index ) => {
414
- const val = secondaryAxis . getValue (
415
- axisSeries [ Number ( seriesIndex ) ] . datums [ index ] . originalDatum
416
- )
417
-
418
- if ( typeof val === 'undefined' || val === null ) {
419
- return 0
420
- }
421
-
422
- return val
423
- } )
424
- . offset ( secondaryAxis . stackOffset ?? stackOffsetNone )
425
-
426
- const stacked = stacker (
427
- Array . from ( {
428
- length : axisSeries . sort (
429
- ( a , b ) => b . datums . length - a . datums . length
430
- ) [ 0 ] . datums . length ,
431
- } )
432
- )
433
-
434
- stacked . forEach ( ( s , sIndex ) => {
435
- s . forEach ( ( datum , i ) => {
436
- // @ts -ignore
437
- datum . data = axisSeries [ sIndex ] . datums [ i ]
438
-
439
- axisSeries [ sIndex ] . datums [ i ] . stackData =
440
- datum as unknown as StackDatum < TDatum >
441
- } )
442
- } )
443
- } )
444
- }
445
-
446
424
return series
447
- } , [ options . data , secondaryAxesOptions ] )
425
+ } , [ options . data ] )
426
+
427
+ let allDatums = React . useMemo ( ( ) => {
428
+ return series . map ( s => s . datums ) . flat ( 2 )
429
+ } , [ series ] )
448
430
449
431
const primaryAxis = React . useMemo ( ( ) => {
450
432
return buildAxisLinear < TDatum > (
451
433
true ,
452
434
primaryAxisOptions ,
453
435
series ,
436
+ allDatums ,
454
437
gridDimensions ,
455
438
width ,
456
439
height
457
440
)
458
- } , [ gridDimensions , height , primaryAxisOptions , series , width ] )
441
+ } , [ allDatums , gridDimensions , height , primaryAxisOptions , series , width ] )
459
442
460
443
const secondaryAxes = React . useMemo ( ( ) => {
461
444
return secondaryAxesOptions . map ( secondaryAxis => {
462
445
return buildAxisLinear < TDatum > (
463
446
false ,
464
447
secondaryAxis ,
465
448
series ,
449
+ allDatums ,
466
450
gridDimensions ,
467
451
width ,
468
452
height
469
453
)
470
454
} )
471
- } , [ gridDimensions , height , secondaryAxesOptions , series , width ] )
455
+ } , [ allDatums , gridDimensions , height , secondaryAxesOptions , series , width ] )
472
456
473
- const groupedDatums = React . useMemo ( ( ) => {
474
- const groupedDatums = new Map < any , Datum < TDatum > [ ] > ( )
457
+ const [ datumsByInteractionGroup , datumsByTooltipGroup ] = React . useMemo ( ( ) => {
458
+ const datumsByInteractionGroup = new Map < any , Datum < TDatum > [ ] > ( )
459
+ const datumsByTooltipGroup = new Map < any , Datum < TDatum > [ ] > ( )
475
460
476
- const allDatums = series . map ( s => s . datums ) . flat ( 2 )
461
+ let getInteractionKey = ( datum : Datum < TDatum > ) => `${ datum . primaryValue } `
462
+ let getTooltipKey = ( datum : Datum < TDatum > ) => `${ datum . primaryValue } `
463
+
464
+ if ( options . interactionMode === 'closest' ) {
465
+ getInteractionKey = datum =>
466
+ `${ datum . primaryValue } _${ datum . secondaryValue } `
467
+ }
468
+
469
+ if ( tooltipOptions . groupingMode === 'single' ) {
470
+ getTooltipKey = datum => `${ datum . primaryValue } _${ datum . secondaryValue } `
471
+ } else if ( tooltipOptions . groupingMode === 'secondary' ) {
472
+ getTooltipKey = datum => `${ datum . secondaryValue } `
473
+ } else if ( tooltipOptions . groupingMode === 'series' ) {
474
+ getTooltipKey = datum => `${ datum . seriesIndex } `
475
+ }
477
476
478
477
allDatums . forEach ( datum => {
479
- const primaryValue = `${ primaryAxis . getValue ( datum . originalDatum ) } `
478
+ const interactionKey = ( getInteractionKey as Function ) ( datum )
479
+ const tooltipKey = ( getTooltipKey as Function ) ( datum )
480
+
481
+ if ( ! datumsByInteractionGroup . has ( interactionKey ) ) {
482
+ datumsByInteractionGroup . set ( interactionKey , [ ] )
483
+ }
480
484
481
- if ( ! groupedDatums . has ( primaryValue ) ) {
482
- groupedDatums . set ( primaryValue , [ ] )
485
+ if ( ! datumsByTooltipGroup . has ( tooltipKey ) ) {
486
+ datumsByTooltipGroup . set ( tooltipKey , [ ] )
483
487
}
484
488
485
- groupedDatums . get ( primaryValue ) ! . push ( datum )
489
+ datumsByInteractionGroup . get ( interactionKey ) ! . push ( datum )
490
+ datumsByTooltipGroup . get ( tooltipKey ) ! . push ( datum )
486
491
} )
487
492
488
- allDatums . forEach ( datum => {
489
- const primaryValue = `${ primaryAxis . getValue ( datum . originalDatum ) } `
493
+ datumsByInteractionGroup . forEach ( ( value , key ) => {
494
+ datumsByInteractionGroup . set ( key , sortDatums ( value , secondaryAxes ) )
495
+ } )
490
496
491
- datum . group = groupedDatums . get ( primaryValue )
497
+ datumsByTooltipGroup . forEach ( ( value , key ) => {
498
+ datumsByTooltipGroup . set ( key , sortDatums ( value , secondaryAxes ) )
499
+ } )
500
+
501
+ allDatums . forEach ( datum => {
502
+ const interactionKey = ( getInteractionKey as Function ) ( datum )
503
+ const tooltipKey = ( getTooltipKey as Function ) ( datum )
504
+ datum . interactiveGroup = datumsByInteractionGroup . get ( interactionKey )
505
+ datum . tooltipGroup = datumsByTooltipGroup . get ( tooltipKey )
492
506
} )
493
507
494
- return groupedDatums
495
- } , [ primaryAxis , series ] )
508
+ return [ datumsByInteractionGroup , datumsByTooltipGroup ]
509
+ } , [ allDatums , options . interactionMode , tooltipOptions . groupingMode ] )
496
510
497
511
const getSeriesStatusStyle = React . useCallback (
498
512
( series : Series < TDatum > , focusedDatum : Datum < TDatum > | null ) => {
@@ -556,7 +570,8 @@ function ChartInner<TDatum>({
556
570
secondaryAxes,
557
571
series,
558
572
orderedSeries,
559
- groupedDatums,
573
+ datumsByInteractionGroup,
574
+ datumsByTooltipGroup,
560
575
width,
561
576
height,
562
577
getSeriesStatusStyle,
0 commit comments