@pie-element/hotspot 11.1.2-next.5 → 11.1.3

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.
Files changed (234) hide show
  1. package/CHANGELOG.json +997 -0
  2. package/CHANGELOG.md +2226 -0
  3. package/LICENSE.md +5 -0
  4. package/README.md +1 -0
  5. package/configure/CHANGELOG.json +682 -0
  6. package/configure/CHANGELOG.md +1963 -0
  7. package/configure/lib/DeleteWidget.js +64 -0
  8. package/configure/lib/DeleteWidget.js.map +1 -0
  9. package/configure/lib/button.js +42 -0
  10. package/configure/lib/button.js.map +1 -0
  11. package/configure/lib/buttons/circle.js +33 -0
  12. package/configure/lib/buttons/circle.js.map +1 -0
  13. package/configure/lib/buttons/polygon.js +39 -0
  14. package/configure/lib/buttons/polygon.js.map +1 -0
  15. package/configure/lib/buttons/rectangle.js +39 -0
  16. package/configure/lib/buttons/rectangle.js.map +1 -0
  17. package/configure/lib/defaults.js +155 -0
  18. package/configure/lib/defaults.js.map +1 -0
  19. package/configure/lib/hotspot-circle.js +192 -0
  20. package/configure/lib/hotspot-circle.js.map +1 -0
  21. package/configure/lib/hotspot-container.js +320 -0
  22. package/configure/lib/hotspot-container.js.map +1 -0
  23. package/configure/lib/hotspot-drawable.js +519 -0
  24. package/configure/lib/hotspot-drawable.js.map +1 -0
  25. package/configure/lib/hotspot-palette.js +107 -0
  26. package/configure/lib/hotspot-palette.js.map +1 -0
  27. package/configure/lib/hotspot-polygon.js +293 -0
  28. package/configure/lib/hotspot-polygon.js.map +1 -0
  29. package/configure/lib/hotspot-rectangle.js +190 -0
  30. package/configure/lib/hotspot-rectangle.js.map +1 -0
  31. package/configure/lib/icons.js +7 -0
  32. package/configure/lib/icons.js.map +1 -0
  33. package/configure/lib/image-konva.js +66 -0
  34. package/configure/lib/image-konva.js.map +1 -0
  35. package/configure/lib/index.js +194 -0
  36. package/configure/lib/index.js.map +1 -0
  37. package/configure/lib/root.js +330 -0
  38. package/configure/lib/root.js.map +1 -0
  39. package/configure/lib/shapes/circle.js +84 -0
  40. package/configure/lib/shapes/circle.js.map +1 -0
  41. package/configure/lib/shapes/index.js +50 -0
  42. package/configure/lib/shapes/index.js.map +1 -0
  43. package/configure/lib/shapes/polygon.js +82 -0
  44. package/configure/lib/shapes/polygon.js.map +1 -0
  45. package/configure/lib/shapes/rectagle.js +84 -0
  46. package/configure/lib/shapes/rectagle.js.map +1 -0
  47. package/configure/lib/shapes/utils.js +21 -0
  48. package/configure/lib/shapes/utils.js.map +1 -0
  49. package/configure/lib/upload-control.js +41 -0
  50. package/configure/lib/upload-control.js.map +1 -0
  51. package/configure/lib/utils.js +185 -0
  52. package/configure/lib/utils.js.map +1 -0
  53. package/configure/package.json +26 -0
  54. package/configure/src/DeleteWidget.jsx +51 -0
  55. package/configure/src/__tests__/DeleteWidget.test.jsx +366 -0
  56. package/configure/src/__tests__/button.test.jsx +198 -0
  57. package/configure/src/__tests__/hotspot-circle.test.jsx +259 -0
  58. package/configure/src/__tests__/hotspot-container.test.js +366 -0
  59. package/configure/src/__tests__/hotspot-drawable.test.js +271 -0
  60. package/configure/src/__tests__/hotspot-palette.test.jsx +71 -0
  61. package/configure/src/__tests__/image-konva.test.jsx +226 -0
  62. package/configure/src/__tests__/index.test.js +329 -0
  63. package/configure/src/__tests__/root.test.js +400 -0
  64. package/configure/src/__tests__/utils.test.js +241 -0
  65. package/configure/src/button.jsx +35 -0
  66. package/configure/src/buttons/circle.jsx +18 -0
  67. package/configure/src/buttons/polygon.jsx +29 -0
  68. package/configure/src/buttons/rectangle.jsx +29 -0
  69. package/configure/src/defaults.js +109 -0
  70. package/configure/src/hotspot-circle.jsx +183 -0
  71. package/configure/src/hotspot-container.jsx +330 -0
  72. package/configure/src/hotspot-drawable.jsx +527 -0
  73. package/configure/src/hotspot-palette.jsx +90 -0
  74. package/configure/src/hotspot-polygon.jsx +294 -0
  75. package/configure/src/hotspot-rectangle.jsx +169 -0
  76. package/configure/src/icons.js +5 -0
  77. package/configure/src/image-konva.jsx +63 -0
  78. package/configure/src/index.js +208 -0
  79. package/configure/src/root.jsx +346 -0
  80. package/configure/src/shapes/circle.js +81 -0
  81. package/configure/src/shapes/index.js +4 -0
  82. package/configure/src/shapes/polygon.js +81 -0
  83. package/configure/src/shapes/rectagle.js +82 -0
  84. package/configure/src/shapes/utils.js +16 -0
  85. package/configure/src/upload-control.jsx +33 -0
  86. package/configure/src/utils.js +210 -0
  87. package/controller/CHANGELOG.json +362 -0
  88. package/controller/CHANGELOG.md +1304 -0
  89. package/controller/lib/defaults.js +33 -0
  90. package/controller/lib/defaults.js.map +1 -0
  91. package/controller/lib/index.js +341 -0
  92. package/controller/lib/index.js.map +1 -0
  93. package/controller/lib/utils.js +32 -0
  94. package/controller/lib/utils.js.map +1 -0
  95. package/controller/package.json +18 -0
  96. package/controller/src/__tests__/index.test.js +419 -0
  97. package/controller/src/__tests__/utils.test.js +5 -0
  98. package/controller/src/defaults.js +19 -0
  99. package/controller/src/index.js +328 -0
  100. package/controller/src/utils.js +29 -0
  101. package/docs/config-schema.json +2023 -0
  102. package/docs/config-schema.json.md +1495 -0
  103. package/docs/demo/config.js +8 -0
  104. package/docs/demo/generate.js +118 -0
  105. package/docs/demo/index.html +1 -0
  106. package/docs/demo/session.js +11 -0
  107. package/docs/pie-schema.json +1204 -0
  108. package/docs/pie-schema.json.md +851 -0
  109. package/lib/hotspot/circle.js +156 -0
  110. package/lib/hotspot/circle.js.map +1 -0
  111. package/lib/hotspot/container.js +206 -0
  112. package/lib/hotspot/container.js.map +1 -0
  113. package/lib/hotspot/icons.js +8 -0
  114. package/lib/hotspot/icons.js.map +1 -0
  115. package/lib/hotspot/image-konva-tooltip.js +86 -0
  116. package/lib/hotspot/image-konva-tooltip.js.map +1 -0
  117. package/lib/hotspot/index.js +163 -0
  118. package/lib/hotspot/index.js.map +1 -0
  119. package/lib/hotspot/polygon.js +203 -0
  120. package/lib/hotspot/polygon.js.map +1 -0
  121. package/lib/hotspot/rectangle.js +175 -0
  122. package/lib/hotspot/rectangle.js.map +1 -0
  123. package/lib/index.js +213 -0
  124. package/lib/index.js.map +1 -0
  125. package/lib/session-updater.js +42 -0
  126. package/lib/session-updater.js.map +1 -0
  127. package/package.json +18 -83
  128. package/src/__tests__/container.test.jsx +58 -0
  129. package/src/__tests__/index.test.js +123 -0
  130. package/src/__tests__/session-updater.test.jsx +69 -0
  131. package/src/hotspot/__tests__/circle.test.jsx +464 -0
  132. package/src/hotspot/__tests__/container.test.jsx +546 -0
  133. package/src/hotspot/__tests__/image-konva-tooltip.test.jsx +510 -0
  134. package/src/hotspot/__tests__/polygon.test.jsx +502 -0
  135. package/src/hotspot/__tests__/rectangle.test.jsx +418 -0
  136. package/src/hotspot/circle.jsx +152 -0
  137. package/src/hotspot/container.jsx +217 -0
  138. package/src/hotspot/icons.js +7 -0
  139. package/src/hotspot/image-konva-tooltip.jsx +76 -0
  140. package/src/hotspot/index.jsx +165 -0
  141. package/src/hotspot/polygon.jsx +195 -0
  142. package/src/hotspot/rectangle.jsx +171 -0
  143. package/src/index.js +226 -0
  144. package/src/session-updater.js +29 -0
  145. package/configure.js +0 -2
  146. package/controller.js +0 -1
  147. package/dist/author/DeleteWidget.d.ts +0 -38
  148. package/dist/author/DeleteWidget.js +0 -46
  149. package/dist/author/button.d.ts +0 -31
  150. package/dist/author/button.js +0 -27
  151. package/dist/author/buttons/circle.d.ts +0 -18
  152. package/dist/author/buttons/circle.js +0 -25
  153. package/dist/author/buttons/polygon.d.ts +0 -18
  154. package/dist/author/buttons/polygon.js +0 -36
  155. package/dist/author/buttons/rectangle.d.ts +0 -18
  156. package/dist/author/buttons/rectangle.js +0 -36
  157. package/dist/author/defaults.d.ts +0 -157
  158. package/dist/author/defaults.js +0 -119
  159. package/dist/author/hotspot-circle.d.ts +0 -21
  160. package/dist/author/hotspot-circle.js +0 -124
  161. package/dist/author/hotspot-container.d.ts +0 -29
  162. package/dist/author/hotspot-container.js +0 -210
  163. package/dist/author/hotspot-drawable.d.ts +0 -31
  164. package/dist/author/hotspot-drawable.js +0 -312
  165. package/dist/author/hotspot-palette.d.ts +0 -14
  166. package/dist/author/hotspot-palette.js +0 -72
  167. package/dist/author/hotspot-polygon.d.ts +0 -38
  168. package/dist/author/hotspot-polygon.js +0 -200
  169. package/dist/author/hotspot-rectangle.d.ts +0 -20
  170. package/dist/author/hotspot-rectangle.js +0 -119
  171. package/dist/author/icons.d.ts +0 -9
  172. package/dist/author/icons.js +0 -4
  173. package/dist/author/image-konva.d.ts +0 -19
  174. package/dist/author/image-konva.js +0 -49
  175. package/dist/author/index.d.ts +0 -52
  176. package/dist/author/index.js +0 -143
  177. package/dist/author/root.d.ts +0 -15
  178. package/dist/author/root.js +0 -215
  179. package/dist/author/shapes/circle.d.ts +0 -18
  180. package/dist/author/shapes/circle.js +0 -47
  181. package/dist/author/shapes/index.d.ts +0 -12
  182. package/dist/author/shapes/polygon.d.ts +0 -19
  183. package/dist/author/shapes/polygon.js +0 -51
  184. package/dist/author/shapes/rectagle.d.ts +0 -18
  185. package/dist/author/shapes/rectagle.js +0 -57
  186. package/dist/author/shapes/utils.d.ts +0 -19
  187. package/dist/author/shapes/utils.js +0 -16
  188. package/dist/author/upload-control.d.ts +0 -29
  189. package/dist/author/upload-control.js +0 -28
  190. package/dist/author/utils.d.ts +0 -24
  191. package/dist/author/utils.js +0 -83
  192. package/dist/browser/ReactKonva-CFo7dxdy.js +0 -19336
  193. package/dist/browser/ReactKonva-CFo7dxdy.js.map +0 -1
  194. package/dist/browser/author/index.js +0 -47549
  195. package/dist/browser/author/index.js.map +0 -1
  196. package/dist/browser/browser-CfnAFove.js +0 -219
  197. package/dist/browser/browser-CfnAFove.js.map +0 -1
  198. package/dist/browser/controller/index.js +0 -198
  199. package/dist/browser/controller/index.js.map +0 -1
  200. package/dist/browser/delivery/index.js +0 -2460
  201. package/dist/browser/delivery/index.js.map +0 -1
  202. package/dist/browser/dist-CeB-1djc.js +0 -100
  203. package/dist/browser/dist-CeB-1djc.js.map +0 -1
  204. package/dist/browser/hotspot.css +0 -2
  205. package/dist/controller/defaults.d.ts +0 -35
  206. package/dist/controller/defaults.js +0 -29
  207. package/dist/controller/index.d.ts +0 -22
  208. package/dist/controller/index.js +0 -154
  209. package/dist/controller/utils.d.ts +0 -10
  210. package/dist/controller/utils.js +0 -12
  211. package/dist/delivery/hotspot/circle.d.ts +0 -19
  212. package/dist/delivery/hotspot/circle.js +0 -100
  213. package/dist/delivery/hotspot/container.d.ts +0 -16
  214. package/dist/delivery/hotspot/container.js +0 -150
  215. package/dist/delivery/hotspot/icons.d.ts +0 -10
  216. package/dist/delivery/hotspot/icons.js +0 -4
  217. package/dist/delivery/hotspot/image-konva-tooltip.d.ts +0 -19
  218. package/dist/delivery/hotspot/image-konva-tooltip.js +0 -66
  219. package/dist/delivery/hotspot/index.d.ts +0 -17
  220. package/dist/delivery/hotspot/index.js +0 -114
  221. package/dist/delivery/hotspot/polygon.d.ts +0 -21
  222. package/dist/delivery/hotspot/polygon.js +0 -108
  223. package/dist/delivery/hotspot/rectangle.d.ts +0 -19
  224. package/dist/delivery/hotspot/rectangle.js +0 -104
  225. package/dist/delivery/index.d.ts +0 -20
  226. package/dist/delivery/index.js +0 -107
  227. package/dist/delivery/session-updater.d.ts +0 -10
  228. package/dist/delivery/session-updater.js +0 -14
  229. package/dist/index.d.ts +0 -1
  230. package/dist/index.iife.d.ts +0 -8
  231. package/dist/index.iife.js +0 -169
  232. package/dist/index.js +0 -2
  233. package/dist/runtime-support.d.ts +0 -12
  234. package/dist/runtime-support.js +0 -12
