{"id":13810123,"url":"https://github.com/sersavan/shadcn-calendar-component","last_synced_at":"2025-04-08T03:19:56.485Z","repository":{"id":244668732,"uuid":"815915987","full_name":"sersavan/shadcn-calendar-component","owner":"sersavan","description":"A calendar date picker component designed with shadcn/ui","archived":false,"fork":false,"pushed_at":"2024-09-06T21:41:54.000Z","size":344,"stargazers_count":238,"open_issues_count":2,"forks_count":6,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T05:34:30.767Z","etag":null,"topics":["calendar","calendar-component","date-picker","date-picker-range","datepicker","nextjs","radix-ui","reactjs","shadcn","shadcn-ui"],"latest_commit_sha":null,"homepage":"https://shadcn-calendar-component.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sersavan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-16T14:16:33.000Z","updated_at":"2025-03-29T02:06:05.000Z","dependencies_parsed_at":"2024-06-29T08:29:02.247Z","dependency_job_id":"76188091-3343-4c4d-8852-1ddb05a37167","html_url":"https://github.com/sersavan/shadcn-calendar-component","commit_stats":null,"previous_names":["sersavan/shadcn-calendar-component"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sersavan%2Fshadcn-calendar-component","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sersavan%2Fshadcn-calendar-component/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sersavan%2Fshadcn-calendar-component/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sersavan%2Fshadcn-calendar-component/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sersavan","download_url":"https://codeload.github.com/sersavan/shadcn-calendar-component/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247767239,"owners_count":20992548,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calendar","calendar-component","date-picker","date-picker-range","datepicker","nextjs","radix-ui","reactjs","shadcn","shadcn-ui"],"created_at":"2024-08-04T02:00:46.287Z","updated_at":"2025-04-08T03:19:56.462Z","avatar_url":"https://github.com/sersavan.png","language":"TypeScript","funding_links":[],"categories":["Libs and Components","Components","TypeScript","Components \u0026 Libraries"],"sub_categories":[],"readme":"## Calendar Date Picker Component in Next.js\n\n### Prerequisites\n\nEnsure you have a Next.js project set up. If not, create one:\n\n```bash\nnpx create-next-app my-app --typescript\ncd my-app\n```\n\n### Step 1: Install Required Dependencies\n\nInstall the necessary dependencies:\n\n```bash\nnpm install date-fns date-fns-tz react-day-picker\nnpx shadcn@latest init\nnpx shadcn@latest add button calendar popover select\n```\n\n### Step 2: Create the Calendar Date Picker Component\n\nCreate `calendar-date-picker.tsx` in your `components` directory:\n\n```tsx\n// src/components/calendar-date-picker.tsx\n\n\"use client\";\n\nimport * as React from \"react\";\nimport { CalendarIcon } from \"lucide-react\";\nimport {\n  startOfWeek,\n  endOfWeek,\n  subDays,\n  startOfMonth,\n  endOfMonth,\n  startOfYear,\n  endOfYear,\n  startOfDay,\n  endOfDay,\n} from \"date-fns\";\nimport { toDate, formatInTimeZone } from \"date-fns-tz\";\nimport { DateRange } from \"react-day-picker\";\nimport { cva, VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { Calendar } from \"@/components/ui/calendar\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\n\nconst months = [\n  \"January\",\n  \"February\",\n  \"March\",\n  \"April\",\n  \"May\",\n  \"June\",\n  \"July\",\n  \"August\",\n  \"September\",\n  \"October\",\n  \"November\",\n  \"December\",\n];\n\nconst multiSelectVariants = cva(\n  \"flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium text-foreground ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n        outline:\n          \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground text-background\",\n        link: \"text-primary underline-offset-4 hover:underline text-background\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n);\n\ninterface CalendarDatePickerProps\n  extends React.HTMLAttributes\u003cHTMLButtonElement\u003e,\n    VariantProps\u003ctypeof multiSelectVariants\u003e {\n  id?: string;\n  className?: string;\n  date: DateRange;\n  closeOnSelect?: boolean;\n  numberOfMonths?: 1 | 2;\n  yearsRange?: number;\n  onDateSelect: (range: { from: Date; to: Date }) =\u003e void;\n}\n\nexport const CalendarDatePicker = React.forwardRef\u003c\n  HTMLButtonElement,\n  CalendarDatePickerProps\n\u003e(\n  (\n    {\n      id = \"calendar-date-picker\",\n      className,\n      date,\n      closeOnSelect = false,\n      numberOfMonths = 2,\n      yearsRange = 10,\n      onDateSelect,\n      variant,\n      ...props\n    },\n    ref\n  ) =\u003e {\n    const [isPopoverOpen, setIsPopoverOpen] = React.useState(false);\n    const [selectedRange, setSelectedRange] = React.useState\u003cstring | null\u003e(\n      numberOfMonths === 2 ? \"This Year\" : \"Today\"\n    );\n    const [monthFrom, setMonthFrom] = React.useState\u003cDate | undefined\u003e(\n      date?.from\n    );\n    const [yearFrom, setYearFrom] = React.useState\u003cnumber | undefined\u003e(\n      date?.from?.getFullYear()\n    );\n    const [monthTo, setMonthTo] = React.useState\u003cDate | undefined\u003e(\n      numberOfMonths === 2 ? date?.to : date?.from\n    );\n    const [yearTo, setYearTo] = React.useState\u003cnumber | undefined\u003e(\n      numberOfMonths === 2 ? date?.to?.getFullYear() : date?.from?.getFullYear()\n    );\n    const [highlightedPart, setHighlightedPart] = React.useState\u003cstring | null\u003e(\n      null\n    );\n\n    const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n    const handleClose = () =\u003e setIsPopoverOpen(false);\n\n    const handleTogglePopover = () =\u003e setIsPopoverOpen((prev) =\u003e !prev);\n\n    const selectDateRange = (from: Date, to: Date, range: string) =\u003e {\n      const startDate = startOfDay(toDate(from, { timeZone }));\n      const endDate =\n        numberOfMonths === 2 ? endOfDay(toDate(to, { timeZone })) : startDate;\n      onDateSelect({ from: startDate, to: endDate });\n      setSelectedRange(range);\n      setMonthFrom(from);\n      setYearFrom(from.getFullYear());\n      setMonthTo(to);\n      setYearTo(to.getFullYear());\n      closeOnSelect \u0026\u0026 setIsPopoverOpen(false);\n    };\n\n    const handleDateSelect = (range: DateRange | undefined) =\u003e {\n      if (range) {\n        let from = startOfDay(toDate(range.from as Date, { timeZone }));\n        let to = range.to ? endOfDay(toDate(range.to, { timeZone })) : from;\n        if (numberOfMonths === 1) {\n          if (range.from !== date.from) {\n            to = from;\n          } else {\n            from = startOfDay(toDate(range.to as Date, { timeZone }));\n          }\n        }\n        onDateSelect({ from, to });\n        setMonthFrom(from);\n        setYearFrom(from.getFullYear());\n        setMonthTo(to);\n        setYearTo(to.getFullYear());\n      }\n      setSelectedRange(null);\n    };\n\n    const handleMonthChange = (newMonthIndex: number, part: string) =\u003e {\n      setSelectedRange(null);\n      if (part === \"from\") {\n        if (yearFrom !== undefined) {\n          if (newMonthIndex \u003c 0 || newMonthIndex \u003e yearsRange + 1) return;\n          const newMonth = new Date(yearFrom, newMonthIndex, 1);\n          const from =\n            numberOfMonths === 2\n              ? startOfMonth(toDate(newMonth, { timeZone }))\n              : date?.from\n              ? new Date(\n                  date.from.getFullYear(),\n                  newMonth.getMonth(),\n                  date.from.getDate()\n                )\n              : newMonth;\n          const to =\n            numberOfMonths === 2\n              ? date.to\n                ? endOfDay(toDate(date.to, { timeZone }))\n                : endOfMonth(toDate(newMonth, { timeZone }))\n              : from;\n          if (from \u003c= to) {\n            onDateSelect({ from, to });\n            setMonthFrom(newMonth);\n            setMonthTo(date.to);\n          }\n        }\n      } else {\n        if (yearTo !== undefined) {\n          if (newMonthIndex \u003c 0 || newMonthIndex \u003e yearsRange + 1) return;\n          const newMonth = new Date(yearTo, newMonthIndex, 1);\n          const from = date.from\n            ? startOfDay(toDate(date.from, { timeZone }))\n            : startOfMonth(toDate(newMonth, { timeZone }));\n          const to =\n            numberOfMonths === 2\n              ? endOfMonth(toDate(newMonth, { timeZone }))\n              : from;\n          if (from \u003c= to) {\n            onDateSelect({ from, to });\n            setMonthTo(newMonth);\n            setMonthFrom(date.from);\n          }\n        }\n      }\n    };\n\n    const handleYearChange = (newYear: number, part: string) =\u003e {\n      setSelectedRange(null);\n      if (part === \"from\") {\n        if (years.includes(newYear)) {\n          const newMonth = monthFrom\n            ? new Date(newYear, monthFrom ? monthFrom.getMonth() : 0, 1)\n            : new Date(newYear, 0, 1);\n          const from =\n            numberOfMonths === 2\n              ? startOfMonth(toDate(newMonth, { timeZone }))\n              : date.from\n              ? new Date(newYear, newMonth.getMonth(), date.from.getDate())\n              : newMonth;\n          const to =\n            numberOfMonths === 2\n              ? date.to\n                ? endOfDay(toDate(date.to, { timeZone }))\n                : endOfMonth(toDate(newMonth, { timeZone }))\n              : from;\n          if (from \u003c= to) {\n            onDateSelect({ from, to });\n            setYearFrom(newYear);\n            setMonthFrom(newMonth);\n            setYearTo(date.to?.getFullYear());\n            setMonthTo(date.to);\n          }\n        }\n      } else {\n        if (years.includes(newYear)) {\n          const newMonth = monthTo\n            ? new Date(newYear, monthTo.getMonth(), 1)\n            : new Date(newYear, 0, 1);\n          const from = date.from\n            ? startOfDay(toDate(date.from, { timeZone }))\n            : startOfMonth(toDate(newMonth, { timeZone }));\n          const to =\n            numberOfMonths === 2\n              ? endOfMonth(toDate(newMonth, { timeZone }))\n              : from;\n          if (from \u003c= to) {\n            onDateSelect({ from, to });\n            setYearTo(newYear);\n            setMonthTo(newMonth);\n            setYearFrom(date.from?.getFullYear());\n            setMonthFrom(date.from);\n          }\n        }\n      }\n    };\n\n    const today = new Date();\n\n    const years = Array.from(\n      { length: yearsRange + 1 },\n      (_, i) =\u003e today.getFullYear() - yearsRange / 2 + i\n    );\n\n    const dateRanges = [\n      { label: \"Today\", start: today, end: today },\n      { label: \"Yesterday\", start: subDays(today, 1), end: subDays(today, 1) },\n      {\n        label: \"This Week\",\n        start: startOfWeek(today, { weekStartsOn: 1 }),\n        end: endOfWeek(today, { weekStartsOn: 1 }),\n      },\n      {\n        label: \"Last Week\",\n        start: subDays(startOfWeek(today, { weekStartsOn: 1 }), 7),\n        end: subDays(endOfWeek(today, { weekStartsOn: 1 }), 7),\n      },\n      { label: \"Last 7 Days\", start: subDays(today, 6), end: today },\n      {\n        label: \"This Month\",\n        start: startOfMonth(today),\n        end: endOfMonth(today),\n      },\n      {\n        label: \"Last Month\",\n        start: startOfMonth(subDays(today, today.getDate())),\n        end: endOfMonth(subDays(today, today.getDate())),\n      },\n      { label: \"This Year\", start: startOfYear(today), end: endOfYear(today) },\n      {\n        label: \"Last Year\",\n        start: startOfYear(subDays(today, 365)),\n        end: endOfYear(subDays(today, 365)),\n      },\n    ];\n\n    const handleMouseOver = (part: string) =\u003e {\n      setHighlightedPart(part);\n    };\n\n    const handleMouseLeave = () =\u003e {\n      setHighlightedPart(null);\n    };\n\n    const handleWheel = (event: React.WheelEvent, part: string) =\u003e {\n      event.preventDefault();\n      setSelectedRange(null);\n      if (highlightedPart === \"firstDay\") {\n        const newDate = new Date(date.from as Date);\n        const increment = event.deltaY \u003e 0 ? -1 : 1;\n        newDate.setDate(newDate.getDate() + increment);\n        if (newDate \u003c= (date.to as Date)) {\n          numberOfMonths === 2\n            ? onDateSelect({ from: newDate, to: new Date(date.to as Date) })\n            : onDateSelect({ from: newDate, to: newDate });\n          setMonthFrom(newDate);\n        } else if (newDate \u003e (date.to as Date) \u0026\u0026 numberOfMonths === 1) {\n          onDateSelect({ from: newDate, to: newDate });\n          setMonthFrom(newDate);\n        }\n      } else if (highlightedPart === \"firstMonth\") {\n        const currentMonth = monthFrom ? monthFrom.getMonth() : 0;\n        const newMonthIndex = currentMonth + (event.deltaY \u003e 0 ? -1 : 1);\n        handleMonthChange(newMonthIndex, \"from\");\n      } else if (highlightedPart === \"firstYear\" \u0026\u0026 yearFrom !== undefined) {\n        const newYear = yearFrom + (event.deltaY \u003e 0 ? -1 : 1);\n        handleYearChange(newYear, \"from\");\n      } else if (highlightedPart === \"secondDay\") {\n        const newDate = new Date(date.to as Date);\n        const increment = event.deltaY \u003e 0 ? -1 : 1;\n        newDate.setDate(newDate.getDate() + increment);\n        if (newDate \u003e= (date.from as Date)) {\n          onDateSelect({ from: new Date(date.from as Date), to: newDate });\n          setMonthTo(newDate);\n        }\n      } else if (highlightedPart === \"secondMonth\") {\n        const currentMonth = monthTo ? monthTo.getMonth() : 0;\n        const newMonthIndex = currentMonth + (event.deltaY \u003e 0 ? -1 : 1);\n        handleMonthChange(newMonthIndex, \"to\");\n      } else if (highlightedPart === \"secondYear\" \u0026\u0026 yearTo !== undefined) {\n        const newYear = yearTo + (event.deltaY \u003e 0 ? -1 : 1);\n        handleYearChange(newYear, \"to\");\n      }\n    };\n\n    React.useEffect(() =\u003e {\n      const firstDayElement = document.getElementById(`firstDay-${id}`);\n      const firstMonthElement = document.getElementById(`firstMonth-${id}`);\n      const firstYearElement = document.getElementById(`firstYear-${id}`);\n      const secondDayElement = document.getElementById(`secondDay-${id}`);\n      const secondMonthElement = document.getElementById(`secondMonth-${id}`);\n      const secondYearElement = document.getElementById(`secondYear-${id}`);\n\n      const elements = [\n        firstDayElement,\n        firstMonthElement,\n        firstYearElement,\n        secondDayElement,\n        secondMonthElement,\n        secondYearElement,\n      ];\n\n      const addPassiveEventListener = (element: HTMLElement | null) =\u003e {\n        if (element) {\n          element.addEventListener(\n            \"wheel\",\n            handleWheel as unknown as EventListener,\n            {\n              passive: false,\n            }\n          );\n        }\n      };\n\n      elements.forEach(addPassiveEventListener);\n\n      return () =\u003e {\n        elements.forEach((element) =\u003e {\n          if (element) {\n            element.removeEventListener(\n              \"wheel\",\n              handleWheel as unknown as EventListener\n            );\n          }\n        });\n      };\n    }, [highlightedPart, date]);\n\n    const formatWithTz = (date: Date, fmt: string) =\u003e\n      formatInTimeZone(date, timeZone, fmt);\n\n    return (\n      \u003c\u003e\n        \u003cstyle\u003e\n          {`\n            .date-part {\n              touch-action: none;\n            }\n          `}\n        \u003c/style\u003e\n        \u003cPopover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}\u003e\n          \u003cPopoverTrigger asChild\u003e\n            \u003cButton\n              id=\"date\"\n              ref={ref}\n              {...props}\n              className={cn(\n                \"w-auto\",\n                multiSelectVariants({ variant, className })\n              )}\n              onClick={handleTogglePopover}\n              suppressHydrationWarning\n            \u003e\n              \u003cCalendarIcon className=\"mr-2 h-4 w-4\" /\u003e\n              \u003cspan\u003e\n                {date?.from ? (\n                  date.to ? (\n                    \u003c\u003e\n                      \u003cspan\n                        id={`firstDay-${id}`}\n                        className={cn(\n                          \"date-part\",\n                          highlightedPart === \"firstDay\" \u0026\u0026\n                            \"underline font-bold\"\n                        )}\n                        onMouseOver={() =\u003e handleMouseOver(\"firstDay\")}\n                        onMouseLeave={handleMouseLeave}\n                      \u003e\n                        {formatWithTz(date.from, \"dd\")}\n                      \u003c/span\u003e{\" \"}\n                      \u003cspan\n                        id={`firstMonth-${id}`}\n                        className={cn(\n                          \"date-part\",\n                          highlightedPart === \"firstMonth\" \u0026\u0026\n                            \"underline font-bold\"\n                        )}\n                        onMouseOver={() =\u003e handleMouseOver(\"firstMonth\")}\n                        onMouseLeave={handleMouseLeave}\n                      \u003e\n                        {formatWithTz(date.from, \"LLL\")}\n                      \u003c/span\u003e\n                      ,{\" \"}\n                      \u003cspan\n                        id={`firstYear-${id}`}\n                        className={cn(\n                          \"date-part\",\n                          highlightedPart === \"firstYear\" \u0026\u0026\n                            \"underline font-bold\"\n                        )}\n                        onMouseOver={() =\u003e handleMouseOver(\"firstYear\")}\n                        onMouseLeave={handleMouseLeave}\n                      \u003e\n                        {formatWithTz(date.from, \"y\")}\n                      \u003c/span\u003e\n                      {numberOfMonths === 2 \u0026\u0026 (\n                        \u003c\u003e\n                          {\" - \"}\n                          \u003cspan\n                            id={`secondDay-${id}`}\n                            className={cn(\n                              \"date-part\",\n                              highlightedPart === \"secondDay\" \u0026\u0026\n                                \"underline font-bold\"\n                            )}\n                            onMouseOver={() =\u003e handleMouseOver(\"secondDay\")}\n                            onMouseLeave={handleMouseLeave}\n                          \u003e\n                            {formatWithTz(date.to, \"dd\")}\n                          \u003c/span\u003e{\" \"}\n                          \u003cspan\n                            id={`secondMonth-${id}`}\n                            className={cn(\n                              \"date-part\",\n                              highlightedPart === \"secondMonth\" \u0026\u0026\n                                \"underline font-bold\"\n                            )}\n                            onMouseOver={() =\u003e handleMouseOver(\"secondMonth\")}\n                            onMouseLeave={handleMouseLeave}\n                          \u003e\n                            {formatWithTz(date.to, \"LLL\")}\n                          \u003c/span\u003e\n                          ,{\" \"}\n                          \u003cspan\n                            id={`secondYear-${id}`}\n                            className={cn(\n                              \"date-part\",\n                              highlightedPart === \"secondYear\" \u0026\u0026\n                                \"underline font-bold\"\n                            )}\n                            onMouseOver={() =\u003e handleMouseOver(\"secondYear\")}\n                            onMouseLeave={handleMouseLeave}\n                          \u003e\n                            {formatWithTz(date.to, \"y\")}\n                          \u003c/span\u003e\n                        \u003c/\u003e\n                      )}\n                    \u003c/\u003e\n                  ) : (\n                    \u003c\u003e\n                      \u003cspan\n                        id=\"day\"\n                        className={cn(\n                          \"date-part\",\n                          highlightedPart === \"day\" \u0026\u0026 \"underline font-bold\"\n                        )}\n                        onMouseOver={() =\u003e handleMouseOver(\"day\")}\n                        onMouseLeave={handleMouseLeave}\n                      \u003e\n                        {formatWithTz(date.from, \"dd\")}\n                      \u003c/span\u003e{\" \"}\n                      \u003cspan\n                        id=\"month\"\n                        className={cn(\n                          \"date-part\",\n                          highlightedPart === \"month\" \u0026\u0026 \"underline font-bold\"\n                        )}\n                        onMouseOver={() =\u003e handleMouseOver(\"month\")}\n                        onMouseLeave={handleMouseLeave}\n                      \u003e\n                        {formatWithTz(date.from, \"LLL\")}\n                      \u003c/span\u003e\n                      ,{\" \"}\n                      \u003cspan\n                        id=\"year\"\n                        className={cn(\n                          \"date-part\",\n                          highlightedPart === \"year\" \u0026\u0026 \"underline font-bold\"\n                        )}\n                        onMouseOver={() =\u003e handleMouseOver(\"year\")}\n                        onMouseLeave={handleMouseLeave}\n                      \u003e\n                        {formatWithTz(date.from, \"y\")}\n                      \u003c/span\u003e\n                    \u003c/\u003e\n                  )\n                ) : (\n                  \u003cspan\u003ePick a date\u003c/span\u003e\n                )}\n              \u003c/span\u003e\n            \u003c/Button\u003e\n          \u003c/PopoverTrigger\u003e\n          {isPopoverOpen \u0026\u0026 (\n            \u003cPopoverContent\n              className=\"w-auto\"\n              align=\"center\"\n              avoidCollisions={false}\n              onInteractOutside={handleClose}\n              onEscapeKeyDown={handleClose}\n              style={{\n                maxHeight: \"var(--radix-popover-content-available-height)\",\n                overflowY: \"auto\",\n              }}\n            \u003e\n              \u003cdiv className=\"flex\"\u003e\n                {numberOfMonths === 2 \u0026\u0026 (\n                  \u003cdiv className=\"hidden md:flex flex-col gap-1 pr-4 text-left border-r border-foreground/10\"\u003e\n                    {dateRanges.map(({ label, start, end }) =\u003e (\n                      \u003cButton\n                        key={label}\n                        variant=\"ghost\"\n                        size=\"sm\"\n                        className={cn(\n                          \"justify-start hover:bg-primary/90 hover:text-background\",\n                          selectedRange === label \u0026\u0026\n                            \"bg-primary text-background hover:bg-primary/90 hover:text-background\"\n                        )}\n                        onClick={() =\u003e {\n                          selectDateRange(start, end, label);\n                          setMonthFrom(start);\n                          setYearFrom(start.getFullYear());\n                          setMonthTo(end);\n                          setYearTo(end.getFullYear());\n                        }}\n                      \u003e\n                        {label}\n                      \u003c/Button\u003e\n                    ))}\n                  \u003c/div\u003e\n                )}\n                \u003cdiv className=\"flex flex-col\"\u003e\n                  \u003cdiv className=\"flex items-center gap-4\"\u003e\n                    \u003cdiv className=\"flex gap-2 ml-3\"\u003e\n                      \u003cSelect\n                        onValueChange={(value) =\u003e {\n                          handleMonthChange(months.indexOf(value), \"from\");\n                          setSelectedRange(null);\n                        }}\n                        value={\n                          monthFrom ? months[monthFrom.getMonth()] : undefined\n                        }\n                      \u003e\n                        \u003cSelectTrigger className=\"hidden sm:flex w-[122px] focus:ring-0 focus:ring-offset-0 font-medium hover:bg-accent hover:text-accent-foreground\"\u003e\n                          \u003cSelectValue placeholder=\"Month\" /\u003e\n                        \u003c/SelectTrigger\u003e\n                        \u003cSelectContent\u003e\n                          {months.map((month, idx) =\u003e (\n                            \u003cSelectItem key={idx} value={month}\u003e\n                              {month}\n                            \u003c/SelectItem\u003e\n                          ))}\n                        \u003c/SelectContent\u003e\n                      \u003c/Select\u003e\n                      \u003cSelect\n                        onValueChange={(value) =\u003e {\n                          handleYearChange(Number(value), \"from\");\n                          setSelectedRange(null);\n                        }}\n                        value={yearFrom ? yearFrom.toString() : undefined}\n                      \u003e\n                        \u003cSelectTrigger className=\"hidden sm:flex w-[122px] focus:ring-0 focus:ring-offset-0 font-medium hover:bg-accent hover:text-accent-foreground\"\u003e\n                          \u003cSelectValue placeholder=\"Year\" /\u003e\n                        \u003c/SelectTrigger\u003e\n                        \u003cSelectContent\u003e\n                          {years.map((year, idx) =\u003e (\n                            \u003cSelectItem key={idx} value={year.toString()}\u003e\n                              {year}\n                            \u003c/SelectItem\u003e\n                          ))}\n                        \u003c/SelectContent\u003e\n                      \u003c/Select\u003e\n                    \u003c/div\u003e\n                    {numberOfMonths === 2 \u0026\u0026 (\n                      \u003cdiv className=\"flex gap-2\"\u003e\n                        \u003cSelect\n                          onValueChange={(value) =\u003e {\n                            handleMonthChange(months.indexOf(value), \"to\");\n                            setSelectedRange(null);\n                          }}\n                          value={\n                            monthTo ? months[monthTo.getMonth()] : undefined\n                          }\n                        \u003e\n                          \u003cSelectTrigger className=\"hidden sm:flex w-[122px] focus:ring-0 focus:ring-offset-0 font-medium hover:bg-accent hover:text-accent-foreground\"\u003e\n                            \u003cSelectValue placeholder=\"Month\" /\u003e\n                          \u003c/SelectTrigger\u003e\n                          \u003cSelectContent\u003e\n                            {months.map((month, idx) =\u003e (\n                              \u003cSelectItem key={idx} value={month}\u003e\n                                {month}\n                              \u003c/SelectItem\u003e\n                            ))}\n                          \u003c/SelectContent\u003e\n                        \u003c/Select\u003e\n                        \u003cSelect\n                          onValueChange={(value) =\u003e {\n                            handleYearChange(Number(value), \"to\");\n                            setSelectedRange(null);\n                          }}\n                          value={yearTo ? yearTo.toString() : undefined}\n                        \u003e\n                          \u003cSelectTrigger className=\"hidden sm:flex w-[122px] focus:ring-0 focus:ring-offset-0 font-medium hover:bg-accent hover:text-accent-foreground\"\u003e\n                            \u003cSelectValue placeholder=\"Year\" /\u003e\n                          \u003c/SelectTrigger\u003e\n                          \u003cSelectContent\u003e\n                            {years.map((year, idx) =\u003e (\n                              \u003cSelectItem key={idx} value={year.toString()}\u003e\n                                {year}\n                              \u003c/SelectItem\u003e\n                            ))}\n                          \u003c/SelectContent\u003e\n                        \u003c/Select\u003e\n                      \u003c/div\u003e\n                    )}\n                  \u003c/div\u003e\n                  \u003cdiv className=\"flex\"\u003e\n                    \u003cCalendar\n                      mode=\"range\"\n                      defaultMonth={monthFrom}\n                      month={monthFrom}\n                      onMonthChange={setMonthFrom}\n                      selected={date}\n                      onSelect={handleDateSelect}\n                      numberOfMonths={numberOfMonths}\n                      showOutsideDays={false}\n                      className={className}\n                    /\u003e\n                  \u003c/div\u003e\n                \u003c/div\u003e\n              \u003c/div\u003e\n            \u003c/PopoverContent\u003e\n          )}\n        \u003c/Popover\u003e\n      \u003c/\u003e\n    );\n  }\n);\n\nCalendarDatePicker.displayName = \"CalendarDatePicker\";\n```\n\n### Step 3: Integrate the Component\n\nUpdate `page.tsx`:\n\n```tsx\n// src/app/page.tsx\n\n\"use client\";\n\nimport React, { useState } from \"react\";\nimport { CalendarDatePicker } from \"@/components/calendar-date-picker\";\n\nfunction Home() {\n  const [selectedDateRange, setSelectedDateRange] = useState({\n    from: new Date(new Date().getFullYear(), 0, 1),\n    to: new Date(),\n  });\n\n  return (\n    \u003cdiv className=\"p-4 max-w-xl\"\u003e\n      \u003ch1 className=\"text-2xl font-bold mb-4\"\u003e\n        Calendar Date Picker Component\n      \u003c/h1\u003e\n      \u003cCalendarDatePicker\n        date={selectedDateRange}\n        onDateSelect={setSelectedDateRange}\n      /\u003e\n      \u003cdiv className=\"mt-4\"\u003e\n        \u003ch2 className=\"text-md font-semibold\"\u003eSelected Date Range:\u003c/h2\u003e\n        \u003cp className=\"text-sm\"\u003e\n          {selectedDateRange.from.toDateString()} -{\" \"}\n          {selectedDateRange.to.toDateString()}\n        \u003c/p\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n}\n\nexport default Home;\n```\n\n### Step 4: Run Your Project\n\n```bash\nnpm run dev\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsersavan%2Fshadcn-calendar-component","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsersavan%2Fshadcn-calendar-component","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsersavan%2Fshadcn-calendar-component/lists"}