Skip to content

Commit bdc8da5

Browse files
committed
feat: add support for extra columns in Gantt chart tasks
1 parent e287736 commit bdc8da5

File tree

11 files changed

+378
-5
lines changed

11 files changed

+378
-5
lines changed

example/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from "react";
22
import { Task, ViewMode, Gantt } from "gantt-task-react";
33
import { ViewSwitcher } from "./components/view-switcher";
44
import { getStartEndDateForProject, initTasks } from "./helper";
5+
import ExtraColumnsApp from "./ExtraColumnsApp";
56
import "gantt-task-react/dist/index.css";
67

78
// Init
@@ -103,6 +104,7 @@ const App = () => {
103104
ganttHeight={300}
104105
columnWidth={columnWidth}
105106
/>
107+
<ExtraColumnsApp />
106108
</div>
107109
);
108110
};

example/src/ExtraColumnsApp.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import React, { useState } from "react";
2+
import { ViewMode, Gantt } from "gantt-task-react";
3+
import { ViewSwitcher } from "./components/view-switcher";
4+
import { initTasksWithExtraColumns, extraColumns } from "./extra-columns-helper";
5+
import "gantt-task-react/dist/index.css";
6+
7+
//Init
8+
const ExtraColumnsApp: React.FC = () => {
9+
const [tasks, setTasks] = useState(initTasksWithExtraColumns());
10+
const [view, setView] = useState<ViewMode>(ViewMode.Day);
11+
let columnWidth = 65;
12+
if (view === ViewMode.Year) {
13+
columnWidth = 350;
14+
} else if (view === ViewMode.Month) {
15+
columnWidth = 300;
16+
} else if (view === ViewMode.Week) {
17+
columnWidth = 250;
18+
}
19+
20+
return (
21+
<div className="Wrapper">
22+
<h2>Gantt Chart with Extra Columns Example</h2>
23+
<p>This example demonstrates how to add custom columns to the Gantt chart task list.</p>
24+
25+
<ViewSwitcher
26+
onViewModeChange={(viewMode) => setView(viewMode)}
27+
onViewListChange={() => {}}
28+
isChecked={true}
29+
/>
30+
31+
<div style={{ marginTop: "20px" }}>
32+
<Gantt
33+
tasks={tasks}
34+
viewMode={view}
35+
extraColumns={extraColumns}
36+
onDateChange={(task, _children) => {
37+
console.log("On date change Id:" + task.id);
38+
setTasks(tasks);
39+
}}
40+
onDelete={(task) => {
41+
const conf = window.confirm("Are you sure about " + task.name + " ?");
42+
if (conf) {
43+
setTasks(tasks.filter((t) => t.id !== task.id));
44+
}
45+
return conf;
46+
}}
47+
onProgressChange={(task, _children) => {
48+
console.log("On progress change Id:" + task.id);
49+
setTasks(tasks);
50+
}}
51+
onDoubleClick={(task) => {
52+
console.log("On Double Click event Id:" + task.id);
53+
}}
54+
onClick={(task) => {
55+
console.log("On Click event Id:" + task.id);
56+
}}
57+
columnWidth={columnWidth}
58+
listCellWidth="180px"
59+
/>
60+
</div>
61+
62+
<div style={{ marginTop: "20px", padding: "15px", backgroundColor: "#f8f9fa", borderRadius: "5px" }}>
63+
<h3>Features Demonstrated:</h3>
64+
<ul>
65+
<li><strong>Status Column:</strong> Shows task status with colored badges</li>
66+
<li><strong>Assignee Column:</strong> Displays who is responsible for each task</li>
67+
<li><strong>Priority Column:</strong> Shows task priority with colored indicators</li>
68+
<li><strong>Budget Column:</strong> Displays formatted budget amounts</li>
69+
</ul>
70+
71+
<h4>How to Use:</h4>
72+
<ol>
73+
<li>Define your extra columns configuration with <code>ExtraColumn[]</code></li>
74+
<li>Add <code>extraColumns</code> data to your task objects</li>
75+
<li>Pass the columns configuration to the <code>Gantt</code> component</li>
76+
<li>Optionally use custom render functions for complex column content</li>
77+
</ol>
78+
</div>
79+
</div>
80+
);
81+
};
82+
83+
export default ExtraColumnsApp;
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import React from "react";
2+
import { Task, ExtraColumn } from "../../dist/types/public-types";
3+
4+
export function initTasksWithExtraColumns() {
5+
const currentDate = new Date();
6+
const tasks: Task[] = [
7+
{
8+
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1),
9+
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15),
10+
name: "Website Redesign",
11+
id: "ProjectSample",
12+
progress: 25,
13+
type: "project",
14+
hideChildren: false,
15+
displayOrder: 1,
16+
extraColumns: {
17+
status: "In Progress",
18+
assignee: "Project Team",
19+
priority: "High",
20+
budget: 50000,
21+
},
22+
},
23+
{
24+
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1),
25+
end: new Date(
26+
currentDate.getFullYear(),
27+
currentDate.getMonth(),
28+
2,
29+
12,
30+
28
31+
),
32+
name: "Initial Planning",
33+
id: "Task 0",
34+
progress: 45,
35+
type: "task",
36+
project: "ProjectSample",
37+
displayOrder: 2,
38+
extraColumns: {
39+
status: "Completed",
40+
assignee: "John Doe",
41+
priority: "High",
42+
budget: 5000,
43+
},
44+
},
45+
{
46+
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 2),
47+
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 4, 0, 0),
48+
name: "Market Research",
49+
id: "Task 1",
50+
progress: 25,
51+
dependencies: ["Task 0"],
52+
type: "task",
53+
project: "ProjectSample",
54+
displayOrder: 3,
55+
extraColumns: {
56+
status: "In Progress",
57+
assignee: "Jane Smith",
58+
priority: "Medium",
59+
budget: 8000,
60+
},
61+
},
62+
{
63+
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 4),
64+
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8, 0, 0),
65+
name: "Design Mockups",
66+
id: "Task 2",
67+
progress: 10,
68+
dependencies: ["Task 1"],
69+
type: "task",
70+
project: "ProjectSample",
71+
displayOrder: 4,
72+
extraColumns: {
73+
status: "Not Started",
74+
assignee: "Bob Wilson",
75+
priority: "High",
76+
budget: 12000,
77+
},
78+
},
79+
{
80+
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8),
81+
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 9, 0, 0),
82+
name: "Client Review",
83+
id: "Task 3",
84+
progress: 0,
85+
dependencies: ["Task 2"],
86+
type: "milestone",
87+
project: "ProjectSample",
88+
displayOrder: 5,
89+
extraColumns: {
90+
status: "Pending",
91+
assignee: "Client",
92+
priority: "Critical",
93+
budget: 0,
94+
},
95+
},
96+
{
97+
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 10),
98+
end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15),
99+
name: "Development",
100+
id: "Task 4",
101+
progress: 0,
102+
dependencies: ["Task 3"],
103+
type: "task",
104+
project: "ProjectSample",
105+
displayOrder: 6,
106+
extraColumns: {
107+
status: "Not Started",
108+
assignee: "Dev Team",
109+
priority: "High",
110+
budget: 25000,
111+
},
112+
},
113+
];
114+
return tasks;
115+
}
116+
117+
export const extraColumns: ExtraColumn[] = [
118+
{
119+
key: "status",
120+
title: "Status",
121+
width: "120px",
122+
render: (task) => {
123+
const status = task.extraColumns?.status as string;
124+
const statusClass = {
125+
"Completed": "status-completed",
126+
"In Progress": "status-in-progress",
127+
"Not Started": "status-not-started",
128+
"Pending": "status-pending"
129+
}[status] || "";
130+
131+
return (
132+
<span className={`status-badge ${statusClass}`}>
133+
{status}
134+
</span>
135+
);
136+
},
137+
},
138+
{
139+
key: "assignee",
140+
title: "Assignee",
141+
width: "140px",
142+
},
143+
{
144+
key: "priority",
145+
title: "Priority",
146+
width: "100px",
147+
render: (task) => {
148+
const priority = task.extraColumns?.priority as string;
149+
const priorityClass = {
150+
"Critical": "priority-critical",
151+
"High": "priority-high",
152+
"Medium": "priority-medium",
153+
"Low": "priority-low"
154+
}[priority] || "";
155+
156+
return (
157+
<span className={`priority-badge ${priorityClass}`}>
158+
{priority}
159+
</span>
160+
);
161+
},
162+
},
163+
{
164+
key: "budget",
165+
title: "Budget",
166+
width: "100px",
167+
render: (task) => {
168+
const budget = task.extraColumns?.budget as number;
169+
return budget > 0 ? `$${budget.toLocaleString()}` : "-";
170+
},
171+
},
172+
];

