UNPKG

devexpress-gantt

Version:

DevExpress Gantt Control

313 lines (296 loc) 13.5 kB
import { Point } from "@devexpress/utils/lib/geometry/point"; import { Size } from "@devexpress/utils/lib/geometry/size"; import { DateRange } from "../../Model/WorkingTime/DateRange"; import { DateUtils } from "../Utils/DateUtils"; import { ViewType } from "./Enums"; export class ScaleItemInfo { constructor(start: Date, end: Date, position: Point, size: Size) { this.start = start; this.end = end; this.position = position; this.size = size; } start: Date; end: Date; position: Point; size: Size; } export class ScaleCalculator { range: DateRange; viewType: ViewType; tickSize: Size; firstDayOfWeek: number = 0; private _topScaleItems: Array<ScaleItemInfo>; private _bottomScaleItems: Array<ScaleItemInfo>; private _scaleWidth: number; public setSettings(range: DateRange, viewType: ViewType, tickSize: Size, firstDayOfWeek: number = 0): void { this.range = range; this.viewType = viewType; this.tickSize = tickSize; this.firstDayOfWeek = firstDayOfWeek; this.reset(); } public setViewType(viewType: ViewType): void { this.viewType = viewType; this.reset(); } protected reset(): void { delete this._bottomScaleItems; delete this._topScaleItems; delete this._scaleWidth; } public getScaleIndexByPos(pos: number, scaleType?: ViewType): number { scaleType ??= this.viewType; const items = scaleType === this.viewType ? this.bottomScaleItems : this.topScaleItems; let index = -1; if(items.findIndex) index = items.findIndex(i => pos >= i.position.x && pos <= i.position.x + i.size.width); else { const item = items.filter(i => pos >= i.position.x && pos <= i.position.x + i.size.width)[0]; if(item) index = items.indexOf(item); } return index; } public getScaleBorderPosition(index: number, scaleType: ViewType): number { const item = this.getScaleItems(scaleType)[index]; if(item) return item.position.x + item.size.width; } public getScaleItems(scaleType: ViewType): Array<ScaleItemInfo> { if(scaleType === this.viewType) return this.bottomScaleItems; if(scaleType === DateUtils.ViewTypeToScaleMap[this.viewType]) return this.topScaleItems; return null; } public getScaleItem(index: number, scaleType: ViewType): ScaleItemInfo { return this.getScaleItems(scaleType)[index]; } public getScaleItemAdjustedStart(index: number, scaleType: ViewType): Date { const item = this.getScaleItems(scaleType)[index]; if(index > 0) return item.start; const isTopScale = scaleType !== this.viewType; let date = isTopScale ? DateUtils.adjustStartDateByViewType(this.range.start, this.viewType, this.firstDayOfWeek) : this.getAdjustedBottomScaleItemStart(item.start, scaleType, this.firstDayOfWeek); if(isTopScale && scaleType === ViewType.Months) { const ref = this.range.start; date = new Date(ref.getFullYear(), ref.getMonth(), 1); } if(isTopScale && scaleType === ViewType.FiveYears) { const year = Math.trunc(date.getFullYear() / 5) * 5; date = new Date(year, date.getMonth(), date.getDate()); } return date; } public get topScaleItems(): Array<ScaleItemInfo> { this._topScaleItems ??= this.calculateTopScale(DateUtils.ViewTypeToScaleMap[this.viewType]); return this._topScaleItems; } public get bottomScaleItems(): Array<ScaleItemInfo> { this._bottomScaleItems ??= this.calculateBottomScale(this.viewType); return this._bottomScaleItems; } public get scaleWidth(): number { this._scaleWidth ??= this.calculateScaleWidth(); return this._scaleWidth; } getFirstScaleIndexForRender(startPos: number): number { let result = this.getScaleIndexByPos(startPos); result = Math.max(result - 10, 0); return result; } getLastScaleIndexForRender(endPos: number): number { let result = this.getScaleIndexByPos(endPos); if(result === -1) result = this.bottomScaleItems.length - 1; else result = Math.min(result + 10, this.bottomScaleItems.length - 1); return result; } public getTopScaleIndexByBottomIndex(index: number): number { const bottomItem = this.bottomScaleItems[index]; return bottomItem ? this.getScaleIndexByPos(bottomItem.position.x, DateUtils.ViewTypeToScaleMap[this.viewType]) : -1; } protected calculateBottomScale(scaleType: ViewType): Array<ScaleItemInfo> { const items = new Array<ScaleItemInfo>(); const defWidth = this.tickSize.width; let currentDate = this.range.start; let x = 0; while(currentDate.getTime() < this.range.end.getTime()) { const nextDate = this.getNextScaleDate(currentDate, scaleType); const isStart = currentDate.getTime() === this.range.start.getTime(); const isEnd = nextDate.getTime() >= this.range.end.getTime(); const needWidthCorrection = isStart || isEnd || (scaleType > ViewType.Hours && DateUtils.hasDST()); const width = needWidthCorrection ? this.getRangeTickCount(currentDate, nextDate) * defWidth : defWidth; items.push(new ScaleItemInfo(currentDate, nextDate, new Point(x, undefined), new Size(width, 0))); currentDate = nextDate; x += width; } return items; } protected calculateTopScale(scaleType: ViewType): Array<ScaleItemInfo> { const items = new Array<ScaleItemInfo>(); const endRangeTime = this.range.end.getTime(); let itemStartDate = this.range.start; let itemStartPos = 0; let lastBottomIndex = 0; while(itemStartDate.getTime() < endRangeTime) { const itemNextDate = this.getNextScaleDate(itemStartDate, scaleType); const nextDateTime = itemNextDate.getTime(); for(let i = lastBottomIndex; i < this.bottomScaleItems.length; i++) { const item = this.bottomScaleItems[i]; const startTime = item.start.getTime(); const endTime = item.end.getTime(); const isNextDateInItem = nextDateTime >= startTime && nextDateTime <= endTime; if(isNextDateInItem) { const itemEndPos = (nextDateTime - startTime) / (endTime - startTime) * item.size.width + item.position.x; items.push(new ScaleItemInfo(itemStartDate, itemNextDate, new Point(itemStartPos, undefined), new Size(itemEndPos - itemStartPos, 0))); lastBottomIndex = i; itemStartPos = itemEndPos; itemStartDate = itemNextDate; break; } } } return items; } getDateInScale(pos: number): Date { if(pos < 0) { const timeOffset = pos / this.tickSize.width; const timeSpan = DateUtils.getTickTimeSpan(this.viewType); return new Date(this.range.start.getTime() + timeOffset * timeSpan); } for(let i = 0; i < this.bottomScaleItems.length; i++) { const item = this.bottomScaleItems[i]; const width = item.size.width; const left = item.position.x; if(pos >= left && pos <= left + width) { const startTime = item.start.getTime(); const endTime = item.end.getTime(); const timeOffset = (pos - left) / width * (endTime - startTime); return new Date(item.start.getTime() + timeOffset); } } return new Date(this.range.end); } protected getNextScaleDate(start: Date, scaleType: ViewType): Date { let date: Date; switch(scaleType) { case ViewType.TenMinutes: date = this.getNextDateInTenMinutesScale(start); break; case ViewType.Hours: date = this.getNextDateInHoursScale(start); break; case ViewType.SixHours: date = this.getNextDateInSixHoursScale(start); break; case ViewType.Days: date = this.getNextDateInDaysScale(start); break; case ViewType.Weeks: date = this.getNextDateInWeeksScale(start, this.firstDayOfWeek); break; case ViewType.Months: date = this.getNextDateInMonthsScale(start); break; case ViewType.Quarter: date = this.getNextDateInQuartersScale(start); break; case ViewType.Years: date = this.getNextDateInYearsScale(start); break; case ViewType.FiveYears: date = this.getNextDateInFiveYearsScale(start); break; } if(date.getTime() > this.range.end.getTime()) date = this.range.end; return date; } protected getNextTimeBySpan(value: number, span: number): number { const k = Math.trunc(value / span); return (k + 1) * span; } getNextDateInTenMinutesScale(date: Date): Date { const minutes = this.getNextTimeBySpan(date.getMinutes(), 10); return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), minutes); } getNextDateInHoursScale(date: Date): Date { return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours() + 1); } getNextDateInSixHoursScale(date: Date): Date { const hours = this.getNextTimeBySpan(date.getHours(), 6); return new Date(date.getFullYear(), date.getMonth(), date.getDate(), hours); } getNextDateInDaysScale(date: Date): Date { return new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1); } getNextDateInWeeksScale(date: Date, firstDayOfWeek: number = 0): Date { return DateUtils.getNextWeekStartDate(date, firstDayOfWeek); } getNextDateInMonthsScale(date: Date): Date { return new Date(date.getFullYear(), date.getMonth() + 1, 1); } getNextDateInQuartersScale(date: Date): Date { const months = this.getNextTimeBySpan(date.getMonth(), 3); return new Date(date.getFullYear(), months, 1); } getNextDateInYearsScale(date: Date): Date { return new Date(date.getFullYear() + 1, 0, 1); } getNextDateInFiveYearsScale(date: Date): Date { const years = this.getNextTimeBySpan(date.getFullYear(), 5); return new Date(years, 0, 1); } getAdjustedBottomScaleItemStart(date: Date, viewType: ViewType, firstDayOfWeek: number = 0): Date { switch(viewType) { case ViewType.TenMinutes: return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), Math.floor(date.getMinutes() / 10) * 10); case ViewType.SixHours: return new Date(date.getFullYear(), date.getMonth(), date.getDate(), Math.floor(date.getHours() / 6) * 6); case ViewType.Hours: return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours()); case ViewType.Days: return new Date(date.getFullYear(), date.getMonth(), date.getDate()); case ViewType.Weeks: return new Date(date.getFullYear(), date.getMonth(), date.getDate()); case ViewType.Months: return new Date(date.getFullYear(), date.getMonth(), 1); case ViewType.Quarter: return new Date(date.getFullYear(), Math.floor(date.getMonth() / 3) * 3, 1); case ViewType.Years: return new Date(date.getFullYear(), 0, 1); default: return new Date(); } } protected calculateScaleWidth(): number { return this.bottomScaleItems.reduce((sum, item) => sum += item.size.width, 0); } public getScaleItemColSpan(scaleType: ViewType): number { if(scaleType.valueOf() === this.viewType.valueOf()) return 1; if(this.viewType === ViewType.TenMinutes) return 6; if(this.viewType === ViewType.Hours) return 24; if(this.viewType === ViewType.SixHours) return 4; if(this.viewType === ViewType.Days) return 7; if(this.viewType === ViewType.Weeks) return 4.29; if(this.viewType === ViewType.Months) return 12; if(this.viewType === ViewType.Quarter) return 4; if(this.viewType === ViewType.Years) return 5; return 1; } public getRangeTickCount(start: Date, end: Date): number { return DateUtils.getRangeTickCount(start, end, this.viewType); } }