diff --git a/packages/map/package.json b/packages/map/package.json index aa0f776..941577c 100644 --- a/packages/map/package.json +++ b/packages/map/package.json @@ -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" } } diff --git a/packages/map/src/MapBox.tsx b/packages/map/src/MapBox.tsx index 1e02e7d..9da0e84 100644 --- a/packages/map/src/MapBox.tsx +++ b/packages/map/src/MapBox.tsx @@ -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 { + 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; + mapCenter?: { + longitude: number, latitude: number + } + draw?: boolean; + buttonList?: ToolsProps['buttonList'] + onLoad?: (e: mapboxgl.MapboxEvent) => 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((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(null) const drawControlRef = useRef(null) // 默认绘制配置 - const [drawConfig, setConfig] = useState({ + const [drawConfig] = useState({ displayControlsDefault: false, position: 'top-left', styles: mapboxDrawStyle, @@ -49,82 +112,54 @@ const MapBox = forwardRef((props, ref) => { defaultMode: 'draw_polygon', }) - const handleDrawCreate = e => { - console.log('handleDrawCreate', e) - } + const initMarker = useMemo( + () => { + return markerData.map((_item, index) => ( + + )) + }, + [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
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 */} { - onLoad && onLoad(e); - }} + {...defaultMapConfig} + initialViewState={{ ...mapCenter }} + onLoad={onLoad} style={{ width: width, height: height, ...style }} - {...merge(defaultMapConfig, others)} + {...others} > + {initMarker} {/* */} - + {/* ---------------绘制图层--------------------- */} + {draw && ( + + )} {children}
diff --git a/packages/map/src/assets/icons/cluster_alarm.png b/packages/map/src/assets/icons/cluster_alarm.png new file mode 100644 index 0000000..395f082 Binary files /dev/null and b/packages/map/src/assets/icons/cluster_alarm.png differ diff --git a/packages/map/src/assets/icons/cluster_escape.png b/packages/map/src/assets/icons/cluster_escape.png new file mode 100644 index 0000000..940203e Binary files /dev/null and b/packages/map/src/assets/icons/cluster_escape.png differ diff --git a/packages/map/src/assets/icons/cluster_normal.png b/packages/map/src/assets/icons/cluster_normal.png new file mode 100644 index 0000000..ac7f5c5 Binary files /dev/null and b/packages/map/src/assets/icons/cluster_normal.png differ diff --git a/packages/map/src/assets/icons/cluterImage.json b/packages/map/src/assets/icons/cluterImage.json new file mode 100644 index 0000000..9d9e67c --- /dev/null +++ b/packages/map/src/assets/icons/cluterImage.json @@ -0,0 +1,79 @@ +[{ + "key": "UNCLUTER", + "state": [{ + "type": "NORMAL", + "bgImage": "", + "bgColor": "#0099ff", + "frontImage": "", + "frontColor": "red", + "ripple": false, + "redDot": false, + "name": "IMAGE_UNCLUTER_NORMAL" + }, + { + "type": "RUNING", + "backImage": "", + "backColor": "green", + "frontImage": "", + "frontColor": "red", + "ripple": false, + "redDot": false, + "name": "IMAGE_UNCLUTER_RUNING" + }, + { + "type": "NORMAL_SELECT", + "backImage": "", + "backColor": "yellow", + "frontImage": "", + "frontColor": "red", + "ripple": false, + "redDot": false, + "name": "IMAGE_UNCLUTER_NORMAL_SELECT" + }, + { + "type": "RUNING_SELECT", + "backImage": "", + "backColor": "red", + "frontImage": "", + "frontColor": "blue", + "ripple": false, + "redDot": false, + "name": "IMAGE_UNCLUTER_RUNING_SELECT" + } + ] + }, + { + "key": "APPEARCLUTER_RED", + "state": [{ + "type": "BLUE", + "bgImage": "", + "bgColor": "#0099ff", + "frontImage": "", + "frontColor": "#ffffff", + "ripple": false, + "redDot": false, + "name": "IMAGE_APPEARCLUTER_BLUE" + }, + { + "type": "YELLOW", + "bgImage": "", + "bgColor": "#FF9900", + "frontImage": "", + "frontColor": "#ffffff", + "ripple": false, + "redDot": false, + "name": "IMAGE_APPEARCLUTER_YELLOW" + }, + { + "type": "RED", + "bgImage": "", + "bgColor": "#F25051", + "frontImage": "", + "frontColor": "#ffffff", + "ripple": false, + "redDot": false, + "name": "IMAGE_APPEARCLUTER_RED" + } + ] + } +] \ No newline at end of file diff --git a/packages/map/src/assets/icons/fecluster_alarm.png b/packages/map/src/assets/icons/fecluster_alarm.png new file mode 100644 index 0000000..777c5ac Binary files /dev/null and b/packages/map/src/assets/icons/fecluster_alarm.png differ diff --git a/packages/map/src/assets/icons/fecluster_escape.png b/packages/map/src/assets/icons/fecluster_escape.png new file mode 100644 index 0000000..de53d47 Binary files /dev/null and b/packages/map/src/assets/icons/fecluster_escape.png differ diff --git a/packages/map/src/assets/icons/fecluster_normal.png b/packages/map/src/assets/icons/fecluster_normal.png new file mode 100644 index 0000000..2b65d02 Binary files /dev/null and b/packages/map/src/assets/icons/fecluster_normal.png differ diff --git a/packages/map/src/assets/icons/fecluster_normal_select.png b/packages/map/src/assets/icons/fecluster_normal_select.png new file mode 100644 index 0000000..7ab4b83 Binary files /dev/null and b/packages/map/src/assets/icons/fecluster_normal_select.png differ diff --git a/packages/map/src/assets/icons/fecluster_uncontrolled.png b/packages/map/src/assets/icons/fecluster_uncontrolled.png new file mode 100644 index 0000000..f91f77f Binary files /dev/null and b/packages/map/src/assets/icons/fecluster_uncontrolled.png differ diff --git a/packages/map/src/assets/icons/index.ts b/packages/map/src/assets/icons/index.ts new file mode 100644 index 0000000..776b75e --- /dev/null +++ b/packages/map/src/assets/icons/index.ts @@ -0,0 +1,186 @@ +//引入地图所所有点图片 +const file: { [key: string]: string } = {}; + +const modules = import.meta.glob('./*.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; // 垂直控制点偏移量

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; +} diff --git a/packages/map/src/assets/icons/marker_escape.png b/packages/map/src/assets/icons/marker_escape.png new file mode 100644 index 0000000..a357791 Binary files /dev/null and b/packages/map/src/assets/icons/marker_escape.png differ diff --git a/packages/map/src/assets/icons/marker_monitor.png b/packages/map/src/assets/icons/marker_monitor.png new file mode 100644 index 0000000..0572538 Binary files /dev/null and b/packages/map/src/assets/icons/marker_monitor.png differ diff --git a/packages/map/src/assets/icons/marker_normal.png b/packages/map/src/assets/icons/marker_normal.png new file mode 100644 index 0000000..14cfa80 Binary files /dev/null and b/packages/map/src/assets/icons/marker_normal.png differ diff --git a/packages/map/src/assets/icons/marker_normal_select.png b/packages/map/src/assets/icons/marker_normal_select.png new file mode 100644 index 0000000..00eeaab Binary files /dev/null and b/packages/map/src/assets/icons/marker_normal_select.png differ diff --git a/packages/map/src/assets/icons/marker_pos.png b/packages/map/src/assets/icons/marker_pos.png new file mode 100644 index 0000000..9c0774b Binary files /dev/null and b/packages/map/src/assets/icons/marker_pos.png differ diff --git a/packages/map/src/assets/icons/marker_runing.png b/packages/map/src/assets/icons/marker_runing.png new file mode 100644 index 0000000..0572538 Binary files /dev/null and b/packages/map/src/assets/icons/marker_runing.png differ diff --git a/packages/map/src/assets/icons/marker_runing_select.png b/packages/map/src/assets/icons/marker_runing_select.png new file mode 100644 index 0000000..00eeaab Binary files /dev/null and b/packages/map/src/assets/icons/marker_runing_select.png differ diff --git a/packages/map/src/assets/icons/marker_track.png b/packages/map/src/assets/icons/marker_track.png new file mode 100644 index 0000000..2d06249 Binary files /dev/null and b/packages/map/src/assets/icons/marker_track.png differ diff --git a/packages/map/src/assets/icons/marker_track_virtual.png b/packages/map/src/assets/icons/marker_track_virtual.png new file mode 100644 index 0000000..7470540 Binary files /dev/null and b/packages/map/src/assets/icons/marker_track_virtual.png differ diff --git a/packages/map/src/assets/icons/marker_uncontrolled.png b/packages/map/src/assets/icons/marker_uncontrolled.png new file mode 100644 index 0000000..7470540 Binary files /dev/null and b/packages/map/src/assets/icons/marker_uncontrolled.png differ diff --git a/packages/map/src/assets/icons/marker_virtual.png b/packages/map/src/assets/icons/marker_virtual.png new file mode 100644 index 0000000..cf271bd Binary files /dev/null and b/packages/map/src/assets/icons/marker_virtual.png differ diff --git a/packages/map/src/assets/icons/marker_virtual_select.png b/packages/map/src/assets/icons/marker_virtual_select.png new file mode 100644 index 0000000..00eeaab Binary files /dev/null and b/packages/map/src/assets/icons/marker_virtual_select.png differ diff --git a/packages/map/src/components/clusters/Clusters.tsx b/packages/map/src/components/clusters/Clusters.tsx new file mode 100644 index 0000000..4b360dd --- /dev/null +++ b/packages/map/src/components/clusters/Clusters.tsx @@ -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((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 ( + + + + + + ) +}) + +export default Cluster diff --git a/packages/map/src/components/clusters/index.tsx b/packages/map/src/components/clusters/index.tsx new file mode 100644 index 0000000..3775dff --- /dev/null +++ b/packages/map/src/components/clusters/index.tsx @@ -0,0 +1,5 @@ +/** + * Created by jiangzhixiong on 2024/05/23 + */ +export { default as Clusters } from './Clusters' +// export type { ClustersProps, ClustersRefProps } from './Clusters' diff --git a/packages/map/src/components/clusters/layers.ts b/packages/map/src/components/clusters/layers.ts new file mode 100644 index 0000000..b4c060a --- /dev/null +++ b/packages/map/src/components/clusters/layers.ts @@ -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' + } +}; diff --git a/packages/map/src/components/drawControl/DrawControl.tsx b/packages/map/src/components/drawControl/DrawControl.tsx index 62788a0..9cf9a70 100644 --- a/packages/map/src/components/drawControl/DrawControl.tsx +++ b/packages/map/src/components/drawControl/DrawControl.tsx @@ -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((props, ref) => { - const drawRef = useRef(null) + const drawRef = useRef(null) useControl( () => { @@ -51,16 +52,15 @@ const DrawControl = forwardRef((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((props, re ); useImperativeHandle(ref, () => ({ - drawer: drawRef.current + drawer: drawRef.current! })) return null; diff --git a/packages/map/src/components/marker/Marker.tsx b/packages/map/src/components/marker/Marker.tsx new file mode 100644 index 0000000..deeca9f --- /dev/null +++ b/packages/map/src/components/marker/Marker.tsx @@ -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((props, ref) => { + const { + prefixCls: customizePrefixCls, + key, + longitude, + latitude, + onClick + } = props + const { getPrefixCls } = useContext(ConfigContext) + const componentName = getPrefixCls('-map-marker', customizePrefixCls); + + useImperativeHandle(ref, () => ({ + + })) + + return ( + + + + ) +}) + +export default Marker diff --git a/packages/map/src/components/marker/index.tsx b/packages/map/src/components/marker/index.tsx new file mode 100644 index 0000000..e2a7138 --- /dev/null +++ b/packages/map/src/components/marker/index.tsx @@ -0,0 +1,8 @@ +/** + * Created by jiangzhixiong on 2024/05/23 + */ +import Marker from './Marker' + +export type { MarkerProps, MarkerRefProps } from './Marker' + +export default Marker diff --git a/packages/map/src/components/marker/pin.tsx b/packages/map/src/components/marker/pin.tsx new file mode 100644 index 0000000..c669333 --- /dev/null +++ b/packages/map/src/components/marker/pin.tsx @@ -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 ( + + + + ); +} + +export default React.memo(Pin); diff --git a/packages/map/src/components/popup/PopUp.tsx b/packages/map/src/components/popup/PopUp.tsx new file mode 100644 index 0000000..e1efd0a --- /dev/null +++ b/packages/map/src/components/popup/PopUp.tsx @@ -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((props, ref) => { + const { + longitude, + latitude, + onClose, + prefixCls: customizePrefixCls + } = props + const { getPrefixCls } = useContext(ConfigContext) + const componentName = getPrefixCls('map-popup', customizePrefixCls); + + useImperativeHandle(ref, () => ({ + + })) + + return ( + +

