ag-grid
Version:
Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
142 lines (113 loc) • 4.58 kB
text/typescript
import {BeanStub} from "../context/beanStub";
import {Autowired, Bean, PostConstruct} from "../context/context";
import {EventService} from "../eventService";
import {Events} from "../eventKeys";
import {GridPanel} from "../gridPanel/gridPanel";
import {_} from "../utils";
/**
* This class solves the 'max height' problem, where the user might want to show more data than
* the max div height actually allows.
*/
export class HeightScaler extends BeanStub {
private eventService: EventService;
private gridPanel: GridPanel;
private maxDivHeight: number;
// if false, then scaling is not active, logic in this class is not used,
// the pixel height of the row container matches what is actually needed,
// no scaling applied.
private scaling: boolean;
private modelHeight: number; // how many pixels the model needs
private uiContainerHeight: number; // how many pixels we actually have
private pixelsToShave: number; // the number of pixels we need to shave
// the number of pixels we add to each rowTop - depends on the scroll position
private offset: number;
// the scrollY position
private scrollY = 0;
// how tall the body is
private uiBodyHeight = 0;
// the max scroll position
private maxScrollY: number;
// we need this for the maths, as it impacts the grid height
private scrollBarWidth: number;
private postConstruct(): void {
this.addDestroyableEventListener(this.eventService, Events.EVENT_BODY_HEIGHT_CHANGED, this.update.bind(this));
this.scrollBarWidth = _.getScrollbarWidth();
this.maxDivHeight = _.getMaxDivHeight();
}
public registerGridComp(gridPanel: GridPanel): void {
this.gridPanel = gridPanel;
}
public isScaling(): boolean {
return this.scaling;
}
public getOffset(): number {
return this.offset;
}
public update(): void {
if (!this.scaling) { return; }
let newScrollY = this.gridPanel.getVScrollPosition().top;
let newBodyHeight = this.getUiBodyHeight();
let atLeastOneChanged = newScrollY!==this.scrollY || newBodyHeight!==this.uiBodyHeight;
if (atLeastOneChanged) {
this.scrollY = newScrollY;
this.uiBodyHeight = newBodyHeight;
this.calculateOffset();
}
}
private calculateOffset(): void {
this.uiContainerHeight = this.maxDivHeight;
this.pixelsToShave = this.modelHeight - this.uiContainerHeight;
this.maxScrollY = this.uiContainerHeight - this.uiBodyHeight;
let scrollPercent = this.scrollY / this.maxScrollY;
this.setOffset(scrollPercent * this.pixelsToShave);
}
private clearOffset(): void {
this.uiContainerHeight = this.modelHeight;
this.pixelsToShave = 0;
this.setOffset(0);
}
private setOffset(newOffset: number): void {
// because we are talking pixels, no point in confusing things with half numbers
let newOffsetFloor = typeof newOffset === 'number' ? Math.floor(newOffset) : null;
if (this.offset!==newOffsetFloor) {
this.offset = newOffsetFloor;
this.eventService.dispatchEvent({type: Events.EVENT_HEIGHT_SCALE_CHANGED});
}
}
public setModelHeight(modelHeight: number): void {
this.modelHeight = modelHeight;
this.scaling = this.maxDivHeight > 0 && modelHeight > this.maxDivHeight;
if (this.scaling) {
this.calculateOffset();
} else {
this.clearOffset();
}
}
public getUiContainerHeight(): number {
return this.uiContainerHeight;
}
public getRealPixelPosition(modelPixel: number): number {
let uiPixel = modelPixel - this.offset;
return uiPixel;
}
private getUiBodyHeight(): number {
let pos = this.gridPanel.getVScrollPosition();
let bodyHeight = pos.bottom - pos.top;
if (this.gridPanel.isHorizontalScrollShowing()) {
bodyHeight -= this.scrollBarWidth;
}
return bodyHeight;
}
public getScrollPositionForPixel(rowTop: number): number {
if (this.pixelsToShave<=0) {
return rowTop;
} else {
let modelMaxScroll = this.modelHeight - this.getUiBodyHeight();
let scrollPercent = rowTop / modelMaxScroll;
let scrollPixel = this.maxScrollY * scrollPercent;
return scrollPixel;
}
}
}