Compare commits

...

20 Commits

Author SHA1 Message Date
c9494e7f79 chore(release): 🚀 publish 2022-10-25 10:52:36 +08:00
1e7f132bed fix: 🐛 fix the error in selected nodes position when snapline enabled () 2022-10-24 21:39:08 +08:00
9a1834e808 chore(release): 🚀 publish 2022-10-20 18:47:07 +08:00
6e1bd9b530 fix: 🐛 debounce update methords in scroller 2022-10-20 17:52:00 +08:00
12e4ac55d7 chore(release): 🚀 publish 2022-10-19 20:07:08 +08:00
294672b306 feat: add snapline plugin 2022-10-19 20:05:34 +08:00
68c2346e0c chore(release): 🚀 publish 2022-10-18 14:58:25 +08:00
24de1254a1 fix: 🐛 ensure css loader for plugin 2022-10-18 14:51:04 +08:00
88918f7611 chore(release): 🚀 publish 2022-10-17 16:20:08 +08:00
5e102a39c5 fix: 🐛 add return value for autoScroller in scroller plugin 2022-10-17 16:13:13 +08:00
2f310fcceb chore: 🔧 optimize project structure 2022-10-16 20:53:27 +08:00
40d53355ce feat: improve auto-resize feature 2022-10-16 16:32:18 +08:00
1dcb3d92fd feat: add some missing api 2022-10-14 16:29:01 +08:00
9fe7cd51a3 chore(release): 🚀 publish 2022-10-11 15:49:11 +08:00
34481de1db chore: 🔧 unify the version numbers of all packages 2022-10-11 15:45:00 +08:00
9d597a92da chore: 🔧 update build-dev script 2022-10-10 12:11:28 +08:00
40f278f064 chore: 🔧 update publish script 2022-10-10 11:25:25 +08:00
f3edbbc95d feat: expose the selection api ()
* chore: 🔧 update publish script

* feat:  expose the selection api
2022-10-09 22:34:13 +08:00
50a5dc7cd8 feat: add selection plugin ()
* feat:  add selection plugin

* fix: 🐛 reset selection default options
2022-10-02 14:21:56 +08:00
5aeae976cd feat: add autoResize feature 2022-09-28 15:47:43 +08:00
133 changed files with 5067 additions and 4040 deletions
examples/x6-example-features/src/pages
lerna.jsonpackage.json
packages
scripts
sites/x6-svg-to-shape

@ -1,262 +0,0 @@
import React from 'react'
import { Graph, Node, Edge, NodeView, Point, Angle, Interp } from '@antv/x6'
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>
)
}
}

