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

Johnny/won/nick/photo carousel 2 #178

Merged
merged 3 commits into from
Apr 29, 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
28 changes: 28 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
"react-datepicker": "^6.1.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.48.2",
"react-loading-skeleton": "^3.4.0",
"react-multi-carousel": "^2.8.5",
"uuidv4": "^6.2.13",
"yarn": "^1.22.19",
"zod": "^3.22.4"
Expand All @@ -46,6 +48,7 @@
"@types/react": "18.0.14",
"@types/react-datepicker": "^6.0.1",
"@types/react-dom": "18.0.5",
"@types/react-image-gallery": "^1.2.4",
"@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0",
"autoprefixer": "^10.4.7",
Expand Down
2 changes: 2 additions & 0 deletions postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module.exports = {
plugins: {
"postcss-import": {},
"tailwindcss/nesting": {},
tailwindcss: {},
autoprefixer: {},
},
Expand Down
2 changes: 1 addition & 1 deletion src/app/public/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const AboutLayout = () => {
};
}, []);
return (
<div className="flex-col justify-center gap-y-4 py-6 sm:flex sm:w-full sm:px-0">
<div className="flex flex-col justify-center gap-y-4 py-6 sm:w-full sm:px-0">
<span className="pt-6 text-center text-4xl font-semibold sm:text-left">
Our Story
</span>
Expand Down
2 changes: 1 addition & 1 deletion src/app/public/team/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const PublicLayout = () => {
<span className="pt-6 text-center text-4xl font-semibold sm:text-left">
Meet TLP
</span>
<p className="mt-7 leading-7">
<p className="mb-4 mt-7 leading-7">
The Legacy Project, Inc. (TLP) connects college students with local
elders in their community with the purpose of building strong
intergenerational relationships and documenting the life histories of
Expand Down
7 changes: 6 additions & 1 deletion src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ const Navbar = () => {
return (
<nav className="fixed top-0 z-20 flex h-[60px] w-full flex-row items-center justify-between border border-dark-tan bg-med-tan px-[30px] py-3 shadow-md shadow-gray-400 sm:px-[50px] md:px-[93px]">
<Link href="/public" onClick={() => setDropdownVisible(false)}>
<Image src={logoicon} alt="Legacy Logo" className="w-[200px]" />
<Image
src={logoicon}
alt="Legacy Logo"
className="w-[200px]"
priority
/>
</Link>

<div className="visible z-10">
Expand Down
166 changes: 65 additions & 101 deletions src/components/PhotoCarousel.tsx
Original file line number Diff line number Diff line change
@@ -1,118 +1,82 @@
import React, { useState, useEffect, useRef } from "react";
import Image from "next/legacy/image";
"use client";

type PhotoProps = {
filePath: string;
};
import "src/styles/animation.css";
import React from "react";
import NextImage from "next/image";
import "react-loading-skeleton/dist/skeleton.css";
import Skeleton from "react-loading-skeleton";
import "react-multi-carousel/lib/styles.css";
import Carousel from "react-multi-carousel";

interface PhotoCarouselParams {
imagePaths: string[];
}

const PhotoCarousel = ({ imagePaths }: PhotoCarouselParams) => {
const [photos, setPhotos] = useState<PhotoProps[]>([]);
const [activeIndex, setActiveIndex] = useState<number>(0);
const [show, setShow] = useState<number>(1);
const widthRef = useRef<HTMLHeadingElement>(null);

const [elemWidth, setElemWidth] = useState<number>(0);
const Image = ({ path }: { path: string }) => {
const [loaded, setLoaded] = React.useState(false);
const [preloaded, setPreloaded] = React.useState(false);

useEffect(() => {
function handleResize() {
setElemWidth(widthRef.current ? widthRef.current.offsetWidth : 0);
const showVal = widthRef.current
? Math.floor(widthRef.current.offsetWidth / 250)
: 1;
setShow(showVal || 1);
}
window.addEventListener("resize", handleResize);
handleResize();
return () => {
window.removeEventListener("resize", handleResize);
};
React.useEffect(() => {
setTimeout(() => {
setPreloaded(true);
}, 2000);
}, []);

const carouselHeight = elemWidth / show;
const carouselPad = 20;
return (
<div className={"aspect-square h-full w-full"}>
{(!preloaded || !loaded) && <Skeleton className="h-full w-full" />}
<NextImage
className={preloaded && loaded ? "object-cover" : "invisible"}
src={path}
alt="Gallery"
layout="fill"
data-loaded="false"
onLoadingComplete={() => {
setLoaded(true);
}}
/>
</div>
);
};

const nextIndex = () => {
setActiveIndex((prevIndex) => (prevIndex + 1) % photos.length);
};
const PhotoCarousel = ({ imagePaths }: PhotoCarouselParams) => {
const photos = React.useMemo(
() => imagePaths.map((path) => <Image key={path} path={path} />),
[imagePaths]
);

const prevIndex = () => {
setActiveIndex(
(prevIndex) => (prevIndex - 1 + photos.length) % photos.length
);
const responsive = {
superLargeDesktop: {
// the naming can be any, depends on you.
breakpoint: { max: 4000, min: 3000 },
items: 5,
},
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 4,
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 2,
},
mobile: {
breakpoint: { max: 464, min: 0 },
items: 1,
},
};

useEffect(() => {
setPhotos(
imagePaths.map((path) => ({
filePath: path,
}))
);
}, [imagePaths]);

return (
<div className="flex w-full flex-col place-content-center gap-y-8 py-6">
<div className="relative flex items-center justify-center">
<svg
width="64"
height="64"
viewBox="0 0 64 64"
fill="none"
className="hover:cursor-pointer"
onClick={() => prevIndex()}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M41.0933 44.24L28.8799 32L41.0933 19.76L37.3333 16L21.3333 32L37.3333 48L41.0933 44.24Z"
fill="#000022"
/>
</svg>

<div
className="relative flex w-full flex-row justify-between overflow-hidden ease-in-out"
style={{ height: carouselHeight - carouselPad }}
ref={widthRef}
>
{photos.map((photo, index) => (
<div
key={photo.filePath}
className={
"absolute aspect-square h-full select-none object-cover transition-all"
}
style={{
left:
((index - activeIndex + photos.length) % photos.length) *
(carouselHeight + carouselPad / 2),
}}
>
<Image
className="object-cover"
layout="fill"
src={photo.filePath}
alt={"object cover"}
/>
</div>
))}
</div>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
fill="none"
onClick={() => nextIndex()}
className="hover:cursor-pointer"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22.9067 19.76L35.1201 32L22.9067 44.24L26.6667 48L42.6667 32L26.6667 16L22.9067 19.76Z"
fill="#000022"
/>
</svg>
</div>
</div>
<Carousel
responsive={responsive}
partialVisbile={false}
sliderClass="gap-x-4"
swipeable={false}
ssr
>
{photos}
{/* @note - We add an empty div because the last image is cut */}
<div />
</Carousel>
);
};

Expand Down
32 changes: 32 additions & 0 deletions src/styles/animation.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.skeleton {
position: relative;
inline-size: 100%;
overflow: hidden;
}

@keyframes shimmer {
from {
transform: "translateX(-100%)";
}
to {
transform: "translateX(100%)";
}
}

&.skeleton {
background: "var(--gray-4)";

&::before {
content: "";
position: absolute;
inset: 0;
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.2) 20%,
rgba(255, 255, 255, 0.5) 60%,
rgba(255, 255, 255, 0)
);
animation: shimmer 2s infinite;
}
}