react-occult
Version:
Layered Information Visualization based on React and D3
165 lines (148 loc) • 5.03 kB
JavaScript
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import Annotation from '../../plots/Annotation';
import SpanOrDiv from '../../widgets/SpanOrDiv';
import processAnnotations from './processAnnotations';
import adjustedAnnotationKeyMapper from './adjustedAnnotationKeyMapper';
const AnnotationLayer = props => {
const [svgAnnotations, setSvgAnnotations] = useState([]);
const [htmlAnnotations, setHtmlAnnotations] = useState([]);
const [adjustedAnnotations, setAdjustedAnnotations] = useState([]);
const [adjustedAnnotationsKey, setAdjustedAnnotationsKey] = useState('');
const createAnnotations = props => {
let newAdjustedAnnotations = adjustedAnnotations.slice();
let newAdjustableAnnotationsKey = adjustedAnnotationsKey;
const newAdjustedAnnotationsKey = adjustedAnnotationsKey;
const {
annotations,
annotationHandling = false,
size,
generateSVGAnnotations,
generateHTMLAnnotations,
voronoiHover
} = props;
const annotationProcessor =
typeof annotationHandling === 'object'
? annotationHandling
: { layout: { type: annotationHandling } };
// todo: create svg anno
const initialSVGAnnotations = annotations
.map((d, i) => generateSVGAnnotations({ d, i, voronoiHover }))
.filter(d => d);
const adjustableAnnotations = initialSVGAnnotations.filter(
d => d && d.props && d.props.noteData && !d.props.noteData.fixedPosition
);
const fixedAnnotations = initialSVGAnnotations.filter(
d => !d.props || !d.props.noteData || d.props.noteData.fixedPosition
);
newAdjustableAnnotationsKey = `${adjustableAnnotations
.map(adjustedAnnotationKeyMapper)
.join(',')}${JSON.stringify(annotationProcessor)}${size.join(',')}`;
if (annotationHandling === false) {
newAdjustedAnnotations = adjustableAnnotations;
}
if (
newAdjustedAnnotations.length !== adjustableAnnotations.length ||
newAdjustedAnnotationsKey !== newAdjustableAnnotationsKey
) {
newAdjustedAnnotations = processAnnotations(
adjustableAnnotations,
annotationProcessor,
props
);
} else {
//Handle when style or other attributes change
newAdjustedAnnotations = newAdjustedAnnotations.map((d, i) => {
const newNoteData = Object.assign(
adjustableAnnotations[i].props.noteData,
{
nx: d.props.noteData.nx,
ny: d.props.noteData.ny,
note: d.props.noteData.note
}
);
return <Annotation key={d.key} noteData={newNoteData} />;
});
}
const renderedSVGAnnotations = [
...newAdjustedAnnotations,
...fixedAnnotations
];
const renderedHTMLAnnotations = annotations.map((d, i) =>
generateHTMLAnnotations({ d, i, voronoiHover })
);
setAdjustedAnnotations(newAdjustedAnnotations);
setSvgAnnotations(renderedSVGAnnotations);
setHtmlAnnotations(renderedHTMLAnnotations);
setAdjustedAnnotationsKey(newAdjustableAnnotationsKey);
};
const { voronoiHover, frameData, frameProps } = props;
useEffect(() => {
createAnnotations(props);
}, [voronoiHover, frameData, frameProps]);
const { useSpans, size, margin } = props;
return (
<SpanOrDiv
span={useSpans}
className="annotation-layer"
style={{
position: 'absolute',
pointerEvents: 'none',
background: 'none'
}}
>
<svg
className="annotation-layer-svg"
height={size[1]}
width={size[0]}
style={{
background: 'none',
pointerEvents: 'none',
position: 'absolute',
left: `${margin.left}px`,
top: `${margin.top}px`,
overflow: 'visible'
}}
>
<g>{svgAnnotations}</g>
</svg>
<SpanOrDiv
span={useSpans}
className="annotation-layer-html"
style={{
background: 'none',
pointerEvents: 'none',
position: 'absolute',
height: `${size[1]}px`,
width: `${size[0]}px`,
left: `${margin.left}px`,
top: `${margin.top}px`
}}
>
{htmlAnnotations}
</SpanOrDiv>
</SpanOrDiv>
);
};
AnnotationLayer.propTypes = {
margin: PropTypes.object,
voronoiHover: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
useSpans: PropTypes.bool,
size: PropTypes.array,
axes: PropTypes.array,
annotationHandling: PropTypes.func,
annotations: PropTypes.array,
pointSizeFunction: PropTypes.func,
labelSizeFunction: PropTypes.func,
position: PropTypes.array,
generateSVGAnnotations: PropTypes.func,
generateHTMLAnnotations: PropTypes.func,
frameData: PropTypes.object,
frameProps: PropTypes.object
};
AnnotationLayer.defaultProps = {
margin: { top: 0, bottom: 0, left: 0, right: 0 },
generateSVGAnnotations: () => [],
generateHTMLAnnotations: () => []
};
export default AnnotationLayer;