Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 新增 menu config #1512

Merged
merged 2 commits into from
Aug 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/graphs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
`2022-08-17`

- 🔥 新增 `menuCfg` 配置
- 🐞 [关系图透传节点配置,支持 image 等节点](https://github.com/ant-design/ant-design-charts/issues/1489)

## 1.2.3
Expand Down
2 changes: 1 addition & 1 deletion packages/graphs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"lint:prettier": "npm run prettier && git diff && prettier --version && prettier --check \"src/**/**.{js,jsx,tsx,ts,less,md,json}\" --end-of-line auto",
"prettier": "prettier --write \"**/**.{js,jsx,tsx,ts,less,md,json}\"",
"test": "jest",
"test:live": "cross-env DEBUG_MODE=1 jest --watch ./tests/utils/error-boundary.test.tsx --no-cache"
"test:live": "cross-env DEBUG_MODE=1 jest --watch ./tests/graphs/menu-spec.tsx --no-cache"
},
"dependencies": {
"@antv/g6": "^4.2.4",
Expand Down
45 changes: 33 additions & 12 deletions packages/graphs/src/hooks/useGraphs.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import G6, { IEdge, INode, ModeType } from '@antv/g6';
import { isEqual, isFunction, isObject, isString } from '@antv/util';
import { useEffect, useRef } from 'react';
import React, { useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
import { createToolbar } from '../components/toolbar';
import { createToolbar, Menu } from '../plugins';
import { ArrowConfig, CardNodeCfg, CommonConfig, EdgeConfig, NodeConfig, StateStyles } from '../interface';
import {
bindDefaultEvents,
Expand Down Expand Up @@ -244,8 +244,19 @@ export default function useGraph(graphClass: string, config: any, extra: { name?
const { name = '' } = extra;
const graphSize = getGraphSize(width, height, container);
const plugins = [];
const { nodeCfg, edgeCfg, behaviors, layout, animate, autoFit, fitCenter, onReady, tooltipCfg, customLayout } =
config;
const {
nodeCfg,
edgeCfg,
behaviors,
layout,
animate,
autoFit,
fitCenter,
onReady,
tooltipCfg,
customLayout,
menuCfg,
} = config;

const {
type: nodeType,
Expand All @@ -266,26 +277,36 @@ export default function useGraph(graphClass: string, config: any, extra: { name?
label: labelCfg,
edgeStateStyles,
} = edgeCfg ?? {};

const createNode = (children: React.ReactNode, className: string) => {
const mountPoint = document.createElement('div');
mountPoint.className = className;
ReactDOM.render(children as React.ReactElement, mountPoint);
return mountPoint;
};
// tooltip
if (tooltipCfg && isFunction(tooltipCfg.customContent)) {
const { customContent, ...rest } = tooltipCfg;
const createNode = (children: React.ReactNode) => {
const mountPoint = document.createElement('div');
mountPoint.className = 'g6-tooltip';
ReactDOM.render(children as React.ReactElement, mountPoint);
return mountPoint;
};
const tooltipPlugin = new G6.Tooltip({
offsetX: 10,
offsetY: 20,
itemTypes: ['node'],
...rest,
getContent(e) {
return createNode(customContent(e.item.getModel()));
return createNode(customContent(e.item.getModel()), 'g6-tooltip');
},
});
plugins.push(tooltipPlugin);
}
// menu
if (menuCfg && isFunction(menuCfg.customContent)) {
const menuPlugin = new Menu({
offsetX: 16 + 10,
offsetY: 0,
itemTypes: ['node'],
...menuCfg,
});
plugins.push(menuPlugin);
}
graphRef.current = new G6[graphClass]({
container: container.current as any,
width: graphSize[0],
Expand Down
42 changes: 29 additions & 13 deletions packages/graphs/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ import {
ShapeStyle,
StateStyles,
TreeGraphData as G6TreeGraphData,
Item,
} from '@antv/g6';

import { MenuConfig } from './plugins';

export interface GraphContainerConfig {
style?: React.CSSProperties;
className?: string;
Expand Down Expand Up @@ -123,16 +126,35 @@ export interface BadgeCfg {
style?: IShapeStyle;
}

export interface ToolbarCfg {
/** toolbar css 类名 */
type PluginContainer<T> = {
/** tooltip css 类名 */
className?: string;
/** toolbar 容器样式 */
/** tooltip 容器样式 */
style?: React.CSSProperties;
/** tooltip 容器,默认和 canvas 使用同一父容器 */
container?: HTMLDivElement | string | null;
/** 自定义模板 */
customContent: (item: T) => React.ReactElement;
};

export interface ToolbarCfg
extends Omit<
PluginContainer<{
zoomIn: () => void;
zoomOut: () => void;
toggleFullscreen: () => void;
fullscreen: boolean;
}>,
'container'
> {
/** 是否展示 */
show?: boolean;
/** 缩放因子 */
zoomFactor?: number;
/** renderIcon,自定义渲染 */
/**
* @title renderIcon,自定义渲染
* @deprecated
*/
renderIcon?: ({
zoomIn,
zoomOut,
Expand Down Expand Up @@ -226,15 +248,7 @@ export interface MarkerCfg {
style?: ShapeStyle;
}

export interface TooltipCfg {
/** tooltip css 类名 */
className?: string;
/** tooltip 容器样式 */
style?: React.CSSProperties;
/** 自定义模板 */
customContent: (item?: NodeConfig) => React.ReactElement;
/** tooltip 容器,默认和 canvas 使用同一父容器 */
container?: HTMLDivElement | string | null;
export interface TooltipCfg extends PluginContainer<NodeConfig | EdgeConfig> {
offsetX?: number;
offsetY?: number;
/** 是否展示 */
Expand Down Expand Up @@ -275,6 +289,8 @@ export interface CommonConfig extends GraphContainerConfig {
toolbarCfg?: ToolbarCfg;
/** 提示 */
tooltipCfg?: TooltipCfg;
/** 右键菜单 */
menuCfg?: MenuConfig;
/** 交互行为 */
behaviors?: string[];
/** 是否开启动画 */
Expand Down
108 changes: 108 additions & 0 deletions packages/graphs/src/plugins/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { wrapBehavior, each, deepMix } from '@antv/util';
import { IAbstractGraph as IGraph, IG6GraphEvent } from '@antv/g6';

export interface IPluginBaseConfig {
container?: HTMLDivElement | string | null;
className?: string;
graph?: IGraph;
[key: string]: any;
}

interface EventMapType {
[key: string]: any;
}

export default abstract class PluginBase {
private _events: EventMapType;

public _cfgs: IPluginBaseConfig;

public destroyed: boolean;

/**
* 插件基类的构造函数
* @param cfgs 插件的配置项
*/
constructor(cfgs?: IPluginBaseConfig) {
this._cfgs = deepMix(this.getDefaultCfgs(), cfgs);
this._events = {};
this.destroyed = false;
}

/**
* 获取默认的插件配置
*/
public getDefaultCfgs() {
return {};
}

/**
* 初始化插件
* @param graph IGraph 实例
*/
public initPlugin(graph: IGraph) {
const self = this;
self.set('graph', graph);
const events = self.getEvents();

const bindEvents: EventMapType = {};

each(events, (v, k) => {
const event = wrapBehavior(self, v) as (e: IG6GraphEvent) => void;
bindEvents[k] = event;
graph.on(k, event);
});
this._events = bindEvents;

this.init();
}

/**
* 初始化方法,供子类实现
*/
public abstract init();

/**
* 获取插件中的事件和事件处理方法,供子类实现
*/
public getEvents() {
return {};
}

/**
* 获取配置项中的某个值
* @param key 键值
*/
public get(key: string) {
return this._cfgs?.[key];
}

/**
* 将指定的值存储到 cfgs 中
* @param key 键值
* @param val 设置的值
*/
public set(key: string, val: any) {
this._cfgs[key] = val;
}

/**
* 销毁方法,供子类复写
*/
public destroy() {}

/**
* 销毁插件
*/
public destroyPlugin() {
this.destroy();
const graph = this.get('graph');
const events = this._events;
each(events, (v, k) => {
graph.off(k, v);
});
(this._events as EventMapType | null) = null;
(this._cfgs as IPluginBaseConfig | null) = null;
this.destroyed = true;
}
}
5 changes: 5 additions & 0 deletions packages/graphs/src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createToolbar } from './toolbar';
import Menu from './menu';
export type { MenuConfig } from './menu';

export { createToolbar, Menu };
Loading