// Reusable checkbox dropdown filter (multi-select) — ported from the original // createDropdown(). Expects markup: #-dropdown, #-btn, #-btn-text, // #-panel already present in the DOM. export interface Dropdown { setValues(values: string[], label: string): void; getSelected(): string[]; onChange(fn: () => void): void; } export function createDropdown(id: string): Dropdown { const root = document.getElementById(id + '-dropdown') as HTMLElement; const btn = document.getElementById(id + '-btn') as HTMLElement; const btnText = document.getElementById(id + '-btn-text') as HTMLElement; const panel = document.getElementById(id + '-panel') as HTMLElement; let allValues: string[] = []; let selected = new Set(); let defaultLabel = ''; let onChange: () => void = () => {}; function updateBtnText(): void { if (selected.size === 0) btnText.textContent = defaultLabel; else if (selected.size === allValues.length) btnText.textContent = 'Все (' + allValues.length + ')'; else if (selected.size <= 2) btnText.textContent = Array.from(selected).join(', '); else btnText.textContent = 'Выбрано: ' + selected.size; } function syncCheckboxes(): void { panel.querySelectorAll('input[type=checkbox]').forEach((cb) => { cb.checked = selected.has(cb.value); }); } function renderPanel(): void { panel.innerHTML = ''; const actions = document.createElement('div'); actions.className = 'dp-actions'; const selAll = document.createElement('a'); selAll.textContent = 'Выбрать все'; selAll.addEventListener('click', () => { selected = new Set(allValues); syncCheckboxes(); updateBtnText(); onChange(); }); const selNone = document.createElement('a'); selNone.textContent = 'Сбросить'; selNone.addEventListener('click', () => { selected.clear(); syncCheckboxes(); updateBtnText(); onChange(); }); actions.appendChild(selAll); actions.appendChild(selNone); panel.appendChild(actions); for (const v of allValues) { const label = document.createElement('label'); const cb = document.createElement('input'); cb.type = 'checkbox'; cb.value = v; cb.checked = selected.has(v); cb.addEventListener('change', () => { if (cb.checked) selected.add(v); else selected.delete(v); updateBtnText(); onChange(); }); label.appendChild(cb); label.appendChild(document.createTextNode(v)); panel.appendChild(label); } } btn.addEventListener('click', (e) => { e.stopPropagation(); document.querySelectorAll('.dropdown.open').forEach((d) => { if (d !== root) d.classList.remove('open'); }); root.classList.toggle('open'); }); document.addEventListener('click', (e) => { if (!root.contains(e.target as Node)) root.classList.remove('open'); }); return { setValues(values: string[], label: string): void { allValues = values; defaultLabel = label; selected = new Set(); updateBtnText(); renderPanel(); }, getSelected(): string[] { return Array.from(selected); }, onChange(fn: () => void): void { onChange = fn; }, }; }