Skip to content

Commit

Permalink
feat: PreviewGroup items
Browse files Browse the repository at this point in the history
  • Loading branch information
linxianxi committed Jun 16, 2023
1 parent bff2fba commit e09c9e1
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 237 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ export default () => (
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| visible | boolean | - | Whether the preview is open or not |
| src | string | - | customize preview src |
| scaleStep | number | 0.5 | The number to which the scale is increased or decreased |
| minScale | number | 1 | min scale |
| maxScale | number | 50 | max scale |
| forceRender | boolean | - | Force render preview |
| showOnlyInPreview | boolean | - | only show image in preview |
| getContainer | string \| HTMLElement \| (() => HTMLElement) \| false | document.body | Return the mount node for preview |
| imageRender | { originalNode: React.ReactNode, transform: [TransformType](#TransformType) } => React.ReactNode | - | Customize image |
| toolbarRender | (params: Omit<[ToolbarRenderType](#ToolbarRenderType), 'current' \| 'total'>) => React.ReactNode | - | Customize toolbar |
Expand Down Expand Up @@ -121,8 +121,8 @@ export default () => (
| minScale | number | 1 | min scale |
| maxScale | number | 50 | max scale |
| forceRender | boolean | - | Force render preview |
| showOnlyInPreview | boolean | - | only show image in preview |
| getContainer | string \| HTMLElement \| (() => HTMLElement) \| false | document.body | Return the mount node for preview |
| items | (string \| { src: string, alt: string, crossOrigin: string, ... })[] | - | preview group |
| countRender | (current: number, total: number) => string | - | Customize count |
| imageRender | { originalNode: React.ReactNode, transform: [TransformType](#TransformType), current: number } => React.ReactNode | - | Customize image |
| toolbarRender | (params: [ToolbarRenderType](#ToolbarRenderType)) => React.ReactNode | - | Customize toolbar |
Expand Down
7 changes: 3 additions & 4 deletions docs/examples/album.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import '../../assets/index.less';
export default function PreviewGroup() {
return (
<div>
<Image.PreviewGroup>
<Image.PreviewGroup
items={[require('./images/1.jpeg'), require('./images/2.jpeg'), require('./images/3.jpeg')]}
>
<Image src={require('./images/1.jpeg')} />
<Image preview={{ showOnlyInPreview: true }} src={require('./images/2.jpeg')} />
<Image src={require('./images/3.jpeg')} />
<Image preview={{ showOnlyInPreview: true }} src={require('./images/1.jpeg')} />
</Image.PreviewGroup>
</div>
);
Expand Down
146 changes: 65 additions & 81 deletions src/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getOffset } from 'rc-util/lib/Dom/css';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { GetContainer } from 'rc-util/lib/PortalWrapper';
import * as React from 'react';
import { useState } from 'react';
import { useContext, useEffect, useRef, useState } from 'react';
import type { TransformType } from './hooks/useImageTransform';
import type { PreviewProps, ToolbarRenderType } from './Preview';
import Preview from './Preview';
Expand All @@ -21,7 +21,6 @@ export interface ImagePreviewType
maxScale?: number;
onVisibleChange?: (value: boolean, prevValue: boolean) => void;
getContainer?: GetContainer | false;
showOnlyInPreview?: boolean;
mask?: React.ReactNode;
maskClassName?: string;
icons?: PreviewProps['icons'];
Expand Down Expand Up @@ -107,7 +106,6 @@ const ImageInternal: CompoundedComponent<ImageProps> = ({
visible: previewVisible = undefined,
onVisibleChange: onPreviewVisibleChange = onInitialPreviewClose,
getContainer: getPreviewContainer = undefined,
showOnlyInPreview,
mask: previewMask,
maskClassName,
icons,
Expand All @@ -119,7 +117,6 @@ const ImageInternal: CompoundedComponent<ImageProps> = ({
...dialogProps
}: ImagePreviewType = typeof preview === 'object' ? preview : {};
const src = previewSrc ?? imgSrc;
const isControlled = previewVisible !== undefined;
const [isShowPreview, setShowPreview] = useMergedState(!!previewVisible, {
value: previewVisible,
onChange: onPreviewVisibleChange,
Expand All @@ -129,19 +126,19 @@ const ImageInternal: CompoundedComponent<ImageProps> = ({
const isError = status === 'error';
const {
isPreviewGroup,
showOnlyInPreview: previewGroupOnly,
setCurrent,
setShowPreview: setGroupShowPreview,
setMousePosition: setGroupMousePosition,
registerImage,
} = React.useContext(context);
const [currentId] = React.useState<number>(() => {
setCurrentIndex,
getStartPreviewIndex,
} = useContext(context);
const [currentId] = useState<number>(() => {
uuid += 1;
return uuid;
});
const canPreview = !!preview;

const isLoaded = React.useRef(false);
const isLoaded = useRef(false);

const imgCommonProps = {
crossOrigin,
Expand All @@ -160,26 +157,20 @@ const ImageInternal: CompoundedComponent<ImageProps> = ({
};

const onPreview: React.MouseEventHandler<HTMLDivElement> = e => {
if (!isControlled) {
const { left, top } = getOffset(e.target);

if (isPreviewGroup) {
setCurrent(currentId);
setGroupMousePosition({
x: left,
y: top,
});
} else {
setMousePosition({
x: left,
y: top,
});
}
}

const { left, top } = getOffset(e.target);
if (isPreviewGroup) {
setGroupMousePosition({
x: left,
y: top,
});

setCurrentIndex(getStartPreviewIndex(currentId));
setGroupShowPreview(true);
} else {
setMousePosition({
x: left,
y: top,
});
setShowPreview(true);
}

Expand All @@ -188,9 +179,7 @@ const ImageInternal: CompoundedComponent<ImageProps> = ({

const onPreviewClose = () => {
setShowPreview(false);
if (!isControlled) {
setMousePosition(null);
}
setMousePosition(null);
};

const getImgRef = (img?: HTMLImageElement) => {
Expand All @@ -202,7 +191,7 @@ const ImageInternal: CompoundedComponent<ImageProps> = ({
}
};

React.useEffect(() => {
useEffect(() => {
isImageValid(src).then(isValid => {
if (!isValid) {
setStatus('error');
Expand All @@ -213,7 +202,7 @@ const ImageInternal: CompoundedComponent<ImageProps> = ({
// Keep order start
// Resolve https://github.com/ant-design/ant-design/issues/28881
// Only need unRegister when component unMount
React.useEffect(() => {
useEffect(() => {
const unRegister = registerImage(currentId, {
src,
imgCommonProps,
Expand All @@ -223,12 +212,12 @@ const ImageInternal: CompoundedComponent<ImageProps> = ({
return unRegister;
}, []);

React.useEffect(() => {
useEffect(() => {
registerImage(currentId, { src, imgCommonProps, canPreview });
}, [src, canPreview, JSON.stringify(imgCommonProps)]);
// Keep order end

React.useEffect(() => {
useEffect(() => {
if (isError) {
setStatus('normal');
}
Expand All @@ -243,61 +232,56 @@ const ImageInternal: CompoundedComponent<ImageProps> = ({

const mergedSrc = isError && fallback ? fallback : src;

const mergedOnly =
isPreviewGroup && showOnlyInPreview === undefined ? previewGroupOnly : showOnlyInPreview;

return (
<>
{!mergedOnly && (
<div
{...otherProps}
className={wrapperClass}
onClick={canPreview ? onPreview : onClick}
<div
{...otherProps}
className={wrapperClass}
onClick={canPreview ? onPreview : onClick}
style={{
width,
height,
...wrapperStyle,
}}
>
<img
{...imgCommonProps}
className={cn(
`${prefixCls}-img`,
{
[`${prefixCls}-img-placeholder`]: placeholder === true,
},
className,
)}
style={{
width,
height,
...wrapperStyle,
...style,
}}
>
<img
{...imgCommonProps}
className={cn(
`${prefixCls}-img`,
{
[`${prefixCls}-img-placeholder`]: placeholder === true,
},
className,
)}
style={{
height,
...style,
}}
ref={getImgRef}
{...(isError && fallback ? { src: fallback } : { onLoad, src: imgSrc })}
width={width}
height={height}
onError={onError}
/>
ref={getImgRef}
{...(isError && fallback ? { src: fallback } : { onLoad, src: imgSrc })}
width={width}
height={height}
onError={onError}
/>

{status === 'loading' && (
<div aria-hidden="true" className={`${prefixCls}-placeholder`}>
{placeholder}
</div>
)}
{status === 'loading' && (
<div aria-hidden="true" className={`${prefixCls}-placeholder`}>
{placeholder}
</div>
)}

{/* Preview Click Mask */}
{previewMask && canPreview && (
<div
className={cn(`${prefixCls}-mask`, maskClassName)}
style={{
display: style?.display === 'none' ? 'none' : undefined,
}}
>
{previewMask}
</div>
)}
</div>
)}
{/* Preview Click Mask */}
{previewMask && canPreview && (
<div
className={cn(`${prefixCls}-mask`, maskClassName)}
style={{
display: style?.display === 'none' ? 'none' : undefined,
}}
>
{previewMask}
</div>
)}
</div>
{!isPreviewGroup && canPreview && (
<Preview
aria-hidden={!isShowPreview}
Expand Down
15 changes: 8 additions & 7 deletions src/Operations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Portal from '@rc-component/portal';
import classnames from 'classnames';
import CSSMotion from 'rc-motion';
import * as React from 'react';
import { useContext } from 'react';
import type { TransformType } from './hooks/useImageTransform';
import type { PreviewProps, ToolbarRenderType } from './Preview';
import { context } from './PreviewGroup';
Expand All @@ -20,8 +21,8 @@ interface OperationsProps
> {
showSwitch: boolean;
showProgress: boolean;
currentIndex: number;
transform: TransformType;
current: number;
count: number;
scale: number;
minScale: number;
Expand Down Expand Up @@ -50,8 +51,8 @@ const Operations: React.FC<OperationsProps> = props => {
countRender,
showSwitch,
showProgress,
currentIndex,
transform,
current,
count,
scale,
minScale,
Expand All @@ -67,7 +68,7 @@ const Operations: React.FC<OperationsProps> = props => {
onFlipY,
toolbarRender,
} = props;
const { isPreviewGroup } = React.useContext(context);
const { isPreviewGroup } = useContext(context);
const { rotateLeft, rotateRight, zoomIn, zoomOut, close, left, right, flipX, flipY } = icons;
const toolClassName = `${prefixCls}-operations-operation`;
const iconClassName = `${prefixCls}-operations-icon`;
Expand Down Expand Up @@ -130,7 +131,7 @@ const Operations: React.FC<OperationsProps> = props => {
<ul className={`${prefixCls}-operations`}>
{showProgress && (
<li className={`${prefixCls}-operations-progress`}>
{countRender?.(current + 1, count) ?? `${current + 1} / ${count}`}
{countRender?.(currentIndex + 1, count) ?? `${currentIndex + 1} / ${count}`}
</li>
)}
{toolsNode}
Expand All @@ -143,15 +144,15 @@ const Operations: React.FC<OperationsProps> = props => {
<>
<div
className={classnames(`${prefixCls}-switch-left`, {
[`${prefixCls}-switch-left-disabled`]: current === 0,
[`${prefixCls}-switch-left-disabled`]: currentIndex === 0,
})}
onClick={onSwitchLeft}
>
{left}
</div>
<div
className={classnames(`${prefixCls}-switch-right`, {
[`${prefixCls}-switch-right-disabled`]: current === count - 1,
[`${prefixCls}-switch-right-disabled`]: currentIndex === count - 1,
})}
onClick={onSwitchRight}
>
Expand Down Expand Up @@ -181,7 +182,7 @@ const Operations: React.FC<OperationsProps> = props => {
close: onClose,
},
transform,
...(isPreviewGroup ? { current, total: count } : {}),
...(isPreviewGroup ? { current: currentIndex, total: count } : {}),
})
: toolbar}
</>
Expand Down
Loading

0 comments on commit e09c9e1

Please sign in to comment.