UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

717 lines (715 loc) • 35.3 kB
/** * DevExtreme (ui/grid_core/ui.grid_core.selection.js) * Version: 18.2.18 * Build date: Tue Oct 18 2022 * * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; var _renderer = require("../../core/renderer"); var _renderer2 = _interopRequireDefault(_renderer); var _events_engine = require("../../events/core/events_engine"); var _events_engine2 = _interopRequireDefault(_events_engine); var _uiData_grid = require("../data_grid/ui.data_grid.core"); var _uiData_grid2 = _interopRequireDefault(_uiData_grid); var _uiGrid_core = require("./ui.grid_core.utils"); var _type = require("../../core/utils/type"); var _iterator = require("../../core/utils/iterator"); var _extend = require("../../core/utils/extend"); var _support = require("../../core/utils/support"); var _support2 = _interopRequireDefault(_support); var _click = require("../../events/click"); var _click2 = _interopRequireDefault(_click); var _message = require("../../localization/message"); var _message2 = _interopRequireDefault(_message); var _utils = require("../../events/utils"); var _hold = require("../../events/hold"); var _hold2 = _interopRequireDefault(_hold); var _selection = require("../selection/selection"); var _selection2 = _interopRequireDefault(_selection); var _deferred = require("../../core/utils/deferred"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj } } var EDITOR_CELL_CLASS = "dx-editor-cell", ROW_CLASS = "dx-row", ROW_SELECTION_CLASS = "dx-selection", SELECT_CHECKBOX_CLASS = "dx-select-checkbox", CHECKBOXES_HIDDEN_CLASS = "dx-select-checkboxes-hidden", COMMAND_SELECT_CLASS = "dx-command-select", SELECTION_DISABLED_CLASS = "dx-selection-disabled", DATA_ROW_CLASS = "dx-data-row"; var SHOW_CHECKBOXES_MODE = "selection.showCheckBoxesMode", SELECTION_MODE = "selection.mode"; var processLongTap = function(that, dxEvent) { var selectionController = that.getController("selection"), rowsView = that.getView("rowsView"), $row = (0, _renderer2.default)(dxEvent.target).closest("." + DATA_ROW_CLASS), rowIndex = rowsView.getRowIndex($row); if (rowIndex < 0) { return } if ("onLongTap" === that.option(SHOW_CHECKBOXES_MODE)) { if (selectionController.isSelectionWithCheckboxes()) { selectionController.stopSelectionWithCheckboxes() } else { selectionController.startSelectionWithCheckboxes() } } else { if ("onClick" === that.option(SHOW_CHECKBOXES_MODE)) { selectionController.startSelectionWithCheckboxes() } if ("always" !== that.option(SHOW_CHECKBOXES_MODE)) { selectionController.changeItemSelection(rowIndex, { control: true }) } } }; exports.SelectionController = _uiData_grid2.default.Controller.inherit(function() { var isSeveralRowsSelected = function(that, selectionFilter) { var keyIndex = 0, store = that._dataController.store(), key = store && store.key(), isComplexKey = Array.isArray(key); if (!selectionFilter.length) { return false } if (isComplexKey && Array.isArray(selectionFilter[0]) && "and" === selectionFilter[1]) { for (var i = 0; i < selectionFilter.length; i++) { if (Array.isArray(selectionFilter[i])) { if (selectionFilter[i][0] !== key[keyIndex] || "=" !== selectionFilter[i][1]) { return true } keyIndex++ } } return false } return key !== selectionFilter[0] }; var selectionCellTemplate = function(container, options) { var rowsView = options.component.getView("rowsView"); rowsView.renderSelectCheckBoxContainer((0, _renderer2.default)(container), options) }; var selectionHeaderTemplate = function(container, options) { var column = options.column, $cellElement = (0, _renderer2.default)(container), columnHeadersView = options.component.getView("columnHeadersView"); $cellElement.addClass(EDITOR_CELL_CLASS); columnHeadersView._renderSelectAllCheckBox($cellElement, column); columnHeadersView._attachSelectAllCheckBoxClickEvent($cellElement) }; return { init: function() { this._dataController = this.getController("data"); this._selectionMode = this.option(SELECTION_MODE); this._isSelectionWithCheckboxes = false; this._selection = this._createSelection(); this._updateSelectColumn(); this.createAction("onSelectionChanged", { excludeValidators: ["disabled", "readOnly"] }) }, _getSelectionConfig: function() { var that = this, dataController = that._dataController, selectionOptions = that.option("selection") || {}; return { selectedKeys: that.option("selectedRowKeys"), mode: that._selectionMode, deferred: selectionOptions.deferred, maxFilterLengthInRequest: selectionOptions.maxFilterLengthInRequest, selectionFilter: that.option("selectionFilter"), key: function() { return dataController && dataController.key() }, keyOf: function(item) { return dataController && dataController.keyOf(item) }, dataFields: function() { return dataController.dataSource() && dataController.dataSource().select() }, load: function(options) { return dataController.dataSource() && dataController.dataSource().load(options) || (new _deferred.Deferred).resolve([]) }, plainItems: function() { return dataController.items(true) }, isItemSelected: function(item) { return item.selected }, isSelectableItem: function(item) { return item && "data" === item.rowType && !item.inserted }, getItemData: function(item) { return item && (item.oldData || item.data || item) }, filter: function() { return dataController.getCombinedFilter() }, totalCount: function() { return dataController.totalCount() }, onSelectionChanged: that._updateSelectedItems.bind(this) } }, _updateSelectColumn: function() { var columnsController = this.getController("columns"), isSelectColumnVisible = this.isSelectColumnVisible(); columnsController.addCommandColumn({ type: "selection", command: "select", visible: isSelectColumnVisible, visibleIndex: -1, dataType: "boolean", alignment: "center", cssClass: COMMAND_SELECT_CLASS, width: "auto", cellTemplate: selectionCellTemplate, headerCellTemplate: selectionHeaderTemplate }); columnsController.columnOption("command:select", "visible", isSelectColumnVisible) }, _createSelection: function() { var options = this._getSelectionConfig(); return new _selection2.default(options) }, _fireSelectionChanged: function(options) { if (options) { this.executeAction("onSelectionChanged", options) } var argument = this.option("selection.deferred") ? { selectionFilter: this.option("selectionFilter") } : { selectedRowKeys: this.option("selectedRowKeys") }; this.selectionChanged.fire(argument) }, _updateCheckboxesState: function(options) { var isDeferredMode = options.isDeferredMode, selectionFilter = options.selectionFilter, selectedItemKeys = options.selectedItemKeys, removedItemKeys = options.removedItemKeys; if ("onClick" === this.option(SHOW_CHECKBOXES_MODE)) { if (isDeferredMode ? selectionFilter && isSeveralRowsSelected(this, selectionFilter) : selectedItemKeys.length > 1) { this.startSelectionWithCheckboxes() } else { if (isDeferredMode ? selectionFilter && !selectionFilter.length : 0 === selectedItemKeys.length && removedItemKeys.length) { this.stopSelectionWithCheckboxes() } } } }, _updateSelectedItems: function(args) { var selectionChangedOptions, that = this, isDeferredMode = that.option("selection.deferred"), selectionFilter = that._selection.selectionFilter(), dataController = that._dataController, items = dataController.items(); if (!items) { return } var isSelectionWithCheckboxes = that.isSelectionWithCheckboxes(); var changedItemIndexes = that.getChangedItemIndexes(items); that._updateCheckboxesState({ selectedItemKeys: args.selectedItemKeys, removedItemKeys: args.removedItemKeys, selectionFilter: selectionFilter, isDeferredMode: isDeferredMode }); if (changedItemIndexes.length || isSelectionWithCheckboxes !== that.isSelectionWithCheckboxes()) { dataController.updateItems({ changeType: "updateSelection", itemIndexes: changedItemIndexes }) } if (isDeferredMode) { that.option("selectionFilter", selectionFilter); selectionChangedOptions = {} } else { if (args.addedItemKeys.length || args.removedItemKeys.length) { that._selectedItemsInternalChange = true; that.option("selectedRowKeys", args.selectedItemKeys.slice(0)); that._selectedItemsInternalChange = false; selectionChangedOptions = { selectedRowsData: args.selectedItems.slice(0), selectedRowKeys: args.selectedItemKeys.slice(0), currentSelectedRowKeys: args.addedItemKeys.slice(0), currentDeselectedRowKeys: args.removedItemKeys.slice(0) } } } that._fireSelectionChanged(selectionChangedOptions) }, getChangedItemIndexes: function(items) { var that = this, itemIndexes = [], isDeferredSelection = this.option("selection.deferred"); for (var i = 0, length = items.length; i < length; i++) { var row = items[i]; var isItemSelected = that.isRowSelected(isDeferredSelection ? row.data : row.key); if (that._selection.isDataItem(row) && row.isSelected !== isItemSelected) { itemIndexes.push(i) } } return itemIndexes }, callbackNames: function() { return ["selectionChanged"] }, optionChanged: function(args) { var that = this; that.callBase(args); switch (args.name) { case "selection": var oldSelectionMode = that._selectionMode; that.init(); var selectionMode = that._selectionMode; var selectedRowKeys = that.option("selectedRowKeys"); if (oldSelectionMode !== selectionMode) { if ("single" === selectionMode) { if (selectedRowKeys.length > 1) { selectedRowKeys = [selectedRowKeys[0]] } } else { if ("multiple" !== selectionMode) { selectedRowKeys = [] } } } that.selectRows(selectedRowKeys).always(function() { that._fireSelectionChanged() }); that.getController("columns").updateColumns(); args.handled = true; break; case "selectionFilter": this._selection.selectionFilter(args.value); args.handled = true; break; case "selectedRowKeys": if (Array.isArray(args.value) && !that._selectedItemsInternalChange && that.component.getDataSource()) { that.selectRows(args.value) } args.handled = true } }, publicMethods: function() { return ["selectRows", "deselectRows", "selectRowsByIndexes", "getSelectedRowKeys", "getSelectedRowsData", "clearSelection", "selectAll", "deselectAll", "startSelectionWithCheckboxes", "stopSelectionWithCheckboxes", "isRowSelected"] }, isRowSelected: function(arg) { return this._selection.isItemSelected(arg) }, isSelectColumnVisible: function() { return "multiple" === this.option(SELECTION_MODE) && ("always" === this.option(SHOW_CHECKBOXES_MODE) || "onClick" === this.option(SHOW_CHECKBOXES_MODE) || this._isSelectionWithCheckboxes) }, _isOnePageSelectAll: function() { return "page" === this.option("selection.selectAllMode") }, isSelectAll: function() { return this._selection.getSelectAllState(this._isOnePageSelectAll()) }, selectAll: function() { if ("onClick" === this.option(SHOW_CHECKBOXES_MODE)) { this.startSelectionWithCheckboxes() } return this._selection.selectAll(this._isOnePageSelectAll()) }, deselectAll: function() { return this._selection.deselectAll(this._isOnePageSelectAll()) }, clearSelection: function() { return this.selectedItemKeys([]) }, refresh: function() { var selectedRowKeys = this.option("selectedRowKeys") || []; if (!this.option("selection.deferred") && selectedRowKeys.length) { return this.selectedItemKeys(selectedRowKeys) } return (new _deferred.Deferred).resolve().promise() }, selectedItemKeys: function(value, preserve, isDeselect, isSelectAll) { return this._selection.selectedItemKeys(value, preserve, isDeselect, isSelectAll) }, getSelectedRowKeys: function() { return this._selection.getSelectedItemKeys() }, selectRows: function(keys, preserve) { return this.selectedItemKeys(keys, preserve) }, deselectRows: function(keys) { return this.selectedItemKeys(keys, true, true) }, selectRowsByIndexes: function(indexes) { var items = this._dataController.items(), keys = []; if (!Array.isArray(indexes)) { indexes = Array.prototype.slice.call(arguments, 0) }(0, _iterator.each)(indexes, function() { var item = items[this]; if (item && "data" === item.rowType) { keys.push(item.key) } }); return this.selectRows(keys) }, getSelectedRowsData: function() { return this._selection.getSelectedItems() }, changeItemSelection: function(itemIndex, keys) { keys = keys || {}; if (this.isSelectionWithCheckboxes()) { keys.control = true } return this._selection.changeItemSelection(this._dataController.getRowIndexDelta() + itemIndex, keys) }, focusedItemIndex: function(itemIndex) { var that = this; if ((0, _type.isDefined)(itemIndex)) { that._selection._focusedItemIndex = itemIndex } else { return that._selection._focusedItemIndex } }, isSelectionWithCheckboxes: function() { return "multiple" === this.option(SELECTION_MODE) && ("always" === this.option(SHOW_CHECKBOXES_MODE) || this._isSelectionWithCheckboxes) }, startSelectionWithCheckboxes: function() { var that = this; if ("multiple" === that.option(SELECTION_MODE) && !that.isSelectionWithCheckboxes()) { that._isSelectionWithCheckboxes = true; that._updateSelectColumn(); return true } return false }, stopSelectionWithCheckboxes: function() { var that = this; if (that._isSelectionWithCheckboxes) { that._isSelectionWithCheckboxes = false; that._updateSelectColumn(); return true } return false } } }()); module.exports = { defaultOptions: function() { return { selection: { mode: "none", showCheckBoxesMode: "onClick", allowSelectAll: true, selectAllMode: "allPages", maxFilterLengthInRequest: 1500, deferred: false }, selectionFilter: [], selectedRowKeys: [] } }, controllers: { selection: exports.SelectionController }, extenders: { controllers: { data: { init: function() { var selectionController = this.getController("selection"), isDeferredMode = this.option("selection.deferred"); this.callBase.apply(this, arguments); if (isDeferredMode) { selectionController._updateCheckboxesState({ isDeferredMode: true, selectionFilter: this.option("selectionFilter") }) } }, _loadDataSource: function() { var that = this; return that.callBase().done(function() { that.getController("selection").refresh() }) }, _processDataItem: function(item, options) { var that = this, selectionController = that.getController("selection"), hasSelectColumn = selectionController.isSelectColumnVisible(), isDeferredSelection = options.isDeferredSelection = void 0 === options.isDeferredSelection ? this.option("selection.deferred") : options.isDeferredSelection, dataItem = this.callBase.apply(this, arguments); dataItem.isSelected = selectionController.isRowSelected(isDeferredSelection ? dataItem.data : dataItem.key); if (hasSelectColumn && dataItem.values) { for (var i = 0; i < options.visibleColumns.length; i++) { if ("select" === options.visibleColumns[i].command) { dataItem.values[i] = dataItem.isSelected; break } } } return dataItem }, refresh: function(options) { var that = this, d = new _deferred.Deferred; this.callBase.apply(this, arguments).done(function() { if (!options || options.selection) { that.getController("selection").refresh().done(d.resolve).fail(d.reject) } else { d.resolve() } }).fail(d.reject); return d.promise() }, _handleDataChanged: function(e) { this.callBase.apply(this, arguments); if ((!e || "refresh" === e.changeType) && !this._repaintChangesOnly) { this.getController("selection").focusedItemIndex(-1) } }, _applyChange: function(change) { var _this = this; if (change && "updateSelection" === change.changeType) { change.items.forEach(function(item, index) { var currentItem = _this._items[index]; if (currentItem) { currentItem.isSelected = item.isSelected; currentItem.values = item.values } }); return } return this.callBase.apply(this, arguments) }, _endUpdateCore: function() { var changes = this._changes; var isUpdateSelection = changes.length > 1 && changes.every(function(change) { return "updateSelection" === change.changeType }); if (isUpdateSelection) { var itemIndexes = changes.map(function(change) { return change.itemIndexes || [] }).reduce(function(a, b) { return a.concat(b) }); this._changes = [{ changeType: "updateSelection", itemIndexes: itemIndexes }] } this.callBase.apply(this, arguments) } }, contextMenu: { _contextMenuPrepared: function(options) { var dxEvent = options.event; if (dxEvent.originalEvent && "dxhold" !== dxEvent.originalEvent.type || options.items && options.items.length > 0) { return } processLongTap(this, dxEvent) } } }, views: { columnHeadersView: { init: function() { var that = this; that.callBase(); that.getController("selection").selectionChanged.add(that._updateSelectAllValue.bind(that)) }, _updateSelectAllValue: function() { var that = this, $element = that.element(), $editor = $element && $element.find("." + SELECT_CHECKBOX_CLASS); if ($element && $editor.length && "multiple" === that.option("selection.mode")) { $editor.dxCheckBox("instance").option("value", that.getController("selection").isSelectAll()) } }, _handleDataChanged: function(e) { this.callBase(e); if (!e || "refresh" === e.changeType) { this._updateSelectAllValue() } }, _renderSelectAllCheckBox: function($container, column) { var groupElement, that = this, selectionController = that.getController("selection"); groupElement = (0, _renderer2.default)("<div>").appendTo($container).addClass(SELECT_CHECKBOX_CLASS); that.setAria("label", _message2.default.format("dxDataGrid-ariaSelectAll"), $container); that.getController("editorFactory").createEditor(groupElement, (0, _extend.extend)({}, column, { parentType: "headerRow", dataType: "boolean", value: selectionController.isSelectAll(), editorOptions: { visible: that.option("selection.allowSelectAll") || false !== selectionController.isSelectAll() }, tabIndex: -1, setValue: function(value, e) { var allowSelectAll = that.option("selection.allowSelectAll"); e.component.option("visible", allowSelectAll || false !== e.component.option("value")); if (!e.event || selectionController.isSelectAll() === value) { return } if (e.value && !allowSelectAll) { e.component.option("value", false) } else { e.value ? selectionController.selectAll() : selectionController.deselectAll() } e.event.preventDefault() } })); return groupElement }, _attachSelectAllCheckBoxClickEvent: function($element) { _events_engine2.default.on($element, _click2.default.name, this.createAction(function(e) { var event = e.event; if (!(0, _renderer2.default)(event.target).closest("." + SELECT_CHECKBOX_CLASS).length) { _events_engine2.default.trigger((0, _renderer2.default)(event.currentTarget).children("." + SELECT_CHECKBOX_CLASS), _click2.default.name) } event.preventDefault() })) } }, rowsView: { renderSelectCheckBoxContainer: function($container, options) { if ("data" === options.rowType && !options.row.inserted) { $container.addClass(EDITOR_CELL_CLASS); this._attachCheckBoxClickEvent($container); this.setAria("label", _message2.default.format("dxDataGrid-ariaSelectRow"), $container); this._renderSelectCheckBox($container, options) } else { (0, _uiGrid_core.setEmptyText)($container) } }, _renderSelectCheckBox: function(container, options) { var groupElement = (0, _renderer2.default)("<div>").addClass(SELECT_CHECKBOX_CLASS).appendTo(container); this.getController("editorFactory").createEditor(groupElement, (0, _extend.extend)({}, options.column, { parentType: "dataRow", dataType: "boolean", lookup: null, value: options.value, tabIndex: -1, setValue: function(value, e) { if (e && e.event && "keydown" === e.event.type) { _events_engine2.default.trigger(container, _click2.default.name, e) } }, row: options.row })); return groupElement }, _attachCheckBoxClickEvent: function($element) { _events_engine2.default.on($element, _click2.default.name, this.createAction(function(e) { var selectionController = this.getController("selection"), event = e.event, rowIndex = this.getRowIndex((0, _renderer2.default)(event.currentTarget).closest("." + ROW_CLASS)); if (rowIndex >= 0) { selectionController.startSelectionWithCheckboxes(); selectionController.changeItemSelection(rowIndex, { shift: event.shiftKey }); if ((0, _renderer2.default)(event.target).closest("." + SELECT_CHECKBOX_CLASS).length) { this.getController("data").updateItems({ changeType: "updateSelection", itemIndexes: [rowIndex] }) } } })) }, _update: function(change) { var that = this, tableElements = that.getTableElements(); if ("updateSelection" === change.changeType) { if (tableElements.length > 0) { (0, _iterator.each)(tableElements, function(_, tableElement) { (0, _iterator.each)(change.itemIndexes || [], function(_, index) { var $row, isSelected; if (change.items[index]) { $row = that._getRowElements((0, _renderer2.default)(tableElement)).eq(index); if ($row.length) { isSelected = change.items[index].isSelected; $row.toggleClass(ROW_SELECTION_CLASS, void 0 === isSelected ? false : isSelected).find("." + SELECT_CHECKBOX_CLASS).dxCheckBox("option", "value", isSelected); that.setAria("selected", isSelected, $row) } } }) }); that._updateCheckboxesClass() } } else { that.callBase(change) } }, _createTable: function() { var that = this, selectionMode = that.option("selection.mode"), $table = that.callBase.apply(that, arguments); if ("none" !== selectionMode) { if ("onLongTap" === that.option(SHOW_CHECKBOXES_MODE) || !_support2.default.touch) { _events_engine2.default.on($table, (0, _utils.addNamespace)(_hold2.default.name, "dxDataGridRowsView"), "." + DATA_ROW_CLASS, that.createAction(function(e) { processLongTap(that.component, e.event); e.event.stopPropagation() })) } _events_engine2.default.on($table, "mousedown selectstart", that.createAction(function(e) { var event = e.event; if (event.shiftKey) { event.preventDefault() } })) } return $table }, _createRow: function(row) { var isSelected, $row = this.callBase(row); if (row) { isSelected = !!row.isSelected; if (isSelected) { $row.addClass(ROW_SELECTION_CLASS) } this.setAria("selected", isSelected, $row) } return $row }, _rowClick: function(e) { var that = this, dxEvent = e.event, isSelectionDisabled = (0, _renderer2.default)(dxEvent.target).closest("." + SELECTION_DISABLED_CLASS).length; if (!that.isClickableElement((0, _renderer2.default)(dxEvent.target))) { if (!isSelectionDisabled && ("multiple" !== that.option(SELECTION_MODE) || "always" !== that.option(SHOW_CHECKBOXES_MODE))) { if (that.getController("selection").changeItemSelection(e.rowIndex, { control: dxEvent.ctrlKey || dxEvent.metaKey, shift: dxEvent.shiftKey })) { dxEvent.preventDefault(); e.handled = true } } that.callBase(e) } }, isClickableElement: function($target) { var isCommandSelect = $target.closest("." + COMMAND_SELECT_CLASS).length; return !!isCommandSelect }, _renderCore: function(change) { this.callBase(change); this._updateCheckboxesClass() }, _updateCheckboxesClass: function() { var tableElements = this.getTableElements(), selectionController = this.getController("selection"), isCheckBoxesHidden = selectionController.isSelectColumnVisible() && !selectionController.isSelectionWithCheckboxes(); (0, _iterator.each)(tableElements, function(_, tableElement) { (0, _renderer2.default)(tableElement).toggleClass(CHECKBOXES_HIDDEN_CLASS, isCheckBoxesHidden) }) } } } } };