devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
553 lines (444 loc) • 23.3 kB
JavaScript
"use strict";
var $ = require("../../core/renderer"),
treeListCore = require("./ui.tree_list.core"),
commonUtils = require("../../core/utils/common"),
noop = require("../../core/utils/common").noop,
selectionModule = require("../grid_core/ui.grid_core.selection"),
errors = require("../widget/ui.errors"),
extend = require("../../core/utils/extend").extend;
var TREELIST_SELECT_ALL_CLASS = "dx-treelist-select-all",
CELL_FOCUS_DISABLED_CLASS = "dx-cell-focus-disabled",
SELECT_CHECKBOX_CLASS = "dx-select-checkbox";
var originalRowClick = selectionModule.extenders.views.rowsView._rowClick;
var nodeExists = function nodeExists(array, currentKey) {
return !!array.filter(function (key) {
return key === currentKey;
}).length;
};
treeListCore.registerModule("selection", extend(true, {}, selectionModule, {
defaultOptions: function defaultOptions() {
return extend(true, selectionModule.defaultOptions(), {
selection: {
showCheckBoxesMode: "always",
/**
* @name dxTreeListOptions.selection.recursive
* @publicName recursive
* @type boolean
* @default false
*/
recursive: false
}
});
},
extenders: {
controllers: {
data: {
_handleDataChanged: function _handleDataChanged(e) {
var selectionController = this.getController("selection"),
isRecursiveSelection = selectionController.isRecursiveSelection();
if (isRecursiveSelection && (!e || e.changeType !== "updateSelectionState")) {
selectionController.updateSelectionState({
selectedItemKeys: this.option("selectedRowKeys")
});
}
this.callBase.apply(this, arguments);
},
loadDescendants: function loadDescendants() {
var that = this,
d = that.callBase.apply(that, arguments),
selectionController = that.getController("selection"),
isRecursiveSelection = selectionController.isRecursiveSelection();
if (isRecursiveSelection) {
d.done(function () {
selectionController.updateSelectionState({
selectedItemKeys: that.option("selectedRowKeys")
});
});
}
return d;
}
},
selection: {
init: function init() {
this.callBase.apply(this, arguments);
this._selectionStateByKey = {};
},
renderSelectCheckBoxContainer: function renderSelectCheckBoxContainer($container, model) {
var that = this,
rowsView = that.component.getView("rowsView");
$container.addClass(CELL_FOCUS_DISABLED_CLASS);
var $checkbox = rowsView._renderSelectCheckBox($container, {
value: model.row.isSelected,
row: model.row,
column: model.column
});
rowsView._attachCheckBoxClickEvent($checkbox);
},
_updateSelectColumn: noop,
_getVisibleNodeKeys: function _getVisibleNodeKeys(isRecursiveSelection) {
var component = this.component,
root = component.getRootNode(),
keys = [];
root && treeListCore.foreachNodes(root.children, function (node) {
if (node.key !== undefined && (node.visible || isRecursiveSelection)) {
keys.push(node.key);
}
return isRecursiveSelection ? false : component.isRowExpanded(node.key);
});
return keys;
},
isSelectAll: function isSelectAll() {
var component = this.component,
hasIndeterminateState,
visibleKeys = this._getVisibleNodeKeys();
var selectedVisibleKeys = visibleKeys.filter(function (key) {
return component.isRowSelected(key);
});
if (!selectedVisibleKeys.length) {
hasIndeterminateState = visibleKeys.some(function (key) {
return component.isRowSelected(key) === undefined;
});
return hasIndeterminateState ? undefined : false;
} else if (selectedVisibleKeys.length === visibleKeys.length) {
return true;
}
},
selectAll: function selectAll() {
var that = this,
isRecursiveSelection = that.isRecursiveSelection(),
visibleKeys = that._getVisibleNodeKeys(isRecursiveSelection).filter(function (key) {
return !that.isRowSelected(key);
});
return that.selectRows(visibleKeys, true);
},
deselectAll: function deselectAll() {
var isRecursiveSelection = this.isRecursiveSelection(),
visibleKeys = this._getVisibleNodeKeys(isRecursiveSelection);
return this.deselectRows(visibleKeys);
},
selectedItemKeys: function selectedItemKeys(value, preserve, isDeselect, isSelectAll) {
var that = this,
selectedRowKeys = that.option("selectedRowKeys"),
isRecursiveSelection = this.isRecursiveSelection(),
normalizedArgs = isRecursiveSelection && that._normalizeSelectionArgs({
keys: value || []
}, !isDeselect);
if (normalizedArgs && !commonUtils.equalByValue(normalizedArgs.selectedRowKeys, selectedRowKeys)) {
that._isSelectionNormalizing = true;
return this.callBase(normalizedArgs.selectedRowKeys, false, false, false).always(function () {
that._isSelectionNormalizing = false;
}).done(function (items) {
normalizedArgs.selectedRowsData = items;
that._fireSelectionChanged(normalizedArgs);
});
}
return this.callBase(value, preserve, isDeselect, isSelectAll);
},
changeItemSelection: function changeItemSelection(itemIndex, keyboardKeys) {
var isRecursiveSelection = this.isRecursiveSelection();
if (isRecursiveSelection && !keyboardKeys.shift) {
var key = this._dataController.getKeyByRowIndex(itemIndex);
return this.selectedItemKeys(key, true, this.isRowSelected(key));
}
return this.callBase.apply(this, arguments);
},
_updateParentSelectionState: function _updateParentSelectionState(node, isSelected) {
var that = this,
state = isSelected,
parentNode = node.parent,
hasNonSelectedState,
hasSelectedState;
if (parentNode.children.length > 1) {
if (isSelected === false) {
hasSelectedState = parentNode.children.some(function (childNode, index, children) {
return that._selectionStateByKey[childNode.key];
});
state = hasSelectedState ? undefined : false;
} else if (isSelected === true) {
hasNonSelectedState = parentNode.children.some(function (childNode) {
return !that._selectionStateByKey[childNode.key];
});
state = hasNonSelectedState ? undefined : true;
}
}
this._selectionStateByKey[parentNode.key] = state;
if (parentNode.parent && parentNode.parent.level >= 0) {
this._updateParentSelectionState(parentNode, state);
}
},
_updateChildrenSelectionState: function _updateChildrenSelectionState(node, isSelected) {
var that = this,
children = node.children;
children.forEach(function (childNode) {
that._selectionStateByKey[childNode.key] = isSelected;
if (childNode.children.length > 0) {
that._updateChildrenSelectionState(childNode, isSelected);
}
});
},
_updateSelectionStateCore: function _updateSelectionStateCore(keys, isSelected) {
var node,
dataController = this._dataController;
for (var i = 0; i < keys.length; i++) {
this._selectionStateByKey[keys[i]] = isSelected;
node = dataController.getNodeByKey(keys[i]);
if (node) {
this._updateParentSelectionState(node, isSelected);
this._updateChildrenSelectionState(node, isSelected);
}
}
},
_getSelectedParentKeys: function _getSelectedParentKeys(key, selectedItemKeys, useCash) {
var isSelected,
selectedParentNode,
node = this._dataController.getNodeByKey(key),
parentNode = node && node.parent,
result = [];
while (parentNode && parentNode.level >= 0) {
result.unshift(parentNode.key);
isSelected = useCash ? !nodeExists(selectedItemKeys, parentNode.key) && this.isRowSelected(parentNode.key) : selectedItemKeys.indexOf(parentNode.key) >= 0;
if (isSelected) {
selectedParentNode = parentNode;
result = this._getSelectedParentKeys(selectedParentNode.key, selectedItemKeys, useCash).concat(result);
break;
} else if (useCash) {
break;
}
parentNode = parentNode.parent;
}
return selectedParentNode && result || [];
},
_getSelectedChildKeys: function _getSelectedChildKeys(node, keysToIgnore) {
var that = this,
childKeys = [];
node && treeListCore.foreachNodes(node.children, function (childNode) {
var ignoreKeyIndex = keysToIgnore.indexOf(childNode.key);
if (ignoreKeyIndex < 0) {
childKeys.push(childNode.key);
}
return ignoreKeyIndex > 0 || ignoreKeyIndex < 0 && that._selectionStateByKey[childNode.key] === undefined;
});
return childKeys;
},
_normalizeParentKeys: function _normalizeParentKeys(key, args) {
var that = this,
index,
childKeys,
parentNode,
keysToIgnore = [key],
parentNodeKeys = that._getSelectedParentKeys(key, args.selectedRowKeys);
if (parentNodeKeys.length) {
keysToIgnore = keysToIgnore.concat(parentNodeKeys);
keysToIgnore.forEach(function (key) {
index = args.selectedRowKeys.indexOf(key);
if (index >= 0) {
args.selectedRowKeys.splice(index, 1);
}
});
parentNode = that._dataController.getNodeByKey(parentNodeKeys[0]);
childKeys = that._getSelectedChildKeys(parentNode, keysToIgnore);
args.selectedRowKeys = args.selectedRowKeys.concat(childKeys);
}
},
_normalizeChildrenKeys: function _normalizeChildrenKeys(key, args) {
var that = this,
index,
node = that._dataController.getNodeByKey(key);
node && node.children.forEach(function (childNode) {
index = args.selectedRowKeys.indexOf(childNode.key);
if (index >= 0) {
args.selectedRowKeys.splice(index, 1);
}
that._normalizeChildrenKeys(childNode.key, args);
});
},
_normalizeSelectedRowKeysCore: function _normalizeSelectedRowKeysCore(keys, args, isSelect) {
var that = this,
index;
keys.forEach(function (key) {
if (that.isRowSelected(key) === isSelect) {
return;
}
that._normalizeChildrenKeys(key, args);
index = args.selectedRowKeys.indexOf(key);
if (isSelect) {
if (index < 0) {
args.selectedRowKeys.push(key);
}
args.currentSelectedRowKeys.push(key);
} else {
if (index >= 0) {
args.selectedRowKeys.splice(index, 1);
}
args.currentDeselectedRowKeys.push(key);
that._normalizeParentKeys(key, args);
}
});
},
_normalizeSelectionArgs: function _normalizeSelectionArgs(args, isSelect) {
var result,
keys = Array.isArray(args.keys) ? args.keys : [args.keys],
selectedRowKeys = this.option("selectedRowKeys") || [];
if (keys.length) {
result = {
currentSelectedRowKeys: [],
currentDeselectedRowKeys: [],
selectedRowKeys: selectedRowKeys.slice(0)
};
this._normalizeSelectedRowKeysCore(keys, result, isSelect);
}
return result;
},
_updateSelectedItems: function _updateSelectedItems(args) {
this.updateSelectionState(args);
this.callBase(args);
},
_fireSelectionChanged: function _fireSelectionChanged() {
if (!this._isSelectionNormalizing) {
this.callBase.apply(this, arguments);
}
},
_isModeLeavesOnly: function _isModeLeavesOnly(mode) {
return mode === "leavesOnly" || mode === true;
},
_getAllSelectedRowKeys: function _getAllSelectedRowKeys(parentKeys) {
var that = this,
result = [];
parentKeys.forEach(function (key) {
var insertIndex = result.length,
parentKeys = that._getSelectedParentKeys(key, result, true),
childKeys = that._dataController.getChildNodeKeys(key);
result.splice.apply(result, [insertIndex, 0].concat(parentKeys));
result.push(key);
result = result.concat(childKeys);
});
return result;
},
_getParentSelectedRowKeys: function _getParentSelectedRowKeys(keys) {
var that = this,
result = [];
keys.forEach(function (key) {
var parentKeys = that._getSelectedParentKeys(key, keys);
!parentKeys.length && result.push(key);
});
return result;
},
_getLeafSelectedRowKeys: function _getLeafSelectedRowKeys(keys) {
var that = this,
result = [],
dataController = that._dataController;
keys.forEach(function (key) {
var node = dataController.getNodeByKey(key);
node && !node.hasChildren && result.push(key);
});
return result;
},
isRecursiveSelection: function isRecursiveSelection() {
var selectionMode = this.option("selection.mode"),
isRecursive = this.option("selection.recursive");
return selectionMode === "multiple" && isRecursive;
},
updateSelectionState: function updateSelectionState(options) {
var removedItemKeys = options.removedItemKeys || [],
selectedItemKeys = options.selectedItemKeys || [];
this._updateSelectionStateCore(removedItemKeys, false);
this._updateSelectionStateCore(selectedItemKeys, true);
},
isRowSelected: function isRowSelected(key) {
var result = this.callBase.apply(this, arguments),
isRecursiveSelection = this.isRecursiveSelection();
if (!result && isRecursiveSelection) {
if (key in this._selectionStateByKey) {
return this._selectionStateByKey[key];
}
return false;
}
return result;
},
/**
* @name dxTreeListMethods.getSelectedRowKeys
* @publicName getSelectedRowKeys(leavesOnly)
* @param1 leavesOnly:boolean
* @return Array<any>
* @deprecated
*/
/**
* @name dxTreeListMethods.getSelectedRowKeys
* @publicName getSelectedRowKeys(mode)
* @param1 mode:string
* @return Array<any>
*/
getSelectedRowKeys: function getSelectedRowKeys(mode) {
var that = this;
if (!that._dataController) {
return [];
}
if (mode === true) {
errors.log("W0002", "dxTreeList", "getSelectedRowKeys(leavesOnly)", "18.1", "Use the 'getSelectedRowKeys(mode)' method with a string parameter instead");
}
var selectedRowKeys = that.callBase.apply(that, arguments);
if (mode) {
if (this.isRecursiveSelection()) {
selectedRowKeys = this._getAllSelectedRowKeys(selectedRowKeys);
}
if (mode !== "all") {
if (mode === "excludeRecursive") {
selectedRowKeys = that._getParentSelectedRowKeys(selectedRowKeys);
} else if (that._isModeLeavesOnly(mode)) {
selectedRowKeys = that._getLeafSelectedRowKeys(selectedRowKeys);
}
}
}
return selectedRowKeys;
}
}
},
views: {
columnHeadersView: {
_processTemplate: function _processTemplate(template, options) {
var that = this,
resultTemplate,
renderingTemplate = this.callBase(template, options);
var firstDataColumnIndex = that._columnsController.getFirstDataColumnIndex();
if (renderingTemplate && options.column.index === firstDataColumnIndex) {
resultTemplate = {
render: function render(options) {
if (that.option("selection.mode") === "multiple") {
that.renderSelectAll(options.container, options.model);
}
renderingTemplate.render(options);
}
};
} else {
resultTemplate = renderingTemplate;
}
return resultTemplate;
},
renderSelectAll: function renderSelectAll($cell, options) {
$cell.addClass(TREELIST_SELECT_ALL_CLASS);
this._renderSelectAllCheckBox($cell);
},
_isSortableElement: function _isSortableElement($target) {
return this.callBase($target) && !$target.closest("." + SELECT_CHECKBOX_CLASS).length;
}
},
rowsView: {
_renderExpandIcon: function _renderExpandIcon($container, options) {
var $iconContainer = this.callBase($container, options);
if (this.option("selection.mode") === "multiple") {
this.getController("selection").renderSelectCheckBoxContainer($iconContainer, options);
}
return $iconContainer;
},
_rowClick: function _rowClick(e) {
var $targetElement = $(e.event.target);
if (this.isExpandIcon($targetElement)) {
this.callBase.apply(this, arguments);
} else {
originalRowClick.apply(this, arguments);
}
}
}
}
}
}));