ERP-node/frontend/components/screen/ResponsiveGridRenderer.tsx

106 lines
3.0 KiB
TypeScript

"use client";
import React, { useRef, useState, useEffect } from "react";
import { ComponentData } from "@/types/screen";
interface ResponsiveGridRendererProps {
components: ComponentData[];
canvasWidth: number;
canvasHeight: number;
renderComponent: (component: ComponentData) => React.ReactNode;
}
function getComponentTypeId(component: ComponentData): string {
const direct =
(component as any).componentType || (component as any).widgetType;
if (direct) return direct;
const url = (component as any).url;
if (url && typeof url === "string") {
const parts = url.split("/");
return parts[parts.length - 1];
}
return component.type || "";
}
/**
* 디자이너 절대좌표를 캔버스 대비 비율로 변환하여 렌더링.
* 화면이 줄어들면 비율에 맞게 축소, 늘어나면 확대.
*/
function ProportionalRenderer({
components,
canvasWidth,
canvasHeight,
renderComponent,
}: ResponsiveGridRendererProps) {
const containerRef = useRef<HTMLDivElement>(null);
const [containerW, setContainerW] = useState(0);
useEffect(() => {
const el = containerRef.current;
if (!el) return;
const ro = new ResizeObserver((entries) => {
const w = entries[0]?.contentRect.width;
if (w && w > 0) setContainerW(w);
});
ro.observe(el);
return () => ro.disconnect();
}, []);
const topLevel = components.filter((c) => !c.parentId);
const ratio = containerW > 0 ? containerW / canvasWidth : 1;
const maxBottom = topLevel.reduce((max, c) => {
const bottom = c.position.y + (c.size?.height || 40);
return Math.max(max, bottom);
}, 0);
return (
<div
ref={containerRef}
data-screen-runtime="true"
className="bg-background relative w-full overflow-x-hidden"
style={{ minHeight: containerW > 0 ? `${maxBottom * ratio}px` : "200px" }}
>
{containerW > 0 &&
topLevel.map((component) => {
const typeId = getComponentTypeId(component);
return (
<div
key={component.id}
data-component-id={component.id}
data-component-type={typeId}
style={{
position: "absolute",
left: `${(component.position.x / canvasWidth) * 100}%`,
top: `${component.position.y * ratio}px`,
width: `${((component.size?.width || 100) / canvasWidth) * 100}%`,
height: `${(component.size?.height || 40) * ratio}px`,
zIndex: component.position.z || 1,
}}
>
{renderComponent(component)}
</div>
);
})}
</div>
);
}
export function ResponsiveGridRenderer({
components,
canvasWidth,
canvasHeight,
renderComponent,
}: ResponsiveGridRendererProps) {
return (
<ProportionalRenderer
components={components}
canvasWidth={canvasWidth}
canvasHeight={canvasHeight}
renderComponent={renderComponent}
/>
);
}
export default ResponsiveGridRenderer;