- Home
- Forum
- React - EJ 2
- Custom Parent Task Progress Calculation (Manual, Average, and Effort-Based Calculation)
Custom Parent Task Progress Calculation (Manual, Average, and Effort-Based Calculation)
I’m currently using the Gantt Chart component in my app (remote data loading, and virtualization).
The automatic behaviour for parent task progress calculated as the simple average of its children, works in some cases but doesn’t always match real project structures. I’d like to offer users more control and flexibility.
Desired Progress Calculation Options:
-
Manual Parent Progress Entry
-
Allow users to set the parent task’s progress manually, overriding the automatic calculation.
-
-
Effort-Based Progress Calculation
-
Enable progress to be calculated based on an “effort factor” per child task.
-
For example, larger tasks (in terms of duration, complexity, or hours) should influence the parent’s progress more than smaller ones.
-
Instead of a blind average, each child could have an “effort factor” assigned by the user, so child tasks contribute proportionally.
-
-
Toggle Between Modes
Automatic average (default),
Manual parent progress,
Effort-based (pseudo-weighted) progress.
My Questions:
-
Is there an existing Gantt API or event hook (such as
taskbarTemplate,actionBegin, etc.) to override parent progress behaviour?
This would greatly improve accuracy and user satisfaction in complex project scenarios.
Hi Kimmo,
Greetings from Syncfusion.
To meet your requirement, we have prepared sample with custom solution. It enables dynamic switching between different progress indicators such as Default, Manual, and Effort Factor in our Gantt chart. It uses React state to control which progress field is displayed and updates the chart accordingly. A custom taskbar template ensures the correct value is shown visually based on the selected mode. Refer to the below following steps:
- Initialize with Default Progress
The Gantt chart is initially configured to use the standardProgressfield, with corresponding label settings "Progress" and task field mappings.
- Track Progress Mode Using State
Three boolean states (isDefault,isManual,isEffortFactor) are used to determine which progress mode is active and control the chart’s behavior.
- Switch Modes via Button Clicks
Clicking a mode button updates the relevant state and dynamically changes theprogressfield and label in the Gantt chart configuration.
- Render Custom Taskbar Template
A customParentTaskbarTemplateconditionally displays the correct progress value (Progress,CustomProgress, orEffortFactor) based on the active mode.
- Force Gantt Chart Re-render
Thekeyprop on the Gantt component is tied to the mode state, ensuring the chart re-renders immediately when the mode changes.
Refer to the code snippet and sample for your reference.
Code-Snippet:
index.js:-
function App() { // State to track which progress mode is active const [isDefault, setIsDefault] = useState(true); const [isManual, setIsManual] = useState(false); const [isEffortFactor, setIsEffortFactor] = useState(false);
// Label settings for the Gantt chart const [labelSettings, setLabelSettings] = useState({ taskLabel: 'Progress', });
// Task field mappings for the Gantt chart const [taskFields, setTaskFields] = useState({ id: 'TaskID', name: 'TaskName', startDate: 'StartDate', duration: 'Duration', progress: 'Progress', parentID: 'ParentID', });
// Custom template for rendering parent taskbars function ParentTaskbarTemplate(props) { return ( <div className="e-gantt-parent-taskbar-inner-div e-gantt-parent-taskbar" style={{ height: '100%' }} > <div className="e-gantt-parent-progressbar-inner-div e-row-expand e-gantt-parent-progressbar" style={{ width: props.ganttProperties.progressWidth + 'px', height: '100%', }} ></div> <span className="e-task-label" style={{ position: 'absolute', fontSize: '12px', color: 'white', top: '5px', left: '10px', fontFamily: 'Segoe UI', cursor: 'move', }} > {/* Display the appropriate progress value based on selected mode */} {isDefault ? props.Progress : isManual ? props.taskData.CustomProgress : isEffortFactor ? props.EffortFactor : props.Progress} </span> </div> ); }
// Handlers to switch between progress modes const onManualClick = () => { setIsDefault(false); setIsEffortFactor(false); setIsManual(true); setTaskFields((prev) => ({ ...prev, progress: 'CustomProgress' })); setLabelSettings({ taskLabel: 'CustomProgress' }); };
const onDefaultClick = () => { setIsDefault(true); setIsEffortFactor(false); setIsManual(false); setTaskFields((prev) => ({ ...prev, progress: 'Progress' })); setLabelSettings({ taskLabel: 'Progress' }); };
const onEffortFactortClick = () => { setIsDefault(false); setIsEffortFactor(true); setIsManual(false); setTaskFields((prev) => ({ ...prev, progress: 'EffortFactor' })); setLabelSettings({ taskLabel: 'EffortFactor' }); };
return ( <> {/* Buttons to switch progress modes */} <button onClick={onDefaultClick}>Default</button> <button onClick={onManualClick}>Manual</button> <button onClick={onEffortFactortClick}>Effort Factor</button>
{/* Gantt chart component */} <GanttComponent id="Gantt" key={isManual} // Forces re-render when switching modes dataSource={data} taskFields={taskFields} labelSettings={labelSettings} parentTaskbarTemplate={ParentTaskbarTemplate} height="450px" /> </> ); } |
Sample - Hjqtqzbp (duplicated) - StackBlitz
Demo - Gantt Chart · Taskbar Template · Syncfusion React UI Components
Documentation - Taskbar in React Gantt component | Syncfusion
If we misunderstood your query, share us detailed information about your requirement with screenshot or video demo.
Regards,
Sridharan
Thanks for your response. But this does not fully address the deeper issues, especially around actual calculation logic for parent task progress in Syncfusion Gantt, especially in the edit mode.
This does not prevent Syncfusion from recalculating parent progress, automatically based on the default average of children. Syncfusion will still internally override the parent progress unless this behaviour is explicitly disabled or overridden.
Implement custom calculation logic (e.g., weighted/effort-based) dynamically. Changing the label or field doesn’t change how Syncfusion actually calculates the progress. You'd need to calculate it manually and supply that calculated value in a data pre-processing step.
Block automatic recalculation when child tasks update. If a child task changes, Syncfusion will likely still recalculate the parent progress unless you override it at the source level.
Code Snippet:-
index.js:- const Editing = () => { const [taskModes, setTaskModes] = useState({}); const [ganttData, setGanttData] = useState(customData); let elem; let ganttInstance; let dropdownlistObj; const sportsData = ['Default', 'Manual', 'EffortFactor']; // custom drop down let dropdownlist = { create: () => { elem = document.createElement('input'); return elem; }, read: () => { return dropdownlistObj.value; }, destroy: () => { dropdownlistObj.destroy(); }, write: (args) => { dropdownlistObj = new DropDownList({ dataSource: sportsData, value: args.rowData[args.column.field] || 'Default', floatLabelType: 'Auto', change: (changeArgs) => { const taskId = args.rowData.TaskID; const selectedMode = changeArgs.value; // Update the task modes state setTaskModes((prev) => ({ ...prev, [taskId]: selectedMode, })); // Update the data source args.rowData[args.column.field] = selectedMode; // Update the gantt data state setGanttData((prevData) => { const updateTaskMode = (tasks) => { return tasks.map((task) => { if (task.TaskID === taskId) { return { ...task, CustomProgress: selectedMode }; } if (task.subtasks) { return { ...task, subtasks: updateTaskMode(task.subtasks) }; } return task; }); }; return updateTaskMode(prevData); }); // Refresh the Gantt to update the template setTimeout(() => { if (ganttInstance) { ganttInstance.refresh(); } }, 100); }, }); dropdownlistObj.appendTo(elem); }, }; function ParentTaskbarTemplate(props) { const taskId = props.TaskID; const selectedMode = taskModes[taskId] || props.CustomProgress || 'Default'; // Determine which progress value to display let displayProgress; if (selectedMode === 'Manual') { displayProgress = props.ManualProgress || 0; } else if (selectedMode === 'EffortFactor') { // your custom logic here for effor factor calculation displayProgress = props.EffortFactorProgress || 0; } else { // Default calculation - use the standard Progress field displayProgress = props.Progress || 0; } return ( <div className="e-gantt-parent-taskbar-inner-div e-gantt-parent-taskbar" style={{ height: '100%' }} > <div className="e-gantt-parent-progressbar-inner-div e-row-expand e-gantt-parent-progressbar" style={{ width: props.ganttProperties.progressWidth + 'px', height: '100%',}} ></div> <span className="e-task-label" style={{ position: 'absolute', fontSize: '12px', color: 'white', top: '5px', left: '10px', fontFamily: 'Segoe UI', cursor: 'move', }} > {displayProgress}% ({selectedMode}) </span> </div> ); } return ( <GanttComponent parentTaskbarTemplate={ParentTaskbarTemplate} > <ColumnsDirective> <ColumnDirective field="CustomProgress" headerText="Progress Mode" width="150" edit={dropdownlist} ></ColumnDirective> </ColumnsDirective> <Inject services={[Edit, Selection, Toolbar, DayMarkers]} /> </GanttComponent> ); }; |
Modified Sample - Odgdcks3 (duplicated) - StackBlitz
Regards,
Sridharan
When I update the progress of a child task, the visual fill (width) of the parent task's taskbar also changes, even though the parent task's progress value remains the same. How can I prevent the parent taskbar's visual fill from updating when child task progress changes?
Kimmo,
We understand your query as: 'While the progress mode is set to Manual, the parent taskbar's progress width should not automatically update when the child task's progress changes.'
To achieve this behavior, we recommend adding a condition to control the parent task's progress width manually. Specifically, you can use the ManualProgress property to set the desired width explicitly. Please refer to the following code snippet and sample for implementation details
Code-Snippet:
function ParentTaskbarTemplate(props) { ................................... return ( <div className="e-gantt-parent-taskbar-inner-div e-gantt-parent-taskbar" style={{ height: '100%' }} > <div className="e-gantt-parent-progressbar-inner-div e-row-expand e-gantt-parent-progressbar" style={{ width: props.CustomProgress == 'Manual' ? props.ManualProgress : props.ganttProperties.progressWidth + 'px', height: '100%', }} ></div> <span className="e-task-label" style={{ position: 'absolute', fontSize: '12px', color: 'white', top: '5px', left: '10px', fontFamily: 'Segoe UI', cursor: 'move', }} > {displayProgress}% ({selectedMode}) </span> </div> ); } |
Modified Sample - Odgdcks3 (duplicated) - StackBlitz
Regards,
Sridharan
width:props.CustomProgress == 'Manual'
? props.ganttProperties.width * (props.ManualProgress / 100)
I was able to set the width with this. Is it possible to add the dragging of the p
Kimmo,
For your query, we suspect that you might be referring to progress resizing for the parent taskbar. However, this is currently not supported, as the parent task’s progress is automatically calculated based on its child tasks.
Alternatively, if you're referring to resizing the parent taskbar itself (i.e., adjusting its start or end dates by dragging), this is also not supported. Parent taskbars do not support left or right resizing. But we have manual taskbar feature which offers you right resize alone. Refer to the below documentation links:
https://ej2.syncfusion.com/react/demos/#/tailwind3/gantt/taskMode
https://ej2.syncfusion.com/react/documentation/api/gantt/#taskmode
If your requirement is different from the above, please share a screenshot or visual reference of the exact behavior you're trying to achieve. This will help us better understand your scenario and provide a more accurate solution.
Regards,
Sridharan
- 7 Replies
- 2 Participants
-
KK Kimmo Kallioniemi
- Jun 30, 2025 06:28 PM UTC
- Jul 23, 2025 02:44 PM UTC