diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index f861d1ba..6fdb7aee 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -1762,6 +1762,12 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD grabOffsetY: relativeMouseY - component.position.y, }); + console.log("πŸš€ λ“œλž˜κ·Έ μ‹œμž‘:", { + componentId: component.id, + componentType: component.type, + initialPosition: { x: component.position.x, y: component.position.y }, + }); + setDragState({ isDragging: true, draggedComponent: component, // μ£Ό λ“œλž˜κ·Έ μ»΄ν¬λ„ŒνŠΈ (마우슀 μœ„μΉ˜ κΈ°μ€€) @@ -1804,13 +1810,30 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD }; // λ“œλž˜κ·Έ μƒνƒœ μ—…λ°μ΄νŠΈ - setDragState((prev) => ({ - ...prev, - currentPosition: newPosition, - })); + console.log("πŸ”₯ ScreenDesigner updateDragPosition:", { + draggedComponentId: dragState.draggedComponent.id, + oldPosition: dragState.currentPosition, + newPosition: newPosition, + }); - // μ‹€μ‹œκ°„ ν”Όλ“œλ°±μ€ λ Œλ”λ§μ—μ„œ μ²˜λ¦¬ν•˜λ―€λ‘œ setLayout 호좜 제거 - // μ„±λŠ₯ μ΅œμ ν™”: λ“œλž˜κ·Έ μ€‘μ—λŠ” μƒνƒœ μ—…λ°μ΄νŠΈλ§Œ ν•˜κ³ , μ‹€μ œ λ ˆμ΄μ•„μ›ƒ μ—…λ°μ΄νŠΈλŠ” endDragμ—μ„œ 처리 + setDragState((prev) => { + const newState = { + ...prev, + currentPosition: { ...newPosition }, // μƒˆλ‘œμš΄ 객체 생성 + }; + console.log("πŸ”„ ScreenDesigner dragState μ—…λ°μ΄νŠΈ:", { + prevPosition: prev.currentPosition, + newPosition: newState.currentPosition, + stateChanged: + prev.currentPosition.x !== newState.currentPosition.x || + prev.currentPosition.y !== newState.currentPosition.y, + }); + return newState; + }); + + // μ„±λŠ₯ μ΅œμ ν™”: λ“œλž˜κ·Έ μ€‘μ—λŠ” μƒνƒœ μ—…λ°μ΄νŠΈλ§Œ ν•˜κ³ , + // μ‹€μ œ λ ˆμ΄μ•„μ›ƒ μ—…λ°μ΄νŠΈλŠ” endDragμ—μ„œ 처리 + // 속성 νŒ¨λ„μ—μ„œλŠ” dragState.currentPosition을 μ°Έμ‘°ν•˜μ—¬ μ‹€μ‹œκ°„ ν‘œμ‹œ }, [dragState.isDragging, dragState.draggedComponent, dragState.grabOffset], ); @@ -1950,6 +1973,19 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const newLayout = { ...layout, components: updatedComponents }; setLayout(newLayout); + // μ„ νƒλœ μ»΄ν¬λ„ŒνŠΈλ„ μ—…λ°μ΄νŠΈ (PropertiesPanel λ™κΈ°ν™”μš©) + if (selectedComponent && dragState.draggedComponents.some((c) => c.id === selectedComponent.id)) { + const updatedSelectedComponent = updatedComponents.find((c) => c.id === selectedComponent.id); + if (updatedSelectedComponent) { + console.log("πŸ”„ ScreenDesigner: μ„ νƒλœ μ»΄ν¬λ„ŒνŠΈ μœ„μΉ˜ μ—…λ°μ΄νŠΈ", { + componentId: selectedComponent.id, + oldPosition: selectedComponent.position, + newPosition: updatedSelectedComponent.position, + }); + setSelectedComponent(updatedSelectedComponent); + } + } + // νžˆμŠ€ν† λ¦¬μ— μ €μž₯ saveToHistory(newLayout); } @@ -3123,8 +3159,10 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD autoHeight={true} > { console.log("πŸ”§ 속성 μ—…λ°μ΄νŠΈ μš”μ²­:", { componentId: selectedComponent?.id, diff --git a/frontend/components/screen/panels/PropertiesPanel.tsx b/frontend/components/screen/panels/PropertiesPanel.tsx index 13c44181..0aa4bf5f 100644 --- a/frontend/components/screen/panels/PropertiesPanel.tsx +++ b/frontend/components/screen/panels/PropertiesPanel.tsx @@ -94,6 +94,11 @@ const DataTableConfigPanelWrapper: React.FC<{ interface PropertiesPanelProps { selectedComponent?: ComponentData; tables?: TableInfo[]; + dragState?: { + isDragging: boolean; + draggedComponent: ComponentData | null; + currentPosition: { x: number; y: number; z: number }; + }; onUpdateProperty: (path: string, value: unknown) => void; onDeleteComponent: () => void; onCopyComponent: () => void; @@ -108,6 +113,7 @@ interface PropertiesPanelProps { const PropertiesPanelComponent: React.FC = ({ selectedComponent, tables = [], + dragState, onUpdateProperty, onDeleteComponent, onCopyComponent, @@ -116,9 +122,29 @@ const PropertiesPanelComponent: React.FC = ({ canGroup = false, canUngroup = false, }) => { + // πŸ” 디버깅: PropertiesPanel λ Œλ”λ§ 및 dragState 전달 확인 + console.log("πŸ“ PropertiesPanel λ Œλ”λ§:", { + renderTime: Date.now(), + selectedComponentId: selectedComponent?.id, + dragState: dragState + ? { + isDragging: dragState.isDragging, + draggedComponentId: dragState.draggedComponent?.id, + currentPosition: dragState.currentPosition, + dragStateRef: dragState, // 객체 μ°Έμ‘° 확인 + } + : "null", + }); + // 동적 μ›Ήνƒ€μž… λͺ©λ‘ κ°€μ Έμ˜€κΈ° - APIμ—μ„œ 직접 쑰회 const { webTypes, isLoading: isWebTypesLoading } = useWebTypes({ active: "Y" }); + // κ°•μ œ λ¦¬λ Œλ”λ§μ„ μœ„ν•œ state (λ“œλž˜κ·Έ 쀑 μ‹€μ‹œκ°„ μ—…λ°μ΄νŠΈμš©) + const [forceRender, setForceRender] = useState(0); + + // λ“œλž˜κ·Έ μƒνƒœλ₯Ό 직접 μΆ”μ ν•˜μ—¬ λ¦¬λ Œλ”λ§ κ°•μ œ + const [lastDragPosition, setLastDragPosition] = useState({ x: 0, y: 0 }); + // μ›Ήνƒ€μž… μ˜΅μ…˜ 생성 - λ°μ΄ν„°λ² μ΄μŠ€ 기반 const webTypeOptions = webTypes.map((webType) => ({ value: webType.web_type as WebType, @@ -131,6 +157,27 @@ const PropertiesPanelComponent: React.FC = ({ const selectedComponentRef = useRef(selectedComponent); const onUpdatePropertyRef = useRef(onUpdateProperty); + // μ‹€μ‹œκ°„ μœ„μΉ˜ 계산 (λ“œλž˜κ·Έ 쀑일 λ•ŒλŠ” dragState.currentPosition μ‚¬μš©) + const getCurrentPosition = () => { + if (dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id) { + console.log("🎯 λ“œλž˜κ·Έ 쀑 μ‹€μ‹œκ°„ μœ„μΉ˜:", { + draggedId: dragState.draggedComponent?.id, + selectedId: selectedComponent?.id, + currentPosition: dragState.currentPosition, + }); + return { + x: Math.round(dragState.currentPosition.x), + y: Math.round(dragState.currentPosition.y), + }; + } + return { + x: selectedComponent?.position?.x || 0, + y: selectedComponent?.position?.y || 0, + }; + }; + + const currentPosition = getCurrentPosition(); + // μž…λ ₯ ν•„λ“œλ“€μ˜ 둜컬 μƒνƒœ (μ‹€μ‹œκ°„ 타이핑 반영용) const [localInputs, setLocalInputs] = useState({ placeholder: (selectedComponent?.type === "widget" ? (selectedComponent as WidgetComponent).placeholder : "") || "", @@ -141,8 +188,8 @@ const PropertiesPanelComponent: React.FC = ({ ? (selectedComponent as AreaComponent).title : "") || "", description: (selectedComponent?.type === "area" ? (selectedComponent as AreaComponent).description : "") || "", - positionX: selectedComponent?.position.x?.toString() || "0", - positionY: selectedComponent?.position.y?.toString() || "0", + positionX: currentPosition.x.toString(), + positionY: currentPosition.y.toString(), positionZ: selectedComponent?.position.z?.toString() || "1", width: selectedComponent?.size.width?.toString() || "0", height: selectedComponent?.size.height?.toString() || "0", @@ -174,40 +221,87 @@ const PropertiesPanelComponent: React.FC = ({ console.log("πŸ”„ PropertiesPanel: μ»΄ν¬λ„ŒνŠΈ λ³€κ²½ 감지", { componentId: selectedComponent.id, componentType: selectedComponent.type, + isDragging: dragState?.isDragging, + justFinishedDrag: dragState?.justFinishedDrag, currentValues: { placeholder: widget?.placeholder, title: group?.title || area?.title, description: area?.description, - positionX: selectedComponent.position.x, - labelText: selectedComponent.style?.labelText || selectedComponent.label, + actualPositionX: selectedComponent.position.x, + actualPositionY: selectedComponent.position.y, + dragPositionX: dragState?.currentPosition.x, + dragPositionY: dragState?.currentPosition.y, }, + getCurrentPosResult: getCurrentPosition(), }); - setLocalInputs({ - placeholder: widget?.placeholder || "", - title: group?.title || area?.title || "", - description: area?.description || "", - positionX: selectedComponent.position.x?.toString() || "0", - positionY: selectedComponent.position.y?.toString() || "0", - positionZ: selectedComponent.position.z?.toString() || "1", - width: selectedComponent.size.width?.toString() || "0", - height: selectedComponent.size.height?.toString() || "0", - gridColumns: selectedComponent.gridColumns?.toString() || "1", - labelText: selectedComponent.style?.labelText || selectedComponent.label || "", - labelFontSize: selectedComponent.style?.labelFontSize || "12px", - labelColor: selectedComponent.style?.labelColor || "#374151", - labelMarginBottom: selectedComponent.style?.labelMarginBottom || "4px", - required: widget?.required || false, - readonly: widget?.readonly || false, - labelDisplay: selectedComponent.style?.labelDisplay !== false, - // widgetType 동기화 - widgetType: widget?.widgetType || "text", - }); + // λ“œλž˜κ·Έ 쀑이 아닐 λ•Œλ§Œ localInputs μ—…λ°μ΄νŠΈ (λ“œλž˜κ·Έ μ™„λ£Œ ν›„ μ΅œμ’… μœ„μΉ˜ 반영) + if (!dragState?.isDragging || dragState.draggedComponent?.id !== selectedComponent.id) { + const currentPos = getCurrentPosition(); + setLocalInputs({ + placeholder: widget?.placeholder || "", + title: group?.title || area?.title || "", + description: area?.description || "", + positionX: currentPos.x.toString(), + positionY: currentPos.y.toString(), + positionZ: selectedComponent.position.z?.toString() || "1", + width: selectedComponent.size.width?.toString() || "0", + height: selectedComponent.size.height?.toString() || "0", + gridColumns: selectedComponent.gridColumns?.toString() || "1", + labelText: selectedComponent.style?.labelText || selectedComponent.label || "", + labelFontSize: selectedComponent.style?.labelFontSize || "12px", + labelColor: selectedComponent.style?.labelColor || "#374151", + labelMarginBottom: selectedComponent.style?.labelMarginBottom || "4px", + required: widget?.required || false, + readonly: widget?.readonly || false, + labelDisplay: selectedComponent.style?.labelDisplay !== false, + // widgetType 동기화 + widgetType: widget?.widgetType || "text", + }); + + console.log("βœ… localInputs μ—…λ°μ΄νŠΈ μ™„λ£Œ:", { + positionX: currentPos.x.toString(), + positionY: currentPos.y.toString(), + }); + } } }, [ selectedComponent?.id, // ID만 κ°μ§€ν•˜μ—¬ μ»΄ν¬λ„ŒνŠΈ λ³€κ²½ μ‹œμ—λ§Œ μ—…λ°μ΄νŠΈ + selectedComponent?.position.x, // μ»΄ν¬λ„ŒνŠΈ μ‹€μ œ μœ„μΉ˜ λ³€κ²½ 감지 (λ“œλž˜κ·Έ μ™„λ£Œ ν›„) + selectedComponent?.position.y, + selectedComponent?.position.z, // z μœ„μΉ˜λ„ 감지 + dragState?.isDragging, // λ“œλž˜κ·Έ μƒνƒœ λ³€κ²½ 감지 (λ“œλž˜κ·Έ μ™„λ£Œ κ°μ§€μš©) + dragState?.justFinishedDrag, // λ“œλž˜κ·Έ μ™„λ£Œ 직후 감지 ]); + // λ Œλ”λ§ μ‹œλ§ˆλ‹€ μ‹€ν–‰λ˜λŠ” 직접적인 λ“œλž˜κ·Έ μƒνƒœ 체크 + if (dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id) { + console.log("🎯 λ Œλ”λ§ 쀑 λ“œλž˜κ·Έ μƒνƒœ 감지:", { + isDragging: dragState.isDragging, + draggedId: dragState.draggedComponent?.id, + selectedId: selectedComponent?.id, + currentPosition: dragState.currentPosition, + }); + + const newPosition = { + x: dragState.currentPosition.x, + y: dragState.currentPosition.y, + }; + + // μœ„μΉ˜κ°€ λ³€κ²½λ˜μ—ˆλŠ”μ§€ 확인 + if (lastDragPosition.x !== newPosition.x || lastDragPosition.y !== newPosition.y) { + console.log("πŸ”„ μœ„μΉ˜ λ³€κ²½ 감지됨:", { + oldPosition: lastDragPosition, + newPosition: newPosition, + }); + // λ‹€μŒ λ Œλ”λ§ μ‚¬μ΄ν΄μ—μ„œ μ—…λ°μ΄νŠΈ + setTimeout(() => { + setLastDragPosition(newPosition); + setForceRender((prev) => prev + 1); + }, 0); + } + } + if (!selectedComponent) { return (
@@ -423,13 +517,26 @@ const PropertiesPanelComponent: React.FC = ({ { + const isDragging = dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id; + if (isDragging) { + const realTimeX = Math.round(dragState.currentPosition.x); + console.log("πŸ”₯ μ‹€μ‹œκ°„ X λ Œλ”λ§:", realTimeX, "forceRender:", forceRender); + return realTimeX.toString(); + } + return localInputs.positionX; + })()} onChange={(e) => { const newValue = e.target.value; setLocalInputs((prev) => ({ ...prev, positionX: newValue })); onUpdateProperty("position", { ...selectedComponent.position, x: Number(newValue) }); }} - className="mt-1" + className={`mt-1 ${ + dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id + ? "border-blue-300 bg-blue-50 text-blue-700" + : "" + }`} + readOnly={dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id} />
@@ -440,13 +547,26 @@ const PropertiesPanelComponent: React.FC = ({ { + const isDragging = dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id; + if (isDragging) { + const realTimeY = Math.round(dragState.currentPosition.y); + console.log("πŸ”₯ μ‹€μ‹œκ°„ Y λ Œλ”λ§:", realTimeY, "forceRender:", forceRender); + return realTimeY.toString(); + } + return localInputs.positionY; + })()} onChange={(e) => { const newValue = e.target.value; setLocalInputs((prev) => ({ ...prev, positionY: newValue })); onUpdateProperty("position", { ...selectedComponent.position, y: Number(newValue) }); }} - className="mt-1" + className={`mt-1 ${ + dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id + ? "border-blue-300 bg-blue-50 text-blue-700" + : "" + }`} + readOnly={dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id} /> diff --git a/frontend/components/screen/templates/DataTableTemplate.tsx b/frontend/components/screen/templates/DataTableTemplate.tsx index 1f6a5a8a..041ec848 100644 --- a/frontend/components/screen/templates/DataTableTemplate.tsx +++ b/frontend/components/screen/templates/DataTableTemplate.tsx @@ -214,8 +214,8 @@ export const DataTableTemplate: React.FC = ({ {filters.length > 0 && (
- {filters.slice(0, 3).map((filter) => ( -