avtopost.kultmost.ru/static/script/select.js
2026-09-03 22:57:14 -04:00

270 lines
8.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(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 = `
<div class="dd-header">
<strong>${title}</strong>
<button class="dd-close" aria-label="Закрыть">&times;</button>
</div>
<div class="dd-custom-container" style="margin-top:8px;"></div>
<div class="dd-body"></div>
<div class="dd-actions" style="margin-top:12px;">
<button class="dd-save">${saveLabel}</button>
</div>
`;
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 = `
<div style="display:flex;gap:8px;align-items:center;flex-direction:column;">
<input type="text" class="dd-custom-input" placeholder="${customPlaceholder}"
style="flex:1;padding:6px;border:1px solid #ccc;width:100%;border-radius:4px;">
<button class="dd-custom-add" style="padding:6px 8px;width:100%;border-radius:5px;background:#5F1798;color:white;">
Добавить
</button>
</div>
`;
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);
})();