Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
310 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { Graph } from '@/src'; | ||
import data from '@@/dataset/cluster.json'; | ||
import { isObject } from '@antv/util'; | ||
import type { STDTestCase } from '../types'; | ||
|
||
export const pluginTooltip: STDTestCase = async (context) => { | ||
const graph = new Graph({ | ||
...context, | ||
autoResize: true, | ||
data, | ||
layout: { type: 'd3force' }, | ||
behaviors: ['drag-canvas', 'drag-element'], | ||
node: { | ||
style: { | ||
labelText: (d) => d.id, | ||
}, | ||
}, | ||
plugins: [ | ||
{ | ||
type: 'tooltip', | ||
getContent: (evt: any, { items }: { items: any[] }) => { | ||
return `<div>${items[0].id}</div>`; | ||
}, | ||
}, | ||
], | ||
}); | ||
|
||
await graph.render(); | ||
|
||
pluginTooltip.form = (panel) => { | ||
const config = { | ||
trigger: 'pointerenter', | ||
}; | ||
return [ | ||
panel | ||
.add(config, 'trigger', ['pointerenter', 'click']) | ||
.name('Change Trigger Method') | ||
.onChange((trigger: string) => { | ||
graph.setPlugins((plugins) => | ||
plugins.map((plugin) => { | ||
if (isObject(plugin) && plugin.type === 'tooltip') return { ...plugin, trigger }; | ||
return plugin; | ||
}), | ||
); | ||
}), | ||
]; | ||
}; | ||
|
||
return graph; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
export { BasePlugin } from './base-plugin'; | ||
export { GridLine } from './grid-line'; | ||
export { Tooltip } from './tooltip'; | ||
|
||
export type { BasePluginOptions } from './base-plugin'; | ||
export type { GridLineOptions } from './grid-line'; | ||
export type { TooltipOptions } from './tooltip'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
import type { TooltipStyleProps } from '@antv/component'; | ||
import { Tooltip as TooltipComponent } from '@antv/component'; | ||
import type { FederatedMouseEvent } from '@antv/g'; | ||
import type { RuntimeContext } from '../runtime/types'; | ||
import type { BehaviorEvent } from '../types'; | ||
import type { BasePluginOptions } from './base-plugin'; | ||
import { BasePlugin } from './base-plugin'; | ||
|
||
interface IG6GraphEvent extends BehaviorEvent<FederatedMouseEvent> { | ||
targetType: 'node' | 'edge' | 'combo'; | ||
} | ||
|
||
export type ContentModel = { | ||
items?: { [key: string]: unknown }[]; | ||
}; | ||
|
||
export interface TooltipOptions | ||
extends BasePluginOptions, | ||
Pick<TooltipStyleProps, 'position' | 'offset' | 'enterable' | 'style' | 'container'> { | ||
/** <zh/> 触发方式 | <en/> Event type that triggers display of tooltip */ | ||
trigger?: 'pointerenter' | 'click'; | ||
/** <zh/> 自定义内容 | <en/> Function for getting tooltip content */ | ||
getContent?: (evt: IG6GraphEvent, item: ContentModel) => HTMLElement | string; | ||
/** <zh/> 触发类型 | <en/> Types of items for which tooltip is allowed to be displayed */ | ||
itemTypes?: ('node' | 'edge' | 'combo')[]; | ||
} | ||
|
||
export class Tooltip extends BasePlugin<TooltipOptions> { | ||
static defaultOptions: Partial<TooltipOptions> = { | ||
trigger: 'pointerenter', | ||
position: 'top-right', | ||
enterable: false, | ||
itemTypes: ['node', 'edge', 'combo'], | ||
style: { | ||
'.tooltip': { | ||
visibility: 'hidden', | ||
}, | ||
}, | ||
container: { | ||
x: 0, | ||
y: 0, | ||
}, | ||
}; | ||
private currentTarget: string | null = null; | ||
private tooltipElement: TooltipComponent | null = null; | ||
private $element: HTMLElement = document.createElement('div'); | ||
|
||
constructor(context: RuntimeContext, options: TooltipOptions) { | ||
super(context, Object.assign({}, Tooltip.defaultOptions, options)); | ||
this.render(); | ||
this.bindEvents(); | ||
} | ||
|
||
public getEvents(trigger: 'pointerenter' | 'click'): { [key: string]: Function } { | ||
if (trigger === 'click') { | ||
return { | ||
'node:click': this.onClick, | ||
'edge:click': this.onClick, | ||
'combo:click': this.onClick, | ||
'canvas:click': this.onPointerLeave, | ||
afterremoveitem: this.onPointerLeave, | ||
contextmenu: this.onPointerLeave, | ||
drag: this.onPointerLeave, | ||
}; | ||
} | ||
|
||
return { | ||
'node:pointerenter': this.onPointerEnter, | ||
'node:pointermove': this.onPointerMove, | ||
'canvas:pointermove': this.onCanvasMove, | ||
'edge:pointerenter': this.onPointerEnter, | ||
'edge:pointermove': this.onPointerMove, | ||
'combo:pointerenter': this.onPointerEnter, | ||
'combo:pointermove': this.onPointerMove, | ||
contextmenu: this.onPointerLeave, | ||
'node:drag': this.onPointerLeave, | ||
}; | ||
} | ||
|
||
public update(options: Partial<TooltipOptions>) { | ||
super.update(options); | ||
this.unbundEvents(true); | ||
if (this.tooltipElement) { | ||
this.$element.removeChild(this.tooltipElement.HTMLTooltipElement); | ||
} | ||
this.tooltipElement = this.initTooltip(); | ||
} | ||
|
||
private render() { | ||
const { canvas } = this.context; | ||
const $container = canvas.getContainer(); | ||
if (!$container) return; | ||
this.$element.className = 'g6-tooltip'; | ||
$container.appendChild(this.$element); | ||
this.tooltipElement = this.initTooltip(); | ||
} | ||
|
||
private unbundEvents(isUpdate?: boolean) { | ||
const { graph } = this.context; | ||
const { trigger } = this.options; | ||
/** The previous event binding needs to be removed when updating the trigger. */ | ||
const events = this.getEvents(!isUpdate ? trigger : trigger === 'click' ? 'pointerenter' : 'click'); | ||
Object.keys(events).forEach((eventName) => { | ||
graph.off(eventName, events[eventName]); | ||
}); | ||
} | ||
|
||
private bindEvents() { | ||
const { graph } = this.context; | ||
const { trigger } = this.options; | ||
const events = this.getEvents(trigger); | ||
Object.keys(events).forEach((eventName) => { | ||
graph.on(eventName, events[eventName]); | ||
}); | ||
} | ||
|
||
public onClick = (e: IG6GraphEvent) => { | ||
const { | ||
targetType, | ||
target: { id }, | ||
} = e; | ||
if (this.options.itemTypes.indexOf(targetType) === -1) return; | ||
// click the same item twice, tooltip will be hidden | ||
if (this.currentTarget === id) { | ||
this.currentTarget = null; | ||
this.hideTooltip(e); | ||
} else { | ||
this.currentTarget = id; | ||
this.showTooltip(e); | ||
} | ||
}; | ||
|
||
public onPointerMove = (e: IG6GraphEvent) => { | ||
const { targetType, target } = e; | ||
if (this.options.itemTypes.indexOf(targetType) === -1) return; | ||
if (!this.currentTarget || target.id === this.currentTarget) { | ||
return; | ||
} | ||
this.showTooltip(e); | ||
}; | ||
|
||
public onPointerLeave = (e: IG6GraphEvent) => { | ||
this.hideTooltip(e); | ||
this.currentTarget = null; | ||
}; | ||
|
||
public onCanvasMove = (e: IG6GraphEvent) => { | ||
this.hideTooltip(e); | ||
this.currentTarget = null; | ||
}; | ||
|
||
private onPointerEnter = (e: IG6GraphEvent) => { | ||
const { targetType } = e; | ||
if (this.options.itemTypes.indexOf(targetType) === -1) return; | ||
this.showTooltip(e); | ||
}; | ||
|
||
public showTooltip(e: IG6GraphEvent) { | ||
const { | ||
targetType, | ||
client: { x, y }, | ||
target: { id, attributes }, | ||
} = e; | ||
if (!this.tooltipElement) return; | ||
const { getContent } = this.options; | ||
const { model } = this.context; | ||
const { color, stroke } = attributes; | ||
this.currentTarget = id; | ||
let items: { [key: string]: unknown }[] = []; | ||
switch (targetType) { | ||
case 'node': | ||
items = model.getNodeData([id]); | ||
break; | ||
case 'edge': | ||
items = model.getEdgeData([id]); | ||
break; | ||
case 'combo': | ||
items = model.getComboData([id]); | ||
break; | ||
default: | ||
break; | ||
} | ||
let tooltipContent: { [key: string]: unknown } = {}; | ||
if (getContent) { | ||
tooltipContent.content = getContent(e, { items }); | ||
} else { | ||
tooltipContent = { | ||
title: targetType, | ||
data: items.map((item) => { | ||
return { | ||
name: 'ID', | ||
value: item.id || `${item.source} -> ${item.target}`, | ||
color: color || stroke, | ||
}; | ||
}), | ||
}; | ||
} | ||
this.tooltipElement.update({ | ||
x, | ||
y, | ||
style: { | ||
'.tooltip': { | ||
visibility: 'visible', | ||
}, | ||
}, | ||
...tooltipContent, | ||
}); | ||
} | ||
|
||
public hideTooltip(e: IG6GraphEvent) { | ||
const { | ||
client: { x, y }, | ||
} = e; | ||
if (!this.tooltipElement) return; | ||
this.tooltipElement.hide(x, y); | ||
} | ||
|
||
private initTooltip = () => { | ||
const { style, position, enterable, container } = this.options; | ||
const { canvas } = this.context; | ||
const { center } = this.context.canvas.getBounds(); | ||
const [x, y] = center; | ||
const [width, height] = canvas.getSize(); | ||
const tooltipElement = new TooltipComponent({ | ||
className: 'tooltip', | ||
style: { | ||
x, | ||
y, | ||
container, | ||
bounding: { | ||
x: 0, | ||
y: 0, | ||
width, | ||
height, | ||
}, | ||
position, | ||
enterable, | ||
title: '', | ||
offset: [10, 10], | ||
style, | ||
}, | ||
}); | ||
this.$element.appendChild(tooltipElement.HTMLTooltipElement); | ||
return tooltipElement; | ||
}; | ||
|
||
public destroy(): void { | ||
this.unbundEvents(); | ||
this.$element.remove(); | ||
super.destroy(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters