# Upgrade guide for Scheduler v3.0

## "Core" package

All our basic classes have been moved from `lib/Common/` to `lib/Core/`. Please update corresponding paths in your
application in case you import classes from sources.

## Examples

The [Validation](https://bryntum.com/products/scheduler/examples/validation/) example now uses a themeable bryntum dialog box
to confirm event changes. It also illustrates how to apply time constraints programmatically to events being dragged,
and how to validate event drop gestures, returning an explanatory message if the drop point is found to be invalid.

## Zooming and ViewPresets

The system-supplied set of [ViewPresets](#Scheduler/preset/ViewPreset), and the way these can be customized has changed
slightly.

A [ViewPreset](#Scheduler/preset/ViewPreset) describes the time units into which a timeline is split, and the on-screen
size of the lowest level unit. It also contains a definition of the layout of a set of headers to display above the
timeline.

The header definitions are now an array, `headers` instead of `headerConfig`. These are processed in top to bottom
order. This is a change from before, when they were named properties in an object with the name "middle", wherever it
was in the list, at the top or the bottom being the one which attracted the "main header" status. Now `mainHeaderLevel`
and
`columnLinesFor` are numeric configs which reference which header level is the main one, and which defines the column
lines.

### Events

Before the `ViewPreset` of a Scheduler is changed, the `beforePresetChange` event is fired. This is vetoable.
The `beforeZoomChange` is fired too, but is deprecated and will be removed in version 4.0.0 After the `ViewPreset` of a
Scheduler has changed, the `presetChange` event is fired. This is vetoable. The `zoomChange` is fired too, but is
deprecated and will be removed in version 4.0.0

The system-supplied set of ViewPresets is still the same. There is a set of
predefined [ViewPresets](#Scheduler/preset/ViewPreset)
stored in the [PresetManager](#Scheduler/preset/PresetManager), which is now
a [PresetStore](#Scheduler/preset/PresetStore).

### System-supplied ViewPresets

- `secondAndMinute` - creates 2 level header - minute and seconds within it.
- `minuteAndHour` - creates 2 level header - hour and minutes within it.
- `hourAndDay` - creates 2 level header - day and hours within it.
- `dayAndWeek` - creates 2 level header - week and days within it.
- `weekAndDay` - just like `dayAndWeek` but with different formatting.
- `weekAndDayLetter` - creates 2 level header - with weeks and day letters within it.
- `weekAndMonth` - creates 2 level header - month and weeks within it.
- `weekDateAndMonth` - creates 2 level header - month and weeks within it (weeks shown by first day only).
- `monthAndYear` - creates 2 level header - year and months within it.
- `year` - creates 2 level header - year and quarters within it.
- `manyYears` - creates 2 level header - 5-years and year within it.

### Presets automatically copied into each Scheduler

Schedulers now import an expanded, preconfigured set of these standard [ViewPresets](#Scheduler/preset/ViewPreset)
into their own [PresetStore](#Scheduler/preset/PresetStore) which is accessed
using [`myScheduler.presets`](#Scheduler/view/mixin/TimelineViewPresets#property-presets).

A [PresetStore](#Scheduler/preset/PresetStore) is automatically sorted into zoom order. That is, it is sorted according
to the widths of events rendered to the screen as a result of their configurations.

These encapsulate the zoom levels which are available. A zoom level now is a [ViewPreset](#Scheduler/preset/ViewPreset).
There is a direct mapping between a numeric zoom level and a [ViewPreset](#Scheduler/preset/ViewPreset)
in the Scheduler's [`presets`](#Scheduler/view/mixin/TimelineViewPresets#property-presets) - it is simply the index of
the [ViewPreset](#Scheduler/preset/ViewPreset) in the Scheduler's [PresetStore](#Scheduler/preset/PresetStore).

A new [ViewPreset](#Scheduler/preset/ViewPreset) may be added to a preset store which is either fully configured, or
which is based upon one of the predefined presets mentioned in the [PresetManager](#Scheduler/preset/PresetManager)'s
API docs.

So you may extend existing preset and set it as current preset:

```javascript
myScheduler.viewPreset = {
    base            : 'hourAndDay', // Based on existing preset id. See above or PresetManager docs
    id              : 'MyHourAndDay',
    tickWidth       : 25,
    columnLinesFor  : 0,
    mainHeaderLevel : 1,
    headers         : [
        {
            unit       : 'd',
            align      : 'center',
            dateFormat : 'ddd DD MMM'
        },
        {
            unit       : 'h',
            align      : 'center',
            dateFormat : 'HH'
        }
    ]
};
```

This adds a new [ViewPreset](#Scheduler/preset/ViewPreset) to the `Scheduler` which is a slightly reconfigured version
of the system-supplied
`hourAndDay` preset. It will be inserted into the store in zoom-level order, and will become another zoom level.

Or you can add new preset to the store and set as current preset later if needed:

```javascript
myScheduler.presets.add({
    base            : 'hourAndDay', // Based on existing preset id. See above or PresetManager docs
    id              : 'MyHourAndDay',
    tickWidth       : 25,
    columnLinesFor  : 0,
    mainHeaderLevel : 1,
    headers         : [
        {
            unit       : 'd',
            align      : 'center',
            dateFormat : 'ddd DD MMM'
        },
        {
            unit       : 'h',
            align      : 'center',
            dateFormat : 'HH'
        }
    ]
});

myScheduler.viewPreset = 'MyHourAndDay';
```

Notice that the headers are now configured as an array, and are displayed in the order specified.

The attachment of column lines to a specific header level is now explicit rather than relying on which header was
configured under the property name "middle".

### zoomLevels config

Scheduler's zoomLevels config was removed. Scheduler's [PresetStore](#Scheduler/preset/PresetStore) is now the source of
zoom levels. To see specified levels you can dump in console:

```javascript
myScheduler.presets.records
```

The line above returns an array of ViewPreset objects used for zooming.

To specify zoom levels for a Scheduler, you need to
specify [scheduler.presets](#Scheduler/view/mixin/TimelineViewPresets#config-presets), for example:

```javascript
const myScheduler = new Scheduler({
    presets    : [
        {
            base : 'hourAndDay',
            id   : 'MyHourAndDay',
            // other preset configs....
        },
        {
            base : 'weekAndMonth',
            id   : 'MyWeekAndMonth',
            // other preset configs....
        }
    ],
    viewPreset : 'MyHourAndDay',
    // other scheduler configs....
});
```

So Scheduler is configured with 2 view presets (2 zoom levels) and one of them is set as the default.

The same can be done on the fly:

```javascript
const myScheduler = new Scheduler({
    viewPreset : 'hourAndDay',
    // other scheduler configs....
});

myScheduler.presets.removeAll();

myScheduler.presets.add([
    {
        base : 'hourAndDay',
        id   : 'MyHourAndDay'
    },
    {
        base : 'weekAndMonth',
        id   : 'MyWeekAndMonth'
    }
]);

myScheduler.viewPreset = 'MyHourAndDay';
```

### Making application-wide changes

For an application-wide addition of a new preset, use

```javascript
const addedPresets = PresetManager.add({
    id              : 'myNewPreset',
    base            : 'hourAndDay', // Based on an existing preset
    tickWidth       : 25,
    columnLinesFor  : 0,
    mainHeaderLevel : 1,
    headers         : [
        {
            unit       : 'd',
            align      : 'center',
            dateFormat : 'ddd DD MMM'
        },
        {
            unit       : 'h',
            align      : 'center',
            dateFormat : 'HH'
        }
    ]
});
```

After that, all **new** created Schedulers will contain that preset in
their [presets](#Scheduler/view/mixin/TimelineViewPresets#property-presets)
which returns Scheduler's [PresetStore](#Scheduler/preset/PresetStore), and it will be part of the available zoom
levels. Not forgetting, above: _"A PresetStore is automatically sorted into zoom order"_.

```javascript
const myNewScheduler = new Scheduler({
    viewPreset : 'myNewPreset',
    // other scheduler configs....
});
```

Note, changes applied to [PresetManager](#Scheduler/preset/PresetManager) store do not affect existing
Scheduler's [PresetStore](#Scheduler/preset/PresetStore). You could then either add that new preset directly to a
Scheduler:

```javascript
myScheduler.presets.add(addedPresets);
```

Or add a preset to a Scheduler using `base : 'myNewPreset'` to create a slightly modified version of that preset:

```javascript
myScheduler.presets.add({
    base : 'myNewPreset',
    id   : 'TheNewestPreset'
    // other preset configs....
});
```

### Examples

**Old code:**

```javascript
PresetManager.registerPreset('dayNightShift', {
    tickWidth         : 35,
    rowHeight         : 32,
    displayDateFormat : 'HH:mm',
    shiftIncrement    : 1,
    shiftUnit         : 'day',
    timeResolution    : {
        unit      : 'minute',
        increment : 15
    },
    defaultSpan  : 24,
    headerConfig : {
        bottom : {
            unit       : 'hour',
            increment  : 1,
            dateFormat : 'H'
        },
        middle : {
            unit      : 'hour',
            increment : 12,
            renderer(startDate, endDate, headerConfig, cellIdx) {
                if (startDate.getHours() === 0) {
                    // Setting a custom CSS on the header cell element
                    headerConfig.headerCellCls = 'b-fa b-fa-moon';
                    return DateHelper.format(startDate, 'MMM DD') + ' Night Shift';
                }
                else {
                    // Setting a custom CSS on the header cell element
                    headerConfig.headerCellCls = 'b-fa b-fa-sun';
                    return DateHelper.format(startDate, 'MMM DD') + ' Day Shift';
                }
            }
        },
        top : {
            unit       : 'day',
            increment  : 1,
            dateFormat : 'MMMM Do YYYY'
        }
    }
});
```

**New code:**

```javascript
PresetManager.registerPreset('dayNightShift', {
    name              : 'Day/night shift (custom)',
    tickWidth         : 35,
    rowHeight         : 32,
    displayDateFormat : 'HH:mm',
    shiftIncrement    : 1,
    shiftUnit         : 'day',
    timeResolution    : {
        unit      : 'minute',
        increment : 15
    },
    defaultSpan     : 24,
    mainHeaderLevel : 1,
    headers         : [
        {
            unit       : 'day',
            increment  : 1,
            dateFormat : 'MMMM Do YYYY'
        },
        {
            unit      : 'hour',
            increment : 12,
            renderer(startDate, endDate, headerConfig, cellIdx) {
                if (startDate.getHours() === 0) {
                    // Setting a custom CSS on the header cell element
                    headerConfig.headerCellCls = 'b-fa b-fa-moon';
                    return DateHelper.format(startDate, 'MMM DD') + ' Night Shift';
                }
                else {
                    // Setting a custom CSS on the header cell element
                    headerConfig.headerCellCls = 'b-fa b-fa-sun';
                    return DateHelper.format(startDate, 'MMM DD') + ' Day Shift';
                }
            }
        },
        {
            unit       : 'hour',
            increment  : 1,
            dateFormat : 'H'
        }
    ]
});
```

Notice that the headers are configured as an array, and instead of the property name `middle` attracting "main header"
status, and therefore dictating the tick size, the `mainHeaderLevel` is explicitly defined as an index.

## Export to PDF/PNG

Added a new feature which allows to export Scheduler content into a PDF/PNG file. Several new demos are added: a generic
JS example and demos for Angular, React and Vue frameworks. The feature uses a special export server, that can be found
in `examples/_shared/server` folder. For more instruction please see `README.md` file in the demos folders.


<p class="last-modified">Last modified on 2023-10-26 8:19:20</p>