]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/src/list/AbstractTreeItem.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / src / list / AbstractTreeItem.js
CommitLineData
6527f429
DM
1/**\r
2 * The base class for all items in the `{@link Ext.list.Tree treelist}`.\r
3 * @since 6.0.0\r
4 */\r
5Ext.define('Ext.list.AbstractTreeItem', {\r
6 extend: 'Ext.Widget',\r
7\r
8 isTreeListItem: true,\r
9\r
10 /**\r
11 * @method setExpandable\r
12 * @ignore\r
13 */\r
14 \r
15 /**\r
16 * @method setExpanded\r
17 * @ignore\r
18 */\r
19 \r
20 /**\r
21 * @method setIconCls\r
22 * @ignore\r
23 */\r
24 \r
25 /**\r
26 * @method setLeaf\r
27 * @ignore\r
28 */\r
29 \r
30 /**\r
31 * @method setOwner\r
32 * @ignore\r
33 */\r
34 \r
35 /**\r
36 * @method setLoading\r
37 * @ignore\r
38 */\r
39 \r
40 /**\r
41 * @method setNode\r
42 * @ignore\r
43 */\r
44 \r
45 /**\r
46 * @method setParentItem\r
47 * @ignore\r
48 */\r
49 \r
50 /**\r
51 * @method setText\r
52 * @ignore\r
53 */\r
54\r
55 cachedConfig: {\r
56 /**\r
57 * @cfg {Boolean} expandable\r
58 * `true` if this item is expandable. This value is taken from\r
59 * the underlying {@link #node}.\r
60 */\r
61 expandable: false,\r
62\r
63 /**\r
64 * @cfg {Boolean} expanded\r
65 * `true` if this item is expanded. This value is taken from\r
66 * the underlying {@link #node}.\r
67 */\r
68 expanded: false,\r
69\r
70 /**\r
71 * @cfg {Boolean} floated\r
72 * `true` if this item is current floated. This mode applies when the owning\r
73 * `{@link Ext.list.Tree treelist}` is in `{@link Ext.list.Tree#micro micro}`\r
74 * mode and the sub-tree under this item should be presented as a floating\r
75 * element.\r
76 */\r
77 floated: false,\r
78\r
79 /**\r
80 * @cfg {String} iconCls\r
81 * @inheritdoc Ext.panel.Header#cfg-iconCls\r
82 * @localdoc **Note:** This value is taken from the underlying {@link #node}.\r
83 */\r
84 iconCls: '',\r
85\r
86 /**\r
87 * @cfg {Boolean} leaf\r
88 * `true` if this item is a leaf. This value is taken from\r
89 * the underlying {@link #node}.\r
90 */\r
91 leaf: true,\r
92\r
93 /**\r
94 * @cfg {Boolean} loading\r
95 * `true` if this item is currently loading data. This value is taken from\r
96 * the underlying {@link #node}.\r
97 */\r
98 loading: false,\r
99\r
100 /**\r
101 * @cfg {Boolean} selected\r
102 * `true` if this is the selected item in the tree.\r
103 */\r
104 selected: false,\r
105\r
106 /**\r
107 * @cfg {Boolean} selectedParent\r
108 * `true` if this item contains the {@link #selected} item in the tree.\r
109 */\r
110 selectedParent: false\r
111 },\r
112\r
113 config: {\r
114 /**\r
115 * @cfg {String} iconClsProperty\r
116 * The property from the {@link #node} to map for the {@link #iconCls} config.\r
117 */\r
118 iconClsProperty: 'iconCls',\r
119\r
120 indent: null,\r
121\r
122 /**\r
123 * @cfg {Ext.list.Tree} owner\r
124 * The owning list for this container.\r
125 */\r
126 owner: null,\r
127\r
128 /**\r
129 * @cfg {Ext.data.TreeModel} node\r
130 * The backing node for this item.\r
131 */\r
132 node: null,\r
133\r
134 /**\r
135 * @cfg {Number} over\r
136 * One of three possible values:\r
137 *\r
138 * - 0 if mouse is not over this item or any of its descendants.\r
139 * - 1 if mouse is not over this item but is over one of this item's descendants.\r
140 * - 2 if mouse is directly over this item.\r
141 */\r
142 over: null,\r
143\r
144 /**\r
145 * @cfg {Ext.list.AbstractTreeItem} parentItem\r
146 * The parent item for this item. \r
147 */\r
148 parentItem: null,\r
149\r
150 /**\r
151 * @cfg {String} text\r
152 * The text for this item. This value is taken from\r
153 * the underlying {@link #node}.\r
154 */\r
155 text: {\r
156 lazy: true,\r
157 $value: ''\r
158 },\r
159\r
160 /**\r
161 * @cfg {String} textProperty\r
162 * The property from the {@link #node} to map for the {@link #text} config.\r
163 */\r
164 textProperty: 'text'\r
165 },\r
166\r
167 updateNode: function (node) {\r
168 if (node) {\r
169 var me = this,\r
170 map = me.itemMap,\r
171 childNodes, owner, len, i, item, child;\r
172\r
173 me.element.dom.setAttribute('data-recordId', node.internalId);\r
174\r
175 if (!map) {\r
176 childNodes = node.childNodes;\r
177 owner = me.getOwner();\r
178 me.itemMap = map = {};\r
179 for (i = 0, len = childNodes.length; i < len; ++i) {\r
180 child = childNodes[i];\r
181 item = owner.createItem(child, me);\r
182 map[child.internalId] = item;\r
183 me.insertItem(item, null);\r
184 }\r
185 }\r
186\r
187 me.setExpanded(node.isExpanded());\r
188 me.doNodeUpdate(node);\r
189 }\r
190 },\r
191\r
192 updateSelected: function(selected) {\r
193 if (!this.isConfiguring) {\r
194 var parent = this.getParentItem();\r
195 while (parent && !parent.isRootListItem) {\r
196 parent.setSelectedParent(selected);\r
197 parent = parent.getParentItem();\r
198 }\r
199 }\r
200 },\r
201\r
202 /**\r
203 * Collapse this item. Does nothing if already collapsed.\r
204 */\r
205 collapse: function () {\r
206 this.getNode().collapse();\r
207 },\r
208\r
209 /**\r
210 * Expand this item. Does nothing if already expanded.\r
211 */\r
212 expand: function () {\r
213 this.getNode().expand();\r
214 },\r
215\r
216 /**\r
217 * @method\r
218 * Gets the element to be used for the tree when it is in \r
219 * {@link Ext.list.Tree#micro micro} mode.\r
220 * @return {Ext.dom.Element} The element.\r
221 *\r
222 * @protected\r
223 * @template\r
224 */\r
225 getToolElement: Ext.emptyFn,\r
226\r
227 /**\r
228 * @method\r
229 * Append a new child item to the DOM.\r
230 * @param {Ext.list.AbstractTreeItem} item The item to insert.\r
231 * @param {Ext.list.AbstractTreeItem} refItem The item the node is to\r
232 * be inserted before. `null` if the item is to be added to the end.\r
233 *\r
234 * @protected\r
235 * @template\r
236 */\r
237 insertItem: Ext.emptyFn,\r
238\r
239 /**\r
240 * Check if the current item is expanded.\r
241 * @return {Boolean} `true` if this item is expanded.\r
242 */\r
243 isExpanded: function () {\r
244 return this.getExpanded();\r
245 },\r
246\r
247 /**\r
248 * @method\r
249 * Checks whether the event is an event that should select this node.\r
250 * @param {Ext.event.Event} e The event object.\r
251 * @return {Boolean} `true` if the event should select this node.\r
252 *\r
253 * @protected\r
254 * @template\r
255 */\r
256 isSelectionEvent: Ext.emptyFn,\r
257\r
258 /**\r
259 * @method\r
260 * Checks whether the event is an event that should toggle the expand/collapse state.\r
261 * @param {Ext.event.Event} e The event object.\r
262 * @return {Boolean} `true` if the event should toggle the expand/collapsed state.\r
263 * \r
264 * @protected\r
265 * @template\r
266 */\r
267 isToggleEvent: Ext.emptyFn,\r
268\r
269 /**\r
270 * Handle this node being collapsed.\r
271 * @param {Ext.data.TreeModel} node The node being collapsed.\r
272 *\r
273 * @protected\r
274 */\r
275 nodeCollapse: function (node, collapsingForExpand) {\r
276 var me = this,\r
277 owner = me.getOwner(),\r
278 animation = me.preventAnimation ? null : owner.getAnimation();\r
279\r
280 me.nodeCollapseBegin(animation, collapsingForExpand);\r
281\r
282 if (!animation) {\r
283 me.nodeCollapseEnd(collapsingForExpand);\r
284 }\r
285 },\r
286\r
287 nodeCollapseBegin: function (animation, collapsingForExpand) {\r
288 var me = this,\r
289 owner = me.getOwner();\r
290\r
291 me.setExpanded(false);\r
292\r
293 owner.fireEvent('itemcollapse', owner, me);\r
294 },\r
295\r
296 nodeCollapseEnd: function (collapsingForExpand) {\r
297 if (!collapsingForExpand) {\r
298 this.getOwner().updateLayout();\r
299 }\r
300 },\r
301\r
302 /**\r
303 * Handle this node being expanded.\r
304 * @param {Ext.data.TreeModel} node The node being expanded.\r
305 *\r
306 * @protected\r
307 */\r
308 nodeExpand: function (node) {\r
309 var me = this,\r
310 owner = me.getOwner(),\r
311 floated = me.getFloated(),\r
312 animation = !floated && owner.getAnimation();\r
313\r
314 me.nodeExpandBegin(animation);\r
315\r
316 if (!animation) {\r
317 me.nodeExpandEnd();\r
318 }\r
319 },\r
320\r
321 nodeExpandBegin: function (animation) {\r
322 var me = this,\r
323 owner = me.getOwner();\r
324\r
325 me.setExpanded(true);\r
326\r
327 owner.fireEvent('itemexpand', owner, me);\r
328 },\r
329\r
330 nodeExpandEnd: function () {\r
331 this.getOwner().updateLayout();\r
332 },\r
333\r
334 /**\r
335 * Handle a node being inserted as a child of this item.\r
336 * @param {Ext.data.TreeModel} node The node being inserted.\r
337 * @param {Ext.data.TreeModel} refNode The node that is to be inserted before. `null`\r
338 * if this operation is an append.\r
339 *\r
340 * @protected\r
341 */\r
342 nodeInsert: function (node, refNode) {\r
343 var me = this,\r
344 owner = me.getOwner(),\r
345 map = me.itemMap,\r
346 id = node.internalId,\r
347 item = owner.getItem(node),\r
348 refItem = null,\r
349 oldParent;\r
350\r
351 if (item) {\r
352 oldParent = item.getParentItem();\r
353 // May have some kind of custom removal processing, allow it to happen, even if it's us\r
354 oldParent.removeItem(item);\r
355 if (oldParent !== me) {\r
356 oldParent.doUpdateExpandable();\r
357 item.setParentItem(me);\r
358 }\r
359 } else {\r
360 item = me.getOwner().createItem(node, me);\r
361 }\r
362 map[id] = item;\r
363\r
364 if (refNode) {\r
365 refItem = map[refNode.internalId];\r
366 }\r
367\r
368 me.insertItem(item, refItem);\r
369 me.doUpdateExpandable();\r
370\r
371 owner.fireEvent('iteminsert', owner, me, item, refItem);\r
372\r
373 owner.updateLayout();\r
374 },\r
375\r
376 /**\r
377 * Handle a node being removed as a child of this item.\r
378 * @param {Ext.data.TreeModel} node The node being removed.\r
379 *\r
380 * @protected\r
381 */\r
382 nodeRemove: function (node) {\r
383 var me = this,\r
384 map = me.itemMap,\r
385 owner = me.getOwner(),\r
386 id = node.internalId,\r
387 item = map[id];\r
388\r
389 if (item) {\r
390 delete map[id];\r
391 me.removeItem(item);\r
392 item.destroy();\r
393\r
394 me.doUpdateExpandable();\r
395\r
396 owner.fireEvent('itemremove', owner, me, item);\r
397\r
398 owner.updateLayout();\r
399 }\r
400 },\r
401\r
402 /**\r
403 * Handle this node having fields changed.\r
404 * \r
405 * @param {Ext.data.TreeModel} node The node.\r
406 * @param {String[]} modifiedFieldNames The modified field names, if known.\r
407 *\r
408 * @protected\r
409 */\r
410 nodeUpdate: function (node, modifiedFieldNames) {\r
411 this.doNodeUpdate(node);\r
412 },\r
413\r
414 /**\r
415 * @method\r
416 *\r
417 * Remove a child item from the DOM.\r
418 * @param {Ext.list.AbstractTreeItem} item The item to remove.\r
419 *\r
420 * @protected\r
421 * @template\r
422 */\r
423 removeItem: Ext.emptyFn,\r
424\r
425 updateFloated: function (floated) {\r
426 var me = this,\r
427 el = me.element,\r
428 placeholder = me.placeholder,\r
429 node, wasExpanded;\r
430\r
431 if (floated) {\r
432 placeholder = el.clone(false, true); // shallow, asDom\r
433 placeholder.id += '-placeholder'; // avoid duplicate id\r
434 me.placeholder = Ext.get(placeholder);\r
435\r
436 me.wasExpanded = me.getExpanded();\r
437 me.setExpanded(true);\r
438\r
439 el.dom.parentNode.insertBefore(placeholder, el.dom);\r
440\r
441 me.floater = me.createFloater(); // toolkit-specific\r
442 } else if (placeholder) {\r
443 wasExpanded = me.wasExpanded;\r
444 node = me.getNode();\r
445 me.setExpanded(wasExpanded);\r
446 if (!wasExpanded && node.isExpanded()) {\r
447 // If we have been floating and expanded a child, we may have been\r
448 // expanded as part of the ancestors. Attempt to restore state.\r
449 me.preventAnimation = true;\r
450 node.collapse();\r
451 me.preventAnimation = false;\r
452 }\r
453 me.floater.remove(me, false); // don't destroy\r
454 placeholder.dom.parentNode.insertBefore(el.dom, placeholder.dom);\r
455\r
456 placeholder.destroy();\r
457 me.floater.destroy();\r
458\r
459 me.placeholder = me.floater = null;\r
460\r
461 me.floatedByHover = false;\r
462 }\r
463 },\r
464\r
465 /**\r
466 * @inheritdoc\r
467 */\r
468 destroy: function () {\r
469 var me = this,\r
470 map = me.itemMap,\r
471 owner = me.getOwner(),\r
472 key;\r
473\r
474 if (map) {\r
475 for (key in map) {\r
476 map[key].destroy();\r
477 }\r
478 me.itemMap = null;\r
479 }\r
480\r
481 if (owner) {\r
482 owner.removeItem(me.getNode());\r
483 }\r
484\r
485 me.setNode(null);\r
486 me.setParentItem(null);\r
487 me.setOwner(null);\r
488\r
489 me.callParent();\r
490 },\r
491\r
492 privates: {\r
493 /**\r
494 * Update properties after a node update.\r
495 *\r
496 * @param {Ext.data.TreeModel} node The node.\r
497 * @param {String[]} modifiedFieldNames The modified field names, if known.\r
498 *\r
499 * @private\r
500 */\r
501 doNodeUpdate: function (node) {\r
502 var me = this,\r
503 textProperty = this.getTextProperty(),\r
504 iconClsProperty = this.getIconClsProperty();\r
505\r
506 if (textProperty) {\r
507 me.setText(node.data[textProperty]);\r
508 }\r
509\r
510 if (iconClsProperty) {\r
511 me.setIconCls(node.data[iconClsProperty]);\r
512 }\r
513\r
514 me.setLoading(node.isLoading());\r
515 me.setLeaf(node.isLeaf());\r
516 me.doUpdateExpandable();\r
517 },\r
518\r
519 doUpdateExpandable: function () {\r
520 var node = this.getNode();\r
521 this.setExpandable(node.isExpandable());\r
522 },\r
523\r
524 /**\r
525 * Handle a click on this item.\r
526 * @param {Ext.event.Event} e The event\r
527 *\r
528 * @private\r
529 */\r
530 onClick: function (e) {\r
531 var me = this,\r
532 owner = me.getOwner(),\r
533 node = me.getNode(),\r
534 info = {\r
535 event: e,\r
536 item: me,\r
537 node: node,\r
538 tree: owner,\r
539 select: node.get('selectable') !== false && me.isSelectionEvent(e),\r
540 toggle: me.isToggleEvent(e)\r
541 };\r
542\r
543 /**\r
544 * @event itemclick\r
545 *\r
546 * @param {Ext.list.Tree} sender The `treelist` that fired this event.\r
547 *\r
548 * @param {Object} info\r
549 * @param {Ext.event.Event} info.event The DOM event that precipitated this\r
550 * event.\r
551 * @param {Ext.list.AbstractTreeItem} info.item The tree node that was clicked.\r
552 * @param {Ext.list.Tree} info.tree The `treelist` that fired this event.\r
553 * @param {Boolean} info.select On input this is value is the result of the\r
554 * {@link #isSelectionEvent} method. On return from event handlers (assuming a\r
555 * `false` return does not cancel things) this property is used to determine\r
556 * if the clicked node should be selected.\r
557 * @param {Boolean} info.toggle On input this is value is the result of the\r
558 * {@link #isToggleEvent} method. On return from event handlers (assuming a\r
559 * `false` return does not cancel things) this property is used to determine\r
560 * if the clicked node's expand/collapse state should be toggled.\r
561 *\r
562 * @since 6.0.1\r
563 */\r
564 if (owner.fireEvent('itemclick', owner, info) !== false) {\r
565 if (info.toggle) {\r
566 me.toggleExpanded();\r
567 }\r
568\r
569 if (info.select) {\r
570 owner.setSelection(me.getNode());\r
571 }\r
572 }\r
573 },\r
574\r
575 toggleExpanded: function() {\r
576 if (this.isExpanded()) {\r
577 this.collapse();\r
578 } else {\r
579 this.expand();\r
580 }\r
581 },\r
582\r
583 updateIndent: function (value) {\r
584 var items = this.itemMap,\r
585 id;\r
586\r
587 for (id in items) {\r
588 items[id].setIndent(value);\r
589 }\r
590 }\r
591 }\r
592});\r