gantt-task-react/src/components/gantt/task-gantt-content.tsx

314 lines
8.3 KiB
TypeScript
Raw Normal View History

2020-08-09 10:51:25 +03:00
import React, { useEffect, useState } from "react";
2020-08-05 08:14:22 +03:00
import { Task, EventOption } from "../../types/public-types";
import { Bar } from "../bar/bar";
2020-08-05 08:14:22 +03:00
import { BarTask } from "../../types/bar-task";
import { Arrow } from "../other/arrow";
2020-07-22 20:50:43 +03:00
import {
convertToBarTasks,
2020-08-09 10:51:25 +03:00
handleTaskBySVGMouseEvent,
BarMoveAction,
2020-08-05 08:14:22 +03:00
} from "../../helpers/bar-helper";
import { Tooltip } from "../other/tooltip";
2020-08-09 10:51:25 +03:00
import { isKeyboardEvent } from "../../helpers/other-helper";
2020-08-05 08:14:22 +03:00
2020-08-09 10:51:25 +03:00
export type GanttContentMoveAction =
| "mouseenter"
| "mouseleave"
| "delete"
2020-08-11 01:16:53 +03:00
| "dblclick"
2020-08-09 10:51:25 +03:00
| BarMoveAction;
export type BarEvent = {
selectedTask?: BarTask;
originalTask?: BarTask;
2020-08-09 10:51:25 +03:00
action: GanttContentMoveAction;
};
export type TaskGanttContentProps = {
2020-07-22 20:50:43 +03:00
tasks: Task[];
dates: Date[];
rowHeight: number;
barCornerRadius: number;
columnWidth: number;
barFill: number;
2020-07-30 00:01:51 +03:00
barProgressColor: string;
barProgressSelectedColor: string;
barBackgroundColor: string;
barBackgroundSelectedColor: string;
2020-07-22 20:50:43 +03:00
handleWidth: number;
timeStep: number;
svg?: React.RefObject<SVGSVGElement>;
svgHeight: number;
2020-07-22 20:50:43 +03:00
arrowColor: string;
arrowIndent: number;
fontSize: string;
fontFamily: string;
TooltipContent: React.FC<{
task: Task;
fontSize: string;
fontFamily: string;
}>;
2020-08-11 01:16:53 +03:00
onTasksDateChange: (tasks: Task[]) => void;
2020-07-22 20:50:43 +03:00
} & EventOption;
export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
2020-07-22 20:50:43 +03:00
tasks,
2020-08-11 01:16:53 +03:00
dates,
2020-07-22 20:50:43 +03:00
rowHeight,
barCornerRadius,
columnWidth,
barFill,
2020-07-30 00:01:51 +03:00
barProgressColor,
barProgressSelectedColor,
barBackgroundColor,
barBackgroundSelectedColor,
2020-07-22 20:50:43 +03:00
handleWidth,
timeStep,
2020-08-11 01:16:53 +03:00
svg,
svgHeight,
2020-08-11 01:16:53 +03:00
arrowColor,
arrowIndent,
2020-07-22 20:50:43 +03:00
fontFamily,
fontSize,
2020-08-11 01:16:53 +03:00
onTasksDateChange,
2020-07-22 20:50:43 +03:00
onDateChange,
onProgressChange,
onDoubleClick,
onTaskDelete,
TooltipContent,
2020-07-22 20:50:43 +03:00
}) => {
const point = svg?.current?.createSVGPoint();
2020-07-30 22:58:23 +03:00
const [barEvent, setBarEvent] = useState<BarEvent>({
2020-08-05 08:14:22 +03:00
action: "",
2020-07-30 22:58:23 +03:00
});
2020-07-22 20:50:43 +03:00
const [barTasks, setBarTasks] = useState<BarTask[]>([]);
const [xStep, setXStep] = useState(0);
const [initEventX1Delta, setInitEventX1Delta] = useState(0);
2020-08-09 10:51:25 +03:00
const [isMoving, setIsMoving] = useState(false);
2020-08-11 01:16:53 +03:00
2020-08-09 10:51:25 +03:00
// create xStep
2020-07-22 20:50:43 +03:00
useEffect(() => {
const dateDelta =
dates[1].getTime() -
dates[0].getTime() -
dates[1].getTimezoneOffset() * 60 * 1000 +
dates[0].getTimezoneOffset() * 60 * 1000;
const newXStep = (timeStep * columnWidth) / dateDelta;
2020-08-13 22:57:59 +03:00
setXStep(newXStep);
}, [columnWidth, dates, timeStep]);
2020-07-22 20:50:43 +03:00
2020-08-09 10:51:25 +03:00
// generate tasks
2020-07-22 20:50:43 +03:00
useEffect(() => {
setBarTasks(
convertToBarTasks(
tasks,
dates,
columnWidth,
rowHeight,
2020-08-11 01:16:53 +03:00
barFill,
2020-07-22 20:50:43 +03:00
barCornerRadius,
2020-07-30 00:01:51 +03:00
handleWidth,
barProgressColor,
barProgressSelectedColor,
barBackgroundColor,
barBackgroundSelectedColor
2020-07-22 20:50:43 +03:00
)
);
}, [
tasks,
rowHeight,
barCornerRadius,
columnWidth,
dates,
barFill,
handleWidth,
2020-08-09 10:51:25 +03:00
barProgressColor,
barProgressSelectedColor,
barBackgroundColor,
barBackgroundSelectedColor,
2020-07-22 20:50:43 +03:00
]);
2020-08-09 10:51:25 +03:00
/**
* Method is Start point of task change
*/
2020-08-11 01:16:53 +03:00
const handleBarEventStart = async (
2020-08-09 10:51:25 +03:00
event: React.MouseEvent | React.KeyboardEvent,
action: GanttContentMoveAction,
selectedTask: BarTask
) => {
if (isKeyboardEvent(event)) {
if (action === "delete") {
2020-08-11 01:16:53 +03:00
if (onTaskDelete) {
await onTaskDelete(selectedTask);
const newTasks = barTasks.filter(t => t.id !== selectedTask.id);
2020-08-11 01:16:53 +03:00
onTasksDateChange(newTasks);
}
2020-08-09 10:51:25 +03:00
}
} else if (action === "mouseenter") {
if (!barEvent.action) {
setBarEvent({ action, selectedTask, originalTask: selectedTask });
2020-08-09 10:51:25 +03:00
}
} else if (action === "mouseleave") {
if (barEvent.action === "mouseenter") {
setBarEvent({ action: "" });
}
} else if (action === "move") {
if (!svg?.current || !point) return;
2020-08-09 10:51:25 +03:00
point.x = event.clientX;
const cursor = point.matrixTransform(
svg.current.getScreenCTM()?.inverse()
);
setInitEventX1Delta(cursor.x - selectedTask.x1);
setBarEvent({ action, selectedTask, originalTask: selectedTask });
2020-08-11 01:16:53 +03:00
} else if (action === "dblclick") {
!!onDoubleClick && onDoubleClick(selectedTask);
2020-08-09 10:51:25 +03:00
} else {
setBarEvent({
action,
selectedTask,
originalTask: selectedTask,
2020-08-09 10:51:25 +03:00
});
}
};
2020-07-30 22:58:23 +03:00
2020-08-09 10:51:25 +03:00
useEffect(() => {
const handleMouseMove = async (event: MouseEvent) => {
if (!barEvent.selectedTask || !point || !svg?.current) return;
2020-08-09 10:51:25 +03:00
event.preventDefault();
2020-08-05 08:14:22 +03:00
2020-08-09 10:51:25 +03:00
point.x = event.clientX;
const cursor = point.matrixTransform(
svg?.current.getScreenCTM()?.inverse()
2020-08-09 10:51:25 +03:00
);
2020-07-30 22:58:23 +03:00
2020-08-09 10:51:25 +03:00
const { isChanged, changedTask } = handleTaskBySVGMouseEvent(
cursor.x,
barEvent.action as BarMoveAction,
barEvent.selectedTask,
xStep,
timeStep,
initEventX1Delta
);
if (isChanged) {
setBarTasks(
barTasks.map(t => (t.id === changedTask.id ? changedTask : t))
);
setBarEvent({ ...barEvent, selectedTask: changedTask });
}
};
2020-07-22 20:50:43 +03:00
2020-08-09 10:51:25 +03:00
const handleMouseUp = async (event: MouseEvent) => {
const { selectedTask, action, originalTask } = barEvent;
if (!selectedTask || !point || !svg?.current || !originalTask) return;
2020-08-09 10:51:25 +03:00
event.preventDefault();
2020-07-22 20:50:43 +03:00
2020-08-09 10:51:25 +03:00
point.x = event.clientX;
const cursor = point.matrixTransform(
svg?.current.getScreenCTM()?.inverse()
2020-08-09 10:51:25 +03:00
);
2020-07-22 20:50:43 +03:00
2020-08-09 10:51:25 +03:00
const { changedTask } = handleTaskBySVGMouseEvent(
cursor.x,
action as BarMoveAction,
selectedTask,
xStep,
timeStep,
initEventX1Delta
);
const isNotLikeOriginal =
originalTask.start !== changedTask.start ||
originalTask.end !== changedTask.end ||
originalTask.progress !== changedTask.progress;
2020-08-09 10:51:25 +03:00
if (
(action === "move" || action === "end" || action === "start") &&
onDateChange &&
isNotLikeOriginal
2020-08-09 10:51:25 +03:00
) {
2020-08-11 01:16:53 +03:00
await onDateChange(changedTask);
const newTasks = barTasks.map(t =>
t.id === changedTask.id ? changedTask : t
);
onTasksDateChange(newTasks);
} else if (onProgressChange && isNotLikeOriginal) {
2020-08-11 01:16:53 +03:00
await onProgressChange(changedTask);
2020-07-22 20:50:43 +03:00
}
2020-08-09 10:51:25 +03:00
svg.current.removeEventListener("mousemove", handleMouseMove);
svg.current.removeEventListener("mouseup", handleMouseUp);
setBarEvent({ action: "" });
setIsMoving(false);
2020-07-22 20:50:43 +03:00
};
2020-08-05 08:14:22 +03:00
if (
2020-08-09 10:51:25 +03:00
!isMoving &&
(barEvent.action === "move" ||
barEvent.action === "end" ||
barEvent.action === "start" ||
barEvent.action === "progress") &&
svg?.current
2020-08-05 08:14:22 +03:00
) {
2020-08-09 10:51:25 +03:00
svg.current.addEventListener("mousemove", handleMouseMove);
svg.current.addEventListener("mouseup", handleMouseUp);
setIsMoving(true);
2020-07-22 20:50:43 +03:00
}
}, [
2020-08-09 10:51:25 +03:00
barTasks,
2020-07-22 20:50:43 +03:00
barEvent,
xStep,
initEventX1Delta,
onProgressChange,
timeStep,
onDateChange,
2020-08-09 10:51:25 +03:00
svg,
isMoving,
2020-07-22 20:50:43 +03:00
]);
return (
2020-08-05 08:14:22 +03:00
<g className="content">
<g className="arrows" fill={arrowColor} stroke={arrowColor}>
2020-07-22 20:50:43 +03:00
{barTasks.map(task => {
return task.barChildren.map(child => {
return (
<Arrow
2020-08-09 10:51:25 +03:00
key={`Arrow from ${task.id} to ${tasks[child].id}`}
2020-07-22 20:50:43 +03:00
taskFrom={task}
taskTo={barTasks[child]}
rowHeight={rowHeight}
arrowIndent={arrowIndent}
/>
);
});
})}
</g>
<g className="bar" fontFamily={fontFamily} fontSize={fontSize}>
{barTasks.map(task => {
return (
<Bar
task={task}
arrowIndent={arrowIndent}
isProgressChangeable={!!onProgressChange && !task.isDisabled}
isDateChangeable={!!onDateChange && !task.isDisabled}
isDelete={!task.isDisabled}
2020-08-09 10:51:25 +03:00
onEventStart={handleBarEventStart}
2020-07-22 20:50:43 +03:00
key={task.id}
/>
);
})}
</g>
<g className="toolTip">
2020-08-09 10:51:25 +03:00
{barEvent.selectedTask && (
<Tooltip
x={barEvent.selectedTask.x2 + arrowIndent + arrowIndent * 0.5}
rowHeight={rowHeight}
svgHeight={svgHeight}
2020-08-09 10:51:25 +03:00
task={barEvent.selectedTask}
fontFamily={fontFamily}
fontSize={fontSize}
TooltipContent={TooltipContent}
2020-08-09 10:51:25 +03:00
/>
)}
2020-07-22 20:50:43 +03:00
</g>
2020-08-05 08:14:22 +03:00
</g>
2020-07-22 20:50:43 +03:00
);
};