feat: add select/deselect buttons and search functionality for catalogs and languages in configuration

This commit is contained in:
mhdzumair
2025-03-02 21:17:03 +05:30
parent 39e3829b0b
commit 7104ec77cd
2 changed files with 218 additions and 1 deletions
+65 -1
View File
@@ -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) {
+153
View File
@@ -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');