Azure Media Player events now working for React Component - reactjs

I tried to create a React component to play Azure Media Services content and it works to play, but not to capture events. This is the code of my component:
import React, { useEffect, useRef } from 'react';
import { Helmet } from 'react-helmet';
export interface AzureMediaPlayerProps {
videoUrl: string;
}
const AzureMediaPlayer = (props: AzureMediaPlayerProps): JSX.Element => {
const { videoUrl } = props;
const videoRef = useRef<HTMLVideoElement>(null);
const clearListener = (): void => {
videoRef.current?.removeEventListener('load', (): void => { });
videoRef.current?.removeEventListener('progress', (): void => { });
};
const addListener = (): void => {
videoRef.current?.addEventListener('load', (ev): void => { console.log(ev); });
videoRef.current?.addEventListener('progress', (ev): void => { console.log(ev); });
};
useEffect((): void => {
addListener();
return clearListener();
}, [videoRef]);
return (
<>
<Helmet>
<link href="//amp.azure.net/libs/amp/2.3.7/skins/amp-default/azuremediaplayer.min.css" rel="stylesheet" />
<script src="//amp.azure.net/libs/amp/2.3.7/azuremediaplayer.min.js" />
</Helmet>
<video
id="vid1"
className="azuremediaplayer amp-default-skin"
autoPlay
controls
width="100%"
data-setup='{"nativeControlsForTouch": false}'
ref={videoRef}
>
<source src={videoUrl} type="application/vnd.ms-sstr+xml" />
<p className="amp-no-js">
To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video
</p>
</video>
</>
);
};
AzureMediaPlayer.displayName = 'AzureMediaPlayer';
export default AzureMediaPlayer;
I also tried:
<video
id="vid1"
className="azuremediaplayer amp-default-skin"
autoPlay
controls
width="100%"
data-setup='{"nativeControlsForTouch": false}'
ref={videoRef}
onProgress={(ev): void => { console.log(ev); }}
>
<source src={videoUrl} type="application/vnd.ms-sstr+xml" />
<p className="amp-no-js">
To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video
</p>
</video>
But I was not able to get the events of the player. Can anybody please help me? How can I get the player events in React?
Thanks a lot.

Recommend that you don't try to use the AMP player in React component directly. It was not written to be componentized.
Try looking at a more modern player like Shaka or HLS.js that may already have a React wrapper for it.
For example -
https://www.npmjs.com/package/shaka-player-react
https://github.com/matvp91/shaka-player-react
Or a more commercial player that supports React like Theo Player or Bitmovin
https://docs.theoplayer.com/getting-started/02-frameworks/02-react/00-getting-started.md
https://github.com/bitmovin/bitmovin-player-web-samples

