yet-another-react-lightbox
Version:
Modern React lightbox component
135 lines (130 loc) • 6.25 kB
JavaScript
import * as React from 'react';
import { useLightboxProps, useEvents, useContainerRect, useEventCallback, reflow, clsx, cssClass } from '../../index.js';
import { ACTIVE_SLIDE_LOADING, CLASS_FLEX_CENTER, CLASS_SLIDE_WRAPPER, ACTIVE_SLIDE_COMPLETE, ACTIVE_SLIDE_PLAYING } from '../../types.js';
const defaultVideoProps = {
controls: true,
playsInline: true,
};
const resolveVideoProps = (video) => ({
...defaultVideoProps,
...video,
});
function useVideoProps() {
const { video } = useLightboxProps();
return resolveVideoProps(video);
}
function VideoSlide({ slide, offset }) {
const video = useVideoProps();
const { publish } = useEvents();
const { setContainerRef, containerRect, containerRef } = useContainerRect();
const { animation } = useLightboxProps();
const videoRef = React.useRef(null);
React.useEffect(() => {
if (offset !== 0 && videoRef.current && !videoRef.current.paused) {
videoRef.current.pause();
}
}, [offset]);
React.useEffect(() => {
if (offset === 0 && videoRef.current && (slide.autoPlay || video.autoPlay)) {
publish(ACTIVE_SLIDE_LOADING);
videoRef.current.play().catch(() => { });
}
}, [offset, video.autoPlay, slide.autoPlay, publish]);
const fixupPlayerControls = useEventCallback(() => {
const timeoutId = setTimeout(() => {
if (containerRef.current) {
const borderStyle = containerRef.current.style.border;
containerRef.current.style.border = "1px solid transparent";
reflow(containerRef.current);
containerRef.current.style.border = borderStyle;
}
}, Math.max(animation.swipe, animation.navigation || 0) + 50);
return () => clearTimeout(timeoutId);
});
React.useEffect(() => {
var _a;
const isChromium = ((_a = navigator.userAgentData) === null || _a === void 0 ? void 0 : _a.brands.some(({ brand }) => brand === "Chromium")) || !!window.chrome;
if (isChromium && offset === 0) {
return fixupPlayerControls();
}
}, [offset, fixupPlayerControls]);
const handleVideoRef = useEventCallback((node) => {
if (offset === 0 && (video.autoPlay || slide.autoPlay) && node.paused) {
node.play().catch(() => { });
}
});
const setVideoRef = React.useCallback((node) => {
videoRef.current = node;
if (node) {
handleVideoRef(node);
}
}, [handleVideoRef]);
const { width, height, poster, sources } = slide;
const scaleWidthAndHeight = () => {
const scalingProps = {};
scalingProps.style = { maxWidth: "100%", maxHeight: "100%" };
if (width && height && containerRect) {
const widthBound = width / height > containerRect.width / containerRect.height;
const elementWidth = widthBound ? containerRect.width : Math.round((containerRect.height / height) * width);
const elementHeight = !widthBound ? containerRect.height : Math.round((containerRect.width / width) * height);
scalingProps.width = elementWidth;
scalingProps.height = elementHeight;
scalingProps.style.width = elementWidth;
scalingProps.style.height = elementHeight;
}
return scalingProps;
};
const resolveBoolean = (attr) => {
if (slide[attr] === false)
return null;
if (slide[attr] === true)
return { [attr]: true };
if (video[attr] === false)
return null;
if (video[attr] === true)
return { [attr]: true };
return null;
};
const resolveString = (attr) => {
if (video[attr] || slide[attr]) {
return { [attr]: slide[attr] || video[attr] };
}
return null;
};
const stopPropagationInSafari = (event) => {
if (/^((?!chrome|android).)*(safari|mobile)/i.test(navigator.userAgent)) {
event.stopPropagation();
}
};
return (React.createElement(React.Fragment, null, sources && (React.createElement("div", { ref: setContainerRef, style: {
width: "100%",
height: "100%",
...(width ? { maxWidth: `${width}px` } : null),
}, className: clsx(cssClass("video_container"), cssClass(CLASS_FLEX_CENTER), cssClass(CLASS_SLIDE_WRAPPER)) }, containerRect && (React.createElement("video", { ref: setVideoRef, poster: poster, ...scaleWidthAndHeight(), ...resolveBoolean("controls"), ...resolveBoolean("playsInline"), ...resolveBoolean("loop"), ...resolveBoolean("muted"), ...resolveBoolean("playsInline"), ...resolveBoolean("disablePictureInPicture"), ...resolveBoolean("disableRemotePlayback"), ...resolveString("controlsList"), ...resolveString("crossOrigin"), ...resolveString("preload"), onPlay: () => {
var _a;
if (offset !== 0) {
(_a = videoRef.current) === null || _a === void 0 ? void 0 : _a.pause();
return;
}
publish(ACTIVE_SLIDE_PLAYING);
}, onEnded: () => {
publish(ACTIVE_SLIDE_COMPLETE);
}, onPointerUp: stopPropagationInSafari, onPointerDown: stopPropagationInSafari, onPointerMove: stopPropagationInSafari }, sources.map(({ src, type, media }) => (React.createElement("source", { key: [src, type, media].filter(Boolean).join("|"), src: src, type: type, media: media })))))))));
}
function isVideoSlide(slide) {
return slide.type === "video";
}
function Video({ augment }) {
augment(({ render: { slide: renderSlide, ...restRender }, video, ...restProps }) => ({
render: {
slide: ({ slide, offset, rect }) => {
var _a;
return isVideoSlide(slide) ? (React.createElement(VideoSlide, { key: (_a = slide.sources) === null || _a === void 0 ? void 0 : _a.map((source) => source.src).join("|"), slide: slide, offset: offset })) : (renderSlide === null || renderSlide === void 0 ? void 0 : renderSlide({ slide, offset, rect }));
},
...restRender,
},
video: resolveVideoProps(video),
...restProps,
}));
}
export { Video as default };