# presentation/renderers/section.renderer.ts

> 92 lines of code and 23 definitions.

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

## Definitions

- `sectionLabel` (lexical_declaration, line 34, exported)
- `subsectionId` (lexical_declaration, line 23, exported)
- `subsectionLabel` (lexical_declaration, line 38, exported)
- `sectionLetter` (lexical_declaration, line 27)
- `renderTitle` (lexical_declaration, line 67)
- `renderSubsectionTitle` (lexical_declaration, line 44)
- `renderTabSection` (lexical_declaration, line 83, exported)
- `renderSubsection` (lexical_declaration, line 52)
- `CALLOUT` (lexical_declaration, line 19)
- `GRID` (lexical_declaration, line 20)
- `DELAY_INTRO` (lexical_declaration, line 21)
- `base` (lexical_declaration, line 28)
- `last` (lexical_declaration, line 29)
- `rest` (lexical_declaration, line 30)
- `group` (lexical_declaration, line 39, exported)
- `position` (lexical_declaration, line 40, exported)
- `blocks` (lexical_declaration, line 58)
- `wrapper` (lexical_declaration, line 68)
- `title` (lexical_declaration, line 72)
- `callout` (lexical_declaration, line 84, exported)
- `className` (lexical_declaration, line 85, exported)
- `element` (lexical_declaration, line 86, exported)
- `subsections` (lexical_declaration, line 92, exported)

## Uses

- [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/renderers/element.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/element.renderer.ts.md)
- [presentation/renderers/text.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/text.renderer.ts.md)

## Used by

- [presentation/renderers/chapter.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/chapter.renderer.ts.md)
- [presentation/renderers/document.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/document.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/tab.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/tab.renderer.ts.md)

## Source

```typescript
import { FADE_IN, FADE_IN_UP } from "#configuration/constants/element.constants";
import {
    LABEL_GROUP_SIZE,
    LABEL_SEPARATOR,
    SECTION_LETTERS,
    SUBSECTION_INFIX,
} from "#configuration/constants/tab.constants";
import type { Section, Subsection } from "#types/document.types";
import { appendChildren, createElement } from "#core/factories/element.factory";
import { COPY_SECTION_TITLE } from "#configuration/strings/tab.strings";
import { ICON_BASE_CLASS } from "#configuration/icons/element.icons";
import { IGNORED_SELECTORS } from "#configuration/constants/document.constants";
import { animated } from "#presentation/renderers/element.renderer";
import { convertText } from "#core/converters/text.converter";
import { createCopyButton } from "#presentation/components/clipboard.component";
import { fillMarkup } from "#presentation/renderers/text.renderer";
import { renderBlock } from "#presentation/renderers/block.renderer";

const CALLOUT = "callout";
const GRID = "grid";
const DELAY_INTRO = 50;

export const subsectionId = function subsectionId(section: string, index: number, own?: string): string {
    return own ?? section + SUBSECTION_INFIX + String(index);
};

const sectionLetter = function sectionLetter(sectionIndex: number): string {
    const base = SECTION_LETTERS.length;
    const last = SECTION_LETTERS.charAt(sectionIndex % base);
    const rest = Math.floor(sectionIndex / base);
    return rest === 0 ? last : sectionLetter(rest - 1) + last;
};

export const sectionLabel = function sectionLabel(sectionIndex: number): string {
    return sectionLetter(sectionIndex) + String(1);
};

export const subsectionLabel = function subsectionLabel(sectionIndex: number, subIndex: number): string {
    const group = Math.floor(subIndex / LABEL_GROUP_SIZE) + 1;
    const position = (subIndex % LABEL_GROUP_SIZE) + 1;
    return sectionLetter(sectionIndex) + String(group) + LABEL_SEPARATOR + String(position);
};

const renderSubsectionTitle = function renderSubsectionTitle(sub: Subsection, label: string | undefined): Element {
    const title = createElement("h3", { className: "subsection-title", text: sub.title });
    if (label !== undefined) {
        title.append(createElement("span", { className: "subsection-symbol", text: label }));
    }
    return title;
};

const renderSubsection = function renderSubsection(sub: Subsection, id: string, label: string | undefined): Element {
    const wrapper = animated("div", { attributes: { id }, className: "tab-subsection" }, FADE_IN_UP);
    wrapper.append(renderSubsectionTitle(sub, label));
    if (sub.content !== undefined) {
        wrapper.append(fillMarkup(createElement("p", { className: "section-text" }), sub.content));
    }
    const blocks = (sub.blocks ?? []).map(renderBlock);
    if (sub.layout === GRID) {
        wrapper.append(createElement("div", { children: blocks, className: "tab-subsection-grid" }));
        return wrapper;
    }
    appendChildren(wrapper, blocks);
    return wrapper;
};

const renderTitle = function renderTitle(section: Section, target: Element, label: string | undefined): Element {
    const wrapper = createElement("div", { className: "section-title-wrapper" });
    if (section.layout === CALLOUT) {
        wrapper.append(createElement("i", { className: `${ICON_BASE_CLASS} ${section.icon} callout-icon` }));
    }
    const title = animated("h2", { className: "section-title", text: section.title }, FADE_IN_UP);
    if (label !== undefined) {
        title.append(createElement("span", { className: "subsection-symbol", text: label }));
    }
    appendChildren(wrapper, [
        title,
        createCopyButton(() => convertText(target, IGNORED_SELECTORS), "copy-button medium", COPY_SECTION_TITLE),
    ]);
    return wrapper;
};

export const renderTabSection = function renderTabSection(section: Section, sectionIndex: number): Element {
    const callout = section.layout === CALLOUT;
    const className = callout ? "tab-section callout" : "tab-section";
    const element = createElement("section", { attributes: { id: section.id }, className });
    element.append(renderTitle(section, element, callout ? undefined : sectionLabel(sectionIndex)));
    if (section.intro !== undefined) {
        element.append(fillMarkup(animated("p", { className: "section-text" }, FADE_IN, DELAY_INTRO), section.intro));
    }
    appendChildren(element, (section.blocks ?? []).map(renderBlock));
    const subsections = section.subsections.map((sub, index) =>
        renderSubsection(
            sub,
            subsectionId(section.id, index, sub.id),
            callout ? undefined : subsectionLabel(sectionIndex, index),
        ),
    );
    element.append(createElement("div", { children: subsections, className: "tab-section-grid" }));
    return element;
};
```
