This commit is contained in:
2026-05-25 17:08:18 +08:00
parent df501f6151
commit f1259b71b5
9178 changed files with 1626125 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
import * as React from 'react';
import type { InternalPreviewConfig, PreviewSemanticName, ToolbarRenderInfoType } from './Preview';
import PreviewGroup from './PreviewGroup';
import type { TransformType } from './hooks/useImageTransform';
export interface ImgInfo {
url: string;
alt: string;
width: string | number;
height: string | number;
}
export interface CoverConfig {
coverNode?: React.ReactNode;
placement?: 'top' | 'bottom' | 'center';
}
export interface PreviewConfig extends Omit<InternalPreviewConfig, 'countRender'> {
cover?: React.ReactNode | CoverConfig;
imageRender?: (originalNode: React.ReactElement, info: {
transform: TransformType;
image: ImgInfo;
}) => React.ReactNode;
actionsRender?: (originalNode: React.ReactElement, info: Omit<ToolbarRenderInfoType, 'current' | 'total'>) => React.ReactNode;
onOpenChange?: (open: boolean) => void;
}
export type SemanticName = 'root' | 'image' | 'cover';
export interface ImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, 'placeholder' | 'onClick' | 'onKeyDown'> {
prefixCls?: string;
previewPrefixCls?: string;
rootClassName?: string;
classNames?: Partial<Record<SemanticName, string> & {
popup?: Partial<Record<PreviewSemanticName, string>>;
}>;
styles?: Partial<Record<SemanticName, React.CSSProperties> & {
popup?: Partial<Record<PreviewSemanticName, React.CSSProperties>>;
}>;
src?: string;
placeholder?: React.ReactNode;
fallback?: string;
preview?: boolean | PreviewConfig;
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
onError?: (e: React.SyntheticEvent<HTMLImageElement, Event>) => void;
onKeyDown?: (e: React.KeyboardEvent<HTMLDivElement>) => void;
}
interface CompoundedComponent<P> extends React.FC<P> {
PreviewGroup: typeof PreviewGroup;
}
declare const ImageInternal: CompoundedComponent<ImageProps>;
export default ImageInternal;
+200
View File
@@ -0,0 +1,200 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _useControlledState = _interopRequireDefault(require("@rc-component/util/lib/hooks/useControlledState"));
var _clsx = require("clsx");
var _react = _interopRequireWildcard(require("react"));
var React = _react;
var _Preview = _interopRequireDefault(require("./Preview"));
var _PreviewGroup = _interopRequireDefault(require("./PreviewGroup"));
var _common = require("./common");
var _context = require("./context");
var _useRegisterImage = _interopRequireDefault(require("./hooks/useRegisterImage"));
var _useStatus = _interopRequireDefault(require("./hooks/useStatus"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
const ImageInternal = props => {
const {
// Misc
prefixCls = 'rc-image',
previewPrefixCls = `${prefixCls}-preview`,
// Style
rootClassName,
className,
style,
classNames = {},
styles = {},
width,
height,
// Image
src: imgSrc,
alt,
placeholder,
fallback,
// Preview
preview = true,
// Events
onClick,
onError,
onKeyDown,
...otherProps
} = props;
const groupContext = (0, _react.useContext)(_context.PreviewGroupContext);
// ========================== Preview ===========================
const canPreview = !!preview;
const {
src: previewSrc,
open: previewOpen,
onOpenChange: onPreviewOpenChange,
cover,
rootClassName: previewRootClassName,
...restProps
} = preview && typeof preview === 'object' ? preview : {};
const coverPlacement = typeof cover === 'object' && cover.placement ? cover.placement || 'center' : 'center';
const coverNode = typeof cover === 'object' && cover.coverNode ? cover.coverNode : cover;
// ============================ Open ============================
const [isShowPreview, setShowPreview] = (0, _useControlledState.default)(!!previewOpen, previewOpen);
const [mousePosition, setMousePosition] = (0, _react.useState)(null);
const triggerPreviewOpen = nextOpen => {
setShowPreview(nextOpen);
onPreviewOpenChange?.(nextOpen);
};
const onPreviewClose = () => {
triggerPreviewOpen(false);
};
// ========================= ImageProps =========================
const isCustomPlaceholder = placeholder && placeholder !== true;
const src = previewSrc ?? imgSrc;
const [getImgRef, srcAndOnload, status] = (0, _useStatus.default)({
src: imgSrc,
isCustomPlaceholder,
fallback
});
const imgCommonProps = (0, _react.useMemo)(() => {
const obj = {};
_common.COMMON_PROPS.forEach(prop => {
if (props[prop] !== undefined) {
obj[prop] = props[prop];
}
});
return obj;
}, _common.COMMON_PROPS.map(prop => props[prop]));
// ========================== Register ==========================
const registerData = (0, _react.useMemo)(() => ({
...imgCommonProps,
src
}), [src, imgCommonProps]);
const imageId = (0, _useRegisterImage.default)(canPreview, registerData);
// ========================== Preview ===========================
const onPreview = e => {
const rect = e.target.getBoundingClientRect();
const left = rect.x + rect.width / 2;
const top = rect.y + rect.height / 2;
if (groupContext) {
groupContext.onPreview(imageId, src, left, top);
} else {
setMousePosition({
x: left,
y: top
});
triggerPreviewOpen(true);
}
onClick?.(e);
};
// ======================= Keyboard Preview =====================
const onPreviewKeyDown = event => {
onKeyDown?.(event);
if (!canPreview) {
return;
}
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
const rect = event.target.getBoundingClientRect();
const left = rect.x + rect.width / 2;
const top = rect.y + rect.height / 2;
if (groupContext) {
groupContext.onPreview(imageId, src, left, top);
} else {
setMousePosition({
x: left,
y: top
});
triggerPreviewOpen(true);
}
}
};
// =========================== Render ===========================
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", _extends({}, otherProps, {
className: (0, _clsx.clsx)(prefixCls, rootClassName, classNames.root, {
[`${prefixCls}-error`]: status === 'error'
}),
onClick: canPreview ? onPreview : onClick,
role: canPreview ? 'button' : otherProps.role,
tabIndex: canPreview && otherProps.tabIndex == null ? 0 : otherProps.tabIndex,
"aria-label": canPreview ? otherProps['aria-label'] ?? alt : otherProps['aria-label'],
onKeyDown: onPreviewKeyDown,
style: {
width,
height,
...styles.root
}
}), /*#__PURE__*/React.createElement("img", _extends({}, imgCommonProps, {
className: (0, _clsx.clsx)(`${prefixCls}-img`, {
[`${prefixCls}-img-placeholder`]: placeholder === true
}, classNames.image, className),
style: {
height,
...styles.image,
...style
},
ref: getImgRef
}, srcAndOnload, {
width: width,
height: height,
onError: onError
})), status === 'loading' && /*#__PURE__*/React.createElement("div", {
"aria-hidden": "true",
className: `${prefixCls}-placeholder`
}, placeholder), cover !== false && canPreview && /*#__PURE__*/React.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-cover`, classNames.cover, `${prefixCls}-cover-${coverPlacement}`),
style: {
display: style?.display === 'none' ? 'none' : undefined,
...styles.cover
}
}, coverNode)), !groupContext && canPreview && /*#__PURE__*/React.createElement(_Preview.default, _extends({
"aria-hidden": !isShowPreview,
open: isShowPreview,
prefixCls: previewPrefixCls,
onClose: onPreviewClose,
mousePosition: mousePosition,
src: src,
alt: alt,
imageInfo: {
width,
height
},
fallback: fallback,
imgCommonProps: imgCommonProps
}, restProps, {
classNames: classNames?.popup,
styles: styles?.popup,
rootClassName: (0, _clsx.clsx)(previewRootClassName, rootClassName)
})));
};
ImageInternal.PreviewGroup = _PreviewGroup.default;
if (process.env.NODE_ENV !== 'production') {
ImageInternal.displayName = 'Image';
}
var _default = exports.default = ImageInternal;
@@ -0,0 +1,9 @@
import * as React from 'react';
export interface CloseBtnProps {
prefixCls: string;
icon?: React.ReactNode;
onClick: React.MouseEventHandler<HTMLButtonElement>;
className?: string;
style?: React.CSSProperties;
}
export default function CloseBtn(props: CloseBtnProps): React.JSX.Element;
@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = CloseBtn;
var _clsx = require("clsx");
var React = _interopRequireWildcard(require("react"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function CloseBtn(props) {
const {
prefixCls,
icon,
onClick,
className,
style
} = props;
return /*#__PURE__*/React.createElement("button", {
className: (0, _clsx.clsx)(`${prefixCls}-close`, className),
style: style,
onClick: onClick
}, icon);
}
@@ -0,0 +1,23 @@
import * as React from 'react';
import type { Actions, PreviewProps } from '.';
import type { ImgInfo } from '../Image';
import type { TransformType } from '../hooks/useImageTransform';
export type FooterSemanticName = 'footer' | 'actions';
export interface FooterProps extends Actions {
prefixCls: string;
showProgress: boolean;
countRender?: PreviewProps['countRender'];
actionsRender?: PreviewProps['actionsRender'];
current: number;
count: number;
showSwitch: boolean;
icons: PreviewProps['icons'];
scale: number;
minScale: number;
maxScale: number;
image: ImgInfo;
transform: TransformType;
classNames: Partial<Record<FooterSemanticName, string>>;
styles: Partial<Record<FooterSemanticName, React.CSSProperties>>;
}
export default function Footer(props: FooterProps): React.JSX.Element;
+161
View File
@@ -0,0 +1,161 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = Footer;
var _clsx = require("clsx");
var React = _interopRequireWildcard(require("react"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function Footer(props) {
// 修改解构,添加缺失的属性,并提供默认值
const {
prefixCls,
showProgress,
current,
count,
showSwitch,
// Style
classNames,
styles,
// render
icons,
image,
transform,
countRender,
actionsRender,
// Scale
scale,
minScale,
maxScale,
// Actions
onActive,
onFlipY,
onFlipX,
onRotateLeft,
onRotateRight,
onZoomOut,
onZoomIn,
onClose,
onReset
} = props;
const {
left,
right,
prev,
next,
flipY,
flipX,
rotateLeft,
rotateRight,
zoomOut,
zoomIn
} = icons;
// ========================== Render ==========================
// >>>>> Progress
const progressNode = showProgress && /*#__PURE__*/React.createElement("div", {
className: `${prefixCls}-progress`
}, countRender ? countRender(current + 1, count) : /*#__PURE__*/React.createElement("bdi", null, `${current + 1} / ${count}`));
// >>>>> Actions
const actionCls = `${prefixCls}-actions-action`;
const renderOperation = ({
type,
disabled,
onClick,
icon
}) => {
return /*#__PURE__*/React.createElement("button", {
type: "button",
key: type,
className: (0, _clsx.clsx)(actionCls, `${actionCls}-${type}`, {
[`${actionCls}-disabled`]: !!disabled
}),
onClick: onClick,
disabled: !!disabled,
"aria-label": type
}, icon);
};
const switchPrevNode = showSwitch ? renderOperation({
icon: prev ?? left,
onClick: () => onActive(-1),
type: 'prev',
disabled: current === 0
}) : undefined;
const switchNextNode = showSwitch ? renderOperation({
icon: next ?? right,
onClick: () => onActive(1),
type: 'next',
disabled: current === count - 1
}) : undefined;
const flipYNode = renderOperation({
icon: flipY,
onClick: onFlipY,
type: 'flipY'
});
const flipXNode = renderOperation({
icon: flipX,
onClick: onFlipX,
type: 'flipX'
});
const rotateLeftNode = renderOperation({
icon: rotateLeft,
onClick: onRotateLeft,
type: 'rotateLeft'
});
const rotateRightNode = renderOperation({
icon: rotateRight,
onClick: onRotateRight,
type: 'rotateRight'
});
const zoomOutNode = renderOperation({
icon: zoomOut,
onClick: onZoomOut,
type: 'zoomOut',
disabled: scale <= minScale
});
const zoomInNode = renderOperation({
icon: zoomIn,
onClick: onZoomIn,
type: 'zoomIn',
disabled: scale === maxScale
});
const actionsNode = /*#__PURE__*/React.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-actions`, classNames.actions),
style: styles.actions
}, flipYNode, flipXNode, rotateLeftNode, rotateRightNode, zoomOutNode, zoomInNode);
// >>>>> Render
return /*#__PURE__*/React.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-footer`, classNames.footer),
style: styles.footer
}, progressNode, actionsRender ? actionsRender(actionsNode, {
icons: {
prevIcon: switchPrevNode,
nextIcon: switchNextNode,
flipYIcon: flipYNode,
flipXIcon: flipXNode,
rotateLeftIcon: rotateLeftNode,
rotateRightIcon: rotateRightNode,
zoomOutIcon: zoomOutNode,
zoomInIcon: zoomInNode
},
actions: {
onActive,
onFlipY,
onFlipX,
onRotateLeft,
onRotateRight,
onZoomOut,
onZoomIn,
onReset,
onClose
},
transform,
current,
total: count,
image
}) : actionsNode);
}
@@ -0,0 +1,10 @@
import * as React from 'react';
import type { OperationIcons } from '.';
export interface PrevNextProps {
prefixCls: string;
onActive: (offset: number) => void;
current: number;
count: number;
icons: OperationIcons;
}
export default function PrevNext(props: PrevNextProps): React.JSX.Element;
@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = PrevNext;
var _clsx = require("clsx");
var React = _interopRequireWildcard(require("react"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function PrevNext(props) {
const {
prefixCls,
onActive,
current,
count,
icons: {
left,
right,
prev,
next
}
} = props;
const switchCls = `${prefixCls}-switch`;
const prevDisabled = current === 0;
const nextDisabled = current === count - 1;
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("button", {
className: (0, _clsx.clsx)(switchCls, `${switchCls}-prev`, {
[`${switchCls}-disabled`]: prevDisabled
}),
onClick: () => onActive(-1),
disabled: prevDisabled
}, prev ?? left), /*#__PURE__*/React.createElement("button", {
type: "button",
className: (0, _clsx.clsx)(switchCls, `${switchCls}-next`, {
[`${switchCls}-disabled`]: nextDisabled
}),
onClick: () => onActive(1),
disabled: nextDisabled
}, next ?? right));
}
+103
View File
@@ -0,0 +1,103 @@
import { type PortalProps } from '@rc-component/portal';
import React from 'react';
import type { TransformAction, TransformType } from '../hooks/useImageTransform';
import type { ImgInfo } from '../Image';
import { type FooterSemanticName } from './Footer';
export type PreviewSemanticName = 'root' | 'mask' | 'body' | 'close' | FooterSemanticName;
export interface OperationIcons {
rotateLeft?: React.ReactNode;
rotateRight?: React.ReactNode;
zoomIn?: React.ReactNode;
zoomOut?: React.ReactNode;
close?: React.ReactNode;
prev?: React.ReactNode;
next?: React.ReactNode;
/** @deprecated Please use `prev` instead */
left?: React.ReactNode;
/** @deprecated Please use `next` instead */
right?: React.ReactNode;
flipX?: React.ReactNode;
flipY?: React.ReactNode;
}
export interface Actions {
onActive: (offset: number) => void;
onFlipY: () => void;
onFlipX: () => void;
onRotateLeft: () => void;
onRotateRight: () => void;
onZoomOut: () => void;
onZoomIn: () => void;
onClose: () => void;
onReset: () => void;
}
export type ToolbarRenderInfoType = {
icons: {
prevIcon?: React.ReactNode;
nextIcon?: React.ReactNode;
flipYIcon: React.ReactNode;
flipXIcon: React.ReactNode;
rotateLeftIcon: React.ReactNode;
rotateRightIcon: React.ReactNode;
zoomOutIcon: React.ReactNode;
zoomInIcon: React.ReactNode;
};
actions: Actions;
transform: TransformType;
current: number;
total: number;
image: ImgInfo;
};
export interface InternalPreviewConfig {
/** Better to use `classNames.root` instead */
rootClassName?: string;
src?: string;
alt?: string;
scaleStep?: number;
minScale?: number;
maxScale?: number;
motionName?: string;
open?: boolean;
getContainer?: PortalProps['getContainer'];
zIndex?: number;
maskClosable?: boolean;
afterOpenChange?: (open: boolean) => void;
/** Whether to trap focus within the preview when open. Default is true. */
focusTrap?: boolean;
movable?: boolean;
icons?: OperationIcons;
closeIcon?: React.ReactNode;
onTransform?: (info: {
transform: TransformType;
action: TransformAction;
}) => void;
countRender?: (current: number, total: number) => React.ReactNode;
imageRender?: (originalNode: React.ReactElement, info: {
transform: TransformType;
current?: number;
image: ImgInfo;
}) => React.ReactNode;
actionsRender?: (originalNode: React.ReactElement, info: ToolbarRenderInfoType) => React.ReactNode;
}
export interface PreviewProps extends InternalPreviewConfig {
prefixCls: string;
classNames?: Partial<Record<PreviewSemanticName, string>>;
styles?: Partial<Record<PreviewSemanticName, React.CSSProperties>>;
imageInfo?: {
width: number | string;
height: number | string;
};
fallback?: string;
imgCommonProps?: React.ImgHTMLAttributes<HTMLImageElement>;
width?: string | number;
height?: string | number;
current?: number;
count?: number;
onChange?: (current: number, prev: number) => void;
onClose?: () => void;
mousePosition: null | {
x: number;
y: number;
};
}
declare const Preview: React.FC<PreviewProps>;
export default Preview;
+371
View File
@@ -0,0 +1,371 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _motion = _interopRequireDefault(require("@rc-component/motion"));
var _portal = _interopRequireDefault(require("@rc-component/portal"));
var _util = require("@rc-component/util");
var _focus = require("@rc-component/util/lib/Dom/focus");
var _useLayoutEffect = _interopRequireDefault(require("@rc-component/util/lib/hooks/useLayoutEffect"));
var _KeyCode = _interopRequireDefault(require("@rc-component/util/lib/KeyCode"));
var _clsx = require("clsx");
var _react = _interopRequireWildcard(require("react"));
var _context = require("../context");
var _useImageTransform = _interopRequireDefault(require("../hooks/useImageTransform"));
var _useMouseEvent = _interopRequireDefault(require("../hooks/useMouseEvent"));
var _useStatus = _interopRequireDefault(require("../hooks/useStatus"));
var _useTouchEvent = _interopRequireDefault(require("../hooks/useTouchEvent"));
var _previewConfig = require("../previewConfig");
var _CloseBtn = _interopRequireDefault(require("./CloseBtn"));
var _Footer = _interopRequireDefault(require("./Footer"));
var _PrevNext = _interopRequireDefault(require("./PrevNext"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
// Note: if you want to add `action`,
// pls contact @zombieJ or @thinkasany first.
const PreviewImage = ({
fallback,
src,
imgRef,
...props
}) => {
const [getImgRef, srcAndOnload] = (0, _useStatus.default)({
src,
fallback
});
return /*#__PURE__*/_react.default.createElement("img", _extends({
ref: ref => {
imgRef.current = ref;
getImgRef(ref);
}
}, props, srcAndOnload));
};
const Preview = props => {
const {
prefixCls,
rootClassName,
src,
alt,
imageInfo,
fallback,
movable = true,
onClose,
open,
afterOpenChange,
maskClosable = true,
icons = {},
closeIcon,
getContainer,
current = 0,
count = 1,
countRender,
scaleStep = 0.5,
minScale = 1,
maxScale = 50,
motionName = 'fade',
imageRender,
imgCommonProps,
actionsRender,
onTransform,
onChange,
classNames = {},
styles = {},
mousePosition,
zIndex,
focusTrap = true
} = props;
const imgRef = (0, _react.useRef)();
const wrapperRef = (0, _react.useRef)(null);
const triggerRef = (0, _react.useRef)(null);
const groupContext = (0, _react.useContext)(_context.PreviewGroupContext);
const showLeftOrRightSwitches = groupContext && count > 1;
const showOperationsProgress = groupContext && count >= 1;
// ======================== Transform =========================
const [enableTransition, setEnableTransition] = (0, _react.useState)(true);
const {
transform,
resetTransform,
updateTransform,
dispatchZoomChange
} = (0, _useImageTransform.default)(imgRef, minScale, maxScale, onTransform);
const {
isMoving,
onMouseDown,
onWheel
} = (0, _useMouseEvent.default)(imgRef, movable, open, scaleStep, transform, updateTransform, dispatchZoomChange);
const {
isTouching,
onTouchStart,
onTouchMove,
onTouchEnd
} = (0, _useTouchEvent.default)(imgRef, movable, open, minScale, transform, updateTransform, dispatchZoomChange);
const {
rotate,
scale
} = transform;
(0, _react.useEffect)(() => {
if (!enableTransition) {
setEnableTransition(true);
}
}, [enableTransition]);
(0, _react.useEffect)(() => {
if (!open) {
resetTransform('close');
}
}, [open]);
// ========================== Image ===========================
const onDoubleClick = event => {
if (open) {
if (scale !== 1) {
updateTransform({
x: 0,
y: 0,
scale: 1
}, 'doubleClick');
} else {
dispatchZoomChange(_previewConfig.BASE_SCALE_RATIO + scaleStep, 'doubleClick', event.clientX, event.clientY);
}
}
};
const imgNode = /*#__PURE__*/_react.default.createElement(PreviewImage, _extends({}, imgCommonProps, {
width: props.width,
height: props.height,
imgRef: imgRef,
className: `${prefixCls}-img`,
alt: alt,
style: {
transform: `translate3d(${transform.x}px, ${transform.y}px, 0) scale3d(${transform.flipX ? '-' : ''}${scale}, ${transform.flipY ? '-' : ''}${scale}, 1) rotate(${rotate}deg)`,
transitionDuration: (!enableTransition || isTouching) && '0s'
},
fallback: fallback,
src: src,
onWheel: onWheel,
onMouseDown: onMouseDown,
onDoubleClick: onDoubleClick,
onTouchStart: onTouchStart,
onTouchMove: onTouchMove,
onTouchEnd: onTouchEnd,
onTouchCancel: onTouchEnd
}));
const image = {
url: src,
alt,
...imageInfo
};
// ======================== Operation =========================
// >>>>> Actions
const onZoomIn = () => {
dispatchZoomChange(_previewConfig.BASE_SCALE_RATIO + scaleStep, 'zoomIn');
};
const onZoomOut = () => {
dispatchZoomChange(_previewConfig.BASE_SCALE_RATIO / (_previewConfig.BASE_SCALE_RATIO + scaleStep), 'zoomOut');
};
const onRotateRight = () => {
updateTransform({
rotate: rotate + 90
}, 'rotateRight');
};
const onRotateLeft = () => {
updateTransform({
rotate: rotate - 90
}, 'rotateLeft');
};
const onFlipX = () => {
updateTransform({
flipX: !transform.flipX
}, 'flipX');
};
const onFlipY = () => {
updateTransform({
flipY: !transform.flipY
}, 'flipY');
};
const onReset = () => {
resetTransform('reset');
};
const onActive = offset => {
const nextCurrent = current + offset;
if (nextCurrent >= 0 && nextCurrent <= count - 1) {
setEnableTransition(false);
resetTransform(offset < 0 ? 'prev' : 'next');
onChange?.(nextCurrent, current);
}
};
// >>>>> Effect: Keyboard
const onKeyDown = (0, _util.useEvent)(event => {
if (open) {
const {
keyCode
} = event;
if (showLeftOrRightSwitches) {
if (keyCode === _KeyCode.default.LEFT) {
onActive(-1);
} else if (keyCode === _KeyCode.default.RIGHT) {
onActive(1);
}
}
}
});
(0, _react.useEffect)(() => {
if (open) {
window.addEventListener('keydown', onKeyDown);
return () => {
window.removeEventListener('keydown', onKeyDown);
};
}
}, [open]);
// ======================= Lock Scroll ========================
const [lockScroll, setLockScroll] = (0, _react.useState)(false);
_react.default.useEffect(() => {
if (open) {
setLockScroll(true);
}
}, [open]);
const onVisibleChanged = nextVisible => {
if (!nextVisible) {
setLockScroll(false);
// Restore focus to the trigger element after leave animation
triggerRef.current?.focus?.();
triggerRef.current = null;
}
afterOpenChange?.(nextVisible);
};
// ========================== Portal ==========================
const [portalRender, setPortalRender] = (0, _react.useState)(false);
(0, _useLayoutEffect.default)(() => {
if (open) {
setPortalRender(true);
}
}, [open]);
const onEsc = ({
top
}) => {
if (top) {
onClose?.();
}
};
// =========================== Focus ============================
(0, _useLayoutEffect.default)(() => {
if (open) {
triggerRef.current = document.activeElement;
}
}, [open]);
(0, _focus.useLockFocus)(focusTrap && open && portalRender, () => wrapperRef.current);
// ========================== Render ==========================
const bodyStyle = {
...styles.body
};
if (mousePosition) {
bodyStyle.transformOrigin = `${mousePosition.x}px ${mousePosition.y}px`;
}
return /*#__PURE__*/_react.default.createElement(_portal.default, {
open: portalRender && open,
autoDestroy: false,
getContainer: getContainer,
autoLock: lockScroll,
onEsc: onEsc
}, /*#__PURE__*/_react.default.createElement(_motion.default, {
motionName: motionName,
visible: portalRender && open,
motionAppear: true,
motionEnter: true,
motionLeave: true,
onVisibleChanged: onVisibleChanged
}, ({
className: motionClassName,
style: motionStyle
}) => {
const mergedStyle = {
...styles.root,
...motionStyle
};
if (zIndex) {
mergedStyle.zIndex = zIndex;
}
return /*#__PURE__*/_react.default.createElement("div", {
ref: wrapperRef,
className: (0, _clsx.clsx)(prefixCls, rootClassName, classNames.root, motionClassName, {
[`${prefixCls}-movable`]: movable,
[`${prefixCls}-moving`]: isMoving
}),
style: mergedStyle,
role: "dialog",
"aria-modal": "true",
"aria-label": alt,
tabIndex: -1
}, /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-mask`, classNames.mask),
style: styles.mask,
onClick: maskClosable ? onClose : undefined
}), /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-body`, classNames.body),
style: bodyStyle
}, imageRender ? imageRender(imgNode, {
transform,
image,
...(groupContext ? {
current
} : {})
}) : imgNode), closeIcon !== false && closeIcon !== null && /*#__PURE__*/_react.default.createElement(_CloseBtn.default, {
prefixCls: prefixCls,
icon: closeIcon === true ? icons.close : closeIcon || icons.close,
onClick: onClose,
className: classNames.close,
style: styles.close
}), showLeftOrRightSwitches && /*#__PURE__*/_react.default.createElement(_PrevNext.default, {
prefixCls: prefixCls,
current: current,
count: count,
icons: icons,
onActive: onActive
}), /*#__PURE__*/_react.default.createElement(_Footer.default, {
prefixCls: prefixCls,
showProgress: showOperationsProgress,
current: current,
count: count,
showSwitch: showLeftOrRightSwitches
// Style
,
classNames: classNames,
styles: styles
// Render
,
image: image,
transform: transform,
icons: icons,
countRender: countRender,
actionsRender: actionsRender
// Scale
,
scale: scale,
minScale: minScale,
maxScale: maxScale
// Actions
,
onActive: onActive,
onFlipY: onFlipY,
onFlipX: onFlipX,
onRotateLeft: onRotateLeft,
onRotateRight: onRotateRight,
onZoomOut: onZoomOut,
onZoomIn: onZoomIn,
onClose: onClose,
onReset: onReset
}));
}));
};
var _default = exports.default = Preview;
+33
View File
@@ -0,0 +1,33 @@
import * as React from 'react';
import type { ImgInfo } from './Image';
import type { InternalPreviewConfig, PreviewProps, PreviewSemanticName } from './Preview';
import type { TransformType } from './hooks/useImageTransform';
import type { ImageElementProps } from './interface';
export interface GroupPreviewConfig extends InternalPreviewConfig {
current?: number;
imageRender?: (originalNode: React.ReactElement, info: {
transform: TransformType;
current: number;
image: ImgInfo;
}) => React.ReactNode;
onOpenChange?: (value: boolean, info: {
current: number;
}) => void;
onChange?: (current: number, prevCurrent: number) => void;
}
export interface PreviewGroupProps {
previewPrefixCls?: string;
classNames?: {
popup?: Partial<Record<PreviewSemanticName, string>>;
};
styles?: {
popup?: Partial<Record<PreviewSemanticName, React.CSSProperties>>;
};
icons?: PreviewProps['icons'];
items?: (string | ImageElementProps)[];
fallback?: string;
preview?: boolean | GroupPreviewConfig;
children?: React.ReactNode;
}
declare const Group: React.FC<PreviewGroupProps>;
export default Group;
+121
View File
@@ -0,0 +1,121 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _useControlledState = _interopRequireDefault(require("@rc-component/util/lib/hooks/useControlledState"));
var _useEvent = _interopRequireDefault(require("@rc-component/util/lib/hooks/useEvent"));
var _react = _interopRequireWildcard(require("react"));
var React = _react;
var _Preview = _interopRequireDefault(require("./Preview"));
var _context = require("./context");
var _usePreviewItems = _interopRequireDefault(require("./hooks/usePreviewItems"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
const Group = ({
previewPrefixCls = 'rc-image-preview',
classNames,
styles,
children,
icons = {},
items,
preview,
fallback
}) => {
const {
open: previewOpen,
onOpenChange,
current: currentIndex,
onChange,
...restProps
} = preview && typeof preview === 'object' ? preview : {};
// ========================== Items ===========================
const [mergedItems, register, fromItems] = (0, _usePreviewItems.default)(items);
// ========================= Preview ==========================
// >>> Index
const [current, setCurrent] = (0, _useControlledState.default)(0, currentIndex);
const [keepOpenIndex, setKeepOpenIndex] = (0, _react.useState)(false);
// >>> Image
const {
src,
...imgCommonProps
} = mergedItems[current]?.data || {};
// >>> Visible
const [isShowPreview, setShowPreview] = (0, _useControlledState.default)(!!previewOpen, previewOpen);
const triggerShowPreview = (0, _useEvent.default)(next => {
setShowPreview(next);
if (next !== isShowPreview) {
onOpenChange?.(next, {
current
});
}
});
// >>> Position
const [mousePosition, setMousePosition] = (0, _react.useState)(null);
const onPreviewFromImage = React.useCallback((id, imageSrc, mouseX, mouseY) => {
const index = fromItems ? mergedItems.findIndex(item => item.data.src === imageSrc) : mergedItems.findIndex(item => item.id === id);
setCurrent(index < 0 ? 0 : index);
triggerShowPreview(true);
setMousePosition({
x: mouseX,
y: mouseY
});
setKeepOpenIndex(true);
}, [mergedItems, fromItems]);
// Reset current when reopen
React.useEffect(() => {
if (isShowPreview) {
if (!keepOpenIndex) {
setCurrent(0);
}
} else {
setKeepOpenIndex(false);
}
}, [isShowPreview]);
// ========================== Events ==========================
const onInternalChange = (next, prev) => {
setCurrent(next);
onChange?.(next, prev);
};
const onPreviewClose = () => {
triggerShowPreview(false);
setMousePosition(null);
};
// ========================= Context ==========================
const previewGroupContext = React.useMemo(() => ({
register,
onPreview: onPreviewFromImage
}), [register, onPreviewFromImage]);
// ========================== Render ==========================
return /*#__PURE__*/React.createElement(_context.PreviewGroupContext.Provider, {
value: previewGroupContext
}, children, /*#__PURE__*/React.createElement(_Preview.default, _extends({
"aria-hidden": !isShowPreview,
open: isShowPreview,
prefixCls: previewPrefixCls,
onClose: onPreviewClose,
mousePosition: mousePosition,
imgCommonProps: imgCommonProps,
src: src,
fallback: fallback,
icons: icons,
current: current,
count: mergedItems.length,
onChange: onInternalChange
}, restProps, {
classNames: classNames?.popup,
styles: styles?.popup
})));
};
var _default = exports.default = Group;
+2
View File
@@ -0,0 +1,2 @@
import type { ImageElementProps } from './interface';
export declare const COMMON_PROPS: (keyof Omit<ImageElementProps, 'src'>)[];
+7
View File
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.COMMON_PROPS = void 0;
const COMMON_PROPS = exports.COMMON_PROPS = ['crossOrigin', 'decoding', 'draggable', 'loading', 'referrerPolicy', 'sizes', 'srcSet', 'useMap', 'alt', 'fetchPriority'];
+7
View File
@@ -0,0 +1,7 @@
import * as React from 'react';
import type { OnGroupPreview, RegisterImage } from './interface';
export interface PreviewGroupContextProps {
register: RegisterImage;
onPreview: OnGroupPreview;
}
export declare const PreviewGroupContext: React.Context<PreviewGroupContextProps>;
+10
View File
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.PreviewGroupContext = void 0;
var React = _interopRequireWildcard(require("react"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const PreviewGroupContext = exports.PreviewGroupContext = /*#__PURE__*/React.createContext(null);
@@ -0,0 +1,16 @@
/**
* Fix positon x,y point when
*
* Ele width && height < client
* - Back origin
*
* - Ele width | height > clientWidth | clientHeight
* - left | top > 0 -> Back 0
* - left | top + width | height < clientWidth | clientHeight -> Back left | top + width | height === clientWidth | clientHeight
*
* Regardless of other
*/
export default function getFixScaleEleTransPosition(width: number, height: number, left: number, top: number): null | {
x: number;
y: number;
};
@@ -0,0 +1,60 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getFixScaleEleTransPosition;
var _util = require("./util");
function fixPoint(key, start, width, clientWidth) {
const startAddWidth = start + width;
const offsetStart = (width - clientWidth) / 2;
if (width > clientWidth) {
if (start > 0) {
return {
[key]: offsetStart
};
}
if (start < 0 && startAddWidth < clientWidth) {
return {
[key]: -offsetStart
};
}
} else if (start < 0 || startAddWidth > clientWidth) {
return {
[key]: start < 0 ? offsetStart : -offsetStart
};
}
return {};
}
/**
* Fix positon x,y point when
*
* Ele width && height < client
* - Back origin
*
* - Ele width | height > clientWidth | clientHeight
* - left | top > 0 -> Back 0
* - left | top + width | height < clientWidth | clientHeight -> Back left | top + width | height === clientWidth | clientHeight
*
* Regardless of other
*/
function getFixScaleEleTransPosition(width, height, left, top) {
const {
width: clientWidth,
height: clientHeight
} = (0, _util.getClientSize)();
let fixPos = null;
if (width <= clientWidth && height <= clientHeight) {
fixPos = {
x: 0,
y: 0
};
} else if (width > clientWidth || height > clientHeight) {
fixPos = {
...fixPoint('x', left, width, clientWidth),
...fixPoint('y', top, height, clientHeight)
};
}
return fixPos;
}
@@ -0,0 +1,28 @@
/// <reference types="react" />
export type TransformType = {
x: number;
y: number;
rotate: number;
scale: number;
flipX: boolean;
flipY: boolean;
};
export type TransformAction = 'flipY' | 'flipX' | 'rotateLeft' | 'rotateRight' | 'zoomIn' | 'zoomOut' | 'close' | 'prev' | 'next' | 'wheel' | 'doubleClick' | 'move' | 'dragRebound' | 'touchZoom' | 'reset';
export type UpdateTransformFunc = (newTransform: Partial<TransformType>, action: TransformAction) => void;
export type DispatchZoomChangeFunc = (ratio: number, action: TransformAction, centerX?: number, centerY?: number, isTouch?: boolean) => void;
export default function useImageTransform(imgRef: React.MutableRefObject<HTMLImageElement>, minScale: number, maxScale: number, onTransform: (info: {
transform: TransformType;
action: TransformAction;
}) => void): {
transform: {
x: number;
y: number;
rotate: number;
scale: number;
flipX: boolean;
flipY: boolean;
};
resetTransform: (action: TransformAction) => void;
updateTransform: UpdateTransformFunc;
dispatchZoomChange: DispatchZoomChangeFunc;
};
@@ -0,0 +1,125 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useImageTransform;
var _util = require("../util");
var _isEqual = _interopRequireDefault(require("@rc-component/util/lib/isEqual"));
var _raf = _interopRequireDefault(require("@rc-component/util/lib/raf"));
var _react = require("react");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const initialTransform = {
x: 0,
y: 0,
rotate: 0,
scale: 1,
flipX: false,
flipY: false
};
function useImageTransform(imgRef, minScale, maxScale, onTransform) {
const frame = (0, _react.useRef)(null);
const queue = (0, _react.useRef)([]);
const [transform, setTransform] = (0, _react.useState)(initialTransform);
const resetTransform = action => {
setTransform(initialTransform);
if (!(0, _isEqual.default)(initialTransform, transform)) {
onTransform?.({
transform: initialTransform,
action
});
}
};
/** Direct update transform */
const updateTransform = (newTransform, action) => {
if (frame.current === null) {
queue.current = [];
frame.current = (0, _raf.default)(() => {
setTransform(preState => {
let memoState = preState;
queue.current.forEach(queueState => {
memoState = {
...memoState,
...queueState
};
});
frame.current = null;
onTransform?.({
transform: memoState,
action
});
return memoState;
});
});
}
queue.current.push({
...transform,
...newTransform
});
};
/** Scale according to the position of centerX and centerY */
const dispatchZoomChange = (ratio, action, centerX, centerY, isTouch) => {
const {
width,
height,
offsetWidth,
offsetHeight,
offsetLeft,
offsetTop
} = imgRef.current;
let newRatio = ratio;
let newScale = transform.scale * ratio;
if (newScale > maxScale) {
newScale = maxScale;
newRatio = maxScale / transform.scale;
} else if (newScale < minScale) {
// For mobile interactions, allow scaling down to the minimum scale.
newScale = isTouch ? newScale : minScale;
newRatio = newScale / transform.scale;
}
/** Default center point scaling */
const mergedCenterX = centerX ?? innerWidth / 2;
const mergedCenterY = centerY ?? innerHeight / 2;
const diffRatio = newRatio - 1;
/** Deviation calculated from image size */
const diffImgX = diffRatio * width * 0.5;
const diffImgY = diffRatio * height * 0.5;
/** The difference between the click position and the edge of the document */
const diffOffsetLeft = diffRatio * (mergedCenterX - transform.x - offsetLeft);
const diffOffsetTop = diffRatio * (mergedCenterY - transform.y - offsetTop);
/** Final positioning */
let newX = transform.x - (diffOffsetLeft - diffImgX);
let newY = transform.y - (diffOffsetTop - diffImgY);
/**
* When zooming the image
* When the image size is smaller than the width and height of the window, the position is initialized
*/
if (ratio < 1 && newScale === 1) {
const mergedWidth = offsetWidth * newScale;
const mergedHeight = offsetHeight * newScale;
const {
width: clientWidth,
height: clientHeight
} = (0, _util.getClientSize)();
if (mergedWidth <= clientWidth && mergedHeight <= clientHeight) {
newX = 0;
newY = 0;
}
}
updateTransform({
x: newX,
y: newY,
scale: newScale
}, action);
};
return {
transform,
resetTransform,
updateTransform,
dispatchZoomChange
};
}
@@ -0,0 +1,9 @@
import type React from 'react';
import type { DispatchZoomChangeFunc, TransformType, UpdateTransformFunc } from './useImageTransform';
export default function useMouseEvent(imgRef: React.MutableRefObject<HTMLImageElement>, movable: boolean, open: boolean, scaleStep: number, transform: TransformType, updateTransform: UpdateTransformFunc, dispatchZoomChange: DispatchZoomChangeFunc): {
isMoving: boolean;
onMouseDown: React.MouseEventHandler<HTMLDivElement>;
onMouseMove: (event: MouseEvent) => void;
onMouseUp: () => void;
onWheel: (event: React.WheelEvent<HTMLImageElement>) => void;
};
@@ -0,0 +1,123 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useMouseEvent;
var _warning = require("@rc-component/util/lib/warning");
var _react = require("react");
var _getFixScaleEleTransPosition = _interopRequireDefault(require("../getFixScaleEleTransPosition"));
var _previewConfig = require("../previewConfig");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function useMouseEvent(imgRef, movable, open, scaleStep, transform, updateTransform, dispatchZoomChange) {
const {
rotate,
scale,
x,
y
} = transform;
const [isMoving, setMoving] = (0, _react.useState)(false);
const startPositionInfo = (0, _react.useRef)({
diffX: 0,
diffY: 0,
transformX: 0,
transformY: 0
});
const onMouseDown = event => {
// Only allow main button
if (!movable || event.button !== 0) return;
event.preventDefault();
event.stopPropagation();
startPositionInfo.current = {
diffX: event.pageX - x,
diffY: event.pageY - y,
transformX: x,
transformY: y
};
setMoving(true);
};
const onMouseMove = event => {
if (open && isMoving) {
updateTransform({
x: event.pageX - startPositionInfo.current.diffX,
y: event.pageY - startPositionInfo.current.diffY
}, 'move');
}
};
const onMouseUp = () => {
if (open && isMoving) {
setMoving(false);
/** No need to restore the position when the picture is not moved, So as not to interfere with the click */
const {
transformX,
transformY
} = startPositionInfo.current;
const hasChangedPosition = x !== transformX && y !== transformY;
if (!hasChangedPosition) return;
const width = imgRef.current.offsetWidth * scale;
const height = imgRef.current.offsetHeight * scale;
// eslint-disable-next-line @typescript-eslint/no-shadow
const {
left,
top
} = imgRef.current.getBoundingClientRect();
const isRotate = rotate % 180 !== 0;
const fixState = (0, _getFixScaleEleTransPosition.default)(isRotate ? height : width, isRotate ? width : height, left, top);
if (fixState) {
updateTransform({
...fixState
}, 'dragRebound');
}
}
};
const onWheel = event => {
if (!open || event.deltaY == 0) return;
// Scale ratio depends on the deltaY size
const scaleRatio = Math.abs(event.deltaY / 100);
// Limit the maximum scale ratio
const mergedScaleRatio = Math.min(scaleRatio, _previewConfig.WHEEL_MAX_SCALE_RATIO);
// Scale the ratio each time
let ratio = _previewConfig.BASE_SCALE_RATIO + mergedScaleRatio * scaleStep;
if (event.deltaY > 0) {
ratio = _previewConfig.BASE_SCALE_RATIO / ratio;
}
dispatchZoomChange(ratio, 'wheel', event.clientX, event.clientY);
};
(0, _react.useEffect)(() => {
if (movable) {
window.addEventListener('mouseup', onMouseUp, false);
window.addEventListener('mousemove', onMouseMove, false);
try {
// Resolve if in iframe lost event
/* istanbul ignore next */
if (window.top !== window.self) {
window.top.addEventListener('mouseup', onMouseUp, false);
window.top.addEventListener('mousemove', onMouseMove, false);
}
} catch (error) {
/* istanbul ignore next */
(0, _warning.warning)(false, `[rc-image] ${error}`);
}
}
return () => {
window.removeEventListener('mouseup', onMouseUp);
window.removeEventListener('mousemove', onMouseMove);
/* istanbul ignore next */
try {
window.top?.removeEventListener('mouseup', onMouseUp);
window.top?.removeEventListener('mousemove', onMouseMove);
} catch (error) {
// Do nothing
}
};
}, [open, isMoving, x, y, rotate, movable]);
return {
isMoving,
onMouseDown,
onMouseMove,
onMouseUp,
onWheel
};
}
@@ -0,0 +1,7 @@
import type { PreviewGroupProps } from '../PreviewGroup';
import type { InternalItem, RegisterImage } from '../interface';
export type Items = Omit<InternalItem, 'canPreview'>[];
/**
* Merge props provided `items` or context collected images
*/
export default function usePreviewItems(items?: PreviewGroupProps['items']): [items: Items, registerImage: RegisterImage, fromItems: boolean];
@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = usePreviewItems;
var React = _interopRequireWildcard(require("react"));
var _common = require("../common");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
* Merge props provided `items` or context collected images
*/
function usePreviewItems(items) {
// Context collection image data
const [images, setImages] = React.useState({});
const registerImage = React.useCallback((id, data) => {
setImages(imgs => ({
...imgs,
[id]: data
}));
return () => {
setImages(imgs => {
const cloneImgs = {
...imgs
};
delete cloneImgs[id];
return cloneImgs;
});
};
}, []);
// items
const mergedItems = React.useMemo(() => {
// use `items` first
if (items) {
return items.map(item => {
if (typeof item === 'string') {
return {
data: {
src: item
}
};
}
const data = {};
Object.keys(item).forEach(key => {
if (['src', ..._common.COMMON_PROPS].includes(key)) {
data[key] = item[key];
}
});
return {
data
};
});
}
// use registered images secondly
return Object.keys(images).reduce((total, id) => {
const {
canPreview,
data
} = images[id];
if (canPreview) {
total.push({
data,
id
});
}
return total;
}, []);
}, [items, images]);
return [mergedItems, registerImage, !!items];
}
@@ -0,0 +1,2 @@
import type { ImageElementProps } from '../interface';
export default function useRegisterImage(canPreview: boolean, data: ImageElementProps): string;
@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useRegisterImage;
var React = _interopRequireWildcard(require("react"));
var _context = require("../context");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
let uid = 0;
function useRegisterImage(canPreview, data) {
const [id] = React.useState(() => {
uid += 1;
return String(uid);
});
const groupContext = React.useContext(_context.PreviewGroupContext);
const registerData = {
data,
canPreview
};
// Keep order start
// Resolve https://github.com/ant-design/ant-design/issues/28881
// Only need unRegister when component unMount
React.useEffect(() => {
if (groupContext) {
return groupContext.register(id, registerData);
}
}, []);
React.useEffect(() => {
if (groupContext) {
groupContext.register(id, registerData);
}
}, [canPreview, data]);
return id;
}
@@ -0,0 +1,13 @@
type ImageStatus = 'normal' | 'error' | 'loading';
export default function useStatus({ src, isCustomPlaceholder, fallback, }: {
src: string;
isCustomPlaceholder?: boolean;
fallback?: string;
}): readonly [(img?: HTMLImageElement) => void, {
src: string;
onLoad?: undefined;
} | {
onLoad: () => void;
src: string;
}, ImageStatus];
export {};
@@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useStatus;
var _react = require("react");
var _util = require("../util");
function useStatus({
src,
isCustomPlaceholder,
fallback
}) {
const [status, setStatus] = (0, _react.useState)(isCustomPlaceholder ? 'loading' : 'normal');
const isLoaded = (0, _react.useRef)(false);
const isError = status === 'error';
// https://github.com/react-component/image/pull/187
(0, _react.useEffect)(() => {
let isCurrentSrc = true;
(0, _util.isImageValid)(src).then(isValid => {
// https://github.com/ant-design/ant-design/issues/44948
// If src changes, the previous setStatus should not be triggered
if (!isValid && isCurrentSrc) {
setStatus('error');
}
});
return () => {
isCurrentSrc = false;
};
}, [src]);
(0, _react.useEffect)(() => {
if (isCustomPlaceholder && !isLoaded.current) {
setStatus('loading');
} else if (isError) {
setStatus('normal');
}
}, [src]);
const onLoad = () => {
setStatus('normal');
};
const getImgRef = img => {
isLoaded.current = false;
if (status === 'loading' && img?.complete && (img.naturalWidth || img.naturalHeight)) {
isLoaded.current = true;
onLoad();
}
};
const srcAndOnload = isError && fallback ? {
src: fallback
} : {
onLoad,
src
};
return [getImgRef, srcAndOnload, status];
}
@@ -0,0 +1,8 @@
import type React from 'react';
import type { DispatchZoomChangeFunc, TransformType, UpdateTransformFunc } from './useImageTransform';
export default function useTouchEvent(imgRef: React.MutableRefObject<HTMLImageElement>, movable: boolean, open: boolean, minScale: number, transform: TransformType, updateTransform: UpdateTransformFunc, dispatchZoomChange: DispatchZoomChangeFunc): {
isTouching: boolean;
onTouchStart: (event: React.TouchEvent<HTMLImageElement>) => void;
onTouchMove: (event: React.TouchEvent<HTMLImageElement>) => void;
onTouchEnd: () => void;
};
@@ -0,0 +1,177 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useTouchEvent;
var _react = require("react");
var _getFixScaleEleTransPosition = _interopRequireDefault(require("../getFixScaleEleTransPosition"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function getDistance(a, b) {
const x = a.x - b.x;
const y = a.y - b.y;
return Math.hypot(x, y);
}
function getCenter(oldPoint1, oldPoint2, newPoint1, newPoint2) {
// Calculate the distance each point has moved
const distance1 = getDistance(oldPoint1, newPoint1);
const distance2 = getDistance(oldPoint2, newPoint2);
// If both distances are 0, return the original points
if (distance1 === 0 && distance2 === 0) {
return [oldPoint1.x, oldPoint1.y];
}
// Calculate the ratio of the distances
const ratio = distance1 / (distance1 + distance2);
// Calculate the new center point based on the ratio
const x = oldPoint1.x + ratio * (oldPoint2.x - oldPoint1.x);
const y = oldPoint1.y + ratio * (oldPoint2.y - oldPoint1.y);
return [x, y];
}
function useTouchEvent(imgRef, movable, open, minScale, transform, updateTransform, dispatchZoomChange) {
const {
rotate,
scale,
x,
y
} = transform;
const [isTouching, setIsTouching] = (0, _react.useState)(false);
const touchPointInfo = (0, _react.useRef)({
point1: {
x: 0,
y: 0
},
point2: {
x: 0,
y: 0
},
eventType: 'none'
});
const updateTouchPointInfo = values => {
touchPointInfo.current = {
...touchPointInfo.current,
...values
};
};
const onTouchStart = event => {
if (!movable) return;
event.stopPropagation();
setIsTouching(true);
const {
touches = []
} = event;
if (touches.length > 1) {
// touch zoom
updateTouchPointInfo({
point1: {
x: touches[0].clientX,
y: touches[0].clientY
},
point2: {
x: touches[1].clientX,
y: touches[1].clientY
},
eventType: 'touchZoom'
});
} else {
// touch move
updateTouchPointInfo({
point1: {
x: touches[0].clientX - x,
y: touches[0].clientY - y
},
eventType: 'move'
});
}
};
const onTouchMove = event => {
const {
touches = []
} = event;
const {
point1,
point2,
eventType
} = touchPointInfo.current;
if (touches.length > 1 && eventType === 'touchZoom') {
// touch zoom
const newPoint1 = {
x: touches[0].clientX,
y: touches[0].clientY
};
const newPoint2 = {
x: touches[1].clientX,
y: touches[1].clientY
};
const [centerX, centerY] = getCenter(point1, point2, newPoint1, newPoint2);
const ratio = getDistance(newPoint1, newPoint2) / getDistance(point1, point2);
dispatchZoomChange(ratio, 'touchZoom', centerX, centerY, true);
updateTouchPointInfo({
point1: newPoint1,
point2: newPoint2,
eventType: 'touchZoom'
});
} else if (eventType === 'move') {
// touch move
updateTransform({
x: touches[0].clientX - point1.x,
y: touches[0].clientY - point1.y
}, 'move');
updateTouchPointInfo({
eventType: 'move'
});
}
};
const onTouchEnd = () => {
if (!open) return;
if (isTouching) {
setIsTouching(false);
}
updateTouchPointInfo({
eventType: 'none'
});
if (minScale > scale) {
/** When the scaling ratio is less than the minimum scaling ratio, reset the scaling ratio */
return updateTransform({
x: 0,
y: 0,
scale: minScale
}, 'touchZoom');
}
const width = imgRef.current.offsetWidth * scale;
const height = imgRef.current.offsetHeight * scale;
// eslint-disable-next-line @typescript-eslint/no-shadow
const {
left,
top
} = imgRef.current.getBoundingClientRect();
const isRotate = rotate % 180 !== 0;
const fixState = (0, _getFixScaleEleTransPosition.default)(isRotate ? height : width, isRotate ? width : height, left, top);
if (fixState) {
updateTransform({
...fixState
}, 'dragRebound');
}
};
(0, _react.useEffect)(() => {
const preventDefault = e => {
e.preventDefault();
};
if (open && movable) {
window.addEventListener('touchmove', preventDefault, {
passive: false
});
}
return () => {
window.removeEventListener('touchmove', preventDefault);
};
}, [open, movable]);
return {
isTouching,
onTouchStart,
onTouchMove,
onTouchEnd
};
}
+3
View File
@@ -0,0 +1,3 @@
import Image from './Image';
export * from './Image';
export default Image;
+22
View File
@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {};
exports.default = void 0;
var _Image = _interopRequireWildcard(require("./Image"));
Object.keys(_Image).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _Image[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _Image[key];
}
});
});
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
var _default = exports.default = _Image.default;
+14
View File
@@ -0,0 +1,14 @@
/// <reference types="react" />
/**
* Used for PreviewGroup passed image data
*/
export type ImageElementProps = Pick<React.ImgHTMLAttributes<HTMLImageElement>, 'src' | 'crossOrigin' | 'decoding' | 'draggable' | 'loading' | 'referrerPolicy' | 'sizes' | 'srcSet' | 'useMap' | 'alt' | 'fetchPriority'>;
export type PreviewImageElementProps = {
data: ImageElementProps;
canPreview: boolean;
};
export type InternalItem = PreviewImageElementProps & {
id?: string;
};
export type RegisterImage = (id: string, data: PreviewImageElementProps) => VoidFunction;
export type OnGroupPreview = (id: string, imageSrc: string, mouseX: number, mouseY: number) => void;
+5
View File
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
@@ -0,0 +1,4 @@
/** Scale the ratio base */
export declare const BASE_SCALE_RATIO = 1;
/** The maximum zoom ratio when the mouse zooms in, adjustable */
export declare const WHEEL_MAX_SCALE_RATIO = 1;
+10
View File
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.WHEEL_MAX_SCALE_RATIO = exports.BASE_SCALE_RATIO = void 0;
/** Scale the ratio base */
const BASE_SCALE_RATIO = exports.BASE_SCALE_RATIO = 1;
/** The maximum zoom ratio when the mouse zooms in, adjustable */
const WHEEL_MAX_SCALE_RATIO = exports.WHEEL_MAX_SCALE_RATIO = 1;
+5
View File
@@ -0,0 +1,5 @@
export declare function isImageValid(src: string): Promise<unknown>;
export declare function getClientSize(): {
width: number;
height: number;
};
+29
View File
@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getClientSize = getClientSize;
exports.isImageValid = isImageValid;
function isImageValid(src) {
return new Promise(resolve => {
if (!src) {
resolve(false);
return;
}
const img = document.createElement('img');
img.onerror = () => resolve(false);
img.onload = () => resolve(true);
img.src = src;
});
}
// ============================= Legacy =============================
function getClientSize() {
const width = document.documentElement.clientWidth;
const height = window.innerHeight || document.documentElement.clientHeight;
return {
width,
height
};
}