example/src/index.css

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,51 @@ input:checked + .Slider:before {
8080
-ms-transform: translateX(26px);
8181
transform: translateX(26px);
8282
}
83+
84+
/* Extra columns styles */
85+
.status-badge, .priority-badge {
86+
padding: 4px 8px;
87+
border-radius: 4px;
88+
font-size: 12px;
89+
font-weight: 500;
90+
color: white;
91+
text-align: center;
92+
display: inline-block;
93+
min-width: 60px;
94+
}
95+
96+
/* Status badge styles */
97+
.status-completed {
98+
background-color: #28a745;
99+
}
100+
101+
.status-in-progress {
102+
background-color: #007bff;
103+
}
104+
105+
.status-not-started {
106+
background-color: #6c757d;
107+
}
108+
109+
.status-pending {
110+
background-color: #ffc107;
111+
color: #212529;
112+
}
113+
114+
/* Priority badge styles */
115+
.priority-critical {
116+
background-color: #dc3545;
117+
}
118+
119+
.priority-high {
120+
background-color: #fd7e14;
121+
}
122+
123+
.priority-medium {
124+
background-color: #ffc107;
125+
color: #212529;
126+
}
127+
128+
.priority-low {
129+
background-color: #28a745;
130+
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/gantt/gantt.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
5858
TooltipContent = StandardTooltipContent,
5959
TaskListHeader = TaskListHeaderDefault,
6060
TaskListTable = TaskListTableDefault,
61+
extraColumns,
6162
onDateChange,
6263
onProgressChange,
6364
onDoubleClick,
@@ -446,6 +447,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
446447
taskListRef,
447448
setSelectedTask: handleSelectedTask,
448449
onExpanderClick: handleExpanderClick,
450+
extraColumns,
449451
TaskListHeader,
450452
TaskListTable,
451453
};