This is the final version of VideoJsPlayer.tsx:
import { Box } from 'grommet';
import React, { useEffect, useRef, useState } from 'react';
import videojs, { VideoJsPlayerOptions } from 'video.js';
import 'video.js/dist/video-js.css';
export interface VideoJsPlayerProps {
videoUrl?: string;
transcriptionUrl?: string;
type?: string;
saveProgress: (minute: number) => Promise<void>;
completeProgress: (progress: string) => Promise<void>;
startingTime?: number;
}
const VideoJsPlayer = (props: VideoJsPlayerProps): JSX.Element => {
const {
videoUrl, transcriptionUrl, type, saveProgress, completeProgress, startingTime = 0,
} = props;
let player;
const videoRef = useRef<HTMLVideoElement>(null);
const [minutes, setMinutes] = useState<number>(0);
const getMin = (sec: number): number => Math.floor(sec / 60);
const updateMinutes = (time: string): void => {
const secs = parseInt(time, 10);
const min = getMin(secs);
if (min > minutes) {
setMinutes(min);
}
};
const videoJsOptions = {
autoplay: true,
controls: true,
responsive: true,
fill: true,
nativeControlsForTouch: false,
playbackRates: [0.5, 1, 1.5, 2],
sources: [
{
src: videoUrl as string,
type: type as string,
},
],
} as VideoJsPlayerOptions;
useEffect((): any => {
const videoElement = videoRef.current;
// mount
if (videoElement) {
// #ts-ignore
player = videojs(
videoElement,
videoJsOptions,
() => {
player.addRemoteTextTrack({
kind: 'captions',
src: transcriptionUrl || '',
label: 'English',
language: 'en',
srcLang: 'en',
default: true,
}, false);
player.on('progress', (): void => {
updateMinutes(player.currentTime());
});
player.on('ended', (): void => {
completeProgress(player.currentTime());
});
},
);
// set starting time
player.currentTime(startingTime);
}
// unmount
return (): void => {
if (player) {
player.dispose();
}
};
}, []);
useEffect((): void => {
if (minutes > 0) {
saveProgress(minutes);
}
}, [minutes]);
return (
<Box
fill="horizontal"
align="center"
background="yellow"
style={{
height: 700,
}}
>
<video
className="video-js vjs-big-play-centered"
ref={videoRef}
/>
</Box>
);
};
VideoJsPlayer.displayName = 'VideoJsPlayer';
export default VideoJsPlayer;
The only details is the url to play: it must be /manifest(format=mpd-time-csf)

Related

How to cluster polygons with react-leaflet?

