presentation/renderers/panel.renderer.ts
presentation/renderers/panel.renderer.ts is a file in Bane's Lab Site. 90 lines of code and 19 definitions.
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" || block.kind === "surface") {
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,
]),
);
}
};