presentation/renderers/text.renderer.ts

presentation/renderers/text.renderer.ts is a file in Bane's Lab Site. 64 lines of code and 10 definitions.

import { CITE_ATTRIBUTE, CITE_CLASS } from "#configuration/constants/chapter.constants";
import type { ElementTag } from "#types/element.types";
import { NO_CLASS } from "#configuration/constants/element.constants";
import type { Run } from "#types/markup.types";
import { convertMarkup } from "#core/converters/markup.converter";
import { createElement } from "#core/factories/element.factory";
import { createLink } from "#presentation/components/link.component";

const TAG_BY_KIND: ReadonlyMap<Run["kind"], ElementTag> = new Map([
    ["strong", "strong"],
    ["emphasis", "em"],
    ["code", "code"],
    ["link", "a"],
    ["break", "br"],
    ["cite", "span"],
]);

const renderRun = function renderRun(run: Run): Node {
    const tag = TAG_BY_KIND.get(run.kind);
    if (tag === undefined) {
        return document.createTextNode(run.text);
    }
    if (run.kind === "link" && run.href !== undefined) {
        return createLink(run.href, NO_CLASS, [run.text]);
    }
    if (run.kind === "cite") {
        return createElement("span", {
            attributes: { [CITE_ATTRIBUTE]: run.text },
            className: CITE_CLASS,
            text: run.text,
        });
    }
    return createElement(tag, run.kind === "break" ? {} : { text: run.text });
};

const isEmphasized = function isEmphasized(run: Run): boolean {
    return run.kind === "emphasis" || run.emphasis === true;
};

const renderInside = function renderInside(run: Run): Node {
    return run.kind === "emphasis" ? document.createTextNode(run.text) : renderRun(run);
};

const renderMarkup = function renderMarkup(markup: string): readonly Node[] {
    const nodes: Node[] = [];
    let open: HTMLElement | null = null;
    for (const run of convertMarkup(markup)) {
        if (!isEmphasized(run)) {
            open = null;
            nodes.push(renderRun(run));
            continue;
        }
        if (open === null) {
            open = createElement("em");
            nodes.push(open);
        }
        open.append(renderInside(run));
    }
    return nodes;
};

export const plainText = function plainText(markup: string): string {
    return convertMarkup(markup)
        .map((run) => run.text)
        .join(NO_CLASS);
};

export const fillMarkup = function fillMarkup<T extends Element>(element: T, markup: string): T {
    element.replaceChildren(...renderMarkup(markup));
    return element;
};