PHP Code:
Ext.define('Ext.ux.grid.filter.ComboFilter', {
extend: 'Ext.ux.grid.filter.Filter',
alias: 'gridfilter.combo',
uses: ['Ext.menu.Menu'],
/**
* @cfg {String} iconCls
* The iconCls to be applied to the menu item.
* Defaults to <tt>'ux-gridfilter-text-icon'</tt>.
*/
iconCls : 'ux-gridfilter-text-icon',
emptyText: 'Select Currency...',
selectOnFocus: true,
width: 125,
/**
* @private
* Template method that is to initialize the filter and install required menu items.
*/
init : function (config) {
var me = this,
itemCfg, i, len, item, cfg, listeners;
listeners = Ext.apply(((me.comboOpts.listeners)? me.comboOpts.listeners:{}),{
select: {scope: me, fn: me.onComboSelect}
// select: {scope: me, fn: me.fireUpdate}
});
itemCfg = Ext.apply(me.comboOpts, {
xtype: 'combobox',
listeners: listeners
});
// console.log('COMBO CFG', itemCfg);
this.inputItem = Ext.create('Ext.form.field.ComboBox', itemCfg);
this.menu.add(this.inputItem);
// this.updateTask = Ext.create('Ext.util.DelayedTask', this.fireUpdate, this);
},
onComboSelect:function(cmb,rec,o){
// console.log('on select This',this);
// console.log('SELECT',cmb);
this.fireUpdate();
this.fireEvent('update', this);
this.menu.hide();
},
/**
* @private
* Template method that is to get and return the value of the filter.
* @return {String} The value of this filter
*/
getValue : function () {
console.log('GetValue',this);
return this.inputItem.getValue();
},
/**
* @private
* Template method that is to set the value of the filter.
* @param {Object} value The value to set the filter
*/
setValue : function (value) {
console.log('SetValue',this, 'VALUE: ',value);
this.inputItem.setValue(value);
this.fireEvent('update', this);
},
/**
* @private
* Template method that is to return <tt>true</tt> if the filter
* has enough configuration information to be activated.
* @return {Boolean}
*/
isActivatable : function () {
return this.inputItem.getValue().length > 0;
},
/**
* @private
* Template method that is to get and return serialized filter data for
* transmission to the server.
* @return {Object/Array} An object or collection of objects containing
* key value pairs representing the current configuration of the filter.
*/
getSerialArgs : function () {
return {type: 'string', value: this.getValue()};
},
/**
* Template method that is to validate the provided Ext.data.Record
* against the filters configuration.
* @param {Ext.data.Record} record The record to validate
* @return {Boolean} true if the record is valid within the bounds
* of the filter, false otherwise.
*/
validateRecord : function (record) {
var val = record.get(this.dataIndex);
if(typeof val != 'string') {
return (this.getValue().length === 0);
}
return val.toLowerCase().indexOf(this.getValue().toLowerCase()) > -1;
},
/**
* @private
* Handler method called when there is a keyup event on this.inputItem
*/
onInputKeyUp : function (field, e) {
var k = e.getKey();
if (k == e.RETURN && field.isValid()) {
e.stopEvent();
this.menu.hide();
return;
}
// restart the timer
this.updateTask.delay(this.updateBuffer);
}
});
PHP Code:
Ext.define('Ext.ux.grid.filter.MonthFilter', {
extend: 'Ext.ux.grid.filter.Filter',
alias: 'gridfilter.month',
uses: [
// 'Ext.picker.Month', // TODO: need change to default picker later
'Ext.ux.picker.Month',
'Ext.menu.Menu'
],
/**
* @cfg {String} afterText
* Defaults to 'After'.
*/
afterText : 'After',
/**
* @cfg {String} beforeText
* Defaults to 'Before'.
*/
beforeText : 'Before',
/**
* @cfg {Object} compareMap
* Map for assigning the comparison values used in serialization.
*/
compareMap : {
before: 'lt',
after: 'gt',
on: 'eq'
},
/**
* @cfg {String} dateFormat
* The date format to return when using getValue.
* Defaults to 'm/d/Y'.
*/
dateFormat : 'd/Y',
/**
* @cfg {Date} maxDate
* Allowable date as passed to the Ext.DatePicker
* Defaults to undefined.
*/
/**
* @cfg {Date} minDate
* Allowable date as passed to the Ext.DatePicker
* Defaults to undefined.
*/
/**
* @cfg {Array} menuItems
* The items to be shown in this menu
* Defaults to:<pre>
* menuItems : ['before', 'after', '-', 'on'],
* </pre>
*/
menuItems : ['before', 'after', '-', 'on'],
/**
* @cfg {Object} menuItemCfgs
* Default configuration options for each menu item
*/
menuItemCfgs : {
selectOnFocus: true,
width: 125
},
/**
* @cfg {String} onText
* Defaults to 'On'.
*/
onText : 'On',
/**
* @cfg {Object} pickerOpts
* Configuration options for the date picker associated with each field.
*/
pickerOpts : {},
/**
* @private
* Template method that is to initialize the filter and install required menu items.
*/
init : function (config) {
var me = this,
pickerCfg, i, len, item, cfg;
pickerCfg = Ext.apply(me.pickerOpts, {
xtype: 'uxmonthpicker',
format: me.dateFormat,
listeners: {
scope: me,
monthdblclick: me.onMenuSelect, // Handle double click for fast selection
yeardblclick: me.onMenuSelect, // Handle double click for fast selection
okclick: me.onMenuSelect
}
});
me.fields = {};
for (i = 0, len = me.menuItems.length; i < len; i++) {
item = me.menuItems[i];
if (item !== '-') {
cfg = {
itemId: 'range-' + item,
text: me[item + 'Text'],
menu: Ext.create('Ext.menu.Menu', {
items: [
Ext.apply(pickerCfg, {
itemId: item
})
]
}),
listeners: {
scope: me,
checkchange: me.onCheckChange
}
};
item = me.fields[item] = Ext.create('Ext.menu.CheckItem', cfg);
}
//me.add(item);
me.menu.add(item);
}
},
onCheckChange : function () {
this.setActive(this.isActivatable());
this.fireEvent('update', this);
},
/**
* @private
* Handler method called when there is a keyup event on an input
* item of this menu.
*/
onInputKeyUp : function (field, e) {
var k = e.getKey();
if (k == e.RETURN && field.isValid()) {
e.stopEvent();
this.menu.hide();
}
},
/**
* Handler for when the DatePicker for a field fires the 'select' event
* @param {Ext.picker.Date} picker
* @param {Object} date
*/
onMenuSelect : function (picker, date) {
var fields = this.fields,
field = this.fields[picker.itemId];
field.setChecked(true);
if (field == fields.on) {
(fields.before)? fields.before.setChecked(false, true):'';
(fields.after)? fields.after.setChecked(false, true):'';
} else {
fields.on.setChecked(false, true);
if (field == fields.after && this.getFieldValue('before') < date) {
fields.before.setChecked(false, true);
} else if (field == fields.before && this.getFieldValue('after') > date) {
fields.after.setChecked(false, true);
}
}
this.fireEvent('update', this);
picker.up('menu').hide();
},
/**
* @private
* Template method that is to get and return the value of the filter.
* @return {String} The value of this filter
*/
getValue : function () {
var key, result = {};
for (key in this.fields) {
if (this.fields[key].checked) {
result[key] = this.getFieldValue(key);
}
}
return result;
},
/**
* @private
* Template method that is to set the value of the filter.
* @param {Object} value The value to set the filter
* @param {Boolean} preserve true to preserve the checked status
* of the other fields. Defaults to false, unchecking the
* other fields
*/
setValue : function (value, preserve) {
var key;
for (key in this.fields) {
if(value[key]){
this.getPicker(key).setValue(value[key]);
this.fields[key].setChecked(true);
} else if (!preserve) {
this.fields[key].setChecked(false);
}
}
this.fireEvent('update', this);
},
/**
* @private
* Template method that is to return <tt>true</tt> if the filter
* has enough configuration information to be activated.
* @return {Boolean}
*/
isActivatable : function () {
var key;
for (key in this.fields) {
if (this.fields[key].checked) {
return true;
}
}
return false;
},
/**
* @private
* Template method that is to get and return serialized filter data for
* transmission to the server.
* @return {Object/Array} An object or collection of objects containing
* key value pairs representing the current configuration of the filter.
*/
getSerialArgs : function () {
var args = [];
for (var key in this.fields) {
if(this.fields[key].checked){
if(this.compareMap[key] === 'eq'){
args.push({
type: 'date',
comparison: 'gt',
// value: Ext.Date.format(this.getFieldValue(key), this.dateFormat)
value: Ext.Date.format(Ext.Date.getFirstDateOfMonth(this.getFieldValue(key)), this.dateFormat)
},{
type: 'date',
comparison: 'lt',
value: Ext.Date.format(Ext.Date.getLastDateOfMonth(this.getFieldValue(key)), this.dateFormat)
});
} else {
args.push({
type: 'date',
comparison: this.compareMap[key],
value: Ext.Date.format(((this.compareMap[key] === 'gt')? Ext.Date.getFirstDateOfMonth(this.getFieldValue(key)):Ext.Date.getLastDateOfMonth(this.getFieldValue(key))), this.dateFormat)
});
}
}
}
return args;
},
/**
* Get and return the date menu picker value
* @param {String} item The field identifier ('before', 'after', 'on')
* @return {Date} Gets the current selected value of the date field
*/
getFieldValue : function(item){
return this.getPicker(item).getValue();
},
/**
* Gets the menu picker associated with the passed field
* @param {String} item The field identifier ('before', 'after', 'on')
* @return {Object} The menu picker
*/
getPicker : function(item){
return this.fields[item].menu.items.first();
},
/**
* Template method that is to validate the provided Ext.data.Record
* against the filters configuration.
* @param {Ext.data.Record} record The record to validate
* @return {Boolean} true if the record is valid within the bounds
* of the filter, false otherwise.
*/
validateRecord : function (record) {
var key,
pickerValue,
val = record.get(this.dataIndex),
clearTime = Ext.Date.clearTime;
if(!Ext.isDate(val)){
return false;
}
val = clearTime(val, true).getTime();
for (key in this.fields) {
if (this.fields[key].checked) {
pickerValue = clearTime(this.getFieldValue(key), true).getTime();
if (key == 'before' && pickerValue <= val) {
return false;
}
if (key == 'after' && pickerValue >= val) {
return false;
}
if (key == 'on' && pickerValue != val) {
return false;
}
}
}
return true;
}
});
PHP Code:
/**
* @private
* A month picker component. This class is used by the {@link Ext.picker.Date Date picker} class
* to allow browsing and selection of year/months combinations.
*/
Ext.define('Ext.ux.picker.Month', {
extend: 'Ext.Component',
requires: [
'Ext.XTemplate',
'Ext.util.ClickRepeater',
'Ext.Date',
'Ext.button.Button'
],
alias: 'widget.uxmonthpicker',
alternateClassName: 'Ext.Ux.MonthPicker',
childEls: [
'bodyEl', 'prevEl', 'nextEl', 'buttonsEl', 'monthEl', 'yearEl'
],
renderTpl: [
'<div id="{id}-bodyEl" class="{baseCls}-body">',
'<div id="{id}-monthEl" class="{baseCls}-months">',
'<tpl for="months">',
'<div class="{parent.baseCls}-item {parent.baseCls}-month"><a style="{parent.monthStyle}" href="#" hidefocus="on">{.}</a></div>',
'</tpl>',
'</div>',
'<div id="{id}-yearEl" class="{baseCls}-years">',
'<div class="{baseCls}-yearnav">',
'<button id="{id}-prevEl" class="{baseCls}-yearnav-prev"></button>',
'<button id="{id}-nextEl" class="{baseCls}-yearnav-next"></button>',
'</div>',
'<tpl for="years">',
'<div class="{parent.baseCls}-item {parent.baseCls}-year"><a href="#" hidefocus="on">{.}</a></div>',
'</tpl>',
'</div>',
'<div class="' + Ext.baseCSSPrefix + 'clear"></div>',
'</div>',
'<tpl if="showButtons">',
'<div id="{id}-buttonsEl" class="{baseCls}-buttons">{%',
'var me=values.$comp, okBtn=me.okBtn, cancelBtn=me.cancelBtn;',
'okBtn.ownerLayout = cancelBtn.ownerLayout = me.componentLayout;',
'okBtn.ownerCt = cancelBtn.ownerCt = me;',
'Ext.DomHelper.generateMarkup(okBtn.getRenderTree(), out);',
'Ext.DomHelper.generateMarkup(cancelBtn.getRenderTree(), out);',
'%}</div>',
'</tpl>'
],
//<locale>
/**
* @cfg {String} okText The text to display on the ok button.
*/
okText: 'OK',
//</locale>
//<locale>
/**
* @cfg {String} cancelText The text to display on the cancel button.
*/
cancelText: 'Cancel',
//</locale>
/**
* @cfg {String} [baseCls='x-monthpicker']
* The base CSS class to apply to the picker element.
*/
baseCls: Ext.baseCSSPrefix + 'monthpicker',
/**
* @cfg {Boolean} showButtons True to show ok and cancel buttons below the picker.
*/
showButtons: true,
/**
* @cfg {String} [selectedCls='x-monthpicker-selected'] The class to be added to selected items in the picker.
*/
/**
* @cfg {Date/Number[]} value The default value to set. See {@link #setValue}
*/
width: 178,
measureWidth: 35,
measureMaxHeight: 20,
// used when attached to date picker which isnt showing buttons
smallCls: Ext.baseCSSPrefix + 'monthpicker-small',
// private
totalYears: 10,
yearOffset: 5, // 10 years in total, 2 per row
monthOffset: 6, // 12 months, 2 per row
// private, inherit docs
initComponent: function(){
var me = this;
me.selectedCls = me.baseCls + '-selected';
me.addEvents(
/**
* @event cancelclick
* Fires when the cancel button is pressed.
* @param {Ext.picker.Month} this
*/
'cancelclick',
/**
* @event monthclick
* Fires when a month is clicked.
* @param {Ext.picker.Month} this
* @param {Array} value The current value
*/
'monthclick',
/**
* @event monthdblclick
* Fires when a month is clicked.
* @param {Ext.picker.Month} this
* @param {Array} value The current value
*/
'monthdblclick',
/**
* @event okclick
* Fires when the ok button is pressed.
* @param {Ext.picker.Month} this
* @param {Array} value The current value
*/
'okclick',
/**
* @event select
* Fires when a month/year is selected.
* @param {Ext.picker.Month} this
* @param {Array} value The current value
*/
'select',
/**
* @event yearclick
* Fires when a year is clicked.
* @param {Ext.picker.Month} this
* @param {Array} value The current value
*/
'yearclick',
/**
* @event yeardblclick
* Fires when a year is clicked.
* @param {Ext.picker.Month} this
* @param {Array} value The current value
*/
'yeardblclick'
);
if (me.small) {
me.addCls(me.smallCls);
}
me.value = (me.value)? Ext.Date.clearTime(me.value, true) : Ext.Date.clearTime(new Date());
me.setValue(me.value);
me.activeYear = me.getYear(new Date().getFullYear() - 4, -4);
if (me.showButtons) {
me.okBtn = new Ext.button.Button({
text: me.okText,
handler: me.onOkClick,
scope: me
});
me.cancelBtn = new Ext.button.Button({
text: me.cancelText,
handler: me.onCancelClick,
scope: me
});
}
this.callParent();
},
// private, inherit docs
beforeRender: function(){
var me = this,
i = 0,
months = [],
shortName = Ext.Date.getShortMonthName,
monthLen = me.monthOffset,
margin = me.monthMargin,
style = '';
me.callParent();
for (; i < monthLen; ++i) {
months.push(shortName(i), shortName(i + monthLen));
}
if (Ext.isDefined(margin)) {
style = 'margin: 0 ' + margin + 'px;';
}
Ext.apply(me.renderData, {
months: months,
years: me.getYears(),
showButtons: me.showButtons,
monthStyle: style
});
},
// private, inherit docs
afterRender: function(){
var me = this,
body = me.bodyEl,
buttonsEl = me.buttonsEl;
me.callParent();
me.mon(body, 'click', me.onBodyClick, me);
me.mon(body, 'dblclick', me.onBodyClick, me);
// keep a reference to the year/month elements since we'll be re-using them
me.years = body.select('.' + me.baseCls + '-year a');
me.months = body.select('.' + me.baseCls + '-month a');
me.backRepeater = new Ext.util.ClickRepeater(me.prevEl, {
handler: Ext.Function.bind(me.adjustYear, me, [-me.totalYears])
});
me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over');
me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, {
handler: Ext.Function.bind(me.adjustYear, me, [me.totalYears])
});
me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over');
me.updateBody();
if (!Ext.isDefined(me.monthMargin)) {
Ext.picker.Month.prototype.monthMargin = me.calculateMonthMargin();
}
},
calculateMonthMargin: function(){
// We use this method for locales where the short month name
// may be longer than we see in English. For example in the
// zh_TW locale the month ends up spanning lines, so we loosen
// the margins to get some extra space
var me = this,
monthEl = me.monthEl,
months = me.months,
first = months.first(),
itemMargin = first.getMargin('l');
while (itemMargin && me.getLargest() > me.measureMaxHeight) {
--itemMargin;
months.setStyle('margin', '0 ' + itemMargin + 'px');
}
return itemMargin;
},
getLargest: function(months){
var largest = 0;
this.months.each(function(item){
var h = item.getHeight();
if (h > largest) {
largest = h;
}
});
return largest;
},
/**
* Set the value for the picker.
* @param {Date/Number[]} value The value to set. It can be a Date object, where the month/year will be extracted, or
* it can be an array, with the month as the first index and the year as the second.
* @return {Ext.picker.Month} this
*/
setValue: function(value){
var me = this,
active = me.activeYear,
offset = me.monthOffset,
year,
index;
if (!value) {
me.value = [null, null];
} else if (Ext.isDate(value)) {
me.value = [value.getMonth(), value.getFullYear()];
} else {
me.value = [value[0], value[1]];
}
if (me.rendered) {
year = me.value[1];
if (year !== null) {
if ((year < active || year > active + me.yearOffset)) {
me.activeYear = year - me.yearOffset + 1;
}
}
me.updateBody();
}
return me;
},
/**
* Gets the selected value. It is returned as an array [month, year]. It may
* be a partial value, for example [null, 2010]. The month is returned as
* 0 based.
* @return {Number[]} The selected value
*/
getValue: function(){
// We change return value to normal formated date (cur date) month coming with index starting 0 so we force +1 for month
var me = this;
return Ext.Date.parse((me.value[0]+1)+'-'+me.value[1],'n-Y');
},
/**
* Checks whether the picker has a selection
* @return {Boolean} Returns true if both a month and year have been selected
*/
hasSelection: function(){
var value = this.value;
return value[0] !== null && value[1] !== null;
},
/**
* Get an array of years to be pushed in the template. It is not in strict
* numerical order because we want to show them in columns.
* @private
* @return {Number[]} An array of years
*/
getYears: function(){
var me = this,
offset = me.yearOffset,
start = me.activeYear, // put the "active" year on the left
end = start + offset,
i = start,
years = [];
for (; i < end; ++i) {
years.push(i, i + offset);
}
return years;
},
/**
* Update the years in the body based on any change
* @private
*/
updateBody: function(){
var me = this,
years = me.years,
months = me.months,
yearNumbers = me.getYears(),
cls = me.selectedCls,
value = me.getYear(null),
month = me.value[0],
monthOffset = me.monthOffset,
year,
yearItems, y, yLen, el;
if (me.rendered) {
years.removeCls(cls);
months.removeCls(cls);
yearItems = years.elements;
yLen = yearItems.length;
for (y = 0; y < yLen; y++) {
el = Ext.fly(yearItems[y]);
year = yearNumbers[y];
el.dom.innerHTML = year;
if (year == value) {
el.dom.className = cls;
}
}
if (month !== null) {
if (month < monthOffset) {
month = month * 2;
} else {
month = (month - monthOffset) * 2 + 1;
}
months.item(month).addCls(cls);
}
}
},
/**
* Gets the current year value, or the default.
* @private
* @param {Number} defaultValue The default value to use if the year is not defined.
* @param {Number} offset A number to offset the value by
* @return {Number} The year value
*/
getYear: function(defaultValue, offset) {
var year = this.value[1];
offset = offset || 0;
return year === null ? defaultValue : year + offset;
},
/**
* React to clicks on the body
* @private
*/
onBodyClick: function(e, t) {
var me = this,
isDouble = e.type == 'dblclick';
if (e.getTarget('.' + me.baseCls + '-month')) {
e.stopEvent();
me.onMonthClick(t, isDouble);
} else if (e.getTarget('.' + me.baseCls + '-year')) {
e.stopEvent();
me.onYearClick(t, isDouble);
}
},
/**
* Modify the year display by passing an offset.
* @param {Number} [offset=10] The offset to move by.
*/
adjustYear: function(offset){
if (typeof offset != 'number') {
offset = this.totalYears;
}
this.activeYear += offset;
this.updateBody();
},
/**
* React to the ok button being pressed
* @private
*/
onOkClick: function(){
// this.fireEvent('okclick', this, this.value);
// We change return value of month-year in valid Date format relevant to selected date
var me = this;
this.fireEvent('okclick', this, Ext.Date.parse((me.value[0]+1)+'-'+me.value[1],'n-Y'));
},
/**
* React to the cancel button being pressed
* @private
*/
onCancelClick: function(){
this.fireEvent('cancelclick', this);
},
/**
* React to a month being clicked
* @private
* @param {HTMLElement} target The element that was clicked
* @param {Boolean} isDouble True if the event was a doubleclick
*/
onMonthClick: function(target, isDouble){
var me = this;
me.value[0] = me.resolveOffset(me.months.indexOf(target), me.monthOffset);
me.updateBody();
// me.fireEvent('month' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
// me.fireEvent('select', me, me.value);
// We return valid Date relevant to selected values
me.fireEvent('month' + (isDouble ? 'dbl' : '') + 'click', me, Ext.Date.parse((me.value[0]+1)+'-'+me.value[1],'n-Y'));
me.fireEvent('select', me, Ext.Date.parse((me.value[0]+1)+'-'+me.value[1],'n-Y'));
},
/**
* React to a year being clicked
* @private
* @param {HTMLElement} target The element that was clicked
* @param {Boolean} isDouble True if the event was a doubleclick
*/
onYearClick: function(target, isDouble){
var me = this;
me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset);
me.updateBody();
// me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
// me.fireEvent('select', me, me.value);
// We return valid Date relevant to selected values
me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, Ext.Date.parse((me.value[0]+1)+'-'+me.value[1],'n-Y'));
me.fireEvent('select', me, Ext.Date.parse((me.value[0]+1)+'-'+me.value[1],'n-Y'));
},
/**
* Returns an offsetted number based on the position in the collection. Since our collections aren't
* numerically ordered, this function helps to normalize those differences.
* @private
* @param {Object} index
* @param {Object} offset
* @return {Number} The correctly offsetted number
*/
resolveOffset: function(index, offset){
if (index % 2 === 0) {
return (index / 2);
} else {
return offset + Math.floor(index / 2);
}
},
// private, inherit docs
beforeDestroy: function(){
var me = this;
me.years = me.months = null;
Ext.destroyMembers(me, 'backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');
me.callParent();
},
// Do the job of a container layout at this point even though we are not a Container.
// TODO: Refactor as a Container.
finishRenderChildren: function () {
var me = this;
this.callParent(arguments);
if (this.showButtons) {
me.okBtn.finishRender();
me.cancelBtn.finishRender();
}
},
onDestroy: function() {
Ext.destroyMembers(this, 'okBtn', 'cancelBtn');
this.callParent();
}
});