UNPKG

13.8 kBPlain TextView Raw
1import {Component} from "../widgets/component";
2import {Autowired, Context, PostConstruct} from "../context/context";
3import {GridOptionsWrapper} from "../gridOptionsWrapper";
4import {ColumnGroupChild} from "../entities/columnGroupChild";
5import {ColumnGroup} from "../entities/columnGroup";
6import {ColumnController} from "../columnController/columnController";
7import {Column} from "../entities/column";
8import {DropTarget} from "../dragAndDrop/dragAndDropService";
9import {EventService} from "../eventService";
10import {Events} from "../events";
11import {Promise, Utils as _} from "../utils";
12import {HeaderWrapperComp} from "./header/headerWrapperComp";
13import {HeaderGroupWrapperComp} from "./headerGroup/headerGroupWrapperComp";
14import {FilterManager} from "../filter/filterManager";
15import {IFloatingFilterWrapperComp} from "../filter/floatingFilterWrapper";
16import {IComponent} from "../interfaces/iComponent";
17import {FloatingFilterChange, IFloatingFilterParams} from "../filter/floatingFilter";
18import {ComponentRecipes} from "../components/framework/componentRecipes";
19import {IFilterComp} from "../interfaces/iFilter";
20import {GridApi} from "../gridApi";
21import {CombinedFilter} from "../filter/baseFilter";
22
23export enum HeaderRowType {
24 COLUMN_GROUP, COLUMN, FLOATING_FILTER
25}
26
27export class HeaderRowComp extends Component {
28
29 @Autowired('gridOptionsWrapper') private gridOptionsWrapper: GridOptionsWrapper;
30 @Autowired('gridApi') private gridApi: GridApi;
31 @Autowired('columnController') private columnController: ColumnController;
32 @Autowired('context') private context: Context;
33 @Autowired('eventService') private eventService: EventService;
34 @Autowired('filterManager') private filterManager: FilterManager;
35 @Autowired('componentRecipes') private componentRecipes: ComponentRecipes;
36
37 private dept: number;
38 private pinned: string;
39
40 private headerComps: { [key: string]: IComponent<any> } = {};
41
42 private dropTarget: DropTarget;
43
44 private type: HeaderRowType;
45
46 constructor(dept: number, type: HeaderRowType, pinned: string, dropTarget: DropTarget) {
47 super(`<div class="ag-header-row" role="presentation"/>`);
48 this.dept = dept;
49 this.type = type;
50 this.pinned = pinned;
51 this.dropTarget = dropTarget;
52 }
53
54 public forEachHeaderElement(callback: (comp: IComponent<any>) => void): void {
55 Object.keys(this.headerComps).forEach(key => {
56 callback(this.headerComps[key]);
57 });
58 }
59
60 public destroy(): void {
61 let idsOfAllChildren = Object.keys(this.headerComps);
62 this.removeAndDestroyChildComponents(idsOfAllChildren);
63 super.destroy();
64 }
65
66 private removeAndDestroyChildComponents(idsToDestroy: string[]): void {
67 idsToDestroy.forEach(id => {
68 let childHeaderComp: IComponent<any> = this.headerComps[id];
69 this.getGui().removeChild(childHeaderComp.getGui());
70 if (childHeaderComp.destroy) {
71 childHeaderComp.destroy();
72 }
73 delete this.headerComps[id];
74 });
75 }
76
77 private onRowHeightChanged(): void {
78 let headerRowCount = this.columnController.getHeaderRowCount();
79 let sizes: number[] = [];
80
81 let numberOfFloating = 0;
82 let groupHeight: number;
83 let headerHeight: number;
84 if (!this.columnController.isPivotMode()) {
85 if (this.gridOptionsWrapper.isFloatingFilter()) {
86 headerRowCount++;
87 }
88 numberOfFloating = (this.gridOptionsWrapper.isFloatingFilter()) ? 1 : 0;
89 groupHeight = this.gridOptionsWrapper.getGroupHeaderHeight();
90 headerHeight = this.gridOptionsWrapper.getHeaderHeight();
91 } else {
92 numberOfFloating = 0;
93 groupHeight = this.gridOptionsWrapper.getPivotGroupHeaderHeight();
94 headerHeight = this.gridOptionsWrapper.getPivotHeaderHeight();
95 }
96 let numberOfNonGroups = 1 + numberOfFloating;
97 let numberOfGroups = headerRowCount - numberOfNonGroups;
98
99 for (let i = 0; i < numberOfGroups; i++) { sizes.push(groupHeight); }
100 sizes.push(headerHeight);
101 for (let i = 0; i < numberOfFloating; i++) { sizes.push(this.gridOptionsWrapper.getFloatingFiltersHeight()); }
102
103 let rowHeight = 0;
104 for (let i = 0; i < this.dept; i++) { rowHeight += sizes[i]; }
105
106 this.getGui().style.top = rowHeight + 'px';
107 this.getGui().style.height = sizes[this.dept] + 'px';
108 }
109
110 //noinspection JSUnusedLocalSymbols
111 @PostConstruct
112 private init(): void {
113
114 this.onRowHeightChanged();
115 this.onVirtualColumnsChanged();
116 this.setWidth();
117
118 this.addDestroyableEventListener(this.gridOptionsWrapper, GridOptionsWrapper.PROP_HEADER_HEIGHT, this.onRowHeightChanged.bind(this));
119 this.addDestroyableEventListener(this.gridOptionsWrapper, GridOptionsWrapper.PROP_PIVOT_HEADER_HEIGHT, this.onRowHeightChanged.bind(this));
120
121 this.addDestroyableEventListener(this.gridOptionsWrapper, GridOptionsWrapper.PROP_GROUP_HEADER_HEIGHT, this.onRowHeightChanged.bind(this));
122 this.addDestroyableEventListener(this.gridOptionsWrapper, GridOptionsWrapper.PROP_PIVOT_GROUP_HEADER_HEIGHT, this.onRowHeightChanged.bind(this));
123
124 this.addDestroyableEventListener(this.gridOptionsWrapper, GridOptionsWrapper.PROP_FLOATING_FILTERS_HEIGHT, this.onRowHeightChanged.bind(this));
125
126 this.addDestroyableEventListener(this.eventService, Events.EVENT_VIRTUAL_COLUMNS_CHANGED, this.onVirtualColumnsChanged.bind(this));
127 this.addDestroyableEventListener(this.eventService, Events.EVENT_DISPLAYED_COLUMNS_CHANGED, this.onDisplayedColumnsChanged.bind(this));
128 this.addDestroyableEventListener(this.eventService, Events.EVENT_COLUMN_RESIZED, this.onColumnResized.bind(this));
129 this.addDestroyableEventListener(this.eventService, Events.EVENT_GRID_COLUMNS_CHANGED, this.onGridColumnsChanged.bind(this));
130 }
131
132 private onColumnResized(): void {
133 this.setWidth();
134 }
135
136 private setWidth(): void {
137 let mainRowWidth = this.columnController.getContainerWidth(this.pinned) + 'px';
138 this.getGui().style.width = mainRowWidth;
139 }
140
141 private onGridColumnsChanged(): void {
142 this.removeAndDestroyAllChildComponents();
143 }
144
145 private removeAndDestroyAllChildComponents(): void {
146 let idsOfAllChildren = Object.keys(this.headerComps);
147 this.removeAndDestroyChildComponents(idsOfAllChildren);
148 }
149
150 private onDisplayedColumnsChanged(): void {
151 this.onVirtualColumnsChanged();
152 this.setWidth();
153 }
154
155 private onVirtualColumnsChanged(): void {
156
157 let currentChildIds = Object.keys(this.headerComps);
158
159 let itemsAtDepth = this.columnController.getVirtualHeaderGroupRow(
160 this.pinned,
161 this.type == HeaderRowType.FLOATING_FILTER ?
162 this.dept - 1 :
163 this.dept
164 );
165
166 let ensureDomOrder = this.gridOptionsWrapper.isEnsureDomOrder();
167 let eBefore: HTMLElement;
168
169 itemsAtDepth.forEach((child: ColumnGroupChild) => {
170 // skip groups that have no displayed children. this can happen when the group is broken,
171 // and this section happens to have nothing to display for the open / closed state.
172 // (a broken group is one that is split, ie columns in the group have a non-group column
173 // in between them)
174 if (child.isEmptyGroup()) {
175 return;
176 }
177
178 let idOfChild = child.getUniqueId();
179 let eParentContainer = this.getGui();
180
181 // if we already have this cell rendered, do nothing
182 let colAlreadyInDom = currentChildIds.indexOf(idOfChild) >= 0;
183 let headerComp: IComponent<any>;
184 let eHeaderCompGui: HTMLElement;
185 if (colAlreadyInDom) {
186 _.removeFromArray(currentChildIds, idOfChild);
187 headerComp = this.headerComps[idOfChild];
188 eHeaderCompGui = headerComp.getGui();
189 if (ensureDomOrder) {
190 _.ensureDomOrder(eParentContainer, eHeaderCompGui, eBefore);
191 }
192 eBefore = eHeaderCompGui;
193 } else {
194 headerComp = this.createHeaderComp(child);
195 this.headerComps[idOfChild] = headerComp;
196 eHeaderCompGui = headerComp.getGui();
197 if (ensureDomOrder) {
198 _.insertWithDomOrder(eParentContainer, eHeaderCompGui, eBefore);
199 } else {
200 eParentContainer.appendChild(eHeaderCompGui);
201 }
202 eBefore = eHeaderCompGui;
203 }
204 });
205
206 // at this point, anything left in currentChildIds is an element that is no longer in the viewport
207 this.removeAndDestroyChildComponents(currentChildIds);
208 }
209
210 private createHeaderComp(columnGroupChild: ColumnGroupChild): IComponent<any> {
211 let result: IComponent<any>;
212
213 switch (this.type) {
214 case HeaderRowType.COLUMN :
215 result = new HeaderWrapperComp(<Column> columnGroupChild, this.dropTarget, this.pinned);
216 break;
217 case HeaderRowType.COLUMN_GROUP :
218 result = new HeaderGroupWrapperComp(<ColumnGroup> columnGroupChild, this.dropTarget, this.pinned);
219 break;
220 case HeaderRowType.FLOATING_FILTER :
221 let column = <Column> columnGroupChild;
222 result = this.createFloatingFilterWrapper(column);
223 break;
224 }
225
226 this.context.wireBean(result);
227
228 return result;
229 }
230
231 private createFloatingFilterWrapper(column: Column): IFloatingFilterWrapperComp<any, any, any, any> {
232 let floatingFilterParams: IFloatingFilterParams<any, any> = this.createFloatingFilterParams(column);
233
234 let floatingFilterWrapper: IFloatingFilterWrapperComp<any, any, any, any> = this.componentRecipes.newFloatingFilterWrapperComponent(
235 column,
236 <null>floatingFilterParams
237 );
238
239 this.addDestroyableEventListener(column, Column.EVENT_FILTER_CHANGED, () => {
240 let filterComponentPromise: Promise<IFilterComp> = this.filterManager.getFilterComponent(column);
241 floatingFilterWrapper.onParentModelChanged(filterComponentPromise.resolveNow(null, filter => filter.getModel()));
242 });
243 let cachedFilter = <any>this.filterManager.cachedFilter(column);
244 if (cachedFilter) {
245 let filterComponentPromise: Promise<IFilterComp> = this.filterManager.getFilterComponent(column);
246 floatingFilterWrapper.onParentModelChanged(filterComponentPromise.resolveNow(null, filter => filter.getModel()));
247 }
248
249 return floatingFilterWrapper;
250 }
251
252 private createFloatingFilterParams<M, F extends FloatingFilterChange>(column: Column): IFloatingFilterParams<M, F> {
253 // We always get the freshest reference to the baseFilter because the filters get sometimes created
254 // and destroyed between calls
255 //
256 // let filterComponent:BaseFilter<any, any, any> = <any>this.filterManager.getFilterComponent(column);
257 //
258 let baseParams: IFloatingFilterParams<M, F> = {
259 api: this.gridApi,
260 column: column,
261 currentParentModel: (): M => {
262 let filterComponentPromise: Promise<IFilterComp> = <any>this.filterManager.getFilterComponent(column);
263 let wholeParentFilter: CombinedFilter<M> | M= filterComponentPromise.resolveNow(null, (filter: any) =>
264 (filter.getNullableModel) ?
265 filter.getNullableModel() :
266 filter.getModel()
267 );
268 return (<CombinedFilter<M>>wholeParentFilter).operator != null ? (<CombinedFilter<M>>wholeParentFilter).condition1 : <M>wholeParentFilter;
269 },
270 onFloatingFilterChanged: (change: F | M): boolean => {
271 let captureModelChangedResolveFunc: (modelChanged: boolean) => void;
272 let modelChanged: Promise<boolean> = new Promise((resolve) => {
273 captureModelChangedResolveFunc = resolve;
274 });
275 let filterComponentPromise: Promise<IFilterComp> = <any>this.filterManager.getFilterComponent(column);
276 filterComponentPromise.then(filterComponent => {
277 if (filterComponent.onFloatingFilterChanged) {
278 //If going through this branch of code the user MUST
279 //be passing an object of type change that contains
280 //a model propery inside and some other stuff
281 let result: boolean = filterComponent.onFloatingFilterChanged(<F>change);
282 captureModelChangedResolveFunc(result);
283 } else {
284 //If going through this branch of code the user MUST
285 //be passing the plain model and delegating to ag-Grid
286 //the responsibility to set the parent model and refresh
287 //the filters
288 filterComponent.setModel(<M>change);
289 this.filterManager.onFilterChanged();
290 captureModelChangedResolveFunc(true);
291 }
292 });
293 return modelChanged.resolveNow(true, modelChanged => modelChanged);
294 },
295 //This one might be overriden from the colDef
296 suppressFilterButton: false
297 };
298 return baseParams;
299 }
300
301}
\No newline at end of file