-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1389 from maykinmedia/feature/2687-selected-multi…
…select-dropdown ✨ [2687] Display selected statuses for multiselect filter
- Loading branch information
Showing
6 changed files
with
301 additions
and
122 deletions.
There are no files selected for viewing
2 changes: 1 addition & 1 deletion
2
src/open_inwoner/components/templates/components/FilterBar/FilterBar.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
323 changes: 218 additions & 105 deletions
323
src/open_inwoner/js/components/FilterBar/multiselect_listbox_checkbox.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,135 +1,248 @@ | ||
/** | ||
* When HTMX replaces parts of the DOM with new content, the JavaScript attached to the old DOM does not apply to the new content unless we explicitly reapply it. | ||
* To fix this, listen for htmx:afterSwap events that are triggered after new content is swapped into the DOM. | ||
*/ | ||
|
||
// Initialize select behavior | ||
function initSelectBehavior() { | ||
const selectButton = document.getElementById('selectButton') | ||
const listboxDropdown = document.getElementById('listboxDropdown') | ||
let currentIndex = -1 // Arrow key navigation | ||
|
||
if (selectButton) { | ||
selectButton.addEventListener('click', () => { | ||
const isExpanded = selectButton.getAttribute('aria-expanded') === 'true' | ||
listboxDropdown.classList.toggle('show') | ||
selectButton.setAttribute('aria-expanded', isExpanded ? 'false' : 'true') | ||
}) | ||
document.addEventListener('DOMContentLoaded', function () { | ||
initFilterBar() // Initialize everything on page load | ||
}) | ||
|
||
selectButton.addEventListener('keydown', (e) => { | ||
const items = listboxDropdown.querySelectorAll('label') | ||
if (e.key === 'ArrowDown') { | ||
e.preventDefault() | ||
currentIndex = (currentIndex + 1) % items.length | ||
items[currentIndex].focus() | ||
} else if (e.key === 'ArrowUp') { | ||
e.preventDefault() | ||
currentIndex = (currentIndex - 1 + items.length) % items.length | ||
items[currentIndex].focus() | ||
} else if (e.key === 'Escape') { | ||
listboxDropdown.classList.remove('show') | ||
selectButton.setAttribute('aria-expanded', 'false') | ||
selectButton.focus() | ||
} | ||
}) | ||
} | ||
function initFilterBar() { | ||
const filterBar = document.getElementById('filterBar') | ||
|
||
if (listboxDropdown) { | ||
listboxDropdown.addEventListener('keydown', (e) => { | ||
if (e.key === 'ArrowDown') { | ||
e.preventDefault() | ||
currentIndex = (currentIndex + 1) % listboxDropdown.children.length | ||
listboxDropdown.children[currentIndex].focus() | ||
} else if (e.key === 'ArrowUp') { | ||
e.preventDefault() | ||
currentIndex = | ||
(currentIndex - 1 + listboxDropdown.children.length) % | ||
listboxDropdown.children.length | ||
listboxDropdown.children[currentIndex].focus() | ||
} else if (e.key === 'Escape') { | ||
listboxDropdown.classList.remove('show') | ||
selectButton.setAttribute('aria-expanded', 'false') | ||
selectButton.focus() | ||
if (filterBar) { | ||
const initCheckboxStateFromURL = function () { | ||
const urlParams = new URLSearchParams(window.location.search) | ||
const checkboxes = document.querySelectorAll( | ||
'.filter-bar .checkbox__input' | ||
) | ||
|
||
checkboxes.forEach((checkbox) => { | ||
const value = checkbox.value | ||
checkbox.checked = urlParams.getAll('status').includes(value) | ||
}) | ||
|
||
calculateAndDisplayCheckedSum() // Update button and sum | ||
} | ||
|
||
const calculateAndDisplayCheckedSum = function () { | ||
const checkboxes = document.querySelectorAll( | ||
'.filter-bar .checkbox__input' | ||
) | ||
let sum = 0 | ||
let selectedFilters = [] | ||
|
||
checkboxes.forEach((checkbox) => { | ||
if (checkbox.checked) { | ||
const label = checkbox.nextElementSibling | ||
selectedFilters.push(label.textContent.trim()) | ||
const frequencyCounter = label.querySelector('.frequency-counter') | ||
const match = frequencyCounter.textContent.match(/\d+/) | ||
const value = match ? parseInt(match[0]) : 0 | ||
|
||
if (!isNaN(value)) { | ||
sum += value | ||
} | ||
} | ||
}) | ||
|
||
const selectButton = document.getElementById('selectButton') | ||
selectButton.innerHTML = '' // Clear the button content before appending new elements | ||
|
||
let expandIcon = document.createElement('span') | ||
expandIcon.classList.add('material-icons') | ||
expandIcon.setAttribute('aria-hidden', 'true') | ||
expandIcon.textContent = 'expand_more' | ||
|
||
let closeIcon = document.createElement('span') | ||
closeIcon.classList.add('material-icons', 'close-icon') | ||
closeIcon.setAttribute('aria-hidden', 'true') | ||
closeIcon.textContent = 'close' | ||
|
||
// Add text and icons based on selected filters | ||
if (selectedFilters.length === 0) { | ||
selectButton.textContent = 'Status ' | ||
selectButton.appendChild(expandIcon) | ||
selectButton.classList.remove('active') | ||
} else if (selectedFilters.length === 1) { | ||
const ellipsisSpan = document.createElement('span') | ||
ellipsisSpan.classList.add('ellipsis') | ||
ellipsisSpan.textContent = selectedFilters[0] | ||
selectButton.appendChild(ellipsisSpan) | ||
selectButton.appendChild(closeIcon) | ||
selectButton.classList.add('active') | ||
} else { | ||
selectButton.textContent = 'Status ' | ||
const activeFilterSpan = document.createElement('span') | ||
activeFilterSpan.classList.add('active-filters') | ||
activeFilterSpan.textContent = `${selectedFilters.length} actieve filters` | ||
selectButton.appendChild(activeFilterSpan) | ||
selectButton.appendChild(closeIcon) | ||
selectButton.classList.add('active') | ||
} | ||
}) | ||
} | ||
|
||
calculateAndDisplayCheckedSum() | ||
} | ||
closeIcon.addEventListener('click', function (event) { | ||
event.stopPropagation() | ||
checkboxes.forEach((checkbox) => { | ||
checkbox.checked = false | ||
}) | ||
calculateAndDisplayCheckedSum() // Recalculate and update the button and sum | ||
}) | ||
|
||
// Display sum of the frequency counters for checked checkboxes | ||
function calculateAndDisplayCheckedSum() { | ||
const checkboxes = document.querySelectorAll('.filter-bar .checkbox__input') | ||
let sum = 0 | ||
selectButton.setAttribute('aria-live', 'polite') | ||
|
||
checkboxes.forEach((checkbox) => { | ||
if (checkbox.checked) { | ||
const label = checkbox.nextElementSibling | ||
const frequencyCounter = label.querySelector('.frequencyCounter') | ||
const match = frequencyCounter.textContent.match(/\d+/) | ||
const value = match ? parseInt(match[0]) : 0 | ||
const frequencySumElement = document.getElementById('frequencySum') | ||
const resultTextElement = document.getElementById('resultText') | ||
|
||
if (!isNaN(value)) { | ||
sum += value | ||
if (frequencySumElement) { | ||
frequencySumElement.textContent = sum | ||
} | ||
|
||
if (resultTextElement) { | ||
resultTextElement.textContent = sum === 1 ? 'resultaat' : 'resultaten' | ||
} | ||
|
||
const filterCasesButton = document.getElementById('filterCases') | ||
const filterFormActions = document.getElementById('filterFormActions') | ||
const resetFilters = document.getElementById('resetFilters') | ||
|
||
if (filterCasesButton && filterFormActions) { | ||
if (sum > 0) { | ||
filterCasesButton.classList.remove('hide') | ||
filterFormActions.classList.remove('hide') | ||
resetFilters.classList.remove('hide') | ||
} else { | ||
filterCasesButton.classList.add('hide') | ||
filterFormActions.classList.add('hide') | ||
resetFilters.classList.add('hide') | ||
} | ||
} | ||
} | ||
}) | ||
|
||
const frequencySumElement = document.getElementById('frequencySum') | ||
const filterCasesButton = document.getElementById('filterCases') | ||
const filterFormActions = document.getElementById('filterFormActions') | ||
const initSelectBehavior = function () { | ||
const selectButton = document.getElementById('selectButton') | ||
const listboxDropdown = document.getElementById('listboxDropdown') | ||
const selectDropdownWrapper = document.getElementById( | ||
'selectDropdownWrapper' | ||
) | ||
let currentIndex = -1 | ||
|
||
if (selectButton) { | ||
selectButton.addEventListener('click', () => { | ||
const isExpanded = | ||
selectButton.getAttribute('aria-expanded') === 'true' | ||
listboxDropdown.classList.toggle('show') | ||
selectButton.setAttribute( | ||
'aria-expanded', | ||
isExpanded ? 'false' : 'true' | ||
) | ||
}) | ||
|
||
selectButton.addEventListener('keydown', (e) => { | ||
const items = listboxDropdown.querySelectorAll( | ||
'.filter-bar .checkbox__label' | ||
) | ||
if (e.key === 'ArrowDown') { | ||
e.preventDefault() | ||
currentIndex = (currentIndex + 1) % items.length | ||
items[currentIndex].focus() | ||
} else if (e.key === 'ArrowUp') { | ||
e.preventDefault() | ||
currentIndex = (currentIndex - 1 + items.length) % items.length | ||
items[currentIndex].focus() | ||
} else if (e.key === 'Escape') { | ||
listboxDropdown.classList.remove('show') | ||
selectButton.setAttribute('aria-expanded', 'false') | ||
selectButton.focus() | ||
} | ||
}) | ||
} | ||
|
||
// Display the sum | ||
if (frequencySumElement) { | ||
frequencySumElement.textContent = sum | ||
} | ||
if (listboxDropdown) { | ||
listboxDropdown.addEventListener('keydown', (e) => { | ||
const items = listboxDropdown.querySelectorAll( | ||
'.filter-bar .checkbox__label' | ||
) | ||
if (e.key === 'ArrowDown') { | ||
e.preventDefault() | ||
currentIndex = (currentIndex + 1) % items.length | ||
items[currentIndex].focus() | ||
} else if (e.key === 'ArrowUp') { | ||
e.preventDefault() | ||
currentIndex = (currentIndex - 1 + items.length) % items.length | ||
items[currentIndex].focus() | ||
} else if (e.key === 'Escape') { | ||
listboxDropdown.classList.remove('show') | ||
selectButton.setAttribute('aria-expanded', 'false') | ||
selectButton.focus() | ||
} | ||
}) | ||
} | ||
|
||
// Close dropdown when clicking outside the selectButton and listboxDropdown | ||
document.addEventListener('click', function (e) { | ||
if (!selectDropdownWrapper.contains(e.target)) { | ||
listboxDropdown.classList.remove('show') | ||
selectButton.setAttribute('aria-expanded', 'false') | ||
} | ||
}) | ||
|
||
// Ensure the filterCasesButton and filterFormActions exist before modifying them | ||
if (filterCasesButton && filterFormActions) { | ||
if (sum > 0) { | ||
filterCasesButton.classList.remove('hide') | ||
filterFormActions.classList.remove('hide') | ||
} else { | ||
filterCasesButton.classList.add('hide') | ||
filterFormActions.classList.add('hide') | ||
initCheckboxStateFromURL() | ||
} | ||
} | ||
} | ||
|
||
// Listen for checkbox change events to update the sum | ||
document.addEventListener('change', function (e) { | ||
if (e.target && e.target.classList.contains('checkbox__input')) { | ||
calculateAndDisplayCheckedSum() // Update sum on toggle | ||
} | ||
}) | ||
document.addEventListener('change', function (e) { | ||
if (e.target && e.target.classList.contains('checkbox__input')) { | ||
if (e.target.closest('.filter-bar')) { | ||
calculateAndDisplayCheckedSum() | ||
} | ||
} | ||
}) | ||
|
||
// Listen for the htmx:afterSwap event to initialize the select behavior and recalculate sum | ||
document.body.addEventListener('htmx:afterSwap', function () { | ||
const listboxDropdown = document.getElementById('listboxDropdown') | ||
const dataMultiSelectLabel = document.querySelectorAll('.checkbox__label') | ||
const resetFilters = document.getElementById('resetFilters') | ||
if (resetFilters) { | ||
resetFilters.addEventListener('click', function (e) { | ||
e.preventDefault() | ||
const checkboxes = document.querySelectorAll( | ||
'.filter-bar .checkbox__input' | ||
) | ||
checkboxes.forEach((checkbox) => { | ||
checkbox.checked = false | ||
}) | ||
|
||
calculateAndDisplayCheckedSum() | ||
|
||
const filterBarForm = document.querySelector('#filterBar .form') | ||
if (filterBarForm) { | ||
filterBarForm.submit() | ||
} | ||
}) | ||
} | ||
|
||
if (listboxDropdown && dataMultiSelectLabel) { | ||
initSelectBehavior() | ||
} | ||
|
||
calculateAndDisplayCheckedSum() | ||
}) | ||
document.body.addEventListener('htmx:afterSwap', function () { | ||
initFilterBar() | ||
calculateAndDisplayCheckedSum() // Make sure sum is updated after swap | ||
}) | ||
|
||
// Run on page load to initialize the component | ||
document.addEventListener('DOMContentLoaded', function () { | ||
initSelectBehavior() | ||
}) | ||
document.addEventListener('DOMContentLoaded', function () { | ||
initSelectBehavior() | ||
}) | ||
} | ||
} | ||
|
||
// Scroll to the top of the DOM | ||
function scrollToTopOfWindow() { | ||
setTimeout(function () { | ||
window.scrollTo({ top: 0, behavior: 'smooth' }) | ||
}, 200) // Allow more time for HTMX to load | ||
}, 10) | ||
} | ||
|
||
// Listen for clicks on any pagination to scroll to the top of the DOM window when HTMX refresh is triggered | ||
document.body.addEventListener('htmx:afterSwap', function () { | ||
setTimeout(function () { | ||
initFilterBar() // Reinitialize filter bar after swap | ||
}, 50) | ||
}) | ||
|
||
document.addEventListener('click', function (e) { | ||
if (e.target && e.target.classList.contains('pagination__link')) { | ||
scrollToTopOfWindow() | ||
setTimeout(function () { | ||
initFilterBar() // Reinitialize filter bar after swap | ||
}, 20) | ||
} | ||
}) |
Oops, something went wrong.