import { mount, unmount, type Component, type SvelteComponent } from "svelte" import BlockRenderer from "./blocks/BlockRenderer.svelte" const previewCssUrl = new URL("./index.css", import.meta.url).toString() type BlockRenderContext = { namespace?: string apiBase?: string projectBase?: string } type BlockHandle = { update(row: Record, context?: BlockRenderContext): void destroy(): void } type BlockDefinition = { render(container: HTMLElement | ShadowRoot, row: Record, context?: BlockRenderContext): BlockHandle css?: string[] previewStyles?: Record label?: string icon?: string color?: string } type BlockPresentation = { label: string icon: string color: string } function getAdminPreviewProps(props?: { [key: string]: any }) { return { isAdminPreview: true, ...(props || {}), } } function getRenderedElement( component: typeof SvelteComponent, options?: { props: { [key: string]: any }; addCss?: string[] }, nestedElements?: { tagName: string; className?: string }[] ) { const el = document.createElement("div") el.attachShadow({ mode: "open" }) const body = document.createElement("body") // build nested divs with css classes let target: HTMLElement = body nestedElements?.forEach((e) => { const newElement = document.createElement(e.tagName) if (e.className) { newElement.className = e.className } target.appendChild(newElement) target = newElement }) el.shadowRoot.appendChild(body) options?.addCss?.forEach((css) => { const link = document.createElement("link") link.rel = "stylesheet" link.href = css link.type = "text/css" el.shadowRoot.appendChild(link) }) new component({ target: target, props: getAdminPreviewProps(options?.props), }) return el } function createContentBlockDefinition(presentation: BlockPresentation): BlockDefinition { return { css: [previewCssUrl], label: presentation.label, icon: presentation.icon, color: presentation.color, previewStyles: { "background-color": "white", position: "relative", }, render(container, row, context) { const target = document.createElement("div") target.dataset.adminPreview = "true" container.appendChild(target) let mountedComponent = mount(BlockRenderer as Component, { target, props: { blocks: [row as ContentBlockEntry], isAdminPreview: true, }, }) return { update(nextRow) { unmount(mountedComponent) target.innerHTML = "" mountedComponent = mount(BlockRenderer as Component, { target, props: { blocks: [nextRow as ContentBlockEntry], isAdminPreview: true, }, }) }, destroy() { unmount(mountedComponent) target.remove() }, } }, } } const blockRegistry = { hero: createContentBlockDefinition({ label: "Hero", icon: "image", color: "#1d4ed8" }), features: createContentBlockDefinition({ label: "Features", icon: "view_quilt", color: "#0f766e" }), richtext: createContentBlockDefinition({ label: "Richtext", icon: "article", color: "#7c3aed" }), accordion: createContentBlockDefinition({ label: "Accordion", icon: "expand", color: "#b45309" }), "contact-form": createContentBlockDefinition({ label: "Contact Form", icon: "mail", color: "#be185d", }), } export { getAdminPreviewProps, getRenderedElement, blockRegistry, // pass also required svelte components here }