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 = '
-
-
From 1dc1fd6cc2b575f24210c5d1c84fc8ad65497905 Mon Sep 17 00:00:00 2001
From: Chun <12265739+rxchun@users.noreply.github.com>
Date: Wed, 18 Jun 2025 03:45:30 +0100
Subject: [PATCH 5/6] UI/UX - Scroll To Top Button + Home Popup + HTML5
implementation
- Added a Scroll To Top button
- Created a popup for the home {{ content }}, since that's a one time message that only needs to be shown to users once.
- HTML5 conversions
- First wave of `site.baseurl` links implementation from @arjunsuresh 's PR
---
_includes/footer.html | 28 +++++++++++++++++--------
_layouts/home.html | 48 +++++++++++++++++++++++++++----------------
2 files changed, 50 insertions(+), 26 deletions(-)
diff --git a/_includes/footer.html b/_includes/footer.html
index 5977d15..adc2a0e 100644
--- a/_includes/footer.html
+++ b/_includes/footer.html
@@ -1,11 +1,23 @@
+