A loose federation of distributed, typed datasets
1// grouped tabsets
2
3export function init() {
4 window.addEventListener("pageshow", (_event) => {
5 function getTabSettings() {
6 const data = localStorage.getItem("quarto-persistent-tabsets-data");
7 if (!data) {
8 localStorage.setItem("quarto-persistent-tabsets-data", "{}");
9 return {};
10 }
11 if (data) {
12 return JSON.parse(data);
13 }
14 }
15
16 function setTabSettings(data) {
17 localStorage.setItem(
18 "quarto-persistent-tabsets-data",
19 JSON.stringify(data)
20 );
21 }
22
23 function setTabState(groupName, groupValue) {
24 const data = getTabSettings();
25 data[groupName] = groupValue;
26 setTabSettings(data);
27 }
28
29 function toggleTab(tab, active) {
30 const tabPanelId = tab.getAttribute("aria-controls");
31 const tabPanel = document.getElementById(tabPanelId);
32 if (active) {
33 tab.classList.add("active");
34 tabPanel.classList.add("active");
35 } else {
36 tab.classList.remove("active");
37 tabPanel.classList.remove("active");
38 }
39 }
40
41 function toggleAll(selectedGroup, selectorsToSync) {
42 for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) {
43 const active = selectedGroup === thisGroup;
44 for (const tab of tabs) {
45 toggleTab(tab, active);
46 }
47 }
48 }
49
50 function findSelectorsToSyncByLanguage() {
51 const result = {};
52 const tabs = Array.from(
53 document.querySelectorAll(`div[data-group] a[id^='tabset-']`)
54 );
55 for (const item of tabs) {
56 const div = item.parentElement.parentElement.parentElement;
57 const group = div.getAttribute("data-group");
58 if (!result[group]) {
59 result[group] = {};
60 }
61 const selectorsToSync = result[group];
62 const value = item.innerHTML;
63 if (!selectorsToSync[value]) {
64 selectorsToSync[value] = [];
65 }
66 selectorsToSync[value].push(item);
67 }
68 return result;
69 }
70
71 function setupSelectorSync() {
72 const selectorsToSync = findSelectorsToSyncByLanguage();
73 Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => {
74 Object.entries(tabSetsByValue).forEach(([value, items]) => {
75 items.forEach((item) => {
76 item.addEventListener("click", (_event) => {
77 setTabState(group, value);
78 toggleAll(value, selectorsToSync[group]);
79 });
80 });
81 });
82 });
83 return selectorsToSync;
84 }
85
86 const selectorsToSync = setupSelectorSync();
87 for (const [group, selectedName] of Object.entries(getTabSettings())) {
88 const selectors = selectorsToSync[group];
89 // it's possible that stale state gives us empty selections, so we explicitly check here.
90 if (selectors) {
91 toggleAll(selectedName, selectors);
92 }
93 }
94 });
95}