2025-01-12 04:36:52 +08:00

239 lines
6.6 KiB
R

# Packages ---------------------------------------
library(shiny)
library(bslib)
library(htmltools)
pkgs_extra <- c("plotly", "colourpicker")
pkgs_yes <- vapply(pkgs_extra, rlang::is_installed, logical(1))
if (any(!pkgs_yes)) {
rlang::abort(paste0(
"The `build-a-box` app requires additional packages: ",
paste(pkgs_extra[!pkgs_yes], collapse = ", ")
))
}
library(plotly)
# Settings ---------------------------------------
ENABLE_THEMER <- identical(Sys.getenv("ENABLE_THEMER"), "true")
# Functions ---------------------------------------
layout_value_box_options <- function(ui_opts) {
layout_columns(
div(h4("Content", class = "border-bottom"), ui_opts$title_value),
div(h4("Theme", class = "border-bottom"), ui_opts$theme_opts),
div(h4("Showcase", class = "border-bottom"), ui_opts$showcase)
)
}
value_box_placeholder <- function(id) {
value_box(
id = id,
class = "placeholder-glow",
title = span(class = "placeholder col-7"),
value = span(class = "placeholder col-4"),
showcase = div(class = "placeholder bg-primary col-12", as_fill_item())
)
}
# Theme ---------------------------------------
theme_build_a_box <- bs_add_rules(
bs_theme(preset = "shiny"),
sass::sass_file("www/build-a-box.scss")
)
# UI ---------------------------------------
ui <- page_fixed(
title = "Build a Box | bslib",
theme = theme_build_a_box,
# Header ----
tags$header(
class = "mt-4 d-flex flex-row justify-content-between align-items-center",
h2("Build a Box"),
div(
class = "d-flex flex-row align-items-center gap-3",
popover(
bsicons::bs_icon(
"info-square-fill",
title = "About Value Boxes",
class = "icon-gradient"
),
title = "About Value Boxes",
HTML(commonmark::markdown_html(readLines("about-value-boxes.md")))
),
actionLink(
"show_code",
tooltip(icon("code"), "Show code"),
class = "nav-link text-blue",
style = css(width = "1em")
),
input_dark_mode(
id = "color_mode",
style = css("--text-1" = "var(--bs-blue)")
)
)
),
# Main ----
tags$main(
# Value Box Previews ----
div(
id = "preview",
class = "my-5",
layout_columns(
class = "value-box-previews",
div(
as_fill_carrier(),
value_box_placeholder("one-value_box_placeholder"),
ui_value_box_output("one")
),
div(
as_fill_carrier(),
value_box_placeholder("two-value_box_placeholder"),
ui_value_box_output("two")
),
div(
as_fill_carrier(),
value_box_placeholder("three-value_box_placeholder"),
ui_value_box_output("three")
)
)
),
# Settings Panel ----
navset_card_pill(
id = "settings",
title = span(
"Value box settings",
popover(
bsicons::bs_icon("question-square-fill"),
class = "ms-1 d-inline-block text-orange",
title = "Getting started",
shiny::markdown("
The Build-a-Box app includes three value boxes. You can customize
all three at once from the **All** tab.
Pick an overall theme, choose whether you'd like to **showcase** a plot or icon, and decide which **showcase layout** works best for your data.
Then, click directly on a value box or switch to the
<a id=\"switch_to_one\" href=\"#\" class=\"action-button\">One</a>,
<a id=\"switch_to_two\" href=\"#\" class=\"action-button\">Two</a>, or
<a id=\"switch_to_three\" href=\"#\" class=\"action-button\">Three</a>
tabs to customize its settings individually.
")
)
),
nav_panel(
"All",
value = "all",
ui_global_controls("all")
),
nav_panel(
"One",
value = "one-value_box",
layout_value_box_options(
ui_value_box_options("one")
)
),
nav_panel(
"Two",
value = "two-value_box",
layout_value_box_options(
ui_value_box_options("two")
)
),
nav_panel(
"Three",
value = "three-value_box",
layout_value_box_options(
ui_value_box_options("three")
)
)
)
),
# Footer ----
tags$footer(
class = "footer mt-auto py-3",
layout_columns(
class = "border-top pt-3 text-muted",
div(
class = "text-center text-sm-start",
"Made with",
a(href = "https://rstudio.github.io/bslib", "{bslib}"),
"and",
a(
href = "https://shiny.rstudio.com",
img(src = "shiny.png", width = "22px", alt = " "),
"Shiny"
)
),
div(
class = "text-center text-sm-end",
HTML('Proudly supported by <a href="https://posit.co/"><img src="https://www.rstudio.com/assets/img/posit-logo-fullcolor-TM.svg" class="img-fluid" alt="Posit" width="65"></a>')
)
)
),
# Extras ----
tags$script(src = "build-a-box.js")
)
# Server ---------------------------------------
server <- function(input, output, session) {
enable_themer <- reactive({
query <- shiny::getQueryString()
query_has_themer <- "themer" %in% names(query)
if (!length(query) || !query_has_themer) return(ENABLE_THEMER)
query$themer %in% c(1, "true", "") || ENABLE_THEMER
})
observeEvent(enable_themer(), {
# TODO: This only runs on app startup right now
req(enable_themer())
insertUI(
selector = "body",
where = "beforeEnd",
ui = tags$script("window.watchForThemer()")
)
bs_themer()
})
one <- module_value_box("one")
two <- module_value_box("two")
three <- module_value_box("three")
module_global_controls("all", one, two, three)
observeEvent(input$clicked_value_box, {
nav_select("settings", input$clicked_value_box)
})
observeEvent(input$switch_to_one, nav_select("settings", "one-value_box"))
observeEvent(input$switch_to_two, nav_select("settings", "two-value_box"))
observeEvent(input$switch_to_three, nav_select("settings", "three-value_box"))
observeEvent(input$settings, {
session$sendCustomMessage("active-value-box", input$settings)
})
observeEvent(input$show_code, {
layout_value_boxes <-
paste0(
"layout_columns(\n ",
rlang::expr_text(one$code()), ",\n ",
rlang::expr_text(two$code()), ",\n ",
rlang::expr_text(three$code()), "\n",
")"
)
code_modal(layout_value_boxes)
})
}
# BUILD A BOX -----------------------------------------
shinyApp(ui, server)