diff --git a/ui/src/parts/lightbox.tsx b/ui/src/parts/lightbox.tsx index f4dc53fd969..c7f765abe50 100644 --- a/ui/src/parts/lightbox.tsx +++ b/ui/src/parts/lightbox.tsx @@ -38,9 +38,13 @@ const touchAction: 'pinch-zoom' // TODO: }, img: { - flexGrow: 1, - objectFit: 'contain', - cursor: 'pointer' + // flexGrow: 1, + maxWidth: '100%', + alignSelf: 'center', + // objectFit: 'scale-down', + transformOrigin: 'top left', + transition: 'transform 0.25s ease', + cursor: 'pointer', }, header: { width: '100%', @@ -49,7 +53,10 @@ const }, content: { display: 'flex', + position: 'relative', maxWidth: '100%', + overflow: 'auto', + justifyContent: 'center', }, footer: { textAlign: 'center', @@ -58,12 +65,14 @@ const }, imageNav: { height: '180px', - padding: '20px 0px', + paddingTop: '20px', overflow: 'auto', whiteSpace: 'nowrap' }, navImg: { height: '160px', + objectFit: 'cover', + width: '160px', padding: '0px 1px', opacity: 0.6, $nest: { @@ -85,7 +94,18 @@ const transition: "0.6s ease", borderRadius: "0 3px 3px 0", userSelect: "none", - } + }, + imgCaptions: { + whiteSpace: 'nowrap', + padding: '10px 40px', + height: '20px' + }, + text: { + textOverflow: 'ellipsis', + overflow: 'hidden', + }, + title: { fontWeight: 500 }, + description: { opacity: 0.85 } }) interface Props { @@ -101,9 +121,23 @@ interface Props { defaultImageIdx?: U } +const + zoomLevels: { [key: U]: U } = { 0: 1, 1: 1.5, 2: 2, 3: 3, 4: 4 }, + lazyImageObserver = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const lazyImage = entry.target as HTMLImageElement + lazyImage.src = lazyImage.dataset.src! + lazyImage.classList.remove("lazy") + lazyImageObserver.unobserve(lazyImage) + } + }) + }) + export const Lightbox = ({ visible, onDismiss, images, defaultImageIdx }: Props) => { const [activeImageIdx, setActiveImageIdx] = React.useState(defaultImageIdx || 0), + [zoomLevel, setZoomLevel] = React.useState(0), imageNavRef = React.useRef() React.useEffect(() => { @@ -111,6 +145,12 @@ export const Lightbox = ({ visible, onDismiss, images, defaultImageIdx }: Props) // eslint-disable-next-line react-hooks/exhaustive-deps }, [imageNavRef.current, activeImageIdx]) + React.useLayoutEffect(() => { + const lazyImages = [].slice.call(document.querySelectorAll(".lazy")) + lazyImages.forEach(lazyImage => lazyImageObserver.observe(lazyImage)) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + if (images.length === 0 || (activeImageIdx >= images.length)) return <>{'Error'} // TODO: const { type, image, path, title, description } = images[activeImageIdx], @@ -120,33 +160,53 @@ export const Lightbox = ({ visible, onDismiss, images, defaultImageIdx }: Props) ? `data:image/${type};base64,${image}` : '' - - - return (
{ + setZoomLevel(0) + onDismiss() + }} iconProps={{ iconName: 'Cancel', style: { fontSize: '22px' } }} />
-
1 ? '300px' : '100px'})` }}> - {title} +
1 ? '300px' : '100px'})` }}> + {title} { + const zoom = zoomLevel === 4 ? 0 : zoomLevel + 1 + setZoomLevel(zoom) + }} + /> {images.length > 1 && <>
setActiveImageIdx((activeImageIdx === 0) ? (images.length - 1) : activeImageIdx - 1)} + onClick={() => { + setActiveImageIdx((activeImageIdx === 0) ? (images.length - 1) : activeImageIdx - 1) + setZoomLevel(0) + }} iconProps={{ iconName: 'ChevronLeft', style: { fontSize: '22px' } }} />
setActiveImageIdx((activeImageIdx === images.length - 1) ? 0 : activeImageIdx + 1)} + onClick={() => { + setActiveImageIdx((activeImageIdx === images.length - 1) ? 0 : activeImageIdx + 1) + setZoomLevel(0) + }} iconProps={{ iconName: 'ChevronRight', style: { fontSize: '22px' } }} />
@@ -154,7 +214,10 @@ export const Lightbox = ({ visible, onDismiss, images, defaultImageIdx }: Props) }
1 ? '260px' : '60px' }}> - {title}{description ? ` - ${description}` : ''} +
+
{title}
+
{description ? description : ''}
+
{images.length > 1 &&
{images.map(({ type, image, path, title }, idx) => { const src = path @@ -162,7 +225,7 @@ export const Lightbox = ({ visible, onDismiss, images, defaultImageIdx }: Props) : (image && type) ? `data:image/${type};base64,${image}` : '' - return {title} { setActiveImageIdx(idx) }} /> + return {title} { setActiveImageIdx(idx) }} /> })}
}