refactor: 4 модалки → LS.modal (classes ×2, library ×2)

classes.html (modal-overlay: 5 → 3):
  - modal-class — создание класса
  - modal-edit-assign — редактирование задания

library.html (modal-overlay: 5 → 3):
  - folder-modal — создание/переименование папки
  - move-modal — перемещение файла в папку

Везде один паттерн:
  1. Удалить inline <div class="modal-overlay">...</div> разметку
  2. Заменить openX/closeX функции на LS.modal({content, actions})
  3. Сохранить state в локальной переменной _xModal вместо
     document.getElementById('modal-id').classList.add('open')
  4. setError() / close() через ссылку на modal-instance
  5. Удалить орфанные closeX функции

Чистый эффект: −154 строки HTML/CSS дубликатов, единое поведение
ESC/backdrop/focus, accessibility (role/aria-modal) автоматически.

Осталось:
  classes.html — modal-assign (128 строк, complex tabs), review-modal
  library.html — folder-access-modal, assign-modal, upload-modal (все
    более сложные с tabs и multi-step)
  frontend/red-book.html (17 modal-overlay — отдельный заход)
  flashcards (5), course (4), dashboard (2), и другие
This commit is contained in:
Maxim Dolgolyov
2026-05-16 19:17:49 +03:00
parent f7b6785050
commit d3b16f55c8
2 changed files with 94 additions and 133 deletions
+52 -56
View File
@@ -761,35 +761,6 @@
</div><!-- /sb-content -->
<!-- Modal: Create class -->
<div class="modal-overlay" id="modal-class" onclick="closeOnOverlay(event,'modal-class')">
<div class="modal">
<div class="modal-title">Создать класс</div>
<div class="ip-row">
<div class="ip-preview" id="c-icon-preview"><i data-lucide="book-open"></i></div>
<div style="flex:1">
<div class="form-group" style="margin-bottom:0">
<label class="form-label">Название класса</label>
<input class="form-input" id="c-name" placeholder="11А · Биология" />
</div>
</div>
</div>
<div class="form-group">
<div class="icon-picker-label">Иконка класса</div>
<div class="icon-picker" id="c-icon-picker"></div>
<div class="icon-picker-label" style="margin-top:10px">Цвет</div>
<div class="color-picker" id="c-color-picker"></div>
</div>
<div class="form-group">
<label class="form-label">Описание (необязательно)</label>
<textarea class="form-textarea" id="c-desc" rows="2" placeholder="Подготовка к ЦТ 2026"></textarea>
</div>
<div class="modal-footer">
<button class="btn-cancel" onclick="closeModal('modal-class')">Отмена</button>
<button class="btn-save" id="btn-save-class" onclick="saveClass()">Создать</button>
</div>
</div>
</div>
<!-- Modal: Create assignment -->
<div class="modal-overlay" id="modal-assign" onclick="closeOnOverlay(event,'modal-assign')">
<div class="modal">
@@ -977,25 +948,6 @@
</div>
</div>
<!-- Modal: Edit assignment -->
<div class="modal-overlay" id="modal-edit-assign" onclick="closeOnOverlay(event,'modal-edit-assign')">
<div class="modal">
<div class="modal-title">Редактировать задание</div>
<div class="form-group">
<label class="form-label">Название</label>
<input class="form-input" id="ea-title" />
</div>
<div class="form-group">
<label class="form-label">Дедлайн</label>
<input class="form-input" id="ea-deadline" type="datetime-local" />
</div>
<div class="modal-footer">
<button class="btn-cancel" onclick="closeModal('modal-edit-assign')">Отмена</button>
<button class="btn-save" id="btn-save-edit-assign" onclick="saveEditAssignment()">Сохранить</button>
</div>
</div>
</div>
<!-- Review submission modal -->
<div class="modal-overlay" id="review-modal" onclick="if(event.target===this)closeReviewModal()">
<div class="modal">
@@ -1216,15 +1168,42 @@
/* ══ Create class ══ */
function openCreateClass() {
document.getElementById('c-name').value = '';
document.getElementById('c-desc').value = '';
_selectedIcon = 'book-open';
_selectedColor = '#9B5DE5';
const body = `
<div class="ip-row">
<div class="ip-preview" id="c-icon-preview"><i data-lucide="book-open"></i></div>
<div style="flex:1">
<div class="form-group" style="margin-bottom:0">
<label class="form-label">Название класса</label>
<input class="form-input" id="c-name" placeholder="11А · Биология" />
</div>
</div>
</div>
<div class="form-group">
<div class="icon-picker-label">Иконка класса</div>
<div class="icon-picker" id="c-icon-picker"></div>
<div class="icon-picker-label" style="margin-top:10px">Цвет</div>
<div class="color-picker" id="c-color-picker"></div>
</div>
<div class="form-group">
<label class="form-label">Описание (необязательно)</label>
<textarea class="form-textarea" id="c-desc" rows="2" placeholder="Подготовка к ЦТ 2026"></textarea>
</div>`;
_classModal = LS.modal({
title: 'Создать класс',
content: body,
size: 'md',
actions: [
{ label: 'Отмена', onClick: () => _classModal.close() },
{ label: 'Создать', primary: true, id: 'btn-save-class', onClick: saveClass },
],
});
renderIconPreview('c-icon-preview');
renderIconPicker('c-icon-picker', 'c-icon-preview');
renderColorPicker('c-color-picker', 'c-icon-preview');
openModal('modal-class');
}
let _classModal = null;
/* ══ Icon picker (Lucide SVG) ══════════════════════════════════════ */
const CLASS_ICONS = [
// Биология / Химия / Наука
@@ -1326,7 +1305,7 @@
btn.disabled = true;
try {
const c = await LS.createClass({ name, description, cover_emoji: encodeIconValue(_selectedIcon, _selectedColor) });
closeModal('modal-class');
_classModal?.close();
toast('Класс создан! Код: ' + c.invite_code);
await loadClasses();
openClass(c.id);
@@ -1721,13 +1700,30 @@
/* ══ Edit assignment ══ */
let _editingAssign = null;
let _editModal = null;
function openEditAssignment(id) {
_editingAssign = _classAssignments.find(x => x.id === id);
if (!_editingAssign) return;
document.getElementById('ea-title').value = _editingAssign.title;
const dl = _editingAssign.deadline;
document.getElementById('ea-deadline').value = dl ? dl.replace(' ', 'T').slice(0, 16) : '';
openModal('modal-edit-assign');
const dlVal = dl ? dl.replace(' ', 'T').slice(0, 16) : '';
const body = `
<div class="form-group">
<label class="form-label">Название</label>
<input class="form-input" id="ea-title" value="${LS.esc(_editingAssign.title)}" />
</div>
<div class="form-group">
<label class="form-label">Дедлайн</label>
<input class="form-input" id="ea-deadline" type="datetime-local" value="${dlVal}" />
</div>`;
_editModal = LS.modal({
title: 'Редактировать задание',
content: body,
size: 'sm',
actions: [
{ label: 'Отмена', onClick: () => _editModal.close() },
{ label: 'Сохранить', primary: true, id: 'btn-save-edit-assign', onClick: saveEditAssignment },
],
});
}
async function saveEditAssignment() {
if (!_editingAssign) return;
@@ -1745,7 +1741,7 @@
count: _editingAssign.count || 25,
test_id: _editingAssign.test_id || null,
});
closeModal('modal-edit-assign');
_editModal?.close();
toast('Изменения сохранены');
await openClass(currentClass.id);
document.querySelector('[data-tab="assign"]').click();