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.
171 lines (153 loc) • 4.82 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;
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,
}),
),
}).isRequired,
visibleNodeIds: PropTypes.arrayOf(PropTypes.string).isRequired,
windowId: PropTypes.string.isRequired,
};