# presentation/renderers/chapter.renderer.ts

> 89 lines of code and 20 definitions.

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

## Definitions

- `renderProseBlocks` (lexical_declaration, line 23)
- `renderChapterSection` (lexical_declaration, line 55, exported)
- `renderSubsection` (lexical_declaration, line 46)
- `renderChapter` (lexical_declaration, line 93, exported)
- `renderFooter` (lexical_declaration, line 35)
- `labelled` (lexical_declaration, line 41)
- `isPanel` (lexical_declaration, line 19)
- `panelBlocksOf` (lexical_declaration, line 27)
- `renderPanels` (lexical_declaration, line 31)
- `footer` (lexical_declaration, line 36)
- `heading` (lexical_declaration, line 47)
- `held` (lexical_declaration, line 48)
- `prose` (lexical_declaration, line 56, exported)
- `panels` (lexical_declaration, line 73, exported)
- `narrow` (lexical_declaration, line 75, exported)
- `wide` (lexical_declaration, line 76, exported)
- `beside` (lexical_declaration, line 77, exported)
- `below` (lexical_declaration, line 78, exported)
- `children` (lexical_declaration, line 79, exported)
- `article` (lexical_declaration, line 94, exported)

## Uses

- [core/factories/element.factory.ts](https://banes-lab.com/source/tree/core/factories/element.factory.ts.md)
- [core/registries/style.registry.ts](https://banes-lab.com/source/tree/core/registries/style.registry.ts.md)
- [presentation/renderers/panel.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/panel.renderer.ts.md)
- [presentation/renderers/section.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/section.renderer.ts.md)
- [presentation/renderers/text.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/text.renderer.ts.md)

## Used by

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

## Source

```typescript
import {
    PAIRED_SECTION_CLASS,
    PANEL_COLUMN_CLASS,
    PANEL_COLUMN_LIMIT,
    PANEL_FOOTER_CLASS,
    PANEL_FOOTER_COLUMNS_PROPERTY,
    PANEL_KINDS,
} from "#configuration/constants/chapter.constants";
import type { Section, Subsection, Tab } from "#types/document.types";
import { appendChildren, createElement } from "#core/factories/element.factory";
import { renderChapterPanel, resolveCitations } from "#presentation/renderers/panel.renderer";
import { sectionLabel, subsectionId, subsectionLabel } from "#presentation/renderers/section.renderer";
import type { Block } from "#types/block.types";
import type { PanelReference } from "#types/panel.types";
import { declareStyle } from "#core/registries/style.registry";
import { fillMarkup } from "#presentation/renderers/text.renderer";
import { renderBlock } from "#presentation/renderers/block.renderer";

const isPanel = function isPanel(block: Block): boolean {
    return PANEL_KINDS.has(block.kind);
};

const renderProseBlocks = function renderProseBlocks(blocks: readonly Block[]): Element[] {
    return blocks.filter((block) => !isPanel(block)).map(renderBlock);
};

const panelBlocksOf = function panelBlocksOf(section: Section): Block[] {
    return [...(section.blocks ?? []), ...section.subsections.flatMap((sub) => sub.blocks ?? [])].filter(isPanel);
};

const renderPanels = function renderPanels(section: Section, label: string): PanelReference[] {
    return panelBlocksOf(section).map((block, index) => renderChapterPanel(block, section.id, label, index));
};

const renderFooter = function renderFooter(panels: readonly Element[]): HTMLDivElement {
    const footer = createElement("div", { children: panels, className: PANEL_FOOTER_CLASS });
    declareStyle(footer, PANEL_FOOTER_COLUMNS_PROPERTY, String(Math.min(panels.length, PANEL_COLUMN_LIMIT)));
    return footer;
};

const labelled = function labelled(heading: Element, label: string): Element {
    heading.prepend(createElement("span", { className: "subsection-symbol chapter-label", text: label }));
    return heading;
};

const renderSubsection = function renderSubsection(sub: Subsection, id: string, label: string): Element[] {
    const heading = createElement("h3", { attributes: { id }, className: "chapter-subsection-title", text: sub.title });
    const held: Element[] = [labelled(heading, label)];
    if (sub.content !== undefined) {
        held.push(fillMarkup(createElement("p", { className: "chapter-text" }), sub.content));
    }
    return [...held, ...renderProseBlocks(sub.blocks ?? [])];
};

export const renderChapterSection = function renderChapterSection(section: Section, sectionIndex: number): Element {
    const prose = createElement("div", { className: "chapter-prose" });
    prose.append(
        labelled(
            createElement("h2", { className: "chapter-section-title", text: section.title }),
            sectionLabel(sectionIndex),
        ),
    );
    if (section.intro !== undefined) {
        prose.append(fillMarkup(createElement("p", { className: "chapter-text chapter-lead" }), section.intro));
    }
    appendChildren(prose, renderProseBlocks(section.blocks ?? []));
    appendChildren(
        prose,
        section.subsections.flatMap((sub, index) =>
            renderSubsection(sub, subsectionId(section.id, index, sub.id), subsectionLabel(sectionIndex, index)),
        ),
    );
    const panels = renderPanels(section, sectionLabel(sectionIndex));
    resolveCitations(prose, panels);
    const narrow = panels.filter((panel) => !panel.wide).map((panel) => panel.element);
    const wide = panels.filter((panel) => panel.wide).map((panel) => panel.element);
    const beside = narrow.slice(0, PANEL_COLUMN_LIMIT);
    const below = [...wide, ...narrow.slice(PANEL_COLUMN_LIMIT)];
    const children = [prose];
    if (beside.length > 0) {
        children.push(createElement("div", { children: beside, className: PANEL_COLUMN_CLASS }));
    }
    if (below.length > 0) {
        children.push(renderFooter(below));
    }
    return createElement("section", {
        attributes: { id: section.id },
        children,
        className: beside.length > 0 ? `chapter-section ${PAIRED_SECTION_CLASS}` : "chapter-section",
    });
};

export const renderChapter = function renderChapter(tab: Tab): Element {
    const article = createElement("article", { className: "chapter" });
    article.append(createElement("h1", { className: "chapter-title", text: tab.label }));
    appendChildren(article, tab.sections.map(renderChapterSection));
    return article;
};
```
