This commit is contained in:
Yuhan Zhang 2026-02-09 10:33:41 +09:00 committed by GitHub
commit 50df9411b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 49 additions and 10 deletions

View File

@ -1,3 +1,4 @@
import dayjs from "dayjs";
import { memo, useMemo } from "react";
import { useInstance } from "@/contexts/InstanceContext";
import { cn } from "@/lib/utils";
@ -35,11 +36,15 @@ const WeekdayHeader = memo(({ weekDays, size }: WeekdayHeaderProps) => (
WeekdayHeader.displayName = "WeekdayHeader";
export const MonthCalendar = memo((props: MonthCalendarProps) => {
const { month, data, maxCount, size = "default", onClick, className, disableTooltips = false } = props;
const { month, selectedDate, data, maxCount, size = "default", onClick, className, disableTooltips = false } = props;
const t = useTranslate();
const { generalSetting } = useInstance();
const today = useTodayDate();
const weekDays = useWeekdayLabels();
const selectedDateFormatted = useMemo(() => {
if (!selectedDate) return null;
return dayjs(selectedDate).format("YYYY-MM-DD");
}, [selectedDate]);
const { weeks, weekDays: rotatedWeekDays } = useCalendarMatrix({
month,
@ -47,7 +52,7 @@ export const MonthCalendar = memo((props: MonthCalendarProps) => {
weekDays,
weekStartDayOffset: generalSetting.weekStartDayOffset,
today,
selectedDate: "",
selectedDate: selectedDateFormatted,
});
const flatDays = useMemo(() => weeks.flatMap((week) => week.days), [weeks]);

View File

@ -73,12 +73,21 @@ interface MonthCardProps {
data: Record<string, number>;
maxCount: number;
onDateClick: (date: string) => void;
selectedDate: string | null;
}
const MonthCard = memo(({ month, data, maxCount, onDateClick }: MonthCardProps) => (
const MonthCard = memo(({ month, data, maxCount, onDateClick, selectedDate }: MonthCardProps) => (
<article className="flex flex-col gap-2 rounded-xl border border-border/20 bg-muted/5 p-3 transition-colors hover:bg-muted/10">
<header className="text-[10px] font-medium text-muted-foreground/80 uppercase tracking-widest">{getMonthLabel(month)}</header>
<MonthCalendar month={month} data={data} maxCount={maxCount} size="small" onClick={onDateClick} disableTooltips />
<MonthCalendar
month={month}
data={data}
maxCount={maxCount}
size="small"
onClick={onDateClick}
selectedDate={selectedDate}
disableTooltips
/>
</article>
));
MonthCard.displayName = "MonthCard";
@ -106,7 +115,7 @@ export const YearCalendar = memo(({ selectedYear, data, onYearChange, onDateClic
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 animate-fade-in">
{months.map((month) => (
<MonthCard key={month} month={month} data={yearData} maxCount={yearMaxCount} onDateClick={onDateClick} />
<MonthCard key={month} month={month} data={yearData} maxCount={yearMaxCount} onDateClick={onDateClick} selectedDate={null} />
))}
</div>
</section>

View File

@ -22,6 +22,7 @@ export interface CalendarMatrixResult {
export interface MonthCalendarProps {
month: string;
selectedDate: string | null;
data: Record<string, number>;
maxCount: number;
size?: CalendarSize;

View File

@ -9,7 +9,7 @@ export interface UseCalendarMatrixParams {
weekDays: string[];
weekStartDayOffset: number;
today: string;
selectedDate: string;
selectedDate: string | null;
}
const createCalendarDayCell = (
@ -17,7 +17,7 @@ const createCalendarDayCell = (
monthKey: string,
data: Record<string, number>,
today: string,
selectedDate: string,
selectedDate: string | null,
): CalendarDayCell => {
const isoDate = current.format("YYYY-MM-DD");
const isCurrentMonth = current.format("YYYY-MM") === monthKey;

View File

@ -1,7 +1,7 @@
import dayjs from "dayjs";
import { useMemo, useState } from "react";
import { MonthCalendar } from "@/components/ActivityCalendar";
import { useDateFilterNavigation } from "@/hooks";
import { type MemoFilter, useMemoFilterContext } from "@/contexts/MemoFilterContext";
import type { StatisticsData } from "@/types/statistics";
import { MonthNavigator } from "./MonthNavigator";
@ -12,20 +12,44 @@ interface Props {
const StatisticsView = (props: Props) => {
const { statisticsData } = props;
const { activityStats } = statisticsData;
const navigateToDateFilter = useDateFilterNavigation();
const [visibleMonthString, setVisibleMonthString] = useState(dayjs().format("YYYY-MM"));
const { getFiltersByFactor, addFilter, removeFilter } = useMemoFilterContext();
const displayTimeFilter = getFiltersByFactor("displayTime").length > 0 ? getFiltersByFactor("displayTime") : [];
const selectedDate = useMemo(() => {
if (!displayTimeFilter.length) return null;
return new Date(displayTimeFilter[0].value);
}, [displayTimeFilter]);
const maxCount = useMemo(() => {
const counts = Object.values(activityStats);
return Math.max(...counts, 1);
}, [activityStats]);
const handleDateCellClick = (date: string) => {
if (displayTimeFilter.length > 0 && displayTimeFilter[0].value === date) {
removeFilter((f: MemoFilter) => f.factor === "displayTime" && f.value === date);
} else {
// Remove all existing tag filters first, then add the new one
removeFilter((f: MemoFilter) => f.factor === "displayTime");
addFilter({
factor: "displayTime",
value: date,
});
}
};
return (
<div className="group w-full mt-2 flex flex-col text-muted-foreground animate-fade-in">
<MonthNavigator visibleMonth={visibleMonthString} onMonthChange={setVisibleMonthString} activityStats={activityStats} />
<div className="w-full animate-scale-in">
<MonthCalendar month={visibleMonthString} data={activityStats} maxCount={maxCount} onClick={navigateToDateFilter} />
<MonthCalendar
month={visibleMonthString}
selectedDate={selectedDate ? selectedDate.toDateString() : null}
data={activityStats}
maxCount={maxCount}
onClick={handleDateCellClick}
/>
</div>
</div>
);