npm package: @mahlaparvaz/vue-datepicker
Description: Vue 3 datepicker supporting Jalali, Hijri, Gregorian, Chinese calendars with range, time picker, i18n, RTL, and full customization.
Author: Mahla Zohourparvaz
Latest version: 0.5.1

# @mahlaparvaz/vue-datepicker

<div align="center">

[![npm version](https://img.shields.io/npm/v/@mahlaparvaz/vue-datepicker.svg?style=flat-square)](https://www.npmjs.com/package/@mahlaparvaz/vue-datepicker)
[![npm downloads](https://img.shields.io/npm/dm/@mahlaparvaz/vue-datepicker.svg?style=flat-square)](https://www.npmjs.com/package/@mahlaparvaz/vue-datepicker)
[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@mahlaparvaz/vue-datepicker?style=flat-square)](https://bundlephobia.com/package/@mahlaparvaz/vue-datepicker)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)

**A powerful, lightweight Vue 3 datepicker with multi-calendar support**

[Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [Documentation](#-documentation) • [Examples](#-examples)

</div>

---

## 🎯 Why Choose This Datepicker?

- **🌍 Multi-Calendar Support**: Jalali (Persian), Gregorian, Hijri (Islamic), and Chinese calendars out of the box
- **🎨 Headless Architecture**: Complete UI control with scoped slots - build your own design
- **📦 Lightweight**: Only **~18KB gzipped** after peer dependency optimization
- **🔧 Fully Customizable**: 70+ CSS variables, theme props, and custom fonts per calendar
- **♿ Accessible**: WCAG compliant with keyboard navigation and ARIA labels
- **🌐 i18n Ready**: Built-in internationalization with RTL/LTR support
- **⚡ Zero Config**: Automatic CSS injection, no manual imports needed
- **📱 Responsive**: Mobile-optimized with touch support

---

## ✨ Features

### Selection Modes

- ✅ **Single Date**: Pick one date
- ✅ **Range Selection**: Start and end dates with visual indicators
- ✅ **Multiple Dates**: Select multiple non-consecutive dates

### Calendar Systems

- 🗓️ **Jalali (Persian/Shamsi)**: Full support for Persian calendar
- 🗓️ **Gregorian**: Standard international calendar
- 🗓️ **Hijri (Islamic)**: Lunar Islamic calendar
- 🗓️ **Chinese**: Traditional Chinese calendar

### Advanced Features

- ⏰ **Time Picker**: Optional 12/24-hour time selection
- 🎨 **Theming**: CSS variables + theme prop with dark mode support
- 🔤 **Custom Fonts**: Configure fonts per calendar type
- 📅 **Date Constraints**: Min/max dates, year range limits
- 🔄 **Output Formats**: Object, timestamp, unix, ISO, custom string, or function
- 💾 **Locale Persistence**: Remembers user's calendar preference
- ⌨️ **Keyboard Navigation**: Full keyboard support
- 📱 **Mobile Friendly**: Responsive overlay with touch gestures
- ♿ **High Contrast**: Accessibility features built-in

---

## 📦 Installation

```bash
npm install @mahlaparvaz/vue-datepicker vue-i18n @vueuse/core
```

```bash
yarn add @mahlaparvaz/vue-datepicker vue-i18n @vueuse/core
```

```bash
pnpm add @mahlaparvaz/vue-datepicker vue-i18n @vueuse/core
```

### Peer Dependencies

This package requires:

- `vue` ^3.5.0
- `vue-i18n` ^10.0.0 || ^11.0.0 || ^12.0.0-alpha.3
- `@vueuse/core` ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0

---

## 🚀 Quick Start

### Basic Usage

```vue
<script setup>
  import { ref } from 'vue';
  import { DatepickerHeadless } from '@mahlaparvaz/vue-datepicker';

  const selectedDate = ref(null);
</script>

<template>
  <DatepickerHeadless v-model="selectedDate">
    <template #default="{ open, formattedDate }">
      <input :value="formattedDate" @click="open" placeholder="Select date" readonly />
    </template>
  </DatepickerHeadless>
</template>
```

### Range Selection

```vue
<script setup>
  import { ref } from 'vue';
  import { DatepickerHeadless } from '@mahlaparvaz/vue-datepicker';

  const dateRange = ref({ start: null, end: null });
</script>

<template>
  <DatepickerHeadless v-model="dateRange" mode="range">
    <template #default="{ open, formattedDate }">
      <input :value="formattedDate" @click="open" placeholder="Select date range" readonly />
    </template>
  </DatepickerHeadless>
</template>
```

### With Time Picker

```vue
<DatepickerHeadless v-model="dateTime" :enable-time="true" :time-format="24">
  <template #default="{ open, formattedDate }">
    <input
      :value="formattedDate"
      @click="open"
      placeholder="Select date and time"
      readonly
    />
  </template>
</DatepickerHeadless>
```

### Custom Available Locales

```vue
<DatepickerHeadless v-model="date" :available-locales="['jalali', 'gregorian']">
  <template #default="{ open, formattedDate }">
    <input
      :value="formattedDate"
      @click="open"
      placeholder="Select date (Jalali/Gregorian only)"
      readonly
    />
  </template>
</DatepickerHeadless>
```

---

## 📖 Documentation

### Props

| Prop                 | Type                                | Default                                       | Description                                                                                 |
| -------------------- | ----------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `modelValue`         | `Object \| Array \| null`           | `null`                                        | Selected date(s) - use with `v-model`                                                       |
| `mode`               | `'single' \| 'range' \| 'multiple'` | `'single'`                                    | Selection mode                                                                              |
| `locale`             | `String`                            | User's saved locale                           | Calendar locale: `'jalali'`, `'gregorian'`, `'hijri'`, `'chinese'`                          |
| `availableLocales`   | `Array<String>`                     | `['jalali', 'gregorian', 'hijri', 'chinese']` | Available calendar types in locale selector                                                 |
| `placeholder`        | `String`                            | Auto-generated                                | Input placeholder text                                                                      |
| `format`             | `String`                            | `'YYYY/MM/DD'`                                | Display format for dates                                                                    |
| `outputFormat`       | `String \| Function`                | `'object'`                                    | Output format: `'object'`, `'timestamp'`, `'unix'`, `'iso'`, `'string'`, or custom function |
| `outputStringFormat` | `String`                            | `'YYYY/MM/DD'`                                | String format when `outputFormat="string"`                                                  |
| `enableTime`         | `Boolean`                           | `false`                                       | Enable time selection                                                                       |
| `timeFormat`         | `Number \| String`                  | `24`                                          | Time format: `12` or `24`                                                                   |
| `yearsBefore`        | `Number`                            | `50`                                          | Years to show before current year                                                           |
| `yearsAfter`         | `Number`                            | `50`                                          | Years to show after current year                                                            |
| `minDate`            | `Date \| String \| Object`          | `null`                                        | Minimum selectable date                                                                     |
| `maxDate`            | `Date \| String \| Object`          | `null`                                        | Maximum selectable date                                                                     |
| `disabled`           | `Boolean`                           | `false`                                       | Disable the datepicker                                                                      |
| `fontConfig`         | `Object`                            | `null`                                        | Custom fonts per calendar: `{ jalali: 'Vazir', gregorian: 'Roboto' }`                       |
| `theme`              | `Object`                            | `null`                                        | Theme configuration object                                                                  |
| `closeOnSelect`      | `Boolean`                           | `true`                                        | Close picker after selection (single mode only)                                             |
| `autoApply`          | `Boolean`                           | `false`                                       | Auto-apply selection without confirm button                                                 |

---

## 🎨 Customization

### Theme Prop

```vue
<DatepickerHeadless
  :theme="{
    colors: {
      primary: '#e91e63',
      primaryHover: '#c2185b',
      textPrimary: '#ffffff',
      background: '#1a1a1a',
    },
    dimensions: {
      inputHeight: '48px',
      borderRadius: '12px',
    },
    spacing: {
      padding: '16px',
    },
  }"
>
  <!-- ... -->
</DatepickerHeadless>
```

### CSS Variables

Override globally in your CSS:

```css
:root {
  --datepicker-primary-500: #3b82f6;
  --datepicker-primary-600: #2563eb;
  --datepicker-gray-50: #f9fafb;
  --datepicker-gray-900: #111827;
  --datepicker-border-radius: 8px;
  --datepicker-font-size-base: 14px;
  /* ...70+ more variables available */
}
```

### Custom Fonts

```vue
<DatepickerHeadless
  :font-config="{
    jalali: 'Vazir, sans-serif',
    gregorian: 'Roboto, sans-serif',
    hijri: 'Noto Naskh Arabic, serif',
    chinese: 'Noto Sans SC, sans-serif',
  }"
>
  <!-- ... -->
</DatepickerHeadless>
```

Load fonts via CDN or npm:

```html
<!-- CDN Example -->
<link
  href="https://fonts.googleapis.com/css2?family=Vazir&family=Roboto&display=swap"
  rel="stylesheet"
/>
```

---

## 💡 Examples

### Multiple Dates Selection

```vue
<script setup>
  import { ref } from 'vue';
  import { DatepickerHeadless } from '@mahlaparvaz/vue-datepicker';

  const selectedDates = ref([]);
</script>

<template>
  <DatepickerHeadless v-model="selectedDates" mode="multiple">
    <template #default="{ open, formattedDate }">
      <button @click="open">
        {{ formattedDate || 'Select multiple dates' }}
      </button>
    </template>
  </DatepickerHeadless>
</template>
```

### Persian Calendar with Custom Theme

```vue
<DatepickerHeadless
  locale="jalali"
  :font-config="{ jalali: 'Vazir' }"
  :theme="{
    colors: {
      primary: '#059669',
      primaryHover: '#047857',
    },
  }"
  v-model="persianDate"
>
  <template #default="{ open, formattedDate, fontFamily }">
    <input
      :value="formattedDate"
      :style="{ fontFamily }"
      @click="open"
      placeholder="تاریخ را انتخاب کنید"
      dir="rtl"
      readonly
    />
  </template>
</DatepickerHeadless>
```

### Date Range with Time and Constraints

```vue
<script setup>
  import { ref } from 'vue';
  import { DatepickerHeadless } from '@mahlaparvaz/vue-datepicker';

  const bookingDates = ref({ start: null, end: null });
  const today = new Date();
  const maxDate = new Date();
  maxDate.setMonth(maxDate.getMonth() + 6);
</script>

<template>
  <DatepickerHeadless
    v-model="bookingDates"
    mode="range"
    :enable-time="true"
    :min-date="today"
    :max-date="maxDate"
    output-format="iso"
  >
    <template #default="{ open, formattedDate }">
      <div class="booking-input" @click="open">
        <span>{{ formattedDate || 'Select booking period' }}</span>
      </div>
    </template>
  </DatepickerHeadless>
</template>
```

### Custom Output Format

```vue
<script setup>
  import { ref } from 'vue';
  import { DatepickerHeadless } from '@mahlaparvaz/vue-datepicker';

  const date = ref(null);

  // Custom formatter function
  const customFormat = (dateObj) => {
    if (!dateObj) return null;
    return {
      year: dateObj.jy || dateObj.year,
      month: dateObj.jm || dateObj.month,
      day: dateObj.jd || dateObj.day,
      formatted: `${dateObj.jy || dateObj.year}-${dateObj.jm || dateObj.month}-${dateObj.jd || dateObj.day}`,
      timestamp: Date.now(),
    };
  };
</script>

<template>
  <DatepickerHeadless v-model="date" :output-format="customFormat">
    <!-- ... -->
  </DatepickerHeadless>
</template>
```

### Programmatic Control

```vue
<script setup>
  import { ref } from 'vue';
  import { DatepickerHeadless } from '@mahlaparvaz/vue-datepicker';

  const date = ref(null);

  const setToday = () => {
    date.value = { year: 2024, month: 12, day: 16 };
  };

  const clearDate = () => {
    date.value = null;
  };
</script>

<template>
  <div>
    <DatepickerHeadless v-model="date">
      <template #default="{ open, formattedDate, close }">
        <input :value="formattedDate" @click="open" readonly />
      </template>
    </DatepickerHeadless>

    <button @click="setToday">Set Today</button>
    <button @click="clearDate">Clear</button>
  </div>
</template>
```

---

## 🌐 Internationalization

### Available Locales

- `jalali` - Persian/Farsi (فارسی) - RTL
- `gregorian` - English - LTR
- `hijri` - Arabic (العربية) - RTL
- `chinese` - Chinese (中文) - LTR

### Dynamic Locale Switching

```vue
<script setup>
  import { ref } from 'vue';
  import { DatepickerHeadless } from '@mahlaparvaz/vue-datepicker';

  const date = ref(null);
  const currentLocale = ref('jalali');
</script>

<template>
  <DatepickerHeadless v-model="date" v-model:locale="currentLocale">
    <template #default="{ open, formattedDate }">
      <input :value="formattedDate" @click="open" readonly />
    </template>
  </DatepickerHeadless>

  <!-- Locale persists to localStorage automatically -->
</template>
```

---

## 🔧 Utility Functions

Export and use validation utilities:

```javascript
import {
  isValidJalaaliDate,
  isValidGregorianDate,
  isValidDate,
  parseDate,
} from '@mahlaparvaz/vue-datepicker';

// Validate Jalali date
isValidJalaaliDate(1403, 9, 15); // true

// Validate Gregorian date
isValidGregorianDate(2024, 2, 29); // true (leap year)

// Parse date strings
const parsed = parseDate('1403/09/15');
// { jy: 1403, jm: 9, jd: 15 }

// Universal date validation
isValidDate({ year: 2024, month: 12, day: 16 }); // true
```

---

## 🤝 Contributing

Contributions are welcome! Please follow these steps:

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

### Development Setup

```bash
# Install dependencies
npm install

# Start development server
npm run dev

# Build library
npm run build

# Lint code
npm run lint

# Format code
npm run format
```

---

## 📝 Changelog

See [CHANGELOG.md](./CHANGELOG.md) for detailed release notes.

## 📄 License

MIT © [Mahla Zohourparvaz](https://github.com/MahlaParvaz)

---

## 💬 Support

- 📧 **Email**: mahla.zph@gmail.com
- 🐛 **Issues**: [GitHub Issues](https://github.com/MahlaParvaz/vue-datepicker/issues)
- 💬 **Discussions**: [GitHub Discussions](https://github.com/MahlaParvaz/vue-datepicker/discussions)

---

## 🌟 Show Your Support

If this package helps you, please give it a ⭐️ on [GitHub](https://github.com/MahlaParvaz/vue-datepicker)!

---

<div align="center">

**Made with ❤️ for the Vue community**

[⬆ Back to Top](#mahlaparvaz-vue-datepicker)

</div>