Skip to content

Commit 3ee7198

Browse files
Merge branch 'main' into main
2 parents 3dff83c + 1bec407 commit 3ee7198

17 files changed

Lines changed: 556 additions & 175 deletions

File tree

docusaurus.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,4 @@ const config: Config = {
308308
},
309309
};
310310

311-
export default config;
311+
export default config;

src/components/blogCarousel/blogCard.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import React from "react";
33
import Link from "@docusaurus/Link";
44
import { getAuthorProfiles } from "../../utils/authors";
5+
import { usePluginData } from "@docusaurus/useGlobalData";
56

67
interface BlogCardProps {
78
type: string;
@@ -13,6 +14,7 @@ interface BlogCardProps {
1314
authors?: string[];
1415
tags?: string[];
1516
category?: string;
17+
readingTime?: number;
1618
}
1719

1820
const TAG_COLORS = [
@@ -48,13 +50,16 @@ const BlogCard = ({
4850
type,
4951
date,
5052
title,
53+
content,
5154
imageUrl,
5255
id,
5356
authors,
5457
tags,
5558
category,
59+
readingTime,
5660
}: BlogCardProps) => {
5761
const authorProfiles = getAuthorProfiles(authors || []);
62+
const computedReadTime = readingTime ?? 1;
5863

5964
if (!id || !type) {
6065
return <div>data not fetched properly, imageId and entryId not found</div>;
@@ -167,6 +172,24 @@ const BlogCard = ({
167172
</div>
168173
)}
169174
{date && <span className="card-date">{formatDate(date)}</span>}
175+
<span className="card-reading-time">
176+
<svg
177+
xmlns="http://www.w3.org/2000/svg"
178+
viewBox="0 0 24 24"
179+
width="11"
180+
height="11"
181+
fill="none"
182+
stroke="currentColor"
183+
strokeWidth="2"
184+
strokeLinecap="round"
185+
strokeLinejoin="round"
186+
aria-hidden="true"
187+
>
188+
<circle cx="12" cy="12" r="10" />
189+
<polyline points="12 6 12 12 16 14" />
190+
</svg>
191+
{computedReadTime} min read
192+
</span>
170193
</div>
171194
</div>
172195

src/components/blogCarousel/blogCarousel.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,4 +634,13 @@
634634
opacity: 0;
635635
transform: translateY(-20px);
636636
transition: opacity 0.4s ease-in, transform 0.4s ease-in;
637+
}
638+
/* ── Reading time ── */
639+
.card-reading-time {
640+
display: inline-flex;
641+
align-items: center;
642+
gap: 3px;
643+
font-size: 0.75rem;
644+
color: var(--ifm-color-emphasis-600);
645+
margin-top: 2px;
637646
}

src/components/blogCarousel/blogCarousel.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export function BlogCarousel() {
9090
authors={blog.authors}
9191
tags={blog.tags}
9292
category={blog.category}
93+
readingTime={blog.readingTime}
9394
/>
9495
</CarouselItem>
9596
))}
Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import React, { useState, useEffect } from "react";
2-
import { FaArrowUp } from "react-icons/fa";
2+
import { FaArrowDown, FaArrowUp } from "react-icons/fa";
33

44
export default function ScrollBottomToTop() {
5-
const [isVisible, setIsVisible] = useState(false);
6-
7-
const toggleVisibility = () => {
8-
if (window.pageYOffset > 300) {
9-
setIsVisible(true);
10-
} else {
11-
setIsVisible(false);
12-
}
5+
const [showTopButton, setShowTopButton] = useState(false);
6+
const [showBottomButton, setShowBottomButton] = useState(false);
7+
8+
const updateVisibility = () => {
9+
const scrollTop = window.pageYOffset;
10+
const documentHeight = document.documentElement.scrollHeight;
11+
const viewportHeight = window.innerHeight;
12+
13+
setShowTopButton(scrollTop > 300);
14+
setShowBottomButton(scrollTop + viewportHeight < documentHeight - 300);
1315
};
1416

1517
const scrollToTop = () => {
@@ -19,26 +21,50 @@ export default function ScrollBottomToTop() {
1921
});
2022
};
2123

24+
const scrollToBottom = () => {
25+
window.scrollTo({
26+
top: document.documentElement.scrollHeight,
27+
behavior: "smooth",
28+
});
29+
};
30+
2231
useEffect(() => {
23-
window.addEventListener("scroll", toggleVisibility);
32+
updateVisibility();
33+
window.addEventListener("scroll", updateVisibility);
34+
window.addEventListener("resize", updateVisibility);
2435

2536
return () => {
26-
window.removeEventListener("scroll", toggleVisibility);
37+
window.removeEventListener("scroll", updateVisibility);
38+
window.removeEventListener("resize", updateVisibility);
2739
};
2840
}, []);
2941

42+
if (!showTopButton && !showBottomButton) {
43+
return null;
44+
}
45+
3046
return (
31-
<div className="scroll-to-top">
32-
{isVisible && (
47+
<div className="scroll-to-top fixed right-5 bottom-5 z-50 flex flex-col gap-3">
48+
{showTopButton && (
3349
<button
34-
aria-label="Scroll to top"
50+
aria-label="Scroll to top"
3551
onClick={scrollToTop}
36-
className="fixed right-5 bottom-5 z-50 cursor-pointer rounded-full border-none bg-gray-700 p-3 text-white shadow-lg transition-opacity duration-300 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-700"
52+
className="cursor-pointer rounded-full border-none bg-gray-700 p-3 text-white shadow-lg transition-opacity duration-300 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-700"
3753
style={{ backgroundColor: "var(--ifm-color-primary)" }}
3854
>
3955
<FaArrowUp />
4056
</button>
4157
)}
58+
{showBottomButton && (
59+
<button
60+
aria-label="Scroll to bottom"
61+
onClick={scrollToBottom}
62+
className="cursor-pointer rounded-full border-none bg-gray-700 p-3 text-white shadow-lg transition-opacity duration-300 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-700"
63+
style={{ backgroundColor: "var(--ifm-color-primary)" }}
64+
>
65+
<FaArrowDown />
66+
</button>
67+
)}
4268
</div>
4369
);
4470
}

0 commit comments

Comments
 (0)