From ec94dd6e2a994aeab542cd8cfe60b5ab202c2888 Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev <572431+veikab@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:56:11 +0700 Subject: [PATCH] fix(calendar): zoom selected block first, then commit level --- .../components/workspace/CrmWorkspaceApp.vue | 106 +++++++++++++----- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/frontend/app/components/workspace/CrmWorkspaceApp.vue b/frontend/app/components/workspace/CrmWorkspaceApp.vue index cf4413e..279df36 100644 --- a/frontend/app/components/workspace/CrmWorkspaceApp.vue +++ b/frontend/app/components/workspace/CrmWorkspaceApp.vue @@ -2695,6 +2695,70 @@ async function animateCalendarFlipTransition( } } +async function animateCalendarZoomIntoSource( + sourceElement: HTMLElement | null, + apply: () => void, +) { + clearCalendarZoomPrime(); + calendarZoomBusy.value = true; + let restoreSiblings = () => {}; + let snapshot: { + transform: string; + transition: string; + transformOrigin: string; + willChange: string; + zIndex: string; + } | null = null; + try { + const viewportRect = calendarContentScrollRef.value?.getBoundingClientRect() ?? null; + const sourceRect = sourceElement?.getBoundingClientRect() ?? null; + if (!sourceElement || !isRenderableRect(viewportRect) || !isRenderableRect(sourceRect)) { + apply(); + return; + } + + restoreSiblings = fadeOutCalendarSiblings(sourceElement); + snapshot = { + transform: sourceElement.style.transform, + transition: sourceElement.style.transition, + transformOrigin: sourceElement.style.transformOrigin, + willChange: sourceElement.style.willChange, + zIndex: sourceElement.style.zIndex, + }; + + const dx = viewportRect.left - sourceRect.left; + const dy = viewportRect.top - sourceRect.top; + const sx = Math.max(0.01, viewportRect.width / sourceRect.width); + const sy = Math.max(0.01, viewportRect.height / sourceRect.height); + + sourceElement.style.transformOrigin = "top left"; + sourceElement.style.willChange = "transform"; + sourceElement.style.zIndex = "24"; + sourceElement.style.transition = "none"; + sourceElement.style.transform = "translate3d(0px, 0px, 0px) scale(1, 1)"; + sourceElement.getBoundingClientRect(); + await nextAnimationFrame(); + + sourceElement.style.transition = `transform ${CALENDAR_ZOOM_DURATION_MS}ms cubic-bezier(0.16, 0.86, 0.18, 1)`; + sourceElement.style.transform = `translate3d(${dx}px, ${dy}px, 0px) scale(${sx}, ${sy})`; + await waitForTransformTransition(sourceElement); + + apply(); + await nextTick(); + await nextAnimationFrame(); + } finally { + if (sourceElement && snapshot) { + sourceElement.style.transform = snapshot.transform; + sourceElement.style.transition = snapshot.transition; + sourceElement.style.transformOrigin = snapshot.transformOrigin; + sourceElement.style.willChange = snapshot.willChange; + sourceElement.style.zIndex = snapshot.zIndex; + } + restoreSiblings(); + calendarZoomBusy.value = false; + } +} + function resolveMonthAnchor(event?: WheelEvent) { const target = event?.target as HTMLElement | null; const monthAttr = target?.closest("[data-calendar-month-index]")?.dataset.calendarMonthIndex; @@ -2738,13 +2802,9 @@ async function zoomInCalendar(event?: Event) { queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`) ?? queryCalendarElement("[data-calendar-month-index]"); if (maybePrimeWheelZoom(wheelEvent, calendarPrimeMonthToken(monthIndex))) return; - await animateCalendarFlipTransition( - sourceElement, - () => { - openYearMonth(monthIndex); - }, - () => queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`), - ); + await animateCalendarZoomIntoSource(sourceElement, () => { + openYearMonth(monthIndex); + }); return; } @@ -2757,14 +2817,9 @@ async function zoomInCalendar(event?: Event) { queryCalendarElement("[data-calendar-week-start-key]") ?? queryCalendarElement("[data-calendar-day-key]"); if (maybePrimeWheelZoom(wheelEvent, calendarPrimeWeekToken(rowStartKey))) return; - const monthIndex = new Date(`${anchorDayKey}T00:00:00`).getMonth(); - await animateCalendarFlipTransition( - sourceElement, - () => { - openWeekView(anchorDayKey); - }, - () => queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`), - ); + await animateCalendarZoomIntoSource(sourceElement, () => { + openWeekView(anchorDayKey); + }); return; } @@ -2772,25 +2827,16 @@ async function zoomInCalendar(event?: Event) { const dayAnchor = resolveDayAnchor(wheelEvent); const sourceElement = queryCalendarElement(`[data-calendar-day-key="${dayAnchor}"]`) ?? queryCalendarElement("[data-calendar-day-key]"); if (maybePrimeWheelZoom(wheelEvent, calendarPrimeDayToken(dayAnchor))) return; - const monthIndex = new Date(`${dayAnchor}T00:00:00`).getMonth(); - await animateCalendarFlipTransition( - sourceElement, - () => { - openDayView(dayAnchor); - }, - () => queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`), - ); + await animateCalendarZoomIntoSource(sourceElement, () => { + openDayView(dayAnchor); + }); } } async function zoomToMonth(monthIndex: number) { - await animateCalendarFlipTransition( - queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`), - () => { - openYearMonth(monthIndex); - }, - () => queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`), - ); + await animateCalendarZoomIntoSource(queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`), () => { + openYearMonth(monthIndex); + }); } async function zoomOutCalendar() {