devexpress-gantt
Version:
DevExpress Gantt Control
922 lines (855 loc) • 40.8 kB
text/typescript
import { BarManager } from "../BarManager/BarManager";
import { Browser } from "@devexpress/utils/lib/browser";
import { CommandManager } from "../Commands/CommandManager";
import { CreateTaskHistoryItem } from "../Model/History/HistoryItems/Task/CreateTaskHistoryItem";
import { DateRange } from "../Model/WorkingTime/DateRange";
import { DateTimeUtils } from "../Model/WorkingTime/DateTimeUtils";
import { DateUtils } from "./Utils/DateUtils";
import { Dependency } from "../Model/Entities/Dependency";
import { DependencyType } from "../Model/Entities/Enums";
import { EditingSettings } from "./Settings/EditingSettings";
import { FullScreenHelperSettings } from "./Settings/InitSettings/FullScreenHelperSettings";
import { FullScreenModeHelper } from "./Helpers/FullScreenModeHelper";
import { GanttClientCommand } from "../Commands/ClientCommand";
import { GanttExportCalculator } from "../Export/Pdf/Calculator";
import { History } from "../Model/History/History";
import { ICommand } from "../Interfaces/ICommand";
import { IGanttOwner } from "../Interfaces/IGanttOwner";
import { IGanttView } from "../Interfaces/IGanttView";
import { IHistory } from "../Model/History/IHistory";
import { isDefined } from "@devexpress/utils/lib/utils/common";
import { ITaskAreaContainer } from "../Interfaces/ITaskAreaContainer";
import { ModelChangesDispatcher } from "../Model/Dispatchers/ModelChangesDispatcher";
import { ModelManipulator } from "../Model/Manipulators/ModelManipulator";
import { PdfGanttExporter } from "../Export/Pdf/Exporter";
import { RenderHelper } from "./Render/RenderHelper";
import { Resource } from "../Model/Entities/Resource";
import { Settings } from "./Settings/Settings";
import { Size } from "@devexpress/utils/lib/geometry/size";
import { StripLineSettings } from "./Settings/StripLineSettings";
import { Task } from "../Model/Entities/Task";
import { TaskEditController } from "./Edit/TaskEditController";
import { TaskEditSettings } from "./Settings/InitSettings/TaskEditSettings";
import { ValidationController } from "../Model/Validation/ValidationController";
import { ValidationControllerSettings } from "./Settings/InitSettings/ValidationControllerSettings";
import { ValidationSettings } from "./Settings/ValidationSettings";
import { ViewType, TaskTitlePosition } from "./Helpers/Enums";
import { ViewVisualModel } from "../Model/VisualModel/VisualModel";
import { ViewVisualModelItem } from "../Model/VisualModel/ViewVisualModelItem";
import { GanttViewApi } from "./Api/GanttViewApi";
import { EvtUtils } from "@devexpress/utils/lib/utils/evt";
import { IHistoryListener } from "../Interfaces/IHistiryListener";
import { DomUtils } from "@devexpress/utils/lib/utils/dom";
import { Point } from "@devexpress/utils/lib/geometry/point";
import { ITaskAreaEventsListener, TaskAreaEventsListener } from "./Events/TaskAreaEventsListener";
import { DialogBase } from "../Dialogs/DialogBase";
import { GanttDataObjectNames } from "../Model/Entities/DataObject";
export class GanttView implements IGanttView {
static cachedPrefix = "cached_";
static taskAreaScrollLeftKey = GanttView.cachedPrefix + "taskAreaScrollLeft";
static taskAreaScrollTopKey = GanttView.cachedPrefix + "taskAreaScrollTop";
static taskTextHeightKey = GanttView.cachedPrefix + "taskTextHeight";
private _taskAreaEventsListener: ITaskAreaEventsListener;
ganttOwner: IGanttOwner;
barManager: BarManager;
commandManager: CommandManager;
dataRange: DateRange;
dispatcher: ModelChangesDispatcher;
fullScreenModeHelper: FullScreenModeHelper;
history: IHistory;
modelManipulator: ModelManipulator;
range: DateRange;
renderHelper: RenderHelper;
settings: Settings;
taskEditController: TaskEditController;
validationController: ValidationController;
viewModel: ViewVisualModel;
ganttViewApi: GanttViewApi;
currentSelectedTaskID = "";
isFocus: boolean = false;
private _updateWithModelReloadLockedCounter: number = 0;
private _pendingUpdateInfo: Record<string, any>;
scaleCount: number = 2;
tickSize: Size = new Size(0, 0);
public currentZoom: number = 1;
private stripLinesUpdaterId: number = null;
_scrollTimeOut: number;
constructor(element: HTMLElement, ganttOwner: IGanttOwner, settings: any) {
this.ganttOwner = ganttOwner;
this.settings = Settings.parse(settings);
this.initValidationController();
this.renderHelper = new RenderHelper(this);
this.renderHelper.initMarkup(element);
this.loadOptionsFromGanttOwner();
this.renderHelper.init(this.tickSize, this.range, this.settings.viewType, this.viewModel, this.settings.firstDayOfWeek);
this.commandManager = new CommandManager(this);
this.barManager = new BarManager(this.commandManager, this.ganttOwner.bars);
this.initTaskEditController();
this.history = new History(this._getHistoryListener());
this.initFullScreenModeHelper();
this.updateView();
this._scrollTimeOut = setTimeout(() => {
this.scrollLeftByViewType();
}, 0);
this.initializeStripLinesUpdater();
this.initGanttViewApi();
}
initGanttViewApi(): void {
this.ganttViewApi = new GanttViewApi(this);
}
private _getHistoryListener(): IHistoryListener {
const listener = {
onTransactionStart: this.onHistoryTransactionStart.bind(this),
onTransactionEnd: this.onHistoryTransactionEnd.bind(this),
};
return listener;
}
protected onHistoryTransactionStart(): void { this.lockUpdateWithReload(); }
protected onHistoryTransactionEnd(): void { this.unlockUpdateWithReload(); }
public lockUpdateWithReload(): void {
this._updateWithModelReloadLockedCounter++;
}
public unlockUpdateWithReload(): void {
this._updateWithModelReloadLockedCounter--;
if(this._updateWithModelReloadLockedCounter === 0 && this._pendingUpdateInfo) {
this.updateWithDataReload(this._pendingUpdateInfo.keepExpandState);
this._pendingUpdateInfo = null;
}
}
initValidationController(): void {
const validationControllerSettings = ValidationControllerSettings.parse({
getViewModel: () => { return this.viewModel; },
getHistory: () => { return this.history; },
getModelManipulator: () => { return this.modelManipulator; },
getRange: () => { return this.range; },
getValidationSettings: () => { return this.settings.validation; },
updateOwnerInAutoParentMode: () => { this.updateOwnerInAutoParentMode(); },
getIsValidateDependenciesRequired: () => { return this.isValidateDependenciesRequired(); }
});
this.validationController = new ValidationController(validationControllerSettings);
}
initTaskEditController(): void {
const taskEditSettings = TaskEditSettings.parse({
destroyTemplate: (container: HTMLElement) => { this.destroyTemplate(container); },
formatDate: (date: Date) => { return this.getDateFormat(date); },
getRenderHelper: () => { return this.renderHelper; },
getGanttSettings: () => { return this.settings; },
getViewModel: () => { return this.viewModel; },
getCommandManager: () => { return this.commandManager; },
getModelManipulator: () => { return this.modelManipulator; },
getValidationController: () => { return this.validationController; }
});
this.taskEditController = new TaskEditController(taskEditSettings);
}
public get taskAreaEventsListener(): ITaskAreaEventsListener {
this._taskAreaEventsListener ??= new TaskAreaEventsListener(this);
return this._taskAreaEventsListener;
}
initFullScreenModeHelper(): void {
const fullScreenModeSeettings = FullScreenHelperSettings.parse({
getMainElement: () => { return this.getOwnerControlMainElement(); },
adjustControl: () => { this.adjustOwnerControl(); } }
);
this.fullScreenModeHelper = new FullScreenModeHelper(fullScreenModeSeettings);
}
getDateRange(modelStartDate: Date, modelEndDate: Date): DateRange {
const visibleAreaTime = this.getVisibleAreaTime();
let start = this.settings.startDateRange || DateUtils.adjustStartDateByViewType(new Date(modelStartDate.getTime() - visibleAreaTime), this.settings.viewType, this.settings.firstDayOfWeek);
let end = this.settings.endDateRange || DateUtils.adjustEndDateByViewType(new Date(modelEndDate.getTime() + visibleAreaTime), this.settings.viewType, this.settings.firstDayOfWeek);
if(this.settings.startDateRange && start > end)
end = start;
else if(this.settings.endDateRange && start > end)
start = end;
return new DateRange(start, end);
}
getVisibleAreaTime(): number {
const visibleTickCount = Math.ceil(this.renderHelper.getTaskAreaContainerWidth() / this.tickSize.width);
return visibleTickCount * DateUtils.getTickTimeSpan(this.settings.viewType);
}
zoomIn(leftPos: number = this.renderHelper.getTaskAreaContainerWidth() / 2): void {
this.ganttViewApi.zoomIn(leftPos);
}
zoomOut(leftPos: number = this.renderHelper.getTaskAreaContainerWidth() / 2): void {
this.ganttViewApi.zoomOut(leftPos);
}
scrollToDate(date: any): void {
if(date) {
const scrollDate = date instanceof Date ? DateUtils.getOrCreateUTCDate(date) : DateUtils.parse(date);
this.scrollToDateCore(scrollDate, 0);
}
}
public showDialog(name: string, parameters: any, callback: (params: any) => void, afterClosing: () => void) {
this.ganttOwner.showDialog(name, parameters, callback, afterClosing);
}
public showPopupMenu(info: any) {
this.ganttOwner.showPopupMenu(info);
}
public hidePopupMenu(): void {
if(this.ganttOwner.hidePopupMenu)
this.ganttOwner.hidePopupMenu();
}
public collapseAll(): void {
this.ganttOwner.collapseAll();
}
public expandAll(): void {
this.ganttOwner.expandAll();
}
public onGanttViewContextMenu(evt: any, key: any, type: string): boolean {
return this.ganttOwner.onGanttViewContextMenu(evt, key, type);
}
public changeGanttTaskSelection(id: string, selected: boolean): void {
this.ganttOwner.changeGanttTaskSelection(id, selected);
}
public hideTaskEditControl() : void {
this.taskEditController.hide();
}
public scrollLeftByViewType(): void {
const adjustedStartDate = DateUtils.roundStartDate(this.dataRange.start, this.settings.viewType);
this.scrollToDateCore(adjustedStartDate, 1);
}
public scrollToDateCore(date: Date, addLeftPos: number): void {
this.renderHelper.setTaskAreaContainerScrollLeftToDate(date, addLeftPos);
}
onVisualModelChanged(): void {
this.resetAndUpdate();
}
private initializeStripLinesUpdater() {
if(this.settings.stripLines.showCurrentTime)
this.stripLinesUpdaterId = setInterval(() => {
this.renderHelper.recreateStripLines();
}, Math.max(this.settings.stripLines.currentTimeUpdateInterval, 100));
}
private clearStripLinesUpdater() {
if(this.stripLinesUpdaterId)
clearInterval(this.stripLinesUpdaterId);
this.stripLinesUpdaterId = null;
}
getGanttViewStartDate(tasks: Task[]): Date {
if(!tasks) return new Date();
const dates = tasks.map(t => typeof t.start === "string" ? new Date(t.start) : t.start).filter(d => isDefined(d));
return dates.length > 0 ? dates.reduce((min, d) => d < min ? d : min, dates[0]) : new Date();
}
getGanttViewEndDate(tasks: Task[]): Date {
if(!tasks) return new Date();
const dates = tasks.map(t => typeof t.end === "string" ? new Date(t.end) : t.end).filter(d => isDefined(d));
return dates.length > 0 ? dates.reduce((max, d) => d > max ? d : max, dates[0]) : new Date();
}
getTask(index: number): Task {
const item = this.getViewItem(index);
return item?.task;
}
getViewItem(index: number): ViewVisualModelItem {
return this.viewModel?.items[index];
}
isValidateDependenciesRequired(): boolean {
return this.settings.validation.validateDependencies && this.settings.showDependencies;
}
updateTickSizeWidth(): void {
this.tickSize.width = this.renderHelper.etalonScaleItemWidths * this.currentZoom;
}
updateView(): void {
this.onBeginUpdateView();
this.renderHelper.setTimeScaleContainerScrollLeft(this.taskAreaContainerScrollLeft);
this.processScroll(false);
this.processScroll(true);
this.ganttOwner.onGanttScroll(this.taskAreaContainerScrollTop);
this.onEndUpdateView();
}
protected onBeginUpdateView(): void {
this[GanttView.taskAreaScrollTopKey] = this.renderHelper.taskAreaContainerScrollTop;
this[GanttView.taskAreaScrollLeftKey] = this.renderHelper.taskAreaContainerScrollLeft;
}
protected onEndUpdateView(): void {
delete this[GanttView.taskAreaScrollTopKey];
delete this[GanttView.taskAreaScrollLeftKey];
delete this[GanttView.taskTextHeightKey];
}
get taskAreaContainerScrollTop(): number {
return this[GanttView.taskAreaScrollTopKey] ?? this.renderHelper.taskAreaContainerScrollTop;
}
get taskAreaContainerScrollLeft(): number {
return this[GanttView.taskAreaScrollLeftKey] ?? this.renderHelper.taskAreaContainerScrollLeft;
}
processScroll(isVertical: boolean): void {
this.hideTaskEditControl();
this.renderHelper.processScroll(isVertical);
}
allowTaskAreaBorders(isVerticalScroll: boolean): boolean {
return isVerticalScroll ? this.settings.areHorizontalBordersEnabled : this.settings.areVerticalBordersEnabled;
}
getScaleItemText(index: number, scale: ViewType): string {
return this.renderHelper.getScaleItemText(index, scale);
}
getTaskText(index: number): string {
return this.renderHelper.getTaskText(index);
}
public rowHasChildren(index: number): boolean {
const item = this.getViewItem(index);
return item?.children.length > 0;
}
public rowHasSelection(index: number): boolean {
const item = this.getViewItem(index);
return item?.selected;
}
getAllVisibleTaskIndices(start?: number, end?: number): number[] { return this.viewModel.getAllVisibleTaskIndices(start, end); }
public getVisibleDependencyKeysByTaskRange(indices: number[]): string[] {
if(!this.settings.showDependencies)
return [];
const model = this.viewModel;
const taskKeys = indices.map(i => model.tasks.items[i].internalId);
const dependencies = model.dependencies.items;
return dependencies.filter(d => taskKeys.indexOf(d.successorId) > -1 || taskKeys.indexOf(d.predecessorId) > -1).map(d => d.internalId);
}
getTreeListTableStyle(): Record<string, any> {
return this.ganttOwner.getTreeListTableStyle?.();
}
getTreeListColCount(): number {
return this.ganttOwner.getTreeListColCount?.();
}
getTreeListHeaderInfo(colIndex: number): Record<string, any> {
return this.ganttOwner.getTreeListHeaderInfo?.(colIndex);
}
getTreeListCellInfo(rowIndex: number, colIndex: number, key: any): Record<string, any> {
return this.ganttOwner.getTreeListCellInfo?.(rowIndex, colIndex, key);
}
getTreeListEmptyDataCellInfo(): Record<string, any> {
return this.ganttOwner.getTreeListEmptyDataCellInfo?.();
}
public exportToPdf(options: Record<string, any>): any {
options["docCreateMethod"] ??= this.getDefaultPdfDocCreateMethod();
const exporter = new PdfGanttExporter(new GanttExportCalculator(this, options));
return exporter.export();
}
private getDefaultPdfDocCreateMethod(): (prop?: Record<string, any>) => any {
return window["jspdf"]?.["jsPDF"];
}
getTaskDependencies(taskInternalId: any): Array<Dependency> {
return this.viewModel.dependencies.items.filter(d => d.predecessorId == taskInternalId || d.successorId == taskInternalId);
}
isHighlightRowElementAllowed(index: number): boolean {
const viewItem = this.getViewItem(index);
return index % 2 !== 0 && this.settings.areAlternateRowsEnabled || viewItem?.children.length > 0;
}
calculateAutoViewType(startDate, endDate): ViewType {
const diffInHours = (endDate.getTime() - startDate.getTime()) / (1000 * 3600);
if(diffInHours > 24 * 365)
return ViewType.Years;
if(diffInHours > 24 * 30)
return ViewType.Months;
if(diffInHours > 24 * 7)
return ViewType.Weeks;
if(diffInHours > 24)
return ViewType.Days;
if(diffInHours > 6)
return ViewType.SixHours;
if(diffInHours > 1)
return ViewType.Hours;
return ViewType.TenMinutes;
}
getExternalTaskAreaContainer(parent: HTMLElement): ITaskAreaContainer {
return this.ganttOwner.getExternalTaskAreaContainer(parent);
}
prepareExternalTaskAreaContainer(element: HTMLElement, info: Record<string, any>): void {
return this.ganttOwner.prepareExternalTaskAreaContainer(element, info);
}
getHeaderHeight(): number {
return this.ganttOwner.getHeaderHeight();
}
changeTaskExpanded(publicId: any, expanded: boolean): void {
const task = this.getTaskByPublicId(publicId);
if(task)
this.viewModel.changeTaskExpanded(task.internalId, expanded);
}
expandTask(id: string): void { this.viewModel.changeTaskExpanded(id, true); }
collapseTask(id: string): void { this.viewModel.changeTaskExpanded(id, false); }
showTask(id: string): void { this.viewModel.changeTaskVisibility(id, true); }
hideTask(id: string): void { this.viewModel.changeTaskVisibility(id, false); }
getTaskVisibility(id: string): boolean { return this.viewModel.getTaskVisibility(id); }
unselectCurrentSelectedTask(): void { this.unselectTask(this.currentSelectedTaskID); }
getTaskSelected(id: string): boolean { return this.viewModel.getTaskSelected(id); }
setViewType(viewType: ViewType, autoPositioning: boolean = true): void {
this.ganttViewApi.setViewType(viewType, autoPositioning);
}
setViewTypeRange(min: ViewType, max: ViewType): void {
this.ganttViewApi.setViewTypeRange(min, max);
}
setTaskTitlePosition(taskTitlePosition: TaskTitlePosition): void {
if(this.settings.taskTitlePosition !== taskTitlePosition) {
this.settings.taskTitlePosition = taskTitlePosition;
this.resetAndUpdate();
}
}
setShowResources(showResources: boolean): void {
if(this.settings.showResources !== showResources) {
this.settings.showResources = showResources;
this.resetAndUpdate();
}
}
toggleResources(): void {
this.setShowResources(!this.settings.showResources);
}
setShowDependencies(showDependencies: boolean): void {
if(this.settings.showDependencies !== showDependencies) {
this.settings.showDependencies = showDependencies;
this.resetAndUpdate();
}
}
toggleDependencies(): void {
this.setShowDependencies(!this.settings.showDependencies);
}
setFirstDayOfWeek(firstDayOfWeek: number): void {
if(this.settings.firstDayOfWeek !== firstDayOfWeek) {
this.settings.firstDayOfWeek = firstDayOfWeek;
this.resetAndUpdate();
}
}
setStartDateRange(start: Date): void {
if(!DateTimeUtils.areDatesEqual(this.settings.startDateRange, start)) {
this.settings.startDateRange = new Date(start);
this.resetAndUpdate();
}
}
setEndDateRange(end: Date): void {
if(!DateTimeUtils.areDatesEqual(this.settings.endDateRange, end)) {
this.settings.endDateRange = new Date(end);
this.resetAndUpdate();
}
}
loadOptionsFromGanttOwner(): void {
this.tickSize.height = this.ganttOwner.getRowHeight();
const tasksData = this.ganttOwner.getGanttTasksData();
this.dataRange = new DateRange(this.getGanttViewStartDate(tasksData), this.getGanttViewEndDate(tasksData));
if(this.settings.viewType == undefined)
this.settings.viewType = this.calculateAutoViewType(this.dataRange.start, this.dataRange.end);
this.updateTickSizeWidth();
this.range = this.getDateRange(this.dataRange.start, this.dataRange.end);
this.dispatcher = new ModelChangesDispatcher();
const modelChangesListener = this.ganttOwner.getModelChangesListener();
if(modelChangesListener)
this.dispatcher.onModelChanged.add(modelChangesListener);
this.viewModel = new ViewVisualModel(this,
tasksData,
this.ganttOwner.getGanttDependenciesData(),
this.ganttOwner.getGanttResourcesData(),
this.ganttOwner.getGanttResourceAssignmentsData(),
this.range,
this.ganttOwner.getGanttWorkTimeRules()
);
this.modelManipulator = new ModelManipulator(this.viewModel, this.dispatcher);
this.history?.historyItems.forEach(i => i.setModelManipulator(this.modelManipulator));
}
resetAndUpdate(): void {
this.range = this.getDateRange(this.dataRange.start, this.dataRange.end);
this.viewModel.updateRange(this.range);
this.renderHelper.resetAndUpdate(this.tickSize, this.range, this.settings.viewType, this.viewModel, this.settings.firstDayOfWeek);
if(Browser.IE)
this.taskEditController.createElements();
this.updateView();
}
cleanMarkup(): void {
this.setNormalScreenMode();
this.renderHelper.taskAreaManagerDetachEvents();
this.taskEditController.detachEvents();
this.clearStripLinesUpdater();
this.renderHelper.reset();
clearTimeout(this._scrollTimeOut);
}
checkAndProcessModelChanges(): boolean {
const tasks = this.ganttOwner.getGanttTasksData();
const changed = this.viewModel.refreshTaskDataIfRequires(tasks);
if(changed)
this.resetAndUpdate();
return changed;
}
updateHistoryObsoleteInsertedKey(oldKey: string, newKey: string, type: string): void {
this.history?.updateObsoleteInsertedKey(oldKey, newKey, type);
if(type === GanttDataObjectNames.dependency)
this.renderHelper.updateRenderedConnectorLinesId(oldKey, newKey);
}
updateRowHeights(height: number): void {
if(this.tickSize.height !== height) {
this.tickSize.height = height;
const leftPosition = this.renderHelper.getTaskAreaContainerScrollLeft();
this.resetAndUpdate();
this.renderHelper.setTaskAreaContainerScrollLeft(leftPosition);
}
}
selectTask(id: string): void {
this.selectDependency(null);
this.viewModel.changeTaskSelected(id, true);
this.currentSelectedTaskID = id;
this.updateBarManager();
}
unselectTask(id: string): void {
this.viewModel.changeTaskSelected(id, false);
this.updateBarManager();
}
selectTaskById(publicId: any): void {
this.unselectCurrentSelectedTask();
const task = this.getTaskByPublicId(publicId);
if(task)
this.selectTask(task.internalId);
}
selectDependency(id: string): void {
this.taskEditController.selectDependency(id);
this.renderHelper.createConnectorLines();
}
getTaskAreaContainer(): ITaskAreaContainer {
return this.renderHelper.taskAreaContainer;
}
setWidth(value: number): void {
this.renderHelper.setMainElementWidth(value);
}
setHeight(value: number): void {
this.renderHelper.setMainElementHeight(value);
}
setAllowSelection(value: boolean): void {
this.settings.allowSelectTask = value;
}
setEditingSettings(value: EditingSettings): void {
this.settings.editing = value;
this.updateBarManager();
}
setValidationSettings(value: ValidationSettings): void {
this.settings.validation = value;
}
setRowLinesVisible(value: boolean): void {
this.settings.areHorizontalBordersEnabled = value;
this.renderHelper.prepareTaskAreaContainer();
this.resetAndUpdate();
}
setStripLines(stripLines: StripLineSettings): void {
this.settings.stripLines = StripLineSettings.parse(stripLines);
this.clearStripLinesUpdater();
this.initializeStripLinesUpdater();
this.renderHelper.recreateStripLines();
}
deleteTask(key: any): void {
const task = this.getTaskByPublicId(key.toString());
if(task)
this.commandManager.removeTaskCommand.execute(task.internalId, false, true);
}
insertTask(data: any): string {
if(data) {
const parentId = data.parentId != null ? String(data.parentId) : null;
const parent = this.getTaskByPublicId(parentId);
const rootId = this.viewModel.getRootTaskId();
const start = typeof data.start === "string" ? new Date(data.start) : data.start as Date;
const end = typeof data.end === "string" ? new Date(data.end) : data.end as Date;
const taskData = {
parentId: rootId && parentId === rootId ? parentId : parent?.internalId,
title: data.title,
start: start,
end: end,
progress: parseInt(data.progress) || 0,
color: data.color
};
if(this.commandManager.createTaskCommand.execute(taskData))
return this.getLastInsertedTaskId();
}
return "";
}
updateTask(key: any, data: Record<string, any>): void {
const task = this.getTaskByPublicId(key.toString());
const dataToExecute = this._getTaskDataForUpdate(data, task);
if(dataToExecute)
this.commandManager.updateTaskCommand.execute(task.internalId, dataToExecute);
}
getTaskData(key: any): any {
const task = this.getTaskByPublicId(key.toString());
if(task)
return this.viewModel.getTaskObjectForDataSource(task);
}
insertDependency(data: any): void {
if(!data)
return;
const predecessorId = String(data.predecessorId);
const predecessor = this.getTaskByPublicId(predecessorId);
const successorId = String(data.successorId);
const successor = this.getTaskByPublicId(successorId);
const type = data.type as DependencyType;
if(predecessor && successor && this.validationController.canCreateDependency(predecessorId, successorId))
this.commandManager.createDependencyCommand.execute(predecessor.internalId, successor.internalId, type);
}
deleteDependency(key: any): void {
const internalKey = this.viewModel.convertPublicToInternalKey("dependency", key);
if(isDefined(internalKey))
this.commandManager.removeDependencyCommand.execute(internalKey);
}
getDependencyData(key: any): any {
return this.viewModel.getDependencyObjectForDataSource(key);
}
insertResource(data: any, taskKeys: any[]): void {
if(data) {
const callback = (id: any) => {
if(isDefined(taskKeys))
for(let i = 0; i < taskKeys.length; i++)
this.assignResourceToTask(id, taskKeys[i]);
};
this.commandManager.createResourceCommand.execute(String(data.text), data.color && String(data.color), callback);
}
}
deleteResource(key: any): void {
const internalKey = this.viewModel.convertPublicToInternalKey("resource", key);
if(isDefined(internalKey))
this.commandManager.removeResourceCommand.execute(internalKey);
}
assignResourceToTask(resourceKey: any, taskKey: any): void {
const resourceInternalKey = this.viewModel.convertPublicToInternalKey("resource", resourceKey);
const taskInternalKey = this.viewModel.convertPublicToInternalKey("task", taskKey);
if(isDefined(resourceInternalKey) && isDefined(taskInternalKey))
this.commandManager.assignResourceCommand.execute(resourceInternalKey, taskInternalKey);
}
unassignResourceFromTask(resourceKey: any, taskKey: any): void {
const assignment = this.viewModel.findAssignment(resourceKey, taskKey);
if(assignment)
this.commandManager.deassignResourceCommand.execute(assignment.internalId);
}
unassignAllResourcesFromTask(taskPublicKey: any): void {
const taskInternalKey = this.viewModel.convertPublicToInternalKey("task", taskPublicKey);
const assignments = this.viewModel.findAllTaskAssignments(taskInternalKey);
assignments.forEach(assignment => this.commandManager.deassignResourceCommand.execute(assignment.internalId));
}
getResourceData(key: any): any {
return this.viewModel.getResourceObjectForDataSource(key);
}
getResourceAssignmentData(key: any): any {
return this.viewModel.getResourceAssignmentObjectForDataSource(key);
}
getTaskResources(key): Array<Resource> {
const model = this.viewModel;
const task = model.getItemByPublicId("task", key) as Task;
return (task && model.getAssignedResources(task).items) || [];
}
getVisibleTaskKeys(): Array<any> { return this.viewModel.getVisibleTasks().map(t => t.id); }
getVisibleDependencyKeys(): Array<any> { return this.viewModel.getVisibleDependencies().map(d => d.id); }
getVisibleResourceKeys(): Array<any> { return this.viewModel.getVisibleResources().map(r => r.id); }
getVisibleResourceAssignmentKeys(): Array<any> { return this.viewModel.getVisibleResourceAssignments().map(a => a.id); }
getTasksExpandedState(): Record<any, boolean> { return this.viewModel.getTasksExpandedState(); }
applyTasksExpandedState(state: Record<any, boolean>): void { this.viewModel.applyTasksExpandedState(state); }
updateWithDataReload(keepExpandState: boolean): void {
if(this._updateWithModelReloadLockedCounter > 0) {
this._pendingUpdateInfo = { keepExpandState: keepExpandState };
return;
}
const state = keepExpandState && this.getTasksExpandedState();
this.loadOptionsFromGanttOwner();
if(keepExpandState)
this.applyTasksExpandedState(state);
else
this.resetAndUpdate();
const activeDialog = DialogBase.activeInstance;
if(activeDialog && activeDialog.canRefresh && activeDialog.getDialogName() === "TaskEdit")
activeDialog.refresh();
this.dispatcher.notifyGanttViewUpdated();
}
onBrowserWindowResize(): void {
if(this.fullScreenModeHelper.isInFullScreenMode)
this.fullScreenModeHelper.adjustControlInFullScreenMode();
else
this.adjustOwnerControl();
}
getTaskTreeLine(taskKey: string): Array<string> {
return this.viewModel.getTaskTreeLine(taskKey).reverse();
}
isInFullScreenMode(): boolean {
return !!this.fullScreenModeHelper?.isInFullScreenMode;
}
setFullScreenMode(): void {
if(!this.isInFullScreenMode())
this.fullScreenModeHelper.toggle();
}
setNormalScreenMode(): void {
if(this.isInFullScreenMode())
this.fullScreenModeHelper.toggle();
}
setTaskValue(id: string, fieldName: string, newValue: any): boolean {
const command = this.commandManager.updateTaskCommand;
const task = this.getTaskByPublicId(id);
const data = { };
if(task) {
if(fieldName === "title")
data[fieldName] = newValue ? newValue : "";
if(fieldName === "progress")
data[fieldName] = newValue;
if(fieldName === "start")
data[fieldName] = DateTimeUtils.getMinDate(newValue, task.end);
if(fieldName === "end")
data[fieldName] = DateTimeUtils.getMaxDate(newValue, task.start);
}
return Object.keys(data).length > 0 ? command.execute(task.internalId, data) : false;
}
getLastInsertedTaskId(): string {
const createTaskItems = this.history.historyItems.filter(i => i instanceof CreateTaskHistoryItem);
const lastItem = createTaskItems[createTaskItems.length - 1] as CreateTaskHistoryItem;
return lastItem && lastItem.insertedKey;
}
getTaskByPublicId(id: string): Task {
return this.viewModel.tasks.getItemByPublicId(id);
}
getPrevTask(taskId: string): Task {
const item = this.viewModel.findItem(taskId);
if(!item)
return null;
const parent = item.parent || this.viewModel.root;
const index = parent.children.indexOf(item) - 1;
return index > -1 ? item.parent.children[index].task : item.parent.task;
}
getTaskIdByInternalId(internalId : string): string {
const item = this.viewModel.findItem(internalId);
const task = item && item.task;
return task ? task.id : null;
}
isTaskHasChildren(taskId: string): boolean {
const item = this.viewModel.findItem(taskId);
return item && item.children.length > 0;
}
requireFirstLoadParentAutoCalc(): boolean {
const owner = this.ganttOwner;
return owner.getRequireFirstLoadParentAutoCalc && owner.getRequireFirstLoadParentAutoCalc();
}
public updateOwnerInAutoParentMode(): void {
if(this.viewModel.parentAutoCalc)
this.dispatcher.notifyParentDataRecalculated(this.viewModel.getCurrentTaskData());
}
getOwnerControlMainElement(): HTMLElement {
const owner = this.ganttOwner;
return owner.getMainElement && owner.getMainElement();
}
adjustOwnerControl(): void {
const owner = this.ganttOwner;
if(owner.adjustControl)
owner.adjustControl();
}
applySettings(settings: any, preventViewUpdate: boolean = false): void {
const ganttSettings = Settings.parse(settings);
const preventUpdate = preventViewUpdate || this.settings.equal(ganttSettings);
this.settings = ganttSettings;
if(!preventUpdate)
this.resetAndUpdate();
}
getDataUpdateErrorCallback(): () => void {
const history = this.history;
const currentHistoryItemInfo = history.getCurrentProcessingItemInfo();
return () => {
this.dispatcher.lock();
history.rollBackAndRemove(currentHistoryItemInfo);
this.dispatcher.unlock();
this.updateBarManager();
};
}
setTaskTooltipContentTemplate(taskTooltipContentTemplate: () => void): void {
this.settings.taskTooltipContentTemplate = taskTooltipContentTemplate;
}
setTaskProgressTooltipContentTemplate(taskProgressTooltipContentTemplate: () => void): void {
this.settings.taskProgressTooltipContentTemplate = taskProgressTooltipContentTemplate;
}
setTaskTimeTooltipContentTemplate(taskTimeTooltipContentTemplate: () => void): void {
this.settings.taskTimeTooltipContentTemplate = taskTimeTooltipContentTemplate;
}
setTaskContentTemplate(taskContentTemplate: () => boolean): void {
this.settings.taskContentTemplate = taskContentTemplate;
}
updateBarManager(): void {
this.barManager.updateItemsState([]);
}
onTaskAreaClick(rowIndex: number, evt: Event): boolean {
const clickedItem = this.viewModel.items[rowIndex];
return clickedItem && this.onTaskClick(clickedItem.task?.id, evt);
}
onTaskAreaDblClick(rowIndex: number, evt: Event): void {
const clickedItem = this.viewModel.items[rowIndex];
if(clickedItem && this.onTaskDblClick(clickedItem.task.id, evt))
this.commandManager.showTaskEditDialog.execute(clickedItem.task);
}
onTaskAreaContextMenu(rowIndex: number, evt: Event, type: string): void {
const isDependency = type === "dependency";
const event = evt as MouseEvent | TouchEvent;
const model = this.viewModel;
const key = isDependency ? model.convertInternalToPublicKey("dependency", EvtUtils.getEventSource(evt).getAttribute("dependency-id")) : model.items[rowIndex]?.task?.id;
if(this.onGanttViewContextMenu(evt, key, type)) {
const info = {
event: evt,
type: type,
key: key,
position: new Point(EvtUtils.getEventX(event), EvtUtils.getEventY(event))
};
this.showPopupMenu(info);
}
}
onTaskSelectionChanged(rowIndex: number, evt: Event): void {
const clickedTask = this.viewModel.items[rowIndex];
this.isFocus = DomUtils.isItParent(this.renderHelper.taskArea, EvtUtils.getEventSource(evt));
if(clickedTask && this.isFocus && this.settings.allowSelectTask)
setTimeout(() => { this.changeGanttTaskSelection(clickedTask.task.id, true); }, 0);
}
onTaskClick(key: any, evt: any): boolean {
if(!this.ganttOwner.onTaskClick)
return true;
return this.ganttOwner.onTaskClick(key, evt);
}
onTaskDblClick(key: any, evt: any): boolean {
if(!this.ganttOwner.onTaskDblClick)
return true;
return this.ganttOwner.onTaskDblClick(key, evt);
}
getDateFormat(date: Date): string {
return this.ganttOwner.getFormattedDateText ? this.ganttOwner.getFormattedDateText(date) : this.getDefaultDateFormat(date);
}
getDefaultDateFormat(date: Date): string {
return ("0" + date.getDate()).slice(-2) + "/" + ("0" + (date.getMonth() + 1)).slice(-2) + "/" + date.getFullYear() + " " + ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2);
}
destroyTemplate(container: HTMLElement): void {
if(this.ganttOwner.destroyTemplate)
this.ganttOwner.destroyTemplate(container);
else
container.innerHTML = "";
}
onTaskAreaSizeChanged(info: Record<string, any>): void {
if(this.ganttOwner.onTaskAreaSizeChanged)
this.ganttOwner.onTaskAreaSizeChanged(info);
}
showTaskEditDialog(): void {
this.commandManager.showTaskEditDialog.execute();
}
showTaskDetailsDialog(taskPublicKey: any): void {
const task = this.getTaskByPublicId(taskPublicKey);
if(task)
this.commandManager.showTaskEditDialog.execute(task, true);
}
showResourcesDialog(): void {
this.commandManager.showResourcesDialog.execute();
}
getCommandByKey(key: GanttClientCommand): ICommand {
return this.commandManager.getCommand(key);
}
private _getTaskDataForUpdate(data: Record<string, any>, task: Task): Record<string, any> {
const result = { };
if(task && data) {
if(isDefined(data.title) && data.title !== task.title)
result["title"] = data.title;
if(isDefined(data.progress) && data.progress !== task.progress)
result["progress"] = data.progress;
if(isDefined(data.start) && data.start !== task.start)
result["start"] = data.start;
if(isDefined(data.end) && data.end !== task.end)
result["end"] = data.end;
if(isDefined(data.color) && data.color !== task.color)
result["color"] = data.color;
}
return Object.keys(result).length > 0 ? result : null;
}
public updateViewDataRange(): void {
const model = this.viewModel;
const minStart = model.getTaskMinStart();
const maxEnd = model.getTaskMaxEnd();
const startChanged = minStart.getTime() < this.dataRange.start.getTime();
const endChanged = maxEnd.getTime() > this.dataRange.end.getTime();
if(startChanged)
this.dataRange.start = minStart;
if(endChanged)
this.dataRange.end = maxEnd;
if(startChanged || endChanged)
this.resetAndUpdate();
}
}