import { FADE_IN, FADE_IN_UP } from "#configuration/constants/element.constants"; import { LABEL_GROUP_SIZE, LABEL_SEPARATOR, SECTION_LETTERS, SUBSECTION_INFIX, } from "#configuration/constants/tab.constants"; import type { Section, Subsection } from "#types/document.types"; import { appendChildren, createElement } from "#core/factories/element.factory"; import { COPY_SECTION_TITLE } from "#configuration/strings/tab.strings"; import { ICON_BASE_CLASS } from "#configuration/icons/element.icons"; import { IGNORED_SELECTORS } from "#configuration/constants/document.constants"; import { animated } from "#presentation/renderers/element.renderer"; import { convertText } from "#core/converters/text.converter"; import { createCopyButton } from "#presentation/components/clipboard.component"; import { fillMarkup } from "#presentation/renderers/text.renderer"; import { renderBlock } from "#presentation/renderers/block.renderer"; const CALLOUT = "callout"; const GRID = "grid"; const DELAY_INTRO = 50; export const subsectionId = function subsectionId(section: string, index: number, own?: string): string { return own ?? section + SUBSECTION_INFIX + String(index); }; const sectionLetter = function sectionLetter(sectionIndex: number): string { const base = SECTION_LETTERS.length; const last = SECTION_LETTERS.charAt(sectionIndex % base); const rest = Math.floor(sectionIndex / base); return rest === 0 ? last : sectionLetter(rest - 1) + last; }; export const sectionLabel = function sectionLabel(sectionIndex: number): string { return sectionLetter(sectionIndex) + String(1); }; export const subsectionLabel = function subsectionLabel(sectionIndex: number, subIndex: number): string { const group = Math.floor(subIndex / LABEL_GROUP_SIZE) + 1; const position = (subIndex % LABEL_GROUP_SIZE) + 1; return sectionLetter(sectionIndex) + String(group) + LABEL_SEPARATOR + String(position); }; const renderSubsectionTitle = function renderSubsectionTitle(sub: Subsection, label: string | undefined): Element { const title = createElement("h3", { className: "subsection-title", text: sub.title }); if (label !== undefined) { title.append(createElement("span", { className: "subsection-symbol", text: label })); } return title; }; const renderSubsection = function renderSubsection(sub: Subsection, id: string, label: string | undefined): Element { const wrapper = animated("div", { attributes: { id }, className: "tab-subsection" }, FADE_IN_UP); wrapper.append(renderSubsectionTitle(sub, label)); if (sub.content !== undefined) { wrapper.append(fillMarkup(createElement("p", { className: "section-text" }), sub.content)); } const blocks = (sub.blocks ?? []).map(renderBlock); if (sub.layout === GRID) { wrapper.append(createElement("div", { children: blocks, className: "tab-subsection-grid" })); return wrapper; } appendChildren(wrapper, blocks); return wrapper; }; const renderTitle = function renderTitle(section: Section, target: Element, label: string | undefined): Element { const wrapper = createElement("div", { className: "section-title-wrapper" }); if (section.layout === CALLOUT) { wrapper.append(createElement("i", { className: `${ICON_BASE_CLASS} ${section.icon} callout-icon` })); } const title = animated("h2", { className: "section-title", text: section.title }, FADE_IN_UP); if (label !== undefined) { title.append(createElement("span", { className: "subsection-symbol", text: label })); } appendChildren(wrapper, [ title, createCopyButton(() => convertText(target, IGNORED_SELECTORS), "copy-button medium", COPY_SECTION_TITLE), ]); return wrapper; }; export const renderTabSection = function renderTabSection(section: Section, sectionIndex: number): Element { const callout = section.layout === CALLOUT; const className = callout ? "tab-section callout" : "tab-section"; const element = createElement("section", { attributes: { id: section.id }, className }); element.append(renderTitle(section, element, callout ? undefined : sectionLabel(sectionIndex))); if (section.intro !== undefined) { element.append(fillMarkup(animated("p", { className: "section-text" }, FADE_IN, DELAY_INTRO), section.intro)); } appendChildren(element, (section.blocks ?? []).map(renderBlock)); const subsections = section.subsections.map((sub, index) => renderSubsection( sub, subsectionId(section.id, index, sub.id), callout ? undefined : subsectionLabel(sectionIndex, index), ), ); element.append(createElement("div", { children: subsections, className: "tab-section-grid" })); return element; };