ngx-bootstrap
Version:
Angular Bootstrap
1,273 lines (1,254 loc) • 174 kB
JavaScript
import { ɵɵdefineInjectable, Injectable, EventEmitter, Component, Renderer2, ElementRef, Directive, ViewContainerRef, Input, Output, forwardRef, Host, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, NgModule } from '@angular/core';
import { filter, map, take, takeUntil, distinctUntilChanged } from 'rxjs/operators';
import { isFirstDayOfWeek, getDay, shiftDate, isBefore, endOf, isAfter, startOf, isArray, isSame, getFirstDayOfMonth, formatDate, getLocale, isSameMonth, isSameDay, isDisabledDay, isSameYear, isDateValid, setFullDate, getMonth, getFullYear, isDate, parseDate, utcAsLocal } from 'ngx-bootstrap/chronos';
import { PositioningService } from 'ngx-bootstrap/positioning';
import { trigger, state, style, transition, animate } from '@angular/animations';
import { Subscription, BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { MiniStore, MiniState } from 'ngx-bootstrap/mini-ngrx';
import { ComponentLoaderFactory } from 'ngx-bootstrap/component-loader';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, FormsModule } from '@angular/forms';
import { isBs3 } from 'ngx-bootstrap/utils';
import { CommonModule } from '@angular/common';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
/**
* 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;
/** sets use UTC date time format */
this.useUtc = false;
/** turn on/off animation */
this.isAnimated = false;
/**
* The view that the datepicker should start in
*/
this.startView = 'day';
/**
* If true, returns focus to the datepicker / daterangepicker input after date selection
*/
this.returnFocusToInput = 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';
/**
* Shows 'today' button
*/
this.showTodayButton = false;
/**
* Shows clear button
*/
this.showClearButton = false;
/**
* Positioning of 'today' button
*/
this.todayPosition = 'center';
/**
* Positioning of 'clear' button
*/
this.clearPosition = 'right';
/**
* Label for 'today' button
*/
this.todayButtonLabel = 'Today';
/**
* Label for 'clear' button
*/
this.clearButtonLabel = 'Clear';
/**
* Label for 'custom range' button
*/
this.customRangeButtonLabel = 'Custom Range';
}
}
BsDatepickerConfig.ɵprov = ɵɵdefineInjectable({ factory: function BsDatepickerConfig_Factory() { return new BsDatepickerConfig(); }, token: BsDatepickerConfig, providedIn: "root" });
BsDatepickerConfig.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
const DATEPICKER_ANIMATION_TIMING = '220ms cubic-bezier(0, 0, 0.2, 1)';
const datepickerAnimation = trigger('datepickerAnimation', [
state('animated-down', style({ height: '*', overflow: 'hidden' })),
transition('* => animated-down', [
style({ height: 0, overflow: 'hidden' }),
animate(DATEPICKER_ANIMATION_TIMING)
]),
state('animated-up', style({ height: '*', overflow: 'hidden' })),
transition('* => animated-up', [
style({ height: '*', overflow: 'hidden' }),
animate(DATEPICKER_ANIMATION_TIMING)
]),
transition('* => unanimated', animate('0s'))
]);
class BsDatepickerAbstractComponent {
constructor() {
this.containerClass = '';
this.customRanges = [];
this.chosenRange = [];
this._daysCalendarSub = new Subscription();
}
set minDate(value) {
var _a;
(_a = this._effects) === null || _a === void 0 ? void 0 : _a.setMinDate(value);
}
set maxDate(value) {
var _a;
(_a = this._effects) === null || _a === void 0 ? void 0 : _a.setMaxDate(value);
}
set daysDisabled(value) {
var _a;
(_a = this._effects) === null || _a === void 0 ? void 0 : _a.setDaysDisabled(value);
}
set datesDisabled(value) {
var _a;
(_a = this._effects) === null || _a === void 0 ? void 0 : _a.setDatesDisabled(value);
}
set datesEnabled(value) {
var _a;
(_a = this._effects) === null || _a === void 0 ? void 0 : _a.setDatesEnabled(value);
}
set isDisabled(value) {
var _a;
(_a = this._effects) === null || _a === void 0 ? void 0 : _a.setDisabled(value);
}
set dateCustomClasses(value) {
var _a;
(_a = this._effects) === null || _a === void 0 ? void 0 : _a.setDateCustomClasses(value);
}
set dateTooltipTexts(value) {
var _a;
(_a = this._effects) === null || _a === void 0 ? void 0 : _a.setDateTooltipTexts(value);
}
set daysCalendar$(value) {
this._daysCalendar$ = value;
this._daysCalendarSub.unsubscribe();
this._daysCalendarSub.add(this._daysCalendar$.subscribe(value => {
this.multipleCalendars = !!value && value.length > 1;
}));
}
get daysCalendar$() {
return this._daysCalendar$;
}
// todo: valorkin fix
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function
setViewMode(event) { }
// eslint-disable-next-line
navigateTo(event) { }
// eslint-disable-next-line
dayHoverHandler(event) { }
// eslint-disable-next-line
weekHoverHandler(event) { }
// eslint-disable-next-line
monthHoverHandler(event) { }
// eslint-disable-next-line
yearHoverHandler(event) { }
// eslint-disable-next-line
daySelectHandler(day) { }
// eslint-disable-next-line
monthSelectHandler(event) { }
// eslint-disable-next-line
yearSelectHandler(event) { }
// eslint-disable-next-line
setRangeOnCalendar(dates) { }
// eslint-disable-next-line
setToday() { }
// eslint-disable-next-line
clearDate() { }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_stopPropagation(event) {
event.stopPropagation();
}
}
class BsDatepickerActions {
calculate() {
return { type: BsDatepickerActions.CALCULATE };
}
format() {
return { type: BsDatepickerActions.FORMAT };
}
flag() {
return { type: BsDatepickerActions.FLAG };
}
select(date) {
return {
type: BsDatepickerActions.SELECT,
payload: date
};
}
changeViewMode(event) {
return {
type: BsDatepickerActions.CHANGE_VIEWMODE,
payload: event
};
}
navigateTo(event) {
return {
type: BsDatepickerActions.NAVIGATE_TO,
payload: event
};
}
navigateStep(step) {
return {
type: BsDatepickerActions.NAVIGATE_OFFSET,
payload: step
};
}
setOptions(options) {
return {
type: BsDatepickerActions.SET_OPTIONS,
payload: options
};
}
// date range picker
selectRange(value) {
return {
type: BsDatepickerActions.SELECT_RANGE,
payload: value
};
}
hoverDay(event) {
return {
type: BsDatepickerActions.HOVER,
payload: event.isHovered ? event.cell.date : null
};
}
minDate(date) {
return {
type: BsDatepickerActions.SET_MIN_DATE,
payload: date
};
}
maxDate(date) {
return {
type: BsDatepickerActions.SET_MAX_DATE,
payload: date
};
}
daysDisabled(days) {
return {
type: BsDatepickerActions.SET_DAYSDISABLED,
payload: days
};
}
datesDisabled(dates) {
return {
type: BsDatepickerActions.SET_DATESDISABLED,
payload: dates
};
}
datesEnabled(dates) {
return {
type: BsDatepickerActions.SET_DATESENABLED,
payload: dates
};
}
isDisabled(value) {
return {
type: BsDatepickerActions.SET_IS_DISABLED,
payload: value
};
}
setDateCustomClasses(value) {
return {
type: BsDatepickerActions.SET_DATE_CUSTOM_CLASSES,
payload: value
};
}
setDateTooltipTexts(value) {
return {
type: BsDatepickerActions.SET_DATE_TOOLTIP_TEXTS,
payload: value
};
}
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_DATESENABLED = '[datepicker] set dates enabled';
BsDatepickerActions.SET_IS_DISABLED = '[datepicker] set is disabled';
BsDatepickerActions.SET_DATE_CUSTOM_CLASSES = '[datepicker] set date custom classes';
BsDatepickerActions.SET_DATE_TOOLTIP_TEXTS = '[datepicker] set date tooltip texts';
BsDatepickerActions.SET_LOCALE = '[datepicker] set datepicker locale';
BsDatepickerActions.SELECT_RANGE = '[daterangepicker] select dates range';
BsDatepickerActions.decorators = [
{ type: Injectable }
];
class BsLocaleService {
constructor() {
this._defaultLocale = 'en';
this._locale = new BehaviorSubject(this._defaultLocale);
this._localeChange = this._locale.asObservable();
}
get locale() {
return this._locale;
}
get localeChange() {
return this._localeChange;
}
get currentLocale() {
return this._locale.getValue();
}
use(locale) {
if (locale === this.currentLocale) {
return;
}
this._locale.next(locale);
}
}
BsLocaleService.decorators = [
{ type: Injectable }
];
class BsDatepickerEffects {
constructor(_actions, _localeService) {
this._actions = _actions;
this._localeService = _localeService;
this._subs = [];
}
init(_bsDatepickerStore) {
this._store = _bsDatepickerStore;
return this;
}
/** setters */
setValue(value) {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.select(value));
}
setRangeValue(value) {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.selectRange(value));
}
setMinDate(value) {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.minDate(value));
return this;
}
setMaxDate(value) {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.maxDate(value));
return this;
}
setDaysDisabled(value) {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.daysDisabled(value));
return this;
}
setDatesDisabled(value) {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.datesDisabled(value));
return this;
}
setDatesEnabled(value) {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.datesEnabled(value));
return this;
}
setDisabled(value) {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.isDisabled(value));
return this;
}
setDateCustomClasses(value) {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.setDateCustomClasses(value));
return this;
}
setDateTooltipTexts(value) {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.setDateTooltipTexts(value));
return this;
}
/* Set rendering options */
setOptions(_config) {
var _a;
const _options = Object.assign({ locale: this._localeService.currentLocale }, _config);
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.setOptions(_options));
return this;
}
/** view to mode bindings */
setBindings(container) {
if (!this._store) {
return this;
}
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 => { var _a; return (_a = state.view) === null || _a === void 0 ? void 0 : _a.mode; });
container.options$ = combineLatest([
this._store.select(state => state.showWeekNumbers),
this._store.select(state => state.displayMonths)
])
.pipe(map((latest) => ({
showWeekNumbers: latest[0],
displayMonths: latest[1]
})));
return this;
}
/** event handlers */
setEventHandlers(container) {
container.setViewMode = (event) => {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.changeViewMode(event));
};
container.navigateTo = (event) => {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.navigateStep(event.step));
};
container.dayHoverHandler = (event) => {
var _a;
const _cell = event.cell;
if (_cell.isOtherMonth || _cell.isDisabled) {
return;
}
(_a = this._store) === null || _a === void 0 ? void 0 : _a.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;
};
return this;
}
registerDatepickerSideEffects() {
if (!this._store) {
return this;
}
this._subs.push(this._store.select(state => state.view).subscribe(() => {
var _a;
(_a = this._store) === null || _a === void 0 ? void 0 : _a.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(() => { var _a; return (_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.format()); }));
// flag day values
this._subs.push(this._store
.select(state => state.formattedMonths)
.pipe(filter(month => !!month))
.subscribe(() => { var _a; return (_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.flag()); }));
// flag day values
this._subs.push(this._store
.select(state => state.selectedDate)
.pipe(filter(selectedDate => !!selectedDate))
.subscribe(() => { var _a; return (_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.flag()); }));
// flag for date range picker
this._subs.push(this._store
.select(state => state.selectedRange)
.pipe(filter(selectedRange => !!selectedRange))
.subscribe(() => { var _a; return (_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.flag()); }));
// monthsCalendar
this._subs.push(this._store
.select(state => state.monthsCalendar)
.subscribe(() => { var _a; return (_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.flag()); }));
// years calendar
this._subs.push(this._store
.select(state => state.yearsCalendarModel)
.pipe(filter(state => !!state))
.subscribe(() => { var _a; return (_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.flag()); }));
// on hover
this._subs.push(this._store
.select(state => state.hoveredDate)
.pipe(filter(hoveredDate => !!hoveredDate))
.subscribe(() => { var _a; return (_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.flag()); }));
// date custom classes
this._subs.push(this._store
.select(state => state.dateCustomClasses)
.pipe(filter(dateCustomClasses => !!dateCustomClasses))
.subscribe(() => { var _a; return (_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.flag()); }));
// date tooltip texts
this._subs.push(this._store
.select(state => state.dateTooltipTexts)
.pipe(filter(dateTooltipTexts => !!dateTooltipTexts))
.subscribe(() => { var _a; return (_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.flag()); }));
// on locale change
this._subs.push(this._localeService.localeChange
.subscribe(locale => { var _a; return (_a = this._store) === null || _a === void 0 ? void 0 : _a.dispatch(this._actions.setLocale(locale)); }));
return this;
}
destroy() {
for (const sub of this._subs) {
sub.unsubscribe();
}
}
}
BsDatepickerEffects.decorators = [
{ type: Injectable }
];
BsDatepickerEffects.ctorParameters = () => [
{ type: BsDatepickerActions },
{ type: BsLocaleService }
];
const defaultMonthOptions = {
width: 7,
height: 6
};
const dayInMilliseconds = 24 * 60 * 60 * 1000;
class BsDatepickerState {
constructor() {
// DatepickerRenderOptions
this.showWeekNumbers = true;
this.displayMonths = 1;
}
}
const _initialView = { date: new Date(), mode: 'day' };
const initialDatepickerState = Object.assign(new BsDatepickerConfig(), {
locale: 'en',
view: _initialView,
selectedRange: [],
monthViewOptions: defaultMonthOptions
});
function getStartingDayOfCalendar(date, options) {
if (isFirstDayOfWeek(date, options.firstDayOfWeek)) {
return date;
}
const weekDay = getDay(date);
const offset = calculateDateOffset(weekDay, options.firstDayOfWeek);
return shiftDate(date, { day: -offset });
}
function calculateDateOffset(weekday, startingDayOffset) {
const _startingDayOffset = Number(startingDayOffset);
if (isNaN(_startingDayOffset)) {
return 0;
}
if (_startingDayOffset === 0) {
return weekday;
}
const offset = weekday - _startingDayOffset % 7;
return offset < 0 ? offset + 7 : offset;
}
function isMonthDisabled(date, min, max) {
const minBound = min && isBefore(endOf(date, 'month'), min, 'day');
const maxBound = max && isAfter(startOf(date, 'month'), max, 'day');
return minBound || maxBound || false;
}
function isYearDisabled(date, min, max) {
const minBound = min && isBefore(endOf(date, 'year'), min, 'day');
const maxBound = max && isAfter(startOf(date, 'year'), max, 'day');
return minBound || maxBound || false;
}
function isDisabledDate(date, datesDisabled) {
if (!datesDisabled || !isArray(datesDisabled) || !datesDisabled.length) {
return false;
}
return datesDisabled.some((dateDisabled) => isSame(date, dateDisabled, 'date'));
}
function isEnabledDate(date, datesEnabled) {
if (!datesEnabled || !isArray(datesEnabled) || !datesEnabled.length) {
return false;
}
return !datesEnabled.some((enabledDate) => isSame(date, enabledDate, 'date'));
}
function getYearsCalendarInitialDate(state, calendarIndex = 0) {
const model = state && state.yearsCalendarModel && state.yearsCalendarModel[calendarIndex];
return (model === null || model === void 0 ? void 0 : model.years[0]) && model.years[0][0] && model.years[0][0].date;
}
function checkRangesWithMaxDate(ranges, maxDate) {
if (!ranges)
return ranges;
if (!maxDate)
return ranges;
if (!ranges.length && !ranges[0].value)
return ranges;
ranges.forEach((item) => {
if (!item || !item.value)
return ranges;
if (item.value instanceof Date)
return ranges;
if (!(item.value instanceof Array && item.value.length))
return ranges;
item.value = compareDateWithMaxDateHelper(item.value, maxDate);
return ranges;
});
return ranges;
}
function checkBsValue(date, maxDate) {
if (!date)
return date;
if (!maxDate)
return date;
if (date instanceof Array && !date.length)
return date;
if (date instanceof Date)
return date;
return compareDateWithMaxDateHelper(date, maxDate);
}
function compareDateWithMaxDateHelper(date, maxDate) {
if (date instanceof Array) {
const editedValues = date.map(item => {
if (!item)
return item;
if (isAfter(item, maxDate, 'date'))
item = maxDate;
return item;
});
return editedValues;
}
return date;
}
function createMatrix(options, fn) {
let prevValue = options.initialDate;
const matrix = new Array(options.height);
for (let i = 0; i < options.height; i++) {
matrix[i] = new Array(options.width);
for (let j = 0; j < options.width; j++) {
matrix[i][j] = fn(prevValue);
prevValue = shiftDate(prevValue, options.shift);
}
}
return matrix;
}
// user and model input should handle parsing and validating input values
function calcDaysCalendar(startingDate, options) {
const firstDay = getFirstDayOfMonth(startingDate);
const initialDate = getStartingDayOfCalendar(firstDay, options);
// todo test
const matrixOptions = {
width: options.width || 0,
height: options.height || 0,
initialDate,
shift: { day: 1 }
};
const daysMatrix = createMatrix(matrixOptions, date => date);
return {
daysMatrix,
month: firstDay
};
}
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
}))
})),
hideLeftArrow: false,
hideRightArrow: false,
disableLeftArrow: false,
disableRightArrow: false
};
}
function getWeekNumbers(daysMatrix, format, locale) {
return daysMatrix.map((days) => (days[0] ? formatDate(days[0], format, locale) : ''));
}
function getShiftedWeekdays(locale) {
const _locale = getLocale(locale);
const weekdays = _locale.weekdaysShort();
const firstDayOfWeek = _locale.firstDayOfWeek();
return [...weekdays.slice(firstDayOfWeek), ...weekdays.slice(0, firstDayOfWeek)];
}
function flagDaysCalendar(formattedMonth, options) {
formattedMonth.weeks.forEach((week) => {
week.days.forEach((day, dayIndex) => {
// datepicker
const isOtherMonth = !isSameMonth(day.date, formattedMonth.month);
const isHovered = !isOtherMonth && isSameDay(day.date, options.hoveredDate);
// date range picker
const isSelectionStart = !isOtherMonth &&
options.selectedRange &&
isSameDay(day.date, options.selectedRange[0]);
const isSelectionEnd = !isOtherMonth &&
options.selectedRange &&
isSameDay(day.date, options.selectedRange[1]);
const isSelected = (!isOtherMonth && isSameDay(day.date, options.selectedDate)) ||
isSelectionStart ||
isSelectionEnd;
const isInRange = !isOtherMonth &&
options.selectedRange &&
isDateInRange(day.date, options.selectedRange, options.hoveredDate);
const 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) ||
isEnabledDate(day.date, options.datesEnabled);
const currentDate = new Date();
const isToday = !isOtherMonth && isSameDay(day.date, currentDate);
const customClasses = options.dateCustomClasses && options.dateCustomClasses
.map(dcc => isSameDay(day.date, dcc.date) ? dcc.classes : [])
.reduce((previousValue, currentValue) => previousValue.concat(currentValue), [])
.join(' ')
|| '';
const tooltipText = options.dateTooltipTexts && options.dateTooltipTexts
.map(tt => isSameDay(day.date, tt.date) ? tt.tooltipText : '')
.reduce((previousValue, currentValue) => {
previousValue.push(currentValue);
return previousValue;
}, [])
.join(' ')
|| '';
// decide update or not
const newDay = Object.assign({}, day, {
isOtherMonth,
isHovered,
isSelected,
isSelectionStart,
isSelectionEnd,
isInRange,
isDisabled,
isToday,
customClasses,
tooltipText
});
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 ||
day.customClasses !== newDay.customClasses ||
day.tooltipText !== newDay.tooltipText) {
week.days[dayIndex] = newDay;
}
});
});
// todo: add check for linked calendars
formattedMonth.hideLeftArrow =
options.isDisabled ||
(!!options.monthIndex && options.monthIndex > 0 && options.monthIndex !== options.displayMonths);
formattedMonth.hideRightArrow =
options.isDisabled ||
((!!options.monthIndex || options.monthIndex === 0) && !!options.displayMonths && 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;
}
function isDateInRange(date, selectedRange, hoveredDate) {
if (!date || !selectedRange || !selectedRange[0]) {
return false;
}
if (selectedRange[1]) {
return date > selectedRange[0] && date <= selectedRange[1];
}
if (hoveredDate) {
return date > selectedRange[0] && date <= hoveredDate;
}
return false;
}
function canSwitchMode(mode, minMode) {
return minMode ? mode >= minMode : true;
}
const height = 4;
const width = 3;
const shift = { month: 1 };
function formatMonthsCalendar(viewDate, formatOptions) {
const initialDate = startOf(viewDate, 'year');
const matrixOptions = { width, height, initialDate, shift };
const monthMatrix = createMatrix(matrixOptions, date => ({
date,
label: formatDate(date, formatOptions.monthLabel, formatOptions.locale)
}));
return {
months: monthMatrix,
monthTitle: '',
yearTitle: formatDate(viewDate, formatOptions.yearTitle, formatOptions.locale),
hideRightArrow: false,
hideLeftArrow: false,
disableRightArrow: false,
disableLeftArrow: false
};
}
function flagMonthsCalendar(monthCalendar, options) {
monthCalendar.months.forEach((months, rowIndex) => {
months.forEach((month, monthIndex) => {
let isSelected;
const isHovered = isSameMonth(month.date, options.hoveredMonth);
const isDisabled = options.isDisabled ||
isMonthDisabled(month.date, options.minDate, options.maxDate);
if (!options.selectedDate && options.selectedRange) {
isSelected = isSameMonth(month.date, options.selectedRange[0]);
if (!isSelected) {
isSelected = isSameMonth(month.date, options.selectedRange[1]);
}
}
else {
isSelected = isSameMonth(month.date, options.selectedDate);
}
const newMonth = Object.assign(/*{},*/ month, {
isHovered,
isDisabled,
isSelected
});
if (month.isHovered !== newMonth.isHovered ||
month.isDisabled !== newMonth.isDisabled ||
month.isSelected !== newMonth.isSelected) {
monthCalendar.months[rowIndex][monthIndex] = newMonth;
}
});
});
// todo: add check for linked calendars
monthCalendar.hideLeftArrow =
!!options.monthIndex && options.monthIndex > 0 && options.monthIndex !== options.displayMonths;
monthCalendar.hideRightArrow =
(!!options.monthIndex || options.monthIndex === 0)
&& (!!options.displayMonths || options.displayMonths === 0)
&& 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;
}
const height$1 = 4;
const width$1 = 4;
const yearsPerCalendar = height$1 * width$1;
const initialYearShift = (Math.floor(yearsPerCalendar / 2) - 1) * -1;
const shift$1 = { year: 1 };
function formatYearsCalendar(viewDate, formatOptions, previousInitialDate) {
const initialDate = calculateInitialDate(viewDate, previousInitialDate);
const matrixOptions = { width: width$1, height: height$1, initialDate, shift: shift$1 };
const yearsMatrix = createMatrix(matrixOptions, date => ({
date,
label: formatDate(date, formatOptions.yearLabel, formatOptions.locale)
}));
const yearTitle = formatYearRangeTitle(yearsMatrix, formatOptions);
return {
years: yearsMatrix,
monthTitle: '',
yearTitle,
hideLeftArrow: false,
hideRightArrow: false,
disableLeftArrow: false,
disableRightArrow: false
};
}
function calculateInitialDate(viewDate, previousInitialDate) {
if (previousInitialDate
&& viewDate.getFullYear() >= previousInitialDate.getFullYear()
&& viewDate.getFullYear() < previousInitialDate.getFullYear() + yearsPerCalendar) {
return previousInitialDate;
}
return shiftDate(viewDate, { year: initialYearShift });
}
function formatYearRangeTitle(yearsMatrix, formatOptions) {
const from = formatDate(yearsMatrix[0][0].date, formatOptions.yearTitle, formatOptions.locale);
const to = formatDate(yearsMatrix[height$1 - 1][width$1 - 1].date, formatOptions.yearTitle, formatOptions.locale);
return `${from} - ${to}`;
}
function flagYearsCalendar(yearsCalendar, options) {
yearsCalendar.years.forEach((years, rowIndex) => {
years.forEach((year, yearIndex) => {
let isSelected;
const isHovered = isSameYear(year.date, options.hoveredYear);
const isDisabled = options.isDisabled ||
isYearDisabled(year.date, options.minDate, options.maxDate);
if (!options.selectedDate && options.selectedRange) {
isSelected = isSameYear(year.date, options.selectedRange[0]);
if (!isSelected) {
isSelected = isSameYear(year.date, options.selectedRange[1]);
}
}
else {
isSelected = isSameYear(year.date, options.selectedDate);
}
const newMonth = Object.assign(/*{},*/ year, { isHovered, isDisabled, isSelected });
if (year.isHovered !== newMonth.isHovered ||
year.isDisabled !== newMonth.isDisabled ||
year.isSelected !== newMonth.isSelected) {
yearsCalendar.years[rowIndex][yearIndex] = newMonth;
}
});
});
// todo: add check for linked calendars
yearsCalendar.hideLeftArrow =
!!options.yearIndex && options.yearIndex > 0 && options.yearIndex !== options.displayMonths;
yearsCalendar.hideRightArrow =
!!options.yearIndex && !!options.displayMonths &&
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 i = yearsCalendar.years.length - 1;
const j = yearsCalendar.years[i].length - 1;
yearsCalendar.disableRightArrow = isYearDisabled(shiftDate(yearsCalendar.years[i][j].date, { year: 1 }), options.minDate, options.maxDate);
return yearsCalendar;
}
function bsDatepickerReducer(state = initialDatepickerState, action) {
switch (action.type) {
case BsDatepickerActions.CALCULATE: {
return calculateReducer(state);
}
case BsDatepickerActions.FORMAT: {
return formatReducer(state);
}
case BsDatepickerActions.FLAG: {
return flagReducer(state);
}
case BsDatepickerActions.NAVIGATE_OFFSET: {
return navigateOffsetReducer(state, action);
}
case BsDatepickerActions.NAVIGATE_TO: {
const payload = action.payload;
if (!state.view || !payload.unit) {
return state;
}
const date = setFullDate(state.view.date, payload.unit);
let newState;
let 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) || !state.view) {
return state;
}
const date = state.view.date;
const mode = action.payload;
const newState = { view: { date, mode } };
return Object.assign({}, state, newState);
}
case BsDatepickerActions.HOVER: {
return Object.assign({}, state, { hoveredDate: action.payload });
}
case BsDatepickerActions.SELECT: {
if (!state.view) {
return state;
}
const newState = {
selectedDate: action.payload,
view: state.view
};
const mode = state.view.mode;
const _date = action.payload || state.view.date;
const date = getViewDate(_date, state.minDate, state.maxDate);
newState.view = { mode, date };
return Object.assign({}, state, newState);
}
case BsDatepickerActions.SET_OPTIONS: {
if (!state.view) {
return state;
}
const newState = action.payload;
// preserve view mode
const mode = newState.minMode ? newState.minMode : state.view.mode;
const _viewDate = isDateValid(newState.value) && newState.value
|| isArray(newState.value) && isDateValid(newState.value[0]) && newState.value[0]
|| state.view.date;
const 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: {
if (!state.view) {
return state;
}
const newState = {
selectedRange: action.payload,
view: state.view
};
const mode = state.view.mode;
const _date = action.payload && action.payload[0] || state.view.date;
const 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
});
}
case BsDatepickerActions.SET_DATE_CUSTOM_CLASSES: {
return Object.assign({}, state, {
dateCustomClasses: action.payload
});
}
case BsDatepickerActions.SET_DATE_TOOLTIP_TEXTS: {
return Object.assign({}, state, {
dateTooltipTexts: action.payload
});
}
default:
return state;
}
}
function calculateReducer(state) {
if (!state.view) {
return state;
}
// how many calendars
let displayMonths;
if (state.displayOneMonthRange &&
isDisplayOneMonth(state.view.date, state.minDate, state.maxDate)) {
displayMonths = 1;
}
else {
displayMonths = state.displayMonths || 1;
}
// use selected date on initial rendering if set
let viewDate = state.view.date;
if (state.view.mode === 'day' && state.monthViewOptions) {
if (state.showPreviousMonth && state.selectedRange && state.selectedRange.length === 0) {
viewDate = shiftDate(viewDate, { month: -1 });
}
state.monthViewOptions.firstDayOfWeek = getLocale(state.locale).firstDayOfWeek();
let monthsModel = new Array(displayMonths);
for (let monthIndex = 0; monthIndex < displayMonths; monthIndex++) {
// todo: for unlinked calendars it will be harder
monthsModel[monthIndex] = calcDaysCalendar(viewDate, state.monthViewOptions);
viewDate = shiftDate(viewDate, { month: 1 });
}
// Check if parameter enabled and check if it's not months navigation event
if (state.preventChangeToNextMonth && state.flaggedMonths && state.hoveredDate) {
const viewMonth = calcDaysCalendar(state.view.date, state.monthViewOptions);
// Check if viewed right month same as in flaggedMonths state, then override months model with flaggedMonths
if (state.flaggedMonths.length && state.flaggedMonths[1].month.getMonth() === viewMonth.month.getMonth()) {
monthsModel = state.flaggedMonths
.map(item => {
if (state.monthViewOptions) {
return calcDaysCalendar(item.month, state.monthViewOptions);
}
return null;
})
.filter(item => item !== null);
}
}
return Object.assign({}, state, { monthsModel });
}
if (state.view.mode === 'month') {
const monthsCalendar = new Array(displayMonths);
for (let 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 yearsCalendarModel = new Array(displayMonths);
for (let calendarIndex = 0; calendarIndex < displayMonths; calendarIndex++) {
// todo: for unlinked calendars it will be harder
yearsCalendarModel[calendarIndex] = formatYearsCalendar(viewDate, getFormatOptions(state), state.minMode === 'year' ? getYearsCalendarInitialDate(state, calendarIndex) : undefined);
viewDate = shiftDate(viewDate, { year: yearsPerCalendar });
}
return Object.assign({}, state, { yearsCalendarModel });
}
return state;
}
function formatReducer(state) {
if (!state.view) {
return state;
}
if (state.view.mode === 'day' && state.monthsModel) {
const formattedMonths = state.monthsModel.map((month, monthIndex) => formatDaysCalendar(month, getFormatOptions(state), monthIndex));
return Object.assign({}, state, { formattedMonths });
}
// how many calendars
const displayMonths = state.displayMonths || 1;
// check initial rendering
// use selected date on initial rendering if set
let viewDate = state.view.date;
if (state.view.mode === 'month') {
const monthsCalendar = new Array(displayMonths);
for (let 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 yearsCalendarModel = new Array(displayMonths);
for (let 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;
}
function flagReducer(state) {
if (!state.view) {
return state;
}
const displayMonths = isDisplayOneMonth(state.view.date, state.minDate, state.maxDate) ? 1 : state.displayMonths;
if (state.formattedMonths && state.view.mode === 'day') {
const flaggedMonths = state.formattedMonths.map((formattedMonth, monthIndex) => flagDaysCalendar(formattedMonth, {
isDisabled: state.isDisabled,
minDate: state.minDate,
maxDate: state.maxDate,
daysDisabled: state.daysDisabled,
datesDisabled: state.datesDisabled,
datesEnabled: state.datesEnabled,
hoveredDate: state.hoveredDate,
selectedDate: state.selectedDate,
selectedRange: state.selectedRange,
displayMonths,
dateCustomClasses: state.dateCustomClasses,
dateTooltipTexts: state.dateTooltipTexts,
monthIndex
}));
return Object.assign({}, state, { flaggedMonths });
}
if (state.view.mode === 'month' && state.monthsCalendar) {
const flaggedMonthsCalendar = state.monthsCalendar.map((formattedMonth, monthIndex) => flagMonthsCalendar(formattedMonth, {
isDisabled: state.isDisabled,
minDate: state.minDate,
maxDate: state.maxDate,
hoveredMonth: state.hoveredMonth,
selectedDate: state.selectedDate,
selectedRange: state.selectedRange,
displayMonths,
monthIndex
}));
return Object.assign({}, state, { flaggedMonthsCalendar });
}
if (state.view.mode === 'year' && state.yearsCalendarModel) {
const yearsCalendarFlagged = state.yearsCalendarModel.map((formattedMonth, yearIndex) => flagYearsCalendar(formattedMonth, {
isDisabled: state.isDisabled,
minDate: state.minDate,
maxDate: state.maxDate,
hoveredYear: state.hoveredYear,
selectedDate: state.selectedDate,
selectedRange: state.selectedRange,
displayMonths,
yearIndex
}));
return Object.assign({}, state, { yearsCalendarFlagged });
}
return state;
}
function navigateOffsetReducer(state, action) {
if (!state.view) {
return state;
}
const date = shiftViewDate(state, action);
if (!date) {
return state;
}
const newState = {
view: {
mode: state.view.mode,
date
}
};
return Object.assign({}, state, newState);
}
function shiftViewDate(state, action) {
if (!state.view) {
return undefined;
}
if (state.view.mode === 'year' && state.minMode === 'year') {
const initialDate = getYearsCalendarInitialDate(state, 0);
if (initialDate) {
const middleDate = shiftDate(initialDate, { year: -initialYearShift });
return shiftDate(middleDate, action.payload);
}
}
return shiftDate(startOf(state.view.date, 'month'), action.payload);
}
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
*/
function getViewDate(viewDate, minDate, maxDate) {
const _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;
}
function isDisplayOneMonth(viewDate, minDate, maxDate) {
if (maxDate && isSame(maxDate,