diff --git a/DESCRIPTION b/DESCRIPTION
index 7dbcc7ea..99ea6ac4 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -19,11 +19,13 @@ Imports:
htmltools (>= 0.5.1.1),
jsonlite (>= 0.9.16),
fresh,
+ grDevices,
waiter (>= 0.2.3),
httpuv (>= 1.5.2),
lifecycle,
bslib (>= 0.2.4),
- httr
+ httr,
+ UU (>= 1.5.1)
Suggests:
knitr,
rmarkdown,
@@ -35,6 +37,7 @@ Encoding: UTF-8
RoxygenNote: 7.2.1
VignetteBuilder: knitr
Collate:
+ 'aaa_reimports.R'
'feedbacks.R'
'useful-items.R'
'tabs.R'
@@ -56,3 +59,5 @@ Collate:
'skinSelector.R'
'utils.R'
RdMacros: lifecycle
+Remotes:
+ yogat3ch/UU
diff --git a/NAMESPACE b/NAMESPACE
index 7e7da764..f994e1fd 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -1,5 +1,12 @@
# Generated by roxygen2: do not edit by hand
+export("%nin%")
+export("%|%")
+export("%|0|%")
+export("%|legit|%")
+export("%|try|%")
+export("%|zchar|%")
+export("%||%")
export(accordion)
export(accordionItem)
export(actionButton)
@@ -20,6 +27,7 @@ export(boxProfileItem)
export(boxSidebar)
export(bs4Accordion)
export(bs4AccordionItem)
+export(bs4Alert)
export(bs4Badge)
export(bs4Callout)
export(bs4Card)
@@ -82,6 +90,7 @@ export(cardSidebar)
export(carousel)
export(carouselItem)
export(closeAlert)
+export(col2css)
export(column)
export(controlbarItem)
export(controlbarMenu)
@@ -179,7 +188,15 @@ export(userPost)
export(userPostMedia)
export(userPostTagItem)
export(userPostTagItems)
+export(validateColors)
export(valueBox)
export(valueBoxOutput)
+importFrom(UU,`%nin%`)
+importFrom(UU,`%|0|%`)
+importFrom(UU,`%|legit|%`)
+importFrom(UU,`%|try|%`)
+importFrom(UU,`%|zchar|%`)
importFrom(jsonlite,toJSON)
importFrom(lifecycle,deprecated)
+importFrom(rlang,`%|%`)
+importFrom(rlang,`%||%`)
diff --git a/R/aaa_reimports.R b/R/aaa_reimports.R
new file mode 100644
index 00000000..43dbb0b8
--- /dev/null
+++ b/R/aaa_reimports.R
@@ -0,0 +1,28 @@
+#' @title Re-imports
+#' @name Re-imports
+#' @description Useful functions from other packages
+#' @importFrom rlang `%||%` `%|%`
+#' @importFrom UU `%|0|%` `%|try|%` `%|zchar|%` `%|legit|%` `%nin%`
+NULL
+
+
+#' @export
+rlang::`%||%`
+
+#' @export
+rlang::`%|%`
+
+#' @export
+UU::`%|try|%`
+
+#' @export
+UU::`%|0|%`
+
+#' @export
+UU::`%|zchar|%`
+
+#' @export
+UU::`%|legit|%`
+
+#' @export
+UU::`%nin%`
diff --git a/R/cards.R b/R/cards.R
index 878b4548..7d8f0f9e 100644
--- a/R/cards.R
+++ b/R/cards.R
@@ -69,6 +69,7 @@
#' @param closable If TRUE, display a button in the upper right that allows the user to close the box.
#' @param maximizable If TRUE, the card can be displayed in full screen mode.
#' @param icon Header icon. Displayed before title. Expect \code{\link[shiny]{icon}}.
+#' @param tip_icon Tip icon. Tooltip Icon displayed after title. Expect \code{\link[tippy]{tippy}}.
#' @param gradient Whether to allow gradient effect for the background color. Default to FALSE.
#' @param boxToolSize Size of the toolbox: choose among "xs", "sm", "md", "lg".
#' @param elevation Card elevation.
@@ -135,11 +136,29 @@
#' @author David Granjon, \email{dgranjon@@ymail.com}
#'
#' @export
-bs4Card <- function(..., title = NULL, footer = NULL, status = NULL,
- solidHeader = FALSE, background = NULL, width = 6, height = NULL,
- collapsible = TRUE, collapsed = FALSE, closable = FALSE, maximizable = FALSE, icon = NULL,
- gradient = FALSE, boxToolSize = "sm", elevation = NULL, headerBorder = TRUE, label = NULL, dropdownMenu = NULL,
- sidebar = NULL, id = NULL) {
+bs4Card <- function(...,
+ title = NULL,
+ footer = NULL,
+ status = NULL,
+ solidHeader = FALSE,
+ background = NULL,
+ width = 6,
+ height = NULL,
+ collapsible = TRUE,
+ collapsed = FALSE,
+ closable = FALSE,
+ maximizable = FALSE,
+ icon = NULL,
+ tip_icon = NULL,
+ gradient = FALSE,
+ boxToolSize = "sm",
+ elevation = NULL,
+ headerBorder = TRUE,
+ label = NULL,
+ dropdownMenu = NULL,
+ sidebar = NULL,
+ id = NULL) {
+
if (is.null(status)) solidHeader <- TRUE
@@ -242,7 +261,7 @@ bs4Card <- function(..., title = NULL, footer = NULL, status = NULL,
headerTag <- shiny::tags$div(
class = if (headerBorder) "card-header" else "card-header border-0",
- shiny::tags$h3(class = "card-title", icon, title)
+ shiny::tags$h3(class = "card-title", tip_icon, icon, title)
)
headerTag <- shiny::tagAppendChild(headerTag, cardToolTag)
@@ -250,7 +269,6 @@ bs4Card <- function(..., title = NULL, footer = NULL, status = NULL,
# body
bodyTag <- shiny::tags$div(
class = "card-body",
- style = style,
...,
sidebar[[2]]
)
@@ -263,7 +281,7 @@ bs4Card <- function(..., title = NULL, footer = NULL, status = NULL,
)
}
- cardTag <- shiny::tags$div(class = cardCl, id = id)
+ cardTag <- shiny::tags$div(class = cardCl, id = id, style = style)
cardTag <- shiny::tagAppendChildren(cardTag, headerTag, bodyTag, footerTag)
# wrapper
@@ -280,6 +298,54 @@ bs4Card <- function(..., title = NULL, footer = NULL, status = NULL,
json_verbatim = TRUE
)
)
+ # , if (maximizable)
+ # shiny::tags$script(
+ # type = "text/javascript",
+ # paste0("
+ # $(document).ready(function() {
+ # var ids = $('div",ifelse(is.null(id), "", paste0("#",id))," div.card-body div').map(function(){
+ # return $(this).attr('id');
+ # }).get();
+ # function resizeBoxContent(trigger, target) {
+ # var target = '#' + target
+ # $(trigger).on('click', function() {
+ # setTimeout(function() {
+ # var isMaximized = $('html').hasClass('maximized-card');
+ # if (isMaximized) {
+ # $(target).css('height', '100%');
+ # $(target).css('width', 'auto');
+ # } else {
+ # $(target).css('height', '400px');
+ # $(target).css('width', 'auto');
+ # }
+ # console.log('resizing '+ target)
+ # }, 300);
+ # $(target).trigger('resize');
+ # });
+ # };
+ # setTimeout(function() {
+ # ids.map(function(x){
+ # resizeBoxContent('div.card button[data-card-widget=\"maximize\"]', x);
+ # })
+ #
+ # }, 500);
+ # console.log(ids);
+ # });"),
+ # paste0("
+ # $(document).ready(function() {
+ # $('[data-card-widget=\"maximize\"]').on('click', function() {
+ # setTimeout(function() {
+ # var isMaximized = $('html').hasClass('maximized-card');
+ # if (isMaximized) {
+ # window.location.reload()
+ # }
+ # }, 300);
+ # $('",ifelse(is.null(id), "", paste0(id)),"').resize();
+ # });
+ # }
+ # ")
+ # )
+
)
}
@@ -936,7 +1002,6 @@ bs4InfoBox <- function(title, value = NULL, subtitle = NULL, icon = shiny::icon(
elevation = NULL, iconElevation = NULL, tabName = NULL) {
# check conditions
- tagAssert(icon, "i")
if (!is.null(color)) validateStatusPlus(color)
if (is.null(color) && (fill || gradient)) {
@@ -987,12 +1052,16 @@ bs4InfoBox <- function(title, value = NULL, subtitle = NULL, icon = shiny::icon(
}
if (!is.null(iconElevation)) infoBoxIconCl <- paste0(infoBoxIconCl, " elevation-", iconElevation)
- iconTag <- shiny::tags$span(
- class = infoBoxIconCl,
- id = if (!is.null(tabName)) paste0("icon-", tabName),
- # icon
- icon
- )
+ if (!is.null(icon)) {
+ tagAssert(icon, "i")
+ iconTag <- shiny::tags$span(
+ class = infoBoxIconCl,
+ id = if (!is.null(tabName)) paste0("icon-", tabName),
+ # icon
+ icon
+ )
+ } else
+ iconTag <- icon
contentTag <- shiny::tags$div(
diff --git a/R/dashboardHeader.R b/R/dashboardHeader.R
index dba27901..08863b99 100644
--- a/R/dashboardHeader.R
+++ b/R/dashboardHeader.R
@@ -59,23 +59,24 @@ bs4DashNavbar <- function(..., title = NULL, titleWidth = NULL, disable = FALSE,
# by the end user.
if (skin == "dark" && is.null(status)) status <- "gray-dark"
+ .types <- c("li", "a", "button", "div", "shiny.tag", "shiny.tag.list")
if (!is.null(leftUi)) {
if (inherits(leftUi, "shiny.tag.list")) {
lapply(leftUi, function(item) {
- tagAssert(item, type = "li", class = "dropdown")
+ tagAssert(item, type = .types)
})
} else {
- tagAssert(leftUi, type = "li", class = "dropdown")
+ tagAssert(leftUi, type = .types)
}
}
if (!is.null(rightUi)) {
if (inherits(rightUi, "shiny.tag.list")) {
lapply(rightUi, function(item) {
- tagAssert(item, type = "li", class = "dropdown")
+ tagAssert(item, type = .types)
})
} else {
- tagAssert(rightUi, type = "li", class = "dropdown")
+ tagAssert(rightUi, type = .types)
}
}
diff --git a/R/dashboardSidebar.R b/R/dashboardSidebar.R
index 1151d476..714bed2f 100644
--- a/R/dashboardSidebar.R
+++ b/R/dashboardSidebar.R
@@ -367,7 +367,7 @@ bs4SidebarMenuItem <- function(text, ..., icon = NULL, badgeLabel = NULL, badgeC
subItems <- c(list(...), .list)
if (!is.null(icon)) {
- tagAssert(icon, type = "i")
+ tagAssert(icon, type = c("i", "img"))
icon$attribs$class <- paste0(icon$attribs$class, " nav-icon")
}
@@ -412,7 +412,7 @@ bs4SidebarMenuItem <- function(text, ..., icon = NULL, badgeLabel = NULL, badgeC
# needed by leftSidebar.js
`data-start-selected` = if (isTRUE(selected)) 1 else NULL,
icon,
- shiny::tags$p(text, badgeTag)
+ shiny::tags$span(text, badgeTag)
)
)
)
@@ -454,7 +454,7 @@ bs4SidebarMenuItem <- function(text, ..., icon = NULL, badgeLabel = NULL, badgeC
class = "nav-link",
`data-start-selected` = if (isTRUE(selected)) 1 else NULL,
icon,
- shiny::tags$p(
+ shiny::tags$span(
text,
shiny::tags$i(class = "right fas fa-angle-left")
)
@@ -517,7 +517,7 @@ bs4SidebarMenuSubItem <- function(text, tabName = NULL, href = NULL,
# below this is needed by leftSidebar.js
`data-start-selected` = if (isTRUE(selected)) 1 else NULL,
icon,
- shiny::tags$p(text)
+ shiny::tags$span(text)
)
)
}
diff --git a/R/useful-items.R b/R/useful-items.R
index 334feeb8..49ff6254 100644
--- a/R/useful-items.R
+++ b/R/useful-items.R
@@ -57,7 +57,16 @@ bs4Badge <- function(..., color, position = c("left", "right"),
)
}
+#' @title Bootstrap 4 Alert box
+#'
+#' @param style \code{(character)} Inline style parameters to add
+#' @inherit bs4Card params
+#' @export
+bs4Alert <- function(..., status = "primary", style = NULL, id = NULL, width = 6) {
+ bs4Dash:::validateStatus(status)
+ shiny::tags$div(class = paste0("alert alert-",status), role = "alert", ..., style = paste0("margin: 6px 5px 6px 15px;", sapply(\(x) {ifelse(grepl(";$", x), x, paste0(x, ";"))}), id = id))
+}
#' Bootstrap 4 accordion container
@@ -120,10 +129,10 @@ bs4Badge <- function(..., color, position = c("left", "right"),
#' }
#'
#' @export
-bs4Accordion <- function(..., id, width = 12) {
+bs4Accordion <- function(..., id, width = 12, collapse_all = TRUE) {
items <- list(...)
-
+ if (collapse_all) {
# patch that enables a proper accordion behavior
# we add the data-parent non standard attribute to each
# item. Each accordion must have a unique id.
@@ -132,6 +141,12 @@ bs4Accordion <- function(..., id, width = 12) {
items[[i]]$children[[1]]$children[[1]]$children[[1]]$attribs$`data-target` <<- paste0("#collapse_", id, "_", i)
items[[i]]$children[[2]]$attribs[["id"]] <<- paste0("collapse_", id, "_", i)
})
+ } else {
+ lapply(seq_along(items), FUN = function(i) {
+ items[[i]]$children[[1]]$children[[1]]$children[[1]]$attribs$`data-target` <<- paste0("#collapse_", id, "_", i)
+ items[[i]]$children[[2]]$attribs[["id"]] <<- paste0("collapse_", id, "_", i)
+ })
+ }
shiny::tags$div(
class = if (!is.null(width)) paste0("col-sm-", width),
@@ -467,14 +482,19 @@ bs4CarouselItem <- function(..., caption = NULL, active = FALSE) {
#'
#' @param size Progress bar size. NULL, "sm", "xs" or "xxs".
#' @param label Progress label. NULL by default.
-#'
+#' @param id HTML ID. NULL by default
+#' @param style \code{chr} CSS Styles applied to each bar
+#' @param values_cumulative \code{multiProgressBar} only. Whether \code{value} is comprised of cumulative \code{TRUE} values or actual \code{FALSE} values. See details for examples.
#' @md
#' @details For `multiProgressBar()`, `value` can be a vector which
#' corresponds to the progress for each segment within the progress bar.
#' If supplied, `striped`, `animated`, `status`, and `label` must be the
#' same length as `value` or length 1, in which case vector recycling is
-#' used.
-#'
+#' used. The `values_cumulative` argument is described below:
+#' \itemize{
+#' \item{cumulative}{ values = c(10, 20, 30) is interpreted as a 10% width bar, a 20% width bar, and a 30% width bar for a total of 60%}
+#' \item{actual}{ values = c(10, 20, 30) is interpreted as a 10% width bar, a 10% width bar, and a 10% width bar for a total of 30%. These values are scaled by \code{min} & \code{max} arguments.}
+#' }
#' @examples
#' if(interactive()){
#' library(shiny)
@@ -549,42 +569,55 @@ bs4CarouselItem <- function(..., caption = NULL, active = FALSE) {
#' @export
bs4ProgressBar <- function (value, min = 0, max = 100, vertical = FALSE, striped = FALSE,
animated = FALSE, status = "primary", size = NULL,
- label = NULL) {
+ label = NULL,
+ style = NULL,
+ id = NULL) {
- if (!is.null(status)) validateStatusPlus(status)
+ if (!is.null(status)) validateColors(status)
stopifnot(value >= min)
stopifnot(value <= max)
# wrapper class
- progressCl <- if (isTRUE(vertical)) "progress vertical" else "progress mb-3"
+ progressCl <- if (isTRUE(vertical)) "progress vertical" else "progress"
if (!is.null(size)) progressCl <- paste0(progressCl, " progress-", size)
# bar class
barCl <- "progress-bar"
- if (!is.null(status)) barCl <- paste0(barCl, " bg-", status)
+ style <- NULL
+ if (!is.null(status) && status %in% validStatusesPlus) barCl <- paste0(barCl, " bg-", status)
+ else if (status %in% validColorsPlus || is_hex_color(status))
+ style <- paste0(style, "background-color: ", col2css(status), ";")
if (striped) barCl <- paste0(barCl, " progress-bar-striped")
if (animated) barCl <- paste0(barCl, " progress-bar-animated")
# wrapper
barTag <- shiny::tags$div(
+ id = id,
class = barCl,
role = "progressbar",
`aria-valuenow` = value,
`aria-valuemin` = min,
`aria-valuemax` = max,
- style = if (vertical) {
- paste0("height: ", paste0(value, "%"))
- }
- else {
- paste0("width: ", paste0(value, "%"))
- },
+ style = paste0(style, ifelse(vertical, "height: ", "width: "), ((value - min) / (max - min) * 100), "%"),
if(!is.null(label)) label
)
- progressTag <- shiny::tags$div(class = progressCl)
+ progressTag <- shiny::tags$div(id = id, style = style, class = progressCl)
progressTag <- shiny::tagAppendChild(progressTag, barTag)
progressTag
}
+# Scale values to a percentage/decimal by min & max
+val2pct <- function(val, min = 0, max = 100, include_min = TRUE, include_max = FALSE, as_percent = TRUE) {
+ .val <- sort(val)
+ if (include_min)
+ .val <- c(min, .val)
+ if (include_max)
+ .val <- c(.val, max)
+ out <- diff((.val - min) / (max - min))
+ if (as_percent)
+ out <- out * 100
+ out
+}
#' @rdname progress
#' @export
@@ -598,22 +631,36 @@ bs4MultiProgressBar <-
animated = FALSE,
status = "primary",
size = NULL,
- label = NULL
+ label = NULL,
+ id = NULL,
+ values_cumulative = TRUE,
+ style = NULL
) {
status <- verify_compatible_lengths(value, status)
striped <- verify_compatible_lengths(value, striped)
animated <- verify_compatible_lengths(value, animated)
if (!is.null(label)) label <- verify_compatible_lengths(value, label)
- if (!is.null(status)) lapply(status, function(x) validateStatusPlus(x))
+ if (!is.null(status)) lapply(status, function(x) validateColors(x))
stopifnot(all(value >= min))
stopifnot(all(value <= max))
- stopifnot(sum(value) <= max)
+ if (!values_cumulative) {
+ .value <- val2pct(value, min = min, max = max)
+ stopifnot(sum(.value) <= 100)
+ } else {
+ .value <- value
+ stopifnot(sum(value) <= max)
+ }
+
bar_segment <- function(value, striped, animated, status, label) {
# bar class
barCl <- "progress-bar"
- if (!is.null(status)) barCl <- paste0(barCl, " bg-", status)
+ if (!is.null(status) && status %in% validStatusesPlus) barCl <- paste0(barCl, " bg-", status)
+ else if (status %in% validColorsPlus || is_hex_color(status))
+ style <- paste0(style, "background-color: ", col2css(status), ";")
+
+
if (striped) barCl <- paste0(barCl, " progress-bar-striped")
if (animated) barCl <- paste0(barCl, " progress-bar-animated")
@@ -623,22 +670,25 @@ bs4MultiProgressBar <-
`aria-valuenow` = value,
`aria-valuemin` = min,
`aria-valuemax` = max,
- style = if (vertical) {
- paste0("height: ", paste0(value, "%"))
- }
- else {
- paste0("width: ", paste0(value, "%"))
- },
+ style = paste0(style, ifelse(vertical, "height: ", "width: "), value, "%;", ifelse(vertical && values_cumulative, "position:relative;", "")),
if(!is.null(label)) label
)
}
+ if (vertical && values_cumulative) {
+ # the vertical bar has the divs rendered in order so it appears top down. This reverses the order so it appears bottom up as is more intuitive.
+ .value <- rev(.value)
+ status <- rev(status)
+ striped <- rev(striped)
+ animated <- rev(animated)
+ label <- rev(label)
+ }
barSegs <- list()
# progress bar segments
for (i in seq_along(value)) {
barSegs[[i]] <-
bar_segment(
- value[[i]],
+ .value[[i]],
striped[[i]],
animated[[i]],
status[[i]],
@@ -647,9 +697,9 @@ bs4MultiProgressBar <-
}
# wrapper class
- progressCl <- if (isTRUE(vertical)) "progress vertical" else "progress mb-3"
+ progressCl <- if (isTRUE(vertical)) "progress vertical" else "progress"
if (!is.null(size)) progressCl <- paste0(progressCl, " progress-", size)
- progressTag <- shiny::tags$div(class = progressCl)
+ progressTag <- shiny::tags$div(id = id, style = style, class = progressCl)
progressTag <- shiny::tagAppendChild(progressTag, barSegs)
progressTag
}
diff --git a/R/utils.R b/R/utils.R
index 464f7af2..c81de609 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -8,11 +8,6 @@
#' contents.
#' @keywords internal
tagAssert <- function(tag, type = NULL, class = NULL, allowUI = TRUE) {
- if (!inherits(tag, "shiny.tag")) {
- print(tag)
- stop("Expected an object with class 'shiny.tag'.")
- }
-
# Skip dynamic output elements
if (allowUI &&
(hasCssClass(tag, "shiny-html-output") ||
@@ -20,8 +15,8 @@ tagAssert <- function(tag, type = NULL, class = NULL, allowUI = TRUE) {
return()
}
- if (!is.null(type) && tag$name != type) {
- stop("Expected tag to be of type ", type)
+ if (!is.null(type) && !(tag$name %||% "" %in% type || inherits(tag, type))) {
+ stop("Expected tag to be of type(s) ", paste0(type, collapse = ", "))
}
if (!is.null(class)) {
@@ -212,6 +207,52 @@ validateStatusPlus <- function(status) {
#' @keywords internal
validStatusesPlus <- c(validStatuses, validNuances, validColors)
+#' All valid colors
+#' @usage NULL
+#' @format NULL
+#'
+#' @keywords internal
+validColorsPlus <- c(grDevices::colours(), validStatusesPlus)
+
+# is string a hex formatted color
+is_hex_color <- function(color) {
+ grepl("^\\#[a-fA-F0-9]{3,6}", color)
+}
+# is string an rgba formatted color
+is_rgba_color <- function(color) {
+ grepl("^rgba\\(", color)
+}
+
+#' Validate all colors & Bootstrap statuses
+#'
+#' @param color \code{chr} string with status name or color name
+#'
+#' @return \code{lgl}
+#' @export
+#' @seealso grDevices::color
+validateColors <- function(color) {
+ if (color %in% validColorsPlus || is_hex_color(color) || is_rgba_color(color))
+ TRUE
+ else
+ stop("Invalid color: ", color, ". Valid colors are hex or rgba formatted or one of the following: ",
+ paste(validColorsPlus, collapse = ", "), ".")
+}
+
+
+#' Create a CSS rgba declaration for a color name
+#'
+#' @param color \code{chr} Color name, see \link[grDevices]{colors}
+#' @param alpha \code{num} alpha value
+#'
+#' @return \code{chr} css `rgba()` formatted color
+#' @export
+
+col2css <- function(color, alpha = NULL) {
+ if (!is_hex_color(color) && !is_rgba_color(color))
+ paste0("rgba(", paste0(c(grDevices::col2rgb(color), alpha), collapse = ", "), ")")
+ else
+ color
+}
diff --git a/inst/bs4Dash-2.0.2/bs4Dash-old.min.js b/inst/bs4Dash-2.0.2/bs4Dash-old.min.js
new file mode 100644
index 00000000..9f0aca32
--- /dev/null
+++ b/inst/bs4Dash-2.0.2/bs4Dash-old.min.js
@@ -0,0 +1,2 @@
+var accordionBinding=new Shiny.InputBinding;$.extend(accordionBinding,{find:function(a){return $(a).find(".accordion")},getValue:function(a){var t=$(a).find(".active").index()+1;if(0!==t)return t},setValue:function(a,t){$(a).find(".active").removeClass("active"),$(a).children().eq(t-1).addClass("active"),$(a).children().eq(t-1).find('[data-toggle="collapse"]').click(),$(a).trigger("change")},receiveMessage:function(a,t){this.setValue(a,t)},subscribe:function(a,t){$(a).on("change",(function(a){t()})),$(a).find('[data-toggle="collapse"]').on("click",(function(e){$(this).closest(".card").hasClass("active")||$(a).find(".active").removeClass("active"),$(this).closest(".card").addClass("active"),t()}))},unsubscribe:function(a){$(a).off(".accordionBinding")}}),Shiny.inputBindings.register(accordionBinding,"accordion-input");const validStatuses=["primary","secondary","success","info","warning","danger"],validStatusesPlus=["dark","white","lightblue","navy","orange","fuchsia","purple","indigo","gray","gray-dark","pink","maroon","teal","lime","olive","green","yellow","red","blue"];var cardBinding=new Shiny.InputBinding;$.extend(cardBinding,{find:function(a){return $(a).find(".card")},getValue:function(a){var t=$(a).parent().find("script[data-for='"+a.id+"']");t=JSON.parse(t.html());var e,i=$(a).hasClass("collapsed-card"),n=$(a).css("display"),s=$(a).hasClass("maximized-card");return e="none"!==n,s?$(a).find("[data-card-widget = 'collapse']").hide():$(a).find("[data-card-widget = 'collapse']").show(),{collapsible:t.collapsible,collapsed:i,closable:t.closable,visible:e,maximizable:t.maximizable,maximized:s,status:t.status,solidHeader:t.solidHeader,background:t.background,width:t.width,height:t.height}},_updateWidth:function(a,t,e){$(a).parent().toggleClass("col-sm-"+t),$(a).parent().addClass("col-sm-"+e),$(a).trigger("resize")},setValue:function(a,t){var e=$(a).parent().find("script[data-for='"+a.id+"']");if(e=JSON.parse(e.html()),"update"===t.action){var i=$(a).hasClass("user-card"),n=$(a).hasClass("social-card");if(t.options.hasOwnProperty("title")&&t.options.title!==e.title){var s;s="string"!=typeof t.options.title?$.parseHTML(t.options.title[0]):$.parseHTML(t.options.title);var r=$(a).find(".card-tools");n?$(a).find(".user-block").replaceWith($(s)):i?("string"==typeof t.options.title?(s=[s[0],s[2]],$(a).removeClass("widget-user-2").addClass("widget-user"),$(a).find(".widget-user-header").replaceWith($(s[0])),$(s[1]).insertAfter($(a).find(".widget-user-header"))):($(a).removeClass("widget-user").addClass("widget-user-2"),$(a).find(".widget-user-image").remove(),$(a).find(".widget-user-header").replaceWith($(s)),null!==t.options.status&&(t.options.gradient?$(a).find(".widget-user-header").addClass("bg-gradient-",c):$(a).find(".widget-user-header").addClass("bg-",c))),$(a).find(".widget-user-header").prepend($(r))):($(s).hasClass("card-title")||$(s).addClass("card-title"),$(a).find(".card-title").replaceWith($(s))),e.title=t.options.title}if(t.options.hasOwnProperty("collapsible")&&t.options.collapsible!==e.collapsible&&(t.options.collapsible?0===$(a).find('[data-card-widget = "collapse"]').length&&($(a).find(".card-tools.float-right").prepend($('')),e.collapsible=!0):($(a).find('[data-card-widget = "collapse"]').remove(),e.collapsible=!1)),t.options.hasOwnProperty("closable")&&t.options.closable!==e.closable&&(t.options.closable?0===$(a).find('[data-card-widget = "remove"]').length&&(0===$(a).find('[data-card-widget = "maximize"]').length?$(a).find(".card-tools.float-right").append($('')):$('').insertBefore($(a).find('[data-card-widget = "maximize"]')),e.closable=!0):($(a).find('[data-card-widget = "remove"]').remove(),e.closable=!1)),t.options.hasOwnProperty("maximizable")&&t.options.maximizable!==e.maximizable&&(t.options.maximizable?0===$(a).find('[data-card-widget = "maximize"]').length&&($(a).find(".card-tools.float-right").append($('')),e.maximizable=!0):($(a).find('[data-card-widget = "maximize"]').remove(),e.maximizable=!1)),t.options.hasOwnProperty("solidHeader")&&!n&&!i)if(t.options.solidHeader!==e.solidHeader&&$(a).hasClass("card-outline"))$(a).removeClass("card-outline"),e.solidHeader=!0;else if($(a).hasClass("card-outline")||t.options.solidHeader){if($(a).hasClass("card-outline")){o=e.status||t.options.status;t.options.background&&o?($(a).removeClass("card-outline"),e.solidHeader=!0):e.background&&o&&($(a).removeClass("card-outline"),e.solidHeader=!1)}}else{var o=e.status||t.options.status;t.options.background&&o&&(null!==t.options.background||e.background&&o)||($(a).addClass("card-outline"),e.solidHeader=!1)}if(t.options.hasOwnProperty("status")&&!n&&t.options.status!==e.status){var d,l,c;if(null===t.options.status&&null!==e.status){if(i||$(a).removeClass("card-"+e.status),$(a).hasClass("card-outline")&&!i&&$(a).addClass("card-outline"),t.options.background){var u=t.options.background;validStatusesPlus.indexOf(u)>-1?$(a).find(".btn-tool").addClass("bg-"+u):validStatuses.indexOf(u)>-1&&$(a).find(".btn-tool").addClass("btn-"+u)}}else t.options.status&&(i?(l="bg-",t.options.gradient&&(l+="gradient-"),l+=t.options.status,$(a).find(".widget-user-header").addClass(l)):(l="card-"+t.options.status,$(a).addClass(l)),e.status&&(i?(d="bg-",e.gradient&&(d+="gradient-"),d+=e.status,$(a).find(".widget-user-header").removeClass(d)):(d="card-"+e.status,$(a).removeClass(d))),$(a).hasClass("card-outline")&&!i||(validStatusesPlus.indexOf(t.options.status)>-1?$(a).find(".btn-tool").addClass("bg-"+t.options.status):validStatuses.indexOf(t.options.status)>-1&&$(a).find(".btn-tool").addClass("btn-"+t.options.status)));(e.status||e.background)&&(e.status?c=e.status:e.background&&(c=e.background),validStatusesPlus.indexOf(c)>-1?$(a).find(".btn-tool").removeClass("bg-"+c):validStatuses.indexOf(c)>-1&&$(a).find(".btn-tool").removeClass("btn-"+c)),e.status=t.options.status}if(t.options.hasOwnProperty("background")&&t.options.background!==e.background){var b="bg-";if(newBgClass=b,e.background){if(e.gradient&&(b+="gradient-"),b+=e.background,i&&!e.status&&!t.options.status){var h=$(a).find(".widget-user-header");$(h).removeClass(b)}$(a).removeClass(b)}if(t.options.background){if((e.gradient||t.options.gradient)&&(newBgClass+="gradient-"),newBgClass+=t.options.background,i&&!e.status&&!t.options.status){h=$(a).find(".widget-user-header");$(h).addClass(newBgClass)}$(a).addClass(newBgClass)}e.gradient!==t.options.gradient&&void 0!==t.options.gradient&&(e.gradient=t.options.gradient),e.background=t.options.background}t.options.hasOwnProperty("width")&&t.options.width!==e.width&&(this._updateWidth(a,e.width,t.options.width),e.width=t.options.width),t.options.hasOwnProperty("height")&&t.options.height!==e.height&&(null===t.options.height?$(a).find(".card-body").css("height",""):$(a).find(".card-body").css("height",t.options.height),e.height=t.options.height),$(a).parent().find("script[data-for='"+a.id+"']").replaceWith('