book-picker
Version:
<p align="center"> <img width="409" height="331" src="https://raw.githubusercontent.com/ymxk/book-picker/master/book-picker.gif"> </p>
586 lines (574 loc) • 18.5 kB
JavaScript
import { EventEmitter, Component, Input, Output, Pipe, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MomentModule } from 'ngx-moment';
import moment from 'moment';
import jspath from 'jspath';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class BookPickerComponent {
constructor() {
this.name = 'Book Picker';
this.bookeds = new Array();
this.hours = new Array();
this.selected = new EventEmitter();
this.onerror = new EventEmitter();
}
/**
* @param {?} value
* @return {?}
*/
onSelectedDate(value) {
this.selectedDate = value.clone();
this.timeRange = null;
}
/**
* @param {?} value
* @return {?}
*/
onSelectedTime(value) {
this.timeRange = value;
this.selected.emit(value);
}
/**
* @return {?}
*/
onErrorTime() {
this.onerror.emit();
}
}
BookPickerComponent.decorators = [
{ type: Component, args: [{
selector: 'book-picker',
template: "<app-date-picker (selected)=\"onSelectedDate($event)\"></app-date-picker>\n<app-time-picker [nowTime]=\"selectedDate\" [bookeds]=\"bookeds\" [hours]=\"hours\" (selected)=\"onSelectedTime($event)\" (onerror)=\"onErrorTime()\"></app-time-picker>\n<app-book-info [timeRange]=\"timeRange\"></app-book-info>",
styles: ["p{font-family:Lato}"]
}] }
];
BookPickerComponent.propDecorators = {
bookeds: [{ type: Input }],
hours: [{ type: Input }],
selected: [{ type: Output }],
onerror: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class HoursOfDay {
/**
* @param {?} start
* @param {?} end
*/
constructor(start, end) {
this.start = start;
this.end = end;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @enum {string} */
const TimeClass = {
BOOKED: 'time-booked', DISABLE: 'time-disable', SELECTED: 'time-selected', NORMAL: '',
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class TimePickerComponent {
constructor() {
this.timeCells = [];
this.nowTime = moment();
this.bookeds = new Array();
this.hours = new Array();
this.selected = new EventEmitter();
this.onerror = new EventEmitter();
}
/**
* @return {?}
*/
ngOnInit() {
this.initBooksOrHours();
this.getHoursForDays();
}
/**
* @return {?}
*/
initBooksOrHours() {
if (!this.bookeds) {
this.bookeds = new Array();
}
if (!this.hours) {
this.hours = new Array();
}
}
/**
* @return {?}
*/
onClear() {
this.start = null;
this.end = null;
}
/**
* @return {?}
*/
emitSelected() {
this.selected.emit({ start: this.start, end: this.addHalfHour(this.end) });
}
/**
* @param {?} v
* @return {?}
*/
addHalfHour(v) {
return v ? v.clone().add(30, 'm') : null;
}
/**
* @return {?}
*/
emitError() {
this.onerror.emit();
}
/**
* @param {?} value
* @return {?}
*/
onSelected(value) {
if (this.start && this.end && this.start.isSame(value, 'm') && this.end.isSame(value, 'm')) {
this.onClear();
this.emitSelected();
return false;
}
if (this.start && this.end && this.start.isSame(value, 'm')) {
this.start = this.end;
this.emitSelected();
return false;
}
if (this.start && this.end && this.end.isSame(value, 'm')) {
this.end = this.start;
this.emitSelected();
return false;
}
if (!this.start && !this.end) {
/** @type {?} */
let x = this.includesDisable(value.clone(), value.clone());
if (x) {
this.emitError();
return false;
}
this.start = value;
this.end = value;
this.emitSelected();
return false;
}
if (value.isBefore(this.start)) {
/** @type {?} */
let x = this.includesDisable(value.clone(), this.start.clone());
if (x) {
this.emitError();
return false;
}
this.end = this.start;
this.start = value;
}
if (value.isAfter(this.start)) {
/** @type {?} */
let x = this.includesDisable(this.start.clone(), value.clone());
if (x) {
this.emitError();
return false;
}
this.end = value;
}
this.emitSelected();
}
/**
* @param {?} start
* @param {?} end
* @return {?}
*/
includesDisable(start, end) {
/** @type {?} */
let range = [];
for (let item = start; item.isBefore(end) || item.isSame(end); item.add(30, 'm')) {
range.push(item.clone());
}
return range.filter((/**
* @param {?} e
* @return {?}
*/
e => { return this.includesBooked(e) || this.includesCloses(e); })).length > 0;
}
/**
* @param {?} v
* @return {?}
*/
includesBooked(v) {
return this.bookeds.filter((/**
* @param {?} e
* @return {?}
*/
e => { return this.isBetweenDate(v, e.start, e.end); })).length > 0;
}
/**
* @param {?} v
* @return {?}
*/
includesCloses(v) {
/** @type {?} */
let ph = this.getOpenHoursOnDated();
if (ph && ph.length == 0) {
return true;
}
return ph.filter((/**
* @param {?} e
* @return {?}
*/
e => { return this.isBetweenNotEnd(v, e.opens, e.closes); })).length == 0;
}
/**
* @param {?} v
* @param {?} s
* @param {?} e
* @return {?}
*/
isBetweenNotEnd(v, s, e) {
/** @type {?} */
let vs = this.setHourMinuteIgnorDate(v);
/** @type {?} */
let ss = this.setHourMinuteIgnorDate(s);
/** @type {?} */
let es = this.setHourMinuteIgnorDate(e);
return (vs.isBetween(ss, es, 'm') || vs.isSame(ss, 'm')) ? true : false;
}
/**
* @param {?} v
* @param {?} s
* @param {?} e
* @return {?}
*/
isBetweenDate(v, s, e) {
return v.isBetween(s, e) || v.isSame(s) || v.isSame(e);
}
/**
* @param {?} v
* @param {?} s
* @param {?} e
* @return {?}
*/
isBetweenM(v, s, e) {
return v.isBetween(s, e, 'm') || v.isSame(s, 'm') || v.isSame(e, 'm');
}
/**
* @param {?} value
* @return {?}
*/
getClassBy(value) {
if (this.includesBooked(value)) {
return TimeClass.BOOKED;
}
if (this.includesCloses(value)) {
return TimeClass.DISABLE;
}
if (this.isBetweenM(value, this.start, this.end)) {
return TimeClass.SELECTED;
}
return TimeClass.NORMAL;
}
/**
* @param {?} v
* @return {?}
*/
isSameDay(v) {
return moment().isSame(v, 'month') && moment().isSame(v, 'day');
}
/**
* @return {?}
*/
getDefaultOpenHours() {
return new HoursOfDay(this.nowTime.clone().startOf('day'), this.nowTime.clone().endOf('day'));
}
/**
* @return {?}
*/
nextHalfHourInNow() {
/** @type {?} */
const y = parseFloat(this.nowTime.clone().format('mm')) % 30;
return this.addHalfHour(this.nowTime.clone().subtract(y, 'm'));
}
/**
* @param {?} oh
* @return {?}
*/
replaceStartByNow(oh) {
/** @type {?} */
let start = this.setHourMinuteIgnorDate(oh.start);
if (this.isSameDay(start)) {
return new HoursOfDay(this.nextHalfHourInNow(), oh.end);
}
return oh;
}
/**
* @return {?}
*/
getHoursForDays() {
/** @type {?} */
let oh = this.getOpenHoursOnDated();
if (oh && oh.length > 0) {
this.createHours(this.toHoursOfDayFrom(oh));
}
else {
this.createHours(this.getDefaultOpenHours());
}
}
/**
* @param {?} v
* @return {?}
*/
setHourMinuteIgnorDate(v) {
return this.nowTime.clone().hour(v.hour()).minute(v.minute());
}
/**
* @param {?} oh
* @return {?}
*/
createHours(oh) {
/** @type {?} */
let t = this.replaceStartByNow(oh);
/** @type {?} */
let start = t.start.clone();
/** @type {?} */
let end = t.end.clone();
this.timeCells = [];
for (let item = start; item.isBefore(end); item.add(30, 'm')) {
this.timeCells.push(item.clone());
}
}
/**
* @return {?}
*/
getOpenHoursOnDated() {
return this.hours.filter((/**
* @param {?} e
* @return {?}
*/
(e) => {
return e.weeks.includes(this.nowTime.day());
}));
}
;
/**
* @param {?} ts
* @return {?}
*/
toHoursOfDayFrom(ts) {
/** @type {?} */
const opens = jspath.apply(`.opens`, ts);
/** @type {?} */
const closes = jspath.apply(`.closes`, ts);
return new HoursOfDay(this.setHourMinuteIgnorDate(moment.min(opens)), this.setHourMinuteIgnorDate(moment.max(closes)));
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
for (let propName in changes) {
/** @type {?} */
let changedProp = changes[propName];
if (changedProp.isFirstChange()) {
this.nowTime = moment();
}
else {
this.onClear();
this.nowTime = changedProp.currentValue;
this.getHoursForDays();
}
}
}
}
TimePickerComponent.decorators = [
{ type: Component, args: [{
selector: 'app-time-picker',
template: "<section class=\"card\">\n\n\t<div *ngFor=\"let c of timeCells\" [ngClass]=\"getClassBy(c)\">\n\t\t<div (click)=\"onSelected(c)\">{{c | amDateFormat: 'HH:mm'}}</div>\n\t</div>\n</section>",
styles: ["@charset \"UTF-8\";.card{display:-webkit-flex;display:flex;min-width:100%;min-height:54px;overflow-x:auto;border-top:1px solid #e9e9e9;border-bottom:1px solid #e9e9e9}.card>div{min-width:55px;height:54px;background-color:#fff;text-align:center;border-left:1px solid #e9e9e9}.card>div>div:first-child{font-size:13px;font-family:PingFangSC-Regular;font-weight:400;color:#242424;line-height:54px}.card>div:last-child{border-right:1px solid #e9e9e9}.card::-webkit-scrollbar{display:none}.time-disable div{background:#fbfbfb;color:#c4c4c4!important;line-height:14px!important;padding:20px 0 0 2px}.time-disable ::after{content:\"\\a\u672A\u8425\u4E1A\";white-space:pre-wrap;font-size:10px;color:#c4c4c4}.time-selected div{background:#e5f2ff!important;color:#007aff!important;border-left:0!important}.time-booked div{background:#fbfbfb;color:#c4c4c4!important;line-height:14px!important;padding:20px 0 0 2px}.time-booked ::after{content:\"\\a\u5DF2\u9884\u8BA2\";white-space:pre-wrap;font-size:10px;color:#c4c4c4}"]
}] }
];
/** @nocollapse */
TimePickerComponent.ctorParameters = () => [];
TimePickerComponent.propDecorators = {
nowTime: [{ type: Input }],
bookeds: [{ type: Input }],
hours: [{ type: Input }],
selected: [{ type: Output }],
onerror: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class DatePickerComponent {
constructor() {
this.days = [];
this.selectedDate = moment();
this.endDayForMonth = moment().add(15, 'd');
this.selected = new EventEmitter();
}
/**
* @return {?}
*/
ngOnInit() {
this.getDaysForMonth();
}
/**
* @param {?} value
* @return {?}
*/
onSelected(value) {
this.selectedDate = value;
this.selected.emit(value);
}
/**
* @param {?} value
* @return {?}
*/
isSameDay(value) {
return this.selectedDate.isSame(value, 'day') ? 'date-selected' : '';
}
/**
* @return {?}
*/
getDaysForMonth() {
for (let item = moment(); item.isBefore(this.endDayForMonth); item.add(1, 'd')) {
this.days.push(item.clone());
}
}
}
DatePickerComponent.decorators = [
{ type: Component, args: [{
selector: 'app-date-picker',
template: "<div class=\"date-title\">{{selectedDate | amDateFormat: 'YYYY'}}</div>\n<section class=\"card\">\n\t<div class=\"card--content\" *ngFor=\"let day of days\" [ngClass]=\"isSameDay(day)\" (click)=\"onSelected(day)\">\n\t\t<div>{{day | weekdays}}</div>\n\t\t<div>{{day | amDateFormat: 'M/D'}}</div>\n\t</div>\n</section>",
styles: [".card{display:-webkit-flex;display:flex;min-width:100%;min-height:86px;overflow-x:auto}.card::-webkit-scrollbar{display:none}.card--content{min-width:55px;height:86px;background-color:#fff;text-align:center}.card--content div:first-child{height:18px;font-size:13px;font-family:PingFangSC-Regular;font-weight:400;color:#999;line-height:18px;margin:19px 0 10px}.card--content div:last-child{height:21px;line-height:21px;font-size:15px;font-family:PingFangSC-Medium;font-weight:500;color:#242424}.date-title{width:100%;height:46px;line-height:46px;background:#f8f8f8;font-size:17px;font-family:PingFangSC-Medium;font-weight:1000;color:#242424;text-align:center}.date-selected div{color:#007aff!important}"]
}] }
];
/** @nocollapse */
DatePickerComponent.ctorParameters = () => [];
DatePickerComponent.propDecorators = {
selected: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class BookInfoComponent {
constructor() { }
/**
* @return {?}
*/
ngOnInit() {
}
}
BookInfoComponent.decorators = [
{ type: Component, args: [{
selector: 'app-book-info',
template: "<div class=\"book-info\" *ngIf=\"timeRange && timeRange.start && timeRange.end\">\n\t<label>\u5171\u8BA1 {{timeRange | booktime}} \u5C0F\u65F6</label>\n <div><i class=\"iconfont icon-clock\"></i> {{timeRange | timetable}} </div>\n</div>",
styles: [".book-info{padding:17px 20px}.book-info label{width:90px;background:#f8f8f8;border-radius:2px;font-size:13px;font-family:PingFangSC-Regular;font-weight:400;color:#999;line-height:23px;display:inline-block;text-align:center}.book-info div:last-child{height:24px;line-height:24px;font-size:15px;font-family:PingFangSC-Regular;font-weight:400;color:#242424;margin-top:10px}"]
}] }
];
/** @nocollapse */
BookInfoComponent.ctorParameters = () => [];
BookInfoComponent.propDecorators = {
timeRange: [{ type: Input }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class WeekdaysPipe {
constructor() {
this.now = moment();
this.weekdays = [
"日", "一", "二", "三", "四", "五", "六"
];
}
/**
* @param {?} value
* @return {?}
*/
transform(value) {
if (!value) {
return '';
}
return this.now.isSame(value, 'day') ? '今日' : this.weekdays[value.day()];
}
}
WeekdaysPipe.decorators = [
{ type: Pipe, args: [{
name: 'weekdays'
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class TimetablePipe {
/**
* @param {?} value
* @return {?}
*/
transform(value) {
return (value && value.end && value.start) ? this.formatTime(value) : this.defaultFormatTime();
}
/**
* @param {?} value
* @return {?}
*/
formatTime(value) {
return `${value.start.clone().format('M月D日 HH:mm')}~${value.end.clone().format('HH:mm')}`;
}
/**
* @return {?}
*/
defaultFormatTime() {
return `${moment().format('M月D日 HH:mm')}~${moment().format('HH:mm')}`;
}
}
TimetablePipe.decorators = [
{ type: Pipe, args: [{
name: 'timetable'
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class BooktimePipe {
/**
* @param {?} value
* @return {?}
*/
transform(value) {
return (value && value.end && value.start) ? `${moment.duration(value.end.diff(value.start)).asHours()}` : '0';
}
}
BooktimePipe.decorators = [
{ type: Pipe, args: [{
name: 'booktime'
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class BookPickerModule {
}
BookPickerModule.decorators = [
{ type: NgModule, args: [{
imports: [CommonModule, MomentModule],
declarations: [TimePickerComponent, DatePickerComponent, BookInfoComponent, WeekdaysPipe, TimetablePipe, BooktimePipe, BookPickerComponent],
exports: [BookPickerComponent]
},] }
];
export { BookPickerComponent, BookPickerModule, TimePickerComponent as ɵa, DatePickerComponent as ɵb, BookInfoComponent as ɵc, WeekdaysPipe as ɵd, TimetablePipe as ɵe, BooktimePipe as ɵf };
//# sourceMappingURL=book-picker.js.map