@eyea/plugin-mind
Version:
233 lines (213 loc) • 5.75 kB
text/typescript
import { NodeData, ShapeData } from '../../types';
import { Graph, Model, Cell, Node, Edge, Addon } from '@antv/x6';
import Hierarchy from '@antv/hierarchy';
import { $, DATA_ELEMENT, NodeInterface, UI } from '@aomao/engine';
import HtmlNode from './html-node';
import './index.css';
/*let GraphMoudle
if (!isServer) {
import('@antv/x6').then(moudle => {
GraphMoudle = moudle
});
}*/
export type Options = {
width?: number;
height?: number;
onChange?: (data: Array<ShapeData>) => void;
onSelectedEditable?: (cell: Cell) => void;
};
class GraphEditor {
#options: Options;
#editableCell?: Cell;
#htmlNode: HtmlNode;
#graph: Graph;
#dnd: Addon.Dnd;
constructor(container: NodeInterface, options: Options) {
this.#options = options;
this.#graph = new Graph({
container: container.get<HTMLElement>()!,
width: options.width,
height: options.height || 600,
selecting: {
enabled: true,
},
getHTMLComponent: (node: Node) => {
return this.#htmlNode.render(node);
},
interacting: ({ cell }) => {
if (cell.isNode() && cell.id === 'main') return true;
return { nodeMovable: false, edgeMovable: false };
},
});
this.#graph.on('cell:changed', () => {
const { onChange } = this.#options;
if (onChange) onChange(this.getData());
});
this.#htmlNode = new HtmlNode(this.#graph, {
onAdded: () => {
console.log('added');
const data = this.getData();
const nodes = this.#graph.getNodes();
const hierarchy = this.getHierarchy(data);
/*const updatePosition = (data: Array<ShapeData>) => {
data.forEach((item) => {
const node = nodes.find((node) => node.id === item.id);
if (node && node.parent) {
const { x, y } = node.getBBox();
const newX =
item.x + item.data.width / 2 + item.hgap;
const newY =
item.y + item.data.height / 2 + item.vgap;
if (newX !== x || newY !== y) {
node.position(newX, newY, {
relative: true,
deep: true,
});
}
}
if (item.children) updatePosition(item.children);
});
};
updatePosition(hierarchy);*/
},
});
this.#dnd = new Addon.Dnd({
target: this.#graph,
getDragNode: (sourceNode) => sourceNode,
});
this.#graph.on('node:mousedown', ({ e, node }) => {
//this.#dnd.start(node, e);
});
}
getData() {
const nodes = this.#graph.getNodes();
const data: Array<ShapeData> = [];
nodes.forEach((node) => {
if (!node.isNode()) return;
const getItem = (node: Cell) => {
const bbox = node.getBBox();
const children: Array<ShapeData> = [];
node.children?.forEach((child) => {
if (!child.isNode()) return;
children.push(getItem(child));
});
const data = node.getData<NodeData>();
return {
id: node.id,
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
data: node.getData<NodeData>(),
children,
hierarchy: data.hierarchy || 1,
};
};
if (node.parent) return;
data.push(getItem(node));
});
return data;
}
getHierarchy(data: Array<ShapeData>) {
return data.map((data) =>
Hierarchy.mindmap(data, {
direction: 'H',
getSubTreeSep: (node: ShapeData) => {
if (node.children && node.children.length > 0) {
if (node.data?.hierarchy && node.data?.hierarchy <= 2) {
return 8;
}
return 2;
}
return 0;
},
getHGap: (node: ShapeData) => {
if (node.data?.hierarchy && node.data?.hierarchy === 1) {
return 8;
}
if (node.data?.hierarchy && node.data?.hierarchy === 2) {
return 24;
}
return 18;
},
getVGap: (node: ShapeData) => {
if (node.data?.hierarchy && node.data?.hierarchy === 1) {
return 8;
}
if (node.data?.hierarchy && node.data?.hierarchy === 2) {
return 12;
}
return 2;
},
getSide: (node: ShapeData) => {
/*if (node.data.side) {
return node.data.side
}*/
return 'right';
},
}),
);
}
setEditableNodeValue(value: string) {
if (!this.#editableCell) return;
this.#editableCell.setData({ value }, { silent: true });
}
render(data: Array<ShapeData>) {
const getNode = (item: any): Node.Metadata => {
const newX = item.x + item.data.width / 2 + item.hgap;
const newY = item.y + item.data.height / 2 + item.vgap;
return {
...item,
x: newX,
y: newY,
data: item.data.data,
shape: 'html',
attributes: {
body: {
fill: 'transparent',
strokeWidth: 0,
},
},
html: {
render: (node: Node) => {
return this.#htmlNode.render(node);
},
shouldComponentUpdate: (node: Node) => {
return node.hasChanged('data');
},
},
};
};
const hierarchyData = this.getHierarchy(data);
hierarchyData.forEach((hierarchy) => {
const node = this.#graph.addNode(getNode(hierarchy));
const appendChild = (root: Node, item: any) => {
item.children?.forEach((child: any) => {
const node = this.#graph.addNode(getNode(child));
root.addChild(node);
appendChild(node, child);
});
};
appendChild(node, hierarchy);
});
}
didRender() {
this.#graph.on('node:dblclick', ({ cell }) => {
if (cell.shape !== 'html') return;
cell.setData({ editable: true });
console.log('node:dblclick');
this.#editableCell = cell;
const { onSelectedEditable } = this.#options;
if (onSelectedEditable) onSelectedEditable(cell);
});
this.#graph.on('node:unselected', () => {
console.log('node:unselected');
this.#editableCell?.setData({ editable: false });
this.#editableCell = undefined;
});
}
destroy() {
this.#graph.dispose();
}
}
export default GraphEditor;