컴포넌트 그룹화(Grouping) 기능 구현

This commit is contained in:
dohyeons
2025-10-01 16:33:25 +09:00
parent d01ade4e4f
commit f8be19c49f
5 changed files with 202 additions and 22 deletions

View File

@@ -10,6 +10,7 @@ interface CanvasComponentProps {
export function CanvasComponent({ component }: CanvasComponentProps) {
const {
components,
selectedComponentId,
selectedComponentIds,
selectComponent,
@@ -28,6 +29,7 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
const isSelected = selectedComponentId === component.id;
const isMultiSelected = selectedComponentIds.includes(component.id);
const isLocked = component.locked === true;
const isGrouped = !!component.groupId;
// 드래그 시작
const handleMouseDown = (e: React.MouseEvent) => {
@@ -48,7 +50,17 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
// Ctrl/Cmd 키 감지 (다중 선택)
const isMultiSelect = e.ctrlKey || e.metaKey;
selectComponent(component.id, isMultiSelect);
// 그룹화된 컴포넌트 클릭 시: 같은 그룹의 모든 컴포넌트 선택
if (isGrouped && !isMultiSelect) {
const groupMembers = components.filter((c) => c.groupId === component.groupId);
const groupMemberIds = groupMembers.map((c) => c.id);
// 첫 번째 컴포넌트를 선택하고, 나머지를 다중 선택에 추가
selectComponent(groupMemberIds[0], false);
groupMemberIds.slice(1).forEach((id) => selectComponent(id, true));
} else {
selectComponent(component.id, isMultiSelect);
}
setIsDragging(true);
setDragStart({
@@ -89,11 +101,27 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
// 정렬 가이드라인 계산
calculateAlignmentGuides(component.id, snappedX, snappedY, component.width, component.height);
// Grid Snap 적용
// 이동 거리 계산
const deltaX = snappedX - component.x;
const deltaY = snappedY - component.y;
// 현재 컴포넌트 이동
updateComponent(component.id, {
x: snappedX,
y: snappedY,
});
// 그룹화된 경우: 같은 그룹의 다른 컴포넌트도 함께 이동
if (isGrouped) {
components.forEach((c) => {
if (c.groupId === component.groupId && c.id !== component.id) {
updateComponent(c.id, {
x: c.x + deltaX,
y: c.y + deltaY,
});
}
});
}
} else if (isResizing) {
const deltaX = e.clientX - resizeStart.x;
const deltaY = e.clientY - resizeStart.y;
@@ -131,8 +159,13 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
resizeStart.width,
resizeStart.height,
component.id,
component.x,
component.y,
component.width,
component.height,
component.groupId,
isGrouped,
components,
updateComponent,
snapValueToGrid,
calculateAlignmentGuides,
@@ -324,6 +357,11 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
<div className="absolute top-1 right-1 rounded bg-red-500 px-1 py-0.5 text-[10px] text-white">🔒</div>
)}
{/* 그룹화 표시 */}
{isGrouped && !isLocked && (
<div className="absolute top-1 left-1 rounded bg-purple-500 px-1 py-0.5 text-[10px] text-white">👥</div>
)}
{/* 리사이즈 핸들 (선택된 경우만, 잠금 안 된 경우만) */}
{isSelected && !isLocked && (
<div