Our powerful JS Calendar component


Post by srilaxmi »

For example we have an event from 9am to 6pm but we want to display starting of the event 9am to 1.30pm with pink and rest of the event as yellow as attached in the screenshot. Could you please help me to achieve this.

Thank You.

event.jpg
event.jpg (68.23 KiB) Viewed 200 times

Post by Animal »

You can gain full control of the internal structure of an event element by configuring your mode with an eventRenderer.

https://www.bryntum.com/docs/calendar/#Calendar/widget/DayView#config-eventRenderer

Hopefully the docs are explanatory. All the information about the event and its context are passed into the renderer.

An example of a custom event renderer in a DayView is here: https://www.bryntum.com/examples/calendar/custom-rendering/


Post by srilaxmi »

aI am actually not finding a way to define startdate and enddate for different colors. for example I want 9am to 10am of the event to display pink color and 10am to 11.30am yellow color, could you please guide me on this.


Post by pmiklashevich »

You can return html markup from eventRenderer function. The inner divs can be positioned using left offset and width calculated based on the dates they represent. You can get inspiration by looking at our Nested Events demo in Scheduler. https://www.bryntum.com/examples/scheduler/nestedevents/

The idea of the demo is to provide additional info in the event record which represents time intervals to split the event by.

            {
                "id"         : 1,
                "resourceId" : 1,
                "startDate"  : "2018-09-24T10:00:00",
                "endDate"    : "2018-09-24T17:00:00",
                "name"       : "Meeting",
                "eventColor" : "blue", // this is a default property provided to set base event color if no details specified for the inner divs
                // custom property to describe inner divs
                "agenda"     : [
                    {
                        "name"       : "Agenda",
                        "eventColor" : "red", // this is a custom property to pass to the inner div block
                        "startDate"  : "2018-09-24T10:00:00",
                        "endDate"    : "2018-09-24T15:00:00"
                    },
                    {
                        "name"       : "Outro",
                        "eventColor" : "yellow",
                        "startDate"  : "2018-09-24T15:00:00",
                        "endDate"    : "2018-09-24T17:00:00"
                    }
                ]
            },

Then in date offsets are recalculated and in eventRenderer we return an array of properties. And in this case eventBodyTemplate function is called. eventBodyTemplate returns html which contains all the inner divs.

    // eventBodyTemplate is used to render markup inside an event. It is populated using data from eventRenderer()
    eventBodyTemplate : values => values.map(value => `
        <div class="nested" style="left: ${value.left}px;width: ${value.width}px; ${value.eventColor ? `background-color : ${value.eventColor};` : ''}">
            ${value.name}
        </div>
    `).join(''),

// eventRenderer is here used to translate the dates of nested events into pixels, passed on to the eventBodyTemplate
eventRenderer({ eventRecord, renderData }) {
    // getCoordinateFromDate gives us a px value in time axis, subtract events left from it to be within the event
    const dateToPx = (date) => this.getCoordinateFromDate(date) - renderData.left;

    // Calculate coordinates for all nested events and put in an array passed on to eventBodyTemplate
    return (eventRecord.agenda || [eventRecord]).map(nestedEvent => ({
        left       : dateToPx(DateHelper.add(eventRecord.startDate, nestedEvent.startOffset)),
        width      : dateToPx(DateHelper.add(eventRecord.startDate, nestedEvent.endOffset)),
        eventColor : nestedEvent.eventColor,
        name       : nestedEvent.name
    }));
},
Снимок экрана 2021-01-19 в 12.13.25.png
Снимок экрана 2021-01-19 в 12.13.25.png (76.64 KiB) Viewed 181 times

I hope the idea is clear. Now in Calendar there is a small difference. There is no eventBodyTemplate in the Calendar, but you can return inner divs directly from the eventRenderer. You just need to calculate offset and apply proper styles to your inner divs. And another difference is that eventRenderer for Scheduler is configured directly on Scheduler, but in Calendar you should specify mode, for example in Basic demo:

