Improved year scroller, removed Numeric outline for simpler forms.
This commit is contained in:
		| @@ -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 | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
|     tabindex="0" |     tabindex="0" | ||||||
|     @pointerdown="onPointerDown" |     @pointerdown="onPointerDown" | ||||||
|     @keydown="onKey" |     @keydown="onKey" | ||||||
|   @wheel.prevent="onWheel" |     @wheel.prevent="onWheel" | ||||||
|   > |   > | ||||||
|     <span class="value" :title="String(current)">{{ display }}</span> |     <span class="value" :title="String(current)">{{ display }}</span> | ||||||
|   </div> |   </div> | ||||||
| @@ -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; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Leo Vasanko
					Leo Vasanko