mirror of
https://github.com/Dvorinka/PPve.git
synced 2026-06-04 04:22:58 +00:00
test
This commit is contained in:
+253
-126
@@ -194,18 +194,94 @@
|
||||
border-color: var(--secondary-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
/* Icon Picker Styles - Enhanced */
|
||||
#iconDropdown {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
/* Full Screen Icon Picker Modal */
|
||||
#iconPickerModal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
z-index: 9999;
|
||||
padding: 2rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#iconPickerModal.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#iconPickerContainer {
|
||||
max-width: 1200px;
|
||||
margin: 2rem auto;
|
||||
background: white;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
z-index: 1000;
|
||||
margin-top: 0.5rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
overflow: hidden;
|
||||
max-height: calc(100vh - 4rem);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#iconPickerHeader {
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #f9fafb;
|
||||
}
|
||||
|
||||
#iconPickerHeader h3 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
#closeIconPicker {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: #6b7280;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
#closeIconPicker:hover {
|
||||
background: #e5e7eb;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
#iconSearchContainer {
|
||||
padding: 1.5rem;
|
||||
background: white;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
#iconSearch {
|
||||
width: 100%;
|
||||
padding: 1rem 1.25rem;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 0.75rem;
|
||||
font-size: 1.125rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
#iconSearch:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
#iconListContainer {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 1.5rem;
|
||||
background: white;
|
||||
}
|
||||
|
||||
#iconSearch {
|
||||
@@ -229,13 +305,8 @@
|
||||
|
||||
#iconList {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, minmax(0, 1fr));
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #cbd5e0 #f1f5f9;
|
||||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
#iconList::-webkit-scrollbar {
|
||||
@@ -274,9 +345,9 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.75rem 0.5rem;
|
||||
padding: 1.5rem 0.5rem;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 0.5rem;
|
||||
border-radius: 0.75rem;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
@@ -285,37 +356,38 @@
|
||||
}
|
||||
|
||||
.icon-option i {
|
||||
font-size: 1.5rem;
|
||||
font-size: 2rem;
|
||||
color: #4b5563;
|
||||
margin-bottom: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
transition: all 0.2s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.icon-option .icon-name {
|
||||
font-size: 0.625rem;
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
opacity: 0;
|
||||
transform: translateY(5px);
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
transition: all 0.2s ease;
|
||||
pointer-events: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.icon-option:hover {
|
||||
background-color: #f3f4f6;
|
||||
border-color: #d1d5db;
|
||||
background-color: #f8fafc;
|
||||
border-color: var(--primary-color);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.icon-option:hover i {
|
||||
color: var(--primary-color);
|
||||
transform: scale(1.1);
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.icon-option:hover .icon-name {
|
||||
@@ -326,6 +398,8 @@
|
||||
.icon-option.active {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-hover);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.icon-option.active i,
|
||||
@@ -334,23 +408,44 @@
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: 1200px) {
|
||||
#iconList {
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#iconPickerContainer {
|
||||
margin: 0.5rem;
|
||||
max-height: calc(100vh - 1rem);
|
||||
}
|
||||
|
||||
#iconList {
|
||||
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.icon-option i {
|
||||
font-size: 1.25rem;
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
#iconList {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.icon-option {
|
||||
padding: 1rem 0.25rem;
|
||||
}
|
||||
|
||||
.icon-option i {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.icon-option .icon-name {
|
||||
font-size: 0.6875rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -966,16 +1061,9 @@
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Ikona</label>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="relative flex-1">
|
||||
<input type="text" id="appIcon" name="appIcon" class="form-control w-full" placeholder="Vyberte ikonu">
|
||||
<div id="iconDropdown" class="absolute z-10 w-full mt-1 bg-white border border-gray-200 rounded-md shadow-lg hidden">
|
||||
<div class="p-2 border-b border-gray-200">
|
||||
<input type="text" id="iconSearch" class="form-control text-sm w-full" placeholder="Hledat ikony...">
|
||||
</div>
|
||||
<div id="iconList" class="max-h-48 overflow-y-auto p-2 grid grid-cols-6 gap-2">
|
||||
<!-- Icons will be populated by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" id="appIcon" name="appIcon" class="form-control w-full cursor-pointer" placeholder="Vyberte ikonu" readonly>
|
||||
<input type="hidden" id="appIconClass">
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" id="customIconBtn" class="btn btn-secondary whitespace-nowrap">
|
||||
<i class="fas fa-upload mr-1"></i> Vlastní
|
||||
@@ -1113,6 +1201,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Icon Picker Modal -->
|
||||
<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>
|
||||
// Get token and check authentication
|
||||
const token = localStorage.getItem('token');
|
||||
@@ -1466,12 +1574,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
dropArea.addEventListener(eventName, unhighlight, false);
|
||||
});
|
||||
|
||||
function highlight(e) {
|
||||
dropArea.classList.add('highlight');
|
||||
function highlight() {
|
||||
dropArea.classList.add('dragover');
|
||||
}
|
||||
|
||||
function unhighlight(e) {
|
||||
dropArea.classList.remove('highlight');
|
||||
function unhighlight() {
|
||||
dropArea.classList.remove('dragover');
|
||||
}
|
||||
|
||||
// Handle drop
|
||||
@@ -2069,59 +2177,54 @@ function openAddAppModal() {
|
||||
document.getElementById('appModalTitle').textContent = 'Přidat aplikaci';
|
||||
|
||||
// Select an icon
|
||||
function selectIcon(iconClass) {
|
||||
const iconInput = document.getElementById('appIcon');
|
||||
const selectedIcon = document.getElementById('selectedIcon');
|
||||
const iconPreview = document.getElementById('iconPreview');
|
||||
const customIconPreview = document.getElementById('customIconPreview');
|
||||
const iconClassInput = document.getElementById('appIconClass');
|
||||
|
||||
if (iconInput && selectedIcon && iconClassInput) {
|
||||
// Hide any custom icon preview
|
||||
if (customIconPreview) {
|
||||
customIconPreview.classList.add('hidden');
|
||||
customIconPreview.src = '';
|
||||
}
|
||||
|
||||
// Show the Font Awesome icon
|
||||
if (selectedIcon) {
|
||||
selectedIcon.className = `fas ${iconClass} text-2xl text-blue-600`;
|
||||
selectedIcon.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Update the hidden input with the icon class
|
||||
iconClassInput.value = iconClass;
|
||||
|
||||
// Update the visible input with a friendly name
|
||||
const iconName = iconClass.replace('fa-', '').replace(/-/g, ' ');
|
||||
iconInput.value = iconName.split(' ')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
|
||||
// Scroll the selected icon into view in the dropdown
|
||||
const selectedOption = document.querySelector(`.icon-option[data-icon="${iconClass}"]`);
|
||||
if (selectedOption) {
|
||||
selectedOption.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
|
||||
// Highlight the selected icon
|
||||
document.querySelectorAll('.icon-option').forEach(option => {
|
||||
option.classList.toggle('active', option.getAttribute('data-icon') === iconClass);
|
||||
});
|
||||
}
|
||||
|
||||
// Hide the dropdown after a short delay
|
||||
setTimeout(() => {
|
||||
const dropdown = document.getElementById('iconDropdown');
|
||||
if (dropdown) {
|
||||
dropdown.classList.add('hidden');
|
||||
}
|
||||
}, 200);
|
||||
|
||||
// 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';
|
||||
}
|
||||
function selectIcon(iconClass) {
|
||||
const iconInput = document.getElementById('appIcon');
|
||||
const selectedIcon = document.getElementById('selectedIcon');
|
||||
const iconPreview = document.getElementById('iconPreview');
|
||||
const customIconPreview = document.getElementById('customIconPreview');
|
||||
const iconClassInput = document.getElementById('appIconClass');
|
||||
const iconPickerModal = document.getElementById('iconPickerModal');
|
||||
|
||||
if (iconInput && selectedIcon && iconClassInput) {
|
||||
// Hide any custom icon preview
|
||||
if (customIconPreview) {
|
||||
customIconPreview.classList.add('hidden');
|
||||
customIconPreview.src = '';
|
||||
}
|
||||
|
||||
// Show the Font Awesome icon
|
||||
if (selectedIcon) {
|
||||
selectedIcon.className = `fas ${iconClass} text-2xl text-blue-600`;
|
||||
selectedIcon.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Update the hidden input with the icon class
|
||||
iconClassInput.value = iconClass;
|
||||
|
||||
// Update the visible input with a friendly name
|
||||
const iconName = iconClass.replace('fa-', '').replace(/-/g, ' ');
|
||||
iconInput.value = iconName.split(' ')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
|
||||
// Highlight the selected icon in the modal
|
||||
document.querySelectorAll('.icon-option').forEach(option => {
|
||||
option.classList.toggle('active', option.getAttribute('data-icon') === iconClass);
|
||||
});
|
||||
|
||||
// Close the modal after a short delay
|
||||
setTimeout(() => {
|
||||
if (iconPickerModal) {
|
||||
iconPickerModal.classList.remove('show');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
}, 200);
|
||||
|
||||
// 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
|
||||
@@ -2317,39 +2420,63 @@ const iconCategories = {
|
||||
|
||||
// Initialize icon picker with enhanced functionality
|
||||
function initIconPicker() {
|
||||
const iconList = document.getElementById('iconList');
|
||||
if (!iconList) return;
|
||||
|
||||
renderIcons('');
|
||||
|
||||
// Add event listener for icon search
|
||||
const iconInput = document.getElementById('appIcon');
|
||||
const iconPickerModal = document.getElementById('iconPickerModal');
|
||||
const closeButton = document.getElementById('closeIconPicker');
|
||||
const iconSearch = document.getElementById('iconSearch');
|
||||
|
||||
if (!iconInput || !iconPickerModal) return;
|
||||
|
||||
// Open modal when clicking the icon input
|
||||
iconInput.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
iconPickerModal.classList.add('show');
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
// Focus search input after a short delay to ensure modal is visible
|
||||
setTimeout(() => {
|
||||
if (iconSearch) {
|
||||
iconSearch.focus();
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Close modal when clicking the close button
|
||||
if (closeButton) {
|
||||
closeButton.addEventListener('click', function() {
|
||||
iconPickerModal.classList.remove('show');
|
||||
document.body.style.overflow = '';
|
||||
});
|
||||
}
|
||||
|
||||
// Close modal when clicking on the overlay (outside the modal)
|
||||
iconPickerModal.addEventListener('click', function(e) {
|
||||
if (e.target === iconPickerModal) {
|
||||
iconPickerModal.classList.remove('show');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Handle search functionality
|
||||
if (iconSearch) {
|
||||
iconSearch.addEventListener('input', function() {
|
||||
renderIcons(this.value.toLowerCase());
|
||||
});
|
||||
|
||||
// Focus the search input when dropdown is shown
|
||||
const iconInput = document.getElementById('appIcon');
|
||||
if (iconInput) {
|
||||
iconInput.addEventListener('focus', function() {
|
||||
setTimeout(() => {
|
||||
iconSearch.focus();
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
// Prevent click events on search input from closing the modal
|
||||
iconSearch.addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', function(event) {
|
||||
const dropdown = document.getElementById('iconDropdown');
|
||||
const iconInput = document.getElementById('appIcon');
|
||||
const isClickInside = dropdown?.contains(event.target) ||
|
||||
event.target === iconInput ||
|
||||
event.target === document.getElementById('iconSearch');
|
||||
|
||||
if (!isClickInside && dropdown && !dropdown.classList.contains('hidden')) {
|
||||
dropdown.classList.add('hidden');
|
||||
// Initial render of icons
|
||||
renderIcons('');
|
||||
|
||||
// Close modal when pressing Escape key
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape' && iconPickerModal.classList.contains('show')) {
|
||||
iconPickerModal.classList.remove('show');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user