Our pure JavaScript Scheduler component


Post by bhagya »

I have start date and end Date component. ON change of start date and end date scheduler strat date and end should change based on date component selected value. when i change end date as nov 30 2026 then start date as sept 1 2026, preset change handler is executing. then change date from start date as sept 10 and end date no changes preset change handler is executing.
preset value is not changed but scheduler is showing value sept 10 to dec 10. on change of start date immediately end date of scheduler is shoifting initial date difference.
I have added zoom to date component selected dates but still preset change is executing .
as per attachment selected dates is different and scheduler date is different.
Note: Frist time date change sept 1 to nov 30 its working fine. next immediate change from sept 22 to nov 30 not working as expected

Attachments
Screenshot 2025-11-03 at 11.10.00 PM.png
Screenshot 2025-11-03 at 11.10.00 PM.png (65.69 KiB) Viewed 2296 times

Post by marcio »

Hey bhagya,

Thanks for reaching out, and welcome to our forums! :)

Could you please provide a runnable test case showing how you update the dates and your Scheduler config (viewPreset, autoAdjustTimeAxis, any ViewPresetCombo settings), so we can debug properly?

In the meantime, you can try to set both dates together to avoid Scheduler recalculating span using scheduler.setTimeSpan(startDate, endDate). See setTimeSpan documentation.

Best regards,
Márcio

How to ask for help? Please read our Support Policy


Post by bhagya »


  const {
    resourcesData,
    eventsEndDate,
    eventsStartDate,
    resourceTimeRanges,
    handlePresetChange,
    weekStartDay,
    columns,
  } = props;
  
