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.
169 lines (151 loc) • 4.88 kB
JSX
import PropTypes from 'prop-types';
import { alpha, styled } from '@mui/material/styles';
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
import { TreeItem } from '@mui/x-tree-view/TreeItem';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import { ScrollTo } from './ScrollTo';
const StyledVisibleNode = styled('div')(() => ({
}));
/** */
function getStartCanvasId(node) {
const jsonld = node.data.__jsonld; // eslint-disable-line no-underscore-dangle
if (jsonld.startCanvas && typeof jsonld.startCanvas === 'string') {
return jsonld.startCanvas;
}
if (jsonld.start) {
if (jsonld.start.type === 'Canvas' && typeof jsonld.start.id === 'string') {
return jsonld.start.id;
}
if (jsonld.start.type === 'SpecificResource' && typeof jsonld.start.source === 'string') {
return jsonld.start.source;
}
}
return node.data.getCanvasIds()[0];
}
/** */
function deepFind(treeNode, id) {
if (treeNode.id === id) {
return treeNode;
}
let result = null;
if (treeNode.nodes) {
for (let i = 0; result == null && i < treeNode.nodes.length; i += 1) {
result = deepFind(treeNode.nodes[i], id);
}
}
return result;
}
/** */
const ScrollToForTreeItem = ({ children, itemId, ...props }) => (
<ScrollTo {...props}>
{children}
</ScrollTo>
);
ScrollToForTreeItem.propTypes = {
children: PropTypes.node.isRequired,
itemId: PropTypes.string.isRequired,
};
/** */
const CollapseIcon = (props) => <ExpandMoreIcon {...props} color="action" />;
/** */
const ExpandIcon = (props) => <ChevronRightIcon {...props} color="action" />;
/** */
export function SidebarIndexTableOfContents({
toggleNode, expandNodes, setCanvas, windowId,
treeStructure, visibleNodeIds, expandedNodeIds, containerRef, nodeIdToScrollTo,
}) {
/** */
const handleNodeSelect = (event, itemId) => {
if (event.key === ' ' || event.key === 'Spacebar') {
toggleNode(itemId);
}
selectTreeItem(itemId);
};
/** */
const handleNodeToggle = (_event, itemIds) => {
expandNodes(itemIds);
};
/** */
const selectTreeItem = (itemId) => {
const node = deepFind(treeStructure, itemId);
// Do not select if there are no canvases listed or it has children
if (!node.data.getCanvasIds()
|| node.data.getCanvasIds().length === 0
|| node.nodes.length > 0) {
return;
}
const target = getStartCanvasId(node);
const canvasId = target.indexOf('#') === -1 ? target : target.substr(0, target.indexOf('#'));
setCanvas(windowId, canvasId);
};
if (!treeStructure) {
return null;
}
/** */
const renderTree = (node) => (
<ScrollToForTreeItem
containerRef={containerRef}
key={node.id}
itemId={node.id}
offsetTop={96}
scrollTo={nodeIdToScrollTo === node.id}
>
<TreeItem
itemId={node.id}
label={(
<StyledVisibleNode
sx={theme => ({
backgroundColor: visibleNodeIds.indexOf(node.id) !== -1
&& alpha(theme.palette.highlights?.primary || theme.palette.action.selected, 0.35),
display: visibleNodeIds.indexOf(node.id) !== -1 && 'inline',
})}
>
{node.label}
</StyledVisibleNode>
)}
>
{node.nodes && node.nodes.length > 0 ? node.nodes.map(renderTree) : null}
</TreeItem>
</ScrollToForTreeItem>
);
return (
<SimpleTreeView
sx={{ flexGrow: 1 }}
slots={{
collapseIcon: CollapseIcon,
endIcon: null,
expandIcon: ExpandIcon,
}}
onSelectedItemsChange={handleNodeSelect}
onExpandedItemsChange={handleNodeToggle}
expandedItems={expandedNodeIds}
>
{Array.isArray(treeStructure.nodes) && treeStructure.nodes.length > 0
? treeStructure.nodes.map(n => renderTree(n))
: <p>No items found</p>}
</SimpleTreeView>
);
}
SidebarIndexTableOfContents.propTypes = {
containerRef: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
]).isRequired,
expandedNodeIds: PropTypes.arrayOf(PropTypes.string).isRequired,
expandNodes: PropTypes.func.isRequired,
nodeIdToScrollTo: PropTypes.string.isRequired,
setCanvas: PropTypes.func.isRequired,
toggleNode: PropTypes.func.isRequired,
treeStructure: PropTypes.shape({
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
nodes: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
nodes: PropTypes.array, // eslint-disable-line react/forbid-prop-types
})),
}).isRequired,
visibleNodeIds: PropTypes.arrayOf(PropTypes.string).isRequired,
windowId: PropTypes.string.isRequired,
};