# presentation/renderers/page.shell.renderer.ts

> 76 lines of code and 16 definitions.

Tree: Bane's Lab Site
Language: typescript
Layer: product
Canonical: https://banes-lab.com/anatomy/tree#file-presentation-renderers-page-shell-renderer-ts
Source text: https://banes-lab.com/source/tree/presentation/renderers/page.shell.renderer.ts.txt

Listed in [presentation/renderers](https://banes-lab.com/api/source/tree/presentation/renderers.md), after [presentation/renderers/page.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/page.renderer.ts.md) and before [presentation/renderers/panel.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/panel.renderer.ts.md).

## Definitions

- `renderPage` (lexical_declaration, line 36, exported)
- `asStatic` (lexical_declaration, line 27)
- `linkOf` (lexical_declaration, line 48)
- `renderSectionPage` (lexical_declaration, line 52, exported)
- `missingPage` (lexical_declaration, line 16)
- `renderStaticPage` (lexical_declaration, line 44, exported)
- `renderStaticSection` (lexical_declaration, line 75, exported)
- `draw` (lexical_declaration, line 57, exported)
- `shell` (lexical_declaration, line 39, exported)
- `{ seed }` (lexical_declaration, line 53, exported)
- `content` (lexical_declaration, line 55, exported)
- `group` (lexical_declaration, line 59, exported)
- `parts` (lexical_declaration, line 63, exported)
- `definition` (lexical_declaration, line 76, exported)
- `{ layout, tabs }` (lexical_declaration, line 80, exported)
- `tab` (lexical_declaration, line 81, exported)

## Contained in

- [presentation/renderers](https://banes-lab.com/anatomy/tree/folder-presentation-renderers.md)

## Uses

- [configuration/strings/report.strings.ts](https://banes-lab.com/source/tree/configuration/strings/report.strings.ts.md)
- [core/converters/text.converter.ts](https://banes-lab.com/source/tree/core/converters/text.converter.ts.md)
- [core/factories/element.factory.ts](https://banes-lab.com/source/tree/core/factories/element.factory.ts.md)
- [presentation/components/clipboard.component.ts](https://banes-lab.com/source/tree/presentation/components/clipboard.component.ts.md)
- [presentation/components/link.component.ts](https://banes-lab.com/source/tree/presentation/components/link.component.ts.md)
- [presentation/renderers/site.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/site.renderer.ts.md)
- [presentation/renderers/tab.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/tab.renderer.ts.md)

## Used by

- [runtime/coordinators/route.coordinator.ts](https://banes-lab.com/source/tree/runtime/coordinators/route.coordinator.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/tree/folder-configuration-strings.md)
- [core/converters](https://banes-lab.com/anatomy/tree/folder-core-converters.md)
- [core/factories](https://banes-lab.com/anatomy/tree/folder-core-factories.md)
- [presentation/components](https://banes-lab.com/anatomy/tree/folder-presentation-components.md)
- [presentation/renderers](https://banes-lab.com/anatomy/tree/folder-presentation-renderers.md)
- [runtime/coordinators](https://banes-lab.com/anatomy/tree/folder-runtime-coordinators.md)

## Source

```typescript
import { MISSING_PAGE_LINK, MISSING_PAGE_MESSAGE, MISSING_PAGE_TITLE } from "#configuration/strings/page.strings";
import type { RecordLink, SectionSubject } from "#types/page.types";
import { COPY_PAGE_TITLE } from "#configuration/strings/clipboard.strings";
import { HOME_PAGE } from "#core/ids/page.ids";
import { STATIC_RENDER_ATTRIBUTE } from "#configuration/constants/document.constants";
import { createCopyButton } from "#presentation/components/clipboard.component";
import { createElement } from "#core/factories/element.factory";
import { createPageLink } from "#presentation/components/link.component";
import { exportText } from "#core/converters/text.converter";
import { loadedPage } from "#domain/registries/page.registry";
import { missingSection } from "#configuration/strings/report.strings";
import { renderOwnSection } from "#presentation/renderers/tab.renderer";
import { renderSiteFooter } from "#presentation/renderers/site.renderer";
import { secondaryTab } from "#presentation/renderers/page.renderer";

const missingPage = function missingPage(): Element {
    return createElement("div", {
        children: [
            createElement("h1", { text: MISSING_PAGE_TITLE }),
            createElement("p", { text: MISSING_PAGE_MESSAGE }),
            createPageLink(HOME_PAGE, "footer-link", [MISSING_PAGE_LINK]),
        ],
        className: "missing-page",
    });
};

const asStatic = function asStatic<Result>(work: () => Result): Result {
    document.documentElement.setAttribute(STATIC_RENDER_ATTRIBUTE, "");
    try {
        return work();
    } finally {
        document.documentElement.removeAttribute(STATIC_RENDER_ATTRIBUTE);
    }
};

export const renderPage = function renderPage(page: string): readonly Element[] {
    const definition = loadedPage(page);
    const content = definition === undefined ? missingPage() : definition.render();
    const shell = createElement("div", { children: [content], className: "page-content" });
    shell.append(createCopyButton(() => exportText(shell), "copy-button large copy-all", COPY_PAGE_TITLE));
    return [shell, renderSiteFooter(definition?.license)];
};

export const renderStaticPage = function renderStaticPage(page: string): readonly Element[] {
    return asStatic(() => renderPage(page));
};

const linkOf = function linkOf(link: RecordLink): Element {
    return createElement("a", { attributes: { href: link.path ?? "" }, text: link.label });
};

export const renderSectionPage = function renderSectionPage(subject: SectionSubject): Element {
    const { seed } = subject;
    const definition = loadedPage(seed.page);
    const content = definition?.content.kind === "tabbed" ? definition.content : null;
    const tab = content?.tabs.find((candidate) => candidate.id === seed.tab);
    const draw = (): Element | null =>
        content === null || tab === undefined ? null : renderOwnSection(content.layout, tab, seed.section, seed.rows);
    const group = asStatic(draw);
    if (group === null) {
        throw new Error(missingSection(seed.page, seed.tab, seed.section));
    }
    const parts = subject.parts.map((link) => createElement("li", { children: [linkOf(link)] }));
    return createElement("article", {
        children: [
            createElement("nav", { children: [linkOf({ label: seed.tabTitle, path: seed.tabPath })] }),
            createElement("h1", { text: seed.tabTitle }),
            group,
            ...(parts.length < 2 ? [] : [createElement("ul", { children: parts, className: "section-parts" })]),
        ],
        className: "section-page",
    });
};

export const renderStaticSection = function renderStaticSection(page: string, path: string, id: string): Element | null {
    const definition = loadedPage(page);
    if (definition?.content.kind !== "tabbed") {
        return null;
    }
    const { layout, tabs } = definition.content;
    const tab = secondaryTab(definition, path) ?? tabs[0];
    return tab === undefined ? null : asStatic(() => renderOwnSection(layout, tab, id));
};
```
