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.
106 lines (87 loc) • 3.11 kB
JSX
import Openseadragon from 'openseadragon';
import PropTypes from 'prop-types';
import { useContext, useEffect, useRef } from 'react';
import OpenSeadragonViewerContext from '../contexts/OpenSeadragonViewerContext';
import FailedImageContext from '../contexts/FailedImageContext';
/** OSD tile source shim that adds + updates its tile source data */
export default function OpenSeadragonTileSource({
index = undefined,
opacity = undefined,
fitBounds = undefined,
tileSource = {},
url = undefined,
}) {
const viewer = useContext(OpenSeadragonViewerContext);
const { notifyFailure, fallbackImage } = useContext(FailedImageContext);
const tiledImage = useRef(undefined);
useEffect(() => {
if (opacity == null) return;
tiledImage.current?.setOpacity(opacity);
}, [opacity]);
useEffect(() => {
if (!fitBounds) return;
tiledImage.current?.fitBounds(new Openseadragon.Rect(...fitBounds));
}, [fitBounds]);
useEffect(() => {
if (!tiledImage.current || !viewer?.current || index == null) return;
viewer.current.world.setItemIndex(tiledImage.current, index);
}, [index, viewer]);
useEffect(() => {
if (!viewer?.current) return undefined;
const loadFallback = () => {
if (!fallbackImage) {
return;
}
viewer.current.addTiledImage({
tileSource: { type: 'image', url: fallbackImage },
index,
opacity,
fitBounds: fitBounds ? new Openseadragon.Rect(...fitBounds) : undefined,
success: (ev) => {
tiledImage.current = ev.item;
},
});
};
const promise = new Promise((resolve, reject) => {
const localTileSource = (url && { type: 'image', url }) ||
((typeof tileSource === 'string' || tileSource instanceof String) && tileSource) ||
// OSD mutates this object, so we give it a shallow copy
{ ...tileSource };
viewer.current?.addTiledImage({
tileSource: localTileSource,
index,
opacity,
fitBounds: fitBounds ? new Openseadragon.Rect(...fitBounds) : undefined,
success: (event) => resolve(event),
error: (event) => {
const imageUrl = url || (typeof tileSource === 'string' ? tileSource : tileSource?.['@id']);
if (imageUrl) notifyFailure(imageUrl);
loadFallback();
reject(event);
},
});
})
.then((event) => {
tiledImage.current = event.item;
})
.catch((err) => {
console.warn('[Mirador: OSD tile image load failed]', err);
});
const osd = viewer.current;
return () =>
promise.finally(() => {
if (osd && tiledImage.current) {
osd.world.removeItem(tiledImage.current);
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [viewer?.current, url]);
return null;
}
OpenSeadragonTileSource.propTypes = {
fitBounds: PropTypes.arrayOf(PropTypes.number),
index: PropTypes.number,
opacity: PropTypes.number,
tileSource: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
url: PropTypes.string,
};