pcm-shared-components 0.0.44 → 0.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,26 @@
1
1
  # Shared Components between client and admin portal
2
2
 
3
3
  The Library is published to NPM on the github build.
4
+
5
+ # Deploying
6
+
7
+ - When the code is checked into Github it will publish it automatically if the package.json version is bumped.
8
+
9
+ # Manual Deploy
10
+
11
+ Manually deploying is faster then committing the code as it doesn't need to create a ful build.
12
+
13
+ ```shell
14
+ # Must build the Storybook dist folder before manually publishing
15
+ > npm run build
16
+
17
+ # Edit the package.json file and up he version.
18
+ > npm version patch --force
19
+
20
+ # Login if not already logged in
21
+ > npm login
22
+
23
+ # Publish your new library
24
+ > npm publish
25
+
26
+ ```
@@ -0,0 +1,207 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import tinycolor from "tinycolor2"; // Brush colour and size
3
+ // // Geometry
4
+ // // Drawing functions
5
+ // // Event helpers
6
+ // // Event handlers
7
+
8
+ export const CanvasPain = () => {
9
+ const canvas = useRef();
10
+ let context; // // Set up our drawing context
11
+ // // const canvas = document.getElementById("canvas");
12
+ // canvas.current.width = window.innerWidth;
13
+ // canvas.current.height = window.innerHeight - 10;
14
+
15
+ let colour;
16
+ const strokeWidth = 40;
17
+ const varyBrightness = 5; // Drawing state
18
+
19
+ let latestPoint;
20
+ let drawing = false;
21
+ let currentAngle;
22
+
23
+ const mouseDown = evt => {
24
+ if (drawing) {
25
+ return;
26
+ }
27
+
28
+ evt.preventDefault();
29
+ canvas.current.addEventListener("mousemove", mouseMove, false);
30
+ startStroke([evt.offsetX, evt.offsetY]);
31
+ };
32
+
33
+ const mouseEnter = evt => {
34
+ if (!mouseButtonIsDown(evt.buttons) || drawing) {
35
+ return;
36
+ }
37
+
38
+ mouseDown(evt);
39
+ };
40
+
41
+ const endStroke = evt => {
42
+ if (!drawing) {
43
+ return;
44
+ }
45
+
46
+ drawing = false;
47
+ evt.currentTarget.removeEventListener("mousemove", mouseMove, false);
48
+ };
49
+
50
+ const continueStroke = newPoint => {
51
+ const newAngle = getNewAngle(latestPoint, newPoint, currentAngle);
52
+ drawStroke(currentBrush, latestPoint, newPoint, currentAngle, newAngle);
53
+ currentAngle = newAngle % (Math.PI * 2);
54
+ latestPoint = newPoint;
55
+ };
56
+
57
+ const startStroke = point => {
58
+ colour = document.getElementById("colourInput").value;
59
+ currentAngle = undefined;
60
+ currentBrush = makeBrush(strokeWidth);
61
+ drawing = true;
62
+ latestPoint = point;
63
+ };
64
+
65
+ const touchStart = evt => {
66
+ if (drawing) {
67
+ return;
68
+ }
69
+
70
+ evt.preventDefault();
71
+ startStroke(getTouchPoint(evt));
72
+ };
73
+
74
+ const mouseMove = evt => {
75
+ if (!drawing) {
76
+ return;
77
+ }
78
+
79
+ continueStroke([evt.offsetX, evt.offsetY]);
80
+ };
81
+
82
+ const touchMove = evt => {
83
+ if (!drawing) {
84
+ return;
85
+ }
86
+
87
+ continueStroke(getTouchPoint(evt));
88
+ };
89
+
90
+ const touchEnd = evt => {
91
+ drawing = false;
92
+ };
93
+
94
+ const varyColour = sourceColour => {
95
+ const amount = Math.round(Math.random() * 2 * varyBrightness);
96
+ const c = tinycolor(sourceColour);
97
+ const varied = amount > varyBrightness ? c.brighten(amount - varyBrightness) : c.darken(amount);
98
+ return varied.toHexString();
99
+ };
100
+
101
+ const makeBrush = size => {
102
+ const brush = [];
103
+ let bristleCount = Math.round(size / 3);
104
+ const gap = strokeWidth / bristleCount;
105
+
106
+ for (let i = 0; i < bristleCount; i++) {
107
+ const distance = i === 0 ? 0 : gap * i + Math.random() * gap / 2 - gap / 2;
108
+ brush.push({
109
+ distance,
110
+ thickness: Math.random() * 2 + 2,
111
+ colour: varyColour(colour)
112
+ });
113
+ }
114
+
115
+ return brush;
116
+ };
117
+
118
+ let currentBrush = makeBrush(strokeWidth);
119
+
120
+ const getNewAngle = (origin, destination, oldAngle) => {
121
+ const bearing = getBearing(origin, destination);
122
+
123
+ if (typeof oldAngle === "undefined") {
124
+ console.log(bearing);
125
+ return bearing;
126
+ }
127
+
128
+ return oldAngle - angleDiff(oldAngle, bearing);
129
+ };
130
+
131
+ const rotatePoint = (distance, angle, origin) => [origin[0] + distance * Math.cos(angle), origin[1] + distance * Math.sin(angle)];
132
+
133
+ const getBearing = (origin, destination) => (Math.atan2(destination[1] - origin[1], destination[0] - origin[0]) - Math.PI / 2) % (Math.PI * 2);
134
+
135
+ const angleDiff = (angleA, angleB) => {
136
+ const twoPi = Math.PI * 2;
137
+ const diff = (angleA - (angleB > 0 ? angleB : angleB + twoPi) + Math.PI) % twoPi - Math.PI;
138
+ return diff < -Math.PI ? diff + twoPi : diff;
139
+ };
140
+
141
+ const drawStroke = (bristles, origin, destination, oldAngle, newAngle) => {
142
+ bristles.forEach(bristle => {
143
+ context.beginPath();
144
+ let bristleOrigin = rotatePoint(bristle.distance - strokeWidth / 2, oldAngle, origin);
145
+ let bristleDestination = rotatePoint(bristle.distance - strokeWidth / 2, newAngle, destination);
146
+ const controlPoint = rotatePoint(bristle.distance - strokeWidth / 2, newAngle, origin);
147
+ bristleDestination = rotatePoint(bristle.distance - strokeWidth / 2, newAngle, destination);
148
+ strokeBristle(bristleOrigin, bristleDestination, bristle, controlPoint);
149
+ });
150
+ };
151
+
152
+ const strokeBristle = (origin, destination, bristle, controlPoint) => {
153
+ context.beginPath();
154
+ context.moveTo(origin[0], origin[1]);
155
+ context.strokeStyle = bristle.colour;
156
+ context.lineWidth = bristle.thickness;
157
+ context.lineCap = "round";
158
+ context.lineJoin = "round";
159
+ context.shadowColor = bristle.colour;
160
+ context.shadowBlur = bristle.thickness / 2;
161
+ context.quadraticCurveTo(controlPoint[0], controlPoint[1], destination[0], destination[1]);
162
+ context.lineTo(destination[0], destination[1]);
163
+ context.stroke();
164
+ };
165
+
166
+ const getTouchPoint = evt => {
167
+ if (!evt.currentTarget) {
168
+ return [0, 0];
169
+ }
170
+
171
+ const rect = evt.currentTarget.getBoundingClientRect();
172
+ const touch = evt.targetTouches[0];
173
+ console.log(rect, touch);
174
+ return [touch.clientX - rect.left, touch.clientY - rect.top];
175
+ };
176
+
177
+ const BUTTON = 0b01;
178
+
179
+ const mouseButtonIsDown = buttons => (BUTTON & buttons) === BUTTON;
180
+
181
+ useEffect(() => {
182
+ // Register event handlers
183
+ canvas.current.addEventListener("touchstart", touchStart, false);
184
+ canvas.current.addEventListener("touchend", touchEnd, false);
185
+ canvas.current.addEventListener("touchcancel", touchEnd, false);
186
+ canvas.current.addEventListener("touchmove", touchMove, false);
187
+ canvas.current.addEventListener("mousedown", mouseDown, false);
188
+ canvas.current.addEventListener("mouseup", endStroke, false);
189
+ canvas.current.addEventListener("mouseout", endStroke, false);
190
+ canvas.current.addEventListener("mouseenter", mouseEnter, false);
191
+ context = canvas.current.getContext("2d");
192
+ }, []);
193
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("canvas", {
194
+ id: "canvas",
195
+ ref: canvas,
196
+ height: "450",
197
+ width: "800",
198
+ style: {
199
+ backgroundColor: 'yellow'
200
+ }
201
+ }), /*#__PURE__*/React.createElement("input", {
202
+ type: "color",
203
+ id: "colourInput",
204
+ value: "#3d34a5"
205
+ }));
206
+ };
207
+ export default CanvasPain;
@@ -0,0 +1,191 @@
1
+ import React from 'react';
2
+ import Slider from '@mui/material/Slider';
3
+ import Typography from '@mui/material/Typography';
4
+ import { combineDrawing } from '../service/image_tools';
5
+ import imageCompression from 'browser-image-compression';
6
+ import { ChromePicker } from 'react-color';
7
+ import Button from '@mui/material/Button';
8
+ import { OUTPUT_TYPES } from '.././constants';
9
+ const imageScaleMarks = [{
10
+ value: 1.5,
11
+ label: 'Full'
12
+ }, {
13
+ value: 2,
14
+ label: 'Medium'
15
+ }, {
16
+ value: 2.5,
17
+ label: 'Small'
18
+ }];
19
+ export const downloadImage = (blob, filename = `pitchcamp_${new Date().toString()}.jpg`) => {
20
+ //Used for testing
21
+ const a = document.createElement('a');
22
+ document.body.appendChild(a);
23
+ a.style = 'display: none';
24
+ const url = window.URL.createObjectURL(blob);
25
+ a.href = url;
26
+ a.download = filename;
27
+ a.click();
28
+ window.URL.revokeObjectURL(url);
29
+ };
30
+ export const toBase64 = async blob => {
31
+ try {
32
+ const reader = new FileReader();
33
+ reader.readAsDataURL(blob);
34
+ return await new Promise(resolve => {
35
+ reader.onloadend = () => {
36
+ resolve(reader.result);
37
+ };
38
+ });
39
+ } catch (error) {
40
+ console.error('Error in toBase64 function of hte ImageCanvasDraw component: ', error);
41
+ }
42
+ };
43
+
44
+ function valueLabelFormat(value) {
45
+ return imageScaleMarks.findIndex(mark => mark.value === value) + 0.5;
46
+ }
47
+
48
+ const exportImage = async (thisFunction, saveableCanvas, outputFormat, imageCompressionOptions) => {
49
+ try {
50
+ const {
51
+ blob,
52
+ dataUri
53
+ } = combineDrawing(saveableCanvas);
54
+ imageCompression(blob, imageCompressionOptions).then(function (compressedBlob) {
55
+ if (OUTPUT_TYPES.BLOB === outputFormat.toLowerCase()) {
56
+ //Return the blob object
57
+ thisFunction(compressedBlob);
58
+ } else {
59
+ //Return the base64 url
60
+ toBase64(compressedBlob).then(base64Data => {
61
+ thisFunction(base64Data);
62
+ });
63
+ }
64
+ }).catch(function (error) {
65
+ console.log(error.message);
66
+ });
67
+ } catch (error) {
68
+ console.log('error in exportImage in ImageCanvasDraw comp: ', error);
69
+ }
70
+ };
71
+
72
+ const Controls = props => {
73
+ const {
74
+ state,
75
+ setState,
76
+ saveableCanvas,
77
+ onSave,
78
+ outputFormat,
79
+ showDownloadButton
80
+ } = props;
81
+ return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
82
+ className: "flex flex-col"
83
+ }, /*#__PURE__*/React.createElement("div", {
84
+ className: "flex flex-row justify-between "
85
+ }, /*#__PURE__*/React.createElement(Button, {
86
+ color: "secondary",
87
+ onClick: () => {
88
+ saveableCanvas.current.eraseAll();
89
+ }
90
+ }, "Erase"), /*#__PURE__*/React.createElement(Button, {
91
+ color: "secondary",
92
+ onClick: () => {
93
+ saveableCanvas.current.undo();
94
+ }
95
+ }, "Undo"), /*#__PURE__*/React.createElement(Button, {
96
+ variant: "outlined",
97
+ onClick: () => {
98
+ exportImage(onSave, saveableCanvas, outputFormat, state.imageCompressionOptions);
99
+ }
100
+ }, "Save")), /*#__PURE__*/React.createElement("div", {
101
+ className: "m-auto my-12"
102
+ }, /*#__PURE__*/React.createElement(ChromePicker, {
103
+ className: "drop-shadow-none",
104
+ disableAlpha: true,
105
+ color: state.color,
106
+ onChange: (color, event) => {
107
+ setState(previousState => {
108
+ return { ...previousState,
109
+ color: color.hex
110
+ };
111
+ });
112
+ }
113
+ })), /*#__PURE__*/React.createElement("div", {
114
+ className: "my-6"
115
+ }, /*#__PURE__*/React.createElement(Typography, {
116
+ id: "image-scale"
117
+ }, "Pull String Length"), /*#__PURE__*/React.createElement("div", {
118
+ className: "mx-8"
119
+ }, /*#__PURE__*/React.createElement(Slider, {
120
+ className: "w-full",
121
+ defaultValue: 1.5,
122
+ "aria-labelledby": "image-scale",
123
+ valueLabelDisplay: "auto",
124
+ value: state.lazyRadius,
125
+ onChange: event => {
126
+ setState(previousState => {
127
+ return { ...previousState,
128
+ lazyRadius: Number(event.target.value)
129
+ };
130
+ });
131
+ },
132
+ step: 1,
133
+ min: 1,
134
+ max: 30
135
+ }))), /*#__PURE__*/React.createElement("div", {
136
+ className: "my-6"
137
+ }, /*#__PURE__*/React.createElement(Typography, {
138
+ id: "image-scale"
139
+ }, "Brush Size"), /*#__PURE__*/React.createElement("div", {
140
+ className: "mx-8"
141
+ }, /*#__PURE__*/React.createElement(Slider, {
142
+ className: "w-full",
143
+ defaultValue: 1.5,
144
+ "aria-labelledby": "image-scale",
145
+ valueLabelDisplay: "auto",
146
+ value: state.brushRadius,
147
+ onChange: event => {
148
+ setState(previousState => {
149
+ return { ...previousState,
150
+ brushRadius: Number(event.target.value)
151
+ };
152
+ });
153
+ },
154
+ step: 1,
155
+ min: 1,
156
+ max: 10
157
+ }))), /*#__PURE__*/React.createElement("div", {
158
+ className: "my-6"
159
+ }, /*#__PURE__*/React.createElement(Typography, {
160
+ id: "image-scale"
161
+ }, "Image Size"), /*#__PURE__*/React.createElement("div", {
162
+ className: "mx-8"
163
+ }, /*#__PURE__*/React.createElement(Slider, {
164
+ className: "w-full",
165
+ defaultValue: 1.5,
166
+ valueLabelFormat: valueLabelFormat,
167
+ "aria-labelledby": "image-scale",
168
+ step: null // valueLabelDisplay="auto"
169
+ ,
170
+ value: state.imageScale,
171
+ onChange: event => {
172
+ setState(previousState => {
173
+ return { ...previousState,
174
+ imageScale: Number(event.target.value)
175
+ };
176
+ });
177
+ },
178
+ marks: imageScaleMarks,
179
+ min: 1.5,
180
+ max: 2.5
181
+ })))), /*#__PURE__*/React.createElement("div", {
182
+ className: "flex flex-row justify-center mt-12"
183
+ }, showDownloadButton && /*#__PURE__*/React.createElement(Button, {
184
+ variant: "outlined",
185
+ onClick: () => {
186
+ exportImage(downloadImage, saveableCanvas, OUTPUT_TYPES.BLOB, state.imageCompressionOptions);
187
+ }
188
+ }, "Download")));
189
+ };
190
+
191
+ export default Controls;
@@ -0,0 +1 @@
1
+ export * from './types';
@@ -0,0 +1,4 @@
1
+ export const OUTPUT_TYPES = {
2
+ BLOB: 'blob',
3
+ BASE64: 'base64'
4
+ };
@@ -0,0 +1,183 @@
1
+ import { useState, useRef, useEffect } from 'react';
2
+ import CanvasDraw from "react-canvas-draw";
3
+ import Controls from './components/Controls';
4
+ import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
5
+ import ChevronRightIcon from '@mui/icons-material/ChevronRight';
6
+ import Button from '@mui/material/Button';
7
+ import { getStorage, setStorage } from './service/local_storage';
8
+ import PropTypes from 'prop-types';
9
+ /**
10
+ * Displays an image that can is used to draw on to generate maps with highlighted routes.
11
+ *
12
+ * - Requires the ```b64DataUrl``` props to contain a dataUrl string.
13
+ * - Returns the ```data``` inside the onSave(data) function passed to the component.
14
+ *
15
+ * ### Importing the component
16
+
17
+ ```//PitchCamp shared component
18
+ import {ImageCanvasDraw} from 'pcm-shared-components';
19
+ ```
20
+
21
+ *
22
+ */
23
+
24
+ const ImageCanvasDraw = props => {
25
+ const {
26
+ onSave,
27
+ outputFormat,
28
+ showDownloadButton,
29
+ b64DataUrl
30
+ } = props;
31
+ const [state, setState] = useState({
32
+ drawerOpen: true,
33
+ color: "darkorange",
34
+ width: 0,
35
+ height: 0,
36
+ brushRadius: 5,
37
+ lazyRadius: 2,
38
+ imageScale: 1.5,
39
+ imageQuality: 0.5,
40
+ b64DataUrl: b64DataUrl,
41
+ imageCompressionOptions: {
42
+ maxSizeMB: 0.4,
43
+ maxWidthOrHeight: 1920,
44
+ useWebWorker: true,
45
+ maxIteration: 10
46
+ }
47
+ });
48
+ const saveableCanvas = useRef();
49
+
50
+ const setImageSize = imageUrl => {
51
+ try {
52
+ const img = new Image();
53
+
54
+ img.onload = () => {
55
+ //Scale the image down
56
+ const new_width = Number(img.width) / Number(state.imageScale);
57
+ const new_height = Number(img.height) / Number(state.imageScale); // Create a temporary canvas to draw the downscaled image on.
58
+
59
+ let canvas = document.createElement("canvas");
60
+ canvas.width = new_width;
61
+ canvas.height = new_height; // Draw the downscaled image on the canvas and return the new data URL.
62
+
63
+ let ctx = canvas.getContext("2d");
64
+ ctx.drawImage(img, 0, 0, new_width, new_height);
65
+ const newDataUrl = canvas.toDataURL("image/jpeg", state.imageQuality);
66
+ setState(previousState => {
67
+ return { ...previousState,
68
+ ...getStorage(),
69
+ height: new_height,
70
+ width: new_width,
71
+ b64DataUrl: newDataUrl
72
+ };
73
+ });
74
+ };
75
+
76
+ img.src = imageUrl;
77
+ } catch (error) {
78
+ console.error('error in setImageSize', error);
79
+ }
80
+ };
81
+
82
+ useEffect(() => {
83
+ //Get the scaled down image and width, height
84
+ setImageSize(state.b64DataUrl);
85
+ }, []);
86
+ useEffect(() => {
87
+ if (saveableCanvas && saveableCanvas.current) {
88
+ //Refresh the canvas each time a props changes
89
+ saveableCanvas.current.drawImage();
90
+ setStorage(state);
91
+ }
92
+ }, [state]);
93
+
94
+ const DrawerButtonOpenClose = ({
95
+ children
96
+ }) => /*#__PURE__*/React.createElement(Button, {
97
+ style: {
98
+ minWidth: 6,
99
+ padding: 0,
100
+ margin: 0
101
+ },
102
+ color: "secondary",
103
+ onClick: () => {
104
+ setState(previousState => {
105
+ return { ...previousState,
106
+ drawerOpen: !previousState.drawerOpen
107
+ };
108
+ });
109
+ }
110
+ }, children);
111
+
112
+ if (state.width && state.width !== 0) {
113
+ return /*#__PURE__*/React.createElement("div", {
114
+ className: "flex flex-row overflow-hidden"
115
+ }, state.drawerOpen ? /*#__PURE__*/React.createElement("div", {
116
+ className: "flex flex-row",
117
+ style: {
118
+ width: 'auto'
119
+ }
120
+ }, /*#__PURE__*/React.createElement(Controls, {
121
+ state: state,
122
+ setState: setState,
123
+ saveableCanvas: saveableCanvas,
124
+ onSave: onSave,
125
+ outputFormat: outputFormat,
126
+ showDownloadButton: showDownloadButton
127
+ }), /*#__PURE__*/React.createElement(DrawerButtonOpenClose, null, /*#__PURE__*/React.createElement(ChevronLeftIcon, null))) : /*#__PURE__*/React.createElement("div", {
128
+ className: "flex flex-row justify-end"
129
+ }, /*#__PURE__*/React.createElement(DrawerButtonOpenClose, null, /*#__PURE__*/React.createElement(ChevronRightIcon, null))), state.width !== 0 && /*#__PURE__*/React.createElement("div", {
130
+ className: "overflow-scroll"
131
+ }, /*#__PURE__*/React.createElement(CanvasDraw, {
132
+ className: "",
133
+ ref: saveableCanvas,
134
+ onChange: evt => console.log("onChange", evt) // enablePanAndZoom //Don't enable pan and zoom or else it gets too complicated when saving...
135
+ // zoomExtents={{ min: 0.33, max: 10 }}
136
+ ,
137
+ clampLinesToDocument: true,
138
+ hideInterface: false,
139
+ hideGrid: false,
140
+ backgroundColor: 'rgba(34,34,34,0.2)',
141
+ brushColor: state.color,
142
+ brushRadius: state.brushRadius,
143
+ lazyRadius: state.lazyRadius,
144
+ canvasWidth: Number(state.width) / Number(state.imageScale),
145
+ canvasHeight: Number(state.height) / Number(state.imageScale),
146
+ catenaryColor: 'black',
147
+ gridColor: "#ccc",
148
+ imgSrc: state.b64DataUrl
149
+ })));
150
+ } else {
151
+ return /*#__PURE__*/React.createElement(React.Fragment, null, "loading...");
152
+ }
153
+ };
154
+
155
+ export default ImageCanvasDraw;
156
+ ImageCanvasDraw.defaultProps = {
157
+ onSave: undefined,
158
+ outputFormat: 'blob',
159
+ showDownloadButton: undefined,
160
+ b64DataUrl: undefined
161
+ };
162
+ ImageCanvasDraw.propTypes = {
163
+ /** On save function to execute returns either base64 or blob format based on the output format specified
164
+ *
165
+ * - onSave(data) data is either a **``blob``** or a **``base64``**
166
+
167
+ ```
168
+ onSave = (data) =>{
169
+
170
+ }
171
+ ```
172
+ */
173
+ onSave: PropTypes.func,
174
+
175
+ /** Specified the type of data the onSave function will return */
176
+ outputFormat: PropTypes.oneOf(['blob', 'base64']),
177
+
178
+ /** If specified then we show the download button which is mainly used for testing to display what the canvas is exporting */
179
+ showDownloadButton: PropTypes.bool,
180
+
181
+ /** This is the image in a base 64 url format to use as the base image to draw on */
182
+ b64DataUrl: PropTypes.string.isRequired
183
+ };
@@ -0,0 +1,37 @@
1
+ export const combineDrawing = canvasRef => {
2
+ try {
3
+ const width = canvasRef.current.props.canvasWidth;
4
+ const height = canvasRef.current.props.canvasHeight;
5
+ const background = canvasRef.current.canvasContainer.children[0]; // const test = canvasRef.current.canvasContainer.children[0];
6
+
7
+ const drawing = canvasRef.current.canvasContainer.children[1];
8
+ const canvas = document.createElement('canvas');
9
+ canvas.width = width;
10
+ canvas.height = height; // composite now
11
+
12
+ canvas.getContext('2d').drawImage(background, 0, 0);
13
+ canvas.getContext('2d').globalAlpha = 1.0;
14
+ canvas.getContext('2d').drawImage(drawing, 0, 0); // canvas.getContext('2d').drawImage(test, 0, 0);
15
+
16
+ const dataUri = canvas.toDataURL('image/jpeg', 1.0);
17
+ const data = dataUri.split(',')[1];
18
+ const mimeType = dataUri.split(';')[0].slice(5);
19
+ const bytes = window.atob(data);
20
+ const buf = new ArrayBuffer(bytes.length);
21
+ const arr = new Uint8Array(buf);
22
+
23
+ for (let i = 0; i < bytes.length; i++) {
24
+ arr[i] = bytes.charCodeAt(i);
25
+ }
26
+
27
+ const blob = new Blob([arr], {
28
+ type: mimeType
29
+ });
30
+ return {
31
+ blob: blob,
32
+ dataUri: dataUri
33
+ };
34
+ } catch (error) {
35
+ console.error('Error in combineDrawing: ', error);
36
+ }
37
+ };
@@ -0,0 +1,20 @@
1
+ const STORAGE_NAME = 'PC_IMAGE_DRAW_STATES';
2
+ export const getStorage = () => {
3
+ try {
4
+ return JSON.parse(localStorage.getItem(STORAGE_NAME));
5
+ } catch (error) {
6
+ console.error('Error in getting storage for IMAGE Draw:', error);
7
+ setStorage('');
8
+ }
9
+ };
10
+ export const setStorage = value => {
11
+ try {
12
+ //Need to filter out the b64DataUrl object from the state or else it might slow down the browser...
13
+ let new_value = Object.fromEntries(Object.entries(value).filter(([key, value]) => key !== 'b64DataUrl'));
14
+ console.log('debug new_value', new_value);
15
+ localStorage.setItem(STORAGE_NAME, JSON.stringify(new_value));
16
+ } catch (error) {
17
+ console.error('Error in setting storage for IMAGE Draw:', error);
18
+ setStorage('');
19
+ }
20
+ };