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
|
<script lang="ts">
import {defineComponent} from 'vue';
import Tile from './Tile.vue';
import {TolNode, LayoutTree, LayoutNode} from '../lib';
import type {LayoutOptions} from '../lib';
// Import paths lack a .ts or .js extension because .ts makes vue-tsc complain, and .js makes vite complain
// Obtain tree-of-life data
import tolRaw from '../tol.json';
function preprocessTol(node: any): any {
// Add 'children' fields if missing
if (node.children == null){
node.children = [];
} else {
node.children.forEach(preprocessTol);
}
return node;
}
const tol: TolNode = preprocessTol(tolRaw);
// Configurable settings
let layoutOptions: LayoutOptions = {
// Integer values specify pixels
tileSpacing: 8,
headerSz: 20,
minTileSz: 50,
maxTileSz: 200,
layoutType: 'sweep', //'sqr' | 'rect' | 'sweep'
rectMode: 'auto', //'horz' | 'vert' | 'linear' | 'auto'
sweepMode: 'left', //'left' | 'top' | 'shorter' | 'auto'
sweptNodesPrio: 'linear', //'linear' | 'sqrt' | 'pow-2/3'
sweepingToParent: true,
};
let otherOptions = {
// Integer values specify milliseconds
transitionDuration: 300,
resizeDelay: 100, // During window-resizing, relayout tiles after this delay instead of continously
};
// Component holds a tree structure representing a subtree of 'tol' to be rendered
// Collects events about tile expansion/collapse and window-resize, and initiates relayout of tiles
export default defineComponent({
data(){
return {
layoutTree: new LayoutTree(tol, layoutOptions, 0),
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
layoutOptions: layoutOptions,
otherOptions: otherOptions,
resizeThrottled: false,
}
},
methods: {
onResize(){
if (!this.resizeThrottled){
// Update data and relayout tiles
this.width = document.documentElement.clientWidth;
this.height = document.documentElement.clientHeight;
if (!this.layoutTree.tryLayout([0,0], [this.width,this.height])){
console.log('Unable to layout tree');
}
// Prevent re-triggering until after a delay
this.resizeThrottled = true;
setTimeout(() => {this.resizeThrottled = false;}, otherOptions.resizeDelay);
}
},
onInnerLeafClicked(clickedNode: LayoutNode){
if (clickedNode.tolNode.children.length == 0){
console.log('Tile-to-expand has no children');
return;
}
if (!this.layoutTree.tryLayoutOnExpand([0,0], [this.width,this.height], clickedNode)){
console.log('Unable to layout tree');
}
},
onInnerHeaderClicked(clickedNode: LayoutNode){
if (!this.layoutTree.tryLayoutOnCollapse([0,0], [this.width,this.height], clickedNode)){
console.log('Unable to layout tree');
}
},
},
created(){
window.addEventListener('resize', this.onResize);
if (!this.layoutTree.tryLayout([0,0], [this.width,this.height])){
console.log('Unable to layout tree');
}
},
unmounted(){
window.removeEventListener('resize', this.onResize);
},
components: {
Tile,
},
});
</script>
<template>
<div class="h-screen bg-stone-100">
<tile :layoutNode="layoutTree.root"
:headerSz="layoutOptions.headerSz" :tileSpacing="layoutOptions.tileSpacing"
:transitionDuration="otherOptions.transitionDuration" :isRoot="true"
@leaf-clicked="onInnerLeafClicked" @header-clicked="onInnerHeaderClicked"/>
</div>
</template>
|