UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

661 lines (659 loc) • 33.4 kB
/** * DevExtreme (ui/grid_core/ui.grid_core.focus.js) * Version: 18.2.18 * Build date: Tue Oct 18 2022 * * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; var _renderer = require("../../core/renderer"); var _renderer2 = _interopRequireDefault(_renderer); var _uiGrid_core = require("./ui.grid_core.modules"); var _uiGrid_core2 = _interopRequireDefault(_uiGrid_core); var _iterator = require("../../core/utils/iterator"); var _uiGrid_core3 = require("./ui.grid_core.utils"); var _common = require("../../core/utils/common"); var _type = require("../../core/utils/type"); var _deferred = require("../../core/utils/deferred"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj } } var ROW_FOCUSED_CLASS = "dx-row-focused", FOCUSED_ROW_SELECTOR = ".dx-row." + ROW_FOCUSED_CLASS, CELL_FOCUS_DISABLED_CLASS = "dx-cell-focus-disabled"; exports.FocusController = _uiGrid_core2.default.ViewController.inherit(function() { return { init: function() { this._dataController = this.getController("data"); this._keyboardController = this.getController("keyboardNavigation") }, optionChanged: function(args) { if ("focusedRowIndex" === args.name) { this._focusRowByIndex(args.value); args.handled = true } else { if ("focusedRowKey" === args.name) { this._focusRowByKey(args.value); args.handled = true } else { if ("focusedColumnIndex" === args.name) { args.handled = true } else { if ("focusedRowEnabled" === args.name) { args.handled = true } else { this.callBase(args) } } } } }, _focusRowByIndex: function(index) { if (!this.option("focusedRowEnabled")) { return } index = void 0 !== index ? index : this.option("focusedRowIndex"); if (index < 0) { this._resetFocusedRow() } else { this._focusRowByIndexCore(index) } }, _focusRowByIndexCore: function(index) { var _this = this; var dataController = this.getController("data"), pageSize = dataController.pageSize(), setKeyByIndex = function() { if (_this._isValidFocusedRowIndex(index)) { var rowIndex = Math.min(index - dataController.getRowIndexOffset(), dataController.items().length - 1), focusedRowKey = dataController.getKeyByRowIndex(rowIndex); if (void 0 !== focusedRowKey && !_this.isRowFocused(focusedRowKey)) { _this.option("focusedRowKey", focusedRowKey) } } }; if (pageSize >= 0) { if (!this._isLocalRowIndex(index)) { var pageIndex = Math.floor(index / dataController.pageSize()); (0, _deferred.when)(dataController.pageIndex(pageIndex), dataController.waitReady()).done(function() { setKeyByIndex() }) } else { setKeyByIndex() } } }, _isLocalRowIndex: function(index) { var dataController = this.getController("data"), isVirtualScrolling = this.getController("keyboardNavigation")._isVirtualScrolling(); if (isVirtualScrolling) { var pageIndex = Math.floor(index / dataController.pageSize()), virtualItems = dataController.virtualItemsCount(), virtualItemsBegin = virtualItems ? virtualItems.begin : -1, visibleRowsCount = dataController.getVisibleRows().length + dataController.getRowIndexOffset(), visiblePagesCount = Math.ceil(visibleRowsCount / dataController.pageSize()); return virtualItemsBegin <= index && visiblePagesCount > pageIndex } return true }, _setFocusedRowKeyByIndex: function(index) { var dataController = this.getController("data"); if (this._isValidFocusedRowIndex(index)) { var rowIndex = Math.min(index - dataController.getRowIndexOffset(), dataController.items().length - 1), focusedRowKey = dataController.getKeyByRowIndex(rowIndex); if (void 0 !== focusedRowKey && !this.isRowFocused(focusedRowKey)) { this.option("focusedRowKey", focusedRowKey) } } }, _focusRowByKey: function(key) { if (void 0 === key) { this._resetFocusedRow() } else { this.navigateToRow(key) } }, _resetFocusedRow: function() { if (void 0 === this.option("focusedRowKey") && this.option("focusedRowIndex") < 0) { return } this.option("focusedRowKey", void 0); this.getController("keyboardNavigation").setFocusedRowIndex(-1); this.option("focusedRowIndex", -1); this.getController("data").updateItems({ changeType: "updateFocusedRow", focusedRowKey: void 0 }) }, _isValidFocusedRowIndex: function(rowIndex) { var dataController = this.getController("data"), row = dataController.getVisibleRows()[rowIndex]; return !row || "data" === row.rowType || "group" === row.rowType }, publicMethods: function() { return ["navigateToRow", "isRowFocused"] }, navigateToRow: function(key) { var that = this, dataController = this.getController("data"), rowIndex = this.option("focusedRowIndex"), result = new _deferred.Deferred; if (void 0 === key || !dataController.dataSource()) { return result.reject().promise() } var rowIndexByKey = that._getFocusedRowIndexByKey(key); if (rowIndex >= 0 && rowIndex === rowIndexByKey) { that._triggerUpdateFocusedRow(key, result) } else { dataController.getPageIndexByKey(key).done(function(pageIndex) { if (pageIndex < 0) { result.resolve(-1); return } if (pageIndex === dataController.pageIndex()) { dataController.reload().done(function() { if (that.isRowFocused(key)) { result.resolve(that._getFocusedRowIndexByKey(key)) } else { that._triggerUpdateFocusedRow(key, result) } }).fail(result.reject) } else { dataController.pageIndex(pageIndex).done(function() { if ("virtual" === that.option("scrolling.rowRenderingMode")) { setTimeout(function() { that._navigateToVirtualRow(key, result) }) } else { that._triggerUpdateFocusedRow(key, result) } }).fail(result.reject) } }).fail(result.reject) } return result.promise() }, _navigateToVirtualRow: function(key, result) { var that = this, dataController = this.getController("data"), rowsScrollController = dataController._rowsScrollController, rowIndex = (0, _uiGrid_core3.getIndexByKey)(key, dataController.items(true)), scrollable = that.getView("rowsView").getScrollable(); if (rowsScrollController && scrollable && rowIndex >= 0) { var focusedRowIndex = rowIndex + dataController.getRowIndexOffset() - dataController.getRowIndexDelta(), offset = rowsScrollController.getItemOffset(focusedRowIndex); var triggerUpdateFocusedRow = function triggerUpdateFocusedRow() { that.component.off("contentReady", triggerUpdateFocusedRow); that._triggerUpdateFocusedRow(key, result) }; that.component.on("contentReady", triggerUpdateFocusedRow); scrollable.scrollTo({ y: offset }) } }, _triggerUpdateFocusedRow: function(key, result) { var dataController = this.getController("data"), focusedRowIndex = this._getFocusedRowIndexByKey(key); if (this._isValidFocusedRowIndex(focusedRowIndex)) { this.getController("keyboardNavigation").setFocusedRowIndex(focusedRowIndex); if (this.option("focusedRowEnabled")) { dataController.updateItems({ changeType: "updateFocusedRow", focusedRowKey: key }) } else { var rowIndex = dataController.getRowIndexByKey(key); this._scrollToFocusedRow(this.getView("rowsView").getRow(rowIndex)) } result && result.resolve(focusedRowIndex) } else { result && result.resolve(-1) } }, _getFocusedRowIndexByKey: function(key) { var dataController = this.getController("data"), rowIndex = dataController.getRowIndexByKey(key); return rowIndex >= 0 ? rowIndex + dataController.getRowIndexOffset() : -1 }, _focusRowByKeyOrIndex: function() { var _this2 = this; var focusedRowKey = this.option("focusedRowKey"), currentFocusedRowIndex = this.option("focusedRowIndex"), keyboardController = this.getController("keyboardNavigation"), dataController = this.getController("data"); if (void 0 !== focusedRowKey) { var visibleRowIndex = dataController.getRowIndexByKey(focusedRowKey); if (visibleRowIndex >= 0) { if (keyboardController._isVirtualScrolling()) { currentFocusedRowIndex = visibleRowIndex + dataController.getRowIndexOffset() } keyboardController.setFocusedRowIndex(currentFocusedRowIndex); this._triggerUpdateFocusedRow(focusedRowKey) } else { this.navigateToRow(focusedRowKey).done(function(focusedRowIndex) { if (currentFocusedRowIndex >= 0 && focusedRowIndex < 0) { _this2._focusRowByIndex() } }) } } else { if (currentFocusedRowIndex >= 0) { this.getController("focus")._focusRowByIndex(currentFocusedRowIndex) } } }, isRowFocused: function(key) { var focusedRowKey = this.option("focusedRowKey"); if (void 0 !== focusedRowKey) { return (0, _common.equalByValue)(key, this.option("focusedRowKey")) } }, updateFocusedRow: function(change) { var $focusedRow, $tableElement, that = this, focusedRowIndex = that._dataController.getRowIndexByKey(change.focusedRowKey), rowsView = that.getView("rowsView"); (0, _iterator.each)(rowsView.getTableElements(), function(index, element) { $tableElement = (0, _renderer2.default)(element); that._clearPreviousFocusedRow($tableElement, focusedRowIndex); var isMainTable = 0 === index; $focusedRow = that._prepareFocusedRow(change.items[focusedRowIndex], $tableElement, focusedRowIndex); if (isMainTable) { that.getController("keyboardNavigation")._fireFocusedRowChanged($focusedRow) } }) }, _clearPreviousFocusedRow: function($tableElement, focusedRowIndex) { var $firstRow, $prevRowFocusedElement = $tableElement.find(FOCUSED_ROW_SELECTOR); $prevRowFocusedElement.removeClass(ROW_FOCUSED_CLASS).removeClass(CELL_FOCUS_DISABLED_CLASS).removeAttr("tabindex"); $prevRowFocusedElement.children("td").removeAttr("tabindex"); if (0 !== focusedRowIndex) { $firstRow = (0, _renderer2.default)(this.getView("rowsView").getRowElement(0)); $firstRow.removeClass(CELL_FOCUS_DISABLED_CLASS).removeAttr("tabIndex") } }, _prepareFocusedRow: function(changedItem, $tableElement, focusedRowIndex) { var $row, tabIndex = this.option("tabindex") || 0; if (changedItem && ("data" === changedItem.rowType || "group" === changedItem.rowType)) { $row = (0, _renderer2.default)(this.getView("rowsView")._getRowElements($tableElement).eq(focusedRowIndex)); $row.addClass(ROW_FOCUSED_CLASS).attr("tabindex", tabIndex); this._scrollToFocusedRow($row) } return $row }, _scrollToFocusedRow: function($row) { var $focusedRow, that = this, rowsView = that.getView("rowsView"), $rowsViewElement = rowsView.element(); if (!$rowsViewElement) { return } $focusedRow = $row || $rowsViewElement.find(FOCUSED_ROW_SELECTOR); if ($focusedRow.length > 0) { var diff, focusedRowRect = $focusedRow[0].getBoundingClientRect(), rowsViewRect = rowsView.element()[0].getBoundingClientRect(); if (focusedRowRect.bottom > rowsViewRect.bottom) { diff = focusedRowRect.bottom - rowsViewRect.bottom } else { if (focusedRowRect.top < rowsViewRect.top) { diff = focusedRowRect.top - rowsViewRect.top } } if (diff) { rowsView.scrollTo({ y: rowsView._scrollTop + diff }) } } } } }()); module.exports = { defaultOptions: function() { return { focusedRowEnabled: false, focusedRowKey: void 0, focusedRowIndex: -1, focusedColumnIndex: -1 } }, controllers: { focus: exports.FocusController }, extenders: { controllers: { keyboardNavigation: { init: function() { var rowIndex = this.option("focusedRowIndex"), columnIndex = this.option("focusedColumnIndex"); if (this.option("focusedRowEnabled")) { this.createAction("onFocusedRowChanging", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onFocusedRowChanged", { excludeValidators: ["disabled", "readOnly"] }) } this.createAction("onFocusedCellChanging", { excludeValidators: ["disabled", "readOnly"] }); this.createAction("onFocusedCellChanged", { excludeValidators: ["disabled", "readOnly"] }); this.callBase(); this.setRowFocusType(); this._focusedCellPosition = {}; if ((0, _type.isDefined)(rowIndex)) { this._focusedCellPosition.rowIndex = this.option("focusedRowIndex") } if ((0, _type.isDefined)(columnIndex)) { this._focusedCellPosition.columnIndex = this.option("focusedColumnIndex") } }, setFocusedRowIndex: function(rowIndex) { this.callBase(rowIndex); var visibleRow = this.getController("data").getVisibleRows()[rowIndex]; if (!visibleRow || !visibleRow.inserted) { this.option("focusedRowIndex", rowIndex) } }, setFocusedColumnIndex: function(columnIndex) { this.callBase(columnIndex); this.option("focusedColumnIndex", columnIndex) }, _escapeKeyHandler: function(eventArgs, isEditing) { if (isEditing || !this.option("focusedRowEnabled")) { this.callBase(eventArgs, isEditing); return } if (this.isCellFocusType()) { this.setRowFocusType(); this._focus(this._getCellElementFromTarget(eventArgs.originalEvent.target), true) } }, _updateFocusedCellPosition: function($cell, direction) { var prevRowIndex = this.option("focusedRowIndex"), prevColumnIndex = this.option("focusedColumnIndex"); this.callBase($cell, direction); this._fireFocusedCellChanged($cell, prevColumnIndex, prevRowIndex) } }, editorFactory: { renderFocusOverlay: function($element, hideBorder) { var $cell, keyboardController = this.getController("keyboardNavigation"), focusedRowEnabled = this.option("focusedRowEnabled"), editingController = this.getController("editing"), isRowElement = "row" === keyboardController._getElementType($element); if (!focusedRowEnabled || !keyboardController.isRowFocusType() || editingController.isEditing()) { this.callBase($element, hideBorder) } else { if (focusedRowEnabled) { if (isRowElement && !$element.hasClass(ROW_FOCUSED_CLASS)) { $cell = keyboardController.getFirstValidCellInRow($element); keyboardController.focus($cell) } } } } }, columns: { getSortDataSourceParameters: function() { var _this3 = this; var result = this.callBase.apply(this, arguments), dataController = this.getController("data"), dataSource = dataController._dataSource, store = dataController.store(), key = store && store.key(), remoteOperations = dataSource && dataSource.remoteOperations() || {}, isLocalOperations = Object.keys(remoteOperations).every(function(operationName) { return !remoteOperations[operationName] }); if (this.option("focusedRowEnabled") && key) { key = Array.isArray(key) ? key : [key]; var notSortedKeys = key.filter(function(key) { return !_this3.columnOption(key, "sortOrder") }); if (notSortedKeys.length) { result = result || []; if (isLocalOperations) { result.push({ selector: dataSource.getDataIndexGetter(), desc: false }) } else { notSortedKeys.forEach(function(notSortedKey) { return result.push({ selector: notSortedKey, desc: false }) }) } } } return result } }, data: { _applyChange: function(change) { if (change && "updateFocusedRow" === change.changeType) { return } return this.callBase.apply(this, arguments) }, _fireChanged: function(e) { var isPartialUpdateWithDeleting; if (this.option("focusedRowEnabled") && this._dataSource) { var isPartialUpdate = "update" === e.changeType && e.repaintChangesOnly; isPartialUpdateWithDeleting = isPartialUpdate && e.changeTypes && e.changeTypes.indexOf("remove") >= 0; if (isPartialUpdateWithDeleting) { this.callBase(e) } if ("refresh" === e.changeType || isPartialUpdateWithDeleting) { this.processUpdateFocusedRow() } } if (!isPartialUpdateWithDeleting) { this.callBase(e) } }, processUpdateFocusedRow: function() { var prevPageIndex = this._prevPageIndex, pageIndex = this.pageIndex(), prevRenderingPageIndex = this._prevRenderingPageIndex || 0, renderingPageIndex = this._rowsScrollController ? this._rowsScrollController.pageIndex() : 0, operationTypes = this._dataSource.operationTypes() || {}, focusController = this.getController("focus"), reload = operationTypes.reload, isVirtualScrolling = this.getController("keyboardNavigation")._isVirtualScrolling(), focusedRowKey = this.option("focusedRowKey"), paging = void 0 !== prevPageIndex && prevPageIndex !== pageIndex, pagingByRendering = renderingPageIndex !== prevRenderingPageIndex; this._prevPageIndex = pageIndex; this._prevRenderingPageIndex = renderingPageIndex; if (reload && void 0 !== focusedRowKey) { focusController.navigateToRow(focusedRowKey).done(function(focusedRowIndex) { if (focusedRowIndex < 0) { focusController._focusRowByIndex() } }) } else { if (paging) { if (!isVirtualScrolling && this.option("focusedRowIndex") >= 0) { focusController._focusRowByIndex() } } else { if (!pagingByRendering) { focusController._focusRowByKeyOrIndex() } } } }, getPageIndexByKey: function(key) { var that = this, d = new _deferred.Deferred; that.getGlobalRowIndexByKey(key).done(function(globalIndex) { d.resolve(globalIndex >= 0 ? Math.floor(globalIndex / that.pageSize()) : -1) }).fail(d.reject); return d.promise() }, getGlobalRowIndexByKey: function(key) { if (this._dataSource.group()) { return this._calculateGlobalRowIndexByGroupedData(key) } return this._calculateGlobalRowIndexByFlatData(key) }, _calculateGlobalRowIndexByFlatData: function(key, groupFilter, useGroup) { var that = this, deferred = new _deferred.Deferred, dataSource = that._dataSource, filter = that._generateFilterByKey(key); dataSource.load({ filter: that._concatWithCombinedFilter(filter), skip: 0, take: 1 }).done(function(data) { if (data.length > 0) { filter = that._generateOperationFilterByKey(key, data[0], useGroup); dataSource.load({ filter: that._concatWithCombinedFilter(filter, groupFilter), skip: 0, take: 1, requireTotalCount: true }).done(function(_, extra) { deferred.resolve(extra.totalCount) }) } else { deferred.resolve(-1) } }); return deferred.promise() }, _concatWithCombinedFilter: function(filter, groupFilter) { var combinedFilter = this.getCombinedFilter(); return (0, _uiGrid_core3.combineFilters)([filter, combinedFilter, groupFilter]) }, _generateOperationFilterByKey: function(key, rowData, useGroup) { var that = this, dataSource = that._dataSource, filter = that._generateFilterByKey(key, "<"), sort = that._columnsController.getSortDataSourceParameters(!dataSource.remoteOperations().filtering); if (useGroup) { var group = that._columnsController.getGroupDataSourceParameters(!dataSource.remoteOperations().filtering); if (group) { sort = sort ? group.concat(sort) : group } } if (sort) { sort.slice().reverse().forEach(function(sortInfo) { var getter, value, selector = sortInfo.selector; if ("function" === typeof selector) { getter = selector } else { getter = that._columnsController.columnOption(selector, "selector") } value = getter ? getter(rowData) : rowData[selector]; filter = [ [selector, "=", value], "and", filter ]; filter = [ [selector, sortInfo.desc ? ">" : "<", value], "or", filter ] }) } return filter }, _generateFilterByKey: function(key, operation) { var keyPart, dataSourceKey = this._dataSource.key(), filter = []; if (!operation) { operation = "=" } if (Array.isArray(dataSourceKey)) { for (var i = 0; i < dataSourceKey.length; ++i) { keyPart = key[dataSourceKey[i]]; if (keyPart) { if (filter.length > 0) { filter.push("and") } filter.push([dataSourceKey[i], operation, keyPart]) } } } else { filter = [dataSourceKey, operation, key] } return filter } } }, views: { rowsView: { _createRow: function(row) { var $row = this.callBase(row); if (this.option("focusedRowEnabled") && row) { if (this.getController("focus").isRowFocused(row.key)) { $row.addClass(ROW_FOCUSED_CLASS) } } return $row }, _checkRowKeys: function(options) { this.callBase.apply(this, arguments); if (this.option("focusedRowEnabled") && this.option("dataSource")) { var store = this._dataController.store(); if (store && !store.key()) { this._dataController.fireError("E1042", "Row focusing") } } }, _update: function(change) { if ("updateFocusedRow" === change.changeType) { if (this.option("focusedRowEnabled")) { this.getController("focus").updateFocusedRow(change) } } else { this.callBase(change) } }, updateFocusElementTabIndex: function($cellElements) { if (this.option("focusedRowEnabled")) { this._setFocusedRowElementTabIndex() } else { this.callBase($cellElements) } }, _setFocusedRowElementTabIndex: function() { var that = this, focusedRowKey = that.option("focusedRowKey"), tabIndex = that.option("tabIndex"), rowIndex = that._dataController.getRowIndexByKey(focusedRowKey), columnIndex = that.option("focusedColumnIndex"), $cellElements = that.getCellElements(rowIndex >= 0 ? rowIndex : 0), $row = $cellElements.eq(0).parent(), dataSource = that.component.getController("data")._dataSource, operationTypes = dataSource && dataSource.operationTypes(); that._scrollToFocusOnResize = that._scrollToFocusOnResize || function() { that.getController("focus")._scrollToFocusedRow($row); that.resizeCompleted.remove(that._scrollToFocusOnResize) }; if ($row.length) { $row.attr("tabIndex", tabIndex); if (rowIndex >= 0) { if (columnIndex < 0) { columnIndex = 0 } rowIndex += that.getController("data").getRowIndexOffset(); that.getController("keyboardNavigation").setFocusedCellPosition(rowIndex, columnIndex); if (operationTypes && !operationTypes.paging) { that.resizeCompleted.remove(that._scrollToFocusOnResize); that.resizeCompleted.add(that._scrollToFocusOnResize) } } } } } } } };