Fit the calendar to the space it actually has
The previous attempt at this was verified against an empty month, which is why it measured clean and still scrolled: put events in the month and the fixed height clipped the grid, leaving the last week behind FullCalendar's own scroller. The media-query floors it added are gone. Two things were guessed and are now measured. The height came from calc(100vh - 230px), a stand-in for chrome that is not 230px tall; it is taken from the container's real top at mount and on resize, then corrected against whatever the page still overflows by. And dayMaxEvents was a fixed 3, which does not fit an 80px row - hence the overflow. It is `true` now, so FullCalendar shows as many chips as the row genuinely holds and rolls the rest into a '+N more'; that is the only setting that cannot outgrow the box. The chips and the day-number strip are tighter, so more fit before that happens. With 44 events across 11 days, at 1920x1080, 1600x900, 1366x768 and 1280x720: all six weeks visible, no page scrollbar anywhere. A full-height display shows every chip; a laptop shows one and a link.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<h1>Calendar</h1>
|
||||
</div>
|
||||
|
||||
<div class="calendar-container card">
|
||||
<div ref="calendarContainer" class="calendar-container card">
|
||||
<FullCalendar :options="calendarOptions" />
|
||||
</div>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
<style scoped>
|
||||
.calendar-container {
|
||||
min-height: 640px;
|
||||
/* The height itself is measured in fitHeight(); this is only a floor for the
|
||||
first paint, before that measurement lands. */
|
||||
min-height: 460px;
|
||||
}
|
||||
|
||||
/* :deep, because these are FullCalendar's own elements and scoped styles do not
|
||||
reach them. A floor under each day cell keeps the grid readable on a short
|
||||
window, where the viewport-relative height above would otherwise squeeze the
|
||||
rows back down. */
|
||||
:deep(.fc-daygrid-day-frame) {
|
||||
min-height: 110px;
|
||||
/* Compact chips: the row height decides how many events a day can show before
|
||||
they roll into a '+N more', so every pixel saved here is one more visible. */
|
||||
:deep(.fc-daygrid-event) {
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
padding: 1px 4px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
:deep(.fc-daygrid-more-link) {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* The calc() height above is viewport-relative, so on a short window - a laptop
|
||||
with a docked browser, or a shopfloor display in portrait - it can resolve
|
||||
smaller than the grid needs. This stops it undercutting the day cells. */
|
||||
:deep(.fc) {
|
||||
min-height: 620px;
|
||||
}
|
||||
|
||||
/* Those floors are sized for a full-height display. On a shorter window they
|
||||
add up to more than calc(100vh - 230px) gives - six rows at 110px plus the
|
||||
toolbar overflows a 768px screen - and the page grew a scrollbar to show a
|
||||
month that used to fit. Relax them there: still floored well above the thin
|
||||
strips this was fixing, just not taller than the window. */
|
||||
@media (max-height: 899px) {
|
||||
.calendar-container {
|
||||
min-height: 520px;
|
||||
}
|
||||
:deep(.fc) {
|
||||
min-height: 500px;
|
||||
}
|
||||
:deep(.fc-daygrid-day-frame) {
|
||||
min-height: 78px;
|
||||
}
|
||||
}
|
||||
|
||||
/* The day number was tight against the top edge once the cells grew. */
|
||||
/* The day number was tight against the top edge once the cells grew. Kept
|
||||
small: this strip is pure overhead in the row-height budget FullCalendar
|
||||
uses to decide how many chips fit before a '+N more'. */
|
||||
:deep(.fc-daygrid-day-top) {
|
||||
padding: 2px 4px;
|
||||
padding: 1px 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
:deep(.fc-daygrid-day-events) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
:deep(.fc-daygrid-event-harness) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.modal {
|
||||
|
||||
Reference in New Issue
Block a user