UNPKG

@xylogeist/twig

Version:

A lightweight TypeScript renderer that converts structured block-based content(from an editor) into HTML.

239 lines (237 loc) 5.47 kB
// src/renderer.ts var renderTwig = (content) => { const MARK_RENDERERS = { bold: (text) => `<strong class="twig-bold">${text}</strong>`, italic: (text) => `<em class="twig-italic">${text}</em>`, underline: (text) => `<span class="twig-underline">${text}</span>`, strikethrough: (text) => `<s class="twig-strikethrough">${text}</s>` }; const renderSpan = (span) => { let html = span.text; for (const mark of span.marks) { const renderer = MARK_RENDERERS[mark]; if (renderer) { html = renderer(html); } } switch (span.type) { case "text": return html; case "link": return `<a href="${span.url}" class="twig-link">${html}</a>`; default: return html; } }; return content.map(renderSpan).join(""); }; var renderListBlock = (block) => { const tag = block.listType === "ul" ? "ul" : "ol"; const className = `twig-${block.listType}`; const items = block.listItems.map((listItem) => { const itemContent = renderTwig(listItem.content); return `<li class="twig-list-item">${itemContent}</li>`; }).join(""); return `<${tag} class="${className}">${items}</${tag}>`; }; var renderTwigToHtml = (blocks) => { return blocks.map((block) => { switch (block.type) { case "paragraph": return `<p>${renderTwig(block.content)}</p>`; case "heading": const level = block.level; return `<h${level}>${renderTwig(block.content)}</h${level}>`; case "list": return renderListBlock(block); default: return ""; } }).join(` `); }; // src/index.ts if (import.meta.main) { const testBlocks = [ { type: "paragraph", content: [ { type: "text", text: "This is some ", marks: [] }, { type: "text", text: "bold", marks: ["bold"] }, { type: "text", text: " and ", marks: [] }, { type: "text", text: "italic", marks: ["italic"] }, { type: "text", text: " text.", marks: [] } ] }, { type: "heading", level: 1, content: [ { type: "text", text: "Main Title - Heading 1 (No marks)", marks: [] } ] }, { type: "heading", level: 2, content: [ { type: "text", text: "Section Title - Heading 2 with Bold", marks: ["bold"] } ] }, { type: "heading", level: 3, content: [ { type: "text", text: "Subsection - Heading 3 with Italic", marks: ["italic"] } ] }, { type: "heading", level: 4, content: [ { type: "text", text: "Minor Section - Heading 4 with Underline", marks: ["underline"] } ] }, { type: "heading", level: 5, content: [ { type: "text", text: "Small Section - Heading 5 with Strikethrough", marks: ["strikethrough"] } ] }, { type: "heading", level: 6, content: [ { type: "text", text: "Tiny Section - Heading 6 with Multiple Marks", marks: ["bold", "italic", "underline"] } ] }, { type: "heading", level: 2, content: [ { type: "link", url: "https://www.google.com", text: "Linked Heading - Heading 2 with Link and Bold", marks: ["bold"] } ] }, { type: "list", listType: "ul", listItems: [ { type: "listItem", content: [ { type: "link", url: "https://www.google.com", text: "Go to google", marks: ["bold"] } ] }, { type: "listItem", content: [{ type: "text", text: "Item 2", marks: ["italic"] }] }, { type: "listItem", content: [{ type: "text", text: "Item 3", marks: [] }] }, { type: "listItem", content: [ { type: "link", url: "https://www.google.com", text: "Item 4", marks: [] } ] } ] }, { type: "list", listType: "ol", listItems: [ { type: "listItem", content: [{ type: "text", text: "Ordered Item 1", marks: ["bold"] }] }, { type: "listItem", content: [{ type: "text", text: "Ordered Item 2", marks: ["italic"] }] }, { type: "listItem", content: [{ type: "text", text: "Ordered Item 3", marks: ["underline"] }] } ] }, { type: "quote", quote: "This is a famous quote with important wisdom!" } ]; const wrapper = (blocks) => { return `<div class="twig-wrapper">${renderTwigToHtml(blocks)}</div>`; }; if (typeof document !== "undefined") { const renderer = document.getElementById("renderer"); if (renderer) { renderer.innerHTML = wrapper(testBlocks); } } else { console.log(wrapper(testBlocks)); } } export { renderTwigToHtml };