레이어 관리 구현
This commit is contained in:
parent
722a413916
commit
172ecf34b3
|
|
@ -22,6 +22,10 @@ import {
|
|||
RectangleHorizontal,
|
||||
RectangleVertical,
|
||||
Square,
|
||||
ChevronDown,
|
||||
ChevronsDown,
|
||||
ChevronsUp,
|
||||
ChevronUp,
|
||||
} from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
|
||||
|
|
@ -70,14 +74,19 @@ export function ReportDesignerToolbar() {
|
|||
makeSameWidth,
|
||||
makeSameHeight,
|
||||
makeSameSize,
|
||||
bringToFront,
|
||||
sendToBack,
|
||||
bringForward,
|
||||
sendBackward,
|
||||
} = useReportDesigner();
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
const [showSaveAsTemplate, setShowSaveAsTemplate] = useState(false);
|
||||
const { toast } = useToast();
|
||||
|
||||
// 정렬 버튼 활성화 조건
|
||||
// 버튼 활성화 조건
|
||||
const canAlign = selectedComponentIds && selectedComponentIds.length >= 2;
|
||||
const canDistribute = selectedComponentIds && selectedComponentIds.length >= 3;
|
||||
const hasSelection = selectedComponentIds && selectedComponentIds.length >= 1;
|
||||
|
||||
// 템플릿 저장 가능 여부: 컴포넌트가 있어야 함
|
||||
const canSaveAsTemplate = components.length > 0;
|
||||
|
|
@ -316,6 +325,38 @@ export function ReportDesignerToolbar() {
|
|||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
{/* 레이어 드롭다운 */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={!hasSelection}
|
||||
className="gap-2"
|
||||
title="레이어 순서 (1개 이상 선택 필요)"
|
||||
>
|
||||
<ChevronsUp className="h-4 w-4" />
|
||||
레이어
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={bringToFront}>
|
||||
<ChevronsUp className="mr-2 h-4 w-4" />맨 앞으로
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={bringForward}>
|
||||
<ChevronUp className="mr-2 h-4 w-4" />한 단계 앞으로
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={sendBackward}>
|
||||
<ChevronDown className="mr-2 h-4 w-4" />한 단계 뒤로
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={sendToBack}>
|
||||
<ChevronsDown className="mr-2 h-4 w-4" />맨 뒤로
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
<Button variant="outline" size="sm" onClick={handleReset} className="gap-2">
|
||||
<RotateCcw className="h-4 w-4" />
|
||||
초기화
|
||||
|
|
|
|||
|
|
@ -345,6 +345,12 @@ interface ReportDesignerContextType {
|
|||
makeSameWidth: () => void;
|
||||
makeSameHeight: () => void;
|
||||
makeSameSize: () => void;
|
||||
|
||||
// 레이어 관리
|
||||
bringToFront: () => void;
|
||||
sendToBack: () => void;
|
||||
bringForward: () => void;
|
||||
sendBackward: () => void;
|
||||
}
|
||||
|
||||
const ReportDesignerContext = createContext<ReportDesignerContextType | undefined>(undefined);
|
||||
|
|
@ -769,6 +775,87 @@ export function ReportDesignerProvider({ reportId, children }: { reportId: strin
|
|||
toast({ title: "크기 조정 완료", description: "같은 크기로 조정되었습니다." });
|
||||
}, [getSelectedComponents, toast]);
|
||||
|
||||
// 레이어 관리 함수들
|
||||
const bringToFront = useCallback(() => {
|
||||
if (!selectedComponentId && selectedComponentIds.length === 0) return;
|
||||
|
||||
const idsToUpdate =
|
||||
selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);
|
||||
const maxZIndex = Math.max(...components.map((c) => c.zIndex));
|
||||
|
||||
setComponents((prev) =>
|
||||
prev.map((c) => {
|
||||
if (idsToUpdate.includes(c.id)) {
|
||||
return { ...c, zIndex: maxZIndex + 1 };
|
||||
}
|
||||
return c;
|
||||
}),
|
||||
);
|
||||
|
||||
toast({ title: "레이어 변경", description: "맨 앞으로 이동했습니다." });
|
||||
}, [selectedComponentId, selectedComponentIds, components, toast]);
|
||||
|
||||
const sendToBack = useCallback(() => {
|
||||
if (!selectedComponentId && selectedComponentIds.length === 0) return;
|
||||
|
||||
const idsToUpdate =
|
||||
selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);
|
||||
const minZIndex = Math.min(...components.map((c) => c.zIndex));
|
||||
|
||||
setComponents((prev) =>
|
||||
prev.map((c) => {
|
||||
if (idsToUpdate.includes(c.id)) {
|
||||
return { ...c, zIndex: minZIndex - 1 };
|
||||
}
|
||||
return c;
|
||||
}),
|
||||
);
|
||||
|
||||
toast({ title: "레이어 변경", description: "맨 뒤로 이동했습니다." });
|
||||
}, [selectedComponentId, selectedComponentIds, components, toast]);
|
||||
|
||||
const bringForward = useCallback(() => {
|
||||
if (!selectedComponentId && selectedComponentIds.length === 0) return;
|
||||
|
||||
const idsToUpdate =
|
||||
selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);
|
||||
|
||||
setComponents((prev) => {
|
||||
const sorted = [...prev].sort((a, b) => a.zIndex - b.zIndex);
|
||||
const updated = sorted.map((c, index) => ({ ...c, zIndex: index }));
|
||||
|
||||
return updated.map((c) => {
|
||||
if (idsToUpdate.includes(c.id)) {
|
||||
return { ...c, zIndex: Math.min(c.zIndex + 1, updated.length - 1) };
|
||||
}
|
||||
return c;
|
||||
});
|
||||
});
|
||||
|
||||
toast({ title: "레이어 변경", description: "한 단계 앞으로 이동했습니다." });
|
||||
}, [selectedComponentId, selectedComponentIds, toast]);
|
||||
|
||||
const sendBackward = useCallback(() => {
|
||||
if (!selectedComponentId && selectedComponentIds.length === 0) return;
|
||||
|
||||
const idsToUpdate =
|
||||
selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);
|
||||
|
||||
setComponents((prev) => {
|
||||
const sorted = [...prev].sort((a, b) => a.zIndex - b.zIndex);
|
||||
const updated = sorted.map((c, index) => ({ ...c, zIndex: index }));
|
||||
|
||||
return updated.map((c) => {
|
||||
if (idsToUpdate.includes(c.id)) {
|
||||
return { ...c, zIndex: Math.max(c.zIndex - 1, 0) };
|
||||
}
|
||||
return c;
|
||||
});
|
||||
});
|
||||
|
||||
toast({ title: "레이어 변경", description: "한 단계 뒤로 이동했습니다." });
|
||||
}, [selectedComponentId, selectedComponentIds, toast]);
|
||||
|
||||
// 캔버스 설정 (기본값)
|
||||
const [canvasWidth, setCanvasWidth] = useState(210);
|
||||
const [canvasHeight, setCanvasHeight] = useState(297);
|
||||
|
|
@ -1180,6 +1267,11 @@ export function ReportDesignerProvider({ reportId, children }: { reportId: strin
|
|||
makeSameWidth,
|
||||
makeSameHeight,
|
||||
makeSameSize,
|
||||
// 레이어 관리
|
||||
bringToFront,
|
||||
sendToBack,
|
||||
bringForward,
|
||||
sendBackward,
|
||||
};
|
||||
|
||||
return <ReportDesignerContext.Provider value={value}>{children}</ReportDesignerContext.Provider>;
|
||||
|
|
|
|||
Loading…
Reference in New Issue