bibimbap/.atp/work-session/20260622-092800/artifacts/w3-2-ui-preview.html

365 lines
27 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>W3-2 UI 프리뷰 — 육각형 레이더 · 6축 평점 · 카드</title>
<style>
html {
color-scheme: light;
--surface: #faf8f5; --card-bg: #fff; --text: #1a1a1a; --text-muted: #5c5c5c;
--accent: #e8a54b; --accent-soft: rgba(232,165,75,0.14);
--border: rgba(0,0,0,0.08); --panel-shadow: 0 4px 24px rgba(26,26,26,0.06); --radius: 16px;
}
html[data-theme="dark"] {
color-scheme: dark;
--surface: #121212; --card-bg: #1e1e1e; --text: #ece8e1; --text-muted: #a39e96;
--border: rgba(255,255,255,0.1); --accent-soft: rgba(232,165,75,0.12);
--panel-shadow: 0 8px 32px rgba(0,0,0,0.35);
}
* { box-sizing: border-box; }
body {
margin: 0; min-height: 100vh; padding: 2rem 1rem 4rem;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans KR", sans-serif;
color: var(--text);
}
html:not([data-theme="dark"]) body { background: linear-gradient(165deg,#fff9f0 0%,var(--surface) 45%,#f3ede6 100%); background-attachment: fixed; }
html[data-theme="dark"] body { background: linear-gradient(165deg,#1c1a18 0%,var(--surface) 38%,#0e0e0e 100%); background-attachment: fixed; }
.wrap { max-width: 56rem; margin: 0 auto; }
.preview-bar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem; }
.preview-bar h1 { font-size: 1.1rem; margin: 0; letter-spacing: -0.02em; }
.preview-bar button { padding: 0.45rem 1rem; font: inherit; font-weight: 700; font-size: 0.8rem; cursor: pointer;
color: var(--text); background: var(--card-bg); border: 1px solid var(--border); border-radius: 999px; }
.game-panel { padding: 1.4rem 1.5rem 1.6rem; margin-bottom: 1.5rem; background: var(--card-bg);
border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--panel-shadow); }
.game-panel__title { margin: 0 0 0.2rem; font-size: 1.05rem; letter-spacing: -0.02em; }
.game-panel__subtitle { margin: 0 0 1.1rem; font-size: 0.8rem; color: var(--text-muted); }
.section-tag { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
color: var(--accent); margin: 0 0 0.6rem; }
/* ===== 아래는 game-detail.jsp 승격본과 동일한 W3-2 컴포넌트 스타일 ===== */
.game-reviews__summary { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1rem; }
.game-reviews__avg { font-size: 1.35rem; font-weight: 800; letter-spacing: -0.02em; color: var(--accent); }
.game-reviews__count { font-size: 0.75rem; font-weight: 600; color: var(--text-muted); }
.game-stars--avg, .game-stars--display { display: inline-flex; gap: 0.05rem; font-size: 0.95rem; line-height: 1; }
.game-stars--avg .game-star, .game-stars--display .game-star { color: var(--border); }
.game-stars--avg .game-star.is-on, .game-stars--display .game-star.is-on { color: var(--accent); }
.game-reviews__summary-panel { margin: 0 0 1.25rem; display: flex; align-items: center; gap: 1.25rem;
padding: 1rem 1.25rem;
background: radial-gradient(125% 125% at 0% 0%, var(--accent-soft), transparent 62%), var(--card-bg);
border: 1px solid rgba(232,165,75,0.22); border-radius: var(--radius); box-shadow: var(--panel-shadow); }
@media (max-width: 480px) { .game-reviews__summary-panel { flex-direction: column; align-items: stretch; gap: 0.85rem; } .game-reviews__radar { justify-content: center; } }
.game-reviews__radar { display: inline-flex; flex-shrink: 0; }
.game-reviews__radar svg { display: block; }
.game-axis-radar { overflow: visible; }
.game-reviews__radar .game-axis-grid { fill: none; stroke: var(--border); stroke-width: 1; }
.game-reviews__radar .game-axis-grid--outer { stroke: rgba(232,165,75,0.4); stroke-width: 1.25; }
.game-reviews__radar .game-axis-line { stroke: var(--border); stroke-width: 1; stroke-dasharray: 2 3; }
.game-reviews__radar .game-axis-poly { stroke: var(--accent); stroke-width: 1.75; stroke-linejoin: round;
filter: drop-shadow(0 2px 7px rgba(232,165,75,0.38)); transform-box: fill-box; transform-origin: center;
animation: game-radar-pop 0.55s cubic-bezier(0.22,1,0.36,1) both; }
.game-reviews__radar .game-axis-dot { fill: var(--accent); stroke: var(--card-bg); stroke-width: 1.5;
animation: game-radar-fade 0.7s ease both; }
@keyframes game-radar-pop { from { opacity: 0; transform: scale(0.55); } to { opacity: 1; transform: scale(1); } }
@keyframes game-radar-fade { from { opacity: 0; } to { opacity: 1; } }
.game-reviews__radar-empty { font-size: 0.75rem; color: var(--text-muted); }
.game-reviews__axis-legend { margin: 0; flex: 1; min-width: 11.5rem; display: grid; gap: 0.4rem 0; font-size: 0.75rem; color: var(--text-muted); }
.game-reviews__axis-legend-row { display: grid; grid-template-columns: 3.4rem 1fr 1.15rem; align-items: center; gap: 0.55rem; }
.game-reviews__axis-legend dt { font-weight: 600; color: var(--text); }
.game-reviews__axis-legend dd { margin: 0; font-variant-numeric: tabular-nums; font-weight: 700; text-align: right; color: var(--accent); }
.game-reviews__axis-meter { height: 0.4rem; border-radius: 999px; background: var(--border); overflow: hidden; }
.game-reviews__axis-meter span { display: block; height: 100%; border-radius: 999px;
background: linear-gradient(90deg,#f0c978,var(--accent)); animation: game-meter-grow 0.7s cubic-bezier(0.22,1,0.36,1) both; transform-origin: left; }
@keyframes game-meter-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* 6축 입력 */
.game-reviews__composer { margin-bottom: 1.25rem; padding: 1.1rem 1.15rem 1.2rem;
background: radial-gradient(140% 120% at 100% 0%, rgba(232,165,75,0.16), transparent 60%), var(--accent-soft);
border: 1px solid rgba(232,165,75,0.22); border-radius: 14px; }
.game-reviews__axes { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 0.5rem 1.1rem; margin-bottom: 0.85rem; }
@media (max-width: 480px) { .game-reviews__axes { grid-template-columns: 1fr; } }
.game-reviews__axis { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
padding: 0.3rem 0.5rem; border-radius: 10px; transition: background 0.15s ease; }
.game-reviews__axis:hover { background: rgba(232,165,75,0.08); }
.game-reviews__axis-label { font-size: 0.8125rem; font-weight: 600; color: var(--text); }
.game-stars { display: inline-flex; gap: 0.15rem; line-height: 1; }
.game-stars--axis { gap: 0.1rem; }
.game-stars__btn { padding: 0.1rem; font-size: 1.6rem; line-height: 1; color: var(--border);
background: none; border: none; cursor: pointer; transition: color 0.12s ease, transform 0.12s ease, text-shadow 0.2s ease; }
.game-stars--axis .game-stars__btn { font-size: 1.25rem; }
.game-stars__btn:hover { transform: scale(1.12); }
.game-stars__btn.is-on { color: var(--accent); text-shadow: 0 1px 4px rgba(232,165,75,0.45); }
.game-stars__btn:active { transform: scale(0.88); }
.game-stars__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.game-reviews__rating-row { display: flex; align-items: center; gap: 0.85rem; margin-bottom: 0.85rem; }
.game-comments__label { font-size: 0.8125rem; font-weight: 600; color: var(--text-muted); }
.game-comments__form textarea { width: 100%; min-height: 5rem; padding: 0.85rem 1rem; font: inherit; font-size: 0.875rem;
line-height: 1.55; color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: 12px; resize: vertical; }
.game-comments__form textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px rgba(232,165,75,0.18); }
.game-comments__actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem; margin-top: 0.65rem; }
.game-comments__hint { font-size: 0.6875rem; color: var(--text-muted); }
.game-comments__hint.is-warn { color: #b33; font-weight: 700; }
.game-comments__form button[type="submit"] { padding: 0.55rem 1.5rem; font: inherit; font-size: 0.875rem; font-weight: 700; color: #1a1a1a;
background: linear-gradient(180deg,#f0c978 0%,var(--accent) 100%); border: none; border-radius: 10px; cursor: pointer;
box-shadow: 0 2px 8px rgba(232,165,75,0.35); transition: transform 0.15s ease, box-shadow 0.2s ease; }
.game-comments__form button[type="submit"]:hover { box-shadow: 0 4px 14px rgba(232,165,75,0.45); }
button[aria-busy="true"] { opacity: 0.65; cursor: progress; }
/* sort 토글 / 더보기 */
.game-comments__toolbar { display: flex; justify-content: flex-end; margin-bottom: 0.85rem; }
.game-comments__sort { display: inline-flex; gap: 0.25rem; padding: 0.2rem; background: var(--surface); border: 1px solid var(--border); border-radius: 999px; }
.game-comments__sort-btn { padding: 0.3rem 0.7rem; font: inherit; font-size: 0.6875rem; font-weight: 700; color: var(--text-muted);
background: transparent; border: none; border-radius: 999px; cursor: pointer; transition: color 0.15s ease, background 0.15s ease; }
.game-comments__sort-btn.is-active { color: #1a1a1a; background: linear-gradient(180deg,#f0c978,var(--accent)); box-shadow: 0 1px 5px rgba(232,165,75,0.4); }
.game-comments__more { margin-top: 1rem; display: flex; justify-content: center; }
.game-comments__more-btn { padding: 0.5rem 1.4rem; font: inherit; font-size: 0.8125rem; font-weight: 700; color: var(--text);
background: var(--card-bg); border: 1px solid var(--border); border-radius: 999px; cursor: pointer; transition: border-color 0.15s ease, color 0.15s ease, transform 0.15s ease, box-shadow 0.2s ease; }
.game-comments__more-btn:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-1px); box-shadow: 0 5px 16px rgba(232,165,75,0.16); }
/* 리뷰/덧글 카드 */
.game-reviews__list, .game-comments__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.85rem; }
.game-reviews__item { padding: 0.95rem 1.1rem; font-size: 0.875rem; line-height: 1.55; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; }
.game-reviews__item.is-mine { border-color: rgba(232,165,75,0.4); background: var(--accent-soft); }
.game-reviews__item-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.65rem; margin-bottom: 0.5rem; }
.game-reviews__nick { font-size: 0.8125rem; font-weight: 700; letter-spacing: -0.02em; }
.game-reviews__mine-badge { padding: 0.05rem 0.45rem; font-size: 0.625rem; font-weight: 700; color: var(--accent); background: rgba(232,165,75,0.16); border-radius: 999px; }
.game-reviews__time, .game-comments__meta time { margin-left: auto; font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.02em; color: var(--text-muted); }
.game-comments__edited, .game-reviews__edited { margin-left: 0.35rem; font-size: 0.6875rem; font-weight: 500; color: var(--text-muted); }
.game-reviews__body, .game-comments__item p { margin: 0; white-space: pre-wrap; word-break: break-word; }
.game-reviews__card-radar { margin-top: 0.6rem; display: inline-flex; }
.game-reviews__card-radar svg { display: block; }
.game-reviews__radar-empty { font-size: 0.75rem; color: var(--text-muted); }
.game-comments__item { display: flex; gap: 0.75rem; align-items: flex-start; padding: 0.85rem 1rem; font-size: 0.875rem; line-height: 1.55; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; }
.game-comments__avatar { flex-shrink: 0; width: 2.25rem; height: 2.25rem; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 0.75rem; font-weight: 800; color: var(--accent); background: var(--accent-soft); border: 1px solid rgba(232,165,75,0.25); }
.game-comments__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem 0.65rem; margin-bottom: 0.4rem; }
.game-comments__nick { font-size: 0.75rem; font-weight: 700; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
@media (prefers-reduced-motion: reduce) {
.game-reviews__radar .game-axis-poly, .game-reviews__radar .game-axis-dot, .game-reviews__axis-meter span, .game-comments__more-btn:hover { animation: none; transform: none; }
}
</style>
</head>
<body>
<div class="wrap">
<div class="preview-bar">
<h1>W3-2 UI 프리뷰 · 육각형 레이더 · 6축 평점</h1>
<button id="theme-toggle" type="button">🌙 다크</button>
</div>
<!-- 리뷰 패널 -->
<section class="game-panel">
<p class="section-tag">리뷰 요약 (game_review_stats)</p>
<div class="game-reviews__summary">
<span class="game-reviews__avg" id="avg">4.2</span>
<div class="game-stars game-stars--avg" id="avg-stars" aria-hidden="true"></div>
<span class="game-reviews__count">(37)</span>
</div>
<div class="game-reviews__summary-panel" id="summary-panel"></div>
<p class="section-tag">6축 평가 입력 (radiogroup · 키보드 ←→↑↓/Home/End)</p>
<div class="game-reviews__composer">
<div class="game-reviews__axes" id="axes" aria-label="6축 평가"></div>
<div class="game-reviews__rating-row">
<span class="game-comments__label" id="overall-label">종합 별점 (선택)</span>
<div class="game-stars game-stars--input" id="overall" role="radiogroup" aria-labelledby="overall-label"></div>
</div>
<label class="game-comments__label" for="rbody">평가</label>
<textarea id="rbody" rows="3" maxlength="1000" placeholder="이 게임을 어떻게 즐기셨나요? (최소 10자)"></textarea>
<div class="game-comments__actions">
<span class="game-comments__hint" id="rhint"><span id="rcount">0/1000</span> · 6축은 필수, 종합 별점은 선택이에요</span>
<button type="submit">리뷰 등록</button>
</div>
</div>
<p class="section-tag">리뷰 목록 · 정렬 · 카드 컴팩트 레이더</p>
<div class="game-comments__toolbar">
<div class="game-comments__sort" role="group" aria-label="리뷰 정렬">
<button type="button" class="game-comments__sort-btn is-active">최신순</button>
<button type="button" class="game-comments__sort-btn">별점 높은순</button>
<button type="button" class="game-comments__sort-btn">별점 낮은순</button>
</div>
</div>
<ul class="game-reviews__list" id="review-list"></ul>
<div class="game-comments__more"><button type="button" class="game-comments__more-btn">더보기</button></div>
</section>
<!-- 덧글 패널 -->
<section class="game-panel">
<p class="section-tag">덧글 · 상대시각 · 글자수 카운터</p>
<div class="game-reviews__composer" style="background: var(--surface); border-color: var(--border);">
<label class="game-comments__label" for="cbody">내용</label>
<textarea id="cbody" rows="2" maxlength="200" placeholder="여기에 덧글을 작성해 주세요."></textarea>
<div class="game-comments__actions">
<span class="game-comments__hint"><span id="ccount">0/200</span></span>
<button type="submit">덧글 등록</button>
</div>
</div>
<ul class="game-comments__list" id="comment-list"></ul>
</section>
</div>
<script>
// game-detail.jsp 승격본과 동일 로직 (좌표공식·미터바)
var AXIS_KEYS = ['immersion','creativity','controls','completeness','sound','visual'];
var AXIS_LABELS_KO = { immersion:'몰입성', creativity:'창의성', controls:'조작성', completeness:'완성도', sound:'사운드', visual:'비주얼' };
var SVG_NS = 'http://www.w3.org/2000/svg';
var labelSeq = 0, hexSeq = 0;
function buildHexRadar(scores, cx, cy, R) {
var canvas = cx * 2;
var svg = document.createElementNS(SVG_NS, 'svg');
svg.setAttribute('viewBox', '0 0 ' + canvas + ' ' + canvas);
svg.setAttribute('width', String(Math.round(canvas * 0.85)));
svg.setAttribute('height', String(Math.round(canvas * 0.85)));
svg.setAttribute('role', 'img');
svg.setAttribute('class', 'game-axis-radar');
var nums = AXIS_KEYS.map(function (k, i) { var v = scores ? Number(scores[i]) : 0; return (isFinite(v) && v > 0) ? v : 0; });
var hasData = nums.some(function (v) { return v > 0; });
function ptOnAxis(i, ratio) { var ang = (-90 + 60 * i) * Math.PI / 180; return { x: cx + R * ratio * Math.cos(ang), y: cy + R * ratio * Math.sin(ang) }; }
function polyPoints(fn) { return AXIS_KEYS.map(function (k, i) { var p = ptOnAxis(i, fn(i)); return p.x.toFixed(2) + ',' + p.y.toFixed(2); }).join(' '); }
var gid = 'hexfill-' + (++hexSeq);
var defs = document.createElementNS(SVG_NS, 'defs');
var grad = document.createElementNS(SVG_NS, 'radialGradient');
grad.setAttribute('id', gid); grad.setAttribute('cx', '50%'); grad.setAttribute('cy', '50%'); grad.setAttribute('r', '62%');
[['0%','rgba(240,201,120,0.55)'],['100%','rgba(232,165,75,0.26)']].forEach(function (s) {
var st = document.createElementNS(SVG_NS,'stop'); st.setAttribute('offset', s[0]); st.setAttribute('stop-color', s[1]); grad.appendChild(st);
});
defs.appendChild(grad); svg.appendChild(defs);
for (var level = 1; level <= 4; level++) {
var grid = document.createElementNS(SVG_NS, 'polygon');
grid.setAttribute('class', 'game-axis-grid' + (level === 4 ? ' game-axis-grid--outer' : ''));
(function (lv) { grid.setAttribute('points', polyPoints(function () { return lv / 4; })); })(level);
svg.appendChild(grid);
}
AXIS_KEYS.forEach(function (k, i) {
var line = document.createElementNS(SVG_NS, 'line'); line.setAttribute('class', 'game-axis-line');
var p = ptOnAxis(i, 1); line.setAttribute('x1', String(cx)); line.setAttribute('y1', String(cy));
line.setAttribute('x2', p.x.toFixed(2)); line.setAttribute('y2', p.y.toFixed(2)); svg.appendChild(line);
});
if (hasData) {
var poly = document.createElementNS(SVG_NS, 'polygon'); poly.setAttribute('class', 'game-axis-poly');
poly.setAttribute('points', polyPoints(function (i) { return nums[i] / 5; })); poly.setAttribute('fill', 'url(#' + gid + ')'); svg.appendChild(poly);
AXIS_KEYS.forEach(function (k, i) {
if (nums[i] <= 0) return; var p = ptOnAxis(i, nums[i] / 5);
var dot = document.createElementNS(SVG_NS, 'circle'); dot.setAttribute('class', 'game-axis-dot');
dot.setAttribute('cx', p.x.toFixed(2)); dot.setAttribute('cy', p.y.toFixed(2)); dot.setAttribute('r', Math.max(1.6, R * 0.05).toFixed(2)); svg.appendChild(dot);
});
}
svg.setAttribute('aria-label', hasData ? ('6축 평가: ' + AXIS_KEYS.map(function (k, i) { return AXIS_LABELS_KO[k] + ' ' + nums[i]; }).join(', ')) : '6축 평가 없음');
return { svg: svg, nums: nums, hasData: hasData };
}
function buildRadarLegend(nums, hasData) {
var dl = document.createElement('dl'); dl.className = 'game-reviews__axis-legend';
AXIS_KEYS.forEach(function (k, i) {
var row = document.createElement('div'); row.className = 'game-reviews__axis-legend-row';
var dt = document.createElement('dt'); dt.textContent = AXIS_LABELS_KO[k];
var meter = document.createElement('div'); meter.className = 'game-reviews__axis-meter';
var bar = document.createElement('span'); bar.style.width = (hasData ? (nums[i] / 5 * 100) : 0) + '%'; meter.appendChild(bar);
var dd = document.createElement('dd'); dd.textContent = hasData ? String(nums[i]) : '';
row.appendChild(dt); row.appendChild(meter); row.appendChild(dd); dl.appendChild(row);
});
return dl;
}
var axisScores = {};
function buildStarRadioGroup(axisKey, labelText) {
var labelId = 'star-' + axisKey + '-' + (++labelSeq);
var group = document.createElement('div'); group.className = 'game-stars game-stars--input game-stars--axis';
group.setAttribute('role', 'radiogroup'); group.setAttribute('aria-labelledby', labelId);
var radios = [];
function paint() { var sel = axisScores[axisKey] || 0; radios.forEach(function (b, idx) { var v = idx + 1; b.classList.toggle('is-on', v <= sel); b.setAttribute('aria-checked', v === sel ? 'true' : 'false'); b.tabIndex = (v === sel) ? 0 : -1; }); if (!sel) radios[0].tabIndex = 0; }
function select(v) { axisScores[axisKey] = v; paint(); }
for (var i = 1; i <= 5; i++) { (function (v) {
var b = document.createElement('button'); b.type = 'button'; b.className = 'game-stars__btn'; b.setAttribute('role','radio'); b.setAttribute('aria-checked','false'); b.setAttribute('aria-label', labelText + ' ' + v + '점'); b.tabIndex = -1; b.textContent = '★';
b.addEventListener('click', function () { select(v); b.focus(); });
b.addEventListener('keydown', function (ev) { var cur = axisScores[axisKey] || 0; var next = null;
switch (ev.key) { case 'ArrowRight': case 'ArrowUp': next = Math.min(5,(cur||v)+1); break; case 'ArrowLeft': case 'ArrowDown': next = Math.max(1,(cur||v)-1); break; case 'Home': next = 1; break; case 'End': next = 5; break; case ' ': case 'Enter': next = v; break; default: return; }
ev.preventDefault(); select(next); radios[next-1].focus(); });
radios.push(b); group.appendChild(b);
})(i); }
paint(); return { group: group, labelId: labelId };
}
function fillStars(c, rating) { c.innerHTML = ''; for (var i = 1; i <= 5; i++) { var s = document.createElement('span'); s.className = 'game-star' + (i <= rating ? ' is-on' : ''); s.textContent = '★'; c.appendChild(s); } }
function buildStarDisplay(rating) { var w = document.createElement('div'); w.className = 'game-stars game-stars--display'; w.setAttribute('aria-hidden','true'); fillStars(w, rating); return w; }
// 요약 (큰 레이더 + 미터바 범례)
fillStars(document.getElementById('avg-stars'), 4);
(function () {
var panel = document.getElementById('summary-panel');
var radar = buildHexRadar([4.3, 4.6, 3.8, 4.4, 3.5, 4.7], 100, 100, 80);
panel.appendChild(radar.svg); panel.appendChild(buildRadarLegend(radar.nums, true));
})();
// 6축 입력 위젯
(function () {
var axesEl = document.getElementById('axes');
AXIS_KEYS.forEach(function (k) {
var row = document.createElement('div'); row.className = 'game-reviews__axis';
var label = document.createElement('span'); label.className = 'game-reviews__axis-label'; label.textContent = AXIS_LABELS_KO[k];
var w = buildStarRadioGroup(k, AXIS_LABELS_KO[k]); label.id = w.labelId;
row.appendChild(label); row.appendChild(w.group); axesEl.appendChild(row);
});
var ov = buildStarRadioGroup('overall', '종합 별점'); var oEl = document.getElementById('overall');
while (ov.group.firstChild) oEl.appendChild(ov.group.firstChild);
})();
// 글자수 카운터 + 최소10자 경고
function counter(ta, out, max, min) {
function upd() { var len = ta.value.length; out.textContent = len + '/' + max; if (min) { var w = len > 0 && len < min; out.parentElement.classList.toggle('is-warn', w); } }
ta.addEventListener('input', upd); upd();
}
counter(document.getElementById('rbody'), document.getElementById('rcount'), 1000, 10);
counter(document.getElementById('cbody'), document.getElementById('ccount'), 200, 0);
// 리뷰 카드 (axes 있음/없음 두 케이스)
function reviewCard(r) {
var li = document.createElement('li'); li.className = 'game-reviews__item' + (r.mine ? ' is-mine' : '');
var head = document.createElement('div'); head.className = 'game-reviews__item-head';
head.appendChild(buildStarDisplay(r.rating));
var nick = document.createElement('span'); nick.className = 'game-reviews__nick'; nick.textContent = r.nick; head.appendChild(nick);
if (r.mine) { var mb = document.createElement('span'); mb.className = 'game-reviews__mine-badge'; mb.textContent = '내 리뷰'; head.appendChild(mb); }
var t = document.createElement('time'); t.className = 'game-reviews__time'; t.textContent = r.time; head.appendChild(t);
if (r.edited) { var ed = document.createElement('span'); ed.className = 'game-reviews__edited'; ed.textContent = '(수정됨)'; head.appendChild(ed); }
li.appendChild(head);
var p = document.createElement('p'); p.className = 'game-reviews__body'; p.textContent = r.body; li.appendChild(p);
var cardScores = AXIS_KEYS.map(function (k) { return r.axes ? r.axes[k] : 0; });
var cr = buildHexRadar(cardScores, 60, 60, 44);
var wrap = document.createElement('div'); wrap.className = 'game-reviews__radar game-reviews__card-radar';
if (cr.hasData) { wrap.appendChild(cr.svg); var lg = buildRadarLegend(cr.nums, true); lg.className += ' sr-only'; wrap.appendChild(lg); }
else { var none = document.createElement('span'); none.className = 'game-reviews__radar-empty'; none.textContent = '6축 평가 데이터 없음'; wrap.appendChild(none); }
li.appendChild(wrap);
return li;
}
var rl = document.getElementById('review-list');
rl.appendChild(reviewCard({ rating: 5, nick: '도토리', time: '12분 전', edited: false, mine: true, body: '몰입감이 정말 뛰어나요. 사운드가 조금 아쉽지만 전반적으로 완성도 높은 게임입니다.', axes: { immersion:5, creativity:5, controls:4, completeness:5, sound:3, visual:5 } }));
rl.appendChild(reviewCard({ rating: 3, nick: '바삭이', time: '3일 전', edited: true, mine: false, body: '아이디어는 좋은데 조작이 좀 불편했어요. 다음 빌드 기대합니다.', axes: { immersion:3, creativity:4, controls:2, completeness:3, sound:3, visual:4 } }));
rl.appendChild(reviewCard({ rating: 4, nick: '레거시유저', time: '2026. 5. 1.', edited: false, mine: false, body: '(구버전 리뷰 — 6축 데이터 없음 케이스)', axes: null }));
// 덧글 카드
function commentCard(c) {
var li = document.createElement('li'); li.className = 'game-comments__item';
var av = document.createElement('div'); av.className = 'game-comments__avatar'; av.textContent = c.nick.charAt(0); li.appendChild(av);
var body = document.createElement('div'); body.style.flex = '1';
var meta = document.createElement('div'); meta.className = 'game-comments__meta';
var nick = document.createElement('span'); nick.className = 'game-comments__nick'; nick.textContent = c.nick; meta.appendChild(nick);
var t = document.createElement('time'); t.textContent = c.time; meta.appendChild(t);
if (c.edited) { var ed = document.createElement('span'); ed.className = 'game-comments__edited'; ed.textContent = '(수정됨)'; meta.appendChild(ed); }
body.appendChild(meta);
var p = document.createElement('p'); p.textContent = c.body; body.appendChild(p); li.appendChild(body);
return li;
}
var cl = document.getElementById('comment-list');
cl.appendChild(commentCard({ nick: '플레이어A', time: '방금 전', edited: false, body: '재밌게 했습니다! 2스테이지 보스가 인상적이네요.' }));
cl.appendChild(commentCard({ nick: '(탈퇴한 사용자)', time: '5일 전', edited: false, body: '탈퇴자 댓글 마스킹 케이스 — 이름이 "(탈퇴한 사용자)" 로 표시됩니다.' }));
// 테마 토글
document.getElementById('theme-toggle').addEventListener('click', function () {
var dark = document.documentElement.getAttribute('data-theme') === 'dark';
document.documentElement.setAttribute('data-theme', dark ? 'light' : 'dark');
this.textContent = dark ? '🌙 다크' : '☀️ 라이트';
});
</script>
</body>
</html>