I'm looking for a way to cluster polygons using react-leaflet v4 and react-leaflet-markercluster. I have not found any up-to-date examples of how I can achieve this, so I'm hoping I might get some help here.
Any example code to get me started would be a great help!
This will probably not solve your problem directly but hopefully show that using markercluster is rather simple. The only thing you need is to have a createMarkerCluster function.
clusterProps has a field for polygonOptions:
/*
* Options to pass when creating the L.Polygon(points, options) to show the bounds of a cluster.
* Defaults to empty
*/
polygonOptions?: PolylineOptions | undefined;
Since you now use a plain leaflet plugin it opens up for mor information on the internet, these two might help how you should configure polygonOptions
How to make MarkerClusterGroup cluster polygons
https://gis.stackexchange.com/questions/197882/is-it-possible-to-cluster-polygons-in-leaflet
Below is my general code to make clustermarkers work with React:
import { createPathComponent } from "#react-leaflet/core";
import L, { LeafletMouseEventHandlerFn } from "leaflet";
import "leaflet.markercluster";
import { ReactElement, useMemo } from "react";
import { Building, BuildingStore, Circle } from "tabler-icons-react";
import { createLeafletIcon } from "./utils";
import styles from "./LeafletMarkerCluster.module.css";
import "leaflet.markercluster/dist/MarkerCluster.css";
type ClusterType = { [key in string]: any };
type ClusterEvents = {
onClick?: LeafletMouseEventHandlerFn;
onDblClick?: LeafletMouseEventHandlerFn;
onMouseDown?: LeafletMouseEventHandlerFn;
onMouseUp?: LeafletMouseEventHandlerFn;
onMouseOver?: LeafletMouseEventHandlerFn;
onMouseOut?: LeafletMouseEventHandlerFn;
onContextMenu?: LeafletMouseEventHandlerFn;
};
// Leaflet is badly typed, if more props needed add them to the interface.
// Look in this file to see what is available.
// node_modules/#types/leaflet.markercluster/index.d.ts
// MarkerClusterGroupOptions
export interface LeafletMarkerClusterProps {
spiderfyOnMaxZoom?: boolean;
children: React.ReactNode;
size?: number;
icon?: ReactElement;
}
const createMarkerCluster = (
{
children: _c,
size = 30,
icon = <Circle size={size} />,
...props
}: LeafletMarkerClusterProps,
context: any
) => {
const markerIcons = {
default: <Circle size={size} />,
property: <Building size={size} />,
business: <BuildingStore size={size} />,
} as { [key in string]: ReactElement };
const clusterProps: ClusterType = {
iconCreateFunction: (cluster: any) => {
const markers = cluster.getAllChildMarkers();
const types = markers.reduce(
(
acc: { [x: string]: number },
marker: {
key: string;
options: { icon: { options: { className: string } } };
}
) => {
const key = marker?.key || "";
const type =
marker.options.icon.options.className || key.split("-")[0];
const increment = (key.split("-")[1] as unknown as number) || 1;
if (type in markerIcons) {
return { ...acc, [type]: (acc[type] || 0) + increment };
}
return { ...acc, default: (acc.default || 0) + increment };
},
{}
) as { [key in string]: number };
const typeIcons = Object.entries(types).map(([type, count], index) => {
if (count > 0) {
const typeIcon = markerIcons[type];
return (
<div key={`${type}-${count}`} style={{ display: "flex" }}>
<span>{typeIcon}</span>
<span style={{ width: "max-content" }}>{count}</span>
</div>
);
}
});
const iconWidth = typeIcons.length * size;
return createLeafletIcon(
<div style={{ display: "flex" }} className={"cluster-marker"}>
{typeIcons}
</div>,
iconWidth,
undefined,
iconWidth,
30
);
},
showCoverageOnHover: false,
animate: true,
animateAddingMarkers: false,
removeOutsideVisibleBounds: false,
};
const clusterEvents: ClusterType = {};
// Splitting props and events to different objects
Object.entries(props).forEach(([propName, prop]) =>
propName.startsWith("on")
? (clusterEvents[propName] = prop)
: (clusterProps[propName] = prop)
);
const instance = new (L as any).MarkerClusterGroup(clusterProps);
instance.on("spiderfied", (e: any) => {
e.cluster._icon?.classList.add(styles.spiderfied);
});
instance.on("unspiderfied", (e: any) => {
e.cluster._icon?.classList.remove(styles.spiderfied);
});
// This is not used at the moment, but could be used to add events to the cluster.
// Initializing event listeners
Object.entries(clusterEvents).forEach(([eventAsProp, callback]) => {
const clusterEvent = `cluster${eventAsProp.substring(2).toLowerCase()}`;
instance.on(clusterEvent, callback);
});
return {
instance,
context: {
...context,
layerContainer: instance,
},
};
};
const updateMarkerCluster = (instance: any, props: any, prevProps: any) => {};
const LeafletMarkerCluster = createPathComponent(
createMarkerCluster,
updateMarkerCluster
);
const LeafletMarkerClusterWrapper: React.FC<LeafletMarkerClusterProps> = ({
children,
...props
}) => {
const markerCluster = useMemo(() => {
return <LeafletMarkerCluster>{children}</LeafletMarkerCluster>;
}, [children]);
return <>{markerCluster}</>;
};
export default LeafletMarkerClusterWrapper;
Below is my function to create a marker icon from react elements:
import { divIcon } from "leaflet";
import { ReactElement } from "react";
import { renderToString } from "react-dom/server";
export const createLeafletIcon = (
icon: ReactElement,
size: number,
className?: string,
width: number = size,
height: number = size
) => {
return divIcon({
html: renderToString(icon),
iconSize: [width, height],
iconAnchor: [width / 2, height],
popupAnchor: [0, -height],
className: className ? className : "",
});
};

Using DraftJS in a Functional Component

