feat(pop): 뷰포트 감지 및 비율 스케일링 시스템 추가
- PopFlexRenderer에 뷰포트 너비 감지 기능 추가 (최대 1366px 제한) - 비율 스케일링 시스템 구현: 컴포넌트 크기, gap, padding에 스케일 적용 - 디자인 모드와 뷰어 모드에서의 스케일 차별화 - 문서 업데이트: 비율 스케일링 시스템 및 적용 위치 설명 추가
This commit is contained in:
parent
63c00174e1
commit
760e545444
|
|
@ -68,12 +68,31 @@ POP 메인 대시보드. 메뉴 그리드, KPI, 공지사항 등을 표시.
|
|||
- v3/v4 레이아웃 자동 감지 및 렌더링
|
||||
- 반응형 모드 감지 (태블릿/모바일, 가로/세로)
|
||||
- 프리뷰 모드 지원 (`?preview=true`)
|
||||
- **뷰포트 감지 및 비율 스케일링**
|
||||
|
||||
```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) {
|
||||
// v4: PopFlexRenderer 사용
|
||||
<PopFlexRenderer layout={popLayoutV4} viewportWidth={...} />
|
||||
// v4: PopFlexRenderer 사용 (비율 스케일링 적용)
|
||||
<div className="mx-auto h-full" style={{ maxWidth: 1366 }}>
|
||||
<PopFlexRenderer
|
||||
layout={popLayoutV4}
|
||||
viewportWidth={viewportWidth}
|
||||
isDesignMode={false} // 뷰어에서 스케일 적용
|
||||
/>
|
||||
</div>
|
||||
} else if (popLayoutV3) {
|
||||
// v3: PopLayoutRenderer 사용
|
||||
<PopLayoutV3Renderer layout={popLayoutV3} modeKey={currentModeKey} />
|
||||
|
|
@ -228,37 +247,77 @@ type SizeMode = "fixed" | "fill" | "hug";
|
|||
|
||||
#### `PopFlexRenderer.tsx` (v4용)
|
||||
|
||||
**역할**: v4 레이아웃을 Flexbox로 렌더링
|
||||
**역할**: v4 레이아웃을 Flexbox로 렌더링 + 비율 스케일링
|
||||
|
||||
**핵심 기능**:
|
||||
- 컨테이너 재귀 렌더링
|
||||
- 반응형 규칙 적용 (breakpoint)
|
||||
- 크기 제약 → CSS 스타일 변환
|
||||
- 컴포넌트 숨김 처리 (hideBelow)
|
||||
- **비율 스케일링** (뷰어 모드)
|
||||
|
||||
**크기 제약 변환 로직**:
|
||||
**비율 스케일링 시스템**:
|
||||
```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 = {};
|
||||
|
||||
// 너비
|
||||
// 너비 (스케일 적용)
|
||||
switch (size.width) {
|
||||
case "fixed": style.width = `${size.fixedWidth}px`; style.flexShrink = 0; break;
|
||||
case "fill": style.flex = 1; style.minWidth = size.minWidth || 0; break;
|
||||
case "hug": style.width = "auto"; style.flexShrink = 0; break;
|
||||
case "fixed":
|
||||
style.width = `${size.fixedWidth * scale}px`;
|
||||
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) {
|
||||
case "fixed": style.height = `${size.fixedHeight}px`; break;
|
||||
case "fill": style.flexGrow = 1; break;
|
||||
case "hug": style.height = "auto"; break;
|
||||
case "fixed":
|
||||
style.height = `${size.fixedHeight * scale}px`;
|
||||
break;
|
||||
case "fill":
|
||||
style.flexGrow = 1;
|
||||
break;
|
||||
case "hug":
|
||||
style.height = "auto";
|
||||
break;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
```
|
||||
|
||||
**컨테이너 스케일 적용**:
|
||||
```typescript
|
||||
// gap, padding도 스케일 적용
|
||||
const scaledGap = gap * scale;
|
||||
const scaledPadding = padding ? padding * scale : undefined;
|
||||
```
|
||||
|
||||
#### `ComponentRenderer.tsx`
|
||||
|
||||
**역할**: 개별 컴포넌트 렌더링 (디자인 모드용 플레이스홀더)
|
||||
|
|
|
|||
|
|
@ -250,10 +250,49 @@ interface PopComponentDefinitionV4 {
|
|||
|------|------|------|
|
||||
| `PopDesigner.tsx` | v3/v4 통합 디자이너 | 완료 |
|
||||
| `PopCanvasV4.tsx` | v4 캔버스 (4개 프리셋 + 슬라이더) | 완료 |
|
||||
| `PopFlexRenderer.tsx` | v4 Flexbox 렌더러 | 완료 |
|
||||
| `PopFlexRenderer.tsx` | v4 Flexbox 렌더러 + 비율 스케일링 | 완료 |
|
||||
| `ComponentPaletteV4.tsx` | v4 컴포넌트 팔레트 | 완료 |
|
||||
| `ComponentEditorPanelV4.tsx` | v4 속성 편집 패널 | 완료 |
|
||||
| `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