first commit
This commit is contained in:
65
public/js/layouts/bookmarkNav.js
Normal file
65
public/js/layouts/bookmarkNav.js
Normal file
@@ -0,0 +1,65 @@
|
||||
export default function initBookmarkNav() {
|
||||
const navItems = document.querySelectorAll('.bookmark-nav-item');
|
||||
const sections = document.querySelectorAll('section[id]');
|
||||
|
||||
if (!navItems.length || !sections.length) return;
|
||||
|
||||
// Throttle function
|
||||
function throttle(func, limit) {
|
||||
let inThrottle;
|
||||
return function() {
|
||||
const args = arguments;
|
||||
const context = this;
|
||||
if (!inThrottle) {
|
||||
func.apply(context, args);
|
||||
inThrottle = true;
|
||||
setTimeout(() => inThrottle = false, limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setActiveNavItem() {
|
||||
const fromTop = window.scrollY + 100;
|
||||
let currentSection = null;
|
||||
|
||||
sections.forEach(section => {
|
||||
const sectionTop = section.offsetTop;
|
||||
const sectionHeight = section.offsetHeight;
|
||||
|
||||
if (fromTop >= sectionTop && fromTop < sectionTop + sectionHeight) {
|
||||
currentSection = section;
|
||||
}
|
||||
});
|
||||
|
||||
navItems.forEach(item => {
|
||||
item.classList.remove('bg-second-background-color');
|
||||
if (currentSection && item.getAttribute('data-category') === currentSection.getAttribute('id')) {
|
||||
item.classList.add('bg-second-background-color');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// // Handle click events on nav items
|
||||
// navItems.forEach(item => {
|
||||
// item.addEventListener('click', (e) => {
|
||||
// e.preventDefault();
|
||||
// const targetId = item.getAttribute('data-category');
|
||||
// const targetSection = document.getElementById(targetId);
|
||||
// if (targetSection) {
|
||||
// targetSection.scrollIntoView();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// Throttle scroll handler to run at most every 100ms
|
||||
window.addEventListener('scroll', throttle(setActiveNavItem, 100));
|
||||
|
||||
// Initial check
|
||||
setActiveNavItem();
|
||||
}
|
||||
|
||||
try {
|
||||
swup.hooks.on("page:view", initBookmarkNav);
|
||||
} catch (e) {}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", initBookmarkNav);
|
||||
53
public/js/layouts/categoryList.js
Normal file
53
public/js/layouts/categoryList.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const toggleStyle = (element, style, firstValue, secondValue) => {
|
||||
element.style[style] =
|
||||
element.style[style] === firstValue ? secondValue : firstValue;
|
||||
};
|
||||
|
||||
const setupCategoryList = () => {
|
||||
const parentElements = Array.from(
|
||||
document.querySelectorAll(".all-category-list-item"),
|
||||
).filter((item) =>
|
||||
item.parentElement.classList.contains("all-category-list"),
|
||||
);
|
||||
|
||||
parentElements.forEach((parentElement) => {
|
||||
const childElements = parentElement.querySelectorAll(
|
||||
".all-category-list-child",
|
||||
);
|
||||
childElements.forEach((childElement) => {
|
||||
childElement.style.maxHeight = "0px";
|
||||
childElement.style.marginTop = "0px";
|
||||
});
|
||||
|
||||
parentElement.addEventListener("click", () => {
|
||||
const clickedElementTopOffset = parentElement.offsetTop;
|
||||
childElements.forEach((childElement) => {
|
||||
toggleStyle(childElement, "maxHeight", "0px", "1000px");
|
||||
toggleStyle(childElement, "marginTop", "0px", "15px");
|
||||
});
|
||||
|
||||
parentElements.forEach((siblingElement) => {
|
||||
if (
|
||||
siblingElement.offsetTop === clickedElementTopOffset &&
|
||||
siblingElement !== parentElement
|
||||
) {
|
||||
const siblingChildElements = siblingElement.querySelectorAll(
|
||||
".all-category-list-child",
|
||||
);
|
||||
siblingChildElements.forEach((siblingChildElement) => {
|
||||
toggleStyle(siblingChildElement, "maxHeight", "0px", "1000px");
|
||||
toggleStyle(siblingChildElement, "marginTop", "0px", "15px");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
swup.hooks.on("page:view", setupCategoryList);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", setupCategoryList);
|
||||
25
public/js/layouts/essays.js
Normal file
25
public/js/layouts/essays.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// Function to format the dates
|
||||
function formatEssayDates() {
|
||||
const dateElements = document.querySelectorAll(".essay-date");
|
||||
|
||||
if (!dateElements) {
|
||||
return;
|
||||
}
|
||||
|
||||
dateElements.forEach(function (element) {
|
||||
const rawDate = element.getAttribute("data-date");
|
||||
const locale = config.language || "en";
|
||||
|
||||
const formattedDate = moment(rawDate).locale(locale).calendar();
|
||||
element.textContent = formattedDate;
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
swup.hooks.on("page:view", formatEssayDates);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
// Initial call for the first page load
|
||||
document.addEventListener("DOMContentLoaded", formatEssayDates);
|
||||
22
public/js/layouts/lazyload.js
Normal file
22
public/js/layouts/lazyload.js
Normal file
@@ -0,0 +1,22 @@
|
||||
export default function initLazyLoad() {
|
||||
const imgs = document.querySelectorAll("img");
|
||||
const options = {
|
||||
rootMargin: "0px",
|
||||
threshold: 0.1,
|
||||
};
|
||||
const observer = new IntersectionObserver((entries, observer) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
const img = entry.target;
|
||||
img.src = img.getAttribute("data-src");
|
||||
img.removeAttribute("lazyload");
|
||||
observer.unobserve(img);
|
||||
}
|
||||
});
|
||||
}, options);
|
||||
imgs.forEach((img) => {
|
||||
if (img.hasAttribute("lazyload")) {
|
||||
observer.observe(img);
|
||||
}
|
||||
});
|
||||
}
|
||||
135
public/js/layouts/navbarShrink.js
Normal file
135
public/js/layouts/navbarShrink.js
Normal file
@@ -0,0 +1,135 @@
|
||||
import { navigationState } from "../utils.js";
|
||||
|
||||
export const navbarShrink = {
|
||||
navbarDom: document.querySelector(".navbar-container"),
|
||||
leftAsideDom: document.querySelector(".page-aside"),
|
||||
isnavbarShrink: false,
|
||||
navbarHeight: 0,
|
||||
|
||||
init() {
|
||||
this.navbarHeight = this.navbarDom.getBoundingClientRect().height;
|
||||
this.shrink();
|
||||
this.togglenavbarDrawerShow();
|
||||
this.toggleSubmenu();
|
||||
window.addEventListener("scroll", () => {
|
||||
this.shrink();
|
||||
});
|
||||
},
|
||||
|
||||
shrink() {
|
||||
const scrollTop =
|
||||
document.documentElement.scrollTop || document.body.scrollTop;
|
||||
|
||||
if (!this.isnavbarShrink && scrollTop > this.navbarHeight) {
|
||||
this.isnavbarShrink = true;
|
||||
document.body.classList.add("navbar-shrink");
|
||||
} else if (this.isnavbarShrink && scrollTop <= this.navbarHeight) {
|
||||
this.isnavbarShrink = false;
|
||||
document.body.classList.remove("navbar-shrink");
|
||||
}
|
||||
},
|
||||
|
||||
togglenavbarDrawerShow() {
|
||||
const domList = [
|
||||
document.querySelector(".window-mask"),
|
||||
document.querySelector(".navbar-bar"),
|
||||
];
|
||||
|
||||
if (document.querySelector(".navbar-drawer")) {
|
||||
domList.push(
|
||||
...document.querySelectorAll(
|
||||
".navbar-drawer .drawer-navbar-list .drawer-navbar-item",
|
||||
),
|
||||
...document.querySelectorAll(".navbar-drawer .tag-count-item"),
|
||||
);
|
||||
}
|
||||
|
||||
domList.forEach((v) => {
|
||||
if (!v.dataset.navbarInitialized) {
|
||||
v.dataset.navbarInitialized = 1;
|
||||
v.addEventListener("click", () => {
|
||||
document.body.classList.toggle("navbar-drawer-show");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const logoTitleDom = document.querySelector(
|
||||
".navbar-container .navbar-content .logo-title",
|
||||
);
|
||||
if (logoTitleDom && !logoTitleDom.dataset.navbarInitialized) {
|
||||
logoTitleDom.dataset.navbarInitialized = 1;
|
||||
logoTitleDom.addEventListener("click", () => {
|
||||
document.body.classList.remove("navbar-drawer-show");
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
toggleSubmenu() {
|
||||
const toggleElements = document.querySelectorAll("[navbar-data-toggle]");
|
||||
|
||||
toggleElements.forEach((toggle) => {
|
||||
if (!toggle.dataset.eventListenerAdded) {
|
||||
toggle.dataset.eventListenerAdded = "true";
|
||||
toggle.addEventListener("click", function () {
|
||||
// console.log("click");
|
||||
const target = document.querySelector(
|
||||
'[data-target="' + this.getAttribute("navbar-data-toggle") + '"]',
|
||||
);
|
||||
const submenuItems = target.children; // Get submenu items
|
||||
const icon = this.querySelector(".fa-chevron-right");
|
||||
|
||||
if (target) {
|
||||
const isVisible = !target.classList.contains("hidden");
|
||||
|
||||
if (icon) {
|
||||
icon.classList.toggle("icon-rotated", !isVisible);
|
||||
}
|
||||
|
||||
if (isVisible) {
|
||||
// Animate to hide (reverse stagger effect)
|
||||
anime({
|
||||
targets: submenuItems,
|
||||
opacity: 0,
|
||||
translateY: -10,
|
||||
duration: 300,
|
||||
easing: "easeInQuart",
|
||||
delay: anime.stagger(80, { start: 20, direction: "reverse" }),
|
||||
complete: function () {
|
||||
target.classList.add("hidden");
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// Animate to show with stagger effect
|
||||
target.classList.remove("hidden");
|
||||
|
||||
anime({
|
||||
targets: submenuItems,
|
||||
opacity: [0, 1],
|
||||
translateY: [10, 0],
|
||||
duration: 300,
|
||||
easing: "easeOutQuart",
|
||||
delay: anime.stagger(80, { start: 20 }),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
swup.hooks.on("page:view", () => {
|
||||
navbarShrink.init();
|
||||
navigationState.isNavigating = false;
|
||||
});
|
||||
|
||||
swup.hooks.on("visit:start", () => {
|
||||
navigationState.isNavigating = true;
|
||||
document.body.classList.remove("navbar-shrink");
|
||||
});
|
||||
} catch (error) {}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
navbarShrink.init();
|
||||
});
|
||||
115
public/js/layouts/toc.js
Normal file
115
public/js/layouts/toc.js
Normal file
@@ -0,0 +1,115 @@
|
||||
/* main function */
|
||||
|
||||
import { initTocToggle } from "../tools/tocToggle.js";
|
||||
import { main } from "../main.js";
|
||||
export function initTOC() {
|
||||
const utils = {
|
||||
navItems: document.querySelectorAll(".post-toc-wrap .post-toc li"),
|
||||
|
||||
updateActiveTOCLink() {
|
||||
if (!Array.isArray(utils.sections)) return;
|
||||
let index = utils.sections.findIndex((element) => {
|
||||
return element && element.getBoundingClientRect().top - 100 > 0;
|
||||
});
|
||||
if (index === -1) {
|
||||
index = utils.sections.length - 1;
|
||||
} else if (index > 0) {
|
||||
index--;
|
||||
}
|
||||
this.activateTOCLink(index);
|
||||
},
|
||||
|
||||
registerTOCScroll() {
|
||||
utils.sections = [
|
||||
...document.querySelectorAll(".post-toc li a.nav-link"),
|
||||
].map((element) => {
|
||||
const target = document.getElementById(
|
||||
decodeURI(element.getAttribute("href")).replace("#", ""),
|
||||
);
|
||||
return target;
|
||||
});
|
||||
},
|
||||
|
||||
activateTOCLink(index) {
|
||||
const target = document.querySelectorAll(".post-toc li a.nav-link")[
|
||||
index
|
||||
];
|
||||
|
||||
if (!target || target.classList.contains("active-current")) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelectorAll(".post-toc .active").forEach((element) => {
|
||||
element.classList.remove("active", "active-current");
|
||||
});
|
||||
target.classList.add("active", "active-current");
|
||||
// Scroll to the active TOC item
|
||||
const tocElement = document.querySelector(".toc-content-container");
|
||||
const tocTop = tocElement.getBoundingClientRect().top;
|
||||
const scrollTopOffset =
|
||||
tocElement.offsetHeight > window.innerHeight
|
||||
? (tocElement.offsetHeight - window.innerHeight) / 2
|
||||
: 0;
|
||||
const targetTop = target.getBoundingClientRect().top - tocTop;
|
||||
const viewportHeight = Math.max(
|
||||
document.documentElement.clientHeight,
|
||||
window.innerHeight || 0,
|
||||
);
|
||||
const distanceToCenter =
|
||||
targetTop -
|
||||
viewportHeight / 2 +
|
||||
target.offsetHeight / 2 -
|
||||
scrollTopOffset;
|
||||
const scrollTop = tocElement.scrollTop + distanceToCenter;
|
||||
|
||||
tocElement.scrollTo({
|
||||
top: scrollTop,
|
||||
behavior: "smooth", // Smooth scroll
|
||||
});
|
||||
},
|
||||
|
||||
showTOCAside() {
|
||||
const openHandle = () => {
|
||||
const styleStatus = main.getStyleStatus();
|
||||
const key = "isOpenPageAside";
|
||||
if (styleStatus && styleStatus.hasOwnProperty(key)) {
|
||||
initTocToggle().pageAsideHandleOfTOC(styleStatus[key]);
|
||||
} else {
|
||||
initTocToggle().pageAsideHandleOfTOC(true);
|
||||
}
|
||||
};
|
||||
|
||||
const initOpenKey = "init_open";
|
||||
|
||||
if (theme.articles.toc.hasOwnProperty(initOpenKey)) {
|
||||
theme.articles.toc[initOpenKey]
|
||||
? openHandle()
|
||||
: initTocToggle().pageAsideHandleOfTOC(false);
|
||||
} else {
|
||||
openHandle();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (utils.navItems.length > 0) {
|
||||
utils.showTOCAside();
|
||||
utils.registerTOCScroll();
|
||||
} else {
|
||||
document
|
||||
.querySelectorAll(".toc-content-container, .toc-marker")
|
||||
.forEach((elem) => {
|
||||
elem.remove();
|
||||
});
|
||||
}
|
||||
|
||||
return utils;
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
try {
|
||||
swup.hooks.on("page:view", () => {
|
||||
initTOC();
|
||||
});
|
||||
} catch (e) {}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", initTOC);
|
||||
Reference in New Issue
Block a user