Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: update the blog page #110 #116

Merged
merged 1 commit into from
Nov 9, 2024
Merged
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: 16 additions & 19 deletions src/components/blog-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Post } from '@/types/posts';
import Image from 'next/image';
import Link from 'next/link';
import React from 'react';
import { Button } from './ui/button';

interface BlogGridProps {
posts: Post[];
Expand All @@ -11,30 +10,33 @@ interface BlogGridProps {
const BlogGrid = ({ posts }: BlogGridProps) => {
return (
<>
<ul className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8 mt-8">
<ul className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8 mt-8 pb-20">
{posts?.slice(1)?.map((post) => (
<li
key={post.id}
className="bg-white rounded-lg shadow-lg hover:shadow-2xl transform transition-transform duration-300 hover:-translate-y-1 overflow-hidden"
className="bg-white text-black dark:bg-neutral-950 relative overflow-hidden rounded-md shadow-xl dark:shadow-lg dark:shadow-zinc-900 transition-transform duration-300 transform hover:-translate-y-1 cursor-pointer group h-[450px]"
>
<Link href={`/blogs/${post.slug}`} className="block">
<div className="relative">
<Link
href={`/blogs/${post.slug}`}
className=" h-full flex flex-col"
>
<div className="relative aspect-w-16 aspect-h-9 rounded-t-md overflow-hidden h-52 ">
<Image
width={400}
height={250}
className="object-cover w-full h-48"
className="object-contain w-full h-full transition-transform duration-300 group-hover:scale-105"
src={post.coverImage.url}
alt={post.title}
/>
<div className="absolute top-2 left-2 bg-gradient-to-r from-purple-500 to-pink-500 text-white rounded-full p-2">
<span className="text-lg">➔</span>
</div>
<div className="absolute inset-0 dark:bg-black opacity-10 transition-opacity duration-300 group-hover:opacity-70"></div>
</div>
<div className="p-4">
<h3 className="text-xl font-semibold text-gray-800">
{post.title}
</h3>
<p className="text-gray-500 mt-2 text-sm">
<div className="p-6 flex flex-col justify-between flex-grow">
<div>
<h3 className="font-light text-sm md:text-xl text-black dark:text-gray-50 group-hover:text-indigo-400 transition-colors duration-300 antialiased">
{post.title}
</h3>
</div>
<p className="text-xs font-extralight text-gray-700 dark:text-gray-400 mt-auto self-end antialiased">
{new Date(post.publishedAt).toLocaleDateString('en-us', {
weekday: 'long',
year: 'numeric',
Expand All @@ -43,11 +45,6 @@ const BlogGrid = ({ posts }: BlogGridProps) => {
})}
</p>
</div>
<div className="p-4">
<Button className="py-2 px-4 bg-gray-800 text-white rounded-lg hover:bg-gray-700 transition-colors">
Read More
</Button>
</div>
</Link>
</li>
))}
Expand Down
17 changes: 11 additions & 6 deletions src/components/blog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { fetchBlogPosts } from '@/utils/hashnode';
import Container from '@/components/Container';
import FeaturedBlogPost from './featured-blog-post';
import BlogGrid from './blog-grid';
import { Lens } from './ui/lens';

export async function Blog() {
const publication = await fetchBlogPosts();
Expand All @@ -13,22 +14,26 @@ export async function Blog() {
const posts: Array<Post> = publication.posts.edges.map(
({ node }: { node: Post }) => node,
);

return (
<Container className="max-w-6xl mx-auto space-y-16">
<div className="text-center mt-16">
<h1 className="text-5xl font-bold mb-4">
<h1 className="text-5xl font-bold mb-4 relative before:absolute before:inset-x-0 before:top-0 before:h-8 before:bg-gradient-to-b before:from-white before:to-transparent dark:before:from-black">
Insights for Building Web Applications
</h1>
<p className="text-lg text-gray-500">
Dive deep into the latest trends, tips, and techniques for creating
powerful, user-focused web applications.
</p>
</div>
<FeaturedBlogPost
title={posts[0].title}
slug={posts[0].slug}
imageUrl={posts[0].coverImage.url}
/>

<Lens zoomFactor={1.5} lensSize={200}>
<FeaturedBlogPost
title={posts[0].title}
slug={posts[0].slug}
imageUrl={posts[0].coverImage.url}
/>
</Lens>

<BlogGrid posts={posts} />
</Container>
Expand Down
8 changes: 5 additions & 3 deletions src/components/featured-blog-post.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import Link from 'next/link';
const FeaturedBlogPost = ({ title, slug, imageUrl }: BlogParams) => {
return (
<div className="relative flex flex-col md:flex-row gap-8 items-center text-white rounded-lg p-8 shadow-lg overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-r from-purple-500 to-pink-500 opacity-70 animate-liquid"></div>
<div className="absolute inset-0 bg-gradient-to-b from-[#454545] to-[#ffffff] dark:from-[#434343] dark:to-[#000000] opacity-70 animate-liquid"></div>
<div className="w-full md:w-1/2 relative z-10 text-center md:text-left">
<h2 className="text-2xl font-semibold">{title}</h2>
<h2 className="text-2xl text-slate-800 dark:text-white font-semibold">
{title}
</h2>
<Link href={`/blogs/${slug}`}>
<Button className="mt-4 py-2 px-4 bg-white text-purple-600 font-semibold rounded-lg hover:bg-gray-100">
<Button className="mt-4 py-2 px-4 bg-white text-pink-700 font-normal antialiased rounded-lg hover:bg-gray-100">
Read More
</Button>
</Link>
Expand Down
127 changes: 127 additions & 0 deletions src/components/ui/lens.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
'use client';

import React, { useRef, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';

interface LensProps {
children: React.ReactNode;
zoomFactor?: number;
lensSize?: number;
position?: {
x: number;
y: number;
};
isStatic?: boolean;
isFocusing?: () => void;
hovering?: boolean;
// eslint-disable-next-line
setHovering?: (hovering: boolean) => void;
}

export const Lens: React.FC<LensProps> = ({
children,
zoomFactor = 1.5,
lensSize = 170,
isStatic = false,
position = { x: 200, y: 150 },
hovering,
setHovering,
}) => {
const containerRef = useRef<HTMLDivElement>(null);

const [localIsHovering, setLocalIsHovering] = useState(false);

const isHovering = hovering !== undefined ? hovering : localIsHovering;
const setIsHovering = setHovering || setLocalIsHovering;

// const [isHovering, setIsHovering] = useState(false);
const [mousePosition, setMousePosition] = useState({ x: 100, y: 100 });

const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
const rect = e.currentTarget.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
setMousePosition({ x, y });
};

return (
<div
ref={containerRef}
className="relative overflow-hidden rounded-lg z-20"
onMouseEnter={() => {
setIsHovering(true);
}}
onMouseLeave={() => setIsHovering(false)}
onMouseMove={handleMouseMove}
>
{children}

{isStatic ? (
<div>
<motion.div
initial={{ opacity: 0, scale: 0.58 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.3, ease: 'easeOut' }}
className="absolute inset-0 overflow-hidden"
style={{
maskImage: `radial-gradient(circle ${lensSize / 2}px at ${
position.x
}px ${position.y}px, black 100%, transparent 100%)`,
WebkitMaskImage: `radial-gradient(circle ${lensSize / 2}px at ${
position.x
}px ${position.y}px, black 100%, transparent 100%)`,
transformOrigin: `${position.x}px ${position.y}px`,
}}
>
<div
className="absolute inset-0"
style={{
transform: `scale(${zoomFactor})`,
transformOrigin: `${position.x}px ${position.y}px`,
}}
>
{children}
</div>
</motion.div>
</div>
) : (
<AnimatePresence>
{isHovering && (
<div>
<motion.div
initial={{ opacity: 0, scale: 0.58 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.3, ease: 'easeOut' }}
className="absolute inset-0 overflow-hidden"
style={{
maskImage: `radial-gradient(circle ${lensSize / 2}px at ${
mousePosition.x
}px ${mousePosition.y}px, black 100%, transparent 100%)`,
WebkitMaskImage: `radial-gradient(circle ${
lensSize / 2
}px at ${mousePosition.x}px ${
mousePosition.y
}px, black 100%, transparent 100%)`,
transformOrigin: `${mousePosition.x}px ${mousePosition.y}px`,
zIndex: 50,
}}
>
<div
className="absolute inset-0"
style={{
transform: `scale(${zoomFactor})`,
transformOrigin: `${mousePosition.x}px ${mousePosition.y}px`,
}}
>
{children}
</div>
</motion.div>
</div>
)}
</AnimatePresence>
)}
</div>
);
};
Loading