Fix: clear mini-cal selection on Today click even when month unchanged

datesSet fires but currentDate stays the same value when already on
the current month, so the useEffect didn't re-run. Added navKey counter
that increments on every datesSet call — MiniCalendar watches it in a
separate useEffect to reliably clear selectedDate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-03-17 20:14:00 +08:00
parent bda02039a6
commit 68337b12a0
3 changed files with 17 additions and 6 deletions

View File

@ -54,6 +54,7 @@ export default function CalendarPage() {
const firstDayOfWeek = settings?.first_day_of_week ?? 0; const firstDayOfWeek = settings?.first_day_of_week ?? 0;
const { data: calendars = [], sharedData, allCalendarIds } = useCalendars({ pollingEnabled: true }); const { data: calendars = [], sharedData, allCalendarIds } = useCalendars({ pollingEnabled: true });
const [currentDate, setCurrentDate] = useState<string>(() => format(new Date(), 'yyyy-MM-dd')); const [currentDate, setCurrentDate] = useState<string>(() => format(new Date(), 'yyyy-MM-dd'));
const [navKey, setNavKey] = useState(0);
const [visibleSharedIds, setVisibleSharedIds] = useState<Set<number>>(new Set()); const [visibleSharedIds, setVisibleSharedIds] = useState<Set<number>>(new Set());
const calendarContainerRef = useRef<HTMLDivElement>(null); const calendarContainerRef = useRef<HTMLDivElement>(null);
@ -521,6 +522,8 @@ export default function CalendarPage() {
); );
// Track current date anchor for mini calendar sync // Track current date anchor for mini calendar sync
setCurrentDate(format(arg.view.currentStart, 'yyyy-MM-dd')); setCurrentDate(format(arg.view.currentStart, 'yyyy-MM-dd'));
// Increment nav key so mini calendar clears selection even when month doesn't change
setNavKey((k) => k + 1);
}; };
const navigatePrev = () => calendarRef.current?.getApi().prev(); const navigatePrev = () => calendarRef.current?.getApi().prev();
@ -599,7 +602,7 @@ export default function CalendarPage() {
return ( return (
<div className="flex h-full overflow-hidden animate-fade-in"> <div className="flex h-full overflow-hidden animate-fade-in">
<div className="hidden lg:flex lg:flex-row shrink-0"> <div className="hidden lg:flex lg:flex-row shrink-0">
<CalendarSidebar ref={sidebarRef} onUseTemplate={handleUseTemplate} onSharedVisibilityChange={setVisibleSharedIds} width={sidebarWidth} onDateClick={handleMiniCalClick} currentDate={currentDate} firstDayOfWeek={firstDayOfWeek} /> <CalendarSidebar ref={sidebarRef} onUseTemplate={handleUseTemplate} onSharedVisibilityChange={setVisibleSharedIds} width={sidebarWidth} onDateClick={handleMiniCalClick} currentDate={currentDate} firstDayOfWeek={firstDayOfWeek} navKey={navKey} />
<div <div
onMouseDown={handleSidebarMouseDown} onMouseDown={handleSidebarMouseDown}
className="w-1 shrink-0 cursor-col-resize hover:bg-accent/30 active:bg-accent/50 transition-colors duration-150" className="w-1 shrink-0 cursor-col-resize hover:bg-accent/30 active:bg-accent/50 transition-colors duration-150"
@ -610,7 +613,7 @@ export default function CalendarPage() {
<Sheet open={mobileSidebarOpen} onOpenChange={setMobileSidebarOpen}> <Sheet open={mobileSidebarOpen} onOpenChange={setMobileSidebarOpen}>
<SheetContent className="w-72 p-0"> <SheetContent className="w-72 p-0">
<SheetClose onClick={() => setMobileSidebarOpen(false)} /> <SheetClose onClick={() => setMobileSidebarOpen(false)} />
<CalendarSidebar onUseTemplate={(tmpl) => { setMobileSidebarOpen(false); handleUseTemplate(tmpl); }} onSharedVisibilityChange={setVisibleSharedIds} width={288} onDateClick={handleMiniCalClick} currentDate={currentDate} firstDayOfWeek={firstDayOfWeek} /> <CalendarSidebar onUseTemplate={(tmpl) => { setMobileSidebarOpen(false); handleUseTemplate(tmpl); }} onSharedVisibilityChange={setVisibleSharedIds} width={288} onDateClick={handleMiniCalClick} currentDate={currentDate} firstDayOfWeek={firstDayOfWeek} navKey={navKey} />
</SheetContent> </SheetContent>
</Sheet> </Sheet>
)} )}

