UNPKG

@antv/x6

Version:

JavaScript diagramming library that uses SVG and HTML for rendering.

231 lines (211 loc) 5.93 kB
import { ToolsView } from '../../view/tool' import { Cell, Edge } from '../../model' import { CellView, NodeView, EdgeView } from '../../view' import { Point } from '../../geometry' import { Dom, FunctionExt } from '../../util' export class CellEditor extends ToolsView.ToolItem< NodeView | EdgeView, CellEditor.CellEditorOptions & { event: JQuery.MouseEventBase } > { private editor: HTMLDivElement private labelIndex = -1 private distance = 0.5 render() { this.createElement() this.update() this.autoFocus() this.delegateDocumentEvents(this.options.documentEvents!) return this } createElement() { const { cell } = this const classNames = [ this.prefixClassName(`${cell.isEdge() ? 'edge' : 'node'}-tool-editor`), this.prefixClassName('cell-tool-editor'), ] this.editor = ToolsView.createElement('div', false) as HTMLDivElement this.addClass(classNames, this.editor) this.editor.contentEditable = 'true' this.container.appendChild(this.editor) } update() { const { graph, cell, editor } = this const style = editor.style // set tool position let pos = new Point() let minWidth = 20 if (cell.isNode()) { pos = cell.getBBox().center minWidth = cell.size().width - 4 } else if (cell.isEdge()) { const e = this.options.event const target = e.target const parent = target.parentElement const isEdgeLabel = parent && Dom.hasClass(parent, this.prefixClassName('edge-label')) if (isEdgeLabel) { const index = parent.getAttribute('data-index') || '0' this.labelIndex = parseInt(index, 10) const matrix = parent.getAttribute('transform') const { translation } = Dom.parseTransformString(matrix) pos = new Point(translation.tx, translation.ty) minWidth = Dom.getBBox(target).width } else { pos = graph.clientToLocal(Point.create(e.clientX, e.clientY)) const view = this.cellView as EdgeView const d = view.path.closestPointLength(pos) this.distance = d } } pos = graph.localToGraph(pos) style.left = `${pos.x}px` style.top = `${pos.y}px` style.minWidth = `${minWidth}px` // set tool transform const scale = graph.scale() style.transform = `scale(${scale.sx}, ${scale.sy}) translate(-50%, -50%)` // set font style const attrs = this.options.attrs style.fontSize = `${attrs.fontSize}px` style.fontFamily = attrs.fontFamily style.color = attrs.color style.backgroundColor = attrs.backgroundColor // set init value const getText = this.options.getText let text if (typeof getText === 'function') { text = FunctionExt.call(getText, this.cellView, { cell: this.cell, index: this.labelIndex, }) } editor.innerText = text || '' return this } onDocumentMouseDown(e: JQuery.MouseDownEvent) { if (e.target !== this.editor) { const cell = this.cell const value = this.editor.innerText.replace(/\n$/, '') || '' // set value const setText = this.options.setText if (typeof setText === 'function') { FunctionExt.call(setText, this.cellView, { cell: this.cell, value, index: this.labelIndex, distance: this.distance, }) } // remove tool cell.removeTool(cell.isEdge() ? 'edge-editor' : 'node-editor') this.undelegateDocumentEvents() } } onDblClick(e: JQuery.DoubleClickEvent) { e.stopPropagation() } onMouseDown(e: JQuery.MouseDownEvent) { e.stopPropagation() } autoFocus() { setTimeout(() => { this.editor.focus() this.selectText() }) } selectText() { if (window.getSelection) { const range = document.createRange() const selection = window.getSelection()! range.selectNodeContents(this.editor) selection.removeAllRanges() selection.addRange(range) } } } export namespace CellEditor { export interface CellEditorOptions extends ToolsView.ToolItem.Options { attrs: { fontSize: number fontFamily: string color: string backgroundColor: string } getText: ( this: CellView, args: { cell: Cell index?: number }, ) => string setText: ( this: CellView, args: { cell: Cell value: string index?: number distance?: number }, ) => void } } export namespace CellEditor { CellEditor.config({ tagName: 'div', isSVGElement: false, events: { dblclick: 'onDblClick', mousedown: 'onMouseDown', }, documentEvents: { mousedown: 'onDocumentMouseDown', }, }) } export namespace CellEditor { export const NodeEditor = CellEditor.define<CellEditorOptions>({ attrs: { fontSize: 14, fontFamily: 'Arial, helvetica, sans-serif', color: '#000', backgroundColor: '#fff', }, getText({ cell }) { return cell.attr('text/text') }, setText({ cell, value }) { cell.attr('text/text', value) }, }) export const EdgeEditor = CellEditor.define<CellEditorOptions>({ attrs: { fontSize: 14, fontFamily: 'Arial, helvetica, sans-serif', color: '#000', backgroundColor: '#fff', }, getText({ cell, index }) { if (index === -1) { return '' } return cell.prop(`labels/${index}/attrs/label/text`) }, setText({ cell, value, index, distance }) { const edge = cell as Edge if (index === -1) { edge.appendLabel({ position: { distance: distance!, }, attrs: { label: { text: value, }, }, }) } else { edge.prop(`labels/${index}/attrs/label/text`, value) } }, }) }