# presentation/renderers/tab.renderer.ts

> 121 lines of code and 26 definitions.

Tree: Site tree
Language: typescript
Layer: product
Canonical: https://banes-lab.com/anatomy/tree#file-presentation-renderers-tab-renderer-ts
Source text: https://banes-lab.com/assets/sources/source.410609d5337e09b457224be83a463470123d7ab586c9078e17d86f11e01bbc68.generated.txt

## Definitions

- `footerBar` (lexical_declaration, line 111)
- `renderContent` (lexical_declaration, line 97, exported)
- `sectionJumps` (lexical_declaration, line 121)
- `renderTabContent` (lexical_declaration, line 56, exported)
- `renderTabFooter` (lexical_declaration, line 117, exported)
- `render` (lexical_declaration, line 98, exported)
- `renderJumps` (lexical_declaration, line 102)
- `renderFooterFor` (lexical_declaration, line 125, exported)
- `renderTabHeader` (lexical_declaration, line 24, exported)
- `renderTabs` (lexical_declaration, line 42, exported)
- `renderChapterContent` (lexical_declaration, line 62)
- `renderGlossaryContent` (lexical_declaration, line 68)
- `renderExplorerContent` (lexical_declaration, line 78)
- `renderTreeContent` (lexical_declaration, line 84)
- `DELAY_TITLE` (lexical_declaration, line 19)
- `DELAY_SUBTITLE` (lexical_declaration, line 20)
- `DELAY_TABS` (lexical_declaration, line 21)
- `DELAY_CONTENT` (lexical_declaration, line 22)
- `header` (lexical_declaration, line 25, exported)
- `anchor` (lexical_declaration, line 48, exported)
- `main` (lexical_declaration, line 49, exported)
- `layoutOf` (lexical_declaration, line 74, exported)
- `content` (lexical_declaration, line 85, exported)
- `CONTENT_BY_LAYOUT` (lexical_declaration, line 90)
- `bar` (lexical_declaration, line 112, exported)
- `resolved` (lexical_declaration, line 126, exported)

## Uses

