Skip to content

Commit

Permalink
#1872 add thumbnails to canvas navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
glefi committed Feb 20, 2019
1 parent 341ad20 commit c73c5a4
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 31 deletions.
31 changes: 19 additions & 12 deletions src/components/CanvasThumbnail.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,33 +34,40 @@ export default class CanvasThumbnail extends Component {
/**
*/
render() {
const { height } = this.props;
const {
height, isValid, onClick, style,
} = this.props;
const { loaded, image } = this.state;
const imgStyle = { height, width: '100%', ...style };
return (
<div>
<IntersectionObserver onChange={this.handleIntersection}>
<img
alt=""
src={loaded ? image.src : CanvasThumbnail.defaultImgPlaceholder}
height={height}
width="100%"
/>
</IntersectionObserver>
</div>
<IntersectionObserver onChange={this.handleIntersection}>
<img
alt=""
onClick={onClick}
onKeyPress={onClick}
role="presentation"
src={loaded && isValid ? image.src : CanvasThumbnail.defaultImgPlaceholder}
style={imgStyle}
/>
</IntersectionObserver>
);
}
}

// Transparent "gray"
CanvasThumbnail.defaultImgPlaceholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mMMDQmtBwADgwF/Op8FmAAAAABJRU5ErkJggg==';


CanvasThumbnail.propTypes = {
imageUrl: PropTypes.string,
isValid: PropTypes.bool,
height: PropTypes.number,
onClick: PropTypes.func.isRequired,
style: PropTypes.object, // eslint-disable-line react/forbid-prop-types,
};

CanvasThumbnail.defaultProps = {
imageUrl: null,
isValid: true,
height: 150,
style: {},
};
4 changes: 1 addition & 3 deletions src/components/ThumbnailNavigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,13 @@ class ThumbnailNavigation extends Component {
className={ns('thumbnail-nav-container')}
>
<div
onClick={() => setCanvas(window.id, canvas.index)}
onKeyPress={() => setCanvas(window.id, canvas.index)}
role="presentation"
style={{
width: style.width - 8,
}}
className={ns(['thumbnail-nav-canvas', `thumbnail-nav-canvas-${canvas.index}`, this.currentCanvasClass(canvas.index)])}
>
<CanvasThumbnail
onClick={() => setCanvas(window.id, canvas.index)}
imageUrl={new ManifestoCanvas(canvas).thumbnail(config.thumbnailNavigation.height)}
height={config.thumbnailNavigation.height}
/>
Expand Down
35 changes: 35 additions & 0 deletions src/components/ValidationCanvas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import ManifestoCanvas from '../lib/ManifestoCanvas';

/**
*/
export default class ValidationCanvas extends ManifestoCanvas {
/**
* checks whether the canvas has a valid height
*/
get hasValidHeight() {
return (
typeof this.canvas.getHeight() === 'number'
&& this.canvas.getHeight() > 0
);
}

/**
* checks whether the canvas has a valid height
*/
get hasValidWidth() {
return (
typeof this.canvas.getHeight() === 'number'
&& this.canvas.getHeight() > 0
);
}

/**
* checks whether the canvas has valid dimensions
*/
get hasValidDimensions() {
return (
this.hasValidHeight
&& this.hasValidWidth
);
}
}
63 changes: 49 additions & 14 deletions src/components/WindowSideBarCanvasPanel.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,68 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Typography from '@material-ui/core/Typography';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import { withStyles } from '@material-ui/core/styles';
import ValidationCanvas from './ValidationCanvas';
import CanvasThumbnail from './CanvasThumbnail';
import { getIdAndLabelOfCanvases } from '../state/selectors';

/**
* a panel showing the canvases for a given manifest
*/
class WindowSideBarCanvasPanel extends Component {
/**
* calculateScaledWidth - calculates the scaled width according to the given width and aspectRatio
*/
static calculateScaledWidth(height, aspectRatio) {
return Math.floor(height * aspectRatio);
}

/**
* render
*/
render() {
const {
canvasesIdAndLabel, setCanvas, windowId, classes, t,
canvases, classes, config, setCanvas, t, windowId,
} = this.props;

const canvasesIdAndLabel = getIdAndLabelOfCanvases(canvases);

return (
<>
<Typography variant="h2" className={classes.windowSideBarH2}>{t('canvasIndex')}</Typography>
<List>
{
canvasesIdAndLabel.map((canvas, canvasIndex) => (
<ListItem key={canvas.id}>
<Typography
className={classes.clickable}
variant="body2"
onClick={() => { setCanvas(windowId, canvasIndex); }}
canvasesIdAndLabel.map((canvas, canvasIndex) => {
const validationCanvas = new ValidationCanvas(canvases[canvasIndex]);
const isValid = validationCanvas.hasValidDimensions;
return (
<ListItem
key={canvas.id}
>
{canvas.label}
</Typography>
</ListItem>
))
<CanvasThumbnail
className={classNames(classes.clickable)}
isValid={isValid}
imageUrl={validationCanvas.thumbnail(config.canvasNavigation.height)}
onClick={() => { setCanvas(windowId, canvasIndex); }}
style={{
cursor: 'pointer',
height: config.canvasNavigation.height,
width: isValid ? WindowSideBarCanvasPanel.calculateScaledWidth(config.canvasNavigation.height, validationCanvas.aspectRatio) : 'auto',
}}
/>
<Typography
className={classNames(classes.clickable, classes.label)}
onClick={() => { setCanvas(windowId, canvasIndex); }}
variant="body2"
>
{canvas.label}
</Typography>
</ListItem>
);
})
}
</List>
</>
Expand All @@ -41,11 +71,12 @@ class WindowSideBarCanvasPanel extends Component {
}

WindowSideBarCanvasPanel.propTypes = {
windowId: PropTypes.string.isRequired,
canvasesIdAndLabel: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
setCanvas: PropTypes.func.isRequired,
canvases: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
config: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
setCanvas: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
windowId: PropTypes.string.isRequired,
};

/**
Expand All @@ -57,6 +88,10 @@ const styles = theme => ({
clickable: {
cursor: 'pointer',
},
label: {
fontSize: '8pt',
paddingLeft: 8,
},
});

export default withStyles(styles)(WindowSideBarCanvasPanel);
3 changes: 3 additions & 0 deletions src/config/settings.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export default {
canvasNavigation: {
height: 100,
},
theme: { // Sets up a MaterialUI theme. See https://material-ui.com/customization/default-theme/
palette: {
type: 'light', // dark also available
Expand Down
5 changes: 3 additions & 2 deletions src/containers/WindowSideBarCanvasPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import WindowSideBarCanvasPanel from '../components/WindowSideBarCanvasPanel';
import {
getManifestCanvases,
getWindowManifest,
getIdAndLabelOfCanvases,
} from '../state/selectors';

/**
Expand All @@ -15,8 +14,10 @@ import {
const mapStateToProps = (state, { windowId }) => {
const manifest = getWindowManifest(state, windowId);
const canvases = getManifestCanvases(manifest);
const { config } = state;
return {
canvasesIdAndLabel: getIdAndLabelOfCanvases(canvases),
canvases,
config,
};
};

Expand Down

0 comments on commit c73c5a4

Please sign in to comment.