UNPKG

@antv/x6

Version:

JavaScript diagramming library that uses SVG and HTML for rendering

247 lines (193 loc) 5.51 kB
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}`