Add timeline cursor label
parent
f65c54f104
commit
c58726d81a
|
@ -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>
|
Loading…
Reference in New Issue