Request new features or modifications


Post by Ollie »

Hi,

I am planning on using a non-continuous time-axis with similar functionality to your timeaxis demo. However it seems filtering the time axis has a limitation where the start and end dates of the events can get confused. In your example you have constrained the dates to within the same day which prevents this confusion, commenting out the getDateConstraints function will allow you to recreate this issue:
ConfusingDates.PNG
ConfusingDates.PNG (17.54 KiB) Viewed 6617 times
As you can see an event was created from 8.00 to 16.00 but the end date is displaying as the start date of the next tick - 8.00 the next day.
I believe this may be handled by passing a context (boolean specifying start or end of the event) within some of the functions that retrieve the date and then handling that boolean in the Sch.data.TimeAxis getDateFromTick method.

My other problem was that I wanted the events to snap to the tick marks even though the ticks are irregular - the business case being that a day must be divided into bookable slots but the working hours do not allow the day to be divided into equal slots. e.g. working day is 6:00 - 21:45, business wants mostly 2 hour slots with the last being 1 hour 45 minutes.

I have started working on some overrides to handle my special case and it seems that with a bit more work these would be fairly straight forward additions to the framework. Maybe with a special flag in the ViewPreset to identify snap to tick?
timeResolution: {unit: "TICK"}
Ext.override(Sch.view.model.TimeAxis,{
	/**
	 * Modified to pass through the boolean specifying start/end context
	 * 
     * Gets the date for a position on the time axis
     * @param {Int} position The page X or Y coordinate
     * @param {String} roundingMethod The rounding method to use
     * @returns {Date} the Date corresponding to the xy coordinate
     */
    getDateFromPosition : function (position, roundingMethod, isStart) {
        var tick = position / this.getTickWidth() + this.timeAxis.visibleTickStart,
            nbrTicks = this.timeAxis.getCount();

        if (tick < 0 || tick > nbrTicks) {
            return null;
        }
        
        return this.timeAxis.getDateFromTick(tick, roundingMethod, isStart);
    },
});

Ext.override(Sch.view.TimelineGridView,{
	/**
	 * Modified to pass through the boolean specifying start/end context
	 * 
	 * Gets the date for an XY coordinate
	 * @param {Array} xy The page X and Y coordinates
	 * @param {String} roundingMethod The rounding method to use
	 * @param {Boolean} local, true if the coordinate is local to the scheduler view element 
	 * @returns {Date} the Date corresponding to the xy coordinate
	 */
    getDateFromXY: function (xy, roundingMethod, local, isStart) {
        return this.getDateFromCoordinate(this.orientation === 'horizontal' ? xy[0] : xy[1], roundingMethod, local, isStart);
    },

    /**
	 * Modified to pass through the boolean specifying start/end context
	 *  
	 * Gets the date for an X or Y coordinate, either local to the view element or the page based on the 3rd argument.
	 * @param {Int} coordinate The X or Y coordinate
	 * @param {String} roundingMethod The rounding method to use
	 * @param {Boolean} local, true if the coordinate is local to the scheduler view element 
	 * @returns {Date} the Date corresponding to the xy coordinate
	 */
    //Not being called??
	getDateFromCoordinate: function (coord, roundingMethod, local, isStart) {
        if (!local) {
            coord = this[this.orientation].translateToScheduleCoordinate(coord);
        }
        
        return this.timeAxisViewModel.getDateFromPosition(coord, roundingMethod, isStart);
    }
});
Ext.override(Sch.view.Horizontal,{
	/**
	 * Modified to add a boolean parameter to the getDateFromCoordinate call, 
	 * specifying the context of the date i.e. start or end.
	 * 
	 * Gets the start and end dates for an element Region 
	 * @param {Region} region The region to map to start and end dates
	 * @param {String} roundingMethod The rounding method to use
	 * @returns {Object} an object containing start/end properties
	 */
    getStartEndDatesFromRegion: function (region, roundingMethod, allowPartial) {
        var view        = this.view;
        var rtl         = view.rtl;
        
        var startDate   = view.getDateFromCoordinate(rtl ? region.right : region.left, roundingMethod, false, true),
            endDate     = view.getDateFromCoordinate(rtl ? region.left : region.right, roundingMethod, false, false);
        
        if (startDate && endDate || allowPartial && (startDate || endDate)) {
            return {
                start   : startDate,
                end     : endDate
            };
        }
        
        return null;
    }
});
Ext.override(Sch.feature.ResizeZone, {
	/**
	 * Modified to add a boolean parameter to the getDateFromCoordinate call, 
	 * specifying the context of the date i.e. start or end.
	 */
	getStartEndDates : function(xy) {
        var r           = this.resizer,
            rEl         = r.el,
            s           = this.schedulerView,
            isStart     = r.isStart,
            start,
            end;

        if (isStart) {
            end         = r.eventRecord.getEndDate();
            start       = s.getDateFromXY([s.rtl ? rEl.getRight() : rEl.getLeft() + 1, rEl.getTop()], 'round', false, true);
        } else {
            start       = r.eventRecord.getStartDate();
            end         = s.getDateFromXY([s.rtl ? rEl.getLeft() : rEl.getRight(), rEl.getBottom()], 'round', false, false);
        }

        if (r.dateConstraints) {
            start       = Sch.util.Date.constrain(start, r.dateConstraints.start, r.dateConstraints.end);
            end         = Sch.util.Date.constrain(end, r.dateConstraints.start, r.dateConstraints.end);
        }
                
        return {
            start   : start,
            end     : end
        };
    }
});

