@pie-lib/mask-markup 2.0.0-beta.2 → 2.1.0-next.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 (80) hide show
  1. package/CHANGELOG.json +1 -871
  2. package/CHANGELOG.md +296 -2
  3. package/LICENSE.md +5 -0
  4. package/NEXT.CHANGELOG.json +1 -0
  5. package/lib/__tests__/drag-in-the-blank.test.js +129 -0
  6. package/lib/__tests__/index.test.js +42 -0
  7. package/lib/__tests__/mask.test.js +163 -0
  8. package/lib/__tests__/serialization.test.js +44 -0
  9. package/lib/__tests__/utils.js +14 -0
  10. package/lib/__tests__/with-mask.test.js +110 -0
  11. package/lib/choices/__tests__/index.test.js +101 -0
  12. package/lib/choices/choice.js +100 -119
  13. package/lib/choices/choice.js.map +1 -1
  14. package/lib/choices/index.js +24 -20
  15. package/lib/choices/index.js.map +1 -1
  16. package/lib/componentize.js +2 -3
  17. package/lib/componentize.js.map +1 -1
  18. package/lib/components/__tests__/blank.test.js +189 -0
  19. package/lib/components/__tests__/correct-input.test.js +132 -0
  20. package/lib/components/__tests__/dropdown.test.js +134 -0
  21. package/lib/components/__tests__/input.test.js +129 -0
  22. package/lib/components/blank.js +316 -222
  23. package/lib/components/blank.js.map +1 -1
  24. package/lib/components/correct-input.js +40 -43
  25. package/lib/components/correct-input.js.map +1 -1
  26. package/lib/components/dropdown.js +394 -125
  27. package/lib/components/dropdown.js.map +1 -1
  28. package/lib/components/input.js +2 -3
  29. package/lib/components/input.js.map +1 -1
  30. package/lib/constructed-response.js +83 -27
  31. package/lib/constructed-response.js.map +1 -1
  32. package/lib/customizable.js +44 -0
  33. package/lib/customizable.js.map +1 -0
  34. package/lib/drag-in-the-blank.js +155 -62
  35. package/lib/drag-in-the-blank.js.map +1 -1
  36. package/lib/index.js +8 -1
  37. package/lib/index.js.map +1 -1
  38. package/lib/inline-dropdown.js +5 -4
  39. package/lib/inline-dropdown.js.map +1 -1
  40. package/lib/mask.js +90 -57
  41. package/lib/mask.js.map +1 -1
  42. package/lib/serialization.js +31 -43
  43. package/lib/serialization.js.map +1 -1
  44. package/lib/with-mask.js +49 -21
  45. package/lib/with-mask.js.map +1 -1
  46. package/package.json +18 -15
  47. package/src/__tests__/drag-in-the-blank.test.js +111 -0
  48. package/src/__tests__/index.test.js +39 -0
  49. package/src/__tests__/mask.test.js +187 -0
  50. package/src/__tests__/serialization.test.js +54 -0
  51. package/src/__tests__/utils.js +1 -0
  52. package/src/__tests__/with-mask.test.js +76 -0
  53. package/src/choices/__tests__/index.test.js +75 -0
  54. package/src/choices/choice.jsx +83 -96
  55. package/src/choices/index.jsx +11 -5
  56. package/src/components/__tests__/blank.test.js +138 -0
  57. package/src/components/__tests__/correct-input.test.js +90 -0
  58. package/src/components/__tests__/dropdown.test.js +93 -0
  59. package/src/components/__tests__/input.test.js +102 -0
  60. package/src/components/blank.jsx +316 -204
  61. package/src/components/correct-input.jsx +37 -38
  62. package/src/components/dropdown.jsx +371 -125
  63. package/src/constructed-response.jsx +71 -18
  64. package/src/customizable.jsx +35 -0
  65. package/src/drag-in-the-blank.jsx +152 -40
  66. package/src/index.js +10 -1
  67. package/src/inline-dropdown.jsx +2 -0
  68. package/src/mask.jsx +71 -25
  69. package/src/serialization.js +22 -34
  70. package/src/with-mask.jsx +43 -3
  71. package/README.md +0 -14
  72. package/lib/new-serialization.js +0 -267
  73. package/lib/new-serialization.js.map +0 -1
  74. package/lib/parse-html.js +0 -17
  75. package/lib/parse-html.js.map +0 -1
  76. package/lib/test-serializer.js +0 -164
  77. package/lib/test-serializer.js.map +0 -1
  78. package/src/new-serialization.jsx +0 -291
  79. package/src/parse-html.js +0 -8
  80. package/src/test-serializer.js +0 -163
