0

I'm having a hard time making a custom header for my react datepicker component.

enter image description here

Anytime I click on any month or the popover itself, it closes the whole datepicker component and does not change the date.

Here is my code:

datepicker.tsx

"use client";

import React from "react";
import DatePicker, { DatePickerProps } from "react-datepicker";
import { Ionicons } from "../../../lib";

import { enUS, pt, ptBR } from "date-fns/locale";
import { useTranslation } from "react-i18next";

import "react-datepicker/dist/react-datepicker.css";
import "./datepicker.css";
import { CustomHeader } from "./custom-header";

function Calendar({ className, ...props }: DatePickerProps) {
  const { i18n } = useTranslation();

  function getLocale() {
    switch (i18n.language) {
      case "en":
        return enUS;
      case "pt-br":
        return ptBR;
      case "pt-pt":
        return pt;
      default:
        return enUS;
    }
  }

  const locale = getLocale();

  type CustomDateInputProps = React.ButtonHTMLAttributes<HTMLButtonElement>;

  const CustomDateInput = React.forwardRef<
    HTMLButtonElement,
    CustomDateInputProps
  >(({ value, onClick }, ref) => (
    <button
      id="date-picker"
      onClick={onClick}
      className="w-full h-10 text-start text-foreground/80 bg-background rounded-lg border border-input px-10"
      ref={ref}
    >
      <Ionicons
        name="calendar-outline"
        className="absolute left-2 top-1 text-input"
        size={25}
      />
      {value}
    </button>
  ));

  return (
    <DatePicker
      locale={locale}
      calendarClassName="bg-background border-input"
      popperClassName="border-input"
      dateFormat="dd/MM/YYYY"
      renderCustomHeader={({
        date,
        changeMonth,
        changeYear,
        decreaseMonth,
        increaseMonth,
      }) => (
        <CustomHeader
          date={date}
          changeMonth={changeMonth}
          changeYear={changeYear}
          decreaseMonth={decreaseMonth}
          increaseMonth={increaseMonth}
        />
      )}
      showPopperArrow={false}
      customInput={<CustomDateInput />}
      fixedHeight
      shouldCloseOnSelect={false}
      {...props}
    />
  );
}
Calendar.displayName = "Calendar";

export { Calendar };

custom-header.tsx

import React from "react";
import { Ionicons } from "../../../lib";
import { Button } from "../button";
import { format } from "date-fns";
import { useTranslation } from "react-i18next";
import { enUS, pt, ptBR } from "date-fns/locale";
import { MonthDropdown } from "./month-dropdown";
import { YearDropdown } from "./year-dropdown";

type CustomHeaderProps = {
  date: Date;
  changeYear: (year: number) => void;
  changeMonth: (month: number) => void;
  decreaseMonth: () => void;
  increaseMonth: () => void;
};

export const CustomHeader = ({
  date,
  changeYear,
  changeMonth,
  decreaseMonth,
  increaseMonth,
}: CustomHeaderProps) => {
  const { i18n } = useTranslation();
  function getLocale() {
    switch (i18n.language) {
      case "en":
        return enUS;
      case "pt-br":
        return ptBR;
      case "pt-pt":
        return pt;
      default:
        return enUS;
    }
  }

  const locale = getLocale();

  return (
    <div className="flex flex-col gap-2">
      <div className="flex px-2 items-center bg-background">
        <Button size="sm-icon" variant="ghost" onPress={() => decreaseMonth()}>
          <Ionicons
            name="chevron-back-outline"
            className="text-foreground/60"
            size={15}
          />
        </Button>
        <span className="w-full text-foreground font-semibold">
          {format(date, "MMMM yyyy", { locale })}
        </span>
        <Button size="sm-icon" variant="ghost" onPress={() => increaseMonth()}>
          <Ionicons
            name="chevron-forward-outline"
            className="text-foreground/60"
            size={15}
          />
        </Button>
      </div>
      <div className="flex w-full justify-evenly">
        <MonthDropdown date={date} changeMonth={changeMonth} />
        <YearDropdown date={date} changeYear={changeYear} />
      </div>
    </div>
  );
};

month-dropdown.tsx

import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "../button";
import { Popover } from "../web-popover";

const months = {
  en: [
    "JAN",
    "FEB",
    "MAR",
    "APR",
    "MAY",
    "JUN",
    "JUL",
    "AUG",
    "SEP",
    "OCT",
    "NOV",
    "DEC",
  ],
  "pt-pt": [
    "jan",
    "fev",
    "mar",
    "abr",
    "mai",
    "jun",
    "jul",
    "ago",
    "set",
    "out",
    "nov",
    "dez",
  ],
  "pt-br": [
    "jan",
    "fev",
    "mar",
    "abr",
    "mai",
    "jun",
    "jul",
    "ago",
    "set",
    "out",
    "nov",
    "dez",
  ],
};

export const MonthDropdown = ({
  date,
  changeMonth,
}: {
  date: Date;
  changeMonth: (mon: number) => void;
}) => {
  const [month, setMonth] = useState<number>(date.getMonth());

  const { i18n } = useTranslation();

  useEffect(() => {
    setMonth(date.getMonth());
  }, [date]);

  return (
    <Popover>
      <Popover.Trigger className="transition-colors duration-300 text-foreground px-2 py-1 border border-input rounded-md hover:bg-accent/30">
        {months[i18n.language][month]}
      </Popover.Trigger>
      <Popover.Content className="bg-background p-2 gap-4">
        <div className="grid grid-cols-3 gap-3">
          {months[i18n.language].map((mon: string, idx: number) => (
            <Button
              key={idx}
              size="sm"
              variant={month === idx ? "default" : "outline"}
              className="w-12"
              onPress={() => {
                changeMonth(idx);
                setMonth(idx);
              }}
            >
              {mon}
            </Button>
          ))}
        </div>
      </Popover.Content>
    </Popover>
  );
};

The Popover component comes from shadcn/ui, react-datepicker is a few of the components that does not belong in the shadcn / radix environment. I've already tried many solutions but nothing worked.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.