Skip to content

Commit

Permalink
Merge pull request #152 from m2ms/feature/#211-ResizableReport
Browse files Browse the repository at this point in the history
resizable reporting
  • Loading branch information
reskyner authored Apr 1, 2020
2 parents e0fe42b + c9c5211 commit 9698f26
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 43 deletions.
53 changes: 32 additions & 21 deletions js/components/common/Modal/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CircularProgress, makeStyles, Modal as MaterialModal } from '@material-ui/core';
import React, { memo } from 'react';
import classNames from 'classnames';
import useResizeObserver from '../../../utils/useResizeObserver';

const useStyles = makeStyles(theme => ({
paper: {
Expand All @@ -18,30 +19,40 @@ const useStyles = makeStyles(theme => ({
},
resizable: {
resize: 'both',
overflow: 'auto'
overflow: 'hidden'
}
}));

export const Modal = memo(({ children, open, loading, onClose, noPadding, resizable, ...rest }) => {
const classes = useStyles();
const content = loading ? <CircularProgress /> : children;
return (
<MaterialModal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
open={open}
onClose={onClose}
{...rest}
>
<div
className={classNames(classes.paper, {
[classes.resizable]: resizable
})}
export const Modal = memo(
({ children, open, loading, onClose, noPadding, resizable, onResize, otherClasses, ...rest }) => {
const classes = useStyles();
const content = loading ? <CircularProgress /> : children;

const [containerDiv] = useResizeObserver(onResize);

return (
<MaterialModal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
open={open}
onClose={onClose}
{...rest}
>
<div className={noPadding ? undefined : classes.withPadding}>{content}</div>
</div>
</MaterialModal>
);
});
<div
ref={containerDiv}
className={classNames(
classes.paper,
{
[classes.resizable]: resizable
},
{ [otherClasses]: !!otherClasses }
)}
>
<div className={noPadding ? undefined : classes.withPadding}>{content}</div>
</div>
</MaterialModal>
);
}
);

export default Modal;
98 changes: 78 additions & 20 deletions js/components/userFeedback/reportForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import { snackbarColors } from '../header/constants';
import CanvasDraw from 'react-canvas-draw';
import { SketchPicker } from 'react-color';

/* Min resolution is 960 x 540 */
const FORM_MIN_WIDTH = 960;
const FORM_MIN_HEIGHT = 540;
const CANVAS_MAX_WIDTH = 605;
const CANVAS_MAX_HEIGHT = 400;

Expand Down Expand Up @@ -64,13 +67,21 @@ const useStyles = makeStyles(theme => ({
marginBottom: '2px'
},
formMinWidth: {
minWidth: '900px'
// 60 is padding (64 actually)
minWidth: FORM_MIN_WIDTH - 60 + 'px'
},
formModal: {
minWidth: FORM_MIN_WIDTH + 'px',
minHeight: FORM_MIN_HEIGHT + 'px'
},
canvasDrawWrapper: {
overflow: 'auto',
maxWidth: CANVAS_MAX_WIDTH + 'px',
maxHeight: CANVAS_MAX_HEIGHT + 'px',
width: CANVAS_MAX_WIDTH + 'px',
height: CANVAS_MAX_HEIGHT + 'px',
position: 'absolute'
},
canvasNoImage: {
paddingTop: '200px'
}
}));

Expand Down Expand Up @@ -164,12 +175,48 @@ export const ReportForm = memo(({ formType }) => {
/* Modal handlers */
const [openForm, setOpenForm] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
const [modalWidth, setModalWidth] = useState(0);
const [modalHeight, setModalHeight] = useState(0);
const canvasWrapperGridItem = useRef();
const canvasWrapper = useRef();
const canvasDraw = useRef();
const colorPicker = useRef();
const [wrapperWidth, setWrapperWidth] = useState(CANVAS_MAX_WIDTH);
const [wrapperHeight, setWrapperHeight] = useState(CANVAS_MAX_HEIGHT);
const [openBrushColor, setOpenBrushColor] = useState(false);
const [brushRadius, setBrushRadius] = useState(2);
const [brushColor, setBrushColor] = useState('#444');

const modalOnResize = (entry, node) => {
// initialize modal width and height
if (node && !modalWidth && !modalHeight) {
setModalWidth(node.offsetWidth);
setModalHeight(node.offsetHeight);
}
if (canvasWrapper && canvasWrapper.current) {
const changedWidth = entry.target.offsetWidth;
if (modalWidth && modalWidth < changedWidth) {
// set new width from its flex div container
let newWidth = canvasWrapperGridItem.current.offsetWidth;
if (newWidth > formState.imageSource.width) {
// 17 is for scroll
newWidth = formState.imageSource.width + 17;
}
canvasWrapper.current.style.width = newWidth + 'px';
}
const changedHeight = entry.target.offsetHeight;
if (modalHeight && modalHeight < changedHeight) {
// compute new height
let newHeight = wrapperHeight + changedHeight - modalHeight;
if (newHeight > formState.imageSource.height) {
newHeight = formState.imageSource.height;
}
canvasWrapperGridItem.current.style.flexBasis = newHeight + 'px';
canvasWrapper.current.style.height = canvasWrapperGridItem.current.offsetHeight + 'px';
}
}
};

const handleColorChange = color => {
setBrushColor(color.hex);
};
Expand Down Expand Up @@ -279,7 +326,7 @@ export const ReportForm = memo(({ formType }) => {
</Grid>
</Grid>
</Modal>
<Modal open={openForm}>
<Modal open={openForm} otherClasses={[classes.formModal]} resizable={true} onResize={modalOnResize}>
<Formik
initialValues={{
name: formState.name,
Expand Down Expand Up @@ -372,7 +419,8 @@ export const ReportForm = memo(({ formType }) => {
</Grid>

<Grid item xs={8}>
<Grid item container justify="flex-end" direction="column">
<Grid item container direction="column">
{/* Canvas options */}
<Grid
item
xs
Expand Down Expand Up @@ -482,21 +530,31 @@ export const ReportForm = memo(({ formType }) => {
</Grid>
</Grid>
</Grid>
<Grid item xs>
<div className={classes.canvasDrawWrapper}>
{/* lazyRadius - how far is cursor from drawing point */}
<CanvasDraw
ref={canvasDraw}
imgSrc={formState.imageSource ? formState.imageSource.toDataURL() : ''}
canvasWidth={formState.imageSource ? formState.imageSource.width : CANVAS_MAX_WIDTH}
canvasHeight={formState.imageSource ? formState.imageSource.height : CANVAS_MAX_HEIGHT}
hideGrid={true}
lazyRadius={0}
brushRadius={brushRadius}
brushColor={brushColor}
disabled={isSubmitting}
/>
</div>
{/* Canvas */}
<Grid ref={canvasWrapperGridItem} item xs align="center">
{formState.imageSource ? (
<div
ref={canvasWrapper}
width={wrapperWidth}
height={wrapperHeight}
className={classes.canvasDrawWrapper}
>
{/* lazyRadius - how far is cursor from drawing point */}
<CanvasDraw
ref={canvasDraw}
imgSrc={formState.imageSource.toDataURL()}
canvasWidth={formState.imageSource.width}
canvasHeight={formState.imageSource.height}
hideGrid={true}
lazyRadius={0}
brushRadius={brushRadius}
brushColor={brushColor}
disabled={isSubmitting}
/>
</div>
) : (
<Typography className={classes.canvasNoImage}>No image source.</Typography>
)}
</Grid>
</Grid>
</Grid>
Expand Down
30 changes: 30 additions & 0 deletions js/utils/useResizeObserver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useRef, useLayoutEffect, useState, useCallback } from 'react';
import ResizeObserver from 'resize-observer-polyfill';

// https://tobbelindstrom.com/blog/resize-observer-hook/

const useResizeObserver = observerCallback => {
const [node, setNode] = useState(null);
const observer = useRef(null);

const disconnect = useCallback(() => {
const { current } = observer;
current && current.disconnect();
}, []);

const observe = useCallback(() => {
observer.current = new ResizeObserver(([entry]) =>
observerCallback !== undefined ? observerCallback(entry, node) : null
);
node && observer.current.observe(node);
}, [node, observerCallback]);

useLayoutEffect(() => {
observe();
return () => disconnect();
}, [disconnect, observe]);

return [setNode];
};

export default useResizeObserver;
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fragalysis-frontend",
"version": "0.5.0",
"version": "0.5.1",
"description": "Frontend for fragalysis",
"main": "webpack.config.js",
"scripts": {
Expand Down Expand Up @@ -110,6 +110,7 @@
"lint-staged": "^9.5.0",
"prettier": "^1.19.1",
"redux-mock-store": "^1.5.4",
"resize-observer-polyfill": "^1.5.1",
"terser-webpack-plugin": "^2.2.1",
"typescript": "^3.7.2",
"webpack": "^4.41.5",
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7935,7 +7935,7 @@ reselect@^4.0.0:
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==

resize-observer-polyfill@^1.5.0:
resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
Expand Down

0 comments on commit 9698f26

Please sign in to comment.