mirror of
https://github.com/Viren070/MediaFusion.git
synced 2025-12-01 23:21:11 +01:00
feat: add select/deselect buttons and search functionality for catalogs and languages in configuration
This commit is contained in:
@@ -165,6 +165,60 @@ body, html {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Match button styles with theme */
|
||||
.btn-group .btn {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-group .btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Search input styling */
|
||||
.input-group .form-control-sm {
|
||||
background: rgba(30, 30, 50, 0.9);
|
||||
border: 1px solid rgba(74, 71, 163, 0.3);
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.input-group .form-control-sm:focus {
|
||||
background: rgba(40, 40, 60, 0.9);
|
||||
border-color: #4a47a3;
|
||||
box-shadow: 0 0 0 0.25rem rgba(74, 71, 163, 0.25);
|
||||
}
|
||||
|
||||
/* Search count badge */
|
||||
.search-count {
|
||||
background: rgba(74, 71, 163, 0.2);
|
||||
color: #e0e0e0;
|
||||
border: 1px solid rgba(74, 71, 163, 0.3);
|
||||
font-size: 0.85rem;
|
||||
padding: 0.2em 0.6em;
|
||||
border-radius: 0.25rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
/* Smooth hide/show transitions */
|
||||
.col-12, .draggable-catalog, .draggable-language {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.d-none {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Hover state for checkboxes */
|
||||
.form-check:hover {
|
||||
background: rgba(74, 71, 163, 0.3);
|
||||
}
|
||||
|
||||
/* Item highlight during search */
|
||||
.search-highlight {
|
||||
background: rgba(74, 71, 163, 0.4);
|
||||
border-left: 3px solid #4a47a3;
|
||||
}
|
||||
|
||||
/* MDBList Selected Lists Section */
|
||||
#selected-lists .list-group-item {
|
||||
@@ -1110,7 +1164,7 @@ small.text-muted,
|
||||
}
|
||||
|
||||
.excluded-file {
|
||||
background-color: rgba(255,255,255,0.05);
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -1280,6 +1334,15 @@ small.text-muted,
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.btn-group-sm > .btn, .btn-sm {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.input-group .form-control-sm {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
@@ -1296,6 +1359,7 @@ small.text-muted,
|
||||
.fallback-url-container button {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (min-width: 769px) and (max-width: 1200px) {
|
||||
|
||||
@@ -664,6 +664,153 @@ async function setupKodiAddon(kodiCode) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add select/deselect buttons and search input for a container
|
||||
*
|
||||
* @param {string} containerId - ID of the container element
|
||||
* @param {string} sectionName - Name of the section for labels and placeholders
|
||||
* @param {string} checkboxSelector - CSS selector for checkboxes in the container
|
||||
*/
|
||||
function addSelectDeselectSearch(containerId, sectionName, checkboxSelector) {
|
||||
const container = document.getElementById(containerId);
|
||||
if (!container) return;
|
||||
|
||||
// Create a controls container
|
||||
const controlsContainer = document.createElement('div');
|
||||
controlsContainer.className = 'mb-3';
|
||||
|
||||
// Create search input
|
||||
const searchGroup = document.createElement('div');
|
||||
searchGroup.className = 'input-group mb-2';
|
||||
|
||||
const searchInput = document.createElement('input');
|
||||
searchInput.type = 'text';
|
||||
searchInput.className = 'form-control form-control-sm';
|
||||
searchInput.placeholder = `Search ${sectionName}...`;
|
||||
searchInput.id = `${containerId}-search`;
|
||||
searchInput.setAttribute('autocomplete', 'off');
|
||||
|
||||
const searchClearBtn = document.createElement('button');
|
||||
searchClearBtn.className = 'btn btn-outline-secondary btn-sm';
|
||||
searchClearBtn.type = 'button';
|
||||
searchClearBtn.innerHTML = '<i class="bi bi-x"></i>';
|
||||
searchClearBtn.setAttribute('data-bs-toggle', 'tooltip');
|
||||
searchClearBtn.setAttribute('data-bs-placement', 'top');
|
||||
searchClearBtn.setAttribute('title', 'Clear Search');
|
||||
|
||||
searchGroup.appendChild(searchInput);
|
||||
searchGroup.appendChild(searchClearBtn);
|
||||
|
||||
// Create button group
|
||||
const buttonGroup = document.createElement('div');
|
||||
buttonGroup.className = 'btn-group btn-group-sm w-100';
|
||||
|
||||
const selectAllBtn = document.createElement('button');
|
||||
selectAllBtn.type = 'button';
|
||||
selectAllBtn.className = 'btn btn-outline-primary';
|
||||
selectAllBtn.innerHTML = '<i class="bi bi-check-all"></i> Select All';
|
||||
|
||||
const deselectAllBtn = document.createElement('button');
|
||||
deselectAllBtn.type = 'button';
|
||||
deselectAllBtn.className = 'btn btn-outline-secondary';
|
||||
deselectAllBtn.innerHTML = '<i class="bi bi-x-lg"></i> Deselect All';
|
||||
|
||||
buttonGroup.appendChild(selectAllBtn);
|
||||
buttonGroup.appendChild(deselectAllBtn);
|
||||
|
||||
// Add elements to controls container
|
||||
controlsContainer.appendChild(searchGroup);
|
||||
controlsContainer.appendChild(buttonGroup);
|
||||
|
||||
// Insert controls at the beginning of the container
|
||||
container.parentNode.insertBefore(controlsContainer, container);
|
||||
|
||||
// Add event listeners
|
||||
selectAllBtn.addEventListener('click', function() {
|
||||
const visibleCheckboxes = getVisibleCheckboxes(container, checkboxSelector);
|
||||
visibleCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = true;
|
||||
});
|
||||
});
|
||||
|
||||
deselectAllBtn.addEventListener('click', function() {
|
||||
const visibleCheckboxes = getVisibleCheckboxes(container, checkboxSelector);
|
||||
visibleCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = false;
|
||||
});
|
||||
});
|
||||
|
||||
searchInput.addEventListener('input', function() {
|
||||
filterItems(container, this.value.toLowerCase());
|
||||
});
|
||||
|
||||
searchClearBtn.addEventListener('click', function() {
|
||||
searchInput.value = '';
|
||||
filterItems(container, '');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all visible checkboxes in a container
|
||||
*
|
||||
* @param {HTMLElement} container - Container element
|
||||
* @param {string} checkboxSelector - CSS selector for checkboxes
|
||||
* @returns {HTMLElement[]} Array of visible checkbox elements
|
||||
*/
|
||||
function getVisibleCheckboxes(container, checkboxSelector) {
|
||||
const allCheckboxes = container.querySelectorAll(checkboxSelector);
|
||||
return Array.from(allCheckboxes).filter(checkbox => {
|
||||
const item = getItemContainer(checkbox);
|
||||
return item && !item.classList.contains('d-none');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the container element of a checkbox
|
||||
*
|
||||
* @param {HTMLElement} checkbox - Checkbox element
|
||||
* @returns {HTMLElement|null} Container element or null
|
||||
*/
|
||||
function getItemContainer(checkbox) {
|
||||
// Navigate up to find the draggable container or column
|
||||
let parent = checkbox.parentElement;
|
||||
while (parent && !parent.classList.contains('draggable-catalog') &&
|
||||
!parent.classList.contains('draggable-language') &&
|
||||
!parent.classList.contains('col-12') &&
|
||||
!parent.classList.contains('col-md-6') &&
|
||||
!parent.classList.contains('col-lg-4')) {
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter items in a container based on search text
|
||||
*
|
||||
* @param {HTMLElement} container - Container element
|
||||
* @param {string} searchText - Text to search for
|
||||
*/
|
||||
function filterItems(container, searchText) {
|
||||
// Find all items (columns or containers)
|
||||
const items = container.querySelectorAll('.draggable-catalog, .draggable-language, .col-12.col-md-6.col-lg-4');
|
||||
|
||||
if (searchText === '') {
|
||||
// Show all items if search is empty
|
||||
items.forEach(item => {
|
||||
item.classList.remove('d-none');
|
||||
});
|
||||
} else {
|
||||
// Show/hide items based on label text
|
||||
items.forEach(item => {
|
||||
const label = item.querySelector('label');
|
||||
if (label && label.textContent.toLowerCase().includes(searchText)) {
|
||||
item.classList.remove('d-none');
|
||||
} else {
|
||||
item.classList.add('d-none');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Event Listeners ----
|
||||
|
||||
@@ -910,6 +1057,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
document.getElementById('languageSortSection').style.display = 'block';
|
||||
}
|
||||
|
||||
// Add UI elements for catalogs
|
||||
addSelectDeselectSearch('catalogs', 'Catalogs', 'input[name="selected_catalogs"]');
|
||||
|
||||
// Add UI elements for languages
|
||||
addSelectDeselectSearch('languageSortOrder', 'Languages', 'input[name="selected_languages"]');
|
||||
|
||||
// Initialize the parental guide checkboxes
|
||||
const parentalGuideCheckboxes = document.querySelectorAll('.parental-guide-checkbox');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user