2 * @class Ext.form.field.Picker
3 * @extends Ext.form.field.Trigger
4 * <p>An abstract class for fields that have a single trigger which opens a "picker" popup below
5 * the field, e.g. a combobox menu list or a date picker. It provides a base implementation for
6 * toggling the picker's visibility when the trigger is clicked, as well as keyboard navigation
7 * and some basic events. Sizing and alignment of the picker can be controlled via the {@link #matchFieldWidth}
8 * and {@link #pickerAlign}/{@link #pickerOffset} config properties respectively.</p>
9 * <p>You would not normally use this class directly, but instead use it as the parent class for
10 * a specific picker field implementation. Subclasses must implement the {@link #createPicker} method
11 * to create a picker component appropriate for the field.</p>
15 * Create a new picker field
16 * @param {Object} config
18 Ext.define('Ext.form.field.Picker', {
19 extend: 'Ext.form.field.Trigger',
20 alias: 'widget.pickerfield',
21 alternateClassName: 'Ext.form.Picker',
22 requires: ['Ext.util.KeyNav'],
25 * @cfg {Boolean} matchFieldWidth
26 * Whether the picker dropdown's width should be explicitly set to match the width of the field.
27 * Defaults to <tt>true</tt>.
29 matchFieldWidth: true,
32 * @cfg {String} pickerAlign
33 * The {@link Ext.core.Element#alignTo alignment position} with which to align the picker. Defaults
34 * to <tt>"tl-bl?"</tt>
36 pickerAlign: 'tl-bl?',
39 * @cfg {Array} pickerOffset
40 * An offset [x,y] to use in addition to the {@link #pickerAlign} when positioning the picker.
41 * Defaults to undefined.
45 * @cfg {String} openCls
46 * A class to be added to the field's {@link #bodyEl} element when the picker is opened. Defaults
47 * to 'x-pickerfield-open'.
49 openCls: Ext.baseCSSPrefix + 'pickerfield-open',
52 * @property isExpanded
54 * True if the picker is currently expanded, false if not.
58 * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field;
59 * the field can only have its value set via selecting a value from the picker. In this state, the picker
60 * can also be opened by clicking directly on the input field itself.
61 * (defaults to <tt>true</tt>).
66 initComponent: function() {
73 * Fires when the field's picker is expanded.
74 * @param {Ext.form.field.Picker} field This field instance
79 * Fires when the field's picker is collapsed.
80 * @param {Ext.form.field.Picker} field This field instance
85 * Fires when a value is selected via the picker.
86 * @param {Ext.form.field.Picker} field This field instance
87 * @param {Mixed} value The value that was selected. The exact type of this value is dependent on
88 * the individual field and picker implementations.
95 initEvents: function() {
99 // Add handlers for keys to expand/collapse the picker
100 me.keyNav = Ext.create('Ext.util.KeyNav', me.inputEl, {
102 if (!me.isExpanded) {
103 // Don't call expand() directly as there may be additional processing involved before
104 // expanding, e.g. in the case of a ComboBox query.
113 // Non-editable allows opening the picker by clicking the field
115 me.mon(me.inputEl, 'click', me.onTriggerClick, me);
118 // Disable native browser autocomplete
120 me.inputEl.dom.setAttribute('autocomplete', 'off');
126 * Expand this field's picker dropdown.
130 bodyEl, picker, collapseIf;
132 if (me.rendered && !me.isExpanded && !me.isDestroyed) {
134 picker = me.getPicker();
135 collapseIf = me.collapseIf;
137 // show the picker and set isExpanded flag
139 me.isExpanded = true;
141 bodyEl.addCls(me.openCls);
143 // monitor clicking and mousewheel
144 me.mon(Ext.getDoc(), {
145 mousewheel: collapseIf,
146 mousedown: collapseIf,
150 me.fireEvent('expand', me);
155 onExpand: Ext.emptyFn,
159 * Aligns the picker to the
161 alignPicker: function() {
166 if (this.isExpanded) {
167 picker = me.getPicker();
168 if (me.matchFieldWidth) {
169 // Auto the height (it will be constrained by min and max width) unless there are no records to display.
170 picker.setSize(me.bodyEl.getWidth(), picker.store && picker.store.getCount() ? null : 0);
172 if (picker.isFloating()) {
173 picker.alignTo(me.inputEl, me.pickerAlign, me.pickerOffset);
175 // add the {openCls}-above class if the picker was aligned above
176 // the field due to hitting the bottom of the viewport
177 isAbove = picker.el.getY() < me.inputEl.getY();
178 me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx);
179 picker.el[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx);
185 * Collapse this field's picker dropdown.
187 collapse: function() {
188 if (this.isExpanded && !this.isDestroyed) {
190 openCls = me.openCls,
193 collapseIf = me.collapseIf,
196 // hide the picker and set isExpanded flag
198 me.isExpanded = false;
200 // remove the openCls
201 me.bodyEl.removeCls([openCls, openCls + aboveSfx]);
202 picker.el.removeCls(picker.baseCls + aboveSfx);
204 // remove event listeners
205 doc.un('mousewheel', collapseIf, me);
206 doc.un('mousedown', collapseIf, me);
208 me.fireEvent('collapse', me);
213 onCollapse: Ext.emptyFn,
218 * Runs on mousewheel and mousedown of doc to check to see if we should collapse the picker
220 collapseIf: function(e) {
222 if (!me.isDestroyed && !e.within(me.bodyEl, false, true) && !e.within(me.picker.el, false, true)) {
228 * Return a reference to the picker component for this field, creating it if necessary by
229 * calling {@link #createPicker}.
230 * @return {Ext.Component} The picker component
232 getPicker: function() {
234 return me.picker || (me.picker = me.createPicker());
238 * Create and return the component to be used as this field's picker. Must be implemented
239 * by subclasses of Picker.
240 * @return {Ext.Component} The picker component
242 createPicker: Ext.emptyFn,
245 * Handles the trigger click; by default toggles between expanding and collapsing the
248 onTriggerClick: function() {
250 if (!me.readOnly && !me.disabled) {
260 mimicBlur: function(e) {
263 // ignore mousedown events within the picker element
264 if (!picker || !e.within(picker.el, false, true)) {
265 me.callParent(arguments);
269 onDestroy : function(){
271 Ext.destroy(me.picker, me.keyNav);