@@ -38,6 +38,9 @@ export class Tooltip extends Mark {
38
38
let indexes = this . indexesBySvg . get ( svg ) ;
39
39
if ( indexes ) return void indexes . push ( index ) ;
40
40
this . indexesBySvg . set ( svg , ( indexes = [ index ] ) ) ;
41
+ const r = 8 ; // padding
42
+ const dx = 0 ; // offsetLeft
43
+ const dy = 12 ; // offsetTop
41
44
const dot = select ( svg )
42
45
. on ( "pointermove" , ( event ) => {
43
46
let i , xi , yi , fxi , fyi ;
@@ -63,26 +66,46 @@ export class Tooltip extends Mark {
63
66
dot . attr ( "display" , "none" ) ;
64
67
} else {
65
68
dot . attr ( "display" , "inline" ) ;
66
- dot . attr ( "transform" , `translate(${ xi } ,${ yi } )` ) ;
69
+ dot . attr ( "transform" , `translate(${ Math . round ( xi ) } ,${ Math . round ( yi ) } )` ) ;
67
70
const text = [ ] ;
68
71
for ( const key in channels ) {
69
72
const channel = channels [ key ] ;
70
73
const label = scales [ channel . scale ] ?. label ?? key ;
71
- text . push ( ` ${ label } = ${ formatDefault ( channel . value [ i ] ) } ` ) ;
74
+ text . push ( [ label , formatDefault ( channel . value [ i ] ) ] ) ;
72
75
}
73
- if ( fxv != null ) text . push ( `${ fx . label ?? "fx" } = ${ formatFx ( fxi ) } ` ) ;
74
- if ( fyv != null ) text . push ( `${ fy . label ?? "fy" } = ${ formatFy ( fyi ) } ` ) ;
75
- title . text ( text . join ( "\n" ) ) ;
76
+ if ( fxv != null ) text . push ( [ fx . label ?? "fx" , formatFx ( fxi ) ] ) ;
77
+ if ( fyv != null ) text . push ( [ fy . label ?? "fy" , formatFy ( fyi ) ] ) ;
78
+ content
79
+ . selectChildren ( )
80
+ . data ( text )
81
+ . join ( "tspan" )
82
+ . attr ( "x" , 0 )
83
+ . attr ( "y" , ( d , i ) => `${ i + 0.9 } em` )
84
+ . selectChildren ( )
85
+ . data ( ( d ) => d )
86
+ . join ( "tspan" )
87
+ . attr ( "font-weight" , ( d , i ) => ( i ? "bold" : null ) )
88
+ . text ( ( d , i ) => ( i ? ` ${ d } ` : String ( d ) ) ) ;
89
+ // TODO Limit the maximum width and clip.
90
+ const { width, height} = content . node ( ) . getBBox ( ) ;
91
+ const w = width + r * 2 ;
92
+ const h = height + r * 2 ;
93
+ path . attr ( "d" , `M${ dx } ,${ dy } v${ - dy } l${ dy } ,${ dy } h${ w - dy } v${ h } h${ - w } z` ) ;
76
94
}
77
95
} )
78
96
. on ( "pointerdown pointerleave" , ( ) => dot . attr ( "display" , "none" ) )
79
97
. append ( "g" )
80
- . attr ( "display" , "none" )
81
- . attr ( "pointer-events" , "all" )
82
- . attr ( "fill" , "none" )
83
- . call ( ( g ) => g . append ( "circle" ) . attr ( "r" , maxRadius ) . attr ( "fill" , "none" ) )
84
- . call ( ( g ) => g . append ( "circle" ) . attr ( "r" , 4.5 ) . attr ( "stroke" , "red" ) . attr ( "stroke-width" , 1.5 ) ) ;
85
- const title = dot . append ( "title" ) ;
98
+ . attr ( "display" , "none" ) ;
99
+ const path = dot
100
+ . append ( "path" )
101
+ . attr ( "fill" , "white" )
102
+ . attr ( "stroke" , "black" )
103
+ . on ( "pointerdown pointermove" , ( event ) => event . stopPropagation ( ) ) ;
104
+ const content = dot
105
+ . append ( "text" )
106
+ . attr ( "transform" , `translate(${ dx + r } ,${ dy + r } )` )
107
+ . attr ( "text-anchor" , "start" )
108
+ . on ( "pointerdown pointermove" , ( event ) => event . stopPropagation ( ) ) ;
86
109
return null ;
87
110
}
88
111
}
0 commit comments