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>
|
<h1>Calendar</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="calendar-container card">
|
<div ref="calendarContainer" class="calendar-container card">
|
||||||
<FullCalendar :options="calendarOptions" />
|
<FullCalendar :options="calendarOptions" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -94,6 +94,7 @@ import { notificationsApi } from '@/api'
|
|||||||
const events = ref([])
|
const events = ref([])
|
||||||
const selectedEvent = ref(null)
|
const selectedEvent = ref(null)
|
||||||
const calendarRef = ref(null)
|
const calendarRef = ref(null)
|
||||||
|
const calendarContainer = ref(null)
|
||||||
const moreTooltip = ref(null)
|
const moreTooltip = ref(null)
|
||||||
const moreTooltipData = ref([])
|
const moreTooltipData = ref([])
|
||||||
const tooltipPosition = ref({ left: 0, top: 0 })
|
const tooltipPosition = ref({ left: 0, top: 0 })
|
||||||
@@ -113,14 +114,19 @@ const calendarOptions = ref({
|
|||||||
center: 'title',
|
center: 'title',
|
||||||
right: 'dayGridMonth,dayGridWeek'
|
right: 'dayGridMonth,dayGridWeek'
|
||||||
},
|
},
|
||||||
// 'auto' sized every row to its own content, so a month of mostly empty days
|
// Measured at mount and on resize (fitHeight), not a viewport calc: the
|
||||||
// collapsed into thin strips and the grid looked squashed. A viewport-relative
|
// calendar starts below a header and a page title whose heights are not
|
||||||
// height makes the calendar fill the space it has been given, and expandRows
|
// knowable here, and guessing at them is what left the grid either clipped
|
||||||
// shares that height evenly across the weeks rather than leaving a gap under
|
// or overflowing. expandRows shares that height across the six weeks so an
|
||||||
// the last one.
|
// empty month fills it instead of collapsing into strips.
|
||||||
height: 'calc(100vh - 230px)',
|
height: 600,
|
||||||
expandRows: true,
|
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
|
moreLinkClick: () => 'none' // Disable click, we use hover
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -185,11 +191,39 @@ function buildEventsByDate() {
|
|||||||
eventsByDate.value = byDate
|
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 () => {
|
onMounted(async () => {
|
||||||
await loadEvents()
|
await loadEvents()
|
||||||
|
|
||||||
// Add event delegation for more links
|
// Add event delegation for more links
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
fitHeight()
|
||||||
|
window.addEventListener('resize', fitHeight)
|
||||||
const container = document.querySelector('.calendar-container')
|
const container = document.querySelector('.calendar-container')
|
||||||
if (container) {
|
if (container) {
|
||||||
container.addEventListener('mouseenter', handleMoreLinkHover, true)
|
container.addEventListener('mouseenter', handleMoreLinkHover, true)
|
||||||
@@ -198,6 +232,7 @@ onMounted(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', fitHeight)
|
||||||
const container = document.querySelector('.calendar-container')
|
const container = document.querySelector('.calendar-container')
|
||||||
if (container) {
|
if (container) {
|
||||||
container.removeEventListener('mouseenter', handleMoreLinkHover, true)
|
container.removeEventListener('mouseenter', handleMoreLinkHover, true)
|
||||||
@@ -254,44 +289,35 @@ function formatDate(dateStr) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.calendar-container {
|
.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
|
/* Compact chips: the row height decides how many events a day can show before
|
||||||
reach them. A floor under each day cell keeps the grid readable on a short
|
they roll into a '+N more', so every pixel saved here is one more visible. */
|
||||||
window, where the viewport-relative height above would otherwise squeeze the
|
:deep(.fc-daygrid-event) {
|
||||||
rows back down. */
|
font-size: 12px;
|
||||||
:deep(.fc-daygrid-day-frame) {
|
line-height: 1.3;
|
||||||
min-height: 110px;
|
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
|
/* The day number was tight against the top edge once the cells grew. Kept
|
||||||
with a docked browser, or a shopfloor display in portrait - it can resolve
|
small: this strip is pure overhead in the row-height budget FullCalendar
|
||||||
smaller than the grid needs. This stops it undercutting the day cells. */
|
uses to decide how many chips fit before a '+N more'. */
|
||||||
: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. */
|
|
||||||
:deep(.fc-daygrid-day-top) {
|
: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 {
|
.modal {
|
||||||
|
|||||||
Reference in New Issue
Block a user