UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,092 lines (1,081 loc) • 91.8 kB
/** * DevExtreme (esm/ui/grid_core/ui.grid_core.keyboard_navigation.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 eventsEngine from "../../events/core/events_engine"; import core from "./ui.grid_core.modules"; import gridCoreUtils from "./ui.grid_core.utils"; import { isDefined, isEmptyObject } from "../../core/utils/type"; import { inArray } from "../../core/utils/array"; import { focused } from "../widget/selectors"; import { addNamespace, createEvent, isCommandKeyPressed } from "../../events/utils/index"; import pointerEvents from "../../events/pointer"; import { name as clickEventName } from "../../events/click"; import { noop } from "../../core/utils/common"; import * as accessibility from "../shared/accessibility"; import browser from "../../core/utils/browser"; import { keyboard } from "../../events/short"; import devices from "../../core/devices"; var ROWS_VIEW_CLASS = "rowsview"; var EDIT_FORM_CLASS = "edit-form"; var GROUP_FOOTER_CLASS = "group-footer"; var ROW_CLASS = "dx-row"; var DATA_ROW_CLASS = "dx-data-row"; var GROUP_ROW_CLASS = "dx-group-row"; var HEADER_ROW_CLASS = "dx-header-row"; var EDIT_FORM_ITEM_CLASS = "edit-form-item"; var MASTER_DETAIL_ROW_CLASS = "dx-master-detail-row"; var FREESPACE_ROW_CLASS = "dx-freespace-row"; var VIRTUAL_ROW_CLASS = "dx-virtual-row"; var MASTER_DETAIL_CELL_CLASS = "dx-master-detail-cell"; var EDITOR_CELL_CLASS = "dx-editor-cell"; var DROPDOWN_EDITOR_OVERLAY_CLASS = "dx-dropdowneditor-overlay"; var COMMAND_EXPAND_CLASS = "dx-command-expand"; var COMMAND_SELECT_CLASS = "dx-command-select"; var COMMAND_EDIT_CLASS = "dx-command-edit"; var COMMAND_CELL_SELECTOR = "[class^=dx-command]"; var CELL_FOCUS_DISABLED_CLASS = "dx-cell-focus-disabled"; var DATEBOX_WIDGET_NAME = "dxDateBox"; var FOCUS_STATE_CLASS = "dx-state-focused"; var WIDGET_CLASS = "dx-widget"; var REVERT_BUTTON_CLASS = "dx-revert-button"; var FAST_EDITING_DELETE_KEY = "delete"; var INTERACTIVE_ELEMENTS_SELECTOR = "input:not([type='hidden']), textarea, a, select, button, [tabindex], .dx-dropdowneditor-icon"; var EDIT_MODE_ROW = "row"; var EDIT_MODE_FORM = "form"; var EDIT_MODE_BATCH = "batch"; var EDIT_MODE_CELL = "cell"; var FOCUS_TYPE_ROW = "row"; var FOCUS_TYPE_CELL = "cell"; var COLUMN_HEADERS_VIEW = "columnHeadersView"; function isGroupRow($row) { return $row && $row.hasClass(GROUP_ROW_CLASS) } function isDetailRow($row) { return $row && $row.hasClass(MASTER_DETAIL_ROW_CLASS) } function isDataRow($row) { return $row && !isGroupRow($row) && !isDetailRow($row) } function isNotFocusedRow($row) { return !$row || $row.hasClass(FREESPACE_ROW_CLASS) || $row.hasClass(VIRTUAL_ROW_CLASS) } function isCellElement($element) { return $element.length && "TD" === $element[0].tagName } function isEditorCell(that, $cell) { return !that._isRowEditMode() && $cell && !$cell.hasClass(COMMAND_SELECT_CLASS) && $cell.hasClass(EDITOR_CELL_CLASS) } function isElementDefined($element) { return isDefined($element) && $element.length > 0 } function isMobile() { return "desktop" !== devices.current().deviceType } function isCellInHeaderRow($cell) { return !!$cell.parent(".".concat(HEADER_ROW_CLASS)).length } function isFixedColumnIndexOffsetRequired(that, column) { var rtlEnabled = that.option("rtlEnabled"); var result = false; if (rtlEnabled) { result = !("right" === column.fixedPosition || isDefined(column.command) && !isDefined(column.fixedPosition)) } else { result = !(!isDefined(column.fixedPosition) || "left" === column.fixedPosition) } return result } function shouldPreventScroll(that) { var keyboardController = that.getController("keyboardNavigation"); return keyboardController._isVirtualScrolling() ? that.option("focusedRowIndex") === keyboardController.getRowIndex() : false } var KeyboardNavigationController = core.ViewController.inherit({ init: function() { this._dataController = this.getController("data"); this._selectionController = this.getController("selection"); this._editingController = this.getController("editing"); this._headerPanel = this.getView("headerPanel"); this._columnsController = this.getController("columns"); this._editorFactory = this.getController("editorFactory"); if (this.isKeyboardEnabled()) { accessibility.subscribeVisibilityChange(); this._updateFocusTimeout = null; this._fastEditingStarted = false; this._focusedCellPosition = {}; this._canceledCellPosition = null; this._editorFactory.focused.add($element => { this.setupFocusedView(); if (this._isNeedScroll) { if ($element.is(":visible") && this._focusedView && this._focusedView.getScrollable) { this._focusedView._scrollToElement($element); this._isNeedScroll = false } } }); this._initViewHandlers(); this._initDocumentHandlers(); this.createAction("onKeyDown") } }, _initViewHandlers: function() { var rowsView = this.getView("rowsView"); var rowsViewFocusHandler = event => { var $element = $(event.target); var isRelatedTargetInRowsView = $(event.relatedTarget).closest(rowsView.element()).length; var isCommandButton = $element.hasClass("dx-link"); if (isCommandButton && !isRelatedTargetInRowsView && this._isEventInCurrentGrid(event)) { var $focusedCell = this._getFocusedCell(); $focusedCell = !isElementDefined($focusedCell) ? rowsView.getCellElements(0).filter("[tabindex]").eq(0) : $focusedCell; if (!$element.closest($focusedCell).length) { event.preventDefault(); eventsEngine.trigger($focusedCell, "focus") } } }; rowsView.renderCompleted.add(e => { var $rowsView = rowsView.element(); var isFullUpdate = !e || "refresh" === e.changeType; var isFocusedViewCorrect = this._focusedView && this._focusedView.name === rowsView.name; var needUpdateFocus = false; var isAppend = e && ("append" === e.changeType || "prepend" === e.changeType); var $focusedElement = $(":focus"); var isFocusedElementCorrect = !$focusedElement.length || $focusedElement.closest($rowsView).length || browser.msie && $focusedElement.is("body"); eventsEngine.off($rowsView, "focusin", rowsViewFocusHandler); eventsEngine.on($rowsView, "focusin", rowsViewFocusHandler); this._initPointerEventHandler(); this._initKeyDownHandler(); this._setRowsViewAttributes(); if (isFocusedViewCorrect && isFocusedElementCorrect) { needUpdateFocus = this._isNeedFocus ? !isAppend : this._isHiddenFocus && isFullUpdate; needUpdateFocus && this._updateFocus(true) } }) }, _initDocumentHandlers: function() { var document = domAdapter.getDocument(); this._documentClickHandler = this.createAction(e => { var $target = $(e.event.target); var isCurrentRowsViewClick = this._isEventInCurrentGrid(e.event) && $target.closest(".".concat(this.addWidgetPrefix(ROWS_VIEW_CLASS))).length; var isEditorOverlay = $target.closest(".".concat(DROPDOWN_EDITOR_OVERLAY_CLASS)).length; var columnsResizerController = this.getController("columnsResizer"); var isColumnResizing = !!columnsResizerController && columnsResizerController.isResizing(); if (!isCurrentRowsViewClick && !isEditorOverlay && !isColumnResizing) { var targetInsideFocusedView = this._focusedView ? $target.parents().filter(this._focusedView.element()).length > 0 : false; !targetInsideFocusedView && this._resetFocusedCell(true); this._resetFocusedView() } }); eventsEngine.on(document, addNamespace(pointerEvents.down, "dxDataGridKeyboardNavigation"), this._documentClickHandler) }, _setRowsViewAttributes: function() { var $rowsView = this._getRowsViewElement(); var isGridEmpty = !this._dataController.getVisibleRows().length; if (isGridEmpty) { this._applyTabIndexToElement($rowsView) } }, _initPointerEventHandler: function() { var pointerEventName = !isMobile() ? pointerEvents.down : clickEventName; var clickSelector = ".".concat(ROW_CLASS, " > td, .").concat(ROW_CLASS); var $rowsView = this._getRowsViewElement(); if (!isDefined(this._pointerEventAction)) { this._pointerEventAction = this.createAction(this._pointerEventHandler) } eventsEngine.off($rowsView, addNamespace(pointerEventName, "dxDataGridKeyboardNavigation"), this._pointerEventAction); eventsEngine.on($rowsView, addNamespace(pointerEventName, "dxDataGridKeyboardNavigation"), clickSelector, this._pointerEventAction) }, _initKeyDownHandler: function() { var $rowsView = this._getRowsViewElement(); keyboard.off(this._keyDownListener); this._keyDownListener = keyboard.on($rowsView, null, e => this._keyDownHandler(e)) }, dispose: function() { this.callBase(); this._resetFocusedView(); keyboard.off(this._keyDownListener); eventsEngine.off(domAdapter.getDocument(), addNamespace(pointerEvents.down, "dxDataGridKeyboardNavigation"), this._documentClickHandler); clearTimeout(this._updateFocusTimeout); accessibility.unsubscribeVisibilityChange() }, optionChanged: function(args) { switch (args.name) { case "keyboardNavigation": case "useLegacyKeyboardNavigation": args.handled = true; break; default: this.callBase(args) } }, isRowFocusType: function() { return this.focusType === FOCUS_TYPE_ROW }, isCellFocusType: function() { return this.focusType === FOCUS_TYPE_CELL }, setRowFocusType: function() { if (this.option("focusedRowEnabled")) { this.focusType = FOCUS_TYPE_ROW } }, setCellFocusType: function() { this.focusType = FOCUS_TYPE_CELL }, _keyDownHandler: function(e) { var isEditing = this._editingController.isEditing(); var needStopPropagation = true; var originalEvent = e.originalEvent; var isHandled = this._processOnKeyDown(e); if (originalEvent.isDefaultPrevented()) { return } this._isNeedFocus = true; this._isNeedScroll = true; this._updateFocusedCellPositionByTarget(originalEvent.target); if (!isHandled) { switch (e.keyName) { case "leftArrow": case "rightArrow": this._leftRightKeysHandler(e, isEditing); break; case "upArrow": case "downArrow": if (e.ctrl) { accessibility.selectView("rowsView", this, originalEvent) } else { this._upDownKeysHandler(e, isEditing) } break; case "pageUp": case "pageDown": this._pageUpDownKeyHandler(e); break; case "space": this._spaceKeyHandler(e, isEditing); break; case "A": if (isCommandKeyPressed(e.originalEvent)) { this._ctrlAKeyHandler(e, isEditing) } else { this._beginFastEditing(e.originalEvent) } break; case "tab": this._tabKeyHandler(e, isEditing); break; case "enter": this._enterKeyHandler(e, isEditing); break; case "escape": this._escapeKeyHandler(e, isEditing); break; case "F": if (isCommandKeyPressed(e.originalEvent)) { this._ctrlFKeyHandler(e) } else { this._beginFastEditing(e.originalEvent) } break; case "F2": this._f2KeyHandler(); break; case "del": case "backspace": if (this._isFastEditingAllowed() && !this._isFastEditingStarted()) { this._beginFastEditing(originalEvent, true) } break; default: if (!this._beginFastEditing(originalEvent)) { this._isNeedFocus = false; this._isNeedScroll = false; needStopPropagation = false } } if (needStopPropagation) { originalEvent.stopPropagation() } } }, _processOnKeyDown: function(eventArgs) { var originalEvent = eventArgs.originalEvent; var args = { handled: false, event: originalEvent }; this.executeAction("onKeyDown", args); eventArgs.ctrl = originalEvent.ctrlKey; eventArgs.alt = originalEvent.altKey; eventArgs.shift = originalEvent.shiftKey; return !!args.handled }, _closeEditCell: function() { setTimeout(() => { this._editingController.closeEditCell() }) }, _leftRightKeysHandler: function(eventArgs, isEditing) { var rowIndex = this.getVisibleRowIndex(); var $event = eventArgs.originalEvent; var $row = this._focusedView && this._focusedView.getRow(rowIndex); var directionCode = this._getDirectionCodeByKey(eventArgs.keyName); var isEditingNavigationMode = this._isFastEditingStarted(); var allowNavigate = (!isEditing || isEditingNavigationMode) && isDataRow($row); if (allowNavigate) { this.setCellFocusType(); isEditingNavigationMode && this._closeEditCell(); if (this._isVirtualColumnRender()) { this._processVirtualHorizontalPosition(directionCode) } var $cell = this._getNextCell(directionCode); if (isElementDefined($cell)) { this._arrowKeysHandlerFocusCell($event, $cell, directionCode) } $event && $event.preventDefault() } }, _upDownKeysHandler: function(eventArgs, isEditing) { var rowIndex = this._focusedCellPosition.rowIndex; var visibleRowIndex = this.getVisibleRowIndex(); var $row = this._focusedView && this._focusedView.getRow(visibleRowIndex); var $event = eventArgs.originalEvent; var isUpArrow = "upArrow" === eventArgs.keyName; var dataSource = this._dataController.dataSource(); var isEditingNavigationMode = this._isFastEditingStarted(); var allowNavigate = (!isEditing || isEditingNavigationMode) && $row && !isDetailRow($row); if (allowNavigate) { isEditingNavigationMode && this._closeEditCell(); if (!this._navigateNextCell($event, eventArgs.keyName)) { if (this._isVirtualRowRender() && isUpArrow && dataSource && !dataSource.isLoading()) { var rowHeight = $row.outerHeight(); rowIndex = this._focusedCellPosition.rowIndex - 1; this._scrollBy(0, -rowHeight, rowIndex, $event) } } $event && $event.preventDefault() } }, _pageUpDownKeyHandler: function(eventArgs) { var pageIndex = this._dataController.pageIndex(); var pageCount = this._dataController.pageCount(); var pagingEnabled = this.option("paging.enabled"); var isPageUp = "pageUp" === eventArgs.keyName; var pageStep = isPageUp ? -1 : 1; var scrollable = this.getView("rowsView").getScrollable(); if (pagingEnabled && !this._isVirtualScrolling()) { if ((isPageUp ? pageIndex > 0 : pageIndex < pageCount - 1) && !this._isVirtualScrolling()) { this._dataController.pageIndex(pageIndex + pageStep); eventArgs.originalEvent.preventDefault() } } else if (scrollable && scrollable._container().height() < scrollable.$content().height()) { this._scrollBy(0, scrollable._container().height() * pageStep); eventArgs.originalEvent.preventDefault() } }, _spaceKeyHandler: function(eventArgs, isEditing) { var rowIndex = this.getVisibleRowIndex(); var $target = $(eventArgs.originalEvent && eventArgs.originalEvent.target); if (this.option("selection") && "none" !== this.option("selection").mode && !isEditing) { var isFocusedRowElement = "row" === this._getElementType($target) && this.isRowFocusType() && isDataRow($target); var isFocusedSelectionCell = $target.hasClass(COMMAND_SELECT_CLASS); if (isFocusedSelectionCell && "onClick" === this.option("selection.showCheckBoxesMode")) { this._selectionController.startSelectionWithCheckboxes() } if (isFocusedRowElement || $target.parent().hasClass(DATA_ROW_CLASS) || $target.hasClass(this.addWidgetPrefix(ROWS_VIEW_CLASS))) { this._selectionController.changeItemSelection(rowIndex, { shift: eventArgs.shift, control: eventArgs.ctrl }); eventArgs.originalEvent.preventDefault() } } else { this._beginFastEditing(eventArgs.originalEvent) } }, _ctrlAKeyHandler: function(eventArgs, isEditing) { if (!isEditing && !eventArgs.alt && "multiple" === this.option("selection.mode") && this.option("selection.allowSelectAll")) { this._selectionController.selectAll(); eventArgs.originalEvent.preventDefault() } }, _tabKeyHandler: function(eventArgs, isEditing) { var editingOptions = this.option("editing"); var direction = eventArgs.shift ? "previous" : "next"; var isCellPositionDefined = isDefined(this._focusedCellPosition) && !isEmptyObject(this._focusedCellPosition); var isOriginalHandlerRequired = !isCellPositionDefined || !eventArgs.shift && this._isLastValidCell(this._focusedCellPosition) || eventArgs.shift && this._isFirstValidCell(this._focusedCellPosition); var eventTarget = eventArgs.originalEvent.target; var focusedViewElement = this._focusedView && this._focusedView.element(); if (this._handleTabKeyOnMasterDetailCell(eventTarget, direction)) { return } $(focusedViewElement).addClass(FOCUS_STATE_CLASS); if (editingOptions && eventTarget && !isOriginalHandlerRequired) { if ($(eventTarget).hasClass(this.addWidgetPrefix(ROWS_VIEW_CLASS))) { this._resetFocusedCell() } if (this._isVirtualColumnRender()) { this._processVirtualHorizontalPosition(direction) } if (isEditing) { if (!this._editingCellTabHandler(eventArgs, direction)) { return } } else if (this._targetCellTabHandler(eventArgs, direction)) { isOriginalHandlerRequired = true } } if (isOriginalHandlerRequired) { this._editorFactory.loseFocus(); if (this._editingController.isEditing() && !this._isRowEditMode()) { this._resetFocusedCell(true); this._resetFocusedView(); this._closeEditCell() } } else { eventArgs.originalEvent.preventDefault() } }, _getMaxHorizontalOffset: function() { var scrollable = this.component.getScrollable(); var rowsView = this.getView("rowsView"); var offset = scrollable ? scrollable.scrollWidth() - $(rowsView.element()).width() : 0; return offset }, _isColumnRendered: function(columnIndex) { var allVisibleColumns = this._columnsController.getVisibleColumns(null, true); var renderedVisibleColumns = this._columnsController.getVisibleColumns(); var column = allVisibleColumns[columnIndex]; var result = false; if (column) { result = renderedVisibleColumns.indexOf(column) >= 0 } return result }, _isFixedColumn: function(columnIndex) { var allVisibleColumns = this._columnsController.getVisibleColumns(null, true); var column = allVisibleColumns[columnIndex]; return !!column && !!column.fixed }, _isColumnVirtual: function(columnIndex) { var localColumnIndex = columnIndex - this._columnsController.getColumnIndexOffset(); var visibleColumns = this._columnsController.getVisibleColumns(); var column = visibleColumns[localColumnIndex]; return !!column && "virtual" === column.command }, _processVirtualHorizontalPosition: function(direction) { var scrollable = this.component.getScrollable(); var columnIndex = this.getColumnIndex(); var nextColumnIndex; var horizontalScrollPosition = 0; var needToScroll = false; switch (direction) { case "next": case "nextInRow": var columnsCount = this._getVisibleColumnCount(); nextColumnIndex = columnIndex + 1; horizontalScrollPosition = this.option("rtlEnabled") ? this._getMaxHorizontalOffset() : 0; if ("next" === direction) { needToScroll = columnsCount === nextColumnIndex || this._isFixedColumn(columnIndex) && !this._isColumnRendered(nextColumnIndex) } else { needToScroll = columnsCount > nextColumnIndex && this._isFixedColumn(columnIndex) && !this._isColumnRendered(nextColumnIndex) } break; case "previous": case "previousInRow": nextColumnIndex = columnIndex - 1; horizontalScrollPosition = this.option("rtlEnabled") ? 0 : this._getMaxHorizontalOffset(); if ("previous" === direction) { var columnIndexOffset = this._columnsController.getColumnIndexOffset(); var leftEdgePosition = nextColumnIndex < 0 && 0 === columnIndexOffset; needToScroll = leftEdgePosition || this._isFixedColumn(columnIndex) && !this._isColumnRendered(nextColumnIndex) } else { needToScroll = nextColumnIndex >= 0 && this._isFixedColumn(columnIndex) && !this._isColumnRendered(nextColumnIndex) } } if (needToScroll) { scrollable.scrollTo({ left: horizontalScrollPosition }) } else if (isDefined(nextColumnIndex) && isDefined(direction) && this._isColumnVirtual(nextColumnIndex)) { horizontalScrollPosition = this._getHorizontalScrollPositionOffset(direction); 0 !== horizontalScrollPosition && scrollable.scrollBy({ left: horizontalScrollPosition, top: 0 }) } }, _getHorizontalScrollPositionOffset: function(direction) { var positionOffset = 0; var $currentCell = this._getCell(this._focusedCellPosition); var currentCellWidth = $currentCell && $currentCell.outerWidth(); if (currentCellWidth > 0) { var rtlMultiplier = this.option("rtlEnabled") ? -1 : 1; positionOffset = "nextInRow" === direction || "next" === direction ? currentCellWidth * rtlMultiplier : currentCellWidth * rtlMultiplier * -1 } return positionOffset }, _editingCellTabHandler: function(eventArgs, direction) { var eventTarget = eventArgs.originalEvent.target; var $cell = this._getCellElementFromTarget(eventTarget); var isEditingAllowed; var $event = eventArgs.originalEvent; var elementType = this._getElementType(eventTarget); if ($cell.is(COMMAND_CELL_SELECTOR)) { return !this._targetCellTabHandler(eventArgs, direction) } this._updateFocusedCellPosition($cell); var nextCellInfo = this._getNextCellByTabKey($event, direction, elementType); $cell = nextCellInfo.$cell; if (!$cell || this._handleTabKeyOnMasterDetailCell($cell, direction)) { return false } var columnsController = this._columnsController; var cellIndex = this.getView("rowsView").getCellIndex($cell); var columnIndex = cellIndex + columnsController.getColumnIndexOffset(); var column = columnsController.getVisibleColumns(null, true)[columnIndex]; var $row = $cell.parent(); var rowIndex = this._getRowIndex($row); var row = this._dataController.items()[rowIndex]; var editingController = this._editingController; if (column && column.allowEditing) { var _isDataRow = !row || "data" === row.rowType; isEditingAllowed = editingController.allowUpdating({ row: row }) ? _isDataRow : row && row.isNewRow } if (!isEditingAllowed) { this._closeEditCell() } if (this._focusCell($cell, !nextCellInfo.isHighlighted)) { if (!this._isRowEditMode() && isEditingAllowed) { this._editFocusedCell() } else { this._focusInteractiveElement($cell, eventArgs.shift) } } return true }, _targetCellTabHandler: function(eventArgs, direction) { var $event = eventArgs.originalEvent; var eventTarget = $event.target; var $cell = this._getCellElementFromTarget(eventTarget); var $lastInteractiveElement = this._getInteractiveElement($cell, !eventArgs.shift); var isOriginalHandlerRequired = false; var elementType; if (!isEditorCell(this, $cell) && $lastInteractiveElement.length && eventTarget !== $lastInteractiveElement.get(0)) { isOriginalHandlerRequired = true } else { if (void 0 === this._focusedCellPosition.rowIndex && $(eventTarget).hasClass(ROW_CLASS)) { this._updateFocusedCellPosition($cell) } elementType = this._getElementType(eventTarget); if (this.isRowFocusType()) { this.setCellFocusType(); if ("row" === elementType && isDataRow($(eventTarget))) { eventTarget = this.getFirstValidCellInRow($(eventTarget)); elementType = this._getElementType(eventTarget) } } var nextCellInfo = this._getNextCellByTabKey($event, direction, elementType); $cell = nextCellInfo.$cell; if (!$cell) { return false } $cell = this._checkNewLineTransition($event, $cell); if (!$cell) { return false } this._focusCell($cell, !nextCellInfo.isHighlighted); if (!isEditorCell(this, $cell)) { this._focusInteractiveElement($cell, eventArgs.shift) } } return isOriginalHandlerRequired }, _getNextCellByTabKey: function($event, direction, elementType) { var $cell = this._getNextCell(direction, elementType); var args = $cell && this._fireFocusedCellChanging($event, $cell, true); if (!args || args.cancel) { return {} } if (args.$newCellElement) { $cell = args.$newCellElement } return { $cell: $cell, isHighlighted: args.isHighlighted } }, _checkNewLineTransition: function($event, $cell) { var rowIndex = this.getVisibleRowIndex(); var $row = $cell.parent(); if (rowIndex !== this._getRowIndex($row)) { var cellPosition = this._getCellPosition($cell); var args = this._fireFocusedRowChanging($event, $row); if (args.cancel) { return } if (args.rowIndexChanged) { this.setFocusedColumnIndex(cellPosition.columnIndex); $cell = this._getFocusedCell() } } return $cell }, _enterKeyHandler: function(eventArgs, isEditing) { var $cell = this._getFocusedCell(); var rowIndex = this.getVisibleRowIndex(); var $row = this._focusedView && this._focusedView.getRow(rowIndex); if (this.option("grouping.allowCollapsing") && isGroupRow($row) || this.option("masterDetail.enabled") && $cell && $cell.hasClass(COMMAND_EXPAND_CLASS)) { var key = this._dataController.getKeyByRowIndex(rowIndex); var item = this._dataController.items()[rowIndex]; if (void 0 !== key && item && item.data && !item.data.isContinuation) { this._dataController.changeRowExpand(key) } } else { this._processEnterKeyForDataCell(eventArgs, isEditing) } }, _processEnterKeyForDataCell: function(eventArgs, isEditing) { var direction = this._getEnterKeyDirection(eventArgs); var allowEditingOnEnterKey = this._allowEditingOnEnterKey(); if (isEditing || !allowEditingOnEnterKey && direction) { this._handleEnterKeyEditingCell(eventArgs.originalEvent); if ("next" === direction || "previous" === direction) { this._targetCellTabHandler(eventArgs, direction) } else if ("upArrow" === direction || "downArrow" === direction) { this._navigateNextCell(eventArgs.originalEvent, direction) } } else if (allowEditingOnEnterKey) { this._startEditing(eventArgs) } }, _getEnterKeyDirection: function(eventArgs) { var enterKeyDirection = this.option("keyboardNavigation.enterKeyDirection"); var isShift = eventArgs.shift; if ("column" === enterKeyDirection) { return isShift ? "upArrow" : "downArrow" } if ("row" === enterKeyDirection) { return isShift ? "previous" : "next" } }, _handleEnterKeyEditingCell: function(event) { var target = event.target; var $cell = this._getCellElementFromTarget(target); var isRowEditMode = this._isRowEditMode(); this._updateFocusedCellPosition($cell); if (isRowEditMode) { this._focusEditFormCell($cell); setTimeout(this._editingController.saveEditData.bind(this._editingController)) } else { eventsEngine.trigger($(target), "change"); this._closeEditCell(); event.preventDefault() } }, _escapeKeyHandler: function(eventArgs, isEditing) { var $cell = this._getCellElementFromTarget(eventArgs.originalEvent.target); if (isEditing) { this._updateFocusedCellPosition($cell); if (!this._isRowEditMode()) { if ("cell" === this._editingController.getEditMode()) { this._editingController.cancelEditData() } else { this._closeEditCell() } } else { this._focusEditFormCell($cell); this._editingController.cancelEditData(); if (0 === this._dataController.items().length) { this._resetFocusedCell(); this._editorFactory.loseFocus() } } eventArgs.originalEvent.preventDefault() } }, _ctrlFKeyHandler: function(eventArgs) { if (this.option("searchPanel.visible")) { var searchTextEditor = this._headerPanel.getSearchTextEditor(); if (searchTextEditor) { searchTextEditor.focus(); eventArgs.originalEvent.preventDefault() } } }, _f2KeyHandler: function() { var isEditing = this._editingController.isEditing(); var rowIndex = this.getVisibleRowIndex(); var $row = this._focusedView && this._focusedView.getRow(rowIndex); if (!isEditing && isDataRow($row)) { this._startEditing() } }, _navigateNextCell: function($event, keyCode) { var $cell = this._getNextCell(keyCode); var directionCode = this._getDirectionCodeByKey(keyCode); var isCellValid = $cell && this._isCellValid($cell); var result = isCellValid ? this._arrowKeysHandlerFocusCell($event, $cell, directionCode) : false; return result }, _arrowKeysHandlerFocusCell: function($event, $nextCell, direction) { var isVerticalDirection = "prevRow" === direction || "nextRow" === direction; var args = this._fireFocusChangingEvents($event, $nextCell, isVerticalDirection, true); $nextCell = args.$newCellElement; if (!args.cancel && this._isCellValid($nextCell)) { this._focus($nextCell, !args.isHighlighted); return true } return false }, _beginFastEditing: function(originalEvent, isDeleting) { if (!this._isFastEditingAllowed() || originalEvent.altKey || originalEvent.ctrlKey || this._editingController.isEditing()) { return false } if (isDeleting) { this._startEditing(originalEvent, FAST_EDITING_DELETE_KEY) } else { var key = originalEvent.key; var keyCode = originalEvent.keyCode || originalEvent.which; var fastEditingKey = key || keyCode && String.fromCharCode(keyCode); if (fastEditingKey && (1 === fastEditingKey.length || fastEditingKey === FAST_EDITING_DELETE_KEY)) { this._startEditing(originalEvent, fastEditingKey) } } return true }, _pointerEventHandler: function(e) { var event = e.event || e; var $target = $(event.currentTarget); var rowsView = this.getView("rowsView"); var focusedViewElement = rowsView && rowsView.element(); var $parent = $target.parent(); var isInteractiveElement = $(event.target).is(INTERACTIVE_ELEMENTS_SELECTOR); var isRevertButton = !!$(event.target).closest(".".concat(REVERT_BUTTON_CLASS)).length; var isExpandCommandCell = $target.hasClass(COMMAND_EXPAND_CLASS); if (!this._isEventInCurrentGrid(event)) { return } if (!isRevertButton && (this._isCellValid($target, !isInteractiveElement) || isExpandCommandCell)) { $target = this._isInsideEditForm($target) ? $(event.target) : $target; this._focusView(); $(focusedViewElement).removeClass(FOCUS_STATE_CLASS); if ($parent.hasClass(FREESPACE_ROW_CLASS)) { this._updateFocusedCellPosition($target); this._applyTabIndexToElement(this._focusedView.element()); this._focusedView.focus() } else if (!this._isMasterDetailCell($target)) { this._clickTargetCellHandler(event, $target) } else { this._updateFocusedCellPosition($target) } } else if ($target.is("td")) { this._resetFocusedCell() } }, _clickTargetCellHandler: function(event, $cell) { var columnIndex = this.getView("rowsView").getCellIndex($cell); var column = this._columnsController.getVisibleColumns()[columnIndex]; var isCellEditMode = this._isCellEditMode(); this.setCellFocusType(); var args = this._fireFocusChangingEvents(event, $cell, true); $cell = args.$newCellElement; if (!args.cancel) { if (args.resetFocusedRow) { this.getController("focus")._resetFocusedRow(); return } if (args.rowIndexChanged) { $cell = this._getFocusedCell() } if (!args.isHighlighted && !isCellEditMode) { this.setRowFocusType() } this._updateFocusedCellPosition($cell); if (this._allowRowUpdating() && isCellEditMode && column && column.allowEditing) { this._isNeedFocus = false; this._isHiddenFocus = false } else { var $target = event && $(event.target).closest(INTERACTIVE_ELEMENTS_SELECTOR + ", td"); var isInteractiveTarget = $target && $target.not($cell).is(INTERACTIVE_ELEMENTS_SELECTOR); var isEditor = !!column && !column.command && $cell.hasClass(EDITOR_CELL_CLASS); var isDisabled = !isEditor && (!args.isHighlighted || isInteractiveTarget); this._focus($cell, isDisabled, isInteractiveTarget) } } else { this.setRowFocusType(); this.setFocusedRowIndex(args.prevRowIndex); $cell = this._getFocusedCell(); if (this._editingController.isEditing() && isCellEditMode) { this._closeEditCell() } } }, _allowRowUpdating: function() { var rowIndex = this.getVisibleRowIndex(); var row = this._dataController.items()[rowIndex]; return this._editingController.allowUpdating({ row: row }, "click") }, focus: function(element) { var activeElementSelector; var focusedRowEnabled = this.option("focusedRowEnabled"); var isHighlighted = isCellElement($(element)); if (!element) { activeElementSelector = ".dx-datagrid-rowsview .dx-row[tabindex]"; if (!focusedRowEnabled) { activeElementSelector += ", .dx-datagrid-rowsview .dx-row > td[tabindex]" } element = this.component.$element().find(activeElementSelector).first() } element && this._focusElement($(element), isHighlighted) }, getFocusedView: function() { return this._focusedView }, setupFocusedView: function() { if (this.isKeyboardEnabled() && !isDefined(this._focusedView)) { this._focusView() } }, _focusElement: function($element, isHighlighted) { var rowsViewElement = $(this._getRowsViewElement()); var $focusedView = $element.closest(rowsViewElement); var isRowFocusType = this.isRowFocusType(); var args = {}; if (!$focusedView.length || isCellElement($element) && !this._isCellValid($element)) { return } this._focusView(); this._isNeedFocus = true; this._isNeedScroll = true; if (isCellElement($element) || isGroupRow($element)) { this.setCellFocusType(); args = this._fireFocusChangingEvents(null, $element, false, isHighlighted); $element = args.$newCellElement; if (isRowFocusType && !args.isHighlighted) { this.setRowFocusType() } } if (!args.cancel) { this._focus($element, !args.isHighlighted); this._focusInteractiveElement($element) } }, _getFocusedViewByElement: function($element) { var view = this.getFocusedView(); var $view = view && $(view.element()); return $element && 0 !== $element.closest($view).length }, _focusView: function() { this._focusedView = this.getView("rowsView") }, _resetFocusedView: function() { this.setRowFocusType(); this._focusedView = null }, _focusInteractiveElement: function($cell, isLast) { if (!$cell) { return } var $focusedElement = this._getInteractiveElement($cell, isLast); gridCoreUtils.focusAndSelectElement(this, $focusedElement) }, _focus: function($cell, disableFocus, isInteractiveElement) { var $row = $cell && !$cell.hasClass(ROW_CLASS) ? $cell.closest(".".concat(ROW_CLASS)) : $cell; if ($row && isNotFocusedRow($row)) { return } var focusedView = this._focusedView; var $focusViewElement = focusedView && focusedView.element(); var $focusElement; this._isHiddenFocus = disableFocus; if (isGroupRow($row) || this.isRowFocusType()) { $focusElement = $row; if (focusedView) { this.setFocusedRowIndex(this._getRowIndex($row)) } } else if (isCellElement($cell)) { $focusElement = $cell; this._updateFocusedCellPosition($cell) } if ($focusElement) { if ($focusViewElement) { $focusViewElement.find(".dx-row[tabindex], .dx-row > td[tabindex]").not($focusElement).removeClass(CELL_FOCUS_DISABLED_CLASS).removeAttr("tabindex") } eventsEngine.one($focusElement, "blur", e => { if (e.relatedTarget) { $focusElement.removeClass(CELL_FOCUS_DISABLED_CLASS) } }); if (!isInteractiveElement) { this._applyTabIndexToElement($focusElement); eventsEngine.trigger($focusElement, "focus") } if (disableFocus) { $focusElement.addClass(CELL_FOCUS_DISABLED_CLASS) } else { this._editorFactory.focus($focusElement) } } }, _updateFocus: function(isRenderView) { this._updateFocusTimeout = setTimeout(() => { var editingController = this._editingController; var isCellEditMode = editingController.getEditMode() === EDIT_MODE_CELL; if (isCellEditMode && editingController.hasChanges()) { editingController._focusEditingCell(); return } var $cell = this._getFocusedCell(); var isEditing = editingController.isEditing(); if ($cell && !(this._isMasterDetailCell($cell) && !this._isRowEditMode())) { if (this._hasSkipRow($cell.parent())) { var direction = this._focusedCellPosition && this._focusedCellPosition.rowIndex > 0 ? "upArrow" : "downArrow"; $cell = this._getNextCell(direction) } if (isElementDefined($cell)) { if (isRenderView && !isEditing && this._checkCellOverlapped($cell)) { return } if ($cell.is("td") || $cell.hasClass(this.addWidgetPrefix(EDIT_FORM_ITEM_CLASS))) { var isCommandCell = $cell.is(COMMAND_CELL_SELECTOR); var $focusedElementInsideCell = $cell.find(":focus"); var isFocusedElementDefined = isElementDefined($focusedElementInsideCell); if ((isRenderView || !isCommandCell) && this._editorFactory.focus()) { if (isCommandCell && isFocusedElementDefined) { gridCoreUtils.focusAndSelectElement(this, $focusedElementInsideCell); return }!isFocusedElementDefined && this._focus($cell) } else if (!isFocusedElementDefined && (this._isNeedFocus || this._isHiddenFocus)) { this._focus($cell, this._isHiddenFocus) } if (isEditing) { this._focusInteractiveElement.bind(this)($cell) } } else { eventsEngine.trigger($cell, "focus") } } } }) }, _checkCellOverlapped: function($cell) { var cellOffset = $cell.offset(); var hasScrollable = this.component.getScrollable && this.component.getScrollable(); var isOverlapped = false; if (hasScrollable) { if (cellOffset.left < 0) { isOverlapped = $cell.width() + cellOffset.left <= 0 } else if (cellOffset.top < 0) { isOverlapped = $cell.height() + cellOffset.top <= 0 } } return isOverlapped }, _getFocusedCell: function() { return $(this._getCell(this._focusedCellPosition)) }, _updateFocusedCellPositionByTarget: function(target) { var _this$_focusedCellPos; var elementType = this._getElementType(target); if ("row" === elementType && isDefined(null === (_this$_focusedCellPos = this._focusedCellPosition) || void 0 === _this$_focusedCellPos ? void 0 : _this$_focusedCellPos.columnIndex)) { var $row = $(target); this._focusedView && isGroupRow($row) && this.setFocusedRowIndex(this._getRowIndex($row)) } else { this._updateFocusedCellPosition(this._getCellElementFromTarget(target)) } }, _updateFocusedCellPosition: function($cell, direction) { var position = this._getCellPosition($cell, direction); if (position) { if (!$cell.length || position.rowIndex >= 0 && position.columnIndex >= 0) { this.setFocusedCellPosition(position.rowIndex, position.columnIndex) } } return position }, _getFocusedColumnIndexOffset: function(columnIndex) { var offset = 0; var column = this._columnsController.getVisibleColumns()[columnIndex]; if (column && column.fixed) { offset = this._getFixedColumnIndexOffset(column) } else if (columnIndex >= 0) { offset = this._columnsController.getColumnIndexOffset() } return offset }, _getFixedColumnIndexOffset: function(column) { var offset = isFixedColumnIndexOffsetRequired(this, column) ? this._getVisibleColumnCount() - this._columnsController.getVisibleColumns().length : 0; return offset }, _getCellPosition: function($cell, direction) { var columnIndex; var $row = isElementDefined($cell) && $cell.closest("tr"); var rowsView = this.getView("rowsView"); if (isElementDefined($row)) { var rowIndex = this._getRowIndex($row); columnIndex = rowsView.getCellIndex($cell, rowIndex); columnIndex += this._getFocusedColumnIndexOffset(columnIndex); if (direction) { columnIndex = "previous" === direction ? columnIndex - 1 : columnIndex + 1; columnIndex = this._applyColumnIndexBoundaries(columnIndex) } return { rowIndex: rowIndex, columnIndex: columnIndex } } }, _focusCell: function($cell, isDisabled) { if (this._isCellValid($cell)) { this._focus($cell, isDisabled); return true } }, _focusEditFormCell: function($cell) { if ($cell.hasClass(MASTER_DETAIL_CELL_CLASS)) { this._editorFactory.focus($cell, true) } }, _resetFocusedCell: function(preventScroll) { var _this$_focusedView; var $cell = this._getFocusedCell(); isElementDefined($cell) && $cell.removeAttr("tabindex"); this._isNeedFocus = false; this._isNeedScroll = false; this._focusedCellPosition = {}; clearTimeout(this._updateFocusTimeout); null === (_this$_focusedView = this._focusedView) || void 0 === _this$_focusedView ? void 0 : _this$_focusedView.renderFocusState(preventScroll) }, restoreFocusableElement: function(rowIndex, $event) { var args; var $rowElement; var isUpArrow = isDefined(rowIndex); var rowsView = this.getView("rowsView"); var $rowsViewE