Create an image detail modal
This recipe covers a modal which can show an image and additional data. It's mostly meant to serve as an example for displaying an image in a modal while maintaining its aspect ratio.
This is by no means an example for a be-all and end-all image detail modal. Assumptions have been made, and functionality has been omitted, in favour of brevity. It should be carefully evaluated and refined when building a production worthy image detail modal.
Recommended reading
-
The 'Create a modal' guide for instructions on creating and setting up a modal.
ImageDetailsModal.jsx.js
JavaScript
import React, { useCallback, useEffect, useState } from 'react';
// More information about each of the following imports can be found in the FontoXML Editor API
// section of the documentation (http://documentation.fontoxml.com/api/).
import {
Button,
ContainedImage,
Flex,
HorizontalSeparationLine,
KeyValueList,
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
SpinnerIcon,
StateMessage
} from 'fds/components';
import documentsManager from 'fontoxml-documents/src/documentsManager.js';
import FxImageLoader from 'fontoxml-fx/src/FxImageLoader.jsx';
import referencesManager from 'fontoxml-references/src/referencesManager.js';
function ImageDetailsModal({ cancelModal, data }) {
const [target, setTarget] = useState(null);
const [metadata, setMetadata] = useState(null);
const handleKeyDown = useCallback(
event => {
switch (event.key) {
case 'Escape':
cancelModal();
break;
}
},
[cancelModal]
);
// Upon loading the modal we use the documentsManager to get the image node. This is then used
// to get the value of the attribute containing the permanentid. This passed to the retrieveSingle
// function of the referencesManager to get the actual target and any metadata associated with
// the image.
useEffect(() => {
const contextNode = documentsManager.getNodeById(data.contextNodeId);
const imagePermanentId = contextNode.getAttribute('href');
referencesManager.retrieveSingle(imagePermanentId).then(result => {
setTarget(result.target);
setMetadata(result.metadata);
});
}, [data.contextNodeId]);
// The FxImageLoader component is used to load the actual image. This component returns an Object
// which we can use to display the image in our modal. The modal contains three StateMessage
// components for various states and uses Flex components to display the image and any metadata
// associated with the image. The image is either shown full size, or if the size would exceed
// that of the modal will use the maximum width while maintaining its aspect ratio.
return (
<Modal size="m" isFullHeight onKeyDown={handleKeyDown}>
<ModalHeader icon="image" title="Image" />
<ModalBody>
<ModalContent alignItems="center" flex="1" flexDirection="column">
{target && (
<FxImageLoader remoteId={target} type="web">
{({ isErrored, isLoading, imageData }) => {
if (isErrored) {
return (
<StateMessage
connotation="warning"
paddingSize="m"
visual="exclamation-triangle"
message="Could not load image."
/>
);
}
if (isLoading) {
return (
<StateMessage
paddingSize="m"
visual={<SpinnerIcon />}
message="Loading image..."
/>
);
}
return (
<Flex flex="1" flexDirection="column">
<Flex
flex="1"
flexDirection="column"
justifyContent="center"
paddingSize="l"
spaceSize="m"
>
<ContainedImage src={imageData.dataUrl} />
</Flex>
{metadata && metadata.properties && (
<Flex flex="none" flexDirection="column">
<Flex paddingSize={{ horizontal: 'l' }}>
<HorizontalSeparationLine />
</Flex>
<KeyValueList
valueByKey={metadata.properties}
scrollLimit={5}
paddingSize="l"
/>
</Flex>
)}
</Flex>
);
}}
</FxImageLoader>
)}
{!target && (
<StateMessage
paddingSize="m"
visual={<SpinnerIcon />}
message="Loading image..."
/>
)}
</ModalContent>
</ModalBody>
<ModalFooter>
<Button label="Close" onClick={cancelModal} />
</ModalFooter>
</Modal>
);
}
export default ImageDetailsModal;