[#1] Less cluttered projects page

This commit is contained in:
Ahmed Al-Taiar
2024-10-25 17:34:29 -04:00
parent 54a34ef5ee
commit 3a9cc20f86
8 changed files with 80 additions and 126 deletions

View File

@ -53,7 +53,7 @@ const ContactCard = ({ portraitUrl, socials }: ContactCardProps) => {
}}
/>
<div className="flex min-h-[calc(100vh-6rem)] items-center justify-center">
<div className="flex min-h-[calc(100dvh-6rem)] items-center justify-center">
<div className="card bg-base-100 shadow-xl md:card-side">
<figure>
<img

View File

@ -73,7 +73,7 @@ const Project = ({ project }: Props) => {
<h2 className="sm:hidden font-bold text-3xl text-center">Images</h2>
)}
</div>
<div className="flex flex-wrap gap-4 pt-8 justify-center h-fit">
<div className="flex flex-wrap gap-4 pt-8 justify-center h-fit sm:p-8">
{project.images.length > 0 &&
project.images.map((image, i) => (
<a
@ -81,7 +81,7 @@ const Project = ({ project }: Props) => {
target="_blank"
rel="noreferrer"
key={i}
className="rounded-xl"
className="rounded-box"
>
<img src={image} alt="" className="rounded-xl" />
</a>

View File

@ -1,116 +0,0 @@
import { useLayoutEffect, useRef, useState } from 'react'
import { format, isAfter, startOfToday } from 'date-fns'
import parseHtml from 'react-html-parser'
import { FindProjects } from 'types/graphql'
import { Link, routes } from '@redwoodjs/router'
import { calculateLuminance } from 'src/lib/color'
const CARD_WIDTH = 384
const ProjectsShowcase = ({ projects }: FindProjects) => {
const ref = useRef<HTMLDivElement>(null)
const [columns, setColumns] = useState<number>(
Math.max(
Math.floor(ref.current?.getBoundingClientRect().width / CARD_WIDTH),
1
)
)
useLayoutEffect(() => {
const handleResize = () =>
setColumns(
Math.max(
Math.floor(ref.current?.getBoundingClientRect().width / CARD_WIDTH),
1
)
)
handleResize()
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
return (
<div ref={ref} className="flex flex-wrap justify-center gap-2">
{split(
projects
.slice()
.sort((a, b) =>
isAfter(new Date(b.date), new Date(a.date)) ? 1 : -1
),
columns
).map((projectChunk, i) => (
<div className="flex flex-col gap-2" key={i}>
{projectChunk.map((project, j) => (
<Link key={`${i}-${j}`} to={routes.project({ id: project.id })}>
<div className="card card-compact bg-base-100 w-96 h-fit shadow-xl transition-all hover:-translate-y-1 hover:shadow-2xl">
{project.images.length > 0 && (
<img
src={project.images[0]}
alt={`${i}`}
className="object-contain rounded-box size-fit p-2"
/>
)}
<div className="card-body">
<div className="card-title overflow-auto">
<p className="whitespace-nowrap">{project.title}</p>
</div>
<div className="line-clamp-5">
<article className="prose text-sm">
{parseHtml(project.description)}
</article>
</div>
<div className="card-actions justify-between">
<div className="flex gap-2">
{isAfter(new Date(project.date), startOfToday()) && (
<div className="badge badge-info">planned</div>
)}
<div className="badge badge-ghost">
{format(project.date, 'yyyy-MM-dd')}
</div>
</div>
<div className="flex flex-wrap gap-2">
{project.tags.map((tag, i) => (
<div
key={i}
className="badge whitespace-nowrap"
style={{
backgroundColor: tag.color,
color:
calculateLuminance(tag.color) > 0.5
? 'black'
: 'white',
}}
>
{tag.tag}
</div>
))}
</div>
</div>
</div>
</div>
</Link>
))}
</div>
))}
</div>
)
}
export default ProjectsShowcase
function split<T>(arr: T[], chunks: number): T[][] {
const result: T[][] = []
const chunkSize = Math.ceil(arr.length / chunks)
for (let i = 0; i < arr.length; i += chunkSize) {
result.push(arr.slice(i, i + chunkSize))
}
return result
}

View File

@ -11,8 +11,7 @@ import {
import CellEmpty from 'src/components/Cell/CellEmpty/CellEmpty'
import CellFailure from 'src/components/Cell/CellFailure/CellFailure'
import CellLoading from 'src/components/Cell/CellLoading/CellLoading'
import ProjectsShowcase from '../ProjectsShowcase/ProjectsShowcase'
import ProjectsShowcaseList from 'src/components/ProjectsShowcaseList/ProjectsShowcaseList'
export const QUERY: TypedDocumentNode<FindProjects, FindProjectsVariables> =
gql`
@ -52,6 +51,6 @@ export const Success = ({
url: routes.projects(),
}}
/>
<ProjectsShowcase projects={projects} />
<ProjectsShowcaseList projects={projects} />
</>
)

View File

@ -0,0 +1,72 @@
import { format, isAfter, startOfToday } from 'date-fns'
import parseHtml from 'react-html-parser'
import { FindProjects } from 'types/graphql'
import { Link, routes } from '@redwoodjs/router'
import { calculateLuminance } from 'src/lib/color'
const ProjectsShowcaseList = ({ projects }: FindProjects) => (
<div className="flex flex-col gap-4 w-fit mx-auto">
{projects
.slice()
.sort((a, b) => (isAfter(new Date(b.date), new Date(a.date)) ? 1 : -1))
.map((project, i) => (
<Link
key={i}
to={routes.project({ id: project.id })}
className="bg-base-100 flex flex-col sm:flex-row p-2 gap-2 shadow-xl rounded-box hover:shadow-2xl transition-all hover:-translate-y-1 sm:max-h-64 sm:max-w-5xl"
>
{project.images.length > 0 && (
<img
src={project.images[0]}
alt={`${i}`}
className="object-cover rounded-lg sm:max-w-[33.33%]"
/>
)}
<div className="flex flex-col gap-2 p-4">
<div className="card-title overflow-auto">
<p className="whitespace-nowrap">{project.title}</p>
</div>
<div className="line-clamp-5 mb-auto">
<article className="prose text-sm max-w-none">
{parseHtml(project.description)}
</article>
</div>
<div className="card-actions justify-between">
<div className="flex gap-2">
{isAfter(new Date(project.date), startOfToday()) && (
<div className="badge badge-info">planned</div>
)}
<div className="badge badge-ghost">
{format(project.date, 'yyyy-MM-dd')}
</div>
</div>
<div className="flex flex-wrap gap-2">
{project.tags.slice(0, 3).map((tag, i) => (
<div
key={i}
className="badge whitespace-nowrap"
style={{
backgroundColor: tag.color,
color:
calculateLuminance(tag.color) > 0.5 ? 'black' : 'white',
}}
>
{tag.tag}
</div>
))}
{project.tags.length > 3 && (
<div key={i} className="badge badge-ghost whitespace-nowrap">
+{project.tags.length - 3}
</div>
)}
</div>
</div>
</div>
</Link>
))}
</div>
)
export default ProjectsShowcaseList

View File

@ -27,7 +27,7 @@ const ContactPage = () => {
}, [width, height])
return (
<div className="flex min-h-[calc(100vh-6rem)] items-center justify-center">
<div className="flex min-h-[calc(100dvh-6rem)] items-center justify-center">
<ContactCardCell />
</div>
)

View File

@ -21,7 +21,7 @@ const HomePage = () => (
}}
/>
<div className="hero min-h-[calc(100vh-6rem)]">
<div className="hero min-h-[calc(100dvh-6rem)]">
<div className="hero-content flex flex-col gap-8">
<div className="flex flex-col text-center gap-8">
<h1 className="text-3xl sm:text-5xl font-bold">

View File

@ -9,7 +9,7 @@ const ProjectsPage = () => {
<div className="hero-content">
<div className="max-w-md text-center">
<h1 className="text-5xl font-bold">Projects</h1>
<p className="py-6">
<p className="mt-8">
{mobile({
tablet: true,
})
@ -20,7 +20,6 @@ const ProjectsPage = () => {
</div>
</div>
</div>
<ProjectsShowcaseCell />
</>
)