gantt-task-react/src/helpers/bar-helper.ts

593 lines
14 KiB
TypeScript
Raw Normal View History

2020-08-05 08:14:22 +03:00
import { Task } from "../types/public-types";
2021-03-30 12:00:43 +03:00
import { BarTask, TaskTypeInternal } from "../types/bar-task";
2021-03-01 21:55:27 +02:00
import { BarMoveAction } from "../types/gantt-task-actions";
2020-07-22 20:50:43 +03:00
export const convertToBarTasks = (
tasks: Task[],
dates: Date[],
columnWidth: number,
rowHeight: number,
2021-03-01 21:55:27 +02:00
taskHeight: number,
2020-07-22 20:50:43 +03:00
barCornerRadius: number,
2020-07-30 00:01:51 +03:00
handleWidth: number,
2021-06-28 23:15:53 +03:00
rtl: boolean,
2020-07-30 00:01:51 +03:00
barProgressColor: string,
barProgressSelectedColor: string,
barBackgroundColor: string,
2021-03-01 21:55:27 +02:00
barBackgroundSelectedColor: string,
2021-03-28 16:12:28 +03:00
projectProgressColor: string,
projectProgressSelectedColor: string,
projectBackgroundColor: string,
projectBackgroundSelectedColor: string,
2021-03-01 21:55:27 +02:00
milestoneBackgroundColor: string,
milestoneBackgroundSelectedColor: string
2020-07-22 20:50:43 +03:00
) => {
let barTasks = tasks.map((t, i) => {
return convertToBarTask(
t,
i,
dates,
columnWidth,
rowHeight,
taskHeight,
barCornerRadius,
2020-07-30 00:01:51 +03:00
handleWidth,
2021-06-28 23:15:53 +03:00
rtl,
2020-07-30 00:01:51 +03:00
barProgressColor,
barProgressSelectedColor,
barBackgroundColor,
2021-03-01 21:55:27 +02:00
barBackgroundSelectedColor,
2021-03-28 16:12:28 +03:00
projectProgressColor,
projectProgressSelectedColor,
projectBackgroundColor,
projectBackgroundSelectedColor,
2021-03-01 21:55:27 +02:00
milestoneBackgroundColor,
milestoneBackgroundSelectedColor
2020-07-22 20:50:43 +03:00
);
});
2020-08-11 01:16:53 +03:00
// set dependencies
2021-08-11 11:28:19 +03:00
barTasks = barTasks.map(task => {
2020-07-22 20:50:43 +03:00
const dependencies = task.dependencies || [];
for (let j = 0; j < dependencies.length; j++) {
const dependence = barTasks.findIndex(
2020-07-22 23:50:25 +03:00
value => value.id === dependencies[j]
2020-07-22 20:50:43 +03:00
);
2021-08-11 11:28:19 +03:00
if (dependence !== -1) barTasks[dependence].barChildren.push(task);
2021-08-09 22:21:18 +03:00
}
return task;
2020-07-22 20:50:43 +03:00
});
return barTasks;
};
2021-03-01 21:55:27 +02:00
const convertToBarTask = (
task: Task,
index: number,
dates: Date[],
columnWidth: number,
rowHeight: number,
taskHeight: number,
barCornerRadius: number,
handleWidth: number,
2021-06-28 23:15:53 +03:00
rtl: boolean,
2021-03-01 21:55:27 +02:00
barProgressColor: string,
barProgressSelectedColor: string,
barBackgroundColor: string,
barBackgroundSelectedColor: string,
2021-03-28 16:12:28 +03:00
projectProgressColor: string,
projectProgressSelectedColor: string,
projectBackgroundColor: string,
projectBackgroundSelectedColor: string,
2021-03-01 21:55:27 +02:00
milestoneBackgroundColor: string,
milestoneBackgroundSelectedColor: string
): BarTask => {
let barTask: BarTask;
switch (task.type) {
case "milestone":
barTask = convertToMilestone(
task,
index,
dates,
columnWidth,
rowHeight,
taskHeight,
barCornerRadius,
handleWidth,
milestoneBackgroundColor,
milestoneBackgroundSelectedColor
);
break;
2021-03-28 16:12:28 +03:00
case "project":
barTask = convertToBar(
task,
index,
dates,
columnWidth,
rowHeight,
taskHeight,
barCornerRadius,
handleWidth,
2021-06-28 23:15:53 +03:00
rtl,
2021-03-28 16:12:28 +03:00
projectProgressColor,
projectProgressSelectedColor,
projectBackgroundColor,
projectBackgroundSelectedColor
);
break;
2021-03-01 21:55:27 +02:00
default:
barTask = convertToBar(
task,
index,
dates,
columnWidth,
rowHeight,
taskHeight,
barCornerRadius,
handleWidth,
2021-06-28 23:15:53 +03:00
rtl,
2021-03-01 21:55:27 +02:00
barProgressColor,
barProgressSelectedColor,
barBackgroundColor,
barBackgroundSelectedColor
);
break;
}
return barTask;
};
const convertToBar = (
2020-07-22 20:50:43 +03:00
task: Task,
index: number,
dates: Date[],
columnWidth: number,
rowHeight: number,
taskHeight: number,
barCornerRadius: number,
2020-07-30 00:01:51 +03:00
handleWidth: number,
2021-06-28 23:15:53 +03:00
rtl: boolean,
2020-07-30 00:01:51 +03:00
barProgressColor: string,
barProgressSelectedColor: string,
barBackgroundColor: string,
barBackgroundSelectedColor: string
2020-07-22 20:50:43 +03:00
): BarTask => {
2021-06-28 23:15:53 +03:00
let x1: number;
let x2: number;
if (rtl) {
x2 = taskXCoordinateRTL(task.start, dates, columnWidth);
x1 = taskXCoordinateRTL(task.end, dates, columnWidth);
2021-06-28 23:15:53 +03:00
} else {
x1 = taskXCoordinate(task.start, dates, columnWidth);
x2 = taskXCoordinate(task.end, dates, columnWidth);
2021-06-28 23:15:53 +03:00
}
let typeInternal: TaskTypeInternal = task.type;
if (typeInternal === "task" && x2 - x1 < handleWidth * 2) {
typeInternal = "smalltask";
x2 = x1 + handleWidth * 2;
}
2021-08-03 21:27:44 +03:00
const [progressWidth, progressX] = progressWithByParams(
x1,
x2,
task.progress,
rtl
);
const y = taskYCoordinate(index, rowHeight, taskHeight);
2021-08-11 11:28:19 +03:00
const hideChildren = task.type === "project" ? task.hideChildren : undefined;
2020-08-09 10:51:25 +03:00
2020-07-30 00:01:51 +03:00
const styles = {
backgroundColor: barBackgroundColor,
backgroundSelectedColor: barBackgroundSelectedColor,
progressColor: barProgressColor,
progressSelectedColor: barProgressSelectedColor,
...task.styles,
};
2020-07-22 20:50:43 +03:00
return {
...task,
2021-03-30 12:00:43 +03:00
typeInternal,
2020-07-22 20:50:43 +03:00
x1,
x2,
y,
index,
2021-06-28 23:15:53 +03:00
progressX,
progressWidth,
2020-07-22 20:50:43 +03:00
barCornerRadius,
handleWidth,
2021-08-11 11:28:19 +03:00
hideChildren,
2020-07-22 20:50:43 +03:00
height: taskHeight,
barChildren: [],
2020-07-30 00:01:51 +03:00
styles,
2020-07-22 20:50:43 +03:00
};
};
2021-03-01 21:55:27 +02:00
const convertToMilestone = (
task: Task,
index: number,
dates: Date[],
columnWidth: number,
rowHeight: number,
taskHeight: number,
barCornerRadius: number,
handleWidth: number,
milestoneBackgroundColor: string,
milestoneBackgroundSelectedColor: string
2021-06-28 23:15:53 +03:00
): BarTask => {
const x = taskXCoordinate(task.start, dates, columnWidth);
2021-03-01 21:55:27 +02:00
const y = taskYCoordinate(index, rowHeight, taskHeight);
const x1 = x - taskHeight * 0.5;
const x2 = x + taskHeight * 0.5;
const rotatedHeight = taskHeight / 1.414;
const styles = {
backgroundColor: milestoneBackgroundColor,
backgroundSelectedColor: milestoneBackgroundSelectedColor,
progressColor: "",
progressSelectedColor: "",
...task.styles,
};
return {
...task,
end: task.start,
x1,
x2,
y,
index,
2021-06-28 23:15:53 +03:00
progressX: 0,
progressWidth: 0,
2021-03-01 21:55:27 +02:00
barCornerRadius,
handleWidth,
2021-03-30 12:00:43 +03:00
typeInternal: task.type,
2021-03-01 21:55:27 +02:00
progress: 0,
height: rotatedHeight,
2021-08-11 11:28:19 +03:00
hideChildren: undefined,
2021-03-01 21:55:27 +02:00
barChildren: [],
styles,
};
};
const taskXCoordinate = (
2020-07-22 20:50:43 +03:00
xDate: Date,
dates: Date[],
columnWidth: number
) => {
const index = dates.findIndex(d => d.getTime() >= xDate.getTime()) - 1;
const remainderMillis = xDate.getTime() - dates[index].getTime();
const percentOfInterval = remainderMillis / (dates[index + 1 ].getTime() - dates[index].getTime());
const x = index * columnWidth + (percentOfInterval * columnWidth);
2020-07-22 20:50:43 +03:00
return x;
};
2021-08-03 21:27:44 +03:00
const taskXCoordinateRTL = (
xDate: Date,
dates: Date[],
columnWidth: number
) => {
let x = taskXCoordinate(xDate, dates, columnWidth);
2021-08-03 21:27:44 +03:00
x += columnWidth;
return x;
};
2021-03-01 21:55:27 +02:00
const taskYCoordinate = (
2020-07-22 20:50:43 +03:00
index: number,
rowHeight: number,
taskHeight: number
2020-07-22 20:50:43 +03:00
) => {
const y = index * rowHeight + (rowHeight - taskHeight) / 2;
2020-07-22 20:50:43 +03:00
return y;
};
export const progressWithByParams = (
taskX1: number,
taskX2: number,
2021-08-03 21:27:44 +03:00
progress: number,
rtl: boolean
2020-07-22 20:50:43 +03:00
) => {
2021-08-03 21:27:44 +03:00
const progressWidth = (taskX2 - taskX1) * progress * 0.01;
let progressX: number;
if (rtl) {
progressX = taskX2 - progressWidth;
} else {
progressX = taskX1;
}
return [progressWidth, progressX];
2020-07-22 20:50:43 +03:00
};
export const progressByProgressWidth = (
progressWidth: number,
barTask: BarTask
) => {
const barWidth = barTask.x2 - barTask.x1;
const progressPercent = Math.round((progressWidth * 100) / barWidth);
if (progressPercent >= 100) return 100;
else if (progressPercent <= 0) return 0;
2021-06-28 23:15:53 +03:00
else return progressPercent;
2020-07-22 20:50:43 +03:00
};
2021-03-01 21:55:27 +02:00
const progressByX = (x: number, task: BarTask) => {
2020-07-22 20:50:43 +03:00
if (x >= task.x2) return 100;
else if (x <= task.x1) return 0;
else {
const barWidth = task.x2 - task.x1;
const progressPercent = Math.round(((x - task.x1) * 100) / barWidth);
return progressPercent;
}
};
2021-08-03 21:27:44 +03:00
const progressByXRTL = (x: number, task: BarTask) => {
if (x >= task.x2) return 0;
else if (x <= task.x1) return 100;
else {
const barWidth = task.x2 - task.x1;
const progressPercent = Math.round(((task.x2 - x) * 100) / barWidth);
return progressPercent;
}
};
2020-07-22 20:50:43 +03:00
export const getProgressPoint = (
progressX: number,
taskY: number,
taskHeight: number
) => {
const point = [
progressX - 5,
taskY + taskHeight,
progressX + 5,
taskY + taskHeight,
progressX,
taskY + taskHeight - 8.66,
];
2020-08-05 08:14:22 +03:00
return point.join(",");
2020-07-22 20:50:43 +03:00
};
2021-03-01 21:55:27 +02:00
const startByX = (x: number, xStep: number, task: BarTask) => {
2020-07-22 20:50:43 +03:00
if (x >= task.x2 - task.handleWidth * 2) {
x = task.x2 - task.handleWidth * 2;
}
const steps = Math.round((x - task.x1) / xStep);
const additionalXValue = steps * xStep;
const newX = task.x1 + additionalXValue;
return newX;
};
2021-03-01 21:55:27 +02:00
const endByX = (x: number, xStep: number, task: BarTask) => {
2020-07-22 20:50:43 +03:00
if (x <= task.x1 + task.handleWidth * 2) {
x = task.x1 + task.handleWidth * 2;
}
const steps = Math.round((x - task.x2) / xStep);
const additionalXValue = steps * xStep;
const newX = task.x2 + additionalXValue;
return newX;
};
2021-03-01 21:55:27 +02:00
const moveByX = (x: number, xStep: number, task: BarTask) => {
2020-07-22 20:50:43 +03:00
const steps = Math.round((x - task.x1) / xStep);
const additionalXValue = steps * xStep;
const newX1 = task.x1 + additionalXValue;
const newX2 = newX1 + task.x2 - task.x1;
return [newX1, newX2];
};
2021-03-01 21:55:27 +02:00
const dateByX = (
2020-07-22 20:50:43 +03:00
x: number,
taskX: number,
taskDate: Date,
xStep: number,
timeStep: number
) => {
let newDate = new Date(((x - taskX) / xStep) * timeStep + taskDate.getTime());
newDate = new Date(
newDate.getTime() +
(newDate.getTimezoneOffset() - taskDate.getTimezoneOffset()) * 60000
);
return newDate;
};
2020-08-09 10:51:25 +03:00
/**
* Method handles event in real time(mousemove) and on finish(mouseup)
*/
export const handleTaskBySVGMouseEvent = (
svgX: number,
action: BarMoveAction,
selectedTask: BarTask,
xStep: number,
timeStep: number,
2021-06-28 23:15:53 +03:00
initEventX1Delta: number,
rtl: boolean
2021-03-01 21:55:27 +02:00
): { isChanged: boolean; changedTask: BarTask } => {
let result: { isChanged: boolean; changedTask: BarTask };
switch (selectedTask.type) {
case "milestone":
result = handleTaskBySVGMouseEventForMilestone(
svgX,
action,
selectedTask,
xStep,
timeStep,
initEventX1Delta
);
break;
default:
result = handleTaskBySVGMouseEventForBar(
svgX,
action,
selectedTask,
xStep,
timeStep,
2021-06-28 23:15:53 +03:00
initEventX1Delta,
rtl
2021-03-01 21:55:27 +02:00
);
break;
}
return result;
};
const handleTaskBySVGMouseEventForBar = (
svgX: number,
action: BarMoveAction,
selectedTask: BarTask,
xStep: number,
timeStep: number,
2021-06-28 23:15:53 +03:00
initEventX1Delta: number,
rtl: boolean
2021-03-01 21:55:27 +02:00
): { isChanged: boolean; changedTask: BarTask } => {
2020-08-09 10:51:25 +03:00
const changedTask: BarTask = { ...selectedTask };
let isChanged = false;
switch (action) {
case "progress":
2021-08-03 21:27:44 +03:00
if (rtl) {
changedTask.progress = progressByXRTL(svgX, selectedTask);
} else {
changedTask.progress = progressByX(svgX, selectedTask);
}
2020-08-09 10:51:25 +03:00
isChanged = changedTask.progress !== selectedTask.progress;
2021-06-28 23:15:53 +03:00
if (isChanged) {
2021-08-03 21:27:44 +03:00
const [progressWidth, progressX] = progressWithByParams(
2021-06-28 23:15:53 +03:00
changedTask.x1,
changedTask.x2,
2021-08-03 21:27:44 +03:00
changedTask.progress,
rtl
2021-06-28 23:15:53 +03:00
);
2021-08-03 21:27:44 +03:00
changedTask.progressWidth = progressWidth;
changedTask.progressX = progressX;
2021-06-28 23:15:53 +03:00
}
2020-08-09 10:51:25 +03:00
break;
case "start": {
const newX1 = startByX(svgX, xStep, selectedTask);
changedTask.x1 = newX1;
isChanged = changedTask.x1 !== selectedTask.x1;
if (isChanged) {
2021-08-03 21:27:44 +03:00
if (rtl) {
changedTask.end = dateByX(
newX1,
selectedTask.x1,
selectedTask.end,
xStep,
timeStep
);
} else {
changedTask.start = dateByX(
newX1,
selectedTask.x1,
selectedTask.start,
xStep,
timeStep
);
}
const [progressWidth, progressX] = progressWithByParams(
changedTask.x1,
changedTask.x2,
changedTask.progress,
rtl
2020-08-09 10:51:25 +03:00
);
2021-08-03 21:27:44 +03:00
changedTask.progressWidth = progressWidth;
changedTask.progressX = progressX;
2020-08-09 10:51:25 +03:00
}
break;
}
case "end": {
const newX2 = endByX(svgX, xStep, selectedTask);
changedTask.x2 = newX2;
isChanged = changedTask.x2 !== selectedTask.x2;
if (isChanged) {
2021-08-03 21:27:44 +03:00
if (rtl) {
changedTask.start = dateByX(
newX2,
selectedTask.x2,
selectedTask.start,
xStep,
timeStep
);
} else {
changedTask.end = dateByX(
newX2,
selectedTask.x2,
selectedTask.end,
xStep,
timeStep
);
}
const [progressWidth, progressX] = progressWithByParams(
changedTask.x1,
changedTask.x2,
changedTask.progress,
rtl
2020-08-09 10:51:25 +03:00
);
2021-08-03 21:27:44 +03:00
changedTask.progressWidth = progressWidth;
changedTask.progressX = progressX;
2020-08-09 10:51:25 +03:00
}
break;
}
case "move": {
const [newMoveX1, newMoveX2] = moveByX(
svgX - initEventX1Delta,
xStep,
selectedTask
);
isChanged = newMoveX1 !== selectedTask.x1;
if (isChanged) {
changedTask.start = dateByX(
newMoveX1,
selectedTask.x1,
selectedTask.start,
xStep,
timeStep
);
changedTask.end = dateByX(
newMoveX2,
selectedTask.x2,
selectedTask.end,
xStep,
timeStep
);
changedTask.x1 = newMoveX1;
changedTask.x2 = newMoveX2;
2021-08-03 21:27:44 +03:00
const [progressWidth, progressX] = progressWithByParams(
changedTask.x1,
changedTask.x2,
changedTask.progress,
rtl
);
changedTask.progressWidth = progressWidth;
changedTask.progressX = progressX;
2020-08-09 10:51:25 +03:00
}
break;
}
}
return { isChanged, changedTask };
};
2021-03-01 21:55:27 +02:00
const handleTaskBySVGMouseEventForMilestone = (
svgX: number,
action: BarMoveAction,
selectedTask: BarTask,
xStep: number,
timeStep: number,
initEventX1Delta: number
): { isChanged: boolean; changedTask: BarTask } => {
const changedTask: BarTask = { ...selectedTask };
let isChanged = false;
switch (action) {
case "move": {
const [newMoveX1, newMoveX2] = moveByX(
svgX - initEventX1Delta,
xStep,
selectedTask
);
isChanged = newMoveX1 !== selectedTask.x1;
if (isChanged) {
changedTask.start = dateByX(
newMoveX1,
selectedTask.x1,
selectedTask.start,
xStep,
timeStep
);
changedTask.end = changedTask.start;
changedTask.x1 = newMoveX1;
changedTask.x2 = newMoveX2;
}
break;
}
}
return { isChanged, changedTask };
};