presentation/components/code.component.ts

presentation/components/code.component.ts is a file in Bane's Lab Site. 89 lines of code and 12 definitions.

import {
    CODE_LINE_ATTRIBUTE,
    CODE_LINE_CLASS,
    CODE_LINE_MARKED_CLASS,
} from "#configuration/constants/syntax.constants";
import {
    COLLAPSED_LINE_LIMIT,
    EXPANDABLE_CLASS,
    LANGUAGE_ATTRIBUTE,
    LINE_BREAK,
} from "#configuration/constants/code.constants";
import { LINES_SUFFIX, VIEW_FULL_CODE_TITLE } from "#configuration/strings/code.strings";
import { PANEL_CLASS, PANEL_REVEAL_EVENT } from "#configuration/constants/panel.constants";
import { SCROLL_BEHAVIOR, SCROLL_BLOCK } from "#configuration/constants/tab.constants";
import { appendChildren, createElement } from "#core/factories/element.factory";
import { createCopyButton } from "#presentation/components/clipboard.component";
import { createExpandButton } from "#presentation/components/overlay.component";
import { createPanel } from "#presentation/components/panel.component";
import { renderSyntax } from "#presentation/renderers/syntax.renderer";

const createCodeBody = function createCodeBody(code: string, language: string, className: string): HTMLElement {
    return createPanel(renderSyntax(code, language), className, "origin").element;
};

export const revealLine = function revealLine(scope: Element, line: number): boolean {
    for (const marked of scope.querySelectorAll(`.${CODE_LINE_CLASS}.${CODE_LINE_MARKED_CLASS}`)) {
        marked.classList.remove(CODE_LINE_MARKED_CLASS);
    }
    const span = scope.querySelector<HTMLElement>(`.${CODE_LINE_CLASS}[${CODE_LINE_ATTRIBUTE}="${String(line)}"]`);
    if (span === null) {
        return false;
    }
    span.classList.add(CODE_LINE_MARKED_CLASS);
    const panel = span.closest(`.${PANEL_CLASS}`);
    if (panel === null) {
        span.scrollIntoView({ behavior: SCROLL_BEHAVIOR, block: SCROLL_BLOCK });
    } else {
        panel.dispatchEvent(new CustomEvent(PANEL_REVEAL_EVENT, { detail: span.offsetTop }));
    }
    return true;
};

export const createPlainCodeBlock = function createPlainCodeBlock(
    code: string,
    title: string,
    language: string,
): HTMLElement {
    const header = createElement("div", {
        children: [
            createElement("span", { className: "code-title", text: title }),
            createElement("div", { children: [createCopyButton(() => code, "code-copy")], className: "code-actions" }),
        ],
        className: "code-header",
    });
    const block = createElement("pre", {
        attributes: { [LANGUAGE_ATTRIBUTE]: language },
        className: "code-block code-plain",
    });
    appendChildren(block, [
        header,
        createElement("div", { children: [renderSyntax(code, language)], className: "code-whole" }),
    ]);
    return block;
};

export const createCodeBlock = function createCodeBlock(code: string, title: string, language: string): HTMLElement {
    const lines = code.split(LINE_BREAK).length;
    const expandable = lines > COLLAPSED_LINE_LIMIT;
    const actions = createElement("div", { className: "code-actions" });
    if (expandable) {
        const count = createElement("span", { text: String(lines) + LINES_SUFFIX });
        actions.append(
            createExpandButton(
                "code-expand",
                VIEW_FULL_CODE_TITLE,
                title,
                () => createCodeBody(code, language, "code-full"),
                [count],
            ),
        );
    }
    actions.append(createCopyButton(() => code, "code-copy"));
    const header = createElement("div", {
        children: [createElement("span", { className: "code-title", text: title }), actions],
        className: "code-header",
    });
    const block = createElement("pre", {
        attributes: { [LANGUAGE_ATTRIBUTE]: language },
        className: expandable ? `code-block ${EXPANDABLE_CLASS}` : "code-block",
    });
    appendChildren(block, [header, createCodeBody(code, language, "code-content")]);
    return block;
};