Major new version #2
@ -24,9 +24,12 @@ const topVirtualWeek = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const currentYear = computed(() => {
|
const currentYear = computed(() => {
|
||||||
const firstDay = new Date(baseDate)
|
const weekStart = new Date(baseDate)
|
||||||
firstDay.setDate(firstDay.getDate() + topVirtualWeek.value * 7)
|
weekStart.setDate(weekStart.getDate() + topVirtualWeek.value * 7)
|
||||||
return isoWeekInfo(firstDay).year
|
// ISO anchor: Thursday of current calendar week
|
||||||
|
const anchor = new Date(weekStart)
|
||||||
|
anchor.setDate(anchor.getDate() + ((4 - anchor.getDay() + 7) % 7))
|
||||||
|
return isoWeekInfo(anchor).year
|
||||||
})
|
})
|
||||||
|
|
||||||
function virtualWeekOf(d) {
|
function virtualWeekOf(d) {
|
||||||
@ -36,30 +39,41 @@ function virtualWeekOf(d) {
|
|||||||
return Math.floor((fd - baseDate) / WEEK_MS)
|
return Math.floor((fd - baseDate) / WEEK_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isoWeekMonday(isoYear, isoWeek) {
|
||||||
|
// Monday of ISO week 1
|
||||||
|
const jan4 = new Date(isoYear, 0, 4)
|
||||||
|
const week1Mon = new Date(jan4)
|
||||||
|
week1Mon.setDate(week1Mon.getDate() - ((week1Mon.getDay() + 6) % 7))
|
||||||
|
const target = new Date(week1Mon)
|
||||||
|
target.setDate(target.getDate() + (isoWeek - 1) * 7)
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
function changeYear(y) {
|
function changeYear(y) {
|
||||||
if (y == null) return
|
if (y == null) return
|
||||||
y = Math.round(Math.max(calendarStore.minYear, Math.min(calendarStore.maxYear, y)))
|
y = Math.round(Math.max(calendarStore.minYear, Math.min(calendarStore.maxYear, y)))
|
||||||
if (y === currentYear.value) return
|
if (y === currentYear.value) return
|
||||||
// Current ISO week + intra-week fraction
|
|
||||||
const vw = topVirtualWeek.value
|
const vw = topVirtualWeek.value
|
||||||
|
// Fraction within current row for smooth vertical position preservation
|
||||||
const weekStartScroll = (vw - props.minVirtualWeek) * props.rowHeight
|
const weekStartScroll = (vw - props.minVirtualWeek) * props.rowHeight
|
||||||
const frac = Math.max(0, Math.min(1, (props.scrollTop - weekStartScroll) / props.rowHeight))
|
const frac = Math.max(0, Math.min(1, (props.scrollTop - weekStartScroll) / props.rowHeight))
|
||||||
const weekStart = new Date(baseDate)
|
// Derive current ISO week via anchor Thursday
|
||||||
weekStart.setDate(weekStart.getDate() + vw * 7)
|
const curCalWeekStart = new Date(baseDate)
|
||||||
let { week: isoW } = isoWeekInfo(weekStart)
|
curCalWeekStart.setDate(curCalWeekStart.getDate() + vw * 7)
|
||||||
// Build Monday of isoW in target year (Jan 4 trick)
|
const curAnchorThu = new Date(curCalWeekStart)
|
||||||
const jan4 = new Date(y, 0, 4)
|
curAnchorThu.setDate(curAnchorThu.getDate() + ((4 - curAnchorThu.getDay() + 7) % 7))
|
||||||
const jan4Mon = new Date(jan4)
|
let { week: isoW } = isoWeekInfo(curAnchorThu)
|
||||||
jan4Mon.setDate(jan4.getDate() - ((jan4.getDay() || 7) - 1)) // Monday of week 1
|
// Build Monday of that ISO week in target year; fallback if week absent (53)
|
||||||
let monday = new Date(jan4Mon)
|
let weekMon = isoWeekMonday(y, isoW)
|
||||||
monday.setDate(monday.getDate() + (isoW - 1) * 7)
|
if (isoWeekInfo(weekMon).year !== y) {
|
||||||
// If overflow (week 53 not present) step back
|
isoW--
|
||||||
if (isoWeekInfo(monday).year !== y) {
|
weekMon = isoWeekMonday(y, isoW)
|
||||||
monday.setDate(monday.getDate() - 7)
|
|
||||||
}
|
}
|
||||||
const shift = (monday.getDay() - calendarStore.config.first_day + 7) % 7
|
// Align to configured first day
|
||||||
monday.setDate(monday.getDate() - shift)
|
const shift = (weekMon.getDay() - calendarStore.config.first_day + 7) % 7
|
||||||
const targetVW = virtualWeekOf(monday)
|
const calWeekStart = new Date(weekMon)
|
||||||
|
calWeekStart.setDate(calWeekStart.getDate() - shift)
|
||||||
|
const targetVW = virtualWeekOf(calWeekStart)
|
||||||
let scrollTop = (targetVW - props.minVirtualWeek) * props.rowHeight + frac * props.rowHeight
|
let scrollTop = (targetVW - props.minVirtualWeek) * props.rowHeight + frac * props.rowHeight
|
||||||
if (Math.abs(scrollTop - props.scrollTop) < 0.5) scrollTop += 0.25 * props.rowHeight
|
if (Math.abs(scrollTop - props.scrollTop) < 0.5) scrollTop += 0.25 * props.rowHeight
|
||||||
emit('year-change', { year: y, scrollTop })
|
emit('year-change', { year: y, scrollTop })
|
||||||
|
@ -130,7 +130,10 @@ function getFirstDayForVirtualWeek(virtualWeek) {
|
|||||||
|
|
||||||
function createWeek(virtualWeek) {
|
function createWeek(virtualWeek) {
|
||||||
const firstDay = getFirstDayForVirtualWeek(virtualWeek)
|
const firstDay = getFirstDayForVirtualWeek(virtualWeek)
|
||||||
const weekNumber = isoWeekInfo(firstDay).week
|
// ISO week number should be based on the Thursday of this week (anchor) to avoid off-by-one when week starts Sunday or other days
|
||||||
|
const isoAnchor = new Date(firstDay)
|
||||||
|
isoAnchor.setDate(isoAnchor.getDate() + ((4 - isoAnchor.getDay() + 7) % 7))
|
||||||
|
const weekNumber = isoWeekInfo(isoAnchor).week
|
||||||
const days = []
|
const days = []
|
||||||
const cur = new Date(firstDay)
|
const cur = new Date(firstDay)
|
||||||
let hasFirst = false
|
let hasFirst = false
|
||||||
|
@ -246,16 +246,17 @@ function onWheel(e) {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0 0.4rem;
|
padding: 0 0.4rem;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
border: 1px solid var(--input-border, var(--muted));
|
border: 1px solid transparent;
|
||||||
background: var(--panel-alt);
|
background: transparent;
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
min-height: 1.8rem;
|
min-height: 1.8rem;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
touch-action: none; /* allow custom drag without scrolling */
|
touch-action: none;
|
||||||
}
|
}
|
||||||
.mini-stepper.drag-mode:focus-visible {
|
.mini-stepper.drag-mode:focus-visible {
|
||||||
outline: 2px solid var(--input-focus, #2563eb);
|
border-color: var(--input-border, var(--muted));
|
||||||
outline-offset: 2px;
|
box-shadow: 0 0 0 2px var(--input-focus, #2563eb);
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
.mini-stepper.drag-mode .value {
|
.mini-stepper.drag-mode .value {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user