dev #46
|
|
@ -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}
|
||||
>
|
||||
<PropertiesPanel
|
||||
key={`properties-${selectedComponent?.id}-${dragState.isDragging ? dragState.currentPosition.x + dragState.currentPosition.y : "static"}`}
|
||||
selectedComponent={selectedComponent || undefined}
|
||||
tables={tables}
|
||||
dragState={dragState}
|
||||
onUpdateProperty={(path: string, value: any) => {
|
||||
console.log("🔧 속성 업데이트 요청:", {
|
||||
componentId: selectedComponent?.id,
|
||||
|
|
|
|||
|
|
@ -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<PropertiesPanelProps> = ({
|
||||
selectedComponent,
|
||||
tables = [],
|
||||
dragState,
|
||||
onUpdateProperty,
|
||||
onDeleteComponent,
|
||||
onCopyComponent,
|
||||
|
|
@ -116,9 +122,29 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
|
|||
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<PropertiesPanelProps> = ({
|
|||
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<PropertiesPanelProps> = ({
|
|||
? (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<PropertiesPanelProps> = ({
|
|||
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 (
|
||||
<div className="flex h-full flex-col items-center justify-center p-6 text-center">
|
||||
|
|
@ -423,13 +517,26 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
|
|||
<Input
|
||||
id="positionX"
|
||||
type="number"
|
||||
value={localInputs.positionX}
|
||||
value={(() => {
|
||||
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}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -440,13 +547,26 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
|
|||
<Input
|
||||
id="positionY"
|
||||
type="number"
|
||||
value={localInputs.positionY}
|
||||
value={(() => {
|
||||
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}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -214,8 +214,8 @@ export const DataTableTemplate: React.FC<DataTableTemplateProps> = ({
|
|||
{filters.length > 0 && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Filter className="text-muted-foreground h-4 w-4" />
|
||||
{filters.slice(0, 3).map((filter) => (
|
||||
<Select key={filter.id} disabled={isPreview}>
|
||||
{filters.slice(0, 3).map((filter, index) => (
|
||||
<Select key={filter.id || `filter-${index}`} disabled={isPreview}>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectValue placeholder={filter.label} />
|
||||
</SelectTrigger>
|
||||
|
|
|
|||
Loading…
Reference in New Issue