npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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) and customBorderRadius (for border radius styles) props.
  • Internationalization (i18n): Support for multiple languages via the language and translations props.
  • 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 the dayWidths prop.
  • 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) via monthCellFormatInQuarterView.
  • 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 via customColors.
  • 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 onScrollToItemInGantt callback or by clicking the target icon in the sidebar.
  • Editable Mode: Control whether the timeline is interactive or read-only using the editable prop.
  • Item Click Handlers: Callbacks onTaskClick and onEpicClick are triggered when a task or epic bar is clicked in the Gantt chart area, respectively. These work even if editable is false.

Installation

To install the library in your project:

npm install @gorkemyildiz/react-gantt-chart
# or
yarn add @gorkemyildiz/react-gantt-chart

Vite & 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 Epic objects. Each epic defines a larger body of work and can contain an array of Task objects.
    • See Epic type definition below.
  • standaloneTasks: Task[]

    • An array of Task objects that do not belong to any epic.
    • See Task type definition below.
  • items: TimelineItem[]

    • This is a crucial prop for rendering the sidebar and Gantt chart rows. It's a flattened, ordered list derived from epics and standaloneTasks.
    • It's typically provided by the useTimelineData hook.
    • Each object in the array is a TimelineItem, which includes type ('epic', 'task', or 'section'), data (the actual Epic, Task, or Section object), and level (for indentation in the sidebar).
  • selectedStartYear: number and selectedEndYear: number

    • These numbers define the overall year range visible in the timeline. The timeline will display from the beginning of selectedStartYear to the end of selectedEndYear.
    • Users can change this range using the year selector controls if enabled.

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.
  • 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. null if 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 initialScrollBehavior is 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.
  • onTaskClick?: (task: Task) => void

    • Callback triggered when a task bar is clicked in the Gantt chart area. It receives the full Task object that was clicked.
    • This event fires regardless of the editable prop's state.
  • onEpicClick?: (epic: Epic) => void

    • Callback triggered when an epic bar is clicked in the Gantt chart area. It receives the full Epic object that was clicked.
    • This event fires regardless of the editable prop's state.

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 timelineControls theme within ComponentThemes applies to standard buttons and can also include specific keys for the optional custom button: customButtonBackground, customButtonText, customButtonBorder, customButtonHoverBackground, customButtonHoverText, and customButtonHoverBorder.
    • The sidebarItemEpic and sidebarItemTask themes can now include hoverBackground, hoverText. sidebarItemEpic can additionally include expandedBackground and expandedText for 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 true to activate the dark mode theme (either default dark or your customColors.dark theme).
  • showProgressBars?: boolean (Default: true)

    • Set to false to hide progress bars on task and epic visuals in both the sidebar and Gantt chart.

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 translations prop and to configure date-fns locale for date formatting.
  • 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'.
  • 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 by selectedStartYear and selectedEndYear, 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 Task object and the current isDarkMode state, and should return a React Node to render as the status badge for that task.
    • Return null or undefined to 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} ... />
  • customSidebarEpicDotRenderer?: (epic: Epic, isDarkMode?: boolean) => React.ReactNode

    • Similar to customSidebarTaskStatusBadge, but for rendering the dot indicator next to epics.
    • Receives an Epic object and isDarkMode state.
    • 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} ... />
  • customSidebarTaskDotRenderer?: (task: Task, isDarkMode?: boolean) => React.ReactNode

    • For rendering the dot indicator next to tasks (both standalone and within epics).
    • Receives a Task object and isDarkMode state.
    • 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 Epic object and the current isDarkMode state. 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 null or undefined if 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} ... />
  • 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 Task object and isDarkMode state.
    • 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} ... />

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-allowed when hovering over these interactive elements if editable is false.
    • However, click events on task and epic bars (via onTaskClick and onEpicClick) will still fire, allowing for read-only interactions like displaying details.

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 startDate or endDate is updated via updateTask, the hook checks if the task belongs to an epic. If so, it ensures the parent epic's startDate and endDate encompass 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 startDate or endDate is updated via updateEpic:
    • 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 moveTask is 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 flattened items array 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.