useEffect(() => { if (window.bryntum && window.bryntum.schedulerpro) { const { SchedulerPro } = window.bryntum.schedulerpro; const { PresetManager, CalendarModel } = window.bryntum.schedulerpro; const customCalendar = new CalendarModel({ intervals: [ { isWorking: true, }, ], name: 'CustomCalendar', }); schedulerInstance.current = new SchedulerPro({ appendTo: schedulerRef.current, columns, createEventOnDblClick: false, endDate: eventsEndDate, eventLayout: 'stack', eventRenderer({ eventRecord, renderData, resourceRecord }) { return eventRecord.name; }, features: { cellMenu: { items: { removeRow: false, }, processItems(e) { props.handleCreateContextMenu(e); }, }, dependencies: false, eventDrag: true, eventDragCreate: false, eventMenu: { items: { copyEvent: false, cutEvent: false, deleteEvent: false, editEvent: false, eventColor: false, showDetails: false, splitEvent: false, unassignEvent: false, }, }, eventResize: true, eventTooltip: { hideOnDelegateChange: true, showOnClick: true, showOnHover: false, }, nonWorkingTime: false, pan: true, resourceTimeRanges: true, scheduleMenu: { items: { addEvent: false, // on copy/paste cut paste, right click hide addevent. }, }, scheduleTooltip: false, timeRanges: true, tree: true, }, presets: getSupportedPresets(), project: { calendar: customCalendar, }, scrollEventPromise: true, subGridConfigs: { locked: { maxWidth: 420, minWidth: 210, overflowX: 'auto', }, }, resources: resourcesData, resourceTimeRanges, rowHeight: 35, startDate: eventsStartDate, timeAxis: { end: eventsEndDate, start: eventsStartDate, }, timeAxisViewModel: { displayDateFormat: 'h:mm A', }, viewPreset: { base: 'hourAndDay', tickWidth: 35, }, weekStartDay, }); schedulerInstance.current.setTimeSpan( eventsStartDate, eventsEndDate, ); schedulerInstance.current.on({ presetChange: (e) => { console.log('***************presetChange event:', e.preset.id); if (handlePresetChange) { handlePresetChange(e); } }, }); if (props.onSchedulerReady) { props.onSchedulerReady(schedulerInstance.current); } return () => { if (schedulerInstance.current && typeof schedulerInstance.current.destroy === 'function') { schedulerInstance.current.destroy(); schedulerInstance.current = null; } }; } }, []);

from parent component getting eventStartDate(startdate) and eventEnddate(end)
preset handler is not working as expected


Post by tasnim »

Could you please share a runnable test case (app without node_modules folder that is runnable with npm i & npm start) that we can download/run locally and see what's wrong?

Best regards,
Tasnim

How to ask for help? Please read our Support Policy


Post by bhagya »

import shared from '../_shared/shared.module.js?488814';
import { DateHelper, SchedulerEventModel, Scheduler, PresetManager } from '../../build/schedulerpro.module.js?488814';
 
//region Presets & Widgets
const hourFormat="h A";
const dayNightFormat="A";
var startDate = new Date();
var endDate = new Date();
export const displayDateFormat = format => {
  const dateDisplayMapper = {
    AM: "AM",
    PM: "PM",
    Monday: 'Monday',
    Tuesday: 'Tuesday',
    Wednesday: 'Wednesday',
    Thursday:  'Thursday',
    Friday:  'Friday',
    Saturday:  'Saturday',
    Sunday:  'Sunday',
    January: 'January',
    February: 'February',
    March: 'March',
    April:  'April',
    May: 'May',
    June: 'June',
    July: 'July',
    August: 'August',
    September: 'September',
    October: 'October',
    November: 'November',
    December: 'December',
    Mon:  'Mon',
    Tue: 'Tue',
    Wed:  'Wed',
    Thu: 'Thu',
    Fri: 'Fri',
    Sat: 'Sat',
    Sun: 'Sun',
    Jan: 'Jan',
    Feb: 'Feb',
    Mar: 'Mar',
    Apr: 'Apr',
    May: 'May',
    Jun: 'Jun',
    Jul: 'Jul',
    Aug: 'Aug',
    Sep: 'Sep',
    Oct: 'Oct',
    Nov: 'Nov',
    Dec: 'Dec',
  };
  return format.replace(
    /AM|PM|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday|January|February|March|April|May|June|July|August|September|October|November|December|Mon|Tue|Wed|Thu|Fri|Sat|Sun|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec/gi,
    format => dateDisplayMapper[format],
  );
};
PresetManager.registerPreset('minuteAndHour-50by60', {
  timeResolution: {
    unit: 'minute',
    increment: 5,
  },
  headers: [
    {
      increment: 1,
      unit: 'hour',
      dateFormat: `dddd MMMM DD YYYY, ${hourFormat}`,
      renderer: start => displayDateFormat(
        DateHelper.format(start, `dddd MMMM DD YYYY, ${hourFormat}`),
      ),
    },
    {
      unit: 'minute',
      increment: 5,
      dateFormat: 'mm',
      renderer: start => displayDateFormat(DateHelper.format(start, 'mm')),
    },
  ],
  id: 'minuteAndHour-50by60',
  rowHeight: 24,
  name: 'Minutes',
  tickWidth: 50,
  tickHeight: 100,
  displayDateFormat: `dddd MMMM DD YYYY, ${hourFormat}`,
  shiftIncrement: 1,
  shiftUnit: 'hour',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('minuteAndHour-30by60', {
  timeResolution: {
    unit: 'minute',
    increment: 5,
  },
  headers: [
    {
      increment: 1,
      unit: 'hour',
      dateFormat: `dddd MMMM DD YYYY, ${hourFormat}`,
      renderer: start => displayDateFormat(
        DateHelper.format(start, `dddd MMMM DD YYYY, ${hourFormat}`),
      ),
    },
    {
      unit: 'minute',
      increment: 5,
      dateFormat: 'mm',
      renderer: start => displayDateFormat(DateHelper.format(start, 'mm')),
    },
  ],
  id: 'minuteAndHour-30by60',
  rowHeight: 24,
  name: 'Minutes',
  tickWidth: 30,
  tickHeight: 60,
  displayDateFormat: `dddd MMMM DD YYYY, ${hourFormat}`,
  shiftIncrement: 1,
  shiftUnit: 'hour',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('hourAndDay-38by60-2', {
  timeResolution: {
    unit: 'minute',
    increment: 45,
  },
  headers: [
    {
      increment: 1,
      unit: 'day',
      dateFormat: 'dddd MMMM DD, YYYY',
      renderer: start => displayDateFormat(DateHelper.format(start, 'dddd MMMM DD, YYYY')),
    },
    {
      increment: 2,
      unit: 'hour',
      dateFormat: `${hourFormat}`,
      renderer: start => displayDateFormat(DateHelper.format(start, `${hourFormat}`)),
    },
  ],
  id: 'hourAndDay-38by60-2',
  rowHeight: 24,
  name: 'Hours',
  tickWidth: 38,
  tickHeight: 60,
  displayDateFormat: 'dddd MMMM DD, YYYY',
  shiftIncrement: 1,
  shiftUnit: 'day',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('hourAndDay-38by60-4', {
  timeResolution: {
    unit: 'hour',
    increment: 1,
  },
  headers: [
    {
      increment: 1,
      unit: 'day',
      dateFormat: 'dddd MMMM DD, YYYY',
      renderer: start => displayDateFormat(DateHelper.format(start, 'dddd MMMM DD, YYYY')),
    },
    {
      increment: 4,
      unit: 'hour',
      dateFormat: `${hourFormat}`,
      renderer: start => displayDateFormat(DateHelper.format(start, `${hourFormat}`)),
    },
  ],
  id: 'hourAndDay-38by60-4',
  rowHeight: 24,
  name: 'Hours',
  tickWidth: 38,
  tickHeight: 60,
  displayDateFormat: 'dddd MMMM DD, YYYY',
  shiftIncrement: 1,
  shiftUnit: 'day',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('hourAndDay-56by60', {
  timeResolution: {
    unit: 'hour',
    increment: 1,
  },
  headers: [
    {
      increment: 1,
      unit: 'day',
      dateFormat: 'ddd MMM DD, YY',
      renderer: start => displayDateFormat(DateHelper.format(start, 'ddd MMM DD, YY')),
    },
    {
      increment: 12,
      unit: 'hour',
      dateFormat: `${dayNightFormat}`,
      renderer: start => displayDateFormat(DateHelper.format(start, `${dayNightFormat}`)),
    },
  ],
  id: 'hourAndDay-56by60',
  rowHeight: 24,
  name: 'Hours',
  tickWidth: 56,
  tickHeight: 60,
  displayDateFormat: 'ddd MMM DD, YY',
  shiftIncrement: 1,
  shiftUnit: 'day',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('monthAndDay-40by60', {
  timeResolution: {
    unit: 'hour',
    increment: 1,
  },
  headers: [
    {
      increment: 1,
      unit: 'month',
      dateFormat: 'MMMM YYYY',
      renderer: start => displayDateFormat(DateHelper.format(start, 'MMMM YYYY')),
    },
    {
      unit: 'day',
      increment: 1,
      dateFormat: 'ddd DD',
      renderer: start => displayDateFormat(DateHelper.format(start, 'ddd DD')),
    },
  ],
  id: 'monthAndDay-40by60',
  rowHeight: 24,
  name: 'Days',
  tickWidth: 44,
  tickHeight: 60,
  displayDateFormat: 'MMMM YYYY',
  shiftIncrement: 1,
  shiftUnit: 'day',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('monthAndDay-20by60', {
  timeResolution: {
    unit: 'hour',
    increment: 4,
  },
  headers: [
    {
      increment: 1,
      unit: 'month',
      dateFormat: 'MMMM YYYY',
      renderer: start => displayDateFormat(DateHelper.format(start, 'MMMM YYYY')),
    },
    {
      unit: 'day',
      increment: 1,
      dateFormat: 'DD',
      renderer: start => displayDateFormat(DateHelper.format(start, 'DD')),
    },
  ],
  id: 'monthAndDay-20by60',
  rowHeight: 24,
  name: 'Days',
  tickWidth: 20,
  tickHeight: 60,
  displayDateFormat: 'MMMM YYYY',
  shiftIncrement: 1,
  shiftUnit: 'month',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('weekAndMonth', {
  timeResolution: {
    unit: 'week',
  },
  headers: [
    {
      unit: 'month',
      dateFormat: 'MMMM YYYY',
      renderer: start => displayDateFormat(DateHelper.format(start, 'MMMM YYYY')),
      align: 'start',
    },
    {
      unit: 'week',
      dateFormat: 'W',
      renderer: start => `W${DateHelper.format(start, 'W')}`,
    },
  ],
  id: 'weekAndMonth',
  rowHeight: 24,
  name: 'Weeks',
  tickWidth: 75,
  tickHeight: 60,
  displayDateFormat: 'MMMM YYYY',
  shiftIncrement: 1,
  shiftUnit: 'month',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('weekAndMonth-36by60', {
  timeResolution: {
    unit: 'week',
  },
  headers: [
    {
      unit: 'month',
      dateFormat: 'MMMM YYYY',
      renderer: start => displayDateFormat(DateHelper.format(start, 'MMMM YYYY')),
      align: 'start',
    },
    {
      unit: 'week',
      dateFormat: 'W',
      renderer: start => `W${DateHelper.format(start, 'W')}`,
    },
  ],
  id: 'weekAndMonth-36by60',
  rowHeight: 24,
  name: 'Weeks',
  tickWidth: 35,
  tickHeight: 60,
  displayDateFormat: 'MMMM YYYY',
  shiftIncrement: 1,
  shiftUnit: 'month',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('monthAndYear', {
  timeResolution: {
    unit: 'quarter',
  },
  headers: [
    {
      unit: 'quarter',
      dateFormat: 'Q',
      renderer: start => `Q${DateHelper.format(start, 'Q, YYYY')}`,
      align: 'start',
    },
    {
      unit: 'month',
      increment: 1,
      dateFormat: 'MMMM',
      renderer: start => displayDateFormat(DateHelper.format(start, 'MMMM')),
    },
  ],
  id: 'monthAndYear',
  rowHeight: 24,
  name: 'Months',
  tickWidth: 78,
  tickHeight: 60,
  displayDateFormat: 'Q, YYYY',
  shiftIncrement: 1,
  shiftUnit: 'quarter',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('monthAndYear-40by60', {
  timeResolution: {
    unit: 'quarter',
  },
  headers: [
    {
      unit: 'quarter',
      dateFormat: 'Q',
      renderer: start => `Q${DateHelper.format(start, 'Q, YYYY')}`,
      align: 'start',
    },
    {
      unit: 'month',
      increment: 1,
      dateFormat: 'MMM',
      renderer: start => displayDateFormat(DateHelper.format(start, 'MMM')),
    },
  ],
  id: 'monthAndYear-40by60',
  rowHeight: 24,
  name: 'Quarters',
  tickWidth: 40,
  tickHeight: 60,
  displayDateFormat: 'Q, YYYY',
  shiftIncrement: 1,
  shiftUnit: 'quarter',
  defaultSpan: 24,
});
 
PresetManager.registerPreset('monthAndHalfYear-60by60', {
  timeResolution: {
    unit: 'year',
  },
  headers: [
    {
      increment: 1,
      unit: 'year',
      dateFormat: 'YYYY',
      renderer: start => displayDateFormat(DateHelper.format(start, 'YYYY')),
    },
    {
      increment: 1,
      unit: 'quarter',
      renderer: start => `Q${DateHelper.format(start, 'Q')}`,
    },
  ],
  id: 'monthAndHalfYear-60by60',
  rowHeight: 24,
  name: 'Years',
  //tickWidth: 60,
  //tickHeight: 50,
  displayDateFormat: 'YYYY',
  //shiftIncrement: 1,
  //shiftUnit: 'year',
  //defaultSpan: 12,
});
 
const
    requiredPresetIds = {
    'minuteAndHour-50by60': 1,
    'minuteAndHour-30by60': 1,
    'minuteAndHour-36by60': 1,
    "hourAndDay": 1,
    'hourAndDay-38by60': 1,
    'hourAndDay-38by60-2': 1,
    'hourAndDay-38by60-4': 1,
    'hourAndDay-56by60': 1,
    'monthAndDay-40by60': 1,
    'monthAndDay-20by60': 1,
    "weekAndMonth": 1,
    'weekAndMonth-36by60': 1,
    "monthAndYear": 1,
    'monthAndYear-40by60': 1,
    'monthAndHalfYear-60by60': 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)
        }
    ];
 
//endregion
 
class EventModelWithPercent extends SchedulerEventModel {
    static get fields() {
        return [
            { name : 'percentDone', type : 'number', defaultValue : 0 }
        ];
    }
}
 
const scheduler = new Scheduler({
    appendTo          : 'container',
    resourceImagePath : '../_shared/images/users/',
 
features : {
    stripe : true,
    sort   : 'name'
},
autoAdjustTimeAxis: false,
 
columns : [
    {
        type  : 'resourceInfo',
        text  : 'Staff',
        width : '10em'
    }
],
 
resources,
eventStore : {
    modelClass : EventModelWithPercent,
    data       : events
},
 
//startDate : new Date(2017, 0, 1),
//endDate   : new Date(2017, 0, 2),
 
// Use our custom list of just the ones we plucked out of the PresetManager
presets,
 
viewPreset : 'hourAndDay-56by60',
 
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 : 'date',
        ref  : 'startdate',
        icon : 'b-icon-search-minus',
        text : 'Start Date',
        onChange(e) {
            startDate=new Date(e.value);
            scheduler.zoomTo({startDate:startDate, endDate:endDate});
        }
    },
    {
        type : 'date',
        ref  : 'enddate',
        icon : 'b-icon-search-minus',
        text : 'End Date',
        onChange(e) {
            endDate=new Date(e.value);
            scheduler.zoomTo({startDate:startDate, endDate:endDate});
        }
    },
     {
        type : 'button',
        ref  : 'enddate',
        icon : 'b-icon-search-minus',
        text : 'ZoomTo',
        onClick() {
            scheduler.zoomTo({startDate:new Date(2017, 0, 1), endDate:new Date(2017, 0, 7)});
        }
    }
]
});

Post by bhagya »

In demo URL with startdate and end date component we are able to replicate presethandler issue.
https://bryntum.com/products/schedulerpro/examples-scheduler/configuration/

Attachments
Screen Recording 2025-11-10 at 7.07.57 PM.mov
(10.59 MiB) Downloaded 104 times
Screen Recording 2025-11-10 at 6.52.40 PM.mov
(19.08 MiB) Downloaded 92 times
Screen Recording 2025-11-10 at 6.55.11 PM.mov
(11.34 MiB) Downloaded 88 times

Post by alex.l »

Hi,

I believe videos are expected to be with sound? Not really clear what you show, could you please explain a bit? Best of all to follow our How to ask for help guide viewtopic.php?f=1&t=772
0) runnable test case or code for our online demo
1) steps to reproduce
2) actual result
3) expected result

As far as I see in the demo provided, you used zoomTo method in date field change handler.
zoomTo method is trying to find viewPreset from available presets that fits best to show date range you passed. If it's not possible it will select the best one, but it is not expected to have exact dates in start and end of visible area, if there is no preset available that ideally fits that range. This is exactly that I see on your videos.

We are happy to help you, but It's quite hard to understand the problem from your first post. Could you please clarify the problem in format described above.

All the best,
Alex Lazarev

How to ask for help? Please read our Support Policy


Post by bhagya »

Adding attachment with audio

Attachments
Screen Recording 2025-11-11 at 10.48.02 AM.mov
(26.04 MiB) Downloaded 124 times

Post by alex.l »

Hi,

Thanks for clarifications! Looks like a bug. I opened a ticket here https://github.com/bryntum/support/issues/12207
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


Post by bhagya »

Hi Team..
We have upgraded Bryntumn library to 6.2.3 recently we need fix in 6.X.X version.
Please provide EAT for this issue.


Post Reply