Skip to content

Commit

Permalink
Merge pull request #3994 from ProjectMirador/thumbnail-function
Browse files Browse the repository at this point in the history
Convert ThumbnailNavigation to a function.
  • Loading branch information
cbeer authored Dec 2, 2024
2 parents f956d2a + ec54962 commit f1164fe
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 156 deletions.
2 changes: 1 addition & 1 deletion src/components/OpenSeadragonViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class OpenSeadragonViewer extends Component {
}
} else if (!isEqual(canvasWorld.layers, prevProps.canvasWorld.layers)) {
this.refreshTileProperties();
} else if (viewerConfig !== prevProps.viewerConfig) {
} else if (viewerConfig && viewerConfig !== prevProps.viewerConfig) {
const { viewport } = viewer;

if (viewerConfig.x !== viewport.centerSpringX.target.value
Expand Down
254 changes: 99 additions & 155 deletions src/components/ThumbnailNavigation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createRef, Component } from 'react';
import { useCallback, useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import Paper from '@mui/material/Paper';
import AutoSizer from 'react-virtualized-auto-sizer';
Expand All @@ -7,44 +7,32 @@ import classNames from 'classnames';
import CanvasWorld from '../lib/CanvasWorld';
import ThumbnailCanvasGrouping from '../containers/ThumbnailCanvasGrouping';
import ns from '../config/css-ns';

/**
*/
export class ThumbnailNavigation extends Component {
/**
*/
constructor(props) {
super(props);

this.scrollbarSize = 15;
this.spacing = 8; // 2 * (2px margin + 2px border + 2px padding + 2px padding)
this.calculateScaledSize = this.calculateScaledSize.bind(this);
this.itemCount = this.itemCount.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.nextCanvas = this.nextCanvas.bind(this);
this.previousCanvas = this.previousCanvas.bind(this);
this.gridRef = createRef();
}

/**
* If the view has changed and the thumbnailNavigation is open, recompute all
* of the grids
*/
componentDidUpdate(prevProps) {
const { canvasIndex, position, view } = this.props;
if (prevProps.view !== view && position !== 'off') {
this.gridRef.current.resetAfterIndex(0);
export function ThumbnailNavigation({
canvasGroupings, canvasIndex, hasNextCanvas = false, hasPreviousCanvas = false, position,
setNextCanvas = () => {}, setPreviousCanvas = () => {}, t, thumbnailNavigation, view = undefined, viewingDirection = '', windowId,
}) {
const scrollbarSize = 15;
const spacing = 8; // 2 * (2px margin + 2px border + 2px padding + 2px padding)
const gridRef = useRef();
const previousView = useRef(view);

useEffect(() => {
if (previousView.current !== view && position !== 'off') {
previousView.current = view;
gridRef.current.resetAfterIndex(0);
}
if (prevProps.canvasIndex !== canvasIndex) {
let index = canvasIndex;
if (view === 'book') index = Math.ceil(index / 2);
this.gridRef.current.scrollToItem(index, 'center');
}
}
}, [view, position]);

useEffect(() => {
let index = canvasIndex;
if (view === 'book') index = Math.ceil(index / 2);
gridRef.current.scrollToItem(index, 'center');
}, [canvasIndex, view]);

/** */
handleKeyDown(e) {
const { position } = this.props;
const handleKeyDown = (e) => {
let nextKey = 'ArrowRight';
let previousKey = 'ArrowLeft';
if (position === 'far-right') {
Expand All @@ -53,76 +41,64 @@ export class ThumbnailNavigation extends Component {
}
switch (e.key) {
case nextKey:
this.nextCanvas();
nextCanvas();
break;
case previousKey:
this.previousCanvas();
previousCanvas();
break;
default:
break;
}
}
};

/**
* When on right, row height
* When on bottom, column width
*/
calculateScaledSize(index) {
const { thumbnailNavigation, canvasGroupings, position } = this.props;
const calculateScaledSize = (index) => {
const canvases = canvasGroupings[index];
if (!canvases) return thumbnailNavigation.width + this.spacing;
if (!canvases) return thumbnailNavigation.width + spacing;

const world = new CanvasWorld(canvases);
const bounds = world.worldBounds();
switch (position) {
case 'far-right': {
const calc = Math.floor(
this.calculatingWidth(canvases.length) * bounds[3] / bounds[2],
calculatingWidth(canvases.length) * bounds[3] / bounds[2],
);
if (!Number.isInteger(calc)) return thumbnailNavigation.width + this.spacing;
return calc + this.spacing;
if (!Number.isInteger(calc)) return thumbnailNavigation.width + spacing;
return calc + spacing;
}
// Default case bottom
default: {
if (bounds[3] === 0) return thumbnailNavigation.width + this.spacing;
if (bounds[3] === 0) return thumbnailNavigation.width + spacing;
const calc = Math.ceil(
(thumbnailNavigation.height - this.scrollbarSize - this.spacing - 4)
(thumbnailNavigation.height - scrollbarSize - spacing - 4)
* bounds[2] / bounds[3],
);
return calc;
}
}
}
};

/** */
calculatingWidth(canvasesLength) {
const { thumbnailNavigation } = this.props;
const calculatingWidth = (canvasesLength) => {
if (canvasesLength === 1) {
return thumbnailNavigation.width;
}
return thumbnailNavigation.width * 2;
}
};

/** */
rightWidth() {
const { view, thumbnailNavigation } = this.props;
switch (view) {
case 'book':
return (thumbnailNavigation.width * 2);
default:
return thumbnailNavigation.width;
}
}
const style = useCallback(() => {
const width = view === 'book' ? thumbnailNavigation.width * 2 : thumbnailNavigation.width;

/** */
style() {
const { position, thumbnailNavigation } = this.props;
switch (position) {
case 'far-right':
return {
height: '100%',
minHeight: 0,
width: `${this.rightWidth() + this.scrollbarSize + this.spacing}px`,
width: `${width + scrollbarSize + spacing}px`,
};
// Default case bottom
default:
Expand All @@ -131,109 +107,86 @@ export class ThumbnailNavigation extends Component {
width: '100%',
};
}
}
}, [position, thumbnailNavigation, view]);

/** */
areaHeight(height) {
const { position, thumbnailNavigation } = this.props;
const areaHeight = (height) => {
switch (position) {
case 'far-right':
return height;
// Default case bottom
default:
return thumbnailNavigation.height;
}
}
};

/** */
itemCount() {
const { canvasGroupings } = this.props;
return canvasGroupings.length;
}
const itemCount = () => canvasGroupings.length;

/**
*/
nextCanvas() {
const { hasNextCanvas, setNextCanvas } = this.props;
if (hasNextCanvas) {
setNextCanvas();
}
}
const nextCanvas = () => {
if (hasNextCanvas) setNextCanvas();
};

/**
*/
previousCanvas() {
const { hasPreviousCanvas, setPreviousCanvas } = this.props;
if (hasPreviousCanvas) {
setPreviousCanvas();
}
}
const previousCanvas = () => {
if (hasPreviousCanvas) setPreviousCanvas();
};

/**
* Renders things
*/
render() {
const {
t,
canvasGroupings,
position,
thumbnailNavigation,
viewingDirection,
windowId,
} = this.props;
if (position === 'off') {
return null;
}
const htmlDir = viewingDirection === 'right-to-left' ? 'rtl' : 'ltr';
const itemData = {
canvasGroupings,
height: thumbnailNavigation.height - this.spacing - this.scrollbarSize,
position,
windowId,
};
return (
<Paper
className={classNames(
ns('thumb-navigation'),
)}
sx={{
'&:focus': {
boxShadow: 0,
outline: 0,
},
}}
aria-label={t('thumbnailNavigation')}
square
elevation={0}
style={this.style()}
tabIndex={0}
onKeyDown={this.handleKeyDown}
role="grid"
>
<div role="row" style={{ height: '100%', width: '100%' }}>
<AutoSizer
defaultHeight={100}
defaultWidth={400}
>
{({ height, width }) => (
<List
direction={htmlDir}
height={this.areaHeight(height)}
itemCount={this.itemCount()}
itemSize={this.calculateScaledSize}
width={width}
layout={(position === 'far-bottom') ? 'horizontal' : 'vertical'}
itemData={itemData}
ref={this.gridRef}
>
{ThumbnailCanvasGrouping}
</List>
)}
</AutoSizer>
</div>
</Paper>
);
if (position === 'off') {
return null;
}
const htmlDir = viewingDirection === 'right-to-left' ? 'rtl' : 'ltr';
const itemData = {
canvasGroupings,
height: thumbnailNavigation.height - spacing - scrollbarSize,
position,
windowId,
};
return (
<Paper
className={classNames(
ns('thumb-navigation'),
)}
sx={{
'&:focus': {
boxShadow: 0,
outline: 0,
},
}}
aria-label={t('thumbnailNavigation')}
square
elevation={0}
style={style()}
tabIndex={0}
onKeyDown={handleKeyDown}
role="grid"
>
<div role="row" style={{ height: '100%', width: '100%' }}>
<AutoSizer
defaultHeight={100}
defaultWidth={400}
>
{({ height, width }) => (
<List
direction={htmlDir}
height={areaHeight(height)}
itemCount={itemCount()}
itemSize={calculateScaledSize}
width={width}
layout={(position === 'far-bottom') ? 'horizontal' : 'vertical'}
itemData={itemData}
ref={gridRef}
>
{ThumbnailCanvasGrouping}
</List>
)}
</AutoSizer>
</div>
</Paper>
);
}

ThumbnailNavigation.propTypes = {
Expand All @@ -250,12 +203,3 @@ ThumbnailNavigation.propTypes = {
viewingDirection: PropTypes.string,
windowId: PropTypes.string.isRequired,
};

ThumbnailNavigation.defaultProps = {
hasNextCanvas: false,
hasPreviousCanvas: false,
setNextCanvas: () => {},
setPreviousCanvas: () => {},
view: undefined,
viewingDirection: '',
};

0 comments on commit f1164fe

Please sign in to comment.