/** * ViewToggle - Unified component for view mode selection * * Usage: * const toggle = new ViewToggle('#container', { * options: [ * { value: 'list', label: 'List' }, * { value: 'charts', label: 'Charts' } * ], * default: 'charts', * storageKey: 'gapup_view_mode', * onChange: (mode) => { } * }) * * Or use the helper for common configs: * ViewToggle.createListChart('#container', 'gapup_view_mode') * ViewToggle.createGridView('#container', 'scanner_view') */ class ViewToggle { constructor(containerOrSelector, options = {}) { this.container = typeof containerOrSelector === "string" ? document.querySelector(containerOrSelector) : containerOrSelector; this.options = { options: options.options || [ { value: "list", label: "List" }, { value: "charts", label: "Charts" }, ], default: options.default || options.options?.[0]?.value || "list", storageKey: options.storageKey || null, onChange: options.onChange || (() => {}), align: options.align || "start", // 'start', 'center', 'end' }; this.currentMode = this._loadState(); this._render(); } _loadState() { if (this.options.storageKey) { return ( localStorage.getItem(this.options.storageKey) || this.options.default ); } return this.options.default; } _saveState(mode) { if (this.options.storageKey) { localStorage.setItem(this.options.storageKey, mode); } } _render() { const buttons = this.options.options .map((opt) => { const isActive = opt.value === this.currentMode; return ``; }) .join(""); this.container.innerHTML = `
${buttons}
`; // Add event listeners this.container.querySelectorAll(".view-btn").forEach((btn) => { btn.addEventListener("click", () => { this._handleClick(btn.dataset.mode); }); }); } _handleClick(mode) { if (mode === this.currentMode) return; this.currentMode = mode; this._saveState(mode); this._updateButtons(); this.options.onChange(mode); } _updateButtons() { this.container.querySelectorAll(".view-btn").forEach((btn) => { btn.classList.toggle("active", btn.dataset.mode === this.currentMode); }); } /** Get current view mode */ getValue() { return this.currentMode; } /** Set current mode programmatically */ setValue(mode) { const validMode = this.options.options.some((o) => o.value === mode); if (validMode) { this._handleClick(mode); } } /** Destroy and clean up */ destroy() { this.container.innerHTML = ""; } /** * Factory: List vs Charts toggle (for watchlist, gainers) */ static createListChart(containerSelector, storageKey, onChange = () => {}) { return new ViewToggle(containerSelector, { options: [ { value: "list", label: "List" }, { value: "charts", label: "Charts" }, ], default: "charts", storageKey: storageKey, onChange: onChange, }); } }