@@ -1,10 +1,152 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import Select from '@material-ui/core/Select';
4
- import MenuItem from '@material-ui/core/MenuItem';
5
- import CorrectInput from './correct-input';
6
- import { withStyles } from '@material-ui/core/styles';
3
+ import Button from '@mui/material/Button';
4
+ import InputLabel from '@mui/material/InputLabel';
5
+ import Menu from '@mui/material/Menu';
6
+ import MenuItem from '@mui/material/MenuItem';
7
+ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
8
+ import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp';
9
+ import Close from '@mui/icons-material/Close';
10
+ import Check from '@mui/icons-material/Check';
11
+ import { styled } from '@mui/material/styles';
12
+
7
13
  import { color } from '@pie-lib/render-ui';
14
+ import { renderMath } from '@pie-lib/math-rendering';
15
+
16
+ const StyledButton = styled(Button)(() => ({
17
+ color: color.text(),
18
+ border: `1px solid ${color.borderGray()}`,
19
+ borderRadius: '4px',
20
+ justifyContent: 'space-between',
21
+ backgroundColor: color.background(),
22
+ position: 'relative',
23
+ height: '45px',
24
+ width: 'fit-content',
25
+ margin: '2px',
26
+ textTransform: 'none',
27
+ '& span': {
28
+ paddingRight: '5px',
29
+ },
30
+ '& svg': {
31
+ position: 'absolute',
32
+ right: 0,
33
+ top: 'calc(50% - 12px)',
34
+ pointerEvents: 'none',
35
+ color: color.text(),
36
+ marginLeft: '5px',
37
+ },
38
+ '&.Mui-focused': {
39
+ outline: `3px solid ${color.tertiary()}`,
40
+ outlineOffset: '2px',
41
+ borderWidth: '3px',
42
+ },
43
+ '&.disabledCorrect': {
44
+ borderWidth: '2px',
45
+ borderColor: color.correct(),
46
+ color: `${color.text()} !important`,
47
+ },
48
+ '&.disabledIncorrect': {
49
+ borderWidth: '2px',
50
+ borderColor: color.incorrectWithIcon(),
51
+ color: `${color.text()} !important`,
52
+ },
53
+ }));
54
+
55
+ const StyledMenu = styled(Menu)(() => ({
56
+ backgroundColor: color.background(),
57
+ border: `1px solid ${color.correct()} !important`,
58
+ '&:hover': {
59
+ border: `1px solid ${color.text()} `,
60
+ borderColor: 'initial',
61
+ },
62
+ '&:focus': {
63
+ border: `1px solid ${color.text()}`,
64
+ borderColor: 'initial',
65
+ },
66
+ // remove default padding on the inner list
67
+ '& .MuiList-root': {
68
+ padding: 0,
69
+ },
70
+ }));
71
+
72
+ const StyledMenuItem = styled(MenuItem)(() => ({
73
+ color: color.text(),
74
+ backgroundColor: color.background(),
75
+ '&.Mui-focused': {
76
+ outline: `3px solid ${color.tertiary()}`,
77
+ outlineOffset: '-1px', // keeps it inside the item
78
+ color: color.text(),
79
+ backgroundColor: color.background(),
80
+ },
81
+ '&:hover': {
82
+ color: color.text(),
83
+ backgroundColor: color.dropdownBackground(),
84
+ },
85
+ boxSizing: 'border-box',
86
+ padding: '25px',
87
+ borderRadius: '4px',
88
+ '&.selected': {
89
+ color: `${color.text()} !important`,
90
+ backgroundColor: `${color.background()} !important`,
91
+ '&:hover': {
92
+ color: color.text(),
93
+ backgroundColor: `${color.dropdownBackground()} !important`,
94
+ },
95
+ },
96
+ }));
97
+
98
+ const StyledLabel = styled('span')(() => ({
99
+ fontSize: 'max(1rem, 14px)',
100
+ }));
101
+
102
+ const StyledSelectedIndicator = styled('span')(() => ({
103
+ fontSize: 'max(1rem, 14px)',
104
+ position: 'absolute',
105
+ right: '10px',
106
+ }));
107
+
108
+ const StyledInputLabel = styled(InputLabel)(() => ({
109
+ position: 'absolute',
110
+ left: '-10000px',
111
+ top: 'auto',
112
+ width: '1px',
113
+ height: '1px',
114
+ overflow: 'hidden',
115
+ }));
116
+
117
+ const StyledCorrectnessIcon = styled(Check)(() => ({
118
+ color: `${color.white()} !important`,
119
+ position: 'absolute',
120
+ top: '-8px !important',
121
+ left: '-8px',
122
+ marginLeft: '0 !important',
123
+ borderRadius: '50%',
124
+ fontSize: '16px',
125
+ padding: '2px',
126
+ '&.correct': {
127
+ backgroundColor: color.correct(),
128
+ },
129
+ '&.incorrect': {
130
+ backgroundColor: color.incorrectWithIcon(),
131
+ },
132
+ }));
133
+
134
+ const StyledIncorrectnessIcon = styled(Close)(() => ({
135
+ color: `${color.white()} !important`,
136
+ position: 'absolute',
137
+ top: '-8px !important',
138
+ left: '-8px',
139
+ marginLeft: '0 !important',
140
+ borderRadius: '50%',
141
+ fontSize: '16px',
142
+ padding: '2px',
143
+ '&.correct': {
144
+ backgroundColor: color.correct(),
145
+ },
146
+ '&.incorrect': {
147
+ backgroundColor: color.incorrectWithIcon(),
148
+ },
149
+ }));
8
150
 
