69 lines
2 KiB
TypeScript
69 lines
2 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useRef, useState } from "react";
|
||
|
||
/**
|
||
* SVGNest panel – same-origin iframe wrapper.
|
||
* Renders /public/svgnest/index.html in an isolated doc so the legacy app can
|
||
* own the DOM/CSS/JS without conflicting with Next.js styles or scripts.
|
||
*/
|
||
export default function SVGNestPanel() {
|
||
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
||
const [ready, setReady] = useState(false);
|
||
const [err, setErr] = useState<string | null>(null);
|
||
|
||
// Basic load/error status for UX
|
||
useEffect(() => {
|
||
const el = iframeRef.current;
|
||
if (!el) return;
|
||
|
||
const onLoad = () => setReady(true);
|
||
const onError = () => setErr("Failed to load /svgnest/index.html");
|
||
|
||
el.addEventListener("load", onLoad);
|
||
el.addEventListener("error", onError);
|
||
return () => {
|
||
el.removeEventListener("load", onLoad);
|
||
el.removeEventListener("error", onError);
|
||
};
|
||
}, []);
|
||
|
||
return (
|
||
<div className="w-full">
|
||
{!ready && !err && (
|
||
<div className="mb-2 text-sm text-zinc-400">Loading SVGnest…</div>
|
||
)}
|
||
{err && (
|
||
<div className="mb-2 rounded border border-rose-600/40 bg-rose-600/10 p-3 text-sm">
|
||
Couldn’t load SVGnest: {err}
|
||
</div>
|
||
)}
|
||
|
||
{/* Same-origin iframe, no sandbox to allow drag/drop, file dialogs, workers, etc. */}
|
||
<iframe
|
||
ref={iframeRef}
|
||
src="/svgnest/index.html"
|
||
title="SVGNest"
|
||
className="w-full rounded-md border"
|
||
style={{
|
||
height: "72vh",
|
||
background: "transparent",
|
||
}}
|
||
/>
|
||
|
||
{/* Fallback open-in-new-tab helper */}
|
||
<div className="mt-2 text-xs text-zinc-400">
|
||
If the app doesn’t respond,{" "}
|
||
<a
|
||
href="/svgnest/index.html"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="underline"
|
||
>
|
||
open SVGnest in a new tab
|
||
</a>
|
||
.
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|