@@ -10,6 +10,38 @@ import type { SanitizedCollectionConfig } from 'payload'
10
10
import { File } from '../../graphics/File/index.js'
11
11
import { ShimmerEffect } from '../ShimmerEffect/index.js'
12
12
13
+ /**
14
+ * Check if specific media exist before render it in it's appropriate HTML tag.
15
+ * This internal hook allows to share this logic between Thumbnail & ThumbnailComponent
16
+ */
17
+ const useFileExists = ( fileSrc : string | undefined , fileType : string ) : boolean => {
18
+ const [ fileExists , setFileExists ] = React . useState < boolean | undefined > ( undefined )
19
+
20
+ React . useEffect ( ( ) => {
21
+ if ( ! fileSrc || ! fileType ) {
22
+ setFileExists ( false )
23
+ return
24
+ }
25
+
26
+ if ( fileType === 'image' ) {
27
+ const img = new Image ( )
28
+ img . src = fileSrc
29
+ img . onload = ( ) => setFileExists ( true )
30
+ img . onerror = ( ) => setFileExists ( false )
31
+ } else if ( fileType === 'video' ) {
32
+ const video = document . createElement ( 'video' )
33
+ video . src = fileSrc
34
+ video . crossOrigin = 'anonymous'
35
+ video . onloadeddata = ( ) => setFileExists ( true )
36
+ video . onerror = ( ) => setFileExists ( false )
37
+ } else {
38
+ setFileExists ( false )
39
+ }
40
+ } , [ fileSrc , fileType ] )
41
+
42
+ return fileExists
43
+ }
44
+
13
45
export type ThumbnailProps = {
14
46
className ?: string
15
47
collectionSlug ?: string
@@ -21,34 +53,28 @@ export type ThumbnailProps = {
21
53
}
22
54
23
55
export const Thumbnail : React . FC < ThumbnailProps > = ( props ) => {
24
- const { className = '' , doc : { filename } = { } , fileSrc, imageCacheTag, size } = props
25
- const [ fileExists , setFileExists ] = React . useState ( undefined )
26
-
56
+ const { className = '' , doc : { filename, mimeType } = { } , fileSrc, imageCacheTag, size } = props
27
57
const classNames = [ baseClass , `${ baseClass } --size-${ size || 'medium' } ` , className ] . join ( ' ' )
58
+ const fileType = ( mimeType as string ) ?. split ( '/' ) ?. [ 0 ]
28
59
29
- React . useEffect ( ( ) => {
30
- if ( ! fileSrc ) {
31
- setFileExists ( false )
32
- return
33
- }
34
-
35
- const img = new Image ( )
36
- img . src = fileSrc
37
- img . onload = ( ) => {
38
- setFileExists ( true )
39
- }
40
- img . onerror = ( ) => {
41
- setFileExists ( false )
42
- }
43
- } , [ fileSrc ] )
60
+ const fileExists = useFileExists ( fileSrc , fileType )
61
+ const src = React . useMemo (
62
+ ( ) => `${ fileSrc } ${ imageCacheTag ? `?${ imageCacheTag } ` : '' } ` ,
63
+ [ fileSrc , imageCacheTag ] ,
64
+ )
44
65
45
66
return (
46
67
< div className = { classNames } >
47
68
{ fileExists === undefined && < ShimmerEffect height = "100%" /> }
48
- { fileExists && (
49
- < img
50
- alt = { filename as string }
51
- src = { `${ fileSrc } ${ imageCacheTag ? `?${ imageCacheTag } ` : '' } ` }
69
+ { fileExists && fileType === 'image' && < img alt = { filename as string } src = { src } /> }
70
+ { fileExists && fileType === 'video' && (
71
+ < video
72
+ aria-label = { filename as string }
73
+ autoPlay = { false }
74
+ controls = { false }
75
+ muted = { true }
76
+ playsInline = { true }
77
+ src = { src }
52
78
/>
53
79
) }
54
80
{ fileExists === false && < File /> }
@@ -62,35 +88,33 @@ type ThumbnailComponentProps = {
62
88
readonly filename : string
63
89
readonly fileSrc : string
64
90
readonly imageCacheTag ?: string
91
+ readonly mimeType ?: string
65
92
readonly size ?: 'expand' | 'large' | 'medium' | 'small'
66
93
}
67
94
export function ThumbnailComponent ( props : ThumbnailComponentProps ) {
68
- const { alt, className = '' , filename, fileSrc, imageCacheTag, size } = props
69
- const [ fileExists , setFileExists ] = React . useState ( undefined )
70
-
95
+ const { alt, className = '' , filename, fileSrc, imageCacheTag, mimeType, size } = props
71
96
const classNames = [ baseClass , `${ baseClass } --size-${ size || 'medium' } ` , className ] . join ( ' ' )
97
+ const fileType = mimeType ?. split ( '/' ) ?. [ 0 ]
72
98
73
- React . useEffect ( ( ) => {
74
- if ( ! fileSrc ) {
75
- setFileExists ( false )
76
- return
77
- }
78
-
79
- const img = new Image ( )
80
- img . src = fileSrc
81
- img . onload = ( ) => {
82
- setFileExists ( true )
83
- }
84
- img . onerror = ( ) => {
85
- setFileExists ( false )
86
- }
87
- } , [ fileSrc ] )
99
+ const fileExists = useFileExists ( fileSrc , fileType )
100
+ const src = React . useMemo (
101
+ ( ) => `${ fileSrc } ${ imageCacheTag ? `?${ imageCacheTag } ` : '' } ` ,
102
+ [ fileSrc , imageCacheTag ] ,
103
+ )
88
104
89
105
return (
90
106
< div className = { classNames } >
91
107
{ fileExists === undefined && < ShimmerEffect height = "100%" /> }
92
- { fileExists && (
93
- < img alt = { alt || filename } src = { `${ fileSrc } ${ imageCacheTag ? `?${ imageCacheTag } ` : '' } ` } />
108
+ { fileExists && fileType === 'image' && < img alt = { alt || filename } src = { src } /> }
109
+ { fileExists && fileType === 'video' && (
110
+ < video
111
+ aria-label = { alt || filename }
112
+ autoPlay = { false }
113
+ controls = { false }
114
+ muted = { true }
115
+ playsInline = { true }
116
+ src = { src }
117
+ />
94
118
) }
95
119
{ fileExists === false && < File /> }
96
120
</ div >
0 commit comments