Files
workspace/code/app/app-rn/src/components/DateTime.tsx
T
2026-06-30 22:46:53 +08:00

121 lines
3.1 KiB
TypeScript

import DateTimePicker from "react-native-dates-picker";
import dayjs from "dayjs";
import { Button, Spinner, useThemeColor } from "heroui-native";
import BottomSheet from "@/components/BottomSheet";
import { useState, useEffect, useCallback, useRef } from "react";
import { View } from "react-native";
import { CheckIcon } from "lucide-react-native";
import { FadeIn } from "react-native-reanimated";
export type DatePickerMode = "year" | "month" | "date" | "time" | "datetime";
export type DateFormatType = "YYYY" | "YYYY-MM" | "YYYY-MM-DD" | "HH:mm:ss" | "YYYY-MM-DD HH:mm";
interface DatePickerProps {
value: Date | string;
show?: boolean;
onClose?: () => void;
onConfirm?: (value: string) => void | Promise<void>;
mode?: DatePickerMode;
format?: DateFormatType;
}
const defaultFormatMap: Record<DatePickerMode, DateFormatType> = {
year: "YYYY",
month: "YYYY-MM",
date: "YYYY-MM-DD",
time: "HH:mm:ss",
datetime: "YYYY-MM-DD HH:mm",
};
const modeViewMap: Record<DatePickerMode, "day" | "month" | "year"> = {
year: "year",
month: "month",
date: "day",
time: "day",
datetime: "day",
};
export default function DateTime({
value,
show = false,
onClose,
onConfirm,
mode = "date",
format,
}: DatePickerProps) {
const finalFormat = format ?? defaultFormatMap[mode];
const accentColor = useThemeColor("accent");
const [open, setOpen] = useState(false);
const [tempDate, setTempDate] = useState<dayjs.Dayjs | undefined>(dayjs());
const [loading, setLoading] = useState(false);
const onCloseRef = useRef(onClose);
onCloseRef.current = onClose;
// 打开
useEffect(() => {
if (show) {
const d = dayjs(value);
setTempDate(d.isValid() ? d : dayjs());
setOpen(true);
}
}, [show, value]);
// 关闭
useEffect(() => {
if (!open && show) {
onCloseRef.current?.();
}
}, [open, show]);
const handleConfirm = useCallback(async () => {
if (loading || !tempDate) return;
try {
setLoading(true);
const result = tempDate.format(finalFormat);
await onConfirm?.(result);
setOpen(false);
} catch (e) {
console.log(e);
} finally {
setLoading(false);
}
}, [loading, tempDate, finalFormat, onConfirm]);
return (
<BottomSheet
isOpen={open}
onOpenChange={setOpen}
>
<BottomSheet.Content>
<View className="w-full items-center">
<DateTimePicker
mode="single"
date={tempDate}
initialView={modeViewMap[mode]}
timePicker={mode === "time" || mode === "datetime"}
selectedItemColor={accentColor}
onChange={({ date }) => {
if (date) setTempDate(dayjs(date));
}}
/>
<Button className="w-full" onPress={handleConfirm} isDisabled={loading}>
{loading ? (
<Spinner entering={FadeIn.delay(50)} color="white" />
) : (
<>
<CheckIcon color="#fff" />
<Button.Label>确认</Button.Label>
</>
)}
</Button>
</View>
</BottomSheet.Content>
</BottomSheet>
);
}