# Calendar

## Overview

Flexible calendar component built on `react-day-picker`. Supports single, multiple, and range selections, localization, week settings, and rich customization via class names and sub-components. Features custom month/year dropdowns for easy navigation.

---

## Props

Props extend `DayPicker` props with additional customization options.

| Prop              | Type                                                                          | Default     | Description                                     |
| ----------------- | ----------------------------------------------------------------------------- | ----------- | ----------------------------------------------- |
| `mode`            | `"default" \| "single" \| "multiple" \| "range"`                              | `"default"` | Selection mode.                                 |
| `selected`        | `Date \| Date[] \| DateRange`                                                 | `undefined` | Controlled selection value.                     |
| `defaultMonth`    | `Date`                                                                        | `undefined` | Month to display initially.                     |
| `minDate`         | `Date`                                                                        | `undefined` | Minimum selectable date (also sets startMonth). |
| `maxDate`         | `Date`                                                                        | `undefined` | Maximum selectable date (also sets endMonth).   |
| `disabled`        | `Matcher \| Matcher[]`                                                        | `undefined` | Disabled dates (combined with minDate/maxDate). |
| `showOutsideDays` | `boolean`                                                                     | `true`      | Show days from adjacent months.                 |
| `ISOWeek`         | `boolean`                                                                     | `false`     | Use ISO week numbering.                         |
| `fixedWeeks`      | `boolean`                                                                     | `false`     | Always show 6 weeks.                            |
| `numberOfMonths`  | `number`                                                                      | `1`         | Number of months to display.                    |
| `locale`          | `Locale`                                                                      | `undefined` | Locale object for formatting (from date-fns).   |
| `weekStartsOn`    | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6`                                             | `0`         | Start of the week (0=Sun, 1=Mon, ...).          |
| `buttonVariant`   | `"default" \| "destructive" \| "outline" \| "secondary" \| "ghost" \| "link"` | `"ghost"`   | Variant for navigation and day buttons.         |
| `formatters`      | `Partial<DayPickerFormatters>`                                                | `{}`        | Custom label formatters.                        |
| `components`      | `Partial<DayPickerComponents>`                                                | `{}`        | Custom component overrides.                     |
| `className`       | `string`                                                                      | `""`        | Additional container classes.                   |
| `classNames`      | `Partial<DayPickerClassNames>`                                                | `{}`        | Custom class names for internal elements.       |
| `onSelect`        | `(value: Date \| Date[] \| DateRange \| undefined) => void`                   | `undefined` | Called when selection changes.                  |
| `onDayClick`      | `(day: Date) => void`                                                         | `undefined` | Called on day click.                            |
| `onMonthChange`   | `(month: Date) => void`                                                       | `undefined` | Called when the displayed month changes.        |

---

## Behavior

- **Selection modes**: Supports default (no selection), single date, multiple dates, and date range selection with visual feedback.
- **Custom caption**: Features custom month/year dropdowns using AppSelect for easy navigation across months and years.
- **Date constraints**: `minDate` and `maxDate` automatically disable dates outside the range and restrict month/year navigation.
- **Disabled dates**: The `disabled` prop is combined with `minDate`/`maxDate` constraints into a unified matcher array.
- **Navigation**: Previous/next month buttons use `buttonVariant` styling and are disabled when reaching min/max boundaries.
- **Year range**: By default, allows navigation 50 years before and after the current year (overridden by minDate/maxDate).
- **Localization**: Supply `locale`, `weekStartsOn`, and `formatters` for internationalization support.
- **Visual feedback**: Today's date shows a dot indicator, selected dates are highlighted, range selections show start/middle/end styling.
- **Accessibility**: Full keyboard navigation and focus management with proper ARIA attributes.

---

## Examples

### Basic

```tsx
import { Calendar } from "laif-ds";

export function BasicCalendar() {
  return (
    <Calendar
      className="border-d-border rounded-md border"
      showOutsideDays
      onDayClick={(d) => console.log("day clicked", d)}
      onMonthChange={(m) => console.log("month", m)}
    />
  );
}
```

### With Min/Max Date Constraints

```tsx
import { Calendar } from "laif-ds";
import { useState } from "react";

export function MinMaxCalendar() {
  const [date, setDate] = useState<Date | undefined>(new Date());

  return (
    <Calendar
      mode="single"
      selected={date}
      onSelect={setDate}
      minDate={new Date(2001, 8, 11)}
      maxDate={new Date(2026, 11, 10)}
      className="border-d-border rounded-md border"
    />
  );
}
```

### Single Selection

```tsx
import { useState } from "react";
import { Calendar } from "laif-ds";

