gg-editor
Version:
A visual graph editor based on G6 and React
207 lines (168 loc) • 4.87 kB
text/typescript
import G6 from '@antv/g6';
import merge from 'lodash/merge';
import isArray from 'lodash/isArray';
import { ItemState } from '@/common/constants';
import { GGroup, NodeModel, CustomNode } from '@/common/interfaces';
import { optimizeMultilineText } from '../utils';
const WRAPPER_BORDER_WIDTH = 2;
const WRAPPER_HORIZONTAL_PADDING = 10;
const WRAPPER_CLASS_NAME = 'node-wrapper';
const CONTENT_CLASS_NAME = 'node-content';
const LABEL_CLASS_NAME = 'node-label';
const bizNode: CustomNode = {
options: {
size: [120, 60],
wrapperStyle: {
fill: '#5487ea',
radius: 8,
},
contentStyle: {
fill: '#ffffff',
radius: 6,
},
labelStyle: {
fill: '#000000',
textAlign: 'center',
textBaseline: 'middle',
},
stateStyles: {
[ItemState.Active]: {
wrapperStyle: {},
contentStyle: {},
labelStyle: {},
} as any,
[ItemState.Selected]: {
wrapperStyle: {},
contentStyle: {},
labelStyle: {},
} as any,
},
},
getOptions(model: NodeModel) {
return merge({}, this.options, this.getCustomConfig(model) || {}, model);
},
draw(model, group) {
const keyShape = this.drawWrapper(model, group);
this.drawContent(model, group);
this.drawLabel(model, group);
return keyShape;
},
drawWrapper(model: NodeModel, group: GGroup) {
const [width, height] = this.getSize(model);
const { wrapperStyle } = this.getOptions(model);
const shape = group.addShape('rect', {
className: WRAPPER_CLASS_NAME,
draggable: true,
attrs: {
x: 0,
y: -WRAPPER_BORDER_WIDTH * 2,
width,
height: height + WRAPPER_BORDER_WIDTH * 2,
...wrapperStyle,
},
});
return shape;
},
drawContent(model: NodeModel, group: GGroup) {
const [width, height] = this.getSize(model);
const { contentStyle } = this.getOptions(model);
const shape = group.addShape('rect', {
className: CONTENT_CLASS_NAME,
draggable: true,
attrs: {
x: 0,
y: 0,
width,
height,
...contentStyle,
},
});
return shape;
},
drawLabel(model: NodeModel, group: GGroup) {
const [width, height] = this.getSize(model);
const { labelStyle } = this.getOptions(model);
const shape = group.addShape('text', {
className: LABEL_CLASS_NAME,
draggable: true,
attrs: {
x: width / 2,
y: height / 2,
text: model.label,
...labelStyle,
},
});
return shape;
},
setLabelText(model: NodeModel, group: GGroup) {
const shape = group.findByClassName(LABEL_CLASS_NAME);
if (!shape) {
return;
}
const [width] = this.getSize(model);
const { fontStyle, fontWeight, fontSize, fontFamily } = shape.attr();
const text = model.label as string;
const font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;
shape.attr('text', optimizeMultilineText(text, font, 2, width - WRAPPER_HORIZONTAL_PADDING * 2));
},
update(model, item) {
const group = item.getContainer();
this.setLabelText(model, group);
},
setState(name, value, item) {
const group = item.getContainer();
const model = item.getModel();
const states = item.getStates() as ItemState[];
[WRAPPER_CLASS_NAME, CONTENT_CLASS_NAME, LABEL_CLASS_NAME].forEach(className => {
const shape = group.findByClassName(className);
const options = this.getOptions(model);
const shapeName = className.split('-')[1];
shape.attr({
...options[`${shapeName}Style`],
});
states.forEach(state => {
if (options.stateStyles[state] && options.stateStyles[state][`${shapeName}Style`]) {
shape.attr({
...options.stateStyles[state][`${shapeName}Style`],
});
}
});
});
if (name === ItemState.Selected) {
const wrapperShape = group.findByClassName(WRAPPER_CLASS_NAME);
const [width, height] = this.getSize(model);
if (value) {
wrapperShape.attr({
x: -WRAPPER_BORDER_WIDTH,
y: -WRAPPER_BORDER_WIDTH * 2,
width: width + WRAPPER_BORDER_WIDTH * 2,
height: height + WRAPPER_BORDER_WIDTH * 3,
});
} else {
wrapperShape.attr({
x: 0,
y: -WRAPPER_BORDER_WIDTH * 2,
width,
height: height + WRAPPER_BORDER_WIDTH * 2,
});
}
}
if (this.afterSetState) {
this.afterSetState(name, value, item);
}
},
getSize(model: NodeModel) {
const { size } = this.getOptions(model);
if (!isArray(size)) {
return [size, size];
}
return size;
},
getCustomConfig() {
return {};
},
getAnchorPoints() {
return [];
},
};
G6.registerNode('bizNode', bizNode);