UNPKG

@progress/kendo-angular-scheduler

Version:

Kendo UI Scheduler Angular - Outlook or Google-style angular scheduler calendar. Full-featured and customizable embedded scheduling from the creator developers trust for professional UI components.

177 lines (176 loc) 7.25 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { TaskCollection, compose } from './tasks.collection'; import { MS_PER_DAY, toLocalDate } from '@progress/kendo-date-math'; import { intersects, eventResources, toUTCDate, addUTCDays, getUTCDate } from '../utils'; import { getField } from '../../common/util'; /** * @hidden */ export function createResourceGroups(groupedResources) { let result = []; const firstResource = groupedResources[0]; const firstResourceData = firstResource.data; for (let dataIdx = 0; dataIdx < firstResourceData.length; dataIdx++) { const item = firstResourceData[dataIdx]; result.push({ resources: [getField(item, firstResource.textField)] }); } for (let idx = 1; idx < groupedResources.length; idx++) { const resource = groupedResources[idx]; const data = resource.data; const current = []; for (let resourceIdx = 0; resourceIdx < result.length; resourceIdx++) { const resourceItem = result[resourceIdx]; for (let dataIdx = 0; dataIdx < data.length; dataIdx++) { const item = data[dataIdx]; current.push({ resources: resourceItem.resources.concat(getField(item, resource.textField)) }); } } result = current; } return result; } function createTask(item, resourceIdx, resources, color) { const event = item.event; return { event, start: item.start.toUTCDate(), end: item.end.toUTCDate(), title: event.title, isAllDay: event.isAllDay, color, resourceIdx, resources }; } const durationInDays = ({ start, end, isAllDay = false }) => { const eventEnd = isAllDay ? getUTCDate(end) : end; const duration = Math.ceil((eventEnd - +getUTCDate(start)) / MS_PER_DAY); if (isAllDay && duration > 0 && !endsAtMidnight(end)) { return duration + 1; } return duration; }; const endsAtMidnight = (end) => { end = toLocalDate(end); return (end.getHours() === 0 && end.getMinutes() === 0 && end.getSeconds() === 0 && end.getMilliseconds() === 0); }; const curry = fn => { const len = fn.length; return (...args) => len === args.length ? fn(...args) : curry(fn.bind(null, ...args)); }; const range = num => Array.from(new Array(num).keys()); const cloneTask = (eventStart, eventEnd) => task => ({ ...task, start: getUTCDate(eventStart), end: task.tail && !task.isAllDay ? eventEnd : addUTCDays(eventStart, 1), startDate: getUTCDate(eventStart) }); const previousEventEnd = (start, events) => events.length ? events[events.length - 1].end : start; const markAsTail = isLast => task => { if (isLast) { task.tail = true; } return task; }; const markAsMid = task => { if (!task.tail) { task.mid = true; } return task; }; const addTaskPart = (task, start) => (tasks, _, day, days) => tasks.concat(compose(markAsMid, cloneTask(previousEventEnd(start, tasks), task.end), markAsTail(day === days.length - 1))(task)); const splitMultiDayTask = (task, start) => range(durationInDays(task) - 1) .reduce(addTaskPart(task, start), []); /** * @hidden */ export const splitTasks = curry((periodStart, periodEnd, tasks) => { const result = []; for (let index = 0; index < tasks.length; index++) { const task = { ...tasks[index] }; task.startDate = getUTCDate(task.start); if (task.start >= periodStart && task.start <= periodEnd) { result.push(task); } if (durationInDays(task) > 1) { task.end = addUTCDays(task.startDate, 1); task.head = true; result.push(...splitMultiDayTask({ ...tasks[index] }, task.end) .filter(tsk => getUTCDate(tsk.end) <= periodEnd && tsk.start >= periodStart)); } } return result; }); function groupByResource(groupedResources, resourceGroups, dateRange) { const groups = resourceGroups.filter(group => group.tasks && group.tasks.length); if (!groups.length) { return []; } const values = groups[0].resources.map(resource => ({ value: resource, span: 0, groupIdx: 0 })); const periodStart = toUTCDate(dateRange.start); const periodEnd = toUTCDate(dateRange.end); for (let groupIdx = 0; groupIdx < groups.length; groupIdx++) { const group = groups[groupIdx]; group.tasks = splitTasks(periodStart, periodEnd, group.tasks); const count = group.tasks.length; group.tasks = new TaskCollection(periodStart, periodEnd, group.tasks); let invalidate = false; for (let resourceIdx = 0; resourceIdx < groupedResources.length; resourceIdx++) { const resourceValue = values[resourceIdx]; if (resourceValue.value !== group.resources[resourceIdx] || invalidate) { resourceValue.value = group.resources[resourceIdx]; const spanGroup = groups[resourceValue.groupIdx]; spanGroup.spans = spanGroup.spans || []; spanGroup.spans[resourceIdx] = resourceValue.span; resourceValue.span = count; resourceValue.groupIdx = groupIdx; invalidate = true; } else { resourceValue.span += count; } } } values.forEach((value, index) => { const group = groups[value.groupIdx]; group.spans = group.spans || []; group.spans[index] = value.span; }); return groups; } /** * @hidden */ export function groupEvents(items, { taskResources, resourceGroups, spans, allResources, dateRange }) { const groups = resourceGroups || [{}]; const periodStart = toUTCDate(dateRange.start); const periodEnd = toUTCDate(dateRange.end); for (let idx = 0; idx < items.length; idx++) { const item = items[idx]; const event = item.event; if (!intersects(item.start.toUTCDate(), item.end.toUTCDate(), periodStart, periodEnd)) { continue; } const resources = eventResources(event, { taskResources, hasGroups: resourceGroups && resourceGroups.length > 0, spans, allResources }); if (resources.length && resources[0].leafIdx >= 0) { for (let resourceIdx = 0; resourceIdx < resources.length; resourceIdx++) { const current = resources[resourceIdx]; const task = createTask(item, current.leafIdx, current.resources, current.color); const tasks = groups[current.leafIdx].tasks = groups[current.leafIdx].tasks || []; tasks.push(task); } } } if (resourceGroups) { return groupByResource(taskResources, groups, dateRange); } groups[0].tasks = new TaskCollection(periodStart, periodEnd, splitTasks(periodStart, periodEnd, groups[0].tasks || [])); return groups; }