PDA

View Full Version : Ext.ux.CustomPicker, Ext.ux.CustomPickerField, Ext.ux.TimePickerField



alexwebb2
19 Nov 2010, 8:04 AM
http://i56.tinypic.com/2cyfvwh.png

In order to get around the issues with Ext.form.Select components on Android in v1.0, I've created Ext.ux.CustomPicker and its field wrapper, Ext.ux.CustomPickerField. As an example of how it's used, I've created Ext.ux.TimePickerField.

The CustomPickerField accepts slotConfigs and slotOrder, which it passes to the CustomPicker to generate the slots (lists). The slotConfigs object consists of named slot configurations, which each have title (displays with useTitles), from, to, step, align, flex, and renderer.

Here is the code for the Ext.ux.CustomPicker and Ext.ux.CustomPickerField components:

Ext.ux.CustomPicker = Ext.extend(Ext.Picker, {
slotConfigs: {
'slot_a': {
title: '1-10',
from: 1,
to: 10,
step: 1,
align: 'center',
flex: 2,
renderer: function(value) {return value;}
},
'slot_b': {
title: '1-100',
from: 1,
to: 100,
step: 1,
align: 'center',
flex: 2,
renderer: function(value) {return value;}
},
'slot_c': {
title: '0-99(.1)',
from: 1,
to: 10,
step: .1,
align: 'center',
flex: 2,
renderer: function(value) {return (value - 1) * 10;}
}
},
slotOrder: [
'slot_a',
'slot_b',
'slot_c'
],

initComponent: function()
{
var tmp, i;

for (var slotName in this.slotConfigs)
{
if (this.slotConfigs.hasOwnProperty(slotName))
{
if (this.slotConfigs[slotName].from && this.slotConfigs[slotName].to && this.slotConfigs[slotName].step)
{
// Swap from and to if slotName
if (this.slotConfigs[slotName].from > this.slotConfigs[slotName].to)
{
tmp = this.slotConfigs[slotName].from;
this.slotConfigs[slotName].from = this.slotConfigs[slotName].to;
this.slotConfigs[slotName].from = tmp;
}

for (i = this.slotConfigs[slotName].from; i <= this.slotConfigs[slotName].to; i += this.slotConfigs[slotName].step)
{
if (!this.slotConfigs[slotName].values)
{
this.slotConfigs[slotName].values = [];
}

this.slotConfigs[slotName].values.push(i);
}
}
}
}

this.slots = [];

for (i = 0; i < this.slotOrder.length; i++)
{
var slotName = this.slotOrder[i];

if (this.slotConfigs[slotName])
{
var data = [];

for (var j = 0; j < this.slotConfigs[slotName].values.length; j++)
{
data.push({
text: this.slotConfigs[slotName].renderer(this.slotConfigs[slotName].values[j]),
value: this.slotConfigs[slotName].values[j]
});
}

this.slots.push({
name: slotName,
align: this.slotConfigs[slotName].align,
data: data,
title: this.useTitles ? this.slotConfigs[slotName].title : false,
flex: this.slotConfigs[slotName].flex
});
}
}

Ext.ux.CustomPicker.superclass.initComponent.call(this);
},

initEvents: function()
{
Ext.ux.CustomPicker.superclass.initEvents.call(this);
},

afterRender: function()
{
Ext.ux.CustomPicker.superclass.afterRender.apply(this, arguments);

this.setValue(this.value);
},

onSlotPick: function(slot, value)
{
Ext.ux.CustomPicker.superclass.onSlotPick.apply(this, arguments);
},

onMaskTap: function(slot, value)
{
Ext.ux.CustomPicker.superclass.onMaskTap.apply(this, arguments);
},

getValue: function()
{
var value = Ext.ux.CustomPicker.superclass.getValue.call(this);
var returnValue = {};

for (var slotName in value)
{
if (value.hasOwnProperty(slotName))
{
returnValue[slotName] = value[slotName];
}
}

return returnValue;
},

getTextValues: function()
{
var value = Ext.ux.CustomPicker.superclass.getValue.call(this);
var returnValue = {};

for (var slotName in value)
{
if (value.hasOwnProperty(slotName))
{
returnValue[slotName] = this.slotConfigs[slotName].renderer(value[slotName]);
}
}

return returnValue;
},

setValue: function(value, animated)
{
this.value = value;

return Ext.ux.CustomPicker.superclass.setValue.call(this, this.value, animated);
}
});

