balibabu
fix: Opening file whose type is Knowledge Graph appear error with tsx #1975 (#1978)
6bf835b
import { Rect } from '@antv/g'; | |
import { | |
Badge, | |
BaseBehavior, | |
BaseNode, | |
CommonEvent, | |
ExtensionCategory, | |
Graph, | |
NodeEvent, | |
Point, | |
Polyline, | |
PolylineStyleProps, | |
register, | |
subStyleProps, | |
treeToGraphData, | |
} from '@antv/g6'; | |
import { TreeData } from '@antv/g6/lib/types'; | |
import isEmpty from 'lodash/isEmpty'; | |
import { useCallback, useEffect, useRef } from 'react'; | |
const rootId = 'root'; | |
const COLORS = [ | |
'#5B8FF9', | |
'#F6BD16', | |
'#5AD8A6', | |
'#945FB9', | |
'#E86452', | |
'#6DC8EC', | |
'#FF99C3', | |
'#1E9493', | |
'#FF9845', | |
'#5D7092', | |
]; | |
const TreeEvent = { | |
COLLAPSE_EXPAND: 'collapse-expand', | |
WHEEL: 'canvas:wheel', | |
}; | |
class IndentedNode extends BaseNode { | |
static defaultStyleProps = { | |
ports: [ | |
{ | |
key: 'in', | |
placement: 'right-bottom', | |
}, | |
{ | |
key: 'out', | |
placement: 'left-bottom', | |
}, | |
], | |
} as any; | |
constructor(options: any) { | |
Object.assign(options.style, IndentedNode.defaultStyleProps); | |
super(options); | |
} | |
get childrenData() { | |
return this.attributes.context?.model.getChildrenData(this.id); | |
} | |
getKeyStyle(attributes: any) { | |
const [width, height] = this.getSize(attributes); | |
const keyStyle = super.getKeyStyle(attributes); | |
return { | |
width, | |
height, | |
...keyStyle, | |
fill: 'transparent', | |
}; | |
} | |
drawKeyShape(attributes: any, container: any) { | |
const keyStyle = this.getKeyStyle(attributes); | |
return this.upsert('key', Rect, keyStyle, container); | |
} | |
getLabelStyle(attributes: any) { | |
if (attributes.label === false || !attributes.labelText) return false; | |
return subStyleProps(this.getGraphicStyle(attributes), 'label') as any; | |
} | |
drawIconArea(attributes: any, container: any) { | |
const [, h] = this.getSize(attributes); | |
const iconAreaStyle = { | |
fill: 'transparent', | |
height: 30, | |
width: 12, | |
x: -6, | |
y: h, | |
zIndex: -1, | |
}; | |
this.upsert('icon-area', Rect, iconAreaStyle, container); | |
} | |
forwardEvent(target: any, type: any, listener: any) { | |
if (target && !Reflect.has(target, '__bind__')) { | |
Reflect.set(target, '__bind__', true); | |
target.addEventListener(type, listener); | |
} | |
} | |
getCountStyle(attributes: any) { | |
const { collapsed, color } = attributes; | |
if (collapsed) { | |
const [, height] = this.getSize(attributes); | |
return { | |
backgroundFill: color, | |
cursor: 'pointer', | |
fill: '#fff', | |
fontSize: 8, | |
padding: [0, 10], | |
text: `${this.childrenData?.length}`, | |
textAlign: 'center', | |
y: height + 8, | |
}; | |
} | |
return false; | |
} | |
drawCountShape(attributes: any, container: any) { | |
const countStyle = this.getCountStyle(attributes); | |
const btn = this.upsert('count', Badge, countStyle as any, container); | |
this.forwardEvent(btn, CommonEvent.CLICK, (event: any) => { | |
event.stopPropagation(); | |
attributes.context.graph.emit(TreeEvent.COLLAPSE_EXPAND, { | |
id: this.id, | |
collapsed: false, | |
}); | |
}); | |
} | |
isShowCollapse(attributes: any) { | |
return ( | |
!attributes.collapsed && | |
Array.isArray(this.childrenData) && | |
this.childrenData?.length > 0 | |
); | |
} | |
getCollapseStyle(attributes: any) { | |
const { showIcon, color } = attributes; | |
if (!this.isShowCollapse(attributes)) return false; | |
const [, height] = this.getSize(attributes); | |
return { | |
visibility: showIcon ? 'visible' : 'hidden', | |
backgroundFill: color, | |
backgroundHeight: 12, | |
backgroundWidth: 12, | |
cursor: 'pointer', | |
fill: '#fff', | |
fontFamily: 'iconfont', | |
fontSize: 8, | |
text: '\ue6e4', | |
textAlign: 'center', | |
x: -1, // half of edge line width | |
y: height + 8, | |
}; | |
} | |
drawCollapseShape(attributes: any, container: any) { | |
const iconStyle = this.getCollapseStyle(attributes); | |
const btn = this.upsert( | |
'collapse-expand', | |
Badge, | |
iconStyle as any, | |
container, | |
); | |
this.forwardEvent(btn, CommonEvent.CLICK, (event: any) => { | |
event.stopPropagation(); | |
attributes.context.graph.emit(TreeEvent.COLLAPSE_EXPAND, { | |
id: this.id, | |
collapsed: !attributes.collapsed, | |
}); | |
}); | |
} | |
getAddStyle(attributes: any) { | |
const { collapsed, showIcon } = attributes; | |
if (collapsed) return false; | |
const [, height] = this.getSize(attributes); | |
const color = '#ddd'; | |
const lineWidth = 1; | |
return { | |
visibility: showIcon ? 'visible' : 'hidden', | |
backgroundFill: '#fff', | |
backgroundHeight: 12, | |
backgroundLineWidth: lineWidth, | |
backgroundStroke: color, | |
backgroundWidth: 12, | |
cursor: 'pointer', | |
fill: color, | |
fontFamily: 'iconfont', | |
text: '\ue664', | |
textAlign: 'center', | |
x: -1, | |
y: height + (this.isShowCollapse(attributes) ? 22 : 8), | |
}; | |
} | |
render(attributes = this.parsedAttributes, container = this) { | |
super.render(attributes, container); | |
this.drawCountShape(attributes, container); | |
this.drawIconArea(attributes, container); | |
this.drawCollapseShape(attributes, container); | |
} | |
} | |
class IndentedEdge extends Polyline { | |
getControlPoints( | |
attributes: Required<PolylineStyleProps>, | |
sourcePoint: Point, | |
targetPoint: Point, | |
) { | |
const [sx] = sourcePoint; | |
const [, ty] = targetPoint; | |
return [[sx, ty]] as any; | |
} | |
} | |
class CollapseExpandTree extends BaseBehavior { | |
constructor(context: any, options: any) { | |
super(context, options); | |
this.bindEvents(); | |
} | |
update(options: any) { | |
this.unbindEvents(); | |
super.update(options); | |
this.bindEvents(); | |
} | |
bindEvents() { | |
const { graph } = this.context; | |
graph.on(NodeEvent.POINTER_ENTER, this.showIcon); | |
graph.on(NodeEvent.POINTER_LEAVE, this.hideIcon); | |
graph.on(TreeEvent.COLLAPSE_EXPAND, this.onCollapseExpand); | |
} | |
unbindEvents() { | |
const { graph } = this.context; | |
graph.off(NodeEvent.POINTER_ENTER, this.showIcon); | |
graph.off(NodeEvent.POINTER_LEAVE, this.hideIcon); | |
graph.off(TreeEvent.COLLAPSE_EXPAND, this.onCollapseExpand); | |
} | |
status = 'idle'; | |
showIcon = (event: any) => { | |
this.setIcon(event, true); | |
}; | |
hideIcon = (event: any) => { | |
this.setIcon(event, false); | |
}; | |
setIcon = (event: any, show: boolean) => { | |
if (this.status !== 'idle') return; | |
const { target } = event; | |
const id = target.id; | |
const { graph, element } = this.context; | |
graph.updateNodeData([{ id, style: { showIcon: show } }]); | |
element?.draw({ animation: false, silence: true }); | |
}; | |
onCollapseExpand = async (event: any) => { | |
this.status = 'busy'; | |
const { id, collapsed } = event; | |
const { graph } = this.context; | |
if (collapsed) await graph.collapseElement(id); | |
else await graph.expandElement(id); | |
this.status = 'idle'; | |
}; | |
} | |
register(ExtensionCategory.NODE, 'indented', IndentedNode); | |
register(ExtensionCategory.EDGE, 'indented', IndentedEdge); | |
register( | |
ExtensionCategory.BEHAVIOR, | |
'collapse-expand-tree', | |
CollapseExpandTree, | |
); | |
interface IProps { | |
data: TreeData; | |
show: boolean; | |
} | |
const IndentedTree = ({ data, show }: IProps) => { | |
const containerRef = useRef<HTMLDivElement>(null); | |
const graphRef = useRef<Graph | null>(null); | |
const render = useCallback(async (data: TreeData) => { | |
const graph: Graph = new Graph({ | |
container: containerRef.current!, | |
x: 60, | |
node: { | |
type: 'indented', | |
style: { | |
size: (d) => [d.id.length * 6 + 10, 20], | |
labelBackground: (datum) => datum.id === rootId, | |
labelBackgroundRadius: 0, | |
labelBackgroundFill: '#576286', | |
labelFill: (datum) => (datum.id === rootId ? '#fff' : '#666'), | |
labelText: (d) => d.style?.labelText || d.id, | |
labelTextAlign: (datum) => (datum.id === rootId ? 'center' : 'left'), | |
labelTextBaseline: 'top', | |
color: (datum: any) => { | |
const depth = graph.getAncestorsData(datum.id, 'tree').length - 1; | |
return COLORS[depth % COLORS.length] || '#576286'; | |
}, | |
}, | |
state: { | |
selected: { | |
lineWidth: 0, | |
labelFill: '#40A8FF', | |
labelBackground: true, | |
labelFontWeight: 'normal', | |
labelBackgroundFill: '#e8f7ff', | |
labelBackgroundRadius: 10, | |
}, | |
}, | |
}, | |
edge: { | |
type: 'indented', | |
style: { | |
radius: 16, | |
lineWidth: 2, | |
sourcePort: 'out', | |
targetPort: 'in', | |
stroke: (datum: any) => { | |
const depth = graph.getAncestorsData(datum.source, 'tree').length; | |
return COLORS[depth % COLORS.length] || 'black'; | |
}, | |
}, | |
}, | |
layout: { | |
type: 'indented', | |
direction: 'LR', | |
isHorizontal: true, | |
indent: 40, | |
getHeight: () => 20, | |
getVGap: () => 10, | |
}, | |
behaviors: [ | |
'scroll-canvas', | |
'collapse-expand-tree', | |
{ | |
type: 'click-select', | |
enable: (event: any) => | |
event.targetType === 'node' && event.target.id !== rootId, | |
}, | |
], | |
}); | |
if (graphRef.current) { | |
graphRef.current.destroy(); | |
} | |
graphRef.current = graph; | |
graph?.setData(treeToGraphData(data)); | |
graph?.render(); | |
}, []); | |
useEffect(() => { | |
if (!isEmpty(data)) { | |
render(data); | |
} | |
}, [render, data]); | |
return ( | |
<div | |
id="tree" | |
ref={containerRef} | |
style={{ | |
width: '90vw', | |
height: '80vh', | |
display: show ? 'block' : 'none', | |
}} | |
/> | |
); | |
}; | |
export default IndentedTree; | |