import { applyHead, headOf } from "#presentation/renderers/page.renderer"; import { renderContent, renderCopyAll, renderFooterFor, renderTabHeader, renderTabs, } from "#presentation/renderers/tab.renderer"; import { scrollToId, scroller } from "#presentation/components/tab.component"; import { tabFromPath, tabPath } from "#assets/link.assets"; import { ANCHOR_PREFIX } from "#configuration/constants/document.constants"; import type { Disposer } from "#types/base.types"; import { TAB_SELECTED } from "#core/ids/tab.ids"; import type { Tab } from "#types/document.types"; import type { TabbedPage } from "#types/tab.types"; import { createElement } from "#core/factories/element.factory"; import { loadedPage } from "#domain/registries/page.registry"; import { subscribeEvent } from "#core/buses/base.bus"; const PAGE_ATTRIBUTE = "data-tab-page"; const activeTab = function activeTab(definition: TabbedPage): Tab | undefined { const requested = tabFromPath(window.location.pathname); return definition.tabs.find((tab) => tab.id === requested) ?? definition.tabs[0]; }; const scrollToRequested = function scrollToRequested(): void { const target = window.location.hash.slice(ANCHOR_PREFIX.length); if (target.length > 0) { requestAnimationFrame(() => { scrollToId(target); }); } }; const renderBody = function renderBody(root: HTMLElement, definition: TabbedPage): void { const tab = activeTab(definition); if (tab === undefined) { return; } const content = renderContent(definition.layout, tab); const footer = renderFooterFor(definition.layout, tab); const page = createElement("div", { children: [ renderTabHeader(definition.meta), renderTabs(definition.page, definition.tabs, tab.id), content, ...(footer === null ? [] : [footer]), definition.footer(), ], className: "tab-page-content", }); root.replaceChildren(page, renderCopyAll(content)); scrollToRequested(); }; const selectTab = function selectTab(definition: TabbedPage, tab: string): void { const root = document.querySelector(`[${PAGE_ATTRIBUTE}="${definition.page}"]`); if (root === null || !definition.tabs.some((candidate) => candidate.id === tab)) { return; } const path = tabPath(definition.page, definition.tabs, tab); window.history.pushState({}, "", path); applyHead(document, headOf(definition.page, loadedPage(definition.page), path)); scroller()?.scrollTo(0, 0); renderBody(root, definition); }; export const renderTabbedPage = function renderTabbedPage(definition: TabbedPage): Element { const root = createElement("div", { attributes: { [PAGE_ATTRIBUTE]: definition.page }, className: `tab-page ${definition.tone}`, }); renderBody(root, definition); return root; }; export const mountTabSwitching = function mountTabSwitching(definition: TabbedPage): Disposer { return subscribeEvent(TAB_SELECTED, (event) => { if (event.page === definition.page) { selectTab(definition, event.tab); } }); };