Add timeline cursor label

pull/37/head
Varun Patil 2022-08-18 01:55:12 +00:00
parent f65c54f104
commit c58726d81a
1 changed files with 62 additions and 13 deletions

View File

@ -53,13 +53,15 @@
@touchmove="timelineTouch" @touchmove="timelineTouch"
@mouseleave="timelineLeave" @mouseleave="timelineLeave"
@mousedown="timelineClick"> @mousedown="timelineClick">
<span class="cursor" <span class="cursor st"
v-bind:style="{ top: timelineCursorY + 'px' }"></span> v-bind:style="{ top: timelineCursorY + 'px' }"></span>
<span class="cursor" <span class="cursor hv"
v-bind:style="{ transform: `translateY(${timelineHoverCursorY}px)` }"></span> v-bind:style="{ transform: `translateY(${timelineHoverCursorY}px)` }">
{{ timelineHoverCursorText }}
</span>
<div v-for="tick of timelineTicks" :key="tick.dayId" class="tick" <div v-for="tick of timelineTicks" :key="tick.dayId" class="tick"
v-bind:style="{ top: Math.floor((tick.topS + tick.top * rowHeight) * timelineHeight / viewHeight) + 'px' }"> v-bind:style="{ top: tick.topC + 'px' }">
<span v-if="tick.text">{{ tick.text }}</span> <span v-if="tick.text">{{ tick.text }}</span>
<span v-else class="dash"></span> <span v-else class="dash"></span>
</div> </div>
@ -99,6 +101,8 @@ export default {
timelineCursorY: 0, timelineCursorY: 0,
/** Timeline hover cursor top */ /** Timeline hover cursor top */
timelineHoverCursorY: -5, timelineHoverCursorY: -5,
/** Timeline hover cursor text */
timelineHoverCursorText: "",
/** Current start index */ /** Current start index */
currentStart: 0, currentStart: 0,
@ -174,6 +178,11 @@ export default {
handleViewSizeChange() { handleViewSizeChange() {
setTimeout(() => { setTimeout(() => {
this.viewHeight = this.$refs.scroller.$refs.wrapper.clientHeight; this.viewHeight = this.$refs.scroller.$refs.wrapper.clientHeight;
// Compute timeline tick positions
for (const tick of this.timelineTicks) {
tick.topC = Math.floor((tick.topS + tick.top * this.rowHeight) * this.timelineHeight / this.viewHeight);
}
}, 0); }, 0);
}, },
@ -185,6 +194,7 @@ export default {
scrollPositionChange(event) { scrollPositionChange(event) {
if (event) { if (event) {
this.timelineCursorY = event.target.scrollTop * this.timelineHeight / this.viewHeight; this.timelineCursorY = event.target.scrollTop * this.timelineHeight / this.viewHeight;
this.timelineMoveHoverCursor(this.timelineCursorY);
} }
if (this.scrollTimer) { if (this.scrollTimer) {
@ -257,8 +267,9 @@ export default {
// Ticks // Ticks
let currTopRow = 0; let currTopRow = 0;
let currTopStatic = 0; let currTopStatic = 0;
let prevYear = new Date().getUTCFullYear(); let prevYear = 9999;
let prevMonth = new Date().getUTCMonth(); let prevMonth = 0;
const thisYear = new Date().getFullYear();
for (const [dayIdx, day] of data.entries()) { for (const [dayIdx, day] of data.entries()) {
day.count = Number(day.count); day.count = Number(day.count);
@ -280,11 +291,18 @@ export default {
const dtYear = dateTaken.getUTCFullYear(); const dtYear = dateTaken.getUTCFullYear();
const dtMonth = dateTaken.getUTCMonth() const dtMonth = dateTaken.getUTCMonth()
if (Number.isInteger(day.day_id) && (dtMonth !== prevMonth || dtYear !== prevYear)) { if (Number.isInteger(day.day_id) && (dtMonth !== prevMonth || dtYear !== prevYear)) {
// Format dateTaken as MM YYYY
const dateTimeFormat = new Intl.DateTimeFormat('en-US', { month: 'short' });
const monthName = dateTimeFormat.formatToParts(dateTaken)[0].value;
// Create tick
this.timelineTicks.push({ this.timelineTicks.push({
dayId: day.id, dayId: day.id,
top: currTopRow, top: currTopRow,
topS: currTopStatic, topS: currTopStatic,
text: dtYear === prevYear ? undefined : dtYear, topC: 0,
text: (dtYear === prevYear || dtYear === thisYear) ? undefined : dtYear,
mText: `${monthName} ${dtYear}`,
}); });
} }
prevMonth = dtMonth; prevMonth = dtMonth;
@ -402,17 +420,32 @@ export default {
}; };
}, },
timelineMoveHoverCursor(y) {
this.timelineHoverCursorY = y;
// Get index of previous tick
let idx = this.timelineTicks.findIndex(t => t.topC > y);
if (idx >= 1) {
idx = idx - 1;
} else if (idx === -1 && this.timelineTicks.length > 0) {
idx = this.timelineTicks.length - 1;
} else {
return;
}
this.timelineHoverCursorText = this.timelineTicks[idx].mText;
},
/** Handle mouse hover on right timeline */ /** Handle mouse hover on right timeline */
timelineHover(event) { timelineHover(event) {
if (event.buttons) { if (event.buttons) {
this.timelineClick(event); this.timelineClick(event);
} }
this.timelineHoverCursorY = event.offsetY; this.timelineMoveHoverCursor(event.offsetY);
}, },
/** Handle mouse leave on right timeline */ /** Handle mouse leave on right timeline */
timelineLeave() { timelineLeave() {
this.timelineHoverCursorY = -5; this.timelineMoveHoverCursor(this.timelineCursorY);
}, },
/** Handle mouse click on right timeline */ /** Handle mouse click on right timeline */
@ -573,7 +606,6 @@ export default {
height: 100%; height: 100%;
width: 40px; width: 40px;
top: 0; right: 0; top: 0; right: 0;
overflow: hidden;
cursor: ns-resize; cursor: ns-resize;
opacity: 0; opacity: 0;
transition: opacity .2s ease-in-out; transition: opacity .2s ease-in-out;
@ -589,6 +621,7 @@ export default {
color: black; color: black;
right: 5px; right: 5px;
transform: translateY(-50%); transform: translateY(-50%);
z-index: 1;
} }
.timeline-scroll .tick .dash { .timeline-scroll .tick .dash {
@ -602,9 +635,25 @@ export default {
position: absolute; position: absolute;
pointer-events: none; pointer-events: none;
right: 5px; right: 5px;
height: 2px;
background-color: var(--color-primary); background-color: var(--color-primary);
border-radius: 4px; min-width: 100%;
width: 100%; min-height: 2px;
}
.timeline-scroll .cursor.st {
opacity: 0;
}
.timeline-scroll:hover .cursor.st {
opacity: 1;
}
.timeline-scroll .cursor.hv {
background-color: rgba(255, 255, 255, 0.8);
padding: 2px 5px;
border-top: 2px solid var(--color-primary);
border-radius: 2px;
width: auto;
white-space: nowrap;
z-index: 100;
} }
</style> </style>