npm package: @mrasti/jalali-react-calendar
Description: Modern dual calendar (Jalali/Gregorian) React component with full Persian support
Author: Mahdi Rasti
Latest version: 1.0.6

# Jalali React Calendar

A modern, fully-featured dual calendar (Jalali/Gregorian) React component with full Persian localization, TypeScript support, and flexible theming.

[![npm version](https://img.shields.io/npm/v/@mrasti/jalali-react-calendar)](https://www.npmjs.com/package/@mrasti/jalali-react-calendar)
[![license](https://img.shields.io/npm/l/@mrasti/jalali-react-calendar)](https://github.com/devmahdi/jalali-date-picker/blob/main/LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/devmahdi/jalali-date-picker)](https://github.com/devmahdi/jalali-date-picker)

**[Live Demo](https://devmahdi.github.io/jalali-date-picker)**

## Features

- **Dual Calendar Support** - Seamlessly switch between Jalali (Persian/Solar Hijri) and Gregorian calendars
- **Full Persian Localization** - Complete Persian month names (فروردین, اردیبهشت, ...), weekdays, and RTL support
- **Selection Modes** - Single date, date range, or multiple dates selection
- **Modern & Minimal Design** - Clean, accessible UI inspired by shadcn/ui
- **Highly Customizable** - CSS variables for theming, custom day rendering, custom icons
- **TypeScript** - Full type definitions included
- **Lightweight** - Tree-shakeable with minimal dependencies
- **Accessible** - ARIA labels, keyboard navigation support

## Installation

```bash
npm install @mrasti/jalali-react-calendar
# or
yarn add @mrasti/jalali-react-calendar
# or
pnpm add @mrasti/jalali-react-calendar
```

## Quick Start

```tsx
import { Calendar } from '@mrasti/jalali-react-calendar';
import '@mrasti/jalali-react-calendar/styles.css';

function App() {
  const [date, setDate] = useState<Date>();

  return (
    <Calendar
      calendarSystem="jalali"
      mode="single"
      selected={date}
      onSelect={setDate}
    />
  );
}
```

## Examples

### Basic Single Selection

```tsx
import { Calendar } from '@mrasti/jalali-react-calendar';

function SingleSelect() {
  const [date, setDate] = useState<Date>();

  return (
    <Calendar
      calendarSystem="jalali"
      mode="single"
      selected={date}
      onSelect={(newDate) => setDate(newDate)}
    />
  );
}
```

### Range Selection

```tsx
import { Calendar, DateRange } from '@mrasti/jalali-react-calendar';

function RangeSelect() {
  const [range, setRange] = useState<DateRange>();

  return (
    <Calendar
      calendarSystem="jalali"
      mode="range"
      numberOfMonths={2}
      selected={range}
      onSelect={(newRange) => setRange(newRange)}
    />
  );
}
```

### Multiple Dates Selection

```tsx
import { Calendar } from '@mrasti/jalali-react-calendar';

function MultipleSelect() {
  const [dates, setDates] = useState<Date[]>([]);

  return (
    <Calendar
      calendarSystem="jalali"
      mode="multiple"
      selected={dates}
      onSelect={(newDates) => setDates(newDates)}
      max={5} // Maximum 5 dates
    />
  );
}
```

### DatePicker with Input

```tsx
import { DatePicker } from '@mrasti/jalali-react-calendar';

function DatePickerExample() {
  const [date, setDate] = useState<Date>();

  return (
    <DatePicker
      calendarSystem="jalali"
      mode="single"
      value={date}
      onChange={setDate}
      placeholder="تاریخ را انتخاب کنید"
    />
  );
}
```

### With Holidays

```tsx
import { Calendar } from '@mrasti/jalali-react-calendar';

function WithHolidays() {
  const holidays = [
    new Date(2024, 2, 20), // Nowruz
    new Date(2024, 2, 21),
    new Date(2024, 2, 22),
  ];

  return (
    <Calendar
      calendarSystem="jalali"
      modifiers={{
        holiday: holidays,
      }}
      modifiersClassNames={{
        holiday: 'my-holiday-class',
      }}
    />
  );
}
```

### Custom Day Rendering

```tsx
import { Calendar, DayModifiers } from '@mrasti/jalali-react-calendar';

function CustomDayRender() {
  return (
    <Calendar
      calendarSystem="jalali"
      renderDay={(date: Date, modifiers: DayModifiers) => (
        <div className="relative">
          <span>{date.getDate()}</span>
          {modifiers.today && (
            <span className="absolute bottom-0 w-1 h-1 bg-blue-500 rounded-full" />
          )}
        </div>
      )}
    />
  );
}
```

### With Min/Max Constraints

```tsx
import { Calendar } from '@mrasti/jalali-react-calendar';

function WithConstraints() {
  const today = new Date();
  const maxDate = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000);

  return (
    <Calendar
      calendarSystem="jalali"
      minDate={today}
      maxDate={maxDate}
    />
  );
}
```

### With Disabled Dates

```tsx
import { Calendar } from '@mrasti/jalali-react-calendar';

function WithDisabledDates() {
  return (
    <Calendar
      calendarSystem="jalali"
      disabled={[
        new Date(2024, 5, 15),
        { dayOfWeek: [5, 6] }, // Disable Fridays and Saturdays
        { before: new Date() }, // Disable past dates
      ]}
    />
  );
}
```

### Gregorian Calendar

```tsx
import { Calendar } from '@mrasti/jalali-react-calendar';

function GregorianCalendar() {
  const [date, setDate] = useState<Date>();

  return (
    <Calendar
      calendarSystem="gregorian"
      mode="single"
      selected={date}
      onSelect={setDate}
    />
  );
}
```

## Usage with UI Libraries

### With MUI Dialog

```tsx
import { Dialog, DialogContent } from '@mui/material';
import { Calendar } from '@mrasti/jalali-react-calendar';

function MUIExample() {
  const [open, setOpen] = useState(false);
  const [date, setDate] = useState<Date>();

  return (
    <>
      <button onClick={() => setOpen(true)}>Open Calendar</button>
      <Dialog open={open} onClose={() => setOpen(false)}>
        <DialogContent>
          <Calendar
            calendarSystem="jalali"
            selected={date}
            onSelect={(newDate) => {
              setDate(newDate);
              setOpen(false);
            }}
          />
        </DialogContent>
      </Dialog>
    </>
  );
}
```

### With shadcn/ui Popover

```tsx
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Button } from '@/components/ui/button';
import { Calendar } from '@mrasti/jalali-react-calendar';

function ShadcnExample() {
  const [date, setDate] = useState<Date>();

  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button variant="outline">
          {date ? date.toLocaleDateString('fa-IR') : 'انتخاب تاریخ'}
        </Button>
      </PopoverTrigger>
      <PopoverContent className="w-auto p-0">
        <Calendar
          calendarSystem="jalali"
          selected={date}
          onSelect={setDate}
        />
      </PopoverContent>
    </Popover>
  );
}
```

### With Headless UI

```tsx
import { Popover } from '@headlessui/react';
import { Calendar } from '@mrasti/jalali-react-calendar';

function HeadlessExample() {
  const [date, setDate] = useState<Date>();

  return (
    <Popover className="relative">
      <Popover.Button>Select Date</Popover.Button>
      <Popover.Panel className="absolute z-10">
        <Calendar
          calendarSystem="jalali"
          selected={date}
          onSelect={setDate}
        />
      </Popover.Panel>
    </Popover>
  );
}
```

## API Reference

### Calendar Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `calendarSystem` | `'jalali' \| 'gregorian'` | `'jalali'` | Calendar system to use |
| `locale` | `Partial<CalendarLocale>` | - | Custom locale configuration |
| `mode` | `'single' \| 'range' \| 'multiple'` | `'single'` | Selection mode |
| `selected` | `Date \| DateRange \| Date[]` | - | Selected date(s) |
| `onSelect` | `function` | - | Selection change handler |
| `numberOfMonths` | `1 \| 2 \| 3` | `1` | Number of months to display |
| `defaultMonth` | `Date` | Current month | Default displayed month |
| `month` | `Date` | - | Controlled displayed month |
| `onMonthChange` | `(month: Date) => void` | - | Month change handler |
| `showOutsideDays` | `boolean` | `true` | Show days from adjacent months |
| `fixedWeeks` | `boolean` | `false` | Always show 6 weeks |
| `showNavigation` | `boolean` | `true` | Show navigation buttons |
| `showTodayButton` | `boolean` | `true` | Show today button |
| `showYearPicker` | `boolean` | `true` | Show year dropdown |
| `showMonthPicker` | `boolean` | `true` | Show month dropdown |
| `minDate` | `Date` | - | Minimum selectable date |
| `maxDate` | `Date` | - | Maximum selectable date |
| `disabled` | `DateMatcher \| DateMatcher[]` | - | Disabled dates |
| `modifiers` | `Record<string, DateMatcher>` | - | Custom day modifiers |
| `modifiersClassNames` | `Record<string, string>` | - | Class names for modifiers |
| `modifiersStyles` | `Record<string, CSSProperties>` | - | Styles for modifiers |
| `renderDay` | `(date, modifiers) => ReactNode` | - | Custom day rendering |
| `renderTooltip` | `(date) => ReactNode` | - | Custom tooltip rendering |
| `icons` | `Partial<CalendarIcons>` | - | Custom icons |
| `labels` | `Partial<CalendarLabels>` | - | Custom labels |
| `className` | `string` | - | Root element class |
| `classNames` | `Partial<CalendarClassNames>` | - | Custom class names |
| `styles` | `Partial<CalendarStyles>` | - | Custom styles |
| `dir` | `'ltr' \| 'rtl' \| 'auto'` | `'auto'` | Text direction |
| `footer` | `ReactNode` | - | Footer content |

### DatePicker Props

Extends all Calendar props, plus:

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `Date \| DateRange \| Date[]` | - | Selected value |
| `onChange` | `function` | - | Value change handler |
| `placeholder` | `string` | - | Input placeholder |
| `format` | `string` | `'yyyy/MM/dd'` | Date format string |
| `clearable` | `boolean` | `true` | Show clear button |
| `open` | `boolean` | - | Controlled open state |
| `onOpenChange` | `(open: boolean) => void` | - | Open state change handler |
| `closeOnSelect` | `boolean` | `true` | Close on date select |

## Theming

### CSS Variables

Override CSS variables to customize the appearance:

```css
.jc-root {
  --jc-background: #ffffff;
  --jc-foreground: #0f172a;
  --jc-primary: #2563eb;
  --jc-primary-foreground: #ffffff;
  --jc-muted: #f1f5f9;
  --jc-muted-foreground: #64748b;
  --jc-border: #e2e8f0;
  --jc-range-middle: #dbeafe;
  --jc-holiday: #dc2626;
  --jc-cell-size: 36px;
  --jc-radius-md: 6px;
}
```

### Dark Mode

Add `data-theme="dark"` to the root element or use the `.dark` class:

```tsx
<Calendar className="dark" />
// or
<div data-theme="dark">
  <Calendar />
</div>
```

### Custom Class Names

```tsx
<Calendar
  classNames={{
    root: 'my-calendar',
    day: 'my-day',
    daySelected: 'my-selected',
  }}
/>
```

## TypeScript

All types are exported:

```tsx
import type {
  CalendarProps,
  DateRange,
  DayModifiers,
  CalendarLocale,
  DateMatcher,
} from '@mrasti/jalali-react-calendar';
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

### Development

```bash
# Install dependencies
npm install

# Start development server
npm run dev

# Build library
npm run build

# Type check
npm run typecheck
```

## License

MIT License - see [LICENSE](LICENSE) for details.

## Author

Mahdi Rasti ([@devmahdi](https://github.com/devmahdi))