From 23b5cc80ba02936659564dd03b173d3214ce5978 Mon Sep 17 00:00:00 2001
From: Terry Truong <terry06890@gmail.com>
Date: Tue, 13 Sep 2022 19:59:06 +1000
Subject: Use Vue Composition API and ESLint

---
 src/components/AncestryBar.vue | 150 ++++++++++++++++++++---------------------
 1 file changed, 72 insertions(+), 78 deletions(-)

(limited to 'src/components/AncestryBar.vue')

diff --git a/src/components/AncestryBar.vue b/src/components/AncestryBar.vue
index 3cfd116..1b4ee81 100644
--- a/src/components/AncestryBar.vue
+++ b/src/components/AncestryBar.vue
@@ -1,92 +1,86 @@
 <template>
-<div :style="styles" @wheel.stop="onWheelEvt">
+<div :style="styles" @wheel.stop="onWheelEvt" ref="rootRef">
 	<tol-tile v-for="(node, idx) in dummyNodes" :key="node.name" class="shrink-0"
 		:layoutNode="node" :tolMap="tolMap" :nonAbsPos="true" :lytOpts="lytOpts" :uiOpts="uiOpts"
 		@leaf-click="onTileClick(nodes[idx])" @info-click="onInfoIconClick"/>
 </div>
 </template>
 
-<script lang="ts">
-import {defineComponent, PropType} from 'vue';
+<script setup lang="ts">
+import {ref, computed, watch, onMounted, nextTick, PropType} from 'vue';
 import TolTile from './TolTile.vue';
 import {TolMap} from '../tol';
 import {LayoutNode, LayoutOptions} from '../layout';
 import {UiOptions} from '../lib';
 
-export default defineComponent({
-	props: {
-		nodes: {type: Array as PropType<LayoutNode[]>, required: true},
-		vert: {type: Boolean, default: false},
-		breadth: {type: Number, required: true},
-		// Other
-		lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
-		uiOpts: {type: Object as PropType<UiOptions>, required: true},
-		tolMap: {type: Object as PropType<TolMap>, required: true},
-	},
-	computed: {
-		imgSz(){
-			return this.breadth - this.lytOpts.tileSpacing - this.uiOpts.scrollGap;
-				// Intentionally omitting extra tileSpacing, to allow for scrollGap with less image shrinkage
-		},
-		dummyNodes(){ // Childless versions of 'nodes' used to parameterise <tol-tile>s
-			return this.nodes.map(n => {
-				let newNode = new LayoutNode(n.name, []);
-				newNode.dims = [this.imgSz, this.imgSz];
-				return newNode;
-			});
-		},
-		styles(): Record<string,string> {
-			return {
-				// For child layout
-				display: 'flex',
-				flexDirection: this.vert ? 'column' : 'row',
-				alignItems: 'center',
-				gap: this.lytOpts.tileSpacing + 'px',
-				padding: this.lytOpts.tileSpacing + 'px',
-				overflowX: this.vert ? 'hidden' : 'auto',
-				overflowY: this.vert ? 'auto' : 'hidden',
-				// Other
-				backgroundColor: this.uiOpts.ancestryBarBgColor,
-				boxShadow: this.uiOpts.shadowNormal,
-			};
-		},
-	},
-	methods: {
-		// Click events
-		onTileClick(node: LayoutNode){
-			this.$emit('ancestor-click', node);
-		},
-		onInfoIconClick(data: string){
-			this.$emit('info-click', data);
-		},
-		// For converting vertical scrolling to horizontal
-		onWheelEvt(evt: WheelEvent){
-			if (!this.vert && Math.abs(evt.deltaX) < Math.abs(evt.deltaY)){
-				this.$el.scrollLeft -= (evt.deltaY > 0 ? -30 : 30);
-			}
-		},
-		// Other
-		scrollToEnd(){
-			if (this.vert){
-				this.$el.scrollTop = this.$el.scrollHeight;
-			} else {
-				this.$el.scrollLeft = this.$el.scrollWidth;
-			}
-		},
-	},
-	watch: {
-		// For scrolling-to-end upon node/screen changes
-		nodes(){
-			this.$nextTick(() => this.scrollToEnd());
-		},
-		vert(){
-			this.$nextTick(() => this.scrollToEnd());
-		},
-	},
-	mounted(){
-		this.scrollToEnd();
-	},
-	components: {TolTile, },
-	emits: ['ancestor-click', 'info-click', ],
+// Refs
+const rootRef = ref(null as HTMLDivElement | null);
+
+// Props + events
+const props = defineProps({
+	nodes: {type: Array as PropType<LayoutNode[]>, required: true},
+	vert: {type: Boolean, default: false},
+	breadth: {type: Number, required: true},
+	//
+	lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
+	uiOpts: {type: Object as PropType<UiOptions>, required: true},
+	tolMap: {type: Object as PropType<TolMap>, required: true},
 });
+const emit = defineEmits(['ancestor-click', 'info-click']);
+
+// Computed prop data for display
+const imgSz = computed(() =>
+	props.breadth - props.lytOpts.tileSpacing - props.uiOpts.scrollGap
+		// Intentionally omitting extra tileSpacing, to allow for scrollGap with less image shrinkage
+);
+const dummyNodes = computed(() => props.nodes.map(n => {
+	let newNode = new LayoutNode(n.name, []);
+	newNode.dims = [imgSz.value, imgSz.value];
+	return newNode;
+}));
+
+// Click handling
+function onTileClick(node: LayoutNode){
+	emit('ancestor-click', node);
+}
+function onInfoIconClick(data: string){
+	emit('info-click', data);
+}
+
+// Scroll handling
+function onWheelEvt(evt: WheelEvent){ // For converting vertical scrolling to horizontal
+	if (!props.vert && Math.abs(evt.deltaX) < Math.abs(evt.deltaY)){
+		rootRef.value!.scrollLeft -= (evt.deltaY > 0 ? -30 : 30);
+	}
+}
+function scrollToEnd(){
+	let el = rootRef.value!;
+	if (props.vert){
+		el.scrollTop = el.scrollHeight;
+	} else {
+		el.scrollLeft = el.scrollWidth;
+	}
+}
+watch(props.nodes, () => {
+	nextTick(() => scrollToEnd());
+});
+watch(() => props.vert, () => {
+	nextTick(() => scrollToEnd());
+});
+onMounted(() => scrollToEnd());
+
+// Styles
+const styles = computed(() => ({
+	// For child layout
+	display: 'flex',
+	flexDirection: props.vert ? 'column' : 'row',
+	alignItems: 'center',
+	gap: props.lytOpts.tileSpacing + 'px',
+	padding: props.lytOpts.tileSpacing + 'px',
+	overflowX: props.vert ? 'hidden' : 'auto',
+	overflowY: props.vert ? 'auto' : 'hidden',
+	// Other
+	backgroundColor: props.uiOpts.ancestryBarBgColor,
+	boxShadow: props.uiOpts.shadowNormal,
+}));
 </script>
-- 
cgit v1.2.3

