import React, { Component } 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 asArray from '../lib/asArray'; import { LabelValueMetadata } from './LabelValueMetadata'; import CollapsibleSection from '../containers/CollapsibleSection'; import ScrollIndicatedDialogContent from '../containers/ScrollIndicatedDialogContent'; import ManifestInfo from '../containers/ManifestInfo'; /** * a dialog providing the possibility to select the collection */ export class CollectionDialog extends Component { /** */ static getUseableLabel(resource, index) { return (resource && resource.getLabel && resource.getLabel().length > 0) ? resource.getLabel().getValue() : String(index + 1); } /** */ constructor(props) { super(props); this.state = { filter: null }; this.hideDialog = this.hideDialog.bind(this); } /** */ setFilter(filter) { this.setState({ filter }); } /** */ hideDialog() { const { hideCollectionDialog, windowId, } = this.props; hideCollectionDialog(windowId); } /** */ selectCollection(c) { const { collectionPath, manifestId, showCollectionDialog, windowId, } = this.props; showCollectionDialog(c.id, [...collectionPath, manifestId], windowId); } /** */ goToPreviousCollection() { const { collectionPath, showCollectionDialog, windowId } = this.props; showCollectionDialog( collectionPath[collectionPath.length - 1], collectionPath.slice(0, -1), windowId, ); } /** */ selectManifest(m) { const { addWindow, collectionPath, manifestId, setWorkspaceAddVisibility, updateWindow, windowId, } = this.props; if (windowId) { updateWindow(windowId, { canvasId: null, collectionPath: [...collectionPath, manifestId], manifestId: m.id, }); } else { addWindow({ collectionPath: [...collectionPath, manifestId], manifestId: m.id }); } this.hideDialog(); setWorkspaceAddVisibility(false); } /** */ dialogContainer() { const { containerId, windowId } = this.props; return document.querySelector(`#${containerId} #${windowId}`); } /** */ placeholder() { const { classes } = this.props; return ( <Dialog className={classes.dialog} onClose={this.hideDialog} open container={this.dialogContainer()} BackdropProps={this.backdropProps()} > <DialogTitle id="select-collection"> <Skeleton className={classes.placeholder} variant="text" /> </DialogTitle> <ScrollIndicatedDialogContent> <Skeleton className={classes.placeholder} variant="text" /> <Skeleton className={classes.placeholder} variant="text" /> </ScrollIndicatedDialogContent> </Dialog> ); } /** */ backdropProps() { const { classes } = this.props; return { classes: { root: classes.dialog } }; } /** */ render() { const { classes, collection, error, isMultipart, manifest, ready, t, } = this.props; const { filter } = this.state; if (error) return null; // If this component is optimistically rendering ahead of the window its in // force a re-render so that it is placed correctly. The right thing here is // to maybe pass a ref. if (!this.dialogContainer()) { this.forceUpdate(); return null; } if (!ready) return this.placeholder(); const rights = manifest && (asArray(manifest.getProperty('rights') || manifest.getProperty('license'))); const requiredStatement = manifest && asArray(manifest.getRequiredStatement()).filter(l => l.getValue()).map(labelValuePair => ({ label: null, values: labelValuePair.getValues(), })); const collections = manifest.getCollections(); const currentFilter = filter || (collections.length > 0 ? 'collections' : 'manifests'); return ( <Dialog className={classes.dialog} onClose={this.hideDialog} container={this.dialogContainer()} BackdropProps={this.backdropProps()} open > <DialogTitle id="select-collection"> <Typography component="div" variant="overline"> { t(isMultipart ? 'multipartCollection' : 'collection') } </Typography> <Typography variant="h3"> {CollectionDialog.getUseableLabel(manifest)} </Typography> </DialogTitle> <ScrollIndicatedDialogContent className={classes.dialogContent}> { collection && ( <Button startIcon={<ArrowBackIcon />} onClick={() => this.goToPreviousCollection()} > {CollectionDialog.getUseableLabel(collection)} </Button> )} <div className={classes.collectionMetadata}> <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} underline="hover"> {v} </Link> </Typography> )) } </> ) } </CollapsibleSection> </div> <div className={classes.collectionFilter}> {manifest.getTotalCollections() > 0 && ( <Chip clickable color={currentFilter === 'collections' ? 'primary' : 'default'} onClick={() => this.setFilter('collections')} label={t('totalCollections', { count: manifest.getTotalCollections() })} /> )} {manifest.getTotalManifests() > 0 && ( <Chip clickable color={currentFilter === 'manifests' ? 'primary' : 'default'} onClick={() => this.setFilter('manifests')} label={t('totalManifests', { count: manifest.getTotalManifests() })} /> )} </div> { currentFilter === 'collections' && ( <MenuList> { collections.map(c => ( <MenuItem key={c.id} onClick={() => { this.selectCollection(c); }} className={classes.collectionItem} > {CollectionDialog.getUseableLabel(c)} </MenuItem> )) } </MenuList> )} { currentFilter === 'manifests' && ( <MenuList> { manifest.getManifests().map(m => ( <MenuItem key={m.id} onClick={() => { this.selectManifest(m); }} className={classes.collectionItem} > {CollectionDialog.getUseableLabel(m)} </MenuItem> )) } </MenuList> )} </ScrollIndicatedDialogContent> <DialogActions> <Button onClick={this.hideDialog}> {t('close')} </Button> </DialogActions> </Dialog> ); } } CollectionDialog.propTypes = { addWindow: PropTypes.func.isRequired, classes: PropTypes.objectOf(PropTypes.string).isRequired, collection: PropTypes.object, // eslint-disable-line react/forbid-prop-types collectionPath: PropTypes.arrayOf(PropTypes.string), containerId: 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, t: PropTypes.func.isRequired, updateWindow: PropTypes.func.isRequired, windowId: PropTypes.string, }; CollectionDialog.defaultProps = { collection: null, collectionPath: [], containerId: null, error: null, isMultipart: false, ready: false, windowId: null, };