2022-04-19 15:12:26 +02:00

171 lines
4.8 KiB
JavaScript

import { analyzeData } from './data';
import { getFullLabelDate } from './format';
import { ZOOM_RANGE_DELTA, ZOOM_RANGE_MIDDLE, ZOOM_TIMEOUT } from './constants';
import { createColors } from './skin';
export function createZoomer(data, overviewData, colors, stateManager, container, header, minimap, tooltip, tools) {
let _isZoomed = false;
let _stateBeforeZoomIn;
let _stateBeforeZoomOut;
function zoomIn(state, labelIndex) {
if (_isZoomed) {
return;
}
const label = data.xLabels[labelIndex];
_stateBeforeZoomIn = state;
header.toggleIsZooming(true);
tooltip.toggleLoading(true);
tooltip.toggleIsZoomed(true);
if (data.shouldZoomToPie) {
container.classList.add('lovely-chart--state-zoomed-in');
container.classList.add('lovely-chart--state-animating');
}
const { value } = label;
const dataPromise = data.shouldZoomToPie ? Promise.resolve(_generatePieData(labelIndex)) : data.onZoom(value);
dataPromise.then((newData) => _replaceData(newData, labelIndex, label));
}
function zoomOut(state) {
if (!_isZoomed) {
return;
}
_stateBeforeZoomOut = state;
header.toggleIsZooming(true);
tooltip.toggleLoading(true);
tooltip.toggleIsZoomed(false);
if (data.shouldZoomToPie) {
container.classList.remove('lovely-chart--state-zoomed-in');
container.classList.add('lovely-chart--state-animating');
}
const labelIndex = Math.round((state.labelFromIndex + state.labelToIndex) / 2);
_replaceData(overviewData, labelIndex);
}
function isZoomed() {
return _isZoomed;
}
function _replaceData(newRawData, labelIndex, zoomInLabel) {
if (!newRawData) {
tooltip.toggleLoading(false);
tooltip.toggleIsZoomed(false);
header.toggleIsZooming(false);
return;
}
tooltip.toggleLoading(false);
const labelWidth = 1 / data.xLabels.length;
const labelMiddle = labelIndex / (data.xLabels.length - 1);
const filter = {};
data.datasets.forEach(({ key }) => filter[key] = false);
const newData = analyzeData(newRawData, _isZoomed || data.shouldZoomToPie ? 'day' : 'hour');
const shouldZoomToLines = Object.keys(data.datasets).length !== Object.keys(newData.datasets).length;
stateManager.update({
range: {
begin: labelMiddle - labelWidth / 2,
end: labelMiddle + labelWidth / 2,
},
filter,
});
setTimeout(() => {
Object.assign(data, newData);
if (shouldZoomToLines && newRawData.colors) {
Object.assign(colors, createColors(newRawData.colors));
}
if (shouldZoomToLines) {
minimap.toggle(_isZoomed);
tools.redraw();
container.style.width = `${container.scrollWidth}px`;
container.style.height = `${container.scrollHeight}px`;
}
stateManager.update({
range: {
begin: ZOOM_RANGE_MIDDLE - ZOOM_RANGE_DELTA,
end: ZOOM_RANGE_MIDDLE + ZOOM_RANGE_DELTA,
},
focusOn: null,
}, true);
const daysCount = _isZoomed || data.shouldZoomToPie ? data.xLabels.length : data.xLabels.length / 24;
const halfDayWidth = (1 / daysCount) / 2;
let range;
let filter;
if (_isZoomed) {
range = {
begin: _stateBeforeZoomIn.begin,
end: _stateBeforeZoomIn.end,
};
filter = shouldZoomToLines ? _stateBeforeZoomIn.filter : _stateBeforeZoomOut.filter;
} else {
if (shouldZoomToLines) {
range = {
begin: 0,
end: 1,
};
filter = {};
data.datasets.forEach(({ key }) => filter[key] = true);
} else {
range = data.shouldZoomToPie ? {
begin: ZOOM_RANGE_MIDDLE - halfDayWidth,
end: ZOOM_RANGE_MIDDLE + halfDayWidth,
} : newData.minimapRange;
filter = _stateBeforeZoomIn.filter;
}
}
stateManager.update({
range,
filter,
minimapDelta: _isZoomed ? null : range.end - range.begin,
});
if (zoomInLabel) {
header.zoom(getFullLabelDate(zoomInLabel));
}
_isZoomed = !_isZoomed;
header.toggleIsZooming(false);
}, stateManager.hasAnimations() ? ZOOM_TIMEOUT : 0);
setTimeout(() => {
if (data.shouldZoomToPie) {
container.classList.remove('lovely-chart--state-animating');
}
}, stateManager.hasAnimations() ? 1000 : 0);
}
function _generatePieData(labelIndex) {
return Object.assign(
{},
overviewData,
{
type: 'pie',
labels: overviewData.labels.slice(labelIndex - 3, labelIndex + 4),
datasets: overviewData.datasets.map((dataset) => {
return {
...dataset,
values: dataset.values.slice(labelIndex - 3, labelIndex + 4),
};
}),
},
);
}
return { zoomIn, zoomOut, isZoomed };
}