/** * Cascading multi-select — filters child options based on parent selection. */ import { getMultiSelectInstance } from './app-multiselect-init.js'; import { warnOnce, resolveHost } from '../core/app-dom.js'; const parseValueList = (value) => String(value || '') .split(',') .map((item) => item.trim()) .filter(Boolean); const setDisabledState = (option, disabled) => { option.dataset.disabled = disabled ? '1' : '0'; option.classList.toggle('is-disabled', disabled); option.setAttribute('aria-disabled', disabled ? 'true' : 'false'); option.style.pointerEvents = disabled ? 'none' : ''; }; const toggleSelectAll = (instance, disabled) => { const selectAll = instance?.element?.querySelector('.multi-select-all'); if (!selectAll) {return;} selectAll.classList.toggle('is-disabled', disabled); selectAll.setAttribute('aria-disabled', disabled ? 'true' : 'false'); selectAll.style.pointerEvents = disabled ? 'none' : ''; }; export const initMultiSelectCascade = ({ parent, child, map = {}, mode = 'disable', clearInvalid = true, root = document, } = {}) => { const host = resolveHost(root); const parentInput = typeof parent === 'string' ? host.querySelector(parent) : parent; const childInput = typeof child === 'string' ? host.querySelector(child) : child; if (!parentInput || !childInput) { if (!parentInput) { warnOnce('UI_EL_MISSING', `Missing multiselect parent: ${parent}`, { module: 'multiselect-cascade' }); } if (!childInput) { warnOnce('UI_EL_MISSING', `Missing multiselect child: ${child}`, { module: 'multiselect-cascade' }); } return { destroy: () => {} }; } const childInstance = getMultiSelectInstance(childInput, host); if (!childInstance) { warnOnce('UI_INIT_MISSING', 'Missing multiselect instance for child', { module: 'multiselect-cascade' }); return { destroy: () => {} }; } const apply = () => { const selectedParents = parseValueList(parentInput.value); const allowAll = selectedParents.length === 0; const allowed = new Set(); if (!allowAll) { selectedParents.forEach((id) => { const entries = map[id] || []; entries.forEach((entry) => allowed.add(String(entry))); }); } if (clearInvalid && !allowAll) { const selected = childInstance.selectedValues || []; selected.forEach((value) => { if (!allowed.has(String(value))) { childInstance.unselect(value); } }); } const options = childInstance.element.querySelectorAll('.multi-select-option'); let disabledCount = 0; options.forEach((option) => { const isAllowed = allowAll || allowed.has(String(option.dataset.value || '')); const disabled = mode === 'disable' ? !isAllowed : false; if (disabled) {disabledCount += 1;} setDisabledState(option, disabled); }); toggleSelectAll(childInstance, disabledCount > 0); }; parentInput.addEventListener('change', apply); apply(); return { destroy: () => { parentInput.removeEventListener('change', apply); const options = childInstance.element.querySelectorAll('.multi-select-option'); options.forEach((option) => setDisabledState(option, false)); toggleSelectAll(childInstance, false); }, }; };