3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.form.field.Trigger
17 * @extends Ext.form.field.Text
18 * <p>Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
19 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
20 * overriding {@link #onTriggerClick}. You can create a Trigger field directly, as it renders exactly like a combobox
21 * for which you can provide a custom implementation.
22 * {@img Ext.form.field.Trigger/Ext.form.field.Trigger.png Ext.form.field.Trigger component}
25 Ext.define('Ext.ux.CustomTrigger', {
26 extend: 'Ext.form.field.Trigger',
27 alias: 'widget.customtrigger',
29 // override onTriggerClick
30 onTriggerClick: function() {
31 Ext.Msg.alert('Status', 'You clicked my trigger!');
35 Ext.create('Ext.form.FormPanel', {
36 title: 'Form with TriggerField',
39 renderTo: Ext.getBody(),
41 xtype: 'customtrigger',
42 fieldLabel: 'Sample Trigger',
43 emptyText: 'click the trigger',
48 * <p>However, in general you will most likely want to use Trigger as the base class for a reusable component.
49 * {@link Ext.form.field.Date} and {@link Ext.form.field.ComboBox} are perfect examples of this.</p>
52 * Create a new Trigger field.
53 * @param {Object} config Configuration options (valid {@Ext.form.field.Text} config options will also be applied
54 * to the base Text field)
56 Ext.define('Ext.form.field.Trigger', {
57 extend:'Ext.form.field.Text',
58 alias: ['widget.triggerfield', 'widget.trigger'],
59 requires: ['Ext.core.DomHelper', 'Ext.util.ClickRepeater', 'Ext.layout.component.field.Trigger'],
60 alternateClassName: ['Ext.form.TriggerField', 'Ext.form.TwinTriggerField', 'Ext.form.Trigger'],
63 '<input id="{id}" type="{type}" ',
64 '<tpl if="name">name="{name}" </tpl>',
65 '<tpl if="size">size="{size}" </tpl>',
66 '<tpl if="tabIdx">tabIndex="{tabIdx}" </tpl>',
67 'class="{fieldCls} {typeCls}" autocomplete="off" />',
68 '<div class="{triggerWrapCls}" role="presentation">',
70 '<div class="{clearCls}" role="presentation"></div>',
79 * @cfg {String} triggerCls
80 * An additional CSS class used to style the trigger button. The trigger will always get the
81 * {@link #triggerBaseCls} by default and <tt>triggerCls</tt> will be <b>appended</b> if specified.
82 * Defaults to undefined.
86 * @cfg {String} triggerBaseCls
87 * The base CSS class that is always added to the trigger button. The {@link #triggerCls} will be
88 * appended in addition to this class.
90 triggerBaseCls: Ext.baseCSSPrefix + 'form-trigger',
93 * @cfg {String} triggerWrapCls
94 * The CSS class that is added to the div wrapping the trigger button(s).
96 triggerWrapCls: Ext.baseCSSPrefix + 'form-trigger-wrap',
99 * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base
100 * text field (defaults to <tt>false</tt>)
105 * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field;
106 * the field can only have its value set via an action invoked by the trigger. (defaults to <tt>true</tt>).
111 * @cfg {Boolean} readOnly <tt>true</tt> to prevent the user from changing the field, and
112 * hides the trigger. Supercedes the editable and hideTrigger options if the value is true.
113 * (defaults to <tt>false</tt>)
118 * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.
119 * Only applies when <tt>{@link #editable editable} = true</tt> (defaults to <tt>false</tt>).
123 * @cfg {Boolean} repeatTriggerClick <tt>true</tt> to attach a {@link Ext.util.ClickRepeater click repeater}
124 * to the trigger. Defaults to <tt>false</tt>.
126 repeatTriggerClick: false,
133 autoSize: Ext.emptyFn,
139 triggerIndexRe: /trigger-index-(\d+)/,
141 componentLayout: 'triggerfield',
143 initComponent: function() {
144 this.wrapFocusCls = this.triggerWrapCls + '-focus';
145 this.callParent(arguments);
149 onRender: function(ct, position) {
152 triggerBaseCls = me.triggerBaseCls,
153 triggerWrapCls = me.triggerWrapCls,
157 // triggerCls is a synonym for trigger1Cls, so copy it.
158 // TODO this trigger<n>Cls API design doesn't feel clean, especially where it butts up against the
159 // single triggerCls config. Should rethink this, perhaps something more structured like a list of
160 // trigger config objects that hold cls, handler, etc.
161 if (!me.trigger1Cls) {
162 me.trigger1Cls = me.triggerCls;
165 // Create as many trigger elements as we have trigger<n>Cls configs, but always at least one
166 for (i = 0; (triggerCls = me['trigger' + (i + 1) + 'Cls']) || i < 1; i++) {
167 triggerConfigs.push({
168 cls: [Ext.baseCSSPrefix + 'trigger-index-' + i, triggerBaseCls, triggerCls].join(' '),
172 triggerConfigs[i - 1].cls += ' ' + triggerBaseCls + '-last';
174 Ext.applyIf(me.renderSelectors, {
176 * @property triggerWrap
177 * @type Ext.core.Element
178 * A reference to the div element wrapping the trigger button(s). Only set after the field has been rendered.
180 triggerWrap: '.' + triggerWrapCls
182 Ext.applyIf(me.subTplData, {
183 triggerWrapCls: triggerWrapCls,
184 triggerEl: Ext.core.DomHelper.markup(triggerConfigs),
185 clearCls: me.clearCls
188 me.callParent(arguments);
191 * @property triggerEl
192 * @type Ext.CompositeElement
193 * A composite of all the trigger button elements. Only set after the field has been rendered.
195 me.triggerEl = Ext.select('.' + triggerBaseCls, true, me.triggerWrap.dom);
197 me.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
201 onEnable: function() {
203 this.triggerWrap.unmask();
206 onDisable: function() {
208 this.triggerWrap.mask();
211 afterRender: function() {
213 this.updateEditState();
216 updateEditState: function() {
218 inputEl = me.inputEl,
219 triggerWrap = me.triggerWrap,
220 noeditCls = Ext.baseCSSPrefix + 'trigger-noedit',
226 inputEl.addCls(noeditCls);
231 inputEl.removeCls(noeditCls);
234 inputEl.addCls(noeditCls);
237 displayed = !me.hideTrigger;
240 triggerWrap.setDisplayed(displayed);
241 inputEl.dom.readOnly = readOnly;
242 me.doComponentLayout();
247 * Get the total width of the trigger button area. Only useful after the field has been rendered.
248 * @return {Number} The trigger width
250 getTriggerWidth: function() {
252 triggerWrap = me.triggerWrap,
253 totalTriggerWidth = 0;
254 if (triggerWrap && !me.hideTrigger && !me.readOnly) {
255 me.triggerEl.each(function(trigger) {
256 totalTriggerWidth += trigger.getWidth();
258 totalTriggerWidth += me.triggerWrap.getFrameWidth('lr');
260 return totalTriggerWidth;
263 setHideTrigger: function(hideTrigger) {
264 if (hideTrigger != this.hideTrigger) {
265 this.hideTrigger = hideTrigger;
266 this.updateEditState();
271 * @param {Boolean} editable True to allow the user to directly edit the field text
272 * Allow or prevent the user from directly editing the field text. If false is passed,
273 * the user will only be able to modify the field using the trigger. Will also add
274 * a click event to the text field which will call the trigger. This method
275 * is the runtime equivalent of setting the 'editable' config option at config time.
277 setEditable: function(editable) {
278 if (editable != this.editable) {
279 this.editable = editable;
280 this.updateEditState();
285 * @param {Boolean} readOnly True to prevent the user changing the field and explicitly
287 * Setting this to true will superceed settings editable and hideTrigger.
288 * Setting this to false will defer back to editable and hideTrigger. This method
289 * is the runtime equivalent of setting the 'readOnly' config option at config time.
291 setReadOnly: function(readOnly) {
292 if (readOnly != this.readOnly) {
293 this.readOnly = readOnly;
294 this.updateEditState();
299 initTrigger: function() {
301 triggerWrap = me.triggerWrap,
302 triggerEl = me.triggerEl;
304 if (me.repeatTriggerClick) {
305 me.triggerRepeater = Ext.create('Ext.util.ClickRepeater', triggerWrap, {
306 preventDefault: true,
307 handler: function(cr, e) {
308 me.onTriggerWrapClick(e);
312 me.mon(me.triggerWrap, 'click', me.onTriggerWrapClick, me);
315 triggerEl.addClsOnOver(me.triggerBaseCls + '-over');
316 triggerEl.each(function(el, c, i) {
317 el.addClsOnOver(me['trigger' + (i + 1) + 'Cls'] + '-over');
319 triggerEl.addClsOnClick(me.triggerBaseCls + '-click');
320 triggerEl.each(function(el, c, i) {
321 el.addClsOnClick(me['trigger' + (i + 1) + 'Cls'] + '-click');
326 onDestroy: function() {
328 Ext.destroyMembers(me, 'triggerRepeater', 'triggerWrap', 'triggerEl');
334 onFocus: function() {
338 me.bodyEl.addCls(me.wrapFocusCls);
340 me.mon(me.doc, 'mousedown', me.mimicBlur, me, {
344 me.on('specialkey', me.checkTab, me);
350 checkTab: function(me, e) {
351 if (!this.ignoreMonitorTab && e.getKey() == e.TAB) {
360 mimicBlur: function(e) {
361 if (!this.isDestroyed && !this.bodyEl.contains(e.target) && this.validateBlur(e)) {
367 triggerBlur: function() {
370 me.mun(me.doc, 'mousedown', me.mimicBlur, me);
371 if (me.monitorTab && me.inputEl) {
372 me.un('specialkey', me.checkTab, me);
374 Ext.form.field.Trigger.superclass.onBlur.call(me);
376 me.bodyEl.removeCls(me.wrapFocusCls);
380 beforeBlur: Ext.emptyFn,
383 // This should be overridden by any subclass that needs to check whether or not the field can be blurred.
384 validateBlur: function(e) {
389 // process clicks upon triggers.
390 // determine which trigger index, and dispatch to the appropriate click handler
391 onTriggerWrapClick: function(e) {
393 t = e && e.getTarget('.' + Ext.baseCSSPrefix + 'form-trigger', null),
394 match = t && t.className.match(me.triggerIndexRe),
398 if (match && !me.readOnly) {
399 idx = parseInt(match[1], 10);
400 triggerClickMethod = me['onTrigger' + (idx + 1) + 'Click'] || me.onTriggerClick;
401 if (triggerClickMethod) {
402 triggerClickMethod.call(me, e);
408 * The function that should handle the trigger's click event. This method does nothing by default
409 * until overridden by an implementing function. See Ext.form.field.ComboBox and Ext.form.field.Date for
410 * sample implementations.
412 * @param {Ext.EventObject} e
414 onTriggerClick: Ext.emptyFn
417 * @cfg {Boolean} grow @hide
420 * @cfg {Number} growMin @hide
423 * @cfg {Number} growMax @hide