import type { Block, GlossaryBlock, ListBlock, MetricBlock } from "#types/block.types"; import { DEFINITIONS_LABEL, EDGES_LABEL, FILES_LABEL, FOLDERS_LABEL, LINES_LABEL, } from "#configuration/strings/folder.strings"; import type { FileNode, FolderNode, TreeNode } from "#types/folder.types"; import { ancestorsOf, isFolder, treeLink } from "#presentation/components/folder.component"; import { appendChildren, createElement } from "#core/factories/element.factory"; import { NODE_CLASS } from "#configuration/constants/anatomy.constants"; import { fillMarkup } from "#presentation/renderers/text.renderer"; import { renderBlock } from "#presentation/renderers/block.renderer"; import { renderFields } from "#presentation/renderers/ontology.renderer"; import { renderMetricGrid } from "#presentation/renderers/metric.renderer"; const SPACE = " "; const isList = function isList(block: Block): block is ListBlock { return block.kind === "list"; }; const isGlossary = function isGlossary(block: Block): block is GlossaryBlock { return block.kind === "glossary"; }; const isMetric = function isMetric(block: Block): block is MetricBlock { return block.kind === "metric"; }; const renderNodeBlock = function renderNodeBlock(block: Block): Element { if (isGlossary(block)) { return renderFields(block); } if (isMetric(block)) { return createElement("div", { children: [ createElement("h3", { className: "filesystem-listing-title", text: block.caption }), renderMetricGrid(block), ], className: "filesystem-metrics", }); } return createElement("div", { children: [renderBlock(block)], className: "chapter-panel" }); }; const renderChips = function renderChips(items: readonly string[]): Element { const list = createElement("ul", { className: "record-chips" }); appendChildren( list, items.map((item) => fillMarkup(createElement("li", { className: "record-chip" }), item)), ); return list; }; const splitBlocks = function splitBlocks(blocks: readonly Block[]): { readonly chips: readonly string[]; readonly rest: readonly Block[]; } { const [first] = blocks; if (first !== undefined && isList(first)) { return { chips: first.items, rest: blocks.slice(1) }; } return { chips: [], rest: blocks }; }; const pathOf = function pathOf(root: FolderNode, node: TreeNode): string { if (isFolder(node)) { return node.path; } return node.section.title === root.name ? "" : node.section.title; }; const renderCrumbs = function renderCrumbs(root: FolderNode, node: TreeNode): Element { const crumbs = ancestorsOf(root, pathOf(root, node)).filter((folder) => folder !== node); const nav = createElement("nav", { className: "filesystem-crumbs" }); appendChildren( nav, crumbs.map((folder) => treeLink(folder, "filesystem-crumb")), ); return nav; }; const CARD_METRICS: ReadonlySet = new Set([LINES_LABEL, DEFINITIONS_LABEL, EDGES_LABEL]); const metricChips = function metricChips(blocks: readonly Block[]): readonly string[] { const metric = blocks.find(isMetric); return metric === undefined ? [] : metric.metrics .filter((entry) => CARD_METRICS.has(entry.label)) .map((entry) => entry.value + SPACE + entry.label.toLowerCase()); }; const renderFileCard = function renderFileCard(file: FileNode): Element { const { chips, rest } = splitBlocks(file.sub.blocks ?? []); const all = [...chips, ...metricChips(rest)]; return createElement("article", { children: [treeLink(file, "filesystem-card-title"), ...(all.length === 0 ? [] : [renderChips(all)])], className: "filesystem-card", }); }; const renderFolderCard = function renderFolderCard(folder: FolderNode): Element { const card = createElement("article", { children: [treeLink(folder, "filesystem-card-title")], className: "filesystem-card", }); if (folder.section.intro !== undefined) { card.append(fillMarkup(createElement("p", { className: "filesystem-card-text" }), folder.section.intro)); } return card; }; const renderListing = function renderListing(label: string, cards: readonly Element[]): readonly Element[] { if (cards.length === 0) { return []; } return [ createElement("h3", { className: "filesystem-listing-title", text: label }), createElement("div", { children: cards, className: "filesystem-listing" }), ]; }; const renderFolderView = function renderFolderView(root: FolderNode, folder: FolderNode): Element { const view = createElement("section", { attributes: { id: folder.id }, className: NODE_CLASS }); view.append( renderCrumbs(root, folder), createElement("h2", { className: "filesystem-node-title", text: folder.name }), ); if (folder.section.intro !== undefined) { view.append(fillMarkup(createElement("p", { className: "filesystem-intro" }), folder.section.intro)); } appendChildren(view, (folder.section.blocks ?? []).map(renderNodeBlock)); appendChildren(view, renderListing(FOLDERS_LABEL, folder.folders.map(renderFolderCard))); appendChildren(view, renderListing(FILES_LABEL, folder.files.map(renderFileCard))); return view; }; const renderFileView = function renderFileView(root: FolderNode, file: FileNode): Element { const { chips, rest } = splitBlocks(file.sub.blocks ?? []); const view = createElement("article", { attributes: { id: file.id }, className: NODE_CLASS }); view.append(renderCrumbs(root, file), createElement("h2", { className: "filesystem-node-title", text: file.name })); if (chips.length > 0) { view.append(renderChips(chips)); } appendChildren(view, rest.map(renderNodeBlock)); return view; }; export const renderNode = function renderNode(root: FolderNode, node: TreeNode): Element { return isFolder(node) ? renderFolderView(root, node) : renderFileView(root, node); };