Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.

Commit 58295db

Browse files
committed
adding code to show thumbnails below tool media carousel (#20)
1 parent 1280ce3 commit 58295db

File tree

6 files changed

+891
-491
lines changed

6 files changed

+891
-491
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
"react-multi-carousel": "^2.5.5",
4242
"react-player": "^2.6.2",
4343
"react-select": "^3.1.0",
44+
"react-slick": "0.26",
4445
"react-typography": "^0.16.19",
46+
"slick-carousel": "^1.8.1",
4547
"styled-components": "^5.1.0",
4648
"tailwindcss": "^1.4.6",
4749
"twin.macro": "^1.1.0",
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { css } from "twin.macro"
2+
export const SliderContentImageStyle = css`
3+
height: inherit;
4+
margin-left: auto;
5+
margin-right: auto;
6+
`
7+
export const SliderStyle = css`
8+
.slick-prev:before,
9+
.slick-next:before {
10+
color: #222425!important;
11+
}
12+
.slick-slide {
13+
padding: 0rem 1rem 0rem 1rem;
14+
}
15+
`
16+
export const SliderContentStyle = css`
17+
margin: 1rem 0rem 1rem 0.1rem;
18+
transition: transform .2s;
19+
height: 8rem;
20+
background: #f7f7f7;
21+
text-align: center;
22+
cursor: pointer;
23+
border-radius: 0.5rem;
24+
25+
&:hover {
26+
-ms-transform: scale(1.005);
27+
-webkit-transform: scale(1.005);
28+
transform: scale(1.005);
29+
}
30+
31+
&:focus {
32+
outline: none;
33+
-webkit-box-shadow: 0px 0px 4px 0px rgba(173,173,173,1);
34+
-moz-box-shadow: 0px 0px 4px 0px rgba(173,173,173,1);
35+
box-shadow: 0px 0px 4px 0px rgba(173,173,173,1);
36+
}
37+
`
38+
export const getSliderSetting = noOfElements => {
39+
const maxSlidesToShow = noOfElements < 3 ? 2 : 3;
40+
return {
41+
focusOnSelect: true,
42+
dots: true,
43+
infinite: false,
44+
speed: 500,
45+
slidesToShow: maxSlidesToShow,
46+
slidesToScroll: 1,
47+
responsive: [{
48+
breakpoint: 1200,
49+
settings: {
50+
slidesToShow: maxSlidesToShow,
51+
slidesToScroll: 1
52+
}
53+
}, {
54+
breakpoint: 600,
55+
settings: {
56+
slidesToShow: 2,
57+
slidesToScroll: 1
58+
}
59+
}, {
60+
breakpoint: 480,
61+
settings: {
62+
slidesToShow: 1,
63+
slidesToScroll: 1
64+
}
65+
}
66+
]
67+
}
68+
}
69+
70+
/**
71+
*
72+
* **Credits**
73+
* Author : yangshun
74+
* Gist link : https://gist.github.com/yangshun/9892961
75+
*/
76+
export const parseVideo = url => {
77+
url.match(/(http:|https:|)\/\/(player.|www.)?(vimeo\.com|youtu(be\.com|\.be|be\.googleapis\.com))\/(video\/|embed\/|watch\?v=|v\/)?([A-Za-z0-9._%-]*)(\&\S+)?/);
78+
79+
const type = RegExp.$3.indexOf("youtu") > -1 ? "youtube" :
80+
RegExp.$3.indexOf("vimeo") > -1 ? "vimeo"
81+
: undefined
82+
83+
return {
84+
type: type,
85+
id: RegExp.$6
86+
}
87+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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+
parseVideo
13+
} from "./main-media-util-helper"
14+
15+
const getVideoThumbnailUrl = async url => {
16+
const video = parseVideo(url)
17+
if (video.type === "youtube")
18+
return video.id ? "https://img.youtube.com/vi/" + video.id + "/maxresdefault.jpg" : "#"
19+
if (video.type === "vimeo") {
20+
const fetched = (async videoId => {
21+
let result = {}
22+
try {
23+
const response = await fetch("https://vimeo.com/api/v2/video/" + videoId + ".json")
24+
result = await response.json()
25+
return result[0].thumbnail_large
26+
} catch (e) {
27+
console.error("Error while fetching Vimeo video data", e)
28+
}
29+
})
30+
return fetched(video.id)
31+
}
32+
}
33+
34+
const renderMainMediaDisplayElement = {
35+
video: video => {
36+
return (
37+
<div tw="h-full text-center" key={video.key} id={video.key} css={video.css}>
38+
<ReactPlayer url={video.url} width="100%" controls={true}/>
39+
</div>
40+
)
41+
},
42+
image: image => (
43+
<a href={image.url} tw="w-full text-center" css={image.css}
44+
key={image.key} id={image.key}>
45+
<img
46+
alt={`Screenshot of ${image.name} website`}
47+
tw="border-4 max-w-full inline-block"
48+
src={image.src}
49+
/>
50+
</a>
51+
),
52+
}
53+
54+
const renderMainMediaSliderElement = {
55+
video: video => sliderThumbnail(video),
56+
image: image => sliderThumbnail(image)
57+
}
58+
59+
const sliderThumbnail = props => {
60+
props = props || {}
61+
const src = props.src || props.thumbnailSrc || "#"
62+
const alt = props.name || "Thumbnail"
63+
return (
64+
<div css={[SliderContentStyle]}
65+
id={props.key}
66+
key={props.key}
67+
onClick={props.onClick}>
68+
<img
69+
alt={alt}
70+
id={props.key + "_img"}
71+
key={props.key + "_img"}
72+
src={src}
73+
css={[
74+
tw`border-4 max-w-full`,
75+
SliderContentImageStyle
76+
]}
77+
/>
78+
</div>
79+
)
80+
}
81+
82+
export const MainMediaUtil = ({data}) => {
83+
const [items] = useState(data)
84+
const [index, setIndex] = useState(0)
85+
const [isRendered, setIsRendered] = useState(false)
86+
const [hasThumbnails, setHasThumbnails] = useState(false)
87+
const sliderSetting = getSliderSetting(items.length)
88+
89+
const toggleDisplayStatusOfElement = options => {
90+
options = options || {}
91+
const idForElementToDisplay = "#main_media_util_in_display_" + index
92+
const elementToDisplay = document.querySelector(idForElementToDisplay)
93+
elementToDisplay.setAttribute('style', options.style || 'display:block')
94+
95+
if (isRendered) return
96+
const idForElementToFocus = "#main_media_util_" + index
97+
const elementToFocus = document.querySelector(idForElementToFocus)
98+
elementToFocus.focus({ preventScroll: true })
99+
setIsRendered(true)
100+
}
101+
102+
const populateVideoThumbnails = async () => {
103+
items.map(async item => {
104+
if (item.type !== "video") return
105+
const url = await getVideoThumbnailUrl(item.source.url)
106+
const target = document.querySelector("#" + item.source.key + "_img")
107+
target.setAttribute("src", url)
108+
})
109+
setHasThumbnails(true)
110+
}
111+
112+
useEffect(() => {
113+
if (items.length > 1) toggleDisplayStatusOfElement()
114+
if (!hasThumbnails) populateVideoThumbnails()
115+
})
116+
117+
return items && items.length > 1 ? (
118+
<>
119+
<div tw="items-center h-full mb-1">
120+
{items.map((item, itemIndex) => {
121+
item.source.key = "main_media_util_in_display_" + itemIndex
122+
item.source.css = css`display:none;`
123+
return renderMainMediaDisplayElement[item.type](item.source)
124+
})}
125+
</div>
126+
<div tw="items-center h-full ml-2 mr-2">
127+
<Slider {...sliderSetting} css={[SliderStyle]}>
128+
{items.map((item, itemIndex) => {
129+
item.source.key = "main_media_util_" + itemIndex
130+
item.source.onClick = () => {
131+
if (itemIndex === index) return
132+
toggleDisplayStatusOfElement({style : 'display:none' })
133+
setIndex(itemIndex)
134+
}
135+
return renderMainMediaSliderElement[item.type](item.source)
136+
})}
137+
</Slider>
138+
</div>
139+
</>
140+
) : (
141+
<div tw="flex justify-center items-center h-full mb-5 pb-4">
142+
{items && items.map(item => {
143+
item.source.key = "main_media_util_in_display_0"
144+
return renderMainMediaDisplayElement[item.type](item.source)
145+
})}
146+
</div>
147+
)
148+
}

src/components/tool/main-media.js

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from "react"
22
import "twin.macro"
3-
import ReactPlayer from "react-player/lazy"
4-
import Carousel from "react-multi-carousel"
3+
import { MainMediaUtil } from "./main-media-util"
54

65
const getVideo = resources => {
76
if (!resources) {
@@ -15,22 +14,6 @@ const getVideo = resources => {
1514
}
1615
}
1716

18-
const renders = {
19-
video: video => (
20-
<ReactPlayer tw="max-w-full" url={video.url} width="100%" controls="true" />
21-
),
22-
image: image => (
23-
<a href={image.url} tw="w-full text-center">
24-
<img
25-
alt={`Screenshot of ${image.name} website`}
26-
tw="border-4 max-w-full inline-block"
27-
src={image.src}
28-
height="360"
29-
/>
30-
</a>
31-
),
32-
}
33-
3417
const MainMedia = ({ tool }) => {
3518
const { name, homepage, resources } = tool
3619
let screenshot = { name, url: homepage, src: tool.fields.screenshot }
@@ -45,32 +28,9 @@ const MainMedia = ({ tool }) => {
4528
items.push({ type: "video", source: video })
4629
}
4730

48-
const carouselProps = {
49-
responsive: {
50-
all: {
51-
breakpoint: { max: 3000, min: 0 },
52-
items: 1,
53-
},
54-
},
55-
infinite: true,
56-
showDots: true,
57-
}
58-
5931
return (
6032
<div tw="mb-5">
61-
{items.length > 1 ? (
62-
<Carousel {...carouselProps}>
63-
{items.map(item => (
64-
<div tw="flex justify-center items-center h-full mb-5 pb-4">
65-
{renders[item.type](item.source)}
66-
</div>
67-
))}
68-
</Carousel>
69-
) : (
70-
<div tw="flex justify-center items-center h-full mb-5 pb-4">
71-
{renders[items[0].type](items[0].source)}
72-
</div>
73-
)}
33+
<MainMediaUtil data={items} />
7434
</div>
7535
)
7636
}
43.5 KB
Loading

0 commit comments

Comments
 (0)