]>
Commit | Line | Data |
---|---|---|
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 | |
5 | Ext.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 |