@webilix/ngx-calendar-m3
Version:
Jalali calendar components for Angular and Material 3
613 lines (607 loc) • 98.1 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, Output, Input, HostBinding, Component, inject, Injectable } from '@angular/core';
import { trigger, transition, style, animate } from '@angular/animations';
import { MatIconButton, MatButton } from '@angular/material/button';
import { MatIcon } from '@angular/material/icon';
import { JalaliDateTime } from '@webilix/jalali-date-time';
import * as i1 from '@angular/material/menu';
import { MatMenuModule } from '@angular/material/menu';
import * as i1$1 from '@webilix/ngx-helper-m3';
import { NgxHelperDatePipe, NGX_HELPER_CONTAINER_DATA, NGX_HELPER_CONTAINER_CLOSE } from '@webilix/ngx-helper-m3';
import { Helper } from '@webilix/helper-library';
import * as i1$2 from '@angular/router';
class NgxCalendarDateComponent {
className = 'ngx-calendar-m3-date';
value;
minDate;
maxDate;
onChange = new EventEmitter();
view = 'CALENDAR';
values;
calendar;
year;
years = [];
seasons = [
[
{ title: 'فروردین', month: '' },
{ title: 'اردیبهشت', month: '' },
{ title: 'خرداد', month: '' },
],
[
{ title: 'تیر', month: '' },
{ title: 'مرداد', month: '' },
{ title: 'شهریور', month: '' },
],
[
{ title: 'مهر', month: '' },
{ title: 'آبان', month: '' },
{ title: 'آذر', month: '' },
],
[
{ title: 'دی', month: '' },
{ title: 'بهمن', month: '' },
{ title: 'اسفند', month: '' },
],
];
jalali = JalaliDateTime();
ngOnInit() {
this.initValues();
}
ngOnChanges(changes) {
this.initValues();
}
formatDate(date) {
return this.jalali.toString(date, { format: 'Y-M-D' });
}
initValues() {
let minDate = this.minDate === 'NOW' ? new Date() : this.minDate;
let maxDate = this.maxDate === 'NOW' ? new Date() : this.maxDate;
// Check MIN and MAX Dates
if (minDate && maxDate && minDate.getTime() > maxDate.getTime()) {
const date = new Date(minDate);
minDate = maxDate;
maxDate = date;
}
// Check Value
if (this.value && minDate && this.formatDate(this.value) < this.formatDate(minDate))
this.value = undefined;
if (this.value && maxDate && this.formatDate(this.value) > this.formatDate(maxDate))
this.value = undefined;
this.values = {
today: this.formatDate(new Date()),
selected: this.value ? this.formatDate(this.value) : '',
minDate: minDate ? this.formatDate(minDate) : '0000-00-00',
maxDate: maxDate ? this.formatDate(maxDate) : '9999-99-99',
};
const month = this.jalali.toString(this.value || new Date(), { format: 'Y-M' });
this.calendar = this.jalali.calendar(month);
this.view = 'CALENDAR';
}
changeMonth(change) {
let [year, month] = this.calendar.month.split('-').map((v) => +v);
switch (change) {
case 12:
case -12:
year += change === 12 ? 1 : -1;
break;
case 1:
case -1:
month += change;
if (month === 13) {
year++;
month = 1;
}
if (month === 0) {
year--;
month = 12;
}
break;
case 0:
[year, month] = this.values.today.split('-').map((v) => +v);
break;
}
this.calendar = this.jalali.calendar(year.toString() + '-' + month.toString().padStart(2, '0'));
}
setDate(value) {
const gregorian = this.jalali.gregorian(value).date;
const date = this.jalali.periodDay(1, new Date(gregorian + 'T00:00:00.000Z')).from;
const title = this.jalali.toFullText(date, { format: 'W، d N Y' });
const jalali = this.formatDate(date);
this.values.selected = value;
this.onChange.next({ date, title, jalali });
}
toggleView() {
this.view = this.view === 'CALENDAR' ? 'MONTH' : 'CALENDAR';
if (this.view === 'MONTH')
this.changeYear(+this.calendar.month.substring(0, 4));
}
changeYear(year) {
if (year && year < 1000)
return;
this.year = year || +this.values.today.substring(0, 4);
let decade = this.year - (this.year % 10);
if (decade <= 1020)
decade = 1020;
this.years = [decade - 20, decade - 10, decade, decade + 10, decade + 20];
this.seasons.forEach((season, s) => {
season.forEach((month, m) => {
month.month = `${this.year.toString()}-${(s * 3 + m + 1).toString().padStart(2, '0')}`;
});
});
}
setMonth(month) {
this.calendar = this.jalali.calendar(month);
this.view = 'CALENDAR';
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: NgxCalendarDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: NgxCalendarDateComponent, isStandalone: true, selector: "ngx-calendar-date", inputs: { value: "value", minDate: "minDate", maxDate: "maxDate" }, outputs: { onChange: "onChange" }, host: { properties: { "className": "this.className" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"calendar-header\">\n <button mat-icon-button type=\"button\" (click)=\"toggleView()\">\n <mat-icon>{{ view === 'CALENDAR' ? 'calendar_month' : 'close' }}</mat-icon>\n </button>\n <div class=\"title\">{{ view === 'CALENDAR' ? calendar.title : year }}</div>\n\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(-12) : changeYear(year - 10)\">\n <mat-icon>keyboard_double_arrow_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(-1) : changeYear(year - 1)\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(0) : changeYear(year)\">\n <mat-icon>radio_button_checked</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(1) : changeYear(year + 1)\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(12) : changeYear(year + 10)\">\n <mat-icon>keyboard_double_arrow_left</mat-icon>\n </button>\n</div>\n\n<div class=\"calendar-nav\">\n @switch (view) {\n <!-- CALENDAR -->\n @case ('CALENDAR') {\n <!-- NAMES -->\n @for (day of ['\u0634', '\u06CC', '\u062F', '\u0633', '\u0686', '\u067E', '\u062C']; track $index) {\n <div class=\"item\">{{ day }}</div>\n } } @case ('MONTH') {\n <!-- YEARS -->\n @for (item of years; track $index) {\n <div class=\"item click\" (click)=\"changeYear(item)\">{{ item }}</div>\n } } }\n</div>\n\n<div class=\"calendar-content\">\n <!-- CALENDAR VIEW -->\n <section class=\"calendar-view\" [style.visibility]=\"view === 'CALENDAR' ? 'visible' : 'hidden'\">\n <!-- WEEK -->\n @for ( week of calendar.weeks; track $index) {\n <div class=\"week\">\n <!-- DAYS -->\n @for (day of week; track $index) {\n <div\n class=\"day\"\n [class.day-today]=\"day.date === values.today\"\n [class.day-selected]=\"day.date === values.selected\"\n [class.day-disable]=\"day.month !== calendar.month || day.date > values.maxDate || day.date < values.minDate\"\n (click)=\"\n day.month !== calendar.month || day.date > values.maxDate || day.date < values.minDate\n ? null\n : setDate(day.date)\n \"\n >\n {{ day.day }}\n </div>\n }\n </div>\n }\n </section>\n\n <!-- MONTH VIEW -->\n @if (view === 'MONTH') {\n <section class=\"month-view\" [@month]>\n <!-- SEASON -->\n @for (season of seasons; track $index) {\n <div class=\"season\">\n <!-- MONTH -->\n @for (month of season; track $index) {\n <div\n class=\"month\"\n [class.month-disable]=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n \"\n (click)=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n ? null\n : setMonth(month.month)\n \"\n >\n {{ month.title }}\n </div>\n }\n </div>\n }\n </section>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], animations: [
trigger('month', [
transition(':enter', [style({ height: '*', opacity: 0 }), animate('150ms', style({ height: '*', opacity: 1 }))]),
]),
] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: NgxCalendarDateComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-calendar-date', imports: [MatIconButton, MatIcon], animations: [
trigger('month', [
transition(':enter', [style({ height: '*', opacity: 0 }), animate('150ms', style({ height: '*', opacity: 1 }))]),
]),
], template: "<div class=\"calendar-header\">\n <button mat-icon-button type=\"button\" (click)=\"toggleView()\">\n <mat-icon>{{ view === 'CALENDAR' ? 'calendar_month' : 'close' }}</mat-icon>\n </button>\n <div class=\"title\">{{ view === 'CALENDAR' ? calendar.title : year }}</div>\n\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(-12) : changeYear(year - 10)\">\n <mat-icon>keyboard_double_arrow_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(-1) : changeYear(year - 1)\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(0) : changeYear(year)\">\n <mat-icon>radio_button_checked</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(1) : changeYear(year + 1)\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(12) : changeYear(year + 10)\">\n <mat-icon>keyboard_double_arrow_left</mat-icon>\n </button>\n</div>\n\n<div class=\"calendar-nav\">\n @switch (view) {\n <!-- CALENDAR -->\n @case ('CALENDAR') {\n <!-- NAMES -->\n @for (day of ['\u0634', '\u06CC', '\u062F', '\u0633', '\u0686', '\u067E', '\u062C']; track $index) {\n <div class=\"item\">{{ day }}</div>\n } } @case ('MONTH') {\n <!-- YEARS -->\n @for (item of years; track $index) {\n <div class=\"item click\" (click)=\"changeYear(item)\">{{ item }}</div>\n } } }\n</div>\n\n<div class=\"calendar-content\">\n <!-- CALENDAR VIEW -->\n <section class=\"calendar-view\" [style.visibility]=\"view === 'CALENDAR' ? 'visible' : 'hidden'\">\n <!-- WEEK -->\n @for ( week of calendar.weeks; track $index) {\n <div class=\"week\">\n <!-- DAYS -->\n @for (day of week; track $index) {\n <div\n class=\"day\"\n [class.day-today]=\"day.date === values.today\"\n [class.day-selected]=\"day.date === values.selected\"\n [class.day-disable]=\"day.month !== calendar.month || day.date > values.maxDate || day.date < values.minDate\"\n (click)=\"\n day.month !== calendar.month || day.date > values.maxDate || day.date < values.minDate\n ? null\n : setDate(day.date)\n \"\n >\n {{ day.day }}\n </div>\n }\n </div>\n }\n </section>\n\n <!-- MONTH VIEW -->\n @if (view === 'MONTH') {\n <section class=\"month-view\" [@month]>\n <!-- SEASON -->\n @for (season of seasons; track $index) {\n <div class=\"season\">\n <!-- MONTH -->\n @for (month of season; track $index) {\n <div\n class=\"month\"\n [class.month-disable]=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n \"\n (click)=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n ? null\n : setMonth(month.month)\n \"\n >\n {{ month.title }}\n </div>\n }\n </div>\n }\n </section>\n }\n</div>\n" }]
}], propDecorators: { className: [{
type: HostBinding,
args: ['className']
}], value: [{
type: Input,
args: [{ required: false }]
}], minDate: [{
type: Input,
args: [{ required: false }]
}], maxDate: [{
type: Input,
args: [{ required: false }]
}], onChange: [{
type: Output
}] } });
class NgxCalendarMomentComponent {
className = 'ngx-calendar-m3-moment';
value;
minDate;
maxDate;
onChange = new EventEmitter();
canSubmit = false;
dateString;
hour;
hours = [...Array(24).keys()].map((hour) => hour.toString().padStart(2, '0'));
minute;
minutes = [...Array(60).keys()].map((minute) => minute.toString().padStart(2, '0'));
jalali = JalaliDateTime();
ngOnInit() {
this.initValues();
}
ngOnChanges(changes) {
this.initValues();
}
formatDate(date) {
return this.jalali.toString(date, { format: 'Y-M-D H:I' });
}
getMinMax() {
const minDate = this.minDate === 'NOW' ? new Date() : this.minDate;
const maxDate = this.maxDate === 'NOW' ? new Date() : this.maxDate;
return { minDate, maxDate };
}
initValues() {
const updateDate = (date) => {
date.setSeconds(0);
date.setMilliseconds(0);
};
let { minDate, maxDate } = this.getMinMax();
if (this.value)
updateDate(this.value);
if (minDate)
updateDate(minDate);
if (maxDate)
updateDate(maxDate);
// Check MIN and MAX Dates
if (minDate && maxDate && minDate.getTime() > maxDate.getTime()) {
const date = new Date(minDate);
minDate = maxDate;
maxDate = date;
}
// Check Value
if (this.value && minDate && this.formatDate(this.value) < this.formatDate(minDate))
this.value = undefined;
if (this.value && maxDate && this.formatDate(this.value) > this.formatDate(maxDate))
this.value = undefined;
this.hour = (this.value || new Date()).getHours().toString().padStart(2, '0');
this.minute = (this.value || new Date()).getMinutes().toString().padStart(2, '0');
this.updateValue();
}
updateValue() {
this.canSubmit = false;
if (!this.value)
return;
const { minDate, maxDate } = this.getMinMax();
this.value.setHours(+this.hour);
this.value.setMinutes(+this.minute);
this.value.setSeconds(0);
this.value.setMilliseconds(0);
if (minDate && this.formatDate(this.value) < this.formatDate(minDate)) {
this.value.setHours(minDate.getHours());
this.value.setMinutes(minDate.getMinutes());
}
if (maxDate && this.formatDate(this.value) > this.formatDate(maxDate)) {
this.value.setHours(maxDate.getHours());
this.value.setMinutes(maxDate.getMinutes());
}
this.hour = this.value.getHours().toString().padStart(2, '0');
this.minute = this.value.getMinutes().toString().padStart(2, '0');
this.canSubmit = true;
}
setDate(date) {
this.value = date.date;
this.updateValue();
}
checkHour(hour) {
const { minDate, maxDate } = this.getMinMax();
if (!this.value)
return false;
if (!minDate && !maxDate)
return true;
const check = this.jalali.toString(this.value, { format: `Y-M-D ${hour}` });
if (minDate && check < this.formatDate(minDate).substring(0, 13))
return false;
if (maxDate && check > this.formatDate(maxDate).substring(0, 13))
return false;
return true;
}
setHour(hour) {
this.hour = hour;
this.updateValue();
}
checkMinute(minute) {
const { minDate, maxDate } = this.getMinMax();
if (!this.value)
return false;
if (!minDate && !maxDate)
return true;
const check = this.jalali.toString(this.value, { format: `Y-M-D H:${minute}` });
if (minDate && check < this.formatDate(minDate))
return false;
if (maxDate && check > this.formatDate(maxDate))
return false;
return true;
}
setMinute(minute) {
this.minute = minute;
this.updateValue();
}
onSubmit() {
if (!this.value || !this.canSubmit)
return;
const moment = new Date(this.value.getTime());
const title = this.jalali.toFullText(moment, { format: 'W، d N Y H:I' });
const jalali = this.formatDate(moment);
this.onChange.next({ moment, title, jalali });
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: NgxCalendarMomentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: NgxCalendarMomentComponent, isStandalone: true, selector: "ngx-calendar-moment", inputs: { value: "value", minDate: "minDate", maxDate: "maxDate" }, outputs: { onChange: "onChange" }, host: { properties: { "className": "this.className" } }, usesOnChanges: true, ngImport: i0, template: "<ngx-calendar-date [value]=\"value\" [minDate]=\"minDate\" [maxDate]=\"maxDate\" (onChange)=\"setDate($event)\"></ngx-calendar-date>\n<div class=\"time\">\n <div class=\"date\">{{ value | ngxHelperDate }}</div>\n\n @if (value) {\n <button mat-button type=\"button\" class=\"value\" [matMenuTriggerFor]=\"minuteMenu\" [disabled]=\"!value\">{{ minute }}</button>\n <mat-menu #minuteMenu=\"matMenu\" [yPosition]=\"'above'\" class=\"ngx-calendar-m3-moment-minute\">\n @for (minute of minutes; track $index) {\n <button mat-menu-item (click)=\"setMinute(minute)\" [disabled]=\"!checkMinute(minute)\">\n <span class=\"minute\">{{ minute }}</span>\n </button>\n }\n </mat-menu>\n\n <div class=\"colon\" [style.opacity]=\"value ? 1 : 0.5\">:</div>\n\n <button mat-button type=\"button\" class=\"value\" [matMenuTriggerFor]=\"hourMenu\" [disabled]=\"!value\">{{ hour }}</button>\n <mat-menu #hourMenu=\"matMenu\" [yPosition]=\"'above'\" class=\"ngx-calendar-m3-moment-hour\">\n @for (hour of hours; track $index) {\n <button mat-menu-item (click)=\"setHour(hour)\" [disabled]=\"!checkHour(hour)\">\n <span class=\"hour\">{{ hour }}</span>\n </button>\n }\n </mat-menu>\n }\n\n <button mat-button type=\"button\" class=\"submit\" (click)=\"onSubmit()\" [disabled]=\"!canSubmit\">\u062A\u0627\u06CC\u06CC\u062F</button>\n</div>\n", styles: ["::ng-deep .ngx-calendar-m3-moment-minute{max-width:fit-content!important;direction:ltr}::ng-deep .ngx-calendar-m3-moment-minute .mat-mdc-menu-content{padding:0;display:grid;grid-template-columns:1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;width:fit-content!important}::ng-deep .ngx-calendar-m3-moment-minute .mat-mdc-menu-content button{padding:.75rem .5rem;min-width:auto;min-height:auto;border-radius:0}::ng-deep .ngx-calendar-m3-moment-minute .mat-mdc-menu-content button span.minute{font-size:90%}::ng-deep .ngx-calendar-m3-moment-hour{max-width:fit-content!important;direction:ltr}::ng-deep .ngx-calendar-m3-moment-hour .mat-mdc-menu-content{padding:0;display:grid;grid-template-columns:1fr 1fr 1fr 1fr 1fr 1fr;width:fit-content!important}::ng-deep .ngx-calendar-m3-moment-hour .mat-mdc-menu-content button{padding:.75rem;min-width:auto;min-height:auto;border-radius:0}::ng-deep .ngx-calendar-m3-moment-hour .mat-mdc-menu-content button span.hour{font-size:90%}\n"], dependencies: [{ kind: "component", type: MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: NgxCalendarDateComponent, selector: "ngx-calendar-date", inputs: ["value", "minDate", "maxDate"], outputs: ["onChange"] }, { kind: "pipe", type: NgxHelperDatePipe, name: "ngxHelperDate" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: NgxCalendarMomentComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-calendar-moment', imports: [MatButton, MatMenuModule, NgxHelperDatePipe, NgxCalendarDateComponent], template: "<ngx-calendar-date [value]=\"value\" [minDate]=\"minDate\" [maxDate]=\"maxDate\" (onChange)=\"setDate($event)\"></ngx-calendar-date>\n<div class=\"time\">\n <div class=\"date\">{{ value | ngxHelperDate }}</div>\n\n @if (value) {\n <button mat-button type=\"button\" class=\"value\" [matMenuTriggerFor]=\"minuteMenu\" [disabled]=\"!value\">{{ minute }}</button>\n <mat-menu #minuteMenu=\"matMenu\" [yPosition]=\"'above'\" class=\"ngx-calendar-m3-moment-minute\">\n @for (minute of minutes; track $index) {\n <button mat-menu-item (click)=\"setMinute(minute)\" [disabled]=\"!checkMinute(minute)\">\n <span class=\"minute\">{{ minute }}</span>\n </button>\n }\n </mat-menu>\n\n <div class=\"colon\" [style.opacity]=\"value ? 1 : 0.5\">:</div>\n\n <button mat-button type=\"button\" class=\"value\" [matMenuTriggerFor]=\"hourMenu\" [disabled]=\"!value\">{{ hour }}</button>\n <mat-menu #hourMenu=\"matMenu\" [yPosition]=\"'above'\" class=\"ngx-calendar-m3-moment-hour\">\n @for (hour of hours; track $index) {\n <button mat-menu-item (click)=\"setHour(hour)\" [disabled]=\"!checkHour(hour)\">\n <span class=\"hour\">{{ hour }}</span>\n </button>\n }\n </mat-menu>\n }\n\n <button mat-button type=\"button\" class=\"submit\" (click)=\"onSubmit()\" [disabled]=\"!canSubmit\">\u062A\u0627\u06CC\u06CC\u062F</button>\n</div>\n", styles: ["::ng-deep .ngx-calendar-m3-moment-minute{max-width:fit-content!important;direction:ltr}::ng-deep .ngx-calendar-m3-moment-minute .mat-mdc-menu-content{padding:0;display:grid;grid-template-columns:1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;width:fit-content!important}::ng-deep .ngx-calendar-m3-moment-minute .mat-mdc-menu-content button{padding:.75rem .5rem;min-width:auto;min-height:auto;border-radius:0}::ng-deep .ngx-calendar-m3-moment-minute .mat-mdc-menu-content button span.minute{font-size:90%}::ng-deep .ngx-calendar-m3-moment-hour{max-width:fit-content!important;direction:ltr}::ng-deep .ngx-calendar-m3-moment-hour .mat-mdc-menu-content{padding:0;display:grid;grid-template-columns:1fr 1fr 1fr 1fr 1fr 1fr;width:fit-content!important}::ng-deep .ngx-calendar-m3-moment-hour .mat-mdc-menu-content button{padding:.75rem;min-width:auto;min-height:auto;border-radius:0}::ng-deep .ngx-calendar-m3-moment-hour .mat-mdc-menu-content button span.hour{font-size:90%}\n"] }]
}], propDecorators: { className: [{
type: HostBinding,
args: ['className']
}], value: [{
type: Input,
args: [{ required: false }]
}], minDate: [{
type: Input,
args: [{ required: false }]
}], maxDate: [{
type: Input,
args: [{ required: false }]
}], onChange: [{
type: Output
}] } });
class NgxCalendarWeekComponent {
className = 'ngx-calendar-m3-week';
value;
minDate;
maxDate;
onChange = new EventEmitter();
view = 'CALENDAR';
values;
calendar;
year;
years = [];
seasons = [
[
{ title: 'فروردین', month: '' },
{ title: 'اردیبهشت', month: '' },
{ title: 'خرداد', month: '' },
],
[
{ title: 'تیر', month: '' },
{ title: 'مرداد', month: '' },
{ title: 'شهریور', month: '' },
],
[
{ title: 'مهر', month: '' },
{ title: 'آبان', month: '' },
{ title: 'آذر', month: '' },
],
[
{ title: 'دی', month: '' },
{ title: 'بهمن', month: '' },
{ title: 'اسفند', month: '' },
],
];
jalali = JalaliDateTime();
ngOnInit() {
this.initValues();
}
ngOnChanges(changes) {
this.initValues();
}
formatDate(date) {
return this.jalali.toString(date, { format: 'Y-M-D' });
}
initValues() {
let minDate = this.minDate === 'NOW' ? new Date() : this.minDate;
let maxDate = this.maxDate === 'NOW' ? new Date() : this.maxDate;
const getWeek = (date, period) => {
const week = this.jalali.periodWeek(1, date);
return this.formatDate(period === 'from' ? week.from : week.to);
};
// Check MIN and MAX Dates
if (minDate && maxDate && minDate.getTime() > maxDate.getTime()) {
const date = new Date(minDate);
minDate = maxDate;
maxDate = date;
}
// Check Value
let value = this.value ? ('from' in this.value ? this.value.from : this.value) : undefined;
if (value && minDate && this.formatDate(value) < getWeek(minDate, 'from'))
value = undefined;
if (value && maxDate && this.formatDate(value) > getWeek(maxDate, 'to'))
value = undefined;
this.values = {
today: this.formatDate(new Date()),
selected: value ? getWeek(value, 'from') : '',
minDate: minDate ? getWeek(minDate, 'from') : '0000-00-00',
maxDate: maxDate ? getWeek(maxDate, 'to') : '9999-99-99',
};
const month = this.jalali.toString(value || new Date(), { format: 'Y-M' });
this.calendar = this.jalali.calendar(month);
this.view = 'CALENDAR';
}
changeMonth(change) {
let [year, month] = this.calendar.month.split('-').map((v) => +v);
switch (change) {
case 12:
case -12:
year += change === 12 ? 1 : -1;
break;
case 1:
case -1:
month += change;
if (month === 13) {
year++;
month = 1;
}
if (month === 0) {
year--;
month = 12;
}
break;
case 0:
[year, month] = this.values.today.split('-').map((v) => +v);
break;
}
this.calendar = this.jalali.calendar(year.toString() + '-' + month.toString().padStart(2, '0'));
}
setDate(value) {
const gregorian = this.jalali.gregorian(value).date;
const date = this.jalali.periodDay(1, new Date(gregorian + 'T00:00:00.000Z')).from;
const period = this.jalali.periodWeek(1, date);
const title = Helper.DATE.jalaliPeriod(period.from, period.to);
this.values.selected = this.formatDate(period.from);
this.onChange.next({ period, title });
}
toggleView() {
this.view = this.view === 'CALENDAR' ? 'MONTH' : 'CALENDAR';
if (this.view === 'MONTH')
this.changeYear(+this.calendar.month.substring(0, 4));
}
changeYear(year) {
if (year && year < 1000)
return;
this.year = year || +this.values.today.substring(0, 4);
let decade = this.year - (this.year % 10);
if (decade <= 1020)
decade = 1020;
this.years = [decade - 20, decade - 10, decade, decade + 10, decade + 20];
this.seasons.forEach((season, s) => {
season.forEach((month, m) => {
month.month = `${this.year.toString()}-${(s * 3 + m + 1).toString().padStart(2, '0')}`;
});
});
}
setMonth(month) {
this.calendar = this.jalali.calendar(month);
this.view = 'CALENDAR';
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: NgxCalendarWeekComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: NgxCalendarWeekComponent, isStandalone: true, selector: "ngx-calendar-week", inputs: { value: "value", minDate: "minDate", maxDate: "maxDate" }, outputs: { onChange: "onChange" }, host: { properties: { "className": "this.className" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"calendar-header\">\n <button mat-icon-button type=\"button\" (click)=\"toggleView()\">\n <mat-icon>{{ view === 'CALENDAR' ? 'calendar_month' : 'close' }}</mat-icon>\n </button>\n <div class=\"title\">{{ view === 'CALENDAR' ? calendar.title : year }}</div>\n\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(-12) : changeYear(year - 10)\">\n <mat-icon>keyboard_double_arrow_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(-1) : changeYear(year - 1)\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(0) : changeYear(year)\">\n <mat-icon>radio_button_checked</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(1) : changeYear(year + 1)\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(12) : changeYear(year + 10)\">\n <mat-icon>keyboard_double_arrow_left</mat-icon>\n </button>\n</div>\n\n<div class=\"calendar-nav\">\n @switch (view) {\n <!-- CALENDAR -->\n @case ('CALENDAR') {\n <!-- NAMES -->\n @for (day of ['\u0634', '\u06CC', '\u062F', '\u0633', '\u0686', '\u067E', '\u062C']; track $index) {\n <div class=\"item\">{{ day }}</div>\n } } @case ('MONTH') {\n <!-- YEARS -->\n @for (item of years; track $index) {\n <div class=\"item click\" (click)=\"changeYear(item)\">{{ item }}</div>\n } } }\n</div>\n\n<div class=\"calendar-content\">\n <!-- CALENDAR VIEW -->\n <section class=\"calendar-view\" [style.visibility]=\"view === 'CALENDAR' ? 'visible' : 'hidden'\">\n <!-- WEEK -->\n @for ( week of calendar.weeks; track $index) {\n <div class=\"week\" [class.week-selected]=\"week[0].date === values.selected\">\n <!-- DAYS -->\n @for (day of week; track $index) {\n <div\n class=\"day\"\n [class.day-today]=\"day.date === values.today\"\n [class.day-selected]=\"week[0].date === values.selected\"\n [class.day-disable]=\"day.month !== calendar.month || day.date > values.maxDate || day.date < values.minDate\"\n (click)=\"\n day.month !== calendar.month || day.date > values.maxDate || day.date < values.minDate\n ? null\n : setDate(day.date)\n \"\n >\n {{ day.day }}\n </div>\n }\n </div>\n }\n </section>\n\n <!-- MONTH VIEW -->\n @if (view === 'MONTH') {\n <section class=\"month-view\" [@month]>\n <!-- SEASON -->\n @for (season of seasons; track $index) {\n <div class=\"season\">\n <!-- MONTH -->\n @for (month of season; track $index) {\n <div\n class=\"month\"\n [class.month-disable]=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n \"\n (click)=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n ? null\n : setMonth(month.month)\n \"\n >\n {{ month.title }}\n </div>\n }\n </div>\n }\n </section>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], animations: [
trigger('month', [
transition(':enter', [style({ height: '*', opacity: 0 }), animate('150ms', style({ height: '*', opacity: 1 }))]),
]),
] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: NgxCalendarWeekComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-calendar-week', imports: [MatIconButton, MatIcon], animations: [
trigger('month', [
transition(':enter', [style({ height: '*', opacity: 0 }), animate('150ms', style({ height: '*', opacity: 1 }))]),
]),
], template: "<div class=\"calendar-header\">\n <button mat-icon-button type=\"button\" (click)=\"toggleView()\">\n <mat-icon>{{ view === 'CALENDAR' ? 'calendar_month' : 'close' }}</mat-icon>\n </button>\n <div class=\"title\">{{ view === 'CALENDAR' ? calendar.title : year }}</div>\n\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(-12) : changeYear(year - 10)\">\n <mat-icon>keyboard_double_arrow_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(-1) : changeYear(year - 1)\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(0) : changeYear(year)\">\n <mat-icon>radio_button_checked</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(1) : changeYear(year + 1)\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"view === 'CALENDAR' ? changeMonth(12) : changeYear(year + 10)\">\n <mat-icon>keyboard_double_arrow_left</mat-icon>\n </button>\n</div>\n\n<div class=\"calendar-nav\">\n @switch (view) {\n <!-- CALENDAR -->\n @case ('CALENDAR') {\n <!-- NAMES -->\n @for (day of ['\u0634', '\u06CC', '\u062F', '\u0633', '\u0686', '\u067E', '\u062C']; track $index) {\n <div class=\"item\">{{ day }}</div>\n } } @case ('MONTH') {\n <!-- YEARS -->\n @for (item of years; track $index) {\n <div class=\"item click\" (click)=\"changeYear(item)\">{{ item }}</div>\n } } }\n</div>\n\n<div class=\"calendar-content\">\n <!-- CALENDAR VIEW -->\n <section class=\"calendar-view\" [style.visibility]=\"view === 'CALENDAR' ? 'visible' : 'hidden'\">\n <!-- WEEK -->\n @for ( week of calendar.weeks; track $index) {\n <div class=\"week\" [class.week-selected]=\"week[0].date === values.selected\">\n <!-- DAYS -->\n @for (day of week; track $index) {\n <div\n class=\"day\"\n [class.day-today]=\"day.date === values.today\"\n [class.day-selected]=\"week[0].date === values.selected\"\n [class.day-disable]=\"day.month !== calendar.month || day.date > values.maxDate || day.date < values.minDate\"\n (click)=\"\n day.month !== calendar.month || day.date > values.maxDate || day.date < values.minDate\n ? null\n : setDate(day.date)\n \"\n >\n {{ day.day }}\n </div>\n }\n </div>\n }\n </section>\n\n <!-- MONTH VIEW -->\n @if (view === 'MONTH') {\n <section class=\"month-view\" [@month]>\n <!-- SEASON -->\n @for (season of seasons; track $index) {\n <div class=\"season\">\n <!-- MONTH -->\n @for (month of season; track $index) {\n <div\n class=\"month\"\n [class.month-disable]=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n \"\n (click)=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n ? null\n : setMonth(month.month)\n \"\n >\n {{ month.title }}\n </div>\n }\n </div>\n }\n </section>\n }\n</div>\n" }]
}], propDecorators: { className: [{
type: HostBinding,
args: ['className']
}], value: [{
type: Input,
args: [{ required: false }]
}], minDate: [{
type: Input,
args: [{ required: false }]
}], maxDate: [{
type: Input,
args: [{ required: false }]
}], onChange: [{
type: Output
}] } });
class NgxCalendarMonthComponent {
className = 'ngx-calendar-m3-month';
value;
minDate;
maxDate;
onChange = new EventEmitter();
values;
year;
years = [];
seasons = [
[
{ title: 'فروردین', month: '' },
{ title: 'اردیبهشت', month: '' },
{ title: 'خرداد', month: '' },
],
[
{ title: 'تیر', month: '' },
{ title: 'مرداد', month: '' },
{ title: 'شهریور', month: '' },
],
[
{ title: 'مهر', month: '' },
{ title: 'آبان', month: '' },
{ title: 'آذر', month: '' },
],
[
{ title: 'دی', month: '' },
{ title: 'بهمن', month: '' },
{ title: 'اسفند', month: '' },
],
];
jalali = JalaliDateTime();
ngOnInit() {
this.initValues();
}
ngOnChanges(changes) {
this.initValues();
}
initValues() {
const getMonth = (date) => this.jalali.toString(date, { format: 'Y-M' });
let minDate = this.minDate === 'NOW' ? new Date() : this.minDate;
let maxDate = this.maxDate === 'NOW' ? new Date() : this.maxDate;
// Check MIN and MAX Dates
if (minDate && maxDate && minDate.getTime() > maxDate.getTime()) {
const date = new Date(minDate);
minDate = maxDate;
maxDate = date;
}
// Check Value
let value = this.value ? ('from' in this.value ? this.value.from : this.value) : undefined;
if (value && minDate && getMonth(value) < getMonth(minDate))
value = undefined;
if (value && maxDate && getMonth(value) > getMonth(maxDate))
value = undefined;
this.values = {
today: getMonth(new Date()),
selected: value ? getMonth(value) : '',
minDate: minDate ? getMonth(minDate) : '0000-00',
maxDate: maxDate ? getMonth(maxDate) : '9999-99',
};
const year = this.values.selected ? this.values.selected : this.values.today;
this.changeYear(+year.substring(0, 4));
}
changeYear(year) {
if (year && year < 1000)
return;
this.year = year || +this.values.today.substring(0, 4);
let decade = this.year - (this.year % 10);
if (decade <= 1020)
decade = 1020;
this.years = [decade - 20, decade - 10, decade, decade + 10, decade + 20];
this.seasons.forEach((season, s) => {
season.forEach((month, m) => {
month.month = `${this.year.toString()}-${(s * 3 + m + 1).toString().padStart(2, '0')}`;
});
});
}
setMonth(value) {
const gregorian = this.jalali.gregorian(`${value}-01`).date;
const date = this.jalali.periodDay(1, new Date(gregorian + 'T00:00:00.000Z')).from;
const period = this.jalali.periodMonth(1, date);
const title = this.jalali.toFullText(period.from, { format: 'N Y' });
this.values.selected = value;
this.onChange.next({ period, title, jalali: value });
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: NgxCalendarMonthComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: NgxCalendarMonthComponent, isStandalone: true, selector: "ngx-calendar-month", inputs: { value: "value", minDate: "minDate", maxDate: "maxDate" }, outputs: { onChange: "onChange" }, host: { properties: { "className": "this.className" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"calendar-header\">\n <div class=\"title\">{{ year }}</div>\n\n <button mat-icon-button type=\"button\" (click)=\"changeYear(year - 10)\">\n <mat-icon>keyboard_double_arrow_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"changeYear(year - 1)\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"changeYear()\">\n <mat-icon>radio_button_checked</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"changeYear(year + 1)\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"changeYear(year + 12)\">\n <mat-icon>keyboard_double_arrow_left</mat-icon>\n </button>\n</div>\n\n<div class=\"calendar-nav\">\n @for (item of years; track $index) {\n <div class=\"item click\" (click)=\"changeYear(item)\">{{ item }}</div>\n }\n</div>\n\n<!-- SEASON -->\n@for (season of seasons; track $index) {\n<div class=\"calendar-content\">\n <!-- MONTH -->\n @for (month of season; track $index) {\n <div\n class=\"month\"\n [class.month-today]=\"month.month === values.today\"\n [class.month-selected]=\"month.month === values.selected\"\n [class.month-disable]=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n \"\n (click)=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n ? null\n : setMonth(month.month)\n \"\n >\n {{ month.title }}\n </div>\n }\n</div>\n}\n", styles: [""], dependencies: [{ kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: NgxCalendarMonthComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-calendar-month', imports: [MatIconButton, MatIcon], template: "<div class=\"calendar-header\">\n <div class=\"title\">{{ year }}</div>\n\n <button mat-icon-button type=\"button\" (click)=\"changeYear(year - 10)\">\n <mat-icon>keyboard_double_arrow_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"changeYear(year - 1)\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"changeYear()\">\n <mat-icon>radio_button_checked</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"changeYear(year + 1)\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"changeYear(year + 12)\">\n <mat-icon>keyboard_double_arrow_left</mat-icon>\n </button>\n</div>\n\n<div class=\"calendar-nav\">\n @for (item of years; track $index) {\n <div class=\"item click\" (click)=\"changeYear(item)\">{{ item }}</div>\n }\n</div>\n\n<!-- SEASON -->\n@for (season of seasons; track $index) {\n<div class=\"calendar-content\">\n <!-- MONTH -->\n @for (month of season; track $index) {\n <div\n class=\"month\"\n [class.month-today]=\"month.month === values.today\"\n [class.month-selected]=\"month.month === values.selected\"\n [class.month-disable]=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n \"\n (click)=\"\n (values.minDate && month.month < values.minDate) || (values.maxDate && month.month > values.maxDate)\n ? null\n : setMonth(month.month)\n \"\n >\n {{ month.title }}\n </div>\n }\n</div>\n}\n" }]
}], propDecorators: { className: [{
type: HostBinding,
args: ['className']
}], value: [{
type: Input,
args: [{ required: false }]
}], minDate: [{
type: Input,
args: [{ required: false }]
}], maxDate: [{
type: Input,
args: [{ required: false }]
}], onChange: [{
type: Output
}] } });
class NgxCalendarYearComponent {
className = 'ngx-calendar-m3-year';
value;
minDate;
maxDate;
onChange = new EventEmitter();
values;
years;
jalali = JalaliDateTime();
ngOnInit() {
this.initValues();
}
ngOnChanges(changes) {
this.initValues();
}
initValues() {
const getYear = (date) => this.jalali.toString(date, { format: 'Y' });
let minDate = this.minDate === 'NOW' ? new Date() : this.minDate;
let maxDate = this.maxDate === 'NOW' ? new Date() : this.maxDate;
// Check MIN and MAX Dates
if (minDate && maxDate && minDate.getTime() > maxDate.getTime()) {
const date = new Date(minDate);
minDate = maxDate;
maxDate = date;
}
// Check Value
let value = this.value ? ('from' in this.value ? this.value.from : this.value) : undefined;
if (value && minDate && getYear(value) < getYear(minDate))
value = undefined;
if (value && maxDate && getYear(value) > getYear(maxDate))
value = undefined;
this.values = {
today: getYear(new Date()),
selected: value ? getYear(value) : '',
minDate: minDate ? getYear(minDate) : '0000',
maxDate: maxDate ? getYear(maxDate) : '9999',
};
const year = this.values.selected ? this.values.selected : this.values.today;