This commit is contained in:
vladislav 2026-09-03 23:28:52 -04:00
parent 8b30ac4c9e
commit 27d9e9820b
Signed by: Vladislav
GPG Key ID: D97C9E5E4860EE45

View File

@ -435,6 +435,278 @@ shareBtns.forEach(btn => {
copyToClipboard(link); copyToClipboard(link);
}); });
});</script> });</script>
<script>
(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);
})();
</script>
<script> <script>
let deferredPrompt = null; let deferredPrompt = null;
let hasUserInteracted = false; let hasUserInteracted = false;
@ -524,7 +796,6 @@ shareBtns.forEach(btn => {
}, 1500); }, 1500);
} }
</script> </script>
<script defer src="/static/script/select.js"></script>
<div id="dd-modal-root"></div> <div id="dd-modal-root"></div>
<div id="modalToolTip"> <div id="modalToolTip">