@pie-element/hotspot 11.1.7 → 11.2.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.
- package/CHANGELOG.md +11 -0
- package/configure/CHANGELOG.md +6 -0
- package/configure/lib/root.js +2 -1
- package/configure/lib/root.js.map +1 -1
- package/configure/package.json +3 -3
- package/configure/src/root.jsx +6 -10
- package/controller/CHANGELOG.md +6 -0
- package/controller/lib/index.js +2 -1
- package/controller/lib/index.js.map +1 -1
- package/controller/package.json +1 -1
- package/controller/src/index.js +26 -24
- package/docs/demo/generate.js +20 -16
- package/lib/hotspot/circle.js +16 -12
- package/lib/hotspot/circle.js.map +1 -1
- package/lib/hotspot/container.js +92 -7
- package/lib/hotspot/container.js.map +1 -1
- package/lib/hotspot/polygon.js +16 -12
- package/lib/hotspot/polygon.js.map +1 -1
- package/lib/hotspot/rectangle.js +19 -16
- package/lib/hotspot/rectangle.js.map +1 -1
- package/package.json +2 -2
- package/src/hotspot/__tests__/circle.test.jsx +42 -42
- package/src/hotspot/__tests__/rectangle.test.jsx +37 -37
- package/src/hotspot/circle.jsx +16 -14
- package/src/hotspot/container.jsx +80 -0
- package/src/hotspot/polygon.jsx +19 -17
- package/src/hotspot/rectangle.jsx +17 -16
package/src/hotspot/circle.jsx
CHANGED
|
@@ -50,6 +50,7 @@ class CircleComponent extends React.Component {
|
|
|
50
50
|
hoverOutlineColor,
|
|
51
51
|
outlineColor,
|
|
52
52
|
selected,
|
|
53
|
+
focused,
|
|
53
54
|
x,
|
|
54
55
|
y,
|
|
55
56
|
evaluateText,
|
|
@@ -88,20 +89,10 @@ class CircleComponent extends React.Component {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
const useHoveredStyle = hovered && hoverOutlineColor;
|
|
92
|
+
const useHoveredStyle = (hovered || focused) && hoverOutlineColor;
|
|
92
93
|
|
|
93
94
|
return (
|
|
94
|
-
<Group scaleX={scale} scaleY={scale}>
|
|
95
|
-
{useHoveredStyle && (
|
|
96
|
-
<Rect
|
|
97
|
-
x={x - radius}
|
|
98
|
-
y={y - radius}
|
|
99
|
-
width={radius * 2}
|
|
100
|
-
height={radius * 2}
|
|
101
|
-
stroke={selected ? 'transparent' : hoverOutlineColor}
|
|
102
|
-
strokeWidth={strokeWidth}
|
|
103
|
-
/>
|
|
104
|
-
)}
|
|
95
|
+
<Group scaleX={scale} scaleY={scale} onMouseLeave={this.handleMouseLeave} onMouseEnter={this.handleMouseEnter}>
|
|
105
96
|
<Circle
|
|
106
97
|
radius={radius}
|
|
107
98
|
fill={selected && selectedHotspotColor ? selectedHotspotColor : hotspotColor}
|
|
@@ -110,11 +101,20 @@ class CircleComponent extends React.Component {
|
|
|
110
101
|
draggable={false}
|
|
111
102
|
stroke={useHoveredStyle && !selected ? 'transparent' : outlineColorParsed}
|
|
112
103
|
strokeWidth={useHoveredStyle && !selected ? 0 : outlineWidth}
|
|
113
|
-
onMouseLeave={this.handleMouseLeave}
|
|
114
|
-
onMouseEnter={this.handleMouseEnter}
|
|
115
104
|
x={x}
|
|
116
105
|
y={y}
|
|
117
106
|
/>
|
|
107
|
+
{useHoveredStyle && (
|
|
108
|
+
<Rect
|
|
109
|
+
x={x - radius}
|
|
110
|
+
y={y - radius}
|
|
111
|
+
width={radius * 2}
|
|
112
|
+
height={radius * 2}
|
|
113
|
+
stroke={hoverOutlineColor}
|
|
114
|
+
strokeWidth={strokeWidth}
|
|
115
|
+
listening={false}
|
|
116
|
+
/>
|
|
117
|
+
)}
|
|
118
118
|
{isEvaluateMode && iconSrc ? <ImageComponent src={iconSrc} x={iconX} y={iconY} tooltip={evaluateText} /> : null}
|
|
119
119
|
</Group>
|
|
120
120
|
);
|
|
@@ -128,6 +128,7 @@ CircleComponent.propTypes = {
|
|
|
128
128
|
isCorrect: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
|
|
129
129
|
isEvaluateMode: PropTypes.bool.isRequired,
|
|
130
130
|
disabled: PropTypes.bool.isRequired,
|
|
131
|
+
focused: PropTypes.bool,
|
|
131
132
|
hoverOutlineColor: PropTypes.string,
|
|
132
133
|
onClick: PropTypes.func.isRequired,
|
|
133
134
|
outlineColor: PropTypes.string.isRequired,
|
|
@@ -145,6 +146,7 @@ CircleComponent.propTypes = {
|
|
|
145
146
|
CircleComponent.defaultProps = {
|
|
146
147
|
isCorrect: false,
|
|
147
148
|
evaluateText: null,
|
|
149
|
+
focused: false,
|
|
148
150
|
strokeWidth: 5,
|
|
149
151
|
scale: 1,
|
|
150
152
|
};
|
|
@@ -14,8 +14,19 @@ const BaseContainer = styled('div')(({ theme }) => ({
|
|
|
14
14
|
background: theme.palette.common.white,
|
|
15
15
|
border: `${theme.spacing(1)} solid ${theme.palette.common.white}`,
|
|
16
16
|
width: 'fit-content',
|
|
17
|
+
maxWidth: '100%',
|
|
18
|
+
overflowX: 'auto',
|
|
17
19
|
}));
|
|
18
20
|
|
|
21
|
+
const HiddenFocusable = styled('span')({
|
|
22
|
+
position: 'absolute',
|
|
23
|
+
width: 1,
|
|
24
|
+
height: 1,
|
|
25
|
+
overflow: 'hidden',
|
|
26
|
+
clip: 'rect(0, 0, 0, 0)',
|
|
27
|
+
whiteSpace: 'nowrap',
|
|
28
|
+
});
|
|
29
|
+
|
|
19
30
|
const ImageContainer = styled('div')({
|
|
20
31
|
position: 'relative',
|
|
21
32
|
width: 'fit-content',
|
|
@@ -34,6 +45,13 @@ const StyledStage = styled(Stage)({
|
|
|
34
45
|
});
|
|
35
46
|
|
|
36
47
|
export class Container extends React.Component {
|
|
48
|
+
constructor(props) {
|
|
49
|
+
super(props);
|
|
50
|
+
this.state = {
|
|
51
|
+
focusedShapeId: null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
37
55
|
isSelected(shape) {
|
|
38
56
|
const selectedShape = this.props.session.answers.filter((answer) => answer.id === shape.id)[0];
|
|
39
57
|
return !!selectedShape;
|
|
@@ -57,6 +75,44 @@ export class Container extends React.Component {
|
|
|
57
75
|
return null;
|
|
58
76
|
};
|
|
59
77
|
|
|
78
|
+
getAllShapesSorted = () => {
|
|
79
|
+
const { shapes: { rectangles = [], polygons = [], circles = [] } } = this.props;
|
|
80
|
+
const allShapes = [
|
|
81
|
+
...rectangles.map((s) => ({ ...s, type: 'rectangle' })),
|
|
82
|
+
...polygons.map((s) => ({ ...s, type: 'polygon' })),
|
|
83
|
+
...circles.map((s) => ({ ...s, type: 'circle' })),
|
|
84
|
+
];
|
|
85
|
+
allShapes.sort((a, b) => String(a.id).localeCompare(String(b.id), undefined, { numeric: true }));
|
|
86
|
+
|
|
87
|
+
return allShapes;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
handleShapeFocus = (shapeId) => {
|
|
91
|
+
this.setState({ focusedShapeId: shapeId });
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
handleShapeBlur = () => {
|
|
95
|
+
this.setState({ focusedShapeId: null });
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
handleShapeKeyDown = (e, shapeId) => {
|
|
99
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
|
|
102
|
+
const { onSelectChoice, disabled } = this.props;
|
|
103
|
+
|
|
104
|
+
if (!disabled) {
|
|
105
|
+
const shape = this.getAllShapesSorted().find((s) => s.id === shapeId);
|
|
106
|
+
|
|
107
|
+
if (shape) {
|
|
108
|
+
const selected = this.isSelected(shape);
|
|
109
|
+
|
|
110
|
+
onSelectChoice({ id: shapeId, selected: !selected, selector: 'Keyboard' });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
60
116
|
render() {
|
|
61
117
|
const {
|
|
62
118
|
dimensions: { width: withProp, height: heightProp },
|
|
@@ -74,9 +130,13 @@ export class Container extends React.Component {
|
|
|
74
130
|
showCorrect,
|
|
75
131
|
} = this.props;
|
|
76
132
|
|
|
133
|
+
const { focusedShapeId } = this.state;
|
|
134
|
+
|
|
77
135
|
const width = withProp * SCALE;
|
|
78
136
|
const height = heightProp * SCALE;
|
|
79
137
|
|
|
138
|
+
const sortedShapes = this.getAllShapesSorted();
|
|
139
|
+
|
|
80
140
|
return (
|
|
81
141
|
<BaseContainer style={{ padding: strokeWidth / 2 }}>
|
|
82
142
|
{imageUrl ? (
|
|
@@ -125,6 +185,7 @@ export class Container extends React.Component {
|
|
|
125
185
|
strokeWidth={strokeWidth}
|
|
126
186
|
markAsCorrect={markAsCorrect}
|
|
127
187
|
showCorrectEnabled={showCorrect}
|
|
188
|
+
focused={focusedShapeId === shape.id}
|
|
128
189
|
/>
|
|
129
190
|
);
|
|
130
191
|
})}
|
|
@@ -153,6 +214,7 @@ export class Container extends React.Component {
|
|
|
153
214
|
selectedHotspotColor={selectedHotspotColor}
|
|
154
215
|
hoverOutlineColor={hoverOutlineColor}
|
|
155
216
|
showCorrectEnabled={showCorrect}
|
|
217
|
+
focused={focusedShapeId === polygon.id}
|
|
156
218
|
/>
|
|
157
219
|
);
|
|
158
220
|
})}
|
|
@@ -183,11 +245,29 @@ export class Container extends React.Component {
|
|
|
183
245
|
selectedHotspotColor={selectedHotspotColor}
|
|
184
246
|
hoverOutlineColor={hoverOutlineColor}
|
|
185
247
|
showCorrectEnabled={showCorrect}
|
|
248
|
+
focused={focusedShapeId === shape.id}
|
|
186
249
|
/>
|
|
187
250
|
);
|
|
188
251
|
})}
|
|
189
252
|
</Layer>
|
|
190
253
|
</StyledStage>
|
|
254
|
+
|
|
255
|
+
{sortedShapes.map((shape) => {
|
|
256
|
+
const selected = this.isSelected(shape);
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<HiddenFocusable
|
|
260
|
+
key={`focus-${shape.id}`}
|
|
261
|
+
tabIndex={disabled ? -1 : 0}
|
|
262
|
+
role="button"
|
|
263
|
+
aria-label={shape.ariaLabel || ''}
|
|
264
|
+
aria-pressed={selected}
|
|
265
|
+
onFocus={() => this.handleShapeFocus(shape.id)}
|
|
266
|
+
onBlur={this.handleShapeBlur}
|
|
267
|
+
onKeyDown={(e) => this.handleShapeKeyDown(e, shape.id)}
|
|
268
|
+
/>
|
|
269
|
+
);
|
|
270
|
+
})}
|
|
191
271
|
</BaseContainer>
|
|
192
272
|
);
|
|
193
273
|
}
|
package/src/hotspot/polygon.jsx
CHANGED
|
@@ -68,6 +68,7 @@ class PolygonComponent extends React.Component {
|
|
|
68
68
|
hoverOutlineColor,
|
|
69
69
|
outlineColor,
|
|
70
70
|
selected,
|
|
71
|
+
focused,
|
|
71
72
|
points,
|
|
72
73
|
evaluateText,
|
|
73
74
|
strokeWidth,
|
|
@@ -120,7 +121,7 @@ class PolygonComponent extends React.Component {
|
|
|
120
121
|
iconSrc = faWrong;
|
|
121
122
|
}
|
|
122
123
|
}
|
|
123
|
-
const useHoveredStyle = hovered && hoverOutlineColor;
|
|
124
|
+
const useHoveredStyle = (hovered || focused) && hoverOutlineColor;
|
|
124
125
|
|
|
125
126
|
const xValues = pointsParsed.filter((_, index) => index % 2 === 0); // Even indices are x-coordinates
|
|
126
127
|
const yValues = pointsParsed.filter((_, index) => index % 2 !== 0); // Odd indices are y-coordinates
|
|
@@ -136,31 +137,30 @@ class PolygonComponent extends React.Component {
|
|
|
136
137
|
const rectHeight = maxY - minY;
|
|
137
138
|
|
|
138
139
|
return (
|
|
139
|
-
<Group scaleX={scale} scaleY={scale}>
|
|
140
|
-
{useHoveredStyle && (
|
|
141
|
-
<Rect
|
|
142
|
-
x={rectX}
|
|
143
|
-
y={rectY}
|
|
144
|
-
width={rectWidth}
|
|
145
|
-
height={rectHeight}
|
|
146
|
-
stroke={selected ? 'transparent' : hoverOutlineColor}
|
|
147
|
-
strokeWidth={strokeWidth}
|
|
148
|
-
/>
|
|
149
|
-
)}
|
|
140
|
+
<Group scaleX={scale} scaleY={scale} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
|
150
141
|
<Line
|
|
151
142
|
points={pointsParsed}
|
|
152
143
|
closed={true}
|
|
153
|
-
fill={selected && selectedHotspotColor? selectedHotspotColor : hotspotColor}
|
|
144
|
+
fill={selected && selectedHotspotColor ? selectedHotspotColor : hotspotColor}
|
|
154
145
|
onClick={this.handleClick}
|
|
155
146
|
onTap={this.handleClick}
|
|
156
147
|
draggable={false}
|
|
157
148
|
stroke={useHoveredStyle && !selected ? 'transparent' : outlineColorParsed}
|
|
158
149
|
strokeWidth={useHoveredStyle && !selected ? 0 : outlineWidth}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
cursor='pointer'
|
|
162
|
-
position='relative'
|
|
150
|
+
cursor="pointer"
|
|
151
|
+
position="relative"
|
|
163
152
|
/>
|
|
153
|
+
{useHoveredStyle && (
|
|
154
|
+
<Rect
|
|
155
|
+
x={rectX}
|
|
156
|
+
y={rectY}
|
|
157
|
+
width={rectWidth}
|
|
158
|
+
height={rectHeight}
|
|
159
|
+
stroke={hoverOutlineColor}
|
|
160
|
+
strokeWidth={strokeWidth}
|
|
161
|
+
listening={false}
|
|
162
|
+
/>
|
|
163
|
+
)}
|
|
164
164
|
{isEvaluateMode && iconSrc ? <ImageComponent src={iconSrc} x={iconX} y={iconY} tooltip={evaluateText} /> : null}
|
|
165
165
|
</Group>
|
|
166
166
|
);
|
|
@@ -174,6 +174,7 @@ PolygonComponent.propTypes = {
|
|
|
174
174
|
isEvaluateMode: PropTypes.bool.isRequired,
|
|
175
175
|
hoverOutlineColor: PropTypes.string,
|
|
176
176
|
disabled: PropTypes.bool.isRequired,
|
|
177
|
+
focused: PropTypes.bool,
|
|
177
178
|
onClick: PropTypes.func.isRequired,
|
|
178
179
|
outlineColor: PropTypes.string.isRequired,
|
|
179
180
|
points: PropTypes.array.isRequired,
|
|
@@ -188,6 +189,7 @@ PolygonComponent.propTypes = {
|
|
|
188
189
|
|
|
189
190
|
PolygonComponent.defaultProps = {
|
|
190
191
|
evaluateText: null,
|
|
192
|
+
focused: false,
|
|
191
193
|
strokeWidth: 5,
|
|
192
194
|
scale: 1,
|
|
193
195
|
};
|
|
@@ -51,6 +51,7 @@ class RectComponent extends React.Component {
|
|
|
51
51
|
isEvaluateMode,
|
|
52
52
|
outlineColor,
|
|
53
53
|
selected,
|
|
54
|
+
focused,
|
|
54
55
|
width,
|
|
55
56
|
x,
|
|
56
57
|
y,
|
|
@@ -60,6 +61,7 @@ class RectComponent extends React.Component {
|
|
|
60
61
|
markAsCorrect,
|
|
61
62
|
showCorrectEnabled,
|
|
62
63
|
} = this.props;
|
|
64
|
+
const { hovered } = this.state;
|
|
63
65
|
|
|
64
66
|
const outlineColorParsed = isEvaluateMode
|
|
65
67
|
? this.getEvaluateOutlineColor(isCorrect, markAsCorrect, outlineColor)
|
|
@@ -102,22 +104,10 @@ class RectComponent extends React.Component {
|
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
const
|
|
106
|
-
const useHoveredStyle = hovered && hoverOutlineColor;
|
|
107
|
+
const useHoveredStyle = (hovered || focused) && hoverOutlineColor;
|
|
107
108
|
|
|
108
109
|
return (
|
|
109
|
-
<Group scaleX={scale} scaleY={scale}>
|
|
110
|
-
{useHoveredStyle && (
|
|
111
|
-
<Rect
|
|
112
|
-
x={x}
|
|
113
|
-
y={y}
|
|
114
|
-
width={width}
|
|
115
|
-
height={height}
|
|
116
|
-
stroke={selected ? 'transparent' : hoverOutlineColor}
|
|
117
|
-
strokeWidth={strokeWidth}
|
|
118
|
-
listening={false}
|
|
119
|
-
/>
|
|
120
|
-
)}
|
|
110
|
+
<Group scaleX={scale} scaleY={scale} onMouseLeave={this.handleMouseLeave} onMouseEnter={this.handleMouseEnter}>
|
|
121
111
|
<Rect
|
|
122
112
|
x={x}
|
|
123
113
|
y={y}
|
|
@@ -129,10 +119,19 @@ class RectComponent extends React.Component {
|
|
|
129
119
|
draggable={false}
|
|
130
120
|
stroke={useHoveredStyle && !selected ? 'transparent' : outlineColorParsed}
|
|
131
121
|
strokeWidth={useHoveredStyle && !selected ? 0 : outlineWidth}
|
|
132
|
-
onMouseLeave={this.handleMouseLeave}
|
|
133
|
-
onMouseEnter={this.handleMouseEnter}
|
|
134
122
|
cursor="pointer"
|
|
135
123
|
/>
|
|
124
|
+
{useHoveredStyle && (
|
|
125
|
+
<Rect
|
|
126
|
+
x={x}
|
|
127
|
+
y={y}
|
|
128
|
+
width={width}
|
|
129
|
+
height={height}
|
|
130
|
+
stroke={hoverOutlineColor}
|
|
131
|
+
strokeWidth={strokeWidth}
|
|
132
|
+
listening={false}
|
|
133
|
+
/>
|
|
134
|
+
)}
|
|
136
135
|
{isEvaluateMode && iconSrc ? <ImageComponent src={iconSrc} x={iconX} y={iconY} tooltip={evaluateText} /> : null}
|
|
137
136
|
</Group>
|
|
138
137
|
);
|
|
@@ -147,6 +146,7 @@ RectComponent.propTypes = {
|
|
|
147
146
|
isEvaluateMode: PropTypes.bool.isRequired,
|
|
148
147
|
hoverOutlineColor: PropTypes.string,
|
|
149
148
|
disabled: PropTypes.bool.isRequired,
|
|
149
|
+
focused: PropTypes.bool,
|
|
150
150
|
onClick: PropTypes.func.isRequired,
|
|
151
151
|
outlineColor: PropTypes.string.isRequired,
|
|
152
152
|
selected: PropTypes.bool.isRequired,
|
|
@@ -164,6 +164,7 @@ RectComponent.propTypes = {
|
|
|
164
164
|
RectComponent.defaultProps = {
|
|
165
165
|
isCorrect: false,
|
|
166
166
|
evaluateText: null,
|
|
167
|
+
focused: false,
|
|
167
168
|
strokeWidth: 5,
|
|
168
169
|
scale: 1,
|
|
169
170
|
};
|