1

I want to reproduce the default tooltip with some additions. However, when I try to do so, it left-aligns both serie and group names, and I am not sure how to fix it.

Some sample data and what I tried to do:

library(dplyr)
library(echarts4r)
library(htmlwidgets)

set.seed(10)
data <- data.frame(name = rep(c("Bob", "Michael"), each = 10), 
                   x = rep(0:9, 2), 
                   y = sample(0:5, 20, replace = TRUE),
                   add = sample(1:100, 20, replace = TRUE))

# Default tooltip
data %>% 
  group_by(name) %>% 
  e_chart(x = x) %>% 
  e_line(serie = y) %>% 
  e_tooltip(trigger = "axis")

# My attempt to recreate the formatting while adding new things
data %>% 
  group_by(name) %>% 
  e_chart(x = x) %>% 
  e_line(serie = y, bind = add) %>% 
  e_tooltip(trigger = "axis",
            formatter = JS("
              function(params){
              var colorSpan = color => `<span style='display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:` + color + `'></span>`;
              let rez = '<strong>Day ' + params[0].value[0] + '</strong>';
              
              params.forEach(item => {
                                       var xx  = '<br />' + colorSpan(item.color) + ' ' + item.seriesName + 
                                                 ' <strong>' + item.value[1] + '</strong> (' + item.name + ')'
                                       rez += xx;
                                     });
  
              return (rez)}"))

So, the default tooltip looks like this (values are right aligned):

values are right aligned

And my tooltip looks something like this, which is not very readable:

values are left aligned

I want to add things to the tooltip while keeping the formatting mostly untouched, but do not know how to do so with right alignment. I am not very familiar with echarts and JS in general though, so I have some troubles with it.

EDIT

So, thanks to @Russ we now have a workaround, which is not exactly what I was hoping to get, but a solution nonetheless. Does not look as pretty as the default tooltip, but for now we have what we have. Still, @Russ's solution does have some issues, so I post my edited version of his answer here. Not sure how to apply css to echarts' tooltip to remove margins caused by <pre> tag, but that does not matter too much right now

# Adding whitespaces after the name
data <- data %>%
  mutate(nameAlt = stringr::str_pad(name, max(nchar(name)), "right"))

data %>% 
  group_by(nameAlt) %>% 
  e_chart(x = x) %>% 
  e_line(serie = y, bind = add) %>% 
  e_tooltip(trigger = "axis",
            formatter = JS("
              function(params){
              var colorSpan = color => `<span style='display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:` + color + `'></span>`;
              let rez = '<strong>Day ' + params[0].value[0] + '</strong><pre>';
              
              params.forEach(item => {
                                       var xx  = colorSpan(item.color)  + item.seriesName + 
                                                 '<span style=`float:right;margin-left:20px;`><strong>' + item.value[1] + '</strong> (' + item.name + ')</span></br>'
                                       rez += xx;
                                     });
              rez += '</pre>'
  
              return (rez)}")) %>% 
  # Removing whitespaces from the legend
  e_legend(formatter = JS("function(name){return(name.trim())}"))

Result:

enter image description here

Darmist
  • 50
  • 9

1 Answers1

2

Overview

The way to solve this is with a combination of html tags with css styles that you can specify inside of the javascript. I tried playing around with it and came up a solution that does what you want at the cost of changing the font and probably not being best practice html/css. So my solution isn't a great one, but it does fix the spacing and keep most of your original formatting.

What I did was use R to add white space to the end of Bob's name so that his name has the same number of characters as Michael's name. So like "Bob" becomes "Bob ". I wrote the code so that it finds the longest name in the data and then adds a variable amount of white space to all the other names so that they're all the same length. It should work with an arbitrary number of names.

Then I added a <pre></pre> html tag around the rows in the tool tip. I put <span></span> tags around the numbers in each row and then defined a style that included a bit of a left side margin to separate Bob and Michael from the numbers.

Code

Add white space to names in R

# Find the longest name in the data
longest_name_length <- data$name %>%       # names column
                       unique()  %>%     # unique values in the name column
                       nchar() %>%       # number of characters in each unique name
                       max(na.rm = TRUE)  # longest name 


# add a new column with the length of the name
data$name_length <- data$name %>%     
                    nchar()

# add a characters to add column
data$characters_to_add <- longest_name_length - data$name_length


# add white space to the shorter characters so that all names are the same length

# add a variable amount of white space to each name using a loop
for (row in 1:nrow(data)){
  
 # Rep " " by the number of characters to add and then collapse 
# that vector into a single string using paste 
white_spaces <- rep(" ", times=data$characters_to_add[row]) %>%
                   paste(collapse = "") 
                   

# paste the name to the white spaces string
data$name[row] <- paste0(data$name[row], white_spaces)

}

update html tags in the code for the chart

data %>% 
  group_by(name) %>% 
  e_chart(x = x) %>% 
  e_line(serie = y, bind = add) %>% 
  e_tooltip(trigger = "axis",
            formatter = JS("
              function(params){
              var colorSpan = color => `<span style='display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:` + color + `'></span>`;
              let rez = '<strong>Day ' + params[0].value[0] + '</strong>';
              
              params.forEach(item => {
                                       var xx  = '<pre>' + colorSpan(item.color)  + item.seriesName + 
                                                 '<span style=`float:right;margin-left:15px;`> <strong>' + item.value[1] + '</strong> (' + item.name + ')</span></pre>'
                                       rez += xx;
                                     });
  
              return (rez)}"))

In this part var xx = '<br />' + I replaced a </br> tag with a <pre> and then in this part
'</strong> (' + item.name + ')' I added ended a </pre> to close the tag. Around this part
<strong>' + item.value[1] + '</strong> (' + item.name + ') I added tags to group these items into an inline html element and then inside I specified a left margin using css to add space between Bob/Michael and their numbers with this style=`float:right;margin-left:15px;`

Result

enter image description here

Other thoughts

This is a javascript question, but it's also an html/css question. It might be worth it to add those tags and hope for a less hacky solution than mine :D

Russ
  • 1,385
  • 5
  • 17
  • 1
    Thanks, I really appreciate your time! Your code looks messy and could be replalced by `str_pad()` (see my edit to the question) but the solution works, at least to some degree. It does not take into account the changes in legend that are caused by additional spaces and does not look very nice, but these issues can be addressed with some minor changes, and I think it looks good enough with them (see edit). I think I will eventually learn more about css and html and maybe figure out something more elegant, but it works for now. – Darmist Feb 05 '23 at 23:51