@pie-element/hotspot 6.3.2-next.9 → 6.4.0

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 (56) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/configure/CHANGELOG.md +19 -0
  3. package/configure/lib/DeleteWidget.js +19 -14
  4. package/configure/lib/DeleteWidget.js.map +1 -1
  5. package/configure/lib/buttons/circle.js +1 -1
  6. package/configure/lib/buttons/circle.js.map +1 -1
  7. package/configure/lib/defaults.js +2 -1
  8. package/configure/lib/defaults.js.map +1 -1
  9. package/configure/lib/hotspot-circle.js +229 -0
  10. package/configure/lib/hotspot-circle.js.map +1 -0
  11. package/configure/lib/hotspot-container.js +18 -7
  12. package/configure/lib/hotspot-container.js.map +1 -1
  13. package/configure/lib/hotspot-drawable.js +41 -14
  14. package/configure/lib/hotspot-drawable.js.map +1 -1
  15. package/configure/lib/hotspot-polygon.js +1 -2
  16. package/configure/lib/hotspot-polygon.js.map +1 -1
  17. package/configure/lib/hotspot-rectangle.js +0 -1
  18. package/configure/lib/hotspot-rectangle.js.map +1 -1
  19. package/configure/lib/index.js +8 -1
  20. package/configure/lib/index.js.map +1 -1
  21. package/configure/lib/shapes/circle.js +29 -10
  22. package/configure/lib/shapes/circle.js.map +1 -1
  23. package/configure/lib/utils.js +14 -1
  24. package/configure/lib/utils.js.map +1 -1
  25. package/configure/package.json +2 -2
  26. package/configure/src/DeleteWidget.jsx +16 -8
  27. package/configure/src/__tests__/__snapshots__/hotspot-container.test.js.snap +25 -0
  28. package/configure/src/__tests__/__snapshots__/hotspot-drawable.test.js.snap +0 -9
  29. package/configure/src/__tests__/__snapshots__/root.test.js.snap +18 -0
  30. package/configure/src/__tests__/hotspot-container.test.js +58 -3
  31. package/configure/src/__tests__/root.test.js +17 -0
  32. package/configure/src/__tests__/utils.test.js +18 -3
  33. package/configure/src/buttons/circle.jsx +1 -1
  34. package/configure/src/defaults.js +1 -0
  35. package/configure/src/hotspot-circle.jsx +145 -0
  36. package/configure/src/hotspot-container.jsx +13 -6
  37. package/configure/src/hotspot-drawable.jsx +26 -16
  38. package/configure/src/hotspot-polygon.jsx +1 -8
  39. package/configure/src/hotspot-rectangle.jsx +1 -9
  40. package/configure/src/index.js +3 -2
  41. package/configure/src/shapes/circle.js +21 -9
  42. package/configure/src/utils.js +12 -0
  43. package/controller/CHANGELOG.md +17 -0
  44. package/controller/lib/index.js +8 -3
  45. package/controller/lib/index.js.map +1 -1
  46. package/controller/package.json +2 -2
  47. package/controller/src/index.js +16 -6
  48. package/lib/hotspot/circle.js +191 -0
  49. package/lib/hotspot/circle.js.map +1 -0
  50. package/lib/hotspot/container.js +34 -2
  51. package/lib/hotspot/container.js.map +1 -1
  52. package/lib/hotspot/index.js.map +1 -1
  53. package/package.json +3 -3
  54. package/src/hotspot/circle.jsx +141 -0
  55. package/src/hotspot/container.jsx +31 -1
  56. package/src/hotspot/index.jsx +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.js"],"names":["updateImageDimensions","initialDim","nextDim","keepAspectRatio","resizeType","imageAspectRatio","width","height","getDelta","referenceInitialValue","referenceNextValue","currentValue","getUpdatedRectangle","shape","x","y","getUpdatedPlygon","points","map","point","getUpdatedShapes","shapes","group","getAllShapes","shapesMap","shapesArray","shapesKeys","Object","keys","length","reduce","acc","currentShapeKey","concat","index","groupShapes","rectangles","polygons","shapeProps","isPointInsidePolygon","polygon","inside","i","j","xi","yi","xj","yj","intersect","calculate","polygonPoints","xPoints","yPoints","minX","Math","min","minY","maxX","max","maxY","textX","textY","generateValidationMessage","config","minShapes","maxShapes","maxSelections","shapesMessage","selectionsMessage","message"],"mappings":"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","sourcesContent":["import cloneDeep from 'lodash/cloneDeep';\n\nconst updateImageDimensions = (initialDim, nextDim, keepAspectRatio, resizeType) => {\n // if we want to keep image aspect ratio\n if (keepAspectRatio) {\n const imageAspectRatio = initialDim.width / initialDim.height;\n\n if (resizeType === 'height') {\n // if we want to change image height => we update the width accordingly\n return {\n width: nextDim.height * imageAspectRatio,\n height: nextDim.height,\n };\n }\n\n // if we want to change image width => we update the height accordingly\n return {\n width: nextDim.width,\n height: nextDim.width / imageAspectRatio,\n };\n }\n\n // if we don't want to keep aspect ratio, we just update both values\n return {\n width: nextDim.width,\n height: nextDim.height,\n };\n};\n\n// referenceInitialValue = the initial value of the Stage\n// referenceNextValue = the next value of the Stage\n// currentValue = the value that has to be re-sized influenced by the changes that were made on the Stage\nconst getDelta = (referenceInitialValue, referenceNextValue, currentValue) =>\n (referenceNextValue / referenceInitialValue) * currentValue;\n\nconst getUpdatedRectangle = (initialDim, nextDim, shape) => ({\n ...shape,\n width: getDelta(initialDim.width, nextDim.width, shape.width),\n height: getDelta(initialDim.height, nextDim.height, shape.height),\n x: getDelta(initialDim.width, nextDim.width, shape.x),\n y: getDelta(initialDim.height, nextDim.height, shape.y),\n});\n\nconst getUpdatedPlygon = (initialDim, nextDim, shape) => ({\n ...shape,\n points: shape.points.map((point) => ({\n x: getDelta(initialDim.width, nextDim.width, point.x),\n y: getDelta(initialDim.height, nextDim.height, point.y),\n })),\n});\n\n// initialDim = the initial dimensions: { width, height } of the Stage\n// nextDim = the next dimensions: { width, height } of the Stage\n// shapes = array of shapes that have to be re-sized and re-positioned\nconst getUpdatedShapes = (initialDim, nextDim, shapes) => {\n return shapes.map((shape) => {\n if (shape.group === 'rectangles') {\n return getUpdatedRectangle(initialDim, nextDim, shape);\n }\n\n if (shape.group === 'polygons') {\n return getUpdatedPlygon(initialDim, nextDim, shape);\n }\n });\n};\n\n// converts shapes map to shapes array\n// example:\n// from: { rectangles: [r1], polygons: [p1, p2]}\n// to: [{ ...r1, group: 'rectangles' }, { ...p1, group: 'polygons' }, { ...p2, group: 'polygons' }]\n// if a shape has index defined, keep it, otherwise initialize it\n// index is used for the UNDO function\nconst getAllShapes = (shapesMap) => {\n shapesMap = shapesMap || {};\n const shapesArray = [];\n const shapesKeys = Object.keys(shapesMap);\n\n return shapesKeys.length\n ? shapesKeys.reduce(\n (acc, currentShapeKey) =>\n acc.concat(\n shapesMap[currentShapeKey]\n ? shapesMap[currentShapeKey].map((shape, index) => ({\n ...shape,\n group: currentShapeKey,\n index: shape.index || acc.length + index,\n }))\n : [],\n ),\n shapesArray,\n )\n : shapesArray;\n};\n\n// converts shapes array to shapes map\n// is the reverse of getAllShapes function\n// example:\n// from: [{ ...r1, group: 'rectangles' }, { ...p1, group: 'polygons' }, { ...p2, group: 'polygons' }]\n// to: { rectangles: [r1], polygons: [p1, p2]}\nconst groupShapes = (shapesArray) => {\n shapesArray = shapesArray || [];\n const shapesMap = {\n rectangles: [],\n polygons: [],\n };\n\n if (shapesArray.length) {\n return shapesArray.reduce((acc, { group, ...shapeProps }) => {\n acc[group] = [...(acc[group] || []), shapeProps];\n return acc;\n }, shapesMap);\n }\n\n return cloneDeep(shapesMap);\n};\n\nconst isPointInsidePolygon = (polygon, x, y) => {\n let inside = false;\n\n if (!polygon || polygon.length <= 0) {\n return inside;\n }\n\n for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\n const xi = polygon[i].x;\n const yi = polygon[i].y;\n const xj = polygon[j].x;\n const yj = polygon[j].y;\n\n const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;\n\n if (intersect) {\n inside = !inside;\n }\n }\n\n return inside;\n};\n\nconst calculate = (polygonPoints) => {\n if (!polygonPoints || polygonPoints.length <= 0) {\n return { x: 0, y: 0 };\n }\n\n const xPoints = polygonPoints.map((point) => point.x);\n const yPoints = polygonPoints.map((point) => point.y);\n const minX = Math.min(...xPoints);\n const minY = Math.min(...yPoints);\n const maxX = Math.max(...xPoints);\n const maxY = Math.max(...yPoints);\n\n // Find a suitable position for the text element within the polygon\n let textX, textY;\n\n for (let x = minX; x <= maxX - 20; x++) {\n for (let y = maxY - 20; y > minY; y--) {\n // Check if the text element's position (x, y) is within the polygon\n if (isPointInsidePolygon(polygonPoints, x, y)) {\n textX = x - 10;\n textY = y;\n break;\n }\n }\n }\n\n return { x: textX, y: textY };\n};\n\nconst generateValidationMessage = (config) => {\n const { minShapes, maxShapes, maxSelections } = config;\n\n const shapesMessage =\n `\\nThere should be at least ${minShapes} ` + (maxShapes ? `and at most ${maxShapes} ` : '') + 'shapes defined.';\n\n const selectionsMessage =\n '\\nThere should be at least 1 ' +\n (maxSelections ? `and at most ${maxSelections} ` : '') +\n 'shape' +\n (maxSelections ? 's' : '') +\n ' selected.';\n\n const message = 'Validation requirements:' + shapesMessage + selectionsMessage;\n\n return message;\n};\n\nexport {\n calculate,\n isPointInsidePolygon,\n updateImageDimensions,\n generateValidationMessage,\n getUpdatedShapes,\n getAllShapes,\n groupShapes,\n getUpdatedRectangle,\n getUpdatedPlygon,\n};\n"],"file":"utils.js"}
1
+ {"version":3,"sources":["../src/utils.js"],"names":["updateImageDimensions","initialDim","nextDim","keepAspectRatio","resizeType","imageAspectRatio","width","height","getDelta","referenceInitialValue","referenceNextValue","currentValue","getUpdatedRectangle","shape","x","y","getUpdatedCircles","radius","getUpdatedPlygon","points","map","point","getUpdatedShapes","shapes","group","getAllShapes","shapesMap","shapesArray","shapesKeys","Object","keys","length","reduce","acc","currentShapeKey","concat","index","groupShapes","rectangles","polygons","circles","shapeProps","isPointInsidePolygon","polygon","inside","i","j","xi","yi","xj","yj","intersect","calculate","polygonPoints","xPoints","yPoints","minX","Math","min","minY","maxX","max","maxY","textX","textY","generateValidationMessage","config","minShapes","maxShapes","maxSelections","shapesMessage","selectionsMessage","message"],"mappings":"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","sourcesContent":["import cloneDeep from 'lodash/cloneDeep';\n\nconst updateImageDimensions = (initialDim, nextDim, keepAspectRatio, resizeType) => {\n // if we want to keep image aspect ratio\n if (keepAspectRatio) {\n const imageAspectRatio = initialDim.width / initialDim.height;\n\n if (resizeType === 'height') {\n // if we want to change image height => we update the width accordingly\n return {\n width: nextDim.height * imageAspectRatio,\n height: nextDim.height,\n };\n }\n\n // if we want to change image width => we update the height accordingly\n return {\n width: nextDim.width,\n height: nextDim.width / imageAspectRatio,\n };\n }\n\n // if we don't want to keep aspect ratio, we just update both values\n return {\n width: nextDim.width,\n height: nextDim.height,\n };\n};\n\n// referenceInitialValue = the initial value of the Stage\n// referenceNextValue = the next value of the Stage\n// currentValue = the value that has to be re-sized influenced by the changes that were made on the Stage\nconst getDelta = (referenceInitialValue, referenceNextValue, currentValue) =>\n (referenceNextValue / referenceInitialValue) * currentValue;\n\nconst getUpdatedRectangle = (initialDim, nextDim, shape) => ({\n ...shape,\n width: getDelta(initialDim.width, nextDim.width, shape.width),\n height: getDelta(initialDim.height, nextDim.height, shape.height),\n x: getDelta(initialDim.width, nextDim.width, shape.x),\n y: getDelta(initialDim.height, nextDim.height, shape.y),\n});\n\nconst getUpdatedCircles = (initialDim, nextDim, shape) => ({\n ...shape,\n radius: getDelta(initialDim.radius, nextDim.radius, shape.radius),\n x: getDelta(initialDim.width, nextDim.width, shape.x),\n y: getDelta(initialDim.height, nextDim.height, shape.y),\n});\n\nconst getUpdatedPlygon = (initialDim, nextDim, shape) => ({\n ...shape,\n points: shape.points.map((point) => ({\n x: getDelta(initialDim.width, nextDim.width, point.x),\n y: getDelta(initialDim.height, nextDim.height, point.y),\n })),\n});\n\n// initialDim = the initial dimensions: { width, height } of the Stage\n// nextDim = the next dimensions: { width, height } of the Stage\n// shapes = array of shapes that have to be re-sized and re-positioned\nconst getUpdatedShapes = (initialDim, nextDim, shapes) => {\n return shapes.map((shape) => {\n if (shape.group === 'rectangles') {\n return getUpdatedRectangle(initialDim, nextDim, shape);\n }\n\n if (shape.group === 'polygons') {\n return getUpdatedPlygon(initialDim, nextDim, shape);\n }\n\n if (shape.group === 'circles') {\n return getUpdatedCircles(initialDim, nextDim, shape);\n }\n });\n};\n\n// converts shapes map to shapes array\n// example:\n// from: { rectangles: [r1], polygons: [p1, p2]}\n// to: [{ ...r1, group: 'rectangles' }, { ...p1, group: 'polygons' }, { ...p2, group: 'polygons' }]\n// if a shape has index defined, keep it, otherwise initialize it\n// index is used for the UNDO function\nconst getAllShapes = (shapesMap) => {\n shapesMap = shapesMap || {};\n const shapesArray = [];\n const shapesKeys = Object.keys(shapesMap);\n\n return shapesKeys.length\n ? shapesKeys.reduce(\n (acc, currentShapeKey) =>\n acc.concat(\n shapesMap[currentShapeKey]\n ? shapesMap[currentShapeKey].map((shape, index) => ({\n ...shape,\n group: currentShapeKey,\n index: shape.index || acc.length + index,\n }))\n : [],\n ),\n shapesArray,\n )\n : shapesArray;\n};\n\n// converts shapes array to shapes map\n// is the reverse of getAllShapes function\n// example:\n// from: [{ ...r1, group: 'rectangles' }, { ...p1, group: 'polygons' }, { ...p2, group: 'polygons' }]\n// to: { rectangles: [r1], polygons: [p1, p2]}\nconst groupShapes = (shapesArray) => {\n shapesArray = shapesArray || [];\n const shapesMap = {\n rectangles: [],\n polygons: [],\n circles: [],\n };\n\n if (shapesArray.length) {\n return shapesArray.reduce((acc, { group, ...shapeProps }) => {\n acc[group] = [...(acc[group] || []), shapeProps];\n return acc;\n }, shapesMap);\n }\n\n return cloneDeep(shapesMap);\n};\n\nconst isPointInsidePolygon = (polygon, x, y) => {\n let inside = false;\n\n if (!polygon || polygon.length <= 0) {\n return inside;\n }\n\n for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\n const xi = polygon[i].x;\n const yi = polygon[i].y;\n const xj = polygon[j].x;\n const yj = polygon[j].y;\n\n const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;\n\n if (intersect) {\n inside = !inside;\n }\n }\n\n return inside;\n};\n\nconst calculate = (polygonPoints) => {\n if (!polygonPoints || polygonPoints.length <= 0) {\n return { x: 0, y: 0 };\n }\n\n const xPoints = polygonPoints.map((point) => point.x);\n const yPoints = polygonPoints.map((point) => point.y);\n const minX = Math.min(...xPoints);\n const minY = Math.min(...yPoints);\n const maxX = Math.max(...xPoints);\n const maxY = Math.max(...yPoints);\n\n // Find a suitable position for the text element within the polygon\n let textX, textY;\n\n for (let x = minX; x <= maxX - 20; x++) {\n for (let y = maxY - 20; y > minY; y--) {\n // Check if the text element's position (x, y) is within the polygon\n if (isPointInsidePolygon(polygonPoints, x, y)) {\n textX = x - 10;\n textY = y;\n break;\n }\n }\n }\n\n return { x: textX, y: textY };\n};\n\nconst generateValidationMessage = (config) => {\n const { minShapes, maxShapes, maxSelections } = config;\n\n const shapesMessage =\n `\\nThere should be at least ${minShapes} ` + (maxShapes ? `and at most ${maxShapes} ` : '') + 'shapes defined.';\n\n const selectionsMessage =\n '\\nThere should be at least 1 ' +\n (maxSelections ? `and at most ${maxSelections} ` : '') +\n 'shape' +\n (maxSelections ? 's' : '') +\n ' selected.';\n\n const message = 'Validation requirements:' + shapesMessage + selectionsMessage;\n\n return message;\n};\n\nexport {\n calculate,\n isPointInsidePolygon,\n updateImageDimensions,\n generateValidationMessage,\n getUpdatedShapes,\n getAllShapes,\n groupShapes,\n getUpdatedRectangle,\n getUpdatedPlygon,\n};\n"],"file":"utils.js"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pie-element/hotspot-configure",
3
3
  "private": true,
4
- "version": "6.3.1",
4
+ "version": "6.4.0",
5
5
  "description": "",
6
6
  "main": "lib/index.js",
7
7
  "module": "src/index.js",
@@ -10,7 +10,7 @@
10
10
  "@material-ui/core": "^3.9.2",
11
11
  "@material-ui/icons": "^3.0.2",
12
12
  "@pie-framework/pie-configure-events": "^1.3.0",
13
- "@pie-lib/pie-toolbox": "1.5.2",
13
+ "@pie-lib/pie-toolbox": "1.5.5",
14
14
  "classnames": "^2.2.6",
15
15
  "debug": "^3.1.0",
16
16
  "konva": "^3.2.4",
@@ -5,16 +5,23 @@ import { faDelete } from './icons';
5
5
  import { ImageComponent } from '@pie-lib/pie-toolbox/icons';
6
6
  import { calculate } from './utils';
7
7
 
8
- const DeleteWidget = ({ height, id, width, x, y, points, outlineColor, handleWidgetClick }) => {
8
+ const DeleteWidget = ({ height, id, width, x, y, points, isCircle, radius, handleWidgetClick }) => {
9
9
  let positionX, positionY;
10
- // if points exist we have an irregular form (polygon) and position should be computed
11
- if (points) {
12
- const { x, y } = calculate(points);
13
- positionX = x;
10
+ const offset = 20;
11
+
12
+ if (isCircle) {
13
+ // For circles, position the delete icon above the circle
14
+ positionX = x + radius - offset;
14
15
  positionY = y;
16
+ } else if (points) {
17
+ // For polygons, compute position based on points
18
+ const calculated = calculate(points);
19
+ positionX = calculated.x;
20
+ positionY = calculated.y;
15
21
  } else {
16
- positionX = x + width - 20; // 10 pixels to the left
17
- positionY = y + height - 20; // 10 pixels above
22
+ // For rectangles
23
+ positionX = x + width - offset;
24
+ positionY = y + height - offset;
18
25
  }
19
26
 
20
27
  return (
@@ -31,13 +38,14 @@ DeleteWidget.propTypes = {
31
38
  x: PropTypes.number.isRequired,
32
39
  y: PropTypes.number.isRequired,
33
40
  handleWidgetClick: PropTypes.func.isRequired,
41
+ radius: PropTypes.number,
42
+ isCircle: PropTypes.bool,
34
43
  points: PropTypes.arrayOf(
35
44
  PropTypes.shape({
36
45
  x: PropTypes.number,
37
46
  y: PropTypes.number,
38
47
  }),
39
48
  ),
40
- outlineColor: PropTypes.string.isRequired,
41
49
  };
42
50
 
43
51
  export default DeleteWidget;
@@ -25,6 +25,13 @@ exports[`HotspotContainer render renders 1`] = `
25
25
  isActive={false}
26
26
  />
27
27
  </div>
28
+ <div
29
+ onClick={[Function]}
30
+ >
31
+ <CircleButton
32
+ isActive={false}
33
+ />
34
+ </div>
28
35
  <WithStyles(UploadControl)
29
36
  label="Replace Image"
30
37
  onInputClick={[Function]}
@@ -157,6 +164,24 @@ exports[`HotspotContainer render renders 1`] = `
157
164
  },
158
165
  ],
159
166
  },
167
+ Object {
168
+ "correct": false,
169
+ "group": "circles",
170
+ "id": "6",
171
+ "index": 6,
172
+ "radius": 70,
173
+ "x": 100,
174
+ "y": 100,
175
+ },
176
+ Object {
177
+ "correct": true,
178
+ "group": "circles",
179
+ "id": "7",
180
+ "index": 7,
181
+ "radius": 30,
182
+ "x": 200,
183
+ "y": 150,
184
+ },
160
185
  ]
161
186
  }
162
187
  strokeWidth={5}
@@ -26,7 +26,6 @@ exports[`HotspotDrawable render renders 1`] = `
26
26
  >
27
27
  <Layer>
28
28
  <WithStyles(RectComponent)
29
- addPolygonPoint={[Function]}
30
29
  correct={true}
31
30
  height={140}
32
31
  hotspotColor="rgba(137, 183, 244, 0.65)"
@@ -45,7 +44,6 @@ exports[`HotspotDrawable render renders 1`] = `
45
44
  y={1}
46
45
  />
47
46
  <WithStyles(RectComponent)
48
- addPolygonPoint={[Function]}
49
47
  height={140}
50
48
  hotspotColor="rgba(137, 183, 244, 0.65)"
51
49
  id="1"
@@ -63,7 +61,6 @@ exports[`HotspotDrawable render renders 1`] = `
63
61
  y={1}
64
62
  />
65
63
  <WithStyles(RectComponent)
66
- addPolygonPoint={[Function]}
67
64
  height={140}
68
65
  hotspotColor="rgba(137, 183, 244, 0.65)"
69
66
  id="2"
@@ -216,7 +213,6 @@ exports[`HotspotDrawable render renders with default strokeWidth 1`] = `
216
213
  >
217
214
  <Layer>
218
215
  <WithStyles(RectComponent)
219
- addPolygonPoint={[Function]}
220
216
  correct={true}
221
217
  height={140}
222
218
  hotspotColor="rgba(137, 183, 244, 0.65)"
@@ -235,7 +231,6 @@ exports[`HotspotDrawable render renders with default strokeWidth 1`] = `
235
231
  y={1}
236
232
  />
237
233
  <WithStyles(RectComponent)
238
- addPolygonPoint={[Function]}
239
234
  height={140}
240
235
  hotspotColor="rgba(137, 183, 244, 0.65)"
241
236
  id="1"
@@ -253,7 +248,6 @@ exports[`HotspotDrawable render renders with default strokeWidth 1`] = `
253
248
  y={1}
254
249
  />
255
250
  <WithStyles(RectComponent)
256
- addPolygonPoint={[Function]}
257
251
  height={140}
258
252
  hotspotColor="rgba(137, 183, 244, 0.65)"
259
253
  id="2"
@@ -406,7 +400,6 @@ exports[`HotspotDrawable render renders with given strokeWidth 1`] = `
406
400
  >
407
401
  <Layer>
408
402
  <WithStyles(RectComponent)
409
- addPolygonPoint={[Function]}
410
403
  correct={true}
411
404
  height={140}
412
405
  hotspotColor="rgba(137, 183, 244, 0.65)"
@@ -425,7 +418,6 @@ exports[`HotspotDrawable render renders with given strokeWidth 1`] = `
425
418
  y={1}
426
419
  />
427
420
  <WithStyles(RectComponent)
428
- addPolygonPoint={[Function]}
429
421
  height={140}
430
422
  hotspotColor="rgba(137, 183, 244, 0.65)"
431
423
  id="1"
@@ -443,7 +435,6 @@ exports[`HotspotDrawable render renders with given strokeWidth 1`] = `
443
435
  y={1}
444
436
  />
445
437
  <WithStyles(RectComponent)
446
- addPolygonPoint={[Function]}
447
438
  height={140}
448
439
  hotspotColor="rgba(137, 183, 244, 0.65)"
449
440
  id="2"
@@ -110,6 +110,15 @@ exports[`Root render renders 1`] = `
110
110
  "partialScoring": false,
111
111
  "prompt": "This is the question prompt",
112
112
  "shapes": Object {
113
+ "circles": Array [
114
+ Object {
115
+ "correct": false,
116
+ "id": "6",
117
+ "radius": 70,
118
+ "x": 100,
119
+ "y": 100,
120
+ },
121
+ ],
113
122
  "polygons": Array [
114
123
  Object {
115
124
  "correct": true,
@@ -273,6 +282,15 @@ There should be at least 1 shape selected."
273
282
  preserveAspectRatioEnabled={true}
274
283
  shapes={
275
284
  Object {
285
+ "circles": Array [
286
+ Object {
287
+ "correct": false,
288
+ "id": "6",
289
+ "radius": 70,
290
+ "x": 100,
291
+ "y": 100,
292
+ },
293
+ ],
276
294
  "polygons": Array [
277
295
  Object {
278
296
  "correct": true,
@@ -63,6 +63,22 @@ const model = () => ({
63
63
  correct: false,
64
64
  },
65
65
  ],
66
+ circles: [
67
+ {
68
+ id: '6',
69
+ radius: 70,
70
+ x: 100,
71
+ y: 100,
72
+ correct: false,
73
+ },
74
+ {
75
+ id: '7',
76
+ radius: 30,
77
+ x: 200,
78
+ y: 150,
79
+ correct: true,
80
+ },
81
+ ],
66
82
  }),
67
83
  ),
68
84
  dimensions: {
@@ -175,6 +191,24 @@ describe('HotspotContainer', () => {
175
191
  group: 'polygons',
176
192
  index: 5,
177
193
  },
194
+ {
195
+ id: '6',
196
+ radius: 70,
197
+ x: 100,
198
+ y: 100,
199
+ correct: false,
200
+ group: 'circles',
201
+ index: 6,
202
+ },
203
+ {
204
+ id: '7',
205
+ radius: 30,
206
+ x: 200,
207
+ y: 150,
208
+ correct: true,
209
+ group: 'circles',
210
+ index: 7,
211
+ },
178
212
  ];
179
213
 
180
214
  beforeEach(() => {
@@ -187,7 +221,7 @@ describe('HotspotContainer', () => {
187
221
 
188
222
  it('onUpdateShapes with new added shape', () => {
189
223
  const newShape = {
190
- id: '7',
224
+ id: '8',
191
225
  height: 140,
192
226
  width: 130,
193
227
  x: 280,
@@ -201,7 +235,7 @@ describe('HotspotContainer', () => {
201
235
  rectangles: [
202
236
  ...initialModel.shapes.rectangles,
203
237
  {
204
- id: '7',
238
+ id: '8',
205
239
  height: 140,
206
240
  width: 130,
207
241
  x: 280,
@@ -209,12 +243,13 @@ describe('HotspotContainer', () => {
209
243
  },
210
244
  ],
211
245
  polygons: initialModel.shapes.polygons,
246
+ circles: initialModel.shapes.circles,
212
247
  });
213
248
  });
214
249
 
215
250
  it('onDeleteShape by id', () => {
216
251
  console.log('wrapper', wrapper.instance());
217
- wrapper.instance().onDeleteShape('7');
252
+ wrapper.instance().onDeleteShape('8');
218
253
  expect(onUpdateShapes).toHaveBeenCalledWith(
219
254
  groupShapes([
220
255
  { correct: true, group: 'rectangles', height: 140, id: '0', index: 0, width: 130, x: 1, y: 1 },
@@ -256,6 +291,24 @@ describe('HotspotContainer', () => {
256
291
  { x: 407, y: 150 },
257
292
  ],
258
293
  },
294
+ {
295
+ id: '6',
296
+ radius: 70,
297
+ x: 100,
298
+ y: 100,
299
+ correct: false,
300
+ group: 'circles',
301
+ index: 6,
302
+ },
303
+ {
304
+ id: '7',
305
+ radius: 30,
306
+ x: 200,
307
+ y: 150,
308
+ correct: true,
309
+ group: 'circles',
310
+ index: 7,
311
+ },
259
312
  ]),
260
313
  );
261
314
  });
@@ -266,6 +319,7 @@ describe('HotspotContainer', () => {
266
319
  expect(onUpdateShapes).toHaveBeenLastCalledWith({
267
320
  rectangles: [],
268
321
  polygons: [],
322
+ circles: [],
269
323
  });
270
324
  });
271
325
 
@@ -274,6 +328,7 @@ describe('HotspotContainer', () => {
274
328
  expect(onUpdateShapes).toHaveBeenLastCalledWith({
275
329
  rectangles: [],
276
330
  polygons: [],
331
+ circles: [],
277
332
  });
278
333
  });
279
334
  });
@@ -80,6 +80,15 @@ const model = () => ({
80
80
  id: '5',
81
81
  },
82
82
  ],
83
+ circles: [
84
+ {
85
+ id: '6',
86
+ radius: 70,
87
+ x: 100,
88
+ y: 100,
89
+ correct: false,
90
+ },
91
+ ],
83
92
  },
84
93
  dimensions: { height: 300, width: 400 },
85
94
  hotspotColor: 'rgba(137, 183, 244, 0.65)',
@@ -177,6 +186,7 @@ describe('Root', () => {
177
186
  index: 1,
178
187
  },
179
188
  ],
189
+ circles: [],
180
190
  });
181
191
  });
182
192
 
@@ -199,6 +209,7 @@ describe('Root', () => {
199
209
  index: 1,
200
210
  },
201
211
  ],
212
+ circles: [],
202
213
  });
203
214
  });
204
215
 
@@ -221,6 +232,7 @@ describe('Root', () => {
221
232
  index: 1,
222
233
  },
223
234
  ],
235
+ circles: [],
224
236
  });
