diff --git a/frontend/app.js b/frontend/app.js
index 7c5dcbd..ed9e767 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -160,6 +160,127 @@
menuDropdown.classList.remove("active");
}
+ // ---------------------------------------------------------------------------
+ // Custom Dropdowns
+ // ---------------------------------------------------------------------------
+ function initCustomDropdowns() {
+ document.querySelectorAll('.custom-dropdown').forEach(dropdown => {
+ const trigger = dropdown.querySelector('.custom-dropdown-trigger');
+ const options = dropdown.querySelectorAll('.custom-dropdown-option');
+ const hiddenInput = dropdown.querySelector('input[type="hidden"]');
+ const selectedText = dropdown.querySelector('.custom-dropdown-selected');
+
+ if (!trigger) return;
+
+ // Toggle dropdown
+ trigger.addEventListener('click', (e) => {
+ e.stopPropagation();
+ const isOpen = dropdown.classList.contains('open');
+
+ // Close all other dropdowns
+ document.querySelectorAll('.custom-dropdown.open').forEach(d => {
+ if (d !== dropdown) d.classList.remove('open');
+ });
+
+ dropdown.classList.toggle('open', !isOpen);
+ trigger.setAttribute('aria-expanded', !isOpen);
+ });
+
+ // Handle option selection
+ options.forEach(option => {
+ option.addEventListener('click', (e) => {
+ e.stopPropagation();
+ const value = option.getAttribute('data-value');
+ const text = option.textContent;
+
+ // Update hidden input
+ if (hiddenInput) {
+ hiddenInput.value = value;
+ // Trigger change event
+ hiddenInput.dispatchEvent(new Event('change', { bubbles: true }));
+ }
+
+ // Update selected text
+ if (selectedText) {
+ selectedText.textContent = text;
+ }
+
+ // Update visual selection
+ options.forEach(opt => opt.classList.remove('selected'));
+ option.classList.add('selected');
+
+ // Close dropdown
+ dropdown.classList.remove('open');
+ trigger.setAttribute('aria-expanded', 'false');
+ });
+ });
+ });
+
+ // Close dropdowns when clicking outside
+ document.addEventListener('click', () => {
+ document.querySelectorAll('.custom-dropdown.open').forEach(dropdown => {
+ dropdown.classList.remove('open');
+ const trigger = dropdown.querySelector('.custom-dropdown-trigger');
+ if (trigger) trigger.setAttribute('aria-expanded', 'false');
+ });
+ });
+ }
+
+ // Helper to populate custom dropdown options
+ function populateCustomDropdown(dropdownId, optionsList, defaultValue) {
+ const dropdown = document.getElementById(dropdownId);
+ if (!dropdown) return;
+
+ const optionsContainer = dropdown.querySelector('.custom-dropdown-menu');
+ const hiddenInput = dropdown.querySelector('input[type="hidden"]');
+ const selectedText = dropdown.querySelector('.custom-dropdown-selected');
+
+ if (!optionsContainer) return;
+
+ // Clear existing options (keep the first one if it's the default)
+ optionsContainer.innerHTML = '';
+
+ // Add new options
+ optionsList.forEach(opt => {
+ const li = document.createElement('li');
+ li.className = 'custom-dropdown-option';
+ li.setAttribute('role', 'option');
+ li.setAttribute('data-value', opt.value);
+ li.textContent = opt.text;
+ if (opt.value === defaultValue) {
+ li.classList.add('selected');
+ if (selectedText) selectedText.textContent = opt.text;
+ if (hiddenInput) hiddenInput.value = opt.value;
+ }
+ optionsContainer.appendChild(li);
+ });
+
+ // Re-initialize click handlers
+ optionsContainer.querySelectorAll('.custom-dropdown-option').forEach(option => {
+ option.addEventListener('click', (e) => {
+ e.stopPropagation();
+ const value = option.getAttribute('data-value');
+ const text = option.textContent;
+
+ if (hiddenInput) {
+ hiddenInput.value = value;
+ hiddenInput.dispatchEvent(new Event('change', { bubbles: true }));
+ }
+
+ if (selectedText) {
+ selectedText.textContent = text;
+ }
+
+ optionsContainer.querySelectorAll('.custom-dropdown-option').forEach(opt => opt.classList.remove('selected'));
+ option.classList.add('selected');
+
+ dropdown.classList.remove('open');
+ const trigger = dropdown.querySelector('.custom-dropdown-trigger');
+ if (trigger) trigger.setAttribute('aria-expanded', 'false');
+ });
+ });
+ }
+
// ---------------------------------------------------------------------------
// API helpers
// ---------------------------------------------------------------------------
@@ -307,11 +428,17 @@
const vaults = await api("/api/vaults");
allVaults = vaults;
const container = document.getElementById("vault-tree");
- const filter = document.getElementById("vault-filter");
- const quickSelect = document.getElementById("vault-quick-select");
container.innerHTML = "";
- filter.innerHTML = '';
- quickSelect.innerHTML = '';
+
+ // Prepare dropdown options
+ const dropdownOptions = [
+ { value: "all", text: "Tous les vaults" },
+ ...vaults.map(v => ({ value: v.name, text: v.name }))
+ ];
+
+ // Populate custom dropdowns
+ populateCustomDropdown("vault-filter-dropdown", dropdownOptions, "all");
+ populateCustomDropdown("vault-quick-select-dropdown", dropdownOptions, "all");
vaults.forEach((v) => {
// Sidebar tree entry
@@ -326,17 +453,6 @@
const childContainer = el("div", { class: "tree-children collapsed", id: `vault-children-${v.name}` });
container.appendChild(childContainer);
-
- // Vault filter dropdown
- const opt = document.createElement("option");
- opt.value = v.name;
- opt.textContent = v.name;
- filter.appendChild(opt);
-
- const quickOpt = document.createElement("option");
- quickOpt.value = v.name;
- quickOpt.textContent = v.name;
- quickSelect.appendChild(quickOpt);
});
syncVaultSelectors();
@@ -1329,6 +1445,7 @@
async function init() {
initTheme();
initHeaderMenu();
+ initCustomDropdowns();
document.getElementById("theme-toggle").addEventListener("click", toggleTheme);
document.getElementById("header-logo").addEventListener("click", goHome);
initSearch();
diff --git a/frontend/index.html b/frontend/index.html
index 4585003..e40c914 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -95,9 +95,16 @@