@pie-element/hotspot 11.1.1-next.83 → 11.1.1-next.92

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.
@@ -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
  }
@@ -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
@@ -174,6 +175,7 @@ PolygonComponent.propTypes = {
174
175
  isEvaluateMode: PropTypes.bool.isRequired,
175
176
  hoverOutlineColor: PropTypes.string,
176
177
  disabled: PropTypes.bool.isRequired,
178
+ focused: PropTypes.bool,
177
179
  onClick: PropTypes.func.isRequired,
178
180
  outlineColor: PropTypes.string.isRequired,
179
181
  points: PropTypes.array.isRequired,
@@ -188,6 +190,7 @@ PolygonComponent.propTypes = {
188
190
 
189
191
  PolygonComponent.defaultProps = {
190
192
  evaluateText: null,
193
+ focused: false,
191
194
  strokeWidth: 5,
192
195
  scale: 1,
193
196
  };
@@ -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,8 +104,7 @@ class RectComponent extends React.Component {
102
104
  }
103
105
  }
104
106
 
105
- const { hovered } = this.state;
106
- const useHoveredStyle = hovered && hoverOutlineColor;
107
+ const useHoveredStyle = (hovered || focused) && hoverOutlineColor;
107
108
 
108
109
  return (
109
110
  <Group scaleX={scale} scaleY={scale}>
@@ -147,6 +148,7 @@ RectComponent.propTypes = {
147
148
  isEvaluateMode: PropTypes.bool.isRequired,
148
149
  hoverOutlineColor: PropTypes.string,
149
150
  disabled: PropTypes.bool.isRequired,
151
+ focused: PropTypes.bool,
150
152
  onClick: PropTypes.func.isRequired,
151
153
  outlineColor: PropTypes.string.isRequired,
152
154
  selected: PropTypes.bool.isRequired,
@@ -164,6 +166,7 @@ RectComponent.propTypes = {
164
166
  RectComponent.defaultProps = {
165
167
  isCorrect: false,
166
168
  evaluateText: null,
169
+ focused: false,
167
170
  strokeWidth: 5,
168
171
  scale: 1,
169
172
  };