225
237
  });
226
238
 
@@ -243,6 +255,7 @@ describe('Root', () => {
243
255
  index: 1,
244
256
  },
245
257
  ],
258
+ circles: [],
246
259
  });
247
260
  });
248
261
  });
@@ -284,6 +297,7 @@ describe('Root', () => {
284
297
  index: 1,
285
298
  },
286
299
  ],
300
+ circles: [],
287
301
  });
288
302
  });
289
303
 
@@ -306,6 +320,7 @@ describe('Root', () => {
306
320
  index: 1,
307
321
  },
308
322
  ],
323
+ circles: [],
309
324
  });
310
325
  });
311
326
 
@@ -328,6 +343,7 @@ describe('Root', () => {
328
343
  ],
329
344
  },
330
345
  ],
346
+ circles: [],
331
347
  });
332
348
  });
333
349
 
@@ -350,6 +366,7 @@ describe('Root', () => {
350
366
  index: 1,
351
367
  },
352
368
  ],
369
+ circles: [],
353
370
  });
354
371
  });
355
372
  });
@@ -160,6 +160,13 @@ const shapesArray = [
160
160
  group: 'polygons',
161
161
  index: 3,
162
162
  },
163
+ {
164
+ radius: 50,
165
+ x: 300,
166
+ y: 150,
167
+ group: 'circles',
168
+ index: 4,
169
+ },
163
170
  ];
