diff --git a/NAMESPACE b/NAMESPACE index f0a630351..f957f5bfe 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -163,7 +163,10 @@ export(toggle_sidebar) export(toggle_switch) export(toggle_tooltip) export(toolbar) +export(toolbar_divider) export(toolbar_input_button) +export(toolbar_input_select) +export(toolbar_input_switch) export(tooltip) export(update_popover) export(update_submit_textarea) diff --git a/NEWS.md b/NEWS.md index 28bf37c1f..df5da422f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,9 @@ * Added a new `toolbar()` component for creating Bootstrap toolbars that can contain buttons, text, and other elements. (#1247) * Added `toolbar_input_button()` for easily creating buttons to include in a `toolbar()`. (#1248) + * Added `toolbar_input_switch()` for easily creating switch inputs to include in a `toolbar()`. (#1257) + * Added `toolbar_input_select()`, a select input designed for use within a `toolbar()`. (#1249) + * Added `toolbar_divider()` for adding visual dividers with customizable width and spacing between toolbar elements. (#1259) ## Improvements and bug fixes diff --git a/R/sysdata.rda b/R/sysdata.rda index b3582f1eb..f8361b99b 100644 Binary files a/R/sysdata.rda and b/R/sysdata.rda differ diff --git a/R/toolbar.R b/R/toolbar.R index 184e1cff3..5d6207f4f 100644 --- a/R/toolbar.R +++ b/R/toolbar.R @@ -55,19 +55,20 @@ toolbar <- function( #' ) #' #' @param id The input ID. -#' @param icon An icon to display in the button. If provided without -#' `show_label = TRUE`, only the icon will be visible. -#' @param label The button label. Used as button text when `show_label = TRUE`, -#' or as an accessibility label when hidden. Also used as the default -#' tooltip text when `tooltip = TRUE`. -#' @param show_label Whether to show the label text in the button. If `FALSE` -#' (the default), only the icon is shown (if provided). If `TRUE`, the label -#' text is shown alongside the icon. -#' @param tooltip Tooltip text to display when hovering over the button. Can be: -#' * `TRUE` (default when `show_label = FALSE`) - shows a tooltip with the `label` text +#' @param icon An icon. If provided without `show_label = TRUE`, only the icon +#' will be visible. +#' @param label The input label. By default, `label` is not shown but is used by +#' `tooltip`. Set `show_label = TRUE` to show the label (see `tooltip` for +#' details on how this affects the tooltip behavior). +#' @param show_label Whether to show the label text. If `FALSE` (the default), +#' only the icon is shown (if provided). If `TRUE`, the label text is shown +#' alongside the icon. +#' @param tooltip Tooltip text to display when hovering over the input. Can be: +#' * `TRUE` (default when `show_label = FALSE`) - shows a tooltip with the +#' `label` text #' * `FALSE` (default when `show_label = TRUE`) - no tooltip -#' * A character string - shows a tooltip with custom text -#' Defaults to `!show_label`. +#' * A character string - shows a tooltip with custom text Defaults to +#' `!show_label`. #' @param ... Additional attributes to pass to the button. #' @param disabled If `TRUE`, the button will not be clickable. Use #' [shiny::updateActionButton()] to dynamically enable/disable the button. @@ -111,15 +112,29 @@ toolbar_input_button <- function( label_id <- paste0("btn-label-", p_randomInt(1000, 10000)) + # We hide the label visually if `!show_label` but keep the label field for + # use with `aria-labelledby`. This ensures that ARIA will always use the + # label text. We found that screen readers will read out the icon's `aria- + # label` even if it is a descendent of an element with `aria-hidden=true`. + label_elem <- span( + id = label_id, + class = "bslib-toolbar-label", + hidden = if (!show_label) NA else NULL, + label + ) + + # And we wrap the icon to ensure that it is always treated as decorative + icon_elem <- span( + class = "bslib-toolbar-icon", + `aria-hidden` = "true", + style = "pointer-events: none", + icon, + ) + button <- shiny::actionButton( id, - # We hide the label visually if `!show_label` but keep the label field for - # use with `aria-labelledby`. This ensures that ARIA will always use the - # label text. We found that screen readers will read out the icon's `aria- - # label` even if it is a descendent of an element with `aria-hidden=true`. - label = span(id = label_id, hidden = if (!show_label) NA else NULL, label), - # And we wrap the icon to ensure that it is always treated as decorative - icon = span(icon, `aria-hidden` = "true", style = "pointer-events: none"), + label = label_elem, + icon = icon_elem, disabled = disabled, class = "bslib-toolbar-input-button btn-sm", class = if (!border) "border-0" else "border-1", @@ -143,3 +158,263 @@ toolbar_input_button <- function( button } + +#' Toolbar Input Select +#' +#' @description +#' Create a select list input control that can be used to choose a single +#' item from a list of values, suitable for use within a [toolbar()]. +#' +#' @examplesIf rlang::is_interactive() +#' toolbar( +#' align = "right", +#' toolbar_input_select( +#' id = "select", +#' label = "Choose option", +#' choices = c("Option 1", "Option 2", "Option 3"), +#' selected = "Option 2" +#' ) +#' ) +#' +#' # With custom tooltip +#' toolbar( +#' align = "right", +#' toolbar_input_select( +#' id = "select", +#' label = "Choose option", +#' choices = c("Option 1", "Option 2", "Option 3"), +#' tooltip = "Select your preferred option from the list" +#' ) +#' ) +#' +#' # With icon and tooltip +#' toolbar( +#' align = "right", +#' toolbar_input_select( +#' id = "select", +#' label = "Choose option", +#' choices = c("Option 1", "Option 2", "Option 3"), +#' icon = shiny::icon("filter"), +#' tooltip = "Filter the data" +#' ) +#' ) +#' +#' @param selected The initially selected value. If not provided, the first +#' choice will be selected by default. +#' @param ... Additional named arguments passed as attributes to the outer +#' container div. +#' @inheritParams toolbar_input_button +#' @inheritParams shiny::selectInput +#' +#' @return Returns a select input control suitable for use in a toolbar. +#' +#' @family Toolbar components +#' @export +toolbar_input_select <- function( + id, + label, + choices, + ..., + selected = NULL, + icon = NULL, + show_label = FALSE, + tooltip = !show_label +) { + # Validate that ... contains only named arguments + dots <- separate_arguments(...) + if (length(dots$children) > 0) { + rlang::abort("All arguments in `...` must be named.") + } + + # Validate that label is a non-empty string + # TODO: Use `check_string()` + if (!is.character(label) || length(label) != 1 || !nzchar(trimws(label))) { + rlang::abort("`label` must be a non-empty string.") + } + + # Restore input for bookmarking + selected <- shiny::restoreInput(id = id, default = selected) + + # Set selected to the first choice if no default or restored value + firstChoice <- asNamespace("shiny")[["firstChoice"]] + if (is.null(selected)) { + selected <- firstChoice(choices) + } + + # Normalize choices using util function imported from Shiny + choicesWithNames <- asNamespace("shiny")[["choicesWithNames"]] + choices <- choicesWithNames(choices) + + select_tag <- tags$select( + id = id, + class = "form-select form-select-sm", + selectOptions(choices, selected, inputId = id) + ) + + # Add optional icon before the select + icon_elem <- span( + icon, + style = "pointer-events: none", + class = "bslib-toolbar-icon", + `aria-hidden` = "true", + `role` = "none", + tabindex = "-1" + ) + + label_elem <- tags$label( + # shiny::selectInput() append `-label` to id for the label `for` attribute + id = sprintf("%s-label", id), + class = "control-label", + `for` = id, + icon_elem, + tags$span( + class = "bslib-toolbar-label", + class = if (!show_label) "visually-hidden", + label + ) + ) + + if (isTRUE(tooltip)) { + # If tooltip is literally TRUE, use the label as the tooltip text, but hide + # it from screen readers since it repeats the label content. + tooltip <- tags$span(label, `aria-hidden` = "true") + } + if (isFALSE(tooltip)) { + tooltip <- NULL + } + if (!is.null(tooltip)) { + select_tag <- bslib::tooltip( + select_tag, + tooltip, + placement = "bottom" + ) + } + + div( + class = "bslib-toolbar-input-select shiny-input-container", + !!!dots$attribs, + label_elem, + select_tag + ) +} + +# This function was copied from shiny's `input-select.R` with a small change +selectOptions <- function( + choices, + selected = NULL, + inputId, + perfWarning = FALSE +) { + if (length(choices) >= 1000) { + # CHANGED: This warning differs to remove mention of sever-side options + rlang::warn( + sprintf( + paste0( + "Select input `%s` contains a large number of options; ", + "this may cause performance issues." + ), + inputId + ) + ) + } + + html <- mapply( + choices, + names(choices), + FUN = function(choice, label) { + if (is.list(choice)) { + # If sub-list, create an optgroup and recurse into the sublist + sprintf( + '\n%s\n', + htmlEscape(label, TRUE), + selectOptions(choice, selected, inputId, perfWarning) + ) + } else { + # If single item, just return option string + sprintf( + '', + htmlEscape(choice, TRUE), + if (choice %in% selected) " selected" else "", + htmlEscape(label) + ) + } + } + ) + + HTML(paste(html, collapse = "\n")) +} + +#' Toolbar: Add a divider to a toolbar +#' +#' @description +#' `toolbar_divider()` creates a visual divider line with customizable width +#' and spacing between toolbar elements. +#' +#' @param width A CSS length unit specifying the width of the divider line. +#' Defaults to `"2px"` for a sensible dividing line. Pass `0px` for no +#' divider line. +#' @param gap A CSS length unit defining the spacing around the divider. +#' Defaults to `"1rem"` for sensible fixed spacing. +#' +#' @examplesIf rlang::is_interactive() +#' toolbar( +#' toolbar_input_button(id = "left1", label = "Left"), +#' toolbar_divider(), +#' toolbar_input_button(id = "right1", label = "Right") +#' ) +#' +#' toolbar( +#' toolbar_input_button(id = "a", label = "A"), +#' toolbar_divider(width = "5px", gap = "20px"), +#' toolbar_input_button(id = "b", label = "B") +#' ) +#' +#' @family Toolbar components +#' @export +toolbar_divider <- function(..., width = NULL, gap = NULL) { + rlang::check_dots_empty() + width <- validateCssUnit(width) + gap <- validateCssUnit(gap) + + div( + class = "bslib-toolbar-divider", + style = css( + # Sets the overall width of divider space + `--_divider-gap` = gap, + # Sets the width of the pseudo-element divider line, defaults to 2px + `--_divider-width` = width + ), + `aria-hidden` = "true" + ) +} + + +#' Add toolbar switch input +#' +#' @description +#' A switch input designed to fit well in small places such as in a [toolbar()]. +#' #' toolbar_input_switch(id = "toggle", label = "Enable", value = TRUE), +#' toolbar_input_switch(id = "switch", value = FALSE) +#' ) +#' +#' @param id The input ID. +#' @param label The label to display next to the switch. If `NULL`, no label +#' is displayed. +#' @param value The initial state of the switch. Default is `FALSE`. +#' +#' @return Returns a switch input suitable for use in a toolbar. +#' +#' @family Toolbar components +#' @export +toolbar_input_switch <- function( + id, + label = NULL, + value = FALSE +) { + input_switch( + id = id, + label = label, + value = value, + width = NULL + ) +} diff --git a/inst/components/dist/components.css b/inst/components/dist/components.css index 9fe6bbcf8..7af2f1c4b 100644 --- a/inst/components/dist/components.css +++ b/inst/components/dist/components.css @@ -1 +1 @@ -.accordion .accordion-header{font-size:calc(1.325rem + .9vw);margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2;color:var(--bs-heading-color);margin-bottom:0}@media (min-width: 1200px){.accordion .accordion-header{font-size:2rem}}.accordion .accordion-icon:not(:empty){margin-right:0.75rem;display:flex}.accordion .accordion-button:not(.collapsed){box-shadow:none}.accordion .accordion-button:not(.collapsed):focus{box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.bslib-card{overflow:auto}.bslib-card .card-body+.card-body{padding-top:0}.bslib-card .card-body{overflow:auto}.bslib-card .card-body p{margin-top:0}.bslib-card .card-body p:last-child{margin-bottom:0}.bslib-card .card-body{max-height:var(--bslib-card-body-max-height, none)}.bslib-card[data-full-screen="true"]>.card-body{max-height:var(--bslib-card-body-max-height-full-screen, none)}.bslib-card .card-header{display:flex;flex-direction:row;align-items:center;align-self:stretch;gap:0.25rem}.bslib-card .card-header>.nav{flex:1;min-width:0}.bslib-card .card-header>.bslib-nav-spacer{margin-left:auto}.bslib-card .card-header .form-group{margin-bottom:0}.bslib-card .card-header .selectize-control{margin-bottom:0}.bslib-card .card-header .selectize-control .item{margin-right:1.15rem}.bslib-card .card-footer{margin-top:auto}.bslib-card .bslib-navs-card-title{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center}.bslib-card .bslib-navs-card-title .nav{margin-left:auto}.bslib-card .bslib-sidebar-layout:not([data-bslib-sidebar-border="true"]){border:none}.bslib-card .bslib-sidebar-layout:not([data-bslib-sidebar-border-radius="true"]){border-top-left-radius:0;border-top-right-radius:0}.bslib-card[data-full-screen="true"]{position:fixed;inset:3.5rem 1rem 1rem;height:auto !important;max-height:none !important;width:auto !important;z-index:1070}.bslib-full-screen-enter{position:absolute;bottom:var(--bslib-full-screen-enter-bottom, 0.2rem);right:var(--bslib-full-screen-enter-right, 0);top:var(--bslib-full-screen-enter-top);left:var(--bslib-full-screen-enter-left);color:var(--bslib-color-fg, var(--bs-card-color));background-color:var(--bslib-color-bg, var(--bs-card-bg, var(--bs-body-bg)));border:var(--bs-card-border-width) solid var(--bslib-color-fg, var(--bs-card-border-color));box-shadow:0 2px 4px rgba(0,0,0,0.15);margin:0.2rem 0.4rem;padding:0.55rem !important;font-size:.8rem;cursor:pointer;opacity:0;z-index:1070}.card:hover>*>.bslib-full-screen-enter,.card:focus-within>*>.bslib-full-screen-enter{opacity:0.6}.card:hover>*>.bslib-full-screen-enter:hover,.card:hover>*>.bslib-full-screen-enter:focus,.card:focus-within>*>.bslib-full-screen-enter:hover,.card:focus-within>*>.bslib-full-screen-enter:focus{opacity:1}.card[data-full-screen="false"]:hover>*>.bslib-full-screen-enter{display:block}.bslib-has-full-screen .bslib-full-screen-enter{display:none !important}.bslib-full-screen-exit{position:relative;top:1.35rem;font-size:0.9rem;cursor:pointer;text-decoration:none;display:flex;float:right;margin-right:2.15rem;align-items:center;color:rgba(var(--bs-body-bg-rgb), 0.8)}.bslib-full-screen-exit:hover{color:rgba(var(--bs-body-bg-rgb), 1)}.bslib-full-screen-exit svg{margin-left:0.5rem;font-size:1.5rem}#bslib-full-screen-overlay{position:fixed;inset:0;background-color:rgba(var(--bs-body-color-rgb), 0.6);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);z-index:1069;animation:bslib-full-screen-overlay-enter 400ms cubic-bezier(0.6, 0.02, 0.65, 1) forwards}@keyframes bslib-full-screen-overlay-enter{0%{opacity:0}100%{opacity:1}}@media (max-width: 575.98px){.bslib-card[data-full-screen="true"]{inset:2.5rem 0.5rem 0.5rem}.bslib-full-screen-exit{top:0.75rem;margin-right:1.25rem}}.bslib-grid{--_item-column-span: 1;display:grid !important;gap:var(--bslib-spacer, 1rem);height:var(--bslib-grid-height)}.bslib-grid>*{grid-column:auto/span var(--_item-column-span, 1)}.bslib-grid.grid{grid-template-columns:repeat(var(--bs-columns, 12), minmax(0, 1fr));grid-template-rows:unset;grid-auto-rows:var(--bslib-grid--row-heights);--bslib-grid--row-heights--xs: unset;--bslib-grid--row-heights--sm: unset;--bslib-grid--row-heights--md: unset;--bslib-grid--row-heights--lg: unset;--bslib-grid--row-heights--xl: unset;--bslib-grid--row-heights--xxl: unset}.bslib-grid.grid.bslib-grid--row-heights--xs{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xs)}@media (min-width: 576px){.bslib-grid.grid.bslib-grid--row-heights--sm{--bslib-grid--row-heights: var(--bslib-grid--row-heights--sm)}}@media (min-width: 768px){.bslib-grid.grid.bslib-grid--row-heights--md{--bslib-grid--row-heights: var(--bslib-grid--row-heights--md)}}@media (min-width: 992px){.bslib-grid.grid.bslib-grid--row-heights--lg{--bslib-grid--row-heights: var(--bslib-grid--row-heights--lg)}}@media (min-width: 1200px){.bslib-grid.grid.bslib-grid--row-heights--xl{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xl)}}@media (min-width: 1400px){.bslib-grid.grid.bslib-grid--row-heights--xxl{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xxl)}}.bslib-grid>*>.shiny-input-container{width:100%}bslib-layout-columns.bslib-grid{--_item-column-span: 6}bslib-layout-columns[hidden-until-init]>*{display:none}@media (max-width: 767.98px){bslib-layout-columns:where(.bslib-grid)>*{grid-column:1 / -1}}@media (max-width: 575.98px){.bslib-grid{grid-template-columns:1fr !important;height:var(--bslib-grid-height-mobile)}.bslib-grid.grid{height:unset !important}}.bslib-input-submit-textarea{margin:0 auto}.bslib-submit-textarea-container{display:flex;flex-direction:column;gap:0.5rem;padding:0.5rem;border:var(--bs-border-width, 1px) solid var(--bs-gray-500, #ced4da);border-radius:var(--bs-border-radius-sm, 4px);background-color:var(--bs-body-bg, white);transition:border-color 0.2s, box-shadow 0.2s}.bslib-submit-textarea-container:focus-within{border-color:var(--bs-primary, #007bff);box-shadow:0 0 0 var(--bs-focus-ring-width, 0.25rem) var(--bs-focus-ring-color, rgba(13,110,253,0.25))}.bslib-submit-textarea-container>textarea{border:none;resize:none;min-height:1rem;max-height:10rem;background-color:transparent;padding:0;color:var(--bs-body-color, #212529)}.bslib-submit-textarea-container>textarea:focus{outline:none;box-shadow:none}.bslib-submit-textarea-container>footer{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:0.5rem}.bslib-submit-textarea-container .bslib-submit-textarea-btn{margin-left:auto}.bslib-toolbar{display:flex;align-items:center;gap:0.25rem}.bslib-submit-key{border-radius:var(--bs-border-radius-sm, 4px);padding:0.25em 0.5em;font-weight:300;font-size:0.7em;vertical-align:0.15em}:not(.disabled) .bslib-submit-key{background-color:rgba(var(--bs-body-color-rgb, 0, 0, 0), 0.2)}@media (min-width: 576px){.nav:not(.nav-hidden){display:flex !important;display:-webkit-flex !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column){float:none !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column)>.bslib-nav-spacer{margin-left:auto !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column)>.form-inline{margin-top:auto;margin-bottom:auto}.nav:not(.nav-hidden).nav-stacked{flex-direction:column;-webkit-flex-direction:column;height:100%}.nav:not(.nav-hidden).nav-stacked>.bslib-nav-spacer{margin-top:auto !important}}.bslib-page-fill{width:100%;height:100%;margin:0;padding:var(--bslib-spacer, 1rem);gap:var(--bslib-spacer, 1rem)}@media (max-width: 575.98px){.bslib-flow-mobile>.html-fill-item{flex:0 0 auto}.bslib-flow-mobile.bslib-page-sidebar>.html-fill-item,.bslib-flow-mobile.bslib-page-navbar.has-page-sidebar>.html-fill-item{flex:1 1 auto}.bslib-flow-mobile.bslib-page-sidebar>.bslib-sidebar-layout>.main>.html-fill-item,.bslib-flow-mobile.bslib-page-navbar.has-page-sidebar>.html-fill-container>.bslib-sidebar-layout>.main>.html-fill-item{flex:0 0 auto}}.navbar+.container-fluid:has(>.tab-content>.tab-pane.active.html-fill-container){padding-left:0;padding-right:0}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container{padding:var(--bslib-spacer, 1rem);gap:var(--bslib-spacer, 1rem)}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child){padding:0}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border="true"]){border-left:none;border-right:none;border-bottom:none}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius="true"]){border-radius:0}.navbar+div>.bslib-sidebar-layout{border-top:var(--bslib-sidebar-border)}:root{--bslib-page-sidebar-title-bg: #202020;--bslib-page-sidebar-title-color: #fff}.bslib-page-sidebar>.navbar{--bs-navbar-brand-color: var(--bslib-page-sidebar-title-color);border-bottom:var(--bs-border-width) solid var(--bs-border-color-translucent);background-color:var(--bslib-page-sidebar-title-bg);color:var(--bslib-page-sidebar-title-color)}.bslib-page-sidebar .bslib-page-title{margin-bottom:0;line-height:var(--bs-body-line-height)}@media (max-width: 991.98px){.bslib-page-sidebar>.bslib-sidebar-layout.sidebar-collapsed:not(.sidebar-right)>.main,.bslib-page-navbar>div>.bslib-sidebar-layout.sidebar-collapsed:not(.sidebar-right)>.main{padding-right:var(--_padding)}.bslib-page-sidebar>.bslib-sidebar-layout.sidebar-collapsed.sidebar-right>.main,.bslib-page-navbar>div>.bslib-sidebar-layout.sidebar-collapsed.sidebar-right>.main{padding-left:var(--_padding)}}@media (min-width: 576px){.bslib-sidebar-layout .bslib-page-main.html-fill-container{min-height:var(--bslib-page-main-min-height, 576px)}.bslib-sidebar-layout:not(.sidebar-collapsed) .bslib-page-main.html-fill-container,.bslib-sidebar-layout.transitioning .bslib-page-main.html-fill-container{min-width:var(--bslib-page-main-min-width, 576px)}}.bslib-sidebar-layout{container-type:style;--_transition-duration: 0;--_transition-easing-x: var(--bslib-sidebar-transition-easing-x, cubic-bezier(0.8, 0.78, 0.22, 1.07));--_border: var(--bslib-sidebar-border, var(--bs-card-border-width, var(--bs-border-width)) solid var(--bs-card-border-color, var(--bs-border-color-translucent)));--_border-radius: var(--bslib-sidebar-border-radius, var(--bs-border-radius));--_vert-border: var(--bslib-sidebar-vert-border, var(--_border));--_sidebar-width: var(--bslib-sidebar-width, 250px);--_sidebar-bg: var(--bslib-sidebar-bg, RGBA(var(--bs-body-bg-rgb), 0.05));--_sidebar-fg: var(--bslib-sidebar-fg, var(--_main-fg));--_main-fg: var(--bslib-sidebar-main-fg, var(--bs-body-color));--_main-bg: var(--bslib-sidebar-main-bg, transparent);--_toggle-bg: var(--bslib-sidebar-toggle-bg, rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.1));--_padding: var(--bslib-sidebar-padding, var(--bslib-spacer, 1.5rem));--_icon-size: var(--bslib-sidebar-icon-size, 1rem);--_icon-button-size: var(--bslib-sidebar-icon-button-size, calc(var(--_icon-size, 1rem) * 2));--_padding-icon: calc(var(--_icon-button-size, 2rem) * 1.5);--_toggle-border-radius: var(--bslib-collapse-toggle-border-radius, var(--bs-border-radius, 3px));--_toggle-transform: var(--bslib-collapse-toggle-transform, 0deg);--_toggle-transition-easing: var(--bslib-sidebar-toggle-transition-easing, cubic-bezier(1, 0, 0, 1));--_toggle-right-transform: var(--bslib-collapse-toggle-right-transform, 180deg);--_toggle-position-y: calc(var(--_js-toggle-count-this-side, 0) * calc(var(--_icon-size) + var(--_padding)) + var(--_icon-size, 1rem) / 2);--_toggle-position-x: calc(-2.5 * var(--_icon-size) - var(--bs-card-border-width, 1px));--_mobile-max-height: var(--bslib-sidebar-mobile-max-height, var(--bslib-sidebar-max-height-mobile));--_sidebar-mobile-opacity: var(--bslib-sidebar-mobile-opacity);--_main-mobile-expanded-opacity: var(--bslib-sidebar-main-mobile-expanded-opacity, 0);--_sidebar-mobile-max-width: var(--bslib-sidebar-mobile-max-width);--_sidebar-mobile-box-shadow: var(--bslib-sidebar-mobile-box-shadow);--_column-main: minmax(0, 1fr);--_toggle-collective-height: calc(calc(var(--_icon-button-size) + 0.5em) * var(--_js-toggle-count-max-side, 1));--_resize-handle-width: var(--bslib-sidebar-resize-handle-width, 12px);--_resize-indicator-color: var(--_sidebar-fg, var(--bs-emphasis-color, black));--_resize-indicator-color-active: var(--bslib-sidebar-resize-indicator-color-active, var(--bs-primary, #0d6efd));display:grid !important;grid-template-columns:Min(calc(100% - var(--_padding-icon)), var(--_sidebar-width)) var(--_column-main);position:relative;transition:grid-template-columns ease-in-out var(--_transition-duration),background-color linear var(--_transition-duration);border:var(--_border);border-radius:var(--_border-radius)}@container style(--bs-card-color: not " "){.bslib-sidebar-layout{--_main-fg: var(--bslib-sidebar-main-fg, var(--bs-card-color, var(--bs-body-color)))}}.bslib-sidebar-layout.transitioning{--_transition-duration: max(var(--bslib-sidebar-transition-duration, 300ms), 5ms)}@media (prefers-reduced-motion: reduce){.bslib-sidebar-layout{transition:none}}.bslib-sidebar-layout,.html-fill-container>.bslib-sidebar-layout.html-fill-item{min-height:var(--_toggle-collective-height)}.bslib-sidebar-layout[data-bslib-sidebar-border="false"]{border:none}.bslib-sidebar-layout[data-bslib-sidebar-border-radius="false"]{border-radius:initial}.bslib-sidebar-layout>.main,.bslib-sidebar-layout>.sidebar{grid-row:1 / 2;border-radius:inherit;overflow:auto}.bslib-sidebar-layout>.main{grid-column:2 / 3;border-top-left-radius:0;border-bottom-left-radius:0;padding:var(--_padding);transition:padding var(--_transition-easing-x) var(--_transition-duration);color:var(--_main-fg);background-color:var(--_main-bg)}.bslib-sidebar-layout>.sidebar{grid-column:1 / 2;width:100%;border-right:var(--_vert-border);border-top-right-radius:0;border-bottom-right-radius:0;color:var(--_sidebar-fg);background-color:var(--_sidebar-bg);position:relative}.bslib-sidebar-layout>.sidebar>.sidebar-content{display:flex;flex-direction:column;gap:var(--bslib-spacer, 1rem);padding:var(--_padding);padding-top:var(--_padding-icon)}.bslib-sidebar-layout>.sidebar>.sidebar-content>:last-child:not(.sidebar-title){margin-bottom:0}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion{margin-left:calc(-1 * var(--_padding));margin-right:calc(-1 * var(--_padding))}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:last-child{margin-bottom:calc(-1 * var(--_padding))}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:last-child){margin-bottom:1rem}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion .accordion-body{display:flex;flex-direction:column}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:first-child) .accordion-item:first-child{border-top:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:last-child) .accordion-item:last-child{border-bottom:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.bslib-sidebar-layout>.sidebar>.sidebar-content.has-accordion>.sidebar-title{border-bottom:none;padding-bottom:0}.bslib-sidebar-layout>.sidebar .shiny-input-container{width:100%}.bslib-sidebar-layout>.collapse-toggle{grid-row:1 / 2;grid-column:1 / 2;z-index:1000;display:inline-flex;align-items:center;position:absolute;right:calc(var(--_icon-size));top:calc(var(--_icon-size, 1rem) / 2);border:none;border-radius:var(--_toggle-border-radius);height:var(--_icon-button-size, 2rem);width:var(--_icon-button-size, 2rem);display:flex;align-items:center;justify-content:center;padding:0;color:var(--_sidebar-fg);background-color:unset;transition:color var(--_transition-easing-x) var(--_transition-duration),top var(--_transition-easing-x) var(--_transition-duration),right var(--_transition-easing-x) var(--_transition-duration),left var(--_transition-easing-x) var(--_transition-duration)}.bslib-sidebar-layout>.collapse-toggle:hover{background-color:var(--_toggle-bg)}.bslib-sidebar-layout>.collapse-toggle>.collapse-icon{opacity:0.8;width:var(--_icon-size);height:var(--_icon-size);transform:rotateY(var(--_toggle-transform));transition:transform var(--_toggle-transition-easing) var(--_transition-duration)}.bslib-sidebar-layout>.collapse-toggle:hover>.collapse-icon{opacity:1}.bslib-sidebar-layout .sidebar-title{font-size:1.25rem;line-height:1.25;margin-top:0;margin-bottom:1rem;padding-bottom:1rem;border-bottom:var(--_border)}.bslib-sidebar-layout.sidebar-right{grid-template-columns:var(--_column-main) Min(calc(100% - var(--_padding-icon)), var(--_sidebar-width))}.bslib-sidebar-layout.sidebar-right>.main{grid-column:1 / 2;border-top-right-radius:0;border-bottom-right-radius:0;border-top-left-radius:inherit;border-bottom-left-radius:inherit}.bslib-sidebar-layout.sidebar-right>.sidebar{grid-column:2 / 3;border-right:none;border-left:var(--_vert-border);border-top-left-radius:0;border-bottom-left-radius:0}.bslib-sidebar-layout.sidebar-right>.collapse-toggle{grid-column:2 / 3;left:var(--_icon-size);right:unset;border:var(--bslib-collapse-toggle-border)}.bslib-sidebar-layout.sidebar-right>.collapse-toggle>.collapse-icon{transform:rotateY(var(--_toggle-right-transform))}.bslib-sidebar-layout.transitioning>.sidebar>.sidebar-content{display:none}.bslib-sidebar-layout.sidebar-collapsed{--_toggle-transform: 180deg;--_toggle-right-transform: 0deg;--_vert-border: none;grid-template-columns:0 minmax(0, 1fr)}.bslib-sidebar-layout.sidebar-collapsed.sidebar-right{grid-template-columns:minmax(0, 1fr) 0}.bslib-sidebar-layout.sidebar-collapsed:not(.transitioning)>.sidebar>*{display:none}.bslib-sidebar-layout.sidebar-collapsed>.main{border-radius:inherit;padding-left:var(--_padding-icon);padding-right:var(--_padding-icon)}.bslib-sidebar-layout.sidebar-collapsed>.collapse-toggle{color:var(--_main-fg);top:var(--_toggle-position-y);right:var(--_toggle-position-x)}.bslib-sidebar-layout.sidebar-collapsed.sidebar-right>.collapse-toggle{left:var(--_toggle-position-x);right:unset}.bslib-sidebar-layout .bslib-sidebar-resize-handle{position:absolute;top:0;bottom:0;width:var(--_resize-handle-width);left:calc(calc(-1 * var(--_resize-handle-width)) - 2px);grid-column:2;cursor:ew-resize;user-select:none;z-index:calc(1000 + 1)}.bslib-sidebar-layout .bslib-sidebar-resize-handle::before{content:"";position:absolute;top:0;bottom:0;left:0;right:calc(-1 * var(--_resize-handle-width) - 2px);z-index:calc(1000 + 1)}.bslib-sidebar-layout .bslib-sidebar-resize-handle .resize-indicator{position:absolute;top:50%;right:2px;width:2px;height:30px;transform:translate(-50%, -50%);background-color:var(--_resize-indicator-color);opacity:0.1;border-radius:1px;transition:all 0.15s ease}.bslib-sidebar-layout .bslib-sidebar-resize-handle:hover .resize-indicator,.bslib-sidebar-layout .bslib-sidebar-resize-handle:focus .resize-indicator,.bslib-sidebar-layout .bslib-sidebar-resize-handle:active .resize-indicator,.bslib-sidebar-layout .bslib-sidebar-resize-handle:focus .resize-indicator{opacity:1}.bslib-sidebar-layout .bslib-sidebar-resize-handle:hover .resize-indicator,.bslib-sidebar-layout .bslib-sidebar-resize-handle:focus .resize-indicator{width:3px;height:40px}.bslib-sidebar-layout .bslib-sidebar-resize-handle:active .resize-indicator{background-color:var(--_resize-indicator-color-active);width:4px;height:50px}.bslib-sidebar-layout .bslib-sidebar-resize-handle:focus{outline:none}.bslib-sidebar-layout .bslib-sidebar-resize-handle:focus .resize-indicator{outline:2px solid var(--bs-focus-ring-color, rgba(13,110,253,0.25));outline-offset:2px}.bslib-sidebar-layout.sidebar-right>.bslib-sidebar-resize-handle{left:2px}.bslib-sidebar-layout.transitioning>.bslib-sidebar-resize-handle,.bslib-sidebar-layout.sidebar-collapsed>.bslib-sidebar-resize-handle{display:none}.bslib-sidebar-layout.sidebar-resizing{user-select:none}.bslib-sidebar-layout.sidebar-resizing>.bslib-sidebar-resize-handle .resize-indicator{background-color:var(--_resize-indicator-color-active);width:4px;height:50px}.bslib-sidebar-layout{--bslib-sidebar-js-window-size: desktop}@media (max-width: 575.98px){.bslib-sidebar-layout{--bslib-sidebar-js-window-size: mobile}.bslib-sidebar-layout .bslib-sidebar-resize-handle{display:none !important}}@media (min-width: 576px){.bslib-sidebar-layout[data-collapsible-desktop="false"]{--_padding-icon: var(--_padding)}.bslib-sidebar-layout[data-collapsible-desktop="false"]>.collapse-toggle{display:none}.bslib-sidebar-layout[data-collapsible-desktop="false"]>.sidebar[hidden]{display:block !important}.bslib-sidebar-layout[data-collapsible-desktop="false"]>.sidebar[hidden]>.sidebar-content{display:flex !important}}@media (max-width: 575.98px){.bslib-sidebar-layout>.sidebar,.bslib-sidebar-layout.sidebar-right>.sidebar{border:none}.bslib-sidebar-layout>.main,.bslib-sidebar-layout.sidebar-right>.main{grid-column:1 / 3}.bslib-sidebar-layout[data-collapsible-mobile="true"]{grid-template-rows:calc(var(--_icon-button-size) + var(--_padding)) 1fr;grid-template-columns:100% 0}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.collapse-toggle{grid-row:1 / 2}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.main{grid-row:2 / 3}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.sidebar{grid-row:1 / 3}.bslib-sidebar-layout[data-collapsible-mobile="true"]:not(.sidebar-collapsed)>.sidebar,.bslib-sidebar-layout[data-collapsible-mobile="true"].transitioning>.sidebar{z-index:1045}.bslib-sidebar-layout[data-collapsible-mobile="true"]:not(.sidebar-collapsed)>.collapse-toggle,.bslib-sidebar-layout[data-collapsible-mobile="true"].transitioning>.collapse-toggle{z-index:1045}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.collapse-toggle{top:unset;position:relative;align-self:center}.bslib-sidebar-layout[data-collapsible-mobile="true"]:not(.sidebar-right)>.collapse-toggle{left:var(--_icon-size);right:unset;justify-self:left}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-right>.collapse-toggle{right:var(--_icon-size);left:unset;justify-self:right}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.sidebar{opacity:var(--_sidebar-mobile-opacity, 1);max-width:var(--_sidebar-mobile-max-width, 100%);box-shadow:var(--_sidebar-mobile-box-shadow)}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.sidebar{margin:0;padding-top:var(--_padding-icon)}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.sidebar>.sidebar-content{padding-top:0;height:100%;overflow-y:auto}.bslib-sidebar-layout[data-collapsible-mobile="true"]:not(.sidebar-right)>.sidebar{margin-right:auto}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-right>.sidebar{margin-left:auto}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-right{grid-template-columns:0 100%}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-collapsed{grid-template-columns:0 100%}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-collapsed.sidebar-right{grid-template-columns:100% 0}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.main{padding-top:1px;padding-left:var(--_padding);padding-right:var(--_padding)}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.main{opacity:var(--_main-mobile-expanded-opacity);transition:opacity var(--_transition-easing-x) var(--_transition-duration)}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-collapsed>.main{opacity:1}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.main{background-color:none}.bslib-sidebar-layout[data-collapsible-mobile="true"],.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-collapsed{background-color:var(--_main-bg)}}@media (max-width: 575.98px){.bslib-sidebar-layout[data-collapsible-mobile="false"]{display:block !important;--_padding-icon: var(--_padding);--_vert-border: var(--_border)}.bslib-sidebar-layout[data-collapsible-mobile="false"]>.sidebar[hidden]{display:block !important}.bslib-sidebar-layout[data-collapsible-mobile="false"]>.sidebar[hidden]>.sidebar-content{display:flex !important}.bslib-sidebar-layout[data-collapsible-mobile="false"]>.sidebar{max-height:var(--_mobile-max-height);overflow-y:auto}.bslib-sidebar-layout[data-collapsible-mobile="false"][data-open-mobile="always"]>.sidebar{border-top:var(--_vert-border)}.bslib-sidebar-layout[data-collapsible-mobile="false"][data-open-mobile="always-above"]>.sidebar{border-bottom:var(--_vert-border)}.bslib-sidebar-layout[data-collapsible-mobile="false"]>.collapse-toggle{display:none}}html[data-bslib-sidebar-resizing="true"]{cursor:ew-resize !important;user-select:none !important}.toast{--bslib-toast-shadow: var(--bs-box-shadow);box-shadow:var(--bslib-toast-shadow);position:relative;overflow:hidden}.toast-body:empty{display:none}.text-bg-primary.toast .toast-body .btn-close,.text-bg-secondary.toast .toast-body .btn-close,.text-bg-success.toast .toast-body .btn-close,.text-bg-info.toast .toast-body .btn-close,.text-bg-warning.toast .toast-body .btn-close,.text-bg-danger.toast .toast-body .btn-close,.text-bg-dark.toast .toast-body .btn-close,.text-white.toast .toast-body .btn-close,.text-light.toast .toast-body .btn-close{filter:var(--bs-btn-close-white-filter)}@keyframes bslib-toast-progress{from{transform:scaleX(0)}to{transform:scaleX(1)}}.bslib-toast-progress-bar{position:absolute;top:0;left:0;height:2px;width:100%;pointer-events:none;z-index:1;transform-origin:left;border-radius:inherit;pointer-events:none;background-color:currentColor}.bslib-value-box{container-name:bslib-value-box;container-type:inline-size}.bslib-value-box.default{--bslib-value-box-bg-default: var(--bs-card-bg, #fff);--bslib-value-box-border-color-default: var(--bs-card-border-color, var(--bs-border-color-translucent));color:var(--bslib-value-box-color, var(--bs-body-color));background-color:var(--bslib-value-box-bg, var(--bslib-value-box-bg-default));border-color:var(--bslib-value-box-border-color, var(--bslib-value-box-border-color-default))}.bslib-value-box .value-box-grid{display:grid;grid-template-areas:"left right";align-items:center;overflow:hidden}.bslib-value-box .value-box-showcase{height:100%;max-height:var(---bslib-value-box-showcase-max-h, 100%)}.bslib-value-box .value-box-showcase,.bslib-value-box .value-box-showcase>.html-fill-item{width:100%}.bslib-value-box[data-full-screen="true"] .value-box-showcase{max-height:var(---bslib-value-box-showcase-max-h-fs, 100%)}@media screen and (min-width: 575.98px){@container bslib-value-box (max-width: 300px){.bslib-value-box:not(.showcase-bottom) .value-box-grid{grid-template-columns:1fr !important;grid-template-rows:auto auto;grid-template-areas:"top" "bottom"}.bslib-value-box:not(.showcase-bottom) .value-box-grid .value-box-showcase{grid-area:top !important}.bslib-value-box:not(.showcase-bottom) .value-box-grid .value-box-area{grid-area:bottom !important;justify-content:end}}}.bslib-value-box .value-box-area{justify-content:center;padding:1.5rem 1rem;font-size:.9rem;font-weight:500}.bslib-value-box .value-box-area *{margin-bottom:0;margin-top:0}.bslib-value-box .value-box-title{font-size:1rem;margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2}.bslib-value-box .value-box-title:empty::after{content:'\00a0 '}.bslib-value-box .value-box-value{font-size:calc(1.325rem + .9vw);margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2}@media (min-width: 1200px){.bslib-value-box .value-box-value{font-size:2rem}}.bslib-value-box .value-box-value:empty::after{content:'\00a0 '}.bslib-value-box .value-box-showcase{align-items:center;justify-content:center;margin-top:auto;margin-bottom:auto;padding:1rem}.bslib-value-box .value-box-showcase .bi,.bslib-value-box .value-box-showcase .fa,.bslib-value-box .value-box-showcase .fab,.bslib-value-box .value-box-showcase .fas,.bslib-value-box .value-box-showcase .far{opacity:.85;min-width:50px;max-width:125%}.bslib-value-box .value-box-showcase .bi,.bslib-value-box .value-box-showcase .fa,.bslib-value-box .value-box-showcase .fab,.bslib-value-box .value-box-showcase .fas,.bslib-value-box .value-box-showcase .far{font-size:4rem}.bslib-value-box.showcase-top-right .value-box-grid{grid-template-columns:1fr var(---bslib-value-box-showcase-w, 50%)}.bslib-value-box.showcase-top-right .value-box-grid .value-box-showcase{grid-area:right;margin-left:auto;align-self:start;align-items:end;padding-left:0;padding-bottom:0}.bslib-value-box.showcase-top-right .value-box-grid .value-box-area{grid-area:left;align-self:end}.bslib-value-box.showcase-top-right[data-full-screen="true"] .value-box-grid{grid-template-columns:auto var(---bslib-value-box-showcase-w-fs, 1fr)}.bslib-value-box.showcase-top-right[data-full-screen="true"] .value-box-grid>div{align-self:center}.bslib-value-box.showcase-top-right:not([data-full-screen="true"]) .value-box-showcase{margin-top:0}@container bslib-value-box (max-width: 300px){.bslib-value-box.showcase-top-right:not([data-full-screen="true"]) .value-box-grid .value-box-showcase{padding-left:1rem}}.bslib-value-box.showcase-left-center .value-box-grid{grid-template-columns:var(---bslib-value-box-showcase-w, 30%) auto}.bslib-value-box.showcase-left-center[data-full-screen="true"] .value-box-grid{grid-template-columns:var(---bslib-value-box-showcase-w-fs, 1fr) auto}.bslib-value-box.showcase-left-center:not([data-fill-screen="true"]) .value-box-grid .value-box-showcase{grid-area:left}.bslib-value-box.showcase-left-center:not([data-fill-screen="true"]) .value-box-grid .value-box-area{grid-area:right}.bslib-value-box.showcase-bottom .value-box-grid{grid-template-columns:1fr;grid-template-rows:1fr var(---bslib-value-box-showcase-h, auto);grid-template-areas:"top" "bottom";overflow:hidden}.bslib-value-box.showcase-bottom .value-box-grid .value-box-showcase{grid-area:bottom;padding:0;margin:0}.bslib-value-box.showcase-bottom .value-box-grid .value-box-area{grid-area:top}.bslib-value-box.showcase-bottom[data-full-screen="true"] .value-box-grid{grid-template-rows:1fr var(---bslib-value-box-showcase-h-fs, 2fr)}.bslib-value-box.showcase-bottom[data-full-screen="true"] .value-box-grid .value-box-showcase{padding:1rem} +.accordion .accordion-header{font-size:calc(1.325rem + .9vw);margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2;color:var(--bs-heading-color);margin-bottom:0}@media (min-width: 1200px){.accordion .accordion-header{font-size:2rem}}.accordion .accordion-icon:not(:empty){margin-right:0.75rem;display:flex}.accordion .accordion-button:not(.collapsed){box-shadow:none}.accordion .accordion-button:not(.collapsed):focus{box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.bslib-card{overflow:auto}.bslib-card .card-body+.card-body{padding-top:0}.bslib-card .card-body{overflow:auto}.bslib-card .card-body p{margin-top:0}.bslib-card .card-body p:last-child{margin-bottom:0}.bslib-card .card-body{max-height:var(--bslib-card-body-max-height, none)}.bslib-card[data-full-screen="true"]>.card-body{max-height:var(--bslib-card-body-max-height-full-screen, none)}.bslib-card .card-header{display:flex;flex-direction:row;align-items:center;align-self:stretch;min-height:2.5rem;padding-block:calc(var(--bs-card-cap-padding-y) / 2);gap:0.25rem}.bslib-card .card-header>.nav{flex:1;min-width:0}.bslib-card .card-header>.bslib-nav-spacer{margin-left:auto}.bslib-card .card-header .form-group{margin-bottom:0}.bslib-card .card-header .selectize-control{margin-bottom:0}.bslib-card .card-header .selectize-control .item{margin-right:1.15rem}.bslib-card .card-footer{margin-top:auto}.bslib-card .bslib-navs-card-title{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center}.bslib-card .bslib-navs-card-title .nav{margin-left:auto}.bslib-card .bslib-sidebar-layout:not([data-bslib-sidebar-border="true"]){border:none}.bslib-card .bslib-sidebar-layout:not([data-bslib-sidebar-border-radius="true"]){border-top-left-radius:0;border-top-right-radius:0}.bslib-card[data-full-screen="true"]{position:fixed;inset:3.5rem 1rem 1rem;height:auto !important;max-height:none !important;width:auto !important;z-index:1070}.bslib-full-screen-enter{position:absolute;bottom:var(--bslib-full-screen-enter-bottom, 0.2rem);right:var(--bslib-full-screen-enter-right, 0);top:var(--bslib-full-screen-enter-top);left:var(--bslib-full-screen-enter-left);color:var(--bslib-color-fg, var(--bs-card-color));background-color:var(--bslib-color-bg, var(--bs-card-bg, var(--bs-body-bg)));border:var(--bs-card-border-width) solid var(--bslib-color-fg, var(--bs-card-border-color));box-shadow:0 2px 4px rgba(0,0,0,0.15);margin:0.2rem 0.4rem;padding:0.55rem !important;font-size:.8rem;cursor:pointer;opacity:0;z-index:1070}.card:hover>*>.bslib-full-screen-enter,.card:focus-within>*>.bslib-full-screen-enter{opacity:0.6}.card:hover>*>.bslib-full-screen-enter:hover,.card:hover>*>.bslib-full-screen-enter:focus,.card:focus-within>*>.bslib-full-screen-enter:hover,.card:focus-within>*>.bslib-full-screen-enter:focus{opacity:1}.card[data-full-screen="false"]:hover>*>.bslib-full-screen-enter{display:block}.bslib-has-full-screen .bslib-full-screen-enter{display:none !important}.bslib-full-screen-exit{position:relative;top:1.35rem;font-size:0.9rem;cursor:pointer;text-decoration:none;display:flex;float:right;margin-right:2.15rem;align-items:center;color:rgba(var(--bs-body-bg-rgb), 0.8)}.bslib-full-screen-exit:hover{color:rgba(var(--bs-body-bg-rgb), 1)}.bslib-full-screen-exit svg{margin-left:0.5rem;font-size:1.5rem}#bslib-full-screen-overlay{position:fixed;inset:0;background-color:rgba(var(--bs-body-color-rgb), 0.6);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);z-index:1069;animation:bslib-full-screen-overlay-enter 400ms cubic-bezier(0.6, 0.02, 0.65, 1) forwards}@keyframes bslib-full-screen-overlay-enter{0%{opacity:0}100%{opacity:1}}@media (max-width: 575.98px){.bslib-card[data-full-screen="true"]{inset:2.5rem 0.5rem 0.5rem}.bslib-full-screen-exit{top:0.75rem;margin-right:1.25rem}}.bslib-grid{--_item-column-span: 1;display:grid !important;gap:var(--bslib-spacer, 1rem);height:var(--bslib-grid-height)}.bslib-grid>*{grid-column:auto/span var(--_item-column-span, 1)}.bslib-grid.grid{grid-template-columns:repeat(var(--bs-columns, 12), minmax(0, 1fr));grid-template-rows:unset;grid-auto-rows:var(--bslib-grid--row-heights);--bslib-grid--row-heights--xs: unset;--bslib-grid--row-heights--sm: unset;--bslib-grid--row-heights--md: unset;--bslib-grid--row-heights--lg: unset;--bslib-grid--row-heights--xl: unset;--bslib-grid--row-heights--xxl: unset}.bslib-grid.grid.bslib-grid--row-heights--xs{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xs)}@media (min-width: 576px){.bslib-grid.grid.bslib-grid--row-heights--sm{--bslib-grid--row-heights: var(--bslib-grid--row-heights--sm)}}@media (min-width: 768px){.bslib-grid.grid.bslib-grid--row-heights--md{--bslib-grid--row-heights: var(--bslib-grid--row-heights--md)}}@media (min-width: 992px){.bslib-grid.grid.bslib-grid--row-heights--lg{--bslib-grid--row-heights: var(--bslib-grid--row-heights--lg)}}@media (min-width: 1200px){.bslib-grid.grid.bslib-grid--row-heights--xl{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xl)}}@media (min-width: 1400px){.bslib-grid.grid.bslib-grid--row-heights--xxl{--bslib-grid--row-heights: var(--bslib-grid--row-heights--xxl)}}.bslib-grid>*>.shiny-input-container{width:100%}bslib-layout-columns.bslib-grid{--_item-column-span: 6}bslib-layout-columns[hidden-until-init]>*{display:none}@media (max-width: 767.98px){bslib-layout-columns:where(.bslib-grid)>*{grid-column:1 / -1}}@media (max-width: 575.98px){.bslib-grid{grid-template-columns:1fr !important;height:var(--bslib-grid-height-mobile)}.bslib-grid.grid{height:unset !important}}.bslib-input-submit-textarea{margin:0 auto}.bslib-submit-textarea-container{display:flex;flex-direction:column;gap:0.5rem;padding:0.5rem;border:var(--bs-border-width, 1px) solid var(--bs-gray-500, #ced4da);border-radius:var(--bs-border-radius-sm, 4px);background-color:var(--bs-body-bg, white);transition:border-color 0.2s, box-shadow 0.2s}.bslib-submit-textarea-container:focus-within{border-color:var(--bs-primary, #007bff);box-shadow:0 0 0 var(--bs-focus-ring-width, 0.25rem) var(--bs-focus-ring-color, rgba(13,110,253,0.25))}.bslib-submit-textarea-container>textarea{border:none;resize:none;min-height:1rem;max-height:10rem;background-color:transparent;padding:0;color:var(--bs-body-color, #212529)}.bslib-submit-textarea-container>textarea:focus{outline:none;box-shadow:none}.bslib-submit-textarea-container>footer{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:0.5rem}.bslib-submit-textarea-container .bslib-submit-textarea-btn{margin-left:auto}.bslib-toolbar{display:flex;align-items:center;gap:0.25rem}.bslib-submit-key{border-radius:var(--bs-border-radius-sm, 4px);padding:0.25em 0.5em;font-weight:300;font-size:0.7em;vertical-align:0.15em}:not(.disabled) .bslib-submit-key{background-color:rgba(var(--bs-body-color-rgb, 0, 0, 0), 0.2)}@media (min-width: 576px){.nav:not(.nav-hidden){display:flex !important;display:-webkit-flex !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column){float:none !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column)>.bslib-nav-spacer{margin-left:auto !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column)>.form-inline{margin-top:auto;margin-bottom:auto}.nav:not(.nav-hidden).nav-stacked{flex-direction:column;-webkit-flex-direction:column;height:100%}.nav:not(.nav-hidden).nav-stacked>.bslib-nav-spacer{margin-top:auto !important}}.bslib-page-fill{width:100%;height:100%;margin:0;padding:var(--bslib-spacer, 1rem);gap:var(--bslib-spacer, 1rem)}@media (max-width: 575.98px){.bslib-flow-mobile>.html-fill-item{flex:0 0 auto}.bslib-flow-mobile.bslib-page-sidebar>.html-fill-item,.bslib-flow-mobile.bslib-page-navbar.has-page-sidebar>.html-fill-item{flex:1 1 auto}.bslib-flow-mobile.bslib-page-sidebar>.bslib-sidebar-layout>.main>.html-fill-item,.bslib-flow-mobile.bslib-page-navbar.has-page-sidebar>.html-fill-container>.bslib-sidebar-layout>.main>.html-fill-item{flex:0 0 auto}}.navbar+.container-fluid:has(>.tab-content>.tab-pane.active.html-fill-container){padding-left:0;padding-right:0}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container{padding:var(--bslib-spacer, 1rem);gap:var(--bslib-spacer, 1rem)}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container:has(>.bslib-sidebar-layout:only-child){padding:0}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border="true"]){border-left:none;border-right:none;border-bottom:none}.navbar+.container-fluid>.tab-content>.tab-pane.active.html-fill-container>.bslib-sidebar-layout:only-child:not([data-bslib-sidebar-border-radius="true"]){border-radius:0}.navbar+div>.bslib-sidebar-layout{border-top:var(--bslib-sidebar-border)}:root{--bslib-page-sidebar-title-bg: #202020;--bslib-page-sidebar-title-color: #fff}.bslib-page-sidebar>.navbar{--bs-navbar-brand-color: var(--bslib-page-sidebar-title-color);border-bottom:var(--bs-border-width) solid var(--bs-border-color-translucent);background-color:var(--bslib-page-sidebar-title-bg);color:var(--bslib-page-sidebar-title-color)}.bslib-page-sidebar .bslib-page-title{margin-bottom:0;line-height:var(--bs-body-line-height)}@media (max-width: 991.98px){.bslib-page-sidebar>.bslib-sidebar-layout.sidebar-collapsed:not(.sidebar-right)>.main,.bslib-page-navbar>div>.bslib-sidebar-layout.sidebar-collapsed:not(.sidebar-right)>.main{padding-right:var(--_padding)}.bslib-page-sidebar>.bslib-sidebar-layout.sidebar-collapsed.sidebar-right>.main,.bslib-page-navbar>div>.bslib-sidebar-layout.sidebar-collapsed.sidebar-right>.main{padding-left:var(--_padding)}}@media (min-width: 576px){.bslib-sidebar-layout .bslib-page-main.html-fill-container{min-height:var(--bslib-page-main-min-height, 576px)}.bslib-sidebar-layout:not(.sidebar-collapsed) .bslib-page-main.html-fill-container,.bslib-sidebar-layout.transitioning .bslib-page-main.html-fill-container{min-width:var(--bslib-page-main-min-width, 576px)}}.bslib-sidebar-layout{container-type:style;--_transition-duration: 0;--_transition-easing-x: var(--bslib-sidebar-transition-easing-x, cubic-bezier(0.8, 0.78, 0.22, 1.07));--_border: var(--bslib-sidebar-border, var(--bs-card-border-width, var(--bs-border-width)) solid var(--bs-card-border-color, var(--bs-border-color-translucent)));--_border-radius: var(--bslib-sidebar-border-radius, var(--bs-border-radius));--_vert-border: var(--bslib-sidebar-vert-border, var(--_border));--_sidebar-width: var(--bslib-sidebar-width, 250px);--_sidebar-bg: var(--bslib-sidebar-bg, RGBA(var(--bs-body-bg-rgb), 0.05));--_sidebar-fg: var(--bslib-sidebar-fg, var(--_main-fg));--_main-fg: var(--bslib-sidebar-main-fg, var(--bs-body-color));--_main-bg: var(--bslib-sidebar-main-bg, transparent);--_toggle-bg: var(--bslib-sidebar-toggle-bg, rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.1));--_padding: var(--bslib-sidebar-padding, var(--bslib-spacer, 1.5rem));--_icon-size: var(--bslib-sidebar-icon-size, 1rem);--_icon-button-size: var(--bslib-sidebar-icon-button-size, calc(var(--_icon-size, 1rem) * 2));--_padding-icon: calc(var(--_icon-button-size, 2rem) * 1.5);--_toggle-border-radius: var(--bslib-collapse-toggle-border-radius, var(--bs-border-radius, 3px));--_toggle-transform: var(--bslib-collapse-toggle-transform, 0deg);--_toggle-transition-easing: var(--bslib-sidebar-toggle-transition-easing, cubic-bezier(1, 0, 0, 1));--_toggle-right-transform: var(--bslib-collapse-toggle-right-transform, 180deg);--_toggle-position-y: calc(var(--_js-toggle-count-this-side, 0) * calc(var(--_icon-size) + var(--_padding)) + var(--_icon-size, 1rem) / 2);--_toggle-position-x: calc(-2.5 * var(--_icon-size) - var(--bs-card-border-width, 1px));--_mobile-max-height: var(--bslib-sidebar-mobile-max-height, var(--bslib-sidebar-max-height-mobile));--_sidebar-mobile-opacity: var(--bslib-sidebar-mobile-opacity);--_main-mobile-expanded-opacity: var(--bslib-sidebar-main-mobile-expanded-opacity, 0);--_sidebar-mobile-max-width: var(--bslib-sidebar-mobile-max-width);--_sidebar-mobile-box-shadow: var(--bslib-sidebar-mobile-box-shadow);--_column-main: minmax(0, 1fr);--_toggle-collective-height: calc(calc(var(--_icon-button-size) + 0.5em) * var(--_js-toggle-count-max-side, 1));--_resize-handle-width: var(--bslib-sidebar-resize-handle-width, 12px);--_resize-indicator-color: var(--_sidebar-fg, var(--bs-emphasis-color, black));--_resize-indicator-color-active: var(--bslib-sidebar-resize-indicator-color-active, var(--bs-primary, #0d6efd));display:grid !important;grid-template-columns:Min(calc(100% - var(--_padding-icon)), var(--_sidebar-width)) var(--_column-main);position:relative;transition:grid-template-columns ease-in-out var(--_transition-duration),background-color linear var(--_transition-duration);border:var(--_border);border-radius:var(--_border-radius)}@container style(--bs-card-color: not " "){.bslib-sidebar-layout{--_main-fg: var(--bslib-sidebar-main-fg, var(--bs-card-color, var(--bs-body-color)))}}.bslib-sidebar-layout.transitioning{--_transition-duration: max(var(--bslib-sidebar-transition-duration, 300ms), 5ms)}@media (prefers-reduced-motion: reduce){.bslib-sidebar-layout{transition:none}}.bslib-sidebar-layout,.html-fill-container>.bslib-sidebar-layout.html-fill-item{min-height:var(--_toggle-collective-height)}.bslib-sidebar-layout[data-bslib-sidebar-border="false"]{border:none}.bslib-sidebar-layout[data-bslib-sidebar-border-radius="false"]{border-radius:initial}.bslib-sidebar-layout>.main,.bslib-sidebar-layout>.sidebar{grid-row:1 / 2;border-radius:inherit;overflow:auto}.bslib-sidebar-layout>.main{grid-column:2 / 3;border-top-left-radius:0;border-bottom-left-radius:0;padding:var(--_padding);transition:padding var(--_transition-easing-x) var(--_transition-duration);color:var(--_main-fg);background-color:var(--_main-bg)}.bslib-sidebar-layout>.sidebar{grid-column:1 / 2;width:100%;border-right:var(--_vert-border);border-top-right-radius:0;border-bottom-right-radius:0;color:var(--_sidebar-fg);background-color:var(--_sidebar-bg);position:relative}.bslib-sidebar-layout>.sidebar>.sidebar-content{display:flex;flex-direction:column;gap:var(--bslib-spacer, 1rem);padding:var(--_padding);padding-top:var(--_padding-icon)}.bslib-sidebar-layout>.sidebar>.sidebar-content>:last-child:not(.sidebar-title){margin-bottom:0}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion{margin-left:calc(-1 * var(--_padding));margin-right:calc(-1 * var(--_padding))}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:last-child{margin-bottom:calc(-1 * var(--_padding))}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:last-child){margin-bottom:1rem}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion .accordion-body{display:flex;flex-direction:column}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:first-child) .accordion-item:first-child{border-top:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.bslib-sidebar-layout>.sidebar>.sidebar-content>.accordion:not(:last-child) .accordion-item:last-child{border-bottom:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.bslib-sidebar-layout>.sidebar>.sidebar-content.has-accordion>.sidebar-title{border-bottom:none;padding-bottom:0}.bslib-sidebar-layout>.sidebar .shiny-input-container{width:100%}.bslib-sidebar-layout>.collapse-toggle{grid-row:1 / 2;grid-column:1 / 2;z-index:1000;display:inline-flex;align-items:center;position:absolute;right:calc(var(--_icon-size));top:calc(var(--_icon-size, 1rem) / 2);border:none;border-radius:var(--_toggle-border-radius);height:var(--_icon-button-size, 2rem);width:var(--_icon-button-size, 2rem);display:flex;align-items:center;justify-content:center;padding:0;color:var(--_sidebar-fg);background-color:unset;transition:color var(--_transition-easing-x) var(--_transition-duration),top var(--_transition-easing-x) var(--_transition-duration),right var(--_transition-easing-x) var(--_transition-duration),left var(--_transition-easing-x) var(--_transition-duration)}.bslib-sidebar-layout>.collapse-toggle:hover{background-color:var(--_toggle-bg)}.bslib-sidebar-layout>.collapse-toggle>.collapse-icon{opacity:0.8;width:var(--_icon-size);height:var(--_icon-size);transform:rotateY(var(--_toggle-transform));transition:transform var(--_toggle-transition-easing) var(--_transition-duration)}.bslib-sidebar-layout>.collapse-toggle:hover>.collapse-icon{opacity:1}.bslib-sidebar-layout .sidebar-title{font-size:1.25rem;line-height:1.25;margin-top:0;margin-bottom:1rem;padding-bottom:1rem;border-bottom:var(--_border)}.bslib-sidebar-layout.sidebar-right{grid-template-columns:var(--_column-main) Min(calc(100% - var(--_padding-icon)), var(--_sidebar-width))}.bslib-sidebar-layout.sidebar-right>.main{grid-column:1 / 2;border-top-right-radius:0;border-bottom-right-radius:0;border-top-left-radius:inherit;border-bottom-left-radius:inherit}.bslib-sidebar-layout.sidebar-right>.sidebar{grid-column:2 / 3;border-right:none;border-left:var(--_vert-border);border-top-left-radius:0;border-bottom-left-radius:0}.bslib-sidebar-layout.sidebar-right>.collapse-toggle{grid-column:2 / 3;left:var(--_icon-size);right:unset;border:var(--bslib-collapse-toggle-border)}.bslib-sidebar-layout.sidebar-right>.collapse-toggle>.collapse-icon{transform:rotateY(var(--_toggle-right-transform))}.bslib-sidebar-layout.transitioning>.sidebar>.sidebar-content{display:none}.bslib-sidebar-layout.sidebar-collapsed{--_toggle-transform: 180deg;--_toggle-right-transform: 0deg;--_vert-border: none;grid-template-columns:0 minmax(0, 1fr)}.bslib-sidebar-layout.sidebar-collapsed.sidebar-right{grid-template-columns:minmax(0, 1fr) 0}.bslib-sidebar-layout.sidebar-collapsed:not(.transitioning)>.sidebar>*{display:none}.bslib-sidebar-layout.sidebar-collapsed>.main{border-radius:inherit;padding-left:var(--_padding-icon);padding-right:var(--_padding-icon)}.bslib-sidebar-layout.sidebar-collapsed>.collapse-toggle{color:var(--_main-fg);top:var(--_toggle-position-y);right:var(--_toggle-position-x)}.bslib-sidebar-layout.sidebar-collapsed.sidebar-right>.collapse-toggle{left:var(--_toggle-position-x);right:unset}.bslib-sidebar-layout .bslib-sidebar-resize-handle{position:absolute;top:0;bottom:0;width:var(--_resize-handle-width);left:calc(calc(-1 * var(--_resize-handle-width)) - 2px);grid-column:2;cursor:ew-resize;user-select:none;z-index:calc(1000 + 1)}.bslib-sidebar-layout .bslib-sidebar-resize-handle::before{content:"";position:absolute;top:0;bottom:0;left:0;right:calc(-1 * var(--_resize-handle-width) - 2px);z-index:calc(1000 + 1)}.bslib-sidebar-layout .bslib-sidebar-resize-handle .resize-indicator{position:absolute;top:50%;right:2px;width:2px;height:30px;transform:translate(-50%, -50%);background-color:var(--_resize-indicator-color);opacity:0.1;border-radius:1px;transition:all 0.15s ease}.bslib-sidebar-layout .bslib-sidebar-resize-handle:hover .resize-indicator,.bslib-sidebar-layout .bslib-sidebar-resize-handle:focus .resize-indicator,.bslib-sidebar-layout .bslib-sidebar-resize-handle:active .resize-indicator,.bslib-sidebar-layout .bslib-sidebar-resize-handle:focus .resize-indicator{opacity:1}.bslib-sidebar-layout .bslib-sidebar-resize-handle:hover .resize-indicator,.bslib-sidebar-layout .bslib-sidebar-resize-handle:focus .resize-indicator{width:3px;height:40px}.bslib-sidebar-layout .bslib-sidebar-resize-handle:active .resize-indicator{background-color:var(--_resize-indicator-color-active);width:4px;height:50px}.bslib-sidebar-layout .bslib-sidebar-resize-handle:focus{outline:none}.bslib-sidebar-layout .bslib-sidebar-resize-handle:focus .resize-indicator{outline:2px solid var(--bs-focus-ring-color, rgba(13,110,253,0.25));outline-offset:2px}.bslib-sidebar-layout.sidebar-right>.bslib-sidebar-resize-handle{left:2px}.bslib-sidebar-layout.transitioning>.bslib-sidebar-resize-handle,.bslib-sidebar-layout.sidebar-collapsed>.bslib-sidebar-resize-handle{display:none}.bslib-sidebar-layout.sidebar-resizing{user-select:none}.bslib-sidebar-layout.sidebar-resizing>.bslib-sidebar-resize-handle .resize-indicator{background-color:var(--_resize-indicator-color-active);width:4px;height:50px}.bslib-sidebar-layout{--bslib-sidebar-js-window-size: desktop}@media (max-width: 575.98px){.bslib-sidebar-layout{--bslib-sidebar-js-window-size: mobile}.bslib-sidebar-layout .bslib-sidebar-resize-handle{display:none !important}}@media (min-width: 576px){.bslib-sidebar-layout[data-collapsible-desktop="false"]{--_padding-icon: var(--_padding)}.bslib-sidebar-layout[data-collapsible-desktop="false"]>.collapse-toggle{display:none}.bslib-sidebar-layout[data-collapsible-desktop="false"]>.sidebar[hidden]{display:block !important}.bslib-sidebar-layout[data-collapsible-desktop="false"]>.sidebar[hidden]>.sidebar-content{display:flex !important}}@media (max-width: 575.98px){.bslib-sidebar-layout>.sidebar,.bslib-sidebar-layout.sidebar-right>.sidebar{border:none}.bslib-sidebar-layout>.main,.bslib-sidebar-layout.sidebar-right>.main{grid-column:1 / 3}.bslib-sidebar-layout[data-collapsible-mobile="true"]{grid-template-rows:calc(var(--_icon-button-size) + var(--_padding)) 1fr;grid-template-columns:100% 0}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.collapse-toggle{grid-row:1 / 2}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.main{grid-row:2 / 3}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.sidebar{grid-row:1 / 3}.bslib-sidebar-layout[data-collapsible-mobile="true"]:not(.sidebar-collapsed)>.sidebar,.bslib-sidebar-layout[data-collapsible-mobile="true"].transitioning>.sidebar{z-index:1045}.bslib-sidebar-layout[data-collapsible-mobile="true"]:not(.sidebar-collapsed)>.collapse-toggle,.bslib-sidebar-layout[data-collapsible-mobile="true"].transitioning>.collapse-toggle{z-index:1045}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.collapse-toggle{top:unset;position:relative;align-self:center}.bslib-sidebar-layout[data-collapsible-mobile="true"]:not(.sidebar-right)>.collapse-toggle{left:var(--_icon-size);right:unset;justify-self:left}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-right>.collapse-toggle{right:var(--_icon-size);left:unset;justify-self:right}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.sidebar{opacity:var(--_sidebar-mobile-opacity, 1);max-width:var(--_sidebar-mobile-max-width, 100%);box-shadow:var(--_sidebar-mobile-box-shadow)}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.sidebar{margin:0;padding-top:var(--_padding-icon)}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.sidebar>.sidebar-content{padding-top:0;height:100%;overflow-y:auto}.bslib-sidebar-layout[data-collapsible-mobile="true"]:not(.sidebar-right)>.sidebar{margin-right:auto}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-right>.sidebar{margin-left:auto}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-right{grid-template-columns:0 100%}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-collapsed{grid-template-columns:0 100%}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-collapsed.sidebar-right{grid-template-columns:100% 0}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.main{padding-top:1px;padding-left:var(--_padding);padding-right:var(--_padding)}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.main{opacity:var(--_main-mobile-expanded-opacity);transition:opacity var(--_transition-easing-x) var(--_transition-duration)}.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-collapsed>.main{opacity:1}.bslib-sidebar-layout[data-collapsible-mobile="true"]>.main{background-color:none}.bslib-sidebar-layout[data-collapsible-mobile="true"],.bslib-sidebar-layout[data-collapsible-mobile="true"].sidebar-collapsed{background-color:var(--_main-bg)}}@media (max-width: 575.98px){.bslib-sidebar-layout[data-collapsible-mobile="false"]{display:block !important;--_padding-icon: var(--_padding);--_vert-border: var(--_border)}.bslib-sidebar-layout[data-collapsible-mobile="false"]>.sidebar[hidden]{display:block !important}.bslib-sidebar-layout[data-collapsible-mobile="false"]>.sidebar[hidden]>.sidebar-content{display:flex !important}.bslib-sidebar-layout[data-collapsible-mobile="false"]>.sidebar{max-height:var(--_mobile-max-height);overflow-y:auto}.bslib-sidebar-layout[data-collapsible-mobile="false"][data-open-mobile="always"]>.sidebar{border-top:var(--_vert-border)}.bslib-sidebar-layout[data-collapsible-mobile="false"][data-open-mobile="always-above"]>.sidebar{border-bottom:var(--_vert-border)}.bslib-sidebar-layout[data-collapsible-mobile="false"]>.collapse-toggle{display:none}}html[data-bslib-sidebar-resizing="true"]{cursor:ew-resize !important;user-select:none !important}.toast{--bslib-toast-shadow: var(--bs-box-shadow);box-shadow:var(--bslib-toast-shadow);position:relative;overflow:hidden}.toast-body:empty{display:none}.text-bg-primary.toast .toast-body .btn-close,.text-bg-secondary.toast .toast-body .btn-close,.text-bg-success.toast .toast-body .btn-close,.text-bg-info.toast .toast-body .btn-close,.text-bg-warning.toast .toast-body .btn-close,.text-bg-danger.toast .toast-body .btn-close,.text-bg-dark.toast .toast-body .btn-close,.text-white.toast .toast-body .btn-close,.text-light.toast .toast-body .btn-close{filter:var(--bs-btn-close-white-filter)}@keyframes bslib-toast-progress{from{transform:scaleX(0)}to{transform:scaleX(1)}}.bslib-toast-progress-bar{position:absolute;top:0;left:0;height:2px;width:100%;pointer-events:none;z-index:1;transform-origin:left;border-radius:inherit;pointer-events:none;background-color:currentColor}.bslib-toolbar{display:flex;align-items:center;gap:0}.bslib-toolbar[data-align="left"]{margin-right:auto;justify-content:start}.bslib-toolbar[data-align="right"]{margin-left:auto;justify-content:end}.bslib-toolbar .bslib-toolbar-input-button{align-items:center;justify-content:center;line-height:1;height:var(--_icon-size, 1.75rem)}.bslib-toolbar .bslib-toolbar-input-button[data-type="label"] .action-label{padding-left:0}.bslib-toolbar .bslib-toolbar-input-button[data-type="icon"]{aspect-ratio:1;line-height:1 !important}.bslib-toolbar .bslib-toolbar-input-button[data-type="icon"] .action-label{padding-left:0}.bslib-toolbar .bslib-toolbar-input-button[data-type="icon"]>.action-icon{display:flex;align-items:center;justify-content:center;line-height:1;margin:0}.bslib-toolbar .bslib-toolbar-spacer{align-self:stretch}.bslib-toolbar .bslib-toolbar-spacer.bslib-toolbar-divider{opacity:0.75}.bslib-toolbar .bslib-toolbar-spacer.bslib-toolbar-divider::before{content:"";display:block;width:var(--divider-width, 2px);height:100%;background-color:var(--bs-border-color);margin:0 auto}.bslib-toolbar,.bslib-toolbar *{font-size:0.9rem}.bslib-toolbar>*{margin-bottom:0 !important;width:auto;align-self:center}.bslib-value-box{container-name:bslib-value-box;container-type:inline-size}.bslib-value-box.default{--bslib-value-box-bg-default: var(--bs-card-bg, #fff);--bslib-value-box-border-color-default: var(--bs-card-border-color, var(--bs-border-color-translucent));color:var(--bslib-value-box-color, var(--bs-body-color));background-color:var(--bslib-value-box-bg, var(--bslib-value-box-bg-default));border-color:var(--bslib-value-box-border-color, var(--bslib-value-box-border-color-default))}.bslib-value-box .value-box-grid{display:grid;grid-template-areas:"left right";align-items:center;overflow:hidden}.bslib-value-box .value-box-showcase{height:100%;max-height:var(---bslib-value-box-showcase-max-h, 100%)}.bslib-value-box .value-box-showcase,.bslib-value-box .value-box-showcase>.html-fill-item{width:100%}.bslib-value-box[data-full-screen="true"] .value-box-showcase{max-height:var(---bslib-value-box-showcase-max-h-fs, 100%)}@media screen and (min-width: 575.98px){@container bslib-value-box (max-width: 300px){.bslib-value-box:not(.showcase-bottom) .value-box-grid{grid-template-columns:1fr !important;grid-template-rows:auto auto;grid-template-areas:"top" "bottom"}.bslib-value-box:not(.showcase-bottom) .value-box-grid .value-box-showcase{grid-area:top !important}.bslib-value-box:not(.showcase-bottom) .value-box-grid .value-box-area{grid-area:bottom !important;justify-content:end}}}.bslib-value-box .value-box-area{justify-content:center;padding:1.5rem 1rem;font-size:.9rem;font-weight:500}.bslib-value-box .value-box-area *{margin-bottom:0;margin-top:0}.bslib-value-box .value-box-title{font-size:1rem;margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2}.bslib-value-box .value-box-title:empty::after{content:'\00a0 '}.bslib-value-box .value-box-value{font-size:calc(1.325rem + .9vw);margin-top:0;margin-bottom:.5rem;font-weight:400;line-height:1.2}@media (min-width: 1200px){.bslib-value-box .value-box-value{font-size:2rem}}.bslib-value-box .value-box-value:empty::after{content:'\00a0 '}.bslib-value-box .value-box-showcase{align-items:center;justify-content:center;margin-top:auto;margin-bottom:auto;padding:1rem}.bslib-value-box .value-box-showcase .bi,.bslib-value-box .value-box-showcase .fa,.bslib-value-box .value-box-showcase .fab,.bslib-value-box .value-box-showcase .fas,.bslib-value-box .value-box-showcase .far{opacity:.85;min-width:50px;max-width:125%}.bslib-value-box .value-box-showcase .bi,.bslib-value-box .value-box-showcase .fa,.bslib-value-box .value-box-showcase .fab,.bslib-value-box .value-box-showcase .fas,.bslib-value-box .value-box-showcase .far{font-size:4rem}.bslib-value-box.showcase-top-right .value-box-grid{grid-template-columns:1fr var(---bslib-value-box-showcase-w, 50%)}.bslib-value-box.showcase-top-right .value-box-grid .value-box-showcase{grid-area:right;margin-left:auto;align-self:start;align-items:end;padding-left:0;padding-bottom:0}.bslib-value-box.showcase-top-right .value-box-grid .value-box-area{grid-area:left;align-self:end}.bslib-value-box.showcase-top-right[data-full-screen="true"] .value-box-grid{grid-template-columns:auto var(---bslib-value-box-showcase-w-fs, 1fr)}.bslib-value-box.showcase-top-right[data-full-screen="true"] .value-box-grid>div{align-self:center}.bslib-value-box.showcase-top-right:not([data-full-screen="true"]) .value-box-showcase{margin-top:0}@container bslib-value-box (max-width: 300px){.bslib-value-box.showcase-top-right:not([data-full-screen="true"]) .value-box-grid .value-box-showcase{padding-left:1rem}}.bslib-value-box.showcase-left-center .value-box-grid{grid-template-columns:var(---bslib-value-box-showcase-w, 30%) auto}.bslib-value-box.showcase-left-center[data-full-screen="true"] .value-box-grid{grid-template-columns:var(---bslib-value-box-showcase-w-fs, 1fr) auto}.bslib-value-box.showcase-left-center:not([data-fill-screen="true"]) .value-box-grid .value-box-showcase{grid-area:left}.bslib-value-box.showcase-left-center:not([data-fill-screen="true"]) .value-box-grid .value-box-area{grid-area:right}.bslib-value-box.showcase-bottom .value-box-grid{grid-template-columns:1fr;grid-template-rows:1fr var(---bslib-value-box-showcase-h, auto);grid-template-areas:"top" "bottom";overflow:hidden}.bslib-value-box.showcase-bottom .value-box-grid .value-box-showcase{grid-area:bottom;padding:0;margin:0}.bslib-value-box.showcase-bottom .value-box-grid .value-box-area{grid-area:top}.bslib-value-box.showcase-bottom[data-full-screen="true"] .value-box-grid{grid-template-rows:1fr var(---bslib-value-box-showcase-h-fs, 2fr)}.bslib-value-box.showcase-bottom[data-full-screen="true"] .value-box-grid .value-box-showcase{padding:1rem} diff --git a/inst/components/scss/toolbar.scss b/inst/components/scss/toolbar.scss index 58b99a206..c05d233cc 100644 --- a/inst/components/scss/toolbar.scss +++ b/inst/components/scss/toolbar.scss @@ -1,5 +1,12 @@ /* Toolbar */ .bslib-toolbar { + --_divider-height: var(--bslib-toolbar-divider-height, 1lh); + --_divider-width: var(--bslib-toolbar-divider-width, 2px); + --_divider-gap: var(--bslib-toolbar-divider-gap, 1rem); + --_divider-color: var(--bslib-toolbar-divider-color, var(--bs-border-color-translucent, rgba(40,70,94,0.1))); + // Sets the height/width (toolbar buttons are square) for toolbar buttons + // Icon size is controlled by font-size + --_toolbar-btn-size: var(--bslib-toolbar-btn-size, 1.75rem); display: flex; align-items: center; gap: 0; @@ -23,13 +30,13 @@ align-items: center; justify-content: center; line-height: 1; // Override Bootstrap's line-height to avoid too much vertical space - height: var(--_icon-size, 1.75rem); // Keep square icon and label + icon button heights consistent + height: var(--_toolbar-btn-size); // Keep square icon and label + icon button heights consistent } // Remove the left padding on label-only buttons so the text is centered .bslib-toolbar-input-button[data-type="label"] { .action-label { - padding-left: 0; + padding-left: 0; } } @@ -37,7 +44,7 @@ .bslib-toolbar-input-button[data-type="icon"] { aspect-ratio: 1; line-height: 1 !important; // Ensure no line-height interference - + // Remove padding on hidden label to keep icon centered .action-label { padding-left: 0; @@ -53,9 +60,47 @@ } } + /* ---- Toolbar Input Switch ---- */ + + .form-group.shiny-input-container:has(.form-switch) { + width: auto; // Overrides default width which adds unnecessary space + } + + .bslib-input-switch.form-check { + margin-bottom: 0; // Remove extra form check to ensure alignment with other items + margin-inline: 0.5rem; // Spacing so it's not crunched against other toolbar items + + // Match button label styling - Bootstrap buttons default to font-weight: 500 + .form-check-label { + font-weight: 500; // Match Bootstrap button default font-weight + line-height: 1; // Match button line-height for consistent vertical alignment + padding-left: 0.25rem; // Add space between switch and label + padding-top: 0; // Remove any default padding that might affect alignment + margin-bottom: 0; // Remove any default margin + } + } + + /* ---- Toolbar Divider ---- */ + + .bslib-toolbar-divider{ + align-self: center; + height: var(--_divider-height); + width: var(--_divider-gap); + + // Use a pseudo-element for the actual line so custom width controls spacing + &::before { + content: ""; + display: block; + width: var(--_divider-width); + height: 100%; + background-color: var(--_divider-color); + margin: 0 auto; + } + } + /* ---- Adjustments to other elements ---- */ - // Ensures uniformity of font sizing in elements and sub-elements (e.g., input select) + // Ensures uniformity of font sizing in elements and sub-elements (e.g., input select) &, & * { font-size: 0.9rem; @@ -67,3 +112,72 @@ align-self: center; } } + + +/* Toolbar Select Input */ +.bslib-toolbar-input-select { + padding-inline: 0.25rem; + height: var(--_toolbar-btn-size, 1.75rem); + display: inline-flex; + align-items: center; + width: auto !important; + border-radius: var(--bs-border-radius-sm, 0.25rem); + gap: 0.05rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + + select { + // Necessary to restore the arrow removed by shiny .form-select class + // appearance:none setting + appearance: auto; + background-image: none; // Removes Bootstrap's arrow to use native arrow + padding: 0.1rem 0.5rem 0.1rem 0.1rem; + border: none; + background-color: transparent; + line-height: 1; + width: auto; + min-width: fit-content; + font-family: inherit; + + // Remove Bootstrap's standard focus styles since we apply them to the container + &:focus { + outline: none; + box-shadow: none; + } + } + + // Hover, active, and focus styling to highlight the select and options on interaction + &:hover { + background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.08); + } + + &:active { + background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.16); + } + + &:focus-within { + background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.12); + // Match the focus styling used by toolbar buttons (uses emphasis color for both border and shadow) + box-shadow: 0 0 0 0.25rem rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.25); + } + + // Icon styling + .bslib-toolbar-icon { + display: inline-flex; + align-items: center; + color: var(--bs-secondary-color); + margin-left: 0.15rem; // Add margin before icon for aesthetics + + &:empty { + display: none; + } + } + + label, label.control-label { + font-weight: 600; + margin-bottom: 0; + } + + .bslib-toolbar-label { + margin-left: 0.15rem; + } +} diff --git a/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2 b/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2 index 8e0eec69f..58e93b075 100644 Binary files a/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2 and b/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2 differ diff --git a/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2 b/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2 index 7bd3c2efc..3cbb8ad79 100644 Binary files a/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2 and b/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2 differ diff --git a/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2 b/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2 index 2c6ba19be..7186c6574 100644 Binary files a/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2 and b/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2 differ diff --git a/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2 b/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2 index c2788c743..6c8d1c003 100644 Binary files a/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2 and b/inst/fonts/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2 differ diff --git a/man/toolbar.Rd b/man/toolbar.Rd index 87e3d048c..42fafb7f8 100644 --- a/man/toolbar.Rd +++ b/man/toolbar.Rd @@ -34,6 +34,9 @@ toolbar( } \seealso{ Other Toolbar components: -\code{\link{toolbar_input_button}()} +\code{\link{toolbar_divider}()}, +\code{\link{toolbar_input_button}()}, +\code{\link{toolbar_input_select}()}, +\code{\link{toolbar_input_switch}()} } \concept{Toolbar components} diff --git a/man/toolbar_divider.Rd b/man/toolbar_divider.Rd new file mode 100644 index 000000000..10989eda6 --- /dev/null +++ b/man/toolbar_divider.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/toolbar.R +\name{toolbar_divider} +\alias{toolbar_divider} +\title{Toolbar: Add a divider to a toolbar} +\usage{ +toolbar_divider(..., width = NULL, gap = NULL) +} +\arguments{ +\item{width}{A CSS length unit specifying the width of the divider line. +Defaults to \code{"2px"} for a sensible dividing line. Pass \verb{0px} for no +divider line.} + +\item{gap}{A CSS length unit defining the spacing around the divider. +Defaults to \code{"1rem"} for sensible fixed spacing.} +} +\description{ +\code{toolbar_divider()} creates a visual divider line with customizable width +and spacing between toolbar elements. +} +\examples{ +\dontshow{if (rlang::is_interactive()) withAutoprint(\{ # examplesIf} +toolbar( + toolbar_input_button(id = "left1", label = "Left"), + toolbar_divider(), + toolbar_input_button(id = "right1", label = "Right") +) + +toolbar( + toolbar_input_button(id = "a", label = "A"), + toolbar_divider(width = "5px", gap = "20px"), + toolbar_input_button(id = "b", label = "B") +) +\dontshow{\}) # examplesIf} +} +\seealso{ +Other Toolbar components: +\code{\link{toolbar}()}, +\code{\link{toolbar_input_button}()}, +\code{\link{toolbar_input_select}()}, +\code{\link{toolbar_input_switch}()} +} +\concept{Toolbar components} diff --git a/man/toolbar_input_button.Rd b/man/toolbar_input_button.Rd index 8e9898009..2a3ae2611 100644 --- a/man/toolbar_input_button.Rd +++ b/man/toolbar_input_button.Rd @@ -18,23 +18,24 @@ toolbar_input_button( \arguments{ \item{id}{The input ID.} -\item{label}{The button label. Used as button text when \code{show_label = TRUE}, -or as an accessibility label when hidden. Also used as the default -tooltip text when \code{tooltip = TRUE}.} +\item{label}{The input label. By default, \code{label} is not shown but is used by +\code{tooltip}. Set \code{show_label = TRUE} to show the label (see \code{tooltip} for +details on how this affects the tooltip behavior).} -\item{icon}{An icon to display in the button. If provided without -\code{show_label = TRUE}, only the icon will be visible.} +\item{icon}{An icon. If provided without \code{show_label = TRUE}, only the icon +will be visible.} -\item{show_label}{Whether to show the label text in the button. If \code{FALSE} -(the default), only the icon is shown (if provided). If \code{TRUE}, the label -text is shown alongside the icon.} +\item{show_label}{Whether to show the label text. If \code{FALSE} (the default), +only the icon is shown (if provided). If \code{TRUE}, the label text is shown +alongside the icon.} -\item{tooltip}{Tooltip text to display when hovering over the button. Can be: +\item{tooltip}{Tooltip text to display when hovering over the input. Can be: \itemize{ -\item \code{TRUE} (default when \code{show_label = FALSE}) - shows a tooltip with the \code{label} text +\item \code{TRUE} (default when \code{show_label = FALSE}) - shows a tooltip with the +\code{label} text \item \code{FALSE} (default when \code{show_label = TRUE}) - no tooltip -\item A character string - shows a tooltip with custom text -Defaults to \code{!show_label}. +\item A character string - shows a tooltip with custom text Defaults to +\code{!show_label}. }} \item{...}{Additional attributes to pass to the button.} @@ -62,6 +63,9 @@ toolbar( } \seealso{ Other Toolbar components: -\code{\link{toolbar}()} +\code{\link{toolbar}()}, +\code{\link{toolbar_divider}()}, +\code{\link{toolbar_input_select}()}, +\code{\link{toolbar_input_switch}()} } \concept{Toolbar components} diff --git a/man/toolbar_input_select.Rd b/man/toolbar_input_select.Rd new file mode 100644 index 000000000..e89752749 --- /dev/null +++ b/man/toolbar_input_select.Rd @@ -0,0 +1,105 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/toolbar.R +\name{toolbar_input_select} +\alias{toolbar_input_select} +\title{Toolbar Input Select} +\usage{ +toolbar_input_select( + id, + label, + choices, + ..., + selected = NULL, + icon = NULL, + show_label = FALSE, + tooltip = !show_label +) +} +\arguments{ +\item{id}{The input ID.} + +\item{label}{The input label. By default, \code{label} is not shown but is used by +\code{tooltip}. Set \code{show_label = TRUE} to show the label (see \code{tooltip} for +details on how this affects the tooltip behavior).} + +\item{choices}{List of values to select from. If elements of the list are +named, then that name --- rather than the value --- is displayed to the +user. It's also possible to group related inputs by providing a named list +whose elements are (either named or unnamed) lists, vectors, or factors. In +this case, the outermost names will be used as the group labels (leveraging +the \verb{} HTML tag) for the elements in the respective sublist. See +the example section for a small demo of this feature.} + +\item{...}{Additional named arguments passed as attributes to the outer +container div.} + +\item{selected}{The initially selected value. If not provided, the first +choice will be selected by default.} + +\item{icon}{An icon. If provided without \code{show_label = TRUE}, only the icon +will be visible.} + +\item{show_label}{Whether to show the label text. If \code{FALSE} (the default), +only the icon is shown (if provided). If \code{TRUE}, the label text is shown +alongside the icon.} + +\item{tooltip}{Tooltip text to display when hovering over the input. Can be: +\itemize{ +\item \code{TRUE} (default when \code{show_label = FALSE}) - shows a tooltip with the +\code{label} text +\item \code{FALSE} (default when \code{show_label = TRUE}) - no tooltip +\item A character string - shows a tooltip with custom text Defaults to +\code{!show_label}. +}} +} +\value{ +Returns a select input control suitable for use in a toolbar. +} +\description{ +Create a select list input control that can be used to choose a single +item from a list of values, suitable for use within a \code{\link[=toolbar]{toolbar()}}. +} +\examples{ +\dontshow{if (rlang::is_interactive()) withAutoprint(\{ # examplesIf} +toolbar( + align = "right", + toolbar_input_select( + id = "select", + label = "Choose option", + choices = c("Option 1", "Option 2", "Option 3"), + selected = "Option 2" + ) +) + +# With custom tooltip +toolbar( + align = "right", + toolbar_input_select( + id = "select", + label = "Choose option", + choices = c("Option 1", "Option 2", "Option 3"), + tooltip = "Select your preferred option from the list" + ) +) + +# With icon and tooltip +toolbar( + align = "right", + toolbar_input_select( + id = "select", + label = "Choose option", + choices = c("Option 1", "Option 2", "Option 3"), + icon = shiny::icon("filter"), + tooltip = "Filter the data" + ) +) +\dontshow{\}) # examplesIf} +} +\seealso{ +Other Toolbar components: +\code{\link{toolbar}()}, +\code{\link{toolbar_divider}()}, +\code{\link{toolbar_input_button}()}, +\code{\link{toolbar_input_switch}()} +} +\concept{Toolbar components} diff --git a/man/toolbar_input_switch.Rd b/man/toolbar_input_switch.Rd new file mode 100644 index 000000000..6d5bca75e --- /dev/null +++ b/man/toolbar_input_switch.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/toolbar.R +\name{toolbar_input_switch} +\alias{toolbar_input_switch} +\title{Add toolbar switch input} +\usage{ +toolbar_input_switch(id, label = NULL, value = FALSE) +} +\arguments{ +\item{id}{The input ID.} + +\item{label}{The label to display next to the switch. If \code{NULL}, no label +is displayed.} + +\item{value}{The initial state of the switch. Default is \code{FALSE}.} +} +\value{ +Returns a switch input suitable for use in a toolbar. +} +\description{ +A switch input designed to fit well in small places such as in a \code{\link[=toolbar]{toolbar()}}. +#' toolbar_input_switch(id = "toggle", label = "Enable", value = TRUE), +toolbar_input_switch(id = "switch", value = FALSE) +) +} +\seealso{ +Other Toolbar components: +\code{\link{toolbar}()}, +\code{\link{toolbar_divider}()}, +\code{\link{toolbar_input_button}()}, +\code{\link{toolbar_input_select}()} +} +\concept{Toolbar components} diff --git a/tests/testthat/_snaps/toolbar.md b/tests/testthat/_snaps/toolbar.md index ca122ae16..26b53d27b 100644 --- a/tests/testthat/_snaps/toolbar.md +++ b/tests/testthat/_snaps/toolbar.md @@ -46,10 +46,10 @@ Output @@ -63,12 +63,12 @@ @@ -81,12 +81,12 @@ Output @@ -98,10 +98,10 @@ Output @@ -113,10 +113,10 @@ Output @@ -128,10 +128,10 @@ Output @@ -143,10 +143,10 @@ Output @@ -160,12 +160,12 @@ @@ -178,12 +178,12 @@ Output @@ -197,12 +197,12 @@ @@ -217,13 +217,310 @@ +# toolbar_divider() creates divider element + + Code + show_raw_html(toolbar_divider()) + Output + + +--- + + Code + show_raw_html(toolbar_divider(gap = "20px")) + Output + + +--- + + Code + show_raw_html(toolbar_divider(width = "5px", gap = "2rem")) + Output + + +# toolbar_input_select() markup snapshots + + Code + show_raw_html(toolbar_input_select(id = "select1", label = "Basic select", + choices = c("A", "B", "C"), tooltip = FALSE)) + Output +
+ + +
+ +--- + + Code + show_raw_html(toolbar_input_select(id = "select2", label = "Select with selected", + choices = c("Option 1", "Option 2", "Option 3"), selected = "Option 2", + tooltip = FALSE)) + Output +
+ + +
+ +--- + + Code + show_raw_html(toolbar_input_select(id = "select3", label = "Select with custom class", + choices = c("X", "Y", "Z"), class = "bg-success-subtle", style = "width: 400px", + tooltip = FALSE)) + Output +
+ + +
+ +# toolbar_input_select() handles grouped choices + + Code + show_raw_html(grouped_select) + Output +
+ + +
+ +# toolbar_input_select() tooltip parameter + + Code + show_raw_html(toolbar_input_select(id = "tooltip_true", label = "My Select Label", + choices = c("A", "B"), tooltip = TRUE)) + Output +
+ + + + + +
+ +--- + + Code + show_raw_html(toolbar_input_select(id = "with_tooltip", label = "With tooltip", + choices = c("A", "B"), tooltip = "This is helpful information")) + Output +
+ + + + + +
+ +# toolbar_input_select() icon parameter + + Code + show_raw_html(toolbar_input_select(id = "with_icon", label = "With icon", + choices = c("A", "B"), icon = shiny::icon("filter"), tooltip = FALSE)) + Output +
+ + +
+ +--- + + Code + show_raw_html(toolbar_input_select(id = "icon_tooltip", label = "Icon and tooltip", + choices = c("A", "B"), icon = shiny::icon("star"), tooltip = "Select an option")) + Output +
+ + + + + +
+ +# toolbar_input_switch() has correct attributes + + Code + show_raw_html(toolbar_input_switch(id = "switch_with_label", label = "Flip", + value = TRUE)) + Output +
+
+ + +
+
+ +--- + + Code + show_raw_html(toolbar_input_switch(id = "switch_no_label", label = NULL, value = FALSE)) + Output +
+
+ + +
+
+ +# toolbar_input_switch() value parameter + + Code + show_raw_html(toolbar_input_switch(id = "default", label = "Default")) + Output +
+
+ + +
+
+ +--- + + Code + show_raw_html(toolbar_input_switch(id = "off", label = "Off", value = FALSE)) + Output +
+
+ + +
+
+ +--- + + Code + show_raw_html(toolbar_input_switch(id = "on", label = "On", value = TRUE)) + Output +
+
+ + +
+
+ +# toolbar_input_switch() label variations + + Code + show_raw_html(toolbar_input_switch(id = "text_label", label = "Enable Feature", + value = FALSE)) + Output +
+
+ + +
+
+ +--- + + Code + show_raw_html(toolbar_input_switch(id = "html_label", label = strong( + "Bold Label"), value = TRUE)) + Output +
+
+ + +
+
+ +--- + + Code + show_raw_html(toolbar_input_switch(id = "no_label", label = NULL, value = FALSE)) + Output +
+
+ + +
+
+ +--- + + Code + show_raw_html(toolbar_input_switch(id = "empty_label", label = "", value = TRUE)) + Output +
+
+ + +
+
+ diff --git a/tests/testthat/test-toolbar.R b/tests/testthat/test-toolbar.R index f0e2322ae..495cd9aa8 100644 --- a/tests/testthat/test-toolbar.R +++ b/tests/testthat/test-toolbar.R @@ -231,3 +231,585 @@ test_that("toolbar_input_button() validates label for accessibility", { ) ) }) + +# Tests for toolbar_divider() # +test_that("toolbar_divider() creates divider element", { + expect_snapshot_html( + toolbar_divider() + ) + expect_snapshot_html( + toolbar_divider(gap = "20px") + ) + expect_snapshot_html( + toolbar_divider(width = "5px", gap = "2rem") + ) +}) + +test_that("toolbar_divider() validates dots are empty", { + expect_error( + toolbar_divider("fake"), + "must be empty" + ) +}) + +# Additional Toolbar Input Select Tests # + +test_that("toolbar_input_select() accepts named attributes in ...", { + tis <- toolbar_input_select( + id = "select", + label = "Choose option", + choices = c("Option 1", "Option 2", "Option 3"), + class = "bg-success-subtle", + `data-test` = "custom", + tooltip = FALSE + ) + + # Check that the outer div (with bslib-toolbar-input-select class) has the custom attributes + expect_match(htmltools::tagGetAttribute(tis, "class"), "bg-success-subtle") + expect_equal(htmltools::tagGetAttribute(tis, "data-test"), "custom") +}) + +test_that("toolbar_input_select() rejects unnamed arguments in ...", { + expect_error( + toolbar_input_select( + id = "select", + label = "Choose option", + choices = c("Option 1", "Option 2", "Option 3"), + "bad" + ), + "All arguments in `...` must be named" + ) +}) + +test_that("toolbar_input_select() has proper label structure", { + tis <- as.tags( + toolbar_input_select( + id = "select", + label = "Choose option", + choices = c("Option 1", "Option 2", "Option 3"), + tooltip = FALSE + ) + ) + + # Check that a