Skip to content

Commit

Permalink
feat(components):add Legend Component
Browse files Browse the repository at this point in the history
  • Loading branch information
pomelo-nwu committed Jan 10, 2021
1 parent b3aa9ad commit d578d91
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 0 deletions.
88 changes: 88 additions & 0 deletions packages/graphin-components/src/Legend/Node.tsx
@@ -0,0 +1,88 @@
import { GraphinContext } from '@antv/graphin/dist';
import React from 'react';
import './index.less';

export interface LegendOption {
/** 标签 */
label: string;
/** 颜色 */
color: string;
/** 值 */
value: string;
/** 是否选中 */
checked?: boolean;
}
export interface LegendProps {
options: LegendOption[];
style?: React.CSSProperties;
onChange?: (checked: LegendOption, newOptions: LegendOption[], props: any) => any; // eslint-disable-line
}

const LegendNode: React.FunctionComponent<LegendProps> = (props) => {
const {
legend,
// apis,
graph,
} = React.useContext(GraphinContext);
// 依然存在两个legend,graphin.context只是一个全局对象
const { onChange = () => {}, style } = props;

const { options: defaultOptions, dataMap } = legend.node;

const [state, setState] = React.useState({
options: defaultOptions,
});

const { options } = state;

const handleClick = (option: LegendOption) => {
const checkedValue = { ...option, checked: !option.checked };
const result = options.map((c) => {
const matched = c.value === option.value;
return matched ? checkedValue : c;
});
setState({
options: result,
});
const nodes = dataMap.get(checkedValue.value);

/** highlight */
// const nodesId = nodes.map((c) => c.id);
// apis.highlightNodeById(nodesId);

nodes.forEach((node) => {
graph.setItemState(node.id, 'active', checkedValue.checked);
graph.setItemState(node.id, 'inactice', !checkedValue.checked);
});

/** 给用户的回调函数 */
onChange(checkedValue, result, props);
};

return (
<ul className="graphin-components-legend-content" style={style}>
{options.map((option: LegendOption) => {
const { label, checked, color } = option;
return (
<li // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
key={option.value}
className="item"
onClick={() => {
handleClick(option);
}}
onKeyDown={() => {
handleClick(option);
}}
>
<span className="dot" style={{ background: checked ? color : '#ddd' }} />
<span className="label" style={{ color: checked ? '#000000d9' : '#ddd' }}>
{label}
</span>
</li>
);
})}
</ul>
);
};

export default LegendNode;
24 changes: 24 additions & 0 deletions packages/graphin-components/src/Legend/index.less
@@ -0,0 +1,24 @@
ul.graphin-components-legend-content {
display: block;
position: relative;
margin: 0px;
padding: 0px;
li.item {
display: inline-block;
vertical-align: top;
margin: 5px 10px;
cursor: pointer;
& > .dot {
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
}
& > .label {
padding: 0px 5px;
font-size: 12px;
display: inline-block;
vertical-align: baseline;
}
}
}
175 changes: 175 additions & 0 deletions packages/graphin-components/src/Legend/index.tsx
@@ -0,0 +1,175 @@
import React from 'react';
import {
GraphinContext,
Graph,
GraphData,
TreeGraphData,
NodeConfig,
EdgeConfig,
GraphinContextType,
} from '@antv/graphin';
import Node from './Node';

export interface LegendProps {
/** 绑定的类型 */
bindType: 'node' | 'edge';
/**
* @description 分类映射的Key值
*/
sortKey: string;
/**
* @description 颜色映射的Key值
* @default "style.fill"
*/
colorKey?: string;
}
export interface OptionType {
/** 颜色 */
color: string;
/** 值 */
value: string | number;
/** 标签 */
label: string;
/** 是否选中 */
checked: boolean;
}

const getEnumValue = (keyString: string, data) => {
const keyArray = keyString.split('.');
const enumValue = keyArray.reduce((acc: {}, curr) => {
return acc[curr] || {};
}, data) as string;
return enumValue;
};

