# DatePicker

## Overview

Date picker component that supports both **single date** and **date range** selection, with optional **time picker** columns. Composed of a trigger with calendar icon and a `Calendar` component in a popover. Supports min/max date constraints, allowed dates, custom display format, sizes, localization, clearable selection, and initial calendar month.

---

## Props

### Common Props

| Prop                   | Type                                    | Default            | Description                                                                  |
| ---------------------- | --------------------------------------- | ------------------ | ---------------------------------------------------------------------------- |
| `mode`                 | `"single" \| "range"`                   | `"single"`         | Selection mode: single date or date range.                                   |
| `placeholder`          | `string`                                | `"Seleziona data"` | Text when no date is selected.                                               |
| `dateFormat`           | `string`                                | `"dd/MM/yyyy"`     | `date-fns` format string for the trigger text. Auto-adjusts with `showTime`. |
| `className`            | `string`                                | `""`               | Additional classes for the trigger element.                                  |
| `wrpClassName`         | `string`                                | `""`               | Additional classes for the outer wrapper `<div>`.                            |
| `labelClassName`       | `string`                                | `""`               | Additional classes for the label element.                                    |
| `label`                | `string \| React.ReactNode`             | `undefined`        | Label displayed above the date picker.                                       |
| `disabled`             | `boolean`                               | `false`            | Disables interactions and the popover.                                       |
| `size`                 | `"sm" \| "default" \| "lg"`             | `"default"`        | Trigger size affecting height and icon size.                                 |
| `clearable`            | `boolean`                               | `false`            | Show a clear (X) button to reset the selection.                              |
| `onClear`              | `() => void`                            | `undefined`        | Callback fired when the value is cleared via the clear button.               |
| `minDate`              | `Date`                                  | `undefined`        | Minimum selectable date. Dates before this are disabled.                     |
| `maxDate`              | `Date`                                  | `undefined`        | Maximum selectable date. Dates after this are disabled.                      |
| `availableDates`       | `Date[]`                                | `undefined`        | Only these dates are selectable; all others are disabled.                    |
| `locale`               | `Partial<Locale>`                       | `it` (Italian)     | Locale for date formatting (from date-fns).                                  |
| `initialCalendarMonth` | `Date`                                  | `undefined`        | Initial month to display in calendar (overridden by value).                  |
| `numberOfMonths`       | `number`                                | `1`                | Number of months to display side-by-side (useful for range mode).            |
| `customCalendarProps`  | `React.ComponentProps<typeof Calendar>` | `undefined`        | Custom props forwarded to the underlying Calendar component.                 |
| `id`                   | `string`                                | auto-generated     | HTML id attribute for the trigger element.                                   |
| `data-testid`          | `string`                                | `undefined`        | Test identifier attribute for E2E testing (e.g. Playwright).                 |

#### Deprecated Common Props

| Prop            | Type     | Replacement | Description                                                |
| --------------- | -------- | ----------- | ---------------------------------------------------------- |
| `firstDate`     | `Date`   | `minDate`   | ⚠️ Deprecated. Use `minDate` instead.                      |
| `lastDate`      | `Date`   | `maxDate`   | ⚠️ Deprecated. Use `maxDate` instead.                      |
| `buttonVariant` | `string` | `className` | ⚠️ Deprecated. This prop is unused. Style via `className`. |

### Single Mode Props (mode="single" or undefined)

| Prop          | Type                                | Default     | Description                                                   |
| ------------- | ----------------------------------- | ----------- | ------------------------------------------------------------- |
| `value`       | `Date`                              | `undefined` | Controlled selected date.                                     |
| `onChange`    | `(date: Date \| undefined) => void` | `undefined` | Called when the date changes.                                 |
| `showTime`    | `boolean`                           | `false`     | Show time picker columns (HH, MM) alongside the calendar.     |
| `withSeconds` | `boolean`                           | `false`     | Show seconds column in the time picker (requires `showTime`). |
| `minuteStep`  | `number`                            | `1`         | Step interval for minute options (`1`-`59`, requires `showTime`). |
| `secondStep`  | `number`                            | `1`         | Step interval for second options (`1`-`59`, useful with `withSeconds`). |

### Range Mode Props (mode="range")

| Prop       | Type                                       | Default     | Description                         |
| ---------- | ------------------------------------------ | ----------- | ----------------------------------- |
| `value`    | `DateRange` (`{ from?: Date, to?: Date }`) | `undefined` | Controlled selected date range.     |
| `onChange` | `(range: DateRange \| undefined) => void`  | `undefined` | Called when the date range changes. |

---

## Behavior

