Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
b817b83219
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.
|
- 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.
|
- 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.
|
- 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**:
|
- [x] **Internationalization (i18n) Features**:
|
||||||
- Integrates [Astro’s internationalization (i18n) features](https://docs.astro.build/en/guides/internationalization/).
|
- 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.
|
- Additionally, a custom LanguagePicker component has been developed to facilitate language selection.
|
||||||
|
|
||||||
### Planned Improvements
|
- [x] **Dynamic Table of Contents (ToC) with Scroll Progress Indicator**:
|
||||||
- [ ] Implement a table of contents (ToC) sidebar for blog articles.
|
- 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
|
### 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).
|
- 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 {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
article h2,
|
article h2,
|
||||||
article h3,
|
article h3,
|
||||||
article h4,
|
article h4,
|
||||||
|
@ -91,21 +92,26 @@ const pageTitle: string = `${post.data.title} | ${SITE.title}`;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
scroll-margin-top: 3rem;
|
scroll-margin-top: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
line-height: 1.75rem;
|
line-height: 1.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
font-size: 1.125rem;
|
font-size: 1.125rem;
|
||||||
line-height: 1.75rem;
|
line-height: 1.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes grow-progress {
|
@keyframes grow-progress {
|
||||||
from {
|
from {
|
||||||
transform: scaleX(0);
|
transform: scaleX(0);
|
||||||
|
@ -114,20 +120,24 @@ const pageTitle: string = `${post.data.title} | ${SITE.title}`;
|
||||||
transform: scaleX(1);
|
transform: scaleX(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#progress {
|
#progress {
|
||||||
transform-origin: 0 50%;
|
transform-origin: 0 50%;
|
||||||
animation: grow-progress auto linear;
|
animation: grow-progress auto linear;
|
||||||
animation-timeline: scroll(block root);
|
animation-timeline: scroll(block root);
|
||||||
}
|
}
|
||||||
|
|
||||||
#toc li {
|
#toc li {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
transition: all 300ms var(--transition-cubic);
|
transition: all 300ms var(--transition-cubic);
|
||||||
}
|
}
|
||||||
|
|
||||||
#toc li.selected {
|
#toc li.selected {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#toc li svg {
|
#toc li svg {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
@ -135,6 +145,7 @@ const pageTitle: string = `${post.data.title} | ${SITE.title}`;
|
||||||
height 400ms var(--transition-cubic),
|
height 400ms var(--transition-cubic),
|
||||||
width 400ms var(--transition-cubic);
|
width 400ms var(--transition-cubic);
|
||||||
}
|
}
|
||||||
|
|
||||||
#toc li.selected svg {
|
#toc li.selected svg {
|
||||||
width: 1.25rem;
|
width: 1.25rem;
|
||||||
height: 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 () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// The article element that contains the Markdown content
|
// The article element that contains the Markdown content
|
||||||
const article: HTMLElement | null = document.querySelector("article");
|
const article: HTMLElement | null = document.querySelector("article");
|
||||||
|
|
||||||
// The ToC container <ul> element
|
// The ToC container <ul> element
|
||||||
const tocList: HTMLElement | null = document.querySelector("#toc ul");
|
const tocList: HTMLElement | null = document.querySelector("#toc ul");
|
||||||
|
|
||||||
// Function to create and add an item to the ToC list
|
const headings: NodeListOf<HTMLElement> | [] = article ? article.querySelectorAll("h1, h2, h3, h4, h5, h6") : [];
|
||||||
function addToC(heading: HTMLElement) {
|
|
||||||
const li = document.createElement("li");
|
headings.forEach((heading, i) => {
|
||||||
li.className = "toc-level-" + heading.tagName.toLowerCase();
|
if (heading instanceof HTMLElement) {
|
||||||
|
const listItem = document.createElement("li");
|
||||||
|
listItem.className = "toc-level-" + heading.tagName.toLowerCase();
|
||||||
|
|
||||||
const tempDiv = document.createElement("div");
|
const tempDiv = document.createElement("div");
|
||||||
tempDiv.innerHTML =
|
tempDiv.innerHTML = 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>';
|
|
||||||
const svg = tempDiv.firstChild;
|
|
||||||
|
|
||||||
li.appendChild(svg as Node);
|
const svg = tempDiv.firstChild;
|
||||||
|
listItem.appendChild(svg as Node);
|
||||||
|
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = "#" + heading.id;
|
link.href = "#" + heading.id;
|
||||||
link.textContent = heading.textContent;
|
link.textContent = heading.textContent;
|
||||||
|
listItem.appendChild(link);
|
||||||
|
|
||||||
li.appendChild(link);
|
tocList?.appendChild(listItem);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
gsap.timeline({
|
gsap.timeline({
|
||||||
scrollTrigger: {
|
scrollTrigger: {
|
||||||
trigger: heading,
|
trigger: heading,
|
||||||
start: "top 20%",
|
start: "top 20%",
|
||||||
end: () =>
|
end: () => `bottom top+=${i === headings.length - 1 ? 0 : (headings[i + 1] as HTMLElement).getBoundingClientRect().height}`,
|
||||||
`bottom top+=${i === headings.length - 1 ? 0 : headings[i + 1].getBoundingClientRect().height}`,
|
onEnter: () => setActiveLinkById(heading.id),
|
||||||
onEnter: () => setActiveLink(heading.id),
|
onLeaveBack: () => setActiveLinkById((headings[i - 1] as HTMLElement)?.id),
|
||||||
onLeaveBack: () =>
|
|
||||||
setActiveLink((headings[i - 1] || { id: null }).id),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue