total-diagram 0.9.2
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/.github/workflows/build.yml +25 -0
- package/.github/workflows/deploy.yml +29 -0
- package/LICENSE +21 -0
- package/README.md +34 -0
- package/build.js +23 -0
- package/demo.html +155 -0
- package/link.js +82 -0
- package/links.js +103 -0
- package/node.js +297 -0
- package/nodes.js +111 -0
- package/package.json +17 -0
- package/render.js +199 -0
- package/total-diagram-logo.png +0 -0
- package/total-diagram.js.ejs +5 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Build
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout repo
|
|
14
|
+
uses: actions/checkout@v3
|
|
15
|
+
|
|
16
|
+
- name: Use Node.js
|
|
17
|
+
uses: actions/setup-node@v3
|
|
18
|
+
with:
|
|
19
|
+
node-version: "16.x"
|
|
20
|
+
|
|
21
|
+
- name: Install
|
|
22
|
+
run: npm install
|
|
23
|
+
|
|
24
|
+
- name: Build
|
|
25
|
+
run: npm run build --if-present
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Deploy
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
deploy:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: Checkout repo
|
|
12
|
+
uses: actions/checkout@v3
|
|
13
|
+
|
|
14
|
+
- name: Use Node.js
|
|
15
|
+
uses: actions/setup-node@v3
|
|
16
|
+
with:
|
|
17
|
+
node-version: "16.x"
|
|
18
|
+
registry-url: 'https://registry.npmjs.org'
|
|
19
|
+
|
|
20
|
+
- name: Install
|
|
21
|
+
run: npm install
|
|
22
|
+
|
|
23
|
+
- name: Build
|
|
24
|
+
run: npm run build --if-present
|
|
25
|
+
|
|
26
|
+
- name: Publish
|
|
27
|
+
run: npm publish
|
|
28
|
+
env:
|
|
29
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-2023 Dariusz Dawidowski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/dariuszdawidowski/total-diagram/main/total-diagram-logo.png">
|
|
3
|
+
</p>
|
|
4
|
+
<h1 align="center">
|
|
5
|
+
Total Diagram
|
|
6
|
+
</h1>
|
|
7
|
+
<p align="center">
|
|
8
|
+
Simple, powerful, extensible and fast JavaScript/ES8 diagram renderer for web browsers.
|
|
9
|
+
<p>
|
|
10
|
+
<p align="center">
|
|
11
|
+
v 0.9.2
|
|
12
|
+
<p>
|
|
13
|
+
|
|
14
|
+
[](https://github.com/dariuszdawidowski/total-diagram/actions/workflows/build.yml)
|
|
15
|
+
[](./LICENSE)
|
|
16
|
+
|
|
17
|
+
# About
|
|
18
|
+
|
|
19
|
+
A library for rendering diagrams consisting of nodes and links.
|
|
20
|
+
Designed for simplicity, it can be the basis for creating a diagramming application or data representation on a website.
|
|
21
|
+
For more details look into self-explanatory demo.html.
|
|
22
|
+
|
|
23
|
+
# Features
|
|
24
|
+
|
|
25
|
+
- Vanilla JavaScript/ES8
|
|
26
|
+
- No dependencies
|
|
27
|
+
|
|
28
|
+
# Build minified bundle file
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install
|
|
32
|
+
npm run build
|
|
33
|
+
```
|
|
34
|
+
Note: This is browser-centric vanilla JavaScript library, npm is only used to minify and bundle files into one.
|
package/build.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build script
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const ejs = require('ejs');
|
|
7
|
+
const { readFile } = require('fs').promises;
|
|
8
|
+
const { minify } = require('terser');
|
|
9
|
+
|
|
10
|
+
const minjs = async (filePath) => {
|
|
11
|
+
try {
|
|
12
|
+
const inputCode = await readFile(filePath, 'utf8');
|
|
13
|
+
const minifiedCode = (await minify(inputCode)).code;
|
|
14
|
+
return minifiedCode;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
console.error(`Error minifying js ${filePath}:`, error);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
ejs.render(fs.readFileSync('total-diagram.js.ejs', 'utf8'), { minjs }, { async: true })
|
|
23
|
+
.then(output => fs.writeFileSync('total-diagram.js', output, 'utf8'));
|
package/demo.html
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
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
|
+
}
|
|
37
|
+
|
|
38
|
+
.node {
|
|
39
|
+
display: flex;
|
|
40
|
+
position: absolute;
|
|
41
|
+
background-color: #e88f56;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.link {
|
|
45
|
+
position: absolute;
|
|
46
|
+
left: 0;
|
|
47
|
+
top: 0;
|
|
48
|
+
overflow: visible;
|
|
49
|
+
z-index: -1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.link line {
|
|
53
|
+
pointer-events: none;
|
|
54
|
+
fill: #fff;
|
|
55
|
+
stroke: #fff;
|
|
56
|
+
stroke-width: 2;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
</style>
|
|
60
|
+
</head>
|
|
61
|
+
<body>
|
|
62
|
+
<!-- Main diagram container -->
|
|
63
|
+
<div id="container"></div>
|
|
64
|
+
|
|
65
|
+
<!-- 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>
|
|
70
|
+
<script type="text/javascript" src="render.js"></script>
|
|
71
|
+
|
|
72
|
+
<script>
|
|
73
|
+
|
|
74
|
+
// Default node definition
|
|
75
|
+
|
|
76
|
+
class NodeSquare extends TotalDiagramNode {
|
|
77
|
+
|
|
78
|
+
constructor(args) {
|
|
79
|
+
super(args);
|
|
80
|
+
|
|
81
|
+
// Class
|
|
82
|
+
this.element.classList.add('node');
|
|
83
|
+
|
|
84
|
+
// Size
|
|
85
|
+
this.setSize({width: 50, height: 50});
|
|
86
|
+
|
|
87
|
+
// Update
|
|
88
|
+
this.update();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Default link definition
|
|
93
|
+
|
|
94
|
+
class LinkLine extends TotalDiagramLink {
|
|
95
|
+
|
|
96
|
+
constructor(args) {
|
|
97
|
+
super(args);
|
|
98
|
+
|
|
99
|
+
// SVG DOM element
|
|
100
|
+
this.line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
101
|
+
this.element.appendChild(this.line);
|
|
102
|
+
|
|
103
|
+
// Update
|
|
104
|
+
this.update();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
update() {
|
|
108
|
+
this.line.setAttribute('x1', this.start.transform.x);
|
|
109
|
+
this.line.setAttribute('y1', this.start.transform.y);
|
|
110
|
+
this.line.setAttribute('x2', this.end.transform.x);
|
|
111
|
+
this.line.setAttribute('y2', this.end.transform.y);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Start
|
|
117
|
+
|
|
118
|
+
window.onload = function() {
|
|
119
|
+
|
|
120
|
+
// Create managers
|
|
121
|
+
const render = new TotalDiagramRenderHTML5({
|
|
122
|
+
container: document.getElementById('container'),
|
|
123
|
+
nodes: new TotalDiagramNodesManager(),
|
|
124
|
+
links: new TotalDiagramLinksManager()
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Create first node
|
|
128
|
+
const node1 = new NodeSquare({
|
|
129
|
+
x: -150,
|
|
130
|
+
y: -150
|
|
131
|
+
});
|
|
132
|
+
render.nodes.add(node1);
|
|
133
|
+
|
|
134
|
+
// Create second node
|
|
135
|
+
const node2 = new NodeSquare({
|
|
136
|
+
x: 150,
|
|
137
|
+
y: 150
|
|
138
|
+
});
|
|
139
|
+
render.nodes.add(node2);
|
|
140
|
+
|
|
141
|
+
// Create link between them
|
|
142
|
+
const link1 = new LinkLine({
|
|
143
|
+
start: node1,
|
|
144
|
+
end: node2
|
|
145
|
+
});
|
|
146
|
+
render.links.add(link1);
|
|
147
|
+
|
|
148
|
+
// Move to view central 0,0
|
|
149
|
+
render.center();
|
|
150
|
+
|
|
151
|
+
};
|
|
152
|
+
</script>
|
|
153
|
+
|
|
154
|
+
</body>
|
|
155
|
+
</html>
|
package/link.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/***************************************************************************************************
|
|
2
|
+
* *
|
|
3
|
+
* Total Diagram Link *
|
|
4
|
+
* \___________________u.. Base class for diagram link *
|
|
5
|
+
* ( ________________ .-` MIT License *
|
|
6
|
+
* '' '' Copyright (c) 2020-2023 Dariusz Dawidowski *
|
|
7
|
+
* *
|
|
8
|
+
**************************************************************************************************/
|
|
9
|
+
|
|
10
|
+
class TotalDiagramLink {
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Constructor
|
|
14
|
+
* @param args.start <TotalDiagramNode>: Start node
|
|
15
|
+
* @param args.end <TotalDiagramNode>: End node
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
constructor(args) {
|
|
19
|
+
|
|
20
|
+
// ID
|
|
21
|
+
this.id = 'id' in args ? args.id : crypto.randomUUID();
|
|
22
|
+
|
|
23
|
+
// Start Node
|
|
24
|
+
this.start = args.start;
|
|
25
|
+
this.start.addLink(this);
|
|
26
|
+
|
|
27
|
+
// End Node
|
|
28
|
+
this.end = args.end;
|
|
29
|
+
this.end.addLink(this);
|
|
30
|
+
|
|
31
|
+
// SVG DOM element
|
|
32
|
+
this.element = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
33
|
+
this.element.classList.add('link');
|
|
34
|
+
|
|
35
|
+
// Copy ID onto DOM element
|
|
36
|
+
this.element.dataset.id = this.id;
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Destructor
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
destructor() {
|
|
45
|
+
// Remove references in related nodes
|
|
46
|
+
if (this.start) this.start.delLink(this);
|
|
47
|
+
if (this.end) this.end.delLink(this);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Serialization
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
serialize() {
|
|
55
|
+
return {
|
|
56
|
+
'id': this.id,
|
|
57
|
+
'type': this.constructor.name,
|
|
58
|
+
'start': this.start.id,
|
|
59
|
+
'end': this.end.id,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Make link transparent
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
transparent(percent) {
|
|
68
|
+
if (percent == 100)
|
|
69
|
+
this.element.style.opacity = null;
|
|
70
|
+
else
|
|
71
|
+
this.element.style.opacity = percent / 100;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Update position
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
update() {
|
|
79
|
+
/*** Overload ***/
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
}
|
package/links.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/***************************************************************************************************
|
|
2
|
+
* *
|
|
3
|
+
* ___ ___ Total Diagram Links Manager *
|
|
4
|
+
* (o,o) (-,-) Create, delete and find links between nodes on the board. *
|
|
5
|
+
* /)''') ('''(\ MIT License *
|
|
6
|
+
* -----m-m-----m-m----- Copyright (c) 2020-2023 Dariusz Dawidowski *
|
|
7
|
+
* *
|
|
8
|
+
**************************************************************************************************/
|
|
9
|
+
|
|
10
|
+
class TotalDiagramLinksManager {
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Constructor
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
|
|
18
|
+
// Main manager reference
|
|
19
|
+
this.render = null;
|
|
20
|
+
|
|
21
|
+
// Array for all links
|
|
22
|
+
this.list = [];
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Add a link between two nodes
|
|
28
|
+
* @param link <TotalDiagramLink>: Link object
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
add(link) {
|
|
32
|
+
|
|
33
|
+
// Check if nodes exists
|
|
34
|
+
if (!link.start || !link.end) return null;
|
|
35
|
+
|
|
36
|
+
// Store link
|
|
37
|
+
this.list.push(link);
|
|
38
|
+
|
|
39
|
+
// Add to DOM
|
|
40
|
+
this.render.board.append(link.element);
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Del link from board
|
|
46
|
+
* @param link <TotalDiagramLink>: Link object
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
del(link) {
|
|
50
|
+
|
|
51
|
+
if (link != '*') {
|
|
52
|
+
// Local link's destructor
|
|
53
|
+
link.destructor();
|
|
54
|
+
|
|
55
|
+
// Remove from DOM
|
|
56
|
+
link.element.remove()
|
|
57
|
+
|
|
58
|
+
// Remove from list
|
|
59
|
+
const index = this.list.indexOf(link);
|
|
60
|
+
if (index !== -1) this.list.splice(index, 1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Remove all links
|
|
64
|
+
else {
|
|
65
|
+
this.list.length = 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get link(s)
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
get(link = 0, node2 = null) {
|
|
75
|
+
|
|
76
|
+
// Find link betwen two nodes
|
|
77
|
+
if (node2) {
|
|
78
|
+
for (const link1 of link.links.get()) {
|
|
79
|
+
for (const link2 of node2.links.get()) {
|
|
80
|
+
if (link1.id == link2.id) return link1;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// All links
|
|
87
|
+
else if (link === 0) return this.list;
|
|
88
|
+
|
|
89
|
+
// Find one link by giving ID
|
|
90
|
+
else if (typeof(link) == 'string') return this.list.find(n => n.element.dataset.id == link);
|
|
91
|
+
|
|
92
|
+
// Find all links type by giving class
|
|
93
|
+
else if (typeof(link) == 'function') return this.list.filter(l => l instanceof link);
|
|
94
|
+
|
|
95
|
+
// Find one link by giving DOM element
|
|
96
|
+
else if (typeof(link) == 'object' && 'dataset' in link) return this.get(link.dataset.id);
|
|
97
|
+
|
|
98
|
+
// Not found
|
|
99
|
+
return null;
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
}
|
package/node.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/***************************************************************************************************
|
|
2
|
+
* *
|
|
3
|
+
* /\_/\ Total Diagram Node *
|
|
4
|
+
* (=^o^=)/\ Base class for diagram node *
|
|
5
|
+
* (___)__/ MIT License *
|
|
6
|
+
* ~~~~~~~~~~~~~~~ Copyright (c) 2020-2023 Dariusz Dawidowski *
|
|
7
|
+
* *
|
|
8
|
+
**************************************************************************************************/
|
|
9
|
+
|
|
10
|
+
class TotalDiagramNode {
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Constructor
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
constructor(args = {}) {
|
|
17
|
+
|
|
18
|
+
// ID
|
|
19
|
+
this.id = 'id' in args ? args.id : crypto.randomUUID();
|
|
20
|
+
|
|
21
|
+
// Human readable name
|
|
22
|
+
this.name = 'name' in args ? args.name : null;
|
|
23
|
+
|
|
24
|
+
// Position, origin, dimensions
|
|
25
|
+
this.transform = {
|
|
26
|
+
// Global x position
|
|
27
|
+
x: 'x' in args ? args.x : 0,
|
|
28
|
+
// Global y position
|
|
29
|
+
y: 'y' in args ? args.y : 0,
|
|
30
|
+
|
|
31
|
+
// Border size
|
|
32
|
+
border: 0,
|
|
33
|
+
|
|
34
|
+
// Z-Sorting
|
|
35
|
+
zindex: 'zindex' in args ? args.zindex : 0,
|
|
36
|
+
|
|
37
|
+
// Local center x position
|
|
38
|
+
ox: 0,
|
|
39
|
+
// Local center y position
|
|
40
|
+
oy: 0,
|
|
41
|
+
|
|
42
|
+
// Width of boundings
|
|
43
|
+
w: 0,
|
|
44
|
+
// Height of boundings
|
|
45
|
+
h: 0,
|
|
46
|
+
// Min width of boundings
|
|
47
|
+
wmin: 64,
|
|
48
|
+
// Min height of boundings
|
|
49
|
+
hmin: 64,
|
|
50
|
+
// Max width of boundings
|
|
51
|
+
wmax: 1024,
|
|
52
|
+
// Max height of boundings
|
|
53
|
+
hmax: 1024,
|
|
54
|
+
|
|
55
|
+
// Identity
|
|
56
|
+
clear: () => {
|
|
57
|
+
this.transform.x = 0;
|
|
58
|
+
this.transform.y = 0;
|
|
59
|
+
this.transform.zindex = 0;
|
|
60
|
+
this.element.style.zIndex = 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Links
|
|
66
|
+
this.links = {
|
|
67
|
+
|
|
68
|
+
list: [], // [TotalDiagramLink, ...]
|
|
69
|
+
|
|
70
|
+
// Assign to link
|
|
71
|
+
add: (link) => {
|
|
72
|
+
this.links.list.push(link);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// Unassign from a link
|
|
76
|
+
del: (link) => {
|
|
77
|
+
const index = this.links.list.indexOf(link);
|
|
78
|
+
if (index !== -1) this.links.list.splice(index, 1);
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// Get assigned link
|
|
82
|
+
get: (id = null) => {
|
|
83
|
+
// All links
|
|
84
|
+
if (id === null) return this.links.list;
|
|
85
|
+
|
|
86
|
+
// Find one link by ID
|
|
87
|
+
else if (typeof(id) == 'string') {
|
|
88
|
+
return this.links.list.find(l => l.id == id);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return null;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// Update assigned links
|
|
95
|
+
update: () => {
|
|
96
|
+
this.links.list.forEach(link => link.update());
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Node DOM element
|
|
102
|
+
this.element = document.createElement('div');
|
|
103
|
+
this.element.classList.add('total-diagram-node');
|
|
104
|
+
this.element.style.zIndex = this.transform.zindex;
|
|
105
|
+
|
|
106
|
+
// Set or generate ID
|
|
107
|
+
this.element.dataset.id = this.id;
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Destructor
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
destructor() {
|
|
116
|
+
/* Overload */
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Init after node is added into DOM tree
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
awake() {
|
|
124
|
+
/* Overload */
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Init after all scene is created and updated once
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
start() {
|
|
132
|
+
/* Overload */
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Size
|
|
137
|
+
* {width: <Number>, height: <Number>, minWidth: <Number>, minHeight: <Number>, maxWidth: <Number>, maxHeight: <Number>, border: [Number]}
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
setSize(size) {
|
|
141
|
+
this.transform.w = size.width;
|
|
142
|
+
this.transform.h = size.height;
|
|
143
|
+
if ('minWidth' in size) this.transform.wmin = size.minWidth;
|
|
144
|
+
if ('minHeight' in size) this.transform.hmin = size.minHeight;
|
|
145
|
+
if ('maxWidth' in size) this.transform.wmax = size.maxWidth;
|
|
146
|
+
if ('maxHeight' in size) this.transform.hmax = size.maxHeight;
|
|
147
|
+
if ('border' in size) this.transform.border = size.border;
|
|
148
|
+
this.setOrigin();
|
|
149
|
+
this.element.style.width = this.transform.w + 'px';
|
|
150
|
+
this.element.style.height = this.transform.h + 'px';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
getSize() {
|
|
154
|
+
return {
|
|
155
|
+
width: this.transform.w,
|
|
156
|
+
height: this.transform.h,
|
|
157
|
+
minWidth: this.transform.wmin,
|
|
158
|
+
minHeight: this.transform.hmin,
|
|
159
|
+
maxWidth: this.transform.wmax,
|
|
160
|
+
maxHeight: this.transform.hmax
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Set origin (local center point of node)
|
|
166
|
+
* Override this to make different origin than middle point
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
setOrigin() {
|
|
170
|
+
this.transform.ox = this.transform.w / 2;
|
|
171
|
+
this.transform.oy = this.transform.h / 2;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Set position
|
|
176
|
+
* transform {x: float, y: float}
|
|
177
|
+
*/
|
|
178
|
+
|
|
179
|
+
setPosition(transform) {
|
|
180
|
+
this.transform.x = transform.x;
|
|
181
|
+
this.transform.y = transform.y;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get position
|
|
186
|
+
* transform {x: float, y: float}
|
|
187
|
+
*/
|
|
188
|
+
|
|
189
|
+
getPosition() {
|
|
190
|
+
return {x: this.transform.x, y: this.transform.y};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Change position
|
|
195
|
+
* transform {x: float, y: float}
|
|
196
|
+
*/
|
|
197
|
+
|
|
198
|
+
addPosition(transform) {
|
|
199
|
+
this.transform.x += transform.x;
|
|
200
|
+
this.transform.y += transform.y;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Change position
|
|
205
|
+
* transform {x: float, y: float}
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
subPosition(transform) {
|
|
209
|
+
this.transform.x -= transform.x;
|
|
210
|
+
this.transform.y -= transform.y;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Add link
|
|
215
|
+
* link: TotalDiagramLink object
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
addLink(link) {
|
|
219
|
+
this.links.add(link);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Delete link
|
|
224
|
+
* link: TotalDiagramLink object
|
|
225
|
+
*/
|
|
226
|
+
|
|
227
|
+
delLink(link) {
|
|
228
|
+
this.links.del(link);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Style
|
|
233
|
+
*/
|
|
234
|
+
|
|
235
|
+
setStyle(key, value = null) {
|
|
236
|
+
if (value !== null) this.element.style[key] = value;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
getStyle(key = null) {
|
|
240
|
+
if (key !== null) return this.element.style[key];
|
|
241
|
+
else return this.element.style;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Make node transparent
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
transparent(percent) {
|
|
249
|
+
if (percent == 100)
|
|
250
|
+
this.element.style.opacity = null;
|
|
251
|
+
else
|
|
252
|
+
this.element.style.opacity = percent / 100;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Enable/disable smooth css animation
|
|
257
|
+
*/
|
|
258
|
+
|
|
259
|
+
animated(state = true) {
|
|
260
|
+
this.element.style.transition = state ? 'transform 0.5s ease-in-out' : null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Serialization
|
|
265
|
+
*/
|
|
266
|
+
|
|
267
|
+
serialize() {
|
|
268
|
+
return {
|
|
269
|
+
id: this.id,
|
|
270
|
+
type: this.constructor.name,
|
|
271
|
+
x: this.transform.x,
|
|
272
|
+
y: this.transform.y,
|
|
273
|
+
w: this.transform.w,
|
|
274
|
+
h: this.transform.h,
|
|
275
|
+
zindex: this.transform.zindex
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Set z-index
|
|
281
|
+
*/
|
|
282
|
+
|
|
283
|
+
setSortingZ(zindex) {
|
|
284
|
+
this.transform.zindex = zindex;
|
|
285
|
+
this.element.style.zIndex = zindex;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Update (everyframe when something is changed e.g. move)
|
|
290
|
+
*/
|
|
291
|
+
|
|
292
|
+
update() {
|
|
293
|
+
// Calculate position (PYQt6 doesn't support separate css translate yet)
|
|
294
|
+
this.element.style.transform = `translate(${this.transform.x - this.transform.ox}px, ${this.transform.y - this.transform.oy}px)`;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
}
|
package/nodes.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/***************************************************************************************************
|
|
2
|
+
* *
|
|
3
|
+
* ____(__) Total Diagram Nodes Manager *
|
|
4
|
+
* /| oo Create, delete and find nodes on the board. *
|
|
5
|
+
* * | _ (..)-* MIT License *
|
|
6
|
+
* v || || v Copyright (c) 2020-2023 Dariusz Dawidowski *
|
|
7
|
+
* '' ' ^^ ^^ ' '' *
|
|
8
|
+
**************************************************************************************************/
|
|
9
|
+
|
|
10
|
+
class TotalDiagramNodesManager {
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Constructor
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
|
|
18
|
+
// Main manager reference
|
|
19
|
+
this.render = null;
|
|
20
|
+
|
|
21
|
+
// Array for all nodes
|
|
22
|
+
this.list = [];
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Add node to scene
|
|
28
|
+
* @param node <TotalDiagramNode>: Node object
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
add(node) {
|
|
32
|
+
|
|
33
|
+
// Add to list
|
|
34
|
+
this.list.push(node);
|
|
35
|
+
|
|
36
|
+
// Add to DOM
|
|
37
|
+
this.render.board.append(node.element);
|
|
38
|
+
|
|
39
|
+
// Call node awake function sice it's added into DOM tree
|
|
40
|
+
node.awake();
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Del object from scene
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
del(node) {
|
|
49
|
+
|
|
50
|
+
// Single node
|
|
51
|
+
if (node != '*') {
|
|
52
|
+
|
|
53
|
+
// First delete associated links on the copy of links list
|
|
54
|
+
let linksToDelete = node.links.get().slice();
|
|
55
|
+
while (linksToDelete.length) {
|
|
56
|
+
const link = linksToDelete.pop();
|
|
57
|
+
this.render.links.del(link);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Local node's destructor
|
|
61
|
+
node.destructor();
|
|
62
|
+
|
|
63
|
+
// Remove from DOM
|
|
64
|
+
node.element.remove();
|
|
65
|
+
|
|
66
|
+
// Remove from list
|
|
67
|
+
const index = this.list.indexOf(node);
|
|
68
|
+
if (index !== -1) this.list.splice(index, 1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Clear all on the list
|
|
72
|
+
else if (node == '*') {
|
|
73
|
+
this.list.length = 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get nodes from scene
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
get(node = 0) {
|
|
83
|
+
|
|
84
|
+
// Find one node by giving DOM element
|
|
85
|
+
if (node != null && typeof(node) == 'object') {
|
|
86
|
+
// Traverse DOM
|
|
87
|
+
let target = node;
|
|
88
|
+
while (target.parentNode) {
|
|
89
|
+
if ('classList' in target && target.classList.contains('total-diagram-node')) {
|
|
90
|
+
// Found node
|
|
91
|
+
return this.get(target.dataset.id);
|
|
92
|
+
}
|
|
93
|
+
target = target.parentNode;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// All nodes
|
|
98
|
+
else if (node === 0) return this.list;
|
|
99
|
+
|
|
100
|
+
// Find all nodes type by giving class
|
|
101
|
+
else if (typeof(node) == 'function') return this.list.filter(n => n instanceof node);
|
|
102
|
+
|
|
103
|
+
// Find one node by giving ID
|
|
104
|
+
else if (typeof(node) == 'string') return this.list.find(n => n.id == node);
|
|
105
|
+
|
|
106
|
+
// Not found
|
|
107
|
+
return null;
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "total-diagram",
|
|
3
|
+
"version": "0.9.2",
|
|
4
|
+
"description": "Simple, powerful, extensible and fast JavaScript/ES8 diagram renderer for web browsers.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build:bundle": "node build.js",
|
|
8
|
+
"build": "npm-run-all build:bundle",
|
|
9
|
+
"start": "npm-run-all build"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"ejs": "^3.1.9",
|
|
13
|
+
"fs": "^0.0.1-security",
|
|
14
|
+
"npm-run-all": "^4.1.5",
|
|
15
|
+
"terser": "^5.17.3"
|
|
16
|
+
}
|
|
17
|
+
}
|
package/render.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/***************************************************************************************************
|
|
2
|
+
* *
|
|
3
|
+
* ,; ) Total Diagram Render HTML5 *
|
|
4
|
+
* _o_ _; ( One manager to rule them all *
|
|
5
|
+
* (c( )/ ____ MIT License *
|
|
6
|
+
* |___| \__/) Copyright (c) 2020-2023 Dariusz Dawidowski *
|
|
7
|
+
* *
|
|
8
|
+
**************************************************************************************************/
|
|
9
|
+
|
|
10
|
+
class TotalDiagramRenderHTML5 {
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Constructor
|
|
14
|
+
* @param args.container <HTMLElement>: DOM container for rendering
|
|
15
|
+
* @param args.nodes <TotalDiagramNodesManager>: Nodes manager
|
|
16
|
+
* @param args.links <TotalDiagramLinksManager>: Links manager
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
constructor(args) {
|
|
20
|
+
|
|
21
|
+
// Container
|
|
22
|
+
this.container = args.container;
|
|
23
|
+
|
|
24
|
+
// Create 0x0 main object to attach nodes/links to follow transforms
|
|
25
|
+
this.board = document.createElement('div');
|
|
26
|
+
this.board.id = 'total-diagram-attach';
|
|
27
|
+
this.board.style.transformOrigin = '0px 0px';
|
|
28
|
+
this.board.style.width = 0;
|
|
29
|
+
this.board.style.height = 0;
|
|
30
|
+
this.board.style.overflow = 'visible';
|
|
31
|
+
this.container.appendChild(this.board);
|
|
32
|
+
|
|
33
|
+
// Board dimensions
|
|
34
|
+
this.size = this.container.getBoundingClientRect();
|
|
35
|
+
this.size.center = {x: this.size.width / 2, y: this.size.height / 2};
|
|
36
|
+
|
|
37
|
+
// Board margins
|
|
38
|
+
this.margin = {
|
|
39
|
+
left: this.size.left - document.documentElement.scrollLeft,
|
|
40
|
+
top: this.size.top - document.documentElement.scrollTop,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Board current pan offset (x,y) and zoom (z)
|
|
44
|
+
this.offset = {
|
|
45
|
+
x: 0,
|
|
46
|
+
y: 0,
|
|
47
|
+
z: 1
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Window resize callback
|
|
51
|
+
window.addEventListener('resize', () => {
|
|
52
|
+
this.size = this.container.getBoundingClientRect();
|
|
53
|
+
this.size.center = {x: this.size.width / 2, y: this.size.height / 2};
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Nodes
|
|
57
|
+
this.nodes = args.nodes;
|
|
58
|
+
this.nodes.render = this;
|
|
59
|
+
|
|
60
|
+
// Link
|
|
61
|
+
this.links = args.links;
|
|
62
|
+
this.links.render = this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Perform pan
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
pan(deltaX, deltaY) {
|
|
70
|
+
this.offset.x += deltaX;
|
|
71
|
+
this.offset.y += deltaY;
|
|
72
|
+
this.update();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Perform zoom
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
zoom(x, y, deltaZ, factorZ) {
|
|
80
|
+
let deltaZoom = this.offset.z;
|
|
81
|
+
this.offset.z = (this.offset.z - (deltaZ / factorZ) * this.offset.z).clamp(0.1, 3.0);
|
|
82
|
+
deltaZoom = this.offset.z / deltaZoom;
|
|
83
|
+
|
|
84
|
+
const boundingRect = this.board.getBoundingClientRect();
|
|
85
|
+
const ox = boundingRect.width / 2 + boundingRect.left - x;
|
|
86
|
+
const oy = boundingRect.height / 2 + boundingRect.top - y;
|
|
87
|
+
|
|
88
|
+
this.offset.x += ox * deltaZoom - ox;
|
|
89
|
+
this.offset.y += oy * deltaZoom - oy;
|
|
90
|
+
this.update();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Perform mobile zoom
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
pinchZoom(x1, y1, x2, y2, deltaZ) {
|
|
98
|
+
let deltaZoom = this.offset.z;
|
|
99
|
+
this.offset.z = (this.offset.z * deltaZ).clamp(0.1, 3.0);
|
|
100
|
+
deltaZoom = this.offset.z / deltaZoom;
|
|
101
|
+
|
|
102
|
+
const boundingRect = this.board.getBoundingClientRect();
|
|
103
|
+
const ox = boundingRect.width / 2 + boundingRect.left - ((x1 + x2) / 2);
|
|
104
|
+
const oy = boundingRect.height / 2 + boundingRect.top - ((y1 + y2) / 2);
|
|
105
|
+
|
|
106
|
+
this.offset.x += ox * deltaZoom - ox;
|
|
107
|
+
this.offset.y += oy * deltaZoom - oy;
|
|
108
|
+
this.update();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Convert window coordinates to world
|
|
113
|
+
* {x: ..., y: ...}
|
|
114
|
+
*/
|
|
115
|
+
|
|
116
|
+
screen2World(coords) {
|
|
117
|
+
return {
|
|
118
|
+
x: Math.round( (((coords.x - this.offset.x) / this.offset.z) - this.margin.left) ),
|
|
119
|
+
y: Math.round( (((coords.y - this.offset.y) / this.offset.z) - this.margin.top) ),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Convert world coordinates to window
|
|
125
|
+
* {x: ..., y: ...}
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
world2Screen(coords) {
|
|
129
|
+
return {
|
|
130
|
+
x: Math.round( ((this.offset.x + (coords.x * this.offset.z)) - this.margin.left) ),
|
|
131
|
+
y: Math.round( ((this.offset.y + (coords.y * this.offset.z)) - this.margin.top) ),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 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}
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
center(coords = {x: 0, y: 0}, z = 'none', animation = 'hard', rect = null) {
|
|
143
|
+
|
|
144
|
+
// Animation
|
|
145
|
+
if (animation == 'smooth') {
|
|
146
|
+
this.board.style.transition = 'transform 1s';
|
|
147
|
+
setInterval(() => { this.board.style.removeProperty('transition'); }, 1000);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Zoom
|
|
151
|
+
if (z == 'reset') this.offset.z = 1;
|
|
152
|
+
else if (z == 'focus') this.offset.z = ((this.size.height) / (rect.height + rect.margin));
|
|
153
|
+
|
|
154
|
+
// Render
|
|
155
|
+
this.offset.x = (-coords.x * this.offset.z) + this.size.center.x;
|
|
156
|
+
this.offset.y = (-coords.y * this.offset.z) + this.size.center.y;
|
|
157
|
+
this.update();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Reset zoom
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
centerZoom() {
|
|
165
|
+
this.offset.z = 1;
|
|
166
|
+
this.update();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Delete all nodes and links and clear DOM
|
|
171
|
+
*/
|
|
172
|
+
|
|
173
|
+
clear() {
|
|
174
|
+
this.board.innerHTML = '';
|
|
175
|
+
this.links.del('*');
|
|
176
|
+
this.nodes.del('*');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Render update
|
|
181
|
+
*/
|
|
182
|
+
|
|
183
|
+
update() {
|
|
184
|
+
// Calculate board's position
|
|
185
|
+
// Note: using 'transform' since PYQt6 doesn't support new css 'translate' and 'scale' yet
|
|
186
|
+
this.board.style.transform = `translate(${this.offset.x}px, ${this.offset.y}px) scale(${this.offset.z})`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Hack to force redraw window
|
|
191
|
+
*/
|
|
192
|
+
|
|
193
|
+
redraw() {
|
|
194
|
+
this.container.style.display = 'none';
|
|
195
|
+
const trick = this.container.offsetHeight;
|
|
196
|
+
this.container.style.display = 'block';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
}
|
|
Binary file
|