memos/web/src/components/StatisticsView/StatisticsView.tsx
johnnyjoygh ec689c3a03 chore(sidebar): replace static mode with a library-content fallback
The rail had two navigation grammars for the same destinations: a
compact icon strip on recognized routes, and StaticNavigation — full
labeled rows — on everything else (/about, /403, /404, wildcard).
Crossing onto one of those pages rearranged the entire rail, and any
route nobody remembered to classify silently inherited the alternate
layout.

Make the frame invariant instead: header, global strip, contextual
content, footer — on every route. Pages without a specific sidebar
tenant now fall back to the default library content (the home
collection for signed-in users, explore for guests), following the
convention that error and utility pages render inside the persistent
shell rather than degrading it. The memo-detail route gets the same
fallback while the page has not yet published its descriptor, instead
of a blank scroll area.

Off the collection routes the library is display-plus-navigation, so
StatisticsView and TagsSection accept a navigationTarget: calendar and
tag clicks land on / or /explore with the filter in the query string,
which RootLayout's route-change effect would otherwise wipe.

StaticNavigation is deleted, About returns to living only in the user
menu (the showAbout prop is gone), and the guest strip now marks About
active on /about.
2026-08-07 00:04:04 +08:00

45 lines
1.7 KiB
TypeScript

import dayjs from "dayjs";
import { useState } from "react";
import { calculateMaxCount, MonthCalendar } from "@/components/ActivityCalendar";
import { useMemoFilterContext } from "@/contexts/MemoFilterContext";
import { useDateFilterNavigation } from "@/hooks";
import type { StatisticsData } from "@/types/statistics";
import { MonthNavigator } from "./MonthNavigator";
interface Props {
statisticsData: StatisticsData;
onDateSelect?: () => void;
/** When set, day clicks land on this route with the date filter instead of filtering the current one. */
navigationTarget?: string;
}
const StatisticsView = (props: Props) => {
const { statisticsData } = props;
const { activityStats, timeBasis } = statisticsData;
const { filters } = useMemoFilterContext();
const navigateToDateFilter = useDateFilterNavigation(props.navigationTarget);
const [visibleMonthString, setVisibleMonthString] = useState(dayjs().format("YYYY-MM"));
const selectedDate = filters.find((filter) => filter.factor === "displayTime")?.value;
return (
<div className="group mt-0.5 flex w-full flex-col text-muted-foreground animate-fade-in">
<MonthNavigator visibleMonth={visibleMonthString} onMonthChange={setVisibleMonthString} />
<div className="w-full animate-scale-in">
<MonthCalendar
month={visibleMonthString}
data={activityStats}
maxCount={calculateMaxCount(activityStats)}
selectedDate={selectedDate}
onClick={(date) => {
navigateToDateFilter(date);
props.onDateSelect?.();
}}
timeBasis={timeBasis}
/>
</div>
</div>
);
};
export default StatisticsView;