UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,100 lines (1,090 loc) • 94.1 kB
/** * DevExtreme (esm/__internal/grids/grid_core/keyboard_navigation/module.js) * Version: 22.1.9 * Build date: Tue Apr 18 2023 * * Copyright (c) 2012 - 2023 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import { getOuterHeight, getHeight, getWidth, getOuterWidth } from "../../../../core/utils/size"; import $ from "../../../../core/renderer"; import domAdapter from "../../../../core/dom_adapter"; import eventsEngine from "../../../../events/core/events_engine"; import { isDefined, isEmptyObject } from "../../../../core/utils/type"; 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 browser from "../../../../core/utils/browser"; import { keyboard } from "../../../../events/short"; import devices from "../../../../core/devices"; import * as accessibility from "../../../../ui/shared/accessibility"; import { focused } from "../../../../ui/widget/selectors"; import gridCoreUtils from "../module_utils"; import core from "../modules"; import { GridCoreKeyboardNavigationDom } from "./dom"; const ROWS_VIEW_CLASS = "rowsview"; const EDIT_FORM_CLASS = "edit-form"; const GROUP_FOOTER_CLASS = "group-footer"; const ROW_CLASS = "dx-row"; const DATA_ROW_CLASS = "dx-data-row"; const GROUP_ROW_CLASS = "dx-group-row"; const HEADER_ROW_CLASS = "dx-header-row"; const EDIT_FORM_ITEM_CLASS = "edit-form-item"; const MASTER_DETAIL_ROW_CLASS = "dx-master-detail-row"; const FREESPACE_ROW_CLASS = "dx-freespace-row"; const VIRTUAL_ROW_CLASS = "dx-virtual-row"; const MASTER_DETAIL_CELL_CLASS = "dx-master-detail-cell"; const EDITOR_CELL_CLASS = "dx-editor-cell"; const DROPDOWN_EDITOR_OVERLAY_CLASS = "dx-dropdowneditor-overlay"; const COMMAND_EXPAND_CLASS = "dx-command-expand"; const COMMAND_SELECT_CLASS = "dx-command-select"; const COMMAND_EDIT_CLASS = "dx-command-edit"; const COMMAND_CELL_SELECTOR = "[class^=dx-command]"; const CELL_FOCUS_DISABLED_CLASS = "dx-cell-focus-disabled"; const DATEBOX_WIDGET_NAME = "dxDateBox"; const FOCUS_STATE_CLASS = "dx-state-focused"; const WIDGET_CLASS = "dx-widget"; const REVERT_BUTTON_CLASS = "dx-revert-button"; const FAST_EDITING_DELETE_KEY = "delete"; const INTERACTIVE_ELEMENTS_SELECTOR = "input:not([type='hidden']), textarea, a, select, button, [tabindex], .dx-checkbox"; const NON_FOCUSABLE_ELEMENTS_SELECTOR = INTERACTIVE_ELEMENTS_SELECTOR + ", .dx-dropdowneditor-icon"; const EDIT_MODE_ROW = "row"; const EDIT_MODE_FORM = "form"; const EDIT_MODE_BATCH = "batch"; const EDIT_MODE_CELL = "cell"; const FOCUS_TYPE_ROW = "row"; const FOCUS_TYPE_CELL = "cell"; const COLUMN_HEADERS_VIEW = "columnHeadersView"; const FUNCTIONAL_KEYS = ["shift", "control", "alt"]; function isGroupRow($row) { return $row && $row.hasClass("dx-group-row") } function isDetailRow($row) { return $row && $row.hasClass("dx-master-detail-row") } function isDataRow($row) { return $row && !isGroupRow($row) && !isDetailRow($row) } function isNotFocusedRow($row) { return !$row || $row.hasClass("dx-freespace-row") || $row.hasClass("dx-virtual-row") } function isEditorCell(that, $cell) { return !that._isRowEditMode() && $cell && !$cell.hasClass("dx-command-select") && $cell.hasClass("dx-editor-cell") } function isElementDefined($element) { return isDefined($element) && $element.length > 0 } function isMobile() { return "desktop" !== devices.current().deviceType } function isCellInHeaderRow($cell) { return !!$cell.parent(".dx-header-row").length } function isFixedColumnIndexOffsetRequired(that, column) { const rtlEnabled = that.option("rtlEnabled"); let 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) { const keyboardController = that.getController("keyboardNavigation"); return keyboardController._isVirtualScrolling() ? that.option("focusedRowIndex") === keyboardController.getRowIndex() : false } const KeyboardNavigationController = core.ViewController.inherit({ init() { 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; const elementFocused = $element => { this.setupFocusedView(); if (this._isNeedScroll) { if ($element.is(":visible") && this._focusedView && this._focusedView.getScrollable) { this._focusedView._scrollToElement($element); this._isNeedScroll = false } } }; this._editorFactory.focused.add(elementFocused); this._initViewHandlers(); this._initDocumentHandlers(); this.createAction("onKeyDown") } }, _initViewHandlers() { const rowsView = this.getView("rowsView"); const rowsViewFocusHandler = event => { const $element = $(event.target); const isRelatedTargetInRowsView = $(event.relatedTarget).closest(rowsView.element()).length; const isLink = $element.is("a"); if (event.relatedTarget && isLink && !isRelatedTargetInRowsView && this._isEventInCurrentGrid(event)) { let $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 => { const $rowsView = rowsView.element(); const isFullUpdate = !e || "refresh" === e.changeType; const isFocusedViewCorrect = this._focusedView && this._focusedView.name === rowsView.name; let needUpdateFocus = false; const isAppend = e && ("append" === e.changeType || "prepend" === e.changeType); const $focusedElement = $(":focus"); const isFocusedElementCorrect = !$focusedElement.length || $focusedElement.closest($rowsView).length; 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 && !(null === e || void 0 === e ? void 0 : e.virtualColumnsScrolling); needUpdateFocus && this._updateFocus(true) } }) }, _initDocumentHandlers() { const document = domAdapter.getDocument(); this._documentClickHandler = this.createAction(e => { const $target = $(e.event.target); const isCurrentRowsViewClick = this._isEventInCurrentGrid(e.event) && $target.closest("." + this.addWidgetPrefix("rowsview")).length; const isEditorOverlay = $target.closest(".dx-dropdowneditor-overlay").length; const columnsResizerController = this.getController("columnsResizer"); const isColumnResizing = !!columnsResizerController && columnsResizerController.isResizing(); if (!isCurrentRowsViewClick && !isEditorOverlay && !isColumnResizing) { const 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() { const $rowsView = this._getRowsViewElement(); const isGridEmpty = !this._dataController.getVisibleRows().length; if (isGridEmpty) { this._applyTabIndexToElement($rowsView) } }, _initPointerEventHandler() { const pointerEventName = !isMobile() ? pointerEvents.down : clickEventName; const $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"), ".dx-row > td, .dx-row", this._pointerEventAction) }, _initKeyDownHandler() { const $rowsView = this._getRowsViewElement(); keyboard.off(this._keyDownListener); this._keyDownListener = keyboard.on($rowsView, null, e => this._keyDownHandler(e)) }, dispose() { this.callBase(); this._resetFocusedView(); keyboard.off(this._keyDownListener); eventsEngine.off(domAdapter.getDocument(), addNamespace(pointerEvents.down, "dxDataGridKeyboardNavigation"), this._documentClickHandler); clearTimeout(this._updateFocusTimeout); accessibility.unsubscribeVisibilityChange() }, optionChanged(args) { const that = this; switch (args.name) { case "keyboardNavigation": case "useLegacyKeyboardNavigation": args.handled = true; break; default: that.callBase(args) } }, isRowFocusType() { return "row" === this.focusType }, isCellFocusType() { return "cell" === this.focusType }, setRowFocusType() { if (this.option("focusedRowEnabled")) { this.focusType = "row" } }, setCellFocusType() { this.focusType = "cell" }, _keyDownHandler(e) { var _a; let needStopPropagation = true; this._isNeedFocus = true; this._isNeedScroll = true; let isHandled = this._processOnKeyDown(e); const isEditing = null === (_a = this._editingController) || void 0 === _a ? void 0 : _a.isEditing(); const { originalEvent: originalEvent } = e; if (originalEvent.isDefaultPrevented()) { this._isNeedFocus = false; this._isNeedScroll = false; return }!FUNCTIONAL_KEYS.includes(e.keyName) && this._updateFocusedCellPositionByTarget(originalEvent.target); if (!isHandled) { switch (e.keyName) { case "leftArrow": case "rightArrow": this._leftRightKeysHandler(e, isEditing); isHandled = true; break; case "upArrow": case "downArrow": if (e.ctrl) { accessibility.selectView("rowsView", this, originalEvent) } else { this._upDownKeysHandler(e, isEditing) } isHandled = true; break; case "pageUp": case "pageDown": this._pageUpDownKeyHandler(e); isHandled = true; break; case "space": isHandled = this._spaceKeyHandler(e, isEditing); break; case "A": if (isCommandKeyPressed(e.originalEvent)) { this._ctrlAKeyHandler(e, isEditing); isHandled = true } else { isHandled = this._beginFastEditing(e.originalEvent) } break; case "tab": this._tabKeyHandler(e, isEditing); isHandled = true; break; case "enter": this._enterKeyHandler(e, isEditing); isHandled = true; break; case "escape": this._escapeKeyHandler(e, isEditing); isHandled = true; break; case "F": if (isCommandKeyPressed(e.originalEvent)) { this._ctrlFKeyHandler(e); isHandled = true } else { isHandled = this._beginFastEditing(e.originalEvent) } break; case "F2": this._f2KeyHandler(); isHandled = true; break; case "del": case "backspace": if (this._isFastEditingAllowed() && !this._isFastEditingStarted()) { isHandled = this._beginFastEditing(originalEvent, true) } } if (!isHandled && !this._beginFastEditing(originalEvent)) { this._isNeedFocus = false; this._isNeedScroll = false; needStopPropagation = false } if (needStopPropagation) { originalEvent.stopPropagation() } } }, _processOnKeyDown(eventArgs) { const { originalEvent: originalEvent } = eventArgs; const 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() { setTimeout(() => { this._editingController.closeEditCell() }) }, _leftRightKeysHandler(eventArgs, isEditing) { const rowIndex = this.getVisibleRowIndex(); const $event = eventArgs.originalEvent; const $row = this._focusedView && this._focusedView.getRow(rowIndex); const directionCode = this._getDirectionCodeByKey(eventArgs.keyName); const isEditingNavigationMode = this._isFastEditingStarted(); const allowNavigate = (!isEditing || isEditingNavigationMode) && isDataRow($row); if (allowNavigate) { this.setCellFocusType(); isEditingNavigationMode && this._closeEditCell(); if (this._isVirtualColumnRender()) { this._processVirtualHorizontalPosition(directionCode) } const $cell = this._getNextCell(directionCode); if (isElementDefined($cell)) { this._arrowKeysHandlerFocusCell($event, $cell, directionCode) } $event && $event.preventDefault() } }, _upDownKeysHandler(eventArgs, isEditing) { var _a, _b; const visibleRowIndex = this.getVisibleRowIndex(); const $row = this._focusedView && this._focusedView.getRow(visibleRowIndex); const $event = eventArgs.originalEvent; const isUpArrow = "upArrow" === eventArgs.keyName; const dataSource = this._dataController.dataSource(); const isRowEditingInCurrentRow = null === (_b = null === (_a = this._editingController) || void 0 === _a ? void 0 : _a.isEditRowByIndex) || void 0 === _b ? void 0 : _b.call(_a, visibleRowIndex); const isEditingNavigationMode = this._isFastEditingStarted(); const allowNavigate = (!isRowEditingInCurrentRow || !isEditing || isEditingNavigationMode) && $row && !isDetailRow($row); if (allowNavigate) { isEditingNavigationMode && this._closeEditCell(); if (!this._navigateNextCell($event, eventArgs.keyName)) { if (this._isVirtualRowRender() && isUpArrow && dataSource && !dataSource.isLoading()) { const rowHeight = getOuterHeight($row); const rowIndex = this._focusedCellPosition.rowIndex - 1; this._scrollBy(0, -rowHeight, rowIndex, $event) } } $event && $event.preventDefault() } }, _pageUpDownKeyHandler(eventArgs) { const pageIndex = this._dataController.pageIndex(); const pageCount = this._dataController.pageCount(); const pagingEnabled = this.option("paging.enabled"); const isPageUp = "pageUp" === eventArgs.keyName; const pageStep = isPageUp ? -1 : 1; const 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 && getHeight(scrollable.container()) < getHeight(scrollable.$content())) { this._scrollBy(0, getHeight(scrollable.container()) * pageStep); eventArgs.originalEvent.preventDefault() } }, _spaceKeyHandler(eventArgs, isEditing) { const rowIndex = this.getVisibleRowIndex(); const $target = $(eventArgs.originalEvent && eventArgs.originalEvent.target); if (this.option("selection") && "none" !== this.option("selection").mode && !isEditing) { const isFocusedRowElement = "row" === this._getElementType($target) && this.isRowFocusType() && isDataRow($target); const isFocusedSelectionCell = $target.hasClass("dx-command-select"); if (isFocusedSelectionCell && "onClick" === this.option("selection.showCheckBoxesMode")) { this._selectionController.startSelectionWithCheckboxes() } if (isFocusedRowElement || $target.parent().hasClass("dx-data-row") || $target.hasClass(this.addWidgetPrefix("rowsview"))) { this._selectionController.changeItemSelection(rowIndex, { shift: eventArgs.shift, control: eventArgs.ctrl }); eventArgs.originalEvent.preventDefault(); return true } return false } return this._beginFastEditing(eventArgs.originalEvent) }, _ctrlAKeyHandler(eventArgs, isEditing) { if (!isEditing && !eventArgs.alt && "multiple" === this.option("selection.mode") && this.option("selection.allowSelectAll")) { this._selectionController.selectAll(); eventArgs.originalEvent.preventDefault() } }, _tabKeyHandler(eventArgs, isEditing) { const editingOptions = this.option("editing"); const direction = eventArgs.shift ? "previous" : "next"; const isCellPositionDefined = isDefined(this._focusedCellPosition) && !isEmptyObject(this._focusedCellPosition); let isOriginalHandlerRequired = !isCellPositionDefined || !eventArgs.shift && this._isLastValidCell(this._focusedCellPosition) || eventArgs.shift && this._isFirstValidCell(this._focusedCellPosition); const eventTarget = eventArgs.originalEvent.target; const 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("rowsview"))) { 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() { const scrollable = this.component.getScrollable(); const rowsView = this.getView("rowsView"); const offset = scrollable ? scrollable.scrollWidth() - getWidth(rowsView.element()) : 0; return offset }, _isColumnRendered(columnIndex) { const allVisibleColumns = this._columnsController.getVisibleColumns(null, true); const renderedVisibleColumns = this._columnsController.getVisibleColumns(); const column = allVisibleColumns[columnIndex]; let result = false; if (column) { result = renderedVisibleColumns.indexOf(column) >= 0 } return result }, _isFixedColumn(columnIndex) { const allVisibleColumns = this._columnsController.getVisibleColumns(null, true); const column = allVisibleColumns[columnIndex]; return !!column && !!column.fixed }, _isColumnVirtual(columnIndex) { const localColumnIndex = columnIndex - this._columnsController.getColumnIndexOffset(); const visibleColumns = this._columnsController.getVisibleColumns(); const column = visibleColumns[localColumnIndex]; return !!column && "virtual" === column.command }, _processVirtualHorizontalPosition(direction) { const scrollable = this.component.getScrollable(); const columnIndex = this.getColumnIndex(); let nextColumnIndex; let horizontalScrollPosition = 0; let needToScroll = false; switch (direction) { case "next": case "nextInRow": { const 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) { const columnIndexOffset = this._columnsController.getColumnIndexOffset(); const 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(direction) { let positionOffset = 0; const $currentCell = this._getCell(this._focusedCellPosition); const currentCellWidth = $currentCell && getOuterWidth($currentCell); if (currentCellWidth > 0) { const rtlMultiplier = this.option("rtlEnabled") ? -1 : 1; positionOffset = "nextInRow" === direction || "next" === direction ? currentCellWidth * rtlMultiplier : currentCellWidth * rtlMultiplier * -1 } return positionOffset }, _editingCellTabHandler(eventArgs, direction) { const eventTarget = eventArgs.originalEvent.target; let $cell = this._getCellElementFromTarget(eventTarget); let isEditingAllowed; const $event = eventArgs.originalEvent; const elementType = this._getElementType(eventTarget); if ($cell.is("[class^=dx-command]")) { return !this._targetCellTabHandler(eventArgs, direction) } this._updateFocusedCellPosition($cell); const nextCellInfo = this._getNextCellByTabKey($event, direction, elementType); $cell = nextCellInfo.$cell; if (!$cell || this._handleTabKeyOnMasterDetailCell($cell, direction)) { return false } const columnsController = this._columnsController; const cellIndex = this.getView("rowsView").getCellIndex($cell); const columnIndex = cellIndex + columnsController.getColumnIndexOffset(); const column = columnsController.getVisibleColumns(null, true)[columnIndex]; const $row = $cell.parent(); const rowIndex = this._getRowIndex($row); const row = this._dataController.items()[rowIndex]; const editingController = this._editingController; if (column && column.allowEditing) { const 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(eventArgs, direction) { const $event = eventArgs.originalEvent; let eventTarget = $event.target; let $cell = this._getCellElementFromTarget(eventTarget); const $lastInteractiveElement = this._getInteractiveElement($cell, !eventArgs.shift); let isOriginalHandlerRequired = false; let elementType; if (!isEditorCell(this, $cell) && $lastInteractiveElement.length && eventTarget !== $lastInteractiveElement.get(0)) { isOriginalHandlerRequired = true } else { if (void 0 === this._focusedCellPosition.rowIndex && $(eventTarget).hasClass("dx-row")) { 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) } } const 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($event, direction, elementType) { let $cell = this._getNextCell(direction, elementType); const 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($event, $cell) { const rowIndex = this.getVisibleRowIndex(); const $row = $cell.parent(); if (rowIndex !== this._getRowIndex($row)) { const cellPosition = this._getCellPosition($cell); const args = this._fireFocusedRowChanging($event, $row); if (args.cancel) { return } if (args.rowIndexChanged) { this.setFocusedColumnIndex(cellPosition.columnIndex); $cell = this._getFocusedCell() } } return $cell }, _enterKeyHandler(eventArgs, isEditing) { const $cell = this._getFocusedCell(); const rowIndex = this.getVisibleRowIndex(); const $row = this._focusedView && this._focusedView.getRow(rowIndex); if (this.option("grouping.allowCollapsing") && isGroupRow($row) || this.option("masterDetail.enabled") && $cell && $cell.hasClass("dx-command-expand")) { const key = this._dataController.getKeyByRowIndex(rowIndex); const 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(eventArgs, isEditing) { const direction = this._getEnterKeyDirection(eventArgs); const 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(eventArgs) { const enterKeyDirection = this.option("keyboardNavigation.enterKeyDirection"); const isShift = eventArgs.shift; if ("column" === enterKeyDirection) { return isShift ? "upArrow" : "downArrow" } if ("row" === enterKeyDirection) { return isShift ? "previous" : "next" } }, _handleEnterKeyEditingCell(event) { const { target: target } = event; const $cell = this._getCellElementFromTarget(target); const 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(eventArgs, isEditing) { const $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(eventArgs) { if (this.option("searchPanel.visible")) { const searchTextEditor = this._headerPanel.getSearchTextEditor(); if (searchTextEditor) { searchTextEditor.focus(); eventArgs.originalEvent.preventDefault() } } }, _f2KeyHandler() { const isEditing = this._editingController.isEditing(); const rowIndex = this.getVisibleRowIndex(); const $row = this._focusedView && this._focusedView.getRow(rowIndex); if (!isEditing && isDataRow($row)) { this._startEditing() } }, _navigateNextCell($event, keyCode) { const $cell = this._getNextCell(keyCode); const directionCode = this._getDirectionCodeByKey(keyCode); const isCellValid = $cell && this._isCellValid($cell); const result = isCellValid ? this._arrowKeysHandlerFocusCell($event, $cell, directionCode) : false; return result }, _arrowKeysHandlerFocusCell($event, $nextCell, direction) { const isVerticalDirection = "prevRow" === direction || "nextRow" === direction; const 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(originalEvent, isDeleting) { if (!this._isFastEditingAllowed() || originalEvent.altKey || originalEvent.ctrlKey || this._editingController.isEditing()) { return false } if (isDeleting) { this._startEditing(originalEvent, "delete") } else { const { key: key } = originalEvent; const keyCode = originalEvent.keyCode || originalEvent.which; const fastEditingKey = key || keyCode && String.fromCharCode(keyCode); if (fastEditingKey && (1 === fastEditingKey.length || "delete" === fastEditingKey)) { this._startEditing(originalEvent, fastEditingKey) } } return true }, _pointerEventHandler(e) { const event = e.event || e; let $target = $(event.currentTarget); const rowsView = this.getView("rowsView"); const focusedViewElement = rowsView && rowsView.element(); const $parent = $target.parent(); const isInteractiveElement = $(event.target).is(INTERACTIVE_ELEMENTS_SELECTOR); const isRevertButton = !!$(event.target).closest(".dx-revert-button").length; const isExpandCommandCell = $target.hasClass("dx-command-expand"); 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("dx-freespace-row")) { this._updateFocusedCellPosition($target); this._applyTabIndexToElement(this._focusedView.element()); this._focusedView.focus(true) } else if (!this._isMasterDetailCell($target)) { this._clickTargetCellHandler(event, $target) } else { this._updateFocusedCellPosition($target) } } else if ($target.is("td")) { this._resetFocusedCell() } }, _clickTargetCellHandler(event, $cell) { const columnIndex = this.getView("rowsView").getCellIndex($cell); const column = this._columnsController.getVisibleColumns()[columnIndex]; const isCellEditMode = this._isCellEditMode(); this.setCellFocusType(); const 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 { $cell = this._getFocusedCell(); const $target = event && $(event.target).closest(NON_FOCUSABLE_ELEMENTS_SELECTOR + ", td"); const skipFocusEvent = $target && $target.not($cell).is(NON_FOCUSABLE_ELEMENTS_SELECTOR); const isEditor = !!column && !column.command && $cell.hasClass("dx-editor-cell"); const isDisabled = !isEditor && (!args.isHighlighted || skipFocusEvent); this._focus($cell, isDisabled, skipFocusEvent) } } else { this.setRowFocusType(); this.setFocusedRowIndex(args.prevRowIndex); if (this._editingController.isEditing() && isCellEditMode) { this._closeEditCell() } } }, _allowRowUpdating() { const rowIndex = this.getVisibleRowIndex(); const row = this._dataController.items()[rowIndex]; return this._editingController.allowUpdating({ row: row }, "click") }, focus(element) { let activeElementSelector; const focusedRowEnabled = this.option("focusedRowEnabled"); const isHighlighted = this._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() { return this._focusedView }, setupFocusedView() { if (this.isKeyboardEnabled() && !isDefined(this._focusedView)) { this._focusView() } }, _focusElement($element, isHighlighted) { const rowsViewElement = $(this._getRowsViewElement()); const $focusedView = $element.closest(rowsViewElement); const isRowFocusType = this.isRowFocusType(); let args = {}; if (!$focusedView.length || this._isCellElement($element) && !this._isCellValid($element)) { return } this._focusView(); this._isNeedFocus = true; this._isNeedScroll = true; if (this._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($element) { const view = this.getFocusedView(); const $view = view && $(view.element()); return $element && 0 !== $element.closest($view).length }, _focusView() { this._focusedView = this.getView("rowsView") }, _resetFocusedView() { this.setRowFocusType(); this._focusedView = null }, _focusInteractiveElement($cell, isLast) { if (!$cell) { return } const $focusedElement = this._getInteractiveElement($cell, isLast); this._testInteractiveElement = $focusedElement; gridCoreUtils.focusAndSelectElement(this, $focusedElement) }, _focus($cell, disableFocus, skipFocusEvent) { const $row = $cell && !$cell.hasClass("dx-row") ? $cell.closest(".dx-row") : $cell; if ($row && isNotFocusedRow($row)) { return } const focusedView = this._focusedView; const $focusViewElement = focusedView && focusedView.element(); let $focusElement; this._isHiddenFocus = disableFocus; const isRowFocus = isGroupRow($row) || this.isRowFocusType(); if (isRowFocus) { $focusElement = $row; if (focusedView) { this.setFocusedRowIndex(this._getRowIndex($row)) } } else if (this._isCellElement($cell)) { $focusElement = $cell; this._updateFocusedCellPosition($cell) } if ($focusElement) { if ($focusViewElement) { $focusViewElement.find(".dx-row[tabindex], .dx-row > td[tabindex]").not($focusElement).removeClass("dx-cell-focus-disabled").removeAttr("tabindex") } eventsEngine.one($focusElement, "blur", e => { if (e.relatedTarget) { $focusElement.removeClass("dx-cell-focus-disabled") } }); if (!skipFocusEvent) { this._applyTabIndexToElement($focusElement); eventsEngine.trigger($focusElement, "focus") } if (disableFocus) { $focusElement.addClass("dx-cell-focus-disabled"); if (isRowFocus) { $cell.addClass("dx-cell-focus-disabled") } } else { this._editorFactory.focus($focusElement) } } }, _updateFocus(isRenderView) { this._updateFocusTimeout = setTimeout(() => { const editingController = this._editingController; const isCellEditMode = "cell" === editingController.getEditMode(); const isBatchEditMode = "batch" === editingController.getEditMode(); if (isCellEditMode && editingController.hasChanges() || isBatchEditMode && editingController.isNewRowInEditMode()) { editingController._focusEditingCell(); return } let $cell = this._getFocusedCell(); const isEditing = editingController.isEditing(); if ($cell && !(this._isMasterDetailCell($cell) && !this._isRowEditMode())) { if (this._hasSkipRow($cell.parent())) { const direction = this._focusedCellPosition && this._focusedCellPosition.rowIndex > 0 ? "upArrow" : "downArrow"; $cell = this._getNextCell(direction) } if (isElementDefined($cell)) { if ($cell.is("td") || $cell.hasClass(this.addWidgetPrefix("edit-form-item"))) { const isCommandCell = $cell.is("[class^=dx-command]"); const $focusedElementInsideCell = $cell.find(":focus"); const 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") } } } }) }, _getFocusedCell() { return $(this._getCell(this._focusedCellPosition)) }, _updateFocusedCellPositionByTarget(target) { var _a; const elementType = this._getElementType(target); if ("row" === elementType && isDefined(null === (_a = this._focusedCellPosition) || void 0 === _a ? void 0 : _a.columnIndex)) { const $row = $(target); this._focusedView && isGroupRow($row) && this.setFocusedRowIndex(this._getRowIndex($row)) } else { this._updateFocusedCellPosition(this._getCellElementFromTarget(target)) } }, _updateFocusedCellPosition($cell, direction) { const 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(columnIndex) { let offset = 0; const column = this._columnsController.getVisibleColumns()[columnIndex]; if (column && column.fixed) { offset = this._getFixedColumnIndexOffset(column) } else if (columnIndex >= 0) { offset = this._columnsController.getColumnIndexOffset() } return offset }, _getFixedColumnIndexOffset(column) { const offset = isFixedColumnIndexOffsetRequired(this, column) ? this._getVisibleColumnCount() - this._columnsController.getVisibleColumns().length : 0; return offset }, _getCellPosition($cell, direction) { let columnIndex; const $row = isElementDefined($cell) && $cell.closest("tr"); const rowsView = this.getView("rowsView"); if (isElementDefined($row)) { const 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($cell, isDisabled) { if (this._isCellValid($cell)) { this._focus($cell, isDisabled); return true } }, _focusEditFormCell($cell) { if ($cell.hasClass("dx-master-detail-cell")) { this._editorFactory.focus($cell, true) } }, _resetFocusedCell(preventScroll) { var _a; const $cell = this._getFocusedCell(); isElementDefined($cell) && $cell.removeAttr("tabindex"); this._isNeedFocus = false; this._isNeedScroll = false; this._focusedCellPosition = {}; clearTimeout(this._updateFocusTimeout); null === (_a = th