fixed chicked&egg&connectedCB issue between graflow & editor + fixed no Flow issue in graflow + Fixed nodesMove issue on displaced graflow + graflowEditor: DND ok

This commit is contained in:
STEINNI
2026-03-22 15:29:48 +00:00
parent 4122545cff
commit 710bddd8e4
4 changed files with 87 additions and 25 deletions

View File

@@ -60,7 +60,8 @@ class Buildoz extends HTMLElement {
fireEvent(eventName, detail){ fireEvent(eventName, detail){
let myname = this.tagName.toLocaleLowerCase() let myname = this.tagName.toLocaleLowerCase()
myname = myname.substring(myname.indexOf('-')+1) myname = myname.substring(myname.indexOf('-')+1)
this.dispatchEvent(new CustomEvent(`bz:${myname}:${eventName}`, { const eventFullName = `bz:${myname}:${eventName}`
this.dispatchEvent(new CustomEvent(eventFullName, {
detail, detail,
bubbles: true, bubbles: true,
composed: true, composed: true,

View File

@@ -16,7 +16,8 @@ class BZgrafloweditor extends Buildoz{
this.defaultAttrs = { } this.defaultAttrs = { }
} }
connectedCallback() { async connectedCallback() {
await customElements.whenDefined('bz-graflow')
super.connectedCallback() super.connectedCallback()
const nodesUrl = this.getBZAttribute('nodes') const nodesUrl = this.getBZAttribute('nodes')
this.mainContainer = document.createElement('div') this.mainContainer = document.createElement('div')
@@ -26,20 +27,64 @@ class BZgrafloweditor extends Buildoz{
this.mainContainer.append(this.nodesContainer) this.mainContainer.append(this.nodesContainer)
this.graflow = document.createElement('bz-graflow') this.graflow = document.createElement('bz-graflow')
this.graflow.setAttribute('nodes', nodesUrl) this.graflow.setAttribute('nodes', nodesUrl)
this.graflow.setAttribute('edit', "nodesmove,editwires,dropnodes")
this.graflow.addEventListener('bz:graflow:domConnected', this.setupDropZone.bind(this))
this.mainContainer.append(this.graflow) this.mainContainer.append(this.graflow)
this.append(this.mainContainer) this.append(this.mainContainer)
this.graflow.addEventListener('bz:graflow:nodesLoaded', this.refreshNodes.bind(this)) this.graflow.addEventListener('bz:graflow:nodesLoaded', this.refreshNodes.bind(this))
this.graflow.loadNodes(nodesUrl) this.graflow.loadNodes(nodesUrl)
} }
refreshNodes(e){ refreshNodes(e){
for(const nodeType in this.graflow.nodesRegistry){ for(const nodeType in this.graflow.nodesRegistry){
const nodeDef = this.graflow.nodesRegistry[nodeType] const nodeDef = this.graflow.nodesRegistry[nodeType]
this.nodesContainer.append(nodeDef.cloneNode(true)) if(nodeDef.dataset.editor=='exclude') continue
const node = nodeDef.cloneNode(true)
this.makeNodeDraggable(node)
this.nodesContainer.append(node)
}
} }
makeNodeDraggable(node){
node.draggable = true
node.style.cursor = 'pointer'
node.addEventListener('dragstart', (evt) => {
evt.dataTransfer.setData('text/plain', node.dataset.nodetype)
evt.dataTransfer.effectAllowed = 'copy'
evt.dataTransfer.setDragImage(node, evt.offsetX, evt.offsetY)
})
}
onNodeDragEnd(evt){
console.log('drag end', evt)
evt.dataTransfer.clearData()
}
setupDropZone(){
const dropZone = this.graflow.wiresContainer
const nodesContainer = this.graflow.nodesContainer
dropZone.addEventListener('dragover', (evt) => {
evt.preventDefault()
evt.dataTransfer.dropEffect = 'copy'
})
dropZone.addEventListener('drop', (evt) => {
evt.preventDefault()
const nodeType = evt.dataTransfer.getData('text/plain')
if(!nodeType || !(nodeType in this.graflow.nodesRegistry)) return
const rect = nodesContainer.getBoundingClientRect()
const x = evt.clientX - rect.left + nodesContainer.scrollLeft
const y = evt.clientY - rect.top + nodesContainer.scrollTop
const id = 'n' + crypto.randomUUID().replace(/-/g, '').slice(0, 8)
this.graflow.addNode({ id, nodeType, coords: { x, y } })
for(const node of this.graflow.flow.nodes){
if(node.id == id){
node.coords.x = x
node.coords.y = y
break
}
}
this.graflow.refresh()
})
} }
} }
Buildoz.define('grafloweditor', BZgrafloweditor) Buildoz.define('grafloweditor', BZgrafloweditor)

View File

@@ -66,10 +66,6 @@ class BZgraflow extends Buildoz{
connectedCallback() { connectedCallback() {
super.connectedCallback() super.connectedCallback()
const flowUrl = this.getBZAttribute('flow')
if(!flowUrl) return // Be tolerant: maybe injected later from JS above
// If attribute "isolated" is present, render inside a shadow root.
// Otherwise, render in light DOM (no shadow DOM).
this.hostContainer = document.createElement('div') this.hostContainer = document.createElement('div')
this.hostContainer.classList.add('bzgf-main-container') this.hostContainer.classList.add('bzgf-main-container')
this.mainContainer = this.hasAttribute('isolated') this.mainContainer = this.hasAttribute('isolated')
@@ -96,7 +92,10 @@ class BZgraflow extends Buildoz{
this.NodesReceiver = new DroppingNodes(this, '.bzgf-node') this.NodesReceiver = new DroppingNodes(this, '.bzgf-node')
} }
} }
this.loadFlow(flowUrl) this.fireEvent('domConnected', { graflow: this })
const flowUrl = this.getBZAttribute('flow')
if(flowUrl) this.loadFlow(flowUrl)
else this.initFlow()
} }
error(msg, err){ error(msg, err){
@@ -126,6 +125,10 @@ class BZgraflow extends Buildoz{
this.fireEvent('flowLoaded', { url: url }) this.fireEvent('flowLoaded', { url: url })
} }
initFlow(){
this.flow = { nodes: [], links: [] }
}
async loadNodes(url) { async loadNodes(url) {
const res = await fetch(url+'?'+crypto.randomUUID()) const res = await fetch(url+'?'+crypto.randomUUID())
const html = await res.text() const html = await res.text()
@@ -1217,17 +1220,17 @@ class MovingNodes{
if(e.target != handle) return if(e.target != handle) return
} }
const rect = node.getBoundingClientRect() const rect = node.getBoundingClientRect()
const parentBB = this.nodesContainer.getBoundingClientRect()
const offsetX = rect.left - parentBB.left + this.nodesContainer.scrollLeft
const offsetY = rect.top - parentBB.top + this.nodesContainer.scrollTop
this.state = { this.state = {
node, node,
handle, handle,
startX: e.clientX, startX: e.clientX,
startY: e.clientY, startY: e.clientY,
offsetX: rect.left, offsetX,
offsetY: rect.top offsetY
} }
const x = e.clientX - this.state.startX + this.state.offsetX const x = e.clientX - this.state.startX + this.state.offsetX
const y = e.clientY - this.state.startY + this.state.offsetY const y = e.clientY - this.state.startY + this.state.offsetY
@@ -1252,11 +1255,20 @@ class MovingNodes{
pointerUp(e){ pointerUp(e){
if(!this.state) return if(!this.state) return
this.state.node.releasePointerCapture(e.pointerId) const { node, startX, startY, offsetX, offsetY } = this.state
this.state.node.style.pointerEvents = '' const x = e.clientX - startX + offsetX
const y = e.clientY - startY + offsetY
node.releasePointerCapture(e.pointerId)
node.style.pointerEvents = ''
for(const n of this.graflow.flow.nodes){
if(n.id == node.dataset.id){
n.coords.x = x
n.coords.y = y
break
}
}
this.graflow.fireEvent('nodeMoved', { nodeId: node.dataset.id, x, y })
this.state = null this.state = null
this.graflow.fireEvent('nodeMoved', { nodeId: this.state.node.dataset.id, x: this.state.x, y: this.state.y })
} }
} }

View File

@@ -110,6 +110,7 @@
width:3em; width:3em;
height:3em; height:3em;
padding: 2px; padding: 2px;
border: none;
} }
.bzgf-node[data-nodetype="refnodein"] .body, .bzgf-node[data-nodetype="refnodeout"] .body{ .bzgf-node[data-nodetype="refnodein"] .body, .bzgf-node[data-nodetype="refnodeout"] .body{
border-radius: 50%; border-radius: 50%;
@@ -118,6 +119,10 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin:0;
}
.bzgf-node[data-nodetype="refnodein"] .port, .bzgf-node[data-nodetype="refnodeout"] .port{
top: 50%;
} }
.bzgf-node[data-nodetype="refnodein"] .body{ background: #0F0; } .bzgf-node[data-nodetype="refnodein"] .body{ background: #0F0; }
.bzgf-node[data-nodetype="refnodeout"] .body{ background: #FF0; } .bzgf-node[data-nodetype="refnodeout"] .body{ background: #FF0; }
@@ -209,16 +214,15 @@
</div> </div>
</template> </template>
<template> <template>
<div class="bzgf-node" data-nodetype="refnodein"> <div class="bzgf-node" data-nodetype="refnodein" data-editor="exclude">
<div class="body">{parentport}</div> <div class="body">{parentport}</div>
<div class="port" data-type="out" data-id="out1" data-direction="e"></div> <div class="port" data-type="out" data-id="out1" data-direction="e"></div>
</div> </div>
</template> </template>
<template> <template>
<div class="bzgf-node" data-nodetype="refnodeout"> <div class="bzgf-node" data-nodetype="refnodeout" data-editor="exclude">
<div class="body">{parentport}</div> <div class="body">{parentport}</div>
<div class="port" data-type="in" data-id="in1" data-direction="w"></div> <div class="port" data-type="in" data-id="in1" data-direction="w"></div>
</div> </div>