diff --git a/frontend/teacher-guide.html b/frontend/teacher-guide.html index 45d7c67..af0ac21 100644 --- a/frontend/teacher-guide.html +++ b/frontend/teacher-guide.html @@ -157,7 +157,12 @@ .tg-hero-chip svg { width: 13px; height: 13px; } /* Chapter */ - .tg-chapter { margin-bottom: 56px; } + .tg-chapter { margin-bottom: 0; display: none; } + .tg-chapter.tg-active { display: block; animation: tgFadeIn 0.28s ease; } + @keyframes tgFadeIn { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } } + /* Search mode: reveal all chapters so matches are visible */ + .tg-content.tg-search-mode .tg-chapter { display: block; margin-bottom: 32px; } + .tg-content.tg-search-mode .tg-chapter.search-hidden { display: none; } .tg-chapter-header { display: flex; align-items: center; gap: 16px; padding: 28px 0 18px; @@ -1061,56 +1066,54 @@ }); lucide.createIcons(); - function navChapterClick(chId, btn) { - const chapter = btn.closest('.tg-nav-chapter'); - chapter.classList.toggle('open'); - scrollToChapter(chId); - } - - function scrollToChapter(chId) { - const el = document.getElementById(chId); - if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); - } - - function scrollToSection(secId) { - const el = document.getElementById(secId); - if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); - } - - /* ── Scroll progress ── */ + /* ── Chapter switching ── */ const scrollEl = document.getElementById('tg-scroll'); const progBar = document.getElementById('tg-prog-bar'); + const tgContent = document.querySelector('.tg-content'); + let _activeChId = null; - scrollEl.addEventListener('scroll', () => { - const pct = scrollEl.scrollTop / (scrollEl.scrollHeight - scrollEl.clientHeight); - progBar.style.width = Math.round(Math.min(pct, 1) * 100) + '%'; - }); - - /* ── Scroll spy (sections) ── */ - const allSections = document.querySelectorAll('.tg-section'); - const allNavSecs = document.querySelectorAll('.tg-nav-sec-link'); - - const sectionObs = new IntersectionObserver((entries) => { - entries.forEach(e => { - if (!e.isIntersecting) return; - const id = e.target.id; - allNavSecs.forEach(a => a.classList.toggle('active', a.dataset.sec === id)); - // Also open+highlight parent chapter - const ch = CHAPTERS.find(c => c.sections.includes(id)); - if (ch) { - document.querySelectorAll('.tg-nav-ch-btn').forEach(b => { - const parentDiv = b.closest('.tg-nav-chapter'); - const isThis = parentDiv.dataset.ch === ch.id; - b.classList.toggle('active', isThis); - if (isThis && !parentDiv.classList.contains('open')) parentDiv.classList.add('open'); - }); - } + function showChapter(chId, sectionId) { + const newEl = document.getElementById(chId); + if (!newEl) return; + if (_activeChId) { + const prev = document.getElementById(_activeChId); + if (prev) prev.classList.remove('tg-active'); + } + newEl.classList.add('tg-active'); + _activeChId = chId; + history.replaceState(null, '', '#' + chId); + scrollEl.scrollTop = 0; + if (sectionId) { + requestAnimationFrame(() => { + const sec = document.getElementById(sectionId); + if (sec) { + const top = sec.offsetTop - 80; + scrollEl.scrollTo({ top, behavior: 'smooth' }); + } + }); + } + // Nav: highlight active chapter, open its sub-list + document.querySelectorAll('.tg-nav-chapter').forEach(div => { + const isThis = div.dataset.ch === chId; + div.querySelector('.tg-nav-ch-btn').classList.toggle('active', isThis); + if (isThis && !div.classList.contains('open')) div.classList.add('open'); }); - }, { root: scrollEl, rootMargin: '-15% 0px -70% 0px' }); + document.querySelectorAll('.tg-nav-sec-link').forEach(a => a.classList.remove('active')); + markRead(chId); + } - allSections.forEach(s => sectionObs.observe(s)); + function scrollToChapter(chId) { showChapter(chId); } + function scrollToSection(secId) { + const ch = CHAPTERS.find(c => c.sections.includes(secId)); + if (ch) showChapter(ch.id, secId); + } - /* ── Chapter read tracking ── */ + function navChapterClick(chId, btn) { + btn.closest('.tg-nav-chapter').classList.toggle('open'); + showChapter(chId); + } + + /* ── Read tracking ── */ const READ_KEY = 'ls_tg_read'; let readChapters = JSON.parse(localStorage.getItem(READ_KEY) || '[]'); @@ -1118,28 +1121,20 @@ if (!readChapters.includes(chId)) { readChapters.push(chId); localStorage.setItem(READ_KEY, JSON.stringify(readChapters)); - updateReadUI(); } + updateReadUI(); } function updateReadUI() { document.querySelectorAll('.tg-nav-chapter').forEach(div => { - const isRead = readChapters.includes(div.dataset.ch); - div.querySelector('.tg-nav-ch-btn').classList.toggle('read', isRead); + div.querySelector('.tg-nav-ch-btn').classList.toggle('read', readChapters.includes(div.dataset.ch)); }); const n = readChapters.length; document.getElementById('tg-prog-text').textContent = `${n} из ${CHAPTERS.length} глав прочитано`; + progBar.style.width = Math.round(n / CHAPTERS.length * 100) + '%'; } updateReadUI(); - const chapterObs = new IntersectionObserver((entries) => { - entries.forEach(e => { - if (e.isIntersecting && e.intersectionRatio >= 0.15) markRead(e.target.id); - }); - }, { root: scrollEl, threshold: 0.15 }); - - document.querySelectorAll('.tg-chapter').forEach(c => chapterObs.observe(c)); - /* ── Checklist ── */ const CL_KEY = 'ls_tg_checklist'; const clState = JSON.parse(localStorage.getItem(CL_KEY) || '{}'); @@ -1154,7 +1149,6 @@ localStorage.setItem(CL_KEY, JSON.stringify(clState)); updateChecklist(); }); - // prevent link from triggering checkbox item.querySelector('.tg-cl-link')?.addEventListener('click', e => e.stopPropagation()); }); @@ -1168,44 +1162,29 @@ /* ── Accordion ── */ function toggleAcc(head) { - const item = head.closest('.tg-acc-item'); - item.classList.toggle('open'); + head.closest('.tg-acc-item').classList.toggle('open'); } - /* ── Nav search ── */ + /* ── Search: show all chapters matching query ── */ document.getElementById('tg-search').addEventListener('input', function() { const q = this.value.trim().toLowerCase(); if (!q) { + tgContent.classList.remove('tg-search-mode'); document.querySelectorAll('.tg-chapter, .tg-section').forEach(el => el.classList.remove('search-hidden')); return; } + tgContent.classList.add('tg-search-mode'); document.querySelectorAll('.tg-section').forEach(sec => { - const text = sec.textContent.toLowerCase(); - sec.classList.toggle('search-hidden', !text.includes(q)); + sec.classList.toggle('search-hidden', !sec.textContent.toLowerCase().includes(q)); }); document.querySelectorAll('.tg-chapter').forEach(ch => { - const visibleSecs = ch.querySelectorAll('.tg-section:not(.search-hidden)'); - ch.classList.toggle('search-hidden', visibleSecs.length === 0); + ch.classList.toggle('search-hidden', ch.querySelectorAll('.tg-section:not(.search-hidden)').length === 0); }); }); - /* ── Card entrance animation ── */ - const chapObs = new IntersectionObserver((entries) => { - entries.forEach(e => { - if (e.isIntersecting) { - e.target.style.opacity = '1'; - e.target.style.transform = 'translateY(0)'; - chapObs.unobserve(e.target); - } - }); - }, { root: scrollEl, threshold: 0.05 }); - - document.querySelectorAll('.tg-chapter').forEach((ch, i) => { - ch.style.opacity = '0'; - ch.style.transform = 'translateY(20px)'; - ch.style.transition = `opacity 0.4s ease ${i * 0.03}s, transform 0.4s ease ${i * 0.03}s`; - chapObs.observe(ch); - }); + /* ── Init from hash or default ch-1 ── */ + const initHash = location.hash.replace('#', ''); + showChapter(CHAPTERS.find(c => c.id === initHash) ? initHash : 'ch-1');