mirador
Version:
An open-source, web-based 'multi-up' viewer that supports zoom-pan-rotate functionality, ability to display/compare simple images, and images with annotations.
113 lines (106 loc) • 4.22 kB
JSX
import { useCallback } from 'react';
import PropTypes from 'prop-types';
import Chip from '@mui/material/Chip';
import MenuList from '@mui/material/MenuList';
import MenuItem from '@mui/material/MenuItem';
import ListItemText from '@mui/material/ListItemText';
import Typography from '@mui/material/Typography';
import { useTranslation } from 'react-i18next';
import SanitizedHtml from '../containers/SanitizedHtml';
import { ScrollTo } from './ScrollTo';
/**
* CanvasAnnotations ~
*/
export function CanvasAnnotations({
annotations = [], index, label, selectedAnnotationId = undefined, totalSize,
listContainerComponent = 'li', htmlSanitizationRuleSet = 'iiif', hoveredAnnotationIds = [],
containerRef = undefined, deselectAnnotation, selectAnnotation, windowId, hoverAnnotation,
}) {
const { t } = useTranslation();
const handleClick = useCallback((_event, annotation) => {
if (selectedAnnotationId === annotation.id) {
deselectAnnotation(windowId, annotation.id);
} else {
selectAnnotation(windowId, annotation.id);
}
}, [windowId, deselectAnnotation, selectAnnotation, selectedAnnotationId]);
const handleAnnotationHover = useCallback((annotation) => {
hoverAnnotation(windowId, [annotation.id]);
}, [hoverAnnotation, windowId]);
const handleAnnotationBlur = useCallback(() => {
hoverAnnotation(windowId, []);
}, [hoverAnnotation, windowId]);
if (annotations.length === 0) return null;
return (
<>
<Typography sx={{ paddingLeft: 2, paddingRight: 1, paddingTop: 2 }} variant="overline">
{t('annotationCanvasLabel', { context: `${index + 1}/${totalSize}`, label })}
</Typography>
<MenuList autoFocusItem variant="selectedMenu">
{annotations.map((annotation) => (
<ScrollTo
containerRef={containerRef}
key={`${annotation.id}-scroll`}
offsetTop={96} // offset for the height of the form above
scrollTo={selectedAnnotationId === annotation.id}
selected={selectedAnnotationId === annotation.id}
>
<MenuItem
component={listContainerComponent}
variant="multiline"
divider
sx={{
'&:hover,&:focus': {
backgroundColor: 'action.hover',
},
backgroundColor: hoveredAnnotationIds.includes(annotation.id) ? 'action.hover' : '',
}}
key={annotation.id}
annotationid={annotation.id}
onClick={(e) => handleClick(e, annotation)}
onFocus={() => handleAnnotationHover(annotation)}
onBlur={handleAnnotationBlur}
onMouseEnter={() => handleAnnotationHover(annotation)}
onMouseLeave={handleAnnotationBlur}
>
<ListItemText
primaryTypographyProps={{ variant: 'body2' }}
primary={
<SanitizedHtml ruleSet={htmlSanitizationRuleSet} htmlString={annotation.content} />
}
secondary={
annotation.tags.map((tag) => (
<Chip component="span" size="small" variant="outlined" label={tag} id={tag} key={tag.toString()} />
))
}
/>
</MenuItem>
</ScrollTo>
))}
</MenuList>
</>
);
}
CanvasAnnotations.propTypes = {
annotations: PropTypes.arrayOf(
PropTypes.shape({
content: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
}),
),
containerRef: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
]),
deselectAnnotation: PropTypes.func.isRequired,
hoverAnnotation: PropTypes.func.isRequired,
hoveredAnnotationIds: PropTypes.arrayOf(PropTypes.string),
htmlSanitizationRuleSet: PropTypes.string,
index: PropTypes.number.isRequired,
label: PropTypes.string.isRequired,
listContainerComponent: PropTypes.elementType,
selectAnnotation: PropTypes.func.isRequired,
selectedAnnotationId: PropTypes.string,
totalSize: PropTypes.number.isRequired,
windowId: PropTypes.string.isRequired,
};