Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework tabset-dropdown to use regular Bootstrap dropdowns #2054

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
56 changes: 0 additions & 56 deletions inst/rmd/h/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -284,62 +284,6 @@
</script>
$endif$

<!-- tabsets -->

<style type="text/css">
.tabset-dropdown > .nav-tabs {
display: inline-table;
max-height: 500px;
min-height: 44px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 4px;
}

.tabset-dropdown > .nav-tabs > li.active:before {
content: "";
font-family: 'Glyphicons Halflings';
display: inline-block;
padding: 10px;
border-right: 1px solid #ddd;
}

.tabset-dropdown > .nav-tabs.nav-tabs-open > li.active:before {
content: "&#xe258;";
border: none;
}

.tabset-dropdown > .nav-tabs.nav-tabs-open:before {
content: "";
font-family: 'Glyphicons Halflings';
display: inline-block;
padding: 10px;
border-right: 1px solid #ddd;
}

.tabset-dropdown > .nav-tabs > li.active {
display: block;
}

.tabset-dropdown > .nav-tabs > li > a,
.tabset-dropdown > .nav-tabs > li > a:focus,
.tabset-dropdown > .nav-tabs > li > a:hover {
border: none;
display: inline-block;
border-radius: 4px;
background-color: transparent;
}

.tabset-dropdown > .nav-tabs.nav-tabs-open > li {
display: block;
float: none;
}

.tabset-dropdown > .nav-tabs > li {
display: none;
}
</style>

<!-- code folding -->
$if(code_menu)$
<style type="text/css">
Expand Down
160 changes: 127 additions & 33 deletions inst/rmd/h/navigation-1.1/tabsets.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


