@pie-lib/mask-markup 0.1.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/dist/_virtual/_rolldown/runtime.js +4 -0
- package/dist/choices/choice.d.ts +24 -0
- package/dist/choices/choice.d.ts.map +1 -0
- package/dist/choices/choice.js +77 -0
- package/dist/choices/index.d.ts +26 -0
- package/dist/choices/index.d.ts.map +1 -0
- package/dist/choices/index.js +49 -0
- package/dist/componentize.d.ts +13 -0
- package/dist/componentize.d.ts.map +1 -0
- package/dist/componentize.js +4 -0
- package/dist/components/blank.d.ts +39 -0
- package/dist/components/blank.d.ts.map +1 -0
- package/dist/components/blank.js +236 -0
- package/dist/components/correct-input.d.ts +11 -0
- package/dist/components/correct-input.d.ts.map +1 -0
- package/dist/components/dropdown.d.ts +38 -0
- package/dist/components/dropdown.d.ts.map +1 -0
- package/dist/components/dropdown.js +309 -0
- package/dist/components/input.d.ts +37 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/constructed-response.d.ts +24 -0
- package/dist/constructed-response.d.ts.map +1 -0
- package/dist/constructed-response.js +55 -0
- package/dist/customizable.d.ts +24 -0
- package/dist/customizable.d.ts.map +1 -0
- package/dist/customizable.js +8 -0
- package/dist/drag-in-the-blank.d.ts +38 -0
- package/dist/drag-in-the-blank.d.ts.map +1 -0
- package/dist/drag-in-the-blank.js +164 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/inline-dropdown.d.ts +24 -0
- package/dist/inline-dropdown.d.ts.map +1 -0
- package/dist/inline-dropdown.js +24 -0
- package/dist/mask.d.ts +31 -0
- package/dist/mask.d.ts.map +1 -0
- package/dist/mask.js +98 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/index.js +17 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/cssPrefix.js +9 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/cssUnitless.js +26 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/hasOwn.js +11 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/isFunction.js +11 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/isObject.js +11 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/prefixInfo.js +24 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/prefixProperties.js +32 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/prefixer.js +29 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/stringUtils/camelize.js +14 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/stringUtils/hyphenRe.js +8 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/stringUtils/hyphenate.js +12 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/stringUtils/separate.js +11 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/stringUtils/toLowerFirst.js +10 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/stringUtils/toUpperFirst.js +10 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/toStyleObject.js +55 -0
- package/dist/node_modules/.bun/to-style@1.3.3/node_modules/to-style/src/toStyleString.js +16 -0
- package/dist/serialization.d.ts +35 -0
- package/dist/serialization.d.ts.map +1 -0
- package/dist/serialization.js +132 -0
- package/dist/with-mask.d.ts +35 -0
- package/dist/with-mask.d.ts.map +1 -0
- package/dist/with-mask.js +45 -0
- package/package.json +44 -0
- package/src/choices/choice.tsx +107 -0
- package/src/choices/index.tsx +74 -0
- package/src/componentize.tsx +23 -0
- package/src/components/blank.tsx +396 -0
- package/src/components/correct-input.tsx +92 -0
- package/src/components/dropdown.tsx +448 -0
- package/src/components/input.tsx +58 -0
- package/src/constructed-response.tsx +91 -0
- package/src/customizable.tsx +44 -0
- package/src/drag-in-the-blank.tsx +251 -0
- package/src/index.ts +26 -0
- package/src/inline-dropdown.tsx +39 -0
- package/src/mask.tsx +178 -0
- package/src/serialization.ts +270 -0
- package/src/with-mask.tsx +85 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @synced-from pie-lib/packages/mask-markup/src/components/dropdown.jsx
|
|
4
|
+
* @auto-generated
|
|
5
|
+
*
|
|
6
|
+
* This file is automatically synced from pie-elements and converted to TypeScript.
|
|
7
|
+
* Manual edits will be overwritten on next sync.
|
|
8
|
+
* To make changes, edit the upstream JavaScript file and run sync again.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import PropTypes from 'prop-types';
|
|
13
|
+
import Button from '@mui/material/Button';
|
|
14
|
+
import InputLabel from '@mui/material/InputLabel';
|
|
15
|
+
import { InlineMenu as MenuImport } from '@pie-lib/render-ui';
|
|
16
|
+
|
|
17
|
+
function isRenderableReactInteropType(value: any) {
|
|
18
|
+
return (
|
|
19
|
+
typeof value === 'function' ||
|
|
20
|
+
(typeof value === 'object' && value !== null && typeof value.$$typeof === 'symbol')
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function unwrapReactInteropSymbol(maybeSymbol: any, namedExport?: string) {
|
|
25
|
+
if (!maybeSymbol) return maybeSymbol;
|
|
26
|
+
if (isRenderableReactInteropType(maybeSymbol)) return maybeSymbol;
|
|
27
|
+
if (isRenderableReactInteropType(maybeSymbol.default)) return maybeSymbol.default;
|
|
28
|
+
if (namedExport && isRenderableReactInteropType(maybeSymbol[namedExport])) {
|
|
29
|
+
return maybeSymbol[namedExport];
|
|
30
|
+
}
|
|
31
|
+
if (namedExport && isRenderableReactInteropType(maybeSymbol[namedExport]?.default)) {
|
|
32
|
+
return maybeSymbol[namedExport].default;
|
|
33
|
+
}
|
|
34
|
+
return maybeSymbol;
|
|
35
|
+
}
|
|
36
|
+
const Menu = unwrapReactInteropSymbol(MenuImport, 'InlineMenu') || unwrapReactInteropSymbol(renderUi.InlineMenu, 'InlineMenu');
|
|
37
|
+
import * as RenderUiNamespace from '@pie-lib/render-ui';
|
|
38
|
+
const renderUiNamespaceAny = RenderUiNamespace as any;
|
|
39
|
+
const renderUiDefaultMaybe = renderUiNamespaceAny['default'];
|
|
40
|
+
const renderUi =
|
|
41
|
+
renderUiDefaultMaybe && typeof renderUiDefaultMaybe === 'object'
|
|
42
|
+
? renderUiDefaultMaybe
|
|
43
|
+
: renderUiNamespaceAny;
|
|
44
|
+
import MenuItem from '@mui/material/MenuItem';
|
|
45
|
+
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
|
|
46
|
+
import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp';
|
|
47
|
+
import Close from '@mui/icons-material/Close';
|
|
48
|
+
import Check from '@mui/icons-material/Check';
|
|
49
|
+
import { styled } from '@mui/material/styles';
|
|
50
|
+
|
|
51
|
+
import { color } from '@pie-lib/render-ui';
|
|
52
|
+
import { renderMath } from '@pie-element/shared-math-rendering-mathjax';
|
|
53
|
+
|
|
54
|
+
const StyledButton: any = styled(Button)(() => ({
|
|
55
|
+
color: color.text(),
|
|
56
|
+
border: `1px solid ${color.borderGray()}`,
|
|
57
|
+
borderRadius: '4px',
|
|
58
|
+
justifyContent: 'space-between',
|
|
59
|
+
backgroundColor: color.background(),
|
|
60
|
+
position: 'relative',
|
|
61
|
+
height: '45px',
|
|
62
|
+
width: 'fit-content',
|
|
63
|
+
margin: '2px',
|
|
64
|
+
textTransform: 'none',
|
|
65
|
+
'& span': {
|
|
66
|
+
paddingRight: '5px',
|
|
67
|
+
},
|
|
68
|
+
'& svg': {
|
|
69
|
+
position: 'absolute',
|
|
70
|
+
right: 0,
|
|
71
|
+
top: 'calc(50% - 12px)',
|
|
72
|
+
pointerEvents: 'none',
|
|
73
|
+
color: color.text(),
|
|
74
|
+
marginLeft: '5px',
|
|
75
|
+
},
|
|
76
|
+
'&.Mui-focused': {
|
|
77
|
+
outline: `3px solid ${color.tertiary()}`,
|
|
78
|
+
outlineOffset: '2px',
|
|
79
|
+
borderWidth: '3px',
|
|
80
|
+
},
|
|
81
|
+
'&.disabledCorrect': {
|
|
82
|
+
borderWidth: '2px',
|
|
83
|
+
borderColor: color.correct(),
|
|
84
|
+
color: `${color.text()} !important`,
|
|
85
|
+
},
|
|
86
|
+
'&.disabledIncorrect': {
|
|
87
|
+
borderWidth: '2px',
|
|
88
|
+
borderColor: color.incorrectWithIcon(),
|
|
89
|
+
color: `${color.text()} !important`,
|
|
90
|
+
},
|
|
91
|
+
}));
|
|
92
|
+
|
|
93
|
+
const StyledMenu: any = styled(Menu)(() => ({
|
|
94
|
+
backgroundColor: color.background(),
|
|
95
|
+
border: `1px solid ${color.correct()} !important`,
|
|
96
|
+
'&:hover': {
|
|
97
|
+
border: `1px solid ${color.text()} `,
|
|
98
|
+
borderColor: 'initial',
|
|
99
|
+
},
|
|
100
|
+
'&:focus': {
|
|
101
|
+
border: `1px solid ${color.text()}`,
|
|
102
|
+
borderColor: 'initial',
|
|
103
|
+
},
|
|
104
|
+
// remove default padding on the inner list
|
|
105
|
+
'& .MuiList-root': {
|
|
106
|
+
padding: 0,
|
|
107
|
+
},
|
|
108
|
+
}));
|
|
109
|
+
|
|
110
|
+
const StyledMenuItem: any = styled(MenuItem)(() => ({
|
|
111
|
+
// base text/layout styles (from old JSS - before mui v5 migration)
|
|
112
|
+
height: 24,
|
|
113
|
+
overflow: 'hidden',
|
|
114
|
+
fontSize: '1rem',
|
|
115
|
+
fontWeight: 400,
|
|
116
|
+
fontFamily: 'inherit',
|
|
117
|
+
lineHeight: '1.5em',
|
|
118
|
+
whiteSpace: 'nowrap',
|
|
119
|
+
|
|
120
|
+
// custom styles
|
|
121
|
+
color: color.text(),
|
|
122
|
+
backgroundColor: color.background(),
|
|
123
|
+
'&.Mui-focusVisible': {
|
|
124
|
+
outline: `3px solid ${color.tertiary()}`,
|
|
125
|
+
outlineOffset: '-1px', // keeps it inside the item
|
|
126
|
+
color: color.text(),
|
|
127
|
+
backgroundColor: color.background(),
|
|
128
|
+
},
|
|
129
|
+
'&:hover': {
|
|
130
|
+
color: color.text(),
|
|
131
|
+
backgroundColor: color.dropdownBackground(),
|
|
132
|
+
},
|
|
133
|
+
boxSizing: 'border-box',
|
|
134
|
+
padding: '25px',
|
|
135
|
+
borderRadius: '4px',
|
|
136
|
+
'&.selected': {
|
|
137
|
+
color: `${color.text()} !important`,
|
|
138
|
+
backgroundColor: `${color.background()} !important`,
|
|
139
|
+
'&:hover': {
|
|
140
|
+
color: color.text(),
|
|
141
|
+
backgroundColor: `${color.dropdownBackground()} !important`,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
}));
|
|
145
|
+
|
|
146
|
+
const StyledLabel: any = styled('span')(() => ({
|
|
147
|
+
fontSize: 'max(1rem, 14px)',
|
|
148
|
+
}));
|
|
149
|
+
|
|
150
|
+
const StyledSelectedIndicator: any = styled('span')(() => ({
|
|
151
|
+
fontSize: 'max(1rem, 14px)',
|
|
152
|
+
position: 'absolute',
|
|
153
|
+
right: '10px',
|
|
154
|
+
}));
|
|
155
|
+
|
|
156
|
+
const StyledInputLabel: any = styled(InputLabel)(() => ({
|
|
157
|
+
position: 'absolute',
|
|
158
|
+
left: '-10000px',
|
|
159
|
+
top: 'auto',
|
|
160
|
+
width: '1px',
|
|
161
|
+
height: '1px',
|
|
162
|
+
overflow: 'hidden',
|
|
163
|
+
}));
|
|
164
|
+
|
|
165
|
+
const StyledCorrectnessIcon: any = styled(Check)(() => ({
|
|
166
|
+
color: `${color.white()} !important`,
|
|
167
|
+
position: 'absolute',
|
|
168
|
+
top: '-8px !important',
|
|
169
|
+
left: '-8px',
|
|
170
|
+
marginLeft: '0 !important',
|
|
171
|
+
borderRadius: '50%',
|
|
172
|
+
fontSize: '16px',
|
|
173
|
+
padding: '2px',
|
|
174
|
+
'&.correct': {
|
|
175
|
+
backgroundColor: color.correct(),
|
|
176
|
+
},
|
|
177
|
+
'&.incorrect': {
|
|
178
|
+
backgroundColor: color.incorrectWithIcon(),
|
|
179
|
+
},
|
|
180
|
+
}));
|
|
181
|
+
|
|
182
|
+
const StyledIncorrectnessIcon: any = styled(Close)(() => ({
|
|
183
|
+
color: `${color.white()} !important`,
|
|
184
|
+
position: 'absolute',
|
|
185
|
+
top: '-8px !important',
|
|
186
|
+
left: '-8px',
|
|
187
|
+
marginLeft: '0 !important',
|
|
188
|
+
borderRadius: '50%',
|
|
189
|
+
fontSize: '16px',
|
|
190
|
+
padding: '2px',
|
|
191
|
+
'&.correct': {
|
|
192
|
+
backgroundColor: color.correct(),
|
|
193
|
+
},
|
|
194
|
+
'&.incorrect': {
|
|
195
|
+
backgroundColor: color.incorrectWithIcon(),
|
|
196
|
+
},
|
|
197
|
+
}));
|
|
198
|
+
|
|
199
|
+
class Dropdown extends React.Component {
|
|
200
|
+
static propTypes = {
|
|
201
|
+
id: PropTypes.string,
|
|
202
|
+
value: PropTypes.string,
|
|
203
|
+
disabled: PropTypes.bool,
|
|
204
|
+
onChange: PropTypes.func,
|
|
205
|
+
correct: PropTypes.bool,
|
|
206
|
+
choices: PropTypes.arrayOf(PropTypes.shape({ value: PropTypes.string, label: PropTypes.string })),
|
|
207
|
+
showCorrectAnswer: PropTypes.bool,
|
|
208
|
+
singleQuery: PropTypes.bool,
|
|
209
|
+
correctValue: PropTypes.string,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
constructor(props) {
|
|
213
|
+
super(props);
|
|
214
|
+
|
|
215
|
+
this.state = {
|
|
216
|
+
anchorEl: null,
|
|
217
|
+
highlightedOptionId: null,
|
|
218
|
+
menuWidth: null,
|
|
219
|
+
previewValue: null,
|
|
220
|
+
};
|
|
221
|
+
this.hiddenRef = React.createRef();
|
|
222
|
+
this.buttonRef = React.createRef();
|
|
223
|
+
this.previewRef = React.createRef();
|
|
224
|
+
this.elementRefs = [];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
componentDidMount() {
|
|
228
|
+
// measure hidden menu width once
|
|
229
|
+
if (this.hiddenRef.current && this.state.menuWidth === null) {
|
|
230
|
+
this.setState({ menuWidth: this.hiddenRef.current.clientWidth });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
componentDidUpdate(prevProps, prevState) {
|
|
235
|
+
const hiddenEl = this.hiddenRef.current;
|
|
236
|
+
|
|
237
|
+
const dropdownJustOpened = !prevState.anchorEl && this.state.anchorEl;
|
|
238
|
+
if (dropdownJustOpened) {
|
|
239
|
+
this.elementRefs.forEach((ref) => {
|
|
240
|
+
if (!ref) return;
|
|
241
|
+
|
|
242
|
+
const containsLatex = ref.querySelector('[data-latex], [data-raw]');
|
|
243
|
+
const hasMathJax = ref.querySelector('mjx-container');
|
|
244
|
+
const mathHandled = ref.querySelector('[data-math-handled="true"]');
|
|
245
|
+
|
|
246
|
+
if (containsLatex && (!mathHandled || !hasMathJax)) {
|
|
247
|
+
renderMath(ref);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (hiddenEl) {
|
|
253
|
+
const newWidth = hiddenEl.clientWidth;
|
|
254
|
+
if (newWidth !== this.state.menuWidth) {
|
|
255
|
+
this.elementRefs.forEach((ref) => {
|
|
256
|
+
if (ref) renderMath(ref);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
renderMath(hiddenEl);
|
|
260
|
+
this.setState({ menuWidth: newWidth });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
handleClick = (event) => this.setState({ anchorEl: event.currentTarget });
|
|
266
|
+
|
|
267
|
+
handleClose: any = () => {
|
|
268
|
+
const { value } = this.props;
|
|
269
|
+
this.setState({ anchorEl: null, previewValue: null, highlightedOptionId: null });
|
|
270
|
+
// clear displayed preview if no selection
|
|
271
|
+
if (!value && this.previewRef.current) {
|
|
272
|
+
this.previewRef.current.innerHTML = '';
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
handleHighlight: any = (index) => {
|
|
277
|
+
const highlightedOptionId = `dropdown-option-${this.props.id}-${index}`;
|
|
278
|
+
|
|
279
|
+
// preview on hover if nothing selected
|
|
280
|
+
const stateUpdate = { highlightedOptionId };
|
|
281
|
+
if (!this.props.value) {
|
|
282
|
+
stateUpdate.previewValue = this.props.choices[index].value;
|
|
283
|
+
}
|
|
284
|
+
this.setState(stateUpdate);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
handleSelect: any = (value, index) => {
|
|
288
|
+
this.props.onChange(this.props.id, value);
|
|
289
|
+
this.handleHighlight(index);
|
|
290
|
+
this.handleClose();
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
handleHover: any = (index) => {
|
|
294
|
+
const selectedValue = this.props.value;
|
|
295
|
+
|
|
296
|
+
if (selectedValue) return;
|
|
297
|
+
|
|
298
|
+
const highlightedOptionId = `dropdown-option-${this.props.id}-${index}`;
|
|
299
|
+
const previewValue = this.state.previewValue;
|
|
300
|
+
|
|
301
|
+
this.setState({ highlightedOptionId, previewValue }, () => {
|
|
302
|
+
// On hover, preview the math-rendered content inside the button if no value is selected.
|
|
303
|
+
const ref = this.elementRefs[index];
|
|
304
|
+
const preview = this.previewRef.current;
|
|
305
|
+
|
|
306
|
+
if (ref && preview) {
|
|
307
|
+
preview.innerHTML = ref.innerHTML;
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
getLabel(choices, value) {
|
|
313
|
+
const found = (choices || []).find((choice) => choice.value === value);
|
|
314
|
+
|
|
315
|
+
return found ? found.label.trim() : undefined;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
render() {
|
|
319
|
+
const { id, correct, disabled, value, choices, showCorrectAnswer, singleQuery, correctValue } = this.props;
|
|
320
|
+
const { anchorEl } = this.state;
|
|
321
|
+
const open = Boolean(anchorEl);
|
|
322
|
+
const buttonId = `dropdown-button-${id}`;
|
|
323
|
+
const menuId = `dropdown-menu-${id}`;
|
|
324
|
+
const valueDisplayId = `dropdown-value-${id}`;
|
|
325
|
+
|
|
326
|
+
// Determine the class for disabled state, view mode and evaluate mode
|
|
327
|
+
let disabledClass;
|
|
328
|
+
// Reset elementRefs before each render to avoid stale references
|
|
329
|
+
this.elementRefs = [];
|
|
330
|
+
|
|
331
|
+
if (disabled && correct !== undefined) {
|
|
332
|
+
disabledClass = correct || showCorrectAnswer ? 'disabledCorrect' : 'disabledIncorrect';
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Create distinct, visually hidden labels for each dropdown
|
|
336
|
+
const incrementedId = parseInt(id, 10) + 1;
|
|
337
|
+
const labelId = singleQuery ? 'Query-label' : `Query-label-${incrementedId}`;
|
|
338
|
+
const labelText = singleQuery ? 'Query' : `Query ${incrementedId}`;
|
|
339
|
+
|
|
340
|
+
// 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.
|
|
341
|
+
let correctnessIcon = null;
|
|
342
|
+
if (disabled && correct !== undefined) {
|
|
343
|
+
correctnessIcon =
|
|
344
|
+
correct || showCorrectAnswer ? (
|
|
345
|
+
<StyledCorrectnessIcon className="correct" />
|
|
346
|
+
) : (
|
|
347
|
+
<StyledIncorrectnessIcon className="incorrect" />
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return (
|
|
352
|
+
<>
|
|
353
|
+
<div
|
|
354
|
+
ref={this.hiddenRef}
|
|
355
|
+
style={{ position: 'absolute', visibility: 'hidden', top: 0, left: 0 }}
|
|
356
|
+
tabIndex={-1}
|
|
357
|
+
aria-hidden="true"
|
|
358
|
+
>
|
|
359
|
+
{(choices || []).map((c, index) => (
|
|
360
|
+
<StyledMenuItem key={index} tabIndex={-1} aria-hidden="true">
|
|
361
|
+
<StyledLabel dangerouslySetInnerHTML={{ __html: c.label }} />
|
|
362
|
+
</StyledMenuItem>
|
|
363
|
+
))}
|
|
364
|
+
</div>
|
|
365
|
+
<StyledInputLabel id={labelId} tabIndex={-1} aria-hidden="true">
|
|
366
|
+
{labelText}
|
|
367
|
+
</StyledInputLabel>
|
|
368
|
+
<StyledButton
|
|
369
|
+
ref={this.buttonRef}
|
|
370
|
+
style={{
|
|
371
|
+
...(this.state.menuWidth && { minWidth: `calc(${this.state.menuWidth}px + 8px)` }),
|
|
372
|
+
borderWidth: open ? '2px' : '1px',
|
|
373
|
+
transition: 'border-width 0.2s ease-in-out',
|
|
374
|
+
}}
|
|
375
|
+
aria-controls={open ? menuId : undefined}
|
|
376
|
+
aria-haspopup="listbox"
|
|
377
|
+
aria-expanded={open ? 'true' : undefined}
|
|
378
|
+
aria-activedescendant={this.state.highlightedOptionId}
|
|
379
|
+
onClick={this.handleClick}
|
|
380
|
+
className={disabledClass}
|
|
381
|
+
disabled={disabled}
|
|
382
|
+
id={buttonId}
|
|
383
|
+
role="combobox"
|
|
384
|
+
aria-label={`Select an option for ${labelText}`}
|
|
385
|
+
aria-labelledby={valueDisplayId}
|
|
386
|
+
>
|
|
387
|
+
{correctnessIcon}
|
|
388
|
+
<StyledLabel
|
|
389
|
+
id={valueDisplayId}
|
|
390
|
+
ref={this.previewRef}
|
|
391
|
+
dangerouslySetInnerHTML={{
|
|
392
|
+
__html: correctValue
|
|
393
|
+
? correctValue
|
|
394
|
+
: open && this.state.previewValue
|
|
395
|
+
? this.getLabel(choices, this.state.previewValue)
|
|
396
|
+
: this.getLabel(choices, value) || '',
|
|
397
|
+
}}
|
|
398
|
+
/>
|
|
399
|
+
{open ? <ArrowDropUpIcon /> : <ArrowDropDownIcon />}
|
|
400
|
+
</StyledButton>
|
|
401
|
+
<StyledMenu
|
|
402
|
+
id={menuId}
|
|
403
|
+
anchorEl={anchorEl}
|
|
404
|
+
keepMounted
|
|
405
|
+
open={open}
|
|
406
|
+
onClose={this.handleClose}
|
|
407
|
+
getContentAnchorEl={null}
|
|
408
|
+
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
|
409
|
+
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|
|
410
|
+
transitionDuration={{ enter: 225, exit: 195 }}
|
|
411
|
+
slotProps={{
|
|
412
|
+
paper: this.state.menuWidth ? { style: { minWidth: this.state.menuWidth, padding: '4px' } } : undefined,
|
|
413
|
+
list: {
|
|
414
|
+
'aria-labelledby': buttonId,
|
|
415
|
+
role: 'listbox',
|
|
416
|
+
disablePadding: true,
|
|
417
|
+
},
|
|
418
|
+
}}
|
|
419
|
+
>
|
|
420
|
+
{(choices || []).map((c, index) => {
|
|
421
|
+
const optionId = `dropdown-option-${id}-${index}`;
|
|
422
|
+
|
|
423
|
+
return (
|
|
424
|
+
<StyledMenuItem
|
|
425
|
+
id={optionId}
|
|
426
|
+
className={c.value === value ? 'selected' : ''}
|
|
427
|
+
key={`${c.label}-${index}`}
|
|
428
|
+
value={c.value}
|
|
429
|
+
onClick={() => this.handleSelect(c.value, index)}
|
|
430
|
+
role="option"
|
|
431
|
+
aria-selected={this.state.highlightedOptionId === optionId ? 'true' : undefined}
|
|
432
|
+
onMouseOver={() => this.handleHover(index)}
|
|
433
|
+
>
|
|
434
|
+
<StyledLabel
|
|
435
|
+
ref={(ref) => (this.elementRefs[index] = ref)}
|
|
436
|
+
dangerouslySetInnerHTML={{ __html: c.label }}
|
|
437
|
+
/>
|
|
438
|
+
<StyledSelectedIndicator dangerouslySetInnerHTML={{ __html: c.value === value ? ' ✓' : '' }} />
|
|
439
|
+
</StyledMenuItem>
|
|
440
|
+
);
|
|
441
|
+
})}
|
|
442
|
+
</StyledMenu>
|
|
443
|
+
</>
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export default Dropdown;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @synced-from pie-lib/packages/mask-markup/src/components/input.jsx
|
|
4
|
+
* @auto-generated
|
|
5
|
+
*
|
|
6
|
+
* This file is automatically synced from pie-elements and converted to TypeScript.
|
|
7
|
+
* Manual edits will be overwritten on next sync.
|
|
8
|
+
* To make changes, edit the upstream JavaScript file and run sync again.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import PropTypes from 'prop-types';
|
|
13
|
+
import CorrectInput from './correct-input.js';
|
|
14
|
+
|
|
15
|
+
const Input = ({
|
|
16
|
+
disabled,
|
|
17
|
+
correct,
|
|
18
|
+
charactersLimit,
|
|
19
|
+
id,
|
|
20
|
+
isConstructedResponse,
|
|
21
|
+
value,
|
|
22
|
+
onChange,
|
|
23
|
+
showCorrectAnswer,
|
|
24
|
+
spellCheck,
|
|
25
|
+
width,
|
|
26
|
+
}) => {
|
|
27
|
+
return (
|
|
28
|
+
<CorrectInput
|
|
29
|
+
disabled={disabled}
|
|
30
|
+
correct={showCorrectAnswer || correct}
|
|
31
|
+
charactersLimit={charactersLimit}
|
|
32
|
+
variant="outlined"
|
|
33
|
+
value={value}
|
|
34
|
+
isConstructedResponse={isConstructedResponse}
|
|
35
|
+
spellCheck={spellCheck}
|
|
36
|
+
isBox={true}
|
|
37
|
+
width={width}
|
|
38
|
+
onChange={(e) => {
|
|
39
|
+
onChange(id, e.target.value);
|
|
40
|
+
}}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
Input.propTypes = {
|
|
46
|
+
id: PropTypes.string,
|
|
47
|
+
value: PropTypes.string,
|
|
48
|
+
onChange: PropTypes.func,
|
|
49
|
+
disabled: PropTypes.bool,
|
|
50
|
+
spellCheck: PropTypes.bool,
|
|
51
|
+
correct: PropTypes.bool,
|
|
52
|
+
showCorrectAnswer: PropTypes.bool,
|
|
53
|
+
charactersLimit: PropTypes.number,
|
|
54
|
+
width: PropTypes.number,
|
|
55
|
+
isConstructedResponse: PropTypes.bool,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default Input;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @synced-from pie-lib/packages/mask-markup/src/constructed-response.jsx
|
|
4
|
+
* @auto-generated
|
|
5
|
+
*
|
|
6
|
+
* This file is automatically synced from pie-elements and converted to TypeScript.
|
|
7
|
+
* Manual edits will be overwritten on next sync.
|
|
8
|
+
* To make changes, edit the upstream JavaScript file and run sync again.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { styled } from '@mui/material/styles';
|
|
13
|
+
import classnames from 'classnames';
|
|
14
|
+
|
|
15
|
+
import { color } from '@pie-lib/render-ui';
|
|
16
|
+
import { withMask } from './with-mask.js';
|
|
17
|
+
import EditableHtmlImport from '@pie-lib/editable-html-tip-tap';
|
|
18
|
+
|
|
19
|
+
const EditableHtml = EditableHtmlImport;
|
|
20
|
+
const StyledEditableHtml: any = styled(EditableHtml)(() => ({
|
|
21
|
+
display: 'inline-block',
|
|
22
|
+
verticalAlign: 'middle',
|
|
23
|
+
margin: '4px',
|
|
24
|
+
borderRadius: '4px',
|
|
25
|
+
border: `1px solid ${color.black()}`,
|
|
26
|
+
'&.correct': {
|
|
27
|
+
border: `1px solid ${color.correct()}`,
|
|
28
|
+
},
|
|
29
|
+
'&.incorrect': {
|
|
30
|
+
border: `1px solid ${color.incorrect()}`,
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
const MaskedInput = (props) => (node, data) => {
|
|
35
|
+
const { adjustedLimit, disabled, feedback, showCorrectAnswer, maxLength, spellCheck, pluginProps, onChange } = props;
|
|
36
|
+
const dataset = node.data?.dataset || {};
|
|
37
|
+
|
|
38
|
+
if (dataset.component === 'input') {
|
|
39
|
+
const correctAnswer = ((props.choices && dataset && props.choices[dataset.id]) || [])[0];
|
|
40
|
+
const finalValue = showCorrectAnswer ? correctAnswer && correctAnswer.label : data[dataset.id] || '';
|
|
41
|
+
const width = maxLength && maxLength[dataset.id];
|
|
42
|
+
const feedbackStatus = feedback && feedback[dataset.id];
|
|
43
|
+
const isCorrect = showCorrectAnswer || feedbackStatus === 'correct';
|
|
44
|
+
const isIncorrect = !showCorrectAnswer && feedbackStatus === 'incorrect';
|
|
45
|
+
|
|
46
|
+
const handleInputChange = (newValue) => {
|
|
47
|
+
const updatedValue = {
|
|
48
|
+
...data,
|
|
49
|
+
[dataset.id]: newValue,
|
|
50
|
+
};
|
|
51
|
+
onChange(updatedValue);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleKeyDown = (event) => {
|
|
55
|
+
// the keyCode value for the Enter/Return key is 13
|
|
56
|
+
if (event.key === 'Enter' || event.keyCode === 13) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<StyledEditableHtml
|
|
63
|
+
id={dataset.id}
|
|
64
|
+
key={`${node.type}-input-${dataset.id}`}
|
|
65
|
+
disabled={showCorrectAnswer || disabled}
|
|
66
|
+
disableUnderline
|
|
67
|
+
onChange={handleInputChange}
|
|
68
|
+
markup={finalValue || ''}
|
|
69
|
+
charactersLimit={adjustedLimit ? width : 25}
|
|
70
|
+
activePlugins={['languageCharacters']}
|
|
71
|
+
pluginProps={pluginProps}
|
|
72
|
+
languageCharactersProps={[{ language: 'spanish' }]}
|
|
73
|
+
spellCheck={spellCheck}
|
|
74
|
+
adjustWidthForLimit
|
|
75
|
+
onKeyDown={handleKeyDown}
|
|
76
|
+
autoWidthToolbar
|
|
77
|
+
toolbarOpts={{
|
|
78
|
+
minWidth: 'auto',
|
|
79
|
+
noBorder: true,
|
|
80
|
+
isHidden: !!pluginProps?.characters?.disabled,
|
|
81
|
+
}}
|
|
82
|
+
className={classnames({
|
|
83
|
+
correct: isCorrect,
|
|
84
|
+
incorrect: isIncorrect,
|
|
85
|
+
})}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export default withMask('input', MaskedInput);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @synced-from pie-lib/packages/mask-markup/src/customizable.jsx
|
|
4
|
+
* @auto-generated
|
|
5
|
+
*
|
|
6
|
+
* This file is automatically synced from pie-elements and converted to TypeScript.
|
|
7
|
+
* Manual edits will be overwritten on next sync.
|
|
8
|
+
* To make changes, edit the upstream JavaScript file and run sync again.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// import Input from './components/input.js';
|
|
12
|
+
import { withMask } from './with-mask.js';
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line react/display-name
|
|
15
|
+
export default withMask('input', (props) => (node, data, onChange) => {
|
|
16
|
+
const dataset = node.data ? node.data.dataset || {} : {};
|
|
17
|
+
if (dataset.component === 'input') {
|
|
18
|
+
// eslint-disable-next-line react/prop-types
|
|
19
|
+
// const { adjustedLimit, disabled, feedback, showCorrectAnswer, maxLength, spellCheck } = props;
|
|
20
|
+
|
|
21
|
+
// the first answer is the correct one
|
|
22
|
+
// eslint-disable-next-line react/prop-types
|
|
23
|
+
// const correctAnswer = ((props.choices && dataset && props.choices[dataset.id]) || [])[0];
|
|
24
|
+
// const finalValue = showCorrectAnswer ? correctAnswer && correctAnswer.label : data[dataset.id] || '';
|
|
25
|
+
// const width = maxLength && maxLength[dataset.id];
|
|
26
|
+
|
|
27
|
+
return props.customMarkMarkupComponent(dataset.id);
|
|
28
|
+
// return (
|
|
29
|
+
// <Input
|
|
30
|
+
// key={`${node.type}-input-${dataset.id}`}
|
|
31
|
+
// correct={feedback && feedback[dataset.id] && feedback[dataset.id] === 'correct'}
|
|
32
|
+
// disabled={showCorrectAnswer || disabled}
|
|
33
|
+
// value={finalValue}
|
|
34
|
+
// id={dataset.id}
|
|
35
|
+
// onChange={onChange}
|
|
36
|
+
// showCorrectAnswer={showCorrectAnswer}
|
|
37
|
+
// width={width}
|
|
38
|
+
// charactersLimit={adjustedLimit ? width : 25}
|
|
39
|
+
// isConstructedResponse={true}
|
|
40
|
+
// spellCheck={spellCheck}
|
|
41
|
+
// />
|
|
42
|
+
// );
|
|
43
|
+
}
|
|
44
|
+
});
|