mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-04-02 04:05:52 +02:00
125 lines
3.4 KiB
JavaScript
125 lines
3.4 KiB
JavaScript
import { styles } from "../ssr.js";
|
|
styles.css`
|
|
[data-dde-mark] {
|
|
opacity: .5;
|
|
filter: grayscale();
|
|
|
|
@media (prefers-reduced-motion: no-preference) {
|
|
animation: fadein 2s infinite ease forwards;;
|
|
}
|
|
|
|
position: relative;
|
|
&::after {
|
|
content: "Loading Ireland…";
|
|
background-color: rgba(0, 0, 0, .5);
|
|
color: white;
|
|
font-weight: bold;
|
|
border-radius: 5px;
|
|
padding: 5px 10px;
|
|
position: absolute;
|
|
top: 3%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
}
|
|
}
|
|
@keyframes fadein {
|
|
from { opacity: .5; }
|
|
to { opacity: .85; }
|
|
}
|
|
`;
|
|
|
|
import { el, queue } from "deka-dom-el";
|
|
import { addEventListener, registerClientFile } from "../ssr.js";
|
|
import { relative } from "node:path";
|
|
|
|
const dir= new URL("./", import.meta.url).pathname;
|
|
const dirFE= "irelands";
|
|
// Track all component instances for client-side rehydration
|
|
const componentsRegistry = new Map();
|
|
/**
|
|
* Creates a component that shows code and its runtime output
|
|
* with server-side pre-rendering and client-side rehydration
|
|
*
|
|
* @param {object} attrs
|
|
* @param {URL} attrs.src - Path to the file containing the component
|
|
* @param {string} [attrs.exportName="default"] - Name of the export to use
|
|
* @param {object} [attrs.props={}] - Props to pass to the component
|
|
*/
|
|
export function ireland({ src, exportName = "default", props = {} }) {
|
|
// relative src against the current directory
|
|
const path= "./"+relative(dir, src.pathname);
|
|
const id = "ireland-" + generateComponentId(src);
|
|
const element = el.mark({ type: "later", name: ireland.name });
|
|
queue(
|
|
import(path)
|
|
.then(module => {
|
|
const component = module[exportName];
|
|
const content= el(component, props, mark(id));
|
|
element.replaceWith(content);
|
|
content.querySelectorAll("input, textarea, button")
|
|
.forEach(el=> el.disabled= true);
|
|
})
|
|
.catch(console.error)
|
|
);
|
|
|
|
if(!componentsRegistry.size)
|
|
addEventListener("oneachrender", registerClientPart);
|
|
componentsRegistry.set(id, {
|
|
src,
|
|
path: dirFE+"/"+path.split("/").pop(),
|
|
exportName,
|
|
props,
|
|
});
|
|
|
|
return element;
|
|
}
|
|
|
|
function registerClientPart(){
|
|
const todo= Array.from(componentsRegistry.entries())
|
|
.map(([ id, d ]) => {
|
|
registerClientFile(d.src, {
|
|
folder: dirFE,
|
|
// not all browsers support importmap
|
|
replacer(file){
|
|
return file
|
|
.replaceAll(/ from "deka-dom-el(\/signals)?";/g, ` from "./esm-with-signals.js";`);
|
|
}
|
|
});
|
|
return [ id, d ];
|
|
});
|
|
const store = JSON.stringify(JSON.stringify(todo));
|
|
registerClientFile(new URL("./ireland.js.js", import.meta.url));
|
|
registerClientFile(new URL("../../dist/esm-with-signals.js", import.meta.url), { folder: dirFE });
|
|
document.head.append(
|
|
// not all browsers support importmap
|
|
el("script", { type: "importmap" }).append(`
|
|
{
|
|
"imports": {
|
|
"deka-dom-el": "./${dirFE}/esm-with-signals.js",
|
|
"deka-dom-el/signals": "./${dirFE}/esm-with-signals.js"
|
|
}
|
|
}
|
|
`.trim())
|
|
);
|
|
document.body.append(
|
|
el("script", { type: "module" }).append(`
|
|
import { loadIrelands } from "./ireland.js.js";
|
|
loadIrelands(new Map(JSON.parse(${store})));
|
|
`.trim())
|
|
)
|
|
}
|
|
function mark(id) { return el=> el.dataset.ddeMark= id; }
|
|
const store_prev= new Map();
|
|
/** @param {URL} src */
|
|
function generateComponentId(src){
|
|
const candidate= parseInt(relative((new URL("..", import.meta.url)).pathname, src.pathname)
|
|
.split("")
|
|
.map(ch=> ch.charCodeAt(0))
|
|
.join(""), 10)
|
|
.toString(36)
|
|
.replace(/000+/g, "");
|
|
const count= 1 + ( store_prev.get(candidate) || 0 );
|
|
store_prev.set(candidate, count);
|
|
return count.toString()+"-"+candidate;
|
|
}
|