aboutsummaryrefslogtreecommitdiff
path: root/src/components/TileTree.vue
blob: ca6001422d2bc4b4e69a41267ac0431cb99c0f2c (plain)
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
<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 { //adds '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: 5,
	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>