diff --git a/resources/css/styles.css b/resources/css/styles.css
index 23dd645..a50587a 100644
--- a/resources/css/styles.css
+++ b/resources/css/styles.css
@@ -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) {
diff --git a/resources/js/config_script.js b/resources/js/config_script.js
index 87b8a58..b504482 100644
--- a/resources/js/config_script.js
+++ b/resources/js/config_script.js
@@ -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 = '';
+ 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 = ' Select All';
+
+ const deselectAllBtn = document.createElement('button');
+ deselectAllBtn.type = 'button';
+ deselectAllBtn.className = 'btn btn-outline-secondary';
+ deselectAllBtn.innerHTML = ' 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');