Our pure JavaScript Scheduler component


Post by coen »

Hi,

We've noted a bug with the width of the displayed timespans when using a custom view where the smallest timeaxis unit is a week. Below you can find two examples on how to reproduce this in the demo's. The examples all contain two day timespans (the first with two seperate days beside eachother and the second a recurrance rule for two days).

Reproduction steps:

    • Copy the code
    • Move to the right a few time (e.g. 5 times)
    • Move back a few times till you see the issue like in the image below.
    • As you can see the days around week 43 are much broader then the ones around it. Also the position is off.

Image
click back a few times more and it displays as it should again
Image

import { DateHelper, EventModel, Scheduler, PresetManager } from '../../build/scheduler.module.js?486179';
import shared from '../_shared/shared.module.js?486179';

//region Presets & Widgets

PresetManager.registerPreset('dayNightShift', {
    name              : 'Day/night shift (custom)',
    tickWidth         : 50,
    displayDateFormat : 'HH:mm',
    shiftIncrement    : 1,
    shiftUnit         : 'month',
    timeResolution    : {
        unit      : 'week',
        increment : 1
    },
    defaultSpan     : 6,
    mainHeaderLevel : 1,
    headers         : [
    {
        "unit": "year",
        "dateFormat": "YYYY",
        "align":"start"
    },
    {
        "unit": "month",
        "dateFormat": "MMM",
        "align":"start"
    },
    {
        "unit": "week",
        "dateFormat": "Wp0",
         "align":"start"
    }
]
});

PresetManager.registerPreset('weekNumberAndYear', {
    name              : 'Year/week number',
    tickWidth         : 35,
    displayDateFormat : '{w.}W YYYY',
    shiftIncrement    : 1,
    shiftUnit         : 'year',
    timeResolution    : {
        unit      : 'd',
        increment : 1
    },
    defaultSpan     : 24,
    mainHeaderLevel : 1,
    headers         : [
        {
            unit       : 'y',
            increment  : 1,
            dateFormat : 'YYYY'
        },
        {
            unit       : 'w',
            increment  : 1,
            dateFormat : 'WW'
        }
    ]
});

const
    requiredPresetIds = {
        secondAndMinute   : 1,
        minuteAndHour     : 1,
        hourAndDay        : 1,
        dayNightShift     : 1,
        weekAndDay        : 1,
        weekAndMonth      : 1,
        weekAndDayLetter  : 1,
        weekDateAndMonth  : 1,
        weekNumberAndYear : 1,
        monthAndYear      : 1,
        year              : 1,
        manyYears         : 1
    },
    // The set of available Presets is what provides the zoom levels.
    presets = PresetManager.records.filter(p => requiredPresetIds[p.id]);

//endregion

//region Data

