UNPKG

@onesy/ui-react

Version:
510 lines (509 loc) 18.9 kB
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; const _excluded = ["ref", "version", "size", "open", "onClose", "value", "items", "minZoom", "maxZoom", "incrementZoom", "overflow", "arrows", "startMain", "endMain", "startImage", "endImage", "startThumbnails", "endThumbnails", "ImageProps", "ImageWrapperProps", "IconButtonProps", "IconStart", "IconEnd", "IconClose", "className"]; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } import React from 'react'; import { clamp, is } from '@onesy/utils'; import { classNames, style as styleMethod, useOnesyTheme } from '@onesy/style-react'; import IconMaterialNavigateNext from '@onesy/icons-material-rounded-react/IconMaterialNavigateNextW100'; import IconMaterialNavigateBefore from '@onesy/icons-material-rounded-react/IconMaterialNavigateBeforeW100'; import IconMaterialClose from '@onesy/icons-material-rounded-react/IconMaterialCloseW100'; import ImageElement from '../Image'; import LineElement from '../Line'; import IconButtonElement from '../IconButton'; import InteractionElement from '../Interaction'; import BackdropElement from '../Backdrop'; import { staticClassName } from '../utils'; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; const useStyle = styleMethod(theme => ({ root: { position: 'relative', '&.onesy-Backdrop-root': { display: 'flex', alignItems: 'center', justifyContent: 'center', userSelect: 'none', zIndex: '14000' }, '& .onesy-Backdrop-backdrop-root': { width: '100%', height: '100%', pointerEvents: 'none' } }, version_regular: { minHeight: 'clamp(240px, 100%, 100vh)' }, pointerEventsAuto: { pointerEvents: 'auto' }, wrapper: { height: '100%' }, header: { padding: theme.methods.space.value(3, 'px'), zIndex: '1' }, main: { position: 'relative', zIndex: '14' }, main_version_modal: { height: '0', padding: theme.methods.space.value(5, 'px'), '& .onesy-Image-root': { maxHeight: '100%' // pointerEvents: 'none' } }, main_version_regular_size_small: { height: '340px' }, main_version_regular_size_regular: { height: '540px' }, main_version_regular_size_large: { height: '740px' }, noOverflow: { overflow: 'hidden' }, footer: { padding: `${theme.methods.space.value(3, 'px')} ${theme.methods.space.value(1.5, 'px')}`, zIndex: '1' }, imageWrapper: { position: 'relative', height: '0px', pointerEvents: 'none', // zIndex: 1, transition: theme.methods.transitions.make(['transform'], { duration: 100, timing_function: 'ease' }) }, image: { objectFit: 'contain', width: 'auto', height: 'auto', maxHeight: '100%', maxWidth: '100%' }, itemsWrapper: { position: 'relative', pointerEvents: 'auto', maxWidth: '100%', userSelect: 'none' }, items: { maxWidth: '1024px', overflow: 'auto hidden', '&::-webkit-scrollbar': { width: '16px', height: '16px' }, '&::-webkit-scrollbar-track, &::-webkit-scrollbar-corner': { background: 'transparent' }, '&::-webkit-scrollbar-thumb': { borderRadius: theme.methods.shape.radius.value(1, 'px'), border: '4px solid transparent', backgroundClip: 'content-box', backgroundColor: 'rgba(221, 221, 221, 0.4)', '&:hover': { backgroundColor: 'rgba(221, 221, 221, 0.7)' } } }, item: { width: '140px', height: '140px', position: 'relative', backgroundSize: 'contain', backgroundPosition: 'center', backgroundRepeat: 'no-repeat', flex: '0 0 auto', border: '2px solid transparent', cursor: 'pointer', userSelect: 'none', transition: theme.methods.transitions.make(['border', 'transform']), '&:active': { transform: 'scale(0.94)' } }, itemSelected: { border: '2px solid !important', borderColor: `${theme.methods.palette.color.value('secondary', 30)} !important` }, arrow: { flex: '0 0 auto', alignSelf: 'center', justifySelf: 'center', transition: theme.methods.transitions.make(['opacity'], { duration: 'xxs' }), '&[disabled]': { opacity: '0' } } }), { name: 'onesy-ImageGallery' }); const ImageGallery = props_ => { const theme = useOnesyTheme(); const props = _objectSpread(_objectSpread(_objectSpread({}, theme?.ui?.elements?.all?.props?.default), theme?.ui?.elements?.onesyImageGallery?.props?.default), props_); const Line = theme?.elements?.Line || LineElement; const Image = theme?.elements?.Image || ImageElement; const IconButton = theme?.elements?.IconButton || IconButtonElement; const Interaction = theme?.elements?.Interaction || InteractionElement; const Backdrop = theme?.elements?.Backdrop || BackdropElement; const { ref, version = 'modal', size = 'regular', open: open_, onClose: onClose_, value: value_, items, minZoom: minZoom_ = 1, maxZoom: maxZoom_ = 2, incrementZoom = 0.07, overflow = true, arrows = true, startMain, endMain, startImage, endImage, startThumbnails, endThumbnails, ImageProps, ImageWrapperProps, IconButtonProps, IconStart = IconMaterialNavigateBefore, IconEnd = IconMaterialNavigateNext, IconClose = IconMaterialClose, className } = props, other = _objectWithoutProperties(props, _excluded); const { classes } = useStyle(); const [, setLoaded] = React.useState(false); const [open, setOpen] = React.useState(false); const [value, setValue] = React.useState(0); const [moveValue, setMoveValue] = React.useState(); const [imageRef, setImageRef] = React.useState(); const [keyDown, setKeyDown] = React.useState(); const refs = { root: React.useRef(null), version: React.useRef(version), more: React.useRef(null), image: React.useRef(null), imageWrapper: React.useRef(null), media: React.useRef(null), zoom: React.useRef(null), incrementZoom: React.useRef(null), minZoom: React.useRef(null), maxZoom: React.useRef(null), mouseDown: React.useRef(null), mouseMovePrevious: React.useRef(null), keyDown: React.useRef(keyDown), useZoom: React.useRef(false) }; const minZoom = clamp(minZoom_, 0.1, 1); const maxZoom = clamp(maxZoom_, 1, 100); const length = clamp((items?.length || 0) - 1, 0); const media = items?.[value]; refs.version.current = version; refs.media.current = !!media; refs.keyDown.current = keyDown; refs.useZoom.current = version === 'modal' || keyDown; refs.incrementZoom.current = incrementZoom; refs.minZoom.current = minZoom; refs.maxZoom.current = maxZoom; const init = () => { setTimeout(() => { setMoveValue({ left: refs.more.current?.scrollLeft, top: refs.more.current?.scrollTop }); setLoaded(true); }, 14); }; const cleanUp = () => { refs.mouseDown.current = false; }; React.useEffect(() => { const onKeyDown = event => { setKeyDown(event.metaKey || event.ctrlKey); }; const onKeyUp = event_0 => { setKeyDown(null); }; window.document.addEventListener('keydown', onKeyDown); window.document.addEventListener('keyup', onKeyUp); return () => { window.document.removeEventListener('keydown', onKeyDown); window.document.removeEventListener('keyup', onKeyUp); }; }, []); React.useEffect(() => { // init if (open) init();else cleanUp(); }, [open]); React.useEffect(() => { if (open_ !== undefined && open !== open_) setOpen(open_); }, [open_]); React.useEffect(() => { if (value_ !== undefined && value !== value_) setValue(clamp(value_, 0, length)); }, [value_]); const onResetZoom = () => { refs.zoom.current = null; if (refs.imageWrapper.current) { refs.imageWrapper.current.style.transition = 'none'; refs.imageWrapper.current.style.removeProperty('transform'); refs.imageWrapper.current.style.removeProperty('left'); refs.imageWrapper.current.style.removeProperty('top'); setTimeout(() => { refs.imageWrapper.current.style.removeProperty('transition'); }, 14); } }; const onValue = index => { onResetZoom(); setValue(index); }; const onClose = () => { setOpen(false); setTimeout(() => { setLoaded(false); }, 140); if (is('function', onClose_)) onClose_(); }; const move = (forward_ = true) => { const forward = forward_; const rect = refs.more.current.getBoundingClientRect(); const moveValue_ = { left: refs.more.current.scrollLeft + (forward ? 1 : -1) * rect.width, behavior: 'smooth' }; refs.more.current.scrollTo(moveValue_); }; const onMouseDown = event_1 => { refs.mouseDown.current = true; refs.mouseMovePrevious.current = { x: event_1.clientX, y: event_1.clientY }; }; const onMouseUp = () => { refs.mouseDown.current = false; refs.mouseMovePrevious.current = null; }; const onWheel = event_2 => { if (!refs.useZoom.current) return; event_2.preventDefault(); event_2.stopPropagation(); let scale = refs.zoom.current?.scale !== undefined ? refs.zoom.current.scale : 1; const part = refs.incrementZoom.current; const up = event_2.wheelDelta > 0 || event_2.deltaY < 0; scale = clamp(up ? scale + part : scale - part, refs.minZoom.current, refs.maxZoom.current); // Only allow in 100's decimal places, ie. 1.01 scale = Math.round(scale * 100) / 100; const imageBoundingRect = refs.image.current?.getBoundingClientRect(); if (imageBoundingRect.width <= window.innerWidth) { refs.imageWrapper.current.style.removeProperty('left'); } if (imageBoundingRect.height <= window.innerHeight) { refs.imageWrapper.current.style.removeProperty('top'); } if (scale !== refs.zoom.current?.scale) { refs.zoom.current = { scale }; refs.imageWrapper.current.style.transform = `scale(${scale})`; } }; const onMouseMove = event_3 => { if (!refs.useZoom.current) return; if (!(refs.mouseDown.current && refs.image.current)) return; const x = event_3.x - (refs.mouseMovePrevious.current?.x || 0); const y = event_3.y - (refs.mouseMovePrevious.current?.y || 0); refs.mouseMovePrevious.current = { x: event_3.clientX, y: event_3.clientY }; let left = Number(refs.imageWrapper.current?.style?.left?.replace('px', '')) || 0; let top = Number(refs.imageWrapper.current?.style?.top?.replace('px', '')) || 0; const imageBoundingRect_0 = refs.image.current.getBoundingClientRect(); if (imageBoundingRect_0.left <= 0 && imageBoundingRect_0.left + x <= 0 && imageBoundingRect_0.width + imageBoundingRect_0.left + x >= window.innerWidth) { left += x; refs.imageWrapper.current.style.left = `${left}px`; } if (imageBoundingRect_0.top <= 0 && imageBoundingRect_0.top + y <= 0 && imageBoundingRect_0.height + imageBoundingRect_0.top + y >= window.innerHeight) { top += y; refs.imageWrapper.current.style.top = `${top}px`; } }; React.useEffect(() => { if (refs.useZoom.current) { if (imageRef) imageRef.addEventListener('wheel', onWheel, { passive: false }); window.addEventListener('mousemove', onMouseMove); window.addEventListener('mouseup', onMouseUp); window.addEventListener('touchend', onMouseUp); } return () => { if (imageRef) imageRef.removeEventListener('wheel', onWheel); window.removeEventListener('mousemove', onMouseMove); window.removeEventListener('mouseup', onMouseUp); window.removeEventListener('touchend', onMouseUp); }; }, [imageRef, refs.useZoom.current]); const onScroll = event_4 => { if (!refs.useZoom.current) return; if (arrows) { setMoveValue({ left: refs.more.current.scrollLeft, top: refs.more.current.scrollTop }); } }; const iconButtonProps = _objectSpread({ version: 'filled', color: 'default', tonal: true, size: 'small' }, IconButtonProps); const arrowPre = moveValue && /*#__PURE__*/_jsx(IconButton, _objectSpread(_objectSpread({ onClick: () => move(false), disabled: !moveValue || refs.more.current?.scrollLeft === 0 }, iconButtonProps), {}, { className: classNames([staticClassName('ImageGallery', theme) && ['onesy-ImageGallery-arrow', 'onesy-ImageGallery-arrow-start'], iconButtonProps?.className, classes.arrow]), children: /*#__PURE__*/_jsx(IconStart, {}) })); const arrowPost = moveValue && /*#__PURE__*/_jsx(IconButton, _objectSpread(_objectSpread({ onClick: () => move(), disabled: Math.ceil(refs.more.current?.clientWidth + refs.more.current?.scrollLeft) === refs.more.current?.scrollWidth }, iconButtonProps), {}, { className: classNames([staticClassName('ImageGallery', theme) && ['onesy-ImageGallery-arrow', 'onesy-ImageGallery-arrow-end'], iconButtonProps?.className, classes.arrow]), children: /*#__PURE__*/_jsx(IconEnd, {}) })); const more = !!items?.length; const onDragStartImage = event_5 => { event_5.preventDefault(); }; const url = media?.url || media?.urlSmall || (is('string', media) ? media : ''); const main = /*#__PURE__*/_jsxs(_Fragment, { children: [/*#__PURE__*/_jsxs(Line, { justify: "center", align: "center", flex: true, fullWidth: true, className: classNames([staticClassName('ImageGallery', theme) && ['onesy-ImageGallery-main'], classes.main, classes[`main_version_${version}`], classes[`main_version_${version}_size_${size}`], !overflow && classes.noOverflow]), children: [startMain, /*#__PURE__*/_jsxs(Line, _objectSpread(_objectSpread({ ref: refs.imageWrapper, justify: "center", align: "center", fullWidth: true, flex: true }, ImageWrapperProps), {}, { className: classNames([staticClassName('ImageGallery', theme) && ['onesy-ImageGallery-image-wrapper'], ImageWrapperProps?.className, classes.imageWrapper]), style: _objectSpread({}, ImageWrapperProps?.style), children: [startImage, url && /*#__PURE__*/_jsx(Image, _objectSpread(_objectSpread({ ref: item => { refs.image.current = item; setImageRef(item); }, src: url, alt: media?.name, onMouseDown: onMouseDown, onMouseUp: onMouseUp, onTouchStart: onMouseDown, onTouchEnd: onMouseUp, onDragStart: onDragStartImage, lazyLoad: version === 'regular' }, ImageProps), {}, { className: classNames([staticClassName('ImageGallery', theme) && ['onesy-ImageGallery-image'], ImageProps?.className, classes.image, classes.pointerEventsAuto]) })), endImage] })), endMain] }), more && /*#__PURE__*/_jsx(Line, { align: "center", fullWidth: true, className: classNames([staticClassName('ImageGallery', theme) && ['onesy-ImageGallery-footer'], classes.footer]), children: /*#__PURE__*/_jsxs(Line, { gap: 1.5, direction: "row", justify: "center", align: "center", className: classNames([classes.itemsWrapper]), children: [startThumbnails, arrows && arrowPre, /*#__PURE__*/_jsx(Line, { ref: refs.more, gap: 1, direction: "row", align: "center", justify: "flex-start", fullWidth: true, onScroll: onScroll, className: classNames([classes.items]), children: items.map((item_0, index_0) => /*#__PURE__*/_jsx(Line, { onClick: () => onValue(index_0), className: classNames([staticClassName('ImageGallery', theme) && ['onesy-ImageGallery-item', value === index_0 && 'onesy-ImageGallery-item-selected'], classes.item, value === index_0 && classes.itemSelected]), style: { backgroundImage: `url('${item_0?.urlSmall || item_0?.url || item_0}')` }, children: /*#__PURE__*/_jsx(Interaction, {}) })) }), arrows && arrowPost, endThumbnails] }) })] }); if (version === 'regular') { return /*#__PURE__*/_jsx(Line, _objectSpread(_objectSpread({ ref: item_1 => { if (ref) { if (is('function', ref)) ref(item_1);else ref.current = item_1; } refs.root.current = item_1; }, fullWidth: true, className: classNames([staticClassName('ImageGallery', theme) && ['onesy-ImageGallery-root', `onesy-ImageGallery-version-${version}`, `onesy-ImageGallery-size-${size}`], className, classes.root, classes[`version_${version}`]]) }, other), {}, { children: main })); } return /*#__PURE__*/_jsx(Backdrop, _objectSpread(_objectSpread({ ref: item_2 => { if (ref) { if (is('function', ref)) ref(item_2);else ref.current = item_2; } refs.root.current = item_2; }, open: open, onClose: onClose, className: classNames([staticClassName('ImageGallery', theme) && ['onesy-ImageGallery-root', `onesy-ImageGallery-version-${version}`], className, classes.root, classes[`version_${version}`]]) }, other), {}, { children: /*#__PURE__*/_jsxs(Line, { gap: 0, fullWidth: true, className: classes.wrapper, children: [/*#__PURE__*/_jsx(Line, { direction: "row", justify: "flex-end", fullWidth: true, className: classNames([staticClassName('ImageGallery', theme) && ['onesy-ImageGallery-header'], classes.header]), children: /*#__PURE__*/_jsx(IconButton, { version: "filled", color: theme.palette.light ? 'default' : 'inverted', onClick: onClose, className: classNames([classes.pointerEventsAuto]), children: /*#__PURE__*/_jsx(IconClose, {}) }) }), main] }) })); }; ImageGallery.displayName = 'onesy-ImageGallery'; export default ImageGallery;