pcm-shared-components 0.0.45 → 0.0.48

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;
@@ -7,8 +7,6 @@ import clsx from 'clsx';
7
7
  import MoreVertIcon from '@mui/icons-material/MoreVert';
8
8
  import { ThemeProvider } from '@mui/system';
9
9
  import { createTheme } from '@mui/material/styles';
10
- import { theme } from '../../Theme/theme';
11
- const defaultTheme = createTheme(theme.pitchCamp);
12
10
  const useStyles = makeStyles(() => ({
13
11
  rootFabElement: {
14
12
  margin: 2,
@@ -59,8 +57,10 @@ const CustomFab = props => {
59
57
  Icon,
60
58
  buttonText,
61
59
  boxShadow,
62
- iconLocation
60
+ iconLocation,
61
+ theme
63
62
  } = props;
63
+ const defaultTheme = createTheme(theme);
64
64
  const classes = useStyles(props);
65
65
  console.log('debug boxShadow', boxShadow);
66
66
  return /*#__PURE__*/React.createElement(ThemeProvider, {
@@ -106,7 +106,8 @@ CustomFab.defaultProps = {
106
106
  textColor: 'textPrimary',
107
107
  hoverTextColor: 'textPrimary',
108
108
  className: '',
109
- color: 'primary'
109
+ color: 'primary',
110
+ theme: {}
110
111
  };
111
112
  CustomFab.propTypes = {
112
113
  /** onClick function passed to the button */
@@ -154,5 +155,8 @@ CustomFab.propTypes = {
154
155
  className: PropTypes.string,
155
156
 
156
157
  /** Theme colors if not using backgroundColor ['primary','secondary'] */
157
- color: PropTypes.oneOf(['primary', 'secondary'])
158
+ color: PropTypes.oneOf(['primary', 'secondary']),
159
+
160
+ /** Theme object to use on this component*/
161
+ theme: PropTypes.object
158
162
  };
@@ -2,6 +2,8 @@ import React from 'react';
2
2
  import CustomFab from '../CustomFab/CustomFab';
3
3
  import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
4
4
  import PropTypes from 'prop-types';
5
+ import { ThemeProvider } from '@mui/system';
6
+ import { createTheme } from '@mui/material/styles';
5
7
  export const HelpButton = props => {
6
8
  const {
7
9
  onClickLink,
@@ -9,9 +11,13 @@ export const HelpButton = props => {
9
11
  color,
10
12
  tooltipText,
11
13
  size,
12
- iconSize
14
+ iconSize,
15
+ theme
13
16
  } = props;
14
- return /*#__PURE__*/React.createElement(CustomFab, {
17
+ const defaultTheme = createTheme(theme);
18
+ return /*#__PURE__*/React.createElement(ThemeProvider, {
19
+ theme: defaultTheme
20
+ }, /*#__PURE__*/React.createElement(CustomFab, {
15
21
  className: className,
16
22
  Icon: HelpOutlineIcon,
17
23
  tooltipText: tooltipText,
@@ -19,10 +25,11 @@ export const HelpButton = props => {
19
25
  size: size,
20
26
  iconSize: iconSize,
21
27
  variant: 'round',
28
+ theme: theme,
22
29
  onClick: () => {
23
30
  window.open(onClickLink, "_blank");
24
31
  }
25
- });
32
+ }));
26
33
  };
27
34
  export default HelpButton;
28
35
  HelpButton.defaultProps = {
@@ -31,7 +38,8 @@ HelpButton.defaultProps = {
31
38
  color: 'primary',
32
39
  tooltipText: '',
33
40
  size: 'large',
34
- iconSize: 24
41
+ iconSize: 24,
42
+ theme: {}
35
43
  };
36
44
  HelpButton.propTypes = {
37
45
  /** On click link, it's the URL to navigate too */
@@ -50,5 +58,8 @@ HelpButton.propTypes = {
50
58
  iconSize: PropTypes.number,
51
59
 
52
60
  /** The size of the button ['small','medium','large'] */
53
- size: PropTypes.oneOf(['small', 'medium', 'large'])
61
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
62
+
63
+ /** Theme object to use on this component*/
64
+ theme: PropTypes.object
54
65
  };
@@ -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
+ };