UNPKG

@webilix/ngx-calendar-m3

Version:

Jalali calendar components for Angular and Material 3

613 lines (607 loc) 98.1 kB
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;