@@ -101,7 +101,7 @@ export function CrossfilterHistogramPlot(
101
101
g . attr ( "class" , "gray" ) ;
102
102
g . selectAll ( ".tick text" )
103
103
. attr ( "text-anchor" , ( _ , i ) => [ "start" , "end" , "start" ] [ i ] )
104
- . attr ( "dx" , ( _ , i ) => i === 0 ? "- 0.25em" : " 0.25em") ;
104
+ . attr ( "dx" , ( _ , i ) => [ "-0.25em" , " 0.25em", "- 0.25em"] [ i ] ) ;
105
105
} ) ;
106
106
107
107
const hoveredTickGroup = axes . node ( ) ?. querySelectorAll ( ".tick" ) [ 2 ] ;
@@ -118,27 +118,30 @@ export function CrossfilterHistogramPlot(
118
118
const fmt = type === "number"
119
119
? d3 . format ( ".3s" )
120
120
: tickFormatterForBins ( type , bins ) ;
121
- // `hovered` signal gets updated in mousemove event
121
+
122
+ let [ xmin , xmax ] = x . domain ( ) ;
122
123
effect ( ( ) => {
123
124
hoveredTick
124
- . attr ( "transform" , `translate(${ x ( hovered . value || 0 ) } ,0)` )
125
+ . attr ( "transform" , `translate(${ x ( hovered . value ?? xmin ) } ,0)` )
125
126
. attr ( "visibility" , hovered . value ? "visible" : "hidden" ) ;
126
127
127
128
hoveredTick
128
129
. selectAll ( "text" )
129
- . text ( `${ fmt ( hovered . value || 0 ) } ` )
130
+ . text ( `${ fmt ( hovered . value ?? xmin ) } ` )
130
131
. attr ( "visibility" , hovered . value ? "visible" : "hidden" ) ;
131
132
132
- const hoveredTickText = hoveredTick . select ( "text" )
133
- . node ( ) as SVGGraphicsElement ;
133
+ const hoveredTickText = hoveredTick
134
+ . select ( "text" )
135
+ . node ( ) as SVGTextElement ;
134
136
const bbox = hoveredTickText . getBBox ( ) ;
137
+ const cond = ( x ( hovered . value ?? xmin ) + bbox . width ) > x ( xmax ) ;
138
+
139
+ hoveredTickText . setAttribute ( "text-anchor" , cond ? "end" : "start" ) ;
140
+ hoveredTickText . setAttribute ( "dx" , cond ? "-0.25em" : "0.25em" ) ;
135
141
136
142
hoverLabelBackground
137
143
. attr ( "visibility" , hovered . value ? "visible" : "hidden" )
138
- . attr (
139
- "transform" ,
140
- `translate(-2.5,0)` ,
141
- )
144
+ . attr ( "transform" , `translate(${ ( cond ? - bbox . width : 0 ) - 2.5 } , 2.5)` )
142
145
. attr ( "width" , bbox . width + 5 )
143
146
. attr ( "height" , bbox . height + 5 ) ;
144
147
} ) ;
@@ -232,7 +235,7 @@ export function CrossfilterHistogramPlot(
232
235
233
236
node . addEventListener ( "mousemove" , ( event ) => {
234
237
const relativeX = event . clientX - node . getBoundingClientRect ( ) . left ;
235
- hovered . value = x . invert ( relativeX ) ;
238
+ hovered . value = clamp ( x . invert ( relativeX ) , xmin , xmax ) ;
236
239
} ) ;
237
240
node . addEventListener ( "mouseleave" , ( ) => {
238
241
hovered . value = undefined ;
@@ -259,3 +262,12 @@ export function CrossfilterHistogramPlot(
259
262
} ,
260
263
} ) ;
261
264
}
265
+
266
+ function clamp (
267
+ value : number | Date ,
268
+ min : number | Date ,
269
+ max : number | Date ,
270
+ ) : number {
271
+ // @ts -expect-error - value is either number or Date
272
+ return Math . max ( min , Math . min ( max , value ) ) ;
273
+ }
0 commit comments