Created
November 1, 2017 05:46
-
-
Save er-ant/fcdfcae92e048f1c78bd13d59e1a9d9a to your computer and use it in GitHub Desktop.
Component code example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Component, forwardRef, Input, Output, EventEmitter, ElementRef, | |
trigger, state, animate, transition, style, keyframes | |
} from '@angular/core'; | |
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; | |
import { IWeekDay, WEEKDAYS, IMonth, MONTHS } from './datepicker.fixture'; | |
interface IDate { | |
dayWeekNumber?: number; | |
day?: number; | |
month?: number; | |
year?: number; | |
} | |
/** | |
* @whatItDoes Flexible datepicker. | |
* | |
* @input showCalendar Manage displaying of calendar. | |
* @input weeks Enable weeks in the calendar. Disabled by default. | |
* | |
* If you call this datepicker by any action, add id 'calendarTrigger' | |
* to a wrapper. | |
* | |
* @example | |
* <app-datepicker [(ngModel)]="campaign.annualReportDate" [weeks]="true"> | |
* </app-datepicker> | |
*/ | |
@Component({ | |
selector: 'app-datepicker', | |
templateUrl: './datepicker.component.html', | |
styleUrls: ['./datepicker.component.scss'], | |
providers: [ | |
{ | |
provide: NG_VALUE_ACCESSOR, | |
useExisting: forwardRef(() => DatepickerComponent), | |
multi: true | |
} | |
], | |
animations: [ | |
trigger('calendarAnimation', [ | |
transition('* => left', [ | |
animate(180, keyframes([ | |
style({ transform: 'translateX(105%)', offset: 0.5 }), | |
style({ transform: 'translateX(-130%)', offset: 0.51 }), | |
style({ transform: 'translateX(0)', offset: 1 }) | |
])) | |
]), | |
transition('* => right', [ | |
animate(180, keyframes([ | |
style({ transform: 'translateX(-105%)', offset: 0.5 }), | |
style({ transform: 'translateX(130%)', offset: 0.51 }), | |
style({ transform: 'translateX(0)', offset: 1 }) | |
])) | |
]) | |
]) | |
], | |
}) | |
export class DatepickerComponent implements ControlValueAccessor { | |
public readonly months: Array<IMonth> = MONTHS; | |
private _propagateChange: any = () => null; | |
public weekDays: Array<IWeekDay> = WEEKDAYS; | |
public templateMonth: number = 1; | |
public templateYear: number = 2017; | |
public showCalendar: boolean; | |
public choosenDate: IDate = <IDate> {}; | |
public calendarData: Array<IDate[]>; | |
public animate: string; | |
@Input() | |
public weeks: boolean = false; | |
@Input() | |
public set openCalendar(value: boolean) { | |
if (this.showCalendar !== value) { | |
this.showCalendar = value; | |
if (!this.showCalendar) { | |
window | |
.document | |
.removeEventListener('click', this.closeDatepickerListener.bind(this), | |
true); | |
} else { | |
window | |
.document | |
.addEventListener('click', this.closeDatepickerListener.bind(this), | |
true); | |
} | |
} | |
} | |
@Output() | |
public closeDatepicker: EventEmitter<boolean> = new EventEmitter(); | |
public writeValue(value: string) { | |
if (value) { | |
let incomingDate = new Date(value); | |
this.choosenDate = { | |
day: incomingDate.getDate(), | |
month: incomingDate.getMonth() + 1, | |
year: incomingDate.getFullYear() | |
} | |
this.templateMonth = this.choosenDate.month; | |
this.templateYear = this.choosenDate.year; | |
this.calendarData = this.buildMonth(); | |
} else { | |
let today = new Date(); | |
this.templateYear = today.getFullYear(); | |
this.calendarData = this.buildMonth(); | |
} | |
} | |
public registerOnChange(fn: Function) { | |
this._propagateChange = fn; | |
} | |
public registerOnTouched() { } | |
constructor(private _ref: ElementRef) { | |
this.setToday(); | |
} | |
private prepareRowArray(row: Array<IDate>): void { | |
let firstDay = new Date(this.templateYear, this.templateMonth - 1, 1); | |
if (firstDay.getDay() !== 0) { | |
for (let i = -firstDay.getDay() + 1; i <= 0; i++) { | |
let date = new Date(this.templateYear, this.templateMonth - 1, i); | |
row.push({ | |
day: date.getDate(), | |
month: date.getMonth() + 1, | |
year: date.getFullYear() | |
}) | |
} | |
} | |
} | |
private closeDatepickerListener(event): void { | |
let datepickerWrapper = window.document.getElementById('calendarTrigger'); | |
if (!this._ref.nativeElement.contains(event.target)) { | |
if (datepickerWrapper && | |
datepickerWrapper !== event.target && | |
!datepickerWrapper.contains(event.target)) { | |
this.closeDatepicker.emit(true); | |
} | |
} | |
} | |
private finishRowArray(row: Array<IDate>): void { | |
let lastMonthDayWeek = new Date(this.templateYear, this.templateMonth, 0).getDay(); | |
if (lastMonthDayWeek !== 6) { | |
for (let i = lastMonthDayWeek, j = 1; i < 6; i++, j++) { | |
let date = new Date(this.templateYear, this.templateMonth, j); | |
row.push({ | |
day: date.getDate(), | |
month: date.getMonth() + 1, | |
year: date.getFullYear() | |
}) | |
} | |
} | |
} | |
private triggerAnimation(direction: string): void { | |
this.animate = direction; | |
setTimeout(() => this.animate = 'reset', 185); | |
} | |
public setToday(): void { | |
let today = new Date(); | |
this.selectDay({ | |
day: today.getDate(), | |
month: today.getMonth() + 1, | |
year: today.getFullYear() | |
}) | |
this.templateMonth = this.choosenDate.month; | |
this.templateYear = this.choosenDate.year; | |
this.calendarData = this.buildMonth(); | |
} | |
public dateToString(date: IDate): string { | |
return (`${date.year}-${date.month}-${date.day}`); | |
} | |
public buildMonth(): Array<IDate[]> { | |
let rowNumber = 0; | |
let monthTemplate: Array<IDate[]> = [[]]; | |
let lastMonthDay = new Date(this.templateYear, this.templateMonth, 0).getDate(); | |
this.prepareRowArray(monthTemplate[rowNumber]); | |
for (let i = 1; i <= lastMonthDay; i++) { | |
monthTemplate[rowNumber].push({day: i, month: this.templateMonth, | |
year: this.templateYear}); | |
if (monthTemplate[rowNumber].length % 7 === 0) { | |
rowNumber++; | |
monthTemplate[rowNumber] = []; | |
} | |
} | |
this.finishRowArray(monthTemplate[rowNumber]); | |
return monthTemplate; | |
} | |
public monthTitle(): string { | |
return this.months.find(item => item.number === this.templateMonth).title; | |
} | |
public selectDay(day: IDate): void { | |
if (this.templateMonth !== day.month) { | |
this.templateMonth = day.month; | |
this.templateYear = day.year; | |
this.calendarData = this.buildMonth(); | |
} else { | |
this.closeDatepicker.emit(true); | |
} | |
this.choosenDate = day; | |
this._propagateChange(this.dateToString(this.choosenDate)); | |
} | |
public prevMonth(): void { | |
this.templateMonth--; | |
if (this.templateMonth < 1) { | |
this.templateMonth = 1; | |
} else { | |
this.triggerAnimation('left'); | |
} | |
this.calendarData = this.buildMonth(); | |
} | |
public nextMonth(): void { | |
this.templateMonth++; | |
if (this.templateMonth > 12) { | |
this.templateMonth = 12; | |
} else { | |
this.triggerAnimation('right'); | |
} | |
this.calendarData = this.buildMonth(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment