xtaothon/docs/_static/tabs.js
2021-06-11 20:24:39 +08:00

142 lines
4.1 KiB
JavaScript

try {
var session = window.sessionStorage || {};
} catch (e) {
var session = {};
}
window.addEventListener("DOMContentLoaded", () => {
const allTabs = document.querySelectorAll('.sphinx-tabs-tab');
const tabLists = document.querySelectorAll('[role="tablist"]');
allTabs.forEach(tab => {
tab.addEventListener("click", changeTabs);
});
tabLists.forEach(tabList => {
tabList.addEventListener("keydown", keyTabs);
});
// Restore group tab selection from session
const lastSelected = session.getItem('sphinx-tabs-last-selected');
if (lastSelected != null) selectNamedTabs(lastSelected);
});
/**
* Key focus left and right between sibling elements using arrows
* @param {Node} e the element in focus when key was pressed
*/
function keyTabs(e) {
const tab = e.target;
let nextTab = null;
if (e.keyCode === 39 || e.keyCode === 37) {
tab.setAttribute("tabindex", -1);
// Move right
if (e.keyCode === 39) {
nextTab = tab.nextElementSibling;
if (nextTab === null) {
nextTab = tab.parentNode.firstElementChild;
}
// Move left
} else if (e.keyCode === 37) {
nextTab = tab.previousElementSibling;
if (nextTab === null) {
nextTab = tab.parentNode.lastElementChild;
}
}
}
if (nextTab !== null) {
nextTab.setAttribute("tabindex", 0);
nextTab.focus();
}
}
/**
* Select or deselect clicked tab. If a group tab
* is selected, also select tab in other tabLists.
* @param {Node} e the element that was clicked
*/
function changeTabs(e) {
// Use this instead of the element that was clicked, in case it's a child
const notSelected = this.getAttribute("aria-selected") === "false";
const positionBefore = this.parentNode.getBoundingClientRect().top;
const notClosable = !this.parentNode.classList.contains("closeable");
deselectTabList(this);
if (notSelected || notClosable) {
selectTab(this);
const name = this.getAttribute("name");
selectNamedTabs(name, this.id);
if (this.classList.contains("group-tab")) {
// Persist during session
session.setItem('sphinx-tabs-last-selected', name);
}
}
const positionAfter = this.parentNode.getBoundingClientRect().top;
const positionDelta = positionAfter - positionBefore;
// Scroll to offset content resizing
window.scrollTo(0, window.scrollY + positionDelta);
}
/**
* Select tab and show associated panel.
* @param {Node} tab tab to select
*/
function selectTab(tab) {
tab.setAttribute("aria-selected", true);
// Show the associated panel
document
.getElementById(tab.getAttribute("aria-controls"))
.removeAttribute("hidden");
}
/**
* Hide the panels associated with all tabs within the
* tablist containing this tab.
* @param {Node} tab a tab within the tablist to deselect
*/
function deselectTabList(tab) {
const parent = tab.parentNode;
const grandparent = parent.parentNode;
Array.from(parent.children)
.forEach(t => t.setAttribute("aria-selected", false));
Array.from(grandparent.children)
.slice(1) // Skip tablist
.forEach(panel => panel.setAttribute("hidden", true));
}
/**
* Select grouped tabs with the same name, but no the tab
* with the given id.
* @param {Node} name name of grouped tab to be selected
* @param {Node} clickedId id of clicked tab
*/
function selectNamedTabs(name, clickedId=null) {
const groupedTabs = document.querySelectorAll(`.sphinx-tabs-tab[name="${name}"]`);
const tabLists = Array.from(groupedTabs).map(tab => tab.parentNode);
tabLists
.forEach(tabList => {
// Don't want to change the tabList containing the clicked tab
const clickedTab = tabList.querySelector(`[id="${clickedId}"]`);
if (clickedTab === null ) {
// Select first tab with matching name
const tab = tabList.querySelector(`.sphinx-tabs-tab[name="${name}"]`);
deselectTabList(tab);
selectTab(tab);
}
})
}
exports.keyTabs = keyTabs;
exports.changeTabs = changeTabs;
exports.selectTab = selectTab;
exports.deselectTabList = deselectTabList;
exports.selectNamedTabs = selectNamedTabs;