import {Component} from "../../widgets/component"; import {Autowired, Context, PostConstruct} from "../../context/context"; import {Column} from "../../entities/column"; import {Utils as _} from "../../utils"; import { DragAndDropService, DragItem, DragSource, DragSourceType, DropTarget } from "../../dragAndDrop/dragAndDropService"; import {IHeaderComp, IHeaderParams} from "./headerComp"; import {ColumnApi} from "../../columnController/columnApi"; import {ColumnController} from "../../columnController/columnController"; import {HorizontalResizeService} from "../horizontalResizeService"; import {GridOptionsWrapper} from "../../gridOptionsWrapper"; import {CssClassApplier} from "../cssClassApplier"; import {SetLeftFeature} from "../../rendering/features/setLeftFeature"; import {IMenuFactory} from "../../interfaces/iMenuFactory"; import {GridApi} from "../../gridApi"; import {SortController} from "../../sortController"; import {EventService} from "../../eventService"; import {ComponentRecipes} from "../../components/framework/componentRecipes"; import {AgCheckbox} from "../../widgets/agCheckbox"; import {RefSelector} from "../../widgets/componentAnnotations"; import {SelectAllFeature} from "./selectAllFeature"; import {Events} from "../../events"; import {ColumnHoverService} from "../../rendering/columnHoverService"; import {Beans} from "../../rendering/beans"; import {HoverFeature} from "../hoverFeature"; import {TouchListener} from "../../widgets/touchListener"; export class HeaderWrapperComp extends Component { private static TEMPLATE = ''; @Autowired('gridOptionsWrapper') private gridOptionsWrapper: GridOptionsWrapper; @Autowired('dragAndDropService') private dragAndDropService: DragAndDropService; @Autowired('columnController') private columnController: ColumnController; @Autowired('horizontalResizeService') private horizontalResizeService: HorizontalResizeService; @Autowired('context') private context: Context; @Autowired('menuFactory') private menuFactory: IMenuFactory; @Autowired('gridApi') private gridApi: GridApi; @Autowired('columnApi') private columnApi: ColumnApi; @Autowired('sortController') private sortController: SortController; @Autowired('eventService') private eventService: EventService; @Autowired('componentRecipes') private componentRecipes: ComponentRecipes; @Autowired('columnHoverService') private columnHoverService: ColumnHoverService; @Autowired('beans') private beans: Beans; @RefSelector('eResize') private eResize: HTMLElement; @RefSelector('cbSelectAll') private cbSelectAll: AgCheckbox; private column: Column; private dragSourceDropTarget: DropTarget; private pinned: string; private resizeStartWidth: number; private resizeWithShiftKey: boolean; constructor(column: Column, dragSourceDropTarget: DropTarget, pinned: string) { super(HeaderWrapperComp.TEMPLATE); this.column = column; this.dragSourceDropTarget = dragSourceDropTarget; this.pinned = pinned; } public getColumn(): Column { return this.column; } @PostConstruct public init(): void { this.instantiate(this.context); let displayName = this.columnController.getDisplayNameForColumn(this.column, 'header', true); let enableSorting = this.gridOptionsWrapper.isEnableSorting() && !this.column.getColDef().suppressSorting; let enableMenu = this.menuFactory.isMenuEnabled(this.column) && !this.column.getColDef().suppressMenu; this.appendHeaderComp(displayName, enableSorting, enableMenu); this.setupWidth(); this.setupMovingCss(); this.setupTooltip(); this.setupResize(); this.setupMenuClass(); this.setupSortableClass(enableSorting); this.addColumnHoverListener(); this.addFeature(this.context, new HoverFeature([this.column], this.getGui())); this.addDestroyableEventListener(this.column, Column.EVENT_FILTER_ACTIVE_CHANGED, this.onFilterChanged.bind(this)); this.onFilterChanged(); this.addFeature(this.context, new SelectAllFeature(this.cbSelectAll, this.column)); let setLeftFeature = new SetLeftFeature(this.column, this.getGui(), this.beans); setLeftFeature.init(); this.addDestroyFunc(setLeftFeature.destroy.bind(setLeftFeature)); this.addAttributes(); CssClassApplier.addHeaderClassesFromColDef(this.column.getColDef(), this.getGui(), this.gridOptionsWrapper, this.column, null); } private addColumnHoverListener(): void { this.addDestroyableEventListener(this.eventService, Events.EVENT_COLUMN_HOVER_CHANGED, this.onColumnHover.bind(this)); this.onColumnHover(); } private onColumnHover(): void { let isHovered = this.columnHoverService.isHovered(this.column); _.addOrRemoveCssClass(this.getGui(), 'ag-column-hover', isHovered) } private setupSortableClass(enableSorting:boolean):void{ if (enableSorting) { let element = this.getGui(); _.addCssClass(element, 'ag-header-cell-sortable'); } } private onFilterChanged(): void { let filterPresent = this.column.isFilterActive(); _.addOrRemoveCssClass(this.getGui(), 'ag-header-cell-filtered', filterPresent); } private appendHeaderComp(displayName: string, enableSorting: boolean, enableMenu: boolean): void { let params = { column: this.column, displayName: displayName, enableSorting: enableSorting, enableMenu: enableMenu, showColumnMenu: (source:HTMLElement) => { this.gridApi.showColumnMenuAfterButtonClick(this.column, source) }, progressSort: (multiSort?:boolean) => { this.sortController.progressSort(this.column, !!multiSort, "uiColumnSorted"); }, setSort: (sort: string, multiSort?: boolean) => { this.sortController.setSortForColumn(this.column, sort, !!multiSort, "uiColumnSorted"); }, api: this.gridApi, columnApi: this.columnApi, context: this.gridOptionsWrapper.getContext() }; let callback = this.afterHeaderCompCreated.bind(this, displayName); this.componentRecipes.newHeaderComponent(params).then(callback); } private afterHeaderCompCreated(displayName: string, headerComp: IHeaderComp): void { this.appendChild(headerComp); this.setupMove(headerComp.getGui(), displayName); if (headerComp.destroy) { this.addDestroyFunc(headerComp.destroy.bind(headerComp)); } } private onColumnMovingChanged(): void { // this function adds or removes the moving css, based on if the col is moving. // this is what makes the header go dark when it is been moved (gives impression to // user that the column was picked up). if (this.column.isMoving()) { _.addCssClass(this.getGui(), 'ag-header-cell-moving'); } else { _.removeCssClass(this.getGui(), 'ag-header-cell-moving'); } } private setupMove(eHeaderCellLabel: HTMLElement, displayName: string): void { let suppressMove = this.gridOptionsWrapper.isSuppressMovableColumns() || this.column.getColDef().suppressMovable || this.column.isLockPosition(); if (suppressMove) { return; } if (eHeaderCellLabel) { let dragSource: DragSource = { type: DragSourceType.HeaderCell, eElement: eHeaderCellLabel, dragItemCallback: () => this.createDragItem(), dragItemName: displayName, dragSourceDropTarget: this.dragSourceDropTarget, dragStarted: () => this.column.setMoving(true, "uiColumnMoved"), dragStopped: () => this.column.setMoving(false, "uiColumnMoved") }; this.dragAndDropService.addDragSource(dragSource, true); this.addDestroyFunc( ()=> this.dragAndDropService.removeDragSource(dragSource) ); } } private createDragItem(): DragItem { let visibleState: { [key: string]: boolean } = {}; visibleState[this.column.getId()] = this.column.isVisible(); return { columns: [this.column], visibleState: visibleState }; } private setupResize(): void { let colDef = this.column.getColDef(); // if no eResize in template, do nothing if (!this.eResize) { return; } if (!this.column.isResizable()) { _.removeFromParent(this.eResize); return; } let finishedWithResizeFunc = this.horizontalResizeService.addResizeBar({ eResizeBar: this.eResize, onResizeStart: this.onResizeStart.bind(this), onResizing: this.onResizing.bind(this, false), onResizeEnd: this.onResizing.bind(this, true) }); this.addDestroyFunc(finishedWithResizeFunc); let weWantAutoSize = !this.gridOptionsWrapper.isSuppressAutoSize() && !colDef.suppressAutoSize; if (weWantAutoSize) { this.addDestroyableEventListener(this.eResize, 'dblclick', () => { this.columnController.autoSizeColumn(this.column, "uiColumnResized"); }); let touchListener: TouchListener = new TouchListener(this.eResize); this.addDestroyableEventListener(touchListener, TouchListener.EVENT_DOUBLE_TAP, ()=> { this.columnController.autoSizeColumn(this.column, "uiColumnResized"); }); this.addDestroyFunc(touchListener.destroy.bind(touchListener)); } } public onResizing(finished: boolean, resizeAmount: number): void { let resizeAmountNormalised = this.normaliseResizeAmount(resizeAmount); let newWidth = this.resizeStartWidth + resizeAmountNormalised; this.columnController.setColumnWidth(this.column, newWidth, this.resizeWithShiftKey, finished, "uiColumnDragged"); } public onResizeStart(shiftKey: boolean): void { this.resizeStartWidth = this.column.getActualWidth(); this.resizeWithShiftKey = shiftKey; } private setupTooltip(): void { let colDef = this.column.getColDef(); // add tooltip if exists if (colDef.headerTooltip) { this.getGui().title = colDef.headerTooltip; } } private setupMovingCss(): void { this.addDestroyableEventListener(this.column, Column.EVENT_MOVING_CHANGED, this.onColumnMovingChanged.bind(this)); this.onColumnMovingChanged(); } private addAttributes(): void { this.getGui().setAttribute("col-id", this.column.getColId()); } private setupWidth(): void { this.addDestroyableEventListener(this.column, Column.EVENT_WIDTH_CHANGED, this.onColumnWidthChanged.bind(this)); this.onColumnWidthChanged(); } private setupMenuClass(): void { this.addDestroyableEventListener(this.column, Column.EVENT_MENU_VISIBLE_CHANGED, this.onMenuVisible.bind(this)); this.onColumnWidthChanged(); } private onMenuVisible(): void { this.addOrRemoveCssClass('ag-column-menu-visible', this.column.isMenuVisible()); } private onColumnWidthChanged(): void { this.getGui().style.width = this.column.getActualWidth() + 'px'; } // optionally inverts the drag, depending on pinned and RTL // note - this method is duplicated in RenderedHeaderGroupCell - should refactor out? private normaliseResizeAmount(dragChange: number): number { let result = dragChange; if (this.gridOptionsWrapper.isEnableRtl()) { // for RTL, dragging left makes the col bigger, except when pinning left if (this.pinned !== Column.PINNED_LEFT) { result *= -1; } } else { // for LTR (ie normal), dragging left makes the col smaller, except when pinning right if (this.pinned === Column.PINNED_RIGHT) { result *= -1; } } return result; } }