From 46ccf78e8421b71a1f7829e70ce5bc064166ad6e Mon Sep 17 00:00:00 2001 From: Michael Bauer <michael-bauer@posteo.de> Date: Wed, 27 Mar 2024 16:01:36 +0100 Subject: [PATCH] Use v-intersect to keep track of currently visible days Seems way more reliable than keeping scroll position (as was done previously; inspired by the vue static site generator vuepress) --- .../components/coursebook/Coursebook.vue | 96 +++++++++++-------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue index 4bcdb82f9..2f15b8ae5 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue @@ -58,10 +58,14 @@ </template> <template #default="{ items }"> <v-list-item - v-for="day in groupDocsByDay(items)" + v-for="{ date, docs, first, last } in groupDocsByDay(items)" + v-intersect="onIntersect" + :data-date="date.toISODate()" + :data-first="first" + :data-last="last" two-line - :key="'day-' + day[0]" - :id="'documentation_' + day[0].toISODate()" + :key="'day-' + date" + :id="'documentation_' + date.toISODate()" > <v-list-item-content> <v-subheader class="text-h6">{{ @@ -69,7 +73,7 @@ }}</v-subheader> <v-list max-width="100%" class="pt-0 mt-n1"> <v-list-item - v-for="doc in day.slice(1)" + v-for="doc in docs" :key="'documentation-' + (doc.oldId || doc.id)" > <documentation-modal @@ -144,8 +148,9 @@ export default { data() { return { gqlQuery: documentationsForCoursebook, + currentDate: "", + visible: [], knownDates: {}, - docsByDay: {}, lastQuery: null, dateStart: "", dateEnd: "", @@ -208,7 +213,8 @@ export default { } // Resetting known dates to dateRange around current date this.knownDates = {}; - const dateRange = this.dateRange(DateTime.fromISO(this.$route.hash.substring(1))) + this.currentDate = this.$route.hash.substring(1); + const dateRange = this.dateRange(DateTime.fromISO(this.$route.hash.substring(1))); dateRange.forEach((ts) => this.knownDates[ts] = true); const lastIdx = dateRange.length - 1; // Returning a dateRange each around first & last date for the initial query @@ -234,47 +240,23 @@ export default { const docsByDay = docs.reduce((byDay, doc) => { // This works with dummy. Does actual doc have dateStart instead? const day = DateTime.fromISO(doc.datetimeStart).startOf("day"); - byDay[day] ??= [day]; - byDay[day].push(doc); + byDay[day] ??= {date: day, docs: [], first: false, last: false}; + byDay[day]['docs'].push(doc); return byDay; }, {}); // => [[dt doc ...] ...] return Object.keys(docsByDay) .sort() - .map((key) => docsByDay[key]); - // sorting is necessary since backend can send docs unordered - }, - debounce(fn, delay) { - let timer; - return () => { - console.log('debounce'); - clearTimeout(timer); - timer = setTimeout(fn, delay); - } - }, - // Adapted from - // https://github.com/vuejs/vuepress/blob/38e98634af117f83b6a32c8ff42488d91b66f663/packages/%40vuepress/plugin-active-header-links/clientRootMixin.js - setCurrentDay() { - const days = Array.from(document.querySelectorAll("[id^='documentation_']")); - - const scrollTop = Math.max( - window.pageYOffset, - document.documentElement.scrollTop, - document.body.scrollTop - ); - - for (let i = 0; i < days.length; i++) { - const day = days[i]; - const nextDay =days[i + 1]; - - if ((scrollTop >= day.offsetTop + 10 || i == 0) && (!nextDay || scrollTop < nextDay.offsetTop - 10)) { - const date = day.id.split("_")[1]; - if (date !== this.$route.hash.substring(1)) { - this.gotoDate(date); + .map((key, i, {length}) => { + const day = docsByDay[key]; + if (i === 0) { + day['first'] = true; + } else if (i === length - 1) { + day['last'] = true; } - return - } - } + return day; + }); + // sorting is necessary since backend can send docs unordered }, /** * @param {"prev"|"next"} direction @@ -382,10 +364,40 @@ export default { this.assureDate(DateTime.fromISO(date)); // scroll }, + onIntersect(entries, observer) { + const entry = entries[0]; + if (entry.isIntersecting) { + // coming + console.log('intersect', this.visible); + + // track visible + if (this.visible[0] > entry.target.dataset.date || this.visible.length === 0) { + // coming is new first (top) date + this.visible.unshift(entry.target.dataset.date); + console.log('current', this.visible[0]); + } else if (this.visible[this.visible.length -1] < entry.target.dataset.date) { + // coming is new last (bottom) date + this.visible.push(entry.target.dataset.date); + } + + // load more + if (entry.target.dataset.first) { + console.log('load up'); + } else if (entry.target.dataset.last) { + console.log('load down'); + } + } else if (this.visible[0] === entry.target.dataset.date) { + // first (top) visible date is going + this.visible.shift() + console.log('current', this.visible[0]); + } else if (this.visible[this.visible.length - 1] === entry.target.dataset.date) { + // last (bottom) visible date is going + this.visible.pop() + } + }, }, created() { this.resetDate(); - window.addEventListener('scroll', this.debounce(this.setCurrentDay, 300)); }, }; </script> -- GitLab