fix
This commit is contained in:
parent
8b30ac4c9e
commit
27d9e9820b
@ -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="Закрыть">×</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">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user