Implement changing year number. Added scroll support to numeric element.
This commit is contained in:
parent
018b9ecc55
commit
271a41dd23
@ -2,6 +2,7 @@
|
||||
import { computed } from 'vue'
|
||||
import { useCalendarStore } from '@/stores/CalendarStore'
|
||||
import { getLocalizedWeekdayNames, reorderByFirstDay, isoWeekInfo } from '@/utils/date'
|
||||
import Numeric from '@/components/Numeric.vue'
|
||||
|
||||
const props = defineProps({
|
||||
scrollTop: { type: Number, default: 0 },
|
||||
@ -11,15 +12,59 @@ const props = defineProps({
|
||||
|
||||
const calendarStore = useCalendarStore()
|
||||
|
||||
const yearLabel = computed(() => {
|
||||
// Emits: year-change -> { year, scrollTop }
|
||||
const emit = defineEmits(['year-change'])
|
||||
|
||||
const baseDate = new Date(1970, 0, 4 + calendarStore.config.first_day)
|
||||
const WEEK_MS = 7 * 24 * 60 * 60 * 1000
|
||||
|
||||
const topVirtualWeek = computed(() => {
|
||||
const topDisplayIndex = Math.floor(props.scrollTop / props.rowHeight)
|
||||
const topVW = topDisplayIndex + props.minVirtualWeek
|
||||
const baseDate = new Date(1970, 0, 4 + calendarStore.config.first_day)
|
||||
return topDisplayIndex + props.minVirtualWeek
|
||||
})
|
||||
|
||||
const currentYear = computed(() => {
|
||||
const firstDay = new Date(baseDate)
|
||||
firstDay.setDate(firstDay.getDate() + topVW * 7)
|
||||
firstDay.setDate(firstDay.getDate() + topVirtualWeek.value * 7)
|
||||
return isoWeekInfo(firstDay).year
|
||||
})
|
||||
|
||||
function virtualWeekOf(d) {
|
||||
const o = (d.getDay() - calendarStore.config.first_day + 7) % 7
|
||||
const fd = new Date(d)
|
||||
fd.setDate(d.getDate() - o)
|
||||
return Math.floor((fd - baseDate) / WEEK_MS)
|
||||
}
|
||||
|
||||
function changeYear(y) {
|
||||
if (y == null) return
|
||||
y = Math.round(Math.max(calendarStore.minYear, Math.min(calendarStore.maxYear, y)))
|
||||
if (y === currentYear.value) return
|
||||
// Current ISO week + intra-week fraction
|
||||
const vw = topVirtualWeek.value
|
||||
const weekStartScroll = (vw - props.minVirtualWeek) * props.rowHeight
|
||||
const frac = Math.max(0, Math.min(1, (props.scrollTop - weekStartScroll) / props.rowHeight))
|
||||
const weekStart = new Date(baseDate)
|
||||
weekStart.setDate(weekStart.getDate() + vw * 7)
|
||||
let { week: isoW } = isoWeekInfo(weekStart)
|
||||
// Build Monday of isoW in target year (Jan 4 trick)
|
||||
const jan4 = new Date(y, 0, 4)
|
||||
const jan4Mon = new Date(jan4)
|
||||
jan4Mon.setDate(jan4.getDate() - ((jan4.getDay() || 7) - 1)) // Monday of week 1
|
||||
let monday = new Date(jan4Mon)
|
||||
monday.setDate(monday.getDate() + (isoW - 1) * 7)
|
||||
// If overflow (week 53 not present) step back
|
||||
if (isoWeekInfo(monday).year !== y) {
|
||||
monday.setDate(monday.getDate() - 7)
|
||||
}
|
||||
const shift = (monday.getDay() - calendarStore.config.first_day + 7) % 7
|
||||
monday.setDate(monday.getDate() - shift)
|
||||
const targetVW = virtualWeekOf(monday)
|
||||
let scrollTop = (targetVW - props.minVirtualWeek) * props.rowHeight + frac * props.rowHeight
|
||||
if (Math.abs(scrollTop - props.scrollTop) < 0.5) scrollTop += 0.25 * props.rowHeight
|
||||
emit('year-change', { year: y, scrollTop })
|
||||
}
|
||||
|
||||
const weekdayNames = computed(() => {
|
||||
// Get Monday-first names, then reorder by first day, then add weekend info
|
||||
const mondayFirstNames = getLocalizedWeekdayNames()
|
||||
@ -36,7 +81,18 @@ const weekdayNames = computed(() => {
|
||||
|
||||
<template>
|
||||
<div class="calendar-header">
|
||||
<div class="year-label">{{ yearLabel }}</div>
|
||||
<div class="year-label">
|
||||
<Numeric
|
||||
:model-value="currentYear"
|
||||
@update:modelValue="changeYear"
|
||||
:min="calendarStore.minYear"
|
||||
:max="calendarStore.maxYear"
|
||||
:step="1"
|
||||
aria-label="Year"
|
||||
number-prefix=""
|
||||
number-postfix=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-for="day in weekdayNames"
|
||||
:key="day.name"
|
||||
|
@ -347,6 +347,14 @@ const handleDayTouchEnd = (dateStr) => {
|
||||
const handleEventClick = (eventInstanceId) => {
|
||||
emit('edit-event', eventInstanceId)
|
||||
}
|
||||
|
||||
// Handle year change emitted from CalendarHeader: scroll to computed target position
|
||||
const handleHeaderYearChange = ({ scrollTop: st }) => {
|
||||
const maxScroll = contentHeight.value - viewportHeight.value
|
||||
const clamped = Math.max(0, Math.min(st, isFinite(maxScroll) ? maxScroll : st))
|
||||
scrollTop.value = clamped
|
||||
viewport.value && (viewport.value.scrollTop = clamped)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -361,6 +369,7 @@ const handleEventClick = (eventInstanceId) => {
|
||||
:scroll-top="scrollTop"
|
||||
:row-height="rowHeight"
|
||||
:min-virtual-week="minVirtualWeek"
|
||||
@year-change="handleHeaderYearChange"
|
||||
/>
|
||||
<div class="calendar-container">
|
||||
<div class="calendar-viewport" ref="viewport">
|
||||
|
@ -12,6 +12,7 @@
|
||||
tabindex="0"
|
||||
@pointerdown="onPointerDown"
|
||||
@keydown="onKey"
|
||||
@wheel.prevent="onWheel"
|
||||
>
|
||||
<span class="value" :title="String(current)">{{ display }}</span>
|
||||
</div>
|
||||
@ -217,6 +218,23 @@ function onKey(e) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
}
|
||||
|
||||
function onWheel(e) {
|
||||
// Inverted: deltaY < 0 => decrement, > 0 => increment
|
||||
const direction = e.deltaY < 0 ? -1 : e.deltaY > 0 ? 1 : 0
|
||||
if (direction === 0) return
|
||||
// Use current index in allValidValues list (prefix values included)
|
||||
const idx = allValidValues.value.indexOf(current.value)
|
||||
if (idx !== -1) {
|
||||
const nextIdx = idx + direction
|
||||
if (nextIdx >= 0 && nextIdx < allValidValues.value.length) {
|
||||
current.value = allValidValues.value[nextIdx]
|
||||
}
|
||||
} else {
|
||||
// Fallback numeric adjustment if not found
|
||||
current.value = current.value + direction * props.step
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
Loading…
x
Reference in New Issue
Block a user