feat(@zhst/map): 添加marker功能

This commit is contained in:
NICE CODE BY DEV 2024-05-23 18:14:32 +08:00
parent 2e753a7259
commit 23706fabf9
79 changed files with 9551 additions and 119 deletions

View File

@ -52,6 +52,7 @@
"react-map-gl": "^7.1.7"
},
"devDependencies": {
"@types/mapbox__mapbox-gl-draw": "^1.4.6"
"@types/mapbox__mapbox-gl-draw": "^1.4.6",
"axios": "^1.7.2"
}
}

View File

@ -1,42 +1,105 @@
import 'mapbox-gl/dist/mapbox-gl.css';
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css';
import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import Map from 'react-map-gl';
import {
// MapboxMap,
MapRef,
// MapStyle
} from "react-map-gl";
import { CSSProperties } from "react";
import { MapboxMap, MapRef, MapStyle } from "react-map-gl";
import classnames from 'classnames'
import Tools from './components/tools'
import Tools, { ToolsProps } from './components/tools'
import DrawControl, { DrawControlProps, DrawControlRefProps } from './components/drawControl';
import { MapProps } from './interface';
import { merge } from './utils';
import { MAP_CENTER, defaultMapConfig } from './utils/constants';
import './index.less';
import mapboxDrawStyle from './utils/drawStyle';
import Marker from './components/marker';
const componentName = 'zhst-map'
export interface MapRefProps {
export interface IMarkerData {
key: string;
title: string;
population: string;
status: string;
latitude: number;
longitude: number;
}
export interface MapProps extends Partial<MapboxMap> {
mapboxAccessToken?: string; //token
markerData?: IMarkerData[]
minZoom?: number; //最小层级
maxZoom?: number; //最大层级
mapStyle?: MapStyle; //地图样式
height?: number | string;
width?: string | number;
mapRef?: MapRef;
style?: CSSProperties;
children?: JSX.Element | JSX.Element[] | Array<JSX.Element | undefined>;
mapCenter?: {
longitude: number, latitude: number
}
draw?: boolean;
buttonList?: ToolsProps['buttonList']
onLoad?: (e: mapboxgl.MapboxEvent<undefined>) => void;
onDrawCreate?: (e: { features: object[], [key: string]: any }) => void;
onDrawUpdate?: (e: { features: object[], [key: string]: any }) => void;
onDrawDelete?: (e: { features: object[], [key: string]: any }) => void;
}
export interface MapRefProps {
}
const MapBox = forwardRef<MapRefProps, MapProps>((props, ref) => {
const {
style = {},
children,
onLoad,
mapCenter = MAP_CENTER,
height = 600,
width = '100%',
draw,
markerData = [],
buttonList = [
{
label: '圆形框选',
key: 'circle',
icon: 'icon-yuan',
onClick: () => drawControlRef.current?.drawer?.changeMode?.('draw_circle')
},
{
label: '矩形框选',
key: 'rect',
icon: 'icon-fang',
onClick: () => drawControlRef.current?.drawer?.changeMode?.('draw_rect')
},
{
label: '多边形框选',
key: 'more',
icon: 'icon-duobianxing',
onClick: () => drawControlRef.current?.drawer?.changeMode?.('draw_polygon')
},
{
label: '路径框选',
key: 'path',
icon: 'icon-lujingkuangxuannor',
onClick: () => drawControlRef.current?.drawer?.changeMode?.('draw_line_string')
},
{
label: '清除',
key: 'clear',
icon: 'icon-gongjuxiangguanbi',
onClick: () => drawControlRef.current?.drawer?.deleteAll()
}
],
onLoad,
onDrawCreate,
onDrawUpdate,
onDrawDelete,
...others
} = props || {};
const mapRef = useRef<MapRef>(null)
const drawControlRef = useRef<DrawControlRefProps>(null)
// 默认绘制配置
const [drawConfig, setConfig] = useState<DrawControlProps>({
const [drawConfig] = useState<DrawControlProps>({
displayControlsDefault: false,
position: 'top-left',
styles: mapboxDrawStyle,
@ -49,82 +112,54 @@ const MapBox = forwardRef<MapRefProps, MapProps>((props, ref) => {
defaultMode: 'draw_polygon',
})
const handleDrawCreate = e => {
console.log('handleDrawCreate', e)
}
const initMarker = useMemo(
() => {
return markerData.map((_item, index) => (
<Marker
key={_item.key || index}
latitude={_item.latitude}
longitude={_item.longitude}
/>
))
},
[markerData]
);
const handleDrawUpdate = e => {
console.log('handleDrawUpdate', e)
}
const handleDrawDelete = e => {
console.log('handleDrawDelete', e)
}
useEffect(() => {
console.log('drawControlRef', drawControlRef.current?.drawer?.deleteAll())
}, [])
useImperativeHandle(ref, () => ({}))
useImperativeHandle(ref, () => ({
mapRef: mapRef.current,
drawer: drawControlRef.current?.drawer,
}))
return (
//@ts-ignore
<div className={classnames(`${componentName}`)}>
<Tools
buttonList={[
{
label: '圆形框选',
key: 'circle',
icon: 'icon-yuan',
onClick: () => drawControlRef.current?.drawer?.changeMode?.('draw_circle')
},
{
label: '矩形框选',
key: 'rect',
icon: 'icon-fang',
onClick: () => drawControlRef.current?.drawer?.changeMode?.('draw_rect')
},
{
label: '多边形框选',
key: 'more',
icon: 'icon-duobianxing',
onClick: () => drawControlRef.current?.drawer?.changeMode?.('draw_polygon')
},
{
label: '路径框选',
key: 'path',
icon: 'icon-lujingkuangxuannor',
onClick: () => drawControlRef.current?.drawer?.changeMode?.('draw_line_string')
},
{
label: '清除',
key: 'clear',
icon: 'icon-cuo',
onClick: () => drawControlRef.current?.drawer?.deleteAll()
}
]}
buttonList={buttonList}
/>
{/* @ts-ignore */}
<Map
ref={mapRef}
className={classnames(`${componentName}-container`)}
initialViewState={{ ...mapCenter, zoom: 10 }}
onLoad={(e) => {
onLoad && onLoad(e);
}}
{...defaultMapConfig}
initialViewState={{ ...mapCenter }}
onLoad={onLoad}
style={{ width: width, height: height, ...style }}
{...merge(defaultMapConfig, others)}
{...others}
>
{initMarker}
{/* <CustomOverlay
>
<Button></Button>
</CustomOverlay> */}
<DrawControl
ref={drawControlRef}
onCreate={handleDrawCreate}
onUpdate={handleDrawUpdate}
onDelete={handleDrawDelete}
{...drawConfig}
/>
{/* ---------------绘制图层--------------------- */}
{draw && (
<DrawControl
ref={drawControlRef}
onCreate={onDrawCreate}
onUpdate={onDrawUpdate}
onDelete={onDrawDelete}
{...drawConfig}
/>
)}
{children}
</Map>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,79 @@
[{
"key": "UNCLUTER",
"state": [{
"type": "NORMAL",
"bgImage": "&#xe6ab;",
"bgColor": "#0099ff",
"frontImage": "&#xe6a8;",
"frontColor": "red",
"ripple": false,
"redDot": false,
"name": "IMAGE_UNCLUTER_NORMAL"
},
{
"type": "RUNING",
"backImage": "&#xe6ab;",
"backColor": "green",
"frontImage": "&#xe6a8;",
"frontColor": "red",
"ripple": false,
"redDot": false,
"name": "IMAGE_UNCLUTER_RUNING"
},
{
"type": "NORMAL_SELECT",
"backImage": "&#xe6ab;",
"backColor": "yellow",
"frontImage": "&#xe6a8;",
"frontColor": "red",
"ripple": false,
"redDot": false,
"name": "IMAGE_UNCLUTER_NORMAL_SELECT"
},
{
"type": "RUNING_SELECT",
"backImage": "&#xe6ab;",
"backColor": "red",
"frontImage": "&#xe6a8;",
"frontColor": "blue",
"ripple": false,
"redDot": false,
"name": "IMAGE_UNCLUTER_RUNING_SELECT"
}
]
},
{
"key": "APPEARCLUTER_RED",
"state": [{
"type": "BLUE",
"bgImage": "&#xe6d4;",
"bgColor": "#0099ff",
"frontImage": "&#xe6d5;",
"frontColor": "#ffffff",
"ripple": false,
"redDot": false,
"name": "IMAGE_APPEARCLUTER_BLUE"
},
{
"type": "YELLOW",
"bgImage": "&#xe6d4;",
"bgColor": "#FF9900",
"frontImage": "&#xe6d5;",
"frontColor": "#ffffff",
"ripple": false,
"redDot": false,
"name": "IMAGE_APPEARCLUTER_YELLOW"
},
{
"type": "RED",
"bgImage": "&#xe6d4;",
"bgColor": "#F25051",
"frontImage": "&#xe6d5;",
"frontColor": "#ffffff",
"ripple": false,
"redDot": false,
"name": "IMAGE_APPEARCLUTER_RED"
}
]
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,186 @@
//引入地图所所有点图片
const file: { [key: string]: string } = {};
const modules = import.meta.glob<true, string, any>('./*.png', { eager: true });
Object.keys(modules).forEach((key) => {
const fileNmae = key.substring(key.lastIndexOf('/') + 1, key.lastIndexOf('.'));
file[`${fileNmae}`] = modules[key].default;
});
export const images: { [key: string]: string } = {};
//图片点规则
export const unClustersImgExpressions: mapboxgl.SymbolLayout['icon-image'] = ['case'];
export const clustersImgExpressions: mapboxgl.SymbolLayout['icon-image'] = ['case'];
export const feClustersImgExpressions: mapboxgl.SymbolLayout['icon-image'] = ['case'];
export const feclusterPrefix = 'IMG_FECLUSTER';
export const unClusterPrefix = 'IMG_UNCLUSTER';
export const clusterPrefix = 'IMG_CLUSTER';
Object.keys(file).forEach((fileNmae) => {
const [prefix, type, status] = fileNmae.split('_');
const key = `${type.toUpperCase()}${!!status ? `_${status.toUpperCase()}` : ''}`;
if (prefix === 'marker') {
const imageId = `${unClusterPrefix}_${key}`;
images[imageId] = file[fileNmae];
unClustersImgExpressions.push(['==', ['get', 'type'], key]);
unClustersImgExpressions.push(imageId);
}
if (prefix === 'cluster') {
const imageId = `${clusterPrefix}_${key}`;
images[imageId] = file[fileNmae];
clustersImgExpressions.push(['==', ['get', 'type'], key]);
clustersImgExpressions.push(imageId);
}
if (prefix === 'fecluster') {
const imageId = `${feclusterPrefix}_${key}`;
images[imageId] = file[fileNmae];
feClustersImgExpressions.push(['==', ['get', 'type'], key]);
feClustersImgExpressions.push(imageId);
}
});
unClustersImgExpressions.push('IMG_UNCLUSTER_NORMAL');
clustersImgExpressions.push('IMG_CLUSTER_NORMAL');
feClustersImgExpressions.push('IMG_FECLUSTER_NORMAL');
export const imageData = [];
export const converImageByJson = (jsonData) => {
jsonData.map((v) => {
const key = v['key'];
v['state'].length > 0 &&
v['state'].map((_v) => {
const backWidth = 60;
const backHeight = 70;
const canvas = document.createElement('canvas');
canvas.width = backWidth;
canvas.height = backHeight;
const context = canvas.getContext('2d');
//context.clearRect(0, 0, backWidth, backHeight);
//context.beginPath();
context.fillStyle = _v['bgColor'];
context.font = '50px IconFont';
context.fillText(
eval(('("' + _v['bgImage']).replace('&#x', '\\u').replace(';', '') + '")'),
0,
backWidth
);
context.fillStyle = _v['frontColor'];
context.font = '50px IconFont';
context.fillText(
eval(('("' + _v['frontImage']).replace('&#x', '\\u').replace(';', '') + '")'),
0,
backWidth
);
//画小红点;
if (v['redDot']) {
context.fillStyle = 'red';
context.arc(20, 5, 3, Math.PI * 2, 0, true);
context.fill();
}
const targetImage = new Image();
targetImage.src = canvas.toDataURL('image/png');
imageData.push({
name: _v['name'],
targetImage: targetImage,
ripple: _v['ripple'],
});
});
});
};
function BezierEllipse2(ctx, x, y, a, b) {
ctx.beginPath();
const k = 0.5522848;
const ox = a * k; // 水平控制点偏移量
const oy = b * k; // 垂直控制点偏移量</p> <p> ctx.beginPath();
//从椭圆的左端点开始顺时针绘制四条三次贝塞尔曲线
ctx.moveTo(x - a, y);
ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b);
ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y);
ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);
ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);
ctx.closePath();
}
function rgbToRgba(color, alp) {
const rgbaAttr = color.match(/[\d.]+/g);
if (rgbaAttr.length >= 3) {
const [r, g, b] = rgbaAttr;
// r = rgbaAttr[0];
// g = rgbaAttr[1];
// b = rgbaAttr[2];
return 'rgba(' + r + ',' + g + ',' + b + ',' + alp + ')';
}
}
export function convertImageToMap(name, map) {
const size = 150;
const imageObject = imageData.find((v) => v['name'] === name);
const initialImage = imageObject.targetImage;
const hasRipper = imageObject['ripple'];
const pulsingDot = {
width: size,
height: size,
data: new Uint8Array(size * size * 4),
onAdd: function () {
const canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
this.canvas = canvas;
this.context = canvas.getContext('2d');
},
render: function () {
const duration = 1.5 * 1000; //1.5s
const t = (performance.now() % duration) / duration;
const alp = 1 - t;
// const size = 10;
const outerRadius = (size / 2) * 1 * t;
const outerRadius2 = (size / 2) * 0.6 * t;
const context = this.context;
// draw outer circle
context.clearRect(0, 0, this.width, this.height);
context.beginPath();
context.drawImage(initialImage, 0, 0, this.width, this.height);
// context.drawImage('./cluster_escape.png', this.width / 2, this.height / 2, this.width / 2, this.height / 2);
// // context.arc(this.width / 2, this.height / 2, outerRadius, 0, Math.PI * 2);
if (hasRipper) {
BezierEllipse2(context, this.width / 2, this.height / 2, outerRadius, outerRadius / 2);
context.fillStyle = rgbToRgba('rgb(255, 100, 100)', alp);
context.fill();
context.beginPath();
// context.arc(this.width / 2, this.height / 2, outerRadius2, 0, Math.PI * 2);
BezierEllipse2(context, this.width / 2, this.height / 2, outerRadius2, outerRadius2 / 2);
context.fillStyle = rgbToRgba('rgb(255, 100, 100)', alp);
context.fill();
// draw inner circle
context.beginPath();
BezierEllipse2(
context,
this.width / 2,
this.height / 2,
(size / 2) * 0.2,
((size / 2) * 0.2) / 2
);
// context.arc(this.width / 2, this.height / 2, (size / 2) * 1 * 0.1, 0, Math.PI * 2);
// context.fillStyle = 'rgba(255, 100, 100, 1)';
context.fillStyle = rgbToRgba('rgb(255, 100, 100)', 1);
context.fill();
}
this.data = context.getImageData(0, 0, this.width, this.height).data;
map.triggerRepaint();
return true;
},
};
return pulsingDot;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,64 @@
/**
* Created by jiangzhixiong on 2024/05/23
*/
import React, { forwardRef, useContext, useImperativeHandle } from 'react'
import { Source, Layer, SourceProps } from 'react-map-gl';
import { ConfigProvider } from '@zhst/meta'
import {clusterLayer, clusterCountLayer, unclusteredPointLayer} from './layers';
const { ConfigContext } = ConfigProvider
export interface ClusterProps extends SourceProps {
prefixCls?: string;
}
export interface ClusterRefProps {
}
const Cluster = forwardRef<ClusterRefProps, ClusterProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
...rest
} = props
const { getPrefixCls } = useContext(ConfigContext)
const componentName = getPrefixCls('map-cluster', customizePrefixCls);
// const onClick = event => {
// const feature = event.features[0];
// const clusterId = feature.properties.cluster_id;
// const mapboxSource = mapRef.current.getSource('earthquakes') as GeoJSONSource;
// mapboxSource.getClusterExpansionZoom(clusterId, (err, zoom) => {
// if (err) {
// return;
// }
// mapRef.current.easeTo({
// center: feature.geometry.coordinates,
// zoom,
// duration: 500
// });
// });
// };
useImperativeHandle(ref, () => ({
}))
return (
<Source
type="geojson"
cluster={true}
clusterMaxZoom={14}
clusterRadius={50}
{...rest}
>
<Layer {...clusterLayer} />
<Layer {...clusterCountLayer} />
<Layer {...unclusteredPointLayer} />
</Source>
)
})
export default Cluster

View File

@ -0,0 +1,5 @@
/**
* Created by jiangzhixiong on 2024/05/23
*/
export { default as Clusters } from './Clusters'
// export type { ClustersProps, ClustersRefProps } from './Clusters'

View File

@ -0,0 +1,37 @@
import type {LayerProps} from 'react-map-gl';
export const clusterLayer: LayerProps = {
id: 'clusters',
type: 'circle',
source: 'earthquakes',
filter: ['has', 'point_count'],
paint: {
'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 100, '#f1f075', 750, '#f28cb1'],
'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40]
}
};
export const clusterCountLayer: LayerProps = {
id: 'cluster-count',
type: 'symbol',
source: 'earthquakes',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
'text-size': 12
}
};
export const unclusteredPointLayer: LayerProps = {
id: 'unclustered-point',
type: 'circle',
source: 'earthquakes',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#11b4da',
'circle-radius': 4,
'circle-stroke-width': 1,
'circle-stroke-color': '#fff'
}
};

View File

@ -1,18 +1,19 @@
/**
* Created by jiangzhixiong on 2024/05/21
*/
import { forwardRef, useImperativeHandle, useRef, } from 'react'
import MapboxDraw, { MapboxDrawOptions } from '@mapbox/mapbox-gl-draw';
import MapboxDraw from '@mapbox/mapbox-gl-draw';
import {
CircleMode,
DragCircleMode,
DirectMode,
SimpleSelectMode
// @ts-ignore
} from 'mapbox-gl-draw-circle'
// @ts-ignore
import drawRectMode from 'mapbox-gl-draw-rectangle-mode'
// @ts-ignore
import drawStaticMode from '@mapbox/mapbox-gl-draw-static-mode'
// import * as MapboxDrawGeodesic from 'mapbox-gl-draw-geodesic';
import { useControl } from 'react-map-gl';
import type { ControlPosition } from 'react-map-gl';
import { MapContextValue } from 'react-map-gl/dist/esm/components/map';
@ -32,7 +33,7 @@ export interface DrawControlRefProps {
}
const DrawControl = forwardRef<DrawControlRefProps, DrawControlProps>((props, ref) => {
const drawRef = useRef<MapboxDrawOptions>(null)
const drawRef = useRef<DrawControlRefProps['drawer']>(null)
useControl<MapboxDraw>(
() => {
@ -51,16 +52,15 @@ const DrawControl = forwardRef<DrawControlRefProps, DrawControlProps>((props, re
...props
}
)
// 修改模式
// drawIns?.changeMode('simple_select');
// @ts-ignore
drawRef.current = draw
return draw
},
(context: MapContextValue) => {
const { map } = context
map.on('draw.create', props.onCreate);
map.on('draw.update', props.onUpdate);
map.on('draw.delete', props.onDelete);
map.on('draw.create', e => props.onCreate?.(e));
map.on('draw.update', e => props.onUpdate?.(e));
map.on('draw.delete', e => props.onDelete?.(e));
},
(context: MapContextValue) => {
const { map } = context
@ -74,7 +74,7 @@ const DrawControl = forwardRef<DrawControlRefProps, DrawControlProps>((props, re
);
useImperativeHandle(ref, () => ({
drawer: drawRef.current
drawer: drawRef.current!
}))
return null;

View File

@ -0,0 +1,50 @@
/**
* Created by jiangzhixiong on 2024/05/23
*/
import React, { forwardRef, useContext, useImperativeHandle } from 'react'
import {
Marker as MapboxMarker,
MarkerProps as MapboxMarkerProps
} from 'react-map-gl'
import { ConfigProvider, Image } from '@zhst/meta'
import markerPic from '../../assets/icons/marker_monitor.png'
const { ConfigContext } = ConfigProvider
export interface MarkerProps extends MapboxMarkerProps {
prefixCls?: string;
key: string;
}
export interface MarkerRefProps {
}
const Marker = forwardRef<MarkerRefProps, MarkerProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
key,
longitude,
latitude,
onClick
} = props
const { getPrefixCls } = useContext(ConfigContext)
const componentName = getPrefixCls('-map-marker', customizePrefixCls);
useImperativeHandle(ref, () => ({
}))
return (
<MapboxMarker
key={key}
longitude={Number(longitude)}
latitude={Number(latitude)}
anchor="bottom"
onClick={onClick}
>
<Image src={markerPic} preview={false} />
</MapboxMarker>
)
})
export default Marker

View File

@ -0,0 +1,8 @@
/**
* Created by jiangzhixiong on 2024/05/23
*/
import Marker from './Marker'
export type { MarkerProps, MarkerRefProps } from './Marker'
export default Marker

View File

@ -0,0 +1,21 @@
import * as React from 'react';
const ICON = `M20.2,15.7L20.2,15.7c1.1-1.6,1.8-3.6,1.8-5.7c0-5.6-4.5-10-10-10S2,4.5,2,10c0,2,0.6,3.9,1.6,5.4c0,0.1,0.1,0.2,0.2,0.3
c0,0,0.1,0.1,0.1,0.2c0.2,0.3,0.4,0.6,0.7,0.9c2.6,3.1,7.4,7.6,7.4,7.6s4.8-4.5,7.4-7.5c0.2-0.3,0.5-0.6,0.7-0.9
C20.1,15.8,20.2,15.8,20.2,15.7z`;
const pinStyle = {
cursor: 'pointer',
fill: '#d00',
stroke: 'none'
};
function Pin({size = 20}) {
return (
<svg height={size} viewBox="0 0 24 24" style={pinStyle}>
<path d={ICON} />
</svg>
);
}
export default React.memo(Pin);

View File

@ -0,0 +1,49 @@
/**
* Created by jiangzhixiong on 2024/05/23
*/
import React, { forwardRef, useContext, useImperativeHandle } from 'react'
import {
PopUp as MapboxPopUp,
PopupProps as MapboxPopupProps
} from 'react-map-gl'
import { ConfigProvider } from '@zhst/meta'
const { ConfigContext } = ConfigProvider
export interface PopUpProps extends MapboxPopupProps {
prefixCls?: string;
size?: number;
}
export interface PopUpRefProps {
}
const PopUp = forwardRef<PopUpRefProps, PopUpProps>((props, ref) => {
const {
longitude,
latitude,
onClose,
prefixCls: customizePrefixCls
} = props
const { getPrefixCls } = useContext(ConfigContext)
const componentName = getPrefixCls('map-popup', customizePrefixCls);
useImperativeHandle(ref, () => ({
}))
return (
<MapboxPopUp
anchor="top"
longitude={Number(longitude)}
latitude={Number(latitude)}
onClose={onClose}
>
<div className={componentName}>
popup
</div>
</MapboxPopUp>
)
})
export default PopUp

View File

@ -0,0 +1,5 @@
/**
* Created by jiangzhixiong on 2024/05/23
*/
export { default as PopUp } from './PopUp'
export type { PopUpProps, PopUpRefProps } from './PopUp'

View File

@ -43,16 +43,14 @@ const Tools = forwardRef<ToolsRefProps, ToolsProps>((props, ref) => {
<div className={componentName}>
<ul className={classNames(`${componentName}-navs`, { [`${componentName}-navs_active`]: state })}>
{buttonList.map((item) => (
<>
<li onClick={item.onClick} className={classNames(`${componentName}-navs-item`)} key={item.key}>
{typeof item.icon === 'string' ? (
<IconFont className={classNames(`${componentName}-navs-item-icon`)} size={24} icon={item.icon} />
) : (
item.icon
)}
{item.label}
</li>
</>
<li onClick={item.onClick} className={classNames(`${componentName}-navs-item`)} key={item.key}>
{typeof item.icon === 'string' ? (
<IconFont className={classNames(`${componentName}-navs-item-icon`)} size={24} icon={item.icon} />
) : (
item.icon
)}
{item.label}
</li>
))}
</ul>
<Button

View File

@ -0,0 +1,46 @@
[
{"name":"Lafayette (LAFY)","coordinates":[-122.123801,37.893394]},
{"name":"12th St. Oakland City Center (12TH)","coordinates":[-122.271604,37.803664]},
{"name":"16th St. Mission (16TH)","coordinates":[-122.419694,37.765062]},
{"name":"19th St. Oakland (19TH)","coordinates":[-122.269029,37.80787]},
{"name":"24th St. Mission (24TH)","coordinates":[-122.418466,37.752254]},
{"name":"Ashby (ASHB)","coordinates":[-122.26978,37.853024]},
{"name":"Balboa Park (BALB)","coordinates":[-122.447414,37.721981]},
{"name":"Bay Fair (BAYF)","coordinates":[-122.126871,37.697185]},
{"name":"Castro Valley (CAST)","coordinates":[-122.075567,37.690754]},
{"name":"Civic Center/UN Plaza (CIVC)","coordinates":[-122.413756,37.779528]},
{"name":"Colma (COLM)","coordinates":[-122.466233,37.684638]},
{"name":"Coliseum/Oakland Airport (COLS)","coordinates":[-122.197273,37.754006]},
{"name":"Concord (CONC)","coordinates":[-122.029095,37.973737]},
{"name":"Daly City (DALY)","coordinates":[-122.469081,37.706121]},
{"name":"Downtown Berkeley (DBRK)","coordinates":[-122.268045,37.869867]},
{"name":"El Cerrito del Norte (DELN)","coordinates":[-122.317269,37.925655]},
{"name":"Dublin/Pleasanton (DUBL)","coordinates":[-121.900367,37.701695]},
{"name":"Embarcadero (EMBR)","coordinates":[-122.396742,37.792976]},
{"name":"Fremont (FRMT)","coordinates":[-121.9764,37.557355]},
{"name":"Fruitvale (FTVL)","coordinates":[-122.224274,37.774963]},
{"name":"Glen Park (GLEN)","coordinates":[-122.434092,37.732921]},
{"name":"Hayward (HAYW)","coordinates":[-122.087967,37.670399]},
{"name":"Lake Merritt (LAKE)","coordinates":[-122.265609,37.797484]},
{"name":"MacArthur (MCAR)","coordinates":[-122.267227,37.828415]},
{"name":"Millbrae (MLBR)","coordinates":[-122.38666,37.599787]},
{"name":"Montgomery St. (MONT)","coordinates":[-122.401407,37.789256]},
{"name":"North Berkeley (NBRK)","coordinates":[-122.283451,37.87404]},
{"name":"North Concord/Martinez (NCON)","coordinates":[-122.024597,38.003275]},
{"name":"Orinda (ORIN)","coordinates":[-122.183791,37.878361]},
{"name":"Pleasant Hill/Contra Costa Centre (PHIL)","coordinates":[-122.056013,37.928403]},
{"name":"Pittsburg/Bay Point (PITT)","coordinates":[-121.945154,38.018914]},
{"name":"El Cerrito Plaza (PLZA)","coordinates":[-122.299272,37.903059]},
{"name":"Powell St. (POWL)","coordinates":[-122.406857,37.784991]},
{"name":"Richmond (RICH)","coordinates":[-122.353165,37.936887]},
{"name":"Rockridge (ROCK)","coordinates":[-122.251793,37.844601]},
{"name":"San Leandro (SANL)","coordinates":[-122.161311,37.722619]},
{"name":"San Bruno (SBRN)","coordinates":[-122.416038,37.637753]},
{"name":"San Francisco Int'l Airport (SFIA)","coordinates":[-122.392612,37.616035]},
{"name":"South Hayward (SHAY)","coordinates":[-122.057551,37.6348]},
{"name":"South San Francisco (SSAN)","coordinates":[-122.444116,37.664174]},
{"name":"Union City (UCTY)","coordinates":[-122.017867,37.591208]},
{"name":"Walnut Creek (WCRK)","coordinates":[-122.067423,37.905628]},
{"name":"West Dublin/Pleasanton (WDUB)","coordinates":[-121.928099,37.699759]},
{"name":"West Oakland (WOAK)","coordinates":[-122.294582,37.804675]}
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,25 +1,58 @@
import React, { useRef } from 'react';
import React, { useRef, useEffect, useState } from 'react';
import { MapBox } from '@zhst/map';
import axios from 'axios';
const demo = () => {
const [markerData, setMarkerData] = useState([])
const mapRef = useRef(null);
// 初始化
const handleMapLoad = (e: mapboxgl.MapboxEvent<undefined>) => {
const map = e.target;
if (!map) {
return;
}
if (!map) return
map.flyTo({
center: [120,30],
zoom: map?.getMaxZoom(),
// center: [120,30],
// zoom: map?.getMaxZoom(),
});
};
const getData = async () => {
let res = await axios({
method: 'post',
url: 'http://10.0.0.120:30003/singer.DeviceService/ListCamera',
headers: {
Authorization: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTY3MTI5MTAsImp0aSI6IjEwOTYiLCJpYXQiOjE3MTY0NTM3MTAsInVzZXJJZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsImRhdGFSaWdodCI6MiwiY2FtZXJhUmlnaHQiOjEsImdwdVJpZ2h0IjoxLCJ1c2VybGVhZGVySWQiOjAsIm9yZ2FuaXphdGlvbklkIjoxLCJyb2xlSWQiOjF9.6s73alRI0iB-5oT-GcQH8lZSiScPTULk14xxz2Aak6k'
},
data: {"labelData":[],"filter":{"realtimeProcessingFilter":0,"cameraFilter":[{"opt":"ORNOT","cameraOpt":"CAMERAOPT_TYPE","value":"100"}]},"maxResults":50}
});
if (res.status === 200) {
setMarkerData(res.data.cameras.map(item => ({
key: item.id,
title: item.name,
population: item.id,
status: item.status,
latitude: item.latitude,
longitude: item.longitude
})))
}
}
useEffect(() => {
getData()
}, [])
return (
<MapBox
onLoad={handleMapLoad}
ref={mapRef}
onDrawCreate={e => console.log('Create', e)}
onDrawDelete={e => console.log('Delete', e)}
onDrawUpdate={e => console.log('Update', e)}
width='100%'
height='100vh'
markerData={markerData}
/>
);
};

View File

@ -1,18 +0,0 @@
import { CSSProperties } from "react";
import { MapboxMap, MapRef, MapStyle } from "react-map-gl";
export interface MapProps extends Partial<MapboxMap> {
mapboxAccessToken?: string; //token
minZoom?: number; //最小层级
maxZoom?: number; //最大层级
mapStyle?: MapStyle; //地图样式
height?: number | string;
width?: string | number;
onLoad?: (e: mapboxgl.MapboxEvent<undefined>) => void;
mapRef?: MapRef;
style?: CSSProperties;
children?: JSX.Element | JSX.Element[] | Array<JSX.Element | undefined>;
mapCenter?: {
longitude: number, latitude: number
}
}

View File

@ -1,7 +1,7 @@
export const mapboxAccessToken =
'pk.eyJ1IjoiZGluZ2xpMTIzIiwiYSI6ImNra204ODhjczBobTgyeHJ6MmJpZHMxNWgifQ.NbKrXh_hb2gvjr5CEMDnyQ';
export const MAP_CENTER = {
export const MAP_CENTER = {
longitude: 120.2667694313269,
latitude: 30.180942826533766,
}; //地图中心

View File

@ -0,0 +1,81 @@
import * as React from 'react';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import LeftOutlined from '@ant-design/icons/LeftOutlined';
import RightOutlined from '@ant-design/icons/RightOutlined';
import RotateLeftOutlined from '@ant-design/icons/RotateLeftOutlined';
import RotateRightOutlined from '@ant-design/icons/RotateRightOutlined';
import SwapOutlined from '@ant-design/icons/SwapOutlined';
import ZoomInOutlined from '@ant-design/icons/ZoomInOutlined';
import ZoomOutOutlined from '@ant-design/icons/ZoomOutOutlined';
import classNames from 'classnames';
import RcImage from 'rc-image';
import type { GroupConsumerProps } from 'rc-image/lib/PreviewGroup';
import { useZIndex } from '../_util/hooks/useZIndex';
import { getTransitionName } from '../_util/motion';
import { ConfigContext } from '../config-provider';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useStyle from './style';
export const icons = {
rotateLeft: <RotateLeftOutlined />,
rotateRight: <RotateRightOutlined />,
zoomIn: <ZoomInOutlined />,
zoomOut: <ZoomOutOutlined />,
close: <CloseOutlined />,
left: <LeftOutlined />,
right: <RightOutlined />,
flipX: <SwapOutlined />,
flipY: <SwapOutlined rotate={90} />,
};
const InternalPreviewGroup: React.FC<GroupConsumerProps> = ({
previewPrefixCls: customizePrefixCls,
preview,
...otherProps
}) => {
const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('image', customizePrefixCls);
const previewPrefixCls = `${prefixCls}-preview`;
const rootPrefixCls = getPrefixCls();
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
const [zIndex] = useZIndex(
'ImagePreview',
typeof preview === 'object' ? preview.zIndex : undefined,
);
const mergedPreview = React.useMemo<GroupConsumerProps['preview']>(() => {
if (preview === false) {
return preview;
}
const _preview = typeof preview === 'object' ? preview : {};
const mergedRootClassName = classNames(
hashId,
cssVarCls,
rootCls,
_preview.rootClassName ?? '',
);
return {
..._preview,
transitionName: getTransitionName(rootPrefixCls, 'zoom', _preview.transitionName),
maskTransitionName: getTransitionName(rootPrefixCls, 'fade', _preview.maskTransitionName),
rootClassName: mergedRootClassName,
zIndex,
};
}, [preview]);
return wrapCSSVar(
<RcImage.PreviewGroup
preview={mergedPreview}
previewPrefixCls={previewPrefixCls}
icons={icons}
{...otherProps}
/>,
);
};
export default InternalPreviewGroup;

View File

@ -0,0 +1,826 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders components/image/demo/basic.tsx extend context correctly 1`] = `
<div
class="ant-image"
style="width: 200px;"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/basic.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/component-token.tsx extend context correctly 1`] = `
Array [
<div
class="ant-image"
style="width: 150px;"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
width="150"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
<div
class="ant-image"
style="width: 150px;"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="150"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
]
`;
exports[`renders components/image/demo/component-token.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/controlled-preview.tsx extend context correctly 1`] = `
Array [
<div>
scaleStep:
<div
class="ant-input-number ant-input-number-outlined"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
aria-valuemax="5"
aria-valuemin="0.1"
aria-valuenow="0.5"
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="0.1"
value="0.5"
/>
</div>
</div>
</div>,
<br />,
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
show image preview
</span>
</button>,
<div
class="ant-image"
style="width: 200px;"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/blur,r_50,s_50/quality,q_1/resize,m_mfit,h_200,w_200"
style="display: none;"
width="200"
/>
<div
class="ant-image-mask"
style="display: none;"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
]
`;
exports[`renders components/image/demo/controlled-preview.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/fallback.tsx extend context correctly 1`] = `
<div
class="ant-image"
style="width: 200px; height: 200px;"
>
<img
class="ant-image-img"
height="200"
src="error"
style="height: 200px;"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/fallback.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/imageRender.tsx extend context correctly 1`] = `
<div
class="ant-image"
style="width: 200px;"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/imageRender.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/nested.tsx extend context correctly 1`] = `
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
showModal
</span>
</button>
`;
exports[`renders components/image/demo/nested.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/placeholder.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center"
style="column-gap: 12px; row-gap: 12px;"
>
<div
class="ant-space-item"
>
<div
class="ant-image"
style="width: 200px;"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?undefined"
width="200"
/>
<div
aria-hidden="true"
class="ant-image-placeholder"
>
<div
class="ant-image"
style="width: 200px;"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/blur,r_50,s_50/quality,q_1/resize,m_mfit,h_200,w_200"
width="200"
/>
</div>
</div>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Reload
</span>
</button>
</div>
</div>
`;
exports[`renders components/image/demo/placeholder.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/preview-group.tsx extend context correctly 1`] = `
Array [
<div
class="ant-image"
style="width: 200px;"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
<div
class="ant-image"
style="width: 200px;"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
]
`;
exports[`renders components/image/demo/preview-group.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/preview-group-top-progress.tsx extend context correctly 1`] = `
Array [
<div
class="ant-image"
style="width: 150px;"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
width="150"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
<div
class="ant-image"
style="width: 150px;"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
width="150"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
<div
class="ant-image"
style="width: 150px;"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="150"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
]
`;
exports[`renders components/image/demo/preview-group-top-progress.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/preview-group-visible.tsx extend context correctly 1`] = `
<div
class="ant-image"
style="width: 200px;"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/preview-group-visible.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/preview-mask.tsx extend context correctly 1`] = `
<div
class="ant-image"
style="width: 96px;"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="96"
/>
<div
class="ant-image-mask customize-mask"
>
<div
class="ant-space ant-space-vertical ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
>
<div
class="ant-space-item"
>
<span
aria-label="zoom-in"
class="anticon anticon-zoom-in"
role="img"
>
<svg
aria-hidden="true"
data-icon="zoom-in"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M637 443H519V309c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v134H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h118v134c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V519h118c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
/>
</svg>
</span>
</div>
<div
class="ant-space-item"
>
示例
</div>
</div>
</div>
</div>
`;
exports[`renders components/image/demo/preview-mask.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/previewSrc.tsx extend context correctly 1`] = `
<div
class="ant-image"
style="width: 200px;"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/blur,r_50,s_50/quality,q_1/resize,m_mfit,h_200,w_200"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/previewSrc.tsx extend context correctly 2`] = `[]`;
exports[`renders components/image/demo/toolbarRender.tsx extend context correctly 1`] = `
<div
class="ant-image"
style="width: 200px;"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/toolbarRender.tsx extend context correctly 2`] = `[]`;

View File

@ -0,0 +1,801 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders components/image/demo/basic.tsx correctly 1`] = `
<div
class="ant-image"
style="width:200px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/component-token.tsx correctly 1`] = `
Array [
<div
class="ant-image"
style="width:150px"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
width="150"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
<div
class="ant-image"
style="width:150px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="150"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
]
`;
exports[`renders components/image/demo/controlled-preview.tsx correctly 1`] = `
Array [
<div>
scaleStep:
<!-- -->
<div
class="ant-input-number ant-input-number-outlined"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
aria-valuemax="5"
aria-valuemin="0.1"
aria-valuenow="0.5"
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="0.1"
value="0.5"
/>
</div>
</div>
</div>,
<br />,
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
show image preview
</span>
</button>,
<div
class="ant-image"
style="width:200px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/blur,r_50,s_50/quality,q_1/resize,m_mfit,h_200,w_200"
style="display:none"
width="200"
/>
<div
class="ant-image-mask"
style="display:none"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
]
`;
exports[`renders components/image/demo/fallback.tsx correctly 1`] = `
<div
class="ant-image"
style="width:200px;height:200px"
>
<img
class="ant-image-img"
height="200"
src="error"
style="height:200px"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/imageRender.tsx correctly 1`] = `
<div
class="ant-image"
style="width:200px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/nested.tsx correctly 1`] = `
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
showModal
</span>
</button>
`;
exports[`renders components/image/demo/placeholder.tsx correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center"
style="column-gap:12px;row-gap:12px"
>
<div
class="ant-space-item"
>
<div
class="ant-image"
style="width:200px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?undefined"
width="200"
/>
<div
aria-hidden="true"
class="ant-image-placeholder"
>
<div
class="ant-image"
style="width:200px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/blur,r_50,s_50/quality,q_1/resize,m_mfit,h_200,w_200"
width="200"
/>
</div>
</div>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Reload
</span>
</button>
</div>
</div>
`;
exports[`renders components/image/demo/preview-group.tsx correctly 1`] = `
Array [
<div
class="ant-image"
style="width:200px"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
<div
class="ant-image"
style="width:200px"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
]
`;
exports[`renders components/image/demo/preview-group-top-progress.tsx correctly 1`] = `
Array [
<div
class="ant-image"
style="width:150px"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
width="150"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
<div
class="ant-image"
style="width:150px"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
width="150"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
<div
class="ant-image"
style="width:150px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="150"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>,
]
`;
exports[`renders components/image/demo/preview-group-visible.tsx correctly 1`] = `
<div
class="ant-image"
style="width:200px"
>
<img
class="ant-image-img"
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/preview-mask.tsx correctly 1`] = `
<div
class="ant-image"
style="width:96px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="96"
/>
<div
class="ant-image-mask customize-mask"
>
<div
class="ant-space ant-space-vertical ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
>
<div
class="ant-space-item"
>
<span
aria-label="zoom-in"
class="anticon anticon-zoom-in"
role="img"
>
<svg
aria-hidden="true"
data-icon="zoom-in"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M637 443H519V309c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v134H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h118v134c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V519h118c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
/>
</svg>
</span>
</div>
<div
class="ant-space-item"
>
示例
</div>
</div>
</div>
</div>
`;
exports[`renders components/image/demo/previewSrc.tsx correctly 1`] = `
<div
class="ant-image"
style="width:200px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/blur,r_50,s_50/quality,q_1/resize,m_mfit,h_200,w_200"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;
exports[`renders components/image/demo/toolbarRender.tsx correctly 1`] = `
<div
class="ant-image"
style="width:200px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width="200"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;

View File

@ -0,0 +1,326 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Image Default Group preview props 1`] = `
<body>
<div>
<div
class="ant-image"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
</div>
<div
class="ant-image-preview-operations-wrapper ant-fade-appear ant-fade-appear-start ant-fade"
>
<button
class="ant-image-preview-close"
>
<span
aria-label="close"
class="anticon anticon-close"
role="img"
>
<svg
aria-hidden="true"
data-icon="close"
fill="currentColor"
fill-rule="evenodd"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z"
/>
</svg>
</span>
</button>
<div
class="ant-image-preview-footer"
>
<div
class="ant-image-preview-progress"
>
1 / 1
</div>
<div
class="ant-image-preview-operations"
>
<div
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-flipY"
>
<span
aria-label="swap"
class="anticon anticon-swap"
role="img"
>
<svg
aria-hidden="true"
data-icon="swap"
fill="currentColor"
focusable="false"
height="1em"
style="transform: rotate(90deg);"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M847.9 592H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h605.2L612.9 851c-4.1 5.2-.4 13 6.3 13h72.5c4.9 0 9.5-2.2 12.6-6.1l168.8-214.1c16.5-21 1.6-51.8-25.2-51.8zM872 356H266.8l144.3-183c4.1-5.2.4-13-6.3-13h-72.5c-4.9 0-9.5 2.2-12.6 6.1L150.9 380.2c-16.5 21-1.6 51.8 25.1 51.8h696c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
/>
</svg>
</span>
</div>
<div
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-flipX"
>
<span
aria-label="swap"
class="anticon anticon-swap"
role="img"
>
<svg
aria-hidden="true"
data-icon="swap"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M847.9 592H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h605.2L612.9 851c-4.1 5.2-.4 13 6.3 13h72.5c4.9 0 9.5-2.2 12.6-6.1l168.8-214.1c16.5-21 1.6-51.8-25.2-51.8zM872 356H266.8l144.3-183c4.1-5.2.4-13-6.3-13h-72.5c-4.9 0-9.5 2.2-12.6 6.1L150.9 380.2c-16.5 21-1.6 51.8 25.1 51.8h696c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
/>
</svg>
</span>
</div>
<div
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-rotateLeft"
>
<span
aria-label="rotate-left"
class="anticon anticon-rotate-left"
role="img"
>
<svg
aria-hidden="true"
data-icon="rotate-left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<defs>
<style />
</defs>
<path
d="M672 418H144c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32zm-44 402H188V494h440v326z"
/>
<path
d="M819.3 328.5c-78.8-100.7-196-153.6-314.6-154.2l-.2-64c0-6.5-7.6-10.1-12.6-6.1l-128 101c-4 3.1-3.9 9.1 0 12.3L492 318.6c5.1 4 12.7.4 12.6-6.1v-63.9c12.9.1 25.9.9 38.8 2.5 42.1 5.2 82.1 18.2 119 38.7 38.1 21.2 71.2 49.7 98.4 84.3 27.1 34.7 46.7 73.7 58.1 115.8a325.95 325.95 0 016.5 140.9h74.9c14.8-103.6-11.3-213-81-302.3z"
/>
</svg>
</span>
</div>
<div
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-rotateRight"
>
<span
aria-label="rotate-right"
class="anticon anticon-rotate-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="rotate-right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<defs>
<style />
</defs>
<path
d="M480.5 251.2c13-1.6 25.9-2.4 38.8-2.5v63.9c0 6.5 7.5 10.1 12.6 6.1L660 217.6c4-3.2 4-9.2 0-12.3l-128-101c-5.1-4-12.6-.4-12.6 6.1l-.2 64c-118.6.5-235.8 53.4-314.6 154.2A399.75 399.75 0 00123.5 631h74.9c-.9-5.3-1.7-10.7-2.4-16.1-5.1-42.1-2.1-84.1 8.9-124.8 11.4-42.2 31-81.1 58.1-115.8 27.2-34.7 60.3-63.2 98.4-84.3 37-20.6 76.9-33.6 119.1-38.8z"
/>
<path
d="M880 418H352c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32zm-44 402H396V494h440v326z"
/>
</svg>
</span>
</div>
<div
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-zoomOut ant-image-preview-operations-operation-disabled"
>
<span
aria-label="zoom-out"
class="anticon anticon-zoom-out"
role="img"
>
<svg
aria-hidden="true"
data-icon="zoom-out"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M637 443H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h312c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
/>
</svg>
</span>
</div>
<div
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-zoomIn"
>
<span
aria-label="zoom-in"
class="anticon anticon-zoom-in"
role="img"
>
<svg
aria-hidden="true"
data-icon="zoom-in"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M637 443H519V309c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v134H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h118v134c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V519h118c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
/>
</svg>
</span>
</div>
</div>
</div>
</div>
<div>
<div
class="ant-image-preview-root"
>
<div
class="ant-image-preview-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
class="ant-image-preview-wrap"
tabindex="-1"
>
<div
aria-modal="true"
class="ant-image-preview ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
>
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden; outline: none;"
tabindex="0"
/>
<div
style="outline: none;"
tabindex="-1"
>
<div
class="ant-image-preview-content"
>
<div
class="ant-image-preview-body"
>
<div
class="ant-image-preview-img-wrapper"
>
<img
class="ant-image-preview-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
style="transform: translate3d(0px, 0px, 0) scale3d(1, 1, 1) rotate(0deg);"
/>
</div>
</div>
</div>
</div>
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden; outline: none;"
tabindex="0"
/>
</div>
</div>
</div>
</div>
</body>
`;
exports[`Image rtl render component should be rendered correctly in RTL direction 1`] = `
<div
class="ant-image"
>
<img
class="ant-image-img"
/>
<div
class="ant-image-mask"
>
<div
class="ant-image-mask-info"
>
<span
aria-label="eye"
class="anticon anticon-eye"
role="img"
>
<svg
aria-hidden="true"
data-icon="eye"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
/>
</svg>
</span>
Preview
</div>
</div>
</div>
`;

View File

@ -0,0 +1,3 @@
import { extendTest } from '../../../tests/shared/demoTest';
extendTest('image');

View File

@ -0,0 +1,3 @@
import demoTest from '../../../tests/shared/demoTest';
demoTest('image');

View File

@ -0,0 +1,5 @@
import { imageDemoTest } from '../../../tests/shared/imageTest';
describe('Image image', () => {
imageDemoTest('image');
});

View File

@ -0,0 +1,194 @@
import React from 'react';
import { Modal } from 'antd';
import Image from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
const src = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png';
describe('Image', () => {
mountTest(Image);
rtlTest(Image);
it('Image preview props set false', () => {
const { container } = render(<Image src={src} preview={false} />);
fireEvent.click(container.querySelector('.ant-image')!);
expect(container.querySelector('.ant-image-preview-root')).toBe(null);
});
it('Group preview props set false', () => {
const { container } = render(
<Image.PreviewGroup preview={false}>
<Image src={src} />
</Image.PreviewGroup>,
);
fireEvent.click(container.querySelector('.ant-image')!);
expect(container.querySelector('.ant-image-preview-root')).toBe(null);
});
it('Default preview props', () => {
const { container, baseElement } = render(<Image src={src} preview={{ visible: true }} />);
fireEvent.click(container.querySelector('.ant-image')!);
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('ant-fade');
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('ant-zoom');
});
it('Default Group preview props', () => {
const { container, baseElement } = render(
<Image.PreviewGroup preview={{ visible: true }}>
<Image src={src} />
</Image.PreviewGroup>,
);
fireEvent.click(container.querySelector('.ant-image')!);
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('ant-fade');
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('ant-zoom');
expect(baseElement).toMatchSnapshot();
});
it('Customize preview props', () => {
const { container, baseElement } = render(
<Image
src={src}
preview={{
visible: true,
transitionName: 'abc',
maskTransitionName: 'def',
getContainer: false,
}}
/>,
);
fireEvent.click(container.querySelector('.ant-image')!);
expect(container.querySelector('.ant-image-preview-root')).not.toBe(null);
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('abc');
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('def');
});
it('Customize Group preview props', () => {
const { container, baseElement } = render(
<Image.PreviewGroup
preview={{ visible: true, transitionName: 'abc', maskTransitionName: 'def' }}
>
<Image src={src} />
</Image.PreviewGroup>,
);
fireEvent.click(container.querySelector('.ant-image')!);
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('abc');
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('def');
});
it('ConfigProvider getPopupContainer', () => {
const { container, baseElement } = render(
<>
<div className="container" />
<ConfigProvider getPopupContainer={() => document.querySelector('.container')!}>
<Image src={src} />
</ConfigProvider>
</>,
);
fireEvent.click(container.querySelector('.ant-image')!);
expect(baseElement.querySelector('.container')?.children.length).not.toBe(0);
});
it('Preview forceRender props', async () => {
const onLoadCb = jest.fn();
const PreviewImage: React.FC = () => (
<Image
preview={{
visible: false,
src,
forceRender: true,
}}
/>
);
const { baseElement } = render(<PreviewImage />);
expect(baseElement.querySelector('.ant-image-preview-root')).not.toBe(null);
baseElement.querySelector('.ant-image-preview-img')?.addEventListener('load', onLoadCb);
fireEvent.load(baseElement.querySelector('.ant-image-preview-img')!);
expect(onLoadCb).toHaveBeenCalled();
});
it('Preview should support rootClassName', () => {
const { container, baseElement } = render(
<Image.PreviewGroup preview={{ visible: true, rootClassName: 'test-root-class' }}>
<Image src={src} />
</Image.PreviewGroup>,
);
fireEvent.click(container.querySelector('.ant-image')!);
expect(baseElement.querySelector('.test-root-class')).toBeTruthy();
});
it('Image.PreviewGroup preview in a nested modal where z-index Settings should be correct', () => {
const App = () => (
<Modal open>
<Modal open>
<Modal open>
<Image
width={200}
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
preview={{
rootClassName: 'test-image-preview-class',
}}
/>
<Image.PreviewGroup
preview={{
rootClassName: 'test-image-preview-group-class',
}}
>
<Image
width={200}
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
/>
<Image
width={200}
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
/>
</Image.PreviewGroup>
</Modal>
</Modal>
</Modal>
);
const { baseElement } = render(<App />);
fireEvent.click(baseElement.querySelector('.ant-image')!);
expect(
(
baseElement.querySelector(
'.test-image-preview-class .ant-image-preview-wrap',
) as HTMLElement
).style.zIndex,
).toBe('1301');
expect(
(
baseElement.querySelector(
'.test-image-preview-class.ant-image-preview-operations-wrapper',
) as HTMLElement
).style.zIndex,
).toBe('1302');
fireEvent.click(baseElement.querySelectorAll('.ant-image')[1]!);
expect(
(
baseElement.querySelector(
'.test-image-preview-group-class .ant-image-preview-wrap',
) as HTMLElement
).style.zIndex,
).toBe('1301');
expect(
(
baseElement.querySelector(
'.test-image-preview-group-class.ant-image-preview-operations-wrapper',
) as HTMLElement
).style.zIndex,
).toBe('1302');
});
});

View File

@ -0,0 +1,7 @@
## zh-CN
单击图像可以放大显示。
## en-US
Click the image to zoom in.

View File

@ -0,0 +1,11 @@
import React from 'react';
import { Image } from 'antd';
const App: React.FC = () => (
<Image
width={200}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
);
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
自定义组件 Token。
## en-US
Custom component token.

View File

@ -0,0 +1,31 @@
import React from 'react';
import { ConfigProvider, Image } from 'antd';
const App: React.FC = () => (
<ConfigProvider
theme={{
components: {
Image: {
previewOperationSize: 20,
previewOperationColor: '#222',
previewOperationColorDisabled: '#b20000',
},
},
}}
>
<Image.PreviewGroup
preview={{ countRender: (current, total) => `当前 ${current} / 总计 ${total}` }}
>
<Image
width={150}
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
/>
<Image
width={150}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
</Image.PreviewGroup>
</ConfigProvider>
);
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
可以使预览受控。
## en-US
You can make preview controlled.

View File

@ -0,0 +1,41 @@
import React, { useState } from 'react';
import { Button, Image, InputNumber } from 'antd';
const App: React.FC = () => {
const [visible, setVisible] = useState(false);
const [scaleStep, setScaleStep] = useState(0.5);
return (
<>
<div>
scaleStep:{' '}
<InputNumber
min={0.1}
max={5}
defaultValue={0.5}
step={0.1}
onChange={(val) => setScaleStep(val!)}
/>
</div>
<br />
<Button type="primary" onClick={() => setVisible(true)}>
show image preview
</Button>
<Image
width={200}
style={{ display: 'none' }}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/blur,r_50,s_50/quality,q_1/resize,m_mfit,h_200,w_200"
preview={{
visible,
scaleStep,
src: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
onVisibleChange: (value) => {
setVisible(value);
},
}}
/>
</>
);
};
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
加载失败显示图像占位符。
## en-US
Load failed to display image placeholder.

View File

@ -0,0 +1,13 @@
import React from 'react';
import { Image } from 'antd';
const App: React.FC = () => (
<Image
width={200}
height={200}
src="error"
fallback="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="
/>
);
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
可以自定义预览内容。
## en-US
You can customize the preview content.

View File

@ -0,0 +1,23 @@
import React from 'react';
import { Image } from 'antd';
const App: React.FC = () => (
<Image
width={200}
preview={{
destroyOnClose: true,
imageRender: () => (
<video
muted
width="100%"
controls
src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/file/A*uYT7SZwhJnUAAAAAAAAAAAAADgCCAQ"
/>
),
toolbarRender: () => null,
}}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
);
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
嵌套在弹框当中使用
## en-US
Nested in the modal

View File

@ -0,0 +1,85 @@
import React, { useState } from 'react';
import { Button, Divider, Image, Modal } from 'antd';
const App: React.FC = () => {
const [show1, setShow1] = useState(false);
const [show2, setShow2] = useState(false);
const [show3, setShow3] = useState(false);
return (
<>
<Button
onClick={() => {
setShow1(true);
}}
>
showModal
</Button>
<Modal
open={show1}
afterOpenChange={(open) => {
setShow1(open);
}}
onCancel={() => {
setShow1(false);
}}
>
<Button
onClick={() => {
setShow2(true);
}}
>
test2
</Button>
<Modal
open={show2}
afterOpenChange={(open) => {
setShow2(open);
}}
onCancel={() => {
setShow2(false);
}}
>
<Button
onClick={() => {
setShow3(true);
}}
>
test3
</Button>
<Modal
open={show3}
afterOpenChange={(open) => {
setShow3(open);
}}
onCancel={() => {
setShow3(false);
}}
>
<Image
width={200}
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
/>
<Divider />
<Image.PreviewGroup
preview={{
onChange: (current, prev) =>
console.log(`current index: ${current}, prev index: ${prev}`),
}}
>
<Image
width={200}
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
/>
<Image
width={200}
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
/>
</Image.PreviewGroup>
</Modal>
</Modal>
</Modal>
</>
);
};
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
大图使用 placeholder 渐进加载。
## en-US
Progressive when large image loading.

View File

@ -0,0 +1,32 @@
import React, { useState } from 'react';
import { Button, Image, Space } from 'antd';
const App: React.FC = () => {
const [random, setRandom] = useState<number>();
return (
<Space size={12}>
<Image
width={200}
src={`https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?${random}`}
placeholder={
<Image
preview={false}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/blur,r_50,s_50/quality,q_1/resize,m_mfit,h_200,w_200"
width={200}
/>
}
/>
<Button
type="primary"
onClick={() => {
setRandom(Date.now());
}}
>
Reload
</Button>
</Space>
);
};
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
多图预览时顶部展示进度, 支持自定义
## en-US
The progress is displayed at the top of the multi-image preview, and customization is supported

View File

@ -0,0 +1,20 @@
import React from 'react';
import { Image } from 'antd';
const App: React.FC = () => (
<Image.PreviewGroup
preview={{ countRender: (current, total) => `当前 ${current} / 总计 ${total}` }}
>
<Image width={150} src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" />
<Image
width={150}
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
/>
<Image
width={150}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
</Image.PreviewGroup>
);
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
从一张图片点开相册。
## en-US
Preview a collection from one image.

View File

@ -0,0 +1,19 @@
import React from 'react';
import { Image } from 'antd';
const App: React.FC = () => (
<Image.PreviewGroup
items={[
'https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp',
'https://gw.alipayobjects.com/zos/antfincdn/cV16ZqzMjW/photo-1473091540282-9b846e7965e3.webp',
'https://gw.alipayobjects.com/zos/antfincdn/x43I27A55%26/photo-1438109491414-7198515b166b.webp',
]}
>
<Image
width={200}
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
/>
</Image.PreviewGroup>
);
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
点击左右切换按钮可以预览多张图片。
## en-US
Click the left and right switch buttons to preview multiple images.

View File

@ -0,0 +1,18 @@
import React from 'react';
import { Image } from 'antd';
const App: React.FC = () => (
<Image.PreviewGroup
preview={{
onChange: (current, prev) => console.log(`current index: ${current}, prev index: ${prev}`),
}}
>
<Image width={200} src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" />
<Image
width={200}
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
/>
</Image.PreviewGroup>
);
export default App;

View File

@ -0,0 +1,17 @@
## zh-CN
自定义预览文本。
## en-US
Custom preview mask.
```css
.customize-mask {
font-size: 20px;
opacity: 1;
}
.customize-mask .anticon {
font-size: 32px;
}
```

View File

@ -0,0 +1,21 @@
import React from 'react';
import { ZoomInOutlined } from '@ant-design/icons';
import { Image, Space } from 'antd';
const App: React.FC = () => (
<Image
width={96}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
preview={{
maskClassName: 'customize-mask',
mask: (
<Space direction="vertical" align="center">
<ZoomInOutlined />
</Space>
),
}}
/>
);
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
可以设置不同的预览图片。
## en-US
You can set different preview image.

View File

@ -0,0 +1,14 @@
import React from 'react';
import { Image } from 'antd';
const App: React.FC = () => (
<Image
width={200}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/blur,r_50,s_50/quality,q_1/resize,m_mfit,h_200,w_200"
preview={{
src: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
}}
/>
);
export default App;

View File

@ -0,0 +1,35 @@
## zh-CN
可以自定义工具栏并添加下载原图或翻转旋转后图片的按钮。
## en-US
You can customize the toolbar and add a button for downloading the original image or downloading the flipped and rotated image.
```css
.toolbar-wrapper {
position: fixed;
bottom: 32px;
left: 50%;
padding: 0px 24px;
color: #fff;
font-size: 20px;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 100px;
transform: translateX(-50%);
}
.toolbar-wrapper .anticon {
padding: 12px;
cursor: pointer;
}
.toolbar-wrapper .anticon[disabled] {
cursor: not-allowed;
opacity: 0.3;
}
.toolbar-wrapper .anticon:hover {
opacity: 0.3;
}
```

View File

@ -0,0 +1,59 @@
import React from 'react';
import {
DownloadOutlined,
RotateLeftOutlined,
RotateRightOutlined,
SwapOutlined,
ZoomInOutlined,
ZoomOutOutlined,
} from '@ant-design/icons';
import { Image, Space } from 'antd';
const src = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png';
const App: React.FC = () => {
// or you can download flipped and rotated image
// https://codesandbox.io/s/zi-ding-yi-gong-ju-lan-antd-5-7-0-forked-c9jvmp
const onDownload = () => {
fetch(src)
.then((response) => response.blob())
.then((blob) => {
const url = URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = url;
link.download = 'image.png';
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(url);
link.remove();
});
};
return (
<Image
width={200}
src={src}
preview={{
toolbarRender: (
_,
{
transform: { scale },
actions: { onFlipY, onFlipX, onRotateLeft, onRotateRight, onZoomOut, onZoomIn },
},
) => (
<Space size={12} className="toolbar-wrapper">
<DownloadOutlined onClick={onDownload} />
<SwapOutlined rotate={90} onClick={onFlipY} />
<SwapOutlined onClick={onFlipX} />
<RotateLeftOutlined onClick={onRotateLeft} />
<RotateRightOutlined onClick={onRotateRight} />
<ZoomOutOutlined disabled={scale === 1} onClick={onZoomOut} />
<ZoomInOutlined disabled={scale === 50} onClick={onZoomIn} />
</Space>
),
}}
/>
);
};
export default App;

View File

@ -0,0 +1,97 @@
import * as React from 'react';
import EyeOutlined from '@ant-design/icons/EyeOutlined';
import classNames from 'classnames';
import RcImage from 'rc-image';
import type { ImageProps } from 'rc-image';
import { useZIndex } from '../_util/hooks/useZIndex';
import { getTransitionName } from '../_util/motion';
import { ConfigContext } from '../config-provider';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import defaultLocale from '../locale/en_US';
import PreviewGroup, { icons } from './PreviewGroup';
import useStyle from './style';
export interface CompositionImage<P> extends React.FC<P> {
PreviewGroup: typeof PreviewGroup;
}
const Image: CompositionImage<ImageProps> = (props) => {
const {
prefixCls: customizePrefixCls,
preview,
className,
rootClassName,
style,
...otherProps
} = props;
const {
getPrefixCls,
locale: contextLocale = defaultLocale,
getPopupContainer: getContextPopupContainer,
image,
} = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('image', customizePrefixCls);
const rootPrefixCls = getPrefixCls();
const imageLocale = contextLocale.Image || defaultLocale.Image;
// Style
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
const mergedRootClassName = classNames(rootClassName, hashId, cssVarCls, rootCls);
const mergedClassName = classNames(className, hashId, image?.className);
const [zIndex] = useZIndex(
'ImagePreview',
typeof preview === 'object' ? preview.zIndex : undefined,
);
const mergedPreview = React.useMemo<ImageProps['preview']>(() => {
if (preview === false) {
return preview;
}
const _preview = typeof preview === 'object' ? preview : {};
const { getContainer, closeIcon, ...restPreviewProps } = _preview;
return {
mask: (
<div className={`${prefixCls}-mask-info`}>
<EyeOutlined />
{imageLocale?.preview}
</div>
),
icons,
...restPreviewProps,
getContainer: getContainer ?? getContextPopupContainer,
transitionName: getTransitionName(rootPrefixCls, 'zoom', _preview.transitionName),
maskTransitionName: getTransitionName(rootPrefixCls, 'fade', _preview.maskTransitionName),
zIndex,
closeIcon: closeIcon ?? image?.preview?.closeIcon,
};
}, [preview, imageLocale, image?.preview?.closeIcon]);
const mergedStyle: React.CSSProperties = { ...image?.style, ...style };
return wrapCSSVar(
<RcImage
prefixCls={prefixCls}
preview={mergedPreview}
rootClassName={mergedRootClassName}
className={mergedClassName}
style={mergedStyle}
{...otherProps}
/>,
);
};
export type { ImageProps };
Image.PreviewGroup = PreviewGroup;
if (process.env.NODE_ENV !== 'production') {
Image.displayName = 'Image';
}
export default Image;

View File

@ -0,0 +1,162 @@
---
category: Components
group: 数据展示
title: Image 图片
subtitle: 图片
description: 可预览的图片。
cols: 2
---
## 何时使用
- 需要展示图片时使用。
- 加载显示大图或加载失败时容错处理。
## 代码演示
<!-- prettier-ignore -->
<code src="./demo/basic.tsx">基本用法</code>
<code src="./demo/fallback.tsx">容错处理</code>
<code src="./demo/placeholder.tsx">渐进加载</code>
<code src="./demo/preview-group.tsx">多张图片预览</code>
<code src="./demo/preview-group-visible.tsx">相册模式</code>
<code src="./demo/previewSrc.tsx">自定义预览图片</code>
<code src="./demo/controlled-preview.tsx">受控的预览</code>
<code src="./demo/toolbarRender.tsx">自定义工具栏</code>
<code src="./demo/imageRender.tsx">自定义预览内容</code>
<code src="./demo/preview-mask.tsx" debug>自定义预览文本</code>
<code src="./demo/nested.tsx">嵌套</code>
<code src="./demo/preview-group-top-progress.tsx" debug>多图预览时顶部进度自定义</code>
<code src="./demo/component-token.tsx" debug>自定义组件 Token</code>
## API
通用属性参考:[通用属性](/docs/react/common-props)
### Image
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| alt | 图像描述 | string | - | 4.6.0 |
| fallback | 加载失败容错地址 | string | - | 4.6.0 |
| height | 图像高度 | string \| number | - | 4.6.0 |
| placeholder | 加载占位,为 `true` 时使用默认占位 | ReactNode | - | 4.6.0 |
| preview | 预览参数,为 `false` 时禁用 | boolean \| [PreviewType](#previewtype) | true | 4.6.0 [PreviewType](#previewyype):4.7.0 |
| src | 图片地址 | string | - | 4.6.0 |
| width | 图像宽度 | string \| number | - | 4.6.0 |
| onError | 加载错误回调 | (event: Event) => void | - | 4.12.0 |
其他属性见 [&lt;img>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)
### PreviewType
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| visible | 是否显示 | boolean | - | - |
| src | 自定义预览 src | string | - | 4.10.0 |
| getContainer | 指定预览挂载的节点但依旧为全屏展示false 为挂载在当前位置 | string \| HTMLElement \| (() => HTMLElement) \| false | - | 4.8.0 |
| movable | 是否可移动 | boolean | true | 5.8.0 |
| mask | 缩略图遮罩 | ReactNode | - | 4.9.0 |
| maskClassName | 缩略图遮罩类名 | string | - | 4.11.0 |
| rootClassName | 预览图的根 DOM 类名 | string | - | 5.4.0 |
| scaleStep | `1 + scaleStep` 为缩放放大的每步倍数 | number | 0.5 | - |
| minScale | 最小缩放倍数 | number | 1 | 5.7.0 |
| maxScale | 最大放大倍数 | number | 50 | 5.7.0 |
| closeIcon | 自定义关闭 Icon | React.ReactNode | - | 5.7.0 |
| forceRender | 强制渲染预览图 | boolean | - | - |
| toolbarRender | 自定义工具栏 | (originalNode: React.ReactElement, info: Omit<[ToolbarRenderInfoType](#toolbarrenderinfotype), 'current' \| 'total'>) => React.ReactNode | - | 5.7.0 |
| imageRender | 自定义预览内容 | (originalNode: React.ReactElement, info: { transform: [TransformType](#transformtype) }) => React.ReactNode | - | 5.7.0 |
| destroyOnClose | 关闭预览时销毁子元素 | boolean | false | |
| onTransform | 预览图 transform 变化的回调 | { transform: [TransformType](#transformtype), action: [TransformAction](#transformaction) } | - | 5.7.0 |
| onVisibleChange | 当 `visible` 发生改变时的回调 | (visible: boolean, prevVisible: boolean) => void | - | - |
## PreviewGroup
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| preview | 预览参数,为 `false` 时禁用 | boolean \| [PreviewGroupType](#previewgrouptype) | true | 4.6.0 [PreviewGroupType](#previewgrouptype):4.7.0 |
| items | 预览数组 | string[] \| { src: string, crossOrigin: string, ... }[] | - | 5.7.0 |
| fallback | 加载失败容错地址 | string | - | 5.7.0 |
### PreviewGroupType
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| visible | 是否显示 | boolean | - | - |
| getContainer | 指定预览挂载的节点但依旧为全屏展示false 为挂载在当前位置 | string \| HTMLElement \| (() => HTMLElement) \| false | - | 4.8.0 |
| movable | 是否可移动 | boolean | true | 5.8.0 |
| current | 当前预览图的 index | number | - | 4.12.0 |
| mask | 缩略图遮罩 | ReactNode | - | 4.9.0 |
| maskClassName | 缩略图遮罩类名 | string | - | 4.11.0 |
| rootClassName | 预览图的根 DOM 类名 | string | - | 5.4.0 |
| scaleStep | `1 + scaleStep` 为缩放放大的每步倍数 | number | 0.5 | - |
| minScale | 最小缩放倍数 | number | 1 | 5.7.0 |
| maxScale | 最大放大倍数 | number | 50 | 5.7.0 |
| closeIcon | 自定义关闭 Icon | React.ReactNode | - | 5.7.0 |
| forceRender | 强制渲染预览图 | boolean | - | - |
| countRender | 自定义预览计数内容 | (current: number, total: number) => React.ReactNode | - | 4.20.0 |
| toolbarRender | 自定义工具栏 | (originalNode: React.ReactElement, info: [ToolbarRenderInfoType](#toolbarrenderinfotype)) => React.ReactNode | - | 5.7.0 |
| imageRender | 自定义预览内容 | (originalNode: React.ReactElement, info: { transform: [TransformType](#transformtype), current: number }) => React.ReactNode | - | 5.7.0 |
| onTransform | 预览图 transform 变化的回调 | { transform: [TransformType](#transformtype), action: [TransformAction](#transformaction) } | - | 5.7.0 |
| onChange | 切换预览图的回调 | (current: number, prevCurrent: number) => void | - | 5.3.0 |
| onVisibleChange | 当 `visible` 发生改变时的回调 | (visible: boolean, prevVisible: boolean, current: number) => void | - | current 参数 5.3.0 |
## Interface
### TransformType
```ts
{
x: number;
y: number;
rotate: number;
scale: number;
flipX: boolean;
flipY: boolean;
}
```
### TransformAction
```ts
type TransformAction =
| 'flipY'
| 'flipX'
| 'rotateLeft'
| 'rotateRight'
| 'zoomIn'
| 'zoomOut'
| 'close'
| 'prev'
| 'next'
| 'wheel'
| 'doubleClick'
| 'move'
| 'dragRebound';
```
### ToolbarRenderInfoType
```ts
{
icons: {
flipYIcon: React.ReactNode;
flipXIcon: React.ReactNode;
rotateLeftIcon: React.ReactNode;
rotateRightIcon: React.ReactNode;
zoomOutIcon: React.ReactNode;
zoomInIcon: React.ReactNode;
};
actions: {
onFlipY: () => void;
onFlipX: () => void;
onRotateLeft: () => void;
onRotateRight: () => void;
onZoomOut: () => void;
onZoomIn: () => void;
};
transform: TransformType,
current: number;
total: number;
}
```

View File

@ -0,0 +1,390 @@
import type { CSSObject } from '@ant-design/cssinjs';
import { unit } from '@ant-design/cssinjs';
import { TinyColor } from '@ctrl/tinycolor';
import { genModalMaskStyle } from '../../modal/style';
import { textEllipsis } from '../../style';
import { initFadeMotion, initZoomMotion } from '../../style/motion';
import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal';
import { genStyleHooks, mergeToken } from '../../theme/internal';
export interface ComponentToken {
/**
* @desc z-index
* @descEN z-index of preview popup
*/
zIndexPopup: number;
/**
* @desc
* @descEN Size of preview operation icon
*/
previewOperationSize: number;
/**
* @desc
* @descEN Color of preview operation icon
*/
previewOperationColor: string;
/**
* @desc
* @descEN Color of hovered preview operation icon
*/
previewOperationHoverColor: string;
/**
* @desc
* @descEN Disabled color of preview operation icon
*/
previewOperationColorDisabled: string;
}
export interface ImageToken extends FullToken<'Image'> {
previewCls: string;
modalMaskBg: string;
imagePreviewSwitchSize: number;
}
export type PositionType = 'static' | 'relative' | 'fixed' | 'absolute' | 'sticky' | undefined;
export const genBoxStyle = (position?: PositionType): CSSObject => ({
position: position || 'absolute',
inset: 0,
});
export const genImageMaskStyle = (token: ImageToken): CSSObject => {
const { iconCls, motionDurationSlow, paddingXXS, marginXXS, prefixCls, colorTextLightSolid } =
token;
return {
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: colorTextLightSolid,
background: new TinyColor('#000').setAlpha(0.5).toRgbString(),
cursor: 'pointer',
opacity: 0,
transition: `opacity ${motionDurationSlow}`,
[`.${prefixCls}-mask-info`]: {
...textEllipsis,
padding: `0 ${unit(paddingXXS)}`,
[iconCls]: {
marginInlineEnd: marginXXS,
svg: {
verticalAlign: 'baseline',
},
},
},
};
};
export const genPreviewOperationsStyle = (token: ImageToken): CSSObject => {
const {
previewCls,
modalMaskBg,
paddingSM,
marginXL,
margin,
paddingLG,
previewOperationColorDisabled,
previewOperationHoverColor,
motionDurationSlow,
iconCls,
colorTextLightSolid,
} = token;
const operationBg = new TinyColor(modalMaskBg).setAlpha(0.1);
const operationBgHover = operationBg.clone().setAlpha(0.2);
return {
[`${previewCls}-footer`]: {
position: 'fixed',
bottom: marginXL,
left: {
_skip_check_: true,
value: 0,
},
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
color: token.previewOperationColor,
},
[`${previewCls}-progress`]: {
marginBottom: margin,
},
[`${previewCls}-close`]: {
position: 'fixed',
top: marginXL,
right: {
_skip_check_: true,
value: marginXL,
},
display: 'flex',
color: colorTextLightSolid,
backgroundColor: operationBg.toRgbString(),
borderRadius: '50%',
padding: paddingSM,
outline: 0,
border: 0,
cursor: 'pointer',
transition: `all ${motionDurationSlow}`,
'&:hover': {
backgroundColor: operationBgHover.toRgbString(),
},
[`& > ${iconCls}`]: {
fontSize: token.previewOperationSize,
},
},
[`${previewCls}-operations`]: {
display: 'flex',
alignItems: 'center',
padding: `0 ${unit(paddingLG)}`,
backgroundColor: operationBg.toRgbString(),
borderRadius: 100,
'&-operation': {
marginInlineStart: paddingSM,
padding: paddingSM,
cursor: 'pointer',
transition: `all ${motionDurationSlow}`,
userSelect: 'none',
[`&:not(${previewCls}-operations-operation-disabled):hover > ${iconCls}`]: {
color: previewOperationHoverColor,
},
'&-disabled': {
color: previewOperationColorDisabled,
cursor: 'not-allowed',
},
'&:first-of-type': {
marginInlineStart: 0,
},
[`& > ${iconCls}`]: {
fontSize: token.previewOperationSize,
},
},
},
};
};
export const genPreviewSwitchStyle = (token: ImageToken): CSSObject => {
const {
modalMaskBg,
iconCls,
previewOperationColorDisabled,
previewCls,
zIndexPopup,
motionDurationSlow,
} = token;
const operationBg = new TinyColor(modalMaskBg).setAlpha(0.1);
const operationBgHover = operationBg.clone().setAlpha(0.2);
return {
[`${previewCls}-switch-left, ${previewCls}-switch-right`]: {
position: 'fixed',
insetBlockStart: '50%',
zIndex: token.calc(zIndexPopup).add(1).equal({ unit: false }),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: token.imagePreviewSwitchSize,
height: token.imagePreviewSwitchSize,
marginTop: token.calc(token.imagePreviewSwitchSize).mul(-1).div(2).equal(),
color: token.previewOperationColor,
background: operationBg.toRgbString(),
borderRadius: '50%',
transform: `translateY(-50%)`,
cursor: 'pointer',
transition: `all ${motionDurationSlow}`,
userSelect: 'none',
'&:hover': {
background: operationBgHover.toRgbString(),
},
[`&-disabled`]: {
'&, &:hover': {
color: previewOperationColorDisabled,
background: 'transparent',
cursor: 'not-allowed',
[`> ${iconCls}`]: {
cursor: 'not-allowed',
},
},
},
[`> ${iconCls}`]: {
fontSize: token.previewOperationSize,
},
},
[`${previewCls}-switch-left`]: {
insetInlineStart: token.marginSM,
},
[`${previewCls}-switch-right`]: {
insetInlineEnd: token.marginSM,
},
};
};
export const genImagePreviewStyle: GenerateStyle<ImageToken> = (token: ImageToken) => {
const { motionEaseOut, previewCls, motionDurationSlow, componentCls } = token;
return [
{
[`${componentCls}-preview-root`]: {
[previewCls]: {
height: '100%',
textAlign: 'center',
pointerEvents: 'none',
},
[`${previewCls}-body`]: {
...genBoxStyle(),
overflow: 'hidden',
},
[`${previewCls}-img`]: {
maxWidth: '100%',
maxHeight: '70%',
verticalAlign: 'middle',
transform: 'scale3d(1, 1, 1)',
cursor: 'grab',
transition: `transform ${motionDurationSlow} ${motionEaseOut} 0s`,
userSelect: 'none',
'&-wrapper': {
...genBoxStyle(),
transition: `transform ${motionDurationSlow} ${motionEaseOut} 0s`,
// https://github.com/ant-design/ant-design/issues/39913
// TailwindCSS will reset img default style.
// Let's set back.
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
'& > *': {
pointerEvents: 'auto',
},
'&::before': {
display: 'inline-block',
width: 1,
height: '50%',
marginInlineEnd: -1,
content: '""',
},
},
},
[`${previewCls}-moving`]: {
[`${previewCls}-preview-img`]: {
cursor: 'grabbing',
'&-wrapper': {
transitionDuration: '0s',
},
},
},
},
},
// Override
{
[`${componentCls}-preview-root`]: {
[`${previewCls}-wrap`]: {
zIndex: token.zIndexPopup,
},
},
},
// Preview operations & switch
{
[`${componentCls}-preview-operations-wrapper`]: {
position: 'fixed',
zIndex: token.calc(token.zIndexPopup).add(1).equal({ unit: false }),
},
'&': [genPreviewOperationsStyle(token), genPreviewSwitchStyle(token)],
},
];
};
const genImageStyle: GenerateStyle<ImageToken> = (token: ImageToken) => {
const { componentCls } = token;
return {
// ============================== image ==============================
[componentCls]: {
position: 'relative',
display: 'inline-block',
[`${componentCls}-img`]: {
width: '100%',
height: 'auto',
verticalAlign: 'middle',
},
[`${componentCls}-img-placeholder`]: {
backgroundColor: token.colorBgContainerDisabled,
backgroundImage:
"url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTQuNSAyLjVoLTEzQS41LjUgMCAwIDAgMSAzdjEwYS41LjUgMCAwIDAgLjUuNWgxM2EuNS41IDAgMCAwIC41LS41VjNhLjUuNSAwIDAgMC0uNS0uNXpNNS4yODEgNC43NWExIDEgMCAwIDEgMCAyIDEgMSAwIDAgMSAwLTJ6bTguMDMgNi44M2EuMTI3LjEyNyAwIDAgMS0uMDgxLjAzSDIuNzY5YS4xMjUuMTI1IDAgMCAxLS4wOTYtLjIwN2wyLjY2MS0zLjE1NmEuMTI2LjEyNiAwIDAgMSAuMTc3LS4wMTZsLjAxNi4wMTZMNy4wOCAxMC4wOWwyLjQ3LTIuOTNhLjEyNi4xMjYgMCAwIDEgLjE3Ny0uMDE2bC4wMTUuMDE2IDMuNTg4IDQuMjQ0YS4xMjcuMTI3IDAgMCAxLS4wMi4xNzV6IiBmaWxsPSIjOEM4QzhDIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4=')",
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center',
backgroundSize: '30%',
},
[`${componentCls}-mask`]: {
...genImageMaskStyle(token),
},
[`${componentCls}-mask:hover`]: {
opacity: 1,
},
[`${componentCls}-placeholder`]: {
...genBoxStyle(),
},
},
};
};
const genPreviewMotion: GenerateStyle<ImageToken> = (token) => {
const { previewCls } = token;
return {
[`${previewCls}-root`]: initZoomMotion(token, 'zoom'),
[`&`]: initFadeMotion(token, true),
};
};
// ============================== Export ==============================
export const prepareComponentToken: GetDefaultToken<'Image'> = (token) => ({
zIndexPopup: token.zIndexPopupBase + 80,
previewOperationColor: new TinyColor(token.colorTextLightSolid).setAlpha(0.65).toRgbString(),
previewOperationHoverColor: new TinyColor(token.colorTextLightSolid).setAlpha(0.85).toRgbString(),
previewOperationColorDisabled: new TinyColor(token.colorTextLightSolid)
.setAlpha(0.25)
.toRgbString(),
previewOperationSize: token.fontSizeIcon * 1.5, // FIXME: fontSizeIconLG
});
export default genStyleHooks(
'Image',
(token) => {
const previewCls = `${token.componentCls}-preview`;
const imageToken = mergeToken<ImageToken>(token, {
previewCls,
modalMaskBg: new TinyColor('#000').setAlpha(0.45).toRgbString(), // FIXME: Shared Token
imagePreviewSwitchSize: token.controlHeightLG,
});
return [
genImageStyle(imageToken),
genImagePreviewStyle(imageToken),
genModalMaskStyle(mergeToken<ImageToken>(imageToken, { componentCls: previewCls })),
genPreviewMotion(imageToken),
];
},
prepareComponentToken,
);

View File

@ -20,6 +20,8 @@ export { default as message } from './message'
export { default as Button } from './button'
export type { ArgsProps } from './message'
export type { ButtonProps, ButtonGroupProps } from './button';
export type { ImageProps } from './image';
export { default as Image } from './image';
export { default as FloatButton } from './float-button';
export type {
FloatButtonGroupProps,