Files
redash/client/app/components/dashboards/AutoHeightController.js
Arik Fraimovich 56d3be2248 Prettier all the Javascript code & GitHub Action (#4433)
* Prettier all the JS files

* Add GitHub Action to autoformat code pushed to master

* Fix eslint violation due to formatting.

* Remove GitHub actions for styling

* Add restyled.io config
2019-12-11 17:05:38 +02:00

120 lines
2.7 KiB
JavaScript

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 => {
// ignore if not an active autoHeight widget
if (!this.exists(id)) {
return;
}
// 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)
.filter(this.exists) // reject already removed items
.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;
};
}