total-diagram 0.9.7 → 0.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ Total Diagram
8
8
  Simple, powerful, extensible and fast JavaScript/ES8 diagram renderer for web browsers.
9
9
  </p>
10
10
  <p align="center">
11
- v0.9.7
11
+ v0.9.8
12
12
  </p>
13
13
 
14
14
  [![build](https://github.com/dariuszdawidowski/total-diagram/actions/workflows/build.yml/badge.svg)](https://github.com/dariuszdawidowski/total-diagram/actions/workflows/build.yml)
@@ -1,6 +1,6 @@
1
1
  class AnonymousTraversalSource{constructor(r){this.traversalSourceClass=r}static traversal(r){return new AnonymousTraversalSource(r||GraphTraversalSource)}withLists(r,s){return new this.traversalSourceClass(r,s)}}class GraphTraversalSource{constructor(r,s){this.vertices=r,this.edges=s}V(r=null){return new GraphTraversal(this.vertices,r)}E(r=null){return new GraphTraversal(this.edges,r)}}class GraphTraversal{constructor(r,s=null){if(this.result=null,null==s)this.result=r;else if("object"==typeof s){let t=s;for(;t.parentNode;)"classList"in t&&t.classList.contains("total-diagram-node")&&(this.result=r.find((r=>r.id==t.dataset.id))),t=t.parentNode}else"function"==typeof node?this.result=r.filter((r=>r instanceof node)):"string"==typeof s&&(this.result=r.find((r=>r.id==s)))}hasNext(){return null!=this.result&&"Array"!=this.result.constructor.name}next(){return this.result}toList(){return"Array"!=this.result.constructor.name?[this.result]:this.result}}
2
- class TotalDiagramNode{constructor(t={}){this.id="id"in t?t.id:crypto.randomUUID(),this.name="name"in t?t.name:null,this.transform={x:"x"in t?t.x:0,y:"y"in t?t.y:0,border:0,zindex:"zindex"in t?t.zindex:0,ox:0,oy:0,w:0,h:0,wmin:64,hmin:64,wmax:1024,hmax:1024,clear:()=>{this.transform.x=0,this.transform.y=0,this.transform.zindex=0,this.element.style.zIndex=0}},this.links={list:[],add:t=>{-1===this.links.list.indexOf(t)&&this.links.list.push(t)},del:t=>{const s=this.links.list.indexOf(t);-1!==s&&this.links.list.splice(s,1)},get:t=>"*"==t?this.links.list:"in"==t?this.links.list.filter((t=>t.end.id==this.id)):"out"==t?this.links.list.filter((t=>t.start.id==this.id)):"string"==typeof t?this.links.list.find((s=>s.id==t)):null,update:()=>{this.links.list.forEach((t=>t.update()))}},this.element=document.createElement("div"),this.element.classList.add("total-diagram-node"),this.element.style.zIndex=this.transform.zindex,this.element.dataset.id=this.id}destructor(){}awake(){}start(){}setSize(t){this.transform.w=t.width,this.transform.h=t.height,"minWidth"in t&&(this.transform.wmin=t.minWidth),"minHeight"in t&&(this.transform.hmin=t.minHeight),"maxWidth"in t&&(this.transform.wmax=t.maxWidth),"maxHeight"in t&&(this.transform.hmax=t.maxHeight),this.transform.w<this.transform.wmin&&(this.transform.w=this.transform.wmin),this.transform.w>this.transform.wmax&&(this.transform.w=this.transform.wmax),this.transform.h<this.transform.hmin&&(this.transform.h=this.transform.hmin),this.transform.h>this.transform.hmax&&(this.transform.h=this.transform.hmax),this.transform.border="border"in t?t.border:parseInt(getComputedStyle(this.element,null).getPropertyValue("border-left-width").replace("px",""))||0,this.setOrigin(),this.element.style.width=this.transform.w+"px",this.element.style.height=this.transform.h+"px"}getSize(){return{width:this.transform.w,height:this.transform.h,minWidth:this.transform.wmin,minHeight:this.transform.hmin,maxWidth:this.transform.wmax,maxHeight:this.transform.hmax}}setOrigin(){this.transform.ox=this.transform.w/2,this.transform.oy=this.transform.h/2}setPosition(t){this.transform.x=t.x,this.transform.y=t.y}getPosition(){return{x:this.transform.x,y:this.transform.y}}addPosition(t){this.transform.x+=t.x,this.transform.y+=t.y}subPosition(t){this.transform.x-=t.x,this.transform.y-=t.y}setStyle(t,s=null){null!==s&&(this.element.style[t]=s)}getStyle(t=null){return null!==t?this.element.style[t]:this.element.style}transparent(t){this.element.style.opacity=100==t?null:t/100}animated(t=!0){this.element.style.transition=t?"transform 0.5s ease-in-out":null}serialize(){return{id:this.id,type:this.constructor.name,x:this.transform.x,y:this.transform.y,w:this.transform.w,h:this.transform.h,zindex:this.transform.zindex}}setSortingZ(t){this.transform.zindex=t,this.element.style.zIndex=t}update(){this.element.style.transform=`translate(${this.transform.x-this.transform.ox}px, ${this.transform.y-this.transform.oy}px)`}}
2
+ class TotalDiagramNode{constructor(t={}){this.id="id"in t?t.id:crypto.randomUUID(),this.name="name"in t?t.name:null,this.transform={x:"x"in t?t.x:0,y:"y"in t?t.y:0,border:0,zindex:"zindex"in t?t.zindex:0,ox:0,oy:0,w:0,h:0,wmin:64,hmin:64,wmax:1024,hmax:1024,clear:()=>{this.transform.x=0,this.transform.y=0,this.transform.zindex=0,this.element.style.zIndex=0}},this.links={list:[],add:t=>{-1===this.links.list.indexOf(t)&&this.links.list.push(t)},del:t=>{const i=this.links.list.indexOf(t);-1!==i&&this.links.list.splice(i,1)},get:t=>"*"==t?this.links.list:"in"==t?this.links.list.filter((t=>t.end.id==this.id)):"out"==t?this.links.list.filter((t=>t.start.id==this.id)):"string"==typeof t?this.links.list.find((i=>i.id==t)):null,update:()=>{this.links.list.forEach((t=>t.update()))}},this.element=document.createElement("div"),this.element.classList.add("total-diagram-node"),this.element.style.zIndex=this.transform.zindex,this.element.dataset.id=this.id}destructor(){}awake(){}start(){}setSize(t){"width"in t&&(this.transform.w=t.width),"height"in t&&(this.transform.h=t.height),"minWidth"in t&&(this.transform.wmin=t.minWidth),"minHeight"in t&&(this.transform.hmin=t.minHeight),"maxWidth"in t&&(this.transform.wmax=t.maxWidth),"maxHeight"in t&&(this.transform.hmax=t.maxHeight),"width"in t&&(this.transform.w<this.transform.wmin&&(this.transform.w=this.transform.wmin),this.transform.w>this.transform.wmax&&(this.transform.w=this.transform.wmax)),"height"in t&&(this.transform.h<this.transform.hmin&&(this.transform.h=this.transform.hmin),this.transform.h>this.transform.hmax&&(this.transform.h=this.transform.hmax)),this.transform.border="border"in t?t.border:parseInt(getComputedStyle(this.element,null).getPropertyValue("border-left-width").replace("px",""))||0,"width"in t&&"height"in t&&this.setOrigin(),"width"in t&&(this.element.style.width=this.transform.w+"px"),"height"in t&&(this.element.style.height=this.transform.h+"px")}getSize(){return{width:this.transform.w,height:this.transform.h,minWidth:this.transform.wmin,minHeight:this.transform.hmin,maxWidth:this.transform.wmax,maxHeight:this.transform.hmax,border:this.transform.border}}setOrigin(){this.transform.ox=this.transform.w/2,this.transform.oy=this.transform.h/2}setPosition(t){this.transform.x=t.x,this.transform.y=t.y}getPosition(){return{x:this.transform.x,y:this.transform.y}}addPosition(t){this.transform.x+=t.x,this.transform.y+=t.y}subPosition(t){this.transform.x-=t.x,this.transform.y-=t.y}setStyle(t,i=null){null!==i&&(this.element.style[t]=i)}getStyle(t=null){return null!==t?this.element.style[t]:this.element.style}transparent(t){this.element.style.opacity=100==t?null:t/100}animated(t=!0){this.element.style.transition=t?"transform 0.5s ease-in-out":null}serialize(){return{id:this.id,type:this.constructor.name,x:this.transform.x,y:this.transform.y,w:this.transform.w,h:this.transform.h,zindex:this.transform.zindex}}setSortingZ(t){this.transform.zindex=t,this.element.style.zIndex=t}update(){this.element.style.transform=`translate(${this.transform.x-this.transform.ox-this.transform.border}px, ${this.transform.y-this.transform.oy-this.transform.border}px)`}}
3
3
  class TotalDiagramNodesManager{constructor(){this.render=null,this.list=[]}add(t){this.list.push(t),this.render.board.append(t.element),t.awake();const e=new CustomEvent("broadcast:addnode",{detail:t});this.render.container.dispatchEvent(e)}del(t){if("*"!=t){const e=t.links.get("*");for(;e.length;){const t=e.pop();this.render.links.del(t)}t.destructor(),t.element.remove();const s=this.list.indexOf(t);-1!==s&&this.list.splice(s,1);const n=new CustomEvent("broadcast:delnode",{detail:t});this.render.container.dispatchEvent(n)}else if("*"==t){this.list.forEach((t=>{t.destructor(),t.element.remove()})),this.list.length=0;const t=new CustomEvent("broadcast:delnodes",{detail:"*"});this.render.container.dispatchEvent(t)}}get(t){if("*"==t)return this.list;if(null!=t&&"object"==typeof t){let e=t;for(;e.parentNode;){if("classList"in e&&e.classList.contains("total-diagram-node"))return this.get(e.dataset.id);e=e.parentNode}}else{if("function"==typeof t)return this.list.filter((e=>e instanceof t));if("string"==typeof t)return this.list.find((e=>e.id==t))}return null}}
4
4
  class TotalDiagramLink{constructor(t){this.id="id"in t?t.id:crypto.randomUUID(),this.start=t.start,this.start.links.add(this),this.end=t.end,this.end.links.add(this),this.element=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.element.classList.add("link"),this.element.dataset.id=this.id}destructor(){this.start&&this.start.links.del(this),this.end&&this.end.links.del(this)}serialize(){return{id:this.id,type:this.constructor.name,start:this.start.id,end:this.end.id}}transparent(t){this.element.style.opacity=100==t?null:t/100}update(){}}
5
5
  class TotalDiagramLinksManager{constructor(){this.render=null,this.list=[]}add(t){if(!t.start||!t.end)return null;this.list.push(t),this.render.board.append(t.element);const e=new CustomEvent("broadcast:addlink",{detail:t});this.render.container.dispatchEvent(e)}del(t){if("*"!=t){t.destructor(),t.element.remove();const e=this.list.indexOf(t);-1!==e&&this.list.splice(e,1);const n=new CustomEvent("broadcast:dellink",{detail:t});this.render.container.dispatchEvent(n)}else{this.list.forEach((t=>{t.destructor(),t.element.remove()})),this.list.length=0;const t=new CustomEvent("broadcast:delnodes",{detail:"*"});this.render.container.dispatchEvent(t)}}get(t,e=null){if("*"==t)return this.list;if(e){for(const n of t.links.get("*"))for(const t of e.links.get("*"))if(n.id==t.id)return n;return null}return"string"==typeof t?this.list.find((e=>e.element.dataset.id==t)):"function"==typeof t?this.list.filter((e=>e instanceof t)):"object"==typeof t&&"dataset"in t?this.get(t.dataset.id):null}}
6
- class TotalDiagramRenderHTML5{constructor(t){this.container=t.container,this.board=document.createElement("div"),this.board.id="total-diagram-attach",this.board.style.transformOrigin="0px 0px",this.board.style.width=0,this.board.style.height=0,this.board.style.overflow="visible",this.container.appendChild(this.board),this.size=this.container.getBoundingClientRect(),this.size.center={x:this.size.width/2,y:this.size.height/2},this.margin={left:this.size.left-document.documentElement.scrollLeft,top:this.size.top-document.documentElement.scrollTop},this.offset={x:0,y:0,z:1},window.addEventListener("resize",(()=>{this.size=this.container.getBoundingClientRect(),this.size.center={x:this.size.width/2,y:this.size.height/2}})),this.nodes=t.nodes,this.nodes.render=this,this.links=t.links,this.links.render=this;const s=AnonymousTraversalSource.traversal;this.g=s().withLists(this.nodes.list,this.links.list)}pan(t,s){this.offset.x+=t,this.offset.y+=s,this.update()}zoom(t,s,i,e){let h=this.offset.z;this.offset.z=Math.max(.1,Math.min(3,this.offset.z-i/e*this.offset.z)),h=this.offset.z/h;const o=this.board.getBoundingClientRect(),n=o.width/2+o.left-t,r=o.height/2+o.top-s;this.offset.x+=n*h-n,this.offset.y+=r*h-r,this.update()}pinchZoom(t,s,i,e,h){let o=this.offset.z;this.offset.z=Math.max(.1,Math.min(3,this.offset.z*h)),o=this.offset.z/o;const n=this.board.getBoundingClientRect(),r=n.width/2+n.left-(t+i)/2,f=n.height/2+n.top-(s+e)/2;this.offset.x+=r*o-r,this.offset.y+=f*o-f,this.update()}screen2World(t){return{x:Math.round((t.x-this.offset.x)/this.offset.z-this.margin.left),y:Math.round((t.y-this.offset.y)/this.offset.z-this.margin.top)}}world2Screen(t){return{x:Math.round(this.offset.x+t.x*this.offset.z-this.margin.left),y:Math.round(this.offset.y+t.y*this.offset.z-this.margin.top)}}center(t={x:0,y:0},s="none",i="hard",e=null){"smooth"==i&&(this.board.style.transition="transform 1s",setInterval((()=>{this.board.style.removeProperty("transition")}),1e3)),"reset"==s?this.offset.z=1:"focus"==s&&(this.offset.z=this.size.height/(e.height+e.margin)),this.offset.x=-t.x*this.offset.z+this.size.center.x,this.offset.y=-t.y*this.offset.z+this.size.center.y,this.update()}centerZoom(){this.offset.z=1,this.update()}focusBounds(){const t=this.getBounds(this.nodes.get("*").filter((t=>t.parent==this.nodes.parent))),s={x:this.size.width/t.width,y:this.size.height/t.height,avg:function(){return(this.x+this.y)/2}};this.offset.z=t.isZero()?1:Math.min(Math.max(s.avg(),.3),1),this.offset.x=-t.x*this.offset.z+this.size.center.x,this.offset.y=-t.y*this.offset.z+this.size.center.y,this.update()}getBounds(t){const s={left:1/0,right:-1/0,top:1/0,bottom:-1/0,x:0,y:0,width:0,height:0,isZero:function(){for(let t in this)if("isZero"!==t&&Math.abs(this[t])>Number.EPSILON)return!1;return!0}};return t.forEach((t=>{s.left=Math.min(t.transform.x-t.transform.w/2,s.left),s.top=Math.min(t.transform.y-t.transform.h/2,s.top),s.right=Math.max(t.transform.x+t.transform.w/2,s.right),s.bottom=Math.max(t.transform.y+t.transform.h/2,s.bottom)})),s.width=s.right-s.left,s.height=s.bottom-s.top,s.x=s.left+s.width/2,s.y=s.top+s.height/2,s}clear(){this.board.innerHTML="",this.links.del("*"),this.nodes.del("*")}update(){this.board.style.transform=`translate(${this.offset.x}px, ${this.offset.y}px) scale(${this.offset.z})`}redraw(){this.container.style.display="none";this.container.offsetHeight;this.container.style.display="block"}}
6
+ class TotalDiagramRenderHTML5{constructor(t){this.container=t.container,this.board=document.createElement("div"),this.board.id="total-diagram-attach",this.board.style.transformOrigin="0px 0px",this.board.style.width=0,this.board.style.height=0,this.board.style.overflow="visible",this.container.appendChild(this.board),this.size=this.container.getBoundingClientRect(),this.size.center={x:this.size.width/2,y:this.size.height/2},this.margin={left:this.size.left-document.documentElement.scrollLeft,top:this.size.top-document.documentElement.scrollTop},this.offset={x:0,y:0,z:1,delta:{x:0,y:0,length:function(){return Math.sqrt(this.x**2+this.y**2)}},timestamp:0,speed:0,add:function(t,s){this.delta.x=t,this.delta.y=s,this.x+=t,this.y+=s,this.calcSpeed()},calcSpeed:function(t=4e3){const s=Date.now();0==this.timestamp&&(this.timestamp=s);const e=s-this.timestamp;this.timestamp=s,this.speed=(this.speed+Math.min(t,this.delta.length()/(e/1e3)))/2}},window.addEventListener("resize",(()=>{this.size=this.container.getBoundingClientRect(),this.size.center={x:this.size.width/2,y:this.size.height/2}})),this.nodes=t.nodes,this.nodes.render=this,this.links=t.links,this.links.render=this;const s=AnonymousTraversalSource.traversal;this.g=s().withLists(this.nodes.list,this.links.list)}pan(t,s){this.offset.add(t,s),this.update()}damp(t=.97,s=300){this.offset.calcSpeed();const e=()=>{this.offset.delta.x*=t,this.offset.delta.y*=t,this.offset.x+=this.offset.delta.x/window.devicePixelRatio,this.offset.y+=this.offset.delta.y/window.devicePixelRatio,this.update(),(Math.abs(this.offset.delta.x)>.1||Math.abs(this.offset.delta.y)>.1)&&requestAnimationFrame(e)};this.offset.speed>s&&e()}zoom(t,s,e,i){this.offset.delta.x=0,this.offset.delta.y=0;let h=this.offset.z;this.offset.z=Math.max(.1,Math.min(3,this.offset.z-e/i*this.offset.z)),h=this.offset.z/h;const o=this.board.getBoundingClientRect(),n=o.width/2+o.left-t,f=o.height/2+o.top-s;this.offset.x+=n*h-n,this.offset.y+=f*h-f,this.update()}pinchZoom(t,s,e,i,h){let o=this.offset.z;this.offset.z=Math.max(.1,Math.min(3,this.offset.z*h)),o=this.offset.z/o;const n=this.board.getBoundingClientRect(),f=n.width/2+n.left-(t+e)/2,a=n.height/2+n.top-(s+i)/2;this.offset.x+=f*o-f,this.offset.y+=a*o-a,this.update()}screen2World(t){return{x:Math.round((t.x-this.offset.x)/this.offset.z-this.margin.left),y:Math.round((t.y-this.offset.y)/this.offset.z-this.margin.top)}}world2Screen(t){return{x:Math.round(this.offset.x+t.x*this.offset.z-this.margin.left),y:Math.round(this.offset.y+t.y*this.offset.z-this.margin.top)}}center(t={x:0,y:0},s="none",e="hard",i=null){"smooth"==e&&(this.board.style.transition="transform 1s",setInterval((()=>{this.board.style.removeProperty("transition")}),1e3)),"reset"==s?this.offset.z=1:"focus"==s&&(this.offset.z=this.size.height/(i.height+i.margin)),this.offset.x=-t.x*this.offset.z+this.size.center.x,this.offset.y=-t.y*this.offset.z+this.size.center.y,this.update()}centerZoom(){this.offset.z=1,this.update()}focusBounds(){const t=this.getBounds(this.nodes.get("*").filter((t=>t.parent==this.nodes.parent))),s={x:this.size.width/t.width,y:this.size.height/t.height,avg:function(){return(this.x+this.y)/2}};this.offset.z=t.isZero()?1:Math.min(Math.max(s.avg(),.3),1),this.offset.x=-t.x*this.offset.z+this.size.center.x,this.offset.y=-t.y*this.offset.z+this.size.center.y,this.update()}getBounds(t){const s={left:1/0,right:-1/0,top:1/0,bottom:-1/0,x:0,y:0,width:0,height:0,isZero:function(){for(let t in this)if("isZero"!==t&&Math.abs(this[t])>Number.EPSILON)return!1;return!0}};return t.forEach((t=>{s.left=Math.min(t.transform.x-t.transform.w/2,s.left),s.top=Math.min(t.transform.y-t.transform.h/2,s.top),s.right=Math.max(t.transform.x+t.transform.w/2,s.right),s.bottom=Math.max(t.transform.y+t.transform.h/2,s.bottom)})),s.width=s.right-s.left,s.height=s.bottom-s.top,s.x=s.left+s.width/2,s.y=s.top+s.height/2,s}clear(){this.board.innerHTML="",this.links.del("*"),this.nodes.del("*")}update(){this.board.style.transform=`translate(${this.offset.x}px, ${this.offset.y}px) scale(${this.offset.z})`}redraw(){this.container.style.display="none";this.container.offsetHeight;this.container.style.display="block"}}
@@ -269,23 +269,23 @@
269
269
 
270
270
  container.addEventListener('mousedown', (event) => {
271
271
  state = 1;
272
- } );
272
+ });
273
273
 
