From 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Tue, 25 Mar 2025 15:55:45 +0900 Subject: initial commit --- components/date-range-picker.tsx | 146 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 components/date-range-picker.tsx (limited to 'components/date-range-picker.tsx') diff --git a/components/date-range-picker.tsx b/components/date-range-picker.tsx new file mode 100644 index 00000000..295160a5 --- /dev/null +++ b/components/date-range-picker.tsx @@ -0,0 +1,146 @@ +"use client" + +import * as React from "react" +import { format } from "date-fns" +import { CalendarIcon } from "lucide-react" +import { parseAsString, useQueryStates } from "nuqs" +import { type DateRange } from "react-day-picker" + +import { cn } from "@/lib/utils" +import { Button, type ButtonProps } from "@/components/ui/button" +import { Calendar } from "@/components/ui/calendar" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" + +interface DateRangePickerProps + extends React.ComponentPropsWithoutRef { + /** + * The selected date range. + * @default undefined + * @type DateRange + * @example { from: new Date(), to: new Date() } + */ + defaultDateRange?: DateRange + + /** + * The placeholder text of the calendar trigger button. + * @default "Pick a date" + * @type string | undefined + */ + placeholder?: string + + /** + * The variant of the calendar trigger button. + * @default "outline" + * @type "default" | "outline" | "secondary" | "ghost" + */ + triggerVariant?: Exclude + + /** + * The size of the calendar trigger button. + * @default "default" + * @type "default" | "sm" | "lg" + */ + triggerSize?: Exclude + + /** + * The class name of the calendar trigger button. + * @default undefined + * @type string + */ + triggerClassName?: string + + /** + * Controls whether query states are updated client-side only (default: true). + * Setting to `false` triggers a network request to update the querystring. + * @default true + */ + shallow?: boolean +} + +export function DateRangePicker({ + defaultDateRange, + placeholder = "Pick a date", + triggerVariant = "outline", + triggerSize = "default", + triggerClassName, + shallow = true, + className, + ...props +}: DateRangePickerProps) { + const [dateParams, setDateParams] = useQueryStates( + { + from: parseAsString.withDefault( + defaultDateRange?.from?.toISOString() ?? "" + ), + to: parseAsString.withDefault(defaultDateRange?.to?.toISOString() ?? ""), + }, + { + clearOnDefault: true, + shallow, + } + ) + + const date = React.useMemo(() => { + function parseDate(dateString: string | null) { + if (!dateString) return undefined + const parsedDate = new Date(dateString) + return isNaN(parsedDate.getTime()) ? undefined : parsedDate + } + + return { + from: parseDate(dateParams.from) ?? defaultDateRange?.from, + to: parseDate(dateParams.to) ?? defaultDateRange?.to, + } + }, [dateParams, defaultDateRange]) + + return ( +
+ + + + + + { + void setDateParams({ + from: newDateRange?.from?.toISOString() ?? "", + to: newDateRange?.to?.toISOString() ?? "", + }) + }} + numberOfMonths={2} + /> + + +
+ ) +} -- cgit v1.2.3