From 5a0d6804bc551db4bb6ae274ce1f9e1485631116 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Wed, 27 Aug 2025 08:04:29 -0600 Subject: [PATCH] Mouse/touch event handling improvement - prefer passive handlers - fix event moving on touch - faster selection updates --- src/components/CalendarView.vue | 18 +++++++++++------- src/components/CalendarWeek.vue | 2 +- src/components/EventOverlay.vue | 7 ++++--- src/plugins/calendarHistory.js | 4 +--- src/plugins/scrollManager.js | 3 +-- src/stores/CalendarStore.js | 8 +------- 6 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/components/CalendarView.vue b/src/components/CalendarView.vue index 7a95939..b335e0b 100644 --- a/src/components/CalendarView.vue +++ b/src/components/CalendarView.vue @@ -248,7 +248,7 @@ function onGlobalTouchMove(e) { if (!isDragging.value) return const t = e.touches && e.touches[0] if (!t) return - e.preventDefault() + if (e.cancelable) e.preventDefault() const dateStr = getDateUnderPoint(t.clientX, t.clientY) if (dateStr) updateDrag(dateStr) } @@ -435,10 +435,11 @@ function buildSearchResults() { watch(searchQuery, buildSearchResults) watch( - () => calendarStore.eventsMutation, + () => calendarStore.events, () => { if (searchOpen.value && searchQuery.value.trim()) buildSearchResults() }, + { deep: true }, ) function openSearch(prefill = '') { @@ -569,19 +570,22 @@ watch( }, ) -// Event changes +// Event changes (optimized): react to mutation counter & targeted range payload watch( () => calendarStore.events, - () => { - refreshEvents('events') - }, + () => refreshEvents('events'), { deep: true }, ) // Reflect selection & events by rebuilding day objects in-place watch( () => [selection.value.startDate, selection.value.dayCount], - () => refreshEvents('selection'), + ([start, count]) => { + const hasSel = !!start && !!count && count > 0 + const end = hasSel ? addDaysStr(start, count, DEFAULT_TZ) : null + for (const w of visibleWeeks.value) + for (const d of w.days) d.isSelected = hasSel && d.date >= start && d.date < end + }, ) // Rebuild if viewport height changes (e.g., resize) diff --git a/src/components/CalendarWeek.vue b/src/components/CalendarWeek.vue index b17f955..e4d7229 100644 --- a/src/components/CalendarWeek.vue +++ b/src/components/CalendarWeek.vue @@ -57,7 +57,7 @@ function shouldRotateMonth(label) { @mousedown="handleDayMouseDown(day.date)" @mouseenter="handleDayMouseEnter(day.date)" @mouseup="handleDayMouseUp(day.date)" - @touchstart="handleDayTouchStart(day.date)" + @touchstart.passive="handleDayTouchStart(day.date)" /> diff --git a/src/components/EventOverlay.vue b/src/components/EventOverlay.vue index 4a4482b..f88735d 100644 --- a/src/components/EventOverlay.vue +++ b/src/components/EventOverlay.vue @@ -278,9 +278,7 @@ function startLocalDrag(init, evt) { } } - if (!(evt.pointerType === 'touch')) { - evt.preventDefault() - } + if (evt.cancelable) evt.preventDefault() window.addEventListener('pointermove', onDragPointerMove, { passive: false }) window.addEventListener('pointerup', onDragPointerUp, { passive: false }) @@ -486,6 +484,8 @@ function applyRangeDuringDrag(st, startDate, endDate) { user-select: none; z-index: 1; text-align: center; + /* Ensure touch pointer events aren't turned into a scroll gesture; needed for reliable drag on mobile */ + touch-action: none; } /* Inner title wrapper ensures proper ellipsis within flex/grid constraints */ @@ -510,6 +510,7 @@ function applyRangeDuringDrag(st, startDate, endDate) { background: transparent; z-index: 2; cursor: ew-resize; + touch-action: none; /* Allow touch resizing without scroll */ } .event-span .resize-handle.left { diff --git a/src/plugins/calendarHistory.js b/src/plugins/calendarHistory.js index e74c544..8219f44 100644 --- a/src/plugins/calendarHistory.js +++ b/src/plugins/calendarHistory.js @@ -18,7 +18,6 @@ function restoreCalendarState(store, snap) { store.weekend = Array.isArray(snap.weekend) ? [...snap.weekend] : snap.weekend store.config = JSON.parse(JSON.stringify(snap.config)) store.events = new Map([...snap.events].map(([k, v]) => [k, { ...v }])) - store.eventsMutation = (store.eventsMutation + 1) % 1_000_000_000 } export function calendarHistory({ store }) { @@ -36,8 +35,7 @@ export function calendarHistory({ store }) { function serializeForComparison() { const evCount = store.events instanceof Map ? store.events.size : 0 - const em = store.eventsMutation || 0 - return `${em}|${evCount}|${store.today}|${JSON.stringify(store.config)}` + return `${evCount}|${store.today}|${JSON.stringify(store.config)}` } function pushSnapshot() { diff --git a/src/plugins/scrollManager.js b/src/plugins/scrollManager.js index 21fb1d9..f31483a 100644 --- a/src/plugins/scrollManager.js +++ b/src/plugins/scrollManager.js @@ -124,7 +124,6 @@ function createMomentumDrag({ return } applyDragPosition(e.touches[0].clientY, reasonDragTouch) - e.preventDefault() } function handlePointerDown(e) { if (e.button !== undefined && e.button !== 0) return @@ -158,7 +157,7 @@ function createMomentumDrag({ window.addEventListener('touchmove', onTouchMove, { passive: false }) window.addEventListener('touchend', endDrag, { passive: false }) window.addEventListener('touchcancel', endDrag, { passive: false }) - e.preventDefault() + if (e.cancelable) e.preventDefault() } function onPointerLockChange() { const lockedEl = document.pointerLockElement diff --git a/src/stores/CalendarStore.js b/src/stores/CalendarStore.js index 04a25ce..fa99a7d 100644 --- a/src/stores/CalendarStore.js +++ b/src/stores/CalendarStore.js @@ -14,9 +14,6 @@ export const useCalendarStore = defineStore('calendar', { today: toLocalString(new Date(), DEFAULT_TZ), now: new Date().toISOString(), events: new Map(), - // Lightweight mutation counter so views can rebuild in a throttled / idle way - // without tracking deep reactivity on every event object. - eventsMutation: 0, // Incremented internally by history plugin to force reactive updates for canUndo/canRedo historyTick: 0, historyCanUndo: false, @@ -117,10 +114,7 @@ export const useCalendarStore = defineStore('calendar', { return 'e-' + Math.random().toString(36).slice(2, 10) + '-' + Date.now().toString(36) }, - notifyEventsChanged() { - // Bump simple counter (wrapping to avoid overflow in extreme long sessions) - this.eventsMutation = (this.eventsMutation + 1) % 1_000_000_000 - }, + notifyEventsChanged() {}, touchEvents() { this.notifyEventsChanged() },