Our pure JavaScript Scheduler component


Post by frederick45 »

Hi, this code work on bryntum live demo :

features: {
                eventDrag : {
                    validatorFn({ draggedRecords, event }) {
                      console.log('freddy');
                        return false;
                    },
                },

it's does'nt work on my salesforce lwc


Post by frederick45 »

i don't see 'freddy' on the dev console


Post by frederick45 »

for this :

listeners: {
                beforeEventAdd: (event) => {
                    return false; 
                },
                afterEventDrop: (event) => {
                    console.log('afterEventDrop'); 
                },
            }

beforeEventAdd work, afterEventDrop KO


Post by frederick45 »

any drag and drop doesn't work and freeze the screen


Post by mats »

Can you please provide us full code or access to your live app so we can inspect?


Post by frederick45 »

import { LightningElement, wire } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import SCHEDULER from '@salesforce/resourceUrl/bryntum_scheduler';
import getListResource from '@salesforce/apex/Scheduler.getListResource';
import getListEvent from '@salesforce/apex/Scheduler.getListEvent';
import getListEventUnplanned from '@salesforce/apex/Scheduler.getListEventUnplanned';
import deleteEvent from '@salesforce/apex/Scheduler.deleteEvent';
import insertEvent from '@salesforce/apex/Scheduler.insertEvent';
import updateEvent from '@salesforce/apex/Scheduler.updateEvent';


export default class Scheduler_component extends LightningElement {

ListResource;
ListEvent;
ListEventNotPlanned;
currentDate = new Date();
scheduler;
UnplannedGrid;
splitter;
Drag;
DomHelper;
DateHelper;
errorUpdate;
eventStore;
me = this;
DH;
today;
start;
center;
animate;

@wire(getListEventUnplanned)
wiredgetListofEventNotPlanned({ error, data }) 
{
    if (data) 
    {
        this.ListEventNotPlanned = JSON.parse(data);

    } else if (error) {
      console.log(error);
    }
}

@wire(getListEvent)
wiredgetListofEvent({ error, data }) 
{
    if (data) 
    {
        this.ListEvent = JSON.parse(data);

    } else if (error) {
      console.log(error);
    }
}

@wire(getListResource)
wiredgetListofResource({ error, data }) 
{
    if (data) 
    {
        this.ListResource = JSON.parse(data);

    } else if (error) {
      console.log(error);
    }
}

renderedCallback() {
    if (this.bryntumInitialized) {
        return;
    }
    this.bryntumInitialized = true;

    Promise.all([
        loadScript(this, SCHEDULER + '/scheduler.lwc.module.js'),
        loadStyle(this, SCHEDULER + '/scheduler.stockholm.css'),
    ])
        .then(() => {

            this.DH        = bryntum.scheduler.DateHelper;
            this.today     = this.DH.clearTime(new Date());
            this.start     = this.DH.startOf(this.today, 'week');
            this.center    = true; 
            this.animate   = 2000;

            console.log('this.today:'+this.today);
            //Fri Dec 02 2022 00:00:00 GMT+0100 (heure normale d’Europe centrale)
            console.log('this.start:'+this.start);
            //Sun Nov 27 2022 00:00:00 GMT+0100 (heure normale d’Europe centrale)

            this.createScheduler();
            this.createSplitter();
            this.createGrid(this.ListEventNotPlanned);

            console.log('this.scheduler.timeAxis.last.startDate:'+this.scheduler.timeAxis.last.startDate);
            //Sat Dec 17 2022 00:00:00 GMT+0100 (heure normale d’Europe centrale)
            console.log('this.DH.add(this.today, value, :'+this.DH.add(this.today, 2, 'days'));
            //Sun Dec 04 2022 00:00:00 GMT+0100 (heure normale d’Europe centrale)


            //this.createDrag();    
        })
        .catch(error => {
            console.log('createScheduler:'+error);
            this.dispatchEvent(
                new ShowToastEvent({
                    title: "Error loading Bryntum Scheduler",
                    message: error,
                    variant: "error"
                })
            );
        });
}


/*    createDrag() 
    {
        this.Drag = new bryntum.scheduler.DragHelper (
            {
                cloneTarget        : true,
                mode               : 'translateXY',               
dropTargetSelector : '.b-timeline-subgrid', targetSelector : '.b-grid-row:not(.b-group-row)',
grid : this.UnplannedGrid, schedule : this.scheduler, constrain : false, outerElement : this.UnplannedGrid.element, construct(config){ const me = this; super.construct(config); console.log('construct config'); me.on({ dragstart : me.onDragStart, drag : me.onDrag, drop : me.onDrop, thisObj : me }); }, createProxy(element) { const proxy = document.createElement('div'), { schedule } = this, task = this.grid.getRecordFromElement(element), durationInPx = schedule.timeAxisViewModel.getDistanceForDuration(task.duration); console.log('task.duration:'+task.duration); console.log('durationInPx:'+durationInPx); console.log('schedule.isHorizontal:'+schedule.isHorizontal); // Fake an event bar proxy.classList.add('b-sch-event-wrap', 'b-sch-event', 'b-unassigned-class', 'b-sch-${schedule.mode}'); proxy.innerHTML = `<div class="b-sch-event b-has-content b-sch-event-withicon"> <div class="b-sch-event-content"> <i class="${task.iconCls}"></i> ${task.name} </div> </div>`; if (schedule.isHorizontal) { proxy.style.height = `${schedule.rowHeight - (2 * schedule.resourceMargin)}px`; proxy.style.width = `${durationInPx}px`; } else { proxy.style.height = `${durationInPx}px`; proxy.style.width = `${schedule.resourceColumnWidth}px`; } console.log('proxy.style.width:'+proxy.style.width); return proxy; }, onDragStart({ context }) { const me = this, { schedule } = me, { eventTooltip, eventDrag } = schedule.features; // save a reference to the task so we can access it later context.task = me.grid.getRecordFromElement(context.grabbed); // Prevent tooltips from showing while dragging eventTooltip.disabled = true; schedule.enableScrollingCloseToEdges(schedule.timeAxisSubGrid); if (eventDrag.showTooltip && !me.tip) { me.tip = new Tooltip({ align : 'b-t', clippedBy : [schedule.timeAxisSubGridElement, schedule.bodyContainer], forElement : context.element, cls : 'b-popup b-sch-event-tooltip' }); } }, onDrag({ event, context }) { const me = this, { schedule } = me, { task } = context, coordinate = bryntum.scheduler.DomHelper[`getTranslate${schedule.isHorizontal ? 'X' : 'Y'}`](context.element), startDate = schedule.getDateFromCoordinate(coordinate, 'round', false), endDate = startDate && DateHelper.add(startDate, task.duration, task.durationUnit), // Coordinates required when used in vertical mode, since it does not use actual columns resource = context.target && schedule.resolveResourceRecord(context.target, [event.offsetX, event.offsetY]); // Don't allow drops anywhere, only allow drops if the drop is on the timeaxis and on top of a Resource context.valid = Boolean(startDate && resource) && (schedule.allowOverlap || schedule.isDateRangeAvailable(startDate, endDate, null, resource)); // Save reference to resource so we can use it in onTaskDrop context.resource = resource; console.log('ondrag'); if (me.tip && context.valid) { const dateFormat = schedule.displayDateFormat, formattedStartDate = DateHelper.format(startDate, dateFormat), formattedEndDate = DateHelper.format(endDate, dateFormat); me.tip.html = ` <div class="b-sch-event-title">${task.name}</div> <div class="b-sch-tooltip-startdate">Starts: ${formattedStartDate}</div> <div class="b-sch-tooltip-enddate">Ends: ${formattedEndDate}</div> `; me.tip.showBy(context.element); } else { me.tip?.hide(); } }, // Drop callback after a mouse up, take action and transfer the unplanned task to the real EventStore (if it's valid) onDrop({ context, event }) { const me = this, { schedule } = me, { task, target, resource, valid, element } = context; me.tip?.hide(); console.log('ondrop'); schedule.disableScrollingCloseToEdges(me.schedule.timeAxisSubGrid); // If drop was done in a valid location, set the startDate and transfer the task to the Scheduler event store if (valid && target) { const coordinate = bryntum.scheduler.DomHelper[`getTranslate${schedule.isHorizontal ? 'X' : 'Y'}`](element), date = schedule.getDateFromCoordinate(coordinate, 'round', false), // Try resolving event record from target element, to determine if drop was on another event targetEventRecord = schedule.resolveEventRecord(target); if (date) { // Remove from grid first so that the data change // below does not fire events into the grid. me.grid.store.remove(task); task.startDate = date; task.assign(resource); schedule.eventStore.add(task); } // Dropped on a scheduled event, display toast if (targetEventRecord) { WidgetHelper.toast(`Dropped on ${targetEventRecord.name}`); } } if (resource) { resource.cls = ''; } schedule.features.eventTooltip.disabled = false; }, onDragAbort() { this.tip?.hide(); } }, ); } */ createSplitter() { this.splitter = new bryntum.scheduler.Splitter({ appendTo : this.template.querySelector('.main') }); } createScheduler() { this.scheduler = window.scheduler = new bryntum.scheduler.Scheduler({ features: { eventDrag : { triggerEvent : 'eventClick', validatorFn({ draggedRecords, event }) { console.log('freddy'); return true; }, }, resourceTimeRanges: true, nonWorkingTime : true, dependencies: false, timeRanges: { showHeaderElements: true, showCurrentTimeLine: true }, eventEdit: { editorConfig : { bbar : { items : { deleteButton : null } } }, triggerEvent : 'eventClick', extraWidgets: [ { type: 'text', name: 'WOnumber', label: 'Number Work Order', typeAhead: true, forceSelection: true, editor: false, selectOnFocus: true, readOnly: true, } ] }, stripe : true, timeRanges : true, eventMenu : { items : { unassign : { text : 'Unassign', icon : 'b-fa b-fa-user-times', weight : 200, onItem : ({ eventRecord, resourceRecord }) => eventRecord.unassign(resourceRecord) }, deleteEvent : false, copyEvent : false, cutEvent : false } }, scheduleMenu : { items : { // Remove "Add event" default item addEvent : false } }, }, insertFirst: this.template.querySelector('.main'), rowHeight: 50, barMargin: 8, columns: [ { text: 'Salariés', width: 150, field: 'name', } ], startDate : this.DH.add(this.start, -2, 'weeks'), endDate : this.DH.add(this.start, 3, 'weeks'), viewPreset : 'dayAndWeek', eventRenderer({ eventRecord, resourceRecord, renderData }) { renderData.eventColor = 'indigo'; return bryntum.scheduler.StringHelper.encodeHtml(eventRecord.name); }, resourceStore: new bryntum.scheduler.ResourceStore({ data: this.ListResource }), eventStore: new bryntum.scheduler.EventStore({ data: this.ListEvent, listeners: { beforeUpdate: (event) => { }, update: (event) => { this.onEventUpdate(event); }, } }), tbar : [ //'', //'->', //{ type : 'viewpresetcombo' }, { type : 'dropdown', ref : 'scrollToTime', placeholder : 'Scroll to time', editable : false, items : [ [0, 'Today'], [2, '2 days from now'], [10, '10 days from now'], [-1, `Last day in view`] ], onAction : ({ value }) => { this.scheduler.scrollToDate( value === -1 ? this.scheduler.timeAxis.last.startDate : this.DH.add(this.today, value, 'days'), { highlight, animate : { easing : 'easeFromTo', duration : this.animate }, block : this.center ? 'center' : 'nearest' } ); } }, ], listeners: { beforeEventAdd: (event) => { return false; }, afterEventDrop: (event) => { console.log('afterEventDrop'); }, } }); } deleteEventJS(IdVisite, payload) { //delete event deleteEvent({ IdVisite: IdVisite, payload: payload }) .then(result => { }) .catch(error => { this.dispatchEvent( new ShowToastEvent({ title: "Erreur", message: error.body.message, variant: "error" }) ); }) } insertEventJS(IdVisite, payload) { //insert event insertEvent({ IdVisite: IdVisite, payload: payload }) .then(result => { }) .catch(error => { this.dispatchEvent( new ShowToastEvent({ title: "Erreur", message: error.body.message, variant: "error" }) ); }) } onEventUpdate(event) { let record = event.record; //{"id":"a105r000001GRQPAA4","startDate":"2022-11-22T06:15:00+01:00","endDate":"2022-11-22T17:15:00+01:00","duration":0.4583333333333333,"durationUnit":"day","cls":"","name":"VI-000002","exceptionDates":[],"resourceId":"a0X5r000006HmeoEAC","allDay":false} let changes = event.changes; //{"recurrenceCombo":{"value":"none"},"startDate":{"value":"2022-11-22T05:15:00.000Z","oldValue":"2022-11-22T05:30:00.000Z"},"endDate":{"value":"2022-11-22T16:15:00.000Z","oldValue":"2022-11-22T16:30:00.000Z"}} //event's employee is update => delete event and create new if(JSON.stringify(changes).includes('resourceId')) { //delete event this.deleteEventJS(record.id, JSON.stringify(changes)); //insert event if(changes.resourceId.value != null) { this.insertEventJS(record.id, JSON.stringify(changes)); } else { const totalMinutes = record.duration*24*60; const minutes = totalMinutes % 60; const hours = Math.floor(totalMinutes / 60); const clocktime = hours+':'+minutes; let task = new bryntum.scheduler.Model( { name : record.name, id : record.id, duration : clocktime, start : record.startDate, finish : record.endDate } ); this.UnplannedGrid.store.add(task); } } else //data update except employee { this.updateEventJS(record.id, JSON.stringify(changes), record, changes); } } updateEventJS(IdVisite, jsonChanges, record, changes) { //update event updateEvent({ IdVisite: IdVisite, payload: jsonChanges }) .then(result => { }) .catch(error => { this.dispatchEvent( new ShowToastEvent({ title: "Erreur", message: error.body.message, variant: "error" }) ); var oldStartDate; if(JSON.stringify(changes).includes('startDate')) { oldStartDate = changes.startDate.oldValue; } var oldEndDate; if(JSON.stringify(changes).includes('endDate')) { oldEndDate = changes.endDate.oldValue; } if(oldStartDate != undefined) { record.startDate = oldStartDate; } if(oldEndDate != undefined) { record.endDate = oldEndDate; } }) } createGrid(ListEventNotPlanned) { this.UnplannedGrid = new bryntum.scheduler.Grid({ features: { rowReorder: true, search: true }, title : 'Visites non planifiées', appendTo : this.template.querySelector('.main'), collapsible : true, store : { fields : [ { name : 'start', type : 'date' } ], data : ListEventNotPlanned, }, columns:[ { type: 'rownumber' }, { text: 'Visite', field: 'name', flex: 1, type: 'template', template: (data) => `${data.record.data.name}`, }, { text: 'Date', field: 'start', flex: 1, type: 'date', format: 'DD/MM/YYYY', }, { text: 'Durée', field: 'duration', flex: 1, type: 'time', format: 'HH:mm' }, ] }); }

}

Post by frederick45 »

i want to add the tbar 'viewpresetcombo' work, and the new 'dropdown' doesn't work


Post by Maxim Gorkovsky »

Could you also post component markup and its usage? If you create a lightning app and append just this one component, does it work?
Which product version do you use? Is LWS enabled?


Post by frederick45 »

LWS ?


Post by frederick45 »

component :

        this.scheduler = new bryntum.scheduler.Scheduler({
            flex        : 40,
            features: {
                eventDrag : {
                    triggerEvent : 'eventClick',
                    validatorFn({ draggedRecords, event }) {
                      console.log('freddy');
                        return true;
                    },
                },
                resourceTimeRanges: true,
                nonWorkingTime : true,
                dependencies: false,
                timeRanges: {
                    showHeaderElements: true,
                    showCurrentTimeLine: true
                },
                eventEdit: {
                    editorConfig : {
                        bbar : {
                            items : {
                                deleteButton : null
                            }
                        }
                    },
                    triggerEvent : 'eventClick',
                },
                stripe           : true,
                timeRanges       : true,
                eventMenu : {
                    items : {
                        unassign : {
                            text   : 'Unassign',
                            icon   : 'b-fa b-fa-user-times',
                            weight : 200,
                            onItem : ({ eventRecord, resourceRecord }) => eventRecord.unassign(resourceRecord)
                        },
                        deleteEvent : false,
                        copyEvent : false,
                        cutEvent : false
                    }
                },
                scheduleMenu : {
                    items : {
                        // Remove "Add event" default item
                        addEvent : false
                    }
                },
        
}, insertFirst: this.template.querySelector('.main'), rowHeight: 50, barMargin: 8, columns: [ { type : 'resourceInfo', text : 'Salariés', minWidth : 150, showEventCount : false, showRole : false, editor : false, }, ], startDate : this.DH.add(this.start, -2, 'weeks'), endDate : this.DH.add(this.start, 3, 'weeks'), viewPreset : 'dayAndWeek', eventRenderer({ eventRecord, resourceRecord, renderData }) { renderData.eventColor = 'indigo'; return bryntum.scheduler.StringHelper.encodeHtml(eventRecord.name); }, resourceStore: new bryntum.scheduler.ResourceStore({ data: this.ListResource }), eventStore: new bryntum.scheduler.EventStore({ data: this.ListEvent, listeners: { beforeUpdate: (event) => { }, update: (event) => { this.onEventUpdate(event); }, } }), tbar : [ //'', //'->', //{ type : 'viewpresetcombo' }, { type : 'dropdown', ref : 'scrollToTime', placeholder : 'Scroll to time', editable : false, items : [ [0, 'Today'], [2, '2 days from now'], [10, '10 days from now'], [-1, `Last day in view`] ], onAction : ({ value }) => { this.scheduler.scrollToDate( value === -1 ? this.scheduler.timeAxis.last.startDate : this.DH.add(this.today, value, 'days'), { highlight, animate : { easing : 'easeFromTo', duration : this.animate }, block : this.center ? 'center' : 'nearest' } ); } }, ], listeners: { beforeEventAdd: (event) => { return false; }, afterEventDrop: (event) => { console.log('afterEventDrop'); }, } });

Post Reply