UNPKG

devexpress-gantt

Version:

DevExpress Gantt Control

448 lines (421 loc) 18.7 kB
import { Size } from "@devexpress/utils/lib/geometry/size"; import { DomUtils } from "@devexpress/utils/lib/utils/dom"; import { ITaskAreaContainer } from "../../Interfaces/ITaskAreaContainer"; import { DateRange } from "../../Model/WorkingTime/DateRange"; import { GanttView } from "../../View/GanttView"; import { ViewType } from "../../View/Helpers/Enums"; import { GridElementInfo } from "../../View/Helpers/GridElementInfo"; import { GridLayoutCalculator } from "../../View/Helpers/GridLayoutCalculator"; import { Settings } from "../../View/Settings/Settings"; import { DateUtils } from "../../View/Utils/DateUtils"; import { IPdfExportRangeProps } from "./Settings/Props"; import { Color } from "./Table/Color"; import { DataExportMode } from "./Settings/Enums"; import { PdfDataRange } from "./Settings/DataRange"; export class TaskAreaExportHelper { private _owner: GanttView; private _visibleLeft: number; private _visibleRight: number; private _visibleTop: number; private _visibleBottom: number; private _scaleLeft: number; private _scaleRight: number; private _scaleTopWidths: Array<number>; private _scaleBottomWidths: Array<number>; private _scaleRanges: number[][]; private _offsetLeft: number; private _offsetTop: number; private _headerRowHeight: number; private _arrowWidth: number; private _customRangeLeftOffset: number; private _visibleTaskIndices: number[]; private _visibleDependencyKeys: string[]; private _connectorLines: Array<GridElementInfo>; private _resourcesElements: Array<HTMLElement>; private _stripLinesElements: Array<HTMLElement>; private _noWorkingIntervalsElements: Array<HTMLElement>; private _scaleTableStyle: Record<string, any>; private _chartMainTableStyle: Record<string, any>; private _parentRowBackColor: string; private _dependencyColor: string; private _props: IPdfExportRangeProps; private _layoutCalculator: GridLayoutCalculator; constructor(owner: GanttView, props: IPdfExportRangeProps) { this._owner = owner; this._props = props; } public get customRangeLeftOffset(): number { this._customRangeLeftOffset ??= this.layoutCalculator.getWidthByDateRange(this.startDate, this.ownerStartDate); return this._customRangeLeftOffset; } public get baseCellSize(): Size { return this._owner.tickSize; } public get objectsLeftDelta(): number { return this.renderedScaleLeft; } public get objectsTopDelta(): number { if(!this.hasTasks) return 0; const firstIndex = this.visibleTaskIndices[0]; return this.getCellTop(firstIndex) + this.getTaskCellOffsetTop(firstIndex); } public get offsetLeft(): number { this._offsetLeft ??= Math.max(this.visibleLeft - this.renderedScaleLeft, 0); return this._offsetLeft; } public get offsetTop(): number { this._offsetTop ??= this.getOffsetTop(); return this._offsetTop; } public get scales(): number[] { const viewType = this.settings.viewType; return [DateUtils.ViewTypeToScaleMap[viewType], viewType]; } public get scaleRanges(): number[][] { this._scaleRanges ??= this.layoutCalculator.getScaleRangesInArea(this.scaleLeft, this.scaleRight); return this._scaleRanges; } public get scaleBottomStartIndex(): number { return this.scaleRanges[1][0]; } public get scaleBottomEndIndex(): number { return this.scaleRanges[1][1]; } public get scaleTopStartIndex(): number { return this.scaleRanges[0][0]; } public get scaleTopEndIndex(): number { return this.scaleRanges[0][1]; } public get scaleTopWidths(): Array<number> { this._scaleTopWidths ??= this.getScaleTopWidths(); return this._scaleTopWidths; } public get scaleBottomWidths(): Array<number> { this._scaleBottomWidths ??= this.getScaleBottomWidths(); return this._scaleBottomWidths; } public get headerRowHeight(): number { if(!this._headerRowHeight) { const element = this.scaleElements[0].filter(el => !!el && el.offsetHeight)[0]; this._headerRowHeight = element?.offsetHeight; } return this._headerRowHeight; } public get visibleTaskIndices(): number[] { this._visibleTaskIndices ??= this.getTaskIndices(); return this._visibleTaskIndices; } public get baseCellHeight(): number { return this.hasTasks ? this.baseCellSize.height : this.taskAreaHeight; } public get taskAreaHeight(): number { return this.hasTasks ? this.visibleTaskIndices.length * this.baseCellHeight : this._owner.renderHelper.taskArea.offsetHeight; } public get scaleTableStyle(): Record<string, any> { this._scaleTableStyle ??= this.getScaleTableStyle(); return this._scaleTableStyle; } public get chartMainTableStyle(): Record<string, any> { this._chartMainTableStyle ??= this.getChartMainTableStyle(); return this._chartMainTableStyle; } public get parentRowBackColor(): string { this._parentRowBackColor ??= this.getParentBackColor(); return this._parentRowBackColor; } public get arrowWidth(): number { this._arrowWidth ??= this.getArrowWidth(); return this._arrowWidth; } public get dependencyColor(): string { this._dependencyColor ??= this.getDependencyColor(); return this._dependencyColor; } public getTaskElementBackColor(taskIndex: number, className: string): string { const style = this.getTaskElementStyle(taskIndex, className); return style?.backgroundColor; } public getTaskElementStyle(taskIndex: number, className: string): CSSStyleDeclaration { const taskWrapper = this.getTaskWrapper(taskIndex); return this.getElementStyle(taskWrapper.getElementsByClassName(className)[0]); } public isElementVisible(element: Element): boolean { return element && getComputedStyle(element).display !== "none"; } protected get hasTasks(): boolean { return this.visibleTaskIndices.length > 0; } protected get visibleLeft(): number { this._visibleLeft ??= this.isVisibleMode ? this.container.scrollLeft : 0; return this._visibleLeft; } protected get visibleTop(): number { this._visibleTop ??= this.isVisibleMode ? this.container.scrollTop : 0; return this._visibleTop; } protected get visibleRight(): number { this._visibleRight ??= this.getVisibleRight(); return this._visibleRight; } protected getVisibleRight(): number { const width = this.container.getElement().offsetWidth; return this.visibleLeft + width; } protected get visibleBottom(): number { this._visibleBottom ??= this.getVisibleBottom(); return this._visibleBottom; } protected getVisibleBottom(): number { if(!this.isVisibleMode) return this.visibleTaskIndices.length * this.baseCellSize.height; return this.visibleTop + this.container.getHeight(); } protected get scaleLeft(): number { this._scaleLeft ??= this.isVisibleMode ? this.visibleLeft : this.getPosByDate(this.startDate); return this._scaleLeft; } protected get scaleRight(): number { this._scaleRight ??= this.isVisibleMode ? this.visibleRight : this.getPosByDate(this.endDate) - 1; return this._scaleRight; } protected getScaleTopWidths(): Array<number> { const widths = this.getScaleWidths(this.scaleTopStartIndex, this.scaleTopEndIndex, this.scales[0]); const calc = this.layoutCalculator; const firstBottom = calc.getScaleItemInfo(this.scaleBottomStartIndex, this.scales[1]); const firstTop = calc.getScaleItemInfo(this.scaleTopStartIndex, this.scales[0]); let delta = Math.max(firstBottom.position.x - firstTop.position.x, 0); widths[this.scaleTopStartIndex] -= delta; const lastTop = calc.getScaleItemInfo(this.scaleTopEndIndex, this.scales[0]); const lastBottom = calc.getScaleItemInfo(this.scaleBottomEndIndex, this.scales[1]); delta = Math.max(lastTop.position.x + lastTop.size.width - lastBottom.position.x - lastBottom.size.width, 0); widths[this.scaleTopEndIndex] -= delta; return widths; } protected getScaleBottomWidths(): Array<number> { return this.getScaleWidths(this.scaleBottomStartIndex, this.scaleBottomEndIndex, this.scales[1]); } protected getScaleWidths(start: number, end: number, scaleType: ViewType): Array<number> { const widths = new Array<number>(); for(let i = start; i <= end; i++) widths[i] = this.layoutCalculator.getScaleItemInfo(i, scaleType).size.width; return widths; } protected getOffsetTop(): number { return this.isVisibleMode && this.hasTasks ? this.getTaskCellOffsetTop(this.visibleTaskIndices[0]) : 0; } protected get renderedScaleLeft(): number { return this.getCellLeft(this.scaleBottomStartIndex); } protected getTaskCellOffsetTop(taskIndex: number): number { const point = this.getCellTop(taskIndex); return Math.max(this.visibleTop - point, 0); } protected getCellTop(index: number): number { const point = this.layoutCalculator.getGridBorderPosition(index - 1, false); return point.y; } protected getCellLeft(index: number): number { const point = this.layoutCalculator.getScaleItemInfo(index, this.scales[1]).position; return point.x; } protected getTaskIndices(): Array<number> { if(this.dataMode === DataExportMode.all || this.exportRange) return this._owner.getAllVisibleTaskIndices(this.exportRange?.startIndex, this.exportRange?.endIndex); return this.getVisibleTaskIndices(); } protected getVisibleTaskIndices(): Array<number> { const indices = []; this.taskElements.forEach((t, i) => { if(t) { const taskTop = DomUtils.pxToInt(t.style.top); const taskBottom = taskTop + t.offsetHeight; const topInArea = taskTop >= this.visibleTop && taskTop <= this.visibleBottom; const bottomInArea = taskBottom >= this.visibleTop && taskBottom <= this.visibleBottom; if(topInArea || bottomInArea) indices.push(i); } }); return indices; } protected get scaleElements(): Array<Array<HTMLElement>> { return this._owner.renderHelper.scaleElements.slice(); } protected get scaleBorders(): Array<Array<HTMLElement>> { return this._owner.renderHelper.scaleBorders.slice(); } protected get hlRowElements(): Array<HTMLElement> { return this._owner.renderHelper.hlRowElements.filter(el => !!el); } protected get selectionElements(): Array<HTMLElement> { return this._owner.renderHelper.selectionElements.filter(el => !!el); } protected get taskElements(): Array<HTMLElement> { return this._owner.renderHelper.taskElements; } public get connectorLines(): Array<GridElementInfo> { this._connectorLines ??= this._owner.renderHelper.allConnectorLines.filter(l => this.isLineVisible(l)); return this._connectorLines; } protected isLineVisible(line: GridElementInfo): boolean { if(this.dataMode === DataExportMode.all) return true; const id = line.attr["dependency-id"]; return this.visibleDependencyKeys.indexOf(id) > -1; } protected get visibleDependencyKeys(): string[] { this._visibleDependencyKeys ??= this._owner.getVisibleDependencyKeysByTaskRange(this.visibleTaskIndices); return this._visibleDependencyKeys; } public get resourcesElements(): Array<HTMLElement> { this._resourcesElements ??= this.visibleTaskIndices.map(tIndex => this._owner.renderHelper.resourcesElements[tIndex]).filter(r => this.isElementVisible(r) && r.parentElement); return this._resourcesElements; } public get stripLinesElements(): Array<HTMLElement> { if(!this._stripLinesElements) { const elements = this._owner.renderHelper.stripLinesMap.filter(s =>!!s).map(s => s as unknown); this._stripLinesElements = elements.map(e => e as HTMLElement); } return this._stripLinesElements; } public get noWorkingIntervalsElements(): Array<HTMLElement> { if(!this._noWorkingIntervalsElements) { this._noWorkingIntervalsElements = [ ]; const hash = this._owner.renderHelper.noWorkingIntervalsToElementsMap; for(const key in hash) { if(!Object.prototype.hasOwnProperty.call(hash, key)) continue; this._noWorkingIntervalsElements.push(hash[key]); } } return this._noWorkingIntervalsElements; } protected get taskArea(): HTMLElement { return this._owner.renderHelper.taskArea; } protected get settings(): Settings { return this._owner.settings; } protected get dataMode(): DataExportMode { return this._props?.exportDataMode; } protected get exportRange(): PdfDataRange { return this._props?.dateRange; } protected get isVisibleMode(): boolean { return this.dataMode === DataExportMode.visible && !this.exportRange; } protected get ownerStartDate(): Date { return this._owner.range.start; } protected get ownerEndDate(): Date { return this._owner.range.end; } protected get startDate(): Date { if(this.exportRange?.startDate && this.exportRange?.endDate) { const min = Math.min(this.exportRange?.startDate.getTime(), this.exportRange?.endDate.getTime()); return new Date(min); } return this.ownerStartDate; } protected get endDate(): Date { if(this.exportRange?.startDate && this.exportRange?.endDate) { const max = Math.max(this.exportRange?.startDate.getTime(), this.exportRange?.endDate.getTime()); return new Date(max); } return this.ownerEndDate; } protected get hasCustomRangeOutOfRender(): boolean { return this.startDate.getTime() !== this.ownerStartDate.getTime() || this.endDate.getTime() !== this.ownerEndDate.getTime(); } public get layoutCalculator(): GridLayoutCalculator { if(!this._layoutCalculator) { const calc = this._owner.renderHelper.gridLayoutCalculator; if(this.hasCustomRangeOutOfRender) { this._layoutCalculator = new GridLayoutCalculator(); this._layoutCalculator.setSettings( calc.visibleTaskAreaSize, calc.tickSize, calc.elementSizeValues, new DateRange(this.startDate, this.endDate), calc.viewModel, calc.viewType, calc.scrollBarHeight, this._owner.settings.firstDayOfWeek ); } else this._layoutCalculator = calc; } return this._layoutCalculator; } protected get container(): ITaskAreaContainer { return this._owner.renderHelper.taskAreaContainer; } protected getPosByDate(date: Date): number { return this.layoutCalculator.getPosByDate(date); } protected getScaleTableStyle(): Record<string, any> { const result = { }; const visibleScaleItem = this.scaleElements[0].filter(el => !!el)[0]; const style = this.getElementStyle(visibleScaleItem); result["backgroundColor"] = this.findElementBackColor(visibleScaleItem); result["borderColor"] = this.getChartTableBorderColor(); result["verticalAlign"] = "middle"; result["textAlign"] = "center"; result["fontSize"] = style.fontSize; result["fontFamily"] = style.fontFamily; result["fontWeight"] = style.fontWeight; result["fontStyle"] = style.fontStyle; result["color"] = style.color; return result; } protected getChartMainTableStyle(): Record<string, any> { const result = { }; result["backgroundColor"] = this.findElementBackColor(this.taskArea); result["borderColor"] = this.getChartTableBorderColor(); return result; } protected findElementBackColor(element: Element): Color { if(!element) return null; let parent = element; const color = new Color("transparent"); while(color.opacity === 0 && parent) { const style = this.getElementStyle(parent); color.assign(style.backgroundColor); parent = parent.parentElement; } return color; } protected getChartTableBorderColor(): string { const style = this.getElementStyle(this.scaleBorders[0].filter(el => !!el)[0]); return style?.borderColor; } protected getParentBackColor(): string { const style = this.getElementStyle(this.hlRowElements[0]); return style?.backgroundColor; } protected getArrowWidth(): number { const style = this.getDependencyLineStyle(GridLayoutCalculator.arrowClassName); const borderWidth = style.borderWidth || style.borderLeftWidth || style.borderRightWidth || style.borderTopWidth || style.borderBottomWidth; return style && DomUtils.pxToInt(borderWidth); } protected getDependencyColor(): string { const style = this.getDependencyLineStyle(GridLayoutCalculator.CLASSNAMES.CONNECTOR_HORIZONTAL); return style?.borderColor; } protected getDependencyLineStyle(className: string): CSSStyleDeclaration { return this.getElementStyle(this.taskArea.getElementsByClassName(className)[0]); } protected getElementStyle(element: Element): CSSStyleDeclaration { return element && getComputedStyle(element); } protected getTaskWrapper(index: number): HTMLElement { if(this.isTaskTemplateMode) return this._owner.renderHelper.fakeTaskWrapper; if(!this.taskElements[index]) this._owner.renderHelper.createDefaultTaskElement(index); return this.taskElements[index]; } protected get isTaskTemplateMode(): boolean { return !!this._owner.settings.taskContentTemplate; } }