UNPKG

nepali-calander

Version:

A beautiful and customizable Nepali Calendar component for React applications

840 lines (692 loc) 26.9 kB
# Nepali Calendar Components Beautiful and customizable Nepali Calendar components for React applications with Bikram Sambat (BS) and Gregorian (AD) date support. ## Overview This library provides two main components: - **NepaliCalendar**: A full calendar widget for displaying monthly views - **NepaliDatePicker**: A date picker component with input field and popup calendar Both components display dates in both the Bikram Sambat (BS) and Gregorian (AD) calendar systems. They automatically detect and display the current month, support month/year navigation, and provide dual date display functionality. The components use an efficient caching system with IndexedDB to store calendar data locally for fast loading. ## Features - 🗓️ **Dual Date Display**: Shows both Nepali (BS) and English (AD) dates - 🎨 **Fully Customizable**: Extensive props for styling every element - 📱 **Responsive Design**: Works perfectly on all device sizes - 🌟 **Interactive**: Click events and date selection support - 🔄 **Month Navigation**: Easy month/year navigation with smooth transitions - 🎯 **TypeScript Support**: Full TypeScript support with type definitions - 🚀 **Lightweight**: Minimal dependencies and optimized bundle size - 🔀 **Calendar Toggle**: Switch between Nepali (BS) and Gregorian (AD) calendars - 🎉 **Event Support**: Display custom events and national holidays on calendar dates - 🇳🇵 **National Holidays**: Automatic detection and display of national holidays from API data - 📅 **Value Prop Support**: Control selected date via value prop (BS or AD format) - ⌨️ **Input Validation**: Auto-formatting and validation for date input in NepaliDatePicker - 💾 **Caching**: IndexedDB caching for fast calendar data loading - 🔍 **Smart Navigation**: Accurate month display in AD mode with proper date conversion ## Installation ```bash npm install nepali-calander ``` ## Usage ### Value Prop Format Both components support a `value` prop to control the selected date: - **NepaliDatePicker**: Value is **always in AD format** (e.g., `"2024-10-10"`), regardless of `calendarMode` - When `calendarMode="bs"`: The input displays the BS date (converted from AD value) - When `calendarMode="ad"`: The input displays the AD date directly - **NepaliCalendar**: Value format depends on `calendarMode`: - When `calendarMode="bs"`: Value should be in BS format (e.g., `"2080-10-28"`) - When `calendarMode="ad"` or default: Value should be in AD format (e.g., `"2024-10-10"`) The components automatically: - Parse the value and convert between BS and AD formats as needed - Navigate to the correct month - Fetch the required calendar data - Display the date as selected **Example:** ```tsx // NepaliDatePicker - always AD format <NepaliDatePicker calendarMode="bs" value="2024-10-10" /> // AD format, displays BS in input // NepaliCalendar - format depends on calendarMode <NepaliCalendar calendarMode="bs" value="2080-10-28" /> // BS format <NepaliCalendar calendarMode="ad" value="2024-10-10" /> // AD format ``` ### NepaliCalendar Component #### Basic Usage ```tsx import { NepaliCalendar } from "nepali-calander"; import "nepali-calander/dist/styles.css"; // Import the CSS function App() { return ( <div> <NepaliCalendar /> </div> ); } ``` #### With Toggle Between Calendars ```tsx import { NepaliCalendar } from "nepali-calander"; import "nepali-calander/dist/styles.css"; function App() { return ( <div> <NepaliCalendar showToggle={true} calendarMode="bs" // or "ad" /> </div> ); } ``` ### NepaliDatePicker Component #### Basic Usage ```tsx import { NepaliDatePicker } from "nepali-calander"; import "nepali-calander/dist/styles.css"; function App() { return ( <div> <NepaliDatePicker /> </div> ); } ``` #### Input Type DatePicker ```tsx import { NepaliDatePicker } from "nepali-calander"; import "nepali-calander/dist/styles.css"; function App() { const [selectedDate, setSelectedDate] = useState(""); const handleDateSelect = (date) => { console.log("Selected date:", date); // date.ad.date is in AD format "YYYY-MM-DD" // date.bs.date is in BS format "YYYY-MM-DD" setSelectedDate(date.ad.date); }; return ( <div> <NepaliDatePicker type="input" placeholder="Choose a date" showToggle={true} calendarMode="bs" value={selectedDate} // Controlled component onDateSelect={handleDateSelect} /> </div> ); } ``` #### DatePicker with Value Prop (Always AD Format) ```tsx import { NepaliDatePicker } from "nepali-calander"; import "nepali-calander/dist/styles.css"; function App() { return ( <div> {/* Value is always in AD format, regardless of calendarMode */} <NepaliDatePicker type="input" calendarMode="bs" value="2024-10-10" // AD format - input will show BS date (converted) showToggle={true} /> <NepaliDatePicker type="input" calendarMode="ad" value="2024-10-10" // AD format - input will show AD date showToggle={true} /> </div> ); } ``` #### Icon Type DatePicker ```tsx import { NepaliDatePicker } from "nepali-calander"; import "nepali-calander/dist/styles.css"; function App() { return ( <div> <NepaliDatePicker type="icon" showToggle={true} calendarMode="ad" /> </div> ); } ``` #### DatePicker with Date Range Restrictions ```tsx import { NepaliDatePicker } from "nepali-calander"; import "nepali-calander/dist/styles.css"; import { useState } from "react"; function App() { const [dateValue, setDateValue] = useState("2025-06-15"); return ( <div> <NepaliDatePicker type="input" value={dateValue} minDate="2025-01-01" // Minimum selectable date (AD format) maxDate="2025-12-31" // Maximum selectable date (AD format) disabled={false} // Enable/disable input field showArrows={true} // Show/hide navigation arrows calendarMode="bs" onDateSelect={(date) => { setDateValue(date.ad.date); }} /> </div> ); } ``` ### Advanced Usage Examples #### NepaliCalendar with Events and National Holidays ```tsx import { NepaliCalendar, CalendarEvent, CalendarDay } from "nepali-calander"; import "nepali-calander/dist/styles.css"; function App() { const handleDateSelect = (date: CalendarDay) => { console.log("Selected date:", date); }; const handleEventClick = (event: CalendarEvent) => { console.log("Event clicked:", event); }; const handleNationalHolidayToggle = (show: boolean) => { console.log("National holidays:", show ? "shown" : "hidden"); }; const events: CalendarEvent[] = [ { id: "1", title: "Vacation", description: "Family vacation", start: "2025-10-23", end: "2025-10-25", color: "#3b82f6", }, { id: "2", title: "Meeting", start: "2025-10-28", end: "2025-10-28", color: "#10b981", }, ]; return ( <NepaliCalendar showToggle={true} calendarMode="bs" events={events} showNationalHoliday={true} showNationalHolidayToggle={true} onDateSelect={handleDateSelect} onEventClick={handleEventClick} onNationalHolidayToggleChange={handleNationalHolidayToggle} /> ); } ``` #### NepaliCalendar with Value Prop (BS Format) ```tsx import { NepaliCalendar } from "nepali-calander"; import "nepali-calander/dist/styles.css"; function App() { return ( <NepaliCalendar showToggle={true} calendarMode="bs" value="2080-10-28" // BS format when calendarMode="bs" showNationalHoliday={true} /> ); } ``` #### NepaliCalendar with Value Prop (AD Format) ```tsx import { NepaliCalendar } from "nepali-calander"; import "nepali-calander/dist/styles.css"; function App() { return ( <NepaliCalendar showToggle={true} calendarMode="ad" value="2024-10-10" // AD format when calendarMode="ad" showNationalHoliday={true} /> ); } ``` #### NepaliCalendar with Controlled Value ```tsx import { NepaliCalendar, CalendarDay } from "nepali-calander"; import "nepali-calander/dist/styles.css"; import { useState } from "react"; function App() { const [selectedDate, setSelectedDate] = useState("2024-10-10"); const handleDateSelect = (date: CalendarDay) => { // Update with AD format date setSelectedDate(date.ad.date); }; return ( <NepaliCalendar showToggle={true} calendarMode="ad" value={selectedDate} onDateSelect={handleDateSelect} /> ); } ``` #### NepaliCalendar with Full Customization ```tsx import { NepaliCalendar, CalendarDay } from "nepali-calander"; import "nepali-calander/dist/styles.css"; function App() { const handleDateSelect = (date: CalendarDay) => { console.log("Selected date:", date); }; const handleMonthChange = (year: number, month: number) => { console.log("Month changed:", year, month); }; return ( <NepaliCalendar year={2081} month={1} showToggle={true} calendarMode="bs" onDateSelect={handleDateSelect} onMonthChange={handleMonthChange} className="my-calendar" calendarWrapperClassName="custom-wrapper" headerClassName="custom-header" titleClassName="custom-title" navButtonClassName="custom-nav-button" weekdaysClassName="custom-weekdays" weekdayClassName="custom-weekday" calendarDaysClassName="custom-calendar-days" calendarDayClassName="custom-calendar-day" bsDateClassName="custom-bs-date" adDateClassName="custom-ad-date" toggleWrapperClassName="custom-toggle-wrapper" toggleButtonClassName="custom-toggle-button" prevArrow="◀" nextArrow="▶" weekdayLabels={["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]} styles={{ calendarWrapper: { maxWidth: "500px", backgroundColor: "#f8f9fa", }, header: { backgroundColor: "#e9ecef", }, title: { fontSize: "1.5rem", }, navButton: { fontSize: "1.2rem", }, weekday: { fontWeight: 600, }, saturday: { color: "#dc3545", backgroundColor: "#fff5f5", }, }} /> ); } ``` #### NepaliDatePicker with Full Customization ```tsx import { NepaliDatePicker, CalendarDay } from "nepali-calander"; import "nepali-calander/dist/styles.css"; function App() { const handleDateSelect = (date: CalendarDay) => { console.log("Selected date:", date); }; return ( <NepaliDatePicker type="input" year={2081} month={1} showToggle={true} calendarMode="bs" onDateSelect={handleDateSelect} className="my-datepicker" calendarWrapperClassName="custom-wrapper" headerClassName="custom-header" titleClassName="custom-title" navButtonClassName="custom-nav-button" weekdaysClassName="custom-weekdays" weekdayClassName="custom-weekday" calendarDaysClassName="custom-calendar-days" calendarDayClassName="custom-calendar-day" bsDateClassName="custom-bs-date" adDateClassName="custom-ad-date" toggleWrapperClassName="custom-toggle-wrapper" toggleButtonClassName="custom-toggle-button" prevArrow="◀" nextArrow="▶" weekdayLabels={["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]} styles={{ datepickerInput: { border: "2px solid #007bff", borderRadius: "8px", }, calendarWrapper: { maxWidth: "400px", backgroundColor: "#f8f9fa", }, header: { backgroundColor: "#e9ecef", }, title: { fontSize: "1.2rem", }, navButton: { fontSize: "1.1rem", }, weekday: { fontWeight: 600, }, saturday: { color: "#dc3545", backgroundColor: "#fff5f5", }, }} /> ); } ``` ## Props ### Shared Props (Both Components) | Prop | Type | Default | Description | | --------------------------- | -------------------- | ---------------- | ---------------------------------------------------------------------------------- | | `year` | `number` | Current BS year | Initial BS year to display | | `month` | `number` | Current BS month | Initial BS month to display (1-12) | | `showToggle` | `boolean` | `false` | Show toggle to switch between BS and AD calendars | | `calendarMode` | `"bs" \| "ad"` | `"bs"` | Default calendar mode (determines which dates to show) | | `holidayWeekdays` | `string \| string[]` | `"Saturday"` | Days to mark as holidays (e.g., `"Saturday"`, `"Sunday"`, `["Saturday","Sunday"]`) | | `events` | `CalendarEvent[]` | `[]` | Array of events to display on the calendar | | `showNationalHoliday` | `boolean` | `true` | Show national holidays from API data (day.events) | | `showNationalHolidayToggle` | `boolean` | `false` | Show toggle button to control national holiday visibility | ### NepaliDatePicker Specific Props | Prop | Type | Default | Description | | ------------- | ------------------- | --------------- | ------------------------------------------ | | `type` | `"icon" \| "input"` | `"input"` | Display type: icon button or input field | | `placeholder` | `string` | `"Select date"` | Placeholder text for input type datepicker | | `value` | `string` | - | Date in format "YYYY-MM-DD" (always AD format, regardless of calendarMode) | | `minDate` | `string` | - | Minimum selectable date in AD format (e.g., "2024-01-01"). Dates before this will be disabled | | `maxDate` | `string` | - | Maximum selectable date in AD format (e.g., "2024-12-31"). Dates after this will be disabled | | `disabled` | `boolean` | `false` | Disable the input field (native input disabled prop) | | `showArrows` | `boolean` | `true` | Show/hide navigation arrows (prev/next month buttons) | ### NepaliCalendar Specific Props | Prop | Type | Default | Description | | ------------------------- | ----------------------------- | ------- | --------------------------------------------------------------------------- | | `value` | `string` | - | Date in format "YYYY-MM-DD" (AD format by default, BS format when calendarMode="bs") | | `events` | `CalendarEvent[]` | `[]` | Array of custom events to display on calendar | | `eventVisibility` | `"default" \| "line"` | `"default"` | How to display events: "default" shows full bars, "line" shows thin lines | | `type` | `"default" \| "full"` | `"full"` | Display type: "default" shows minimal view, "full" shows event bars | | `isClickable` | `boolean` | `true` | Whether dates are clickable | | `showAllNationalHolidays` | `boolean` | `false` | If true, shows all events from API (including non-national holidays) when showNationalHoliday is true | ### Display Props | Prop | Type | Default | Description | | -------- | --------- | ------- | -------------------- | | `showAD` | `boolean` | `true` | Show Gregorian dates | | `showBS` | `boolean` | `true` | Show Nepali dates | ### Styling Props (ClassName) | Prop | Type | Default | Description | | -------------------------- | -------- | ------- | ---------------------------------- | | `className` | `string` | `""` | Additional CSS class for container | | `calendarWrapperClassName` | `string` | `""` | CSS class for calendar wrapper | | `headerClassName` | `string` | `""` | CSS class for header | | `titleClassName` | `string` | `""` | CSS class for title | | `navButtonClassName` | `string` | `""` | CSS class for navigation buttons | | `weekdaysClassName` | `string` | `""` | CSS class for weekdays container | | `weekdayClassName` | `string` | `""` | CSS class for individual weekday | | `calendarDaysClassName` | `string` | `""` | CSS class for calendar days grid | | `calendarDayClassName` | `string` | `""` | CSS class for individual day | | `bsDateClassName` | `string` | `""` | CSS class for BS date | | `adDateClassName` | `string` | `""` | CSS class for AD date | | `toggleWrapperClassName` | `string` | `""` | CSS class for toggle wrapper | | `toggleButtonClassName` | `string` | `""` | CSS class for toggle buttons | ### Styles Props (Inline Styles) ```typescript styles?: { calendarWrapper?: React.CSSProperties; header?: React.CSSProperties; title?: React.CSSProperties; navButton?: React.CSSProperties; navButtonDisabled?: React.CSSProperties; weekdays?: React.CSSProperties; weekday?: React.CSSProperties; weekdaySaturday?: React.CSSProperties; calendarDays?: React.CSSProperties; calendarDay?: React.CSSProperties; calendarDayHover?: React.CSSProperties; saturday?: React.CSSProperties; bsDate?: React.CSSProperties; adDate?: React.CSSProperties; toggleWrapper?: React.CSSProperties; toggleButton?: React.CSSProperties; toggleButtonActive?: React.CSSProperties; loadingSpinner?: React.CSSProperties; loadingText?: React.CSSProperties; } ``` ### Customization Props | Prop | Type | Default | Description | | --------------- | --------------------- | ------- | -------------------------- | | `prevArrow` | `string \| ReactNode` | `"←"` | Custom previous arrow icon | | `nextArrow` | `string \| ReactNode` | `"→"` | Custom next arrow icon | | `weekdayLabels` | `string[]` | - | Custom weekday labels | ### Callback Props | Prop | Type | Description | | ------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------- | | `onDateSelect` | `(date: CalendarDay) => void` | Called when a date is clicked. Receives the selected day with both BS and AD date information | | `onMonthChange` | `(year: number, month: number) => void` | Called when month/year changes. Returns year and month in the current calendar mode context | | `onCalendarModeChange` | `(mode: "bs" \| "ad") => void` | Called when calendar mode is toggled between BS and AD | | `onEventClick` | `(event: CalendarEvent) => void` | Called when an event bar is clicked. Receives the clicked event | | `onNationalHolidayToggleChange` | `(show: boolean) => void` | Called when national holiday toggle is changed. Receives the new visibility state | ## API Reference ### Date Conversion Functions The library provides date conversion utilities for converting between Gregorian (AD) and Bikram Sambat (BS) calendars: #### `adToBS(adYear, adMonth, adDay)` Converts a Gregorian date to Bikram Sambat date. ```typescript const bsDate = adToBS(2024, 12, 15); // Returns: { bs_y: 2081, bs_m: 9, bs_d: 30 } ``` #### `bsToAD(bsYear, bsMonth, bsDay)` Converts a Bikram Sambat date to Gregorian date. ```typescript const adDate = bsToAD(2081, 10, 15); // Returns: { ad_y: 2024, ad_m: 1, ad_d: 29 } ``` #### `getCurrentBSDate()` Gets the current date in Bikram Sambat calendar. ```typescript const currentBS = getCurrentBSDate(); // Returns: { bs_y: 2081, bs_m: 9, bs_d: 5 } ``` #### `bsYearToAD(bsYear)` / `adYearToBS(adYear)` Simple year conversion utilities. ```typescript const adYear = bsYearToAD(2081); // Returns: 2024 const bsYear = adYearToBS(2024); // Returns: 2081 ``` #### `getDaysInBSMonth(month, year?)` Returns the approximate number of days in a BS month (actual range is 29-32 days). ```typescript const days = getDaysInBSMonth(9, 2081); // Returns: 30 ``` **Note:** The conversion functions use lookup tables for accurate date conversion. The conversion algorithm: - Uses a comprehensive lookup table for BS years 1978-2099 - Calculates day differences from a base date (April 13, 1921 AD = Baishakh 1, 1978 BS) - Handles variable month lengths (29-32 days per month) in the Bikram Sambat calendar - For exact dates, the component also uses API data from a calendar service ### Types ```typescript interface CalendarEvent { id: string; title: string; description?: string; start: string; // YYYY-MM-DD format (AD) end: string; // YYYY-MM-DD format (AD) color?: string; isHoliday?: boolean; } interface CalendarDay { bs: { date: string; // Format: "YYYY-MM-DD" date_np: string; // Nepali unicode number bs_y: number; // BS year bs_m: number; // BS month (1-12) bs_d: number; // BS day }; ad: { date: string; // Format: "YYYY-MM-DD" date_np: string; // Nepali unicode number ad_y: number; // AD year ad_m: number; // AD month (1-12) ad_d: number; // AD day }; today: boolean; // True if this is today's date holiday: string | null; tithi: string | null; events?: Array<{ id: number; title: string; title_en: string; national_holiday: number; description: string | null; created_at: string; updated_at: string; pivot: { date_converter_id: number; event_id: number; }; }>; } interface BSDay { bs_y: number; // BS year bs_m: number; // BS month (1-12) bs_d: number; // BS day } interface ADDay { ad_y: number; // AD year ad_m: number; // AD month (1-12) ad_d: number; // AD day } interface CalendarData { days: CalendarDay[]; otherData: { total_days: number; starting_weekday: { name: string; number: string; }; ending_weekday: { name: string; number: string; }; next_month: number; next_year: number; prev_month: number; prev_year: number; }; } ``` ### Utilities ```typescript import { calendarDB, fetchCalendarData, adToBS, bsToAD, getCurrentBSDate, bsYearToAD, adYearToBS, getDaysInBSMonth, } from "nepali-calander"; // Initialize and use IndexedDB await calendarDB.init(); const data = await calendarDB.get(year, month); await calendarDB.set(year, month, data); // Fetch directly from API const data = await fetchCalendarData(year, month); // Date conversion utilities const bsDate = adToBS(2024, 12, 15); // Convert AD to BS const adDate = bsToAD(2081, 10, 1); // Convert BS to AD const currentBS = getCurrentBSDate(); // Get current BS date const adYear = bsYearToAD(2081); // Convert BS year to AD year const bsYear = adYearToBS(2024); // Convert AD year to BS year const daysInMonth = getDaysInBSMonth(10, 2081); // Get days in BS month ``` ## Styling Both components come with default styles but can be customized using component-specific CSS classes: ### NepaliCalendar CSS Classes ```css /* Calendar-specific styles */ .nepali-calendar { border: 2px solid #007bff; } .nepali-calendar-day { background-color: #f8f9fa; } .nepali-calendar-day.saturday { background-color: #fff5f5; color: #dc3545; } .nepali-calendar-toggle-wrapper { margin-bottom: 1rem; } .nepali-calendar-toggle-switch { background-color: #007bff; } ``` ### NepaliDatePicker CSS Classes ```css /* DatePicker-specific styles */ .nepali-datepicker-input { border: 2px solid #007bff; border-radius: 8px; } .nepali-datepicker-day { background-color: #f8f9fa; } .nepali-datepicker-day.saturday { background-color: #fff5f5; color: #dc3545; } .nepali-datepicker-toggle-wrapper { margin-bottom: 1rem; } .nepali-datepicker-toggle-switch { background-color: #007bff; } ``` ### Shared CSS Classes ```css /* Shared styles for both components */ .bs-date { font-weight: 600; } .ad-date { font-size: 0.8rem; } .ad-corner { opacity: 0.7; } ``` ## Contributing 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 ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## Author **Shirish Pokhrel** - GitLab: [@shirishpokhrel](https://git.techarttrekkies.com.np/shirishpokhrel) - Email: techart.shirish@groupiig.com ## Support If you find this project helpful, please give it a ⭐ on GitLab! For issues and feature requests, please use the [GitLab Issues](https://git.techarttrekkies.com.np/shirishpokhrel/calendar/issues) page.