fix(calendar): remove overlay swap and keep in-place zoom flow
This commit is contained in:
@@ -2309,7 +2309,6 @@ onBeforeUnmount(() => {
|
|||||||
clearInterval(lifecycleClock);
|
clearInterval(lifecycleClock);
|
||||||
lifecycleClock = null;
|
lifecycleClock = null;
|
||||||
}
|
}
|
||||||
clearCalendarZoomOverlay();
|
|
||||||
clearCalendarZoomPrime();
|
clearCalendarZoomPrime();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2319,7 +2318,7 @@ const selectedDateKey = ref(dayKey(new Date()));
|
|||||||
|
|
||||||
const sortedEvents = computed(() => [...calendarEvents.value].sort((a, b) => a.start.localeCompare(b.start)));
|
const sortedEvents = computed(() => [...calendarEvents.value].sort((a, b) => a.start.localeCompare(b.start)));
|
||||||
const focusedCalendarEvent = computed(() => {
|
const focusedCalendarEvent = computed(() => {
|
||||||
const id = focusedCalendarEventId.value.trim();
|
const id = (focusedCalendarEventId.value ?? "").trim();
|
||||||
if (!id) return null;
|
if (!id) return null;
|
||||||
return sortedEvents.value.find((event) => event.id === id) ?? null;
|
return sortedEvents.value.find((event) => event.id === id) ?? null;
|
||||||
});
|
});
|
||||||
@@ -2356,15 +2355,10 @@ const calendarViewOptions: { value: CalendarView; label: string }[] = [
|
|||||||
|
|
||||||
type CalendarHierarchyView = "year" | "month" | "week" | "day";
|
type CalendarHierarchyView = "year" | "month" | "week" | "day";
|
||||||
type CalendarRect = { left: number; top: number; width: number; height: number };
|
type CalendarRect = { left: number; top: number; width: number; height: number };
|
||||||
type CalendarZoomGhost = {
|
|
||||||
title: string;
|
|
||||||
subtitle?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const calendarContentWrapRef = ref<HTMLElement | null>(null);
|
const calendarContentWrapRef = ref<HTMLElement | null>(null);
|
||||||
const calendarContentScrollRef = ref<HTMLElement | null>(null);
|
const calendarContentScrollRef = ref<HTMLElement | null>(null);
|
||||||
const calendarSceneRef = ref<HTMLElement | null>(null);
|
const calendarSceneRef = ref<HTMLElement | null>(null);
|
||||||
const calendarZoomOverlayRef = ref<HTMLElement | null>(null);
|
|
||||||
const calendarHoveredMonthIndex = ref<number | null>(null);
|
const calendarHoveredMonthIndex = ref<number | null>(null);
|
||||||
const calendarHoveredWeekStartKey = ref("");
|
const calendarHoveredWeekStartKey = ref("");
|
||||||
const calendarHoveredDayKey = ref("");
|
const calendarHoveredDayKey = ref("");
|
||||||
@@ -2381,10 +2375,6 @@ function setCalendarSceneRef(element: HTMLElement | null) {
|
|||||||
calendarSceneRef.value = element;
|
calendarSceneRef.value = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCalendarZoomOverlayRef(element: HTMLElement | null) {
|
|
||||||
calendarZoomOverlayRef.value = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCalendarHoveredMonthIndex(value: number | null) {
|
function setCalendarHoveredMonthIndex(value: number | null) {
|
||||||
calendarHoveredMonthIndex.value = value;
|
calendarHoveredMonthIndex.value = value;
|
||||||
}
|
}
|
||||||
@@ -2403,14 +2393,6 @@ function onCalendarSceneMouseLeave() {
|
|||||||
calendarHoveredDayKey.value = "";
|
calendarHoveredDayKey.value = "";
|
||||||
clearCalendarZoomPrime();
|
clearCalendarZoomPrime();
|
||||||
}
|
}
|
||||||
const calendarZoomOverlay = ref<{ active: boolean } & CalendarRect>({
|
|
||||||
active: false,
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
});
|
|
||||||
const calendarZoomGhost = ref<CalendarZoomGhost | null>(null);
|
|
||||||
const calendarZoomBusy = ref(false);
|
const calendarZoomBusy = ref(false);
|
||||||
const calendarCameraState = ref({
|
const calendarCameraState = ref({
|
||||||
active: false,
|
active: false,
|
||||||
@@ -2426,8 +2408,6 @@ let calendarWheelLockUntil = 0;
|
|||||||
let calendarZoomPrimeTimer: ReturnType<typeof setTimeout> | null = null;
|
let calendarZoomPrimeTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
let calendarZoomPrimeLastAt = 0;
|
let calendarZoomPrimeLastAt = 0;
|
||||||
const CALENDAR_ZOOM_DURATION_MS = 2400;
|
const CALENDAR_ZOOM_DURATION_MS = 2400;
|
||||||
const CALENDAR_ZOOM_FOCUS_MS = 1400;
|
|
||||||
const CALENDAR_ZOOM_REVEAL_MS = Math.max(500, CALENDAR_ZOOM_DURATION_MS - CALENDAR_ZOOM_FOCUS_MS);
|
|
||||||
const CALENDAR_ZOOM_PRIME_STEPS = 2;
|
const CALENDAR_ZOOM_PRIME_STEPS = 2;
|
||||||
const CALENDAR_ZOOM_PRIME_MAX_SCALE = 1.05;
|
const CALENDAR_ZOOM_PRIME_MAX_SCALE = 1.05;
|
||||||
const CALENDAR_ZOOM_PRIME_RESET_MS = 900;
|
const CALENDAR_ZOOM_PRIME_RESET_MS = 900;
|
||||||
@@ -2437,12 +2417,6 @@ const normalizedCalendarView = computed<CalendarHierarchyView>(() =>
|
|||||||
calendarView.value === "agenda" ? "month" : calendarView.value,
|
calendarView.value === "agenda" ? "month" : calendarView.value,
|
||||||
);
|
);
|
||||||
const calendarZoomLevelIndex = computed(() => Math.max(0, calendarZoomOrder.indexOf(normalizedCalendarView.value)));
|
const calendarZoomLevelIndex = computed(() => Math.max(0, calendarZoomOrder.indexOf(normalizedCalendarView.value)));
|
||||||
const calendarZoomOverlayStyle = computed(() => ({
|
|
||||||
left: `${calendarZoomOverlay.value.left}px`,
|
|
||||||
top: `${calendarZoomOverlay.value.top}px`,
|
|
||||||
width: `${calendarZoomOverlay.value.width}px`,
|
|
||||||
height: `${calendarZoomOverlay.value.height}px`,
|
|
||||||
}));
|
|
||||||
const calendarSceneTransformStyle = computed(() => {
|
const calendarSceneTransformStyle = computed(() => {
|
||||||
if (!calendarCameraState.value.active) return undefined;
|
if (!calendarCameraState.value.active) return undefined;
|
||||||
return {
|
return {
|
||||||
@@ -2456,14 +2430,6 @@ const calendarSceneTransformStyle = computed(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function clearCalendarZoomOverlay() {
|
|
||||||
calendarZoomOverlay.value = {
|
|
||||||
...calendarZoomOverlay.value,
|
|
||||||
active: false,
|
|
||||||
};
|
|
||||||
calendarZoomGhost.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearCalendarZoomPrime() {
|
function clearCalendarZoomPrime() {
|
||||||
if (calendarZoomPrimeTimer) {
|
if (calendarZoomPrimeTimer) {
|
||||||
clearTimeout(calendarZoomPrimeTimer);
|
clearTimeout(calendarZoomPrimeTimer);
|
||||||
@@ -2592,92 +2558,6 @@ function weekRowStartForDate(key: string) {
|
|||||||
return dayKey(date);
|
return dayKey(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
function zoomGhostForMonth(monthIndex: number): CalendarZoomGhost {
|
|
||||||
const item = yearMonths.value.find((entry) => entry.monthIndex === monthIndex);
|
|
||||||
if (!item) {
|
|
||||||
return {
|
|
||||||
title: new Intl.DateTimeFormat("en-US", { month: "long" }).format(new Date(calendarCursor.value.getFullYear(), monthIndex, 1)),
|
|
||||||
subtitle: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
title: item.label,
|
|
||||||
subtitle: `${item.count} events`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function zoomGhostForWeek(startKey: string): CalendarZoomGhost {
|
|
||||||
const start = new Date(`${startKey}T00:00:00`);
|
|
||||||
const end = new Date(start);
|
|
||||||
end.setDate(start.getDate() + 6);
|
|
||||||
const row = monthRows.value.find((item) => item.startKey === startKey);
|
|
||||||
const count = row ? row.cells.reduce((sum, cell) => sum + cell.events.length, 0) : 0;
|
|
||||||
return {
|
|
||||||
title: `${formatDay(`${dayKey(start)}T00:00:00`)} - ${formatDay(`${dayKey(end)}T00:00:00`)}`,
|
|
||||||
subtitle: `${count} events`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function zoomGhostForDay(dayKeyValue: string): CalendarZoomGhost {
|
|
||||||
const day = weekDays.value.find((entry) => entry.key === dayKeyValue);
|
|
||||||
if (!day) {
|
|
||||||
return {
|
|
||||||
title: formatDay(`${dayKeyValue}T00:00:00`),
|
|
||||||
subtitle: `${getEventsByDate(dayKeyValue).length} events`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
title: `${day.label} ${day.day}`,
|
|
||||||
subtitle: `${day.events.length} events`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function zoomGhostForCurrentView(): CalendarZoomGhost {
|
|
||||||
if (calendarView.value === "day") {
|
|
||||||
return {
|
|
||||||
title: formatDay(`${selectedDateKey.value}T00:00:00`),
|
|
||||||
subtitle: `${selectedDayEvents.value.length} events`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (calendarView.value === "week") {
|
|
||||||
return {
|
|
||||||
title: calendarPeriodLabel.value,
|
|
||||||
subtitle: `${weekDays.value.reduce((sum, day) => sum + day.events.length, 0)} events`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (calendarView.value === "month" || calendarView.value === "agenda") {
|
|
||||||
return {
|
|
||||||
title: monthLabel.value,
|
|
||||||
subtitle: `${monthCells.value.reduce((sum, cell) => sum + cell.events.length, 0)} events`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
title: String(calendarCursor.value.getFullYear()),
|
|
||||||
subtitle: `${sortedEvents.value.filter((event) => new Date(event.start).getFullYear() === calendarCursor.value.getFullYear()).length} events`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function primeCalendarRect(rect: CalendarRect) {
|
|
||||||
clearCalendarZoomOverlay();
|
|
||||||
calendarZoomOverlay.value = {
|
|
||||||
active: true,
|
|
||||||
left: rect.left,
|
|
||||||
top: rect.top,
|
|
||||||
width: rect.width,
|
|
||||||
height: rect.height,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function morphCalendarRect(toRect: CalendarRect) {
|
|
||||||
calendarZoomOverlay.value = {
|
|
||||||
active: true,
|
|
||||||
left: toRect.left,
|
|
||||||
top: toRect.top,
|
|
||||||
width: toRect.width,
|
|
||||||
height: toRect.height,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function nextAnimationFrame() {
|
function nextAnimationFrame() {
|
||||||
return new Promise<void>((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
requestAnimationFrame(() => resolve());
|
requestAnimationFrame(() => resolve());
|
||||||
@@ -2745,50 +2625,13 @@ async function resetCalendarCamera() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function flushCalendarZoomStartFrame() {
|
|
||||||
await nextTick();
|
|
||||||
await nextAnimationFrame();
|
|
||||||
calendarZoomOverlayRef.value?.getBoundingClientRect();
|
|
||||||
await nextAnimationFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
function waitCalendarZoomTransition() {
|
|
||||||
const overlay = calendarZoomOverlayRef.value;
|
|
||||||
if (!overlay) {
|
|
||||||
return new Promise<void>((resolve) => {
|
|
||||||
setTimeout(() => resolve(), CALENDAR_ZOOM_DURATION_MS);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return new Promise<void>((resolve) => {
|
|
||||||
let settled = false;
|
|
||||||
const finish = () => {
|
|
||||||
if (settled) return;
|
|
||||||
settled = true;
|
|
||||||
overlay.removeEventListener("transitionend", onTransitionEnd);
|
|
||||||
clearTimeout(fallbackTimer);
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
const onTransitionEnd = (event: TransitionEvent) => {
|
|
||||||
if (event.target !== overlay) return;
|
|
||||||
if (!["left", "top", "width", "height"].includes(event.propertyName)) return;
|
|
||||||
finish();
|
|
||||||
};
|
|
||||||
const fallbackTimer = setTimeout(() => finish(), CALENDAR_ZOOM_DURATION_MS + 140);
|
|
||||||
overlay.addEventListener("transitionend", onTransitionEnd);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function animateCalendarZoomIn(
|
async function animateCalendarZoomIn(
|
||||||
sourceElement: HTMLElement | null,
|
sourceElement: HTMLElement | null,
|
||||||
ghost: CalendarZoomGhost,
|
|
||||||
apply: () => void,
|
apply: () => void,
|
||||||
resolveRevealTarget?: () => HTMLElement | null,
|
|
||||||
) {
|
) {
|
||||||
clearCalendarZoomPrime();
|
clearCalendarZoomPrime();
|
||||||
calendarZoomBusy.value = true;
|
calendarZoomBusy.value = true;
|
||||||
clearCalendarZoomOverlay();
|
|
||||||
try {
|
try {
|
||||||
calendarZoomGhost.value = ghost;
|
|
||||||
const fromRect = getElementRectInScene(sourceElement) ?? fallbackZoomOriginRectInScene();
|
const fromRect = getElementRectInScene(sourceElement) ?? fallbackZoomOriginRectInScene();
|
||||||
const cameraTarget = fromRect ? cameraTransformForRect(fromRect) : null;
|
const cameraTarget = fromRect ? cameraTransformForRect(fromRect) : null;
|
||||||
if (!cameraTarget) {
|
if (!cameraTarget) {
|
||||||
@@ -2810,36 +2653,21 @@ async function animateCalendarZoomIn(
|
|||||||
left: cameraTarget.left,
|
left: cameraTarget.left,
|
||||||
top: cameraTarget.top,
|
top: cameraTarget.top,
|
||||||
scale: cameraTarget.scale,
|
scale: cameraTarget.scale,
|
||||||
durationMs: CALENDAR_ZOOM_FOCUS_MS,
|
durationMs: CALENDAR_ZOOM_DURATION_MS,
|
||||||
};
|
};
|
||||||
await waitCalendarCameraTransition();
|
await waitCalendarCameraTransition();
|
||||||
apply();
|
apply();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
const revealTargetElement = resolveRevealTarget ? resolveRevealTarget() : sourceElement;
|
|
||||||
const revealTargetRect = getElementRectInScene(revealTargetElement) ?? fallbackZoomOriginRectInScene();
|
|
||||||
const revealTarget = revealTargetRect ? cameraTransformForRect(revealTargetRect) : null;
|
|
||||||
if (revealTarget) {
|
|
||||||
calendarCameraState.value = {
|
|
||||||
active: true,
|
|
||||||
left: revealTarget.left,
|
|
||||||
top: revealTarget.top,
|
|
||||||
scale: revealTarget.scale,
|
|
||||||
durationMs: 0,
|
|
||||||
};
|
|
||||||
await nextTick();
|
|
||||||
await nextAnimationFrame();
|
|
||||||
}
|
|
||||||
calendarCameraState.value = {
|
calendarCameraState.value = {
|
||||||
active: true,
|
active: true,
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
durationMs: CALENDAR_ZOOM_REVEAL_MS,
|
durationMs: 0,
|
||||||
};
|
};
|
||||||
await waitCalendarCameraTransition();
|
await nextAnimationFrame();
|
||||||
} finally {
|
} finally {
|
||||||
await resetCalendarCamera();
|
await resetCalendarCamera();
|
||||||
calendarZoomGhost.value = null;
|
|
||||||
calendarZoomBusy.value = false;
|
calendarZoomBusy.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2847,9 +2675,7 @@ async function animateCalendarZoomIn(
|
|||||||
async function animateCalendarZoomOut(apply: () => void, resolveTarget: () => HTMLElement | null) {
|
async function animateCalendarZoomOut(apply: () => void, resolveTarget: () => HTMLElement | null) {
|
||||||
clearCalendarZoomPrime();
|
clearCalendarZoomPrime();
|
||||||
calendarZoomBusy.value = true;
|
calendarZoomBusy.value = true;
|
||||||
clearCalendarZoomOverlay();
|
|
||||||
try {
|
try {
|
||||||
calendarZoomGhost.value = zoomGhostForCurrentView();
|
|
||||||
apply();
|
apply();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
const targetRect = getElementRectInScene(resolveTarget()) ?? fallbackZoomOriginRectInScene();
|
const targetRect = getElementRectInScene(resolveTarget()) ?? fallbackZoomOriginRectInScene();
|
||||||
@@ -2875,7 +2701,6 @@ async function animateCalendarZoomOut(apply: () => void, resolveTarget: () => HT
|
|||||||
await waitCalendarCameraTransition();
|
await waitCalendarCameraTransition();
|
||||||
} finally {
|
} finally {
|
||||||
await resetCalendarCamera();
|
await resetCalendarCamera();
|
||||||
calendarZoomGhost.value = null;
|
|
||||||
calendarZoomBusy.value = false;
|
calendarZoomBusy.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2923,9 +2748,9 @@ async function zoomInCalendar(event?: Event) {
|
|||||||
queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`) ??
|
queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`) ??
|
||||||
queryCalendarElement("[data-calendar-month-index]");
|
queryCalendarElement("[data-calendar-month-index]");
|
||||||
if (maybePrimeWheelZoom(wheelEvent, calendarPrimeMonthToken(monthIndex))) return;
|
if (maybePrimeWheelZoom(wheelEvent, calendarPrimeMonthToken(monthIndex))) return;
|
||||||
await animateCalendarZoomIn(sourceElement, zoomGhostForMonth(monthIndex), () => {
|
await animateCalendarZoomIn(sourceElement, () => {
|
||||||
openYearMonth(monthIndex);
|
openYearMonth(monthIndex);
|
||||||
}, () => queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`));
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2938,16 +2763,9 @@ async function zoomInCalendar(event?: Event) {
|
|||||||
queryCalendarElement("[data-calendar-week-start-key]") ??
|
queryCalendarElement("[data-calendar-week-start-key]") ??
|
||||||
queryCalendarElement("[data-calendar-day-key]");
|
queryCalendarElement("[data-calendar-day-key]");
|
||||||
if (maybePrimeWheelZoom(wheelEvent, calendarPrimeWeekToken(rowStartKey))) return;
|
if (maybePrimeWheelZoom(wheelEvent, calendarPrimeWeekToken(rowStartKey))) return;
|
||||||
await animateCalendarZoomIn(
|
await animateCalendarZoomIn(sourceElement, () => {
|
||||||
sourceElement,
|
|
||||||
zoomGhostForWeek(rowStartKey),
|
|
||||||
() => {
|
|
||||||
openWeekView(anchorDayKey);
|
openWeekView(anchorDayKey);
|
||||||
},
|
});
|
||||||
() =>
|
|
||||||
queryCalendarElement(`[data-calendar-week-start-key="${rowStartKey}"]`) ??
|
|
||||||
queryCalendarElement(`[data-calendar-day-key="${anchorDayKey}"]`),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2955,21 +2773,16 @@ async function zoomInCalendar(event?: Event) {
|
|||||||
const dayAnchor = resolveDayAnchor(wheelEvent);
|
const dayAnchor = resolveDayAnchor(wheelEvent);
|
||||||
const sourceElement = queryCalendarElement(`[data-calendar-day-key="${dayAnchor}"]`) ?? queryCalendarElement("[data-calendar-day-key]");
|
const sourceElement = queryCalendarElement(`[data-calendar-day-key="${dayAnchor}"]`) ?? queryCalendarElement("[data-calendar-day-key]");
|
||||||
if (maybePrimeWheelZoom(wheelEvent, calendarPrimeDayToken(dayAnchor))) return;
|
if (maybePrimeWheelZoom(wheelEvent, calendarPrimeDayToken(dayAnchor))) return;
|
||||||
await animateCalendarZoomIn(sourceElement, zoomGhostForDay(dayAnchor), () => {
|
await animateCalendarZoomIn(sourceElement, () => {
|
||||||
openDayView(dayAnchor);
|
openDayView(dayAnchor);
|
||||||
}, () => queryCalendarElement(`[data-calendar-day-key="${dayAnchor}"]`));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function zoomToMonth(monthIndex: number) {
|
async function zoomToMonth(monthIndex: number) {
|
||||||
await animateCalendarZoomIn(
|
await animateCalendarZoomIn(queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`), () => {
|
||||||
queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`),
|
|
||||||
zoomGhostForMonth(monthIndex),
|
|
||||||
() => {
|
|
||||||
openYearMonth(monthIndex);
|
openYearMonth(monthIndex);
|
||||||
},
|
});
|
||||||
() => queryCalendarElement(`[data-calendar-month-index="${monthIndex}"]`),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function zoomOutCalendar() {
|
async function zoomOutCalendar() {
|
||||||
@@ -4950,10 +4763,6 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
|||||||
:week-days="weekDays"
|
:week-days="weekDays"
|
||||||
:calendar-prime-day-token="calendarPrimeDayToken"
|
:calendar-prime-day-token="calendarPrimeDayToken"
|
||||||
:selected-day-events="selectedDayEvents"
|
:selected-day-events="selectedDayEvents"
|
||||||
:calendar-zoom-overlay="calendarZoomOverlay"
|
|
||||||
:set-calendar-zoom-overlay-ref="setCalendarZoomOverlayRef"
|
|
||||||
:calendar-zoom-overlay-style="calendarZoomOverlayStyle"
|
|
||||||
:calendar-zoom-ghost="calendarZoomGhost"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -79,10 +79,6 @@ defineProps<{
|
|||||||
weekDays: WeekDay[];
|
weekDays: WeekDay[];
|
||||||
calendarPrimeDayToken: (dayKey: string) => string;
|
calendarPrimeDayToken: (dayKey: string) => string;
|
||||||
selectedDayEvents: CalendarEvent[];
|
selectedDayEvents: CalendarEvent[];
|
||||||
calendarZoomOverlay: { active: boolean };
|
|
||||||
setCalendarZoomOverlayRef: (element: HTMLDivElement | null) => void;
|
|
||||||
calendarZoomOverlayStyle: Record<string, string>;
|
|
||||||
calendarZoomGhost: { title: string; subtitle?: string } | null;
|
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -199,10 +195,8 @@ defineProps<{
|
|||||||
{{ formatDay(item.first.start) }} · {{ item.first.title }}
|
{{ formatDay(item.first.start) }} · {{ item.first.title }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div
|
<div v-if="item.monthIndex === calendarCursorMonth" class="mt-3">
|
||||||
v-if="item.monthIndex === calendarCursorMonth && (calendarView === 'month' || calendarView === 'agenda')"
|
<div v-show="calendarView === 'month' || calendarView === 'agenda'" class="space-y-1">
|
||||||
class="mt-3 space-y-1"
|
|
||||||
>
|
|
||||||
<div class="grid grid-cols-7 gap-1 text-center text-xs font-semibold text-base-content/60">
|
<div class="grid grid-cols-7 gap-1 text-center text-xs font-semibold text-base-content/60">
|
||||||
<span>Sun</span>
|
<span>Sun</span>
|
||||||
<span>Mon</span>
|
<span>Mon</span>
|
||||||
@@ -256,10 +250,7 @@ defineProps<{
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-show="calendarView === 'week'" class="calendar-week-scroll overflow-x-auto pb-1">
|
||||||
v-else-if="item.monthIndex === calendarCursorMonth && calendarView === 'week'"
|
|
||||||
class="mt-3 calendar-week-scroll overflow-x-auto pb-1"
|
|
||||||
>
|
|
||||||
<div class="calendar-week-grid">
|
<div class="calendar-week-grid">
|
||||||
<article
|
<article
|
||||||
v-for="day in weekDays"
|
v-for="day in weekDays"
|
||||||
@@ -294,10 +285,7 @@ defineProps<{
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-show="calendarView === 'day'" class="space-y-2">
|
||||||
v-else-if="item.monthIndex === calendarCursorMonth && calendarView === 'day'"
|
|
||||||
class="mt-3 space-y-2"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
v-for="event in selectedDayEvents"
|
v-for="event in selectedDayEvents"
|
||||||
:key="event.id"
|
:key="event.id"
|
||||||
@@ -312,21 +300,11 @@ defineProps<{
|
|||||||
</button>
|
</button>
|
||||||
<p v-if="selectedDayEvents.length === 0" class="text-sm text-base-content/60">No events on this day.</p>
|
<p v-if="selectedDayEvents.length === 0" class="text-sm text-base-content/60">No events on this day.</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-if="calendarZoomOverlay.active"
|
|
||||||
:ref="setCalendarZoomOverlayRef"
|
|
||||||
class="calendar-zoom-overlay"
|
|
||||||
:style="calendarZoomOverlayStyle"
|
|
||||||
>
|
|
||||||
<div v-if="calendarZoomGhost" class="calendar-zoom-overlay-content">
|
|
||||||
<p class="calendar-zoom-overlay-title">{{ calendarZoomGhost.title }}</p>
|
|
||||||
<p v-if="calendarZoomGhost.subtitle" class="calendar-zoom-overlay-subtitle">{{ calendarZoomGhost.subtitle }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -492,44 +470,6 @@ defineProps<{
|
|||||||
background: color-mix(in oklab, var(--color-base-content) 85%, transparent);
|
background: color-mix(in oklab, var(--color-base-content) 85%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-zoom-overlay {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 18;
|
|
||||||
border: 1px solid color-mix(in oklab, var(--color-primary) 62%, transparent);
|
|
||||||
border-radius: 14px;
|
|
||||||
background:
|
|
||||||
radial-gradient(circle at 20% 20%, color-mix(in oklab, var(--color-primary) 30%, transparent), transparent 55%),
|
|
||||||
color-mix(in oklab, var(--color-base-100) 84%, transparent);
|
|
||||||
box-shadow:
|
|
||||||
0 0 0 1px color-mix(in oklab, var(--color-primary) 42%, transparent) inset,
|
|
||||||
0 18px 38px rgba(18, 30, 58, 0.28);
|
|
||||||
pointer-events: none;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calendar-zoom-overlay-content {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
gap: 4px;
|
|
||||||
padding: 14px;
|
|
||||||
color: color-mix(in oklab, var(--color-base-content) 90%, transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.calendar-zoom-overlay-title {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calendar-zoom-overlay-subtitle {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 11px;
|
|
||||||
opacity: 0.72;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 960px) {
|
@media (max-width: 960px) {
|
||||||
.calendar-content-wrap {
|
.calendar-content-wrap {
|
||||||
padding-left: 32px;
|
padding-left: 32px;
|
||||||
|
|||||||
Reference in New Issue
Block a user