/**
* The default implementation of the class used for `{@link Ext.list.Tree}`.
*
* This class can only be used in conjunction with {@link Ext.list.Tree}.
* @since 6.0.0
*/
Ext.define('Ext.list.TreeItem', {
extend: 'Ext.list.AbstractTreeItem',
xtype: 'treelistitem',
collapsedCls: Ext.baseCSSPrefix + 'treelist-item-collapsed',
expandedCls: Ext.baseCSSPrefix + 'treelist-item-expanded',
floatedToolCls: Ext.baseCSSPrefix + 'treelist-item-tool-floated',
leafCls: Ext.baseCSSPrefix + 'treelist-item-leaf',
expandableCls: Ext.baseCSSPrefix + 'treelist-item-expandable',
hideIconCls: Ext.baseCSSPrefix + 'treelist-item-hide-icon',
loadingCls: Ext.baseCSSPrefix + 'treelist-item-loading',
selectedCls: Ext.baseCSSPrefix + 'treelist-item-selected',
selectedParentCls: Ext.baseCSSPrefix + 'treelist-item-selected-parent',
withIconCls: Ext.baseCSSPrefix + 'treelist-item-with-icon',
hoverCls: Ext.baseCSSPrefix + 'treelist-item-over',
rowHoverCls: Ext.baseCSSPrefix + 'treelist-row-over',
/**
* This property is `true` to allow type checking for this or derived class.
* @property {Boolean} isTreeListItem
* @readonly
*/
isTreeListItem: true,
config: {
/**
* @cfg {String} rowCls
* One or more CSS classes to add to the tree item's logical "row" element. This
* element fills from left-to-right of the line.
* @since 6.0.1
*/
rowCls: null
},
/**
* @cfg {String} [rowClsProperty="rowCls"]
* The name of the associated record's field to map to the {@link #rowCls} config.
* @since 6.0.1
*/
rowClsProperty: 'rowCls',
element: {
reference: 'element',
tag: 'li',
cls: Ext.baseCSSPrefix + 'treelist-item',
children: [{
reference: 'rowElement',
cls: Ext.baseCSSPrefix + 'treelist-row',
children: [{
reference: 'wrapElement',
cls: Ext.baseCSSPrefix + 'treelist-item-wrap',
children: [{
reference: 'iconElement',
cls: Ext.baseCSSPrefix + 'treelist-item-icon'
}, {
reference: 'textElement',
cls: Ext.baseCSSPrefix + 'treelist-item-text'
}, {
reference: 'expanderElement',
cls: Ext.baseCSSPrefix + 'treelist-item-expander'
}]
}]
}, {
reference: 'itemContainer',
tag: 'ul',
cls: Ext.baseCSSPrefix + 'treelist-container'
}, {
reference: 'toolElement',
cls: Ext.baseCSSPrefix + 'treelist-item-tool'
}]
},
constructor: function(config) {
var toolDom;
this.callParent([config]);
toolDom = this.toolElement.dom;
// We don't want the tool in the normal <li> structure but it is simpler to let
// that process create the toolElement.
toolDom.parentNode.removeChild(toolDom);
},
getToolElement: function() {
return this.toolElement;
},
insertItem: function(item, refItem) {
if (refItem) {
item.element.insertBefore(refItem.element);
}
else {
this.itemContainer.appendChild(item.element);
}
},
isSelectionEvent: function(e) {
var owner = this.getOwner();
return (!this.isToggleEvent(e) || !owner.getExpanderOnly() || owner.getSelectOnExpander());
},
isToggleEvent: function(e) {
var isExpand = false;
if (this.getOwner().getExpanderOnly()) {
isExpand = e.target === this.expanderElement.dom;
}
else {
// contains also includes the element itself
isExpand = !this.itemContainer.contains(e.target);
}
return isExpand;
},
nodeCollapseBegin: function(animation, collapsingForExpand) {
var me = this,
itemContainer = me.itemContainer,
height;
if (me.expanding) {
me.stopAnimation(me.expanding); // also calls the nodeExpandDone method
}
// Measure before collapse since that hides the element (if animating) but after
// ending any in progress expand animation.
height = animation && itemContainer.getHeight();
me.callParent([ animation, collapsingForExpand ]);
if (animation) {
// The collapsed state is now in effect, so itemContainer is hidden.
itemContainer.dom.style.display = 'block';
me.collapsingForExpand = collapsingForExpand;
me.collapsing = this.runAnimation(Ext.merge({
from: {
height: height
},
to: {
height: 0
},
callback: me.nodeCollapseDone,
scope: me
}, animation));
}
},
nodeCollapseDone: function(animation) {
var me = this,
itemContainer = me.itemContainer;
// stopAnimation is called on destroy, so don't
// bother continuing if we don't need to
if (!me.destroying && !me.destroyed) {
me.collapsing = null;
itemContainer.dom.style.display = '';
itemContainer.setHeight(null);
me.nodeCollapseEnd(me.collapsingForExpand);
}
},
nodeExpandBegin: function(animation) {
var me = this,
itemContainer = me.itemContainer,
height;
if (me.collapsing) {
me.stopAnimation(me.collapsing);
}
me.callParent([ animation ]);
if (animation) {
// The expanded state is in effect, so itemContainer is visible again.
height = itemContainer.getHeight();
itemContainer.setHeight(0);
me.expanding = me.runAnimation(Ext.merge({
to: {
height: height
},
callback: me.nodeExpandDone,
scope: me
}, animation));
}
},
nodeExpandDone: function() {
this.expanding = null;
this.itemContainer.setHeight(null);
this.nodeExpandEnd();
},
removeItem: function(item) {
this.itemContainer.removeChild(item.element);
},
//-------------------------------------------------------------------------
// Updaters
updateNode: function(node, oldNode) {
this.syncIndent();
this.callParent([ node, oldNode ]);
},
updateExpandable: function(expandable) {
this.updateExpandCls();
// We need not to set the expandable attribute of node here,
// Refer to isExapndable() function of the node.
// This function may get called on removal of child, and thus setting expandable to false
// But we may not need to set same to node as isExapndable() will be deciding function
// not the 'Exapndable' attribute. Fase 'Exapndable' attribute means node will never
// be expandable irrespective of the child values
},
updateExpanded: function(expanded) {
var node = this.getNode();
this.updateExpandCls();
if (node) {
node.set('expanded', expanded);
}
},
updateIconCls: function(iconCls, oldIconCls) {
var me = this,
el = me.element;
me.doIconCls(me.iconElement, iconCls, oldIconCls);
me.doIconCls(me.toolElement, iconCls, oldIconCls);
el.toggleCls(me.withIconCls, !!iconCls);
// Blank iconCls leaves room for icon to line up w/sibling items
el.toggleCls(me.hideIconCls, iconCls === null);
},
updateLeaf: function(leaf) {
this.element.toggleCls(this.leafCls, leaf);
},
updateLoading: function(loading) {
this.element.toggleCls(this.loadingCls, loading);
},
updateOver: function(over) {
var me = this;
me.element.toggleCls(me.hoverCls, !! over); // off if over==0, on otherwise
me.rowElement.toggleCls(me.rowHoverCls, over > 1); // off if over = 0 or 1
},
updateRowCls: function(value, oldValue) {
this.rowElement.replaceCls(oldValue, value);
},
updateSelected: function(selected, oldSelected) {
var me = this,
cls = me.selectedCls,
tool = me.getToolElement();
me.callParent([ selected, oldSelected ]);
me.element.toggleCls(cls, selected);
if (tool) {
tool.toggleCls(cls, selected);
}
},
updateSelectedParent: function(selectedParent) {
var me = this,
tool;
me.element.toggleCls(me.selectedParentCls, selectedParent);
tool = me.getToolElement();
if (tool) {
tool.toggleCls(me.selectedCls, selectedParent);
}
},
updateText: function(text) {
this.textElement.update(text);
},
//-------------------------------------------------------------------------
// Private
privates: {
doNodeUpdate: function(node) {
this.callParent([ node ]);
this.setRowCls(node && node.data[this.rowClsProperty]);
},
doIconCls: function(element, iconCls, oldIconCls) {
if (oldIconCls) {
element.removeCls(oldIconCls);
}
if (iconCls) {
element.addCls(iconCls);
}
},
syncIndent: function() {
var me = this,
indent = me.getIndent(),
node = me.getNode(),
depth;
if (node) {
depth = node.data.depth - 1;
me.wrapElement.dom.style.marginLeft = (depth * indent) + 'px';
}
},
updateExpandCls: function() {
if (!this.updatingExpandCls) {
// eslint-disable-next-line vars-on-top
var me = this,
expandable = me.getExpandable(),
element = me.element,
expanded = me.getExpanded(),
expandedCls = me.expandedCls,
collapsedCls = me.collapsedCls;
me.updatingExpandCls = true;
element.toggleCls(me.expandableCls, expandable);
if (expandable) {
element.toggleCls(expandedCls, expanded);
element.toggleCls(collapsedCls, !expanded);
}
else {
element.removeCls([expandedCls, collapsedCls]);
}
me.updatingExpandCls = false;
}
},
updateIndent: function(value, oldValue) {
this.syncIndent();
this.callParent([ value, oldValue ]);
}
}
}, function(TreeItem) {
TreeItem.prototype.floatedCls = [
Ext.Widget.prototype.floatedCls,
Ext.baseCSSPrefix + 'treelist-item-floated'
];
});