Skip to content

Commit

Permalink
Merge pull request #8 from osakunta/next-site
Browse files Browse the repository at this point in the history
Blocking Updates:
  • Loading branch information
YB-BigSwan authored Jul 19, 2024
2 parents 8eaa269 + 9d9fe64 commit 37d0cdd
Show file tree
Hide file tree
Showing 11 changed files with 621 additions and 27 deletions.
121 changes: 121 additions & 0 deletions components/Carousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import styles from "@/styles/Carousel.module.css";
import React, { useCallback, useEffect, useRef } from "react";
import Link from "next/link";
import {
EmblaCarouselType,
EmblaEventType,
EmblaOptionsType,
} from "embla-carousel";
import useEmblaCarousel from "embla-carousel-react";
import { NextButton, PrevButton, usePrevNextButtons } from "./CarouselArrows";

const TWEEN_FACTOR_BASE = 0.84;

const numberWithinRange = (number: number, min: number, max: number): number =>
Math.min(Math.max(number, min), max);

type PropType = {
slides: number[];
options?: EmblaOptionsType;
};

const Carousel: React.FC<PropType> = (props) => {
const { slides, options } = props;
const [emblaRef, emblaApi] = useEmblaCarousel(options);
const tweenFactor = useRef(0);

const {
prevBtnDisabled,
nextBtnDisabled,
onPrevButtonClick,
onNextButtonClick,
} = usePrevNextButtons(emblaApi);

const setTweenFactor = useCallback((emblaApi: EmblaCarouselType) => {
tweenFactor.current = TWEEN_FACTOR_BASE * emblaApi.scrollSnapList().length;
}, []);

const tweenOpacity = useCallback(
(emblaApi: EmblaCarouselType, eventName?: EmblaEventType) => {
const engine = emblaApi.internalEngine();
const scrollProgress = emblaApi.scrollProgress();
const slidesInView = emblaApi.slidesInView();
const isScrollEvent = eventName === "scroll";

emblaApi.scrollSnapList().forEach((scrollSnap, snapIndex) => {
let diffToTarget = scrollSnap - scrollProgress;
const slidesInSnap = engine.slideRegistry[snapIndex];

slidesInSnap.forEach((slideIndex) => {
if (isScrollEvent && !slidesInView.includes(slideIndex)) return;

if (engine.options.loop) {
engine.slideLooper.loopPoints.forEach((loopItem) => {
const target = loopItem.target();

if (slideIndex === loopItem.index && target !== 0) {
const sign = Math.sign(target);

if (sign === -1) {
diffToTarget = scrollSnap - (1 + scrollProgress);
}
if (sign === 1) {
diffToTarget = scrollSnap + (1 - scrollProgress);
}
}
});
}

const tweenValue = 1 - Math.abs(diffToTarget * tweenFactor.current);
const opacity = numberWithinRange(tweenValue, 0, 1).toString();
emblaApi.slideNodes()[slideIndex].style.opacity = opacity;
});
});
},
[],
);

useEffect(() => {
if (!emblaApi) return;

setTweenFactor(emblaApi);
tweenOpacity(emblaApi);
emblaApi
.on("reInit", setTweenFactor)
.on("reInit", tweenOpacity)
.on("scroll", tweenOpacity)
.on("slideFocus", tweenOpacity);
}, [emblaApi, tweenOpacity]);

return (
<div className="embla">
<div className="embla__viewport" ref={emblaRef}>
<div className="embla__container">
{slides.map((index) => (
<div className="embla__slide" key={index}>
<img
className="embla__slide__img"
src={`https://picsum.photos/600/350?v=${index}`}
alt="Your alt text"
/>
</div>
))}
</div>
</div>

<div className="embla__controls">
<div className="embla__buttons">
<PrevButton onClick={onPrevButtonClick} disabled={prevBtnDisabled} />
<NextButton onClick={onNextButtonClick} disabled={nextBtnDisabled} />
</div>
<div className="embla__buttons">
<Link href="" className="embla__link">
Katso Lisää
</Link>
</div>
</div>
</div>
);
};

export default Carousel;
95 changes: 95 additions & 0 deletions components/CarouselArrows.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, {
ComponentPropsWithRef,
useCallback,
useEffect,
useState,
} from "react";
import { EmblaCarouselType } from "embla-carousel";

type UsePrevNextButtonsType = {
prevBtnDisabled: boolean;
nextBtnDisabled: boolean;
onPrevButtonClick: () => void;
onNextButtonClick: () => void;
};

export const usePrevNextButtons = (
emblaApi: EmblaCarouselType | undefined,
onButtonClick?: (emblaApi: EmblaCarouselType) => void,
): UsePrevNextButtonsType => {
const [prevBtnDisabled, setPrevBtnDisabled] = useState(true);
const [nextBtnDisabled, setNextBtnDisabled] = useState(true);

const onPrevButtonClick = useCallback(() => {
if (!emblaApi) return;
emblaApi.scrollPrev();
if (onButtonClick) onButtonClick(emblaApi);
}, [emblaApi, onButtonClick]);

const onNextButtonClick = useCallback(() => {
if (!emblaApi) return;
emblaApi.scrollNext();
if (onButtonClick) onButtonClick(emblaApi);
}, [emblaApi, onButtonClick]);

const onSelect = useCallback((emblaApi: EmblaCarouselType) => {
setPrevBtnDisabled(!emblaApi.canScrollPrev());
setNextBtnDisabled(!emblaApi.canScrollNext());
}, []);

useEffect(() => {
if (!emblaApi) return;

onSelect(emblaApi);
emblaApi.on("reInit", onSelect).on("select", onSelect);
}, [emblaApi, onSelect]);

return {
prevBtnDisabled,
nextBtnDisabled,
onPrevButtonClick,
onNextButtonClick,
};
};

