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>
165 lines
5.2 KiB
JavaScript
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);
|
|
});
|
|
}());
|