@pie-lib/render-ui 7.0.2 → 8.0.0-next.20261002094057
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 +11 -0
- package/dist/assets/enableAudioAutoplayImage.d.ts +10 -0
- package/dist/assets/enableAudioAutoplayImage.js +4 -0
- package/dist/collapsible/index.d.ts +31 -0
- package/dist/collapsible/index.js +53 -0
- package/dist/color.d.ts +128 -0
- package/dist/color.js +132 -0
- package/dist/feedback.d.ts +20 -0
- package/dist/feedback.js +65 -0
- package/dist/has-media.d.ts +9 -0
- package/dist/has-media.js +10 -0
- package/dist/has-text.d.ts +9 -0
- package/dist/has-text.js +10 -0
- package/dist/html-and-math.d.ts +22 -0
- package/dist/html-and-math.js +25 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +18 -0
- package/dist/inline-menu.d.ts +33 -0
- package/dist/inline-menu.js +29 -0
- package/dist/input-container.d.ts +23 -0
- package/dist/input-container.js +39 -0
- package/dist/preview-layout.d.ts +24 -0
- package/dist/preview-layout.js +37 -0
- package/dist/preview-prompt.d.ts +39 -0
- package/dist/preview-prompt.js +153 -0
- package/dist/purpose.d.ts +18 -0
- package/dist/purpose.js +11 -0
- package/dist/readable.d.ts +18 -0
- package/dist/readable.js +11 -0
- package/dist/response-indicators.d.ts +121 -0
- package/dist/response-indicators.js +62 -0
- package/{src/transform-headings.js → dist/transform-headings.d.ts} +9 -30
- package/dist/transform-headings.js +15 -0
- package/dist/ui-layout.d.ts +31 -0
- package/dist/ui-layout.js +55 -0
- package/dist/withUndoReset.d.ts +47 -0
- package/dist/withUndoReset.js +84 -0
- package/package.json +29 -20
- package/CHANGELOG.json +0 -1
- package/CHANGELOG.md +0 -1184
- package/LICENSE.md +0 -5
- package/lib/assets/enableAudioAutoplayImage.js +0 -8
- package/lib/assets/enableAudioAutoplayImage.js.map +0 -1
- package/lib/collapsible/index.js +0 -105
- package/lib/collapsible/index.js.map +0 -1
- package/lib/color.js +0 -253
- package/lib/color.js.map +0 -1
- package/lib/feedback.js +0 -112
- package/lib/feedback.js.map +0 -1
- package/lib/has-media.js +0 -22
- package/lib/has-media.js.map +0 -1
- package/lib/has-text.js +0 -22
- package/lib/has-text.js.map +0 -1
- package/lib/html-and-math.js +0 -54
- package/lib/html-and-math.js.map +0 -1
- package/lib/index.js +0 -113
- package/lib/index.js.map +0 -1
- package/lib/input-container.js +0 -59
- package/lib/input-container.js.map +0 -1
- package/lib/preview-layout.js +0 -65
- package/lib/preview-layout.js.map +0 -1
- package/lib/preview-prompt.js +0 -316
- package/lib/preview-prompt.js.map +0 -1
- package/lib/purpose.js +0 -22
- package/lib/purpose.js.map +0 -1
- package/lib/readable.js +0 -22
- package/lib/readable.js.map +0 -1
- package/lib/response-indicators.js +0 -102
- package/lib/response-indicators.js.map +0 -1
- package/lib/transform-headings.js +0 -53
- package/lib/transform-headings.js.map +0 -1
- package/lib/ui-layout.js +0 -125
- package/lib/ui-layout.js.map +0 -1
- package/lib/withUndoReset.js +0 -135
- package/lib/withUndoReset.js.map +0 -1
- package/src/__tests__/color.test.js +0 -259
- package/src/__tests__/feedback.test.jsx +0 -279
- package/src/__tests__/has-media.test.js +0 -19
- package/src/__tests__/has-text.test.js +0 -20
- package/src/__tests__/html-and-math.test.js +0 -36
- package/src/__tests__/input-container.test.jsx +0 -328
- package/src/__tests__/preview-layout.test.jsx +0 -349
- package/src/__tests__/preview-prompt.test.jsx +0 -464
- package/src/__tests__/purpose.test.jsx +0 -51
- package/src/__tests__/readable.test.jsx +0 -69
- package/src/__tests__/response-indicators.test.jsx +0 -111
- package/src/__tests__/ui-layout.test.jsx +0 -52
- package/src/__tests__/withUndoReset.test.jsx +0 -176
- package/src/assets/enableAudioAutoplayImage.js +0 -1
- package/src/collapsible/__tests__/index.test.jsx +0 -39
- package/src/collapsible/index.jsx +0 -64
- package/src/color.js +0 -151
- package/src/feedback.jsx +0 -84
- package/src/has-media.js +0 -16
- package/src/has-text.js +0 -18
- package/src/html-and-math.js +0 -21
- package/src/index.js +0 -35
- package/src/input-container.jsx +0 -47
- package/src/preview-layout.jsx +0 -41
- package/src/preview-prompt.jsx +0 -300
- package/src/purpose.jsx +0 -17
- package/src/readable.jsx +0 -19
- package/src/response-indicators.jsx +0 -82
- package/src/ui-layout.jsx +0 -96
- package/src/withUndoReset.jsx +0 -114
package/src/preview-prompt.jsx
DELETED
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
import React, { Component } from 'react';
|
|
2
|
-
import { styled } from '@mui/material/styles';
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
|
-
import * as color from './color';
|
|
5
|
-
import { renderMath } from '@pie-lib/math-rendering';
|
|
6
|
-
|
|
7
|
-
const StyledPromptContainer = styled('div')(({ theme, tagName }) => ({
|
|
8
|
-
// presentation tables should not have any custom style
|
|
9
|
-
// Base promptTable styles
|
|
10
|
-
'&:not(.MathJax) > table:not([role="presentation"])': {
|
|
11
|
-
borderCollapse: 'collapse',
|
|
12
|
-
},
|
|
13
|
-
/*
|
|
14
|
-
* The editor writes border="1", and the UA answers it with `border-style: outset`
|
|
15
|
-
* on the table and `inset` on the cells -- which browsers paint as synthesized 3D
|
|
16
|
-
* shades rather than the colour the border resolves to, so the grid comes out grey
|
|
17
|
-
* on every scheme (1.19:1 on black-on-violet). `solid` is what removes the
|
|
18
|
-
* shading; without it, setting the colour has no effect.
|
|
19
|
-
*/
|
|
20
|
-
'&:not(.MathJax) table[border]:not([role="presentation"]), &:not(.MathJax) table[border]:not([role="presentation"]) td, &:not(.MathJax) table[border]:not([role="presentation"]) th':
|
|
21
|
-
{
|
|
22
|
-
borderStyle: 'solid',
|
|
23
|
-
borderColor: color.tableGrid(),
|
|
24
|
-
},
|
|
25
|
-
// Apply vertical striping when first column is a header (th) and NOT mixed with td.
|
|
26
|
-
// Ink stays at 5.44:1 or better on tableStripe in every scheme, so the cell inherits
|
|
27
|
-
// its text colour rather than pinning the `color: black` this used to carry.
|
|
28
|
-
'&:not(.MathJax) > table:not([role="presentation"]):has(tbody tr > th:first-child):not(:has(tbody tr > td:first-child)) tbody td:nth-child(even)':
|
|
29
|
-
{
|
|
30
|
-
backgroundColor: color.tableStripe(),
|
|
31
|
-
},
|
|
32
|
-
// Apply horizontal striping for tables where first element is a data cell (td)
|
|
33
|
-
'&:not(.MathJax) > table:not([role="presentation"]):has(tbody tr > td:first-child) tbody tr:nth-child(even) td': {
|
|
34
|
-
backgroundColor: color.tableStripe(),
|
|
35
|
-
},
|
|
36
|
-
// align table content to left as per STAR requirement PD-3687
|
|
37
|
-
'&:not(.MathJax) table:not([role="presentation"]) td, &:not(.MathJax) table:not([role="presentation"]) th': {
|
|
38
|
-
padding: '.6em 1em',
|
|
39
|
-
textAlign: 'left',
|
|
40
|
-
},
|
|
41
|
-
// added this to fix alignment of text in prompt imported from studio (PD-3423)
|
|
42
|
-
'&:not(.MathJax) > table td > p.kds-indent': {
|
|
43
|
-
textAlign: 'initial',
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
// Conditional styles based on class names
|
|
47
|
-
'&.prompt': {
|
|
48
|
-
verticalAlign: 'middle',
|
|
49
|
-
color: color.text(),
|
|
50
|
-
},
|
|
51
|
-
'&.legend': {
|
|
52
|
-
width: '100%',
|
|
53
|
-
fontSize: 'inherit !important',
|
|
54
|
-
},
|
|
55
|
-
'&.rationale': {
|
|
56
|
-
paddingLeft: theme.spacing(4),
|
|
57
|
-
paddingBottom: theme.spacing(1),
|
|
58
|
-
},
|
|
59
|
-
'&.prompt-label': {
|
|
60
|
-
color: `${color.text()} !important`,
|
|
61
|
-
display: 'flex',
|
|
62
|
-
flexDirection: 'column',
|
|
63
|
-
verticalAlign: 'middle',
|
|
64
|
-
cursor: 'pointer',
|
|
65
|
-
'& > p': {
|
|
66
|
-
margin: '0 0 0 0 !important',
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
}));
|
|
70
|
-
|
|
71
|
-
//Used these below to replace \\embed{newLine} with \\newline from prompt which will get parsed in MathJax
|
|
72
|
-
const NEWLINE_BLOCK_REGEX = /\\embed\{newLine\}\[\]/g;
|
|
73
|
-
const NEWLINE_LATEX = '\\newline ';
|
|
74
|
-
|
|
75
|
-
// stable hook for 'is this node inside a prompt' checks - a class rather than an id,
|
|
76
|
-
// so it stays valid when a page renders more than one prompt
|
|
77
|
-
const PROMPT_CLASS = 'preview-prompt';
|
|
78
|
-
|
|
79
|
-
export class PreviewPrompt extends Component {
|
|
80
|
-
static propTypes = {
|
|
81
|
-
prompt: PropTypes.string,
|
|
82
|
-
tagName: PropTypes.string,
|
|
83
|
-
className: PropTypes.string,
|
|
84
|
-
onClick: PropTypes.func,
|
|
85
|
-
defaultClassName: PropTypes.string,
|
|
86
|
-
autoplayAudioEnabled: PropTypes.bool,
|
|
87
|
-
customAudioButton: {
|
|
88
|
-
playImage: PropTypes.string,
|
|
89
|
-
pauseImage: PropTypes.string,
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
static defaultProps = {
|
|
94
|
-
onClick: () => {},
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
promptRef = React.createRef();
|
|
98
|
-
|
|
99
|
-
parsedText = (text) => {
|
|
100
|
-
const { customAudioButton } = this.props;
|
|
101
|
-
const div = document.createElement('div');
|
|
102
|
-
div.innerHTML = text;
|
|
103
|
-
|
|
104
|
-
const audio = div.querySelector('audio');
|
|
105
|
-
if (audio) {
|
|
106
|
-
const source = document.createElement('source');
|
|
107
|
-
|
|
108
|
-
source.setAttribute('type', 'audio/mp3');
|
|
109
|
-
source.setAttribute('src', audio.getAttribute('src'));
|
|
110
|
-
|
|
111
|
-
audio.removeAttribute('src');
|
|
112
|
-
audio.setAttribute('id', 'pie-prompt-audio-player');
|
|
113
|
-
|
|
114
|
-
audio.appendChild(source);
|
|
115
|
-
|
|
116
|
-
if (customAudioButton) {
|
|
117
|
-
audio.style.display = 'none';
|
|
118
|
-
|
|
119
|
-
const playButton = document.createElement('div');
|
|
120
|
-
playButton.id = 'play-audio-button';
|
|
121
|
-
|
|
122
|
-
Object.assign(playButton.style, {
|
|
123
|
-
cursor: 'pointer',
|
|
124
|
-
display: 'block',
|
|
125
|
-
width: '128px',
|
|
126
|
-
height: '128px',
|
|
127
|
-
backgroundImage: `url(${customAudioButton.pauseImage})`,
|
|
128
|
-
backgroundSize: 'cover',
|
|
129
|
-
borderRadius: '50%',
|
|
130
|
-
border: '1px solid #326295',
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
audio.parentNode.insertBefore(playButton, audio);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return div.innerHTML;
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
addCustomAudioButtonControls() {
|
|
141
|
-
const { autoplayAudioEnabled, customAudioButton } = this.props;
|
|
142
|
-
const playButton = document.getElementById('play-audio-button');
|
|
143
|
-
const audio = document.getElementById('pie-prompt-audio-player');
|
|
144
|
-
|
|
145
|
-
if (autoplayAudioEnabled && audio) {
|
|
146
|
-
audio
|
|
147
|
-
.play()
|
|
148
|
-
.then(() => {
|
|
149
|
-
if (playButton && customAudioButton) {
|
|
150
|
-
audio.addEventListener('ended', handleAudioEnded);
|
|
151
|
-
}
|
|
152
|
-
})
|
|
153
|
-
.catch((error) => {
|
|
154
|
-
console.error('Error playing audio', error);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (!playButton || !audio || !customAudioButton) return;
|
|
159
|
-
|
|
160
|
-
const handlePlayClick = () => {
|
|
161
|
-
// if already playing, don't play again
|
|
162
|
-
if (!audio.paused) return;
|
|
163
|
-
if (playButton.style.backgroundImage.includes(customAudioButton.pauseImage)) return;
|
|
164
|
-
|
|
165
|
-
audio.play();
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const handleAudioEnded = () => {
|
|
169
|
-
playButton.style.backgroundImage = `url(${customAudioButton.playImage})`;
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const handleAudioPlay = () => {
|
|
173
|
-
Object.assign(playButton.style, {
|
|
174
|
-
backgroundImage: `url(${customAudioButton.pauseImage})`,
|
|
175
|
-
border: '1px solid #ccc',
|
|
176
|
-
});
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const handleAudioPause = () => {
|
|
180
|
-
Object.assign(playButton.style, {
|
|
181
|
-
backgroundImage: `url(${customAudioButton.playImage})`,
|
|
182
|
-
border: '1px solid #326295',
|
|
183
|
-
});
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
playButton.addEventListener('click', handlePlayClick);
|
|
187
|
-
audio.addEventListener('play', handleAudioPlay);
|
|
188
|
-
audio.addEventListener('pause', handleAudioPause);
|
|
189
|
-
audio.addEventListener('ended', handleAudioEnded);
|
|
190
|
-
|
|
191
|
-
// store event handler references so they can be removed later
|
|
192
|
-
this._handlePlayClick = handlePlayClick;
|
|
193
|
-
this._handleAudioPlay = handleAudioPlay;
|
|
194
|
-
this._handleAudioPause = handleAudioPause;
|
|
195
|
-
this._handleAudioEnded = handleAudioEnded;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
removeCustomAudioButtonListeners() {
|
|
199
|
-
const playButton = document.getElementById('play-audio-button');
|
|
200
|
-
const audio = document.querySelector('audio');
|
|
201
|
-
|
|
202
|
-
if (!playButton || !audio) return;
|
|
203
|
-
|
|
204
|
-
// remove event listeners using stored references
|
|
205
|
-
playButton.removeEventListener('click', this._handlePlayClick);
|
|
206
|
-
audio.removeEventListener('play', this._handleAudioPlay);
|
|
207
|
-
audio.removeEventListener('pause', this._handleAudioPause);
|
|
208
|
-
audio.removeEventListener('ended', this._handleAudioEnded);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
componentDidMount() {
|
|
212
|
-
this.alignImages();
|
|
213
|
-
this.addCustomAudioButtonControls();
|
|
214
|
-
this.setupMathRendering();
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
componentDidUpdate(prevProps) {
|
|
218
|
-
this.alignImages();
|
|
219
|
-
|
|
220
|
-
if (prevProps.prompt !== this.props.prompt) {
|
|
221
|
-
this.renderMathContent();
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
componentWillUnmount() {
|
|
226
|
-
this.removeCustomAudioButtonListeners();
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
setupMathRendering() {
|
|
230
|
-
this.renderMathContent();
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
renderMathContent() {
|
|
234
|
-
const container = this.promptRef.current;
|
|
235
|
-
if (container && typeof renderMath === 'function') {
|
|
236
|
-
renderMath(container);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
alignImages() {
|
|
241
|
-
const previewPrompt = this.promptRef.current;
|
|
242
|
-
|
|
243
|
-
if (!previewPrompt) {
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const images = previewPrompt.getElementsByTagName('img');
|
|
248
|
-
|
|
249
|
-
if (images && images.length) {
|
|
250
|
-
for (let image of images) {
|
|
251
|
-
if (image.attributes && image.attributes.alignment && image.attributes.alignment.value) {
|
|
252
|
-
const alignment = image.attributes.alignment.value;
|
|
253
|
-
const justifyContent =
|
|
254
|
-
alignment === 'center' ? 'center' : alignment === 'right' ? 'flex-end' : 'flex-start';
|
|
255
|
-
|
|
256
|
-
const parentNode = image.parentElement;
|
|
257
|
-
|
|
258
|
-
if (
|
|
259
|
-
parentNode.tagName === 'DIV' &&
|
|
260
|
-
parentNode.style.display === 'flex' &&
|
|
261
|
-
parentNode.style.width === '100%'
|
|
262
|
-
) {
|
|
263
|
-
parentNode.style.justifyContent = justifyContent;
|
|
264
|
-
} else {
|
|
265
|
-
const div = document.createElement('div');
|
|
266
|
-
div.style.display = 'flex';
|
|
267
|
-
div.style.width = '100%';
|
|
268
|
-
div.style.justifyContent = justifyContent;
|
|
269
|
-
|
|
270
|
-
const copyImage = image.cloneNode(true);
|
|
271
|
-
div.appendChild(copyImage);
|
|
272
|
-
parentNode.replaceChild(div, image);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
render() {
|
|
280
|
-
const { prompt, tagName, className, onClick, defaultClassName } = this.props;
|
|
281
|
-
// legend tag was added once with accessibility tasks, we need extra style to make it work with images alignment
|
|
282
|
-
const legendClass = tagName === 'legend' ? 'legend' : '';
|
|
283
|
-
const customClasses = `${className || ''} ${defaultClassName || ''} ${legendClass} ${PROMPT_CLASS}`.trim();
|
|
284
|
-
|
|
285
|
-
return (
|
|
286
|
-
<StyledPromptContainer
|
|
287
|
-
as={tagName || 'div'}
|
|
288
|
-
ref={this.promptRef}
|
|
289
|
-
onClick={onClick}
|
|
290
|
-
className={customClasses}
|
|
291
|
-
tagName={tagName}
|
|
292
|
-
dangerouslySetInnerHTML={{
|
|
293
|
-
__html: this.parsedText(prompt || '').replace(NEWLINE_BLOCK_REGEX, NEWLINE_LATEX),
|
|
294
|
-
}}
|
|
295
|
-
/>
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export default PreviewPrompt;
|
package/src/purpose.jsx
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import PropTypes from 'prop-types';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
|
-
const Purpose = (props) => {
|
|
5
|
-
return (
|
|
6
|
-
<>
|
|
7
|
-
{React.Children.map(props.children, (child) => React.cloneElement(child, { 'data-pie-purpose': props.purpose }))}
|
|
8
|
-
</>
|
|
9
|
-
);
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
Purpose.propTypes = {
|
|
13
|
-
children: PropTypes.node,
|
|
14
|
-
purpose: PropTypes.string,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export default Purpose;
|
package/src/readable.jsx
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import PropTypes from 'prop-types';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
|
-
const Readable = (props) => {
|
|
5
|
-
return (
|
|
6
|
-
<>
|
|
7
|
-
{React.Children.map(props.children, (child) =>
|
|
8
|
-
React.cloneElement(child, { 'data-pie-readable': props.false === undefined }),
|
|
9
|
-
)}
|
|
10
|
-
</>
|
|
11
|
-
);
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
Readable.propTypes = {
|
|
15
|
-
children: PropTypes.node,
|
|
16
|
-
false: PropTypes.bool,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export default Readable;
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import * as icons from '@pie-lib/icons';
|
|
4
|
-
import Popover from '@mui/material/Popover';
|
|
5
|
-
import { styled } from '@mui/material/styles';
|
|
6
|
-
import Feedback from './feedback';
|
|
7
|
-
import debug from 'debug';
|
|
8
|
-
|
|
9
|
-
const log = debug('pie-libs:render-ui:response-indicators');
|
|
10
|
-
|
|
11
|
-
const ResponseIndicatorContainer = styled('div')(({ hasFeedback }) => ({
|
|
12
|
-
cursor: hasFeedback ? 'pointer' : 'default',
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
|
-
const StyledPopover = styled(Popover)({
|
|
16
|
-
cursor: 'pointer',
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const PopoverPaper = styled('div')({
|
|
20
|
-
padding: '0',
|
|
21
|
-
borderRadius: '4px',
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const BuildIndicator = (Icon, correctness) => {
|
|
25
|
-
class Indicator extends React.Component {
|
|
26
|
-
constructor(props) {
|
|
27
|
-
super(props);
|
|
28
|
-
this.state = {};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
handlePopoverOpen = (event) => {
|
|
32
|
-
log('[handlePopoverOpen]', event.target);
|
|
33
|
-
this.setState({ anchorEl: event.target });
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
handlePopoverClose = () => {
|
|
37
|
-
this.setState({ anchorEl: null });
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
render() {
|
|
41
|
-
const { feedback } = this.props;
|
|
42
|
-
const { anchorEl } = this.state;
|
|
43
|
-
return (
|
|
44
|
-
<ResponseIndicatorContainer hasFeedback={!!feedback}>
|
|
45
|
-
<span ref={(r) => (this.icon = r)} onClick={this.handlePopoverOpen}>
|
|
46
|
-
<Icon />
|
|
47
|
-
</span>
|
|
48
|
-
|
|
49
|
-
{feedback && (
|
|
50
|
-
<StyledPopover
|
|
51
|
-
PaperComponent={PopoverPaper}
|
|
52
|
-
open={!!anchorEl}
|
|
53
|
-
anchorEl={anchorEl}
|
|
54
|
-
anchorOrigin={{
|
|
55
|
-
vertical: 'bottom',
|
|
56
|
-
horizontal: 'left',
|
|
57
|
-
}}
|
|
58
|
-
transformOrigin={{
|
|
59
|
-
vertical: 'top',
|
|
60
|
-
horizontal: 'left',
|
|
61
|
-
}}
|
|
62
|
-
onClose={this.handlePopoverClose}
|
|
63
|
-
>
|
|
64
|
-
<Feedback feedback={feedback} correctness={correctness} />
|
|
65
|
-
</StyledPopover>
|
|
66
|
-
)}
|
|
67
|
-
</ResponseIndicatorContainer>
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
Indicator.propTypes = {
|
|
73
|
-
feedback: PropTypes.string,
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
return Indicator;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export const Correct = BuildIndicator(icons.Correct, 'correct');
|
|
80
|
-
export const Incorrect = BuildIndicator(icons.Incorrect, 'incorrect');
|
|
81
|
-
export const PartiallyCorrect = BuildIndicator(icons.PartiallyCorrect, 'partially-correct');
|
|
82
|
-
export const NothingSubmitted = BuildIndicator(icons.NothingSubmitted, 'nothing-submitted');
|
package/src/ui-layout.jsx
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { createTheme, styled, StyledEngineProvider, ThemeProvider } from '@mui/material/styles';
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
|
-
|
|
5
|
-
const theme = createTheme({
|
|
6
|
-
typography: {
|
|
7
|
-
fontFamily: 'inherit',
|
|
8
|
-
},
|
|
9
|
-
palette: {
|
|
10
|
-
action: {
|
|
11
|
-
disabled: 'rgba(0, 0, 0, 0.54);',
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
components: {
|
|
15
|
-
MuiTypography: {
|
|
16
|
-
styleOverrides: {
|
|
17
|
-
root: { fontFamily: 'inherit' },
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
MuiButton: {
|
|
21
|
-
styleOverrides: {
|
|
22
|
-
contained: {
|
|
23
|
-
backgroundColor: '#e0e0e0',
|
|
24
|
-
color: '#000000',
|
|
25
|
-
'&:hover': {
|
|
26
|
-
backgroundColor: '#bdbdbd',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const StyledContainer = styled('div')({
|
|
35
|
-
// need this because some browsers set their own style on table
|
|
36
|
-
'& table, th, td': {
|
|
37
|
-
fontSize: 'inherit' /* Ensure table elements inherit font size */,
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
class UiLayout extends React.Component {
|
|
42
|
-
static propTypes = {
|
|
43
|
-
className: PropTypes.string,
|
|
44
|
-
children: PropTypes.array,
|
|
45
|
-
extraCSSRules: PropTypes.shape({
|
|
46
|
-
names: PropTypes.arrayOf(PropTypes.string),
|
|
47
|
-
rules: PropTypes.string,
|
|
48
|
-
}),
|
|
49
|
-
fontSizeFactor: PropTypes.number,
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
static defaultProps = {
|
|
53
|
-
extraCSSRules: {},
|
|
54
|
-
fontSizeFactor: 1,
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
constructor(props) {
|
|
58
|
-
super(props);
|
|
59
|
-
this.classesSheet = document.createElement('style');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
computeStyle(fontSizeFactor) {
|
|
63
|
-
const getFontSize = (element) => parseFloat(getComputedStyle(element).fontSize);
|
|
64
|
-
|
|
65
|
-
const rootFontSize = getFontSize(document.documentElement);
|
|
66
|
-
const bodyFontSize = getFontSize(document.body);
|
|
67
|
-
const effectiveFontSize = Math.max(rootFontSize, bodyFontSize);
|
|
68
|
-
|
|
69
|
-
// Handle null, undefined, or invalid values by defaulting to 1
|
|
70
|
-
const factor = fontSizeFactor != null && typeof fontSizeFactor === 'number' ? fontSizeFactor : 1;
|
|
71
|
-
return factor !== 1 ? { fontSize: `${effectiveFontSize * factor}px` } : null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
render() {
|
|
75
|
-
const { children, className, fontSizeFactor, ...rest } = this.props;
|
|
76
|
-
|
|
77
|
-
const { extraCSSRules, ...restProps } = rest;
|
|
78
|
-
const style = this.computeStyle(fontSizeFactor);
|
|
79
|
-
|
|
80
|
-
return (
|
|
81
|
-
<StyledEngineProvider injectFirst>
|
|
82
|
-
<ThemeProvider theme={theme}>
|
|
83
|
-
{extraCSSRules?.rules ? (
|
|
84
|
-
<style dangerouslySetInnerHTML={{ __html: `.extraCSSRules { ${extraCSSRules.rules} }` }} />
|
|
85
|
-
) : null}
|
|
86
|
-
|
|
87
|
-
<StyledContainer className={`${className} extraCSSRules`} {...restProps} {...(style && { style })}>
|
|
88
|
-
{children}
|
|
89
|
-
</StyledContainer>
|
|
90
|
-
</ThemeProvider>
|
|
91
|
-
</StyledEngineProvider>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export default UiLayout;
|
package/src/withUndoReset.jsx
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import Button from '@mui/material/Button';
|
|
4
|
-
import { styled } from '@mui/material/styles';
|
|
5
|
-
import Restore from '@mui/icons-material/Restore';
|
|
6
|
-
import Undo from '@mui/icons-material/Undo';
|
|
7
|
-
|
|
8
|
-
const Wrapper = styled('div')({
|
|
9
|
-
display: 'flex',
|
|
10
|
-
flexDirection: 'column',
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const ResetUndoContainer = styled('div')({
|
|
14
|
-
display: 'flex',
|
|
15
|
-
alignItems: 'center',
|
|
16
|
-
justifyContent: 'center',
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const StyledIcon = styled('div')(({ theme }) => ({
|
|
20
|
-
width: '24px',
|
|
21
|
-
height: '24px',
|
|
22
|
-
color: 'gray',
|
|
23
|
-
marginRight: theme.spacing(1),
|
|
24
|
-
display: 'flex',
|
|
25
|
-
alignItems: 'center',
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
const StyledButton = styled(Button)(({ theme }) => ({
|
|
29
|
-
display: 'flex',
|
|
30
|
-
alignItems: 'center',
|
|
31
|
-
marginLeft: theme.spacing(3),
|
|
32
|
-
marginRight: theme.spacing(3),
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* HOC that adds undo and reset functionality for session values
|
|
37
|
-
*/
|
|
38
|
-
const withUndoReset = (WrappedComponent) => {
|
|
39
|
-
class WithUndoReset extends React.Component {
|
|
40
|
-
static propTypes = {
|
|
41
|
-
session: PropTypes.object,
|
|
42
|
-
onSessionChange: PropTypes.func,
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
constructor(props) {
|
|
46
|
-
super(props);
|
|
47
|
-
|
|
48
|
-
this.state = {
|
|
49
|
-
sessionInitialValues: JSON.parse(JSON.stringify(props.session)),
|
|
50
|
-
session: props.session,
|
|
51
|
-
changes: [],
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
onSessionChange = (session) => {
|
|
56
|
-
this.setState(
|
|
57
|
-
(state) => ({ session, changes: [...state.changes, session] }),
|
|
58
|
-
() => this.props.onSessionChange(session),
|
|
59
|
-
);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
onUndo = () => {
|
|
63
|
-
this.setState(
|
|
64
|
-
(state) => {
|
|
65
|
-
const newChanges = [...state.changes];
|
|
66
|
-
|
|
67
|
-
newChanges.pop();
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
changes: newChanges,
|
|
71
|
-
session: newChanges.length ? newChanges[newChanges.length - 1] : state.sessionInitialValues,
|
|
72
|
-
};
|
|
73
|
-
},
|
|
74
|
-
() => this.props.onSessionChange(this.state.session),
|
|
75
|
-
);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
onReset = () => {
|
|
79
|
-
this.setState(
|
|
80
|
-
(state) => ({ session: state.sessionInitialValues, changes: [] }),
|
|
81
|
-
() => this.props.onSessionChange(this.state.sessionInitialValues),
|
|
82
|
-
);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
render() {
|
|
86
|
-
const { ...rest } = this.props;
|
|
87
|
-
const { changes, session } = this.state;
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<Wrapper>
|
|
91
|
-
<ResetUndoContainer>
|
|
92
|
-
<StyledButton color="primary" disabled={changes.length === 0} onClick={this.onUndo}>
|
|
93
|
-
<StyledIcon>
|
|
94
|
-
<Undo />
|
|
95
|
-
</StyledIcon>
|
|
96
|
-
Undo
|
|
97
|
-
</StyledButton>
|
|
98
|
-
<StyledButton color="primary" disabled={changes.length === 0} onClick={this.onReset}>
|
|
99
|
-
<StyledIcon>
|
|
100
|
-
<Restore />
|
|
101
|
-
</StyledIcon>
|
|
102
|
-
Start Over
|
|
103
|
-
</StyledButton>
|
|
104
|
-
</ResetUndoContainer>
|
|
105
|
-
<WrappedComponent {...rest} session={session} onSessionChange={this.onSessionChange} />
|
|
106
|
-
</Wrapper>
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return WithUndoReset;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
export default withUndoReset;
|