ngx-mat-shift-selection
Version:
This is a simple date and time selection component design specificly for job shifts etc, with 24-hours available option.
555 lines (548 loc) • 24.8 kB
JavaScript
import { Injectable, ɵɵdefineInjectable, EventEmitter, Component, Input, Output, NgModule } from '@angular/core';
import { __awaiter } from 'tslib';
import * as _moment from 'moment';
import { sortBy, remove, findIndex, range } from 'lodash';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatFormFieldModule, MatInputModule, MatAutocompleteModule, MatCheckboxModule, MatDividerModule, MatButtonModule, MatIconModule } from '@angular/material';
/**
* @fileoverview added by tsickle
* Generated from: lib/ngx-mat-shift-selection.service.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class NgxMatShiftSelectionService {
constructor() { }
}
NgxMatShiftSelectionService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
NgxMatShiftSelectionService.ctorParameters = () => [];
/** @nocollapse */ NgxMatShiftSelectionService.ngInjectableDef = ɵɵdefineInjectable({ factory: function NgxMatShiftSelectionService_Factory() { return new NgxMatShiftSelectionService(); }, token: NgxMatShiftSelectionService, providedIn: "root" });
/**
* @fileoverview added by tsickle
* Generated from: lib/ngx-mat-shift-selection.component.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @type {?} */
const moment = _moment;
class NgxMatShiftSelectionComponent {
constructor() {
this.currentDate = moment();
this.dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
this.weeks = [];
this.sortedDates = [];
this.selectAll = false;
this.availableAll = false;
this.time = [
"00:00",
"00:30",
"01:00",
"01:30",
"02:00",
"02:30",
"03:00",
"03:30",
"04:00",
"04:30",
"05:00",
"05:30",
"06:00",
"06:30",
"07:00",
"07:30",
"08:00",
"08:30",
"09:00",
"09:30",
"10:00",
"10:30",
"11:00",
"11:30",
"12:00",
"12:30",
"13:00",
"13:30",
"14:00",
"14:30",
"15:00",
"15:30",
"16:00",
"16:30",
"17:00",
"17:30",
"18:00",
"18:30",
"19:00",
"19:30",
"20:00",
"20:30",
"21:00",
"21:30",
"22:00",
"22:30",
"23:00",
"23:30"
];
this.selectedDates = [];
this.calendarType = "month";
this.class = "";
this.onSelectDate = new EventEmitter();
}
/**
* @return {?}
*/
ngOnInit() {
this.generateCalendar();
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
if (changes.selectedDates &&
changes.selectedDates.currentValue &&
changes.selectedDates.currentValue.length > 1) {
// sort on date changes for better performance when range checking
this.sortedDates = sortBy(changes.selectedDates.currentValue, (/**
* @param {?} m
* @return {?}
*/
(m) => m.mDate.valueOf()));
this.generateCalendar();
}
}
/**
* @param {?} date
* @return {?}
*/
selectDate(date) {
return __awaiter(this, void 0, void 0, function* () {
date.selected = !date.selected;
date.isFullTime = false;
if (!date.selected) {
yield remove(this.selectedDates, (/**
* @param {?} v
* @return {?}
*/
v => {
return v.mDate.format("ll") == date.mDate.format("ll");
}));
}
else {
date.startTime = "";
date.endTime = "";
date.isTimerVisible =
this.weeks[0][1].mDate.week() == date.mDate.week() &&
!this.isPast(date.mDate);
if (!this.isDuplicate(date.mDate))
this.selectedDates.push(date);
}
this.sortedDates = yield sortBy(this.selectedDates, (/**
* @param {?} m
* @return {?}
*/
(m) => m.mDate.valueOf()));
this.selectAll = this.areAllSelected();
this.availableAll = this.areAllAvailable();
this.onSelectDate.emit(this.sortedDates);
});
}
// date checkers
/**
* @param {?} date
* @return {?}
*/
isToday(date) {
return moment().isSame(moment(date), "day");
}
/**
* @param {?} date
* @return {?}
*/
isSelected(date) {
return (findIndex(this.selectedDates, (/**
* @param {?} selectedDate
* @return {?}
*/
selectedDate => {
return moment(date).isSame(selectedDate.mDate, "day");
})) > -1);
}
/**
* @param {?} date
* @return {?}
*/
isDuplicate(date) {
return (findIndex(this.selectedDates, (/**
* @param {?} selectedDate
* @return {?}
*/
selectedDate => {
return moment(date).isSame(selectedDate.mDate, "day");
})) > -1);
}
/**
* @return {?}
*/
areAllSelected() {
for (let v of this.weeks[0]) {
if (!v.selected && !this.isPast(v.mDate))
return false;
}
return true;
}
/**
* @return {?}
*/
areAllAvailable() {
for (let v of this.sortedDates) {
if (!v.isFullTime && v.isTimerVisible)
return false;
}
for (let v of this.weeks[0]) {
if (!v.selected)
return false;
}
return true;
}
/**
* @param {?} date
* @return {?}
*/
isFullTime(date) {
return (findIndex(this.selectedDates, (/**
* @param {?} selectedDate
* @return {?}
*/
selectedDate => {
return (moment(date).isSame(selectedDate.mDate, "day") &&
selectedDate.isFullTime);
})) > -1);
}
/**
* @param {?} date
* @return {?}
*/
isSelectedMonth(date) {
return moment(date).isSame(this.currentDate, "month");
}
/**
* @param {?} date
* @return {?}
*/
isPast(date) {
return moment(date).isBefore(moment().subtract(1, "day"));
}
// actions from calendar
/**
* @return {?}
*/
prevMonth() {
this.currentDate = moment(this.currentDate).subtract(1, "months");
this.generateCalendar();
}
/**
* @return {?}
*/
nextMonth() {
this.currentDate = moment(this.currentDate).add(1, "months");
this.generateCalendar();
}
/**
* @return {?}
*/
nextWeek() {
this.currentDate = moment(this.currentDate).add(1, "week");
this.generateCalendar();
}
/**
* @return {?}
*/
prevWeek() {
this.currentDate = moment(this.currentDate).subtract(1, "week");
this.generateCalendar();
}
/**
* @return {?}
*/
firstMonth() {
this.currentDate = moment(this.currentDate).startOf("year");
this.generateCalendar();
}
/**
* @return {?}
*/
lastMonth() {
this.currentDate = moment(this.currentDate).endOf("year");
this.generateCalendar();
}
/**
* @return {?}
*/
prevYear() {
this.currentDate = moment(this.currentDate).subtract(1, "year");
this.generateCalendar();
}
/**
* @return {?}
*/
nextYear() {
this.currentDate = moment(this.currentDate).add(1, "year");
this.generateCalendar();
}
// generate the calendar grid
/**
* @return {?}
*/
generateCalendar() {
/** @type {?} */
const dates = this.fillDates(this.currentDate);
/** @type {?} */
const weeks = [];
while (dates.length > 0) {
weeks.push(dates.splice(0, 7));
}
for (let v of this.sortedDates) {
v.isTimerVisible =
weeks[0][1].mDate.week() == v.mDate.week() && !this.isPast(v.mDate);
}
this.weeks = weeks;
this.selectAll = this.areAllSelected();
this.availableAll = this.areAllAvailable();
}
/**
* @param {?} currentMoment
* @return {?}
*/
fillDates(currentMoment) {
if (this.calendarType == "week") {
/** @type {?} */
const firstOfWeek = moment(currentMoment)
.startOf("week")
.day();
/** @type {?} */
const firstDayOfGrid = moment(currentMoment)
.startOf("week")
.subtract(firstOfWeek, "days");
/** @type {?} */
const start = firstDayOfGrid.date();
return range(start, start + 7).map((/**
* @param {?} date
* @return {?}
*/
(date) => {
/** @type {?} */
const d = moment(firstDayOfGrid).date(date);
return {
today: this.isToday(d),
selected: this.isSelected(d),
mDate: d,
startTime: undefined,
endTime: undefined,
isTimerVisible: false,
isFullTime: this.isFullTime(d)
};
}));
}
/** @type {?} */
const firstOfMonth = moment(currentMoment)
.startOf("month")
.day();
/** @type {?} */
const firstDayOfGrid = moment(currentMoment)
.startOf("month")
.subtract(firstOfMonth, "days");
/** @type {?} */
const start = firstDayOfGrid.date();
return range(start, start + 35).map((/**
* @param {?} date
* @return {?}
*/
(date) => {
/** @type {?} */
const d = moment(firstDayOfGrid).date(date);
return {
today: this.isToday(d),
selected: this.isSelected(d),
mDate: d,
startTime: undefined,
endTime: undefined,
isTimerVisible: false,
isFullTime: this.isFullTime(d)
};
}));
}
/**
* @param {?} val
* @return {?}
*/
timeChanged(val) {
val.isFullTime =
val.startTime == "00:00" &&
val.endTime == "23:59" &&
this.weeks[0].map((/**
* @param {?} v
* @return {?}
*/
v => {
if (v.mDate.format("ll") == val.mDate.format("ll")) {
v.isFullTime = val.isFullTime;
}
}));
this.availableAll = this.areAllAvailable();
this.onSelectDate.emit(this.sortedDates);
}
/**
* @param {?} val
* @return {?}
*/
fullTimeChecked(val) {
val.startTime = val.isFullTime ? "00:00" : "";
val.endTime = val.isFullTime ? "23:59" : "";
for (let v in this.sortedDates) {
if (this.sortedDates[v].mDate.format("ll") == val.mDate.format("ll")) {
this.sortedDates[v].startTime = val.startTime;
this.sortedDates[v].endTime = val.endTime;
}
}
this.timeChanged(val);
}
/**
* @return {?}
*/
selectAllChecked() {
for (let v of this.weeks[0]) {
v.selected = this.selectAll;
this.selectDate(v);
}
}
/**
* @return {?}
*/
availableAllChecked() {
return __awaiter(this, void 0, void 0, function* () {
this.availableAll = !this.availableAll;
for (let v of this.sortedDates) {
v.isFullTime = v.isTimerVisible ? this.availableAll : v.isFullTime;
v.startTime = v.isFullTime ? "00:00" : "";
v.endTime = v.isFullTime ? "23:59" : "";
this.weeks[0].map((/**
* @param {?} val
* @return {?}
*/
val => {
if (val.mDate.format("ll") == v.mDate.format("ll")) {
val.isFullTime = v.isFullTime;
}
}));
}
this.onSelectDate.emit(this.sortedDates);
});
}
/**
* @return {?}
*/
timeSelected() {
this.onSelectDate.emit(this.sortedDates);
}
}
NgxMatShiftSelectionComponent.decorators = [
{ type: Component, args: [{
selector: "ngx-mat-shift-selection",
template: "<div fxLayout=\"column\" [ngClass]=\"class\" class=\"calendar\">\n <div fxLayout=\"column\" class=\"calendar-navs\" *ngIf=\"calendarType != 'week'\">\n <div class=\"month-nav\" fxLayout=\"row\" fxLayoutAlign=\"space-between center\">\n <button mat-icon-button (click)=\"prevMonth()\">\n <mat-icon>arrow_back_ios</mat-icon>\n </button>\n <span class=\"title p4 mt-1\">{{ currentDate.format(\"MMMM\") }}</span>\n <button mat-icon-button (click)=\"nextMonth()\">\n <mat-icon>arrow_forward_ios</mat-icon>\n </button>\n </div>\n <div class=\"year-nav\" fxLayout=\"row\" fxLayoutAlign=\"space-between center\">\n <button mat-icon-button (click)=\"prevYear()\">\n <mat-icon>arrow_back_ios</mat-icon>\n </button>\n <span class=\"mt-1\">{{ currentDate.format(\"YYYY\") }}</span>\n <button mat-icon-button (click)=\"nextYear()\">\n <mat-icon>arrow_forward_ios</mat-icon>\n </button>\n </div>\n </div>\n <div fxLayout=\"column\" class=\"calendar-navs\" *ngIf=\"calendarType == 'week'\">\n <div class=\"month-nav\" fxLayout=\"row\" fxLayoutAlign=\"space-between center\">\n <button mat-icon-button (click)=\"prevWeek()\">\n <mat-icon>arrow_back_ios</mat-icon>\n </button>\n <span class=\"title p4 mt-1\">{{ currentDate.format(\"MMMM YYYY\") }}</span>\n <button mat-icon-button (click)=\"nextWeek()\">\n <mat-icon>arrow_forward_ios</mat-icon>\n </button>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div fxLayout=\"column\" class=\"month-grid\">\n <div class=\"day-names\" fxLayout=\"row\" fxLayoutAlign=\"space-between center\">\n <div\n fxFlex=\"14\"\n *ngFor=\"let name of dayNames\"\n class=\"day-name p9\"\n style=\"text-align: center;\"\n >\n {{ name }}\n </div>\n </div>\n <div class=\"weeks\" fxLayout=\"column\">\n <div\n *ngFor=\"let week of weeks\"\n class=\"week\"\n style=\"text-align: center;\"\n fxLayout=\"row\"\n fxLayoutAlign=\"space-between center\"\n >\n <ng-container *ngFor=\"let day of week\">\n <div fxFlex=\"14\" class=\"week-date disabled\" *ngIf=\"isPast(day.mDate)\">\n <span class=\"date-text\">{{ day.mDate.date() }}</span>\n </div>\n <div\n fxFlex=\"14\"\n class=\"week-date enabled\"\n *ngIf=\"!isPast(day.mDate)\"\n (click)=\"selectDate(day)\"\n >\n <div\n fxLayout=\"row\"\n fxLayoutAlign=\"center center\"\n [ngClass]=\"{\n today: day.today,\n fullTimeSelected: day.isFullTime,\n selected: day.selected\n }\"\n >\n <span class=\"date-text\">{{ day.mDate.date() }}</span>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div class=\"row justify-content-between p-05\">\n <div class=\"col-12 mb-1\">\n Select All Days\n <mat-checkbox\n [checked]=\"selectAll\"\n (change)=\"selectAllChecked()\"\n ></mat-checkbox>\n </div>\n </div>\n <mat-divider></mat-divider>\n <ng-container *ngIf=\"sortedDates.length > 0\">\n <div class=\"p-05\" fxLayout=\"row\" fxLayoutAlign=\"start center\">\n <div fxFlex=\"50\">Select Available Time</div>\n <div fxFlex=\"50\" class=\"text-right\">\n <mat-checkbox\n [checked]=\"availableAll\"\n (change)=\"availableAllChecked()\"\n >\n </mat-checkbox>\n Available 24 Hours\n </div>\n </div>\n <mat-divider></mat-divider>\n </ng-container>\n <div *ngFor=\"let availableDate of sortedDates\">\n <div class=\"mt-1\" *ngIf=\"availableDate.isTimerVisible\">\n <div fxLayout=\"row\" fxLayoutAlign=\"start center\" class=\"p-05\">\n <div fxFlex=\"50\">{{ availableDate.mDate.format(\"LL\") }}</div>\n <div fxFlex=\"50\" class=\"text-right\">\n <mat-checkbox\n [(ngModel)]=\"availableDate.isFullTime\"\n (ngModelChange)=\"fullTimeChecked(availableDate)\"\n aria-label=\"Checkbox for following text input\"\n >\n </mat-checkbox>\n Available 24 Hours\n </div>\n </div>\n <div\n fxLayout=\"row\"\n fxLayoutAlign=\"start center\"\n *ngIf=\"!availableDate.isFullTime\"\n class=\"p-05\"\n >\n <mat-form-field fxFlex=\"40\">\n <input\n type=\"text\"\n placeholder=\"Start Time\"\n aria-label=\"Number\"\n matInput\n [(ngModel)]=\"availableDate.startTime\"\n [matAutocomplete]=\"auto\"\n (ngModelChange)=\"timeSelected()\"\n />\n <mat-autocomplete #auto=\"matAutocomplete\">\n <mat-option *ngFor=\"let option of time\" [value]=\"option\">\n {{ option }}\n </mat-option>\n </mat-autocomplete>\n </mat-form-field>\n <span class=\"text-center\" fxFlex=\"20\">to</span>\n <mat-form-field fxFlex=\"40\">\n <input\n type=\"text\"\n placeholder=\"End Time\"\n aria-label=\"Number\"\n matInput\n [(ngModel)]=\"availableDate.endTime\"\n [matAutocomplete]=\"auto\"\n (ngModelChange)=\"timeSelected()\"\n />\n <mat-autocomplete #auto=\"matAutocomplete\">\n <mat-option *ngFor=\"let option of time\" [value]=\"option\">\n {{ option }}\n </mat-option>\n </mat-autocomplete>\n </mat-form-field>\n </div>\n <mat-divider></mat-divider>\n </div>\n </div>\n</div>\n",
styles: [".calendar{display:block;min-width:calc(45px * 7);background-color:transparent}.calendar *{box-sizing:border-box}.calendar .calendar-navs{background-color:transparent}.calendar .month-nav{padding:5px;display:flex;flex-direction:row;justify-content:space-between}.calendar .year-nav{padding:5px;display:flex;flex-direction:row;justify-content:space-between;font-family:Montserrat}.calendar .month-grid .day-names{display:flex;flex-direction:row;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.calendar .month-grid .weeks{display:flex;flex-direction:column}.calendar .month-grid .week{display:flex;flex-direction:row}.calendar .month-grid .day-name,.calendar .month-grid .week-date{text-align:center;padding:5px;display:block;width:45px;display:flex;justify-content:center;align-items:center;font-size:1rem}.calendar .month-grid .day-name{color:#28a1e5}.calendar .month-grid .week-date{height:45px;position:relative;border-radius:50%}.calendar .month-grid .week-date .date-text{z-index:10;font-size:1rem;font-family:Montserrat,sans-serif}.calendar .month-grid .week-date::after{content:\"\";height:calc(45px * .9);width:calc(45px * .9);position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);border-radius:50%;transition:background-color 150ms linear,color 150ms linear;z-index:1}.calendar .month-grid .week-date.enabled{cursor:pointer}.calendar .month-grid .week-date.enabled:hover{opacity:.6}.calendar .month-grid .week-date .selected,.calendar .month-grid .week-date .selected:after{background-color:#28a1e5;color:#fff;width:34px;height:34px;border-radius:50%}.calendar .month-grid .week-date .selected:hover:after{background-color:transparent;width:34px;height:34px;border-radius:50%}.calendar .month-grid .week-date .fullTimeSelected,.calendar .month-grid .week-date .fullTimeSelected:after{background-color:#757374!important;color:#fff;width:34px;height:34px;border-radius:50%}.calendar .month-grid .week-date .fullTimeSelected:hover:after{background-color:transparent;width:34px;height:34px;border-radius:50%}.calendar .month-grid .week-date.disabled{opacity:.6}.calendar .month-grid .today{font-weight:700}.to-span{padding-top:2.5rem;margin-left:1rem;margin-right:1rem}.p-05{padding:.5rem}.time-selector{display:inline-flex}.title{font-size:1.2rem;font-weight:700}.hr-margin{margin-top:1px;margin-bottom:1px}.text-center{text-align:center}.text-right{text-align:right}*{font-size:.8rem}"]
}] }
];
/** @nocollapse */
NgxMatShiftSelectionComponent.ctorParameters = () => [];
NgxMatShiftSelectionComponent.propDecorators = {
selectedDates: [{ type: Input }],
calendarType: [{ type: Input }],
class: [{ type: Input }],
onSelectDate: [{ type: Output }]
};
if (false) {
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.currentDate;
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.dayNames;
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.weeks;
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.sortedDates;
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.selectAll;
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.availableAll;
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.time;
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.selectedDates;
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.calendarType;
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.class;
/** @type {?} */
NgxMatShiftSelectionComponent.prototype.onSelectDate;
}
/**
* @record
*/
function CalendarDate() { }
if (false) {
/** @type {?} */
CalendarDate.prototype.mDate;
/** @type {?|undefined} */
CalendarDate.prototype.selected;
/** @type {?|undefined} */
CalendarDate.prototype.today;
/** @type {?} */
CalendarDate.prototype.startTime;
/** @type {?} */
CalendarDate.prototype.endTime;
/** @type {?|undefined} */
CalendarDate.prototype.isTimerVisible;
/** @type {?|undefined} */
CalendarDate.prototype.isFullTime;
}
/**
* @fileoverview added by tsickle
* Generated from: lib/ngx-mat-shift-selection.module.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class NgxMatShiftSelectionModule {
}
NgxMatShiftSelectionModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgxMatShiftSelectionComponent],
imports: [
CommonModule,
FormsModule,
FlexLayoutModule,
MatFormFieldModule,
MatInputModule,
MatAutocompleteModule,
MatCheckboxModule,
MatDividerModule,
MatButtonModule,
MatIconModule
],
exports: [NgxMatShiftSelectionComponent]
},] }
];
/**
* @fileoverview added by tsickle
* Generated from: public-api.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* Generated from: ngx-mat-shift-selection.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
export { NgxMatShiftSelectionComponent, NgxMatShiftSelectionModule, NgxMatShiftSelectionService };
//# sourceMappingURL=ngx-mat-shift-selection.js.map