Merge branch 'jskim-node' of http://39.117.244.52:3000/kjs/ERP-node into gbpark-node
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useCallback, useEffect } from "react";
|
||||
import React, { useState, useCallback, useEffect, useSyncExternalStore } from "react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
@@ -20,6 +20,11 @@ import { findAllButtonGroups } from "@/lib/utils/flowButtonGroupUtils";
|
||||
import { useScreenPreview } from "@/contexts/ScreenPreviewContext";
|
||||
import { useSplitPanelContext } from "@/contexts/SplitPanelContext";
|
||||
import { evaluateConditional } from "@/lib/utils/conditionalEvaluator";
|
||||
import {
|
||||
subscribe as canvasSplitSubscribe,
|
||||
getSnapshot as canvasSplitGetSnapshot,
|
||||
getServerSnapshot as canvasSplitGetServerSnapshot,
|
||||
} from "@/lib/registry/components/v2-split-line/canvasSplitStore";
|
||||
|
||||
// 컴포넌트 렌더러들을 강제로 로드하여 레지스트리에 등록
|
||||
import "@/lib/registry/components/ButtonRenderer";
|
||||
@@ -82,9 +87,14 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
|
||||
parentTabId,
|
||||
parentTabsComponentId,
|
||||
}) => {
|
||||
const { isPreviewMode } = useScreenPreview(); // 프리뷰 모드 확인
|
||||
const { isPreviewMode } = useScreenPreview();
|
||||
const { userName: authUserName, user: authUser } = useAuth();
|
||||
const splitPanelContext = useSplitPanelContext(); // 분할 패널 컨텍스트
|
||||
const splitPanelContext = useSplitPanelContext();
|
||||
|
||||
// 캔버스 분할선 글로벌 스토어 구독
|
||||
const canvasSplit = useSyncExternalStore(canvasSplitSubscribe, canvasSplitGetSnapshot, canvasSplitGetServerSnapshot);
|
||||
const canvasSplitSideRef = React.useRef<"left" | "right" | null>(null);
|
||||
const myScopeIdRef = React.useRef<string | null>(null);
|
||||
|
||||
// 외부에서 전달받은 사용자 정보가 있으면 우선 사용 (ScreenModal 등에서)
|
||||
const userName = externalUserName || authUserName;
|
||||
@@ -1079,22 +1089,96 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
|
||||
const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
|
||||
const labelOffset = hasVisibleLabel ? (labelFontSize + labelMarginBottom + 2) : 0;
|
||||
|
||||
// 캔버스 분할선에 따른 X 위치 조정 (너비는 변경하지 않음 - 내부 컴포넌트 깨짐 방지)
|
||||
const calculateCanvasSplitX = (): number => {
|
||||
const compType = (component as any).componentType || "";
|
||||
const isSplitLine = type === "component" && compType === "v2-split-line";
|
||||
const origX = position?.x || 0;
|
||||
|
||||
if (isSplitLine) return origX;
|
||||
|
||||
// DEBUG: 스플릿 스토어 상태 확인 (첫 컴포넌트만)
|
||||
if (canvasSplit.active && origX > 0 && origX < 50) {
|
||||
console.log("[SplitDebug]", {
|
||||
compId: component.id,
|
||||
compType,
|
||||
type,
|
||||
active: canvasSplit.active,
|
||||
scopeId: canvasSplit.scopeId,
|
||||
myScopeId: myScopeIdRef.current,
|
||||
canvasWidth: canvasSplit.canvasWidth,
|
||||
initialX: canvasSplit.initialDividerX,
|
||||
currentX: canvasSplit.currentDividerX,
|
||||
origX,
|
||||
});
|
||||
}
|
||||
|
||||
if (!canvasSplit.active || canvasSplit.canvasWidth <= 0 || !canvasSplit.scopeId) {
|
||||
return origX;
|
||||
}
|
||||
|
||||
if (myScopeIdRef.current === null) {
|
||||
const el = document.getElementById(`interactive-${component.id}`);
|
||||
const container = el?.closest("[data-screen-runtime]");
|
||||
myScopeIdRef.current = container?.getAttribute("data-split-scope") || "__none__";
|
||||
console.log("[SplitDebug] scope resolved:", { compId: component.id, elFound: !!el, containerFound: !!container, myScopeId: myScopeIdRef.current, storeScopeId: canvasSplit.scopeId });
|
||||
}
|
||||
if (myScopeIdRef.current !== canvasSplit.scopeId) {
|
||||
return origX;
|
||||
}
|
||||
|
||||
const { initialDividerX, currentDividerX, canvasWidth } = canvasSplit;
|
||||
const delta = currentDividerX - initialDividerX;
|
||||
if (Math.abs(delta) < 1) return origX;
|
||||
|
||||
const origW = size?.width || 200;
|
||||
if (canvasSplitSideRef.current === null) {
|
||||
const componentCenterX = origX + (origW / 2);
|
||||
canvasSplitSideRef.current = componentCenterX < initialDividerX ? "left" : "right";
|
||||
}
|
||||
|
||||
let newX = origX;
|
||||
|
||||
if (canvasSplitSideRef.current === "left") {
|
||||
if (initialDividerX > 0) {
|
||||
newX = origX * (currentDividerX / initialDividerX);
|
||||
}
|
||||
} else {
|
||||
const initialRightWidth = canvasWidth - initialDividerX;
|
||||
const currentRightWidth = canvasWidth - currentDividerX;
|
||||
if (initialRightWidth > 0) {
|
||||
const posRatio = (origX - initialDividerX) / initialRightWidth;
|
||||
newX = currentDividerX + posRatio * currentRightWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// 캔버스 범위 내로 클램핑
|
||||
return Math.max(0, Math.min(newX, canvasWidth - 10));
|
||||
};
|
||||
|
||||
const adjustedX = calculateCanvasSplitX();
|
||||
const isSplitActive = canvasSplit.active && canvasSplit.scopeId && myScopeIdRef.current === canvasSplit.scopeId;
|
||||
|
||||
const componentStyle = {
|
||||
position: "absolute" as const,
|
||||
left: position?.x || 0,
|
||||
top: position?.y || 0, // 원래 위치 유지 (음수로 가면 overflow-hidden에 잘림)
|
||||
left: adjustedX,
|
||||
top: position?.y || 0,
|
||||
zIndex: position?.z || 1,
|
||||
...styleWithoutSize, // width/height 제외한 스타일만 먼저 적용
|
||||
width: size?.width || 200, // size의 픽셀 값이 최종 우선순위
|
||||
...styleWithoutSize,
|
||||
width: size?.width || 200,
|
||||
height: isTableSearchWidget ? "auto" : size?.height || 10,
|
||||
minHeight: isTableSearchWidget ? "48px" : undefined,
|
||||
// 🆕 라벨이 있으면 overflow visible로 설정하여 라벨이 잘리지 않게 함
|
||||
overflow: labelOffset > 0 ? "visible" : undefined,
|
||||
// GPU 가속: 드래그 중 will-change 활성화, 끝나면 해제
|
||||
willChange: canvasSplit.isDragging && isSplitActive ? "left" as const : undefined,
|
||||
transition: isSplitActive
|
||||
? (canvasSplit.isDragging ? "none" : "left 0.15s ease-out")
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="absolute" style={componentStyle}>
|
||||
<div id={`interactive-${component.id}`} className="absolute" style={componentStyle}>
|
||||
{/* 위젯 렌더링 (라벨은 V2Input 내부에서 absolute로 표시됨) */}
|
||||
{renderInteractiveWidget(component)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user