Skip to content

Commit a3c2e3a

Browse files
committed
Close #3401: bootstrapLib() now always sets state on render and cleans up post static render
1 parent 6405056 commit a3c2e3a

File tree

1 file changed

+18
-46
lines changed

1 file changed

+18
-46
lines changed

R/bootstrap.R

Lines changed: 18 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ bootstrapPage <- function(..., title = NULL, theme = NULL, lang = NULL) {
5050
# the tagList() contents to avoid breaking user code that makes assumptions
5151
# about the return value https://github.com/rstudio/shiny/issues/3235
5252
if (is_bs_theme(theme)) {
53-
args <- c(bootstrapLib(theme), args)
53+
args <- list(bootstrapLib(theme), args)
5454
ui <- do.call(tagList, args)
5555
} else {
5656
ui <- do.call(tagList, args)
@@ -82,53 +82,25 @@ getLang <- function(ui) {
8282
#' @inheritParams bootstrapPage
8383
#' @export
8484
bootstrapLib <- function(theme = NULL) {
85-
tagFunction(function() {
86-
if (isRunning()) {
85+
# To support static rendering of version dependent markup (e.g.,
86+
# tabsetPanel()), we setCurrentTheme() at the start of the render (since
87+
# bootstrapLib() comes first in bootstrapPage()), and cleanup afterwards if
88+
# shiny isn't running (in that case, since setCurrentTheme() uses
89+
# shinyOptions(), state will be automatically be restored when the app exits)
90+
oldTheme <- NULL
91+
tagList(
92+
.renderHook = function(x) {
93+
oldTheme <<- getCurrentTheme()
8794
setCurrentTheme(theme)
95+
# For refreshing Bootstrap CSS when session$setCurrentTheme() happens
96+
if (isRunning()) registerThemeDependency(bs_theme_deps)
97+
bslib::bs_theme_dependencies(theme)
98+
},
99+
.postRenderHook = function(x) {
100+
if (!isRunning()) setCurrentTheme(oldTheme)
101+
NULL
88102
}
89-
90-
# If we're not compiling Bootstrap Sass (from bslib), return the
91-
# static Bootstrap build.
92-
if (!is_bs_theme(theme)) {
93-
# We'll enter here if `theme` is the path to a .css file, like that
94-
# provided by `shinythemes::shinytheme("darkly")`.
95-
return(bootstrapDependency(theme))
96-
}
97-
98-
# Make bootstrap Sass available so other tagFunction()s (e.g.,
99-
# sliderInput() et al) can resolve their HTML dependencies at render time
100-
# using getCurrentTheme(). Note that we're making an implicit assumption
101-
# that this tagFunction() executes *before* all other tagFunction()s; but
102-
# that should be fine considering that, DOM tree order is preorder,
103-
# depth-first traversal, and at least in the bootstrapPage(theme) case, we
104-
# have control over the relative ordering.
105-
# https://dom.spec.whatwg.org/#concept-tree
106-
# https://stackoverflow.com/a/16113998/1583084
107-
#
108-
# Note also that since this is shinyOptions() (and not options()), the
109-
# option is automatically reset when the app (or session) exits
110-
if (isRunning()) {
111-
registerThemeDependency(bs_theme_deps)
112-
113-
} else {
114-
# Technically, this a potential issue (someone trying to execute/render
115-
# bootstrapLib outside of a Shiny app), but it seems that, in that case,
116-
# you likely have other problems, since sliderInput() et al. already assume
117-
# that Shiny is the one doing the rendering
118-
#warning(
119-
# "It appears `shiny::bootstrapLib()` was rendered outside of an Shiny ",
120-
# "application context, likely by calling `as.tags()`, `as.character()`, ",
121-
# "or `print()` directly on `bootstrapLib()` or UI components that may ",
122-
# "depend on it (e.g., `fluidPage()`, etc). For 'themable' UI components ",
123-
# "(e.g., `sliderInput()`, `selectInput()`, `dateInput()`, etc) to style ",
124-
# "themselves based on the Bootstrap theme, make sure `bootstrapLib()` is ",
125-
# "provided directly to the UI and that the UI is provided direction to ",
126-
# "`shinyApp()` (or `runApp()`)", call. = FALSE
127-
#)
128-
}
129-
130-
bslib::bs_theme_dependencies(theme)
131-
})
103+
)
132104
}
133105

134106
# This is defined outside of bootstrapLib() because registerThemeDependency()

0 commit comments

Comments
 (0)