mirror of
https://github.com/Dvorinka/PPve.git
synced 2026-06-04 20:42:59 +00:00
test
This commit is contained in:
+209
-82
@@ -194,18 +194,94 @@
|
|||||||
border-color: var(--secondary-color);
|
border-color: var(--secondary-color);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
/* Icon Picker Styles - Enhanced */
|
/* Full Screen Icon Picker Modal */
|
||||||
#iconDropdown {
|
#iconPickerModal {
|
||||||
position: absolute;
|
display: none;
|
||||||
width: 100%;
|
position: fixed;
|
||||||
max-width: 500px;
|
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;
|
background: white;
|
||||||
border: 1px solid #e5e7eb;
|
border-radius: 1rem;
|
||||||
border-radius: 0.5rem;
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
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;
|
|
||||||
overflow: hidden;
|
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 {
|
#iconSearch {
|
||||||
@@ -229,13 +305,8 @@
|
|||||||
|
|
||||||
#iconList {
|
#iconList {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(8, minmax(0, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||||||
gap: 0.75rem;
|
gap: 1rem;
|
||||||
padding: 1rem;
|
|
||||||
max-height: 400px;
|
|
||||||
overflow-y: auto;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: #cbd5e0 #f1f5f9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#iconList::-webkit-scrollbar {
|
#iconList::-webkit-scrollbar {
|
||||||
@@ -274,9 +345,9 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0.75rem 0.5rem;
|
padding: 1.5rem 0.5rem;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid #e5e7eb;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.75rem;
|
||||||
background: white;
|
background: white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
@@ -285,37 +356,38 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon-option i {
|
.icon-option i {
|
||||||
font-size: 1.5rem;
|
font-size: 2rem;
|
||||||
color: #4b5563;
|
color: #4b5563;
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.5rem;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-option .icon-name {
|
.icon-option .icon-name {
|
||||||
font-size: 0.625rem;
|
font-size: 0.75rem;
|
||||||
color: #6b7280;
|
color: #6b7280;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
opacity: 0;
|
opacity: 1;
|
||||||
transform: translateY(5px);
|
transform: translateY(0);
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-option:hover {
|
.icon-option:hover {
|
||||||
background-color: #f3f4f6;
|
background-color: #f8fafc;
|
||||||
border-color: #d1d5db;
|
border-color: var(--primary-color);
|
||||||
transform: translateY(-2px);
|
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 {
|
.icon-option:hover i {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
transform: scale(1.1);
|
transform: scale(1.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-option:hover .icon-name {
|
.icon-option:hover .icon-name {
|
||||||
@@ -326,6 +398,8 @@
|
|||||||
.icon-option.active {
|
.icon-option.active {
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
border-color: var(--primary-hover);
|
border-color: var(--primary-hover);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-option.active i,
|
.icon-option.active i,
|
||||||
@@ -334,23 +408,44 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive adjustments */
|
/* Responsive adjustments */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 1200px) {
|
||||||
#iconList {
|
#iconList {
|
||||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
|
||||||
gap: 0.5rem;
|
}
|
||||||
padding: 0.75rem;
|
}
|
||||||
|
|
||||||
|
@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 {
|
.icon-option i {
|
||||||
font-size: 1.25rem;
|
font-size: 1.75rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
#iconList {
|
#iconList {
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
|
||||||
gap: 0.5rem;
|
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,15 +1061,8 @@
|
|||||||
<label class="block text-sm font-medium text-gray-700 mb-2">Ikona</label>
|
<label class="block text-sm font-medium text-gray-700 mb-2">Ikona</label>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<div class="relative flex-1">
|
<div class="relative flex-1">
|
||||||
<input type="text" id="appIcon" name="appIcon" class="form-control w-full" placeholder="Vyberte ikonu">
|
<input type="text" id="appIcon" name="appIcon" class="form-control w-full cursor-pointer" placeholder="Vyberte ikonu" readonly>
|
||||||
<div id="iconDropdown" class="absolute z-10 w-full mt-1 bg-white border border-gray-200 rounded-md shadow-lg hidden">
|
<input type="hidden" id="appIconClass">
|
||||||
<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>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button type="button" id="customIconBtn" class="btn btn-secondary whitespace-nowrap">
|
<button type="button" id="customIconBtn" class="btn btn-secondary whitespace-nowrap">
|
||||||
@@ -1113,6 +1201,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</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>
|
<script>
|
||||||
// Get token and check authentication
|
// Get token and check authentication
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
@@ -1466,12 +1574,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
dropArea.addEventListener(eventName, unhighlight, false);
|
dropArea.addEventListener(eventName, unhighlight, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
function highlight(e) {
|
function highlight() {
|
||||||
dropArea.classList.add('highlight');
|
dropArea.classList.add('dragover');
|
||||||
}
|
}
|
||||||
|
|
||||||
function unhighlight(e) {
|
function unhighlight() {
|
||||||
dropArea.classList.remove('highlight');
|
dropArea.classList.remove('dragover');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle drop
|
// Handle drop
|
||||||
@@ -2075,6 +2183,7 @@ function openAddAppModal() {
|
|||||||
const iconPreview = document.getElementById('iconPreview');
|
const iconPreview = document.getElementById('iconPreview');
|
||||||
const customIconPreview = document.getElementById('customIconPreview');
|
const customIconPreview = document.getElementById('customIconPreview');
|
||||||
const iconClassInput = document.getElementById('appIconClass');
|
const iconClassInput = document.getElementById('appIconClass');
|
||||||
|
const iconPickerModal = document.getElementById('iconPickerModal');
|
||||||
|
|
||||||
if (iconInput && selectedIcon && iconClassInput) {
|
if (iconInput && selectedIcon && iconClassInput) {
|
||||||
// Hide any custom icon preview
|
// Hide any custom icon preview
|
||||||
@@ -2098,22 +2207,16 @@ function openAddAppModal() {
|
|||||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
.join(' ');
|
.join(' ');
|
||||||
|
|
||||||
// Scroll the selected icon into view in the dropdown
|
// Highlight the selected icon in the modal
|
||||||
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 => {
|
document.querySelectorAll('.icon-option').forEach(option => {
|
||||||
option.classList.toggle('active', option.getAttribute('data-icon') === iconClass);
|
option.classList.toggle('active', option.getAttribute('data-icon') === iconClass);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Hide the dropdown after a short delay
|
// Close the modal after a short delay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const dropdown = document.getElementById('iconDropdown');
|
if (iconPickerModal) {
|
||||||
if (dropdown) {
|
iconPickerModal.classList.remove('show');
|
||||||
dropdown.classList.add('hidden');
|
document.body.style.overflow = '';
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
@@ -2317,39 +2420,63 @@ const iconCategories = {
|
|||||||
|
|
||||||
// Initialize icon picker with enhanced functionality
|
// Initialize icon picker with enhanced functionality
|
||||||
function initIconPicker() {
|
function initIconPicker() {
|
||||||
const iconList = document.getElementById('iconList');
|
const iconInput = document.getElementById('appIcon');
|
||||||
if (!iconList) return;
|
const iconPickerModal = document.getElementById('iconPickerModal');
|
||||||
|
const closeButton = document.getElementById('closeIconPicker');
|
||||||
renderIcons('');
|
|
||||||
|
|
||||||
// Add event listener for icon search
|
|
||||||
const iconSearch = document.getElementById('iconSearch');
|
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) {
|
if (iconSearch) {
|
||||||
iconSearch.addEventListener('input', function() {
|
iconSearch.addEventListener('input', function() {
|
||||||
renderIcons(this.value.toLowerCase());
|
renderIcons(this.value.toLowerCase());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Focus the search input when dropdown is shown
|
// Prevent click events on search input from closing the modal
|
||||||
const iconInput = document.getElementById('appIcon');
|
iconSearch.addEventListener('click', function(e) {
|
||||||
if (iconInput) {
|
e.stopPropagation();
|
||||||
iconInput.addEventListener('focus', function() {
|
|
||||||
setTimeout(() => {
|
|
||||||
iconSearch.focus();
|
|
||||||
}, 100);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Close dropdown when clicking outside
|
// Initial render of icons
|
||||||
document.addEventListener('click', function(event) {
|
renderIcons('');
|
||||||
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')) {
|
// Close modal when pressing Escape key
|
||||||
dropdown.classList.add('hidden');
|
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