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