@progress/kendo-angular-gantt
Version:
Kendo UI Angular Gantt
202 lines (201 loc) • 8.42 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { orderBy } from '@progress/kendo-data-query';
import { isWorkDay, isWorkHour, lastDayOfWeek } from '../utils';
import { addDays, firstDayInWeek, getDate, firstDayOfMonth, lastDayOfMonth, addMonths, addWeeks, lastMonthOfYear, cloneDate } from '@progress/kendo-date-math';
const setTime = (origin, candidate) => {
const date = cloneDate(origin);
date.setHours(candidate.getHours(), candidate.getMinutes(), candidate.getSeconds(), candidate.getMilliseconds());
return date;
};
/**
* @hidden
*/
export const DAY_FORMAT = 'E d/MM';
/**
* @hidden
*/
export const HOUR_FORMAT = 'HH:mm aa';
/**
* @hidden
*/
export const MONTH_FORMAT = 'MMM';
/**
* @hidden
*/
export const YEAR_FORMAT = 'yyyy';
const END_OF_DAY_DATE = new Date(1980, 0, 1, 23, 59, 59, 999);
/**
* @hidden
*/
export class TimelineBaseViewService {
intlService;
mapper;
options;
_viewStart = 0;
constructor(intlService, mapper) {
this.intlService = intlService;
this.mapper = mapper;
}
get viewStart() {
return this._viewStart;
}
/**
*
* @param tasks - The tasks which are going to be rendered in the table
* @returns {Object} - An object containing the range start and end dates
*/
getRange(tasks) {
if (!tasks || !tasks.length) {
return { start: new Date(), end: new Date() };
}
const startResult = orderBy(tasks, [{ field: this.mapper.taskFields.start, dir: 'asc' }]);
const endResult = orderBy(tasks, [{ field: this.mapper.taskFields.end, dir: 'desc' }]);
const rangeStart = new Date(this.mapper.extractFromTask(startResult[0], 'start'));
const rangeEnd = new Date(this.mapper.extractFromTask(endResult[0], 'end'));
const start = this.getStartOffset(rangeStart);
const end = this.getEndOffset(rangeEnd);
this._viewStart = Number(start);
return {
start: new Date(start),
end: new Date(end)
};
}
/**
*
* @param start - The tasks' range start date
* @param end - The tasks' range end date
* @returns {Array<Object>} - A collection containing the hour slots
*/
getHours(start, end, customDateFormat) {
const slots = [];
const workDayStart = this.intlService.parseDate(this.options.workDayStart).getHours();
const workDayEnd = this.intlService.parseDate(this.options.workDayEnd).getHours();
// TODO: retrieve from option?
const hourSpan = 1;
let startDate = new Date(start);
const hoursFormat = customDateFormat ? customDateFormat : HOUR_FORMAT;
const endDate = new Date(end);
while (startDate < endDate) {
const slotEnd = new Date(startDate);
const isWorkSlot = isWorkHour(slotEnd, workDayStart, workDayEnd);
slotEnd.setHours(slotEnd.getHours() + hourSpan);
slots.push({
start: startDate,
end: slotEnd,
isWorking: isWorkSlot,
text: this.intlService.formatDate(startDate, hoursFormat, this.intlService.localeId),
span: 1,
slotWidth: this.options.slotWidth
});
startDate = slotEnd;
}
return slots;
}
/**
*
* @param start - The tasks' range start date
* @param end - The tasks' range end date
* @returns {Array<Object>} - A collection containing the day slots
*/
getDays(start, end, customDateFormat) {
const slots = [];
let startDay = new Date(start);
const dayFormat = customDateFormat ? customDateFormat : DAY_FORMAT;
const endDay = new Date(end);
while (startDay <= endDay) {
// Get the days with cleared time values (except for start and end day)
const nextDay = getDate(addDays(startDay, 1));
const isWorking = isWorkDay(startDay, this.options.workWeekStart, this.options.workWeekEnd);
// Need to get the end date with correct hours (important for day view)
const slotEnd = endDay < nextDay ? endDay : nextDay;
slots.push({
start: startDay,
end: slotEnd,
isWorking: isWorking,
text: this.intlService.formatDate(startDay, dayFormat, this.intlService.localeId),
span: 1,
slotWidth: this.options.slotWidth
});
startDay = nextDay;
}
return slots;
}
getWeeks(start, end, customDateFormat) {
const weekStart = this.intlService.firstDay();
const slots = [];
let startDay = new Date(start);
const dayFormat = customDateFormat ? customDateFormat : DAY_FORMAT;
const endDay = new Date(end);
while (startDay <= endDay) {
const lastWeekDay = lastDayOfWeek(startDay, weekStart);
const slotEnd = lastWeekDay > endDay ? endDay : lastWeekDay;
const daySlots = this.getDays(startDay, slotEnd);
const span = daySlots.length;
const firstDay = this.intlService.formatDate(firstDayInWeek(getDate(startDay), weekStart), dayFormat, this.intlService.localeId);
const lastDay = this.intlService.formatDate(slotEnd, dayFormat, this.intlService.localeId);
if (span > 0) {
slots.push({
start: daySlots[0].start,
end: setTime(daySlots[span - 1].end, END_OF_DAY_DATE),
text: `${firstDay} - ${lastDay}`,
span: span,
slotWidth: this.options.slotWidth
});
}
startDay = firstDayInWeek(addWeeks(slotEnd, 1), weekStart);
}
return slots;
}
getMonths(start, end, isMainViewType, customDateFormat) {
const slots = [];
let startDay = new Date(start);
const monthFormat = customDateFormat ? customDateFormat : MONTH_FORMAT;
const endDay = new Date(end);
while (startDay < endDay) {
const endMonth = lastDayOfMonth(startDay);
const slotEnd = endDay < endMonth ? endDay : endMonth;
const daySlots = this.getDays(startDay, slotEnd);
const weekSlots = this.getWeeks(startDay, slotEnd);
const span = isMainViewType ? daySlots.length : weekSlots.length;
const monthStart = firstDayOfMonth(getDate(startDay));
const shortText = this.intlService.formatDate(monthStart, monthFormat, this.intlService.localeId);
if (span > 0) {
slots.push({
start: isMainViewType ? daySlots[0].start : weekSlots[0].start,
end: isMainViewType ? daySlots[span - 1] : setTime(weekSlots[span - 1].end, END_OF_DAY_DATE),
span: span,
text: shortText,
slotWidth: this.options.slotWidth
});
}
startDay = firstDayOfMonth(addMonths(slotEnd, 1));
}
return slots;
}
getYears(start, end, customDateFormat) {
const slots = [];
let startDay = new Date(start);
const endDay = new Date(end);
const yearFormat = customDateFormat ? customDateFormat : YEAR_FORMAT;
while (startDay < endDay) {
const yearEnd = lastDayOfMonth(lastMonthOfYear(startDay));
const slotEnd = endDay < yearEnd ? endDay : yearEnd;
const monthSlots = this.getMonths(startDay, slotEnd);
const span = monthSlots.length;
if (span > 0) {
slots.push({
start: monthSlots[0].start,
end: setTime(monthSlots[span - 1].end, END_OF_DAY_DATE),
span: span,
text: this.intlService.formatDate(slotEnd, yearFormat),
slotWidth: this.options.slotWidth
});
}
startDay = addDays(slotEnd, 1);
}
return slots;
}
}