From 44df7918de3d7ab701369c80bda47cbd23a1e363 Mon Sep 17 00:00:00 2001 From: Marek Mihok Date: Mon, 26 Sep 2022 21:54:58 +0200 Subject: [PATCH] feat: add multiple image support #1542 --- ui/src/image.tsx | 2 +- ui/src/parts/lightbox.tsx | 173 ++++++++++++++++++++++++++++---------- 2 files changed, 130 insertions(+), 45 deletions(-) diff --git a/ui/src/image.tsx b/ui/src/image.tsx index edd0f0f8084..594e0c6387b 100644 --- a/ui/src/image.tsx +++ b/ui/src/image.tsx @@ -77,7 +77,7 @@ export const : '' return <> {title} setLightboxVisible(true)} /> - setLightboxVisible(false)} /> + setLightboxVisible(false)} /> }, View = bond(({ name, state, changed }: Model) => { diff --git a/ui/src/parts/lightbox.tsx b/ui/src/parts/lightbox.tsx index 95bd20bd47e..f4dc53fd969 100644 --- a/ui/src/parts/lightbox.tsx +++ b/ui/src/parts/lightbox.tsx @@ -12,75 +12,160 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { B, S } from 'h2o-wave' +import { B, S, U } from 'h2o-wave' import React from 'react' import { stylesheet } from 'typestyle' import * as Fluent from '@fluentui/react' +import { clas } from '../theme' -const css = stylesheet({ - body: { - position: 'fixed', - inset: '0px', - width: '100%', - height: '100%', - zIndex: 1, - backgroundColor: '#000000', // TODO: - touchAction: 'pinch-zoom' // TODO: +const + iconStyles: Fluent.IButtonStyles = { + icon: { color: '#ffffff', lineHeight: 22, height: 'unset', padding: 4 }, + iconHovered: { color: '#ffffff' }, // TODO: + iconPressed: { color: 'rgba(255, 255, 255, 0.7)' }, + flexContainer: { justifyContent: 'center' }, + root: { margin: '4px 4px', width: 38, height: 38, backgroundColor: 'rgba(0, 0, 0, 0.3)' }, + rootHovered: { backgroundColor: 'rgba(255, 255, 255, 0.3)' } }, - img: { - flexGrow: 1, - objectFit: 'contain', - cursor: 'pointer' - }, - header: { - width: '100%', - height: '40px', - textAlign: 'right' - }, - content: { - display: 'flex', - maxHeight: 'calc(100% - 100px)', - maxWidth: '100%', - }, - footer: { - height: '60px', - textAlign: 'center', - color: '#ffffff', // TODO: - width: '100%' - } -}) + css = stylesheet({ + body: { + position: 'fixed', + inset: '0px', + width: '100%', + height: '100%', + zIndex: 1, + backgroundColor: '#000000', // TODO: + touchAction: 'pinch-zoom' // TODO: + }, + img: { + flexGrow: 1, + objectFit: 'contain', + cursor: 'pointer' + }, + header: { + width: '100%', + height: '40px', + textAlign: 'right' + }, + content: { + display: 'flex', + maxWidth: '100%', + }, + footer: { + textAlign: 'center', + color: '#ffffff', // TODO: + width: '100%' + }, + imageNav: { + height: '180px', + padding: '20px 0px', + overflow: 'auto', + whiteSpace: 'nowrap' + }, + navImg: { + height: '160px', + padding: '0px 1px', + opacity: 0.6, + $nest: { + '&:hover': { + opacity: 1 + } + } + }, + arrow: { + cursor: "pointer", + position: "absolute", + top: "50%", + width: "auto", + padding: "16px", + marginTop: "-50px", + color: "white", + fontWeight: "bold", + fontSize: "20px", + transition: "0.6s ease", + borderRadius: "0 3px 3px 0", + userSelect: "none", + } + }) interface Props { visible: B, onDismiss: () => void, - title: S, - description?: S, - type?: S, - image?: S, - path?: S, + images: { + title: S, + description?: S, + type?: S, + image?: S, + path?: S, + }[], + defaultImageIdx?: U } -export const Lightbox = ({ visible, onDismiss, type, image, path, title, description }: Props) => { +export const Lightbox = ({ visible, onDismiss, images, defaultImageIdx }: Props) => { + const + [activeImageIdx, setActiveImageIdx] = React.useState(defaultImageIdx || 0), + imageNavRef = React.useRef() + + React.useEffect(() => { + if (imageNavRef.current) imageNavRef.current.scrollLeft = activeImageIdx * 162 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [imageNavRef.current, activeImageIdx]) + + if (images.length === 0 || (activeImageIdx >= images.length)) return <>{'Error'} // TODO: const + { type, image, path, title, description } = images[activeImageIdx], src = path ? path : (image && type) ? `data:image/${type};base64,${image}` : '' + + + + return (
-
{title}
-
{title}{description ? ` - ${description}` : ''}
+
1 ? '300px' : '100px'})` }}> + {title} + {images.length > 1 && + <> +
+ setActiveImageIdx((activeImageIdx === 0) ? (images.length - 1) : activeImageIdx - 1)} + iconProps={{ iconName: 'ChevronLeft', style: { fontSize: '22px' } }} + /> +
+
+ setActiveImageIdx((activeImageIdx === images.length - 1) ? 0 : activeImageIdx + 1)} + iconProps={{ iconName: 'ChevronRight', style: { fontSize: '22px' } }} + /> +
+ + } +
+
1 ? '260px' : '60px' }}> + {title}{description ? ` - ${description}` : ''} + {images.length > 1 &&
+ {images.map(({ type, image, path, title }, idx) => { + const src = path + ? path + : (image && type) + ? `data:image/${type};base64,${image}` + : '' + return {title} { setActiveImageIdx(idx) }} /> + })} +
} +
) }