Skip to content

Commit

Permalink
feat: add fold and unfold buttons for mind graph
Browse files Browse the repository at this point in the history
  • Loading branch information
gaoli committed Jan 15, 2020
1 parent 0c38302 commit 86a9067
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 15 deletions.
10 changes: 0 additions & 10 deletions src/common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,6 @@ export const MIND_CONTAINER_ID = 'J_MindContainer';

export const LABEL_DEFAULT_TEXT = '新建节点';

export enum ShapeClassName {
Label = 'node-label',
KeyShape = 'node-shape',
Wrapper = 'node-wrapper',
Appendix = 'node-appendix',
Anchor = 'Anchor',
CollapseExpandButton = 'CollapseExpandButton',
StatusIcon = 'StatusIcon',
}

export enum ItemType {
Node = 'node',
Edge = 'edge',
Expand Down
2 changes: 1 addition & 1 deletion src/common/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export interface FlowData {
* MindData
*/
export interface MindData extends NodeModel {
isRoot?: boolean;
side?: 'left' | 'right';
children?: MindData[];
collapsed?: boolean;
}
Expand Down
12 changes: 10 additions & 2 deletions src/components/Mind/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import merge from 'lodash/merge';
import G6 from '@antv/g6';
import { guid, recursiveTraversal } from '@/utils';
import global from '@/common/global';
import { MIND_CONTAINER_ID, ShapeClassName, GraphType } from '@/common/constants';
import { MIND_CONTAINER_ID, GraphType } from '@/common/constants';
import { FOLD_BUTTON_CLASS_NAME, UNFOLD_BUTTON_CLASS_NAME } from '@/shape/nodes/bizMindNode';
import { MindData, GraphReactEventProps } from '@/common/interfaces';
import behaviorManager from '@/common/behaviorManager';
import Graph from '@/components/Graph';
Expand Down Expand Up @@ -45,7 +46,7 @@ class Mind extends React.Component<MindProps, MindState> {
};

canCollapseExpand = ({ target }) => {
return target && target.get('className') === ShapeClassName.CollapseExpandButton;
return target && [FOLD_BUTTON_CLASS_NAME, UNFOLD_BUTTON_CLASS_NAME].includes(target.get('className'));
};

parseData = data => {
Expand Down Expand Up @@ -104,6 +105,13 @@ class Mind extends React.Component<MindProps, MindState> {
getHeight: () => 60,
getHGap: () => 100,
getVGap: () => 50,
getSide: ({ data }) => {
if (data.side) {
return data.side;
}

return 'right';
},
},
animate: false,
defaultNode: {
Expand Down
74 changes: 72 additions & 2 deletions src/shape/nodes/bizMindNode.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,77 @@
import G6 from '@antv/g6';
import { CustomNode } from '@/common/interfaces';
import { G } from '@antv/g6/types/g';
import { CustomNode, MindData } from '@/common/interfaces';
import { getNodeSide, getFoldButtonPath, getUnfoldButtonPath } from '../utils';

export const FOLD_BUTTON_CLASS_NAME = 'node-fold-button';
export const UNFOLD_BUTTON_CLASS_NAME = 'node-unfold-button';

const bizMindNode: CustomNode<MindData> = {
afterDraw(model, group) {
this.drawButton(model, group);
},

afterUpdate(model, item) {
const group = item.getContainer();

this.drawButton(model, group);
this.adjustButton(model, item);
},

drawButton(model: MindData, group: G.Group) {
const { children, collapsed } = model;

[FOLD_BUTTON_CLASS_NAME, UNFOLD_BUTTON_CLASS_NAME].forEach(className => {
const shape = group.findByClassName(className);

if (shape) {
shape.destroy();
}
});

if (!children || !children.length) {
return;
}

if (!collapsed) {
group.addShape('path', {
className: FOLD_BUTTON_CLASS_NAME,
attrs: {
path: getFoldButtonPath(),
fill: '#ffffff',
stroke: '#ccc1d8',
},
});
} else {
group.addShape('path', {
className: UNFOLD_BUTTON_CLASS_NAME,
attrs: {
path: getUnfoldButtonPath(),
fill: '#ffffff',
stroke: '#ccc1d8',
},
});
}
},

adjustButton(model: MindData, item: G6.Node) {
const { children, collapsed } = model;

if (!children || !children.length) {
return;
}

const group = item.getContainer();
const shape = group.findByClassName(!collapsed ? FOLD_BUTTON_CLASS_NAME : UNFOLD_BUTTON_CLASS_NAME);

const [width, height] = this.getSize(model);

const x = getNodeSide(item) === 'left' ? -24 : width + 10;
const y = height / 2 - 9;

shape.translate(x, y);
},

const bizMindNode: CustomNode = {
getAnchorPoints() {
return [
[0, 0.5],
Expand Down
4 changes: 4 additions & 0 deletions src/shape/nodes/bizNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ const bizNode: CustomNode = {
return labelShape;
},

update() {},

afterUpdate() {},

setState(name, value, item) {
if (this.beforeSetState) {
this.beforeSetState(name, value, item);
Expand Down
61 changes: 61 additions & 0 deletions src/shape/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,67 @@
import { MindData } from '@/common/interfaces';

const canvas = document.createElement('canvas');
const canvasContext = canvas.getContext('2d');

export function getNodeSide(item: G6.Node): 'left' | 'right' {
const model = item.getModel<MindData>();

if (model.side) {
return model.side;
}

const parent = item.get('parent');

if (parent) {
return getNodeSide(parent);
}

return 'right';
}

export function getRectPath(x: number, y: number, w: number, h: number, r: number) {
if (r) {
return [
['M', +x + +r, y],
['l', w - r * 2, 0],
['a', r, r, 0, 0, 1, r, r],
['l', 0, h - r * 2],
['a', r, r, 0, 0, 1, -r, r],
['l', r * 2 - w, 0],
['a', r, r, 0, 0, 1, -r, -r],
['l', 0, r * 2 - h],
['a', r, r, 0, 0, 1, r, -r],
['z'],
];
}

const res = [['M', x, y], ['l', w, 0], ['l', 0, h], ['l', -w, 0], ['z']];

res.toString = toString;

return res;
}

export function getFoldButtonPath() {
const w = 14;
const h = 14;
const rect = getRectPath(0, 0, w, h, 2);
const hp = `M${(w * 3) / 14},${h / 2}L${(w * 11) / 14},${h / 2}`;
const vp = '';

return rect + hp + vp;
}

export function getUnfoldButtonPath() {
const w = 14;
const h = 14;
const rect = getRectPath(0, 0, w, h, 2);
const hp = `M${(w * 3) / 14},${h / 2}L${(w * 11) / 14},${h / 2}`;
const vp = `M${w / 2},${(h * 3) / 14}L${w / 2},${(h * 11) / 14}`;

return rect + hp + vp;
}

export function optimizeMultilineText(text: string, font: string, maxRows: number, maxWidth: number) {
canvasContext.font = font;

Expand Down

0 comments on commit 86a9067

Please sign in to comment.