UNPKG

persian-datepicker-element

Version:

A modern Jalali (Shamsi) Date Picker web component with shadcn-like styling

521 lines (416 loc) 15.8 kB
# Persian Date Picker Element A modern, customizable Persian (Jalali) date picker web component with framework integrations. ## Features - 🎨 Fully customizable with CSS variables - 🌙 Dark mode support - 📱 Mobile-friendly with touch gestures - 🎯 Framework integrations (React, Vue, Angular) - 📅 Holiday support with multiple event types (Iran, Afghanistan, Ancient Iran, International) - 🔄 RTL support - 🎨 Multiple theme options - 📦 Zero dependencies - 🎯 TypeScript support - 📊 Range selection mode - 🚫 Disabled dates support - 🎨 Customizable UI elements visibility ## Installation ### Web Component ```bash npm install persian-datepicker-element # or yarn add persian-datepicker-element # or pnpm add persian-datepicker-element ``` ### Framework Integrations #### React ```bash npm install react-persian-datepicker-element persian-datepicker-element # or yarn add react-persian-datepicker-element persian-datepicker-element # or pnpm add react-persian-datepicker-element persian-datepicker-element ``` #### Vue ```bash npm install vue-persian-datepicker-element persian-datepicker-element # or yarn add vue-persian-datepicker-element persian-datepicker-element # or pnpm add vue-persian-datepicker-element persian-datepicker-element ``` #### Angular ```bash npm install ngx-persian-datepicker-element persian-datepicker-element # or yarn add ngx-persian-datepicker-element persian-datepicker-element # or pnpm add ngx-persian-datepicker-element persian-datepicker-element ``` ## Usage ### Web Component ```html <!-- Import the component --> <script type="module" src="node_modules/persian-datepicker-element/dist/persian-datepicker-element.min.js"></script> <!-- Use the component --> <persian-datepicker-element placeholder="انتخاب تاریخ" format="YYYY/MM/DD" show-holidays rtl ></persian-datepicker-element> ``` ### React ```tsx import { PersianDatepicker } from 'react-persian-datepicker-element'; function App() { const handleChange = (event) => { console.log('تاریخ انتخاب شده:', event.detail); }; return ( <PersianDatepicker placeholder="انتخاب تاریخ" format="YYYY/MM/DD" showEvents rtl onChange={handleChange} /> ); } ``` ### Vue ```vue <template> <PersianDatepicker placeholder="انتخاب تاریخ" format="YYYY/MM/DD" :show-holidays="true" :rtl="true" @change="handleChange" /> </template> <script setup> import { PersianDatepicker } from 'vue-persian-datepicker-element'; const handleChange = (event) => { console.log('تاریخ انتخاب شده:', event.detail); }; </script> ``` ### Angular #### 1. Using the NgModule (Traditional Angular) ```typescript // app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { NgxPersianDatepickerModule } from 'ngx-persian-datepicker-element'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, NgxPersianDatepickerModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } // app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <ngx-persian-datepicker-element placeholder="انتخاب تاریخ" format="YYYY/MM/DD" [showEvents]="true" [rtl]="true" (dateChange)="onDateChange($event)" ></ngx-persian-datepicker-element> ` }) export class AppComponent { onDateChange(event: any) { console.log('تاریخ شمسی:', event.jalali); // [year, month, day] console.log('تاریخ میلادی:', event.gregorian); console.log('آیا تعطیل است:', event.isHoliday); console.log('رویدادها:', event.events); } } ``` #### 2. As a Standalone Component (Angular 17+) ```typescript // app.component.ts import { Component } from '@angular/core'; import { NgxPersianDatepickerComponent } from 'ngx-persian-datepicker-element'; @Component({ selector: 'app-root', standalone: true, imports: [NgxPersianDatepickerComponent], template: ` <ngx-persian-datepicker-element placeholder="تاریخ را انتخاب کنید" format="YYYY/MM/DD" [showEvents]="true" (dateChange)="onDateChange($event)" ></ngx-persian-datepicker-element> ` }) export class AppComponent { onDateChange(event: any) { console.log('تاریخ شمسی:', event.jalali); // [سال, ماه, روز] console.log('تاریخ میلادی:', event.gregorian); console.log('آیا تعطیل است:', event.isHoliday); console.log('رویدادها:', event.events); } } ``` #### 3. With Reactive Forms ```typescript // app.component.ts import { Component } from '@angular/core'; import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { NgxPersianDatepickerComponent } from 'ngx-persian-datepicker-element'; @Component({ selector: 'app-root', standalone: true, imports: [NgxPersianDatepickerComponent, ReactiveFormsModule], template: ` <form [formGroup]="dateForm"> <ngx-persian-datepicker-element formControlName="date" placeholder="تاریخ را انتخاب کنید" format="YYYY/MM/DD"> </ngx-persian-datepicker-element> </form> ` }) export class AppComponent { dateForm: FormGroup; constructor(private fb: FormBuilder) { this.dateForm = this.fb.group({ date: [1403, 6, 15] // Initial value: [year, month, day] }); } } ``` ## Props & Attributes | Prop/Attribute | Type | Default | Description | |---------------|------|---------|-------------| | value | string \| [number, number, number] | - | The selected date value | | placeholder | string | - | Placeholder text | | format | string | "YYYY/MM/DD" | Date format string | | show-holidays | boolean | false | Show holiday indicators | | holiday-types | string | "Iran,Afghanistan,AncientIran,International" | Comma-separated list of holiday types to display. Use "all" to show all available holiday types | | rtl | boolean | false | Right-to-left layout | | min-date | [number, number, number] | - | Minimum selectable date | | max-date | [number, number, number] | - | Maximum selectable date | | disabled-dates | string | - | The name of a function that determines if a date should be disabled | | disabled | boolean | false | Disable the datepicker | | dark-mode | boolean | false | Enable dark mode | | range-mode | boolean | false | Enable date range selection mode | | show-month-selector | boolean | true | Show month selector dropdown | | show-year-selector | boolean | true | Show year selector dropdown | | show-prev-button | boolean | true | Show previous month button | | show-next-button | boolean | true | Show next month button | | show-today-button | boolean | true | Show today button | | show-tomorrow-button | boolean | true | Show tomorrow button | | today-button-text | string | "امروز" | Custom text for today button | | tomorrow-button-text | string | "فردا" | Custom text for tomorrow button | | today-button-class | string | "" | Additional CSS classes for today button | | tomorrow-button-class | string | "" | Additional CSS classes for tomorrow button | ## Events | Event | Detail Type | Description | |-------|-------------|-------------| | change | { jalali: [number, number, number], gregorian: [number, number, number], isHoliday: boolean, events: Array, formattedDate: string, isoString: string } | Fired when a date is selected | | change | { range: { start: [number, number, number], end: [number, number, number], startISOString: string, endISOString: string, startGregorian: [number, number, number], endGregorian: [number, number, number] }, isRange: true } | Fired when a date range is selected (in range mode) | ### Examples for accessing ISO strings #### For single date selection: ```javascript datepicker.addEventListener('change', (event) => { // ISO string for the selected date console.log('Selected date ISO string:', event.detail.isoString); // Access ISO strings from events (like holidays) if (event.detail.events.length > 0) { event.detail.events.forEach(eventItem => { console.log(`Event: ${eventItem.title}, ISO date: ${eventItem.isoString}`); }); } }); ``` #### For range selection: ```javascript rangePicker.addEventListener('change', (event) => { if (event.detail.isRange) { // ISO strings for range start and end console.log('Range start ISO:', event.detail.range.startISOString); console.log('Range end ISO:', event.detail.range.endISOString); } }); ``` ## Methods | Method | Parameters | Return Type | Description | |--------|------------|-------------|-------------| | setValue | (year: number, month: number, day: number) | void | Sets the datepicker value | | getValue | () | [number, number, number] \| null | Gets the current selected date as a tuple | | open | () | void | Opens the datepicker calendar | | close | () | void | Closes the datepicker calendar | | setMinDate | (year: number, month: number, day: number) | void | Sets the minimum allowed date | | setMaxDate | (year: number, month: number, day: number) | void | Sets the maximum allowed date | | setDisabledDatesFn | (fn: (year: number, month: number, day: number) => boolean) | void | Sets a function to determine disabled dates | | setRange | (start: [number, number, number], end: [number, number, number]) | void | Sets a date range (in range mode) | | getRange | () | { start: [number, number, number] \| null, end: [number, number, number] \| null } | Gets the current selected range | | clear | () | void | Clears the selected date or range | | seteventTypes | (types: string \| string[]) | void | Sets the holiday types to display | | geteventTypes | () | string[] | Gets the current holiday types | | isShowingAllTypes | () | boolean | Checks if all holiday types are being shown | | isSelectedDateHoliday | () | boolean | Checks if the currently selected date is a holiday | | getSelectedDateEvents | () | any[] | Gets events for the currently selected date | ## Advanced Usage ### Date Range Selection To enable date range selection mode: ```html <persian-datepicker-element range-mode></persian-datepicker-element> ``` In React: ```tsx <PersianDatepicker rangeMode /> ``` In Vue: ```vue <PersianDatepicker :range-mode="true" /> ``` In Angular: ```html <ngx-persian-datepicker-element [rangeMode]="true"></ngx-persian-datepicker-element> ``` ### Customizing UI Elements You can control the visibility of various UI elements: ```html <persian-datepicker-element show-month-selector="false" show-year-selector="true" show-prev-button="true" show-next-button="true" show-today-button="false" show-tomorrow-button="true" ></persian-datepicker-element> ``` ### Custom Button Text and Styling ```html <persian-datepicker-element today-button-text="Go to Today" tomorrow-button-text="Next Day" today-button-class="primary rounded" tomorrow-button-class="secondary rounded" ></persian-datepicker-element> ``` ### Dark Mode ```html <persian-datepicker-element dark-mode></persian-datepicker-element> ``` ## Disabled Dates There are three ways to specify which dates should be disabled: ### 1. Global Function Define a function in the global scope and reference it by name: ```html <script> function isWeekend(year, month, day) { const date = new Date(year, month - 1, day); const dayOfWeek = date.getDay(); return dayOfWeek === 5 || dayOfWeek === 6; // Disable Friday and Saturday (Persian weekend) } </script> <persian-datepicker-element disabled-dates="isWeekend"></persian-datepicker-element> ``` ### 2. Element Method Define a method directly on the element after retrieving it: ```html <persian-datepicker-element id="my-picker"></persian-datepicker-element> <script> const picker = document.getElementById('my-picker'); // Add a method to the element picker.isHoliday = function(year, month, day) { // Custom logic to determine holidays return day === 13; // Disable 13th of each month as an example }; // Reference the method by name picker.setAttribute('disabled-dates', 'isHoliday'); </script> ``` ### 3. Direct Function Assignment (Recommended for Framework Users) For React, Vue, or other framework users, you can pass a function directly: ```tsx // React example import { PersianDatepicker } from 'react-persian-datepicker-element'; function App() { // Define the function locally const isEvenDay = (year, month, day) => { return day % 2 === 0; // Disable even days }; return ( <PersianDatepicker placeholder="Select date" disabledDates={isEvenDay} /> ); } ``` You can also use the `setDisabledDatesFn` method directly: ```javascript const picker = document.getElementById('my-picker'); picker.setDisabledDatesFn((year, month, day) => { return day % 2 === 0; // Disable even days }); ``` ## Framework-Specific Features ### React - Full TypeScript support - Ref forwarding for imperative methods - React event handling - Controlled and uncontrolled modes - Custom hooks for date manipulation ### Vue - Vue 3 Composition API support - TypeScript support - Vue event handling - v-model support - Custom directives for date formatting ### Angular - Angular Ivy and Angular Signals support - TypeScript support - Angular event binding - Reactive Forms and Template-driven Forms integration - Customization using CSS variables and direct inputs - Zero configuration required - Both module-based and standalone component support ## Mobile Support The component includes built-in support for mobile devices: - Touch swipe gestures for month navigation - Mobile-optimized tooltips - Responsive design - Touch-friendly UI elements ## Browser Support - Chrome 67+ - Firefox 63+ - Safari 10.1+ - Edge 79+ ## Troubleshooting ### Common Issues 1. **Component not rendering**: Make sure you've imported the component correctly and that the script is loaded before using the component. 2. **Events not firing**: Check that you're using the correct event name and that the event handler is properly attached. 3. **Styling issues**: Verify that your CSS variables are correctly defined and that there are no conflicting styles. 4. **Date format issues**: Ensure that the format string is valid and that the date is in the correct format. 5. **Holidays not showing**: Check that the `show-holidays` attribute is set to `true` and that the `holiday-types` attribute includes the desired holiday types. ### Debugging For debugging purposes, you can enable verbose logging: ```javascript const picker = document.getElementById('my-picker'); picker.setAttribute('debug', 'true'); ``` This will log additional information to the console, which can help identify issues. ## Contributing Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests. ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.