PluginProbe
Extendify / 3.0.5
Extendify v3.0.5
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / Draft / components / stock-images / UnsplashImage.jsx

UnsplashImage.jsx in Extendify 3.0.5, at src/Draft/components/stock-images/UnsplashImage.jsx

95 lines 2.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { loadImage } from '@shared/api/wp';
2 import { Spinner } from '@wordpress/components';
3 import { useEffect, useInsertionEffect, useState } from '@wordpress/element';
4 import classNames from 'classnames';
5 import { AnimatePresence, motion } from 'framer-motion';
6
7 export const UnsplashImage = ({
8 image,
9 skeletonHeight,
10 isInsertingImage,
11 onClick,
12 }) => {
13 const [authorUrl, setAuthorUrl] = useState('');
14 const [loaded, setLoaded] = useState(false);
15 const aspectRatio = image?.width
16 ? Number(image?.width) / Number(image?.height)
17 : 122 / skeletonHeight;
18
19 useEffect(() => {
20 if (!image?.user?.links?.html) {
21 setAuthorUrl('');
22 return;
23 }
24 const authorUrl = new URL(image.user.links.html);
25 authorUrl.searchParams.set('utm_source', 'extendify');
26 authorUrl.searchParams.set('utm_medium', 'referral');
27 setAuthorUrl(authorUrl.toString());
28 }, [image]);
29
30 useInsertionEffect(() => {
31 if (!image?.urls || loaded) return;
32 const img = new Image();
33 img.src = image.urls.thumb || image.urls.small;
34 loadImage(img).then(() => setLoaded(true));
35 }, [image, loaded]);
36
37 return (
38 <motion.div
39 className="relative mb-1"
40 initial={{ aspectRatio }}
41 animate={{ aspectRatio }}
42 >
43 <AnimatePresence>
44 {loaded ? null : (
45 <motion.div
46 className="absolute inset-0 z-10 bg-white"
47 initial={{ opacity: 1 }}
48 animate={{ opacity: 1 }}
49 exit={{ opacity: 0 }}
50 >
51 <div className="animate-pulse absolute inset-0 z-10 bg-gray-150" />
52 </motion.div>
53 )}
54 </AnimatePresence>
55 <div className="group relative">
56 <button
57 type="button"
58 className={classNames('relative block border-0 p-0', {
59 'bg-transparent': !isInsertingImage,
60 'bg-black': isInsertingImage,
61 })}
62 onClick={() => onClick(image)}
63 disabled={isInsertingImage}
64 >
65 {isInsertingImage && isInsertingImage?.id === image?.id && (
66 <div className="absolute inset-0 flex items-center justify-center">
67 <Spinner style={{ height: '24px', width: '24px' }} />
68 </div>
69 )}
70 <img
71 src={image?.urls?.thumb || image?.urls?.small}
72 className={classNames('block transition-opacity duration-300', {
73 'opacity-50': isInsertingImage,
74 })}
75 alt={image?.alt_description}
76 />
77 </button>
78 {image?.user?.name && authorUrl ? (
79 <a
80 href={authorUrl}
81 target="_blank"
82 className={classNames(
83 'absolute bottom-1 bg-black/70 px-1 text-white no-underline opacity-0',
84 {
85 'group-focus-within:opacity-100 group-hover:opacity-100':
86 !isInsertingImage,
87 },
88 )}
89 >{`${image.user?.name}`}</a>
90 ) : null}
91 </div>
92 </motion.div>
93 );
94 };
95