+ popup +
+ + ) +}) + +export default PopUp diff --git a/packages/map/src/components/popup/index.tsx b/packages/map/src/components/popup/index.tsx new file mode 100644 index 0000000..166a7c3 --- /dev/null +++ b/packages/map/src/components/popup/index.tsx @@ -0,0 +1,5 @@ +/** + * Created by jiangzhixiong on 2024/05/23 + */ +export { default as PopUp } from './PopUp' +export type { PopUpProps, PopUpRefProps } from './PopUp' diff --git a/packages/map/src/components/tools/Tools.tsx b/packages/map/src/components/tools/Tools.tsx index 299a7c5..3e009b8 100644 --- a/packages/map/src/components/tools/Tools.tsx +++ b/packages/map/src/components/tools/Tools.tsx @@ -43,16 +43,14 @@ const Tools = forwardRef((props, ref) => {
    {buttonList.map((item) => ( - <> -
  • - {typeof item.icon === 'string' ? ( - - ) : ( - item.icon - )} - {item.label} -
  • - +
  • + {typeof item.icon === 'string' ? ( + + ) : ( + item.icon + )} + {item.label} +
  • ))}
, +
+ + +
, +] +`; + +exports[`renders components/image/demo/controlled-preview.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/image/demo/fallback.tsx extend context correctly 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; + +exports[`renders components/image/demo/fallback.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/image/demo/imageRender.tsx extend context correctly 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; + +exports[`renders components/image/demo/imageRender.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/image/demo/nested.tsx extend context correctly 1`] = ` + +`; + +exports[`renders components/image/demo/nested.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/image/demo/placeholder.tsx extend context correctly 1`] = ` +
+
+
+ + +
+
+ + + + Preview +
+
+
+
+
+ +
+
+`; + +exports[`renders components/image/demo/placeholder.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/image/demo/preview-group.tsx extend context correctly 1`] = ` +Array [ +
+ +
+
+ + + + Preview +
+
+
, +
+ +
+
+ + + + Preview +
+
+
, +] +`; + +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 [ +
+ +
+
+ + + + Preview +
+
+
, +
+ +
+
+ + + + Preview +
+
+
, +
+ +
+
+ + + + Preview +
+
+
, +] +`; + +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`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; + +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`] = ` +
+ +
+
+
+ + + +
+
+ 示例 +
+
+
+
+`; + +exports[`renders components/image/demo/preview-mask.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/image/demo/previewSrc.tsx extend context correctly 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; + +exports[`renders components/image/demo/previewSrc.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/image/demo/toolbarRender.tsx extend context correctly 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; + +exports[`renders components/image/demo/toolbarRender.tsx extend context correctly 2`] = `[]`; diff --git a/packages/meta/src/image/__tests__/__snapshots__/demo.test.ts.snap b/packages/meta/src/image/__tests__/__snapshots__/demo.test.ts.snap new file mode 100644 index 0000000..af6eb9c --- /dev/null +++ b/packages/meta/src/image/__tests__/__snapshots__/demo.test.ts.snap @@ -0,0 +1,801 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders components/image/demo/basic.tsx correctly 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; + +exports[`renders components/image/demo/component-token.tsx correctly 1`] = ` +Array [ +
+ +
+
+ + + + Preview +
+
+
, +
+ +
+
+ + + + Preview +
+
+
, +] +`; + +exports[`renders components/image/demo/controlled-preview.tsx correctly 1`] = ` +Array [ +
+ scaleStep: + +
+
+ + + + + + + + + + +
+
+ +
+
+
, +
, + , +
+ + +
, +] +`; + +exports[`renders components/image/demo/fallback.tsx correctly 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; + +exports[`renders components/image/demo/imageRender.tsx correctly 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; + +exports[`renders components/image/demo/nested.tsx correctly 1`] = ` + +`; + +exports[`renders components/image/demo/placeholder.tsx correctly 1`] = ` +
+
+
+ + +
+
+ + + + Preview +
+
+
+
+
+ +
+
+`; + +exports[`renders components/image/demo/preview-group.tsx correctly 1`] = ` +Array [ +
+ +
+
+ + + + Preview +
+
+
, +
+ +
+
+ + + + Preview +
+
+
, +] +`; + +exports[`renders components/image/demo/preview-group-top-progress.tsx correctly 1`] = ` +Array [ +
+ +
+
+ + + + Preview +
+
+
, +
+ +
+
+ + + + Preview +
+
+
, +
+ +
+
+ + + + Preview +
+
+
, +] +`; + +exports[`renders components/image/demo/preview-group-visible.tsx correctly 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; + +exports[`renders components/image/demo/preview-mask.tsx correctly 1`] = ` +
+ +
+
+
+ + + +
+
+ 示例 +
+
+
+
+`; + +exports[`renders components/image/demo/previewSrc.tsx correctly 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; + +exports[`renders components/image/demo/toolbarRender.tsx correctly 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; diff --git a/packages/meta/src/image/__tests__/__snapshots__/index.test.tsx.snap b/packages/meta/src/image/__tests__/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000..186df35 --- /dev/null +++ b/packages/meta/src/image/__tests__/__snapshots__/index.test.tsx.snap @@ -0,0 +1,326 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Image Default Group preview props 1`] = ` + +
+
+ +
+
+ + + + Preview +
+
+
+
+
+ + +
+
+
+
+
+ +
+ +`; + +exports[`Image rtl render component should be rendered correctly in RTL direction 1`] = ` +
+ +
+
+ + + + Preview +
+
+
+`; diff --git a/packages/meta/src/image/__tests__/demo-extend.test.ts b/packages/meta/src/image/__tests__/demo-extend.test.ts new file mode 100644 index 0000000..26e2be1 --- /dev/null +++ b/packages/meta/src/image/__tests__/demo-extend.test.ts @@ -0,0 +1,3 @@ +import { extendTest } from '../../../tests/shared/demoTest'; + +extendTest('image'); diff --git a/packages/meta/src/image/__tests__/demo.test.ts b/packages/meta/src/image/__tests__/demo.test.ts new file mode 100644 index 0000000..639111d --- /dev/null +++ b/packages/meta/src/image/__tests__/demo.test.ts @@ -0,0 +1,3 @@ +import demoTest from '../../../tests/shared/demoTest'; + +demoTest('image'); diff --git a/packages/meta/src/image/__tests__/image.test.ts b/packages/meta/src/image/__tests__/image.test.ts new file mode 100644 index 0000000..2e0739b --- /dev/null +++ b/packages/meta/src/image/__tests__/image.test.ts @@ -0,0 +1,5 @@ +import { imageDemoTest } from '../../../tests/shared/imageTest'; + +describe('Image image', () => { + imageDemoTest('image'); +}); diff --git a/packages/meta/src/image/__tests__/index.test.tsx b/packages/meta/src/image/__tests__/index.test.tsx new file mode 100644 index 0000000..e523ba2 --- /dev/null +++ b/packages/meta/src/image/__tests__/index.test.tsx @@ -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(); + + fireEvent.click(container.querySelector('.ant-image')!); + expect(container.querySelector('.ant-image-preview-root')).toBe(null); + }); + it('Group preview props set false', () => { + const { container } = render( + + + , + ); + + fireEvent.click(container.querySelector('.ant-image')!); + + expect(container.querySelector('.ant-image-preview-root')).toBe(null); + }); + + it('Default preview props', () => { + const { container, baseElement } = render(); + + 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( + + + , + ); + + 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( + , + ); + + 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( + + + , + ); + + 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( + <> +
+ document.querySelector('.container')!}> + + + , + ); + 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 = () => ( + + ); + const { baseElement } = render(); + 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( + + + , + ); + + 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 = () => ( + + + + + + + + + + + + ); + const { baseElement } = render(); + + 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'); + }); +}); diff --git a/packages/meta/src/image/demo/basic.md b/packages/meta/src/image/demo/basic.md new file mode 100644 index 0000000..d61b945 --- /dev/null +++ b/packages/meta/src/image/demo/basic.md @@ -0,0 +1,7 @@ +## zh-CN + +单击图像可以放大显示。 + +## en-US + +Click the image to zoom in. diff --git a/packages/meta/src/image/demo/basic.tsx b/packages/meta/src/image/demo/basic.tsx new file mode 100644 index 0000000..30a75d9 --- /dev/null +++ b/packages/meta/src/image/demo/basic.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { Image } from 'antd'; + +const App: React.FC = () => ( + +); + +export default App; diff --git a/packages/meta/src/image/demo/component-token.md b/packages/meta/src/image/demo/component-token.md new file mode 100644 index 0000000..135a6b2 --- /dev/null +++ b/packages/meta/src/image/demo/component-token.md @@ -0,0 +1,7 @@ +## zh-CN + +自定义组件 Token。 + +## en-US + +Custom component token. diff --git a/packages/meta/src/image/demo/component-token.tsx b/packages/meta/src/image/demo/component-token.tsx new file mode 100644 index 0000000..b2843ad --- /dev/null +++ b/packages/meta/src/image/demo/component-token.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { ConfigProvider, Image } from 'antd'; + +const App: React.FC = () => ( + + `当前 ${current} / 总计 ${total}` }} + > + + + + +); + +export default App; diff --git a/packages/meta/src/image/demo/controlled-preview.md b/packages/meta/src/image/demo/controlled-preview.md new file mode 100644 index 0000000..e6edd79 --- /dev/null +++ b/packages/meta/src/image/demo/controlled-preview.md @@ -0,0 +1,7 @@ +## zh-CN + +可以使预览受控。 + +## en-US + +You can make preview controlled. diff --git a/packages/meta/src/image/demo/controlled-preview.tsx b/packages/meta/src/image/demo/controlled-preview.tsx new file mode 100644 index 0000000..252e282 --- /dev/null +++ b/packages/meta/src/image/demo/controlled-preview.tsx @@ -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 ( + <> +
+ scaleStep:{' '} + setScaleStep(val!)} + /> +
+
+ + { + setVisible(value); + }, + }} + /> + + ); +}; + +export default App; diff --git a/packages/meta/src/image/demo/fallback.md b/packages/meta/src/image/demo/fallback.md new file mode 100644 index 0000000..45c2957 --- /dev/null +++ b/packages/meta/src/image/demo/fallback.md @@ -0,0 +1,7 @@ +## zh-CN + +加载失败显示图像占位符。 + +## en-US + +Load failed to display image placeholder. diff --git a/packages/meta/src/image/demo/fallback.tsx b/packages/meta/src/image/demo/fallback.tsx new file mode 100644 index 0000000..7c2e8ce --- /dev/null +++ b/packages/meta/src/image/demo/fallback.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Image } from 'antd'; + +const App: React.FC = () => ( + +); + +export default App; diff --git a/packages/meta/src/image/demo/imageRender.md b/packages/meta/src/image/demo/imageRender.md new file mode 100644 index 0000000..be37917 --- /dev/null +++ b/packages/meta/src/image/demo/imageRender.md @@ -0,0 +1,7 @@ +## zh-CN + +可以自定义预览内容。 + +## en-US + +You can customize the preview content. diff --git a/packages/meta/src/image/demo/imageRender.tsx b/packages/meta/src/image/demo/imageRender.tsx new file mode 100644 index 0000000..e667a44 --- /dev/null +++ b/packages/meta/src/image/demo/imageRender.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Image } from 'antd'; + +const App: React.FC = () => ( + ( +