devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
578 lines (576 loc) • 30.8 kB
JavaScript
/**
* DevExtreme (esm/ui/grid_core/ui.grid_core.editing_cell_based.js)
* Version: 21.1.4
* Build date: Mon Jun 21 2021
*
* Copyright (c) 2012 - 2021 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import $ from "../../core/renderer";
import domAdapter from "../../core/dom_adapter";
import {
getWindow
} from "../../core/utils/window";
import eventsEngine from "../../events/core/events_engine";
import {
isDefined,
isString
} from "../../core/utils/type";
import {
name as clickEventName
} from "../../events/click";
import pointerEvents from "../../events/pointer";
import {
addNamespace
} from "../../events/utils/index";
import holdEvent from "../../events/hold";
import {
when,
Deferred
} from "../../core/utils/deferred";
import {
deferRender
} from "../../core/utils/common";
import {
createObjectWithChanges
} from "../../data/array_utils";
import {
EDIT_MODE_BATCH,
EDIT_MODE_CELL,
TARGET_COMPONENT_NAME
} from "./ui.grid_core.editing_constants";
var FOCUS_OVERLAY_CLASS = "focus-overlay";
var ADD_ROW_BUTTON_CLASS = "addrow-button";
var DROPDOWN_EDITOR_OVERLAY_CLASS = "dx-dropdowneditor-overlay";
var EDITOR_CELL_CLASS = "dx-editor-cell";
var ROW_CLASS = "dx-row";
var CELL_MODIFIED_CLASS = "dx-cell-modified";
var DATA_ROW_CLASS = "dx-data-row";
var ROW_REMOVED = "dx-row-removed";
var EDITING_EDITROWKEY_OPTION_NAME = "editing.editRowKey";
var EDITING_EDITCOLUMNNAME_OPTION_NAME = "editing.editColumnName";
var DATA_EDIT_DATA_REMOVE_TYPE = "remove";
export default {
extenders: {
controllers: {
editing: {
init: function() {
var needCreateHandlers = !this._saveEditorHandler;
this.callBase.apply(this, arguments);
if (needCreateHandlers) {
var $pointerDownTarget;
var isResizing;
this._pointerUpEditorHandler = () => {
var _this$getController;
isResizing = null === (_this$getController = this.getController("columnsResizer")) || void 0 === _this$getController ? void 0 : _this$getController.isResizing()
};
this._pointerDownEditorHandler = e => $pointerDownTarget = $(e.target);
this._saveEditorHandler = this.createAction((function(e) {
var event = e.event;
var $target = $(event.target);
var targetComponent = event[TARGET_COMPONENT_NAME];
if ($pointerDownTarget && $pointerDownTarget.is("input") && !$pointerDownTarget.is($target)) {
return
}
function checkEditorPopup($element) {
return $element && !!$element.closest(".".concat(DROPDOWN_EDITOR_OVERLAY_CLASS)).length
}
if (this.isCellOrBatchEditMode() && !this._editCellInProgress) {
var isEditorPopup = checkEditorPopup($target) || checkEditorPopup(null === targetComponent || void 0 === targetComponent ? void 0 : targetComponent.$element());
var isDomElement = !!$target.closest(getWindow().document).length;
var isAnotherComponent = targetComponent && !targetComponent._disposed && targetComponent !== this.component;
var isAddRowButton = !!$target.closest(".".concat(this.addWidgetPrefix(ADD_ROW_BUTTON_CLASS))).length;
var isFocusOverlay = $target.hasClass(this.addWidgetPrefix(FOCUS_OVERLAY_CLASS));
var isCellEditMode = this.isCellEditMode();
if (!isResizing && !isEditorPopup && !isFocusOverlay && !(isAddRowButton && isCellEditMode && this.isEditing()) && (isDomElement || isAnotherComponent)) {
this._closeEditItem.bind(this)($target)
}
}
}));
eventsEngine.on(domAdapter.getDocument(), pointerEvents.up, this._pointerUpEditorHandler);
eventsEngine.on(domAdapter.getDocument(), pointerEvents.down, this._pointerDownEditorHandler);
eventsEngine.on(domAdapter.getDocument(), clickEventName, this._saveEditorHandler)
}
},
isCellEditMode: function() {
return this.option("editing.mode") === EDIT_MODE_CELL
},
isBatchEditMode: function() {
return this.option("editing.mode") === EDIT_MODE_BATCH
},
isCellOrBatchEditMode: function() {
return this.isCellEditMode() || this.isBatchEditMode()
},
_needToCloseEditableCell: function($targetElement) {
var $element = this.component.$element();
var result = this.isEditing();
var isCurrentComponentElement = !$element || !!$targetElement.closest($element).length;
if (isCurrentComponentElement) {
var isDataRow = $targetElement.closest("." + DATA_ROW_CLASS).length;
if (isDataRow) {
var rowsView = this.getView("rowsView");
var $targetCell = $targetElement.closest("." + ROW_CLASS + "> td");
var rowIndex = rowsView.getRowIndex($targetCell.parent());
var columnIndex = rowsView.getCellElements(rowIndex).index($targetCell);
var visibleColumns = this._columnsController.getVisibleColumns();
var allowEditing = visibleColumns[columnIndex] && visibleColumns[columnIndex].allowEditing;
result = result && !allowEditing && !this.isEditCell(rowIndex, columnIndex)
}
}
return result || this.callBase.apply(this, arguments)
},
_closeEditItem: function($targetElement) {
if (this._needToCloseEditableCell($targetElement)) {
this.closeEditCell()
}
},
_focusEditorIfNeed: function() {
if (this._needFocusEditor && this.isCellOrBatchEditMode()) {
var _this$_rowsView;
var editColumnIndex = this._getVisibleEditColumnIndex();
var $cell = null === (_this$_rowsView = this._rowsView) || void 0 === _this$_rowsView ? void 0 : _this$_rowsView._getCellElement(this._getVisibleEditRowIndex(), editColumnIndex);
if ($cell && !$cell.find(":focus").length) {
this._focusEditingCell(() => {
this._editCellInProgress = false
}, $cell, true)
} else {
this._editCellInProgress = false
}
this._needFocusEditor = false
} else {
this.callBase.apply(this, arguments)
}
},
isEditing: function() {
if (this.isCellOrBatchEditMode()) {
var isEditRowKeyDefined = isDefined(this.option(EDITING_EDITROWKEY_OPTION_NAME));
var isEditColumnNameDefined = isDefined(this.option(EDITING_EDITCOLUMNNAME_OPTION_NAME));
return isEditRowKeyDefined && isEditColumnNameDefined
}
return this.callBase.apply(this, arguments)
},
_handleEditColumnNameChange: function(args) {
var oldRowIndex = this._getVisibleEditRowIndex(args.previousValue);
if (this.isCellOrBatchEditMode() && -1 !== oldRowIndex && isDefined(args.value) && args.value !== args.previousValue) {
var columnIndex = this._columnsController.getVisibleColumnIndex(args.value);
var oldColumnIndex = this._columnsController.getVisibleColumnIndex(args.previousValue);
this._editCellFromOptionChanged(columnIndex, oldColumnIndex, oldRowIndex)
}
},
_addRow: function(parentKey, deferred) {
if (this.isCellEditMode() && this.hasChanges()) {
var _deferred = new Deferred;
this.saveEditData().done(() => {
if (!this.hasChanges()) {
this.addRow(parentKey).done(_deferred.resolve).fail(_deferred.reject)
} else {
_deferred.reject("cancel")
}
});
return _deferred.promise()
}
return this.callBase.apply(this, arguments)
},
editCell: function(rowIndex, columnIndex) {
return this._editCell({
rowIndex: rowIndex,
columnIndex: columnIndex
})
},
_editCell: function(options) {
var d = new Deferred;
var coreResult;
this.executeOperation(d, () => {
coreResult = this._editCellCore(options);
when(coreResult).done(d.resolve).fail(d.reject)
});
return void 0 !== coreResult ? coreResult : d.promise()
},
_editCellCore: function(options) {
var dataController = this._dataController;
var isEditByOptionChanged = isDefined(options.oldColumnIndex) || isDefined(options.oldRowIndex);
var {
columnIndex: columnIndex,
rowIndex: rowIndex,
column: column,
item: item
} = this._getNormalizedEditCellOptions(options);
var params = {
data: null === item || void 0 === item ? void 0 : item.data,
cancel: false,
column: column
};
if (void 0 === item.key) {
this._dataController.fireError("E1043");
return
}
if (column && item && ("data" === item.rowType || "detailAdaptive" === item.rowType) && !item.removed && this.isCellOrBatchEditMode()) {
if (!isEditByOptionChanged && this.isEditCell(rowIndex, columnIndex)) {
return true
}
var editRowIndex = rowIndex + dataController.getRowIndexOffset();
return when(this._beforeEditCell(rowIndex, columnIndex, item)).done(cancel => {
if (cancel) {
return
}
if (!this._prepareEditCell(params, item, columnIndex, editRowIndex)) {
this._processCanceledEditingCell()
}
})
}
return false
},
_beforeEditCell: function(rowIndex, columnIndex, item) {
if (this.isCellEditMode() && !item.isNewRow && this.hasChanges()) {
var d = new Deferred;
this.saveEditData().always(() => {
d.resolve(this.hasChanges())
});
return d
}
},
publicMethods: function() {
var publicMethods = this.callBase.apply(this, arguments);
return publicMethods.concat(["editCell", "closeEditCell"])
},
_getNormalizedEditCellOptions: function(_ref) {
var {
oldColumnIndex: oldColumnIndex,
oldRowIndex: oldRowIndex,
columnIndex: columnIndex,
rowIndex: rowIndex
} = _ref;
var columnsController = this._columnsController;
var visibleColumns = columnsController.getVisibleColumns();
var items = this._dataController.items();
var item = items[rowIndex];
var oldColumn;
if (isDefined(oldColumnIndex)) {
oldColumn = visibleColumns[oldColumnIndex]
} else {
oldColumn = this._getEditColumn()
}
if (!isDefined(oldRowIndex)) {
oldRowIndex = this._getVisibleEditRowIndex()
}
if (isString(columnIndex)) {
columnIndex = columnsController.columnOption(columnIndex, "index");
columnIndex = columnsController.getVisibleIndex(columnIndex)
}
var column = visibleColumns[columnIndex];
return {
oldColumn: oldColumn,
columnIndex: columnIndex,
oldRowIndex: oldRowIndex,
rowIndex: rowIndex,
column: column,
item: item
}
},
_prepareEditCell: function(params, item, editColumnIndex, editRowIndex) {
if (!item.isNewRow) {
params.key = item.key
}
if (this._isEditingStart(params)) {
return false
}
this._pageIndex = this._dataController.pageIndex();
this._setEditRowKey(item.key);
this._setEditColumnNameByIndex(editColumnIndex);
if (!params.column.showEditorAlways) {
this._addInternalData({
key: item.key,
oldData: item.data
})
}
return true
},
closeEditCell: function(isError, withoutSaveEditData) {
var result = when();
var oldEditRowIndex = this._getVisibleEditRowIndex();
if (this.isCellOrBatchEditMode()) {
var deferred = new Deferred;
result = new Deferred;
this.executeOperation(deferred, () => {
this._closeEditCellCore(isError, oldEditRowIndex, withoutSaveEditData).always(result.resolve)
})
}
return result.promise()
},
_closeEditCellCore(isError, oldEditRowIndex, withoutSaveEditData) {
var dataController = this._dataController;
var deferred = new Deferred;
var promise = deferred.promise();
if (this.isCellEditMode() && this.hasChanges()) {
if (!withoutSaveEditData) {
this.saveEditData().done(error => {
if (!this.hasChanges()) {
this.closeEditCell(!!error).always(deferred.resolve);
return
}
deferred.resolve()
});
return promise
}
} else if (oldEditRowIndex >= 0) {
var rowIndices = [oldEditRowIndex];
this._resetEditRowKey();
this._resetEditColumnName();
this._beforeCloseEditCellInBatchMode(rowIndices);
if (!isError) {
dataController.updateItems({
changeType: "update",
rowIndices: rowIndices
})
}
}
deferred.resolve();
return promise
},
_resetModifiedClassCells: function(changes) {
if (this.isBatchEditMode()) {
var columnsCount = this._columnsController.getVisibleColumns().length;
changes.forEach(_ref2 => {
var {
key: key
} = _ref2;
var rowIndex = this._dataController.getRowIndexByKey(key);
if (-1 !== rowIndex) {
for (var columnIndex = 0; columnIndex < columnsCount; columnIndex++) {
this._rowsView._getCellElement(rowIndex, columnIndex).removeClass(CELL_MODIFIED_CLASS)
}
}
})
}
},
_prepareChange: function(options, value, text) {
var $cellElement = $(options.cellElement);
if (this.isBatchEditMode() && void 0 !== options.key) {
this._applyModified($cellElement, options)
}
return this.callBase.apply(this, arguments)
},
_cancelSaving: function() {
var dataController = this._dataController;
if (this.isCellOrBatchEditMode()) {
if (this.isBatchEditMode()) {
this._resetEditIndices()
}
dataController.updateItems()
}
this.callBase.apply(this, arguments)
},
optionChanged: function(args) {
var fullName = args.fullName;
if ("editing" === args.name && fullName === EDITING_EDITCOLUMNNAME_OPTION_NAME) {
this._handleEditColumnNameChange(args);
args.handled = true
} else {
this.callBase(args)
}
},
_editCellFromOptionChanged: function(columnIndex, oldColumnIndex, oldRowIndex) {
var columns = this._columnsController.getVisibleColumns();
if (columnIndex > -1) {
deferRender(() => {
this._repaintEditCell(columns[columnIndex], columns[oldColumnIndex], oldRowIndex)
})
}
},
_handleEditRowKeyChange: function(args) {
if (this.isCellOrBatchEditMode()) {
var columnIndex = this._getVisibleEditColumnIndex();
var oldRowIndexCorrection = this._getEditRowIndexCorrection();
var oldRowIndex = this._dataController.getRowIndexByKey(args.previousValue) + oldRowIndexCorrection;
if (isDefined(args.value) && args.value !== args.previousValue) {
var _this$_editCellFromOp;
null === (_this$_editCellFromOp = this._editCellFromOptionChanged) || void 0 === _this$_editCellFromOp ? void 0 : _this$_editCellFromOp.call(this, columnIndex, columnIndex, oldRowIndex)
}
} else {
this.callBase.apply(this, arguments)
}
},
deleteRow: function(rowIndex) {
if (this.isCellEditMode() && this.isEditing()) {
var isNewRow = this._dataController.items()[rowIndex].isNewRow;
var rowKey = this._dataController.getKeyByRowIndex(rowIndex);
this.closeEditCell(null, isNewRow).always(() => {
rowIndex = this._dataController.getRowIndexByKey(rowKey);
this._checkAndDeleteRow(rowIndex)
})
} else {
this.callBase.apply(this, arguments)
}
},
_checkAndDeleteRow: function(rowIndex) {
if (this.isBatchEditMode()) {
this._deleteRowCore(rowIndex)
} else {
this.callBase.apply(this, arguments)
}
},
_refreshCore: function(isPageChanged) {
var needResetIndexes = this.isBatchEditMode() || isPageChanged && "virtual" !== this.option("scrolling.mode");
if (this.isCellOrBatchEditMode()) {
if (needResetIndexes) {
this._resetEditColumnName();
this._resetEditRowKey()
}
} else {
this.callBase.apply(this, arguments)
}
},
_allowRowAdding: function(params) {
if (this.isBatchEditMode()) {
return true
}
return this.callBase.apply(this, arguments)
},
_afterDeleteRow: function(rowIndex, oldEditRowIndex) {
var dataController = this._dataController;
if (this.isBatchEditMode()) {
dataController.updateItems({
changeType: "update",
rowIndices: [oldEditRowIndex, rowIndex]
});
return (new Deferred).resolve()
}
return this.callBase.apply(this, arguments)
},
_updateEditRow: function(row, forceUpdateRow, isCustomSetCellValue) {
if (this.isCellOrBatchEditMode()) {
this._updateRowImmediately(row, forceUpdateRow, isCustomSetCellValue)
} else {
this.callBase.apply(this, arguments)
}
},
_isDefaultButtonVisible: function(button, options) {
if (this.isCellOrBatchEditMode()) {
var isBatchMode = this.isBatchEditMode();
switch (button.name) {
case "save":
case "cancel":
case "edit":
return false;
case "delete":
return this.callBase.apply(this, arguments) && (!isBatchMode || !options.row.removed);
case "undelete":
return isBatchMode && this.allowDeleting(options) && options.row.removed;
default:
return this.callBase.apply(this, arguments)
}
}
return this.callBase.apply(this, arguments)
},
_isRowDeleteAllowed: function() {
var callBase = this.callBase.apply(this, arguments);
return callBase || this.isBatchEditMode()
},
_beforeEndSaving: function(changes) {
if (this.isCellEditMode()) {
var _changes$;
if ("update" !== (null === (_changes$ = changes[0]) || void 0 === _changes$ ? void 0 : _changes$.type)) {
this.callBase.apply(this, arguments)
}
} else {
if (this.isBatchEditMode()) {
this._resetModifiedClassCells(changes)
}
this.callBase.apply(this, arguments)
}
},
prepareEditButtons: function(headerPanel) {
var editingOptions = this.option("editing") || {};
var buttonItems = this.callBase.apply(this, arguments);
if ((editingOptions.allowUpdating || editingOptions.allowAdding || editingOptions.allowDeleting) && this.isBatchEditMode()) {
buttonItems.push(this.prepareButtonItem(headerPanel, "save", "saveEditData", 21));
buttonItems.push(this.prepareButtonItem(headerPanel, "revert", "cancelEditData", 22))
}
return buttonItems
},
_applyChange: function(options, params, forceUpdateRow) {
var isUpdateInCellMode = this.isCellEditMode() && options.row && !options.row.isNewRow;
var showEditorAlways = options.column.showEditorAlways;
var isCustomSetCellValue = options.column.setCellValue !== options.column.defaultSetCellValue;
var focusPreviousEditingCell = showEditorAlways && !forceUpdateRow && isUpdateInCellMode && this.hasEditData() && !this.isEditCell(options.rowIndex, options.columnIndex);
if (focusPreviousEditingCell) {
this._focusEditingCell();
this._updateEditRow(options.row, true, isCustomSetCellValue);
return
}
return this.callBase.apply(this, arguments)
},
_applyChangeCore: function(options, forceUpdateRow) {
var showEditorAlways = options.column.showEditorAlways;
var isUpdateInCellMode = this.isCellEditMode() && options.row && !options.row.isNewRow;
if (showEditorAlways && !forceUpdateRow) {
if (isUpdateInCellMode) {
this._setEditRowKey(options.row.key, true);
this._setEditColumnNameByIndex(options.columnIndex, true);
return this.saveEditData()
} else if (this.isBatchEditMode()) {
forceUpdateRow = this._needUpdateRow(options.column);
return this.callBase(options, forceUpdateRow)
}
}
return this.callBase.apply(this, arguments)
},
_processDataItemCore: function(item, _ref3) {
var {
data: data,
type: type
} = _ref3;
if (this.isBatchEditMode() && type === DATA_EDIT_DATA_REMOVE_TYPE) {
item.data = createObjectWithChanges(item.data, data)
}
this.callBase.apply(this, arguments)
},
_processRemoveCore: function(changes, editIndex, processIfBatch) {
if (this.isBatchEditMode() && !processIfBatch) {
return
}
return this.callBase.apply(this, arguments)
},
_processRemoveIfError: function() {
if (this.isBatchEditMode()) {
return
}
return this.callBase.apply(this, arguments)
}
}
},
views: {
rowsView: {
_createTable: function() {
var $table = this.callBase.apply(this, arguments);
var editingController = this._editingController;
if (editingController.isCellOrBatchEditMode() && this.option("editing.allowUpdating")) {
eventsEngine.on($table, addNamespace(holdEvent.name, "dxDataGridRowsView"), "td:not(." + EDITOR_CELL_CLASS + ")", this.createAction(() => {
if (editingController.isEditing()) {
editingController.closeEditCell()
}
}))
}
return $table
},
_createRow: function(row) {
var $row = this.callBase(row);
if (row) {
var editingController = this._editingController;
var isRowRemoved = !!row.removed;
if (editingController.isBatchEditMode()) {
isRowRemoved && $row.addClass(ROW_REMOVED)
}
}
return $row
}
},
headerPanel: {
isVisible: function() {
var editingOptions = this.getController("editing").option("editing");
return this.callBase() || editingOptions && (editingOptions.allowUpdating || editingOptions.allowDeleting) && editingOptions.mode === EDIT_MODE_BATCH
}
}
}
}
};