I am trying to implement DraftJS within an existing functional component and I am unable to figure out how to do this. It appears that all of the documentation and user-submitted content refers to class components.
I try to set it up using the following:
import { Editor } from "react-draft-wysiwyg";
import { EditorState } from 'draft-js'
export default function myFunctionalComponent() {
const [editorState, setEditorState] = useState(EditorState.createEmpty())
return(
<Editor
editorState={editorState}
onChange={setEditorState}
/>
)
}
However, unfortunately, I get this error in the console:
Warning: Can't call setState on a component that is not yet mounted.
This is a no-op, but it might indicate a bug in your application.
Instead, assign to this.state directly or define a state = {};
class property with the desired state in the r component.
Is there a way to make this work in a functional component?
As my very first answer in StackOverflow. :)
I took the example from https://github.com/facebook/draft-js/blob/main/examples/draft-0-10-0/rich/rich.html and converted it into a functional components 'RTEditor', .. .
Use the component with setContent as a prop. It takes the function to update parent elements state from useState
const [content, setContent] = useState<any>({})
...
<RTEditor setContent={setContent} />
RTEditor.tsx
import React, { useState, useRef } from 'react'
import {
Editor,
EditorState,
RichUtils,
getDefaultKeyBinding,
ContentBlock,
DraftHandleValue,
convertFromHTML,
convertFromRaw,
convertToRaw,
ContentState,
RawDraftContentState,
} from 'draft-js'
import 'draft-js/dist/Draft.css'
import BlockStyleControls from './BlockStyleControls'
import InlineStyleControls from './InlineStyleControls'
type Props = {
setContent: (state: RawDraftContentState) => void
}
const RTEditor = ({ setContent }: Props) => {
const editorRef = useRef(null)
const [editorState, setEditorState] = useState(EditorState.createEmpty())
const styleMap = {
CODE: {
backgroundColor: 'rgba(0, 0, 0, 0.05)',
fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
fontSize: 16,
padding: 2,
},
}
const getBlockStyle = (block: ContentBlock) => {
switch (block.getType()) {
case 'blockquote':
return 'RichEditor-blockquote'
default:
return ''
}
}
const onChange = (state: EditorState) => {
setEditorState(state)
setContent(convertToRaw(editorState.getCurrentContent()))
}
const mapKeyToEditorCommand = (e: any): string | null => {
if (e.keyCode === 9 /* TAB */) {
const newEditorState = RichUtils.onTab(e, editorState, 4 /* maxDepth */)
if (newEditorState !== editorState) {
onChange(newEditorState)
}
return null
}
return getDefaultKeyBinding(e)
}
const handleKeyCommand = (
command: string,
editorState: EditorState,
eventTimeStamp: number
): DraftHandleValue => {
const newState = RichUtils.handleKeyCommand(editorState, command)
if (newState) {
onChange(newState)
return 'handled'
}
return 'not-handled'
}
const toggleBlockType = (blockType: string) => {
onChange(RichUtils.toggleBlockType(editorState, blockType))
}
const toggleInlineStyle = (inlineStyle: string) => {
onChange(RichUtils.toggleInlineStyle(editorState, inlineStyle))
}
return (
<>
<BlockStyleControls
editorState={editorState}
onToggle={toggleBlockType}
/>
<InlineStyleControls
editorState={editorState}
onToggle={toggleInlineStyle}
/>
<Editor
ref={editorRef}
editorState={editorState}
placeholder='Tell a story...'
customStyleMap={styleMap}
blockStyleFn={(block: ContentBlock) => getBlockStyle(block)}
keyBindingFn={(e) => mapKeyToEditorCommand(e)}
onChange={onChange}
spellCheck={true}
handleKeyCommand={handleKeyCommand}
/>
</>
)
}
export default React.memo(RTEditor)
BlockStyleControls.tsx
import React from 'react'
import { EditorState } from 'draft-js'
import StyleButton from './StyleButton'
const BLOCK_TYPES = [
{ label: 'H1', style: 'header-one' },
{ label: 'H2', style: 'header-two' },
{ label: 'H3', style: 'header-three' },
{ label: 'H4', style: 'header-four' },
{ label: 'H5', style: 'header-five' },
{ label: 'H6', style: 'header-six' },
{ label: 'Blockquote', style: 'blockquote' },
{ label: 'UL', style: 'unordered-list-item' },
{ label: 'OL', style: 'ordered-list-item' },
{ label: 'Code Block', style: 'code-block' },
]
type Props = {
editorState: EditorState
onToggle: (bockType: string) => void
}
const BlockStyleControls = ({ editorState, onToggle }: Props) => {
const selection = editorState.getSelection()
const blockType = editorState
.getCurrentContent()
.getBlockForKey(selection.getStartKey())
.getType()
return (
<div className='RichEditor-controls'>
{BLOCK_TYPES.map((type) => (
<StyleButton
key={type.label}
active={type.style === blockType}
label={type.label}
onToggle={onToggle}
style={type.style}
/>
))}
</div>
)
}
export default React.memo(BlockStyleControls)
InlineStyleControls.tsx
import React from 'react'
import { EditorState } from 'draft-js'
import StyleButton from './StyleButton'
const INLINE_STYLES = [
{ label: 'Bold', style: 'BOLD' },
{ label: 'Italic', style: 'ITALIC' },
{ label: 'Underline', style: 'UNDERLINE' },
{ label: 'Monospace', style: 'CODE' },
]
type Props = {
editorState: EditorState
onToggle: (bockType: string) => void
}
const InlineStyleControls = ({ editorState, onToggle }: Props) => {
const currentStyle = editorState.getCurrentInlineStyle()
return (
<div className='RichEditor-controls'>
{INLINE_STYLES.map((type) => (
<StyleButton
key={type.label}
active={currentStyle.has(type.style)}
label={type.label}
onToggle={onToggle}
style={type.style}
/>
))}
</div>
)
}
export default React.memo(InlineStyleControls)
StyleButton.tsx
import React from 'react'
type Props = {
active: boolean
style: string
label: string
onToggle: (bockType: string) => void
}
const StyleButton = ({ active, style, label, onToggle }: Props) => {
const _onToggle = (e: any) => {
e.preventDefault()
onToggle(style)
}
const className = 'RichEditor-styleButton'
return (
<button
className={className + `${active ? ' RichEditor-activeButton' : ''}`}
onClick={_onToggle}
>
{label}
</button>
)
}
export default React.memo(StyleButton)
Sry for not covering all typings. Hope that helps.
I was able to solve this using React useCallback hook and it works for me.
import { EditorState } from 'draft-js'
export default function myFunctionalComponent() {
const [editorState, setEditorState] = useState(EditorState.createEmpty())
const onEditorStateChange = useCallback(
(rawcontent) => {
setEditorState(rawcontent.blocks[0].text);
},
[editorState]
);
return(
<Editor
placeholder="Tell a story..."
onChange={onEditorStateChange}
/>
)
}

