presentation/renderers/grammar.renderer.ts

presentation/renderers/grammar.renderer.ts is a file in Bane's Lab Site. 127 lines of code and 20 definitions.

import { CHECKED_ICON, CORRECT_ICON, WRONG_ICON } from "#configuration/icons/grammar.icons";
import { CORRECT_LABEL, WRONG_LABEL } from "#configuration/strings/tab.strings";
import type {
    CheckBlock,
    CompareBlock,
    Comparison,
    DiagramBlock,
    GrammarBlock,
    Keyword,
    KeywordBlock,
    Signature,
    TypeBlock,
} from "#types/grammar.types";
import { appendChildren, createElement } from "#core/factories/element.factory";
import { ICON_BASE_CLASS } from "#configuration/icons/element.icons";
import { SPACE } from "#configuration/constants/document.constants";
import { fillMarkup } from "#presentation/renderers/text.renderer";

const CHECKED_CLASS = "checked";
const WRONG_CLASS = "compare-wrong";
const CORRECT_CLASS = "compare-correct";

const renderKeyword = function renderKeyword(keyword: Keyword | string): Element {
    if (typeof keyword === "string") {
        return createElement("code", { className: "keyword", text: keyword });
    }
    const tooltip = createElement("div", {
        children: [createElement("span", { className: "keyword-tooltip-text", text: keyword.description })],
        className: "keyword-tooltip",
    });
    if (keyword.example !== undefined) {
        tooltip.append(createElement("code", { className: "keyword-tooltip-example", text: keyword.example }));
    }
    return createElement("div", {
        children: [createElement("code", { className: "keyword interactive", text: keyword.name }), tooltip],
        className: "keyword-wrapper",
    });
};

const renderKeywords = function renderKeywords(block: KeywordBlock): Element {
    const grid = createElement("div", { className: "keyword-grid" });
    appendChildren(grid, block.keywords.map(renderKeyword));
    return grid;
};

const renderVerdict = function renderVerdict(className: string, icon: string, label: string, code: string): Element {
    const caption = createElement("span", { className: "compare-label" });
    caption.append(createElement("i", { className: `${ICON_BASE_CLASS} ${icon}` }), SPACE + label);
    return createElement("div", {
        children: [caption, createElement("code", { className: "compare-code", text: code })],
        className,
    });
};

const renderComparison = function renderComparison(entry: Comparison): Element {
    return createElement("div", {
        children: [
            createElement("span", { className: "compare-issue", text: entry.issue }),
            renderVerdict(WRONG_CLASS, WRONG_ICON, WRONG_LABEL, entry.wrong),
            renderVerdict(CORRECT_CLASS, CORRECT_ICON, CORRECT_LABEL, entry.correct),
        ],
        className: "compare-card",
    });
};

const renderCompare = function renderCompare(block: CompareBlock): Element {
    const grid = createElement("div", { className: "compare-grid" });
    appendChildren(grid, block.entries.map(renderComparison));
    return grid;
};

const renderCheckItem = function renderCheckItem(item: string): Element {
    const box = createElement("span", {
        children: [createElement("i", { className: `${ICON_BASE_CLASS} ${CHECKED_ICON}` })],
        className: "check-box",
    });
    const element = createElement("div", {
        children: [box, fillMarkup(createElement("span", { className: "check-label" }), item)],
        className: "check-item",
    });
    element.addEventListener("click", () => {
        element.classList.toggle(CHECKED_CLASS);
    });
    return element;
};

const renderChecks = function renderChecks(block: CheckBlock): Element {
    const grid = createElement("div", { className: "check-grid" });
    appendChildren(grid, block.items.map(renderCheckItem));
    return grid;
};

const renderSignature = function renderSignature(signature: Signature): Element {
    return createElement("div", {
        children: [
            createElement("code", { className: "type-name", text: signature.name }),
            createElement("span", { className: "type-verb", text: signature.verb }),
            createElement("span", { className: "type-purpose", text: signature.purpose }),
        ],
        className: "type-card",
    });
};

const renderTypes = function renderTypes(block: TypeBlock): Element {
    const grid = createElement("div", { className: "type-grid" });
    appendChildren(grid, block.types.map(renderSignature));
    return grid;
};

const renderDiagram = function renderDiagram(block: DiagramBlock): Element {
    const className = block.grammar === true ? "diagram grammar" : "diagram";
    return createElement("div", {
        children: [createElement("pre", { children: [createElement("code", { text: block.text })], className })],
        className: "diagram-block",
    });
};

export const renderGrammarBlock = function renderGrammarBlock(block: GrammarBlock): Element {
    switch (block.kind) {
        case "keyword": {
            return renderKeywords(block);
        }
        case "compare": {
            return renderCompare(block);
        }
        case "check": {
            return renderChecks(block);
        }
        case "type": {
            return renderTypes(block);
        }
        case "diagram": {
            return renderDiagram(block);
        }
        default: {
            return renderDiagram(block);
        }
    }
};