total-diagram 0.9.3 → 0.9.5

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
@@ -6,10 +6,10 @@ Total Diagram
6
6
  </h1>
7
7
  <p align="center">
8
8
  Simple, powerful, extensible and fast JavaScript/ES8 diagram renderer for web browsers.
9
- <p>
9
+ </p>
10
10
  <p align="center">
11
- v0.9.3
12
- <p>
11
+ v0.9.5
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)
15
15
  [![npm](https://img.shields.io/npm/v/total-diagram)](https://www.npmjs.com/package/total-diagram)
@@ -20,7 +20,11 @@ v0.9.3
20
20
  A library for rendering diagrams consisting of nodes and links.
21
21
  Designed for simplicity, it can be the basis for creating a diagramming application or data representation on a website.
22
22
 
23
- For more details look into examples/ directory. You can find self-explanatory tutorials there.
23
+ <img src="./total-diagram-showcase.png" alt="" />
24
+
25
+ # Quick Start
26
+
27
+ For more details look into 'examples/' directory. You can find self-explanatory tutorials there.
24
28
 
25
29
  # Features
26
30
 
@@ -33,4 +37,12 @@ For more details look into examples/ directory. You can find self-explanatory tu
33
37
  npm install
34
38
  npm run build
35
39
  ```
36
- Note: This is browser-centric vanilla JavaScript library, npm is only used to minify and bundle files into one.
40
+ Note: This is browser-centric vanilla JavaScript library, npm is only used to minify and bundle files.
41
+
42
+ # Load from CDN
43
+
44
+ https://unpkg.com/total-diagram@latest/dist/total-diagram.js
45
+
46
+ # Authors
47
+
48
+ Dariusz Dawidowski
package/build.js CHANGED
@@ -1,10 +1,14 @@
1
1
  /**
2
- * Build script
2
+ * Build script v6-custom
3
3
  */
4
4
 
5
+ const files = [
6
+ { src: 'total-diagram.js.ejs', dst: 'total-diagram.js' },
7
+ ];
8
+
5
9
  const fs = require('fs');
6
- const ejs = require('ejs');
7
10
  const { readFile } = require('fs').promises;
11
+ const ejs = require('ejs');
8
12
  const { minify } = require('terser');
9
13
 
10
14
  const minjs = async (filePath) => {
@@ -19,5 +23,11 @@ const minjs = async (filePath) => {
19
23
  }
20
24
  };
21
25
 
22
- ejs.render(fs.readFileSync('total-diagram.js.ejs', 'utf8'), { minjs }, { async: true })
23
- .then(output => fs.writeFileSync('dist/total-diagram.js', output, 'utf8'));
26
+ if (!fs.existsSync('dist')) fs.mkdirSync('dist');
27
+
28
+ files.forEach((file) => {
29
+
30
+ ejs.render(fs.readFileSync(file.src, 'utf8'), { minjs }, {async: true})
31
+ .then(output => fs.writeFileSync('dist/' + file.dst, output, 'utf8'));
32
+
33
+ });
@@ -1,4 +1,4 @@
1
- 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=>{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:"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){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),"border"in t&&(this.transform.border=t.border),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}addLink(t){this.links.add(t)}delLink(t){this.links.del(t)}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}px, ${this.transform.y-this.transform.oy}px)`}}
1
+ 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=>{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:"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){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.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}addLink(t){this.links.add(t)}delLink(t){this.links.del(t)}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}px, ${this.transform.y-this.transform.oy}px)`}}
2
2
  class TotalDiagramNodesManager{constructor(){this.render=null,this.list=[]}add(t){this.list.push(t),this.render.board.append(t.element),t.awake()}del(t){if("*"!=t){let e=t.links.get("*");for(;e.length;){const t=e.pop();this.render.links.del(t)}t.destructor(),t.element.remove();const i=this.list.indexOf(t);-1!==i&&this.list.splice(i,1)}else"*"==t&&(this.list.length=0)}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}}
