Files
Transcendence/srcs/frontend/src/windows.js
T

235 lines
5.9 KiB
JavaScript

import { CSS } from './config.js';
import { eventBus, Events } from './events.js';
/**
* Centralized window registry
* Manages window visibility and positioning
*/
class WindowRegistry {
constructor() {
this.windows = new Map();
}
/**
* Registers a window in the registry
* @param {string} name - Unique window name
* @param {Window} window - Window instance
*/
register(name, window) {
this.windows.set(name, window);
}
/**
* Gets a window by its name
* @param {string} name - Window name
* @returns {Window|undefined}
*/
get(name) {
return this.windows.get(name);
}
/**
* Returns all visible windows
* @returns {Window[]}
*/
getVisible() {
return Array.from(this.windows.values()).filter(w => w.isVisible());
}
/**
* Reorganizes visible windows
*/
reorganize() {
const visible = this.getVisible();
if (visible.length === 0) return;
if (visible.length === 1) {
visible[0].setPosition('center');
} else if (visible.length === 2) {
visible[0].setPosition('left');
visible[1].setPosition('right');
} else {
visible.forEach(w => w.setPosition('center'));
}
}
/**
* Shows a window and reorganizes
* @param {string} name - Window name
*/
show(name) {
const window = this.get(name);
if (window) {
window.show();
}
}
/**
* Hides a window and reorganizes
* @param {string} name - Window name
*/
hide(name) {
const window = this.get(name);
if (window) {
window.hide();
}
}
/**
* Toggles window visibility
* @param {string} name - Window name
*/
toggle(name) {
const window = this.get(name);
if (window) {
window.toggle();
}
}
}
// Singleton registry instance
export const windowRegistry = new WindowRegistry();
/**
* Base class for all windows
* Uses CSS classes instead of inline styles
*/
export class Window {
/**
* @param {Object} options - Configuration options
* @param {string} options.name - Unique name for the registry
* @param {string} options.title - Title displayed in the header
* @param {string[]} [options.cssClasses] - Additional CSS classes
*/
constructor({ name, title, cssClasses = [] }) {
this.name = name;
// Create main container
this.element = document.createElement('div');
this.element.className = [CSS.WINDOW, ...cssClasses].join(' ');
// Header
this.header = document.createElement('div');
this.header.className = CSS.WINDOW_HEADER;
this.titleElement = document.createElement('span');
this.titleElement.className = CSS.WINDOW_TITLE;
this.titleElement.textContent = title;
this.closeBtn = document.createElement('button');
this.closeBtn.className = CSS.WINDOW_CLOSE;
this.closeBtn.textContent = '✖';
this.closeBtn.setAttribute('aria-label', 'Close');
this.closeBtn.addEventListener('click', () => this.hide());
this.header.append(this.titleElement, this.closeBtn);
// Body
this.body = document.createElement('div');
this.body.className = CSS.WINDOW_BODY;
// Assembly
this.element.append(this.header, this.body);
document.body.appendChild(this.element);
// Register in the registry
windowRegistry.register(name, this);
}
/**
* Checks if the window is visible
* @returns {boolean}
*/
isVisible() {
return this.element.classList.contains(CSS.WINDOW_VISIBLE);
}
/**
* Sets the window position
* @param {'center'|'left'|'right'} position
*/
setPosition(position) {
this.element.classList.remove('window--left', 'window--right');
if (position === 'left') {
this.element.classList.add('window--left');
} else if (position === 'right') {
this.element.classList.add('window--right');
}
}
/**
* Shows the window
*/
show() {
const wasHidden = !this.isVisible();
this.element.classList.add(CSS.WINDOW_VISIBLE);
if (wasHidden) {
windowRegistry.reorganize();
eventBus.emit(Events.WINDOW_OPENED, { name: this.name });
}
}
/**
* Hides the window
*/
hide() {
const wasVisible = this.isVisible();
this.element.classList.remove(CSS.WINDOW_VISIBLE);
if (wasVisible) {
windowRegistry.reorganize();
eventBus.emit(Events.WINDOW_CLOSED, { name: this.name });
}
}
/**
* Toggles visibility
*/
toggle() {
if (this.isVisible()) {
this.hide();
} else {
this.show();
}
}
/**
* Updates the window title
* @param {string} title
*/
setTitle(title) {
this.titleElement.textContent = title;
}
/**
* Creates an element with CSS classes
* @param {string} tag - HTML tag
* @param {string|string[]} classes - CSS classes
* @param {Object} [attrs] - Additional attributes
* @returns {HTMLElement}
*/
createElement(tag, classes, attrs = {}) {
const element = document.createElement(tag);
const classList = Array.isArray(classes) ? classes : [classes];
element.className = classList.filter(Boolean).join(' ');
Object.entries(attrs).forEach(([key, value]) => {
if (key === 'text') {
element.textContent = value;
} else if (key === 'html') {
element.innerHTML = value;
} else {
element.setAttribute(key, value);
}
});
return element;
}
}
// Export old class name for compatibility (alias)
export { Window as fenetre };