164
171
 
165
172
  const shapesMap = {
@@ -199,6 +206,14 @@ const shapesMap = {
199
206
  index: 3,
200
207
  },
201
208
  ],
209
+ circles: [
210
+ {
211
+ radius: 50,
212
+ x: 300,
213
+ y: 150,
214
+ index: 4,
215
+ },
216
+ ],
202
217
  };
203
218
 
204
219
  describe('getAllShapes', () => {
@@ -217,9 +232,9 @@ describe('groupShapes', () => {
217
232
  it.each`
218
233
  shapesArray | expected
219
234
  ${shapesArray} | ${shapesMap}
220
- ${null} | ${{ rectangles: [], polygons: [] }}
221
- ${undefined} | ${{ rectangles: [], polygons: [] }}
222
- ${[]} | ${{ rectangles: [], polygons: [] }}
235
+ ${null} | ${{ rectangles: [], polygons: [], circles: [] }}
236
+ ${undefined} | ${{ rectangles: [], polygons: [], circles: [] }}
237
+ ${[]} | ${{ rectangles: [], polygons: [], circles: [] }}
223
238
  `('TURNS shapes = $shapesArray INTO $expected', async ({ shapesArray, expected }) => {
224
239
  expect(groupShapes(shapesArray)).toEqual(expected);
225
240
  });
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
3
 
4
4
  export const CircleButton = ({ isActive = false }) => (
5
- <svg xmlns="http://www.w3.org/2000/svg" width="48" height="32" viewBox="0 0 48 32" fill="none">
5
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="32" viewBox="56 0 48 32" fill="none">
6
6
  <path
7
7
  fillRule="evenodd"
8
8
  clipRule="evenodd"
@@ -6,6 +6,7 @@ export default {
6
6
  shapes: {
7
7
  rectangles: [],
8
8
  polygons: [],
9
+ circles: [],
9
10
  },
10
11
  multipleCorrect: true,
11
12
  partialScoring: false,
@@ -0,0 +1,145 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Circle, Group, Transformer } from 'react-konva';
4
+ import { withStyles } from '@material-ui/core/styles';
5
+ import DeleteWidget from './DeleteWidget';
6
+
7
+ class CircleComponent extends React.Component {
8
+ constructor(props) {
9
+ super(props);
10
+ this.state = {
11
+ hovered: false,
12
+ isDragging: false,
13
+ };
14
+ this.shapeRef = React.createRef();
15
+ this.trRef = React.createRef();
16
+ }
17
+
18
+ handleClick = (e) => {
19
+ const { radius, isDrawing, onClick, id } = this.props;
20
+ if (radius <= 0 && isDrawing) {
21
+ return;
22
+ }
23
+ e.cancelBubble = true;
24
+ onClick(id);
25
+ };
26
+
27
+ handleMouseEnter = () => {
28
+ document.body.style.cursor = 'pointer';
29
+ this.setState({ hovered: true });
30
+ this.trRef.current.setNode(this.shapeRef.current);
31
+ this.trRef.current.getLayer().batchDraw();
32
+ };
33
+
34
+ handleMouseLeave = () => {
35
+ this.setState({ hovered: false });
36
+ document.body.style.cursor = 'default';
37
+ };
38
+
39
+ handleOnDragEnd = (e) => {
40
+ const { onDragEnd, id } = this.props;
41
+ this.setState({ isDragging: false });
42
+ onDragEnd(id, {
43
+ x: e.target.x(),
44
+ y: e.target.y(),
45
+ });
46
+ };
47
+
48
+ onResizeEnd = () => {
49
+ const { onDragEnd, id } = this.props;
50
+ const node = this.shapeRef.current;
51
+
52
+ const newRadius = Math.max(node.radius() * node.scaleX(), 5);
53
+
54
+ this.setState({ isDragging: false });
55
+ onDragEnd(id, {
56
+ x: node.x(),
57
+ y: node.y(),
58
+ radius: newRadius,
59
+ });
60
+
61
+ node.scaleX(1);
62
+ node.scaleY(1);
63
+ };
64
+
65
+ handleDelete = (id) => {
66
+ const { onDeleteShape } = this.props;
67
+ onDeleteShape(id);
68
+ };
69
+
70
+ render() {
71
+ const { classes, correct, radius, hotspotColor, id, outlineColor, x, y, strokeWidth = 5 } = this.props;
72
+
73
+ return (
74
+ <Group classes={classes.group} onMouseLeave={this.handleMouseLeave} onMouseEnter={this.handleMouseEnter}>
75
+ <Circle
76
+ classes={classes.base}
77
+ ref={this.shapeRef}
78
+ radius={radius}
79
+ fill={hotspotColor}
80
+ onClick={this.handleClick}
81
+ onTap={this.handleClick}
82
+ draggable
83
+ stroke={outlineColor}
84
+ strokeWidth={correct ? strokeWidth : 0}
85
+ onDragStart={() => this.setState({ isDragging: true })}
86
+ onDragEnd={this.handleOnDragEnd}
87
+ onTransformStart={() => this.setState({ isDragging: true })}
88
+ onTransformEnd={this.onResizeEnd}
89
+ x={x}
90
+ y={y}
91
+ />
92
+ {!this.state.isDragging && this.state.hovered && (
93
+ <DeleteWidget id={id} radius={radius} x={x} y={y} handleWidgetClick={this.handleDelete} isCircle={true} />
94
+ )}
95
+ {this.state.hovered && (
96
+ <Transformer
97
+ ref={this.trRef}
98
+ rotateEnabled={false}
99
+ keepRatio={true}
100
+ boundBoxFunc={(oldBox, newBox) => {
101
+ // Constraint to prevent resizing too small
102
+ if (newBox.width < 10 || newBox.height < 10) {
103
+ return oldBox;
104
+ }
105
+ return newBox;
106
+ }}
107
+ />
108
+ )}
109
+ </Group>
110
+ );
111
+ }
112
+ }
113
+
114
+ const styles = () => ({
115
+ base: {
116
+ cursor: 'pointer',
117
+ opacity: 0.5,
118
+ },
119
+
120
+ group: {
121
+ padding: '12px',
122
+ },
123
+ });
124
+
125
+ CircleComponent.propTypes = {
126
+ classes: PropTypes.object.isRequired,
127
+ correct: PropTypes.bool,
128
+ isDrawing: PropTypes.bool.isRequired,
129
+ id: PropTypes.string.isRequired,
130
+ radius: PropTypes.number.isRequired,
131
+ hotspotColor: PropTypes.string.isRequired,
132
+ onClick: PropTypes.func.isRequired,
133
+ onDeleteShape: PropTypes.func.isRequired,
134
+ onDragEnd: PropTypes.func.isRequired,
135
+ outlineColor: PropTypes.string.isRequired,
136
+ x: PropTypes.number.isRequired,
137
+ y: PropTypes.number.isRequired,
138
+ strokeWidth: PropTypes.number,
139
+ };
140
+
141
+ CircleComponent.defaultProps = {
142
+ correct: false,
143
+ };
144
+
145
+ export default withStyles(styles)(CircleComponent);