From 8985843a16df94729fb5c264ee60cc29471aaf1c Mon Sep 17 00:00:00 2001
From: Terry Truong <terry06890@gmail.com>
Date: Wed, 19 Oct 2022 18:54:53 +1100
Subject: Place event labels above image

---
 src/components/TimeLine.vue | 50 ++++++++++++++++++++++++++++-----------------
 1 file changed, 31 insertions(+), 19 deletions(-)

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

diff --git a/src/components/TimeLine.vue b/src/components/TimeLine.vue
index b3bab51..4df77d9 100644
--- a/src/components/TimeLine.vue
+++ b/src/components/TimeLine.vue
@@ -39,10 +39,13 @@
 		<line :stroke="store.color.alt" stroke-width="2px" x1="-1" y1="0" x2="2" y2="0" :style="mainlineStyles"/>
 	</svg>
 	<!-- Events -->
-	<div v-for="id in idToPos.keys()" :key="id"
-		class="absolute bg-black text-white rounded-full border border-yellow-500 animate-fadein text-sm text-center"
-		:style="eventStyles(id)">
-		{{idToEvent.get(id)!.title}}
+	<div v-for="id in idToPos.keys()" :key="id" class="absolute animate-fadein" :style="eventStyles(id)">
+		<!-- Label -->
+		<div class="text-center text-stone-100 text-sm whitespace-nowrap text-ellipsis overflow-hidden">
+			{{idToEvent.get(id)!.title}}
+		</div>
+		<!-- Image -->
+		<div class="rounded-full border border-yellow-500" :style="eventImgStyles(id)"></div>
 	</div>
 	<!-- Buttons -->
 	<icon-button :size="30" class="absolute top-2 right-2"
@@ -66,8 +69,6 @@ import {WRITING_MODE_HORZ, MIN_DATE, MAX_DATE, MONTH_SCALE, DAY_SCALE, SCALES, M
 import {useStore} from '../store';
 import {RBTree} from '../rbtree';
 
-const SCRIM_GRADIENT = 'linear-gradient(to bottom, rgba(0,0,0,0.4), #0000 40%)';
-
 // Refs
 const rootRef: Ref<HTMLElement | null> = ref(null);
 
@@ -115,10 +116,15 @@ onMounted(() => resizeObserver.observe(rootRef.value as HTMLElement));
 
 //
 const MAINLINE_WIDTH = 80; // Breadth of mainline area (including ticks and labels)
-const EVENT_SIZE = 100; // Width/height of event elements
+const EVENT_IMG_SZ = 100; // Width/height of event images
+const EVENT_LABEL_HEIGHT = 15;
+const eventWidth = computed(() => EVENT_IMG_SZ);
+const eventHeight = computed(() => EVENT_IMG_SZ + EVENT_LABEL_HEIGHT);
+const eventMajorSz = computed(() => props.vert ? eventHeight.value : eventWidth.value);
+const eventMinorSz = computed(() => props.vert ? eventWidth.value : eventHeight.value)
 const SPACING = 10;
 const sideMainline = computed( // True if unable to fit mainline in middle with events on both sides
-	() => availBreadth.value < MAINLINE_WIDTH + (EVENT_SIZE + SPACING * 2) * 2);
+	() => availBreadth.value < MAINLINE_WIDTH + (eventMinorSz.value + SPACING * 2) * 2);
 const mainlineOffset = computed(() => { // Distance from side of display area
 	if (!sideMainline.value){
 		return availBreadth.value / 2 - MAINLINE_WIDTH /2 + LARGE_TICK_LEN;
@@ -312,11 +318,11 @@ const idToPos = computed(() => {
 	let full = false;
 	for (let event of idToEvent.value.values()){
 		// Layout as if props.vert
-		if (posY + EVENT_SIZE + SPACING > availLen.value){ // If at end of column
+		if (posY + eventMajorSz.value + SPACING > availLen.value){ // If at end of column
 			posY = SPACING;
-			posX += EVENT_SIZE + SPACING;
+			posX += eventMinorSz.value + SPACING;
 			// If finished last row
-			if (posX + EVENT_SIZE + SPACING > availBreadth.value){
+			if (posX + eventMinorSz.value + SPACING > availBreadth.value){
 				full = true;
 				break;
 			}
@@ -324,7 +330,7 @@ const idToPos = computed(() => {
 		// Avoid collision with timeline
 		if (!sideMainline.value){
 			if (posX <= availBreadth.value / 2 + MAINLINE_WIDTH / 2 + SPACING &&
-					posX + EVENT_SIZE >= availBreadth.value / 2 - MAINLINE_WIDTH / 2 - SPACING){
+					posX + eventMinorSz.value >= availBreadth.value / 2 - MAINLINE_WIDTH / 2 - SPACING){
 				posX = availBreadth.value / 2 + MAINLINE_WIDTH / 2 + SPACING;
 			}
 		} else {
@@ -333,19 +339,19 @@ const idToPos = computed(() => {
 					posX = SPACING + MAINLINE_WIDTH + SPACING;
 				}
 			} else {
-				if (posX + EVENT_SIZE + SPACING > mainlineOffset.value){
+				if (posX + eventMinorSz.value + SPACING > mainlineOffset.value){
 					break;
 				}
 			}
 		}
 		// Add coords
 		if (props.vert){
-			map.set(event.id, [posX, posY, EVENT_SIZE, EVENT_SIZE]);
+			map.set(event.id, [posX, posY, eventWidth.value, eventHeight.value]);
 		} else {
-			map.set(event.id, [posY, posX, EVENT_SIZE, EVENT_SIZE]);
+			map.set(event.id, [posY, posX, eventWidth.value, eventHeight.value]);
 		}
 		// Update to next position
-		posY += EVENT_SIZE + SPACING;
+		posY += eventMajorSz.value + SPACING;
 	}
 	// If more events could be displayed, notify parent
 	if (!full){
@@ -856,20 +862,26 @@ function tickLabelStyles(idx: number){
 	}
 }
 function eventStyles(eventId: number){
-	const event = idToEvent.value.get(eventId)!;
 	const [x, y, w, h] = idToPos.value.get(eventId)!;
 	return {
 		left: x + 'px',
 		top: y + 'px',
 		width: w + 'px',
 		height: h + 'px',
-		backgroundImage: `${SCRIM_GRADIENT},url(${getImagePath(event.imgId)})`,
-		backgroundSize: 'cover',
 		transitionProperty: skipTransition.value ? 'none' : 'all',
 		transitionDuration,
 		transitionTimingFunction,
 	};
 }
+function eventImgStyles(eventId: number){
+	const event = idToEvent.value.get(eventId)!;
+	return {
+		width: EVENT_IMG_SZ + 'px',
+		height: EVENT_IMG_SZ + 'px',
+		backgroundImage: `url(${getImagePath(event.imgId)})`,
+		backgroundSize: 'cover',
+	};
+}
 function eventLineStyles(eventId: number){
 	const [x, y, l, a] = eventLines.value.get(eventId)!;
 	return {
-- 
cgit v1.2.3

