Roo/bootstrap/TriggerField.js
[roojs1] / Roo / bootstrap / TriggerField.js
1 /*
2  * - LGPL
3  *
4  * trigger field - base class for combo..
5  * 
6  */
7  
8 /**
9  * @class Roo.bootstrap.TriggerField
10  * @extends Roo.bootstrap.Input
11  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
14  * for which you can provide a custom implementation.  For example:
15  * <pre><code>
16 var trigger = new Roo.bootstrap.TriggerField();
17 trigger.onTriggerClick = myTriggerFn;
18 trigger.applyTo('my-field');
19 </code></pre>
20  *
21  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
23  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
24  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
25  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
26
27  * @constructor
28  * Create a new TriggerField.
29  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
30  * to the base TextField)
31  */
32 Roo.bootstrap.TriggerField = function(config){
33     this.mimicing = false;
34     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
35 };
36
37 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
38     /**
39      * @cfg {String} triggerClass A CSS class to apply to the trigger
40      */
41      /**
42      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
43      */
44     hideTrigger:false,
45
46     /**
47      * @cfg {Boolean} removable (true|false) special filter default false
48      */
49     removable : false,
50     
51     /** @cfg {Boolean} grow @hide */
52     /** @cfg {Number} growMin @hide */
53     /** @cfg {Number} growMax @hide */
54
55     /**
56      * @hide 
57      * @method
58      */
59     autoSize: Roo.emptyFn,
60     // private
61     monitorTab : true,
62     // private
63     deferHeight : true,
64
65     
66     actionMode : 'wrap',
67     
68     caret : false,
69     
70     
71     getAutoCreate : function(){
72        
73         var align = this.labelAlign || this.parentLabelAlign();
74         
75         var id = Roo.id();
76         
77         var cfg = {
78             cls: 'form-group' //input-group
79         };
80         
81         
82         var input =  {
83             tag: 'input',
84             id : id,
85             type : this.inputType,
86             cls : 'form-control',
87             autocomplete: 'new-password',
88             placeholder : this.placeholder || '' 
89             
90         };
91         if (this.name) {
92             input.name = this.name;
93         }
94         if (this.size) {
95             input.cls += ' input-' + this.size;
96         }
97         
98         if (this.disabled) {
99             input.disabled=true;
100         }
101         
102         var inputblock = input;
103         
104         if(this.hasFeedback && !this.allowBlank){
105             
106             var feedback = {
107                 tag: 'span',
108                 cls: 'glyphicon form-control-feedback'
109             };
110             
111             if(this.removable && !this.editable && !this.tickable){
112                 inputblock = {
113                     cls : 'has-feedback',
114                     cn :  [
115                         inputblock,
116                         {
117                             tag: 'button',
118                             html : 'x',
119                             cls : 'roo-combo-removable-btn close'
120                         },
121                         feedback
122                     ] 
123                 };
124             } else {
125                 inputblock = {
126                     cls : 'has-feedback',
127                     cn :  [
128                         inputblock,
129                         feedback
130                     ] 
131                 };
132             }
133
134         } else {
135             if(this.removable && !this.editable && !this.tickable){
136                 inputblock = {
137                     cls : 'roo-removable',
138                     cn :  [
139                         inputblock,
140                         {
141                             tag: 'button',
142                             html : 'x',
143                             cls : 'roo-combo-removable-btn close'
144                         }
145                     ] 
146                 };
147             }
148         }
149         
150         if (this.before || this.after) {
151             
152             inputblock = {
153                 cls : 'input-group',
154                 cn :  [] 
155             };
156             if (this.before) {
157                 inputblock.cn.push({
158                     tag :'span',
159                     cls : 'input-group-addon',
160                     html : this.before
161                 });
162             }
163             
164             inputblock.cn.push(input);
165             
166             if(this.hasFeedback && !this.allowBlank){
167                 inputblock.cls += ' has-feedback';
168                 inputblock.cn.push(feedback);
169             }
170             
171             if (this.after) {
172                 inputblock.cn.push({
173                     tag :'span',
174                     cls : 'input-group-addon',
175                     html : this.after
176                 });
177             }
178             
179         };
180         
181         var box = {
182             tag: 'div',
183             cn: [
184                 {
185                     tag: 'input',
186                     type : 'hidden',
187                     cls: 'form-hidden-field'
188                 },
189                 inputblock
190             ]
191             
192         };
193         
194         if(this.multiple){
195             box = {
196                 tag: 'div',
197                 cn: [
198                     {
199                         tag: 'input',
200                         type : 'hidden',
201                         cls: 'form-hidden-field'
202                     },
203                     {
204                         tag: 'ul',
205                         cls: 'roo-select2-choices',
206                         cn:[
207                             {
208                                 tag: 'li',
209                                 cls: 'roo-select2-search-field',
210                                 cn: [
211
212                                     inputblock
213                                 ]
214                             }
215                         ]
216                     }
217                 ]
218             }
219         };
220         
221         var combobox = {
222             cls: 'roo-select2-container input-group',
223             cn: [
224                 box
225 //                {
226 //                    tag: 'ul',
227 //                    cls: 'typeahead typeahead-long dropdown-menu',
228 //                    style: 'display:none'
229 //                }
230             ]
231         };
232         
233         if(!this.multiple && this.showToggleBtn){
234             
235             var caret = {
236                         tag: 'span',
237                         cls: 'caret'
238              };
239             if (this.caret != false) {
240                 caret = {
241                      tag: 'i',
242                      cls: 'fa fa-' + this.caret
243                 };
244                 
245             }
246             
247             combobox.cn.push({
248                 tag :'span',
249                 cls : 'input-group-addon btn dropdown-toggle',
250                 cn : [
251                     caret,
252                     {
253                         tag: 'span',
254                         cls: 'combobox-clear',
255                         cn  : [
256                             {
257                                 tag : 'i',
258                                 cls: 'icon-remove'
259                             }
260                         ]
261                     }
262                 ]
263
264             })
265         }
266         
267         if(this.multiple){
268             combobox.cls += ' roo-select2-container-multi';
269         }
270         
271         if (align ==='left' && this.fieldLabel.length) {
272             
273             cfg.cn = [
274                 {
275                     tag : 'i',
276                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
277                     tooltip : 'This field is required'
278                 },
279                 {
280                     tag: 'label',
281                     'for' :  id,
282                     cls : 'control-label',
283                     html : this.fieldLabel
284
285                 },
286                 {
287                     cls : "", 
288                     cn: [
289                         combobox
290                     ]
291                 }
292
293             ];
294             
295             var labelCfg = cfg.cn[1];
296             var contentCfg = cfg.cn[2];
297             
298             if(this.indicatorpos == 'right'){
299                 cfg.cn = [
300                     {
301                         tag: 'label',
302                         'for' :  id,
303                         cls : 'control-label',
304                         html : this.fieldLabel,
305                         cn: [
306                             {
307                                 tag : 'i',
308                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
309                                 tooltip : 'This field is required'
310                             }
311                         ]
312                     },
313                     {
314                         cls : "", 
315                         cn: [
316                             combobox
317                         ]
318                     }
319
320                 ];
321                 
322                 labelCfg = cfg.cn[0];
323                 contentCfg = cfg.cn[1];
324             }
325             
326             if(this.labelWidth > 12){
327                 labelCfg.style = "width: " + this.labelWidth + 'px';
328             }
329             
330             if(this.labelWidth < 13 && this.labelmd == 0){
331                 this.labelmd = this.labelWidth;
332             }
333             
334             if(this.labellg > 0){
335                 labelCfg.cls += ' col-lg-' + this.labellg;
336                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
337             }
338             
339             if(this.labelmd > 0){
340                 labelCfg.cls += ' col-md-' + this.labelmd;
341                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
342             }
343             
344             if(this.labelsm > 0){
345                 labelCfg.cls += ' col-sm-' + this.labelsm;
346                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
347             }
348             
349             if(this.labelxs > 0){
350                 labelCfg.cls += ' col-xs-' + this.labelxs;
351                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
352             }
353             
354         } else if ( this.fieldLabel.length) {
355 //                Roo.log(" label");
356             cfg.cn = [
357                 {
358                    tag : 'i',
359                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
360                    tooltip : 'This field is required'
361                },
362                {
363                    tag: 'label',
364                    //cls : 'input-group-addon',
365                    html : this.fieldLabel
366
367                },
368
369                combobox
370
371             ];
372             
373             if(this.indicatorpos == 'right'){
374                 
375                 cfg.cn = [
376                     {
377                        tag: 'label',
378                        //cls : 'input-group-addon',
379                        html : this.fieldLabel
380
381                     },
382                     {
383                        tag : 'i',
384                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
385                        tooltip : 'This field is required'
386                     },
387                     
388                     combobox
389
390                 ];
391
392             }
393
394         } else {
395             
396 //                Roo.log(" no label && no align");
397                 cfg = combobox
398                      
399                 
400         }
401         
402         var settings=this;
403         ['xs','sm','md','lg'].map(function(size){
404             if (settings[size]) {
405                 cfg.cls += ' col-' + size + '-' + settings[size];
406             }
407         });
408         
409         return cfg;
410         
411     },
412     
413     
414     
415     // private
416     onResize : function(w, h){
417 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
418 //        if(typeof w == 'number'){
419 //            var x = w - this.trigger.getWidth();
420 //            this.inputEl().setWidth(this.adjustWidth('input', x));
421 //            this.trigger.setStyle('left', x+'px');
422 //        }
423     },
424
425     // private
426     adjustSize : Roo.BoxComponent.prototype.adjustSize,
427
428     // private
429     getResizeEl : function(){
430         return this.inputEl();
431     },
432
433     // private
434     getPositionEl : function(){
435         return this.inputEl();
436     },
437
438     // private
439     alignErrorIcon : function(){
440         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
441     },
442
443     // private
444     initEvents : function(){
445         
446         this.createList();
447         
448         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
449         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
450         if(!this.multiple && this.showToggleBtn){
451             this.trigger = this.el.select('span.dropdown-toggle',true).first();
452             if(this.hideTrigger){
453                 this.trigger.setDisplayed(false);
454             }
455             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
456         }
457         
458         if(this.multiple){
459             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
460         }
461         
462         if(this.removable && !this.editable && !this.tickable){
463             var close = this.closeTriggerEl();
464             
465             if(close){
466                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
467                 close.on('click', this.removeBtnClick, this, close);
468             }
469         }
470         
471         //this.trigger.addClassOnOver('x-form-trigger-over');
472         //this.trigger.addClassOnClick('x-form-trigger-click');
473         
474         //if(!this.width){
475         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
476         //}
477     },
478     
479     closeTriggerEl : function()
480     {
481         var close = this.el.select('.roo-combo-removable-btn', true).first();
482         return close ? close : false;
483     },
484     
485     removeBtnClick : function(e, h, el)
486     {
487         e.preventDefault();
488         
489         if(this.fireEvent("remove", this) !== false){
490             this.reset();
491             this.fireEvent("afterremove", this)
492         }
493     },
494     
495     createList : function()
496     {
497         this.list = Roo.get(document.body).createChild({
498             tag: 'ul',
499             cls: 'typeahead typeahead-long dropdown-menu',
500             style: 'display:none'
501         });
502         
503         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
504         
505     },
506
507     // private
508     initTrigger : function(){
509        
510     },
511
512     // private
513     onDestroy : function(){
514         if(this.trigger){
515             this.trigger.removeAllListeners();
516           //  this.trigger.remove();
517         }
518         //if(this.wrap){
519         //    this.wrap.remove();
520         //}
521         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
522     },
523
524     // private
525     onFocus : function(){
526         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
527         /*
528         if(!this.mimicing){
529             this.wrap.addClass('x-trigger-wrap-focus');
530             this.mimicing = true;
531             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
532             if(this.monitorTab){
533                 this.el.on("keydown", this.checkTab, this);
534             }
535         }
536         */
537     },
538
539     // private
540     checkTab : function(e){
541         if(e.getKey() == e.TAB){
542             this.triggerBlur();
543         }
544     },
545
546     // private
547     onBlur : function(){
548         // do nothing
549     },
550
551     // private
552     mimicBlur : function(e, t){
553         /*
554         if(!this.wrap.contains(t) && this.validateBlur()){
555             this.triggerBlur();
556         }
557         */
558     },
559
560     // private
561     triggerBlur : function(){
562         this.mimicing = false;
563         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
564         if(this.monitorTab){
565             this.el.un("keydown", this.checkTab, this);
566         }
567         //this.wrap.removeClass('x-trigger-wrap-focus');
568         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
569     },
570
571     // private
572     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
573     validateBlur : function(e, t){
574         return true;
575     },
576
577     // private
578     onDisable : function(){
579         this.inputEl().dom.disabled = true;
580         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
581         //if(this.wrap){
582         //    this.wrap.addClass('x-item-disabled');
583         //}
584     },
585
586     // private
587     onEnable : function(){
588         this.inputEl().dom.disabled = false;
589         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
590         //if(this.wrap){
591         //    this.el.removeClass('x-item-disabled');
592         //}
593     },
594
595     // private
596     onShow : function(){
597         var ae = this.getActionEl();
598         
599         if(ae){
600             ae.dom.style.display = '';
601             ae.dom.style.visibility = 'visible';
602         }
603     },
604
605     // private
606     
607     onHide : function(){
608         var ae = this.getActionEl();
609         ae.dom.style.display = 'none';
610     },
611
612     /**
613      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
614      * by an implementing function.
615      * @method
616      * @param {EventObject} e
617      */
618     onTriggerClick : Roo.emptyFn
619 });
620