pcm-shared-components 2.0.14 → 2.0.16
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/dist/app/config/reservation_state.js +89 -0
- package/dist/app/config/yes_no_status.js +72 -0
- package/dist/app/tools/date_formatter.js +141 -0
- package/dist/app/tools/date_overlap.js +177 -0
- package/dist/app/tools/seasonal_dates.js +229 -0
- package/dist/components/Buttons/CustomFab/index.js +0 -27
- package/dist/components/Buttons/DropdownButton/index.js +3 -11
- package/dist/components/Buttons/HelpButton/index.js +1 -6
- package/dist/components/Buttons/ProductItemButton/index.js +8 -22
- package/dist/components/Buttons/TillButton/index.js +3 -10
- package/dist/components/Date/DateRangeCalendar/index.js +1 -21
- package/dist/components/Dialogs/GenericAppBar/components/GenericAppBarSubMenu.js +4 -7
- package/dist/components/Dialogs/GenericAppBar/index.js +24 -22
- package/dist/components/Dialogs/GenericDialogWindow/index.js +20 -34
- package/dist/components/Drawing/CanvasPaint/index.js +12 -37
- package/dist/components/Drawing/ImageCanvasDraw/components/Controls.js +13 -10
- package/dist/components/Drawing/ImageCanvasDraw/index.js +11 -18
- package/dist/components/Drawing/ImageCanvasDraw/service/image_tools.js +6 -6
- package/dist/components/Images/ImageStack/index.js +0 -5
- package/dist/components/Labels/SimpleLabel/index.js +1 -7
- package/dist/components/Layout/SimpleTab/components/TabPanel.js +2 -3
- package/dist/components/Layout/SimpleTab/index.js +8 -15
- package/dist/components/Layout/TwoColumnDisplay/index.js +0 -2
- package/dist/components/Links/HrefLink/index.js +1 -5
- package/dist/components/Loaders/LoadingBackdrop/index.js +4 -4
- package/dist/components/Loaders/WaitingGalaxy/index.js +1 -2
- package/dist/components/Menus/ReusablePopupMenu/components/GenericSubMenu.js +2 -7
- package/dist/components/Menus/ReusablePopupMenu/components/ReusableMenu.js +6 -7
- package/dist/components/Menus/ReusablePopupMenu/index.js +7 -10
- package/dist/components/NEW_COMPONENT/index.js +1 -2
- package/dist/components/NEW_COMPONENT/storybook/index.stories.js +0 -2
- package/dist/components/PcSharedComponentThemeInheritor/index.js +1 -1
- package/dist/components/Platform/PlatformPaymentPlans/components/PriceToggleButton/index.js +0 -2
- package/dist/components/Platform/PlatformPaymentPlans/index.js +12 -27
- package/dist/components/Platform/PlatformPlanPrices/index.js +3 -10
- package/dist/components/Platform/PlatformPlanTitles/index.js +0 -6
- package/dist/components/Sandbox/ScrollingInnerDiv/index.js +2 -5
- package/dist/components/TextBoxes/CurrencyTextInput/index.js +7 -42
- package/dist/components/TextBoxes/SearchBar/index.js +10 -14
- package/dist/components/Timelines/ReservationTimelineComponent/components/SearchLotsTextBox.js +34 -0
- package/dist/components/Timelines/ReservationTimelineComponent/components/TimelinePopUp.js +11 -0
- package/dist/components/Timelines/ReservationTimelineComponent/css/header.css +98 -0
- package/dist/components/Timelines/ReservationTimelineComponent/css/styles.css +121 -0
- package/dist/components/Timelines/ReservationTimelineComponent/css/styles.old +209 -0
- package/dist/components/Timelines/ReservationTimelineComponent/index.js +965 -0
- package/dist/components/Tools/CodeHighlight/index.js +17 -21
- package/dist/components/Tools/PCMScrollbar/index.js +3 -5
- package/dist/components/Tools/isValidFunction.js +0 -1
- package/dist/index.js +3 -1
- package/dist/languages/en/translations.json +1 -1
- package/dist/languages/es/translations.json +1 -1
- package/dist/languages/fr/translations.json +1 -1
- package/dist/locals/coreLocals.js +0 -8
- package/dist/locals/currencySymbol.js +0 -3
- package/dist/locals/i18n.js +11 -5
- package/dist/locals/translationResources.js +2 -1
- package/dist/styles/tailwind.css +1 -1
- package/package.json +4 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { Tooltip, IconButton, MenuItem, Menu } from "@mui/material";
|
|
3
3
|
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
|
4
|
-
|
|
5
4
|
const GenericAppBarSubMenu = ({
|
|
6
5
|
menu,
|
|
7
6
|
classes,
|
|
@@ -10,18 +9,17 @@ const GenericAppBarSubMenu = ({
|
|
|
10
9
|
}) => {
|
|
11
10
|
const [anchorSubMenu, setAnchorSubMenu] = useState(false);
|
|
12
11
|
const [isSubMenuOpen, setSubMenuOpen] = useState(false);
|
|
13
|
-
|
|
14
12
|
const openSubMenu = event => {
|
|
15
13
|
setAnchorSubMenu(event.currentTarget);
|
|
16
14
|
};
|
|
17
|
-
|
|
18
15
|
const handleSubMenuClose = () => {
|
|
19
|
-
setAnchorSubMenu(false);
|
|
16
|
+
setAnchorSubMenu(false);
|
|
20
17
|
|
|
18
|
+
//Closes the previous parent menu too..
|
|
21
19
|
if (handleParentMenuClose) {
|
|
22
20
|
handleParentMenuClose();
|
|
23
|
-
}
|
|
24
|
-
|
|
21
|
+
}
|
|
22
|
+
// handleMobileMenuClose();
|
|
25
23
|
};
|
|
26
24
|
|
|
27
25
|
useEffect(() => {
|
|
@@ -79,5 +77,4 @@ const GenericAppBarSubMenu = ({
|
|
|
79
77
|
color: "inherit"
|
|
80
78
|
}, sub_menu.icon), /*#__PURE__*/React.createElement("p", null, sub_menu.name))))));
|
|
81
79
|
};
|
|
82
|
-
|
|
83
80
|
export default GenericAppBarSubMenu;
|
|
@@ -17,6 +17,7 @@ const defaultState = {
|
|
|
17
17
|
},
|
|
18
18
|
mainMenu: []
|
|
19
19
|
};
|
|
20
|
+
|
|
20
21
|
/**
|
|
21
22
|
* Generic App Bar component used to create dynamic top bar with dynamic menus.
|
|
22
23
|
*
|
|
@@ -86,13 +87,14 @@ Has three main part to it:
|
|
|
86
87
|
|
|
87
88
|
*
|
|
88
89
|
*/
|
|
89
|
-
|
|
90
90
|
export const GenericAppBar = props => {
|
|
91
91
|
const {
|
|
92
92
|
appBarMenu,
|
|
93
93
|
theme
|
|
94
|
-
} = {
|
|
95
|
-
|
|
94
|
+
} = {
|
|
95
|
+
...props,
|
|
96
|
+
appBarMenu: {
|
|
97
|
+
...defaultState,
|
|
96
98
|
...props.appBarMenu
|
|
97
99
|
}
|
|
98
100
|
};
|
|
@@ -138,36 +140,35 @@ export const GenericAppBar = props => {
|
|
|
138
140
|
paddingRight: 6
|
|
139
141
|
}
|
|
140
142
|
}));
|
|
141
|
-
const classes = useStyles();
|
|
143
|
+
const classes = useStyles();
|
|
144
|
+
|
|
145
|
+
//---------------------------------------------------
|
|
142
146
|
// Hooks
|
|
143
147
|
//---------------------------------------------------
|
|
148
|
+
const [mobileMoreAnchorEl, setmobileMoreAnchorEl] = useState(null);
|
|
144
149
|
|
|
145
|
-
|
|
150
|
+
//---------------------------------------------------
|
|
146
151
|
// Use Effect
|
|
147
152
|
//---------------------------------------------------
|
|
153
|
+
|
|
148
154
|
//---------------------------------------------------
|
|
149
155
|
// Local Functions
|
|
150
156
|
//---------------------------------------------------
|
|
151
|
-
|
|
152
157
|
const handleMobileMenuOpen = event => {
|
|
153
158
|
setmobileMoreAnchorEl(event.currentTarget);
|
|
154
159
|
};
|
|
155
|
-
|
|
156
160
|
const handleMobileMenuClose = () => {
|
|
157
161
|
setmobileMoreAnchorEl(null);
|
|
158
162
|
};
|
|
159
|
-
|
|
160
163
|
const handleMenuClose = () => {
|
|
161
164
|
handleMobileMenuClose();
|
|
162
165
|
};
|
|
163
|
-
|
|
164
166
|
const menu_divider = /*#__PURE__*/React.createElement(Typography, {
|
|
165
167
|
className: classes.divider
|
|
166
168
|
});
|
|
167
169
|
const mobile_menu_divider = /*#__PURE__*/React.createElement(Typography, {
|
|
168
170
|
className: classes.mobile_divider
|
|
169
171
|
});
|
|
170
|
-
|
|
171
172
|
const CloseContext = props => {
|
|
172
173
|
const {
|
|
173
174
|
onClick,
|
|
@@ -182,17 +183,17 @@ export const GenericAppBar = props => {
|
|
|
182
183
|
"aria-label": "Menu"
|
|
183
184
|
}, /*#__PURE__*/React.createElement(ArrowBackIcon, null)));
|
|
184
185
|
};
|
|
185
|
-
|
|
186
186
|
const TitleContext = () => /*#__PURE__*/React.createElement(Typography, {
|
|
187
187
|
variant: "h6",
|
|
188
188
|
color: "inherit"
|
|
189
189
|
}, /*#__PURE__*/React.createElement("div", {
|
|
190
190
|
className: "flex flex-row"
|
|
191
|
-
}, appBarMenu.appBarTitle));
|
|
191
|
+
}, appBarMenu.appBarTitle));
|
|
192
|
+
|
|
193
|
+
//? ------------------------------------------------
|
|
192
194
|
//! Menu Items
|
|
193
195
|
//? ------------------------------------------------
|
|
194
196
|
|
|
195
|
-
|
|
196
197
|
const MobileItemMenu = ({
|
|
197
198
|
menu,
|
|
198
199
|
idx
|
|
@@ -222,7 +223,6 @@ export const GenericAppBar = props => {
|
|
|
222
223
|
}, /*#__PURE__*/React.createElement(IconButton, {
|
|
223
224
|
color: "inherit"
|
|
224
225
|
}, menu.icon), /*#__PURE__*/React.createElement("p", null, menu.name)), menu.displayDivider ? mobile_menu_divider : undefined)));
|
|
225
|
-
|
|
226
226
|
const DesktopItemMenu = ({
|
|
227
227
|
menu,
|
|
228
228
|
idx
|
|
@@ -259,11 +259,12 @@ export const GenericAppBar = props => {
|
|
|
259
259
|
onClick: () => {
|
|
260
260
|
menu.onClick();
|
|
261
261
|
}
|
|
262
|
-
}, menu.icon)))));
|
|
262
|
+
}, menu.icon)))));
|
|
263
|
+
|
|
264
|
+
//? ------------------------------------------------
|
|
263
265
|
//! Mobile menu
|
|
264
266
|
//? ------------------------------------------------
|
|
265
267
|
|
|
266
|
-
|
|
267
268
|
const renderMobileMenu = /*#__PURE__*/React.createElement(Menu, {
|
|
268
269
|
anchorEl: mobileMoreAnchorEl,
|
|
269
270
|
anchorOrigin: {
|
|
@@ -284,10 +285,11 @@ export const GenericAppBar = props => {
|
|
|
284
285
|
key: `mobile_item_menu_${idx}`,
|
|
285
286
|
menu: menu,
|
|
286
287
|
idx: idx
|
|
287
|
-
})), /*#__PURE__*/React.createElement("div", null, mobile_menu_divider))));
|
|
288
|
+
})), /*#__PURE__*/React.createElement("div", null, mobile_menu_divider))));
|
|
289
|
+
|
|
290
|
+
//? ------------------------------------------------
|
|
288
291
|
//! Main Menu Items
|
|
289
292
|
//? ------------------------------------------------
|
|
290
|
-
|
|
291
293
|
const MainItems = () => {
|
|
292
294
|
const menuItems = (appBarMenu.mainMenu || []).filter(mn => !(mn.hideMenu && mn.hideMenu === true));
|
|
293
295
|
return /*#__PURE__*/React.createElement("div", {
|
|
@@ -309,7 +311,6 @@ export const GenericAppBar = props => {
|
|
|
309
311
|
}
|
|
310
312
|
})))));
|
|
311
313
|
};
|
|
312
|
-
|
|
313
314
|
return /*#__PURE__*/React.createElement(ThemeProvider, {
|
|
314
315
|
theme: defaultTheme
|
|
315
316
|
}, /*#__PURE__*/React.createElement("div", {
|
|
@@ -331,17 +332,18 @@ export const GenericAppBar = props => {
|
|
|
331
332
|
color: "inherit"
|
|
332
333
|
}, /*#__PURE__*/React.createElement(MoreIcon, null)))))), renderMobileMenu));
|
|
333
334
|
};
|
|
334
|
-
export default GenericAppBar;
|
|
335
|
+
export default GenericAppBar;
|
|
335
336
|
|
|
337
|
+
// Specifies the default values for the props:
|
|
336
338
|
GenericAppBar.defaultProps = {
|
|
337
339
|
appBarMenu: defaultState,
|
|
338
340
|
theme: undefined
|
|
339
|
-
};
|
|
341
|
+
};
|
|
340
342
|
|
|
343
|
+
//Define the types associated with the prop types
|
|
341
344
|
GenericAppBar.propTypes = {
|
|
342
345
|
/** The menu structure used to control the menu: */
|
|
343
346
|
appBarMenu: PropTypes.object.isRequired,
|
|
344
|
-
|
|
345
347
|
/** Theme object to use on this component, if none provided then the MUI5 theme provider theme is used*/
|
|
346
348
|
theme: PropTypes.object
|
|
347
349
|
};
|
|
@@ -22,17 +22,17 @@ export const GenericDialogWindow = props => {
|
|
|
22
22
|
theme
|
|
23
23
|
} = props;
|
|
24
24
|
const compTheme = theme ? theme : useTheme();
|
|
25
|
-
const defaultTheme = createTheme(compTheme);
|
|
25
|
+
const defaultTheme = createTheme(compTheme);
|
|
26
|
+
|
|
27
|
+
//---------------------------------------------------
|
|
26
28
|
// Hooks
|
|
27
29
|
//---------------------------------------------------
|
|
28
|
-
|
|
29
30
|
const [canSave, setCanSave] = useState(false);
|
|
30
31
|
const [isLoading, setIsLoading] = useState(false);
|
|
31
32
|
const [managedAppBarMenu, setManagedAppBarMenu] = useState(undefined);
|
|
32
33
|
const [save_button_label, setSaveButtonLabel] = useState(undefined);
|
|
33
34
|
const [onClickOverride, setOnClickOverride] = useState([]);
|
|
34
35
|
const childRef = useRef();
|
|
35
|
-
|
|
36
36
|
const onSave = () => {
|
|
37
37
|
clearTimeout(debounce);
|
|
38
38
|
debounce = setTimeout(() => {
|
|
@@ -40,7 +40,6 @@ export const GenericDialogWindow = props => {
|
|
|
40
40
|
childRef.current ? childRef.current.childRefSave() : undefined;
|
|
41
41
|
}, 300);
|
|
42
42
|
};
|
|
43
|
-
|
|
44
43
|
const closeDialog = () => {
|
|
45
44
|
//returns the onClick function assigned to the appBarMenu.closeMenu.onClick
|
|
46
45
|
try {
|
|
@@ -49,28 +48,24 @@ export const GenericDialogWindow = props => {
|
|
|
49
48
|
console.error('Error no onClose function found in the appBarMenu.closeMenu, Please ensure the dialog has a valid closeMenu with a close function assigned to the onClick', error);
|
|
50
49
|
}
|
|
51
50
|
};
|
|
52
|
-
|
|
53
51
|
const getSaveButtonLabel = () => {
|
|
54
|
-
let return_value = 'Save';
|
|
52
|
+
let return_value = 'Save';
|
|
53
|
+
//returns the save label for the save button in the bottom right of the dialog.
|
|
55
54
|
// The save label is taken from the mainMenu name item having an id='save'
|
|
56
|
-
|
|
57
55
|
try {
|
|
58
56
|
const save_menu = appBarMenu.mainMenu.find(mm => mm.id === 'save');
|
|
59
|
-
|
|
60
57
|
if (save_menu && save_menu.name) {
|
|
61
58
|
return_value = save_menu.name;
|
|
62
59
|
}
|
|
63
60
|
} catch (error) {}
|
|
64
|
-
|
|
65
61
|
return return_value;
|
|
66
|
-
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
//?-------------------------------------------------------
|
|
67
65
|
//? Children on click callback override, updates the onclick for a given menu
|
|
68
66
|
//?-------------------------------------------------------
|
|
69
|
-
|
|
70
|
-
|
|
71
67
|
const updateObjectByID = (obj, id, onClick) => {
|
|
72
68
|
if (!obj) return;
|
|
73
|
-
|
|
74
69
|
if (obj.id && obj.id === id) {
|
|
75
70
|
obj.onClick = onClick;
|
|
76
71
|
} else if (typeof obj === 'object' && Array.isArray(obj)) {
|
|
@@ -80,10 +75,8 @@ export const GenericDialogWindow = props => {
|
|
|
80
75
|
} else if (obj.subMenu) {
|
|
81
76
|
updateObjectByID(obj.subMenu, id, onClick);
|
|
82
77
|
}
|
|
83
|
-
|
|
84
78
|
return obj;
|
|
85
79
|
};
|
|
86
|
-
|
|
87
80
|
const setMenuOnClickCallback = (id, onClick) => {
|
|
88
81
|
//Used to set the onClick callback on a specific menu matching the id provided.
|
|
89
82
|
//Example: Used by the children component to set the onClick of a menu.
|
|
@@ -96,14 +89,15 @@ export const GenericDialogWindow = props => {
|
|
|
96
89
|
}];
|
|
97
90
|
});
|
|
98
91
|
};
|
|
99
|
-
|
|
100
92
|
const setOverrides = () => {
|
|
101
93
|
try {
|
|
102
94
|
//Loop over all our on click overrides and apply the onClick callback for each id found in the onClickOverride state
|
|
95
|
+
|
|
103
96
|
onClickOverride.forEach(item => {
|
|
104
97
|
if (item && item.id) {
|
|
105
98
|
setManagedAppBarMenu(previousState => {
|
|
106
|
-
return {
|
|
99
|
+
return {
|
|
100
|
+
...previousState,
|
|
107
101
|
mainMenu: updateObjectByID(previousState.mainMenu, item.id, item.onClick)
|
|
108
102
|
};
|
|
109
103
|
});
|
|
@@ -113,17 +107,18 @@ export const GenericDialogWindow = props => {
|
|
|
113
107
|
console.error('Error in setMenuOnClickCallback: ', error);
|
|
114
108
|
}
|
|
115
109
|
};
|
|
116
|
-
|
|
117
110
|
useEffect(() => {
|
|
118
111
|
//Wait for the state to be updated then loop over the overrides to ensure they are properly applied
|
|
119
112
|
setOverrides();
|
|
120
113
|
}, [onClickOverride]);
|
|
121
114
|
useEffect(() => {
|
|
122
115
|
if (appBarMenu && appBarMenu.mainMenu && appBarMenu.mainMenu.length > 0) {
|
|
123
|
-
setManagedAppBarMenu({
|
|
116
|
+
setManagedAppBarMenu({
|
|
117
|
+
...appBarMenu,
|
|
124
118
|
mainMenu: [...appBarMenu.mainMenu.map(mm => {
|
|
125
119
|
if (mm.id && mm.id === 'save') {
|
|
126
|
-
return {
|
|
120
|
+
return {
|
|
121
|
+
...mm,
|
|
127
122
|
onClick: onSave,
|
|
128
123
|
name: save_button_label ? save_button_label : mm.name,
|
|
129
124
|
hideMenu: !(canSave && showSaveButton)
|
|
@@ -132,23 +127,23 @@ export const GenericDialogWindow = props => {
|
|
|
132
127
|
return mm;
|
|
133
128
|
}
|
|
134
129
|
})]
|
|
135
|
-
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// If we have a mainMenu item with the id='save' then override the onClick event to use the onSave function which reaches into the child ref
|
|
136
133
|
//Need to re-apply any onClick override to ensure they are still properly set by what was overridden from the child component.
|
|
137
134
|
// setOverrides();
|
|
138
135
|
}
|
|
139
136
|
}, [appBarMenu, canSave, showSaveButton, save_button_label]);
|
|
140
|
-
|
|
141
137
|
const handleOnClose = (event, reason) => {
|
|
142
138
|
if (reason !== 'backdropClick' || closeOnBackdropClick) {
|
|
143
139
|
closeDialog();
|
|
144
140
|
}
|
|
145
|
-
};
|
|
141
|
+
};
|
|
142
|
+
// -----------------------------------------
|
|
146
143
|
// Note make sure to include disableEnforceFocus or else I'll have infinite error/warnings on Uncaught RangeError.
|
|
147
144
|
// I think this error comes only after a state has changed when multiple dialog windows are open they are fighting for focus.
|
|
148
145
|
// Bug: https://github.com/mui/material-ui/issues/8741
|
|
149
146
|
// -----------------------------------------
|
|
150
|
-
|
|
151
|
-
|
|
152
147
|
return /*#__PURE__*/React.createElement(ThemeProvider, {
|
|
153
148
|
theme: defaultTheme
|
|
154
149
|
}, /*#__PURE__*/React.createElement(Dialog, {
|
|
@@ -210,31 +205,22 @@ GenericDialogWindow.defaultProps = {
|
|
|
210
205
|
GenericDialogWindow.propTypes = {
|
|
211
206
|
/** Controls weather to open or close the dialog */
|
|
212
207
|
open: PropTypes.bool,
|
|
213
|
-
|
|
214
208
|
/** Any React component to be displayed inside of the dialog body */
|
|
215
209
|
component: PropTypes.element,
|
|
216
|
-
|
|
217
210
|
/** Any data needing to be passed down to the child component */
|
|
218
211
|
data: PropTypes.any,
|
|
219
|
-
|
|
220
212
|
/** To display or hide the save button on the dialog */
|
|
221
213
|
showSaveButton: PropTypes.bool,
|
|
222
|
-
|
|
223
214
|
/** To display or hide the back button in the top left corner */
|
|
224
215
|
showBackButton: PropTypes.bool,
|
|
225
|
-
|
|
226
216
|
/** Same props as the GenericAppBar component... */
|
|
227
217
|
appBarMenu: PropTypes.object,
|
|
228
|
-
|
|
229
218
|
/** Determine the max-width of the dialog. The dialog width grows with the size of the screen. Set to false to disable maxWidth.*/
|
|
230
219
|
dialogSize: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl', false]),
|
|
231
|
-
|
|
232
220
|
/**If true, the dialog stretches to maxWidth. Notice that the dialog width grow is limited by the default margin. */
|
|
233
221
|
fullWidth: PropTypes.bool,
|
|
234
|
-
|
|
235
222
|
/**Determines if the dialog window will close when the user clicks on the backdrop area */
|
|
236
223
|
closeOnBackdropClick: PropTypes.bool,
|
|
237
|
-
|
|
238
224
|
/** Theme object to use on this component, if none provided then the MUI5 theme provider theme is used*/
|
|
239
225
|
theme: PropTypes.object
|
|
240
226
|
};
|
|
@@ -1,59 +1,59 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react';
|
|
2
|
-
import tinycolor from "tinycolor2";
|
|
2
|
+
import tinycolor from "tinycolor2";
|
|
3
|
+
// Brush colour and size
|
|
4
|
+
|
|
3
5
|
// // Geometry
|
|
6
|
+
|
|
4
7
|
// // Drawing functions
|
|
8
|
+
|
|
5
9
|
// // Event helpers
|
|
10
|
+
|
|
6
11
|
// // Event handlers
|
|
7
12
|
|
|
8
13
|
export const CanvasPain = () => {
|
|
9
14
|
const canvas = useRef();
|
|
10
|
-
let context;
|
|
15
|
+
let context;
|
|
16
|
+
|
|
17
|
+
// // Set up our drawing context
|
|
11
18
|
// // const canvas = document.getElementById("canvas");
|
|
19
|
+
|
|
12
20
|
// canvas.current.width = window.innerWidth;
|
|
13
21
|
// canvas.current.height = window.innerHeight - 10;
|
|
14
|
-
|
|
15
22
|
let colour;
|
|
16
23
|
const strokeWidth = 40;
|
|
17
|
-
const varyBrightness = 5;
|
|
24
|
+
const varyBrightness = 5;
|
|
18
25
|
|
|
26
|
+
// Drawing state
|
|
19
27
|
let latestPoint;
|
|
20
28
|
let drawing = false;
|
|
21
29
|
let currentAngle;
|
|
22
|
-
|
|
23
30
|
const mouseDown = evt => {
|
|
24
31
|
if (drawing) {
|
|
25
32
|
return;
|
|
26
33
|
}
|
|
27
|
-
|
|
28
34
|
evt.preventDefault();
|
|
29
35
|
canvas.current.addEventListener("mousemove", mouseMove, false);
|
|
30
36
|
startStroke([evt.offsetX, evt.offsetY]);
|
|
31
37
|
};
|
|
32
|
-
|
|
33
38
|
const mouseEnter = evt => {
|
|
34
39
|
if (!mouseButtonIsDown(evt.buttons) || drawing) {
|
|
35
40
|
return;
|
|
36
41
|
}
|
|
37
|
-
|
|
38
42
|
mouseDown(evt);
|
|
39
43
|
};
|
|
40
|
-
|
|
41
44
|
const endStroke = evt => {
|
|
42
45
|
if (!drawing) {
|
|
43
46
|
return;
|
|
44
47
|
}
|
|
45
|
-
|
|
46
48
|
drawing = false;
|
|
47
49
|
evt.currentTarget.removeEventListener("mousemove", mouseMove, false);
|
|
48
50
|
};
|
|
49
|
-
|
|
50
51
|
const continueStroke = newPoint => {
|
|
51
52
|
const newAngle = getNewAngle(latestPoint, newPoint, currentAngle);
|
|
52
53
|
drawStroke(currentBrush, latestPoint, newPoint, currentAngle, newAngle);
|
|
53
54
|
currentAngle = newAngle % (Math.PI * 2);
|
|
54
55
|
latestPoint = newPoint;
|
|
55
56
|
};
|
|
56
|
-
|
|
57
57
|
const startStroke = point => {
|
|
58
58
|
colour = document.getElementById("colourInput").value;
|
|
59
59
|
currentAngle = undefined;
|
|
@@ -61,48 +61,38 @@ export const CanvasPain = () => {
|
|
|
61
61
|
drawing = true;
|
|
62
62
|
latestPoint = point;
|
|
63
63
|
};
|
|
64
|
-
|
|
65
64
|
const touchStart = evt => {
|
|
66
65
|
if (drawing) {
|
|
67
66
|
return;
|
|
68
67
|
}
|
|
69
|
-
|
|
70
68
|
evt.preventDefault();
|
|
71
69
|
startStroke(getTouchPoint(evt));
|
|
72
70
|
};
|
|
73
|
-
|
|
74
71
|
const mouseMove = evt => {
|
|
75
72
|
if (!drawing) {
|
|
76
73
|
return;
|
|
77
74
|
}
|
|
78
|
-
|
|
79
75
|
continueStroke([evt.offsetX, evt.offsetY]);
|
|
80
76
|
};
|
|
81
|
-
|
|
82
77
|
const touchMove = evt => {
|
|
83
78
|
if (!drawing) {
|
|
84
79
|
return;
|
|
85
80
|
}
|
|
86
|
-
|
|
87
81
|
continueStroke(getTouchPoint(evt));
|
|
88
82
|
};
|
|
89
|
-
|
|
90
83
|
const touchEnd = evt => {
|
|
91
84
|
drawing = false;
|
|
92
85
|
};
|
|
93
|
-
|
|
94
86
|
const varyColour = sourceColour => {
|
|
95
87
|
const amount = Math.round(Math.random() * 2 * varyBrightness);
|
|
96
88
|
const c = tinycolor(sourceColour);
|
|
97
89
|
const varied = amount > varyBrightness ? c.brighten(amount - varyBrightness) : c.darken(amount);
|
|
98
90
|
return varied.toHexString();
|
|
99
91
|
};
|
|
100
|
-
|
|
101
92
|
const makeBrush = size => {
|
|
102
93
|
const brush = [];
|
|
103
94
|
let bristleCount = Math.round(size / 3);
|
|
104
95
|
const gap = strokeWidth / bristleCount;
|
|
105
|
-
|
|
106
96
|
for (let i = 0; i < bristleCount; i++) {
|
|
107
97
|
const distance = i === 0 ? 0 : gap * i + Math.random() * gap / 2 - gap / 2;
|
|
108
98
|
brush.push({
|
|
@@ -111,33 +101,24 @@ export const CanvasPain = () => {
|
|
|
111
101
|
colour: varyColour(colour)
|
|
112
102
|
});
|
|
113
103
|
}
|
|
114
|
-
|
|
115
104
|
return brush;
|
|
116
105
|
};
|
|
117
|
-
|
|
118
106
|
let currentBrush = makeBrush(strokeWidth);
|
|
119
|
-
|
|
120
107
|
const getNewAngle = (origin, destination, oldAngle) => {
|
|
121
108
|
const bearing = getBearing(origin, destination);
|
|
122
|
-
|
|
123
109
|
if (typeof oldAngle === "undefined") {
|
|
124
110
|
console.log(bearing);
|
|
125
111
|
return bearing;
|
|
126
112
|
}
|
|
127
|
-
|
|
128
113
|
return oldAngle - angleDiff(oldAngle, bearing);
|
|
129
114
|
};
|
|
130
|
-
|
|
131
115
|
const rotatePoint = (distance, angle, origin) => [origin[0] + distance * Math.cos(angle), origin[1] + distance * Math.sin(angle)];
|
|
132
|
-
|
|
133
116
|
const getBearing = (origin, destination) => (Math.atan2(destination[1] - origin[1], destination[0] - origin[0]) - Math.PI / 2) % (Math.PI * 2);
|
|
134
|
-
|
|
135
117
|
const angleDiff = (angleA, angleB) => {
|
|
136
118
|
const twoPi = Math.PI * 2;
|
|
137
119
|
const diff = (angleA - (angleB > 0 ? angleB : angleB + twoPi) + Math.PI) % twoPi - Math.PI;
|
|
138
120
|
return diff < -Math.PI ? diff + twoPi : diff;
|
|
139
121
|
};
|
|
140
|
-
|
|
141
122
|
const drawStroke = (bristles, origin, destination, oldAngle, newAngle) => {
|
|
142
123
|
bristles.forEach(bristle => {
|
|
143
124
|
context.beginPath();
|
|
@@ -148,7 +129,6 @@ export const CanvasPain = () => {
|
|
|
148
129
|
strokeBristle(bristleOrigin, bristleDestination, bristle, controlPoint);
|
|
149
130
|
});
|
|
150
131
|
};
|
|
151
|
-
|
|
152
132
|
const strokeBristle = (origin, destination, bristle, controlPoint) => {
|
|
153
133
|
context.beginPath();
|
|
154
134
|
context.moveTo(origin[0], origin[1]);
|
|
@@ -162,22 +142,17 @@ export const CanvasPain = () => {
|
|
|
162
142
|
context.lineTo(destination[0], destination[1]);
|
|
163
143
|
context.stroke();
|
|
164
144
|
};
|
|
165
|
-
|
|
166
145
|
const getTouchPoint = evt => {
|
|
167
146
|
if (!evt.currentTarget) {
|
|
168
147
|
return [0, 0];
|
|
169
148
|
}
|
|
170
|
-
|
|
171
149
|
const rect = evt.currentTarget.getBoundingClientRect();
|
|
172
150
|
const touch = evt.targetTouches[0];
|
|
173
151
|
console.log(rect, touch);
|
|
174
152
|
return [touch.clientX - rect.left, touch.clientY - rect.top];
|
|
175
153
|
};
|
|
176
|
-
|
|
177
154
|
const BUTTON = 0b01;
|
|
178
|
-
|
|
179
155
|
const mouseButtonIsDown = buttons => (BUTTON & buttons) === BUTTON;
|
|
180
|
-
|
|
181
156
|
useEffect(() => {
|
|
182
157
|
// Register event handlers
|
|
183
158
|
canvas.current.addEventListener("touchstart", touchStart, false);
|
|
@@ -41,11 +41,9 @@ export const toBase64 = async blob => {
|
|
|
41
41
|
console.error('Error in toBase64 function of hte ImageCanvasDraw component: ', error);
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
|
-
|
|
45
44
|
function valueLabelFormat(value) {
|
|
46
45
|
return imageScaleMarks.findIndex(mark => mark.value === value) + 0.5;
|
|
47
46
|
}
|
|
48
|
-
|
|
49
47
|
const exportImage = async (thisFunction, saveableCanvas, outputFormat, imageCompressionOptions) => {
|
|
50
48
|
try {
|
|
51
49
|
const {
|
|
@@ -69,7 +67,6 @@ const exportImage = async (thisFunction, saveableCanvas, outputFormat, imageComp
|
|
|
69
67
|
console.log('error in exportImage in ImageCanvasDraw comp: ', error);
|
|
70
68
|
}
|
|
71
69
|
};
|
|
72
|
-
|
|
73
70
|
const Controls = props => {
|
|
74
71
|
const {
|
|
75
72
|
state,
|
|
@@ -89,7 +86,8 @@ const Controls = props => {
|
|
|
89
86
|
color: state.color,
|
|
90
87
|
onChange: (color, event) => {
|
|
91
88
|
setState(previousState => {
|
|
92
|
-
return {
|
|
89
|
+
return {
|
|
90
|
+
...previousState,
|
|
93
91
|
color: rgbToHex(color.rgb)
|
|
94
92
|
};
|
|
95
93
|
});
|
|
@@ -108,7 +106,8 @@ const Controls = props => {
|
|
|
108
106
|
value: state.lazyRadius,
|
|
109
107
|
onChange: event => {
|
|
110
108
|
setState(previousState => {
|
|
111
|
-
return {
|
|
109
|
+
return {
|
|
110
|
+
...previousState,
|
|
112
111
|
lazyRadius: Number(event.target.value)
|
|
113
112
|
};
|
|
114
113
|
});
|
|
@@ -130,7 +129,8 @@ const Controls = props => {
|
|
|
130
129
|
value: state.brushRadius,
|
|
131
130
|
onChange: event => {
|
|
132
131
|
setState(previousState => {
|
|
133
|
-
return {
|
|
132
|
+
return {
|
|
133
|
+
...previousState,
|
|
134
134
|
brushRadius: Number(event.target.value)
|
|
135
135
|
};
|
|
136
136
|
});
|
|
@@ -149,16 +149,20 @@ const Controls = props => {
|
|
|
149
149
|
defaultValue: 1.5,
|
|
150
150
|
valueLabelFormat: valueLabelFormat,
|
|
151
151
|
"aria-labelledby": "image-scale",
|
|
152
|
-
step: null
|
|
152
|
+
step: null
|
|
153
|
+
// valueLabelDisplay="auto"
|
|
153
154
|
,
|
|
154
155
|
value: state.imageScale,
|
|
155
156
|
onChange: event => {
|
|
156
157
|
setState(previousState => {
|
|
157
|
-
return {
|
|
158
|
+
return {
|
|
159
|
+
...previousState,
|
|
158
160
|
imageScale: Number(event.target.value)
|
|
159
161
|
};
|
|
160
|
-
});
|
|
162
|
+
});
|
|
163
|
+
// saveableCanvas.current.eraseAll();
|
|
161
164
|
},
|
|
165
|
+
|
|
162
166
|
marks: imageScaleMarks,
|
|
163
167
|
min: 1.5,
|
|
164
168
|
max: 2.5
|
|
@@ -188,5 +192,4 @@ const Controls = props => {
|
|
|
188
192
|
}
|
|
189
193
|
}, "Download")));
|
|
190
194
|
};
|
|
191
|
-
|
|
192
195
|
export default Controls;
|