{
    "id"         : 6,
    "startDate"  : "2020-10-12T10:00:00",
    "endDate"    : "2020-10-12T12:00:00",
    "name"       : "Team Scrum",
    "resourceId" : "bryntum",
    "agenda"     : [
        {
            "name"      : "Agenda",
            "eventColor" : "red",
            "startDate" : "2020-10-12T10:00:00",
            "endDate"   : "2020-10-12T11:00:00"
        },
        {
            "name"      : "Outro",
            "eventColor" : "yellow",
            "startDate" : "2020-10-12T11:00:00",
            "endDate"   : "2020-10-12T12:00:00"
        }
    ]
},
const calendar = new Calendar({
    modes : {
        week : {
            eventRenderer({ eventRecord, renderData }) {
                return (eventRecord.agenda || [eventRecord]).map(nestedEvent => {
                    // Need to apply styles to position it
                    return `<div class="my-nested-event" style="${nestedEvent.eventColor ? `background-color : ${nestedEvent.eventColor};` : ''}">${nestedEvent.name}</div>`;
                }).join('');
            }
        }
    },

You just need to calculate the offset and apply corresponding styles.

Pavel Miklashevich - Core Developer

Post by pmiklashevich »

I've opened a ticket to create similar demo in Calendar: https://github.com/bryntum/support/issues/2254

Pavel Miklashevich - Core Developer

Post by srilaxmi »

Thank You very much @pmiklashevich for explaining this. But I am facing one issue in Calendar that is "eventRecord.agenda" is always false and the background color or style that we are returning in map function is applied to all events for which we have mentioned eventcolor even if we have not mentioned agenda in the event description(My event data is combination of both nested events and simple events).

One more issue is as we don't have getCoordinateFromDate() function in Calendar how to calculat offset from date to pixel value.


Post by pmiklashevich »

Hello,

I am facing one issue in Calendar that is "eventRecord.agenda" is always false

This happens because Calendar tries to autodetect all fields you are using in the event model. It's called autoExposeFields. In case the first record loaded to the store doesn't have all possible fields, you have to define your fields manually. The code below you can apply to the Calendar Basic demo to check how it works.

Calendar/examples/basic/app.js

import EventModel from '../../lib/Scheduler/model/EventModel.js';

class NestedEventModel extends EventModel {
    static get fields() {
        return [
            // Add new field
            { name : 'agenda' }
        ];
    }
}
........
    crudManager : {
        eventStore : {
            modelClass : NestedEventModel
        },

the background color or style that we are returning in map function is applied to all events for which we have mentioned eventcolor even if we have not mentioned agenda in the event description

It is up to you what to return from the eventRender function. I gave you an example of processing all records the same way:

return (eventRecord.agenda || [eventRecord]).map(nestedEvent => {

[eventRecord] => this is to process a record which does not have any nested elements as a nested element. Feel free to change it the way you need.

we don't have getCoordinateFromDate() function in Calendar how to calculat offset from date to pixel value.

Well, this is just simple math. You know duration of the main event, and you can calculate duration of its nested event. Then just divide the nested event duration by the main event duration and *100%. To calculate duration of the nested event you can either use our helper method DateHelper.getDurationInUnit(...) or just create a new TimeSpan and read its property.

Here is just an example:

import TimeSpan from '../../lib/Scheduler/model/TimeSpan.js';

const calendar = new Calendar({
    modes : {
        week : {
            eventRenderer({ eventRecord, renderData }) {
                return (eventRecord.agenda || [eventRecord]).map(nestedEvent => {
                    let style = '';

                if (eventRecord.agenda) {
                    // you can cache the record, or even prepare records on store load
                    const nestedRecord = new TimeSpan(nestedEvent);

                    // check duration is calculated and not equal to 0
                    if (eventRecord.duration) {
                        style += `flex : ${(nestedRecord.duration / eventRecord.duration).toFixed(3)};`;
                    }
                }

                if (nestedEvent.eventColor) {
                    style += `background-color : ${nestedEvent.eventColor};`;
                }

                return `<div class="my-nested-event" style="${style}">${nestedEvent.name}</div>`;
            }).join('');
        }
    }
},

Beside that you will need to tweak scss (Calendar/examples/basic/resources/app.scss):

.b-dayview-day-container .b-calendar-cell .b-cal-event-body {
    padding : 0;
    display : flex;

.b-event-header {
    display : none;
}

.b-cal-event-desc {
    display        : flex;
    flex-direction : column;
    flex           : 1;

    .my-nested-event {
        flex : 1;
    }
}
}

And if you set the data (Calendar/examples/basic/data/data.json):

{
    "id"         : 6,
    "startDate"  : "2020-10-12T10:00:00",
    "endDate"    : "2020-10-12T12:00:00",
    "name"       : "Team Scrum",
    "resourceId" : "bryntum",
    "agenda"     : [
        {
            "name"      : "Agenda",
            "eventColor" : "LightSkyBlue",
            "startDate" : "2020-10-12T10:00:00",
            "endDate"   : "2020-10-12T11:30:00"
        },
        {
            "name"      : "Outro",
            "eventColor" : "LemonChiffon",
            "startDate" : "2020-10-12T11:30:00",
            "endDate"   : "2020-10-12T12:00:00"
        }
    ]
},

You'll get the result:

Снимок экрана 2021-01-22 в 18.55.49.png
Снимок экрана 2021-01-22 в 18.55.49.png (94.75 KiB) Viewed 159 times

Best regards,
Pavel

Pavel Miklashevich - Core Developer

Post by pmiklashevich »

In case you don't need to have different text and you like the current renderer, but just want to visually split the task using different colors, you can simplify the approach above and just specify linear-gradient as a background. Have a try:
Calendar/examples/basic/app.js

import EventModel from '../../lib/Scheduler/model/EventModel.js';
import TimeSpan from '../../lib/Scheduler/model/TimeSpan.js';

class NestedEventModel extends EventModel {
    static get fields() {
        return [
            // Add new field
            { name : 'agenda' }
        ];
    }
}

const calendar = new Calendar({
    modes : {
        week : {
            eventRenderer({ eventRecord, renderData }) {
                // check duration is calculated and not equal to 0
                if (eventRecord.agenda && eventRecord.duration) {
                    const len = eventRecord.agenda.length;
                    let prevPercentage = 0;

                const nestedColors = eventRecord.agenda.map((nestedEvent, i) => {
                    // you can cache the record, or even prepare records on store load
                    const nestedRecord = new TimeSpan(nestedEvent);

                    // last element should be colored to the end
                    const percent = i === len - 1 ? 100 : (Math.round(nestedRecord.duration / eventRecord.duration * 100));
                    const color = `${nestedEvent.eventColor} ${prevPercentage}% ${percent}%`;

                    prevPercentage = percent;

                    return color;
                });

                renderData.style = `background: linear-gradient(${nestedColors.join(',')});`;
                renderData.cls = 'event-split-color';
            }

            return eventRecord.name;
        }
    }
},

// Start life looking at this date
date : new Date(2020, 9, 12),

// CrudManager arranges loading and syncing of data in JSON form from/to a web service
crudManager : {
    eventStore : {
        modelClass : NestedEventModel
    },

Calendar/examples/basic/resources/app.scss

.b-cal-event-wrap.event-split-color .b-cal-event {
    background-color : inherit !important;
}
Снимок экрана 2021-01-22 в 19.19.44.png
Снимок экрана 2021-01-22 в 19.19.44.png (514.68 KiB) Viewed 158 times

Cheers!

Pavel Miklashevich - Core Developer

Post by srilaxmi »

Thank you very much for providing all this information, I will try to it work out on my example based on this.


Post Reply