feat: add following config for selection (#687)

This commit is contained in:
vector
2021-02-23 20:29:13 +08:00
committed by GitHub
parent 5272e8cab7
commit 4de6701c0b
3 changed files with 107 additions and 42 deletions

View File

@ -288,7 +288,8 @@ export class Selection extends View<Selection.EventArgs> {
protected stopSelecting(evt: JQuery.MouseUpEvent) {
const graph = this.graph
const action = this.getEventData<EventData.Common>(evt).action
const eventData = this.getEventData<EventData.Common>(evt)
const action = eventData.action
switch (action) {
case 'selecting': {
let width = this.$container.width()!
@ -306,8 +307,15 @@ export class Selection extends View<Selection.EventArgs> {
}
case 'translating': {
this.graph.model.stopBatch('move-selection')
const client = graph.snapToGrid(evt.clientX, evt.clientY)
if (!this.options.following) {
const data = eventData as EventData.Translating
this.updateSelectedNodesPosition({
dx: data.clientX - data.originX,
dy: data.clientY - data.originY,
})
}
this.graph.model.stopBatch('move-selection')
this.notifyBoxEvent('box:mouseup', evt, client.x, client.y)
break
}
@ -328,6 +336,7 @@ export class Selection extends View<Selection.EventArgs> {
}
protected onSelectionBoxMouseDown(evt: JQuery.MouseDownEvent) {
evt.stopPropagation()
const e = this.normalizeEvent(evt)
if (this.options.movable) {
@ -348,9 +357,69 @@ export class Selection extends View<Selection.EventArgs> {
action: 'translating',
clientX: client.x,
clientY: client.y,
originX: client.x,
originY: client.y,
})
}
protected getSelectionOffset(client: Point, data: EventData.Translating) {
let dx = client.x - data.clientX
let dy = client.y - data.clientY
const restrict = this.graph.hook.getRestrictArea()
if (restrict) {
const cells = this.collection.toArray()
const totalBBox = Cell.getCellsBBox(cells)!
let minDx = restrict.x - totalBBox.x
let minDy = restrict.y - totalBBox.y
let maxDx =
restrict.x + restrict.width - (totalBBox.x + totalBBox.width)
let maxDy =
restrict.y + restrict.height - (totalBBox.y + totalBBox.height)
if (dx < minDx) {
dx = minDx
}
if (dy < minDy) {
dy = minDy
}
if (maxDx < dx) {
dx = maxDx
}
if (maxDy < dy) {
dy = maxDy
}
if (!this.options.following) {
const offsetX = client.x - data.originX
const offsetY = client.y - data.originY
dx = offsetX <= minDx || offsetX >= maxDx ? 0 : dx
dy = offsetY <= minDy || offsetY >= maxDy ? 0 : dy
}
}
return {
dx,
dy,
}
}
protected updateSelectedNodesPosition(offset: { dx: number, dy: number }) {
const { dx, dy } = offset
if (dx || dy) {
if ((this.translateSelectedNodes(dx, dy), this.boxesUpdated)) {
if (this.collection.length > 1) {
this.updateSelectionBoxes()
}
} else {
const scale = this.graph.transform.getScale()
this.$boxes.add(this.$selectionContainer).css({
left: `+=${dx * scale.sx}`,
top: `+=${dy * scale.sy}`,
})
}
}
}
protected adjustSelection(evt: JQuery.MouseMoveEvent) {
const e = this.normalizeEvent(evt)
const eventData = this.getEventData<EventData.Common>(e)
@ -377,50 +446,20 @@ export class Selection extends View<Selection.EventArgs> {
}
case 'translating': {
const data = eventData as EventData.Translating
const client = this.graph.snapToGrid(e.clientX, e.clientY)
let dx = client.x - data.clientX
let dy = client.y - data.clientY
const restrict = this.graph.hook.getRestrictArea()
if (restrict) {
const cells = this.collection.toArray()
const totalBBox = Cell.getCellsBBox(cells)!
const minDx = restrict.x - totalBBox.x
const minDy = restrict.y - totalBBox.y
const maxDx =
restrict.x + restrict.width - (totalBBox.x + totalBBox.width)
const maxDy =
restrict.y + restrict.height - (totalBBox.y + totalBBox.height)
if (dx < minDx) {
dx = minDx
}
if (dy < minDy) {
dy = minDy
}
if (maxDx < dx) {
dx = maxDx
}
if (maxDy < dy) {
dy = maxDy
}
const data = eventData as EventData.Translating
const offset = this.getSelectionOffset(client, data)
if (this.options.following) {
this.updateSelectedNodesPosition(offset)
} else {
this.updateContainerPosition(offset)
}
if (dx || dy) {
if ((this.translateSelectedNodes(dx, dy), this.boxesUpdated)) {
if (this.collection.length > 1) {
this.updateSelectionBoxes()
}
} else {
const scale = this.graph.transform.getScale()
this.$boxes.add(this.$selectionContainer).css({
left: `+=${dx * scale.sx}`,
top: `+=${dy * scale.sy}`,
})
}
if (offset.dx) {
data.clientX = client.x
data.clientY = client.y
}
if (offset.dy) {
data.clientY = client.y
}
this.notifyBoxEvent('box:mousemove', evt, client.x, client.y)
break
}
@ -566,6 +605,16 @@ export class Selection extends View<Selection.EventArgs> {
this.$handleContainer = this.$selectionContainer
}
protected updateContainerPosition(offset: { dx: number, dy: number }) {
if (offset.dx || offset.dy) {
this.$selectionContainer
.css({
left: `+=${offset.dx}`,
top: `+=${offset.dy}`,
})
}
}
protected updateContainer() {
const origin = { x: Infinity, y: Infinity }
const corner = { x: 0, y: 0 }
@ -910,6 +959,7 @@ export namespace Selection {
showEdgeSelectionBox?: boolean
showNodeSelectionBox?: boolean
movable?: boolean
following?: boolean
useCellGeometry?: boolean
content?: Content
}
@ -995,6 +1045,7 @@ namespace Private {
export const defaultOptions: Partial<Selection.Options> = {
movable: true,
following: true,
strict: false,
useCellGeometry: false,
content(selection) {
@ -1054,6 +1105,8 @@ namespace EventData {
action: 'translating'
clientX: number
clientY: number
originX: number
originY: number
}
export interface SelectionBox {

View File

@ -134,6 +134,12 @@ if (graph.isRubberbandEnabled()) {
<img src="https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*MD11S4oxIJIAAAAAAAAAAAAAARQnAQ" width="600px" height="300px" alt="movable"/>
### following
在多选情况下,选中的节点是否跟随鼠标实时移动,默认为 `true`。下图是设置为 `false` 下的表现:
<img src="https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*nt5KQqqZHDUAAAAAAAAAAAAAARQnAQ" width="600px" height="300px" alt="following"/>
### content
设置附加显示的内容。

View File

@ -134,6 +134,12 @@ if (graph.isRubberbandEnabled()) {
<img src="https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*MD11S4oxIJIAAAAAAAAAAAAAARQnAQ" width="600px" height="300px" alt="movable"/>
### following
在多选情况下,选中的节点是否跟随鼠标实时移动,默认为 `true`。下图是设置为 `false` 下的表现:
<img src="https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*nt5KQqqZHDUAAAAAAAAAAAAAARQnAQ" width="600px" height="300px" alt="following"/>
### content
设置附加显示的内容。