365 lines
27 KiB
HTML
365 lines
27 KiB
HTML
<!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>
|