View File

@ -19,9 +19,10 @@ interface CalendarSidebarProps {
onDateClick?: (dateStr: string) => void; onDateClick?: (dateStr: string) => void;
currentDate?: string; currentDate?: string;
firstDayOfWeek?: number; firstDayOfWeek?: number;
navKey?: number;
} }
const CalendarSidebar = forwardRef<HTMLDivElement, CalendarSidebarProps>(function CalendarSidebar({ onUseTemplate, onSharedVisibilityChange, width, onDateClick, currentDate, firstDayOfWeek }, ref) { const CalendarSidebar = forwardRef<HTMLDivElement, CalendarSidebarProps>(function CalendarSidebar({ onUseTemplate, onSharedVisibilityChange, width, onDateClick, currentDate, firstDayOfWeek, navKey }, ref) {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { data: calendars = [], sharedData: sharedCalendars = [] } = useCalendars(); const { data: calendars = [], sharedData: sharedCalendars = [] } = useCalendars();
const [showForm, setShowForm] = useState(false); const [showForm, setShowForm] = useState(false);
@ -108,6 +109,7 @@ const CalendarSidebar = forwardRef<HTMLDivElement, CalendarSidebarProps>(functio
onDateClick={onDateClick} onDateClick={onDateClick}
currentDate={currentDate} currentDate={currentDate}
firstDayOfWeek={firstDayOfWeek} firstDayOfWeek={firstDayOfWeek}
navKey={navKey}
/> />
</div> </div>
)} )}

View File

@ -11,6 +11,7 @@ interface MiniCalendarProps {
onDateClick: (dateStr: string) => void; onDateClick: (dateStr: string) => void;
currentDate?: string; currentDate?: string;
firstDayOfWeek?: number; firstDayOfWeek?: number;
navKey?: number;
} }
const DAY_LABELS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; const DAY_LABELS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
@ -31,6 +32,7 @@ const MiniCalendar = memo(function MiniCalendar({
onDateClick, onDateClick,
currentDate, currentDate,
firstDayOfWeek = 0, firstDayOfWeek = 0,
navKey,
}: MiniCalendarProps) { }: MiniCalendarProps) {
const REF_DATE = useMemo(() => new Date(), []); const REF_DATE = useMemo(() => new Date(), []);
@ -44,17 +46,21 @@ const MiniCalendar = memo(function MiniCalendar({
); );
const [selectedDate, setSelectedDate] = useState<string | null>(null); const [selectedDate, setSelectedDate] = useState<string | null>(null);
// Sync displayed month when main calendar navigates (toolbar prev/next/today) // Sync displayed month when main calendar navigates across months
// Also clear selected date — avoids stale highlight after "Today" click
useEffect(() => { useEffect(() => {
if (!currentDate) return; if (!currentDate) return;
const incoming = startOfMonth(parseDate(currentDate)); const incoming = startOfMonth(parseDate(currentDate));
setDisplayedMonth((prev) => setDisplayedMonth((prev) =>
prev.getTime() === incoming.getTime() ? prev : incoming prev.getTime() === incoming.getTime() ? prev : incoming
); );
setSelectedDate(null);
}, [currentDate, parseDate]); }, [currentDate, parseDate]);
// Clear selection on any toolbar navigation (today/prev/next)
// navKey increments on every datesSet, even when the month doesn't change
useEffect(() => {
setSelectedDate(null);
}, [navKey]);
const days = useMemo( const days = useMemo(
() => buildGrid(displayedMonth, firstDayOfWeek), () => buildGrid(displayedMonth, firstDayOfWeek),
[displayedMonth, firstDayOfWeek] [displayedMonth, firstDayOfWeek]