Skip to contents

Make a click on the mascot open a menu in a Shiny app. Pass it as click to mascot() or update_mascot(). Clicking or tapping the mascot opens the menu, and clicking outside or pressing Escape closes it. While the menu is open, the mascot looks towards it, on the side where it actually opened (see "Where the menu opens" below). You provide the menu choices and handle the selected item in your Shiny server. For a single action, use mascot_action() instead.

Usage

mascot_popup_menu(
  items = NULL,
  ...,
  disabled = FALSE,
  direction = "auto",
  label = NULL,
  tooltip = TRUE
)

Arguments

items

A named character vector of plain-text labels. Names are unique, non-empty item IDs without whitespace (for example c(about = "About", help = "Help")). Defaults to NULL, which gives an empty helper; in update_mascot() it removes the click, so the mascot is a picture again. An empty vector is not accepted. Labels are always escaped. With items = NULL, setting disabled, direction, label or tooltip yourself is an error; use mascot_popup_menu() without options to remove the click.

...

Must be empty. Forces the arguments after it to be named.

disabled

A flag. TRUE keeps the menu button reachable by keyboard, but opens nothing and sends no input. Screen readers announce it as unavailable. FALSE (the default) allows the menu to open.

direction

Which side of the mascot the menu opens on: "auto" (the default), "top", "bottom", "left" or "right". "auto" picks the side with room (preferring above, then below, then the side towards the page centre). A chosen side is a preference: see "Where the menu opens" below.

label

The name screen readers announce for the mascot's menu button, such as "Help and settings": one string that is not blank. NULL (the default) uses the character's name, such as "Tessa, menu".

tooltip

A flag. TRUE (the default) shows the label in a tooltip when the pointer rests on the mascot or the keyboard focuses it, provided a label is set. FALSE hides the tooltip; screen readers still announce the label.

Value

A mascot_popup_menu object. Its fields are internal; pass it on rather than reading them.

Details

Bubble or menu? A menu lets the visitor ask the app to do something: attach it with click = and read the choice from input$<id>_menu. For a message the app sends to the visitor, use mascot_bubble() and update_mascot(id, bubble = ...).

Where the menu opens. The menu must fit in the space where it can show. For an inline mascot, that space is bounded by the nearest container that scrolls or clips (such as a bslib::card() body or a sidebar), not the whole window. For a floating mascot, it uses the window. When the chosen direction has no room there, the menu opens on the opposite side; when neither side has room, it opens wherever "auto" finds space. So direction = "bottom" on a mascot near the bottom of a card opens above it. Give the mascot room on the chosen side, or use "auto".

Keyboard navigation. Enter or Space on the focused mascot opens or closes the menu and moves the focus to its first item; holding the key down toggles it once. After opening it with a click, the down and up arrows move from the mascot to the first or last item. Use the arrow keys, Home and End to move between items, and press Escape to close the menu and return focus to the mascot.

Reading the selection in Shiny. Choosing an item sets input$<id>_menu to the item ID (for example, input$helper_menu for mascot("helper")). Choosing the same item twice triggers the input twice. Opening and closing the menu never set input$<id>_click; that input is only for mascot_action(). In a module, use ns("helper") in the UI and read input$helper_menu inside that module's server:

helper_ui <- function(id) {
  ns <- shiny::NS(id)
  mascot(ns("helper"), click = mascot_popup_menu(
    c(about = "About this app")))
}
helper_server <- function(id) {
  shiny::moduleServer(id, function(input, output, session) {
    shiny::observeEvent(input$helper_menu, {
      if (input$helper_menu == "about") {
        shiny::showModal(shiny::modalDialog("About this app"))
      }
    })
  })
}
shiny::shinyApp(
  shiny::fluidPage(helper_ui("demo")),
  function(input, output, session) helper_server("demo")
)

The menu opens next to the mascot, on the side you asked for. It is sized to fit the space on screen, and can change side or scroll when room is short. Action controls hide while the menu is open. Opening the menu closes any speech bubble that is showing. The restore button only restores a minimised mascot; it does not open the menu. On static pages the mascot stays an image. Run the Shiny example below to try the menu.

Examples

mascot_popup_menu(c(about = "About this app", help = "Help"))
#> mascot_popup_menu(c(about = "About this app", help = "Help"))
mascot_popup_menu(c(about = "About this app"), direction = "top")
#> mascot_popup_menu(c(about = "About this app"), direction = "top")
mascot_popup_menu(c(about = "About this app"), label = "Help and settings")
#> mascot_popup_menu(c(about = "About this app"), label = "Help and settings")
mascot_popup_menu(c(about = "About this app"), label = "Help and settings", tooltip = FALSE)
#> mascot_popup_menu(c(about = "About this app"), label = "Help and settings", tooltip = FALSE)
mascot_popup_menu()
#> mascot_popup_menu()
ui <- mascot("helper", click = mascot_popup_menu(c(about = "About this app")))
if (FALSE) { # interactive()
shiny::shinyApp(
  shiny::fluidPage(mascot("helper", click = mascot_popup_menu(
    c(about = "About this app")))),
  function(input, output, session) {
    shiny::observeEvent(input$helper_menu, {
      shiny::showModal(shiny::modalDialog(paste("Selected", input$helper_menu)))
    })
  }
)
}