### Single Mode

- **Selection**: Click a date to select it. The selected date is highlighted.
- **Display**: Shows the formatted date in the trigger (e.g., "13/01/2025").

### Range Mode

- **Selection**: Click to set the start date, click again to set the end date. The range is highlighted.
- **Display**: Shows "from - to" format in the trigger (e.g., "13/01/2025 - 20/01/2025").
- **Multiple months**: Use `numberOfMonths={2}` to show two months side by side for easier range selection.

### Time Picker (Single Mode Only)

- **Activation**: Set `showTime` to display hour and minute columns next to the calendar.
- **Seconds**: Set `withSeconds` alongside `showTime` to add a seconds column.
- **Steps**: Use `minuteStep` and `secondStep` to control minute/second increments in their respective columns.
- **Auto-close**: When `showTime` is enabled, the popover stays open after selecting a date so the user can adjust the time. Without `showTime`, the popover closes on date selection.
- **Format auto-detection**: If no `dateFormat` is provided, the component automatically uses `"dd/MM/yyyy HH:mm"` (or `"dd/MM/yyyy HH:mm:ss"` with `withSeconds`).
- **Time preservation**: When changing the date, the previously selected time is preserved.

### Clearable

- **Clear button**: When `clearable` is `true` and a value is selected, an X button appears in the trigger.
- **Callback**: The `onClear` callback fires when the clear button is clicked.
- **Works in both modes**: Clearing resets the value to `undefined` in both single and range modes.

### Common Behavior

- **Disabled dates**: `minDate`, `maxDate`, and `availableDates` are combined into the `Calendar`'s `disabled` matcher array. Legacy `firstDate`/`lastDate` are also supported.
- **Disabled state**: When `disabled` is true, the popover will not open and the trigger is non-interactive with reduced opacity.
- **Formatting**: `dateFormat` is applied via `date-fns/format` to the selected date(s) in the trigger.
- **Calendar month**: The calendar displays the month of the selected date (or `from` date in range mode), or `initialCalendarMonth` if no date is selected.
- **State synchronization**: The component syncs internal state with the `value` prop via useEffect.
- **Localization**: The `locale` prop affects both the calendar display and the formatted date in the trigger.

---

## Examples

### Basic

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

export function BasicDatePicker() {
  return <DatePicker />;
}
```

### Controlled with State

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

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

  return (
    <DatePicker
      value={date}
      onChange={setDate}
      placeholder="Seleziona una data"
      dateFormat="PPP"
      className="w-[300px]"
      label="Data di nascita"
    />
  );
}
```

### Range Mode - Basic

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

export function RangeDatePicker() {
  const [range, setRange] = useState<DateRange | undefined>();

  return (
    <DatePicker
      mode="range"
      value={range}
      onChange={setRange}
      placeholder="Seleziona intervallo"
    />
  );
}
```

### Range Mode - With Two Months

```tsx
import { DatePicker } from "laif-ds";
import { addDays } from "date-fns";
import { useState } from "react";
import { DateRange } from "react-day-picker";

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

  return (
    <DatePicker
      mode="range"
      value={range}
      onChange={setRange}
      placeholder="Seleziona intervallo"
      numberOfMonths={2}
      label="Periodo di prenotazione"
    />
  );
}
```

### Range Mode - With Constraints

```tsx
import { DatePicker } from "laif-ds";
import { addDays } from "date-fns";
import { useState } from "react";
import { DateRange } from "react-day-picker";

export function RangeWithConstraints() {
  const today = new Date();
  const [range, setRange] = useState<DateRange | undefined>();

  return (
    <DatePicker
      mode="range"
      value={range}
      onChange={setRange}
      placeholder="Seleziona (prossimi 30 giorni)"
      firstDate={today}
      lastDate={addDays(today, 30)}
      numberOfMonths={2}
    />
  );
}
```

### Clearable

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

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

  return (
    <DatePicker
      value={date}
      onChange={setDate}
      placeholder="Seleziona data"
      clearable
      onClear={() => console.log("cleared!")}
      label="Data cancellabile"
    />
  );
}
```

### With Time Picker

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

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

  return (
    <DatePicker
      value={date}
      onChange={setDate}
      showTime
      minuteStep={5}
      label="Data e ora"
      placeholder="Seleziona data e ora"
    />
  );
}
```

### With Time Picker and Seconds

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

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

  return (
    <DatePicker
      value={date}
      onChange={setDate}
      showTime
      withSeconds
      minuteStep={5}
      secondStep={10}
      label="Data e ora con secondi"
    />
  );
}
```

