roojs-ui.js
[roojs1] / Roo / form / ComboNested.js
1 /*
2  * RooJS Library 1.1.1
3  * Copyright(c) 2008-2011  Alan Knowles
4  *
5  * License - LGPL
6  */
7  
8
9 /**
10  * @class Roo.form.ComboNested
11  * @extends Roo.form.ComboBox
12  * A combobox for that allows selection of nested items in a list,
13  * eg.
14  *
15  *  Book
16  *    -> red
17  *    -> green
18  *  Table
19  *    -> square
20  *      ->red
21  *      ->green
22  *    -> rectangle
23  *      ->green
24  *      
25  * 
26  * @constructor
27  * Create a new ComboNested
28  * @param {Object} config Configuration options
29  */
30 Roo.form.ComboNested = function(config){
31     Roo.form.ComboCheck.superclass.constructor.call(this, config);
32     // should verify some data...
33     // like
34     // hiddenName = required..
35     // displayField = required
36     // valudField == required
37     var req= [ 'hiddenName', 'displayField', 'valueField' ];
38     var _t = this;
39     Roo.each(req, function(e) {
40         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
41             throw "Roo.form.ComboNested : missing value for: " + e;
42         }
43     });
44      
45     
46 };
47
48 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
49    
50    
51     list : null, // the outermost div..
52     innerLists : null, // the
53     views : null,
54     stores : null,
55     // private
56     onRender : function(ct, position)
57     {
58         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
59         
60         if(this.hiddenName){
61             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
62                     'before', true);
63             this.hiddenField.value =
64                 this.hiddenValue !== undefined ? this.hiddenValue :
65                 this.value !== undefined ? this.value : '';
66
67             // prevent input submission
68             this.el.dom.removeAttribute('name');
69              
70              
71         }
72         
73         if(Roo.isGecko){
74             this.el.dom.setAttribute('autocomplete', 'off');
75         }
76
77         var cls = 'x-combo-list';
78
79         this.list = new Roo.Layer({
80             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
81         });
82
83         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
84         this.list.setWidth(lw);
85         this.list.swallowEvent('mousewheel');
86         this.assetHeight = 0;
87
88         if(this.title){
89             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
90             this.assetHeight += this.header.getHeight();
91         }
92         this.innerLists = [];
93         this.views = [];
94         this.stores = [];
95         for (var i =0 ; i < 3; i++) {
96             this.onRenderList( cls, i);
97         }
98         
99         // always needs footer, as we are going to have an 'OK' button.
100         this.footer = this.list.createChild({cls:cls+'-ft'});
101         this.pageTb = new Roo.Toolbar(this.footer);  
102         var _this = this;
103         this.pageTb.add(  {
104             
105             text: 'Done',
106             handler: function()
107             {
108                 _this.collapse();
109             }
110         });
111         
112         if ( this.allowBlank && !this.disableClear) {
113             
114             this.pageTb.add(new Roo.Toolbar.Fill(), {
115                 cls: 'x-btn-icon x-btn-clear',
116                 text: '&#160;',
117                 handler: function()
118                 {
119                     _this.collapse();
120                     _this.clearValue();
121                     _this.onSelect(false, -1);
122                 }
123             });
124         }
125         if (this.footer) {
126             this.assetHeight += this.footer.getHeight();
127         }
128         
129     },
130     onRenderList : function (  cls, i)
131     {
132         
133         var lw = Math.floor(
134                 ((this.listWidth * 3 || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / 3
135         );
136         
137         this.list.setWidth(lw); // default to '1'
138
139         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
140         //il.on('mouseover', this.onViewOver, this, { list:  i });
141         //il.on('mousemove', this.onViewMove, this, { list:  i });
142         il.setWidth(lw);
143         il.setStyle({ 'overflow-x' : 'hidden'});
144
145         if(!this.tpl){
146             this.tpl = new Roo.Template({
147                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
148                 isEmpty: function (value, allValues) {
149                     //Roo.log(value);
150                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
151                     return dl ? 'has-children' : 'no-children'
152                 }
153             });
154         }
155         
156         var store  = this.store;
157         if (i > 0) {
158             store  = new Roo.data.SimpleStore({
159                 //fields : this.store.reader.meta.fields,
160                 reader : this.store.reader,
161                 data : [ ]
162             });
163         }
164         this.stores[i]  = store;
165                 
166         
167         
168         var view = this.views[i] = new Roo.View(
169             il,
170             this.tpl,
171             {
172                 singleSelect:true,
173                 store: store,
174                 selectedClass: this.selectedClass
175             }
176         );
177         view.getEl().setWidth(lw);
178         view.getEl().setStyle({
179             position: i < 1 ? 'relative' : 'absolute',
180             top: 0,
181             left: (i * lw ) + 'px',
182             display : i > 0 ? 'none' : 'block'
183         });
184         view.on('selectionchange', this.onSelectChange, this, {list : i });
185         view.on('dblclick', this.onDoubleClick, this, {list : i });
186         //view.on('click', this.onViewClick, this, { list : i });
187
188         store.on('beforeload', this.onBeforeLoad, this);
189         store.on('load',  this.onLoad, this, { list  : i});
190         store.on('loadexception', this.onLoadException, this);
191
192         // hide the other vies..
193         
194         
195         
196     },
197     onResize : function()  {},
198     
199     restrictHeight : function()
200     {
201         var mh = 0;
202         Roo.each(this.innerLists, function(il,i) {
203             var el = this.views[i].getEl();
204             el.dom.style.height = '';
205             var inner = el.dom;
206             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
207             // only adjust heights on other ones..
208             if (i < 1) {
209                 
210                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
211                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
212                 mh = Math.max(el.getHeight(), mh);
213             }
214             
215             
216         }, this);
217         
218         this.list.beginUpdate();
219         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
220         this.list.alignTo(this.el, this.listAlign);
221         this.list.endUpdate();
222         
223     },
224      
225     
226     // -- store handlers..
227     // private
228     onBeforeLoad : function()
229     {
230         if(!this.hasFocus){
231             return;
232         }
233         this.innerLists[0].update(this.loadingText ?
234                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
235         this.restrictHeight();
236         this.selectedIndex = -1;
237     },
238     // private
239     onLoad : function(a,b,c,d)
240     {
241         
242         if(!this.hasFocus){
243             return;
244         }
245         
246         if(this.store.getCount() > 0) {
247             this.expand();
248             this.restrictHeight();   
249         } else {
250             this.onEmptyResults();
251         }
252         /*
253         this.stores[1].loadData([]);
254         this.stores[2].loadData([]);
255         this.views
256         */    
257     
258         //this.el.focus();
259     },
260     
261     
262     // private
263     onLoadException : function()
264     {
265         this.collapse();
266         Roo.log(this.store.reader.jsonData);
267         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
268             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
269         }
270         
271         
272     } ,
273      
274      
275
276     onSelectChange : function (view, sels, opts )
277     {
278         var ix = view.getSelectedIndexes();
279         
280         
281         if (opts.list > 1) {
282              
283             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
284             return;
285         }
286         
287         if (!ix.length) {
288             this.setFromData({});
289             this.stores[opts.list+1].loadData( [] );
290             return;
291         }
292         
293         var rec = view.store.getAt(ix[0]);
294         this.setFromData(rec.data);
295         
296         var lw = Math.floor(
297                 ((this.listWidth * 3 || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / 3
298         );
299         var data =  typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
300         var dl = typeof(data.data.cn) != 'undefined' ? data.data.total : data.length; ///json is a nested response..
301         this.stores[opts.list+1].loadData( data );
302         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
303         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
304         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
305         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1))); 
306     },
307     onDoubleClick : function()
308     {
309         this.collapse(); //??
310     },
311     
312      
313     
314     findRecord : function (prop,value)
315     {
316         return this.findRecordInStore(this.store, prop,value);
317     },
318     
319      // private
320     findRecordInStore : function(store, prop, value)
321     {
322         var cstore = new Roo.data.SimpleStore({
323             //fields : this.store.reader.meta.fields, // we need array reader.. for
324             reader : this.store.reader,
325             data : [ ]
326         });
327         var _this = this;
328         var record  = false;
329         if(store.getCount() > 0){
330            store.each(function(r){
331                 if(r.data[prop] == value){
332                     record = r;
333                     return false;
334                 }
335                 if (r.data.cn && r.data.cn.length) {
336                     cstore.loadData( r.data.cn);
337                     var cret = _this.findRecordInStore(cstore, prop, value);
338                     if (cret !== false) {
339                         record = cret;
340                         return false;
341                     }
342                 }
343                 
344                 return true;
345             });
346         }
347         return record;
348     }
349     
350     
351     
352     
353 });