Ext.define('MyTimeAxis', {
    extend     : "Sch.data.TimeAxis",
    continuous : false,
    
    getDateFromTick : function (tick, roundingMethod, isStart) {
        if (tick === this.visibleTickEnd) return this.getEnd();

        var wholeTick   = Math.floor(tick),
            fraction    = tick - wholeTick,
            t           = this.getAt(wholeTick);
        
        if (fraction > 0.5) {
        	date = t.data.end;
        	if (isStart && wholeTick < this.count()) {
        		date = this.getAt(wholeTick+1).data.start;
        	}       	
        } else {
        	date = t.data.start;
        	if (isStart == false && wholeTick > 1) {
        		date = this.getAt(wholeTick-1).data.end;
        	}       	
        }
        return date;
    },
    
    generateTicks : function (start, end, unit, increment) {
    	
    	var ticks = [];
    	this.baseTickStore.each(function(baseTick){
    		this.push({
    			start: baseTick.get('start'),
    			end: baseTick.get('end')
    		})
    	},ticks);
    	
    	return ticks;
    }
});

Ext.onReady(function() {
    Ext.QuickTips.init();
    Sch.preset.Manager.registerPreset('workweek', {
        displayDateFormat : 'G:i',
        shiftIncrement    : 1,
        timeColumnWidth	  : 90,
        shiftUnit         : "WEEK",
        timeResolution    : {
            unit      : "HOUR",
            increment : 1
        },
        headerConfig      : {
            middle : {
                unit     : "DAY",
                renderer : function (start, end, cfg) {
                    cfg.headerCls = 'sch-hdr-startend';
                    return Ext.String.format('<span class="sch-hdr-start">{0}</span> <span class="sch-hdr-end">{1}</span>', Ext.Date.format(start, 'G:i'), Ext.Date.format(end, 'G:i'));
                }
            },
            top    : {
                unit       : "DAY",
                dateFormat : 'D d M'
            }
        }
    });
    App.SchedulerDemo.init();
});



App.SchedulerDemo = {
    
    // Initialize application
    init : function() {
    	
    	ta = Ext.create('MyTimeAxis',{
    		baseTickStore: myTickStore
    	}); 

        eventStore = new Sch.data.EventStore();           

        var sched = new Sch.panel.SchedulerGrid({
            height : ExampleDefaults.height,
            width : ExampleDefaults.width,
            eventBarTextField : 'Title',
            viewPreset : 'workweek',
            
            startDate : new Date(2010, 11, 6),
            endDate : new Date(2010, 11, 13),
            eventResizeHandles : 'both',
            snapToIncrement : true,
            rowHeight : 30,
            
            timeAxis: ta,
            
            onEventCreated : function(newEventRecord) {
                console.log(newEventRecord.get('StartDate') + ' - ' + newEventRecord.get('EndDate'));
            },
            
            // Setup static columns
            columns : [
                {header : 'Name', sortable:true, width:100, dataIndex : 'Name'}
            ],
                            
            // Store holding all the resources
            resourceStore : new Sch.data.ResourceStore({
                model : 'Sch.model.Resource',
                data : [
                    {Id : 'MadMike',        Name : 'Mike'},
                    {Id : 'LindaAnderson',  Name : 'Linda'},
                    {Id : 'DonJohnson',     Name : 'Don'},
                    {Id : 'KarenJohnson',   Name : 'Karen'},
                    {Id : 'DougHendricks',  Name : 'Doug'},
                    {Id : 'PeterPan',       Name : 'Peter'}
                ]
            }),
        
            // Store holding all the events
            eventStore : eventStore
        });

        sched.render('example-container');
    }
};

