From bdc8da536d20c043ef06010943c6ce01ab2b58e1 Mon Sep 17 00:00:00 2001 From: tim Date: Mon, 16 Jun 2025 13:21:59 +0000 Subject: [PATCH 1/5] feat: add support for extra columns in Gantt chart tasks --- example/src/App.tsx | 2 + example/src/ExtraColumnsApp.tsx | 83 +++++++++ example/src/extra-columns-helper.tsx | 172 ++++++++++++++++++ example/src/index.css | 48 +++++ package-lock.json | 4 +- src/components/gantt/gantt.tsx | 2 + src/components/task-list/task-list-header.tsx | 24 ++- src/components/task-list/task-list-table.tsx | 21 ++- src/components/task-list/task-list.tsx | 8 +- src/index.tsx | 1 + src/types/public-types.ts | 18 ++ 11 files changed, 378 insertions(+), 5 deletions(-) create mode 100644 example/src/ExtraColumnsApp.tsx create mode 100644 example/src/extra-columns-helper.tsx diff --git a/example/src/App.tsx b/example/src/App.tsx index c2ab602eb..c132031a8 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -2,6 +2,7 @@ import React from "react"; import { Task, ViewMode, Gantt } from "gantt-task-react"; import { ViewSwitcher } from "./components/view-switcher"; import { getStartEndDateForProject, initTasks } from "./helper"; +import ExtraColumnsApp from "./ExtraColumnsApp"; import "gantt-task-react/dist/index.css"; // Init @@ -103,6 +104,7 @@ const App = () => { ganttHeight={300} columnWidth={columnWidth} /> + ); }; diff --git a/example/src/ExtraColumnsApp.tsx b/example/src/ExtraColumnsApp.tsx new file mode 100644 index 000000000..579976679 --- /dev/null +++ b/example/src/ExtraColumnsApp.tsx @@ -0,0 +1,83 @@ +import React, { useState } from "react"; +import { ViewMode, Gantt } from "gantt-task-react"; +import { ViewSwitcher } from "./components/view-switcher"; +import { initTasksWithExtraColumns, extraColumns } from "./extra-columns-helper"; +import "gantt-task-react/dist/index.css"; + +//Init +const ExtraColumnsApp: React.FC = () => { + const [tasks, setTasks] = useState(initTasksWithExtraColumns()); + const [view, setView] = useState(ViewMode.Day); + let columnWidth = 65; + if (view === ViewMode.Year) { + columnWidth = 350; + } else if (view === ViewMode.Month) { + columnWidth = 300; + } else if (view === ViewMode.Week) { + columnWidth = 250; + } + + return ( +
+

Gantt Chart with Extra Columns Example

+

This example demonstrates how to add custom columns to the Gantt chart task list.

+ + setView(viewMode)} + onViewListChange={() => {}} + isChecked={true} + /> + +
+ { + console.log("On date change Id:" + task.id); + setTasks(tasks); + }} + onDelete={(task) => { + const conf = window.confirm("Are you sure about " + task.name + " ?"); + if (conf) { + setTasks(tasks.filter((t) => t.id !== task.id)); + } + return conf; + }} + onProgressChange={(task, _children) => { + console.log("On progress change Id:" + task.id); + setTasks(tasks); + }} + onDoubleClick={(task) => { + console.log("On Double Click event Id:" + task.id); + }} + onClick={(task) => { + console.log("On Click event Id:" + task.id); + }} + columnWidth={columnWidth} + listCellWidth="180px" + /> +
+ +
+

Features Demonstrated:

+
    +
  • Status Column: Shows task status with colored badges
  • +
  • Assignee Column: Displays who is responsible for each task
  • +
  • Priority Column: Shows task priority with colored indicators
  • +
  • Budget Column: Displays formatted budget amounts
  • +
+ +

How to Use:

+
    +
  1. Define your extra columns configuration with ExtraColumn[]
  2. +
  3. Add extraColumns data to your task objects
  4. +
  5. Pass the columns configuration to the Gantt component
  6. +
  7. Optionally use custom render functions for complex column content
  8. +
