@khanacademy/perseus-editor 4.1.5 → 4.1.6

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 (175) hide show
  1. package/dist/es/index.js +1 -1
  2. package/dist/index.js +1 -1
  3. package/package.json +9 -6
  4. package/.babelrc.js +0 -8
  5. package/.eslintrc.js +0 -12
  6. package/CHANGELOG.md +0 -1155
  7. package/src/README.md +0 -7
  8. package/src/__stories__/editor-page.stories.tsx +0 -65
  9. package/src/__stories__/editor.stories.tsx +0 -134
  10. package/src/__stories__/item-editor.stories.tsx +0 -41
  11. package/src/__stories__/tex-error-view.stories.tsx +0 -26
  12. package/src/__testdata__/input-number.testdata.ts +0 -27
  13. package/src/__tests__/editor.test.tsx +0 -142
  14. package/src/__tests__/hint-editor.test.tsx +0 -83
  15. package/src/__tests__/i18n.test.ts +0 -501
  16. package/src/__tests__/integration.test.ts +0 -5
  17. package/src/__tests__/item-extras-editor.test.tsx +0 -85
  18. package/src/__tests__/traversal.test.ts +0 -377
  19. package/src/all-editors.test.ts +0 -16
  20. package/src/all-editors.ts +0 -83
  21. package/src/all-widgets.ts +0 -7
  22. package/src/article-editor.tsx +0 -438
  23. package/src/components/__stories__/blur-input.stories.tsx +0 -22
  24. package/src/components/__stories__/section-control-button.stories.tsx +0 -26
  25. package/src/components/__tests__/blur-input.test.tsx +0 -67
  26. package/src/components/blur-input.tsx +0 -69
  27. package/src/components/checkbox.tsx +0 -194
  28. package/src/components/device-framer.tsx +0 -90
  29. package/src/components/drag-target.tsx +0 -87
  30. package/src/components/dropdown-option.tsx +0 -311
  31. package/src/components/form-wrapped-text-field.tsx +0 -245
  32. package/src/components/graph-points-count-selector.tsx +0 -31
  33. package/src/components/graph-settings.tsx +0 -627
  34. package/src/components/graph-type-selector.tsx +0 -31
  35. package/src/components/hover-behavior.ts +0 -177
  36. package/src/components/json-editor.tsx +0 -121
  37. package/src/components/link.ts +0 -173
  38. package/src/components/section-control-button.tsx +0 -53
  39. package/src/components/segment-count-selector.tsx +0 -27
  40. package/src/components/simple-button.tsx +0 -85
  41. package/src/components/sortable.tsx +0 -187
  42. package/src/components/util.ts +0 -38
  43. package/src/components/viewport-resizer.tsx +0 -72
  44. package/src/components/widget-editor.tsx +0 -222
  45. package/src/components/widget-select.tsx +0 -48
  46. package/src/diffs/__stories__/perseus-diff-wrapper.tsx +0 -11
  47. package/src/diffs/__stories__/structured-item-diff.stories.tsx +0 -855
  48. package/src/diffs/__stories__/tags-diff.stories.tsx +0 -36
  49. package/src/diffs/__stories__/text-diff.stories.tsx +0 -35
  50. package/src/diffs/__tests__/split-diff.test.ts +0 -33
  51. package/src/diffs/__tests__/string-array-diff.test.ts +0 -73
  52. package/src/diffs/__tests__/structured-item-diff.test.ts +0 -433
  53. package/src/diffs/__tests__/widget-diff-performer.test.ts +0 -43
  54. package/src/diffs/article-diff.tsx +0 -69
  55. package/src/diffs/item-diff.tsx +0 -76
  56. package/src/diffs/renderer-diff.tsx +0 -137
  57. package/src/diffs/split-diff.ts +0 -31
  58. package/src/diffs/string-array-diff.ts +0 -74
  59. package/src/diffs/structured-item-diff.tsx +0 -275
  60. package/src/diffs/tags-diff.tsx +0 -49
  61. package/src/diffs/text-diff.tsx +0 -193
  62. package/src/diffs/widget-diff-performer.ts +0 -69
  63. package/src/diffs/widget-diff.tsx +0 -290
  64. package/src/editor-page.tsx +0 -325
  65. package/src/editor.tsx +0 -1099
  66. package/src/hint-editor.tsx +0 -469
  67. package/src/i18n.ts +0 -140
  68. package/src/iframe-content-renderer.tsx +0 -188
  69. package/src/index.ts +0 -30
  70. package/src/item-editor.tsx +0 -158
  71. package/src/item-extras-editor.tsx +0 -230
  72. package/src/katex-mhchem/index.js +0 -73
  73. package/src/katex-mhchem/parser.js +0 -2659
  74. package/src/multirenderer-editor.tsx +0 -1061
  75. package/src/stateful-article-editor.tsx +0 -173
  76. package/src/stateful-editor-page.tsx +0 -88
  77. package/src/styles/perseus-editor.less +0 -1321
  78. package/src/testing-widgets.ts +0 -11
  79. package/src/tex-error-view.tsx +0 -90
  80. package/src/util/points.test.ts +0 -24
  81. package/src/util/points.ts +0 -13
  82. package/src/util/register-all-widgets-and-editors-for-testing.test.ts +0 -17
  83. package/src/util/register-all-widgets-and-editors-for-testing.ts +0 -17
  84. package/src/version.ts +0 -10
  85. package/src/widgets/__stories__/categorizer-editor.stories.tsx +0 -24
  86. package/src/widgets/__stories__/definition-editor.stories.tsx +0 -18
  87. package/src/widgets/__stories__/dropdown-editor.stories.tsx +0 -18
  88. package/src/widgets/__stories__/explanation-editor.stories.tsx +0 -18
  89. package/src/widgets/__stories__/expression-editor.stories.tsx +0 -122
  90. package/src/widgets/__stories__/image-editor.stories.tsx +0 -72
  91. package/src/widgets/__stories__/input-number-editor.stories.tsx +0 -18
  92. package/src/widgets/__stories__/interaction-editor.stories.tsx +0 -37
  93. package/src/widgets/__stories__/label-image-editor.stories.tsx +0 -99
  94. package/src/widgets/__stories__/matcher-editor.stories.tsx +0 -18
  95. package/src/widgets/__stories__/number-line-editor.stories.tsx +0 -18
  96. package/src/widgets/__stories__/numeric-input-editor.stories.tsx +0 -18
  97. package/src/widgets/__stories__/radio-editor.stories.tsx +0 -116
  98. package/src/widgets/__stories__/sorter-editor.stories.tsx +0 -18
  99. package/src/widgets/__tests__/categorizer-editor.test.tsx +0 -51
  100. package/src/widgets/__tests__/definition-editor.test.tsx +0 -41
  101. package/src/widgets/__tests__/dropdown-editor.test.tsx +0 -57
  102. package/src/widgets/__tests__/explanation-editor.test.tsx +0 -59
  103. package/src/widgets/__tests__/expression-editor.test.tsx +0 -403
  104. package/src/widgets/__tests__/input-number-editor.test.tsx +0 -128
  105. package/src/widgets/__tests__/matcher-editor.test.tsx +0 -47
  106. package/src/widgets/__tests__/number-line-editor.test.tsx +0 -144
  107. package/src/widgets/__tests__/numeric-input-editor.test.tsx +0 -177
  108. package/src/widgets/__tests__/radio-editor.test.tsx +0 -118
  109. package/src/widgets/__tests__/sorter-editor.test.tsx +0 -55
  110. package/src/widgets/categorizer-editor.tsx +0 -104
  111. package/src/widgets/cs-program-editor.tsx +0 -256
  112. package/src/widgets/definition-editor.tsx +0 -77
  113. package/src/widgets/deprecated-standin-editor.tsx +0 -29
  114. package/src/widgets/dropdown-editor.tsx +0 -211
  115. package/src/widgets/example-graphie-widget-editor.tsx +0 -70
  116. package/src/widgets/example-graphie-widget.tsx +0 -172
  117. package/src/widgets/example-widget-editor.tsx +0 -62
  118. package/src/widgets/example-widget.tsx +0 -154
  119. package/src/widgets/explanation-editor.tsx +0 -92
  120. package/src/widgets/expression-editor.tsx +0 -557
  121. package/src/widgets/graded-group-editor.tsx +0 -161
  122. package/src/widgets/graded-group-set-editor.tsx +0 -104
  123. package/src/widgets/grapher-editor.tsx +0 -155
  124. package/src/widgets/group-editor.tsx +0 -83
  125. package/src/widgets/iframe-editor.tsx +0 -202
  126. package/src/widgets/image-editor.tsx +0 -367
  127. package/src/widgets/input-number-editor.tsx +0 -288
  128. package/src/widgets/interaction-editor/constraint-editor.tsx +0 -134
  129. package/src/widgets/interaction-editor/function-editor.tsx +0 -108
  130. package/src/widgets/interaction-editor/index.ts +0 -3
  131. package/src/widgets/interaction-editor/interaction-editor.tsx +0 -749
  132. package/src/widgets/interaction-editor/label-editor.tsx +0 -87
  133. package/src/widgets/interaction-editor/line-editor.tsx +0 -125
  134. package/src/widgets/interaction-editor/mathquill-input.tsx +0 -81
  135. package/src/widgets/interaction-editor/movable-line-editor.tsx +0 -121
  136. package/src/widgets/interaction-editor/movable-point-editor.tsx +0 -89
  137. package/src/widgets/interaction-editor/parametric-editor.tsx +0 -117
  138. package/src/widgets/interaction-editor/point-editor.tsx +0 -72
  139. package/src/widgets/interaction-editor/rectangle-editor.tsx +0 -96
  140. package/src/widgets/interactive-graph-editor.tsx +0 -571
  141. package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -82
  142. package/src/widgets/label-image/__stories__/behavior.stories.tsx +0 -31
  143. package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -75
  144. package/src/widgets/label-image/__stories__/question-markers.stories.tsx +0 -107
  145. package/src/widgets/label-image/__stories__/select-image.stories.tsx +0 -59
  146. package/src/widgets/label-image/answer-choices.tsx +0 -231
  147. package/src/widgets/label-image/behavior.tsx +0 -122
  148. package/src/widgets/label-image/marker.tsx +0 -290
  149. package/src/widgets/label-image/question-markers.tsx +0 -176
  150. package/src/widgets/label-image/select-image.tsx +0 -81
  151. package/src/widgets/label-image-editor.tsx +0 -252
  152. package/src/widgets/lights-puzzle-editor.tsx +0 -278
  153. package/src/widgets/matcher-editor.tsx +0 -149
  154. package/src/widgets/matrix-editor.tsx +0 -156
  155. package/src/widgets/measurer-editor.tsx +0 -253
  156. package/src/widgets/molecule-editor.tsx +0 -64
  157. package/src/widgets/number-line-editor.tsx +0 -512
  158. package/src/widgets/numeric-input-editor.tsx +0 -536
  159. package/src/widgets/orderer-editor.tsx +0 -172
  160. package/src/widgets/passage-editor.tsx +0 -117
  161. package/src/widgets/passage-ref-editor.tsx +0 -80
  162. package/src/widgets/passage-ref-target-editor.tsx +0 -49
  163. package/src/widgets/plotter-editor.tsx +0 -542
  164. package/src/widgets/radio/editor.tsx +0 -398
  165. package/src/widgets/reaction-diagram-editor.tsx +0 -131
  166. package/src/widgets/sequence-editor.tsx +0 -205
  167. package/src/widgets/simple-markdown-tester-editor.tsx +0 -86
  168. package/src/widgets/simple-markdown-tester.tsx +0 -92
  169. package/src/widgets/simulator-editor.tsx +0 -146
  170. package/src/widgets/sorter-editor.tsx +0 -103
  171. package/src/widgets/table-editor.tsx +0 -164
  172. package/src/widgets/unit-editor.tsx +0 -299
  173. package/src/widgets/video-editor.tsx +0 -73
  174. package/tsconfig-build.json +0 -22
  175. package/tsconfig-build.tsbuildinfo +0 -1
