(function() { 'use strict'; const root = document.getElementById('dd-modal-root') || document.body; // Утилиты парсинга function parseList(attr) { if (!attr) return []; try { const fixed = attr.replace(/'/g, '"'); return JSON.parse(fixed); } catch (e) { return String(attr).split(',').map(s => s.trim()).filter(Boolean); } } function parseSavedValue(el) { const v = el.getAttribute('data-value'); if (!v) return []; try { return JSON.parse(v); } catch (e) { return v.includes(',') ? v.split(',').map(s => s.trim()).filter(Boolean) : [v]; } } async function openModalForInput(triggerEl) { const listRaw = triggerEl.getAttribute('data-list') || '[]'; const options = parseList(listRaw); const isRadio = triggerEl.getAttribute('data-radio') === '1' || triggerEl.getAttribute('data-radio') === 'true'; const takeAll = triggerEl.getAttribute('data-takeall') === '1' || triggerEl.getAttribute('data-takeall') === 'true'; const allowCustom = triggerEl.getAttribute('data-custom') === '1' || triggerEl.getAttribute('data-custom') === 'true'; const title = triggerEl.getAttribute('data-title') || 'Выберите'; const saveLabel = triggerEl.getAttribute('data-label') || 'Сохранить'; const customPlaceholder = triggerEl.getAttribute('data-custom_placeholder') || 'Добавить...'; const initial = parseSavedValue(triggerEl); return new Promise(resolve => { const backdrop = document.createElement('div'); backdrop.className = 'dd-modal-backdrop'; const modal = document.createElement('div'); modal.className = 'dd-modal'; modal.innerHTML = `
${title}
`; backdrop.appendChild(modal); root.appendChild(backdrop); const body = modal.querySelector('.dd-body'); const customContainer = modal.querySelector('.dd-custom-container'); const saveBtn = modal.querySelector('.dd-save'); const closeBtn = modal.querySelector('.dd-close'); const groupName = 'dd-group-' + Date.now() + Math.random().toString(36).slice(2); let currentOptions = options.slice(); let selected = initial.slice(); function renderOptions() { body.innerHTML = ''; // "Выбрать все" if (takeAll) { const wrapper = createOption('Выбрать все', true, groupName, isRadio); const takeAllInput = wrapper.querySelector('input'); takeAllInput.id = 'takeAll'; takeAllInput.checked = selected.length === 0 || selected.includes('Выбрать все'); body.appendChild(wrapper); } currentOptions.forEach(opt => { const label = typeof opt === 'object' ? opt.name : String(opt); const tooltip = typeof opt === 'object' ? opt.tooltip : null; const count = typeof opt === 'object' ? opt.count : null; const wrapper = createOption(label, false, groupName, isRadio, tooltip, count); const input = wrapper.querySelector('input'); input.checked = selected.includes(label); body.appendChild(wrapper); }); } function createOption(label, isTakeAll, name, isRadio, tooltip = null, count = null) { const wrapper = document.createElement('label'); wrapper.className = 'dd-option'; const input = document.createElement('input'); input.type = isRadio ? 'radio' : 'checkbox'; input.name = name; input.value = label; input.className = 'offer-checkbox'; const span = document.createElement('span'); span.textContent = label; const div = document.createElement('div'); div.className = 'some-class'; // count if (count !== undefined && count !== null) { const countNode = document.createElement('span'); countNode.textContent = count; div.appendChild(countNode); } // tooltip if (tooltip) { const img = document.createElement('img'); img.className = 'tooltip-trigger'; img.dataset.tooltip = tooltip; img.src = '/static/help.svg'; img.alt = 'Подсказка'; div.appendChild(img); } span.appendChild(div); wrapper.append(input, span); return wrapper; } if (allowCustom) { customContainer.innerHTML = `
`; const customInput = customContainer.querySelector('.dd-custom-input'); const customAdd = customContainer.querySelector('.dd-custom-add'); const addCustom = (val) => { val = String(val).trim(); if (!val) return; if (!currentOptions.some(o => String(o) === val && typeof o !== 'object')) { currentOptions.push(val); triggerEl.setAttribute('data-list', JSON.stringify(currentOptions)); } if (isRadio) { selected = [val]; } else if (!selected.includes(val)) { selected.push(val); } renderOptions(); updateCheckedState(); }; customAdd.addEventListener('click', () => { addCustom(customInput.value); customInput.value = ''; customInput.focus(); }); customInput.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); addCustom(customInput.value); customInput.value = ''; } }); } function updateCheckedState() { Array.from(body.querySelectorAll('input')).forEach(inp => { inp.checked = selected.includes(inp.value); }); } function collectChecked() { return Array.from(body.querySelectorAll('input:checked')).map(i => i.value); } function closeModal() { const result = collectChecked(); triggerEl.setAttribute('data-value', isRadio ? (result[0] ?? '') : JSON.stringify(result)); if (triggerEl.type && triggerEl.type !== 'hidden') { triggerEl.value = isRadio ? (result[0] ?? '') : result.join(', '); } backdrop.remove(); resolve(result); } // Обработчики закрытия closeBtn.addEventListener('click', closeModal); saveBtn.addEventListener('click', closeModal); backdrop.addEventListener('click', e => { if (e.target === backdrop) closeModal(); }); // Логика "Выбрать все" function setupTakeAll() { const takeAllInput = document.getElementById('takeAll'); if (!takeAllInput) return; const allInputs = () => Array.from(body.querySelectorAll('input.offer-checkbox')); takeAllInput.addEventListener('change', () => { if (takeAllInput.checked) { selected = []; allInputs().forEach(inp => { if (inp !== takeAllInput) inp.checked = false; }); closeModal(); } }); // Сброс "Выбрать все" при выборе других body.addEventListener('change', e => { const inp = e.target; if (inp.type !== 'checkbox' || inp === takeAllInput) return; if (inp.checked) { takeAllInput.checked = false; } else { const anyChecked = allInputs().some(x => x !== takeAllInput && x.checked); takeAllInput.checked = !anyChecked; } }); } renderOptions(); setupTakeAll(); updateCheckedState(); }); } // Главный обработчик document.addEventListener('click', async function(e) { const trigger = e.target.closest('[data-select="1"]'); if (!trigger) return; const input = trigger.querySelector('input'); if (input) { input.readOnly = true; input.blur(); } e.preventDefault(); const result = await openModalForInput(trigger); if (result && result.length) { trigger.dispatchEvent(new Event('change', { bubbles: true })); } }, true); })();