Skip to content

Commit

Permalink
Merge pull request #4 from rstudio/joe/feature/navbar-compat
Browse files Browse the repository at this point in the history
navbar compatibility
  • Loading branch information
jcheng5 authored Nov 22, 2019
2 parents 023d455 + ef71e10 commit 2175554
Show file tree
Hide file tree
Showing 13 changed files with 638 additions and 80 deletions.
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
^\.Rproj\.user$
inst/node_modules/.yarn-integrity
inst/node_modules/bootswatch/.github
^test-apps$
12 changes: 10 additions & 2 deletions R/themes.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ bs4_themes_join <- function(theme1 = bs4_theme(), theme2 = bs4_theme()) {
bs4_theme(
pre = as_sass(paste0(theme1$pre, theme2$pre)),
post = as_sass(paste0(theme2$post, theme1$post)),
deps = c(theme1$deps, theme1$deps)
deps = c(theme1$deps, theme2$deps)
)
}

Expand Down Expand Up @@ -69,7 +69,15 @@ bs4_theme_bootswatch <- function(theme = "") {
bs4_theme_bs3compat <- function() {
bs4_theme(
pre = sass_file(system.file("bs3compat", "_pre_variables.scss", package = "bootscss")),
post = sass_file(system.file("bs3compat", "_post_variables.scss", package = "bootscss"))
post = sass_file(system.file("bs3compat", "_post_variables.scss", package = "bootscss")),
deps = list(
htmltools::htmlDependency(
"bs3compat", packageVersion("bootscss"),
package = "bootscss",
src = "bs3compat/js",
script = c("tabs.js", "bs3compat.js")
)
)
)
}

Expand Down
7 changes: 7 additions & 0 deletions inst/bs3compat/_components_compat.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.well {
@extend .bg-light; @extend .card; @extend .p-3;
}

.help-text, .help-block {
@extend .form-text; @extend .text-muted;
}
23 changes: 23 additions & 0 deletions inst/bs3compat/_dropdown_compat.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// # DROPDOWNS
//
// In bs3, dropdown menus are .dropdown-menu>li.active>a
// In bs4, dropdown menus are .dropdown-menu>.dropdown-item.active
//
// Also, bs3 dropdowns within tabs/pills are interfered with in bs4 by
// selectors like `.bs-tabs li>a`, making menu items look like tabs.

.dropdown-menu>li>a {
@extend .dropdown-item;
}
.dropdown-menu>li.active>a {
// This @extend works, but it litters `.dropdown-menu>li.active>a` all over
// the bootstrap.css output because it's such a common class. Instead, we
// copy these few properties from from _dropdown.scss.
// @extend .active;
color: $dropdown-link-active-color;
text-decoration: none;
@include gradient-bg($dropdown-link-active-bg);
}
.dropdown-menu>li.divider {
@extend .dropdown-divider;
}
32 changes: 32 additions & 0 deletions inst/bs3compat/_nav_compat.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Fix tab selector borders in bs3.
.nav-tabs>li,
.nav-pills>li {
@extend .nav-item;
}
.nav-tabs>li>a,
.nav-pills>li>a {
@extend .nav-link;
}

// Active tab/pill.
//
// bs3 uses .nav>li.active>a, bs4 uses .nav>li>a.active or .nav>li.show>a.
//
// My original approach to this was making .nav>li.active @extend .show, but
// after a lot of trial and error I could not get it to fully work.
.nav-tabs>li.active>a {
color: $nav-tabs-link-active-color;
background-color: $nav-tabs-link-active-bg;
border-color: $nav-tabs-link-active-border-color;
}
.nav-pills>li.active>a {
color: $nav-pills-link-active-color;
background-color: $nav-pills-link-active-bg;
}

// Support vertical pills
.nav-stacked {
// Don't extend the .flex-column utility, it uses !important
// @extend .flex-column;
flex-direction: column;
}
53 changes: 53 additions & 0 deletions inst/bs3compat/_navbar_compat.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// bs4 navbars require .navbar-expand[-sm|-md|-lg|-xl], but bs3 navbars
// don't have them. This selector matches .navbar without .navbar-expand
// and defaults it to .navbar-expand-sm.
.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) {
@extend .navbar-expand-sm;
}

