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 ContentBanner from "../../components/ContentBanner";
|
||||
import RelatedArticles from "../../components/RelatedArticles";
|
||||
import AskOrganizer from "../../components/AskOrganizer";
|
||||
import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
|
||||
|
||||
/**
|
||||
@@ -127,6 +128,16 @@ export default async function BlogPostPage({ params }) {
|
||||
relatedPosts={relatedArticles}
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ const ContentThumbnailTemplate = ({
|
||||
return (
|
||||
<Link
|
||||
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 */}
|
||||
<div className="absolute inset-0 z-0">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
@@ -76,9 +76,9 @@ const ContentThumbnailTemplate = ({
|
||||
return (
|
||||
<Link
|
||||
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 */}
|
||||
<div className="absolute inset-0 z-0">
|
||||
{/* 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 [progress, setProgress] = useState(0);
|
||||
const [isMobile, setIsMobile] = useState(true);
|
||||
|
||||
// Auto-advance every 3 seconds
|
||||
// Check if we're on mobile (below lg breakpoint)
|
||||
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(() => {
|
||||
setProgress(0);
|
||||
@@ -22,11 +34,11 @@ export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
|
||||
}, 3000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [filteredPosts.length]);
|
||||
}, [filteredPosts.length, isMobile]);
|
||||
|
||||
// Progress animation
|
||||
// Progress animation (only on mobile)
|
||||
useEffect(() => {
|
||||
if (filteredPosts.length <= 1) return;
|
||||
if (filteredPosts.length <= 1 || !isMobile) return;
|
||||
|
||||
const progressInterval = setInterval(() => {
|
||||
setProgress((prev) => {
|
||||
@@ -38,33 +50,64 @@ export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
|
||||
}, 30); // 30ms intervals for smooth animation
|
||||
|
||||
return () => clearInterval(progressInterval);
|
||||
}, [currentIndex, filteredPosts.length]);
|
||||
}, [currentIndex, filteredPosts.length, isMobile]);
|
||||
|
||||
if (filteredPosts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="py-[var(--spacing-scale-032)]">
|
||||
<div className="flex flex-col gap-[var(--spacing-scale-032)]">
|
||||
<h2 className="text-[32px] leading-[110%] font-medium text-[var(--color-content-inverse-primary)] text-center">
|
||||
<section className="py-[var(--spacing-scale-032)] lg:py-[var(--spacing-scale-064)]">
|
||||
<div className="flex flex-col gap-[var(--spacing-scale-032)] lg:gap-[51px]">
|
||||
<h2 className="text-[32px] lg:text-[44px] leading-[110%] font-medium text-[var(--color-content-inverse-primary)] text-center">
|
||||
Related Articles
|
||||
</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 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={{
|
||||
transform: `translateX(calc(50% - 130px - ${
|
||||
currentIndex * 260
|
||||
}px))`,
|
||||
transform: isMobile
|
||||
? `translateX(calc(50% - 130px - ${currentIndex * 260}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) => (
|
||||
<div
|
||||
key={relatedPost.slug}
|
||||
className="flex flex-col items-center"
|
||||
className="flex flex-col items-center flex-shrink-0"
|
||||
>
|
||||
<ContentThumbnailTemplate
|
||||
post={relatedPost}
|
||||
@@ -75,27 +118,29 @@ export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Three separate progress bars below the carousel */}
|
||||
<div className="flex justify-center gap-[var(--measures-spacing-008)] px-[var(--measures-spacing-064)]">
|
||||
{filteredPosts.map((relatedPost, index) => (
|
||||
<div
|
||||
key={relatedPost.slug}
|
||||
className="max-w-[var(--measures-spacing-056)] w-full h-[var(--measures-spacing-004)] bg-gray-200 rounded-full overflow-hidden"
|
||||
>
|
||||
{/* Progress bars - only show on mobile */}
|
||||
{isMobile && (
|
||||
<div className="flex justify-center gap-[var(--measures-spacing-008)] px-[var(--measures-spacing-064)]">
|
||||
{filteredPosts.map((relatedPost, index) => (
|
||||
<div
|
||||
className="h-full bg-gray-600 rounded-full transition-all duration-75 ease-linear"
|
||||
style={{
|
||||
width:
|
||||
index === currentIndex
|
||||
? `${progress}%`
|
||||
: index < currentIndex
|
||||
? "100%"
|
||||
: "0%",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
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
|
||||
className="h-full bg-gray-600 rounded-full transition-all duration-75 ease-linear"
|
||||
style={{
|
||||
width:
|
||||
index === currentIndex
|
||||
? `${progress}%`
|
||||
: index < currentIndex
|
||||
? "100%"
|
||||
: "0%",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
@source "../.storybook/**/*";
|
||||
@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 {
|
||||
/* Custom breakpoints */
|
||||
--breakpoint-xsm: 429px;
|
||||
|
||||
Reference in New Issue
Block a user