9
151
  class Dropdown extends React.Component {
10
152
  static propTypes = {
@@ -12,149 +154,253 @@ class Dropdown extends React.Component {
12
154
  value: PropTypes.string,
13
155
  disabled: PropTypes.bool,
14
156
  onChange: PropTypes.func,
15
- classes: PropTypes.object,
16
157
  correct: PropTypes.bool,
17
158
  choices: PropTypes.arrayOf(PropTypes.shape({ value: PropTypes.string, label: PropTypes.string })),
18
159
  showCorrectAnswer: PropTypes.bool,
160
+ singleQuery: PropTypes.bool,
161
+ correctValue: PropTypes.string,
19
162
  };
20
163
 
21
164
  constructor(props) {
22
165
  super(props);
23
166
 
24
167
  this.state = {
25
- showCheckmark: false,
26
- open: false,
168
+ anchorEl: null,
169
+ highlightedOptionId: null,
170
+ menuWidth: null,
171
+ previewValue: null,
27
172
  };
173
+ this.hiddenRef = React.createRef();
174
+ this.buttonRef = React.createRef();
175
+ this.previewRef = React.createRef();
176
+ this.elementRefs = [];
28
177
  }
29
178
 
30
- showCheckmarkAndOpen = () => {
31
- this.setState({
32
- showCheckmark: true,
33
- open: true,
34
- });
179
+ componentDidMount() {
180
+ // measure hidden menu width once
181
+ if (this.hiddenRef.current && this.state.menuWidth === null) {
182
+ this.setState({ menuWidth: this.hiddenRef.current.clientWidth });
183
+ }
184
+ }
185
+
186
+ componentDidUpdate(prevProps, prevState) {
187
+ const hiddenEl = this.hiddenRef.current;
188
+
189
+ const dropdownJustOpened = !prevState.anchorEl && this.state.anchorEl;
190
+ if (dropdownJustOpened) {
191
+ this.elementRefs.forEach((ref) => {
192
+ if (!ref) return;
193
+
194
+ const containsLatex = ref.querySelector('[data-latex], [data-raw]');
195
+ const hasMathJax = ref.querySelector('mjx-container');
196
+ const mathHandled = ref.querySelector('[data-math-handled="true"]');
197
+
198
+ if (containsLatex && (!mathHandled || !hasMathJax)) {
199
+ renderMath(ref);
200
+ }
201
+ });
202
+ }
203
+
204
+ if (hiddenEl) {
205
+ const newWidth = hiddenEl.clientWidth;
206
+ if (newWidth !== this.state.menuWidth) {
207
+ this.elementRefs.forEach((ref) => {
208
+ if (ref) renderMath(ref);
209
+ });
210
+
211
+ renderMath(hiddenEl);
212
+ this.setState({ menuWidth: newWidth });
213
+ }
214
+ }
215
+ }
216
+
217
+ handleClick = (event) => this.setState({ anchorEl: event.currentTarget });
218
+
219
+ handleClose = () => {
220
+ const { value } = this.props;
221
+ this.setState({ anchorEl: null, previewValue: null, highlightedOptionId: null });
222
+ // clear displayed preview if no selection
223
+ if (!value && this.previewRef.current) {
224
+ this.previewRef.current.innerHTML = '';
225
+ }
226
+ };
227
+
228
+ handleHighlight = (index) => {
229
+ const highlightedOptionId = `dropdown-option-${this.props.id}-${index}`;
230
+
231
+ // preview on hover if nothing selected
232
+ const stateUpdate = { highlightedOptionId };
233
+ if (!this.props.value) {
234
+ stateUpdate.previewValue = this.props.choices[index].value;
235
+ }
236
+ this.setState(stateUpdate);
237
+ };
238
+
239
+ handleSelect = (value, index) => {
240
+ this.props.onChange(this.props.id, value);
241
+ this.handleHighlight(index);
242
+ this.handleClose();
35
243
  };
36
244
 
37
- hideCheckmarkAndClose = () => {
38
- this.setState({
39
- showCheckmark: false,
40
- open: false,
245
+ handleHover = (index) => {
246
+ const selectedValue = this.props.value;
247
+
248
+ if (selectedValue) return;
249
+
250
+ const highlightedOptionId = `dropdown-option-${this.props.id}-${index}`;
251
+ const previewValue = this.state.previewValue;
252
+
253
+ this.setState({ highlightedOptionId, previewValue }, () => {
254
+ // On hover, preview the math-rendered content inside the button if no value is selected.
255
+ const ref = this.elementRefs[index];
256
+ const preview = this.previewRef.current;
257
+
258
+ if (ref && preview) {
259
+ preview.innerHTML = ref.innerHTML;
260
+ }
41
261
  });
42
262
  };
43
263
 
264
+ getLabel(choices, value) {
265
+ const found = (choices || []).find((choice) => choice.value === value);
266
+
267
+ return found ? found.label.trim() : undefined;
268
+ }
269
+
44
270
  render() {
45
- const { classes, id, correct, disabled, value, onChange, choices, showCorrectAnswer } = this.props;
271
+ const { id, correct, disabled, value, choices, showCorrectAnswer, singleQuery, correctValue } = this.props;
272
+ const { anchorEl } = this.state;
273
+ const open = Boolean(anchorEl);
274
+ const buttonId = `dropdown-button-${id}`;
275
+ const menuId = `dropdown-menu-${id}`;
276
+ const valueDisplayId = `dropdown-value-${id}`;
277
+
278
+ // Determine the class for disabled state, view mode and evaluate mode
279
+ let disabledClass;
280
+ // Reset elementRefs before each render to avoid stale references
281
+ this.elementRefs = [];
46
282
 
47
- const { showCheckmark, open } = this.state;
283
+ if (disabled && correct !== undefined) {
284
+ disabledClass = correct || showCorrectAnswer ? 'disabledCorrect' : 'disabledIncorrect';
285
+ }
286
+
287
+ // Create distinct, visually hidden labels for each dropdown
288
+ const incrementedId = parseInt(id, 10) + 1;
289
+ const labelId = singleQuery ? 'Query-label' : `Query-label-${incrementedId}`;
290
+ const labelText = singleQuery ? 'Query' : `Query ${incrementedId}`;
291
+
292
+ // Changed from Select to Button for dropdown to enhance accessibility. This modification offers explicit control over aria attributes and focuses management, ensuring the dropdown is compliant with accessibility standards. The use of Button and Menu components allows for better handling of keyboard interactions and provides accessible labels and menus, aligning with WCAG guidelines and improving usability for assistive technology users.
293
+ let correctnessIcon = null;
294
+ if (disabled && correct !== undefined) {
295
+ correctnessIcon =
296
+ correct || showCorrectAnswer ? (
297
+ <StyledCorrectnessIcon className="correct" />
298
+ ) : (
299
+ <StyledIncorrectnessIcon className="incorrect" />
300
+ );
301
+ }
48
302
 
49
303
  return (
50
- <Select
51
- classes={{
52
- root: classes.root,
53
- icon: classes.icon,
54
- selectMenu: classes.selectMenu,
55
- select: classes.select,
56
- }}
57
- disabled={disabled}
58
- value={value || ''}
59
- onOpen={this.showCheckmarkAndOpen}
60
- onClose={this.hideCheckmarkAndClose}
61
- open={open}
62
- input={<CorrectInput correct={showCorrectAnswer || correct} />}
63
- MenuProps={{
64
- keepMounted: true,
65
- disablePortal: true,
66
- }}
67
- onChange={(e) => {
68
- onChange(id, e.target.value);
69
- }}
70
- >
71
- {(choices || []).map((c, index) => (
72
- <MenuItem
73
- classes={{ root: classes.menuRoot, selected: classes.selected }}
74
- key={`${c.label}-${index}`}
75
- value={c.value}
76
- >
77
- <span
78
- className={classes.label}
79
- dangerouslySetInnerHTML={{
80
- __html: c.label,
81
- }}
82
- />
83
- {showCheckmark && (
84
- <span
85
- className={classes.label}
86
- dangerouslySetInnerHTML={{ __html: c.value === value ? ' &check;' : '' }}
87
- />
88
- )}
89
- </MenuItem>
90
- ))}
91
- </Select>
304
+ <>
305
+ <div
306
+ ref={this.hiddenRef}
307
+ style={{ position: 'absolute', visibility: 'hidden', top: 0, left: 0 }}
308
+ tabIndex={-1}
309
+ aria-hidden="true"
310
+ >
311
+ {(choices || []).map((c, index) => (
312
+ <StyledMenuItem
313
+ key={index}
314
+ tabIndex={-1}
315
+ aria-hidden="true"
316
+ >
317
+ <StyledLabel dangerouslySetInnerHTML={{ __html: c.label }} />
318
+ </StyledMenuItem>
319
+ ))}
320
+ </div>
321
+ <StyledInputLabel id={labelId} tabIndex={-1} aria-hidden="true">
322
+ {labelText}
323
+ </StyledInputLabel>
324
+ <StyledButton
325
+ ref={this.buttonRef}
326
+ style={{
327
+ ...(this.state.menuWidth && { minWidth: `calc(${this.state.menuWidth}px + 8px)` }),
328
+ borderWidth: open ? '2px' : '1px',
329
+ transition: 'border-width 0.2s ease-in-out',
330
+ }}
331
+ aria-controls={open ? menuId : undefined}
332
+ aria-haspopup="listbox"
333
+ aria-expanded={open ? 'true' : undefined}
334
+ aria-activedescendant={this.state.highlightedOptionId}
335
+ onClick={this.handleClick}
336
+ className={disabledClass}
337
+ disabled={disabled}
338
+ id={buttonId}
339
+ role="combobox"
340
+ aria-label={`Select an option for ${labelText}`}
341
+ aria-labelledby={valueDisplayId}
342
+ >
343
+ {correctnessIcon}
344
+ <StyledLabel
345
+ id={valueDisplayId}
346
+ ref={this.previewRef}
347
+ dangerouslySetInnerHTML={{
348
+ __html: correctValue
349
+ ? correctValue
350
+ : open && this.state.previewValue
351
+ ? this.getLabel(choices, this.state.previewValue)
352
+ : this.getLabel(choices, value) || '',
353
+ }}
354
+ />
355
+ {open ? <ArrowDropUpIcon /> : <ArrowDropDownIcon />}
356
+ </StyledButton>
357
+ <StyledMenu
358
+ id={menuId}
359
+ anchorEl={anchorEl}
360
+ keepMounted
361
+ open={open}
362
+ onClose={this.handleClose}
363
+ getContentAnchorEl={null}
364
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
365
+ transformOrigin={{ vertical: 'top', horizontal: 'left' }}
366
+ transitionDuration={{ enter: 225, exit: 195 }}
367
+ slotProps={{
368
+ paper: this.state.menuWidth ? { style: { minWidth: this.state.menuWidth, padding: '4px' } } : undefined,
369
+ list: {
370
+ 'aria-labelledby': buttonId,
371
+ role: 'listbox',
372
+ disablePadding: true,
373
+ },
374
+ }}
375
+ >
376
+ {(choices || []).map((c, index) => {
377
+ const optionId = `dropdown-option-${id}-${index}`;
378
+
379
+ return (
380
+ <StyledMenuItem
381
+ id={optionId}
382
+ className={c.value === value ? 'selected' : ''}
383
+ key={`${c.label}-${index}`}
384
+ value={c.value}
385
+ onClick={() => this.handleSelect(c.value, index)}
386
+ role="option"
387
+ aria-selected={this.state.highlightedOptionId === optionId ? 'true' : undefined}
388
+ onMouseOver={() => this.handleHover(index)}
389
+ >
390
+ <StyledLabel
391
+ ref={(ref) => (this.elementRefs[index] = ref)}
392
+ dangerouslySetInnerHTML={{ __html: c.label }}
393
+ />
394
+ <StyledSelectedIndicator
395
+ dangerouslySetInnerHTML={{ __html: c.value === value ? ' &check;' : '' }}
396
+ />
397
+ </StyledMenuItem>
398
+ );
399
+ })}
400
+ </StyledMenu>
401
+ </>
92
402
  );
93
403
  }
94
404
  }
95
405
 
96
- const styles = () => ({
97
- root: {
98
- color: color.text(),
99
- backgroundColor: color.background(),
100
- borderColor: color.secondaryLight(),
101
- '& ul': {
102
- paddingTop: 0,
103
- paddingBottom: 0,
104
- border: `1px solid ${color.text()}`,
105
- borderRadius: '5px',
106
- color: color.text(),
107
- backgroundColor: color.background(),
108
- },
109
- },
110
- select: {
111
- '&:focus': {
112
- borderRadius: '4px',
113
- },
114
- },
115
- selectMenu: {
116
- backgroundColor: color.background(),
117
- '&:hover': {
118
- borderColor: 'initial',
119
- },
120
- '&:focus': {
121
- borderColor: 'initial',
122
- },
123
- },
124
- icon: {
125
- color: color.text(),
126
- },
127
- selected: {
128
- color: `${color.text()} !important`,
129
- backgroundColor: `${color.background()} !important`,
130
- '&:hover': {
131
- color: color.text(),
132
- backgroundColor: `${color.secondaryLight()} !important`,
133
- },
134
- },
135
- menuRoot: {
136
- color: color.text(),
137
- backgroundColor: color.background(),
138
- '&:focus': {
139
- color: color.text(),
140
- backgroundColor: color.background(),
141
- },
142
- '&:hover': {
143
- color: color.text(),
144
- backgroundColor: color.secondaryLight(),
145
- },
146
- boxSizing: 'border-box',
147
- padding: '25px',
148
- '&:first-of-type': {
149
- borderRadius: '3px 3px 0 0',
150
- },
151
- '&:last-of-type': {
152
- borderRadius: '0 0 3px 3px',
153
- },
154
- },
155
- label: {
156
- fontSize: 'max(1rem, 14px)',
157
- },
158
- });
159
-
160
- export default withStyles(styles)(Dropdown);
406
+ export default Dropdown;
@@ -1,34 +1,87 @@
1
1
  import React from 'react';
2
- import Input from './components/input';
2
+ import { styled } from '@mui/material/styles';
3
+ import classnames from 'classnames';
4
+
5
+ import { color } from '@pie-lib/render-ui';
3
6
  import { withMask } from './with-mask';
7
+ //import EditableHtml from '@pie-lib/editable-html-tip-tap';
4
8
 
5
- // eslint-disable-next-line react/display-name
6
- export default withMask('input', (props) => (node, data, onChange) => {
7
- const dataset = node.data ? node.data.dataset || {} : {};
8
- if (dataset.component === 'input') {
9
- // eslint-disable-next-line react/prop-types
10
- const { adjustedLimit, disabled, feedback, showCorrectAnswer, maxLength, spellCheck } = props;
9
+ let EditableHtml;
10
+ let StyledEditableHtml;
11
+
12
+ // - mathquill error window not defined
13
+ if (typeof window !== 'undefined') {
14
+ EditableHtml = require('@pie-lib/editable-html-tip-tap')['default'];
15
+ StyledEditableHtml = styled(EditableHtml)(() => ({
16
+ display: 'inline-block',
17
+ verticalAlign: 'middle',
18
+ margin: '4px',
19
+ borderRadius: '4px',
20
+ border: `1px solid ${color.black()}`,
21
+ '&.correct': {
22
+ border: `1px solid ${color.correct()}`,
23
+ },
24
+ '&.incorrect': {
25
+ border: `1px solid ${color.incorrect()}`,
26
+ },
27
+ }));
28
+ }
11
29
 
12
- // the first answer is the correct one
13
- // eslint-disable-next-line react/prop-types
30
+ const MaskedInput = (props) => (node, data) => {
31
+ const { adjustedLimit, disabled, feedback, showCorrectAnswer, maxLength, spellCheck, pluginProps, onChange } = props;
32
+ const dataset = node.data?.dataset || {};
33
+
34
+ if (dataset.component === 'input') {
14
35
  const correctAnswer = ((props.choices && dataset && props.choices[dataset.id]) || [])[0];
15
36
  const finalValue = showCorrectAnswer ? correctAnswer && correctAnswer.label : data[dataset.id] || '';
16
37
  const width = maxLength && maxLength[dataset.id];
38
+ const feedbackStatus = feedback && feedback[dataset.id];
39
+ const isCorrect = showCorrectAnswer || feedbackStatus === 'correct';
40
+ const isIncorrect = !showCorrectAnswer && feedbackStatus === 'incorrect';
41
+
42
+ const handleInputChange = (newValue) => {
43
+ const updatedValue = {
44
+ ...data,
45
+ [dataset.id]: newValue,
46
+ };
47
+ onChange(updatedValue);
48
+ };
49
+
50
+ const handleKeyDown = (event) => {
51
+ // the keyCode value for the Enter/Return key is 13
52
+ if (event.key === 'Enter' || event.keyCode === 13) {
53
+ return true;
54
+ }
55
+ };
17
56
 
18
57
  return (
19
- <Input
58
+ <StyledEditableHtml
59
+ id={dataset.id}
20
60
  key={`${node.type}-input-${dataset.id}`}
21
- correct={feedback && feedback[dataset.id] && feedback[dataset.id] === 'correct'}
22
61
  disabled={showCorrectAnswer || disabled}
23
- value={finalValue}
24
- id={dataset.id}
25
- onChange={onChange}
26
- showCorrectAnswer={showCorrectAnswer}
27
- width={width}
62
+ disableUnderline
63
+ onChange={handleInputChange}
64
+ markup={finalValue || ''}
28
65
  charactersLimit={adjustedLimit ? width : 25}
29
- isConstructedResponse={true}
66
+ activePlugins={['languageCharacters']}
67
+ pluginProps={pluginProps}
68
+ languageCharactersProps={[{ language: 'spanish' }]}
30
69
  spellCheck={spellCheck}
70
+ width={`calc(${width}em + 32px)`} // added 32px for left and right padding of editable-html
71
+ onKeyDown={handleKeyDown}
72
+ autoWidthToolbar
73
+ toolbarOpts={{
74
+ minWidth: 'auto',
75
+ noBorder: true,
76
+ isHidden: !!pluginProps?.characters?.disabled,
77
+ }}
78
+ className={classnames({
79
+ correct: isCorrect,
80
+ incorrect: isIncorrect,
81
+ })}
31
82
  />
32
83
  );
33
84
  }
34
- });
85
+ };
86
+
87
+ export default withMask('input', MaskedInput);
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ // import Input from './components/input';
3
+ import { withMask } from './with-mask';
4
+
5
+ // eslint-disable-next-line react/display-name
6
+ export default withMask('input', (props) => (node, data, onChange) => {
7
+ const dataset = node.data ? node.data.dataset || {} : {};
8
+ if (dataset.component === 'input') {
9
+ // eslint-disable-next-line react/prop-types
10
+ // const { adjustedLimit, disabled, feedback, showCorrectAnswer, maxLength, spellCheck } = props;
11
+
12
+ // the first answer is the correct one
13
+ // eslint-disable-next-line react/prop-types
14
+ // const correctAnswer = ((props.choices && dataset && props.choices[dataset.id]) || [])[0];
15
+ // const finalValue = showCorrectAnswer ? correctAnswer && correctAnswer.label : data[dataset.id] || '';
16
+ // const width = maxLength && maxLength[dataset.id];
17
+
18
+ return props.customMarkMarkupComponent(dataset.id);
19
+ // return (
20
+ // <Input
21
+ // key={`${node.type}-input-${dataset.id}`}
22
+ // correct={feedback && feedback[dataset.id] && feedback[dataset.id] === 'correct'}
23
+ // disabled={showCorrectAnswer || disabled}
24
+ // value={finalValue}
25
+ // id={dataset.id}
26
+ // onChange={onChange}
27
+ // showCorrectAnswer={showCorrectAnswer}
28
+ // width={width}
29
+ // charactersLimit={adjustedLimit ? width : 25}
30
+ // isConstructedResponse={true}
31
+ // spellCheck={spellCheck}
32
+ // />
33
+ // );
34
+ }
35
+ });