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:
59
web/src/main/resources/static/js/ui-state.js
Normal file
59
web/src/main/resources/static/js/ui-state.js
Normal 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);
|
||||
})();
|
||||
Reference in New Issue
Block a user