type PropType = ComponentPropsWithRef<"button">;

export const PrevButton: React.FC<PropType> = (props) => {
const { children, ...restProps } = props;

return (
<button
className="embla__button embla__button--prev"
type="button"
{...restProps}
>
<svg className="embla__button__svg" viewBox="0 0 532 532">
<path
fill="currentColor"
d="M355.66 11.354c13.793-13.805 36.208-13.805 50.001 0 13.785 13.804 13.785 36.238 0 50.034L201.22 266l204.442 204.61c13.785 13.805 13.785 36.239 0 50.044-13.793 13.796-36.208 13.796-50.002 0a5994246.277 5994246.277 0 0 0-229.332-229.454 35.065 35.065 0 0 1-10.326-25.126c0-9.2 3.393-18.26 10.326-25.2C172.192 194.973 332.731 34.31 355.66 11.354Z"
/>
</svg>
{children}
</button>
);
};

export const NextButton: React.FC<PropType> = (props) => {
const { children, ...restProps } = props;

return (
<button
className="embla__button embla__button--next"
type="button"
{...restProps}
>
<svg className="embla__button__svg" viewBox="0 0 532 532">
<path
fill="currentColor"
d="M176.34 520.646c-13.793 13.805-36.208 13.805-50.001 0-13.785-13.804-13.785-36.238 0-50.034L330.78 266 126.34 61.391c-13.785-13.805-13.785-36.239 0-50.044 13.793-13.796 36.208-13.796 50.002 0 22.928 22.947 206.395 206.507 229.332 229.454a35.065 35.065 0 0 1 10.326 25.126c0 9.2-3.393 18.26-10.326 25.2-45.865 45.901-206.404 206.564-229.332 229.52Z"
/>
</svg>
{children}
</button>
);
};
69 changes: 45 additions & 24 deletions components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { useState, useEffect } from "react";
import close from "../public/close.svg";
import menu from "../public/menu.svg";
import sato_logo_nav from "../public/sato_logo_nav.png";
Expand All @@ -22,6 +22,25 @@ const Navbar = () => {
right: false,
});

useEffect(() => {
let prevScrollpos = window.scrollY;
const handleScroll = () => {
const currentScrollpos = window.scrollY;
const navWrapper = document.getElementById("navContainer");
if (navWrapper) {
if (prevScrollpos > currentScrollpos) {
navWrapper.style.top = "0";
} else {
navWrapper.style.top = "-10rem";
}
}
prevScrollpos = currentScrollpos;
};
window.addEventListener("scroll", handleScroll);

return () => window.removeEventListener("scroll", handleScroll);
}, []);

const toggleDrawer =
(anchor: Anchor, open: boolean) =>
(event: React.KeyboardEvent | React.MouseEvent) => {
Expand Down Expand Up @@ -72,29 +91,31 @@ const Navbar = () => {
);

return (
<nav id="navbar" className={styles.navbar}>
<Link href="/">
<Image
src={sato_logo_nav}
alt="A nav link to the home page"
width={120}
/>
</Link>
{(["right"] as const).map((anchor) => (
<div key={anchor}>
<Button onClick={toggleDrawer(anchor, true)}>
<Image src={menu} alt="Hamburger menu" width={45} />
</Button>
<Drawer
anchor={anchor}
open={state[anchor]}
onClose={toggleDrawer(anchor, false)}
>
{list(anchor)}
</Drawer>
</div>
))}
</nav>
<div className={styles.navContainer} id="navContainer">
<nav id="navbar" className={styles.navbar}>
<Link href="/">
<Image
src={sato_logo_nav}
alt="A nav link to the home page"
width={120}
/>
</Link>
{(["right"] as const).map((anchor) => (
<div key={anchor}>
<Button onClick={toggleDrawer(anchor, true)}>
<Image src={menu} alt="Hamburger menu" width={45} />
</Button>
<Drawer
anchor={anchor}
open={state[anchor]}
onClose={toggleDrawer(anchor, false)}
>
{list(anchor)}
</Drawer>
</div>
))}
</nav>
</div>
);
};

Expand Down
29 changes: 29 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: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
"@emotion/styled": "^11.11.5",
"@mui/material": "^5.15.20",
"dotenv": "^16.4.5",
"embla-carousel-react": "^8.1.6",
"next": "14.2.4",
"react": "^18",
"react-dom": "^18"
"react-dom": "^18"
},
"devDependencies": {
"@testing-library/react": "^16.0.0",
Expand Down
Loading

0 comments on commit 37d0cdd

Please sign in to comment.