diff --git a/NAMESPACE b/NAMESPACE index 07c1eb683..e81c71c94 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -163,6 +163,7 @@ export(toggle_sidebar) export(toggle_switch) export(toggle_tooltip) export(toolbar) +export(toolbar_input_select) export(tooltip) export(update_popover) export(update_submit_textarea) diff --git a/NEWS.md b/NEWS.md index 45a9d1a03..cb9f30976 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,7 @@ * Added toast notifications based on [Bootstrap's Toast component](https://getbootstrap.com/docs/5.3/components/toasts/): Use `toast()` to create customizable toast objects, `show_toast()` to display a toast message, `hide_toast()` for manual dismissal, and `toast_header()` for structured headers with icons and status indicators. (#1246) * Added a new `toolbar()` component for creating Bootstrap toolbars that can contain buttons, text, and other elements. (#1247) + * Added `toolbar_input_select()`, a select input designed for use within a `toolbar()`. (#1249) ## Improvements and bug fixes diff --git a/R/toolbar.R b/R/toolbar.R index f100b5951..ee76f367b 100644 --- a/R/toolbar.R +++ b/R/toolbar.R @@ -26,3 +26,57 @@ toolbar <- function( tag_require(tag, version = 5, caller = "toolbar()") as_fragment(tag) } + +#' 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()]. This is a +#' wrapper around [shiny::selectInput()] with `selectize = FALSE` and +#' appropriate styling for toolbar usage. +#' +#' @examplesIf rlang::is_interactive() +#' toolbar( +#' align = "right", +#' toolbar_input_select( +#' id = "select", +#' choices = c("Option 1", "Option 2", "Option 3"), +#' selected = "Option 2" +#' ) +#' ) +#' +#' @param id The input ID. +#' @param 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 +#' `` HTML tag) for the elements in the respective sublist. +#' @param selected The initially selected value. If not specified then defaults +#' to the first value. +#' @param width The width of the input, e.g. '400px', or '100%'; see +#' [validateCssUnit()]. +#' +#' @return Returns a select input control suitable for use in a toolbar. +#' +#' @family Toolbar components +#' @export +toolbar_input_select <- function( + id, + choices, + selected = NULL, + width = NULL +) { + htmltools::div( + class = "bslib-toolbar-input-select form-select-sm", + shiny::selectInput( + id, + label = NULL, # Removed for slimline input component + choices = choices, + selected = selected, + multiple = FALSE, + selectize = FALSE, + width = width + ) + ) +} diff --git a/inst/components/dist/components.css b/inst/components/dist/components.css deleted file mode 100644 index 9fe6bbcf8..000000000 --- a/inst/components/dist/components.css +++ /dev/null @@ -1 +0,0 @@ -.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} diff --git a/inst/components/scss/card.scss b/inst/components/scss/card.scss index 30833e7e3..47d2f0572 100644 --- a/inst/components/scss/card.scss +++ b/inst/components/scss/card.scss @@ -31,6 +31,8 @@ flex-direction: row; align-items: center; align-self: stretch; + min-height: 2.5rem; + padding-block: 4px; gap: 0.25rem; // Give the nav flex: 1 so that if the card header contains a nav, it will take all the available space diff --git a/inst/components/scss/toolbar.scss b/inst/components/scss/toolbar.scss index 75f11209d..9b18f9bb6 100644 --- a/inst/components/scss/toolbar.scss +++ b/inst/components/scss/toolbar.scss @@ -2,15 +2,18 @@ .bslib-toolbar { display: flex; align-items: center; + gap: 0; /* ---- Toolbar options ---- */ &[data-align="left"] { margin-right: auto; + justify-content: start; } &[data-align="right"] { margin-left: auto; + justify-content: end; } /* ---- Adjustments to other elements ---- */ @@ -18,7 +21,7 @@ // Ensures uniformity of font sizing in elements and sub-elements (e.g., input select) &, & * { - font-size: 0.8rem; + font-size: 0.9rem; } &>* { @@ -26,11 +29,57 @@ width: auto; align-self: center; } +} + +/* Toolbar Select Input */ +.bslib-toolbar-input-select { + // Decrease this to keep header slim and leave a margin between select & border + padding-block: 2px; + + .form-group.shiny-input-container { + width: auto; + + select { + width: auto; + } + } + + /* Remove the select label visually */ + #select-label { + display: none; + } + + /* Make children take width=auto to size to content */ + &>* { + width: auto; + } + + select { + appearance: auto; + padding-block: 3px; // Decreased to slim down select to fit in header + /* .form-controls takes away the drop-down arrow, this ensures it shows up */ + border: none; + transition: background-color 120ms ease, color 120ms ease; + } + + // Hover, active, and focus styling to highlight the select and options on interaction + // because default styles are disabled to make the input slimmer + & select:hover { + background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.08); + } + + & select:active { + background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.16); + } + + & select:focus, + select:focus-visible { + background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.12); + } - // Card header is flex by default, but card footer is not and must be in order for - // toolbar alignment to work - .card-footer:has(> &) { - display: flex; - align-items: center; + & select option:hover, + & select option:focus, + & select option:checked { + background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.12) !important; } } diff --git a/man/toolbar_input_select.Rd b/man/toolbar_input_select.Rd new file mode 100644 index 000000000..d695c301b --- /dev/null +++ b/man/toolbar_input_select.Rd @@ -0,0 +1,46 @@ +% 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, choices, selected = NULL, width = NULL) +} +\arguments{ +\item{id}{The input ID.} + +\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.} + +\item{selected}{The initially selected value. If not specified then defaults +to the first value.} + +\item{width}{The width of the input, e.g. '400px', or '100\%'; see +\code{\link[htmltools:validateCssUnit]{htmltools::validateCssUnit()}}.} +} +\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()}}. This is a +wrapper around \code{\link[shiny:selectInput]{shiny::selectInput()}} with \code{selectize = FALSE} and +appropriate styling for toolbar usage. +} +\examples{ +\dontshow{if (rlang::is_interactive()) withAutoprint(\{ # examplesIf} +toolbar( + align = "right", + toolbar_input_select( + id = "select", + choices = c("Option 1", "Option 2", "Option 3"), + selected = "Option 2" + ) +) +\dontshow{\}) # examplesIf} +} +\concept{Toolbar components} diff --git a/tests/testthat/_snaps/toolbar.md b/tests/testthat/_snaps/toolbar.md index 2b0f09a1d..a7a59c027 100644 --- a/tests/testthat/_snaps/toolbar.md +++ b/tests/testthat/_snaps/toolbar.md @@ -8,6 +8,16 @@ Item 2 +--- + + Code + show_raw_html(toolbar("Item 1", "Item 2", gap = "10px")) + Output +
+ Item 1 + Item 2 +
+ # toolbar() aligns correctly Code @@ -28,3 +38,20 @@ Item 2 +# toolbar_input_select() basic attributes and defaults + + Code + show_raw_html(toolbar_input_select(id = "select", choices = c("Option 1", + "Option 2", "Option 3"), selected = "Option 2")) + Output +
+
+ +
+ +
+
+
+ diff --git a/tests/testthat/helper-html.R b/tests/testthat/helper-html.R index 9965671bf..7048305d4 100644 --- a/tests/testthat/helper-html.R +++ b/tests/testthat/helper-html.R @@ -3,7 +3,7 @@ show_raw_html <- function(x) { } expect_snapshot_html <- function(x, .envir = parent.frame()) { - x_str <- deparse(substitute(x)) + x_str <- deparse1(substitute(x)) code <- parse(text = sprintf("expect_snapshot(show_raw_html(%s))", x_str)) eval(code, envir = .envir) } diff --git a/tests/testthat/test-toolbar.R b/tests/testthat/test-toolbar.R index b58d13bd5..f0ec3fe08 100644 --- a/tests/testthat/test-toolbar.R +++ b/tests/testthat/test-toolbar.R @@ -1,3 +1,4 @@ +# Tests for toolbar container # test_that("toolbar() basic attributes and defaults", { tb <- as.tags(toolbar(htmltools::span("Test"))) expect_match(htmltools::tagGetAttribute(tb, "class"), "bslib-toolbar") @@ -5,6 +6,9 @@ test_that("toolbar() basic attributes and defaults", { expect_snapshot_html( toolbar("Item 1", "Item 2") ) + expect_snapshot_html( + toolbar("Item 1", "Item 2", gap = "10px") + ) }) test_that("toolbar() aligns correctly", { @@ -18,3 +22,27 @@ test_that("toolbar() aligns correctly", { ) expect_error(toolbar("x", align = "center")) }) + + +# Toolbar Input Select Tests # + +test_that("toolbar_input_select() basic attributes and defaults", { + tis <- as.tags( + toolbar_input_select( + id = "select", + choices = c("Option 1", "Option 2", "Option 3"), + selected = "Option 2" + ) + ) + expect_match( + htmltools::tagGetAttribute(tis, "class"), + "bslib-toolbar-input-select" + ) + expect_snapshot_html( + toolbar_input_select( + id = "select", + choices = c("Option 1", "Option 2", "Option 3"), + selected = "Option 2" + ) + ) +})