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         Roo.log(this);
104         if(this.hasFeedback && !this.allowBlank){
105             
106             Roo.log('in 1');
107             var feedback = {
108                 tag: 'span',
109                 cls: 'glyphicon form-control-feedback'
110             };
111             
112             if(this.removable && !this.editable && !this.tickable){
113                 
114                 Roo.log('in 2');
115                 
116                 inputblock = {
117                     cls : 'has-feedback',
118                     cn :  [
119                         inputblock,
120                         {
121                             tag: 'button',
122                             html : 'x',
123                             cls : 'roo-combo-removable-btn close'
124                         },
125                         feedback
126                     ] 
127                 };
128             } else {
129                 
130                 Roo.log('in 3');
131                 
132                 inputblock = {
133                     cls : 'has-feedback',
134                     cn :  [
135                         inputblock,
136                         feedback
137                     ] 
138                 };
139             }
140
141         } else {
142             if(this.removable && !this.editable && !this.tickable){
143                 inputblock = {
144                     cls : 'roo-removable',
145                     cn :  [
146                         inputblock,
147                         {
148                             tag: 'button',
149                             html : 'x',
150                             cls : 'roo-combo-removable-btn close'
151                         }
152                     ] 
153                 };
154             }
155         }
156         
157         if (this.before || this.after) {
158             
159             inputblock = {
160                 cls : 'input-group',
161                 cn :  [] 
162             };
163             if (this.before) {
164                 inputblock.cn.push({
165                     tag :'span',
166                     cls : 'input-group-addon',
167                     html : this.before
168                 });
169             }
170             
171             inputblock.cn.push(input);
172             
173             if(this.hasFeedback && !this.allowBlank){
174                 inputblock.cls += ' has-feedback';
175                 inputblock.cn.push(feedback);
176             }
177             
178             if (this.after) {
179                 inputblock.cn.push({
180                     tag :'span',
181                     cls : 'input-group-addon',
182                     html : this.after
183                 });
184             }
185             
186         };
187         
188         var box = {
189             tag: 'div',
190             cn: [
191                 {
192                     tag: 'input',
193                     type : 'hidden',
194                     cls: 'form-hidden-field'
195                 },
196                 inputblock
197             ]
198             
199         };
200         
201         if(this.multiple){
202             box = {
203                 tag: 'div',
204                 cn: [
205                     {
206                         tag: 'input',
207                         type : 'hidden',
208                         cls: 'form-hidden-field'
209                     },
210                     {
211                         tag: 'ul',
212                         cls: 'roo-select2-choices',
213                         cn:[
214                             {
215                                 tag: 'li',
216                                 cls: 'roo-select2-search-field',
217                                 cn: [
218
219                                     inputblock
220                                 ]
221                             }
222                         ]
223                     }
224                 ]
225             }
226         };
227         
228         var combobox = {
229             cls: 'roo-select2-container input-group',
230             cn: [
231                 box
232 //                {
233 //                    tag: 'ul',
234 //                    cls: 'typeahead typeahead-long dropdown-menu',
235 //                    style: 'display:none'
236 //                }
237             ]
238         };
239         
240         if(!this.multiple && this.showToggleBtn){
241             
242             var caret = {
243                         tag: 'span',
244                         cls: 'caret'
245              };
246             if (this.caret != false) {
247                 caret = {
248                      tag: 'i',
249                      cls: 'fa fa-' + this.caret
250                 };
251                 
252             }
253             
254             combobox.cn.push({
255                 tag :'span',
256                 cls : 'input-group-addon btn dropdown-toggle',
257                 cn : [
258                     caret,
259                     {
260                         tag: 'span',
261                         cls: 'combobox-clear',
262                         cn  : [
263                             {
264                                 tag : 'i',
265                                 cls: 'icon-remove'
266                             }
267                         ]
268                     }
269                 ]
270
271             })
272         }
273         
274         if(this.multiple){
275             combobox.cls += ' roo-select2-container-multi';
276         }
277         
278         if (align ==='left' && this.fieldLabel.length) {
279             
280             cfg.cls += ' roo-form-group-label-left';
281
282             cfg.cn = [
283                 {
284                     tag : 'i',
285                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
286                     tooltip : 'This field is required'
287                 },
288                 {
289                     tag: 'label',
290                     'for' :  id,
291                     cls : 'control-label',
292                     html : this.fieldLabel
293
294                 },
295                 {
296                     cls : "", 
297                     cn: [
298                         combobox
299                     ]
300                 }
301
302             ];
303             
304             var labelCfg = cfg.cn[1];
305             var contentCfg = cfg.cn[2];
306             
307             if(this.indicatorpos == 'right'){
308                 cfg.cn = [
309                     {
310                         tag: 'label',
311                         'for' :  id,
312                         cls : 'control-label',
313                         cn : [
314                             {
315                                 tag : 'span',
316                                 html : this.fieldLabel
317                             },
318                             {
319                                 tag : 'i',
320                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
321                                 tooltip : 'This field is required'
322                             }
323                         ]
324                     },
325                     {
326                         cls : "", 
327                         cn: [
328                             combobox
329                         ]
330                     }
331
332                 ];
333                 
334                 labelCfg = cfg.cn[0];
335                 contentCfg = cfg.cn[1];
336             }
337             
338             if(this.labelWidth > 12){
339                 labelCfg.style = "width: " + this.labelWidth + 'px';
340             }
341             
342             if(this.labelWidth < 13 && this.labelmd == 0){
343                 this.labelmd = this.labelWidth;
344             }
345             
346             if(this.labellg > 0){
347                 labelCfg.cls += ' col-lg-' + this.labellg;
348                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
349             }
350             
351             if(this.labelmd > 0){
352                 labelCfg.cls += ' col-md-' + this.labelmd;
353                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
354             }
355             
356             if(this.labelsm > 0){
357                 labelCfg.cls += ' col-sm-' + this.labelsm;
358                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
359             }
360             
361             if(this.labelxs > 0){
362                 labelCfg.cls += ' col-xs-' + this.labelxs;
363                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
364             }
365             
366         } else if ( this.fieldLabel.length) {
367 //                Roo.log(" label");
368             cfg.cn = [
369                 {
370                    tag : 'i',
371                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
372                    tooltip : 'This field is required'
373                },
374                {
375                    tag: 'label',
376                    //cls : 'input-group-addon',
377                    html : this.fieldLabel
378
379                },
380
381                combobox
382
383             ];
384             
385             if(this.indicatorpos == 'right'){
386                 
387                 cfg.cn = [
388                     {
389                        tag: 'label',
390                        cn : [
391                            {
392                                tag : 'span',
393                                html : this.fieldLabel
394                            },
395                            {
396                               tag : 'i',
397                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
398                               tooltip : 'This field is required'
399                            }
400                        ]
401
402                     },
403                     combobox
404
405                 ];
406
407             }
408
409         } else {
410             
411 //                Roo.log(" no label && no align");
412                 cfg = combobox
413                      
414                 
415         }
416         
417         var settings=this;
418         ['xs','sm','md','lg'].map(function(size){
419             if (settings[size]) {
420                 cfg.cls += ' col-' + size + '-' + settings[size];
421             }
422         });
423         
424         return cfg;
425         
426     },
427     
428     
429     
430     // private
431     onResize : function(w, h){
432 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
433 //        if(typeof w == 'number'){
434 //            var x = w - this.trigger.getWidth();
435 //            this.inputEl().setWidth(this.adjustWidth('input', x));
436 //            this.trigger.setStyle('left', x+'px');
437 //        }
438     },
439
440     // private
441     adjustSize : Roo.BoxComponent.prototype.adjustSize,
442
443     // private
444     getResizeEl : function(){
445         return this.inputEl();
446     },
447
448     // private
449     getPositionEl : function(){
450         return this.inputEl();
451     },
452
453     // private
454     alignErrorIcon : function(){
455         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
456     },
457
458     // private
459     initEvents : function(){
460         
461         this.createList();
462         
463         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
464         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
465         if(!this.multiple && this.showToggleBtn){
466             this.trigger = this.el.select('span.dropdown-toggle',true).first();
467             if(this.hideTrigger){
468                 this.trigger.setDisplayed(false);
469             }
470             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
471         }
472         
473         if(this.multiple){
474             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
475         }
476         
477         if(this.removable && !this.editable && !this.tickable){
478             var close = this.closeTriggerEl();
479             
480             if(close){
481                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
482                 close.on('click', this.removeBtnClick, this, close);
483             }
484         }
485         
486         //this.trigger.addClassOnOver('x-form-trigger-over');
487         //this.trigger.addClassOnClick('x-form-trigger-click');
488         
489         //if(!this.width){
490         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
491         //}
492     },
493     
494     closeTriggerEl : function()
495     {
496         var close = this.el.select('.roo-combo-removable-btn', true).first();
497         return close ? close : false;
498     },
499     
500     removeBtnClick : function(e, h, el)
501     {
502         e.preventDefault();
503         
504         if(this.fireEvent("remove", this) !== false){
505             this.reset();
506             this.fireEvent("afterremove", this)
507         }
508     },
509     
510     createList : function()
511     {
512         this.list = Roo.get(document.body).createChild({
513             tag: 'ul',
514             cls: 'typeahead typeahead-long dropdown-menu',
515             style: 'display:none'
516         });
517         
518         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
519         
520     },
521
522     // private
523     initTrigger : function(){
524        
525     },
526
527     // private
528     onDestroy : function(){
529         if(this.trigger){
530             this.trigger.removeAllListeners();
531           //  this.trigger.remove();
532         }
533         //if(this.wrap){
534         //    this.wrap.remove();
535         //}
536         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
537     },
538
539     // private
540     onFocus : function(){
541         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
542         /*
543         if(!this.mimicing){
544             this.wrap.addClass('x-trigger-wrap-focus');
545             this.mimicing = true;
546             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
547             if(this.monitorTab){
548                 this.el.on("keydown", this.checkTab, this);
549             }
550         }
551         */
552     },
553
554     // private
555     checkTab : function(e){
556         if(e.getKey() == e.TAB){
557             this.triggerBlur();
558         }
559     },
560
561     // private
562     onBlur : function(){
563         // do nothing
564     },
565
566     // private
567     mimicBlur : function(e, t){
568         /*
569         if(!this.wrap.contains(t) && this.validateBlur()){
570             this.triggerBlur();
571         }
572         */
573     },
574
575     // private
576     triggerBlur : function(){
577         this.mimicing = false;
578         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
579         if(this.monitorTab){
580             this.el.un("keydown", this.checkTab, this);
581         }
582         //this.wrap.removeClass('x-trigger-wrap-focus');
583         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
584     },
585
586     // private
587     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
588     validateBlur : function(e, t){
589         return true;
590     },
591
592     // private
593     onDisable : function(){
594         this.inputEl().dom.disabled = true;
595         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
596         //if(this.wrap){
597         //    this.wrap.addClass('x-item-disabled');
598         //}
599     },
600
601     // private
602     onEnable : function(){
603         this.inputEl().dom.disabled = false;
604         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
605         //if(this.wrap){
606         //    this.el.removeClass('x-item-disabled');
607         //}
608     },
609
610     // private
611     onShow : function(){
612         var ae = this.getActionEl();
613         
614         if(ae){
615             ae.dom.style.display = '';
616             ae.dom.style.visibility = 'visible';
617         }
618     },
619
620     // private
621     
622     onHide : function(){
623         var ae = this.getActionEl();
624         ae.dom.style.display = 'none';
625     },
626
627     /**
628      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
629      * by an implementing function.
630      * @method
631      * @param {EventObject} e
632      */
633     onTriggerClick : Roo.emptyFn
634 });
635