UNPKG

@mdn/bob

Version:

Builder of Bits aka The MDN Web Docs interactive examples, example builder

165 lines 5.95 kB
import { languageCSS, languageHTML, languageJavaScript, initCodeEditor, } from "./codemirror-editor.js"; const cssEditor = document.getElementById("css-editor"); const htmlEditor = document.getElementById("html-editor"); const jsEditor = document.getElementById("js-editor"); const staticHTMLCode = htmlEditor.querySelector("pre"); const staticCSSCode = cssEditor.querySelector("pre"); const staticJSCode = jsEditor.querySelector("pre"); const tabContainer = document.getElementById("tab-container"); const tabs = tabContainer.querySelectorAll('button[role="tab"]'); const tabList = document.getElementById("tablist"); /** * Hides all tabpanels */ function hideTabPanels() { // get all section with a role of tabpanel const tabPanels = tabContainer.querySelectorAll('[role="tabpanel"]'); // hide all tabpanels for (const panel of tabPanels) { panel.classList.add("hidden"); } } /** * Sets the newly activated tab as active, and ensures that * the previously active tab is unset. * @param {Object} nextActiveTab - The tab to activate * @param {Object} [activeTab] - The current active tab */ function setActiveTab(nextActiveTab, activeTab) { if (activeTab) { // set the currentSelectedTab to false activeTab.setAttribute("aria-selected", "false"); activeTab.setAttribute("tabindex", "-1"); } // set the activated tab to selected nextActiveTab.setAttribute("aria-selected", "true"); nextActiveTab.removeAttribute("tabindex"); nextActiveTab.focus(); } /** * Set the default tab, and shows the relevant panel * @param {Object} tab - The tab to set as default */ function setDefaultTab(tab) { const panel = document.getElementById(tab.id + "-panel"); tab.setAttribute("aria-selected", "true"); tab.removeAttribute("tabindex"); panel.classList.remove("hidden"); panel.setAttribute("aria-hidden", "false"); tab.focus(); } /** * Handles moving focus and activating the next tab in either direction, * based on arrow key events * @param {String} direction - The direction in which to move tab focus * Must be either forward, or reverse. */ function setNextActiveTab(direction) { const activeTab = tabList.querySelector('button[aria-selected="true"]'); // if the direction specified is not valid, simply return if (direction !== "forward" && direction !== "reverse") { return; } if (direction === "forward") { if (activeTab.nextElementSibling instanceof HTMLElement) { setActiveTab(activeTab.nextElementSibling, activeTab); activeTab.nextElementSibling.click(); } else { // reached the last tab, loop back to the first tab setActiveTab(tabs[0]); tabs[0].click(); } } else if (direction === "reverse") { if (activeTab.previousElementSibling instanceof HTMLElement) { setActiveTab(activeTab.previousElementSibling, activeTab); activeTab.previousElementSibling.click(); } else { // reached the first tab, loop around to the last tab setActiveTab(tabs[tabs.length - 1]); tabs[tabs.length - 1].click(); } } } export const editors = { html: { editor: undefined, code: htmlEditor, initialContent: staticHTMLCode.querySelector("code")?.textContent || "", language: languageHTML(), }, css: { editor: undefined, code: cssEditor, initialContent: staticCSSCode.querySelector("code")?.textContent || "", language: languageCSS(), }, js: { editor: undefined, code: jsEditor, initialContent: staticJSCode.querySelector("code")?.textContent || "", language: languageJavaScript(), }, }; /** * Initialise the specified editor if not already initialised * @param {Array} editorTypes - The editors to initialise * @param {Object} defaultTab - The deafult active tab */ export function initEditor(editorTypes, defaultTab) { if (defaultTab) { setDefaultTab(defaultTab); } for (const editorName of editorTypes) { if (!(editorName in editors)) { continue; } // enable relevant tabs const editorData = editors[editorName]; document.getElementById(editorName)?.classList.remove("hidden"); editorData.editor = initCodeEditor(editorData.code, editorData.initialContent, editorData.language); } } /** * Registers the required click and keyboard event listeners */ export function registerEventListeners() { tabList.addEventListener("click", (event) => { const eventTarget = event.target; const role = eventTarget.getAttribute("role"); if (role === "tab") { const activeTab = tabList.querySelector('button[aria-selected="true"]'); const controls = eventTarget.getAttribute("aria-controls") || ""; const selectedPanel = document.getElementById(controls); hideTabPanels(); setActiveTab(eventTarget, activeTab); // now show the selected tabpanel selectedPanel.classList.remove("hidden"); selectedPanel.setAttribute("aria-hidden", "false"); } }); tabList.addEventListener("keyup", (event) => { event.stopPropagation(); switch (event.key) { case "ArrowRight": case "ArrowDown": setNextActiveTab("forward"); break; case "ArrowLeft": case "ArrowUp": setNextActiveTab("reverse"); break; case "Home": setActiveTab(tabs[0]); break; case "End": setActiveTab(tabs[tabs.length - 1]); break; case "default": return; } }); } //# sourceMappingURL=tabby.js.map