gantt and timeline has separated, added OOB list
This commit is contained in:
parent
ac061d6cb6
commit
764b027bd8
44
README.md
44
README.md
@ -84,26 +84,32 @@ npm start
|
|||||||
|
|
||||||
### StylingOption
|
### StylingOption
|
||||||
|
|
||||||
| Parameter Name | Type | Description |
|
| Parameter Name | Type | Description |
|
||||||
| :------------------------- | :----- | :---------------------------------------------------------------------- |
|
| :------------------------- | :------- | :--------------------------------------------------------------------------------------------- |
|
||||||
| headerHeight | number | Specifies the header height. |
|
| headerHeight | number | Specifies the header height. |
|
||||||
| columnWidth | number | Specifies the time period width. |
|
| ganttHeight | number | Specifies the gantt chart height without header. Default is 0. It`s mean no height limitation. |
|
||||||
| rowHeight | number | Specifies the task row height. |
|
| columnWidth | number | Specifies the time period width. |
|
||||||
| barCornerRadius | number | Specifies the taskbar corner rounding. |
|
| listCellWidth | string | Specifies the task list cell width. Empty string is mean "no display". |
|
||||||
| barFill | number | Specifies the taskbar occupation. Sets in percent from 0 to 100. |
|
| rowHeight | number | Specifies the task row height. |
|
||||||
| handleWidth | number | Specifies width the taskbar drag event control for start and end dates. |
|
| barCornerRadius | number | Specifies the taskbar corner rounding. |
|
||||||
| fontFamily | string | Specifies the application font. |
|
| barFill | number | Specifies the taskbar occupation. Sets in percent from 0 to 100. |
|
||||||
| fontSize | string | Specifies the application font size. |
|
| handleWidth | number | Specifies width the taskbar drag event control for start and end dates. |
|
||||||
| barProgressColor | string | Specifies the taskbar progress fill color globally. |
|
| fontFamily | string | Specifies the application font. |
|
||||||
| barProgressSelectedColor | string | Specifies the taskbar progress fill color globally on select. |
|
| fontSize | string | Specifies the application font size. |
|
||||||
| barBackgroundColor | string | Specifies the taskbar background fill color globally. |
|
| barProgressColor | string | Specifies the taskbar progress fill color globally. |
|
||||||
| barBackgroundSelectedColor | string | Specifies the taskbar background fill color globally on select. |
|
| barProgressSelectedColor | string | Specifies the taskbar progress fill color globally on select. |
|
||||||
| arrowColor | string | Specifies the relationship arrow fill color. |
|
| barBackgroundColor | string | Specifies the taskbar background fill color globally. |
|
||||||
| arrowIndent | number | Specifies the relationship arrow right indent. Sets in px |
|
| barBackgroundSelectedColor | string | Specifies the taskbar background fill color globally on select. |
|
||||||
| todayColor | string | Specifies the current period column fill color. |
|
| arrowColor | string | Specifies the relationship arrow fill color. |
|
||||||
| getTooltipContent | \*\* | Specifies the Tooltip for selected taskbar. |
|
| arrowIndent | number | Specifies the relationship arrow right indent. Sets in px |
|
||||||
|
| todayColor | string | Specifies the current period column fill color. |
|
||||||
|
| TooltipContent | \*\* | Specifies the Tooltip view for selected taskbar. |
|
||||||
|
| TaskListHeader | \*\*\* | Specifies the task list Header view |
|
||||||
|
| TaskListTable | \*\*\*\* | Specifies the task list Table view |
|
||||||
|
|
||||||
[\*\*`(task:Task, fontSize:string , fontFamily:string) => JSX.Element;`](https://github.com/MaTeMaTuK/gantt-task-react/blob/07dfeddd4d96ecc418619cad9cd9ba3c31bb82a8/src/components/Other/tooltip.tsx#L47)
|
[\*\*`React.FC<{ task: Task; fontSize: string; fontFamily: string; }>;`](https://github.com/MaTeMaTuK/gantt-task-react/blob/07dfeddd4d96ecc418619cad9cd9ba3c31bb82a8/src/components/Other/tooltip.tsx#L47)
|
||||||
|
\*\*\*React.SFC<{ headerHeight: number; rowWidth: string; fontFamily: string; fontSize: string;}>;
|
||||||
|
\*\*\*\*TaskListTable: React.SFC<{ rowHeight: number; rowWidth: string; fontFamily: string; fontSize: string; locale: string; tasks: Task[]; }>;
|
||||||
|
|
||||||
### Task
|
### Task
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,19 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import "gantt-task-react/dist/index.css";
|
import "gantt-task-react/dist/index.css";
|
||||||
import { Task, ViewMode } from "gantt-task-react";
|
import { Task, ViewMode, Gantt } from "gantt-task-react";
|
||||||
import { ViewSwitcher } from "./components/view-switcher";
|
import { ViewSwitcher } from "./components/view-switcher";
|
||||||
import { GanttTableExample } from "./components/gantt-table";
|
|
||||||
|
|
||||||
//Init
|
//Init
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
const [view, setView] = React.useState<ViewMode>(ViewMode.Day);
|
const [view, setView] = React.useState<ViewMode>(ViewMode.Day);
|
||||||
|
const [isChecked, setIsChecked] = React.useState(true);
|
||||||
|
let columnWidth = 60;
|
||||||
|
if (view === ViewMode.Month) {
|
||||||
|
columnWidth = 300;
|
||||||
|
} else if (view === ViewMode.Week) {
|
||||||
|
columnWidth = 250;
|
||||||
|
}
|
||||||
let tasks: Task[] = [
|
let tasks: Task[] = [
|
||||||
{
|
{
|
||||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1),
|
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1),
|
||||||
@ -21,7 +27,6 @@ const App = () => {
|
|||||||
name: "Idea",
|
name: "Idea",
|
||||||
id: "Task 0",
|
id: "Task 0",
|
||||||
progress: 45,
|
progress: 45,
|
||||||
isDisabled: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 2),
|
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 2),
|
||||||
@ -58,12 +63,20 @@ const App = () => {
|
|||||||
{
|
{
|
||||||
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15),
|
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15),
|
||||||
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 16),
|
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 16),
|
||||||
name: "Release & Eat Burgers",
|
name: "Release & Eat Pizza",
|
||||||
id: "Task 6",
|
id: "Task 6",
|
||||||
progress: currentDate.getMonth(),
|
progress: currentDate.getMonth(),
|
||||||
dependencies: ["Task 4"],
|
dependencies: ["Task 4"],
|
||||||
styles: { progressColor: "#ffbb54", progressSelectedColor: "#ff9e0d" },
|
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,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let onTaskChange = (task: Task) => {
|
let onTaskChange = (task: Task) => {
|
||||||
@ -85,14 +98,33 @@ const App = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ViewSwitcher onViewChange={viewMode => setView(viewMode)} />
|
<ViewSwitcher
|
||||||
<GanttTableExample
|
onViewModeChange={viewMode => setView(viewMode)}
|
||||||
|
onViewListChange={setIsChecked}
|
||||||
|
isChecked={isChecked}
|
||||||
|
/>
|
||||||
|
<h3>Gantt With Unlimited Height</h3>
|
||||||
|
<Gantt
|
||||||
tasks={tasks}
|
tasks={tasks}
|
||||||
viewMode={view}
|
viewMode={view}
|
||||||
onDateChange={onTaskChange}
|
onDateChange={onTaskChange}
|
||||||
onTaskDelete={onTaskDelete}
|
onTaskDelete={onTaskDelete}
|
||||||
onProgressChange={onProgressChange}
|
onProgressChange={onProgressChange}
|
||||||
onDoubleClick={onDblClick}
|
onDoubleClick={onDblClick}
|
||||||
|
listCellWidth={isChecked ? "150px" : ""}
|
||||||
|
columnWidth={columnWidth}
|
||||||
|
/>
|
||||||
|
<h3>Gantt With Limited Height</h3>
|
||||||
|
<Gantt
|
||||||
|
tasks={tasks}
|
||||||
|
viewMode={view}
|
||||||
|
onDateChange={onTaskChange}
|
||||||
|
onTaskDelete={onTaskDelete}
|
||||||
|
onProgressChange={onProgressChange}
|
||||||
|
onDoubleClick={onDblClick}
|
||||||
|
listCellWidth={isChecked ? "150px" : ""}
|
||||||
|
ganttHeight={300}
|
||||||
|
columnWidth={columnWidth}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,96 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import "gantt-task-react/dist/index.css";
|
|
||||||
import {
|
|
||||||
Gantt,
|
|
||||||
Task,
|
|
||||||
EventOption,
|
|
||||||
StylingOption,
|
|
||||||
ViewMode,
|
|
||||||
DisplayOption,
|
|
||||||
} from "gantt-task-react";
|
|
||||||
|
|
||||||
//Gantt with Custom table example
|
|
||||||
type GanttTableExampleProps = { tasks: Task[] } & EventOption & DisplayOption;
|
|
||||||
export const GanttTableExample: React.SFC<GanttTableExampleProps> = props => {
|
|
||||||
const gridColumnWidth = 150;
|
|
||||||
let options: StylingOption = {
|
|
||||||
fontSize: "14px",
|
|
||||||
fontFamily:
|
|
||||||
"Arial, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue",
|
|
||||||
headerHeight: 50,
|
|
||||||
rowHeight: 50,
|
|
||||||
};
|
|
||||||
if (props.viewMode === ViewMode.Month) {
|
|
||||||
options.columnWidth = 300;
|
|
||||||
} else if (props.viewMode === ViewMode.Week) {
|
|
||||||
options.columnWidth = 250;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="Wrapper">
|
|
||||||
<div
|
|
||||||
className="GanttTable"
|
|
||||||
style={{
|
|
||||||
fontFamily: options.fontFamily,
|
|
||||||
fontSize: options.fontSize,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="GanttTable-header"
|
|
||||||
style={{
|
|
||||||
height: options.headerHeight,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="GanttTable-headerItem"
|
|
||||||
style={{
|
|
||||||
minWidth: gridColumnWidth,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span role="img" aria-label="fromDate" className="GanttTable-icon">
|
|
||||||
📃
|
|
||||||
</span>
|
|
||||||
Name
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="GanttTable-headerItem"
|
|
||||||
style={{
|
|
||||||
minWidth: gridColumnWidth,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span role="img" aria-label="fromDate" className="GanttTable-icon">
|
|
||||||
📅
|
|
||||||
</span>
|
|
||||||
From
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="GanttTable-headerItem"
|
|
||||||
style={{
|
|
||||||
minWidth: gridColumnWidth,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span role="img" aria-label="toDate" className="GanttTable-icon">
|
|
||||||
📅
|
|
||||||
</span>
|
|
||||||
To
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{props.tasks.map(t => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="GanttTable-row"
|
|
||||||
style={{ height: options.rowHeight }}
|
|
||||||
>
|
|
||||||
<div className="GanttTable-cell">{t.name}</div>
|
|
||||||
<div className="GanttTable-cell">{t.start.toDateString()}</div>
|
|
||||||
<div className="GanttTable-cell">{t.end.toDateString()}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div style={{ overflowX: "scroll" }}>
|
|
||||||
<Gantt {...props} {...options} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -2,31 +2,56 @@ import React from "react";
|
|||||||
import "gantt-task-react/dist/index.css";
|
import "gantt-task-react/dist/index.css";
|
||||||
import { ViewMode } from "gantt-task-react";
|
import { ViewMode } from "gantt-task-react";
|
||||||
type ViewSwitcherProps = {
|
type ViewSwitcherProps = {
|
||||||
onViewChange: (viewMode: ViewMode) => void;
|
isChecked: boolean;
|
||||||
|
onViewListChange: (isChecked: boolean) => void;
|
||||||
|
onViewModeChange: (viewMode: ViewMode) => void;
|
||||||
};
|
};
|
||||||
export const ViewSwitcher: React.SFC<ViewSwitcherProps> = ({
|
export const ViewSwitcher: React.SFC<ViewSwitcherProps> = ({
|
||||||
onViewChange,
|
onViewModeChange,
|
||||||
|
onViewListChange,
|
||||||
|
isChecked,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="ViewContainer">
|
<div className="ViewContainer">
|
||||||
<button
|
<button
|
||||||
className="Button"
|
className="Button"
|
||||||
onClick={() => onViewChange(ViewMode.QuarterDay)}
|
onClick={() => onViewModeChange(ViewMode.QuarterDay)}
|
||||||
>
|
>
|
||||||
Quarter of Day
|
Quarter of Day
|
||||||
</button>
|
</button>
|
||||||
<button className="Button" onClick={() => onViewChange(ViewMode.HalfDay)}>
|
<button
|
||||||
|
className="Button"
|
||||||
|
onClick={() => onViewModeChange(ViewMode.HalfDay)}
|
||||||
|
>
|
||||||
Half of Day
|
Half of Day
|
||||||
</button>
|
</button>
|
||||||
<button className="Button" onClick={() => onViewChange(ViewMode.Day)}>
|
<button className="Button" onClick={() => onViewModeChange(ViewMode.Day)}>
|
||||||
Day
|
Day
|
||||||
</button>
|
</button>
|
||||||
<button className="Button" onClick={() => onViewChange(ViewMode.Week)}>
|
<button
|
||||||
|
className="Button"
|
||||||
|
onClick={() => onViewModeChange(ViewMode.Week)}
|
||||||
|
>
|
||||||
Week
|
Week
|
||||||
</button>
|
</button>
|
||||||
<button className="Button" onClick={() => onViewChange(ViewMode.Month)}>
|
<button
|
||||||
|
className="Button"
|
||||||
|
onClick={() => onViewModeChange(ViewMode.Month)}
|
||||||
|
>
|
||||||
Month
|
Month
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div className="Switch">
|
||||||
|
<label className="Switch_Toggle">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
defaultChecked={isChecked}
|
||||||
|
onClick={() => onViewListChange(!isChecked)}
|
||||||
|
/>
|
||||||
|
<span className="Slider" />
|
||||||
|
</label>
|
||||||
|
Show Task List
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,57 +1,10 @@
|
|||||||
/*Styles for Example Table*/
|
|
||||||
.Wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
background: #ffff;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GanttTable {
|
|
||||||
display: table;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GanttTable-header {
|
|
||||||
display: table-row;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GanttTable-headerItem {
|
|
||||||
display: table-cell;
|
|
||||||
padding-top: 24px;
|
|
||||||
vertical-align: text-bottom;
|
|
||||||
|
|
||||||
border-left: #e6e4e4 1px solid;
|
|
||||||
border-top: #e6e4e4 1px solid;
|
|
||||||
border-bottom: #e6e4e4 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GanttTable-row {
|
|
||||||
display: table-row;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GanttTable-row:nth-of-type(odd) {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GanttTable-cell {
|
|
||||||
display: table-cell;
|
|
||||||
vertical-align: middle;
|
|
||||||
border-left: #e6e4e4 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GanttTable-icon {
|
|
||||||
padding-right: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ViewContainer {
|
.ViewContainer {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
-ms-box-orient: horizontal;
|
-ms-box-orient: horizontal;
|
||||||
display: flex;
|
display: flex;
|
||||||
-webkit-justify-content: flex-end;
|
-webkit-justify-content: flex-end;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Button {
|
.Button {
|
||||||
@ -59,9 +12,68 @@
|
|||||||
color: black;
|
color: black;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 7px 16px;
|
padding: 7px 16px;
|
||||||
text-align: center;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-size: 14px;
|
|
||||||
margin: 4px 2px;
|
margin: 4px 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.Switch {
|
||||||
|
margin: 4px 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: "Arial, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue";
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.Switch_Toggle {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 30px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Switch_Toggle input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
-webkit-transition: 0.4s;
|
||||||
|
transition: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 21px;
|
||||||
|
width: 21px;
|
||||||
|
left: 6px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: white;
|
||||||
|
-webkit-transition: 0.4s;
|
||||||
|
transition: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .Slider {
|
||||||
|
background-color: #2196f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus + .Slider {
|
||||||
|
box-shadow: 0 0 1px #2196f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .Slider:before {
|
||||||
|
-webkit-transform: translateX(26px);
|
||||||
|
-ms-transform: translateX(26px);
|
||||||
|
transform: translateX(26px);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gantt-task-react",
|
"name": "gantt-task-react",
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"description": "Interactive Gantt Chart for React with TypeScript.",
|
"description": "Interactive Gantt Chart for React with TypeScript.",
|
||||||
"author": "MaTeMaTuK <maksym.vikarii@gmail.com>",
|
"author": "MaTeMaTuK <maksym.vikarii@gmail.com>",
|
||||||
"homepage": "https://github.com/MaTeMaTuK/gantt-task-react",
|
"homepage": "https://github.com/MaTeMaTuK/gantt-task-react",
|
||||||
|
|||||||
@ -32,6 +32,12 @@
|
|||||||
.barLabelOutside {
|
.barLabelOutside {
|
||||||
fill: #555;
|
fill: #555;
|
||||||
text-anchor: start;
|
text-anchor: start;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.barBackground {
|
.barBackground {
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { BarProgressHandle } from "./bar-progress-handle";
|
|
||||||
import { BarDateHandle } from "./bar-date-handle";
|
|
||||||
import { BarDisplay } from "./bar-display";
|
|
||||||
import { BarTask } from "../../types/bar-task";
|
import { BarTask } from "../../types/bar-task";
|
||||||
import {
|
import {
|
||||||
progressWithByParams,
|
progressWithByParams,
|
||||||
getProgressPoint,
|
getProgressPoint,
|
||||||
} from "../../helpers/bar-helper";
|
} from "../../helpers/bar-helper";
|
||||||
import styles from "./bar.module.css";
|
import styles from "./bar.module.css";
|
||||||
import { GanttContentMoveAction } from "../Gantt/gantt-content";
|
import { GanttContentMoveAction } from "../gantt/task-gantt-content";
|
||||||
|
import { BarDisplay } from "./bar-display";
|
||||||
|
import { BarDateHandle } from "./bar-date-handle";
|
||||||
|
import { BarProgressHandle } from "./bar-progress-handle";
|
||||||
|
|
||||||
export type BarProps = {
|
export type BarProps = {
|
||||||
task: BarTask;
|
task: BarTask;
|
||||||
|
|||||||
@ -23,3 +23,9 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.calendarHeader {
|
||||||
|
fill: #ffffff;
|
||||||
|
stroke: #e0e0e0;
|
||||||
|
stroke-width: 1.4;
|
||||||
|
}
|
||||||
|
|||||||
@ -67,7 +67,7 @@ export const Calendar: React.FC<CalendarProps> = ({
|
|||||||
const getCalendarValuesForWeek = () => {
|
const getCalendarValuesForWeek = () => {
|
||||||
const topValues: ReactChild[] = [];
|
const topValues: ReactChild[] = [];
|
||||||
const bottomValues: ReactChild[] = [];
|
const bottomValues: ReactChild[] = [];
|
||||||
let weeksCount: number = 0;
|
let weeksCount: number = 1;
|
||||||
const topDefaultHeight = headerHeight * 0.5;
|
const topDefaultHeight = headerHeight * 0.5;
|
||||||
for (let i = dates.length - 1; i >= 0; i--) {
|
for (let i = dates.length - 1; i >= 0; i--) {
|
||||||
const date = dates[i];
|
const date = dates[i];
|
||||||
@ -138,7 +138,7 @@ export const Calendar: React.FC<CalendarProps> = ({
|
|||||||
|
|
||||||
topValues.push(
|
topValues.push(
|
||||||
<TopPartOfCalendar
|
<TopPartOfCalendar
|
||||||
key={topValue}
|
key={topValue + date.getFullYear()}
|
||||||
value={topValue}
|
value={topValue}
|
||||||
x1Line={columnWidth * (i + 1)}
|
x1Line={columnWidth * (i + 1)}
|
||||||
y1Line={0}
|
y1Line={0}
|
||||||
@ -179,7 +179,7 @@ export const Calendar: React.FC<CalendarProps> = ({
|
|||||||
const topValue = `${date.getDate()} ${getLocaleMonth(date, locale)}`;
|
const topValue = `${date.getDate()} ${getLocaleMonth(date, locale)}`;
|
||||||
topValues.push(
|
topValues.push(
|
||||||
<TopPartOfCalendar
|
<TopPartOfCalendar
|
||||||
key={topValue}
|
key={topValue + date.getFullYear()}
|
||||||
value={topValue}
|
value={topValue}
|
||||||
x1Line={columnWidth * i + ticks * columnWidth}
|
x1Line={columnWidth * i + ticks * columnWidth}
|
||||||
y1Line={0}
|
y1Line={0}
|
||||||
@ -210,6 +210,13 @@ export const Calendar: React.FC<CalendarProps> = ({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<g className="calendar" fontSize={fontSize} fontFamily={fontFamily}>
|
<g className="calendar" fontSize={fontSize} fontFamily={fontFamily}>
|
||||||
|
<rect
|
||||||
|
x={0}
|
||||||
|
y={0}
|
||||||
|
width={columnWidth * dates.length}
|
||||||
|
height={headerHeight}
|
||||||
|
className={styles.calendarHeader}
|
||||||
|
/>
|
||||||
{bottomValues} {topValues}
|
{bottomValues} {topValues}
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
|
|||||||
20
src/components/Gantt/gantt.module.css
Normal file
20
src/components/Gantt/gantt.module.css
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.ganttVerticalContainer {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
font-size: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontalContainer {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
@ -1,15 +1,24 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useState, SyntheticEvent } from "react";
|
||||||
import { ViewMode, GanttProps, Task } from "../../types/public-types";
|
import { ViewMode, GanttProps, Task } from "../../types/public-types";
|
||||||
import { Grid, GridProps } from "../Grid/grid";
|
import { GridProps } from "../grid/grid";
|
||||||
import { Calendar, CalendarProps } from "../Calendar/calendar";
|
|
||||||
import { GanttContent, GanttContentProps } from "./gantt-content";
|
|
||||||
import { ganttDateRange, seedDates } from "../../helpers/date-helper";
|
import { ganttDateRange, seedDates } from "../../helpers/date-helper";
|
||||||
|
import { CalendarProps } from "../calendar/calendar";
|
||||||
|
import { TaskGanttContentProps } from "./task-gantt-content";
|
||||||
|
import { TaskListHeaderDefault } from "../task-list/task-list-header";
|
||||||
|
import { TaskListTableDefault } from "../task-list/task-list-table";
|
||||||
|
import { StandardTooltipContent } from "../other/tooltip";
|
||||||
|
import { Scroll } from "../other/scroll";
|
||||||
|
import { TaskListProps, TaskList } from "../task-list/task-list";
|
||||||
|
import styles from "./gantt.module.css";
|
||||||
|
import { TaskGantt } from "./task-gantt";
|
||||||
|
|
||||||
export const Gantt: React.SFC<GanttProps> = ({
|
export const Gantt: React.SFC<GanttProps> = ({
|
||||||
tasks,
|
tasks,
|
||||||
headerHeight = 50,
|
headerHeight = 50,
|
||||||
columnWidth = 60,
|
columnWidth = 60,
|
||||||
|
listCellWidth = "150px",
|
||||||
rowHeight = 50,
|
rowHeight = 50,
|
||||||
|
ganttHeight = 0,
|
||||||
viewMode = ViewMode.Day,
|
viewMode = ViewMode.Day,
|
||||||
locale = "en-GB",
|
locale = "en-GB",
|
||||||
barFill = 60,
|
barFill = 60,
|
||||||
@ -25,27 +34,36 @@ export const Gantt: React.SFC<GanttProps> = ({
|
|||||||
fontSize = "14px",
|
fontSize = "14px",
|
||||||
arrowIndent = 20,
|
arrowIndent = 20,
|
||||||
todayColor = "rgba(252, 248, 227, 0.5)",
|
todayColor = "rgba(252, 248, 227, 0.5)",
|
||||||
|
TooltipContent = StandardTooltipContent,
|
||||||
|
TaskListHeader = TaskListHeaderDefault,
|
||||||
|
TaskListTable = TaskListTableDefault,
|
||||||
onDateChange,
|
onDateChange,
|
||||||
onProgressChange,
|
onProgressChange,
|
||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
onTaskDelete,
|
onTaskDelete,
|
||||||
getTooltipContent,
|
|
||||||
}) => {
|
}) => {
|
||||||
const svg = useRef<SVGSVGElement>(null);
|
|
||||||
const [ganttTasks, setGanttTasks] = useState<Task[]>(tasks);
|
const [ganttTasks, setGanttTasks] = useState<Task[]>(tasks);
|
||||||
|
const [scroll, setScroll] = useState(0);
|
||||||
|
|
||||||
const [startDate, endDate] = ganttDateRange(ganttTasks, viewMode);
|
const [startDate, endDate] = ganttDateRange(ganttTasks, viewMode);
|
||||||
const dates = seedDates(startDate, endDate, viewMode);
|
const dates = seedDates(startDate, endDate, viewMode);
|
||||||
|
|
||||||
const handleOnTasksChange = (tasks: Task[]) => {
|
const svgHeight = rowHeight * tasks.length;
|
||||||
|
const gridWidth = dates.length * columnWidth;
|
||||||
|
|
||||||
|
const onTasksDateChange = (tasks: Task[]) => {
|
||||||
setGanttTasks(tasks);
|
setGanttTasks(tasks);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleScroll = (event: SyntheticEvent<HTMLDivElement>) => {
|
||||||
|
setScroll(event.currentTarget.scrollTop);
|
||||||
|
};
|
||||||
|
|
||||||
const gridProps: GridProps = {
|
const gridProps: GridProps = {
|
||||||
columnWidth,
|
columnWidth,
|
||||||
gridWidth: dates.length * columnWidth,
|
gridWidth,
|
||||||
tasks: ganttTasks,
|
tasks: ganttTasks,
|
||||||
rowHeight,
|
rowHeight,
|
||||||
headerHeight,
|
|
||||||
dates,
|
dates,
|
||||||
todayColor,
|
todayColor,
|
||||||
};
|
};
|
||||||
@ -58,7 +76,7 @@ export const Gantt: React.SFC<GanttProps> = ({
|
|||||||
fontFamily,
|
fontFamily,
|
||||||
fontSize,
|
fontSize,
|
||||||
};
|
};
|
||||||
const barProps: GanttContentProps = {
|
const barProps: TaskGanttContentProps = {
|
||||||
tasks: ganttTasks,
|
tasks: ganttTasks,
|
||||||
rowHeight,
|
rowHeight,
|
||||||
barCornerRadius,
|
barCornerRadius,
|
||||||
@ -69,32 +87,52 @@ export const Gantt: React.SFC<GanttProps> = ({
|
|||||||
barProgressSelectedColor,
|
barProgressSelectedColor,
|
||||||
barBackgroundColor,
|
barBackgroundColor,
|
||||||
barBackgroundSelectedColor,
|
barBackgroundSelectedColor,
|
||||||
headerHeight,
|
|
||||||
handleWidth,
|
handleWidth,
|
||||||
arrowColor,
|
arrowColor,
|
||||||
timeStep,
|
timeStep,
|
||||||
fontFamily,
|
fontFamily,
|
||||||
fontSize,
|
fontSize,
|
||||||
arrowIndent,
|
arrowIndent,
|
||||||
svg,
|
svgHeight,
|
||||||
onTasksDateChange: handleOnTasksChange,
|
onTasksDateChange: onTasksDateChange,
|
||||||
onDateChange,
|
onDateChange,
|
||||||
onProgressChange,
|
onProgressChange,
|
||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
onTaskDelete,
|
onTaskDelete,
|
||||||
getTooltipContent,
|
TooltipContent,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const tableProps: TaskListProps = {
|
||||||
|
rowHeight,
|
||||||
|
rowWidth: listCellWidth,
|
||||||
|
fontFamily,
|
||||||
|
fontSize,
|
||||||
|
tasks: ganttTasks,
|
||||||
|
locale,
|
||||||
|
headerHeight,
|
||||||
|
scroll,
|
||||||
|
ganttHeight,
|
||||||
|
horizontalContainerClass: styles.horizontalContainer,
|
||||||
|
TaskListHeader,
|
||||||
|
TaskListTable,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<div className={styles.wrapper}>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
{listCellWidth && <TaskList {...tableProps} />}
|
||||||
width={columnWidth * dates.length}
|
<TaskGantt
|
||||||
height={headerHeight + rowHeight * tasks.length}
|
gridProps={gridProps}
|
||||||
fontFamily={fontFamily}
|
calendarProps={calendarProps}
|
||||||
ref={svg}
|
barProps={barProps}
|
||||||
>
|
ganttHeight={ganttHeight}
|
||||||
<Grid {...gridProps} />
|
scroll={scroll}
|
||||||
<Calendar {...calendarProps} />
|
/>
|
||||||
<GanttContent {...barProps} />
|
<Scroll
|
||||||
</svg>
|
ganttFullHeight={ganttTasks.length * rowHeight}
|
||||||
|
ganttHeight={ganttHeight}
|
||||||
|
headerHeight={headerHeight}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Task, EventOption } from "../../types/public-types";
|
import { Task, EventOption } from "../../types/public-types";
|
||||||
import { Bar } from "../Bar/bar";
|
import { Bar } from "../bar/bar";
|
||||||
import { BarTask } from "../../types/bar-task";
|
import { BarTask } from "../../types/bar-task";
|
||||||
import { Arrow } from "../Other/arrow";
|
import { Arrow } from "../other/arrow";
|
||||||
import {
|
import {
|
||||||
convertToBarTasks,
|
convertToBarTasks,
|
||||||
handleTaskBySVGMouseEvent,
|
handleTaskBySVGMouseEvent,
|
||||||
BarMoveAction,
|
BarMoveAction,
|
||||||
} from "../../helpers/bar-helper";
|
} from "../../helpers/bar-helper";
|
||||||
import { Tooltip } from "../Other/tooltip";
|
import { Tooltip } from "../other/tooltip";
|
||||||
import { isKeyboardEvent } from "../../helpers/other-helper";
|
import { isKeyboardEvent } from "../../helpers/other-helper";
|
||||||
|
|
||||||
export type GanttContentMoveAction =
|
export type GanttContentMoveAction =
|
||||||
@ -21,7 +21,7 @@ export type BarEvent = {
|
|||||||
selectedTask?: BarTask;
|
selectedTask?: BarTask;
|
||||||
action: GanttContentMoveAction;
|
action: GanttContentMoveAction;
|
||||||
};
|
};
|
||||||
export type GanttContentProps = {
|
export type TaskGanttContentProps = {
|
||||||
tasks: Task[];
|
tasks: Task[];
|
||||||
dates: Date[];
|
dates: Date[];
|
||||||
rowHeight: number;
|
rowHeight: number;
|
||||||
@ -32,23 +32,23 @@ export type GanttContentProps = {
|
|||||||
barProgressSelectedColor: string;
|
barProgressSelectedColor: string;
|
||||||
barBackgroundColor: string;
|
barBackgroundColor: string;
|
||||||
barBackgroundSelectedColor: string;
|
barBackgroundSelectedColor: string;
|
||||||
headerHeight: number;
|
|
||||||
handleWidth: number;
|
handleWidth: number;
|
||||||
timeStep: number;
|
timeStep: number;
|
||||||
svg: React.RefObject<SVGSVGElement>;
|
svg?: React.RefObject<SVGSVGElement>;
|
||||||
|
svgHeight: number;
|
||||||
arrowColor: string;
|
arrowColor: string;
|
||||||
arrowIndent: number;
|
arrowIndent: number;
|
||||||
fontSize: string;
|
fontSize: string;
|
||||||
fontFamily: string;
|
fontFamily: string;
|
||||||
getTooltipContent?: (
|
TooltipContent: React.FC<{
|
||||||
task: Task,
|
task: Task;
|
||||||
fontSize: string,
|
fontSize: string;
|
||||||
fontFamily: string
|
fontFamily: string;
|
||||||
) => JSX.Element;
|
}>;
|
||||||
onTasksDateChange: (tasks: Task[]) => void;
|
onTasksDateChange: (tasks: Task[]) => void;
|
||||||
} & EventOption;
|
} & EventOption;
|
||||||
|
|
||||||
export const GanttContent: React.FC<GanttContentProps> = ({
|
export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||||
tasks,
|
tasks,
|
||||||
dates,
|
dates,
|
||||||
rowHeight,
|
rowHeight,
|
||||||
@ -59,10 +59,10 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
barProgressSelectedColor,
|
barProgressSelectedColor,
|
||||||
barBackgroundColor,
|
barBackgroundColor,
|
||||||
barBackgroundSelectedColor,
|
barBackgroundSelectedColor,
|
||||||
headerHeight,
|
|
||||||
handleWidth,
|
handleWidth,
|
||||||
timeStep,
|
timeStep,
|
||||||
svg,
|
svg,
|
||||||
|
svgHeight,
|
||||||
arrowColor,
|
arrowColor,
|
||||||
arrowIndent,
|
arrowIndent,
|
||||||
fontFamily,
|
fontFamily,
|
||||||
@ -72,9 +72,9 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
onProgressChange,
|
onProgressChange,
|
||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
onTaskDelete,
|
onTaskDelete,
|
||||||
getTooltipContent,
|
TooltipContent,
|
||||||
}) => {
|
}) => {
|
||||||
const point = svg.current?.createSVGPoint();
|
const point = svg?.current?.createSVGPoint();
|
||||||
const [barEvent, setBarEvent] = useState<BarEvent>({
|
const [barEvent, setBarEvent] = useState<BarEvent>({
|
||||||
action: "",
|
action: "",
|
||||||
});
|
});
|
||||||
@ -103,7 +103,6 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
columnWidth,
|
columnWidth,
|
||||||
rowHeight,
|
rowHeight,
|
||||||
barFill,
|
barFill,
|
||||||
headerHeight,
|
|
||||||
barCornerRadius,
|
barCornerRadius,
|
||||||
handleWidth,
|
handleWidth,
|
||||||
barProgressColor,
|
barProgressColor,
|
||||||
@ -120,7 +119,6 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
dates,
|
dates,
|
||||||
barFill,
|
barFill,
|
||||||
handleWidth,
|
handleWidth,
|
||||||
headerHeight,
|
|
||||||
barProgressColor,
|
barProgressColor,
|
||||||
barProgressSelectedColor,
|
barProgressSelectedColor,
|
||||||
barBackgroundColor,
|
barBackgroundColor,
|
||||||
@ -139,9 +137,7 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
if (action === "delete") {
|
if (action === "delete") {
|
||||||
if (onTaskDelete) {
|
if (onTaskDelete) {
|
||||||
await onTaskDelete(selectedTask);
|
await onTaskDelete(selectedTask);
|
||||||
const newTasks = barTasks.filter(
|
const newTasks = barTasks.filter(t => t.id !== selectedTask.id);
|
||||||
t => t.id !== barEvent.selectedTask?.id
|
|
||||||
);
|
|
||||||
onTasksDateChange(newTasks);
|
onTasksDateChange(newTasks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,7 +150,7 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
setBarEvent({ action: "" });
|
setBarEvent({ action: "" });
|
||||||
}
|
}
|
||||||
} else if (action === "move") {
|
} else if (action === "move") {
|
||||||
if (!svg.current || !point) return;
|
if (!svg?.current || !point) return;
|
||||||
point.x = event.clientX;
|
point.x = event.clientX;
|
||||||
const cursor = point.matrixTransform(
|
const cursor = point.matrixTransform(
|
||||||
svg.current.getScreenCTM()?.inverse()
|
svg.current.getScreenCTM()?.inverse()
|
||||||
@ -173,12 +169,12 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleMouseMove = async (event: MouseEvent) => {
|
const handleMouseMove = async (event: MouseEvent) => {
|
||||||
if (!barEvent.selectedTask || !point || !svg.current) return;
|
if (!barEvent.selectedTask || !point || !svg?.current) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
point.x = event.clientX;
|
point.x = event.clientX;
|
||||||
const cursor = point.matrixTransform(
|
const cursor = point.matrixTransform(
|
||||||
svg.current.getScreenCTM()?.inverse()
|
svg?.current.getScreenCTM()?.inverse()
|
||||||
);
|
);
|
||||||
|
|
||||||
const { isChanged, changedTask } = handleTaskBySVGMouseEvent(
|
const { isChanged, changedTask } = handleTaskBySVGMouseEvent(
|
||||||
@ -199,12 +195,12 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
|
|
||||||
const handleMouseUp = async (event: MouseEvent) => {
|
const handleMouseUp = async (event: MouseEvent) => {
|
||||||
const { selectedTask, action } = barEvent;
|
const { selectedTask, action } = barEvent;
|
||||||
if (!selectedTask || !point || !svg.current) return;
|
if (!selectedTask || !point || !svg?.current) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
point.x = event.clientX;
|
point.x = event.clientX;
|
||||||
const cursor = point.matrixTransform(
|
const cursor = point.matrixTransform(
|
||||||
svg.current.getScreenCTM()?.inverse()
|
svg?.current.getScreenCTM()?.inverse()
|
||||||
);
|
);
|
||||||
|
|
||||||
const { changedTask } = handleTaskBySVGMouseEvent(
|
const { changedTask } = handleTaskBySVGMouseEvent(
|
||||||
@ -239,7 +235,7 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
barEvent.action === "end" ||
|
barEvent.action === "end" ||
|
||||||
barEvent.action === "start" ||
|
barEvent.action === "start" ||
|
||||||
barEvent.action === "progress") &&
|
barEvent.action === "progress") &&
|
||||||
svg.current
|
svg?.current
|
||||||
) {
|
) {
|
||||||
svg.current.addEventListener("mousemove", handleMouseMove);
|
svg.current.addEventListener("mousemove", handleMouseMove);
|
||||||
svg.current.addEventListener("mouseup", handleMouseUp);
|
svg.current.addEventListener("mouseup", handleMouseUp);
|
||||||
@ -282,7 +278,7 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
arrowIndent={arrowIndent}
|
arrowIndent={arrowIndent}
|
||||||
isProgressChangeable={!!onProgressChange && !task.isDisabled}
|
isProgressChangeable={!!onProgressChange && !task.isDisabled}
|
||||||
isDateChangeable={!!onDateChange && !task.isDisabled}
|
isDateChangeable={!!onDateChange && !task.isDisabled}
|
||||||
isDelete={!!onTaskDelete && !task.isDisabled}
|
isDelete={!task.isDisabled}
|
||||||
onEventStart={handleBarEventStart}
|
onEventStart={handleBarEventStart}
|
||||||
key={task.id}
|
key={task.id}
|
||||||
/>
|
/>
|
||||||
@ -293,11 +289,12 @@ export const GanttContent: React.FC<GanttContentProps> = ({
|
|||||||
{barEvent.selectedTask && (
|
{barEvent.selectedTask && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
x={barEvent.selectedTask.x2 + arrowIndent + arrowIndent * 0.5}
|
x={barEvent.selectedTask.x2 + arrowIndent + arrowIndent * 0.5}
|
||||||
y={barEvent.selectedTask.y + rowHeight}
|
rowHeight={rowHeight}
|
||||||
|
svgHeight={svgHeight}
|
||||||
task={barEvent.selectedTask}
|
task={barEvent.selectedTask}
|
||||||
fontFamily={fontFamily}
|
fontFamily={fontFamily}
|
||||||
fontSize={fontSize}
|
fontSize={fontSize}
|
||||||
getTooltipContent={getTooltipContent}
|
TooltipContent={TooltipContent}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</g>
|
</g>
|
||||||
63
src/components/Gantt/task-gantt.tsx
Normal file
63
src/components/Gantt/task-gantt.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import React, { useRef, useEffect } from "react";
|
||||||
|
import { GridProps, Grid } from "../grid/grid";
|
||||||
|
import { CalendarProps, Calendar } from "../calendar/calendar";
|
||||||
|
import { TaskGanttContentProps, TaskGanttContent } from "./task-gantt-content";
|
||||||
|
import styles from "./gantt.module.css";
|
||||||
|
|
||||||
|
export type TaskGanttProps = {
|
||||||
|
gridProps: GridProps;
|
||||||
|
calendarProps: CalendarProps;
|
||||||
|
barProps: TaskGanttContentProps;
|
||||||
|
ganttHeight: number;
|
||||||
|
scroll: number;
|
||||||
|
};
|
||||||
|
export const TaskGantt: React.FC<TaskGanttProps> = ({
|
||||||
|
gridProps,
|
||||||
|
calendarProps,
|
||||||
|
barProps,
|
||||||
|
ganttHeight,
|
||||||
|
scroll,
|
||||||
|
}) => {
|
||||||
|
const ganttSVGRef = useRef<SVGSVGElement>(null);
|
||||||
|
const horizontalContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const newBarProps = { ...barProps, svg: ganttSVGRef };
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (horizontalContainerRef.current) {
|
||||||
|
horizontalContainerRef.current.scrollTop = scroll;
|
||||||
|
}
|
||||||
|
}, [scroll]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.ganttVerticalContainer}>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={gridProps.gridWidth}
|
||||||
|
height={calendarProps.headerHeight}
|
||||||
|
fontFamily={barProps.fontFamily}
|
||||||
|
>
|
||||||
|
<Calendar {...calendarProps} />
|
||||||
|
</svg>
|
||||||
|
<div
|
||||||
|
ref={horizontalContainerRef}
|
||||||
|
className={styles.horizontalContainer}
|
||||||
|
style={
|
||||||
|
ganttHeight
|
||||||
|
? { height: ganttHeight, width: gridProps.gridWidth }
|
||||||
|
: { width: gridProps.gridWidth }
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={gridProps.gridWidth}
|
||||||
|
height={barProps.rowHeight * barProps.tasks.length}
|
||||||
|
fontFamily={barProps.fontFamily}
|
||||||
|
ref={ganttSVGRef}
|
||||||
|
>
|
||||||
|
<Grid {...gridProps} />
|
||||||
|
<TaskGanttContent {...newBarProps} />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -8,7 +8,6 @@ export type GridBodyProps = {
|
|||||||
dates: Date[];
|
dates: Date[];
|
||||||
gridWidth: number;
|
gridWidth: number;
|
||||||
rowHeight: number;
|
rowHeight: number;
|
||||||
headerHeight: number;
|
|
||||||
columnWidth: number;
|
columnWidth: number;
|
||||||
todayColor: string;
|
todayColor: string;
|
||||||
};
|
};
|
||||||
@ -16,14 +15,22 @@ export const GridBody: React.FC<GridBodyProps> = ({
|
|||||||
tasks,
|
tasks,
|
||||||
dates,
|
dates,
|
||||||
rowHeight,
|
rowHeight,
|
||||||
headerHeight,
|
|
||||||
gridWidth,
|
gridWidth,
|
||||||
columnWidth,
|
columnWidth,
|
||||||
todayColor,
|
todayColor,
|
||||||
}) => {
|
}) => {
|
||||||
let y = headerHeight;
|
let y = 0;
|
||||||
const gridRows: ReactChild[] = [];
|
const gridRows: ReactChild[] = [];
|
||||||
const rowLines: ReactChild[] = [];
|
const rowLines: ReactChild[] = [
|
||||||
|
<line
|
||||||
|
key="RowLineFirst"
|
||||||
|
x="0"
|
||||||
|
y1={0}
|
||||||
|
x2={gridWidth}
|
||||||
|
y2={0}
|
||||||
|
className={styles.gridRowLine}
|
||||||
|
/>,
|
||||||
|
];
|
||||||
for (const task of tasks) {
|
for (const task of tasks) {
|
||||||
gridRows.push(
|
gridRows.push(
|
||||||
<rect
|
<rect
|
||||||
@ -58,7 +65,7 @@ export const GridBody: React.FC<GridBodyProps> = ({
|
|||||||
<line
|
<line
|
||||||
key={date.getTime()}
|
key={date.getTime()}
|
||||||
x1={tickX}
|
x1={tickX}
|
||||||
y1={headerHeight}
|
y1={0}
|
||||||
x2={tickX}
|
x2={tickX}
|
||||||
y2={y}
|
y2={y}
|
||||||
className={styles.gridTick}
|
className={styles.gridTick}
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import styles from "./grid.module.css";
|
|
||||||
|
|
||||||
export type GridHeaderProps = {
|
|
||||||
gridWidth: number;
|
|
||||||
headerHeight: number;
|
|
||||||
};
|
|
||||||
export const GridHeader: React.FC<GridHeaderProps> = ({
|
|
||||||
gridWidth,
|
|
||||||
headerHeight,
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<rect
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width={gridWidth}
|
|
||||||
height={headerHeight}
|
|
||||||
className={styles.gridHeader}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,17 +1,11 @@
|
|||||||
.gridRow {
|
.gridRow {
|
||||||
fill: #ffffff;
|
fill: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gridRow:nth-child(even) {
|
.gridRow:nth-child(even) {
|
||||||
fill: #f5f5f5;
|
fill: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gridHeader {
|
|
||||||
fill: #ffffff;
|
|
||||||
stroke: #e0e0e0;
|
|
||||||
stroke-width: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gridRowLine {
|
.gridRowLine {
|
||||||
stroke: #ebeff2;
|
stroke: #ebeff2;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { GridBody, GridBodyProps } from "./grid-body";
|
import { GridBody, GridBodyProps } from "./grid-body";
|
||||||
import { GridHeader, GridHeaderProps } from "./grid-header";
|
|
||||||
|
|
||||||
export type GridProps = GridBodyProps & GridHeaderProps;
|
export type GridProps = GridBodyProps;
|
||||||
export const Grid: React.FC<GridProps> = props => {
|
export const Grid: React.FC<GridProps> = props => {
|
||||||
return (
|
return (
|
||||||
<g className="grid">
|
<g className="grid">
|
||||||
<GridHeader {...props} />
|
|
||||||
<GridBody {...props} />
|
<GridBody {...props} />
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
|
|||||||
6
src/components/Other/scroll.module.css
Normal file
6
src/components/Other/scroll.module.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.scroll {
|
||||||
|
overflow: hidden auto;
|
||||||
|
margin-left: -17px;
|
||||||
|
width: 17px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
19
src/components/Other/scroll.tsx
Normal file
19
src/components/Other/scroll.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import React, { SyntheticEvent } from "react";
|
||||||
|
import styles from "./scroll.module.css";
|
||||||
|
|
||||||
|
export const Scroll: React.FC<{
|
||||||
|
ganttHeight: number;
|
||||||
|
ganttFullHeight: number;
|
||||||
|
headerHeight: number;
|
||||||
|
onScroll: (event: SyntheticEvent<HTMLDivElement>) => void;
|
||||||
|
}> = ({ ganttHeight, ganttFullHeight, headerHeight, onScroll }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{ height: ganttHeight, marginTop: headerHeight }}
|
||||||
|
className={styles.scroll}
|
||||||
|
onScroll={onScroll}
|
||||||
|
>
|
||||||
|
<div style={{ height: ganttFullHeight, width: 1 }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,54 +1,63 @@
|
|||||||
import React, { useRef, useEffect, useState } from "react";
|
import React, { useRef, useEffect, useState } from "react";
|
||||||
import { Task } from "../../types/public-types";
|
import { Task } from "../../types/public-types";
|
||||||
|
import { BarTask } from "../../types/bar-task";
|
||||||
import styles from "./tooltip.module.css";
|
import styles from "./tooltip.module.css";
|
||||||
|
|
||||||
export type TooltipProps = {
|
export type TooltipProps = {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
svgHeight: number;
|
||||||
task: Task;
|
rowHeight: number;
|
||||||
|
task: BarTask;
|
||||||
fontSize: string;
|
fontSize: string;
|
||||||
fontFamily: string;
|
fontFamily: string;
|
||||||
getTooltipContent?: (
|
TooltipContent: React.FC<{
|
||||||
task: Task,
|
task: Task;
|
||||||
fontSize: string,
|
fontSize: string;
|
||||||
fontFamily: string
|
fontFamily: string;
|
||||||
) => JSX.Element;
|
}>;
|
||||||
};
|
};
|
||||||
export const Tooltip: React.FC<TooltipProps> = ({
|
export const Tooltip: React.FC<TooltipProps> = ({
|
||||||
x,
|
x,
|
||||||
y,
|
rowHeight,
|
||||||
|
svgHeight,
|
||||||
task,
|
task,
|
||||||
fontSize,
|
fontSize,
|
||||||
fontFamily,
|
fontFamily,
|
||||||
|
TooltipContent,
|
||||||
getTooltipContent = getStandardTooltipContent,
|
|
||||||
}) => {
|
}) => {
|
||||||
const tooltipRef = useRef<HTMLDivElement | null>(null);
|
const tooltipRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [toolWidth, setToolWidth] = useState(1000);
|
const [toolWidth, setToolWidth] = useState(1000);
|
||||||
const [relatedY, setRelatedY] = useState(y);
|
const [relatedY, setRelatedY] = useState((task.index - 1) * rowHeight);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tooltipRef.current) {
|
if (tooltipRef.current) {
|
||||||
const height =
|
const tooltipHeight = tooltipRef.current.offsetHeight;
|
||||||
tooltipRef.current.offsetHeight +
|
const tooltipY = task.index * rowHeight + rowHeight;
|
||||||
tooltipRef.current.offsetHeight * 0.15;
|
if (tooltipHeight > tooltipY) {
|
||||||
setRelatedY(y - height);
|
setRelatedY(tooltipHeight * 0.5);
|
||||||
|
} else if (tooltipY + tooltipHeight > svgHeight) {
|
||||||
|
setRelatedY(svgHeight - tooltipHeight * 1.05);
|
||||||
|
}
|
||||||
setToolWidth(tooltipRef.current.scrollWidth * 1.1);
|
setToolWidth(tooltipRef.current.scrollWidth * 1.1);
|
||||||
}
|
}
|
||||||
}, [tooltipRef, y]);
|
}, [tooltipRef, task]);
|
||||||
return (
|
return (
|
||||||
<foreignObject x={x} y={relatedY} width={toolWidth} height={1000}>
|
<foreignObject x={x} y={relatedY} width={toolWidth} height={1000}>
|
||||||
<div ref={tooltipRef} className={styles.tooltipDetailsContainer}>
|
<div ref={tooltipRef} className={styles.tooltipDetailsContainer}>
|
||||||
{getTooltipContent(task, fontSize, fontFamily)}
|
<TooltipContent
|
||||||
|
task={task}
|
||||||
|
fontSize={fontSize}
|
||||||
|
fontFamily={fontFamily}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStandardTooltipContent = (
|
export const StandardTooltipContent: React.FC<{
|
||||||
task: Task,
|
task: Task;
|
||||||
fontSize: string,
|
fontSize: string;
|
||||||
fontFamily: string
|
fontFamily: string;
|
||||||
) => {
|
}> = ({ task, fontSize, fontFamily }) => {
|
||||||
const style = {
|
const style = {
|
||||||
fontSize,
|
fontSize,
|
||||||
fontFamily,
|
fontFamily,
|
||||||
@ -66,9 +75,9 @@ const getStandardTooltipContent = (
|
|||||||
(task.end.getTime() - task.start.getTime()) /
|
(task.end.getTime() - task.start.getTime()) /
|
||||||
(1000 * 60 * 60 * 24)
|
(1000 * 60 * 60 * 24)
|
||||||
)} day(s)`}</p>
|
)} day(s)`}</p>
|
||||||
<p
|
<p className={styles.tooltipDefaultContainerParagraph}>
|
||||||
className={styles.tooltipDefaultContainerParagraph}
|
{!!task.progress && `Progress: ${task.progress} %`}
|
||||||
>{`Progress: ${task.progress} %`}</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
23
src/components/task-list/task-list-header.module.css
Normal file
23
src/components/task-list/task-list-header.module.css
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.ganttTable {
|
||||||
|
display: table;
|
||||||
|
border-bottom: #e6e4e4 1px solid;
|
||||||
|
border-top: #e6e4e4 1px solid;
|
||||||
|
border-left: #e6e4e4 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ganttTable_Header {
|
||||||
|
display: table-row;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ganttTable_HeaderSeparator {
|
||||||
|
border-right: 1px solid rgb(196, 196, 196);
|
||||||
|
opacity: 1;
|
||||||
|
margin-left: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ganttTable_HeaderItem {
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: -webkit-baseline-middle;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
65
src/components/task-list/task-list-header.tsx
Normal file
65
src/components/task-list/task-list-header.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React from "react";
|
||||||
|
import styles from "./task-list-header.module.css";
|
||||||
|
|
||||||
|
export const TaskListHeaderDefault: React.SFC<{
|
||||||
|
headerHeight: number;
|
||||||
|
rowWidth: string;
|
||||||
|
fontFamily: string;
|
||||||
|
fontSize: string;
|
||||||
|
}> = ({ headerHeight, fontFamily, fontSize, rowWidth }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.ganttTable}
|
||||||
|
style={{
|
||||||
|
fontFamily: fontFamily,
|
||||||
|
fontSize: fontSize,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={styles.ganttTable_Header}
|
||||||
|
style={{
|
||||||
|
height: headerHeight - 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={styles.ganttTable_HeaderItem}
|
||||||
|
style={{
|
||||||
|
minWidth: rowWidth,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={styles.ganttTable_HeaderSeparator}
|
||||||
|
style={{
|
||||||
|
height: headerHeight * 0.5,
|
||||||
|
marginTop: headerHeight * 0.2,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={styles.ganttTable_HeaderItem}
|
||||||
|
style={{
|
||||||
|
minWidth: rowWidth,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
From
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={styles.ganttTable_HeaderSeparator}
|
||||||
|
style={{
|
||||||
|
height: headerHeight * 0.5,
|
||||||
|
marginTop: headerHeight * 0.25,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={styles.ganttTable_HeaderItem}
|
||||||
|
style={{
|
||||||
|
minWidth: rowWidth,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
To
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
19
src/components/task-list/task-list-table.module.css
Normal file
19
src/components/task-list/task-list-table.module.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.taskListWrapper {
|
||||||
|
display: table;
|
||||||
|
border-bottom: #e6e4e4 1px solid;
|
||||||
|
border-left: #e6e4e4 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskListTableRow {
|
||||||
|
display: table-row;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskListTableRow:nth-of-type(even) {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskListCell {
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
64
src/components/task-list/task-list-table.tsx
Normal file
64
src/components/task-list/task-list-table.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import React from "react";
|
||||||
|
import styles from "./task-list-table.module.css";
|
||||||
|
import { Task } from "../../types/public-types";
|
||||||
|
|
||||||
|
export const TaskListTableDefault: React.SFC<{
|
||||||
|
rowHeight: number;
|
||||||
|
rowWidth: string;
|
||||||
|
fontFamily: string;
|
||||||
|
fontSize: string;
|
||||||
|
locale: string;
|
||||||
|
tasks: Task[];
|
||||||
|
}> = ({ rowHeight, rowWidth, tasks, fontFamily, fontSize, locale }) => {
|
||||||
|
const dateTimeOptions = {
|
||||||
|
weekday: "short",
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.taskListWrapper}
|
||||||
|
style={{
|
||||||
|
fontFamily: fontFamily,
|
||||||
|
fontSize: fontSize,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tasks.map(t => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.taskListTableRow}
|
||||||
|
style={{ height: rowHeight }}
|
||||||
|
key={`${t.id}row`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={styles.taskListCell}
|
||||||
|
style={{
|
||||||
|
minWidth: rowWidth,
|
||||||
|
maxWidth: rowWidth,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t.name}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={styles.taskListCell}
|
||||||
|
style={{
|
||||||
|
minWidth: rowWidth,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t.start.toLocaleDateString(locale, dateTimeOptions)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={styles.taskListCell}
|
||||||
|
style={{
|
||||||
|
minWidth: rowWidth,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t.end.toLocaleDateString(locale, dateTimeOptions)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
78
src/components/task-list/task-list.tsx
Normal file
78
src/components/task-list/task-list.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import { Task } from "../../types/public-types";
|
||||||
|
|
||||||
|
export type TaskListProps = {
|
||||||
|
headerHeight: number;
|
||||||
|
rowWidth: string;
|
||||||
|
fontFamily: string;
|
||||||
|
fontSize: string;
|
||||||
|
rowHeight: number;
|
||||||
|
ganttHeight: number;
|
||||||
|
scroll: number;
|
||||||
|
locale: string;
|
||||||
|
tasks: Task[];
|
||||||
|
horizontalContainerClass?: string;
|
||||||
|
TaskListHeader: React.SFC<{
|
||||||
|
headerHeight: number;
|
||||||
|
rowWidth: string;
|
||||||
|
fontFamily: string;
|
||||||
|
fontSize: string;
|
||||||
|
}>;
|
||||||
|
TaskListTable: React.SFC<{
|
||||||
|
rowHeight: number;
|
||||||
|
rowWidth: string;
|
||||||
|
fontFamily: string;
|
||||||
|
fontSize: string;
|
||||||
|
locale: string;
|
||||||
|
tasks: Task[];
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TaskList: React.FC<TaskListProps> = ({
|
||||||
|
headerHeight,
|
||||||
|
fontFamily,
|
||||||
|
fontSize,
|
||||||
|
rowWidth,
|
||||||
|
rowHeight,
|
||||||
|
scroll,
|
||||||
|
tasks,
|
||||||
|
locale,
|
||||||
|
ganttHeight,
|
||||||
|
horizontalContainerClass,
|
||||||
|
TaskListHeader,
|
||||||
|
TaskListTable,
|
||||||
|
}) => {
|
||||||
|
const horizontalContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (horizontalContainerRef.current) {
|
||||||
|
horizontalContainerRef.current.scrollTop = scroll;
|
||||||
|
}
|
||||||
|
}, [scroll]);
|
||||||
|
|
||||||
|
const headerProps = {
|
||||||
|
headerHeight,
|
||||||
|
fontFamily,
|
||||||
|
fontSize,
|
||||||
|
rowWidth,
|
||||||
|
};
|
||||||
|
const tableProps = {
|
||||||
|
rowHeight,
|
||||||
|
rowWidth,
|
||||||
|
fontFamily,
|
||||||
|
fontSize,
|
||||||
|
tasks,
|
||||||
|
locale,
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<TaskListHeader {...headerProps} />
|
||||||
|
<div
|
||||||
|
ref={horizontalContainerRef}
|
||||||
|
className={horizontalContainerClass}
|
||||||
|
style={ganttHeight ? { height: ganttHeight } : {}}
|
||||||
|
>
|
||||||
|
<TaskListTable {...tableProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -7,7 +7,6 @@ export const convertToBarTasks = (
|
|||||||
columnWidth: number,
|
columnWidth: number,
|
||||||
rowHeight: number,
|
rowHeight: number,
|
||||||
barFill: number,
|
barFill: number,
|
||||||
headerHeight: number,
|
|
||||||
barCornerRadius: number,
|
barCornerRadius: number,
|
||||||
handleWidth: number,
|
handleWidth: number,
|
||||||
barProgressColor: string,
|
barProgressColor: string,
|
||||||
@ -31,7 +30,6 @@ export const convertToBarTasks = (
|
|||||||
columnWidth,
|
columnWidth,
|
||||||
rowHeight,
|
rowHeight,
|
||||||
taskHeight,
|
taskHeight,
|
||||||
headerHeight,
|
|
||||||
barCornerRadius,
|
barCornerRadius,
|
||||||
handleWidth,
|
handleWidth,
|
||||||
barProgressColor,
|
barProgressColor,
|
||||||
@ -64,7 +62,6 @@ export const convertToBarTask = (
|
|||||||
columnWidth: number,
|
columnWidth: number,
|
||||||
rowHeight: number,
|
rowHeight: number,
|
||||||
taskHeight: number,
|
taskHeight: number,
|
||||||
headerHeight: number,
|
|
||||||
barCornerRadius: number,
|
barCornerRadius: number,
|
||||||
handleWidth: number,
|
handleWidth: number,
|
||||||
barProgressColor: string,
|
barProgressColor: string,
|
||||||
@ -74,7 +71,7 @@ export const convertToBarTask = (
|
|||||||
): BarTask => {
|
): BarTask => {
|
||||||
const x1 = taskXCoordinate(task.start, dates, dateDelta, columnWidth);
|
const x1 = taskXCoordinate(task.start, dates, dateDelta, columnWidth);
|
||||||
const x2 = taskXCoordinate(task.end, dates, dateDelta, columnWidth);
|
const x2 = taskXCoordinate(task.end, dates, dateDelta, columnWidth);
|
||||||
const y = taskYCoordinate(index, rowHeight, taskHeight, headerHeight);
|
const y = taskYCoordinate(index, rowHeight, taskHeight);
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
backgroundColor: barBackgroundColor,
|
backgroundColor: barBackgroundColor,
|
||||||
@ -125,10 +122,9 @@ export const taskXCoordinate = (
|
|||||||
export const taskYCoordinate = (
|
export const taskYCoordinate = (
|
||||||
index: number,
|
index: number,
|
||||||
rowHeight: number,
|
rowHeight: number,
|
||||||
taskHeight: number,
|
taskHeight: number
|
||||||
headerHeight: number
|
|
||||||
) => {
|
) => {
|
||||||
const y = index * rowHeight + headerHeight + (rowHeight - taskHeight) / 2;
|
const y = index * rowHeight + (rowHeight - taskHeight) / 2;
|
||||||
return y;
|
return y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export { Gantt } from "./components/Gantt/gantt";
|
export { Gantt } from "./components/gantt/gantt";
|
||||||
export { ViewMode } from "./types/public-types";
|
export { ViewMode } from "./types/public-types";
|
||||||
export type {
|
export type {
|
||||||
GanttProps,
|
GanttProps,
|
||||||
|
|||||||
@ -8,6 +8,14 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.GanttBar-handle {
|
||||||
|
fill: #ddd;
|
||||||
|
cursor: ew-resize;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.GanttBar-wrapper:hover .GanttBar-handle {
|
.GanttBar-wrapper:hover .GanttBar-handle {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -31,14 +39,6 @@
|
|||||||
text-anchor: start;
|
text-anchor: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.GanttBar-handle {
|
|
||||||
fill: #ddd;
|
|
||||||
cursor: ew-resize;
|
|
||||||
opacity: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GanttCalendar-bottomText {
|
.GanttCalendar-bottomText {
|
||||||
text-anchor: middle;
|
text-anchor: middle;
|
||||||
fill: #333;
|
fill: #333;
|
||||||
@ -66,7 +66,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.GanttGrid-row {
|
.GanttGrid-row {
|
||||||
fill: #ffffff;
|
fill: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.GanttGrid-row:nth-child(even) {
|
.GanttGrid-row:nth-child(even) {
|
||||||
@ -74,7 +74,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.GanttGrid-header {
|
.GanttGrid-header {
|
||||||
fill: #ffffff;
|
fill: #fff;
|
||||||
stroke: #e0e0e0;
|
stroke: #e0e0e0;
|
||||||
stroke-width: 1.4;
|
stroke-width: 1.4;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,7 +47,9 @@ export interface DisplayOption {
|
|||||||
export interface StylingOption {
|
export interface StylingOption {
|
||||||
headerHeight?: number;
|
headerHeight?: number;
|
||||||
columnWidth?: number;
|
columnWidth?: number;
|
||||||
|
listCellWidth?: string;
|
||||||
rowHeight?: number;
|
rowHeight?: number;
|
||||||
|
ganttHeight?: number;
|
||||||
barCornerRadius?: number;
|
barCornerRadius?: number;
|
||||||
handleWidth?: number;
|
handleWidth?: number;
|
||||||
fontFamily?: string;
|
fontFamily?: string;
|
||||||
@ -64,11 +66,25 @@ export interface StylingOption {
|
|||||||
arrowColor?: string;
|
arrowColor?: string;
|
||||||
arrowIndent?: number;
|
arrowIndent?: number;
|
||||||
todayColor?: string;
|
todayColor?: string;
|
||||||
getTooltipContent?: (
|
TooltipContent?: React.FC<{
|
||||||
task: Task,
|
task: Task;
|
||||||
fontSize: string,
|
fontSize: string;
|
||||||
fontFamily: string
|
fontFamily: string;
|
||||||
) => JSX.Element;
|
}>;
|
||||||
|
TaskListHeader?: React.SFC<{
|
||||||
|
headerHeight: number;
|
||||||
|
rowWidth: string;
|
||||||
|
fontFamily: string;
|
||||||
|
fontSize: string;
|
||||||
|
}>;
|
||||||
|
TaskListTable?: React.SFC<{
|
||||||
|
rowHeight: number;
|
||||||
|
rowWidth: string;
|
||||||
|
fontFamily: string;
|
||||||
|
fontSize: string;
|
||||||
|
locale: string;
|
||||||
|
tasks: Task[];
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GanttProps extends EventOption, DisplayOption, StylingOption {
|
export interface GanttProps extends EventOption, DisplayOption, StylingOption {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user