3
3
  class TotalDiagramLink{constructor(t){this.id="id"in t?t.id:crypto.randomUUID(),this.start=t.start,this.start.addLink(this),this.end=t.end,this.end.addLink(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.delLink(this),this.end&&this.end.delLink(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(){}}
4
4
  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)}del(t){if("*"!=t){t.destructor(),t.element.remove();const e=this.list.indexOf(t);-1!==e&&this.list.splice(e,1)}else this.list.length=0}get(t,e=null){if("*"==t)return this.list;if(e){for(const i of t.links.get("*"))for(const t of e.links.get("*"))if(i.id==t.id)return i;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}}
@@ -33,6 +33,12 @@
33
33
  user-select: none;
34
34
  touch-action: none;
35
35
  overflow: hidden;
36
+ font-family: Arial, sans-serif;
37
+ }
38
+
39
+ h3, p {
40
+ color: white;
41
+ text-align: center;
36
42
  }
37
43
 
38
44
  .node {
@@ -60,13 +66,16 @@
60
66
  </head>
61
67
  <body>
62
68
  <!-- Main diagram container -->
63
- <div id="container"></div>
69
+ <div id="container">
70
+ <h3>1: Basic demo</h3>
71
+ <p>How to create two nodes and link them.</p>
72
+ </div>
64
73
 
65
74
  <!-- Total Diagram library -->
66
- <script type="text/javascript" src="../node.js"></script>
67
- <script type="text/javascript" src="../link.js"></script>
68
- <script type="text/javascript" src="../nodes.js"></script>
69
- <script type="text/javascript" src="../links.js"></script>
75
+ <script type="text/javascript" src="../render-node.js"></script>
76
+ <script type="text/javascript" src="../render-link.js"></script>
77
+ <script type="text/javascript" src="../manager-nodes.js"></script>
78
+ <script type="text/javascript" src="../manager-links.js"></script>
70
79
  <script type="text/javascript" src="../render.js"></script>
71
80
 
72
81
  <script>
@@ -0,0 +1,235 @@
1
+ <!doctype html>
2
+
3
+ <html>
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <title>Total Diagram Demo</title>
7
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
8
+ <meta http-equiv="Pragma" content="no-cache">
9
+ <meta http-equiv="Expires" content="0">
10
+ <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
11
+ <style>
12
+
13
+ html, body {
14
+ width: 100%;
15
+ height: 100%;
16
+ margin: 0;
17
+ }
18
+
19
+ #container {
20
+ position: relative;
21
+ margin: 0 auto;
22
+ display: block;
23
+ background-color: #060531;
24
+ width: 100%;
25
+ min-width: 640px;
26
+ height: 100%;
27
+ min-height: 480px;
28
+ -webkit-touch-callout: none;
29
+ -webkit-user-select: none;
30
+ -khtml-user-select: none;
31
+ -moz-user-select: none;
32
+ -ms-user-select: none;
33
+ user-select: none;
34
+ touch-action: none;
35
+ overflow: hidden;
36
+ font-family: Arial, sans-serif;
37
+ }
38
+
39
+ h3, p {
40
+ color: white;
41
+ text-align: center;
42
+ }
43
+
44
+ .node {
45
+ display: flex;
46
+ flex-direction: column;
47
+ position: absolute;
48
+ background-color: white;
49
+ border-radius: 8px;
50
+ border-bottom: 10px solid #f19331;
51
+ }
52
+
53
+ .node .titlebar {
54
+ color: white;
55
+ background-color: #f19331;
56
+ width: 100%;
57
+ height: 35px;
58
+ line-height: 35px;
59
+ text-align: center;
60
+ font-size: 16px;
61
+ font-weight: bold;
62
+ border-radius: 8px 8px 0 0;
63
+ }
64
+
65
+ .node .header {
66
+ color: #222;
67
+ text-align: left;
68
+ font-size: 14px;
69
+ margin: 12px 16px;
70
+ font-weight: bold;
71
+ }
72
+
73
+ .node .option {
74
+ color: #222;
75
+ background-color: #fff5f5;
76
+ text-align: left;
77
+ font-size: 14px;
78
+ margin: 8px 16px 0 16px;
79
+ padding: 10px;
80
+ border-radius: 8px;
81
+ }
82
+
83
+ .link {
84
+ position: absolute;
85
+ left: 0;
86
+ top: 0;
87
+ overflow: visible;
88
+ z-index: -1;
89
+ }
90
+
91
+ .link line {
92
+ pointer-events: none;
93
+ fill: #e88f56;
94
+ stroke: #e88f56;
95
+ stroke-width: 2;
96
+ }
97
+
98
+ </style>
99
+ </head>
100
+ <body>
101
+ <!-- Main diagram container -->
102
+ <div id="container">
103
+ <h3>2: Node demo</h3>
104
+ <p>How to make and style custom nodes.</p>
105
+ </div>
106
+
107
+ <!-- Total Diagram library -->
108
+ <script type="text/javascript" src="../render-node.js"></script>
109
+ <script type="text/javascript" src="../render-link.js"></script>
110
+ <script type="text/javascript" src="../manager-nodes.js"></script>
111
+ <script type="text/javascript" src="../manager-links.js"></script>
112
+ <script type="text/javascript" src="../render.js"></script>
113
+
114
+ <script>
115
+
116
+ // More advanced node definition
117
+
118
+ class NodeMoreAdvanced extends TotalDiagramNode {
119
+
120
+ constructor(args) {
121
+ super(args);
122
+
123
+ // Class
124
+ this.element.classList.add('node');
125
+
126
+ // Title bar
127
+ this.titlebar = document.createElement('div');
128
+ this.titlebar.classList.add('titlebar');
129
+ this.titlebar.innerText = args.title || 'Node';
130
+ this.element.append(this.titlebar);
131
+
132
+ // Header
133
+ this.header = document.createElement('div');
134
+ this.header.classList.add('header');
135
+ this.header.innerText = args.header || 'Header';
136
+ this.element.append(this.header);
137
+
138
+ // Options
139
+ if ('options' in args) {
140
+ args.options.forEach(text => {
141
+ const option = document.createElement('div');
142
+ option.classList.add('option');
143
+ option.innerHTML = text;
144
+ this.element.append(option);
145
+ });
146
+ }
147
+
148
+ // Size
149
+ this.setSize({width: 200, height: 300});
150
+
151
+ // Update
152
+ this.update();
153
+ }
154
+ }
155
+
156
+ // Default link definition
157
+
158
+ class LinkLine extends TotalDiagramLink {
159
+
160
+ constructor(args) {
161
+ super(args);
162
+
163
+ // SVG DOM element
164
+ this.line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
165
+ this.element.appendChild(this.line);
166
+
167
+ // Update
168
+ this.update();
169
+ }
170
+
171
+ update() {
172
+ this.line.setAttribute('x1', this.start.transform.x);
173
+ this.line.setAttribute('y1', this.start.transform.y);
174
+ this.line.setAttribute('x2', this.end.transform.x);
175
+ this.line.setAttribute('y2', this.end.transform.y);
176
+ }
177
+
178
+ }
179
+
180
+ // Start
181
+
182
+ window.onload = function() {
183
+
184
+ // Create managers
185
+ const render = new TotalDiagramRenderHTML5({
186
+ container: document.getElementById('container'),
187
+ nodes: new TotalDiagramNodesManager(),
188
+ links: new TotalDiagramLinksManager()
189
+ });
190
+
191
+ // Create first node
192
+ const node1 = new NodeMoreAdvanced({
193
+ title: 'Node #1',
194
+ header: 'Options for node 1:',
195
+ options: [
196
+ '<b>Option 1:</b> Hello',
197
+ '<b>Option 2:</b> World',
198
+ '<b>Option 3:</b> Lorem',
199
+ '<b>Option 4:</b> Ipsum'
200
+ ],
201
+ x: -200,
202
+ y: -50
203
+ });
204
+ render.nodes.add(node1);
205
+
206
+ // Create second node
207
+ const node2 = new NodeMoreAdvanced({
208
+ title: 'Node #2',
209
+ header: 'Options for node 2:',
210
+ options: [
211
+ '<b>Option 1:</b> Hello',
212
+ '<b>Option 2:</b> World',
213
+ '<b>Option 3:</b> Lorem',
214
+ '<b>Option 4:</b> Ipsum'
215
+ ],
216
+ x: 200,
217
+ y: 50
218
+ });
219
+ render.nodes.add(node2);
220
+
221
+ // Create link between them
222
+ const link1 = new LinkLine({
223
+ start: node1,
224
+ end: node2
225
+ });
226
+ render.links.add(link1);
227
+
228
+ // Move to view central 0,0
229
+ render.center();
230
+
231
+ };
232
+ </script>
233
+
234
+ </body>
235
+ </html>
@@ -0,0 +1,319 @@
1
+ <!doctype html>
2
+
3
+ <html>
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <title>Total Diagram Demo</title>
7
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
8
+ <meta http-equiv="Pragma" content="no-cache">
9
+ <meta http-equiv="Expires" content="0">
10
+ <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
11
+ <style>
12
+
13
+ html, body {
14
+ width: 100%;
15
+ height: 100%;
16
+ margin: 0;
17
+ }
18
+
19
+ #container {
20
+ position: relative;
21
+ margin: 0 auto;
22
+ display: block;
23
+ background-color: #060531;
24
+ width: 100%;
25
+ min-width: 640px;
26
+ height: 100%;
27
+ min-height: 480px;
28
+ -webkit-touch-callout: none;
29
+ -webkit-user-select: none;
30
+ -khtml-user-select: none;
31
+ -moz-user-select: none;
32
+ -ms-user-select: none;
33
+ user-select: none;
34
+ touch-action: none;
35
+ overflow: hidden;
36
+ font-family: Arial, sans-serif;
37
+ }
38
+
39
+ h3, p {
40
+ color: white;
41
+ text-align: center;
42
+ }
43
+
44
+ .node {
45
+ display: flex;
46
+ flex-direction: column;
47
+ position: absolute;
48
+ background-color: white;
49
+ border-radius: 8px;
50
+ border-bottom: 10px solid #f19331;
51
+ }
52
+
53
+ .node .titlebar {
54
+ color: white;
55
+ background-color: #f19331;
56
+ width: 100%;
57
+ height: 35px;
58
+ line-height: 35px;
59
+ text-align: center;
60
+ font-size: 16px;
61
+ font-weight: bold;
62
+ border-radius: 8px 8px 0 0;
63
+ }
64
+
65
+ .node .header {
66
+ color: #222;
67
+ text-align: left;
68
+ font-size: 14px;
69
+ margin: 12px 16px;
70
+ font-weight: bold;
71
+ }
72
+
73
+ .node .option {
74
+ color: #222;
75
+ background-color: #fff5f5;
76
+ text-align: left;
77
+ font-size: 14px;
78
+ margin: 8px 16px 0 16px;
79
+ padding: 10px;
80
+ border-radius: 8px;
81
+ }
82
+
83
+ .socket {
84
+ position: absolute;
85
+ width: 8px;
86
+ height: 8px;
87
+ background-color: #fff5f5;
88
+ border: 2px solid #f19331;
89
+ border-radius: 50%;
90
+ }
91
+
92
+ .link {
93
+ position: absolute;
94
+ left: 0;
95
+ top: 0;
96
+ overflow: visible;
97
+ z-index: -1;
98
+ }
99
+
100
+ .link path {
101
+ pointer-events: none;
102
+ fill: none;
103
+ stroke: #e88f56;
104
+ stroke-width: 2;
105
+ }
106
+
107
+ </style>
108
+ </head>
109
+ <body>
110
+ <!-- Main diagram container -->
111
+ <div id="container">
112
+ <h3>3: Link demo</h3>
113
+ <p>How to make custom and stylized link connected to node's socket.</p>
114
+ </div>
115
+
116
+ <!-- Total Diagram library -->
117
+ <script type="text/javascript" src="../render-node.js"></script>
118
+ <script type="text/javascript" src="../render-link.js"></script>
119
+ <script type="text/javascript" src="../manager-nodes.js"></script>
120
+ <script type="text/javascript" src="../manager-links.js"></script>
121
+ <script type="text/javascript" src="../render.js"></script>
122
+
123
+ <script>
124
+
125
+ // Socket definition
126
+
127
+ class SocketPoint {
128
+
129
+ constructor(args) {
130
+
131
+ // Store parent
132
+ this.parent = args.parent;
133
+
134
+ // Main element
135
+ this.element = document.createElement('div');
136
+ this.element.classList.add('socket');
137
+ args.parent.element.append(this.element);
138
+
139
+ // Position
140
+ this.position = args.pos;
141
+ if ('top' in args.pos) this.element.style.top = args.pos.top + 'px';
142
+ if ('left' in args.pos) this.element.style.left = args.pos.left + 'px';
143
+ if ('right' in args.pos) this.element.style.right = args.pos.right + 'px';
144
+
145
+ }
146
+
147
+ getWorldPosition() {
148
+ let x = 0;
149
+ let y = this.parent.transform.y - (this.parent.transform.h / 2) + this.position.top + 5;
150
+ if ('left' in this.position) {
151
+ x = this.parent.transform.x - (this.parent.transform.w / 2) + this.position.left + 5;
152
+ }
153
+ else if ('right' in this.position) {
154
+ x = this.parent.transform.x + (this.parent.transform.w / 2) + this.position.right + 5;
155
+ }
156
+ return {x, y};
157
+ }
158
+
159
+ }
160
+
161
+ // More advanced node definition
162
+
163
+ class NodeMoreAdvanced extends TotalDiagramNode {
164
+
165
+ constructor(args) {
166
+ super(args);
167
+
168
+ // Class
169
+ this.element.classList.add('node');
170
+
171
+ // Title bar
172
+ this.titlebar = document.createElement('div');
173
+ this.titlebar.classList.add('titlebar');
174
+ this.titlebar.innerText = args.title || 'Node';
175
+ this.element.append(this.titlebar);
176
+
177
+ // Header
178
+ this.header = document.createElement('div');
179
+ this.header.classList.add('header');
180
+ this.header.innerText = args.header || 'Header';
181
+ this.element.append(this.header);
182
+
183
+ // Options
184
+ if ('options' in args) {
185
+ args.options.forEach(text => {
186
+ const option = document.createElement('div');
187
+ option.classList.add('option');
188
+ option.innerHTML = text;
189
+ this.element.append(option);
190
+ });
191
+ }
192
+
193
+ // Sockets
194
+ this.socketIn = new SocketPoint({
195
+ parent: this,
196
+ pos: {left: -5, top: 11}
197
+ });
198
+ this.socketOut = new SocketPoint({
199
+ parent: this,
200
+ pos: {right: -5, top: 11}
201
+ });
202
+
203
+ // Size
204
+ this.setSize({width: 200, height: 300});
205
+
206
+ // Update
207
+ this.update();
208
+ }
209
+ }
210
+
211
+ // Bezier curve link definition
212
+
213
+ class LinkBezier extends TotalDiagramLink {
214
+
215
+ constructor(args) {
216
+ super(args);
217
+
218
+ // SVG DOM element
219
+ this.path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
220
+ this.element.appendChild(this.path);
221
+
222
+ // Curvature
223
+ this.curvature = 0.5;
224
+
225
+ // Update
226
+ this.update();
227
+ }
228
+
229
+ update() {
230
+
231
+ // Start & end position
232
+ const startPos = this.start.socketOut.getWorldPosition();
233
+ const endPos = this.end.socketIn.getWorldPosition();
234
+ let x1 = 0;
235
+ let y1 = 0;
236
+ let x2 = 0;
237
+ let y2 = 0;
238
+
239
+ // Left -> right
240
+ if (this.start.transform.x <= this.end.transform.x) {
241
+ x1 = startPos.x;
242
+ y1 = startPos.y;
243
+ x2 = endPos.x;
244
+ y2 = endPos.y;
245
+ }
246
+
247
+ // Right -> left
248
+ else {
249
+ x1 = endPos.x;
250
+ y1 = endPos.y;
251
+ x2 = startPos.x;
252
+ y2 = startPos.y;
253
+ }
254
+
255
+ // Calculate & render
256
+ const hx1 = x1 + Math.abs(x2 - x1) * this.curvature;
257
+ const hx2 = x2 - Math.abs(x2 - x1) * this.curvature;
258
+ const angle = Math.atan2(y2 - y1, ((x2 + hx2) / 2) - ((x1 + hx1) / 2));
259
+ this.path.setAttribute('d', `M ${x1} ${y1} C ${hx1} ${y1} ${hx2} ${y2} ${x2} ${y2}`);
260
+ }
261
+
262
+ }
263
+
264
+ // Start
265
+
266
+ window.onload = function() {
267
+
268
+ // Create managers
269
+ const render = new TotalDiagramRenderHTML5({
270
+ container: document.getElementById('container'),
271
+ nodes: new TotalDiagramNodesManager(),
272
+ links: new TotalDiagramLinksManager()
273
+ });
274
+
275
+ // Create first node
276
+ const node1 = new NodeMoreAdvanced({
277
+ title: 'Node #1',
278
+ header: 'Options for node 1:',
279
+ options: [
280
+ '<b>Option 1:</b> Hello',
281
+ '<b>Option 2:</b> World',
282
+ '<b>Option 3:</b> Lorem',
283
+ '<b>Option 4:</b> Ipsum'
284
+ ],
285
+ x: -200,
286
+ y: -50
287
+ });
288
+ render.nodes.add(node1);
289
+
290
+ // Create second node
291
+ const node2 = new NodeMoreAdvanced({
292
+ title: 'Node #2',
293
+ header: 'Options for node 2:',
294
+ options: [
295
+ '<b>Option 1:</b> Hello',
296
+ '<b>Option 2:</b> World',
297
+ '<b>Option 3:</b> Lorem',
298
+ '<b>Option 4:</b> Ipsum'
299
+ ],
300
+ x: 200,
301
+ y: 50
302
+ });
303
+ render.nodes.add(node2);
304
+
305
+ // Create link between them
306
+ const link1 = new LinkBezier({
307
+ start: node1,
308
+ end: node2
309
+ });
310
+ render.links.add(link1);
311
+
312
+ // Move to view central 0,0
313
+ render.center();
314
+
315
+ };
316
+ </script>
317
+
318
+ </body>
319
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "total-diagram",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "Simple, powerful, extensible and fast JavaScript/ES8 diagram renderer for web browsers.",
5
5
  "license": "MIT",
