UNPKG

ag-grid-enterprise

Version:

ag-Grid Enterprise Features

446 lines (445 loc) 22.8 kB
// ag-grid-enterprise v19.1.4 "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); var ag_grid_community_1 = require("ag-grid-community"); var GroupStage = /** @class */ (function () { function GroupStage() { // we use a sequence variable so that each time we do a grouping, we don't // reuse the ids - otherwise the rowRenderer will confuse rowNodes between redraws // when it tries to animate between rows. we set to -1 as others row id 0 will be shared // with the other rows. this.groupIdSequence = new ag_grid_community_1.NumberSequence(1); } // when grouping, these items are of note: // rowNode.parent: RowNode: set to the parent // rowNode.childrenAfterGroup: RowNode[] = the direct children of this group // rowNode.childrenMapped: string=>RowNode = children mapped by group key (when groups) or an empty map if leaf group (this is then used by pivot) // for leaf groups, rowNode.childrenAfterGroup = rowNode.allLeafChildren; GroupStage.prototype.postConstruct = function () { this.usingTreeData = this.gridOptionsWrapper.isTreeData(); if (this.usingTreeData) { this.getDataPath = this.gridOptionsWrapper.getDataPathFunc(); } }; GroupStage.prototype.execute = function (params) { var details = this.createGroupingDetails(params); if (details.transaction) { this.handleTransaction(details); } else { this.shotgunResetEverything(details); } this.sortGroupsWithComparator(details.rootNode); this.selectableService.updateSelectableAfterGrouping(details.rootNode); }; GroupStage.prototype.createGroupingDetails = function (params) { var rowNode = params.rowNode, changedPath = params.changedPath, rowNodeTransaction = params.rowNodeTransaction, rowNodeOrder = params.rowNodeOrder; var groupedCols = this.usingTreeData ? null : this.columnController.getRowGroupColumns(); var isGrouping = this.usingTreeData || (groupedCols && groupedCols.length > 0); var usingTransaction = isGrouping && ag_grid_community_1._.exists(rowNodeTransaction); var details = { // someone complained that the parent attribute was causing some change detection // to break is some angular add-on - which i never used. taking the parent out breaks // a cyclic dependency, hence this flag got introduced. includeParents: !this.gridOptionsWrapper.isSuppressParentsInRowNodes(), expandByDefault: this.gridOptionsWrapper.isGroupSuppressRow() ? -1 : this.gridOptionsWrapper.getGroupDefaultExpanded(), groupedCols: groupedCols, rootNode: rowNode, pivotMode: this.columnController.isPivotMode(), groupedColCount: this.usingTreeData || !groupedCols ? 0 : groupedCols.length, rowNodeOrder: rowNodeOrder, // important not to do transaction if we are not grouping, as otherwise the 'insert index' is ignored. // ie, if not grouping, then we just want to shotgun so the rootNode.allLeafChildren gets copied // to rootNode.childrenAfterGroup and maintaining order (as delta transaction misses the order). transaction: usingTransaction ? rowNodeTransaction : null, // if no transaction, then it's shotgun, changed path would be 'not active' at this point anyway changedPath: changedPath }; return details; }; GroupStage.prototype.handleTransaction = function (details) { var tran = details.transaction; if (tran.add) { this.insertNodes(tran.add, details); } if (tran.update) { this.moveNodesInWrongPath(tran.update, details); } if (tran.remove) { this.removeNodes(tran.remove, details); } if (details.rowNodeOrder) { this.recursiveSortChildren(details.rootNode, details); } }; // this is used when doing delta updates, eg Redux, keeps nodes in right order GroupStage.prototype.recursiveSortChildren = function (node, details) { var _this = this; ag_grid_community_1._.sortRowNodesByOrder(node.childrenAfterGroup, details.rowNodeOrder); node.childrenAfterGroup.forEach(function (childNode) { if (childNode.childrenAfterGroup) { _this.recursiveSortChildren(childNode, details); } }); }; GroupStage.prototype.sortGroupsWithComparator = function (rootNode) { // we don't do group sorting for tree data if (this.usingTreeData) { return; } var comparator = this.gridOptionsWrapper.getDefaultGroupSortComparator(); if (ag_grid_community_1._.exists(comparator)) { recursiveSort(rootNode); } function recursiveSort(rowNode) { var doSort = ag_grid_community_1._.exists(rowNode.childrenAfterGroup) && // we only want to sort groups, so we do not sort leafs (a leaf group has leafs as children) !rowNode.leafGroup; if (doSort) { rowNode.childrenAfterGroup.sort(comparator); rowNode.childrenAfterGroup.forEach(function (childNode) { return recursiveSort(childNode); }); } } }; GroupStage.prototype.getExistingPathForNode = function (node, details) { var res = []; // when doing tree data, the node is part of the path, // but when doing grid grouping, the node is not part of the path so we start with the parent. var pointer = this.usingTreeData ? node : node.parent; while (pointer && pointer !== details.rootNode) { res.push({ key: pointer.key, rowGroupColumn: pointer.rowGroupColumn, field: pointer.field }); pointer = pointer.parent; } res.reverse(); return res; }; GroupStage.prototype.moveNodesInWrongPath = function (childNodes, details) { var _this = this; childNodes.forEach(function (childNode) { // we add node, even if parent has not changed, as the data could have // changed, hence aggregations will be wrong if (details.changedPath.isActive()) { details.changedPath.addParentNode(childNode.parent); } var infoToKeyMapper = function (item) { return item.key; }; var oldPath = _this.getExistingPathForNode(childNode, details).map(infoToKeyMapper); var newPath = _this.getGroupInfo(childNode, details).map(infoToKeyMapper); var nodeInCorrectPath = ag_grid_community_1._.compareArrays(oldPath, newPath); if (!nodeInCorrectPath) { _this.moveNode(childNode, details); } }); }; GroupStage.prototype.moveNode = function (childNode, details) { this.removeOneNode(childNode, details); this.insertOneNode(childNode, details); // hack - if we didn't do this, then renaming a tree item (ie changing rowNode.key) wouldn't get // refreshed into the gui. // this is needed to kick off the event that rowComp listens to for refresh. this in turn // then will get each cell in the row to refresh - which is what we need as we don't know which // columns will be displaying the rowNode.key info. childNode.setData(childNode.data); // we add both old and new parents to changed path, as both will need to be refreshed. // we already added the old parent (in calling method), so just add the new parent here if (details.changedPath.isActive()) { var newParent = childNode.parent; details.changedPath.addParentNode(newParent); } }; GroupStage.prototype.removeNodes = function (leafRowNodes, details) { var _this = this; leafRowNodes.forEach(function (leafToRemove) { _this.removeOneNode(leafToRemove, details); if (details.changedPath.isActive()) { details.changedPath.addParentNode(leafToRemove.parent); } }); }; GroupStage.prototype.removeOneNode = function (childNode, details) { var _this = this; // utility func to execute once on each parent node var forEachParentGroup = function (callback) { var pointer = childNode.parent; while (pointer && pointer !== details.rootNode) { callback(pointer); pointer = pointer.parent; } }; // remove leaf from direct parent this.removeFromParent(childNode); // remove from allLeafChildren forEachParentGroup(function (parentNode) { return ag_grid_community_1._.removeFromArray(parentNode.allLeafChildren, childNode); }); // if not group, and children are present, need to move children to a group. // otherwise if no children, we can just remove without replacing. var replaceWithGroup = childNode.hasChildren(); if (replaceWithGroup) { var oldPath = this.getExistingPathForNode(childNode, details); // because we just removed the userGroup, this will always return new support group var newGroupNode_1 = this.findParentForNode(childNode, oldPath, details); // these properties are the ones that will be incorrect in the newly created group, // so copy them form the old childNode newGroupNode_1.expanded = childNode.expanded; newGroupNode_1.allLeafChildren = childNode.allLeafChildren; newGroupNode_1.childrenAfterGroup = childNode.childrenAfterGroup; newGroupNode_1.childrenMapped = childNode.childrenMapped; newGroupNode_1.childrenAfterGroup.forEach(function (rowNode) { return rowNode.parent = newGroupNode_1; }); } // remove empty groups forEachParentGroup(function (node) { if (node.isEmptyFillerNode()) { _this.removeFromParent(node); // we remove selection on filler nodes here, as the selection would not be removed // from the RowNodeManager, as filler nodes don't exist on teh RowNodeManager node.setSelected(false); } }); }; GroupStage.prototype.removeFromParent = function (child) { if (child.parent) { ag_grid_community_1._.removeFromArray(child.parent.childrenAfterGroup, child); } var mapKey = this.getChildrenMappedKey(child.key, child.rowGroupColumn); if (child.parent && child.parent.childrenMapped) { child.parent.childrenMapped[mapKey] = undefined; } // this is important for transition, see rowComp removeFirstPassFuncs. when doing animation and // remove, if rowTop is still present, the rowComp thinks it's just moved position. child.setRowTop(null); }; GroupStage.prototype.addToParent = function (child, parent) { var mapKey = this.getChildrenMappedKey(child.key, child.rowGroupColumn); if (parent) { if (parent.childrenMapped) { parent.childrenMapped[mapKey] = child; } parent.childrenAfterGroup.push(child); } }; GroupStage.prototype.shotgunResetEverything = function (details) { // because we are not creating the root node each time, we have the logic // here to change leafGroup once. // we set .leafGroup to false for tree data, as .leafGroup is only used when pivoting, and pivoting // isn't allowed with treeData, so the grid never actually use .leafGroup when doing treeData. details.rootNode.leafGroup = this.usingTreeData ? false : details.groupedCols.length === 0; // we are going everything from scratch, so reset childrenAfterGroup and childrenMapped from the rootNode details.rootNode.childrenAfterGroup = []; details.rootNode.childrenMapped = {}; this.insertNodes(details.rootNode.allLeafChildren, details); }; GroupStage.prototype.insertNodes = function (newRowNodes, details) { var _this = this; newRowNodes.forEach(function (rowNode) { _this.insertOneNode(rowNode, details); if (details.changedPath.isActive()) { details.changedPath.addParentNode(rowNode.parent); } }); }; GroupStage.prototype.insertOneNode = function (childNode, details) { var path = this.getGroupInfo(childNode, details); var parentGroup = this.findParentForNode(childNode, path, details); if (!parentGroup.group) { console.warn("ag-Grid: duplicate group keys for row data, keys should be unique", [parentGroup.data, childNode.data]); } if (this.usingTreeData) { this.swapGroupWithUserNode(parentGroup, childNode); } else { childNode.parent = parentGroup; childNode.level = path.length; parentGroup.childrenAfterGroup.push(childNode); } }; GroupStage.prototype.findParentForNode = function (childNode, path, details) { var _this = this; var nextNode = details.rootNode; path.forEach(function (groupInfo, level) { nextNode = _this.getOrCreateNextNode(nextNode, groupInfo, level, details); // node gets added to all group nodes. // note: we do not add to rootNode here, as the rootNode is the master list of rowNodes nextNode.allLeafChildren.push(childNode); }); return nextNode; }; GroupStage.prototype.swapGroupWithUserNode = function (fillerGroup, userGroup) { userGroup.parent = fillerGroup.parent; userGroup.key = fillerGroup.key; userGroup.field = fillerGroup.field; userGroup.groupData = fillerGroup.groupData; userGroup.level = fillerGroup.level; userGroup.expanded = fillerGroup.expanded; // we set .leafGroup to false for tree data, as .leafGroup is only used when pivoting, and pivoting // isn't allowed with treeData, so the grid never actually use .leafGroup when doing treeData. userGroup.leafGroup = fillerGroup.leafGroup; // always null for userGroups, as row grouping is not allowed when doing tree data userGroup.rowGroupIndex = fillerGroup.rowGroupIndex; userGroup.allLeafChildren = fillerGroup.allLeafChildren; userGroup.childrenAfterGroup = fillerGroup.childrenAfterGroup; userGroup.childrenMapped = fillerGroup.childrenMapped; this.removeFromParent(fillerGroup); userGroup.childrenAfterGroup.forEach(function (rowNode) { return rowNode.parent = userGroup; }); this.addToParent(userGroup, fillerGroup.parent); }; GroupStage.prototype.getOrCreateNextNode = function (parentGroup, groupInfo, level, details) { var mapKey = this.getChildrenMappedKey(groupInfo.key, groupInfo.rowGroupColumn); var nextNode = parentGroup.childrenMapped ? parentGroup.childrenMapped[mapKey] : undefined; if (!nextNode) { nextNode = this.createGroup(groupInfo, parentGroup, level, details); // attach the new group to the parent this.addToParent(nextNode, parentGroup); } return nextNode; }; GroupStage.prototype.createGroup = function (groupInfo, parent, level, details) { var _this = this; var groupNode = new ag_grid_community_1.RowNode(); this.context.wireBean(groupNode); groupNode.group = true; groupNode.field = groupInfo.field; groupNode.rowGroupColumn = groupInfo.rowGroupColumn; groupNode.groupData = {}; var groupDisplayCols = this.columnController.getGroupDisplayColumns(); groupDisplayCols.forEach(function (col) { // newGroup.rowGroupColumn=null when working off GroupInfo, and we always display the group in the group column // if rowGroupColumn is present, then it's grid row grouping and we only include if configuration says so var displayGroupForCol = _this.usingTreeData || (groupNode.rowGroupColumn ? col.isRowGroupDisplayed(groupNode.rowGroupColumn.getId()) : false); if (displayGroupForCol) { groupNode.groupData[col.getColId()] = groupInfo.key; } }); // we use negative number for the ids of the groups, this makes sure we don't clash with the // id's of the leaf nodes. groupNode.id = (this.groupIdSequence.next() * -1).toString(); groupNode.key = groupInfo.key; groupNode.level = level; groupNode.leafGroup = this.usingTreeData ? false : level === (details.groupedColCount - 1); // if doing pivoting, then the leaf group is never expanded, // as we do not show leaf rows if (details.pivotMode && groupNode.leafGroup) { groupNode.expanded = false; } else { groupNode.expanded = this.isExpanded(details.expandByDefault, level); } groupNode.allLeafChildren = []; // why is this done here? we are not updating the children could as we go, // i suspect this is updated in the filter stage groupNode.setAllChildrenCount(0); groupNode.rowGroupIndex = this.usingTreeData ? null : level; groupNode.childrenAfterGroup = []; groupNode.childrenMapped = {}; groupNode.parent = details.includeParents ? parent : null; return groupNode; }; GroupStage.prototype.getChildrenMappedKey = function (key, rowGroupColumn) { if (rowGroupColumn) { // grouping by columns return rowGroupColumn.getId() + '-' + key; } else { // tree data - we don't have rowGroupColumns return key; } }; GroupStage.prototype.isExpanded = function (expandByDefault, level) { if (expandByDefault === -1) { return true; } else { return level < expandByDefault; } }; GroupStage.prototype.getGroupInfo = function (rowNode, details) { if (this.usingTreeData) { return this.getGroupInfoFromCallback(rowNode); } else { return this.getGroupInfoFromGroupColumns(rowNode, details); } }; GroupStage.prototype.getGroupInfoFromCallback = function (rowNode) { var keys = this.getDataPath ? this.getDataPath(rowNode.data) : null; if (keys === null || keys === undefined || keys.length === 0) { ag_grid_community_1._.doOnce(function () { return console.warn("getDataPath() should not return an empty path for data", rowNode.data); }, 'groupStage.getGroupInfoFromCallback'); } var groupInfoMapper = function (key) { return ({ key: key, field: null, rowGroupColumn: null }); }; return keys ? keys.map(groupInfoMapper) : []; }; GroupStage.prototype.getGroupInfoFromGroupColumns = function (rowNode, details) { var _this = this; var res = []; details.groupedCols.forEach(function (groupCol) { var key = _this.valueService.getKeyForNode(groupCol, rowNode); var keyExists = key !== null && key !== undefined; // unbalanced tree and pivot mode don't work together - not because of the grid, it doesn't make // mathematical sense as you are building up a cube. so if pivot mode, we put in a blank key where missing. // this keeps the tree balanced and hence can be represented as a group. if (details.pivotMode && !keyExists) { key = ' '; keyExists = true; } if (keyExists) { var item = { key: key, field: groupCol.getColDef().field, rowGroupColumn: groupCol }; res.push(item); } }); return res; }; __decorate([ ag_grid_community_1.Autowired('selectionController'), __metadata("design:type", ag_grid_community_1.SelectionController) ], GroupStage.prototype, "selectionController", void 0); __decorate([ ag_grid_community_1.Autowired('gridOptionsWrapper'), __metadata("design:type", ag_grid_community_1.GridOptionsWrapper) ], GroupStage.prototype, "gridOptionsWrapper", void 0); __decorate([ ag_grid_community_1.Autowired('columnController'), __metadata("design:type", ag_grid_community_1.ColumnController) ], GroupStage.prototype, "columnController", void 0); __decorate([ ag_grid_community_1.Autowired('selectableService'), __metadata("design:type", ag_grid_community_1.SelectableService) ], GroupStage.prototype, "selectableService", void 0); __decorate([ ag_grid_community_1.Autowired('valueService'), __metadata("design:type", ag_grid_community_1.ValueService) ], GroupStage.prototype, "valueService", void 0); __decorate([ ag_grid_community_1.Autowired('eventService'), __metadata("design:type", ag_grid_community_1.EventService) ], GroupStage.prototype, "eventService", void 0); __decorate([ ag_grid_community_1.Autowired('context'), __metadata("design:type", ag_grid_community_1.Context) ], GroupStage.prototype, "context", void 0); __decorate([ ag_grid_community_1.PostConstruct, __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], GroupStage.prototype, "postConstruct", null); GroupStage = __decorate([ ag_grid_community_1.Bean('groupStage') ], GroupStage); return GroupStage; }()); exports.GroupStage = GroupStage;