@pie-element/hotspot 5.18.2-next.9 → 5.19.1-next.12
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/CHANGELOG.md +17 -0
- package/configure/CHANGELOG.md +17 -0
- package/configure/lib/DeleteWidget.js +74 -0
- package/configure/lib/DeleteWidget.js.map +1 -0
- package/configure/lib/hotspot-container.js +5 -9
- package/configure/lib/hotspot-container.js.map +1 -1
- package/configure/lib/hotspot-drawable.js +6 -3
- package/configure/lib/hotspot-drawable.js.map +1 -1
- package/configure/lib/hotspot-polygon.js +44 -19
- package/configure/lib/hotspot-polygon.js.map +1 -1
- package/configure/lib/hotspot-rectangle.js +40 -1
- package/configure/lib/hotspot-rectangle.js.map +1 -1
- package/configure/lib/icons.js +7 -0
- package/configure/lib/icons.js.map +1 -0
- package/configure/lib/image.js +133 -0
- package/configure/lib/image.js.map +1 -0
- package/configure/lib/utils.js +65 -1
- package/configure/lib/utils.js.map +1 -1
- package/configure/package.json +2 -2
- package/configure/src/DeleteWidget.jsx +43 -0
- package/configure/src/__tests__/__snapshots__/hotspot-container.test.js.snap +1 -5
- package/configure/src/__tests__/hotspot-container.test.js +50 -27
- package/configure/src/hotspot-container.jsx +19 -28
- package/configure/src/hotspot-drawable.jsx +5 -4
- package/configure/src/hotspot-polygon.jsx +48 -15
- package/configure/src/hotspot-rectangle.jsx +24 -3
- package/configure/src/icons.js +5 -0
- package/configure/src/image.jsx +76 -0
- package/configure/src/utils.js +54 -0
- package/controller/package.json +1 -1
- package/package.json +3 -3
|
@@ -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","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 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 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","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"}
|
package/configure/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-element/hotspot-configure",
|
|
3
3
|
"private": true,
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.15.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.0.
|
|
13
|
+
"@pie-lib/pie-toolbox": "1.0.5",
|
|
14
14
|
"classnames": "^2.2.6",
|
|
15
15
|
"debug": "^3.1.0",
|
|
16
16
|
"konva": "^3.2.4",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { Group } from 'react-konva';
|
|
4
|
+
import { faDelete } from './icons';
|
|
5
|
+
import Image from './image';
|
|
6
|
+
import { calculate } from './utils';
|
|
7
|
+
|
|
8
|
+
const DeleteWidget = ({ height, id, width, x, y, points, outlineColor, handleWidgetClick }) => {
|
|
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;
|
|
14
|
+
positionY = y;
|
|
15
|
+
} else {
|
|
16
|
+
positionX = x + width - 20; // 10 pixels to the left
|
|
17
|
+
positionY = y + height - 20; // 10 pixels above
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Group onClick={() => handleWidgetClick(id)}>
|
|
22
|
+
<Image width={20} height={20} x={positionX} y={positionY} src={faDelete} />
|
|
23
|
+
</Group>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
DeleteWidget.propTypes = {
|
|
28
|
+
id: PropTypes.string.isRequired,
|
|
29
|
+
height: PropTypes.number,
|
|
30
|
+
width: PropTypes.number,
|
|
31
|
+
x: PropTypes.number.isRequired,
|
|
32
|
+
y: PropTypes.number.isRequired,
|
|
33
|
+
handleWidgetClick: PropTypes.func.isRequired,
|
|
34
|
+
points: PropTypes.arrayOf(
|
|
35
|
+
PropTypes.shape({
|
|
36
|
+
x: PropTypes.number,
|
|
37
|
+
y: PropTypes.number,
|
|
38
|
+
}),
|
|
39
|
+
),
|
|
40
|
+
outlineColor: PropTypes.string.isRequired,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default DeleteWidget;
|
|
@@ -16,11 +16,6 @@ exports[`HotspotContainer render renders 1`] = `
|
|
|
16
16
|
onInputClick={[Function]}
|
|
17
17
|
setRef={[Function]}
|
|
18
18
|
/>
|
|
19
|
-
<WithStyles(RawButton)
|
|
20
|
-
disabled={false}
|
|
21
|
-
label="Undo"
|
|
22
|
-
onClick={[Function]}
|
|
23
|
-
/>
|
|
24
19
|
<WithStyles(RawButton)
|
|
25
20
|
disabled={false}
|
|
26
21
|
label="Clear all"
|
|
@@ -40,6 +35,7 @@ exports[`HotspotContainer render renders 1`] = `
|
|
|
40
35
|
hotspotColor="rgba(137, 183, 244, 0.65)"
|
|
41
36
|
imageUrl="https://cdn.fluence.net/image/0240eb1455ce4c4bb6180232347b6aef_W"
|
|
42
37
|
multipleCorrect={true}
|
|
38
|
+
onDeleteShape={[Function]}
|
|
43
39
|
onUpdateImageDimension={[MockFunction]}
|
|
44
40
|
onUpdateShapes={[Function]}
|
|
45
41
|
outlineColor="blue"
|
|
@@ -78,6 +78,7 @@ describe('HotspotContainer', () => {
|
|
|
78
78
|
let w,
|
|
79
79
|
onImageUpload = jest.fn(),
|
|
80
80
|
onUpdateImageDimension = jest.fn(),
|
|
81
|
+
onDeleteShape = jest.fn(),
|
|
81
82
|
onUpdateShapes = jest.fn(),
|
|
82
83
|
initialModel = model();
|
|
83
84
|
beforeEach(() => {
|
|
@@ -91,6 +92,7 @@ describe('HotspotContainer', () => {
|
|
|
91
92
|
outlineColor: initialModel.outlineColor,
|
|
92
93
|
onUpdateImageDimension: onUpdateImageDimension,
|
|
93
94
|
onUpdateShapes: onUpdateShapes,
|
|
95
|
+
onDeleteShape: onDeleteShape,
|
|
94
96
|
onImageUpload: onImageUpload,
|
|
95
97
|
shapes: initialModel.shapes,
|
|
96
98
|
...extras,
|
|
@@ -210,6 +212,54 @@ describe('HotspotContainer', () => {
|
|
|
210
212
|
});
|
|
211
213
|
});
|
|
212
214
|
|
|
215
|
+
it('onDeleteShape by id', () => {
|
|
216
|
+
console.log('wrapper', wrapper.instance());
|
|
217
|
+
wrapper.instance().onDeleteShape('7');
|
|
218
|
+
expect(onUpdateShapes).toHaveBeenCalledWith(
|
|
219
|
+
groupShapes([
|
|
220
|
+
{ correct: true, group: 'rectangles', height: 140, id: '0', index: 0, width: 130, x: 1, y: 1 },
|
|
221
|
+
{ group: 'rectangles', height: 140, id: '1', index: 1, width: 130, x: 140, y: 1 },
|
|
222
|
+
{ group: 'rectangles', height: 140, id: '2', index: 2, width: 130, x: 280, y: 1 },
|
|
223
|
+
{
|
|
224
|
+
correct: true,
|
|
225
|
+
group: 'polygons',
|
|
226
|
+
id: '3',
|
|
227
|
+
index: 3,
|
|
228
|
+
points: [
|
|
229
|
+
{ x: 1, y: 148 },
|
|
230
|
+
{ x: 1, y: 288 },
|
|
231
|
+
{ x: 129, y: 288 },
|
|
232
|
+
{ x: 129, y: 148 },
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
correct: false,
|
|
237
|
+
group: 'polygons',
|
|
238
|
+
id: '4',
|
|
239
|
+
index: 4,
|
|
240
|
+
points: [
|
|
241
|
+
{ x: 141, y: 151 },
|
|
242
|
+
{ x: 141, y: 289 },
|
|
243
|
+
{ x: 269, y: 289 },
|
|
244
|
+
{ x: 269, y: 151 },
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
correct: false,
|
|
249
|
+
group: 'polygons',
|
|
250
|
+
id: '5',
|
|
251
|
+
index: 5,
|
|
252
|
+
points: [
|
|
253
|
+
{ x: 279, y: 150 },
|
|
254
|
+
{ x: 279, y: 289 },
|
|
255
|
+
{ x: 407, y: 289 },
|
|
256
|
+
{ x: 407, y: 150 },
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
]),
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
|
|
213
263
|
it('onUpdateShapes with no shapes', () => {
|
|
214
264
|
wrapper.instance().onUpdateShapes([]);
|
|
215
265
|
|
|
@@ -219,33 +269,6 @@ describe('HotspotContainer', () => {
|
|
|
219
269
|
});
|
|
220
270
|
});
|
|
221
271
|
|
|
222
|
-
it('handleUndo if no new shape was added', () => {
|
|
223
|
-
wrapper.instance().handleUndo();
|
|
224
|
-
expect(onUpdateShapes).toHaveBeenLastCalledWith({
|
|
225
|
-
...initialModel.shapes,
|
|
226
|
-
polygons: initialModel.shapes.polygons.slice(0, -1),
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('handleUndo if new shape was added', () => {
|
|
231
|
-
const newShape = {
|
|
232
|
-
id: '7',
|
|
233
|
-
height: 140,
|
|
234
|
-
width: 130,
|
|
235
|
-
x: 280,
|
|
236
|
-
y: 1,
|
|
237
|
-
index: 6,
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
// first, add a new shape
|
|
241
|
-
wrapper.instance().state.shapes = getAllShapes({
|
|
242
|
-
...initialModel.shapes,
|
|
243
|
-
rectangles: [...initialModel.shapes.rectangles, newShape],
|
|
244
|
-
});
|
|
245
|
-
wrapper.instance().handleUndo();
|
|
246
|
-
expect(onUpdateShapes).toBeCalledWith(initialModel.shapes);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
272
|
it('handleClearAll', () => {
|
|
250
273
|
wrapper.instance().handleClearAll();
|
|
251
274
|
expect(onUpdateShapes).toHaveBeenLastCalledWith({
|
|
@@ -33,13 +33,10 @@ export class Container extends Component {
|
|
|
33
33
|
shapes: getAllShapes(props.shapes),
|
|
34
34
|
};
|
|
35
35
|
this.fakeImageHandler = {
|
|
36
|
-
cancel: () => {
|
|
37
|
-
},
|
|
36
|
+
cancel: () => {},
|
|
38
37
|
done: (a, url) => this.props.onImageUpload(url),
|
|
39
|
-
fileChosen: () => {
|
|
40
|
-
},
|
|
41
|
-
progress: () => {
|
|
42
|
-
}
|
|
38
|
+
fileChosen: () => {},
|
|
39
|
+
progress: () => {},
|
|
43
40
|
};
|
|
44
41
|
}
|
|
45
42
|
|
|
@@ -55,7 +52,7 @@ export class Container extends Component {
|
|
|
55
52
|
insertImage({
|
|
56
53
|
...this.fakeImageHandler,
|
|
57
54
|
getChosenFile: () => file,
|
|
58
|
-
isPasted: true
|
|
55
|
+
isPasted: true,
|
|
59
56
|
});
|
|
60
57
|
}
|
|
61
58
|
}
|
|
@@ -108,23 +105,18 @@ export class Container extends Component {
|
|
|
108
105
|
|
|
109
106
|
onUpdateShapes = (newShapes) => {
|
|
110
107
|
const { onUpdateShapes } = this.props;
|
|
111
|
-
|
|
112
108
|
this.setState(
|
|
113
109
|
{ shapes: newShapes },
|
|
114
110
|
// always transform shapes array back into shapes map when saving changes
|
|
115
|
-
() => onUpdateShapes(groupShapes(newShapes))
|
|
111
|
+
() => onUpdateShapes(groupShapes(newShapes)),
|
|
116
112
|
);
|
|
117
113
|
};
|
|
118
114
|
|
|
119
|
-
|
|
115
|
+
onDeleteShape = (id) => {
|
|
120
116
|
const { shapes } = this.state;
|
|
121
|
-
|
|
122
117
|
if (shapes && shapes.length) {
|
|
123
|
-
//
|
|
124
|
-
let newShapes = shapes.
|
|
125
|
-
|
|
126
|
-
newShapes = newShapes ? newShapes.slice(0, newShapes.length - 1) : [];
|
|
127
|
-
|
|
118
|
+
// filter the deleted shape out
|
|
119
|
+
let newShapes = shapes.filter((shape) => shape.id !== id);
|
|
128
120
|
this.onUpdateShapes(newShapes);
|
|
129
121
|
}
|
|
130
122
|
};
|
|
@@ -170,12 +162,12 @@ export class Container extends Component {
|
|
|
170
162
|
})}
|
|
171
163
|
{...(dragEnabled
|
|
172
164
|
? {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
165
|
+
onDragExit: this.handleOnDragExit,
|
|
166
|
+
onDragLeave: this.handleOnDragExit,
|
|
167
|
+
onDragOver: this.handleOnDragOver,
|
|
168
|
+
onDrop: this.handleOnDrop,
|
|
169
|
+
onPaste: this.handleOnPaste,
|
|
170
|
+
}
|
|
179
171
|
: {})}
|
|
180
172
|
>
|
|
181
173
|
<div className={classes.toolbar}>
|
|
@@ -190,9 +182,7 @@ export class Container extends Component {
|
|
|
190
182
|
}}
|
|
191
183
|
/>
|
|
192
184
|
)}
|
|
193
|
-
|
|
194
|
-
<Button disabled={!(shapes && shapes.length)} onClick={this.handleUndo} label="Undo"/>
|
|
195
|
-
<Button disabled={!(shapes && shapes.length)} onClick={this.handleClearAll} label="Clear all"/>
|
|
185
|
+
<Button disabled={!(shapes && shapes.length)} onClick={this.handleClearAll} label="Clear all" />
|
|
196
186
|
</div>
|
|
197
187
|
|
|
198
188
|
<div
|
|
@@ -211,6 +201,7 @@ export class Container extends Component {
|
|
|
211
201
|
multipleCorrect={multipleCorrect}
|
|
212
202
|
onUpdateImageDimension={onUpdateImageDimension}
|
|
213
203
|
onUpdateShapes={this.onUpdateShapes}
|
|
204
|
+
onDeleteShape={this.onDeleteShape}
|
|
214
205
|
outlineColor={outlineColor}
|
|
215
206
|
shapes={shapes}
|
|
216
207
|
strokeWidth={strokeWidth}
|
|
@@ -219,7 +210,7 @@ export class Container extends Component {
|
|
|
219
210
|
) : (
|
|
220
211
|
<div className={classNames(classes.drawableHeight, classes.centered)}>
|
|
221
212
|
<label>Drag and drop or upload image from computer</label>
|
|
222
|
-
<br/>
|
|
213
|
+
<br />
|
|
223
214
|
<UploadControl
|
|
224
215
|
label="Upload Image"
|
|
225
216
|
onInputClick={this.handleInputClick}
|
|
@@ -238,11 +229,11 @@ export class Container extends Component {
|
|
|
238
229
|
<label>
|
|
239
230
|
Click and drag to create a hotspot. Click the hotspot to mark correct. Click again to unmark.
|
|
240
231
|
</label>
|
|
241
|
-
<div className={classes.tooltipArrow}/>
|
|
232
|
+
<div className={classes.tooltipArrow} />
|
|
242
233
|
</div>
|
|
243
234
|
)}
|
|
244
235
|
|
|
245
|
-
<Help className={classes.icon} onMouseOut={this.toggleTooltip} onMouseOver={this.toggleTooltip}/>
|
|
236
|
+
<Help className={classes.icon} onMouseOut={this.toggleTooltip} onMouseOver={this.toggleTooltip} />
|
|
246
237
|
</div>
|
|
247
238
|
)}
|
|
248
239
|
</div>
|
|
@@ -102,7 +102,6 @@ export class Drawable extends React.Component {
|
|
|
102
102
|
x: currShape.x,
|
|
103
103
|
y: currShape.y,
|
|
104
104
|
};
|
|
105
|
-
|
|
106
105
|
// On mouse move don't trigger any event. Put the shapes on this state instead.
|
|
107
106
|
this.setState({ stateShapes: newShapesList });
|
|
108
107
|
}
|
|
@@ -254,6 +253,7 @@ export class Drawable extends React.Component {
|
|
|
254
253
|
outlineColor,
|
|
255
254
|
shapes,
|
|
256
255
|
strokeWidth,
|
|
256
|
+
onDeleteShape,
|
|
257
257
|
} = this.props;
|
|
258
258
|
const {
|
|
259
259
|
stateShapes,
|
|
@@ -293,9 +293,8 @@ export class Drawable extends React.Component {
|
|
|
293
293
|
onMouseMove={this.handleMouseMove}
|
|
294
294
|
>
|
|
295
295
|
<Layer>
|
|
296
|
-
{shapesToUse.map((shape,
|
|
296
|
+
{shapesToUse.map((shape, i) => {
|
|
297
297
|
const Tag = shape.group === 'polygons' ? Polygon : Rectangle;
|
|
298
|
-
|
|
299
298
|
return (
|
|
300
299
|
<Tag
|
|
301
300
|
correct={shape.correct}
|
|
@@ -303,9 +302,10 @@ export class Drawable extends React.Component {
|
|
|
303
302
|
height={shape.height}
|
|
304
303
|
hotspotColor={hotspotColor}
|
|
305
304
|
id={shape.id}
|
|
306
|
-
key={
|
|
305
|
+
key={i}
|
|
307
306
|
onClick={() => this.handleOnSetAsCorrect(shape)}
|
|
308
307
|
onDragEnd={this.handleOnDragEnd}
|
|
308
|
+
onDeleteShape={onDeleteShape}
|
|
309
309
|
outlineColor={outlineColor}
|
|
310
310
|
width={shape.width}
|
|
311
311
|
x={shape.x}
|
|
@@ -364,6 +364,7 @@ Drawable.propTypes = {
|
|
|
364
364
|
multipleCorrect: PropTypes.bool.isRequired,
|
|
365
365
|
onUpdateImageDimension: PropTypes.func.isRequired,
|
|
366
366
|
onUpdateShapes: PropTypes.func.isRequired,
|
|
367
|
+
onDeleteShape: PropTypes.func.isRequired,
|
|
367
368
|
outlineColor: PropTypes.string.isRequired,
|
|
368
369
|
shapes: PropTypes.array.isRequired,
|
|
369
370
|
strokeWidth: PropTypes.number,
|
|
@@ -2,15 +2,19 @@ import React from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { Group, Line, Circle } from 'react-konva';
|
|
4
4
|
import { withStyles } from '@material-ui/core/styles/index';
|
|
5
|
+
import DeleteWidget from './DeleteWidget';
|
|
5
6
|
|
|
6
7
|
const HOVERED_COLOR = '#00BFFF';
|
|
7
8
|
|
|
8
9
|
class PolComponent extends React.Component {
|
|
9
10
|
static getDerivedStateFromProps(nextProps, prevState) {
|
|
10
|
-
const { points, imageHeight, imageWidth } = nextProps;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const { id, points, imageHeight, imageWidth } = nextProps;
|
|
12
|
+
// we execute this code only if image dimensions changed or an hotspot was added/deleted
|
|
13
|
+
if (
|
|
14
|
+
prevState.imageHeight !== nextProps.imageHeight ||
|
|
15
|
+
prevState.imageWidth !== nextProps.imageWidth ||
|
|
16
|
+
prevState.id !== nextProps.id
|
|
17
|
+
) {
|
|
14
18
|
if (points.length) {
|
|
15
19
|
const xList = points.map((p) => p.x);
|
|
16
20
|
const yList = points.map((p) => p.y);
|
|
@@ -19,6 +23,7 @@ class PolComponent extends React.Component {
|
|
|
19
23
|
const y = Math.max(...yList);
|
|
20
24
|
|
|
21
25
|
return {
|
|
26
|
+
id,
|
|
22
27
|
x,
|
|
23
28
|
y,
|
|
24
29
|
points,
|
|
@@ -28,6 +33,7 @@ class PolComponent extends React.Component {
|
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
return {
|
|
36
|
+
id: '',
|
|
31
37
|
x: 0,
|
|
32
38
|
y: 0,
|
|
33
39
|
points: [],
|
|
@@ -67,6 +73,7 @@ class PolComponent extends React.Component {
|
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
return {
|
|
76
|
+
id: '',
|
|
70
77
|
x: 0,
|
|
71
78
|
y: 0,
|
|
72
79
|
points: [],
|
|
@@ -75,6 +82,7 @@ class PolComponent extends React.Component {
|
|
|
75
82
|
|
|
76
83
|
state = {
|
|
77
84
|
hovered: false,
|
|
85
|
+
isDragging: false,
|
|
78
86
|
...this.getInitialState(this.props.points),
|
|
79
87
|
};
|
|
80
88
|
|
|
@@ -124,11 +132,11 @@ class PolComponent extends React.Component {
|
|
|
124
132
|
return acc;
|
|
125
133
|
}, []);
|
|
126
134
|
|
|
127
|
-
this.setState({
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
135
|
+
this.setState({
|
|
136
|
+
points: newPoints,
|
|
137
|
+
...this.getOffset(newPoints),
|
|
138
|
+
isDragging: updateModel ? false : this.state.isDragging,
|
|
139
|
+
});
|
|
132
140
|
};
|
|
133
141
|
|
|
134
142
|
handleOnDragVertex = (e, changedIndex, updateModel) => {
|
|
@@ -139,20 +147,32 @@ class PolComponent extends React.Component {
|
|
|
139
147
|
index === changedIndex ? { x: e.target.x(), y: e.target.y() } : point,
|
|
140
148
|
);
|
|
141
149
|
|
|
142
|
-
this.setState({
|
|
150
|
+
this.setState({
|
|
151
|
+
points: newPoints,
|
|
152
|
+
...this.getOffset(newPoints),
|
|
153
|
+
isDragging: updateModel ? false : this.state.isDragging,
|
|
154
|
+
});
|
|
143
155
|
|
|
144
156
|
if (updateModel) {
|
|
145
157
|
onDragEnd(id, { points: newPoints });
|
|
146
158
|
}
|
|
147
159
|
};
|
|
148
160
|
|
|
161
|
+
onDragStart = () => {
|
|
162
|
+
this.setState({ isDragging: true });
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
handleDelete = (id) => {
|
|
166
|
+
const { onDeleteShape } = this.props;
|
|
167
|
+
onDeleteShape(id);
|
|
168
|
+
};
|
|
169
|
+
|
|
149
170
|
render() {
|
|
150
|
-
const { classes, correct, hotspotColor, outlineColor, strokeWidth = 5 } = this.props;
|
|
171
|
+
const { classes, correct, id, hotspotColor, outlineColor, strokeWidth = 5 } = this.props;
|
|
151
172
|
const { points, x, y, hovered } = this.state;
|
|
152
173
|
|
|
153
174
|
const calculatedStrokeWidth = correct ? strokeWidth : hovered ? 1 : 0;
|
|
154
175
|
const calculatedStroke = correct ? outlineColor : hovered ? HOVERED_COLOR : '';
|
|
155
|
-
|
|
156
176
|
return (
|
|
157
177
|
<Group classes={classes.group} onMouseLeave={this.handleMouseLeave} onMouseEnter={this.handleMouseEnter}>
|
|
158
178
|
<Line
|
|
@@ -165,6 +185,7 @@ class PolComponent extends React.Component {
|
|
|
165
185
|
draggable
|
|
166
186
|
stroke={calculatedStroke}
|
|
167
187
|
strokeWidth={calculatedStrokeWidth}
|
|
188
|
+
onDragStart={this.onDragStart}
|
|
168
189
|
onDragMove={this.handleOnDragEnd}
|
|
169
190
|
onDragEnd={(e) => this.handleOnDragEnd(e, true)}
|
|
170
191
|
x={x}
|
|
@@ -182,15 +203,26 @@ class PolComponent extends React.Component {
|
|
|
182
203
|
fill={'white'}
|
|
183
204
|
stroke={HOVERED_COLOR}
|
|
184
205
|
strokeWidth={1}
|
|
185
|
-
|
|
206
|
+
onDragStart={this.onDragStart}
|
|
186
207
|
onDragMove={(e) => {
|
|
187
208
|
this.handleOnDragVertex(e, index);
|
|
188
209
|
}}
|
|
189
210
|
onDragEnd={(e) => {
|
|
190
211
|
this.handleOnDragVertex(e, index, true);
|
|
191
212
|
}}
|
|
213
|
+
draggable
|
|
192
214
|
/>
|
|
193
215
|
))}
|
|
216
|
+
{!this.state.isDragging && this.state.hovered && (
|
|
217
|
+
<DeleteWidget
|
|
218
|
+
x={x}
|
|
219
|
+
y={y}
|
|
220
|
+
id={id}
|
|
221
|
+
handleWidgetClick={this.handleDelete}
|
|
222
|
+
points={points}
|
|
223
|
+
outlineColor={outlineColor}
|
|
224
|
+
/>
|
|
225
|
+
)}
|
|
194
226
|
</Group>
|
|
195
227
|
);
|
|
196
228
|
}
|
|
@@ -214,8 +246,11 @@ PolComponent.propTypes = {
|
|
|
214
246
|
correct: PropTypes.bool,
|
|
215
247
|
isDrawing: PropTypes.bool.isRequired,
|
|
216
248
|
id: PropTypes.string.isRequired,
|
|
249
|
+
imageHeight: PropTypes.number,
|
|
250
|
+
imageWidth: PropTypes.number,
|
|
217
251
|
hotspotColor: PropTypes.string.isRequired,
|
|
218
252
|
onClick: PropTypes.func.isRequired,
|
|
253
|
+
onDeleteShape: PropTypes.func.isRequired,
|
|
219
254
|
onDragEnd: PropTypes.func.isRequired,
|
|
220
255
|
outlineColor: PropTypes.string.isRequired,
|
|
221
256
|
points: PropTypes.arrayOf(
|
|
@@ -225,8 +260,6 @@ PolComponent.propTypes = {
|
|
|
225
260
|
}),
|
|
226
261
|
).isRequired,
|
|
227
262
|
strokeWidth: PropTypes.number,
|
|
228
|
-
imageHeight: PropTypes.number,
|
|
229
|
-
imageWidth: PropTypes.number,
|
|
230
263
|
};
|
|
231
264
|
|
|
232
265
|
PolComponent.defaultProps = {
|
|
@@ -2,12 +2,14 @@ import React from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { Rect, Group, Transformer } from 'react-konva';
|
|
4
4
|
import { withStyles } from '@material-ui/core/styles/index';
|
|
5
|
+
import DeleteWidget from './DeleteWidget';
|
|
5
6
|
|
|
6
7
|
class RectComponent extends React.Component {
|
|
7
8
|
constructor(props) {
|
|
8
9
|
super(props);
|
|
9
10
|
this.state = {
|
|
10
11
|
hovered: false,
|
|
12
|
+
isDragging: false,
|
|
11
13
|
};
|
|
12
14
|
this.shapeRef = React.createRef();
|
|
13
15
|
this.trRef = React.createRef();
|
|
@@ -36,7 +38,7 @@ class RectComponent extends React.Component {
|
|
|
36
38
|
|
|
37
39
|
handleOnDragEnd = (e) => {
|
|
38
40
|
const { onDragEnd, id } = this.props;
|
|
39
|
-
|
|
41
|
+
this.setState({ isDragging: false });
|
|
40
42
|
onDragEnd(id, {
|
|
41
43
|
x: e.target.x(),
|
|
42
44
|
y: e.target.y(),
|
|
@@ -56,6 +58,7 @@ class RectComponent extends React.Component {
|
|
|
56
58
|
// we will reset it back
|
|
57
59
|
node.scaleX(1);
|
|
58
60
|
node.scaleY(1);
|
|
61
|
+
this.setState({ isDragging: false });
|
|
59
62
|
onDragEnd(id, {
|
|
60
63
|
x: node.x(),
|
|
61
64
|
y: node.y(),
|
|
@@ -65,9 +68,13 @@ class RectComponent extends React.Component {
|
|
|
65
68
|
});
|
|
66
69
|
};
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
const {
|
|
71
|
+
handleDelete = (id) => {
|
|
72
|
+
const { onDeleteShape } = this.props;
|
|
73
|
+
onDeleteShape(id);
|
|
74
|
+
};
|
|
70
75
|
|
|
76
|
+
render() {
|
|
77
|
+
const { classes, correct, height, hotspotColor, id, outlineColor, width, x, y, strokeWidth = 5 } = this.props;
|
|
71
78
|
return (
|
|
72
79
|
<Group classes={classes.group} onMouseLeave={this.handleMouseLeave} onMouseEnter={this.handleMouseEnter}>
|
|
73
80
|
<Rect
|
|
@@ -81,11 +88,24 @@ class RectComponent extends React.Component {
|
|
|
81
88
|
draggable
|
|
82
89
|
stroke={outlineColor}
|
|
83
90
|
strokeWidth={correct ? strokeWidth : 0}
|
|
91
|
+
onDragStart={() => this.setState({ isDragging: true })}
|
|
84
92
|
onDragEnd={this.handleOnDragEnd}
|
|
93
|
+
onTransformStart={() => this.setState({ isDragging: true })}
|
|
85
94
|
onTransformEnd={this.onResizeEnd}
|
|
86
95
|
x={x}
|
|
87
96
|
y={y}
|
|
88
97
|
/>
|
|
98
|
+
{!this.state.isDragging && this.state.hovered && (
|
|
99
|
+
<DeleteWidget
|
|
100
|
+
id={id}
|
|
101
|
+
height={height}
|
|
102
|
+
width={width}
|
|
103
|
+
x={x}
|
|
104
|
+
y={y}
|
|
105
|
+
outlineColor={outlineColor}
|
|
106
|
+
handleWidgetClick={this.handleDelete}
|
|
107
|
+
/>
|
|
108
|
+
)}
|
|
89
109
|
{this.state.hovered && (
|
|
90
110
|
<Transformer
|
|
91
111
|
ref={this.trRef}
|
|
@@ -123,6 +143,7 @@ RectComponent.propTypes = {
|
|
|
123
143
|
height: PropTypes.number.isRequired,
|
|
124
144
|
hotspotColor: PropTypes.string.isRequired,
|
|
125
145
|
onClick: PropTypes.func.isRequired,
|
|
146
|
+
onDeleteShape: PropTypes.func.isRequired,
|
|
126
147
|
onDragEnd: PropTypes.func.isRequired,
|
|
127
148
|
outlineColor: PropTypes.string.isRequired,
|
|
128
149
|
width: PropTypes.number.isRequired,
|