ngx-bootstrap
Version:
Angular Bootstrap
1,257 lines (1,237 loc) • 213 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, EventEmitter, Output, Input, ChangeDetectionStrategy, Component, HostBinding, ViewChild, Directive, forwardRef, HostListener, Host, 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, getFullYear, getMonth, isDate, parseDate, utcAsLocal } from 'ngx-bootstrap/chronos';
import * as i5 from 'ngx-bootstrap/positioning';
import { PositioningService } from 'ngx-bootstrap/positioning';
import * as i6 from 'ngx-bootstrap/timepicker';
import { TimepickerModule } from 'ngx-bootstrap/timepicker';
import { trigger, state, transition, style, animate } from '@angular/animations';
import { Subscription, BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { MiniStore, MiniState } from 'ngx-bootstrap/mini-ngrx';
import { NgFor, NgIf, NgClass, NgSwitch, NgSwitchCase, AsyncPipe, CommonModule } from '@angular/common';
import * as i2 from 'ngx-bootstrap/tooltip';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import * as i2$1 from 'ngx-bootstrap/component-loader';
import { ComponentLoaderFactory } from 'ngx-bootstrap/component-loader';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
/**
* 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';
/**
* Shows timepicker under datepicker
*/
this.withTimepicker = false;
/**
* Set allowed positions of container.
*/
this.allowedPositions = ['top', 'bottom'];
/**
* Set rule for datepicker closing. If value is true datepicker closes only if date is changed, if user changes only time datepicker doesn't close. It is available only if property withTimepicker is set true
* */
this.keepDatepickerOpened = false;
/**
* Allows keep invalid dates in range. Can be used with minDate, maxDate
* */
this.keepDatesOutOfRules = false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsDatepickerConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsDatepickerConfig, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: 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();
this.selectedTimeSub = new Subscription();
}
set minDate(value) {
this._effects?.setMinDate(value);
}
set maxDate(value) {
this._effects?.setMaxDate(value);
}
set daysDisabled(value) {
this._effects?.setDaysDisabled(value);
}
set datesDisabled(value) {
this._effects?.setDatesDisabled(value);
}
set datesEnabled(value) {
this._effects?.setDatesEnabled(value);
}
set isDisabled(value) {
this._effects?.setDisabled(value);
}
set dateCustomClasses(value) {
this._effects?.setDateCustomClasses(value);
}
set dateTooltipTexts(value) {
this._effects?.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
timeSelectHandler(date, index) { }
// 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 {
static { this.CALCULATE = '[datepicker] calculate dates matrix'; }
static { this.FORMAT = '[datepicker] format datepicker values'; }
static { this.FLAG = '[datepicker] set flags'; }
static { this.SELECT = '[datepicker] select date'; }
static { this.NAVIGATE_OFFSET = '[datepicker] shift view date'; }
static { this.NAVIGATE_TO = '[datepicker] change view date'; }
static { this.SET_OPTIONS = '[datepicker] update render options'; }
static { this.HOVER = '[datepicker] hover date'; }
static { this.CHANGE_VIEWMODE = '[datepicker] switch view mode'; }
static { this.SET_MIN_DATE = '[datepicker] set min date'; }
static { this.SET_MAX_DATE = '[datepicker] set max date'; }
static { this.SET_DAYSDISABLED = '[datepicker] set days disabled'; }
static { this.SET_DATESDISABLED = '[datepicker] set dates disabled'; }
static { this.SET_DATESENABLED = '[datepicker] set dates enabled'; }
static { this.SET_IS_DISABLED = '[datepicker] set is disabled'; }
static { this.SET_DATE_CUSTOM_CLASSES = '[datepicker] set date custom classes'; }
static { this.SET_DATE_TOOLTIP_TEXTS = '[datepicker] set date tooltip texts'; }
static { this.SET_LOCALE = '[datepicker] set datepicker locale'; }
static { this.SELECT_TIME = '[datepicker] select time'; }
static { this.SELECT_RANGE = '[daterangepicker] select dates range'; }
calculate() {
return { type: BsDatepickerActions.CALCULATE };
}
format() {
return { type: BsDatepickerActions.FORMAT };
}
flag() {
return { type: BsDatepickerActions.FLAG };
}
select(date) {
return {
type: BsDatepickerActions.SELECT,
payload: date
};
}
selectTime(date, index) {
return {
type: BsDatepickerActions.SELECT_TIME,
payload: { date, index },
};
}
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
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsDatepickerActions, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsDatepickerActions, providedIn: 'platform' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsDatepickerActions, decorators: [{
type: Injectable,
args: [{ providedIn: 'platform' }]
}] });
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);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsLocaleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsLocaleService, providedIn: 'platform' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsLocaleService, decorators: [{
type: Injectable,
args: [{ providedIn: 'platform' }]
}] });
class BsDatepickerEffects {
constructor(_actions, _localeService) {
this._actions = _actions;
this._localeService = _localeService;
this._subs = [];
}
init(_bsDatepickerStore) {
this._store = _bsDatepickerStore;
return this;
}
/** setters */
setValue(value) {
this._store?.dispatch(this._actions.select(value));
}
setRangeValue(value) {
this._store?.dispatch(this._actions.selectRange(value));
}
setMinDate(value) {
this._store?.dispatch(this._actions.minDate(value));
return this;
}
setMaxDate(value) {
this._store?.dispatch(this._actions.maxDate(value));
return this;
}
setDaysDisabled(value) {
this._store?.dispatch(this._actions.daysDisabled(value));
return this;
}
setDatesDisabled(value) {
this._store?.dispatch(this._actions.datesDisabled(value));
return this;
}
setDatesEnabled(value) {
this._store?.dispatch(this._actions.datesEnabled(value));
return this;
}
setDisabled(value) {
this._store?.dispatch(this._actions.isDisabled(value));
return this;
}
setDateCustomClasses(value) {
this._store?.dispatch(this._actions.setDateCustomClasses(value));
return this;
}
setDateTooltipTexts(value) {
this._store?.dispatch(this._actions.setDateTooltipTexts(value));
return this;
}
/* Set rendering options */
setOptions(_config) {
const _options = Object.assign({ locale: this._localeService.currentLocale }, _config);
this._store?.dispatch(this._actions.setOptions(_options));
return this;
}
/** view to mode bindings */
setBindings(container) {
if (!this._store) {
return this;
}
container.selectedTime = this._store.select(state => state.selectedTime)
.pipe(filter(times => !!times));
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$ = 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) => {
this._store?.dispatch(this._actions.changeViewMode(event));
};
container.navigateTo = (event) => {
this._store?.dispatch(this._actions.navigateStep(event.step));
};
container.dayHoverHandler = (event) => {
const _cell = 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;
};
return this;
}
registerDatepickerSideEffects() {
if (!this._store) {
return this;
}
this._subs.push(this._store.select(state => state.view).subscribe(() => {
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(() => this._store?.dispatch(this._actions.format())));
// flag day values
this._subs.push(this._store
.select(state => state.formattedMonths)
.pipe(filter(month => !!month))
.subscribe(() => this._store?.dispatch(this._actions.flag())));
// flag day values
this._subs.push(this._store
.select(state => state.selectedDate)
.pipe(filter(selectedDate => !!selectedDate))
.subscribe(() => 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(() => 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(() => this._store?.dispatch(this._actions.flag())));
// date custom classes
this._subs.push(this._store
.select(state => state.dateCustomClasses)
.pipe(filter(dateCustomClasses => !!dateCustomClasses))
.subscribe(() => this._store?.dispatch(this._actions.flag())));
// date tooltip texts
this._subs.push(this._store
.select(state => state.dateTooltipTexts)
.pipe(filter(dateTooltipTexts => !!dateTooltipTexts))
.subscribe(() => 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;
}
destroy() {
for (const sub of this._subs) {
sub.unsubscribe();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsDatepickerEffects, deps: [{ token: BsDatepickerActions }, { token: BsLocaleService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsDatepickerEffects, providedIn: 'platform' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: BsDatepickerEffects, decorators: [{
type: Injectable,
args: [{ providedIn: 'platform' }]
}], 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: [],
selectedTime: [],
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, unit) {
if (!datesDisabled || !isArray(datesDisabled) || !datesDisabled.length) {
return false;
}
if (unit && unit === 'year' && !datesDisabled[0].getDate()) {
return datesDisabled.some((dateDisabled) => isSame(date, dateDisabled, 'year'));
}
return datesDisabled.some((dateDisabled) => isSame(date, dateDisabled, 'date'));
}
function isEnabledDate(date, datesEnabled, unit) {
if (!datesEnabled || !isArray(datesEnabled) || !datesEnabled.length) {
return false;
}
return !datesEnabled.some((enabledDate) => isSame(date, enabledDate, unit || 'date'));
}
function getYearsCalendarInitialDate(state, calendarIndex = 0) {
const model = state && state.yearsCalendarModel && state.yearsCalendarModel[calendarIndex];
return 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 setCurrentTimeOnDateSelect(value) {
if (!value)
return value;
return setCurrentTimeHelper(value);
}
function setDateRangesCurrentTimeOnDateSelect(value) {
if (!value?.length)
return value;
value.map((date) => {
if (!date) {
return date;
}
return setCurrentTimeHelper(date);
});
return value;
}
function setCurrentTimeHelper(date) {
const now = new Date();
date.setMilliseconds(now.getMilliseconds());
date.setSeconds(now.getSeconds());
date.setMinutes(now.getMinutes());
date.setHours(now.getHours());
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$1 = 4;
const width$1 = 3;
const shift$1 = { month: 1 };
function formatMonthsCalendar(viewDate, formatOptions) {
const initialDate = startOf(viewDate, 'year');
const matrixOptions = { width: width$1, height: height$1, initialDate, shift: shift$1 };
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 ||
isDisabledDate(month.date, options.datesDisabled) ||
isEnabledDate(month.date, options.datesEnabled, 'month') ||
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 = 4;
const width = 4;
const yearsPerCalendar = height * width;
const initialYearShift = (Math.floor(yearsPerCalendar / 2) - 1) * -1;
const shift = { year: 1 };
function formatYearsCalendar(viewDate, formatOptions, previousInitialDate) {
const initialDate = calculateInitialDate(viewDate, previousInitialDate);
const matrixOptions = { width, height, initialDate, shift };
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][width - 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 ||
isDisabledDate(year.date, options.datesDisabled, 'year') ||
isEnabledDate(year.date, options.datesEnabled, 'year') ||
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 copyTime(sourceDate, time) {
if (!sourceDate || !isNaN(sourceDate.getTime())) {
return;
}
sourceDate.setHours(time.getHours());
sourceDate.setMinutes(time.getMinutes());
sourceDate.setSeconds(time.getSeconds());
sourceDate.setMilliseconds(time.getMilliseconds());
}
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
};
if (Array.isArray(state.selectedTime)) {
const _time = state.selectedTime[0];
if (newState.selectedDate && _time) {
copyTime(newState.selectedDate, _time);
}
}
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.SELECT_TIME: {
const { date, index } = action.payload;
const selectedTime = state.selectedTime ? [...state.selectedTime] : [];
selectedTime[index] = date;
return Object.assign({}, state, { selectedTime });
}
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;
newState.selectedTime = newState.value.map((i) => i);
}
// if new value is a date -> datepicker
if (newState.value instanceof Date) {
newState.selectedDate = newState.value;
newState.selectedTime = [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
};
newState.selectedRange?.forEach((dte, index) => {
if (Array.isArray(state.selectedTime)) {
const _time = state.selectedTime[index];
if (_time) {
copyTime(dte, _time);
}
}
});
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.