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: 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)
}]
});