diff --git a/plugins/notifications/frontend/views/CalendarView.vue b/plugins/notifications/frontend/views/CalendarView.vue index f6cc351..402306c 100644 --- a/plugins/notifications/frontend/views/CalendarView.vue +++ b/plugins/notifications/frontend/views/CalendarView.vue @@ -3,7 +3,7 @@

Calendar

-
+
@@ -94,6 +94,7 @@ import { notificationsApi } from '@/api' const events = ref([]) const selectedEvent = ref(null) const calendarRef = ref(null) +const calendarContainer = ref(null) const moreTooltip = ref(null) const moreTooltipData = ref([]) const tooltipPosition = ref({ left: 0, top: 0 }) @@ -113,14 +114,19 @@ const calendarOptions = ref({ center: 'title', right: 'dayGridMonth,dayGridWeek' }, - // 'auto' sized every row to its own content, so a month of mostly empty days - // collapsed into thin strips and the grid looked squashed. A viewport-relative - // height makes the calendar fill the space it has been given, and expandRows - // shares that height evenly across the weeks rather than leaving a gap under - // the last one. - height: 'calc(100vh - 230px)', + // Measured at mount and on resize (fitHeight), not a viewport calc: the + // calendar starts below a header and a page title whose heights are not + // knowable here, and guessing at them is what left the grid either clipped + // or overflowing. expandRows shares that height across the six weeks so an + // empty month fills it instead of collapsing into strips. + height: 600, expandRows: true, - dayMaxEvents: 3, + // true = FullCalendar fits as many chips as the row actually has room for and + // rolls the rest into a '+N more'. That is the only setting that cannot + // overflow the box: a fixed number outgrows short rows and pushes the last + // weeks behind the calendar's own scroller. Compact chip styling below buys + // back a chip or two per day. + dayMaxEvents: true, moreLinkClick: () => 'none' // Disable click, we use hover }) @@ -185,11 +191,39 @@ function buildEventsByDate() { eventsByDate.value = byDate } +// Height the calendar can have without pushing the page into a scrollbar. +// A first guess from the container's top, then corrected against whatever the +// page actually overflows by - the card's padding, page margins and anything +// else below it are not worth enumerating, and guessing at them is exactly how +// this ended up either clipped or scrolling. Floored so a very short window +// scrolls rather than crushing the grid. +const MIN_CALENDAR_HEIGHT = 460 +const FIT_PASSES = 3 +async function fitHeight() { + const el = calendarContainer.value + if (!el) return + const available = window.innerHeight - el.getBoundingClientRect().top + calendarOptions.value.height = Math.max(MIN_CALENDAR_HEIGHT, Math.round(available)) + for (let pass = 0; pass < FIT_PASSES; pass++) { + await nextTick() + const overflow = document.documentElement.scrollHeight - window.innerHeight + if (overflow <= 0) break + const next = calendarOptions.value.height - overflow + if (next <= MIN_CALENDAR_HEIGHT) { + calendarOptions.value.height = MIN_CALENDAR_HEIGHT + break + } + calendarOptions.value.height = next + } +} + onMounted(async () => { await loadEvents() // Add event delegation for more links await nextTick() + fitHeight() + window.addEventListener('resize', fitHeight) const container = document.querySelector('.calendar-container') if (container) { container.addEventListener('mouseenter', handleMoreLinkHover, true) @@ -198,6 +232,7 @@ onMounted(async () => { }) onUnmounted(() => { + window.removeEventListener('resize', fitHeight) const container = document.querySelector('.calendar-container') if (container) { container.removeEventListener('mouseenter', handleMoreLinkHover, true) @@ -254,44 +289,35 @@ function formatDate(dateStr) {