diff --git a/src/components/LegendCategories/demos/default.tsx b/src/components/LegendCategories/demos/default.tsx new file mode 100644 index 00000000..4a9d72a7 --- /dev/null +++ b/src/components/LegendCategories/demos/default.tsx @@ -0,0 +1,31 @@ +import { LegendCategories } from '@antv/larkmap'; +import React from 'react'; + +export default () => { + return ( +
+ + + + + +
+ ); +}; diff --git a/src/components/LegendCategories/demos/map-default.tsx b/src/components/LegendCategories/demos/map-default.tsx new file mode 100644 index 00000000..33e625e8 --- /dev/null +++ b/src/components/LegendCategories/demos/map-default.tsx @@ -0,0 +1,25 @@ +import { LegendCategories, LarkMap, CustomControl } from '@antv/larkmap'; +import React from 'react'; + +const config = { + mapType: 'GaodeV1' as const, + mapOptions: { + style: 'light', + center: [120.210792, 30.246026] as [number, number], + zoom: 9, + // token: 'xxxx - token', + }, +}; +export default () => { + return ( + + + + + + ); +}; diff --git a/src/components/LegendCategories/index.less b/src/components/LegendCategories/index.less new file mode 100644 index 00000000..cf0ac42b --- /dev/null +++ b/src/components/LegendCategories/index.less @@ -0,0 +1,30 @@ +@cls-prefix: larkmap-legend-category; + +.@{cls-prefix} { + &__content { + display: flex; + align-items: center; + margin-bottom: 5px; + &__labels { + letter-spacing: 2; + text-transform: uppercase; + font-family: PingFangSC; + line-height: 2; + } + + &__shape { + margin-right: 15px; + } + + &__square { + width: 32px; + height: 15px + } + + &__circle { + width: 14px; + height: 14px; + border-radius: 50%; + } + } +} diff --git a/src/components/LegendCategories/index.md b/src/components/LegendCategories/index.md new file mode 100644 index 00000000..b51686fa --- /dev/null +++ b/src/components/LegendCategories/index.md @@ -0,0 +1,28 @@ +--- +toc: content +order: 5 +group: + title: 分析组件 + order: 3 +nav: + title: 组件 + path: /components +--- + +# 分类图例 - LegendCategories + +## 介绍 + +分类图例 + +## 代码演示 + +### 默认演示 + + + +### 在地图中展示 + + + + diff --git a/src/components/LegendCategories/index.tsx b/src/components/LegendCategories/index.tsx new file mode 100644 index 00000000..336f36d6 --- /dev/null +++ b/src/components/LegendCategories/index.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import classnames from 'classnames'; +import { getGradientColors } from './../../utils/color'; +import './index.less'; +import type { LegendCategoriesProps } from './types'; + +export const CLS_PREFIX = 'larkmap-legend-category'; + +export function LegendCategories(props: LegendCategoriesProps) { + const { labels, colors, geometryType = 'circle', isStrokeColor, style, className: cls_name } = props; + + function getColor(item: string) { + return isStrokeColor ? { border: `2px solid ${item}` } : { background: item }; + } + + function Conent(color: string[]) { + return ( +
+ {labels.map((item, index) => ( +
+
+
{item}
+
+ ))} +
+ ); + } + function Renders() { + if (Array.isArray(colors)) { + return Conent(colors); + } + const colorGradient = getGradientColors(colors.startColor, colors.endColor, labels.length); + return Conent(colorGradient); + } + return ; +} + +LegendCategories.defaultProps = { + geometryType: 'circle', + isStrokeColor: false, +}; diff --git a/src/components/LegendCategories/types.ts b/src/components/LegendCategories/types.ts new file mode 100644 index 00000000..8d7cb123 --- /dev/null +++ b/src/components/LegendCategories/types.ts @@ -0,0 +1,18 @@ +import type { CommonProps } from '../../types/common'; + +export interface LegendCategoriesProps extends CommonProps { + /** + * 图形形状 + * @default "circle" + */ + geometryType?: 'circle' | 'square'; + /** 图例项名称 */ + labels: string[]; + /** 图例项颜色 */ + colors: string[] | { startColor: string; endColor: string }; + /** + * 是否颜色填充 + * @default false + */ + isStrokeColor?: boolean; +} diff --git a/src/components/LegendIcon/demos/default.tsx b/src/components/LegendIcon/demos/default.tsx new file mode 100644 index 00000000..3022ea01 --- /dev/null +++ b/src/components/LegendIcon/demos/default.tsx @@ -0,0 +1,17 @@ +import { LegendIcon } from '@antv/larkmap'; +import React from 'react'; + +export default () => { + return ( +
+ +
+ ); +}; diff --git a/src/components/LegendIcon/demos/map-default.tsx b/src/components/LegendIcon/demos/map-default.tsx new file mode 100644 index 00000000..e4cccb86 --- /dev/null +++ b/src/components/LegendIcon/demos/map-default.tsx @@ -0,0 +1,29 @@ +import { LegendIcon, LarkMap, CustomControl } from '@antv/larkmap'; +import React from 'react'; + +const config = { + mapType: 'GaodeV1' as const, + mapOptions: { + style: 'light', + center: [120.210792, 30.246026] as [number, number], + zoom: 9, + // token: 'xxxx - token', + }, +}; +export default () => { + return ( + + + + + + ); +}; diff --git a/src/components/LegendIcon/index.less b/src/components/LegendIcon/index.less new file mode 100644 index 00000000..804ddc6d --- /dev/null +++ b/src/components/LegendIcon/index.less @@ -0,0 +1,15 @@ +@cls-prefix: larkmap-legend-icon; + +.@{cls-prefix} { + &__content { + display: flex; + align-items: center; + line-height: 2; + + &__icon { + width: 16px; + height: 16px; + margin-right: 10px + } + } +} diff --git a/src/components/LegendIcon/index.md b/src/components/LegendIcon/index.md new file mode 100644 index 00000000..4ab63493 --- /dev/null +++ b/src/components/LegendIcon/index.md @@ -0,0 +1,28 @@ +--- +toc: content +order: 6 +group: + title: 分析组件 + order: 3 +nav: + title: 组件 + path: /components +--- + +# 图标图例 - LegendIcon + +## 介绍 + +图标图例 + +## 代码演示 + +### 默认演示 + + + +### 在地图中展示 + + + + diff --git a/src/components/LegendIcon/index.tsx b/src/components/LegendIcon/index.tsx new file mode 100644 index 00000000..ea1f9b61 --- /dev/null +++ b/src/components/LegendIcon/index.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import classnames from 'classnames'; +import type { LegendIconProps } from './types'; +import './index.less'; + +export const CLS_PREFIX = 'larkmap-legend-icon'; + +export const LegendIcon = (props: LegendIconProps) => { + const { labels, icons, className: cls, style } = props; + return ( +
+ {labels.map((item, index) => ( +
+ +
{item}
+
+ ))} +
+ ); +}; diff --git a/src/components/LegendIcon/types.ts b/src/components/LegendIcon/types.ts new file mode 100644 index 00000000..1764d07c --- /dev/null +++ b/src/components/LegendIcon/types.ts @@ -0,0 +1,8 @@ +import type { CommonProps } from '../../types/common'; + +export interface LegendIconProps extends CommonProps { + /** 图例项名称 */ + labels: string[]; + /** 图例项图标 */ + icons: string[]; +} diff --git a/src/components/LegendProportion/demos/default.tsx b/src/components/LegendProportion/demos/default.tsx new file mode 100644 index 00000000..d02491f2 --- /dev/null +++ b/src/components/LegendProportion/demos/default.tsx @@ -0,0 +1,12 @@ +import { LegendProportion } from '@antv/larkmap'; +import React from 'react'; +import './index.less'; + +export default () => { + return ( +
+ + +
+ ); +}; diff --git a/src/components/LegendProportion/demos/index.less b/src/components/LegendProportion/demos/index.less new file mode 100644 index 00000000..cca0e075 --- /dev/null +++ b/src/components/LegendProportion/demos/index.less @@ -0,0 +1,6 @@ +.demo_cls{ + background-color: #fff; + border-radius: 4px; + padding: 5px; + width: 160px; +} diff --git a/src/components/LegendProportion/demos/map-default.tsx b/src/components/LegendProportion/demos/map-default.tsx new file mode 100644 index 00000000..1c840a3b --- /dev/null +++ b/src/components/LegendProportion/demos/map-default.tsx @@ -0,0 +1,22 @@ +import { LegendProportion, LarkMap, CustomControl } from '@antv/larkmap'; +import React from 'react'; +import './index.less'; + +const config = { + mapType: 'GaodeV1' as const, + mapOptions: { + style: 'light', + center: [120.210792, 30.246026] as [number, number], + zoom: 9, + // token: 'xxxx - token', + }, +}; +export default () => { + return ( + + + + + + ); +}; diff --git a/src/components/LegendProportion/index.less b/src/components/LegendProportion/index.less new file mode 100644 index 00000000..675c50a3 --- /dev/null +++ b/src/components/LegendProportion/index.less @@ -0,0 +1,24 @@ +@cls-prefix: larkmap-legend-proportion; + +.@{cls-prefix} { + display: flex; + align-items: flex-end; + + &__circlebox { + position: relative; + display: flex; + justify-content: end; + align-items: end; + margin-right: 10px; + + &__item { + border-radius: 50%; + position: absolute; + border: 1px solid #e1e3e4; + } + } + + &__labelitem{ + font-size: 14px; + } +} diff --git a/src/components/LegendProportion/index.md b/src/components/LegendProportion/index.md new file mode 100644 index 00000000..917559eb --- /dev/null +++ b/src/components/LegendProportion/index.md @@ -0,0 +1,28 @@ +--- +toc: content +order: 7 +group: + title: 分析组件 + order: 3 +nav: + title: 组件 + path: /components +--- + +# 面积图例 - LegendProportion + +## 介绍 + +面积图例 + +## 代码演示 + +### 默认演示 + + + +### 在地图中展示 + + + + diff --git a/src/components/LegendProportion/index.tsx b/src/components/LegendProportion/index.tsx new file mode 100644 index 00000000..16bc30ef --- /dev/null +++ b/src/components/LegendProportion/index.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { isNumber, isNaN, uniqueId } from 'lodash'; +import classnames from 'classnames'; +import type { LegendProportionProp } from './types'; +import './index.less'; + +export const CLS_PREFIX = 'larkmap-legend-proportion'; + +export const LegendProportion = (props: LegendProportionProp) => { + const { labels, fillColor = '#f9f9f9', className: cls, style } = props; + const [min, max] = labels; + const circleSizes = [86, 62, 38, 24]; + /** + * 计算跨度 + * @param min 最小值 + * @param max 最大值 + * @returns + */ + function calculateSteps(newMin: number, newMax: number) { + const gap = (newMax - newMin) / 4; + const step1 = Math.floor(newMin + gap); + const step2 = Math.floor(newMax - gap); + return [step1, step2]; + } + + /** + * 生成label范围 + * @returns + */ + function generateRange() { + let newMin = min, + newMax = max; + if (!isNumber(newMin) || !isNumber(newMin)) { + newMin = newMax = 0; + } + if (isNaN(newMin)) { + newMin = 0; + } + + if (isNaN(newMax)) { + newMax = 0; + } + + const steps = calculateSteps(newMin, newMax); + const range = [{ prefix: 'MIN:', val: newMin }, ...steps, { prefix: 'MAX:', val: newMax }]; + return range; + } + + function CircleBox() { + return ( +
+ {circleSizes.map((item) => ( +
+ ))} +
+ ); + } + + function LabelBox() { + return ( +
+ {generateRange() + .reverse() + .map((item: any) => ( +
+ {item?.prefix} + {typeof item === 'number' ? item : item.val} +
+ ))} +
+ ); + } + + return ( +
+ + +
+ ); +}; + +LegendProportion.defaultProps = { + fillColor: '#f9f9f9', +}; diff --git a/src/components/LegendProportion/types.ts b/src/components/LegendProportion/types.ts new file mode 100644 index 00000000..e08d25ac --- /dev/null +++ b/src/components/LegendProportion/types.ts @@ -0,0 +1,11 @@ +import type { CommonProps } from '../../types/common'; + +export interface LegendProportionProp extends CommonProps { + /**图例项名称 */ + labels: number[]; + /** + * 填充颜色 + * @default "#f9f9f9" + */ + fillColor?: string; +} diff --git a/src/components/LegendRamp/demos/default.tsx b/src/components/LegendRamp/demos/default.tsx new file mode 100644 index 00000000..39e93ebb --- /dev/null +++ b/src/components/LegendRamp/demos/default.tsx @@ -0,0 +1,34 @@ +import { LegendRamp } from '@antv/larkmap'; +import React from 'react'; + +export default () => { + return ( +
+ + +
+ ); +}; diff --git a/src/components/LegendRamp/demos/map-default.tsx b/src/components/LegendRamp/demos/map-default.tsx new file mode 100644 index 00000000..1153f36d --- /dev/null +++ b/src/components/LegendRamp/demos/map-default.tsx @@ -0,0 +1,33 @@ +import { LegendRamp, LarkMap, CustomControl } from '@antv/larkmap'; +import React from 'react'; + +const config = { + mapType: 'GaodeV1' as const, + mapOptions: { + style: 'light', + center: [120.210792, 30.246026] as [number, number], + zoom: 9, + // token: 'xxxx - token', + }, +}; +export default () => { + return ( + + + + + + ); +}; diff --git a/src/components/LegendRamp/index.less b/src/components/LegendRamp/index.less new file mode 100644 index 00000000..b1d862df --- /dev/null +++ b/src/components/LegendRamp/index.less @@ -0,0 +1,32 @@ +@cls-prefix: larkmap-legend-ramp; + +.@{cls-prefix} { + &__labelbar { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 8px; + } + + &__continuousBar { + height: 10px; + border-radius: 6px + } + + + &__equidistantBar { + display: flex; + + &__bar { + height: 10px; + } + + &__bar:first-child { + border-radius: 6px 0 0 6px; + } + + &__bar:last-child { + border-radius: 0 6px 6px 0; + } + } +} diff --git a/src/components/LegendRamp/index.md b/src/components/LegendRamp/index.md new file mode 100644 index 00000000..6d313434 --- /dev/null +++ b/src/components/LegendRamp/index.md @@ -0,0 +1,28 @@ +--- +toc: content +order: 8 +group: + title: 分析组件 + order: 3 +nav: + title: 组件 + path: /components +--- + +# 色带图例 - LegendRamp + +## 介绍 + +色带图例 + +## 代码演示 + +### 默认演示 + + + +### 在地图中展示 + + + + diff --git a/src/components/LegendRamp/index.tsx b/src/components/LegendRamp/index.tsx new file mode 100644 index 00000000..31cb8c0f --- /dev/null +++ b/src/components/LegendRamp/index.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import classnames from 'classnames'; +import type { LegendRampProps } from './types'; +import './index.less'; +import { getGradientColors } from './../../utils/color'; + +export const CLS_PREFIX = 'larkmap-legend-ramp'; + +export function LegendRamp(props: LegendRampProps) { + const { isContinuous, labels, colors, lableUnit, className: cls, style, barWidth = 200 } = props; + + function Continuous({ gradient }: Record) { + return ( +
+ ); + } + + function Equidistant({ color }: Record) { + return ( +
+ {color.map((item: string) => ( +
+ ))} +
+ ); + } + + function Conent(color: string[]) { + const gradient = color.join(','); + const [min, max] = [labels[0], labels[labels.length - 1]]; + return ( +
+ {isContinuous ? : } +
+
{`${min}${lableUnit ?? ''} ${isContinuous ? '' : '<'}`}
+
{`${isContinuous ? '' : '≥'} ${max}${lableUnit ?? ''}`}
+
+
+ ); + } + + function Renders() { + if (Array.isArray(colors)) { + return Conent(colors); + } + const colorGradient = getGradientColors(colors.startColor, colors.endColor, labels.length); + return Conent(colorGradient); + } + + return ; +} + +LegendRamp.defaultProps = { + isContinuous: false, + barWidth: 200, +}; diff --git a/src/components/LegendRamp/types.ts b/src/components/LegendRamp/types.ts new file mode 100644 index 00000000..355cec74 --- /dev/null +++ b/src/components/LegendRamp/types.ts @@ -0,0 +1,20 @@ +import type { CommonProps } from '../../types/common'; + +export interface LegendRampProps extends CommonProps { + /** 图例项名称 */ + labels: string[] | number[]; + /** 图例项单位 */ + lableUnit: string; + /**图例项颜色 */ + colors: { startColor: string; endColor: string } | string[]; + /** + * 是否连续 + * @default false + */ + isContinuous?: boolean; + /** + * 图例项宽度 + * @default 200 + */ + barWidth?: number; +} diff --git a/src/index.ts b/src/index.ts index 27ac4bd8..1bcaf892 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,6 +38,17 @@ export { Marker } from './components/Marker'; export { MarkerProps } from './components/Marker/types'; export { Popup } from './components/Popup'; export { PopupProps } from './components/Popup/types'; +/** + * 图例组件 + */ +export { LegendIcon } from './components/LegendIcon'; +export { LegendIconProps } from './components/LegendIcon/types'; +export { LegendProportion } from './components/LegendProportion'; +export { LegendProportionProp } from './components/LegendProportion/types'; +export { LegendCategories } from './components/LegendCategories'; +export { LegendCategoriesProps } from './components/LegendCategories/types'; +export { LegendRamp } from './components/LegendRamp'; +export { LegendRampProps } from './components/LegendRamp/types'; /** * 绘制组件 diff --git a/src/utils/color.ts b/src/utils/color.ts new file mode 100644 index 00000000..0e35bb90 --- /dev/null +++ b/src/utils/color.ts @@ -0,0 +1,33 @@ +import Color from 'color'; +import { forEach, round } from 'lodash-es'; + +export const getGradientColors = (colorStart: string, colorEnd: string, count = 5): string[] => { + const singleColors: string[] = [colorStart]; // 初始颜色 + + const colorMinInt = Color(colorStart).object(); + const colorMaxInt = Color(colorEnd).object(); + + if (count < 2) { + return count < 1 ? [] : singleColors; + } + + const diffNumber = { + r: (colorMinInt.r - colorMaxInt.r) / (count - 1), + g: (colorMinInt.g - colorMaxInt.g) / (count - 1), + b: (colorMinInt.b - colorMaxInt.b) / (count - 1), + }; + + forEach(new Array(count - 2), (item, index) => + singleColors.push( + Color({ + r: round(colorMinInt.r - diffNumber.r * (index + 1), 0), + g: round(colorMinInt.g - diffNumber.g * (index + 1), 0), + b: round(colorMinInt.b - diffNumber.b * (index + 1), 0), + }).hex(), + ), + ); + + singleColors.push(colorEnd); + + return singleColors; +};