Our pure JavaScript Scheduler component


Post by nate »

For our application, we need to change the scroll to a new date around the same time we change the view preset, so that the visible timeline starts at the beginning of the month/week/year.
We're seeing a problem when we do this where the visible area falls out of sync with the scrollbar. After the code in the below example runs, the scheduler looks correct, but the scrollbar is at the year 2003, which causes issues for people who use the scrollbar.

Is there a workaround that will allow us to set these two things together? Thanks in advance

import "./styles.css";
import {
  Scheduler,
  SchedulerResourceModel,
  ViewPreset,
} from "bryntum-schedulerpro";

const startDate = new Date(2000, 0, 1);
const endDate = new Date(2099, 11, 31);

const monthPreset = {
  tickWidth: 76,
  shiftIncrement: 1,
  shiftUnit: "month",
  timeResolution: {
    unit: "month",
    increment: 1,
  },
  headers: [
    {
      unit: "year",
    },
    {
      unit: "month",
    },
  ],

  columnLinesFor: 0,
} as ViewPreset;

const dayPreset = {
  tickWidth: 65,
  shiftIncrement: 1,
  shiftUnit: "day",
  timeResolution: { unit: "day", increment: 1 },
  headers: [{ unit: "month" }, { unit: "day", dateFormat: "DD" }],
  columnLinesFor: 0,
} as ViewPreset;

const loadSchedule = (): Scheduler => {
  class Gate extends SchedulerResourceModel {
    static get fields() {
      return [
        {
          name: "capacity",
          type: "number",
        },
      ];
    }
  }

  const scheduler = new Scheduler({
    appendTo: document.getElementById("scheduler"),
    minHeight: "99vh",
    eventColor: null,
    eventStyle: null,
    startDate,
    endDate,

features: {
  timeRanges: {
    showHeaderElements: false,
  },
  tree: true,
  regionResize: true,
},

rowHeight: 60,
barMargin: 5,

columns: [
  {
    type: "tree",
    text: "Name",
    width: 220,
    field: "name",
  },
  {
    type: "number",
    text: "Capacity",
    width: 90,
    field: "capacity",
  },
],
viewPreset: "weekAndMonth",
  });

  return scheduler;
};

let scheduler: Scheduler;

window.addEventListener("load", () => {
  scheduler = loadSchedule();

  initializeTimelines();
});

const timeout = (ms: number) => {
  return new Promise((resolve) => setTimeout(resolve, ms));
};

const setTimeline = async (date: Date) => {
  console.log("scrolling to date " + date.toString());
  await scheduler.scrollToDate(date, {
    block: "start",
  });
};

const initializeTimelines = async () => {
  console.log("setting view preset to " + JSON.stringify(monthPreset));
  scheduler.viewPreset = monthPreset;
  await setTimeline(new Date("2020-12-29"));
  await timeout(3000);
  console.log("setting view preset to " + JSON.stringify(dayPreset));
  scheduler.viewPreset = dayPreset;
  await setTimeline(new Date("2020-12-01"));
};

Post by pmiklashevich »

Hello!

the scheduler looks correct, but the scrollbar is at the year 2003

