Skip to content

devOpifex/litter

Repository files navigation

Lit components for shiny, with a twist.

Inputs

Currently implemented inputs:

  • litActionButton
  • litActionLink
  • litCheckboxesInput
  • litColorInput
  • litDatalistInput
  • litFilterInput
  • litPasswordInput
  • litTextInput
  • litTextAreaInput
  • litRadioInput
  • litSwitchInput
  • litTextLabelInput
  • litTogglerInput
  • litRangeInput
  • litSelectInput
  • litSelectizeInput

Limitations

  • Only works with bslib's Bootstrap 5.
  • These inputs are not captured by Shiny's bookmarking session
  • These inputs are not captured by shinytest2

These limitations are unlikely to be lifted in the future.

Installation

You can install the development version of litter from GitHub with:

# install.packages("remotes")
remotes::install_github("devOpifex/litter")

Example

{litter} allows using multiple inputs with a single observer.

library(shiny)
library(litter)

ui <- fluidPage(
  theme = bslib::bs_theme(5L),
  litActionButton(
    name = "btn",
    "Button #1"
  ),
  litActionButton(
    name = "btn",
    "Button #2"
  )
)

server <- function(input, output, session){
  observeEvent(input$btn, {
    print(input$btn)
  })
}

shinyApp(ui, server)

Conventions

  • All input functions start in lit.
  • All inputs have a value (even if it can be a vector of multiple values)
  • All inputs can be updated with update_input
  • All inputs accept a callback argument (Javascript function)
  • Labels are not part of the input
  • All inputs accept a send_on_render argument
  • All inputs return data in the same format:
list(
  props = list(),
  id = "", # omitted if not set
  value = 1L
)

Properties

Whilst inputs that share name trigger the same input it can be difficult to distinguish between inputs.

For this you can pass any "prop" to the three dot construct .... For example, the application below will return the values set to myProp to input$btn.

library(shiny)
library(litter)

ui <- fluidPage(
  theme = bslib::bs_theme(5L),
  litActionButton(
    name = "btn",
    "Button #1",
    myProp = "A"
  ),
  litActionButton(
    name = "btn",
    "Button #2",
    myProp = "B"
  )
)

server <- function(input, output, session){
  observeEvent(input$btn, {
    print(input$btn)
  })
}

shinyApp(ui, server)

Callback

You can pass a JavaScript callback function, in which case it is called instead of sending the value to the shiny server.

library(shiny)
library(litter)

ui <- fluidPage(
  theme = bslib::bs_theme(5L),
  litActionButton(
    callback = "() => {alert('Hello!')}"
  )
)

server <- function(...){}

shinyApp(ui, server)

Update

All inputs can be updated with update_input.

library(shiny)
library(litter)

ui <- fluidPage(
  theme = bslib::bs_theme(5L),
  span("Type", tags$code("error"), "below"),
  litTextInput(
    name = "txt"
  )
)

server <- function(input, output){
  observeEvent(input$txt, {
    if(input$txt$value == ""){
      update_input(name = "txt", class = "")
      return()
    }

    if(input$txt$value == "error"){
      update_input(name = "txt", class = "is-invalid")
      return()
    }

    update_input(name = "txt", class = "is-valid")
  })
}

shinyApp(ui, server)

Generate

An example of generating inputs with {litter}.

library(shiny)
library(litter)

ui <- fluidPage(
  theme = bslib::bs_theme(5L),
  litRangeInput(
    "n",
    value = 5,
    min = 3,
    max = 10
  ),
  uiOutput("created"),
  verbatimTextOutput("result")
)

server <- function(input, output, session) {
  output$created <- renderUI({
    # we create n input
    lapply(seq(input$n$value), \(x){
      div(
        class = "mb-1",
        span("Input", x),
        # all inputs have the same name
        litTextInput(
          name = "text",
          n = x,
          send_on_render = FALSE
        )
      )
    })
  })

  output$result <- renderPrint({
    print(input$text)
  })
}

shinyApp(ui, server, options = list(port = 3000L))

Styling

All styling is taken from bslib's set theme as well as the class argument of all inputs.