ngx-bootstrap-ci
Version:
Native Angular Bootstrap Components
1,474 lines (1,453 loc) • 422 kB
JavaScript
import { Injectable, Component, EventEmitter, Directive, ElementRef, Input, Output, Renderer2, ViewContainerRef, ChangeDetectorRef, forwardRef, Host, ChangeDetectionStrategy, NgModule, ViewChild } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import { getFullYear, getMonth, getDay, isFirstDayOfWeek, isAfter, isBefore, shiftDate, endOf, startOf, isSame, getFirstDayOfMonth, formatDate, getLocale, isDisabledDay, isSameDay, isSameMonth, isSameYear, setFullDate, isArray, isDateValid, parseDate, isDate } from 'ngx-bootstrap/chronos';
import { MiniStore, MiniState } from 'ngx-bootstrap/mini-ngrx';
import { PositioningService } from 'ngx-bootstrap/positioning';
import { ComponentLoaderFactory } from 'ngx-bootstrap/component-loader';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { isBs3 } from 'ngx-bootstrap/utils';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* For date range picker there are `BsDaterangepickerConfig` which inherits all properties,
* except `displayMonths`, for range picker it default to `2`
*/
class BsDatepickerConfig {
constructor() {
/**
* sets use adaptive position
*/
this.adaptivePosition = false;
/**
* CSS class which will be applied to datepicker container,
* usually used to set color theme
*/
this.containerClass = 'theme-green';
// DatepickerRenderOptions
this.displayMonths = 1;
/**
* Allows to hide week numbers in datepicker
*/
this.showWeekNumbers = true;
this.dateInputFormat = 'L';
// range picker
this.rangeSeparator = ' - ';
/**
* Date format for date range input field
*/
this.rangeInputFormat = 'L';
// DatepickerFormatOptions
this.monthTitle = 'MMMM';
this.yearTitle = 'YYYY';
this.dayLabel = 'D';
this.monthLabel = 'MMMM';
this.yearLabel = 'YYYY';
this.weekNumbers = 'w';
}
}
BsDatepickerConfig.decorators = [
{ type: Injectable }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @abstract
*/
class BsDatepickerAbstractComponent {
constructor() {
this._customRangesFish = [];
}
/**
* @param {?} value
* @return {?}
*/
set minDate(value) {
this._effects.setMinDate(value);
}
/**
* @param {?} value
* @return {?}
*/
set maxDate(value) {
this._effects.setMaxDate(value);
}
/**
* @param {?} value
* @return {?}
*/
set daysDisabled(value) {
this._effects.setDaysDisabled(value);
}
/**
* @param {?} value
* @return {?}
*/
set datesDisabled(value) {
this._effects.setDatesDisabled(value);
}
/**
* @param {?} value
* @return {?}
*/
set isDisabled(value) {
this._effects.setDisabled(value);
}
/**
* @param {?} event
* @return {?}
*/
setViewMode(event) { }
/**
* @param {?} event
* @return {?}
*/
navigateTo(event) { }
/**
* @param {?} event
* @return {?}
*/
dayHoverHandler(event) { }
/**
* @param {?} event
* @return {?}
*/
weekHoverHandler(event) { }
/**
* @param {?} event
* @return {?}
*/
monthHoverHandler(event) { }
/**
* @param {?} event
* @return {?}
*/
yearHoverHandler(event) { }
/**
* @param {?} day
* @return {?}
*/
daySelectHandler(day) { }
/**
* @param {?} event
* @return {?}
*/
monthSelectHandler(event) { }
/**
* @param {?} event
* @return {?}
*/
yearSelectHandler(event) { }
/**
* @param {?} event
* @return {?}
*/
_stopPropagation(event) {
event.stopPropagation();
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class BsDatepickerActions {
/**
* @return {?}
*/
calculate() {
return { type: BsDatepickerActions.CALCULATE };
}
/**
* @return {?}
*/
format() {
return { type: BsDatepickerActions.FORMAT };
}
/**
* @return {?}
*/
flag() {
return { type: BsDatepickerActions.FLAG };
}
/**
* @param {?} date
* @return {?}
*/
select(date) {
return {
type: BsDatepickerActions.SELECT,
payload: date
};
}
/**
* @param {?} event
* @return {?}
*/
changeViewMode(event) {
return {
type: BsDatepickerActions.CHANGE_VIEWMODE,
payload: event
};
}
/**
* @param {?} event
* @return {?}
*/
navigateTo(event) {
return {
type: BsDatepickerActions.NAVIGATE_TO,
payload: event
};
}
/**
* @param {?} step
* @return {?}
*/
navigateStep(step) {
return {
type: BsDatepickerActions.NAVIGATE_OFFSET,
payload: step
};
}
/**
* @param {?} options
* @return {?}
*/
setOptions(options) {
return {
type: BsDatepickerActions.SET_OPTIONS,
payload: options
};
}
/**
* @param {?} value
* @return {?}
*/
selectRange(value) {
return {
type: BsDatepickerActions.SELECT_RANGE,
payload: value
};
}
/**
* @param {?} event
* @return {?}
*/
hoverDay(event) {
return {
type: BsDatepickerActions.HOVER,
payload: event.isHovered ? event.cell.date : null
};
}
/**
* @param {?} date
* @return {?}
*/
minDate(date) {
return {
type: BsDatepickerActions.SET_MIN_DATE,
payload: date
};
}
/**
* @param {?} date
* @return {?}
*/
maxDate(date) {
return {
type: BsDatepickerActions.SET_MAX_DATE,
payload: date
};
}
/**
* @param {?} days
* @return {?}
*/
daysDisabled(days) {
return {
type: BsDatepickerActions.SET_DAYSDISABLED,
payload: days
};
}
/**
* @param {?} dates
* @return {?}
*/
datesDisabled(dates) {
return {
type: BsDatepickerActions.SET_DATESDISABLED,
payload: dates
};
}
/**
* @param {?} value
* @return {?}
*/
isDisabled(value) {
return {
type: BsDatepickerActions.SET_IS_DISABLED,
payload: value
};
}
/**
* @param {?} locale
* @return {?}
*/
setLocale(locale) {
return {
type: BsDatepickerActions.SET_LOCALE,
payload: locale
};
}
}
BsDatepickerActions.CALCULATE = '[datepicker] calculate dates matrix';
BsDatepickerActions.FORMAT = '[datepicker] format datepicker values';
BsDatepickerActions.FLAG = '[datepicker] set flags';
BsDatepickerActions.SELECT = '[datepicker] select date';
BsDatepickerActions.NAVIGATE_OFFSET = '[datepicker] shift view date';
BsDatepickerActions.NAVIGATE_TO = '[datepicker] change view date';
BsDatepickerActions.SET_OPTIONS = '[datepicker] update render options';
BsDatepickerActions.HOVER = '[datepicker] hover date';
BsDatepickerActions.CHANGE_VIEWMODE = '[datepicker] switch view mode';
BsDatepickerActions.SET_MIN_DATE = '[datepicker] set min date';
BsDatepickerActions.SET_MAX_DATE = '[datepicker] set max date';
BsDatepickerActions.SET_DAYSDISABLED = '[datepicker] set days disabled';
BsDatepickerActions.SET_DATESDISABLED = '[datepicker] set dates disabled';
BsDatepickerActions.SET_IS_DISABLED = '[datepicker] set is disabled';
BsDatepickerActions.SET_LOCALE = '[datepicker] set datepicker locale';
BsDatepickerActions.SELECT_RANGE = '[daterangepicker] select dates range';
BsDatepickerActions.decorators = [
{ type: Injectable }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class BsLocaleService {
constructor() {
this._defaultLocale = 'en';
this._locale = new BehaviorSubject(this._defaultLocale);
this._localeChange = this._locale.asObservable();
}
/**
* @return {?}
*/
get locale() {
return this._locale;
}
/**
* @return {?}
*/
get localeChange() {
return this._localeChange;
}
/**
* @return {?}
*/
get currentLocale() {
return this._locale.getValue();
}
/**
* @param {?} locale
* @return {?}
*/
use(locale) {
if (locale === this.currentLocale) {
return;
}
this._locale.next(locale);
}
}
BsLocaleService.decorators = [
{ type: Injectable }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class BsDatepickerEffects {
/**
* @param {?} _actions
* @param {?} _localeService
*/
constructor(_actions, _localeService) {
this._actions = _actions;
this._localeService = _localeService;
this._subs = [];
}
/**
* @param {?} _bsDatepickerStore
* @return {?}
*/
init(_bsDatepickerStore) {
this._store = _bsDatepickerStore;
return this;
}
/**
* setters
* @param {?} value
* @return {?}
*/
setValue(value) {
this._store.dispatch(this._actions.select(value));
}
/**
* @param {?} value
* @return {?}
*/
setRangeValue(value) {
this._store.dispatch(this._actions.selectRange(value));
}
/**
* @param {?} value
* @return {?}
*/
setMinDate(value) {
this._store.dispatch(this._actions.minDate(value));
return this;
}
/**
* @param {?} value
* @return {?}
*/
setMaxDate(value) {
this._store.dispatch(this._actions.maxDate(value));
return this;
}
/**
* @param {?} value
* @return {?}
*/
setDaysDisabled(value) {
this._store.dispatch(this._actions.daysDisabled(value));
return this;
}
/**
* @param {?} value
* @return {?}
*/
setDatesDisabled(value) {
this._store.dispatch(this._actions.datesDisabled(value));
return this;
}
/**
* @param {?} value
* @return {?}
*/
setDisabled(value) {
this._store.dispatch(this._actions.isDisabled(value));
return this;
}
/**
* @param {?} _config
* @return {?}
*/
setOptions(_config) {
const /** @type {?} */ _options = Object.assign({ locale: this._localeService.currentLocale }, _config);
this._store.dispatch(this._actions.setOptions(_options));
return this;
}
/**
* view to mode bindings
* @param {?} container
* @return {?}
*/
setBindings(container) {
container.daysCalendar = this._store
.select(state => state.flaggedMonths)
.pipe(filter(months => !!months));
// month calendar
container.monthsCalendar = this._store
.select(state => state.flaggedMonthsCalendar)
.pipe(filter(months => !!months));
// year calendar
container.yearsCalendar = this._store
.select(state => state.yearsCalendarFlagged)
.pipe(filter(years => !!years));
container.viewMode = this._store.select(state => state.view.mode);
container.options = this._store
.select(state => state.showWeekNumbers)
.pipe(map(showWeekNumbers => ({ showWeekNumbers })));
return this;
}
/**
* event handlers
* @param {?} container
* @return {?}
*/
setEventHandlers(container) {
container.setViewMode = (event) => {
this._store.dispatch(this._actions.changeViewMode(event));
};
container.navigateTo = (event) => {
this._store.dispatch(this._actions.navigateStep(event.step));
};
container.dayHoverHandler = (event) => {
const /** @type {?} */ _cell = /** @type {?} */ (event.cell);
if (_cell.isOtherMonth || _cell.isDisabled) {
return;
}
this._store.dispatch(this._actions.hoverDay(event));
_cell.isHovered = event.isHovered;
};
container.monthHoverHandler = (event) => {
event.cell.isHovered = event.isHovered;
};
container.yearHoverHandler = (event) => {
event.cell.isHovered = event.isHovered;
};
container.monthSelectHandler = (event) => {
if (event.isDisabled) {
return;
}
this._store.dispatch(this._actions.navigateTo({
unit: {
month: getMonth(event.date),
year: getFullYear(event.date)
},
viewMode: 'day'
}));
};
container.yearSelectHandler = (event) => {
if (event.isDisabled) {
return;
}
this._store.dispatch(this._actions.navigateTo({
unit: {
year: getFullYear(event.date)
},
viewMode: 'month'
}));
};
return this;
}
/**
* @return {?}
*/
registerDatepickerSideEffects() {
this._subs.push(this._store.select(state => state.view).subscribe(view => {
this._store.dispatch(this._actions.calculate());
}));
// format calendar values on month model change
this._subs.push(this._store
.select(state => state.monthsModel)
.pipe(filter(monthModel => !!monthModel))
.subscribe(month => this._store.dispatch(this._actions.format())));
// flag day values
this._subs.push(this._store
.select(state => state.formattedMonths)
.pipe(filter(month => !!month))
.subscribe(month => this._store.dispatch(this._actions.flag())));
// flag day values
this._subs.push(this._store
.select(state => state.selectedDate)
.pipe(filter(selectedDate => !!selectedDate))
.subscribe(selectedDate => this._store.dispatch(this._actions.flag())));
// flag for date range picker
this._subs.push(this._store
.select(state => state.selectedRange)
.pipe(filter(selectedRange => !!selectedRange))
.subscribe(selectedRange => this._store.dispatch(this._actions.flag())));
// monthsCalendar
this._subs.push(this._store
.select(state => state.monthsCalendar)
.subscribe(() => this._store.dispatch(this._actions.flag())));
// years calendar
this._subs.push(this._store
.select(state => state.yearsCalendarModel)
.pipe(filter(state => !!state))
.subscribe(() => this._store.dispatch(this._actions.flag())));
// on hover
this._subs.push(this._store
.select(state => state.hoveredDate)
.pipe(filter(hoveredDate => !!hoveredDate))
.subscribe(hoveredDate => this._store.dispatch(this._actions.flag())));
// on locale change
this._subs.push(this._localeService.localeChange
.subscribe(locale => this._store.dispatch(this._actions.setLocale(locale))));
return this;
}
/**
* @return {?}
*/
destroy() {
for (const /** @type {?} */ sub of this._subs) {
sub.unsubscribe();
}
}
}
BsDatepickerEffects.decorators = [
{ type: Injectable }
];
/** @nocollapse */
BsDatepickerEffects.ctorParameters = () => [
{ type: BsDatepickerActions, },
{ type: BsLocaleService, },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
const /** @type {?} */ defaultMonthOptions = {
width: 7,
height: 6
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
const /** @type {?} */ _initialView = { date: new Date(), mode: 'day' };
const /** @type {?} */ initialDatepickerState = Object.assign(new BsDatepickerConfig(), {
locale: 'en',
view: _initialView,
selectedRange: [],
monthViewOptions: defaultMonthOptions
});
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @param {?} date
* @param {?} options
* @return {?}
*/
function getStartingDayOfCalendar(date, options) {
if (isFirstDayOfWeek(date, options.firstDayOfWeek)) {
return date;
}
const /** @type {?} */ weekDay = getDay(date);
const /** @type {?} */ offset = calculateDateOffset(weekDay, options.firstDayOfWeek);
return shiftDate(date, { day: -offset });
}
/**
* @param {?} weekday
* @param {?} startingDayOffset
* @return {?}
*/
function calculateDateOffset(weekday, startingDayOffset) {
if (startingDayOffset === 0) {
return weekday;
}
const /** @type {?} */ offset = weekday - startingDayOffset % 7;
return offset < 0 ? offset + 7 : offset;
}
/**
* @param {?} date
* @param {?} min
* @param {?} max
* @return {?}
*/
function isMonthDisabled(date, min, max) {
const /** @type {?} */ minBound = min && isBefore(endOf(date, 'month'), min, 'day');
const /** @type {?} */ maxBound = max && isAfter(startOf(date, 'month'), max, 'day');
return minBound || maxBound;
}
/**
* @param {?} date
* @param {?} min
* @param {?} max
* @return {?}
*/
function isYearDisabled(date, min, max) {
const /** @type {?} */ minBound = min && isBefore(endOf(date, 'year'), min, 'day');
const /** @type {?} */ maxBound = max && isAfter(startOf(date, 'year'), max, 'day');
return minBound || maxBound;
}
/**
* @param {?} date
* @param {?} datesDisabled
* @return {?}
*/
function isDisabledDate(date, datesDisabled) {
if (datesDisabled === undefined || !datesDisabled || !datesDisabled.length) {
return false;
}
return datesDisabled.some((dateDisabled) => isSame(date, dateDisabled, 'date'));
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @template T
* @param {?} options
* @param {?} fn
* @return {?}
*/
function createMatrix(options, fn) {
let /** @type {?} */ prevValue = options.initialDate;
const /** @type {?} */ matrix = new Array(options.height);
for (let /** @type {?} */ i = 0; i < options.height; i++) {
matrix[i] = new Array(options.width);
for (let /** @type {?} */ j = 0; j < options.width; j++) {
matrix[i][j] = fn(prevValue);
prevValue = shiftDate(prevValue, options.shift);
}
}
return matrix;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @param {?} startingDate
* @param {?} options
* @return {?}
*/
function calcDaysCalendar(startingDate, options) {
const /** @type {?} */ firstDay = getFirstDayOfMonth(startingDate);
const /** @type {?} */ initialDate = getStartingDayOfCalendar(firstDay, options);
const /** @type {?} */ matrixOptions = {
width: options.width,
height: options.height,
initialDate,
shift: { day: 1 }
};
const /** @type {?} */ daysMatrix = createMatrix(matrixOptions, date => date);
return {
daysMatrix,
month: firstDay
};
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @param {?} daysCalendar
* @param {?} formatOptions
* @param {?} monthIndex
* @return {?}
*/
function formatDaysCalendar(daysCalendar, formatOptions, monthIndex) {
return {
month: daysCalendar.month,
monthTitle: formatDate(daysCalendar.month, formatOptions.monthTitle, formatOptions.locale),
yearTitle: formatDate(daysCalendar.month, formatOptions.yearTitle, formatOptions.locale),
weekNumbers: getWeekNumbers(daysCalendar.daysMatrix, formatOptions.weekNumbers, formatOptions.locale),
weekdays: getShiftedWeekdays(formatOptions.locale),
weeks: daysCalendar.daysMatrix.map((week, weekIndex) => ({
days: week.map((date, dayIndex) => ({
date,
label: formatDate(date, formatOptions.dayLabel, formatOptions.locale),
monthIndex,
weekIndex,
dayIndex
}))
}))
};
}
/**
* @param {?} daysMatrix
* @param {?} format
* @param {?} locale
* @return {?}
*/
function getWeekNumbers(daysMatrix, format, locale) {
return daysMatrix.map((days) => (days[0] ? formatDate(days[0], format, locale) : ''));
}
/**
* @param {?} locale
* @return {?}
*/
function getShiftedWeekdays(locale) {
const /** @type {?} */ _locale = getLocale(locale);
const /** @type {?} */ weekdays = /** @type {?} */ (_locale.weekdaysShort());
const /** @type {?} */ firstDayOfWeek = _locale.firstDayOfWeek();
return [...weekdays.slice(firstDayOfWeek), ...weekdays.slice(0, firstDayOfWeek)];
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @param {?} formattedMonth
* @param {?} options
* @return {?}
*/
function flagDaysCalendar(formattedMonth, options) {
formattedMonth.weeks.forEach((week) => {
/* tslint:disable-next-line: cyclomatic-complexity */
week.days.forEach((day, dayIndex) => {
// datepicker
const /** @type {?} */ isOtherMonth = !isSameMonth(day.date, formattedMonth.month);
const /** @type {?} */ isHovered = !isOtherMonth && isSameDay(day.date, options.hoveredDate);
// date range picker
const /** @type {?} */ isSelectionStart = !isOtherMonth &&
options.selectedRange &&
isSameDay(day.date, options.selectedRange[0]);
const /** @type {?} */ isSelectionEnd = !isOtherMonth &&
options.selectedRange &&
isSameDay(day.date, options.selectedRange[1]);
const /** @type {?} */ isSelected = (!isOtherMonth && isSameDay(day.date, options.selectedDate)) ||
isSelectionStart ||
isSelectionEnd;
const /** @type {?} */ isInRange = !isOtherMonth &&
options.selectedRange &&
isDateInRange(day.date, options.selectedRange, options.hoveredDate);
const /** @type {?} */ isDisabled = options.isDisabled ||
isBefore(day.date, options.minDate, 'day') ||
isAfter(day.date, options.maxDate, 'day') ||
isDisabledDay(day.date, options.daysDisabled) ||
isDisabledDate(day.date, options.datesDisabled);
const /** @type {?} */ currentDate = new Date();
const /** @type {?} */ isToday = !isOtherMonth && isSameDay(day.date, currentDate);
// decide update or not
const /** @type {?} */ newDay = Object.assign({}, day, {
isOtherMonth,
isHovered,
isSelected,
isSelectionStart,
isSelectionEnd,
isInRange,
isDisabled,
isToday
});
if (day.isOtherMonth !== newDay.isOtherMonth ||
day.isHovered !== newDay.isHovered ||
day.isSelected !== newDay.isSelected ||
day.isSelectionStart !== newDay.isSelectionStart ||
day.isSelectionEnd !== newDay.isSelectionEnd ||
day.isDisabled !== newDay.isDisabled ||
day.isInRange !== newDay.isInRange) {
week.days[dayIndex] = newDay;
}
});
});
// todo: add check for linked calendars
formattedMonth.hideLeftArrow =
options.isDisabled ||
(options.monthIndex > 0 && options.monthIndex !== options.displayMonths);
formattedMonth.hideRightArrow =
options.isDisabled ||
(options.monthIndex < options.displayMonths &&
options.monthIndex + 1 !== options.displayMonths);
formattedMonth.disableLeftArrow = isMonthDisabled(shiftDate(formattedMonth.month, { month: -1 }), options.minDate, options.maxDate);
formattedMonth.disableRightArrow = isMonthDisabled(shiftDate(formattedMonth.month, { month: 1 }), options.minDate, options.maxDate);
return formattedMonth;
}
/**
* @param {?} date
* @param {?} selectedRange
* @param {?} hoveredDate
* @return {?}
*/
function isDateInRange(date, selectedRange, hoveredDate) {
if (!date || !selectedRange[0]) {
return false;
}
if (selectedRange[1]) {
return date > selectedRange[0] && date <= selectedRange[1];
}
if (hoveredDate) {
return date > selectedRange[0] && date <= hoveredDate;
}
return false;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @param {?} mode
* @param {?=} minMode
* @return {?}
*/
function canSwitchMode(mode, minMode) {
return minMode ? mode >= minMode : true;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
const /** @type {?} */ height = 4;
const /** @type {?} */ width = 3;
const /** @type {?} */ shift = { month: 1 };
/**
* @param {?} viewDate
* @param {?} formatOptions
* @return {?}
*/
function formatMonthsCalendar(viewDate, formatOptions) {
const /** @type {?} */ initialDate = startOf(viewDate, 'year');
const /** @type {?} */ matrixOptions = { width, height, initialDate, shift };
const /** @type {?} */ monthMatrix = createMatrix(matrixOptions, date => ({
date,
label: formatDate(date, formatOptions.monthLabel, formatOptions.locale)
}));
return {
months: monthMatrix,
monthTitle: '',
yearTitle: formatDate(viewDate, formatOptions.yearTitle, formatOptions.locale)
};
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @param {?} monthCalendar
* @param {?} options
* @return {?}
*/
function flagMonthsCalendar(monthCalendar, options) {
monthCalendar.months.forEach((months, rowIndex) => {
months.forEach((month, monthIndex) => {
const /** @type {?} */ isHovered = isSameMonth(month.date, options.hoveredMonth);
const /** @type {?} */ isDisabled = options.isDisabled ||
isMonthDisabled(month.date, options.minDate, options.maxDate);
const /** @type {?} */ newMonth = Object.assign(/*{},*/ month, {
isHovered,
isDisabled
});
if (month.isHovered !== newMonth.isHovered ||
month.isDisabled !== newMonth.isDisabled) {
monthCalendar.months[rowIndex][monthIndex] = newMonth;
}
});
});
// todo: add check for linked calendars
monthCalendar.hideLeftArrow =
options.monthIndex > 0 && options.monthIndex !== options.displayMonths;
monthCalendar.hideRightArrow =
options.monthIndex < options.displayMonths &&
options.monthIndex + 1 !== options.displayMonths;
monthCalendar.disableLeftArrow = isYearDisabled(shiftDate(monthCalendar.months[0][0].date, { year: -1 }), options.minDate, options.maxDate);
monthCalendar.disableRightArrow = isYearDisabled(shiftDate(monthCalendar.months[0][0].date, { year: 1 }), options.minDate, options.maxDate);
return monthCalendar;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
const /** @type {?} */ height$1 = 4;
const /** @type {?} */ width$1 = 4;
const /** @type {?} */ yearsPerCalendar = height$1 * width$1;
const /** @type {?} */ initialShift = (Math.floor(yearsPerCalendar / 2) - 1) * -1;
const /** @type {?} */ shift$1 = { year: 1 };
/**
* @param {?} viewDate
* @param {?} formatOptions
* @return {?}
*/
function formatYearsCalendar(viewDate, formatOptions) {
const /** @type {?} */ initialDate = shiftDate(viewDate, { year: initialShift });
const /** @type {?} */ matrixOptions = { width: width$1, height: height$1, initialDate, shift: shift$1 };
const /** @type {?} */ yearsMatrix = createMatrix(matrixOptions, date => ({
date,
label: formatDate(date, formatOptions.yearLabel, formatOptions.locale)
}));
const /** @type {?} */ yearTitle = formatYearRangeTitle(yearsMatrix, formatOptions);
return {
years: yearsMatrix,
monthTitle: '',
yearTitle
};
}
/**
* @param {?} yearsMatrix
* @param {?} formatOptions
* @return {?}
*/
function formatYearRangeTitle(yearsMatrix, formatOptions) {
const /** @type {?} */ from = formatDate(yearsMatrix[0][0].date, formatOptions.yearTitle, formatOptions.locale);
const /** @type {?} */ to = formatDate(yearsMatrix[height$1 - 1][width$1 - 1].date, formatOptions.yearTitle, formatOptions.locale);
return `${from} - ${to}`;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @param {?} yearsCalendar
* @param {?} options
* @return {?}
*/
function flagYearsCalendar(yearsCalendar, options) {
yearsCalendar.years.forEach((years, rowIndex) => {
years.forEach((year, yearIndex) => {
const /** @type {?} */ isHovered = isSameYear(year.date, options.hoveredYear);
const /** @type {?} */ isDisabled = options.isDisabled ||
isYearDisabled(year.date, options.minDate, options.maxDate);
const /** @type {?} */ newMonth = Object.assign(/*{},*/ year, { isHovered, isDisabled });
if (year.isHovered !== newMonth.isHovered ||
year.isDisabled !== newMonth.isDisabled) {
yearsCalendar.years[rowIndex][yearIndex] = newMonth;
}
});
});
// todo: add check for linked calendars
yearsCalendar.hideLeftArrow =
options.yearIndex > 0 && options.yearIndex !== options.displayMonths;
yearsCalendar.hideRightArrow =
options.yearIndex < options.displayMonths &&
options.yearIndex + 1 !== options.displayMonths;
yearsCalendar.disableLeftArrow = isYearDisabled(shiftDate(yearsCalendar.years[0][0].date, { year: -1 }), options.minDate, options.maxDate);
const /** @type {?} */ i = yearsCalendar.years.length - 1;
const /** @type {?} */ j = yearsCalendar.years[i].length - 1;
yearsCalendar.disableRightArrow = isYearDisabled(shiftDate(yearsCalendar.years[i][j].date, { year: 1 }), options.minDate, options.maxDate);
return yearsCalendar;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @param {?=} state
* @param {?=} action
* @return {?}
*/
function bsDatepickerReducer(state = initialDatepickerState, action) {
switch (action.type) {
case BsDatepickerActions.CALCULATE: {
return calculateReducer(state);
}
case BsDatepickerActions.FORMAT: {
return formatReducer(state, action);
}
case BsDatepickerActions.FLAG: {
return flagReducer(state, action);
}
case BsDatepickerActions.NAVIGATE_OFFSET: {
const /** @type {?} */ date = shiftDate(startOf(state.view.date, 'month'), action.payload);
const /** @type {?} */ newState = {
view: {
mode: state.view.mode,
date
}
};
return Object.assign({}, state, newState);
}
case BsDatepickerActions.NAVIGATE_TO: {
const /** @type {?} */ payload = action.payload;
const /** @type {?} */ date = setFullDate(state.view.date, payload.unit);
let /** @type {?} */ newState;
let /** @type {?} */ mode;
if (canSwitchMode(payload.viewMode, state.minMode)) {
mode = payload.viewMode;
newState = { view: { date, mode } };
}
else {
mode = state.view.mode;
newState = { selectedDate: date, view: { date, mode } };
}
return Object.assign({}, state, newState);
}
case BsDatepickerActions.CHANGE_VIEWMODE: {
if (!canSwitchMode(action.payload, state.minMode)) {
return state;
}
const /** @type {?} */ date = state.view.date;
const /** @type {?} */ mode = action.payload;
const /** @type {?} */ newState = { view: { date, mode } };
return Object.assign({}, state, newState);
}
case BsDatepickerActions.HOVER: {
return Object.assign({}, state, { hoveredDate: action.payload });
}
case BsDatepickerActions.SELECT: {
const /** @type {?} */ newState = {
selectedDate: action.payload,
view: state.view
};
const /** @type {?} */ mode = state.view.mode;
const /** @type {?} */ _date = action.payload || state.view.date;
const /** @type {?} */ date = getViewDate(_date, state.minDate, state.maxDate);
newState.view = { mode, date };
return Object.assign({}, state, newState);
}
case BsDatepickerActions.SET_OPTIONS: {
const /** @type {?} */ newState = action.payload;
// preserve view mode
const /** @type {?} */ mode = newState.minMode ? newState.minMode : state.view.mode;
const /** @type {?} */ _viewDate = isDateValid(newState.value) && newState.value
|| isArray(newState.value) && isDateValid(newState.value[0]) && newState.value[0]
|| state.view.date;
const /** @type {?} */ date = getViewDate(_viewDate, newState.minDate, newState.maxDate);
newState.view = { mode, date };
// update selected value
if (newState.value) {
// if new value is array we work with date range
if (isArray(newState.value)) {
newState.selectedRange = newState.value;
}
// if new value is a date -> datepicker
if (newState.value instanceof Date) {
newState.selectedDate = newState.value;
}
// provided value is not supported :)
// need to report it somehow
}
return Object.assign({}, state, newState);
}
// date range picker
case BsDatepickerActions.SELECT_RANGE: {
const /** @type {?} */ newState = {
selectedRange: action.payload,
view: state.view
};
const /** @type {?} */ mode = state.view.mode;
const /** @type {?} */ _date = action.payload && action.payload[0] || state.view.date;
const /** @type {?} */ date = getViewDate(_date, state.minDate, state.maxDate);
newState.view = { mode, date };
return Object.assign({}, state, newState);
}
case BsDatepickerActions.SET_MIN_DATE: {
return Object.assign({}, state, {
minDate: action.payload
});
}
case BsDatepickerActions.SET_MAX_DATE: {
return Object.assign({}, state, {
maxDate: action.payload
});
}
case BsDatepickerActions.SET_IS_DISABLED: {
return Object.assign({}, state, {
isDisabled: action.payload
});
}
default:
return state;
}
}
/**
* @param {?} state
* @return {?}
*/
function calculateReducer(state) {
// how many calendars
const /** @type {?} */ displayMonths = state.displayMonths;
// use selected date on initial rendering if set
let /** @type {?} */ viewDate = state.view.date;
if (state.view.mode === 'day') {
state.monthViewOptions.firstDayOfWeek = getLocale(state.locale).firstDayOfWeek();
const /** @type {?} */ monthsModel = new Array(displayMonths);
for (let /** @type {?} */ monthIndex = 0; monthIndex < displayMonths; monthIndex++) {
// todo: for unlinked calendars it will be harder
monthsModel[monthIndex] = calcDaysCalendar(viewDate, state.monthViewOptions);
viewDate = shiftDate(viewDate, { month: 1 });
}
return Object.assign({}, state, { monthsModel });
}
if (state.view.mode === 'month') {
const /** @type {?} */ monthsCalendar = new Array(displayMonths);
for (let /** @type {?} */ calendarIndex = 0; calendarIndex < displayMonths; calendarIndex++) {
// todo: for unlinked calendars it will be harder
monthsCalendar[calendarIndex] = formatMonthsCalendar(viewDate, getFormatOptions(state));
viewDate = shiftDate(viewDate, { year: 1 });
}
return Object.assign({}, state, { monthsCalendar });
}
if (state.view.mode === 'year') {
const /** @type {?} */ yearsCalendarModel = new Array(displayMonths);
for (let /** @type {?} */ calendarIndex = 0; calendarIndex < displayMonths; calendarIndex++) {
// todo: for unlinked calendars it will be harder
yearsCalendarModel[calendarIndex] = formatYearsCalendar(viewDate, getFormatOptions(state));
viewDate = shiftDate(viewDate, { year: yearsPerCalendar });
}
return Object.assign({}, state, { yearsCalendarModel });
}
return state;
}
/**
* @param {?} state
* @param {?} action
* @return {?}
*/
function formatReducer(state, action) {
if (state.view.mode === 'day') {
const /** @type {?} */ formattedMonths = state.monthsModel.map((month, monthIndex) => formatDaysCalendar(month, getFormatOptions(state), monthIndex));
return Object.assign({}, state, { formattedMonths });
}
// how many calendars
const /** @type {?} */ displayMonths = state.displayMonths;
// check initial rendering
// use selected date on initial rendering if set
let /** @type {?} */ viewDate = state.view.date;
if (state.view.mode === 'month') {
const /** @type {?} */ monthsCalendar = new Array(displayMonths);
for (let /** @type {?} */ calendarIndex = 0; calendarIndex < displayMonths; calendarIndex++) {
// todo: for unlinked calendars it will be harder
monthsCalendar[calendarIndex] = formatMonthsCalendar(viewDate, getFormatOptions(state));
viewDate = shiftDate(viewDate, { year: 1 });
}
return Object.assign({}, state, { monthsCalendar });
}
if (state.view.mode === 'year') {
const /** @type {?} */ yearsCalendarModel = new Array(displayMonths);
for (let /** @type {?} */ calendarIndex = 0; calendarIndex < displayMonths; calendarIndex++) {
// todo: for unlinked calendars it will be harder
yearsCalendarModel[calendarIndex] = formatYearsCalendar(viewDate, getFormatOptions(state));
viewDate = shiftDate(viewDate, { year: 16 });
}
return Object.assign({}, state, { yearsCalendarModel });
}
return state;
}
/**
* @param {?} state
* @param {?} action
* @return {?}
*/
function flagReducer(state, action) {
if (state.view.mode === 'day') {
const /** @type {?} */ flaggedMonths = state.formattedMonths.map((formattedMonth, monthIndex) => flagDaysCalendar(formattedMonth, {
isDisabled: state.isDisabled,
minDate: state.minDate,
maxDate: state.maxDate,
daysDisabled: state.daysDisabled,
datesDisabled: state.datesDisabled,
hoveredDate: state.hoveredDate,
selectedDate: state.selectedDate,
selectedRange: state.selectedRange,
displayMonths: state.displayMonths,
monthIndex
}));
return Object.assign({}, state, { flaggedMonths });
}
if (state.view.mode === 'month') {
const /** @type {?} */ flaggedMonthsCalendar = state.monthsCalendar.map((formattedMonth, monthIndex) => flagMonthsCalendar(formattedMonth, {
isDisabled: state.isDisabled,
minDate: state.minDate,
maxDate: state.maxDate,
hoveredMonth: state.hoveredMonth,
displayMonths: state.displayMonths,
monthIndex
}));
return Object.assign({}, state, { flaggedMonthsCalendar });
}
if (state.view.mode === 'year') {
const /** @type {?} */ yearsCalendarFlagged = state.yearsCalendarModel.map((formattedMonth, yearIndex) => flagYearsCalendar(formattedMonth, {
isDisabled: state.isDisabled,
minDate: state.minDate,
maxDate: state.maxDate,
hoveredYear: state.hoveredYear,
displayMonths: state.displayMonths,
yearIndex
}));
return Object.assign({}, state, { yearsCalendarFlagged });
}
return state;
}
/**
* @param {?} state
* @return {?}
*/
function getFormatOptions(state) {
return {
locale: state.locale,
monthTitle: state.monthTitle,
yearTitle: state.yearTitle,
dayLabel: state.dayLabel,
monthLabel: state.monthLabel,
yearLabel: state.yearLabel,
weekNumbers: state.weekNumbers
};
}
/**
* if view date is provided (bsValue|ngModel) it should be shown
* if view date is not provider:
* if minDate>currentDate (default view value), show minDate
* if maxDate<currentDate(default view value) show maxDate
* @param {?} viewDate
* @param {?} minDate
* @param {?} maxDate
* @return {?}
*/
function getViewDate(viewDate, minDate, maxDate) {
const /** @type {?} */ _date = Array.isArray(viewDate) ? viewDate[0] : viewDate;
if (minDate && isAfter(minDate, _date, 'day')) {
return minDate;
}
if (maxDate && isBefore(maxDate, _date, 'day')) {
return maxDate;
}
return _date;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class BsDatepickerStore extends MiniStore {
constructor() {
const /** @type {?} */ _dispatcher = new BehaviorSubject({
type: '[datepicker] dispatcher init'
});
const /** @type {?} */ state = new MiniState(initialDatepickerState, _dispatcher, bsDatepickerReducer);
super(_dispatcher, bsDatepickerReducer, state);
}
}
BsDatepickerStore.decorators = [
{ type: Injectable }
];
/** @nocollapse */
BsDatepickerStore.ctorParameters = () => [];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class BsDatepickerContainerComponent extends BsDatepickerAbstractComponent {
/**
* @param {?} _config
* @param {?} _store
* @param {?} _actions
* @param {?} _effects
* @param {?} _positionService
*/
constructor(_config, _store, _actions, _effects, _positionService) {
super();
this._config = _config;
this._store = _store;
this._actions = _actions;
this._positionService = _positionService;
this.valueChange = new EventEmitter();
this._subs = [];
this._effects = _effects;
}
/**
* @param {?} value
* @return {?}
*/
set value(value) {
this._effects.setValue(value);
}
/**
* @return {?}
*/
ngOnInit() {
this._positionService.setOptions({
modifiers: {
flip: {
enabled: this._config.adaptivePosition
}
}
});
this.isOtherMonthsActive = this._config.selectFromOtherMonth;
this.containerClass = this._config.containerClass;
this._effects
.init(this._store)
.setOptions(this._config)
.setBindings(this)
.setEventHandlers(this)
.registerDatepickerSideEffects();
// todo: move it somewhere else
// on selected date change
this._subs.push(this._store
.select((state) => state.selectedDate)
.subscribe((date) => this.valueChange.emit(date)));
}
/**
* @param {?} day
* @return {?}
*/
daySelectHandler(day) {
const /** @type {?} */ isDisabled = this.isOtherMonthsActive ? day.isDisabled : (day.isOtherMonth || day.isDisabled);
if (isDisabled) {
return;
}
this._store.dispatch(this._actions.select(day.date));
}
/**
* @return {?}
*/
ngOnDestroy() {
for (const /** @type {?} */ sub of this._subs) {
sub.unsubscribe();
}
this._effects.destroy();
}
}
BsDatepickerContainerComponent.decorators = [
{ type: Component, args: [{
selector: 'bs-datepicker-container',
providers: [BsDatepickerStore, BsDatepickerEffects],
template: "<!-- days calendar view mode -->\n<div class=\"bs-datepicker\" [ngClass]=\"containerClass\" *ngIf=\"viewMode | async\">\n <div class=\"bs-datepicker-container\">\n\n <!--calendars-->\n <div class=\"bs-calendar-container\" [ngSwitch]=\"viewMode | async\" role=\"application\">\n <!--days calendar-->\n <div *ngSwitchCase=\"'day'\" class=\"bs-media-container\">\n <bs-days-calendar-view\n *ngFor=\"let calendar of (daysCalendar | async)\"\n [class.bs-datepicker-multiple]=\"(daysCalendar | async)?.length > 1\"\n [calendar]=\"calendar\"\n [options]=\"options | async\"\n (onNavigate)=\"navigateTo($event)\"\n (onViewMode)=\"setViewMode($event)\"\n (onHover)=\"dayHoverHandler($event)\"\n (onHoverWeek)=\"weekHoverHandler($event)\"\n (onSelect)=\"daySelectHandler($event)\"\n ></bs-days-calendar-view>\n </div>\n\n <!--months calendar-->\n <div *ngSwitchCase=\"'month'\" class=\"bs-media-container\">\n <bs-month-calendar-view\n *ngFor=\"let calendar of (monthsCalendar | async)\"\n [class.bs-datepicker-multiple]=\"(daysCalendar | async)?.length > 1\"\n [calendar]=\"calendar\"\n (onNavigate)=\"navigateTo($event)\"\n (onViewMode)=\"setViewMode($event)\"\n (onHover)=\"monthHoverHandler($event)\"\n (onSelect)=\"monthSelectHandler($event)\"\n ></bs-month-calendar-view>\n </div>\n\n <!--years calendar-->\n <div *ngSwitchCase=\"'year'\" class=\"bs-media-container\">\n <bs-years-calendar-view\n *ngFor=\"let calendar of (yearsCalendar | async)\"\n [class.bs-datepicker-multiple]=\"(daysCalendar | async )?.length > 1\"\n [calendar]=\"calendar\"\n (onNavigate)=\"navigateTo($event)\"\n (onViewMode)=\"setViewMode($event)\"\n (onHover)=\"yearHoverHandler($event)\"\n (onSelect)=\"yearSelectHandler($event)\"\n ></bs-years-calendar-view>\n </div>\n\n </div>\n\n <!--applycancel buttons-->\n <div class=\"bs-datepicker-buttons\" *ngIf=\"false\">\n <button class=\"btn btn-success\">Apply</button>\n <button class=\"btn btn-default\">Cancel</button>\n </div>\n\n </div>\n\n <!--custom dates or date ranges picker-->\n <div class=\"bs-datepicker-custom-range\" *ngIf=\"false\">\n <bs-custom-date-view [ranges]=\"_customRangesFish\"></bs-custom-date-view>\n </div>\n</div>\n",
host: {
'(click)': '_stopPropagation($event)',
style: 'position: absolute; display: block;',
role: 'dialog',
'aria-label': 'calendar'
}
}] }
];
/** @nocollapse */
BsDatepickerContainerComponent.ctorParameters = () => [
{ type: BsDatepickerConfig, },
{ type: BsDatepickerStore, },
{ type: BsDatepickerActions, },
{ type: BsDatepickerEffects, },
{ type: PositioningService, },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class BsDatepickerDirective {
/**
* @param {?} _config
* @param {?} _elementRef
* @param {?} _renderer
* @param {?} _viewContainerRef
* @param {?} cis
*/
constructor(_config, _elementRef, _renderer, _viewContainerRef, cis) {
this._config = _config;
/**
* Placement of a datepicker. Accepts: "top", "bottom", "left", "right"
*/
this.placement = 'bottom';
/**
* Specifies events that should trigger. Supports a space separated list of
* event names.
*/
this.triggers = 'click';
/**
* Close datepicker on outside click
*/
this.outsideClick = true;
/**
* A selector specifying the element the datepicker should be appended to.
* Currently only supports "body".
*/
this.container = 'body';
this.outsideEsc = true;
/**
* Emits when datepicker value has been changed
*/
this.bsValueChange = new EventEmitter();
this._subs = [];
// todo: assign only subset of fields
Object.assign(this, this._config);
this._datepicker = cis.createLoader(_elementRef, _viewContainerRef, _renderer);
this.onShown = this._datepicker.onShown;
this.onHidden = this._datepicker.onHidden;
}
/**
* Returns whether or not the datepicker is currently being shown
* @return {?}
*/
get isOpen() {
return this._datepicker.isShown;
}
/**
* @param {?} value
* @return {?}
*/
set isOpen(value) {
if (value) {
this.show();
}
else {
this.hide();
}
}
/**
* Init