Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: convert MiradorImageTools to functional component to fix no image bug #52

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions __tests__/MiradorImageTools.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function createWrapper(props) {
windowId="x"
width="sm"
theme={mockPalette}
size={{ width: 100, height: 200 }}
t={(k) => k}
{...props}
/>,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
},
"peerDependencies": {
"@mui/material": "^5.x",
"mirador": "^4.0.0-alpha.1",
"mirador": "^4.0.0-alpha.4",
"react": "18.x",
"react-dom": "18.x"
},
Expand Down Expand Up @@ -70,7 +70,7 @@
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.4.3",
"jest-puppeteer": "^9.0.2",
"mirador": "^4.0.0-alpha.1",
"mirador": "^4.0.0-alpha.4",
"puppeteer": "^21.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
Expand Down
4 changes: 1 addition & 3 deletions setupJest.js
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
import '@testing-library/jest-dom'
import sizeMe from 'react-sizeme';
sizeMe.noPlaceholders = true;
import '@testing-library/jest-dom'
202 changes: 91 additions & 111 deletions src/plugins/MiradorImageTools.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { Component } from 'react';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import compose from 'lodash/flowRight';
import { withSize } from 'react-sizeme';
import { withSize } from 'mirador/dist/es/src/extend/withSize';
import { withRef } from 'mirador/dist/es/src/extend/withRef';
import BrightnessIcon from '@mui/icons-material/Brightness5';
import TonalityIcon from '@mui/icons-material/Tonality';
import GradientIcon from '@mui/icons-material/Gradient';
Expand All @@ -26,9 +27,9 @@ const ToggleContainer = styled('div')(() => ({
}));

const ToolContainer = styled('div')(() => ({
display: 'flex',
border: 0,
borderImageSlice: 1,
display: 'flex',
}));

/** Styles for withStyles HOC */
Expand Down Expand Up @@ -74,34 +75,35 @@ const Root = styled('div')(({ small, theme: { palette } }) => {
};
});

