diff --git a/src/Pages/EmissionManager.vue b/src/Pages/EmissionManager.vue index c1c33edff5e9cfed673c19028dd89015bc3d5141..0edb78e67276097bae3fd17ce9a0ccc637a05347 100644 --- a/src/Pages/EmissionManager.vue +++ b/src/Pages/EmissionManager.vue @@ -211,6 +211,8 @@ </template> <script> +import { parseISO } from 'date-fns' +import { h } from 'vue' import { mapGetters } from 'vuex' import FullCalendar from '@fullcalendar/vue3' @@ -227,7 +229,12 @@ import playlist from '@/mixins/playlist' import ServerErrors from '@/components/ServerErrors.vue' import { getISODateString } from '@/utilities' import PageHeader from '@/components/PageHeader.vue' -import { getClosestSlot, getNextAvailableSlot, sanitizeHTML } from '@/util' +import { + calculateDurationSeconds, + getClosestSlot, + getNextAvailableSlot, + sanitizeHTML, +} from '@/util' import SafeHTML from '@/components/generic/SafeHTML' export default { @@ -341,6 +348,37 @@ export default { allDaySlot: false, editable: false, nowIndicator: true, + eventContent({ event, timeText }) { + // The eventContent function doesn’t quite seem to work like documented in the fullcalendar docs: + // * the slot is broken because it doesn’t receive a context object ('arg' is undefined) + // * the Preact createElement/h function that is passed as the second argument to this function + // doesn’t render anything + // * returning { html: '<i>hello</i>' } doesn’t render anything + // * returning { domNodes: [(() => { const i = document.createElement('i'); i.textContent = 'hello'; return i })()] } + // doesn’t render anything + // + // Instead, this comment [1] mentions that one should use the Vue createElement/h function. + // Surprisingly this works. + // + // It is unclear to me why all these (documented) options fail. One major difference is that we run Vue 3 in + // Vue 2 compat mode which might be a source of errors. + // + // [1]: https://github.com/fullcalendar/fullcalendar/issues/7175#issuecomment-1409519357 + + const { durationMinutes, title } = event.extendedProps + // don’t render any content if it would be crammed anyway + const content = + durationMinutes > slotDurationMinutes + ? [ + h('div', { class: 'fc-event-time' }, timeText), + h('div', { class: 'fc-event-title-container' }, [ + h('div', { class: 'fc-event-title fc-sticky' }, title), + ]), + ] + : [] + + return h('div', { class: 'fc-event-main-frame' }, content) + }, datesSet: (view) => { if ( this.currentStart?.toISOString?.() !== view.start.toISOString() || @@ -802,6 +840,8 @@ export default { start: timeslot.start, end: timeslot.end, title, + durationMinutes: + calculateDurationSeconds(parseISO(timeslot.start), parseISO(timeslot.end)) / 60, }, }) } diff --git a/src/util.ts b/src/util.ts index bdd7077e84f0a125d34676f233dd43b6aab7cbc6..1ff59873167dfaba72739d34c6c66f62bca39799 100644 --- a/src/util.ts +++ b/src/util.ts @@ -75,6 +75,10 @@ export function getNextAvailableSlot(slotDurationMinutes: number, date?: Date): return new Date(nextSlotTimestamp) } +export function calculateDurationSeconds(start: Date, end: Date): number { + return Math.abs(end.getTime() - start.getTime()) / 1000 +} + export function sanitizeHTML( html: string, preset?: 'inline-noninteractive' | 'safe-html' | 'strip',