var myTickStore = Ext.create('Ext.data.Store',{
	fields: [{
		name: 'start',
		type: 'date'
	},{
		name: 'end',
		type: 'date'
	}],
	data: [{
		start: new Date(2011,1,7,6),
		end: new Date(2011,1,7,8)
	},{
		start: new Date(2011,1,7,8),
		end: new Date(2011,1,7,10)
	},{
		start: new Date(2011,1,7,10),
		end: new Date(2011,1,7,12)
	},{
		start: new Date(2011,1,7,12),
		end: new Date(2011,1,7,14)
	},{
		start: new Date(2011,1,7,14),
		end: new Date(2011,1,7,16)
	},{
		start: new Date(2011,1,7,16),
		end: new Date(2011,1,7,18)
	},{
		start: new Date(2011,1,7,18),
		end: new Date(2011,1,7,20)
	},{
		start: new Date(2011,1,7,20),
		end: new Date(2011,1,7,21,45)
	},
	//Day End
	{
		start: new Date(2011,1,8,6),
		end: new Date(2011,1,8,8)
	},{
		start: new Date(2011,1,8,8),
		end: new Date(2011,1,8,10)
	},{
		start: new Date(2011,1,8,10),
		end: new Date(2011,1,8,12)
	},{
		start: new Date(2011,1,8,12),
		end: new Date(2011,1,8,14)
	},{
		start: new Date(2011,1,8,14),
		end: new Date(2011,1,8,16)
	},{
		start: new Date(2011,1,8,16),
		end: new Date(2011,1,8,18)
	},{
		start: new Date(2011,1,8,18),
		end: new Date(2011,1,8,20)
	},{
		start: new Date(2011,1,8,20),
		end: new Date(2011,1,8,21,45)
	},
	//Day End
	{
		start: new Date(2011,1,9,6),
		end: new Date(2011,1,9,8)
	},{
		start: new Date(2011,1,9,8),
		end: new Date(2011,1,9,10)
	},{
		start: new Date(2011,1,9,10),
		end: new Date(2011,1,9,12)
	},{
		start: new Date(2011,1,9,12),
		end: new Date(2011,1,9,14)
	},{
		start: new Date(2011,1,9,14),
		end: new Date(2011,1,9,16)
	},{
		start: new Date(2011,1,9,16),
		end: new Date(2011,1,9,18)
	},{
		start: new Date(2011,1,9,18),
		end: new Date(2011,1,9,20)
	},{
		start: new Date(2011,1,9,20),
		end: new Date(2011,1,9,21,45)
	},
	//Day End
	{
		start: new Date(2011,1,10,6),
		end: new Date(2011,1,10,8)
	},{
		start: new Date(2011,1,10,8),
		end: new Date(2011,1,10,10)
	},{
		start: new Date(2011,1,10,10),
		end: new Date(2011,1,10,12)
	},{
		start: new Date(2011,1,10,12),
		end: new Date(2011,1,10,14)
	},{
		start: new Date(2011,1,10,14),
		end: new Date(2011,1,10,16)
	},{
		start: new Date(2011,1,10,16),
		end: new Date(2011,1,10,18)
	},{
		start: new Date(2011,1,10,18),
		end: new Date(2011,1,10,20)
	},{
		start: new Date(2011,1,10,20),
		end: new Date(2011,1,10,21,45)
	},
	//Day End
	{
		start: new Date(2011,1,11,8),
		end: new Date(2011,1,11,10)
	},{
		start: new Date(2011,1,11,10),
		end: new Date(2011,1,11,12)
	},{
		start: new Date(2011,1,11,12),
		end: new Date(2011,1,11,14)
	},{
		start: new Date(2011,1,11,14),
		end: new Date(2011,1,11,16)
	},{
		start: new Date(2011,1,11,16),
		end: new Date(2011,1,11,17,45)
	}]
});
I realise this code only handles my special case but thought it was worth suggesting...

Post by nickolay »

What version of our component do you use? We've fixed one bug in functionality related to filtered timeaxis recently. Seems I can't reproduce your tooltip problem - the 8:00 - 16:00 event is displayed correctly. Are you able to reproduce this problem in our latest nightly build?

As about the 2nd question - not sure snapToIncrement with irregular ticks can be easily supported - because ticks are passed to underlaying ExtJS drag and drop mechanism, which assumes ticks are always regular.

Post by Ollie »

I am using 2.2.17, I did notice a filtered timeaxis fix between 2.2.16 and 2.2.17.

Did you comment out the "getDateConstraints" method in the timeaxis example? To recreate the problem you do a drag create operation, dragging to the next working day and then back to the tick between each working day. The problem isn't limited to the tooltip it actually sets the event time to 8.00.

This is a feature request not a bug as I have previously been told you don't support an irregular time axis. I have only submitted it as I think this may be a relatively simple enhancement. Please see overrides for partial implementation.

Please correct me if I am wrong, aren't the dates calculated relative to the last tick so having an irregular time axis doesn't matter?

Post by mats »

Tick *size* in pixels is fixed, but as you know the dates represented by the ticks on the TimeAxis can be irregular.

Post by Ollie »

Yes, so the framework is supporting irregular dates between and within ticks however the dates can get confused at the point between two ticks.

Sometimes the framework will return the start date of the tick to the right or the end date of the tick to the left (I believe this depends on whether the drag motion comes from the right or left respectively).

I think the user would expect the framework to pick up the end date from the tick to the left if they are setting the end date of the event. Or the start date of the tick to the right if they are setting the start date of the event?

Post Reply