class MiradorImageTools extends Component {
constructor(props) {
super(props);
this.toggleState = this.toggleState.bind(this);
this.toggleRotate = this.toggleRotate.bind(this);
this.toggleFlip = this.toggleFlip.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleReset = this.handleReset.bind(this);
}

componentDidMount() {
const { viewer } = this.props;
if (viewer) this.applyFilters();
}
const MiradorImageTools = (({
enabled,
innerRef,
open,
size,
t,
updateViewport,
updateWindow,
viewer,
viewConfig,
windowId,
}) => {
const [isSmallDisplay, setIsSmallDisplay] = useState(false);

componentDidUpdate(prevProps) {
const { viewConfig, viewer } = this.props;
if (viewer && viewConfig !== prevProps.viewConfig) this.applyFilters();
}
const {
flip = false,
brightness = 100,
contrast = 100,
saturate = 100,
grayscale = 0,
invert = 0,
} = viewConfig;

handleChange(param) {
const { updateViewport, windowId } = this.props;
return (value) => updateViewport(windowId, { [param]: value });
}
const handleChange = (param) => (value) => {
updateViewport(windowId, { [param]: value });
};

handleReset() {
const { updateViewport, windowId } = this.props;
const viewConfig = {
const handleReset = () => {
const viewConfigReset = {
rotation: 0,
flip: false,
brightness: 100,
Expand All @@ -110,22 +112,11 @@ class MiradorImageTools extends Component {
grayscale: 0,
invert: 0,
};
updateViewport(windowId, viewConfigReset);
};

updateViewport(windowId, viewConfig);
}

applyFilters() {
const {
viewConfig: {
brightness = 100,
contrast = 100,
saturate = 100,
grayscale = 0,
invert = 0,
},
viewer: { canvas },
} = this.props;

const applyFilters = () => {
const { canvas } = viewer || {};
if (!canvas) return;

const controlledFilters = ['brightness', 'contrast', 'saturate', 'grayscale', 'invert'];
Expand All @@ -140,80 +131,64 @@ class MiradorImageTools extends Component {
newFilters.push(`grayscale(${grayscale}%)`);
newFilters.push(`invert(${invert}%)`);
canvas.style.filter = newFilters.join(' ');
}

toggleState() {
const { open, updateWindow, windowId } = this.props;
};

const toggleState = () => {
updateWindow(windowId, { imageToolsOpen: !open });
}

toggleRotate(value) {
const { updateViewport, viewConfig: { flip = false, rotation = 0 }, windowId } = this.props;
};

const toggleRotate = (value) => {
const offset = flip ? -1 * value : value;
updateViewport(windowId, { rotation: (viewConfig.rotation + offset) % 360 });
};

updateViewport(windowId, { rotation: (rotation + offset) % 360 });
}

toggleFlip() {
const { updateViewport, viewConfig: { flip = false }, windowId } = this.props;

const toggleFlip = () => {
updateViewport(windowId, { flip: !flip });
}
};

render() {
const {
enabled, open, viewer, windowId,
viewConfig: {
flip = false,
brightness = 100,
contrast = 100,
saturate = 100,
grayscale = 0,
invert = 0,
}, size: { width },
t,
} = this.props;
useEffect(() => {
setIsSmallDisplay(size.width && size.width < 480);
}, [size.width]);

if (!viewer || !enabled) return null;
useEffect(() => {
if (viewer) applyFilters();
}, [viewer, viewConfig]);

const isSmallDisplay = width && (width < 480);
if (!viewer || !enabled) return <SizeContainer ref={innerRef} />;

/** Button for toggling the menu */
const toggleButton = (
<ToggleContainer>
<MiradorMenuButton
aria-expanded={open}
aria-haspopup
aria-label={t('collapse', { context: open ? 'open' : 'close' })}
onClick={this.toggleState}
>
{ open ? <CloseSharpIcon /> : <TuneSharpIcon /> }
</MiradorMenuButton>
</ToggleContainer>
);
return (
<SizeContainer>
<Root className="MuiPaper-elevation4" small={isSmallDisplay}>
{isSmallDisplay && toggleButton}
{open
&& (
const toggleButton = (
<ToggleContainer>
<MiradorMenuButton
aria-expanded={open}
aria-haspopup
aria-label={t('collapse', { context: open ? 'open' : 'close' })}
onClick={toggleState}
>
{open ? <CloseSharpIcon /> : <TuneSharpIcon />}
</MiradorMenuButton>
</ToggleContainer>
);

return (
<SizeContainer ref={innerRef}>
<Root className="MuiPaper-elevation4" small={isSmallDisplay}>
{isSmallDisplay && toggleButton}
{open && (
<React.Fragment>
<ToolContainer>
<ImageRotation
label={t('rotateRight')}
onClick={() => this.toggleRotate(90)}
onClick={() => toggleRotate(90)}
variant="right"
/>
<ImageRotation
label={t('rotateLeft')}
onClick={() => this.toggleRotate(-90)}
onClick={() => toggleRotate(-90)}
variant="left"
/>
<ImageFlip
label={t('flip')}
onClick={this.toggleFlip}
onClick={toggleFlip}
flipped={flip}
/>
</ToolContainer>
Expand All @@ -224,7 +199,7 @@ class MiradorImageTools extends Component {
max={200}
windowId={windowId}
value={brightness}
onChange={this.handleChange('brightness')}
onChange={handleChange('brightness')}
small={isSmallDisplay}
>
<BrightnessIcon />
Expand All @@ -235,7 +210,7 @@ class MiradorImageTools extends Component {
max={200}
windowId={windowId}
value={contrast}
onChange={this.handleChange('contrast')}
onChange={handleChange('contrast')}
small={isSmallDisplay}
>
<ContrastIcon style={{ transform: 'rotate(180deg)' }} />
Expand All @@ -246,7 +221,7 @@ class MiradorImageTools extends Component {
max={200}
windowId={windowId}
value={saturate}
onChange={this.handleChange('saturate')}
onChange={handleChange('saturate')}
small={isSmallDisplay}
>
<GradientIcon />
Expand All @@ -257,7 +232,7 @@ class MiradorImageTools extends Component {
label={t('greyscale')}
windowId={windowId}
value={grayscale}
onChange={this.handleChange('grayscale')}
onChange={handleChange('grayscale')}
small={isSmallDisplay}
>
<TonalityIcon />
Expand All @@ -268,7 +243,7 @@ class MiradorImageTools extends Component {
label={t('invert')}
windowId={windowId}
value={invert}
onChange={this.handleChange('invert')}
onChange={handleChange('invert')}
small={isSmallDisplay}
>
<InvertColorsIcon />
Expand All @@ -277,24 +252,30 @@ class MiradorImageTools extends Component {
<ToolContainer>
<MiradorMenuButton
aria-label={t('revert')}
onClick={this.handleReset}
onClick={handleReset}
>
<ReplaySharpIcon />
</MiradorMenuButton>
</ToolContainer>
</React.Fragment>
)}
{!isSmallDisplay && toggleButton}
</Root>
</SizeContainer>
);
}
}
)}
{!isSmallDisplay && toggleButton}
</Root>
</SizeContainer>
);
});

MiradorImageTools.propTypes = {
enabled: PropTypes.bool,
innerRef: PropTypes.oneOfType([
PropTypes.func,
PropTypes.any,
]).isRequired,
open: PropTypes.bool,
size: PropTypes.object, // eslint-disable-line react/forbid-prop-types
size: PropTypes.shape({
width: PropTypes.number,
height: PropTypes.number,
}).isRequired,
t: PropTypes.func.isRequired,
updateViewport: PropTypes.func.isRequired,
updateWindow: PropTypes.func.isRequired,
Expand All @@ -306,12 +287,11 @@ MiradorImageTools.propTypes = {
MiradorImageTools.defaultProps = {
enabled: true,
open: true,
size: {},
viewer: undefined,
viewConfig: {},
};

// Export without wrapping HOC for testing.
export const TestableImageTools = MiradorImageTools;

export default compose(withSize())(MiradorImageTools);
export default compose(withSize(), withRef())(MiradorImageTools);