dev temp changes
This commit is contained in:
parent
af2ae879f8
commit
cbded1ad8a
115
example/package-lock.json
generated
115
example/package-lock.json
generated
@ -35,9 +35,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
|
||||
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
|
||||
"version": "7.12.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz",
|
||||
"integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
@ -75,9 +75,9 @@
|
||||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "13.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.10.tgz",
|
||||
"integrity": "sha512-MU10TSgzNABgdzKvQVW1nuuT+sgBMWeXNc3XOs5YXV5SDAK+PPja2eUuBNB9iqElu03xyEDqlnGw0jgl4nbqGQ==",
|
||||
"version": "13.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.11.tgz",
|
||||
"integrity": "sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ==",
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
@ -301,17 +301,17 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
|
||||
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
|
||||
"version": "7.12.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz",
|
||||
"integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"@babel/runtime-corejs3": {
|
||||
"version": "7.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.11.2.tgz",
|
||||
"integrity": "sha512-qh5IR+8VgFz83VBa6OkaET6uN/mJOhHONuy3m1sgF0CV6mXdPSEBdA7e1eUbVvyNtANjMbg22JUv71BaDXLY6A==",
|
||||
"version": "7.12.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz",
|
||||
"integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==",
|
||||
"requires": {
|
||||
"core-js-pure": "^3.0.0",
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
@ -346,11 +346,6 @@
|
||||
"wait-for-expect": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"@types/color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
|
||||
},
|
||||
"@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
|
||||
@ -379,18 +374,18 @@
|
||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.9.49",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.49.tgz",
|
||||
"integrity": "sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g==",
|
||||
"version": "16.9.56",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.56.tgz",
|
||||
"integrity": "sha512-gIkl4J44G/qxbuC6r2Xh+D3CGZpJ+NdWTItAPmZbR5mUS+JQ8Zvzpl0ea5qT/ZT3ZNTUcDKUVqV3xBE8wv/DyQ==",
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"version": "16.9.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz",
|
||||
"integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==",
|
||||
"version": "16.9.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.9.tgz",
|
||||
"integrity": "sha512-jE16FNWO3Logq/Lf+yvEAjKzhpST/Eac8EMd1i4dgZdMczfgqC8EjpxwNgEe3SExHYLliabXDh9DEhhqnlXJhg==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@ -427,9 +422,9 @@
|
||||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "13.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.10.tgz",
|
||||
"integrity": "sha512-MU10TSgzNABgdzKvQVW1nuuT+sgBMWeXNc3XOs5YXV5SDAK+PPja2eUuBNB9iqElu03xyEDqlnGw0jgl4nbqGQ==",
|
||||
"version": "13.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.11.tgz",
|
||||
"integrity": "sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ==",
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
@ -471,11 +466,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
@ -508,14 +502,14 @@
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"core-js-pure": {
|
||||
"version": "3.6.5",
|
||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
|
||||
"integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA=="
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.7.0.tgz",
|
||||
"integrity": "sha512-EZD2ckZysv8MMt4J6HSvS9K2GdtlZtdBncKAmF9lr2n0c9dJUaUN88PSTjvgwCgQPWKTkERXITgS6JJRAnljtg=="
|
||||
},
|
||||
"csstype": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz",
|
||||
"integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag=="
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz",
|
||||
"integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA=="
|
||||
},
|
||||
"dom-accessibility-api": {
|
||||
"version": "0.3.0",
|
||||
@ -550,9 +544,9 @@
|
||||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "15.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
|
||||
"integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
|
||||
"version": "15.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz",
|
||||
"integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==",
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
@ -563,11 +557,10 @@
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
@ -634,11 +627,6 @@
|
||||
"chalk": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@types/color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
|
||||
},
|
||||
"@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
|
||||
@ -662,9 +650,9 @@
|
||||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "15.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
|
||||
"integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
|
||||
"version": "15.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz",
|
||||
"integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==",
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
@ -680,11 +668,10 @@
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
@ -780,9 +767,9 @@
|
||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
|
||||
},
|
||||
"csstype": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz",
|
||||
"integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag=="
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz",
|
||||
"integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -799,18 +786,18 @@
|
||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.9.49",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.49.tgz",
|
||||
"integrity": "sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g==",
|
||||
"version": "16.9.56",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.56.tgz",
|
||||
"integrity": "sha512-gIkl4J44G/qxbuC6r2Xh+D3CGZpJ+NdWTItAPmZbR5mUS+JQ8Zvzpl0ea5qT/ZT3ZNTUcDKUVqV3xBE8wv/DyQ==",
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"csstype": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz",
|
||||
"integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag=="
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz",
|
||||
"integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -2,11 +2,12 @@ import React from "react";
|
||||
import "gantt-task-react/dist/index.css";
|
||||
import { Task, ViewMode, Gantt } from "gantt-task-react";
|
||||
import { ViewSwitcher } from "./components/view-switcher";
|
||||
import { initTasks } from "./helper";
|
||||
|
||||
//Init
|
||||
const App = () => {
|
||||
const currentDate = new Date();
|
||||
const [view, setView] = React.useState<ViewMode>(ViewMode.Day);
|
||||
const [tasks, setTasks] = React.useState<Task[]>(initTasks());
|
||||
const [isChecked, setIsChecked] = React.useState(true);
|
||||
let columnWidth = 60;
|
||||
if (view === ViewMode.Month) {
|
||||
@ -14,85 +15,24 @@ const App = () => {
|
||||
} else if (view === ViewMode.Week) {
|
||||
columnWidth = 250;
|
||||
}
|
||||
let tasks: Task[] = [
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1),
|
||||
end: new Date(
|
||||
currentDate.getFullYear(),
|
||||
currentDate.getMonth(),
|
||||
2,
|
||||
12,
|
||||
28
|
||||
),
|
||||
name: "Idea",
|
||||
id: "Task 0",
|
||||
progress: 45,
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 2),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 4, 0, 0),
|
||||
name: "Research",
|
||||
id: "Task 1",
|
||||
progress: 25,
|
||||
dependencies: ["Task 0"],
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 4),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8, 0, 0),
|
||||
name: "Discussion with team",
|
||||
id: "Task 2",
|
||||
progress: 10,
|
||||
dependencies: ["Task 1"],
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 9, 0, 0),
|
||||
name: "Developing",
|
||||
id: "Task 3",
|
||||
progress: 2,
|
||||
dependencies: ["Task 2"],
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 10),
|
||||
name: "Review",
|
||||
id: "Task 4",
|
||||
progress: 70,
|
||||
dependencies: ["Task 2"],
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 16),
|
||||
name: "Release & Eat Pizza",
|
||||
id: "Task 6",
|
||||
progress: currentDate.getMonth(),
|
||||
dependencies: ["Task 4"],
|
||||
styles: { progressColor: "#ffbb54", progressSelectedColor: "#ff9e0d" },
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 24),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 25),
|
||||
name: "Closing",
|
||||
id: "Task 9",
|
||||
progress: 0,
|
||||
isDisabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
const sleep = (milliseconds: number) => {
|
||||
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||
};
|
||||
let onTaskChange = (task: Task) => {
|
||||
console.log("On date change Id:" + task.id);
|
||||
debugger;
|
||||
const newTasks = tasks.map(t => (t.id === task.id ? task : t));
|
||||
setTasks(newTasks);
|
||||
};
|
||||
|
||||
let onTaskDelete = (task: Task) => {
|
||||
const conf = window.confirm("Are you sure about " + task.name + " ?");
|
||||
if (conf) {
|
||||
setTasks(tasks.filter(t => t.id !== task.id));
|
||||
}
|
||||
return conf;
|
||||
};
|
||||
|
||||
let onProgressChange = async (task: Task) => {
|
||||
await sleep(5000);
|
||||
setTasks(tasks.map(t => (t.id === task.id ? task : t)));
|
||||
console.log("On progress change Id:" + task.id);
|
||||
};
|
||||
|
||||
@ -123,6 +63,7 @@ const App = () => {
|
||||
listCellWidth={isChecked ? "155px" : ""}
|
||||
columnWidth={columnWidth}
|
||||
/>
|
||||
<h3 style={{ color: "#e56b6f" }}>Milestones are not available</h3>
|
||||
<h3>Gantt With Limited Height</h3>
|
||||
<Gantt
|
||||
tasks={tasks}
|
||||
|
||||
77
example/src/helper.tsx
Normal file
77
example/src/helper.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import { Task } from "../../dist/types/public-types";
|
||||
|
||||
export function initTasks() {
|
||||
const currentDate = new Date();
|
||||
const tasks: Task[] = [
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1),
|
||||
end: new Date(
|
||||
currentDate.getFullYear(),
|
||||
currentDate.getMonth(),
|
||||
2,
|
||||
12,
|
||||
28
|
||||
),
|
||||
name: "Idea",
|
||||
id: "Task 0",
|
||||
progress: 45,
|
||||
type: "task",
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 2),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 4, 0, 0),
|
||||
name: "Research",
|
||||
id: "Task 1",
|
||||
progress: 25,
|
||||
dependencies: ["Task 0"],
|
||||
type: "task",
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 4),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8, 0, 0),
|
||||
name: "Discussion with team",
|
||||
id: "Task 2",
|
||||
progress: 10,
|
||||
dependencies: ["Task 1"],
|
||||
type: "task",
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 9, 0, 0),
|
||||
name: "Developing",
|
||||
id: "Task 3",
|
||||
progress: 2,
|
||||
dependencies: ["Task 2"],
|
||||
type: "task",
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 10),
|
||||
name: "Review",
|
||||
id: "Task 4",
|
||||
type: "task",
|
||||
progress: 70,
|
||||
dependencies: ["Task 2"],
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 16),
|
||||
name: "Release",
|
||||
id: "Task 6",
|
||||
progress: currentDate.getMonth(),
|
||||
type: "milestone",
|
||||
dependencies: ["Task 4"],
|
||||
styles: { progressColor: "#ffbb54", progressSelectedColor: "#ff9e0d" },
|
||||
},
|
||||
{
|
||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 24),
|
||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 25),
|
||||
name: "Closing",
|
||||
id: "Task 9",
|
||||
progress: 0,
|
||||
isDisabled: true,
|
||||
type: "task",
|
||||
},
|
||||
];
|
||||
return tasks;
|
||||
}
|
||||
1372
package-lock.json
generated
1372
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@ -43,29 +43,29 @@
|
||||
"@testing-library/react": "^9.5.0",
|
||||
"@testing-library/user-event": "^7.2.1",
|
||||
"@types/jest": "^25.1.4",
|
||||
"@types/node": "^12.12.38",
|
||||
"@types/react": "^16.9.27",
|
||||
"@types/react-dom": "^16.9.7",
|
||||
"@types/node": "^12.19.11",
|
||||
"@types/react": "^16.14.2",
|
||||
"@types/react-dom": "^16.9.10",
|
||||
"@typescript-eslint/eslint-plugin": "^2.26.0",
|
||||
"@typescript-eslint/parser": "^2.26.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint-config-prettier": "^6.7.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint-config-prettier": "^6.15.0",
|
||||
"eslint-config-standard": "^14.1.0",
|
||||
"eslint-config-standard-react": "^9.2.0",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-node": "^11.0.0",
|
||||
"eslint-plugin-prettier": "^3.1.1",
|
||||
"eslint-plugin-prettier": "^3.3.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.17.0",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"eslint-plugin-standard": "^4.1.0",
|
||||
"gh-pages": "^2.2.0",
|
||||
"microbundle-crl": "^0.13.11",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.0.4",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-scripts": "^3.4.3",
|
||||
"prettier": "^2.2.1",
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-scripts": "^3.4.4",
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useState, SyntheticEvent, useRef, useEffect } from "react";
|
||||
import { ViewMode, GanttProps, Task } from "../../types/public-types";
|
||||
import { ViewMode, GanttProps } from "../../types/public-types";
|
||||
import { GridProps } from "../grid/grid";
|
||||
import { ganttDateRange, seedDates } from "../../helpers/date-helper";
|
||||
import { CalendarProps } from "../calendar/calendar";
|
||||
@ -11,8 +11,11 @@ import { Scroll } from "../other/scroll";
|
||||
import { TaskListProps, TaskList } from "../task-list/task-list";
|
||||
import styles from "./gantt.module.css";
|
||||
import { TaskGantt } from "./task-gantt";
|
||||
import { BarTask } from "../../types/bar-task";
|
||||
import { convertToBarTasks } from "../../helpers/bar-helper";
|
||||
import { GanttEvent } from "../../types/gantt-task-actions";
|
||||
|
||||
export const Gantt: React.SFC<GanttProps> = ({
|
||||
export const Gantt: React.FunctionComponent<GanttProps> = ({
|
||||
tasks,
|
||||
headerHeight = 50,
|
||||
columnWidth = 60,
|
||||
@ -27,6 +30,8 @@ export const Gantt: React.SFC<GanttProps> = ({
|
||||
barProgressSelectedColor = "#8282f5",
|
||||
barBackgroundColor = "#b8c2cc",
|
||||
barBackgroundSelectedColor = "#aeb8c2",
|
||||
milestoneBackgroundColor = "#f1c453",
|
||||
milestoneBackgroundSelectedColor = "#f29e4c",
|
||||
handleWidth = 8,
|
||||
timeStep = 300000,
|
||||
arrowColor = "grey",
|
||||
@ -44,21 +49,106 @@ export const Gantt: React.SFC<GanttProps> = ({
|
||||
onSelect,
|
||||
}) => {
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
const [ganttTasks, setGanttTasks] = useState<Task[]>(tasks);
|
||||
const [selectedTask, setSelectedTask] = useState<string>("");
|
||||
const [dates, setDates] = useState<Date[]>(() => {
|
||||
const [startDate, endDate] = ganttDateRange(tasks, viewMode);
|
||||
return seedDates(startDate, endDate, viewMode);
|
||||
});
|
||||
|
||||
const [taskHeight, setTaskHeight] = useState((rowHeight * barFill) / 100);
|
||||
const [barTasks, setBarTasks] = useState<BarTask[]>([]);
|
||||
|
||||
const [ganttEvent, setGanttEvent] = useState<GanttEvent>({
|
||||
action: "",
|
||||
});
|
||||
|
||||
const [selectedTask, setSelectedTask] = useState<BarTask>();
|
||||
const [failedTask, setFailedTask] = useState<BarTask | null>(null);
|
||||
const [scrollY, setScrollY] = useState(0);
|
||||
const [scrollX, setScrollX] = useState(0);
|
||||
const [ignoreScrollEvent, setIgnoreScrollEvent] = useState(false);
|
||||
const [startDate, endDate] = ganttDateRange(ganttTasks, viewMode);
|
||||
const dates = seedDates(startDate, endDate, viewMode);
|
||||
|
||||
const svgHeight = rowHeight * ganttTasks.length;
|
||||
const svgHeight = rowHeight * barTasks.length;
|
||||
const gridWidth = dates.length * columnWidth;
|
||||
const ganttFullHeight = ganttTasks.length * rowHeight;
|
||||
const ganttFullHeight = barTasks.length * rowHeight;
|
||||
|
||||
// task change events
|
||||
useEffect(() => {
|
||||
const [startDate, endDate] = ganttDateRange(tasks, viewMode);
|
||||
const newDates = seedDates(startDate, endDate, viewMode);
|
||||
setDates(newDates);
|
||||
setBarTasks(
|
||||
convertToBarTasks(
|
||||
tasks,
|
||||
newDates,
|
||||
columnWidth,
|
||||
rowHeight,
|
||||
taskHeight,
|
||||
barCornerRadius,
|
||||
handleWidth,
|
||||
barProgressColor,
|
||||
barProgressSelectedColor,
|
||||
barBackgroundColor,
|
||||
barBackgroundSelectedColor,
|
||||
milestoneBackgroundColor,
|
||||
milestoneBackgroundSelectedColor
|
||||
)
|
||||
);
|
||||
}, [
|
||||
tasks,
|
||||
viewMode,
|
||||
rowHeight,
|
||||
barCornerRadius,
|
||||
columnWidth,
|
||||
taskHeight,
|
||||
handleWidth,
|
||||
barProgressColor,
|
||||
barProgressSelectedColor,
|
||||
barBackgroundColor,
|
||||
barBackgroundSelectedColor,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
setGanttTasks(tasks);
|
||||
}, [tasks]);
|
||||
const { changedTask, action } = ganttEvent;
|
||||
if (changedTask) {
|
||||
if (action === "delete") {
|
||||
setGanttEvent({ action: "" });
|
||||
setBarTasks(barTasks.filter(t => t.id !== changedTask.id));
|
||||
} else if (
|
||||
action === "move" ||
|
||||
action === "end" ||
|
||||
action === "start" ||
|
||||
action === "progress"
|
||||
) {
|
||||
const prevStateTask = barTasks.find(t => t.id === changedTask.id);
|
||||
if (
|
||||
prevStateTask &&
|
||||
(prevStateTask.start.getTime() !== changedTask.start.getTime() ||
|
||||
prevStateTask.end.getTime() !== changedTask.end.getTime() ||
|
||||
prevStateTask.progress !== changedTask.progress)
|
||||
) {
|
||||
// actions for change
|
||||
const newTaskList = barTasks.map(t =>
|
||||
t.id === changedTask.id ? changedTask : t
|
||||
);
|
||||
setBarTasks(newTaskList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [ganttEvent, barTasks]);
|
||||
|
||||
useEffect(() => {
|
||||
if (failedTask) {
|
||||
setBarTasks(barTasks.map(t => (t.id !== failedTask.id ? t : failedTask)));
|
||||
setFailedTask(null);
|
||||
}
|
||||
}, [failedTask, barTasks]);
|
||||
|
||||
useEffect(() => {
|
||||
const newTaskHeight = (rowHeight * barFill) / 100;
|
||||
if (newTaskHeight !== taskHeight) {
|
||||
setTaskHeight(newTaskHeight);
|
||||
}
|
||||
}, [rowHeight, barFill, taskHeight]);
|
||||
|
||||
// scroll events
|
||||
useEffect(() => {
|
||||
@ -79,7 +169,7 @@ export const Gantt: React.SFC<GanttProps> = ({
|
||||
if (
|
||||
wrapperRef.current &&
|
||||
ganttHeight &&
|
||||
ganttHeight < ganttTasks.length * rowHeight
|
||||
ganttHeight < barTasks.length * rowHeight
|
||||
) {
|
||||
wrapperRef.current.addEventListener("wheel", handleWheel, {
|
||||
passive: false,
|
||||
@ -90,7 +180,7 @@ export const Gantt: React.SFC<GanttProps> = ({
|
||||
wrapperRef.current.removeEventListener("wheel", handleWheel);
|
||||
}
|
||||
};
|
||||
}, [wrapperRef.current, scrollY, ganttHeight, ganttTasks, rowHeight]);
|
||||
}, [wrapperRef.current, scrollY, ganttHeight, barTasks, rowHeight]);
|
||||
|
||||
const handleScrollY = (event: SyntheticEvent<HTMLDivElement>) => {
|
||||
if (scrollY !== event.currentTarget.scrollTop && !ignoreScrollEvent) {
|
||||
@ -154,40 +244,29 @@ export const Gantt: React.SFC<GanttProps> = ({
|
||||
setIgnoreScrollEvent(true);
|
||||
};
|
||||
|
||||
// task change event
|
||||
const handleTasksChange = (tasks: Task[]) => {
|
||||
setGanttTasks(tasks);
|
||||
};
|
||||
|
||||
/**
|
||||
* Task select event
|
||||
*/
|
||||
const handleSelectedTask = (taskId: string) => {
|
||||
const newSelectedTask = ganttTasks.find(t => t.id === taskId);
|
||||
if (newSelectedTask) {
|
||||
if (onSelect) {
|
||||
const oldSelectedTask = ganttTasks.find(t => t.id === selectedTask);
|
||||
if (oldSelectedTask) {
|
||||
onSelect(oldSelectedTask, false);
|
||||
}
|
||||
const newSelectedTask = barTasks.find(t => t.id === taskId);
|
||||
const oldSelectedTask = barTasks.find(
|
||||
t => !!selectedTask && t.id === selectedTask.id
|
||||
);
|
||||
if (onSelect) {
|
||||
if (oldSelectedTask) {
|
||||
onSelect(oldSelectedTask, false);
|
||||
}
|
||||
if (newSelectedTask) {
|
||||
onSelect(newSelectedTask, true);
|
||||
}
|
||||
setSelectedTask(newSelectedTask.id);
|
||||
} else {
|
||||
if (onSelect) {
|
||||
const oldSelectedTask = ganttTasks.find(t => t.id === selectedTask);
|
||||
if (oldSelectedTask) {
|
||||
onSelect(oldSelectedTask, false);
|
||||
}
|
||||
}
|
||||
setSelectedTask("");
|
||||
}
|
||||
setSelectedTask(newSelectedTask);
|
||||
};
|
||||
|
||||
const gridProps: GridProps = {
|
||||
columnWidth,
|
||||
gridWidth,
|
||||
tasks: ganttTasks,
|
||||
tasks: tasks,
|
||||
rowHeight,
|
||||
dates,
|
||||
todayColor,
|
||||
@ -202,26 +281,22 @@ export const Gantt: React.SFC<GanttProps> = ({
|
||||
fontSize,
|
||||
};
|
||||
const barProps: TaskGanttContentProps = {
|
||||
tasks: ganttTasks,
|
||||
selectedTask,
|
||||
setSelectedTask: handleSelectedTask,
|
||||
rowHeight,
|
||||
barCornerRadius,
|
||||
columnWidth,
|
||||
tasks: barTasks,
|
||||
dates,
|
||||
barFill,
|
||||
barProgressColor,
|
||||
barProgressSelectedColor,
|
||||
barBackgroundColor,
|
||||
barBackgroundSelectedColor,
|
||||
handleWidth,
|
||||
ganttEvent,
|
||||
selectedTask,
|
||||
rowHeight,
|
||||
taskHeight,
|
||||
columnWidth,
|
||||
arrowColor,
|
||||
timeStep,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
arrowIndent,
|
||||
svgHeight,
|
||||
onTasksChange: handleTasksChange,
|
||||
setGanttEvent,
|
||||
setFailedTask,
|
||||
setSelectedTask: handleSelectedTask,
|
||||
onDateChange,
|
||||
onProgressChange,
|
||||
onDoubleClick,
|
||||
@ -234,13 +309,13 @@ export const Gantt: React.SFC<GanttProps> = ({
|
||||
rowWidth: listCellWidth,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
tasks: ganttTasks,
|
||||
tasks: barTasks,
|
||||
locale,
|
||||
headerHeight,
|
||||
scrollY,
|
||||
ganttHeight,
|
||||
horizontalContainerClass: styles.horizontalContainer,
|
||||
selectedTaskId: selectedTask,
|
||||
selectedTask,
|
||||
setSelectedTask: handleSelectedTask,
|
||||
TaskListHeader,
|
||||
TaskListTable,
|
||||
|
||||
@ -1,79 +1,60 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Task, EventOption } from "../../types/public-types";
|
||||
import { Bar } from "../bar/bar";
|
||||
import { BarTask } from "../../types/bar-task";
|
||||
import { Arrow } from "../other/arrow";
|
||||
import {
|
||||
convertToBarTasks,
|
||||
handleTaskBySVGMouseEvent,
|
||||
BarMoveAction,
|
||||
} from "../../helpers/bar-helper";
|
||||
import { handleTaskBySVGMouseEvent } from "../../helpers/bar-helper";
|
||||
import { Tooltip } from "../other/tooltip";
|
||||
import { isKeyboardEvent } from "../../helpers/other-helper";
|
||||
import { TaskItem } from "../task-item/task-item";
|
||||
import {
|
||||
BarMoveAction,
|
||||
GanttContentMoveAction,
|
||||
GanttEvent,
|
||||
} from "../../types/gantt-task-actions";
|
||||
|
||||
export type GanttContentMoveAction =
|
||||
| "mouseenter"
|
||||
| "mouseleave"
|
||||
| "delete"
|
||||
| "dblclick"
|
||||
| "select"
|
||||
| BarMoveAction;
|
||||
export type BarEvent = {
|
||||
changedTask?: BarTask;
|
||||
originalTask?: BarTask;
|
||||
action: GanttContentMoveAction;
|
||||
};
|
||||
export type TaskGanttContentProps = {
|
||||
tasks: Task[];
|
||||
tasks: BarTask[];
|
||||
dates: Date[];
|
||||
selectedTask: string;
|
||||
ganttEvent: GanttEvent;
|
||||
selectedTask: BarTask | undefined;
|
||||
rowHeight: number;
|
||||
barCornerRadius: number;
|
||||
columnWidth: number;
|
||||
barFill: number;
|
||||
barProgressColor: string;
|
||||
barProgressSelectedColor: string;
|
||||
barBackgroundColor: string;
|
||||
barBackgroundSelectedColor: string;
|
||||
handleWidth: number;
|
||||
timeStep: number;
|
||||
svg?: React.RefObject<SVGSVGElement>;
|
||||
svgHeight: number;
|
||||
taskHeight: number;
|
||||
arrowColor: string;
|
||||
arrowIndent: number;
|
||||
fontSize: string;
|
||||
fontFamily: string;
|
||||
setGanttEvent: (value: GanttEvent) => void;
|
||||
setFailedTask: (value: BarTask | null) => void;
|
||||
setSelectedTask: (taskId: string) => void;
|
||||
TooltipContent: React.FC<{
|
||||
task: Task;
|
||||
fontSize: string;
|
||||
fontFamily: string;
|
||||
}>;
|
||||
onTasksChange: (tasks: Task[]) => void;
|
||||
} & EventOption;
|
||||
|
||||
export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
tasks,
|
||||
dates,
|
||||
ganttEvent,
|
||||
selectedTask,
|
||||
rowHeight,
|
||||
barCornerRadius,
|
||||
columnWidth,
|
||||
barFill,
|
||||
barProgressColor,
|
||||
barProgressSelectedColor,
|
||||
barBackgroundColor,
|
||||
barBackgroundSelectedColor,
|
||||
handleWidth,
|
||||
timeStep,
|
||||
svg,
|
||||
svgHeight,
|
||||
taskHeight,
|
||||
arrowColor,
|
||||
arrowIndent,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
setGanttEvent,
|
||||
setFailedTask,
|
||||
setSelectedTask,
|
||||
onTasksChange,
|
||||
onDateChange,
|
||||
onProgressChange,
|
||||
onDoubleClick,
|
||||
@ -81,11 +62,6 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
TooltipContent,
|
||||
}) => {
|
||||
const point = svg?.current?.createSVGPoint();
|
||||
const [barEvent, setBarEvent] = useState<BarEvent>({
|
||||
action: "",
|
||||
});
|
||||
const [barTasks, setBarTasks] = useState<BarTask[]>([]);
|
||||
const [failedTask, setFailedTask] = useState<BarTask | null>(null);
|
||||
const [xStep, setXStep] = useState(0);
|
||||
const [initEventX1Delta, setInitEventX1Delta] = useState(0);
|
||||
const [isMoving, setIsMoving] = useState(false);
|
||||
@ -101,51 +77,9 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
setXStep(newXStep);
|
||||
}, [columnWidth, dates, timeStep]);
|
||||
|
||||
// generate tasks
|
||||
useEffect(() => {
|
||||
setBarTasks(
|
||||
convertToBarTasks(
|
||||
tasks,
|
||||
dates,
|
||||
columnWidth,
|
||||
rowHeight,
|
||||
barFill,
|
||||
barCornerRadius,
|
||||
handleWidth,
|
||||
barProgressColor,
|
||||
barProgressSelectedColor,
|
||||
barBackgroundColor,
|
||||
barBackgroundSelectedColor
|
||||
)
|
||||
);
|
||||
}, [
|
||||
tasks,
|
||||
rowHeight,
|
||||
barCornerRadius,
|
||||
columnWidth,
|
||||
dates,
|
||||
barFill,
|
||||
handleWidth,
|
||||
barProgressColor,
|
||||
barProgressSelectedColor,
|
||||
barBackgroundColor,
|
||||
barBackgroundSelectedColor,
|
||||
]);
|
||||
|
||||
// on failed task update
|
||||
useEffect(() => {
|
||||
if (failedTask) {
|
||||
const newTasks = barTasks.map(t =>
|
||||
t.id === failedTask.id ? failedTask : t
|
||||
);
|
||||
onTasksChange(newTasks);
|
||||
setFailedTask(null);
|
||||
}
|
||||
}, [failedTask, barTasks]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseMove = async (event: MouseEvent) => {
|
||||
if (!barEvent.changedTask || !point || !svg?.current) return;
|
||||
if (!ganttEvent.changedTask || !point || !svg?.current) return;
|
||||
event.preventDefault();
|
||||
|
||||
point.x = event.clientX;
|
||||
@ -155,54 +89,46 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
|
||||
const { isChanged, changedTask } = handleTaskBySVGMouseEvent(
|
||||
cursor.x,
|
||||
barEvent.action as BarMoveAction,
|
||||
barEvent.changedTask,
|
||||
ganttEvent.action as BarMoveAction,
|
||||
ganttEvent.changedTask,
|
||||
xStep,
|
||||
timeStep,
|
||||
initEventX1Delta
|
||||
);
|
||||
if (isChanged) {
|
||||
setBarTasks(
|
||||
barTasks.map(t => (t.id === changedTask.id ? changedTask : t))
|
||||
);
|
||||
setBarEvent({ ...barEvent, changedTask: changedTask });
|
||||
setGanttEvent({ action: ganttEvent.action, changedTask });
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = async (event: MouseEvent) => {
|
||||
const { changedTask: selectedTask, action, originalTask } = barEvent;
|
||||
|
||||
if (!selectedTask || !point || !svg?.current || !originalTask) return;
|
||||
const { action, originalSelectedTask, changedTask } = ganttEvent;
|
||||
if (!changedTask || !point || !svg?.current || !originalSelectedTask)
|
||||
return;
|
||||
event.preventDefault();
|
||||
|
||||
point.x = event.clientX;
|
||||
const cursor = point.matrixTransform(
|
||||
svg?.current.getScreenCTM()?.inverse()
|
||||
);
|
||||
|
||||
const { changedTask } = handleTaskBySVGMouseEvent(
|
||||
const { changedTask: newChangedTask } = handleTaskBySVGMouseEvent(
|
||||
cursor.x,
|
||||
action as BarMoveAction,
|
||||
selectedTask,
|
||||
changedTask,
|
||||
xStep,
|
||||
timeStep,
|
||||
initEventX1Delta
|
||||
);
|
||||
|
||||
const isNotLikeOriginal =
|
||||
originalTask.start !== changedTask.start ||
|
||||
originalTask.end !== changedTask.end ||
|
||||
originalTask.progress !== changedTask.progress;
|
||||
originalSelectedTask.start !== newChangedTask.start ||
|
||||
originalSelectedTask.end !== newChangedTask.end ||
|
||||
originalSelectedTask.progress !== newChangedTask.progress;
|
||||
|
||||
// remove listeners
|
||||
svg.current.removeEventListener("mousemove", handleMouseMove);
|
||||
svg.current.removeEventListener("mouseup", handleMouseUp);
|
||||
setBarEvent({ action: "" });
|
||||
setGanttEvent({ action: "" });
|
||||
setIsMoving(false);
|
||||
const newTasks = barTasks.map(t =>
|
||||
t.id === changedTask.id ? changedTask : t
|
||||
);
|
||||
onTasksChange(newTasks);
|
||||
|
||||
// custom operation start
|
||||
let operationSuccess = true;
|
||||
@ -212,7 +138,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
isNotLikeOriginal
|
||||
) {
|
||||
try {
|
||||
const result = await onDateChange(changedTask);
|
||||
const result = await onDateChange(newChangedTask);
|
||||
if (result !== undefined) {
|
||||
operationSuccess = result;
|
||||
}
|
||||
@ -221,7 +147,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
}
|
||||
} else if (onProgressChange && isNotLikeOriginal) {
|
||||
try {
|
||||
const result = await onProgressChange(changedTask);
|
||||
const result = await onProgressChange(newChangedTask);
|
||||
if (result !== undefined) {
|
||||
operationSuccess = result;
|
||||
}
|
||||
@ -232,16 +158,16 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
|
||||
// If operation is failed - return old state
|
||||
if (!operationSuccess) {
|
||||
setFailedTask(originalTask);
|
||||
setFailedTask(originalSelectedTask);
|
||||
}
|
||||
};
|
||||
|
||||
if (
|
||||
!isMoving &&
|
||||
(barEvent.action === "move" ||
|
||||
barEvent.action === "end" ||
|
||||
barEvent.action === "start" ||
|
||||
barEvent.action === "progress") &&
|
||||
(ganttEvent.action === "move" ||
|
||||
ganttEvent.action === "end" ||
|
||||
ganttEvent.action === "start" ||
|
||||
ganttEvent.action === "progress") &&
|
||||
svg?.current
|
||||
) {
|
||||
svg.current.addEventListener("mousemove", handleMouseMove);
|
||||
@ -249,8 +175,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
setIsMoving(true);
|
||||
}
|
||||
}, [
|
||||
barTasks,
|
||||
barEvent,
|
||||
ganttEvent,
|
||||
xStep,
|
||||
initEventX1Delta,
|
||||
onProgressChange,
|
||||
@ -280,9 +205,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
try {
|
||||
const result = await onTaskDelete(task);
|
||||
if (result !== undefined && result) {
|
||||
const newTasks = barTasks.filter(t => t.id !== task.id);
|
||||
onTasksChange(newTasks);
|
||||
setSelectedTask("");
|
||||
setGanttEvent({ action, changedTask: task });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error on Delete. " + error);
|
||||
@ -292,16 +215,16 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
}
|
||||
// Mouse Events
|
||||
else if (action === "mouseenter") {
|
||||
if (!barEvent.action) {
|
||||
setBarEvent({
|
||||
if (!ganttEvent.action) {
|
||||
setGanttEvent({
|
||||
action,
|
||||
changedTask: task,
|
||||
originalTask: task,
|
||||
originalSelectedTask: task,
|
||||
});
|
||||
}
|
||||
} else if (action === "mouseleave") {
|
||||
if (barEvent.action === "mouseenter") {
|
||||
setBarEvent({ action: "" });
|
||||
if (ganttEvent.action === "mouseenter") {
|
||||
setGanttEvent({ action: "" });
|
||||
}
|
||||
} else if (action === "dblclick") {
|
||||
!!onDoubleClick && onDoubleClick(task);
|
||||
@ -314,16 +237,16 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
svg.current.getScreenCTM()?.inverse()
|
||||
);
|
||||
setInitEventX1Delta(cursor.x - task.x1);
|
||||
setBarEvent({
|
||||
setGanttEvent({
|
||||
action,
|
||||
changedTask: task,
|
||||
originalTask: task,
|
||||
originalSelectedTask: task,
|
||||
});
|
||||
} else {
|
||||
setBarEvent({
|
||||
setGanttEvent({
|
||||
action,
|
||||
changedTask: task,
|
||||
originalTask: task,
|
||||
originalSelectedTask: task,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -331,14 +254,15 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
return (
|
||||
<g className="content">
|
||||
<g className="arrows" fill={arrowColor} stroke={arrowColor}>
|
||||
{barTasks.map(task => {
|
||||
{tasks.map(task => {
|
||||
return task.barChildren.map(child => {
|
||||
return (
|
||||
<Arrow
|
||||
key={`Arrow from ${task.id} to ${barTasks[child].id}`}
|
||||
key={`Arrow from ${task.id} to ${tasks[child].id}`}
|
||||
taskFrom={task}
|
||||
taskTo={barTasks[child]}
|
||||
taskTo={tasks[child]}
|
||||
rowHeight={rowHeight}
|
||||
taskHeight={taskHeight}
|
||||
arrowIndent={arrowIndent}
|
||||
/>
|
||||
);
|
||||
@ -346,28 +270,29 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
})}
|
||||
</g>
|
||||
<g className="bar" fontFamily={fontFamily} fontSize={fontSize}>
|
||||
{barTasks.map(task => {
|
||||
{tasks.map(task => {
|
||||
return (
|
||||
<Bar
|
||||
<TaskItem
|
||||
task={task}
|
||||
arrowIndent={arrowIndent}
|
||||
taskHeight={taskHeight}
|
||||
isProgressChangeable={!!onProgressChange && !task.isDisabled}
|
||||
isDateChangeable={!!onDateChange && !task.isDisabled}
|
||||
isDelete={!task.isDisabled}
|
||||
onEventStart={handleBarEventStart}
|
||||
key={task.id}
|
||||
isSelected={task.id === selectedTask}
|
||||
isSelected={!!selectedTask && task.id === selectedTask.id}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</g>
|
||||
<g className="toolTip">
|
||||
{barEvent.changedTask && (
|
||||
{ganttEvent.changedTask && (
|
||||
<Tooltip
|
||||
x={barEvent.changedTask.x2 + arrowIndent + arrowIndent * 0.5}
|
||||
x={ganttEvent.changedTask.x2 + arrowIndent + arrowIndent * 0.5}
|
||||
rowHeight={rowHeight}
|
||||
svgHeight={svgHeight}
|
||||
task={barEvent.changedTask}
|
||||
task={ganttEvent.changedTask}
|
||||
fontFamily={fontFamily}
|
||||
fontSize={fontSize}
|
||||
TooltipContent={TooltipContent}
|
||||
|
||||
@ -5,18 +5,20 @@ type ArrowProps = {
|
||||
taskFrom: BarTask;
|
||||
taskTo: BarTask;
|
||||
rowHeight: number;
|
||||
taskHeight: number;
|
||||
arrowIndent: number;
|
||||
};
|
||||
export const Arrow: React.FC<ArrowProps> = ({
|
||||
taskFrom,
|
||||
taskTo,
|
||||
rowHeight,
|
||||
taskHeight,
|
||||
arrowIndent,
|
||||
}) => {
|
||||
const indexCompare = taskFrom.index > taskTo.index ? -1 : 1;
|
||||
const taskToEndPosition = taskTo.y + taskTo.height / 2;
|
||||
const taskToEndPosition = taskTo.y + taskHeight / 2;
|
||||
|
||||
const path = `M ${taskFrom.x2} ${taskFrom.y + taskFrom.height / 2}
|
||||
const path = `M ${taskFrom.x2} ${taskFrom.y + taskHeight / 2}
|
||||
h ${arrowIndent}
|
||||
v ${(indexCompare * rowHeight) / 2}
|
||||
H ${taskTo.x1 - arrowIndent}
|
||||
|
||||
@ -71,10 +71,13 @@ export const StandardTooltipContent: React.FC<{
|
||||
}-${task.start.getFullYear()} - ${task.end.getDate()}-${
|
||||
task.end.getMonth() + 1
|
||||
}-${task.end.getFullYear()}`}</b>
|
||||
<p className={styles.tooltipDefaultContainerParagraph}>{`Duration: ${~~(
|
||||
(task.end.getTime() - task.start.getTime()) /
|
||||
(1000 * 60 * 60 * 24)
|
||||
)} day(s)`}</p>
|
||||
{task.end.getTime() - task.start.getTime() !== 0 && (
|
||||
<p className={styles.tooltipDefaultContainerParagraph}>{`Duration: ${~~(
|
||||
(task.end.getTime() - task.start.getTime()) /
|
||||
(1000 * 60 * 60 * 24)
|
||||
)} day(s)`}</p>
|
||||
)}
|
||||
|
||||
<p className={styles.tooltipDefaultContainerParagraph}>
|
||||
{!!task.progress && `Progress: ${task.progress} %`}
|
||||
</p>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import React from "react";
|
||||
import style from "./bar.module.css";
|
||||
|
||||
type BarDisplayProps = {
|
||||
@ -9,9 +9,6 @@ type BarDisplayProps = {
|
||||
isSelected: boolean;
|
||||
progressWidth: number;
|
||||
barCornerRadius: number;
|
||||
text: string;
|
||||
hasChild: boolean;
|
||||
arrowIndent: number;
|
||||
styles: {
|
||||
backgroundColor: string;
|
||||
backgroundSelectedColor: string;
|
||||
@ -28,20 +25,9 @@ export const BarDisplay: React.FC<BarDisplayProps> = ({
|
||||
isSelected,
|
||||
progressWidth,
|
||||
barCornerRadius,
|
||||
text,
|
||||
hasChild,
|
||||
arrowIndent,
|
||||
styles,
|
||||
onMouseDown,
|
||||
}) => {
|
||||
const textRef = useRef<SVGTextElement>(null);
|
||||
const [isTextInside, setIsTextInside] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (textRef.current)
|
||||
setIsTextInside(textRef.current.getBBox().width < width);
|
||||
}, [textRef, width]);
|
||||
|
||||
const getProcessColor = () => {
|
||||
return isSelected ? styles.progressSelectedColor : styles.progressColor;
|
||||
};
|
||||
@ -50,12 +36,6 @@ export const BarDisplay: React.FC<BarDisplayProps> = ({
|
||||
return isSelected ? styles.backgroundSelectedColor : styles.backgroundColor;
|
||||
};
|
||||
|
||||
const getX = () => {
|
||||
return isTextInside
|
||||
? x + width * 0.5
|
||||
: x + width + arrowIndent * +hasChild + arrowIndent * 0.2;
|
||||
};
|
||||
|
||||
return (
|
||||
<g onMouseDown={onMouseDown}>
|
||||
<rect
|
||||
@ -77,18 +57,6 @@ export const BarDisplay: React.FC<BarDisplayProps> = ({
|
||||
rx={barCornerRadius}
|
||||
fill={getProcessColor()}
|
||||
/>
|
||||
<text
|
||||
x={getX()}
|
||||
y={y + height * 0.5}
|
||||
className={
|
||||
isTextInside
|
||||
? style.barLabel
|
||||
: style.barLabel && style.barLabelOutside
|
||||
}
|
||||
ref={textRef}
|
||||
>
|
||||
{text}
|
||||
</text>
|
||||
</g>
|
||||
);
|
||||
};
|
||||
22
src/components/task-item/bar/bar.module.css
Normal file
22
src/components/task-item/bar/bar.module.css
Normal file
@ -0,0 +1,22 @@
|
||||
.barWrapper {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.barWrapper:hover .barHandle {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.barHandle {
|
||||
fill: #ddd;
|
||||
cursor: ew-resize;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.barBackground {
|
||||
user-select: none;
|
||||
stroke-width: 0;
|
||||
}
|
||||
@ -1,36 +1,19 @@
|
||||
import React from "react";
|
||||
import { BarTask } from "../../types/bar-task";
|
||||
import {
|
||||
progressWithByParams,
|
||||
getProgressPoint,
|
||||
} from "../../helpers/bar-helper";
|
||||
import styles from "./bar.module.css";
|
||||
import { GanttContentMoveAction } from "../gantt/task-gantt-content";
|
||||
} from "../../../helpers/bar-helper";
|
||||
import { BarDisplay } from "./bar-display";
|
||||
import { BarDateHandle } from "./bar-date-handle";
|
||||
import { BarProgressHandle } from "./bar-progress-handle";
|
||||
import { TaskItemProps } from "../task-item";
|
||||
import styles from "./bar.module.css";
|
||||
|
||||
export type BarProps = {
|
||||
task: BarTask;
|
||||
arrowIndent: number;
|
||||
isProgressChangeable: boolean;
|
||||
isDateChangeable: boolean;
|
||||
isDelete: boolean;
|
||||
isSelected: boolean;
|
||||
onEventStart: (
|
||||
action: GanttContentMoveAction,
|
||||
selectedTask: BarTask,
|
||||
event?: React.MouseEvent | React.KeyboardEvent
|
||||
) => any;
|
||||
};
|
||||
|
||||
export const Bar: React.FC<BarProps> = ({
|
||||
export const Bar: React.FC<TaskItemProps> = ({
|
||||
task,
|
||||
arrowIndent,
|
||||
isProgressChangeable,
|
||||
isDateChangeable,
|
||||
onEventStart,
|
||||
isDelete,
|
||||
isSelected,
|
||||
}) => {
|
||||
const progressWidth = progressWithByParams(task.x1, task.x2, task.progress);
|
||||
@ -39,33 +22,9 @@ export const Bar: React.FC<BarProps> = ({
|
||||
task.y,
|
||||
task.height
|
||||
);
|
||||
|
||||
const handleHeight = task.height - 2;
|
||||
return (
|
||||
<g
|
||||
className={styles.barWrapper}
|
||||
tabIndex={0}
|
||||
onKeyDown={e => {
|
||||
switch (e.key) {
|
||||
case "Delete": {
|
||||
if (isDelete) onEventStart("delete", task, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onMouseEnter={e => {
|
||||
onEventStart("mouseenter", task, e);
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
onEventStart("mouseleave", task, e);
|
||||
}}
|
||||
onDoubleClick={e => {
|
||||
onEventStart("dblclick", task, e);
|
||||
}}
|
||||
onFocus={() => {
|
||||
onEventStart("select", task);
|
||||
}}
|
||||
>
|
||||
<g className={styles.barWrapper} tabIndex={0}>
|
||||
<BarDisplay
|
||||
x={task.x1}
|
||||
y={task.y}
|
||||
@ -73,9 +32,6 @@ export const Bar: React.FC<BarProps> = ({
|
||||
height={task.height}
|
||||
progressWidth={progressWidth}
|
||||
barCornerRadius={task.barCornerRadius}
|
||||
text={task.name}
|
||||
hasChild={task.barChildren.length > 0}
|
||||
arrowIndent={arrowIndent}
|
||||
styles={task.styles}
|
||||
isSelected={isSelected}
|
||||
onMouseDown={e => {
|
||||
@ -90,7 +46,7 @@ export const Bar: React.FC<BarProps> = ({
|
||||
x={task.x1 + 1}
|
||||
y={task.y + 1}
|
||||
width={task.handleWidth}
|
||||
height={task.height - 2}
|
||||
height={handleHeight}
|
||||
barCornerRadius={task.barCornerRadius}
|
||||
onMouseDown={e => {
|
||||
onEventStart("start", task, e);
|
||||
@ -101,7 +57,7 @@ export const Bar: React.FC<BarProps> = ({
|
||||
x={task.x2 - task.handleWidth - 1}
|
||||
y={task.y + 1}
|
||||
width={task.handleWidth}
|
||||
height={task.height - 2}
|
||||
height={handleHeight}
|
||||
barCornerRadius={task.barCornerRadius}
|
||||
onMouseDown={e => {
|
||||
onEventStart("end", task, e);
|
||||
8
src/components/task-item/milestone/milestone.module.css
Normal file
8
src/components/task-item/milestone/milestone.module.css
Normal file
@ -0,0 +1,8 @@
|
||||
.milestoneWrapper {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.milestoneBackground {
|
||||
user-select: none;
|
||||
}
|
||||
37
src/components/task-item/milestone/milestone.tsx
Normal file
37
src/components/task-item/milestone/milestone.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { TaskItemProps } from "../task-item";
|
||||
import styles from "./milestone.module.css";
|
||||
|
||||
export const Milestone: React.FC<TaskItemProps> = ({
|
||||
task,
|
||||
isDateChangeable,
|
||||
onEventStart,
|
||||
isSelected,
|
||||
}) => {
|
||||
const transform = `rotate(45 ${task.x1 + task.height * 0.356}
|
||||
${task.y + task.height * 0.85})`;
|
||||
const getBarColor = () => {
|
||||
return isSelected
|
||||
? task.styles.backgroundSelectedColor
|
||||
: task.styles.backgroundColor;
|
||||
};
|
||||
|
||||
return (
|
||||
<g tabIndex={0} className={styles.milestoneWrapper}>
|
||||
<rect
|
||||
fill={getBarColor()}
|
||||
x={task.x1}
|
||||
width={task.height}
|
||||
y={task.y}
|
||||
height={task.height}
|
||||
rx={task.barCornerRadius}
|
||||
ry={task.barCornerRadius}
|
||||
transform={transform}
|
||||
className={styles.milestoneBackground}
|
||||
onMouseDown={e => {
|
||||
isDateChangeable && onEventStart("move", task, e);
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
);
|
||||
};
|
||||
102
src/components/task-item/task-item.tsx
Normal file
102
src/components/task-item/task-item.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { BarTask } from "../../types/bar-task";
|
||||
import { GanttContentMoveAction } from "../../types/gantt-task-actions";
|
||||
import { Bar } from "./bar/bar";
|
||||
import { Milestone } from "./milestone/milestone";
|
||||
import style from "./task-list.module.css";
|
||||
|
||||
export type TaskItemProps = {
|
||||
task: BarTask;
|
||||
arrowIndent: number;
|
||||
taskHeight: number;
|
||||
isProgressChangeable: boolean;
|
||||
isDateChangeable: boolean;
|
||||
isDelete: boolean;
|
||||
isSelected: boolean;
|
||||
onEventStart: (
|
||||
action: GanttContentMoveAction,
|
||||
selectedTask: BarTask,
|
||||
event?: React.MouseEvent | React.KeyboardEvent
|
||||
) => any;
|
||||
};
|
||||
|
||||
export const TaskItem: React.FC<TaskItemProps> = props => {
|
||||
const {
|
||||
task,
|
||||
arrowIndent,
|
||||
isDelete,
|
||||
taskHeight,
|
||||
isSelected,
|
||||
onEventStart,
|
||||
} = {
|
||||
...props,
|
||||
};
|
||||
const textRef = useRef<SVGTextElement>(null);
|
||||
const [taskItem, setTaskItem] = useState<JSX.Element>(<div />);
|
||||
const [isTextInside, setIsTextInside] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
switch (task.type) {
|
||||
case "milestone":
|
||||
setTaskItem(<Milestone {...props} />);
|
||||
break;
|
||||
default:
|
||||
setTaskItem(<Bar {...props} />);
|
||||
break;
|
||||
}
|
||||
}, [task, isSelected]);
|
||||
|
||||
useEffect(() => {
|
||||
if (textRef.current) {
|
||||
setIsTextInside(textRef.current.getBBox().width < task.x2 - task.x1);
|
||||
}
|
||||
}, [textRef, task]);
|
||||
|
||||
const getX = () => {
|
||||
const width = task.x2 - task.x1;
|
||||
const hasChild = task.barChildren.length > 0;
|
||||
return isTextInside
|
||||
? task.x1 + width * 0.5
|
||||
: task.x1 + width + arrowIndent * +hasChild + arrowIndent * 0.2;
|
||||
};
|
||||
|
||||
return (
|
||||
<g
|
||||
onKeyDown={e => {
|
||||
switch (e.key) {
|
||||
case "Delete": {
|
||||
if (isDelete) onEventStart("delete", task, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onMouseEnter={e => {
|
||||
onEventStart("mouseenter", task, e);
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
onEventStart("mouseleave", task, e);
|
||||
}}
|
||||
onDoubleClick={e => {
|
||||
onEventStart("dblclick", task, e);
|
||||
}}
|
||||
onFocus={() => {
|
||||
onEventStart("select", task);
|
||||
}}
|
||||
>
|
||||
{taskItem}
|
||||
<text
|
||||
x={getX()}
|
||||
y={task.y + taskHeight * 0.5}
|
||||
className={
|
||||
isTextInside
|
||||
? style.barLabel
|
||||
: style.barLabel && style.barLabelOutside
|
||||
}
|
||||
ref={textRef}
|
||||
>
|
||||
{task.name}
|
||||
</text>
|
||||
</g>
|
||||
);
|
||||
};
|
||||
@ -1,21 +1,3 @@
|
||||
.barWrapper {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.barWrapper:hover .barHandle {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.barHandle {
|
||||
fill: #ddd;
|
||||
cursor: ew-resize;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.barLabel {
|
||||
fill: #fff;
|
||||
text-anchor: middle;
|
||||
@ -39,8 +21,3 @@
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.barBackground {
|
||||
user-select: none;
|
||||
stroke-width: 0;
|
||||
}
|
||||
@ -59,7 +59,8 @@ export const TaskListTableDefault: React.FC<{
|
||||
maxWidth: rowWidth,
|
||||
}}
|
||||
>
|
||||
{t.end.toLocaleDateString(locale, dateTimeOptions)}
|
||||
|
||||
{t.end.toLocaleDateString(locale, dateTimeOptions)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { BarTask } from "../../types/bar-task";
|
||||
import { Task } from "../../types/public-types";
|
||||
|
||||
export type TaskListProps = {
|
||||
@ -12,8 +13,8 @@ export type TaskListProps = {
|
||||
locale: string;
|
||||
tasks: Task[];
|
||||
horizontalContainerClass?: string;
|
||||
selectedTaskId: string;
|
||||
setSelectedTask: (taskId: string) => void;
|
||||
selectedTask: BarTask | undefined;
|
||||
setSelectedTask: (task: string) => void;
|
||||
TaskListHeader: React.FC<{
|
||||
headerHeight: number;
|
||||
rowWidth: string;
|
||||
@ -40,7 +41,7 @@ export const TaskList: React.FC<TaskListProps> = ({
|
||||
rowHeight,
|
||||
scrollY,
|
||||
tasks,
|
||||
selectedTaskId,
|
||||
selectedTask,
|
||||
setSelectedTask,
|
||||
locale,
|
||||
ganttHeight,
|
||||
@ -61,6 +62,7 @@ export const TaskList: React.FC<TaskListProps> = ({
|
||||
fontSize,
|
||||
rowWidth,
|
||||
};
|
||||
const selectedTaskId = selectedTask ? selectedTask.id : "";
|
||||
const tableProps = {
|
||||
rowHeight,
|
||||
rowWidth,
|
||||
@ -68,7 +70,7 @@ export const TaskList: React.FC<TaskListProps> = ({
|
||||
fontSize,
|
||||
tasks,
|
||||
locale,
|
||||
selectedTaskId,
|
||||
selectedTaskId: selectedTaskId,
|
||||
setSelectedTask,
|
||||
};
|
||||
|
||||
|
||||
@ -1,26 +1,27 @@
|
||||
import { Task } from "../types/public-types";
|
||||
import { BarTask } from "../types/bar-task";
|
||||
import { BarMoveAction } from "../types/gantt-task-actions";
|
||||
|
||||
export const convertToBarTasks = (
|
||||
tasks: Task[],
|
||||
dates: Date[],
|
||||
columnWidth: number,
|
||||
rowHeight: number,
|
||||
barFill: number,
|
||||
taskHeight: number,
|
||||
barCornerRadius: number,
|
||||
handleWidth: number,
|
||||
barProgressColor: string,
|
||||
barProgressSelectedColor: string,
|
||||
barBackgroundColor: string,
|
||||
barBackgroundSelectedColor: string
|
||||
barBackgroundSelectedColor: string,
|
||||
milestoneBackgroundColor: string,
|
||||
milestoneBackgroundSelectedColor: string
|
||||
) => {
|
||||
const dateDelta =
|
||||
dates[1].getTime() -
|
||||
dates[0].getTime() -
|
||||
dates[1].getTimezoneOffset() * 60 * 1000 +
|
||||
dates[0].getTimezoneOffset() * 60 * 1000;
|
||||
const taskHeight = (rowHeight * barFill) / 100;
|
||||
|
||||
let barTasks = tasks.map((t, i) => {
|
||||
return convertToBarTask(
|
||||
t,
|
||||
@ -35,7 +36,9 @@ export const convertToBarTasks = (
|
||||
barProgressColor,
|
||||
barProgressSelectedColor,
|
||||
barBackgroundColor,
|
||||
barBackgroundSelectedColor
|
||||
barBackgroundSelectedColor,
|
||||
milestoneBackgroundColor,
|
||||
milestoneBackgroundSelectedColor
|
||||
);
|
||||
});
|
||||
|
||||
@ -54,7 +57,62 @@ export const convertToBarTasks = (
|
||||
return barTasks;
|
||||
};
|
||||
|
||||
export const convertToBarTask = (
|
||||
const convertToBarTask = (
|
||||
task: Task,
|
||||
index: number,
|
||||
dates: Date[],
|
||||
dateDelta: number,
|
||||
columnWidth: number,
|
||||
rowHeight: number,
|
||||
taskHeight: number,
|
||||
barCornerRadius: number,
|
||||
handleWidth: number,
|
||||
barProgressColor: string,
|
||||
barProgressSelectedColor: string,
|
||||
barBackgroundColor: string,
|
||||
barBackgroundSelectedColor: string,
|
||||
milestoneBackgroundColor: string,
|
||||
milestoneBackgroundSelectedColor: string
|
||||
): BarTask => {
|
||||
let barTask: BarTask;
|
||||
switch (task.type) {
|
||||
case "milestone":
|
||||
barTask = convertToMilestone(
|
||||
task,
|
||||
index,
|
||||
dates,
|
||||
dateDelta,
|
||||
columnWidth,
|
||||
rowHeight,
|
||||
taskHeight,
|
||||
barCornerRadius,
|
||||
handleWidth,
|
||||
milestoneBackgroundColor,
|
||||
milestoneBackgroundSelectedColor
|
||||
);
|
||||
break;
|
||||
default:
|
||||
barTask = convertToBar(
|
||||
task,
|
||||
index,
|
||||
dates,
|
||||
dateDelta,
|
||||
columnWidth,
|
||||
rowHeight,
|
||||
taskHeight,
|
||||
barCornerRadius,
|
||||
handleWidth,
|
||||
barProgressColor,
|
||||
barProgressSelectedColor,
|
||||
barBackgroundColor,
|
||||
barBackgroundSelectedColor
|
||||
);
|
||||
break;
|
||||
}
|
||||
return barTask;
|
||||
};
|
||||
|
||||
const convertToBar = (
|
||||
task: Task,
|
||||
index: number,
|
||||
dates: Date[],
|
||||
@ -94,7 +152,50 @@ export const convertToBarTask = (
|
||||
};
|
||||
};
|
||||
|
||||
export const taskXCoordinate = (
|
||||
const convertToMilestone = (
|
||||
task: Task,
|
||||
index: number,
|
||||
dates: Date[],
|
||||
dateDelta: number,
|
||||
columnWidth: number,
|
||||
rowHeight: number,
|
||||
taskHeight: number,
|
||||
barCornerRadius: number,
|
||||
handleWidth: number,
|
||||
milestoneBackgroundColor: string,
|
||||
milestoneBackgroundSelectedColor: string
|
||||
) => {
|
||||
const x = taskXCoordinate(task.start, dates, dateDelta, columnWidth);
|
||||
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,
|
||||
barCornerRadius,
|
||||
handleWidth,
|
||||
progress: 0,
|
||||
height: rotatedHeight,
|
||||
barChildren: [],
|
||||
styles,
|
||||
};
|
||||
};
|
||||
|
||||
const taskXCoordinate = (
|
||||
xDate: Date,
|
||||
dates: Date[],
|
||||
dateDelta: number,
|
||||
@ -119,7 +220,7 @@ export const taskXCoordinate = (
|
||||
return x;
|
||||
};
|
||||
|
||||
export const taskYCoordinate = (
|
||||
const taskYCoordinate = (
|
||||
index: number,
|
||||
rowHeight: number,
|
||||
taskHeight: number
|
||||
@ -149,7 +250,7 @@ export const progressByProgressWidth = (
|
||||
}
|
||||
};
|
||||
|
||||
export const progressByX = (x: number, task: BarTask) => {
|
||||
const progressByX = (x: number, task: BarTask) => {
|
||||
if (x >= task.x2) return 100;
|
||||
else if (x <= task.x1) return 0;
|
||||
else {
|
||||
@ -175,7 +276,7 @@ export const getProgressPoint = (
|
||||
return point.join(",");
|
||||
};
|
||||
|
||||
export const startByX = (x: number, xStep: number, task: BarTask) => {
|
||||
const startByX = (x: number, xStep: number, task: BarTask) => {
|
||||
if (x >= task.x2 - task.handleWidth * 2) {
|
||||
x = task.x2 - task.handleWidth * 2;
|
||||
}
|
||||
@ -185,7 +286,7 @@ export const startByX = (x: number, xStep: number, task: BarTask) => {
|
||||
return newX;
|
||||
};
|
||||
|
||||
export const endByX = (x: number, xStep: number, task: BarTask) => {
|
||||
const endByX = (x: number, xStep: number, task: BarTask) => {
|
||||
if (x <= task.x1 + task.handleWidth * 2) {
|
||||
x = task.x1 + task.handleWidth * 2;
|
||||
}
|
||||
@ -195,7 +296,7 @@ export const endByX = (x: number, xStep: number, task: BarTask) => {
|
||||
return newX;
|
||||
};
|
||||
|
||||
export const moveByX = (x: number, xStep: number, task: BarTask) => {
|
||||
const moveByX = (x: number, xStep: number, task: BarTask) => {
|
||||
const steps = Math.round((x - task.x1) / xStep);
|
||||
const additionalXValue = steps * xStep;
|
||||
const newX1 = task.x1 + additionalXValue;
|
||||
@ -203,7 +304,7 @@ export const moveByX = (x: number, xStep: number, task: BarTask) => {
|
||||
return [newX1, newX2];
|
||||
};
|
||||
|
||||
export const dateByX = (
|
||||
const dateByX = (
|
||||
x: number,
|
||||
taskX: number,
|
||||
taskDate: Date,
|
||||
@ -218,8 +319,6 @@ export const dateByX = (
|
||||
return newDate;
|
||||
};
|
||||
|
||||
export type BarMoveAction = "progress" | "end" | "start" | "move" | "";
|
||||
|
||||
/**
|
||||
* Method handles event in real time(mousemove) and on finish(mouseup)
|
||||
*/
|
||||
@ -230,7 +329,41 @@ export const handleTaskBySVGMouseEvent = (
|
||||
xStep: number,
|
||||
timeStep: number,
|
||||
initEventX1Delta: number
|
||||
) => {
|
||||
): { 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,
|
||||
initEventX1Delta
|
||||
);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const handleTaskBySVGMouseEventForBar = (
|
||||
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) {
|
||||
@ -298,3 +431,39 @@ export const handleTaskBySVGMouseEvent = (
|
||||
}
|
||||
return { isChanged, changedTask };
|
||||
};
|
||||
|
||||
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 };
|
||||
};
|
||||
|
||||
@ -55,7 +55,7 @@ export const startOfDate = (date: Date, scale: DateHelperScales) => {
|
||||
|
||||
export const ganttDateRange = (tasks: Task[], viewMode: ViewMode) => {
|
||||
let newStartDate: Date = tasks[0].start;
|
||||
let newEndDate: Date = tasks[0].end;
|
||||
let newEndDate: Date = tasks[0].start;
|
||||
for (const task of tasks) {
|
||||
if (task.start < newStartDate) {
|
||||
newStartDate = task.start;
|
||||
|
||||
26
src/helpers/reducer.ts
Normal file
26
src/helpers/reducer.ts
Normal file
@ -0,0 +1,26 @@
|
||||
export function foo() {
|
||||
return 1;
|
||||
}
|
||||
// import { BarTask } from "../types/bar-task";
|
||||
// export type TaskListAction =
|
||||
// | { type: GanttContentMoveAction; task: BarTask }
|
||||
// | { type: "update"; tasks: BarTask[] };
|
||||
|
||||
// export type TaskListState = {
|
||||
// tasks: BarTask[];
|
||||
// changedTask?: BarTask;
|
||||
// originalTask?: BarTask;
|
||||
// selectedTask?: BarTask;
|
||||
// activeAction: GanttContentMoveAction;
|
||||
// };
|
||||
|
||||
// export function taskListReducer(state: TaskListState, action: TaskListAction) {
|
||||
// switch (action.type) {
|
||||
// case "update":
|
||||
// return { ...state, tasks: action.tasks };
|
||||
// case "select":
|
||||
// return { ...state, selectedTask: action.task };
|
||||
// default:
|
||||
// return state;
|
||||
// }
|
||||
// }
|
||||
@ -14,6 +14,7 @@ describe("gantt", () => {
|
||||
name: "Redesign website",
|
||||
id: "Task 0",
|
||||
progress: 45,
|
||||
type: "task",
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
|
||||
17
src/types/gantt-task-actions.ts
Normal file
17
src/types/gantt-task-actions.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { BarTask } from "./bar-task";
|
||||
|
||||
export type BarMoveAction = "progress" | "end" | "start" | "move";
|
||||
export type GanttContentMoveAction =
|
||||
| "mouseenter"
|
||||
| "mouseleave"
|
||||
| "delete"
|
||||
| "dblclick"
|
||||
| "select"
|
||||
| ""
|
||||
| BarMoveAction;
|
||||
|
||||
export type GanttEvent = {
|
||||
changedTask?: BarTask;
|
||||
originalSelectedTask?: BarTask;
|
||||
action: GanttContentMoveAction;
|
||||
};
|
||||
@ -6,8 +6,10 @@ export enum ViewMode {
|
||||
Week = "Week",
|
||||
Month = "Month",
|
||||
}
|
||||
export type TaskType = "task" | "milestone";
|
||||
export interface Task {
|
||||
id: string;
|
||||
type: TaskType;
|
||||
name: string;
|
||||
start: Date;
|
||||
end: Date;
|
||||
@ -85,6 +87,8 @@ export interface StylingOption {
|
||||
barProgressSelectedColor?: string;
|
||||
barBackgroundColor?: string;
|
||||
barBackgroundSelectedColor?: string;
|
||||
milestoneBackgroundColor?: string;
|
||||
milestoneBackgroundSelectedColor?: string;
|
||||
arrowColor?: string;
|
||||
arrowIndent?: number;
|
||||
todayColor?: string;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user