Ext.ux.CustomPickerField = Ext.extend(Ext.form.Field, {
ui: 'select',
picker: null,
destroyPickerOnHide: false,

initComponent: function()
{
this.addEvents('select');

this.tabIndex = -1;
this.useMask = true;

Ext.form.Text.superclass.initComponent.apply(this, arguments);
},

getCustomPicker: function()
{
if (!this.customPicker)
{
if (this.picker instanceof Ext.ux.CustomPicker)
{
this.customPicker = this.picker;
}
else
{
this.customPicker = new Ext.ux.CustomPicker(Ext.apply(this.picker || {}));
}

this.customPicker.setValue(this.value || null);

this.customPicker.on({
scope: this,
change: this.onPickerChange,
hide: this.onPickerHide
});
}

return this.customPicker;
},

onMaskTap: function()
{
this.getCustomPicker().show();
},

onPickerChange: function(picker, value)
{
this.setValue(value);
this.fireEvent('select', this, this.getValue());
},

onPickerHide: function()
{
if (this.destroyPickerOnHide && this.customPicker)
{
this.customPicker.destroy();
}
},

setValue: function(value, animated)
{
this.value = value;

if (this.getCustomPicker())
{
this.getCustomPicker().setValue(value, animated);
}

if (this.rendered)
{
this.fieldEl.dom.value = this.getDisplayValue();
}

return this;
},

getValue: function()
{
return this.getCustomPicker() ? this.getCustomPicker().getValue() : this.value;
},

getDisplayValue: function()
{
return this.renderer ? this.renderer(this.getTextValues()) : null;
},

getTextValues: function()
{
return this.getCustomPicker() ? this.getCustomPicker().getTextValues() : null;
},

onDestroy: function()
{
if (this.customPicker)
{
this.customPicker.destroy();
}

Ext.form.customPicker.superclass.onDestroy.call(this);
}
});

The CustomPickerField is not intended to be used on its own, but extended and given a getDisplayValue() function to convert the list of text values in this.value (indexed by slot name) into a displayed string. As an example, here is the code for Ext.ux.TimePickerField (though if you're specifically looking for a TimePicker, you may want to check out blessan's DateTimePicker (http://www.sencha.com/forum/showthread.php?115128-A-DateTimePicker) as an alternative):

Ext.ux.TimePickerField = Ext.extend(Ext.ux.CustomPickerField, {
slotConfigs: {
'hour': {
title: 'Hour',
from: 1,
to: 12,
step: 1,
align: 'right',
flex: 4,
renderer: function(value) {
return value;
}
},
'minute': {
title: 'Minute',
from: 1,
to: 60,
step: 5,
align: 'left',
flex: 2,
renderer: function(value) {
value = value - 1;

return value >= 10 ? value + '' : '0' + value;
}
},
'am_pm': {
title: 'AM/PM',
from: 1,
to: 2,
step: 1,
align: 'left',
flex: 4,
renderer: function(value) {
return value == 1 ? 'AM' : 'PM';
}
}
},
slotOrder: [
'hour',
'minute',
'am_pm'
],

initComponent: function()
{
this.picker = new Ext.ux.CustomPicker({slotConfigs: this.slotConfigs, slotOrder: this.slotOrder});

Ext.ux.TimePickerField.superclass.initComponent.call(this);
},

onPickerChange: function(picker, value)
{
this.setValue(value);
this.fireEvent('select', this, this.getValue());
},

afterRender: function()
{
Ext.ux.TimePickerField.superclass.afterRender.apply(this, arguments);

this.setValue(this.value);
},

onSlotPick: function(slot, value)
{
Ext.ux.TimePickerField.superclass.onSlotPick.apply(this, arguments);
},

getValue: function()
{
return Ext.ux.TimePickerField.superclass.getValue.call(this);
},

getTextValues: function()
{
return Ext.ux.TimePickerField.superclass.getTextValues.call(this);
},

getDisplayValue: function()
{
var values = this.getTextValues.call(this);
var value;

if (values && values.hour && values.minute && values.am_pm)
{
value = values.hour + ':' + values.minute + ' ' + values.am_pm;
}

return value ? value : null;
},

setValue: function(value, animated)
{
this.value = value;

Ext.ux.TimePickerField.superclass.setValue.call(this, this.value, animated);

if (this.rendered)
{
this.fieldEl.dom.value = this.getDisplayValue();
}
}
});

Finally, an example of how to instantiate the field and provide a default value ("2:30 AM"):

this.startTimePickerField = new Ext.ux.TimePickerField({
label: 'Start Time',
value: {
hour: 2,
minute: 31,
am_pm: 1
}
});

As this was a one-night hack, it's got a LOT of room for improvement. If you end up making any fixes/changes/improvements to it, please leave a post!

blessan
19 Nov 2010, 4:19 PM
Looks good. It would be great if you could attach a zip file with the extension and a test file so the we can download and check right away.