# DatePicker

## Introduction

DatePicker is a form input component that allows users to select a single date from a calendar popup or by typing directly into the input field. It provides flexible date formatting options, date range restrictions, and supports both controlled and uncontrolled modes. DatePicker is essential for forms that require date input, such as booking systems, scheduling interfaces, and data filtering.

```tsx
<DatePicker onChange={fn()} />
```

| Field           | Description | Default |
| --------------- | ----------- | ------- |
| size            | —           | —       |
| name            | —           | —       |
| required        | —           | —       |
| format          | —           | —       |
| displayFormat   | —           | —       |
| label           | —           | —       |
| helperText      | —           | —       |
| error           | —           | —       |
| inputReadOnly   | —           | —       |
| readOnly        | —           | —       |
| hideClearButton | —           | —       |
| value           | —           | —       |
| defaultValue    | —           | —       |
| minDate         | —           | —       |
| maxDate         | —           | —       |
| disableFuture   | —           | —       |
| disablePast     | —           | —       |

> ⚠️ **Usage Warning** ⚠️
>
> DatePicker involves complex date handling logic:
>
> - **Format vs DisplayFormat**: `format` affects the value in `onChange`, while `displayFormat` affects what users see
> - **Value format consistency**: The `value` and `defaultValue` props must match the `format` prop
> - **Date validation**: Invalid date strings can cause unexpected behavior
> - **Timezone considerations**: Be aware of timezone issues when working with dates

> 💡 **Use built-in form props**
>
> This component natively supports form elements such as `label` and `helperText` props.
> When building forms, use these built-in props instead of manually composing labels and helper text with Typography.

## Usage

```tsx
import { DatePicker } from '@ceed/ads';

function DateForm() {
  const [date, setDate] = useState('');

  return (
    <DatePicker
      label="Select Date"
      value={date}
      onChange={(e) => setDate(e.target.value)}
    />
  );
}
```

## Examples

### Playground

Interactive example with all controls.

```tsx
<DatePicker onChange={fn()} />
```

### Sizes

DatePicker supports three sizes for different layouts.

```tsx
<Stack gap={2}>
  <DatePicker {...args} size="sm" />
  <DatePicker {...args} size="md" />
  <DatePicker {...args} size="lg" />
</Stack>
```

### With Label

Add a label above the date picker.

```tsx
<DatePicker
  onChange={fn()}
  label="Date"
/>
```

### With Helper Text

Provide additional guidance below the input.

```tsx
<DatePicker
  onChange={fn()}
  label="Date"
  helperText="Please select a date"
/>
```

### Error State

Show validation errors with error styling.

```tsx
<DatePicker
  onChange={fn()}
  label="Date"
  helperText="Please select a date"
  error
/>
```

### Required Field

Mark the field as required in forms.

```tsx
<DatePicker
  onChange={fn()}
  label="Label"
  helperText="I'm helper text"
  required
/>
```

### Disabled

Prevent user interaction when disabled.

```tsx
<DatePicker
  onChange={fn()}
  disabled
/>
```

### Minimum Date

Restrict selection to dates on or after a minimum date.

```tsx
<DatePicker
  onChange={fn()}
  minDate="2024-04-10"
/>
```

### Maximum Date

Restrict selection to dates on or before a maximum date.

```tsx
<DatePicker
  onChange={fn()}
  maxDate="2024-04-10"
/>
```

### Disable Future Dates

Prevent selection of dates in the future.

```tsx
<DatePicker
  onChange={fn()}
  disableFuture
/>
```

### Disable Past Dates

Prevent selection of dates in the past.

```tsx
<DatePicker
  onChange={fn()}
  disablePast
/>
```

### Controlled

Parent component manages the date state.

```tsx
<Stack gap={2}>
  <DatePicker {...args} value={value} onChange={e => setValue(e.target.value)} />
  <Button onClick={() => {
    const currentValue = new Date(value);
    currentValue.setDate(currentValue.getDate() + 1);
    const year = currentValue.getFullYear();
    const month = String(currentValue.getMonth() + 1).padStart(2, '0');
    const day = String(currentValue.getDate()).padStart(2, '0');
    setValue(`${year}/${month}/${day}`);
  }}>
  Next Day
</Button>
</Stack>
```