+
+
+ ); +}; + +export default ExtraColumnsApp; diff --git a/example/src/extra-columns-helper.tsx b/example/src/extra-columns-helper.tsx new file mode 100644 index 000000000..c0ae6fac3 --- /dev/null +++ b/example/src/extra-columns-helper.tsx @@ -0,0 +1,172 @@ +import React from "react"; +import { Task, ExtraColumn } from "../../dist/types/public-types"; + +export function initTasksWithExtraColumns() { + const currentDate = new Date(); + const tasks: Task[] = [ + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15), + name: "Website Redesign", + id: "ProjectSample", + progress: 25, + type: "project", + hideChildren: false, + displayOrder: 1, + extraColumns: { + status: "In Progress", + assignee: "Project Team", + priority: "High", + budget: 50000, + }, + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1), + end: new Date( + currentDate.getFullYear(), + currentDate.getMonth(), + 2, + 12, + 28 + ), + name: "Initial Planning", + id: "Task 0", + progress: 45, + type: "task", + project: "ProjectSample", + displayOrder: 2, + extraColumns: { + status: "Completed", + assignee: "John Doe", + priority: "High", + budget: 5000, + }, + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 2), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 4, 0, 0), + name: "Market Research", + id: "Task 1", + progress: 25, + dependencies: ["Task 0"], + type: "task", + project: "ProjectSample", + displayOrder: 3, + extraColumns: { + status: "In Progress", + assignee: "Jane Smith", + priority: "Medium", + budget: 8000, + }, + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 4), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8, 0, 0), + name: "Design Mockups", + id: "Task 2", + progress: 10, + dependencies: ["Task 1"], + type: "task", + project: "ProjectSample", + displayOrder: 4, + extraColumns: { + status: "Not Started", + assignee: "Bob Wilson", + priority: "High", + budget: 12000, + }, + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 9, 0, 0), + name: "Client Review", + id: "Task 3", + progress: 0, + dependencies: ["Task 2"], + type: "milestone", + project: "ProjectSample", + displayOrder: 5, + extraColumns: { + status: "Pending", + assignee: "Client", + priority: "Critical", + budget: 0, + }, + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 10), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15), + name: "Development", + id: "Task 4", + progress: 0, + dependencies: ["Task 3"], + type: "task", + project: "ProjectSample", + displayOrder: 6, + extraColumns: { + status: "Not Started", + assignee: "Dev Team", + priority: "High", + budget: 25000, + }, + }, + ]; + return tasks; +} + +export const extraColumns: ExtraColumn[] = [ + { + key: "status", + title: "Status", + width: "120px", + render: (task) => { + const status = task.extraColumns?.status as string; + const statusClass = { + "Completed": "status-completed", + "In Progress": "status-in-progress", + "Not Started": "status-not-started", + "Pending": "status-pending" + }[status] || ""; + + return ( + + {status} + + ); + }, + }, + { + key: "assignee", + title: "Assignee", + width: "140px", + }, + { + key: "priority", + title: "Priority", + width: "100px", + render: (task) => { + const priority = task.extraColumns?.priority as string; + const priorityClass = { + "Critical": "priority-critical", + "High": "priority-high", + "Medium": "priority-medium", + "Low": "priority-low" + }[priority] || ""; + + return ( + + {priority} + + ); + }, + }, + { + key: "budget", + title: "Budget", + width: "100px", + render: (task) => { + const budget = task.extraColumns?.budget as number; + return budget > 0 ? `$${budget.toLocaleString()}` : "-"; + }, + }, +]; diff --git a/example/src/index.css b/example/src/index.css index 9fd1ea03d..8ddef1d31 100644 --- a/example/src/index.css +++ b/example/src/index.css @@ -80,3 +80,51 @@ input:checked + .Slider:before { -ms-transform: translateX(26px); transform: translateX(26px); } + +/* Extra columns styles */ +.status-badge, .priority-badge { + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + color: white; + text-align: center; + display: inline-block; + min-width: 60px; +} + +/* Status badge styles */ +.status-completed { + background-color: #28a745; +} + +.status-in-progress { + background-color: #007bff; +} + +.status-not-started { + background-color: #6c757d; +} + +.status-pending { + background-color: #ffc107; + color: #212529; +} + +/* Priority badge styles */ +.priority-critical { + background-color: #dc3545; +} + +.priority-high { + background-color: #fd7e14; +} + +.priority-medium { + background-color: #ffc107; + color: #212529; +} + +.priority-low { + background-color: #28a745; +} diff --git a/package-lock.json b/package-lock.json index aeca7dc81..46dbdfe5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gantt-task-react", - "version": "0.3.8", + "version": "0.3.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "gantt-task-react", - "version": "0.3.8", + "version": "0.3.9", "license": "MIT", "devDependencies": { "@testing-library/jest-dom": "^5.16.4", diff --git a/src/components/gantt/gantt.tsx b/src/components/gantt/gantt.tsx index b90483f3d..b3f3797b9 100644 --- a/src/components/gantt/gantt.tsx +++ b/src/components/gantt/gantt.tsx @@ -58,6 +58,7 @@ export const Gantt: React.FunctionComponent = ({ TooltipContent = StandardTooltipContent, TaskListHeader = TaskListHeaderDefault, TaskListTable = TaskListTableDefault, + extraColumns, onDateChange, onProgressChange, onDoubleClick, @@ -446,6 +447,7 @@ export const Gantt: React.FunctionComponent = ({ taskListRef, setSelectedTask: handleSelectedTask, onExpanderClick: handleExpanderClick, + extraColumns, TaskListHeader, TaskListTable, }; diff --git a/src/components/task-list/task-list-header.tsx b/src/components/task-list/task-list-header.tsx index 4e8cdb66b..4b98f780b 100644 --- a/src/components/task-list/task-list-header.tsx +++ b/src/components/task-list/task-list-header.tsx @@ -1,12 +1,14 @@ import React from "react"; import styles from "./task-list-header.module.css"; +import { ExtraColumn } from "../../types/public-types"; export const TaskListHeaderDefault: React.FC<{ headerHeight: number; rowWidth: string; fontFamily: string; fontSize: string; -}> = ({ headerHeight, fontFamily, fontSize, rowWidth }) => { + extraColumns?: ExtraColumn[]; +}> = ({ headerHeight, fontFamily, fontSize, rowWidth, extraColumns = [] }) => { return (
 To
+ {/* Render extra column headers */} + {extraColumns.map((column) => ( + +
+
+  {column.title} +
+ + ))}
); diff --git a/src/components/task-list/task-list-table.tsx b/src/components/task-list/task-list-table.tsx index b165f6002..c8ee94ed8 100644 --- a/src/components/task-list/task-list-table.tsx +++ b/src/components/task-list/task-list-table.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from "react"; import styles from "./task-list-table.module.css"; -import { Task } from "../../types/public-types"; +import { Task, ExtraColumn } from "../../types/public-types"; const localeDateStringCache = {}; const toLocaleDateStringFactory = @@ -31,6 +31,7 @@ export const TaskListTableDefault: React.FC<{ selectedTaskId: string; setSelectedTask: (taskId: string) => void; onExpanderClick: (task: Task) => void; + extraColumns?: ExtraColumn[]; }> = ({ rowHeight, rowWidth, @@ -39,6 +40,7 @@ export const TaskListTableDefault: React.FC<{ fontSize, locale, onExpanderClick, + extraColumns = [], }) => { const toLocaleDateString = useMemo( () => toLocaleDateStringFactory(locale), @@ -107,6 +109,23 @@ export const TaskListTableDefault: React.FC<{ >  {toLocaleDateString(t.end, dateTimeOptions)} + {/* Render extra column values */} + {extraColumns.map((column) => ( +
+  {column.render + ? column.render(t) + : t.extraColumns?.[column.key] || "" + } +
+ ))} ); })} diff --git a/src/components/task-list/task-list.tsx b/src/components/task-list/task-list.tsx index bbfed4365..b190f930e 100644 --- a/src/components/task-list/task-list.tsx +++ b/src/components/task-list/task-list.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef } from "react"; import { BarTask } from "../../types/bar-task"; -import { Task } from "../../types/public-types"; +import { Task, ExtraColumn } from "../../types/public-types"; export type TaskListProps = { headerHeight: number; @@ -17,11 +17,13 @@ export type TaskListProps = { selectedTask: BarTask | undefined; setSelectedTask: (task: string) => void; onExpanderClick: (task: Task) => void; + extraColumns?: ExtraColumn[]; TaskListHeader: React.FC<{ headerHeight: number; rowWidth: string; fontFamily: string; fontSize: string; + extraColumns?: ExtraColumn[]; }>; TaskListTable: React.FC<{ rowHeight: number; @@ -33,6 +35,7 @@ export type TaskListProps = { selectedTaskId: string; setSelectedTask: (taskId: string) => void; onExpanderClick: (task: Task) => void; + extraColumns?: ExtraColumn[]; }>; }; @@ -51,6 +54,7 @@ export const TaskList: React.FC = ({ ganttHeight, taskListRef, horizontalContainerClass, + extraColumns, TaskListHeader, TaskListTable, }) => { @@ -66,6 +70,7 @@ export const TaskList: React.FC = ({ fontFamily, fontSize, rowWidth, + extraColumns, }; const selectedTaskId = selectedTask ? selectedTask.id : ""; const tableProps = { @@ -78,6 +83,7 @@ export const TaskList: React.FC = ({ selectedTaskId: selectedTaskId, setSelectedTask, onExpanderClick, + extraColumns, }; return ( diff --git a/src/index.tsx b/src/index.tsx index 47e8b63a4..2f8a3955f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,6 +3,7 @@ export { ViewMode } from "./types/public-types"; export type { GanttProps, Task, + ExtraColumn, StylingOption, DisplayOption, EventOption, diff --git a/src/types/public-types.ts b/src/types/public-types.ts index cc44ff17c..657c27992 100644 --- a/src/types/public-types.ts +++ b/src/types/public-types.ts @@ -10,6 +10,14 @@ export enum ViewMode { Year = "Year", } export type TaskType = "task" | "milestone" | "project"; + +export interface ExtraColumn { + key: string; + title: string; + width?: string; + render?: (task: Task) => React.ReactNode; +} + export interface Task { id: string; type: TaskType; @@ -31,6 +39,10 @@ export interface Task { dependencies?: string[]; hideChildren?: boolean; displayOrder?: number; + /** + * Extra column data - key-value pairs for additional columns + */ + extraColumns?: { [key: string]: string | number }; } export interface EventOption { @@ -123,6 +135,7 @@ export interface StylingOption { rowWidth: string; fontFamily: string; fontSize: string; + extraColumns?: ExtraColumn[]; }>; TaskListTable?: React.FC<{ rowHeight: number; @@ -137,9 +150,14 @@ export interface StylingOption { */ setSelectedTask: (taskId: string) => void; onExpanderClick: (task: Task) => void; + extraColumns?: ExtraColumn[]; }>; } export interface GanttProps extends EventOption, DisplayOption, StylingOption { tasks: Task[]; + /** + * Extra columns to display in the task list + */ + extraColumns?: ExtraColumn[]; } From e9586a4a4bae4e987ccbe038d9d8f418ff11bb4d Mon Sep 17 00:00:00 2001 From: tim Date: Mon, 16 Jun 2025 13:42:32 +0000 Subject: [PATCH 2/5] feat: add support for customizable column widths and extra columns in Gantt chart --- README.md | 78 +++++++++++++++++++ example/src/ExtraColumnsApp.tsx | 3 + src/components/gantt/gantt.tsx | 6 ++ src/components/task-list/task-list-header.tsx | 20 ++++- src/components/task-list/task-list-table.tsx | 18 +++-- src/components/task-list/task-list.tsx | 18 +++++ src/types/public-types.ts | 18 +++++ 7 files changed, 151 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 9191d87ba..27438a59f 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,84 @@ npm start \*Required +## Extra Columns Support + +You can add custom columns to the task list by using the `extraColumns` prop: + +```javascript +import { Gantt, Task, ExtraColumn } from 'gantt-task-react'; + +// Define extra columns +const extraColumns: ExtraColumn[] = [ + { + key: "status", + title: "Status", + width: "100px", + }, + { + key: "assignee", + title: "Assignee", + width: "120px", + }, + { + key: "priority", + title: "Priority", + width: "80px", + render: (task) => ( + + {task.extraColumns?.priority} + + ), + }, +]; + +// Add extra data to your tasks +const tasks: Task[] = [ + { + id: "1", + name: "Task 1", + start: new Date(), + end: new Date(), + progress: 50, + type: "task", + extraColumns: { + status: "In Progress", + assignee: "John Doe", + priority: "High", + }, + }, +]; + + +``` + +### ExtraColumn Interface + +| Parameter Name | Type | Description | +| :------------- | :-------------------------------------- | :----------------------------------------------------------------------- | +| key\* | string | Unique key for the column, used to access data in task.extraColumns | +| title\* | string | Column header title | +| width | string | Column width (e.g., "100px", "120px"). Defaults to listCellWidth | +| render | `(task: Task) => React.ReactNode` | Optional custom render function for complex column content | + +### Column Width Configuration + +You can customize the width of the default columns: + +| Parameter Name | Type | Description | +| :---------------- | :----- | :----------------------------------------------- | +| nameColumnWidth | string | Width of the Name column (e.g., "200px") | +| fromColumnWidth | string | Width of the From/Start date column (e.g., "130px") | +| toColumnWidth | string | Width of the To/End date column (e.g., "130px") | + +\*Required + ## License [MIT](https://oss.ninja/mit/jaredpalmer/) diff --git a/example/src/ExtraColumnsApp.tsx b/example/src/ExtraColumnsApp.tsx index 579976679..1580f170b 100644 --- a/example/src/ExtraColumnsApp.tsx +++ b/example/src/ExtraColumnsApp.tsx @@ -33,6 +33,9 @@ const ExtraColumnsApp: React.FC = () => { tasks={tasks} viewMode={view} extraColumns={extraColumns} + nameColumnWidth="180px" + fromColumnWidth="120px" + toColumnWidth="120px" onDateChange={(task, _children) => { console.log("On date change Id:" + task.id); setTasks(tasks); diff --git a/src/components/gantt/gantt.tsx b/src/components/gantt/gantt.tsx index b3f3797b9..750066a04 100644 --- a/src/components/gantt/gantt.tsx +++ b/src/components/gantt/gantt.tsx @@ -59,6 +59,9 @@ export const Gantt: React.FunctionComponent = ({ TaskListHeader = TaskListHeaderDefault, TaskListTable = TaskListTableDefault, extraColumns, + nameColumnWidth, + fromColumnWidth, + toColumnWidth, onDateChange, onProgressChange, onDoubleClick, @@ -448,6 +451,9 @@ export const Gantt: React.FunctionComponent = ({ setSelectedTask: handleSelectedTask, onExpanderClick: handleExpanderClick, extraColumns, + nameColumnWidth, + fromColumnWidth, + toColumnWidth, TaskListHeader, TaskListTable, }; diff --git a/src/components/task-list/task-list-header.tsx b/src/components/task-list/task-list-header.tsx index 4b98f780b..f655d679e 100644 --- a/src/components/task-list/task-list-header.tsx +++ b/src/components/task-list/task-list-header.tsx @@ -8,7 +8,19 @@ export const TaskListHeaderDefault: React.FC<{ fontFamily: string; fontSize: string; extraColumns?: ExtraColumn[]; -}> = ({ headerHeight, fontFamily, fontSize, rowWidth, extraColumns = [] }) => { + nameColumnWidth?: string; + fromColumnWidth?: string; + toColumnWidth?: string; +}> = ({ + headerHeight, + fontFamily, + fontSize, + rowWidth, + extraColumns = [], + nameColumnWidth, + fromColumnWidth, + toColumnWidth +}) => { return (
 Name @@ -41,7 +53,7 @@ export const TaskListHeaderDefault: React.FC<{
 From @@ -56,7 +68,7 @@ export const TaskListHeaderDefault: React.FC<{
 To diff --git a/src/components/task-list/task-list-table.tsx b/src/components/task-list/task-list-table.tsx index c8ee94ed8..0b2244557 100644 --- a/src/components/task-list/task-list-table.tsx +++ b/src/components/task-list/task-list-table.tsx @@ -32,6 +32,9 @@ export const TaskListTableDefault: React.FC<{ setSelectedTask: (taskId: string) => void; onExpanderClick: (task: Task) => void; extraColumns?: ExtraColumn[]; + nameColumnWidth?: string; + fromColumnWidth?: string; + toColumnWidth?: string; }> = ({ rowHeight, rowWidth, @@ -41,6 +44,9 @@ export const TaskListTableDefault: React.FC<{ locale, onExpanderClick, extraColumns = [], + nameColumnWidth, + fromColumnWidth, + toColumnWidth, }) => { const toLocaleDateString = useMemo( () => toLocaleDateStringFactory(locale), @@ -72,8 +78,8 @@ export const TaskListTableDefault: React.FC<{
@@ -94,8 +100,8 @@ export const TaskListTableDefault: React.FC<{
 {toLocaleDateString(t.start, dateTimeOptions)} @@ -103,8 +109,8 @@ export const TaskListTableDefault: React.FC<{
 {toLocaleDateString(t.end, dateTimeOptions)} diff --git a/src/components/task-list/task-list.tsx b/src/components/task-list/task-list.tsx index b190f930e..595ed839a 100644 --- a/src/components/task-list/task-list.tsx +++ b/src/components/task-list/task-list.tsx @@ -18,12 +18,18 @@ export type TaskListProps = { setSelectedTask: (task: string) => void; onExpanderClick: (task: Task) => void; extraColumns?: ExtraColumn[]; + nameColumnWidth?: string; + fromColumnWidth?: string; + toColumnWidth?: string; TaskListHeader: React.FC<{ headerHeight: number; rowWidth: string; fontFamily: string; fontSize: string; extraColumns?: ExtraColumn[]; + nameColumnWidth?: string; + fromColumnWidth?: string; + toColumnWidth?: string; }>; TaskListTable: React.FC<{ rowHeight: number; @@ -36,6 +42,9 @@ export type TaskListProps = { setSelectedTask: (taskId: string) => void; onExpanderClick: (task: Task) => void; extraColumns?: ExtraColumn[]; + nameColumnWidth?: string; + fromColumnWidth?: string; + toColumnWidth?: string; }>; }; @@ -55,6 +64,9 @@ export const TaskList: React.FC = ({ taskListRef, horizontalContainerClass, extraColumns, + nameColumnWidth, + fromColumnWidth, + toColumnWidth, TaskListHeader, TaskListTable, }) => { @@ -71,6 +83,9 @@ export const TaskList: React.FC = ({ fontSize, rowWidth, extraColumns, + nameColumnWidth, + fromColumnWidth, + toColumnWidth, }; const selectedTaskId = selectedTask ? selectedTask.id : ""; const tableProps = { @@ -84,6 +99,9 @@ export const TaskList: React.FC = ({ setSelectedTask, onExpanderClick, extraColumns, + nameColumnWidth, + fromColumnWidth, + toColumnWidth, }; return ( diff --git a/src/types/public-types.ts b/src/types/public-types.ts index 657c27992..a3ec5bf8e 100644 --- a/src/types/public-types.ts +++ b/src/types/public-types.ts @@ -136,6 +136,9 @@ export interface StylingOption { fontFamily: string; fontSize: string; extraColumns?: ExtraColumn[]; + nameColumnWidth?: string; + fromColumnWidth?: string; + toColumnWidth?: string; }>; TaskListTable?: React.FC<{ rowHeight: number; @@ -151,6 +154,9 @@ export interface StylingOption { setSelectedTask: (taskId: string) => void; onExpanderClick: (task: Task) => void; extraColumns?: ExtraColumn[]; + nameColumnWidth?: string; + fromColumnWidth?: string; + toColumnWidth?: string; }>; } @@ -160,4 +166,16 @@ export interface GanttProps extends EventOption, DisplayOption, StylingOption { * Extra columns to display in the task list */ extraColumns?: ExtraColumn[]; + /** + * Width of the Name column + */ + nameColumnWidth?: string; + /** + * Width of the From column + */ + fromColumnWidth?: string; + /** + * Width of the To column + */ + toColumnWidth?: string; } From 939ca2ae46e97419fed5fb4aab02b90e7448b698 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 17 Jun 2025 07:38:50 +0000 Subject: [PATCH 3/5] feat: add extra columns support and configurable column widths - Add ExtraColumn interface for custom columns - Add nameColumnWidth, fromColumnWidth, toColumnWidth props - Update TaskListHeader and TaskListTable components - Add example demonstrating extra columns - Create CONTRIBUTING.md and PUBLISHING.md documentation - Update package.json with new repository info --- CONTRIBUTING.md | 72 ++++++++++++++++++++++++ PUBLISHING.md | 146 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 6 ++ package.json | 21 ++++--- 4 files changed, 238 insertions(+), 7 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 PUBLISHING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..2801aaeac --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing to @scharinger/gantt-task-react + +Thank you for your interest in contributing! This guide will help you set up the development environment. + +## Development Setup + +### Prerequisites +- Node.js (v14 or higher) +- npm + +### Getting Started + +```bash +# Clone the repository +git clone https://github.com/yourusername/gantt-task-react.git +cd gantt-task-react + +# Install dependencies +npm install + +# Build the library +npm run build + +# Run tests +npm test +``` + +### Running the Example + +To test your changes with the example application: + +```bash +cd example +npm install +npm start +``` + +The example will be available at `http://localhost:3000`. + +## Development Workflow + +1. **Make your changes** in the `src/` directory +2. **Build the library** with `npm run build` +3. **Test your changes** using the example application +4. **Run tests** with `npm test` +5. **Update documentation** if needed + +## Code Guidelines + +- Follow TypeScript best practices +- Maintain existing code style +- Add JSDoc comments for public APIs +- Include tests for new features +- Update documentation for new features + +## Submitting Changes + +1. Fork the repository +2. Create a feature branch: `git checkout -b feature/your-feature-name` +3. Make your changes +4. Test your changes +5. Commit with descriptive messages +6. Push to your fork +7. Create a Pull Request + +## Publishing + +For maintainers: See [PUBLISHING.md](PUBLISHING.md) for instructions on publishing new versions. + +## Questions? + +If you have questions about contributing, please open an issue on GitHub. diff --git a/PUBLISHING.md b/PUBLISHING.md new file mode 100644 index 000000000..541ffb9c0 --- /dev/null +++ b/PUBLISHING.md @@ -0,0 +1,146 @@ +# Publishing Guide for @scharinger/gantt-task-react + +This guide covers the process of publishing new versions of the package to npm. + +## Version Strategy + +We follow [Semantic Versioning](https://semver.org/): + +- **Patch (0.4.X)**: Bug fixes and small improvements +- **Minor (0.X.0)**: New features, backwards compatible +- **Major (X.0.0)**: Breaking changes +- **Beta (X.Y.Z-beta.N)**: Pre-release versions for testing + +## Prerequisites + +1. **npm account**: Make sure you're logged in to npm + ```bash + npm whoami # Check if logged in + npm login # Login if needed + ``` + +2. **Repository access**: You should have write access to the repository + +3. **Clean working directory**: Commit all changes before publishing + +## Publishing Steps + +### 1. Update Version + +Update the version in `package.json`: + +```json +{ + "version": "0.4.1-beta.0" // For beta releases + // or + "version": "0.4.1" // For stable releases +} +``` + +### 2. Update Documentation + +- Update `README.md` if there are new features or API changes +- Update `CHANGELOG.md` (if it exists) with the changes + +### 3. Build and Test + +```bash +# Build the library +npm run build + +# Run tests +npm test + +# Test with example project +cd example +npm install +npm start +``` + +### 4. Publish to NPM + +**For beta releases:** +```bash +npm publish --tag beta --access public +``` + +**For stable releases:** +```bash +npm publish --access public +``` + +### 5. Create Git Tag (Recommended) + +```bash +git tag v0.4.1-beta.0 +git push origin v0.4.1-beta.0 +``` + +### 6. Update GitHub Release (Optional) + +Create a release on GitHub with: +- Tag version +- Release notes describing changes +- Any breaking changes or migration notes + +## Post-Publishing + +### Verify Publication + +Check that the package is available: + +```bash +npm view @scharinger/gantt-task-react versions --json +``` + +### Test Installation + +Test in a fresh project: + +```bash +mkdir test-install +cd test-install +npm init -y +npm install @scharinger/gantt-task-react@beta # or @latest +``` + +## Installation Commands for Users + +After publishing, users can install with: + +```bash +# Latest stable version +npm install @scharinger/gantt-task-react + +# Beta version +npm install @scharinger/gantt-task-react@beta + +# Specific version +npm install @scharinger/gantt-task-react@0.4.0-beta.0 +``` + +## Troubleshooting + +### Common Issues + +1. **402 Payment Required**: Use `--access public` for scoped packages +2. **403 Forbidden**: Check npm login and package permissions +3. **Version already exists**: Increment version number + +### Rollback + +If you need to unpublish (only for packages published less than 72 hours ago): + +```bash +npm unpublish @scharinger/gantt-task-react@0.4.1-beta.0 +``` + +**Note**: Unpublishing is discouraged and may not be possible for stable releases. + +## Automation (Future) + +Consider setting up GitHub Actions for automated publishing: + +- Automated testing on PR +- Automated beta releases on merge to develop +- Automated stable releases on merge to main diff --git a/README.md b/README.md index 27438a59f..61f7a2d4b 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,12 @@ You can customize the width of the default columns: \*Required +## Contributing + +For development setup and contributing guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md). + +For maintainers: Publishing instructions are in [PUBLISHING.md](PUBLISHING.md). + ## License [MIT](https://oss.ninja/mit/jaredpalmer/) diff --git a/package.json b/package.json index a0097baa7..5b2d018a4 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,23 @@ { - "name": "gantt-task-react", - "version": "0.3.9", - "description": "Interactive Gantt Chart for React with TypeScript.", - "author": "MaTeMaTuK ", - "homepage": "https://github.com/MaTeMaTuK/gantt-task-react", + "name": "@scharinger/gantt-task-react", + "version": "0.4.0-beta.0", + "description": "Interactive Gantt Chart for React with TypeScript and Extra Columns Support. Based on gantt-task-react by MaTeMaTuK.", + "author": "scharinger ", + "homepage": "https://github.com/scharinger/gantt-task-react", "license": "MIT", - "repository": "MaTeMaTuK/gantt-task-react", + "repository": { + "type": "git", + "url": "git+https://github.com/scharinger/gantt-task-react.git" + }, "main": "dist/index.js", "module": "dist/index.modern.js", "source": "src/index.tsx", "engines": { "node": ">=10" }, + "contributors": [ + "MaTeMaTuK (original author)" + ], "keywords": [ "react", "gantt", @@ -21,7 +27,8 @@ "gantt-chart", "gantt chart", "react-gantt", - "task" + "task", + "configurable-columns" ], "scripts": { "build": "microbundle-crl --no-compress --format modern,cjs", From f69561053f4ea7b82a1e49c0d24e5c6e7425fb48 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 17 Jun 2025 10:19:53 +0000 Subject: [PATCH 4/5] feat: add extra columns and configurable column widths with ISO 8601 date format - Add ExtraColumn interface for custom columns with optional render functions - Add nameColumnWidth, fromColumnWidth, toColumnWidth props for configurable column widths - Add DateFormat type with 'locale' and 'iso8601' options for date formatting - Add extraColumns prop to Task interface for storing additional column data - Update TaskListHeader and TaskListTable components to support extra columns - Add date format selector in example with ISO 8601 (YYYY-MM-DD) support - Export new DateFormat type from main index BREAKING CHANGE: TaskListHeader and TaskListTable component props have been extended with new optional parameters --- README.md | 25 ++++++++++++++- example/src/ExtraColumnsApp.tsx | 33 ++++++++++++++++---- src/components/gantt/gantt.tsx | 2 ++ src/components/task-list/task-list-table.tsx | 22 ++++++++++--- src/components/task-list/task-list.tsx | 6 +++- src/index.tsx | 1 + src/types/public-types.ts | 7 +++++ 7 files changed, 84 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 61f7a2d4b..e1b77fcf8 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ npm install gantt-task-react ## How to use it ```javascript -import { Gantt, Task, EventOption, StylingOption, ViewMode, DisplayOption } from 'gantt-task-react'; +import { Gantt, Task, ExtraColumn, DateFormat, EventOption, StylingOption, ViewMode, DisplayOption } from 'gantt-task-react'; import "gantt-task-react/dist/index.css"; let tasks: Task[] = [ @@ -40,6 +40,7 @@ You may handle actions ``` @@ -220,6 +222,27 @@ You can customize the width of the default columns: | fromColumnWidth | string | Width of the From/Start date column (e.g., "130px") | | toColumnWidth | string | Width of the To/End date column (e.g., "130px") | +### Date Format Configuration + +You can choose how dates are displayed in the From and To columns: + +```javascript +import { Gantt, DateFormat } from 'gantt-task-react'; + + +``` + +| Parameter Name | Type | Description | +| :------------- | :--------- | :------------------------------------------------------------------- | +| dateFormat | DateFormat | Date display format. "locale" uses locale formatting (e.g., "Fri, June 15, 2025"), "iso8601" uses ISO 8601 format (YYYY-MM-DD) | + +**DateFormat Options:** +- `"locale"` (default): Displays dates in locale-specific format (e.g., "Fri, June 15, 2025") +- `"iso8601"`: Displays dates in ISO 8601 format (e.g., "2025-06-15") + \*Required ## Contributing diff --git a/example/src/ExtraColumnsApp.tsx b/example/src/ExtraColumnsApp.tsx index 1580f170b..562ff85b3 100644 --- a/example/src/ExtraColumnsApp.tsx +++ b/example/src/ExtraColumnsApp.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { ViewMode, Gantt } from "gantt-task-react"; +import { ViewMode, Gantt, DateFormat } from "gantt-task-react"; import { ViewSwitcher } from "./components/view-switcher"; import { initTasksWithExtraColumns, extraColumns } from "./extra-columns-helper"; import "gantt-task-react/dist/index.css"; @@ -8,6 +8,8 @@ import "gantt-task-react/dist/index.css"; const ExtraColumnsApp: React.FC = () => { const [tasks, setTasks] = useState(initTasksWithExtraColumns()); const [view, setView] = useState(ViewMode.Day); + const [dateFormat, setDateFormat] = useState("locale"); + let columnWidth = 65; if (view === ViewMode.Year) { columnWidth = 350; @@ -22,11 +24,25 @@ const ExtraColumnsApp: React.FC = () => {

Gantt Chart with Extra Columns Example

This example demonstrates how to add custom columns to the Gantt chart task list.

- setView(viewMode)} - onViewListChange={() => {}} - isChecked={true} - /> +
+ setView(viewMode)} + onViewListChange={() => {}} + isChecked={true} + /> + +
+ + +
+
{ nameColumnWidth="180px" fromColumnWidth="120px" toColumnWidth="120px" + dateFormat={dateFormat} onDateChange={(task, _children) => { console.log("On date change Id:" + task.id); setTasks(tasks); @@ -69,6 +86,8 @@ const ExtraColumnsApp: React.FC = () => {
  • Assignee Column: Displays who is responsible for each task
  • Priority Column: Shows task priority with colored indicators
  • Budget Column: Displays formatted budget amounts
  • +
  • Custom Column Widths: Name, From, and To columns have custom widths
  • +
  • Date Format Options: Toggle between locale and ISO (YYYY-MM-DD) date formats
  • How to Use:

    @@ -77,6 +96,8 @@ const ExtraColumnsApp: React.FC = () => {
  • Add extraColumns data to your task objects
  • Pass the columns configuration to the Gantt component
  • Optionally use custom render functions for complex column content
  • +
  • Set custom widths using nameColumnWidth, fromColumnWidth, toColumnWidth
  • +
  • Choose date format with dateFormat prop: "locale" or "iso"
  • diff --git a/src/components/gantt/gantt.tsx b/src/components/gantt/gantt.tsx index 750066a04..d5f2ce816 100644 --- a/src/components/gantt/gantt.tsx +++ b/src/components/gantt/gantt.tsx @@ -62,6 +62,7 @@ export const Gantt: React.FunctionComponent = ({ nameColumnWidth, fromColumnWidth, toColumnWidth, + dateFormat = "locale", onDateChange, onProgressChange, onDoubleClick, @@ -454,6 +455,7 @@ export const Gantt: React.FunctionComponent = ({ nameColumnWidth, fromColumnWidth, toColumnWidth, + dateFormat, TaskListHeader, TaskListTable, }; diff --git a/src/components/task-list/task-list-table.tsx b/src/components/task-list/task-list-table.tsx index 0b2244557..f3328c61b 100644 --- a/src/components/task-list/task-list-table.tsx +++ b/src/components/task-list/task-list-table.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from "react"; import styles from "./task-list-table.module.css"; -import { Task, ExtraColumn } from "../../types/public-types"; +import { Task, ExtraColumn, DateFormat } from "../../types/public-types"; const localeDateStringCache = {}; const toLocaleDateStringFactory = @@ -14,6 +14,11 @@ const toLocaleDateStringFactory = } return lds; }; + +const toISODateString = (date: Date): string => { + return date.toISOString().split("T")[0]; // Returns yyyy-MM-dd format +}; + const dateTimeOptions: Intl.DateTimeFormatOptions = { weekday: "short", year: "numeric", @@ -35,6 +40,7 @@ export const TaskListTableDefault: React.FC<{ nameColumnWidth?: string; fromColumnWidth?: string; toColumnWidth?: string; + dateFormat?: DateFormat; }> = ({ rowHeight, rowWidth, @@ -47,12 +53,20 @@ export const TaskListTableDefault: React.FC<{ nameColumnWidth, fromColumnWidth, toColumnWidth, + dateFormat = "locale", }) => { const toLocaleDateString = useMemo( () => toLocaleDateStringFactory(locale), [locale] ); + const formatDate = useMemo(() => { + if (dateFormat === "iso8601") { + return toISODateString; + } + return (date: Date) => toLocaleDateString(date, dateTimeOptions); + }, [dateFormat, toLocaleDateString]); + return (
    -  {toLocaleDateString(t.start, dateTimeOptions)} +  {formatDate(t.start)}
    -  {toLocaleDateString(t.end, dateTimeOptions)} +  {formatDate(t.end)}
    {/* Render extra column values */} {extraColumns.map((column) => ( @@ -126,7 +140,7 @@ export const TaskListTableDefault: React.FC<{ }} title={column.render ? undefined : String(t.extraColumns?.[column.key] || "")} > -  {column.render + {column.render ? column.render(t) : t.extraColumns?.[column.key] || "" } diff --git a/src/components/task-list/task-list.tsx b/src/components/task-list/task-list.tsx index 595ed839a..5c78d65d2 100644 --- a/src/components/task-list/task-list.tsx +++ b/src/components/task-list/task-list.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef } from "react"; import { BarTask } from "../../types/bar-task"; -import { Task, ExtraColumn } from "../../types/public-types"; +import { Task, ExtraColumn, DateFormat } from "../../types/public-types"; export type TaskListProps = { headerHeight: number; @@ -21,6 +21,7 @@ export type TaskListProps = { nameColumnWidth?: string; fromColumnWidth?: string; toColumnWidth?: string; + dateFormat?: DateFormat; TaskListHeader: React.FC<{ headerHeight: number; rowWidth: string; @@ -45,6 +46,7 @@ export type TaskListProps = { nameColumnWidth?: string; fromColumnWidth?: string; toColumnWidth?: string; + dateFormat?: DateFormat; }>; }; @@ -67,6 +69,7 @@ export const TaskList: React.FC = ({ nameColumnWidth, fromColumnWidth, toColumnWidth, + dateFormat, TaskListHeader, TaskListTable, }) => { @@ -102,6 +105,7 @@ export const TaskList: React.FC = ({ nameColumnWidth, fromColumnWidth, toColumnWidth, + dateFormat, }; return ( diff --git a/src/index.tsx b/src/index.tsx index 2f8a3955f..3899ab394 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,6 +4,7 @@ export type { GanttProps, Task, ExtraColumn, + DateFormat, StylingOption, DisplayOption, EventOption, diff --git a/src/types/public-types.ts b/src/types/public-types.ts index a3ec5bf8e..883a888b5 100644 --- a/src/types/public-types.ts +++ b/src/types/public-types.ts @@ -11,6 +11,8 @@ export enum ViewMode { } export type TaskType = "task" | "milestone" | "project"; +export type DateFormat = "locale" | "iso8601"; + export interface ExtraColumn { key: string; title: string; @@ -157,6 +159,7 @@ export interface StylingOption { nameColumnWidth?: string; fromColumnWidth?: string; toColumnWidth?: string; + dateFormat?: DateFormat; }>; } @@ -178,4 +181,8 @@ export interface GanttProps extends EventOption, DisplayOption, StylingOption { * Width of the To column */ toColumnWidth?: string; + /** + * Date format for start and end dates. "locale" uses locale formatting, "iso8601" uses yyyy-MM-dd format + */ + dateFormat?: DateFormat; } From 359e2a6600e80a7c2f53c1026b00fd7b27427944 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 17 Jun 2025 10:51:45 +0000 Subject: [PATCH 5/5] chore: bump version to 0.4.0-beta.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b2d018a4..0d13e3504 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scharinger/gantt-task-react", - "version": "0.4.0-beta.0", + "version": "0.4.0-beta.2", "description": "Interactive Gantt Chart for React with TypeScript and Extra Columns Support. Based on gantt-task-react by MaTeMaTuK.", "author": "scharinger ", "homepage": "https://github.com/scharinger/gantt-task-react",