Upgrade to ExtJS 3.0.0 - Released 07/06/2009
[extjs.git] / docs / source / TreeSelector.html
1 <html>\r
2 <head>\r
3   <title>The source code</title>\r
4     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />\r
5     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>\r
6 </head>\r
7 <body  onload="prettyPrint();">\r
8     <pre class="prettyprint lang-js">// custom menu item to contain Ext trees\r
9 Ext.menu.TreeItem = Ext.extend(Ext.menu.Adapter, {\r
10         constructor : function(config){\r
11         Ext.menu.TreeItem.superclass.constructor.call(this, config.tree, config);\r
12         this.tree = this.component;\r
13         this.addEvents('selectionchange');\r
14 \r
15         this.tree.on("render", function(tree){\r
16             tree.body.swallowEvent(['click','keydown', 'keypress', 'keyup']);\r
17         });\r
18 \r
19         this.tree.getSelectionModel().on("selectionchange", this.onSelect, this);\r
20     },\r
21 \r
22     onSelect : function(tree, sel){\r
23         this.fireEvent("select", this, sel, tree);\r
24     }\r
25 });\r
26 \r
27 \r
28 // custom menu containing a single tree\r
29 Ext.menu.TreeMenu = Ext.extend(Ext.menu.Menu, {\r
30     cls:'x-tree-menu',\r
31         keyNav: true,\r
32         hideOnClick:false,\r
33     plain: true,\r
34 \r
35     constructor : function(config){\r
36         Ext.menu.TreeMenu.superclass.constructor.call(this, config);\r
37         this.treeItem = new Ext.menu.TreeItem(config);\r
38         this.add(this.treeItem);\r
39 \r
40         this.tree = this.treeItem.tree;\r
41         this.tree.on('click', this.onNodeClick, this);\r
42         this.relayEvents(this.treeItem, ["selectionchange"]);\r
43     },\r
44 \r
45     // private\r
46     beforeDestroy : function() {\r
47         this.tree.destroy();\r
48     },\r
49         \r
50         onNodeClick : function(node, e){\r
51                 if(!node.attributes.isFolder){\r
52                         this.treeItem.handleClick(e);\r
53                 }\r
54         }\r
55 });\r
56 \r
57 \r
58 // custom form field for displaying a tree, similar to select or combo\r
59 Ext.ux.TreeSelector = Ext.extend(Ext.form.TriggerField, {\r
60         initComponent : function(){\r
61                 Ext.ux.TreeSelector.superclass.initComponent.call(this);\r
62                 this.addEvents('selectionchange');\r
63 \r
64                 this.tree.getSelectionModel().on('selectionchange', this.onSelection, this);\r
65                 this.tree.on({\r
66                         'expandnode': this.sync,\r
67                         'collapsenode' : this.sync,\r
68                         'append' : this.sync,\r
69                         'remove' : this.sync,\r
70                         'insert' : this.sync,\r
71                         scope: this\r
72                 });\r
73                 this.on('focus', this.onTriggerClick, this);\r
74     },\r
75 \r
76         sync : function(){\r
77                 if(this.menu && this.menu.isVisible()){\r
78                         if(this.tree.body.getHeight() > this.maxHeight){\r
79                                 this.tree.body.setHeight(this.maxHeight);\r
80                                 this.restricted = true;\r
81                         }else if(this.restricted && this.tree.body.dom.firstChild.offsetHeight < this.maxHeight){\r
82                                 this.tree.body.setHeight('');\r
83                                 this.restricted = false;\r
84                         }\r
85                         this.menu.el.sync();\r
86                 }\r
87         },\r
88 \r
89         onSelection : function(tree, node){\r
90                 if(!node){\r
91                         this.setRawValue('');\r
92                 }else{\r
93                         this.setRawValue(node.text);\r
94                 }\r
95         },\r
96 \r
97         initEvents : function(){\r
98                 Ext.ux.TreeSelector.superclass.initEvents.call(this);\r
99                 this.el.on('mousedown', this.onTriggerClick, this);\r
100                 this.el.on("keydown", this.onKeyDown,  this);\r
101         },\r
102 \r
103         onKeyDown : function(e){\r
104                 if(e.getKey() == e.DOWN){\r
105                         this.onTriggerClick();\r
106                 }\r
107         },\r
108 \r
109     validateBlur : function(){\r
110         return !this.menu || !this.menu.isVisible();\r
111     },\r
112 \r
113     getValue : function(){\r
114                 var sm = this.tree.getSelectionModel();\r
115                 var s = sm.getSelectedNode();\r
116         return s ? s.id : '';\r
117     },\r
118 \r
119     setValue : function(id){\r
120                 var n = this.tree.getNodeById(id);\r
121                 if(n){\r
122                         n.select();\r
123                 }else{\r
124                         this.tree.getSelectionModel().clearSelections();\r
125                 }\r
126     },\r
127 \r
128     // private\r
129     onDestroy : function(){\r
130         if(this.menu) {\r
131             this.menu.destroy();\r
132         }\r
133         if(this.wrap){\r
134             this.wrap.remove();\r
135         }\r
136         Ext.ux.TreeSelector.superclass.onDestroy.call(this);\r
137     },\r
138 \r
139         // private\r
140     menuListeners : {\r
141         show : function(){ // retain focus styling\r
142             this.onFocus();\r
143         },\r
144         hide : function(){\r
145             this.focus.defer(10, this);\r
146             var ml = this.menuListeners;\r
147             this.menu.un("show", ml.show,  this);\r
148             this.menu.un("hide", ml.hide,  this);\r
149         }\r
150     },\r
151 \r
152     onTriggerClick : function(){\r
153                 if(this.disabled){\r
154             return;\r
155         }\r
156         this.menu.on(Ext.apply({}, this.menuListeners, {\r
157             scope:this\r
158         }));\r
159 \r
160         this.menu.show(this.el, "tl-bl?");\r
161                 this.sync();\r
162                 var sm = this.tree.getSelectionModel();\r
163                 var selected = sm.getSelectedNode();\r
164                 if(selected){\r
165                         selected.ensureVisible();\r
166                         sm.activate.defer(250, sm, [selected]);\r
167                 }\r
168     },\r
169 \r
170     beforeBlur : function(){\r
171         //\r
172     },\r
173 \r
174         onRender : function(){\r
175                 Ext.ux.TreeSelector.superclass.onRender.apply(this, arguments);\r
176                 this.menu = new Ext.menu.TreeMenu(Ext.apply(this.menuConfig || {}, {tree: this.tree}));\r
177                 this.menu.render();\r
178 \r
179                 this.tree.body.addClass('x-tree-selector');\r
180         },\r
181 \r
182         readOnly: true\r
183 });\r
184 \r
185 /*\r
186  * Custom tree keyboard navigation that supports node navigation without selection\r
187  */\r
188 Ext.tree.ActivationModel = Ext.extend(Ext.tree.DefaultSelectionModel, {\r
189         select : function(node){\r
190         return this.activate(Ext.tree.ActivationModel.superclass.select.call(this, node));\r
191     },\r
192     \r
193     activate : function(node){\r
194                 if(!node){\r
195                         return;\r
196                 }\r
197                 if(this.activated != node) {\r
198                         if(this.activated){\r
199                                 this.activated.ui.removeClass('x-tree-activated');\r
200                         }\r
201                         this.activated = node;\r
202                         node.ui.addClass('x-tree-activated');\r
203                 }\r
204                 node.ui.focus();\r
205                 return node;    \r
206         },\r
207         \r
208         activatePrevious : function(){\r
209         var s = this.activated;\r
210         if(!s){\r
211             return null;\r
212         }\r
213         var ps = s.previousSibling;\r
214         if(ps){\r
215             if(!ps.isExpanded() || ps.childNodes.length < 1){\r
216                 return this.activate(ps);\r
217             } else{\r
218                 var lc = ps.lastChild;\r
219                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){\r
220                     lc = lc.lastChild;\r
221                 }\r
222                 return this.activate(lc);\r
223             }\r
224         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){\r
225             return this.activate(s.parentNode);\r
226         }\r
227         return null;\r
228     },\r
229 \r
230     activateNext : function(){\r
231         var s = this.activated;\r
232         if(!s){\r
233             return null;\r
234         }\r
235         if(s.firstChild && s.isExpanded()){\r
236              return this.activate(s.firstChild);\r
237          }else if(s.nextSibling){\r
238              return this.activate(s.nextSibling);\r
239          }else if(s.parentNode){\r
240             var newS = null;\r
241             s.parentNode.bubble(function(){\r
242                 if(this.nextSibling){\r
243                     newS = this.getOwnerTree().selModel.activate(this.nextSibling);\r
244                     return false;\r
245                 }\r
246             });\r
247             return newS;\r
248          }\r
249         return null;\r
250     },\r
251 \r
252     onKeyDown : function(e){\r
253         var s = this.activated;\r
254         // undesirable, but required\r
255         var sm = this;\r
256         if(!s){\r
257             return;\r
258         }\r
259         var k = e.getKey();\r
260         switch(k){\r
261              case e.DOWN:\r
262                  e.stopEvent();\r
263                  this.activateNext();\r
264              break;\r
265              case e.UP:\r
266                  e.stopEvent();\r
267                  this.activatePrevious();\r
268              break;\r
269              case e.RIGHT:\r
270                  e.preventDefault();\r
271                  if(s.hasChildNodes()){\r
272                      if(!s.isExpanded()){\r
273                          s.expand();\r
274                      }else if(s.firstChild){\r
275                          this.activate(s.firstChild, e);\r
276                      }\r
277                  }\r
278              break;\r
279              case e.LEFT:\r
280                  e.preventDefault();\r
281                  if(s.hasChildNodes() && s.isExpanded()){\r
282                      s.collapse();\r
283                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){\r
284                      this.activate(s.parentNode, e);\r
285                  }\r
286              break;\r
287         };\r
288     }\r
289 });</pre>    \r
290 </body>\r
291 </html>