| |
| function getCollapsibleNestedContainers(root) { |
| if (!root) return []; |
| const headers = root.querySelectorAll( |
| '.category > .category-header[data-toggle="category"], .group > .group-header[data-toggle="group"]' |
| ); |
| return Array.from(headers) |
| .map((header) => header.parentElement) |
| .filter(Boolean); |
| } |
|
|
| function isNestedContainerCollapsible(container) { |
| if (!container) return false; |
|
|
| if (container.classList.contains('category')) { |
| return Boolean(container.querySelector(':scope > .category-header[data-toggle="category"]')); |
| } |
|
|
| if (container.classList.contains('group')) { |
| return Boolean(container.querySelector(':scope > .group-header[data-toggle="group"]')); |
| } |
|
|
| return false; |
| } |
|
|
| |
| function updateCategoryToggleIcon(state) { |
| const toggleBtn = document.getElementById('category-toggle'); |
| if (!toggleBtn) return; |
|
|
| const icon = toggleBtn.querySelector('i'); |
| if (!icon) return; |
|
|
| if (state === 'up') { |
| icon.className = 'fas fa-angle-double-up'; |
| toggleBtn.setAttribute('aria-label', '收起分类'); |
| } else { |
| icon.className = 'fas fa-angle-double-down'; |
| toggleBtn.setAttribute('aria-label', '展开分类'); |
| } |
| } |
|
|
| |
| function toggleNestedElement(container) { |
| if (!isNestedContainerCollapsible(container)) return; |
| const isCollapsed = container.classList.contains('collapsed'); |
|
|
| if (isCollapsed) { |
| container.classList.remove('collapsed'); |
| saveToggleState(container, 'expanded'); |
| } else { |
| container.classList.add('collapsed'); |
| saveToggleState(container, 'collapsed'); |
| } |
|
|
| |
| const event = new CustomEvent('nestedToggle', { |
| detail: { |
| element: container, |
| type: container.dataset.type, |
| name: container.dataset.name, |
| isCollapsed: !isCollapsed, |
| }, |
| }); |
| document.dispatchEvent(event); |
| } |
|
|
| |
| function saveToggleState(element, state) { |
| const type = element.dataset.type; |
| const name = element.dataset.name; |
| const level = element.dataset.level || '1'; |
| const key = `menav-toggle-${type}-${level}-${name}`; |
| localStorage.setItem(key, state); |
| } |
|
|
| |
| function restoreToggleState(element) { |
| const type = element.dataset.type; |
| const name = element.dataset.name; |
| const level = element.dataset.level || '1'; |
| const key = `menav-toggle-${type}-${level}-${name}`; |
| const savedState = localStorage.getItem(key); |
|
|
| if (savedState === 'collapsed') { |
| element.classList.add('collapsed'); |
| } |
| } |
|
|
| |
| function initializeNestedCategories() { |
| |
| document.querySelectorAll('[data-toggle="category"], [data-toggle="group"]').forEach((header) => { |
| header.addEventListener('click', function (e) { |
| e.stopPropagation(); |
| const container = this.parentElement; |
| toggleNestedElement(container); |
| }); |
|
|
| |
| restoreToggleState(header.parentElement); |
| }); |
| } |
|
|
| |
| function extractNestedData(element) { |
| const data = { |
| name: element.dataset.name, |
| type: element.dataset.type, |
| level: element.dataset.level, |
| isCollapsed: element.classList.contains('collapsed'), |
| }; |
|
|
| |
| const subcategories = element.querySelectorAll( |
| ':scope > .category-content > .subcategories-container > .category' |
| ); |
| if (subcategories.length > 0) { |
| data.subcategories = Array.from(subcategories).map((sub) => extractNestedData(sub)); |
| } |
|
|
| const groups = element.querySelectorAll( |
| ':scope > .category-content > .groups-container > .group' |
| ); |
| if (groups.length > 0) { |
| data.groups = Array.from(groups).map((group) => extractNestedData(group)); |
| } |
|
|
| const subgroups = element.querySelectorAll( |
| ':scope > .group-content > .subgroups-container > .group' |
| ); |
| if (subgroups.length > 0) { |
| data.subgroups = Array.from(subgroups).map((subgroup) => extractNestedData(subgroup)); |
| } |
|
|
| const sites = element.querySelectorAll( |
| ':scope > .category-content > .sites-grid > .site-card, :scope > .group-content > .sites-grid > .site-card' |
| ); |
| if (sites.length > 0) { |
| data.sites = Array.from(sites).map((site) => ({ |
| name: site.dataset.name, |
| url: site.dataset.url, |
| icon: site.dataset.icon, |
| description: site.dataset.description, |
| })); |
| } |
|
|
| return data; |
| } |
|
|
| function registerNestedApi() { |
| if (!window.MeNav) { |
| |
| window.MeNav = {}; |
| } |
|
|
| window.MeNav.expandAll = function () { |
| const activePage = document.querySelector('.page.active'); |
| if (activePage) { |
| getCollapsibleNestedContainers(activePage).forEach((element) => { |
| element.classList.remove('collapsed'); |
| saveToggleState(element, 'expanded'); |
| }); |
| } |
| }; |
|
|
| window.MeNav.collapseAll = function () { |
| const activePage = document.querySelector('.page.active'); |
| if (activePage) { |
| getCollapsibleNestedContainers(activePage).forEach((element) => { |
| element.classList.add('collapsed'); |
| saveToggleState(element, 'collapsed'); |
| }); |
| } |
| }; |
|
|
| |
| window.MeNav.toggleCategories = function () { |
| const activePage = document.querySelector('.page.active'); |
| if (!activePage) return; |
|
|
| const allElements = getCollapsibleNestedContainers(activePage); |
| const collapsedElements = allElements.filter((element) => |
| element.classList.contains('collapsed') |
| ); |
| if (allElements.length === 0) return; |
|
|
| |
| if (collapsedElements.length >= allElements.length / 2) { |
| window.MeNav.expandAll(); |
| updateCategoryToggleIcon('up'); |
| } else { |
| window.MeNav.collapseAll(); |
| updateCategoryToggleIcon('down'); |
| } |
| }; |
|
|
| window.MeNav.toggleCategory = function ( |
| categoryName, |
| subcategoryName = null, |
| groupName = null, |
| subgroupName = null |
| ) { |
| let selector = `[data-name="${categoryName}"]`; |
|
|
| if (subcategoryName) selector += ` [data-name="${subcategoryName}"]`; |
| if (groupName) selector += ` [data-name="${groupName}"]`; |
| if (subgroupName) selector += ` [data-name="${subgroupName}"]`; |
|
|
| const element = document.querySelector(selector); |
| if (element) { |
| toggleNestedElement(element); |
| } |
| }; |
|
|
| window.MeNav.getNestedStructure = function () { |
| |
| const categories = []; |
| document.querySelectorAll('.category-level-1').forEach((cat) => { |
| categories.push(extractNestedData(cat)); |
| }); |
| return categories; |
| }; |
| } |
|
|
| registerNestedApi(); |
|
|
| module.exports = { |
| initializeNestedCategories, |
| updateCategoryToggleIcon, |
| extractNestedData, |
| }; |
|
|