Add semantic block parser and PDF export
Some checks are pending
Build / build (push) Waiting to run
Some checks are pending
Build / build (push) Waiting to run
This commit is contained in:
parent
632543db4d
commit
58310e1944
47 changed files with 3078 additions and 0 deletions
77
sites/reflections/src/interactives/runtime.tsx
Normal file
77
sites/reflections/src/interactives/runtime.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
type InteractiveProps = {
|
||||
id: string;
|
||||
label: string;
|
||||
caption?: string;
|
||||
description?: string;
|
||||
fallback?: string;
|
||||
attrs?: Record<string, string>;
|
||||
};
|
||||
|
||||
type InteractiveModule = {
|
||||
default: React.ComponentType<InteractiveProps>;
|
||||
};
|
||||
|
||||
const modules = import.meta.glob<InteractiveModule>("./*/index.tsx");
|
||||
|
||||
function readProps(element: HTMLElement): InteractiveProps {
|
||||
const encoded = element.dataset.interactiveProps;
|
||||
if (!encoded) {
|
||||
return {
|
||||
id: element.dataset.interactiveId ?? "",
|
||||
label: "",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(decodeURIComponent(encoded)) as InteractiveProps;
|
||||
} catch {
|
||||
return {
|
||||
id: element.dataset.interactiveId ?? "",
|
||||
label: "",
|
||||
fallback: element.textContent?.trim() ?? "",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function mountInteractive(element: HTMLElement) {
|
||||
const id = element.dataset.interactiveId;
|
||||
if (!id || element.dataset.interactiveMounted) return;
|
||||
|
||||
const modulePath = `./${id}/index.tsx`;
|
||||
const loader = modules[modulePath];
|
||||
if (!loader) {
|
||||
element.dataset.interactiveState = "missing-component";
|
||||
return;
|
||||
}
|
||||
|
||||
element.dataset.interactiveMounted = "true";
|
||||
element.dataset.interactiveState = "loading";
|
||||
|
||||
try {
|
||||
const props = readProps(element);
|
||||
const { default: Component } = await loader();
|
||||
element.replaceChildren();
|
||||
createRoot(element).render(<Component {...props} />);
|
||||
element.dataset.interactiveState = "ready";
|
||||
} catch (error) {
|
||||
element.dataset.interactiveState = "failed";
|
||||
console.error(`Failed to mount interactive "${id}"`, error);
|
||||
}
|
||||
}
|
||||
|
||||
function mountAllInteractives() {
|
||||
document
|
||||
.querySelectorAll<HTMLElement>(".interactive-mount[data-interactive-id]")
|
||||
.forEach((element) => {
|
||||
void mountInteractive(element);
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", mountAllInteractives, { once: true });
|
||||
} else {
|
||||
mountAllInteractives();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue