diff --git a/README.md b/README.md
index 61704f8..c64ba07 100644
--- a/README.md
+++ b/README.md
@@ -84,26 +84,32 @@ npm start
### StylingOption
-| Parameter Name | Type | Description |
-| :------------------------- | :----- | :---------------------------------------------------------------------- |
-| headerHeight | number | Specifies the header height. |
-| columnWidth | number | Specifies the time period width. |
-| rowHeight | number | Specifies the task row height. |
-| barCornerRadius | number | Specifies the taskbar corner rounding. |
-| barFill | number | Specifies the taskbar occupation. Sets in percent from 0 to 100. |
-| handleWidth | number | Specifies width the taskbar drag event control for start and end dates. |
-| fontFamily | string | Specifies the application font. |
-| fontSize | string | Specifies the application font size. |
-| barProgressColor | string | Specifies the taskbar progress fill color globally. |
-| barProgressSelectedColor | string | Specifies the taskbar progress fill color globally on select. |
-| barBackgroundColor | string | Specifies the taskbar background fill color globally. |
-| barBackgroundSelectedColor | string | Specifies the taskbar background fill color globally on select. |
-| arrowColor | string | Specifies the relationship arrow fill color. |
-| arrowIndent | number | Specifies the relationship arrow right indent. Sets in px |
-| todayColor | string | Specifies the current period column fill color. |
-| getTooltipContent | \*\* | Specifies the Tooltip for selected taskbar. |
+| Parameter Name | Type | Description |
+| :------------------------- | :------- | :--------------------------------------------------------------------------------------------- |
+| headerHeight | number | Specifies the header height. |
+| ganttHeight | number | Specifies the gantt chart height without header. Default is 0. It`s mean no height limitation. |
+| columnWidth | number | Specifies the time period width. |
+| listCellWidth | string | Specifies the task list cell width. Empty string is mean "no display". |
+| rowHeight | number | Specifies the task row height. |
+| barCornerRadius | number | Specifies the taskbar corner rounding. |
+| barFill | number | Specifies the taskbar occupation. Sets in percent from 0 to 100. |
+| handleWidth | number | Specifies width the taskbar drag event control for start and end dates. |
+| fontFamily | string | Specifies the application font. |
+| fontSize | string | Specifies the application font size. |
+| barProgressColor | string | Specifies the taskbar progress fill color globally. |
+| barProgressSelectedColor | string | Specifies the taskbar progress fill color globally on select. |
+| barBackgroundColor | string | Specifies the taskbar background fill color globally. |
+| barBackgroundSelectedColor | string | Specifies the taskbar background fill color globally on select. |
+| arrowColor | string | Specifies the relationship arrow fill color. |
+| 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
diff --git a/example/src/App.tsx b/example/src/App.tsx
index b14f529..e2c9ea4 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -1,13 +1,19 @@
import React from "react";
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 { GanttTableExample } from "./components/gantt-table";
//Init
const App = () => {
const currentDate = new Date();
const [view, setView] = React.useState(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[] = [
{
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1),
@@ -21,7 +27,6 @@ const App = () => {
name: "Idea",
id: "Task 0",
progress: 45,
- isDisabled: true,
},
{
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 2),
@@ -58,12 +63,20 @@ const App = () => {
{
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15),
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 16),
- name: "Release & Eat Burgers",
+ 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,
+ },
];
let onTaskChange = (task: Task) => {
@@ -85,14 +98,33 @@ const App = () => {
return (
- setView(viewMode)} />
- setView(viewMode)}
+ onViewListChange={setIsChecked}
+ isChecked={isChecked}
+ />
+ Gantt With Unlimited Height
+
+ Gantt With Limited Height
+
);
diff --git a/example/src/components/gantt-table.tsx b/example/src/components/gantt-table.tsx
deleted file mode 100644
index 5687115..0000000
--- a/example/src/components/gantt-table.tsx
+++ /dev/null
@@ -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 = 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 (
-
-
-
-
-
- 📃
-
- Name
-
-
-
- 📅
-
- From
-
-
-
- 📅
-
- To
-
-
- {props.tasks.map(t => {
- return (
-
-
{t.name}
-
{t.start.toDateString()}
-
{t.end.toDateString()}
-
- );
- })}
-
-
-
-
-
- );
-};
diff --git a/example/src/components/view-switcher.tsx b/example/src/components/view-switcher.tsx
index 93b700a..44d96ae 100644
--- a/example/src/components/view-switcher.tsx
+++ b/example/src/components/view-switcher.tsx
@@ -2,31 +2,56 @@ import React from "react";
import "gantt-task-react/dist/index.css";
import { ViewMode } from "gantt-task-react";
type ViewSwitcherProps = {
- onViewChange: (viewMode: ViewMode) => void;
+ isChecked: boolean;
+ onViewListChange: (isChecked: boolean) => void;
+ onViewModeChange: (viewMode: ViewMode) => void;
};
export const ViewSwitcher: React.SFC = ({
- onViewChange,
+ onViewModeChange,
+ onViewListChange,
+ isChecked,
}) => {
return (
);
};
diff --git a/example/src/index.css b/example/src/index.css
index ec6adb0..1e7e9de 100644
--- a/example/src/index.css
+++ b/example/src/index.css
@@ -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 {
list-style: none;
-ms-box-orient: horizontal;
display: flex;
-webkit-justify-content: flex-end;
justify-content: flex-end;
+ align-items: center;
}
.Button {
@@ -59,9 +12,68 @@
color: black;
border: none;
padding: 7px 16px;
- text-align: center;
text-decoration: none;
- font-size: 14px;
margin: 4px 2px;
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);
}
diff --git a/package.json b/package.json
index 1278d78..862aa35 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gantt-task-react",
- "version": "0.1.2",
+ "version": "0.1.3",
"description": "Interactive Gantt Chart for React with TypeScript.",
"author": "MaTeMaTuK ",
"homepage": "https://github.com/MaTeMaTuK/gantt-task-react",
diff --git a/src/components/Bar/bar.module.css b/src/components/Bar/bar.module.css
index 41fde69..8a6414b 100644
--- a/src/components/Bar/bar.module.css
+++ b/src/components/Bar/bar.module.css
@@ -32,6 +32,12 @@
.barLabelOutside {
fill: #555;
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 {
diff --git a/src/components/Bar/bar.tsx b/src/components/Bar/bar.tsx
index e32c698..37e7885 100644
--- a/src/components/Bar/bar.tsx
+++ b/src/components/Bar/bar.tsx
@@ -1,14 +1,14 @@
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 {
progressWithByParams,
getProgressPoint,
} from "../../helpers/bar-helper";
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 = {
task: BarTask;
diff --git a/src/components/Calendar/calendar.module.css b/src/components/Calendar/calendar.module.css
index 4186c7c..1be99d3 100644
--- a/src/components/Calendar/calendar.module.css
+++ b/src/components/Calendar/calendar.module.css
@@ -23,3 +23,9 @@
user-select: none;
pointer-events: none;
}
+
+.calendarHeader {
+ fill: #ffffff;
+ stroke: #e0e0e0;
+ stroke-width: 1.4;
+}
diff --git a/src/components/Calendar/calendar.tsx b/src/components/Calendar/calendar.tsx
index bb1badc..c0a409e 100644
--- a/src/components/Calendar/calendar.tsx
+++ b/src/components/Calendar/calendar.tsx
@@ -67,7 +67,7 @@ export const Calendar: React.FC = ({
const getCalendarValuesForWeek = () => {
const topValues: ReactChild[] = [];
const bottomValues: ReactChild[] = [];
- let weeksCount: number = 0;
+ let weeksCount: number = 1;
const topDefaultHeight = headerHeight * 0.5;
for (let i = dates.length - 1; i >= 0; i--) {
const date = dates[i];
@@ -138,7 +138,7 @@ export const Calendar: React.FC = ({
topValues.push(
= ({
const topValue = `${date.getDate()} ${getLocaleMonth(date, locale)}`;
topValues.push(
= ({
}
return (
+
{bottomValues} {topValues}
);
diff --git a/src/components/Gantt/gantt.module.css b/src/components/Gantt/gantt.module.css
new file mode 100644
index 0000000..39e74b9
--- /dev/null
+++ b/src/components/Gantt/gantt.module.css
@@ -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;
+}
diff --git a/src/components/Gantt/gantt.tsx b/src/components/Gantt/gantt.tsx
index 9e3990c..3047efc 100644
--- a/src/components/Gantt/gantt.tsx
+++ b/src/components/Gantt/gantt.tsx
@@ -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 { Grid, GridProps } from "../Grid/grid";
-import { Calendar, CalendarProps } from "../Calendar/calendar";
-import { GanttContent, GanttContentProps } from "./gantt-content";
+import { GridProps } from "../grid/grid";
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 = ({
tasks,
headerHeight = 50,
columnWidth = 60,
+ listCellWidth = "150px",
rowHeight = 50,
+ ganttHeight = 0,
viewMode = ViewMode.Day,
locale = "en-GB",
barFill = 60,
@@ -25,27 +34,36 @@ export const Gantt: React.SFC = ({
fontSize = "14px",
arrowIndent = 20,
todayColor = "rgba(252, 248, 227, 0.5)",
+ TooltipContent = StandardTooltipContent,
+ TaskListHeader = TaskListHeaderDefault,
+ TaskListTable = TaskListTableDefault,
onDateChange,
onProgressChange,
onDoubleClick,
onTaskDelete,
- getTooltipContent,
}) => {
- const svg = useRef(null);
const [ganttTasks, setGanttTasks] = useState(tasks);
+ const [scroll, setScroll] = useState(0);
+
const [startDate, endDate] = ganttDateRange(ganttTasks, 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);
};
+ const handleScroll = (event: SyntheticEvent) => {
+ setScroll(event.currentTarget.scrollTop);
+ };
+
const gridProps: GridProps = {
columnWidth,
- gridWidth: dates.length * columnWidth,
+ gridWidth,
tasks: ganttTasks,
rowHeight,
- headerHeight,
dates,
todayColor,
};
@@ -58,7 +76,7 @@ export const Gantt: React.SFC = ({
fontFamily,
fontSize,
};
- const barProps: GanttContentProps = {
+ const barProps: TaskGanttContentProps = {
tasks: ganttTasks,
rowHeight,
barCornerRadius,
@@ -69,32 +87,52 @@ export const Gantt: React.SFC = ({
barProgressSelectedColor,
barBackgroundColor,
barBackgroundSelectedColor,
- headerHeight,
handleWidth,
arrowColor,
timeStep,
fontFamily,
fontSize,
arrowIndent,
- svg,
- onTasksDateChange: handleOnTasksChange,
+ svgHeight,
+ onTasksDateChange: onTasksDateChange,
onDateChange,
onProgressChange,
onDoubleClick,
onTaskDelete,
- getTooltipContent,
+ TooltipContent,
};
+
+ const tableProps: TaskListProps = {
+ rowHeight,
+ rowWidth: listCellWidth,
+ fontFamily,
+ fontSize,
+ tasks: ganttTasks,
+ locale,
+ headerHeight,
+ scroll,
+ ganttHeight,
+ horizontalContainerClass: styles.horizontalContainer,
+ TaskListHeader,
+ TaskListTable,
+ };
+
return (
-
+
+ {listCellWidth && }
+
+
+
);
};
diff --git a/src/components/Gantt/gantt-content.tsx b/src/components/Gantt/task-gantt-content.tsx
similarity index 87%
rename from src/components/Gantt/gantt-content.tsx
rename to src/components/Gantt/task-gantt-content.tsx
index db3929c..0666803 100644
--- a/src/components/Gantt/gantt-content.tsx
+++ b/src/components/Gantt/task-gantt-content.tsx
@@ -1,14 +1,14 @@
import React, { useEffect, useState } from "react";
import { Task, EventOption } from "../../types/public-types";
-import { Bar } from "../Bar/bar";
+import { Bar } from "../bar/bar";
import { BarTask } from "../../types/bar-task";
-import { Arrow } from "../Other/arrow";
+import { Arrow } from "../other/arrow";
import {
convertToBarTasks,
handleTaskBySVGMouseEvent,
BarMoveAction,
} from "../../helpers/bar-helper";
-import { Tooltip } from "../Other/tooltip";
+import { Tooltip } from "../other/tooltip";
import { isKeyboardEvent } from "../../helpers/other-helper";
export type GanttContentMoveAction =
@@ -21,7 +21,7 @@ export type BarEvent = {
selectedTask?: BarTask;
action: GanttContentMoveAction;
};
-export type GanttContentProps = {
+export type TaskGanttContentProps = {
tasks: Task[];
dates: Date[];
rowHeight: number;
@@ -32,23 +32,23 @@ export type GanttContentProps = {
barProgressSelectedColor: string;
barBackgroundColor: string;
barBackgroundSelectedColor: string;
- headerHeight: number;
handleWidth: number;
timeStep: number;
- svg: React.RefObject;
+ svg?: React.RefObject;
+ svgHeight: number;
arrowColor: string;
arrowIndent: number;
fontSize: string;
fontFamily: string;
- getTooltipContent?: (
- task: Task,
- fontSize: string,
- fontFamily: string
- ) => JSX.Element;
+ TooltipContent: React.FC<{
+ task: Task;
+ fontSize: string;
+ fontFamily: string;
+ }>;
onTasksDateChange: (tasks: Task[]) => void;
} & EventOption;
-export const GanttContent: React.FC = ({
+export const TaskGanttContent: React.FC = ({
tasks,
dates,
rowHeight,
@@ -59,10 +59,10 @@ export const GanttContent: React.FC = ({
barProgressSelectedColor,
barBackgroundColor,
barBackgroundSelectedColor,
- headerHeight,
handleWidth,
timeStep,
svg,
+ svgHeight,
arrowColor,
arrowIndent,
fontFamily,
@@ -72,9 +72,9 @@ export const GanttContent: React.FC = ({
onProgressChange,
onDoubleClick,
onTaskDelete,
- getTooltipContent,
+ TooltipContent,
}) => {
- const point = svg.current?.createSVGPoint();
+ const point = svg?.current?.createSVGPoint();
const [barEvent, setBarEvent] = useState({
action: "",
});
@@ -103,7 +103,6 @@ export const GanttContent: React.FC = ({
columnWidth,
rowHeight,
barFill,
- headerHeight,
barCornerRadius,
handleWidth,
barProgressColor,
@@ -120,7 +119,6 @@ export const GanttContent: React.FC = ({
dates,
barFill,
handleWidth,
- headerHeight,
barProgressColor,
barProgressSelectedColor,
barBackgroundColor,
@@ -139,9 +137,7 @@ export const GanttContent: React.FC = ({
if (action === "delete") {
if (onTaskDelete) {
await onTaskDelete(selectedTask);
- const newTasks = barTasks.filter(
- t => t.id !== barEvent.selectedTask?.id
- );
+ const newTasks = barTasks.filter(t => t.id !== selectedTask.id);
onTasksDateChange(newTasks);
}
}
@@ -154,7 +150,7 @@ export const GanttContent: React.FC = ({
setBarEvent({ action: "" });
}
} else if (action === "move") {
- if (!svg.current || !point) return;
+ if (!svg?.current || !point) return;
point.x = event.clientX;
const cursor = point.matrixTransform(
svg.current.getScreenCTM()?.inverse()
@@ -173,12 +169,12 @@ export const GanttContent: React.FC = ({
useEffect(() => {
const handleMouseMove = async (event: MouseEvent) => {
- if (!barEvent.selectedTask || !point || !svg.current) return;
+ if (!barEvent.selectedTask || !point || !svg?.current) return;
event.preventDefault();
point.x = event.clientX;
const cursor = point.matrixTransform(
- svg.current.getScreenCTM()?.inverse()
+ svg?.current.getScreenCTM()?.inverse()
);
const { isChanged, changedTask } = handleTaskBySVGMouseEvent(
@@ -199,12 +195,12 @@ export const GanttContent: React.FC = ({
const handleMouseUp = async (event: MouseEvent) => {
const { selectedTask, action } = barEvent;
- if (!selectedTask || !point || !svg.current) return;
+ if (!selectedTask || !point || !svg?.current) return;
event.preventDefault();
point.x = event.clientX;
const cursor = point.matrixTransform(
- svg.current.getScreenCTM()?.inverse()
+ svg?.current.getScreenCTM()?.inverse()
);
const { changedTask } = handleTaskBySVGMouseEvent(
@@ -239,7 +235,7 @@ export const GanttContent: React.FC = ({
barEvent.action === "end" ||
barEvent.action === "start" ||
barEvent.action === "progress") &&
- svg.current
+ svg?.current
) {
svg.current.addEventListener("mousemove", handleMouseMove);
svg.current.addEventListener("mouseup", handleMouseUp);
@@ -282,7 +278,7 @@ export const GanttContent: React.FC = ({
arrowIndent={arrowIndent}
isProgressChangeable={!!onProgressChange && !task.isDisabled}
isDateChangeable={!!onDateChange && !task.isDisabled}
- isDelete={!!onTaskDelete && !task.isDisabled}
+ isDelete={!task.isDisabled}
onEventStart={handleBarEventStart}
key={task.id}
/>
@@ -293,11 +289,12 @@ export const GanttContent: React.FC = ({
{barEvent.selectedTask && (
)}
diff --git a/src/components/Gantt/task-gantt.tsx b/src/components/Gantt/task-gantt.tsx
new file mode 100644
index 0000000..3f8c290
--- /dev/null
+++ b/src/components/Gantt/task-gantt.tsx
@@ -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 = ({
+ gridProps,
+ calendarProps,
+ barProps,
+ ganttHeight,
+ scroll,
+}) => {
+ const ganttSVGRef = useRef(null);
+ const horizontalContainerRef = useRef(null);
+ const newBarProps = { ...barProps, svg: ganttSVGRef };
+
+ useEffect(() => {
+ if (horizontalContainerRef.current) {
+ horizontalContainerRef.current.scrollTop = scroll;
+ }
+ }, [scroll]);
+
+ return (
+
+ );
+};
diff --git a/src/components/Grid/grid-body.tsx b/src/components/Grid/grid-body.tsx
index 980cdcb..4f0e246 100644
--- a/src/components/Grid/grid-body.tsx
+++ b/src/components/Grid/grid-body.tsx
@@ -8,7 +8,6 @@ export type GridBodyProps = {
dates: Date[];
gridWidth: number;
rowHeight: number;
- headerHeight: number;
columnWidth: number;
todayColor: string;
};
@@ -16,14 +15,22 @@ export const GridBody: React.FC = ({
tasks,
dates,
rowHeight,
- headerHeight,
gridWidth,
columnWidth,
todayColor,
}) => {
- let y = headerHeight;
+ let y = 0;
const gridRows: ReactChild[] = [];
- const rowLines: ReactChild[] = [];
+ const rowLines: ReactChild[] = [
+ ,
+ ];
for (const task of tasks) {
gridRows.push(
= ({
= ({
- gridWidth,
- headerHeight,
-}) => {
- return (
-
- );
-};
diff --git a/src/components/Grid/grid.module.css b/src/components/Grid/grid.module.css
index 18b2bf9..964303f 100644
--- a/src/components/Grid/grid.module.css
+++ b/src/components/Grid/grid.module.css
@@ -1,17 +1,11 @@
.gridRow {
- fill: #ffffff;
+ fill: #fff;
}
.gridRow:nth-child(even) {
fill: #f5f5f5;
}
-.gridHeader {
- fill: #ffffff;
- stroke: #e0e0e0;
- stroke-width: 1.4;
-}
-
.gridRowLine {
stroke: #ebeff2;
}
diff --git a/src/components/Grid/grid.tsx b/src/components/Grid/grid.tsx
index 2b77ecd..488cfa3 100644
--- a/src/components/Grid/grid.tsx
+++ b/src/components/Grid/grid.tsx
@@ -1,12 +1,10 @@
import React from "react";
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 = props => {
return (
-
);
diff --git a/src/components/Other/scroll.module.css b/src/components/Other/scroll.module.css
new file mode 100644
index 0000000..c2dcbc5
--- /dev/null
+++ b/src/components/Other/scroll.module.css
@@ -0,0 +1,6 @@
+.scroll {
+ overflow: hidden auto;
+ margin-left: -17px;
+ width: 17px;
+ flex-shrink: 0;
+}
diff --git a/src/components/Other/scroll.tsx b/src/components/Other/scroll.tsx
new file mode 100644
index 0000000..bec2f16
--- /dev/null
+++ b/src/components/Other/scroll.tsx
@@ -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) => void;
+}> = ({ ganttHeight, ganttFullHeight, headerHeight, onScroll }) => {
+ return (
+
+ );
+};
diff --git a/src/components/Other/tooltip.tsx b/src/components/Other/tooltip.tsx
index cb66fb0..14f2bd2 100644
--- a/src/components/Other/tooltip.tsx
+++ b/src/components/Other/tooltip.tsx
@@ -1,54 +1,63 @@
import React, { useRef, useEffect, useState } from "react";
import { Task } from "../../types/public-types";
+import { BarTask } from "../../types/bar-task";
import styles from "./tooltip.module.css";
export type TooltipProps = {
x: number;
- y: number;
- task: Task;
+ svgHeight: number;
+ rowHeight: number;
+ task: BarTask;
fontSize: string;
fontFamily: string;
- getTooltipContent?: (
- task: Task,
- fontSize: string,
- fontFamily: string
- ) => JSX.Element;
+ TooltipContent: React.FC<{
+ task: Task;
+ fontSize: string;
+ fontFamily: string;
+ }>;
};
export const Tooltip: React.FC = ({
x,
- y,
+ rowHeight,
+ svgHeight,
task,
fontSize,
fontFamily,
-
- getTooltipContent = getStandardTooltipContent,
+ TooltipContent,
}) => {
const tooltipRef = useRef(null);
const [toolWidth, setToolWidth] = useState(1000);
- const [relatedY, setRelatedY] = useState(y);
+ const [relatedY, setRelatedY] = useState((task.index - 1) * rowHeight);
useEffect(() => {
if (tooltipRef.current) {
- const height =
- tooltipRef.current.offsetHeight +
- tooltipRef.current.offsetHeight * 0.15;
- setRelatedY(y - height);
+ const tooltipHeight = tooltipRef.current.offsetHeight;
+ const tooltipY = task.index * rowHeight + rowHeight;
+ if (tooltipHeight > tooltipY) {
+ setRelatedY(tooltipHeight * 0.5);
+ } else if (tooltipY + tooltipHeight > svgHeight) {
+ setRelatedY(svgHeight - tooltipHeight * 1.05);
+ }
setToolWidth(tooltipRef.current.scrollWidth * 1.1);
}
- }, [tooltipRef, y]);
+ }, [tooltipRef, task]);
return (
- {getTooltipContent(task, fontSize, fontFamily)}
+
);
};
-const getStandardTooltipContent = (
- task: Task,
- fontSize: string,
- fontFamily: string
-) => {
+export const StandardTooltipContent: React.FC<{
+ task: Task;
+ fontSize: string;
+ fontFamily: string;
+}> = ({ task, fontSize, fontFamily }) => {
const style = {
fontSize,
fontFamily,
@@ -66,9 +75,9 @@ const getStandardTooltipContent = (
(task.end.getTime() - task.start.getTime()) /
(1000 * 60 * 60 * 24)
)} day(s)`}
- {`Progress: ${task.progress} %`}
+
+ {!!task.progress && `Progress: ${task.progress} %`}
+
);
};
diff --git a/src/components/task-list/task-list-header.module.css b/src/components/task-list/task-list-header.module.css
new file mode 100644
index 0000000..c250354
--- /dev/null
+++ b/src/components/task-list/task-list-header.module.css
@@ -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;
+}
diff --git a/src/components/task-list/task-list-header.tsx b/src/components/task-list/task-list-header.tsx
new file mode 100644
index 0000000..9be6702
--- /dev/null
+++ b/src/components/task-list/task-list-header.tsx
@@ -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 (
+
+
+
+ Name
+
+
+
+ From
+
+
+
+ To
+
+
+
+ );
+};
diff --git a/src/components/task-list/task-list-table.module.css b/src/components/task-list/task-list-table.module.css
new file mode 100644
index 0000000..1556a1d
--- /dev/null
+++ b/src/components/task-list/task-list-table.module.css
@@ -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;
+}
diff --git a/src/components/task-list/task-list-table.tsx b/src/components/task-list/task-list-table.tsx
new file mode 100644
index 0000000..4e6fb20
--- /dev/null
+++ b/src/components/task-list/task-list-table.tsx
@@ -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 (
+
+ {tasks.map(t => {
+ return (
+
+
+ {t.name}
+
+
+ {t.start.toLocaleDateString(locale, dateTimeOptions)}
+
+
+ {t.end.toLocaleDateString(locale, dateTimeOptions)}
+
+
+ );
+ })}
+
+ );
+};
diff --git a/src/components/task-list/task-list.tsx b/src/components/task-list/task-list.tsx
new file mode 100644
index 0000000..98d770d
--- /dev/null
+++ b/src/components/task-list/task-list.tsx
@@ -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 = ({
+ headerHeight,
+ fontFamily,
+ fontSize,
+ rowWidth,
+ rowHeight,
+ scroll,
+ tasks,
+ locale,
+ ganttHeight,
+ horizontalContainerClass,
+ TaskListHeader,
+ TaskListTable,
+}) => {
+ const horizontalContainerRef = useRef(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 (
+
+ );
+};
diff --git a/src/helpers/bar-helper.ts b/src/helpers/bar-helper.ts
index 125d4e7..a86adc9 100644
--- a/src/helpers/bar-helper.ts
+++ b/src/helpers/bar-helper.ts
@@ -7,7 +7,6 @@ export const convertToBarTasks = (
columnWidth: number,
rowHeight: number,
barFill: number,
- headerHeight: number,
barCornerRadius: number,
handleWidth: number,
barProgressColor: string,
@@ -31,7 +30,6 @@ export const convertToBarTasks = (
columnWidth,
rowHeight,
taskHeight,
- headerHeight,
barCornerRadius,
handleWidth,
barProgressColor,
@@ -64,7 +62,6 @@ export const convertToBarTask = (
columnWidth: number,
rowHeight: number,
taskHeight: number,
- headerHeight: number,
barCornerRadius: number,
handleWidth: number,
barProgressColor: string,
@@ -74,7 +71,7 @@ export const convertToBarTask = (
): BarTask => {
const x1 = taskXCoordinate(task.start, 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 = {
backgroundColor: barBackgroundColor,
@@ -125,10 +122,9 @@ export const taskXCoordinate = (
export const taskYCoordinate = (
index: number,
rowHeight: number,
- taskHeight: number,
- headerHeight: number
+ taskHeight: number
) => {
- const y = index * rowHeight + headerHeight + (rowHeight - taskHeight) / 2;
+ const y = index * rowHeight + (rowHeight - taskHeight) / 2;
return y;
};
diff --git a/src/index.tsx b/src/index.tsx
index 7f7e625..47e8b63 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,4 +1,4 @@
-export { Gantt } from "./components/Gantt/gantt";
+export { Gantt } from "./components/gantt/gantt";
export { ViewMode } from "./types/public-types";
export type {
GanttProps,
diff --git a/src/styles.css b/src/styles.css
index aa68035..d83ae7d 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -8,6 +8,14 @@
outline: none;
}
+.GanttBar-handle {
+ fill: #ddd;
+ cursor: ew-resize;
+ opacity: 0;
+ visibility: hidden;
+ transition: opacity 0.3s ease;
+}
+
.GanttBar-wrapper:hover .GanttBar-handle {
visibility: visible;
opacity: 1;
@@ -31,14 +39,6 @@
text-anchor: start;
}
-.GanttBar-handle {
- fill: #ddd;
- cursor: ew-resize;
- opacity: 0;
- visibility: hidden;
- transition: opacity 0.3s ease;
-}
-
.GanttCalendar-bottomText {
text-anchor: middle;
fill: #333;
@@ -66,7 +66,7 @@
}
.GanttGrid-row {
- fill: #ffffff;
+ fill: #fff;
}
.GanttGrid-row:nth-child(even) {
@@ -74,7 +74,7 @@
}
.GanttGrid-header {
- fill: #ffffff;
+ fill: #fff;
stroke: #e0e0e0;
stroke-width: 1.4;
}
diff --git a/src/types/public-types.ts b/src/types/public-types.ts
index 424e133..7872b2f 100644
--- a/src/types/public-types.ts
+++ b/src/types/public-types.ts
@@ -47,7 +47,9 @@ export interface DisplayOption {
export interface StylingOption {
headerHeight?: number;
columnWidth?: number;
+ listCellWidth?: string;
rowHeight?: number;
+ ganttHeight?: number;
barCornerRadius?: number;
handleWidth?: number;
fontFamily?: string;
@@ -64,11 +66,25 @@ export interface StylingOption {
arrowColor?: string;
arrowIndent?: number;
todayColor?: string;
- getTooltipContent?: (
- task: Task,
- fontSize: string,
- fontFamily: string
- ) => JSX.Element;
+ TooltipContent?: React.FC<{
+ task: Task;
+ fontSize: string;
+ fontFamily: 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 interface GanttProps extends EventOption, DisplayOption, StylingOption {