feat(pop): 뷰포트 감지 및 비율 스케일링 시스템 추가
- PopFlexRenderer에 뷰포트 너비 감지 기능 추가 (최대 1366px 제한) - 비율 스케일링 시스템 구현: 컴포넌트 크기, gap, padding에 스케일 적용 - 디자인 모드와 뷰어 모드에서의 스케일 차별화 - 문서 업데이트: 비율 스케일링 시스템 및 적용 위치 설명 추가
This commit is contained in:
parent
63c00174e1
commit
760e545444
|
|
@ -68,12 +68,31 @@ POP 메인 대시보드. 메뉴 그리드, KPI, 공지사항 등을 표시.
|
||||||
- v3/v4 레이아웃 자동 감지 및 렌더링
|
- v3/v4 레이아웃 자동 감지 및 렌더링
|
||||||
- 반응형 모드 감지 (태블릿/모바일, 가로/세로)
|
- 반응형 모드 감지 (태블릿/모바일, 가로/세로)
|
||||||
- 프리뷰 모드 지원 (`?preview=true`)
|
- 프리뷰 모드 지원 (`?preview=true`)
|
||||||
|
- **뷰포트 감지 및 비율 스케일링**
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
// 뷰포트 너비 감지 (최대 1366px 제한)
|
||||||
|
const [viewportWidth, setViewportWidth] = useState(1024);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateViewportWidth = () => {
|
||||||
|
setViewportWidth(Math.min(window.innerWidth, 1366));
|
||||||
|
};
|
||||||
|
updateViewportWidth();
|
||||||
|
window.addEventListener("resize", updateViewportWidth);
|
||||||
|
return () => window.removeEventListener("resize", updateViewportWidth);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 레이아웃 버전 감지 및 렌더링
|
// 레이아웃 버전 감지 및 렌더링
|
||||||
if (popLayoutV4) {
|
if (popLayoutV4) {
|
||||||
// v4: PopFlexRenderer 사용
|
// v4: PopFlexRenderer 사용 (비율 스케일링 적용)
|
||||||
<PopFlexRenderer layout={popLayoutV4} viewportWidth={...} />
|
<div className="mx-auto h-full" style={{ maxWidth: 1366 }}>
|
||||||
|
<PopFlexRenderer
|
||||||
|
layout={popLayoutV4}
|
||||||
|
viewportWidth={viewportWidth}
|
||||||
|
isDesignMode={false} // 뷰어에서 스케일 적용
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
} else if (popLayoutV3) {
|
} else if (popLayoutV3) {
|
||||||
// v3: PopLayoutRenderer 사용
|
// v3: PopLayoutRenderer 사용
|
||||||
<PopLayoutV3Renderer layout={popLayoutV3} modeKey={currentModeKey} />
|
<PopLayoutV3Renderer layout={popLayoutV3} modeKey={currentModeKey} />
|
||||||
|
|
@ -228,37 +247,77 @@ type SizeMode = "fixed" | "fill" | "hug";
|
||||||
|
|
||||||
#### `PopFlexRenderer.tsx` (v4용)
|
#### `PopFlexRenderer.tsx` (v4용)
|
||||||
|
|
||||||
**역할**: v4 레이아웃을 Flexbox로 렌더링
|
**역할**: v4 레이아웃을 Flexbox로 렌더링 + 비율 스케일링
|
||||||
|
|
||||||
**핵심 기능**:
|
**핵심 기능**:
|
||||||
- 컨테이너 재귀 렌더링
|
- 컨테이너 재귀 렌더링
|
||||||
- 반응형 규칙 적용 (breakpoint)
|
- 반응형 규칙 적용 (breakpoint)
|
||||||
- 크기 제약 → CSS 스타일 변환
|
- 크기 제약 → CSS 스타일 변환
|
||||||
- 컴포넌트 숨김 처리 (hideBelow)
|
- 컴포넌트 숨김 처리 (hideBelow)
|
||||||
|
- **비율 스케일링** (뷰어 모드)
|
||||||
|
|
||||||
**크기 제약 변환 로직**:
|
**비율 스케일링 시스템**:
|
||||||
```typescript
|
```typescript
|
||||||
function calculateSizeStyle(size: PopSizeConstraintV4): React.CSSProperties {
|
// 기준 너비 (10인치 태블릿 가로)
|
||||||
|
const BASE_VIEWPORT_WIDTH = 1024;
|
||||||
|
|
||||||
|
// 스케일 계산 (디자인 모드: 1, 뷰어 모드: 실제 비율)
|
||||||
|
const scale = isDesignMode ? 1 : viewportWidth / BASE_VIEWPORT_WIDTH;
|
||||||
|
|
||||||
|
// 예시: 12인치(1366px) 화면
|
||||||
|
// scale = 1366 / 1024 = 1.33
|
||||||
|
// 200px 컴포넌트 → 266px
|
||||||
|
```
|
||||||
|
|
||||||
|
**크기 제약 변환 로직** (스케일 적용):
|
||||||
|
```typescript
|
||||||
|
function calculateSizeStyle(
|
||||||
|
size: PopSizeConstraintV4,
|
||||||
|
settings: PopGlobalSettingsV4,
|
||||||
|
scale: number = 1 // 스케일 파라미터 추가
|
||||||
|
): React.CSSProperties {
|
||||||
const style: React.CSSProperties = {};
|
const style: React.CSSProperties = {};
|
||||||
|
|
||||||
// 너비
|
// 너비 (스케일 적용)
|
||||||
switch (size.width) {
|
switch (size.width) {
|
||||||
case "fixed": style.width = `${size.fixedWidth}px`; style.flexShrink = 0; break;
|
case "fixed":
|
||||||
case "fill": style.flex = 1; style.minWidth = size.minWidth || 0; break;
|
style.width = `${size.fixedWidth * scale}px`;
|
||||||
case "hug": style.width = "auto"; style.flexShrink = 0; break;
|
style.flexShrink = 0;
|
||||||
|
break;
|
||||||
|
case "fill":
|
||||||
|
style.flex = 1;
|
||||||
|
style.minWidth = size.minWidth ? `${size.minWidth * scale}px` : 0;
|
||||||
|
break;
|
||||||
|
case "hug":
|
||||||
|
style.width = "auto";
|
||||||
|
style.flexShrink = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 높이
|
// 높이 (스케일 적용)
|
||||||
switch (size.height) {
|
switch (size.height) {
|
||||||
case "fixed": style.height = `${size.fixedHeight}px`; break;
|
case "fixed":
|
||||||
case "fill": style.flexGrow = 1; break;
|
style.height = `${size.fixedHeight * scale}px`;
|
||||||
case "hug": style.height = "auto"; break;
|
break;
|
||||||
|
case "fill":
|
||||||
|
style.flexGrow = 1;
|
||||||
|
break;
|
||||||
|
case "hug":
|
||||||
|
style.height = "auto";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**컨테이너 스케일 적용**:
|
||||||
|
```typescript
|
||||||
|
// gap, padding도 스케일 적용
|
||||||
|
const scaledGap = gap * scale;
|
||||||
|
const scaledPadding = padding ? padding * scale : undefined;
|
||||||
|
```
|
||||||
|
|
||||||
#### `ComponentRenderer.tsx`
|
#### `ComponentRenderer.tsx`
|
||||||
|
|
||||||
**역할**: 개별 컴포넌트 렌더링 (디자인 모드용 플레이스홀더)
|
**역할**: 개별 컴포넌트 렌더링 (디자인 모드용 플레이스홀더)
|
||||||
|
|
|
||||||
|
|
@ -250,10 +250,49 @@ interface PopComponentDefinitionV4 {
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `PopDesigner.tsx` | v3/v4 통합 디자이너 | 완료 |
|
| `PopDesigner.tsx` | v3/v4 통합 디자이너 | 완료 |
|
||||||
| `PopCanvasV4.tsx` | v4 캔버스 (4개 프리셋 + 슬라이더) | 완료 |
|
| `PopCanvasV4.tsx` | v4 캔버스 (4개 프리셋 + 슬라이더) | 완료 |
|
||||||
| `PopFlexRenderer.tsx` | v4 Flexbox 렌더러 | 완료 |
|
| `PopFlexRenderer.tsx` | v4 Flexbox 렌더러 + 비율 스케일링 | 완료 |
|
||||||
| `ComponentPaletteV4.tsx` | v4 컴포넌트 팔레트 | 완료 |
|
| `ComponentPaletteV4.tsx` | v4 컴포넌트 팔레트 | 완료 |
|
||||||
| `ComponentEditorPanelV4.tsx` | v4 속성 편집 패널 | 완료 |
|
| `ComponentEditorPanelV4.tsx` | v4 속성 편집 패널 | 완료 |
|
||||||
| `pop-layout.ts` | v3/v4 타입 정의 | 완료, Phase 2-3에서 수정 예정 |
|
| `pop-layout.ts` | v3/v4 타입 정의 | 완료, Phase 2-3에서 수정 예정 |
|
||||||
|
| `page.tsx` (뷰어) | v4 뷰어 + viewportWidth 감지 | 완료 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 비율 스케일링 시스템
|
||||||
|
|
||||||
|
### 업계 표준
|
||||||
|
Rockwell Automation HMI의 "Scale with Fixed Aspect Ratio" 방식 적용
|
||||||
|
|
||||||
|
### 원리
|
||||||
|
10인치(1024px) 기준으로 디자인 → 8~12인치에서 배치 유지, 크기만 비례 조정
|
||||||
|
|
||||||
|
### 계산
|
||||||
|
```
|
||||||
|
scale = viewportWidth / 1024
|
||||||
|
scaledWidth = originalWidth * scale
|
||||||
|
scaledHeight = originalHeight * scale
|
||||||
|
scaledGap = originalGap * scale
|
||||||
|
scaledPadding = originalPadding * scale
|
||||||
|
```
|
||||||
|
|
||||||
|
### 화면별 결과
|
||||||
|
|
||||||
|
| 화면 | scale | 200px 컴포넌트 | 8px gap |
|
||||||
|
|------|-------|----------------|---------|
|
||||||
|
| 8인치 (800px) | 0.78 | 156px | 6px |
|
||||||
|
| 10인치 (1024px) | 1.00 | 200px | 8px |
|
||||||
|
| 12인치 (1366px) | 1.33 | 266px | 11px |
|
||||||
|
| 14인치+ | 1.33 (max) | 266px + 여백 | 11px |
|
||||||
|
|
||||||
|
### 적용 위치
|
||||||
|
|
||||||
|
| 파일 | 함수/변수 | 역할 |
|
||||||
|
|------|----------|------|
|
||||||
|
| `PopFlexRenderer.tsx` | `BASE_VIEWPORT_WIDTH` | 기준 너비 상수 (1024) |
|
||||||
|
| `PopFlexRenderer.tsx` | `calculateSizeStyle(size, settings, scale)` | 크기 스케일 적용 |
|
||||||
|
| `PopFlexRenderer.tsx` | `ContainerRenderer.containerStyle` | gap, padding 스케일 적용 |
|
||||||
|
| `page.tsx` | `viewportWidth` state | 뷰포트 너비 감지 |
|
||||||
|
| `page.tsx` | `Math.min(window.innerWidth, 1366)` | 최대 너비 제한 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue