/
rules.ts
87 lines (78 loc) · 2.72 KB
/
rules.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
import { type IGroupData, type IGroupItem, isGroup } from '../../model';
import { groupEndLevel, type ITopNGetter } from '../../provider/internal';
export interface IRule {
apply(
data: (IGroupData | IGroupItem)[],
availableHeight: number,
selection: Set<number>,
topNGetter: ITopNGetter
): IRuleInstance;
levelOfDetail(item: IGroupData | IGroupItem, height: number): 'high' | 'low';
}
export interface IRuleInstance {
item: number | ((item: IGroupItem) => number);
group: number | ((group: IGroupData) => number);
violation?: string;
}
export function spaceFillingRule(config: { groupHeight: number; rowHeight: number; groupPadding: number }) {
function levelOfDetail(item: IGroupData | IGroupItem, height: number): 'high' | 'low' {
const group = isGroup(item);
const maxHeight = group ? config.groupHeight : config.rowHeight;
if (height >= maxHeight * 0.9) {
return 'high';
}
return 'low';
}
function itemHeight(
data: (IGroupData | IGroupItem)[],
availableHeight: number,
selection: Set<number>,
topNGetter: ITopNGetter
) {
const visibleHeight = availableHeight - config.rowHeight - 5; // some padding for hover
const items = data.filter((d) => !isGroup(d)) as IGroupItem[];
const groups = data.length - items.length;
const selected = items.reduce((a, d) => a + (selection.has(d.dataIndex) ? 1 : 0), 0);
const unselected = items.length - selected;
const groupSeparators = items.reduce((a, d) => a + groupEndLevel(d, topNGetter), 0);
if (unselected <= 0) {
// doesn't matter since all are selected anyhow
return { height: config.rowHeight, violation: '' };
}
const available =
visibleHeight - groups * config.groupHeight - groupSeparators * config.groupPadding - selected * config.rowHeight;
const height = Math.floor(available / unselected); // round to avoid sub pixel issues
if (height < 1) {
return {
height: 1,
violation: `Not possible to fit all rows on the screen.`,
};
}
// clamp to max height
if (height > config.rowHeight) {
return {
height: config.rowHeight,
violation: '',
};
}
return { height, violation: '' };
}
return {
apply: (
data: (IGroupData | IGroupItem)[],
availableHeight: number,
selection: Set<number>,
topNGetter: ITopNGetter
) => {
const { violation, height } = itemHeight(data, availableHeight, selection, topNGetter);
const item = (item: IGroupItem) => {
if (selection.has(item.dataIndex)) {
return config.rowHeight;
}
return height;
};
return { item, group: config.groupHeight, violation };
},
levelOfDetail,
} as IRule;
}