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 108 additions and 1 deletions
Showing only changes of commit 9b3b6f62a3 - Show all commits

View File

@ -124,6 +124,10 @@ const todayString = computed(() => {
const visibleWeeks = ref([]) const visibleWeeks = ref([])
let lastScrollRange = { startVW: null, endVW: null } let lastScrollRange = { startVW: null, endVW: null }
let pendingRebuild = false let pendingRebuild = false
// Week label column drag scrolling state (no momentum)
const isWeekColDragging = ref(false)
let weekColDragStartY = 0
let weekColDragStartScroll = 0
function scheduleRebuild(reason) { function scheduleRebuild(reason) {
if (pendingRebuild) return if (pendingRebuild) return
@ -498,6 +502,49 @@ function calculateSelection(anchorStr, otherStr) {
} }
} }
// ---------------- Week label column drag scrolling ----------------
function getWeekLabelRect() {
// Prefer header year label width as stable reference
const headerYear = document.querySelector('.calendar-header .year-label')
if (headerYear) return headerYear.getBoundingClientRect()
const weekLabel = viewport.value?.querySelector('.week-row .week-label')
return weekLabel ? weekLabel.getBoundingClientRect() : null
}
function handleWeekColMouseDown(e) {
if (e.button !== 0) return
if (e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return
if (!viewport.value) return
const rect = getWeekLabelRect()
if (!rect) return
if (e.clientX < rect.left || e.clientX > rect.right) return
isWeekColDragging.value = true
weekColDragStartY = e.clientY
weekColDragStartScroll = viewport.value.scrollTop
window.addEventListener('mousemove', handleWeekColMouseMove, { passive: false })
window.addEventListener('mouseup', handleWeekColMouseUp, { passive: false })
e.preventDefault()
e.stopPropagation()
}
function handleWeekColMouseMove(e) {
if (!isWeekColDragging.value || !viewport.value) return
const dy = e.clientY - weekColDragStartY
// Natural: drag down moves view to earlier content (scroll up)
viewport.value.scrollTop = Math.max(0, weekColDragStartScroll - dy)
e.preventDefault()
}
// (momentum removed per requirements)
function handleWeekColMouseUp(e) {
if (!isWeekColDragging.value) return
isWeekColDragging.value = false
window.removeEventListener('mousemove', handleWeekColMouseMove)
window.removeEventListener('mouseup', handleWeekColMouseUp)
e.preventDefault()
}
const onScroll = () => { const onScroll = () => {
if (viewport.value) scrollTop.value = viewport.value.scrollTop if (viewport.value) scrollTop.value = viewport.value.scrollTop
scheduleRebuild('scroll') scheduleRebuild('scroll')
@ -517,6 +564,8 @@ onMounted(() => {
viewportHeight.value = viewport.value.clientHeight viewportHeight.value = viewport.value.clientHeight
viewport.value.scrollTop = initialScrollTop.value viewport.value.scrollTop = initialScrollTop.value
viewport.value.addEventListener('scroll', onScroll) viewport.value.addEventListener('scroll', onScroll)
// Capture mousedown in viewport to allow dragging via week label column
viewport.value.addEventListener('mousedown', handleWeekColMouseDown, true)
} }
const timer = setInterval(() => { const timer = setInterval(() => {
@ -541,6 +590,7 @@ onMounted(() => {
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (viewport.value) { if (viewport.value) {
viewport.value.removeEventListener('scroll', onScroll) viewport.value.removeEventListener('scroll', onScroll)
viewport.value.removeEventListener('mousedown', handleWeekColMouseDown, true)
} }
if (rowProbeObserver && rowProbe.value) { if (rowProbeObserver && rowProbe.value) {
try { try {

View File

@ -5,7 +5,7 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, watch } from 'vue' import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
const props = defineProps({ const props = defineProps({
totalVirtualWeeks: { type: Number, required: true }, totalVirtualWeeks: { type: Number, required: true },
@ -19,6 +19,11 @@ const emit = defineEmits(['scroll-to'])
const jogwheelViewport = ref(null) const jogwheelViewport = ref(null)
const jogwheelContent = ref(null) const jogwheelContent = ref(null)
const syncLock = ref(null) const syncLock = ref(null)
// Drag state (no momentum, 1:1 mapping)
const isDragging = ref(false)
let dragStartY = 0
let mainStartScroll = 0
let dragScale = 1 // mainScrollPixels per mouse pixel
// Jogwheel content height is 1/10th of main calendar // Jogwheel content height is 1/10th of main calendar
const jogwheelHeight = computed(() => { const jogwheelHeight = computed(() => {
@ -30,6 +35,58 @@ const handleJogwheelScroll = () => {
syncFromJogwheel() syncFromJogwheel()
} }
function onDragMouseDown(e) {
if (e.button !== 0) return
isDragging.value = true
dragStartY = e.clientY
mainStartScroll = props.scrollTop
// Precompute scale between jogwheel scrollable range and main scrollable range
const mainScrollable = Math.max(0, props.totalVirtualWeeks * props.rowHeight - props.viewportHeight)
let jogScrollable = 0
if (jogwheelViewport.value && jogwheelContent.value) {
jogScrollable = Math.max(0, jogwheelContent.value.scrollHeight - jogwheelViewport.value.clientHeight)
}
dragScale = jogScrollable > 0 ? mainScrollable / jogScrollable : 1
if (!isFinite(dragScale) || dragScale <= 0) dragScale = 1
window.addEventListener('mousemove', onDragMouseMove, { passive: false })
window.addEventListener('mouseup', onDragMouseUp, { passive: false })
e.preventDefault()
}
function onDragMouseMove(e) {
if (!isDragging.value) return
const dy = e.clientY - dragStartY
// Natural content drag (drag down => scrollTop decreases)
let desired = mainStartScroll - dy * dragScale
if (desired < 0) desired = 0
const maxScroll = Math.max(0, props.totalVirtualWeeks * props.rowHeight - props.viewportHeight)
if (desired > maxScroll) desired = maxScroll
emit('scroll-to', desired)
e.preventDefault()
}
function onDragMouseUp(e) {
if (!isDragging.value) return
isDragging.value = false
window.removeEventListener('mousemove', onDragMouseMove)
window.removeEventListener('mouseup', onDragMouseUp)
e.preventDefault()
}
onMounted(() => {
if (jogwheelViewport.value) {
jogwheelViewport.value.addEventListener('mousedown', onDragMouseDown)
}
})
onBeforeUnmount(() => {
if (jogwheelViewport.value) {
jogwheelViewport.value.removeEventListener('mousedown', onDragMouseDown)
}
window.removeEventListener('mousemove', onDragMouseMove)
window.removeEventListener('mouseup', onDragMouseUp)
})
const syncFromJogwheel = () => { const syncFromJogwheel = () => {
if (!jogwheelViewport.value || !jogwheelContent.value) return if (!jogwheelViewport.value || !jogwheelContent.value) return