Skip to content

Commit e1c02c3

Browse files
committed
add group attribute to panel-tabset for syncing selected tab across many tabsets
1 parent 7ad27fb commit e1c02c3

File tree

3 files changed

+95
-1
lines changed

3 files changed

+95
-1
lines changed

news/changelog-1.1.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
## HTML Format
5151

5252
- Respect toc-depth in the HTML format (bootstrap) rather than always acting as if depth is 3.
53+
- Add `group` attribute to `panel-tabset` for syncing selected tab across many tabsets
5354

5455
## RevealJS Format
5556

src/command/render/filters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ function pdfEngine(options: PandocOptions): string {
611611
}
612612

613613
const kQuartoExtOrganization = "quarto-ext";
614-
const kQuartoExtBuiltIn = ["code-filename"];
614+
const kQuartoExtBuiltIn = ["code-filename", "grouped-tabsets"];
615615

616616
function resolveFilterExtension(
617617
options: PandocOptions,

src/resources/formats/html/quarto.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,99 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
653653
highlightReaderToggle(isReaderMode());
654654
});
655655

656+
// grouped tabsets
657+
window.addEventListener("pageshow", (_event) => {
658+
function getTabSettings() {
659+
const data = localStorage.getItem("quarto-persistent-tabsets-data");
660+
if (!data) {
661+
localStorage.setItem("quarto-persistent-tabsets-data", "{}");
662+
return {};
663+
}
664+
if (data) {
665+
return JSON.parse(data);
666+
}
667+
}
668+
669+
function setTabSettings(data) {
670+
localStorage.setItem(
671+
"quarto-persistent-tabsets-data",
672+
JSON.stringify(data)
673+
);
674+
}
675+
676+
function setTabState(groupName, groupValue) {
677+
const data = getTabSettings();
678+
data[groupName] = groupValue;
679+
setTabSettings(data);
680+
}
681+
682+
function toggleTab(tab, active) {
683+
const tabPanelId = tab.getAttribute("aria-controls");
684+
const tabPanel = document.getElementById(tabPanelId);
685+
if (active) {
686+
tab.classList.add("active");
687+
tabPanel.classList.add("active");
688+
} else {
689+
tab.classList.remove("active");
690+
tabPanel.classList.remove("active");
691+
}
692+
}
693+
694+
function toggleAll(selectedGroup, selectorsToSync) {
695+
for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) {
696+
const active = selectedGroup === thisGroup;
697+
for (const tab of tabs) {
698+
toggleTab(tab, active);
699+
}
700+
}
701+
}
702+
703+
function findSelectorsToSyncByLanguage() {
704+
const result = {};
705+
const tabs = Array.from(
706+
document.querySelectorAll(`div[data-group] a[id^='tabset-']`)
707+
);
708+
for (const item of tabs) {
709+
const div = item.parentElement.parentElement.parentElement;
710+
const group = div.getAttribute("data-group");
711+
if (!result[group]) {
712+
result[group] = {};
713+
}
714+
const selectorsToSync = result[group];
715+
const value = item.innerHTML;
716+
if (!selectorsToSync[value]) {
717+
selectorsToSync[value] = [];
718+
}
719+
selectorsToSync[value].push(item);
720+
}
721+
return result;
722+
}
723+
724+
function setupSelectorSync() {
725+
const selectorsToSync = findSelectorsToSyncByLanguage();
726+
Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => {
727+
Object.entries(tabSetsByValue).forEach(([value, items]) => {
728+
items.forEach((item) => {
729+
item.addEventListener("click", (_event) => {
730+
setTabState(group, value);
731+
toggleAll(value, selectorsToSync[group]);
732+
});
733+
});
734+
});
735+
});
736+
return selectorsToSync;
737+
}
738+
739+
const selectorsToSync = setupSelectorSync();
740+
for (const [group, selectedName] of Object.entries(getTabSettings())) {
741+
const selectors = selectorsToSync[group];
742+
// it's possible that stale state gives us empty selections, so we explicitly check here.
743+
if (selectors) {
744+
toggleAll(selectedName, selectors);
745+
}
746+
}
747+
});
748+
656749
function throttle(func, wait) {
657750
let waiting = false;
658751
return function () {

0 commit comments

Comments
 (0)