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.
245 lines (223 loc) • 7.84 kB
JSX
import { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import {
Button,
Chip,
Dialog,
DialogActions,
DialogTitle,
Link,
MenuList,
MenuItem,
Typography,
} from '@mui/material';
import ArrowBackIcon from '@mui/icons-material/ArrowBackSharp';
import Skeleton from '@mui/material/Skeleton';
import { styled } from '@mui/material/styles';
import { useTranslation } from 'react-i18next';
import asArray from '../lib/asArray';
import LabelValueMetadata from '../containers/LabelValueMetadata';
import CollapsibleSection from '../containers/CollapsibleSection';
import ScrollIndicatedDialogContent from '../containers/ScrollIndicatedDialogContent';
import ManifestInfo from '../containers/ManifestInfo';
import WorkspaceContext from '../contexts/WorkspaceContext';
import { IIIFResourceLabel } from './IIIFResourceLabel';
const StyledScrollIndicatedDialogContent = styled(ScrollIndicatedDialogContent)(() => ({
padding: (theme) => theme.spacing(1),
}));
const StyledCollectionMetadata = styled('div')(() => ({
'& .MuiPaper-root': {
background: 'transparent',
},
padding: (theme) => theme.spacing(2),
}));
const StyledCollectionFilter = styled('div')(() => ({
padding: (theme) => theme.spacing(2),
paddingTop: 0,
}));
/** */
const Placeholder = ({ onClose, container }) => (
<Dialog
variant="contained"
onClose={onClose}
open
container={container}
>
<DialogTitle id="select-collection">
<Skeleton variant="text" />
</DialogTitle>
<ScrollIndicatedDialogContent>
<Skeleton variant="text" />
<Skeleton variant="text" />
</ScrollIndicatedDialogContent>
</Dialog>
);
Placeholder.propTypes = {
container: PropTypes.instanceOf(Element).isRequired,
onClose: PropTypes.func.isRequired,
};
/**
* a dialog providing the possibility to select the collection
*/
export function CollectionDialog({
addWindow, collection = null, dialogCollectionPath = [], error = null, hideCollectionDialog,
isMultipart = false, manifest, manifestId, ready = false,
setWorkspaceAddVisibility, showCollectionDialog, updateWindow, windowId = null,
}) {
const container = useContext(WorkspaceContext);
const { t } = useTranslation();
const [filter, setFilter] = useState(null);
/** */
const hideDialog = () => {
hideCollectionDialog(windowId);
};
/** */
const selectCollection = (c) => {
showCollectionDialog(c.id, [...dialogCollectionPath, manifestId], windowId);
};
/** */
const goToPreviousCollection = () => {
showCollectionDialog(
dialogCollectionPath[dialogCollectionPath.length - 1],
dialogCollectionPath.slice(0, -1),
windowId,
);
};
/** */
const selectManifest = (m) => {
if (windowId) {
updateWindow(windowId, {
canvasId: null, collectionPath: [...dialogCollectionPath, manifestId], manifestId: m.id,
});
} else {
addWindow({ collectionPath: [...dialogCollectionPath, manifestId], manifestId: m.id });
}
hideDialog();
setWorkspaceAddVisibility(false);
};
/** */
const dialogContainer = (container?.current || document.body).querySelector(`#${windowId}`);
if (error) return null;
if (!dialogContainer) {
return null;
}
if (!ready) return <Placeholder container={dialogContainer} onClose={hideDialog} />;
const rights = manifest && (asArray(manifest.getProperty('rights') || manifest.getProperty('license')));
const requiredStatement = manifest
&& asArray(manifest.getRequiredStatement()).filter(l => l && l.getValue()).map(labelValuePair => ({
label: null,
values: labelValuePair.getValues(),
}));
const collections = manifest.getCollections();
const currentFilter = filter || (collections.length > 0 ? 'collections' : 'manifests');
return (
<Dialog
variant="contained"
onClose={hideDialog}
container={dialogContainer}
open
>
<DialogTitle id="select-collection">
<Typography component="div" variant="overline">
{ t(isMultipart ? 'multipartCollection' : 'collection') }
</Typography>
<Typography component="div" variant="h3">
<IIIFResourceLabel resource={manifest} />
</Typography>
</DialogTitle>
<StyledScrollIndicatedDialogContent>
{ collection && (
<Button
startIcon={<ArrowBackIcon />}
onClick={() => goToPreviousCollection()}
>
<IIIFResourceLabel resource={collection} />
</Button>
)}
<StyledCollectionMetadata>
<ManifestInfo manifestId={manifest.id} />
<CollapsibleSection
id="select-collection-rights"
label={t('attributionTitle')}
>
{ requiredStatement && (
<LabelValueMetadata labelValuePairs={requiredStatement} defaultLabel={t('attribution')} />
)}
{
rights && rights.length > 0 && (
<>
<Typography variant="subtitle2" component="dt">{t('rights')}</Typography>
{ rights.map(v => (
<Typography variant="body1" component="dd" key={v}>
<Link target="_blank" rel="noopener noreferrer" href={v}>
{v}
</Link>
</Typography>
)) }
</>
)
}
</CollapsibleSection>
</StyledCollectionMetadata>
<StyledCollectionFilter>
{manifest.getTotalCollections() > 0 && (
<Chip clickable color={currentFilter === 'collections' ? 'primary' : 'default'} onClick={() => setFilter('collections')} label={t('totalCollections', { count: manifest.getTotalCollections() })} />
)}
{manifest.getTotalManifests() > 0 && (
<Chip clickable color={currentFilter === 'manifests' ? 'primary' : 'default'} onClick={() => setFilter('manifests')} label={t('totalManifests', { count: manifest.getTotalManifests() })} />
)}
</StyledCollectionFilter>
{ currentFilter === 'collections' && (
<MenuList>
{
collections.map(c => (
<MenuItem
key={c.id}
onClick={() => { selectCollection(c); }}
variant="multiline"
>
<IIIFResourceLabel resource={c} />
</MenuItem>
))
}
</MenuList>
)}
{ currentFilter === 'manifests' && (
<MenuList>
{
manifest.getManifests().map(m => (
<MenuItem
key={m.id}
onClick={() => { selectManifest(m); }}
variant="multiline"
>
<IIIFResourceLabel resource={m} />
</MenuItem>
))
}
</MenuList>
)}
</StyledScrollIndicatedDialogContent>
<DialogActions>
<Button onClick={hideDialog}>
{t('close')}
</Button>
</DialogActions>
</Dialog>
);
}
CollectionDialog.propTypes = {
addWindow: PropTypes.func.isRequired,
collection: PropTypes.object, // eslint-disable-line react/forbid-prop-types
dialogCollectionPath: PropTypes.arrayOf(PropTypes.string),
error: PropTypes.string,
hideCollectionDialog: PropTypes.func.isRequired,
isMultipart: PropTypes.bool,
manifest: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
manifestId: PropTypes.string.isRequired,
ready: PropTypes.bool,
setWorkspaceAddVisibility: PropTypes.func.isRequired,
showCollectionDialog: PropTypes.func.isRequired,
updateWindow: PropTypes.func.isRequired,
windowId: PropTypes.string,
};