SHA256
- styles/main.css: роли цветов (по умолчанию «Индиго»), шкала отступов/скруглений/шрифтов, цвета отношений для обеих тем; жёсткие цвета в стилях заменены на роли. - Палитра: пресеты и личные правки, «Оформление» Авто/День/Ночь, долгое нажатие — редактор цветов с экспортом/импортом. - Каналы: пузыри постов автора, плашки дней, строка «Написать в канал…», «О канале», создание канала с адресом из названия; лента открывается на свежих постах. - Чаты: плоский список, чипы-фильтры, пузыри, плашки дней; нижняя панель скрыта в переписке. - Корневые разделы — единая шапка; профиль и чужой профиль — общая карточка; настройки, кошелёк, сеансы — меню-списки. - Нижняя панель: иконки без подписей, бейджи на иконках. - confirmDialog вместо window.confirm/alert; на телефоне диалоги — шторки снизу. - docs/UI-Design/ISSUES-for-dev.md — найденные ошибки сервера/UI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
2.0 KiB
JavaScript
49 lines
2.0 KiB
JavaScript
// Возвращает Promise<boolean>: true — подтвердили, false — отмена, Escape или тап мимо.
|
|
export function confirmDialog({
|
|
title = 'Подтвердите действие',
|
|
text = '',
|
|
confirmLabel = 'Да',
|
|
cancelLabel = 'Отмена',
|
|
danger = false,
|
|
} = {}) {
|
|
return new Promise((resolve) => {
|
|
const opener = document.activeElement;
|
|
const modal = document.createElement('div');
|
|
modal.className = 'modal confirm-dialog';
|
|
modal.innerHTML = `
|
|
<div class="modal-card confirm-dialog__card" role="alertdialog" aria-modal="true" aria-labelledby="confirm-dialog-title">
|
|
<h2 class="modal-title" id="confirm-dialog-title"></h2>
|
|
<p class="confirm-dialog__text"></p>
|
|
<div class="form-actions-grid">
|
|
<button type="button" class="secondary-btn" data-answer="no"></button>
|
|
<button type="button" class="${danger ? 'destructive-btn confirm-dialog__danger' : 'primary-btn'}" data-answer="yes"></button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
modal.querySelector('.modal-title').textContent = title;
|
|
const textEl = modal.querySelector('.confirm-dialog__text');
|
|
textEl.textContent = text;
|
|
textEl.hidden = !text;
|
|
modal.querySelector('[data-answer="no"]').textContent = cancelLabel;
|
|
modal.querySelector('[data-answer="yes"]').textContent = confirmLabel;
|
|
|
|
const finish = (answer) => {
|
|
document.removeEventListener('keydown', onKeydown, true);
|
|
modal.remove();
|
|
opener?.focus?.();
|
|
resolve(answer);
|
|
};
|
|
const onKeydown = (event) => {
|
|
if (event.key === 'Escape') { event.stopPropagation(); finish(false); }
|
|
};
|
|
modal.addEventListener('click', (event) => {
|
|
if (event.target === modal) { finish(false); return; }
|
|
const answer = event.target.closest('[data-answer]')?.dataset.answer;
|
|
if (answer) finish(answer === 'yes');
|
|
});
|
|
document.addEventListener('keydown', onKeydown, true);
|
|
document.body.append(modal);
|
|
modal.querySelector('[data-answer="no"]').focus();
|
|
});
|
|
}
|