refactor: ещё 6 модалок → LS.modal (dashboard, theory, course)
dashboard.html: 2 → 0 ✅ - join-modal — вступить в класс - qs-modal — быстрый тест с выбором предмета + режим + кол-во theory.html: 1 → 0 ✅ - new-course-modal — создание нового курса учителем course.html: 4 → 0 ✅ - add-section-modal — новый раздел курса - edit-course-modal — редактирование курса - add-lesson-modal — новый урок - save-course-tpl-modal — сохранить курс как шаблон Везде: - Inline <div class=\"modal-overlay\">...</div> → удалён - openX(): создаёт modal через LS.modal({content, actions}) - closeX() удалена — _xModal.close() - Глобальный selectQsSubject() inline'нут как listener на body модалки - Enter-handler на главных inputs сохранён Не трогаю: - biochem.html#lib-modal — кастомная тёмная тема, не подходит под светлый LS.modal без редизайна - library.html — 3 сложные модалки (folder-access, assign, upload) с tabs и dynamic state — отдельный заход - classes.html — modal-assign (128 строк, complex) + review-modal - flashcards.html — fc-modal (не modal-overlay, своя CSS) Прогресс миграции: 12 простых модалок → LS.modal за серию (4 ранее + 2 ранее + 6 сейчас). 4 страницы полностью очищены от modal-overlay. Унифицированы: - ESC/backdrop/focus поведение - z-index (9000) - Анимация (scale .22s) - Адаптив на мобилке
This commit is contained in:
+96
-140
@@ -448,70 +448,6 @@
|
||||
</div>
|
||||
|
||||
<!-- Add section modal -->
|
||||
<div class="modal-overlay" id="add-section-modal" onclick="if(event.target===this)closeAddSectionModal()">
|
||||
<div class="modal">
|
||||
<div class="modal-title">Новый раздел</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Название раздела</label>
|
||||
<input class="form-input" id="as-title" placeholder="Например: Часть 1. Введение" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-cancel" onclick="closeAddSectionModal()">Отмена</button>
|
||||
<button class="btn-primary" id="btn-do-add-section" onclick="doAddSection()">Создать</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit course modal -->
|
||||
<div class="modal-overlay" id="edit-course-modal" onclick="if(event.target===this)closeEditModal()">
|
||||
<div class="modal">
|
||||
<div class="modal-title">Редактировать курс</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Название</label>
|
||||
<input class="form-input" id="ec-title" placeholder="Название курса" />
|
||||
</div>
|
||||
<div class="form-group" style="margin-top:12px">
|
||||
<label class="form-label">Описание</label>
|
||||
<textarea class="form-input" id="ec-desc" rows="3" placeholder="Краткое описание курса" style="resize:vertical"></textarea>
|
||||
</div>
|
||||
<div style="display:flex;gap:12px;margin-top:12px">
|
||||
<div class="form-group" style="flex:0 0 80px">
|
||||
<label class="form-label">Эмодзи</label>
|
||||
<input class="form-input" id="ec-emoji" placeholder="" maxlength="4" style="text-align:center;font-size:1.4rem" />
|
||||
</div>
|
||||
<div class="form-group" style="flex:1">
|
||||
<label class="form-label">Предмет</label>
|
||||
<select class="form-input" id="ec-subject">
|
||||
<option value="">— Не указан —</option>
|
||||
<option value="bio">Биология</option>
|
||||
<option value="chem">Химия</option>
|
||||
<option value="math">Математика</option>
|
||||
<option value="phys">Физика</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-cancel" onclick="closeEditModal()">Отмена</button>
|
||||
<button class="btn-primary" id="btn-do-edit-course" onclick="doEditCourse()">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add lesson modal -->
|
||||
<div class="modal-overlay" id="add-lesson-modal" onclick="if(event.target===this)closeAddLessonModal()">
|
||||
<div class="modal">
|
||||
<div class="modal-title">Новый урок</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Название урока</label>
|
||||
<input class="form-input" id="al-title" placeholder="Например: Строение клетки" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-cancel" onclick="closeAddLessonModal()">Отмена</button>
|
||||
<button class="btn-primary" id="btn-do-add-lesson" onclick="doAddLesson()">Создать</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/api.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script>
|
||||
@@ -900,17 +836,42 @@
|
||||
}
|
||||
|
||||
/* ── edit course modal ── */
|
||||
let _editCourseModal = null;
|
||||
function openEditModal() {
|
||||
if (!course) return;
|
||||
document.getElementById('ec-title').value = course.title || '';
|
||||
document.getElementById('ec-desc').value = course.description || '';
|
||||
document.getElementById('ec-emoji').value = course.coverEmoji || '';
|
||||
document.getElementById('ec-subject').value = course.subjectSlug || '';
|
||||
document.getElementById('edit-course-modal').classList.add('open');
|
||||
setTimeout(() => document.getElementById('ec-title').focus(), 50);
|
||||
}
|
||||
function closeEditModal() {
|
||||
document.getElementById('edit-course-modal').classList.remove('open');
|
||||
const body = `
|
||||
<div class="form-group">
|
||||
<label class="form-label">Название</label>
|
||||
<input class="form-input" id="ec-title" value="${LS.esc(course.title || '')}" />
|
||||
</div>
|
||||
<div class="form-group" style="margin-top:12px">
|
||||
<label class="form-label">Описание</label>
|
||||
<textarea class="form-input" id="ec-desc" rows="3" style="resize:vertical">${LS.esc(course.description || '')}</textarea>
|
||||
</div>
|
||||
<div style="display:flex;gap:12px;margin-top:12px">
|
||||
<div class="form-group" style="flex:0 0 80px">
|
||||
<label class="form-label">Эмодзи</label>
|
||||
<input class="form-input" id="ec-emoji" maxlength="4" style="text-align:center;font-size:1.4rem" value="${LS.esc(course.coverEmoji || '')}" />
|
||||
</div>
|
||||
<div class="form-group" style="flex:1">
|
||||
<label class="form-label">Предмет</label>
|
||||
<select class="form-input" id="ec-subject">
|
||||
<option value="">— Не указан —</option>
|
||||
<option value="bio">Биология</option>
|
||||
<option value="chem">Химия</option>
|
||||
<option value="math">Математика</option>
|
||||
<option value="phys">Физика</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>`;
|
||||
_editCourseModal = LS.modal({
|
||||
title: 'Редактировать курс', content: body, size: 'sm',
|
||||
actions: [
|
||||
{ label: 'Отмена', onClick: () => _editCourseModal.close() },
|
||||
{ label: 'Сохранить', primary: true, id: 'btn-do-edit-course', onClick: doEditCourse },
|
||||
],
|
||||
});
|
||||
_editCourseModal.body.querySelector('#ec-subject').value = course.subjectSlug || '';
|
||||
}
|
||||
async function doEditCourse() {
|
||||
const btn = document.getElementById('btn-do-edit-course');
|
||||
@@ -926,10 +887,9 @@
|
||||
subjectSlug: document.getElementById('ec-subject').value || null,
|
||||
}),
|
||||
});
|
||||
closeEditModal();
|
||||
_editCourseModal?.close();
|
||||
loadCourse();
|
||||
} catch (e) { LS.toast(e.message || 'Ошибка', 'error'); }
|
||||
finally { btn.disabled = false; }
|
||||
} catch (e) { LS.toast(e.message || 'Ошибка', 'error'); btn.disabled = false; }
|
||||
}
|
||||
|
||||
/* ── toggle publish ── */
|
||||
@@ -1000,13 +960,21 @@
|
||||
}
|
||||
|
||||
/* ── add section modal ── */
|
||||
let _addSectionModal = null;
|
||||
function openAddSectionModal() {
|
||||
document.getElementById('as-title').value = '';
|
||||
document.getElementById('add-section-modal').classList.add('open');
|
||||
setTimeout(() => document.getElementById('as-title').focus(), 50);
|
||||
}
|
||||
function closeAddSectionModal() {
|
||||
document.getElementById('add-section-modal').classList.remove('open');
|
||||
const body = `
|
||||
<div class="form-group">
|
||||
<label class="form-label">Название раздела</label>
|
||||
<input class="form-input" id="as-title" placeholder="Например: Часть 1. Введение" />
|
||||
</div>`;
|
||||
_addSectionModal = LS.modal({
|
||||
title: 'Новый раздел', content: body, size: 'sm',
|
||||
actions: [
|
||||
{ label: 'Отмена', onClick: () => _addSectionModal.close() },
|
||||
{ label: 'Создать', primary: true, id: 'btn-do-add-section', onClick: doAddSection },
|
||||
],
|
||||
});
|
||||
_addSectionModal.body.querySelector('#as-title').addEventListener('keydown', e => { if (e.key === 'Enter') doAddSection(); });
|
||||
}
|
||||
async function doAddSection() {
|
||||
const title = document.getElementById('as-title').value.trim();
|
||||
@@ -1019,24 +987,27 @@
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ title, orderIndex: (course?.sections?.length || 0) }),
|
||||
});
|
||||
closeAddSectionModal();
|
||||
_addSectionModal?.close();
|
||||
loadCourse();
|
||||
} catch (e) {
|
||||
LS.toast(e.message || 'Ошибка', 'error');
|
||||
} finally { btn.disabled = false; }
|
||||
} catch (e) { LS.toast(e.message || 'Ошибка', 'error'); btn.disabled = false; }
|
||||
}
|
||||
document.getElementById('as-title').addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') doAddSection();
|
||||
});
|
||||
|
||||
/* ── add lesson modal ── */
|
||||
let _addLessonModal = null;
|
||||
function openAddLessonModal() {
|
||||
document.getElementById('al-title').value = '';
|
||||
document.getElementById('add-lesson-modal').classList.add('open');
|
||||
setTimeout(() => document.getElementById('al-title').focus(), 50);
|
||||
}
|
||||
function closeAddLessonModal() {
|
||||
document.getElementById('add-lesson-modal').classList.remove('open');
|
||||
const body = `
|
||||
<div class="form-group">
|
||||
<label class="form-label">Название урока</label>
|
||||
<input class="form-input" id="al-title" placeholder="Например: Строение клетки" />
|
||||
</div>`;
|
||||
_addLessonModal = LS.modal({
|
||||
title: 'Новый урок', content: body, size: 'sm',
|
||||
actions: [
|
||||
{ label: 'Отмена', onClick: () => _addLessonModal.close() },
|
||||
{ label: 'Создать', primary: true, id: 'btn-do-add-lesson', onClick: doAddLesson },
|
||||
],
|
||||
});
|
||||
_addLessonModal.body.querySelector('#al-title').addEventListener('keydown', e => { if (e.key === 'Enter') doAddLesson(); });
|
||||
}
|
||||
async function doAddLesson() {
|
||||
const title = document.getElementById('al-title').value.trim();
|
||||
@@ -1049,26 +1020,40 @@
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ courseId: parseInt(courseId), title }),
|
||||
});
|
||||
closeAddLessonModal();
|
||||
_addLessonModal?.close();
|
||||
location.href = '/lesson-editor?id=' + res.id;
|
||||
} catch (e) {
|
||||
LS.toast(e.message || 'Ошибка', 'error');
|
||||
} finally { btn.disabled = false; }
|
||||
} catch (e) { LS.toast(e.message || 'Ошибка', 'error'); btn.disabled = false; }
|
||||
}
|
||||
document.getElementById('al-title').addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') doAddLesson();
|
||||
});
|
||||
|
||||
/* ── save course as template ── */
|
||||
let _saveCtModal = null;
|
||||
function openSaveCourseTplModal() {
|
||||
if (!course) return;
|
||||
document.getElementById('ct-title').value = course.title || '';
|
||||
document.getElementById('ct-desc').value = course.description || '';
|
||||
document.getElementById('save-course-tpl-modal').classList.add('open');
|
||||
setTimeout(() => document.getElementById('ct-title').focus(), 50);
|
||||
}
|
||||
function closeSaveCourseTplModal() {
|
||||
document.getElementById('save-course-tpl-modal').classList.remove('open');
|
||||
const body = `
|
||||
<div class="form-group">
|
||||
<label class="form-label">Название шаблона</label>
|
||||
<input class="form-input" id="ct-title" value="${LS.esc(course.title || '')}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Описание</label>
|
||||
<textarea class="form-input" id="ct-desc" rows="2" style="resize:vertical">${LS.esc(course.description || '')}</textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Категория</label>
|
||||
<select class="form-input" id="ct-cat">
|
||||
<option value="general">Общее</option>
|
||||
<option value="lecture">Лекционный курс</option>
|
||||
<option value="practice">Практикум</option>
|
||||
<option value="exam">Подготовка к экзамену</option>
|
||||
</select>
|
||||
</div>`;
|
||||
_saveCtModal = LS.modal({
|
||||
title: 'Сохранить курс как шаблон', content: body, size: 'sm',
|
||||
actions: [
|
||||
{ label: 'Отмена', onClick: () => _saveCtModal.close() },
|
||||
{ label: 'Сохранить', primary: true, id: 'btn-do-save-ct', onClick: doSaveCourseTpl },
|
||||
],
|
||||
});
|
||||
}
|
||||
async function doSaveCourseTpl() {
|
||||
const title = document.getElementById('ct-title').value.trim();
|
||||
@@ -1083,43 +1068,14 @@
|
||||
subject_slug: course.subjectSlug || null,
|
||||
courseId: parseInt(courseId),
|
||||
});
|
||||
closeSaveCourseTplModal();
|
||||
_saveCtModal?.close();
|
||||
LS.toast('Шаблон курса сохранён', 'success');
|
||||
} catch (e) { LS.toast(e.message || 'Ошибка', 'error'); }
|
||||
finally { btn.disabled = false; }
|
||||
} catch (e) { LS.toast(e.message || 'Ошибка', 'error'); btn.disabled = false; }
|
||||
}
|
||||
|
||||
loadCourse();
|
||||
</script>
|
||||
|
||||
<!-- Save course template modal -->
|
||||
<div class="modal-overlay" id="save-course-tpl-modal" onclick="if(event.target===this)closeSaveCourseTplModal()">
|
||||
<div class="modal">
|
||||
<div class="modal-title">Сохранить курс как шаблон</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Название шаблона</label>
|
||||
<input class="form-input" id="ct-title" placeholder="Название шаблона" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Описание</label>
|
||||
<textarea class="form-input" id="ct-desc" rows="2" placeholder="Краткое описание" style="resize:vertical"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Категория</label>
|
||||
<select class="form-input" id="ct-cat">
|
||||
<option value="general">Общее</option>
|
||||
<option value="lecture">Лекционный курс</option>
|
||||
<option value="practice">Практикум</option>
|
||||
<option value="exam">Подготовка к экзамену</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-cancel" onclick="closeSaveCourseTplModal()">Отмена</button>
|
||||
<button class="btn-primary" id="btn-do-save-ct" onclick="doSaveCourseTpl()">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/notifications.js"></script>
|
||||
<script src="/js/search.js"></script>
|
||||
<script src="/js/mobile.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user