// Base class for shared UI functionality class UIHandler { constructor() { this.elements = {}; this.activeComponents = new Set(); this.componentStates = new Map(); this.observers = new Map(); this.handleOutsideClick = this.handleOutsideClick.bind(this); this.handleEscapeKey = this.handleEscapeKey.bind(this); } bindElements() { console.error('bindElements must be implemented by child class'); } // Add shared event binding bindComponentEvents() { if (!this.handlers) return; Object.entries(this.handlers).forEach(([elementKey, config]) => { const elements = this.elements[elementKey]; if (!elements) return; // Handle NodeList if (elements instanceof NodeList || Array.isArray(elements)) { elements.forEach(element => { this.bindEventsToElement(element, config); }); } // Handle single element else { this.bindEventsToElement(elements, config); } }); } bindEventsToElement(element, config) { if (typeof config === 'function') { // If config is a function, bind it to click event element.addEventListener('click', config.bind(this)); } else if (typeof config === 'object') { // Handle object with multiple events Object.entries(config).forEach(([event, handler]) => { if (event !== 'forEach' && typeof handler === 'function') { element.addEventListener(event, handler); } }); } } bindEvents() { document.addEventListener('click', this.handleOutsideClick); document.addEventListener('keydown', this.handleEscapeKey); } // Component State Management isComponentActive(componentKey) { return this.activeComponents.has(componentKey); } // Add helper method for handling component state setComponentState(e, t, n = {}) { const { element: s, toggle: i, activeClass: r = "open", focusElement: o = null, ariaLabel: c = null, ariaHidden: a = null, cleanup: l = null } = n; if (s) { t ? this.activeComponents.add(e) : this.activeComponents.delete(e); s.classList.toggle(r, t); if (i) { i.setAttribute("aria-expanded", t.toString()); c && i.setAttribute("aria-label", c); } if (null !== a) { s.setAttribute("aria-hidden", (!t).toString()); } // Add null check before calling focus() if (o && typeof o.focus === 'function') { o.focus(); } if (!t && l) { l(); } this.componentStates.set(e, { isActive: t, activeClass: r, options: n }); } } // Add keyboard navigation management initializeKeyboardNavigation(config) { this.keyboardConfig = config; Object.entries(config).forEach(([elementKey, keyHandlers]) => { const element = this.elements[elementKey]; if (!element) return; element.addEventListener('keydown', (e) => { const handler = keyHandlers[e.key]; if (handler) { handler.call(this, e); } }); }); } handleOutsideClick(event) { console.error('handleOutsideClick must be implemented by child class'); } handleEscapeKey(event) { console.error('handleEscapeKey must be implemented by child class'); } // Add shared handler initialization initializeHandlers(handlers) { if (!handlers || typeof handlers !== 'object') { console.error('Invalid handlers configuration'); return; } this.handlers = Object.entries(handlers).reduce((acc, [key, value]) => { if (typeof value === 'function') { acc[key] = value.bind(this); } else if (value.forEach) { acc[key] = { ...value, handler: value.handler?.bind(this) }; } else if (typeof value === 'object') { acc[key] = Object.entries(value).reduce((events, [event, handler]) => { events[event] = typeof handler === 'function' ? handler.bind(this) : handler; return events; }, {}); } return acc; }, {}); } createObserver(options, callback) { const defaultOptions = { root: null, rootMargin: '0px', threshold: 0 }; return new IntersectionObserver( callback, { ...defaultOptions, ...options } ); } initializeObserver(observerId, elements, options, callback) { if (!elements || !elements.length) return; // Cleanup existing observer if it exists this.cleanupObserver(observerId); // Create and store new observer const observer = this.createObserver(options, callback); this.observers.set(observerId, { observer, elements: new Set(elements) }); // Start observing elements elements.forEach(element => { if (element) { observer.observe(element); } }); return observer; } cleanupObserver(observerId) { const observerData = this.observers.get(observerId); if (observerData) { const { observer, elements } = observerData; elements.forEach(element => { if (element) { observer.unobserve(element); } }); observer.disconnect(); this.observers.delete(observerId); } } cleanupAllObservers() { this.observers.forEach((_, observerId) => { this.cleanupObserver(observerId); }); } // Optional cleanup method for removing event listeners cleanup() { document.removeEventListener('click', this.handleOutsideClick); document.removeEventListener('keydown', this.handleEscapeKey); this.cleanupComponentEvents(); this.cleanupAllObservers(); } // Add shared event cleanup cleanupComponentEvents() { Object.entries(this.handlers).forEach(([elementKey, config]) => { const element = this.elements[elementKey]; if (!element) return; if (config.forEach && element.forEach) { element.forEach(item => { if (item._boundHandler) { item.removeEventListener('click', item._boundHandler); delete item._boundHandler; } }); } else if (typeof config === 'object') { Object.entries(config).forEach(([event, handler]) => { if (event !== 'forEach') { element.removeEventListener(event, handler); } }); } }); } handleSearchCheckboxes(form) { if (!form) return; const allCheckbox = form.querySelector('input[type="checkbox"][value="1"]'); const otherCheckboxes = form.querySelectorAll('input[type="checkbox"]:not([value="1"])'); if (!allCheckbox) return; const updateCheckboxes = (e) => { const checkbox = e.target; if (checkbox === allCheckbox) { // If 'all' is checked, uncheck others if (checkbox.checked) { otherCheckboxes.forEach(cb => { cb.checked = false; }); } } else { // If any other checkbox is checked, uncheck 'all' if (checkbox.checked) { allCheckbox.checked = false; } else { // If no other checkboxes are checked, check 'all' const anyOthersChecked = Array.from(otherCheckboxes) .some(cb => cb.checked); if (!anyOthersChecked) { allCheckbox.checked = true; } } } }; // Add event listeners to all checkboxes form.querySelectorAll('input[type="checkbox"]') .forEach(checkbox => { checkbox.addEventListener('change', updateCheckboxes); }); // Store cleanup function form._removeCheckboxListeners = () => { form.querySelectorAll('input[type="checkbox"]') .forEach(checkbox => { checkbox.removeEventListener('change', updateCheckboxes); }); }; } } window.UIHandler = UIHandler;