From a7be2ca47173d5a296407de2e28a36e9b89a3255 Mon Sep 17 00:00:00 2001 From: Chun <12265739+rxchun@users.noreply.github.com> Date: Wed, 18 Jun 2025 01:50:50 +0100 Subject: [PATCH 1/6] New JS folder, for scalability and easier maintenance ### Part 1 of dividing my last PR into multiple sections. - Created a `JS` folder for structure organization. - Created a file called `scripts.js` that holds all scripts in one place, for easier maintenance in the future. - The script contains reusable / utility functions like `loopAddClass`, `loopRemoveClass` and `loopToggleClass`. - There's already functions inside this file that at the moment might not point to anything, but everything will tie together at the end. --- _layouts/default.html | 78 ++++++-------- _layouts/page.html | 12 --- js/scripts.js | 237 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+), 58 deletions(-) create mode 100644 js/scripts.js diff --git a/_layouts/default.html b/_layouts/default.html index 77348c4..de4d406 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -1,49 +1,35 @@ - - - - - - - - - - - {% if page.title %}{{ page.title | escape }}{% else %}{{ site.title }}{% endif %} - - - - - - {% include analytics.html %} - - - - - {% include header.html %} - -
- {{ content }} -
- - {% include footer.html %} - - - + + + + + + + + + + + {% if page.title %}{{ page.title | escape }}{% else %}{{ site.title }}{% endif %} + + + + + + {% include analytics.html %} + + + + + {% include header.html %} + +
+ {{ content }} +
+ + {% include footer.html %} + + + + diff --git a/_layouts/page.html b/_layouts/page.html index b308436..d8e6a9e 100644 --- a/_layouts/page.html +++ b/_layouts/page.html @@ -11,15 +11,3 @@ {{ content }} - - diff --git a/js/scripts.js b/js/scripts.js new file mode 100644 index 0000000..4a6c505 --- /dev/null +++ b/js/scripts.js @@ -0,0 +1,237 @@ +// -------------------------------- +// Common functions, Reusable STUFF +// -------------------------------- + +// Check for Parents +const hasParent = (element, ...parents) => parents.some( parent => parent.includes(element)); + +// Loop add class +const loopAddClass = (targetClass, addClass) => { + document.querySelectorAll(targetClass).forEach( element => { + element.classList.add(addClass); + }); +} + +// Loop remove class +const loopRemoveClass = (targetClass, removeClass) => { + document.querySelectorAll(targetClass).forEach( element => { + element.classList.remove(removeClass); + }); +} + +// Loop toggle class +const loopToggleClass = (targetClass, toggleClass) => { + document.querySelectorAll(targetClass).forEach( element => { + element.classList.toggle(toggleClass); + }); +} + +// Toggle Visibility. +const toggleVisibility = e => e.classList.toggle('display-none'); + +// Scroll to Elements +const scrollToElement = e => { + window.scroll({ + behavior: 'smooth', + left: 0, + top: e.offsetTop - 90 + }); +} + +// ---- + +// Add current page "selected-nav" class, for the Mega Menu "Documentation" Link item +if (document.querySelectorAll('.nav-main .selected-nav').length > 0) { + document.querySelector('.mega-menu-trigger .nav-item').classList.add('selected-nav'); +} +// Add selected nav to the top level parent (second nav) +if (document.querySelectorAll('.nav-main-second .selected-nav').length > 0 && + !document.body.classList.contains('template-contribute') && + !document.body.classList.contains('template-addons') + ){ + document.querySelector('.nav-main-second .selected-nav').closest('.sub-nav').previousElementSibling.classList.add('selected-nav'); +} + +// toggle menu children +let opened = null; +const handleDropdown = e => { + const clickedItem = e.parentElement.lastChild.previousSibling; + + toggleVisibility(clickedItem); + if (window.screen.width < 1024) { + loopAddClass('.nav-container .nav > li', 'obfuscate'); + e.parentElement.classList.remove('obfuscate'); // Remove it for the current List item + } + + if (!opened) { + opened = clickedItem; + } else if (opened == clickedItem) { + opened = null; + } else { + toggleVisibility(opened); + opened = clickedItem; + } +} + +const navContainer = document.querySelector('.nav-container'); +const backgroundSheet = document.querySelector('.darkPane'); +const navMain = document.querySelector('.nav-main'); +const noticeContainer = document.querySelector('.notice-container'); + +// Handle Click +const handleClick = e => { + + // Menu Trigger + if (e.target.id === 'main-nav-trigger') { + if (navContainer.classList.contains('active')) { + navContainer.classList.remove('active'); + backgroundSheet.classList.remove('active'); + } else { + navContainer.classList.add('active'); + backgroundSheet.classList.add('active'); + } + } + + // Mega Menu + if (e.target.parentElement.className.includes('mega-menu-trigger')) { + toggleVisibility(navMain); + } else if (!hasParent(e.target, '.nav-main')) { + navMain.classList.add('display-none'); + } + + // Nav Menu Children + if (e.target.className.includes('toggleChildren')) { + handleDropdown(e.target); + } else if (opened) { + toggleVisibility(opened); + if (window.screen.width < 1024) + loopRemoveClass('.nav-container .nav > li', 'obfuscate'); + opened = null; + } + + // Scroll to section on Sidebar navigation click + if (e.target.className.includes('docs-nav-item')) { + const targetDataId = e.target.getAttribute('data-attr-scroll'); + scrollToElement(document.getElementById(targetDataId)); + } + + // Go Top + if (e.target.className.includes('jump-top')) { + window.scrollTo({top: 0, behavior: 'smooth'}); + } + + // Close dark sheet + if (e.target.className.includes('darkPane')) { + if (backgroundSheet.classList.contains('active')) { + navContainer.classList.remove('active'); + backgroundSheet.classList.remove('active'); + } + } + + // Needs Page refresh after fetching + if (e.target.className.includes('close-page-status')) { + location.reload(); + } + + // Notice + if (e.target.className.includes('close-notice') || e.target.className.includes('close-sheet')) { + noticeContainer.classList.add('display-none'); + // set Notice localStorage + localStorage.q2adocs_notice = 'closed'; + } + +} // End handleClick() +document.addEventListener('click', handleClick); + +// Show / Hide Notice on the front page +if (localStorage.getItem('q2adocs_notice') === null && noticeContainer != null) { + noticeContainer.classList.remove('display-none'); +} + +// Quick fix to close Mega Menu, when clicking secondary nav items +document.querySelectorAll('.nav-main-second .toggleChildren').forEach(element => { + element.addEventListener('click', (e) => { + navMain.classList.add('display-none'); + }); +}); + + +// On Scroll +const header = document.querySelector('header.header'); +const stickyPos = header ? header.offsetTop : 0; + +const jumpTopContainer = document.querySelector('.jump-top-container'); +const jumpTop = document.getElementById('jump-top'); + +// Handle Scroll +const handleScroll = e => { + + // Sticky Topbar + if (window.scrollY > stickyPos) { + header.classList.add('sticky'); + } else { + header.classList.remove('sticky'); + } + + if(window.screen.width > 900) { + // Get the offset position of the scroll-to-top button + const stickyJumpPos = 700; + + const jtRect = jumpTopContainer.getBoundingClientRect().top; + const jtOH = ( window.pageYOffset || jumpTopContainer.scrollTop ) - ( jumpTopContainer.clientTop || 0 ); + const winHeight = window.innerHeight - jumpTopContainer.offsetHeight; + const stopStickyJump = (jtRect + jtOH - winHeight); + + if (window.scrollY > stickyJumpPos && window.scrollY < stopStickyJump) { + jumpTop.classList.add ('active'); + } else if (window.scrollY > stopStickyJump) { + jumpTop.classList.remove('active'); + } else { + jumpTop.classList.remove('active'); + } + } + +} +handleScroll(); +window.addEventListener('scroll', handleScroll); + + +// ---------------------------- +// Single page ---------------- +// ---------------------------- + +// Docs Navigation +const articleHeaders = document.querySelectorAll('\ + .page-content h1, .page-content h2, .page-content h3, .page-content h4, .page-content h5, .page-content h6\ +'); +const docsNav = document.querySelector('.docs-nav'); + +articleHeaders.forEach(element => { + element.classList.add('sectionTitle'); + let dataId = element.getAttribute('id'); + const html = '
  • ' + element.innerHTML +'
  • '; + docsNav.insertAdjacentHTML('beforeend', html); +}); + + +const sidebarDocslinks = document.querySelectorAll('.docs-nav-item'); +const docsSection = document.querySelectorAll('.sectionTitle'); + +const sidebarLinkNav = e => { + if(!document.body.classList.contains('template-homepage') && sidebarDocslinks != null && docsSection != null) { + let index = docsSection.length; + + while (--index && window.scrollY + 200 < docsSection[index].offsetTop) {} + + // add the active class if within visible height of the element + if (scrollY - docsSection[index].offsetHeight < docsSection[index].offsetTop) { + sidebarDocslinks.forEach((link) => link.classList.remove('active')); + sidebarDocslinks[index].classList.add('active'); + } + } +} +sidebarLinkNav(); +window.addEventListener('scroll', sidebarLinkNav); + + + From 0ef49080f8e9a26a8bd020ed5bb5a1a04e265cd8 Mon Sep 17 00:00:00 2001 From: Chun <12265739+rxchun@users.noreply.github.com> Date: Wed, 18 Jun 2025 02:57:32 +0100 Subject: [PATCH 2/6] Breadcrumbs --- _includes/breadcrumbs.html | 17 +++++++++++++++++ _includes/header.html | 5 +++++ 2 files changed, 22 insertions(+) create mode 100644 _includes/breadcrumbs.html diff --git a/_includes/breadcrumbs.html b/_includes/breadcrumbs.html new file mode 100644 index 0000000..c63c5c7 --- /dev/null +++ b/_includes/breadcrumbs.html @@ -0,0 +1,17 @@ + diff --git a/_includes/header.html b/_includes/header.html index e786056..b87a2d7 100644 --- a/_includes/header.html +++ b/_includes/header.html @@ -13,3 +13,8 @@ {% endfor %} + +{% if page.slug != 'homepage' %} + {% include breadcrumbs.html %} +{% endif %} + From ca23687c666dc1952beeeb902b2a635b85cda1b3 Mon Sep 17 00:00:00 2001 From: Chun <12265739+rxchun@users.noreply.github.com> Date: Wed, 18 Jun 2025 03:24:09 +0100 Subject: [PATCH 3/6] Page Slugs + Dynamic Sidebar The sidebar will be populated with link anchors to sections/titles of the page, via JS --- _includes/header.html | 2 ++ _includes/sidebar.html | 7 +++++++ _layouts/default.html | 2 +- _layouts/page.html | 11 +++++------ addons/clients.md | 1 + addons/index.md | 1 + addons/plugins.md | 1 + addons/themes.md | 1 + addons/translations-old.md | 1 + addons/translations.md | 1 + code/external.md | 1 + code/functions.md | 1 + code/structure.md | 1 + contribute/coding-style.md | 1 + contribute/docs.md | 1 + contribute/index.md | 1 + contribute/unit-tests.md | 1 + index.md | 1 + install/htaccess.md | 1 + install/index.md | 1 + install/optimize.md | 1 + install/security.md | 1 + install/single-sign-on.md | 1 + install/upgrade.md | 1 + install/versions/1.md | 1 + install/versions/index.md | 1 + install/wordpress.md | 1 + license.md | 1 + plugins/index.md | 1 + plugins/layers.md | 1 + plugins/modules-captcha.md | 1 + plugins/modules-editor.md | 1 + plugins/modules-event.md | 1 + plugins/modules-filter.md | 1 + plugins/modules-login.md | 1 + plugins/modules-page.md | 1 + plugins/modules-process.md | 1 + plugins/modules-search.md | 1 + plugins/modules-viewer.md | 1 + plugins/modules-widget.md | 1 + plugins/modules.md | 1 + plugins/overrides.md | 1 + plugins/tutorial.md | 1 + services.md | 1 + themes/index.md | 1 + translate/index.md | 1 + 46 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 _includes/sidebar.html diff --git a/_includes/header.html b/_includes/header.html index b87a2d7..da929cc 100644 --- a/_includes/header.html +++ b/_includes/header.html @@ -1,4 +1,6 @@ +{% if page.slug == 'homepage' %}
    +{% endif %}