@@ -0,0 +1,527 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Layer, Stage } from 'react-konva';
4
+ import { cloneDeep } from 'lodash-es';
5
+ import { styled } from '@mui/material/styles';
6
+
7
+ import Rectangle from './hotspot-rectangle';
8
+ import Polygon from './hotspot-polygon';
9
+ import Circle from './hotspot-circle';
10
+ import { getUpdatedShapes, updateImageDimensions } from './utils';
11
+ import { RectangleShape, CircleShape, PolygonShape, SUPPORTED_SHAPES, SHAPE_GROUPS } from './shapes';
12
+
13
+ const BaseContainer = styled('div')({
14
+ position: 'relative',
15
+ });
16
+
17
+ const ImageContainer = styled('div')({
18
+ position: 'relative',
19
+ width: 'fit-content',
20
+ });
21
+
22
+ const Image = styled('img')({
23
+ alignItems: 'center',
24
+ display: 'flex',
25
+ justifyContent: 'center',
26
+ });
27
+
28
+ const ResizeHandle = styled('div')({
29
+ borderBottom: '1px solid #727272',
30
+ borderRight: '1px solid #727272',
31
+ bottom: '-10px',
32
+ cursor: 'se-resize',
33
+ height: '10px',
34
+ position: 'absolute',
35
+ right: '-10px',
36
+ width: '10px',
37
+ });
38
+
39
+ const StyledStage = styled(Stage)({
40
+ left: 0,
41
+ top: 0,
42
+ position: 'absolute',
43
+ });
44
+
45
+ const IMAGE_MAX_WIDTH = 800;
46
+
47
+ export class Drawable extends React.Component {
48
+ static getDerivedStateFromProps(nextProps, prevState) {
49
+ // Update the state only if the props have changed
50
+ if (nextProps.shapes !== prevState.shapes) {
51
+ return { shapes: nextProps.shapes };
52
+ }
53
+
54
+ return null;
55
+ }
56
+
57
+ constructor(props) {
58
+ super(props);
59
+
60
+ this.state = {
61
+ isDrawing: false,
62
+ stateShapes: false,
63
+ resizing: false,
64
+ temporaryPolygon: null,
65
+ shapes: [],
66
+ dimensions: {
67
+ height: 0,
68
+ width: 0,
69
+ },
70
+ };
71
+ }
72
+
73
+ handleOnMouseDown = (e) => {
74
+ const { shapeType, onUpdateShapes, shapes } = this.props;
75
+ let newState, newShapeId;
76
+
77
+ // Ensure that the click originated from the expected element
78
+ if (e.target !== e.currentTarget) {
79
+ return;
80
+ }
81
+
82
+ if (!Object.values(SUPPORTED_SHAPES).includes(shapeType)) {
83
+ return;
84
+ }
85
+
86
+ switch (shapeType) {
87
+ case SUPPORTED_SHAPES.RECTANGLE:
88
+ newState = RectangleShape.create(shapes, e);
89
+ break;
90
+ case SUPPORTED_SHAPES.CIRCLE:
91
+ newState = CircleShape.create(shapes, e);
92
+ break;
93
+ case SUPPORTED_SHAPES.POLYGON:
94
+ newShapeId = this.state.isDrawingShapeId;
95
+
96
+ if (newShapeId) {
97
+ // If a polygon is in progress, add a new point
98
+ const shapes = this.addPolygonPoint(e);
99
+
100
+ newState = {
101
+ isDrawing: true,
102
+ isDrawingShapeId: newShapeId,
103
+ shapes: shapes,
104
+ };
105
+ } else {
106
+ // Else start a new one
107
+ newState = PolygonShape.create(shapes, e);
108
+ }
109
+ break;
110
+ default:
111
+ return;
112
+ }
113
+
114
+ this.setState({
115
+ ...newState,
116
+ });
117
+
118
+ onUpdateShapes(newState.shapes);
119
+ };
120
+
121
+ addPolygonPoint(e) {
122
+ const { shapes } = PolygonShape.addPoint(this.state, e, (newShapes) => {
123
+ this.setState({
124
+ isDrawing: false,
125
+ shapes: newShapes,
126
+ isDrawingShapeId: undefined,
127
+ });
128
+
129
+ this.props.onUpdateShapes(newShapes);
130
+ });
131
+
132
+ return shapes;
133
+ }
134
+
135
+ handleOnMouseUp = () => {
136
+ const { shapeType, onUpdateShapes } = this.props;
137
+ let newState;
138
+
139
+ if (shapeType === SUPPORTED_SHAPES.POLYGON) {
140
+ return;
141
+ }
142
+
143
+ switch (shapeType) {
144
+ case SUPPORTED_SHAPES.RECTANGLE:
145
+ newState = RectangleShape.finalizeCreation(this.state, this.props);
146
+ break;
147
+ case SUPPORTED_SHAPES.CIRCLE:
148
+ newState = CircleShape.finalizeCreation(this.state, this.props);
149
+ break;
150
+ default:
151
+ return;
152
+ }
153
+
154
+ this.setState({
155
+ ...newState,
156
+ isDrawing: false,
157
+ });
158
+
159
+ onUpdateShapes(newState.shapes);
160
+ };
161
+
162
+ handleMouseMove = (e) => {
163
+ const { shapeType, onUpdateShapes } = this.props;
164
+ let newState;
165
+
166
+ if (!this.state.isDrawing || !Object.values(SUPPORTED_SHAPES).includes(shapeType)) {
167
+ return;
168
+ }
169
+
170
+ switch (shapeType) {
171
+ case SUPPORTED_SHAPES.RECTANGLE:
172
+ newState = RectangleShape.handleMouseMove(this.state, e);
173
+ break;
174
+ case SUPPORTED_SHAPES.CIRCLE:
175
+ newState = CircleShape.handleMouseMove(this.state, e);
176
+ break;
177
+ case SUPPORTED_SHAPES.POLYGON:
178
+ newState = PolygonShape.handleMouseMove(this.state, e);
179
+ break;
180
+ default:
181
+ return;
182
+ }
183
+
184
+ this.setState(newState);
185
+ onUpdateShapes(newState.shapes);
186
+ };
187
+
188
+ handleOnMouseOutOrLeave = (e) => {
189
+ if (this.state.isDrawing) {
190
+ this.handleOnMouseUp(e);
191
+ }
192
+ };
193
+
194
+ handleOnDragEnd = (id, updatedProps) => {
195
+ const { shapes, onUpdateShapes, dimensions } = this.props;
196
+ const { width: canvasWidth, height: canvasHeight } = dimensions;
197
+
198
+ // when a shape is moved completely outside the canvas
199
+ // remove that shape
200
+ const newShapes = shapes
201
+ .map((shape) => {
202
+ if (shape.id !== id) {
203
+ return shape;
204
+ }
205
+
206
+ let newX = updatedProps.x;
207
+ let newY = updatedProps.y;
208
+
209
+ if (shape.group === 'rectangles') {
210
+ if (newX + shape.width < 0 || newX > canvasWidth || newY + shape.height < 0 || newY > canvasHeight) {
211
+ return null;
212
+ }
213
+
214
+ return { ...shape, ...updatedProps };
215
+ }
216
+
217
+ if (shape.group === 'circles') {
218
+ const radius = shape.radius;
219
+ if (newX + radius < 0 || newX - radius > canvasWidth || newY + radius < 0 || newY - radius > canvasHeight) {
220
+ return null;
221
+ }
222
+
223
+ return { ...shape, ...updatedProps };
224
+ }
225
+
226
+ if (shape.group === 'polygons') {
227
+ const points = shape.points;
228
+ const xValues = points.map((point) => point.x);
229
+ const yValues = points.map((point) => point.y);
230
+
231
+ let minX = Math.min(...xValues);
232
+ let minY = Math.min(...yValues);
233
+ let maxX = Math.max(...xValues);
234
+ let maxY = Math.max(...yValues);
235
+
236
+ // Calculate deltas based on the first point as a reference
237
+ const deltaX = updatedProps['points'][0].x - points[0].x;
238
+ const deltaY = updatedProps['points'][0].y - points[0].y;
239
+
240
+ minX = minX + deltaX;
241
+ maxX = maxX + deltaX;
242
+ minY = minY + deltaY;
243
+ maxY = maxY + deltaY;
244
+
245
+ if (maxX < 0 || minX > canvasWidth || maxY < 0 || minY > canvasHeight) {
246
+ return null;
247
+ }
248
+
249
+ return { ...shape, ...updatedProps };
250
+ }
251
+
252
+ return shape;
253
+ })
254
+ .filter((shape) => shape !== null);
255
+
256
+ onUpdateShapes(cloneDeep(newShapes));
257
+ };
258
+
259
+ closeInProgressPolygons = (id) => {
260
+ const { shapes, onUpdateShapes } = this.props;
261
+
262
+ const inProgressPolygon = (shapes || []).find((shape) => shape.id === 'newPolygon');
263
+ if (id === 'newPolygon' || (inProgressPolygon && this.state.isDrawing)) {
264
+ PolygonShape.finalizeCreation(this.state, (newShapes) => {
265
+ this.setState({
266
+ isDrawing: false,
267
+ shapes: newShapes,
268
+ isDrawingShapeId: undefined,
269
+ });
270
+
271
+ onUpdateShapes(cloneDeep(newShapes));
272
+ });
273
+ }
274
+ }
275
+
276
+ handleOnSetAsCorrect = (shape) => {
277
+ const { id } = shape;
278
+ const { multipleCorrect, shapes, onUpdateShapes } = this.props;
279
+
280
+ let newShapes;
281
+
282
+ if (multipleCorrect) {
283
+ newShapes = shapes.map((shape) => {
284
+ if (shape.id === id) {
285
+ shape.correct = !shape.correct;
286
+ }
287
+ return shape;
288
+ });
289
+ } else {
290
+ newShapes = shapes.map((shape) => {
291
+ shape.correct = shape.id === id;
292
+ return shape;
293
+ });
294
+ }
295
+
296
+ onUpdateShapes(cloneDeep(newShapes));
297
+
298
+ this.closeInProgressPolygons(id);
299
+ };
300
+ /// end of handling HotSpots section
301
+
302
+ /// start of handling Image section
303
+ handleOnImageLoad = ({ target }) => {
304
+ const { onUpdateImageDimension } = this.props;
305
+ const resizeHandle = this.resize;
306
+ const elementStyle = getComputedStyle(target);
307
+ const newHeight = parseFloat(elementStyle.height);
308
+ const newWidth = parseFloat(elementStyle.width);
309
+ const aspectWidth = newWidth / IMAGE_MAX_WIDTH;
310
+
311
+ const dimensions =
312
+ newWidth > IMAGE_MAX_WIDTH
313
+ ? {
314
+ height: newHeight / aspectWidth,
315
+ width: IMAGE_MAX_WIDTH,
316
+ }
317
+ : {
318
+ height: newHeight,
319
+ width: newWidth,
320
+ };
321
+
322
+ this.setState({ dimensions }, () => onUpdateImageDimension(dimensions));
323
+
324
+ resizeHandle.addEventListener('mousedown', this.initialiseResize, false);
325
+ };
326
+
327
+ initialiseResize = () => {
328
+ window.addEventListener('mousemove', this.startResizing, false);
329
+ window.addEventListener('mouseup', this.stopResizing, false);
330
+ };
331
+
332
+ checkIfResizeValid(x, y) {
333
+ const { shapes } = this.state;
334
+ let drawable = true;
335
+
336
+ // Do not allow resizing over the hotspots
337
+ shapes &&
338
+ shapes.forEach((shape) => {
339
+ const right = shape.x + shape.width + 5;
340
+ const bottom = shape.y + shape.height + 5;
341
+ if (x <= right || y <= bottom) {
342
+ drawable = false;
343
+ }
344
+ });
345
+ return drawable;
346
+ }
347
+
348
+ startResizing = (e) => {
349
+ const bounds = e.target.getBoundingClientRect();
350
+ const box = this.image;
351
+ const { disableDrag, preserveAspectRatioEnabled, dimensions, shapes } = this.props;
352
+
353
+ const { width, height } = updateImageDimensions(
354
+ dimensions,
355
+ {
356
+ width: e.clientX - bounds.left,
357
+ height: e.clientY - bounds.top,
358
+ },
359
+ preserveAspectRatioEnabled,
360
+ );
361
+
362
+ const resizeValid = this.checkIfResizeValid(width, height);
363
+ const hasMinimumWidth = width > 150 && height > 150;
364
+
365
+ if (resizeValid && hasMinimumWidth && box) {
366
+ box.style.width = `${width}px`;
367
+ box.style.height = `${height}px`;
368
+
369
+ this.setState({
370
+ resizing: true,
371
+ dimensions: { height: height, width: width },
372
+ stateShapes: getUpdatedShapes(dimensions, { width, height }, shapes),
373
+ });
374
+ }
375
+
376
+ disableDrag();
377
+ };
378
+
379
+ stopResizing = () => {
380
+ const { enableDrag, onUpdateImageDimension, onUpdateShapes } = this.props;
381
+ const { dimensions, stateShapes } = this.state;
382
+
383
+ enableDrag();
384
+
385
+ if (stateShapes) {
386
+ onUpdateShapes(cloneDeep(stateShapes));
387
+ }
388
+
389
+ onUpdateImageDimension(dimensions);
390
+
391
+ window.removeEventListener('mousemove', this.startResizing, false);
392
+ window.removeEventListener('mouseup', this.stopResizing, false);
393
+
394
+ this.setState({ resizing: false, stateShapes: false });
395
+ };
396
+
397
+ deleteShape = (id) => {
398
+ this.setState({
399
+ isDrawing: false,
400
+ isDrawingShapeId: undefined,
401
+ });
402
+ this.props.onDeleteShape(id);
403
+ };
404
+
405
+ /// end of handling Image section
406
+
407
+ render() {
408
+ const {
409
+ imageUrl,
410
+ dimensions: { height, width },
411
+ hotspotColor,
412
+ outlineColor,
413
+ shapes,
414
+ strokeWidth,
415
+ hoverOutlineColor,
416
+ selectedHotspotColor,
417
+ } = this.props;
418
+
419
+ const {
420
+ stateShapes,
421
+ isDrawing,
422
+ dimensions: { height: heightFromState, width: widthFromState },
423
+ } = this.state;
424
+ const shapesToUse = stateShapes || shapes;
425
+
426
+ return (
427
+ <BaseContainer>
428
+ {imageUrl && (
429
+ <ImageContainer>
430
+ <Image
431
+ onLoad={this.handleOnImageLoad}
432
+ ref={(ref) => {
433
+ this.image = ref;
434
+ }}
435
+ src={imageUrl}
436
+ {...(height && width ? { style: { height, width } } : {})}
437
+ />
438
+ <ResizeHandle
439
+ ref={(ref) => {
440
+ this.resize = ref;
441
+ }}
442
+ />
443
+ </ImageContainer>
444
+ )}
445
+
446
+ <StyledStage
447
+ height={heightFromState || height}
448
+ width={widthFromState || width}
449
+ onMouseDown={this.handleOnMouseDown}
450
+ onMouseUp={this.handleOnMouseUp}
451
+ onMouseMove={this.handleMouseMove}
452
+ onContentMouseOut={this.handleOnMouseOutOrLeave}
453
+ onContentMouseLeave={this.handleOnMouseOutOrLeave}
454
+ >
455
+ <Layer>
456
+ {shapesToUse.map((shape, i) => {
457
+ let Tag;
458
+ switch (shape.group) {
459
+ case SHAPE_GROUPS.RECTANGLES:
460
+ Tag = Rectangle;
461
+ break;
462
+ case SHAPE_GROUPS.CIRCLES:
463
+ Tag = Circle;
464
+ break;
465
+ case SHAPE_GROUPS.POLYGONS:
466
+ Tag = Polygon;
467
+ break;
468
+ default:
469
+ return null;
470
+ }
471
+
472
+ return (
473
+ <Tag
474
+ {...(shape.group === SHAPE_GROUPS.CIRCLES ? { radius: shape.radius } : {})}
475
+ {...(shape.group === SHAPE_GROUPS.RECTANGLES ? { height: shape.height, width: shape.width } : {})}
476
+ {...(shape.group === SHAPE_GROUPS.POLYGONS
477
+ ? { points: shape.points, addPolygonPoint: (e) => this.addPolygonPoint(e) }
478
+ : {})}
479
+ correct={shape.correct}
480
+ isDrawing={isDrawing}
481
+ hotspotColor={hotspotColor}
482
+ hoverOutlineColor={hoverOutlineColor}
483
+ selectedHotspotColor={selectedHotspotColor}
484
+ id={shape.id}
485
+ key={i}
486
+ onClick={() => this.handleOnSetAsCorrect(shape)}
487
+ onDragEnd={this.handleOnDragEnd}
488
+ onDeleteShape={this.deleteShape}
489
+ outlineColor={outlineColor}
490
+ width={shape.width}
491
+ x={shape.x}
492
+ y={shape.y}
493
+ strokeWidth={strokeWidth}
494
+ imageHeight={heightFromState || height}
495
+ imageWidth={widthFromState || width}
496
+ {...(shape.group === 'polygons' ? { addPolygonPoint: (e) => this.addPolygonPoint(e) } : {})}
497
+ />
498
+ );
499
+ })}
500
+ </Layer>
501
+ </StyledStage>
502
+ </BaseContainer>
503
+ );
504
+ }
505
+ }
506
+
507
+ Drawable.propTypes = {
508
+ disableDrag: PropTypes.func.isRequired,
509
+ dimensions: PropTypes.object.isRequired,
510
+ enableDrag: PropTypes.func.isRequired,
511
+ shapeType: PropTypes.oneOf(Object.values(SUPPORTED_SHAPES)),
512
+ imageUrl: PropTypes.string.isRequired,
513
+ handleFinishDrawing: PropTypes.func.isRequired,
514
+ hotspotColor: PropTypes.string.isRequired,
515
+ selectedHotspotColor: PropTypes.string,
516
+ hoverOutlineColor: PropTypes.string,
517
+ multipleCorrect: PropTypes.bool.isRequired,
518
+ onUpdateImageDimension: PropTypes.func.isRequired,
519
+ onUpdateShapes: PropTypes.func.isRequired,
520
+ onDeleteShape: PropTypes.func.isRequired,
521
+ outlineColor: PropTypes.string.isRequired,
522
+ shapes: PropTypes.array.isRequired,
523
+ strokeWidth: PropTypes.number,
524
+ preserveAspectRatioEnabled: PropTypes.bool,
525
+ };
526
+
527
+ export default Drawable;
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { InputContainer } from '@pie-lib/config-ui';
4
+ import Select from '@mui/material/Select';
5
+ import MenuItem from '@mui/material/MenuItem';
6
+ import { styled } from '@mui/material/styles';
7
+
8
+ const BaseContainer = styled('div')(({ theme }) => ({
9
+ marginTop: theme.spacing(2),
10
+ display: 'flex',
11
+ }));
12
+
13
+ const StyledInputContainer = styled(InputContainer)({
14
+ flex: 1,
15
+ width: '90%',
16
+ });
17
+
18
+ const StyledMenuItem = styled(MenuItem)(({ theme }) => ({
19
+ borderRadius: '2px',
20
+ height: '22px',
21
+ marginLeft: theme.spacing(2),
22
+ marginRight: theme.spacing(2),
23
+ marginTop: theme.spacing(2),
24
+ }));
25
+
26
+ class Palette extends React.Component {
27
+ onChange = (name) => (event) => {
28
+ const { value } = event.target;
29
+ const { onHotspotColorChange, onOutlineColorChange } = this.props;
30
+
31
+ if (name === 'hotspot') {
32
+ onHotspotColorChange(value);
33
+ } else {
34
+ onOutlineColorChange(value);
35
+ }
36
+ };
37
+
38
+ render() {
39
+ const { hotspotColor, outlineColor, hotspotList, outlineList } = this.props;
40
+
41
+ return (
42
+ <BaseContainer>
43
+ <StyledInputContainer label="Hot Spot">
44
+ <Select
45
+ onChange={this.onChange('hotspot')}
46
+ value={hotspotColor}
47
+ variant='standard'
48
+ MenuProps={{ transitionDuration: { enter: 225, exit: 195 } }}
49
+ >
50
+ {hotspotList.map((hotspot) => (
51
+ <StyledMenuItem key={hotspot} value={hotspot} style={{ backgroundColor: hotspot }}>
52
+ {hotspot}
53
+ </StyledMenuItem>
54
+ ))}
55
+ </Select>
56
+ </StyledInputContainer>
57
+
58
+ <StyledInputContainer label="Response Outline">
59
+ <Select
60
+ onChange={this.onChange('outline')}
61
+ value={outlineColor}
62
+ variant='standard'
63
+ MenuProps={{ transitionDuration: { enter: 225, exit: 195 } }}
64
+ >
65
+ {outlineList.map((outline) => (
66
+ <StyledMenuItem
67
+ key={outline}
68
+ value={outline}
69
+ style={{ border: `2px solid ${outline}` }}
70
+ >
71
+ {outline}
72
+ </StyledMenuItem>
73
+ ))}
74
+ </Select>
75
+ </StyledInputContainer>
76
+ </BaseContainer>
77
+ );
78
+ }
79
+ }
80
+
81
+ Palette.propTypes = {
82
+ hotspotColor: PropTypes.string.isRequired,
83
+ hotspotList: PropTypes.array.isRequired,
84
+ onHotspotColorChange: PropTypes.func.isRequired,
85
+ onOutlineColorChange: PropTypes.func.isRequired,
86
+ outlineColor: PropTypes.string.isRequired,
87
+ outlineList: PropTypes.array.isRequired,
88
+ };
89
+
90
+ export default Palette;