ng-jalali-date-time
Version:
Jalali date / month / range picker for Angular with Material Design support
794 lines (786 loc) • 65.1 kB
JavaScript
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 <<\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 <\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)\">></button>\n </div>\n <div>\n <button mat-icon-button type=\"button\" [ngStyle]=\"css.arrow\" (click)=\"changeMonth(12)\">>></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 <<\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 <\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)\">></button>\n </div>\n <div>\n <button mat-icon-button type=\"button\" [ngStyle]=\"css.arrow\" (click)=\"changeYear(10)\">>></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 <<\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 <\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 >\n </button>\n </div>\n <div>\n <button mat-icon-button type=\"button\" [ngStyle]=\"css.arrow\" (click)=\"changeMonth('from', 12)\">\n >>\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 <<\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