Merge pull request #41 from mearashadowfax/update-toc
Update README and refactor Table of Contents (ToC) code
This commit is contained in:
commit
f28987a86e
2 changed files with 62 additions and 60 deletions
|
@ -94,15 +94,18 @@ ScrewFast is an open-source template designed for quick and efficient web projec
|
|||
- Convenient and reusable Icon component that allows adding icons simply by providing a name prop.
|
||||
- Render any pre-defined icon SVG using `<Icon name="iconName" />` in your Astro components.
|
||||
- The Icon Component offers a centralized location for all SVG Icons across the project in one TypeScript file - allowing unified updates and easy maintenance.
|
||||
- **Note:** Users have the option to use other community integrations like [astro-icons](https://github.com/natemoo-re/astro-icon). However, the author decided to create a custom icon set component for managing custom icons.
|
||||
- **Note:** Developers have the option to use other community integrations like [astro-icons](https://github.com/natemoo-re/astro-icon). However, the author decided to create a custom icon set component for managing custom icons.
|
||||
|
||||
- [x] **Internationalization (i18n) Features**:
|
||||
- Integrates [Astro’s internationalization (i18n) features](https://docs.astro.build/en/guides/internationalization/).
|
||||
- Additionally, a custom LanguagePicker component has been developed to facilitate language selection.
|
||||
|
||||
### Planned Improvements
|
||||
- [ ] Implement a table of contents (ToC) sidebar for blog articles.
|
||||
- [x] **Dynamic Table of Contents (ToC) with Scroll Progress Indicator**:
|
||||
- Enhances ease of navigation in insight posts by highlighting the relevant section in the ToC, and includes a progress indicator to visually represent scroll progress.
|
||||
- Developers seeking alternatives might consider the [remark-toc](https://github.com/remarkjs/remark-toc) plugin.
|
||||
|
||||
### Planned Improvements
|
||||
- Currently, there are no planned improvements. We'll update this section as plans develop.
|
||||
|
||||
### Bug Fixes
|
||||
- Currently, there are no known bugs. If you encounter any issues, please report them on our [issues page](https://github.com/mearashadowfax/ScrewFast/issues).
|
||||
|
|
|
@ -82,6 +82,7 @@ const pageTitle: string = `${post.data.title} | ${SITE.title}`;
|
|||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
article h2,
|
||||
article h3,
|
||||
article h4,
|
||||
|
@ -91,21 +92,26 @@ const pageTitle: string = `${post.data.title} | ${SITE.title}`;
|
|||
margin-top: 2.5rem;
|
||||
scroll-margin-top: 3rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
@keyframes grow-progress {
|
||||
from {
|
||||
transform: scaleX(0);
|
||||
|
@ -114,20 +120,24 @@ const pageTitle: string = `${post.data.title} | ${SITE.title}`;
|
|||
transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
|
||||
#progress {
|
||||
transform-origin: 0 50%;
|
||||
animation: grow-progress auto linear;
|
||||
animation-timeline: scroll(block root);
|
||||
}
|
||||
|
||||
#toc li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: 0.8;
|
||||
transition: all 300ms var(--transition-cubic);
|
||||
}
|
||||
|
||||
#toc li.selected {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#toc li svg {
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
@ -135,6 +145,7 @@ const pageTitle: string = `${post.data.title} | ${SITE.title}`;
|
|||
height 400ms var(--transition-cubic),
|
||||
width 400ms var(--transition-cubic);
|
||||
}
|
||||
|
||||
#toc li.selected svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
|
@ -159,67 +170,55 @@ const pageTitle: string = `${post.data.title} | ${SITE.title}`;
|
|||
},
|
||||
});
|
||||
|
||||
const SVG_HTML_STRING = '<svg class="w-0 h-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="#fa5a15"><path stroke-linecap="round" stroke-linejoin="round" d="m12.75 15 3-3m0 0-3-3m3 3h-7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"></svg>';
|
||||
|
||||
function setActiveLinkById(id: string | null) {
|
||||
const listItems = document.querySelectorAll("#toc li");
|
||||
listItems.forEach(item => item.classList.remove("selected"));
|
||||
|
||||
if (!id) return;
|
||||
|
||||
const activeLink = document.querySelector(`#toc a[href="#${id}"]`);
|
||||
|
||||
if (!activeLink) return;
|
||||
|
||||
const listItem = activeLink.parentElement;
|
||||
listItem?.classList.add("selected");
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// The article element that contains the Markdown content
|
||||
const article: HTMLElement | null = document.querySelector("article");
|
||||
|
||||
// The ToC container <ul> element
|
||||
const tocList: HTMLElement | null = document.querySelector("#toc ul");
|
||||
|
||||
// Function to create and add an item to the ToC list
|
||||
function addToC(heading: HTMLElement) {
|
||||
const li = document.createElement("li");
|
||||
li.className = "toc-level-" + heading.tagName.toLowerCase();
|
||||
const headings: NodeListOf<HTMLElement> | [] = article ? article.querySelectorAll("h1, h2, h3, h4, h5, h6") : [];
|
||||
|
||||
headings.forEach((heading, i) => {
|
||||
if (heading instanceof HTMLElement) {
|
||||
const listItem = document.createElement("li");
|
||||
listItem.className = "toc-level-" + heading.tagName.toLowerCase();
|
||||
|
||||
const tempDiv = document.createElement("div");
|
||||
tempDiv.innerHTML =
|
||||
'<svg class="w-0 h-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="#fa5a15"><path stroke-linecap="round" stroke-linejoin="round" d="m12.75 15 3-3m0 0-3-3m3 3h-7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"></svg>';
|
||||
const svg = tempDiv.firstChild;
|
||||
tempDiv.innerHTML = SVG_HTML_STRING;
|
||||
|
||||
li.appendChild(svg as Node);
|
||||
const svg = tempDiv.firstChild;
|
||||
listItem.appendChild(svg as Node);
|
||||
|
||||
const link = document.createElement("a");
|
||||
link.href = "#" + heading.id;
|
||||
link.textContent = heading.textContent;
|
||||
listItem.appendChild(link);
|
||||
|
||||
li.appendChild(link);
|
||||
|
||||
tocList?.appendChild(li);
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
// Helper function to toggle the 'selected' class
|
||||
function setActiveLink(id: string) {
|
||||
document.querySelectorAll("#toc li").forEach((li) => {
|
||||
li.classList.remove("selected");
|
||||
});
|
||||
|
||||
const activeLink = document.querySelector(`#toc a[href="#${id}"]`);
|
||||
if (activeLink) {
|
||||
const li: HTMLElement | null = activeLink.parentElement;
|
||||
li?.classList.add("selected");
|
||||
}
|
||||
}
|
||||
|
||||
// Observe headings and add them to the ToC
|
||||
let headings: NodeListOf<HTMLElement> | [] = article
|
||||
? article.querySelectorAll("h1, h2, h3, h4, h5, h6")
|
||||
: [];
|
||||
|
||||
headings.forEach((heading: Element, i: number) => {
|
||||
if (heading instanceof HTMLElement) {
|
||||
addToC(heading);
|
||||
tocList?.appendChild(listItem);
|
||||
|
||||
gsap.timeline({
|
||||
scrollTrigger: {
|
||||
trigger: heading,
|
||||
start: "top 20%",
|
||||
end: () =>
|
||||
`bottom top+=${i === headings.length - 1 ? 0 : headings[i + 1].getBoundingClientRect().height}`,
|
||||
onEnter: () => setActiveLink(heading.id),
|
||||
onLeaveBack: () =>
|
||||
setActiveLink((headings[i - 1] || { id: null }).id),
|
||||
end: () => `bottom top+=${i === headings.length - 1 ? 0 : (headings[i + 1] as HTMLElement).getBoundingClientRect().height}`,
|
||||
onEnter: () => setActiveLinkById(heading.id),
|
||||
onLeaveBack: () => setActiveLinkById((headings[i - 1] as HTMLElement)?.id),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue