Files
magistr/frontend/admin/js/utils.js
2026-07-19 14:40:43 +03:00

123 lines
4.1 KiB
JavaScript
Executable File

export const ESCAPE_MAP = {
'&': '&',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;'
};
export function escapeHtml(str) {
if (!str) return '';
return String(str).replace(/[&<>"']/g, m => ESCAPE_MAP[m]);
}
export function showAlert(elementId, msg, type) {
const el = document.getElementById(elementId);
if (!el) return;
el.className = 'form-alert ' + type;
el.textContent = msg;
}
export function hideAlert(elementId) {
const el = document.getElementById(elementId);
if (!el) return;
el.className = 'form-alert';
el.textContent = '';
}
/** Форматирует календарную дату по локальным компонентам без UTC-сдвига. */
export function formatLocalDate(date) {
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
throw new TypeError('Ожидалась корректная дата');
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
export function renderStatusMessage(container, message, className = 'form-alert error') {
if (!container) return;
const element = document.createElement('div');
element.className = className;
element.textContent = message;
container.replaceChildren(element);
}
export function renderTableMessage(tbody, columnCount, message) {
if (!tbody) return;
const row = document.createElement('tr');
const cell = document.createElement('td');
cell.colSpan = columnCount;
cell.className = 'loading-row';
cell.textContent = message;
row.appendChild(cell);
tbody.replaceChildren(row);
}
export function applyRippleEffect() {
document.addEventListener('click', function (e) {
const btn = e.target.closest('.btn-primary, .btn-danger, .btn-danger-subtle, .btn-logout');
if (!btn) return;
const ripple = document.createElement('span');
ripple.classList.add('ripple');
btn.classList.add('ripple-host');
btn.appendChild(ripple);
setTimeout(() => ripple.remove(), 600);
});
}
export function initMultiSelect(boxId, menuId, textId, checkboxContainerId) {
const box = document.getElementById(boxId);
const menu = document.getElementById(menuId);
const container = document.getElementById(checkboxContainerId);
if (!box || !menu || !container) return;
// Remove old listeners to prevent duplication if re-initialized
const newBox = box.cloneNode(true);
box.parentNode.replaceChild(newBox, box);
newBox.addEventListener('click', (e) => {
e.stopPropagation();
const isOpen = menu.classList.contains('open');
document.querySelectorAll('.dropdown-menu').forEach(m => m.classList.remove('open'));
document.querySelectorAll('.select-box').forEach(b => b.classList.remove('active'));
if (!isOpen) {
menu.classList.add('open');
newBox.classList.add('active');
}
});
menu.addEventListener('click', (e) => {
e.stopPropagation();
});
container.addEventListener('change', () => {
updateSelectText(checkboxContainerId, textId);
});
}
export function updateSelectText(containerId, textId) {
const container = document.getElementById(containerId);
const textEl = document.getElementById(textId);
if (!container || !textEl) return;
const checked = Array.from(container.querySelectorAll('input:checked'));
if (checked.length === 0) {
textEl.textContent = 'Выберите оборудование...';
} else if (checked.length === 1) {
textEl.textContent = checked[0].parentElement.textContent.trim();
} else {
textEl.textContent = `Выбрано: ${checked.length}`;
}
}
export function closeAllDropdownsOnOutsideClick() {
document.addEventListener('click', () => {
document.querySelectorAll('.dropdown-menu').forEach(m => m.classList.remove('open'));
document.querySelectorAll('.select-box').forEach(b => b.classList.remove('active'));
});
}