javascriptrshinynouislidershinywidgets

How to format the tick labels of the noUiSlider with SI symbols?


Here is how my noUiSlider looks like:

enter image description here

I would like to format the tick labels as: 1000 -> 1K, 900000 -> 900K, 5000000 -> 5M, etc; i.e. abbreviating the number with the appropriate SI symbol.

library(shiny)
library(shinyWidgets)

ui <- fluidPage(
  
  tags$br(),
  
  noUiSliderInput(
    inputId = "noui", 
    label = "Slider vertical:",
    value =  50,
    orientation = "vertical",
    range = list("min" = list(0),
                 "25%" = list(1000, 500),
                 "50%" = list(10000, 50000),
                 "75%" = list(4000000, 500000),
                 "max" = list(10000000)),
    pips = list(
      mode = "values",
      values = list(0, 500, 1000, 900000, 5000000, 10000000),
      density = 4
    ),
    format = wNumbFormat(decimals = 0),
    width = "300px", height = "300px"
  ),
  verbatimTextOutput(outputId = "res")
  
)

server <- function(input, output, session) {
  
  output$res <- renderPrint(input$noui)
  
}

shinyApp(ui, server)

Solution

  • We need to set the pips option with JavaScript, to be able to pass a function to the format suboption:

    library(shiny)
    library(shinyWidgets)
    library(shinyjs)
    
    abbreviateNumber <- '
    const SI_SYMBOLS = ["", "k", "M", "G", "T", "P", "E"];
    
    const abbreviateNumber = (number, minDigits, maxDigits) => {
        if (number === 0) return number;
    
        // determines SI symbol
        const tier = Math.floor(Math.log10(Math.abs(number)) / 3);
    
        // get suffix and determine scale
        const suffix = SI_SYMBOLS[tier];
        const scale = 10 ** (tier * 3);
    
        // scale the number
        const scaled = number / scale;
    
        // format number and add suffix
        return scaled.toLocaleString(undefined, {
            minimumFractionDigits: minDigits,
            maximumFractionDigits: maxDigits,
        }) + suffix;
    };' # https://stackoverflow.com/a/65324090/1100107
    
    js <- paste(
      "var slider = document.getElementById('noui').noUiSlider;",
      "slider.pips({",
      "  mode: 'values',",
      "  values: [0, 500, 1000, 900000, 5000000, 10000000],",
      "  density: 4,", 
      "  format: {", 
      "    to: function(x) {return abbreviateNumber(x);}",
      "  }",
      "});",
      sep = "\n"
    )
    
    
    ui <- fluidPage(
      
      useShinyjs(),
      
      tags$head(
        tags$script(HTML(abbreviateNumber))
      ),
      
      tags$br(),
      
      noUiSliderInput(
        inputId = "noui", 
        label = "Slider vertical:",
        value =  50,
        orientation = "vertical",
        range = list("min" = list(0),
                     "25%" = list(1000, 500),
                     "50%" = list(10000, 50000),
                     "75%" = list(4000000, 500000),
                     "max" = list(10000000)),
        format = wNumbFormat(decimals = 0),
        width = "300px", height = "300px"
      ),
      
      verbatimTextOutput(outputId = "res")
      
    )
    
    server <- function(input, output, session) {
      
      observeEvent(input$noui, {
        runjs(js)
      }, once = TRUE)
      
      output$res <- renderPrint(input$noui)
      
    }
    
    shinyApp(ui, server)
    

    enter image description here