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 @@
+
@@ -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) {