### Uncontrolled

Component manages its own state internally.

```tsx
<DatePicker
  onChange={fn()}
  label="Uncontrolled DatePicker"
  helperText="Please select a date"
  defaultValue="2024/04/01"
/>
```

### With Formats

Different value formats for the `onChange` event.

```tsx
<Stack gap={2}>
  <DatePicker {...args} value={value['YYYY.MM.DD']} label="YYYY.MM.DD" name="YYYY.MM.DD" format="YYYY.MM.DD" onChange={handleChange} />
  <DatePicker {...args} value={value['YYYY/MM/DD']} label="YYYY/MM/DD" name="YYYY/MM/DD" format="YYYY/MM/DD" onChange={handleChange} />
  <DatePicker {...args} value={value['MM/DD/YYYY']} label="MM/DD/YYYY" name="MM/DD/YYYY" format="MM/DD/YYYY" onChange={handleChange} />
  <DatePicker {...args} value={value['YYYY-MM-DD']} label="YYYY-MM-DD" name="YYYY-MM-DD" format="YYYY-MM-DD" onChange={handleChange} />
  <DatePicker {...args} value={value['DD/MM/YYYY']} label="DD/MM/YYYY" name="DD/MM/YYYY" format="DD/MM/YYYY" onChange={handleChange} />
  <DatePicker {...args} value={value['DD.MM.YYYY']} label="DD.MM.YYYY" name="DD.MM.YYYY" format="DD.MM.YYYY" onChange={handleChange} />
</Stack>
```

### With Display Formats

Different display formats shown in the input field.

```tsx
<Stack gap={2}>
  <DatePicker {...args} value={value1} label="YYYY.MM.DD" name="YYYY.MM.DD" displayFormat="YYYY.MM.DD" onChange={e => {
    setValue1(e.target.value);
    args.onChange?.(e);
  }} />
  <DatePicker {...args} value={value2} label="YYYY/MM/DD" name="YYYY/MM/DD" displayFormat="YYYY/MM/DD" onChange={e => {
    setValue2(e.target.value);
    args.onChange?.(e);
  }} />
  <DatePicker {...args} value={value3} label="MM/DD/YYYY" name="MM/DD/YYYY" displayFormat="MM/DD/YYYY" onChange={e => {
    setValue3(e.target.value);
    args.onChange?.(e);
  }} />
  <DatePicker {...args} value={value4} label="YYYY-MM-DD" name="YYYY-MM-DD" displayFormat="YYYY-MM-DD" onChange={e => {
    setValue4(e.target.value);
    args.onChange?.(e);
  }} />
  <DatePicker {...args} value={value5} label="DD/MM/YYYY" name="DD/MM/YYYY" displayFormat="DD/MM/YYYY" onChange={e => {
    setValue5(e.target.value);
    args.onChange?.(e);
  }} />
  <DatePicker {...args} value={value6} label="DD.MM.YYYY" name="DD.MM.YYYY" displayFormat="DD.MM.YYYY" onChange={e => {
    setValue6(e.target.value);
    args.onChange?.(e);
  }} />
</Stack>
```

### Input Read Only

Allow calendar selection only, prevent typing.

```tsx
<DatePicker
  onChange={fn()}
  value="2024/04/01"
  inputReadOnly
/>
```

### Read Only

Fully read-only state with no interaction.

```tsx
<DatePicker
  onChange={fn()}
  value="2024/04/01"
  readOnly
/>
```

### Hide Clear Button

Remove the clear button from the calendar popup.

```tsx
<DatePicker
  onChange={fn()}
  value="2024/04/01"
  hideClearButton
/>
```

### Custom Date Disabling

Use `shouldDisableDate` to disable specific dates.

```tsx
<DatePicker
  onChange={fn()}
  disablePast
  shouldDisableDate={date => [0, 6].includes(new Date(date).getDay()) || new Date(date).getTime() >= new Date().getTime() + 7 * 24 * 60 * 60 * 1000}
/>
```