### With Min/Max Dates

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

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

  return (
    <DatePicker
      value={date}
      onChange={setDate}
      placeholder="Seleziona una data nel range"
      minDate={new Date("2025-09-20")}
      maxDate={new Date("2025-09-28")}
    />
  );
}
```

### With Available Dates

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

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

  return (
    <DatePicker
      value={date}
      onChange={setDate}
      placeholder="Scegli una delle date disponibili"
      availableDates={[
        new Date("2025-09-22"),
        new Date("2025-09-25"),
        new Date("2025-09-27"),
      ]}
    />
  );
}
```

### With Localization

```tsx
import { DatePicker } from "laif-ds";
import { enGB } from "date-fns/locale";
import { useState } from "react";

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

  return (
    <DatePicker
      value={date}
      onChange={setDate}
      placeholder="Select a date"
      locale={enGB}
      dateFormat="PPP"
      label="Date (English)"
    />
  );
}
```

### With Initial Calendar Month

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

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

  return (
    <DatePicker
      value={date}
      onChange={setDate}
      placeholder="Seleziona una data"
      initialCalendarMonth={new Date("2000-01-01")}
      label="Data (calendario inizia nel 2000)"
    />
  );
}
```

### With Custom Calendar Props

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

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

  return (
    <DatePicker
      value={date}
      onChange={setDate}
      placeholder="Seleziona una data"
      customCalendarProps={{
        mode: "single",
        selected: date,
        onSelect: setDate,
        numberOfMonths: 2,
        showOutsideDays: true,
      }}
      label="Data (calendario a 2 mesi)"
    />
  );
}
```

### In Dialog or Drawer

```tsx
import { DatePicker } from "laif-ds";
import { Dialog, DialogContent, DialogTrigger } from "laif-ds";
import { Button } from "laif-ds";
import { useState } from "react";

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

  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button>Apri Dialog</Button>
      </DialogTrigger>
      <DialogContent>
        <DatePicker
          value={date}
          onChange={setDate}
          placeholder="Seleziona una data"
          label="Data"
        />
      </DialogContent>
    </Dialog>
  );
}
```

### In Drawer

```tsx
import { DatePicker } from "laif-ds";
import {
  Drawer,
  DrawerContent,
  DrawerTrigger,
  DrawerHeader,
  DrawerTitle,
  DrawerDescription,
  DrawerFooter,
  DrawerClose,
} from "laif-ds";
import { Button } from "laif-ds";
import { useState } from "react";

export function DatePickerInDrawer() {
  const [date, setDate] = useState<Date | undefined>(new Date("2023-08-12"));

  return (
    <Drawer>
      <DrawerTrigger asChild>
        <Button variant="outline">Apri Drawer con DatePicker</Button>
      </DrawerTrigger>
      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Seleziona una data</DrawerTitle>
          <DrawerDescription>
            Scegli una data dal calendario qui sotto.
          </DrawerDescription>
        </DrawerHeader>
        <div className="px-4 py-4">
          <DatePicker
            placeholder="Seleziona una data"
            value={date}
            onChange={setDate}
          />
        </div>
        {date && (
          <div className="text-d-secondary-foreground px-4 text-sm">
            Data selezionata: {date.toLocaleDateString("it-IT")}
          </div>
        )}
        <DrawerFooter>
          <DrawerClose asChild>
            <Button variant="outline">Chiudi</Button>
          </DrawerClose>
        </DrawerFooter>
      </DrawerContent>
    </Drawer>
  );
}
```

---

## Notes

- **Sizing**: Use `size` to control the trigger height and icon size (sm: h-8, default: h-9, lg: h-10).
- **Popover width**: The popover content automatically sizes to fit the Calendar (and time columns when `showTime` is enabled).
- **Internationalization**: Use `locale` prop for calendar localization and `dateFormat` for display formatting.
- **Deprecated props**: `firstDate`/`lastDate` → use `minDate`/`maxDate`. `buttonVariant` → use `className`.
- **Accessibility**: The trigger has proper ARIA attributes and keyboard navigation support.
- **State management**: Component maintains internal state but syncs with external `value` prop.
- **Range mode**: Use `mode="range"` for date range selection. The `value` and `onChange` types change accordingly. `showTime` is not available in range mode.
- **Multiple months**: In range mode, use `numberOfMonths={2}` to display two months side by side for better UX.
- **DateRange type**: Import `DateRange` from `react-day-picker` when using range mode: `{ from?: Date, to?: Date }`.
- **Clearable**: Use `clearable` to allow users to reset the selection. Works in both single and range modes.
- **Label styling**: Use `labelClassName` to apply custom styles to the label element.
