11const view = document . querySelector ( '#view' ) ;
22let dashboard = null ;
3+ const MODULE_COLLAPSE_KEY = 'python-path-module-collapse' ;
34const sandboxHistory = [ ] ;
45const sandboxSamples = [
56 { label : 'Вывод текста' , code : "print('Привет, Python!')\n" , inputs : [ ] } ,
@@ -11,6 +12,23 @@ const esc = (value = '') => String(value)
1112 . replaceAll ( '&' , '&' ) . replaceAll ( '<' , '<' ) . replaceAll ( '>' , '>' ) . replaceAll ( '"' , '"' ) . replaceAll ( "'" , ''' ) ;
1213const rich = ( value = '' ) => esc ( value ) . replace ( / ` ( [ ^ ` ] + ) ` / g, '<code class="inline-code">$1</code>' ) ;
1314
15+ function moduleCollapseStates ( ) {
16+ try { return JSON . parse ( localStorage . getItem ( MODULE_COLLAPSE_KEY ) || '{}' ) ; }
17+ catch { return { } ; }
18+ }
19+
20+ function isModuleCollapsed ( module ) {
21+ if ( module . completed !== module . total ) return false ;
22+ const states = moduleCollapseStates ( ) ;
23+ return states [ module . id ] ?? true ;
24+ }
25+
26+ function saveModuleCollapsed ( moduleId , collapsed ) {
27+ const states = moduleCollapseStates ( ) ;
28+ states [ moduleId ] = collapsed ;
29+ localStorage . setItem ( MODULE_COLLAPSE_KEY , JSON . stringify ( states ) ) ;
30+ }
31+
1432async function api ( path , options = { } ) {
1533 const response = await fetch ( path , {
1634 headers : { 'Content-Type' : 'application/json' , ...( options . headers || { } ) } ,
@@ -76,6 +94,17 @@ function renderDashboard(data) {
7694 ${ moduleHtml }
7795 <div class="section-heading"><div><h2>Коллекция достижений</h2><p>Маленькие победы поддерживают ритм.</p></div></div>
7896 <section class="badges">${ badgeHtml } </section>` ;
97+ view . querySelectorAll ( '[data-module-toggle]' ) . forEach ( ( button ) => {
98+ button . addEventListener ( 'click' , ( ) => {
99+ const module = button . closest ( '.module' ) ;
100+ const body = module . querySelector ( '[data-module-body]' ) ;
101+ const collapsed = ! body . hidden ;
102+ body . hidden = collapsed ;
103+ button . setAttribute ( 'aria-expanded' , String ( ! collapsed ) ) ;
104+ button . querySelector ( '.module-collapse' ) . textContent = collapsed ? 'Развернуть ▾' : 'Свернуть ▴' ;
105+ saveModuleCollapsed ( button . dataset . moduleToggle , collapsed ) ;
106+ } ) ;
107+ } ) ;
79108}
80109
81110function renderModule ( module ) {
@@ -92,10 +121,15 @@ function renderModule(module) {
92121 const examMarkup = exam . available
93122 ? `<a class="button ${ exam . passed ? 'ghost' : 'blue' } " href="#/exam/${ module . id } ">${ exam . passed ? `Сдан: ${ exam . score } б.` : 'Пройти' } </a>`
94123 : '<button class="button ghost" disabled>🔒 Закрыт</button>' ;
124+ const collapsed = isModuleCollapsed ( module ) ;
125+ const title = `<div><h2>${ module . icon } ${ esc ( module . title ) } </h2><p>${ esc ( module . description ) } </p></div><span class="module-progress">${ module . completed } из ${ module . total } <br>уроков</span>` ;
126+ const header = module . completed === module . total
127+ ? `<button class="module-title ${ module . color } module-toggle" type="button" data-module-toggle="${ module . id } " aria-expanded="${ String ( ! collapsed ) } ">${ title } <span class="module-collapse">${ collapsed ? 'Развернуть ▾' : 'Свернуть ▴' } </span></button>`
128+ : `<div class="module-title ${ module . color } ">${ title } </div>` ;
95129 return `<section class="module card">
96- <div class="module-title ${ module . color } "><div><h2> ${ module . icon } ${ esc ( module . title ) } </h2><p> ${ esc ( module . description ) } </p></div><span class="module-progress"> ${ module . completed } из ${ module . total } <br>уроков</span></div>
97- <div class="lesson-list">${ lessons } </div>
98- <div class="exam-row"><div><strong>🏆 ${ module . id === 'realworld' ? 'Финальный экзамен' : 'Мини-экзамен раздела' } </strong><p>${ exam . available ? 'Проверь раздел и получи +50 XP.' : 'Откроется после всех уроков раздела.' } </p></div>${ examMarkup } </div>
130+ ${ header }
131+ <div data-module-body ${ collapsed ? 'hidden' : '' } ><div class="lesson-list">${ lessons } </div>
132+ <div class="exam-row"><div><strong>🏆 ${ module . id === 'realworld' ? 'Финальный экзамен' : 'Мини-экзамен раздела' } </strong><p>${ exam . available ? 'Проверь раздел и получи +50 XP.' : 'Откроется после всех уроков раздела.' } </p></div>${ examMarkup } </div></div>
99133 </section>` ;
100134}
101135
0 commit comments