- [core/assets/link.assets.ts](https://banes-lab.com/source/tree/core/assets/link.assets.ts.md)
- [core/factories/element.factory.ts](https://banes-lab.com/source/tree/core/factories/element.factory.ts.md)
- [core/observers/tab.observer.ts](https://banes-lab.com/source/tree/core/observers/tab.observer.ts.md)
- [presentation/components/tab.component.ts](https://banes-lab.com/source/tree/presentation/components/tab.component.ts.md)
- [presentation/renderers/anatomy.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/anatomy.renderer.ts.md)
- [presentation/renderers/chapter.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/chapter.renderer.ts.md)
- [presentation/renderers/element.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/element.renderer.ts.md)
- [presentation/renderers/glossary.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/glossary.renderer.ts.md)
- [presentation/renderers/ontology.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/ontology.renderer.ts.md)
- [presentation/renderers/section.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/section.renderer.ts.md)

## Used by

- [presentation/renderers/search.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/search.renderer.ts.md)

## Source

```typescript
import { FADE_IN, FADE_IN_UP } from "#configuration/constants/element.constants";
import type { Section, Tab, TabLayout, TabbedMeta } from "#types/document.types";
import { appendChildren, createElement } from "#core/factories/element.factory";
import { createJumpButton, createTabButton, scroller } from "#presentation/components/tab.component";
import { renderGlossaryTab, renderLetterJumps } from "#presentation/renderers/glossary.renderer";
import {
    renderTabSection,
    sectionLabel,
    subsectionId,
    subsectionLabel,
} from "#presentation/renderers/section.renderer";
import { animated } from "#presentation/renderers/element.renderer";
import { observePin } from "#core/observers/tab.observer";
import { renderChapter } from "#presentation/renderers/chapter.renderer";
import { renderExplorer } from "#presentation/renderers/ontology.renderer";
import { renderFilesystem } from "#presentation/renderers/anatomy.renderer";
import { tabPath } from "#assets/link.assets";

const DELAY_TITLE = 50;
const DELAY_SUBTITLE = 100;
const DELAY_TABS = 100;
const DELAY_CONTENT = 150;

export const renderTabHeader = function renderTabHeader(meta: TabbedMeta): Element {
    const header = createElement("header", { className: "tab-page-header" });
    if (meta.emblem !== undefined) {
        header.append(
            animated(
                "img",
                { attributes: { alt: meta.title, src: meta.emblem }, className: "tab-page-emblem" },
                FADE_IN,
            ),
        );
    }
    appendChildren(header, [
        animated("h1", { className: "tab-page-title", text: meta.title }, FADE_IN_UP, DELAY_TITLE),
        animated("p", { className: "tab-page-subtitle", text: meta.subtitle }, FADE_IN, DELAY_SUBTITLE),
    ]);
    return header;
};

export const renderTabs = function renderTabs(page: string, tabs: readonly Tab[], active: string): Element {
    const bar = animated("nav", { className: "tab-bar" }, FADE_IN_UP, DELAY_TABS);
    appendChildren(
        bar,
        tabs.map((tab) => createTabButton(page, tab, tab.id === active, tabPath(page, tabs, tab.id))),
    );
    const anchor = createElement("div", { children: [bar], className: "tab-anchor" });
    const main = scroller();
    if (main !== null) {
        void observePin(main, anchor, bar);
    }
    return anchor;
};

export const renderTabContent = function renderTabContent(sections: readonly Section[]): Element {
    const content = animated("div", { className: "tab-content" }, FADE_IN, DELAY_CONTENT);
    appendChildren(content, sections.map(renderTabSection));
    return content;
};

const renderChapterContent = function renderChapterContent(tab: Tab): Element {
    const content = animated("div", { className: "tab-content chapter-content" }, FADE_IN, DELAY_CONTENT);
    content.append(renderChapter(tab));
    return content;
};

const renderGlossaryContent = function renderGlossaryContent(tab: Tab): Element {
    const content = animated("div", { className: "tab-content glossary-content" }, FADE_IN, DELAY_CONTENT);
    content.append(renderGlossaryTab(tab));
    return content;
};

export const layoutOf = function layoutOf(page: TabLayout, tab: Tab): TabLayout {
    return tab.layout ?? page;
};

const renderExplorerContent = function renderExplorerContent(tab: Tab): Element {
    const content = animated("div", { className: "tab-content explorer-content" }, FADE_IN, DELAY_CONTENT);
    content.append(renderExplorer(tab));
    return content;
};

const renderTreeContent = function renderTreeContent(tab: Tab): Element {
    const content = animated("div", { className: "tab-content filesystem-content" }, FADE_IN, DELAY_CONTENT);
    content.append(renderFilesystem(tab));
    return content;
};

const CONTENT_BY_LAYOUT: ReadonlyMap<TabLayout, (tab: Tab) => Element> = new Map([
    ["chapter", renderChapterContent],
    ["glossary", renderGlossaryContent],
    ["ontology", renderExplorerContent],
    ["tree", renderTreeContent],
]);

export const renderContent = function renderContent(layout: TabLayout, tab: Tab): Element {
    const render = CONTENT_BY_LAYOUT.get(layoutOf(layout, tab));
    return render === undefined ? renderTabContent(tab.sections) : render(tab);
};

const renderJumps = function renderJumps(section: Section, sectionIndex: number): readonly Element[] {
    return [
        createJumpButton(sectionLabel(sectionIndex), section.title, section.id),
        ...section.subsections.map((sub, index) =>
            createJumpButton(subsectionLabel(sectionIndex, index), sub.title, subsectionId(section.id, index, sub.id)),
        ),
    ];
};

const footerBar = function footerBar(jumps: readonly Element[]): Element {
    const bar = createElement("div", { className: "tab-bar" });
    appendChildren(bar, jumps);
    return createElement("div", { children: [bar], className: "tab-anchor tab-footer" });
};

export const renderTabFooter = function renderTabFooter(sections: readonly Section[]): Element {
    return footerBar(sections.flatMap(renderJumps));
};

const sectionJumps = function sectionJumps(sections: readonly Section[]): readonly Element[] {
    return sections.map((section, index) => createJumpButton(sectionLabel(index), section.title, section.id));
};

export const renderFooterFor = function renderFooterFor(layout: TabLayout, tab: Tab): Element | null {
    const resolved = layoutOf(layout, tab);
    if (resolved === "tree") {
        return null;
    }
    if (resolved === "glossary") {
        return footerBar(renderLetterJumps(tab));
    }
    if (resolved === "ontology") {
        return footerBar(sectionJumps(tab.sections));
    }
    return renderTabFooter(tab.sections);
};
```