export function SingleSelection() {
  const [date, setDate] = useState<Date | undefined>(new Date());

  return (
    <div className="flex flex-col gap-2">
      <Calendar
        mode="single"
        selected={date}
        onSelect={setDate}
        className="border-d-border rounded-md border"
      />
      <p className="text-d-secondary-foreground text-center text-sm">
        {date ? date.toDateString() : "Nessuna data selezionata"}
      </p>
    </div>
  );
}
```

### Multiple Selection

```tsx
import { useState } from "react";
import { Calendar } from "laif-ds";

export function MultipleSelection() {
  const [dates, setDates] = useState<Date[] | undefined>([new Date()]);

  return (
    <div className="flex flex-col gap-2">
      <Calendar
        mode="multiple"
        selected={dates}
        onSelect={setDates}
        className="border-d-border rounded-md border"
      />
      <p className="text-d-secondary-foreground text-center text-sm">
        {dates && dates.length > 0
          ? `${dates.length} date selezionate`
          : "Nessuna data selezionata"}
      </p>
    </div>
  );
}
```

### Range Selection

```tsx
import { useState } from "react";
import { Calendar } from "laif-ds";
import type { DateRange } from "react-day-picker";

export function RangeSelection() {
  const [range, setRange] = useState<DateRange | undefined>({
    from: new Date(),
    to: new Date(),
  });

  return (
    <div className="flex flex-col gap-2">
      <Calendar
        mode="range"
        selected={range}
        onSelect={setRange}
        className="border-d-border rounded-md border"
        numberOfMonths={2}
        showOutsideDays
      />
      <p className="text-d-secondary-foreground text-center text-sm">
        {range?.from && range?.to
          ? `${range.from.toDateString()} - ${range.to.toDateString()}`
          : range?.from
            ? `Da ${range.from.toDateString()}`
            : "Seleziona un intervallo"}
      </p>
    </div>
  );
}
```

### Disabled Dates

```tsx
import { useState } from "react";
import { Calendar } from "laif-ds";
import { addDays } from "date-fns";

export function DisabledDates() {
  const [date, setDate] = useState<Date | undefined>(new Date());
  const disabledDays = [
    { from: addDays(new Date(), 1), to: addDays(new Date(), 5) },
    new Date(new Date().setDate(15)),
    { before: addDays(new Date(), -10) },
  ];

  return (
    <Calendar
      mode="single"
      selected={date}
      onSelect={setDate}
      disabled={disabledDays}
      className="border-d-border rounded-md border"
    />
  );
}
```

### Localized (Italian) and Week Start

```tsx
import { Calendar } from "laif-ds";
import { it } from "date-fns/locale";
import { format } from "date-fns";

export function LocalizedCalendar() {
  return (
    <Calendar
      className="border-d-border rounded-md border"
      locale={it}
      weekStartsOn={1}
      formatters={{
        formatMonthCaption: (date: Date) =>
          format(date, "MMMM yyyy", { locale: it }),
        formatWeekdayName: (date: Date) =>
          format(date, "EEEEEE", { locale: it }),
      }}
    />
  );
}
```

### ISO Week, Fixed Weeks, Multiple Months

```tsx
import { Calendar } from "laif-ds";

export function ISOWeekCalendar() {
  return (
    <Calendar
      className="border-d-border rounded-md border"
      ISOWeek
      weekStartsOn={1}
    />
  );
}

export function FixedWeeksCalendar() {
  return (
    <Calendar
      className="border-d-border rounded-md border"
      fixedWeeks
      showOutsideDays
    />
  );
}

export function MultiMonthCalendar() {
  return (
    <Calendar
      className="border-d-border rounded-md border"
      numberOfMonths={2}
      showOutsideDays
    />
  );
}
```

---

## Notes

- **Custom caption component**: The calendar uses a custom `MonthCaption` component with searchable AppSelect dropdowns for month and year selection.
- **Month/year navigation**: Months and years outside the minDate/maxDate range are automatically disabled in the dropdowns.
- **Default year range**: Without minDate/maxDate, the calendar allows navigation 50 years before and after the current year.
- **Customization**: Use `classNames` and `components` props to override internal parts and styling.
- **Button variants**: `buttonVariant` controls the appearance of navigation buttons and day buttons.
- **Range selection**: Range mode highlights start, middle, and end days with appropriate rounded edges and background colors.
- **Today indicator**: Current date shows a small dot at the bottom of the day cell.
- **Design tokens**: Uses design tokens for consistent theming across light/dark modes.
- **Performance**: Efficient rendering with React.useMemo for computed values and proper ref management.