I cannot reproduce your issue, but I spot some places in the code, which might lead to problems in the future. Here they are:

  1. viewPreset property does not support object configs. Please provide a viewpreset instance or preset id. See how to add a new viewpreset in the docs of PresetManager. For example:
    PresetManager.add([{
        id                : 'myMonth',
        .....
    }, {
        id                : 'myDay',
        .....
    }]);
    
  2. Also you can specify date format for your view presets, so it's clear what date it is on the timeline
    PresetManager.add([{
        id                : 'myMonth',
        displayDateFormat : 'll',
    
  3. Gate class is not used in the testcase, please remove.
  4. minHeight : '99vh' moves the horizontal scroll beyond the screen. I don't see its position. Please remove or update with correct value.
  5. There is no data in your testcase, better to add some resources, so the schedule zone is visible and it's clear what date the timeline shows. Also fix date format for headers.
  6. Passing strings to Date constructor is not safe. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date

    Note: Parsing of date strings with the Date constructor (and Date.parse(), which works the same way) is strongly discouraged due to browser differences and inconsistencies.

    // please avoid passing strings to the constructor
    //new Date('2020-12-29')
    new Date(2020, 11, 29);
    
    Please replace it with individual params, our dates are the same. In chrome, for example, passing the string like you did gives me a date including the timezone offset.
  7. What is the use case to have 100 year timespan to be rendered on the screen? I doubt someone can manage to process such big period of time visually with a day tick on the screen. Could you please explain the business requirements?
    // 100 years timespan?
    const startDate = new Date(2000, 0, 1);
    const endDate = new Date(2099, 11, 31);
    

When produce a testcase please apply minimal changes to one of our basic examples. I tried yours against grouping demo locally. Could you please download the latest version of Scheduler Pro (4.0.6), get schedulerpro/examples/grouping/ demo up and running locally, modify it to reproduce your issue. Here is the code I tried at the end:

import .... // demo imports
import '../../lib/Grid/feature/Tree.js';
import SchedulerPro from '../../lib/SchedulerPro/view/SchedulerPro.js';
import PresetManager from '../../lib/Scheduler/preset/PresetManager.js';

const startDate = new Date(2000, 0, 1);
const endDate = new Date(2099, 11, 31);

PresetManager.add([{
    id                : 'myMonth',
    displayDateFormat : 'll',
    tickWidth         : 76,
    shiftIncrement    : 1,
    shiftUnit         : 'month',
    timeResolution    : {
        unit      : 'month',
        increment : 1
    },
    headers : [
        {
            unit : 'year'
        },
        {
            unit : 'month'
        }
    ],

columnLinesFor : 0
}, {
    id                : 'myDay',
    displayDateFormat : 'll',
    tickWidth         : 65,
    shiftIncrement    : 1,
    shiftUnit         : 'day',
    timeResolution    : { unit : 'day', increment : 1 },
    headers           : [{ unit : 'month' }, { unit : 'day', dateFormat : 'DD' }],
    columnLinesFor    : 0
}]);

const loadSchedule = () => {

const scheduler = new SchedulerPro({
    project : {
        autoLoad  : true,
        transport : {
            load : {
                url : './data/data.json'
            }
        }
    },
    appendTo   : document.getElementById('container'),
    // minHeight  : '99vh',
    eventColor : null,
    eventStyle : null,
    startDate,
    endDate,

    features : {
        timeRanges : {
            showHeaderElements : false
        },
        tree         : true,
        regionResize : true
    },

    rowHeight : 60,
    barMargin : 5,

    columns : [
        {
            type  : 'tree',
            text  : 'Name',
            width : 220,
            field : 'name'
        },
        {
            type  : 'number',
            text  : 'Capacity',
            width : 90,
            field : 'capacity'
        }
    ],
    viewPreset : 'weekAndMonth'
});

return scheduler;
};

let scheduler;

window.addEventListener('load', () => {
    scheduler = loadSchedule();

initializeTimelines();
});

const timeout = (ms) => {
    return new Promise((resolve) => setTimeout(resolve, ms));
};

const setTimeline = async(date) => {
    console.log('scrolling to date ' + date.toString());
    await scheduler.scrollToDate(date, {
        block : 'start'
    });
};

const initializeTimelines = async() => {
    console.log('setting view preset to myMonth');
    scheduler.viewPreset = 'myMonth';
    await setTimeline(new Date('2020-12-29'));
    await timeout(3000);
    console.log('setting view preset to myDay');
    scheduler.viewPreset = 'myDay';
    await setTimeline(new Date('2020-12-01'));
};
Снимок экрана 2021-01-04 в 15.46.36.png
Снимок экрана 2021-01-04 в 15.46.36.png (290.72 KiB) Viewed 1008 times

Please start over and apply minimal changes to grouping demo, so it shows your issue. Plus add a video showing the issue.

Best,
Pavel

Pavlo Miklashevych
Sr. Frontend Developer


Post by nate »

I addressed most of the comments and the issue persists. Tried with the latest official scheduler pro release as well. The scrollable area is 20 years but the visible area is less. In the future we plan to have the scrollable area expand as a user scrolls, but there's still no limit to how large it can get, so I think there will still be a risk of this issue.

PFA my project code in a zip folder

timeline-skip.gif
timeline-skip.gif (128.8 KiB) Viewed 1003 times
timeline-skip.zip
(141.58 KiB) Downloaded 80 times
import "./styles.css";
import { PresetManager, Scheduler, ViewPreset } from "bryntum-schedulerpro";
import { Chance } from "chance";

const startDate = new Date(2000, 0, 1);
const endDate = new Date(2099, 11, 31);
const chance = new Chance();

const getResourceArray = () =>
  new Array(chance.natural({ min: 1, max: 4 })).fill(0).map(() => ({
    id: chance.guid(),
    name: chance.name(),
  }));

const getRandomResourceRow = () => {
  const children = getResourceArray().map((r) => ({
    ...r,
    children: getResourceArray(),
  }));
  return {
    id: chance.guid(),
    name: chance.name(),
    children,
    capacity: chance.natural({ min: 100, max: 1000 }),
  };
};

const monthPreset = {
  id: "myMonth",
  displayDateFormat: "ll",
  tickWidth: 76,
  shiftIncrement: 1,
  shiftUnit: "month",
  timeResolution: {
    unit: "month",
    increment: 1,
  },
  headers: [
    {
      unit: "year",
    },
    {
      unit: "month",
    },
  ],

  columnLinesFor: 0,
} as ViewPreset;

const dayPreset = {
  id: "myDay",
  tickWidth: 65,
  shiftIncrement: 1,
  shiftUnit: "day",
  timeResolution: { unit: "day", increment: 1 },
  headers: [{ unit: "month" }, { unit: "day", dateFormat: "DD" }],
  columnLinesFor: 0,
} as ViewPreset;

const loadSchedule = (): Scheduler => {
  const resources = new Array(40).fill(0).map(() => getRandomResourceRow());

  const scheduler = new Scheduler({
    appendTo: document.getElementById("scheduler"),
    minHeight: "300px",
    eventColor: null,
    eventStyle: null,
    startDate,
    endDate,
    resources,

features: {
  timeRanges: {
    showHeaderElements: false,
  },
  tree: true,
  regionResize: true,
},

rowHeight: 60,
barMargin: 5,

columns: [
  {
    type: "tree",
    text: "Name",
    width: 220,
    field: "name",
  },
  {
    type: "number",
    text: "Capacity",
    width: 90,
    field: "capacity",
  },
],
viewPreset: "weekAndMonth",
  });

  return scheduler;
};

let scheduler: Scheduler;

window.addEventListener("load", () => {
  scheduler = loadSchedule();

  (PresetManager as any).add([dayPreset, monthPreset]);

  initializeTimelines();
});

const timeout = (ms: number) => {
  return new Promise((resolve) => setTimeout(resolve, ms));
};

const setTimeline = async (date: Date) => {
  console.log("scrolling to date " + date.toString());
  await scheduler.scrollToDate(date, {
    block: "start",
  });
};

const initializeTimelines = async () => {
  console.log("setting view preset to " + JSON.stringify(monthPreset));
  scheduler.viewPreset = "myMonth";
  await setTimeline(new Date(2020, 12, 29));
  await timeout(3000);
  console.log("setting view preset to " + JSON.stringify(dayPreset));
  scheduler.viewPreset = "myDay";
  await setTimeline(new Date(2020, 12, 1));
};

Post by pmiklashevich »

Reproduced! Thank you for the report, ticket here: https://github.com/bryntum/support/issues/2193

Pavlo Miklashevych
Sr. Frontend Developer


Post by mats »

The scrollable area is 20 years but the visible area is less

In your test case it's 100 years.

In the future we plan to have the scrollable area expand as a user scrolls, but there's still no limit to how large it can get, so I think there will still be a risk of this issue.

No need to make it grow infinitely large, just use a sliding window technique. We'll support infinite scrolling too in not too long, might be worth waiting for.

Can you only reproduce this with those extreme settings? Rendering such a huge time axis will produce massive element widths, and browsers are known to produce unstable results for these conditions.


Post by pmiklashevich »

Hello @nate!

I cannot reproduce the issue with "4.1.0-beta-2" release. I tried the testcase I added to the ticket and your original testcase with typescript. Could you please download the release from customer zone and confirm that the problem is gone? If not, please provide new steps which we can follow to reproduce the issue.

Best regards,
Pavel

P.S. Beta has an access to the online npm packages. Here is a small guide which you can find in the docs:

Bryntum NPM repository access
Bryntum packages are hosted on Bryntum private npm registry. Login to the registry using the following commands which configure npm to download packages in the @bryntum scope from the Bryntum registry.

npm config set @bryntum: registry https: //npm.bryntum.com
    npm login--registry =
    https: //npm.bryntum.com # only necessary for non-trial users

Use your Bryntum Customer Zone credentials when logging-in but replace @ in your username with .. (double dot) when entering the response to the login command. For example, if your username in Customer Zone is user@yourdomain.com, use the following:

Username: user..yourdomain.com
Password: type - your - password
Email: (this IS public) user @yourdomain.com

When these steps are done you can include the package this way:

// package.json
"dependencies": {
    "@bryntum/schedulerpro": "4.1.0-beta-2",
// index.ts
import {
    PresetManager,
    Scheduler,
    ViewPreset
} from "@bryntum/schedulerpro";
// style.css
@import '~@bryntum/schedulerpro/schedulerpro.material.css';

Pavlo Miklashevych
Sr. Frontend Developer


Post Reply