import React, { Component } from 'react'; import { Chart, Tooltip, Geom, Coord } from 'bizcharts'; import { DataView } from '@antv/data-set'; import { Divider } from 'antd'; import classNames from 'classnames'; import ReactFitText from 'react-fittext'; import Debounce from 'lodash-decorators/debounce'; import Bind from 'lodash-decorators/bind'; import autoHeight from '../autoHeight'; import styles from './index.less'; export interface IPieProps { animate?: boolean; color?: string; colors?: string[]; selected?: boolean; height?: number; margin?: [number, number, number, number]; hasLegend?: boolean; padding?: [number, number, number, number]; percent?: number; data?: Array<{ x: string | string; y: number; }>; inner?: number; lineWidth?: number; forceFit?: boolean; style?: React.CSSProperties; className?: string; total?: React.ReactNode | number | (() => React.ReactNode | number); title?: React.ReactNode; tooltip?: boolean; valueFormat?: (value: string) => string | React.ReactNode; subTitle?: React.ReactNode; } interface IPieState { legendData: Array<{ checked: boolean; x: string; color: string; percent: number; y: string }>; legendBlock: boolean; } class Pie extends Component { state: IPieState = { legendData: [], legendBlock: false, }; requestRef: number | undefined; root!: HTMLDivElement; chart: G2.Chart | undefined; componentDidMount() { window.addEventListener( 'resize', () => { this.requestRef = requestAnimationFrame(() => this.resize()); }, { passive: true }, ); } componentDidUpdate(preProps: IPieProps) { const { data } = this.props; if (data !== preProps.data) { // because of charts data create when rendered // so there is a trick for get rendered time this.getLegendData(); } } componentWillUnmount() { if (this.requestRef) { window.cancelAnimationFrame(this.requestRef); } window.removeEventListener('resize', this.resize); if (this.resize) { (this.resize as any).cancel(); } } getG2Instance = (chart: G2.Chart) => { this.chart = chart; requestAnimationFrame(() => { this.getLegendData(); this.resize(); }); }; // for custom lengend view getLegendData = () => { if (!this.chart) return; const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形 if (!geom) return; const items = geom.get('dataArray') || []; // 获取图形对应的 const legendData = items.map((item: { color: any; _origin: any }[]) => { /* eslint no-underscore-dangle:0 */ const origin = item[0]._origin; origin.color = item[0].color; origin.checked = true; return origin; }); this.setState({ legendData, }); }; handleRoot = (n: HTMLDivElement) => { this.root = n; }; handleLegendClick = (item: any, i: string | number) => { const newItem = item; newItem.checked = !newItem.checked; const { legendData } = this.state; legendData[i] = newItem; const filteredLegendData = legendData.filter(l => l.checked).map(l => l.x); if (this.chart) { this.chart.filter('x', val => filteredLegendData.indexOf(val + '') > -1); } this.setState({ legendData, }); }; // for window resize auto responsive legend @Bind() @Debounce(300) resize() { const { hasLegend } = this.props; const { legendBlock } = this.state; if (!hasLegend || !this.root) { window.removeEventListener('resize', this.resize); return; } if ( this.root && this.root.parentNode && (this.root.parentNode as HTMLElement).clientWidth <= 380 ) { if (!legendBlock) { this.setState({ legendBlock: true, }); } } else if (legendBlock) { this.setState({ legendBlock: false, }); } } render() { const { valueFormat, subTitle, total, hasLegend = false, className, style, height = 0, forceFit = true, percent, color, inner = 0.75, animate = true, colors, lineWidth = 1, } = this.props; const { legendData, legendBlock } = this.state; const pieClassName = classNames(styles.pie, className, { [styles.hasLegend]: !!hasLegend, [styles.legendBlock]: legendBlock, }); const { data: propsData, selected: propsSelected = true, tooltip: propsTooltip = true, } = this.props; let data = propsData || []; let selected = propsSelected; let tooltip = propsTooltip; const defaultColors = colors; data = data || []; selected = selected || true; tooltip = tooltip || true; let formatColor; const scale = { x: { type: 'cat', range: [0, 1], }, y: { min: 0, }, }; if (percent || percent === 0) { selected = false; tooltip = false; formatColor = (value: string) => { if (value === '占比') { return color || 'rgba(24, 144, 255, 0.85)'; } return '#F0F2F5'; }; data = [ { x: '占比', y: parseFloat(percent + ''), }, { x: '反比', y: 100 - parseFloat(percent + ''), }, ]; } const tooltipFormat: [string, (...args: any[]) => { name?: string; value: string }] = [ 'x*percent', (x: string, p: number) => ({ name: x, value: `${(p * 100).toFixed(2)}%`, }), ]; const padding = [12, 0, 12, 0] as [number, number, number, number]; const dv = new DataView(); dv.source(data).transform({ type: 'percent', field: 'y', dimension: 'x', as: 'percent', }); return (
{!!tooltip && } {(subTitle || total) && (
{subTitle &&

{subTitle}

} {/* eslint-disable-next-line */} {total && (
{typeof total === 'function' ? total() : total}
)}
)}
{hasLegend && ( )}
); } } export default autoHeight()(Pie);