src/aop/controller/legend.ts (198 lines of code) (raw):
import RawLegendController from '@antv/g2/esm/chart/controller/legend';
import { Geometry } from '@antv/g2';
import { Attribute, CategoryLegend, Scale, ListItem } from '@antv/g2/esm/dependents';
import { LegendCfg } from '@antv/g2/esm/interface';
// 引入自定义的图例组件
import ReactLegend from '../component/reactLegend';
import { FullCrossName } from '../../constants';
import { View } from '@antv/g2/esm';
import { merge } from '../../common/common';
import { getStatistics } from '../../common/chartRefs';
import omit from 'lodash/omit';
// @ts-ignore
class WidgetsLegendController extends RawLegendController {
private parentDom: HTMLElement;
private legendContainer: HTMLElement;
constructor(view: View) {
super(view);
this.parentDom = this.view.getCanvas().get('el').parentNode.parentNode.parentNode;
this.legendContainer = document.createElement('div');
this.legendContainer.className = `${FullCrossName} widgets-legend`;
this.parentDom.appendChild(this.legendContainer);
}
layout() {
const widgetsCtx = (this.view as any)?.widgetsCtx;
if (!widgetsCtx?.size) {
return;
}
const [w, h] = widgetsCtx?.size;
const legendConfig: any = {};
const globalBaseConfig = widgetsCtx?.context?.defaultConfig?.baseConfig;
const globalComsConfig = widgetsCtx?.context?.defaultConfig?.[widgetsCtx.chartName.replace('G2', '')] ?? {};
merge(
legendConfig,
widgetsCtx?.defaultConfig?.legend ?? {},
globalBaseConfig?.legend ?? {},
globalComsConfig?.legend ?? {},
widgetsCtx?.props?.config?.legend ?? {},
);
if (
this.legendContainer &&
!widgetsCtx?.isEmpty &&
!widgetsCtx?.props?.loading &&
!widgetsCtx?.props?.errorInfo &&
legendConfig?.visible !== false &&
(legendConfig?.table || legendConfig?.gradient || legendConfig?.foldable)
) {
this.legendContainer.style.visibility = 'visible';
const legendElement = this.legendContainer?.childNodes?.[0];
const position = (legendConfig?.position ?? 'bottom').split('-')[0];
// 根据legend配置项计算图表宽高
let size = [w, h];
let legendSize = [0, 0];
let legendMaxSize = null;
if (legendConfig?.table) {
// 根据数据量计算高度
// 目前暂时对多重圆环进行特殊处理,待规则统一梳理后,整理数据类型
const dataType = widgetsCtx.chartName === 'G2MultiPie' ? 'treeNode' : 'common';
let items = getStatistics(widgetsCtx.chart, [], widgetsCtx?.legendField || 'type', dataType);
// 增加特殊逻辑,如果目前包含2层以上,则只展示第一层数据
if (dataType === 'treeNode') {
let filterData = [...(this?.view?.options?.data ?? [])];
const firstDepthCount = filterData.filter((sub: any) => sub.depth === 1)?.length;
const secondDepthCount = filterData.filter((sub: any) => sub.depth === 2)?.length;
if (firstDepthCount > 0 && secondDepthCount > 0) {
filterData = filterData.filter((sub: any) => sub.depth === 2);
} else {
filterData = [];
}
const filterDataIdList = filterData.map((sub: any) => sub.id);
items = omit(items, filterDataIdList);
}
const num = Object.keys(items).length ?? 0;
if (position === 'right') {
size = [w / 2, h];
const height = legendConfig?.table?.style?.height ?? Math.min(h, 20 * (num + 1));
legendSize = [w / 2, height];
legendMaxSize = [w / 2, h];
} else {
const height = legendConfig?.table?.style?.height ?? Math.min(h * 0.3, 20 * (num + 1));
size = [w, h - height];
legendSize = [w, height];
legendMaxSize = [w, height];
}
} else if (legendConfig?.gradient) {
size = [w, h - 50];
legendSize = [w, 50];
legendMaxSize = [w, 50];
} else if (legendConfig?.foldable) {
if (widgetsCtx.legendFolded || widgetsCtx.legendFolded === undefined) {
size = [w, h - 20];
legendSize = [w, 20];
} else {
// 折叠型legend的展开逻辑在FoldableLegend内实现
if (legendElement) {
legendElement.style.width = 'auto';
legendElement.style.height = 'auto';
}
return;
}
}
if (this.view?.canvas?.get('el')) {
// @ts-ignore
this.view?.changeSize(size[0], size[1]);
}
// 设置图表宽高
const chartContainer = this.view.getCanvas().get('el')?.parentNode?.parentNode;
if (chartContainer) {
chartContainer.style.width = `${size[0]}px`;
chartContainer.style.height = `${size[1]}px`;
}
// 设置legend宽高
if (legendElement) {
legendElement.style.width = `${legendSize[0]}px`;
legendElement.style.height = `${legendSize[1]}px`;
if (legendMaxSize) {
legendElement.style.maxHeight = `${legendMaxSize[1]}px`;
}
// 记录legend尺寸
widgetsCtx.legendSize = legendSize;
}
} else {
if (this.legendContainer) {
this.legendContainer.style.visibility = 'hidden';
}
super.layout();
}
}
// @ts-ignore
private createCustomLegend(geometry: Geometry, attr: Attribute, scale: Scale, legendOption: LegendCfg) {
// 直接使用 分类图例渲染
// @ts-ignore
const cfg = this.getCategoryCfg(geometry, attr, scale, legendOption, true);
return new CategoryLegend(cfg);
}
// @ts-ignore
private createCategoryLegend(geometry: Geometry, attr: Attribute, scale: Scale, legendOption: any) {
// @ts-ignore
const cfg = this.getCategoryCfg(geometry, attr, scale, legendOption);
this.legendContainer = this.parentDom.getElementsByClassName('widgets-legend')?.[0] as HTMLElement;
// // 当以下配置项生效时,使用g2 原生legend
// if (
// !table &&
// !foldable &&
// (autoCollapse ||
// collapseRow ||
// showData ||
// marker ||
// allowAllCanceled ||
// hoverable === false ||
// onHover ||
// clickable === false ||
// onClick ||
// customConfig ||
// maxWidth ||
// maxHeight ||
// maxWidthRatio ||
// maxHeightRatio ||
// useReverseChecked === false ||
// dodge)
// ) {
// console.log('normal', this.view?.widgetsCtx?.props?.config?.legend);
// // 普通legend
// return new CategoryLegend(cfg);
// }
// 使用自定义的legend:列表型legend、折叠型legend或阶梯状legend
if (legendOption?.table || legendOption?.foldable || legendOption?.gradient) {
// if (!this.legendContainer) {
// this.legendContainer = document.createElement('div');
// this.legendContainer.className = `${FullCrossName} widgets-legend`;
// this.parentDom.appendChild(this.legendContainer);
// }
const position = legendOption?.position?.split('-')?.[0];
const align = legendOption?.position?.split('-')?.[1] ?? 'center';
const directionX = ['top', 'bottom'].includes(position)
? align === 'center'
? 'center'
: align === 'left' || align === 'top'
? 'flex-start'
: 'flex-end'
: 'flex-start';
const directionY =
align === 'center' ? 'center' : align === 'left' || align === 'top' ? 'flex-start' : 'flex-end';
this.legendContainer.style.cssText = `width: 100%; display: flex; justify-content: ${directionX}; align-items: ${directionY}; overflow-x: auto;overflow-y: hidden;`;
this.legendContainer.style.visibility = 'visible';
return new ReactLegend({
cfg,
container: this.legendContainer,
chart: this.view,
legendConfig: legendOption,
});
}
this.legendContainer.style.visibility = 'hidden';
// 普通legend
return new CategoryLegend(cfg);
}
public clear() {
super.clear();
if (this.legendContainer) {
this.legendContainer.style.visibility = 'hidden';
}
}
public destroy() {
super.destroy();
// 销毁之前的legend legendContainer
if (this.legendContainer) {
this.parentDom.removeChild(this.legendContainer);
this.legendContainer = null;
}
}
}
export default WidgetsLegendController;