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