-
Notifications
You must be signed in to change notification settings - Fork 4.4k
/
AutoHeightController.js
110 lines (88 loc) · 2.54 KB
/
AutoHeightController.js
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
import { includes, reduce, some } from 'lodash';
// TODO: Revisit this implementation when migrating widget component to React
const WIDGET_SELECTOR = '[data-widgetid="{0}"]';
const WIDGET_CONTENT_SELECTOR = [
'.widget-header', // header
'visualization-renderer', // visualization
'.scrollbox .alert', // error state
'.spinner-container', // loading state
'.tile__bottom-control', // footer
].join(',');
const INTERVAL = 200;
export default class AutoHeightController {
widgets = {};
interval = null;
onHeightChange = null;
constructor(handler) {
this.onHeightChange = handler;
}
update(widgets) {
const newWidgetIds = widgets
.filter(widget => widget.options.position.autoHeight)
.map(widget => widget.id.toString());
// added
newWidgetIds
.filter(id => !includes(Object.keys(this.widgets), id))
.forEach(this.add);
// removed
Object.keys(this.widgets)
.filter(id => !includes(newWidgetIds, id))
.forEach(this.remove);
}
add = (id) => {
if (this.isEmpty()) {
this.start();
}
const selector = WIDGET_SELECTOR.replace('{0}', id);
this.widgets[id] = [
function getHeight() {
const widgetEl = document.querySelector(selector);
if (!widgetEl) {
return undefined; // safety
}
// get all content elements
const els = widgetEl.querySelectorAll(WIDGET_CONTENT_SELECTOR);
// calculate accumulated height
return reduce(els, (acc, el) => {
const height = el ? el.getBoundingClientRect().height : 0;
return acc + height;
}, 0);
},
];
};
remove = (id) => {
// not actually deleting from this.widgets to prevent case of unwanted re-adding
this.widgets[id.toString()] = false;
if (this.isEmpty()) {
this.stop();
}
};
exists = id => !!this.widgets[id.toString()];
isEmpty = () => !some(this.widgets);
checkHeightChanges = () => {
Object.keys(this.widgets).forEach((id) => {
const [getHeight, prevHeight] = this.widgets[id];
const height = getHeight();
if (height && height !== prevHeight) {
this.widgets[id][1] = height; // save
this.onHeightChange(id, height); // dispatch
}
});
};
start = () => {
this.stop();
this.interval = setInterval(this.checkHeightChanges, INTERVAL);
};
stop = () => {
clearInterval(this.interval);
};
resume = () => {
if (!this.isEmpty()) {
this.start();
}
};
destroy = () => {
this.stop();
this.widgets = null;
}
}