|
1 |
| -import { useEffect, useState } from 'react'; |
| 1 | +import { useEffect, useMemo, useState } from 'react'; |
2 | 2 |
|
3 | 3 | import { getRandomElementFromArray } from '@/utils/array';
|
4 | 4 | import { cn } from '@/utils/styles';
|
5 | 5 |
|
| 6 | +import type { HeroImageProps } from '@/libs/gallery/transform'; |
6 | 7 | import type { FC, ImgHTMLAttributes } from 'react';
|
7 | 8 |
|
8 | 9 | interface Props extends ImgHTMLAttributes<HTMLImageElement> {
|
9 | 10 | // Important: must pass all allImagesSrc from the server or they wont be included on client
|
10 |
| - allImagesSrc: string[]; |
| 11 | + galleryImages: HeroImageProps[]; |
11 | 12 | }
|
12 | 13 |
|
13 |
| -const initialImageSrc = ''; |
| 14 | +const initialImage: HeroImageProps = { |
| 15 | + blur: { |
| 16 | + src: '', |
| 17 | + width: 0, |
| 18 | + height: 0, |
| 19 | + }, |
| 20 | + hero: { |
| 21 | + src: '', |
| 22 | + width: 0, |
| 23 | + height: 0, |
| 24 | + }, |
| 25 | +}; |
14 | 26 |
|
15 |
| -const ImageRandomReact: FC<Props> = ({ allImagesSrc, className, ...props }) => { |
16 |
| - const [imageSrc, setImageSrc] = useState(initialImageSrc); |
| 27 | +const ImageRandomReact: FC<Props> = ({ galleryImages, className, ...props }) => { |
| 28 | + const randomImage = useMemo(() => getRandomElementFromArray(galleryImages), [galleryImages]); |
17 | 29 |
|
18 |
| - useEffect(() => { |
19 |
| - const run = async () => { |
20 |
| - setImageSrc(getRandomElementFromArray(allImagesSrc)); |
21 |
| - }; |
| 30 | + const [image, setImage] = useState(initialImage); |
| 31 | + const [isLoading, setIsLoading] = useState(true); |
22 | 32 |
|
23 |
| - run(); |
24 |
| - }, [setImageSrc]); |
| 33 | + useEffect(() => { |
| 34 | + setImage(randomImage); |
| 35 | + }, [setImage, randomImage]); |
25 | 36 |
|
26 | 37 | const handleClick = async () => {
|
27 |
| - setImageSrc(getRandomElementFromArray(allImagesSrc)); |
| 38 | + const randomImage = getRandomElementFromArray(galleryImages); |
| 39 | + setImage(randomImage); |
| 40 | + setIsLoading(true); |
28 | 41 | };
|
29 | 42 |
|
30 | 43 | // Todo: use <picture srcSet> for responsive images
|
31 | 44 |
|
32 | 45 | return (
|
33 |
| - <img |
34 |
| - {...props} |
35 |
| - src={imageSrc} |
36 |
| - onClick={handleClick} |
37 |
| - className={cn('cursor-pointer', className)} |
38 |
| - alt="Hero image" |
39 |
| - /> |
| 46 | + <> |
| 47 | + {image.blur.src && ( |
| 48 | + <img |
| 49 | + {...props} |
| 50 | + src={image.blur.src} |
| 51 | + onClick={handleClick} |
| 52 | + className={cn('cursor-pointer hidden', { block: isLoading }, className)} |
| 53 | + alt="Blur image" |
| 54 | + /> |
| 55 | + )} |
| 56 | + |
| 57 | + {image.hero.src && ( |
| 58 | + <img |
| 59 | + {...props} |
| 60 | + src={image.hero.src} |
| 61 | + onClick={handleClick} |
| 62 | + onLoad={() => setIsLoading(false)} |
| 63 | + className={cn( |
| 64 | + 'cursor-pointer transition-opacity duration-500 ease-in-out opacity-100 block', |
| 65 | + { 'hidden opacity-0': isLoading }, |
| 66 | + className |
| 67 | + )} |
| 68 | + alt="Hero image" |
| 69 | + /> |
| 70 | + )} |
| 71 | + |
| 72 | + {!image.blur.src && !image.hero.src && <div className={cn('', className)}>placeholder</div>} |
| 73 | + </> |
40 | 74 | );
|
41 | 75 | };
|
42 | 76 |
|
|
0 commit comments