@khanacademy/perseus-editor 4.1.5 → 4.1.7
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/es/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +9 -6
- package/.babelrc.js +0 -8
- package/.eslintrc.js +0 -12
- package/CHANGELOG.md +0 -1155
- package/src/README.md +0 -7
- package/src/__stories__/editor-page.stories.tsx +0 -65
- package/src/__stories__/editor.stories.tsx +0 -134
- package/src/__stories__/item-editor.stories.tsx +0 -41
- package/src/__stories__/tex-error-view.stories.tsx +0 -26
- package/src/__testdata__/input-number.testdata.ts +0 -27
- package/src/__tests__/editor.test.tsx +0 -142
- package/src/__tests__/hint-editor.test.tsx +0 -83
- package/src/__tests__/i18n.test.ts +0 -501
- package/src/__tests__/integration.test.ts +0 -5
- package/src/__tests__/item-extras-editor.test.tsx +0 -85
- package/src/__tests__/traversal.test.ts +0 -377
- package/src/all-editors.test.ts +0 -16
- package/src/all-editors.ts +0 -83
- package/src/all-widgets.ts +0 -7
- package/src/article-editor.tsx +0 -438
- package/src/components/__stories__/blur-input.stories.tsx +0 -22
- package/src/components/__stories__/section-control-button.stories.tsx +0 -26
- package/src/components/__tests__/blur-input.test.tsx +0 -67
- package/src/components/blur-input.tsx +0 -69
- package/src/components/checkbox.tsx +0 -194
- package/src/components/device-framer.tsx +0 -90
- package/src/components/drag-target.tsx +0 -87
- package/src/components/dropdown-option.tsx +0 -311
- package/src/components/form-wrapped-text-field.tsx +0 -245
- package/src/components/graph-points-count-selector.tsx +0 -31
- package/src/components/graph-settings.tsx +0 -627
- package/src/components/graph-type-selector.tsx +0 -31
- package/src/components/hover-behavior.ts +0 -177
- package/src/components/json-editor.tsx +0 -121
- package/src/components/link.ts +0 -173
- package/src/components/section-control-button.tsx +0 -53
- package/src/components/segment-count-selector.tsx +0 -27
- package/src/components/simple-button.tsx +0 -85
- package/src/components/sortable.tsx +0 -187
- package/src/components/util.ts +0 -38
- package/src/components/viewport-resizer.tsx +0 -72
- package/src/components/widget-editor.tsx +0 -222
- package/src/components/widget-select.tsx +0 -48
- package/src/diffs/__stories__/perseus-diff-wrapper.tsx +0 -11
- package/src/diffs/__stories__/structured-item-diff.stories.tsx +0 -855
- package/src/diffs/__stories__/tags-diff.stories.tsx +0 -36
- package/src/diffs/__stories__/text-diff.stories.tsx +0 -35
- package/src/diffs/__tests__/split-diff.test.ts +0 -33
- package/src/diffs/__tests__/string-array-diff.test.ts +0 -73
- package/src/diffs/__tests__/structured-item-diff.test.ts +0 -433
- package/src/diffs/__tests__/widget-diff-performer.test.ts +0 -43
- package/src/diffs/article-diff.tsx +0 -69
- package/src/diffs/item-diff.tsx +0 -76
- package/src/diffs/renderer-diff.tsx +0 -137
- package/src/diffs/split-diff.ts +0 -31
- package/src/diffs/string-array-diff.ts +0 -74
- package/src/diffs/structured-item-diff.tsx +0 -275
- package/src/diffs/tags-diff.tsx +0 -49
- package/src/diffs/text-diff.tsx +0 -193
- package/src/diffs/widget-diff-performer.ts +0 -69
- package/src/diffs/widget-diff.tsx +0 -290
- package/src/editor-page.tsx +0 -325
- package/src/editor.tsx +0 -1099
- package/src/hint-editor.tsx +0 -469
- package/src/i18n.ts +0 -140
- package/src/iframe-content-renderer.tsx +0 -188
- package/src/index.ts +0 -30
- package/src/item-editor.tsx +0 -158
- package/src/item-extras-editor.tsx +0 -230
- package/src/katex-mhchem/index.js +0 -73
- package/src/katex-mhchem/parser.js +0 -2659
- package/src/multirenderer-editor.tsx +0 -1061
- package/src/stateful-article-editor.tsx +0 -173
- package/src/stateful-editor-page.tsx +0 -88
- package/src/styles/perseus-editor.less +0 -1321
- package/src/testing-widgets.ts +0 -11
- package/src/tex-error-view.tsx +0 -90
- package/src/util/points.test.ts +0 -24
- package/src/util/points.ts +0 -13
- package/src/util/register-all-widgets-and-editors-for-testing.test.ts +0 -17
- package/src/util/register-all-widgets-and-editors-for-testing.ts +0 -17
- package/src/version.ts +0 -10
- package/src/widgets/__stories__/categorizer-editor.stories.tsx +0 -24
- package/src/widgets/__stories__/definition-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/dropdown-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/explanation-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/expression-editor.stories.tsx +0 -122
- package/src/widgets/__stories__/image-editor.stories.tsx +0 -72
- package/src/widgets/__stories__/input-number-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/interaction-editor.stories.tsx +0 -37
- package/src/widgets/__stories__/label-image-editor.stories.tsx +0 -99
- package/src/widgets/__stories__/matcher-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/number-line-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/numeric-input-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/radio-editor.stories.tsx +0 -116
- package/src/widgets/__stories__/sorter-editor.stories.tsx +0 -18
- package/src/widgets/__tests__/categorizer-editor.test.tsx +0 -51
- package/src/widgets/__tests__/definition-editor.test.tsx +0 -41
- package/src/widgets/__tests__/dropdown-editor.test.tsx +0 -57
- package/src/widgets/__tests__/explanation-editor.test.tsx +0 -59
- package/src/widgets/__tests__/expression-editor.test.tsx +0 -403
- package/src/widgets/__tests__/input-number-editor.test.tsx +0 -128
- package/src/widgets/__tests__/matcher-editor.test.tsx +0 -47
- package/src/widgets/__tests__/number-line-editor.test.tsx +0 -144
- package/src/widgets/__tests__/numeric-input-editor.test.tsx +0 -177
- package/src/widgets/__tests__/radio-editor.test.tsx +0 -118
- package/src/widgets/__tests__/sorter-editor.test.tsx +0 -55
- package/src/widgets/categorizer-editor.tsx +0 -104
- package/src/widgets/cs-program-editor.tsx +0 -256
- package/src/widgets/definition-editor.tsx +0 -77
- package/src/widgets/deprecated-standin-editor.tsx +0 -29
- package/src/widgets/dropdown-editor.tsx +0 -211
- package/src/widgets/example-graphie-widget-editor.tsx +0 -70
- package/src/widgets/example-graphie-widget.tsx +0 -172
- package/src/widgets/example-widget-editor.tsx +0 -62
- package/src/widgets/example-widget.tsx +0 -154
- package/src/widgets/explanation-editor.tsx +0 -92
- package/src/widgets/expression-editor.tsx +0 -557
- package/src/widgets/graded-group-editor.tsx +0 -161
- package/src/widgets/graded-group-set-editor.tsx +0 -104
- package/src/widgets/grapher-editor.tsx +0 -155
- package/src/widgets/group-editor.tsx +0 -83
- package/src/widgets/iframe-editor.tsx +0 -202
- package/src/widgets/image-editor.tsx +0 -367
- package/src/widgets/input-number-editor.tsx +0 -288
- package/src/widgets/interaction-editor/constraint-editor.tsx +0 -134
- package/src/widgets/interaction-editor/function-editor.tsx +0 -108
- package/src/widgets/interaction-editor/index.ts +0 -3
- package/src/widgets/interaction-editor/interaction-editor.tsx +0 -749
- package/src/widgets/interaction-editor/label-editor.tsx +0 -87
- package/src/widgets/interaction-editor/line-editor.tsx +0 -125
- package/src/widgets/interaction-editor/mathquill-input.tsx +0 -81
- package/src/widgets/interaction-editor/movable-line-editor.tsx +0 -121
- package/src/widgets/interaction-editor/movable-point-editor.tsx +0 -89
- package/src/widgets/interaction-editor/parametric-editor.tsx +0 -117
- package/src/widgets/interaction-editor/point-editor.tsx +0 -72
- package/src/widgets/interaction-editor/rectangle-editor.tsx +0 -96
- package/src/widgets/interactive-graph-editor.tsx +0 -571
- package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -82
- package/src/widgets/label-image/__stories__/behavior.stories.tsx +0 -31
- package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -75
- package/src/widgets/label-image/__stories__/question-markers.stories.tsx +0 -107
- package/src/widgets/label-image/__stories__/select-image.stories.tsx +0 -59
- package/src/widgets/label-image/answer-choices.tsx +0 -231
- package/src/widgets/label-image/behavior.tsx +0 -122
- package/src/widgets/label-image/marker.tsx +0 -290
- package/src/widgets/label-image/question-markers.tsx +0 -176
- package/src/widgets/label-image/select-image.tsx +0 -81
- package/src/widgets/label-image-editor.tsx +0 -252
- package/src/widgets/lights-puzzle-editor.tsx +0 -278
- package/src/widgets/matcher-editor.tsx +0 -149
- package/src/widgets/matrix-editor.tsx +0 -156
- package/src/widgets/measurer-editor.tsx +0 -253
- package/src/widgets/molecule-editor.tsx +0 -64
- package/src/widgets/number-line-editor.tsx +0 -512
- package/src/widgets/numeric-input-editor.tsx +0 -536
- package/src/widgets/orderer-editor.tsx +0 -172
- package/src/widgets/passage-editor.tsx +0 -117
- package/src/widgets/passage-ref-editor.tsx +0 -80
- package/src/widgets/passage-ref-target-editor.tsx +0 -49
- package/src/widgets/plotter-editor.tsx +0 -542
- package/src/widgets/radio/editor.tsx +0 -398
- package/src/widgets/reaction-diagram-editor.tsx +0 -131
- package/src/widgets/sequence-editor.tsx +0 -205
- package/src/widgets/simple-markdown-tester-editor.tsx +0 -86
- package/src/widgets/simple-markdown-tester.tsx +0 -92
- package/src/widgets/simulator-editor.tsx +0 -146
- package/src/widgets/sorter-editor.tsx +0 -103
- package/src/widgets/table-editor.tsx +0 -164
- package/src/widgets/unit-editor.tsx +0 -299
- package/src/widgets/video-editor.tsx +0 -73
- package/tsconfig-build.json +0 -22
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,536 +0,0 @@
|
|
|
1
|
-
/* eslint-disable jsx-a11y/anchor-is-valid, react/sort-comp */
|
|
2
|
-
import {
|
|
3
|
-
components,
|
|
4
|
-
icons,
|
|
5
|
-
Changeable,
|
|
6
|
-
EditorJsonify,
|
|
7
|
-
Util,
|
|
8
|
-
} from "@khanacademy/perseus";
|
|
9
|
-
import createReactClass from "create-react-class";
|
|
10
|
-
import * as React from "react";
|
|
11
|
-
import _ from "underscore";
|
|
12
|
-
|
|
13
|
-
import Editor from "../editor";
|
|
14
|
-
|
|
15
|
-
const {
|
|
16
|
-
ButtonGroup,
|
|
17
|
-
InfoTip,
|
|
18
|
-
InlineIcon,
|
|
19
|
-
MultiButtonGroup,
|
|
20
|
-
NumberInput,
|
|
21
|
-
PropCheckBox,
|
|
22
|
-
TextInput,
|
|
23
|
-
} = components;
|
|
24
|
-
const {iconGear, iconTrash} = icons;
|
|
25
|
-
const {firstNumericalParse} = Util;
|
|
26
|
-
|
|
27
|
-
const answerFormButtons = [
|
|
28
|
-
{title: "Integers", value: "integer", content: "6"},
|
|
29
|
-
{title: "Decimals", value: "decimal", content: "0.75"},
|
|
30
|
-
{title: "Proper fractions", value: "proper", content: "\u2157"},
|
|
31
|
-
{
|
|
32
|
-
title: "Improper fractions",
|
|
33
|
-
value: "improper",
|
|
34
|
-
content: "\u2077\u2044\u2084",
|
|
35
|
-
},
|
|
36
|
-
{title: "Mixed numbers", value: "mixed", content: "1\u00BE"},
|
|
37
|
-
{title: "Numbers with \u03C0", value: "pi", content: "\u03C0"},
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
const initAnswer = (status: string) => {
|
|
41
|
-
return {
|
|
42
|
-
value: null,
|
|
43
|
-
status: status,
|
|
44
|
-
message: "",
|
|
45
|
-
simplify: "required",
|
|
46
|
-
answerForms: [],
|
|
47
|
-
strict: false,
|
|
48
|
-
maxError: null,
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const NumericInputEditor: any = createReactClass({
|
|
53
|
-
displayName: "NumericInputEditor",
|
|
54
|
-
|
|
55
|
-
propTypes: {
|
|
56
|
-
...Changeable.propTypes,
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
statics: {
|
|
60
|
-
widgetName: "numeric-input",
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
getDefaultProps: function () {
|
|
64
|
-
return {
|
|
65
|
-
answers: [initAnswer("correct")],
|
|
66
|
-
size: "normal",
|
|
67
|
-
coefficient: false,
|
|
68
|
-
labelText: "",
|
|
69
|
-
rightAlign: false,
|
|
70
|
-
};
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
getInitialState: function () {
|
|
74
|
-
return {
|
|
75
|
-
lastStatus: "wrong",
|
|
76
|
-
showOptions: _.map(this.props.answers, () => false),
|
|
77
|
-
};
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
render: function () {
|
|
81
|
-
const answers = this.props.answers;
|
|
82
|
-
|
|
83
|
-
const unsimplifiedAnswers = (i: any) => (
|
|
84
|
-
<div className="perseus-widget-row">
|
|
85
|
-
<label>Unsimplified answers are</label>
|
|
86
|
-
<ButtonGroup
|
|
87
|
-
value={answers[i]["simplify"]}
|
|
88
|
-
allowEmpty={false}
|
|
89
|
-
buttons={[
|
|
90
|
-
{value: "required", content: "ungraded"},
|
|
91
|
-
{value: "optional", content: "accepted"},
|
|
92
|
-
{value: "enforced", content: "wrong"},
|
|
93
|
-
]}
|
|
94
|
-
onChange={this.updateAnswer(i, "simplify")}
|
|
95
|
-
/>
|
|
96
|
-
<InfoTip>
|
|
97
|
-
<p>
|
|
98
|
-
Normally select "ungraded". This will give the user a
|
|
99
|
-
message saying the answer is correct but not simplified.
|
|
100
|
-
The user will then have to simplify it and re-enter, but
|
|
101
|
-
will not be penalized. (5th grade and after)
|
|
102
|
-
</p>
|
|
103
|
-
<p>
|
|
104
|
-
Select "accepted" only if the user is not expected to
|
|
105
|
-
know how to simplify fractions yet. (Anything prior to
|
|
106
|
-
5th grade)
|
|
107
|
-
</p>
|
|
108
|
-
<p>
|
|
109
|
-
Select "wrong" <em>only</em> if we are specifically
|
|
110
|
-
assessing the ability to simplify.
|
|
111
|
-
</p>
|
|
112
|
-
</InfoTip>
|
|
113
|
-
</div>
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const suggestedAnswerTypes = (i: any) => (
|
|
117
|
-
<div>
|
|
118
|
-
<div className="perseus-widget-row">
|
|
119
|
-
<label>Choose the suggested answer formats</label>
|
|
120
|
-
<MultiButtonGroup
|
|
121
|
-
buttons={answerFormButtons}
|
|
122
|
-
values={answers[i]["answerForms"]}
|
|
123
|
-
onChange={this.updateAnswer(i, "answerForms")}
|
|
124
|
-
/>
|
|
125
|
-
<InfoTip>
|
|
126
|
-
<p>
|
|
127
|
-
Formats will be autoselected for you based on the
|
|
128
|
-
given answer; to show no suggested formats and
|
|
129
|
-
accept all types, simply have a decimal/integer be
|
|
130
|
-
the answer. Values with π will have format "pi",
|
|
131
|
-
and values that are fractions will have some subset
|
|
132
|
-
(mixed will be "mixed" and "proper"; improper/proper
|
|
133
|
-
will both be "improper" and "proper"). If you would
|
|
134
|
-
like to specify that it is only a proper fraction
|
|
135
|
-
(or only a mixed/improper fraction), deselect the
|
|
136
|
-
other format. Except for specific cases, you should
|
|
137
|
-
not need to change the autoselected formats.
|
|
138
|
-
</p>
|
|
139
|
-
<p>
|
|
140
|
-
To restrict the answer to <em>only</em> an improper
|
|
141
|
-
fraction (i.e. 7/4), select the improper fraction
|
|
142
|
-
and toggle "strict" to true. This <b>will not</b>{" "}
|
|
143
|
-
accept 1.75 as an answer.{" "}
|
|
144
|
-
</p>
|
|
145
|
-
<p>
|
|
146
|
-
Unless you are testing that specific skill, please
|
|
147
|
-
do not restrict the answer format.
|
|
148
|
-
</p>
|
|
149
|
-
</InfoTip>
|
|
150
|
-
</div>
|
|
151
|
-
<div className="perseus-widget-row">
|
|
152
|
-
<PropCheckBox
|
|
153
|
-
label="Strictly match only these formats"
|
|
154
|
-
strict={answers[i]["strict"]}
|
|
155
|
-
// eslint-disable-next-line react/jsx-no-bind
|
|
156
|
-
onChange={this.updateAnswer.bind(this, i)}
|
|
157
|
-
/>
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
const maxError = (i: any) => (
|
|
163
|
-
<div className="perseus-widget-row">
|
|
164
|
-
<label>
|
|
165
|
-
Max error{" "}
|
|
166
|
-
<NumberInput
|
|
167
|
-
className="max-error"
|
|
168
|
-
value={answers[i]["maxError"]}
|
|
169
|
-
onChange={this.updateAnswer(i, "maxError")}
|
|
170
|
-
placeholder="0"
|
|
171
|
-
/>
|
|
172
|
-
</label>
|
|
173
|
-
</div>
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
const inputSize = (
|
|
177
|
-
<div className="perseus-widget-row">
|
|
178
|
-
<label>Width: </label>
|
|
179
|
-
<ButtonGroup
|
|
180
|
-
value={this.props.size}
|
|
181
|
-
allowEmpty={false}
|
|
182
|
-
buttons={[
|
|
183
|
-
{value: "normal", content: "Normal (80px)"},
|
|
184
|
-
{value: "small", content: "Small (40px)"},
|
|
185
|
-
]}
|
|
186
|
-
onChange={this.change("size")}
|
|
187
|
-
/>
|
|
188
|
-
<InfoTip>
|
|
189
|
-
<p>
|
|
190
|
-
Use size "Normal" for all text boxes, unless there are
|
|
191
|
-
multiple text boxes in one line and the answer area is
|
|
192
|
-
too narrow to fit them.
|
|
193
|
-
</p>
|
|
194
|
-
</InfoTip>
|
|
195
|
-
</div>
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
const rightAlign = (
|
|
199
|
-
<div className="perseus-widget-row">
|
|
200
|
-
<PropCheckBox
|
|
201
|
-
label="Right alignment"
|
|
202
|
-
rightAlign={this.props.rightAlign}
|
|
203
|
-
onChange={this.props.onChange}
|
|
204
|
-
/>
|
|
205
|
-
</div>
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
const labelText = (
|
|
209
|
-
<div className="perseus-widget-row">
|
|
210
|
-
<label>
|
|
211
|
-
Label text:{" "}
|
|
212
|
-
<TextInput
|
|
213
|
-
value={this.props.labelText}
|
|
214
|
-
onChange={this.change("labelText")}
|
|
215
|
-
/>
|
|
216
|
-
</label>
|
|
217
|
-
<InfoTip>
|
|
218
|
-
<p>
|
|
219
|
-
Text to describe this input. This will be shown to users
|
|
220
|
-
using screenreaders.
|
|
221
|
-
</p>
|
|
222
|
-
</InfoTip>
|
|
223
|
-
</div>
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
const coefficientCheck = (
|
|
227
|
-
<div>
|
|
228
|
-
<div className="perseus-widget-row">
|
|
229
|
-
<PropCheckBox
|
|
230
|
-
label="Coefficient"
|
|
231
|
-
coefficient={this.props.coefficient}
|
|
232
|
-
onChange={this.props.onChange}
|
|
233
|
-
/>
|
|
234
|
-
<InfoTip>
|
|
235
|
-
<p>
|
|
236
|
-
A coefficient style number allows the student to use
|
|
237
|
-
- for -1 and an empty string to mean 1.
|
|
238
|
-
</p>
|
|
239
|
-
</InfoTip>
|
|
240
|
-
</div>
|
|
241
|
-
</div>
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
const addAnswerButton = (
|
|
245
|
-
<div>
|
|
246
|
-
<a
|
|
247
|
-
href="javascript:void(0)"
|
|
248
|
-
className="simple-button orange"
|
|
249
|
-
onClick={() => this.addAnswer()}
|
|
250
|
-
onKeyDown={(e) => this.onSpace(e, this.addAnswer)}
|
|
251
|
-
>
|
|
252
|
-
<span>Add new answer</span>
|
|
253
|
-
</a>
|
|
254
|
-
</div>
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
const instructions = {
|
|
258
|
-
wrong: "(address the mistake/misconception)",
|
|
259
|
-
ungraded: "(explain in detail to avoid confusion)",
|
|
260
|
-
correct: "(reinforce the user's understanding)",
|
|
261
|
-
} as const;
|
|
262
|
-
|
|
263
|
-
const generateInputAnswerEditors = () =>
|
|
264
|
-
answers.map((answer, i) => {
|
|
265
|
-
const editor = (
|
|
266
|
-
<Editor
|
|
267
|
-
apiOptions={this.props.apiOptions}
|
|
268
|
-
content={answer.message || ""}
|
|
269
|
-
placeholder={
|
|
270
|
-
"Why is this answer " +
|
|
271
|
-
answer.status +
|
|
272
|
-
"?\t" +
|
|
273
|
-
instructions[answer.status]
|
|
274
|
-
}
|
|
275
|
-
widgetEnabled={false}
|
|
276
|
-
onChange={(newProps) => {
|
|
277
|
-
if ("content" in newProps) {
|
|
278
|
-
this.updateAnswer(i, {
|
|
279
|
-
message: newProps.content,
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
}}
|
|
283
|
-
/>
|
|
284
|
-
);
|
|
285
|
-
return (
|
|
286
|
-
<div className="perseus-widget-row" key={i}>
|
|
287
|
-
<div
|
|
288
|
-
className={
|
|
289
|
-
"input-answer-editor-value-container" +
|
|
290
|
-
(answer.maxError ? " with-max-error" : "")
|
|
291
|
-
}
|
|
292
|
-
>
|
|
293
|
-
<NumberInput
|
|
294
|
-
value={answer.value}
|
|
295
|
-
className="numeric-input-value"
|
|
296
|
-
placeholder="answer"
|
|
297
|
-
format={_.last(answer.answerForms)}
|
|
298
|
-
onFormatChange={(newValue, format) => {
|
|
299
|
-
// NOTE(charlie): The mobile web expression
|
|
300
|
-
// editor relies on this automatic answer
|
|
301
|
-
// form resolution for determining when to
|
|
302
|
-
// show the Pi symbol. If we get rid of it,
|
|
303
|
-
// we should also disable Pi for
|
|
304
|
-
// NumericInput and require problems that
|
|
305
|
-
// use Pi to build on Expression.
|
|
306
|
-
// Alternatively, we could store answers
|
|
307
|
-
// as plaintext and parse them to determine
|
|
308
|
-
// whether or not to reveal Pi on the
|
|
309
|
-
// keypad (right now, answers are stored as
|
|
310
|
-
// resolved values, like '0.125' rather
|
|
311
|
-
// than '1/8').
|
|
312
|
-
let forms;
|
|
313
|
-
if (format === "pi") {
|
|
314
|
-
forms = ["pi"];
|
|
315
|
-
} else if (format === "mixed") {
|
|
316
|
-
forms = ["proper", "mixed"];
|
|
317
|
-
} else if (
|
|
318
|
-
format === "proper" ||
|
|
319
|
-
format === "improper"
|
|
320
|
-
) {
|
|
321
|
-
forms = ["proper", "improper"];
|
|
322
|
-
}
|
|
323
|
-
this.updateAnswer(i, {
|
|
324
|
-
value: firstNumericalParse(newValue),
|
|
325
|
-
answerForms: forms,
|
|
326
|
-
});
|
|
327
|
-
}}
|
|
328
|
-
onChange={(newValue) => {
|
|
329
|
-
this.updateAnswer(i, {
|
|
330
|
-
value: firstNumericalParse(newValue),
|
|
331
|
-
});
|
|
332
|
-
}}
|
|
333
|
-
/>
|
|
334
|
-
{answer.strict && (
|
|
335
|
-
<div
|
|
336
|
-
className="is-strict-indicator"
|
|
337
|
-
title="strictly equivalent to"
|
|
338
|
-
>
|
|
339
|
-
≡
|
|
340
|
-
</div>
|
|
341
|
-
)}
|
|
342
|
-
{answer.simplify !== "required" &&
|
|
343
|
-
answer.status === "correct" && (
|
|
344
|
-
<div
|
|
345
|
-
className={
|
|
346
|
-
"simplify-indicator " +
|
|
347
|
-
answer.simplify
|
|
348
|
-
}
|
|
349
|
-
title="accepts unsimplified answers"
|
|
350
|
-
>
|
|
351
|
-
‰
|
|
352
|
-
</div>
|
|
353
|
-
)}
|
|
354
|
-
{answer.maxError ? (
|
|
355
|
-
<div className="max-error-container">
|
|
356
|
-
<div className="max-error-plusmn">
|
|
357
|
-
±
|
|
358
|
-
</div>
|
|
359
|
-
<NumberInput
|
|
360
|
-
placeholder={0}
|
|
361
|
-
value={answers[i]["maxError"]}
|
|
362
|
-
format={_.last(answer.answerForms)}
|
|
363
|
-
onChange={this.updateAnswer(
|
|
364
|
-
i,
|
|
365
|
-
"maxError",
|
|
366
|
-
)}
|
|
367
|
-
/>
|
|
368
|
-
</div>
|
|
369
|
-
) : null}
|
|
370
|
-
<div className="value-divider" />
|
|
371
|
-
<a
|
|
372
|
-
href="javascript:void(0)"
|
|
373
|
-
className={"answer-status " + answer.status}
|
|
374
|
-
onClick={() => this.onStatusChange(i)}
|
|
375
|
-
onKeyDown={(e) =>
|
|
376
|
-
this.onSpace(e, this.onStatusChange, i)
|
|
377
|
-
}
|
|
378
|
-
>
|
|
379
|
-
{answer.status}
|
|
380
|
-
</a>
|
|
381
|
-
<a
|
|
382
|
-
href="javascript:void(0)"
|
|
383
|
-
className="answer-trash"
|
|
384
|
-
aria-label="Delete answer"
|
|
385
|
-
onClick={() => this.onTrashAnswer(i)}
|
|
386
|
-
onKeyDown={(e) =>
|
|
387
|
-
this.onSpace(e, this.onTrashAnswer, i)
|
|
388
|
-
}
|
|
389
|
-
>
|
|
390
|
-
<InlineIcon {...iconTrash} />
|
|
391
|
-
</a>
|
|
392
|
-
<a
|
|
393
|
-
href="javascript:void(0)"
|
|
394
|
-
className="options-toggle"
|
|
395
|
-
aria-label="Toggle options"
|
|
396
|
-
onClick={() => this.onToggleOptions(i)}
|
|
397
|
-
onKeyDown={(e) =>
|
|
398
|
-
this.onSpace(e, this.onToggleOptions, i)
|
|
399
|
-
}
|
|
400
|
-
>
|
|
401
|
-
<InlineIcon {...iconGear} />
|
|
402
|
-
</a>
|
|
403
|
-
</div>
|
|
404
|
-
<div className="input-answer-editor-message">
|
|
405
|
-
{editor}
|
|
406
|
-
</div>
|
|
407
|
-
{this.state.showOptions[i] && (
|
|
408
|
-
<div className="options-container">
|
|
409
|
-
{maxError(i)}
|
|
410
|
-
{answer.status === "correct" &&
|
|
411
|
-
unsimplifiedAnswers(i)}
|
|
412
|
-
{suggestedAnswerTypes(i)}
|
|
413
|
-
</div>
|
|
414
|
-
)}
|
|
415
|
-
</div>
|
|
416
|
-
);
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
return (
|
|
420
|
-
<div className="perseus-input-number-editor">
|
|
421
|
-
<div className="ui-title">User input</div>
|
|
422
|
-
<div className="msg-title">
|
|
423
|
-
Message shown to user on attempt
|
|
424
|
-
</div>
|
|
425
|
-
{generateInputAnswerEditors()}
|
|
426
|
-
{addAnswerButton}
|
|
427
|
-
{inputSize}
|
|
428
|
-
{rightAlign}
|
|
429
|
-
{coefficientCheck}
|
|
430
|
-
{labelText}
|
|
431
|
-
</div>
|
|
432
|
-
);
|
|
433
|
-
},
|
|
434
|
-
|
|
435
|
-
change(...args) {
|
|
436
|
-
return Changeable.change.apply(this, args);
|
|
437
|
-
},
|
|
438
|
-
|
|
439
|
-
onToggleOptions: function (choiceIndex) {
|
|
440
|
-
const showOptions = this.state.showOptions.slice();
|
|
441
|
-
showOptions[choiceIndex] = !showOptions[choiceIndex];
|
|
442
|
-
this.setState({showOptions: showOptions});
|
|
443
|
-
},
|
|
444
|
-
|
|
445
|
-
onTrashAnswer: function (choiceIndex) {
|
|
446
|
-
if (choiceIndex >= 0 && choiceIndex < this.props.answers.length) {
|
|
447
|
-
const answers = this.props.answers.slice(0);
|
|
448
|
-
answers.splice(choiceIndex, 1);
|
|
449
|
-
this.props.onChange({answers: answers});
|
|
450
|
-
}
|
|
451
|
-
},
|
|
452
|
-
|
|
453
|
-
onSpace: function (e, callback) {
|
|
454
|
-
if (e.key === " ") {
|
|
455
|
-
e.preventDefault(); // prevent page shifting
|
|
456
|
-
// eslint-disable-next-line prefer-rest-params
|
|
457
|
-
const args = _.toArray(arguments).slice(2);
|
|
458
|
-
callback.apply(this, args);
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
|
|
462
|
-
onStatusChange: function (choiceIndex) {
|
|
463
|
-
const statuses = ["wrong", "ungraded", "correct"];
|
|
464
|
-
const answers = this.props.answers;
|
|
465
|
-
const i = _.indexOf(statuses, answers[choiceIndex].status);
|
|
466
|
-
const newStatus = statuses[(i + 1) % statuses.length];
|
|
467
|
-
|
|
468
|
-
this.updateAnswer(choiceIndex, {
|
|
469
|
-
status: newStatus,
|
|
470
|
-
simplify: newStatus === "correct" ? "required" : "accepted",
|
|
471
|
-
});
|
|
472
|
-
},
|
|
473
|
-
|
|
474
|
-
updateAnswer: function (choiceIndex, update) {
|
|
475
|
-
if (!_.isObject(update)) {
|
|
476
|
-
return _.partial(
|
|
477
|
-
(choiceIndex, key, value) => {
|
|
478
|
-
const update: Record<string, any> = {};
|
|
479
|
-
update[key] = value;
|
|
480
|
-
this.updateAnswer(choiceIndex, update);
|
|
481
|
-
},
|
|
482
|
-
choiceIndex,
|
|
483
|
-
update,
|
|
484
|
-
);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
let answers = _.clone(this.props.answers);
|
|
488
|
-
|
|
489
|
-
// Don't bother to make a new answer box unless we are editing the last
|
|
490
|
-
// one.
|
|
491
|
-
// TODO(oliver): This might not be necessary anymore.
|
|
492
|
-
if (choiceIndex === answers.length) {
|
|
493
|
-
const lastAnswer = initAnswer(this.state.lastStatus);
|
|
494
|
-
answers = answers.concat(lastAnswer);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
answers[choiceIndex] = _.extend({}, answers[choiceIndex], update);
|
|
498
|
-
this.props.onChange({answers: answers});
|
|
499
|
-
},
|
|
500
|
-
|
|
501
|
-
addAnswer: function () {
|
|
502
|
-
const lastAnswer = initAnswer(this.state.lastStatus);
|
|
503
|
-
const answers = this.props.answers.concat(lastAnswer);
|
|
504
|
-
this.props.onChange({answers: answers});
|
|
505
|
-
},
|
|
506
|
-
|
|
507
|
-
getSaveWarnings: function () {
|
|
508
|
-
// Filter out all the empty answers
|
|
509
|
-
const warnings = [];
|
|
510
|
-
// TODO(emily): This doesn't actually work, because the value is either
|
|
511
|
-
// null or undefined when undefined, probably.
|
|
512
|
-
if (_.contains(_.pluck(this.props.answers, "value"), "")) {
|
|
513
|
-
// @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'never'.
|
|
514
|
-
warnings.push("One or more answers is empty");
|
|
515
|
-
}
|
|
516
|
-
this.props.answers.forEach((answer, i) => {
|
|
517
|
-
const formatError =
|
|
518
|
-
answer.strict &&
|
|
519
|
-
(!answer.answerForms || answer.answerForms.length === 0);
|
|
520
|
-
if (formatError) {
|
|
521
|
-
warnings.push(
|
|
522
|
-
// @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'never'.
|
|
523
|
-
`Answer ${i + 1} is set to string format ` +
|
|
524
|
-
"matching, but no format was selected",
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
});
|
|
528
|
-
return warnings;
|
|
529
|
-
},
|
|
530
|
-
|
|
531
|
-
serialize() {
|
|
532
|
-
return EditorJsonify.serialize.call(this);
|
|
533
|
-
},
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
export default NumericInputEditor;
|