@@ -1,557 +0,0 @@
1
- /* eslint-disable react/sort-comp */
2
- import * as KAS from "@khanacademy/kas";
3
- import {
4
- components,
5
- Changeable,
6
- Dependencies,
7
- Expression,
8
- PerseusExpressionAnswerFormConsidered,
9
- } from "@khanacademy/perseus";
10
- import {isTruthy} from "@khanacademy/wonder-stuff-core";
11
- // eslint-disable-next-line import/no-extraneous-dependencies
12
- import lens from "hubble";
13
- import * as React from "react";
14
- import _ from "underscore";
15
-
16
- import SortableArea from "../components/sortable";
17
-
18
- import type {PerseusExpressionWidgetOptions} from "@khanacademy/perseus";
19
-
20
- const {InfoTip, PropCheckBox} = components;
21
-
22
- type Props = {
23
- widgetId?: any;
24
- value?: string;
25
- } & Omit<PerseusExpressionWidgetOptions, "buttonsVisible"> &
26
- Changeable.ChangeableProps;
27
-
28
- // types for iterables
29
- type AnswerForm = PerseusExpressionWidgetOptions["answerForms"][number];
30
- type LegacyButtonSet = PerseusExpressionWidgetOptions["buttonSets"][number];
31
-
32
- type DefaultProps = {
33
- answerForms: Props["answerForms"];
34
- times: Props["times"];
35
- buttonSets: Props["buttonSets"];
36
- functions: Props["functions"];
37
- };
38
-
39
- const buttonSetsList: any = [
40
- "basic",
41
- "trig",
42
- "prealgebra",
43
- "logarithms",
44
- "basic relations",
45
- "advanced relations",
46
- ];
47
-
48
- const parseAnswerKey = ({key}: AnswerForm): number => {
49
- // We don't throw here because there is data stored in some
50
- // exercises/articles where the answer forms don't have a key. If we throw,
51
- // it blocks content editors from loading the page at all.
52
- // TODO(Jeremy): find a way to handle these answer forms that are missing
53
- // keys more gracefully.
54
- return Number.parseInt(key ?? "");
55
- };
56
-
57
- // Pick a key that isn't currently used by an answer in answerForms
58
- const _makeNewKey = (answerForms: ReadonlyArray<AnswerForm>) => {
59
- // first note all the currently used keys in an array, used like a map :3
60
- // note that this automatically updates the array's length property to
61
- // be one past the largest key.
62
- const usedKeys: Array<boolean> = [];
63
- answerForms.forEach((ans) => {
64
- usedKeys[parseAnswerKey(ans)] = true;
65
- });
66
-
67
- // then scan through the array to find the first unused (undefined) key
68
- for (let i = 0; i < usedKeys.length; i++) {
69
- if (!usedKeys[i]) {
70
- return i;
71
- }
72
- }
73
-
74
- // if we didn't find a key, make one bigger than all the other keys,
75
- // since that's how the length property is defined to work on arrays
76
- return usedKeys.length;
77
- };
78
-
79
- class ExpressionEditor extends React.Component<Props> {
80
- static widgetName = "expression" as const;
81
-
82
- static defaultProps: DefaultProps = {
83
- answerForms: [],
84
- times: false,
85
- buttonSets: ["basic"],
86
- functions: ["f", "g", "h"],
87
- };
88
-
89
- change(...args) {
90
- return Changeable.change.apply(this, args);
91
- }
92
-
93
- render(): React.ReactNode {
94
- const answerOptions = this.props.answerForms
95
- .map((ans: AnswerForm) => {
96
- const key = parseAnswerKey(ans);
97
-
98
- const expressionProps = {
99
- // note we're using
100
- // *this.props*.{times,functions,buttonSets} since each
101
- // answer area has the same settings for those
102
- times: this.props.times,
103
- functions: this.props.functions,
104
- buttonSets: this.props.buttonSets,
105
-
106
- buttonsVisible: "focused",
107
- form: ans.form,
108
- simplify: ans.simplify,
109
- value: ans.value,
110
-
111
- onChange: (props) => this.updateForm(key, props),
112
- trackInteraction: () => {},
113
-
114
- widgetId: this.props.widgetId + "-" + ans.key,
115
- } as const;
116
-
117
- return lens(ans)
118
- .merge([], {
119
- draggable: true,
120
- onChange: (props) =>
121
- this.updateForm(
122
- Number.parseInt(ans.key ?? ""),
123
- props,
124
- ),
125
- onDelete: () => this.handleRemoveForm(key),
126
- expressionProps: expressionProps,
127
- })
128
- .freeze();
129
- })
130
- .map((obj) => <AnswerOption key={obj.key} {...obj} />);
131
-
132
- const sortable = (
133
- <SortableArea
134
- components={answerOptions}
135
- onReorder={this.handleReorder}
136
- className="answer-options-list"
137
- />
138
- );
139
-
140
- // checkboxes to choose which sets of input buttons are shown
141
- const buttonSetChoices = buttonSetsList.map((name) => {
142
- // The first one gets special cased to always be checked, disabled,
143
- // and float left.
144
- const isFirst = name === "basic";
145
- const checked =
146
- this.props.buttonSets.includes(
147
- name as PerseusExpressionWidgetOptions["buttonSets"][number],
148
- ) || isFirst;
149
- return (
150
- <label className="button-set-label" key={name}>
151
- <input
152
- type="checkbox"
153
- checked={checked}
154
- disabled={isFirst}
155
- onChange={() => this.handleButtonSet(name)}
156
- />
157
- {name}
158
- </label>
159
- );
160
- });
161
-
162
- const {TeX} = Dependencies.getDependencies(); // OldExpression only
163
-
164
- buttonSetChoices.unshift(
165
- <label className="button-set-label" key="show-div">
166
- <input type="checkbox" onChange={this.handleToggleDiv} />
167
- <span className="show-div-button">
168
- show <TeX>\div</TeX> button
169
- </span>
170
- </label>,
171
- );
172
-
173
- return (
174
- <div className="perseus-widget-expression-editor">
175
- <h3 className="expression-editor-h3">Global Options</h3>
176
-
177
- <div>
178
- <PropCheckBox
179
- times={this.props.times}
180
- onChange={this.props.onChange}
181
- labelAlignment="right"
182
- label="Use × for rendering multiplication instead of a
183
- center dot."
184
- />
185
- <InfoTip>
186
- <p>
187
- For pre-algebra problems this option displays
188
- multiplication as \times instead of \cdot in both
189
- the rendered output and the acceptable formats
190
- examples.
191
- </p>
192
- </InfoTip>
193
- </div>
194
-
195
- <div>
196
- <label>
197
- {"Function variables: "}
198
- <input
199
- type="text"
200
- defaultValue={this.props.functions.join(" ")}
201
- onChange={this.handleFunctions}
202
- />
203
- </label>
204
- <InfoTip>
205
- <p>
206
- Single-letter variables listed here will be
207
- interpreted as functions. This let us know that f(x)
208
- means "f of x" and not "f times x".
209
- </p>
210
- </InfoTip>
211
- </div>
212
-
213
- <div>
214
- <div>Button sets:</div>
215
- {buttonSetChoices}
216
- </div>
217
-
218
- <h3 className="expression-editor-h3">Answers</h3>
219
-
220
- <p style={{margin: "4px 0"}}>
221
- student responses area matched against these from top to
222
- bottom
223
- </p>
224
-
225
- {sortable}
226
-
227
- <div>
228
- <button
229
- className="simple-button orange"
230
- style={{fontSize: 13}}
231
- onClick={this.newAnswer}
232
- type="button"
233
- >
234
- Add new answer
235
- </button>
236
- </div>
237
- </div>
238
- );
239
- }
240
-
241
- serialize: () => any = () => {
242
- const formSerializables = [
243
- "value",
244
- "form",
245
- "simplify",
246
- "considered",
247
- // it's a little weird to serialize the react key, but saves some
248
- // effort reconstructing them when this item is loaded later.
249
- "key",
250
- ];
251
- const serializables = [
252
- "answerForms",
253
- "buttonSets",
254
- "functions",
255
- "times",
256
- ];
257
-
258
- const answerForms = this.props.answerForms.map((form) => {
259
- return _(form).pick(formSerializables);
260
- });
261
-
262
- return lens(this.props)
263
- .set(["answerForms"], answerForms)
264
- .mod([], (props) => _(props).pick(serializables))
265
- .freeze();
266
- };
267
-
268
- getSaveWarnings: () => any = () => {
269
- const issues: Array<any | string> = [];
270
-
271
- if (this.props.answerForms.length === 0) {
272
- issues.push("No answers specified");
273
- } else {
274
- const hasCorrect = this.props.answerForms.some((form) => {
275
- return form.considered === "correct";
276
- });
277
- if (!hasCorrect) {
278
- issues.push("No correct answer specified");
279
- }
280
-
281
- _(this.props.answerForms).each((form, ix) => {
282
- if (this.props.value === "") {
283
- issues.push(`Answer ${ix + 1} is empty`);
284
- } else {
285
- // note we're not using icu for content creators
286
- const expression = KAS.parse(form.value, {
287
- functions: this.props.functions,
288
- });
289
- if (!expression.parsed) {
290
- issues.push(`Couldn't parse ${form.value}`);
291
- } else if (
292
- form.simplify &&
293
- !expression.expr.isSimplified()
294
- ) {
295
- issues.push(
296
- `${form.value} isn't simplified, but is required" +
297
- " to be`,
298
- );
299
- }
300
- }
301
- });
302
-
303
- // TODO(joel) - warn about:
304
- // - unreachable answers (how??)
305
- // - specific answers following unspecific answers
306
- // - incorrect answers as the final form
307
- }
308
-
309
- return issues;
310
- };
311
-
312
- _newEmptyAnswerForm: () => any = () => {
313
- return {
314
- considered: "correct",
315
- form: false,
316
-
317
- // note: the key means "n-th form created" - not "form in
318
- // position n" and will stay the same for the life of this form
319
- key: _makeNewKey(this.props.answerForms),
320
-
321
- simplify: false,
322
- value: "",
323
- };
324
- };
325
-
326
- newAnswer: () => void = () => {
327
- const answerForms = this.props.answerForms.slice();
328
- answerForms.push(this._newEmptyAnswerForm());
329
- this.change({answerForms});
330
- };
331
-
332
- handleRemoveForm: (answerKey: number) => void = (i) => {
333
- const answerForms = this.props.answerForms.slice();
334
- answerForms.splice(i, 1);
335
- this.change({answerForms});
336
- };
337
-
338
- // called when the options (including the expression itself) to an answer
339
- // form change
340
- updateForm: (i: number, props: any) => void = (i, props) => {
341
- const answerForms = lens(this.props.answerForms)
342
- .merge([i], props)
343
- .freeze();
344
-
345
- this.change({answerForms});
346
- };
347
-
348
- handleReorder: (components: any) => void = (components) => {
349
- const answerForms = components.map((component) => {
350
- const form = _(component.props).pick(
351
- "considered",
352
- "form",
353
- "simplify",
354
- "value",
355
- );
356
- // @ts-expect-error - TS2339 - Property 'key' does not exist on type 'Pick<any, "form" | "value" | "simplify" | "considered">'.
357
- form.key = component.key;
358
- return form;
359
- });
360
-
361
- this.change({answerForms});
362
- };
363
-
364
- // called when the selected buttonset changes
365
- handleButtonSet: (changingName: string) => void = (changingName) => {
366
- const buttonSetNames = buttonSetsList;
367
-
368
- // Filter to preserve order - using .union and .difference would always
369
- // move the last added button set to the end.
370
- const buttonSets = buttonSetNames.filter((set) => {
371
- return (
372
- this.props.buttonSets.includes(set) !== (set === changingName)
373
- );
374
- });
375
-
376
- this.props.onChange({buttonSets});
377
- };
378
-
379
- handleToggleDiv: () => void = () => {
380
- // We always want buttonSets to contain exactly one of "basic" and
381
- // "basic+div". Toggle between the two of them.
382
- // If someone can think of a more elegant formulation of this (there
383
- // must be one!) feel free to change it.
384
- let keep: LegacyButtonSet | undefined;
385
- let remove: LegacyButtonSet | undefined;
386
- if (this.props.buttonSets.includes("basic+div")) {
387
- keep = "basic";
388
- remove = "basic+div";
389
- } else {
390
- keep = "basic+div";
391
- remove = "basic";
392
- }
393
-
394
- const buttonSets = this.props.buttonSets
395
- .filter((set) => set !== remove)
396
- .concat(keep);
397
-
398
- this.change("buttonSets", buttonSets);
399
- };
400
-
401
- // called when the correct answer changes
402
- handleTexInsert: (arg1: string) => void = (str) => {
403
- // eslint-disable-next-line react/no-string-refs
404
- // @ts-expect-error - TS2339 - Property 'insert' does not exist on type 'ReactInstance'.
405
- this.refs.expression.insert(str);
406
- };
407
-
408
- // called when the function variables change
409
- handleFunctions: (arg1: React.ChangeEvent<HTMLInputElement>) => void = (
410
- e,
411
- ) => {
412
- const newProps: Record<string, any> = {};
413
- newProps.functions = e.target.value.split(/[ ,]+/).filter(isTruthy);
414
- this.props.onChange(newProps);
415
- };
416
- }
417
-
418
- // Find the next element in arr after val, wrapping around to the first.
419
- const findNextIn = function (arr: ReadonlyArray<string>, val: any) {
420
- let ix = arr.indexOf(val);
421
- ix = (ix + 1) % arr.length;
422
- return arr[ix];
423
- };
424
-
425
- type AnswerOptionProps = {
426
- considered: typeof PerseusExpressionAnswerFormConsidered[number];
427
- expressionProps: any;
428
-
429
- // Must the answer have the same form as this answer.
430
- form: boolean;
431
-
432
- // Must the answer be simplified.
433
- simplify: boolean;
434
-
435
- onDelete: () => void;
436
- } & Changeable.ChangeableProps;
437
-
438
- type AnswerOptionState = {
439
- deleteFocused: boolean;
440
- };
441
-
442
- class AnswerOption extends React.Component<
443
- AnswerOptionProps,
444
- AnswerOptionState
445
- > {
446
- state = {deleteFocused: false};
447
-
448
- handleDeleteBlur = () => {
449
- this.setState({deleteFocused: false});
450
- };
451
-
452
- change = (...args) => {
453
- return Changeable.change.apply(this, args);
454
- };
455
-
456
- render(): React.ReactNode {
457
- let removeButton: React.ReactNode | null = null;
458
- if (this.state.deleteFocused) {
459
- removeButton = (
460
- <button
461
- type="button"
462
- className="simple-button orange"
463
- onClick={this.handleImSure}
464
- onBlur={this.handleDeleteBlur}
465
- >
466
- I'm sure!
467
- </button>
468
- );
469
- } else {
470
- removeButton = (
471
- <button
472
- type="button"
473
- className="simple-button orange"
474
- onClick={this.handleDelete}
475
- >
476
- Delete
477
- </button>
478
- );
479
- }
480
-
481
- return (
482
- <div className="expression-answer-option">
483
- <div className="answer-handle" />
484
-
485
- <div className="answer-body">
486
- <div className="answer-considered">
487
- <div
488
- onClick={this.toggleConsidered}
489
- className={"answer-status " + this.props.considered}
490
- >
491
- {this.props.considered}
492
- </div>
493
-
494
- <div className="answer-expression">
495
- <Expression {...this.props.expressionProps} />
496
- </div>
497
- </div>
498
-
499
- <div className="answer-option">
500
- <PropCheckBox
501
- form={this.props.form}
502
- onChange={this.props.onChange}
503
- labelAlignment="right"
504
- label="Answer expression must have the same form."
505
- />
506
- <InfoTip>
507
- <p>
508
- The student's answer must be in the same form.
509
- Commutativity and excess negative signs are
510
- ignored.
511
- </p>
512
- </InfoTip>
513
- </div>
514
-
515
- <div className="answer-option">
516
- <PropCheckBox
517
- simplify={this.props.simplify}
518
- onChange={this.props.onChange}
519
- labelAlignment="right"
520
- label="Answer expression must be fully expanded and
521
- simplified."
522
- />
523
- <InfoTip>
524
- <p>
525
- The student's answer must be fully expanded and
526
- simplified. Answering this equation (x^2+2x+1)
527
- with this factored equation (x+1)^2 will render
528
- this response "Your answer is not fully expanded
529
- and simplified."
530
- </p>
531
- </InfoTip>
532
- </div>
533
-
534
- <div className="remove-container">{removeButton}</div>
535
- </div>
536
- </div>
537
- );
538
- }
539
-
540
- handleImSure = () => {
541
- this.props.onDelete();
542
- };
543
-
544
- handleDelete = () => {
545
- this.setState({deleteFocused: true});
546
- };
547
-
548
- toggleConsidered = () => {
549
- const newVal = findNextIn(
550
- PerseusExpressionAnswerFormConsidered,
551
- this.props.considered,
552
- );
553
- this.change({considered: newVal});
554
- };
555
- }
556
-
557
- export default ExpressionEditor;