сохранение состояния expanded / collapsed
This commit is contained in:
parent
ad9a5df712
commit
1e8919e33c
72
script.js
72
script.js
@ -1,4 +1,5 @@
|
||||
const STORAGE_KEY = "ecosystem-panel-preferences";
|
||||
const COLLAPSE_STORAGE_KEY = "ecosystem-panel-collapse-state";
|
||||
|
||||
// Меняй навигацию здесь: разделы -> подразделы -> элементы.
|
||||
// url можно заменить на реальные адреса, tags помогают поиску.
|
||||
@ -141,6 +142,7 @@ const defaultPreferences = {
|
||||
const availableViews = ["cards", "list", "compact-cards", "compact-list"];
|
||||
|
||||
const preferences = { ...defaultPreferences, ...readPreferences() };
|
||||
const collapseState = readCollapseState();
|
||||
const catalog = document.querySelector("[data-catalog-root]");
|
||||
const navTree = document.querySelector("[data-nav-tree]");
|
||||
const searchInput = document.querySelector("[data-search-input]");
|
||||
@ -172,6 +174,9 @@ navTree.addEventListener("click", (event) => {
|
||||
focusCatalogTarget(button.dataset.focusTarget);
|
||||
});
|
||||
|
||||
navTree.addEventListener("toggle", handleDetailsToggle, true);
|
||||
catalog.addEventListener("toggle", handleDetailsToggle, true);
|
||||
|
||||
settingsToggle.addEventListener("click", () => {
|
||||
const isOpen = settingsPanel.classList.toggle("is-open");
|
||||
settingsOverlay.classList.toggle("is-open", isOpen);
|
||||
@ -235,11 +240,12 @@ function createItem(number, title, type, tags = [], url = "#") {
|
||||
|
||||
function renderCatalog() {
|
||||
const query = normalize(searchInput.value);
|
||||
const forceOpen = Boolean(query);
|
||||
const fragment = document.createDocumentFragment();
|
||||
let visibleItems = 0;
|
||||
|
||||
ecosystemSections.forEach((section) => {
|
||||
const sectionElement = createSectionElement(section);
|
||||
const sectionElement = createSectionElement(section, forceOpen);
|
||||
let visibleGroups = 0;
|
||||
let sectionVisibleItems = 0;
|
||||
|
||||
@ -252,7 +258,7 @@ function renderCatalog() {
|
||||
|
||||
visibleGroups += 1;
|
||||
sectionVisibleItems += filteredItems.length;
|
||||
sectionElement.querySelector(".section-groups").append(createGroupElement(group, filteredItems));
|
||||
sectionElement.querySelector(".section-groups").append(createGroupElement(group, filteredItems, forceOpen));
|
||||
});
|
||||
|
||||
if (visibleGroups) {
|
||||
@ -273,12 +279,13 @@ function renderCatalog() {
|
||||
|
||||
}
|
||||
|
||||
function createSectionElement(section) {
|
||||
function createSectionElement(section, forceOpen = false) {
|
||||
const sectionElement = document.createElement("details");
|
||||
sectionElement.className = "catalog-section";
|
||||
sectionElement.id = getSectionId(section);
|
||||
sectionElement.dataset.collapseKey = sectionElement.id;
|
||||
sectionElement.tabIndex = -1;
|
||||
sectionElement.open = true;
|
||||
sectionElement.open = forceOpen || getSavedOpenState(sectionElement.dataset.collapseKey);
|
||||
sectionElement.innerHTML = `
|
||||
<summary class="section-head">
|
||||
<div>
|
||||
@ -296,12 +303,13 @@ function createSectionElement(section) {
|
||||
return sectionElement;
|
||||
}
|
||||
|
||||
function createGroupElement(group, items) {
|
||||
function createGroupElement(group, items, forceOpen = false) {
|
||||
const groupElement = document.createElement("details");
|
||||
groupElement.className = "catalog-group";
|
||||
groupElement.id = getGroupId(group);
|
||||
groupElement.dataset.collapseKey = groupElement.id;
|
||||
groupElement.tabIndex = -1;
|
||||
groupElement.open = true;
|
||||
groupElement.open = forceOpen || getSavedOpenState(groupElement.dataset.collapseKey);
|
||||
|
||||
const cards = items.map(createCardElement);
|
||||
groupElement.innerHTML = `
|
||||
@ -384,7 +392,8 @@ function renderNavigationTree() {
|
||||
ecosystemSections.forEach((section) => {
|
||||
const sectionNode = document.createElement("details");
|
||||
sectionNode.className = "tree-section";
|
||||
sectionNode.open = true;
|
||||
sectionNode.dataset.collapseKey = getTreeSectionId(section);
|
||||
sectionNode.open = getSavedOpenState(sectionNode.dataset.collapseKey);
|
||||
|
||||
const sectionSummary = document.createElement("summary");
|
||||
sectionSummary.innerHTML = `
|
||||
@ -398,7 +407,8 @@ function renderNavigationTree() {
|
||||
section.groups.forEach((group) => {
|
||||
const groupNode = document.createElement("details");
|
||||
groupNode.className = "tree-group";
|
||||
groupNode.open = true;
|
||||
groupNode.dataset.collapseKey = getTreeGroupId(group);
|
||||
groupNode.open = getSavedOpenState(groupNode.dataset.collapseKey);
|
||||
|
||||
const groupSummary = document.createElement("summary");
|
||||
groupSummary.innerHTML = `
|
||||
@ -446,12 +456,34 @@ function focusCatalogTarget(targetId) {
|
||||
|
||||
target.closest(".catalog-section")?.setAttribute("open", "");
|
||||
target.closest(".catalog-group")?.setAttribute("open", "");
|
||||
persistOpenedAncestor(target.closest(".catalog-section"));
|
||||
persistOpenedAncestor(target.closest(".catalog-group"));
|
||||
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
target.focus({ preventScroll: true });
|
||||
target.classList.add("is-focused");
|
||||
window.setTimeout(() => target.classList.remove("is-focused"), 1200);
|
||||
}
|
||||
|
||||
function handleDetailsToggle(event) {
|
||||
const details = event.target;
|
||||
|
||||
if (!(details instanceof HTMLDetailsElement) || !details.dataset.collapseKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
collapseState[details.dataset.collapseKey] = details.open;
|
||||
saveCollapseState();
|
||||
}
|
||||
|
||||
function persistOpenedAncestor(details) {
|
||||
if (!details?.dataset.collapseKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
collapseState[details.dataset.collapseKey] = true;
|
||||
saveCollapseState();
|
||||
}
|
||||
|
||||
function attachCardInteractions() {
|
||||
document.querySelectorAll(".nav-card").forEach((card) => {
|
||||
let leaveTimer;
|
||||
@ -525,6 +557,14 @@ function getGroupId(group) {
|
||||
return `group-${group.id}`;
|
||||
}
|
||||
|
||||
function getTreeSectionId(section) {
|
||||
return `tree-${getSectionId(section)}`;
|
||||
}
|
||||
|
||||
function getTreeGroupId(group) {
|
||||
return `tree-${getGroupId(group)}`;
|
||||
}
|
||||
|
||||
function getItemId(item) {
|
||||
return `item-${item.number.replaceAll(".", "-")}`;
|
||||
}
|
||||
@ -545,6 +585,22 @@ function savePreferences(nextPreferences) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(nextPreferences));
|
||||
}
|
||||
|
||||
function getSavedOpenState(key) {
|
||||
return collapseState[key] ?? true;
|
||||
}
|
||||
|
||||
function readCollapseState() {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(COLLAPSE_STORAGE_KEY)) || {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function saveCollapseState() {
|
||||
localStorage.setItem(COLLAPSE_STORAGE_KEY, JSON.stringify(collapseState));
|
||||
}
|
||||
|
||||
function closeSettingsPanel() {
|
||||
settingsPanel.classList.remove("is-open");
|
||||
settingsOverlay.classList.remove("is-open");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user