diff --git a/src/components/CalendarHeader.vue b/src/components/CalendarHeader.vue index 1e52287..17d90b7 100644 --- a/src/components/CalendarHeader.vue +++ b/src/components/CalendarHeader.vue @@ -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(() => {