A lightweight, multi-calendar Angular date picker supporting Gregorian, Shamsi (Jalali / Persian), Imperial, and Hijri (Islamic) calendars — with a modern UI, inline year-grid picker, and an optional time picker.
- Four calendar systems: Gregorian · Shamsi (Jalali) · Imperial · Hijri (Islamic / Umm al-Qura)
- Year-grid picker: click the year to browse and select from a 12-year grid
- Month-grid picker: full month overlay
- Disable rules: past days, weekends, specific dates, or a custom list
- Date ranges & multi-select
- Optional time picker (hours · minutes · seconds)
- Configurable popup placement — six positions relative to the attached input
- Light, dark, and system themes controlled through an ancestor
data-themeattribute - RTL ready — Persian and Arabic month/weekday names built-in; Hijri uses Sunday-first week with Friday/Saturday weekends
- Keyboard-accessible popup controls with labeled navigation and time inputs
- Separate display vs. value format — show slashes to the user, store hyphens for APIs
- No jQuery — pure Angular + Bootstrap 5
- Signal-based state, Angular control-flow syntax (
@if,@for)
Interactive examples with all calendar types, time picker, date restrictions and calendar switcher.
| Peer dependency | Minimum | Notes |
|---|---|---|
@angular/core |
17.0.0 | |
@angular/common |
17.0.0 | |
@angular/forms |
17.0.0 | |
jalaali-js |
^2.0.1 |
Required for Shamsi & Imperial calendars |
moment-hijri |
^3.0.0 |
Required for Hijri calendar |
Angular 17 introduced signal inputs,
input<>(),computed(),effect(), and the@if/@forcontrol-flow syntax that this library depends on.
npm install ng-cyrus-calendar
# Shamsi (Jalali) and Imperial calendars
npm install jalaali-js@^2.0.1
# Hijri (Islamic) calendar
npm install moment-hijri@^3.0.0
# To support all calendar types
npm install jalaali-js@^2.0.1 moment-hijri@^3.0.0Note: In the current
1.1.xpackage, both calendar libraries are statically imported. Install both packages if your build reports a missing-module error, even when your app uses only one calendar type.
import { CalendarPopupComponent, CyrusCalendarDirective } from 'ng-cyrus-calendar';
@Component({
standalone: true,
imports: [CommonModule, FormsModule, CalendarPopupComponent, CyrusCalendarDirective],
})
export class MyComponent {}import { CalendarPopupComponent, CyrusCalendarDirective } from 'ng-cyrus-calendar';
@NgModule({
imports: [CommonModule, FormsModule, CalendarPopupComponent, CyrusCalendarDirective]
})
export class AppModule {}Add the cyrus-calendar directive to any <input> and place a <calendar-popup> next to it.
Export the directive as a template reference (#cal="cyrusCalendar") and pass it to the popup via [directive].
<input type="text" #myInput #cal="cyrusCalendar"
cyrus-calendar [calendar-type]="'shamsi'"
autocomplete="off" inputmode="none" />
<calendar-popup [input]="myInput" [directive]="cal" [(ngModel)]="selectedDate">
</calendar-popup><input type="text" #myInput #cal="cyrusCalendar"
cyrus-calendar [calendar-type]="'gregorian'"
autocomplete="off" inputmode="none" />
<calendar-popup [input]="myInput" [directive]="cal" [(ngModel)]="selectedDate">
</calendar-popup><input type="text" #myInput #cal="cyrusCalendar"
cyrus-calendar [calendar-type]="'imperial'"
autocomplete="off" inputmode="none" />
<calendar-popup [input]="myInput" [directive]="cal" [(ngModel)]="selectedDate">
</calendar-popup>Requires
moment-hijrito be installed.
<input type="text" #myInput #cal="cyrusCalendar"
cyrus-calendar [calendar-type]="'hijri'"
autocomplete="off" inputmode="none" />
<calendar-popup [input]="myInput" [directive]="cal" [(ngModel)]="selectedDate">
</calendar-popup><calendar-popup
[input]="myInput"
[directive]="cal"
[time]="true"
[(ngModel)]="selectedDateTime">
</calendar-popup>
<!-- Value emitted : 1403-06-15T14:30:00 -->
<!-- Input displays: 1403/06/15 - 14:30:00 -->The popup defaults to bottom-left. Choose top, top-left, top-right, bottom, bottom-left, or bottom-right; the bare top and bottom placements center the popup horizontally.
Theme is controlled with CSS, not a component input. Set data-theme on an ancestor of the popup to light, dark, or system. The system value follows the user's prefers-color-scheme setting.
<div data-theme="system">
<input type="text" #myInput #cal="cyrusCalendar"
cyrus-calendar [calendar-type]="'gregorian'"
autocomplete="off" inputmode="none" />
<calendar-popup
[input]="myInput"
[directive]="cal"
placement="top-right"
[(ngModel)]="selectedDate">
</calendar-popup>
</div>calendarType = signal<DatePickerType>(DatePickerType.Imperial);<input type="text" #myInput #cal="cyrusCalendar"
cyrus-calendar [calendar-type]="calendarType()"
autocomplete="off" inputmode="none" />
<calendar-popup [input]="myInput" [directive]="cal" [(ngModel)]="value">
</calendar-popup>
⚠️ The emitted value is always in the Gregorian calendar, regardless of the calendar type displayed. A Shamsi date1404/12/17and an Imperial date2584/12/17both emit2026-03-08. This makes the value safe to send directly to REST APIs and databases without any conversion.
Apply to <input>. Export as #ref="cyrusCalendar" to pass to <calendar-popup [directive]="ref">.
| Input | Type | Default | Description |
|---|---|---|---|
[calendar-type] |
DatePickerType |
'imperial' |
Active calendar system |
[disable-weekends] |
boolean |
false |
Disable weekend days |
[disable-past-days] |
boolean |
false |
Disable dates before today |
[placeholder] |
string |
auto | Custom placeholder text |
Implements ControlValueAccessor — works with ngModel and reactive forms.
| Input | Type | Default | Description |
|---|---|---|---|
[input] |
HTMLInputElement |
required | Template reference of the attached input |
[directive] |
CyrusCalendarDirective |
null |
Directive reference — inherits calendar-type and disable rules |
[calendar-type] |
DatePickerType |
'shamsi' |
Calendar system (ignored when [directive] provided) |
[placement] |
DatePickerPlacement |
'bottom-left' |
Popup placement: top, top-left, top-right, bottom, bottom-left, or bottom-right; bare top/bottom are centered |
[format] |
string |
'yyyy/MM/dd' |
Display format shown in the input |
[value-format] |
string |
'yyyy-MM-dd' |
Format of the emitted Gregorian date string |
[time] |
boolean |
false |
Show time picker panel |
[time-format] |
string |
'hh:mm:ss' |
Format for the time part |
[date] |
boolean |
true |
Show date picker panel |
[min] |
string |
null |
Minimum selectable date |
[max] |
string |
null |
Maximum selectable date |
[disable-past-days] |
boolean |
false |
Disable past dates |
[from-tomorow] |
boolean |
false |
Only allow tomorrow onwards |
[disable-weekends] |
boolean |
false |
Disable weekend days |
[multiple] |
boolean |
false |
Multi-date selection |
[range] |
boolean |
false |
Date-range selection |
[options] |
DatePickerOptions |
— | Advanced configuration object |
⚠️ The emitted value is always in the Gregorian calendar, regardless of the calendar type displayed. A Shamsi date1404/12/17and an Imperial date2584/12/17both emit2026-03-08.
| Token | Meaning | Example |
|---|---|---|
yyyy |
4-digit year | 1403, 2025 |
MM |
2-digit month (zero-padded) | 07 |
dd |
2-digit day (zero-padded) | 05 |
hh |
Hour 0–23 (zero-padded) | 14 |
mm |
Minute (zero-padded) | 30 |
ss |
Second (zero-padded) | 00 |
| System | Typical year | Layout | Week start | Weekends |
|---|---|---|---|---|
| Gregorian | 2025 | LTR | Monday | Sat + Sun |
| Shamsi (Jalali) | 1403 | RTL | Saturday | Friday |
| Imperial | 2584 | RTL | Saturday | Friday |
| Hijri (Islamic) | 1446 | RTL | Sunday | Fri + Sat |
git clone https://github.com/mhmfofa/cyrus-calendar.git
cd cyrus-calendar
npm install
npm start # dev server → http://localhost:4200
npm run build:lib # build the distributable library → dist/cyrus-calendar/# 1. Build the library
npm run build:lib
# 2. Publish from the dist folder (NOT the root)
cd dist/cyrus-calendar
npm publish --access publicSee NPM-PUBLISH.md for the full publishing guide.
See CHANGELOG.md for the full version history.
MIT © mhmfofa
| [disable-past-days] | boolean | false | Disable past dates |
| [from-tomorow] | boolean | false | Only allow tomorrow onwards |
| [disable-weekends] | boolean | false | Disable weekend days |
| [multiple] | boolean | false | Multi-date selection |
| [range] | boolean | false | Date-range selection |
| [options] | DatePickerOptions | — | Advanced configuration object |
| Token | Meaning | Example |
|---|---|---|
yyyy |
4-digit year | 1403, 2025 |
MM |
2-digit month (zero-padded) | 07 |
dd |
2-digit day (zero-padded) | 05 |
hh |
Hour 0–23 (zero-padded) | 14 |
mm |
Minute (zero-padded) | 30 |
ss |
Second (zero-padded) | 00 |
| System | Typical year | Layout | Week start | Weekends |
|---|---|---|---|---|
| Gregorian | 2025 | LTR | Monday | Sat + Sun |
| Shamsi (Jalali) | 1403 | RTL | Saturday | Friday |
| Imperial | 2584 | RTL | Saturday | Friday |
| Hijri (Islamic) | 1446 | RTL | Sunday | Fri + Sat |
git clone https://github.com/mhmfofa/cyrus-calendar.git
cd cyrus-calendar
npm install
npm start # dev server → http://localhost:4200
npm run build:lib # build the distributable library → dist/cyrus-calendar/# 1. Build the library
npm run build:lib
# 2. Publish from the dist folder (NOT the root)
cd dist/cyrus-calendar
npm publish --access publicSee NPM-PUBLISH.md for the full publishing guide.
See CHANGELOG.md for the full version history.
MIT © mhmfofa