const
    resources = [
        { id : 1, name : 'Arcady', role : 'Core developer', eventColor : 'purple' },
        { id : 2, name : 'Dave', role : 'Tech Sales', eventColor : 'indigo' },
        { id : 3, name : 'Henrik', role : 'Sales', eventColor : 'blue' },
        { id : 4, name : 'Linda', role : 'Core developer', eventColor : 'cyan' },
        { id : 5, name : 'Maxim', role : 'Developer & UX', eventColor : 'green' },
        { id : 6, name : 'Mike', role : 'CEO', eventColor : 'lime' },
        { id : 7, name : 'Lee', role : 'CTO', eventColor : 'orange' }
    ],
    events    = [
        {
            id          : 1,
            resourceId  : 1,
            percentDone : 60,
            startDate   : new Date(2017, 0, 1, 10),
            endDate     : new Date(2017, 0, 1, 12)
        },
        {
            id          : 2,
            resourceId  : 2,
            percentDone : 20,
            startDate   : new Date(2017, 0, 1, 12),
            endDate     : new Date(2017, 0, 1, 17)
        },
        {
            id          : 3,
            resourceId  : 3,
            percentDone : 80,
            startDate   : new Date(2017, 0, 1, 14),
            endDate     : new Date(2017, 0, 1, 16)
        },
        {
            id          : 4,
            resourceId  : 4,
            percentDone : 90,
            startDate   : new Date(2017, 0, 1, 8),
            endDate     : new Date(2017, 0, 1, 11)
        },
        {
            id          : 5,
            resourceId  : 5,
            percentDone : 40,
            startDate   : new Date(2017, 0, 1, 15),
            endDate     : new Date(2017, 0, 1, 17)
        },
        {
            id          : 6,
            resourceId  : 6,
            percentDone : 70,
            startDate   : new Date(2017, 0, 1, 16),
            endDate     : new Date(2017, 0, 1, 18)
        }
    ],
    timespans = [
    
]; //endregion class EventModelWithPercent extends EventModel { static get fields() { return [ { name : 'percentDone', type : 'number', defaultValue : 0 } ]; } } const scheduler = new Scheduler({ appendTo : 'container', resourceImagePath : '../_shared/images/users/', features : { timeRanges : true, sort : 'name' }, columns : [ { type : 'resourceInfo', text : 'Staff', width : '10em' } ], resources, eventStore : { modelClass : EventModelWithPercent, data : events }, timeRanges : [ { "id": "ts0", "startDate": "2025-08-03T22:00:00.000Z", "endDate": "2025-08-04T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts1", "startDate": "2025-08-02T22:00:00.000Z", "endDate": "2025-08-03T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts2", "startDate": "2025-08-10T22:00:00.000Z", "endDate": "2025-08-11T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts3", "startDate": "2025-08-09T22:00:00.000Z", "endDate": "2025-08-10T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts4", "startDate": "2025-08-17T22:00:00.000Z", "endDate": "2025-08-18T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts5", "startDate": "2025-08-16T22:00:00.000Z", "endDate": "2025-08-17T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts6", "startDate": "2025-08-24T22:00:00.000Z", "endDate": "2025-08-25T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts7", "startDate": "2025-08-23T22:00:00.000Z", "endDate": "2025-08-24T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts8", "startDate": "2025-08-31T22:00:00.000Z", "endDate": "2025-09-01T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts9", "startDate": "2025-08-30T22:00:00.000Z", "endDate": "2025-08-31T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts10", "startDate": "2025-09-07T22:00:00.000Z", "endDate": "2025-09-08T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts11", "startDate": "2025-09-06T22:00:00.000Z", "endDate": "2025-09-07T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts12", "startDate": "2025-09-14T22:00:00.000Z", "endDate": "2025-09-15T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts13", "startDate": "2025-09-13T22:00:00.000Z", "endDate": "2025-09-14T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts14", "startDate": "2025-09-21T22:00:00.000Z", "endDate": "2025-09-22T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts15", "startDate": "2025-09-20T22:00:00.000Z", "endDate": "2025-09-21T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts16", "startDate": "2025-09-28T22:00:00.000Z", "endDate": "2025-09-29T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts17", "startDate": "2025-09-27T22:00:00.000Z", "endDate": "2025-09-28T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts18", "startDate": "2025-10-05T22:00:00.000Z", "endDate": "2025-10-06T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts19", "startDate": "2025-10-04T22:00:00.000Z", "endDate": "2025-10-05T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts20", "startDate": "2025-10-12T22:00:00.000Z", "endDate": "2025-10-13T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts21", "startDate": "2025-10-11T22:00:00.000Z", "endDate": "2025-10-12T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts22", "startDate": "2025-10-19T22:00:00.000Z", "endDate": "2025-10-20T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts23", "startDate": "2025-10-18T22:00:00.000Z", "endDate": "2025-10-19T22:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts24", "startDate": "2025-10-26T23:00:00.000Z", "endDate": "2025-10-27T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts25", "startDate": "2025-10-25T22:00:00.000Z", "endDate": "2025-10-26T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts26", "startDate": "2025-11-02T23:00:00.000Z", "endDate": "2025-11-03T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts27", "startDate": "2025-11-01T23:00:00.000Z", "endDate": "2025-11-02T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts28", "startDate": "2025-11-09T23:00:00.000Z", "endDate": "2025-11-10T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts29", "startDate": "2025-11-08T23:00:00.000Z", "endDate": "2025-11-09T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts30", "startDate": "2025-11-16T23:00:00.000Z", "endDate": "2025-11-17T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts31", "startDate": "2025-11-15T23:00:00.000Z", "endDate": "2025-11-16T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts32", "startDate": "2025-11-23T23:00:00.000Z", "endDate": "2025-11-24T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts33", "startDate": "2025-11-22T23:00:00.000Z", "endDate": "2025-11-23T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts34", "startDate": "2025-11-30T23:00:00.000Z", "endDate": "2025-12-01T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts35", "startDate": "2025-11-29T23:00:00.000Z", "endDate": "2025-11-30T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts36", "startDate": "2025-12-07T23:00:00.000Z", "endDate": "2025-12-08T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts37", "startDate": "2025-12-06T23:00:00.000Z", "endDate": "2025-12-07T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts38", "startDate": "2025-12-14T23:00:00.000Z", "endDate": "2025-12-15T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts39", "startDate": "2025-12-13T23:00:00.000Z", "endDate": "2025-12-14T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts40", "startDate": "2025-12-21T23:00:00.000Z", "endDate": "2025-12-22T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts41", "startDate": "2025-12-20T23:00:00.000Z", "endDate": "2025-12-21T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts42", "startDate": "2025-12-28T23:00:00.000Z", "endDate": "2025-12-29T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts43", "startDate": "2025-12-27T23:00:00.000Z", "endDate": "2025-12-28T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts44", "startDate": "2026-01-04T23:00:00.000Z", "endDate": "2026-01-05T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts45", "startDate": "2026-01-03T23:00:00.000Z", "endDate": "2026-01-04T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts46", "startDate": "2026-01-11T23:00:00.000Z", "endDate": "2026-01-12T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts47", "startDate": "2026-01-10T23:00:00.000Z", "endDate": "2026-01-11T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts48", "startDate": "2026-01-18T23:00:00.000Z", "endDate": "2026-01-19T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts49", "startDate": "2026-01-17T23:00:00.000Z", "endDate": "2026-01-18T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts50", "startDate": "2026-01-25T23:00:00.000Z", "endDate": "2026-01-26T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts51", "startDate": "2026-01-24T23:00:00.000Z", "endDate": "2026-01-25T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts52", "startDate": "2026-02-01T23:00:00.000Z", "endDate": "2026-02-02T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts53", "startDate": "2026-01-31T23:00:00.000Z", "endDate": "2026-02-01T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts54", "startDate": "2026-02-08T23:00:00.000Z", "endDate": "2026-02-09T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" }, { "id": "ts55", "startDate": "2026-02-07T23:00:00.000Z", "endDate": "2026-02-08T23:00:00.000Z", "name": "", "cls": "timespan-type--default b-sch-color-gray" } ], startDate : new Date(2025, 9, 1), endDate : new Date(2025, 12, 2), // Use our custom list of just the ones we plucked out of the PresetManager presets, viewPreset : 'dayNightShift', eventRenderer : ({ eventRecord, renderData }) => { const value = eventRecord.percentDone || 0; // Add a child to the event element (b-sch-event) renderData.children.push({ className : 'value', style : { width : `${value}%` }, html : value }); }, listeners : { presetChange({ from, to }) { const me = this, { presetCombo, zoomInButton, zoomOutButton } = me.widgetMap; // To disable buttons based on zoom levels use this code: // zoomOutButton.disabled = level <= 0; // zoomInButton.disabled = level >= this.presets.length - 1; // To disable buttons based on presets in combo use this code: const index = this.presets.indexOf(to); zoomOutButton.disabled = index === 0; zoomInButton.disabled = index === presetCombo.store.count - 1; } }, tbar : [ { type : 'viewpresetcombo', width : '16em', ref : 'presetCombo', presets : presets.map(p => p.id), picker : { maxHeight : 500 } }, { type : 'button', ref : 'zoomInButton', icon : 'b-icon-search-plus', text : 'Zoom in', onClick() { scheduler.zoomIn(); } }, { type : 'button', ref : 'zoomOutButton', icon : 'b-icon-search-minus', text : 'Zoom out', onClick() { scheduler.zoomOut(); } }, { type : 'buttongroup', items : [ { type : 'button', icon : 'b-icon-previous', tooltip : 'View previous day', onAction() { scheduler.shiftPrevious(); } }, { type : 'button', ref : 'todayButton', text : 'Today', tooltip : 'View today, to see the current time line', onAction() { const today = DateHelper.clearTime(new Date()); today.setHours(5); scheduler.setTimeSpan(today, DateHelper.add(today, 18, 'hour')); } }, { type : 'button', icon : 'b-icon-next', tooltip : 'View next day', onAction() { scheduler.shiftNext(); } } ] } ] });

