Major new version #2

Merged
LeoVasanko merged 86 commits from vol002 into main 2025-08-26 05:58:24 +01:00
2 changed files with 90 additions and 91 deletions
Showing only changes of commit 10c9cedc8e - Show all commits

View File

@ -115,20 +115,25 @@ const contentHeight = computed(() => {
const visibleWeeks = ref([]) const visibleWeeks = ref([])
let lastScrollRange = { startVW: null, endVW: null } let lastScrollRange = { startVW: null, endVW: null }
let pendingRebuild = false
let rebuildTimer = null
let lastReason = null
const REBUILD_DEBOUNCE_MS = 40
function scheduleRebuild(reason) { function scheduleRebuild(reason) {
if (pendingRebuild) return lastReason = lastReason ? lastReason + ',' + reason : reason
pendingRebuild = true if (rebuildTimer) return
const cb = () => { rebuildTimer = setTimeout(() => {
pendingRebuild = false const r = lastReason
rebuildVisibleWeeks(reason) rebuildTimer = null
} lastReason = null
const fn = () => rebuildVisibleWeeks(r)
if ('requestIdleCallback' in window) { if ('requestIdleCallback' in window) {
requestIdleCallback(cb, { timeout: 120 }) requestIdleCallback(fn, { timeout: 120 })
} else { } else {
requestAnimationFrame(cb) requestAnimationFrame(fn)
} }
}, REBUILD_DEBOUNCE_MS)
} }
const scrollManager = createScrollManager({ viewport, scheduleRebuild }) const scrollManager = createScrollManager({ viewport, scheduleRebuild })
@ -152,7 +157,7 @@ const monthScrollManager = createMonthScrollManager({
setScrollTop, setScrollTop,
}) })
const { handleMonthScrollMouseDown, handleMonthScrollTouchStart, handleMonthScrollWheel } = const { handleMonthScrollPointerDown, handleMonthScrollTouchStart, handleMonthScrollWheel } =
monthScrollManager monthScrollManager
const initialScrollTop = computed(() => { const initialScrollTop = computed(() => {
@ -178,7 +183,7 @@ function rebuildVisibleWeeks(reason) {
) )
const startVW = Math.max(minVirtualWeek.value, startIdx + minVirtualWeek.value) const startVW = Math.max(minVirtualWeek.value, startIdx + minVirtualWeek.value)
const endVW = Math.min(maxVirtualWeek.value, endIdx + minVirtualWeek.value) const endVW = Math.min(maxVirtualWeek.value, endIdx + minVirtualWeek.value)
console.log('[CalendarView] rebuildVisibleWeeks', { console.debug('[CalendarView] rebuildVisibleWeeks', {
reason, reason,
currentScrollTop, currentScrollTop,
startIdx, startIdx,
@ -229,13 +234,6 @@ function measureFromProbe() {
const topVirtualWeek = Math.floor(scrollTop.value / oldH) + minVirtualWeek.value const topVirtualWeek = Math.floor(scrollTop.value / oldH) + minVirtualWeek.value
rowHeight.value = newH rowHeight.value = newH
const newScrollTop = (topVirtualWeek - minVirtualWeek.value) * newH const newScrollTop = (topVirtualWeek - minVirtualWeek.value) * newH
console.log('[CalendarView] measureFromProbe rowHeight change', {
oldH,
newH,
topVirtualWeek,
oldScrollTop: scrollTop.value,
newScrollTop,
})
setScrollTop(newScrollTop, 'row-height-change') setScrollTop(newScrollTop, 'row-height-change')
} }
} }
@ -712,8 +710,8 @@ window.addEventListener('resize', () => {
height: `calc(var(--row-h) * ${monthWeek.monthLabel?.weeksSpan || 1})`, height: `calc(var(--row-h) * ${monthWeek.monthLabel?.weeksSpan || 1})`,
top: (monthWeek.top || 0) + 'px', top: (monthWeek.top || 0) + 'px',
}" }"
@mousedown="handleMonthScrollMouseDown" @pointerdown="handleMonthScrollPointerDown"
@touchstart="handleMonthScrollTouchStart" @touchstart.prevent="handleMonthScrollTouchStart"
@wheel="handleMonthScrollWheel" @wheel="handleMonthScrollWheel"
> >
<span :class="{ bottomup: shouldRotateMonth(monthWeek.monthLabel?.text) }">{{ <span :class="{ bottomup: shouldRotateMonth(monthWeek.monthLabel?.text) }">{{
@ -805,6 +803,7 @@ header h1 {
overflow: hidden; overflow: hidden;
cursor: ns-resize; cursor: ns-resize;
user-select: none; user-select: none;
touch-action: none;
} }
.month-label > span { .month-label > span {

View File

@ -156,79 +156,79 @@ export function createMonthScrollManager({
contentHeight, contentHeight,
setScrollTop, setScrollTop,
}) { }) {
let isMonthScrolling = false let dragging = false
let monthScrollStartY = 0 let startY = 0
let monthScrollStartScroll = 0 let startScroll = 0
const SPEED = 10
const handleMonthScrollMouseDown = (e) => { function applyDrag(clientY, reason) {
if (e.button !== 0) return const deltaY = clientY - startY
isMonthScrolling = true const newScrollTop = startScroll - deltaY * SPEED
monthScrollStartY = e.clientY
monthScrollStartScroll = viewport.value?.scrollTop || 0
const handleMouseMove = (e) => {
if (!isMonthScrolling) return
const deltaY = e.clientY - monthScrollStartY
const newScrollTop = monthScrollStartScroll - deltaY * 10
const maxScroll = Math.max(0, contentHeight.value - viewportHeight.value) const maxScroll = Math.max(0, contentHeight.value - viewportHeight.value)
const clampedScroll = Math.max(0, Math.min(newScrollTop, maxScroll)) const clamped = Math.max(0, Math.min(newScrollTop, maxScroll))
setScrollTop(clamped, reason)
}
setScrollTop(clampedScroll, 'month-scroll-drag') function endDrag() {
dragging = false
window.removeEventListener('pointermove', onPointerMove, true)
window.removeEventListener('pointerup', onPointerUp, true)
window.removeEventListener('pointercancel', onPointerUp, true)
window.removeEventListener('touchmove', onTouchMove)
window.removeEventListener('touchend', onTouchEnd)
window.removeEventListener('touchcancel', onTouchEnd)
}
function onPointerMove(e) {
if (!dragging) return
applyDrag(e.clientY, 'month-scroll-drag')
e.preventDefault() e.preventDefault()
} }
function onPointerUp() {
const handleMouseUp = () => { if (dragging) endDrag()
isMonthScrolling = false
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
} }
document.addEventListener('mousemove', handleMouseMove) function onTouchMove(e) {
document.addEventListener('mouseup', handleMouseUp) if (!dragging) return
const t = e.touches[0]
applyDrag(t.clientY, 'month-scroll-touch')
e.preventDefault() e.preventDefault()
} }
function onTouchEnd() {
if (dragging) endDrag()
}
const handleMonthScrollTouchStart = (e) => { function handleMonthScrollPointerDown(e) {
if (e.button !== undefined && e.button !== 0) return
e.preventDefault()
dragging = true
startY = e.clientY
startScroll = viewport.value?.scrollTop || 0
window.addEventListener('pointermove', onPointerMove, true)
window.addEventListener('pointerup', onPointerUp, true)
window.addEventListener('pointercancel', onPointerUp, true)
}
function handleMonthScrollTouchStart(e) {
if (e.touches.length !== 1) return if (e.touches.length !== 1) return
isMonthScrolling = true dragging = true
monthScrollStartY = e.touches[0].clientY const t = e.touches[0]
monthScrollStartScroll = viewport.value?.scrollTop || 0 startY = t.clientY
startScroll = viewport.value?.scrollTop || 0
const handleTouchMove = (e) => { window.addEventListener('touchmove', onTouchMove, { passive: false })
if (!isMonthScrolling || e.touches.length !== 1) return window.addEventListener('touchend', onTouchEnd, { passive: false })
const deltaY = e.touches[0].clientY - monthScrollStartY window.addEventListener('touchcancel', onTouchEnd, { passive: false })
const newScrollTop = monthScrollStartScroll - deltaY * 10
const maxScroll = Math.max(0, contentHeight.value - viewportHeight.value)
const clampedScroll = Math.max(0, Math.min(newScrollTop, maxScroll))
setScrollTop(clampedScroll, 'month-scroll-touch')
e.preventDefault() e.preventDefault()
} }
const handleTouchEnd = () => { function handleMonthScrollWheel(e) {
isMonthScrolling = false
document.removeEventListener('touchmove', handleTouchMove)
document.removeEventListener('touchend', handleTouchEnd)
}
document.addEventListener('touchmove', handleTouchMove, { passive: false })
document.addEventListener('touchend', handleTouchEnd)
e.preventDefault()
}
const handleMonthScrollWheel = (e) => {
const currentScroll = viewport.value?.scrollTop || 0 const currentScroll = viewport.value?.scrollTop || 0
const newScrollTop = currentScroll + e.deltaY * 10 const newScrollTop = currentScroll + e.deltaY * SPEED
const maxScroll = Math.max(0, contentHeight.value - viewportHeight.value) const maxScroll = Math.max(0, contentHeight.value - viewportHeight.value)
const clampedScroll = Math.max(0, Math.min(newScrollTop, maxScroll)) const clamped = Math.max(0, Math.min(newScrollTop, maxScroll))
setScrollTop(clamped, 'month-scroll-wheel')
setScrollTop(clampedScroll, 'month-scroll-wheel')
e.preventDefault() e.preventDefault()
} }
return { return { handleMonthScrollPointerDown, handleMonthScrollTouchStart, handleMonthScrollWheel }
handleMonthScrollMouseDown,
handleMonthScrollTouchStart,
handleMonthScrollWheel,
}
} }