-
Briggs, Aaron Shane authoredBriggs, Aaron Shane authored
tooltip.js 6.24 KiB
import {DateTime} from 'luxon';
import {createSelector, createStructuredSelector} from 'reselect';
import config from 'ui/config';
import {link} from 'ui/lib/d3-redux';
import {drawFocusOverlay, drawFocusCircles, drawFocusLine} from 'd3render/graph-tooltip';
import {setGraphCursorOffset} from 'ml/store/hydrograph-state';
import {getIVParameter} from 'ml/selectors/hydrograph-data-selector';
import {getCursorTime, getIVDataCursorPoint, getIVDataTooltipPoint, getGroundwaterLevelCursorPoint,
getGroundwaterLevelTooltipPoint
} from './selectors/cursor';
import {getMainLayout} from './selectors/layout';
import {getMainXScale, getMainYScale} from './selectors/scales';
import {getPrimaryParameter} from './selectors/time-series-data';
import {drawCursorSlider} from './cursor-slider';
const getIVDataTooltipTextInfo = function(point, dataKind, unitCode) {
const timeLabel = DateTime.fromMillis(
point.dateTime,
{zone: config.locationTimeZone}
).toFormat('MMM dd, yyyy hh:mm:ss a ZZZZ');
const valueLabel = point.isMasked ? point.label : `${point.value} ${unitCode}`;
return {
label: `${valueLabel} - ${timeLabel}`,
classes: [`${dataKind}-tooltip-text`, point.class]
};
};
const getGWLevelTextInfo = function(point, unitCode) {
if (!point) {
return null;
}
const valueLabel = point.value !== null ? `${point.value} ${unitCode}` : ' ';
const timeLabel = DateTime.fromMillis(point.dateTime, {zone: config.locationTimeZone}).toFormat('MMM dd, yyyy hh:mm:ss a ZZZZ');
return {
label: `${valueLabel} - ${timeLabel}`,
classes: point.classes
};
};
const createTooltipTextGroup = function(elem, {
currentPoint,
secondaryParameterPoint,
comparePoint,
gwLevelPoint,
parameter,
secondaryParameter,
layout
}, textGroup) {
const adjustMarginOfTooltips = function(elem) {
let marginAdjustment = layout.margin.left;
elem.style('margin-left', marginAdjustment + 'px');
};
if (!textGroup) {
textGroup = elem.append('div')
.attr('class', 'tooltip-text-group')
.call(adjustMarginOfTooltips);
}
const primaryParameterUnitCode = parameter ? parameter.unit : '';
const secondaryParameterUnitCode = secondaryParameter ? secondaryParameter.unit : '';
let tooltipTextData = [];
if (currentPoint) {
tooltipTextData.push(getIVDataTooltipTextInfo(currentPoint, 'primary', primaryParameterUnitCode));
}
if (secondaryParameterPoint) {
tooltipTextData.push(getIVDataTooltipTextInfo(secondaryParameterPoint, 'secondary', secondaryParameterUnitCode));
}
if (comparePoint) {
tooltipTextData.push(getIVDataTooltipTextInfo(comparePoint, 'compare', primaryParameterUnitCode));
}
if (gwLevelPoint) {
tooltipTextData.push(getGWLevelTextInfo(gwLevelPoint, primaryParameterUnitCode));
}
const texts = textGroup
.selectAll('div')
.data(tooltipTextData);
// Remove old text labels
texts.exit()
.remove();
// Add new text labels
const newTexts = texts.enter()
.append('div');
// Update the text and backgrounds of all tooltip labels
const allTexts = texts.merge(newTexts);
allTexts
.text(textData => {
return textData.label;
})
.attr('class', textData => textData.classes.join(' '));
return textGroup;
};
/*
* Append a group containing the tooltip text elements to elem
* @param {Object} elem - D3 selector
*/
export const drawTooltipText = function(elem, store) {
elem.call(link(store, createTooltipTextGroup, createStructuredSelector({
currentPoint: getIVDataCursorPoint('primary', 'current'),
secondaryParameterPoint: getIVDataCursorPoint('secondary', 'current'),
comparePoint: getIVDataCursorPoint('compare', 'prioryear'),
gwLevelPoint: getGroundwaterLevelCursorPoint,
parameter: getPrimaryParameter,
secondaryParameter: getIVParameter('secondary'),
layout: getMainLayout
})));
};
/*
* Appends a group to elem containing a focus line and circles for the current and compare time series
* @param {Object} elem - D3 select
* @param {Object} store - Redux.Store
* @param {Object} yScale - D3 Y scale for the graph
*/
export const drawTooltipFocus = function(elem, store) {
elem.call(link(store, drawFocusLine, createStructuredSelector({
xScale: getMainXScale('current'),
yScale: getMainYScale,
cursorTime: getCursorTime('current')
})));
elem.call(link(store, drawFocusCircles, createSelector(
getIVDataTooltipPoint('primary', 'current'),
getIVDataTooltipPoint('secondary', 'current'),
getIVDataTooltipPoint('compare', 'prioryear'),
getGroundwaterLevelTooltipPoint,
(current, secondary, compare, gwLevel) => {
let points = [];
if (current) {
points.push(current);
}
if (secondary) {
points.push(secondary);
}
if (compare) {
points.push(compare);
}
if (gwLevel) {
points.push(gwLevel);
}
return points;
}
)));
elem.call(link(
store,
drawFocusOverlay,
createStructuredSelector({
xScale: getMainXScale('current'),
layout: getMainLayout
}),
store,
setGraphCursorOffset)
);
};
/*
* Initial rendering of the tooltip slider
* @param {D3 selection} elem
* @param {Redux store} store
*/
export const initializeTooltipCursorSlider = function(elem, store) {
elem.append('svg')
.classed('cursor-slider-svg', true)
.attr('xmlns', 'http://www.w3.org/2000/svg')
.call(link(store, (elem, layout) => {
elem.attr('viewBox', `0 0 ${layout.width + layout.margin.left + layout.margin.right} 25`);
}, getMainLayout));
};
/*
* Renders the cursor slider used to move the tooltip focus and sets up handlers
* to update the slider as the tooltip focus changes.
* @param {D3 selection} elem
* @param {Redux store} store
*/
export const drawTooltipCursorSlider = function(elem, store) {
elem.select('.cursor-slider-svg')
.call(drawCursorSlider, store);
};