@antv/x6
Version:
JavaScript diagramming library that uses SVG and HTML for rendering
247 lines (193 loc) • 5.51 kB
text/typescript
import { Dom, type KeyValue, ObjectExt } from '../../common'
import type { ViewEvents } from '../../types'
import type { CellView } from '../cell'
import { Markup, type MarkupType } from '../markup'
import { View } from '../view'
import { createViewElement } from '../view/util'
import type { ToolsView } from './tool-view'
import { getClassName } from './util'
export interface ToolItemOptions {
name?: string
tagName?: string
isSVGElement?: boolean
className?: string
markup?: Exclude<MarkupType, string>
events?: ViewEvents | null
documentEvents?: ViewEvents | null
focusOpacity?: number
}
export type ToolItemDefinition =
| typeof ToolItem
| (new (options: ToolItemOptions) => ToolItem)
export class ToolItem<
TargetView extends CellView = CellView,
Options extends ToolItemOptions = ToolItemOptions,
> extends View {
// #region static
public static toStringTag = `X6.${ToolItem.name}`
public static defaults: ToolItemOptions = {
isSVGElement: true,
tagName: 'g',
}
public static isToolItem(instance: any): instance is ToolItem {
if (instance == null) {
return false
}
if (instance instanceof ToolItem) {
return true
}
const tag = instance[Symbol.toStringTag]
const view = instance as ToolItem
if (
(tag == null || tag === ToolItemToStringTag) &&
view.graph != null &&
view.cell != null &&
typeof view.config === 'function' &&
typeof view.update === 'function' &&
typeof view.focus === 'function' &&
typeof view.blur === 'function' &&
typeof view.show === 'function' &&
typeof view.hide === 'function' &&
typeof view.isVisible === 'function'
) {
return true
}
return false
}
public static define<T extends ToolItemOptions>(options: T) {
const Base = this as any as ToolItemDefinition
const tool = ObjectExt.createClass<ToolItemDefinition>(
getClassName(options.name),
Base,
) as typeof ToolItem
tool.config(options)
return tool
}
public static getDefaults<T extends ToolItemOptions>() {
return this.defaults as T
}
public static config<T extends ToolItemOptions = ToolItemOptions>(
options: Partial<T>,
) {
this.defaults = this.getOptions(options)
}
public static getOptions<T extends ToolItemOptions = ToolItemOptions>(
options: Partial<T>,
): T {
return ObjectExt.merge(
ObjectExt.cloneDeep(this.getDefaults()),
options,
) as T
}
// #endregion
public readonly options: Options
public container: HTMLElement | SVGElement
public parent: ToolsView
protected cellView: TargetView
protected visible = true
protected childNodes: KeyValue<Element>
public get graph() {
return this.cellView.graph
}
public get cell() {
return this.cellView.cell
}
public get name() {
return this.options.name
}
protected get [Symbol.toStringTag]() {
return ToolItemToStringTag
}
constructor(options: Partial<Options> = {}) {
super()
this.options = this.getOptions(options)
this.container = createViewElement(
this.options.tagName || 'g',
this.options.isSVGElement !== false,
)
Dom.addClass(this.container, this.prefixClassName('cell-tool'))
if (typeof this.options.className === 'string') {
Dom.addClass(this.container, this.options.className)
}
this.init()
}
protected init() {}
protected getOptions(options: Partial<Options>): Options {
const ctor = this.constructor as any as ToolItem
return ctor.getOptions(options) as Options
}
delegateEvents() {
if (this.options.events) {
super.delegateEvents(this.options.events)
}
return this
}
config(view: CellView, toolsView: ToolsView) {
this.cellView = view as TargetView
this.parent = toolsView
this.stamp(this.container)
if (this.cell.isEdge()) {
Dom.addClass(this.container, this.prefixClassName('edge-tool'))
} else if (this.cell.isNode()) {
Dom.addClass(this.container, this.prefixClassName('node-tool'))
}
if (this.name) {
this.container.setAttribute('data-tool-name', this.name)
}
this.delegateEvents()
return this
}
render() {
this.empty()
const markup = this.options.markup
if (markup) {
const meta = Markup.parseJSONMarkup(markup)
this.container.appendChild(meta.fragment)
this.childNodes = meta.selectors as KeyValue<Element>
}
this.onRender()
return this
}
protected onRender() {}
update() {
return this
}
protected stamp(elem: Element) {
if (elem) {
elem.setAttribute('data-cell-id', this.cellView.cell.id)
}
}
show() {
this.container.style.display = ''
this.visible = true
return this
}
hide() {
this.container.style.display = 'none'
this.visible = false
return this
}
isVisible() {
return this.visible
}
focus() {
const opacity = this.options.focusOpacity
if (opacity != null && Number.isFinite(opacity)) {
this.container.style.opacity = `${opacity}`
}
this.parent.focus(this)
return this
}
blur() {
this.container.style.opacity = ''
this.parent.blur(this)
return this
}
protected guard(evt: Dom.EventObject) {
if (this.graph == null || this.cellView == null) {
return true
}
return this.graph.view.guard(evt, this.cellView)
}
}
export const ToolItemToStringTag = `X6.${ToolItem.name}`