mcp-quiz-server
Version:
๐ง AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.
166 lines โข 7.47 kB
JavaScript
import { EventManagement } from './EventManagement';
export class AnswerHandler {
constructor(store, settingsService) {
this.currentSelectionTimeout = null;
this.store = store;
this.settingsService = settingsService;
}
setupAnswerHandling(container) {
const settings = this.settingsService.getSettings();
const state = this.store.getState();
const isSingleMode = state.ui.viewMode === 'single';
if (this.currentSelectionTimeout) {
clearTimeout(this.currentSelectionTimeout);
this.currentSelectionTimeout = null;
}
EventManagement.cleanup(['answer-cards', 'radio-inputs']);
const quizMode = settings.ui.quizMode ||
(settings.quiz.instantSubmission
? 'instant'
: settings.ui.clickToSelectCards
? 'cards'
: 'traditional');
console.log('๐ฎ Setting up answer handling for mode:', quizMode);
if (quizMode === 'instant') {
this.setupInstantSubmission(container);
}
else if (isSingleMode && quizMode === 'cards') {
this.setupClickableCards(container);
}
else {
this.setupTraditionalRadioButtons(container);
}
}
setupInstantSubmission(container) {
EventManagement.delegate(container, '.quiz-option-card', 'click', (event, target) => this.handleInstantSubmission(event, target), 'answer-cards-instant');
EventManagement.delegate(container, '.quiz-option-card', 'keydown', (event, target) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
this.handleInstantSubmission(event, target);
}
}, 'answer-cards-instant-keyboard');
}
setupClickableCards(container) {
EventManagement.delegate(container, '.quiz-option-card', 'click', (event, target) => this.handleClickableCard(event, target), 'answer-cards-clickable');
EventManagement.delegate(container, '.quiz-option-card', 'keydown', (event, target) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
this.handleClickableCard(event, target);
}
}, 'answer-cards-clickable-keyboard');
}
setupTraditionalRadioButtons(container) {
EventManagement.delegate(container, 'input[type=\"radio\"]', 'change', event => this.handleRadioSelection(event), 'radio-inputs');
}
handleInstantSubmission(event, target) {
const questionId = target.getAttribute('data-question-id');
const answer = target.getAttribute('data-answer');
if (!questionId || !answer) {
console.error('๐จ Missing question ID or answer in instant submission');
return;
}
console.log('โก Instant submission:', { questionId, answer });
this.store.updateAnswer(questionId, answer);
this.showAnswerFeedback(target, true);
const settings = this.settingsService.getSettings();
if (settings.ui.autoAdvanceQuestions) {
this.currentSelectionTimeout = setTimeout(() => {
this.triggerNextQuestion();
}, 1500);
}
}
handleClickableCard(event, target) {
const questionId = target.getAttribute('data-question-id');
const answer = target.getAttribute('data-answer');
if (!questionId || !answer) {
console.error('๐จ Missing question ID or answer in clickable card');
return;
}
console.log('๐ Card selection:', { questionId, answer });
this.store.updateAnswer(questionId, answer);
this.updateCardSelection(questionId, answer);
this.showContinueButton();
}
handleRadioSelection(event) {
const radio = event.target;
const questionId = radio.name;
const answer = radio.value;
console.log('๐ป Radio selection:', { questionId, answer });
this.store.updateAnswer(questionId, answer);
this.showContinueButton();
}
updateCardSelection(questionId, selectedAnswer) {
const cards = document.querySelectorAll(`[data-question-id=\"${questionId}\"]`);
cards.forEach(card => {
const cardAnswer = card.getAttribute('data-answer');
const isSelected = cardAnswer === selectedAnswer;
if (isSelected) {
card.classList.add('border-blue-500', 'bg-blue-50', 'dark:bg-blue-900/20', 'shadow-md');
card.classList.remove('border-gray-200', 'dark:border-gray-600');
card.setAttribute('aria-pressed', 'true');
}
else {
card.classList.remove('border-blue-500', 'bg-blue-50', 'dark:bg-blue-900/20', 'shadow-md');
card.classList.add('border-gray-200', 'dark:border-gray-600');
card.setAttribute('aria-pressed', 'false');
}
const indicator = card.querySelector('.answer-indicator');
if (indicator) {
if (isSelected) {
indicator.classList.add('border-blue-500', 'bg-blue-500');
indicator.classList.remove('border-gray-300', 'dark:border-gray-500');
indicator.innerHTML = '<div class=\"w-2 h-2 bg-white rounded-full mx-auto mt-1\"></div>';
}
else {
indicator.classList.remove('border-blue-500', 'bg-blue-500');
indicator.classList.add('border-gray-300', 'dark:border-gray-500');
indicator.innerHTML = '';
}
}
});
}
showAnswerFeedback(target, isCorrect) {
if (isCorrect) {
target.classList.add('bg-green-100', 'border-green-500', 'dark:bg-green-900/20');
}
else {
target.classList.add('bg-red-100', 'border-red-500', 'dark:bg-red-900/20');
}
const announcement = isCorrect ? 'Answer selected' : 'Answer selected';
this.announceToScreenReader(announcement);
}
showContinueButton() {
const continueContainer = document.querySelector('.continue-button-container');
const completeContainer = document.querySelector('.complete-quiz-container');
if (continueContainer) {
continueContainer.style.display = '';
}
if (completeContainer) {
completeContainer.style.display = '';
}
}
triggerNextQuestion() {
const event = new CustomEvent('quiz:next-question');
document.dispatchEvent(event);
}
announceToScreenReader(message) {
const announcement = document.createElement('div');
announcement.setAttribute('aria-live', 'polite');
announcement.setAttribute('aria-atomic', 'true');
announcement.className = 'sr-only';
announcement.textContent = message;
document.body.appendChild(announcement);
setTimeout(() => {
document.body.removeChild(announcement);
}, 1000);
}
cleanup() {
if (this.currentSelectionTimeout) {
clearTimeout(this.currentSelectionTimeout);
this.currentSelectionTimeout = null;
}
EventManagement.componentCleanup('answer-');
console.log('๐งน AnswerHandler cleanup completed');
}
}
//# sourceMappingURL=AnswerHandler.js.map