Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Imports:
mime (>= 0.3),
jsonlite (>= 0.9.16),
xtable,
htmltools (>= 0.5.0.9001),
htmltools (>= 0.5.1.9003),
R6 (>= 2.0),
sourcetools,
later (>= 1.0.0),
Expand All @@ -92,7 +92,7 @@ Imports:
withr,
commonmark (>= 1.7),
glue (>= 1.3.2),
bslib (>= 0.2.2.9002),
bslib (>= 0.2.4.9003),
cachem,
ellipsis,
lifecycle (>= 0.2.0)
Expand All @@ -114,7 +114,9 @@ Suggests:
showtext,
sass
Remotes:
r-lib/rlang
r-lib/rlang,
rstudio/bslib,
rstudio/htmltools
URL: https://shiny.rstudio.com/
BugReports: https://github.com/rstudio/shiny/issues
Collate:
Expand Down
147 changes: 57 additions & 90 deletions R/bootstrap.R
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ getCurrentTheme <- function() {
getShinyOption("bootstrapTheme", default = NULL)
}

getCurrentVersion <- function() {
getCurrentThemeVersion <- function() {
theme <- getCurrentTheme()
if (bslib::is_bs_theme(theme)) {
bslib::theme_version(theme)
Expand Down Expand Up @@ -1026,119 +1026,86 @@ buildNavItem <- function(divTag, tabsetId, index) {
divTag$attribs$title <- NULL
list(
divTag = divTag,
liTag = tagFunction(function() {
navItem <- if ("3" %in% getCurrentVersion()) bs3NavItem else bs4NavItem
navItem(id, title, value, icon, active)
})
)
}

buildDropdown <- function(divTag, tabset) {
title <- divTag$title
value <- divTag$menuName
icon <- getIcon(iconClass = divTag$iconClass)
active <- containsSelectedTab(divTag$tabs)
list(
# list of tab content divs from the child tabset
divTag = tabset$content$children,
liTag = tagFunction(function() {
if ("3" %in% getCurrentVersion()) {
bs3NavItemDropdown(title, value, icon, active, tabset$navList)
} else {
# In BS4, dropdown nav anchors can't be wrapped in a <li> tag
# and also need .nav-link replaced with .dropdown-item to be
# styled sensibly
items <- tabset$navList
items$children <- lapply(items$children, function(x) {
# x should be a tagFunction() due to the else block below
x <- if (inherits(x, "shiny.tag.function")) x() else x
# Replace <li class="nav-item"><a class="nav-link"></a></li>
# with <a class="dropdown-item"></a>
if (tagHasClass(x, "nav-item")) {
x <- x$children[[1]]
x$attribs$class <- "dropdown-item"
}
x
})
bs4NavItemDropdown(title, value, icon, active, items)
liTag = tagAddRenderHook(
liTag(id, title, value, icon),
function(x) {
if (isTRUE(getCurrentThemeVersion() >= 4)) {
tagQuery(x)$
addClass("nav-item")$
find("a")$
addClass(c("nav-link", if (active) "active"))$
allTags()
} else {
tagAppendAttributes(x, class = if (active) "active")
}
}
})
)
}


bs3NavItemDropdown <- function(title, value, icon, active, items) {
tags$li(
class = "dropdown",
class = if (active) "active", # BS3
tags$a(
href = "#",
class = "dropdown-toggle",
`data-toggle` = "dropdown",
`data-value` = value,
icon,
title, tags$b(class = "caret")
),
items
)
)
}

bs3NavItem <- function(id, title, value, icon, active) {
liTag <- function(id, title, value, icon) {
tags$li(
class = if (active) "active",
tags$a(
href = paste0("#", id),
`data-toggle` = "tab",
`data-value` = value,
icon,
title
icon, title
)
)
}

bs4NavItemDropdown <- function(title, value, icon, active, items) {
tags$li(
buildDropdown <- function(divTag, tabset) {

navList <- tagAddRenderHook(
tabset$navList,
function(x) {
if (isTRUE(getCurrentThemeVersion() >= 4)) {
tagQuery(x)$
find(".nav-item")$
removeClass("nav-item")$
find(".nav-link")$
removeClass("nav-link")$
addClass("dropdown-item")$
allTags()
} else {
x
}
}
)

active <- containsSelectedTab(divTag$tabs)

dropdown <- tags$li(
class = "dropdown",
class = "nav-item",
tags$a(
href = "#",
class = "dropdown-toggle",
class = "nav-link",
class = if (active) "active",
`data-toggle` = "dropdown",
`data-value` = value,
icon,
title,
tags$b(class = "caret") # TODO: can be removed?
`data-value` = divTag$menuName,
getIcon(iconClass = divTag$iconClass),
divTag$title,
tags$b(class = "caret")
),
items
navList,
.renderHook = function(x) {
if (isTRUE(getCurrentThemeVersion() >= 4)) {
tagQuery(x)$
addClass("nav-item")$
find(".dropdown-toggle")$
addClass("nav-link")$
allTags()
} else {
x
}
}
)
}

bs4NavItem <- function(id, title, value, icon, active) {
tags$li(
class = "nav-item",
tags$a(
class = "nav-link",
class = if (active) "active",
href = paste0("#", id),
`data-toggle` = "tab",
`data-value` = value,
icon,
title
)
list(
divTag = tabset$content$children,
liTag = dropdown
)
}

# TODO: something like this should exist in htmltools
tagHasClass <- function(x, class) {
if (!inherits(x, "shiny.tag")) return(FALSE)
classes <- unlist(x$attribs[names(x$attribs) %in% "class"], use.names = FALSE)
if (!length(classes)) return(FALSE)
classes <- unlist(strsplit(classes, split = "\\s+"), use.names = FALSE)
isTRUE(class %in% classes)
}

#' Create a text output element
#'
#' Render a reactive output variable as text within an application page.
Expand Down
16 changes: 3 additions & 13 deletions inst/www/shared/shiny.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions inst/www/shared/shiny.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion inst/www/shared/shiny.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions inst/www/shared/shiny.min.js.map

Large diffs are not rendered by default.

26 changes: 6 additions & 20 deletions srcts/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1242,10 +1242,8 @@ function main(): void {
function getTargetTabs($tabset, $tabContent, target) {
const dataValue = "[data-value='" + $escape(target) + "']";
const $aTag = $tabset.find("a" + dataValue);
let $liTag = $aTag.parent("li");
// BS3 dropdown anchors are wrapped in <li>, but they can't be in BS4
let $liTag = $aTag.parent();

if ($liTag.length === 0) $liTag = $aTag;
if ($liTag.length === 0) {
throw (
"There is no tabPanel (or navbarMenu) with value" +
Expand All @@ -1262,12 +1260,10 @@ function main(): void {
const $dropdownTabset = $aTag.find("+ ul.dropdown-menu");
const dropdownId = $dropdownTabset.attr("data-tabsetid");

let $dropdownLiTags = $dropdownTabset.find("a[data-toggle='tab']");
// BS3 dropdown anchors are wrapped in <li>, but they can't be in BS4
const $dropdownLiTags = $dropdownTabset.find("a[data-toggle='tab']");

parent("li");

if ($dropdownLiTags.parent("li").length > 0) {
$dropdownLiTags = $dropdownLiTags.parent("li");
}
$dropdownLiTags.each(function (i, el) {
$liTags.push($(el));
});
Expand All @@ -1291,7 +1287,7 @@ function main(): void {
let tabsetId = $parentTabset.attr("data-tabsetid");

const $divTag = $(message.divTag.html);
let $liTag = $(message.liTag.html);
const $liTag = $(message.liTag.html);
const $aTag = $liTag.find("> a");

// Unless the item is being prepended/appended, the target tab
Expand All @@ -1302,12 +1298,6 @@ function main(): void {
if (message.target !== null) {
target = getTargetTabs($tabset, $tabContent, message.target);
$targetLiTag = target.$liTag;
// If the target is a (BS4) .dropdown-item, then we can't insert
// <li class='nav-item'><a class='nav-link'>...</a></li>,
// instead, we need <a class='dropdown-item'>...</a>
if ($targetLiTag.hasClass("dropdown-item")) {
$liTag = $aTag.removeClass("nav-link").addClass("dropdown-item");
}
}

// If the item is to be placed inside a navbarMenu (dropdown),
Expand All @@ -1332,11 +1322,7 @@ function main(): void {
const index = getTabIndex($tabset, tabsetId);
const tabId = "tab-" + tabsetId + "-" + index;

let anchor = $liTag.find("> a");
// BS3 dropdown anchors are wrapped in <li>, but they can't be in BS4

if (anchor.length === 0) anchor = $liTag;
anchor.attr("href", "#" + tabId);
$liTag.find("> a").attr("href", "#" + tabId);
$divTag.attr("id", tabId);
}

Expand Down
Loading