1
+ import React , { useState , useEffect } from "react"
2
+ import tw , { css } from "twin.macro"
3
+ import ReactPlayer from "react-player/lazy"
4
+ import "slick-carousel/slick/slick.css"
5
+ import "slick-carousel/slick/slick-theme.css"
6
+ import Slider from "react-slick"
7
+ import {
8
+ SliderContentImageStyle ,
9
+ SliderStyle ,
10
+ SliderContentStyle ,
11
+ getSliderSetting
12
+ } from "./main-media-util-helper"
13
+
14
+ /**
15
+ *
16
+ * **Credits**
17
+ * Author : yangshun
18
+ * Gist link : https://gist.github.com/yangshun/9892961
19
+ */
20
+ const parseVideo = url => {
21
+ url . match ( / ( h t t p : | h t t p s : | ) \/ \/ ( p l a y e r .| w w w .) ? ( v i m e o \. c o m | y o u t u ( b e \. c o m | \. b e | b e \. g o o g l e a p i s \. c o m ) ) \/ ( v i d e o \/ | e m b e d \/ | w a t c h \? v = | v \/ ) ? ( [ A - Z a - z 0 - 9 . _ % - ] * ) ( & \S + ) ? / ) ;
22
+
23
+ const type = RegExp . $3 . indexOf ( "youtu" ) > - 1 ? "youtube" :
24
+ RegExp . $3 . indexOf ( "vimeo" ) > - 1 ? "vimeo"
25
+ : undefined
26
+
27
+ return {
28
+ type : type ,
29
+ id : RegExp . $6
30
+ }
31
+ }
32
+
33
+ const getVideoThumbnailUrl = async url => {
34
+ const video = parseVideo ( url )
35
+ if ( video . type === "youtube" )
36
+ return video . id ? "https://img.youtube.com/vi/" + video . id + "/maxresdefault.jpg" : "#"
37
+ if ( video . type === "vimeo" ) {
38
+ const fetched = ( async videoId => {
39
+ let result = { }
40
+ try {
41
+ const response = await fetch ( "https://vimeo.com/api/v2/video/" + videoId + ".json" )
42
+ result = await response . json ( )
43
+ return result [ 0 ] . thumbnail_large
44
+ } catch ( e ) {
45
+ console . error ( "Error while fetching Vimeo video data" , e )
46
+ }
47
+ } )
48
+ return fetched ( video . id )
49
+ }
50
+ }
51
+
52
+ const renderMainMediaDisplayElement = {
53
+ video : video => {
54
+ return (
55
+ < div tw = "h-full text-center" key = { video . key } id = { video . key } css = { video . css } >
56
+ < ReactPlayer url = { video . url } width = "100%" controls = { true } />
57
+ </ div >
58
+ )
59
+ } ,
60
+ image : image => (
61
+ < a href = { image . url } tw = "w-full text-center" css = { image . css }
62
+ key = { image . key } id = { image . key } >
63
+ < img
64
+ alt = { `Screenshot of ${ image . name } website` }
65
+ tw = "border-4 max-w-full inline-block"
66
+ src = { image . src }
67
+ />
68
+ </ a >
69
+ ) ,
70
+ }
71
+
72
+ const renderMainMediaSliderElement = {
73
+ video : video => sliderThumbnail ( video ) ,
74
+ image : image => sliderThumbnail ( image )
75
+ }
76
+
77
+ const sliderThumbnail = props => {
78
+ props = props || { }
79
+ const src = props . src || props . thumbnailSrc || "#"
80
+ const alt = props . name || "Thumbnail"
81
+ return (
82
+ < div css = { [ SliderContentStyle ] }
83
+ id = { props . key }
84
+ key = { props . key }
85
+ onClick = { props . onClick }
86
+ aria-hidden = "true" >
87
+ < img
88
+ alt = { alt }
89
+ id = { props . key + "_img" }
90
+ key = { props . key + "_img" }
91
+ src = { src }
92
+ css = { [
93
+ tw `border-4 max-w-full` ,
94
+ SliderContentImageStyle
95
+ ] }
96
+ />
97
+ </ div >
98
+ )
99
+ }
100
+
101
+ export const MainMediaUtil = ( { data} ) => {
102
+ const [ items ] = useState ( data )
103
+ const [ index , setIndex ] = useState ( 0 )
104
+ const [ isRendered , setIsRendered ] = useState ( false )
105
+ const [ hasThumbnails , setHasThumbnails ] = useState ( false )
106
+ const sliderSetting = getSliderSetting ( items . length )
107
+
108
+ const toggleDisplayStatusOfElement = options => {
109
+ options = options || { }
110
+ const idForElementToDisplay = "#main_media_util_in_display_" + index
111
+ const elementToDisplay = document . querySelector ( idForElementToDisplay )
112
+ elementToDisplay . setAttribute ( 'style' , options . style || 'display:block' )
113
+
114
+ if ( isRendered ) return
115
+ const idForElementToFocus = "#main_media_util_" + index
116
+ const elementToFocus = document . querySelector ( idForElementToFocus )
117
+ elementToFocus . focus ( { preventScroll : true } )
118
+ setIsRendered ( true )
119
+ }
120
+
121
+ const populateVideoThumbnails = async ( ) => {
122
+ items . map ( async item => {
123
+ if ( item . type !== "video" ) return
124
+ const url = await getVideoThumbnailUrl ( item . source . url )
125
+ const target = document . querySelector ( "#" + item . source . key + "_img" )
126
+ target . setAttribute ( "src" , url )
127
+ } )
128
+ setHasThumbnails ( true )
129
+ }
130
+
131
+ useEffect ( ( ) => {
132
+ if ( items . length > 1 ) toggleDisplayStatusOfElement ( )
133
+ if ( ! hasThumbnails ) populateVideoThumbnails ( )
134
+ } )
135
+
136
+ return items && items . length > 1 ? (
137
+ < >
138
+ < div tw = "items-center h-full mb-1" >
139
+ { items . map ( ( item , itemIndex ) => {
140
+ item . source . key = "main_media_util_in_display_" + itemIndex
141
+ item . source . css = css `display : none;`
142
+ return renderMainMediaDisplayElement [ item . type ] ( item . source )
143
+ } ) }
144
+ </ div >
145
+ < div tw = "items-center h-full ml-2 mr-2" >
146
+ < Slider { ...sliderSetting } css = { [ SliderStyle ] } >
147
+ { items . map ( ( item , itemIndex ) => {
148
+ item . source . key = "main_media_util_" + itemIndex
149
+ item . source . onClick = ( ) => {
150
+ if ( itemIndex === index ) return
151
+ toggleDisplayStatusOfElement ( { style : 'display:none' } )
152
+ setIndex ( itemIndex )
153
+ }
154
+ return renderMainMediaSliderElement [ item . type ] ( item . source )
155
+ } ) }
156
+ </ Slider >
157
+ </ div >
158
+ </ >
159
+ ) : (
160
+ < div tw = "flex justify-center items-center h-full mb-5 pb-4" >
161
+ { items && items . map ( item => {
162
+ item . source . key = "main_media_util_in_display_0"
163
+ return renderMainMediaDisplayElement [ item . type ] ( item . source )
164
+ } ) }
165
+ </ div >
166
+ )
167
+ }
0 commit comments