import {ensureCSS} from "./network.js" import {escapeText} from "./basic.js" import {staticUrl} from "./settings.js" export interface DialogTab { id?: string title: string description?: string template: () => string } export interface DialogTabsOptions { containerId?: string onShow?: (index: number) => void } /** * Reusable tab switcher for dialog content. * * Renders a `.fw-tabs-nav` list plus `.fw-tab-content.fw-tabs-panel` panels and * handles switching between them. */ export class DialogTabs { tabs: DialogTab[] options: DialogTabsOptions constructor(tabs: DialogTab[], options: DialogTabsOptions = {}) { this.tabs = tabs this.options = options ensureCSS(staticUrl("css/fwtoolkit/ui_tabs.css")) } /** * Generate the tab HTML. The caller is responsible for wrapping it in a * container element if needed. */ render(): string { const {containerId} = this.options const wrapperAttrs = containerId ? ` id="${containerId}"` : "" return ` ${this.tabs .map( (tab, index) => `
${tab.template()}
` ) .join("")} ` } /** * Wire click handlers on the tab links inside `container` and show the * first tab. Call this after the rendered HTML has been inserted into the * DOM. */ bind(container: HTMLElement): void { const tabLinks = Array.from( container.querySelectorAll(".fw-tabs-nav .fw-tab-link a") ) tabLinks.forEach((linkEl, index) => { linkEl.addEventListener("click", event => { event.preventDefault() this.showTab(index, container) }) }) this.showTab(0, container) } /** * Show the tab at the given index and hide all others. */ showTab(index: number, container?: HTMLElement): void { if (index < 0 || index >= this.tabs.length) { return } const tabContainer = container || this.findContainer() if (!tabContainer) { return } const targetId = this.tabId(index, this.tabs[index]) tabContainer .querySelectorAll(".fw-tabs-nav .fw-tab-link") .forEach((tabLink, tabIndex) => { if (tabIndex === index) { tabLink.classList.add("fw-current-tab") } else { tabLink.classList.remove("fw-current-tab") } }) tabContainer.querySelectorAll(".fw-tab-content").forEach(contentEl => { if ((contentEl as HTMLElement).id === targetId) { ;(contentEl as HTMLElement).style.display = "" } else { ;(contentEl as HTMLElement).style.display = "none" } }) if (this.options.onShow) { this.options.onShow(index) } } private tabId(index: number, tab: DialogTab): string { return tab.id || `dialog-tab-${index}` } private findContainer(): HTMLElement | null { const {containerId} = this.options if (containerId) { return document.getElementById(containerId) } return document.querySelector(".fw-tabs-nav")?.closest("div") || null } }