@ -12,7 +12,6 @@ export default class Example extends React.Component {
container: this.container,
width: 650,
height: 400,
grid: 1,
background: {
color: '#F2F7FA',
},

@ -0,0 +1,9 @@
.full {
width: 100%;
height: 100%;
}
.x6-split-box-horizontal > .x6-split-box-resizer,
.x6-split-box-vertical > .x6-split-box-resizer {
background: #ffe58f;
}

@ -1,68 +1,45 @@
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'
import './index.less'
export default class Example extends React.Component {
private graphContainer1: HTMLDivElement
private graphContainer2: HTMLDivElement
private graphContainer3: HTMLDivElement
componentDidMount() {
const graph1 = new Graph({
new Graph({
container: this.graphContainer1,
background: {
color: '#f5f5f5',
color: '#D94111',
},
grid: true,
scroller: true,
autoResize: true,
})
const rect = graph1.addNode({
x: 300,
y: 300,
width: 90,
height: 60,
})
const circle = graph1.addNode({
x: 400,
y: 400,
width: 40,
height: 40,
})
graph1.addEdge({
source: rect,
target: circle,
})
const graph2 = new Graph({
new Graph({
container: this.graphContainer2,
background: {
color: '#f5f5f5',
color: '#90C54C',
},
grid: true,
autoResize: true,
})
const source = graph2.addNode({
x: 40,
y: 40,
width: 80,
height: 40,
})
const target = graph2.addNode({
x: 120,
y: 100,
width: 80,
height: 40,
})
graph2.addEdge({
source,
target,
const graph = new Graph({
container: this.graphContainer3,
background: {
color: '#0491E4',
},
autoResize: true,
})
graph.use(
new Scroller({
enabled: true,
}),
)
}
refContainer1 = (container: HTMLDivElement) => {
@ -73,23 +50,29 @@ export default class Example extends React.Component {
this.graphContainer2 = container
}
refContainer3 = (container: HTMLDivElement) => {
this.graphContainer3 = container
}
render() {
return (
<div style={{ width: '100%', height: '100%' }}>
<div className="x6-graph-wrap" style={{ display: 'flex' }}>
<div
ref={this.refContainer1}
style={{ flex: 1, margin: '0 32px' }}
className="x6-graph"
/>
</div>
<div className="x6-graph-wrap" style={{ display: 'flex' }}>
<div
ref={this.refContainer2}
style={{ flex: 1, margin: '0 32px' }}
className="x6-graph"
/>
</div>
<div
className="x6-graph-wrap"
style={{ width: 800, height: 800, margin: '0 auto' }}
>
<SplitBox split="horizontal">
<div className="full">
<div ref={this.refContainer1} className="x6-graph" />
</div>
<SplitBox split="vertical">
<div className="full">
<div ref={this.refContainer2} className="x6-graph" />
</div>
<div className="full">
<div ref={this.refContainer3} className="x6-graph" />
</div>
</SplitBox>
</SplitBox>
</div>
)
}

@ -9,24 +9,31 @@ export default class Example extends React.Component {
const graph = new Graph({
container: this.container,
width: 800,
height: 400,
height: 600,
grid: true,
async: true,
})
graph.addNode({
x: 120,
const source = graph.addNode({
shape: 'rect',
x: 80,
y: 80,
width: 100,
height: 40,
label: 'rect',
zIndex: 10,
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: { x: 10, y: 10 },
target: { x: 300, y: 200 },
zIndex: 1,
source,
target,
})
}

@ -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 = {

@ -1,468 +1,468 @@
import React from 'react'
import { Graph } from '@antv/x6'
import { Marker } from '@antv/x6/es/registry'
import '../index.less'
// import React from 'react'
// import { Graph } from '@antv/x6'
// import { Marker } from '@antv/x6/es/registry'
// import '../index.less'
export default class Example extends React.Component {
private container: HTMLDivElement
// export default class Example extends React.Component {
// private container: HTMLDivElement
componentDidMount() {
const graph = new Graph({
container: this.container,
width: 800,
height: 600,
interacting: { edgeMovable: false },
connecting: {
connectionPoint: {
name: 'boundary',
args: {
extrapolate: true,
sticky: true,
},
},
validateConnection: function () {
return false
},
},
})
// componentDidMount() {
// const graph = new Graph({
// container: this.container,
// width: 800,
// height: 600,
// interacting: { edgeMovable: false },
// connecting: {
// connectionPoint: {
// name: 'boundary',
// args: {
// extrapolate: true,
// sticky: true,
// },
// },
// validateConnection: function () {
// return false
// },
// },
// })
graph.addEdge({
source: { x: 20, y: 20 },
target: { x: 350, y: 20 },
attrs: {
line: {
stroke: '#222138',
sourceMarker: {
name: 'classic',
fill: '#31d0c6',
stroke: 'none',
size: 20,
},
targetMarker: {
name: 'block',
fill: '#fe854f',
stroke: 'none',
size: 20,
},
},
},
})
// graph.addEdge({
// source: { x: 20, y: 20 },
// target: { x: 350, y: 20 },
// attrs: {
// line: {
// stroke: '#222138',
// sourceMarker: {
// name: 'classic',
// fill: '#31d0c6',
// stroke: 'none',
// size: 20,
// },
// targetMarker: {
// name: 'block',
// fill: '#fe854f',
// stroke: 'none',
// size: 20,
// },
// },
// },
// })
graph.addEdge({
source: { x: 20, y: 80 },
target: { x: 350, y: 80 },
attrs: {
line: {
stroke: '#fe854f',
strokeWidth: 1,
sourceMarker: 'block',
targetMarker: {
tagName: 'circle',
r: 5,
},
},
},
})
// graph.addEdge({
// source: { x: 20, y: 80 },
// target: { x: 350, y: 80 },
// attrs: {
// line: {
// stroke: '#fe854f',
// strokeWidth: 1,
// sourceMarker: 'block',
// targetMarker: {
// tagName: 'circle',
// r: 5,
// },
// },
// },
// })
graph.addEdge({
source: { x: 20, y: 400 },
target: { x: 280, y: 400 },
attrs: {
line: {
stroke: '#fe854f',
strokeWidth: 1,
sourceMarker: 'block',
targetMarker: {
name: 'block',
width: 12,
height: 6,
open: true,
},
},
},
})
// graph.addEdge({
// source: { x: 20, y: 400 },
// target: { x: 280, y: 400 },
// attrs: {
// line: {
// stroke: '#fe854f',
// strokeWidth: 1,
// sourceMarker: 'block',
// targetMarker: {
// name: 'block',
// width: 12,
// height: 6,
// open: true,
// },
// },
// },
// })
graph.addEdge({
source: { x: 20, y: 430 },
target: { x: 280, y: 430 },
attrs: {
line: {
stroke: '#fe854f',
strokeWidth: 1,
sourceMarker: 'diamond',
targetMarker: {
name: 'diamond',
width: 12,
height: 6,
},
},
},
})
// graph.addEdge({
// source: { x: 20, y: 430 },
// target: { x: 280, y: 430 },
// attrs: {
// line: {
// stroke: '#fe854f',
// strokeWidth: 1,
// sourceMarker: 'diamond',
// targetMarker: {
// name: 'diamond',
// width: 12,
// height: 6,
// },
// },
// },
// })
graph.addEdge({
source: { x: 20, y: 460 },
target: { x: 280, y: 460 },
attrs: {
line: {
stroke: '#fe854f',
strokeWidth: 1,
sourceMarker: 'ellipse',
targetMarker: {
name: 'ellipse',
rx: 6,
ry: 4,
},
},
},
})
// graph.addEdge({
// source: { x: 20, y: 460 },
// target: { x: 280, y: 460 },
// attrs: {
// line: {
// stroke: '#fe854f',
// strokeWidth: 1,
// sourceMarker: 'ellipse',
// targetMarker: {
// name: 'ellipse',
// rx: 6,
// ry: 4,
// },
// },
// },
// })
graph.addEdge({
source: { x: 20, y: 490 },
target: { x: 280, y: 490 },
attrs: {
line: {
stroke: '#fe854f',
strokeWidth: 1,
sourceMarker: 'circle',
targetMarker: {
name: 'circlePlus',
r: 10,
},
},
},
})
// graph.addEdge({
// source: { x: 20, y: 490 },
// target: { x: 280, y: 490 },
// attrs: {
// line: {
// stroke: '#fe854f',
// strokeWidth: 1,
// sourceMarker: 'circle',
// targetMarker: {
// name: 'circlePlus',
// r: 10,
// },
// },
// },
// })
graph.addEdge({
source: { x: 20, y: 520 },
target: { x: 280, y: 520 },
attrs: {
line: {
stroke: '#fe854f',
strokeWidth: 1,
sourceMarker: 'cross',
targetMarker: {
name: 'cross',
width: 12,
height: 8,
offset: -10,
},
},
},
})
// graph.addEdge({
// source: { x: 20, y: 520 },
// target: { x: 280, y: 520 },
// attrs: {
// line: {
// stroke: '#fe854f',
// strokeWidth: 1,
// sourceMarker: 'cross',
// targetMarker: {
// name: 'cross',
// width: 12,
// height: 8,
// offset: -10,
// },
// },
// },
// })
graph.addEdge({
source: { x: 20, y: 550 },
target: { x: 280, y: 550 },
attrs: {
line: {
stroke: '#fe854f',
strokeWidth: 1,
sourceMarker: 'async',
targetMarker: {
name: 'async',
width: 12,
height: 8,
offset: -10,
open: true,
flip: true,
},
},
},
})
// graph.addEdge({
// source: { x: 20, y: 550 },
// target: { x: 280, y: 550 },
// attrs: {
// line: {
// stroke: '#fe854f',
// strokeWidth: 1,
// sourceMarker: 'async',
// targetMarker: {
// name: 'async',
// width: 12,
// height: 8,
// offset: -10,
// open: true,
// flip: true,
// },
// },
// },
// })
graph.addEdge({
source: { x: 10, y: 140 },
target: { x: 350, y: 140 },
attrs: {
line: {
stroke: '#31d0c6',
strokeWidth: 3,
strokeDasharray: '5 2',
sourceMarker: {
stroke: '#31d0c6',
fill: '#31d0c6',
d: Marker.normalize(
'M5.5,15.499,15.8,21.447,15.8,15.846,25.5,21.447,25.5,9.552,15.8,15.152,15.8,9.552z',
),
},
targetMarker: {
stroke: '#31d0c6',
fill: '#31d0c6',
name: 'path',
d: 'M4.834,4.834L4.833,4.833c-5.889,5.892-5.89,15.443,0.001,21.334s15.44,5.888,21.33-0.002c5.891-5.891,5.893-15.44,0.002-21.33C20.275-1.056,10.725-1.056,4.834,4.834zM25.459,5.542c0.833,0.836,1.523,1.757,2.104,2.726l-4.08,4.08c-0.418-1.062-1.053-2.06-1.912-2.918c-0.859-0.859-1.857-1.494-2.92-1.913l4.08-4.08C23.7,4.018,24.622,4.709,25.459,5.542zM10.139,20.862c-2.958-2.968-2.959-7.758-0.001-10.725c2.966-2.957,7.756-2.957,10.725,0c2.954,2.965,2.955,7.757-0.001,10.724C17.896,23.819,13.104,23.817,10.139,20.862zM5.542,25.459c-0.833-0.837-1.524-1.759-2.105-2.728l4.081-4.081c0.418,1.063,1.055,2.06,1.914,2.919c0.858,0.859,1.855,1.494,2.917,1.913l-4.081,4.081C7.299,26.982,6.379,26.292,5.542,25.459zM8.268,3.435l4.082,4.082C11.288,7.935,10.29,8.571,9.43,9.43c-0.858,0.859-1.494,1.855-1.912,2.918L3.436,8.267c0.58-0.969,1.271-1.89,2.105-2.727C6.377,4.707,7.299,4.016,8.268,3.435zM22.732,27.563l-4.082-4.082c1.062-0.418,2.061-1.053,2.919-1.912c0.859-0.859,1.495-1.857,1.913-2.92l4.082,4.082c-0.58,0.969-1.271,1.891-2.105,2.728C24.623,26.292,23.701,26.983,22.732,27.563z',
offsetX: 10,
},
},
},
})
// graph.addEdge({
// source: { x: 10, y: 140 },
// target: { x: 350, y: 140 },
// attrs: {
// line: {
// stroke: '#31d0c6',
// strokeWidth: 3,
// strokeDasharray: '5 2',
// sourceMarker: {
// stroke: '#31d0c6',
// fill: '#31d0c6',
// d: Marker.normalize(
// 'M5.5,15.499,15.8,21.447,15.8,15.846,25.5,21.447,25.5,9.552,15.8,15.152,15.8,9.552z',
// ),
// },
// targetMarker: {
// stroke: '#31d0c6',
// fill: '#31d0c6',
// name: 'path',
// d: 'M4.834,4.834L4.833,4.833c-5.889,5.892-5.89,15.443,0.001,21.334s15.44,5.888,21.33-0.002c5.891-5.891,5.893-15.44,0.002-21.33C20.275-1.056,10.725-1.056,4.834,4.834zM25.459,5.542c0.833,0.836,1.523,1.757,2.104,2.726l-4.08,4.08c-0.418-1.062-1.053-2.06-1.912-2.918c-0.859-0.859-1.857-1.494-2.92-1.913l4.08-4.08C23.7,4.018,24.622,4.709,25.459,5.542zM10.139,20.862c-2.958-2.968-2.959-7.758-0.001-10.725c2.966-2.957,7.756-2.957,10.725,0c2.954,2.965,2.955,7.757-0.001,10.724C17.896,23.819,13.104,23.817,10.139,20.862zM5.542,25.459c-0.833-0.837-1.524-1.759-2.105-2.728l4.081-4.081c0.418,1.063,1.055,2.06,1.914,2.919c0.858,0.859,1.855,1.494,2.917,1.913l-4.081,4.081C7.299,26.982,6.379,26.292,5.542,25.459zM8.268,3.435l4.082,4.082C11.288,7.935,10.29,8.571,9.43,9.43c-0.858,0.859-1.494,1.855-1.912,2.918L3.436,8.267c0.58-0.969,1.271-1.89,2.105-2.727C6.377,4.707,7.299,4.016,8.268,3.435zM22.732,27.563l-4.082-4.082c1.062-0.418,2.061-1.053,2.919-1.912c0.859-0.859,1.495-1.857,1.913-2.92l4.082,4.082c-0.58,0.969-1.271,1.891-2.105,2.728C24.623,26.292,23.701,26.983,22.732,27.563z',
// offsetX: 10,
// },
// },
// },
// })
graph.addEdge({
source: { x: 400, y: 20 },
target: { x: 740, y: 20 },
vertices: [
{ x: 400, y: 60 },
{ x: 550, y: 60 },
{ x: 550, y: 20 },
],
attrs: {
line: {
stroke: '#3c4260',
strokeWidth: 2,
sourceMarker: {
fill: '#4b4a67',
stroke: '#4b4a67',
d: Marker.normalize(
'M5.5,15.499,15.8,21.447,15.8,15.846,25.5,21.447,25.5,9.552,15.8,15.152,15.8,9.552z',
),
},
targetMarker: {
fill: '#4b4a67',
stroke: '#4b4a67',
d: Marker.normalize(
'M5.5,15.499,15.8,21.447,15.8,15.846,25.5,21.447,25.5,9.552,15.8,15.152,15.8,9.552z',
),
},
vertexMarker: {
tagName: 'circle',
r: 4,
strokeWidth: 2,
fill: 'white',
},
},
},
})
// graph.addEdge({
// source: { x: 400, y: 20 },
// target: { x: 740, y: 20 },
// vertices: [
// { x: 400, y: 60 },
// { x: 550, y: 60 },
// { x: 550, y: 20 },
// ],
// attrs: {
// line: {
// stroke: '#3c4260',
// strokeWidth: 2,
// sourceMarker: {
// fill: '#4b4a67',
// stroke: '#4b4a67',
// d: Marker.normalize(
// 'M5.5,15.499,15.8,21.447,15.8,15.846,25.5,21.447,25.5,9.552,15.8,15.152,15.8,9.552z',
// ),
// },
// targetMarker: {
// fill: '#4b4a67',
// stroke: '#4b4a67',
// d: Marker.normalize(
// 'M5.5,15.499,15.8,21.447,15.8,15.846,25.5,21.447,25.5,9.552,15.8,15.152,15.8,9.552z',
// ),
// },
// vertexMarker: {
// tagName: 'circle',
// r: 4,
// strokeWidth: 2,
// fill: 'white',
// },
// },
// },
// })
graph.addEdge({
source: { x: 440, y: 100 },
target: { x: 740, y: 100 },
vertices: [
{ x: 400, y: 140 },
{ x: 550, y: 100 },
{ x: 600, y: 140 },
],
smooth: true,
attrs: {
line: {
stroke: '#7c68fc',
strokeWidth: 3,
sourceMarker: {
stroke: '#7c68fc',
fill: '#7c68fc',
d: Marker.normalize(
'M24.316,5.318,9.833,13.682,9.833,5.5,5.5,5.5,5.5,25.5,9.833,25.5,9.833,17.318,24.316,25.682z',
),
},
targetMarker: {
stroke: '#feb663',
fill: '#feb663',
d: Marker.normalize(
'M14.615,4.928c0.487-0.986,1.284-0.986,1.771,0l2.249,4.554c0.486,0.986,1.775,1.923,2.864,2.081l5.024,0.73c1.089,0.158,1.335,0.916,0.547,1.684l-3.636,3.544c-0.788,0.769-1.28,2.283-1.095,3.368l0.859,5.004c0.186,1.085-0.459,1.553-1.433,1.041l-4.495-2.363c-0.974-0.512-2.567-0.512-3.541,0l-4.495,2.363c-0.974,0.512-1.618,0.044-1.432-1.041l0.858-5.004c0.186-1.085-0.307-2.6-1.094-3.368L3.93,13.977c-0.788-0.768-0.542-1.525,0.547-1.684l5.026-0.73c1.088-0.158,2.377-1.095,2.864-2.081L14.615,4.928z',
),
},
},
},
})
// graph.addEdge({
// source: { x: 440, y: 100 },
// target: { x: 740, y: 100 },
// vertices: [
// { x: 400, y: 140 },
// { x: 550, y: 100 },
// { x: 600, y: 140 },
// ],
// smooth: true,
// attrs: {
// line: {
// stroke: '#7c68fc',
// strokeWidth: 3,
// sourceMarker: {
// stroke: '#7c68fc',
// fill: '#7c68fc',
// d: Marker.normalize(
// 'M24.316,5.318,9.833,13.682,9.833,5.5,5.5,5.5,5.5,25.5,9.833,25.5,9.833,17.318,24.316,25.682z',
// ),
// },
// targetMarker: {
// stroke: '#feb663',
// fill: '#feb663',
// d: Marker.normalize(
// 'M14.615,4.928c0.487-0.986,1.284-0.986,1.771,0l2.249,4.554c0.486,0.986,1.775,1.923,2.864,2.081l5.024,0.73c1.089,0.158,1.335,0.916,0.547,1.684l-3.636,3.544c-0.788,0.769-1.28,2.283-1.095,3.368l0.859,5.004c0.186,1.085-0.459,1.553-1.433,1.041l-4.495-2.363c-0.974-0.512-2.567-0.512-3.541,0l-4.495,2.363c-0.974,0.512-1.618,0.044-1.432-1.041l0.858-5.004c0.186-1.085-0.307-2.6-1.094-3.368L3.93,13.977c-0.788-0.768-0.542-1.525,0.547-1.684l5.026-0.73c1.088-0.158,2.377-1.095,2.864-2.081L14.615,4.928z',
// ),
// },
// },
// },
// })
graph.addEdge({
shape: 'double-edge',
source: { x: 10, y: 200 },
target: { x: 350, y: 200 },
attrs: {
line: {
stroke: '#7c68fc',
},
},
labels: [
{
attrs: { text: { text: 'Label' } },
position: {
offset: 15,
distance: 0.5,
},
},
],
})
// graph.addEdge({
// shape: 'double-edge',
// source: { x: 10, y: 200 },
// target: { x: 350, y: 200 },
// attrs: {
// line: {
// stroke: '#7c68fc',
// },
// },
// labels: [
// {
// attrs: { text: { text: 'Label' } },
// position: {
// offset: 15,
// distance: 0.5,
// },
// },
// ],
// })
graph.addEdge({
source: { x: 400, y: 200 },
target: { x: 740, y: 200 },
connector: { name: 'smooth' },
attrs: {
line: {
targetMarker: {
d: 'M 0 -5 L -10 0 L 0 5 Z',
},
},
},
labels: [
{
markup: [
{
tagName: 'rect',
selector: 'labelBody',
},
{
tagName: 'text',
selector: 'labelText',
},
],
attrs: {
labelText: {
text: 'First',
fill: '#7c68fc',
fontFamily: 'sans-serif',
textAnchor: 'middle',
textVerticalAnchor: 'middle',
},
labelBody: {
ref: 'labelText',
refX: -5,
refY: -5,
refWidth: '100%',
refHeight: '100%',
refWidth2: 10,
refHeight2: 10,
stroke: '#7c68fc',
fill: 'white',
strokeWidth: 2,
rx: 5,
ry: 5,
},
},
position: {
distance: 0.3,
options: {
keepGradient: true,
ensureLegibility: true,
},
},
},
{
markup: [
{
tagName: 'ellipse',
selector: 'labelBody',
},
{
tagName: 'text',
selector: 'labelText',
},
],
attrs: {
labelText: {
text: 'Second',
fill: '#31d0c6',
fontFamily: 'sans-serif',
textAnchor: 'middle',
textVerticalAnchor: 'middle',
},
labelBody: {
ref: 'labelText',
refRx: '70%',
refRy: '80%',
stroke: '#31d0c6',
fill: 'white',
strokeWidth: 2,
},
},
position: {
distance: 0.7,
angle: 45,
},
},
],
})
// graph.addEdge({
// source: { x: 400, y: 200 },
// target: { x: 740, y: 200 },
// connector: { name: 'smooth' },
// attrs: {
// line: {
// targetMarker: {
// d: 'M 0 -5 L -10 0 L 0 5 Z',
// },
// },
// },
// labels: [
// {
// markup: [
// {
// tagName: 'rect',
// selector: 'labelBody',
// },
// {
// tagName: 'text',
// selector: 'labelText',
// },
// ],
// attrs: {
// labelText: {
// text: 'First',
// fill: '#7c68fc',
// fontFamily: 'sans-serif',
// textAnchor: 'middle',
// textVerticalAnchor: 'middle',
// },
// labelBody: {
// ref: 'labelText',
// refX: -5,
// refY: -5,
// refWidth: '100%',
// refHeight: '100%',
// refWidth2: 10,
// refHeight2: 10,
// stroke: '#7c68fc',
// fill: 'white',
// strokeWidth: 2,
// rx: 5,
// ry: 5,
// },
// },
// position: {
// distance: 0.3,
// options: {
// keepGradient: true,
// ensureLegibility: true,
// },
// },
// },
// {
// markup: [
// {
// tagName: 'ellipse',
// selector: 'labelBody',
// },
// {
// tagName: 'text',
// selector: 'labelText',
// },
// ],
// attrs: {
// labelText: {
// text: 'Second',
// fill: '#31d0c6',
// fontFamily: 'sans-serif',
// textAnchor: 'middle',
// textVerticalAnchor: 'middle',
// },
// labelBody: {
// ref: 'labelText',
// refRx: '70%',
// refRy: '80%',
// stroke: '#31d0c6',
// fill: 'white',
// strokeWidth: 2,
// },
// },
// position: {
// distance: 0.7,
// angle: 45,
// },
// },
// ],
// })
graph.addEdge({
shape: 'shadow-edge',
source: { x: 10, y: 280 },
target: { x: 440, y: 280 },
vertices: [
{ x: 150, y: 350 },
{ x: 300, y: 280 },
],
connector: { name: 'smooth' },
markup: [
{
tagName: 'path',
selector: 'shadow',
attrs: {
fill: 'none',
},
},
{
tagName: 'path',
selector: 'line',
attrs: {
fill: 'none',
},
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
line: {
stroke: '#3c4260',
},
label: {
textPath: {
selector: 'line',
startOffset: '50%',
},
textAnchor: 'middle',
textVerticalAnchor: 'middle',
text: 'Label Along Path',
fill: '#f6f6f6',
fontSize: 15,
fontWeight: 'bold',
fontFamily: 'fantasy',
},
},
})
// graph.addEdge({
// shape: 'shadow-edge',
// source: { x: 10, y: 280 },
// target: { x: 440, y: 280 },
// vertices: [
// { x: 150, y: 350 },
// { x: 300, y: 280 },
// ],
// connector: { name: 'smooth' },
// markup: [
// {
// tagName: 'path',
// selector: 'shadow',
// attrs: {
// fill: 'none',
// },
// },
// {
// tagName: 'path',
// selector: 'line',
// attrs: {
// fill: 'none',
// },
// },
// {
// tagName: 'text',
// selector: 'label',
// },
// ],
// attrs: {
// line: {
// stroke: '#3c4260',
// },
// label: {
// textPath: {
// selector: 'line',
// startOffset: '50%',
// },
// textAnchor: 'middle',
// textVerticalAnchor: 'middle',
// text: 'Label Along Path',
// fill: '#f6f6f6',
// fontSize: 15,
// fontWeight: 'bold',
// fontFamily: 'fantasy',
// },
// },
// })
// Custom Edge
// -----------
// // Custom Edge
// // -----------
const node1 = graph.addNode({
shape: 'path',
x: 500,
y: 450,
width: 100,
height: 100,
attrs: {
body: {
fill: '#31d0c6',
refD: 'M 0 20 10 20 10 30 30 30 30 0 40 0 40 40 0 40 z',
},
},
})
// const node1 = graph.addNode({
// shape: 'path',
// x: 500,
// y: 450,
// width: 100,
// height: 100,
// attrs: {
// body: {
// fill: '#31d0c6',
// refD: 'M 0 20 10 20 10 30 30 30 30 0 40 0 40 40 0 40 z',
// },
// },
// })
graph.addEdge({
source: { x: 300, y: 400 },
target: node1,
attrs: {
line: {
sourceMarker: {
d: 'M 0 0 15 0',
stroke: 'white',
strokeWidth: 3,
},
},
},
})
}
// graph.addEdge({
// source: { x: 300, y: 400 },
// target: node1,
// attrs: {
// line: {
// sourceMarker: {
// d: 'M 0 0 15 0',
// stroke: 'white',
// strokeWidth: 3,
// },
// },
// },
// })
// }
refContainer = (container: HTMLDivElement) => {
this.container = container
}
// refContainer = (container: HTMLDivElement) => {
// this.container = container
// }
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}
// render() {
// return (
// <div className="x6-graph-wrap">
// <div ref={this.refContainer} className="x6-graph" />
// </div>
// )
// }
// }

@ -1,75 +1,75 @@
import React from 'react'
import { Graph, Point } from '@antv/x6'
import { Router } from '@antv/x6/es/registry/router'
import '../index.less'
// import React from 'react'
// import { Graph, Point } from '@antv/x6'
// import { Router } from '@antv/x6/es/registry/router'
// import '../index.less'
Router.registry.register(
'random',
(vertices, args, view) => {
const BOUNCES = args.bounces || 20
const points = vertices.map((p) => Point.create(p))
// Router.registry.register(
// 'random',
// (vertices, args, view) => {
// const BOUNCES = args.bounces || 20
// const points = vertices.map((p) => Point.create(p))
for (var i = 0; i < BOUNCES; i++) {
const sourceCorner = view.sourceBBox.getCenter()
const targetCorner = view.targetBBox.getCenter()
const randomPoint = Point.random(
sourceCorner.x,
targetCorner.x,
sourceCorner.y,
targetCorner.y,
)
points.push(randomPoint)
}
// for (var i = 0; i < BOUNCES; i++) {
// const sourceCorner = view.sourceBBox.getCenter()
// const targetCorner = view.targetBBox.getCenter()
// const randomPoint = Point.random(
// sourceCorner.x,
// targetCorner.x,
// sourceCorner.y,
// targetCorner.y,
// )
// points.push(randomPoint)
// }
return points
},
true,
)
// return points
// },
// true,
// )
export default class Example extends React.Component {
private container: HTMLDivElement
// export default class Example extends React.Component {
// private container: HTMLDivElement
componentDidMount() {
const graph = new Graph({
container: this.container,
width: 1000,
height: 600,
grid: 10,
})
// componentDidMount() {
// const graph = new Graph({
// container: this.container,
// width: 1000,
// height: 600,
// grid: 10,
// })
const source = graph.addNode({
x: 50,
y: 50,
width: 120,
height: 80,
attrs: { label: { text: 'Source' } },
})
// const source = graph.addNode({
// x: 50,
// y: 50,
// width: 120,
// height: 80,
// attrs: { label: { text: 'Source' } },
// })
const target = graph.addNode(
source.clone().translate(600, 400).attr('label/text', 'Target'),
)
// const target = graph.addNode(
// source.clone().translate(600, 400).attr('label/text', 'Target'),
// )
graph.addEdge({
source,
target,
router: {
name: 'random',
args: {
bounces: 10,
},
},
})
}
// graph.addEdge({
// source,
// target,
// router: {
// name: 'random',
// args: {
// bounces: 10,
// },
// },
// })
// }
refContainer = (container: HTMLDivElement) => {
this.container = container
}
// refContainer = (container: HTMLDivElement) => {
// this.container = container
// }
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}
// render() {
// return (
// <div className="x6-graph-wrap">
// <div ref={this.refContainer} className="x6-graph" />
// </div>
// )
// }
// }

@ -1,298 +1,298 @@
import { Rect, Edge } from '@antv/x6/es/shape/standard'
// import { Rect, Edge } from '@antv/x6/es/shape/standard'
export class Event extends Rect {
static create(label: string) {
return new Event({
attrs: {
label: {
text: label,
},
},
})
}
}
// export class Event extends Rect {
// static create(label: string) {
// return new Event({
// attrs: {
// label: {
// text: label,
// },
// },
// })
// }
// }
Event.config({
zIndex: 3,
attrs: {
root: {
pointerEvents: 'bounding-box',
},
body: {
strokeWidth: 2,
fillOpacity: 0.2,
},
label: {
textWrap: {
height: -20,
width: -20,
ellipsis: true,
},
refX: '50%',
refY: '50%',
fontSize: 16,
fontFamily: 'sans-serif',
fill: '#333333',
textAnchor: 'middle',
textVerticalAnchor: 'middle',
},
},
})
// Event.config({
// zIndex: 3,
// attrs: {
// root: {
// pointerEvents: 'bounding-box',
// },
// body: {
// strokeWidth: 2,
// fillOpacity: 0.2,
// },
// label: {
// textWrap: {
// height: -20,
// width: -20,
// ellipsis: true,
// },
// refX: '50%',
// refY: '50%',
// fontSize: 16,
// fontFamily: 'sans-serif',
// fill: '#333333',
// textAnchor: 'middle',
// textVerticalAnchor: 'middle',
// },
// },
// })
export class IntermediateEvent extends Event {
static create(label: string, type?: string) {
return new IntermediateEvent({
attrs: {
label: {
text: label,
},
gate: {
gateType: type,
},
},
})
}
gateTypes = {
or: 'M -20 0 C -20 -15 -10 -30 0 -30 C 10 -30 20 -15 20 0 C 10 -6 -10 -6 -20 0',
xor: 'M -20 0 C -20 -15 -10 -30 0 -30 C 10 -30 20 -15 20 0 C 10 -6 -10 -6 -20 0 M -20 0 0 -30 M 0 -30 20 0',
and: 'M -20 0 C -20 -25 -10 -30 0 -30 C 10 -30 20 -25 20 0 Z',
priority_and:
'M -20 0 C -20 -25 -10 -30 0 -30 C 10 -30 20 -25 20 0 Z M -20 0 0 -30 20 0',
inhibit: 'M -10 0 -20 -15 -10 -30 10 -30 20 -15 10 0 Z',
transfer: 'M -20 0 20 0 0 -30 z',
}
// export class IntermediateEvent extends Event {
// static create(label: string, type?: string) {
// return new IntermediateEvent({
// attrs: {
// label: {
// text: label,
// },
// gate: {
// gateType: type,
// },
// },
// })
// }
// gateTypes = {
// or: 'M -20 0 C -20 -15 -10 -30 0 -30 C 10 -30 20 -15 20 0 C 10 -6 -10 -6 -20 0',
// xor: 'M -20 0 C -20 -15 -10 -30 0 -30 C 10 -30 20 -15 20 0 C 10 -6 -10 -6 -20 0 M -20 0 0 -30 M 0 -30 20 0',
// and: 'M -20 0 C -20 -25 -10 -30 0 -30 C 10 -30 20 -25 20 0 Z',
// priority_and:
// 'M -20 0 C -20 -25 -10 -30 0 -30 C 10 -30 20 -25 20 0 Z M -20 0 0 -30 20 0',
// inhibit: 'M -10 0 -20 -15 -10 -30 10 -30 20 -15 10 0 Z',
// transfer: 'M -20 0 20 0 0 -30 z',
// }
gate(): string
gate(type: string): this
gate(type?: string) {
if (type === undefined) {
return this.attr<string>(['gate', 'gateType'])
}
// gate(): string
// gate(type: string): this
// gate(type?: string) {
// if (type === undefined) {
// return this.attr<string>(['gate', 'gateType'])
// }
this.attr(['gate'], {
gateType: type,
title: type.toUpperCase() + ' Gate',
})
// this.attr(['gate'], {
// gateType: type,
// title: type.toUpperCase() + ' Gate',
// })
return this
}
}
// return this
// }
// }
IntermediateEvent.config(
{
size: {
width: 100,
height: 100,
},
markup: [
{
tagName: 'path',
selector: 'gate',
},
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
root: {
title: 'Intermediate Event',
},
body: {
refWidth: '100%',
refHeight: -40,
stroke: '#3c4260',
fill: '#3c4260',
},
gate: {
event: 'element:gate:click',
gateType: 'xor',
stroke: '#7c68fc',
fill: '#7c68fc',
fillOpacity: 0.2,
strokeWidth: 2,
refX: '50%',
refY: '100%',
fillRule: 'nonzero',
cursor: 'pointer',
},
label: {
textWrap: {
height: -40,
width: -10,
},
refY2: -20,
},
},
},
{
gateType: {
set(type: string) {
var data = this.cell.gateTypes[type]
return { d: data ? data + ' M 0 -30 0 -40' : 'M 0 0 0 0' }
},
},
},
)
// IntermediateEvent.config(
// {
// size: {
// width: 100,
// height: 100,
// },
// markup: [
// {
// tagName: 'path',
// selector: 'gate',
// },
// {
// tagName: 'rect',
// selector: 'body',
// },
// {
// tagName: 'text',
// selector: 'label',
// },
// ],
// attrs: {
// root: {
// title: 'Intermediate Event',
// },
// body: {
// refWidth: '100%',
// refHeight: -40,
// stroke: '#3c4260',
// fill: '#3c4260',
// },
// gate: {
// event: 'element:gate:click',
// gateType: 'xor',
// stroke: '#7c68fc',
// fill: '#7c68fc',
// fillOpacity: 0.2,
// strokeWidth: 2,
// refX: '50%',
// refY: '100%',
// fillRule: 'nonzero',
// cursor: 'pointer',
// },
// label: {
// textWrap: {
// height: -40,
// width: -10,
// },
// refY2: -20,
// },
// },
// },
// {
// gateType: {
// set(type: string) {
// var data = this.cell.gateTypes[type]
// return { d: data ? data + ' M 0 -30 0 -40' : 'M 0 0 0 0' }
// },
// },
// },
// )
export class ExternalEvent extends Event {}
// export class ExternalEvent extends Event {}
ExternalEvent.config({
size: {
width: 80,
height: 100,
},
markup: [
{
tagName: 'path',
selector: 'body',
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
root: {
title: 'External Event',
},
body: {
refD: 'M 0 0 10 -10 20 0 20 40 0 40 Z',
stroke: '#fe854f',
fill: '#fe854f',
},
},
})
// ExternalEvent.config({
// size: {
// width: 80,
// height: 100,
// },
// markup: [
// {
// tagName: 'path',
// selector: 'body',
// },
// {
// tagName: 'text',
// selector: 'label',
// },
// ],
// attrs: {
// root: {
// title: 'External Event',
// },
// body: {
// refD: 'M 0 0 10 -10 20 0 20 40 0 40 Z',
// stroke: '#fe854f',
// fill: '#fe854f',
// },
// },
// })
export class UndevelopedEvent extends Event {}
UndevelopedEvent.config({
size: {
width: 140,
height: 80,
},
markup: [
{
tagName: 'path',
selector: 'body',
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
root: {
title: 'Undeveloped Event',
},
body: {
refD: 'M -1 0 0 1 1 0 0 -1 Z',
stroke: '#feb663',
fill: '#feb663',
},
},
})
// export class UndevelopedEvent extends Event {}
// UndevelopedEvent.config({
// size: {
// width: 140,
// height: 80,
// },
// markup: [
// {
// tagName: 'path',
// selector: 'body',
// },
// {
// tagName: 'text',
// selector: 'label',
// },
// ],
// attrs: {
// root: {
// title: 'Undeveloped Event',
// },
// body: {
// refD: 'M -1 0 0 1 1 0 0 -1 Z',
// stroke: '#feb663',
// fill: '#feb663',
// },
// },
// })
export class BasicEvent extends Event {}
BasicEvent.config({
zIndex: 3,
size: {
width: 80,
height: 80,
},
markup: [
{
tagName: 'circle',
selector: 'body',
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
root: {
title: 'Basic Event',
},
body: {
refCx: '50%',
refCy: '50%',
refR: '50%',
stroke: '#30d0c6',
fill: '#30d0c6',
},
},
})
// export class BasicEvent extends Event {}
// BasicEvent.config({
// zIndex: 3,
// size: {
// width: 80,
// height: 80,
// },
// markup: [
// {
// tagName: 'circle',
// selector: 'body',
// },
// {
// tagName: 'text',
// selector: 'label',
// },
// ],
// attrs: {
// root: {
// title: 'Basic Event',
// },
// body: {
// refCx: '50%',
// refCy: '50%',
// refR: '50%',
// stroke: '#30d0c6',
// fill: '#30d0c6',
// },
// },
// })
export class ConditioningEvent extends Event {}
ConditioningEvent.config({
zIndex: 2,
size: {
width: 140,
height: 80,
},
markup: [
{
tagName: 'ellipse',
selector: 'body',
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
root: {
title: 'Conditioning Event',
},
body: {
refCx: '50%',
refCy: '50%',
refRx: '50%',
refRy: '50%',
stroke: '#7c68fc',
fill: '#7c68fc',
fillOpacity: 0.2,
},
},
})
// export class ConditioningEvent extends Event {}
// ConditioningEvent.config({
// zIndex: 2,
// size: {
// width: 140,
// height: 80,
// },
// markup: [
// {
// tagName: 'ellipse',
// selector: 'body',
// },
// {
// tagName: 'text',
// selector: 'label',
// },
// ],
// attrs: {
// root: {
// title: 'Conditioning Event',
// },
// body: {
// refCx: '50%',
// refCy: '50%',
// refRx: '50%',
// refRy: '50%',
// stroke: '#7c68fc',
// fill: '#7c68fc',
// fillOpacity: 0.2,
// },
// },
// })
export class Connector extends Edge {
static create(event1: Event, event2: Event) {
return new Connector({
zIndex: 1,
source: {
cell: event1.id,
selector: event1 instanceof IntermediateEvent ? 'gate' : 'body',
},
target: {
cell: event2.id,
selector: 'body',
},
})
}
}
// export class Connector extends Edge {
// static create(event1: Event, event2: Event) {
// return new Connector({
// zIndex: 1,
// source: {
// cell: event1.id,
// selector: event1 instanceof IntermediateEvent ? 'gate' : 'body',
// },
// target: {
// cell: event2.id,
// selector: 'body',
// },
// })
// }
// }
Connector.config({
attrs: {
line: {
connection: true,
stroke: '#333333',
strokeWidth: 2,
strokeLinejoin: 'round',
},
},
markup: [
{
tagName: 'path',
selector: 'line',
attrs: {
fill: 'none',
'pointer-events': 'none',
},
},
],
})
// Connector.config({
// attrs: {
// line: {
// connection: true,
// stroke: '#333333',
// strokeWidth: 2,
// strokeLinejoin: 'round',
// },
// },
// markup: [
// {
// tagName: 'path',
// selector: 'line',
// attrs: {
// fill: 'none',
// 'pointer-events': 'none',
// },
// },
// ],
// })

@ -1,140 +1,140 @@
import React from 'react'
import { Node, Vector, Graph, Point, Line, Polyline } from '@antv/x6'
import { Connector } from '@antv/x6/es/registry/connector'
import '../index.less'
// import React from 'react'
// import { Node, Vector, Graph, Point, Line, Polyline } from '@antv/x6'
// import { Connector } from '@antv/x6/es/registry/connector'
// import '../index.less'
function random(max: number, min: number) {
return Math.floor(Math.random() * (max - min)) + min
}
// function random(max: number, min: number) {
// return Math.floor(Math.random() * (max - min)) + min
// }
export default class Example extends React.Component {
private container: HTMLDivElement
// export default class Example extends React.Component {
// private container: HTMLDivElement
componentDidMount() {
const graph = new Graph({
container: this.container,
width: 800,
height: 600,
grid: 1,
translating: {
restrict: {
x: 50,
y: 50,
width: 700,
height: 500,
},
},
})
// componentDidMount() {
// const graph = new Graph({
// container: this.container,
// width: 800,
// height: 600,
// grid: 1,
// translating: {
// restrict: {
// x: 50,
// y: 50,
// width: 700,
// height: 500,
// },
// },
// })
function createCircle(x: number, y: number, group: 'inner' | 'outer') {
const node = graph.addNode({
shape: 'circle',
size: { width: 20, height: 20 },
position: { x: x, y: y },
group: group,
attrs: {
body: {
strokeWidth: 3,
fill: group === 'inner' ? '#af9bff' : '#31d0c6',
stroke: group === 'inner' ? '#7c68fc' : '#009d93',
},
},
})
// function createCircle(x: number, y: number, group: 'inner' | 'outer') {
// const node = graph.addNode({
// shape: 'circle',
// size: { width: 20, height: 20 },
// position: { x: x, y: y },
// group: group,
// attrs: {
// body: {
// strokeWidth: 3,
// fill: group === 'inner' ? '#af9bff' : '#31d0c6',
// stroke: group === 'inner' ? '#7c68fc' : '#009d93',
// },
// },
// })
node.on('change:position', updateBoundaries)
}
// node.on('change:position', updateBoundaries)
// }
function createBoundary(color: string) {
var boundary = Vector.create('path').attr({
fill: color,
'fill-opacity': 0.2,
stroke: color,
'stroke-width': 3,
})
// function createBoundary(color: string) {
// var boundary = Vector.create('path').attr({
// fill: color,
// 'fill-opacity': 0.2,
// stroke: color,
// 'stroke-width': 3,
// })
Vector.create(graph.view.stage).prepend(boundary)
// Vector.create(graph.view.stage).prepend(boundary)
return boundary
}
// return boundary
// }
function updateBoundaries() {
var padding = 10
// function updateBoundaries() {
// var padding = 10
var innerPoints = getPointsByGroup('inner', padding)
var outerPoints = getPointsByGroup('outer', padding)
// var innerPoints = getPointsByGroup('inner', padding)
// var outerPoints = getPointsByGroup('outer', padding)
var innerHullPoints = convexHullAlgorithm(innerPoints)
var innerBoundaryPoints = getPaddedPoints(innerHullPoints, padding)
var outerHullPoints = convexHullAlgorithm(
outerPoints.concat(innerBoundaryPoints),
)
// var innerHullPoints = convexHullAlgorithm(innerPoints)
// var innerBoundaryPoints = getPaddedPoints(innerHullPoints, padding)
// var outerHullPoints = convexHullAlgorithm(
// outerPoints.concat(innerBoundaryPoints),
// )
innerBoundary.attr('d', createData(innerHullPoints))
outerBoundary.attr('d', createData(outerHullPoints))
}
// innerBoundary.attr('d', createData(innerHullPoints))
// outerBoundary.attr('d', createData(outerHullPoints))
// }
function getPointsByGroup(group: 'inner' | 'outer', padding: number) {
var node = graph.model.getNodes().filter((node) => {
return node.getProp('group') === group
})
// function getPointsByGroup(group: 'inner' | 'outer', padding: number) {
// var node = graph.model.getNodes().filter((node) => {
// return node.getProp('group') === group
// })
return node.reduce<Point[]>((memo, el) => {
return memo.concat(getNodeCornerPoints(el, padding))
}, [])
}
// return node.reduce<Point[]>((memo, el) => {
// return memo.concat(getNodeCornerPoints(el, padding))
// }, [])
// }
function getNodeCornerPoints(node: Node, padding: number = 0) {
var bbox = node.getBBox().inflate(padding)
return [bbox.origin, bbox.bottomLeft, bbox.corner, bbox.topRight]
}
// function getNodeCornerPoints(node: Node, padding: number = 0) {
// var bbox = node.getBBox().inflate(padding)
// return [bbox.origin, bbox.bottomLeft, bbox.corner, bbox.topRight]
// }
function getPaddedPoints(points: Point[], padding: number = 0) {
return points.reduce<Point[]>((memo, point) => {
memo.push(
point.clone().translate(padding, padding),
point.clone().translate(-padding, padding),
point.clone().translate(padding, -padding),
point.clone().translate(-padding, -padding),
)
return memo
}, [])
}
// function getPaddedPoints(points: Point[], padding: number = 0) {
// return points.reduce<Point[]>((memo, point) => {
// memo.push(
// point.clone().translate(padding, padding),
// point.clone().translate(-padding, padding),
// point.clone().translate(padding, -padding),
// point.clone().translate(-padding, -padding),
// )
// return memo
// }, [])
// }
function createData(points: Point[], radius?: number) {
var origin = new Line(points[0], points[points.length - 1]).getCenter()
return Connector.presets.rounded.call(this, origin, origin, points, {
radius: radius || 30,
})
}
// function createData(points: Point[], radius?: number) {
// var origin = new Line(points[0], points[points.length - 1]).getCenter()
// return Connector.presets.rounded.call(this, origin, origin, points, {
// radius: radius || 30,
// })
// }
function convexHullAlgorithm(points: Point[]) {
return new Polyline(points).toHull().points
}
// function convexHullAlgorithm(points: Point[]) {
// return new Polyline(points).toHull().points
// }
// bootstrap
// ---------
Array.from({ length: 10 }).forEach((_, i) => {
var x = random(100, 700)
var y = random(100, 500)
createCircle(x, y, i % 3 === 0 ? 'inner' : 'outer')
})
// // bootstrap
// // ---------
// Array.from({ length: 10 }).forEach((_, i) => {
// var x = random(100, 700)
// var y = random(100, 500)
// createCircle(x, y, i % 3 === 0 ? 'inner' : 'outer')
// })
// create boundaries around elements
var innerBoundary = createBoundary('#fe854f')
var outerBoundary = createBoundary('#feb663')
// // create boundaries around elements
// var innerBoundary = createBoundary('#fe854f')
// var outerBoundary = createBoundary('#feb663')
updateBoundaries()
}
// updateBoundaries()
// }
refContainer = (container: HTMLDivElement) => {
this.container = container
}
// refContainer = (container: HTMLDivElement) => {
// this.container = container
// }
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}
// render() {
// return (
// <div className="x6-graph-wrap">
// <div ref={this.refContainer} className="x6-graph" />
// </div>
// )
// }
// }

@ -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,166 +0,0 @@
import React from 'react'
import { Graph } from '@antv/x6'
import { Stencil } from '@antv/x6/es/addon/stencil'
import { Rect, Circle } from '@antv/x6/es/shape/basic'
import '../index.less'
export default class Example extends React.Component {
private container: HTMLDivElement
private stencilContainer: HTMLDivElement
componentDidMount() {
const graph = new Graph({
container: this.container,
width: 800,
height: 800,
snapline: {
enabled: true,
sharp: true,
},
grid: {
visible: true,
},
history: true,
scroller: {
enabled: true,
width: 600,
height: 400,
pageVisible: true,
pageBreak: false,
pannable: true,
},
})
graph.centerContent()
graph.addNode({
x: 130,
y: 30,
width: 100,
height: 40,
attrs: {
label: {
text: 'rect',
fill: '#6a6c8a',
},
body: {
stroke: '#31d0c6',
strokeWidth: 2,
},
},
})
const stencil = new Stencil({
target: graph,
width: 200,
height: 300,
search: true,
collapsable: true,
grid: 1,
groups: [
{
name: 'group1',
},
{
name: 'group2',
},
],
})
this.stencilContainer.appendChild(stencil.container)
var r = new Rect({
position: { x: 10, y: 10 },
size: { width: 70, height: 40 },
attrs: {
rect: { fill: '#31D0C6', stroke: '#4B4A67', 'stroke-width': 8 },
text: { text: 'rect', fill: 'white' },
},
})
var c = new Circle({
position: { x: 100, y: 10 },
size: { width: 70, height: 40 },
attrs: {
circle: { fill: '#FE854F', 'stroke-width': 8, stroke: '#4B4A67' },
text: { text: 'ellipse', fill: 'white' },
},
})
var c2 = new Circle({
position: { x: 10, y: 70 },
size: { width: 70, height: 40 },
attrs: {
circle: { fill: '#4B4A67', 'stroke-width': 8, stroke: '#FE854F' },
text: { text: 'ellipse', fill: 'white' },
},
})
var r2 = new Rect({
position: { x: 100, y: 70 },
size: { width: 70, height: 40 },
attrs: {
rect: { fill: '#4B4A67', stroke: '#31D0C6', 'stroke-width': 8 },
text: { text: 'rect', fill: 'white' },
},
})
var r3 = new Rect({
position: { x: 10, y: 130 },
size: { width: 70, height: 40 },
attrs: {
rect: { fill: '#31D0C6', stroke: '#4B4A67', 'stroke-width': 8 },
text: { text: 'rect', fill: 'white' },
},
})
var c3 = new Circle({
position: { x: 100, y: 130 },
size: { width: 70, height: 40 },
attrs: {
circle: { fill: '#FE854F', 'stroke-width': 8, stroke: '#4B4A67' },
text: { text: 'ellipse', fill: 'white' },
},
})
stencil.load([r, c, c2, r2.clone()], 'group1')
stencil.load([c2.clone(), r2, r3, c3], 'group2')
graph.once('node:added', ({ node }) => {
console.log(node)
setTimeout(() => {
graph.undo()
setTimeout(() => {
graph.redo()
}, 2000)
}, 2000)
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
refStencil = (container: HTMLDivElement) => {
this.stencilContainer = container
}
render() {
return (
<div className="x6-graph-wrap">
<h1>Default Settings</h1>
<div
ref={this.refStencil}
style={{
position: 'absolute',
left: 32,
top: 40,
width: 200,
height: 300,
}}
/>
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}

@ -1,179 +0,0 @@
import React from 'react'
import { Graph, Shape } 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: 400,
grid: true,
connecting: {
snap: true,
allowBlank: false,
allowMulti: true,
highlight: true,
router: { name: 'manhattan' },
connector: { name: 'normal' },
connectionPoint: 'boundary',
},
})
Shape.Rect.config({
width: 100,
height: 40,
ports: {
items: [
{ group: 'left', id: '4' },
{ group: 'right', id: '2' },
{ group: 'top', id: '1' },
{ group: 'bottom', id: '3' },
],
groups: {
left: {
position: { name: 'left' },
attrs: {
portBody: {
magnet: 'active',
r: 4,
// cy: -1,
fill: 'lightblue',
stroke: 'black',
opacity: 1,
},
},
z: 0,
},
right: {
position: { name: 'right' },
attrs: {
portBody: {
magnet: 'active',
r: 4,
// cy: -1,
fill: 'lightblue',
stroke: 'black',
opacity: 1,
},
},
z: 0,
},
top: {
position: { name: 'top' },
attrs: {
portBody: {
magnet: 'active',
r: 4,
// cy: -1,
fill: 'lightblue',
stroke: 'black',
opacity: 1,
},
},
z: 0,
},
bottom: {
position: { name: 'bottom' },
attrs: {
portBody: {
magnet: 'active',
r: 4,
// cy: -1,
fill: 'lightblue',
stroke: 'black',
opacity: 1,
},
},
z: 0,
},
},
},
portMarkup: [
{
tagName: 'circle',
selector: 'portBody',
},
],
})
graph.fromJSON({
edges: [
{
source: {
cell: 'start',
port: '2',
},
target: {
cell: 'create',
port: '2',
},
attrs: {
line: {
targetMarker: {
name: 'classic',
offset: 0,
},
},
},
},
{
source: {
cell: 'create',
port: '1',
},
target: {
cell: 'end',
port: '1',
},
attrs: {
line: {
targetMarker: {
name: 'classic',
offset: 0,
},
},
},
},
],
nodes: [
{
id: 'start',
label: 'start',
type: 'start',
x: 100,
y: 100,
},
{
id: 'create',
label: 'userTask',
type: 'userTask',
x: 300,
y: 300,
},
{
id: 'end',
label: 'end',
type: 'end',
x: 170,
y: 200,
},
],
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}

@ -1,256 +0,0 @@
import React from 'react'
import { Button } from 'antd'
import { Graph, Edge } from '@antv/x6'
import '../index.less'
export default class Example extends React.Component<
Example.Props,
Example.State
> {
state: Example.State = {
canRedo: false,
canUndo: false,
}
private container: HTMLDivElement
private graph: Graph
componentDidMount() {
const graph = (this.graph = new Graph({
container: this.container,
width: 800,
height: 600,
grid: true,
panning: true,
history: {
enabled: true,
beforeAddCommand(event, args: any) {
if (args.options.ignoreHistory) {
return false
}
},
},
connecting: {
allowBlank: false,
allowNode: false,
connector: {
name: 'smooth',
},
createEdge() {
return Edge.create({
attrs: {
line: {
strokeDasharray: '5 5',
},
},
})
},
},
}))
graph.history.on('change', () => {
this.setState({
canRedo: graph.canRedo(),
canUndo: graph.canUndo(),
})
})
graph.on('edge:connected', ({ edge }) => {
edge.attr('line/strokeDasharray', null, { ignoreHistory: true })
})
graph.addNode({
x: 100,
y: 80,
width: 160,
height: 80,
label: 'hello',
ports: {
groups: {
top: {
position: 'top',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
right: {
position: 'right',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
bottom: {
position: 'bottom',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
left: {
position: 'left',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
},
items: [
{
id: 'top',
group: 'top',
},
{
id: 'right',
group: 'right',
},
{
id: 'bottom',
group: 'bottom',
},
{
id: 'left',
group: 'left',
},
],
},
})
graph.addNode({
x: 400,
y: 320,
width: 160,
height: 80,
label: 'world',
ports: {
groups: {
top: {
position: 'top',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
right: {
position: 'right',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
bottom: {
position: 'bottom',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
left: {
position: 'left',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
},
items: [
{
id: 'top',
group: 'top',
},
{
id: 'right',
group: 'right',
},
{
id: 'bottom',
group: 'bottom',
},
{
id: 'left',
group: 'left',
},
],
},
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
onUndo = () => {
this.graph.undo()
}
onRedo = () => {
this.graph.redo()
}
render() {
return (
<div className="x6-graph-wrap">
<div className="x6-graph-tools">
<Button onClick={this.onUndo} disabled={!this.state.canUndo}>
Undo
</Button>
<Button onClick={this.onRedo} disabled={!this.state.canRedo}>
Redo
</Button>
</div>
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}
// eslint-disable-next-line
export namespace Example {
export interface Props {}
export interface State {
canUndo: boolean
canRedo: boolean
}
}

@ -1,185 +0,0 @@
import React from 'react'
import { Button } from 'antd'
import { Graph } from '@antv/x6'
import '../index.less'
export default class Example extends React.Component<
Example.Props,
Example.State
> {
state: Example.State = {
canRedo: false,
canUndo: false,
}
private graph: Graph
private graphContainer: HTMLDivElement
private minimapContainer: HTMLDivElement
componentDidMount() {
const graph = (this.graph = new Graph({
container: this.graphContainer,
width: 800,
height: 600,
grid: true,
history: {
enabled: true,
beforeAddCommand(event, options) {
// 鼠标移入和移除时触发 添加 和 删除 工具,不应该添加到历时记录中
if (
event === 'cell:change:*' &&
options != null &&
options.key === 'tools'
) {
return false
}
if (event === 'cell:removed') {
const cell = options && options.cell
if (cell) {
cell.removeTools()
}
}
},
},
scroller: {
enabled: true,
pannable: true,
},
minimap: {
enabled: true,
container: this.minimapContainer,
width: 300,
height: 200,
padding: 10,
graphOptions: {
async: true,
createCellView(cell) {
if (cell.isEdge()) {
return null
}
},
},
},
}))
graph.on('cell:mouseenter', function ({ cell }) {
if (cell.isNode()) {
cell.addTools([
{
name: 'boundary',
args: {
attrs: {
fill: '#7c68fc',
stroke: '#333',
'stroke-width': 1,
'fill-opacity': 0.2,
},
},
},
{
name: 'button-remove',
args: {
x: 0,
y: 0,
offset: { x: 10, y: 10 },
},
},
])
} else {
cell.addTools(['vertices', 'segments'])
}
})
graph.on('cell:mouseleave', ({ cell }) => {
cell.removeTools()
})
graph.history.on('change', () => {
this.setState({
canRedo: graph.canRedo(),
canUndo: graph.canUndo(),
})
})
graph.on('node:click', function ({ node }) {
node.attr('body/stroke', 'orange')
})
const rect1 = graph.addNode({
x: 100,
y: 80,
width: 100,
height: 40,
label: 'hello',
})
const rect2 = graph.addNode({
x: 400,
y: 320,
width: 100,
height: 40,
label: 'world',
})
graph.addEdge({
source: rect1,
target: rect2,
})
}
refContainer = (container: HTMLDivElement) => {
this.graphContainer = container
}
refMinimap = (container: HTMLDivElement) => {
this.minimapContainer = container
}
onUndo = () => {
this.graph.undo()
}
onRedo = () => {
this.graph.redo()
}
render() {
return (
<div className="x6-graph-wrap">
<h1>Scroller</h1>
<div className="x6-graph-tools">
<Button onClick={this.onUndo} disabled={!this.state.canUndo}>
Undo
</Button>
<Button onClick={this.onRedo} disabled={!this.state.canRedo}>
Redo
</Button>
</div>
<div
ref={this.refMinimap}
style={{
position: 'absolute',
right: '50%',
top: 40,
marginRight: -720,
width: 300,
height: 200,
boxShadow: '0 0 10px 1px #e9e9e9',
}}
/>
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}
// eslint-disable-next-line
export namespace Example {
export interface Props {}
export interface State {
canUndo: boolean
canRedo: boolean
}
}

@ -1,161 +0,0 @@
import React from 'react'
import ReactDom from 'react-dom'
import { Dropdown, Menu } from 'antd'
import { Graph, ToolsView, EdgeView } from '@antv/x6'
import '../index.less'
class ContextMenuTool extends ToolsView.ToolItem<
EdgeView,
ContextMenuToolOptions
> {
private knob: HTMLDivElement
render() {
super.render()
this.knob = ToolsView.createElement('div', false) as HTMLDivElement
this.knob.style.position = 'absolute'
this.container.appendChild(this.knob)
this.updatePosition(this.options)
setTimeout(() => {
this.toggleContextMenu(true)
})
return this
}
private toggleContextMenu(visible: boolean) {
ReactDom.unmountComponentAtNode(this.knob)
document.removeEventListener('mousedown', this.onMouseDown)
if (visible) {
ReactDom.render(
<Dropdown
visible={true}
trigger={['click']}
overlay={this.options.menu}
>
<a href="#" />
</Dropdown>,
this.knob,
() => {
document.addEventListener('mousedown', this.onMouseDown)
},
)
}
}
private updatePosition(pos?: { x: number; y: number }) {
const style = this.knob.style
if (pos) {
style.left = `${pos.x}px`
style.top = `${pos.y}px`
} else {
style.left = '-1000px'
style.top = '-1000px'
}
}
private onMouseDown = (e: MouseEvent) => {
console.log('')
setTimeout(() => {
this.updatePosition()
this.toggleContextMenu(false)
if (this.options.onHide) {
this.options.onHide.call(this)
}
}, 100)
}
}
ContextMenuTool.config({
tagName: 'div',
isSVGElement: false,
})
export interface ContextMenuToolOptions extends ToolsView.ToolItem.Options {
x: number
y: number
menu?: Menu | (() => Menu)
onHide?: (this: ContextMenuTool) => void
}
Graph.registerEdgeTool('contextmenu', ContextMenuTool, true)
Graph.registerNodeTool('contextmenu', ContextMenuTool, true)
const onMenuClick = (e: any) => {
console.log('menu click ', e)
}
const menu = () => (
<Menu onClick={onMenuClick}>
<Menu.Item key="1">1st menu item</Menu.Item>
<Menu.Item key="2">2nd menu item</Menu.Item>
<Menu.Item key="3">
<a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">
3rd menu item
</a>
</Menu.Item>
<Menu.Item key="4">a danger item</Menu.Item>
</Menu>
)
export default class Example extends React.Component {
private container: HTMLDivElement
componentDidMount() {
const graph = new Graph({
container: this.container,
width: 800,
height: 600,
grid: true,
panning: true,
})
const source = graph.addNode({
x: 40,
y: 40,
width: 100,
height: 40,
})
const target = graph.addNode({
x: 240,
y: 200,
width: 100,
height: 40,
})
graph.addEdge({
source,
target,
})
graph.on('cell:contextmenu', ({ cell, e }) => {
const p = graph.clientToGraph(e.clientX, e.clientY)
cell.addTools([
{
name: 'contextmenu',
args: {
menu,
x: p.x,
y: p.y,
onHide() {
this.cell.removeTools()
},
},
},
])
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}

@ -1,185 +0,0 @@
import React from 'react'
import { Button } from 'antd'
import { Graph, NodeView, DataUri } from '@antv/x6'
import '../index.less'
class SimpleNodeView extends NodeView {
protected renderMarkup() {
return this.renderJSONMarkup({
tagName: 'rect',
selector: 'body',
})
}
protected renderPorts() {}
update() {
super.update({
body: {
refWidth: '100%',
refHeight: '100%',
fill: '#31d0c6',
},
})
}
}
export default class Example extends React.Component {
private graph: Graph
private graphContainer: HTMLDivElement
private minimapContainer: HTMLDivElement
private scroller: any
componentDidMount() {
const graph = new Graph({
container: this.graphContainer,
width: 800,
height: 500,
resizing: true,
background: {
color: '#f5f5f5',
},
grid: {
visible: true,
},
selecting: {
enabled: true,
rubberband: true,
modifiers: 'shift',
},
scroller: {
enabled: true,
// width: 600,
// height: 400,
pageVisible: true,
pageBreak: true,
pannable: true,
autoResize: true,
// modifiers: 'shift',
},
minimap: {
enabled: true,
container: this.minimapContainer,
width: 300,
height: 200,
padding: 10,
graphOptions: {
async: true,
getCellView(cell) {
if (cell.isNode()) {
return SimpleNodeView
}
},
createCellView(cell) {
if (cell.isEdge()) {
return null
}
},
},
},
mousewheel: {
enabled: true,
// fixed: false,
modifiers: ['ctrl', 'meta'],
minScale: 0.5,
maxScale: 2,
},
})
this.scroller = graph.scroller.widget
const rect = graph.addNode({
x: 40,
y: 40,
width: 90,
height: 60,
})
rect.on('removed', () => {
console.log('rect was removed')
})
const circle = graph.addNode({
shape: 'circle',
x: 160,
y: 160,
width: 40,
height: 40,
})
graph.addEdge({
source: rect,
target: circle,
})
this.graph = graph
}
refContainer = (container: HTMLDivElement) => {
this.graphContainer = container
}
refMinimap = (container: HTMLDivElement) => {
this.minimapContainer = container
}
onResizeClick = () => {
this.graph.resizeGraph(200, 150)
}
onCenterClick = () => {
this.graph.center()
}
onCenterContentClick = () => {
this.graph.centerContent()
}
onZoomOutClick = () => {
this.scroller.zoom(-0.2)
}
onZoomInClick = () => {
this.scroller.zoom(0.2)
}
onZoomToFitClick = () => {
this.scroller.zoomToFit()
}
onDownload = () => {
this.graph.toPNG((datauri: string) => {
DataUri.downloadDataUri(datauri, 'chart.png')
})
}
render() {
return (
<div className="x6-graph-wrap">
<h1>Scroller</h1>
<div className="x6-graph-tools">
<Button onClick={this.onResizeClick}>Resize</Button>
<Button onClick={this.onCenterClick}>Center</Button>
<Button onClick={this.onCenterContentClick}>Center Content</Button>
<Button onClick={this.onZoomOutClick}>Zoom Out</Button>
<Button onClick={this.onZoomInClick}>Zoom In</Button>
<Button onClick={this.onZoomToFitClick}>Zoom To Fit</Button>
<Button onClick={this.onDownload}>Download</Button>
</div>
<div
ref={this.refMinimap}
style={{
position: 'absolute',
right: '50%',
top: 40,
marginRight: -720,
width: 300,
height: 200,
boxShadow: '0 0 10px 1px #e9e9e9',
}}
/>
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}

@ -1,100 +0,0 @@
import React from 'react'
import { Graph } from '@antv/x6'
import '../index.less'
Graph.registerNode(
'new-rect',
{
inherit: 'rect',
width: 160,
height: 40,
attrs: {
label: {
textAnchor: 'left',
refX: 0,
textWrap: {
width: 160,
height: 48,
ellipsis: true,
},
},
},
},
true,
)
const data = {
nodes: [
{
shape: 'new-rect',
id: 'node1',
x: 40,
y: 40,
attrs: {
label: {
text: 'hello-test-foo-bar-count-lint-jsvascriptjsvascriptjsvascript',
},
},
},
{
shape: 'new-rect',
id: 'node2',
x: 220,
y: 40,
attrs: {
label: {
text: 'testing测试测试测试thisisaveryveryveryveryveryverylongword jsvascriptjsvascriptjsvascript',
},
},
},
{
shape: 'new-rect',
id: 'node3',
x: 40,
y: 100,
attrs: {
label: {
text: '文字文字文字测试测试测试测试',
},
},
},
{
shape: 'new-rect',
id: 'node4',
x: 220,
y: 100,
attrs: {
label: {
text: 'asdfghjklasdfghjklasdfghjkl',
},
},
},
],
}
export default class Example extends React.Component {
private container: HTMLDivElement
componentDidMount() {
const graph = new Graph({
container: this.container,
width: 1000,
height: 800,
grid: true,
})
graph.fromJSON(data)
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}

@ -1,95 +0,0 @@
import React from 'react'
import { Button } from 'antd'
import { Graph } from '@antv/x6'
import '../index.less'
export default class Example extends React.Component<
Example.Props,
Example.State
> {
private container: HTMLDivElement
private history: Graph.HistoryManager
state: Example.State = {
canRedo: false,
canUndo: false,
}
componentDidMount() {
const graph = new Graph({
container: this.container,
width: 800,
height: 600,
grid: true,
history: true,
})
const parent = graph.addNode({
x: 100,
y: 60,
width: 500,
height: 140,
label: 'Parent Node',
})
this.history = graph.history
this.history.on('change', () => {
this.setState({
canRedo: this.history.canRedo(),
canUndo: this.history.canUndo(),
})
})
graph.batchUpdate(() => {
const child = graph.addNode({
x: 160,
y: 100,
width: 100,
height: 40,
label: 'Drag Me',
})
parent.addChild(child)
})
}
onUndo = () => {
this.history.undo()
}
onRedo = () => {
this.history.redo()
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="x6-graph-wrap">
<h1>Default Settings</h1>
<div className="x6-graph-tools">
<Button.Group>
<Button onClick={this.onUndo} disabled={!this.state.canUndo}>
Undo
</Button>
<Button onClick={this.onRedo} disabled={!this.state.canRedo}>
Redo
</Button>
</Button.Group>
</div>
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}
// eslint-disable-next-line
export namespace Example {
export interface Props {}
export interface State {
canUndo: boolean
canRedo: boolean
}
}

@ -1,81 +0,0 @@
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: 400,
grid: true,
})
graph.addEdge({
source: { x: 300, y: 240 },
target: { x: 460, y: 240 },
attrs: {
line: {
strokeWidth: 8,
// stroke: '#13C2C1',
stroke: {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#13C2C1' },
{ offset: '100%', color: '#3366FF' },
],
},
fill: 'none',
targetMarker: {
name: 'block',
size: 15,
fill: '#6ED7D8',
offset: 0,
},
},
},
tools: ['target-arrowhead'],
})
graph.addEdge({
source: { x: 240, y: 40 },
target: { x: 40, y: 160 },
connector: 'smooth',
attrs: {
line: {
strokeWidth: 8,
// stroke: '#13C2C1',
stroke: {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#13C2C1' },
{ offset: '100%', color: '#3366FF' },
],
},
fill: 'none',
targetMarker: {
name: 'block',
size: 15,
fill: '#6ED7D8',
offset: 0,
},
},
},
tools: ['target-arrowhead'],
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}

@ -1,58 +0,0 @@
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: 400,
grid: true,
})
graph.addEdge({
source: { x: 100, y: 100 },
target: { x: 400, y: 100 },
vertices: [
[160, 160],
[340, 160],
],
attrs: {
line: {
strokeWidth: 8,
// stroke: '#13C2C1',
stroke: {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#13C2C1' },
{ offset: '100%', color: '#3366FF' },
],
},
fill: 'none',
targetMarker: {
name: 'block',
size: 15,
fill: '#6ED7D8',
offset: 0,
},
},
},
tools: ['button-remove', 'vertices'],
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}

@ -1,61 +0,0 @@
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,
rotating: true,
resizing: {
enabled: true,
restricted: true,
},
translating: {
restrict: -20,
},
scroller: {
enabled: true,
pannable: true,
autoResize: false,
pageVisible: true,
autoResizeOptions: {
border: 20,
},
},
})
graph.addNode({
x: 100,
y: 80,
width: 80,
height: 40,
angle: 30,
})
graph.addNode({
shape: 'ellipse',
x: 240,
y: 200,
width: 80,
height: 40,
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}

@ -1,249 +0,0 @@
import React from 'react'
import { Graph, Edge } 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: 1000,
height: 800,
grid: true,
panning: true,
connecting: {
allowBlank: false,
allowNode: false,
// sourceAnchor: 'midSide',
// targetAnchor: 'midSide',
router: {
name: 'er',
},
connector: {
name: 'rounded',
args: {
radius: 50,
},
},
createEdge({ sourceMagnet }) {
const map = {
top: 'B',
bottom: 'T',
left: 'R',
right: 'L',
}
const group = sourceMagnet.getAttribute(
'port-group',
) as keyof typeof map
return Edge.create({
router: {
name: 'er',
args: {
direction: group ? map[group] : undefined,
},
},
attrs: {
line: {
strokeDasharray: '5 5',
},
},
})
},
},
})
graph.on('edge:connected', ({ edge }) => {
graph.batchUpdate(() => {
const { port: sourcePort, ...source } =
edge.getSource() as Edge.TerminalCellData
const { port: targetPort, ...target } =
edge.getTarget() as Edge.TerminalCellData
edge.removeProp('source')
edge.removeProp('target')
edge.removeProp('router')
edge.attr('line/strokeDasharray', null)
edge.prop({
source,
target,
})
})
})
const rect1 = graph.addNode({
x: 100,
y: 80,
width: 268,
height: 100,
label: 'hello',
ports: {
groups: {
top: {
position: 'top',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
right: {
position: 'right',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
bottom: {
position: 'bottom',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
left: {
position: 'left',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
},
items: [
{
id: 'top',
group: 'top',
},
{
id: 'right',
group: 'right',
},
{
id: 'bottom',
group: 'bottom',
},
{
id: 'left',
group: 'left',
},
],
},
})
const rect2 = graph.addNode({
x: 400,
y: 320,
width: 268,
height: 100,
label: 'world',
ports: {
groups: {
top: {
position: 'top',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
right: {
position: 'right',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
bottom: {
position: 'bottom',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
left: {
position: 'left',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
},
items: [
{
id: 'top',
group: 'top',
},
{
id: 'right',
group: 'right',
},
{
id: 'bottom',
group: 'bottom',
},
{
id: 'left',
group: 'left',
},
],
},
})
graph.addEdge({
source: rect1,
target: rect2,
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}

@ -1,5 +1,7 @@
import React from 'react'
import { Graph, Node, NodeView, Point, Angle, Dom, Vector } from '@antv/x6'
import { Graph, Node, NodeView } from '@antv/x6'
import { Dom, Vector } from '@antv/x6-common'
import { Point, Angle } from '@antv/x6-geometry'
import '../index.less'
class ConveyorNode extends Node {

@ -1,6 +1,7 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { Graph, Dom, Shape, Node } from '@antv/x6'
import { Graph, Shape, Node } from '@antv/x6'
import { Dom } from '@antv/x6-common'
import { Tooltip } from 'antd'
import classnames from 'classnames'
import '@antv/x6-react-shape'

@ -1,5 +1,6 @@
import React from 'react'
import { Graph, Color } from '@antv/x6'
import { Graph } from '@antv/x6'
import { Color } from '@antv/x6-common'
import '@antv/x6-react-shape'
import '../index.less'
import './ports.less'

@ -1,5 +1,7 @@
import React from 'react'
import { Graph } from '@antv/x6'
import { Keyboard } from '@antv/x6-plugin-keyboard'
import { Selection } from '@antv/x6-plugin-selection'
import '../index.less'
export default class Example extends React.Component {
@ -11,30 +13,25 @@ export default class Example extends React.Component {
width: 800,
height: 600,
grid: true,
keyboard: {
enabled: true,
global: true,
},
// resizing: {
// enabled: true,
// },
// rotating: {
// enabled: true,
// },
selecting: {
enabled: true,
rubberband: true,
multiple: true,
strict: true,
showNodeSelectionBox: true,
selectCellOnMoved: false,
useCellGeometry: true,
filter(cell) {
return cell !== a
},
// content: '123',
})
const keyboard = new Keyboard({
enabled: true,
})
const selection = new Selection({
enabled: true,
rubberband: true,
multiple: true,
strict: true,
showNodeSelectionBox: true,
selectCellOnMoved: false,
useCellGeometry: true,
filter(cell) {
return cell !== a
},
})
graph.use(keyboard)
graph.use(selection)
const a = graph.addNode({
x: 50,
@ -64,75 +61,12 @@ export default class Example extends React.Component {
graph.addEdge({ source: a, target: b })
graph.addEdge({ source: b, target: c })
// graph.toggleMultipleSelection(false)
// console.log(graph.isMultipleSelection())
// graph.on('node:selected', ({ node }) => {
// console.log(node)
// })
// graph.on('node:unselected', ({ node }) => {
// console.log(node)
// })
// graph.on('selection:changed', ({ selected, added, removed }) => {
// console.log(selected, added, removed)
// })
// graph.on('cell:selected', ({ cell }) => {
// console.log('selected', cell)
// })
// graph.on('cell:unselected', ({ cell }) => {
// console.log('unselected', cell)
// })
// graph.on('node:change:position', ({ node, options }) => {
// console.log(node, options)
// })
graph.bindKey('backspace', () => {
graph.removeCells(graph.getSelectedCells())
keyboard.bindKey('backspace', () => {
graph.removeCells(selection.getSelectedCells())
})
// graph.on('blank:mousedown', () => {
// console.log('blank:mousedown')
// })
// graph.on('blank:click', () => {
// console.log('blank:click')
// })
// graph.on('node:mousedown', args => {
// console.log('node:mousedown', args)
// })
// graph.on('node:mousemove', args => {
// console.log('node:mousemove', args)
// })
// graph.on('node:mouseup', args => {
// console.log('node:mouseup', args)
// })
// graph.on('node:click', args => {
// console.log('node:click', args)
// })
// graph.on('node:dblclick', args => {
// console.log('node:dblclick', args)
// })
// graph.on('node:contextmenu', args => {
// console.log('node:contextmenu', args)
// })
graph.select(a)
graph.select([b, c])
graph.on('cell:removed', (args) => {
console.log('cell:removed', args)
})
selection.select(a)
selection.select([b, c])
}
refContainer = (container: HTMLDivElement) => {

@ -1,95 +0,0 @@
import React from 'react'
import { Graph, Markup, ObjectExt, Dom } from '@antv/x6'
import '../index.less'
Graph.registerNode('xml-node', {}, true)
const { markup, attrs } = Markup.xml2json(`
<g class="rotatable">
<g class="scalable">
<rect class="card" ref-width="100%" ref-height="100%" rx="10" ry="10" fill="#fff" stroke="#000" stroke-width="0" pointer-events="visiblePainted"/>
<image x="16" y="16" width="56" height="56" opacity="0.7"/>
</g>
<text class="rank" ref-x="0.95" ref-y="0.5" font-family="Courier New" font-size="13" text-anchor="end" text-vertical-anchor="middle"/>
<text class="name" ref-x="0.95" ref-y="0.7" font-family="Arial" font-size="14" font-weight="600" text-anchor="end"/>
<g class="btn add" ref-dx="-16" ref-y="16" event="node:add">
<circle class="add" r="10" fill="transparent" stroke="#333" stroke-width="1"/>
<text class="add" x="-5.5" y="7" font-size="20" font-weight="600" font-family="Times New Roman" stroke="#000">+</text>
</g>
<g class="btn del" ref-dx="-44" ref-y="16" event="node:delete">
<circle class="del" r="10" fill="transparent" stroke="#333" stroke-width="1"/>
<text class="del" x="-4.5" y="6" font-size="28" font-weight="600" font-family="Times New Roman" stroke="#000">-</text>
</g>
</g>
`)
const male =
'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*kUy8SrEDp6YAAAAAAAAAAAAAARQnAQ'
const female =
'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*f6hhT75YjkIAAAAAAAAAAAAAARQnAQ'
export default class Example extends React.Component {
private container: HTMLDivElement
componentDidMount() {
const graph = new Graph({
container: this.container,
width: 800,
height: 600,
grid: true,
panning: true,
})
console.log(markup, attrs)
graph.addNode({
x: 100,
y: 100,
width: 260,
height: 88,
shape: 'xml-node',
markup,
attrs: ObjectExt.merge(attrs, {
'.card': {
fill: '#31d0c6',
},
image: {
xlinkHref: Math.random() < 0.5 ? male : female,
},
'.rank': {
text: Dom.breakText('President, Ebay Global Marketplaces', {
width: 160,
height: 45,
}),
},
'.name': {
text: Dom.breakText('Devin Wenig', { width: 160, height: 45 }),
},
}),
})
graph.on('node:add', () => {
alert('add click')
})
graph.on('node:delete', () => {
alert('delete click')
})
graph.on('node:click', () => {
alert('node click')
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="x6-graph-wrap">
<div ref={this.refContainer} className="x6-graph" />
</div>
)
}
}

@ -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.7",
"version": "2.0.6-beta.22",
"npmClient": "yarn",
"useWorkspaces": true,
"command": {
@ -25,11 +25,5 @@
"pr(test)": ":white_check_mark: Test Case"
}
},
"packages": [
"packages/x6-common",
"packages/x6-geometry",
"packages/x6",
"packages/x6-react-shape",
"packages/x6-vue-shape"
]
"packages": ["packages/*"]
}

@ -26,9 +26,8 @@
"package:inherit": "yarn package-inherit update",
"prepare": "is-ci || husky install configs/husky-config",
"precommit": "yarn lint-staged && lerna run --concurrency 1 --stream precommit",
"version": "lerna version --no-private",
"publish:latest": "lerna publish from-package --no-private --ignore-scripts",
"publish:beta": "lerna publish from-package --no-private --ignore-scripts --canary --preid beta --dist-tag=beta"
"publish:latest": "yarn build:dev && lerna publish --no-private --ignore-scripts",
"publish:beta": "yarn build:dev && lerna publish --no-private --ignore-scripts --dist-tag=beta"
},
"lint-staged": {
"**/*.{js,jsx,tsx,ts,less,md,json}": [

@ -1 +0,0 @@
export * from './src'

@ -1,6 +1,6 @@
{
"name": "@antv/x6-common",
"version": "2.0.6-beta.3",
"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', () => {

@ -154,9 +154,14 @@ export function contains(parent: Element, child: Element) {
)
}
export function remove(elem: Element) {
if (elem.parentNode) {
elem.parentNode.removeChild(elem)
export function remove(elem: Element | Element[] | null) {
if (elem) {
const elems = Array.isArray(elem) ? elem : [elem]
elems.forEach((item) => {
if (item.parentNode) {
item.parentNode.removeChild(item)
}
})
}
}
@ -239,3 +244,18 @@ export function isHTMLElement(elem: any): elem is HTMLElement {
)
}
}
export function children(parent: Element, className?: string) {
const matched: Element[] = []
let elem = parent.firstChild
for (; elem; elem = elem.nextSibling) {
if (elem.nodeType === 1) {
if (!className || hasClass(elem as Element, className)) {
matched.push(elem as Element)
}
}
}
return matched
}

@ -19,3 +19,6 @@ export * from './transform'
// event
export * from './event'
export * from './mousewheel'
// postion
export * from './position'

@ -0,0 +1,19 @@
export function offset(elem: Element) {
const rect = elem.getBoundingClientRect()
const win = elem.ownerDocument.defaultView!
return {
top: rect.top + win.pageYOffset,
left: rect.left + win.pageXOffset,
}
}
export function width(elem: Element) {
const rect = elem.getBoundingClientRect()
return rect.width
}
export function height(elem: Element) {
const rect = elem.getBoundingClientRect()
return rect.height
}

@ -1 +0,0 @@
export * from './src'

@ -1,5 +1,5 @@
{
"version": "2.0.6-beta.2",
"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.7",
"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",

@ -3,31 +3,33 @@ import { Graph } from '@antv/x6'
import { KeyboardImpl } from './keyboard'
export class Keyboard extends Disposable {
private keyboardImpl: KeyboardImpl
public name = 'keyboard'
private keyboard: KeyboardImpl
constructor(public readonly options: KeyboardImpl.Options) {
super()
}
public init(graph: Graph) {
this.keyboard = new KeyboardImpl({
init(graph: Graph) {
this.keyboardImpl = new KeyboardImpl({
...this.options,
graph,
})
}
// #region api
isKeyboardEnabled() {
return !this.keyboard.disabled
return !this.keyboardImpl.disabled
}
enableKeyboard() {
this.keyboard.enable()
this.keyboardImpl.enable()
return this
}
disableKeyboard() {
this.keyboard.disable()
this.keyboardImpl.disable()
return this
}
@ -53,17 +55,19 @@ export class Keyboard extends Disposable {
callback: KeyboardImpl.Handler,
action?: KeyboardImpl.Action,
) {
this.keyboard.on(keys, callback, action)
this.keyboardImpl.on(keys, callback, action)
return this
}
unbindKey(keys: string | string[], action?: KeyboardImpl.Action) {
this.keyboard.off(keys, action)
this.keyboardImpl.off(keys, action)
return this
}
// #endregion
@Disposable.dispose()
dispose() {
this.keyboard.dispose()
this.keyboardImpl.dispose()
}
}

@ -103,7 +103,7 @@ export class KeyboardImpl extends Disposable implements IDisablable {
}
protected isGraphEvent(e: KeyboardEvent) {
const target = (e.srcElement || e.target) as Element
const target = e.target as Element
const currentTarget = e.currentTarget as Element
if (target) {
if (

@ -1 +0,0 @@
export * from './src'

@ -1,6 +1,6 @@
{
"name": "@antv/x6-plugin-scroller",
"version": "2.0.6-beta.7",
"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",

@ -11,11 +11,11 @@ import { ScrollerImpl } from './scroller'
import { content } from './style/raw'
export class Scroller extends Disposable {
public name = 'scroller'
private graph: Graph
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,18 @@ 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,
graph,
@ -42,92 +47,16 @@ export class Scroller extends Disposable {
this.scrollerImpl.center()
}
private startListening() {
let eventTypes = []
const pannable = this.options.pannable
if (typeof pannable === 'object') {
eventTypes = pannable.eventTypes || []
} else {
eventTypes = ['leftMouseDown']
}
if (eventTypes.includes('leftMouseDown')) {
this.graph.on('blank:mousedown', this.preparePanning, this)
this.graph.on('node:unhandled:mousedown', this.preparePanning, this)
this.graph.on('edge:unhandled:mousedown', this.preparePanning, this)
}
if (eventTypes.includes('rightMouseDown')) {
this.onRightMouseDown = this.onRightMouseDown.bind(this)
Dom.Event.on(
this.scrollerImpl.container,
'mousedown',
this.onRightMouseDown,
)
}
}
private stopListening() {
let eventTypes = []
const pannable = this.options.pannable
if (typeof pannable === 'object') {
eventTypes = pannable.eventTypes || []
} else {
eventTypes = ['leftMouseDown']
}
if (eventTypes.includes('leftMouseDown')) {
this.graph.off('blank:mousedown', this.preparePanning, this)
this.graph.off('node:unhandled:mousedown', this.preparePanning, this)
this.graph.off('edge:unhandled:mousedown', this.preparePanning, this)
}
if (eventTypes.includes('rightMouseDown')) {
Dom.Event.off(
this.scrollerImpl.container,
'mousedown',
this.onRightMouseDown,
)
}
}
private onRightMouseDown(e: Dom.MouseDownEvent) {
if (e.button === 2 && this.allowPanning(e, true)) {
this.updateClassName(true)
this.scrollerImpl.startPanning(e)
this.scrollerImpl.once('pan:stop', () => this.updateClassName(false))
}
}
private 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) {
this.updateClassName(true)
this.scrollerImpl.startPanning(e)
this.scrollerImpl.once('pan:stop', () => this.updateClassName(false))
}
}
private allowPanning(e: Dom.MouseDownEvent, strict?: boolean) {
return (
this.pannable && ModifierKey.isMatch(e, this.options.modifiers, strict)
)
}
private updateClassName(isPanning?: boolean) {
const container = this.scrollerImpl.container!
const pannable = Config.prefix('graph-scroller-pannable')
if (this.pannable) {
Dom.addClass(container, pannable)
container.dataset.panning = (!!isPanning).toString() // Use dataset to control scroller panning style to avoid reflow caused by changing classList
} else {
Dom.removeClass(container, pannable)
}
}
// #region api
resize(width?: number, height?: number) {
this.scrollerImpl.resize(width, height)
}
resizePage(width?: number, height?: number) {
this.scrollerImpl.updatePageSize(width, height)
}
zoom(): number
zoom(factor: number, options?: TransformManager.ZoomOptions): this
zoom(factor?: number, options?: TransformManager.ZoomOptions) {
@ -354,11 +283,98 @@ export class Scroller extends Disposable {
this.scrollerImpl.disableAutoResize()
}
autoScroll(clientX: number, clientY: number) {
return this.scrollerImpl.autoScroll(clientX, clientY)
}
// #endregion
protected startListening() {
let eventTypes = []
const pannable = this.options.pannable
if (typeof pannable === 'object') {
eventTypes = pannable.eventTypes || []
} else {
eventTypes = ['leftMouseDown']
}
if (eventTypes.includes('leftMouseDown')) {
this.graph.on('blank:mousedown', this.preparePanning, this)
this.graph.on('node:unhandled:mousedown', this.preparePanning, this)
this.graph.on('edge:unhandled:mousedown', this.preparePanning, this)
}
if (eventTypes.includes('rightMouseDown')) {
this.onRightMouseDown = this.onRightMouseDown.bind(this)
Dom.Event.on(
this.scrollerImpl.container,
'mousedown',
this.onRightMouseDown,
)
}
}
protected stopListening() {
let eventTypes = []
const pannable = this.options.pannable
if (typeof pannable === 'object') {
eventTypes = pannable.eventTypes || []
} else {
eventTypes = ['leftMouseDown']
}
if (eventTypes.includes('leftMouseDown')) {
this.graph.off('blank:mousedown', this.preparePanning, this)
this.graph.off('node:unhandled:mousedown', this.preparePanning, this)
this.graph.off('edge:unhandled:mousedown', this.preparePanning, this)
}
if (eventTypes.includes('rightMouseDown')) {
Dom.Event.off(
this.scrollerImpl.container,
'mousedown',
this.onRightMouseDown,
)
}
}
protected onRightMouseDown(e: Dom.MouseDownEvent) {
if (e.button === 2 && this.allowPanning(e, true)) {
this.updateClassName(true)
this.scrollerImpl.startPanning(e)
this.scrollerImpl.once('pan:stop', () => this.updateClassName(false))
}
}
protected preparePanning({ e }: { e: Dom.MouseDownEvent }) {
const allowPanning = this.allowPanning(e, true)
const selection = this.graph.getPlugin('selection') as any
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))
}
}
protected allowPanning(e: Dom.MouseDownEvent, strict?: boolean) {
return (
this.pannable && ModifierKey.isMatch(e, this.options.modifiers, strict)
)
}
protected updateClassName(isPanning?: boolean) {
const container = this.scrollerImpl.container!
const pannable = Config.prefix('graph-scroller-pannable')
if (this.pannable) {
Dom.addClass(container, pannable)
container.dataset.panning = (!!isPanning).toString() // Use dataset to control scroller panning style to avoid reflow caused by changing classList
} else {
Dom.removeClass(container, pannable)
}
}
@Disposable.dispose()
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,
) {

@ -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.

@ -0,0 +1,5 @@
# `x6-plugin-selection`
> TODO: description
## Usage

@ -0,0 +1,112 @@
{
"name": "@antv/x6-plugin-selection",
"version": "2.0.6-beta.22",
"description": "selection plugin for X6.",
"main": "lib/index.js",
"module": "es/index.js",
"unpkg": "dist/x6-plugin-selection.js",
"jsdelivr": "dist/x6-plugin-selection.js",
"types": "lib/index.d.ts",
"files": [
"dist",
"es",
"lib"
],
"keywords": [
"plugin",
"selection",
"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-selection"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
}
}

@ -0,0 +1,18 @@
import config from '../../configs/rollup-config'
export default config({
output: [
{
name: 'X6PluginSelection',
format: 'umd',
file: 'dist/x6-plugin-selection.js',
sourcemap: true,
globals: {
'@antv/x6': 'X6',
'@antv/x6-common': 'X6Common',
'@antv/x6-geometry': 'X6Geometry',
},
},
],
external: ['@antv/x6', '@antv/x6-common', '@antv/x6-geometry'],
})

@ -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, 'selection.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, 'selection.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()

@ -0,0 +1,78 @@
@selection-prefix-cls: ~'x6-widget-selection';
.@{selection-prefix-cls} {
position: absolute;
display: none;
width: 0;
height: 0;
touch-action: none;
&-rubberband {
display: block;
overflow: visible;
opacity: 0.3;
}
&-selected {
display: block;
}
&-box {
cursor: move;
}
&-inner[data-selection-length='0'],
&-inner[data-selection-length='1'] {
display: none;
}
&-content {
position: absolute;
top: 100%;
right: -20px;
left: -20px;
margin-top: 30px;
padding: 6px;
line-height: 14px;
text-align: center;
border-radius: 6px;
&:empty {
display: none;
}
}
}
// theme
.@{selection-prefix-cls} {
&-rubberband {
background-color: #3498db;
border: 2px solid #2980b9;
}
&-box {
box-sizing: content-box !important;
margin-top: -4px;
margin-left: -4px;
padding-right: 4px;
padding-bottom: 4px;
border: 2px dashed #feb663;
box-shadow: 2px 2px 5px #d3d3d3;
}
&-inner {
box-sizing: content-box !important;
margin-top: -8px;
margin-left: -8px;
padding-right: 12px;
padding-bottom: 12px;
border: 2px solid #feb663;
box-shadow: 2px 2px 5px #d3d3d3;
}
&-content {
color: #fff;
font-size: 10px;
background-color: #6a6b8a;
}
}

@ -0,0 +1,488 @@
import {
Disposable,
ModifierKey,
CssLoader,
Dom,
ObjectExt,
} from '@antv/x6-common'
import { Cell, EventArgs, Graph } from '@antv/x6'
import { SelectionImpl } from './selection'
import { content } from './style/raw'
export class Selection extends Disposable {
private graph: Graph
private selectionImpl: SelectionImpl
private readonly options: Selection.Options
private movedMap = new WeakMap<Cell, boolean>()
private unselectMap = new WeakMap<Cell, boolean>()
public name = 'selection'
private get rubberbandDisabled() {
return this.options.enabled !== true || this.options.rubberband !== true
}
public get disabled() {
return this.options.enabled !== true
}
public get length() {
return this.selectionImpl.length
}
public get cells() {
return this.selectionImpl.cells
}
constructor(options: Selection.Options) {
super()
this.options = ObjectExt.merge({}, Selection.defaultOptions, options)
}
public init(graph: Graph) {
this.graph = graph
CssLoader.ensure(this.name, content)
this.selectionImpl = new SelectionImpl({
...this.options,
graph,
})
this.startListening()
}
// #region api
isSelectionEnabled() {
return !this.disabled
}
enableSelection() {
this.enable()
return this
}
disableSelection() {
this.disable()
return this
}
toggleSelection(enabled?: boolean) {
if (enabled != null) {
if (enabled !== this.isSelectionEnabled()) {
if (enabled) {
this.enableSelection()
} else {
this.disableSelection()
}
}
} else if (this.isSelectionEnabled()) {
this.disableSelection()
} else {
this.enableSelection()
}
return this
}
isMultipleSelection() {
return this.isMultiple()
}
enableMultipleSelection() {
this.enableMultiple()
return this
}
disableMultipleSelection() {
this.disableMultiple()
return this
}
toggleMultipleSelection(multiple?: boolean) {
if (multiple != null) {
if (multiple !== this.isMultipleSelection()) {
if (multiple) {
this.enableMultipleSelection()
} else {
this.disableMultipleSelection()
}
}
} else if (this.isMultipleSelection()) {
this.disableMultipleSelection()
} else {
this.enableMultipleSelection()
}
return this
}
isSelectionMovable() {
return this.options.movable !== false
}
enableSelectionMovable() {
this.selectionImpl.options.movable = true
return this
}
disableSelectionMovable() {
this.selectionImpl.options.movable = false
return this
}
toggleSelectionMovable(movable?: boolean) {
if (movable != null) {
if (movable !== this.isSelectionMovable()) {
if (movable) {
this.enableSelectionMovable()
} else {
this.disableSelectionMovable()
}
}
} else if (this.isSelectionMovable()) {
this.disableSelectionMovable()
} else {
this.enableSelectionMovable()
}
return this
}
isRubberbandEnabled() {
return !this.rubberbandDisabled
}
enableRubberband() {
if (this.rubberbandDisabled) {
this.options.rubberband = true
}
return this
}
disableRubberband() {
if (!this.rubberbandDisabled) {
this.options.rubberband = false
}
return this
}
toggleRubberband(enabled?: boolean) {
if (enabled != null) {
if (enabled !== this.isRubberbandEnabled()) {
if (enabled) {
this.enableRubberband()
} else {
this.disableRubberband()
}
}
} else if (this.isRubberbandEnabled()) {
this.disableRubberband()
} else {
this.enableRubberband()
}
return this
}
isStrictRubberband() {
return this.selectionImpl.options.strict === true
}
enableStrictRubberband() {
this.selectionImpl.options.strict = true
return this
}
disableStrictRubberband() {
this.selectionImpl.options.strict = false
return this
}
toggleStrictRubberband(strict?: boolean) {
if (strict != null) {
if (strict !== this.isStrictRubberband()) {
if (strict) {
this.enableStrictRubberband()
} else {
this.disableStrictRubberband()
}
}
} else if (this.isStrictRubberband()) {
this.disableStrictRubberband()
} else {
this.enableStrictRubberband()
}
return this
}
setRubberbandModifiers(modifiers?: string | ModifierKey[] | null) {
this.setModifiers(modifiers)
}
setSelectionFilter(filter?: Selection.Filter) {
this.setFilter(filter)
return this
}
setSelectionDisplayContent(content?: Selection.Content) {
this.setContent(content)
return this
}
isSelectionEmpty() {
return this.isEmpty()
}
cleanSelection(options?: Selection.SetOptions) {
this.clean(options)
return this
}
resetSelection(
cells?: Cell | string | (Cell | string)[],
options?: Selection.SetOptions,
) {
this.reset(cells, options)
return this
}
getSelectedCells() {
return this.cells
}
getSelectedCellCount() {
return this.length
}
isSelected(cell: Cell | string) {
return this.selectionImpl.isSelected(cell)
}
select(
cells: Cell | string | (Cell | string)[],
options: Selection.AddOptions = {},
) {
const selected = this.getCells(cells)
if (selected.length) {
if (this.isMultiple()) {
this.selectionImpl.select(selected, options)
} else {
this.reset(selected.slice(0, 1), options)
}
}
return this
}
unselect(
cells: Cell | string | (Cell | string)[],
options: Selection.RemoveOptions = {},
) {
this.selectionImpl.unselect(this.getCells(cells), options)
return this
}
// #endregion
protected startListening() {
this.graph.on('blank:mousedown', this.onBlankMouseDown, this)
this.graph.on('blank:click', this.onBlankClick, this)
this.graph.on('cell:mousemove', this.onCellMouseMove, this)
this.graph.on('cell:mouseup', this.onCellMouseUp, this)
this.selectionImpl.on('box:mousedown', this.onBoxMouseDown, this)
}
protected stopListening() {
this.graph.off('blank:mousedown', this.onBlankMouseDown, this)
this.graph.off('blank:click', this.onBlankClick, this)
this.graph.off('cell:mousemove', this.onCellMouseMove, this)
this.graph.off('cell:mouseup', this.onCellMouseUp, this)
this.selectionImpl.off('box:mousedown', this.onBoxMouseDown, this)
}
protected onBlankMouseDown({ e }: EventArgs['blank:mousedown']) {
const allowGraphPanning = this.graph.panning.allowPanning(e, true)
const scroller = this.graph.getPlugin('scroller') as any
const allowScrollerPanning = scroller && scroller.allowPanning(e, true)
if (
this.allowRubberband(e, true) ||
(this.allowRubberband(e) && !allowScrollerPanning && !allowGraphPanning)
) {
this.startRubberband(e)
}
}
protected onBlankClick() {
this.clean()
}
protected allowRubberband(e: Dom.MouseDownEvent, strict?: boolean) {
return (
!this.rubberbandDisabled &&
ModifierKey.isMatch(e, this.options.modifiers, strict)
)
}
protected allowMultipleSelection(e: Dom.MouseDownEvent | Dom.MouseUpEvent) {
return (
this.isMultiple() &&
ModifierKey.isMatch(e, this.options.multipleSelectionModifiers)
)
}
protected onCellMouseMove({ cell }: EventArgs['cell:mousemove']) {
this.movedMap.set(cell, true)
}
protected onCellMouseUp({ e, cell }: EventArgs['cell:mouseup']) {
const options = this.options
let disabled = this.disabled
if (!disabled && this.movedMap.has(cell)) {
disabled = options.selectCellOnMoved === false
if (!disabled) {
disabled = options.selectNodeOnMoved === false && cell.isNode()
}
if (!disabled) {
disabled = options.selectEdgeOnMoved === false && cell.isEdge()
}
}
if (!disabled) {
if (!this.allowMultipleSelection(e)) {
this.reset(cell)
} else if (this.unselectMap.has(cell)) {
this.unselectMap.delete(cell)
} else if (this.isSelected(cell)) {
this.unselect(cell)
} else {
this.select(cell)
}
}
this.movedMap.delete(cell)
}
protected onBoxMouseDown({
e,
cell,
}: SelectionImpl.EventArgs['box:mousedown']) {
if (!this.disabled) {
if (this.allowMultipleSelection(e)) {
this.unselect(cell)
this.unselectMap.set(cell, true)
}
}
}
protected isEmpty() {
return this.length <= 0
}
protected getCells(cells: Cell | string | (Cell | string)[]) {
return (Array.isArray(cells) ? cells : [cells])
.map((cell) =>
typeof cell === 'string' ? this.graph.getCellById(cell) : cell,
)
.filter((cell) => cell != null)
}
protected reset(
cells?: Cell | string | (Cell | string)[],
options: Selection.SetOptions = {},
) {
this.selectionImpl.reset(cells ? this.getCells(cells) : [], options)
return this
}
protected clean(options: Selection.SetOptions = {}) {
this.selectionImpl.clean(options)
return this
}
protected enable() {
if (this.disabled) {
this.options.enabled = true
}
return this
}
protected disable() {
if (!this.disabled) {
this.options.enabled = false
}
return this
}
protected startRubberband(e: Dom.MouseDownEvent) {
if (!this.rubberbandDisabled) {
this.selectionImpl.startSelecting(e)
}
return this
}
protected isMultiple() {
return this.options.multiple !== false
}
protected enableMultiple() {
this.options.multiple = true
return this
}
protected disableMultiple() {
this.options.multiple = false
return this
}
protected setModifiers(modifiers?: string | ModifierKey[] | null) {
this.options.modifiers = modifiers
return this
}
protected setContent(content?: Selection.Content) {
this.selectionImpl.setContent(content)
return this
}
protected setFilter(filter?: Selection.Filter) {
this.selectionImpl.setFilter(filter)
return this
}
@Disposable.dispose()
dispose() {
this.stopListening()
this.selectionImpl.dispose()
CssLoader.clean(this.name)
}
}
export namespace Selection {
export interface Options extends SelectionImpl.CommonOptions {
enabled?: boolean
}
export type Filter = SelectionImpl.Filter
export type Content = SelectionImpl.Content
export type SetOptions = SelectionImpl.SetOptions
export type AddOptions = SelectionImpl.AddOptions
export type RemoveOptions = SelectionImpl.RemoveOptions
export const defaultOptions: Partial<SelectionImpl.Options> = {
rubberband: false,
rubberNode: true,
rubberEdge: false, // next version will set to true
pointerEvents: 'auto',
multiple: true,
multipleSelectionModifiers: ['ctrl', 'meta'],
movable: true,
strict: false,
useCellGeometry: false,
selectCellOnMoved: false,
selectNodeOnMoved: false,
selectEdgeOnMoved: false,
following: true,
content: null,
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,70 @@
/* eslint-disable */
/**
* Auto generated file, do not modify it!
*/
export const content = `.x6-widget-selection {
position: absolute;
display: none;
width: 0;
height: 0;
touch-action: none;
}
.x6-widget-selection-rubberband {
display: block;
overflow: visible;
opacity: 0.3;
}
.x6-widget-selection-selected {
display: block;
}
.x6-widget-selection-box {
cursor: move;
}
.x6-widget-selection-inner[data-selection-length='0'],
.x6-widget-selection-inner[data-selection-length='1'] {
display: none;
}
.x6-widget-selection-content {
position: absolute;
top: 100%;
right: -20px;
left: -20px;
margin-top: 30px;
padding: 6px;
line-height: 14px;
text-align: center;
border-radius: 6px;
}
.x6-widget-selection-content:empty {
display: none;
}
.x6-widget-selection-rubberband {
background-color: #3498db;
border: 2px solid #2980b9;
}
.x6-widget-selection-box {
box-sizing: content-box !important;
margin-top: -4px;
margin-left: -4px;
padding-right: 4px;
padding-bottom: 4px;
border: 2px dashed #feb663;
box-shadow: 2px 2px 5px #d3d3d3;
}
.x6-widget-selection-inner {
box-sizing: content-box !important;
margin-top: -8px;
margin-left: -8px;
padding-right: 12px;
padding-bottom: 12px;
border: 2px solid #feb663;
box-shadow: 2px 2px 5px #d3d3d3;
}
.x6-widget-selection-content {
color: #fff;
font-size: 10px;
background-color: #6a6b8a;
}
`

@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.json"
}

@ -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.

Some files were not shown because too many files have changed in this diff Show More