Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Trigger.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-form-field-Trigger'>/**
19 </span> * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
20  * The trigger has no default action, so you must assign a function to implement the trigger click handler by overriding
21  * {@link #onTriggerClick}. You can create a Trigger field directly, as it renders exactly like a combobox for which you
22  * can provide a custom implementation.
23  *
24  * For example:
25  *
26  *     @example
27  *     Ext.define('Ext.ux.CustomTrigger', {
28  *         extend: 'Ext.form.field.Trigger',
29  *         alias: 'widget.customtrigger',
30  *
31  *         // override onTriggerClick
32  *         onTriggerClick: function() {
33  *             Ext.Msg.alert('Status', 'You clicked my trigger!');
34  *         }
35  *     });
36  *
37  *     Ext.create('Ext.form.FormPanel', {
38  *         title: 'Form with TriggerField',
39  *         bodyPadding: 5,
40  *         width: 350,
41  *         renderTo: Ext.getBody(),
42  *         items:[{
43  *             xtype: 'customtrigger',
44  *             fieldLabel: 'Sample Trigger',
45  *             emptyText: 'click the trigger',
46  *         }]
47  *     });
48  *
49  * However, in general you will most likely want to use Trigger as the base class for a reusable component.
50  * {@link Ext.form.field.Date} and {@link Ext.form.field.ComboBox} are perfect examples of this.
51  */
52 Ext.define('Ext.form.field.Trigger', {
53     extend:'Ext.form.field.Text',
54     alias: ['widget.triggerfield', 'widget.trigger'],
55     requires: ['Ext.DomHelper', 'Ext.util.ClickRepeater', 'Ext.layout.component.field.Trigger'],
56     alternateClassName: ['Ext.form.TriggerField', 'Ext.form.TwinTriggerField', 'Ext.form.Trigger'],
57
58     // note: {id} here is really {inputId}, but {cmpId} is available
59     fieldSubTpl: [
60         '&lt;input id=&quot;{id}&quot; type=&quot;{type}&quot; ',
61             '&lt;tpl if=&quot;name&quot;&gt;name=&quot;{name}&quot; &lt;/tpl&gt;',
62             '&lt;tpl if=&quot;size&quot;&gt;size=&quot;{size}&quot; &lt;/tpl&gt;',
63             '&lt;tpl if=&quot;tabIdx&quot;&gt;tabIndex=&quot;{tabIdx}&quot; &lt;/tpl&gt;',
64             'class=&quot;{fieldCls} {typeCls}&quot; autocomplete=&quot;off&quot; /&gt;',
65         '&lt;div id=&quot;{cmpId}-triggerWrap&quot; class=&quot;{triggerWrapCls}&quot; role=&quot;presentation&quot;&gt;',
66             '{triggerEl}',
67             '&lt;div class=&quot;{clearCls}&quot; role=&quot;presentation&quot;&gt;&lt;/div&gt;',
68         '&lt;/div&gt;',
69         {
70             compiled: true,
71             disableFormats: true
72         }
73     ],
74
75 <span id='Ext-form-field-Trigger-cfg-triggerCls'>    /**
76 </span>     * @cfg {String} triggerCls
77      * An additional CSS class used to style the trigger button. The trigger will always get the {@link #triggerBaseCls}
78      * by default and triggerCls will be **appended** if specified.
79      */
80
81 <span id='Ext-form-field-Trigger-cfg-triggerBaseCls'>    /**
82 </span>     * @cfg {String} [triggerBaseCls='x-form-trigger']
83      * The base CSS class that is always added to the trigger button. The {@link #triggerCls} will be appended in
84      * addition to this class.
85      */
86     triggerBaseCls: Ext.baseCSSPrefix + 'form-trigger',
87
88 <span id='Ext-form-field-Trigger-cfg-triggerWrapCls'>    /**
89 </span>     * @cfg {String} [triggerWrapCls='x-form-trigger-wrap']
90      * The CSS class that is added to the div wrapping the trigger button(s).
91      */
92     triggerWrapCls: Ext.baseCSSPrefix + 'form-trigger-wrap',
93
94 <span id='Ext-form-field-Trigger-cfg-hideTrigger'>    /**
95 </span>     * @cfg {Boolean} hideTrigger
96      * true to hide the trigger element and display only the base text field
97      */
98     hideTrigger: false,
99
100 <span id='Ext-form-field-Trigger-cfg-editable'>    /**
101 </span>     * @cfg {Boolean} editable
102      * false to prevent the user from typing text directly into the field; the field can only have its value set via an
103      * action invoked by the trigger.
104      */
105     editable: true,
106
107 <span id='Ext-form-field-Trigger-cfg-readOnly'>    /**
108 </span>     * @cfg {Boolean} readOnly
109      * true to prevent the user from changing the field, and hides the trigger. Supercedes the editable and hideTrigger
110      * options if the value is true.
111      */
112     readOnly: false,
113
114 <span id='Ext-form-field-Trigger-cfg-selectOnFocus'>    /**
115 </span>     * @cfg {Boolean} [selectOnFocus=false]
116      * true to select any existing text in the field immediately on focus. Only applies when
117      * {@link #editable editable} = true
118      */
119
120 <span id='Ext-form-field-Trigger-cfg-repeatTriggerClick'>    /**
121 </span>     * @cfg {Boolean} repeatTriggerClick
122      * true to attach a {@link Ext.util.ClickRepeater click repeater} to the trigger.
123      */
124     repeatTriggerClick: false,
125
126
127 <span id='Ext-form-field-Trigger-method-autoSize'>    /**
128 </span>     * @hide
129      * @method autoSize
130      */
131     autoSize: Ext.emptyFn,
132     // private
133     monitorTab: true,
134     // private
135     mimicing: false,
136     // private
137     triggerIndexRe: /trigger-index-(\d+)/,
138
139     componentLayout: 'triggerfield',
140
141     initComponent: function() {
142         this.wrapFocusCls = this.triggerWrapCls + '-focus';
143         this.callParent(arguments);
144     },
145
146     // private
147     onRender: function(ct, position) {
148         var me = this,
149             triggerCls,
150             triggerBaseCls = me.triggerBaseCls,
151             triggerWrapCls = me.triggerWrapCls,
152             triggerConfigs = [],
153             i;
154
155         // triggerCls is a synonym for trigger1Cls, so copy it.
156         // TODO this trigger&lt;n&gt;Cls API design doesn't feel clean, especially where it butts up against the
157         // single triggerCls config. Should rethink this, perhaps something more structured like a list of
158         // trigger config objects that hold cls, handler, etc.
159         if (!me.trigger1Cls) {
160             me.trigger1Cls = me.triggerCls;
161         }
162
163         // Create as many trigger elements as we have trigger&lt;n&gt;Cls configs, but always at least one
164         for (i = 0; (triggerCls = me['trigger' + (i + 1) + 'Cls']) || i &lt; 1; i++) {
165             triggerConfigs.push({
166                 cls: [Ext.baseCSSPrefix + 'trigger-index-' + i, triggerBaseCls, triggerCls].join(' '),
167                 role: 'button'
168             });
169         }
170         triggerConfigs[i - 1].cls += ' ' + triggerBaseCls + '-last';
171
172 <span id='Ext-form-field-Trigger-property-triggerWrap'>        /**
173 </span>         * @property {Ext.Element} triggerWrap
174          * A reference to the div element wrapping the trigger button(s). Only set after the field has been rendered.
175          */
176         me.addChildEls('triggerWrap');
177
178         Ext.applyIf(me.subTplData, {
179             triggerWrapCls: triggerWrapCls,
180             triggerEl: Ext.DomHelper.markup(triggerConfigs),
181             clearCls: me.clearCls
182         });
183
184         me.callParent(arguments);
185
186 <span id='Ext-form-field-Trigger-property-triggerEl'>        /**
187 </span>         * @property {Ext.CompositeElement} triggerEl
188          * A composite of all the trigger button elements. Only set after the field has been rendered.
189          */
190         me.triggerEl = Ext.select('.' + triggerBaseCls, true, me.triggerWrap.dom);
191
192         me.doc = Ext.getDoc();
193         me.initTrigger();
194     },
195
196     onEnable: function() {
197         this.callParent();
198         this.triggerWrap.unmask();
199     },
200     
201     onDisable: function() {
202         this.callParent();
203         this.triggerWrap.mask();
204     },
205     
206     afterRender: function() {
207         this.callParent();
208         this.updateEditState();
209         this.triggerEl.unselectable();
210     },
211
212     updateEditState: function() {
213         var me = this,
214             inputEl = me.inputEl,
215             triggerWrap = me.triggerWrap,
216             noeditCls = Ext.baseCSSPrefix + 'trigger-noedit',
217             displayed,
218             readOnly;
219
220         if (me.rendered) {
221             if (me.readOnly) {
222                 inputEl.addCls(noeditCls);
223                 readOnly = true;
224                 displayed = false;
225             } else {
226                 if (me.editable) {
227                     inputEl.removeCls(noeditCls);
228                     readOnly = false;
229                 } else {
230                     inputEl.addCls(noeditCls);
231                     readOnly = true;
232                 }
233                 displayed = !me.hideTrigger;
234             }
235
236             triggerWrap.setDisplayed(displayed);
237             inputEl.dom.readOnly = readOnly;
238             me.doComponentLayout();
239         }
240     },
241
242 <span id='Ext-form-field-Trigger-method-getTriggerWidth'>    /**
243 </span>     * Get the total width of the trigger button area. Only useful after the field has been rendered.
244      * @return {Number} The trigger width
245      */
246     getTriggerWidth: function() {
247         var me = this,
248             triggerWrap = me.triggerWrap,
249             totalTriggerWidth = 0;
250         if (triggerWrap &amp;&amp; !me.hideTrigger &amp;&amp; !me.readOnly) {
251             me.triggerEl.each(function(trigger) {
252                 totalTriggerWidth += trigger.getWidth();
253             });
254             totalTriggerWidth += me.triggerWrap.getFrameWidth('lr');
255         }
256         return totalTriggerWidth;
257     },
258
259     setHideTrigger: function(hideTrigger) {
260         if (hideTrigger != this.hideTrigger) {
261             this.hideTrigger = hideTrigger;
262             this.updateEditState();
263         }
264     },
265
266 <span id='Ext-form-field-Trigger-method-setEditable'>    /**
267 </span>     * Sets the editable state of this field. This method is the runtime equivalent of setting the 'editable' config
268      * option at config time.
269      * @param {Boolean} editable True to allow the user to directly edit the field text. If false is passed, the user
270      * will only be able to modify the field using the trigger. Will also add a click event to the text field which
271      * will call the trigger. 
272      */
273     setEditable: function(editable) {
274         if (editable != this.editable) {
275             this.editable = editable;
276             this.updateEditState();
277         }
278     },
279
280 <span id='Ext-form-field-Trigger-method-setReadOnly'>    /**
281 </span>     * Sets the read-only state of this field. This method is the runtime equivalent of setting the 'readOnly' config
282      * option at config time.
283      * @param {Boolean} readOnly True to prevent the user changing the field and explicitly hide the trigger. Setting
284      * this to true will superceed settings editable and hideTrigger. Setting this to false will defer back to editable
285      * and hideTrigger.
286      */
287     setReadOnly: function(readOnly) {
288         if (readOnly != this.readOnly) {
289             this.readOnly = readOnly;
290             this.updateEditState();
291         }
292     },
293
294     // private
295     initTrigger: function() {
296         var me = this,
297             triggerWrap = me.triggerWrap,
298             triggerEl = me.triggerEl;
299
300         if (me.repeatTriggerClick) {
301             me.triggerRepeater = Ext.create('Ext.util.ClickRepeater', triggerWrap, {
302                 preventDefault: true,
303                 handler: function(cr, e) {
304                     me.onTriggerWrapClick(e);
305                 }
306             });
307         } else {
308             me.mon(me.triggerWrap, 'click', me.onTriggerWrapClick, me);
309         }
310
311         triggerEl.addClsOnOver(me.triggerBaseCls + '-over');
312         triggerEl.each(function(el, c, i) {
313             el.addClsOnOver(me['trigger' + (i + 1) + 'Cls'] + '-over');
314         });
315         triggerEl.addClsOnClick(me.triggerBaseCls + '-click');
316         triggerEl.each(function(el, c, i) {
317             el.addClsOnClick(me['trigger' + (i + 1) + 'Cls'] + '-click');
318         });
319     },
320
321     // private
322     onDestroy: function() {
323         var me = this;
324         Ext.destroyMembers(me, 'triggerRepeater', 'triggerWrap', 'triggerEl');
325         delete me.doc;
326         me.callParent();
327     },
328
329     // private
330     onFocus: function() {
331         var me = this;
332         me.callParent();
333         if (!me.mimicing) {
334             me.bodyEl.addCls(me.wrapFocusCls);
335             me.mimicing = true;
336             me.mon(me.doc, 'mousedown', me.mimicBlur, me, {
337                 delay: 10
338             });
339             if (me.monitorTab) {
340                 me.on('specialkey', me.checkTab, me);
341             }
342         }
343     },
344
345     // private
346     checkTab: function(me, e) {
347         if (!this.ignoreMonitorTab &amp;&amp; e.getKey() == e.TAB) {
348             this.triggerBlur();
349         }
350     },
351
352     // private
353     onBlur: Ext.emptyFn,
354
355     // private
356     mimicBlur: function(e) {
357         if (!this.isDestroyed &amp;&amp; !this.bodyEl.contains(e.target) &amp;&amp; this.validateBlur(e)) {
358             this.triggerBlur();
359         }
360     },
361
362     // private
363     triggerBlur: function() {
364         var me = this;
365         me.mimicing = false;
366         me.mun(me.doc, 'mousedown', me.mimicBlur, me);
367         if (me.monitorTab &amp;&amp; me.inputEl) {
368             me.un('specialkey', me.checkTab, me);
369         }
370         Ext.form.field.Trigger.superclass.onBlur.call(me);
371         if (me.bodyEl) {
372             me.bodyEl.removeCls(me.wrapFocusCls);
373         }
374     },
375
376     beforeBlur: Ext.emptyFn,
377
378     // private
379     // This should be overridden by any subclass that needs to check whether or not the field can be blurred.
380     validateBlur: function(e) {
381         return true;
382     },
383
384     // private
385     // process clicks upon triggers.
386     // determine which trigger index, and dispatch to the appropriate click handler
387     onTriggerWrapClick: function(e) {
388         var me = this,
389             t = e &amp;&amp; e.getTarget('.' + Ext.baseCSSPrefix + 'form-trigger', null),
390             match = t &amp;&amp; t.className.match(me.triggerIndexRe),
391             idx,
392             triggerClickMethod;
393
394         if (match &amp;&amp; !me.readOnly) {
395             idx = parseInt(match[1], 10);
396             triggerClickMethod = me['onTrigger' + (idx + 1) + 'Click'] || me.onTriggerClick;
397             if (triggerClickMethod) {
398                 triggerClickMethod.call(me, e);
399             }
400         }
401     },
402
403 <span id='Ext-form-field-Trigger-method-onTriggerClick'>    /**
404 </span>     * @method onTriggerClick
405      * @protected
406      * The function that should handle the trigger's click event. This method does nothing by default until overridden
407      * by an implementing function. See Ext.form.field.ComboBox and Ext.form.field.Date for sample implementations.
408      * @param {Ext.EventObject} e
409      */
410     onTriggerClick: Ext.emptyFn
411
412 <span id='Ext-form-field-Trigger-cfg-grow'>    /**
413 </span>     * @cfg {Boolean} grow @hide
414      */
415 <span id='Ext-form-field-Trigger-cfg-growMin'>    /**
416 </span>     * @cfg {Number} growMin @hide
417      */
418 <span id='Ext-form-field-Trigger-cfg-growMax'>    /**
419 </span>     * @cfg {Number} growMax @hide
420      */
421 });
422 </pre>
423 </body>
424 </html>