// Map BS3 navbar positioning to general utilities
.navbar-fixed-top {
@extend .fixed-top;
}
.navbar-fixed-bottom {
@extend .fixed-bottom;
}
.navbar-sticky-top {
@extend .sticky-top;
}

ul.nav.navbar-nav {
flex: 1;
min-width: map-get($container-max-widths, sm) - 30px;
&.navbar-right {
justify-content: end;
}
}

ul.nav.navbar-nav>li:not(.dropdown) {
@extend .nav-item;
}
ul.nav.navbar-nav>li>a {
@extend .nav-link;
}
.navbar.navbar-default {
@extend .navbar-light;
@extend .bg-light;
}
.navbar.navbar-inverse {
color: $navbar-dark-color;
@extend .navbar-dark;
@extend .bg-dark;
}

// Implement bs3 navbar toggler; used in Rmd websites, i.e.
// https://github.com/rstudio/rmarkdown-website/blob/453e1802b32b5baf1c8a67f80947adcc53e49b7f/_navbar.html
.navbar-toggle {
@extend .navbar-toggler;
}
.navbar-toggle>.icon-bar+.icon-bar {
display: none;
}
.navbar-toggle>.icon-bar:first-child {
@extend .navbar-toggler-icon;
}
84 changes: 6 additions & 78 deletions inst/bs3compat/_post_variables.scss
Original file line number Diff line number Diff line change
@@ -1,79 +1,7 @@
.well {
@extend .bg-light; @extend .card; @extend .p-3;
}
@import "components_compat";
@import "dropdown_compat";
@import "navbar_compat";
@import "nav_compat";

// For verbatimTextOutput()
pre.shiny-text-output {
@extend .bg-light; @extend .card; @extend .p-2;
}

// For code inside of showcase mode
pre.shiny-code {
padding: 0.5rem;
}

.help-text, .help-block {
@extend .form-text; @extend .text-muted;
}

/*
/ Shim for navbarMenu() inside tabsetPanel() (or navlistPanel())
/ Note that the additional CSS intentionally makes it virtually impossible
/ to click the <li> tag, so we have "one source of truth" when we clean up
/ active tags in shiny's tab showing logic
/ https://github.com/rstudio/shiny/blob/2d979d0/srcjs/input_binding_tabinput.js#L41-L63
*/
.nav .dropdown-menu li:not(.divider) {
@extend .nav-pills, .dropdown-item;
padding: 0;
a.nav-link {
border: none;
border-radius: 0;
}
a.nav-link:not(.active):hover {
background-color: $light !important;
}
}

.shiny-input-checkboxgroup, .shiny-input-radiogroup {
.checkbox, .radio {
@extend .form-check;
label {
@extend .form-check-label;
}
label > input {
@extend .form-check-input;
}
}

// Since these inline classes don't have a proper div container
// (they're labels), we borrow just the styling we need from
// .form-check-inline
// https://github.com/rstudio/bs4/blob/7aadd19/inst/node_modules/bootstrap/scss/_forms.scss#L227-L240
.checkbox-inline, .radio-inline {
padding-left: 0;
margin-right: $form-check-inline-margin-x;

label > input {
margin-top: 0;
margin-right: $form-check-inline-input-margin-x;
margin-bottom: 0;
}
}
}

.input-daterange .input-group-addon.input-group-prepend.input-group-append {
padding: inherit;
line-height: inherit;
text-shadow: inherit;
border-width: 0;
}

.selectize-input.focus {
@extend .form-control:focus;
}

.selectize-control.multi .selectize-input > div.active {
background: $component-active-bg;
color: $component-active-color;
}
@import "shiny_input";
@import "shiny_misc";
42 changes: 42 additions & 0 deletions inst/bs3compat/_shiny_input.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.shiny-input-checkboxgroup, .shiny-input-radiogroup {
.checkbox, .radio {
@extend .form-check;
label {
@extend .form-check-label;
}
label > input {
@extend .form-check-input;
}
}

// Since these inline classes don't have a proper div container
// (they're labels), we borrow just the styling we need from
// .form-check-inline
// https://github.com/rstudio/bs4/blob/7aadd19/inst/node_modules/bootstrap/scss/_forms.scss#L227-L240
.checkbox-inline, .radio-inline {
padding-left: 0;
margin-right: $form-check-inline-margin-x;

label > input {
margin-top: 0;
margin-right: $form-check-inline-input-margin-x;
margin-bottom: 0;
}
}
}

