sistema-de-chamados/src/components/ui/date-picker.tsx

128 lines
3.6 KiB
TypeScript

"use client"
import { useMemo, useState } from "react"
import { format, parseISO, isValid as isValidDate } from "date-fns"
import { ptBR } from "date-fns/locale"
import { Calendar as CalendarIcon, XIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { useLocalTimeZone } from "@/hooks/use-local-time-zone"
import { cn } from "@/lib/utils"
type DatePickerProps = {
value?: string | Date | null
onChange?: (value: string | null) => void
placeholder?: string
disabled?: boolean
minYear?: number
maxYear?: number
className?: string
align?: "start" | "center" | "end"
allowClear?: boolean
}
function normalizeDate(value?: string | Date | null) {
if (!value) return undefined
if (value instanceof Date) {
return isValidDate(value) ? value : undefined
}
const parsed = parseISO(value)
return isValidDate(parsed) ? parsed : undefined
}
export function DatePicker({
value,
onChange,
placeholder = "Selecionar data",
disabled,
minYear = 1900,
maxYear = new Date().getFullYear() + 5,
className,
align = "start",
allowClear = true,
}: DatePickerProps) {
const [open, setOpen] = useState(false)
const timeZone = useLocalTimeZone()
const selectedDate = useMemo(() => normalizeDate(value), [value])
const startMonth = useMemo(() => new Date(minYear, 0, 1), [minYear])
const endMonth = useMemo(() => new Date(maxYear, 11, 31), [maxYear])
const handleSelect = (date: Date | undefined) => {
if (!date) {
onChange?.(null)
return
}
onChange?.(format(date, "yyyy-MM-dd"))
setOpen(false)
}
const handleClear = () => {
onChange?.(null)
setOpen(false)
}
return (
<Popover open={open} onOpenChange={(next) => !disabled && setOpen(next)}>
<PopoverTrigger asChild>
<Button
type="button"
variant="outline"
className={cn(
"w-full justify-between gap-2 text-left font-normal",
!selectedDate && "text-muted-foreground",
className
)}
disabled={disabled}
>
<span>
{selectedDate
? format(selectedDate, "dd/MM/yyyy", { locale: ptBR })
: placeholder}
</span>
{allowClear && selectedDate ? (
<XIcon
className="size-4 text-muted-foreground"
aria-label="Limpar data"
role="presentation"
onClick={(event) => {
event.preventDefault()
event.stopPropagation()
handleClear()
}}
/>
) : (
<CalendarIcon className="size-4 text-muted-foreground" aria-hidden="true" />
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align={align}>
<Calendar
mode="single"
selected={selectedDate}
onSelect={handleSelect}
initialFocus
captionLayout="dropdown"
startMonth={startMonth}
endMonth={endMonth}
locale={ptBR}
timeZone={timeZone}
/>
{allowClear ? (
<div className="border-t border-border/60 p-2">
<Button
type="button"
variant="ghost"
size="sm"
className="w-full justify-center text-xs text-muted-foreground"
onClick={handleClear}
>
Limpar data
</Button>
</div>
) : null}
</PopoverContent>
</Popover>
)
}