1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
<script>
import Tile from './Tile.vue';
import tol from '../tol.json';
function preprocessTol(tree){
if (!tree.children){
tree.children = [];
} else {
tree.children.forEach(preprocessTol);
}
}
preprocessTol(tol);
//tol-node format: {name: string1, children: [node1, ...]}
import {staticSqrLayout, staticRectLayout, sweepToSideLayout, layoutInfoHooks} from '../layout.js';
let LAYOUT_FUNC = sweepToSideLayout;
export default {
data(){
return {
tree: this.initTree(tol, 1),
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
resizeThrottled: false,
}
},
methods: {
initTree(tol, lvl){
let tree = {
tolNode:tol, children:[],
x:0, y:0, w:0, h:0, headerSz:0,
sideArea:null,
};
function initTreeRec(tree, lvl){
if (lvl > 0)
tree.children = tree.tolNode.children.map(tNode => initTreeRec({
tolNode: tNode, children: [],
x:0, y:0, w:0, h:0, headerSz:0,
sideArea:null,
}, lvl-1));
return tree;
}
initTreeRec(tree, lvl);
layoutInfoHooks.initLayoutInfo(tree)
return tree;
},
onResize(){
if (!this.resizeThrottled){
this.width = document.documentElement.clientWidth;
this.height = document.documentElement.clientHeight;
this.tryLayout();
//prevent re-triggering until after a delay
this.resizeThrottled = true;
setTimeout(() => {this.resizeThrottled = false;}, 100);
}
},
onInnerTileClicked(nodeList){
//nodeList holds an array of tree-objects, from the clicked-on-tile's tree-object upward
let numNewTiles = nodeList[0].tolNode.children.length;
if (numNewTiles == 0){
console.log('Tile-to-expand has no children');
return;
}
//add children
nodeList[0].children = nodeList[0].tolNode.children.map(tNode => ({
tolNode: tNode, children: [],
x:0, y:0, w:0, h:0, headerSz:0,
}));
layoutInfoHooks.updateLayoutInfoOnExpand(nodeList);
//try to layout tree
if (!this.tryLayout())
nodeList[0].children = [];
},
onInnerHeaderClicked(nodeList){ //nodeList is array of tree-objects, from clicked-on-tile's tree-object upward
let children = nodeList[0].children;
nodeList[0].children = [];
layoutInfoHooks.updateLayoutInfoOnCollapse(nodeList);
if (!this.tryLayout())
nodeList[0].children = children;
},
tryLayout(){
let layout = LAYOUT_FUNC(this.tree, 0, 0, this.width, this.height, true);
if (layout == null){
console.log('Unable to layout tree');
return false;
} else {
this.applyLayout(layout, this.tree);
return true;
}
},
applyLayout(layout, tree){
tree.x = layout.x;
tree.y = layout.y;
tree.w = layout.w;
tree.h = layout.h;
tree.headerSz = layout.headerSz;
layout.children.forEach((n,i) => this.applyLayout(n, tree.children[i]));
//handle case where leaf nodes placed in leftover space from parent-sweep
if (layout.sideArea){
//add parent area coords
tree.sideArea = layout.sideArea;
//move leaf node children to parent area
tree.children.filter(n => n.children.length == 0).map(n => {
n.x += layout.sideArea.x;
n.y += layout.sideArea.y;
});
} else {
tree.sideArea = null;
}
}
},
created(){
window.addEventListener('resize', this.onResize);
this.tryLayout();
},
unmounted(){
window.removeEventListener('resize', this.onResize);
},
components: {
Tile
}
}
</script>
<template>
<div class="h-[100vh]">
<tile :tree="tree" @tile-clicked="onInnerTileClicked" @header-clicked="onInnerHeaderClicked"></tile>
</div>
</template>
|