NestedEvents
Feature
A feature that renders child events nested inside their parent. Requires Scheduler Pro to use a tree event store (normally handled automatically when events in data has children).
const schedulerPro = new SchedulerPro({ appendTo : targetElement, // makes scheduler as high as it needs to be to fit rows autoHeight : true, features : { nestedEvents : true }, rowHeight : 160, startDate : new Date(2022, 2, 20), endDate : new Date(2022, 2, 27), columns : [ { field : 'name', text : 'Name', width : 100 } ], project : { resources : [ { id : 1, name : 'Bruce' }, { id : 2, name : 'Diana' } ], events : [ { id : 1, name : 'Art project', startDate : '2022-03-21', duration : 5, children : [ { id : 11, name : 'Get supplies', startDate : '2022-03-21', duration : 2 }, { id : 12, name : 'Sketch', startDate : '2022-03-22', duration : 1, eventColor : 'indigo' }, { id : 13, name : 'Outline', startDate : '2022-03-22', duration : 2, eventColor : 'blue' }, { id : 14, name : 'Ink', startDate : '2022-03-23', duration : 2, eventColor : 'violet' }, { id : 15, name : 'Share', startDate : '2022-03-24', duration : 2, eventColor : 'pink' } ] }, { id : 2, name : 'DIY project', startDate : '2022-03-24', duration : 5, children : [ { id : 21, name : 'Plan', startDate : '2022-03-21', duration : 1, eventColor : 'indigo' }, { id : 22, name : 'Get supplies', startDate : '2022-03-22', duration : 2 }, { id : 23, name : 'Prototype', startDate : '2022-03-22', duration : 3, eventColor : 'violet' }, { id : 24, name : 'Make', startDate : '2022-03-24', duration : 1, eventColor : 'blue' } ] } ], assignments : [ { id : 1, event : 1, resource : 1 }, { id : 2, event : 11, resource : 1 }, { id : 3, event : 12, resource : 1 }, { id : 4, event : 13, resource : 1 }, { id : 5, event : 14, resource : 1 }, { id : 6, event : 15, resource : 1 }, { id : 7, event : 2, resource : 2 }, { id : 8, event : 21, resource : 2 }, { id : 9, event : 22, resource : 2 }, { id : 10, event : 23, resource : 2 }, { id : 11, event : 24, resource : 2 } ] }, tbar : [ { type : 'buttongroup', toggleGroup : true, rendition : 'padded', items : { none : { text : 'Overlap' }, stack : { text : 'Stack' }, pack : { text : 'Pack', pressed : true } }, onToggle({ source, pressed }) { if (pressed) { schedulerPro.features.nestedEvents.eventLayout = source.ref; } } } ] }); The feature has configs for eventLayout, resourceMargin and barMargin that are separate from those on Scheduler Pro and only affect nested events.
You can by default drag nested events out of their parents and drop any event onto root level events to nest. The drag and drop behaviour can be customized using the constrainDragToParent, allowNestingOnDrop and allowDeNestingOnDrop configs.
Parent / children scheduling
Scheduler Pro uses a scheduling engine closely related to the one used by Gantt (a subset of it). It for example schedules based on calendars (skipping non-working time), dependencies and constraints.
Scheduling parents
Part of the scheduling engines default logic is that parent events' start and end dates (and thus duration) is defined by their children. This means that if you remove the latest scheduled child of a parent, the parents end date and duration will be adjusted to match the new latest scheduled child (shrink-wrapping children).
Depending on what you plan to use nested events for in your application, this might not be the desired behaviour. If you want the parent event to keep its dates regardless of its children, you should flag it as manuallyScheduled.
A parent defined like this will shrink / grow with its children:
{
"id" : 1,
"startDate" : "2022-03-24",
"children" : [
...
]
}
Try removing an event here to see what happens:
const schedulerPro = new SchedulerPro({ appendTo : targetElement, // makes scheduler as high as it needs to be to fit rows autoHeight : true, features : { nestedEvents : true }, rowHeight : 80, startDate : new Date(2022, 2, 20), endDate : new Date(2022, 2, 27), columns : [ { field : 'name', text : 'Name', width : 100 } ], project : { resources : [ { id : 1, name : 'Dude' } ], events : [ { id : 1, name : 'Automatically scheduled', startDate : '2022-03-21', children : [ { id : 11, name : 'Start here', startDate : '2022-03-21', duration : 2 }, { id : 12, name : 'End here', startDate : '2022-03-24', duration : 2, eventColor : 'orange' } ] } ], assignments : [ { id : 1, event : 1, resource : 1 }, { id : 2, event : 11, resource : 1 }, { id : 3, event : 12, resource : 1 } ] } }); A parent with manuallyScheduled : true will not shrink / grow with is children:
{
"id" : 1,
"startDate" : "2022-03-24",
"duration" : 10,
"manuallyScheduled" : true
"children" : [
...
]
}
Try the same thing here:
const schedulerPro = new SchedulerPro({ appendTo : targetElement, // makes scheduler as high as it needs to be to fit rows autoHeight : true, features : { nestedEvents : true }, rowHeight : 80, startDate : new Date(2022, 2, 20), endDate : new Date(2022, 2, 27), columns : [ { field : 'name', text : 'Name', width : 100 } ], project : { resources : [ { id : 1, name : 'Guy' } ], events : [ { id : 1, name : 'Manually scheduled', startDate : '2022-03-21', duration : 5, manuallyScheduled : true, children : [ { id : 11, name : 'Start here?', startDate : '2022-03-21', duration : 2 }, { id : 12, name : 'End here?', startDate : '2022-03-24', duration : 2, eventColor : 'orange' } ] } ], assignments : [ { id : 1, event : 1, resource : 1 }, { id : 2, event : 11, resource : 1 }, { id : 3, event : 12, resource : 1 } ] } }); manuallyScheduled: trueDrag and drop for parent events
Normally the dates of a parent event is defined by its children (as described above), with exception for when drag dropping a parent event along the time axis. In this case the operation will update the dates of all the children, which will thus also move the parent event in time.
If a parent event is dragged to a new resource, all its children will also be assigned to that resource.
Scheduling children (nested events)
Nested events are scheduled using much of the same logic as normal/parent events, but with some differences:
-
To maintain the relative position in time of nested events within their parent, they utilize a
delayFromParentfield. The field accepts a magnitude ofdurationUnit(defaults to days). Sample dataset (note that supplyingdelayFromParentis optional, see the next bullet):{ "name" : "Parent", "startDate" : "2023-08-21", // Monday "children" : [ { "name" : "Child 1", "delayFromParent" : 0 } { "name" : "Child 2", "delayFromParent" : 2 } ] }Parent starts on 2023-08-21, the first child will start on the same date, the second child will start 2 days later (2023-08-23).
If the parent instead started on a Friday, the outcome would be that the second child starts 2 working days later, which would be the following Tuesday.
Note that when supplying
delayFromParentin data, since parent events shrink wrap their children, the earliest child must have"delayFromParent": 0. -
If
delayFromParentis not present in the loaded data, the field is calculated asnestedStart - parentStart - non-working time(for example if parent starts on a Friday, and nested event on a Monday,delayFromParentwill be 1). Sample dataset:{ "name" : "Parent", "startDate" : "2023-08-21", // Monday "children" : [ { "name" : "Child 1", "startDate" : "2023-08-21" }, { "name" : "Child 2", "startDate" : "2023-08-23" } ] }Yields the same result as above, parent and first child starts on 2023-08-21, second child starts 2 days later. Child 1 gets
delayFromParent: 0and child 2 getsdelayFromParent: 2from the calculation.
Dependencies
Nested events support dependencies, with some caveats:
- Dependency lines are by default drawn on the top of events, instead of behind them. This is to ensure they are visible when drawn into a parent (or fully within one). The Dependencies feature can be configured with drawAroundParents set to
trueto instead attempt to draw around parents when possible. - When using dependencies, the body of parent events with overflowing nested children is not scrollable. This is because there is no tracking of the scrolling of parent events, and thus dependency lines would not be drawn correctly on scroll. If your app only needs dependencies between parents, you can configure allowCreateOnlyParent as
true. In this case, their container is scrollable. - Dependencies are only supported for one level of nesting (with
maxNesting: 1, which is the default).
const schedulerPro = new SchedulerPro({ appendTo : targetElement, autoHeight : true, features : { nestedEvents : true, dependencies : { terminalOffset : 8 } }, rowHeight : 90, startDate : new Date(2022, 2, 20), endDate : new Date(2022, 2, 27), project : { resources : [ { id : 1, name : 'Bruce' } ], events : [ { id : 1, name : 'Art project', startDate : '2022-03-21', duration : 5, children : [ { id : 11, name : 'Supplies', delayFromParent : 0, duration : 1 }, { id : 12, name : 'Sketch', delayFromParent : 1, duration : 1, eventColor : 'indigo' }, { id : 13, name : 'Outline', delayFromParent : 2, duration : 1, eventColor : 'blue' } ] } ], assignments : [ { id : 1, event : 1, resource : 1 }, { id : 2, event : 11, resource : 1 }, { id : 3, event : 12, resource : 1 }, { id : 4, event : 13, resource : 1 } ], dependencies : [ { id : 1, from : 11, to : 12, lag : 1 }, { id : 2, from : 12, to : 13, lag : 0.5 } ] } }); Caveats
Usage of the feature comes with some requirements/caveats:
- As already mentioned, it requires a tree event store
- Requires using an AssignmentStore, the legacy single assignment mode does not handle tree stores
- Scheduler must use stack or overlap eventLayout, pack not supported
- Multi event drag is not supported for nested events
- Cannot EventDragCreate within parent events
- Labels are not supported for nested events
- EventBuffer won't work with nested events
- TaskEdit does not allow assigning resources or dependencies to nested events
- Does not work in combination with recurring events
This feature is disabled by default. For info on enabling it, see GridFeatures.
Useful configs
| Config | Description |
|---|---|
| eventLayout | Layout mode for nested events (pack/stack/none) |
| barMargin | Vertical space between nested event bars |
| headerHeight | Space reserved for parent event title |
| constrainDragToParent | Keep nested events inside their parent on drag |
| allowNestingOnDrop | Allow nesting by dropping on another event |
| maxNesting | Maximum nesting depth for events |
See also
- EventModel — The event model, supports parent/child tree structure
- SchedulerPro — The Scheduler Pro view this feature plugs into
- EventDrag — Event drag feature that coordinates with nested event behavior
Configs
Configs are options you supply in a configuration object when creating an instance of this class-
Vertical (horizontal in vertical mode) space between nested event bars, in px
Has a corresponding runtime barMargin property.
-
Constrains dragging of nested events within their parent when configured as
true, allows them to be dragged out of it when configured asfalse(the default).Has a corresponding runtime constrainDragToParent property.
-
This config defines how to handle overlapping nested events. Valid values are:
stack, events use fixed height and stack on top of each other (not supported in vertical mode)pack, adjusts event heightnone, allows events to overlap
Note that stacking works differently for nested events as compared to normal events (and not at all in vertical mode). The height of the parent event will never change, all nested events use fixed height and will stack until all available space is consumed, after which they will overflow the parent.Also note that stacked nested events are clipped by the parent, making it scrollable on vertical overflow. This cannot be combined with sticky events. If stacking events in your app won't overflow the parent, you can specify `overflow: visible` on `.b-nested-events-container.b-nested-events-layout-stack` to not clip and make sticky events work.Has a corresponding runtime eventLayout property.
-
Internal listeners, that cannot be removed by the user.
-
The widget which this plugin is to attach to.
Has a corresponding runtime client property.
-
Set to
falseto disable localization of this object.
Properties
Properties are getters/setters or publicly accessible variables on this class-
Identifies an object as an instance of Events class, or subclass thereof.
-
Identifies an object as an instance of Localizable class, or subclass thereof.
-
Identifies an object as an instance of NestedEvents class, or subclass thereof.
-
A class property getter for the default values of internal properties for this class.
-
Vertical (horizontal in vertical mode) space between nested event bars, in px
Has a corresponding barMargin config.
-
Constrains dragging of nested events within their parent when configured as
true, allows them to be dragged out of it when configured asfalse(the default).Has a corresponding constrainDragToParent config.
-
This config defines how to handle overlapping nested events. Valid values are:
stack, events use fixed height and stack on top of each other (not supported in vertical mode)pack, adjusts event heightnone, allows events to overlap
Note that stacking works differently for nested events as compared to normal events (and not at all in vertical mode). The height of the parent event will never change, all nested events use fixed height and will stack until all available space is consumed, after which they will overflow the parent.Also note that stacked nested events are clipped by the parent, making it scrollable on vertical overflow. This cannot be combined with sticky events. If stacking events in your app won't overflow the parent, you can specify `overflow: visible` on `.b-nested-events-container.b-nested-events-layout-stack` to not clip and make sticky events work.Has a corresponding eventLayout config.
-
An empty array that can be used as a default value.
-
An empty object that can be used as a default value.
-
Identifies an object as an instance of InstancePlugin class, or subclass thereof.
-
Identifies an object as an instance of NestedEvents class, or subclass thereof.
-
Returns a copy of the full configuration which was used to configure this object.
-
This property is set to
truebefore theconstructorreturns. -
This property is set to
trueon entry to the destroy method. It remains on the objects after returning fromdestroy(). If isDestroyed istrue, this property will also betrue, so there is no need to test for both (for example,comp.isDestroying || comp.isDestroyed). -
The Widget which was passed into the constructor, which is the Widget we are providing extra services for.
Has a corresponding client config.
-
Get the global LocaleHelper
-
Get the global LocaleManager
Functions
Functions are methods available for calling on the class-
This optional class method is called when a class is mixed in using the mixin() method.
-
Registers this class type with its Factory
-
Internal function used to hook destroy() calls when using thisObj
-
Internal function used restore hooked destroy() calls when using thisObj
-
Auto detaches listeners registered from start, if set as detachable
-
Internal function used to run a callback function after an event is triggered
-
Removes all listeners registered to this object by the application.
-
This will merge a feature's (subclass of InstancePlugin) keyMap with it's client's keyMap.
Events
Events are triggered for certain actions in this class and can be listened for to react to those actions in your codeEvent handlers
Event handlers are callbacks called as a result of certain actions in this classCSS variables
CSS variables that can be set to adjust appearance| Name | Description | |
|---|---|---|
| --b-nested-events-container-background | Nested event container background (below the header in a parent event) | |
| --b-nested-events-header-height | Parent event header height, to display the name at top | |
| --b-nested-events-parent-background | Parent event background | |
| --b-nested-events-parent-color | Parent event color | |
| Hovered | ||
| --b-nested-events-container-hover-background | Nested event container hover background | |
| --b-nested-events-parent-hover-background | Parent event hover background | |
| Selected | ||
| --b-nested-events-container-selected-background | Nested event container selected background | |
| --b-nested-events-parent-selected-background | Parent event selected background | |