UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,266 lines • 97 kB
/** * DevExtreme (esm/__internal/grids/grid_core/editing/m_editing.js) * Version: 24.2.7 * Build date: Mon Apr 28 2025 * * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import _extends from "@babel/runtime/helpers/esm/extends"; import { name as clickEventName } from "../../../../common/core/events/click"; import eventsEngine from "../../../../common/core/events/core/events_engine"; import pointerEvents from "../../../../common/core/events/pointer"; import { removeEvent } from "../../../../common/core/events/remove"; import { addNamespace } from "../../../../common/core/events/utils/index"; import messageLocalization from "../../../../common/core/localization/message"; import { createObjectWithChanges } from "../../../../common/data/array_utils"; import devices from "../../../../core/devices"; import domAdapter from "../../../../core/dom_adapter"; import Guid from "../../../../core/guid"; import $ from "../../../../core/renderer"; import { equalByValue, getKeyHash } from "../../../../core/utils/common"; import { Deferred, fromPromise, when } from "../../../../core/utils/deferred"; import { extend } from "../../../../core/utils/extend"; import * as iconUtils from "../../../../core/utils/icon"; import { each } from "../../../../core/utils/iterator"; import { deepExtendArraySafe } from "../../../../core/utils/object"; import { isDefined, isEmptyObject, isFunction, isObject } from "../../../../core/utils/type"; import { confirm } from "../../../../ui/dialog"; import { current, isFluent } from "../../../../ui/themes"; import domUtils from "../../../core/utils/m_dom"; import modules from "../m_modules"; import gridCoreUtils from "../m_utils"; import { ACTION_OPTION_NAMES, BUTTON_NAMES, CELL_BASED_MODES, CELL_FOCUS_DISABLED_CLASS, CELL_MODIFIED, COMMAND_EDIT_CLASS, COMMAND_EDIT_WITH_ICONS_CLASS, DATA_EDIT_DATA_INSERT_TYPE, DATA_EDIT_DATA_REMOVE_TYPE, DATA_EDIT_DATA_UPDATE_TYPE, DEFAULT_START_EDIT_ACTION, EDIT_BUTTON_CLASS, EDIT_FORM_CLASS, EDIT_ICON_CLASS, EDIT_LINK_CLASS, EDIT_MODE_POPUP, EDIT_MODE_ROW, EDIT_MODES, EDITING_CHANGES_OPTION_NAME, EDITING_EDITCOLUMNNAME_OPTION_NAME, EDITING_EDITROWKEY_OPTION_NAME, EDITING_NAMESPACE, EDITING_POPUP_OPTION_NAME, EDITOR_CELL_CLASS, EDITORS_INPUT_SELECTOR, FIRST_NEW_ROW_POSITION, FOCUSABLE_ELEMENT_SELECTOR, INSERT_INDEX, LAST_NEW_ROW_POSITION, LINK_CLASS, LINK_ICON_CLASS, METHOD_NAMES, PAGE_BOTTOM_NEW_ROW_POSITION, PAGE_TOP_NEW_ROW_POSITION, READONLY_CLASS, REQUIRED_EDITOR_LABELLEDBY_MODES, ROW_BASED_MODES, ROW_CLASS, ROW_INSERTED, ROW_MODIFIED, ROW_SELECTED, TARGET_COMPONENT_NAME, VIEWPORT_BOTTOM_NEW_ROW_POSITION, VIEWPORT_TOP_NEW_ROW_POSITION } from "./const"; import { createFailureHandler, generateNewRowTempKey, getButtonIndex, getButtonName, getEditingTexts, isEditingCell, isEditingOrShowEditorAlwaysDataCell } from "./m_editing_utils"; class EditingControllerImpl extends modules.ViewController { init() { this._columnsController = this.getController("columns"); this._dataController = this.getController("data"); this._adaptiveColumnsController = this.getController("adaptiveColumns"); this._validatingController = this.getController("validating"); this._editorFactoryController = this.getController("editorFactory"); this._focusController = this.getController("focus"); this._keyboardNavigationController = this.getController("keyboardNavigation"); this._columnsResizerController = this.getController("columnsResizer"); this._errorHandlingController = this.getController("errorHandling"); this._rowsView = this.getView("rowsView"); this._headerPanelView = this.getView("headerPanel"); this._lastOperation = null; this._changes = []; if (this._deferreds) { this._deferreds.forEach((d => { d.reject("cancel") })) } this._deferreds = []; if (!this._dataChangedHandler) { this._dataChangedHandler = this._handleDataChanged.bind(this); this._dataController.changed.add(this._dataChangedHandler) } if (!this._saveEditorHandler) { this.createAction("onInitNewRow", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onRowInserting", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onRowInserted", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onEditingStart", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onRowUpdating", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onRowUpdated", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onRowRemoving", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onRowRemoved", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onSaved", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onSaving", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onEditCanceling", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onEditCanceled", { excludeValidators: ["disabled", "readOnly"] }) } this._updateEditColumn(); this._updateEditButtons(); if (!this._internalState) { this._internalState = new Map } this.component._optionsByReference[EDITING_EDITROWKEY_OPTION_NAME] = true; this.component._optionsByReference[EDITING_CHANGES_OPTION_NAME] = true } getEditMode() { const editMode = this.option("editing.mode") ?? EDIT_MODE_ROW; if (EDIT_MODES.includes(editMode)) { return editMode } return EDIT_MODE_ROW } isCellBasedEditMode() { const editMode = this.getEditMode(); return CELL_BASED_MODES.includes(editMode) } _getDefaultEditorTemplate() { return (container, options) => { const $editor = $("<div>").appendTo(container); const editorOptions = extend({}, options.column, { value: options.value, setValue: options.setValue, row: options.row, parentType: "dataRow", width: null, readOnly: !options.setValue, isOnForm: options.isOnForm, id: options.id }); const needLabel = REQUIRED_EDITOR_LABELLEDBY_MODES.includes(this.getEditMode()); if (needLabel) { editorOptions["aria-labelledby"] = options.column.headerId } this._editorFactoryController.createEditor($editor, editorOptions) } } _getNewRowPosition() { const newRowPosition = this.option("editing.newRowPosition"); const scrollingMode = this.option("scrolling.mode"); if ("virtual" === scrollingMode) { switch (newRowPosition) { case PAGE_TOP_NEW_ROW_POSITION: return VIEWPORT_TOP_NEW_ROW_POSITION; case PAGE_BOTTOM_NEW_ROW_POSITION: return VIEWPORT_BOTTOM_NEW_ROW_POSITION; default: return newRowPosition } } return newRowPosition } getChanges() { return this.option(EDITING_CHANGES_OPTION_NAME) } getInsertRowCount() { const changes = this.option(EDITING_CHANGES_OPTION_NAME); return changes.filter((change => "insert" === change.type)).length } resetChanges() { const changes = this.getChanges(); const needReset = null === changes || void 0 === changes ? void 0 : changes.length; if (needReset) { this._silentOption(EDITING_CHANGES_OPTION_NAME, []); this._internalState.clear() } } _getInternalData(key) { return this._internalState.get(getKeyHash(key)) } _addInternalData(params) { const internalData = this._getInternalData(params.key); if (internalData) { return extend(internalData, params) } this._internalState.set(getKeyHash(params.key), params); return params } _getOldData(key) { var _this$_getInternalDat; return null === (_this$_getInternalDat = this._getInternalData(key)) || void 0 === _this$_getInternalDat ? void 0 : _this$_getInternalDat.oldData } getUpdatedData(data) { const key = this._dataController.keyOf(data); const changes = this.getChanges(); const editIndex = gridCoreUtils.getIndexByKey(key, changes); if (changes[editIndex]) { return createObjectWithChanges(data, changes[editIndex].data) } return data } getInsertedData() { return this.getChanges().filter((change => change.data && change.type === DATA_EDIT_DATA_INSERT_TYPE)).map((change => change.data)) } getRemovedData() { return this.getChanges().filter((change => this._getOldData(change.key) && change.type === DATA_EDIT_DATA_REMOVE_TYPE)).map((change => this._getOldData(change.key))) } _fireDataErrorOccurred(arg) { if ("cancel" === arg) { return } const $popupContent = this.getPopupContent(); this._dataController.dataErrorOccurred.fire(arg, $popupContent) } _needToCloseEditableCell($targetElement) {} _closeEditItem($targetElement) {} _handleDataChanged(args) {} _isDefaultButtonVisible(button, options) { let result = true; switch (button.name) { case "delete": result = this.allowDeleting(options); break; case "undelete": result = false } return result } isPopupEditMode() { const editMode = this.option("editing.mode"); return editMode === EDIT_MODE_POPUP } _isButtonVisible(button, options) { const { visible: visible } = button; if (!isDefined(visible)) { return this._isDefaultButtonVisible(button, options) } return isFunction(visible) ? visible.call(button, { component: options.component, row: options.row, column: options.column }) : visible } _isButtonDisabled(button, options) { const { disabled: disabled } = button; return isFunction(disabled) ? disabled.call(button, { component: options.component, row: options.row, column: options.column }) : !!disabled } _getButtonConfig(button, options) { const config = isObject(button) ? button : {}; const buttonName = getButtonName(button); const editingTexts = getEditingTexts(options); const methodName = METHOD_NAMES[buttonName]; const editingOptions = this.option("editing"); const actionName = ACTION_OPTION_NAMES[buttonName]; const allowAction = actionName ? editingOptions[actionName] : true; return extend({ name: buttonName, text: editingTexts[buttonName], cssClass: EDIT_LINK_CLASS[buttonName] }, { onClick: methodName && (e => { const { event: event } = e; event.stopPropagation(); event.preventDefault(); setTimeout((() => { options.row && allowAction && this[methodName] && this[methodName](options.row.rowIndex) })) }) }, config) } _getEditingButtons(options) { let buttonIndex; const haveCustomButtons = !!options.column.buttons; let buttons = (options.column.buttons || []).slice(); if (haveCustomButtons) { buttonIndex = getButtonIndex(buttons, "edit"); if (buttonIndex >= 0) { if (getButtonIndex(buttons, "save") < 0) { buttons.splice(buttonIndex + 1, 0, "save") } if (getButtonIndex(buttons, "cancel") < 0) { buttons.splice(getButtonIndex(buttons, "save") + 1, 0, "cancel") } } buttonIndex = getButtonIndex(buttons, "delete"); if (buttonIndex >= 0 && getButtonIndex(buttons, "undelete") < 0) { buttons.splice(buttonIndex + 1, 0, "undelete") } } else { buttons = BUTTON_NAMES.slice() } return buttons.map((button => this._getButtonConfig(button, options))) } _renderEditingButtons($container, buttons, options, change) { buttons.forEach((button => { if (this._isButtonVisible(button, options)) { this._createButton($container, button, options, change) } })) } _getEditCommandCellTemplate() { return (container, options, change) => { const $container = $(container); if ("data" === options.rowType) { const buttons = this._getEditingButtons(options); this._renderEditingButtons($container, buttons, options, change); if (options.watch) { const dispose = options.watch((() => buttons.map((button => ({ visible: this._isButtonVisible(button, options), disabled: this._isButtonDisabled(button, options) })))), (() => { $container.empty(); this._renderEditingButtons($container, buttons, options) })); eventsEngine.on($container, removeEvent, dispose) } } else { gridCoreUtils.setEmptyText($container) } } } isRowBasedEditMode() { const editMode = this.getEditMode(); return ROW_BASED_MODES.includes(editMode) } getFirstEditableColumnIndex() { let columnIndex; const visibleColumns = this._columnsController.getVisibleColumns(); each(visibleColumns, ((index, column) => { if (column.allowEditing) { columnIndex = index; return false } })); return columnIndex } getFirstEditableCellInRow(rowIndex) { var _this$_rowsView; const columnIndex = this.getFirstEditableColumnIndex(); return null === (_this$_rowsView = this._rowsView) || void 0 === _this$_rowsView ? void 0 : _this$_rowsView._getCellElement(rowIndex || 0, columnIndex) } getFocusedCellInRow(rowIndex) { return this.getFirstEditableCellInRow(rowIndex) } getIndexByKey(key, items) { return gridCoreUtils.getIndexByKey(key, items) } hasChanges(rowIndex) { const changes = this.getChanges(); let result = false; for (let i = 0; i < (null === changes || void 0 === changes ? void 0 : changes.length); i++) { if (changes[i].type && (!isDefined(rowIndex) || this._dataController.getRowIndexByKey(changes[i].key) === rowIndex)) { result = true; break } } return result } dispose() { super.dispose(); clearTimeout(this._inputFocusTimeoutID); eventsEngine.off(domAdapter.getDocument(), pointerEvents.up, this._pointerUpEditorHandler); eventsEngine.off(domAdapter.getDocument(), pointerEvents.down, this._pointerDownEditorHandler); eventsEngine.off(domAdapter.getDocument(), clickEventName, this._saveEditorHandler) } _silentOption(name, value) { if ("editing.changes" === name) { this._changes = deepExtendArraySafe([], value) } super._silentOption(name, value) } optionChanged(args) { if ("editing" === args.name) { const { fullName: fullName } = args; if (fullName === EDITING_EDITROWKEY_OPTION_NAME) { this._handleEditRowKeyChange(args) } else if (fullName === EDITING_CHANGES_OPTION_NAME) { const isEqual = equalByValue(args.value, this._changes, { maxDepth: 4 }); if (!isEqual) { this._changes = deepExtendArraySafe([], args.value); this._handleChangesChange(args) } } else if (!args.handled) { this._columnsController.reinit(); this.init(); this.resetChanges(); this._resetEditColumnName(); this._resetEditRowKey() } args.handled = true } else { super.optionChanged(args) } } _handleEditRowKeyChange(args) { const rowIndex = this._dataController.getRowIndexByKey(args.value); const oldRowIndexCorrection = this._getEditRowIndexCorrection(); const oldRowIndex = this._dataController.getRowIndexByKey(args.previousValue) + oldRowIndexCorrection; if (isDefined(args.value)) { if (args.value !== args.previousValue) { this._editRowFromOptionChanged(rowIndex, oldRowIndex) } } else { this.cancelEditData() } } _handleChangesChange(args) { const dataController = this._dataController; const changes = args.value; if (!args.value.length && !args.previousValue.length) { return } changes.forEach((change => { if ("insert" === change.type) { this._addInsertInfo(change) } else { var _dataController$items; const items = dataController.getCachedStoreData() || (null === (_dataController$items = dataController.items()) || void 0 === _dataController$items ? void 0 : _dataController$items.map((item => item.data))); const rowIndex = gridCoreUtils.getIndexByKey(change.key, items, dataController.key()); this._addInternalData({ key: change.key, oldData: items[rowIndex] }) } })); dataController.updateItems({ repaintChangesOnly: true, isLiveUpdate: false, isOptionChanged: true }) } publicMethods() { return ["addRow", "deleteRow", "undeleteRow", "editRow", "saveEditData", "cancelEditData", "hasEditData"] } refresh() { if (!isDefined(this._pageIndex)) { return } this._refreshCore.apply(this, arguments) } _refreshCore(params) {} isEditing() { const isEditRowKeyDefined = isDefined(this.option(EDITING_EDITROWKEY_OPTION_NAME)); return isEditRowKeyDefined } isEditRow(rowIndex) { return false } _setEditRowKey(value, silent) { if (silent) { this._silentOption(EDITING_EDITROWKEY_OPTION_NAME, value) } else { this.option(EDITING_EDITROWKEY_OPTION_NAME, value) } if (this._refocusEditCell) { this._refocusEditCell = false; this._focusEditingCell() } } _setEditRowKeyByIndex(rowIndex, silent) { const key = this._dataController.getKeyByRowIndex(rowIndex); if (void 0 === key) { this._dataController.fireError("E1043"); return } this._setEditRowKey(key, silent) } getEditRowIndex() { return this._getVisibleEditRowIndex() } getEditFormRowIndex() { return -1 } isEditRowByIndex(rowIndex) { const key = this._dataController.getKeyByRowIndex(rowIndex); const isKeyEqual = isDefined(key) && equalByValue(this.option(EDITING_EDITROWKEY_OPTION_NAME), key); if (isKeyEqual) { return this._getVisibleEditRowIndex() === rowIndex } return isKeyEqual } isEditCell(visibleRowIndex, columnIndex) { return this.isEditRowByIndex(visibleRowIndex) && this._getVisibleEditColumnIndex() === columnIndex } getPopupContent() {} _isProcessedItem(item) { return false } _getInsertRowIndex(items, change, isProcessedItems) { let result = -1; const dataController = this._dataController; const key = this._getInsertAfterOrBeforeKey(change); if (!isDefined(key) && 0 === items.length) { result = 0 } else if (isDefined(key)) { items.some(((item, index) => { const isProcessedItem = isProcessedItems || this._isProcessedItem(item); if (isObject(item)) { if (isProcessedItem || isDefined(item[INSERT_INDEX])) { if (equalByValue(item.key, key)) { result = index } } else if (equalByValue(dataController.keyOf(item), key)) { result = index } } if (result >= 0) { const nextItem = items[result + 1]; if (nextItem && ("detail" === nextItem.rowType || "detailAdaptive" === nextItem.rowType) && isDefined(change.insertAfterKey)) { return } if (isDefined(change.insertAfterKey)) { result += 1 } return true } })) } return result } _generateNewItem(key) { var _this$_getInternalDat2; const item = { key: key }; const insertInfo = null === (_this$_getInternalDat2 = this._getInternalData(key)) || void 0 === _this$_getInternalDat2 ? void 0 : _this$_getInternalDat2.insertInfo; if (null !== insertInfo && void 0 !== insertInfo && insertInfo[INSERT_INDEX]) { item[INSERT_INDEX] = insertInfo[INSERT_INDEX] } return item } _getLoadedRowIndex(items, change, isProcessedItems) { let loadedRowIndex = this._getInsertRowIndex(items, change, isProcessedItems); const dataController = this._dataController; if (loadedRowIndex < 0) { const newRowPosition = this._getNewRowPosition(); const pageIndex = dataController.pageIndex(); const insertAfterOrBeforeKey = this._getInsertAfterOrBeforeKey(change); if (newRowPosition !== LAST_NEW_ROW_POSITION && 0 === pageIndex && !isDefined(insertAfterOrBeforeKey)) { loadedRowIndex = 0 } else if (newRowPosition === LAST_NEW_ROW_POSITION && dataController.isLastPageLoaded()) { loadedRowIndex = items.length } } return loadedRowIndex } processItems(items, e) { const { changeType: changeType } = e; this.update(changeType); const changes = this.getChanges(); changes.forEach((change => { var _this$_getInternalDat3; const isInsert = change.type === DATA_EDIT_DATA_INSERT_TYPE; if (!isInsert) { return } let { key: key } = change; let insertInfo = null === (_this$_getInternalDat3 = this._getInternalData(key)) || void 0 === _this$_getInternalDat3 ? void 0 : _this$_getInternalDat3.insertInfo; if (!isDefined(key) || !isDefined(insertInfo)) { insertInfo = this._addInsertInfo(change); key = insertInfo.key } const loadedRowIndex = this._getLoadedRowIndex(items, change); const item = this._generateNewItem(key); if (loadedRowIndex >= 0) { items.splice(loadedRowIndex, 0, item) } })); return items } processDataItem(item, options, generateDataValues) { const columns = options.visibleColumns; const key = item.data[INSERT_INDEX] ? item.data.key : item.key; const changes = this.getChanges(); const editIndex = gridCoreUtils.getIndexByKey(key, changes); item.isEditing = false; if (editIndex >= 0) { this._processDataItemCore(item, changes[editIndex], key, columns, generateDataValues) } } _processDataItemCore(item, change, key, columns, generateDataValues) { const { data: data, type: type } = change; switch (type) { case DATA_EDIT_DATA_INSERT_TYPE: item.isNewRow = true; item.key = key; item.data = data; break; case DATA_EDIT_DATA_UPDATE_TYPE: item.modified = true; item.oldData = item.data; item.data = createObjectWithChanges(item.data, data); item.modifiedValues = generateDataValues(data, columns, true); break; case DATA_EDIT_DATA_REMOVE_TYPE: item.removed = true } } _initNewRow(options) { this.executeAction("onInitNewRow", options); if (options.promise) { const deferred = new Deferred; when(fromPromise(options.promise)).done(deferred.resolve).fail(createFailureHandler(deferred)).fail((arg => this._fireDataErrorOccurred(arg))); return deferred } } _createInsertInfo() { const insertInfo = {}; insertInfo[INSERT_INDEX] = this._getInsertIndex(); return insertInfo } _addInsertInfo(change, parentKey) { var _this$_getInternalDat4; let insertInfo; change.key = this.getChangeKeyValue(change); const { key: key } = change; insertInfo = null === (_this$_getInternalDat4 = this._getInternalData(key)) || void 0 === _this$_getInternalDat4 ? void 0 : _this$_getInternalDat4.insertInfo; if (!isDefined(insertInfo)) { const insertAfterOrBeforeKey = this._getInsertAfterOrBeforeKey(change); insertInfo = this._createInsertInfo(); if (!isDefined(insertAfterOrBeforeKey)) { this._setInsertAfterOrBeforeKey(change, parentKey) } } this._addInternalData({ insertInfo: insertInfo, key: key }); return { insertInfo: insertInfo, key: key } } getChangeKeyValue(change) { if (isDefined(change.key)) { return change.key } const keyExpr = this._dataController.key(); let keyValue; if (change.data && keyExpr && !Array.isArray(keyExpr)) { keyValue = change.data[keyExpr] } if (!isDefined(keyValue)) { keyValue = generateNewRowTempKey() } return keyValue } _setInsertAfterOrBeforeKey(change, parentKey) { const rowsView = this.getView("rowsView"); const dataController = this._dataController; const allItems = dataController.items(true); const newRowPosition = this._getNewRowPosition(); switch (newRowPosition) { case FIRST_NEW_ROW_POSITION: case LAST_NEW_ROW_POSITION: break; case PAGE_TOP_NEW_ROW_POSITION: case PAGE_BOTTOM_NEW_ROW_POSITION: if (allItems.length) { const itemIndex = newRowPosition === PAGE_TOP_NEW_ROW_POSITION ? 0 : allItems.length - 1; change[0 === itemIndex ? "insertBeforeKey" : "insertAfterKey"] = allItems[itemIndex].key } break; default: { const isViewportBottom = newRowPosition === VIEWPORT_BOTTOM_NEW_ROW_POSITION; let visibleItemIndex = isViewportBottom ? null === rowsView || void 0 === rowsView ? void 0 : rowsView.getBottomVisibleItemIndex() : null === rowsView || void 0 === rowsView ? void 0 : rowsView.getTopVisibleItemIndex(); const row = dataController.getVisibleRows()[visibleItemIndex]; if (row && (!row.isEditing && "detail" === row.rowType || "detailAdaptive" === row.rowType)) { visibleItemIndex++ } const insertKey = dataController.getKeyByRowIndex(visibleItemIndex); if (isDefined(insertKey)) { change.insertBeforeKey = insertKey } } } } _getInsertIndex() { let maxInsertIndex = 0; this.getChanges().forEach((editItem => { var _this$_getInternalDat5; const insertInfo = null === (_this$_getInternalDat5 = this._getInternalData(editItem.key)) || void 0 === _this$_getInternalDat5 ? void 0 : _this$_getInternalDat5.insertInfo; if (isDefined(insertInfo) && editItem.type === DATA_EDIT_DATA_INSERT_TYPE && insertInfo[INSERT_INDEX] > maxInsertIndex) { maxInsertIndex = insertInfo[INSERT_INDEX] } })); return maxInsertIndex + 1 } _getInsertAfterOrBeforeKey(insertChange) { return insertChange.insertAfterKey ?? insertChange.insertBeforeKey } _getPageIndexToInsertRow() { const newRowPosition = this._getNewRowPosition(); const dataController = this._dataController; const pageIndex = dataController.pageIndex(); const lastPageIndex = dataController.pageCount() - 1; if (newRowPosition === FIRST_NEW_ROW_POSITION && 0 !== pageIndex) { return 0 } if (newRowPosition === LAST_NEW_ROW_POSITION && pageIndex !== lastPageIndex) { return lastPageIndex } return -1 } addRow(parentKey) { const dataController = this._dataController; const store = dataController.store(); if (!store) { dataController.fireError("E1052", this.component.NAME); return (new Deferred).reject() } return this._addRow(parentKey) } _addRow(parentKey) { const dataController = this._dataController; const store = dataController.store(); const key = store && store.key(); const param = { data: {} }; const oldEditRowIndex = this._getVisibleEditRowIndex(); const deferred = new Deferred; this.refresh({ allowCancelEditing: true }); if (!this._allowRowAdding()) { when(this._navigateToNewRow(oldEditRowIndex)).done(deferred.resolve).fail(deferred.reject); return deferred.promise() } if (!key) { param.data.__KEY__ = String(new Guid) } when(this._initNewRow(param, parentKey)).done((() => { if (this._allowRowAdding()) { when(this._addRowCore(param.data, parentKey, oldEditRowIndex)).done(deferred.resolve).fail(deferred.reject) } else { deferred.reject("cancel") } })).fail(deferred.reject); return deferred.promise() } _allowRowAdding(params) { const insertIndex = this._getInsertIndex(); if (insertIndex > 1) { return false } return true } _addRowCore(data, parentKey, initialOldEditRowIndex) { const change = { data: data, type: DATA_EDIT_DATA_INSERT_TYPE }; const editRowIndex = this._getVisibleEditRowIndex(); const insertInfo = this._addInsertInfo(change, parentKey); const { key: key } = insertInfo; this._setEditRowKey(key, true); this._addChange(change); return this._navigateToNewRow(initialOldEditRowIndex, change, editRowIndex) } _navigateToNewRow(oldEditRowIndex, change, editRowIndex) { const d = new Deferred; const dataController = this._dataController; editRowIndex = editRowIndex ?? -1; change = change ?? this.getChanges().filter((c => c.type === DATA_EDIT_DATA_INSERT_TYPE))[0]; if (!change) { return d.reject("cancel").promise() } const pageIndexToInsertRow = this._getPageIndexToInsertRow(); let rowIndex = this._getLoadedRowIndex(dataController.items(), change, true); const navigateToRowByKey = key => { var _this$_focusControlle; when(null === (_this$_focusControlle = this._focusController) || void 0 === _this$_focusControlle ? void 0 : _this$_focusControlle.navigateToRow(key)).done((() => { rowIndex = dataController.getRowIndexByKey(change.key); d.resolve() })) }; const insertAfterOrBeforeKey = this._getInsertAfterOrBeforeKey(change); if (pageIndexToInsertRow >= 0) { dataController.pageIndex(pageIndexToInsertRow).done((() => { navigateToRowByKey(change.key) })).fail(d.reject) } else if (rowIndex < 0 && isDefined(insertAfterOrBeforeKey)) { navigateToRowByKey(insertAfterOrBeforeKey) } else { dataController.updateItems({ changeType: "update", rowIndices: [oldEditRowIndex, editRowIndex, rowIndex] }); rowIndex = dataController.getRowIndexByKey(change.key); if (rowIndex < 0) { navigateToRowByKey(change.key) } else { d.resolve() } } d.done((() => { var _this$_rowsView2; null === (_this$_rowsView2 = this._rowsView) || void 0 === _this$_rowsView2 || _this$_rowsView2.waitAsyncTemplates(true).done((() => { this._showAddedRow(rowIndex); this._afterInsertRow(change.key) })) })); return d.promise() } _showAddedRow(rowIndex) { this._focusFirstEditableCellInRow(rowIndex) } _beforeFocusElementInRow(rowIndex) {} _focusFirstEditableCellInRow(rowIndex) { var _this$_keyboardNaviga; const dataController = this._dataController; const key = dataController.getKeyByRowIndex(rowIndex); const $firstCell = this.getFirstEditableCellInRow(rowIndex); null === (_this$_keyboardNaviga = this._keyboardNavigationController) || void 0 === _this$_keyboardNaviga || _this$_keyboardNaviga.focus($firstCell); this.option("focusedRowKey", key); this._editCellInProgress = true; this._delayedInputFocus($firstCell, (() => { rowIndex = dataController.getRowIndexByKey(key); this._editCellInProgress = false; this._beforeFocusElementInRow(rowIndex) })) } _isEditingStart(options) { this.executeAction("onEditingStart", options); return options.cancel } _beforeUpdateItems(rowIndices, rowIndex) {} _getVisibleEditColumnIndex() { const editColumnName = this.option(EDITING_EDITCOLUMNNAME_OPTION_NAME); if (!isDefined(editColumnName)) { return -1 } return this._columnsController.getVisibleColumnIndex(editColumnName) } _setEditColumnNameByIndex(index, silent) { var _visibleColumns$index; const visibleColumns = this._columnsController.getVisibleColumns(); this._setEditColumnName(null === (_visibleColumns$index = visibleColumns[index]) || void 0 === _visibleColumns$index ? void 0 : _visibleColumns$index.name, silent) } _setEditColumnName(name, silent) { if (silent) { this._silentOption(EDITING_EDITCOLUMNNAME_OPTION_NAME, name) } else { this.option(EDITING_EDITCOLUMNNAME_OPTION_NAME, name) } } _resetEditColumnName() { this._setEditColumnName(null, true) } _getEditColumn() { const editColumnName = this.option(EDITING_EDITCOLUMNNAME_OPTION_NAME); return this._getColumnByName(editColumnName) } _getColumnByName(name) { const visibleColumns = this._columnsController.getVisibleColumns(); let editColumn; isDefined(name) && visibleColumns.some((column => { if (column.name === name) { editColumn = column; return true } })); return editColumn } _getVisibleEditRowIndex(columnName) { const dataController = this._dataController; const editRowKey = this.option(EDITING_EDITROWKEY_OPTION_NAME); const rowIndex = dataController.getRowIndexByKey(editRowKey); if (-1 === rowIndex) { return rowIndex } return rowIndex + this._getEditRowIndexCorrection(columnName) } _getEditRowIndexCorrection(columnName) { const editColumn = columnName ? this._getColumnByName(columnName) : this._getEditColumn(); const isColumnHidden = "adaptiveHidden" === (null === editColumn || void 0 === editColumn ? void 0 : editColumn.visibleWidth); return isColumnHidden ? 1 : 0 } _resetEditRowKey() { this._refocusEditCell = false; this._setEditRowKey(null, true) } _resetEditIndices() { this._resetEditColumnName(); this._resetEditRowKey() } editRow(rowIndex) { const dataController = this._dataController; const items = dataController.items(); const item = items[rowIndex]; const params = { data: item && item.data, cancel: false }; const oldRowIndex = this._getVisibleEditRowIndex(); if (!item) { return } if (rowIndex === oldRowIndex) { return true } if (void 0 === item.key) { this._dataController.fireError("E1043"); return } if (!item.isNewRow) { params.key = item.key } if (this._isEditingStart(params)) { return } this.resetChanges(); this.init(); this._resetEditColumnName(); this._pageIndex = dataController.pageIndex(); this._addInternalData({ key: item.key, oldData: item.oldData ?? item.data }); this._setEditRowKey(item.key) } _editRowFromOptionChanged(rowIndex, oldRowIndex) { const rowIndices = [oldRowIndex, rowIndex]; this._beforeUpdateItems(rowIndices, rowIndex, oldRowIndex); this._editRowFromOptionChangedCore(rowIndices, rowIndex) } _editRowFromOptionChangedCore(rowIndices, rowIndex, preventRendering) { this._needFocusEditor = true; this._dataController.updateItems({ changeType: "update", rowIndices: rowIndices, cancel: preventRendering }) } _focusEditorIfNeed() {} _showEditPopup(rowIndex, repaintForm) {} _repaintEditPopup() {} _getEditPopupHiddenHandler() { return e => { if (this.isEditing()) { this.cancelEditData() } } } _getPopupEditFormTemplate(rowIndex) {} _getSaveButtonConfig() { const buttonConfig = { text: this.option("editing.texts.saveRowChanges"), onClick: this.saveEditData.bind(this) }; if (isFluent(current())) { buttonConfig.stylingMode = "contained"; buttonConfig.type = "default" } return buttonConfig } _getCancelButtonConfig() { const buttonConfig = { text: this.option("editing.texts.cancelRowChanges"), onClick: this.cancelEditData.bind(this) }; if (isFluent(current())) { buttonConfig.stylingMode = "outlined" } return buttonConfig } _removeInternalData(key) { this._internalState.delete(getKeyHash(key)) } _updateInsertAfterOrBeforeKeys(changes, index) { const removeChange = changes[index]; changes.forEach((change => { if (change.type === DATA_EDIT_DATA_INSERT_TYPE) { const insertAfterOrBeforeKey = this._getInsertAfterOrBeforeKey(change); if (equalByValue(insertAfterOrBeforeKey, removeChange.key)) { change[isDefined(change.insertAfterKey) ? "insertAfterKey" : "insertBeforeKey"] = this._getInsertAfterOrBeforeKey(removeChange) } } })) } _removeChange(index) { if (index >= 0) { const changes = [...this.getChanges()]; const { key: key } = changes[index]; this._removeInternalData(key); this._updateInsertAfterOrBeforeKeys(changes, index); changes.splice(index, 1); this._silentOption(EDITING_CHANGES_OPTION_NAME, changes); if (equalByValue(this.option(EDITING_EDITROWKEY_OPTION_NAME), key)) { this._resetEditIndices() } } } executeOperation(deferred, func) { this._lastOperation && this._lastOperation.reject(); this._lastOperation = deferred; this.waitForDeferredOperations().done((() => { if ("rejected" === deferred.state()) { return } func(); this._lastOperation = null })).fail((() => { deferred.reject(); this._lastOperation = null })) } waitForDeferredOperations() { return when(...this._deferreds) } _processCanceledEditingCell() {} _repaintEditCell(column, oldColumn, oldEditRowIndex) { if (!column || !column.showEditorAlways || oldColumn && !oldColumn.showEditorAlways) { this._editCellInProgress = true; this._needFocusEditor = true; this._editorFactoryController.loseFocus(); this._dataController.updateItems({ changeType: "update", rowIndices: [oldEditRowIndex, this._getVisibleEditRowIndex()] }) } else if (column !== oldColumn) { this._needFocusEditor = true; this._dataController.updateItems({ changeType: "update", rowIndices: [] }) } } _delayedInputFocus($cell, beforeFocusCallback, callBeforeFocusCallbackAlways) { const inputFocus = () => { if (beforeFocusCallback) { beforeFocusCallback() } if ($cell) { const $focusableElement = $cell.find(FOCUSABLE_ELEMENT_SELECTOR).first(); gridCoreUtils.focusAndSelectElement(this, $focusableElement) } this._beforeFocusCallback = null }; if (devices.real().ios || devices.real().android) { inputFocus() } else { if (this._beforeFocusCallback) { this._beforeFocusCallback() } clearTimeout(this._inputFocusTimeoutID); if (callBeforeFocusCallbackAlways) { this._beforeFocusCallback = beforeFocusCallback } this._inputFocusTimeoutID = setTimeout(inputFocus) } } _focusEditingCell(beforeFocusCallback, $editCell, callBeforeFocusCallbackAlways) { const editColumnIndex = this._getVisibleEditColumnIndex(); $editCell = $editCell || this._rowsView && this._rowsView._getCellElement(this._getVisibleEditRowIndex(), editColumnIndex); if ($editCell) { this._delayedInputFocus($editCell, beforeFocusCallback, callBeforeFocusCallbackAlways) } } deleteRow(rowIndex) { this._checkAndDeleteRow(rowIndex) } _checkAndDeleteRow(rowIndex) { const editingOptions = this.option("editing"); const editingTexts = null === editingOptions || void 0 === editingOptions ? void 0 : editingOptions.texts; const confirmDelete = null === editingOptions || void 0 === editingOptions ? void 0 : editingOptions.confirmDelete; const confirmDeleteMessage = null === editingTexts || void 0 === editingTexts ? void 0 : editingTexts.confirmDeleteMessage; const item = this._dataController.items()[rowIndex]; const allowDeleting = !this.isEditing() || item.isNewRow; if (item && allowDeleting) { if (!confirmDelete || !confirmDeleteMessage) { this._deleteRowCore(rowIndex) } else { const confirmDeleteTitle = editingTexts && editingTexts.confirmDeleteTitle; const showDialogTitle = isDefined(confirmDeleteTitle) && confirmDeleteTitle.length > 0; confirm(confirmDeleteMessage, confirmDeleteTitle, showDialogTitle).done((confirmResult => { if (confirmResult) { this._deleteRowCore(rowIndex) } })) } } } _deleteRowCore(rowIndex) { const dataController = this._dataController; const item = dataController.items()[rowIndex]; const key = item && item.key; const oldEditRowIndex = this._getVisibleEditRowIndex(); this.refresh(); const changes = this.getChanges(); const editIndex = gridCoreUtils.getIndexByKey(key, changes); if (editIndex >= 0) { if (changes[editIndex].type === DATA_EDIT_DATA_INSERT_TYPE) { this._removeChange(editIndex) } else { this._addChange({ key: key, type: DATA_EDIT_DATA_REMOVE_TYPE }) } } else { this._addChange({ key: key, oldData: item.data, type: DATA_EDIT_DATA_REMOVE_TYPE }) } return this._afterDeleteRow(rowIndex, oldEditRowIndex) } _afterDeleteRow(rowIndex, oldEditRowIndex) { return this.saveEditData() } undeleteRow(rowIndex) { const dataController = this._dataController; const item = dataController.items()[rowIndex]; const oldEditRowIndex = this._getVisibleEditRowIndex(); const key = item && item.key; const changes = this.getChanges(); if (item) { const editIndex = gridCoreUtils.getIndexByKey(key, changes); if (editIndex >= 0) { const { data: data } = changes[editIndex]; if (isEmptyObject(data)) { this._removeChange(editIndex) } else { this._addChange({ key: key, type: DATA_EDIT_DATA_UPDATE_TYPE }) } dataController.updateItems({ changeType: "update", rowIndices: [oldEditRowIndex, rowIndex] }) } } } _fireOnSaving() { const onSavingParams = { cancel: false, promise: null, changes: [...this.getChanges()] }; this.executeAction("onSaving", onSavingParams); const d = new Deferred; when(fromPromise(onSavingParams.promise)).done((() => { d.resolve(onSavingParams) })).fail((arg => { createFailureHandler(d); this._fireDataErrorOccurred(arg); d.resolve({ cancel: true }) })); return d } _executeEditingAction(actionName, params, func) { if (this.component._disposed) { return null } const deferred = new Deferred; this.executeAction(actionName, params); when(fromPromise(params.cancel)).do