const calculate = ({
bindType,
sortKey,
graph,
colorKey,
}: {
bindType: LegendProps['bindType'];
sortKey: LegendProps['sortKey'];
graph: Graph;
colorKey: string;
}) => {
const data = graph.save();
const treeData = data as TreeGraphData;
const graphData = data as GraphData;
const nodeMapByMapKey: Map<string | number, NodeConfig[]> = new Map();
const edgeMapByMapKey: Map<string | number, EdgeConfig[]> = new Map();
/** 暂时不支持treeGraph的legend */
if (treeData.children) {
console.error('not support tree graph');
return {
dataMap: new Map(),
options: {},
};
}

const { nodes = [], edges = [] } = graphData;

if (bindType === 'node') {
nodes.forEach((node) => {
/** 得到枚举值 */
const enumValue = getEnumValue(sortKey, node);
/** 按照枚举值重新将节点存放 */
const current = nodeMapByMapKey.get(enumValue);
if (current) {
nodeMapByMapKey.set(enumValue, [...current, node]);
} else {
nodeMapByMapKey.set(enumValue, [node]);
}
});
/** 计算legend.content 的 options */
const keys = [...nodeMapByMapKey.keys()];
const options = keys.map((key) => {
const node = (nodeMapByMapKey.get(key) || [{}])[0];
const color = getEnumValue(colorKey, node);
return {
/** 颜色 */
color,
/** 值 */
value: key,
/** 标签 */
label: key,
/** 是否选中 */
checked: true,
};
});
return {
dataMap: nodeMapByMapKey,
options,
};
}
// if (bindType === 'edge') {
edges.forEach((edge) => {
/** 得到枚举值 */
const enumValue = getEnumValue(sortKey, edge);

const current = edgeMapByMapKey.get(enumValue);
if (current) {
edgeMapByMapKey.set(enumValue, [...current, edge]);
} else {
edgeMapByMapKey.set(enumValue, [edge]);
}
});
/** 计算legend.content 的 options */
const keys = [...edgeMapByMapKey.keys()];
const options = keys.map((key) => {
const edge = (edgeMapByMapKey.get(key) || [{}])[0];
const color = getEnumValue(colorKey, edge);
return {
/** 颜色 */
color,
/** 值 */
value: key,
/** 标签 */
label: key,
/** 是否选中 */
checked: true,
};
});

return { dataMap: edgeMapByMapKey, options };
};
export interface LegendContextType extends GraphinContextType {
legend: {
node?: {
bindType: string;
sortKey: string;
colorKey: string;
dataMap: Map<string, NodeConfig[]>;
options: OptionType[];
};
edge?: {
bindType: string;
sortKey: string;
colorKey: string;
dataMap: Map<string, EdgeConfig[]>;
options: OptionType[];
};
};
}
const Legend: React.FunctionComponent<LegendProps> & { Node: typeof Node } = (props) => {
const graphin = React.useContext<GraphinContextType>(GraphinContext);
const { graph } = graphin;
const { bindType, sortKey, children, colorKey = 'style.fill' } = props;
const { dataMap, options } = calculate({ bindType, sortKey, graph, colorKey });

graphin.legend = {
...graphin.legend,
// 一个Graphin组件下,最多仅有2个Legend组件:node和edge
[bindType]: {
bindType,
sortKey,
colorKey,
dataMap,
options,
},
};
return <div className="graphin-components-legend">{children}</div>;
};

Legend.Node = Node;
export default Legend;
1 change: 1 addition & 0 deletions packages/graphin-components/src/index.ts
Expand Up @@ -11,3 +11,4 @@ export { default as Tooltip } from './Tooltip';
export { default as FishEye } from './FishEye';
export { default as Toolbar } from './Toolbar';
export { default as MiniMap } from './MiniMap';
export { default as Legend } from './Legend';

0 comments on commit d578d91

Please sign in to comment.