feat(projects): update showcase and support dark thumbnails
This commit is contained in:
45
src/components/ProjectThumbnail.tsx
Normal file
45
src/components/ProjectThumbnail.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { useState, type FC } from 'react';
|
||||
import { useTheme } from '@/context/ThemeContext';
|
||||
|
||||
interface ProjectThumbnailProps {
|
||||
image: string;
|
||||
title: string;
|
||||
priority?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const getDarkImagePath = (image: string) =>
|
||||
image.replace(/(\.[^./]+)$/, '-dark$1');
|
||||
|
||||
export const ProjectThumbnail: FC<ProjectThumbnailProps> = ({
|
||||
image,
|
||||
title,
|
||||
priority = false,
|
||||
className,
|
||||
}) => {
|
||||
const { theme } = useTheme();
|
||||
const darkImage = getDarkImagePath(image);
|
||||
const [unavailableDarkImage, setUnavailableDarkImage] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const useDarkImage = theme === 'dark' && unavailableDarkImage !== darkImage;
|
||||
|
||||
return (
|
||||
<Image
|
||||
key={useDarkImage ? darkImage : image}
|
||||
src={useDarkImage ? darkImage : image}
|
||||
alt={title}
|
||||
fill
|
||||
sizes="(max-width: 650px) 100vw, (max-width: 1100px) 100vw, 360px"
|
||||
priority={priority}
|
||||
draggable={false}
|
||||
className={className}
|
||||
onError={() => {
|
||||
if (useDarkImage) setUnavailableDarkImage(darkImage);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
import { ArrowLeft, ArrowRight, ArrowUpRight } from 'lucide-react';
|
||||
import { projects } from '@/data/projects';
|
||||
import { ProjectThumbnail } from './ProjectThumbnail';
|
||||
import styles from './ProjectsSection.module.css';
|
||||
|
||||
export const ProjectsSection: React.FC = () => {
|
||||
@@ -255,13 +255,10 @@ export const ProjectsSection: React.FC = () => {
|
||||
{projects.map((project) => (
|
||||
<article key={project.id} className={styles.card} aria-label={project.title}>
|
||||
<div className={styles.mediaContainer}>
|
||||
<Image
|
||||
src={project.image}
|
||||
alt={project.title}
|
||||
fill
|
||||
sizes="(max-width: 650px) 100vw, (max-width: 1100px) 100vw, 360px"
|
||||
<ProjectThumbnail
|
||||
image={project.image}
|
||||
title={project.title}
|
||||
priority={project.id === 'qlockify'}
|
||||
draggable={false}
|
||||
className={styles.projectImage}
|
||||
/>
|
||||
</div>
|
||||
@@ -269,12 +266,14 @@ export const ProjectsSection: React.FC = () => {
|
||||
<div className={styles.cardBody}>
|
||||
<div className={styles.cardTitleRow}>
|
||||
<h3 className={styles.cardTitle}>{project.title}</h3>
|
||||
<ArrowUpRight
|
||||
size={19}
|
||||
strokeWidth={2.5}
|
||||
className={styles.titleLinkIcon}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{project.link && (
|
||||
<ArrowUpRight
|
||||
size={19}
|
||||
strokeWidth={2.5}
|
||||
className={styles.titleLinkIcon}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.cardDate}>{project.date}</div>
|
||||
|
||||
Reference in New Issue
Block a user