Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/components/articles/ArticleSuggestion.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Card from "./Card";

function ArticleCard() {
const articles = [
{
img: "https://images.unsplash.com/photo-1461749280684-dccba630e2f6?auto=format&fit=crop&w=600&q=80",
title: "Understanding React Context API",
description: "A deep dive into the React Context API and how to use it effectively in your applications. The Context API is a powerful feature in React that allows you to share state across the entire app lightly and with ease. In this article, we will explore its use cases, implementation, and best practices for scalable applications.",
author: "Alex Johnson",
readTime: "5 min read",
link: "#",
},
{
img: "https://images.unsplash.com/photo-1519389950473-47ba0277781c?auto=format&fit=crop&w=600&q=80",
title: "Top 10 JavaScript ES6 Features",
description: "Explore the most important ES6 features every JavaScript developer should know. From arrow functions to destructuring, ES6 brought a lot of improvements to the language. This article covers the top features and how they can improve your code.",
author: "Priya Singh",
readTime: "7 min read",
link: "#",
},
{
img: "https://images.unsplash.com/photo-1506744038136-46273834b3fb?auto=format&fit=crop&w=600&q=80",
title: "Mastering CSS Grid Layout",
description: "Learn how to build complex and responsive layouts easily using CSS Grid. CSS Grid is a two-dimensional layout system that enables developers to create web layouts quickly and efficiently. This guide will help you master the basics and advanced techniques.",
author: "Samuel Lee",
readTime: "6 min read",
link: "#",
},
];


return <Card articles={articles} />;
}

export default ArticleCard;
165 changes: 165 additions & 0 deletions src/components/articles/Card.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React, { useState, useEffect, useRef } from "react";
import { Link } from "react-router-dom";
import {
FaRegClock,
FaRegThumbsUp,
FaRegThumbsDown,
FaShareAlt,
FaBookmark,
FaChevronDown,
FaChevronUp,
} from "react-icons/fa";
import { motion as Motion, AnimatePresence } from "framer-motion";

function Card({ articles }) {
const [expanded, setExpanded] = useState(() =>
Array(articles.length).fill(false)
);
const descRefs = useRef([]);

useEffect(() => {
setExpanded(Array(articles.length).fill(false));
}, [articles]);

const toggleExpand = (idx) => {
setExpanded((prev) =>
prev.map((val, i) => (i === idx ? !val : val))
);
setTimeout(() => {
if (descRefs.current[idx]) {
descRefs.current[idx].scrollIntoView({ behavior: "smooth", block: "center" });
}
}, 200);
};

return (
<div className="container mx-auto px-4 py-8">
<h2 className="text-3xl font-bold mb-6 text-center text-[#00ff00] font-mono tracking-tight">
Latest Articles
</h2>

<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{articles.map((article, idx) => {
const isLong = article.description.length > 120;
const showFull = expanded[idx];
const preview = isLong
? article.description.slice(0, 120) + "..."
: article.description;

return (
<Motion.div
key={idx}
className="bg-[#1a1a1a] text-[#cccccc] rounded-xl shadow-lg overflow-hidden border border-[#333] hover:border-[#00ff00] hover:shadow-green-500/10 transition-all duration-300 font-mono"
whileHover={{ scale: 1.02 }}
transition={{ type: "spring", stiffness: 200, damping: 15 }}
>
<img
src={article.img}
alt={article.title}
className="w-full h-48 object-cover border-b-2 border-[#222]"
/>

<div className="p-5">
<h3 className="text-lg font-semibold mb-2 text-[#00ff00] font-mono">
{article.title}
</h3>

{/* Description section with animation */}
<AnimatePresence initial={false}>
<Motion.p
ref={el => descRefs.current[idx] = el}
key={showFull ? `expanded-${idx}` : `collapsed-${idx}`}
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.3 }}
className="text-[#cccccc] mb-2 overflow-hidden"
>
{showFull ? article.description : preview}
</Motion.p>
</AnimatePresence>

{isLong && (
<button
onClick={() => toggleExpand(idx)}
aria-expanded={showFull}
className="text-[#00ff00] hover:text-[#1aff66] font-mono text-xs inline-flex items-center gap-1 focus:outline-none mb-3"
>
{showFull ? (
<>
Show less <FaChevronUp className="inline-block" />
</>
) : (
<>
Read more <FaChevronDown className="inline-block" />
</>
)}
</button>
)}

{/* Author and time */}
<div className="flex items-center text-xs text-gray-400 mb-3 flex-wrap gap-2">
<span>
By{" "}
<span className="text-[#1aff66] font-semibold">
{article.author}
</span>
</span>
<div className="flex items-center gap-1">
<FaRegClock /> <span>{article.readTime}</span>
</div>
</div>

{/* Footer actions */}
<div className="flex justify-between items-center">
<Link
to={article.link}
className="text-[#00ff00] hover:text-[#1aff66] border-b border-dashed border-[#00ff00] hover:border-[#1aff66] transition-colors duration-200 font-mono text-sm"
>
Go to Article
</Link>

<div className="flex space-x-3">
<Motion.button
whileTap={{ scale: 0.9 }}
className="text-gray-400 hover:text-[#00ff00] transition-colors duration-200"
title="Like"
>
<FaRegThumbsUp />
</Motion.button>

<Motion.button
whileTap={{ scale: 0.9 }}
className="text-gray-400 hover:text-red-500 transition-colors duration-200"
title="Dislike"
>
<FaRegThumbsDown />
</Motion.button>

<Motion.button
whileTap={{ scale: 0.9 }}
className="text-gray-400 hover:text-[#00ff00] transition-colors duration-200"
title="Share"
>
<FaShareAlt />
</Motion.button>

<Motion.button
whileTap={{ scale: 0.9 }}
className="text-gray-400 hover:text-yellow-400 transition-colors duration-200"
title="Bookmark"
>
<FaBookmark />
</Motion.button>
</div>
</div>
</div>
</Motion.div>
);
})}
</div>
</div>
);
}

export default Card;
5 changes: 4 additions & 1 deletion src/pages/LandingPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import About from "../components/ui/About";
import Terminal from "../components/ui/Terminal";
import Community from "../components/ui/Community";
import Footer from "../components/Footer";
import ArticleCard from "../components/articles/ArticleSuggestion";

// modularized the code for readablity as requested in the issue
const LandingPage = () => {
Expand All @@ -12,12 +13,14 @@ const LandingPage = () => {
<Hero />
{/* about section */}
<About />
{/* articles section */}
<ArticleCard />
{/* hacktoberfest terminal section */}
<Terminal />
{/* community section */}
<Community />
{/* future footer component will be added here */}
<Footer/>
<Footer />
</div>
);
};
Expand Down