From c1990203ed18c7c14626ddb7e576a69c389f0b68 Mon Sep 17 00:00:00 2001 From: yuchenglong Date: Mon, 12 Jan 2026 17:52:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=8E=86=E6=A8=A1?= =?UTF-8?q?=E6=80=81=E6=A1=86=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=97=A5=E6=9C=9F?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=9C=80=E5=B0=8F=E5=92=8C=E6=9C=80=E5=A4=A7=E6=97=A5=E6=9C=9F?= =?UTF-8?q?=E9=99=90=E5=88=B6=EF=BC=8C=E6=9B=B4=E6=96=B0=E5=9B=BD=E9=99=85?= =?UTF-8?q?=E5=8C=96=E6=96=87=E6=9C=AC=EF=BC=8C=E6=94=B9=E8=BF=9B=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E5=92=8C=E4=BA=A4=E4=BA=92=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/simple-calendar.tsx | 265 +++++++++++++++++++++------------ 1 file changed, 166 insertions(+), 99 deletions(-) diff --git a/components/simple-calendar.tsx b/components/simple-calendar.tsx index 895e53a..49dc942 100644 --- a/components/simple-calendar.tsx +++ b/components/simple-calendar.tsx @@ -1,8 +1,11 @@ import { ThemedText } from "@/components/themed-text"; +import { IconSymbol } from "@/components/ui/icon-symbol"; import { Colors } from "@/constants/theme"; import { useTheme } from "@/context/ThemeContext"; import React, { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; import { Modal, Pressable, StyleSheet, View } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; interface CalendarModalProps { visible: boolean; @@ -18,11 +21,24 @@ export function CalendarModal({ onSelectDate, }: CalendarModalProps) { const { theme } = useTheme(); + const { i18n } = useTranslation(); const isDark = theme === "dark"; const bg = isDark ? "#1C1C1E" : "#FFFFFF"; + const textColor = isDark ? "#FFFFFF" : "#000000"; + const subTextColor = isDark ? "#8E8E93" : "#8E8E93"; const [currentMonth, setCurrentMonth] = useState(new Date(selectedDate)); + const today = useMemo(() => new Date(), []); + const minDate = useMemo( + () => new Date(today.getFullYear() - 1, today.getMonth(), 1), + [today] + ); + const maxDate = useMemo( + () => new Date(today.getFullYear() + 1, today.getMonth(), 1), + [today] + ); + const daysInMonth = useMemo(() => { const year = currentMonth.getFullYear(); const month = currentMonth.getMonth(); @@ -35,110 +51,145 @@ export function CalendarModal({ return days; }, [currentMonth]); - const weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + const weekDays = i18n.language.startsWith("zh") + ? ["周日", "周一", "周二", "周三", "周四", "周五", "周六"] + : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; // Add empty slots for start of month const startDay = daysInMonth[0]?.getDay() || 0; const blanks = Array(startDay).fill(null); + // Calculate padding cells to keep 6 rows (42 cells total) + const totalCells = 42; + const paddingBlanks = useMemo(() => { + const spaceLeft = totalCells - (blanks.length + daysInMonth.length); + return spaceLeft > 0 ? Array(spaceLeft).fill(null) : []; + }, [blanks.length, daysInMonth.length]); + const handleDayPress = (date: Date) => { - onSelectDate(date); + onSelectDate(new Date(date)); onClose(); }; + const monthTitle = currentMonth.toLocaleString(i18n.language, { + month: "long", + year: "numeric", + }); + + const canGoBack = currentMonth > minDate; + const canGoForward = currentMonth < maxDate; + return ( - - - {/* Header */} - - - setCurrentMonth( - new Date( - currentMonth.getFullYear(), - currentMonth.getMonth() - 1, - 1 + + + + + + + setCurrentMonth( + new Date( + currentMonth.getFullYear(), + currentMonth.getMonth() - 1, + 1 + ) ) - ) - } - > - {"<"} - - - {currentMonth.toLocaleString("default", { - month: "long", - year: "numeric", - })} - - - setCurrentMonth( - new Date( - currentMonth.getFullYear(), - currentMonth.getMonth() + 1, - 1 - ) - ) - } - > - {">"} - - + } + > + + - {/* Week Headers */} - - {weekDays.map((day) => ( - - {day} + + {monthTitle} - ))} - - {/* Days Grid */} - - {blanks.map((_, index) => ( - - ))} - {daysInMonth.map((date) => { - const isSelected = - date.getDate() === selectedDate.getDate() && - date.getMonth() === selectedDate.getMonth() && - date.getFullYear() === selectedDate.getFullYear(); - return ( + handleDayPress(date)} + disabled={!canGoForward} + style={[styles.navBtn, !canGoForward && { opacity: 0.2 }]} + onPress={() => + setCurrentMonth( + new Date( + currentMonth.getFullYear(), + currentMonth.getMonth() + 1, + 1 + ) + ) + } > - - {date.getDate()} - + - ); - })} - + + + + + - - Close - + + {weekDays.map((day) => ( + + {day} + + ))} + + + + {blanks.map((_, index) => ( + + ))} + {daysInMonth.map((date) => { + const isSelected = + date.getDate() === selectedDate.getDate() && + date.getMonth() === selectedDate.getMonth() && + date.getFullYear() === selectedDate.getFullYear(); + + const isToday = + new Date().toDateString() === date.toDateString(); + + return ( + handleDayPress(date)} + > + + {date.getDate()} + + + ); + })} + {/* Add padding blanks to keep constant height (6 rows) */} + {paddingBlanks.map((_, index) => ( + + ))} + + - + ); } @@ -146,49 +197,65 @@ export function CalendarModal({ const styles = StyleSheet.create({ overlay: { flex: 1, - backgroundColor: "rgba(0,0,0,0.5)", - justifyContent: "center", - padding: 20, + backgroundColor: "rgba(0,0,0,0.4)", + justifyContent: "flex-end", }, - calendarContainer: { - borderRadius: 16, - padding: 20, + dismissArea: { + flex: 1, + }, + sheet: { + borderTopLeftRadius: 20, + borderTopRightRadius: 20, + paddingHorizontal: 16, + paddingTop: 12, + paddingBottom: 20, }, header: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginBottom: 20, + height: 44, }, - navText: { - fontSize: 20, - padding: 10, + titleText: { + fontSize: 18, + fontWeight: "600", + }, + headerRight: { + flexDirection: "row", + alignItems: "center", + gap: 12, + }, + navBtn: { + padding: 8, + }, + closeBtn: { + padding: 8, }, weekRow: { flexDirection: "row", justifyContent: "space-around", - marginBottom: 10, + marginBottom: 16, }, weekDayText: { - opacity: 0.5, - width: 30, + color: "#8E8E93", + width: 44, textAlign: "center", - fontSize: 12, + fontSize: 14, }, daysGrid: { flexDirection: "row", flexWrap: "wrap", + paddingBottom: 20, }, dayCell: { - width: "14.28%", // 100% / 7 + width: "14.28%", aspectRatio: 1, justifyContent: "center", alignItems: "center", - marginBottom: 5, + marginBottom: 4, }, - closeBtn: { - alignItems: "center", - marginTop: 20, - padding: 10, + dayText: { + fontSize: 16, }, });