UNPKG

ng-jalali-date-time

Version:

Jalali date / month / range picker for Angular with Material Design support

794 lines (786 loc) 65.1 kB
import { Injectable, NgModule, EventEmitter, Component, Output, Directive, ComponentFactoryResolver, ViewContainerRef, Input, Pipe } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { JalaliDateTime } from 'jalali-date-time'; import { MatButtonModule } from '@angular/material/button'; import { MatDividerModule } from '@angular/material/divider'; import { MatInputModule } from '@angular/material/input'; import { trigger, state, style, transition, animate } from '@angular/animations'; class JalaliLocale { constructor() { this.required = 'مشخص کردن مقدار الزامی است'; this.notSpecified = 'مشخص نشده است'; this.date = 'تاریخ'; this.month = 'ماه'; this.range = 'دوره زمانی'; this.rangeTo = 'تا'; this.rangeCustom = 'سفارشی'; this.rangeCustomConfirm = 'انتخاب'; this.rangeCurrentWeek = 'هفته جاری'; this.rangeCurrentMonth = 'ماه جاری'; this.rangeLast7Days = '۷ روز گذشته'; this.rangeLast30Days = '۳۰ روز گذشته'; this.rangeNext7Days = '۷ روز آینده'; this.rangeNext30Days = '۳۰ روز آینده'; this.monthNames = [ 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند' ]; this.dayNames = ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج']; } } class NgJalaliDateTimeService { constructor() { this.jalali = JalaliDateTime({ timezone: 'Asia/Tehran', locale: 'en', dateFormat: 'Y-M-D', timeFormat: 'H:I:S', titleFormat: 'W، d N Y', fullTextFormat: 'W، d N Y H:I:S', }); this.jalaliLocale = new JalaliLocale(); } checkDate(date) { if (typeof date !== 'string' || date.length !== 10) return ''; if (!date.match(/([0-9]{4}-[0-9]{2}-[0-9]{2})/)) return ''; const [year, month, day] = date.split('-'); if (Number(year) < 1000 || Number(year) > 4000) return ''; if (Number(month) < 1 || Number(month) > 12) return ''; if (Number(day) < 1 || Number(day) > 31) return ''; return date; } checkTime(time) { if (typeof time !== 'string' || time.length !== 8) return ''; if (!time.match(/([0-9]{2}:[0-9]{2}:[0-9]{2})/)) return ''; const [hour, minute, second] = time.split('-'); if (Number(hour) < 0 || Number(hour) > 23) return ''; if (Number(minute) < 0 || Number(minute) > 59) return ''; if (Number(second) < 0 || Number(second) > 59) return ''; return time; } checkMonth(date) { if (typeof date !== 'string' || date.length !== 7) return ''; if (!date.match(/([0-9]{4}-[0-9]{2})/)) return ''; const [year, month] = date.split('-'); if (Number(year) < 1000 || Number(year) > 4000) return ''; if (Number(month) < 1 || Number(month) > 12) return ''; return date; } checkAppearance(appearance) { return ['legacy', 'standard', 'fill', 'outline'].includes(appearance) ? appearance : 'legacy'; } setFontCSS(css, font) { return Object.assign(Object.assign({}, css), { fontFamily: font }); } checkCSS(css) { if (!css) css = {}; css.font = css.font ? css.font : "Roboto, 'Helvetica Neue', sans-serif"; css.field = css.field ? css.field : {}; css.input = this.setFontCSS(css.input ? css.input : {}, css.font); css.reset = css.reset ? css.reset : {}; css.box = css.box ? css.box : {}; css.arrow = css.arrow ? css.arrow : {}; css.header = this.setFontCSS(css.header ? css.header : {}, css.font); css.item = this.setFontCSS(css.item ? css.item : {}, css.font); css.selected = css.selected ? css.selected : {}; css.selected = Object.assign(Object.assign({}, css.item), css.selected); css.current = css.current ? css.current : {}; css.current = Object.assign(Object.assign({}, css.item), css.current); if (!css.date) css.date = {}; css.date.name = this.setFontCSS(css.date.name ? css.date.name : {}, css.font); css.date.time = this.setFontCSS(css.date.time ? css.date.time : {}, css.font); if (!css.month) css.month = {}; if (!css.range) css.range = {}; css.range.tool = this.setFontCSS(css.range.tool ? css.range.tool : {}, css.font); css.range.custom = css.range.custom ? css.range.custom : {}; css.range.custom = Object.assign(Object.assign({}, css.range.tool), css.range.custom); css.range.confirm = css.range.confirm ? css.range.confirm : {}; css.range.confirm = Object.assign(Object.assign(Object.assign({}, css.range.tool), css.range.custom), css.range.confirm); css.range.name = this.setFontCSS(css.range.name ? css.range.name : {}, css.font); css.range.date = this.setFontCSS(css.range.date ? css.range.date : {}, css.font); return css; } jalaliToGregorian(jalali, time) { const gregorian = this.jalali.gregorian(jalali).date; return new Date(gregorian + 'T' + (this.checkTime(time) ? time : '12:00:00')); } getCalendar(month) { const now = this.jalaliToGregorian(month + '-01'); const title = this.jalali.toFullText(now, { format: 'N Y' }); let begin = now; while (begin.getDay() !== 6) begin = new Date(begin.getTime() - 24 * 3600 * 1000); const weeks = []; let days = []; let date = this.jalali.toDate(begin); while (date.substr(0, 7) <= month || days.length % 7 !== 0) { days.push({ date, month: date.substr(0, 7), day: Number(date.substr(8)), }); if (days.length === 7) { weeks.push({ days }); days = []; } begin = new Date(begin.getTime() + 24 * 3600 * 1000); date = this.jalali.toDate(begin); } return { month, title, weeks }; } getDayInMonth(month) { const [Y, M] = month.split('-'); if (Number(M) < 7) return 31; if (Number(M) < 12) return 30; const date = this.jalaliToGregorian(month + '-30'); const check = this.jalali.toString(date, { format: 'Y-M-D' }).substr(0, 7); return check === month ? 30 : 29; } /* * DATETIME */ getDateTimeValue(jalali, time) { const gregorian = jalali ? this.jalali.gregorian(jalali).date : ''; const date = gregorian ? new Date(gregorian + 'T' + (time ? time : '12:00:00')) : null; const timestamp = gregorian ? Math.floor(date.getTime() / 1000) : null; time = time ? time : '00:00:00'; return { jalali, gregorian, date, time, timestamp, }; } getDateTimeTitle(value, time) { if (!value.jalali) return ''; return time ? this.jalali.toFullText(value.date) : this.jalali.toTitle(value.date); } /* * MONTH */ getMonthValue(month) { let days = 0; const gregorian = { first: '', last: '' }; if (month) { days = this.getDayInMonth(month); gregorian.first = this.jalali.gregorian(month + '-01').date; gregorian.last = this.jalali.gregorian(month + '-' + days.toString()).date; } return { month, days, gregorian, }; } getMonthTitle(value) { if (!value.month) return ''; const date = this.jalaliToGregorian(value.month + '-01'); return this.jalali.toTitle(date, { format: 'N Y' }); } /* * RANGE */ getRangeValue(from, to) { let days = 0; let hours = 0; let minutes = 0; let seconds = 0; const gregorian = { from: '', to: '' }; if (from && to) { gregorian.from = this.jalali.gregorian(from).date; gregorian.to = this.jalali.gregorian(to).date; const fTimestamp = Math.floor(new Date(gregorian.from + 'T12:00:00').getTime() / 1000); const tTimestamp = Math.floor(new Date(gregorian.to + 'T12:00:00').getTime() / 1000); days = Math.floor(Math.abs((tTimestamp - fTimestamp) / (24 * 3600))) + 1; hours = days * 24; minutes = hours * 60; seconds = minutes * 60; } return { range: from && to ? from + '|' + to : '', from, to, days, hours, minutes, seconds, gregorian, }; } getRangeTitle(value) { if (!value.from || !value.to) return ''; const fDate = this.jalaliToGregorian(value.from); const tDate = this.jalaliToGregorian(value.to); const [fY, fM, fD] = value.from.split('-'); const [tY, tM, tD] = value.to.split('-'); let from = ''; if (fY === tY && fM === tM) { from = this.jalali.toTitle(fDate, { format: 'd' }); } else if (fY === tY) { from = this.jalali.toTitle(fDate, { format: 'd N' }); } else { from = this.jalali.toTitle(fDate, { format: 'd N Y' }); } return from + ' ' + this.jalaliLocale.rangeTo + ' ' + this.jalali.toTitle(tDate, { format: 'd N Y' }); } } NgJalaliDateTimeService.decorators = [ { type: Injectable } ]; NgJalaliDateTimeService.ctorParameters = () => []; class MaterialModule { } MaterialModule.decorators = [ { type: NgModule, args: [{ exports: [MatInputModule, MatButtonModule, MatDividerModule], },] } ]; class NgJalaliDateTimeComponent { constructor(service) { this.service = service; this.show = false; this.random = Date.now().toString(); this.month = ''; this.today = ''; this.reset = false; this.jalaliLocale = new JalaliLocale(); this.week = this.jalaliLocale.dayNames; this.changed = new EventEmitter(); } ngOnInit() { this.month = this.value.jalali ? this.value.jalali.substr(0, 7) : this.service.jalali.now({ format: 'Y-M' }); this.calendar = this.service.getCalendar(this.month); this.today = this.service.jalali.now({ format: 'Y-M-D' }); const validators = []; if (this.config.required) validators.push(this.validate.bind(this)); this.form.addControl(this.name, new FormControl(this.value)); this.form.addControl(this.name + '-title', new FormControl('', validators)); this.setTitle(); } validate() { return this.value.jalali ? null : { required: true }; } showCalendar(show) { const input = document.querySelector('#input' + this.random); input.blur(); if (this.reset) { this.reset = false; show = false; } this.show = show; if (this.show) { this.form.get(this.name + '-title').markAsUntouched(); if (!this.value.jalali) this.value.time = '00:00:00'; this.month = this.value.jalali ? this.value.jalali.substr(0, 7) : this.service.jalali.now({ format: 'Y-M' }); this.calendar = this.service.getCalendar(this.month); } else this.form.get(this.name + '-title').markAsTouched(); } changeMonth(change) { const [Y, M] = this.month.split('-'); let year = Number(Y); let month = Number(M) + change; while (month > 12) { month -= 12; year++; } while (month < 1) { month += 12; year--; } this.month = year.toString() + '-' + (month < 10 ? '0' : '') + month.toString(); this.calendar = this.service.getCalendar(this.month); } setTitle() { this.form.get(this.name).setValue(this.value); const title = this.service.getDateTimeTitle(this.value, this.config.time); this.form.get(this.name + '-title').setValue(title); } setDate(jalali) { this.value = this.service.getDateTimeValue(jalali, this.value.time); this.setTitle(); this.showCalendar(false); this.changed.emit(this.value.jalali ? this.value : null); this.form.get(this.name + '-title').updateValueAndValidity(); } getTime(type) { if (!this.value.time) return 0; let value = 0; switch (type) { case 'H': value = Number(this.value.time.substr(0, 2)); break; case 'I': value = Number(this.value.time.substr(3, 2)); break; case 'S': value = Number(this.value.time.substr(6, 2)); break; } return isNaN(value) ? 0 : value; } setTime() { const hour = document.querySelector('#hour' + this.random); const minute = document.querySelector('#minute' + this.random); const second = document.querySelector('#second' + this.random); let H = Math.floor(Number(hour.value)); H = isNaN(H) || H < 0 || H > 23 ? 0 : H; hour.value = H.toString(); let I = Math.floor(Number(minute.value)); I = isNaN(I) || I < 0 || I > 59 ? 0 : I; minute.value = I.toString(); let S = Math.floor(Number(second.value)); S = isNaN(S) || S < 0 || S > 59 ? 0 : S; second.value = this.config.second ? S.toString() : '00'; const time = (H < 10 ? '0' : '') + H.toString() + ':' + ((I < 10 ? '0' : '') + I.toString()) + ':' + ((S < 10 ? '0' : '') + S.toString()); this.value = this.service.getDateTimeValue(this.value.jalali, time); this.setTitle(); this.changed.emit(this.value.jalali ? this.value : null); this.form.get(this.name + '-title').updateValueAndValidity(); } } NgJalaliDateTimeComponent.decorators = [ { type: Component, args: [{ template: "<div class=\"ng-jalali-back\" *ngIf=\"show\" (click)=\"showCalendar(false)\"></div>\n\n<div [formGroup]=\"form\" [ngStyle]=\"{ display: 'none' }\">\n <input type=\"text\" [formControlName]=\"name\" />\n</div>\n\n<content class=\"ng-jalali\" dir=\"rtl\">\n <mat-form-field [ngStyle]=\"css.field\" class=\"ng-jalali\" [formGroup]=\"form\" [appearance]=\"appearance\">\n <mat-label [ngStyle]=\"{ fontFamily: css.font }\">{{ label }}</mat-label>\n <input\n matInput\n id=\"input{{ random }}\"\n type=\"text\"\n [formControlName]=\"name + '-title'\"\n (focus)=\"showCalendar(true)\"\n [ngStyle]=\"css.input\"\n class=\"ng-jalali\"\n />\n <span *ngIf=\"config.reset && value.jalali !== ''\" matSuffix (click)=\"setDate(''); reset = true\">\n <button mat-button type=\"button\" [ngStyle]=\"css.reset\" class=\"ng-jalali-reset\">x</button>\n </span>\n <mat-error [ngStyle]=\"{ fontFamily: css.font }\" *ngIf=\"form.get(name + '-title').hasError('required')\">\n {{ config.error }}\n </mat-error>\n </mat-form-field>\n\n <div class=\"ng-jalali-calendar\" [ngStyle]=\"css.box\" [@calendar] *ngIf=\"show\">\n <div class=\"ng-jalali-header\">\n <div>\n <button\n mat-icon-button\n type=\"button\"\n [ngStyle]=\"css.arrow\"\n (click)=\"changeMonth(-12)\"\n [disabled]=\"month.substr(0, 4) === '1000'\"\n >\n &lt;&lt;\n </button>\n </div>\n <div>\n <button\n mat-icon-button\n type=\"button\"\n [ngStyle]=\"css.arrow\"\n (click)=\"changeMonth(-1)\"\n [disabled]=\"month === '1000-01'\"\n >\n &lt;\n </button>\n </div>\n <div class=\"ng-jalali-title\" [ngStyle]=\"css.header\">{{ calendar.title }}</div>\n <div>\n <button mat-icon-button type=\"button\" [ngStyle]=\"css.arrow\" (click)=\"changeMonth(1)\">&gt;</button>\n </div>\n <div>\n <button mat-icon-button type=\"button\" [ngStyle]=\"css.arrow\" (click)=\"changeMonth(12)\">&gt;&gt;</button>\n </div>\n </div>\n <div class=\"ng-jalali-date-time-header\">\n <div [ngStyle]=\"css.date.name\" *ngFor=\"let day of week\">{{ day }}</div>\n </div>\n <div *ngFor=\"let week of calendar.weeks\" class=\"ng-jalali-date-time-week\">\n <div *ngFor=\"let day of week.days\" class=\"ng-jalali-date-time-day\">\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"\n day.month !== month ||\n (config.min && day.date < config.min) ||\n (config.max && day.date > config.max)\n \"\n [ngClass]=\"{\n 'ng-jalali-selected': day.date === value.jalali,\n 'ng-jalali-current': day.date === today\n }\"\n [ngStyle]=\"day.date === today ? css.current : day.date === value.jalali ? css.selected : css.item\"\n (click)=\"setDate(day.date)\"\n >\n {{ day.day }}\n </button>\n </div>\n </div>\n <ng-container *ngIf=\"config.time\">\n <mat-divider></mat-divider>\n <div class=\"ng-jalali-date-time-time\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"23\"\n step=\"1\"\n [value]=\"getTime('H')\"\n [ngStyle]=\"css.date.time\"\n id=\"hour{{ random }}\"\n (blur)=\"setTime()\"\n />\n <div>:</div>\n <input\n type=\"number\"\n min=\"0\"\n max=\"59\"\n step=\"1\"\n [value]=\"getTime('I')\"\n [ngStyle]=\"css.date.time\"\n id=\"minute{{ random }}\"\n (blur)=\"setTime()\"\n />\n <div>:</div>\n <input\n type=\"number\"\n min=\"0\"\n max=\"59\"\n step=\"1\"\n [disabled]=\"!config.second\"\n [value]=\"config.second ? getTime('S') : '00'\"\n [ngStyle]=\"css.date.time\"\n id=\"second{{ random }}\"\n (blur)=\"setTime()\"\n /></div\n ></ng-container>\n </div>\n</content>\n", animations: [ trigger('calendar', [ state('init', style({ opacity: 1, transform: 'translateY(0) scaleY(1)' })), transition('void => *', [style({ opacity: 0, transform: 'translateY(-45%) scaleY(0)' }), animate(100)]), transition('* => void', animate(100, style({ opacity: 0, transform: 'translateY(-45%) scaleY(0)' }))) ]) ], styles: ["div.ng-jalali-date-time-header{display:flex}div.ng-jalali-date-time-header div{background-color:#ddd;flex:1;padding:1rem .2rem;text-align:center}div.ng-jalali-date-time-week{display:flex}div.ng-jalali-date-time-day{flex:1;padding:.2rem;text-align:center}div.ng-jalali-date-time-time{align-items:center;direction:ltr;display:flex;margin:.5rem}div.ng-jalali-date-time-time div{text-align:center;width:2rem}div.ng-jalali-date-time-time input{background-color:#eee;border:0;direction:rtl;flex:1;outline:none;padding:.5rem;text-align:center}", "div.ng-jalali-back{background-color:transparent;bottom:0;left:0;position:fixed;right:0;top:0;z-index:999}content.ng-jalali{display:block;position:relative}mat-form-field.ng-jalali{direction:rtl;margin:0;text-align:right;width:100%}input.ng-jalali{cursor:default;text-align:right}button.ng-jalali-reset{color:#555;line-height:30px;min-width:auto;padding:0 .5rem}div.ng-jalali-calendar{background-color:#fff;box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12);direction:rtl;position:absolute;right:0;top:45px;z-index:1000}div.ng-jalali-header{align-items:center;display:flex}div.ng-jalali-header div{flex:1;padding:.5rem .2rem;text-align:center}div.ng-jalali-header div.ng-jalali-title{flex:3}button.ng-jalali-selected{background-color:#eee;border:1px solid #999}button.ng-jalali-current{color:#009}"] },] } ]; NgJalaliDateTimeComponent.ctorParameters = () => [ { type: NgJalaliDateTimeService } ]; NgJalaliDateTimeComponent.propDecorators = { changed: [{ type: Output }] }; class NgJalaliDateTimeDirective { constructor(resolver, container, service) { this.resolver = resolver; this.container = container; this.service = service; this.jalaliLocale = new JalaliLocale(); this.changed = new EventEmitter(); } ngOnInit() { if (!this.form) this.form = new FormGroup({}); if (!this.name) this.name = 'jalali-date-time-' + Date.now().toString(); this.appearance = this.service.checkAppearance(this.appearance); if (!this.config) this.config = {}; this.config.required = this.config.required ? true : false; this.config.min = this.service.checkDate(this.config.min) ? this.config.min : null; this.config.max = this.service.checkDate(this.config.max) ? this.config.max : null; this.config.reset = this.config.reset === false ? false : true; this.config.error = this.config.error ? this.config.error : this.jalaliLocale.required; this.config.time = this.config.time ? true : false; this.config.second = this.config.second ? true : false; this.css = this.service.checkCSS(this.css); this.date = this.service.checkDate(this.date); this.time = this.config.time ? this.service.checkTime(this.time) : ''; this.label = this.label ? this.label : this.jalaliLocale.date; const factory = this.resolver.resolveComponentFactory(NgJalaliDateTimeComponent); this.component = this.container.createComponent(factory); this.component.instance.form = this.form; this.component.instance.name = this.name; this.component.instance.appearance = this.appearance; this.component.instance.config = this.config; this.component.instance.css = this.css; this.component.instance.value = this.service.getDateTimeValue(this.date, this.time); this.component.instance.label = this.label; this.component.instance.changed = this.changed; } } NgJalaliDateTimeDirective.decorators = [ { type: Directive, args: [{ selector: '[jalali-date-time]' },] } ]; NgJalaliDateTimeDirective.ctorParameters = () => [ { type: ComponentFactoryResolver }, { type: ViewContainerRef }, { type: NgJalaliDateTimeService } ]; NgJalaliDateTimeDirective.propDecorators = { form: [{ type: Input }], name: [{ type: Input }], appearance: [{ type: Input }], config: [{ type: Input }], css: [{ type: Input }], date: [{ type: Input }], time: [{ type: Input }], label: [{ type: Input }], changed: [{ type: Output }] }; class NgJalaliMonthComponent { constructor(service) { this.service = service; this.show = false; this.random = Date.now().toString(); this.month = ''; this.year = ''; this.current = ''; this.reset = false; this.jalaliLocale = new JalaliLocale(); this.calendar = [ [ { month: '01', title: this.jalaliLocale.monthNames[0] }, { month: '02', title: this.jalaliLocale.monthNames[1] }, { month: '03', title: this.jalaliLocale.monthNames[2] } ], [ { month: '04', title: this.jalaliLocale.monthNames[3] }, { month: '05', title: this.jalaliLocale.monthNames[4] }, { month: '06', title: this.jalaliLocale.monthNames[5] } ], [ { month: '07', title: this.jalaliLocale.monthNames[6] }, { month: '08', title: this.jalaliLocale.monthNames[7] }, { month: '09', title: this.jalaliLocale.monthNames[8] } ], [ { month: '10', title: this.jalaliLocale.monthNames[9] }, { month: '11', title: this.jalaliLocale.monthNames[10] }, { month: '12', title: this.jalaliLocale.monthNames[11] } ] ]; this.changed = new EventEmitter(); } ngOnInit() { this.year = this.value.month ? this.value.month.substr(0, 4) : this.service.jalali.now({ format: 'Y' }); this.current = this.service.jalali.now({ format: 'Y-M' }); const validators = []; if (this.config.required) validators.push(this.validate.bind(this)); this.form.addControl(this.name, new FormControl(this.value)); this.form.addControl(this.name + '-title', new FormControl('', validators)); this.setTitle(); } validate() { return this.value.month ? null : { required: true }; } showCalendar(show) { const input = document.querySelector('#input' + this.random); input.blur(); if (this.reset) { this.reset = false; show = false; } this.show = show; if (this.show) { this.form.get(this.name + '-title').markAsUntouched(); this.year = this.value.month ? this.value.month.substr(0, 4) : this.service.jalali.now({ format: 'Y' }); } else this.form.get(this.name + '-title').markAsTouched(); } changeYear(change) { const year = Number(this.year) + change; this.year = year < 1000 ? '1000' : year.toString(); } setTitle() { this.form.get(this.name).setValue(this.value); const title = this.service.getMonthTitle(this.value); this.form.get(this.name + '-title').setValue(title); } setMonth(month) { this.value = this.service.getMonthValue(month ? this.year + '-' + month : ''); this.setTitle(); this.showCalendar(false); this.changed.emit(this.value.month ? this.value : null); this.form.get(this.name + '-title').updateValueAndValidity(); } } NgJalaliMonthComponent.decorators = [ { type: Component, args: [{ template: "<div class=\"ng-jalali-back\" *ngIf=\"show\" (click)=\"showCalendar(false)\"></div>\n\n<div [formGroup]=\"form\" [ngStyle]=\"{ display: 'none' }\">\n <input type=\"text\" [formControlName]=\"name\" />\n</div>\n\n<content class=\"ng-jalali\" dir=\"rtl\">\n <mat-form-field [ngStyle]=\"css.field\" class=\"ng-jalali\" [formGroup]=\"form\" [appearance]=\"appearance\">\n <mat-label [ngStyle]=\"{ fontFamily: css.font }\">{{ label }}</mat-label>\n <input\n matInput\n id=\"input{{ random }}\"\n type=\"text\"\n [formControlName]=\"name + '-title'\"\n (focus)=\"showCalendar(true)\"\n [ngStyle]=\"css.input\"\n class=\"ng-jalali\"\n />\n <span *ngIf=\"config.reset && value.month !== ''\" matSuffix (click)=\"setMonth(''); reset = true\">\n <button mat-button type=\"button\" [ngStyle]=\"css.reset\" class=\"ng-jalali-reset\">x</button>\n </span>\n <mat-error [ngStyle]=\"{ fontFamily: css.font }\" *ngIf=\"form.get(name + '-title').hasError('required')\">\n {{ config.error }}\n </mat-error>\n </mat-form-field>\n\n <div class=\"ng-jalali-calendar\" [ngStyle]=\"css.box\" [@calendar] *ngIf=\"show\">\n <div class=\"ng-jalali-header\">\n <div>\n <button\n mat-icon-button\n type=\"button\"\n [ngStyle]=\"css.arrow\"\n (click)=\"changeYear(-10)\"\n [disabled]=\"year <= '1010'\"\n >\n &lt;&lt;\n </button>\n </div>\n <div>\n <button\n mat-icon-button\n type=\"button\"\n [ngStyle]=\"css.arrow\"\n (click)=\"changeYear(-1)\"\n [disabled]=\"year === '1000'\"\n >\n &lt;\n </button>\n </div>\n <div class=\"ng-jalali-title\" [ngStyle]=\"css.header\">{{ year }}</div>\n <div>\n <button mat-icon-button type=\"button\" [ngStyle]=\"css.arrow\" (click)=\"changeYear(1)\">&gt;</button>\n </div>\n <div>\n <button mat-icon-button type=\"button\" [ngStyle]=\"css.arrow\" (click)=\"changeYear(10)\">&gt;&gt;</button>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div *ngFor=\"let season of calendar\" class=\"ng-jalali-month-season\">\n <ng-container *ngFor=\"let month of season\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n (config.min && year + '-' + month.month < config.min) ||\n (config.max && year + '-' + month.month > config.max)\n \"\n [ngClass]=\"{\n 'ng-jalali-selected': year + '-' + month.month === value.month,\n 'ng-jalali-current': year + '-' + month.month === current\n }\"\n [ngStyle]=\"\n year + '-' + month.month === current\n ? css.current\n : year + '-' + month.month === value.month\n ? css.selected\n : css.item\n \"\n (click)=\"setMonth(month.month)\"\n >\n {{ month.title }}\n </button>\n </ng-container>\n </div>\n </div>\n</content>\n", animations: [ trigger('calendar', [ state('init', style({ opacity: 1, transform: 'translateY(0) scaleY(1)' })), transition('void => *', [style({ opacity: 0, transform: 'translateY(-45%) scaleY(0)' }), animate(100)]), transition('* => void', animate(100, style({ opacity: 0, transform: 'translateY(-45%) scaleY(0)' }))) ]) ], styles: ["div.ng-jalali-month-season{display:flex}div.ng-jalali-month-season button{flex:1;padding:.5rem 2rem}", "div.ng-jalali-back{background-color:transparent;bottom:0;left:0;position:fixed;right:0;top:0;z-index:999}content.ng-jalali{display:block;position:relative}mat-form-field.ng-jalali{direction:rtl;margin:0;text-align:right;width:100%}input.ng-jalali{cursor:default;text-align:right}button.ng-jalali-reset{color:#555;line-height:30px;min-width:auto;padding:0 .5rem}div.ng-jalali-calendar{background-color:#fff;box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12);direction:rtl;position:absolute;right:0;top:45px;z-index:1000}div.ng-jalali-header{align-items:center;display:flex}div.ng-jalali-header div{flex:1;padding:.5rem .2rem;text-align:center}div.ng-jalali-header div.ng-jalali-title{flex:3}button.ng-jalali-selected{background-color:#eee;border:1px solid #999}button.ng-jalali-current{color:#009}"] },] } ]; NgJalaliMonthComponent.ctorParameters = () => [ { type: NgJalaliDateTimeService } ]; NgJalaliMonthComponent.propDecorators = { changed: [{ type: Output }] }; class NgJalaliMonthDirective { constructor(resolver, container, service) { this.resolver = resolver; this.container = container; this.service = service; this.jalaliLocale = new JalaliLocale(); this.changed = new EventEmitter(); } ngOnInit() { if (!this.form) this.form = new FormGroup({}); if (!this.name) this.name = 'jalali-month-' + Date.now().toString(); this.appearance = this.service.checkAppearance(this.appearance); if (!this.config) this.config = {}; this.config.required = this.config.required ? true : false; this.config.min = this.service.checkMonth(this.config.min) ? this.config.min : null; this.config.max = this.service.checkMonth(this.config.max) ? this.config.max : null; this.config.reset = this.config.reset === false ? false : true; this.config.error = this.config.error ? this.config.error : this.jalaliLocale.required; this.css = this.service.checkCSS(this.css); this.month = this.service.checkMonth(this.month); this.label = this.label ? this.label : this.jalaliLocale.month; const factory = this.resolver.resolveComponentFactory(NgJalaliMonthComponent); this.component = this.container.createComponent(factory); this.component.instance.form = this.form; this.component.instance.name = this.name; this.component.instance.appearance = this.appearance; this.component.instance.config = this.config; this.component.instance.css = this.css; this.component.instance.value = this.service.getMonthValue(this.month); this.component.instance.label = this.label; this.component.instance.changed = this.changed; } } NgJalaliMonthDirective.decorators = [ { type: Directive, args: [{ selector: '[jalali-month]' },] } ]; NgJalaliMonthDirective.ctorParameters = () => [ { type: ComponentFactoryResolver }, { type: ViewContainerRef }, { type: NgJalaliDateTimeService } ]; NgJalaliMonthDirective.propDecorators = { form: [{ type: Input }], name: [{ type: Input }], appearance: [{ type: Input }], config: [{ type: Input }], css: [{ type: Input }], month: [{ type: Input }], label: [{ type: Input }], changed: [{ type: Output }] }; class NgJalaliRangeComponent { constructor(service) { this.service = service; this.show = false; this.random = Date.now().toString(); this.today = ''; this.reset = false; this.jalaliLocale = new JalaliLocale(); this.week = this.jalaliLocale.dayNames; this.changed = new EventEmitter(); } ngOnInit() { this.custom = { from: this.value.range ? this.value.from : '', to: this.value.range ? this.value.to : '' }; this.setCustomTitle(); this.fMonth = this.value.range ? this.value.from.substr(0, 7) : this.service.jalali.now({ format: 'Y-M' }); this.fCalendar = this.service.getCalendar(this.fMonth); this.tMonth = this.value.range ? this.value.to.substr(0, 7) : this.service.jalali.now({ format: 'Y-M' }); this.tCalendar = this.service.getCalendar(this.tMonth); this.today = this.service.jalali.now({ format: 'Y-M-D' }); const validators = []; if (this.config.required) validators.push(this.validate.bind(this)); this.form.addControl(this.name, new FormControl(this.value)); this.form.addControl(this.name + '-title', new FormControl('', validators)); this.setTitle(); } validate() { return this.value.range ? null : { required: true }; } showCalendar(show) { const input = document.querySelector('#input' + this.random); input.blur(); if (this.reset) { this.reset = false; show = false; } this.show = show; if (this.show) { this.setCustomTitle(); this.form.get(this.name + '-title').markAsUntouched(); if (this.value.range) { this.fMonth = this.value.from.substr(0, 7); this.fCalendar = this.service.getCalendar(this.fMonth); this.tMonth = this.value.to.substr(0, 7); this.tCalendar = this.service.getCalendar(this.tMonth); } else { this.fMonth = this.service.jalali.now({ format: 'Y-M' }); this.fCalendar = this.service.getCalendar(this.fMonth); this.tMonth = this.service.jalali.now({ format: 'Y-M' }); this.tCalendar = this.service.getCalendar(this.tMonth); this.setCustomDate('', ''); } } else this.form.get(this.name + '-title').markAsTouched(); } changeMonth(type, change) { let Y = ''; let M = ''; switch (type) { case 'from': [Y, M] = this.fMonth.split('-'); break; case 'to': [Y, M] = this.tMonth.split('-'); break; } let year = Number(Y); let month = Number(M) + change; while (month > 12) { month -= 12; year++; } while (month < 1) { month += 12; year--; } switch (type) { case 'from': this.fMonth = year.toString() + '-' + (month < 10 ? '0' : '') + month.toString(); this.fCalendar = this.service.getCalendar(this.fMonth); break; case 'to': this.tMonth = year.toString() + '-' + (month < 10 ? '0' : '') + month.toString(); this.tCalendar = this.service.getCalendar(this.tMonth); break; } } setTitle() { this.form.get(this.name).setValue(this.value); const title = this.service.getRangeTitle(this.value); this.form.get(this.name + '-title').setValue(title); } setCustomTitle() { this.fTitle = this.custom.from ? this.service.jalali.toTitle(this.service.jalaliToGregorian(this.custom.from)) : this.jalaliLocale.notSpecified; this.tTitle = this.custom.to ? this.service.jalali.toTitle(this.service.jalaliToGregorian(this.custom.to)) : this.jalaliLocale.notSpecified; } setCustomDate(from, to) { if (from && to && from >= to) return; this.custom.from = from; this.custom.to = to; this.setCustomTitle(); } setRange(type) { const format = { format: 'Y-M-D' }; let from = ''; let to = ''; let date = null; switch (type) { case 'current-week': date = this.service.jalaliToGregorian(this.today); while (date.getDay() !== 6) { date = new Date(date.getTime() - 24 * 3600 * 1000); } from = this.service.jalali.toString(date, format); to = this.service.jalali.toString(new Date(date.getTime() + 6 * 24 * 3600 * 1000), format); break; case 'current-month': date = this.service.jalaliToGregorian(this.today.substr(0, 8) + '01'); const days = this.service.getDayInMonth(this.today.substr(0, 7)); from = this.service.jalali.toString(date, format); to = this.service.jalali.toString(new Date(date.getTime() + (days - 1) * 24 * 3600 * 1000), format); break; case 'last-7days': date = this.service.jalaliToGregorian(this.today); from = this.service.jalali.toString(new Date(date.getTime() - 6 * 24 * 3600 * 1000), format); to = this.service.jalali.toString(date, format); break; case 'last-30days': date = this.service.jalaliToGregorian(this.today); from = this.service.jalali.toString(new Date(date.getTime() - 29 * 24 * 3600 * 1000), format); to = this.service.jalali.toString(date, format); break; case 'next-7days': date = this.service.jalaliToGregorian(this.today); from = this.service.jalali.toString(date, format); to = this.service.jalali.toString(new Date(date.getTime() + 6 * 24 * 3600 * 1000), format); break; case 'next-30days': date = this.service.jalaliToGregorian(this.today); from = this.service.jalali.toString(date, format); to = this.service.jalali.toString(new Date(date.getTime() + 29 * 24 * 3600 * 1000), format); break; case 'custom': from = this.custom.from; to = this.custom.to; break; case 'reset': default: break; } this.custom = { from, to }; this.value = this.service.getRangeValue(from, to); this.setTitle(); this.showCalendar(false); this.changed.emit(this.value.range ? this.value : null); this.form.get(this.name + '-title').updateValueAndValidity(); } } NgJalaliRangeComponent.decorators = [ { type: Component, args: [{ template: "<div class=\"ng-jalali-back\" *ngIf=\"show\" (click)=\"showCalendar(false)\"></div>\n\n<div [formGroup]=\"form\" [ngStyle]=\"{ display: 'none' }\">\n <input type=\"text\" [formControlName]=\"name\" />\n</div>\n\n<content class=\"ng-jalali\" dir=\"rtl\">\n <mat-form-field [ngStyle]=\"css.field\" class=\"ng-jalali\" [formGroup]=\"form\" [appearance]=\"appearance\">\n <mat-label [ngStyle]=\"{ fontFamily: css.font }\">{{ label }}</mat-label>\n <input\n matInput\n id=\"input{{ random }}\"\n type=\"text\"\n [formControlName]=\"name + '-title'\"\n (focus)=\"showCalendar(true)\"\n [ngStyle]=\"css.input\"\n class=\"ng-jalali\"\n />\n <span *ngIf=\"config.reset && value.range !== ''\" matSuffix (click)=\"setRange('reset'); reset = true\">\n <button mat-button type=\"button\" [ngStyle]=\"css.reset\" class=\"ng-jalali-reset\">x</button>\n </span>\n <mat-error [ngStyle]=\"{ fontFamily: css.font }\" *ngIf=\"form.get(name + '-title').hasError('required')\">\n {{ config.error }}\n </mat-error>\n </mat-form-field>\n\n <div class=\"ng-jalali-calendar\" [ngStyle]=\"css.box\" [@calendar] *ngIf=\"show\">\n <div class=\"ng-jalali-range-tools\">\n <button\n mat-button\n type=\"button\"\n [ngStyle]=\"custom.from === '' || custom.to === '' ? css.range.custom : css.range.confirm\"\n [disabled]=\"custom.from === '' || custom.to === ''\"\n (click)=\"setRange('custom')\"\n class=\"ng-jalali-range-custom\"\n >\n {{\n custom.from === '' || custom.to === '' ? jalaliLocale.rangeCustom : jalaliLocale.rangeCustomConfirm\n }}\n </button>\n <button mat-button type=\"button\" [ngStyle]=\"css.range.tool\" (click)=\"setRange('current-week')\">\n {{ jalaliLocale.rangeCurrentWeek }}\n </button>\n <button mat-button type=\"button\" [ngStyle]=\"css.range.tool\" (click)=\"setRange('current-month')\">\n {{ jalaliLocale.rangeCurrentMonth }}\n </button>\n <button mat-button type=\"button\" [ngStyle]=\"css.range.tool\" (click)=\"setRange('last-7days')\">\n {{ jalaliLocale.rangeLast7Days }}\n </button>\n <button mat-button type=\"button\" [ngStyle]=\"css.range.tool\" (click)=\"setRange('last-30days')\">\n {{ jalaliLocale.rangeLast30Days }}\n </button>\n <button mat-button type=\"button\" [ngStyle]=\"css.range.tool\" (click)=\"setRange('next-7days')\">\n {{ jalaliLocale.rangeNext7Days }}\n </button>\n <button mat-button type=\"button\" [ngStyle]=\"css.range.tool\" (click)=\"setRange('next-30days')\">\n {{ jalaliLocale.rangeNext30Days }}\n </button>\n </div>\n <div class=\"ng-jalali-range-custom\">\n <div class=\"ng-jalali-range-from\">\n <div class=\"ng-jalali-header\">\n <div>\n <button\n mat-icon-button\n type=\"button\"\n [ngStyle]=\"css.arrow\"\n (click)=\"changeMonth('from', -12)\"\n [disabled]=\"fMonth.substr(0, 4) === '1000'\"\n >\n &lt;&lt;\n </button>\n </div>\n <div>\n <button\n mat-icon-button\n type=\"button\"\n [ngStyle]=\"css.arrow\"\n (click)=\"changeMonth('from', -1)\"\n [disabled]=\"fMonth === '1000-01'\"\n >\n &lt;\n </button>\n </div>\n <div class=\"ng-jalali-title\" [ngStyle]=\"css.header\">{{ fCalendar.title }}</div>\n <div>\n <button mat-icon-button type=\"button\" [ngStyle]=\"css.arrow\" (click)=\"changeMonth('from', 1)\">\n &gt;\n </button>\n </div>\n <div>\n <button mat-icon-button type=\"button\" [ngStyle]=\"css.arrow\" (click)=\"changeMonth('from', 12)\">\n &gt;&gt;\n </button>\n </div>\n </div>\n <div class=\"ng-jalali-range-header\">\n <div [ngStyle]=\"css.range.name\" *ngFor=\"let day of week\">{{ day }}</div>\n </div>\n <section>\n <div *ngFor=\"let week of fCalendar.weeks\" class=\"ng-jalali-range-week\">\n <div *ngFor=\"let day of week.days\" class=\"ng-jalali-range-day\">\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"\n day.month !== fMonth ||\n (config.min && day.date < config.min) ||\n (config.max && day.date > config.max) ||\n (custom.to !== '' && day.date >= custom.to)\n \"\n [ngClass]=\"{\n 'ng-jalali-selected': day.date === custom.from,\n 'ng-jalali-current': day.date === today\n }\"\n [ngStyle]=\"\n day.date === today\n ? css.current\n : day.date === custom.from\n ? css.selected\n : css.item\n \"\n (click)=\"setCustomDate(day.date, custom.to)\"\n >\n {{ day.day }}\n </button>\n </div>\n </div>\n </section>\n <div class=\"ng-jalali-range-custom-title\" [ngStyle]=\"css.range.date\">{{ fTitle }}</div>\n </div>\n <div class=\"ng-jalali-range-to\">\n <div class=\"ng-jalali-header\">\n <div>\n <button\n mat-icon-button\n type=\"button\"\n [ngStyle]=\"css.arrow\"\n (click)=\"changeMonth('to', -12)\"\n [disabled]=\"tMonth.substr(0, 4) === '1000'\"\n >\n &lt;&lt;\n </button>\n </div>\n <div>\n <button\n mat-icon-button\n type=\"button\"\n [ngStyle]=\"css.arrow\"\n (click)=\"changeMonth('to', -1)\"\n [disabled]=\"tMonth === '1000-01'\"\n >\n