Compare commits
13 Commits
v2.0.6-bet
...
v2.0.6-bet
Author | SHA1 | Date | |
---|---|---|---|
c9494e7f79 | |||
1e7f132bed | |||
9a1834e808 | |||
6e1bd9b530 | |||
12e4ac55d7 | |||
294672b306 | |||
68c2346e0c | |||
24de1254a1 | |||
88918f7611 | |||
5e102a39c5 | |||
2f310fcceb | |||
40d53355ce | |||
1dcb3d92fd |
@ -1,264 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Graph, Node, Edge, NodeView } from '@antv/x6'
|
||||
import { Point, Angle } from '@antv/x6-geometry'
|
||||
import { Interp } from '@antv/x6-common'
|
||||
import '../index.less'
|
||||
|
||||
class BallView extends NodeView {
|
||||
protected speed: number = 0
|
||||
protected angle: number = 0
|
||||
protected edge: Edge | null
|
||||
|
||||
protected init() {
|
||||
this.cell.transition('attrs/label/opacity', 1, {
|
||||
delay: 0,
|
||||
duration: 3000,
|
||||
timing: 'inout',
|
||||
interp: function (a: number, b: number) {
|
||||
return function (t: number) {
|
||||
return a + b * (1 - Math.abs(1 - 2 * t))
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
this.cell.on('transition:complete', ({ cell, path }) => {
|
||||
if (path === 'position' && this.speed > 5) {
|
||||
this.speed /= cell.prop<number>('bounciness') || 2
|
||||
this.fly({ angle: 180 - this.angle, speed: this.speed })
|
||||
}
|
||||
})
|
||||
|
||||
this.cell.on('change:position', ({ cell, current }) => {
|
||||
const node = cell as any as Node
|
||||
this.angle = Point.create(node.getPosition()).theta(
|
||||
node.previous('position'),
|
||||
)
|
||||
//this.speed = we are using constant speed for simplicity
|
||||
|
||||
if (current) {
|
||||
if (
|
||||
current.x < 0 ||
|
||||
current.x > this.graph.options.width - node.getSize().width
|
||||
) {
|
||||
this.angle -= 180
|
||||
node.position(node.previous('position')!.x, current.y, {
|
||||
silent: true,
|
||||
})
|
||||
cell.stopTransition('position')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fly(opts: { speed?: number; angle?: number } = {}) {
|
||||
const options = {
|
||||
speed: 100,
|
||||
angle: 90,
|
||||
...opts,
|
||||
}
|
||||
const pos = this.cell.getPosition()
|
||||
const size = this.cell.getSize()
|
||||
const ga = 9.81
|
||||
const h0 = this.graph.options.height - pos.y - size.height
|
||||
const v0 = options.speed
|
||||
const sin1 = Math.sin(Angle.toRad(options.angle))
|
||||
|
||||
const flightTime =
|
||||
(v0 * sin1 +
|
||||
Math.sqrt(Math.pow(v0, 2) * Math.pow(sin1, 2) + 2 * h0 * ga)) /
|
||||
ga
|
||||
|
||||
this.cell.transition('position', options, {
|
||||
duration: 100 * flightTime,
|
||||
interp(
|
||||
position: Point.PointLike,
|
||||
params: { speed: number; angle: number },
|
||||
) {
|
||||
return function (t: number) {
|
||||
t = flightTime * t
|
||||
return {
|
||||
x:
|
||||
position.x +
|
||||
params.speed * t * Math.cos((Math.PI / 180) * params.angle),
|
||||
y:
|
||||
position.y -
|
||||
params.speed * t * Math.sin((Math.PI / 180) * params.angle) +
|
||||
(ga / 2) * t * t,
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
this.cell.transition('angle', -options.angle, {
|
||||
duration: 100 * flightTime,
|
||||
})
|
||||
|
||||
this.speed = options.speed
|
||||
this.angle = options.angle
|
||||
}
|
||||
|
||||
onMouseDown(e: JQuery.MouseDownEvent, x: number, y: number) {
|
||||
console.log('mousedown1')
|
||||
// Do not allow drag element while it's still in a transition.
|
||||
if (this.cell.getTransitions().indexOf('position') > -1) {
|
||||
console.log('mousedown2')
|
||||
return
|
||||
}
|
||||
|
||||
this.edge = this.graph.addEdge({
|
||||
shape: 'edge',
|
||||
source: this.cell.getBBox().getCenter(),
|
||||
target: { x, y },
|
||||
zIndex: -1,
|
||||
attrs: {
|
||||
line: {
|
||||
stroke: 'rgba(0,0,0,0.1)',
|
||||
strokeWidth: 6,
|
||||
targetMarker: {
|
||||
stroke: 'black',
|
||||
strokeWidth: 2,
|
||||
d: 'M 20 -10 L 0 0 L 20 10 z',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Change the marker arrow color.
|
||||
this.edge.on('change:target', ({ cell }) => {
|
||||
const edge = cell as any as Edge
|
||||
const sourcePoint = edge.getSourcePoint()!
|
||||
const targetPoint = edge.getTargetPoint()!
|
||||
const dist = sourcePoint.distance(targetPoint)
|
||||
const maxDist = Math.max(
|
||||
this.graph.options.width,
|
||||
this.graph.options.height,
|
||||
)
|
||||
const interp = Interp.color('#ffffff', '#ff0000')
|
||||
edge.attr('line/targetMarker/fill', interp(dist / maxDist / Math.sqrt(2)))
|
||||
})
|
||||
}
|
||||
|
||||
onMouseMove(e: JQuery.MouseMoveEvent, x: number, y: number) {
|
||||
if (this.edge) {
|
||||
this.edge.setTarget({ x, y })
|
||||
}
|
||||
}
|
||||
|
||||
onMouseUp(e: JQuery.MouseUpEvent, x: number, y: number) {
|
||||
if (!this.edge) {
|
||||
return
|
||||
}
|
||||
|
||||
const sourcePoint = this.edge.getSourcePoint()!
|
||||
const targetPoint = this.edge.getTargetPoint()!
|
||||
|
||||
this.edge.remove()
|
||||
this.edge = null
|
||||
|
||||
this.fly({
|
||||
angle: Math.abs(targetPoint.theta(sourcePoint) - 180),
|
||||
speed: sourcePoint.distance(targetPoint) / 2,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
NodeView.registry.register('ball', BallView as any, true)
|
||||
|
||||
Node.registry.register(
|
||||
'ball',
|
||||
{
|
||||
view: 'ball',
|
||||
markup: [
|
||||
{
|
||||
tagName: 'text',
|
||||
selector: 'label',
|
||||
},
|
||||
{
|
||||
tagName: 'image',
|
||||
selector: 'ball',
|
||||
},
|
||||
],
|
||||
attrs: {
|
||||
label: {
|
||||
text: 'Drag me!',
|
||||
fontSize: 40,
|
||||
fontWeight: 900,
|
||||
refX: 0.5,
|
||||
refY: -20,
|
||||
textVerticalAnchor: 'middle',
|
||||
textAnchor: 'middle',
|
||||
fill: 'white',
|
||||
strokeWidth: 2,
|
||||
stroke: 'black',
|
||||
opacity: 0,
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
ball: {
|
||||
refWidth: 1,
|
||||
refHeight: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
)
|
||||
|
||||
export default class Example extends React.Component {
|
||||
private container: HTMLDivElement
|
||||
|
||||
componentDidMount() {
|
||||
const graph = new Graph({
|
||||
container: this.container,
|
||||
width: 650,
|
||||
height: 400,
|
||||
grid: 1,
|
||||
})
|
||||
|
||||
graph.addNode({
|
||||
shape: 'ball',
|
||||
x: 250,
|
||||
y: 370,
|
||||
width: 30,
|
||||
height: 30,
|
||||
bounciness: 3,
|
||||
attrs: {
|
||||
image: {
|
||||
'xlink:href':
|
||||
'data:image/svg+xml;base64,PHN2ZyB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIiB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIgaWQ9InN2ZzIiIHZpZXdCb3g9IjAgMCA1MTAgNTEwIiB2ZXJzaW9uPSIxLjEiIGlua3NjYXBlOnZlcnNpb249IjAuNDcgcjIyNTgzIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBzb2RpcG9kaTpkb2NuYW1lPSJibHVlIGJhbGwuc3ZnIj4KICA8c29kaXBvZGk6bmFtZWR2aWV3IHBhZ2Vjb2xvcj0iI2ZmZmZmZiIgYm9yZGVyY29sb3I9IiM2NjY2NjYiIGJvcmRlcm9wYWNpdHk9IjEiIG9iamVjdHRvbGVyYW5jZT0iMTAiIGdyaWR0b2xlcmFuY2U9IjEwIiBndWlkZXRvbGVyYW5jZT0iMTAiIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIiBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIiBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEwMjQiIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjU0OCIgaWQ9Im5hbWVkdmlldzE2IiBzaG93Z3JpZD0iZmFsc2UiIGlua3NjYXBlOnNuYXAtZ2xvYmFsPSJmYWxzZSIgaW5rc2NhcGU6em9vbT0iMC40MzY2Mzc1MyIgaW5rc2NhcGU6Y3g9IjI1Ni4yMzMxMSIgaW5rc2NhcGU6Y3k9IjI1OC44NTEwNyIgaW5rc2NhcGU6d2luZG93LXg9Ii04IiBpbmtzY2FwZTp3aW5kb3cteT0iLTgiIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIvPgogIDxkZWZzIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGF0aC1lZmZlY3QgZWZmZWN0PSJzcGlybyIgaWQ9InBhdGgtZWZmZWN0MzYyMCIgaXNfdmlzaWJsZT0idHJ1ZSIvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIGlua3NjYXBlOnZwX3g9IjAgOiAyNTUgOiAxIiBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiIGlua3NjYXBlOnZwX3o9IjUxMCA6IDI1NSA6IDEiIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNTUgOiAxNzAgOiAxIiBpZD0icGVyc3BlY3RpdmUxOCIvPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXJHcmFkaWVudDM1OTQiPgogICAgICA8c3RvcCBpZD0ic3RvcDM1OTYiIHN0b3AtY29sb3I9IiNGRkYiIG9mZnNldD0iMCIvPgogICAgICA8c3RvcCBpZD0ic3RvcDM1OTgiIHN0b3AtY29sb3I9IiMwMDAiIHN0b3Atb3BhY2l0eT0iMC40MTA3MTQzIiBvZmZzZXQ9IjEiLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8ZmlsdGVyIGlkPSJmaWx0ZXI0MDY3IiBoZWlnaHQ9IjEuMjYwMTk3MiIgd2lkdGg9IjEuMjg0MjYyOCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiB5PSItMC4xMzAwOTg1OCIgeD0iLTAuMTQyMTMxNCI+CiAgICAgIDxmZUdhdXNzaWFuQmx1ciBpZD0iZmVHYXVzc2lhbkJsdXI0MDY5IiBzdGREZXZpYXRpb249IjEuOTEyMzg5NCIvPgogICAgPC9maWx0ZXI+CiAgICA8cmFkaWFsR3JhZGllbnQgaWQ9InJhZGlhbEdyYWRpZW50NDA4MyIgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzU5NCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGN5PSI0NzkuNTQiIGN4PSIyNzIuMTUiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMzExNjQ5OTIsMS4zMTAxMDczLC0xLjMxMDE2NjEsLTAuMzExNjYzODksMTA0MS4yNzkxLDE5OS4yMDMwOSkiIHI9IjI1My4yNiIvPgogICAgPGZpbHRlciB4PSItMC4xNDMxMjQ1NSIgeT0iLTAuMTMxMDEwNzYiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgd2lkdGg9IjEuMjg2MjQ5IiBoZWlnaHQ9IjEuMjYyMDIxNSIgaWQ9ImZpbHRlcjM2MDEiPgogICAgICA8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIyMy4xMDkyNDgiIGlkPSJmZUdhdXNzaWFuQmx1cjM2MDMiLz4KICAgIDwvZmlsdGVyPgogICAgPGZpbHRlciB4PSItMC4xNDIxMzE0IiB5PSItMC4xMzAwOTg1OCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiB3aWR0aD0iMS4yODQyNjI4IiBoZWlnaHQ9IjEuMjYwMTk3MiIgaWQ9ImZpbHRlcjM2MDUiPgogICAgICA8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIyMi45NDg2NzMiIGlkPSJmZUdhdXNzaWFuQmx1cjM2MDciLz4KICAgIDwvZmlsdGVyPgogICAgPGZpbHRlciB4PSItMC4xNDIxMzE0IiB5PSItMC4xMzAwOTg1OCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiB3aWR0aD0iMS4yODQyNjI4IiBoZWlnaHQ9IjEuMjYwMTk3MiIgaWQ9ImZpbHRlcjM2MDkiPgogICAgICA8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIyMi45NDg2NzMiIGlkPSJmZUdhdXNzaWFuQmx1cjM2MTEiLz4KICAgIDwvZmlsdGVyPgogICAgPGZpbHRlciBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIGlkPSJmaWx0ZXIzNzU2Ij4KICAgICAgPGZlR2F1c3NpYW5CbHVyIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgc3RkRGV2aWF0aW9uPSI3LjMxNTE1MzMiIGlkPSJmZUdhdXNzaWFuQmx1cjM3NTgiLz4KICAgIDwvZmlsdGVyPgogIDwvZGVmcz4KICA8bWV0YWRhdGEgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiLz4KICAgICAgICA8ZGM6dGl0bGUvPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZyBpZD0ibGF5ZXIxIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNTQuMjg1NjksLTIyOC4wNzY0OSkiPgogICAgPHBhdGggaWQ9InBhdGgyODE4IiBzdHJva2UtbGluZWpvaW49ImJldmVsIiBzdHlsZT0ic3Ryb2tlLWRhc2hhcnJheTpub25lOyIgZD0iTTY2MCw0ODUuNzhhMjUyLjg2LDI1Mi44NiwwLDEsMSwwLC0wLjU2IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0LC0yKSIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAuNDc5IiBzdHJva2U9IiM3ODc4NzgiIHN0cm9rZS1taXRlcmxpbWl0PSI0IiBzdHJva2Utd2lkdGg9IjAuODAwMDAwMDEiIGZpbGw9IiMxMDZkZDUiLz4KICAgIDxwYXRoIGlkPSJwYXRoMzU5MiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNCwtMikiIGZpbGw9InVybCgjcmFkaWFsR3JhZGllbnQ0MDgzKSIgZD0iTTY2MCw0ODUuNzhhMjUyLjg2LDI1Mi44NiwwLDEsMSwwLC0wLjU2Ii8+CiAgICA8cGF0aCBpZD0icGF0aDQwNzgiIGQ9Ik02NjAsNDg1Ljc4YTI1Mi44NiwyNTIuODYsMCwxLDEsMCwtMC41NiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNCwtMikiIGZpbGw9InVybCgjcmFkaWFsR3JhZGllbnQ0MDgzKSIvPgogIDwvZz4KPC9zdmc+',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
graph.addNode({
|
||||
shape: 'ball',
|
||||
x: 400,
|
||||
y: 350,
|
||||
width: 50,
|
||||
height: 50,
|
||||
bounciness: 1.5,
|
||||
attrs: {
|
||||
image: {
|
||||
'xlink:href':
|
||||
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iODAwIiBoZWlnaHQ9IjgwMCIgdmlld0JveD0iLTEwNSAtMTA1IDIxMCAyMTAiPgogICA8ZGVmcz4KICAgICAgPGNsaXBQYXRoIGlkPSJiYWxsIj4KICAgICAgICAgPGNpcmNsZSByPSIxMDAiIHN0cm9rZS13aWR0aD0iMCIvPgogICAgICA8L2NsaXBQYXRoPgogICAgICA8cmFkaWFsR3JhZGllbnQgaWQ9InNoYWRvdzEiIGN4PSIuNCIgY3k9Ii4zIiByPSIuOCI+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0id2hpdGUiIHN0b3Atb3BhY2l0eT0iMSIvPgogICAgICAgIDxzdG9wIG9mZnNldD0iLjgiIHN0b3AtY29sb3I9IiNFRUVFRUUiIHN0b3Atb3BhY2l0eT0iMSIvPgogICAgICA8L3JhZGlhbEdyYWRpZW50PgogICAgICA8cmFkaWFsR3JhZGllbnQgaWQ9InNoYWRvdzIiIGN4PSIuNSIgY3k9Ii41IiByPSIuNSI+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIuOCIgc3RvcC1jb2xvcj0id2hpdGUiIHN0b3Atb3BhY2l0eT0iMCIvPgogICAgICAgIDxzdG9wIG9mZnNldD0iLjk5IiBzdG9wLWNvbG9yPSJibGFjayIgc3RvcC1vcGFjaXR5PSIuMyIvPgogICAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iYmxhY2siIHN0b3Atb3BhY2l0eT0iMSIvPgogICAgICA8L3JhZGlhbEdyYWRpZW50PgogICAgICA8ZyBpZD0iYmxhY2tfc3R1ZmYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsaXAtcGF0aD0idXJsKCNiYWxsKSI+CiAgICAgICAgIDxnIGZpbGw9ImJsYWNrIj4KICAgICAgICAgICAgPHBhdGggZD0iTSA2LC0zMiBRIDI2LC0yOCA0NiwtMTkgUSA1NywtMzUgNjQsLTQ3IFEgNTAsLTY4IDM3LC03NiBRIDE3LC03NSAxLC02OCBRIDQsLTUxIDYsLTMyIi8+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0gLTI2LC0yIFEgLTQ1LC04IC02MiwtMTEgUSAtNzQsNSAtNzYsMjIgUSAtNjksNDAgLTUwLDU0IFEgLTMyLDQ3IC0xNywzOSBRIC0yMywxNSAtMjYsLTIiLz4KICAgICAgICAgICAgPHBhdGggZD0iTSAtOTUsMjIgUSAtMTAyLDEyIC0xMDIsLTggViA4MCBIIC04NSBRIC05NSw0NSAtOTUsMjIiLz4KICAgICAgICAgICAgPHBhdGggZD0iTSA1NSwyNCBRIDQxLDQxIDI0LDUyIFEgMjgsNjUgMzEsNzkgUSA1NSw3OCA2OCw2NyBRIDc4LDUwIDgwLDM1IFEgNjUsMjggNTUsMjQiLz4KICAgICAgICAgICAgPHBhdGggZD0iTSAwLDEyMCBMIC0zLDk1IFEgLTI1LDkzIC00Miw4MiBRIC01MCw4NCAtNjAsODEiLz4KICAgICAgICAgICAgPHBhdGggZD0iTSAtOTAsLTQ4IFEgLTgwLC01MiAtNjgsLTQ5IFEgLTUyLC03MSAtMzUsLTc3IFEgLTM1LC0xMDAgLTQwLC0xMDAgSCAtMTAwIi8+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0gMTAwLC01NSBMIDg3LC0zNyBRIDk4LC0xMCA5Nyw1IEwgMTAwLDYiLz4KICAgICAgICAgPC9nPgogICAgICAgICA8ZyBmaWxsPSJub25lIj4KICAgICAgICAgICAgPHBhdGggZD0iTSA2LC0zMiBRIC0xOCwtMTIgLTI2LC0yICAgICAgICAgICAgICAgICAgICAgIE0gNDYsLTE5IFEgNTQsNSA1NSwyNCAgICAgICAgICAgICAgICAgICAgICBNIDY0LC00NyBRIDc3LC00NCA4NywtMzcgICAgICAgICAgICAgICAgICAgICAgTSAzNywtNzYgUSAzOSwtOTAgMzYsLTEwMCAgICAgICAgICAgICAgICAgICAgICBNIDEsLTY4IFEgLTEzLC03NyAtMzUsLTc3ICAgICAgICAgICAgICAgICAgICAgIE0gLTYyLC0xMSBRIC02NywtMjUgLTY4LC00OSAgICAgICAgICAgICAgICAgICAgICBNIC03NiwyMiBRIC04NSwyNCAtOTUsMjIgICAgICAgICAgICAgICAgICAgICAgTSAtNTAsNTQgUSAtNDksNzAgLTQyLDgyICAgICAgICAgICAgICAgICAgICAgIE0gLTE3LDM5IFEgMCw0OCAyNCw1MiAgICAgICAgICAgICAgICAgICAgICBNIDMxLDc5IFEgMjAsOTIgLTMsOTUgICAgICAgICAgICAgICAgICAgICAgTSA2OCw2NyBMIDgwLDgwICAgICAgICAgICAgICAgICAgICAgIE0gODAsMzUgUSA5MCwyNSA5Nyw1ICAgICAgICAgICAgICIvPgogICAgICAgICA8L2c+CiAgICAgIDwvZz4KICAgPC9kZWZzPgogICA8Y2lyY2xlIHI9IjEwMCIgZmlsbD0id2hpdGUiIHN0cm9rZT0ibm9uZSIvPgogICA8Y2lyY2xlIHI9IjEwMCIgZmlsbD0idXJsKCNzaGFkb3cxKSIgc3Ryb2tlPSJub25lIi8+CiAgIDx1c2UgeGxpbms6aHJlZj0iI2JsYWNrX3N0dWZmIiBzdHJva2U9IiNFRUUiIHN0cm9rZS13aWR0aD0iNyIvPgogICA8dXNlIHhsaW5rOmhyZWY9IiNibGFja19zdHVmZiIgc3Ryb2tlPSIjREREIiBzdHJva2Utd2lkdGg9IjQiLz4KICAgPHVzZSB4bGluazpocmVmPSIjYmxhY2tfc3R1ZmYiIHN0cm9rZT0iIzk5OSIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgIDx1c2UgeGxpbms6aHJlZj0iI2JsYWNrX3N0dWZmIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjEiLz4KICAgPGNpcmNsZSByPSIxMDAiIGZpbGw9InVybCgjc2hhZG93MikiIHN0cm9rZT0ibm9uZSIvPgo8L3N2Zz4=',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
refContainer = (container: HTMLDivElement) => {
|
||||
this.container = container
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="x6-graph-wrap">
|
||||
<div
|
||||
ref={this.refContainer}
|
||||
className="x6-graph"
|
||||
style={{
|
||||
backgroundImage: 'linear-gradient(to bottom, #00BFFF , #FFFFFF)',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import { Graph } from '@antv/x6'
|
||||
import { Scroller } from '@antv/x6-plugin-scroller'
|
||||
import { SplitBox } from '@antv/x6-react-components'
|
||||
import '@antv/x6-react-components/es/split-box/style/index.css'
|
||||
import '../index.less'
|
||||
@ -14,7 +15,7 @@ export default class Example extends React.Component {
|
||||
new Graph({
|
||||
container: this.graphContainer1,
|
||||
background: {
|
||||
color: '#F2F7FA',
|
||||
color: '#D94111',
|
||||
},
|
||||
autoResize: true,
|
||||
})
|
||||
@ -22,18 +23,23 @@ export default class Example extends React.Component {
|
||||
new Graph({
|
||||
container: this.graphContainer2,
|
||||
background: {
|
||||
color: '#F2F7FA',
|
||||
color: '#90C54C',
|
||||
},
|
||||
autoResize: true,
|
||||
})
|
||||
|
||||
new Graph({
|
||||
const graph = new Graph({
|
||||
container: this.graphContainer3,
|
||||
background: {
|
||||
color: '#F2F7FA',
|
||||
color: '#0491E4',
|
||||
},
|
||||
autoResize: true,
|
||||
})
|
||||
graph.use(
|
||||
new Scroller({
|
||||
enabled: true,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
refContainer1 = (container: HTMLDivElement) => {
|
||||
|
51
examples/x6-example-features/src/pages/basic/index.tsx
Normal file
51
examples/x6-example-features/src/pages/basic/index.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React from 'react'
|
||||
import { Graph } from '@antv/x6'
|
||||
import '../index.less'
|
||||
|
||||
export default class Example extends React.Component {
|
||||
private container: HTMLDivElement
|
||||
|
||||
componentDidMount() {
|
||||
const graph = new Graph({
|
||||
container: this.container,
|
||||
width: 800,
|
||||
height: 600,
|
||||
grid: true,
|
||||
})
|
||||
|
||||
const source = graph.addNode({
|
||||
shape: 'rect',
|
||||
x: 80,
|
||||
y: 80,
|
||||
width: 160,
|
||||
height: 60,
|
||||
label: 'source',
|
||||
})
|
||||
|
||||
const target = graph.addNode({
|
||||
shape: 'rect',
|
||||
x: 320,
|
||||
y: 320,
|
||||
width: 160,
|
||||
height: 60,
|
||||
label: 'target',
|
||||
})
|
||||
|
||||
graph.addEdge({
|
||||
source,
|
||||
target,
|
||||
})
|
||||
}
|
||||
|
||||
refContainer = (container: HTMLDivElement) => {
|
||||
this.container = container
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="x6-graph-wrap">
|
||||
<div ref={this.refContainer} className="x6-graph" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import { Graph, Cell } from '@antv/x6'
|
||||
import '../index.less'
|
||||
|
||||
Graph.registerNode(
|
||||
'event',
|
||||
'bpmn-event',
|
||||
{
|
||||
inherit: 'circle',
|
||||
attrs: {
|
||||
@ -18,7 +18,7 @@ Graph.registerNode(
|
||||
)
|
||||
|
||||
Graph.registerNode(
|
||||
'activity',
|
||||
'bpmn-activity',
|
||||
{
|
||||
inherit: 'rect',
|
||||
markup: [
|
||||
@ -61,7 +61,7 @@ Graph.registerNode(
|
||||
)
|
||||
|
||||
Graph.registerNode(
|
||||
'gateway',
|
||||
'bpmn-gateway',
|
||||
{
|
||||
inherit: 'polygon',
|
||||
attrs: {
|
||||
@ -98,14 +98,14 @@ Graph.registerEdge(
|
||||
const data = [
|
||||
{
|
||||
id: '1',
|
||||
shape: 'event',
|
||||
shape: 'bpmn-event',
|
||||
width: 40,
|
||||
height: 40,
|
||||
position: { x: 50, y: 180 },
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
shape: 'activity',
|
||||
shape: 'bpmn-activity',
|
||||
width: 100,
|
||||
height: 60,
|
||||
position: { x: 20, y: 280 },
|
||||
@ -119,7 +119,7 @@ const data = [
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
shape: 'gateway',
|
||||
shape: 'bpmn-gateway',
|
||||
width: 55,
|
||||
height: 55,
|
||||
position: { x: 170, y: 282.5 },
|
||||
@ -132,7 +132,7 @@ const data = [
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
shape: 'activity',
|
||||
shape: 'bpmn-activity',
|
||||
width: 100,
|
||||
height: 60,
|
||||
position: { x: 300, y: 240 },
|
||||
@ -140,7 +140,7 @@ const data = [
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
shape: 'activity',
|
||||
shape: 'bpmn-activity',
|
||||
width: 100,
|
||||
height: 60,
|
||||
position: { x: 300, y: 320 },
|
||||
@ -160,7 +160,7 @@ const data = [
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
shape: 'gateway',
|
||||
shape: 'bpmn-gateway',
|
||||
width: 55,
|
||||
height: 55,
|
||||
position: { x: 460, y: 282.5 },
|
||||
@ -179,7 +179,7 @@ const data = [
|
||||
},
|
||||
{
|
||||
id: '13',
|
||||
shape: 'activity',
|
||||
shape: 'bpmn-activity',
|
||||
width: 100,
|
||||
height: 60,
|
||||
position: { x: 560, y: 280 },
|
||||
@ -193,7 +193,7 @@ const data = [
|
||||
},
|
||||
{
|
||||
id: '15',
|
||||
shape: 'event',
|
||||
shape: 'bpmn-event',
|
||||
width: 40,
|
||||
height: 40,
|
||||
position: { x: 710, y: 290 },
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import { Graph, ObjectExt, Cell } from '@antv/x6'
|
||||
import { Graph, Cell } from '@antv/x6'
|
||||
import { ObjectExt } from '@antv/x6-common'
|
||||
import '../index.less'
|
||||
|
||||
Graph.registerNode(
|
||||
|
@ -1,453 +1,454 @@
|
||||
// import React from 'react'
|
||||
// import { Graph, Node, Path, Cell } from '@antv/x6'
|
||||
// import '@antv/x6-react-shape'
|
||||
// import '../index.less'
|
||||
// import './index.less'
|
||||
// interface NodeStatus {
|
||||
// id: string
|
||||
// status: 'default' | 'success' | 'failed' | 'running'
|
||||
// label?: string
|
||||
// }
|
||||
import React from 'react'
|
||||
import { Graph, Node, Cell } from '@antv/x6'
|
||||
import { Path } from '@antv/x6-geometry'
|
||||
import { register } from '@antv/x6-react-shape'
|
||||
import { Selection } from '@antv/x6-plugin-selection'
|
||||
import { Snapline } from '@antv/x6-plugin-snapline'
|
||||
import '../index.less'
|
||||
import './index.less'
|
||||
interface NodeStatus {
|
||||
id: string
|
||||
status: string
|
||||
label?: string
|
||||
}
|
||||
|
||||
// const image = {
|
||||
// logo: 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*evDjT5vjkX0AAAAAAAAAAAAAARQnAQ',
|
||||
// success:
|
||||
// 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*6l60T6h8TTQAAAAAAAAAAAAAARQnAQ',
|
||||
// failed:
|
||||
// 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SEISQ6My-HoAAAAAAAAAAAAAARQnAQ',
|
||||
// running:
|
||||
// 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*t8fURKfgSOgAAAAAAAAAAAAAARQnAQ',
|
||||
// }
|
||||
// export class AlgoNode extends React.Component<{ node?: Node }> {
|
||||
// shouldComponentUpdate() {
|
||||
// const { node } = this.props
|
||||
// if (node) {
|
||||
// if (node.hasChanged('data')) {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
const image = {
|
||||
logo: 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*evDjT5vjkX0AAAAAAAAAAAAAARQnAQ',
|
||||
success:
|
||||
'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*6l60T6h8TTQAAAAAAAAAAAAAARQnAQ',
|
||||
failed:
|
||||
'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SEISQ6My-HoAAAAAAAAAAAAAARQnAQ',
|
||||
running:
|
||||
'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*t8fURKfgSOgAAAAAAAAAAAAAARQnAQ',
|
||||
}
|
||||
export class AlgoNode extends React.Component<{ node?: Node }> {
|
||||
shouldComponentUpdate() {
|
||||
const { node } = this.props
|
||||
if (node) {
|
||||
if (node.hasChanged('data')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// render() {
|
||||
// const { node } = this.props
|
||||
// const data = node?.getData() as NodeStatus
|
||||
// const { label, status = 'default' } = data
|
||||
render() {
|
||||
const { node } = this.props
|
||||
const data = node?.getData() as NodeStatus
|
||||
const { label, status = 'default' } = data
|
||||
|
||||
// return (
|
||||
// <div className={`node ${status}`}>
|
||||
// <img src={image.logo} alt="logo" />
|
||||
// <span className="label">{label}</span>
|
||||
// <span className="status">
|
||||
// {status === 'success' && <img src={image.success} alt="success" />}
|
||||
// {status === 'failed' && <img src={image.failed} alt="failed" />}
|
||||
// {status === 'running' && <img src={image.running} alt="running" />}
|
||||
// </span>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
return (
|
||||
<div className={`node ${status}`}>
|
||||
<img src={image.logo} alt="logo" />
|
||||
<span className="label">{label}</span>
|
||||
<span className="status">
|
||||
{status === 'success' && <img src={image.success} alt="success" />}
|
||||
{status === 'failed' && <img src={image.failed} alt="failed" />}
|
||||
{status === 'running' && <img src={image.running} alt="running" />}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Graph.registerNode(
|
||||
// 'dag-node',
|
||||
// {
|
||||
// inherit: 'react-shape',
|
||||
// width: 180,
|
||||
// height: 36,
|
||||
// component: <AlgoNode />,
|
||||
// ports: {
|
||||
// groups: {
|
||||
// top: {
|
||||
// position: 'top',
|
||||
// attrs: {
|
||||
// circle: {
|
||||
// r: 4,
|
||||
// magnet: true,
|
||||
// stroke: '#C2C8D5',
|
||||
// strokeWidth: 1,
|
||||
// fill: '#fff',
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// bottom: {
|
||||
// position: 'bottom',
|
||||
// attrs: {
|
||||
// circle: {
|
||||
// r: 4,
|
||||
// magnet: true,
|
||||
// stroke: '#C2C8D5',
|
||||
// strokeWidth: 1,
|
||||
// fill: '#fff',
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// true,
|
||||
// )
|
||||
register(AlgoNode, {
|
||||
shape: 'dag-node',
|
||||
width: 180,
|
||||
height: 36,
|
||||
ports: {
|
||||
groups: {
|
||||
top: {
|
||||
position: 'top',
|
||||
attrs: {
|
||||
circle: {
|
||||
r: 4,
|
||||
magnet: true,
|
||||
stroke: '#C2C8D5',
|
||||
strokeWidth: 1,
|
||||
fill: '#fff',
|
||||
},
|
||||
},
|
||||
},
|
||||
bottom: {
|
||||
position: 'bottom',
|
||||
attrs: {
|
||||
circle: {
|
||||
r: 4,
|
||||
magnet: true,
|
||||
stroke: '#C2C8D5',
|
||||
strokeWidth: 1,
|
||||
fill: '#fff',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Graph.registerEdge(
|
||||
// 'dag-edge',
|
||||
// {
|
||||
// inherit: 'edge',
|
||||
// attrs: {
|
||||
// line: {
|
||||
// stroke: '#C2C8D5',
|
||||
// strokeWidth: 1,
|
||||
// targetMarker: null,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// true,
|
||||
// )
|
||||
Graph.registerEdge(
|
||||
'dag-edge',
|
||||
{
|
||||
inherit: 'edge',
|
||||
attrs: {
|
||||
line: {
|
||||
stroke: '#C2C8D5',
|
||||
strokeWidth: 1,
|
||||
targetMarker: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
)
|
||||
|
||||
// Graph.registerConnector(
|
||||
// 'algo-connector',
|
||||
// (s, e) => {
|
||||
// const offset = 4
|
||||
// const deltaY = Math.abs(e.y - s.y)
|
||||
// const control = Math.floor((deltaY / 3) * 2)
|
||||
Graph.registerConnector(
|
||||
'algo-connector',
|
||||
(s, e) => {
|
||||
const offset = 4
|
||||
const deltaY = Math.abs(e.y - s.y)
|
||||
const control = Math.floor((deltaY / 3) * 2)
|
||||
|
||||
// const v1 = { x: s.x, y: s.y + offset + control }
|
||||
// const v2 = { x: e.x, y: e.y - offset - control }
|
||||
const v1 = { x: s.x, y: s.y + offset + control }
|
||||
const v2 = { x: e.x, y: e.y - offset - control }
|
||||
|
||||
// return Path.normalize(
|
||||
// `M ${s.x} ${s.y}
|
||||
// L ${s.x} ${s.y + offset}
|
||||
// C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset}
|
||||
// L ${e.x} ${e.y}
|
||||
// `,
|
||||
// )
|
||||
// },
|
||||
// true,
|
||||
// )
|
||||
return Path.normalize(
|
||||
`M ${s.x} ${s.y}
|
||||
L ${s.x} ${s.y + offset}
|
||||
C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset}
|
||||
L ${e.x} ${e.y}
|
||||
`,
|
||||
)
|
||||
},
|
||||
true,
|
||||
)
|
||||
|
||||
// const data = [
|
||||
// {
|
||||
// id: '1',
|
||||
// shape: 'dag-node',
|
||||
// x: 290,
|
||||
// y: 110,
|
||||
// data: {
|
||||
// label: '读数据',
|
||||
// status: 'success',
|
||||
// },
|
||||
// ports: [
|
||||
// {
|
||||
// id: '1-1',
|
||||
// group: 'bottom',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// id: '2',
|
||||
// shape: 'dag-node',
|
||||
// x: 290,
|
||||
// y: 225,
|
||||
// data: {
|
||||
// label: '读数据',
|
||||
// status: 'success',
|
||||
// },
|
||||
// ports: [
|
||||
// {
|
||||
// id: '2-1',
|
||||
// group: 'top',
|
||||
// },
|
||||
// {
|
||||
// id: '2-2',
|
||||
// group: 'bottom',
|
||||
// },
|
||||
// {
|
||||
// id: '2-3',
|
||||
// group: 'bottom',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// id: '3',
|
||||
// shape: 'dag-node',
|
||||
// x: 170,
|
||||
// y: 350,
|
||||
// data: {
|
||||
// label: '读数据',
|
||||
// status: 'success',
|
||||
// },
|
||||
// ports: [
|
||||
// {
|
||||
// id: '3-1',
|
||||
// group: 'top',
|
||||
// },
|
||||
// {
|
||||
// id: '3-2',
|
||||
// group: 'bottom',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// id: '4',
|
||||
// shape: 'dag-node',
|
||||
// x: 450,
|
||||
// y: 350,
|
||||
// data: {
|
||||
// label: '读数据',
|
||||
// status: 'success',
|
||||
// },
|
||||
// ports: [
|
||||
// {
|
||||
// id: '4-1',
|
||||
// group: 'top',
|
||||
// },
|
||||
// {
|
||||
// id: '4-2',
|
||||
// group: 'bottom',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// id: '5',
|
||||
// shape: 'dag-edge',
|
||||
// source: {
|
||||
// cell: '1',
|
||||
// port: '1-1',
|
||||
// },
|
||||
// target: {
|
||||
// cell: '2',
|
||||
// port: '2-1',
|
||||
// },
|
||||
// zIndex: 0,
|
||||
// },
|
||||
// {
|
||||
// id: '6',
|
||||
// shape: 'dag-edge',
|
||||
// source: {
|
||||
// cell: '2',
|
||||
// port: '2-2',
|
||||
// },
|
||||
// target: {
|
||||
// cell: '3',
|
||||
// port: '3-1',
|
||||
// },
|
||||
// zIndex: 0,
|
||||
// },
|
||||
// {
|
||||
// id: '7',
|
||||
// shape: 'dag-edge',
|
||||
// source: {
|
||||
// cell: '2',
|
||||
// port: '2-3',
|
||||
// },
|
||||
// target: {
|
||||
// cell: '4',
|
||||
// port: '4-1',
|
||||
// },
|
||||
// zIndex: 0,
|
||||
// },
|
||||
// ]
|
||||
const data = [
|
||||
{
|
||||
id: '1',
|
||||
shape: 'dag-node',
|
||||
x: 290,
|
||||
y: 110,
|
||||
data: {
|
||||
label: '读数据',
|
||||
status: 'success',
|
||||
},
|
||||
ports: [
|
||||
{
|
||||
id: '1-1',
|
||||
group: 'bottom',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
shape: 'dag-node',
|
||||
x: 290,
|
||||
y: 225,
|
||||
data: {
|
||||
label: '读数据',
|
||||
status: 'success',
|
||||
},
|
||||
ports: [
|
||||
{
|
||||
id: '2-1',
|
||||
group: 'top',
|
||||
},
|
||||
{
|
||||
id: '2-2',
|
||||
group: 'bottom',
|
||||
},
|
||||
{
|
||||
id: '2-3',
|
||||
group: 'bottom',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
shape: 'dag-node',
|
||||
x: 170,
|
||||
y: 350,
|
||||
data: {
|
||||
label: '读数据',
|
||||
status: 'success',
|
||||
},
|
||||
ports: [
|
||||
{
|
||||
id: '3-1',
|
||||
group: 'top',
|
||||
},
|
||||
{
|
||||
id: '3-2',
|
||||
group: 'bottom',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
shape: 'dag-node',
|
||||
x: 450,
|
||||
y: 350,
|
||||
data: {
|
||||
label: '读数据',
|
||||
status: 'success',
|
||||
},
|
||||
ports: [
|
||||
{
|
||||
id: '4-1',
|
||||
group: 'top',
|
||||
},
|
||||
{
|
||||
id: '4-2',
|
||||
group: 'bottom',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
shape: 'dag-edge',
|
||||
source: {
|
||||
cell: '1',
|
||||
port: '1-1',
|
||||
},
|
||||
target: {
|
||||
cell: '2',
|
||||
port: '2-1',
|
||||
},
|
||||
zIndex: 0,
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
shape: 'dag-edge',
|
||||
source: {
|
||||
cell: '2',
|
||||
port: '2-2',
|
||||
},
|
||||
target: {
|
||||
cell: '3',
|
||||
port: '3-1',
|
||||
},
|
||||
zIndex: 0,
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
shape: 'dag-edge',
|
||||
source: {
|
||||
cell: '2',
|
||||
port: '2-3',
|
||||
},
|
||||
target: {
|
||||
cell: '4',
|
||||
port: '4-1',
|
||||
},
|
||||
zIndex: 0,
|
||||
},
|
||||
]
|
||||
|
||||
// const nodeStatusList = [
|
||||
// [
|
||||
// {
|
||||
// id: '1',
|
||||
// status: 'running',
|
||||
// },
|
||||
// {
|
||||
// id: '2',
|
||||
// status: 'default',
|
||||
// },
|
||||
// {
|
||||
// id: '3',
|
||||
// status: 'default',
|
||||
// },
|
||||
// {
|
||||
// id: '4',
|
||||
// status: 'default',
|
||||
// },
|
||||
// ],
|
||||
// [
|
||||
// {
|
||||
// id: '1',
|
||||
// status: 'success',
|
||||
// },
|
||||
// {
|
||||
// id: '2',
|
||||
// status: 'running',
|
||||
// },
|
||||
// {
|
||||
// id: '3',
|
||||
// status: 'default',
|
||||
// },
|
||||
// {
|
||||
// id: '4',
|
||||
// status: 'default',
|
||||
// },
|
||||
// ],
|
||||
// [
|
||||
// {
|
||||
// id: '1',
|
||||
// status: 'success',
|
||||
// },
|
||||
// {
|
||||
// id: '2',
|
||||
// status: 'success',
|
||||
// },
|
||||
// {
|
||||
// id: '3',
|
||||
// status: 'running',
|
||||
// },
|
||||
// {
|
||||
// id: '4',
|
||||
// status: 'running',
|
||||
// },
|
||||
// ],
|
||||
// [
|
||||
// {
|
||||
// id: '1',
|
||||
// status: 'success',
|
||||
// },
|
||||
// {
|
||||
// id: '2',
|
||||
// status: 'success',
|
||||
// },
|
||||
// {
|
||||
// id: '3',
|
||||
// status: 'success',
|
||||
// },
|
||||
// {
|
||||
// id: '4',
|
||||
// status: 'failed',
|
||||
// },
|
||||
// ],
|
||||
// ]
|
||||
// export default class Example extends React.Component {
|
||||
// private container: HTMLDivElement
|
||||
const nodeStatusList = [
|
||||
[
|
||||
{
|
||||
id: '1',
|
||||
status: 'running',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
status: 'default',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
status: 'default',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
status: 'default',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: '1',
|
||||
status: 'success',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
status: 'running',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
status: 'default',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
status: 'default',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: '1',
|
||||
status: 'success',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
status: 'success',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
status: 'running',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
status: 'running',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: '1',
|
||||
status: 'success',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
status: 'success',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
status: 'success',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
status: 'failed',
|
||||
},
|
||||
],
|
||||
]
|
||||
export default class Example extends React.Component {
|
||||
private container: HTMLDivElement
|
||||
|
||||
// componentDidMount() {
|
||||
// const graph: Graph = new Graph({
|
||||
// container: this.container,
|
||||
// width: 800,
|
||||
// height: 600,
|
||||
// panning: {
|
||||
// enabled: true,
|
||||
// eventTypes: ['leftMouseDown', 'mouseWheel'],
|
||||
// },
|
||||
// mousewheel: {
|
||||
// enabled: true,
|
||||
// modifiers: 'ctrl',
|
||||
// factor: 1.1,
|
||||
// maxScale: 1.5,
|
||||
// minScale: 0.5,
|
||||
// },
|
||||
// highlighting: {
|
||||
// magnetAdsorbed: {
|
||||
// name: 'stroke',
|
||||
// args: {
|
||||
// attrs: {
|
||||
// fill: '#fff',
|
||||
// stroke: '#31d0c6',
|
||||
// strokeWidth: 4,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// connecting: {
|
||||
// snap: true,
|
||||
// allowBlank: false,
|
||||
// allowLoop: false,
|
||||
// highlight: true,
|
||||
// connector: 'algo-connector',
|
||||
// connectionPoint: 'anchor',
|
||||
// anchor: 'center',
|
||||
// validateMagnet({ magnet }) {
|
||||
// return magnet.getAttribute('port-group') !== 'top'
|
||||
// },
|
||||
// createEdge() {
|
||||
// return graph.createEdge({
|
||||
// shape: 'dag-edge',
|
||||
// attrs: {
|
||||
// line: {
|
||||
// strokeDasharray: '5 5',
|
||||
// },
|
||||
// },
|
||||
// zIndex: -1,
|
||||
// })
|
||||
// },
|
||||
// },
|
||||
// selecting: {
|
||||
// enabled: true,
|
||||
// multiple: true,
|
||||
// rubberEdge: true,
|
||||
// rubberNode: true,
|
||||
// modifiers: 'shift',
|
||||
// rubberband: true,
|
||||
// },
|
||||
// })
|
||||
componentDidMount() {
|
||||
const graph: Graph = new Graph({
|
||||
container: this.container,
|
||||
width: 800,
|
||||
height: 600,
|
||||
panning: {
|
||||
enabled: true,
|
||||
eventTypes: ['leftMouseDown', 'mouseWheel'],
|
||||
},
|
||||
mousewheel: {
|
||||
enabled: true,
|
||||
modifiers: 'ctrl',
|
||||
factor: 1.1,
|
||||
maxScale: 1.5,
|
||||
minScale: 0.5,
|
||||
},
|
||||
highlighting: {
|
||||
magnetAdsorbed: {
|
||||
name: 'stroke',
|
||||
args: {
|
||||
attrs: {
|
||||
fill: '#fff',
|
||||
stroke: '#31d0c6',
|
||||
strokeWidth: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
connecting: {
|
||||
snap: true,
|
||||
allowBlank: false,
|
||||
allowLoop: false,
|
||||
highlight: true,
|
||||
connector: 'algo-connector',
|
||||
connectionPoint: 'anchor',
|
||||
anchor: 'center',
|
||||
validateMagnet({ magnet }) {
|
||||
return magnet.getAttribute('port-group') !== 'top'
|
||||
},
|
||||
createEdge() {
|
||||
return graph.createEdge({
|
||||
shape: 'dag-edge',
|
||||
attrs: {
|
||||
line: {
|
||||
strokeDasharray: '5 5',
|
||||
},
|
||||
},
|
||||
zIndex: -1,
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// graph.on('edge:connected', ({ edge }) => {
|
||||
// edge.attr({
|
||||
// line: {
|
||||
// strokeDasharray: '',
|
||||
// },
|
||||
// })
|
||||
// })
|
||||
const selection = new Selection({
|
||||
enabled: true,
|
||||
multiple: true,
|
||||
rubberEdge: true,
|
||||
rubberNode: true,
|
||||
modifiers: 'shift',
|
||||
rubberband: true,
|
||||
})
|
||||
graph.use(selection)
|
||||
graph.use(new Snapline({ enabled: true }))
|
||||
|
||||
// graph.on('node:change:data', ({ node }) => {
|
||||
// const edges = graph.getIncomingEdges(node)
|
||||
// const { status } = node.getData() as NodeStatus
|
||||
// edges?.forEach((edge) => {
|
||||
// if (status === 'running') {
|
||||
// edge.attr('line/strokeDasharray', 5)
|
||||
// edge.attr('line/style/animation', 'running-line 30s infinite linear')
|
||||
// } else {
|
||||
// edge.attr('line/strokeDasharray', '')
|
||||
// edge.attr('line/style/animation', '')
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
graph.on('edge:connected', ({ edge }) => {
|
||||
edge.attr({
|
||||
line: {
|
||||
strokeDasharray: '',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
// // 初始化节点/边
|
||||
// const init = (data: Cell.Metadata[]) => {
|
||||
// const cells: Cell[] = []
|
||||
// data.forEach((item) => {
|
||||
// if (item.shape === 'dag-node') {
|
||||
// cells.push(graph.createNode(item))
|
||||
// } else {
|
||||
// cells.push(graph.createEdge(item))
|
||||
// }
|
||||
// })
|
||||
// graph.resetCells(cells)
|
||||
// }
|
||||
graph.on('node:change:data', ({ node }) => {
|
||||
const edges = graph.getIncomingEdges(node)
|
||||
const { status } = node.getData() as NodeStatus
|
||||
edges?.forEach((edge) => {
|
||||
if (status === 'running') {
|
||||
edge.attr('line/strokeDasharray', 5)
|
||||
edge.attr('line/style/animation', 'running-line 30s infinite linear')
|
||||
} else {
|
||||
edge.attr('line/strokeDasharray', '')
|
||||
edge.attr('line/style/animation', '')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// // 显示节点状态
|
||||
// const showNodeStatus = async (statusList: NodeStatus[][]) => {
|
||||
// const status = statusList.shift()
|
||||
// status?.forEach((item) => {
|
||||
// const { id, status } = item
|
||||
// const node = graph.getCellById(id)
|
||||
// const data = node.getData() as NodeStatus
|
||||
// node.setData({
|
||||
// ...data,
|
||||
// status: status,
|
||||
// })
|
||||
// })
|
||||
// setTimeout(() => {
|
||||
// showNodeStatus(statusList)
|
||||
// }, 3000)
|
||||
// }
|
||||
// 初始化节点/边
|
||||
const init = (data: Cell.Metadata[]) => {
|
||||
const cells: Cell[] = []
|
||||
data.forEach((item) => {
|
||||
if (item.shape === 'dag-node') {
|
||||
cells.push(graph.createNode(item))
|
||||
} else {
|
||||
cells.push(graph.createEdge(item))
|
||||
}
|
||||
})
|
||||
graph.resetCells(cells)
|
||||
}
|
||||
|
||||
// init(data)
|
||||
// showNodeStatus(nodeStatusList)
|
||||
// }
|
||||
// 显示节点状态
|
||||
const showNodeStatus = async (statusList: NodeStatus[][]) => {
|
||||
const status = statusList.shift()
|
||||
status?.forEach((item) => {
|
||||
const { id, status } = item
|
||||
const node = graph.getCellById(id)
|
||||
const data = node.getData() as NodeStatus
|
||||
node.setData({
|
||||
...data,
|
||||
status: status,
|
||||
})
|
||||
})
|
||||
setTimeout(() => {
|
||||
showNodeStatus(statusList)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
// refContainer = (container: HTMLDivElement) => {
|
||||
// this.container = container
|
||||
// }
|
||||
init(data)
|
||||
showNodeStatus(nodeStatusList)
|
||||
}
|
||||
|
||||
// render() {
|
||||
// return (
|
||||
// <div className="x6-graph-wrap">
|
||||
// <div ref={this.refContainer} className="dag" />
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
refContainer = (container: HTMLDivElement) => {
|
||||
this.container = container
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="x6-graph-wrap">
|
||||
<div ref={this.refContainer} className="dag" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ import React from 'react'
|
||||
import { Graph, Cell, Node } from '@antv/x6'
|
||||
import { connectors } from '../connector/xmind-definitions'
|
||||
import Hierarchy from '@antv/hierarchy'
|
||||
import { Selection } from '@antv/x6-plugin-selection'
|
||||
import { Keyboard } from '@antv/x6-plugin-keyboard'
|
||||
import '../index.less'
|
||||
import './mind.less'
|
||||
|
||||
@ -181,13 +183,15 @@ export default class Example extends React.Component {
|
||||
connecting: {
|
||||
connectionPoint: 'anchor',
|
||||
},
|
||||
selecting: {
|
||||
enabled: true,
|
||||
},
|
||||
keyboard: {
|
||||
enabled: true,
|
||||
},
|
||||
})
|
||||
const selection = new Selection({
|
||||
enabled: true,
|
||||
})
|
||||
graph.use(selection)
|
||||
const keyboard = new Keyboard({
|
||||
enabled: true,
|
||||
})
|
||||
graph.use(keyboard)
|
||||
|
||||
const render = () => {
|
||||
const result: HierarchyResult = Hierarchy.mindmap(data, {
|
||||
@ -346,8 +350,8 @@ export default class Example extends React.Component {
|
||||
render()
|
||||
}
|
||||
})
|
||||
graph.bindKey(['backspace', 'delete'], () => {
|
||||
const selectedNodes = graph
|
||||
keyboard.bindKey(['backspace', 'delete'], () => {
|
||||
const selectedNodes = selection
|
||||
.getSelectedCells()
|
||||
.filter((item) => item.isNode())
|
||||
if (selectedNodes.length) {
|
||||
@ -358,9 +362,9 @@ export default class Example extends React.Component {
|
||||
}
|
||||
})
|
||||
|
||||
graph.bindKey('tab', (e) => {
|
||||
keyboard.bindKey('tab', (e) => {
|
||||
e.preventDefault()
|
||||
const selectedNodes = graph
|
||||
const selectedNodes = selection
|
||||
.getSelectedCells()
|
||||
.filter((item) => item.isNode())
|
||||
if (selectedNodes.length) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Graph, Path, Point } from '@antv/x6'
|
||||
import { Graph } from '@antv/x6'
|
||||
import { Path, Point } from '@antv/x6-geometry'
|
||||
import { Connector } from '@antv/x6/lib/registry'
|
||||
|
||||
export const connectors = {
|
||||
|
@ -8,6 +8,51 @@ const dataSource = [
|
||||
example: 'animation/transition',
|
||||
description: 'transition 动画',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
example: 'auto-resize',
|
||||
description: '画布大小自适应',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
example: 'case/bpmn',
|
||||
description: 'BPMN 图',
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
example: 'case/class',
|
||||
description: '类图',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
example: 'case/dag',
|
||||
description: 'DAG 图',
|
||||
},
|
||||
{
|
||||
key: '6',
|
||||
example: 'case/elk',
|
||||
description: 'ELK 图',
|
||||
},
|
||||
{
|
||||
key: '7',
|
||||
example: 'case/er',
|
||||
description: 'ER 图',
|
||||
},
|
||||
{
|
||||
key: '8',
|
||||
example: 'case/mind',
|
||||
description: '脑图',
|
||||
},
|
||||
{
|
||||
key: '9',
|
||||
example: 'case/swimlane',
|
||||
description: '泳道图',
|
||||
},
|
||||
{
|
||||
key: '10',
|
||||
example: 'snapline',
|
||||
description: '对齐线',
|
||||
},
|
||||
]
|
||||
|
||||
const columns = [
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import { Graph } from '@antv/x6'
|
||||
import { Snapline } from '@antv/x6-plugin-snapline'
|
||||
import '../index.less'
|
||||
|
||||
export default class Example extends React.Component {
|
||||
@ -11,9 +12,14 @@ export default class Example extends React.Component {
|
||||
width: 800,
|
||||
height: 600,
|
||||
grid: true,
|
||||
snapline: true,
|
||||
})
|
||||
|
||||
const snapline = new Snapline({
|
||||
enabled: true,
|
||||
sharp: true,
|
||||
})
|
||||
graph.use(snapline)
|
||||
|
||||
graph.addNode({
|
||||
shape: 'rect',
|
||||
x: 50,
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "2.0.6-beta.16",
|
||||
"version": "2.0.6-beta.22",
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"command": {
|
||||
|
@ -1 +0,0 @@
|
||||
export * from './src'
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@antv/x6-common",
|
||||
"version": "2.0.6-beta.16",
|
||||
"version": "2.0.6-beta.18",
|
||||
"description": "Basic toolkit for x6.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Color } from '../../src/color'
|
||||
import { Color } from '../../color'
|
||||
|
||||
describe('Color', () => {
|
||||
describe('#constructor', () => {
|
@ -3,7 +3,7 @@ import {
|
||||
IDisposable,
|
||||
DisposableSet,
|
||||
DisposableDelegate,
|
||||
} from '../../src/common/disposable'
|
||||
} from '../../common/disposable'
|
||||
|
||||
class TestDisposable implements IDisposable {
|
||||
count = 0
|
@ -1,5 +1,5 @@
|
||||
import sinon from 'sinon'
|
||||
import { DataUri } from '../../src/datauri'
|
||||
import { DataUri } from '../../datauri'
|
||||
|
||||
describe('DataUri', () => {
|
||||
describe('#isDataUrl', () => {
|
@ -1,5 +1,5 @@
|
||||
import sinon from 'sinon'
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
describe('af', () => {
|
||||
describe('#requestAnimationFrame', () => {
|
@ -1,5 +1,5 @@
|
||||
import { Vector } from '../../src/vector'
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Vector } from '../../vector'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
describe('Dom', () => {
|
||||
describe('attr', () => {
|
@ -1,5 +1,5 @@
|
||||
import { Vector } from '../../src/vector'
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Vector } from '../../vector'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
describe('Dom', () => {
|
||||
describe('class', () => {
|
@ -1,5 +1,5 @@
|
||||
import { Vector } from '../../src/vector'
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Vector } from '../../vector'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
describe('Dom', () => {
|
||||
describe('css', () => {
|
@ -1,6 +1,6 @@
|
||||
import { Vector } from '../../src/vector'
|
||||
import { Dom } from '../../src/dom'
|
||||
import { ObjectExt } from '../../src/object'
|
||||
import { Vector } from '../../vector'
|
||||
import { Dom } from '../../dom'
|
||||
import { ObjectExt } from '../../object'
|
||||
|
||||
describe('Dom', () => {
|
||||
describe('data', () => {
|
@ -1,5 +1,5 @@
|
||||
import { Vector } from '../../src/vector'
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Vector } from '../../vector'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
const wrap = document.createElement('div')
|
||||
const svgContent =
|
@ -1,6 +1,6 @@
|
||||
import sinon from 'sinon'
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Core } from '../../src/dom/event/core'
|
||||
import { Dom } from '../../dom'
|
||||
import { Core } from '../../dom/event/core'
|
||||
// import { Hook } from './hook'
|
||||
|
||||
describe('EventDom', () => {
|
||||
@ -30,7 +30,7 @@ describe('EventDom', () => {
|
||||
return this
|
||||
}
|
||||
|
||||
once(p1: any, p2?: any, p3?: any, p4?: any, p5?: any) {
|
||||
once(p1: any, p2?: any, p3?: any, p4?: any) {
|
||||
Dom.Event.once(this.node, p1, p2, p3, p4)
|
||||
return this
|
||||
}
|
||||
@ -610,7 +610,6 @@ describe('EventDom', () => {
|
||||
expect(spy2.callCount).toEqual(0)
|
||||
})
|
||||
|
||||
// Todo Error: Expected 2 to equal 1.
|
||||
// it('should apply hook to prevent window to unload', () => {
|
||||
// const win = new EventDom(window as any)
|
||||
// const spy1 = sinon.spy(() => {
|
@ -1,5 +1,5 @@
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Vector } from '../../src/vector'
|
||||
import { Dom } from '../../dom'
|
||||
import { Vector } from '../../vector'
|
||||
|
||||
describe('Dom', () => {
|
||||
describe('matrix', () => {
|
@ -1,5 +1,5 @@
|
||||
import { Vector } from '../../src/vector'
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Vector } from '../../vector'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
describe('Dom', () => {
|
||||
describe('path', () => {
|
@ -1,4 +1,4 @@
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
describe('Dom', () => {
|
||||
describe('#prefix', () => {
|
@ -1,4 +1,4 @@
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
describe('Dom', () => {
|
||||
describe('#clearSelection', () => {
|
@ -1,4 +1,4 @@
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
describe('Dom', () => {
|
||||
describe('#setPrefixedStyle', () => {
|
@ -1,5 +1,5 @@
|
||||
import { Vector } from '../../src/vector'
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Vector } from '../../vector'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
describe('Dom', () => {
|
||||
describe('#text', () => {
|
@ -1,5 +1,5 @@
|
||||
import { Vector } from '../../src/vector'
|
||||
import { Dom } from '../../src/dom'
|
||||
import { Vector } from '../../vector'
|
||||
import { Dom } from '../../dom'
|
||||
|
||||
describe('Dom', () => {
|
||||
const fixture = document.createElement('div')
|
@ -1,5 +1,5 @@
|
||||
import sinon from 'sinon'
|
||||
import { Events } from '../../src/event'
|
||||
import { Events } from '../../event'
|
||||
|
||||
describe('events', () => {
|
||||
it('should trigger with context', () => {
|
@ -1,4 +1,4 @@
|
||||
import { FunctionExt } from '../../src/function'
|
||||
import { FunctionExt } from '../../function'
|
||||
|
||||
describe('async', () => {
|
||||
describe('#toDeferredBoolean', () => {
|
@ -1,5 +1,5 @@
|
||||
import sinon from 'sinon'
|
||||
import { FunctionExt } from '../../src/function'
|
||||
import { FunctionExt } from '../../function'
|
||||
|
||||
describe('FunctionExt', () => {
|
||||
describe('#call', () => {
|
@ -1,4 +1,4 @@
|
||||
import { JSONArray, JSONExt, JSONObject, JSONPrimitive } from '../../src/json'
|
||||
import { JSONArray, JSONExt, JSONObject, JSONPrimitive } from '../../json'
|
||||
|
||||
describe('JSONExt', () => {
|
||||
describe('isPrimitive()', () => {
|
@ -1,4 +1,4 @@
|
||||
import { NumberExt } from '../../src/number'
|
||||
import { NumberExt } from '../../number'
|
||||
|
||||
describe('NumberExt', () => {
|
||||
describe('#mod', () => {
|
@ -1,4 +1,4 @@
|
||||
import { ObjectExt } from '../../src/object'
|
||||
import { ObjectExt } from '../../object'
|
||||
|
||||
describe('object', () => {
|
||||
class Parent {
|
@ -1,4 +1,4 @@
|
||||
import { ObjectExt } from '../../src/object'
|
||||
import { ObjectExt } from '../../object'
|
||||
|
||||
describe('Ojbect', () => {
|
||||
describe('applyMixins', () => {
|
@ -1,4 +1,4 @@
|
||||
import { ObjectExt } from '../../src/object'
|
||||
import { ObjectExt } from '../../object'
|
||||
|
||||
describe('Object', () => {
|
||||
const obj = {
|
@ -1,4 +1,4 @@
|
||||
import { StringExt } from '../../src/string'
|
||||
import { StringExt } from '../../string'
|
||||
|
||||
describe('String', () => {
|
||||
describe('#format', () => {
|
@ -1,4 +1,4 @@
|
||||
import { StringExt } from '../../src/string'
|
||||
import { StringExt } from '../../string'
|
||||
|
||||
describe('StringExt', () => {
|
||||
describe('#StringExt.hashcode', () => {
|
@ -1,4 +1,4 @@
|
||||
import { StringExt } from '../../src/string'
|
||||
import { StringExt } from '../../string'
|
||||
|
||||
describe('String', () => {
|
||||
describe('#getSpellingSuggestion', () => {
|
@ -1,4 +1,4 @@
|
||||
import { StringExt } from '../../src/string'
|
||||
import { StringExt } from '../../string'
|
||||
|
||||
describe('string', () => {
|
||||
describe('#uuid', () => {
|
@ -1,4 +1,4 @@
|
||||
import { Text } from '../../src/text'
|
||||
import { Text } from '../../text'
|
||||
|
||||
describe('Text', () => {
|
||||
describe('#annotate', () => {
|
@ -1,4 +1,4 @@
|
||||
import { Text } from '../../src/text'
|
||||
import { Text } from '../../text'
|
||||
|
||||
describe('Text', () => {
|
||||
describe('#sanitize', () => {
|
@ -1,4 +1,4 @@
|
||||
import { Unit } from '../../src'
|
||||
import { Unit } from '../..'
|
||||
|
||||
describe('Unit', () => {
|
||||
describe('#toPx', () => {
|
@ -1,4 +1,4 @@
|
||||
import { Vector } from '../../src/vector'
|
||||
import { Vector } from '../../vector'
|
||||
|
||||
describe('Vector', () => {
|
||||
describe('#create', () => {
|
@ -1 +0,0 @@
|
||||
export * from './src'
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "2.0.6-beta.16",
|
||||
"version": "2.0.6-beta.18",
|
||||
"name": "@antv/x6-geometry",
|
||||
"description": "Geometry operations for x6.",
|
||||
"main": "lib/index.js",
|
||||
|
@ -1,7 +1,4 @@
|
||||
import { Ellipse } from '../src'
|
||||
import { Line } from '../src'
|
||||
import { Point } from '../src'
|
||||
import { Rectangle } from '../src'
|
||||
import { Ellipse, Line, Point, Rectangle } from '..'
|
||||
|
||||
describe('ellipse', () => {
|
||||
describe('#constructor', () => {
|
@ -1,6 +1,4 @@
|
||||
import { Line } from '../src'
|
||||
import { Point } from '../src'
|
||||
import { Rectangle } from '../src'
|
||||
import { Line, Point, Rectangle } from '..'
|
||||
|
||||
describe('Line', () => {
|
||||
describe('#constructor', () => {
|
@ -1,5 +1,4 @@
|
||||
import { Path } from '../../src/path'
|
||||
import { normalizePathData } from '../../src/path'
|
||||
import { Path, normalizePathData } from '../../path'
|
||||
|
||||
describe('Path', () => {
|
||||
describe('#normalizePathData', () => {
|
@ -1,4 +1,4 @@
|
||||
import { Point } from '../src'
|
||||
import { Point } from '..'
|
||||
|
||||
describe('Point', () => {
|
||||
describe('#constructor', () => {
|
@ -1,6 +1,4 @@
|
||||
import { Line } from '../src'
|
||||
import { Point } from '../src'
|
||||
import { Polyline } from '../src'
|
||||
import { Line, Point, Polyline } from '..'
|
||||
|
||||
describe('Polyline', () => {
|
||||
describe('#constructor', () => {
|
@ -1,7 +1,4 @@
|
||||
import { Ellipse } from '../src'
|
||||
import { Line } from '../src'
|
||||
import { Point } from '../src'
|
||||
import { Rectangle } from '../src'
|
||||
import { Ellipse, Line, Point, Rectangle } from '..'
|
||||
|
||||
describe('rectangle', () => {
|
||||
describe('#constructor', () => {
|
@ -1 +0,0 @@
|
||||
export * from './src'
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@antv/x6-plugin-keyboard",
|
||||
"version": "2.0.6-beta.16",
|
||||
"version": "2.0.6-beta.20",
|
||||
"description": "keyboard plugin for X6.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -33,6 +33,7 @@
|
||||
"build": "run-p build:dev build:umd",
|
||||
"prebuild": "run-s lint clean",
|
||||
"coveralls": "cat ./test/coverage/lcov.info | coveralls",
|
||||
"test": "echo \"no test case\"",
|
||||
"pretest": "run-p clean:coverage",
|
||||
"prepare": "run-s test build",
|
||||
"precommit": "lint-staged"
|
||||
@ -104,7 +105,7 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@github.com/antvis/x6.git",
|
||||
"directory": "packages/x6-common"
|
||||
"directory": "packages/x6-plugin-keyboard"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
@ -1 +0,0 @@
|
||||
export * from './src'
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@antv/x6-plugin-scroller",
|
||||
"version": "2.0.6-beta.16",
|
||||
"version": "2.0.6-beta.21",
|
||||
"description": "scroller plugin for X6.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -34,6 +34,7 @@
|
||||
"build": "run-p build:dev build:umd",
|
||||
"prebuild": "run-s lint clean",
|
||||
"coveralls": "cat ./test/coverage/lcov.info | coveralls",
|
||||
"test": "echo \"no test case\"",
|
||||
"pretest": "run-p clean:coverage",
|
||||
"prepare": "run-s test build",
|
||||
"precommit": "lint-staged"
|
||||
@ -101,7 +102,7 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@github.com/antvis/x6.git",
|
||||
"directory": "packages/x6-common"
|
||||
"directory": "packages/x6-plugin-scroller"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
@ -15,7 +15,7 @@ export class Scroller extends Disposable {
|
||||
private scrollerImpl: ScrollerImpl
|
||||
public name = 'scroller'
|
||||
|
||||
private get pannable() {
|
||||
public get pannable() {
|
||||
if (this.options) {
|
||||
if (typeof this.options.pannable === 'object') {
|
||||
return this.options.pannable.enabled
|
||||
@ -26,13 +26,17 @@ export class Scroller extends Disposable {
|
||||
return false
|
||||
}
|
||||
|
||||
public get container() {
|
||||
return this.scrollerImpl.container
|
||||
}
|
||||
|
||||
constructor(public readonly options: Scroller.Options) {
|
||||
super()
|
||||
}
|
||||
|
||||
public init(graph: Graph) {
|
||||
this.graph = graph
|
||||
CssLoader.ensure('scroller', content)
|
||||
CssLoader.ensure(this.name, content)
|
||||
|
||||
this.scrollerImpl = new ScrollerImpl({
|
||||
...this.options,
|
||||
@ -280,7 +284,7 @@ export class Scroller extends Disposable {
|
||||
}
|
||||
|
||||
autoScroll(clientX: number, clientY: number) {
|
||||
this.scrollerImpl.autoScroll(clientX, clientY)
|
||||
return this.scrollerImpl.autoScroll(clientX, clientY)
|
||||
}
|
||||
|
||||
// #endregion
|
||||
@ -341,9 +345,8 @@ export class Scroller extends Disposable {
|
||||
protected preparePanning({ e }: { e: Dom.MouseDownEvent }) {
|
||||
const allowPanning = this.allowPanning(e, true)
|
||||
const selection = this.graph.getPlugin('selection') as any
|
||||
const allowRubberband =
|
||||
this.allowPanning(e) && selection && selection.allowRubberband(e, true)
|
||||
if (allowPanning || !allowRubberband) {
|
||||
const allowRubberband = selection && selection.allowRubberband(e, true)
|
||||
if (allowPanning || (this.allowPanning(e) && !allowRubberband)) {
|
||||
this.updateClassName(true)
|
||||
this.scrollerImpl.startPanning(e)
|
||||
this.scrollerImpl.once('pan:stop', () => this.updateClassName(false))
|
||||
@ -371,7 +374,7 @@ export class Scroller extends Disposable {
|
||||
dispose() {
|
||||
this.scrollerImpl.dispose()
|
||||
this.stopListening()
|
||||
CssLoader.clean('scroller')
|
||||
CssLoader.clean(this.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,7 @@ export class ScrollerImpl extends View {
|
||||
super()
|
||||
|
||||
this.options = ScrollerImpl.getOptions(options)
|
||||
this.onUpdate = FunctionExt.debounce(this.onUpdate, 200)
|
||||
|
||||
const scale = this.graph.transform.getScale()
|
||||
this.sx = scale.sx
|
||||
@ -79,31 +80,6 @@ export class ScrollerImpl extends View {
|
||||
Dom.before(graphContainer, this.container)
|
||||
}
|
||||
|
||||
// todo copy style
|
||||
// const style = graphContainer.getAttribute('style')
|
||||
// if (style) {
|
||||
// const obj: { [name: string]: string } = {}
|
||||
// const styles = style.split(';')
|
||||
// styles.forEach((item) => {
|
||||
// const section = item.trim()
|
||||
// if (section) {
|
||||
// const pair = section.split(':')
|
||||
// if (pair.length) {
|
||||
// obj[pair[0].trim()] = pair[1] ? pair[1].trim() : ''
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
// Object.keys(obj).forEach((key: any) => {
|
||||
// if (key === 'width' || key === 'height') {
|
||||
// return
|
||||
// }
|
||||
|
||||
// graphContainer.style[key] = ''
|
||||
// this.container.style[key] = obj[key]
|
||||
// })
|
||||
// }
|
||||
|
||||
this.content = document.createElement('div')
|
||||
Dom.addClass(this.content, this.prefixClassName(ScrollerImpl.contentClass))
|
||||
Dom.css(this.content, {
|
||||
@ -185,11 +161,9 @@ export class ScrollerImpl extends View {
|
||||
}
|
||||
|
||||
protected onUpdate() {
|
||||
if (!this.options.autoResize) {
|
||||
return
|
||||
if (this.options.autoResize) {
|
||||
this.update()
|
||||
}
|
||||
|
||||
this.update()
|
||||
}
|
||||
|
||||
protected delegateBackgroundEvents(events?: View.Events) {
|
||||
@ -381,76 +355,12 @@ export class ScrollerImpl extends View {
|
||||
gridWidth: this.options.pageWidth,
|
||||
gridHeight: this.options.pageHeight,
|
||||
allowNewOrigin: 'negative',
|
||||
contentArea: this.calcContextArea(resizeOptions),
|
||||
...resizeOptions,
|
||||
}
|
||||
|
||||
this.graph.fitToContent(this.getFitToContentOptions(options))
|
||||
}
|
||||
|
||||
protected calcContextArea(
|
||||
resizeOptions:
|
||||
| (TransformManager.FitToContentFullOptions & {
|
||||
direction:
|
||||
| ScrollerImpl.AutoResizeDirection
|
||||
| ScrollerImpl.AutoResizeDirection[]
|
||||
})
|
||||
| undefined,
|
||||
) {
|
||||
const direction = resizeOptions?.direction
|
||||
|
||||
if (!direction) {
|
||||
return this.graph.transform.getContentArea({ useCellGeometry: true })
|
||||
}
|
||||
|
||||
function getCellBBox(cell: Cell) {
|
||||
let rect = cell.getBBox()
|
||||
if (rect) {
|
||||
if (cell.isNode()) {
|
||||
const angle = cell.getAngle()
|
||||
if (angle != null && angle !== 0) {
|
||||
rect = rect.bbox(angle)
|
||||
}
|
||||
}
|
||||
}
|
||||
return rect
|
||||
}
|
||||
|
||||
const gridWidth = this.options.pageWidth || 1
|
||||
const gridHeight = this.options.pageHeight || 1
|
||||
let calculativeCells = this.graph.getCells()
|
||||
|
||||
if (!direction.includes('top')) {
|
||||
calculativeCells = calculativeCells.filter((cell) => {
|
||||
const bbox = getCellBBox(cell)
|
||||
return bbox.y >= 0
|
||||
})
|
||||
}
|
||||
|
||||
if (!direction.includes('left')) {
|
||||
calculativeCells = calculativeCells.filter((cell) => {
|
||||
const bbox = getCellBBox(cell)
|
||||
return bbox.x >= 0
|
||||
})
|
||||
}
|
||||
|
||||
if (!direction.includes('right')) {
|
||||
calculativeCells = calculativeCells.filter((cell) => {
|
||||
const bbox = getCellBBox(cell)
|
||||
return bbox.x + bbox.width <= gridWidth
|
||||
})
|
||||
}
|
||||
|
||||
if (!direction.includes('bottom')) {
|
||||
calculativeCells = calculativeCells.filter((cell) => {
|
||||
const bbox = getCellBBox(cell)
|
||||
return bbox.y + bbox.height <= gridHeight
|
||||
})
|
||||
}
|
||||
|
||||
return this.model.getCellsBBox(calculativeCells) || new Rectangle()
|
||||
}
|
||||
|
||||
protected getFitToContentOptions(
|
||||
options: TransformManager.FitToContentFullOptions,
|
||||
) {
|
||||
|
@ -1 +0,0 @@
|
||||
export * from './src'
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@antv/x6-plugin-selection",
|
||||
"version": "2.0.6-beta.16",
|
||||
"version": "2.0.6-beta.22",
|
||||
"description": "selection plugin for X6.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -34,6 +34,7 @@
|
||||
"build": "run-p build:dev build:umd",
|
||||
"prebuild": "run-s lint clean",
|
||||
"coveralls": "cat ./test/coverage/lcov.info | coveralls",
|
||||
"test": "echo \"no test case\"",
|
||||
"pretest": "run-p clean:coverage",
|
||||
"prepare": "run-s test build",
|
||||
"precommit": "lint-staged"
|
||||
@ -102,7 +103,7 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@github.com/antvis/x6.git",
|
||||
"directory": "packages/x6-common"
|
||||
"directory": "packages/x6-plugin-selection"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
@ -22,7 +22,7 @@ function compile(source, target) {
|
||||
|
||||
compile(path.join(src, 'index.less'), path.join(es, 'index.css'))
|
||||
compile(path.join(src, 'index.less'), path.join(lib, 'index.css'))
|
||||
compile(path.join(src, 'index.less'), path.join(dist, 'scroller.css'))
|
||||
compile(path.join(src, 'index.less'), path.join(dist, 'selection.css'))
|
||||
|
||||
function toCSSPath(source) {
|
||||
const dir = path.dirname(source)
|
||||
@ -61,7 +61,7 @@ function processLessInDir(dir) {
|
||||
}
|
||||
|
||||
function makeStyleModule() {
|
||||
const source = path.join(dist, 'scroller.css')
|
||||
const source = path.join(dist, 'selection.css')
|
||||
const target = path.join(src, 'style/raw.ts')
|
||||
const content = fs.readFileSync(source, { encoding: 'utf8' })
|
||||
const prev = fs.existsSync(target)
|
||||
|
@ -40,7 +40,7 @@ export class Selection extends Disposable {
|
||||
|
||||
public init(graph: Graph) {
|
||||
this.graph = graph
|
||||
CssLoader.ensure('scroller', content)
|
||||
CssLoader.ensure(this.name, content)
|
||||
this.selectionImpl = new SelectionImpl({
|
||||
...this.options,
|
||||
graph,
|
||||
@ -453,6 +453,7 @@ export class Selection extends Disposable {
|
||||
dispose() {
|
||||
this.stopListening()
|
||||
this.selectionImpl.dispose()
|
||||
CssLoader.clean(this.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,8 @@ export class SelectionImpl extends View<SelectionImpl.EventArgs> {
|
||||
options,
|
||||
}: Collection.EventArgs['node:change:position']) {
|
||||
const { showNodeSelectionBox, pointerEvents } = this.options
|
||||
const { ui, selection, translateBy } = options
|
||||
const { ui, selection, translateBy, snapped } = options
|
||||
|
||||
let allowTranslating = !this.translating
|
||||
|
||||
/* Scenarios where this method is not called:
|
||||
@ -135,6 +136,9 @@ export class SelectionImpl extends View<SelectionImpl.EventArgs> {
|
||||
allowTranslating =
|
||||
allowTranslating && translateBy && node.id === translateBy
|
||||
|
||||
// enabled when snapline snapped
|
||||
allowTranslating = allowTranslating || snapped
|
||||
|
||||
if (allowTranslating) {
|
||||
this.translating = true
|
||||
const current = node.position()
|
||||
|
21
packages/x6-plugin-snapline/LICENSE
Normal file
21
packages/x6-plugin-snapline/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-2022 Alipay.inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
5
packages/x6-plugin-snapline/README.md
Normal file
5
packages/x6-plugin-snapline/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# `x6-plugin-snapline`
|
||||
|
||||
> TODO: description
|
||||
|
||||
## Usage
|
112
packages/x6-plugin-snapline/package.json
Normal file
112
packages/x6-plugin-snapline/package.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"name": "@antv/x6-plugin-snapline",
|
||||
"version": "2.0.6-beta.20",
|
||||
"description": "snapline plugin for X6.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"unpkg": "dist/x6-plugin-snapline.js",
|
||||
"jsdelivr": "dist/x6-plugin-snapline.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"es",
|
||||
"lib"
|
||||
],
|
||||
"keywords": [
|
||||
"plugin",
|
||||
"snapline",
|
||||
"x6",
|
||||
"antv"
|
||||
],
|
||||
"scripts": {
|
||||
"clean:build": "rimraf dist es lib",
|
||||
"clean:coverage": "rimraf ./test/coverage",
|
||||
"clean": "run-p clean:build clean:coverage",
|
||||
"lint": "eslint 'src/**/*.{js,ts}?(x)' --fix",
|
||||
"build:less": "node ./scripts/style",
|
||||
"build:esm": "tsc --module esnext --target es2015 --outDir ./es",
|
||||
"build:cjs": "tsc --module commonjs --target es2015 --outDir ./lib",
|
||||
"build:umd": "rollup -c",
|
||||
"build:dev": "run-p build:less build:cjs build:esm",
|
||||
"build:watch": "yarn build:esm --w",
|
||||
"build:watch:esm": "yarn build:esm --w",
|
||||
"build:watch:cjs": "yarn build:cjs --w",
|
||||
"build": "run-p build:dev build:umd",
|
||||
"prebuild": "run-s lint clean",
|
||||
"coveralls": "cat ./test/coverage/lcov.info | coveralls",
|
||||
"test": "echo \"no test case\"",
|
||||
"pretest": "run-p clean:coverage",
|
||||
"prepare": "run-s test build",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.ts": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"inherits": [
|
||||
"@antv/x6-package-json/cli.json",
|
||||
"@antv/x6-package-json/eslint.json",
|
||||
"@antv/x6-package-json/rollup.json"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@antv/x6": ">=2.0.6-beta.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^20.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.4",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"@rollup/plugin-typescript": "^8.2.5",
|
||||
"@types/mousetrap": "^1.6.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.31.0",
|
||||
"@typescript-eslint/parser": "^4.31.0",
|
||||
"coveralls": "^3.1.1",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb-base": "^14.2.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-jest": "^24.4.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-react": "^7.25.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-unicorn": "^36.0.0",
|
||||
"less": "^4.1.1",
|
||||
"lint-staged": "^11.1.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.3.6",
|
||||
"prettier": "^2.4.0",
|
||||
"pretty-quick": "^3.1.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.56.3",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
"rollup-plugin-filesize": "^9.1.1",
|
||||
"rollup-plugin-postcss": "^4.0.1",
|
||||
"rollup-plugin-progress": "^1.1.2",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"ts-node": "^10.2.1",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"author": {
|
||||
"name": "bubkoo",
|
||||
"email": "bubkoo.wy@gmail.com"
|
||||
},
|
||||
"contributors": [],
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/antvis/x6",
|
||||
"bugs": {
|
||||
"url": "https://github.com/antvis/x6/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@github.com/antvis/x6.git",
|
||||
"directory": "packages/x6-plugin-snapline"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
}
|
||||
}
|
18
packages/x6-plugin-snapline/rollup.config.js
Normal file
18
packages/x6-plugin-snapline/rollup.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
import config from '../../configs/rollup-config'
|
||||
|
||||
export default config({
|
||||
output: [
|
||||
{
|
||||
name: 'X6PluginSnapline',
|
||||
format: 'umd',
|
||||
file: 'dist/x6-plugin-snapline.js',
|
||||
sourcemap: true,
|
||||
globals: {
|
||||
'@antv/x6': 'X6',
|
||||
'@antv/x6-common': 'X6Common',
|
||||
'@antv/x6-geometry': 'X6Geometry',
|
||||
},
|
||||
},
|
||||
],
|
||||
external: ['@antv/x6', '@antv/x6-common', '@antv/x6-geometry'],
|
||||
})
|
85
packages/x6-plugin-snapline/scripts/style.js
Normal file
85
packages/x6-plugin-snapline/scripts/style.js
Normal file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const fse = require('fs-extra')
|
||||
const cp = require('child_process')
|
||||
|
||||
const cwd = process.cwd()
|
||||
const es = path.join(cwd, 'es')
|
||||
const lib = path.join(cwd, 'lib')
|
||||
const src = path.join(cwd, 'src')
|
||||
const dist = path.join(cwd, 'dist')
|
||||
|
||||
function compile(source, target) {
|
||||
let cmd = './node_modules/.bin/lessc'
|
||||
if (os.type() === 'Windows_NT') {
|
||||
cmd = path.join(cwd, './node_modules/.bin/lessc.cmd')
|
||||
}
|
||||
cp.execFileSync(cmd, [source, target])
|
||||
}
|
||||
|
||||
compile(path.join(src, 'index.less'), path.join(es, 'index.css'))
|
||||
compile(path.join(src, 'index.less'), path.join(lib, 'index.css'))
|
||||
compile(path.join(src, 'index.less'), path.join(dist, 'snapline.css'))
|
||||
|
||||
function toCSSPath(source) {
|
||||
const dir = path.dirname(source)
|
||||
const file = `${path.basename(source, '.less')}.css`
|
||||
return path.join(dir, file)
|
||||
}
|
||||
|
||||
// Copy less files
|
||||
function processLessInDir(dir) {
|
||||
const stat = fs.statSync(dir)
|
||||
if (stat) {
|
||||
if (stat.isDirectory()) {
|
||||
fs.readdir(dir, (err, files) => {
|
||||
files.forEach((file) => {
|
||||
processLessInDir(path.join(dir, file))
|
||||
})
|
||||
})
|
||||
} else {
|
||||
const ext = path.extname(dir)
|
||||
if (ext === '.less' || ext === '.css') {
|
||||
fse.copySync(dir, path.join(es, path.relative(src, dir)))
|
||||
fse.copySync(dir, path.join(lib, path.relative(src, dir)))
|
||||
}
|
||||
|
||||
if (ext === '.less') {
|
||||
let source = path.join(es, path.relative(src, dir))
|
||||
let target = toCSSPath(source)
|
||||
compile(dir, target)
|
||||
|
||||
source = path.join(lib, path.relative(src, dir))
|
||||
target = toCSSPath(source)
|
||||
compile(dir, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makeStyleModule() {
|
||||
const source = path.join(dist, 'snapline.css')
|
||||
const target = path.join(src, 'style/raw.ts')
|
||||
const content = fs.readFileSync(source, { encoding: 'utf8' })
|
||||
const prev = fs.existsSync(target)
|
||||
? fs.readFileSync(target, { encoding: 'utf8' })
|
||||
: null
|
||||
const curr = `/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Auto generated file, do not modify it!
|
||||
*/
|
||||
|
||||
export const content = \`${content}\`
|
||||
`
|
||||
|
||||
if (prev !== curr) {
|
||||
fs.writeFileSync(target, curr)
|
||||
}
|
||||
}
|
||||
|
||||
processLessInDir(src)
|
||||
makeStyleModule()
|
16
packages/x6-plugin-snapline/src/index.less
Normal file
16
packages/x6-plugin-snapline/src/index.less
Normal file
@ -0,0 +1,16 @@
|
||||
@snapline-prefix-cls: ~'x6-widget-snapline';
|
||||
|
||||
.@{snapline-prefix-cls} {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
|
||||
&-vertical,
|
||||
&-horizontal {
|
||||
stroke: #2ecc71;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
}
|
149
packages/x6-plugin-snapline/src/index.ts
Normal file
149
packages/x6-plugin-snapline/src/index.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import { Disposable, CssLoader } from '@antv/x6-common'
|
||||
import { Graph } from '@antv/x6'
|
||||
import { SnaplineImpl } from './snapline'
|
||||
import { content } from './style/raw'
|
||||
|
||||
export class Snapline extends Disposable {
|
||||
private snaplineImpl: SnaplineImpl
|
||||
public name = 'snapline'
|
||||
|
||||
constructor(public readonly options: Snapline.Options) {
|
||||
super()
|
||||
}
|
||||
|
||||
public init(graph: Graph) {
|
||||
CssLoader.ensure(this.name, content)
|
||||
this.snaplineImpl = new SnaplineImpl({
|
||||
...this.options,
|
||||
graph,
|
||||
})
|
||||
}
|
||||
|
||||
// #region api
|
||||
|
||||
isSnaplineEnabled() {
|
||||
return !this.snaplineImpl.disabled
|
||||
}
|
||||
|
||||
enableSnapline() {
|
||||
this.snaplineImpl.enable()
|
||||
return this
|
||||
}
|
||||
|
||||
disableSnapline() {
|
||||
this.snaplineImpl.disable()
|
||||
return this
|
||||
}
|
||||
|
||||
toggleSnapline(enabled?: boolean) {
|
||||
if (enabled != null) {
|
||||
if (enabled !== this.isSnaplineEnabled()) {
|
||||
if (enabled) {
|
||||
this.enableSnapline()
|
||||
} else {
|
||||
this.disableSnapline()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.isSnaplineEnabled()) {
|
||||
this.disableSnapline()
|
||||
} else {
|
||||
this.enableSnapline()
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
hideSnapline() {
|
||||
this.snaplineImpl.hide()
|
||||
return this
|
||||
}
|
||||
|
||||
setSnaplineFilter(filter?: SnaplineImpl.Filter) {
|
||||
this.snaplineImpl.setFilter(filter)
|
||||
return this
|
||||
}
|
||||
|
||||
isSnaplineOnResizingEnabled() {
|
||||
return this.snaplineImpl.options.resizing === true
|
||||
}
|
||||
|
||||
enableSnaplineOnResizing() {
|
||||
this.snaplineImpl.options.resizing = true
|
||||
return this
|
||||
}
|
||||
|
||||
disableSnaplineOnResizing() {
|
||||
this.snaplineImpl.options.resizing = false
|
||||
return this
|
||||
}
|
||||
|
||||
toggleSnaplineOnResizing(enableOnResizing?: boolean) {
|
||||
if (enableOnResizing != null) {
|
||||
if (enableOnResizing !== this.isSnaplineOnResizingEnabled()) {
|
||||
if (enableOnResizing) {
|
||||
this.enableSnaplineOnResizing()
|
||||
} else {
|
||||
this.disableSnaplineOnResizing()
|
||||
}
|
||||
}
|
||||
} else if (this.isSnaplineOnResizingEnabled()) {
|
||||
this.disableSnaplineOnResizing()
|
||||
} else {
|
||||
this.enableSnaplineOnResizing()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
isSharpSnapline() {
|
||||
return this.snaplineImpl.options.sharp === true
|
||||
}
|
||||
|
||||
enableSharpSnapline() {
|
||||
this.snaplineImpl.options.sharp = true
|
||||
return this
|
||||
}
|
||||
|
||||
disableSharpSnapline() {
|
||||
this.snaplineImpl.options.sharp = false
|
||||
return this
|
||||
}
|
||||
|
||||
toggleSharpSnapline(sharp?: boolean) {
|
||||
if (sharp != null) {
|
||||
if (sharp !== this.isSharpSnapline()) {
|
||||
if (sharp) {
|
||||
this.enableSharpSnapline()
|
||||
} else {
|
||||
this.disableSharpSnapline()
|
||||
}
|
||||
}
|
||||
} else if (this.isSharpSnapline()) {
|
||||
this.disableSharpSnapline()
|
||||
} else {
|
||||
this.enableSharpSnapline()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
getSnaplineTolerance() {
|
||||
return this.snaplineImpl.options.tolerance
|
||||
}
|
||||
|
||||
setSnaplineTolerance(tolerance: number) {
|
||||
this.snaplineImpl.options.tolerance = tolerance
|
||||
return this
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
@Disposable.dispose()
|
||||
dispose() {
|
||||
this.snaplineImpl.dispose()
|
||||
CssLoader.clean(this.name)
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Snapline {
|
||||
export interface Options extends SnaplineImpl.Options {}
|
||||
}
|
695
packages/x6-plugin-snapline/src/snapline.ts
Normal file
695
packages/x6-plugin-snapline/src/snapline.ts
Normal file
@ -0,0 +1,695 @@
|
||||
import { IDisablable, ArrayExt, FunctionExt, Vector } from '@antv/x6-common'
|
||||
import { Angle, Point, Rectangle } from '@antv/x6-geometry'
|
||||
import {
|
||||
Graph,
|
||||
EventArgs,
|
||||
Model,
|
||||
Node,
|
||||
CellView,
|
||||
NodeView,
|
||||
View,
|
||||
} from '@antv/x6'
|
||||
|
||||
export class SnaplineImpl extends View implements IDisablable {
|
||||
public readonly options: SnaplineImpl.Options
|
||||
protected readonly graph: Graph
|
||||
protected filterShapes: { [type: string]: boolean }
|
||||
protected filterCells: { [id: string]: boolean }
|
||||
protected filterFunction: SnaplineImpl.FilterFunction | null
|
||||
protected offset: Point.PointLike
|
||||
protected timer: number | null
|
||||
|
||||
public container: SVGElement
|
||||
protected containerWrapper: Vector
|
||||
protected horizontal: Vector
|
||||
protected vertical: Vector
|
||||
|
||||
protected get model() {
|
||||
return this.graph.model
|
||||
}
|
||||
|
||||
protected get containerClassName() {
|
||||
return this.prefixClassName('widget-snapline')
|
||||
}
|
||||
|
||||
protected get verticalClassName() {
|
||||
return `${this.containerClassName}-vertical`
|
||||
}
|
||||
|
||||
protected get horizontalClassName() {
|
||||
return `${this.containerClassName}-horizontal`
|
||||
}
|
||||
|
||||
constructor(options: SnaplineImpl.Options & { graph: Graph }) {
|
||||
super()
|
||||
|
||||
const { graph, ...others } = options
|
||||
this.graph = graph
|
||||
this.options = { tolerance: 10, ...others }
|
||||
this.offset = { x: 0, y: 0 }
|
||||
this.render()
|
||||
this.parseFilter()
|
||||
if (!this.disabled) {
|
||||
this.startListening()
|
||||
}
|
||||
}
|
||||
|
||||
public get disabled() {
|
||||
return this.options.enabled !== true
|
||||
}
|
||||
|
||||
enable() {
|
||||
if (this.disabled) {
|
||||
this.options.enabled = true
|
||||
this.startListening()
|
||||
}
|
||||
}
|
||||
|
||||
disable() {
|
||||
if (!this.disabled) {
|
||||
this.options.enabled = false
|
||||
this.stopListening()
|
||||
}
|
||||
}
|
||||
|
||||
setFilter(filter?: SnaplineImpl.Filter) {
|
||||
this.options.filter = filter
|
||||
this.parseFilter()
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const container = (this.containerWrapper = new Vector('svg'))
|
||||
const horizontal = (this.horizontal = new Vector('line'))
|
||||
const vertical = (this.vertical = new Vector('line'))
|
||||
|
||||
container.addClass(this.containerClassName)
|
||||
horizontal.addClass(this.horizontalClassName)
|
||||
vertical.addClass(this.verticalClassName)
|
||||
|
||||
container.setAttribute('width', '100%')
|
||||
container.setAttribute('height', '100%')
|
||||
|
||||
horizontal.setAttribute('display', 'none')
|
||||
vertical.setAttribute('display', 'none')
|
||||
|
||||
container.append([horizontal, vertical])
|
||||
|
||||
if (this.options.className) {
|
||||
container.addClass(this.options.className)
|
||||
}
|
||||
|
||||
this.container = this.containerWrapper.node
|
||||
}
|
||||
|
||||
protected startListening() {
|
||||
this.stopListening()
|
||||
this.graph.on('node:mousedown', this.captureCursorOffset, this)
|
||||
this.graph.on('node:mousemove', this.snapOnMoving, this)
|
||||
this.model.on('batch:stop', this.onBatchStop, this)
|
||||
this.delegateDocumentEvents({
|
||||
mouseup: 'hide',
|
||||
touchend: 'hide',
|
||||
})
|
||||
}
|
||||
|
||||
protected stopListening() {
|
||||
this.graph.off('node:mousedown', this.captureCursorOffset, this)
|
||||
this.graph.off('node:mousemove', this.snapOnMoving, this)
|
||||
this.model.off('batch:stop', this.onBatchStop, this)
|
||||
this.undelegateDocumentEvents()
|
||||
}
|
||||
|
||||
protected parseFilter() {
|
||||
this.filterShapes = {}
|
||||
this.filterCells = {}
|
||||
this.filterFunction = null
|
||||
const filter = this.options.filter
|
||||
if (Array.isArray(filter)) {
|
||||
filter.forEach((item) => {
|
||||
if (typeof item === 'string') {
|
||||
this.filterShapes[item] = true
|
||||
} else {
|
||||
this.filterCells[item.id] = true
|
||||
}
|
||||
})
|
||||
} else if (typeof filter === 'function') {
|
||||
this.filterFunction = filter
|
||||
}
|
||||
}
|
||||
|
||||
protected onBatchStop({ name, data }: Model.EventArgs['batch:stop']) {
|
||||
if (name === 'resize') {
|
||||
this.snapOnResizing(data.cell, data as Node.ResizeOptions)
|
||||
}
|
||||
}
|
||||
|
||||
captureCursorOffset({ view, x, y }: EventArgs['node:mousedown']) {
|
||||
const targetView = view.getDelegatedView()
|
||||
if (targetView && this.isNodeMovable(targetView)) {
|
||||
const pos = view.cell.getPosition()
|
||||
this.offset = {
|
||||
x: x - pos.x,
|
||||
y: y - pos.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected isNodeMovable(view: CellView) {
|
||||
return view && view.cell.isNode() && view.can('nodeMovable')
|
||||
}
|
||||
|
||||
protected getRestrictArea(view?: NodeView): Rectangle.RectangleLike | null {
|
||||
const restrict = this.graph.options.translating.restrict
|
||||
const area =
|
||||
typeof restrict === 'function'
|
||||
? FunctionExt.call(restrict, this.graph, view!)
|
||||
: restrict
|
||||
|
||||
if (typeof area === 'number') {
|
||||
return this.graph.transform.getGraphArea().inflate(area)
|
||||
}
|
||||
|
||||
if (area === true) {
|
||||
return this.graph.transform.getGraphArea()
|
||||
}
|
||||
|
||||
return area || null
|
||||
}
|
||||
|
||||
protected snapOnResizing(node: Node, options: Node.ResizeOptions) {
|
||||
if (
|
||||
this.options.resizing &&
|
||||
!options.snapped &&
|
||||
options.ui &&
|
||||
options.direction &&
|
||||
options.trueDirection
|
||||
) {
|
||||
const view = this.graph.renderer.findViewByCell(node) as NodeView
|
||||
if (view && view.cell.isNode()) {
|
||||
const nodeBbox = node.getBBox()
|
||||
const nodeBBoxRotated = nodeBbox.bbox(node.getAngle())
|
||||
const nodeTopLeft = nodeBBoxRotated.getTopLeft()
|
||||
const nodeBottomRight = nodeBBoxRotated.getBottomRight()
|
||||
const angle = Angle.normalize(node.getAngle())
|
||||
const tolerance = this.options.tolerance || 0
|
||||
let verticalLeft: number | undefined
|
||||
let verticalTop: number | undefined
|
||||
let verticalHeight: number | undefined
|
||||
let horizontalTop: number | undefined
|
||||
let horizontalLeft: number | undefined
|
||||
let horizontalWidth: number | undefined
|
||||
|
||||
const snapOrigin = {
|
||||
vertical: 0,
|
||||
horizontal: 0,
|
||||
}
|
||||
|
||||
const direction = options.direction
|
||||
const trueDirection = options.trueDirection
|
||||
const relativeDirection = options.relativeDirection
|
||||
|
||||
if (trueDirection.indexOf('right') !== -1) {
|
||||
snapOrigin.vertical = nodeBottomRight.x
|
||||
} else {
|
||||
snapOrigin.vertical = nodeTopLeft.x
|
||||
}
|
||||
|
||||
if (trueDirection.indexOf('bottom') !== -1) {
|
||||
snapOrigin.horizontal = nodeBottomRight.y
|
||||
} else {
|
||||
snapOrigin.horizontal = nodeTopLeft.y
|
||||
}
|
||||
|
||||
this.model.getNodes().some((cell) => {
|
||||
if (this.isIgnored(node, cell)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const snapBBox = cell.getBBox().bbox(cell.getAngle())
|
||||
const snapTopLeft = snapBBox.getTopLeft()
|
||||
const snapBottomRight = snapBBox.getBottomRight()
|
||||
const groups = {
|
||||
vertical: [snapTopLeft.x, snapBottomRight.x],
|
||||
horizontal: [snapTopLeft.y, snapBottomRight.y],
|
||||
}
|
||||
|
||||
const distances = {} as {
|
||||
vertical: { position: number; distance: number }[]
|
||||
horizontal: { position: number; distance: number }[]
|
||||
}
|
||||
|
||||
Object.keys(groups).forEach((k) => {
|
||||
const key = k as 'vertical' | 'horizontal'
|
||||
const list = groups[key]
|
||||
.map((value) => ({
|
||||
position: value,
|
||||
distance: Math.abs(value - snapOrigin[key]),
|
||||
}))
|
||||
.filter((item) => item.distance <= tolerance)
|
||||
|
||||
distances[key] = ArrayExt.sortBy(list, (item) => item.distance)
|
||||
})
|
||||
|
||||
if (verticalLeft == null && distances.vertical.length > 0) {
|
||||
verticalLeft = distances.vertical[0].position
|
||||
verticalTop = Math.min(nodeBBoxRotated.y, snapBBox.y)
|
||||
verticalHeight =
|
||||
Math.max(nodeBottomRight.y, snapBottomRight.y) - verticalTop
|
||||
}
|
||||
|
||||
if (horizontalTop == null && distances.horizontal.length > 0) {
|
||||
horizontalTop = distances.horizontal[0].position
|
||||
horizontalLeft = Math.min(nodeBBoxRotated.x, snapBBox.x)
|
||||
horizontalWidth =
|
||||
Math.max(nodeBottomRight.x, snapBottomRight.x) - horizontalLeft
|
||||
}
|
||||
|
||||
return verticalLeft != null && horizontalTop != null
|
||||
})
|
||||
|
||||
this.hide()
|
||||
|
||||
let dx = 0
|
||||
let dy = 0
|
||||
if (horizontalTop != null || verticalLeft != null) {
|
||||
if (verticalLeft != null) {
|
||||
dx =
|
||||
trueDirection.indexOf('right') !== -1
|
||||
? verticalLeft - nodeBottomRight.x
|
||||
: nodeTopLeft.x - verticalLeft
|
||||
}
|
||||
|
||||
if (horizontalTop != null) {
|
||||
dy =
|
||||
trueDirection.indexOf('bottom') !== -1
|
||||
? horizontalTop - nodeBottomRight.y
|
||||
: nodeTopLeft.y - horizontalTop
|
||||
}
|
||||
}
|
||||
|
||||
let dWidth = 0
|
||||
let dHeight = 0
|
||||
if (angle % 90 === 0) {
|
||||
if (angle === 90 || angle === 270) {
|
||||
dWidth = dy
|
||||
dHeight = dx
|
||||
} else {
|
||||
dWidth = dx
|
||||
dHeight = dy
|
||||
}
|
||||
} else {
|
||||
const quadrant =
|
||||
angle >= 0 && angle < 90
|
||||
? 1
|
||||
: angle >= 90 && angle < 180
|
||||
? 4
|
||||
: angle >= 180 && angle < 270
|
||||
? 3
|
||||
: 2
|
||||
|
||||
if (horizontalTop != null && verticalLeft != null) {
|
||||
if (dx < dy) {
|
||||
dy = 0
|
||||
horizontalTop = undefined
|
||||
} else {
|
||||
dx = 0
|
||||
verticalLeft = undefined
|
||||
}
|
||||
}
|
||||
|
||||
const rad = Angle.toRad(angle % 90)
|
||||
if (dx) {
|
||||
dWidth = quadrant === 3 ? dx / Math.cos(rad) : dx / Math.sin(rad)
|
||||
}
|
||||
if (dy) {
|
||||
dHeight = quadrant === 3 ? dy / Math.cos(rad) : dy / Math.sin(rad)
|
||||
}
|
||||
|
||||
const quadrant13 = quadrant === 1 || quadrant === 3
|
||||
switch (relativeDirection) {
|
||||
case 'top':
|
||||
case 'bottom':
|
||||
dHeight = dy
|
||||
? dy / (quadrant13 ? Math.cos(rad) : Math.sin(rad))
|
||||
: dx / (quadrant13 ? Math.sin(rad) : Math.cos(rad))
|
||||
break
|
||||
case 'left':
|
||||
case 'right':
|
||||
dWidth = dx
|
||||
? dx / (quadrant13 ? Math.cos(rad) : Math.sin(rad))
|
||||
: dy / (quadrant13 ? Math.sin(rad) : Math.cos(rad))
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch (relativeDirection) {
|
||||
case 'top':
|
||||
case 'bottom':
|
||||
dWidth = 0
|
||||
break
|
||||
case 'left':
|
||||
case 'right':
|
||||
dHeight = 0
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
const gridSize = this.graph.getGridSize()
|
||||
let newWidth = Math.max(nodeBbox.width + dWidth, gridSize)
|
||||
let newHeight = Math.max(nodeBbox.height + dHeight, gridSize)
|
||||
|
||||
if (options.minWidth && options.minWidth > gridSize) {
|
||||
newWidth = Math.max(newWidth, options.minWidth)
|
||||
}
|
||||
|
||||
if (options.minHeight && options.minHeight > gridSize) {
|
||||
newHeight = Math.max(newHeight, options.minHeight)
|
||||
}
|
||||
|
||||
if (options.maxWidth) {
|
||||
newWidth = Math.min(newWidth, options.maxWidth)
|
||||
}
|
||||
|
||||
if (options.maxHeight) {
|
||||
newHeight = Math.min(newHeight, options.maxHeight)
|
||||
}
|
||||
|
||||
if (options.preserveAspectRatio) {
|
||||
if (dHeight < dWidth) {
|
||||
newHeight = newWidth * (nodeBbox.height / nodeBbox.width)
|
||||
} else {
|
||||
newWidth = newHeight * (nodeBbox.width / nodeBbox.height)
|
||||
}
|
||||
}
|
||||
|
||||
if (newWidth !== nodeBbox.width || newHeight !== nodeBbox.height) {
|
||||
node.resize(newWidth, newHeight, {
|
||||
direction,
|
||||
relativeDirection,
|
||||
trueDirection,
|
||||
snapped: true,
|
||||
snaplines: this.cid,
|
||||
restrict: this.getRestrictArea(view),
|
||||
})
|
||||
|
||||
if (verticalHeight) {
|
||||
verticalHeight += newHeight - nodeBbox.height
|
||||
}
|
||||
|
||||
if (horizontalWidth) {
|
||||
horizontalWidth += newWidth - nodeBbox.width
|
||||
}
|
||||
}
|
||||
|
||||
const newRotatedBBox = node.getBBox().bbox(angle)
|
||||
if (
|
||||
verticalLeft &&
|
||||
Math.abs(newRotatedBBox.x - verticalLeft) > 1 &&
|
||||
Math.abs(newRotatedBBox.width + newRotatedBBox.x - verticalLeft) > 1
|
||||
) {
|
||||
verticalLeft = undefined
|
||||
}
|
||||
|
||||
if (
|
||||
horizontalTop &&
|
||||
Math.abs(newRotatedBBox.y - horizontalTop) > 1 &&
|
||||
Math.abs(newRotatedBBox.height + newRotatedBBox.y - horizontalTop) > 1
|
||||
) {
|
||||
horizontalTop = undefined
|
||||
}
|
||||
|
||||
this.update({
|
||||
verticalLeft,
|
||||
verticalTop,
|
||||
verticalHeight,
|
||||
horizontalTop,
|
||||
horizontalLeft,
|
||||
horizontalWidth,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snapOnMoving({ view, e, x, y }: EventArgs['node:mousemove']) {
|
||||
const targetView: NodeView = view.getEventData(e).delegatedView || view
|
||||
if (!this.isNodeMovable(targetView)) {
|
||||
return
|
||||
}
|
||||
|
||||
const node = targetView.cell
|
||||
const size = node.getSize()
|
||||
const position = node.getPosition()
|
||||
const cellBBox = new Rectangle(
|
||||
x - this.offset.x,
|
||||
y - this.offset.y,
|
||||
size.width,
|
||||
size.height,
|
||||
)
|
||||
const angle = node.getAngle()
|
||||
const nodeCenter = cellBBox.getCenter()
|
||||
const nodeBBoxRotated = cellBBox.bbox(angle)
|
||||
const nodeTopLeft = nodeBBoxRotated.getTopLeft()
|
||||
const nodeBottomRight = nodeBBoxRotated.getBottomRight()
|
||||
|
||||
const distance = this.options.tolerance || 0
|
||||
let verticalLeft: number | undefined
|
||||
let verticalTop: number | undefined
|
||||
let verticalHeight: number | undefined
|
||||
let horizontalTop: number | undefined
|
||||
let horizontalLeft: number | undefined
|
||||
let horizontalWidth: number | undefined
|
||||
let verticalFix = 0
|
||||
let horizontalFix = 0
|
||||
|
||||
this.model.getNodes().some((targetNode) => {
|
||||
if (this.isIgnored(node, targetNode)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const snapBBox = targetNode.getBBox().bbox(targetNode.getAngle())
|
||||
const snapCenter = snapBBox.getCenter()
|
||||
const snapTopLeft = snapBBox.getTopLeft()
|
||||
const snapBottomRight = snapBBox.getBottomRight()
|
||||
|
||||
if (verticalLeft == null) {
|
||||
if (Math.abs(snapCenter.x - nodeCenter.x) < distance) {
|
||||
verticalLeft = snapCenter.x
|
||||
verticalFix = 0.5
|
||||
} else if (Math.abs(snapTopLeft.x - nodeTopLeft.x) < distance) {
|
||||
verticalLeft = snapTopLeft.x
|
||||
verticalFix = 0
|
||||
} else if (Math.abs(snapTopLeft.x - nodeBottomRight.x) < distance) {
|
||||
verticalLeft = snapTopLeft.x
|
||||
verticalFix = 1
|
||||
} else if (Math.abs(snapBottomRight.x - nodeBottomRight.x) < distance) {
|
||||
verticalLeft = snapBottomRight.x
|
||||
verticalFix = 1
|
||||
} else if (Math.abs(snapBottomRight.x - nodeTopLeft.x) < distance) {
|
||||
verticalLeft = snapBottomRight.x
|
||||
}
|
||||
|
||||
if (verticalLeft != null) {
|
||||
verticalTop = Math.min(nodeBBoxRotated.y, snapBBox.y)
|
||||
verticalHeight =
|
||||
Math.max(nodeBottomRight.y, snapBottomRight.y) - verticalTop
|
||||
}
|
||||
}
|
||||
|
||||
if (horizontalTop == null) {
|
||||
if (Math.abs(snapCenter.y - nodeCenter.y) < distance) {
|
||||
horizontalTop = snapCenter.y
|
||||
horizontalFix = 0.5
|
||||
} else if (Math.abs(snapTopLeft.y - nodeTopLeft.y) < distance) {
|
||||
horizontalTop = snapTopLeft.y
|
||||
} else if (Math.abs(snapTopLeft.y - nodeBottomRight.y) < distance) {
|
||||
horizontalTop = snapTopLeft.y
|
||||
horizontalFix = 1
|
||||
} else if (Math.abs(snapBottomRight.y - nodeBottomRight.y) < distance) {
|
||||
horizontalTop = snapBottomRight.y
|
||||
horizontalFix = 1
|
||||
} else if (Math.abs(snapBottomRight.y - nodeTopLeft.y) < distance) {
|
||||
horizontalTop = snapBottomRight.y
|
||||
}
|
||||
|
||||
if (horizontalTop != null) {
|
||||
horizontalLeft = Math.min(nodeBBoxRotated.x, snapBBox.x)
|
||||
horizontalWidth =
|
||||
Math.max(nodeBottomRight.x, snapBottomRight.x) - horizontalLeft
|
||||
}
|
||||
}
|
||||
|
||||
return verticalLeft != null && horizontalTop != null
|
||||
})
|
||||
|
||||
this.hide()
|
||||
|
||||
if (horizontalTop != null || verticalLeft != null) {
|
||||
if (horizontalTop != null) {
|
||||
nodeBBoxRotated.y =
|
||||
horizontalTop - horizontalFix * nodeBBoxRotated.height
|
||||
}
|
||||
|
||||
if (verticalLeft != null) {
|
||||
nodeBBoxRotated.x = verticalLeft - verticalFix * nodeBBoxRotated.width
|
||||
}
|
||||
|
||||
const newCenter = nodeBBoxRotated.getCenter()
|
||||
const newX = newCenter.x - cellBBox.width / 2
|
||||
const newY = newCenter.y - cellBBox.height / 2
|
||||
const dx = newX - position.x
|
||||
const dy = newY - position.y
|
||||
|
||||
if (dx !== 0 || dy !== 0) {
|
||||
node.translate(dx, dy, {
|
||||
snapped: true,
|
||||
restrict: this.getRestrictArea(targetView),
|
||||
})
|
||||
|
||||
if (horizontalWidth) {
|
||||
horizontalWidth += dx
|
||||
}
|
||||
|
||||
if (verticalHeight) {
|
||||
verticalHeight += dy
|
||||
}
|
||||
}
|
||||
|
||||
this.update({
|
||||
verticalLeft,
|
||||
verticalTop,
|
||||
verticalHeight,
|
||||
horizontalTop,
|
||||
horizontalLeft,
|
||||
horizontalWidth,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
protected isIgnored(snapNode: Node, targetNode: Node) {
|
||||
return (
|
||||
targetNode.id === snapNode.id ||
|
||||
targetNode.isDescendantOf(snapNode) ||
|
||||
this.filterShapes[targetNode.shape] ||
|
||||
this.filterCells[targetNode.id] ||
|
||||
(this.filterFunction &&
|
||||
FunctionExt.call(this.filterFunction, this.graph, targetNode))
|
||||
)
|
||||
}
|
||||
|
||||
protected update(metadata: {
|
||||
verticalLeft?: number
|
||||
verticalTop?: number
|
||||
verticalHeight?: number
|
||||
horizontalTop?: number
|
||||
horizontalLeft?: number
|
||||
horizontalWidth?: number
|
||||
}) {
|
||||
// https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations
|
||||
if (metadata.horizontalTop) {
|
||||
const start = this.graph.localToGraph(
|
||||
new Point(metadata.horizontalLeft, metadata.horizontalTop),
|
||||
)
|
||||
const end = this.graph.localToGraph(
|
||||
new Point(
|
||||
metadata.horizontalLeft! + metadata.horizontalWidth!,
|
||||
metadata.horizontalTop,
|
||||
),
|
||||
)
|
||||
this.horizontal.setAttributes({
|
||||
x1: this.options.sharp ? `${start.x}` : '0',
|
||||
y1: `${start.y}`,
|
||||
x2: this.options.sharp ? `${end.x}` : '100%',
|
||||
y2: `${end.y}`,
|
||||
display: 'inherit',
|
||||
})
|
||||
} else {
|
||||
this.horizontal.setAttribute('display', 'none')
|
||||
}
|
||||
|
||||
if (metadata.verticalLeft) {
|
||||
const start = this.graph.localToGraph(
|
||||
new Point(metadata.verticalLeft, metadata.verticalTop),
|
||||
)
|
||||
const end = this.graph.localToGraph(
|
||||
new Point(
|
||||
metadata.verticalLeft,
|
||||
metadata.verticalTop! + metadata.verticalHeight!,
|
||||
),
|
||||
)
|
||||
this.vertical.setAttributes({
|
||||
x1: `${start.x}`,
|
||||
y1: this.options.sharp ? `${start.y}` : '0',
|
||||
x2: `${end.x}`,
|
||||
y2: this.options.sharp ? `${end.y}` : '100%',
|
||||
display: 'inherit',
|
||||
})
|
||||
} else {
|
||||
this.vertical.setAttribute('display', 'none')
|
||||
}
|
||||
|
||||
this.show()
|
||||
}
|
||||
|
||||
protected resetTimer() {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
}
|
||||
|
||||
show() {
|
||||
this.resetTimer()
|
||||
if (this.container.parentNode == null) {
|
||||
this.graph.container.appendChild(this.container)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.resetTimer()
|
||||
this.vertical.setAttribute('display', 'none')
|
||||
this.horizontal.setAttribute('display', 'none')
|
||||
const clean = this.options.clean
|
||||
const delay = typeof clean === 'number' ? clean : clean !== false ? 3000 : 0
|
||||
if (delay > 0) {
|
||||
this.timer = window.setTimeout(() => {
|
||||
if (this.container.parentNode !== null) {
|
||||
this.unmount()
|
||||
}
|
||||
}, delay)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
protected onRemove() {
|
||||
this.stopListening()
|
||||
this.hide()
|
||||
}
|
||||
|
||||
@View.dispose()
|
||||
dispose() {
|
||||
this.remove()
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SnaplineImpl {
|
||||
export interface Options {
|
||||
enabled?: boolean
|
||||
className?: string
|
||||
tolerance?: number
|
||||
sharp?: boolean
|
||||
/**
|
||||
* Specify if snap on node resizing or not.
|
||||
*/
|
||||
resizing?: boolean
|
||||
clean?: boolean | number
|
||||
filter?: Filter
|
||||
}
|
||||
|
||||
export type Filter = null | (string | { id: string })[] | FilterFunction
|
||||
|
||||
export type FilterFunction = (this: Graph, node: Node) => boolean
|
||||
}
|
20
packages/x6-plugin-snapline/src/style/raw.ts
Normal file
20
packages/x6-plugin-snapline/src/style/raw.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Auto generated file, do not modify it!
|
||||
*/
|
||||
|
||||
export const content = `.x6-widget-snapline {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.x6-widget-snapline-vertical,
|
||||
.x6-widget-snapline-horizontal {
|
||||
stroke: #2ecc71;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
`
|
3
packages/x6-plugin-snapline/tsconfig.json
Normal file
3
packages/x6-plugin-snapline/tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@antv/x6-vue-shape",
|
||||
"version": "2.0.6-beta.16",
|
||||
"version": "2.0.6-beta.20",
|
||||
"description": "X6 shape for rendering vue components.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -110,7 +110,7 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@github.com/antvis/x6.git",
|
||||
"directory": "packages/x6-react-shape"
|
||||
"directory": "packages/x6-vue-shape"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
@ -1 +0,0 @@
|
||||
export * from './src'
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@antv/x6",
|
||||
"version": "2.0.6-beta.16",
|
||||
"version": "2.0.6-beta.21",
|
||||
"description": "JavaScript diagramming library that uses SVG and HTML for rendering.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -65,8 +65,8 @@
|
||||
"@antv/x6-package-json/rollup.json"
|
||||
],
|
||||
"dependencies": {
|
||||
"@antv/x6-common": "^2.0.6-beta.16",
|
||||
"@antv/x6-geometry": "^2.0.6-beta.16"
|
||||
"@antv/x6-common": "^2.0.6-beta.18",
|
||||
"@antv/x6-geometry": "^2.0.6-beta.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^20.0.0",
|
||||
|
@ -3,7 +3,7 @@ import config from '../../configs/rollup-config'
|
||||
export default config({
|
||||
output: [
|
||||
{
|
||||
name: 'X6Next',
|
||||
name: 'X6',
|
||||
format: 'umd',
|
||||
file: 'dist/x6.js',
|
||||
sourcemap: true,
|
||||
|
@ -1,6 +1,6 @@
|
||||
// import { Rectangle, Ellipse, Polyline, Path } from '../../geometry'
|
||||
import { Util } from '../../src/util'
|
||||
import { Vector } from '@antv/x6-common'
|
||||
import { Util } from '../../util'
|
||||
|
||||
describe('Util', () => {
|
||||
const fixture = document.createElement('div')
|
@ -113,14 +113,27 @@ export class MouseWheel extends Base {
|
||||
targetScale = NumberExt.clamp(targetScale, minScale, maxScale)
|
||||
|
||||
if (targetScale !== currentScale) {
|
||||
if (this.widgetOptions.zoomAtMousePosition) {
|
||||
const origin = this.graph.coord.clientToGraphPoint(this.startPos)
|
||||
this.graph.zoom(targetScale, {
|
||||
absolute: true,
|
||||
center: origin.clone(),
|
||||
})
|
||||
const scroller = this.graph.getPlugin('scroller') as any
|
||||
if (scroller) {
|
||||
if (this.widgetOptions.zoomAtMousePosition) {
|
||||
const origin = this.graph.coord.clientToLocalPoint(this.startPos)
|
||||
scroller.zoom(targetScale, {
|
||||
absolute: true,
|
||||
center: origin.clone(),
|
||||
})
|
||||
} else {
|
||||
scroller.zoom(targetScale, { absolute: true })
|
||||
}
|
||||
} else {
|
||||
this.graph.zoom(targetScale, { absolute: true })
|
||||
if (this.widgetOptions.zoomAtMousePosition) {
|
||||
const origin = this.graph.coord.clientToGraphPoint(this.startPos)
|
||||
this.graph.zoom(targetScale, {
|
||||
absolute: true,
|
||||
center: origin.clone(),
|
||||
})
|
||||
} else {
|
||||
this.graph.zoom(targetScale, { absolute: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
this.currentScale = null
|
||||
|
@ -51,6 +51,7 @@ export namespace Options {
|
||||
guard: (e: Dom.EventObject, view?: CellView | null) => boolean
|
||||
|
||||
onPortRendered?: (args: OnPortRenderedArgs) => void
|
||||
onEdgeLabelRendered?: (args: OnEdgeLabelRenderedArgs) => void
|
||||
}
|
||||
|
||||
export interface ManualBooleans {
|
||||
@ -375,6 +376,13 @@ export namespace Options {
|
||||
contentContainer: Element
|
||||
contentSelectors?: Markup.Selectors
|
||||
}
|
||||
|
||||
export interface OnEdgeLabelRenderedArgs {
|
||||
edge: Edge
|
||||
label: Edge.Label
|
||||
container: Element
|
||||
selectors: Markup.Selectors
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Options {
|
||||
|
@ -65,8 +65,12 @@ export class PanningManager extends Base {
|
||||
}
|
||||
|
||||
protected preparePanning({ e }: { e: Dom.MouseDownEvent }) {
|
||||
// todo 暂时删除 selection 的判断
|
||||
if (this.allowPanning(e, true)) {
|
||||
const selection = this.graph.getPlugin('selection') as any
|
||||
const allowRubberband = selection && selection.allowRubberband(e, true)
|
||||
if (
|
||||
this.allowPanning(e, true) ||
|
||||
(this.allowPanning(e) && !allowRubberband)
|
||||
) {
|
||||
this.startPanning(e)
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,27 @@ import { SizeSensor } from '@antv/x6-common'
|
||||
import { Base } from './base'
|
||||
|
||||
export class SizeManager extends Base {
|
||||
private getScroller() {
|
||||
const scroller = this.graph.getPlugin('scroller') as any
|
||||
if (scroller && scroller.options.enabled) {
|
||||
return scroller
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private getContainer() {
|
||||
const scroller = this.getScroller()
|
||||
if (scroller) {
|
||||
return scroller.container.parentElement
|
||||
}
|
||||
return this.graph.container.parentElement
|
||||
}
|
||||
|
||||
private getSensorTarget() {
|
||||
const autoResize = this.options.autoResize
|
||||
if (autoResize) {
|
||||
if (typeof autoResize === 'boolean') {
|
||||
return this.graph.container.parentElement
|
||||
return this.getContainer()
|
||||
}
|
||||
return autoResize as HTMLElement
|
||||
}
|
||||
@ -27,7 +43,12 @@ export class SizeManager extends Base {
|
||||
}
|
||||
|
||||
resize(width?: number, height?: number) {
|
||||
this.graph.transform.resize(width, height)
|
||||
const scroller = this.getScroller()
|
||||
if (scroller) {
|
||||
scroller.resize(width, height)
|
||||
} else {
|
||||
this.graph.transform.resize(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
@Base.dispose()
|
||||
|
@ -397,7 +397,8 @@ export class TransformManager extends Base {
|
||||
}
|
||||
|
||||
getContentArea(options: TransformManager.GetContentAreaOptions = {}) {
|
||||
if (options.useCellGeometry) {
|
||||
// use geometry calc default
|
||||
if (options.useCellGeometry !== false) {
|
||||
return this.model.getAllCellsBBox() || new Rectangle()
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,7 @@ export namespace Boundary {
|
||||
name: 'boundary',
|
||||
tagName: 'rect',
|
||||
padding: 10,
|
||||
useCellGeometry: true,
|
||||
attrs: {
|
||||
fill: 'none',
|
||||
stroke: '#333',
|
||||
|
@ -154,6 +154,7 @@ export namespace Button {
|
||||
export namespace Button {
|
||||
Button.config<Button.Options>({
|
||||
name: 'button',
|
||||
useCellGeometry: true,
|
||||
events: {
|
||||
mousedown: 'onMouseDown',
|
||||
touchstart: 'onMouseDown',
|
||||
@ -188,6 +189,7 @@ export namespace Button {
|
||||
],
|
||||
distance: 60,
|
||||
offset: 0,
|
||||
useCellGeometry: true,
|
||||
onClick({ view, btn }) {
|
||||
btn.parent.remove()
|
||||
view.cell.remove({ ui: true, toolId: btn.cid })
|
||||
|
@ -5,8 +5,9 @@ export interface Job {
|
||||
}
|
||||
|
||||
export enum JOB_PRIORITY {
|
||||
Manual = 1,
|
||||
Render = 2,
|
||||
RenderEdge = 1,
|
||||
RenderNode = 2,
|
||||
Update = 3,
|
||||
}
|
||||
|
||||
let isFlushing = false
|
||||
@ -106,7 +107,7 @@ function flushJobs() {
|
||||
|
||||
function findInsertionIndex(job: Job) {
|
||||
let start = 0
|
||||
while (queue[start] && queue[start].priority <= job.priority) {
|
||||
while (queue[start] && queue[start].priority >= job.priority) {
|
||||
start += 1
|
||||
}
|
||||
return start
|
||||
|
@ -14,11 +14,7 @@ export class Renderer extends Base {
|
||||
}
|
||||
|
||||
isViewMounted(view: CellView) {
|
||||
if (view == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true // todo
|
||||
return this.schedule.isViewMounted(view)
|
||||
}
|
||||
|
||||
setRenderArea(area?: Rectangle) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { KeyValue, Dom } from '@antv/x6-common'
|
||||
import { KeyValue, Dom, Disposable } from '@antv/x6-common'
|
||||
import { Rectangle } from '@antv/x6-geometry'
|
||||
import { Model, Cell } from '../model'
|
||||
import { View, CellView, NodeView, EdgeView } from '../view'
|
||||
@ -6,7 +6,7 @@ import { queueJob, queueFlush, clearJobs, JOB_PRIORITY } from './queueJob'
|
||||
import { FlagManager } from '../view/flag'
|
||||
import { Graph } from '../graph'
|
||||
|
||||
export class Scheduler {
|
||||
export class Scheduler extends Disposable {
|
||||
public views: KeyValue<Scheduler.View> = {}
|
||||
protected zPivots: KeyValue<Comment>
|
||||
private graph: Graph
|
||||
@ -21,6 +21,7 @@ export class Scheduler {
|
||||
}
|
||||
|
||||
constructor(graph: Graph) {
|
||||
super()
|
||||
this.graph = graph
|
||||
this.init()
|
||||
}
|
||||
@ -74,7 +75,7 @@ export class Scheduler {
|
||||
viewItem.view,
|
||||
Scheduler.FLAG_INSERT,
|
||||
options,
|
||||
JOB_PRIORITY.Render,
|
||||
JOB_PRIORITY.Update,
|
||||
true,
|
||||
)
|
||||
}
|
||||
@ -91,7 +92,7 @@ export class Scheduler {
|
||||
view: CellView,
|
||||
flag: number,
|
||||
options: any = {},
|
||||
priority: JOB_PRIORITY = JOB_PRIORITY.Manual,
|
||||
priority: JOB_PRIORITY = JOB_PRIORITY.Update,
|
||||
flush = true,
|
||||
) {
|
||||
const id = view.cell.id
|
||||
@ -114,17 +115,9 @@ export class Scheduler {
|
||||
|
||||
const effectedEdges = this.getEffectedEdges(view)
|
||||
effectedEdges.forEach((edge) => {
|
||||
queueJob({
|
||||
id: edge.id,
|
||||
priority,
|
||||
cb: () => {
|
||||
this.renderViewInArea(edge.view, edge.flag, options)
|
||||
},
|
||||
})
|
||||
this.requestViewUpdate(edge.view, edge.flag, options, priority, false)
|
||||
})
|
||||
|
||||
viewItem.state = Scheduler.ViewState.REQUESTED
|
||||
|
||||
if (flush) {
|
||||
queueFlush()
|
||||
}
|
||||
@ -135,6 +128,20 @@ export class Scheduler {
|
||||
this.flushWaittingViews()
|
||||
}
|
||||
|
||||
isViewMounted(view: CellView) {
|
||||
if (view == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
const viewItem = this.views[view.cell.id]
|
||||
|
||||
if (!viewItem) {
|
||||
return false
|
||||
}
|
||||
|
||||
return viewItem.state === Scheduler.ViewState.MOUNTED
|
||||
}
|
||||
|
||||
protected renderViews(cells: Cell[], options: any = {}) {
|
||||
cells.sort((c1, c2) => {
|
||||
if (c1.isNode() && c2.isEdge()) {
|
||||
@ -170,7 +177,7 @@ export class Scheduler {
|
||||
viewItem.view,
|
||||
flag,
|
||||
options,
|
||||
JOB_PRIORITY.Render,
|
||||
cell.isNode() ? JOB_PRIORITY.RenderNode : JOB_PRIORITY.RenderEdge,
|
||||
false,
|
||||
)
|
||||
})
|
||||
@ -211,7 +218,10 @@ export class Scheduler {
|
||||
const viewItem = this.views[ids[i]]
|
||||
if (viewItem && viewItem.state === Scheduler.ViewState.WAITTING) {
|
||||
const { view, flag, options } = viewItem
|
||||
this.requestViewUpdate(view, flag, options, JOB_PRIORITY.Render, false)
|
||||
const priority = view.cell.isNode()
|
||||
? JOB_PRIORITY.RenderNode
|
||||
: JOB_PRIORITY.RenderEdge
|
||||
this.requestViewUpdate(view, flag, options, priority, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,6 +415,7 @@ export class Scheduler {
|
||||
)
|
||||
}
|
||||
|
||||
@Disposable.dispose()
|
||||
dispose() {
|
||||
this.stopListening()
|
||||
}
|
||||
@ -419,7 +430,6 @@ export namespace Scheduler {
|
||||
export enum ViewState {
|
||||
CREATED,
|
||||
MOUNTED,
|
||||
REQUESTED,
|
||||
WAITTING,
|
||||
}
|
||||
export interface View {
|
||||
|
@ -165,23 +165,26 @@ export class EdgeView<
|
||||
this.container.append(ret.fragment)
|
||||
}
|
||||
|
||||
// protected customizeLabels() {
|
||||
// if (this.containers.labels) {
|
||||
// const edge = this.cell
|
||||
// const labels = edge.labels
|
||||
// for (let i = 0, n = labels.length; i < n; i += 1) {
|
||||
// const label = labels[i]
|
||||
// const container = this.labelCache[i]
|
||||
// const selectors = this.labelSelectors[i]
|
||||
// this.graph.hook.onEdgeLabelRendered({
|
||||
// edge,
|
||||
// label,
|
||||
// container,
|
||||
// selectors,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
protected customizeLabels() {
|
||||
if (this.labelContainer) {
|
||||
const edge = this.cell
|
||||
const labels = edge.labels
|
||||
for (let i = 0, n = labels.length; i < n; i += 1) {
|
||||
const label = labels[i]
|
||||
const container = this.labelCache[i]
|
||||
const selectors = this.labelSelectors[i]
|
||||
const onEdgeLabelRendered = this.graph.options.onEdgeLabelRendered
|
||||
if (onEdgeLabelRendered) {
|
||||
onEdgeLabelRendered({
|
||||
edge,
|
||||
label,
|
||||
container,
|
||||
selectors,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected renderLabels() {
|
||||
const edge = this.cell
|
||||
@ -245,8 +248,7 @@ export class EdgeView<
|
||||
}
|
||||
|
||||
this.updateLabels()
|
||||
// todo
|
||||
// this.customizeLabels()
|
||||
this.customizeLabels()
|
||||
|
||||
return this
|
||||
}
|
||||
|
@ -945,16 +945,15 @@ export class NodeView<
|
||||
y: number,
|
||||
cell: Cell,
|
||||
) {
|
||||
const cells = [cell]
|
||||
let cells = [cell]
|
||||
|
||||
// todo
|
||||
// const selection = this.graph.selection.widget
|
||||
// if (selection && selection.options.movable) {
|
||||
// const selectedCells = this.graph.getSelectedCells()
|
||||
// if (selectedCells.includes(cell)) {
|
||||
// cells = selectedCells.filter((c: Cell) => c.isNode())
|
||||
// }
|
||||
// }
|
||||
const selection = this.graph.getPlugin('selection') as any
|
||||
if (selection && selection.isSelectionMovable()) {
|
||||
const selectedCells = selection.getSelectedCells()
|
||||
if (selectedCells.includes(cell)) {
|
||||
cells = selectedCells.filter((c: Cell) => c.isNode())
|
||||
}
|
||||
}
|
||||
|
||||
cells.forEach((c: Cell) => {
|
||||
this.notify(name, {
|
||||
@ -1055,11 +1054,10 @@ export class NodeView<
|
||||
|
||||
// eslint-disable-next-line
|
||||
protected autoScrollGraph(x: number, y: number) {
|
||||
// todo
|
||||
// const scroller = this.graph.scroller.widget
|
||||
// if (scroller) {
|
||||
// scroller.autoScroll(x, y)
|
||||
// }
|
||||
const scroller = this.graph.getPlugin('scroller') as any
|
||||
if (scroller) {
|
||||
scroller.autoScroll(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
@ -391,10 +391,15 @@ export namespace View {
|
||||
}
|
||||
|
||||
if (Config.useCSSSelector) {
|
||||
const validSelector = selector.includes('>')
|
||||
? `:scope ${selector}`
|
||||
: selector
|
||||
return {
|
||||
isCSSSelector: true,
|
||||
// $(rootElem).find(selector).toArray() as Element[] todo
|
||||
elems: Array.prototype.slice.call(rootElem.querySelectorAll(selector)),
|
||||
// $(rootElem).find(selector).toArray() as Element[]
|
||||
elems: Array.prototype.slice.call(
|
||||
rootElem.querySelectorAll(validSelector),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ lerna run build --stream --scope @antv/x6-vue-shape \
|
||||
--scope @antv/x6-react-shape \
|
||||
--scope @antv/x6-plugin-keyboard \
|
||||
--scope @antv/x6-plugin-scroller \
|
||||
--scope @antv/x6-plugin-selection
|
||||
--scope @antv/x6-plugin-selection \
|
||||
--scope @antv/x6-plugin-snapline
|
||||
|
||||
wait
|
||||
|
Reference in New Issue
Block a user