274
274
  container.addEventListener('mousemove', (event) => {
275
-
276
275
  if (state == 1) {
277
276
  render.pan(event.movementX, event.movementY);
278
277
  }
279
278
  });
280
279
 
281
280
  container.addEventListener('mouseup', (event) => {
281
+ render.damp();
282
282
  state = 0;
283
- } );
283
+ });
284
284
 
285
285
  container.addEventListener('mousewheel', (event) => {
286
286
  event.preventDefault();
287
287
  render.zoom(event.pageX, event.pageY, event.deltaY, 1000);
288
- } );
288
+ });
289
289
 
290
290
  }
291
291
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "total-diagram",
3
- "version": "0.9.7",
3
+ "version": "0.9.8",
4
4
  "description": "Simple, powerful, extensible and fast JavaScript/ES8 diagram renderer for web browsers.",
5
5
  "license": "MIT",
6
6
  "scripts": {
package/render-node.js CHANGED
@@ -144,29 +144,39 @@ class TotalDiagramNode {
144
144
 
145
145
  /**
146
146
  * Size
147
- * {width: <Number>, height: <Number>, minWidth: <Number>, minHeight: <Number>, maxWidth: <Number>, maxHeight: <Number>, border: [Number]}
147
+ * @param size.width: <Number>
148
+ * @param size.height: <Number>
149
+ * @param size.minWidth: <Number>
150
+ * @param size.minHeight: <Number>
151
+ * @param size.maxWidth: <Number>
152
+ * @param size.maxHeight: <Number>
153
+ * @param size.border: [Number]
148
154
  */
149
155
 
150
156
  setSize(size) {
151
- this.transform.w = size.width;
152
- this.transform.h = size.height;
157
+ if ('width' in size) this.transform.w = size.width;
158
+ if ('height' in size) this.transform.h = size.height;
153
159
  if ('minWidth' in size) this.transform.wmin = size.minWidth;
154
160
  if ('minHeight' in size) this.transform.hmin = size.minHeight;
155
161
  if ('maxWidth' in size) this.transform.wmax = size.maxWidth;
156
162
  if ('maxHeight' in size) this.transform.hmax = size.maxHeight;
157
- if (this.transform.w < this.transform.wmin) this.transform.w = this.transform.wmin;
158
- if (this.transform.w > this.transform.wmax) this.transform.w = this.transform.wmax;
159
- if (this.transform.h < this.transform.hmin) this.transform.h = this.transform.hmin;
160
- if (this.transform.h > this.transform.hmax) this.transform.h = this.transform.hmax;
163
+ if ('width' in size) {
164
+ if (this.transform.w < this.transform.wmin) this.transform.w = this.transform.wmin;
165
+ if (this.transform.w > this.transform.wmax) this.transform.w = this.transform.wmax;
166
+ }
167
+ if ('height' in size) {
168
+ if (this.transform.h < this.transform.hmin) this.transform.h = this.transform.hmin;
169
+ if (this.transform.h > this.transform.hmax) this.transform.h = this.transform.hmax;
170
+ }
161
171
  if ('border' in size) {
162
172
  this.transform.border = size.border;
163
173
  }
164
174
  else {
165
175
  this.transform.border = parseInt(getComputedStyle(this.element, null).getPropertyValue('border-left-width').replace('px', '')) || 0;
166
176
  }
167
- this.setOrigin();
168
- this.element.style.width = this.transform.w + 'px';
169
- this.element.style.height = this.transform.h + 'px';
177
+ if ('width' in size && 'height' in size) this.setOrigin();
178
+ if ('width' in size) this.element.style.width = this.transform.w + 'px';
179
+ if ('height' in size) this.element.style.height = this.transform.h + 'px';
170
180
  }
171
181
 
172
182
  getSize() {
@@ -176,7 +186,8 @@ class TotalDiagramNode {
176
186
  minWidth: this.transform.wmin,
177
187
  minHeight: this.transform.hmin,
178
188
  maxWidth: this.transform.wmax,
179
- maxHeight: this.transform.hmax
189
+ maxHeight: this.transform.hmax,
190
+ border: this.transform.border
180
191
  };
181
192
  }
182
193
 
@@ -292,7 +303,7 @@ class TotalDiagramNode {
292
303
 
293
304
  update() {
294
305
  // Calculate position (PYQt6 doesn't support separate css translate yet)
295
- this.element.style.transform = `translate(${this.transform.x - this.transform.ox}px, ${this.transform.y - this.transform.oy}px)`;
306
+ this.element.style.transform = `translate(${this.transform.x - this.transform.ox - this.transform.border}px, ${this.transform.y - this.transform.oy - this.transform.border}px)`;
296
307
  }
297
308
 
298
309
  }
package/render.js CHANGED
@@ -40,11 +40,34 @@ class TotalDiagramRenderHTML5 {
40
40
  top: this.size.top - document.documentElement.scrollTop,
41
41
  };
42
42
 
43
- // Board current pan offset (x,y) and zoom (z)
43
+ // Board current pan offset (x,y), zoom (z) and last delta movement (delta.x delta.y)
44
44
  this.offset = {
45
45
  x: 0,
46
46
  y: 0,
47
- z: 1
47
+ z: 1,
48
+ delta: {
49
+ x: 0,
50
+ y: 0,
51
+ length: function() {
52
+ return Math.sqrt(this.x ** 2 + this.y ** 2);
53
+ }
54
+ },
55
+ timestamp: 0,
56
+ speed: 0, // avg px/s
57
+ add: function(deltaX, deltaY) {
58
+ this.delta.x = deltaX;
59
+ this.delta.y = deltaY;
60
+ this.x += deltaX;
61
+ this.y += deltaY;
62
+ this.calcSpeed();
63
+ },
64
+ calcSpeed: function(maxSpeed = 4000) {
65
+ const currentTime = Date.now();
66
+ if (this.timestamp == 0) this.timestamp = currentTime;
67
+ const deltaTime = currentTime - this.timestamp;
68
+ this.timestamp = currentTime;
69
+ this.speed = (this.speed + Math.min(maxSpeed, this.delta.length() / (deltaTime / 1000))) / 2;
70
+ }
48
71
  };
49
72
 
50
73
  // Window resize callback
@@ -74,11 +97,33 @@ class TotalDiagramRenderHTML5 {
74
97
  */
75
98
 
76
99
  pan(deltaX, deltaY) {
77
- this.offset.x += deltaX;
78
- this.offset.y += deltaY;
100
+ this.offset.add(deltaX, deltaY);
79
101
  this.update();
80
102
  }
81
103
 
104
+ /**
105
+ * Perform pan damping
106
+ */
107
+
108
+ damp(factor = 0.97, minSpeed = 300) {
109
+
110
+ this.offset.calcSpeed();
111
+
112
+ const dampAnimation = () => {
113
+ this.offset.delta.x *= factor;
114
+ this.offset.delta.y *= factor;
115
+ this.offset.x += this.offset.delta.x / window.devicePixelRatio;
116
+ this.offset.y += this.offset.delta.y / window.devicePixelRatio;
117
+ this.update();
118
+
119
+ if (Math.abs(this.offset.delta.x) > 0.1 || Math.abs(this.offset.delta.y) > 0.1) {
120
+ requestAnimationFrame(dampAnimation);
121
+ }
122
+ };
123
+
124
+ if (this.offset.speed > minSpeed) dampAnimation();
125
+ }
126
+
82
127
  /**
83
128
  * Perform zoom
84
129
  * @param x: x position
@@ -88,6 +133,10 @@ class TotalDiagramRenderHTML5 {
88
133
  */
89
134
 
90
135
  zoom(x, y, deltaZ, factorZ) {
136
+
137
+ this.offset.delta.x = 0;
138
+ this.offset.delta.y = 0;
139
+
91
140
  let deltaZoom = this.offset.z;
92
141
  this.offset.z = Math.max(0.1, Math.min(3.0, this.offset.z - (deltaZ / factorZ) * this.offset.z));
93
142
  deltaZoom = this.offset.z / deltaZoom;