/**
* jQuery Plugin: Sticky Tabs
*
Expand Down Expand Up @@ -75,58 +73,155 @@ window.buildTabsets = function(tocID) {
var tabContent = $('<div class="tab-content"></div>');
$(tabs[0]).before(tabContent);

// build the tabset
var activeTab = 0;
var activeTabIndex = 0;
var activeDropdownTabIndex = 0;
tabs.each(function(i) {

// get the tab div
var tab = $(tabs[i]);

// get the id then sanitize it for use with bootstrap tabs
var id = tab.attr('id');

// see if this is marked as the active tab
if (tab.hasClass('active'))
activeTab = i;
var tabId = tab.attr('id');

// remove any table of contents entries associated with
// this ID (since we'll be removing the heading element)
$("div#" + tocID + " li a[href='#" + id + "']").parent().remove();
$("div#" + tocID + " li a[href='#" + tabId + "']").parent().remove();

// sanitize the id for use with bootstrap tabs
id = id.replace(/[.\/?&!#<>]/g, '').replace(/\s/g, '_');
tab.attr('id', id);
tabId = tabId.replace(/[.\/?&!#<>]/g, '').replace(/\s/g, '_');
tab.attr('id', tabId);

// get the heading element within it, grab it's text, then remove it
var heading = tab.find('h' + tabLevel + ':first');
var headingText = heading.html();
heading.remove();
var tabHeading = tab.find('h' + tabLevel + ':first');
var tabHeadingText = tabHeading.html();
tabHeading.remove();

// build and append the tab list item
var a = $('<a role="tab" data-toggle="tab">' + headingText + '</a>');
a.attr('href', '#' + id);
a.attr('aria-controls', id);
var li = $('<li role="presentation"></li>');
li.append(a);
tabList.append(li);

// set it's attributes
tab.attr('role', 'tabpanel');
tab.addClass('tab-pane');
tab.addClass('tabbed-pane');
if (fade)
tab.addClass('fade');
// see if this is marked as the active tab, or set the first tab by
// default
if (tab.hasClass('active')) {
activeTabIndex = i;
}

// check if this is a dropdown tab and process it accordingly
var dropdown = tab.hasClass("tabset-dropdown");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is comestic maybe but I would put this variable with the others defined above (l. 54) so that all variables are based on class are in the same place in code

if (dropdown) {
li.addClass('dropdown');

// build and append the dropdown toggle
var a = $('<a class="dropdown-toggle" data-toggle="dropdown" href="#">'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var a = $('<a class="dropdown-toggle" data-toggle="dropdown" href="#">'
var a = $('<a class="dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-expanded="false">'

as in bootstrap doc - I think it is better for accessibility

+ tabHeadingText
+ ' <span class="caret"></span></a>');
li.append(a);

// build and append the dropdown menu
var dropdownMenu = $('<ul class="dropdown-menu"></ul>');
li.append(dropdownMenu)

// determine the heading level of the dropdown tabs
var match = tab.attr('class').match(/level(\d) /);
if (match === null)
return;
var dropdownLevel = Number(match[1]);
var dropdownTabLevel = dropdownLevel + 1;

// find all subheadings immediately below
var dropdownTabs = tab.find("div.section.level" + dropdownTabLevel);
if (!dropdownTabs.length)
return;

dropdownTabs.each(function(j) {

// get the dropdown tab div
var dropdownTab = $(dropdownTabs[j]);

// get the id then sanitize it for use with bootstrap tabs
var dropdownId = dropdownTab.attr('id');

// remove any table of contents entries associated with
// this ID (since we'll be removing the heading element)
$("div#" + tocID + " li a[href='#" + dropdownId + "']")
.parent().remove();

// sanitize the id for use with bootstrap tabs
dropdownId = dropdownId
.replace(/[.\/?&!#<>]/g, '')
.replace(/\s/g, '_');
dropdownTab.attr('id', dropdownId);

// get the heading element within it, grab it's text, then remove it
var dropdownHeading = dropdownTab
.find('h' + dropdownTabLevel + ':first');
var dropdownHeadingText = dropdownHeading.html();
dropdownHeading.remove();

// build and append the dropdown tab list item
var dropdownLi = $('<li role="presentation"></li>');
dropdownMenu.append(dropdownLi);
Comment on lines +142 to +163
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make a JS function as this is the same process as for other tabs above but with different parameter ? Refactoring would help later maintaining


// see if this is marked as the active tab
if (dropdownTab.hasClass('active')) {
activeTabIndex = i;
activeDropdownTabIndex = j;
}

// move it into the tab content div
tab.detach().appendTo(tabContent);
// build and append the dropdown tab link
var dropdownA = $('<a role="tab" data-toggle="tab">'
+ dropdownHeadingText
+ '</a>');
dropdownA.attr('href', '#' + dropdownId);
dropdownA.attr('aria-controls', dropdownId);
dropdownLi.append(dropdownA);

// set its attributes
dropdownTab.attr('role', 'tabpanel');
dropdownTab.addClass('tab-pane');
dropdownTab.addClass('tabbed-pane');
if (fade)
dropdownTab.addClass('fade');

// move it into the tab content div
dropdownTab.detach().appendTo(tabContent);
});
} else {
// build and append the tab link
var a = $('<a role="tab" data-toggle="tab">' + tabHeadingText + '</a>');
Comment on lines +190 to +191
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// build and append the tab link
var a = $('<a role="tab" data-toggle="tab">' + tabHeadingText + '</a>');
// build and append the tab link
var a = $('<a role="tab" data-toggle="tab" role="button" aria-expanded="false">' + tabHeadingText + '</a>');

Same as above

a.attr('href', '#' + tabId);
a.attr('aria-controls', tabId);
li.append(a);

// set its attributes
tab.attr('role', 'tabpanel');
tab.addClass('tab-pane');
tab.addClass('tabbed-pane');
if (fade)
tab.addClass('fade');

// move it into the tab content div
tab.detach().appendTo(tabContent);
}
});

// set active tab
$(tabList.children('li')[activeTab]).addClass('active');
var active = $(tabContent.children('div.section')[activeTab]);
active.addClass('active');
// set active dropdown item
var activeTab = $(tabList.children('li')[activeTabIndex])
activeTab.addClass('active');
var contentTabID = activeTab.children('a').attr('aria-controls');

// set active dropdown tab if necessary
if (activeTab.hasClass("dropdown")) {
var activeDropdownTab = $(activeTab.find('li')[activeDropdownTabIndex]);
activeDropdownTab.addClass('active');

var contentTabID = activeDropdownTab.children('a').attr('aria-controls');
}

// set active content tab
$('#'+contentTabID).addClass('active');
if (fade)
active.addClass('in');
$('#'+contentTabID).addClass('in');

if (tabset.hasClass("tabset-sticky"))
tabset.rmarkdownStickyTabs();
Expand All @@ -138,4 +233,3 @@ window.buildTabsets = function(tocID) {
buildTabset($(tabsets[i]));
});
};