2026-02-02 15:15:01 +09:00
|
|
|
"use client";
|
|
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
import { useCallback, useRef, useState, useEffect } from "react";
|
2026-02-02 15:15:01 +09:00
|
|
|
import { useDrop } from "react-dnd";
|
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
|
import {
|
2026-02-03 19:11:03 +09:00
|
|
|
PopLayoutDataV3,
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
PopLayoutModeKey,
|
2026-02-02 15:15:01 +09:00
|
|
|
PopComponentType,
|
|
|
|
|
GridPosition,
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
MODE_RESOLUTIONS,
|
2026-02-02 15:15:01 +09:00
|
|
|
} from "./types/pop-layout";
|
2026-02-03 19:11:03 +09:00
|
|
|
import { DND_ITEM_TYPES, DragItemComponent } from "./panels/PopPanel";
|
|
|
|
|
import { ZoomIn, ZoomOut, Maximize2 } from "lucide-react";
|
2026-02-02 15:15:01 +09:00
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
// ========================================
|
|
|
|
|
// 타입 정의
|
|
|
|
|
// ========================================
|
2026-02-02 15:15:01 +09:00
|
|
|
type DeviceType = "mobile" | "tablet";
|
|
|
|
|
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
// 모드별 라벨
|
|
|
|
|
const MODE_LABELS: Record<PopLayoutModeKey, string> = {
|
|
|
|
|
tablet_landscape: "태블릿 가로",
|
|
|
|
|
tablet_portrait: "태블릿 세로",
|
|
|
|
|
mobile_landscape: "모바일 가로",
|
|
|
|
|
mobile_portrait: "모바일 세로",
|
|
|
|
|
};
|
2026-02-02 15:15:01 +09:00
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
// 컴포넌트 타입별 라벨
|
|
|
|
|
const COMPONENT_TYPE_LABELS: Record<PopComponentType, string> = {
|
|
|
|
|
"pop-field": "필드",
|
|
|
|
|
"pop-button": "버튼",
|
|
|
|
|
"pop-list": "리스트",
|
|
|
|
|
"pop-indicator": "인디케이터",
|
|
|
|
|
"pop-scanner": "스캐너",
|
|
|
|
|
"pop-numpad": "숫자패드",
|
|
|
|
|
};
|
|
|
|
|
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
// ========================================
|
|
|
|
|
// Props
|
|
|
|
|
// ========================================
|
2026-02-02 15:15:01 +09:00
|
|
|
interface PopCanvasProps {
|
2026-02-03 19:11:03 +09:00
|
|
|
layout: PopLayoutDataV3;
|
2026-02-02 15:15:01 +09:00
|
|
|
activeDevice: DeviceType;
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
activeModeKey: PopLayoutModeKey;
|
|
|
|
|
onModeKeyChange: (modeKey: PopLayoutModeKey) => void;
|
2026-02-02 15:15:01 +09:00
|
|
|
selectedComponentId: string | null;
|
|
|
|
|
onSelectComponent: (id: string | null) => void;
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
onUpdateComponentPosition: (componentId: string, position: GridPosition, modeKey?: PopLayoutModeKey) => void;
|
2026-02-03 19:11:03 +09:00
|
|
|
onDropComponent: (type: PopComponentType, gridPosition: GridPosition) => void;
|
|
|
|
|
onDeleteComponent: (componentId: string) => void;
|
2026-02-02 15:15:01 +09:00
|
|
|
}
|
|
|
|
|
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
// ========================================
|
|
|
|
|
// 메인 컴포넌트
|
|
|
|
|
// ========================================
|
2026-02-02 15:15:01 +09:00
|
|
|
export function PopCanvas({
|
|
|
|
|
layout,
|
|
|
|
|
activeDevice,
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
activeModeKey,
|
|
|
|
|
onModeKeyChange,
|
2026-02-02 15:15:01 +09:00
|
|
|
selectedComponentId,
|
|
|
|
|
onSelectComponent,
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
onUpdateComponentPosition,
|
2026-02-02 15:15:01 +09:00
|
|
|
onDropComponent,
|
|
|
|
|
onDeleteComponent,
|
|
|
|
|
}: PopCanvasProps) {
|
2026-02-03 19:11:03 +09:00
|
|
|
const { settings, components, layouts } = layout;
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
const canvasGrid = settings.canvasGrid;
|
|
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
// 줌 상태 (0.3 ~ 1.5 범위)
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
const [canvasScale, setCanvasScale] = useState(0.6);
|
|
|
|
|
|
|
|
|
|
// 패닝 상태
|
|
|
|
|
const [isPanning, setIsPanning] = useState(false);
|
|
|
|
|
const [panStart, setPanStart] = useState({ x: 0, y: 0 });
|
2026-02-03 19:11:03 +09:00
|
|
|
const [isSpacePressed, setIsSpacePressed] = useState(false);
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
// 줌 컨트롤
|
|
|
|
|
const handleZoomIn = () => setCanvasScale((prev) => Math.min(1.5, prev + 0.1));
|
|
|
|
|
const handleZoomOut = () => setCanvasScale((prev) => Math.max(0.3, prev - 0.1));
|
|
|
|
|
const handleZoomFit = () => setCanvasScale(1.0);
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
// 패닝
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
const handlePanStart = (e: React.MouseEvent) => {
|
|
|
|
|
const isMiddleButton = e.button === 1;
|
|
|
|
|
const isScrollAreaClick = (e.target as HTMLElement).classList.contains("canvas-scroll-area");
|
|
|
|
|
if (isMiddleButton || isSpacePressed || isScrollAreaClick) {
|
|
|
|
|
setIsPanning(true);
|
|
|
|
|
setPanStart({ x: e.clientX, y: e.clientY });
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePanMove = (e: React.MouseEvent) => {
|
|
|
|
|
if (!isPanning || !containerRef.current) return;
|
|
|
|
|
const deltaX = e.clientX - panStart.x;
|
|
|
|
|
const deltaY = e.clientY - panStart.y;
|
|
|
|
|
containerRef.current.scrollLeft -= deltaX;
|
|
|
|
|
containerRef.current.scrollTop -= deltaY;
|
|
|
|
|
setPanStart({ x: e.clientX, y: e.clientY });
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
const handlePanEnd = () => setIsPanning(false);
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
// 마우스 휠 줌
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
const handleWheel = useCallback((e: React.WheelEvent) => {
|
2026-02-03 19:11:03 +09:00
|
|
|
e.preventDefault();
|
|
|
|
|
const delta = e.deltaY > 0 ? -0.1 : 0.1;
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
setCanvasScale((prev) => Math.max(0.3, Math.min(1.5, prev + delta)));
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// Space 키 감지
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
2026-02-03 19:11:03 +09:00
|
|
|
if (e.code === "Space" && !isSpacePressed) setIsSpacePressed(true);
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
};
|
|
|
|
|
const handleKeyUp = (e: KeyboardEvent) => {
|
2026-02-03 19:11:03 +09:00
|
|
|
if (e.code === "Space") setIsSpacePressed(false);
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
};
|
|
|
|
|
window.addEventListener("keydown", handleKeyDown);
|
|
|
|
|
window.addEventListener("keyup", handleKeyUp);
|
|
|
|
|
return () => {
|
|
|
|
|
window.removeEventListener("keydown", handleKeyDown);
|
|
|
|
|
window.removeEventListener("keyup", handleKeyUp);
|
|
|
|
|
};
|
|
|
|
|
}, [isSpacePressed]);
|
|
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
// 초기 로드 시 캔버스 중앙 스크롤
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (containerRef.current) {
|
|
|
|
|
const container = containerRef.current;
|
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
|
const scrollX = (container.scrollWidth - container.clientWidth) / 2;
|
|
|
|
|
const scrollY = (container.scrollHeight - container.clientHeight) / 2;
|
|
|
|
|
container.scrollTo(scrollX, scrollY);
|
|
|
|
|
}, 100);
|
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
|
}
|
2026-02-03 19:11:03 +09:00
|
|
|
}, [activeDevice]);
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
|
|
|
|
|
// 현재 디바이스의 가로/세로 모드 키
|
|
|
|
|
const landscapeModeKey: PopLayoutModeKey = activeDevice === "tablet"
|
|
|
|
|
? "tablet_landscape"
|
|
|
|
|
: "mobile_landscape";
|
|
|
|
|
const portraitModeKey: PopLayoutModeKey = activeDevice === "tablet"
|
|
|
|
|
? "tablet_portrait"
|
|
|
|
|
: "mobile_portrait";
|
|
|
|
|
|
2026-02-02 15:15:01 +09:00
|
|
|
return (
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
<div className="relative flex h-full flex-col bg-gray-50">
|
|
|
|
|
{/* 줌 컨트롤 바 */}
|
|
|
|
|
<div className="flex shrink-0 items-center justify-end gap-2 border-b bg-white px-4 py-2">
|
|
|
|
|
<span className="text-xs text-muted-foreground">
|
|
|
|
|
줌: {Math.round(canvasScale * 100)}%
|
|
|
|
|
</span>
|
2026-02-03 19:11:03 +09:00
|
|
|
<Button variant="outline" size="icon" className="h-7 w-7" onClick={handleZoomOut} title="줌 아웃">
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
<ZoomOut className="h-4 w-4" />
|
|
|
|
|
</Button>
|
2026-02-03 19:11:03 +09:00
|
|
|
<Button variant="outline" size="icon" className="h-7 w-7" onClick={handleZoomIn} title="줌 인">
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
<ZoomIn className="h-4 w-4" />
|
|
|
|
|
</Button>
|
2026-02-03 19:11:03 +09:00
|
|
|
<Button variant="outline" size="icon" className="h-7 w-7" onClick={handleZoomFit} title="맞춤 (100%)">
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
<Maximize2 className="h-4 w-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
{/* 캔버스 영역 */}
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
<div
|
|
|
|
|
ref={containerRef}
|
|
|
|
|
className={cn(
|
|
|
|
|
"relative flex-1 overflow-auto",
|
|
|
|
|
isPanning && "cursor-grabbing",
|
|
|
|
|
isSpacePressed && "cursor-grab"
|
|
|
|
|
)}
|
|
|
|
|
onMouseDown={handlePanStart}
|
|
|
|
|
onMouseMove={handlePanMove}
|
|
|
|
|
onMouseUp={handlePanEnd}
|
|
|
|
|
onMouseLeave={handlePanEnd}
|
|
|
|
|
onWheel={handleWheel}
|
|
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
className="canvas-scroll-area flex items-center justify-center gap-16"
|
2026-02-03 19:11:03 +09:00
|
|
|
style={{ padding: "500px", minWidth: "fit-content", minHeight: "fit-content" }}
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
>
|
2026-02-03 19:11:03 +09:00
|
|
|
{/* 가로 모드 */}
|
|
|
|
|
<DeviceFrame
|
|
|
|
|
modeKey={landscapeModeKey}
|
|
|
|
|
isActive={landscapeModeKey === activeModeKey}
|
|
|
|
|
scale={canvasScale}
|
|
|
|
|
canvasGrid={canvasGrid}
|
|
|
|
|
layout={layout}
|
|
|
|
|
selectedComponentId={selectedComponentId}
|
|
|
|
|
onModeKeyChange={onModeKeyChange}
|
|
|
|
|
onSelectComponent={onSelectComponent}
|
|
|
|
|
onUpdateComponentPosition={onUpdateComponentPosition}
|
|
|
|
|
onDropComponent={onDropComponent}
|
|
|
|
|
onDeleteComponent={onDeleteComponent}
|
|
|
|
|
/>
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
{/* 세로 모드 */}
|
|
|
|
|
<DeviceFrame
|
|
|
|
|
modeKey={portraitModeKey}
|
|
|
|
|
isActive={portraitModeKey === activeModeKey}
|
|
|
|
|
scale={canvasScale}
|
|
|
|
|
canvasGrid={canvasGrid}
|
|
|
|
|
layout={layout}
|
|
|
|
|
selectedComponentId={selectedComponentId}
|
|
|
|
|
onModeKeyChange={onModeKeyChange}
|
|
|
|
|
onSelectComponent={onSelectComponent}
|
|
|
|
|
onUpdateComponentPosition={onUpdateComponentPosition}
|
|
|
|
|
onDropComponent={onDropComponent}
|
|
|
|
|
onDeleteComponent={onDeleteComponent}
|
|
|
|
|
/>
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-02-02 15:15:01 +09:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
// ========================================
|
2026-02-03 19:11:03 +09:00
|
|
|
// CSS Grid 기반 디바이스 프레임 (v3: 컴포넌트 직접 배치)
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
// ========================================
|
2026-02-03 19:11:03 +09:00
|
|
|
interface DeviceFrameProps {
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
modeKey: PopLayoutModeKey;
|
2026-02-02 15:15:01 +09:00
|
|
|
isActive: boolean;
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
scale: number;
|
2026-02-03 19:11:03 +09:00
|
|
|
canvasGrid: { columns: number; rows: number; gap: number };
|
|
|
|
|
layout: PopLayoutDataV3;
|
2026-02-02 15:15:01 +09:00
|
|
|
selectedComponentId: string | null;
|
2026-02-03 19:11:03 +09:00
|
|
|
onModeKeyChange: (modeKey: PopLayoutModeKey) => void;
|
2026-02-02 15:15:01 +09:00
|
|
|
onSelectComponent: (id: string | null) => void;
|
2026-02-03 19:11:03 +09:00
|
|
|
onUpdateComponentPosition: (componentId: string, position: GridPosition, modeKey?: PopLayoutModeKey) => void;
|
|
|
|
|
onDropComponent: (type: PopComponentType, gridPosition: GridPosition) => void;
|
|
|
|
|
onDeleteComponent: (componentId: string) => void;
|
2026-02-02 15:15:01 +09:00
|
|
|
}
|
|
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
function DeviceFrame({
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
modeKey,
|
2026-02-02 15:15:01 +09:00
|
|
|
isActive,
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
scale,
|
2026-02-03 19:11:03 +09:00
|
|
|
canvasGrid,
|
|
|
|
|
layout,
|
2026-02-02 15:15:01 +09:00
|
|
|
selectedComponentId,
|
2026-02-03 19:11:03 +09:00
|
|
|
onModeKeyChange,
|
2026-02-02 15:15:01 +09:00
|
|
|
onSelectComponent,
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
onUpdateComponentPosition,
|
2026-02-03 19:11:03 +09:00
|
|
|
onDropComponent,
|
2026-02-02 15:15:01 +09:00
|
|
|
onDeleteComponent,
|
2026-02-03 19:11:03 +09:00
|
|
|
}: DeviceFrameProps) {
|
|
|
|
|
const gridRef = useRef<HTMLDivElement>(null);
|
2026-02-02 15:15:01 +09:00
|
|
|
const dropRef = useRef<HTMLDivElement>(null);
|
2026-02-03 19:11:03 +09:00
|
|
|
|
|
|
|
|
const { components, layouts } = layout;
|
|
|
|
|
const resolution = MODE_RESOLUTIONS[modeKey];
|
|
|
|
|
const modeLayout = layouts[modeKey];
|
|
|
|
|
const componentPositions = modeLayout.componentPositions;
|
|
|
|
|
const componentIds = Object.keys(componentPositions);
|
|
|
|
|
|
|
|
|
|
const cols = canvasGrid.columns;
|
|
|
|
|
const rows = canvasGrid.rows || 24;
|
|
|
|
|
const gap = canvasGrid.gap;
|
|
|
|
|
|
|
|
|
|
// 드래그 상태
|
|
|
|
|
const [dragState, setDragState] = useState<{
|
|
|
|
|
componentId: string;
|
|
|
|
|
startPos: GridPosition;
|
|
|
|
|
currentPos: GridPosition;
|
|
|
|
|
isDragging: boolean;
|
|
|
|
|
} | null>(null);
|
|
|
|
|
|
|
|
|
|
// 리사이즈 상태
|
|
|
|
|
const [resizeState, setResizeState] = useState<{
|
|
|
|
|
componentId: string;
|
|
|
|
|
startPos: GridPosition;
|
|
|
|
|
currentPos: GridPosition;
|
|
|
|
|
handle: "se" | "sw" | "ne" | "nw" | "e" | "w" | "n" | "s";
|
|
|
|
|
isResizing: boolean;
|
|
|
|
|
} | null>(null);
|
|
|
|
|
|
|
|
|
|
// 라벨
|
|
|
|
|
const sizeLabel = `${resolution.width}x${resolution.height}`;
|
|
|
|
|
const modeLabel = `${MODE_LABELS[modeKey]} (${sizeLabel})`;
|
|
|
|
|
|
|
|
|
|
// 마우스 → 그리드 좌표 변환
|
|
|
|
|
const getGridPosition = useCallback((clientX: number, clientY: number): { col: number; row: number } => {
|
|
|
|
|
if (!gridRef.current) return { col: 1, row: 1 };
|
|
|
|
|
const rect = gridRef.current.getBoundingClientRect();
|
|
|
|
|
const x = (clientX - rect.left) / scale;
|
|
|
|
|
const y = (clientY - rect.top) / scale;
|
|
|
|
|
const cellWidth = (resolution.width - gap * (cols + 1)) / cols;
|
|
|
|
|
const cellHeight = (resolution.height - gap * (rows + 1)) / rows;
|
|
|
|
|
const col = Math.max(1, Math.min(cols, Math.floor((x - gap) / (cellWidth + gap)) + 1));
|
|
|
|
|
const row = Math.max(1, Math.min(rows, Math.floor((y - gap) / (cellHeight + gap)) + 1));
|
|
|
|
|
return { col, row };
|
|
|
|
|
}, [scale, resolution, cols, rows, gap]);
|
|
|
|
|
|
|
|
|
|
// 드래그 시작
|
|
|
|
|
const handleDragStart = useCallback((e: React.MouseEvent, componentId: string) => {
|
|
|
|
|
if (!isActive) return;
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
const pos = componentPositions[componentId];
|
|
|
|
|
setDragState({
|
|
|
|
|
componentId,
|
|
|
|
|
startPos: { ...pos },
|
|
|
|
|
currentPos: { ...pos },
|
|
|
|
|
isDragging: true,
|
|
|
|
|
});
|
|
|
|
|
}, [isActive, componentPositions]);
|
|
|
|
|
|
|
|
|
|
// 마우스 이동
|
|
|
|
|
const handleMouseMove = useCallback((e: React.MouseEvent) => {
|
|
|
|
|
if (dragState?.isDragging && gridRef.current) {
|
|
|
|
|
const { col, row } = getGridPosition(e.clientX, e.clientY);
|
|
|
|
|
const newCol = Math.max(1, Math.min(cols - dragState.startPos.colSpan + 1, col));
|
|
|
|
|
const newRow = Math.max(1, Math.min(rows - dragState.startPos.rowSpan + 1, row));
|
|
|
|
|
setDragState(prev => prev ? {
|
|
|
|
|
...prev,
|
|
|
|
|
currentPos: { ...prev.startPos, col: newCol, row: newRow }
|
|
|
|
|
} : null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (resizeState?.isResizing && gridRef.current) {
|
|
|
|
|
const { col, row } = getGridPosition(e.clientX, e.clientY);
|
|
|
|
|
const startPos = resizeState.startPos;
|
|
|
|
|
let newPos = { ...startPos };
|
|
|
|
|
|
|
|
|
|
switch (resizeState.handle) {
|
|
|
|
|
case "se":
|
|
|
|
|
newPos.colSpan = Math.max(2, col - startPos.col + 1);
|
|
|
|
|
newPos.rowSpan = Math.max(2, row - startPos.row + 1);
|
|
|
|
|
break;
|
|
|
|
|
case "e":
|
|
|
|
|
newPos.colSpan = Math.max(2, col - startPos.col + 1);
|
|
|
|
|
break;
|
|
|
|
|
case "s":
|
|
|
|
|
newPos.rowSpan = Math.max(2, row - startPos.row + 1);
|
|
|
|
|
break;
|
|
|
|
|
case "sw":
|
|
|
|
|
const newColSW = Math.min(col, startPos.col + startPos.colSpan - 2);
|
|
|
|
|
newPos.col = newColSW;
|
|
|
|
|
newPos.colSpan = startPos.col + startPos.colSpan - newColSW;
|
|
|
|
|
newPos.rowSpan = Math.max(2, row - startPos.row + 1);
|
|
|
|
|
break;
|
|
|
|
|
case "w":
|
|
|
|
|
const newColW = Math.min(col, startPos.col + startPos.colSpan - 2);
|
|
|
|
|
newPos.col = newColW;
|
|
|
|
|
newPos.colSpan = startPos.col + startPos.colSpan - newColW;
|
|
|
|
|
break;
|
|
|
|
|
case "ne":
|
|
|
|
|
const newRowNE = Math.min(row, startPos.row + startPos.rowSpan - 2);
|
|
|
|
|
newPos.row = newRowNE;
|
|
|
|
|
newPos.rowSpan = startPos.row + startPos.rowSpan - newRowNE;
|
|
|
|
|
newPos.colSpan = Math.max(2, col - startPos.col + 1);
|
|
|
|
|
break;
|
|
|
|
|
case "n":
|
|
|
|
|
const newRowN = Math.min(row, startPos.row + startPos.rowSpan - 2);
|
|
|
|
|
newPos.row = newRowN;
|
|
|
|
|
newPos.rowSpan = startPos.row + startPos.rowSpan - newRowN;
|
|
|
|
|
break;
|
|
|
|
|
case "nw":
|
|
|
|
|
const newColNW = Math.min(col, startPos.col + startPos.colSpan - 2);
|
|
|
|
|
const newRowNW = Math.min(row, startPos.row + startPos.rowSpan - 2);
|
|
|
|
|
newPos.col = newColNW;
|
|
|
|
|
newPos.row = newRowNW;
|
|
|
|
|
newPos.colSpan = startPos.col + startPos.colSpan - newColNW;
|
|
|
|
|
newPos.rowSpan = startPos.row + startPos.rowSpan - newRowNW;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newPos.col = Math.max(1, newPos.col);
|
|
|
|
|
newPos.row = Math.max(1, newPos.row);
|
|
|
|
|
newPos.colSpan = Math.min(cols - newPos.col + 1, newPos.colSpan);
|
|
|
|
|
newPos.rowSpan = Math.min(rows - newPos.row + 1, newPos.rowSpan);
|
|
|
|
|
|
|
|
|
|
setResizeState(prev => prev ? { ...prev, currentPos: newPos } : null);
|
|
|
|
|
}
|
|
|
|
|
}, [dragState, resizeState, getGridPosition, cols, rows]);
|
2026-02-02 15:15:01 +09:00
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
// 드래그/리사이즈 종료
|
|
|
|
|
const handleMouseUp = useCallback(() => {
|
|
|
|
|
if (dragState?.isDragging) {
|
|
|
|
|
onUpdateComponentPosition(dragState.componentId, dragState.currentPos, modeKey);
|
|
|
|
|
setDragState(null);
|
|
|
|
|
}
|
|
|
|
|
if (resizeState?.isResizing) {
|
|
|
|
|
onUpdateComponentPosition(resizeState.componentId, resizeState.currentPos, modeKey);
|
|
|
|
|
setResizeState(null);
|
|
|
|
|
}
|
|
|
|
|
}, [dragState, resizeState, onUpdateComponentPosition, modeKey]);
|
|
|
|
|
|
|
|
|
|
// 리사이즈 시작
|
|
|
|
|
const handleResizeStart = useCallback((e: React.MouseEvent, componentId: string, handle: string) => {
|
|
|
|
|
if (!isActive) return;
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
const pos = componentPositions[componentId];
|
|
|
|
|
setResizeState({
|
|
|
|
|
componentId,
|
|
|
|
|
startPos: { ...pos },
|
|
|
|
|
currentPos: { ...pos },
|
|
|
|
|
handle: handle as any,
|
|
|
|
|
isResizing: true,
|
|
|
|
|
});
|
|
|
|
|
}, [isActive, componentPositions]);
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
// 컴포넌트 드롭
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
const [{ isOver, canDrop }, drop] = useDrop(
|
|
|
|
|
() => ({
|
2026-02-03 19:11:03 +09:00
|
|
|
accept: DND_ITEM_TYPES.COMPONENT,
|
|
|
|
|
drop: (item: DragItemComponent, monitor) => {
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
if (!isActive) return;
|
|
|
|
|
const clientOffset = monitor.getClientOffset();
|
2026-02-03 19:11:03 +09:00
|
|
|
if (!clientOffset || !gridRef.current) return;
|
|
|
|
|
const { col, row } = getGridPosition(clientOffset.x, clientOffset.y);
|
|
|
|
|
onDropComponent(item.componentType, { col, row, colSpan: 4, rowSpan: 3 });
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
},
|
|
|
|
|
canDrop: () => isActive,
|
|
|
|
|
collect: (monitor) => ({
|
|
|
|
|
isOver: monitor.isOver(),
|
|
|
|
|
canDrop: monitor.canDrop(),
|
|
|
|
|
}),
|
2026-02-02 15:15:01 +09:00
|
|
|
}),
|
2026-02-03 19:11:03 +09:00
|
|
|
[isActive, getGridPosition, onDropComponent]
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
);
|
2026-02-02 15:15:01 +09:00
|
|
|
|
|
|
|
|
drop(dropRef);
|
|
|
|
|
|
2026-02-03 19:11:03 +09:00
|
|
|
// 현재 표시할 위치
|
|
|
|
|
const getDisplayPosition = (componentId: string): GridPosition => {
|
|
|
|
|
if (dragState?.componentId === componentId && dragState.isDragging) {
|
|
|
|
|
return dragState.currentPos;
|
|
|
|
|
}
|
|
|
|
|
if (resizeState?.componentId === componentId && resizeState.isResizing) {
|
|
|
|
|
return resizeState.currentPos;
|
|
|
|
|
}
|
|
|
|
|
return componentPositions[componentId];
|
|
|
|
|
};
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
|
2026-02-02 15:15:01 +09:00
|
|
|
return (
|
2026-02-03 19:11:03 +09:00
|
|
|
<div className="relative shrink-0">
|
|
|
|
|
{/* 모드 라벨 */}
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"absolute -top-6 left-1/2 -translate-x-1/2 whitespace-nowrap text-xs font-medium",
|
|
|
|
|
isActive ? "text-primary" : "text-muted-foreground"
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{modeLabel}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 디바이스 프레임 */}
|
|
|
|
|
<div
|
|
|
|
|
ref={dropRef}
|
|
|
|
|
className={cn(
|
|
|
|
|
"relative cursor-pointer overflow-hidden rounded-xl bg-white shadow-lg transition-all",
|
|
|
|
|
isActive ? "ring-2 ring-primary ring-offset-2" : "ring-1 ring-gray-200 hover:ring-gray-300",
|
|
|
|
|
isOver && canDrop && "ring-2 ring-primary bg-primary/5"
|
|
|
|
|
)}
|
|
|
|
|
style={{
|
|
|
|
|
width: resolution.width * scale,
|
|
|
|
|
height: resolution.height * scale,
|
|
|
|
|
}}
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
if (e.target === e.currentTarget) {
|
|
|
|
|
if (!isActive) onModeKeyChange(modeKey);
|
|
|
|
|
else onSelectComponent(null);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
onMouseMove={handleMouseMove}
|
|
|
|
|
onMouseUp={handleMouseUp}
|
|
|
|
|
onMouseLeave={handleMouseUp}
|
|
|
|
|
>
|
|
|
|
|
{/* CSS Grid (뷰어와 동일) */}
|
|
|
|
|
<div
|
|
|
|
|
ref={gridRef}
|
|
|
|
|
className="origin-top-left"
|
|
|
|
|
style={{
|
|
|
|
|
transform: `scale(${scale})`,
|
|
|
|
|
width: resolution.width,
|
|
|
|
|
height: resolution.height,
|
|
|
|
|
display: "grid",
|
|
|
|
|
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
|
|
|
gridTemplateRows: `repeat(${rows}, 1fr)`,
|
|
|
|
|
gap: `${gap}px`,
|
|
|
|
|
padding: `${gap}px`,
|
|
|
|
|
}}
|
2026-02-02 15:15:01 +09:00
|
|
|
>
|
2026-02-03 19:11:03 +09:00
|
|
|
{componentIds.length > 0 ? (
|
|
|
|
|
componentIds.map((componentId) => {
|
|
|
|
|
const compDef = components[componentId];
|
|
|
|
|
if (!compDef) return null;
|
|
|
|
|
|
|
|
|
|
const pos = getDisplayPosition(componentId);
|
|
|
|
|
const isSelected = selectedComponentId === componentId;
|
|
|
|
|
const isDragging = dragState?.componentId === componentId && dragState.isDragging;
|
|
|
|
|
const isResizing = resizeState?.componentId === componentId && resizeState.isResizing;
|
|
|
|
|
|
|
|
|
|
return (
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
<div
|
2026-02-03 19:11:03 +09:00
|
|
|
key={componentId}
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
className={cn(
|
2026-02-03 19:11:03 +09:00
|
|
|
"group relative flex cursor-move items-center justify-center overflow-hidden rounded-lg border-2 bg-white transition-all",
|
|
|
|
|
isSelected
|
|
|
|
|
? "border-primary ring-2 ring-primary/30"
|
|
|
|
|
: "border-gray-200 hover:border-gray-300",
|
|
|
|
|
(isDragging || isResizing) && "opacity-80 shadow-xl z-50"
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
)}
|
2026-02-03 19:11:03 +09:00
|
|
|
style={{
|
|
|
|
|
gridColumn: `${pos.col} / span ${pos.colSpan}`,
|
|
|
|
|
gridRow: `${pos.row} / span ${pos.rowSpan}`,
|
|
|
|
|
}}
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
if (!isActive) onModeKeyChange(modeKey);
|
|
|
|
|
onSelectComponent(componentId);
|
|
|
|
|
}}
|
|
|
|
|
onMouseDown={(e) => handleDragStart(e, componentId)}
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
>
|
2026-02-03 19:11:03 +09:00
|
|
|
{/* 컴포넌트 라벨 */}
|
|
|
|
|
<span className="text-xs text-gray-500 select-none">
|
|
|
|
|
{compDef.label || COMPONENT_TYPE_LABELS[compDef.type]}
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
|
|
{/* 리사이즈 핸들 */}
|
|
|
|
|
{isActive && isSelected && (
|
|
|
|
|
<>
|
|
|
|
|
<div className="absolute -right-1 -bottom-1 h-3 w-3 cursor-se-resize rounded-full bg-primary" onMouseDown={(e) => handleResizeStart(e, componentId, "se")} />
|
|
|
|
|
<div className="absolute -left-1 -bottom-1 h-3 w-3 cursor-sw-resize rounded-full bg-primary" onMouseDown={(e) => handleResizeStart(e, componentId, "sw")} />
|
|
|
|
|
<div className="absolute -right-1 -top-1 h-3 w-3 cursor-ne-resize rounded-full bg-primary" onMouseDown={(e) => handleResizeStart(e, componentId, "ne")} />
|
|
|
|
|
<div className="absolute -left-1 -top-1 h-3 w-3 cursor-nw-resize rounded-full bg-primary" onMouseDown={(e) => handleResizeStart(e, componentId, "nw")} />
|
|
|
|
|
<div className="absolute right-0 top-1/2 h-6 w-1.5 -translate-y-1/2 cursor-e-resize rounded bg-primary/50" onMouseDown={(e) => handleResizeStart(e, componentId, "e")} />
|
|
|
|
|
<div className="absolute left-0 top-1/2 h-6 w-1.5 -translate-y-1/2 cursor-w-resize rounded bg-primary/50" onMouseDown={(e) => handleResizeStart(e, componentId, "w")} />
|
|
|
|
|
<div className="absolute bottom-0 left-1/2 h-1.5 w-6 -translate-x-1/2 cursor-s-resize rounded bg-primary/50" onMouseDown={(e) => handleResizeStart(e, componentId, "s")} />
|
|
|
|
|
<div className="absolute top-0 left-1/2 h-1.5 w-6 -translate-x-1/2 cursor-n-resize rounded bg-primary/50" onMouseDown={(e) => handleResizeStart(e, componentId, "n")} />
|
|
|
|
|
</>
|
feat(pop-designer): POP 디자이너 v2.0 - 4가지 디바이스 모드 및 캔버스 UX 개선
- v2 레이아웃 데이터 구조 도입 (4모드별 별도 레이아웃 + 공유 컴포넌트 정의)
- tablet_landscape, tablet_portrait, mobile_landscape, mobile_portrait
- sections/components를 Record<string, Definition> 객체로 관리
- v1 → v2 자동 마이그레이션 지원
- 캔버스 UX 개선
- 줌 기능 (30%~150%, 마우스 휠 + 버튼)
- 패닝 기능 (중앙 마우스, Space+드래그, 배경 드래그)
- 2개 캔버스 동시 표시 (가로/세로 모드)
- Delete 키로 섹션/컴포넌트 삭제 기능 추가
- layout.sections 순회하여 componentIds에서 부모 섹션 찾는 방식
- 미리보기 v2 레이아웃 호환성 수정
- Object.keys(layout.sections).length 체크로 변경
수정 파일: PopDesigner.tsx, PopCanvas.tsx, SectionGridV2.tsx(신규),
types/pop-layout.ts, PopPanel.tsx, PopScreenPreview.tsx,
PopCategoryTree.tsx, screenManagementService.ts
2026-02-03 11:25:00 +09:00
|
|
|
)}
|
2026-02-02 15:15:01 +09:00
|
|
|
</div>
|
2026-02-03 19:11:03 +09:00
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
) : (
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"col-span-full row-span-full flex items-center justify-center text-sm",
|
|
|
|
|
isOver && canDrop ? "text-primary" : "text-gray-400"
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{isOver && canDrop
|
|
|
|
|
? "여기에 컴포넌트를 놓으세요"
|
|
|
|
|
: isActive
|
|
|
|
|
? "왼쪽 패널에서 컴포넌트를 드래그하세요"
|
|
|
|
|
: "클릭하여 편집"}
|
|
|
|
|
</div>
|
2026-02-02 15:15:01 +09:00
|
|
|
)}
|
|
|
|
|
</div>
|
2026-02-03 19:11:03 +09:00
|
|
|
</div>
|
2026-02-02 15:15:01 +09:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|