kendo-multi-calendar
Version:
Kendo UI calendar with multiple date selection.
194 lines (148 loc) • 5.12 kB
text/typescript
declare namespace kendo {
namespace ui {
interface Calendar {
_todayClick(e: CalendarEvent): void;
}
}
}
namespace kendoExt {
export type CalendarDepth = 'month' | 'year' | 'decade' | 'century';
export interface MultiCalendarOptions extends kendo.ui.CalendarOptions {
values?: Date[];
maxSelectedItems?: number | null;
cleanSelectedItemsOnTodayClick?: boolean;
}
export class MultiCalendar extends kendo.ui.Calendar {
public static fn: MultiCalendar;
private static _views: { [key: string]: number } = {
month: 0,
year: 1,
decade: 2,
century: 3
};
public options: MultiCalendarOptions;
private _values: Date[];
constructor(element: Element | JQuery | string, options?: MultiCalendarOptions) {
super(element as Element, options);
this._values = (options && options.values) || [];
(this as any).navigate();
}
private static isSameDate(first: Date, second: Date) {
return (
first &&
second &&
first.getFullYear() === second.getFullYear() &&
first.getMonth() === second.getMonth() &&
first.getDate() === second.getDate()
);
}
public values(newValues?: Date[]): Date[] {
if (newValues !== undefined && this.canSelectItems(newValues.length)) {
const valuesToClear = this._values.filter(value => {
return newValues.every(newValue => !MultiCalendar.isSameDate(value, newValue));
});
let i = null;
this._values = newValues.filter((item, index) => {
for (i = index - 1; i >= 0; i -= 1) {
if (MultiCalendar.isSameDate(item, newValues[i])) {
return false;
}
}
return true;
});
this.updateSelection(this._values, valuesToClear);
}
return this._values;
}
public value(newValue?: Date | string): Date {
const valueIndex: number = (this._values || [])
.reduce(
(acc, val, index) => (MultiCalendar.isSameDate(val, newValue as Date) ? index : acc),
-1
);
const shouldAddNewValue = valueIndex === -1;
const canAddNewValue =
shouldAddNewValue && this._values && this.canSelectItems(this._values.length + 1);
if (!shouldAddNewValue || canAddNewValue) {
super.value(newValue as Date);
}
if (newValue) {
let valuesToClear: Date[] = [];
if (canAddNewValue) {
this._values.push(newValue as Date);
} else if (!shouldAddNewValue) {
valuesToClear = this._values.splice(valueIndex, 1);
}
this.updateSelection(this._values, valuesToClear);
}
return super.value();
}
public navigate(value: Date, view: string) {
super.navigate(value, view);
this.updateSelection(this._values);
}
public navigateDown(value: Date) {
if (!value) {
return;
}
const index: number = (this as any)._index;
if (index === MultiCalendar._views[this.options.depth as string]) {
this.value(value);
this.trigger('change');
return;
}
this.navigate(value, (index - 1) as any);
}
public _todayClick(e: kendo.ui.CalendarEvent) {
this.selectToday();
super._todayClick(e);
}
private canSelectItems(amount: number): boolean {
const max = this.options.maxSelectedItems;
return !max || amount <= max;
}
private cellByDate(value: string): JQuery {
return (this as any)._table.find('td').filter(function () {
return $(this.firstChild).attr((kendo as any).attr('value')) === value;
});
}
private updateSelection(valuesToSelect: Date[], valuesToClear: Date[] = []) {
if ((this as any)._index === MultiCalendar._views[this.options.depth as string]) {
const selected = 'k-state-selected';
const _value = (this as any)._value;
if (_value) {
this.cellByDate(this.view().toDateString(_value)).removeClass(selected);
}
valuesToSelect.forEach(value => {
this.cellByDate(this.view().toDateString(value)).addClass(selected);
});
valuesToClear.forEach(value => {
this.cellByDate(this.view().toDateString(value)).removeClass(selected);
});
}
}
private selectToday = () => {
const today = new Date();
const value = new Date(today.getFullYear(), today.getMonth(), today.getDate());
const values = this.options.cleanSelectedItemsOnTodayClick
? [value]
: [...this._values, value];
this.values(values);
}
}
MultiCalendar.fn = MultiCalendar.prototype;
MultiCalendar.fn.options = {
...kendo.ui.Calendar.fn.options,
...{
name: 'MultiCalendar',
values: [],
maxSelectedItems: null,
cleanSelectedItemsOnTodayClick: true
}
};
kendo.ui.plugin(MultiCalendar);
}
interface JQuery {
kendoMultiCalendar(options?: kendoExt.MultiCalendarOptions): JQuery;
data(key: 'kendoMultiCalendar'): kendoExt.MultiCalendar;
}