UNPKG

preact-arco-design

Version:

Arco Design React UI Library.

536 lines (468 loc) 15.9 kB
var __assign = this && this.__assign || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } } return t; }; return __assign.apply(this, arguments); }; var __read = this && this.__read || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) { ar.push(r.value); } } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; import React, { forwardRef, useContext, useRef, useState, useImperativeHandle, useEffect, useCallback, useMemo } from "preact/compat"; import { CSSTransition } from "preact-transition-group-4"; import { findDOMNode } from "preact/compat"; import useMergeValue from "../_util/hooks/useMergeValue"; import cs from "../_util/classNames"; import { on, off, isServerRendering } from "../_util/dom"; import ResizeObserver from "../_util/resizeObserver"; import IconLoading from "../../icon/react-icon/IconLoading"; import IconZoomOut from "../../icon/react-icon/IconZoomOut"; import IconZoomIn from "../../icon/react-icon/IconZoomIn"; import IconFullscreen from "../../icon/react-icon/IconFullscreen"; import IconClose from "../../icon/react-icon/IconClose"; import IconRotateLeft from "../../icon/react-icon/IconRotateLeft"; import IconRotateRight from "../../icon/react-icon/IconRotateRight"; import IconOriginalSize from "../../icon/react-icon/IconOriginalSize"; import ConfigProvider, { ConfigContext } from "../ConfigProvider"; import useImageStatus from "./utils/hooks/useImageStatus"; import PreviewScales, { defaultScales } from "./utils/getScale"; import getFixTranslate from "./utils/getFixTranslate"; import ImagePreviewToolbar from "./image-preview-toolbar"; import Portal from "../Portal"; import { PreviewGroupContext } from "./previewGroupContext"; import ImagePreviewArrow from "./image-preview-arrow"; import useOverflowHidden from "../_util/hooks/useOverflowHidden"; import { Esc } from "../_util/keycode"; import useUpdate from "../_util/hooks/useUpdate"; import { isUndefined } from "../_util/is"; var ROTATE_STEP = 90; function Preview(props, ref) { var _a, _b; var className = props.className, style = props.style, src = props.src, defaultVisible = props.defaultVisible, _c = props.maskClosable, maskClosable = _c === void 0 ? true : _c, _d = props.closable, closable = _d === void 0 ? true : _d, _e = props.breakPoint, breakPoint = _e === void 0 ? 316 : _e, actions = props.actions, _f = props.actionsLayout, actionsLayout = _f === void 0 ? ['fullScreen', 'rotateRight', 'rotateLeft', 'zoomIn', 'zoomOut', 'originalSize', 'extra'] : _f, _g = props.getPopupContainer, getPopupContainer = _g === void 0 ? function () { return document.body; } : _g, onVisibleChange = props.onVisibleChange, _h = props.scales, scales = _h === void 0 ? defaultScales : _h, _j = props.escToExit, escToExit = _j === void 0 ? true : _j; var _k = useContext(PreviewGroupContext), previewGroup = _k.previewGroup, previewUrlMap = _k.previewUrlMap, currentIndex = _k.currentIndex, setCurrentIndex = _k.setCurrentIndex, infinite = _k.infinite; var mergedSrc = previewGroup ? previewUrlMap.get(currentIndex) : src; var _l = __read(useState(mergedSrc), 2), previewImgSrc = _l[0], setPreviewImgSrc = _l[1]; var _m = __read(useMergeValue(false, { defaultValue: defaultVisible, value: props.visible }), 2), visible = _m[0], setVisible = _m[1]; var globalContext = useContext(ConfigContext); var getPrefixCls = globalContext.getPrefixCls, locale = globalContext.locale, rtl = globalContext.rtl; var prefixCls = getPrefixCls('image'); var previewPrefixCls = "".concat(prefixCls, "-preview"); var classNames = cs(previewPrefixCls, (_a = {}, _a["".concat(previewPrefixCls, "-hide")] = !visible, _a["".concat(previewPrefixCls, "-rtl")] = rtl, _a), className); var refImage = useRef(); var refImageContainer = useRef(); var refWrapper = useRef(); var keyboardEventOn = useRef(false); var refMoveData = useRef({ pageX: 0, pageY: 0, originX: 0, originY: 0 }); var _o = useImageStatus('loading'), isLoading = _o.isLoading, isLoaded = _o.isLoaded, setStatus = _o.setStatus; var _p = __read(useState(false), 2), toolbarSimple = _p[0], setToolbarSimple = _p[1]; var _q = __read(useState({ x: 0, y: 0 }), 2), translate = _q[0], setTranslate = _q[1]; var _r = __read(useState(1), 2), scale = _r[0], setScale = _r[1]; var _s = __read(useState(false), 2), scaleValueVisible = _s[0], setScaleValueVisible = _s[1]; var _t = __read(useState(0), 2), rotate = _t[0], setRotate = _t[1]; var _u = __read(useState(false), 2), moving = _u[0], setMoving = _u[1]; var previewScales = useMemo(function () { return new PreviewScales(scales); }, []); // Reset image params function reset() { setTranslate({ x: 0, y: 0 }); setScale(1); setRotate(0); } useImperativeHandle(ref, function () { return { reset: reset }; }); var _v = __read(useState(), 2), container = _v[0], setContainer = _v[1]; var getContainer = useCallback(function () { return container; }, [container]); useEffect(function () { var container = getPopupContainer && getPopupContainer(); var containerDom = findDOMNode(container) || document.body; setContainer(containerDom); }, [getPopupContainer]); useOverflowHidden(getContainer, { hidden: visible }); var isFixed = useMemo(function () { return !isServerRendering && container === document.body; }, [container]); // Jump to image at the specified index function jumpTo(index) { var previewListLen = previewUrlMap.size; if (infinite) { index %= previewListLen; if (index < 0) index = previewListLen - Math.abs(index); } if (index !== currentIndex && index >= 0 && index <= previewListLen - 1) { setCurrentIndex(index); } } function onPrev() { jumpTo(currentIndex - 1); } function onNext() { jumpTo(currentIndex + 1); } // Anticlockwise rotation function onRotateLeft() { setRotate(rotate === 0 ? 360 - ROTATE_STEP : rotate - ROTATE_STEP); } // Clockwise rotation function onRotateRight() { setRotate((rotate + ROTATE_STEP) % 360); } // Scale var hideScaleTimer = useRef(null); var showScaleValue = function showScaleValue() { !scaleValueVisible && setScaleValueVisible(true); hideScaleTimer.current && clearTimeout(hideScaleTimer.current); hideScaleTimer.current = setTimeout(function () { setScaleValueVisible(false); }, 1000); }; var onScaleChange = function onScaleChange(newScale) { if (scale !== newScale) { setScale(newScale); showScaleValue(); } }; function onZoomIn() { var newScale = previewScales.getNextScale(scale, 'zoomIn'); onScaleChange(newScale); } function onZoomOut() { var newScale = previewScales.getNextScale(scale, 'zoomOut'); onScaleChange(newScale); } function onResetScale() { onScaleChange(1); } function onFullScreen() { var wrapperRect = refWrapper.current.getBoundingClientRect(); var imgRect = refImage.current.getBoundingClientRect(); var newHeightScale = wrapperRect.height / (imgRect.height / scale); var newWidthScale = wrapperRect.width / (imgRect.width / scale); var newScale = Math.max(newHeightScale, newWidthScale); onScaleChange(newScale); } // Image container is clicked function onOutsideImgClick(e) { if (e.target === e.currentTarget && maskClosable) { close(); } } // Close button is clicked. function onCloseClick() { close(); } function close() { if (visible) { onVisibleChange && onVisibleChange(false, visible); isUndefined(props.visible) && setVisible(false); } } function onWrapperResize(entry) { if (entry && entry.length) { var wrapperRect = entry[0].contentRect; var nextSimple = wrapperRect.width < breakPoint; setToolbarSimple(nextSimple); } } // Check the translate and correct it if needed var checkAndFixTranslate = function checkAndFixTranslate() { if (!refWrapper.current || !refImage.current) return; var wrapperRect = refWrapper.current.getBoundingClientRect(); var imgRect = refImage.current.getBoundingClientRect(); var _a = __read(getFixTranslate(wrapperRect, imgRect, translate.x, translate.y, scale), 2), x = _a[0], y = _a[1]; if (x !== translate.x || y !== translate.y) { setTranslate({ x: x, y: y }); } }; // Update position on moving if needed var onMoving = function onMoving(e) { if (visible && moving) { e.preventDefault && e.preventDefault(); var _a = refMoveData.current, originX = _a.originX, originY = _a.originY, pageX = _a.pageX, pageY = _a.pageY; var nextX = originX + (e.pageX - pageX) / scale; var nextY = originY + (e.pageY - pageY) / scale; setTranslate({ x: nextX, y: nextY }); } }; var onMoveEnd = function onMoveEnd(e) { e.preventDefault && e.preventDefault(); setMoving(false); }; // Record position data on move start var onMoveStart = function onMoveStart(e) { e.preventDefault && e.preventDefault(); setMoving(true); var ev = e.type === 'touchstart' ? e.touches[0] : e; refMoveData.current.pageX = ev.pageX; refMoveData.current.pageY = ev.pageY; refMoveData.current.originX = translate.x; refMoveData.current.originY = translate.y; }; useEffect(function () { if (visible && moving) { on(document, 'mousemove', onMoving, false); on(document, 'mouseup', onMoveEnd, false); } return function () { off(document, 'mousemove', onMoving, false); off(document, 'mouseup', onMoveEnd, false); }; }, [visible, moving]); // Correct translate after moved useEffect(function () { if (!moving) { checkAndFixTranslate(); } }, [moving, translate]); // Correct translate when scale changes useEffect(function () { checkAndFixTranslate(); }, [scale]); // Reset when preview is opened useEffect(function () { if (visible) { reset(); } }, [visible]); // Reset on first mount or image switches useEffect(function () { setPreviewImgSrc(mergedSrc); setStatus(mergedSrc ? 'loading' : 'loaded'); reset(); }, [mergedSrc]); useUpdate(function () { previewScales.updateScale(scales); setScale(1); }, [scales]); // Close when pressing esc useEffect(function () { var onKeyDown = function onKeyDown(e) { if (escToExit && e && e.key === Esc.key) { close(); } }; if (visible && !moving && !keyboardEventOn.current) { keyboardEventOn.current = true; on(document, 'keydown', onKeyDown); } return function () { keyboardEventOn.current = false; off(document, 'keydown', onKeyDown); }; }, [visible, escToExit, moving]); var defaultActions = [{ key: 'fullScreen', name: locale.ImagePreview.fullScreen, content: React.createElement(IconFullscreen, null), onClick: onFullScreen }, { key: 'rotateRight', name: locale.ImagePreview.rotateRight, content: React.createElement(IconRotateRight, null), onClick: onRotateRight }, { key: 'rotateLeft', name: locale.ImagePreview.rotateLeft, content: React.createElement(IconRotateLeft, null), onClick: onRotateLeft }, { key: 'zoomIn', name: locale.ImagePreview.zoomIn, content: React.createElement(IconZoomIn, null), onClick: onZoomIn, disabled: scale === previewScales.maxScale }, { key: 'zoomOut', name: locale.ImagePreview.zoomOut, content: React.createElement(IconZoomOut, null), onClick: onZoomOut, disabled: scale === previewScales.minScale }, { key: 'originalSize', name: locale.ImagePreview.originalSize, content: React.createElement(IconOriginalSize, null), onClick: onResetScale }]; return React.createElement(Portal, { visible: visible, forceRender: false, getContainer: getContainer }, React.createElement(ConfigProvider, __assign({}, globalContext, { getPopupContainer: function getPopupContainer() { return refWrapper.current; } }), React.createElement("div", { className: classNames, style: __assign(__assign({}, style || {}), isFixed ? {} : { zIndex: 'inherit', position: 'absolute' }) }, React.createElement(CSSTransition, { in: visible, timeout: 400, appear: true, classNames: "fadeImage", mountOnEnter: true, unmountOnExit: false, onEnter: function onEnter(e) { e.parentNode.style.display = 'block'; e.style.display = 'block'; }, onExited: function onExited(e) { e.parentNode.style.display = ''; e.style.display = 'none'; } }, React.createElement("div", { className: "".concat(previewPrefixCls, "-mask") })), visible && React.createElement(ResizeObserver, { onResize: onWrapperResize }, React.createElement("div", { ref: refWrapper, className: "".concat(previewPrefixCls, "-wrapper"), onClick: onOutsideImgClick }, React.createElement("div", { ref: refImageContainer, className: "".concat(previewPrefixCls, "-img-container"), style: { transform: "scale(".concat(scale, ", ").concat(scale, ")") }, onClick: onOutsideImgClick }, React.createElement("img", { ref: refImage, className: cs("".concat(previewPrefixCls, "-img"), (_b = {}, _b["".concat(previewPrefixCls, "-img-moving")] = moving, _b)), style: { transform: "translate(".concat(translate.x, "px, ").concat(translate.y, "px) rotate(").concat(rotate, "deg)") }, onLoad: function onLoad() { setStatus('loaded'); }, onError: function onError() { setStatus('error'); }, onMouseDown: onMoveStart, key: previewImgSrc, src: previewImgSrc }), isLoading && React.createElement("div", { className: "".concat(previewPrefixCls, "-loading") }, React.createElement(IconLoading, null))), React.createElement(CSSTransition, { in: scaleValueVisible, timeout: 400, appear: true, classNames: "fadeImage", unmountOnExit: true }, React.createElement("div", { className: "".concat(previewPrefixCls, "-scale-value") }, (scale * 100).toFixed(0), "%")), isLoaded && React.createElement(ImagePreviewToolbar, { prefixCls: prefixCls, previewPrefixCls: previewPrefixCls, actions: actions, actionsLayout: actionsLayout, defaultActions: defaultActions, simple: toolbarSimple }), closable && React.createElement("div", { className: "".concat(previewPrefixCls, "-close-btn"), onClick: onCloseClick }, React.createElement(IconClose, null)), previewGroup && React.createElement(ImagePreviewArrow, { previewCount: previewUrlMap.size, current: currentIndex, infinite: infinite, onPrev: onPrev, onNext: onNext })))))); } var PreviewComponent = forwardRef(Preview); PreviewComponent.displayName = 'ImagePreview'; export default PreviewComponent;