How to properly setup Azure Media Player in React.js?

I'm currently integrating a React component with Azure Media Player. I followed the documentation and first, I added the required CDN urls to the index.html file. Then I added the sample code into the App. The problem is, it throws the error 'amp' is not defined no-undef
videoPlayer.js
class videoPlayer extends Component {
render () {
const myOptions = {
"nativeControlsForTouch": false,
controls: true,
autoplay: true,
width: "640",
height: "400",
}
const myPlayer = amp("azuremediaplayer", myOptions);
myPlayer.src([
{
"src": "https://devpflmedia-uswe.streaming.media.azure.net//d5f1a8b6-0d52-4e62-addc-aee7bafe408d/097cee43-6822-49bd-84f5-9f6efb05.ism/manifest",
"type": "application/vnd.ms-sstr+xml"
}
]);
return (
<video id="azuremediaplayer" class="azuremediaplayer amp-default-skin amp-big-play-centered" tabindex="0"></video>
)
}
}
How can I fix this?
When I use the amp this way, the mentioned on.progress callback works for me. Good luck!
import * as React from "react"
import loader from "./loader";
import { RefObject } from "react";
import './videoPlayer.css';
const DEFAULT_SKIN = "amp-flush";
const DEFAULT_RATIO = [16, 9];
const DEFAULT_OPTIONS = {
controls: true,
autoplay: true,
muted: true,
logo: {
enabled: false
},
};
declare const window: any;
export interface IVideoPlayerProps {
readonly src: { src: string; }[];
readonly options: any;
readonly skin: string;
readonly className: string;
readonly adaptRatio: Array<number>;
}
export default class VideoPlayer extends React.PureComponent<IVideoPlayerProps, {}> {
public static defaultProps = {
skin: DEFAULT_SKIN,
className: "",
adaptRatio: DEFAULT_RATIO,
options: DEFAULT_OPTIONS,
}
videoNode: RefObject<any>;
player: any;
initialization: any;
constructor(props: IVideoPlayerProps) {
super(props);
this.videoNode = React.createRef();
}
componentWillUnmount() {
this._destroyPlayer();
}
componentDidMount() {
const { skin } = this.props;
this.initialization = loader(skin).then(() => {
this._createPlayer();
this._setVideo();
});
}
componentDidUpdate(prevProps: IVideoPlayerProps) {
if (prevProps.src !== this.props.src) {
this.initialization.then(() => this._setVideo());
}
}
_destroyPlayer() {
this.player && this.player.dispose();
}
_setVideo() {
const { src } = this.props;
this.player.src(src);
}
_createPlayer() {
this.player = window.amp(this.videoNode.current, this.props.options);
this.player.on("progress", () => alert('on progress called'));
}
render(): JSX.Element {
return (
<div>
<video
ref={this.videoNode}
/>
</div>
);
}
}
Also the loader function - I use it this way since I may need to use the player in the (possible) offline environment.
export default (skin = 'amp-flush') => {
return new Promise((resolve, _) => {
if (document.querySelector('#amp-azure')) {
// video player is already rendered
return resolve()
}
let scriptTag = document.createElement('script')
let linkTag = document.createElement('link')
linkTag.rel = 'stylesheet'
scriptTag.id = 'amp-azure'
scriptTag.src = '//amp.azure.net/libs/amp/2.1.5/azuremediaplayer.min.js'
linkTag.href = `//amp.azure.net/libs/amp/2.1.5/skins/${skin}/azuremediaplayer.min.css`
document.body.appendChild(scriptTag)
document.head.insertBefore(linkTag, document.head.firstChild)
scriptTag.onload = () => resolve({ skin: skin })
})
}

