Compare commits
10 Commits
5338a4c021
...
2d3a4631c8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d3a4631c8 | ||
|
|
ae173f5b92 | ||
|
|
20523ce0aa | ||
|
|
56c2052f40 | ||
|
|
6408d3377b | ||
|
|
bcb76e197e | ||
|
|
6ffdb79001 | ||
|
|
fc16a1840f | ||
|
|
4b107e772c | ||
|
|
85a03f7c1d |
11
buildoz.css
11
buildoz.css
@@ -206,7 +206,9 @@ bz-graflow .bzgf-main-container{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* BZGRAFLOW_CORE_START */
|
/* BZGRAFLOW_CORE_START */
|
||||||
/* bz-graflow internal layout rules (used in light DOM, and injected into shadow DOM when isolated) */
|
/* Keep this commented section !
|
||||||
|
bz-graflow internal layout rules (used in light DOM, and injected into shadow DOM when isolated)
|
||||||
|
*/
|
||||||
bz-graflow .bzgf-wires-container,
|
bz-graflow .bzgf-wires-container,
|
||||||
bz-graflow .bzgf-nodes-container{
|
bz-graflow .bzgf-nodes-container{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -214,6 +216,13 @@ bz-graflow .bzgf-nodes-container{
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
bz-graflow .bzgf-nodes-container{ /* used to keep the nodes container pointer-events: none, but allow the nodes to be moved ! */
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
bz-graflow .bzgf-nodes-container > * { /* allow the nodes to be moved ! */
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
bz-graflow .bzgf-nodes-container .bzgf-node{ position:absolute; }
|
bz-graflow .bzgf-nodes-container .bzgf-node{ position:absolute; }
|
||||||
bz-graflow .bzgf-nodes-container .bzgf-fake-node{
|
bz-graflow .bzgf-nodes-container .bzgf-fake-node{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
319
bzGraflow.js
319
bzGraflow.js
@@ -85,9 +85,21 @@ class BZgraflow extends Buildoz{
|
|||||||
this.mainContainer.append(this.nodesContainer)
|
this.mainContainer.append(this.nodesContainer)
|
||||||
this.append(this.hostContainer)
|
this.append(this.hostContainer)
|
||||||
this.loadFlow(flowUrl).then(() => {
|
this.loadFlow(flowUrl).then(() => {
|
||||||
if((this.getBZAttribute('edit')=='move') || (this.getBZAttribute('edit')=='full')){
|
if(this.getBZAttribute('edit')){
|
||||||
this.dnd = new MovingNodes(this)
|
const edit = this.getBZAttribute('edit').split(',')
|
||||||
this.dnd.enableMovingNodes('.bzgf-node')
|
if(edit.includes('nodesmove')){
|
||||||
|
this.nodesMover = new MovingNodes(this)
|
||||||
|
this.nodesMover.enableMovingNodes('.bzgf-node')
|
||||||
|
}
|
||||||
|
if(edit.includes('wires')){
|
||||||
|
this.WiresEditor = new EditWires(this)
|
||||||
|
this.WiresEditor.enableEditWires()
|
||||||
|
//this.WiresEditor.enableMovingNodes('.bzgf-wire')
|
||||||
|
}
|
||||||
|
if(edit.includes('dropnodes')){
|
||||||
|
this.NodesReceiver = new DroppingNodes(this)
|
||||||
|
//this.NodesReceiver.enableDroppingNodes('.bzgf-node')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -225,7 +237,7 @@ class BZgraflow extends Buildoz{
|
|||||||
btnExitSubflow.addEventListener('click', () => {
|
btnExitSubflow.addEventListener('click', () => {
|
||||||
this.exitSubflow(childEl)
|
this.exitSubflow(childEl)
|
||||||
})
|
})
|
||||||
// Put the child in the exact same viewport rect as the parent (fixed overlay)
|
// Put the child in the exact same viewport rect as the parent
|
||||||
this.invade(this, childEl)
|
this.invade(this, childEl)
|
||||||
childEl.hostContainer.appendChild(btnExitSubflow)
|
childEl.hostContainer.appendChild(btnExitSubflow)
|
||||||
|
|
||||||
@@ -266,9 +278,9 @@ class BZgraflow extends Buildoz{
|
|||||||
const ty0 = (nodeBB.top - parentBB.top) + (this.scrollTop || 0)
|
const ty0 = (nodeBB.top - parentBB.top) + (this.scrollTop || 0)
|
||||||
|
|
||||||
// Inline "scaler" (shadow styles don't apply to the child element)
|
// Inline "scaler" (shadow styles don't apply to the child element)
|
||||||
|
childEl.style.border = 'none'
|
||||||
childEl.style.transformOrigin = 'top left'
|
childEl.style.transformOrigin = 'top left'
|
||||||
childEl.style.willChange = 'transform'
|
childEl.style.willChange = 'transform'
|
||||||
childEl.style.transition = 'transform 1000ms ease-in-out'
|
|
||||||
childEl.style.transform = 'translate(var(--tx, 0px), var(--ty, 0px)) scale(var(--sx, 1), var(--sy, 1))'
|
childEl.style.transform = 'translate(var(--tx, 0px), var(--ty, 0px)) scale(var(--sx, 1), var(--sy, 1))'
|
||||||
childEl.style.setProperty('--tx', tx0 + 'px')
|
childEl.style.setProperty('--tx', tx0 + 'px')
|
||||||
childEl.style.setProperty('--ty', ty0 + 'px')
|
childEl.style.setProperty('--ty', ty0 + 'px')
|
||||||
@@ -277,7 +289,10 @@ class BZgraflow extends Buildoz{
|
|||||||
|
|
||||||
// Force style flush, then animate back to identity (full parent size)
|
// Force style flush, then animate back to identity (full parent size)
|
||||||
childEl.getBoundingClientRect()
|
childEl.getBoundingClientRect()
|
||||||
|
childEl.style.transition = 'transform 1000ms ease-in-out'
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
|
childEl.style.top = 0;
|
||||||
|
childEl.style.left = 0;
|
||||||
childEl.style.setProperty('--tx', '0px')
|
childEl.style.setProperty('--tx', '0px')
|
||||||
childEl.style.setProperty('--ty', '0px')
|
childEl.style.setProperty('--ty', '0px')
|
||||||
childEl.style.setProperty('--sx', 1)
|
childEl.style.setProperty('--sx', 1)
|
||||||
@@ -290,6 +305,7 @@ class BZgraflow extends Buildoz{
|
|||||||
this.hostContainer.style.visibility = 'hidden'
|
this.hostContainer.style.visibility = 'hidden'
|
||||||
childEl.style.transform = 'none' // Important for nested subflows to position correctly
|
childEl.style.transform = 'none' // Important for nested subflows to position correctly
|
||||||
childEl.style.willChange = ''
|
childEl.style.willChange = ''
|
||||||
|
newEl.style.overflow = 'auto'
|
||||||
this.dispatchEvent(new CustomEvent('subflowLoaded', {
|
this.dispatchEvent(new CustomEvent('subflowLoaded', {
|
||||||
detail: { subflow: childEl },
|
detail: { subflow: childEl },
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
@@ -299,16 +315,14 @@ class BZgraflow extends Buildoz{
|
|||||||
}
|
}
|
||||||
|
|
||||||
invade(oldEl, newEl){
|
invade(oldEl, newEl){
|
||||||
// Scroll-proof overlay: position inside oldEl so it follows oldEl scrolling.
|
|
||||||
// Ensure oldEl is a positioning context.
|
|
||||||
const pos = getComputedStyle(oldEl).position
|
|
||||||
if(pos === 'static') oldEl.style.position = 'relative'
|
|
||||||
newEl.style.position = 'absolute'
|
newEl.style.position = 'absolute'
|
||||||
newEl.style.inset = '0'
|
const bbox = oldEl.getBoundingClientRect()
|
||||||
// Override bz-graflow's default width/height (100vw/50vh) when used as an embedded overlay
|
newEl.style.left = `${bbox.left+bbox.width/2}px`
|
||||||
newEl.style.width = '100%'
|
newEl.style.top = `${bbox.top+bbox.height/2}px`
|
||||||
newEl.style.height = '100%'
|
newEl.style.width = `${bbox.width}px`
|
||||||
|
newEl.style.height = `${bbox.height}px`
|
||||||
newEl.style.display = 'block'
|
newEl.style.display = 'block'
|
||||||
|
newEl.style.overflow = 'hidden'
|
||||||
oldEl.appendChild(newEl)
|
oldEl.appendChild(newEl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,6 +473,60 @@ class BZgraflow extends Buildoz{
|
|||||||
return({ x: x - r.left, y: y - r.top })
|
return({ x: x - r.left, y: y - r.top })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildSegment(x1, y1, c1x, c1y, c2x, c2y, x2, y2, wireType, node1, node2, dir1, dir2, tension, loop=false){
|
||||||
|
if(loop) wireType = 'bezier' // loops only use bezier to look good
|
||||||
|
|
||||||
|
const startAxis = ['n', 's'].includes(dir1) ? 'v' : 'h'
|
||||||
|
if(wireType == 'bezier'){
|
||||||
|
return(`C ${c1x} ${c1y}, ${c2x} ${c2y}, ${x2} ${y2}`)
|
||||||
|
}
|
||||||
|
if(wireType == 'straight'){
|
||||||
|
return(`L ${c1x} ${c1y} L ${c2x} ${c2y} L ${x2} ${y2}`)
|
||||||
|
}
|
||||||
|
if(wireType == 'ortho'){
|
||||||
|
const medianx = (x1 + x2) / 2
|
||||||
|
const mediany = (y1 + y2) / 2
|
||||||
|
if(startAxis == 'v') {
|
||||||
|
if( ((dir1 == 's') && (c1y < mediany)) || ((dir1 == 'n') && (c1y > mediany)) ){
|
||||||
|
if( (dir2=='e') && (c2x > x1) || (dir2=='w') && (c2x < x1)) return(`V ${mediany} H ${c2x} V ${y2} H ${x2}`)
|
||||||
|
else if((dir2=='e') || (dir2=='w')) return(`V ${y2} H ${x2}`)
|
||||||
|
else if(dir2 == dir1) { // walk-around node
|
||||||
|
const deviation = node2.offsetWidth / 2
|
||||||
|
if(x1>x2) {
|
||||||
|
if(x1>x2+deviation+tension) return(`V ${c2y} H ${x2} V ${y2}`)
|
||||||
|
else return(`V ${c1y} H ${x1+deviation+tension} V ${c2y} H ${x2} V ${y2}`)
|
||||||
|
} else {
|
||||||
|
if(x1<x2-deviation-tension) return(`V ${c2y} H ${x2} V ${y2}`)
|
||||||
|
else return(`V ${c1y} H ${x1-deviation-tension} V ${c2y} H ${x2} V ${y2}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else return(`V ${mediany} H ${c2x} V ${y2} H ${x2}`)
|
||||||
|
} else {
|
||||||
|
return(`V ${c1y} H ${medianx} V ${c2y} H ${x2} V ${y2}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if( ((dir1 == 'e') && (c1x <medianx)) || ((dir1 == 'w') && (c1x > medianx)) ){
|
||||||
|
if( (dir2=='s') && (c2y > y1) || (dir2=='n') && (c2y < y1)) return(`H ${medianx} V ${c2y} H ${x2} V ${y2}`)
|
||||||
|
else if((dir2=='n') || (dir2=='s')) return(`H ${x2} V ${y2}`)
|
||||||
|
else if(dir2 == dir1) { // walk-around node
|
||||||
|
const deviation = node2.offsetHeight / 2
|
||||||
|
if(y1>y2) {
|
||||||
|
if(y1>y2+deviation+tension) return(`H ${c2x} V ${y2} H ${x2}`)
|
||||||
|
else return(`H ${c1x} V ${y1+deviation+tension} H ${c2x} V ${y2} H ${x2}`)
|
||||||
|
} else {
|
||||||
|
if(y1<y2-deviation-tension) return(`H ${c2x} V ${y2} H ${x2}`)
|
||||||
|
else return(`H ${c1x} V ${y1-deviation-tension} H ${c2x} V ${y2} H ${x2}`)
|
||||||
|
}
|
||||||
|
} else return(`H ${medianx} V ${c2y} H ${x2} V ${y2}`)
|
||||||
|
} else {
|
||||||
|
return(`H ${c1x} V ${mediany} H ${c2x} V ${y2} H ${x2}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return('')
|
||||||
|
}
|
||||||
|
|
||||||
linkNodes(idNode1, idPort1, idNode2, idPort2) {
|
linkNodes(idNode1, idPort1, idNode2, idPort2) {
|
||||||
const tension = parseInt(this.getBZAttribute('tension')) || 60
|
const tension = parseInt(this.getBZAttribute('tension')) || 60
|
||||||
const wireType = this.getBZAttribute('wiretype') || 'bezier'
|
const wireType = this.getBZAttribute('wiretype') || 'bezier'
|
||||||
@@ -494,24 +562,11 @@ class BZgraflow extends Buildoz{
|
|||||||
c2y -= 1*tension
|
c2y -= 1*tension
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const seg = this.buildSegment(x1, y1, c1x, c1y, c2x, c2y, x2, y2, wireType, node1, node2, port1.direction, port2.direction, tension, loop)
|
||||||
if(wireType === 'bezier') {
|
if(!seg) return('')
|
||||||
return(`M ${x1} ${y1} C ${c1x} ${c1y}, ${c2x} ${c2y}, ${x2} ${y2}`)
|
return(`M ${x1} ${y1} ${seg}`)
|
||||||
}
|
|
||||||
if(wireType === 'straight') {
|
|
||||||
return(`M ${x1} ${y1} L ${c1x} ${c1y} L ${c2x} ${c2y} L ${x2} ${y2}`)
|
|
||||||
}
|
|
||||||
if(wireType === 'ortho') {
|
|
||||||
let path = `M ${x1} ${y1} `
|
|
||||||
if(['n', 's'].includes(port1.direction)) {
|
|
||||||
path += `V ${(c1y+c2y)/2} H ${c2x} V ${y2}`
|
|
||||||
} else {
|
|
||||||
path += `H ${(c1x+c2x)/2} V ${c2y} H ${x2}`
|
|
||||||
}
|
|
||||||
return(path)
|
|
||||||
}
|
|
||||||
return('')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
linkInterNodes(idNode1, idPort1, idNode2, idPort2, interNodes, orientation='horizontal') {
|
linkInterNodes(idNode1, idPort1, idNode2, idPort2, interNodes, orientation='horizontal') {
|
||||||
const tension = parseInt(this.getBZAttribute('tension')) || 60
|
const tension = parseInt(this.getBZAttribute('tension')) || 60
|
||||||
const wireType = this.getBZAttribute('wiretype') || 'bezier'
|
const wireType = this.getBZAttribute('wiretype') || 'bezier'
|
||||||
@@ -524,38 +579,12 @@ class BZgraflow extends Buildoz{
|
|||||||
return('')
|
return('')
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeSegment = (x1, y1, x2, y2, orientation1, orientation2) => {
|
const makeSegment = (x1, y1, x2, y2, node1, node2, dir1, dir2, tension) => {
|
||||||
let c1x, c1y, c2x, c2y
|
const c1x = Math.floor(x1 + (this.dirVect[dir1].x * tension))
|
||||||
if(orientation1=='horizontal') {
|
const c1y = Math.floor(y1 + (this.dirVect[dir1].y * tension))
|
||||||
c1x = Math.floor(x1 + tension)
|
const c2x = Math.floor(x2 + (this.dirVect[dir2].x * tension))
|
||||||
c1y = y1
|
const c2y = Math.floor(y2 + (this.dirVect[dir2].y * tension))
|
||||||
} else {
|
return(this.buildSegment(x1, y1, c1x, c1y, c2x, c2y, x2, y2, wireType, node1, node2, dir1, dir2, tension, false))
|
||||||
c1x = x1
|
|
||||||
c1y = Math.floor(y1 + tension)
|
|
||||||
}
|
|
||||||
if(orientation2=='horizontal') {
|
|
||||||
c2x = Math.floor(x2 - tension)
|
|
||||||
c2y = y2
|
|
||||||
} else {
|
|
||||||
c2x = x2
|
|
||||||
c2y = Math.floor(y2 - tension)
|
|
||||||
}
|
|
||||||
if(wireType === 'bezier') {
|
|
||||||
return(`C ${c1x} ${c1y}, ${c2x} ${c2y}, ${x2} ${y2}`)
|
|
||||||
}
|
|
||||||
if(wireType === 'straight') {
|
|
||||||
return(`L ${c1x} ${c1y} L ${c2x} ${c2y} L ${x2} ${y2}`)
|
|
||||||
}
|
|
||||||
if(wireType === 'ortho') {
|
|
||||||
let path = `M ${x1} ${y1} `
|
|
||||||
if(['n', 's'].includes(port1.direction)) {
|
|
||||||
path += `V ${(c1y+c2y)/2} H ${c2x} V ${y2}`
|
|
||||||
} else {
|
|
||||||
path += `H ${(c1x+c2x)/2} V ${c2y} H ${x2}`
|
|
||||||
}
|
|
||||||
return(path)
|
|
||||||
}
|
|
||||||
return(``)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start/end points in SVG coords (works for both bezier and line)
|
// Start/end points in SVG coords (works for both bezier and line)
|
||||||
@@ -569,6 +598,8 @@ class BZgraflow extends Buildoz{
|
|||||||
const yEnd = Math.floor(ep.y)
|
const yEnd = Math.floor(ep.y)
|
||||||
let path = `M ${x1} ${y1} `
|
let path = `M ${x1} ${y1} `
|
||||||
|
|
||||||
|
const entryDir = (orientation == 'horizontal') ? 'w' : 'n'
|
||||||
|
const exitDir = (orientation == 'horizontal') ? 'e' : 's'
|
||||||
let firstPort = port1
|
let firstPort = port1
|
||||||
for(const interNode of interNodes){
|
for(const interNode of interNodes){
|
||||||
const bb = this.stagedNodes[interNode].getBoundingClientRect()
|
const bb = this.stagedNodes[interNode].getBoundingClientRect()
|
||||||
@@ -587,10 +618,10 @@ class BZgraflow extends Buildoz{
|
|||||||
let y2 = Math.floor(entry.y)
|
let y2 = Math.floor(entry.y)
|
||||||
|
|
||||||
if(firstPort){
|
if(firstPort){
|
||||||
path += makeSegment(x1, y1, x2, y2, ['w','e'].includes(firstPort.direction) ? 'horizontal' : 'vertical', orientation)
|
path += makeSegment(x1, y1, x2, y2, node1, node2, firstPort.direction, entryDir, tension)
|
||||||
firstPort = null
|
firstPort = null
|
||||||
} else {
|
} else {
|
||||||
path += makeSegment(x1, y1, x2, y2, orientation, orientation)
|
path += makeSegment(x1, y1, x2, y2, node1, node2, exitDir, entryDir, tension)
|
||||||
}
|
}
|
||||||
|
|
||||||
const x3 = Math.floor(exit.x)
|
const x3 = Math.floor(exit.x)
|
||||||
@@ -600,7 +631,7 @@ class BZgraflow extends Buildoz{
|
|||||||
x1 = x3
|
x1 = x3
|
||||||
y1 = y3
|
y1 = y3
|
||||||
}
|
}
|
||||||
path += ' ' + makeSegment(x1, y1, xEnd, yEnd, orientation, orientation)
|
path += ' ' + makeSegment(x1, y1, xEnd, yEnd, node1, node2, exitDir, port2.direction, tension)
|
||||||
return(path)
|
return(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,8 +641,6 @@ class BZgraflow extends Buildoz{
|
|||||||
if(tween == null) tween = parseInt(this.getBZAttribute('tween')) || 500
|
if(tween == null) tween = parseInt(this.getBZAttribute('tween')) || 500
|
||||||
if(align == null) align = this.getBZAttribute('align') || 'center'
|
if(align == null) align = this.getBZAttribute('align') || 'center'
|
||||||
|
|
||||||
console.log('autoPlace', orientation, gapx, gapy, tween, align)
|
|
||||||
|
|
||||||
this.currentOrientation = orientation
|
this.currentOrientation = orientation
|
||||||
// Cancel any previous autoPlace() animations by bumping a token.
|
// Cancel any previous autoPlace() animations by bumping a token.
|
||||||
// moveNode() checks this token each frame and will no-op if superseded.
|
// moveNode() checks this token each frame and will no-op if superseded.
|
||||||
@@ -711,44 +740,97 @@ class BZgraflow extends Buildoz{
|
|||||||
// Finally place everything
|
// Finally place everything
|
||||||
if(orientation=='horizontal'){
|
if(orientation=='horizontal'){
|
||||||
const fakeNodeHeight = 10
|
const fakeNodeHeight = 10
|
||||||
|
const parentsY = {}
|
||||||
|
const nodeY = {}
|
||||||
|
const nodeX = {}
|
||||||
let x = gapx
|
let x = gapx
|
||||||
for(const [idx, layer] of layers.entries()){
|
for(const [idx, layer] of layers.entries()){
|
||||||
let wMax = this.getMaxWidth(layer)
|
let wMax = this.getMaxWidth(layer)
|
||||||
let y = 0
|
let y = 0
|
||||||
switch(align){
|
switch(align){
|
||||||
case'center':
|
case 'center':
|
||||||
y = ((maxHeight - layerHeights[idx]) / 2) + gapy
|
y = ((maxHeight - layerHeights[idx]) / 2) + gapy
|
||||||
break
|
break
|
||||||
case'first':
|
case 'first':
|
||||||
y = gapy
|
y = gapy
|
||||||
break
|
break
|
||||||
case'last':
|
case 'last':
|
||||||
y = maxHeight - layerHeights[idx] + gapy
|
y = maxHeight - layerHeights[idx] + gapy
|
||||||
break
|
break
|
||||||
case 'auto':
|
case 'parent': // y will be absolutely positioned by the parent(s) but have fallback for 1st layer
|
||||||
//TODO
|
|
||||||
y = ((maxHeight - layerHeights[idx]) / 2) + gapy
|
y = ((maxHeight - layerHeights[idx]) / 2) + gapy
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
for(const nid of layer){
|
for(const nid of layer){
|
||||||
|
let placedY
|
||||||
if(!nid.startsWith('longLinkPlaceHolder_')) {
|
if(!nid.startsWith('longLinkPlaceHolder_')) {
|
||||||
const bb = this.stagedNodes[nid].getBoundingClientRect()
|
const bb = this.stagedNodes[nid].getBoundingClientRect()
|
||||||
this.moveNode(nid, x, y, orientation, tween, null, token)
|
const nodeHeight = this.stagedNodes[nid].offsetHeight || bb.height
|
||||||
y += gapy + (this.stagedNodes[nid].offsetHeight || bb.height)
|
if((align == 'parent') && (nid in parents) && (parents[nid][0] in parentsY)) {
|
||||||
|
y = Math.max(parentsY[parents[nid][0]], y) //TODO handle multiple parents with avg
|
||||||
|
console.log('parent', nid, parents[nid], parentsY[parents[nid][0]])
|
||||||
|
}
|
||||||
|
placedY = y
|
||||||
|
this.moveNode(nid, x, y, orientation, tween, null, token)
|
||||||
|
if((align == 'parent') && (nid in parents) && (parents[nid][0] in parentsY)) {
|
||||||
|
parentsY[parents[nid][0]] += gapy + nodeHeight
|
||||||
|
} else {
|
||||||
|
y += gapy + nodeHeight
|
||||||
|
}
|
||||||
|
y = Math.max(y, placedY + gapy + nodeHeight)
|
||||||
} else {
|
} else {
|
||||||
|
if((align == 'parent') && (nid in parents) && (parents[nid][0] in parentsY)) {
|
||||||
|
y = Math.max(parentsY[parents[nid][0]], y)
|
||||||
|
}
|
||||||
|
placedY = y
|
||||||
this.addFakeNode(nid, x, y, wMax*0.75, fakeNodeHeight)
|
this.addFakeNode(nid, x, y, wMax*0.75, fakeNodeHeight)
|
||||||
this.moveNode(nid, x, y, orientation, tween, null, token)
|
this.moveNode(nid, x, y, orientation, tween, null, token)
|
||||||
y += gapy + fakeNodeHeight
|
// Never increment parentsY for fake nodes: they're placeholders and must not disalign real children
|
||||||
|
y = Math.max(y, placedY + gapy + fakeNodeHeight)
|
||||||
}
|
}
|
||||||
|
parentsY[nid] = placedY
|
||||||
|
nodeY[nid] = placedY
|
||||||
|
nodeX[nid] = x
|
||||||
}
|
}
|
||||||
x += wMax + gapx
|
x += wMax + gapx
|
||||||
}
|
}
|
||||||
|
// Correct parent positions: when fake nodes pushed children down, align parents with their first real child
|
||||||
|
if(align == 'parent'){
|
||||||
|
for(let idx = 1; idx < layers.length; idx++){
|
||||||
|
const layer = layers[idx]
|
||||||
|
const prevLayer = layers[idx - 1]
|
||||||
|
for(const pid of prevLayer){
|
||||||
|
if(pid.startsWith('longLinkPlaceHolder_')) continue
|
||||||
|
const firstRealChild = layer.find(nid =>
|
||||||
|
!nid.startsWith('longLinkPlaceHolder_') && nid in parents && parents[nid][0] === pid
|
||||||
|
)
|
||||||
|
if(firstRealChild && nodeY[pid] !== nodeY[firstRealChild]){
|
||||||
|
this.moveNode(pid, nodeX[pid], nodeY[firstRealChild], orientation, tween, null, token)
|
||||||
|
nodeY[pid] = nodeY[firstRealChild]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if(orientation=='vertical'){
|
} else if(orientation=='vertical'){
|
||||||
const fakeNodeWidth = 10
|
const fakeNodeWidth = 10
|
||||||
let y = gapy
|
let y = gapy
|
||||||
for(const [idx, layer] of layers.entries()){
|
for(const [idx, layer] of layers.entries()){
|
||||||
let hMax = this.getMaxHeight(layer)
|
let hMax = this.getMaxHeight(layer)
|
||||||
let x = ((maxWidth - layerWidths[idx]) / 2) + gapx
|
let x = 0
|
||||||
|
switch(align){
|
||||||
|
case 'center':
|
||||||
|
x = ((maxWidth - layerWidths[idx]) / 2) + gapx
|
||||||
|
break
|
||||||
|
case 'first':
|
||||||
|
x = gapx
|
||||||
|
break
|
||||||
|
case 'last':
|
||||||
|
x = maxWidth - layerWidths[idx] + gapx
|
||||||
|
break
|
||||||
|
case 'parent': // x will be absolutely positioned by the parent(s)
|
||||||
|
//TODO
|
||||||
|
break
|
||||||
|
}
|
||||||
for(const nid of layer){
|
for(const nid of layer){
|
||||||
if(!nid.startsWith('longLinkPlaceHolder_')){
|
if(!nid.startsWith('longLinkPlaceHolder_')){
|
||||||
const bb = this.stagedNodes[nid].getBoundingClientRect()
|
const bb = this.stagedNodes[nid].getBoundingClientRect()
|
||||||
@@ -1075,6 +1157,17 @@ class BZgraflow extends Buildoz{
|
|||||||
return(crossLayerLinks)
|
return(crossLayerLinks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
autofit(){
|
||||||
|
const parentBB = this.parentElement.getBoundingClientRect()
|
||||||
|
// Use scroll dimensions for actual content extent (nodes can extend beyond element bounds)
|
||||||
|
const contentW = Math.max(this.scrollWidth || this.offsetWidth || 1, 1)
|
||||||
|
const contentH = Math.max(this.scrollHeight || this.offsetHeight || 1, 1)
|
||||||
|
const sx = parentBB.width / contentW
|
||||||
|
const sy = parentBB.height / contentH
|
||||||
|
const scale = Math.min(sx, sy) // uniform scale to fit inside parent
|
||||||
|
this.style.transformOrigin = 'top left'
|
||||||
|
this.style.transform = `scale(${scale})`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Buildoz.define('graflow', BZgraflow)
|
Buildoz.define('graflow', BZgraflow)
|
||||||
|
|
||||||
@@ -1083,6 +1176,14 @@ class MovingNodes{
|
|||||||
this.graflow = graflow
|
this.graflow = graflow
|
||||||
this.nodesContainer = this.graflow.mainContainer.querySelector('.bzgf-nodes-container')
|
this.nodesContainer = this.graflow.mainContainer.querySelector('.bzgf-nodes-container')
|
||||||
this.state = null
|
this.state = null
|
||||||
|
|
||||||
|
this.interactiveElementsSelector = `
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select,
|
||||||
|
button,
|
||||||
|
a[href]
|
||||||
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
enableMovingNodes(itemSelector, handleSelector = itemSelector) {
|
enableMovingNodes(itemSelector, handleSelector = itemSelector) {
|
||||||
@@ -1095,16 +1196,44 @@ class MovingNodes{
|
|||||||
this._handleCursorStyle = style
|
this._handleCursorStyle = style
|
||||||
}
|
}
|
||||||
|
|
||||||
this.nodesContainer.addEventListener('pointerdown', this.pointerDown.bind(this))
|
this.nodesContainer.querySelectorAll(this.handleSelector).forEach(item =>
|
||||||
|
item.addEventListener('pointerdown', this.pointerDown.bind(this))
|
||||||
|
)
|
||||||
this.nodesContainer.addEventListener('pointermove', this.pointerMove.bind(this))
|
this.nodesContainer.addEventListener('pointermove', this.pointerMove.bind(this))
|
||||||
this.nodesContainer.addEventListener('pointerup', this.pointerUp.bind(this))
|
this.nodesContainer.querySelectorAll(this.handleSelector).forEach(item =>
|
||||||
|
item.addEventListener('pointerup', this.pointerUp.bind(this))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
disableMovingNodes(){
|
||||||
|
this.nodesContainer.querySelectorAll(this.handleSelector).forEach(item =>
|
||||||
|
item.removeEventListener('pointerdown', this.pointerDown.bind(this))
|
||||||
|
)
|
||||||
|
this.nodesContainer.removeEventListener('pointermove', this.pointerMove.bind(this))
|
||||||
|
this.nodesContainer.querySelectorAll(this.handleSelector).forEach(item =>
|
||||||
|
item.removeEventListener('pointerup', this.pointerUp.bind(this))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pointerDown(e){
|
pointerDown(e){
|
||||||
this.graflow.clearFakeNodes()
|
this.graflow.clearFakeNodes()
|
||||||
const node = (e.target.classList.contains(this.itemSelector)) ? e.target : e.target.closest(this.itemSelector)
|
console.log('=====> interactive element', e.target)
|
||||||
const handle = (node.classList.contains(this.handleSelector)) ? node : node.querySelector(this.handleSelector)
|
|
||||||
|
const node = e.target.closest(this.itemSelector)
|
||||||
|
if(!node) return
|
||||||
|
|
||||||
|
let handle
|
||||||
|
if(this.handleSelector == this.itemSelector) {
|
||||||
|
handle = node
|
||||||
|
if(e.target.closest(this.interactiveElementsSelector)) return
|
||||||
|
e.preventDefault()
|
||||||
|
} else { // If defined handle, then no need to care about interactive elements
|
||||||
|
handle = node.querySelector(this.handleSelector)
|
||||||
|
if(e.target != handle) return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const rect = node.getBoundingClientRect()
|
const rect = node.getBoundingClientRect()
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -1123,6 +1252,7 @@ class MovingNodes{
|
|||||||
node.style.top = `${y}px`
|
node.style.top = `${y}px`
|
||||||
node.style.margin = '0'
|
node.style.margin = '0'
|
||||||
node.style.zIndex = '9999'
|
node.style.zIndex = '9999'
|
||||||
|
node.style.pointerEvents = 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
pointerMove(e){
|
pointerMove(e){
|
||||||
@@ -1138,6 +1268,33 @@ class MovingNodes{
|
|||||||
pointerUp(e){
|
pointerUp(e){
|
||||||
if(!this.state) return
|
if(!this.state) return
|
||||||
this.state.node.releasePointerCapture(e.pointerId)
|
this.state.node.releasePointerCapture(e.pointerId)
|
||||||
|
this.state.node.style.pointerEvents = ''
|
||||||
this.state = null
|
this.state = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EditWires{
|
||||||
|
constructor(graflow){
|
||||||
|
this.graflow = graflow
|
||||||
|
this.nodesContainer = this.graflow.mainContainer.querySelector('.bzgf-nodes-container')
|
||||||
|
this.state = null
|
||||||
|
}
|
||||||
|
enableEditWires(){
|
||||||
|
for(const ref in this.graflow.stagedWires ){
|
||||||
|
this.graflow.stagedWires[ref].addEventListener('click', this.onSelectWire.bind(this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSelectWire(e){
|
||||||
|
const wire = e.target
|
||||||
|
console.log('wire', wire)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DroppingNodes{
|
||||||
|
constructor(graflow){
|
||||||
|
this.graflow = graflow
|
||||||
|
this.nodesContainer = this.graflow.mainContainer.querySelector('.bzgf-nodes-container')
|
||||||
|
this.state = null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user