import { type ButtonFeatures, ButtonType, type CheckboxFeatures, type ElementFeatures, extendClasses, type FormFeatures, HyperlinkDestination, type HyperlinkFeatures, type LabelFeatures, type TemplateBuilder } from '../../common/template'; import escapeHTML from 'escape-html'; import { kebabCase } from 'change-case'; function tag(tagName: string, features: ElementFeatures, attributes: string[], contents: string[]): string { if (typeof features.id !== "undefined") { attributes.push(`id="${escapeHTML(features.id)}"`) } if (typeof features.classes !== "undefined") { attributes.push(`class="${typeof features.classes === "string" ? escapeHTML(features.classes) : Array.from(features.classes).map(escapeHTML).join(" ")}"`) } if (typeof features.data !== "undefined") { for (const [key, value] of features.data) { attributes.push(`data-${escapeHTML(kebabCase(key))}="${escapeHTML(value)}"`) } } return `<${tagName}${attributes.length === 0 ? "" : " " + attributes.join(" ")}>${contents.join("")}` } class StringTemplateBuilderImpl implements TemplateBuilder { makeButton(features: ButtonFeatures, ...contents: string[]): string { const attributes = [ `type="${escapeHTML(features.type ?? ButtonType.Button)}"`, ] if (typeof features.name === "string") { attributes.push(`name="${escapeHTML(features.name)}"`) } if (typeof features.value === "string") { attributes.push(`value="${escapeHTML(features.value)}"`) } return tag('button', {...features, classes: extendClasses(features.classes, "button")}, attributes, contents) } makeCheckbox(features: CheckboxFeatures, ...contents: string[]): string { const attributes = [`type="checkbox"`, `name="${escapeHTML(features.name)}"`] if (features.checked) { attributes.push("checked") } if (typeof features.value === "string") { attributes.push(`value="${escapeHTML(features.value)}"`) } return tag('input', features, attributes, contents); } makeDiv(features: ElementFeatures, ...contents: string[]): string { return tag('div', features, [], contents); } makeFooter(features: ElementFeatures, ...contents: string[]): string { return tag('footer', features, [], contents); } makeForm(features: FormFeatures, ...contents: string[]): string { const attributes = [`action="${escapeHTML(features.action)}"`, `method="${escapeHTML(features.method)}"`] return tag('form', features, attributes, contents); } makeHeader(features: ElementFeatures, ...contents: string[]): string { return tag('header', features, [], contents) } makeHeading1(features: ElementFeatures, ...contents: string[]): string { return tag('h1', features, [], contents); } makeHeading2(features: ElementFeatures, ...contents: string[]): string { return tag('h2', features, [], contents); } makeHyperlink(features: HyperlinkFeatures, ...contents: string[]): string { const attributes = [`href="${escapeHTML(features.url)}"`] if (features.destination === HyperlinkDestination.External) { attributes.push(`rel="external nofollow noreferrer"`) } if (features.asButton) { attributes.push(`draggable="false"`) } return tag('a', {...features, classes: extendClasses(features.classes, features.asButton ? ["button"] : [])}, attributes, contents); } makeLabel(features: LabelFeatures, ...contents: string[]): string { const attributes = [] if (typeof features.forId === "string") { attributes.push(`for="${escapeHTML(features.forId)}"`) } return tag('label', features, attributes, contents); } makeListItem(features: ElementFeatures, ...contents: string[]): string { return tag('li', features, [], contents) } makeNav(features: ElementFeatures, ...contents: string[]): string { return tag('nav', features, [], contents) } makeNoscript(features: ElementFeatures, ...contents: string[]): string { return tag('noscript', features, [], contents); } makeParagraph(features: ElementFeatures, ...contents: string[]): string { return tag('p', features, [], contents); } makeSpan(features: ElementFeatures, ...contents: string[]): string { return tag('span', features, [], contents); } makeText(text: string): string { return escapeHTML(text); } makeUnorderedList(features: ElementFeatures, ...contents: string[]): string { return tag('ul', features, [], contents); } } export const StringTemplateBuilder = new StringTemplateBuilderImpl()