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.
136 lines (121 loc) • 4.05 kB
text/typescript
/**
* @moduleName: Base Component Class - Frontend Architecture Foundation
* @version: 2.0.0
* @since: 2025-07-23
* @lastUpdated: 2025-07-24
* @projectSummary: MCP Quiz Server - Base component class providing lifecycle management and event handling for all UI components
* @techStack: TypeScript, DOM API, Event System, Component Architecture
* @dependency: EventEmitter utility class
* @interModuleDependency: All frontend components extend this base class
* @requirementsTraceability:
* {@link Requirements.REQ_ARCH_001} (Component Integration System)
* {@link Requirements.REQ_UI_001} (Component Architecture Foundation)
* @briefDescription: Abstract base class that provides common functionality for all UI components including lifecycle management, event handling, DOM manipulation, and cleanup
* @methods: mount, unmount, render, bindEvents, onMount, onUnmount, destroy
* @contributors: Architecture Team, GitHub Copilot
* @examples: class MyComponent extends Component with render method implementation
* @vulnerabilitiesAssessment: DOM element validation prevents XSS, proper event cleanup prevents memory leaks
*/
import { EventEmitter } from '../utils/index';
export abstract class Component extends EventEmitter {
protected element: HTMLElement;
protected mounted = false;
/**
* @description Creates a new component instance
* @param {HTMLElement | string} element - DOM element or CSS selector
* @throws {Error} If element selector is not found in DOM
*/
constructor(element: HTMLElement | string) {
super();
if (typeof element === 'string') {
const found = document.querySelector(element);
if (!found) {
throw new Error(`Element not found: ${element}`);
}
this.element = found as HTMLElement;
} else {
this.element = element;
}
}
/**
* @description Mounts the component to the DOM, rendering content and binding events
* @returns {void}
*/
mount(): void {
if (this.mounted) return;
this.mounted = true;
this.render();
this.bindEvents();
this.onMount();
}
/**
* @description Unmounts the component, cleaning up events and resources
* @returns {void}
*/
unmount(): void {
if (!this.mounted) return;
this.mounted = false;
this.unbindEvents();
this.onUnmount();
}
/**
* @description Abstract method for rendering component content - must be implemented by subclasses
* @returns {void}
*/
protected abstract render(): void;
protected bindEvents(): void {
// Override in subclasses
}
protected unbindEvents(): void {
// Override in subclasses
}
protected onMount(): void {
// Override in subclasses
}
protected onUnmount(): void {
// Override in subclasses
}
protected updateElement(
updates: Partial<{
innerHTML: string;
textContent: string;
className: string;
hidden: boolean;
}>
): void {
if (updates.innerHTML !== undefined) {
this.element.innerHTML = updates.innerHTML;
}
if (updates.textContent !== undefined) {
this.element.textContent = updates.textContent;
}
if (updates.className !== undefined) {
this.element.className = updates.className;
}
if (updates.hidden !== undefined) {
this.element.hidden = updates.hidden;
}
}
/**
* @description Shows the component by removing hidden class
* @returns {void}
*/
show(): void {
this.element.classList.remove('hidden');
}
/**
* @description Hides the component by adding hidden class
* @returns {void}
*/
hide(): void {
this.element.classList.add('hidden');
}
/**
* @description Toggles component visibility
* @param {boolean} [force] - Optional force parameter for toggle
* @returns {void}
*/
toggle(force?: boolean): void {
this.element.classList.toggle('hidden', force);
}
}