Another example using recurrance

import { Scheduler, DateHelper, PresetManager } from '../../build/schedulerpro.module.js?486179';
import shared from '../_shared/shared.module.js?486179';


PresetManager.registerPreset('dayNightShift', {
    name              : 'Day/night shift (custom)',
    tickWidth         : 50,
    displayDateFormat : 'HH:mm',
    shiftIncrement    : 1,
    shiftUnit         : 'month',
    timeResolution    : {
        unit      : 'week',
        increment : 1
    },
    defaultSpan     : 6,
    mainHeaderLevel : 1,
    headers         : [
    {
        "unit": "year",
        "dateFormat": "YYYY",
        "align":"start"
    },
    {
        "unit": "month",
        "dateFormat": "MMM",
        "align":"start"
    },
    {
        "unit": "week",
        "dateFormat": "Wp0",
         "align":"start"
    }
]
});

const scheduler = new Scheduler({
    appendTo          : 'container',
    eventStyle        : 'colored',
    resourceImagePath : '../_shared/images/users/',

features : {
    timeRanges : {
        showCurrentTimeLine : true,
        showHeaderElements  : false
    }
},

columns : [
    { type : 'resourceInfo', text : 'Staff', field : 'name', width : '10em' }
],
events: [],

resources: [
  {
    "id"         : "a",
    "name"       : "Rob",
    "type"       : "Sales",
    "eventColor" : "green"
  },
  {
    "id"         : "b",
    "name"       : "Mike",
    "type"       : "Sales",
    "eventColor" : "green"
  },
  {
    "id"         : "c",
    "name"       : "Kate",
    "type"       : "Sales",
    "eventColor" : "orange"
  },
  {
    "id"         : "d",
    "name"       : "Lisa",
    "type"       : "Developer",
    "eventColor" : "orange"
  },
  {
    "id"         : "e",
    "name"       : "Dave",
    "type"       : "Developer",
    "eventColor" : "blue"
  },
  {
    "id"         : "f",
    "name"       : "Arnold",
    "type"       : "Developer",
    "eventColor" : "blue"
  },
  {
    "id"         : "g",
    "name"       : "Lee",
    "type"       : "Marketing",
    "eventColor" : "violet"
  },
  {
    "id"         : "h",
    "name"       : "Jong",
    "type"       : "Marketing",
    "eventColor" : "violet"
  }
],

timeRanges:  [
  {
    "id"             : 2,
    "name"           : "",
    "recurrenceRule" : "FREQ=WEEKLY;BYDAY=SA,SU;",
    "startDate"      : "2019-02-07 00:00",
    "endDate"        : "2019-02-08 00:00"
  }
]
,

// crudManager : {
//     autoLoad  : true,
//     transport : {
//         load : {
//             url : 'data/data.json'
//         }
//     },
//     // This config enables response validation and dumping of found errors to the browser console.
//     // It's meant to be used as a development stage helper only so please set it to false for production systems.
//     validateResponse : true
// },

barMargin : 5,

startDate  : new Date(2025, 1, 7, 8),
endDate    : new Date(2025, 1, 29, 18),
viewPreset : {
    tickWidth : 50,
    base      : 'dayNightShift'
},

tbar : [
    {
        type    : 'button',
        ref     : 'addCoffeeButton',
        icon    : 'b-fa-coffee',
        text    : 'More coffee',
        tooltip : 'Click to add morning coffee to Thursdays too',
        onAction({ source : button }) {
            const coffee = scheduler.features.timeRanges.store.getById(1);

            coffee.recurrenceRule = 'FREQ=WEEKLY;BYDAY=MO,TH;';

            button.disable();
        }
    },
    '->',
    {
        type  : 'buttongroup',
        items : [
            {
                type    : 'button',
                icon    : 'b-icon-previous',
                tooltip : 'View previous day',
                cls     : 'navigate',
                onAction() {
                    scheduler.shiftPrevious();
                }
            },
            {
                type    : 'button',
                ref     : 'todayButton',
                text    : 'Today',
                tooltip : 'View today, to see the current time line',
                onAction() {
                    const today = DateHelper.clearTime(new Date());
                    today.setHours(5);
                    scheduler.setTimeSpan(today, DateHelper.add(today, 18, 'hour'));
                }
            },
            {
                type    : 'button',
                icon    : 'b-icon-next',
                tooltip : 'View next day',
                cls     : 'navigate',
                onAction() {
                    scheduler.shiftNext();
                }
            }
        ]
    },
    {
        type    : 'button',
        text    : 'Start',
        tooltip : 'Return to initial view',
        onAction() {
            scheduler.setTimeSpan(new Date(2019, 1, 7, 8), new Date(2019, 1, 29, 18));
        }
    }
]
});

Thanks,

Coen


Post by alex.l »

Hi Coen,

Thanks for the report, I've opened a ticket here https://github.com/bryntum/support/issues/11605
You can subscribe on ticket updates to be notified when it's done.

All the best,
Alex Lazarev

How to ask for help? Please read our Support Policy

We do not write the code in bounds of forum support. If you need help with development, contact us via bryntum.com/services


Post by coen »

Thanks Alex


Post Reply