InvalidValueError: setMap: not an instance of Map; and not an instance of StreetViewPanorama #react-google-maps/api

I am using #react-google-maps/api and loading the script from cdn to my public directory index.html file.
I get InvalidValueError: setMap: not an instance of Map; and not an instance of StreetViewPanorama and the markers do not appear on my map. Weird thing is that it is totally random to get this error. Sometimes it works without any problem. That is what I do not understand in the first place.
import React, { useEffect } from 'react';
import { GoogleMap, Marker } from '#react-google-maps/api';
import mapStyles from './mapStyles';
//google map height 100
import './MapDisplay.css';
import { connect } from 'react-redux';
const mapContainerStyle = {
height: '100%',
width: '100%',
};
const options = {
styles: mapStyles,
disableDefaultUI: true,
zoomControl: true,
};
const MapDisplay = ({
userLocation,
mouseHoverIdR,
selectedInfoR,
searchedResults,
unAuthNoSearchResults,
selectedLocationInfoWindowS,
}) => {
const onClickMarker = (e) => {
selectedLocationInfoWindowS({
selectedInfoR: e,
type: 'infoWindowLocations',
});
};
const onClickMap = () => {
selectedLocationInfoWindowS({
type: '',
selectedInfoR: {
_id: '',
},
});
};
return (
<div id='map'>
<GoogleMap
id='map_canvas'
mapContainerStyle={mapContainerStyle}
zoom={12}
center={userLocation}
options={options}
onClick={() => onClickMap()}
>
{!unAuthNoSearchResults &&
searchedResults.map((searchedResult) => {
if (
mouseHoverIdR === searchedResult._id ||
selectedInfoR._id === searchedResult._id
) {
var a = window.google.maps.Animation.BOUNCE;
}
return (
<Marker
position={searchedResult.coordinates}
key={searchedResult._id}
clickable
onClick={() => onClickMarker(searchedResult)}
animation={a}
id={'hello'}
/>
);
})}
</GoogleMap>
</div>
);
};
How can I fix the error that I get when I try to display the marker on the map?

