UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

621 lines (619 loc) • 26.5 kB
/** * DevExtreme (cjs/__internal/grids/data_grid/grouping/m_grouping.js) * Version: 24.2.6 * Build date: Mon Mar 17 2025 * * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GroupingHeaderPanelExtender = void 0; var _message = _interopRequireDefault(require("../../../../common/core/localization/message")); var _devices = _interopRequireDefault(require("../../../../core/devices")); var _renderer = _interopRequireDefault(require("../../../../core/renderer")); var _deferred = require("../../../../core/utils/deferred"); var _iterator = require("../../../../core/utils/iterator"); var _size = require("../../../../core/utils/size"); var _type = require("../../../../core/utils/type"); var _accessibility = require("../../../../ui/shared/accessibility"); var _m_accessibility = require("../../../grids/grid_core/m_accessibility"); var _m_core = _interopRequireDefault(require("../m_core")); var _m_data_source_adapter = _interopRequireDefault(require("../m_data_source_adapter")); var _m_grouping_collapsed = require("./m_grouping_collapsed"); var _m_grouping_expanded = require("./m_grouping_expanded"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e } } const DATAGRID_GROUP_PANEL_CLASS = "dx-datagrid-group-panel"; const DATAGRID_GROUP_PANEL_MESSAGE_CLASS = "dx-group-panel-message"; const DATAGRID_GROUP_PANEL_ITEM_CLASS = "dx-group-panel-item"; const DATAGRID_GROUP_PANEL_LABEL_CLASS = "dx-toolbar-label"; const DATAGRID_GROUP_PANEL_CONTAINER_CLASS = "dx-toolbar-item"; const DATAGRID_EXPAND_CLASS = "dx-datagrid-expand"; const DATAGRID_GROUP_ROW_CLASS = "dx-group-row"; const HEADER_FILTER_CLASS_SELECTOR = ".dx-header-filter"; const dataSourceAdapterExtender = Base => class extends Base { init() { super.init.apply(this, arguments); this._initGroupingHelper() } _initGroupingHelper(options) { const grouping = this._grouping; const isAutoExpandAll = this.option("grouping.autoExpandAll"); const isFocusedRowEnabled = this.option("focusedRowEnabled"); const remoteOperations = options ? options.remoteOperations : this.remoteOperations(); const isODataRemoteOperations = remoteOperations.filtering && remoteOperations.sorting && remoteOperations.paging; if (isODataRemoteOperations && !remoteOperations.grouping && (isAutoExpandAll || !isFocusedRowEnabled)) { if (!grouping || grouping instanceof _m_grouping_collapsed.GroupingHelper) { this._grouping = new _m_grouping_expanded.GroupingHelper(this) } } else if (!grouping || grouping instanceof _m_grouping_expanded.GroupingHelper) { this._grouping = new _m_grouping_collapsed.GroupingHelper(this) } } totalItemsCount() { const totalCount = super.totalItemsCount(); return totalCount > 0 && this._dataSource.group() && this._dataSource.requireTotalCount() ? totalCount + this._grouping.totalCountCorrection() : totalCount } itemsCount() { return this._dataSource.group() ? this._grouping.itemsCount() || 0 : super.itemsCount.apply(this, arguments) } allowCollapseAll() { return this._grouping.allowCollapseAll() } isGroupItemCountable(item) { return this._grouping.isGroupItemCountable(item) } isRowExpanded(key) { const groupInfo = this._grouping.findGroupInfo(key); return groupInfo ? groupInfo.isExpanded : !this._grouping.allowCollapseAll() } collapseAll(groupIndex) { return this._collapseExpandAll(groupIndex, false) } expandAll(groupIndex) { return this._collapseExpandAll(groupIndex, true) } _collapseExpandAll(groupIndex, isExpand) { const that = this; const dataSource = that._dataSource; const group = dataSource.group(); const groups = _m_core.default.normalizeSortingInfo(group || []); if (groups.length) { for (let i = 0; i < groups.length; i++) { if (void 0 === groupIndex || groupIndex === i) { groups[i].isExpanded = isExpand } else if (group && group[i]) { groups[i].isExpanded = group[i].isExpanded } } dataSource.group(groups); that._grouping.foreachGroups(((groupInfo, parents) => { if (void 0 === groupIndex || groupIndex === parents.length - 1) { groupInfo.isExpanded = isExpand } }), false, true); that.resetPagesCache() } return true } refresh() { super.refresh.apply(this, arguments); return this._grouping.refresh.apply(this._grouping, arguments) } changeRowExpand(path) { const that = this; const dataSource = that._dataSource; if (dataSource.group()) { dataSource.beginLoading(); if (that._lastLoadOptions) { that._lastLoadOptions.groupExpand = true } return that._changeRowExpandCore(path).always((() => { dataSource.endLoading() })) } } _changeRowExpandCore(path) { return this._grouping.changeRowExpand(path) } _hasGroupLevelsExpandState(group, isExpanded) { if (group && Array.isArray(group)) { for (let i = 0; i < group.length; i++) { if (group[i].isExpanded === isExpanded) { return true } } } } _customizeRemoteOperations(options, operationTypes) { const { remoteOperations: remoteOperations } = options; if (options.storeLoadOptions.group) { if (remoteOperations.grouping && !options.isCustomLoading) { if (!remoteOperations.groupPaging || this._hasGroupLevelsExpandState(options.storeLoadOptions.group, true)) { remoteOperations.paging = false } } if (!remoteOperations.grouping && (!remoteOperations.sorting || !remoteOperations.filtering || options.isCustomLoading || this._hasGroupLevelsExpandState(options.storeLoadOptions.group, false))) { remoteOperations.paging = false } } else if (!options.isCustomLoading && remoteOperations.paging && operationTypes.grouping) { this.resetCache() } super._customizeRemoteOperations.apply(this, arguments) } _handleDataLoading(options) { super._handleDataLoading(options); this._initGroupingHelper(options); return this._grouping.handleDataLoading(options) } _handleDataLoaded(options) { return this._grouping.handleDataLoaded(options, super._handleDataLoaded.bind(this)) } _handleDataLoadedCore(options) { return this._grouping.handleDataLoadedCore(options, super._handleDataLoadedCore.bind(this)) } }; _m_data_source_adapter.default.extend(dataSourceAdapterExtender); const GroupingDataControllerExtender = Base => class extends Base { init() { super.init(); this.createAction("onRowExpanding"); this.createAction("onRowExpanded"); this.createAction("onRowCollapsing"); this.createAction("onRowCollapsed") } _beforeProcessItems(items) { const groupColumns = this._columnsController.getGroupColumns(); items = super._beforeProcessItems(items); if (items.length && groupColumns.length) { items = this._processGroupItems(items, groupColumns.length) } return items } _processItem(item, options) { if ((0, _type.isDefined)(item.groupIndex) && (0, _type.isString)(item.rowType) && 0 === item.rowType.indexOf("group")) { item = this._processGroupItem(item, options); options.dataIndex = 0 } else { item = super._processItem.apply(this, arguments) } return item } _processGroupItem(item, options) { return item } _processGroupItems(items, groupsCount, options) { const that = this; const groupedColumns = that._columnsController.getGroupColumns(); const column = groupedColumns[groupedColumns.length - groupsCount]; if (!options) { const scrollingMode = that.option("scrolling.mode"); options = { collectContinuationItems: "virtual" !== scrollingMode && "infinite" !== scrollingMode, resultItems: [], path: [], values: [] } } const { resultItems: resultItems } = options; if (options.data) { if (options.collectContinuationItems || !options.data.isContinuation) { resultItems.push({ rowType: "group", data: options.data, groupIndex: options.path.length - 1, isExpanded: !!options.data.items, key: options.path.slice(0), values: options.values.slice(0) }) } } if (items) { if (0 === groupsCount) { resultItems.push.apply(resultItems, items) } else { for (let i = 0; i < items.length; i++) { const item = items[i]; if (item && "items" in item) { options.data = item; options.path.push(item.key); options.values.push(column && column.deserializeValue && !column.calculateDisplayValue ? column.deserializeValue(item.key) : item.key); that._processGroupItems(item.items, groupsCount - 1, options); options.data = void 0; options.path.pop(); options.values.pop() } else { resultItems.push(item) } } } } return resultItems } publicMethods() { return super.publicMethods().concat(["collapseAll", "expandAll", "isRowExpanded", "expandRow", "collapseRow"]) } collapseAll(groupIndex) { const dataSource = this._dataSource; if (dataSource && dataSource.collapseAll(groupIndex)) { dataSource.pageIndex(0); dataSource.reload() } } expandAll(groupIndex) { const dataSource = this._dataSource; if (dataSource && dataSource.expandAll(groupIndex)) { dataSource.pageIndex(0); dataSource.reload() } } changeRowExpand(key) { const that = this; const expanded = that.isRowExpanded(key); const args = { key: key, expanded: expanded }; that.executeAction(expanded ? "onRowCollapsing" : "onRowExpanding", args); if (!args.cancel) { return (0, _deferred.when)(that._changeRowExpandCore(key)).done((() => { args.expanded = !expanded; that.executeAction(expanded ? "onRowCollapsed" : "onRowExpanded", args) })) } return (new _deferred.Deferred).resolve() } _changeRowExpandCore(key) { const that = this; const dataSource = this._dataSource; const d = new _deferred.Deferred; if (!dataSource) { d.resolve() } else { (0, _deferred.when)(dataSource.changeRowExpand(key)).done((() => { that.load().done(d.resolve).fail(d.reject) })).fail(d.reject) } return d } isRowExpanded(key) { const dataSource = this._dataSource; return dataSource && dataSource.isRowExpanded(key) } expandRow(key) { if (!this.isRowExpanded(key)) { return this.changeRowExpand(key) } return (new _deferred.Deferred).resolve() } collapseRow(key) { if (this.isRowExpanded(key)) { return this.changeRowExpand(key) } return (new _deferred.Deferred).resolve() } optionChanged(args) { if ("grouping" === args.name) { args.name = "dataSource" } super.optionChanged(args) } }; const onGroupingMenuItemClick = function(column, params) { const columnsController = this._columnsController; switch (params.itemData.value) { case "group": { const groups = columnsController._dataSource.group() || []; columnsController.columnOption(column.dataField, "groupIndex", groups.length); break } case "ungroup": columnsController.columnOption(column.dataField, "groupIndex", -1); break; case "ungroupAll": this.component.clearGrouping() } }; const isGroupPanelVisible = groupPanelOptions => { const visible = null === groupPanelOptions || void 0 === groupPanelOptions ? void 0 : groupPanelOptions.visible; return "auto" === visible ? "desktop" === _devices.default.current().deviceType : !!visible }; const allowDragging = (groupPanelOptions, column) => { const isVisible = isGroupPanelVisible(groupPanelOptions); const canDrag = (null === groupPanelOptions || void 0 === groupPanelOptions ? void 0 : groupPanelOptions.allowColumnDragging) && column.allowGrouping; return isVisible && !!canDrag }; const GroupingHeaderPanelExtender = Base => class extends Base { _getToolbarItems() { const items = super._getToolbarItems(); return this._appendGroupingItem(items) } _appendGroupingItem(items) { if (this._isGroupPanelVisible()) { let isRendered = false; const toolbarItem = { template: () => { const $groupPanel = (0, _renderer.default)("<div>").addClass("dx-datagrid-group-panel"); this._updateGroupPanelContent($groupPanel); (0, _m_accessibility.registerKeyboardAction)("groupPanel", this, $groupPanel, void 0, this._handleActionKeyDown.bind(this)); return $groupPanel }, name: "groupPanel", onItemRendered: () => { isRendered && this.renderCompleted.fire(); isRendered = true }, location: "before", locateInMenu: "never", sortIndex: 1 }; items.push(toolbarItem); this.updateToolbarDimensions() } return items } _handleActionKeyDown(args) { const { event: event } = args; const $target = (0, _renderer.default)(event.target); const groupColumnIndex = $target.closest(".dx-group-panel-item").index(); const column = this._columnsController.getGroupColumns()[groupColumnIndex]; const columnIndex = column && column.index; if ($target.is(".dx-header-filter")) { this._headerFilterController.showHeaderFilterMenu(columnIndex, true) } else { this._processGroupItemAction(columnIndex) } event.preventDefault() } _isGroupPanelVisible() { return isGroupPanelVisible(this.option("groupPanel")) } _renderGroupPanelItems($groupPanel, groupColumns) { const that = this; $groupPanel.empty(); (0, _iterator.each)(groupColumns, ((index, groupColumn) => { that._createGroupPanelItem($groupPanel, groupColumn) })); (0, _accessibility.restoreFocus)(this) } _createGroupPanelItem($rootElement, groupColumn) { const $groupPanelItem = (0, _renderer.default)("<div>").addClass(groupColumn.cssClass).addClass("dx-group-panel-item").data("columnData", groupColumn).appendTo($rootElement).text(groupColumn.caption); (0, _accessibility.setTabIndex)(this, $groupPanelItem); return $groupPanelItem } _columnOptionChanged(e) { if (!this._requireReady && !_m_core.default.checkChanges(e.optionNames, ["width", "visibleWidth"])) { const $toolbarElement = this.element(); const $groupPanel = $toolbarElement && $toolbarElement.find(".dx-datagrid-group-panel"); if ($groupPanel && $groupPanel.length) { this._updateGroupPanelContent($groupPanel); this.updateToolbarDimensions(); this.renderCompleted.fire() } } super._columnOptionChanged() } _updateGroupPanelContent($groupPanel) { const groupColumns = this.getColumns(); const groupPanelOptions = this.option("groupPanel"); this._renderGroupPanelItems($groupPanel, groupColumns); if (groupPanelOptions.allowColumnDragging && !groupColumns.length) { (0, _renderer.default)("<div>").addClass("dx-group-panel-message").text(groupPanelOptions.emptyPanelText).appendTo($groupPanel); $groupPanel.closest(".dx-toolbar-item").addClass("dx-toolbar-label"); $groupPanel.closest(".dx-toolbar-label").css("maxWidth", "none") } } allowDragging(column) { const groupPanelOptions = this.option("groupPanel"); return allowDragging(groupPanelOptions, column) } getColumnElements() { const $element = this.element(); return $element && $element.find(".dx-group-panel-item") } getColumns() { return this._columnsController.getGroupColumns() } getBoundingRect() { const $element = this.element(); if ($element && $element.find(".dx-datagrid-group-panel").length) { const offset = $element.offset(); return { top: offset.top, bottom: offset.top + (0, _size.getHeight)($element) } } return null } getName() { return "group" } getContextMenuItems(options) { const that = this; const contextMenuEnabled = that.option("grouping.contextMenuEnabled"); const $groupedColumnElement = (0, _renderer.default)(options.targetElement).closest(".dx-group-panel-item"); let items; if ($groupedColumnElement.length) { options.column = $groupedColumnElement.data("columnData") } if (contextMenuEnabled && options.column) { const { column: column } = options; const isGroupingAllowed = (0, _type.isDefined)(column.allowGrouping) ? column.allowGrouping : true; if (isGroupingAllowed) { const isColumnGrouped = (0, _type.isDefined)(column.groupIndex) && column.groupIndex > -1; const groupingTexts = that.option("grouping.texts"); const onItemClick = onGroupingMenuItemClick.bind(that, column); items = [{ text: groupingTexts.ungroup, value: "ungroup", disabled: !isColumnGrouped, onItemClick: onItemClick }, { text: groupingTexts.ungroupAll, value: "ungroupAll", onItemClick: onItemClick }] } } return items } hasGroupedColumns() { return this._isGroupPanelVisible() && !!this.getColumns().length } optionChanged(args) { if ("groupPanel" === args.name) { this._invalidate(); args.handled = true } else { super.optionChanged(args) } } }; exports.GroupingHeaderPanelExtender = GroupingHeaderPanelExtender; const GroupingRowsViewExtender = Base => class extends Base { getContextMenuItems(options) { const that = this; const contextMenuEnabled = that.option("grouping.contextMenuEnabled"); let items; if (contextMenuEnabled && options.row && "group" === options.row.rowType) { const columnsController = that._columnsController; const column = columnsController.columnOption(`groupIndex:${options.row.groupIndex}`); if (column && column.allowGrouping) { const groupingTexts = that.option("grouping.texts"); const onItemClick = onGroupingMenuItemClick.bind(that, column); items = []; items.push({ text: groupingTexts.ungroup, value: "ungroup", onItemClick: onItemClick }, { text: groupingTexts.ungroupAll, value: "ungroupAll", onItemClick: onItemClick }) } } return items } _rowClick(e) { const that = this; const expandMode = that.option("grouping.expandMode"); const scrollingMode = that.option("scrolling.mode"); const isGroupRowStateChanged = "infinite" !== scrollingMode && "rowClick" === expandMode && (0, _renderer.default)(e.event.target).closest(".dx-group-row").length; const isExpandButtonClicked = (0, _renderer.default)(e.event.target).closest(".dx-datagrid-expand").length; if (isGroupRowStateChanged || isExpandButtonClicked) { that._changeGroupRowState(e) } super._rowClick(e) } _changeGroupRowState(e) { const row = this._dataController.items()[e.rowIndex]; const allowCollapsing = this._columnsController.columnOption(`groupIndex:${row.groupIndex}`, "allowCollapsing"); if ("data" === row.rowType || "group" === row.rowType && false !== allowCollapsing) { this._dataController.changeRowExpand(row.key, true); e.event.preventDefault(); e.handled = true } } }; const columnHeadersViewExtender = Base => class extends Base { getContextMenuItems(options) { const that = this; const contextMenuEnabled = that.option("grouping.contextMenuEnabled"); let items = super.getContextMenuItems(options); if (contextMenuEnabled && options.row && ("header" === options.row.rowType || "detailAdaptive" === options.row.rowType)) { const { column: column } = options; if (!column.command && (!(0, _type.isDefined)(column.allowGrouping) || column.allowGrouping)) { const groupingTexts = that.option("grouping.texts"); const isColumnGrouped = (0, _type.isDefined)(column.groupIndex) && column.groupIndex > -1; const onItemClick = onGroupingMenuItemClick.bind(that, column); items = items || []; items.push({ text: groupingTexts.groupByThisColumn, value: "group", beginGroup: true, disabled: isColumnGrouped, onItemClick: onItemClick }); if (column.showWhenGrouped) { items.push({ text: groupingTexts.ungroup, value: "ungroup", disabled: !isColumnGrouped, onItemClick: onItemClick }) } items.push({ text: groupingTexts.ungroupAll, value: "ungroupAll", onItemClick: onItemClick }) } } return items } allowDragging(column) { const groupPanelOptions = this.option("groupPanel"); return allowDragging(groupPanelOptions, column) || super.allowDragging(column) } }; _m_core.default.registerModule("grouping", { defaultOptions: () => ({ grouping: { autoExpandAll: true, allowCollapsing: true, contextMenuEnabled: false, expandMode: "buttonClick", texts: { groupContinuesMessage: _message.default.format("dxDataGrid-groupContinuesMessage"), groupContinuedMessage: _message.default.format("dxDataGrid-groupContinuedMessage"), groupByThisColumn: _message.default.format("dxDataGrid-groupHeaderText"), ungroup: _message.default.format("dxDataGrid-ungroupHeaderText"), ungroupAll: _message.default.format("dxDataGrid-ungroupAllText") } }, groupPanel: { visible: false, emptyPanelText: _message.default.format("dxDataGrid-groupPanelEmptyText"), allowColumnDragging: true } }), extenders: { controllers: { data: GroupingDataControllerExtender, columns: Base => class extends Base { _getExpandColumnOptions() { const options = super._getExpandColumnOptions.apply(this, arguments); options.cellTemplate = _m_core.default.getExpandCellTemplate(); return options } }, editing: Base => class extends Base { _isProcessedItem(item) { return (0, _type.isDefined)(item.groupIndex) && (0, _type.isString)(item.rowType) && 0 === item.rowType.indexOf("group") } } }, views: { headerPanel: GroupingHeaderPanelExtender, rowsView: GroupingRowsViewExtender, columnHeadersView: columnHeadersViewExtender } } });