네비게이션 바 디자인 전환 기능 추가
This commit is contained in:
@@ -0,0 +1,165 @@
|
|||||||
|
# 네비게이션 바 디자인 Survey 기능 가이드
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
직원 대상 네비게이션 바 디자인 선호도 조사를 위한 UI 기능입니다.
|
||||||
|
사용자가 A, B, C 등의 버튼을 클릭하면 실시간으로 네비게이션 바 디자인이 변경됩니다.
|
||||||
|
|
||||||
|
## 기능 활성화/비활성화
|
||||||
|
|
||||||
|
### PortalProperty 설정
|
||||||
|
|
||||||
|
DB의 `ptl_property` 테이블에서 `ui.design-survey` 값을 설정합니다.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 활성화
|
||||||
|
UPDATE ptl_property
|
||||||
|
SET property_value = 'true'
|
||||||
|
WHERE property_group_name = 'Portal' AND property_name = 'ui.design-survey';
|
||||||
|
|
||||||
|
-- 비활성화
|
||||||
|
UPDATE ptl_property
|
||||||
|
SET property_value = 'false'
|
||||||
|
WHERE property_group_name = 'Portal' AND property_name = 'ui.design-survey';
|
||||||
|
```
|
||||||
|
|
||||||
|
> 최초 페이지 로드 시 속성이 자동 생성됩니다 (기본값: `false`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 디자인 옵션 추가/수정 방법
|
||||||
|
|
||||||
|
### 파일 위치
|
||||||
|
`src/main/resources/templates/views/fragment/kjbank/header_container.html`
|
||||||
|
|
||||||
|
### DESIGN_OPTIONS 배열
|
||||||
|
|
||||||
|
디자인 옵션은 JavaScript의 `DESIGN_OPTIONS` 배열에서 관리됩니다:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const DESIGN_OPTIONS = [
|
||||||
|
{
|
||||||
|
id: 'A', // 고유 식별자 (필수)
|
||||||
|
label: 'A (현재)', // 버튼에 표시될 텍스트 (필수)
|
||||||
|
isDefault: true, // 기본 선택 옵션 (선택, 하나만 true)
|
||||||
|
styles: {} // 적용할 CSS 스타일 (필수)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'B',
|
||||||
|
label: 'B',
|
||||||
|
styles: {
|
||||||
|
'.logo-text': { 'font-size': '18px' },
|
||||||
|
'.nav-link': { 'margin': '0 20px' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'C',
|
||||||
|
label: 'C',
|
||||||
|
styles: {
|
||||||
|
'.nav-link': { 'margin': '0 20px', 'font-weight': 'bold' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
### 새 디자인 옵션 추가 예시
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// D 옵션 추가
|
||||||
|
{
|
||||||
|
id: 'D',
|
||||||
|
label: 'D',
|
||||||
|
styles: {
|
||||||
|
'.logo-text': {
|
||||||
|
'font-size': '16px',
|
||||||
|
'color': '#333333'
|
||||||
|
},
|
||||||
|
'.nav-link': {
|
||||||
|
'padding': '10px 24px',
|
||||||
|
'font-weight': '600'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### styles 객체 구조
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
styles: {
|
||||||
|
'CSS 셀렉터': {
|
||||||
|
'CSS 속성': '값',
|
||||||
|
'CSS 속성2': '값2'
|
||||||
|
},
|
||||||
|
'다른 셀렉터': {
|
||||||
|
'CSS 속성': '값'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 자주 사용되는 CSS 셀렉터
|
||||||
|
|
||||||
|
| 셀렉터 | 설명 |
|
||||||
|
|--------|------|
|
||||||
|
| `.logo-text` | 로고 텍스트 "API Portal" |
|
||||||
|
| `.nav-link` | 네비게이션 메뉴 링크 |
|
||||||
|
| `.nav-menu` | 네비게이션 메뉴 컨테이너 |
|
||||||
|
| `.header-content` | 헤더 콘텐츠 영역 |
|
||||||
|
| `.global-header` | 전체 헤더 |
|
||||||
|
| `.logo-wrapper` | 로고 래퍼 (이미지 + 텍스트) |
|
||||||
|
|
||||||
|
## 자주 사용되는 CSS 속성
|
||||||
|
|
||||||
|
| 속성 | 예시 값 | 설명 |
|
||||||
|
|------|---------|------|
|
||||||
|
| `font-size` | `18px`, `1.2rem` | 글자 크기 |
|
||||||
|
| `font-weight` | `400`, `500`, `600`, `bold` | 글자 두께 |
|
||||||
|
| `margin` | `0 20px`, `10px 15px` | 바깥 여백 |
|
||||||
|
| `padding` | `8px 16px` | 안쪽 여백 |
|
||||||
|
| `color` | `#333333`, `#0049b4` | 글자 색상 |
|
||||||
|
| `background` | `#ffffff`, `#f5f5f5` | 배경 색상 |
|
||||||
|
| `gap` | `12px`, `20px` | flex 아이템 간격 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 사용자 선택 저장
|
||||||
|
|
||||||
|
사용자의 디자인 선택은 브라우저의 `localStorage`에 저장됩니다.
|
||||||
|
|
||||||
|
- Key: `design-survey-selection`
|
||||||
|
- Value: 선택한 디자인 ID (예: `A`, `B`, `C`)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 저장된 선택 확인 (개발자 도구 콘솔)
|
||||||
|
localStorage.getItem('design-survey-selection')
|
||||||
|
|
||||||
|
// 선택 초기화
|
||||||
|
localStorage.removeItem('design-survey-selection')
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 관련 파일
|
||||||
|
|
||||||
|
| 파일 | 역할 |
|
||||||
|
|------|------|
|
||||||
|
| `GlobalControllerAdvice.java` | `designSurveyEnabled` ModelAttribute 제공 |
|
||||||
|
| `header_container.html` | Survey 바 HTML 및 DESIGN_OPTIONS 설정 |
|
||||||
|
| `_header.scss` | Survey 바 스타일 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 주의사항
|
||||||
|
|
||||||
|
1. **디자인 옵션 ID는 고유해야 합니다** - 중복 ID 사용 시 오동작
|
||||||
|
2. **isDefault는 하나의 옵션에만 설정** - 여러 개 설정 시 첫 번째만 적용
|
||||||
|
3. **CSS 속성명은 kebab-case 사용** - `fontSize` (X) → `font-size` (O)
|
||||||
|
4. **스타일 적용 시 `!important` 자동 추가** - 기존 스타일 덮어씀
|
||||||
|
5. **Survey 기능 종료 후** - `ui.design-survey`를 `false`로 설정하여 비활성화
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 문의
|
||||||
|
|
||||||
|
기능 관련 문의는 개발팀에 연락해주세요.
|
||||||
@@ -697,18 +697,6 @@ body.design-survey-active .global-header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body.design-variant-B .logo-text {
|
|
||||||
font-size: 18px !important;
|
|
||||||
}
|
|
||||||
body.design-variant-B .nav-link {
|
|
||||||
margin: 0 20px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.design-variant-C .nav-link {
|
|
||||||
margin: 0 20px !important;
|
|
||||||
font-weight: bold !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blind {
|
.blind {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -100,16 +100,8 @@ body.design-survey-active {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Design Variant B
|
// 디자인 변형 스타일은 JavaScript에서 동적으로 적용됩니다.
|
||||||
body.design-variant-B {
|
// header_container.html의 DESIGN_OPTIONS 참조
|
||||||
.logo-text { font-size: 18px !important; }
|
|
||||||
.nav-link { margin: 0 20px !important; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Design Variant C
|
|
||||||
body.design-variant-C {
|
|
||||||
.nav-link { margin: 0 20px !important; font-weight: bold !important; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blind text for screen readers
|
// Blind text for screen readers
|
||||||
.blind {
|
.blind {
|
||||||
|
|||||||
@@ -7,10 +7,8 @@
|
|||||||
<div th:if="${designSurveyEnabled}" class="design-survey-bar" id="designSurveyBar">
|
<div th:if="${designSurveyEnabled}" class="design-survey-bar" id="designSurveyBar">
|
||||||
<div class="survey-container">
|
<div class="survey-container">
|
||||||
<span class="survey-label">네비게이션 디자인을 선택해주세요:</span>
|
<span class="survey-label">네비게이션 디자인을 선택해주세요:</span>
|
||||||
<div class="survey-buttons">
|
<div class="survey-buttons" id="surveyButtons">
|
||||||
<button type="button" class="survey-btn active" data-design="A">A (현재)</button>
|
<!-- 버튼은 JavaScript에서 동적으로 생성됩니다 -->
|
||||||
<button type="button" class="survey-btn" data-design="B">B</button>
|
|
||||||
<button type="button" class="survey-btn" data-design="C">C</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -341,16 +339,68 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Design Survey
|
// ============================================================
|
||||||
|
// Design Survey Configuration
|
||||||
|
// 새 디자인 옵션 추가: DESIGN_OPTIONS 배열에 항목 추가
|
||||||
|
// 예: { id: 'D', label: 'D', styles: { '.logo-text': { 'font-size': '16px' } } }
|
||||||
|
// ============================================================
|
||||||
|
const DESIGN_OPTIONS = [
|
||||||
|
{
|
||||||
|
id: 'A',
|
||||||
|
label: 'A (현재)',
|
||||||
|
isDefault: true,
|
||||||
|
styles: {} // 기본 스타일 (변경 없음)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'B',
|
||||||
|
label: 'B',
|
||||||
|
styles: {
|
||||||
|
'.logo-text': { 'font-size': '18px' },
|
||||||
|
'.nav-link': { 'margin': '0 20px' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'C',
|
||||||
|
label: 'C',
|
||||||
|
styles: {
|
||||||
|
'.nav-link': { 'margin': '0 20px', 'font-weight': 'bold' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 새 디자인 추가 예시:
|
||||||
|
// {
|
||||||
|
// id: 'D',
|
||||||
|
// label: 'D',
|
||||||
|
// styles: {
|
||||||
|
// '.logo-text': { 'font-size': '16px', 'color': '#333' },
|
||||||
|
// '.nav-link': { 'padding': '10px 24px' }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Design Survey Logic
|
||||||
const surveyBar = document.getElementById('designSurveyBar');
|
const surveyBar = document.getElementById('designSurveyBar');
|
||||||
if (surveyBar) {
|
if (surveyBar) {
|
||||||
const body = document.body;
|
const body = document.body;
|
||||||
const surveyButtons = surveyBar.querySelectorAll('.survey-btn');
|
const buttonContainer = document.getElementById('surveyButtons');
|
||||||
const STORAGE_KEY = 'design-survey-selection';
|
const STORAGE_KEY = 'design-survey-selection';
|
||||||
|
let styleElement = null;
|
||||||
|
|
||||||
body.classList.add('design-survey-active');
|
body.classList.add('design-survey-active');
|
||||||
|
|
||||||
const savedDesign = localStorage.getItem(STORAGE_KEY) || 'A';
|
// 버튼 동적 생성
|
||||||
|
DESIGN_OPTIONS.forEach(option => {
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.type = 'button';
|
||||||
|
btn.className = 'survey-btn';
|
||||||
|
btn.dataset.design = option.id;
|
||||||
|
btn.textContent = option.label;
|
||||||
|
buttonContainer.appendChild(btn);
|
||||||
|
});
|
||||||
|
|
||||||
|
const surveyButtons = buttonContainer.querySelectorAll('.survey-btn');
|
||||||
|
const defaultOption = DESIGN_OPTIONS.find(o => o.isDefault) || DESIGN_OPTIONS[0];
|
||||||
|
const savedDesign = localStorage.getItem(STORAGE_KEY) || defaultOption.id;
|
||||||
|
|
||||||
applyDesign(savedDesign);
|
applyDesign(savedDesign);
|
||||||
surveyButtons.forEach(btn => {
|
surveyButtons.forEach(btn => {
|
||||||
btn.classList.toggle('active', btn.dataset.design === savedDesign);
|
btn.classList.toggle('active', btn.dataset.design === savedDesign);
|
||||||
@@ -365,9 +415,29 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function applyDesign(design) {
|
function applyDesign(designId) {
|
||||||
body.classList.remove('design-variant-B', 'design-variant-C');
|
// 기존 동적 스타일 제거
|
||||||
if (design !== 'A') body.classList.add('design-variant-' + design);
|
if (styleElement) {
|
||||||
|
styleElement.remove();
|
||||||
|
styleElement = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const option = DESIGN_OPTIONS.find(o => o.id === designId);
|
||||||
|
if (!option || Object.keys(option.styles).length === 0) return;
|
||||||
|
|
||||||
|
// 동적 스타일 생성
|
||||||
|
let css = '';
|
||||||
|
for (const [selector, props] of Object.entries(option.styles)) {
|
||||||
|
const propsStr = Object.entries(props)
|
||||||
|
.map(([prop, val]) => `${prop}: ${val} !important`)
|
||||||
|
.join('; ');
|
||||||
|
css += `${selector} { ${propsStr}; }\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
styleElement = document.createElement('style');
|
||||||
|
styleElement.id = 'design-survey-styles';
|
||||||
|
styleElement.textContent = css;
|
||||||
|
document.head.appendChild(styleElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user