feat(saju-ui-v2): _shell/helpers — hexA/daeunLabel/deriveTraits/colorMap + tests
This commit is contained in:
20
src/pages/saju/_shell/helpers/colorMap.js
Normal file
20
src/pages/saju/_shell/helpers/colorMap.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const ELEMENT_TO_VAR = {
|
||||||
|
wood: 'var(--el-wood)',
|
||||||
|
fire: 'var(--el-fire)',
|
||||||
|
earth: 'var(--el-earth)',
|
||||||
|
metal: 'var(--el-metal)',
|
||||||
|
water: 'var(--el-water)',
|
||||||
|
};
|
||||||
|
|
||||||
|
const ELEMENT_KO = { wood: '목', fire: '화', earth: '토', metal: '금', water: '수' };
|
||||||
|
const ELEMENT_CH = { wood: '木', fire: '火', earth: '土', metal: '金', water: '水' };
|
||||||
|
|
||||||
|
export function elementColor(id) {
|
||||||
|
return ELEMENT_TO_VAR[id] || 'var(--navy)';
|
||||||
|
}
|
||||||
|
export function elementKo(id) {
|
||||||
|
return ELEMENT_KO[id] || '';
|
||||||
|
}
|
||||||
|
export function elementCh(id) {
|
||||||
|
return ELEMENT_CH[id] || '';
|
||||||
|
}
|
||||||
10
src/pages/saju/_shell/helpers/daeunLabel.js
Normal file
10
src/pages/saju/_shell/helpers/daeunLabel.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export default function daeunLabel(age) {
|
||||||
|
if (age < 10) return '성장기';
|
||||||
|
if (age < 20) return '학습기';
|
||||||
|
if (age < 30) return '도전기';
|
||||||
|
if (age < 40) return '성장기';
|
||||||
|
if (age < 50) return '전성기';
|
||||||
|
if (age < 60) return '안정기';
|
||||||
|
if (age < 70) return '정리기';
|
||||||
|
return '여유기';
|
||||||
|
}
|
||||||
31
src/pages/saju/_shell/helpers/deriveTraits.js
Normal file
31
src/pages/saju/_shell/helpers/deriveTraits.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const TRAIT_DEFS = {
|
||||||
|
fire: { id: 'challenge', ko: '도전정신', icon: 'challenge', color: 'var(--el-fire)' },
|
||||||
|
metal: { id: 'lead', ko: '리더십', icon: 'lead', color: 'var(--el-metal)' },
|
||||||
|
wood: { id: 'adapt', ko: '적응력', icon: 'adapt', color: 'var(--el-wood)' },
|
||||||
|
water: { id: 'wisdom', ko: '지혜', icon: 'wisdom', color: 'var(--el-water)' },
|
||||||
|
earth: { id: 'wealth', ko: '풍부함', icon: 'wealth', color: 'var(--el-earth)' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const WILL_TRAIT = { id: 'will', ko: '의지', icon: 'will', color: 'var(--purple)' };
|
||||||
|
|
||||||
|
export default function deriveTraits(elements, sipsin = []) {
|
||||||
|
const sorted = Object.entries(elements || {})
|
||||||
|
.filter(([, v]) => typeof v === 'number')
|
||||||
|
.sort((a, b) => b[1] - a[1]);
|
||||||
|
|
||||||
|
const traits = [];
|
||||||
|
for (const [el, score] of sorted) {
|
||||||
|
if (score >= 30 && TRAIT_DEFS[el]) {
|
||||||
|
traits.push(TRAIT_DEFS[el]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!traits.find((t) => t.id === 'will')) traits.push(WILL_TRAIT);
|
||||||
|
|
||||||
|
for (const [el] of sorted) {
|
||||||
|
if (traits.length >= 6) break;
|
||||||
|
if (TRAIT_DEFS[el] && !traits.find((t) => t.id === TRAIT_DEFS[el].id)) {
|
||||||
|
traits.push(TRAIT_DEFS[el]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return traits.slice(0, 6);
|
||||||
|
}
|
||||||
48
src/pages/saju/_shell/helpers/helpers.test.js
Normal file
48
src/pages/saju/_shell/helpers/helpers.test.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import hexA from './hexA';
|
||||||
|
import daeunLabel from './daeunLabel';
|
||||||
|
import deriveTraits from './deriveTraits';
|
||||||
|
import { elementColor } from './colorMap';
|
||||||
|
|
||||||
|
describe('hexA', () => {
|
||||||
|
it('converts hex with alpha', () => {
|
||||||
|
expect(hexA('#1F2A44', 0.5)).toBe('rgba(31,42,68,0.5)');
|
||||||
|
});
|
||||||
|
it('handles 3-digit hex', () => {
|
||||||
|
expect(hexA('#abc', 1)).toBe('rgba(170,187,204,1)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('daeunLabel', () => {
|
||||||
|
it('maps age ranges', () => {
|
||||||
|
expect(daeunLabel(5)).toBe('성장기');
|
||||||
|
expect(daeunLabel(15)).toBe('학습기');
|
||||||
|
expect(daeunLabel(25)).toBe('도전기');
|
||||||
|
expect(daeunLabel(35)).toBe('성장기');
|
||||||
|
expect(daeunLabel(45)).toBe('전성기');
|
||||||
|
expect(daeunLabel(55)).toBe('안정기');
|
||||||
|
expect(daeunLabel(65)).toBe('정리기');
|
||||||
|
expect(daeunLabel(75)).toBe('여유기');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deriveTraits', () => {
|
||||||
|
it('derives strong-element traits (sorted by score)', () => {
|
||||||
|
const traits = deriveTraits({ fire: 55, metal: 40, wood: 35, earth: 15, water: 20 }, []);
|
||||||
|
expect(traits.length).toBeLessThanOrEqual(6);
|
||||||
|
expect(traits[0].id).toBe('challenge');
|
||||||
|
expect(traits.map((t) => t.id)).toContain('lead');
|
||||||
|
});
|
||||||
|
it('always includes will trait', () => {
|
||||||
|
const traits = deriveTraits({ fire: 50, metal: 30, wood: 30, earth: 30, water: 30 }, []);
|
||||||
|
expect(traits.map((t) => t.id)).toContain('will');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('elementColor', () => {
|
||||||
|
it('maps element ids to CSS vars', () => {
|
||||||
|
expect(elementColor('wood')).toBe('var(--el-wood)');
|
||||||
|
expect(elementColor('fire')).toBe('var(--el-fire)');
|
||||||
|
expect(elementColor('unknown')).toBe('var(--navy)');
|
||||||
|
});
|
||||||
|
});
|
||||||
6
src/pages/saju/_shell/helpers/hexA.js
Normal file
6
src/pages/saju/_shell/helpers/hexA.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export default function hexA(hex, alpha) {
|
||||||
|
const h = hex.replace('#', '');
|
||||||
|
const expanded = h.length === 3 ? h.split('').map((c) => c + c).join('') : h;
|
||||||
|
const n = parseInt(expanded, 16);
|
||||||
|
return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${alpha})`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user