Content sort: autosave without reload; keep page state across reloads

- drag & drop now saves in the background (700 ms debounce) without reloading:
  sections stay open, scroll position is kept; on failure it retries every 3 s,
  and navigator.sendBeacon flushes unsaved changes on page leave
- removed the explicit save button (autosave only), status indicator shows
  «Изменения не сохранены… / Сохранение… / Порядок сохранён»
- new global ui-state.js: restores open <details> and scroll position after any
  reload/redirect within the same page (applies to all pages, not just /learn)
- auto-expand a collapsed chapter/section while hovering during drag
This commit is contained in:
2026-09-16 19:00:03 +03:00
parent 5ffeaa0217
commit da464f882a
5 changed files with 157 additions and 21 deletions

View File

@@ -0,0 +1,59 @@
/* Состояние страницы при перезагрузке: открытые <details> и позиция прокрутки.
Нужно, чтобы после сохранения формы или перерисовки страница возвращалась
туда же, а не «схлопывала» разделы и не сбрасывала прокрутку в начало. */
(function () {
var KEY = 'lms-ui-state';
function keyOf(element, index) {
return element.getAttribute('data-chapter-id')
|| element.getAttribute('data-section-id')
|| element.id
|| ('details-' + index);
}
function currentUrl() {
return location.pathname + location.search;
}
function save() {
var open = [];
document.querySelectorAll('details').forEach(function (element, index) {
if (element.open) open.push(keyOf(element, index));
});
try {
sessionStorage.setItem(KEY, JSON.stringify({
url: currentUrl(),
y: window.scrollY || window.pageYOffset || 0,
open: open
}));
} catch (e) { /* приватный режим — просто пропускаем */ }
}
function restore() {
var raw;
try {
raw = sessionStorage.getItem(KEY);
if (raw) sessionStorage.removeItem(KEY);
} catch (e) { return; }
if (!raw) return;
var state;
try { state = JSON.parse(raw); } catch (e) { return; }
// состояние применяем только при возврате на ту же страницу (перезагрузка/redirect)
if (!state || state.url !== currentUrl()) return;
var open = state.open || [];
document.querySelectorAll('details').forEach(function (element, index) {
if (open.indexOf(keyOf(element, index)) >= 0) element.open = true;
});
if (typeof state.y === 'number' && state.y > 0) {
var y = state.y;
requestAnimationFrame(function () { window.scrollTo(0, y); });
setTimeout(function () { window.scrollTo(0, y); }, 80);
}
}
window.addEventListener('beforeunload', save);
document.addEventListener('DOMContentLoaded', restore);
})();