diff --git a/plugins/notifications/frontend/views/CalendarView.vue b/plugins/notifications/frontend/views/CalendarView.vue
index f3121df..601bde0 100644
--- a/plugins/notifications/frontend/views/CalendarView.vue
+++ b/plugins/notifications/frontend/views/CalendarView.vue
@@ -7,25 +7,6 @@
-
-
@@ -95,12 +76,8 @@ 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 })
// Store events by date for hover lookup
-const eventsByDate = ref({})
const calendarOptions = ref({
plugins: [dayGridPlugin],
@@ -127,70 +104,16 @@ const calendarOptions = ref({
// 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
+ // FullCalendar's own popover: click '+N more' and it lists that day's events,
+ // each still opening the detail modal through eventClick. The custom hover
+ // tooltip this replaces sliced the hidden events from a hardcoded index of 3,
+ // which was the fixed dayMaxEvents at the time. Now the cap varies with the
+ // row height, so that slice pointed at the wrong events - and when a short
+ // row showed one chip, at an empty slice, leaving '+2 more' with nothing
+ // behind it. A popover has no such bookkeeping, and works on a touch screen.
+ moreLinkClick: 'popover'
})
-function hideMoreTooltip() {
- moreTooltipData.value = []
-}
-
-function handleMoreLinkHover(e) {
- const moreLink = e.target.closest('.fc-daygrid-more-link')
- if (!moreLink) {
- return
- }
-
- // Find the day cell and get its date
- const dayCell = moreLink.closest('.fc-daygrid-day')
- if (!dayCell) return
-
- const dateStr = dayCell.getAttribute('data-date')
- if (!dateStr || !eventsByDate.value[dateStr]) return
-
- // Get events for this date that would be hidden (after first 3)
- const dayEvents = eventsByDate.value[dateStr]
- const hiddenEvents = dayEvents.slice(3).map(evt => ({
- title: evt.title,
- color: evt.backgroundColor || '#14abef',
- // Include full event data for modal
- start: evt.start,
- end: evt.end,
- extendedProps: evt.extendedProps || {}
- }))
-
- if (hiddenEvents.length === 0) return
-
- moreTooltipData.value = hiddenEvents
-
- // Position tooltip
- const rect = moreLink.getBoundingClientRect()
- tooltipPosition.value = {
- left: rect.left,
- top: rect.bottom + 5
- }
-}
-
-function handleMoreLinkLeave(e) {
- const related = e.relatedTarget
- // Don't hide if moving to the tooltip itself
- if (related && (related.closest('.fc-more-tooltip') || related.closest('.fc-daygrid-more-link'))) {
- return
- }
- hideMoreTooltip()
-}
-
-function buildEventsByDate() {
- const byDate = {}
- for (const evt of events.value) {
- const dateStr = evt.start ? evt.start.split('T')[0] : null
- if (dateStr) {
- if (!byDate[dateStr]) byDate[dateStr] = []
- byDate[dateStr].push(evt)
- }
- }
- 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
@@ -220,24 +143,13 @@ async function fitHeight() {
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)
- container.addEventListener('mouseleave', handleMoreLinkLeave, true)
- }
})
onUnmounted(() => {
window.removeEventListener('resize', fitHeight)
- const container = document.querySelector('.calendar-container')
- if (container) {
- container.removeEventListener('mouseenter', handleMoreLinkHover, true)
- container.removeEventListener('mouseleave', handleMoreLinkLeave, true)
- }
})
onMounted(async () => {
@@ -249,7 +161,6 @@ async function loadEvents() {
const response = await notificationsApi.getCalendar()
events.value = response.data.data
calendarOptions.value.events = events.value
- buildEventsByDate()
} catch (error) {
console.error('Error loading calendar events:', error)
}
@@ -259,17 +170,6 @@ function closeEventModal() {
selectedEvent.value = null
}
-function openEventFromTooltip(evt) {
- // Create an event-like object that matches FullCalendar's event structure
- selectedEvent.value = {
- title: evt.title,
- start: evt.start,
- end: evt.end,
- extendedProps: evt.extendedProps
- }
- hideMoreTooltip()
-}
-
function formatDate(dateStr) {
if (!dateStr) return ''
// allDay events carry a site-local date-only value (YYYY-MM-DD). Build the
@@ -304,6 +204,23 @@ function formatDate(dateStr) {
overflow-y: hidden !important;
}
+/* The popover is FullCalendar's own element, so it ships light-themed. */
+:deep(.fc-popover) {
+ background: var(--bg-card-solid);
+ border: 1px solid var(--border);
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
+ z-index: 1000;
+}
+:deep(.fc-popover-header) {
+ background: var(--bg);
+ color: var(--text);
+ padding: 8px 10px;
+ font-weight: 600;
+}
+:deep(.fc-popover-body) {
+ color: var(--text);
+}
+
/* 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) {