### Synchronized Date Pickers

Multiple date pickers sharing the same value.

```tsx
<Stack gap={2}>
  <DatePicker label="Select Date" value={value} inputReadOnly onChange={e => {
    setValue(e.target.value);
    setValue2(e.target.value);
  }} />
  <DatePicker label="Synchronized" value={value2} disabled />
</Stack>
```

## When to Use

### ✅ Good Use Cases

- **Form date fields**: Birthdays, appointment dates, due dates
- **Booking systems**: Hotel check-in/out, flight dates, reservations
- **Scheduling**: Event dates, meeting times, deadlines
- **Data filtering**: Date range filters in reports and dashboards
- **Historical data**: Recording when events occurred
- **Deadline selection**: Task due dates, project milestones

### ❌ When Not to Use

- **Date ranges**: Use DateRangePicker for start/end date pairs
- **Month/Year only**: Use MonthPicker for month-level granularity
- **Time selection**: Use a dedicated TimePicker component
- **Relative dates**: For "last 7 days" style filters, use dropdown selection
- **Known date sets**: For selecting from predefined dates, consider Select or RadioButton

## Common Use Cases

### Form with Validation

```tsx
function BookingForm() {
  const [checkIn, setCheckIn] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = () => {
    if (!checkIn) {
      setError('Please select a check-in date');
      return;
    }
    // Submit form
  };

  return (
    <form onSubmit={handleSubmit}>
      <DatePicker
        label="Check-in Date"
        value={checkIn}
        onChange={(e) => {
          setCheckIn(e.target.value);
          setError('');
        }}
        error={!!error}
        helperText={error || 'Select your arrival date'}
        disablePast
        required
      />
      <Button type="submit">Book Now</Button>
    </form>
  );
}
```

### Date Range Restriction

```tsx
function EventScheduler() {
  const [eventDate, setEventDate] = useState('');

  // Event must be within the next 30 days
  const today = new Date();
  const maxDate = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000);

  const formatDate = (date) => {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    return `${year}/${month}/${day}`;
  };

  return (
    <DatePicker
      label="Event Date"
      value={eventDate}
      onChange={(e) => setEventDate(e.target.value)}
      minDate={formatDate(today)}
      maxDate={formatDate(maxDate)}
      helperText="Select a date within the next 30 days"
    />
  );
}
```

### Custom Date Validation

```tsx
function AppointmentPicker() {
  const [date, setDate] = useState('');

  // Disable weekends and holidays
  const shouldDisableDate = (dateString) => {
    const date = new Date(dateString);
    const day = date.getDay();

    // Disable weekends (Saturday = 6, Sunday = 0)
    if (day === 0 || day === 6) return true;

    // Disable specific holidays
    const holidays = ['2024/01/01', '2024/12/25'];
    if (holidays.includes(dateString)) return true;

    return false;
  };

  return (
    <DatePicker
      label="Appointment Date"
      value={date}
      onChange={(e) => setDate(e.target.value)}
      shouldDisableDate={shouldDisableDate}
      disablePast
      helperText="Weekdays only, excluding holidays"
    />
  );
}
```

### Different Regional Formats

```tsx
function RegionalDateForm({ locale }) {
  const [date, setDate] = useState('');

  // Format based on locale
  const getDisplayFormat = () => {
    switch (locale) {
      case 'en-US':
        return 'MM/DD/YYYY';
      case 'en-GB':
        return 'DD/MM/YYYY';
      case 'de-DE':
        return 'DD.MM.YYYY';
      default:
        return 'YYYY/MM/DD';
    }
  };

  return (
    <DatePicker
      label="Date"
      value={date}
      onChange={(e) => setDate(e.target.value)}
      format="YYYY/MM/DD"  // Internal value format stays consistent
      displayFormat={getDisplayFormat()}  // Display varies by locale
    />
  );
}
```

### Mobile-Friendly Input