src/components/task-list/task-list-header.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import React from "react";
22
import styles from "./task-list-header.module.css";
3+
import { ExtraColumn } from "../../types/public-types";
34

45
export const TaskListHeaderDefault: React.FC<{
56
headerHeight: number;
67
rowWidth: string;
78
fontFamily: string;
89
fontSize: string;
9-
}> = ({ headerHeight, fontFamily, fontSize, rowWidth }) => {
10+
extraColumns?: ExtraColumn[];
11+
}> = ({ headerHeight, fontFamily, fontSize, rowWidth, extraColumns = [] }) => {
1012
return (
1113
<div
1214
className={styles.ganttTable}
@@ -59,6 +61,26 @@ export const TaskListHeaderDefault: React.FC<{
5961
>
6062
&nbsp;To
6163
</div>
64+
{/* Render extra column headers */}
65+
{extraColumns.map((column) => (
66+
<React.Fragment key={column.key}>
67+
<div
68+
className={styles.ganttTable_HeaderSeparator}
69+
style={{
70+
height: headerHeight * 0.5,
71+
marginTop: headerHeight * 0.25,
72+
}}
73+
/>
74+
<div
75+
className={styles.ganttTable_HeaderItem}
76+
style={{
77+
minWidth: column.width || rowWidth,
78+
}}
79+
>
80+
&nbsp;{column.title}
81+
</div>
82+
</React.Fragment>
83+
))}
6284
</div>
6385
</div>
6486
);

0 commit comments

Comments
 (0)