-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
autoWidthCalculator.ts
115 lines (94 loc) · 5.26 KB
/
autoWidthCalculator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { RowRenderer } from "./rowRenderer";
import { GridPanel } from "../gridPanel/gridPanel";
import { Column } from "../entities/column";
import { Autowired, Bean } from "../context/context";
import { GridOptionsWrapper } from "../gridOptionsWrapper";
import { HeaderWrapperComp } from "../headerRendering/header/headerWrapperComp";
import { Component } from "../widgets/component";
import { HeaderRootComp } from "../headerRendering/headerRootComp";
@Bean('autoWidthCalculator')
export class AutoWidthCalculator {
@Autowired('rowRenderer') private rowRenderer: RowRenderer;
@Autowired('gridOptionsWrapper') private gridOptionsWrapper: GridOptionsWrapper;
private gridPanel: GridPanel;
private headerRootComp: HeaderRootComp;
public registerGridComp(gridPanel: GridPanel): void {
this.gridPanel = gridPanel;
}
public registerHeaderRootComp(headerRootComp: HeaderRootComp): void {
this.headerRootComp = headerRootComp;
}
// this is the trick: we create a dummy container and clone all the cells
// into the dummy, then check the dummy's width. then destroy the dummy
// as we don't need it any more.
// drawback: only the cells visible on the screen are considered
public getPreferredWidthForColumn(column: Column): number {
const eHeaderCell = this.getHeaderCellForColumn(column);
// cell isn't visible
if (!eHeaderCell) {
return -1;
}
const eDummyContainer = document.createElement('span');
// position fixed, so it isn't restricted to the boundaries of the parent
eDummyContainer.style.position = 'fixed';
// we put the dummy into the body container, so it will inherit all the
// css styles that the real cells are inheriting
const eBodyContainer = this.gridPanel.getCenterContainer();
eBodyContainer.appendChild(eDummyContainer);
// get all the cells that are currently displayed (this only brings back
// rendered cells, rows not rendered due to row visualisation will not be here)
this.putRowCellsIntoDummyContainer(column, eDummyContainer);
// also put header cell in
// we only consider the lowest level cell, not the group cell. in 99% of the time, this
// will be enough. if we consider groups, then it gets to complicated for what it's worth,
// as the groups can span columns and this class only considers one column at a time.
this.cloneItemIntoDummy(eHeaderCell, eDummyContainer);
// at this point, all the clones are lined up vertically with natural widths. the dummy
// container will have a width wide enough just to fit the largest.
const dummyContainerWidth = eDummyContainer.offsetWidth;
// we are finished with the dummy container, so get rid of it
eBodyContainer.removeChild(eDummyContainer);
// we add padding as I found sometimes the gui still put '...' after some of the texts. so the
// user can configure the grid to add a few more pixels after the calculated width
const autoSizePadding = this.gridOptionsWrapper.getAutoSizePadding();
return dummyContainerWidth + autoSizePadding;
}
private getHeaderCellForColumn(column: Column): HTMLElement {
let comp: Component = null;
// find the rendered header cell
this.headerRootComp.forEachHeaderElement(headerElement => {
if (headerElement instanceof HeaderWrapperComp) {
const headerWrapperComp = headerElement;
if (headerWrapperComp.getColumn() === column) {
comp = headerWrapperComp;
}
}
});
return comp ? comp.getGui() : null;
}
private putRowCellsIntoDummyContainer(column: Column, eDummyContainer: HTMLElement): void {
const eCells = this.rowRenderer.getAllCellsForColumn(column);
eCells.forEach(eCell => this.cloneItemIntoDummy(eCell, eDummyContainer));
}
private cloneItemIntoDummy(eCell: HTMLElement, eDummyContainer: HTMLElement): void {
// make a deep clone of the cell
const eCellClone: HTMLElement = eCell.cloneNode(true) as HTMLElement;
// the original has a fixed width, we remove this to allow the natural width based on content
eCellClone.style.width = '';
// the original has position = absolute, we need to remove this so it's positioned normally
eCellClone.style.position = 'static';
eCellClone.style.left = '';
// we put the cell into a containing div, as otherwise the cells would just line up
// on the same line, standard flow layout, by putting them into divs, they are laid
// out one per line
const eCloneParent = document.createElement('div');
// table-row, so that each cell is on a row. i also tried display='block', but this
// didn't work in IE
eCloneParent.style.display = 'table-row';
// the twig on the branch, the branch on the tree, the tree in the hole,
// the hole in the bog, the bog in the clone, the clone in the parent,
// the parent in the dummy, and the dummy down in the vall-e-ooo, OOOOOOOOO! Oh row the rattling bog....
eCloneParent.appendChild(eCellClone);
eDummyContainer.appendChild(eCloneParent);
}
}