-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from osakunta/next-site
Blocking Updates:
- Loading branch information
Showing
11 changed files
with
621 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.