mirror of
https://github.com/Dvorinka/PPve.git
synced 2026-06-04 04:22:58 +00:00
nefunguje mi tam ten icon picker
This commit is contained in:
+98
-118
@@ -1214,10 +1214,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Include icon picker component -->
|
<!-- Icon Picker Modal -->
|
||||||
<script src="components/icon-picker.js" defer></script>
|
<div id="iconPickerModal" class="fixed inset-0 z-50 hidden">
|
||||||
|
<div id="iconPickerContainer" class="bg-white rounded-xl shadow-2xl overflow-hidden flex flex-col">
|
||||||
|
<div id="iconPickerHeader" class="bg-white border-b border-gray-200 px-6 py-4">
|
||||||
|
<h3 class="text-xl font-semibold text-gray-900">Vyberte ikonu</h3>
|
||||||
|
<button id="closeIconPicker" class="text-gray-400 hover:text-gray-500">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="iconSearchContainer" class="px-6 py-4 border-b border-gray-200">
|
||||||
|
<input type="text" id="iconSearch" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Hledat ikony..." autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div id="iconListContainer" class="flex-1 overflow-y-auto">
|
||||||
|
<div id="iconList" class="p-6">
|
||||||
|
<!-- Icons will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Get token and check authentication
|
// Get token and check authentication
|
||||||
@@ -2202,7 +2217,37 @@ function selectIcon(iconClass) {
|
|||||||
// Update the visible input with a friendly name
|
// Update the visible input with a friendly name
|
||||||
const iconName = iconClass.replace('fa-', '').replace(/-/g, ' ');
|
const iconName = iconClass.replace('fa-', '').replace(/-/g, ' ');
|
||||||
iconInput.value = iconName.split(' ')
|
iconInput.value = iconName.split(' ')
|
||||||
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
// Highlight the selected icon
|
||||||
|
const selectedOption = document.querySelector(`.icon-option[data-icon="${iconClass}"]`);
|
||||||
|
if (selectedOption) {
|
||||||
|
// Remove active class from all icons
|
||||||
|
document.querySelectorAll('.icon-option').forEach(option => {
|
||||||
|
option.classList.remove('active');
|
||||||
|
});
|
||||||
|
// Add active class to selected icon
|
||||||
|
selectedOption.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the modal
|
||||||
|
isModalOpen = false;
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
iconPickerModal.style.display = 'none';
|
||||||
|
|
||||||
|
// Update the preview
|
||||||
|
if (iconPreview) {
|
||||||
|
iconPreview.className = 'w-12 h-12 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center mr-3';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Reset icon selection
|
// Reset icon selection
|
||||||
|
const appIcon = document.getElementById('appIcon');
|
||||||
|
const customIconInput = document.getElementById('customIconInput');
|
||||||
|
const customIconPreview = document.getElementById('customIconPreview');
|
||||||
|
const selectedIcon = document.getElementById('selectedIcon');
|
||||||
|
|
||||||
if (appIcon) appIcon.value = '';
|
if (appIcon) appIcon.value = '';
|
||||||
if (customIconInput) customIconInput.value = '';
|
if (customIconInput) customIconInput.value = '';
|
||||||
if (customIconPreview) {
|
if (customIconPreview) {
|
||||||
@@ -2213,17 +2258,23 @@ function selectIcon(iconClass) {
|
|||||||
selectedIcon.className = 'fas fa-cube text-2xl text-gray-400';
|
selectedIcon.className = 'fas fa-cube text-2xl text-gray-400';
|
||||||
selectedIcon.classList.remove('hidden');
|
selectedIcon.classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset color picker to default
|
// Reset color picker to default
|
||||||
const colorInput = document.getElementById('appColor');
|
const colorInput = document.getElementById('appColor');
|
||||||
const colorText = document.getElementById('appColorText');
|
const colorText = document.getElementById('appColorText');
|
||||||
if (colorInput) colorInput.value = '#4a6cf7';
|
if (colorInput) colorInput.value = '#4a6cf7';
|
||||||
if (colorText) colorText.value = '#4a6cf7';
|
if (colorText) colorText.value = '#4a6cf7';
|
||||||
|
|
||||||
// Reset icon picker selection
|
// Reset icon picker selection
|
||||||
const selectedIcons = document.querySelectorAll('.icon-option.selected');
|
const selectedIcons = document.querySelectorAll('.icon-option.selected');
|
||||||
selectedIcons.forEach(icon => icon.classList.remove('selected'));
|
selectedIcons.forEach(icon => icon.classList.remove('selected'));
|
||||||
|
|
||||||
|
// Show the modal
|
||||||
|
const modal = document.getElementById('appModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
// Set focus to the first input field
|
// Set focus to the first input field
|
||||||
const firstInput = form?.querySelector('input, textarea, select');
|
const firstInput = form?.querySelector('input, textarea, select');
|
||||||
if (firstInput) firstInput.focus();
|
if (firstInput) firstInput.focus();
|
||||||
@@ -2298,9 +2349,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
// Initialize file input for custom icon upload
|
// Initialize file input for custom icon upload
|
||||||
setupFileInput();
|
setupFileInput();
|
||||||
|
|
||||||
// Initialize icon picker
|
|
||||||
window.iconPicker = IconPicker.getInstance();
|
|
||||||
|
|
||||||
// Add click handler for custom icon button
|
// Add click handler for custom icon button
|
||||||
const customIconBtn = document.getElementById('customIconBtn');
|
const customIconBtn = document.getElementById('customIconBtn');
|
||||||
const customIconInput = document.getElementById('customIconInput');
|
const customIconInput = document.getElementById('customIconInput');
|
||||||
@@ -2311,18 +2359,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add click handler for icon picker
|
|
||||||
const appIcon = document.getElementById('appIcon');
|
|
||||||
if (appIcon) {
|
|
||||||
appIcon.addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
if (window.iconPicker) {
|
|
||||||
window.iconPicker.open();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const appColor = document.getElementById('appColor');
|
const appColor = document.getElementById('appColor');
|
||||||
const appColorText = document.getElementById('appColorText');
|
const appColorText = document.getElementById('appColorText');
|
||||||
|
|
||||||
@@ -2409,84 +2445,78 @@ function initIconPicker() {
|
|||||||
|
|
||||||
let isModalOpen = false;
|
let isModalOpen = false;
|
||||||
|
|
||||||
// Improved modal toggle with cleanup
|
// Simple show/hide functions
|
||||||
const toggleModal = () => {
|
const toggleModal = () => {
|
||||||
isModalOpen = !isModalOpen;
|
isModalOpen = !isModalOpen;
|
||||||
document.body.style.overflow = isModalOpen ? 'hidden' : '';
|
document.body.style.overflow = isModalOpen ? 'hidden' : '';
|
||||||
iconPickerModal.style.display = isModalOpen ? 'flex' : 'none';
|
iconPickerModal.style.display = isModalOpen ? 'block' : 'none';
|
||||||
|
|
||||||
if (isModalOpen) {
|
if (isModalOpen) {
|
||||||
// Focus search input only after modal is visible
|
// Focus search input only after modal is visible
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (iconSearch) {
|
if (iconSearch) {
|
||||||
iconSearch.value = '';
|
|
||||||
renderIcons('');
|
|
||||||
iconSearch.focus();
|
iconSearch.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Toggle modal on icon input click
|
// Toggle modal on icon input click
|
||||||
iconInput.addEventListener('click', (e) => {
|
iconInput.addEventListener('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
toggleModal();
|
toggleModal();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close modal handlers
|
// Close modal handlers
|
||||||
if (closeButton) {
|
if (closeButton) {
|
||||||
closeButton.addEventListener('click', toggleModal);
|
closeButton.addEventListener('click', () => {
|
||||||
|
isModalOpen = false;
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
iconPickerModal.style.display = 'none';
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close when clicking outside the modal content
|
// Close when clicking outside the modal content
|
||||||
iconPickerModal.addEventListener('click', (e) => {
|
iconPickerModal.addEventListener('click', (e) => {
|
||||||
if (e.target === iconPickerModal && isModalOpen) {
|
if (e.target === iconPickerModal) {
|
||||||
toggleModal();
|
isModalOpen = false;
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
iconPickerModal.style.display = 'none';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle icon selection
|
// Handle icon selection
|
||||||
if (iconList) {
|
if (iconList) {
|
||||||
iconList.addEventListener('click', (e) => {
|
iconList.addEventListener('click', (e) => {
|
||||||
const iconOption = e.target.closest('.icon-option');
|
const iconOption = e.target.closest('.icon-option');
|
||||||
if (iconOption && isModalOpen) {
|
if (iconOption) {
|
||||||
const iconClass = iconOption.getAttribute('data-icon');
|
const iconClass = iconOption.getAttribute('data-icon');
|
||||||
if (iconClass) {
|
selectIcon(iconClass);
|
||||||
selectIcon(iconClass);
|
|
||||||
toggleModal(); // Close modal after selection
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle search with improved debounce
|
// Handle search
|
||||||
if (iconSearch) {
|
if (iconSearch) {
|
||||||
let searchTimeout;
|
iconSearch.addEventListener('input', () => {
|
||||||
iconSearch.addEventListener('input', (e) => {
|
// Debounce search to prevent excessive re-renders
|
||||||
const searchTerm = e.target.value.toLowerCase();
|
clearTimeout(iconSearch.dataset.searchTimeout);
|
||||||
if (searchTimeout) clearTimeout(searchTimeout);
|
iconSearch.dataset.searchTimeout = setTimeout(() => {
|
||||||
searchTimeout = setTimeout(() => {
|
renderIcons(iconSearch.value.toLowerCase());
|
||||||
renderIcons(searchTerm);
|
|
||||||
}, 200);
|
}, 200);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial render
|
// Initial render
|
||||||
renderIcons('');
|
renderIcons('');
|
||||||
|
|
||||||
// Close on Escape
|
// Close on Escape
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Escape' && isModalOpen) {
|
if (e.key === 'Escape' && isModalOpen) {
|
||||||
toggleModal();
|
isModalOpen = false;
|
||||||
}
|
document.body.style.overflow = '';
|
||||||
});
|
iconPickerModal.style.display = 'none';
|
||||||
|
|
||||||
// Prevent scrolling when modal is open
|
|
||||||
document.addEventListener('scroll', (e) => {
|
|
||||||
if (isModalOpen) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2496,11 +2526,6 @@ function renderIcons(searchTerm) {
|
|||||||
const iconList = document.getElementById('iconList');
|
const iconList = document.getElementById('iconList');
|
||||||
if (!iconList) return;
|
if (!iconList) return;
|
||||||
|
|
||||||
// Clear existing event listeners
|
|
||||||
iconList.querySelectorAll('.icon-option').forEach(option => {
|
|
||||||
option.removeEventListener('click', handleIconClick);
|
|
||||||
});
|
|
||||||
|
|
||||||
let iconsHtml = '';
|
let iconsHtml = '';
|
||||||
let hasVisibleIcons = false;
|
let hasVisibleIcons = false;
|
||||||
|
|
||||||
@@ -2523,8 +2548,8 @@ function renderIcons(searchTerm) {
|
|||||||
<div class="icon-option"
|
<div class="icon-option"
|
||||||
data-icon="${iconClass}"
|
data-icon="${iconClass}"
|
||||||
title="${displayName}">
|
title="${displayName}">
|
||||||
<i class="fas ${iconClass} text-xl hover:text-blue-500 transition-colors duration-200"></i>
|
<i class="fas ${iconClass}"></i>
|
||||||
<span class="icon-name text-sm text-gray-600">${displayName}</span>
|
<span class="icon-name">${displayName}</span>
|
||||||
</div>`;
|
</div>`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2540,24 +2565,15 @@ function renderIcons(searchTerm) {
|
|||||||
|
|
||||||
iconList.innerHTML = iconsHtml;
|
iconList.innerHTML = iconsHtml;
|
||||||
|
|
||||||
// Add click handlers to new icon options
|
// Add click handlers to icon options
|
||||||
iconList.querySelectorAll('.icon-option').forEach(option => {
|
document.querySelectorAll('.icon-option').forEach(option => {
|
||||||
option.addEventListener('click', handleIconClick);
|
option.addEventListener('click', function() {
|
||||||
|
const iconClass = this.getAttribute('data-icon');
|
||||||
|
selectIcon(iconClass);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle icon click
|
|
||||||
function handleIconClick(e) {
|
|
||||||
const iconClass = this.getAttribute('data-icon');
|
|
||||||
if (iconClass) {
|
|
||||||
selectIcon(iconClass);
|
|
||||||
const modal = document.getElementById('iconPickerModal');
|
|
||||||
if (modal) {
|
|
||||||
modal.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter icons based on search input (now handled in renderIcons)
|
// Filter icons based on search input (now handled in renderIcons)
|
||||||
|
|
||||||
// Select an icon
|
// Select an icon
|
||||||
@@ -2578,24 +2594,17 @@ function selectIcon(iconClass) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the app icon value to the selected icon class
|
// Set the app icon value to the selected icon class
|
||||||
if (appIcon) {
|
if (appIcon) appIcon.value = iconClass;
|
||||||
appIcon.value = iconClass;
|
|
||||||
appIcon.dispatchEvent(new Event('change'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset file input
|
// Reset file input
|
||||||
const customIconInput = document.getElementById('customIconInput');
|
const customIconInput = document.getElementById('customIconInput');
|
||||||
if (customIconInput) {
|
if (customIconInput) customIconInput.value = '';
|
||||||
customIconInput.value = '';
|
|
||||||
customIconInput.dispatchEvent(new Event('change'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update preview with random color
|
// Update preview with random color
|
||||||
const colors = ['blue', 'green', 'red', 'yellow', 'indigo', 'purple', 'pink', 'gray'];
|
const colors = ['blue', 'green', 'red', 'yellow', 'indigo', 'purple', 'pink', 'gray'];
|
||||||
const randomColor = colors[Math.floor(Math.random() * colors.length)];
|
const randomColor = colors[Math.floor(Math.random() * colors.length)];
|
||||||
if (iconPreview) {
|
if (iconPreview) {
|
||||||
iconPreview.className = `mt-2 flex items-center justify-center w-16 h-16 bg-${randomColor}-100 rounded-md overflow-hidden`;
|
iconPreview.className = `mt-2 flex items-center justify-center w-16 h-16 bg-${randomColor}-100 rounded-md overflow-hidden`;
|
||||||
iconPreview.innerHTML = `<i class="fas ${iconClass} text-2xl text-gray-400"></i>`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4110,39 +4119,10 @@ function applyTemplate(templateId) {
|
|||||||
if (bannerPreview) {
|
if (bannerPreview) {
|
||||||
bannerPreview.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
bannerPreview.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
}
|
}
|
||||||
}}}
|
}
|
||||||
|
|
||||||
// Load apps when the page loads
|
// Load apps when the page loads
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Initialize icon picker
|
|
||||||
window.iconPicker = IconPicker.getInstance();
|
|
||||||
|
|
||||||
// Initialize file input for custom icon upload
|
|
||||||
setupFileInput();
|
|
||||||
|
|
||||||
// Add click handler for custom icon button
|
|
||||||
const customIconBtn = document.getElementById('customIconBtn');
|
|
||||||
const customIconInput = document.getElementById('customIconInput');
|
|
||||||
if (customIconBtn && customIconInput) {
|
|
||||||
customIconBtn.addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
customIconInput.click();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add click handler for icon picker
|
|
||||||
const appIcon = document.getElementById('appIcon');
|
|
||||||
if (appIcon) {
|
|
||||||
appIcon.addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
if (window.iconPicker) {
|
|
||||||
window.iconPicker.open();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load apps
|
|
||||||
loadApps();
|
loadApps();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,301 +0,0 @@
|
|||||||
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 = `
|
|
||||||
<div class="fixed inset-0 flex items-center justify-center p-4">
|
|
||||||
<div class="bg-white rounded-xl shadow-2xl max-w-4xl w-full max-h-[90vh] overflow-hidden">
|
|
||||||
<div class="flex flex-col h-full">
|
|
||||||
<div class="border-b border-gray-200">
|
|
||||||
<div class="flex justify-between items-center p-6">
|
|
||||||
<h3 class="text-xl font-semibold text-gray-900">Vyberte ikonu</h3>
|
|
||||||
<button class="icon-picker-close text-gray-400 hover:text-gray-500 p-2">
|
|
||||||
<i class="fas fa-times"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="border-b border-gray-200">
|
|
||||||
<div class="p-6">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 icon-picker-search"
|
|
||||||
placeholder="Hledat ikony..."
|
|
||||||
autocomplete="off"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1 overflow-y-auto">
|
|
||||||
<div class="p-6 grid grid-cols-6 gap-4 icon-picker-list">
|
|
||||||
<!-- Icons will be populated here -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// 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 = `
|
|
||||||
<i class="fas ${icon.className} text-xl hover:text-blue-500 transition-colors duration-200"></i>
|
|
||||||
<span class="icon-name text-sm text-gray-600">${icon.displayName}</span>
|
|
||||||
`;
|
|
||||||
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 = `
|
|
||||||
<i class="fas fa-search mb-2 text-2xl"></i>
|
|
||||||
<p>Žádné ikony nenalezeny</p>
|
|
||||||
`;
|
|
||||||
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 = `<i class="fas ${iconClass} text-2xl text-gray-400"></i>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close modal
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static getInstance() {
|
|
||||||
if (!window.iconPickerInstance) {
|
|
||||||
window.iconPickerInstance = new IconPicker();
|
|
||||||
}
|
|
||||||
return window.iconPickerInstance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user