캔버스 여백 설정

This commit is contained in:
dohyeons 2025-10-02 14:19:38 +09:00
parent 27e33e27d1
commit 4e1e5b0d51
3 changed files with 59 additions and 66 deletions

View File

@ -22,6 +22,7 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
clearAlignmentGuides,
canvasWidth,
canvasHeight,
margins,
} = useReportDesigner();
const [isDragging, setIsDragging] = useState(false);
const [isResizing, setIsResizing] = useState(false);
@ -99,16 +100,24 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
const newX = Math.max(0, e.clientX - dragStart.x);
const newY = Math.max(0, e.clientY - dragStart.y);
// 캔버스 경계 체크 (mm를 px로 변환: 1mm ≈ 3.7795px)
// 여백을 px로 변환 (1mm ≈ 3.7795px)
const marginTopPx = margins.top * 3.7795;
const marginBottomPx = margins.bottom * 3.7795;
const marginLeftPx = margins.left * 3.7795;
const marginRightPx = margins.right * 3.7795;
// 캔버스 경계 체크 (mm를 px로 변환)
const canvasWidthPx = canvasWidth * 3.7795;
const canvasHeightPx = canvasHeight * 3.7795;
// 컴포넌트가 캔버스 안에 있도록 제한
const maxX = canvasWidthPx - component.width;
const maxY = canvasHeightPx - component.height;
// 컴포넌트가 여백 안에 있도록 제한
const minX = marginLeftPx;
const minY = marginTopPx;
const maxX = canvasWidthPx - marginRightPx - component.width;
const maxY = canvasHeightPx - marginBottomPx - component.height;
const boundedX = Math.min(Math.max(0, newX), maxX);
const boundedY = Math.min(Math.max(0, newY), maxY);
const boundedX = Math.min(Math.max(minX, newX), maxX);
const boundedY = Math.min(Math.max(minY, newY), maxY);
const snappedX = snapValueToGrid(boundedX);
const snappedY = snapValueToGrid(boundedY);
@ -150,13 +159,17 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
const newWidth = Math.max(50, resizeStart.width + deltaX);
const newHeight = Math.max(30, resizeStart.height + deltaY);
// 여백을 px로 변환
const marginRightPx = margins.right * 3.7795;
const marginBottomPx = margins.bottom * 3.7795;
// 캔버스 경계 체크
const canvasWidthPx = canvasWidth * 3.7795;
const canvasHeightPx = canvasHeight * 3.7795;
// 컴포넌트가 캔버스를 벗어나지 않도록 최대 크기 제한
const maxWidth = canvasWidthPx - component.x;
const maxHeight = canvasHeightPx - component.y;
// 컴포넌트가 여백을 벗어나지 않도록 최대 크기 제한
const maxWidth = canvasWidthPx - marginRightPx - component.x;
const maxHeight = canvasHeightPx - marginBottomPx - component.y;
const boundedWidth = Math.min(newWidth, maxWidth);
const boundedHeight = Math.min(newHeight, maxHeight);

View File

@ -18,6 +18,7 @@ export function ReportDesignerCanvas() {
updateComponent,
canvasWidth,
canvasHeight,
margins,
selectComponent,
selectedComponentId,
selectedComponentIds,
@ -66,12 +67,33 @@ export function ReportDesignerCanvas() {
height = 70;
}
// 여백을 px로 변환 (1mm ≈ 3.7795px)
const marginTopPx = margins.top * 3.7795;
const marginLeftPx = margins.left * 3.7795;
const marginRightPx = margins.right * 3.7795;
const marginBottomPx = margins.bottom * 3.7795;
// 캔버스 경계 (px)
const canvasWidthPx = canvasWidth * 3.7795;
const canvasHeightPx = canvasHeight * 3.7795;
// 드롭 위치 계산 (여백 내부로 제한)
const rawX = x - 100;
const rawY = y - 25;
const minX = marginLeftPx;
const minY = marginTopPx;
const maxX = canvasWidthPx - marginRightPx - width;
const maxY = canvasHeightPx - marginBottomPx - height;
const boundedX = Math.min(Math.max(minX, rawX), maxX);
const boundedY = Math.min(Math.max(minY, rawY), maxY);
// 새 컴포넌트 생성 (Grid Snap 적용)
const newComponent: ComponentConfig = {
id: `comp_${uuidv4()}`,
type: item.componentType,
x: snapValueToGrid(Math.max(0, x - 100)),
y: snapValueToGrid(Math.max(0, y - 25)),
x: snapValueToGrid(boundedX),
y: snapValueToGrid(boundedY),
width: snapValueToGrid(width),
height: snapValueToGrid(height),
zIndex: components.length,
@ -319,6 +341,19 @@ export function ReportDesignerCanvas() {
}}
onClick={handleCanvasClick}
>
{/* 페이지 여백 가이드 */}
{currentPage && (
<div
className="pointer-events-none absolute border-2 border-dashed border-blue-300/50"
style={{
top: `${currentPage.margins.top}mm`,
left: `${currentPage.margins.left}mm`,
right: `${currentPage.margins.right}mm`,
bottom: `${currentPage.margins.bottom}mm`,
}}
/>
)}
{/* 정렬 가이드라인 렌더링 */}
{alignmentGuides.vertical.map((x, index) => (
<div

View File

@ -1352,61 +1352,6 @@ export function ReportDesignerRightPanel() {
</div>
</CardContent>
</Card>
{/* 배경색 */}
<Card>
<CardHeader>
<CardTitle className="text-sm"></CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<div>
<Label className="text-xs"></Label>
<div className="mt-1 flex gap-2">
<Input
type="color"
value={currentPage.background_color}
onChange={(e) =>
updatePageSettings(currentPageId, {
background_color: e.target.value,
})
}
className="h-10 w-20"
/>
<Input
type="text"
value={currentPage.background_color}
onChange={(e) =>
updatePageSettings(currentPageId, {
background_color: e.target.value,
})
}
className="flex-1"
placeholder="#ffffff"
/>
</div>
</div>
{/* 배경색 프리셋 */}
<div className="grid grid-cols-4 gap-2">
{["#ffffff", "#f3f4f6", "#e5e7eb", "#d1d5db"].map((color) => (
<button
key={color}
onClick={() =>
updatePageSettings(currentPageId, {
background_color: color,
})
}
className="h-8 rounded border-2 transition-all hover:scale-110"
style={{
backgroundColor: color,
borderColor: currentPage.background_color === color ? "#3b82f6" : "#d1d5db",
}}
title={color}
/>
))}
</div>
</CardContent>
</Card>
</>
) : (
<div className="flex h-full items-center justify-center">