autofit fixed and simplified, thx Mike ! (Mike code readapted for edge cases)
This commit is contained in:
+17
-61
@@ -1332,69 +1332,25 @@ class BZgraflow extends Buildoz{
|
|||||||
return(crossLayerLinks)
|
return(crossLayerLinks)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Union bounding boxes of nodes and wires (viewport coords).
|
|
||||||
* Same measurement strategy as autofit(). Call with transform cleared for stable sizes.
|
|
||||||
*/
|
|
||||||
getContentSize(){
|
|
||||||
let left = Infinity
|
|
||||||
let top = Infinity
|
|
||||||
let right = -Infinity
|
|
||||||
let bottom = -Infinity
|
|
||||||
|
|
||||||
const includeBB = (bb) => {
|
|
||||||
if(!bb) return
|
|
||||||
left = Math.min(left, bb.left)
|
|
||||||
top = Math.min(top, bb.top)
|
|
||||||
right = Math.max(right, bb.right)
|
|
||||||
bottom = Math.max(bottom, bb.bottom)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nodesContainer?.querySelectorAll?.('.bzgf-node').forEach(nodeEl => includeBB(nodeEl.getBoundingClientRect()))
|
|
||||||
this.wiresContainer?.querySelectorAll?.('path.bzgf-wire').forEach(path => includeBB(path.getBoundingClientRect()))
|
|
||||||
|
|
||||||
const gapx = parseInt(this.getBZAttribute('gapx')) || 80
|
|
||||||
const gapy = parseInt(this.getBZAttribute('gapy')) || 80
|
|
||||||
const hasBounds = Number.isFinite(left) && Number.isFinite(right) && Number.isFinite(top) && Number.isFinite(bottom)
|
|
||||||
const rawWidth = hasBounds ? Math.max(right - left, 1) : Math.max(this.mainContainer?.clientWidth || this.offsetWidth || 1, 1)
|
|
||||||
const rawHeight = hasBounds ? Math.max(bottom - top, 1) : Math.max(this.mainContainer?.clientHeight || this.offsetHeight || 1, 1)
|
|
||||||
|
|
||||||
return({
|
|
||||||
left: hasBounds ? left : null,
|
|
||||||
top: hasBounds ? top : null,
|
|
||||||
right: hasBounds ? right : null,
|
|
||||||
bottom: hasBounds ? bottom : null,
|
|
||||||
rawWidth,
|
|
||||||
rawHeight,
|
|
||||||
width: rawWidth + (2 * gapx),
|
|
||||||
height: rawHeight + (2 * gapy),
|
|
||||||
gapx,
|
|
||||||
gapy,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
autofit(percent=100){
|
autofit(percent=100){
|
||||||
if(!this.parentElement) return
|
const outwidth = Math.max(this.clientWidth, 1)
|
||||||
|
const outheight = Math.max(this.clientHeight, 1)
|
||||||
const prevTransformOrigin = this.style.transformOrigin
|
let inwidth = this.nodesContainer.scrollWidth
|
||||||
this.style.transform = 'none'
|
let inheight = this.nodesContainer.scrollHeight
|
||||||
this.style.transformOrigin = 'top left'
|
try { // SVG scrollWidth ignores overflowing paths; getBBox() covers wire extent.
|
||||||
|
const wireBB = this.wiresContainer.getBBox()
|
||||||
const { left, top, width: contentW, height: contentH, gapx, gapy } = this.getContentSize()
|
if(wireBB.width > 0 || wireBB.height > 0){
|
||||||
const parentBB = this.parentElement.getBoundingClientRect()
|
inwidth = Math.max(inwidth, wireBB.x + wireBB.width)
|
||||||
const sx = parentBB.width / contentW
|
inheight = Math.max(inheight, wireBB.y + wireBB.height)
|
||||||
const sy = parentBB.height / contentH
|
|
||||||
const scale = Math.min(sx, sy)*(percent/100) // uniform scale to fit inside parent
|
|
||||||
const tx = left != null ? (-left + gapx) : gapx
|
|
||||||
const ty = top != null ? (-top + gapy) : gapy
|
|
||||||
if(!this.isSubflow) {
|
|
||||||
this.style.transformOrigin = prevTransformOrigin || 'top left'
|
|
||||||
this.style.transform = `scale(${scale}) translate(${tx}px, ${ty}px)`
|
|
||||||
} else {
|
|
||||||
this.style.transform = `scale(${scale})`
|
|
||||||
this.style.width = `calc(100% / ${scale})` // means 100% of the parent node DESPITE the scaling
|
|
||||||
this.style.height = `calc(100% / ${scale})` // means 100% of the parent node DESPITE the scaling
|
|
||||||
}
|
}
|
||||||
|
} catch(_){ // getBBox throws if SVG not rendered yet, but should not happen as we have _canRunAutoPlace() / _scheduleLayoutWhenReady()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const ratio = Math.min( (outwidth / inwidth), (outheight / inheight) ) * (percent / 100)
|
||||||
|
this.nodesContainer.style.scale = ratio
|
||||||
|
this.wiresContainer.style.scale = ratio
|
||||||
|
this.nodesContainer.style.transformOrigin = 'top left'
|
||||||
|
this.wiresContainer.style.transformOrigin = 'top left'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Buildoz.define('graflow', BZgraflow)
|
Buildoz.define('graflow', BZgraflow)
|
||||||
|
|||||||
@@ -45,19 +45,25 @@
|
|||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
bz-graflow{
|
bz-graflow{
|
||||||
overflow: scroll;
|
|
||||||
border: 2px solid black;
|
border: 2px solid black;
|
||||||
}
|
}
|
||||||
bz-graflow.icmp{ grid-column: 1 / -1; width: 100vw; height: 80vh; background: var(--eicui-base-color-grey-10); }
|
/* Fill the demo frame — autofit sizes to the graflow box, not the parent */
|
||||||
|
bz-graflow.icmp{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--eicui-base-color-grey-10);
|
||||||
|
}
|
||||||
.container {
|
.container {
|
||||||
resize: both;
|
resize: both;
|
||||||
overflow: auto; /* required in practice */
|
overflow: hidden;
|
||||||
min-width: 800px;
|
width: 90vw;
|
||||||
min-height: 300px;
|
height: 80vh;
|
||||||
border: 1px solid #999;
|
min-width: 400px;
|
||||||
|
min-height: 240px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
border:2px solid #A00
|
border: 2px solid #A00;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.container::after {
|
.container::after {
|
||||||
content: "";
|
content: "";
|
||||||
@@ -102,7 +108,6 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
const el = document.querySelector('.container')
|
const el = document.querySelector('.container')
|
||||||
let timer = null // debounce
|
|
||||||
const ro = new ResizeObserver(([entry]) => {
|
const ro = new ResizeObserver(([entry]) => {
|
||||||
const grfl = entry.target.querySelector('bz-graflow')
|
const grfl = entry.target.querySelector('bz-graflow')
|
||||||
grfl.autofit()
|
grfl.autofit()
|
||||||
|
|||||||
Reference in New Issue
Block a user