.input-daterange .input-group-addon.input-group-prepend.input-group-append {
padding: inherit;
line-height: inherit;
text-shadow: inherit;
border-width: 0;
}

.selectize-input.focus {
@extend .form-control:focus;
}

.selectize-control.multi .selectize-input > div.active {
background: $component-active-bg;
color: $component-active-color;
}
9 changes: 9 additions & 0 deletions inst/bs3compat/_shiny_misc.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// For verbatimTextOutput()
pre.shiny-text-output {
@extend .bg-light; @extend .card; @extend .p-2;
}

// For code inside of showcase mode
pre.shiny-code {
padding: 0.5rem;
}
72 changes: 72 additions & 0 deletions inst/bs3compat/js/bs3compat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
(function($) {
if (!$.fn.tab.Constructor.VERSION.match(/^3\./)) {
(console.warn || console.error || console.log)("bs3compat.js couldn't find bs3 tab impl; bs3 tabs will not be properly supported");
return;
}
var bs3TabPlugin = $.fn.tab.noConflict();

if (!$.fn.tab.Constructor.VERSION.match(/^4\./)) {
(console.warn || console.error || console.log)("bs3compat.js couldn't find bs4 tab impl; bs3 tabs will not be properly supported");
return;
}
var bs4TabPlugin = $.fn.tab.noConflict();

var EVENT_KEY = "click.bs.tab.data-api";
var SELECTOR = '[data-toggle="tab"], [data-toggle="pill"]';

$(document).off(EVENT_KEY);
$(document).on(EVENT_KEY, SELECTOR, function(event) {
event.preventDefault();
$(this).tab("show");
});

function TabPlugin(config) {
if ($(this).closest(".nav").find(".nav-item, .nav-link").length === 0) {
// Bootstrap 3 tabs detected
bs3TabPlugin.call($(this), config);
} else {
// Bootstrap 4 tabs detected
bs4TabPlugin.call($(this), config);
}
}

var noconflict = $.fn.tab;
$.fn.tab = TabPlugin;
$.fn.tab.Constructor = bs4TabPlugin.Constructor;
$.fn.tab.noConflict = function() {
$.fn.tab = noconflict;
return TabPlugin;
};

})(jQuery);

// bs3 navbar: li.active > a
// bs4 navbar: li > a.active
// bs3 tabset: li.active > a
// bs4 tabset: li > a.active


(function($) {
/*
* Bootstrap 4 uses poppler.js to choose what direction to show dropdown
* menus, except in the case of navbars; they assume that navbars are always
* at the top of the page, so this isn't necessary. However, Bootstrap 3
* explicitly supported bottom-positioned navbars via .navbar-fixed-bottom,
* and .fixed-bottom works on Bootstrap 4 as well.
*
* We monkeypatch the dropdown plugin's _detectNavbar method to return false
* if we're in a bottom-positioned navbar.
*/
if (!$.fn.dropdown.Constructor.prototype._detectNavbar) {
// If we get here, the dropdown plugin's implementation must've changed.
// Someone will need to go into Bootstrap's dropdown.js.
(console.warn || console.error || console.log)("bs3compat.js couldn't detect the dropdown plugin's _detectNavbar method");
return;
}

var oldDetectNavbar = $.fn.dropdown.Constructor.prototype._detectNavbar;
$.fn.dropdown.Constructor.prototype._detectNavbar = function() {
return oldDetectNavbar.apply(this, this.arguments) &&
!($(this._element).closest('.navbar').filter('.navbar-fixed-bottom, .fixed-bottom').length > 0);
};
})(jQuery);
Loading

0 comments on commit 2175554

Please sign in to comment.