import { CODE_LINE_ATTRIBUTE, CODE_LINE_CLASS, CODE_LINE_MARKED_CLASS, } from "#configuration/constants/syntax.constants"; import { COLLAPSED_LINE_LIMIT, EXPANDABLE_CLASS, LANGUAGE_ATTRIBUTE, LINE_BREAK, } from "#configuration/constants/code.constants"; import { LINES_SUFFIX, VIEW_FULL_CODE_TITLE } from "#configuration/strings/code.strings"; import { PANEL_CLASS, PANEL_REVEAL_EVENT } from "#configuration/constants/panel.constants"; import { SCROLL_BEHAVIOR, SCROLL_BLOCK } from "#configuration/constants/tab.constants"; import { appendChildren, createElement } from "#core/factories/element.factory"; import { createCopyButton } from "#presentation/components/clipboard.component"; import { createExpandButton } from "#presentation/components/overlay.component"; import { createPanel } from "#presentation/components/panel.component"; import { renderSyntax } from "#presentation/renderers/syntax.renderer"; const createCodeBody = function createCodeBody(code: string, language: string, className: string): HTMLElement { return createPanel(renderSyntax(code, language), className, "origin").element; }; export const revealLine = function revealLine(scope: Element, line: number): boolean { for (const marked of scope.querySelectorAll(`.${CODE_LINE_CLASS}.${CODE_LINE_MARKED_CLASS}`)) { marked.classList.remove(CODE_LINE_MARKED_CLASS); } const span = scope.querySelector(`.${CODE_LINE_CLASS}[${CODE_LINE_ATTRIBUTE}="${String(line)}"]`); if (span === null) { return false; } span.classList.add(CODE_LINE_MARKED_CLASS); const panel = span.closest(`.${PANEL_CLASS}`); if (panel === null) { span.scrollIntoView({ behavior: SCROLL_BEHAVIOR, block: SCROLL_BLOCK }); } else { panel.dispatchEvent(new CustomEvent(PANEL_REVEAL_EVENT, { detail: span.offsetTop })); } return true; }; export const createPlainCodeBlock = function createPlainCodeBlock( code: string, title: string, language: string, ): HTMLElement { const header = createElement("div", { children: [ createElement("span", { className: "code-title", text: title }), createElement("div", { children: [createCopyButton(() => code, "code-copy")], className: "code-actions" }), ], className: "code-header", }); const block = createElement("pre", { attributes: { [LANGUAGE_ATTRIBUTE]: language }, className: "code-block code-plain", }); appendChildren(block, [ header, createElement("div", { children: [renderSyntax(code, language)], className: "code-whole" }), ]); return block; }; export const createCodeBlock = function createCodeBlock(code: string, title: string, language: string): HTMLElement { const lines = code.split(LINE_BREAK).length; const expandable = lines > COLLAPSED_LINE_LIMIT; const actions = createElement("div", { className: "code-actions" }); if (expandable) { actions.append( createExpandButton( "code-expand", VIEW_FULL_CODE_TITLE, title, () => createCodeBody(code, language, "code-full"), [createElement("span", { text: String(lines) + LINES_SUFFIX })], ), ); } actions.append(createCopyButton(() => code, "code-copy")); const header = createElement("div", { children: [createElement("span", { className: "code-title", text: title }), actions], className: "code-header", }); const block = createElement("pre", { attributes: { [LANGUAGE_ATTRIBUTE]: language }, className: expandable ? `code-block ${EXPANDABLE_CLASS}` : "code-block", }); appendChildren(block, [header, createCodeBody(code, language, "code-content")]); return block; };