@onesy/ui-react
Version:
UI for React
510 lines (509 loc) • 18.9 kB
JavaScript
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;