import { CONTENTS_TITLE, EFFECTIVE_LABEL, UPDATED_LABEL, VERSION_LABEL, VERSION_PREFIX, } from "#configuration/strings/document.strings"; import type { DocumentMeta, Section, Subsection } from "#types/document.types"; import { FADE_IN, FADE_IN_UP } from "#configuration/constants/element.constants"; import { appendChildren, createElement } from "#core/factories/element.factory"; import { ANCHOR_PREFIX } from "#configuration/constants/document.constants"; import { animated } from "#presentation/renderers/element.renderer"; import { createCompanyFooter } from "#presentation/components/company.component"; import { fillMarkup } from "#presentation/renderers/text.renderer"; import { observeSections } from "#core/observers/document.observer"; import { renderBlock } from "#presentation/renderers/block.renderer"; const renderHeader = function renderHeader(meta: DocumentMeta): Element { const header = animated("header", { className: "document-header" }, FADE_IN_UP); const dates = createElement("div", { className: "document-dates" }); appendChildren(dates, [ createElement("span", { text: EFFECTIVE_LABEL + meta.effectiveDate }), createElement("span", { text: UPDATED_LABEL + meta.lastUpdated }), createElement("span", { text: VERSION_PREFIX + meta.version }), ]); if (meta.emblem !== undefined) { header.append( createElement("img", { attributes: { alt: meta.title, src: meta.emblem }, className: "document-emblem" }), ); } appendChildren(header, [createElement("h1", { className: "document-title", text: meta.title }), dates]); if (meta.intro !== undefined) { header.append(animated("p", { className: "document-intro", text: meta.intro }, FADE_IN)); } return header; }; const renderContentsLink = function renderContentsLink(section: Section, target: Element): HTMLAnchorElement { const link = createElement("a", { attributes: { href: ANCHOR_PREFIX + section.id }, className: "contents-link" }); appendChildren(link, [ createElement("i", { className: section.icon }), createElement("span", { text: section.title }), ]); link.addEventListener("click", (click) => { click.preventDefault(); target.scrollIntoView({ behavior: "smooth", block: "start" }); }); return link; }; const renderContents = function renderContents(links: readonly Element[]): Element { const contents = animated("nav", { className: "document-contents" }, FADE_IN_UP); const list = createElement("ul", { className: "contents-list" }); appendChildren( list, links.map((link) => createElement("li", { children: [link], className: "contents-item" })), ); appendChildren(contents, [createElement("h2", { className: "contents-title", text: CONTENTS_TITLE }), list]); return contents; }; const renderSubsection = function renderSubsection(subsection: Subsection): Element { const wrapper = createElement("div", { className: "subsection" }); wrapper.append(createElement("h3", { className: "subsection-title", text: subsection.title })); if (subsection.content !== undefined) { wrapper.append(fillMarkup(createElement("p", { className: "subsection-text" }), subsection.content)); } appendChildren(wrapper, (subsection.blocks ?? []).map(renderBlock)); return wrapper; }; const renderSection = function renderSection(section: Section): Element { const wrapper = animated("section", { attributes: { id: section.id }, className: "document-section" }, FADE_IN_UP); const header = createElement("div", { className: "section-header" }); appendChildren(header, [ createElement("i", { className: `${section.icon} section-icon` }), createElement("h2", { className: "section-title", text: section.title }), ]); const content = createElement("div", { className: "section-content" }); if (section.intro !== undefined) { content.append(fillMarkup(createElement("p", { className: "section-intro" }), section.intro)); } appendChildren(content, section.subsections.map(renderSubsection)); appendChildren(wrapper, [header, content]); return wrapper; }; const renderFooter = function renderFooter(meta: DocumentMeta): Element { const footer = animated("footer", { className: "document-footer" }, FADE_IN); const closing = createElement("div", { className: "document-closing" }); appendChildren(closing, [ createElement("p", { text: meta.closing }), createElement("p", { text: VERSION_LABEL + meta.version }), ]); appendChildren(footer, [closing, createCompanyFooter()]); return footer; }; export const renderDocument = function renderDocument(meta: DocumentMeta, sections: readonly Section[]): Element { const pairs = sections.map((section) => { const target = renderSection(section); return { link: renderContentsLink(section, target), target }; }); const rendered = pairs.map((pair) => pair.target); const links = pairs.map((pair) => pair.link); const body = createElement("div", { children: rendered, className: "document-sections" }); const layout = createElement("div", { children: [renderContents(links), body], className: "document-layout" }); void observeSections(rendered, links); return createElement("article", { children: [renderHeader(meta), layout, renderFooter(meta)], className: "document", }); };