onImageLoad callback react js

Im using react-image-gallery: https://www.npmjs.com/package/react-image-gallery
Im trying to set a useState variable on onImageLoad, but its not working.
the docs say to use a callback, and I tried using a callback but I don't think I was doing it correctly for functional components.
Could someone show me how to create the proper callback to get the onImageLoad prop?
Docs say..... onImageLoad: Function, callback(event)
import React, { useEffect, useState} from "react";
import ImageGallery from 'react-image-gallery';
import "react-image-gallery/styles/css/image-gallery.css";
const Job = (props) => {
const {job} = props;
const [image, setImage] = useState([]);
const [showImages, setShowImages] = useState(false);
useEffect(() => {
async function onLoad() {
try {
const downloadedImage = await getImage(job.jobId);
setImage(downloadedImage);
} catch (e) {
alert(e);
}
}
onLoad();
}, []);
return (
{showImages ? (
<div style={{width: "95%"}}>
<ImageGallery items={image} onImageLoad={() => setShowImages(true)}/>
</div>
):(
<div>
<IonSpinner name="crescent" />
</div>
)}
);
Example given from package website
import React from 'react';
import ReactDOM from 'react-dom';
import ImageGallery from '../src/ImageGallery';
const PREFIX_URL = 'https://raw.githubusercontent.com/xiaolin/react-image-gallery/master/static/';
class App extends React.Component {
constructor() {
super();
this.state = {
showIndex: false,
showBullets: true,
infinite: true,
showThumbnails: true,
showFullscreenButton: true,
showGalleryFullscreenButton: true,
showPlayButton: true,
showGalleryPlayButton: true,
showNav: true,
isRTL: false,
slideDuration: 450,
slideInterval: 2000,
slideOnThumbnailOver: false,
thumbnailPosition: 'bottom',
showVideo: {},
};
this.images = [
{
thumbnail: `${PREFIX_URL}4v.jpg`,
original: `${PREFIX_URL}4v.jpg`,
embedUrl: 'https://www.youtube.com/embed/4pSzhZ76GdM?autoplay=1&showinfo=0',
description: 'Render custom slides within the gallery',
renderItem: this._renderVideo.bind(this)
},
{
original: `${PREFIX_URL}image_set_default.jpg`,
thumbnail: `${PREFIX_URL}image_set_thumb.jpg`,
imageSet: [
{
srcSet: `${PREFIX_URL}image_set_cropped.jpg`,
media : '(max-width: 1280px)',
},
{
srcSet: `${PREFIX_URL}image_set_default.jpg`,
media : '(min-width: 1280px)',
}
]
},
{
original: `${PREFIX_URL}1.jpg`,
thumbnail: `${PREFIX_URL}1t.jpg`,
originalClass: 'featured-slide',
thumbnailClass: 'featured-thumb',
description: 'Custom class for slides & thumbnails'
},
].concat(this._getStaticImages());
}
_onImageLoad(event) {
console.debug('loaded image', event.target.src);
}
render() {
return (
<section className='app'>
<ImageGallery
ref={i => this._imageGallery = i}
items={this.images}
onImageLoad={this._onImageLoad}
/>
</section>
);
}
}
ReactDOM.render(<App/>, document.getElementById('container'));

Resources