Last active
June 26, 2024 16:39
-
-
Save tanpld/2698a98f4d99fe5e639b8d668c15633c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/solid"; | |
import clsx from "clsx"; | |
import { useState, useMemo } from "react"; | |
interface YearCalendarProps { | |
availableYears?: number[]; | |
defaultYear?: number; | |
onChange: (year: number) => void; | |
} | |
const MIN_YEAR = 1900; | |
const MAX_YEAR = 2099; | |
const VIEW_LENGTH = 12; | |
export default function YearCalendar({ | |
availableYears, | |
defaultYear, | |
onChange, | |
}: YearCalendarProps) { | |
const currentYear = new Date().getFullYear(); | |
const initialStartYear = useMemo(() => { | |
const baseYear = defaultYear || currentYear; | |
return Math.min( | |
Math.max(baseYear - (baseYear % 12), MIN_YEAR), | |
MAX_YEAR - (MAX_YEAR % 12) | |
); | |
}, [defaultYear, currentYear]); | |
const [startYear, setStartYear] = useState(initialStartYear); | |
const [selectedYear, setSelectedYear] = useState<number | null>( | |
defaultYear || availableYears?.[0] || currentYear | |
); | |
const years = useMemo( | |
() => Array.from({ length: VIEW_LENGTH }, (_, i) => startYear + i), | |
[startYear] | |
); | |
const goToPrevView = () => | |
setStartYear((prevStartYear) => | |
Math.max(prevStartYear - VIEW_LENGTH, MIN_YEAR) | |
); | |
const goToNextView = () => | |
setStartYear((prevStartYear) => | |
Math.min(prevStartYear + VIEW_LENGTH, MAX_YEAR - VIEW_LENGTH + 1) | |
); | |
const handleOnClick = (year: number) => { | |
setSelectedYear(year); | |
onChange(year); | |
}; | |
const isSelected = (year: number) => year === selectedYear; | |
const isAvailable = (year: number) => | |
availableYears ? availableYears.includes(year) : true; | |
return ( | |
<div className="bg-gray-800 rounded-3xl w-[192px] py-5 px-6"> | |
<div className="flex items-center justify-between px-2"> | |
<button onClick={goToPrevView} aria-label="Previous Decade"> | |
<ChevronLeftIcon className="size-4" /> | |
</button> | |
<span className="text-lg font-bold text-amber-400">{selectedYear}</span> | |
<button onClick={goToNextView} aria-label="Next Decade"> | |
<ChevronRightIcon className="size-4" /> | |
</button> | |
</div> | |
<div className="grid grid-cols-3 mt-4"> | |
{years.map((year) => ( | |
<button | |
key={year} | |
className={clsx( | |
"size-12 text-sm flex items-center justify-center rounded-full", | |
isSelected(year) && "bg-emerald-600 text-white", | |
isAvailable(year) | |
? "cursor-pointer hover:bg-gray-600 hover:text-white" | |
: "cursor-not-allowed opacity-50" | |
)} | |
onClick={() => isAvailable(year) && handleOnClick(year)} | |
aria-label={`Select Year ${year}`} | |
> | |
{year} | |
</button> | |
))} | |
</div> | |
</div> | |
); | |
} |
Author
tanpld
commented
Jun 26, 2024

Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment