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