docbrown/app/javascript/chat/draw/index.jsx
Sarthak Sharma 83b3baeac3
[deploy] 🚀 Feature: Draw Command for connect with image annotations (#10136)
* 🚀 Draw Command ready for connect [Rebase]

* revert schema changes

* fix & replace Button with theme button

* fix UI

* add test cases

* fix travis issue

* on image upload enable send button

* revert schema.db changes

* revert UI changes

* fix suggestions

* Update app/javascript/chat/draw/index.jsx

Co-authored-by: Nick Taylor <nick@iamdeveloper.com>

* update test cases

* remove commented code

* Trigger Travis CI

Co-authored-by: Narender Singh <narender2031@gmail.com>
Co-authored-by: Nick Taylor <nick@iamdeveloper.com>
2020-10-07 16:06:22 +02:00

186 lines
4.8 KiB
JavaScript

import { h } from 'preact';
import { useRef, useEffect, useState } from 'preact/hooks';
import PropTypes from 'prop-types';
import { chatDrawPalettes } from '../../utilities/Constants';
import { Button } from '@crayons';
/**
* Draw function is wrapped in this component
*
* @example
* <Draw
* sendCanvasImage={sendCanvasImage}
* />
*
*
* @param {object} props
* @param {function} props.sendCanvasImage
*/
function Draw({ sendCanvasImage }) {
const canvasRef = useRef(null);
const [isDrawing, setIsDrawing] = useState(false);
const [drawColor, setDrawColor] = useState('#F58F8E');
const [coordinates, setCoordinates] = useState({});
const [sendButtonDisabled, setSendButtonDisabled] = useState(true);
const prevCoordinates = usePrevious(coordinates);
const handleCanvasSend = () => {
const canvas = canvasRef.current;
canvas.toBlob((blob) => {
sendCanvasImage(new File([blob], 'draw.png'));
});
};
const handleMouseDown = (e) => {
setCoordinates({ x: e.offsetX, y: e.offsetY });
setIsDrawing(true);
};
const handleImageDrop = (e) => {
e.preventDefault();
if (!canvasRef.current) {
return;
}
const canvas = canvasRef.current;
const context = canvas.getContext('2d');
canvas.classList.remove('opacity-25');
const { files } = e.dataTransfer;
const img = new Image();
img.src = URL.createObjectURL(files[0]);
img.onload = () => {
const scale = Math.min(
canvas.width / img.width,
canvas.height / img.height,
);
setSendButtonDisabled(false);
const x = canvas.width / 2 - (img.width / 2) * scale;
const y = canvas.height / 2 - (img.height / 2) * scale;
context.drawImage(img, x, y, img.width * scale, img.height * scale);
};
};
const handleClearCanvas = () => {
setSendButtonDisabled(true);
if (!canvasRef.current) {
return;
}
const context = canvasRef.current.getContext('2d');
context.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
};
const handleDragHover = (e) => {
e.preventDefault();
canvasRef.current.classList.add('opacity-25');
};
const handleChangeColor = (e) => {
setDrawColor(e.target.style.backgroundColor);
};
const handleDragExit = (e) => {
e.preventDefault();
canvasRef.current.classList.remove('opacity-25');
canvasRef.current.classList.add('opacity-100');
};
const handleMouseMove = (e) => {
if (isDrawing) {
setSendButtonDisabled(false);
setCoordinates({ x: e.offsetX, y: e.offsetY });
}
};
const handleMouseUp = () => {
setCoordinates({});
setIsDrawing(false);
};
useEffect(() => {
if (!canvasRef.current) {
return;
}
const context = canvasRef.current.getContext('2d');
if (isDrawing) {
context.beginPath();
context.strokeStyle = drawColor;
context.lineWidth = 2;
context.moveTo(prevCoordinates.x, prevCoordinates.y);
context.lineTo(coordinates.x, coordinates.y);
context.stroke();
context.closePath();
}
}, [drawColor, isDrawing, coordinates, prevCoordinates]);
return (
<div className="p-4 grid gap-2 crayons-card mb-4 connect-draw">
<div className="mb-1 draw-title">
<h2>Connect Draw</h2>
<div className="connect-draw__colors">
{chatDrawPalettes.map((color) => (
<button
className="connect-draw__color"
onClick={handleChangeColor}
style={`background-color: ${color}`}
title={`Change color to ${color}`}
key={color}
/>
))}
</div>
</div>
<div
aria-hidden
className="connect-draw__draw-area"
onMouseUp={handleMouseUp}
>
<canvas
className="connect-draw__draw"
ref={canvasRef}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onDrop={handleImageDrop}
onDragOver={handleDragHover}
onDragExit={handleDragExit}
height="600"
width="400"
/>
<div className="connect-draw__actions">
<Button
onClick={handleClearCanvas}
title="Clear the canvas."
type="button"
size="s"
variant="secondary"
>
Clear
</Button>
<Button
onClick={handleCanvasSend}
title="Send the canvas."
disabled={sendButtonDisabled}
>
Send
</Button>
</div>
</div>
</div>
);
}
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
Draw.propTypes = {
sendCanvasImage: PropTypes.func.isRequired,
};
export default Draw;