Files
orderPRO/public/assets/js/modules/checkbox-multiselect.js
Jacek Pyziak 6d3dba89ed feat(109): checkbox multiselect filters
Phase 109 complete:
- Add checkbox dropdown enhancement for statistics multi-select filters
- Preserve GET contract for channels[] and status_groups[]
- Update PAUL plan context to read .paul/codebase docs

Co-Authored-By: Codex <noreply@openai.com>
2026-04-28 22:15:04 +02:00

165 lines
5.2 KiB
JavaScript

(function () {
'use strict';
var ENHANCED_ATTR = 'data-checkbox-multiselect-enhanced';
function optionList(select) {
return Array.prototype.slice.call(select.options).filter(function (option) {
return option.value !== '';
});
}
function selectedOptions(select) {
return optionList(select).filter(function (option) {
return option.selected;
});
}
function selectedLabel(select) {
var selectedCount = selectedOptions(select).length;
if (selectedCount === 0) {
return select.dataset.emptyLabel || 'Nic nie wybrano';
}
var suffix = selectedCount === 1
? (select.dataset.selectedLabelSingular || 'zaznaczono')
: (select.dataset.selectedLabelPlural || 'zaznaczono');
return selectedCount + ' ' + suffix;
}
function setOpen(wrapper, isOpen) {
wrapper.classList.toggle('is-open', isOpen);
wrapper.querySelector('.checkbox-multiselect__trigger').setAttribute('aria-expanded', isOpen ? 'true' : 'false');
}
function updateAllState(select, allCheckbox) {
var options = optionList(select);
var selectedCount = selectedOptions(select).length;
allCheckbox.checked = options.length > 0 && selectedCount === options.length;
allCheckbox.indeterminate = selectedCount > 0 && selectedCount < options.length;
}
function syncTrigger(select, wrapper) {
wrapper.querySelector('.checkbox-multiselect__value').textContent = selectedLabel(select);
updateAllState(select, wrapper.querySelector('.checkbox-multiselect__all'));
}
function createCheckbox(option, select, wrapper, index) {
var row = document.createElement('label');
row.className = 'checkbox-multiselect__option';
var checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = option.selected;
checkbox.value = option.value;
checkbox.dataset.optionIndex = String(index);
var text = document.createElement('span');
text.textContent = option.text;
checkbox.addEventListener('change', function () {
option.selected = checkbox.checked;
syncTrigger(select, wrapper);
select.dispatchEvent(new Event('change', { bubbles: true }));
});
row.appendChild(checkbox);
row.appendChild(text);
return row;
}
function enhanceSelect(select) {
if (select.hasAttribute(ENHANCED_ATTR)) {
return;
}
var wrapper = document.createElement('div');
wrapper.className = 'checkbox-multiselect';
var trigger = document.createElement('button');
trigger.type = 'button';
trigger.className = 'checkbox-multiselect__trigger';
trigger.setAttribute('aria-haspopup', 'listbox');
trigger.setAttribute('aria-expanded', 'false');
var value = document.createElement('span');
value.className = 'checkbox-multiselect__value';
value.textContent = selectedLabel(select);
var arrow = document.createElement('span');
arrow.className = 'checkbox-multiselect__arrow';
arrow.setAttribute('aria-hidden', 'true');
var dropdown = document.createElement('div');
dropdown.className = 'checkbox-multiselect__dropdown';
dropdown.setAttribute('role', 'listbox');
var allRow = document.createElement('label');
allRow.className = 'checkbox-multiselect__option checkbox-multiselect__option--all';
var allCheckbox = document.createElement('input');
allCheckbox.type = 'checkbox';
allCheckbox.className = 'checkbox-multiselect__all';
var allText = document.createElement('span');
allText.textContent = select.dataset.allLabel || 'Wszystkie';
allCheckbox.addEventListener('change', function () {
var shouldSelect = allCheckbox.checked;
optionList(select).forEach(function (option) {
option.selected = shouldSelect;
});
wrapper.querySelectorAll('.checkbox-multiselect__option input:not(.checkbox-multiselect__all)').forEach(function (checkbox) {
checkbox.checked = shouldSelect;
});
syncTrigger(select, wrapper);
select.dispatchEvent(new Event('change', { bubbles: true }));
});
allRow.appendChild(allCheckbox);
allRow.appendChild(allText);
dropdown.appendChild(allRow);
optionList(select).forEach(function (option, index) {
dropdown.appendChild(createCheckbox(option, select, wrapper, index));
});
trigger.appendChild(value);
trigger.appendChild(arrow);
wrapper.appendChild(trigger);
wrapper.appendChild(dropdown);
select.parentNode.insertBefore(wrapper, select);
wrapper.appendChild(select);
select.setAttribute(ENHANCED_ATTR, '1');
trigger.addEventListener('click', function () {
setOpen(wrapper, !wrapper.classList.contains('is-open'));
});
wrapper.addEventListener('keydown', function (event) {
if (event.key === 'Escape') {
setOpen(wrapper, false);
trigger.focus();
}
});
syncTrigger(select, wrapper);
}
document.addEventListener('click', function (event) {
document.querySelectorAll('.checkbox-multiselect.is-open').forEach(function (wrapper) {
if (!wrapper.contains(event.target)) {
setOpen(wrapper, false);
}
});
});
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('select[data-checkbox-multiselect]').forEach(enhanceSelect);
});
}());