Related Articles lg breakpoint implemented
This commit is contained in:
@@ -3,6 +3,7 @@ import Link from "next/link";
|
|||||||
import { getBlogPostBySlug, getAllPosts } from "../../../lib/contentProcessor";
|
import { getBlogPostBySlug, getAllPosts } from "../../../lib/contentProcessor";
|
||||||
import ContentBanner from "../../components/ContentBanner";
|
import ContentBanner from "../../components/ContentBanner";
|
||||||
import RelatedArticles from "../../components/RelatedArticles";
|
import RelatedArticles from "../../components/RelatedArticles";
|
||||||
|
import AskOrganizer from "../../components/AskOrganizer";
|
||||||
import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
|
import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,6 +128,16 @@ export default async function BlogPostPage({ params }) {
|
|||||||
relatedPosts={relatedArticles}
|
relatedPosts={relatedArticles}
|
||||||
currentPostSlug={post.slug}
|
currentPostSlug={post.slug}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Ask Organizer Section */}
|
||||||
|
<AskOrganizer
|
||||||
|
title="Have questions about this topic?"
|
||||||
|
subtitle="Get help from experienced organizers"
|
||||||
|
description="Our community organizers are here to help you implement these strategies in your own community. Reach out for personalized guidance and support."
|
||||||
|
buttonText="Ask an organizer"
|
||||||
|
buttonHref="/contact"
|
||||||
|
variant="centered"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,9 @@ const ContentThumbnailTemplate = ({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={`/blog/${post.slug}`}
|
href={`/blog/${post.slug}`}
|
||||||
className={`block group transition-transform duration-300 hover:scale-105 ${className}`}
|
className={`block transition-transform duration-200 hover:scale-[1.02] ${className}`}
|
||||||
>
|
>
|
||||||
<div className="relative w-[260px] h-[390px] overflow-hidden rounded-lg shadow-lg pt-[18px] pl-[18px] pr-[42px] pb-[212px]">
|
<div className="relative w-[260px] h-[390px] overflow-hidden pt-[18px] pl-[18px] pr-[42px] pb-[212px]">
|
||||||
{/* Background SVG - sized to fit the 260x390 container exactly */}
|
{/* Background SVG - sized to fit the 260x390 container exactly */}
|
||||||
<div className="absolute inset-0 z-0">
|
<div className="absolute inset-0 z-0">
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
@@ -76,9 +76,9 @@ const ContentThumbnailTemplate = ({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={`/blog/${post.slug}`}
|
href={`/blog/${post.slug}`}
|
||||||
className={`block group transition-transform duration-300 hover:scale-105 ${className}`}
|
className={`block transition-transform duration-200 hover:scale-[1.02] ${className}`}
|
||||||
>
|
>
|
||||||
<div className="relative w-[320px] h-[225.5px] overflow-hidden rounded-lg shadow-lg pt-[13.75px] pr-[76px] pb-[73.75px] pl-[14px]">
|
<div className="relative w-[320px] h-[225.5px] overflow-hidden pt-[13.75px] pr-[76px] pb-[73.75px] pl-[14px]">
|
||||||
{/* Background SVG - sized to fit the 320x225.5 container exactly */}
|
{/* Background SVG - sized to fit the 320x225.5 container exactly */}
|
||||||
<div className="absolute inset-0 z-0">
|
<div className="absolute inset-0 z-0">
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
|
|||||||
@@ -11,10 +11,22 @@ export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
|
|||||||
|
|
||||||
const [currentIndex, setCurrentIndex] = useState(0);
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
const [progress, setProgress] = useState(0);
|
const [progress, setProgress] = useState(0);
|
||||||
|
const [isMobile, setIsMobile] = useState(true);
|
||||||
|
|
||||||
// Auto-advance every 3 seconds
|
// Check if we're on mobile (below lg breakpoint)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (filteredPosts.length <= 1) return;
|
const checkScreenSize = () => {
|
||||||
|
setIsMobile(window.innerWidth < 1024); // lg breakpoint is 1024px
|
||||||
|
};
|
||||||
|
|
||||||
|
checkScreenSize();
|
||||||
|
window.addEventListener("resize", checkScreenSize);
|
||||||
|
return () => window.removeEventListener("resize", checkScreenSize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Auto-advance every 3 seconds (only on mobile)
|
||||||
|
useEffect(() => {
|
||||||
|
if (filteredPosts.length <= 1 || !isMobile) return;
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
@@ -22,11 +34,11 @@ export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [filteredPosts.length]);
|
}, [filteredPosts.length, isMobile]);
|
||||||
|
|
||||||
// Progress animation
|
// Progress animation (only on mobile)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (filteredPosts.length <= 1) return;
|
if (filteredPosts.length <= 1 || !isMobile) return;
|
||||||
|
|
||||||
const progressInterval = setInterval(() => {
|
const progressInterval = setInterval(() => {
|
||||||
setProgress((prev) => {
|
setProgress((prev) => {
|
||||||
@@ -38,33 +50,64 @@ export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
|
|||||||
}, 30); // 30ms intervals for smooth animation
|
}, 30); // 30ms intervals for smooth animation
|
||||||
|
|
||||||
return () => clearInterval(progressInterval);
|
return () => clearInterval(progressInterval);
|
||||||
}, [currentIndex, filteredPosts.length]);
|
}, [currentIndex, filteredPosts.length, isMobile]);
|
||||||
|
|
||||||
if (filteredPosts.length === 0) {
|
if (filteredPosts.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="py-[var(--spacing-scale-032)]">
|
<section className="py-[var(--spacing-scale-032)] lg:py-[var(--spacing-scale-064)]">
|
||||||
<div className="flex flex-col gap-[var(--spacing-scale-032)]">
|
<div className="flex flex-col gap-[var(--spacing-scale-032)] lg:gap-[51px]">
|
||||||
<h2 className="text-[32px] leading-[110%] font-medium text-[var(--color-content-inverse-primary)] text-center">
|
<h2 className="text-[32px] lg:text-[44px] leading-[110%] font-medium text-[var(--color-content-inverse-primary)] text-center">
|
||||||
Related Articles
|
Related Articles
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{/* Horizontal Articles Row - All Visible with Centering */}
|
{/* Horizontal Articles Row - Carousel on mobile, Scrollable slider on desktop */}
|
||||||
<div className="flex justify-center overflow-hidden">
|
<div className="flex justify-center overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="flex gap-0 transition-transform duration-500 ease-in-out"
|
className={`flex gap-0 transition-transform duration-500 ease-in-out ${
|
||||||
|
!isMobile
|
||||||
|
? "overflow-x-auto scrollbar-hide cursor-grab active:cursor-grabbing"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
style={{
|
style={{
|
||||||
transform: `translateX(calc(50% - 130px - ${
|
transform: isMobile
|
||||||
currentIndex * 260
|
? `translateX(calc(50% - 130px - ${currentIndex * 260}px))`
|
||||||
}px))`,
|
: "none",
|
||||||
|
scrollBehavior: !isMobile ? "smooth" : "auto",
|
||||||
}}
|
}}
|
||||||
|
onMouseDown={
|
||||||
|
!isMobile
|
||||||
|
? (e) => {
|
||||||
|
const slider = e.currentTarget;
|
||||||
|
const startX = e.pageX - slider.offsetLeft;
|
||||||
|
const scrollLeft = slider.scrollLeft;
|
||||||
|
|
||||||
|
const handleMouseMove = (e) => {
|
||||||
|
const x = e.pageX - slider.offsetLeft;
|
||||||
|
const walk = (x - startX) * 2;
|
||||||
|
slider.scrollLeft = scrollLeft - walk;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
document.removeEventListener(
|
||||||
|
"mousemove",
|
||||||
|
handleMouseMove
|
||||||
|
);
|
||||||
|
document.removeEventListener("mouseup", handleMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", handleMouseMove);
|
||||||
|
document.addEventListener("mouseup", handleMouseUp);
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{filteredPosts.map((relatedPost, index) => (
|
{filteredPosts.map((relatedPost, index) => (
|
||||||
<div
|
<div
|
||||||
key={relatedPost.slug}
|
key={relatedPost.slug}
|
||||||
className="flex flex-col items-center"
|
className="flex flex-col items-center flex-shrink-0"
|
||||||
>
|
>
|
||||||
<ContentThumbnailTemplate
|
<ContentThumbnailTemplate
|
||||||
post={relatedPost}
|
post={relatedPost}
|
||||||
@@ -75,27 +118,29 @@ export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Three separate progress bars below the carousel */}
|
{/* Progress bars - only show on mobile */}
|
||||||
<div className="flex justify-center gap-[var(--measures-spacing-008)] px-[var(--measures-spacing-064)]">
|
{isMobile && (
|
||||||
{filteredPosts.map((relatedPost, index) => (
|
<div className="flex justify-center gap-[var(--measures-spacing-008)] px-[var(--measures-spacing-064)]">
|
||||||
<div
|
{filteredPosts.map((relatedPost, index) => (
|
||||||
key={relatedPost.slug}
|
|
||||||
className="max-w-[var(--measures-spacing-056)] w-full h-[var(--measures-spacing-004)] bg-gray-200 rounded-full overflow-hidden"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className="h-full bg-gray-600 rounded-full transition-all duration-75 ease-linear"
|
key={relatedPost.slug}
|
||||||
style={{
|
className="max-w-[var(--measures-spacing-056)] w-full h-[var(--measures-spacing-004)] bg-gray-200 rounded-full overflow-hidden"
|
||||||
width:
|
>
|
||||||
index === currentIndex
|
<div
|
||||||
? `${progress}%`
|
className="h-full bg-gray-600 rounded-full transition-all duration-75 ease-linear"
|
||||||
: index < currentIndex
|
style={{
|
||||||
? "100%"
|
width:
|
||||||
: "0%",
|
index === currentIndex
|
||||||
}}
|
? `${progress}%`
|
||||||
/>
|
: index < currentIndex
|
||||||
</div>
|
? "100%"
|
||||||
))}
|
: "0%",
|
||||||
</div>
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,6 +6,15 @@
|
|||||||
@source "../.storybook/**/*";
|
@source "../.storybook/**/*";
|
||||||
@source "./**/*";
|
@source "./**/*";
|
||||||
|
|
||||||
|
/* Hide scrollbar utility */
|
||||||
|
.scrollbar-hide {
|
||||||
|
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
}
|
||||||
|
.scrollbar-hide::-webkit-scrollbar {
|
||||||
|
display: none; /* Safari and Chrome */
|
||||||
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
/* Custom breakpoints */
|
/* Custom breakpoints */
|
||||||
--breakpoint-xsm: 429px;
|
--breakpoint-xsm: 429px;
|
||||||
|
|||||||
Reference in New Issue
Block a user