What's new in Gantt v5.0.0+
Gantt v5.0.0
Tasks display faster
Gantt now uses a new "early render" mode by default. When data is loaded, it is rendered immediately based on the raw data. After the initial rendering, the data normalization phase begins and the scheduling engine calculates the whole schedule (incl. adjusting any "dirty" data). This early rendering greatly decreases the time taken until tasks are displayed for big data sets, but might display tasks at wrong positions or with wrong size if the loaded data is not correctly normalized. In such cases, the tasks will transition to their correct position / size as soon as the calculations are finalized.
During the calculation phase, the Gantt chart is made read-only, users are not allowed to manipulate the data until calculations are finished. The calculation progress is displayed in a small progress bar in the time axis header (configurable using the projectProgressReporting config).
If you want to disable the new mode, you can do so using the delayCalculation config on the project.
This new mode might have consequences for your application if you have code which manipulates tasks after the initial load. Please see the Upgrade guide.
New resource utilization view
A new view displaying resources allocation has been added to the Gantt. The component is similar to the resource histogram and shows resources in a tree, having assignments nested. That allows seeing individual assignments allocation thus gives a more detailed representation.
const project = new ProjectModel({ events : [ { id : 1, startDate : '2010-01-18', name : 'Task 1', duration : 8, manuallyScheduled : true }, { id : 2, startDate : '2010-01-18', name : 'Task 2', duration : 3, manuallyScheduled : true }, { id : 3, startDate : '2010-01-18', name : 'Task 3', duration : 1, manuallyScheduled : true }, { id : 4, startDate : '2010-01-18', name : 'Task 4', duration : 2, manuallyScheduled : true } ], resources : [ { id : 'r1', name : 'Mike', city : 'Montreal' }, { id : 'r2', name : 'John', city : 'Montreal' }, { id : 'r3', name : 'David', city : 'Barcelona' } ], assignments : [ { id : 'a1', resource : 'r1', event : 1, units : 50 }, { id : 'a2', resource : 'r2', event : 2, units : 60 }, { id : 'a3', resource : 'r3', event : 3, units : 70 }, { id : 'a4', resource : 'r3', event : 4, units : 40 } ] }); const resourceUtilization = new ResourceUtilization({ project, startDate : new Date(2010, 0, 17), endDate : new Date(2010, 0, 29), appendTo : targetElement, autoHeight : true, minHeight : '20em', // display tooltip showBarTip : true }); Please check the new demo demonstrating the view and the component docs.
New scheduling issues handling popup
With this release the Gantt starts displaying a special popup informing user of the following scheduling issues:
- scheduling conflicts - when some constraints/dependencies contradict each other
- cycles - when dependencies structure builds a cycle
- calendar misconfigurations - when some calendar doesn't provide any working time intervals which makes its usage impossible
The popup allows user to pick a resolution for the case at hand.
Please check the new demo demonstrating the popup.
Also the project throws new events for the cases that can be listened to in order to provide a resolution programmatically:
new Gantt({
// disable default scheduling resolution popup
displaySchedulingIssueResolutionPopup : false,
project : {
// handle scheduling conflicts case
schedulingConflict({ schedulingIssue, continueWithResolutionResult }) {
// apply the very first available resolution
schedulingError.getResolutions()[0].resolve();
// and continue calculation w/ the Engine
continueWithResolutionResult(EffectResolutionResult.Resume);
}
}
});
Useful links:
- displaySchedulingIssueResolutionPopup - config to disable the popup showing.
- cycle - event that indicates a cycle has been detected.
- schedulingConflict - event that indicates a scheduling conflict has been detected.
- emptyCalendar - event that indicates a calendar misconfiguration has been detected.
- SchedulingIssueResolutionPopup - class implementing the popup to handle scheduling conflicts and calendar misconfigurations
- CycleResolutionPopup - class implementing the popup to handle scheduling cycles.
Inactive dependencies support in UI
Dependency model has active field which allows deactivating it. Inactive dependencies do not take part in the scheduling process (don't push linked tasks).
The field was represented on the data level before and this version adds its support to user interface as well.
These changes include Scheduler Pro view where inactive dependencies are displayed dashed now. Also new Active field has been added to the dependency editor.
New JavaScript bundles for combining products
Each product now has a new ES module based JavaScript bundle that only contains the product specific code, called a thin bundle. They are intended to be used when combining multiple products on page, to avoid having shared code loaded multiple times. Previously if you combined for example Grid and TaskBoard on a single page you would import from these bundles (minified size):
grid.module.min.js(~993 kB)taskboard.module.min.js(~1100 kB)
Both products are built upon Bryntum's core library and hence both of them include the core JavaScript (buttons, toolbars, helpers etc.). With thin bundles you would instead import what you need from each product in the hierarchy separately:
core.module.thin.min.js(~643 kB)grid.module.thin.min.js(~268 kB)taskboard.module.thin.min.js(~88 kB)
This way the shared code (from core) is only included once. With the old approach, about 2 MB of JavaScript was loaded, with the new about 1 MB.
The gain (loss actually) will be greater if you combine products that share even more code, like Gantt and Calendar. Calendar builds upon Scheduler > Grid > Core, while Gantt builds upon Scheduler Pro > Scheduler > Grid > Core. When not using thin bundles:
gantt.module.min.js(~1978 kB)calendar.module.min.js(~1833 kB)
With thin bundles (many since these products build upon others):
core.module.thin.min.js(~643 kB)grid.module.thin.min.js(~268 kB)scheduler.module.thin.min.js(~466 kB)schedulerpro.module.thin.min.js(~119 kB)engine.module.thin.min.js(~266 kB)gantt.module.thin.min.js(~122 kB)calendar.module.thin.min.js(~164 kB)
Total ~ 3811 kB vs 2048 kB (1763 kB less).
You import from the thin bundles in the same way as with any other ES modules bundle. The difference compared to using the full bundles is that you have to import from the correct bundle. For example to use the StringHelper class from Core and GridRowModel from Grid, previously you would have something similar to this:
import { StringHelper, GridRowModel } from 'grid.module.js';
Now you have to import them separately (since they are from different bundles):
import { StringHelper } from 'core.module.thin.js';
import { GridRowModel } from 'grid.module.thin.js';
New CSS bundles for combining products
Each theme now has a new CSS bundle that only contains product specific styling, called a thin bundle. They are intended to be used when combining multiple products on page, to avoid having shared styling loaded multiple times. Previously if you combined for example Grid and TaskBoard on a single page, using the Stockholm theme, you would load:
grid.stockholm.css(~244 kB)taskboard.stockholm.css(~243 kB)
Both products are built upon Bryntum's core library and hence both of them include the core CSS (buttons, toolbars, icons etc.). With thin bundles you would instead load each product in the hierarchy separately:
core.stockholm.thin.css(~203 kB)grid.stockholm.thin.css(~40 kB)taskboard.stockholm.thin.css(~40 kB)
This way the shared CSS (from core) is only included once. With the old approach, about 487 kB of CSS was loaded, with the new about 283 kB (204 kB less).
The gain (loss actually) will be greater if you combine products that share even more styling, like Gantt and Calendar. Calendar builds upon Scheduler > Grid > Core, while Gantt builds upon Scheduler Pro > Scheduler > Grid > Core. When not using thin bundles:
gantt.stockholm.css(~659 kB)calendar.stockholm.css(~655 kB)
With thin bundles (many since these products build upon others):
core.stockholm.thin.css(~203 kB)grid.stockholm.thin.css(~40 kB)scheduler.stockholm.thin.css(~363 kB)schedulerpro.stockholm.thin.css(~10 kB)gantt.stockholm.thin.css(~27 kB)calendar.stockholm.thin.css(~47 kB)
Total ~ 1314 kB vs 690 kB (624 kB less).
In your html file, you would for the Gantt + Calendar scenario have something similar to:
<link rel="stylesheet" href="core.stockholm.thin.css" data-bryntum-theme>
<link rel="stylesheet" href="grid.stockholm.thin.css" data-bryntum-theme>
<link rel="stylesheet" href="scheduler.stockholm.thin.css" data-bryntum-theme>
<link rel="stylesheet" href="schedulerpro.stockholm.thin.css" data-bryntum-theme>
<link rel="stylesheet" href="gantt.stockholm.thin.css" data-bryntum-theme>
<link rel="stylesheet" href="calendar.stockholm.thin.css" data-bryntum-theme>
data-bryntum-theme attribute above, it is required if the app will be switching theme at runtime using DomHelper.setTheme(). Tree grouping
Gantt has a new TreeGroup feature that can transform a task tree structure at runtime. It accepts an array of field names or functions that output a value, which it uses to create and populate the parent tasks in the tree. Comes with a new demo, grouping. Try it out in the live demo here:
const gantt = new Gantt({ appendTo : targetElement, // makes the Gantt chart as tall as it needs to be to fit all rows autoHeight : true, columns : [ { type : 'name', field : 'name', text : 'Name' } ], features : { treeGroup : { levels : ['prio'] } }, startDate : new Date(2022, 1, 4), endDate : new Date(2022, 1, 11), project : { taskStore : { fields : ['prio', 'category'] }, tasks : [ { id : 1, name : 'Project A', startDate : '2022-02-01', expanded : true, children : [ { id : 11, name : 'Task 1', startDate : '2022-02-01', duration : 5, prio : 'High', category : 'In house' }, { id : 12, name : 'Task 2', startDate : '2022-02-01', duration : 5, prio : 'Low', category : 'In house' }, { id : 13, name : 'Task 3', startDate : '2022-02-01', duration : 4, prio : 'High', category : 'In house' }, { id : 14, name : 'Task 4', startDate : '2022-02-01', duration : 5, prio : 'High', category : 'External' }, { id : 15, name : 'Task 5', startDate : '2022-02-01', duration : 5, prio : 'Low', category : 'External' } ] } ], dependencies : [ { id : 1, fromTask : 11, toTask : 12 }, { id : 2, fromTask : 13, toTask : 14 } ] }, tbar : [ { type : 'buttongroup', toggleGroup : true, items : [ { text : 'Prio', pressed : true, onToggle({ pressed }) { pressed && gantt.group(['prio']); } }, { text : 'Category', onToggle({ pressed }) { pressed && gantt.group(['category']); } }, { text : 'Prio + Category', onToggle({ pressed }) { pressed && gantt.group(['prio', 'category']); } }, { text : 'none', onToggle({ pressed }) { pressed && gantt.clearGroups(); } } ] } ] }); Pinning dependencies during drag drop/resize
Gantt allows dragging and resizing task with outgoing dependencies while keeping successors in place. If you enable pinSuccessors config on the feature and drag/resize task while holding CTRL key, lag on the outgoing dependencies will be recalculated so successors are not moved.
const gantt = new Gantt({ appendTo : targetElement, // makes the Gantt chart as tall as it needs to be to fit all rows autoHeight : true, columns : [ { type : 'name', field : 'name', text : 'Name' } ], features : { taskDrag : { pinSuccessors : true }, taskResize : { pinSuccessors : true } }, startDate : new Date(2022, 0, 9), endDate : new Date(2022, 0, 23), project : { tasks : [ { id : 1, name : 'Setup web server', percentDone : 50, duration : 10, expanded : true, children : [ { id : 11, name : 'Install Apache', percentDone : 50, startDate : '2022-01-10', duration : 3, color : 'teal' }, { id : 12, name : 'Configure firewall', percentDone : 50, duration : 3 }, { id : 13, name : 'Setup load balancer', percentDone : 50, duration : 3 }, { id : 14, name : 'Configure ports', percentDone : 50, duration : 2 }, { id : 15, name : 'Run tests', percentDone : 0, duration : 2 } ] } ], dependencies : [ { id : 1, fromTask : 11, toTask : 15, lag : 2 }, { id : 2, fromTask : 12, toTask : 15 }, { id : 3, fromTask : 13, toTask : 15 }, { id : 4, fromTask : 14, toTask : 15 } ] } }); Simplified test case creation
When reporting a hard to reproduce issue on Bryntum's support forum we often request a test case showing the issue. Getting a good test case greatly reduces the time it takes from reporting the bug until a fix can be released. Worst case we won't be able to find and fix the bug at all without one.
We understand that for complex apps it is not always trivial to produce a standalone test case. The app might be using a lot of different configs and the issue might only appear with a certain dataset etc. To simplify the process of creating a test case we have added a new function called downloadTestCase() to all Bryntum products. Running it collects the current value for the configs your app is using, inlines the current dataset and compiles configs and data into a JavaScript app that is then downloaded.
The app will most likely require manual tweaking before you can submit it to us, but we are hoping it will make creating a test case easier for you. Run gantt.downloadTestCase() on the console in a demo to try it. Any feedback on how this could be improved further is welcome on the forums!
Auto-saving state
Gantt allows persisting state changes automatically. See new state demo. Resize columns, rearrange them, sort or filter the store and after refresh application state will be restored, no special action is required.
ProjectModel wrapper for Angular, React & Vue
Gantt ships with a new ProjectModel wrapper that can be used to share data between multiple Bryntum components in the same app by pointing them all to the same project instance. Either you use it to load data from the backend using the built-in CrudManager or you bind your locally available data to it.
Integration details can be found in guides for Angular, React and Vue.
Gantt v5.0.4
Long distance projects support
By default the Gantt supports projects of 5 years duration max roughly. That limit was hardcoded in some private code. And in this release ProjectModel has got new maxCalendarRange config allowing to adjust this limit.
The value should be provided in milliseconds and should be configured in case the project deals with long tasks:
new Gantt({
project : {
// adjust calendar iteration limit to 10 years roughly:
// 10 years expressed in ms
maxCalendarRange : 10 * 365 * 24 * 3600000,
/* ... */
}
});
Gantt v5.1.0
Introducing Create React App templates
Create React App script templates are now available in the public npm repository.
If you are using javascript only, just type:
npx create-react-app my-app --template @bryntum/cra-template-javascript-gantt
or if you prefer using typescript:
npx create-react-app my-app --template @bryntum/cra-template-typescript-gantt
Note: Please feel free to change my-app to your preferred application name
Check the React integration guide for more information.
Simplified url configuration on the project
Configuring the crud manager functionality of the project was made a little easier by introducing shortcuts for setting load and sync urls using the new loadUrl and syncUrl configs. When your application does not need to supply any additional configs to the transport layer you can use them in favor of having to nest the urls. Old code like this:
const gantt = new Gantt({
project : {
transport : {
load : {
url : 'load.aspx'
}
}
}
})
Can now be written more conveniently like this:
const gantt = new Gantt({
project : {
loadUrl : 'load.aspx'
}
})
Flattening the structure out makes it easier for framework developers, who can now assign directly to the prop on the project component rather than having to supply a config object. Pseudo framework code to illustrate the old approach:
const projectConfig = {
transport : {
load : {
url : 'load.php'
}
}
}
<BryntumProjectModel {...projectConfig} />
Simplified approach:
<BryntumProjectModel loadUrl={"load.php"} />
New module bundle for Angular
Bryntum Gantt is now delivered with new ES Module bundle without WebComponents. This was done to avoid conflicts with Angular which also uses WebComponents for applications.
Angular wrappers use gantt.module.js bundle in favor of removed gantt.lite.umd.js one.
Check the upgrading guide for the details.
New module bundle with WebComponents
Bryntum Gantt is now delivered with new gantt.wc.module.js ES Module bundle with WebComponents.
Check the upgrading guide for the details.
New widget for switching views
The ViewPresetCombo is an easy-to-setup preset picker which can be added to the Gantt's toolbar and provides a simple UI for switching between different "views". It's based on ViewPreset which is built-in to Gantt.
Try it out in the live demo here:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>404 Not Found</title> </head><body> <h1>Not Found</h1> <p>The requested URL was not found on this server.</p> </body></html> Gantt v5.1.2
Keyboard shortcuts
Gantt now supports using keyboard shortcuts for indenting and outdenting tasks. Defaults are Alt + Shift + Right for indenting, and Alt + Shift + Left for outdenting. Note that the Alt key matches the Option key on Mac. Configurable with the KeyMap mixin. There is a new guide describing how to customize.
Gantt v5.2.0
Segmented tasks support
In this release Gantt adds support for segmented tasks. Each task can be split into segments which then can be adjusted separately, moved or merged back.
On the data level splitting a task can be done with a task model splitToSegments method call:
// split task1 into segments at 2022-07-27 with split duration of 1 day
await task1.splitToSegments(new Date(2022, 6, 27), 1, "day")
/* here rescheduling is done */
Task model has got a new segments field (plus properties to access the first and last segment) which returns an array of EventSegmentModel class instances.
Each segment can be changed individually:
// change last segment end date
task1.lastSegment.endDate = new Date(2022, 7, 4)
// wait till rescheduling is done
await task1.commitAsync()
/* here rescheduling is done */
Or with setEndDate method call:
await task1.lastSegment.setEndDate(new Date(2022, 7, 4))
/* here rescheduling is done */
Bringing two segments close to each merges them.
On the UI level segments support is handled by the following new features:
- EventSegments - implements rendering and adds new "Split event" entry to the event context menu.
- TaskSegmentResize - implement individual segments resizing with drag'n'drop.
- TaskSegmentDrag - implement individual segments moving with drag'n'drop.
const gantt = new Gantt({ appendTo : targetElement, height : 200, startDate : '2022-07-25', endDate : '2022-08-01', rowHeight : 60, project : new ProjectModel({ startDate : '2022-07-25', events : [ { id : 1, name : 'Task A', startDate : '2022-07-25', duration : 3, segments : [ { startDate : '2022-07-25', duration : 2 }, { startDate : '2022-07-28', duration : 1 } ] }, { id : 2, name : 'Task B', startDate : '2022-07-25', duration : 3, segments : [ { startDate : '2022-07-25', duration : 1 }, { startDate : '2022-07-27', duration : 2 }, { startDate : '2022-07-30', duration : 1 } ] } ] }) }); New widget for switching views
The ViewPresetCombo is an easy-to-setup preset picker which can be added to the Gantt's toolbar and provides a simple UI for switching between different "views". It's based on ViewPreset which is built-in to Gantt.
Try it out in the live demo here:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>404 Not Found</title> </head><body> <h1>Not Found</h1> <p>The requested URL was not found on this server.</p> </body></html> Stretch tasks by enabling fillTicks
The fillTicks config from Scheduler was ported over to Gantt. Enable it to always render task bars as full ticks (time cells, the bottom level of the time axis header). When for example having days as the bottom level, a 6-hour task will be stretched to fill the entire width of the tick, while a 25-hour task will fill two ticks.
const gantt = new Gantt({ appendTo : targetElement, autoHeight : true, columns : [ { type : 'name', field : 'name', text : 'Name' } ], fillTicks : true, startDate : new Date(2022, 1, 4), endDate : new Date(2022, 1, 11), project : { startDate : '2022-02-01T10:00', tasks : [ { id : 1, name : 'Project A', expanded : true, children : [ { id : 11, name : 'Task 1', duration : 5 }, { id : 12, name : 'Task 2', duration : 4 } ] } ], dependencies : [ { id : 1, fromTask : 11, toTask : 12 }, { id : 2, fromTask : 13, toTask : 14 } ] }, tbar : [ { text : 'Fill ticks', pressed : true, toggleable : true, icon : 'fa fa-square', pressedIcon : 'fa fa-check-square', onToggle({ pressed }) { gantt.fillTicks = pressed; } } ] }); Task non-working time
Gantt has a new feature TaskNonWorkingTime, which highlights the non-working time of the tasks, meaning time intervals when they can not be worked on. It supports two modes of visualizing the intervals, 'row' mode displays the intervals as ranges in the tasks row (very similar to ResourceNonWorkingTime in Scheduler Pro) and 'bar' mode displays them as shaded regions of the task bar. Try it out in the live demos below, or the new calendars demo.
Row mode:
const gantt = new Gantt({ appendTo : targetElement, // makes the Gantt chart as tall as it needs to be to fit all rows autoHeight : true, columns : [ { type : 'name', width : 150 }, { type : 'calendar' } ], features : { taskNonWorkingTime : true }, project : { startDate : new Date(2022, 7, 4), tasks : [ { id : 1, name : 'Task 1', calendar : 'mon-thu', duration : 5 }, { id : 2, name : 'Task 2', calendar : 'break', duration : 5 } ], calendars : [ { id : 'general', name : 'General', intervals : [ { recurrentStartDate : 'on Sat', recurrentEndDate : 'on Mon', isWorking : false } ] }, { id : 'mon-thu', name : 'Mon-Thu', intervals : [ { recurrentStartDate : 'on Fri', recurrentEndDate : 'on Mon', isWorking : false } ] }, { id : 'break', name : 'Breaks', intervals : [ { startDate : '2022-08-07', endDate : '2022-08-11', isWorking : false }, { startDate : '2022-08-18', endDate : '2022-08-20', isWorking : false } ] } ] } }); Bar mode:
const gantt = new Gantt({ appendTo : targetElement, // makes the Gantt chart as tall as it needs to be to fit all rows autoHeight : true, columns : [ { type : 'name', width : 150 }, { type : 'calendar' } ], features : { taskNonWorkingTime : { mode : 'bar' } }, project : { startDate : new Date(2022, 7, 4), tasks : [ { id : 1, name : 'Task 1', calendar : 'mon-thu', duration : 5 }, { id : 2, name : 'Task 2', calendar : 'break', duration : 5 } ], calendars : [ { id : 'general', name : 'General', intervals : [ { recurrentStartDate : 'on Sat', recurrentEndDate : 'on Mon', isWorking : false } ] }, { id : 'mon-thu', name : 'Mon-Thu', intervals : [ { recurrentStartDate : 'on Fri', recurrentEndDate : 'on Mon', isWorking : false } ] }, { id : 'break', name : 'Breaks', intervals : [ { startDate : '2022-08-07', endDate : '2022-08-11', isWorking : false }, { startDate : '2022-08-18', endDate : '2022-08-20', isWorking : false } ] } ] } }); The modes can also be combined by specifying mode: 'both'.
Improved grouping
The TreeGroup feature was reworked to allow manipulating the tasks in the generated structure, applying the same rules as in the original view. Previously all tasks were all flagged as manuallyScheduled when grouped, circumventing the rules. The improved behaviour was made possible by grouping to a new store instead of manipulating the original task store, and then pointing Gantt to that store as the source if tasks to display. The tasks in the new store are linked to those in the original store, manipulations are applied to both.
Improved PDF Export feature
Pdf Export feature now renders exported content directly and does not scroll the view anymore. This significantly improves performance (by an order of magnitude) and robustness of the export process. You can enable old behavior by setting enableDirectRendering config on the export feature to false. Old behavior is deprecated and will be removed in the next major release. Please report issues that make you disable direct rendering.
Ignoring resource calendars
A task normally performs work when both its own calendar and some of the assigned resource calendars (if any resource is assigned to the task) allow that.
With this release task model has got a new ignoreResourceCalendar boolean field allowing to toggle that logic. When the field is set to true the task will not take its assigned resource calendars into account and will perform according to its own calendar only.
On the UI level the field is represented as "Ignore resource calendar" checkbox on the "Advanced" tab of the task editor and a new Gantt column.
Multi-filter widget
You can now create UI to define and manage a set of CollectionFilters using the GridFieldFilterPickerGroup widget. The widget is configured with a reference to a Gantt, and shows a set of filters that filter the Gantt's store as they are modified. The widget supports a variety of filtering operators based on the filter's selected data field type, including new date range operators.
The Gantt's filter feature can also be configured with isMulti to show the new multi-filter UI in the column header popup.
const gantt = new Gantt({ autoHeight : true, columns : [ { type : 'name', field : 'name', text : 'Name' }, { type : 'date', field : 'startDate', text : 'Start Date' } ], fillTicks : true, startDate : new Date(2022, 1, 4), endDate : new Date(2022, 1, 11), project : { startDate : '2022-02-01T10:00', tasks : [ { id : 1, name : 'Project A', expanded : true, children : [ { id : 11, name : 'Design website', duration : 5 }, { id : 12, name : 'Lease office space', duration : 4 }, { id : 13, name : 'Buy coffee machine', duration : 3 }, { id : 14, name : 'Hire designer', duration : 3 }, { id : 15, name : 'Write design handbook', duration : 3 } ] } ], dependencies : [ { id : 1, fromTask : 11, toTask : 12 }, { id : 2, fromTask : 13, toTask : 14 }, { id : 3, fromTask : 14, toTask : 15 } ], listeners : { load : ({ source : project }) => project.taskStore.filter() } }, features : { filter : true } }); const demo = new Container({ appendTo : targetElement, layout : 'vbox', items : { gantt, filtersPanel : { type : 'panel', title : 'Filters', maxWidth : '30em', style : { marginBlockStart : '1em' }, items : { filters : { type : 'gridfieldfilterpickergroup', grid : gantt, filters : [{ property : 'name', operator : 'includes', value : 'design', caseSensitive : false, disabled : true }, { property : 'startDate', operator : '>', value : new Date(2022, 1, 4) }] } } } } }); Gantt v5.2.6
Support for React Components in tooltips and widgets
React components are now supported in tooltips and widgets. Tooltips have renderer or template function that can now return valid JSX that represents a React component. Widgets can now supply JSX as their html property. For example:
Cell tooltip:
cellTooltipFeature: {
tooltipRenderer: ({ record }) => (
<React.StrictMode>
<DemoTooltip record={record} />
</React.StrictMode>
)
},
Event tooltip:
taskTooltipFeature: {
template: data => (
<React.StrictMode>
<DemoEventTip data={data} />
</React.StrictMode>
)
},
React component in widget:
bbar : {
items:[{
type:'widget',
html:<DemoWidget />
}]
},
See the React guide for details.
Gantt v5.3.0
AjaxHelper.fetch now supports passing parameters in request body
Since this release AjaxHelper.fetch method can pass provided queryParams in the request body instead of query string. This happens for application/x-www-form-urlencoded and multipart/form-data content types when addQueryParamsToBody option is set to true:
AjaxHelper.fetch('url', {
headers : {
'Content-Type' : 'application/x-www-form-urlencoded'
},
addQueryParamsToBody : true
});
Please check addQueryParamsToBody in FetchOptions
Please note that this behavior is disabled by default so if you need to enable it globally please use AjaxHelper.DEFAULTFETCHOPTIONS:
// enable addQueryParamsToBody by default
AjaxHelper.DEFAULT_FETCH_OPTIONS = {
addQueryParamsToBody : true
}
Time zone support
Support for Time zone conversion has been added to all Bryntum scheduling products. Simply set a IANA time zone identifier as value for the timeZone config and that's it.
But since time zones is not natively supported in JavaScript we strongly recommend to read our Time zone guide.
Also, please check out the new Time zone demo.
new Gantt({
timeZone : 'America/Chicago'
});
Versions feature
You can now enable the Versions feature on Gantt to capture, restore, and compare snapshots of a project, plus detailed change logs. The Versions feature adds a VersionStore and a ChangeLogStore to the project, and captures change logs using the STM feature.
Versions can be captured at any time programmatically, or on a configured schedule. Versions contain both a full snapshot of the project and a set of change log transactions describing the changes since the previous version, in a format useful for display to the user.
const gantt = new Gantt({ enableUndoRedoKeys : true, flex : 1, project : { autoHeight : true, tasks : [ { id : 11, name : 'Design website', startDate : new Date(2022, 10, 7), duration : 5 }, { id : 12, name : 'Lease office space', startDate : new Date(2022, 10, 12), duration : 4 }, { id : 13, name : 'Buy coffee machine', startDate : new Date(2022, 10, 15), duration : 3 }, { id : 14, name : 'Hire designer', startDate : new Date(2022, 10, 17), duration : 3 }, { id : 15, name : 'Write design handbook', startDate : new Date(2022, 10, 27), duration : 3 } ], dependencies : [ { id : 1, fromTask : 11, toTask : 12 }, { id : 2, fromTask : 13, toTask : 14 }, { id : 3, fromTask : 14, toTask : 15 } ], stm : { autoRecord : true } }, columns : [ { field : 'startDate', text : 'Start date' }, { field : 'duration', text : 'Duration' } ], subGridConfigs : { locked : { width : 200 } }, features : { versions : true, dependencies : true, dependencyEdit : true }, listeners : { /** * Demonstrates overriding the default transaction description to provide more detail * about which user action initiated the transaction. In this case, we set a custom * description for transactions involving a task drag event. */ taskDrop({ taskRecords }) { this.features.versions.transactionDescription = taskRecords.length === 1 ? `Dragged task ${taskRecords[0].name}` : `Dragged ${taskRecords.length} tasks`; }, taskResizeEnd({ taskRecord }) { this.features.versions.transactionDescription = `Resized task ${taskRecord.name}`; }, afterDependencyCreateDrop() { this.features.versions.transactionDescription = `Drew a link`; } } }); const app = new Container({ appendTo : targetElement, layout : 'box', height : 600, items : { gantt, splitter : { type : 'splitter' }, versionGrid : { type : 'versiongrid', flex : 1, emptyText : 'No versions to display', project : gantt.project, showUnattachedTransactions : true, selectionMode : { row : true, cell : false }, features : { cellMenu : { /** * Add a button to the version row context menu. */ items : { compareButton : undefined, duplicateButton : { text : 'Duplicate', icon : 'fa fa-copy', onItem : async({ record, source : grid }) => { const result = await MessageDialog.confirm({ title : 'Duplicate Version?', message : `This will create a new project from the content of the selected version. Do you want to continue?` }); if (result === MessageDialog.yesButton) { // Sample code demonstrating cloning a saved version await gantt.features.versions.getVersionContent(record.versionModel); const clonedProject = new ProjectModel(record.versionModel.content); gantt.project = clonedProject; } } } } } }, dateFormat : 'M/D/YY h:mm a', tbar : { items : [ { ref : 'saveButton', text : 'Save Version', icon : 'fa fa-plus', listeners : { click : () => { gantt.features.versions.saveVersion(); } } }, '->', { ref : 'onlyNamedToggle', type : 'slidetoggle', text : 'Show named versions only', listeners : { change : ({ checked }) => { app.widgetMap.versionGrid.showNamedVersionsOnly = checked; } } }, { ref : 'showVersionsToggle', type : 'slidetoggle', text : 'Changes only', checked : false, listeners : { change : ({ checked }) => { app.widgetMap.versionGrid.showVersions = !checked; } } } ] }, listeners : { // Handle the user asking to restore a given version restore : async({ version }) => { const result = await MessageDialog.confirm({ title : 'Restore Version?', message : `Are you sure you want to restore the selected version, replacing the current project? You will lose any unsaved changes.` }); if (result === MessageDialog.yesButton) { await gantt.features.versions.restoreVersion(version); gantt.features.baselines.disabled = true; // FIXME known issue with Undo after restoring version project.stm.resetQueue(); } } } } } }); gantt.project.stm.enable(); New locales
New locales for 31 languages have been added. Currently available languages are listed in the localization guide.
Gantt v5.3.3
Angular IVY and View Engine wrappers
Bryntum Gantt now ships with two npm Angular wrappers packages to support different versions of Angular framework.
@bryntum/gantt-angular is designed to work with Angular 12 and newer versions, which use the IVY rendering engine.
@bryntum/gantt-angular-view is designed to work with Angular 11 and older versions, which use the View Engine for rendering.
Please check Angular integration guide for the additional information.
Gantt v5.4.0
Scheduling direction
Gantt now supports setting scheduling direction on a per task basis. This is controlled by either using the direction field, or in a MS Project compatible way, by setting corresponding AsSoonAsPossible or AsLateAsPossible constraints.
const gantt = new Gantt({ appendTo : targetElement, autoHeight : true, columns : [ { type : 'name', field : 'name', text : 'Name' }, { type : 'schedulingdirection' } ], startDate : new Date(2024, 5, 1), endDate : new Date(2024, 5, 11), project : { startDate : '2024-06-01', tasks : [ { id : 1, name : 'Project A', expanded : true, children : [ { id : 11, name : 'Task 1', duration : 10 }, { id : 12, name : 'Task 2', duration : 3, direction : 'Backward' }, { id : 13, name : 'Task 3', duration : 0 } ] } ], dependencies : [ { id : 1, fromTask : 11, toTask : 13 }, { id : 2, fromTask : 12, toTask : 13 } ] } }); For more details, see docs for the direction field on TaskModel, and the includeAsapAlapAsConstraints config on ProjectModel.
Task coloring
Gantt now supports setting the eventColor field on a Task to a pre-defined named color (red, green, blue etc.) or a CSS color value. This changes the background color of the rendered task bar. It is also possible to configure the Gantt with a default eventColor that applies to all tasks, overriding the default coloring.
const gantt = new Gantt({ appendTo : targetElement, autoHeight : true, showTaskColorPickers : true, columns : [ { type : 'name', field : 'name', text : 'Name' }, { type : 'eventColor', text : 'Color' } ], startDate : new Date(2024, 5, 1), endDate : new Date(2024, 5, 11), project : { startDate : '2024-06-01', tasks : [ { id : 1, name : 'Project A', expanded : true, children : [ { id : 11, name : 'Task 1', duration : 10, eventColor : 'deep-orange' }, { id : 12, name : 'Task 2', duration : 3 }, { id : 13, name : 'Task 3', duration : 0, eventColor : 'purple' } ] } ], dependencies : [ { id : 1, fromTask : 11, toTask : 13 }, { id : 2, fromTask : 12, toTask : 13 } ] } }); For more details, check out the TaskModel docs.
new Gantt({
taskColor : 'cyan', // Sets a default background color for all tasks
project : {
tasksData : [
{
id : 1,
name : 'Task1',
// This sets a different background color on the task
taskColor : 'purple',
...
},
{
id : 2,
name : 'Task2',
taskColor : 'yellow'
}
]
}
});
Task color editing
We have added default editors for the eventColor field mentioned above. There is one in the TaskMenu feature's context menu and one in the TaskEdit feature's event editing panel. Just set showTaskColorPickers to true and the editors will appear. There is also a new EventColorColumn which can be added to any Gantt. It renders a colored element which the user can click and select a new color for each task.
new Gantt({
showTaskColorPickers : true,
columns : [
// Adds a column in which the user can see and edit the task color
{ type : 'eventcolor', text : 'Color', size : 80 }
]
})
See all editors in action in the updated Task editor customization demo.
Native clipboard support for TaskCopyPaste
The TaskCopyPaste feature has been enhanced to usa a page-global internal clipboard and also supports the browser's native Clipboard API if accessible. This means that it is possible to copy and paste tasks between multiple instances of Gantt or other Grid-based components. It is also possible to copy a task and paste it inside a Spreadsheet app like Excel.
Another improvement is that both the beforeCopy and beforePaste events are now asynchronously preventable.
If your browser supports it, you can try the native clipboard functionality out in the Aggregation column demo. And in this live demo:
const gantt = new Gantt({ appendTo : targetElement, autoHeight : true, features : { taskCopyPaste : { useNativeClipboard : true } }, columns : [ { type : 'name' }, { type : 'startdate' }, { type : 'enddate' } ], startDate : new Date(2024, 4, 27), endDate : new Date(2024, 5, 5), project : { startDate : '2024-05-27', tasks : [ { id : 1, name : 'Project A', expanded : true, children : [ { id : 11, name : 'Task 1', duration : 10 }, { id : 12, name : 'Task 2', duration : 0 } ] } ], dependencies : [ { id : 1, fromTask : 11, toTask : 13 } ] } }); Please note that this enhancement has required API changes:
- The
copyRowsandpasteRowsfunctions are now asynchronous.
New TimelineHistogram view
This release introduces a new TimelineHistogram class which implements a grid with histogram charts displayed for rows in the timeaxis section.
const histogram = new TimelineHistogram({ appendTo : targetElement, startDate : new Date(2023, 0, 1), endDate : new Date(2023, 0, 6), autoHeight : true, columns : [{ text : 'Name', field : 'name' }], // define histogram value series series : { work : { // display work values as bars type : 'bar' }, maxWork : { // display maxWork values as outline type : 'outline' } }, // grid data store : new Store({ data : [ { id : 1, name : 'Mats', // the record histogram data histogramData : [ { work : 8, maxWork : 16 }, { work : 18, maxWork : 12 }, { work : 12, maxWork : 10 }, { work : 13, maxWork : 16 }, { work : 15, maxWork : 16 }, { work : 1, maxWork : 6 } ] }, { id : 2, name : 'Johan', histogramData : [ { work : 15, maxWork : 16 }, { work : 8, maxWork : 26 }, { work : 12, maxWork : 6 }, { work : 13, maxWork : 16 }, { work : 18, maxWork : 16 }, { work : 8, maxWork : 16 } ] }, { id : 3, name : 'Arcady', histogramData : [ { work : 15, maxWork : 16 }, { work : 18, maxWork : 9 }, { work : 8, maxWork : 11 }, { work : 12, maxWork : 16 }, { work : 13, maxWork : 16 }, { work : 10, maxWork : 5 } ] } ] }) }); Please check the new Timeline histogram demo and the "Timeline histogram" guide for more details.
Grouping support for ResourceHistogram and ResourceUtilization views
This release adds better grouping support to the ResourceHistogram and ResourceUtilization views. The classes now automatically aggregate data on parent node levels based on corresponding child nodes.
For more details on the views please see new "Resource histogram" and "Resource utilization" guides
New Baseline columns
We have added a set of baseline columns: "Baseline start" (BaselineStartDateColumn), "Baseline finish" (BaselineEndDateColumn), "Baseline duration" (BaselineDurationColumn), "Baseline start variance" (BaselineStartVarianceColumn), "Baseline end variance" (BaselineEndVarianceColumn) & "Baseline duration variance" (BaselineDurationVarianceColumn).
By default, they show values for the first baseline, but can be configured to show values for any baseline using the field config. For example to show the duration of the second baseline:
const gantt = new Gantt({
columns : [
{ type : 'baselineduration', field : 'baselines[1].duration' }
]
});
Used in the updated baselines demo.
Gantt v5.6.0
Task project border treatment
In this release TaskModel has got new projectConstraintResolution field allowing to configure how a task should treat the project fixed border (project start or end - for a forward or backward scheduled project respectively).
A task can either respect the project border and in that case it cannot be placed before the project start (or after the project end respectively if it's scheduled backwards). Or a task can ignore the project border and be scheduled freely.
Possible values are:
honor- task respects the project border. This is the field's default value out of the box.ignore- task ignores the project border.conflict- the project triggers schedulingConflict event when a task attempts to violate its border. So if the Gantt has displaySchedulingIssueResolutionPopup enabled
(default) it displays a special popup asking user to choose an appropriate resolution.
In order to change the behavior for all tasks one should adjust the field default value. Here, for example, we specify change the value to conflict to ask user's opinion every time a task violates the project border:
class Task extends TaskModel {
static fields = [
// enable project border constraint check
{ name : 'projectConstraintResolution', defaultValue : 'conflict' }
];
}
new Gantt {
project : {
// Let the Project know we want to use our custom Task model
taskModelClass : Task,
...
},
...
}
The projectConstraintResolution field is also represented on UI level with new "Project border" combobox on the task editor "Advanced" tab.
New npm packages for combining products
This release introduces a new set of npm packages and framework components, that allows combining multiple Bryntum products in the same application.
These packages contain the product specific code only, as opposed to the current packages that has all code for the products each product builds upon (for example Scheduler contains all code from Grid & Core).
The new packages are called thin packages, and moving forward it will be the recommended way of using Bryntum products in npm based applications (for all supported frameworks). The packages are initially available for licensed users only, but will be made available for trial users in the near future.
The following packages are available:
| Package | Purpose |
|---|---|
| @bryntum/core-thin | Bryntum Core data and UI components package |
| @bryntum/grid-thin | Bryntum Grid component package |
| @bryntum/scheduler-thin | Bryntum Scheduler component package |
| @bryntum/schedulerpro-thin | Bryntum Scheduler Pro component package |
| @bryntum/gantt-thin | Bryntum Gantt component package |
| @bryntum/calendar-thin | Bryntum Calendar component package |
| @bryntum/taskboard-thin | Bryntum TaskBoard component package |
| @bryntum/engine-thin | Bryntum Scheduling engine component package |
Applications should install packages for the products they use, and the products those are built upon (see links to guides below for more information). For example an application using Scheduler Pro should also install Scheduler, Grid & Core:
npm install @bryntum/core-thin @bryntum/grid-thin @bryntum/scheduler-thin @bryntum/schedulerpro-thin
There are also new corresponding wrappers for the supported frameworks, which should be used instead of the current wrappers. For example for React:
| Package | Purpose |
|---|---|
| @bryntum/core-react-thin | Bryntum Core UI widgets React wrappers package |
| @bryntum/grid-react-thin | Bryntum Grid React wrapper package |
| @bryntum/scheduler-react-thin | Bryntum Scheduler React wrapper package |
| @bryntum/schedulerpro-react-thin | Bryntum Scheduler Pro React wrapper package |
| @bryntum/gantt-react-thin | Bryntum Gantt React wrapper package |
| @bryntum/calendar-react-thin | Bryntum Calendar React wrapper package |
| @bryntum/taskboard-react-thin | Bryntum TaskBoard React wrapper package |
Applications should install wrappers only for the products they use, there is no need to install them for the products those are built upon. For example an application using Scheduler Pro:
npm install @bryntum/schedulerpro-react-thin
More information:
Locale updates
There are some new locales used by the logic allowing to move a task before the project start date:
| Class | Locale key | English value |
|---|---|---|
| AdvancedTab | askUser | Ask user |
| AdvancedTab | honor | Honor |
| AdvancedTab | ignore | Ignore |
| AdvancedTab | projectBorder | Project border |
| HonorProjectConstraintResolution | descriptionTpl | Adjust the task to honor the project border. |
| IgnoreProjectConstraintResolution | descriptionTpl | Ignore the project border and proceed with the change. |
| ProjectConstraintConflictEffectDescription | endDescriptionTpl | You moved "{0}" task to finish on {1}. This is after the project end date {2}. |
| ProjectConstraintConflictEffectDescription | startDescriptionTpl | You moved "{0}" task to start on {1}. This is before the project start date {2}. |
Functions and events declarations for TypeScript have been improved
Declarations of class config/property functions and events (which are represented as onEventName functions) were improved to contain all available parameters and return type.
See examples below:
Old declarations:
/**
* User typed into the field. Please note that the value attached to this event is the raw input field value and
* not the combos value
*/
onInput: Function|string
/**
* Template function that can be used to customize the displayed value
*/
displayValueRenderer: Function
New declarations:
/**
* User typed into the field. Please note that the value attached to this event is the raw input field value and
* not the combos value
*/
onInput: ((event : { source: Combo, value: string, event: Event }) => void)|string
/**
* Template function that can be used to customize the displayed value
*/
displayValueRenderer: (record: Model, combo: Combo) => string|null
Client side print / PDF export
A new Print feature was added to allow printing Gantt content using the browser print dialog. It extends the PDFExport feature and uses same configs which manage HTML markup rendering (exporters, columns, paper size, etc.). The only difference is that instead of sending generated HTML to a backend, the feature creates an iframe element with generated content and opens the browser print dialog from it.
ScrollButtons feature
ScrollButtons is a new feature that injects buttons in each row, that scrolls the task bar into view when clicked. The buttons are only visible when the task bar is out of view.
const gantt = new Gantt({ appendTo : targetElement, height : 350, startDate : '2023-01-01', endDate : '2024-01-01', tickSize : 30, features : { scrollButtons : true, projectLines : false }, project : { startDate : '2023-01-08', duration : 30, events : [ { id : 1, name : 'Project A', startDate : '2023-01-01', duration : 30, expanded : true, children : [ { id : 11, name : 'Early task', startDate : '2023-01-08', duration : 1, leaf : true, manuallyScheduled : true }, { id : 12, name : 'Early task #2', startDate : '2023-01-08', duration : 1, leaf : true }, { id : 13, name : 'Late task', startDate : '2023-07-08', duration : 3, leaf : true, manuallyScheduled : true }, { id : 14, name : 'Really late task', duration : 0, startDate : '2023-11-08', leaf : true, manuallyScheduled : true } ] } ], dependencies : [{ id : 1, lag : 1, fromEvent : 11, toEvent : 12 }, { id : 2, lag : 1, fromEvent : 12, toEvent : 13 }] } }); New respectFillTicks config for TaskNonWorkingTime feature
A new respectFillTicks config was added to allow rendering to take into account the Gantt fillTicks config: