UNPKG

12.6 kBPlain TextView Raw
1import {Component} from "../../widgets/component";
2import {Autowired, Context, PostConstruct} from "../../context/context";
3import {Column} from "../../entities/column";
4import {Utils as _} from "../../utils";
5import {
6 DragAndDropService, DragItem, DragSource, DragSourceType,
7 DropTarget
8} from "../../dragAndDrop/dragAndDropService";
9import {IHeaderComp, IHeaderParams} from "./headerComp";
10import {ColumnApi} from "../../columnController/columnApi";
11import {ColumnController} from "../../columnController/columnController";
12import {HorizontalResizeService} from "../horizontalResizeService";
13import {GridOptionsWrapper} from "../../gridOptionsWrapper";
14import {CssClassApplier} from "../cssClassApplier";
15import {SetLeftFeature} from "../../rendering/features/setLeftFeature";
16import {IMenuFactory} from "../../interfaces/iMenuFactory";
17import {GridApi} from "../../gridApi";
18import {SortController} from "../../sortController";
19import {EventService} from "../../eventService";
20import {ComponentRecipes} from "../../components/framework/componentRecipes";
21import {AgCheckbox} from "../../widgets/agCheckbox";
22import {RefSelector} from "../../widgets/componentAnnotations";
23import {SelectAllFeature} from "./selectAllFeature";
24import {Events} from "../../events";
25import {ColumnHoverService} from "../../rendering/columnHoverService";
26import {Beans} from "../../rendering/beans";
27import {HoverFeature} from "../hoverFeature";
28import {TouchListener} from "../../widgets/touchListener";
29
30export class HeaderWrapperComp extends Component {
31
32 private static TEMPLATE =
33 '<div class="ag-header-cell" role="presentation" >' +
34 '<div ref="eResize" class="ag-header-cell-resize" role="presentation"></div>' +
35 '<ag-checkbox ref="cbSelectAll" class="ag-header-select-all" role="presentation"></ag-checkbox>' +
36 // <inner component goes here>
37 '</div>';
38
39 @Autowired('gridOptionsWrapper') private gridOptionsWrapper: GridOptionsWrapper;
40 @Autowired('dragAndDropService') private dragAndDropService: DragAndDropService;
41 @Autowired('columnController') private columnController: ColumnController;
42 @Autowired('horizontalResizeService') private horizontalResizeService: HorizontalResizeService;
43 @Autowired('context') private context: Context;
44 @Autowired('menuFactory') private menuFactory: IMenuFactory;
45 @Autowired('gridApi') private gridApi: GridApi;
46 @Autowired('columnApi') private columnApi: ColumnApi;
47 @Autowired('sortController') private sortController: SortController;
48 @Autowired('eventService') private eventService: EventService;
49 @Autowired('componentRecipes') private componentRecipes: ComponentRecipes;
50 @Autowired('columnHoverService') private columnHoverService: ColumnHoverService;
51 @Autowired('beans') private beans: Beans;
52
53 @RefSelector('eResize') private eResize: HTMLElement;
54 @RefSelector('cbSelectAll') private cbSelectAll: AgCheckbox;
55
56 private column: Column;
57 private dragSourceDropTarget: DropTarget;
58 private pinned: string;
59
60 private resizeStartWidth: number;
61 private resizeWithShiftKey: boolean;
62
63 constructor(column: Column, dragSourceDropTarget: DropTarget, pinned: string) {
64 super(HeaderWrapperComp.TEMPLATE);
65 this.column = column;
66 this.dragSourceDropTarget = dragSourceDropTarget;
67 this.pinned = pinned;
68 }
69
70 public getColumn(): Column {
71 return this.column;
72 }
73
74 @PostConstruct
75 public init(): void {
76 this.instantiate(this.context);
77
78 let displayName = this.columnController.getDisplayNameForColumn(this.column, 'header', true);
79 let enableSorting = this.gridOptionsWrapper.isEnableSorting() && !this.column.getColDef().suppressSorting;
80 let enableMenu = this.menuFactory.isMenuEnabled(this.column) && !this.column.getColDef().suppressMenu;
81
82 this.appendHeaderComp(displayName, enableSorting, enableMenu);
83
84 this.setupWidth();
85 this.setupMovingCss();
86 this.setupTooltip();
87 this.setupResize();
88 this.setupMenuClass();
89 this.setupSortableClass(enableSorting);
90 this.addColumnHoverListener();
91
92 this.addFeature(this.context, new HoverFeature([this.column], this.getGui()));
93
94 this.addDestroyableEventListener(this.column, Column.EVENT_FILTER_ACTIVE_CHANGED, this.onFilterChanged.bind(this));
95 this.onFilterChanged();
96
97 this.addFeature(this.context, new SelectAllFeature(this.cbSelectAll, this.column));
98
99 let setLeftFeature = new SetLeftFeature(this.column, this.getGui(), this.beans);
100 setLeftFeature.init();
101 this.addDestroyFunc(setLeftFeature.destroy.bind(setLeftFeature));
102
103 this.addAttributes();
104 CssClassApplier.addHeaderClassesFromColDef(this.column.getColDef(), this.getGui(), this.gridOptionsWrapper, this.column, null);
105 }
106
107 private addColumnHoverListener(): void {
108 this.addDestroyableEventListener(this.eventService, Events.EVENT_COLUMN_HOVER_CHANGED, this.onColumnHover.bind(this));
109 this.onColumnHover();
110 }
111
112 private onColumnHover(): void {
113 let isHovered = this.columnHoverService.isHovered(this.column);
114 _.addOrRemoveCssClass(this.getGui(), 'ag-column-hover', isHovered)
115 }
116
117 private setupSortableClass(enableSorting:boolean):void{
118 if (enableSorting) {
119 let element = this.getGui();
120 _.addCssClass(element, 'ag-header-cell-sortable');
121 }
122 }
123
124 private onFilterChanged(): void {
125 let filterPresent = this.column.isFilterActive();
126 _.addOrRemoveCssClass(this.getGui(), 'ag-header-cell-filtered', filterPresent);
127 }
128
129 private appendHeaderComp(displayName: string, enableSorting: boolean, enableMenu: boolean): void {
130 let params = <IHeaderParams> {
131 column: this.column,
132 displayName: displayName,
133 enableSorting: enableSorting,
134 enableMenu: enableMenu,
135 showColumnMenu: (source:HTMLElement) => {
136 this.gridApi.showColumnMenuAfterButtonClick(this.column, source)
137 },
138 progressSort: (multiSort?:boolean) => {
139 this.sortController.progressSort(this.column, !!multiSort, "uiColumnSorted");
140 },
141 setSort: (sort: string, multiSort?: boolean) => {
142 this.sortController.setSortForColumn(this.column, sort, !!multiSort, "uiColumnSorted");
143 },
144 api: this.gridApi,
145 columnApi: this.columnApi,
146 context: this.gridOptionsWrapper.getContext()
147 };
148
149 let callback = this.afterHeaderCompCreated.bind(this, displayName);
150
151 this.componentRecipes.newHeaderComponent(params).then(callback);
152 }
153
154 private afterHeaderCompCreated(displayName: string, headerComp: IHeaderComp): void {
155 this.appendChild(headerComp);
156 this.setupMove(headerComp.getGui(), displayName);
157
158 if (headerComp.destroy) {
159 this.addDestroyFunc(headerComp.destroy.bind(headerComp));
160 }
161 }
162
163 private onColumnMovingChanged(): void {
164 // this function adds or removes the moving css, based on if the col is moving.
165 // this is what makes the header go dark when it is been moved (gives impression to
166 // user that the column was picked up).
167 if (this.column.isMoving()) {
168 _.addCssClass(this.getGui(), 'ag-header-cell-moving');
169 } else {
170 _.removeCssClass(this.getGui(), 'ag-header-cell-moving');
171 }
172 }
173
174 private setupMove(eHeaderCellLabel: HTMLElement, displayName: string): void {
175 let suppressMove = this.gridOptionsWrapper.isSuppressMovableColumns()
176 || this.column.getColDef().suppressMovable
177 || this.column.isLockPosition();
178
179 if (suppressMove) { return; }
180
181 if (eHeaderCellLabel) {
182 let dragSource: DragSource = {
183 type: DragSourceType.HeaderCell,
184 eElement: eHeaderCellLabel,
185 dragItemCallback: () => this.createDragItem(),
186 dragItemName: displayName,
187 dragSourceDropTarget: this.dragSourceDropTarget,
188 dragStarted: () => this.column.setMoving(true, "uiColumnMoved"),
189 dragStopped: () => this.column.setMoving(false, "uiColumnMoved")
190 };
191 this.dragAndDropService.addDragSource(dragSource, true);
192 this.addDestroyFunc( ()=> this.dragAndDropService.removeDragSource(dragSource) );
193 }
194 }
195
196 private createDragItem(): DragItem {
197 let visibleState: { [key: string]: boolean } = {};
198 visibleState[this.column.getId()] = this.column.isVisible();
199
200 return {
201 columns: [this.column],
202 visibleState: visibleState
203 };
204 }
205
206 private setupResize(): void {
207 let colDef = this.column.getColDef();
208
209 // if no eResize in template, do nothing
210 if (!this.eResize) {
211 return;
212 }
213
214 if (!this.column.isResizable()) {
215 _.removeFromParent(this.eResize);
216 return;
217 }
218
219 let finishedWithResizeFunc = this.horizontalResizeService.addResizeBar({
220 eResizeBar: this.eResize,
221 onResizeStart: this.onResizeStart.bind(this),
222 onResizing: this.onResizing.bind(this, false),
223 onResizeEnd: this.onResizing.bind(this, true)
224 });
225
226 this.addDestroyFunc(finishedWithResizeFunc);
227
228 let weWantAutoSize = !this.gridOptionsWrapper.isSuppressAutoSize() && !colDef.suppressAutoSize;
229 if (weWantAutoSize) {
230 this.addDestroyableEventListener(this.eResize, 'dblclick', () => {
231 this.columnController.autoSizeColumn(this.column, "uiColumnResized");
232 });
233 let touchListener: TouchListener = new TouchListener(this.eResize);
234 this.addDestroyableEventListener(touchListener, TouchListener.EVENT_DOUBLE_TAP, ()=> {
235 this.columnController.autoSizeColumn(this.column, "uiColumnResized");
236 });
237 this.addDestroyFunc(touchListener.destroy.bind(touchListener));
238 }
239 }
240
241 public onResizing(finished: boolean, resizeAmount: number): void {
242 let resizeAmountNormalised = this.normaliseResizeAmount(resizeAmount);
243 let newWidth = this.resizeStartWidth + resizeAmountNormalised;
244 this.columnController.setColumnWidth(this.column, newWidth, this.resizeWithShiftKey, finished, "uiColumnDragged");
245 }
246
247 public onResizeStart(shiftKey: boolean): void {
248 this.resizeStartWidth = this.column.getActualWidth();
249 this.resizeWithShiftKey = shiftKey;
250 }
251
252 private setupTooltip(): void {
253 let colDef = this.column.getColDef();
254
255 // add tooltip if exists
256 if (colDef.headerTooltip) {
257 this.getGui().title = colDef.headerTooltip;
258 }
259 }
260
261 private setupMovingCss(): void {
262 this.addDestroyableEventListener(this.column, Column.EVENT_MOVING_CHANGED, this.onColumnMovingChanged.bind(this));
263 this.onColumnMovingChanged();
264 }
265
266 private addAttributes(): void {
267 this.getGui().setAttribute("col-id", this.column.getColId());
268 }
269
270 private setupWidth(): void {
271 this.addDestroyableEventListener(this.column, Column.EVENT_WIDTH_CHANGED, this.onColumnWidthChanged.bind(this));
272 this.onColumnWidthChanged();
273 }
274
275 private setupMenuClass(): void {
276 this.addDestroyableEventListener(this.column, Column.EVENT_MENU_VISIBLE_CHANGED, this.onMenuVisible.bind(this));
277 this.onColumnWidthChanged();
278 }
279
280 private onMenuVisible(): void {
281 this.addOrRemoveCssClass('ag-column-menu-visible', this.column.isMenuVisible());
282 }
283
284 private onColumnWidthChanged(): void {
285 this.getGui().style.width = this.column.getActualWidth() + 'px';
286 }
287
288 // optionally inverts the drag, depending on pinned and RTL
289 // note - this method is duplicated in RenderedHeaderGroupCell - should refactor out?
290 private normaliseResizeAmount(dragChange: number): number {
291 let result = dragChange;
292 if (this.gridOptionsWrapper.isEnableRtl()) {
293 // for RTL, dragging left makes the col bigger, except when pinning left
294 if (this.pinned !== Column.PINNED_LEFT) {
295 result *= -1;
296 }
297 } else {
298 // for LTR (ie normal), dragging left makes the col smaller, except when pinning right
299 if (this.pinned === Column.PINNED_RIGHT) {
300 result *= -1;
301 }
302 }
303 return result;
304 }
305
306}