```tsx
function MobileDatePicker() {
  const [date, setDate] = useState('');

  return (
    <DatePicker
      label="Select Date"
      value={date}
      onChange={(e) => setDate(e.target.value)}
      inputReadOnly  // Prevent keyboard on mobile, use calendar only
      size="lg"  // Larger touch targets
    />
  );
}
```

### Form Integration with React Hook Form

```tsx
function DateFormWithHookForm() {
  const { register, handleSubmit, setValue, watch } = useForm();
  const birthDate = watch('birthDate');

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <DatePicker
        label="Birth Date"
        value={birthDate || ''}
        onChange={(e) => setValue('birthDate', e.target.value)}
        disableFuture
        required
      />
    </form>
  );
}
```

## Props and Customization

### Key Props

| Prop                | Type                                                        | Default          | Description                                 |
| ------------------- | ----------------------------------------------------------- | ---------------- | ------------------------------------------- |
| `value`             | `string`                                                    | -                | Controlled date value (must match `format`) |
| `defaultValue`      | `string`                                                    | -                | Default value for uncontrolled mode         |
| `onChange`          | `(e: { target: { name?: string; value: string } }) => void` | -                | Change handler                              |
| `format`            | `string`                                                    | `'YYYY/MM/DD'`   | Format for `value` and `onChange`           |
| `displayFormat`     | `string`                                                    | Same as `format` | Format displayed in the input               |
| `label`             | `string`                                                    | -                | Label text                                  |
| `helperText`        | `string`                                                    | -                | Helper text below input                     |
| `error`             | `boolean`                                                   | `false`          | Error state                                 |
| `size`              | `'sm' \| 'md' \| 'lg'`                                      | `'md'`           | Component size                              |
| `disabled`          | `boolean`                                                   | `false`          | Disabled state                              |
| `required`          | `boolean`                                                   | `false`          | Required field indicator                    |
| `minDate`           | `string`                                                    | -                | Minimum selectable date                     |
| `maxDate`           | `string`                                                    | -                | Maximum selectable date                     |
| `disableFuture`     | `boolean`                                                   | `false`          | Disable all future dates                    |
| `disablePast`       | `boolean`                                                   | `false`          | Disable all past dates                      |
| `shouldDisableDate` | `(date: string) => boolean`                                 | -                | Custom date disable function                |
| `inputReadOnly`     | `boolean`                                                   | `false`          | Prevent typing, calendar only               |
| `readOnly`          | `boolean`                                                   | `false`          | Fully read-only                             |
| `hideClearButton`   | `boolean`                                                   | `false`          | Hide clear button in calendar               |

### Format vs DisplayFormat

Understanding the difference between `format` and `displayFormat`:

```tsx
// format: Affects the value in onChange
// displayFormat: Affects what users see in the input

<DatePicker
  format="YYYY-MM-DD"      // onChange returns "2024-04-15"
  displayFormat="MM/DD/YYYY" // Input shows "04/15/2024"
  onChange={(e) => {
    console.log(e.target.value); // "2024-04-15"
  }}
/>
```

### Supported Format Tokens

| Token  | Description   | Example |
| ------ | ------------- | ------- |
| `YYYY` | 4-digit year  | 2024    |
| `MM`   | 2-digit month | 04      |
| `DD`   | 2-digit day   | 15      |

Common format patterns:

- `YYYY/MM/DD` - ISO-like (default)
- `YYYY-MM-DD` - ISO 8601
- `MM/DD/YYYY` - US format
- `DD/MM/YYYY` - European format
- `DD.MM.YYYY` - German format
- `YYYY.MM.DD` - East Asian format

### Controlled vs Uncontrolled

```tsx
// Uncontrolled - component manages state
<DatePicker
  defaultValue="2024/04/01"
  onChange={(e) => console.log(e.target.value)}
/>

// Controlled - you manage state
const [date, setDate] = useState('2024/04/01');
<DatePicker
  value={date}
  onChange={(e) => setDate(e.target.value)}
/>
```

## Accessibility

DatePicker includes built-in accessibility features:

### ARIA Attributes

- Input has proper `role="textbox"`
- Calendar button has `aria-label="Toggle Calendar"`
- Calendar popup uses `role="tooltip"` with proper labeling
- Date buttons announce the full date to screen readers

