@@ -108,6 +108,11 @@ function findClosest(data, target) {
108108 return finalIx
109109}
110110
111+ function getClosest(data, target) {
112+ const closestIndex = findClosest(data, target)
113+ return data[closestIndex]
114+ }
115+
111116function postMessage(type, payload) {
112117 if (window.ReactNativeWebView) {
113118 window.ReactNativeWebView.postMessage(JSON.stringify({ type, payload }))
@@ -380,11 +385,61 @@ window.draw = (props) => {
380385 .attr('x', 0)
381386 .attr('y', 0)
382387
388+ defs
389+ .append('linearGradient')
390+ .attr('id', 'error-gradient')
391+ .attr('gradientUnits', 'userSpaceOnUse')
392+ .attr('x1', 0)
393+ .attr('x2', 0)
394+ .attr('y1', 0)
395+ .attr('y2', height)
396+ .selectAll('stop')
397+ .data([
398+ { offset: '0%', opacity: 0 },
399+ { offset: '100%', opacity: 0.1 },
400+ ])
401+ .enter()
402+ .append('stop')
403+ .attr('offset', (d) => d.offset)
404+ .attr('stop-opacity', (d) => d.opacity)
405+ .attr('stop-color', '#000000')
406+
383407 const chart = selectOrAppend(svg, 'g', 'chart').attr(
384408 'clip-path',
385409 'url(#clip)'
386410 )
387411
412+ var drawErrorSegments = () => {
413+ const errorSegmentsHolder = selectOrAppend(
414+ chart,
415+ 'g',
416+ 'errorSegmentsHolder'
417+ )
418+ errorSegmentsHolder.selectAll('rect').remove()
419+ if (!props.errorSegments?.length) return
420+
421+ try {
422+ const parseTime = d3.timeParse('%Q')
423+ errorSegmentsHolder
424+ .selectAll('rect')
425+ .data(
426+ props.errorSegments.map(({ start, end }) => ({
427+ startDate: parseTime(start),
428+ endDate: parseTime(end),
429+ }))
430+ )
431+ .enter()
432+ .append('rect')
433+ .attr('fill', 'url(#error-gradient)')
434+ .attr('height', height)
435+ .attr('width', (d) => x(d.endDate) - x(d.startDate))
436+ .attr('x', (d) => x(d.startDate))
437+ } catch (e) {
438+ postMessage('error segment update error', e.message)
439+ }
440+ }
441+ drawErrorSegments()
442+
388443 const getGradientOffset =
389444 (yScale) =>
390445 ({ value }) =>
@@ -445,7 +500,7 @@ window.draw = (props) => {
445500 opacity: 0.223958 * baseOpacity,
446501 },
447502 { value: y.domain()[0], opacity: 0 },
448- ]
503+ ]
449504 defs
450505 .append('linearGradient')
451506 .attr('id', 'area-gradient' + index)
@@ -767,6 +822,7 @@ window.draw = (props) => {
767822
768823 updateHighlight()
769824 updateSlices()
825+ drawErrorSegments()
770826 } catch (e) {
771827 postMessage('zoomerror', e.message)
772828 }
@@ -788,6 +844,10 @@ window.draw = (props) => {
788844 const x0 = width * highlightPosition
789845 const highlightExactDate = x.invert(x0)
790846
847+ const highlightedErrorSegment = props.errorSegments?.find(
848+ (s) => s.start < highlightExactDate && s.end > highlightExactDate
849+ )
850+
791851 var highlightTime = null
792852 props.datasets.forEach((dataset, index) => {
793853 const { unit, decimals } = dataset
@@ -802,19 +862,25 @@ window.draw = (props) => {
802862 return
803863 }
804864
805- const pointIndex = findClosest(definedData, highlightExactDate)
806- const highlight = definedData[pointIndex]
807-
865+ const highlight = getClosest(definedData, highlightExactDate)
808866 const xValue = x(highlight.date)
809867
810- const tooFar =
811- Math.abs(x0 - xValue) > 8 &&
868+ const is10MinutesAway =
812869 Math.abs(highlightExactDate - highlight.date) > 10 * 60 * 1000
870+ const is8PixelsAway = Math.abs(x0 - xValue) > 8
813871
814- const color = getDatasetColor(
815- dataset,
816- tooFar ? undefined : highlight.value
817- )
872+ // Closest defined point is more than 10 minutes away, or we are in error segment + closest _unfiltered_ point is null
873+ const tooFar =
874+ is8PixelsAway &&
875+ (is10MinutesAway ||
876+ (!!highlightedErrorSegment &&
877+ getClosest(operators[index].domainData, highlightExactDate)
878+ ?.value === null))
879+ const isInErrorSegment = highlightedErrorSegment && tooFar
880+
881+ const color = isInErrorSegment
882+ ? highlightedErrorSegment.messageColor
883+ : getDatasetColor(dataset, tooFar ? undefined : highlight.value)
818884
819885 if (!tooFar) {
820886 highlightTime = highlight.date
@@ -832,9 +898,11 @@ window.draw = (props) => {
832898 .select('span#highlightvalue' + index)
833899 .style('color', color)
834900 .html(
835- tooFar
836- ? props.noDataString
837- : d3.format('.' + decimals + 'f')(highlight.value) + ' ' + unit
901+ isInErrorSegment
902+ ? highlightedErrorSegment.message
903+ : tooFar
904+ ? props.noDataString
905+ : d3.format('.' + decimals + 'f')(highlight.value) + ' ' + unit
838906 )
839907 })
840908
0 commit comments