# presentation/renderers/panel.renderer.ts

> 90 lines of code and 19 definitions.

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

## Definitions

- `panelLetter` (lexical_declaration, line 31)
- `renderChapterPanel` (lexical_declaration, line 66, exported)
- `resolveCitations` (lexical_declaration, line 85, exported)
- `markTitle` (lexical_declaration, line 55)
- `tagOf` (lexical_declaration, line 45)
- `panelMark` (lexical_declaration, line 37)
- `panelId` (lexical_declaration, line 41)
- `isFigure` (lexical_declaration, line 20)
- `isWide` (lexical_declaration, line 24)
- `base` (lexical_declaration, line 32)
- `trailing` (lexical_declaration, line 33)
- `title` (lexical_declaration, line 57)
- `inner` (lexical_declaration, line 72, exported)
- `mark` (lexical_declaration, line 73, exported)
- `id` (lexical_declaration, line 74, exported)
- `wide` (lexical_declaration, line 75, exported)
- `element` (lexical_declaration, line 76, exported)
- `byTag` (lexical_declaration, line 86, exported)
- `panel` (lexical_declaration, line 88, exported)

## Uses

- [core/factories/element.factory.ts](https://banes-lab.com/source/tree/core/factories/element.factory.ts.md)
- [presentation/components/link.component.ts](https://banes-lab.com/source/tree/presentation/components/link.component.ts.md)

## Used by

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

## Source

```typescript
import type { Block, FigureBlock } from "#types/block.types";
import {
    CITE_ATTRIBUTE,
    CITE_CLASS,
    LETTERS,
    MARK_CLASS,
    MARK_SEPARATOR,
    PANEL_ID_INFIX,
    PANEL_TITLE_SELECTORS,
    TAG_CLASS,
    WIDE_PANEL_CLASS,
} from "#configuration/constants/chapter.constants";
import { ANCHOR_PREFIX } from "#configuration/constants/document.constants";
import type { PanelReference } from "#types/panel.types";
import { createElement } from "#core/factories/element.factory";
import { createLink } from "#presentation/components/link.component";
import { renderBlock } from "#presentation/renderers/block.renderer";
import { renderFigure } from "#presentation/renderers/figure.renderer";

const isFigure = function isFigure(block: Block): block is FigureBlock {
    return block.kind === "figure";
};

const isWide = function isWide(block: Block): boolean {
    if (block.kind === "definition") {
        return true;
    }
    return (block.kind === "mermaid" || block.kind === "walk" || block.kind === "metric") && block.wide === true;
};

const panelLetter = function panelLetter(index: number): string {
    const base = LETTERS.length;
    const trailing = LETTERS.charAt(index % base);
    return index < base ? trailing : panelLetter(Math.floor(index / base) - 1) + trailing;
};

const panelMark = function panelMark(sectionLabel: string, index: number): string {
    return sectionLabel + MARK_SEPARATOR + panelLetter(index);
};

const panelId = function panelId(sectionId: string, index: number): string {
    return sectionId + PANEL_ID_INFIX + panelLetter(index);
};

const tagOf = function tagOf(block: Block): string | null {
    if (block.kind === "code" || block.kind === "definition") {
        return block.title;
    }
    if (block.kind === "figure" || block.kind === "mermaid") {
        return block.caption ?? null;
    }
    return null;
};

const markTitle = function markTitle(panel: Element, mark: string): void {
    for (const selector of PANEL_TITLE_SELECTORS) {
        const title = panel.querySelector(selector);
        if (title !== null) {
            title.classList.add(TAG_CLASS);
            title.prepend(createElement("span", { className: MARK_CLASS, text: mark }));
            return;
        }
    }
};

export const renderChapterPanel = function renderChapterPanel(
    block: Block,
    sectionId: string,
    sectionLabel: string,
    index: number,
): PanelReference {
    const inner = isFigure(block) ? renderFigure(block) : renderBlock(block);
    const mark = panelMark(sectionLabel, index);
    const id = panelId(sectionId, index);
    const wide = isWide(block);
    const element = createElement("div", {
        attributes: { id },
        children: [inner],
        className: wide ? `chapter-panel ${WIDE_PANEL_CLASS}` : "chapter-panel",
    });
    markTitle(element, mark);
    return { element, id, mark, tag: tagOf(block), wide };
};

export const resolveCitations = function resolveCitations(prose: Element, panels: readonly PanelReference[]): void {
    const byTag = new Map(panels.flatMap((panel) => (panel.tag === null ? [] : [[panel.tag, panel] as const])));
    for (const placeholder of [...prose.querySelectorAll(`.${CITE_CLASS}`)]) {
        const panel = byTag.get(placeholder.getAttribute(CITE_ATTRIBUTE) ?? "");
        if (panel === undefined) {
            continue;
        }
        placeholder.replaceWith(
            createLink(ANCHOR_PREFIX + panel.id, CITE_CLASS, [
                createElement("span", { className: MARK_CLASS, text: panel.mark }),
                placeholder.textContent,
            ]),
        );
    }
};
```