6
6
  "scripts": {
@@ -9,9 +9,9 @@
9
9
  "start": "npm-run-all build"
10
10
  },
11
11
  "devDependencies": {
12
- "ejs": "^3.1.9",
13
- "fs": "^0.0.1-security",
14
12
  "npm-run-all": "^4.1.5",
13
+ "fs": "^0.0.1-security",
14
+ "ejs": "^3.1.9",
15
15
  "terser": "^5.17.3"
16
16
  }
17
17
  }
@@ -146,6 +146,7 @@ class TotalDiagramNode {
146
146
  if ('maxWidth' in size) this.transform.wmax = size.maxWidth;
147
147
  if ('maxHeight' in size) this.transform.hmax = size.maxHeight;
148
148
  if ('border' in size) this.transform.border = size.border;
149
+ else this.transform.border = parseInt(getComputedStyle(this.element, null).getPropertyValue('border-left-width').replace('px', '')) || 0;
149
150
  this.setOrigin();
150
151
  this.element.style.width = this.transform.w + 'px';
151
152
  this.element.style.height = this.transform.h + 'px';
package/render.js CHANGED
@@ -64,6 +64,8 @@ class TotalDiagramRenderHTML5 {
64
64
 
65
65
  /**
66
66
  * Perform pan
67
+ * @param deltaX: change x
68
+ * @param deltaY: change y
67
69
  */
68
70
 
69
71
  pan(deltaX, deltaY) {
@@ -74,6 +76,10 @@ class TotalDiagramRenderHTML5 {
74
76
 
75
77
  /**
76
78
  * Perform zoom
79
+ * @param x: x position
80
+ * @param y: y position
81
+ * @param deltaZ: zoom change
82
+ * @param factorZ: multplier
77
83
  */
78
84
 
79
85
  zoom(x, y, deltaZ, factorZ) {
@@ -92,6 +98,11 @@ class TotalDiagramRenderHTML5 {
92
98
 
93
99
  /**
94
100
  * Perform mobile zoom
101
+ * @param x1: finger 1 x position
102
+ * @param y1: finger 1 y position
103
+ * @param x2: finger 2 x position
104
+ * @param y2: finger 2 y position
105
+ * @param deltaZ: zoom change
95
106
  */
96
107
 
97
108
  pinchZoom(x1, y1, x2, y2, deltaZ) {
@@ -110,7 +121,7 @@ class TotalDiagramRenderHTML5 {
110
121
 
111
122
  /**
112
123
  * Convert window coordinates to world
113
- * {x: ..., y: ...}
124
+ * @param coords {x: <Number>, y: <Number>}
114
125
  */
115
126
 
116
127
  screen2World(coords) {
@@ -122,7 +133,7 @@ class TotalDiagramRenderHTML5 {
122
133
 
123
134
  /**
124
135
  * Convert world coordinates to window
125
- * {x: ..., y: ...}
136
+ * @param coords {x: <Number>, y: <Number>}
126
137
  */
127
138
 
128
139
  world2Screen(coords) {
@@ -134,9 +145,9 @@ class TotalDiagramRenderHTML5 {
134
145
 
135
146
  /**
136
147
  * Reset to origin
137
- * z: 'none' (don't zoom) | 'reset': standard zoom | 'focus': zoom to node size
138
- * animation: 'hard' | 'smooth'
139
- * rect: {width, height: dimensions of the focus target}
148
+ * @param z: 'none' (don't zoom) | 'reset': standard zoom | 'focus': zoom to node size
149
+ * @param animation: 'hard' | 'smooth'
150
+ * @param rect: {width, height: dimensions of the focus target}
140
151
  */
141
152
 
142
153
  center(coords = {x: 0, y: 0}, z = 'none', animation = 'hard', rect = null) {
@@ -178,11 +189,11 @@ class TotalDiagramRenderHTML5 {
178
189
 
179
190
  /**
180
191
  * Render update
192
+ * Note: use 'transform' since PYQt6 doesn't support separate 'translate' and 'scale' yet
181
193
  */
182
194
 
183
195
  update() {
184
196
  // Calculate board's position
185
- // Note: using 'transform' since PYQt6 doesn't support new css 'translate' and 'scale' yet
186
197
  this.board.style.transform = `translate(${this.offset.x}px, ${this.offset.y}px) scale(${this.offset.z})`;
187
198
  }
188
199
 
Binary file
@@ -1,5 +1,5 @@
1
- <%- await minjs('node.js') %>
2
- <%- await minjs('nodes.js') %>
3
- <%- await minjs('link.js') %>
4
- <%- await minjs('links.js') %>
1
+ <%- await minjs('render-node.js') %>
2
+ <%- await minjs('manager-nodes.js') %>
3
+ <%- await minjs('render-link.js') %>
4
+ <%- await minjs('manager-links.js') %>
5
5
  <%- await minjs('render.js') %>
File without changes
File without changes
File without changes