refactor: 격자 시스템을 10px 단위로 단순화
- 복잡한 컬럼 시스템 제거 - 웹타입별 고정 픽셀 너비 사용 (10px 단위) - 격자 설정 패널 단순화 (컬럼 수 설정 제거) - 간격/여백 조정을 10px 단위로 변경 - 더 직관적이고 예측 가능한 레이아웃 시스템
This commit is contained in:
parent
7ab3781372
commit
c4290f2d0e
|
|
@ -2338,45 +2338,43 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
||||||
snapToGrid: layout.gridSettings?.snapToGrid,
|
snapToGrid: layout.gridSettings?.snapToGrid,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 웹타입별 기본 그리드 컬럼 수 계산
|
// 웹타입별 기본 너비 계산 (10px 단위 고정)
|
||||||
const getDefaultGridColumns = (widgetType: string): number => {
|
const getDefaultWidth = (widgetType: string): number => {
|
||||||
const widthMap: Record<string, number> = {
|
const widthMap: Record<string, number> = {
|
||||||
// 텍스트 입력 계열 (넓게)
|
// 텍스트 입력 계열
|
||||||
text: 4, // 1/3 (33%)
|
text: 200,
|
||||||
email: 4, // 1/3 (33%)
|
email: 200,
|
||||||
tel: 3, // 1/4 (25%)
|
tel: 150,
|
||||||
url: 4, // 1/3 (33%)
|
url: 250,
|
||||||
textarea: 6, // 절반 (50%)
|
textarea: 300,
|
||||||
|
|
||||||
// 숫자/날짜 입력 (중간)
|
// 숫자/날짜 입력
|
||||||
number: 2, // 2/12 (16.67%)
|
number: 120,
|
||||||
decimal: 2, // 2/12 (16.67%)
|
decimal: 120,
|
||||||
date: 3, // 1/4 (25%)
|
date: 150,
|
||||||
datetime: 3, // 1/4 (25%)
|
datetime: 180,
|
||||||
time: 2, // 2/12 (16.67%)
|
time: 120,
|
||||||
|
|
||||||
// 선택 입력 (중간)
|
// 선택 입력
|
||||||
select: 3, // 1/4 (25%)
|
select: 180,
|
||||||
radio: 3, // 1/4 (25%)
|
radio: 180,
|
||||||
checkbox: 2, // 2/12 (16.67%)
|
checkbox: 120,
|
||||||
boolean: 2, // 2/12 (16.67%)
|
boolean: 120,
|
||||||
|
|
||||||
// 코드/참조 (넓게)
|
// 코드/참조
|
||||||
code: 3, // 1/4 (25%)
|
code: 180,
|
||||||
entity: 4, // 1/3 (33%)
|
entity: 200,
|
||||||
|
|
||||||
// 파일/이미지 (넓게)
|
// 파일/이미지
|
||||||
file: 4, // 1/3 (33%)
|
file: 250,
|
||||||
image: 3, // 1/4 (25%)
|
image: 200,
|
||||||
|
|
||||||
// 기타
|
// 기타
|
||||||
button: 2, // 2/12 (16.67%)
|
button: 100,
|
||||||
label: 2, // 2/12 (16.67%)
|
label: 100,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultColumns = widthMap[widgetType] || 3; // 기본값 3 (1/4, 25%)
|
return widthMap[widgetType] || 200; // 기본값 200px
|
||||||
console.log("🎯 [ScreenDesigner] getDefaultGridColumns:", { widgetType, defaultColumns });
|
|
||||||
return defaultColumns;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 웹타입별 기본 높이 계산
|
// 웹타입별 기본 높이 계산
|
||||||
|
|
@ -2544,24 +2542,12 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
||||||
const componentId = getComponentIdFromWebType(column.widgetType);
|
const componentId = getComponentIdFromWebType(column.widgetType);
|
||||||
// console.log(`🔄 폼 컨테이너 드롭: ${column.widgetType} → ${componentId}`);
|
// console.log(`🔄 폼 컨테이너 드롭: ${column.widgetType} → ${componentId}`);
|
||||||
|
|
||||||
// 웹타입별 적절한 gridColumns 계산
|
// 웹타입별 기본 너비 계산 (10px 단위 고정)
|
||||||
const calculatedGridColumns = getDefaultGridColumns(column.widgetType);
|
const componentWidth = getDefaultWidth(column.widgetType);
|
||||||
|
|
||||||
// gridColumns에 맞는 실제 너비 계산
|
|
||||||
const componentWidth =
|
|
||||||
currentGridInfo && layout.gridSettings?.snapToGrid
|
|
||||||
? calculateWidthFromColumns(
|
|
||||||
calculatedGridColumns,
|
|
||||||
currentGridInfo,
|
|
||||||
layout.gridSettings as GridUtilSettings,
|
|
||||||
)
|
|
||||||
: defaultWidth;
|
|
||||||
|
|
||||||
console.log("🎯 폼 컨테이너 컴포넌트 생성:", {
|
console.log("🎯 폼 컨테이너 컴포넌트 생성:", {
|
||||||
widgetType: column.widgetType,
|
widgetType: column.widgetType,
|
||||||
calculatedGridColumns,
|
|
||||||
componentWidth,
|
componentWidth,
|
||||||
defaultWidth,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
newComponent = {
|
newComponent = {
|
||||||
|
|
@ -2576,7 +2562,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
||||||
componentType: componentId, // DynamicComponentRenderer용 컴포넌트 타입
|
componentType: componentId, // DynamicComponentRenderer용 컴포넌트 타입
|
||||||
position: { x: relativeX, y: relativeY, z: 1 } as Position,
|
position: { x: relativeX, y: relativeY, z: 1 } as Position,
|
||||||
size: { width: componentWidth, height: getDefaultHeight(column.widgetType) },
|
size: { width: componentWidth, height: getDefaultHeight(column.widgetType) },
|
||||||
gridColumns: calculatedGridColumns,
|
|
||||||
// 코드 타입인 경우 코드 카테고리 정보 추가
|
// 코드 타입인 경우 코드 카테고리 정보 추가
|
||||||
...(column.widgetType === "code" &&
|
...(column.widgetType === "code" &&
|
||||||
column.codeCategory && {
|
column.codeCategory && {
|
||||||
|
|
@ -2588,7 +2573,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
||||||
labelColor: "#212121",
|
labelColor: "#212121",
|
||||||
labelFontWeight: "500",
|
labelFontWeight: "500",
|
||||||
labelMarginBottom: "6px",
|
labelMarginBottom: "6px",
|
||||||
width: `${(calculatedGridColumns / (layout.gridSettings?.columns || 12)) * 100}%`, // 퍼센트 너비
|
|
||||||
},
|
},
|
||||||
componentConfig: {
|
componentConfig: {
|
||||||
type: componentId, // text-input, number-input 등
|
type: componentId, // text-input, number-input 등
|
||||||
|
|
@ -2611,36 +2595,14 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
||||||
const componentId = getComponentIdFromWebType(column.widgetType);
|
const componentId = getComponentIdFromWebType(column.widgetType);
|
||||||
// console.log(`🔄 캔버스 드롭: ${column.widgetType} → ${componentId}`);
|
// console.log(`🔄 캔버스 드롭: ${column.widgetType} → ${componentId}`);
|
||||||
|
|
||||||
// 웹타입별 적절한 gridColumns 계산
|
// 웹타입별 기본 너비 계산 (10px 단위 고정)
|
||||||
const calculatedGridColumns = getDefaultGridColumns(column.widgetType);
|
const componentWidth = getDefaultWidth(column.widgetType);
|
||||||
|
|
||||||
// gridColumns에 맞는 실제 너비 계산
|
|
||||||
const componentWidth =
|
|
||||||
currentGridInfo && layout.gridSettings?.snapToGrid
|
|
||||||
? calculateWidthFromColumns(
|
|
||||||
calculatedGridColumns,
|
|
||||||
currentGridInfo,
|
|
||||||
layout.gridSettings as GridUtilSettings,
|
|
||||||
)
|
|
||||||
: defaultWidth;
|
|
||||||
|
|
||||||
console.log("🎯 캔버스 컴포넌트 생성:", {
|
console.log("🎯 캔버스 컴포넌트 생성:", {
|
||||||
widgetType: column.widgetType,
|
widgetType: column.widgetType,
|
||||||
calculatedGridColumns,
|
|
||||||
componentWidth,
|
componentWidth,
|
||||||
defaultWidth,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 🔍 이미지 타입 드래그앤드롭 디버깅
|
|
||||||
// if (column.widgetType === "image") {
|
|
||||||
// console.log("🖼️ 이미지 컬럼 드래그앤드롭:", {
|
|
||||||
// columnName: column.columnName,
|
|
||||||
// widgetType: column.widgetType,
|
|
||||||
// componentId,
|
|
||||||
// column,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
newComponent = {
|
newComponent = {
|
||||||
id: generateComponentId(),
|
id: generateComponentId(),
|
||||||
type: "component", // ✅ 새로운 컴포넌트 시스템 사용
|
type: "component", // ✅ 새로운 컴포넌트 시스템 사용
|
||||||
|
|
@ -2652,7 +2614,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
||||||
componentType: componentId, // DynamicComponentRenderer용 컴포넌트 타입
|
componentType: componentId, // DynamicComponentRenderer용 컴포넌트 타입
|
||||||
position: { x, y, z: 1 } as Position,
|
position: { x, y, z: 1 } as Position,
|
||||||
size: { width: componentWidth, height: getDefaultHeight(column.widgetType) },
|
size: { width: componentWidth, height: getDefaultHeight(column.widgetType) },
|
||||||
gridColumns: calculatedGridColumns,
|
|
||||||
// 코드 타입인 경우 코드 카테고리 정보 추가
|
// 코드 타입인 경우 코드 카테고리 정보 추가
|
||||||
...(column.widgetType === "code" &&
|
...(column.widgetType === "code" &&
|
||||||
column.codeCategory && {
|
column.codeCategory && {
|
||||||
|
|
@ -2664,7 +2625,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
||||||
labelColor: "#000000", // 순수한 검정
|
labelColor: "#000000", // 순수한 검정
|
||||||
labelFontWeight: "500",
|
labelFontWeight: "500",
|
||||||
labelMarginBottom: "8px",
|
labelMarginBottom: "8px",
|
||||||
width: `${(calculatedGridColumns / (layout.gridSettings?.columns || 12)) * 100}%`, // 퍼센트 너비
|
|
||||||
},
|
},
|
||||||
componentConfig: {
|
componentConfig: {
|
||||||
type: componentId, // text-input, number-input 등
|
type: componentId, // text-input, number-input 등
|
||||||
|
|
|
||||||
|
|
@ -128,49 +128,14 @@ export const GridPanel: React.FC<GridPanelProps> = ({
|
||||||
|
|
||||||
{/* 설정 영역 */}
|
{/* 설정 영역 */}
|
||||||
<div className="flex-1 space-y-4 overflow-y-auto p-4">
|
<div className="flex-1 space-y-4 overflow-y-auto p-4">
|
||||||
{/* 격자 구조 */}
|
{/* 격자 구조 - 10px 단위 */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<h4 className="text-xs font-semibold">격자 구조</h4>
|
<h4 className="text-xs font-semibold">격자 크기 (10px 단위)</h4>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="bg-muted/50 rounded-md p-3">
|
||||||
<Label htmlFor="columns" className="text-xs font-medium">
|
<p className="text-xs text-muted-foreground">
|
||||||
컬럼 수
|
모든 컴포넌트는 10px 단위로 배치되고 크기가 조정됩니다.
|
||||||
</Label>
|
</p>
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Input
|
|
||||||
id="columns"
|
|
||||||
type="number"
|
|
||||||
min={1}
|
|
||||||
max={safeMaxColumns}
|
|
||||||
value={gridSettings.columns}
|
|
||||||
onChange={(e) => {
|
|
||||||
const value = parseInt(e.target.value, 10);
|
|
||||||
if (!isNaN(value) && value >= 1 && value <= safeMaxColumns) {
|
|
||||||
updateSetting("columns", value);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="h-8 text-xs"
|
|
||||||
/>
|
|
||||||
<span className="text-muted-foreground text-xs">/ {safeMaxColumns}</span>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
id="columns-slider"
|
|
||||||
min={1}
|
|
||||||
max={safeMaxColumns}
|
|
||||||
step={1}
|
|
||||||
value={[gridSettings.columns]}
|
|
||||||
onValueChange={([value]) => updateSetting("columns", value)}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<div className="text-muted-foreground flex justify-between text-xs">
|
|
||||||
<span>1열</span>
|
|
||||||
<span>{safeMaxColumns}열</span>
|
|
||||||
</div>
|
|
||||||
{isColumnsTooSmall && (
|
|
||||||
<p className="text-xs text-amber-600">
|
|
||||||
⚠️ 컬럼 너비가 너무 작습니다 (최소 {MIN_COLUMN_WIDTH}px 권장)
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|
@ -181,7 +146,7 @@ export const GridPanel: React.FC<GridPanelProps> = ({
|
||||||
id="gap"
|
id="gap"
|
||||||
min={0}
|
min={0}
|
||||||
max={40}
|
max={40}
|
||||||
step={2}
|
step={10}
|
||||||
value={[gridSettings.gap]}
|
value={[gridSettings.gap]}
|
||||||
onValueChange={([value]) => updateSetting("gap", value)}
|
onValueChange={([value]) => updateSetting("gap", value)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
|
@ -200,7 +165,7 @@ export const GridPanel: React.FC<GridPanelProps> = ({
|
||||||
id="padding"
|
id="padding"
|
||||||
min={0}
|
min={0}
|
||||||
max={60}
|
max={60}
|
||||||
step={4}
|
step={10}
|
||||||
value={[gridSettings.padding]}
|
value={[gridSettings.padding]}
|
||||||
onValueChange={([value]) => updateSetting("padding", value)}
|
onValueChange={([value]) => updateSetting("padding", value)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
|
@ -288,14 +253,14 @@ export const GridPanel: React.FC<GridPanelProps> = ({
|
||||||
<div className="border-t border-gray-200 bg-gray-50 p-3">
|
<div className="border-t border-gray-200 bg-gray-50 p-3">
|
||||||
<div className="text-muted-foreground text-xs">💡 격자 설정은 실시간으로 캔버스에 반영됩니다 </div>
|
<div className="text-muted-foreground text-xs">💡 격자 설정은 실시간으로 캔버스에 반영됩니다 </div>
|
||||||
|
|
||||||
{/* 해상도 및 격자 정보 */}
|
{/* 해상도 정보 */}
|
||||||
{screenResolution && actualGridInfo && (
|
{screenResolution && (
|
||||||
<>
|
<>
|
||||||
<Separator />
|
<Separator />
|
||||||
<div className="space-y-3">
|
<div className="space-y-2">
|
||||||
<h4 className="font-medium text-gray-900">격자 정보</h4>
|
<h4 className="text-xs font-semibold">화면 정보</h4>
|
||||||
|
|
||||||
<div className="space-y-2 text-xs" style={{ fontSize: "12px" }}>
|
<div className="space-y-2 text-xs">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-muted-foreground">해상도:</span>
|
<span className="text-muted-foreground">해상도:</span>
|
||||||
<span className="font-mono">
|
<span className="font-mono">
|
||||||
|
|
@ -304,25 +269,9 @@ export const GridPanel: React.FC<GridPanelProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-muted-foreground">컬럼 너비:</span>
|
<span className="text-muted-foreground">격자 단위:</span>
|
||||||
<span className={`font-mono ${isColumnsTooSmall ? "text-destructive" : "text-gray-900"}`}>
|
<span className="font-mono text-primary">10px</span>
|
||||||
{actualGridInfo.columnWidth.toFixed(1)}px
|
|
||||||
{isColumnsTooSmall && " (너무 작음)"}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="text-muted-foreground">사용 가능 너비:</span>
|
|
||||||
<span className="font-mono">
|
|
||||||
{(screenResolution.width - gridSettings.padding * 2).toLocaleString()}px
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isColumnsTooSmall && (
|
|
||||||
<div className="rounded-md bg-yellow-50 p-2 text-xs text-yellow-800">
|
|
||||||
💡 컬럼이 너무 작습니다. 컬럼 수를 줄이거나 간격을 줄여보세요.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue