Files
cogmind/public/testPage.js
T
2026-08-10 21:35:17 +09:00

137 lines
4.8 KiB
JavaScript

class TestPage extends HTMLElement {
connectedCallback() {
this.className = 'screen';
this.renderQuestion();
}
renderHeader(step) {
const wrapper = document.createElement('div');
const counter = document.createElement('span');
counter.className = 'muted';
counter.textContent = `${step} / ${state.questions.length}`;
const topbar = createTopbar([counter]);
topbar.querySelector('.brand').onclick = () => {
if (confirm('처음 화면으로 돌아갈까요? 지금까지의 응답은 사라집니다.')) {
showStartScreen();
}
};
const track = document.createElement('div');
track.className = 'progress-track';
const fill = document.createElement('div');
fill.className = 'progress-fill';
fill.style.width = `${(step / state.questions.length) * 100}%`;
track.appendChild(fill);
wrapper.append(topbar, track);
return wrapper;
}
renderQuestion() {
const index = state.current;
const question = state.questions[index];
let selected = state.answers[index] ?? null;
let confidence = state.confidences[index] ?? null;
this.replaceChildren(this.renderHeader(index + 1));
const card = document.createElement('div');
card.className = 'card';
const title = document.createElement('p');
title.className = 'question-text';
title.textContent = `${index + 1}. ${question.question}`;
card.appendChild(title);
const list = document.createElement('ul');
list.className = 'options';
const optionButtons = question.options.map((option, i) => {
const item = document.createElement('li');
const button = document.createElement('button');
button.type = 'button';
button.className = 'option';
button.setAttribute('aria-pressed', String(option === selected));
const key = document.createElement('span');
key.className = 'key';
key.textContent = String.fromCharCode(65 + i);
const text = document.createElement('span');
text.textContent = option;
button.append(key, text);
button.onclick = () => {
selected = option;
optionButtons.forEach((b) => b.setAttribute('aria-pressed', 'false'));
button.setAttribute('aria-pressed', 'true');
updateSubmitState();
};
item.appendChild(button);
list.appendChild(item);
return button;
});
card.appendChild(list);
const confidenceBox = document.createElement('div');
confidenceBox.className = 'confidence';
const confidenceLabel = document.createElement('label');
confidenceLabel.textContent = '이 답에 얼마나 확신하나요?';
confidenceBox.append(
confidenceLabel,
createChipGroup(CONFIDENCE, {
selected: confidence,
onSelect: (value) => {
confidence = value;
updateSubmitState();
},
})
);
card.appendChild(confidenceBox);
const actions = document.createElement('div');
actions.className = 'actions';
if (index > 0) {
actions.appendChild(createButton('이전 문제', () => history.back(), 'ghost'));
}
const isLast = index === state.questions.length - 1;
const submitButton = document.createElement('button');
submitButton.className = 'primary';
submitButton.textContent = isLast ? '결과 보기' : '다음 문제';
submitButton.onclick = () => {
state.answers[index] = selected;
state.confidences[index] = confidence;
if (isLast) {
finishQuiz();
} else {
goToQuestion(index + 1);
}
};
actions.appendChild(submitButton);
card.appendChild(actions);
const updateSubmitState = () => {
submitButton.disabled = !(selected && confidence);
};
updateSubmitState();
this.appendChild(card);
this.onkeydown = (event) => {
if (event.target.tagName === 'INPUT') return;
const fromLetter = event.key.toUpperCase().charCodeAt(0) - 65;
const fromNumber = Number(event.key) - 1;
const at = optionButtons[fromLetter] ?? optionButtons[fromNumber];
if (event.key.length === 1 && at) {
at.click();
at.focus();
}
};
this.tabIndex = -1;
this.focus({ preventScroll: true });
}
}
customElements.define('test-page', TestPage);