UNPKG

6.66 kBPlain TextView Raw
1import {RowNode} from "../entities/rowNode";
2import {Column} from "../entities/column";
3import {Autowired, Bean, PostConstruct} from "../context/context";
4import {SortController} from "../sortController";
5import {_} from "../utils";
6import {ValueService} from "../valueService/valueService";
7import {GridOptionsWrapper} from "../gridOptionsWrapper";
8import {ColumnController} from "../columnController/columnController";
9
10export interface SortOption {
11 inverter: number;
12 column: Column;
13}
14
15export interface SortedRowNode {
16 currentPos: number;
17 rowNode: RowNode;
18}
19
20@Bean('sortService')
21export class SortService {
22
23 @Autowired('sortController') private sortController: SortController;
24 @Autowired('columnController') private columnController: ColumnController;
25 @Autowired('valueService') private valueService: ValueService;
26 @Autowired('gridOptionsWrapper') private gridOptionsWrapper: GridOptionsWrapper;
27
28 private postSortFunc: (rowNodes: RowNode[]) => void;
29
30 @PostConstruct
31 public init(): void {
32 this.postSortFunc = this.gridOptionsWrapper.getPostSortFunc();
33 }
34
35 public sortAccordingToColumnsState(rowNode: RowNode) {
36 let sortOptions: SortOption[] = this.sortController.getSortForRowController();
37 this.sort(rowNode, sortOptions);
38 }
39
40 public sort(rowNode: RowNode, sortOptions: SortOption[]) {
41 rowNode.childrenAfterSort = rowNode.childrenAfterFilter.slice(0);
42
43 // we clear out the 'pull down open parents' first, as the values mix up the sorting
44 this.pullDownDataForHideOpenParents(rowNode, true);
45
46 let sortActive = _.exists(sortOptions) && sortOptions.length > 0;
47 if (sortActive) {
48 // RE https://ag-grid.atlassian.net/browse/AG-444
49 //Javascript sort is non deterministic when all the array items are equals
50 //ie Comparator always returns 0, so if you want to ensure the array keeps its
51 //order, then you need to add an additional sorting condition manually, in this
52 //case we are going to inspect the original array position
53 let sortedRowNodes: SortedRowNode[] = rowNode.childrenAfterSort.map((it, pos) => {
54 return {currentPos: pos, rowNode: it};
55 });
56 sortedRowNodes.sort(this.compareRowNodes.bind(this, sortOptions));
57 rowNode.childrenAfterSort = sortedRowNodes.map(sorted => sorted.rowNode);
58 }
59
60 this.updateChildIndexes(rowNode);
61 this.pullDownDataForHideOpenParents(rowNode, false);
62
63 // sort any groups recursively
64 rowNode.childrenAfterFilter.forEach(child => {
65 if (child.hasChildren()) {
66 this.sort(child, sortOptions);
67 }
68 });
69
70 if (this.postSortFunc) {
71 this.postSortFunc(rowNode.childrenAfterSort);
72 }
73 }
74
75 private compareRowNodes(sortOptions: any, sortedNodeA: SortedRowNode, sortedNodeB: SortedRowNode) {
76 let nodeA: RowNode = sortedNodeA.rowNode;
77 let nodeB: RowNode = sortedNodeB.rowNode;
78
79 // Iterate columns, return the first that doesn't match
80 for (let i = 0, len = sortOptions.length; i < len; i++) {
81 let sortOption = sortOptions[i];
82 // let compared = compare(nodeA, nodeB, sortOption.column, sortOption.inverter === -1);
83
84 let isInverted = sortOption.inverter === -1;
85 let valueA: any = this.getValue(nodeA, sortOption.column);
86 let valueB: any = this.getValue(nodeB, sortOption.column);
87 let comparatorResult: number;
88 if (sortOption.column.getColDef().comparator) {
89 //if comparator provided, use it
90 comparatorResult = sortOption.column.getColDef().comparator(valueA, valueB, nodeA, nodeB, isInverted);
91 } else {
92 //otherwise do our own comparison
93 comparatorResult = _.defaultComparator(valueA, valueB, this.gridOptionsWrapper.isAccentedSort());
94 }
95
96 if (comparatorResult !== 0) {
97 return comparatorResult * sortOption.inverter;
98 }
99 }
100 // All matched, we make is so that the original sort order is kept:
101 return sortedNodeA.currentPos - sortedNodeB.currentPos;
102 }
103
104 private getValue(nodeA: RowNode, column: Column): string {
105 return this.valueService.getValue(column, nodeA);
106 }
107
108 private updateChildIndexes(rowNode: RowNode) {
109 if (_.missing(rowNode.childrenAfterSort)) {
110 return;
111 }
112
113 rowNode.childrenAfterSort.forEach((child: RowNode, index: number) => {
114 let firstChild = index === 0;
115 let lastChild = index === rowNode.childrenAfterSort.length - 1;
116 child.setFirstChild(firstChild);
117 child.setLastChild(lastChild);
118 child.setChildIndex(index);
119 });
120 }
121
122 private pullDownDataForHideOpenParents(rowNode: RowNode, clearOperation: boolean) {
123 if (_.missing(rowNode.childrenAfterSort)) {
124 return;
125 }
126
127 if (!this.gridOptionsWrapper.isGroupHideOpenParents()) {
128 return;
129 }
130
131 rowNode.childrenAfterSort.forEach( childRowNode => {
132
133 let groupDisplayCols = this.columnController.getGroupDisplayColumns();
134 groupDisplayCols.forEach( groupDisplayCol => {
135
136 let showRowGroup = groupDisplayCol.getColDef().showRowGroup;
137 if (typeof showRowGroup !== 'string') {
138 console.error('ag-Grid: groupHideOpenParents only works when specifying specific columns for colDef.showRowGroup');
139 return;
140 }
141 let displayingGroupKey: string = <string> showRowGroup;
142
143 let rowGroupColumn = this.columnController.getPrimaryColumn(displayingGroupKey);
144
145 let thisRowNodeMatches = rowGroupColumn === childRowNode.rowGroupColumn;
146 if (thisRowNodeMatches) { return; }
147
148 if (clearOperation) {
149 // if doing a clear operation, we clear down the value for every possible group column
150 childRowNode.setGroupValue(groupDisplayCol.getId(), null);
151 } else {
152 // if doing a set operation, we set only where the pull down is to occur
153 let parentToStealFrom = childRowNode.getFirstChildOfFirstChild(rowGroupColumn);
154 if (parentToStealFrom) {
155 childRowNode.setGroupValue(groupDisplayCol.getId(), parentToStealFrom.key);
156 }
157 }
158 });
159 });
160 }
161
162}
\No newline at end of file