ng-cyrus-calendar

May 16, 2026 · View on GitHub

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.


Features

  • 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)
  • RTL ready — Persian and Arabic month/weekday names built-in; Hijri uses Sunday-first week with Friday/Saturday weekends
  • 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)

Live Demo

http://calendar.molayemi.com

Interactive examples with all calendar types, time picker, date restrictions and calendar switcher.


Requirements

Peer dependencyMinimumNotes
@angular/core17.0.0
@angular/common17.0.0
@angular/forms17.0.0
jalaali-js2.0.0Required for Shamsi & Imperial calendars
moment-hijri3.0.0Required for Hijri calendar only

Angular 17 introduced signal inputs, input<>(), computed(), effect(), and the @if/@for control-flow syntax that this library depends on.


Installation

npm install ng-cyrus-calendar jalaali-js
# For Hijri calendar support, also install:
npm install moment-hijri

Setup

Standalone component

import { CalendarPopupComponent, CyrusCalendarDirective } from 'ng-cyrus-calendar';

@Component({
  standalone: true,
  imports: [CommonModule, FormsModule, CalendarPopupComponent, CyrusCalendarDirective],
})
export class MyComponent {}

NgModule-based app

import { CalendarPopupComponent, CyrusCalendarDirective } from 'ng-cyrus-calendar';

@NgModule({
  imports: [CommonModule, FormsModule, CalendarPopupComponent, CyrusCalendarDirective]
})
export class AppModule {}

Usage

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].

Shamsi (Jalali / Persian)

<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>

Gregorian

<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>

Imperial

<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>

Hijri (Islamic)

Requires moment-hijri to 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>

With time picker

<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 -->

Calendar switcher (value preserved on type change)

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 date 1404/12/17 and an Imperial date 2584/12/17 both emit 2026-03-08. This makes the value safe to send directly to REST APIs and databases without any conversion.


API Reference

Directive: cyrus-calendar

Apply to <input>. Export as #ref="cyrusCalendar" to pass to <calendar-popup [directive]="ref">.

InputTypeDefaultDescription
[calendar-type]DatePickerType'imperial'Active calendar system
[disable-weekends]booleanfalseDisable weekend days
[disable-past-days]booleanfalseDisable dates before today
[placeholder]stringautoCustom placeholder text

Component: <calendar-popup>

Implements ControlValueAccessor — works with ngModel and reactive forms.

InputTypeDefaultDescription
[input]HTMLInputElementrequiredTemplate reference of the attached input
[directive]CyrusCalendarDirectivenullDirective reference — inherits calendar-type and disable rules
[calendar-type]DatePickerType'shamsi'Calendar system (ignored when [directive] provided)
[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]booleanfalseShow time picker panel
[time-format]string'hh:mm:ss'Format for the time part
[date]booleantrueShow date picker panel
[min]stringnullMinimum selectable date
[max]stringnullMaximum selectable date
[disable-past-days]booleanfalseDisable past dates
[from-tomorow]booleanfalseOnly allow tomorrow onwards
[disable-weekends]booleanfalseDisable weekend days
[multiple]booleanfalseMulti-date selection
[range]booleanfalseDate-range selection
[options]DatePickerOptionsAdvanced configuration object

⚠️ The emitted value is always in the Gregorian calendar, regardless of the calendar type displayed. A Shamsi date 1404/12/17 and an Imperial date 2584/12/17 both emit 2026-03-08.


Format Tokens

TokenMeaningExample
yyyy4-digit year1403, 2025
MM2-digit month (zero-padded)07
dd2-digit day (zero-padded)05
hhHour 0–23 (zero-padded)14
mmMinute (zero-padded)30
ssSecond (zero-padded)00

Calendar System Notes

SystemTypical yearLayoutWeek startWeekends
Gregorian2025LTRMondaySat + Sun
Shamsi (Jalali)1403RTLSaturdayFriday
Imperial2584RTLSaturdayFriday
Hijri (Islamic)1446RTLSundayFri + Sat

Development

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/

Publishing to npm

# 1. Build the library
npm run build:lib

# 2. Publish from the dist folder (NOT the root)
cd dist/cyrus-calendar
npm publish --access public

See NPM-PUBLISH.md for the full publishing guide.


Changelog

See CHANGELOG.md for the full version history.


License

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 |


Format Tokens

TokenMeaningExample
yyyy4-digit year1403, 2025
MM2-digit month (zero-padded)07
dd2-digit day (zero-padded)05
hhHour 0–23 (zero-padded)14
mmMinute (zero-padded)30
ssSecond (zero-padded)00

Calendar System Notes

SystemTypical yearLayoutWeek startWeekends
Gregorian2025LTRMondaySat + Sun
Shamsi (Jalali)1403RTLSaturdayFriday
Imperial2584RTLSaturdayFriday
Hijri (Islamic)1446RTLSundayFri + Sat

Development

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/

Publishing to npm

# 1. Build the library
npm run build:lib

# 2. Publish from the dist folder (NOT the root)
cd dist/cyrus-calendar
npm publish --access public

See NPM-PUBLISH.md for the full publishing guide.


Changelog

See CHANGELOG.md for the full version history.


License

MIT © mhmfofa