nepali-calander
Version:
A beautiful and customizable Nepali Calendar component for React applications
840 lines (692 loc) • 26.9 kB
Markdown
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
```
```tsx
import { NepaliCalendar } from "nepali-calander";
import "nepali-calander/dist/styles.css"; // Import the CSS
function App() {
return (
<div>
<NepaliCalendar />
</div>
);
}
```
```tsx
import { NepaliCalendar } from "nepali-calander";
import "nepali-calander/dist/styles.css";
function App() {
return (
<div>
<NepaliCalendar
showToggle={true}
calendarMode="bs" // or "ad"
/>
</div>
);
}
```
```tsx
import { NepaliDatePicker } from "nepali-calander";
import "nepali-calander/dist/styles.css";
function App() {
return (
<div>
<NepaliDatePicker />
</div>
);
}
```
```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>
);
}
```
```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>
);
}
```
```tsx
import { NepaliDatePicker } from "nepali-calander";
import "nepali-calander/dist/styles.css";
function App() {
return (
<div>
<NepaliDatePicker type="icon" showToggle={true} calendarMode="ad" />
</div>
);
}
```
```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>
);
}
```
```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}
/>
);
}
```
```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}
/>
);
}
```
```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}
/>
);
}
```
```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}
/>
);
}
```
```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",
},
}}
/>
);
}
```
```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",
},
}}
/>
);
}
```
| 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;
}
```
| Prop | Type | Default | Description |
| --------------- | --------------------- | ------- | -------------------------- |
| `prevArrow` | `string \| ReactNode` | `"←"` | Custom previous arrow icon |
| `nextArrow` | `string \| ReactNode` | `"→"` | Custom next arrow icon |
| `weekdayLabels` | `string[]` | - | Custom weekday labels |
| 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 |
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 }
```
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 }
```
Gets the current date in Bikram Sambat calendar.
```typescript
const currentBS = getCurrentBSDate();
// Returns: { bs_y: 2081, bs_m: 9, bs_d: 5 }
```
Simple year conversion utilities.
```typescript
const adYear = bsYearToAD(2081); // Returns: 2024
const bsYear = adYearToBS(2024); // Returns: 2081
```
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;
};
}
```
```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
```
Both components come with default styles but can be customized using component-specific CSS classes:
```css
/* Calendar-specific styles */
.nepali-calendar {
border: 2px solid
}
.nepali-calendar-day {
background-color:
}
.nepali-calendar-day.saturday {
background-color:
color:
}
.nepali-calendar-toggle-wrapper {
margin-bottom: 1rem;
}
.nepali-calendar-toggle-switch {
background-color:
}
```
```css
/* DatePicker-specific styles */
.nepali-datepicker-input {
border: 2px solid
border-radius: 8px;
}
.nepali-datepicker-day {
background-color:
}
.nepali-datepicker-day.saturday {
background-color:
color:
}
.nepali-datepicker-toggle-wrapper {
margin-bottom: 1rem;
}
.nepali-datepicker-toggle-switch {
background-color:
}
```
```css
/* Shared styles for both components */
.bs-date {
font-weight: 600;
}
.ad-date {
font-size: 0.8rem;
}
.ad-corner {
opacity: 0.7;
}
```
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.