forked from cms/tibi-svelte-starter
e84b87ed16
🔧 fix: update navigation href resolution to include localized paths 🆕 feat: add new FeatureIcon component for feature boxes 🎨 style: improve styling for prose elements in richtext blocks 🛠️ refactor: streamline medialib image loading and caching logic 📦 chore: update mock data handling to support new medialib entries 🔄 chore: synchronize i18n initialization and locale management 📝 docs: update video tour descriptions to reflect recent changes
140 lines
4.0 KiB
TypeScript
140 lines
4.0 KiB
TypeScript
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<string, any>, context?: BlockRenderContext): void
|
|
destroy(): void
|
|
}
|
|
|
|
type BlockDefinition = {
|
|
render(container: HTMLElement | ShadowRoot, row: Record<string, any>, context?: BlockRenderContext): BlockHandle
|
|
css?: string[]
|
|
previewStyles?: Record<string, string>
|
|
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<any>, {
|
|
target,
|
|
props: {
|
|
blocks: [row as ContentBlockEntry],
|
|
isAdminPreview: true,
|
|
},
|
|
})
|
|
|
|
return {
|
|
update(nextRow) {
|
|
unmount(mountedComponent)
|
|
target.innerHTML = ""
|
|
mountedComponent = mount(BlockRenderer as Component<any>, {
|
|
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
|
|
}
|