@@ -114,40 +114,13 @@ export class Tip extends Mark {
114114 const widthof = monospace ? monospaceWidth : defaultWidth ;
115115 const ee = widthof ( ellipsis ) ;
116116
117- // We borrow the scale’s tick format for facet channels; this is safe for
118- // ordinal scales (but not continuous scales where the display value may
119- // need higher precision), and generally better than the default format.
120- const formatFx = fx && inferTickFormat ( fx ) ;
121- const formatFy = fy && inferTickFormat ( fy ) ;
122-
123- function * format ( sources , i ) {
124- if ( "title" in sources ) {
125- const text = sources . title . value [ i ] ;
126- for ( const line of mark . splitLines ( formatDefault ( text ) ) ) {
127- yield { name : "" , value : mark . clipLine ( line ) } ;
128- }
129- return ;
130- }
131- for ( const key in sources ) {
132- if ( key === "x1" && "x2" in sources ) continue ;
133- if ( key === "y1" && "y2" in sources ) continue ;
134- const channel = sources [ key ] ;
135- const value = channel . value [ i ] ;
136- if ( ! defined ( value ) && channel . scale == null ) continue ;
137- if ( key === "x2" && "x1" in sources ) {
138- yield { name : formatPairLabel ( scales , sources . x1 , channel , "x" ) , value : formatPair ( sources . x1 , channel , i ) } ;
139- } else if ( key === "y2" && "y1" in sources ) {
140- yield { name : formatPairLabel ( scales , sources . y1 , channel , "y" ) , value : formatPair ( sources . y1 , channel , i ) } ;
141- } else {
142- const scale = channel . scale ;
143- const line = { name : formatLabel ( scales , channel , key ) , value : formatDefault ( value ) } ;
144- if ( scale === "color" || scale === "opacity" ) line [ scale ] = values [ key ] [ i ] ;
145- yield line ;
146- }
147- }
148- if ( index . fi != null && fx ) yield { name : String ( fx . label ?? "fx" ) , value : formatFx ( index . fx ) } ;
149- if ( index . fi != null && fy ) yield { name : String ( fy . label ?? "fy" ) , value : formatFy ( index . fy ) } ;
150- }
117+ // Determine the appropriate formatter.
118+ const format =
119+ "title" in sources // if there is a title channel
120+ ? formatTitle // display the title as-is
121+ : index . fi == null // if this mark is not faceted
122+ ? formatChannels // display name-value pairs for channels
123+ : formatFacetedChannels ( index , scales ) ; // same, plus facets
151124
152125 // We don’t call applyChannelStyles because we only use the channels to
153126 // derive the content of the tip, not its aesthetics.
@@ -173,8 +146,8 @@ export class Tip extends Mark {
173146 this . setAttribute ( "stroke" , "none" ) ;
174147 // iteratively render each channel value
175148 const names = new Set ( ) ;
176- for ( const line of format ( sources , i ) ) {
177- const name = line . name ;
149+ for ( const line of format . call ( mark , i , sources , scales , values ) ) {
150+ const { name = "" } = line ;
178151 if ( name && names . has ( name ) ) continue ;
179152 else names . add ( name ) ;
180153 renderLine ( that , line ) ;
@@ -188,7 +161,7 @@ export class Tip extends Mark {
188161 // just the initial layout of the text; in postrender we will compute the
189162 // exact text metrics and translate the text as needed once we know the
190163 // tip’s orientation (anchor).
191- function renderLine ( selection , { name, value, color, opacity} ) {
164+ function renderLine ( selection , { name = "" , value = "" , color, opacity} ) {
192165 const swatch = color != null || opacity != null ;
193166 let title ;
194167 let w = lineWidth * 100 ;
@@ -328,6 +301,47 @@ function getSources({channels}) {
328301 return sources ;
329302}
330303
304+ function * formatTitle ( i , { title} ) {
305+ const text = title . value [ i ] ;
306+ for ( const line of this . splitLines ( formatDefault ( text ) ) ) {
307+ yield { name : "" , value : this . clipLine ( line ) } ;
308+ }
309+ }
310+
311+ function * formatChannels ( i , channels , scales , values ) {
312+ for ( const key in channels ) {
313+ if ( key === "x1" && "x2" in channels ) continue ;
314+ if ( key === "y1" && "y2" in channels ) continue ;
315+ const channel = channels [ key ] ;
316+ const value = channel . value [ i ] ;
317+ if ( ! defined ( value ) && channel . scale == null ) continue ;
318+ if ( key === "x2" && "x1" in channels ) {
319+ yield { name : formatPairLabel ( scales , channels . x1 , channel , "x" ) , value : formatPair ( channels . x1 , channel , i ) } ;
320+ } else if ( key === "y2" && "y1" in channels ) {
321+ yield { name : formatPairLabel ( scales , channels . y1 , channel , "y" ) , value : formatPair ( channels . y1 , channel , i ) } ;
322+ } else {
323+ const scale = channel . scale ;
324+ const line = { name : formatLabel ( scales , channel , key ) , value : formatDefault ( value ) } ;
325+ if ( scale === "color" || scale === "opacity" ) line [ scale ] = values [ key ] [ i ] ;
326+ yield line ;
327+ }
328+ }
329+ }
330+
331+ function formatFacetedChannels ( index , scales ) {
332+ const { fx, fy} = scales ;
333+ // We borrow the scale’s tick format for facet channels; this is safe for
334+ // ordinal scales (but not continuous scales where the display value may need
335+ // higher precision), and generally better than the default format.
336+ const formatFx = fx && inferTickFormat ( fx ) ;
337+ const formatFy = fy && inferTickFormat ( fy ) ;
338+ return function * ( i , channels , scales , values ) {
339+ yield * formatChannels ( i , channels , scales , values ) ;
340+ if ( fx ) yield { name : String ( fx . label ?? "fx" ) , value : formatFx ( index . fx ) } ;
341+ if ( fy ) yield { name : String ( fy . label ?? "fy" ) , value : formatFy ( index . fy ) } ;
342+ } ;
343+ }
344+
331345function formatPair ( c1 , c2 , i ) {
332346 return c2 . hint ?. length // e.g., stackY’s y1 and y2
333347 ? `${ formatDefault ( c2 . value [ i ] - c1 . value [ i ] ) } `
0 commit comments