### Keyboard Navigation

- **Tab**: Move focus between input and calendar button
- **Enter/Space**: Open calendar when focused on button
- **Arrow Keys**: Navigate within calendar
- **Escape**: Close calendar popup
- **Enter**: Select focused date

### Screen Reader Support

```tsx
// Dates are announced with full context
<button aria-label="April 15, 2024">15</button>

// Navigation buttons are descriptive
<button aria-label="Previous Month">←</button>
<button aria-label="Next Month">→</button>
```

### Focus Management

- Focus moves to calendar when opened
- Focus returns to input when calendar closes
- Clear visual focus indicators on all interactive elements

## Best Practices

### ✅ Do

1. **Use appropriate date restrictions**: Set min/max dates when applicable

```tsx
// ✅ Good: Reasonable date constraints
<DatePicker
  label="Birth Date"
  disableFuture
  minDate="1900/01/01"
/>
```

2. **Provide clear labels and helper text**: Guide users on expected input

```tsx
// ✅ Good: Clear guidance
<DatePicker
  label="Preferred Delivery Date"
  helperText="Select a weekday within the next 2 weeks"
/>
```

3. **Use consistent formats**: Match your application's locale conventions

```tsx
// ✅ Good: Consistent with app locale
<DatePicker
  format="YYYY-MM-DD"  // API format
  displayFormat="MM/DD/YYYY"  // US locale display
/>
```

4. **Handle empty states**: Consider what empty value means in your context

```tsx
// ✅ Good: Clear empty state handling
const [date, setDate] = useState('');
{date ? <span>Selected: {date}</span> : <span>No date selected</span>}
```

### ❌ Don't

1. **Don't use inconsistent value formats**: Always match value format to `format` prop

```tsx
// ❌ Bad: Mismatched formats
<DatePicker
  format="YYYY/MM/DD"
  value="04/15/2024"  // Wrong! Should be "2024/04/15"
/>
```

2. **Don't disable validation feedback**: Show errors when dates are invalid

```tsx
// ❌ Bad: No error feedback
<DatePicker value={invalidDate} />

// ✅ Good: Show validation state
<DatePicker value={date} error={!isValid} helperText={errorMessage} />
```

3. **Don't forget mobile users**: Use `inputReadOnly` for better mobile experience

4. **Don't use placeholder as label**: Use the `label` prop instead

```tsx
// ❌ Bad: Placeholder as label
<DatePicker placeholder="Birth Date" />

// ✅ Good: Proper label
<DatePicker label="Birth Date" />
```

## Performance Considerations

### Memoize shouldDisableDate

For complex date validation logic, memoize the function:

```tsx
const shouldDisableDate = useCallback((dateString) => {
  const date = new Date(dateString);
  // Complex validation logic
  return isHoliday(date) || isWeekend(date);
}, [holidays]); // Only recreate when holidays change

<DatePicker shouldDisableDate={shouldDisableDate} />
```

### Controlled Component Optimization

When using controlled mode, avoid unnecessary state updates:

```tsx
const handleChange = useCallback((e) => {
  setDate(e.target.value);
}, []);

<DatePicker value={date} onChange={handleChange} />
```

### Format Conversion

When working with APIs that expect different date formats:

```tsx
function DateField({ value, onChange, apiFormat = 'YYYY-MM-DD' }) {
  // Convert from API format to display
  const displayValue = useMemo(() =>
    convertFormat(value, apiFormat, 'YYYY/MM/DD'),
    [value, apiFormat]
  );

  const handleChange = useCallback((e) => {
    // Convert back to API format
    onChange(convertFormat(e.target.value, 'YYYY/MM/DD', apiFormat));
  }, [onChange, apiFormat]);

  return (
    <DatePicker
      value={displayValue}
      onChange={handleChange}
      format="YYYY/MM/DD"
    />
  );
}
```

DatePicker is a fundamental form component for date input. Choose appropriate date restrictions based on your use case, maintain consistent formats throughout your application, and provide clear guidance to users for the best experience.
