Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/embed/widget-loader.ts
T

68 lines
2.7 KiB
TypeScript

/**
* Form-Widget bootstrap loader (Phase 05 Task #58).
*
* Tenants embed this on their site with:
*
* <script src="https://<host>/embed/widget.js" data-form-key="abc123"
* data-host="https://forms.example.com"
* async></script>
*
* The loader:
* 1. Reads `data-form-key` and optional `data-host` from its own <script> tag.
* 2. Inserts a sandboxed <iframe> pointing at `<host>/embed/forms/<formKey>`.
* 3. Listens for postMessage events from the iframe:
* - `tcform:resize` -> updates iframe height for dynamic content
* - `tcform:submitted` / `tcform:error` -> re-emits as
* CustomEvents on document for parent-site code to listen on.
*
* Build-target: hand-crafted minified ES5-compatible JS in
* `apps/web/public/embed/widget.js`. This file is the readable source
* kept for documentation + tests. Keep both in sync.
*/
(function () {
if (typeof window === "undefined" || typeof document === "undefined") return;
const script = (document.currentScript as HTMLScriptElement | null) ?? findSelf();
if (!script) return;
const formKey = script.getAttribute("data-form-key");
if (!formKey) return;
const explicitHost = script.getAttribute("data-host");
const defaultHost = new URL(script.src).origin;
const host = explicitHost ?? defaultHost;
const iframe = document.createElement("iframe");
iframe.src = host + "/embed/forms/" + encodeURIComponent(formKey) + "?origin=" + encodeURIComponent(location.origin);
iframe.setAttribute("title", "Form");
iframe.setAttribute("loading", "lazy");
iframe.setAttribute("sandbox", "allow-scripts allow-forms allow-same-origin");
iframe.style.cssText = "border:0;width:100%;min-height:200px;max-width:100%;display:block";
const parent = script.parentNode;
if (!parent) return;
parent.insertBefore(iframe, script.nextSibling);
window.addEventListener("message", (event) => {
if (event.source !== iframe.contentWindow) return;
const data = event.data;
if (!data || typeof data !== "object" || typeof data.type !== "string") return;
if (data.type === "tcform:resize" && typeof data.height === "number") {
iframe.style.height = Math.max(100, Math.min(8000, data.height)) + "px";
return;
}
if (data.type === "tcform:submitted" || data.type === "tcform:error") {
document.dispatchEvent(new CustomEvent(data.type, { detail: data }));
}
});
function findSelf(): HTMLScriptElement | null {
const scripts = document.getElementsByTagName("script");
for (let i = scripts.length - 1; i >= 0; i--) {
const s = scripts[i];
if (s.src && s.src.indexOf("/embed/widget.js") !== -1) return s;
}
return null;
}
})();