class IconPicker { constructor() { this.modal = null; this.container = null; this.searchInput = null; this.iconList = null; this.closeButton = null; this.isInitialized = false; this.isModalOpen = false; this.icons = []; this.currentSearch = ''; this.eventListeners = []; } static getInstance() { if (!window._iconPickerInstance) { window._iconPickerInstance = new IconPicker(); } return window._iconPickerInstance; } init() { if (this.isInitialized) return; // Check if DOM is ready if (!document.body) { console.error('DOM not ready - cannot initialize icon picker'); return; } // Create modal structure this.modal = document.createElement('div'); this.modal.className = 'icon-picker-modal fixed inset-0 bg-black bg-opacity-50 z-50 hidden'; this.modal.innerHTML = `

Vyberte ikonu

`; // Get references to elements this.container = this.modal.querySelector('.icon-picker-modal > div'); this.searchInput = this.modal.querySelector('.icon-picker-search'); this.iconList = this.modal.querySelector('.icon-picker-list'); this.closeButton = this.modal.querySelector('.icon-picker-close'); if (!this.container || !this.searchInput || !this.iconList || !this.closeButton) { console.error('Failed to initialize icon picker - missing elements'); return; } // Add to body document.body.appendChild(this.modal); // Initialize icons this.initializeIcons(); // Setup event listeners this.setupEventListeners(); this.isInitialized = true; } initializeIcons() { // Load icons from categories const categories = { 'Doprava': ['car', 'car-side', 'truck', 'bus', 'bicycle', 'motorcycle', 'plane', 'plane-departure', 'ship', 'subway', 'train', 'train-subway', 'walking', 'gas-pump', 'map-marker-alt', 'route'], 'Jídlo a nápoje': ['utensils', 'hamburger', 'pizza-slice', 'ice-cream', 'coffee', 'mug-hot', 'beer', 'wine-glass', 'wine-bottle', 'wine-glass-alt', 'wine-bottle-alt', 'apple-alt', 'bread-slice', 'cheese', 'drumstick-bite', 'egg', 'fish', 'hotdog', 'ice-cream', 'lemon', 'pepper-hot', 'shrimp', 'stroopwafel'], 'Nástroje': ['tools', 'wrench', 'screwdriver', 'hammer', 'toolbox', 'ruler', 'ruler-combined', 'ruler-horizontal', 'ruler-vertical', 'screwdriver-wrench', 'screwdriver', 'hammer', 'paint-roller', 'paint-brush', 'pencil-ruler', 'ruler', 'screwdriver', 'toolbox', 'wrench'], 'Kancelář': ['briefcase', 'folder', 'folder-open', 'file', 'file-alt', 'file-archive', 'file-audio', 'file-code', 'file-excel', 'file-image', 'file-pdf', 'file-word', 'file-powerpoint', 'file-signature', 'file-upload', 'file-download', 'file-export', 'file-import', 'file-invoice', 'file-invoice-dollar', 'file-medical', 'file-prescription'] }; // Convert to array format this.icons = Object.entries(categories).map(([category, icons]) => ({ category, icons: icons.map(icon => ({ name: icon, displayName: icon.replace(/-/g, ' '), className: `fa-${icon}` })) })); } renderIcons(searchTerm = '') { searchTerm = searchTerm.toLowerCase(); this.currentSearch = searchTerm; // Clear existing content this.iconList.innerHTML = ''; // Add icons this.icons.forEach(category => { const filteredIcons = category.icons.filter(icon => icon.name.toLowerCase().includes(searchTerm) || category.category.toLowerCase().includes(searchTerm) ); if (filteredIcons.length > 0) { // Add category header const categoryHeader = document.createElement('div'); categoryHeader.className = 'icon-category'; categoryHeader.textContent = category.category; this.iconList.appendChild(categoryHeader); // Add icons filteredIcons.forEach(icon => { const iconElement = document.createElement('div'); iconElement.className = 'icon-option'; iconElement.setAttribute('data-icon', icon.className); iconElement.title = icon.displayName; iconElement.innerHTML = ` ${icon.displayName} `; this.iconList.appendChild(iconElement); }); } }); // Add no results message if needed if (this.iconList.children.length === 0) { const noResults = document.createElement('div'); noResults.className = 'col-span-full text-center py-8 text-gray-500'; noResults.innerHTML = `

Žádné ikony nenalezeny

`; this.iconList.appendChild(noResults); } } setupEventListeners() { // Close button const closeHandler = () => this.close(); this.closeButton.addEventListener('click', closeHandler); this.eventListeners.push({ element: this.closeButton, event: 'click', handler: closeHandler }); // Search input const searchHandler = (e) => { const searchTerm = e.target.value.toLowerCase(); this.renderIcons(searchTerm); }; this.searchInput.addEventListener('input', searchHandler); this.eventListeners.push({ element: this.searchInput, event: 'input', handler: searchHandler }); // Document click - close when clicking outside const clickHandler = (e) => { if (this.isModalOpen && !this.container.contains(e.target)) { this.close(); } }; document.addEventListener('click', clickHandler); this.eventListeners.push({ element: document, event: 'click', handler: clickHandler }); // Escape key const keydownHandler = (e) => { if (e.key === 'Escape' && this.isModalOpen) { this.close(); } }; document.addEventListener('keydown', keydownHandler); this.eventListeners.push({ element: document, event: 'keydown', handler: keydownHandler }); // Icon selection const iconClickHandler = (e) => { const iconOption = e.target.closest('.icon-option'); if (iconOption && iconOption.dataset.icon) { this.selectIcon(iconOption.dataset.icon); } }; this.iconList.addEventListener('click', iconClickHandler); this.eventListeners.push({ element: this.iconList, event: 'click', handler: iconClickHandler }); } cleanupEventListeners() { this.eventListeners.forEach(listener => { const { element, event, handler } = listener; element.removeEventListener(event, handler); }); this.eventListeners = []; } close() { if (!this.isModalOpen) return; this.isModalOpen = false; this.modal.style.display = 'none'; document.body.style.overflow = ''; // Clear search this.searchInput.value = ''; this.renderIcons(''); // Cleanup event listeners this.cleanupEventListeners(); } open() { if (!this.isInitialized) this.init(); if (this.isModalOpen) return; this.isModalOpen = true; this.modal.style.display = 'flex'; document.body.style.overflow = 'hidden'; // Focus search input requestAnimationFrame(() => { this.searchInput.focus(); this.searchInput.value = ''; this.renderIcons(''); }); } close() { if (!this.isModalOpen) return; this.isModalOpen = false; this.modal.style.display = 'none'; document.body.style.overflow = ''; // Clear search this.searchInput.value = ''; this.renderIcons(''); } selectIcon(iconClass) { // Get target elements const selectedIcon = document.getElementById('selectedIcon'); const customIconPreview = document.getElementById('customIconPreview'); const iconPreview = document.getElementById('iconPreview'); const appIcon = document.getElementById('appIcon'); const customIconInput = document.getElementById('customIconInput'); // Update selected icon if (selectedIcon) { selectedIcon.className = `fas ${iconClass} text-2xl text-gray-400`; selectedIcon.classList.remove('hidden'); } // Hide custom icon preview if (customIconPreview) { customIconPreview.src = ''; customIconPreview.classList.add('hidden'); } // Update app icon if (appIcon) { appIcon.value = iconClass; appIcon.dispatchEvent(new Event('change')); } // Reset custom icon input if (customIconInput) { customIconInput.value = ''; customIconInput.dispatchEvent(new Event('change')); } // Update preview if (iconPreview) { const colors = ['blue', 'green', 'red', 'yellow', 'indigo', 'purple', 'pink', 'gray']; const randomColor = colors[Math.floor(Math.random() * colors.length)]; iconPreview.className = `mt-2 flex items-center justify-center w-16 h-16 bg-${randomColor}-100 rounded-md overflow-hidden`; iconPreview.innerHTML = ``; } // Close modal this.close(); } static getInstance() { if (!window.iconPickerInstance) { window.iconPickerInstance = new IconPicker(); } return window.iconPickerInstance; } }