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") { 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, ]), ); } };