Improve UI

pull/37/head
Varun Patil 2022-08-15 05:53:02 +00:00
parent 20ff9a4b7d
commit 0845e6fb54
1 changed files with 121 additions and 14 deletions

View File

@ -22,6 +22,17 @@
v-bind:style="{ width: rowHeight + 'px', height: rowHeight + 'px' }"/> v-bind:style="{ width: rowHeight + 'px', height: rowHeight + 'px' }"/>
</div> </div>
</RecycleScroller> </RecycleScroller>
<div ref="timelineScroll" class="timeline-scroll"
@mousemove="timelineHover"
@click="timelineClick">
<div v-for="tick of timelineTicks" :key="tick.dayId" class="tick"
v-bind:style="{ top: Math.floor(tick.top * timelineHeight / viewHeight) + 'px' }">
<span v-if="tick.text">{{ tick.text }}</span>
<span v-else class="dash"></span>
</div>
</div>
</div> </div>
</template> </template>
@ -37,9 +48,17 @@ export default {
numCols: 5, numCols: 5,
/** Header rows for dayId key */ /** Header rows for dayId key */
heads: {}, heads: {},
/** Original days response */
days: [],
/** Computed row height */ /** Computed row height */
rowHeight: 100, rowHeight: 100,
/** Total height of recycler */
viewHeight: 1000,
/** Total height of timeline */
timelineHeight: 100,
/** Computed timeline ticks */
timelineTicks: [],
/** Current start index */ /** Current start index */
currentStart: 0, currentStart: 0,
@ -57,13 +76,21 @@ export default {
/** Handle window resize and initialization */ /** Handle window resize and initialization */
handleResize() { handleResize() {
let height = this.$refs.container.clientHeight; let height = this.$refs.container.clientHeight;
let width = this.$refs.container.clientWidth; let width = this.$refs.container.clientWidth - 40;
this.timelineHeight = this.$refs.timelineScroll.clientHeight;
this.$refs.scroller.$el.style.height = (height - 4) + 'px'; this.$refs.scroller.$el.style.height = (height - 4) + 'px';
this.numCols = Math.max(4, Math.floor(width / 150)); this.numCols = Math.max(4, Math.floor(width / 150));
this.rowHeight = Math.floor(width / this.numCols) - 4; this.rowHeight = Math.floor(width / this.numCols) - 4;
}, },
/** Handle change in rows and view size */
handleViewSizeChange() {
setTimeout(() => {
this.viewHeight = this.$refs.scroller.$refs.wrapper.clientHeight;
}, 0);
},
/** Trigger when recycler view changes */ /** Trigger when recycler view changes */
scrollChange(startIndex, endIndex) { scrollChange(startIndex, endIndex) {
if (startIndex === this.currentStart && endIndex === this.currentEnd) { if (startIndex === this.currentStart && endIndex === this.currentEnd) {
@ -88,8 +115,8 @@ export default {
} }
let head = this.heads[item.dayId]; let head = this.heads[item.dayId];
if (head && !head.loaded) { if (head && !head.loadedImages) {
head.loaded = true; head.loadedImages = true;
this.fetchDay(item.dayId); this.fetchDay(item.dayId);
} }
} }
@ -99,8 +126,16 @@ export default {
async fetchDays() { async fetchDays() {
const res = await fetch('/apps/betterphotos/api/days'); const res = await fetch('/apps/betterphotos/api/days');
const data = await res.json(); const data = await res.json();
this.days = data;
// Ticks
let currTop = 0;
let prevYear = new Date().getUTCFullYear();
let prevMonth = new Date().getUTCMonth();
for (const [dayIdx, day] of data.entries()) {
day.count = Number(day.count);
for (const day of data) {
// Nothing here // Nothing here
if (day.count === 0) { if (day.count === 0) {
continue; continue;
@ -114,30 +149,49 @@ export default {
dateStr = dateStr.substring(0, dateStr.length - 6); dateStr = dateStr.substring(0, dateStr.length - 6);
} }
// Create tick if month changed
const dtYear = dateTaken.getUTCFullYear();
const dtMonth = dateTaken.getUTCMonth()
if (dtMonth !== prevMonth || dtYear !== prevYear) {
this.timelineTicks.push({
dayId: day.id,
top: currTop,
text: dtYear === prevYear ? undefined : dtYear,
});
prevMonth = dtMonth;
prevYear = dtYear;
}
// Add header to list // Add header to list
const head = { const head = {
id: ++this.numRows, id: ++this.numRows,
name: dateStr, name: dateStr,
size: 60, size: 60,
head: true, head: true,
loaded: false, loadedImages: false,
dayId: day.day_id, dayId: day.day_id,
}; };
this.heads[day.day_id] = head; this.heads[day.day_id] = head;
this.list.push(head); this.list.push(head);
currTop += head.size;
// Add rows // Add rows
const nrows = Math.ceil(day.count / this.numCols); const nrows = Math.ceil(day.count / this.numCols);
for (let i = 0; i < nrows; i++) { for (let i = 0; i < nrows; i++) {
this.list.push(this.getBlankRow(day.day_id)); const row = this.getBlankRow(day.day_id);
this.list.push(row);
currTop += row.size;
} }
} }
// Fix view height variable
this.handleViewSizeChange();
}, },
/** Fetch image data for one dayId */ /** Fetch image data for one dayId */
async fetchDay(dayId) { async fetchDay(dayId) {
const head = this.heads[dayId]; const head = this.heads[dayId];
head.loaded = true; head.loadedImages = true;
let data = []; let data = [];
try { try {
@ -145,7 +199,7 @@ export default {
data = await res.json(); data = await res.json();
} catch (e) { } catch (e) {
console.error(e); console.error(e);
head.loaded = false; head.loadedImages = false;
} }
// Get index of header O(n) // Get index of header O(n)
@ -189,7 +243,33 @@ export default {
size: this.rowHeight, size: this.rowHeight,
dayId: dayId, dayId: dayId,
}; };
},
/** Handle mouse hover on right timeline */
timelineHover(event) {
},
/** Handle mouse click on right timeline */
timelineClick(event) {
this.$refs.scroller.scrollToPosition(this.getTimelinePosition(event));
},
/** Get scroller equivalent position from event */
getTimelinePosition(event) {
const tH = this.viewHeight;
const maxH = this.timelineHeight;
return event.offsetY * tH / maxH;
},
/** Scroll to given day Id */
scrollToDay(dayId) {
const head = this.heads[dayId];
if (!head) {
return;
} }
this.$refs.scroller.scrollToPosition(1000);
},
}, },
} }
</script> </script>
@ -197,11 +277,13 @@ export default {
<style scoped> <style scoped>
.container { .container {
height: 100%; height: 100%;
width: 100%;
overflow: hidden;
} }
.scroller { .scroller {
height: 300px; height: 300px;
width: 100%; width: calc(100% + 20px);
} }
.photo img { .photo img {
@ -214,4 +296,29 @@ export default {
font-size: 20px; font-size: 20px;
font-weight: lighter; font-weight: lighter;
} }
.timeline-scroll {
position: absolute;
height: 100%;
width: 40px;
top: 0; right: 0;
overflow: hidden;
cursor: ns-resize;
}
.timeline-scroll .tick {
pointer-events: none;
position: absolute;
font-size: 0.8em;
color: grey;
right: 5px;
}
.timeline-scroll .tick .dash {
height: 1px;
width: 6px;
background-color: grey;
opacity: 0.8;
display: block;
}
</style> </style>