@gorkemyildiz/react-gantt-chart
v1.0.11
Published
`@gorkemyildiz/react-gantt-chart` is a flexible and customizable Gantt chart component built with React, TypeScript, and Tailwind CSS. It's designed to be easily integrated into modern web applications for visualizing project timelines, tasks, and epics.
Downloads
29
Readme
@gorkemyildiz/react-gantt-chart
@gorkemyildiz/react-gantt-chart is a flexible and customizable Gantt chart component built with React, TypeScript, and Tailwind CSS. It's designed to be easily integrated into modern web applications for visualizing project timelines, tasks, and epics.
Features
- Interactive Timeline: Drag and resize tasks and epics directly on the chart.
- Multiple View Modes: View the timeline in Quarters, Months, or Weeks.
- Zoom Functionality: Zoom in and out for different levels of detail, adapting the display of time units.
- Customizable Date Range: Select start and end years for the timeline display.
- Epic and Task Management: Supports nested tasks within epics and standalone tasks.
- Expand/Collapse Epics: Easily manage visibility of tasks within epics in the sidebar.
- Drag & Drop Reordering: Reorder epics and tasks within the sidebar, and move tasks between epics or to standalone.
- Progress Tracking: Visualize task and epic progress with progress bars.
- Theming: Extensive customization options for light and dark modes using
customColors(for non-border styles) andcustomBorderRadius(for border radius styles) props. - Internationalization (i18n): Support for multiple languages via the
languageandtranslationsprops. - Customizable Controls: Show or hide specific timeline controls (zoom, year range, view mode selector, jump to start/today) and add an optional custom button with its own icon, title (localizable), and action.
- Responsive Day Widths: Adjust the width of days in different views (
weeks,months,quarters) using thedayWidthsprop. - Tooltip Control: Globally enable or disable tooltips for task/epic bars using
showTooltips. - Flexible Month Cell Formatting: Customize how months are displayed in the Quarter view's sub-header (
short,long,numeric) viamonthCellFormatInQuarterView. - Customizable Sidebar Elements: Provide custom React Node renderers for task status badges (
customSidebarTaskStatusBadge), epic dots (customSidebarEpicDotRenderer), task dots (customSidebarTaskDotRenderer), and custom components after badges for epics (customSidebarEpicBadgeSuffix) and tasks (customSidebarTaskBadgeSuffix) in the sidebar. Sidebar item hover and expanded states can also be themed viacustomColors. - Initial Scroll Behavior: Configure the timeline to initially scroll to the first task/epic or to the current date using
initialScrollBehavior. - Scroll to Specific Item: Programmatically scroll the Gantt chart to a specific task or epic using the
onScrollToItemInGanttcallback or by clicking the target icon in the sidebar. - Editable Mode: Control whether the timeline is interactive or read-only using the
editableprop. - Item Click Handlers: Callbacks
onTaskClickandonEpicClickare triggered when a task or epic bar is clicked in the Gantt chart area, respectively. These work even ifeditableisfalse.
Installation
To install the library in your project:
npm install @gorkemyildiz/react-gantt-chart
# or
yarn add @gorkemyildiz/react-gantt-chartVite & Tailwind CSS Configuration
To ensure Tailwind CSS processes the classes used within the library, you need to include its path in your tailwind.config.js content array:
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
darkMode: "class", // Or 'media' if you prefer
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}',
// Add the following line:
'./node_modules/@gorkemyildiz/react-gantt-chart/dist/**/*.js',
],
theme: {
extend: {},
},
plugins: [],
};Basic Usage
Import the Timeline component and the useTimelineData hook. The hook helps manage the state of your Gantt data.
import React, { useState } from 'react';
import {
Timeline,
useTimelineData,
type Epic, // Type for epic data
type Task, // Type for task data
type CustomColors, // For theming
// ... other types and props as needed
} from '@gorkemyildiz/react-gantt-chart';
const initialEpicsData: Epic[] = [
{
id: 'EPIC-1',
type: 'epic',
title: 'Project Alpha Development',
startDate: new Date('2024-01-15'),
endDate: new Date('2024-04-30'),
tasks: [
{
id: 'TASK-1.1',
type: 'task',
title: 'Requirement Analysis',
startDate: new Date('2024-01-15'),
endDate: new Date('2024-01-31'),
epicId: 'EPIC-1',
progress: 100,
status: 'completed',
},
{
id: 'TASK-1.2',
type: 'task',
title: 'UI/UX Design',
startDate: new Date('2024-02-01'),
endDate: new Date('2024-02-28'),
epicId: 'EPIC-1',
progress: 75,
status: 'in-progress',
},
],
isExpanded: true,
},
];
const initialStandaloneTasksData: Task[] = [
{
id: 'TASK-S1',
type: 'task',
title: 'Project Kick-off Meeting',
startDate: new Date('2024-01-10'),
endDate: new Date('2024-01-10'),
epicId: null,
progress: 100,
status: 'completed',
},
];
function MyApp() {
const [selectedStartYear, setSelectedStartYear] = useState(new Date().getFullYear());
const [selectedEndYear, setSelectedEndYear] = useState(new Date().getFullYear() + 1);
const [isDarkMode, setIsDarkMode] = useState(false);
const [isEditable, setIsEditable] = useState(true);
const {
epics,
standaloneTasks,
items, // Flattened list of epics and tasks for rendering
updateTask,
updateEpic,
moveTask,
handleToggleExpand,
handleReorderEpics
} = useTimelineData({
initialEpics: initialEpicsData,
initialStandaloneTasks: initialStandaloneTasksData,
onEpicsChange: (updatedEpics) => console.log('Epics changed in app:', updatedEpics),
onStandaloneTasksChange: (updatedTasks) => console.log('Standalone tasks changed in app:', updatedTasks),
});
const handleYearRangeChange = (start: number, end: number) => {
setSelectedStartYear(start);
setSelectedEndYear(end);
};
const handleTaskItemClick = (task: Task) => {
console.log('Task clicked in App:', task);
alert(`Task Clicked: ${task.title}`);
};
const handleEpicItemClick = (epic: Epic) => {
console.log('Epic clicked in App:', epic);
alert(`Epic Clicked: ${epic.title}`);
};
return (
<div className={`app-container ${isDarkMode ? 'dark' : ''}`} style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
<div className="controls p-4">
<button onClick={() => setIsDarkMode(!isDarkMode)}>
Toggle {isDarkMode ? 'Light' : 'Dark'} Mode
</button>
<button onClick={() => setIsEditable(!isEditable)} style={{ marginLeft: '10px' }}>
{isEditable ? 'Make Read-only' : 'Make Editable'}
</button>
{/* Add other controls for year range, language, etc. here */}
</div>
<div style={{ flexGrow: 1, overflow: 'hidden' }}>
<Timeline
epics={epics}
standaloneTasks={standaloneTasks}
items={items}
selectedStartYear={selectedStartYear}
selectedEndYear={selectedEndYear}
onUpdateTask={updateTask}
onUpdateEpic={updateEpic}
onMoveTask={moveTask}
onToggleExpand={handleToggleExpand}
onReorderEpics={handleReorderEpics}
onYearRangeChange={handleYearRangeChange}
isDarkMode={isDarkMode}
editable={isEditable}
onTaskClick={handleTaskItemClick}
onEpicClick={handleEpicItemClick}
// Other props like customColors, language, initialScrollBehavior, etc.
/>
</div>
</div>
);
}
export default MyApp;Timeline Component Props
The Timeline component is highly customizable through its props. Below is a summary table followed by detailed explanations for key props.
| Prop | Type | Default | Description |
| ---------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| epics | Epic[] | [] | Array of epic objects. Each epic can contain tasks. |
| standaloneTasks | Task[] | [] | Array of task objects that do not belong to any epic. |
| items | TimelineItem[] | [] | A flattened list of all items (epics, tasks, section headers) to be rendered. Typically derived from epics and standaloneTasks via useTimelineData. |
| selectedStartYear | number | Current Year | The starting year for the timeline view. |
| selectedEndYear | number | Current Year + 1 | The ending year for the timeline view. |
| onUpdateTask | (taskId: string, updates: Partial<Task>) => void | () => {} | Callback when a task is updated (e.g., dragged, resized). |
| onUpdateEpic | (epicId: string, updates: Partial<Epic>) => void | () => {} | Callback when an epic is updated (e.g., dragged, resized). |
| onMoveTask | (taskId: string, targetEpicId: string \| null, targetIndex?: number) => void | () => {} | Callback when a task is moved (e.g., to another epic, to standalone, or reordered). |
| onToggleExpand | (epicId: string) => void | () => {} | Callback when an epic's expansion state is toggled. |
| onReorderEpics | (sourceIndex: number, targetIndex: number) => void | () => {} | Callback when epics are reordered in the sidebar. |
| onYearRangeChange | (start: number, end: number) => void | () => {} | Callback when the year range is changed via controls. |
| onScrollToItemInGantt | (itemId: string, itemType: 'task' \| 'epic') => void (Optional) | undefined | Custom handler for scrolling to an item in the Gantt area. If not provided, a default scroll behavior is used. |
| onTaskClick | (task: Task) => void (Optional) | undefined | Callback triggered when a task bar is clicked in the Gantt chart area. Provides the clicked Task object. Works even if editable is false. |
| onEpicClick | (epic: Epic) => void (Optional) | undefined | Callback triggered when an epic bar is clicked in the Gantt chart area. Provides the clicked Epic object. Works even if editable is false. |
| customColors | CustomColors (Optional) | undefined | Object to define custom non-border color themes for light/dark modes. |
| customBorderRadius | CustomBorderRadius (Optional) | undefined | Object to define custom border-radius styles for various components. |
| isDarkMode | boolean (Optional) | false | Toggles dark mode for the timeline. |
| showProgressBars | boolean (Optional) | true | Whether to show progress bars on task and epic visuals. |
| language | string (Optional) | 'en' | Current language code (e.g., 'en', 'tr', 'fr') for i18n. |
| translations | Translations (Optional) | undefined (uses defaults) | Object containing translation strings for different languages. |
| defaultViewMode | ViewMode (Optional) | 'quarters' | Initial view mode ('quarters', 'months', 'weeks'). |
| timelineControlsConfig | TimelineControlsConfig (Optional) | {} (all visible) | Object to configure visibility of individual timeline controls and an optional custom button. |
| monthCellFormatInQuarterView | MonthCellFormat (Optional) | 'short' | How months are displayed in the Quarter view's sub-header ('short', 'long', 'numeric'). |
| showTooltips | boolean (Optional) | true | Globally enable/disable tooltips for task/epic bars. |
| dayWidths | DayWidths (Optional) | undefined (uses defaults) | Object to specify custom widths (px) for days in different views. |
| initialScrollBehavior | 'task' \| 'today' (Optional) | 'task' | Initial horizontal scroll position: 'task' (to first item) or 'today' (to current date). |
| customSidebarTaskStatusBadge | (task: Task, isDarkMode?: boolean) => React.ReactNode (Optional) | undefined (uses default) | Custom renderer for task status badges in the sidebar. |
| customSidebarEpicDotRenderer | (epic: Epic, isDarkMode?: boolean) => React.ReactNode (Optional) | undefined (uses default) | Custom renderer for epic dot indicators in the sidebar. |
| customSidebarTaskDotRenderer | (task: Task, isDarkMode?: boolean) => React.ReactNode (Optional) | undefined (uses default) | Custom renderer for task dot indicators in the sidebar. |
| customSidebarEpicBadgeSuffix | (epic: Epic, isDarkMode?: boolean) => React.ReactNode (Optional) | undefined | Custom renderer for a component to display after the epic's progress/badge in the sidebar. |
| customSidebarTaskBadgeSuffix | (task: Task, isDarkMode?: boolean) => React.ReactNode (Optional) | undefined | Custom renderer for a component to display after the task's status badge/progress in the sidebar. |
| editable | boolean (Optional) | true | If false, disables all interactions (drag, resize, click on controls) and sets a cursor-not-allowed style. Click events on bars (onTaskClick, onEpicClick) still fire. |
Detailed Prop Explanations
Data Props
epics: Epic[]- An array of
Epicobjects. Each epic defines a larger body of work and can contain an array ofTaskobjects. - See
Epictype definition below.
- An array of
standaloneTasks: Task[]- An array of
Taskobjects that do not belong to any epic. - See
Tasktype definition below.
- An array of
items: TimelineItem[]- This is a crucial prop for rendering the sidebar and Gantt chart rows. It's a flattened, ordered list derived from
epicsandstandaloneTasks. - It's typically provided by the
useTimelineDatahook. - Each object in the array is a
TimelineItem, which includestype('epic', 'task', or 'section'),data(the actual Epic, Task, or Section object), andlevel(for indentation in the sidebar).
- This is a crucial prop for rendering the sidebar and Gantt chart rows. It's a flattened, ordered list derived from
selectedStartYear: numberandselectedEndYear: number- These numbers define the overall year range visible in the timeline. The timeline will display from the beginning of
selectedStartYearto the end ofselectedEndYear. - Users can change this range using the year selector controls if enabled.
- These numbers define the overall year range visible in the timeline. The timeline will display from the beginning of
Event Handlers
These callback props allow you to react to user interactions and data changes within the timeline. You should use these to update your application's state or persist changes to a backend.
onUpdateTask: (taskId: string, updates: Partial<Task>) => void- Triggered when a task is modified (e.g., dragged to a new date, resized, or its properties are changed programmatically if you extend functionality).
taskId: The ID of the task that was updated.updates: An object containing the properties of the task that changed (e.g.,{ startDate, endDate }).
onUpdateEpic: (epicId: string, updates: Partial<Epic>) => void- Similar to
onUpdateTask, but for epics.
- Similar to
onMoveTask: (taskId: string, targetEpicId: string \| null, targetIndex?: number) => void- Called when a task is moved via drag-and-drop in the sidebar.
taskId: ID of the task being moved.targetEpicId: ID of the epic the task is dropped into.nullif dropped into the standalone tasks section.targetIndex(Optional): The desired index within the target epic's tasks list or the standalone tasks list.
onToggleExpand: (epicId: string) => void- Fires when an epic's expand/collapse toggle is clicked in the sidebar.
onReorderEpics: (sourceIndex: number, targetIndex: number) => void- Called when an epic is reordered via drag-and-drop in the sidebar.
sourceIndex: The original index of the epic.targetIndex: The new index where the epic should be placed.
onYearRangeChange: (start: number, end: number) => void- Triggered when the user changes the start or end year using the timeline controls.
onScrollToItemInGantt?: (itemId: string, itemType: 'task' \| 'epic') => void- An optional callback that, if provided, overrides the default behavior when a user clicks the "target" icon next to an item in the sidebar or when
initialScrollBehavioris set. - Use this if you need custom logic to scroll or highlight items in the Gantt chart area, perhaps in conjunction with other page elements.
- An optional callback that, if provided, overrides the default behavior when a user clicks the "target" icon next to an item in the sidebar or when
onTaskClick?: (task: Task) => void- Callback triggered when a task bar is clicked in the Gantt chart area. It receives the full
Taskobject that was clicked. - This event fires regardless of the
editableprop's state.
- Callback triggered when a task bar is clicked in the Gantt chart area. It receives the full
onEpicClick?: (epic: Epic) => void- Callback triggered when an epic bar is clicked in the Gantt chart area. It receives the full
Epicobject that was clicked. - This event fires regardless of the
editableprop's state.
- Callback triggered when an epic bar is clicked in the Gantt chart area. It receives the full
Appearance & Theming
customColors?: CustomColors- Provides extensive control over the color scheme.
- Structure:
interface CustomColors { light?: ComponentThemes; dark?: ComponentThemes; } // Defines colors for major components interface ComponentThemes { timelineWrapper?: ElementThemeColors; sidebar?: ElementThemeColors; sidebarHeaderEpics?: ElementThemeColors; // Can include badgeBackground, badgeText, badgeBorder sidebarHeaderTasks?: ElementThemeColors; // Can include badgeBackground, badgeText, badgeBorder sidebarItemEpic?: ElementThemeColors; // Can include hoverBackground, hoverText, expandedBackground, expandedText sidebarItemTask?: ElementThemeColors; // Can include hoverBackground, hoverText timeScale?: ElementThemeColors; timeScaleHeader?: ElementThemeColors; timeScaleSubHeader?: ElementThemeColors; ganttChartArea?: ElementThemeColors; taskBar?: ElementThemeColors; epicBar?: ElementThemeColors; timelineControls?: ElementThemeColors; // Themes standard controls and the custom button currentDateIndicator?: ElementThemeColors; ganttGrid?: ElementThemeColors; } // Defines specific color properties for an element interface ElementThemeColors { background?: string; // e.g., '#FFFFFF', 'rgb(255,0,0)', 'bg-blue-500' (if mapping Tailwind) text?: string; border?: string; dotColor?: string; // For sidebar item dots progressFill?: string; // For task/epic progress bars badgeBackground?: string; // For sidebar header count badges badgeText?: string; // For sidebar header count badges badgeBorder?: string; // For sidebar header count badges // Standard button colors (within timelineControls) buttonBackground?: string; buttonText?: string; buttonHoverBackground?: string; buttonHoverText?: string; buttonBorder?: string; // Custom button colors (within timelineControls) customButtonBackground?: string; customButtonText?: string; customButtonBorder?: string; customButtonHoverBackground?: string; customButtonHoverText?: string; customButtonHoverBorder?: string; // Sidebar Item specific states (within sidebarItemEpic, sidebarItemTask) hoverBackground?: string; hoverText?: string; expandedBackground?: string; expandedText?: string; // ... other specific keys like weekendBackground, etc. } - The
timelineControlstheme withinComponentThemesapplies to standard buttons and can also include specific keys for the optional custom button:customButtonBackground,customButtonText,customButtonBorder,customButtonHoverBackground,customButtonHoverText, andcustomButtonHoverBorder. - The
sidebarItemEpicandsidebarItemTaskthemes can now includehoverBackground,hoverText.sidebarItemEpiccan additionally includeexpandedBackgroundandexpandedTextfor when an epic is expanded. - Example:
const myColors: CustomColors = { light: { sidebar: { background: '#f0f0f0', text: '#333' }, sidebarItemEpic: { background: 'transparent', text: '#374151', hoverBackground: '#E9D5FF', hoverText: '#581C87', expandedBackground: '#D1C4E9', expandedText: '#4527A0', }, sidebarItemTask: { background: 'transparent', text: '#374151', hoverBackground: '#A7F3D0', hoverText: '#047857', }, taskBar: { background: 'lightblue', text: 'darkblue', border: 'blue', progressFill: 'royalblue' }, timelineControls: { background: '#F8F9FA', buttonBackground: '#FFFFFF', buttonText: '#007BFF', buttonBorder: '#007BFF', customButtonBackground: '#A0A0FF', // Light purple for custom button customButtonText: '#FFFFFF', customButtonBorder: '#8080DD', } }, dark: { sidebar: { background: '#222', text: '#eee' }, sidebarItemEpic: { background: 'transparent', text: '#D1D5DB', hoverBackground: '#3A0CA3', hoverText: '#F4E1FF', expandedBackground: '#2A0A73', expandedText: '#E0CFF0', }, sidebarItemTask: { background: 'transparent', text: '#D1D5DB', hoverBackground: '#065F46', hoverText: '#A7F3D0', }, taskBar: { background: 'darkslateblue', text: 'aliceblue', border: 'slateblue', progressFill: 'mediumpurple' }, timelineControls: { background: '#1E1E1E', buttonBackground: '#2C2C2C', buttonText: '#00BFFF', buttonBorder: '#00BFFF', customButtonBackground: '#5050A0', // Darker purple for custom button customButtonText: '#E0E0E0', customButtonBorder: '#404080', } } }; <Timeline customColors={myColors} isDarkMode={isDarkMode} ... />
customBorderRadius?: CustomBorderRadius- Allows customization of border-radius for various UI elements.
- Structure (
ComponentBorderRadiusThemes):interface ComponentBorderRadiusThemes { timelineWrapper?: string; // e.g., 'rounded-lg', 'rounded-none', 'rounded-xl' sidebar?: string; sidebarHeaderContainer?: string; sidebarHeaderCountBadge?: string; sidebarItemContainer?: string; sidebarItemStatusBadge?: string; timeScaleHeaderCell?: string; timeScaleDayCell?: string; timeScaleMonthCell?: string; taskBarMain?: string; taskBarProgress?: string; epicBarMain?: string; epicBarProgress?: string; timelineControlsButton?: string; // For standard control buttons timelineControlsSelect?: string; timelineControlsGroupContainer?: string; timelineControlsCustomButton?: string; // For the optional custom button tooltipBubble?: string; clippedBarDateIndicator?: string; currentDateIndicatorLabel?: string; } // CustomBorderRadius is type alias for ComponentBorderRadiusThemes | undefined - Example:
const myBorderRadius: CustomBorderRadius = { timelineWrapper: 'rounded-xl', taskBarMain: 'rounded-full', timelineControlsButton: 'rounded-md', timelineControlsCustomButton: 'rounded-lg', // Custom radius for the custom button }; <Timeline customBorderRadius={myBorderRadius} ... />
isDarkMode?: boolean(Default:false)- Set to
trueto activate the dark mode theme (either default dark or yourcustomColors.darktheme).
- Set to
showProgressBars?: boolean(Default:true)- Set to
falseto hide progress bars on task and epic visuals in both the sidebar and Gantt chart.
- Set to
Internationalization (i18n)
language?: string(Default:'en')- The current language code (e.g., 'en', 'tr', 'fr'). This key is used to look up translations in the
translationsprop and to configuredate-fnslocale for date formatting.
- The current language code (e.g., 'en', 'tr', 'fr'). This key is used to look up translations in the
translations?: Translations- An object to provide custom translation strings.
- Structure:
type TranslationKey = | 'headerTimeline' | 'headerEpics' | 'headerTasks' | 'statusNotStarted' | 'statusInProgress' | 'statusCompleted' // ... other keys (see src/types/timeline.ts for full list) | 'controlsCustomButtonTitle' // Key for the custom button's title | 'timeScaleLabelQuarterFormat' // e.g., "Q{quarter} {year}" | 'currentDateIndicatorLabelFormat'; // e.g., "{monthAbbr} {day}" type TranslationSet = Partial<Record<TranslationKey, string>>; type Translations = Record<string, TranslationSet>; // Key is language code (e.g., 'en') - Example:
const myTranslations: Translations = { fr: { headerTimeline: 'Chronologie', statusInProgress: 'En cours', controlsCustomButtonTitle: 'Paramètres', timeScaleLabelQuarterFormat: 'T{quarter} {year}', // Example with placeholders }, es: { headerTimeline: 'Línea de Tiempo', statusInProgress: 'En Progreso', controlsCustomButtonTitle: 'Configuración', } }; <Timeline language="fr" translations={myTranslations} ... />
View & Controls Configuration
defaultViewMode?: ViewMode(Default:'quarters')- Sets the initial view mode when the timeline first renders. Can be
'quarters','months', or'weeks'.
- Sets the initial view mode when the timeline first renders. Can be
timelineControlsConfig?: TimelineControlsConfig- An object to show/hide individual controls in the timeline header and configure an optional custom button.
- Structure:
// Configuration for the optional custom button interface CustomTimelineButtonConfig { title?: string | TranslationKey; // Text label or TranslationKey for the button icon?: React.ReactNode; // Icon element (e.g., from lucide-react) onClick?: () => void; // Function to call when clicked isVisible?: boolean; // Show or hide the button (default true if config object exists) } // Main configuration for all timeline controls interface TimelineControlsConfig { showJumpToStartButton?: boolean; // Default true showJumpToTodayButton?: boolean; // Default true showZoomControls?: boolean; // Default true showYearRangeControls?: boolean; // Default true showViewModeSelector?: boolean; // Default true customButton?: CustomTimelineButtonConfig; // Configuration for the custom button } - Example (hide zoom, add custom settings button with localizable title):
import { Settings } from 'lucide-react'; // Example icon import type { TranslationKey } from '@gorkemyildiz/react-gantt-chart'; const myControlsConfig: TimelineControlsConfig = { showZoomControls: false, customButton: { isVisible: true, title: 'controlsCustomButtonTitle' as TranslationKey, // Use a translation key icon: <Settings size={14} />, onClick: () => alert("Settings button clicked!"), } }; // Assuming 'controlsCustomButtonTitle' is defined in your 'translations' prop: // const myTranslations: Translations = { // en: { controlsCustomButtonTitle: 'Settings' }, // fr: { controlsCustomButtonTitle: 'Paramètres' }, // }; <Timeline timelineControlsConfig={myControlsConfig} translations={myTranslations} ... />
monthCellFormatInQuarterView?: MonthCellFormat(Default:'short')- Determines how month names are displayed in the sub-header of the 'quarters' view mode.
'short': e.g., "Jan", "Feb"'long': e.g., "January", "February"'numeric': e.g., "1", "2"
showTooltips?: boolean(Default:true)- Globally enables or disables tooltips that appear when hovering over task and epic bars in the Gantt chart, especially when the bar is too narrow to display its full title or progress.
dayWidths?: DayWidths- Allows specifying custom widths (in pixels) for individual day cells in different view modes. This affects the overall zoom and density of the timeline.
- Structure:
interface DayWidths { weeks?: number; // Width of a day cell in 'weeks' view (e.g., 70) months?: number; // Width of a day cell in 'months' view (e.g., 40) quarters?: number; // Width of a day cell in 'quarters' view (e.g., 20) // Note: In 'quarters' view, month cells are composed of these day cells. } - Example:
const customDayWidths: DayWidths = { weeks: 90, // Wider days in week view months: 50, quarters: 25, }; <Timeline dayWidths={customDayWidths} ... />
initialScrollBehavior?: 'task' \| 'today'(Default:'task')- Determines the initial horizontal scroll position of the Gantt chart.
'task': Scrolls to the beginning of the content (first task or epic).'today': Scrolls the timeline to the current date. If the current date is outside the visible range defined byselectedStartYearandselectedEndYear, it scrolls to the nearest edge of that range (start or end).
Custom Sidebar Renderers
These props allow you to provide your own React components to render specific parts of the sidebar, offering deep customization.
customSidebarTaskStatusBadge?: (task: Task, isDarkMode?: boolean) => React.ReactNode- A function that receives a
Taskobject and the currentisDarkModestate, and should return a React Node to render as the status badge for that task. - Return
nullorundefinedto fall back to the default badge rendering for that specific task. - Example:
import { CheckCircle } from 'lucide-react'; // Example icon const myTaskBadgeRenderer = (task: Task, isDark?: boolean) => { if (task.status === 'completed') { return <CheckCircle size={16} className={isDark ? "text-green-400" : "text-green-600"} />; } if (task.status === 'not-started' && task.title.includes('Urgent')) { return <span className="px-2 py-0.5 text-xs bg-red-500 text-white rounded-full">URGENT</span>; } return null; // Fallback to default for other statuses }; <Timeline customSidebarTaskStatusBadge={myTaskBadgeRenderer} ... />
- A function that receives a
customSidebarEpicDotRenderer?: (epic: Epic, isDarkMode?: boolean) => React.ReactNode- Similar to
customSidebarTaskStatusBadge, but for rendering the dot indicator next to epics. - Receives an
Epicobject andisDarkModestate. - Example:
import { FolderArchive } from 'lucide-react'; const myEpicDotRenderer = (epic: Epic, isDark?: boolean) => { if (epic.tasks.length > 5) { // Example: different dot for large epics return <FolderArchive size={12} className={isDark ? "text-yellow-400" : "text-yellow-600"} />; } // Default dot (or your custom default) can be rendered here if needed, // or return null to use library's default. return <div className={`w-3 h-3 rounded-sm ${isDark ? 'bg-purple-400' : 'bg-purple-600'}`} />; }; <Timeline customSidebarEpicDotRenderer={myEpicDotRenderer} ... />
- Similar to
customSidebarTaskDotRenderer?: (task: Task, isDarkMode?: boolean) => React.ReactNode- For rendering the dot indicator next to tasks (both standalone and within epics).
- Receives a
Taskobject andisDarkModestate. - Example:
import { AlertTriangle } from 'lucide-react'; const myTaskDotRenderer = (task: Task, isDark?: boolean) => { if (task.progress < 25 && task.status === 'in-progress') { // Example: warning for low progress tasks return <AlertTriangle size={12} className={isDark ? "text-orange-400" : "text-orange-600"} />; } return <div className={`w-2 h-2 rounded-full ${isDark ? 'bg-sky-400' : 'bg-sky-600'}`} />; }; <Timeline customSidebarTaskDotRenderer={myTaskDotRenderer} ... />
customSidebarEpicBadgeSuffix?: (epic: Epic, isDarkMode?: boolean) => React.ReactNode- A function that receives an
Epicobject and the currentisDarkModestate. It should return a React Node to be rendered to the right of the epic's progress percentage (or other default badges) in the sidebar. - Return
nullorundefinedif no suffix should be rendered for a specific epic. - Example:
import { StarIcon } from 'lucide-react'; const myEpicBadgeSuffix = (epic: Epic, isDark?: boolean) => { if (epic.title.includes('Priority')) { return <StarIcon size={12} className={`ml-1 ${isDark ? 'text-yellow-300' : 'text-yellow-500'}`} />; } return null; }; <Timeline customSidebarEpicBadgeSuffix={myEpicBadgeSuffix} ... />
- A function that receives an
customSidebarTaskBadgeSuffix?: (task: Task, isDarkMode?: boolean) => React.ReactNode- Similar to
customSidebarEpicBadgeSuffix, but for tasks. It renders to the right of the task's status badge and progress percentage. - Receives a
Taskobject andisDarkModestate. - Example:
import { ThumbsUpIcon } from 'lucide-react'; const myTaskBadgeSuffix = (task: Task, isDark?: boolean) => { if (task.progress === 100 && task.status === 'completed') { return <ThumbsUpIcon size={12} className={`ml-1 ${isDark ? 'text-green-400' : 'text-green-600'}`} />; } return null; }; <Timeline customSidebarTaskBadgeSuffix={myTaskBadgeSuffix} ... />
- Similar to
Other Props
editable?: boolean(Default:true)- If set to
false, most interactive features of the timeline component become read-only. This includes dragging tasks/epics, resizing items, clicking on controls (zoom, view mode, year range, jump to start/today, custom button), and expanding/collapsing epics in the sidebar. - The cursor will change to
not-allowedwhen hovering over these interactive elements ifeditableisfalse. - However, click events on task and epic bars (via
onTaskClickandonEpicClick) will still fire, allowing for read-only interactions like displaying details.
- If set to
useTimelineData Hook
This hook is essential for managing the state of your Gantt chart data. It simplifies state management for epics, tasks, and their interactions, providing the necessary functions to update and manipulate the timeline data.
Options (UseTimelineDataOptions)
When calling useTimelineData, you can pass an options object:
| Option | Type | Description | Default |
| ------------------------ | ------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| initialEpics | Epic[] (Optional) | Your initial array of Epic objects. | Sample mock data |
| initialStandaloneTasks | Task[] (Optional) | Your initial array of Task objects that are not part of any epic. | Sample mock data |
| onEpicsChange | (epics: Epic[]) => void (Optional) | Callback triggered whenever the epics array changes. Useful for persisting changes (e.g., to a backend). | undefined |
| onStandaloneTasksChange| (tasks: Task[]) => void (Optional)| Callback triggered whenever the standaloneTasks array changes. Useful for persisting changes. | undefined |
Return Values
The hook returns an object with the following properties and methods:
| Property | Type | Description |
| -------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| epics | Epic[] | The current state of the epics array. |
| standaloneTasks | Task[] | The current state of the standalone tasks array. |
| items | TimelineItem[] | A derived, flattened list of all epics, tasks, and section headers, suitable for rendering in the Timeline component's items prop. |
| updateTask | (taskId: string, updates: Partial<Task>) => void | Function to update properties of a specific task. Automatically recalculates parent epic dates if necessary. |
| updateEpic | (epicId: string, updates: Partial<Epic>) => void | Function to update properties of a specific epic. Also handles adjustments to child tasks if epic dates change significantly (e.g., tasks are shifted with the epic). |
| moveTask | (taskId: string, targetEpicId: string \| null, targetIndex?: number) => void | Function to move a task. It can be moved to another epic, made a standalone task, or reordered within its current list. Automatically updates parent epic dates. |
| handleToggleExpand | (epicId: string) => void | Function to toggle the isExpanded state of an epic, controlling the visibility of its tasks in the sidebar and timeline. |
| handleReorderEpics | (sourceIndex: number, targetIndex: number) => void | Function to reorder epics in the sidebar via drag and drop. |
Example Usage:
import { useTimelineData, type Epic, type Task } from '@gorkemyildiz/react-gantt-chart';
function MyGanttComponent() {
const myInitialEpics: Epic[] = [/* ... your data ... */];
const myInitialStandaloneTasks: Task[] = [/* ... your data ... */];
const {
epics,
standaloneTasks,
items,
updateTask,
updateEpic,
moveTask,
handleToggleExpand,
handleReorderEpics
} = useTimelineData({
initialEpics: myInitialEpics,
initialStandaloneTasks: myInitialStandaloneTasks,
onEpicsChange: (updatedEpics) => {
console.log('Epics have changed:', updatedEpics);
// Here you might save `updatedEpics` to your backend or global state
},
onStandaloneTasksChange: (updatedTasks) => {
console.log('Standalone tasks have changed:', updatedTasks);
// Persist `updatedTasks`
},
});
// Now pass these to the <Timeline /> component
return (
<Timeline
epics={epics}
standaloneTasks={standaloneTasks}
items={items}
onUpdateTask={updateTask} // Pass the updater functions
onUpdateEpic={updateEpic}
onMoveTask={moveTask}
onToggleExpand={handleToggleExpand}
onReorderEpics={handleReorderEpics}
// ... other props
/>
);
}How useTimelineData Manages Dates:
- Task Updates: When a task's
startDateorendDateis updated viaupdateTask, the hook checks if the task belongs to an epic. If so, it ensures the parent epic'sstartDateandendDateencompass all its child tasks, including the updated one. If the task update causes it to fall outside the epic's current dates, the epic's dates are expanded. - Epic Updates: When an epic's
startDateorendDateis updated viaupdateEpic:- If the epic's date range shrinks and tasks fall outside, those tasks are not automatically truncated or moved by default (this behavior might be configurable or handled by specific drag/resize logic in the Gantt chart area). The epic's dates will generally try to encompass its tasks.
- If the epic is dragged, its tasks are typically dragged along with it, maintaining their relative positions and durations.
- Moving Tasks: When
moveTaskis called:- The source epic (if any) will have its dates recalculated if the moved task was defining one of its boundaries.
- The target epic (if any) will have its dates recalculated to encompass the newly added task.
This internal management helps maintain data consistency between epics and their constituent tasks.
Key Data Types
The library exports several types to help you structure your data and customize the component. You can import them like so:
import type {
Epic, Task, TimelineItem, ViewMode, CustomColors, Translations, // etc.
} from '@gorkemyildiz/react-gantt-chart';For the complete structure of all types, please refer to the src/types/timeline.ts file within the library's source code, or rely on your IDE's TypeScript intellisense.
Here's an overview of the most important types:
Task: Interface for task objects.interface Task { id: string; title: string; type: 'task'; startDate: Date; endDate: Date; epicId: string \| null; // ID of the parent epic, or null if standalone progress: number; // Percentage (0-100) status: 'not-started' \| 'in-progress' \| 'completed'; }Epic: Interface for epic objects.interface Epic { id: string; type: 'epic'; title: string; startDate: Date; endDate: Date; tasks: Task[]; // Array of tasks belonging to this epic isExpanded: boolean; // Controls visibility of tasks in the sidebar and timeline }TimelineItem: A union type representing items in the flatteneditemsarray used for rendering.type TimelineItem = { type: 'epic' \| 'task' \| 'section'; data: Epic \| Task \| Section; // The actual epic, task, or section data level: number; // Indentation level in the sidebar (0 for epics/standalone, 1 for tasks in epics) }Section: Interface for section headers in the sidebar (e.g., "Epics", "Tasks").interface Section { id: string; title: string; // Usually a key for translation or direct display isDropZone?: boolean; // Indicates if the section header can be a drop target for tasks }
(Other type definitions like ViewMode, CustomColors, Translations, TimelineControlsConfig, DayWidths, CustomBorderRadius are detailed within their respective prop explanations above.)
Contributing
Contributions are welcome! Please refer to the main README.md in the monorepo root for development and contribution guidelines.
License
This project is licensed under the MIT License.
