From 226348e4ab12ba6d77eb28c9da4ae4b38e7935ea Mon Sep 17 00:00:00 2001 From: Lincoln Lee Date: Wed, 26 Jul 2023 12:22:17 -0700 Subject: [PATCH 1/7] feat: minimium mvp, needs optimization --- package.json | 2 + src/assets/icons/colorpicker.svg | 7 ++ src/assets/icons/trashcan.svg | 1 + src/components/ApplicationForm/Skills.js | 18 +++ src/components/Input/SketchCanvas.js | 133 +++++++++++++++++++++++ yarn.lock | 55 +++++++++- 6 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/assets/icons/colorpicker.svg create mode 100644 src/assets/icons/trashcan.svg create mode 100644 src/components/Input/SketchCanvas.js diff --git a/package.json b/package.json index ebb301b1..ec18eca7 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,13 @@ "husky": "^4.3.0", "next-qrcode": "^2.4.0", "react": "^16.13.1", + "react-color": "2.17.2", "react-csv": "^2.2.1", "react-dom": "^16.13.1", "react-markdown": "^4.3.1", "react-scripts": "3.4.1", "react-select": "^3.1.0", + "react-sketch-canvas": "^6.2.0", "react-spinner": "^0.2.7", "react-spinners": "^0.9.0", "styled-components": "^5.1.1", diff --git a/src/assets/icons/colorpicker.svg b/src/assets/icons/colorpicker.svg new file mode 100644 index 00000000..ea09b4c8 --- /dev/null +++ b/src/assets/icons/colorpicker.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/icons/trashcan.svg b/src/assets/icons/trashcan.svg new file mode 100644 index 00000000..1c5dceab --- /dev/null +++ b/src/assets/icons/trashcan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ApplicationForm/Skills.js b/src/components/ApplicationForm/Skills.js index 9da4d9c7..3409bbd6 100644 --- a/src/components/ApplicationForm/Skills.js +++ b/src/components/ApplicationForm/Skills.js @@ -6,6 +6,7 @@ import { Select } from '../Input' import { FormSpacing, SubHeading } from './' import { CONTRIBUTION_ROLE_OPTIONS, copyText } from '../../utility/Constants' import styled from 'styled-components' +import SketchCanvas from '../Input/SketchCanvas' const QuestionForm = styled.form` display: flex; @@ -275,6 +276,23 @@ export default ({ refs, errors, formInputs, onChange, role, handleResume }) => { customRef={refs['longAnswers2Ref']} /> + + question 21 + Draw your favourite character! + + onChange({ + longAnswers3: val, + }) + } + customRef={refs['longAnswers3Ref']} + /> + ) diff --git a/src/components/Input/SketchCanvas.js b/src/components/Input/SketchCanvas.js new file mode 100644 index 00000000..1475aa15 --- /dev/null +++ b/src/components/Input/SketchCanvas.js @@ -0,0 +1,133 @@ +import React, { useRef, useState } from 'react' +import { ReactSketchCanvas } from 'react-sketch-canvas' +import { SketchPicker } from 'react-color' +import trashcanIcon from '../../assets/icons/trashcan.svg' +import colorpickerIcon from '../../assets/icons/colorpicker.svg' + +export default ({ width, height, value, invalid, errorMsg, onChange, customRef }) => { + const [strokeWidth, setStrokeWidth] = useState(4) + const [strokeColor, setStrokeColor] = useState('black') + const [showPicker, setShowPicker] = useState(false) + const canvasRef = useRef() + + const handleChange = async () => { + const newSVG = await canvasRef.current.exportSvg() + onChange(newSVG) + } + + const handleClear = () => { + if (canvasRef.current) canvasRef.current.clearCanvas() + } + + return ( +
+ + +
+
+ Color Picker Icon { + setShowPicker(!showPicker) + }} + /> +
{ + setStrokeWidth(4) + }} + /> +
{ + setStrokeWidth(6) + }} + /> +
{ + setStrokeWidth(8) + }} + /> +
+ Trashcan Icon + + {showPicker && ( +
+ { + setStrokeColor(e.hex) + }} + /> +
+ )} +
+
+ ) +} diff --git a/yarn.lock b/yarn.lock index 0db73742..44fb5282 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1303,6 +1303,11 @@ dependencies: "@hapi/hoek" "^8.3.0" +"@icons/material@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8" + integrity sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw== + "@jest/console@^24.7.1", "@jest/console@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" @@ -6406,6 +6411,11 @@ lodash.uniq@^4.5.0: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" +lodash@^4.0.1: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + lodash@^4.17.19: version "4.17.19" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" @@ -6479,6 +6489,11 @@ markdown-escapes@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" +material-colors@^1.2.1: + version "1.2.6" + resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46" + integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -8133,6 +8148,15 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.4" +prop-types@^15.5.10: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -8307,6 +8331,18 @@ react-app-polyfill@^1.0.6: regenerator-runtime "^0.13.3" whatwg-fetch "^3.0.0" +react-color@2.17.2: + version "2.17.2" + resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.17.2.tgz#ef002cafd5ae8daf3d374b85bb44ac9d36785c0a" + integrity sha512-TpT/D3wEQmyCOcIXrUgKOvEXpJtMylEx5yzw0laGP30mfC5yO4CoUsiYvfPiUbbkFjMGurPJjkKOkkOfBBIjFQ== + dependencies: + "@icons/material" "^0.2.4" + lodash "^4.17.11" + material-colors "^1.2.1" + prop-types "^15.5.10" + reactcss "^1.2.0" + tinycolor2 "^1.4.1" + react-csv@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/react-csv/-/react-csv-2.2.1.tgz#2ec723dd3ce8069269bffb3a95d968302fc87740" @@ -8361,7 +8397,7 @@ react-input-autosize@^2.2.2: dependencies: prop-types "^15.5.8" -react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: +react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -8451,6 +8487,11 @@ react-select@^3.1.0: react-input-autosize "^2.2.2" react-transition-group "^4.3.0" +react-sketch-canvas@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/react-sketch-canvas/-/react-sketch-canvas-6.2.0.tgz#4aa1f0b85012f37b8b2ae746a412ab9424badfa9" + integrity sha512-Z56RQN0viV9W3Y+DpZfQMi2lf3pO+UB4l+cMwnx7j7pQ9WHkQwN+qZKVFGANgpjXQMyY42NODF/DXjuKPCMf1w== + react-spinner@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/react-spinner/-/react-spinner-0.2.7.tgz#ea3ca3375dd7a54edbb5cc01d17496a2e2fc14db" @@ -8481,6 +8522,13 @@ react@^16.13.1: object-assign "^4.1.1" prop-types "^15.6.2" +reactcss@^1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd" + integrity sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A== + dependencies: + lodash "^4.0.1" + read-pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" @@ -9694,6 +9742,11 @@ timsort@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" +tinycolor2@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e" + integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" From 515b5132a9ec04d9949577ecb4debce699743d29 Mon Sep 17 00:00:00 2001 From: Lincoln Lee Date: Wed, 26 Jul 2023 15:04:02 -0700 Subject: [PATCH 2/7] feat: add undo/redo, debounced onChange, error handling, ref merging --- src/assets/icons/redo.svg | 4 + src/assets/icons/undo.svg | 4 + src/components/ApplicationForm/Skills.js | 10 +- src/components/Input/SketchCanvas.js | 122 +++++++++++++++++++++-- src/utility/utilities.js | 8 ++ 5 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 src/assets/icons/redo.svg create mode 100644 src/assets/icons/undo.svg diff --git a/src/assets/icons/redo.svg b/src/assets/icons/redo.svg new file mode 100644 index 00000000..d199a660 --- /dev/null +++ b/src/assets/icons/redo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/icons/undo.svg b/src/assets/icons/undo.svg new file mode 100644 index 00000000..d711d493 --- /dev/null +++ b/src/assets/icons/undo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/components/ApplicationForm/Skills.js b/src/components/ApplicationForm/Skills.js index 3409bbd6..17660462 100644 --- a/src/components/ApplicationForm/Skills.js +++ b/src/components/ApplicationForm/Skills.js @@ -280,11 +280,11 @@ export default ({ refs, errors, formInputs, onChange, role, handleResume }) => { question 21 Draw your favourite character! onChange({ longAnswers3: val, diff --git a/src/components/Input/SketchCanvas.js b/src/components/Input/SketchCanvas.js index 1475aa15..74169bb3 100644 --- a/src/components/Input/SketchCanvas.js +++ b/src/components/Input/SketchCanvas.js @@ -1,24 +1,54 @@ -import React, { useRef, useState } from 'react' +/* eslint-disable import/no-anonymous-default-export */ +import React, { useEffect, useRef, useState } from 'react' import { ReactSketchCanvas } from 'react-sketch-canvas' import { SketchPicker } from 'react-color' import trashcanIcon from '../../assets/icons/trashcan.svg' import colorpickerIcon from '../../assets/icons/colorpicker.svg' +import undoIcon from '../../assets/icons/undo.svg' +import redoIcon from '../../assets/icons/redo.svg' +import { mergeRefs } from '../../utility/utilities' -export default ({ width, height, value, invalid, errorMsg, onChange, customRef }) => { +export default ({ width, height, invalid, errorMsg, onChange, customRef }) => { const [strokeWidth, setStrokeWidth] = useState(4) const [strokeColor, setStrokeColor] = useState('black') const [showPicker, setShowPicker] = useState(false) const canvasRef = useRef() - const handleChange = async () => { - const newSVG = await canvasRef.current.exportSvg() - onChange(newSVG) + const [debounceTimerId, setDebounceTimerId] = useState(null) + const loadedRef = useRef(false) + + useEffect(() => { + loadedRef.current = true + return () => { + loadedRef.current = false + } + }, []) + + const handleChange = e => { + if (!onChange) return + + if (debounceTimerId) clearTimeout(debounceTimerId) + const newTimerId = setTimeout(async () => { + if (!loadedRef.current) return + const newSVG = await canvasRef.current.exportSvg() + onChange(newSVG) + }, 500) + + setDebounceTimerId(newTimerId) } const handleClear = () => { if (canvasRef.current) canvasRef.current.clearCanvas() } + const handleUndo = () => { + if (canvasRef.current) canvasRef.current.undo() + } + + const handleRedo = () => { + if (canvasRef.current) canvasRef.current.redo() + } + return (
{ + setShowPicker(false) }} > + {errorMsg && ( +

+ {errorMsg} +

+ )} + -
+
Color Picker Icon { + onClick={e => { + e.stopPropagation() setShowPicker(!showPicker) }} /> @@ -104,6 +168,37 @@ export default ({ width, height, value, invalid, errorMsg, onChange, customRef } }} />
+ +
+ Undo Icon + Redo Icon +
+ {showPicker && ( -
+
{ + e.stopPropagation() + }} + > { diff --git a/src/utility/utilities.js b/src/utility/utilities.js index fc8a7646..b5af2cb9 100644 --- a/src/utility/utilities.js +++ b/src/utility/utilities.js @@ -144,3 +144,11 @@ export const cutString = (string, maxLength) => { } return `${string.substring(0, cut)}...` } + +export const mergeRefs = (...refs) => { + return node => { + for (const ref of refs) { + ref.current = node + } + } +} From 97e0539ff5fd8d0cc7fbf79ab6fbc725ff79a249 Mon Sep 17 00:00:00 2001 From: Lincoln Lee Date: Wed, 26 Jul 2023 15:27:37 -0700 Subject: [PATCH 3/7] chore: refactor to use styled components --- src/components/Input/SketchCanvas.js | 207 +++++++++++++-------------- 1 file changed, 100 insertions(+), 107 deletions(-) diff --git a/src/components/Input/SketchCanvas.js b/src/components/Input/SketchCanvas.js index 74169bb3..59641916 100644 --- a/src/components/Input/SketchCanvas.js +++ b/src/components/Input/SketchCanvas.js @@ -7,6 +7,89 @@ import colorpickerIcon from '../../assets/icons/colorpicker.svg' import undoIcon from '../../assets/icons/undo.svg' import redoIcon from '../../assets/icons/redo.svg' import { mergeRefs } from '../../utility/utilities' +import styled from 'styled-components' + +const CanvasContainer = styled.div` + background: #f3f3f3; + display: flex; + flex-direction: column; + position: relative; +` + +const ErrorMessage = styled.p` + position: absolute; + top: 0px; + left: 0px; + padding: 5px; + padding-left: 8px; + padding-right: 8px; + margin: 0px; + background: #ffaaaa; + color: red; + z-index: 10; + box-sizing: border-box; +` + +const Stroke4Button = styled.div` + background: #333333; + border-radius: 999px; + cursor: pointer; + width: 8px; + height: 8px; +` + +const Stroke6Button = styled.div` + background: #333333; + border-radius: 999px; + cursor: pointer; + width: 12px; + height: 12px; +` + +const Stroke8Button = styled.div` + background: #333333; + border-radius: 999px; + cursor: pointer; + width: 16px; + height: 16px; +` + +const CanvasIcon = styled.img` + width: 20px; + height: 25px; + cursor: pointer; +` + +const HistoryIcons = styled.div` + display: flex; + flex-direction: row; + gap: 5px; + align-items: center; +` + +const ColorPickerWrapper = styled.div` + position: absolute; + z-index: 100; + top: 0; + right: 0; +` + +const LeftIcons = styled.div` + display: flex; + flex-direction: row; + align-items: center; + gap: 5px; +` + +const ToolBar = styled.div` + display: flex; + flex-direction: row; + padding: 3px; + padding-left: 7px; + padding-right: 7px; + justify-content: space-between; + user-select: none; +` export default ({ width, height, invalid, errorMsg, onChange, customRef }) => { const [strokeWidth, setStrokeWidth] = useState(4) @@ -50,12 +133,8 @@ export default ({ width, height, invalid, errorMsg, onChange, customRef }) => { } return ( -
{ setShowPicker(false) }} > - {errorMsg && ( -

- {errorMsg} -

- )} + {errorMsg && {errorMsg}} { style={{ border: 'none' }} /> -
-
+ + { setShowPicker(!showPicker) }} /> -
{ setStrokeWidth(4) }} /> -
{ setStrokeWidth(6) }} /> -
{ setStrokeWidth(8) }} /> -
+ -
- Undo Icon - Redo Icon -
+ + + + - { /> {showPicker && ( -
{ e.stopPropagation() }} @@ -225,9 +218,9 @@ export default ({ width, height, invalid, errorMsg, onChange, customRef }) => { setStrokeColor(e.hex) }} /> -
+ )} -
-
+
+ ) } From 6ab7661912b0ecfa3c8ca0d32b811aad50ff5797 Mon Sep 17 00:00:00 2001 From: Lincoln Lee Date: Wed, 26 Jul 2023 15:28:39 -0700 Subject: [PATCH 4/7] chore: minor sweepup --- src/components/Input/SketchCanvas.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/Input/SketchCanvas.js b/src/components/Input/SketchCanvas.js index 59641916..90402d68 100644 --- a/src/components/Input/SketchCanvas.js +++ b/src/components/Input/SketchCanvas.js @@ -157,13 +157,10 @@ export default ({ width, height, invalid, errorMsg, onChange, customRef }) => { - Date: Wed, 26 Jul 2023 18:23:36 -0700 Subject: [PATCH 5/7] feat: add base64 export option, docstring --- src/components/ApplicationForm/Skills.js | 1 + src/components/Input/SketchCanvas.js | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/components/ApplicationForm/Skills.js b/src/components/ApplicationForm/Skills.js index 17660462..cdf8df0a 100644 --- a/src/components/ApplicationForm/Skills.js +++ b/src/components/ApplicationForm/Skills.js @@ -291,6 +291,7 @@ export default ({ refs, errors, formInputs, onChange, role, handleResume }) => { }) } customRef={refs['longAnswers3Ref']} + exportAsBase64 /> diff --git a/src/components/Input/SketchCanvas.js b/src/components/Input/SketchCanvas.js index 90402d68..95a94877 100644 --- a/src/components/Input/SketchCanvas.js +++ b/src/components/Input/SketchCanvas.js @@ -90,8 +90,16 @@ const ToolBar = styled.div` justify-content: space-between; user-select: none; ` - -export default ({ width, height, invalid, errorMsg, onChange, customRef }) => { +/** + * @param width number, width of the entire component + * @param height number, height of the entire component + * @param invalid boolean, indicates dissatisfaction with current value + * @param errorMsg string, indicates some error + * @param onChange (result: string) => void, triggers with result (SVG or Base64) on change. Has debouncing. + * @param customRef a ref directly to the canvas component + * @param exportAsBase64 boolean, onChange() will return a base64 string instead of svg if true + */ +export default ({ width, height, invalid, errorMsg, onChange, customRef, exportAsBase64 }) => { const [strokeWidth, setStrokeWidth] = useState(4) const [strokeColor, setStrokeColor] = useState('black') const [showPicker, setShowPicker] = useState(false) @@ -113,8 +121,14 @@ export default ({ width, height, invalid, errorMsg, onChange, customRef }) => { if (debounceTimerId) clearTimeout(debounceTimerId) const newTimerId = setTimeout(async () => { if (!loadedRef.current) return - const newSVG = await canvasRef.current.exportSvg() - onChange(newSVG) + + if (exportAsBase64) { + const newBase64 = await canvasRef.current.exportImage('jpeg') + onChange(newBase64) + } else { + const newSVG = await canvasRef.current.exportSvg() + onChange(newSVG) + } }, 500) setDebounceTimerId(newTimerId) From 283b901ce75d0f7d83fcf91e0fbecc1183f36dc3 Mon Sep 17 00:00:00 2001 From: michelleykim Date: Thu, 17 Aug 2023 01:12:13 -0700 Subject: [PATCH 6/7] add responsivity to canvas --- src/components/ApplicationForm/Skills.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ApplicationForm/Skills.js b/src/components/ApplicationForm/Skills.js index cdf8df0a..1b0e3334 100644 --- a/src/components/ApplicationForm/Skills.js +++ b/src/components/ApplicationForm/Skills.js @@ -280,7 +280,7 @@ export default ({ refs, errors, formInputs, onChange, role, handleResume }) => { question 21 Draw your favourite character! Date: Thu, 17 Aug 2023 02:23:37 -0700 Subject: [PATCH 7/7] extract debouncer logic. use useDebounce() --- src/components/Input/SketchCanvas.js | 30 +++++++++++++--------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/components/Input/SketchCanvas.js b/src/components/Input/SketchCanvas.js index 95a94877..6c605354 100644 --- a/src/components/Input/SketchCanvas.js +++ b/src/components/Input/SketchCanvas.js @@ -6,7 +6,7 @@ import trashcanIcon from '../../assets/icons/trashcan.svg' import colorpickerIcon from '../../assets/icons/colorpicker.svg' import undoIcon from '../../assets/icons/undo.svg' import redoIcon from '../../assets/icons/redo.svg' -import { mergeRefs } from '../../utility/utilities' +import { mergeRefs, useDebounce } from '../../utility/utilities' import styled from 'styled-components' const CanvasContainer = styled.div` @@ -105,7 +105,6 @@ export default ({ width, height, invalid, errorMsg, onChange, customRef, exportA const [showPicker, setShowPicker] = useState(false) const canvasRef = useRef() - const [debounceTimerId, setDebounceTimerId] = useState(null) const loadedRef = useRef(false) useEffect(() => { @@ -115,23 +114,22 @@ export default ({ width, height, invalid, errorMsg, onChange, customRef, exportA } }, []) - const handleChange = e => { - if (!onChange) return + const debounceUpdate = useDebounce(async () => { + if (!loadedRef.current) return - if (debounceTimerId) clearTimeout(debounceTimerId) - const newTimerId = setTimeout(async () => { - if (!loadedRef.current) return + if (exportAsBase64) { + const newBase64 = await canvasRef.current.exportImage('jpeg') + onChange(newBase64) + } else { + const newSVG = await canvasRef.current.exportSvg() + onChange(newSVG) + } + }, 500) - if (exportAsBase64) { - const newBase64 = await canvasRef.current.exportImage('jpeg') - onChange(newBase64) - } else { - const newSVG = await canvasRef.current.exportSvg() - onChange(newSVG) - } - }, 500) + const handleChange = e => { + if (!onChange) return - setDebounceTimerId(newTimerId) + debounceUpdate() } const handleClear = () => {