diff --git a/src/components/CalendarDay.vue b/src/components/CalendarDay.vue index 4974cab..7032d6a 100644 --- a/src/components/CalendarDay.vue +++ b/src/components/CalendarDay.vue @@ -17,16 +17,7 @@ const formattedDate = computed(() => {
{{ formattedDate }} @@ -43,10 +34,8 @@ const formattedDate = computed(() => { position: relative; user-select: none; display: grid; - /* Updated grid for centered day number */ grid-template-columns: 1fr; grid-template-rows: 1fr auto; - /* Named grid areas */ grid-template-areas: 'day-number' 'holiday-info'; @@ -89,6 +78,26 @@ const formattedDate = computed(() => { z-index: 15; pointer-events: none; } + +/* Search highlight animation */ +.cell.search-highlight-flash::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: calc(100% + .2rem); + height: calc(100% + .2rem); + border-radius: 1rem; + background: transparent; + border: 0.3em solid var(--strong); + z-index: 16; + pointer-events: none; +} + +.cell.search-highlight-flash::after { animation: search-highlight-flash 1.5s ease-out forwards; } + +@keyframes search-highlight-flash {0%{opacity:0;transform:translate(-50%,-50%) scale(.8);border-width:.1em}15%{opacity:1;transform:translate(-50%,-50%) scale(1.05);border-width:.4em}30%{opacity:1;transform:translate(-50%,-50%) scale(1);border-width:.3em}100%{opacity:0;transform:translate(-50%,-50%) scale(1);border-width:.3em}} .cell.selected h1.day-number { opacity: 0.3; filter: brightness(1.2); diff --git a/src/components/CalendarView.vue b/src/components/CalendarView.vue index 9c1a168..bd88005 100644 --- a/src/components/CalendarView.vue +++ b/src/components/CalendarView.vue @@ -197,11 +197,24 @@ function measureFromProbe() { const { getWeekIndex, getFirstDayForVirtualWeek, - goToToday, handleHeaderYearChange, scrollToWeekCentered, } = vwm +function showDay(input) { + const dateStr = input instanceof Date ? toLocalString(input, DEFAULT_TZ) : String(input) + const weekIndex = getWeekIndex(fromLocalString(dateStr, DEFAULT_TZ)) + scrollToWeekCentered(weekIndex, 'nav', true) + const diff = Math.abs(weekIndex - centerVisibleWeek.value) + const delay = Math.min(800, diff * 40) + setTimeout(() => { + const el = document.querySelector(`[data-date="${dateStr}"]`) + if (!el) return + el.classList.add('search-highlight-flash') + setTimeout(() => el.classList.remove('search-highlight-flash'), 1500) + }, delay) +} + // Reference date for search: center of the current viewport (virtual week at vertical midpoint) const centerVisibleWeek = computed(() => { const midRow = (scrollTop.value + viewportHeight.value / 2) / rowHeight.value @@ -457,26 +470,15 @@ const handleEventClick = (payload) => { openEditEventDialog(payload) } -function scrollToEventStart(startDate, smooth = true) { - try { - const dateObj = fromLocalString(startDate, DEFAULT_TZ) - const weekIndex = getWeekIndex(dateObj) - scrollToWeekCentered(weekIndex, 'search-jump', smooth) - } catch { - /* noop */ +function handleHeaderSearchPreview(r) { if (r) showDay(r.startDate) } +function handleHeaderSearchActivate(r) { + if (!r) return + showDay(r.startDate) + if (!r._goto && !r._holiday) { + const ev = calendarStore.getEventById(r.id) + if (ev) openEditEventDialog({ id: ev.id, event: ev }) } } -function handleHeaderSearchPreview(result) { - if (!result) return - scrollToEventStart(result.startDate, true) -} -function handleHeaderSearchActivate(result) { - if (!result) return - scrollToEventStart(result.startDate, true) - // Open edit dialog for the event - const ev = calendarStore.getEventById(result.id) - if (ev) openEditEventDialog({ id: ev.id, event: ev }) -} // Heuristic: rotate month label (180deg) only for predominantly Latin text. // We explicitly avoid locale detection; rely solely on characters present. @@ -543,7 +545,7 @@ window.addEventListener('resize', () => {