blumjs
Version:
A UI Package for Angular2
307 lines (268 loc) • 8.6 kB
text/typescript
import {
Component,
Input,
Output,
EventEmitter,
HostListener,
ElementRef,
Inject,
OnChanges,
SimpleChanges
} from "@angular/core";
import {DateUnit} from "./dateunit";
import {CalendarBase, Calendar} from "./calendarbase";
import {Gregorian} from "./calendars/gregorian";
import {Jalali} from "./calendars/jalali";
import {Row, Cell} from "./elements";
import {DateToStringPipe, NameOfMonthPipe} from "./pipes";
({
selector: 'bl-datepicker',
template: `
<div class="datepicker">
<div class="datepicker-input">
<input type="text" [value]="model | dateToString:'shortDate':c" (click)="pickerClick()" readonly>
</div>
<div class="calendar-container" [hidden]="!pickerOpen">
<div class="calendar-title">
<a type="button" class="navigation-arrow left" (click)="prevMonth()">
<i class="fa fa-chevron-left" aria-hidden="true"></i>
</a>
<span class="current-date">
{{currentViewDate.month | nameOfMonth:c.calendar}}
<span class="year">
{{currentViewDate.year}}
</span>
</span>
<a type="button" class="navigation-arrow right" (click)="nextMonth()">
<i class="fa fa-chevron-right" aria-hidden="true"></i>
</a>
</div>
<table>
<thead>
<tr>
<th *ngFor="let cell of header.cells">{{cell.content}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of rows">
<td *ngFor="let cell of row.cells"
[ngClass]="{'selected': selectedDate.day == cell.content &&
selectedDate.year == currentViewDate.year &&
selectedDate.month == currentViewDate.month,
'not-empty': cell.content}"
(click)="setDate(cell)"
>
<a *ngIf="cell.content">{{cell.content}}</a>
</td>
</tr>
</tbody>
</table>
{{selectedDate.year}}/{{selectedDate.month+1}}/{{selectedDate.day}}
<button (click)="today()" class="today-button">امروز</button>
</div>
</div>
`,
styles: [`
a {
text-decoration: none !important;
color: black !important;
}
.datepicker {
font-family: 'Roboto', sans-serif;
box-sizing: border-box;
width: 100%;
}
.today-button {
background-color: rgb(34,116,71);
border: 0;
color: white;
float: left;
}
.datepicker-input input {
min-height: 23.3333px;
padding: 2px;
border: 1px solid rgb(34,116,71);
}
.datepicker-input input:hover {
cursor: pointer;
}
.today-button:hover {
cursor: pointer;
background-color: rgb(67, 148, 103);
}
.calendar-container {
width: 250px;
padding: 10px;
margin: 5px;
background-color: white;
border: 0;
border-radius: 5px;
overflow: hidden;
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.22);
position: fixed;
z-index: 9999;
}
.calendar-title {
text-align: center;
font-weight: bold;
}
.calendar-title > .current-date {
font-size: 1.1em;
line-height: 2em;
}
.calendar-title > .current-date > .year {
color: rgb(34,116,71);;
}
.calendar-title > .navigation-arrow:hover {
cursor: pointer;
}
.calendar-title > .navigation-arrow {
color: grey;
font-size: 2em;
}
.calendar-title > .navigation-arrow.left {
float: left;
}
.calendar-title > .navigation-arrow.right {
float: right;
}
.calendar-container > table {
table-layout: fixed;
text-align: center;
vertical-align: middle;
width: 98%;
font-size: 0.5vw;
}
.calendar-container > table > tbody > tr > td, .calendar-container > table > thead > tr > th {
width: 14%;
position: relative;
margin: 10px;
}
.calendar-container > table > tbody > tr > td:after {
content: '';
display: block;
margin-top: 100%;
}
.calendar-container > table > tbody > tr > td > a {
position: absolute;
top: 50%;
line-height: 50%;
bottom: 0;
left: 0;
right: 0;
}
.calendar-container > table > thead {
font-weight: bold;
}
.calendar-container > table > tbody {
font-size: 2em;
}
.calendar-container > table > tbody > tr > td.selected {
background-color: rgb(34,116,71);;
color: white;
border-radius: 100%;
}
.calendar-container > table > tbody > tr > td.not-empty:hover {
cursor: pointer;
border: 1px solid rgb(34,116,71);;
border-radius: 100%;
}
`],
providers: [DateToStringPipe, NameOfMonthPipe]
})
export class DatepickerComponent implements OnChanges {
() calendar: string;
() width: number = 200;
() modelChange: EventEmitter<Date> = new EventEmitter<Date>();
() model: Date;
private selectedDate: DateUnit;
private currentViewDate: DateUnit;
private pickerOpen: boolean = false;
private rows: Row[];
private header: Row;
private c: Calendar<CalendarBase>;
constructor((ElementRef) private _elementRef: ElementRef) {
this.init();
};
ngOnChanges(changes: SimpleChanges): void {
this.init();
}
init() {
if (!this.model) {
return;
}
if (this.calendar && this.calendar.toLowerCase() == 'jalali') {
this.c = new Calendar<Jalali>(Jalali);
} else {
this.c = new Calendar<Gregorian>(Gregorian);
}
this.currentViewDate = this.c.calendar.dateToDateUnit(this.model);
this.selectedDate = this.c.calendar.dateToDateUnit(this.model);
this.resetView();
}
resetView() {
this.header = this.c.weekHeaders();
let currentMonthLength = this.c.calendar.getMonthLength(this.currentViewDate);
let dayNumberOfMonthFirst = this.c.calendar.dayNumberOfMonthFirst(this.currentViewDate);
this.rows = [];
let cellIndex = 0;
for (let weekIndex = 0; weekIndex < Math.ceil((currentMonthLength + dayNumberOfMonthFirst) / 7); weekIndex++) {
this.rows[weekIndex] = new Row();
for (let colIndex = 0; colIndex < 7; colIndex++) {
this.rows[weekIndex].cells[colIndex] = new Cell();
if (weekIndex == 0 && colIndex < dayNumberOfMonthFirst) {
continue;
}
if (cellIndex < currentMonthLength) {
this.rows[weekIndex].cells[colIndex].content = (cellIndex + 1).toString();
}
cellIndex++;
}
}
}
private setDate(cell: Cell) {
this.selectedDate.year = this.currentViewDate.year;
this.selectedDate.month = this.currentViewDate.month;
this.selectedDate.day = +cell.content;
this.confirm();
}
private confirm() {
this.model = this.c.calendar.dateUnitToDate(this.selectedDate);
if(this.calendar === 'jalali') {
this.model.setMonth(this.model.getMonth()+1);
this.model = Jalali.jalaliToGregorian(this.model.getFullYear(), this.model.getMonth(), this.model.getDate());
this.model.setMonth(this.model.getMonth()-1);
}
this.modelChange.emit(this.model);
this.pickerOpen = false;
}
private today() {
this.model = new Date();
this.modelChange.emit(this.model);
this.pickerOpen = false;
}
private nextMonth() {
this.currentViewDate.addMonth();
this.resetView();
}
private prevMonth() {
this.currentViewDate.subMonth();
this.resetView();
}
private pickerClick() {
this.pickerOpen = true;
this.init();
};
private cancel() {
this.pickerOpen = false;
}
('document:click', ['$event', '$event.target'])
public onClick(event: MouseEvent, targetElement: HTMLElement): void {
if (!targetElement) {
return;
}
if (!this._elementRef.nativeElement.contains(targetElement)) {
this.cancel();
}
};
}