@khanacademy/perseus 21.5.0 → 22.0.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/article-renderer.d.ts +6 -0
- package/dist/components/highlighting/ui/highlight-set-renderer.d.ts +14 -0
- package/dist/components/highlighting/ui/highlighting-ui.d.ts +17 -0
- package/dist/components/i18n-context.d.ts +16 -0
- package/dist/components/input-with-examples.d.ts +6 -0
- package/dist/components/math-input.d.ts +22 -2
- package/dist/components/number-input.d.ts +6 -0
- package/dist/es/index.css +3 -0
- package/dist/es/index.js +953 -530
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +312 -0
- package/dist/es/strings.js.map +1 -0
- package/dist/hint-renderer.d.ts +6 -0
- package/dist/hints-renderer.d.ts +6 -0
- package/dist/index.css +3 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1175 -565
- package/dist/index.js.map +1 -1
- package/dist/multi-items/multi-renderer.d.ts +6 -0
- package/dist/perseus-api.d.ts +5 -3
- package/dist/perseus-types.d.ts +18 -8
- package/dist/renderer.d.ts +2 -0
- package/dist/server-item-renderer.d.ts +11 -3
- package/dist/strings.d.ts +190 -0
- package/dist/strings.js +395 -119
- package/dist/strings.js.map +1 -0
- package/dist/types.d.ts +10 -6
- package/dist/util/answer-types.d.ts +4 -12
- package/dist/util.d.ts +2 -1
- package/dist/widgets/categorizer.d.ts +8 -1
- package/dist/widgets/definition.d.ts +6 -0
- package/dist/widgets/deprecated-standin/index.d.ts +6 -0
- package/dist/widgets/explanation.d.ts +6 -0
- package/dist/widgets/expression.d.ts +42 -19
- package/dist/widgets/graded-group-answer-bar.d.ts +6 -0
- package/dist/widgets/graded-group.d.ts +6 -0
- package/dist/widgets/group.d.ts +6 -0
- package/dist/widgets/image.d.ts +6 -0
- package/dist/widgets/input-number.d.ts +8 -1
- package/dist/widgets/interactive-graph.d.ts +6 -0
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +21 -0
- package/dist/widgets/interactive-graphs/locked-line.d.ts +4 -0
- package/dist/widgets/interactive-graphs/locked-point.d.ts +4 -0
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +1 -0
- package/dist/widgets/interactive-graphs/reducer/use-graph-config.d.ts +1 -0
- package/dist/widgets/label-image/marker.d.ts +6 -0
- package/dist/widgets/label-image.d.ts +16 -6
- package/dist/widgets/matcher.d.ts +6 -0
- package/dist/widgets/matrix.d.ts +9 -1
- package/dist/widgets/molecule.d.ts +6 -0
- package/dist/widgets/number-line.d.ts +6 -0
- package/dist/widgets/numeric-input.d.ts +8 -1
- package/dist/widgets/passage-ref-target.d.ts +6 -0
- package/dist/widgets/passage-ref.d.ts +6 -0
- package/dist/widgets/passage.d.ts +6 -0
- package/dist/widgets/plotter.d.ts +6 -0
- package/dist/widgets/radio/choice-a11y-text.d.ts +2 -1
- package/dist/widgets/radio/radio.d.ts +8 -1
- package/dist/widgets/radio/util.d.ts +2 -1
- package/dist/widgets/table.d.ts +7 -0
- package/dist/widgets/unit.d.ts +8 -1
- package/dist/widgets/video.d.ts +6 -0
- package/dist/widgets.d.ts +2 -1
- package/package.json +50 -39
- package/dist/widgets/label-image/strings.d.ts +0 -3
package/dist/es/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import * as KAS from '@khanacademy/kas';
|
|
2
|
-
import {
|
|
2
|
+
import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
|
|
3
3
|
import * as PerseusLinter from '@khanacademy/perseus-linter';
|
|
4
4
|
import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
|
|
5
5
|
import { View, RenderStateRoot, useUniqueIdWithMock, UniqueIDProvider, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
|
|
6
|
-
import * as i18n from '@khanacademy/wonder-blocks-i18n';
|
|
7
6
|
import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
|
|
8
7
|
import classNames from 'classnames';
|
|
9
8
|
import * as React from 'react';
|
|
10
|
-
import React__default, { useState, useEffect, useRef, createContext, useImperativeHandle } from 'react';
|
|
9
|
+
import React__default, { useContext, useState, useEffect, useRef, createContext, useImperativeHandle } from 'react';
|
|
11
10
|
import _, { debounce as debounce$1 } from 'underscore';
|
|
12
11
|
import Clickable from '@khanacademy/wonder-blocks-clickable';
|
|
13
12
|
import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder-blocks-popover';
|
|
@@ -30,7 +29,7 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
|
|
|
30
29
|
import caretDown from '@phosphor-icons/core/assets/regular/caret-down.svg';
|
|
31
30
|
import caretUp from '@phosphor-icons/core/assets/regular/caret-up.svg';
|
|
32
31
|
import debounce$2 from 'lodash.debounce';
|
|
33
|
-
import { Point as Point$2, useTransformContext, vec, useMovable, Text as Text$1, Polygon, Coordinates, Mafs } from 'mafs';
|
|
32
|
+
import { Line as Line$3, Point as Point$2, useTransformContext, vec, useMovable, Text as Text$1, Polygon, Coordinates, Mafs } from 'mafs';
|
|
34
33
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
35
34
|
import questionIcon from '@phosphor-icons/core/regular/question.svg';
|
|
36
35
|
import { LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
@@ -45,14 +44,12 @@ function _extends() {
|
|
|
45
44
|
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
46
45
|
for (var i = 1; i < arguments.length; i++) {
|
|
47
46
|
var source = arguments[i];
|
|
48
|
-
|
|
49
47
|
for (var key in source) {
|
|
50
48
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
51
49
|
target[key] = source[key];
|
|
52
50
|
}
|
|
53
51
|
}
|
|
54
52
|
}
|
|
55
|
-
|
|
56
53
|
return target;
|
|
57
54
|
};
|
|
58
55
|
return _extends.apply(this, arguments);
|
|
@@ -63,16 +60,219 @@ function _objectWithoutPropertiesLoose(source, excluded) {
|
|
|
63
60
|
var target = {};
|
|
64
61
|
var sourceKeys = Object.keys(source);
|
|
65
62
|
var key, i;
|
|
66
|
-
|
|
67
63
|
for (i = 0; i < sourceKeys.length; i++) {
|
|
68
64
|
key = sourceKeys[i];
|
|
69
65
|
if (excluded.indexOf(key) >= 0) continue;
|
|
70
66
|
target[key] = source[key];
|
|
71
67
|
}
|
|
72
|
-
|
|
73
68
|
return target;
|
|
74
69
|
}
|
|
75
70
|
|
|
71
|
+
/**
|
|
72
|
+
* The translated strings that are used to render Perseus.
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Mock strings for the Perseus package, to be used for tests and Storybook.
|
|
77
|
+
*/
|
|
78
|
+
const mockStrings = {
|
|
79
|
+
closeKeypad: "close math keypad",
|
|
80
|
+
openKeypad: "open math keypad",
|
|
81
|
+
removeHighlight: "Remove highlight",
|
|
82
|
+
addHighlight: "Add highlight",
|
|
83
|
+
hintPos: ({
|
|
84
|
+
pos
|
|
85
|
+
}) => `Hint #${pos}`,
|
|
86
|
+
errorRendering: ({
|
|
87
|
+
error
|
|
88
|
+
}) => `Error rendering: ${error}`,
|
|
89
|
+
APPROXIMATED_PI_ERROR: "Your answer is close, but you may " + "have approximated pi. Enter your " + "answer as a multiple of pi, like " + "<code>12\\ \\text{pi}</code> or " + "<code>2/3\\ \\text{pi}</code>",
|
|
90
|
+
EXTRA_SYMBOLS_ERROR: "We could not understand your " + "answer. Please check your answer for extra " + "text or symbols.",
|
|
91
|
+
NEEDS_TO_BE_SIMPLFIED_ERROR: "Your answer is almost correct, " + "but it needs to be simplified.",
|
|
92
|
+
MISSING_PERCENT_ERROR: "Your answer is almost correct, " + "but it is missing a " + "<code>\\%</code> at the end.",
|
|
93
|
+
MULTIPLICATION_SIGN_ERROR: "I'm a computer. I only understand " + "multiplication if you use an asterisk " + "(*) as the multiplication sign.",
|
|
94
|
+
WRONG_CASE_ERROR: "Your answer includes use of a variable with the wrong case.",
|
|
95
|
+
WRONG_LETTER_ERROR: "Your answer includes a wrong variable letter.",
|
|
96
|
+
invalidSelection: "Make sure you select something for every row.",
|
|
97
|
+
ERROR_TITLE: "Oops!",
|
|
98
|
+
ERROR_MESSAGE: "Sorry, I don't understand that!",
|
|
99
|
+
hints: "Hints",
|
|
100
|
+
getAnotherHint: "Get another hint",
|
|
101
|
+
deprecatedStandin: "Sorry, this part of the question is no longer available. 😅 Don't worry, you won't be graded on this part. Keep going!",
|
|
102
|
+
keepTrying: "Keep trying",
|
|
103
|
+
tryAgain: "Try again",
|
|
104
|
+
check: "Check",
|
|
105
|
+
correctExcited: "Correct!",
|
|
106
|
+
nextQuestion: "Next question",
|
|
107
|
+
skipToTitle: ({
|
|
108
|
+
title
|
|
109
|
+
}) => `Skip to ${title}`,
|
|
110
|
+
current: "Current",
|
|
111
|
+
correct: "Correct",
|
|
112
|
+
correctSelected: "Correct (selected)",
|
|
113
|
+
correctCrossedOut: "Correct (but you crossed it out)",
|
|
114
|
+
incorrect: "Incorrect",
|
|
115
|
+
incorrectSelected: "Incorrect (selected)",
|
|
116
|
+
hideExplanation: "Hide explanation",
|
|
117
|
+
explain: "Explain",
|
|
118
|
+
INVALID_MESSAGE_PREFIX: "We couldn't grade your answer.",
|
|
119
|
+
DEFAULT_INVALID_MESSAGE_1: "It looks like you left something blank or ",
|
|
120
|
+
DEFAULT_INVALID_MESSAGE_2: "entered in an invalid answer.",
|
|
121
|
+
integerExample: "an integer, like $6$",
|
|
122
|
+
properExample: "a *proper* fraction, like $1/2$ or $6/10$",
|
|
123
|
+
simplifiedProperExample: "a *simplified proper* fraction, like $3/5$",
|
|
124
|
+
improperExample: "an *improper* fraction, like $10/7$ or $14/8$",
|
|
125
|
+
simplifiedImproperExample: "a *simplified improper* fraction, like $7/4$",
|
|
126
|
+
mixedExample: "a mixed number, like $1\\ 3/4$",
|
|
127
|
+
decimalExample: "an *exact* decimal, like $0.75$",
|
|
128
|
+
percentExample: "a percent, like $12.34\\%$",
|
|
129
|
+
piExample: "a multiple of pi, like $12\\ \\text{pi}$ or $2/3\\ \\text{pi}$",
|
|
130
|
+
yourAnswer: "**Your answer should be** ",
|
|
131
|
+
yourAnswerLabel: "Your answer:",
|
|
132
|
+
addPoints: "Click to add points",
|
|
133
|
+
addVertices: "Click to add vertices",
|
|
134
|
+
tapMultiple: "Tap each dot on the image to select all answers that apply.",
|
|
135
|
+
tapSingle: "Tap each dot on the image to select an answer.",
|
|
136
|
+
clickMultiple: "Click each dot on the image to select all answers that apply.",
|
|
137
|
+
clickSingle: "Click each dot on the image to select an answer.",
|
|
138
|
+
choices: "Choices:",
|
|
139
|
+
answers: ({
|
|
140
|
+
num
|
|
141
|
+
}) => num === 1 ? `${num} answer` : `${num} answers`,
|
|
142
|
+
hideAnswersToggleLabel: "Hide answer choices",
|
|
143
|
+
moves: ({
|
|
144
|
+
num
|
|
145
|
+
}) => num === 1 ? `Moves: ${num}` : `Moves: ${num}`,
|
|
146
|
+
clickTiles: "Click on the tiles to change the lights.",
|
|
147
|
+
turnOffLights: "You must turn on all of the lights to continue.",
|
|
148
|
+
fillAllCells: "Make sure you fill in all cells in the matrix.",
|
|
149
|
+
molecularDrawing: ({
|
|
150
|
+
content
|
|
151
|
+
}) => `A molecular structure drawing. SMILES notation: ${content}`,
|
|
152
|
+
switchDirection: "Switch direction",
|
|
153
|
+
circleOpen: "Make circle open",
|
|
154
|
+
circleFilled: "Make circle filled",
|
|
155
|
+
numDivisions: "Number of divisions:",
|
|
156
|
+
divisions: ({
|
|
157
|
+
divRangeString
|
|
158
|
+
}) => `Please make sure the number of divisions is in the range ${divRangeString}.`,
|
|
159
|
+
lineRange: ({
|
|
160
|
+
lineRange
|
|
161
|
+
}) => `lines ${lineRange}`,
|
|
162
|
+
lineNumber: ({
|
|
163
|
+
lineNumber
|
|
164
|
+
}) => `line ${lineNumber}`,
|
|
165
|
+
symbolPassage: ({
|
|
166
|
+
questionSymbol,
|
|
167
|
+
questionNumber
|
|
168
|
+
}) => `The symbol ${questionSymbol} indicates that question ${questionNumber} references this portion of the passage.`,
|
|
169
|
+
symbolQuestion: ({
|
|
170
|
+
sentenceSymbol
|
|
171
|
+
}) => ` The symbol ${sentenceSymbol} indicates that the following sentence is referenced in a question.`,
|
|
172
|
+
lineLabel: "Line",
|
|
173
|
+
beginningPassage: "Beginning of reading passage.",
|
|
174
|
+
beginningFootnotes: "Beginning of reading passage footnotes.",
|
|
175
|
+
endPassage: "End of reading passage.",
|
|
176
|
+
questionMarker: ({
|
|
177
|
+
number
|
|
178
|
+
}) => `[Marker for question ${number}]`,
|
|
179
|
+
circleMarker: ({
|
|
180
|
+
number
|
|
181
|
+
}) => `[Circle marker ${number}]`,
|
|
182
|
+
sentenceMarker: ({
|
|
183
|
+
number
|
|
184
|
+
}) => `[Sentence ${number}]`,
|
|
185
|
+
dragHandles: "Drag handles to make graph",
|
|
186
|
+
tapAddPoints: "Tap to add points",
|
|
187
|
+
false: "False",
|
|
188
|
+
true: "True",
|
|
189
|
+
no: "No",
|
|
190
|
+
yes: "Yes",
|
|
191
|
+
chooseCorrectNum: "Please choose the correct number of answers.",
|
|
192
|
+
notNoneOfTheAbove: "'None of the above' may not be selected when other answers are selected.",
|
|
193
|
+
noneOfTheAbove: "None of the above",
|
|
194
|
+
chooseNumAnswers: ({
|
|
195
|
+
numCorrect
|
|
196
|
+
}) => `Choose ${numCorrect} answers:`,
|
|
197
|
+
chooseAllAnswers: "Choose all answers that apply:",
|
|
198
|
+
chooseOneAnswer: "Choose 1 answer:",
|
|
199
|
+
choiceCheckedCorrect: ({
|
|
200
|
+
letter
|
|
201
|
+
}) => `(Choice ${letter}, Checked, Correct)`,
|
|
202
|
+
choiceCrossedOutCorrect: ({
|
|
203
|
+
letter
|
|
204
|
+
}) => `(Choice ${letter}, Crossed out, Correct)`,
|
|
205
|
+
choiceCorrect: ({
|
|
206
|
+
letter
|
|
207
|
+
}) => `(Choice ${letter}, Correct)`,
|
|
208
|
+
choiceCheckedIncorrect: ({
|
|
209
|
+
letter
|
|
210
|
+
}) => `(Choice ${letter}, Checked, Incorrect)`,
|
|
211
|
+
choiceCrossedOutIncorrect: ({
|
|
212
|
+
letter
|
|
213
|
+
}) => `(Choice ${letter}, Crossed out, Incorrect)`,
|
|
214
|
+
choiceIncorrect: ({
|
|
215
|
+
letter
|
|
216
|
+
}) => `(Choice ${letter}, Incorrect)`,
|
|
217
|
+
choiceChecked: ({
|
|
218
|
+
letter
|
|
219
|
+
}) => `(Choice ${letter}, Checked)`,
|
|
220
|
+
choiceCrossedOut: ({
|
|
221
|
+
letter
|
|
222
|
+
}) => `(Choice ${letter}, Crossed out)`,
|
|
223
|
+
choice: ({
|
|
224
|
+
letter
|
|
225
|
+
}) => `(Choice ${letter})`,
|
|
226
|
+
crossOut: "Cross out",
|
|
227
|
+
crossOutOption: "Cross out option",
|
|
228
|
+
crossOutChoice: ({
|
|
229
|
+
letter
|
|
230
|
+
}) => `Cross out Choice ${letter}`,
|
|
231
|
+
bringBack: "Bring back",
|
|
232
|
+
openMenuForChoice: ({
|
|
233
|
+
letter
|
|
234
|
+
}) => `Open menu for Choice ${letter}`,
|
|
235
|
+
letters: "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z",
|
|
236
|
+
rightArrow: "Reaction arrow pointing to the right.",
|
|
237
|
+
dontUnderstandUnits: "I couldn't understand those units.",
|
|
238
|
+
checkSigFigs: "Check your significant figures.",
|
|
239
|
+
answerNumericallyIncorrect: "That answer is numerically incorrect.",
|
|
240
|
+
checkUnits: "Check your units.",
|
|
241
|
+
dontUnderstand: "I don't understand that",
|
|
242
|
+
loading: "Loading...",
|
|
243
|
+
videoTranscript: "See video transcript",
|
|
244
|
+
somethingWrong: "Something went wrong.",
|
|
245
|
+
videoWrapper: "Khan Academy video wrapper"
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* PerseusI18nContext provides a way to set the strings and locale that are
|
|
250
|
+
* used inside the Perseus package.
|
|
251
|
+
*
|
|
252
|
+
*/
|
|
253
|
+
// @ts-expect-error - TS2322 - Type 'Context<{ strings: {}; locale: string; }>' is not assignable to type 'Context<I18nContextType>'.
|
|
254
|
+
const PerseusI18nContext = /*#__PURE__*/React.createContext(process.env.NODE_ENV === "test" || process.env.STORYBOOK ? {
|
|
255
|
+
strings: mockStrings,
|
|
256
|
+
locale: "en"
|
|
257
|
+
} :
|
|
258
|
+
// We want to return null here, not an empty object, so that we
|
|
259
|
+
// are will throw an error when attempting to access the
|
|
260
|
+
// undefined locale or strings, making it easier to debug.
|
|
261
|
+
null);
|
|
262
|
+
function PerseusI18nContextProvider({
|
|
263
|
+
children,
|
|
264
|
+
strings,
|
|
265
|
+
locale
|
|
266
|
+
}) {
|
|
267
|
+
return /*#__PURE__*/React.createElement(PerseusI18nContext.Provider, {
|
|
268
|
+
value: {
|
|
269
|
+
strings,
|
|
270
|
+
locale
|
|
271
|
+
}
|
|
272
|
+
}, children);
|
|
273
|
+
}
|
|
274
|
+
const usePerseusI18n = () => useContext(PerseusI18nContext);
|
|
275
|
+
|
|
76
276
|
const debounce = (func, delay) => {
|
|
77
277
|
let timer = null;
|
|
78
278
|
return (...args) => {
|
|
@@ -85,21 +285,8 @@ const debounce = (func, delay) => {
|
|
|
85
285
|
};
|
|
86
286
|
};
|
|
87
287
|
|
|
88
|
-
const customKeyTranslator = _extends({}, keyTranslator, {
|
|
89
|
-
// If there's something in the input that can become part of a
|
|
90
|
-
// fraction, typing "/" puts it in the numerator. If not, typing
|
|
91
|
-
// "/" does nothing. In that case, enter a \frac.
|
|
92
|
-
FRAC: mathQuill => {
|
|
93
|
-
const contents = mathQuill.latex();
|
|
94
|
-
mathQuill.typedText("/");
|
|
95
|
-
if (mathQuill.latex() === contents) {
|
|
96
|
-
mathQuill.cmd("\\frac");
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
288
|
// A WYSIWYG math input that calls `onChange(LaTeX-string)`
|
|
102
|
-
class
|
|
289
|
+
class InnerMathInput extends React.Component {
|
|
103
290
|
constructor(...args) {
|
|
104
291
|
super(...args);
|
|
105
292
|
// @ts-expect-error - TS2564 - Property 'mouseDown' has no initializer and is not definitely assigned in the constructor.
|
|
@@ -113,6 +300,21 @@ class MathInput extends React.Component {
|
|
|
113
300
|
};
|
|
114
301
|
this.insert = value => {
|
|
115
302
|
const input = this.mathField();
|
|
303
|
+
const {
|
|
304
|
+
locale
|
|
305
|
+
} = this.context;
|
|
306
|
+
const customKeyTranslator = _extends({}, getKeyTranslator(locale), {
|
|
307
|
+
// If there's something in the input that can become part of a
|
|
308
|
+
// fraction, typing "/" puts it in the numerator. If not, typing
|
|
309
|
+
// "/" does nothing. In that case, enter a \frac.
|
|
310
|
+
FRAC: mathQuill => {
|
|
311
|
+
const contents = mathQuill.latex();
|
|
312
|
+
mathQuill.typedText("/");
|
|
313
|
+
if (mathQuill.latex() === contents) {
|
|
314
|
+
mathQuill.cmd("\\frac");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
});
|
|
116
318
|
const inputModifier = customKeyTranslator[value];
|
|
117
319
|
if (inputModifier) {
|
|
118
320
|
inputModifier(input, value);
|
|
@@ -135,8 +337,11 @@ class MathInput extends React.Component {
|
|
|
135
337
|
};
|
|
136
338
|
this.mathField = () => {
|
|
137
339
|
if (!this.__mathField && this.__mathFieldWrapperRef) {
|
|
340
|
+
const {
|
|
341
|
+
locale
|
|
342
|
+
} = this.context;
|
|
138
343
|
// Initialize MathQuill.MathField instance
|
|
139
|
-
this.__mathField = createMathField(this.__mathFieldWrapperRef, baseConfig => _extends({}, baseConfig, {
|
|
344
|
+
this.__mathField = createMathField(this.__mathFieldWrapperRef, this.props.mathInputStrings, baseConfig => _extends({}, baseConfig, {
|
|
140
345
|
handlers: {
|
|
141
346
|
edit: debounce(mathField => {
|
|
142
347
|
// This handler is guaranteed to be called on change, but
|
|
@@ -152,7 +357,7 @@ class MathInput extends React.Component {
|
|
|
152
357
|
// Use the specified symbol to represent multiplication
|
|
153
358
|
// TODO(alex): Add an option to disallow variables, in
|
|
154
359
|
// which case 'x' should get converted to '\\times'
|
|
155
|
-
if (convertDotToTimesByLocale(this.props.convertDotToTimes)) {
|
|
360
|
+
if (convertDotToTimesByLocale(locale, this.props.convertDotToTimes)) {
|
|
156
361
|
value = value.replace(/\\cdot/g, "\\times");
|
|
157
362
|
|
|
158
363
|
// Preserve cursor position in the common case:
|
|
@@ -212,7 +417,10 @@ class MathInput extends React.Component {
|
|
|
212
417
|
focused: false
|
|
213
418
|
});
|
|
214
419
|
this.handleKeypadPress = (key, e) => {
|
|
215
|
-
const
|
|
420
|
+
const {
|
|
421
|
+
locale
|
|
422
|
+
} = this.context;
|
|
423
|
+
const translator = getKeyTranslator(locale)[key];
|
|
216
424
|
const mathField = this.mathField();
|
|
217
425
|
if (mathField) {
|
|
218
426
|
if (translator) {
|
|
@@ -311,7 +519,7 @@ class MathInput extends React.Component {
|
|
|
311
519
|
focused: false,
|
|
312
520
|
active: false
|
|
313
521
|
}) : /*#__PURE__*/React.createElement(Clickable, {
|
|
314
|
-
"aria-label": this.state.keypadOpen ?
|
|
522
|
+
"aria-label": this.state.keypadOpen ? this.context.strings.closeKeypad : this.context.strings.openKeypad,
|
|
315
523
|
"aria-checked": this.state.keypadOpen,
|
|
316
524
|
role: "switch",
|
|
317
525
|
onClick: () => this.state.keypadOpen ? this.closeKeypad() : this.openKeypad()
|
|
@@ -320,10 +528,36 @@ class MathInput extends React.Component {
|
|
|
320
528
|
}, props))))));
|
|
321
529
|
}
|
|
322
530
|
}
|
|
323
|
-
|
|
531
|
+
|
|
532
|
+
// We need to have two contexts (one for Perseus and one for MathInput), so we
|
|
533
|
+
// add a wrapper around the MathInput component to provide the MathInput context
|
|
534
|
+
// to it.
|
|
535
|
+
InnerMathInput.contextType = PerseusI18nContext;
|
|
536
|
+
InnerMathInput.defaultProps = {
|
|
324
537
|
value: "",
|
|
325
538
|
convertDotToTimes: false
|
|
326
539
|
};
|
|
540
|
+
class MathInput extends React.Component {
|
|
541
|
+
constructor(...args) {
|
|
542
|
+
super(...args);
|
|
543
|
+
this.inputRef = /*#__PURE__*/React.createRef();
|
|
544
|
+
}
|
|
545
|
+
focus() {
|
|
546
|
+
var _this$inputRef$curren;
|
|
547
|
+
(_this$inputRef$curren = this.inputRef.current) == null || _this$inputRef$curren.focus();
|
|
548
|
+
}
|
|
549
|
+
insert(value) {
|
|
550
|
+
var _this$inputRef$curren2;
|
|
551
|
+
(_this$inputRef$curren2 = this.inputRef.current) == null || _this$inputRef$curren2.insert(value);
|
|
552
|
+
}
|
|
553
|
+
render() {
|
|
554
|
+
return /*#__PURE__*/React.createElement(InnerMathInput, _extends({}, this.props, {
|
|
555
|
+
ref: this.inputRef,
|
|
556
|
+
mathInputStrings: this.context.strings
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
MathInput.contextType = MathInputI18nContext;
|
|
327
561
|
const MathInputIcon = ({
|
|
328
562
|
hovered,
|
|
329
563
|
focused,
|
|
@@ -1239,16 +1473,6 @@ const MAXERROR_EPSILON = Math.pow(2, -42);
|
|
|
1239
1473
|
// TOOD(kevinb): Figure out how this relates to KEScore in
|
|
1240
1474
|
// perseus-all-package/types.js and see if there's a way to
|
|
1241
1475
|
// unify these types.
|
|
1242
|
-
const errors = {
|
|
1243
|
-
APPROXIMATED_PI_ERROR: i18n._("Your answer is close, but you may " + "have approximated pi. Enter your " + "answer as a multiple of pi, like " + "<code>12\\ \\text{pi}</code> or " + "<code>2/3\\ \\text{pi}</code>"),
|
|
1244
|
-
EXTRA_SYMBOLS_ERROR: i18n._("We could not understand your " + "answer. Please check your answer for extra " + "text or symbols."),
|
|
1245
|
-
NEEDS_TO_BE_SIMPLFIED_ERROR: i18n._("Your answer is almost correct, " + "but it needs to be simplified."),
|
|
1246
|
-
MISSING_PERCENT_ERROR: i18n._("Your answer is almost correct, " + "but it is missing a " + "<code>\\%</code> at the end."),
|
|
1247
|
-
MULTIPLICATION_SIGN_ERROR: i18n._("I'm a computer. I only understand " + "multiplication if you use an asterisk " + "(*) as the multiplication sign."),
|
|
1248
|
-
WRONG_CASE_ERROR: i18n._("Your answer includes use of a variable with the wrong case."),
|
|
1249
|
-
WRONG_LETTER_ERROR: i18n._("Your answer includes a wrong variable letter.")
|
|
1250
|
-
};
|
|
1251
|
-
|
|
1252
1476
|
/*
|
|
1253
1477
|
* Answer types
|
|
1254
1478
|
*
|
|
@@ -1321,7 +1545,7 @@ const KhanAnswerTypes = {
|
|
|
1321
1545
|
*/
|
|
1322
1546
|
predicate: {
|
|
1323
1547
|
defaultForms: "integer, proper, improper, mixed, decimal",
|
|
1324
|
-
createValidatorFunctional: function (predicate, options) {
|
|
1548
|
+
createValidatorFunctional: function (predicate, options, strings) {
|
|
1325
1549
|
// Extract the options from the given solution object
|
|
1326
1550
|
options = _.extend({
|
|
1327
1551
|
simplify: "required",
|
|
@@ -1709,12 +1933,12 @@ const KhanAnswerTypes = {
|
|
|
1709
1933
|
} else if (form === "percent") {
|
|
1710
1934
|
// Otherwise, an error was returned
|
|
1711
1935
|
score.empty = true;
|
|
1712
|
-
score.message =
|
|
1936
|
+
score.message = strings.MISSING_PERCENT_ERROR;
|
|
1713
1937
|
} else {
|
|
1714
1938
|
if (options.simplify !== "enforced") {
|
|
1715
1939
|
score.empty = true;
|
|
1716
1940
|
}
|
|
1717
|
-
score.message =
|
|
1941
|
+
score.message = strings.NEEDS_TO_BE_SIMPLFIED_ERROR;
|
|
1718
1942
|
}
|
|
1719
1943
|
// The return false below stops the looping of the
|
|
1720
1944
|
// callback since predicate check succeeded.
|
|
@@ -1723,7 +1947,7 @@ const KhanAnswerTypes = {
|
|
|
1723
1947
|
}
|
|
1724
1948
|
if (piApprox && predicate(val, Math.abs(val * 0.001))) {
|
|
1725
1949
|
score.empty = true;
|
|
1726
|
-
score.message =
|
|
1950
|
+
score.message = strings.APPROXIMATED_PI_ERROR;
|
|
1727
1951
|
}
|
|
1728
1952
|
}
|
|
1729
1953
|
});
|
|
@@ -1739,7 +1963,7 @@ const KhanAnswerTypes = {
|
|
|
1739
1963
|
});
|
|
1740
1964
|
if (!interpretedGuess) {
|
|
1741
1965
|
score.empty = true;
|
|
1742
|
-
score.message =
|
|
1966
|
+
score.message = strings.EXTRA_SYMBOLS_ERROR;
|
|
1743
1967
|
return score;
|
|
1744
1968
|
}
|
|
1745
1969
|
}
|
|
@@ -1762,10 +1986,10 @@ const KhanAnswerTypes = {
|
|
|
1762
1986
|
type: "predicate"
|
|
1763
1987
|
})];
|
|
1764
1988
|
},
|
|
1765
|
-
createValidatorFunctional: function (correctAnswer, options) {
|
|
1989
|
+
createValidatorFunctional: function (correctAnswer, options, strings) {
|
|
1766
1990
|
return KhanAnswerTypes.predicate.createValidatorFunctional(
|
|
1767
1991
|
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
1768
|
-
...KhanAnswerTypes.number.convertToPredicate(correctAnswer, options));
|
|
1992
|
+
...KhanAnswerTypes.number.convertToPredicate(correctAnswer, options), strings);
|
|
1769
1993
|
}
|
|
1770
1994
|
},
|
|
1771
1995
|
/*
|
|
@@ -1830,7 +2054,7 @@ const KhanAnswerTypes = {
|
|
|
1830
2054
|
}
|
|
1831
2055
|
return solution;
|
|
1832
2056
|
},
|
|
1833
|
-
createValidatorFunctional: function (solution, options) {
|
|
2057
|
+
createValidatorFunctional: function (solution, options, strings) {
|
|
1834
2058
|
return function (guess) {
|
|
1835
2059
|
const score = {
|
|
1836
2060
|
empty: false,
|
|
@@ -1881,7 +2105,7 @@ const KhanAnswerTypes = {
|
|
|
1881
2105
|
// @ts-expect-error - TS2540 - Cannot assign to 'ungraded' because it is a read-only property.
|
|
1882
2106
|
score.ungraded = true;
|
|
1883
2107
|
// @ts-expect-error - TS2540 - Cannot assign to 'message' because it is a read-only property.
|
|
1884
|
-
score.message = result.wrongVariableCase ?
|
|
2108
|
+
score.message = result.wrongVariableCase ? strings.WRONG_CASE_ERROR : strings.WRONG_LETTER_ERROR;
|
|
1885
2109
|
// Don't tell the use they're "almost there" in this case, that may not be true and isn't helpful.
|
|
1886
2110
|
// @ts-expect-error - TS2339 - Property 'suppressAlmostThere' does not exist on type '{ readonly empty: false; readonly correct: false; readonly message: string | null | undefined; readonly guess: any; readonly ungraded: false; }'.
|
|
1887
2111
|
score.suppressAlmostThere = true;
|
|
@@ -1907,7 +2131,7 @@ const KhanAnswerTypes = {
|
|
|
1907
2131
|
// @ts-expect-error - TS2540 - Cannot assign to 'ungraded' because it is a read-only property.
|
|
1908
2132
|
score.ungraded = true;
|
|
1909
2133
|
// @ts-expect-error - TS2540 - Cannot assign to 'message' because it is a read-only property.
|
|
1910
|
-
score.message =
|
|
2134
|
+
score.message = strings.MULTIPLICATION_SIGN_ERROR;
|
|
1911
2135
|
} else if (resultX.message) {
|
|
1912
2136
|
// TODO(aasmund): I18nize `score.message`
|
|
1913
2137
|
// @ts-expect-error - TS2540 - Cannot assign to 'message' because it is a read-only property.
|
|
@@ -1922,9 +2146,6 @@ const KhanAnswerTypes = {
|
|
|
1922
2146
|
};
|
|
1923
2147
|
|
|
1924
2148
|
const _excluded$5 = ["scoring"];
|
|
1925
|
-
const ERROR_TITLE = i18n._("Oops!");
|
|
1926
|
-
const ERROR_MESSAGE = i18n._("Sorry, I don't understand that!");
|
|
1927
|
-
|
|
1928
2149
|
// Map of international operator names to their English equivalents
|
|
1929
2150
|
const englishOperators = {
|
|
1930
2151
|
arctg: "arctan",
|
|
@@ -1954,6 +2175,32 @@ const deriveKeypadVersion = apiOptions => {
|
|
|
1954
2175
|
// can remove this `virtualKeypadVersion` field entirely.
|
|
1955
2176
|
return apiOptions.nativeKeypadProxy != null ? "REACT_NATIVE_KEYPAD" : "MATH_INPUT_KEYPAD_V2";
|
|
1956
2177
|
};
|
|
2178
|
+
/**
|
|
2179
|
+
* Get the character used for separating decimals.
|
|
2180
|
+
*/
|
|
2181
|
+
const getDecimalSeparator = locale => {
|
|
2182
|
+
var _match$;
|
|
2183
|
+
switch (locale) {
|
|
2184
|
+
// TODO(somewhatabstract): Remove this when Chrome supports the `ka`
|
|
2185
|
+
// locale properly.
|
|
2186
|
+
// https://github.com/formatjs/formatjs/issues/1526#issuecomment-559891201
|
|
2187
|
+
//
|
|
2188
|
+
// Supported locales in Chrome:
|
|
2189
|
+
// https://source.chromium.org/chromium/chromium/src/+/master:third_party/icu/scripts/chrome_ui_languages.list
|
|
2190
|
+
case "ka":
|
|
2191
|
+
return ",";
|
|
2192
|
+
default:
|
|
2193
|
+
const numberWithDecimalSeparator = 1.1;
|
|
2194
|
+
// TODO(FEI-3647): Update to use .formatToParts() once we no longer have to
|
|
2195
|
+
// support Safari 12.
|
|
2196
|
+
const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator)
|
|
2197
|
+
// 0x661 is ARABIC-INDIC DIGIT ONE
|
|
2198
|
+
// 0x6F1 is EXTENDED ARABIC-INDIC DIGIT ONE
|
|
2199
|
+
.match(/[^\d\u0661\u06F1]/);
|
|
2200
|
+
return (_match$ = match == null ? void 0 : match[0]) != null ? _match$ : ".";
|
|
2201
|
+
}
|
|
2202
|
+
};
|
|
2203
|
+
|
|
1957
2204
|
// The new, MathQuill input expression widget
|
|
1958
2205
|
class Expression extends React.Component {
|
|
1959
2206
|
constructor(..._args) {
|
|
@@ -1986,7 +2233,7 @@ class Expression extends React.Component {
|
|
|
1986
2233
|
if (!this.parse(this.props.value, this.props).parsed) {
|
|
1987
2234
|
const apiResult = this.props.apiOptions.onInputError(null,
|
|
1988
2235
|
// reserved for some widget identifier
|
|
1989
|
-
this.props.value,
|
|
2236
|
+
this.props.value, this.context.strings.ERROR_TITLE);
|
|
1990
2237
|
if (apiResult !== false) {
|
|
1991
2238
|
this.setState({
|
|
1992
2239
|
invalid: true
|
|
@@ -2010,7 +2257,7 @@ class Expression extends React.Component {
|
|
|
2010
2257
|
scoring
|
|
2011
2258
|
} = _ref,
|
|
2012
2259
|
rubric = _objectWithoutPropertiesLoose(_ref, _excluded$5);
|
|
2013
|
-
const score = Expression.validate(this.getUserInput(), rubric, onInputError || function () {});
|
|
2260
|
+
const score = Expression.validate(this.getUserInput(), rubric, onInputError || function () {}, this.context.strings, this.context.locale);
|
|
2014
2261
|
|
|
2015
2262
|
// "scoring" is a flag that indicates when we are checking answers.
|
|
2016
2263
|
// otherwise, we may just be checking validity after changes.
|
|
@@ -2037,7 +2284,7 @@ class Expression extends React.Component {
|
|
|
2037
2284
|
// make it so that solution answers with ','s or '.'s work
|
|
2038
2285
|
const options = _.pick(props || this.props, "functions");
|
|
2039
2286
|
_.extend(options, {
|
|
2040
|
-
decimal_separator:
|
|
2287
|
+
decimal_separator: getDecimalSeparator(this.context.locale)
|
|
2041
2288
|
});
|
|
2042
2289
|
return KAS.parse(normalizeTex(value), options);
|
|
2043
2290
|
};
|
|
@@ -2115,10 +2362,10 @@ class Expression extends React.Component {
|
|
|
2115
2362
|
*/
|
|
2116
2363
|
static validate(userInput, rubric,
|
|
2117
2364
|
// @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
|
|
2118
|
-
onInputError = function () {}) {
|
|
2365
|
+
onInputError = function () {}, strings, locale) {
|
|
2119
2366
|
const options = _.clone(rubric);
|
|
2120
2367
|
_.extend(options, {
|
|
2121
|
-
decimal_separator:
|
|
2368
|
+
decimal_separator: getDecimalSeparator(locale)
|
|
2122
2369
|
});
|
|
2123
2370
|
const createValidator = answer => {
|
|
2124
2371
|
// We give options to KAS.parse here because it is parsing the
|
|
@@ -2141,7 +2388,7 @@ class Expression extends React.Component {
|
|
|
2141
2388
|
return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, _({}).extend(options, {
|
|
2142
2389
|
simplify: answer.simplify,
|
|
2143
2390
|
form: answer.form
|
|
2144
|
-
}));
|
|
2391
|
+
}), strings);
|
|
2145
2392
|
};
|
|
2146
2393
|
|
|
2147
2394
|
// Find the first answer form that matches the user's input and that
|
|
@@ -2284,6 +2531,10 @@ class Expression extends React.Component {
|
|
|
2284
2531
|
"perseus-widget-expression": true,
|
|
2285
2532
|
"show-error-tooltip": this.state.showErrorTooltip
|
|
2286
2533
|
});
|
|
2534
|
+
const {
|
|
2535
|
+
ERROR_MESSAGE,
|
|
2536
|
+
ERROR_TITLE
|
|
2537
|
+
} = this.context.strings;
|
|
2287
2538
|
return /*#__PURE__*/React.createElement("div", {
|
|
2288
2539
|
className: className,
|
|
2289
2540
|
onBlur: () => this.state.invalid && this.setState({
|
|
@@ -2332,6 +2583,7 @@ class Expression extends React.Component {
|
|
|
2332
2583
|
* to be included as keys on the keypad. These are scraped from the answer
|
|
2333
2584
|
* forms.
|
|
2334
2585
|
*/
|
|
2586
|
+
Expression.contextType = PerseusI18nContext;
|
|
2335
2587
|
Expression.defaultProps = {
|
|
2336
2588
|
value: "",
|
|
2337
2589
|
times: false,
|
|
@@ -2719,7 +2971,7 @@ function keScoreFromPerseusScore(score, guess, state) {
|
|
|
2719
2971
|
* Return the first valid interpretation of 'text' as a number, in the form
|
|
2720
2972
|
* {value: 2.3, exact: true}.
|
|
2721
2973
|
*/
|
|
2722
|
-
function firstNumericalParse$1(text) {
|
|
2974
|
+
function firstNumericalParse$1(text, strings) {
|
|
2723
2975
|
// TODO(alpert): This is sort of hacky...
|
|
2724
2976
|
let first;
|
|
2725
2977
|
const val = KhanAnswerTypes.predicate.createValidatorFunctional(function (ans) {
|
|
@@ -2729,7 +2981,7 @@ function firstNumericalParse$1(text) {
|
|
|
2729
2981
|
simplify: "optional",
|
|
2730
2982
|
inexact: true,
|
|
2731
2983
|
forms: "integer, proper, improper, pi, log, mixed, decimal"
|
|
2732
|
-
});
|
|
2984
|
+
}, strings);
|
|
2733
2985
|
val(text);
|
|
2734
2986
|
return first;
|
|
2735
2987
|
}
|
|
@@ -4255,7 +4507,6 @@ function _classPrivateFieldBase(receiver, privateKey) {
|
|
|
4255
4507
|
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
|
|
4256
4508
|
throw new TypeError("attempted to use private field on non-instance");
|
|
4257
4509
|
}
|
|
4258
|
-
|
|
4259
4510
|
return receiver;
|
|
4260
4511
|
}
|
|
4261
4512
|
|
|
@@ -10282,8 +10533,6 @@ const getClipPoint = function getClipPoint(graph, coord, angle) {
|
|
|
10282
10533
|
class WrappedDrawing {
|
|
10283
10534
|
constructor(graphie, initialPoint) {
|
|
10284
10535
|
this.graphie = void 0;
|
|
10285
|
-
this.wrapper = void 0;
|
|
10286
|
-
this.visibleShape = void 0;
|
|
10287
10536
|
this.initialPoint = void 0;
|
|
10288
10537
|
this.graphie = graphie;
|
|
10289
10538
|
this.initialPoint = initialPoint;
|
|
@@ -18317,7 +18566,7 @@ const rules$1 = _extends({}, pureMarkdownRules, {
|
|
|
18317
18566
|
// just a stub for testing.
|
|
18318
18567
|
return /*#__PURE__*/React.createElement("em", {
|
|
18319
18568
|
key: state.key
|
|
18320
|
-
},
|
|
18569
|
+
}, `[Widget: ${node.id}]`);
|
|
18321
18570
|
}
|
|
18322
18571
|
}),
|
|
18323
18572
|
blockMath: _extends({}, pureMarkdownRules.blockMath, {
|
|
@@ -18790,6 +19039,7 @@ const replaceDeprecatedEditors = () => {
|
|
|
18790
19039
|
replaceEditor("simulator", "deprecated-standin");
|
|
18791
19040
|
};
|
|
18792
19041
|
const getWidget = name => {
|
|
19042
|
+
var _widgets$name;
|
|
18793
19043
|
// TODO(alex): Consider referring to these as renderers to avoid
|
|
18794
19044
|
// overloading "widget"
|
|
18795
19045
|
if (!_.has(widgets, name)) {
|
|
@@ -18797,9 +19047,9 @@ const getWidget = name => {
|
|
|
18797
19047
|
}
|
|
18798
19048
|
|
|
18799
19049
|
// Allow widgets to specify a widget directly or via a function
|
|
18800
|
-
if (widgets[name].getWidget) {
|
|
18801
|
-
|
|
18802
|
-
return widgets[name].getWidget();
|
|
19050
|
+
if ((_widgets$name = widgets[name]) != null && _widgets$name.getWidget) {
|
|
19051
|
+
var _widgets$name$getWidg, _widgets$name2;
|
|
19052
|
+
return (_widgets$name$getWidg = (_widgets$name2 = widgets[name]).getWidget) == null ? void 0 : _widgets$name$getWidg.call(_widgets$name2);
|
|
18803
19053
|
}
|
|
18804
19054
|
return widgets[name].widget;
|
|
18805
19055
|
};
|
|
@@ -18948,7 +19198,7 @@ const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
|
|
|
18948
19198
|
options: newEditorProps
|
|
18949
19199
|
});
|
|
18950
19200
|
};
|
|
18951
|
-
const getRendererPropsForWidgetInfo = (widgetInfo, problemNum) => {
|
|
19201
|
+
const getRendererPropsForWidgetInfo = (widgetInfo, strings, problemNum) => {
|
|
18952
19202
|
const type = widgetInfo.type;
|
|
18953
19203
|
const widgetExports = widgets[type];
|
|
18954
19204
|
if (widgetExports == null) {
|
|
@@ -18969,7 +19219,7 @@ const getRendererPropsForWidgetInfo = (widgetInfo, problemNum) => {
|
|
|
18969
19219
|
transform = widgetExports.transform || _.identity;
|
|
18970
19220
|
}
|
|
18971
19221
|
// widgetInfo.options are the widgetEditor's props:
|
|
18972
|
-
return transform(widgetInfo.options, problemNum);
|
|
19222
|
+
return transform(widgetInfo.options, strings, problemNum);
|
|
18973
19223
|
};
|
|
18974
19224
|
const traverseChildWidgets$3 = (widgetInfo, traverseRenderer) => {
|
|
18975
19225
|
if (!traverseRenderer) {
|
|
@@ -19368,8 +19618,11 @@ class Renderer extends React.Component {
|
|
|
19368
19618
|
problemNum
|
|
19369
19619
|
} = props;
|
|
19370
19620
|
const widgetsStartProps = {};
|
|
19621
|
+
const {
|
|
19622
|
+
strings
|
|
19623
|
+
} = this.props;
|
|
19371
19624
|
entries(allWidgetInfo).forEach(([key, widgetInfo]) => {
|
|
19372
|
-
widgetsStartProps[key] = getRendererPropsForWidgetInfo(widgetInfo, problemNum);
|
|
19625
|
+
widgetsStartProps[key] = getRendererPropsForWidgetInfo(widgetInfo, strings, problemNum);
|
|
19373
19626
|
});
|
|
19374
19627
|
|
|
19375
19628
|
// Call the onWidgetStartProps callback if it exists
|
|
@@ -21429,10 +21682,12 @@ class InputWithExamples extends React.Component {
|
|
|
21429
21682
|
id: this._getUniqueId()
|
|
21430
21683
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
21431
21684
|
content: examplesContent,
|
|
21432
|
-
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "input-with-examples")
|
|
21685
|
+
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "input-with-examples"),
|
|
21686
|
+
strings: this.context.strings
|
|
21433
21687
|
})));
|
|
21434
21688
|
}
|
|
21435
21689
|
}
|
|
21690
|
+
InputWithExamples.contextType = PerseusI18nContext;
|
|
21436
21691
|
InputWithExamples.defaultProps = {
|
|
21437
21692
|
type: "text",
|
|
21438
21693
|
shouldShowExamples: true,
|
|
@@ -21554,32 +21809,32 @@ const answerTypes = {
|
|
|
21554
21809
|
}
|
|
21555
21810
|
};
|
|
21556
21811
|
const formExamples$1 = {
|
|
21557
|
-
integer: function (options) {
|
|
21558
|
-
return
|
|
21812
|
+
integer: function (options, strings) {
|
|
21813
|
+
return strings.integerExample;
|
|
21559
21814
|
},
|
|
21560
|
-
proper: function (options) {
|
|
21815
|
+
proper: function (options, strings) {
|
|
21561
21816
|
if (options.simplify === "optional") {
|
|
21562
|
-
return
|
|
21817
|
+
return strings.properExample;
|
|
21563
21818
|
}
|
|
21564
|
-
return
|
|
21819
|
+
return strings.simplifiedProperExample;
|
|
21565
21820
|
},
|
|
21566
|
-
improper: function (options) {
|
|
21821
|
+
improper: function (options, strings) {
|
|
21567
21822
|
if (options.simplify === "optional") {
|
|
21568
|
-
return
|
|
21823
|
+
return strings.improperExample;
|
|
21569
21824
|
}
|
|
21570
|
-
return
|
|
21825
|
+
return strings.simplifiedImproperExample;
|
|
21571
21826
|
},
|
|
21572
|
-
mixed: function (options) {
|
|
21573
|
-
return
|
|
21827
|
+
mixed: function (options, strings) {
|
|
21828
|
+
return strings.mixedExample;
|
|
21574
21829
|
},
|
|
21575
|
-
decimal: function (options) {
|
|
21576
|
-
return
|
|
21830
|
+
decimal: function (options, strings) {
|
|
21831
|
+
return strings.decimalExample;
|
|
21577
21832
|
},
|
|
21578
|
-
percent: function (options) {
|
|
21579
|
-
return
|
|
21833
|
+
percent: function (options, strings) {
|
|
21834
|
+
return strings.percentExample;
|
|
21580
21835
|
},
|
|
21581
|
-
pi: function (options) {
|
|
21582
|
-
return
|
|
21836
|
+
pi: function (options, strings) {
|
|
21837
|
+
return strings.piExample;
|
|
21583
21838
|
}
|
|
21584
21839
|
};
|
|
21585
21840
|
class InputNumber extends React.Component {
|
|
@@ -21637,18 +21892,16 @@ class InputNumber extends React.Component {
|
|
|
21637
21892
|
};
|
|
21638
21893
|
this.simpleValidate = (rubric, onInputError) => {
|
|
21639
21894
|
onInputError = onInputError || function () {};
|
|
21640
|
-
return InputNumber.validate(this.getUserInput(), rubric, onInputError);
|
|
21895
|
+
return InputNumber.validate(this.getUserInput(), rubric, this.context.strings, onInputError);
|
|
21641
21896
|
};
|
|
21642
21897
|
this.examples = () => {
|
|
21898
|
+
const {
|
|
21899
|
+
strings
|
|
21900
|
+
} = this.context;
|
|
21643
21901
|
const type = this.props.answerType;
|
|
21644
21902
|
const forms = answerTypes[type].forms.split(/\s*,\s*/);
|
|
21645
|
-
const examples = _.map(forms,
|
|
21646
|
-
|
|
21647
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
21648
|
-
// eslint-disable-next-line @babel/no-invalid-this
|
|
21649
|
-
return formExamples$1[form](this.props);
|
|
21650
|
-
}, this);
|
|
21651
|
-
return [i18n._("**Your answer should be** ")].concat(examples);
|
|
21903
|
+
const examples = _.map(forms, form => formExamples$1[form](this.props, strings));
|
|
21904
|
+
return [strings.yourAnswer].concat(examples);
|
|
21652
21905
|
};
|
|
21653
21906
|
}
|
|
21654
21907
|
render() {
|
|
@@ -21701,7 +21954,7 @@ class InputNumber extends React.Component {
|
|
|
21701
21954
|
linterContext: this.props.linterContext
|
|
21702
21955
|
});
|
|
21703
21956
|
}
|
|
21704
|
-
static validate(state, rubric, onInputError = () => {}) {
|
|
21957
|
+
static validate(state, rubric, strings, onInputError = () => {}) {
|
|
21705
21958
|
if (rubric.answerType == null) {
|
|
21706
21959
|
rubric.answerType = "number";
|
|
21707
21960
|
}
|
|
@@ -21715,7 +21968,7 @@ class InputNumber extends React.Component {
|
|
|
21715
21968
|
inexact: rubric.inexact || undefined,
|
|
21716
21969
|
maxError: rubric.maxError,
|
|
21717
21970
|
forms: answerTypes[rubric.answerType].forms
|
|
21718
|
-
});
|
|
21971
|
+
}, strings);
|
|
21719
21972
|
|
|
21720
21973
|
// We may have received TeX; try to parse it before grading.
|
|
21721
21974
|
// If `currentValue` is not TeX, this should be a no-op.
|
|
@@ -21757,6 +22010,7 @@ class InputNumber extends React.Component {
|
|
|
21757
22010
|
return answerString;
|
|
21758
22011
|
}
|
|
21759
22012
|
}
|
|
22013
|
+
InputNumber.contextType = PerseusI18nContext;
|
|
21760
22014
|
InputNumber.defaultProps = {
|
|
21761
22015
|
currentValue: "",
|
|
21762
22016
|
size: "normal",
|
|
@@ -21840,12 +22094,12 @@ const answerFormButtons = [{
|
|
|
21840
22094
|
content: "\u03C0"
|
|
21841
22095
|
}];
|
|
21842
22096
|
const formExamples = {
|
|
21843
|
-
integer: () =>
|
|
21844
|
-
proper: form => form.simplify === "optional" ?
|
|
21845
|
-
improper: form => form.simplify === "optional" ?
|
|
21846
|
-
mixed: () =>
|
|
21847
|
-
decimal: () =>
|
|
21848
|
-
pi: () =>
|
|
22097
|
+
integer: (form, strings) => strings.integerExample,
|
|
22098
|
+
proper: (form, strings) => form.simplify === "optional" ? strings.properExample : strings.simplifiedProperExample,
|
|
22099
|
+
improper: (form, strings) => form.simplify === "optional" ? strings.improperExample : strings.simplifiedImproperExample,
|
|
22100
|
+
mixed: (form, strings) => strings.mixedExample,
|
|
22101
|
+
decimal: (form, strings) => strings.decimalExample,
|
|
22102
|
+
pi: (form, strings) => strings.piExample
|
|
21849
22103
|
};
|
|
21850
22104
|
class NumericInput extends React.Component {
|
|
21851
22105
|
constructor(...args) {
|
|
@@ -21867,13 +22121,12 @@ class NumericInput extends React.Component {
|
|
|
21867
22121
|
};
|
|
21868
22122
|
});
|
|
21869
22123
|
let examples = _.map(forms, form => {
|
|
21870
|
-
|
|
21871
|
-
return formExamples[form.name](form);
|
|
22124
|
+
return formExamples[form.name](form, this.context.strings);
|
|
21872
22125
|
});
|
|
21873
22126
|
// Ensure no duplicate tooltip text from simplified and unsimplified
|
|
21874
22127
|
// versions of the same format
|
|
21875
22128
|
examples = _.uniq(examples);
|
|
21876
|
-
return [
|
|
22129
|
+
return [this.context.strings.yourAnswer].concat(examples);
|
|
21877
22130
|
};
|
|
21878
22131
|
this.shouldShowExamples = () => {
|
|
21879
22132
|
var _this$props$answerFor2, _this$props$answerFor3;
|
|
@@ -21886,7 +22139,7 @@ class NumericInput extends React.Component {
|
|
|
21886
22139
|
return !noFormsAccepted && !allFormsAccepted;
|
|
21887
22140
|
};
|
|
21888
22141
|
this.simpleValidate = rubric => {
|
|
21889
|
-
return NumericInput.validate(this.getUserInput(), rubric);
|
|
22142
|
+
return NumericInput.validate(this.getUserInput(), rubric, this.context.strings);
|
|
21890
22143
|
};
|
|
21891
22144
|
this.focus = () => {
|
|
21892
22145
|
var _this$inputRef;
|
|
@@ -21966,7 +22219,7 @@ class NumericInput extends React.Component {
|
|
|
21966
22219
|
}
|
|
21967
22220
|
return answerStrings[0];
|
|
21968
22221
|
}
|
|
21969
|
-
static validate(userInput, rubric) {
|
|
22222
|
+
static validate(userInput, rubric, strings) {
|
|
21970
22223
|
const defaultAnswerForms = answerFormButtons.map(e => e["value"])
|
|
21971
22224
|
// Don't default to validating the answer as a pi answer
|
|
21972
22225
|
// if answerForm isn't set on the answer
|
|
@@ -21993,7 +22246,7 @@ class NumericInput extends React.Component {
|
|
|
21993
22246
|
// TODO(merlob) backfill / delete
|
|
21994
22247
|
maxError: answer.maxError,
|
|
21995
22248
|
forms: validatorForms
|
|
21996
|
-
});
|
|
22249
|
+
}, strings);
|
|
21997
22250
|
};
|
|
21998
22251
|
|
|
21999
22252
|
// We may have received TeX; try to parse it before grading.
|
|
@@ -22063,7 +22316,7 @@ class NumericInput extends React.Component {
|
|
|
22063
22316
|
render() {
|
|
22064
22317
|
let labelText = this.props.labelText;
|
|
22065
22318
|
if (labelText == null || labelText === "") {
|
|
22066
|
-
labelText =
|
|
22319
|
+
labelText = this.context.strings.yourAnswerLabel;
|
|
22067
22320
|
}
|
|
22068
22321
|
|
|
22069
22322
|
// To right align a custom keypad we need to wrap it.
|
|
@@ -22118,6 +22371,7 @@ class NumericInput extends React.Component {
|
|
|
22118
22371
|
// a given *problem* rather than for each possible [correct/wrong] *answer*.
|
|
22119
22372
|
// When should two answers to a problem take different answer types?
|
|
22120
22373
|
// See D27790 for more discussion.
|
|
22374
|
+
NumericInput.contextType = PerseusI18nContext;
|
|
22121
22375
|
NumericInput.defaultProps = {
|
|
22122
22376
|
currentValue: "",
|
|
22123
22377
|
size: "normal",
|
|
@@ -22273,18 +22527,21 @@ class PassageRef extends React.Component {
|
|
|
22273
22527
|
return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
|
|
22274
22528
|
}
|
|
22275
22529
|
render() {
|
|
22530
|
+
const {
|
|
22531
|
+
strings
|
|
22532
|
+
} = this.context;
|
|
22276
22533
|
const lineRange = this.state.lineRange;
|
|
22277
22534
|
let lineRangeOutput;
|
|
22278
22535
|
if (!lineRange) {
|
|
22279
|
-
lineRangeOutput =
|
|
22536
|
+
lineRangeOutput = strings.lineRange({
|
|
22280
22537
|
lineRange: `?${EN_DASH$1}?`
|
|
22281
22538
|
});
|
|
22282
22539
|
} else if (lineRange[0] === lineRange[1]) {
|
|
22283
|
-
lineRangeOutput =
|
|
22284
|
-
lineNumber: lineRange[0]
|
|
22540
|
+
lineRangeOutput = strings.lineNumber({
|
|
22541
|
+
lineNumber: String(lineRange[0])
|
|
22285
22542
|
});
|
|
22286
22543
|
} else {
|
|
22287
|
-
lineRangeOutput =
|
|
22544
|
+
lineRangeOutput = strings.lineRange({
|
|
22288
22545
|
lineRange: lineRange[0] + EN_DASH$1 + lineRange[1]
|
|
22289
22546
|
});
|
|
22290
22547
|
}
|
|
@@ -22326,6 +22583,7 @@ class PassageRef extends React.Component {
|
|
|
22326
22583
|
};
|
|
22327
22584
|
}
|
|
22328
22585
|
}
|
|
22586
|
+
PassageRef.contextType = PerseusI18nContext;
|
|
22329
22587
|
PassageRef.defaultProps = {
|
|
22330
22588
|
passageNumber: 1,
|
|
22331
22589
|
referenceNumber: 1,
|
|
@@ -22689,7 +22947,7 @@ Icon.defaultProps = {
|
|
|
22689
22947
|
color: "currentColor"
|
|
22690
22948
|
};
|
|
22691
22949
|
|
|
22692
|
-
function getA11yText(letter, checked, correct, crossedOut, showCorrectness) {
|
|
22950
|
+
function getA11yText(letter, checked, correct, crossedOut, showCorrectness, strings) {
|
|
22693
22951
|
// There are two pieces of metadata we want to add to each a11yText:
|
|
22694
22952
|
// whether the answer was checked/crossed-out/neither, and whether the
|
|
22695
22953
|
// answer is correct/incorrect/not-yet-revealed.
|
|
@@ -22698,45 +22956,45 @@ function getA11yText(letter, checked, correct, crossedOut, showCorrectness) {
|
|
|
22698
22956
|
// we've just enumerated all 9 possibilities as separate strings.
|
|
22699
22957
|
if (showCorrectness && correct) {
|
|
22700
22958
|
if (checked) {
|
|
22701
|
-
return
|
|
22959
|
+
return strings.choiceCheckedCorrect({
|
|
22702
22960
|
letter
|
|
22703
22961
|
});
|
|
22704
22962
|
}
|
|
22705
22963
|
if (crossedOut) {
|
|
22706
|
-
return
|
|
22964
|
+
return strings.choiceCrossedOutCorrect({
|
|
22707
22965
|
letter
|
|
22708
22966
|
});
|
|
22709
22967
|
}
|
|
22710
|
-
return
|
|
22968
|
+
return strings.choiceCorrect({
|
|
22711
22969
|
letter
|
|
22712
22970
|
});
|
|
22713
22971
|
}
|
|
22714
22972
|
if (showCorrectness && !correct) {
|
|
22715
22973
|
if (checked) {
|
|
22716
|
-
return
|
|
22974
|
+
return strings.choiceCheckedIncorrect({
|
|
22717
22975
|
letter
|
|
22718
22976
|
});
|
|
22719
22977
|
}
|
|
22720
22978
|
if (crossedOut) {
|
|
22721
|
-
return
|
|
22979
|
+
return strings.choiceCrossedOutIncorrect({
|
|
22722
22980
|
letter
|
|
22723
22981
|
});
|
|
22724
22982
|
}
|
|
22725
|
-
return
|
|
22983
|
+
return strings.choiceIncorrect({
|
|
22726
22984
|
letter
|
|
22727
22985
|
});
|
|
22728
22986
|
}
|
|
22729
22987
|
if (checked) {
|
|
22730
|
-
return
|
|
22988
|
+
return strings.choiceChecked({
|
|
22731
22989
|
letter
|
|
22732
22990
|
});
|
|
22733
22991
|
}
|
|
22734
22992
|
if (crossedOut) {
|
|
22735
|
-
return
|
|
22993
|
+
return strings.choiceCrossedOut({
|
|
22736
22994
|
letter
|
|
22737
22995
|
});
|
|
22738
22996
|
}
|
|
22739
|
-
return
|
|
22997
|
+
return strings.choice({
|
|
22740
22998
|
letter
|
|
22741
22999
|
});
|
|
22742
23000
|
}
|
|
@@ -22758,7 +23016,7 @@ const FocusRing = function FocusRing(props) {
|
|
|
22758
23016
|
borderRadius
|
|
22759
23017
|
};
|
|
22760
23018
|
return /*#__PURE__*/React.createElement("span", {
|
|
22761
|
-
"data-
|
|
23019
|
+
"data-testid": "focus-ring",
|
|
22762
23020
|
className: css(styles$z.ring),
|
|
22763
23021
|
style: style
|
|
22764
23022
|
}, children);
|
|
@@ -22783,18 +23041,8 @@ const styles$z = StyleSheet.create({
|
|
|
22783
23041
|
* "choice letter". (For example, in English, `getChoiceLetter(0)` is "A",
|
|
22784
23042
|
* `getChoiceLetter(1)` is "B", etc.)
|
|
22785
23043
|
*/
|
|
22786
|
-
function getChoiceLetter(pos) {
|
|
22787
|
-
|
|
22788
|
-
* front of multiple-choice options. For instance, a multiple-choice
|
|
22789
|
-
* question with three options would display
|
|
22790
|
-
* (A) first option
|
|
22791
|
-
* (B) second option
|
|
22792
|
-
* (C) third option
|
|
22793
|
-
* There must be spaces between each of the different characters. The
|
|
22794
|
-
* characters will show up next to options in the order that they are
|
|
22795
|
-
* listed here. Most multiple choice questions have 5 or fewer options.
|
|
22796
|
-
*/
|
|
22797
|
-
const lettersString = i18n._("A B C D E F G H I J K L M N O P Q R S T U V W X Y Z");
|
|
23044
|
+
function getChoiceLetter(pos, strings) {
|
|
23045
|
+
const lettersString = strings.letters;
|
|
22798
23046
|
const letters = lettersString.split(" ");
|
|
22799
23047
|
if (pos < letters.length) {
|
|
22800
23048
|
// If the position we need is listed in the localized string, use that.
|
|
@@ -22829,7 +23077,7 @@ const CrossOutLine = function CrossOutLine(props) {
|
|
|
22829
23077
|
height: CROSS_OUT_LINE_SIZE,
|
|
22830
23078
|
viewBox: `0 0 ${CROSS_OUT_LINE_SIZE} ${CROSS_OUT_LINE_SIZE}`,
|
|
22831
23079
|
className: css(styles$x.crossOutLine),
|
|
22832
|
-
"data-
|
|
23080
|
+
"data-testid": "choice-icon__cross-out-line"
|
|
22833
23081
|
}, /*#__PURE__*/React.createElement("line", {
|
|
22834
23082
|
// The line stretches from the bottom-left to top-right.
|
|
22835
23083
|
// We don't quite go to the _very_ corner, because the cute
|
|
@@ -22858,7 +23106,10 @@ function ChoiceInner(props) {
|
|
|
22858
23106
|
showCorrectness,
|
|
22859
23107
|
correct
|
|
22860
23108
|
} = props;
|
|
22861
|
-
const
|
|
23109
|
+
const {
|
|
23110
|
+
strings
|
|
23111
|
+
} = usePerseusI18n();
|
|
23112
|
+
const letter = getChoiceLetter(pos, strings);
|
|
22862
23113
|
if (!showCorrectness) {
|
|
22863
23114
|
return letter;
|
|
22864
23115
|
}
|
|
@@ -22932,7 +23183,7 @@ const ChoiceIcon = function ChoiceIcon(props) {
|
|
|
22932
23183
|
}, /*#__PURE__*/React.createElement("div", {
|
|
22933
23184
|
// @ts-expect-error - TS2322 - Type '{ backgroundColor: string | null | undefined; borderColor: string; color: string; borderRadius: number; }' is not assignable to type 'Properties<string | number, string & {}>'.
|
|
22934
23185
|
style: dynamicStyles,
|
|
22935
|
-
"data-
|
|
23186
|
+
"data-testid": "choice-icon__library-choice-icon",
|
|
22936
23187
|
className: css(styles$w.circle, showCorrectness && correct && styles$w.circleCorrect, showCorrectness && !correct && styles$w.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$w.circleIncorrectAnswered)
|
|
22937
23188
|
// used in BaseRadio editMode to check
|
|
22938
23189
|
// if we actually clicked on the radio icon
|
|
@@ -23004,24 +23255,24 @@ const styles$w = StyleSheet.create({
|
|
|
23004
23255
|
* ChoiceIcon visualizations but is meant to make the distinctions
|
|
23005
23256
|
* between the states more immediately clear to users.
|
|
23006
23257
|
*/
|
|
23007
|
-
function renderText(checked, correct, crossedOut) {
|
|
23258
|
+
function renderText(checked, correct, crossedOut, strings) {
|
|
23008
23259
|
if (correct) {
|
|
23009
23260
|
// For correct answers, we surface checked _or_ crossedOut state,
|
|
23010
23261
|
// because any interaction with the correct answer is noteworthy!
|
|
23011
23262
|
if (checked) {
|
|
23012
|
-
return
|
|
23263
|
+
return strings.correctSelected;
|
|
23013
23264
|
}
|
|
23014
23265
|
if (crossedOut) {
|
|
23015
|
-
return
|
|
23266
|
+
return strings.correctCrossedOut;
|
|
23016
23267
|
}
|
|
23017
|
-
return
|
|
23268
|
+
return strings.correct;
|
|
23018
23269
|
}
|
|
23019
23270
|
// But, for incorrect answers, we only surface checked state,
|
|
23020
23271
|
// because crossing out an incorrect answer is not noteworthy.
|
|
23021
23272
|
if (checked) {
|
|
23022
|
-
return
|
|
23273
|
+
return strings.incorrectSelected;
|
|
23023
23274
|
}
|
|
23024
|
-
return
|
|
23275
|
+
return strings.incorrect;
|
|
23025
23276
|
}
|
|
23026
23277
|
const OptionStatus = function OptionStatus(props) {
|
|
23027
23278
|
const {
|
|
@@ -23031,6 +23282,9 @@ const OptionStatus = function OptionStatus(props) {
|
|
|
23031
23282
|
previouslyAnswered,
|
|
23032
23283
|
reviewMode
|
|
23033
23284
|
} = props;
|
|
23285
|
+
const {
|
|
23286
|
+
strings
|
|
23287
|
+
} = usePerseusI18n();
|
|
23034
23288
|
|
|
23035
23289
|
// Option status is shown only in review mode, or for incorrectly
|
|
23036
23290
|
// answered items.
|
|
@@ -23050,7 +23304,7 @@ const OptionStatus = function OptionStatus(props) {
|
|
|
23050
23304
|
}
|
|
23051
23305
|
return /*#__PURE__*/React.createElement("div", {
|
|
23052
23306
|
className: css(styles$v.text, textStyle)
|
|
23053
|
-
}, renderText(checked, correct, crossedOut));
|
|
23307
|
+
}, renderText(checked, correct, crossedOut, strings));
|
|
23054
23308
|
};
|
|
23055
23309
|
const styles$v = StyleSheet.create({
|
|
23056
23310
|
text: {
|
|
@@ -23097,6 +23351,9 @@ const Choice = function Choice(props) {
|
|
|
23097
23351
|
forwardedRef
|
|
23098
23352
|
} = props;
|
|
23099
23353
|
const [isInputFocused, setIsInputFocused] = useState(false);
|
|
23354
|
+
const {
|
|
23355
|
+
strings
|
|
23356
|
+
} = usePerseusI18n();
|
|
23100
23357
|
useEffect(() => {
|
|
23101
23358
|
if (isInputFocused && disabled) {
|
|
23102
23359
|
setIsInputFocused(false);
|
|
@@ -23126,8 +23383,8 @@ const Choice = function Choice(props) {
|
|
|
23126
23383
|
// However, we don't want to do this when we're in review mode in the
|
|
23127
23384
|
// content library.
|
|
23128
23385
|
const showDimmed = !reviewMode && apiOptions.readOnly || crossedOut;
|
|
23129
|
-
const letter = getChoiceLetter(pos);
|
|
23130
|
-
const a11yText = getA11yText(letter, checked, correct, crossedOut, showCorrectness);
|
|
23386
|
+
const letter = getChoiceLetter(pos, strings);
|
|
23387
|
+
const a11yText = getA11yText(letter, checked, correct, crossedOut, showCorrectness, strings);
|
|
23131
23388
|
const idFactory = useUniqueIdWithMock(`choice`);
|
|
23132
23389
|
const choiceId = idFactory.get(letter);
|
|
23133
23390
|
return /*#__PURE__*/React.createElement("div", {
|
|
@@ -23227,15 +23484,15 @@ const Choice = function Choice(props) {
|
|
|
23227
23484
|
content: ({
|
|
23228
23485
|
close
|
|
23229
23486
|
}) => /*#__PURE__*/React.createElement(PopoverContent, {
|
|
23230
|
-
title:
|
|
23231
|
-
content:
|
|
23487
|
+
title: strings.crossOut,
|
|
23488
|
+
content: strings.crossOutOption,
|
|
23232
23489
|
closeButtonVisible: true,
|
|
23233
23490
|
actions: /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(Strut, {
|
|
23234
23491
|
size: spacing.medium_16
|
|
23235
23492
|
}), /*#__PURE__*/React.createElement(Button, {
|
|
23236
23493
|
kind: "primary",
|
|
23237
|
-
"aria-label":
|
|
23238
|
-
letter: getChoiceLetter(pos)
|
|
23494
|
+
"aria-label": strings.crossOutChoice({
|
|
23495
|
+
letter: getChoiceLetter(pos, strings)
|
|
23239
23496
|
}),
|
|
23240
23497
|
disabled: apiOptions.readOnly || reviewMode,
|
|
23241
23498
|
onClick: () => {
|
|
@@ -23255,14 +23512,14 @@ const Choice = function Choice(props) {
|
|
|
23255
23512
|
}
|
|
23256
23513
|
close();
|
|
23257
23514
|
}
|
|
23258
|
-
}, crossedOut ?
|
|
23515
|
+
}, crossedOut ? strings.bringBack : strings.crossOut))
|
|
23259
23516
|
})
|
|
23260
23517
|
}, ({
|
|
23261
23518
|
open
|
|
23262
23519
|
}) => /*#__PURE__*/React.createElement(Clickable, {
|
|
23263
23520
|
onClick: open,
|
|
23264
|
-
"aria-label":
|
|
23265
|
-
letter: getChoiceLetter(pos)
|
|
23521
|
+
"aria-label": strings.openMenuForChoice({
|
|
23522
|
+
letter: getChoiceLetter(pos, strings)
|
|
23266
23523
|
}),
|
|
23267
23524
|
style: {
|
|
23268
23525
|
alignSelf: "center",
|
|
@@ -23282,7 +23539,7 @@ const Choice = function Choice(props) {
|
|
|
23282
23539
|
color: color.offBlack64
|
|
23283
23540
|
})))), showRationale && /*#__PURE__*/React.createElement("div", {
|
|
23284
23541
|
className: rationaleClassName,
|
|
23285
|
-
"data-
|
|
23542
|
+
"data-testid": `perseus-radio-rationale-content-${pos}`
|
|
23286
23543
|
}, rationale));
|
|
23287
23544
|
};
|
|
23288
23545
|
const styles$u = StyleSheet.create({
|
|
@@ -23314,6 +23571,9 @@ const ChoiceNoneAbove = function ChoiceNoneAbove(props) {
|
|
|
23314
23571
|
forwardedRef
|
|
23315
23572
|
} = props,
|
|
23316
23573
|
rest = _objectWithoutPropertiesLoose(props, _excluded$3);
|
|
23574
|
+
const {
|
|
23575
|
+
strings
|
|
23576
|
+
} = usePerseusI18n();
|
|
23317
23577
|
const choiceProps = _extends({}, rest, {
|
|
23318
23578
|
content: showContent ? content :
|
|
23319
23579
|
/*#__PURE__*/
|
|
@@ -23329,7 +23589,8 @@ const ChoiceNoneAbove = function ChoiceNoneAbove(props) {
|
|
|
23329
23589
|
// just to render a string
|
|
23330
23590
|
React.createElement(Renderer, {
|
|
23331
23591
|
key: "noneOfTheAboveRenderer",
|
|
23332
|
-
content:
|
|
23592
|
+
content: strings.noneOfTheAbove,
|
|
23593
|
+
strings: strings
|
|
23333
23594
|
})
|
|
23334
23595
|
});
|
|
23335
23596
|
return /*#__PURE__*/React.createElement(Choice$1, _extends({}, choiceProps, {
|
|
@@ -23349,16 +23610,16 @@ const {
|
|
|
23349
23610
|
|
|
23350
23611
|
// exported for tests
|
|
23351
23612
|
|
|
23352
|
-
function getInstructionsText(multipleSelect, countChoices, numCorrect) {
|
|
23613
|
+
function getInstructionsText(multipleSelect, countChoices, numCorrect, strings) {
|
|
23353
23614
|
if (multipleSelect) {
|
|
23354
23615
|
if (countChoices) {
|
|
23355
|
-
return
|
|
23356
|
-
numCorrect: numCorrect
|
|
23616
|
+
return strings.chooseNumAnswers({
|
|
23617
|
+
numCorrect: String(numCorrect)
|
|
23357
23618
|
});
|
|
23358
23619
|
}
|
|
23359
|
-
return
|
|
23620
|
+
return strings.chooseAllAnswers;
|
|
23360
23621
|
}
|
|
23361
|
-
return
|
|
23622
|
+
return strings.chooseOneAnswer;
|
|
23362
23623
|
}
|
|
23363
23624
|
const BaseRadio = function BaseRadio(props) {
|
|
23364
23625
|
const {
|
|
@@ -23373,6 +23634,9 @@ const BaseRadio = function BaseRadio(props) {
|
|
|
23373
23634
|
isLastUsedWidget,
|
|
23374
23635
|
registerFocusFunction
|
|
23375
23636
|
} = props;
|
|
23637
|
+
const {
|
|
23638
|
+
strings
|
|
23639
|
+
} = usePerseusI18n();
|
|
23376
23640
|
|
|
23377
23641
|
// useEffect doesn't have previous props
|
|
23378
23642
|
const prevReviewModeRubric = useRef();
|
|
@@ -23479,7 +23743,7 @@ const BaseRadio = function BaseRadio(props) {
|
|
|
23479
23743
|
const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
|
|
23480
23744
|
const className = classNames("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$t.radio, styles$t.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$t.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$t.radioContainerLastHighlighted));
|
|
23481
23745
|
const instructionsClassName = classNames("instructions", css(styles$t.instructions, isMobile && styles$t.instructionsMobile));
|
|
23482
|
-
const instructions = getInstructionsText(multipleSelect, countChoices, numCorrect);
|
|
23746
|
+
const instructions = getInstructionsText(multipleSelect, countChoices, numCorrect, strings);
|
|
23483
23747
|
const responsiveClassName = css(styles$t.responsiveFieldset);
|
|
23484
23748
|
const fieldset = /*#__PURE__*/React.createElement("fieldset", {
|
|
23485
23749
|
className: `perseus-widget-radio-fieldset ${responsiveClassName}`
|
|
@@ -23744,7 +24008,8 @@ class Radio$1 extends React.Component {
|
|
|
23744
24008
|
linterContext: _extends({}, this.props.linterContext, {
|
|
23745
24009
|
// @ts-expect-error - TS2322 - Type '{ blockHighlight: true; contentType: string; highlightLint: boolean; paths: readonly string[]; stack: readonly string[]; }' is not assignable to type 'LinterContextProps'.
|
|
23746
24010
|
blockHighlight: true
|
|
23747
|
-
})
|
|
24011
|
+
}),
|
|
24012
|
+
strings: this.context.strings
|
|
23748
24013
|
});
|
|
23749
24014
|
};
|
|
23750
24015
|
// When `BaseRadio`'s `onChange` handler is called, indicating a change in
|
|
@@ -23796,7 +24061,7 @@ class Radio$1 extends React.Component {
|
|
|
23796
24061
|
return Radio$1.getUserInputFromProps(this.props);
|
|
23797
24062
|
};
|
|
23798
24063
|
this.simpleValidate = rubric => {
|
|
23799
|
-
return Radio$1.validate(this.getUserInput(), rubric);
|
|
24064
|
+
return Radio$1.validate(this.getUserInput(), rubric, this.context.strings);
|
|
23800
24065
|
};
|
|
23801
24066
|
/**
|
|
23802
24067
|
* Turn on rationale display for the currently selected choices. Note that
|
|
@@ -23857,7 +24122,7 @@ class Radio$1 extends React.Component {
|
|
|
23857
24122
|
}
|
|
23858
24123
|
};
|
|
23859
24124
|
}
|
|
23860
|
-
static validate(userInput, rubric) {
|
|
24125
|
+
static validate(userInput, rubric, strings) {
|
|
23861
24126
|
const numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
23862
24127
|
return sum + (selected ? 1 : 0);
|
|
23863
24128
|
}, 0);
|
|
@@ -23870,7 +24135,7 @@ class Radio$1 extends React.Component {
|
|
|
23870
24135
|
if (userInput.numCorrect && userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
23871
24136
|
return {
|
|
23872
24137
|
type: "invalid",
|
|
23873
|
-
message:
|
|
24138
|
+
message: strings.chooseCorrectNum
|
|
23874
24139
|
};
|
|
23875
24140
|
// If NOTA and some other answer are checked, ...
|
|
23876
24141
|
}
|
|
@@ -23878,7 +24143,7 @@ class Radio$1 extends React.Component {
|
|
|
23878
24143
|
if (userInput.noneOfTheAboveSelected && numSelected > 1) {
|
|
23879
24144
|
return {
|
|
23880
24145
|
type: "invalid",
|
|
23881
|
-
message:
|
|
24146
|
+
message: strings.notNoneOfTheAbove
|
|
23882
24147
|
};
|
|
23883
24148
|
}
|
|
23884
24149
|
const correct = userInput.choicesSelected.every((selected, i) => {
|
|
@@ -23990,6 +24255,9 @@ class Radio$1 extends React.Component {
|
|
|
23990
24255
|
const {
|
|
23991
24256
|
choices
|
|
23992
24257
|
} = this.props;
|
|
24258
|
+
const {
|
|
24259
|
+
strings
|
|
24260
|
+
} = this.context;
|
|
23993
24261
|
let choiceStates;
|
|
23994
24262
|
if (this.props.static) {
|
|
23995
24263
|
choiceStates = choices.map(choice => ({
|
|
@@ -24041,11 +24309,7 @@ class Radio$1 extends React.Component {
|
|
|
24041
24309
|
}));
|
|
24042
24310
|
}
|
|
24043
24311
|
const choicesProp = choices.map((choice, i) => {
|
|
24044
|
-
const content = choice.isNoneOfTheAbove && !choice.content ?
|
|
24045
|
-
// we use i18n._ instead of $_ here because the content
|
|
24046
|
-
// sent to a renderer needs to be a string, not a react
|
|
24047
|
-
// node (/renderable/fragment).
|
|
24048
|
-
i18n._("None of the above") : choice.content;
|
|
24312
|
+
const content = choice.isNoneOfTheAbove && !choice.content ? strings.noneOfTheAbove : choice.content;
|
|
24049
24313
|
const {
|
|
24050
24314
|
selected,
|
|
24051
24315
|
crossedOut,
|
|
@@ -24097,6 +24361,7 @@ class Radio$1 extends React.Component {
|
|
|
24097
24361
|
});
|
|
24098
24362
|
}
|
|
24099
24363
|
}
|
|
24364
|
+
Radio$1.contextType = PerseusI18nContext;
|
|
24100
24365
|
Radio$1.defaultProps = {
|
|
24101
24366
|
choices: [],
|
|
24102
24367
|
multipleSelect: false,
|
|
@@ -24111,14 +24376,8 @@ const {
|
|
|
24111
24376
|
random
|
|
24112
24377
|
} = Util;
|
|
24113
24378
|
|
|
24114
|
-
// Represents choices that we automatically re-order if encountered.
|
|
24115
|
-
// Note: these are in the reversed (incorrect) order that we will swap, if
|
|
24116
|
-
// found.
|
|
24117
|
-
// Note 2: these are internationalized when compared later on.
|
|
24118
|
-
const ReversedChoices = [[i18n._("False"), i18n._("True")], [i18n._("No"), i18n._("Yes")]];
|
|
24119
|
-
|
|
24120
24379
|
// Transforms the choices for display.
|
|
24121
|
-
const _choiceTransform = (widgetOptions, problemNum) => {
|
|
24380
|
+
const _choiceTransform = (widgetOptions, strings, problemNum) => {
|
|
24122
24381
|
const _maybeRandomize = function _maybeRandomize(array) {
|
|
24123
24382
|
const randomSeed = problemNum === undefined ? random : problemNum;
|
|
24124
24383
|
// NOTE: `problemNum` will only be set when the radio widget is
|
|
@@ -24147,6 +24406,11 @@ const _choiceTransform = (widgetOptions, problemNum) => {
|
|
|
24147
24406
|
return newChoices;
|
|
24148
24407
|
};
|
|
24149
24408
|
const enforceOrdering = choices => {
|
|
24409
|
+
// Represents choices that we automatically re-order if encountered.
|
|
24410
|
+
// Note: these are in the reversed (incorrect) order that we will swap, if
|
|
24411
|
+
// found.
|
|
24412
|
+
// Note 2: these are internationalized when compared later on.
|
|
24413
|
+
const ReversedChoices = [[strings.false, strings.true], [strings.no, strings.yes]];
|
|
24150
24414
|
const content = choices.map(c => c.content);
|
|
24151
24415
|
if (ReversedChoices.some(reversed => _.isEqual(content, reversed))) {
|
|
24152
24416
|
return [choices[1], choices[0]];
|
|
@@ -24172,8 +24436,8 @@ const _choiceTransform = (widgetOptions, problemNum) => {
|
|
|
24172
24436
|
// 1) we randomize the order first
|
|
24173
24437
|
_maybeRandomize(choices)));
|
|
24174
24438
|
};
|
|
24175
|
-
const transform = (widgetOptions, problemNum) => {
|
|
24176
|
-
const choices = _choiceTransform(widgetOptions, problemNum);
|
|
24439
|
+
const transform = (widgetOptions, strings, problemNum) => {
|
|
24440
|
+
const choices = _choiceTransform(widgetOptions, strings, problemNum);
|
|
24177
24441
|
const numCorrect = _.reduce(widgetOptions.choices, function (memo, choice) {
|
|
24178
24442
|
return choice.correct ? memo + 1 : memo;
|
|
24179
24443
|
}, 0);
|
|
@@ -24259,7 +24523,7 @@ class Categorizer extends React.Component {
|
|
|
24259
24523
|
return Categorizer.getUserInputFromProps(this.props);
|
|
24260
24524
|
};
|
|
24261
24525
|
this.simpleValidate = rubric => {
|
|
24262
|
-
return Categorizer.validate(this.getUserInput(), rubric);
|
|
24526
|
+
return Categorizer.validate(this.getUserInput(), rubric, this.context.strings);
|
|
24263
24527
|
};
|
|
24264
24528
|
}
|
|
24265
24529
|
render() {
|
|
@@ -24288,7 +24552,8 @@ class Categorizer extends React.Component {
|
|
|
24288
24552
|
key: i
|
|
24289
24553
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
24290
24554
|
content: category,
|
|
24291
|
-
linterContext: this.props.linterContext
|
|
24555
|
+
linterContext: this.props.linterContext,
|
|
24556
|
+
strings: this.context.strings
|
|
24292
24557
|
}));
|
|
24293
24558
|
}))), /*#__PURE__*/React.createElement("tbody", null, indexedItems.map(indexedItem => {
|
|
24294
24559
|
const item = indexedItem[0];
|
|
@@ -24300,7 +24565,8 @@ class Categorizer extends React.Component {
|
|
|
24300
24565
|
// @ts-expect-error - TS2322 - Type 'string | number' is not assignable to type 'string | undefined'.
|
|
24301
24566
|
, {
|
|
24302
24567
|
content: item,
|
|
24303
|
-
linterContext: this.props.linterContext
|
|
24568
|
+
linterContext: this.props.linterContext,
|
|
24569
|
+
strings: this.context.strings
|
|
24304
24570
|
})), self.props.categories.map((catName, catNum) => {
|
|
24305
24571
|
const selected = self.props.values[itemNum] === catNum;
|
|
24306
24572
|
return /*#__PURE__*/React.createElement("td", {
|
|
@@ -24343,7 +24609,7 @@ class Categorizer extends React.Component {
|
|
|
24343
24609
|
this.change("values", values);
|
|
24344
24610
|
this.props.trackInteraction();
|
|
24345
24611
|
}
|
|
24346
|
-
static validate(userInput, rubric) {
|
|
24612
|
+
static validate(userInput, rubric, strings) {
|
|
24347
24613
|
let completed = true;
|
|
24348
24614
|
let allCorrect = true;
|
|
24349
24615
|
rubric.values.forEach((value, i) => {
|
|
@@ -24357,7 +24623,7 @@ class Categorizer extends React.Component {
|
|
|
24357
24623
|
if (!completed) {
|
|
24358
24624
|
return {
|
|
24359
24625
|
type: "invalid",
|
|
24360
|
-
message:
|
|
24626
|
+
message: strings.invalidSelection
|
|
24361
24627
|
};
|
|
24362
24628
|
}
|
|
24363
24629
|
return {
|
|
@@ -24373,6 +24639,7 @@ class Categorizer extends React.Component {
|
|
|
24373
24639
|
}
|
|
24374
24640
|
|
|
24375
24641
|
// TODO(benkomalo): inject page-margin into Perseus instead of hardcoding.
|
|
24642
|
+
Categorizer.contextType = PerseusI18nContext;
|
|
24376
24643
|
Categorizer.defaultProps = {
|
|
24377
24644
|
items: [],
|
|
24378
24645
|
categories: [],
|
|
@@ -24698,7 +24965,8 @@ class Definition extends React.Component {
|
|
|
24698
24965
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
24699
24966
|
apiOptions: this.props.apiOptions,
|
|
24700
24967
|
content: this.props.definition,
|
|
24701
|
-
widgets: this.props.widgets
|
|
24968
|
+
widgets: this.props.widgets,
|
|
24969
|
+
strings: this.context.strings
|
|
24702
24970
|
})),
|
|
24703
24971
|
opened: activeDefinitionId === this.props.widgetId,
|
|
24704
24972
|
onClose: () => setActiveDefinitionId(null),
|
|
@@ -24720,6 +24988,7 @@ class Definition extends React.Component {
|
|
|
24720
24988
|
}, this.props.togglePrompt))));
|
|
24721
24989
|
}
|
|
24722
24990
|
}
|
|
24991
|
+
Definition.contextType = PerseusI18nContext;
|
|
24723
24992
|
Definition.defaultProps = {
|
|
24724
24993
|
togglePrompt: "define me",
|
|
24725
24994
|
definition: "definition goes here"
|
|
@@ -24770,12 +25039,13 @@ class DeprecatedStandin extends React__default.Component {
|
|
|
24770
25039
|
paddingBottom: 8
|
|
24771
25040
|
}
|
|
24772
25041
|
}, /*#__PURE__*/React__default.createElement(Banner, {
|
|
24773
|
-
text:
|
|
25042
|
+
text: this.context.strings.deprecatedStandin,
|
|
24774
25043
|
kind: "info",
|
|
24775
25044
|
layout: "full-width"
|
|
24776
25045
|
}));
|
|
24777
25046
|
}
|
|
24778
25047
|
}
|
|
25048
|
+
DeprecatedStandin.contextType = PerseusI18nContext;
|
|
24779
25049
|
var DeprecatedStandin$1 = {
|
|
24780
25050
|
name: "deprecated-standin",
|
|
24781
25051
|
displayName: "Deprecated Standin",
|
|
@@ -24970,10 +25240,12 @@ class Explanation extends React.Component {
|
|
|
24970
25240
|
apiOptions: this.props.apiOptions,
|
|
24971
25241
|
content: this.props.explanation,
|
|
24972
25242
|
widgets: this.props.widgets,
|
|
24973
|
-
linterContext: this.props.linterContext
|
|
25243
|
+
linterContext: this.props.linterContext,
|
|
25244
|
+
strings: this.context.strings
|
|
24974
25245
|
})))));
|
|
24975
25246
|
}
|
|
24976
25247
|
}
|
|
25248
|
+
Explanation.contextType = PerseusI18nContext;
|
|
24977
25249
|
Explanation.defaultProps = {
|
|
24978
25250
|
showPrompt: "Explain",
|
|
24979
25251
|
hidePrompt: "Hide explanation",
|
|
@@ -25041,6 +25313,13 @@ class GradedGroupAnswerBar extends React.Component {
|
|
|
25041
25313
|
onCheckAnswer,
|
|
25042
25314
|
onNextQuestion
|
|
25043
25315
|
} = this.props;
|
|
25316
|
+
const {
|
|
25317
|
+
keepTrying,
|
|
25318
|
+
tryAgain,
|
|
25319
|
+
check,
|
|
25320
|
+
correctExcited,
|
|
25321
|
+
nextQuestion
|
|
25322
|
+
} = this.context.strings;
|
|
25044
25323
|
const answerBarStyle = _extends({}, styles$o.answerBar, {
|
|
25045
25324
|
backgroundColor: answerBarState === "CORRECT" ? color.offWhite : "white",
|
|
25046
25325
|
// Center the "Correct!" message only when there's no next question
|
|
@@ -25054,10 +25333,10 @@ class GradedGroupAnswerBar extends React.Component {
|
|
|
25054
25333
|
style: {
|
|
25055
25334
|
marginLeft: 8
|
|
25056
25335
|
}
|
|
25057
|
-
},
|
|
25336
|
+
}, keepTrying)) : /*#__PURE__*/React.createElement("span", null); // empty span keeps the button on the right side
|
|
25058
25337
|
|
|
25059
25338
|
if (answerBarState !== "CORRECT") {
|
|
25060
|
-
const buttonLabel = answerBarState === "INCORRECT" ?
|
|
25339
|
+
const buttonLabel = answerBarState === "INCORRECT" ? tryAgain : check;
|
|
25061
25340
|
return /*#__PURE__*/React.createElement("div", {
|
|
25062
25341
|
style: answerBarStyle
|
|
25063
25342
|
}, message, /*#__PURE__*/React.createElement(Button, {
|
|
@@ -25080,15 +25359,16 @@ class GradedGroupAnswerBar extends React.Component {
|
|
|
25080
25359
|
}
|
|
25081
25360
|
}))), /*#__PURE__*/React.createElement("span", {
|
|
25082
25361
|
role: "alert",
|
|
25083
|
-
"aria-label":
|
|
25362
|
+
"aria-label": correctExcited,
|
|
25084
25363
|
style: {
|
|
25085
25364
|
marginLeft: 8
|
|
25086
25365
|
}
|
|
25087
|
-
},
|
|
25366
|
+
}, correctExcited)), onNextQuestion && /*#__PURE__*/React.createElement(Button, {
|
|
25088
25367
|
onClick: onNextQuestion
|
|
25089
|
-
},
|
|
25368
|
+
}, nextQuestion));
|
|
25090
25369
|
}
|
|
25091
25370
|
}
|
|
25371
|
+
GradedGroupAnswerBar.contextType = PerseusI18nContext;
|
|
25092
25372
|
const fontSize = 17;
|
|
25093
25373
|
const styles$o = {
|
|
25094
25374
|
answerBar: {
|
|
@@ -25142,10 +25422,6 @@ const getNextState = (currentState, answerable) => {
|
|
|
25142
25422
|
}
|
|
25143
25423
|
};
|
|
25144
25424
|
|
|
25145
|
-
// Prepended to all invalid messages to make the widget messages a bit clearer
|
|
25146
|
-
const INVALID_MESSAGE_PREFIX = i18n._("We couldn't grade your answer.");
|
|
25147
|
-
const DEFAULT_INVALID_MESSAGE = i18n._("It looks like you left something blank or ") + i18n._("entered in an invalid answer.");
|
|
25148
|
-
|
|
25149
25425
|
// exports has no 'transform'
|
|
25150
25426
|
|
|
25151
25427
|
// A Graded Group is more or less a Group widget that displays a check
|
|
@@ -25197,8 +25473,13 @@ class GradedGroup extends React.Component {
|
|
|
25197
25473
|
// eslint-disable-next-line react/no-string-refs
|
|
25198
25474
|
// @ts-expect-error - TS2339 - Property 'score' does not exist on type 'ReactInstance'.
|
|
25199
25475
|
const score = this.refs.renderer.score();
|
|
25476
|
+
const {
|
|
25477
|
+
INVALID_MESSAGE_PREFIX,
|
|
25478
|
+
DEFAULT_INVALID_MESSAGE_1,
|
|
25479
|
+
DEFAULT_INVALID_MESSAGE_2
|
|
25480
|
+
} = this.context.strings;
|
|
25200
25481
|
const status = score.type === "points" ? score.total === score.earned ? GRADING_STATUSES.correct : GRADING_STATUSES.incorrect : GRADING_STATUSES.invalid;
|
|
25201
|
-
const message = score.type === "points" ? score.message || "" : score.message ? `${INVALID_MESSAGE_PREFIX} ${score.message}` : `${INVALID_MESSAGE_PREFIX} ${
|
|
25482
|
+
const message = score.type === "points" ? score.message || "" : score.message ? `${INVALID_MESSAGE_PREFIX} ${score.message}` : `${INVALID_MESSAGE_PREFIX} ${DEFAULT_INVALID_MESSAGE_1}${DEFAULT_INVALID_MESSAGE_2}`;
|
|
25202
25483
|
this.setState({
|
|
25203
25484
|
status: status,
|
|
25204
25485
|
message: message,
|
|
@@ -25263,8 +25544,7 @@ class GradedGroup extends React.Component {
|
|
|
25263
25544
|
color: "#526f03"
|
|
25264
25545
|
}
|
|
25265
25546
|
}));
|
|
25266
|
-
|
|
25267
|
-
gradeStatus = i18n._("Correct");
|
|
25547
|
+
gradeStatus = this.context.strings.correct;
|
|
25268
25548
|
} else if (this.state.status === GRADING_STATUSES.incorrect) {
|
|
25269
25549
|
// TODO(jeremy): update to a WB colour
|
|
25270
25550
|
// @ts-expect-error - TS2322 - Type 'Element' is not assignable to type 'null'.
|
|
@@ -25273,8 +25553,7 @@ class GradedGroup extends React.Component {
|
|
|
25273
25553
|
color: "#ff5454"
|
|
25274
25554
|
}
|
|
25275
25555
|
}));
|
|
25276
|
-
|
|
25277
|
-
gradeStatus = i18n._("Incorrect");
|
|
25556
|
+
gradeStatus = this.context.strings.incorrect;
|
|
25278
25557
|
}
|
|
25279
25558
|
const mobileClass = this.props.inGradedGroupSet ? css(styles$n.gradedGroupInSet) : css(styles$n.gradedGroup);
|
|
25280
25559
|
const classes = classNames({
|
|
@@ -25303,7 +25582,8 @@ class GradedGroup extends React.Component {
|
|
|
25303
25582
|
readOnly
|
|
25304
25583
|
}),
|
|
25305
25584
|
onInteractWithWidget: this._onInteractWithWidget,
|
|
25306
|
-
linterContext: this.props.linterContext
|
|
25585
|
+
linterContext: this.props.linterContext,
|
|
25586
|
+
strings: this.context.strings
|
|
25307
25587
|
})), !apiOptions.isMobile && icon && /*#__PURE__*/React.createElement("div", {
|
|
25308
25588
|
className: "group-icon"
|
|
25309
25589
|
}, icon), !apiOptions.isMobile && gradeStatus && /*#__PURE__*/React.createElement("div", {
|
|
@@ -25317,14 +25597,14 @@ class GradedGroup extends React.Component {
|
|
|
25317
25597
|
kind: "secondary",
|
|
25318
25598
|
disabled: this.props.apiOptions.readOnly,
|
|
25319
25599
|
onClick: this._checkAnswer
|
|
25320
|
-
},
|
|
25600
|
+
}, this.context.strings.check), !apiOptions.isMobile && isCorrect && this.props.onNextQuestion && /*#__PURE__*/React.createElement(Button, {
|
|
25321
25601
|
kind: "secondary",
|
|
25322
25602
|
disabled: this.props.apiOptions.readOnly,
|
|
25323
25603
|
onClick: this.props.onNextQuestion,
|
|
25324
25604
|
style: {
|
|
25325
25605
|
marginLeft: 5
|
|
25326
25606
|
}
|
|
25327
|
-
},
|
|
25607
|
+
}, this.context.strings.nextQuestion), this.props.hint && this.props.hint.content && (this.state.showHint ? /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("button", {
|
|
25328
25608
|
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'number | undefined'.
|
|
25329
25609
|
tabIndex: "0",
|
|
25330
25610
|
className: css(styles$n.explanationTitle),
|
|
@@ -25338,11 +25618,12 @@ class GradedGroup extends React.Component {
|
|
|
25338
25618
|
showHint: false
|
|
25339
25619
|
});
|
|
25340
25620
|
}
|
|
25341
|
-
},
|
|
25621
|
+
}, this.context.strings.hideExplanation), /*#__PURE__*/React.createElement(Renderer, _extends({}, this.props.hint, {
|
|
25342
25622
|
// eslint-disable-next-line react/no-string-refs
|
|
25343
25623
|
ref: "hints-renderer",
|
|
25344
25624
|
apiOptions: apiOptions,
|
|
25345
|
-
linterContext: this.props.linterContext
|
|
25625
|
+
linterContext: this.props.linterContext,
|
|
25626
|
+
strings: this.context.strings
|
|
25346
25627
|
}))) :
|
|
25347
25628
|
/*#__PURE__*/
|
|
25348
25629
|
// Not using Button here bc the styles won't work.
|
|
@@ -25360,7 +25641,7 @@ class GradedGroup extends React.Component {
|
|
|
25360
25641
|
});
|
|
25361
25642
|
},
|
|
25362
25643
|
className: css(styles$n.showHintLink)
|
|
25363
|
-
},
|
|
25644
|
+
}, this.context.strings.explain)), apiOptions.isMobile && answerBarState !== "HIDDEN" && /*#__PURE__*/React.createElement(GradedGroupAnswerBar, {
|
|
25364
25645
|
apiOptions: apiOptions,
|
|
25365
25646
|
answerBarState: answerBarState,
|
|
25366
25647
|
onCheckAnswer: this._checkAnswer,
|
|
@@ -25368,6 +25649,7 @@ class GradedGroup extends React.Component {
|
|
|
25368
25649
|
}));
|
|
25369
25650
|
}
|
|
25370
25651
|
}
|
|
25652
|
+
GradedGroup.contextType = PerseusI18nContext;
|
|
25371
25653
|
GradedGroup.defaultProps = {
|
|
25372
25654
|
title: "",
|
|
25373
25655
|
content: "",
|
|
@@ -25461,7 +25743,7 @@ class Indicators extends React.Component {
|
|
|
25461
25743
|
key: title
|
|
25462
25744
|
}, /*#__PURE__*/React.createElement(Clickable, {
|
|
25463
25745
|
role: "button",
|
|
25464
|
-
"aria-label":
|
|
25746
|
+
"aria-label": this.context.strings.skipToTitle({
|
|
25465
25747
|
title
|
|
25466
25748
|
}),
|
|
25467
25749
|
style: styles$m.indicatorButton,
|
|
@@ -25477,12 +25759,10 @@ class Indicators extends React.Component {
|
|
|
25477
25759
|
style: styles$m.indicatorDotActive
|
|
25478
25760
|
}, /*#__PURE__*/React.createElement("span", {
|
|
25479
25761
|
className: css(a11y.srOnly)
|
|
25480
|
-
},
|
|
25762
|
+
}, this.context.strings.current)))))));
|
|
25481
25763
|
}
|
|
25482
25764
|
}
|
|
25483
|
-
|
|
25484
|
-
// no transform
|
|
25485
|
-
|
|
25765
|
+
Indicators.contextType = PerseusI18nContext; // no transform
|
|
25486
25766
|
// TODO(jared): find a better name for this :) and for GradedGroup; the names
|
|
25487
25767
|
// are currently a little confusing.
|
|
25488
25768
|
class GradedGroupSet extends React.Component {
|
|
@@ -25558,7 +25838,7 @@ class GradedGroupSet extends React.Component {
|
|
|
25558
25838
|
}
|
|
25559
25839
|
const currentGroup = this.props.gradedGroups[this.state.currentGroup];
|
|
25560
25840
|
if (!currentGroup) {
|
|
25561
|
-
return /*#__PURE__*/React.createElement("span", null,
|
|
25841
|
+
return /*#__PURE__*/React.createElement("span", null, "No current group...");
|
|
25562
25842
|
}
|
|
25563
25843
|
const numGroups = this.props.gradedGroups.length;
|
|
25564
25844
|
const handleNextQuestion = this.state.currentGroup < numGroups - 1 ? this.handleNextQuestion : null;
|
|
@@ -26853,7 +27133,8 @@ class Group extends React.Component {
|
|
|
26853
27133
|
findExternalWidgets: this.props.findWidgets,
|
|
26854
27134
|
reviewMode: !!this.props.reviewModeRubric,
|
|
26855
27135
|
onInteractWithWidget: onInteractWithWidget,
|
|
26856
|
-
linterContext: this.props.linterContext
|
|
27136
|
+
linterContext: this.props.linterContext,
|
|
27137
|
+
strings: this.context.strings
|
|
26857
27138
|
}), this.props.icon &&
|
|
26858
27139
|
/*#__PURE__*/
|
|
26859
27140
|
// @ts-expect-error - TS2339 - Property 'icon' does not exist on type 'Readonly<Props> & Readonly<{ children?: ReactNode; }>'.
|
|
@@ -26862,6 +27143,7 @@ class Group extends React.Component {
|
|
|
26862
27143
|
}, this.props.icon));
|
|
26863
27144
|
}
|
|
26864
27145
|
}
|
|
27146
|
+
Group.contextType = PerseusI18nContext;
|
|
26865
27147
|
Group.defaultProps = {
|
|
26866
27148
|
content: "",
|
|
26867
27149
|
widgets: {},
|
|
@@ -27127,7 +27409,8 @@ class ImageWidget extends React.Component {
|
|
|
27127
27409
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
27128
27410
|
content: this.props.alt,
|
|
27129
27411
|
apiOptions: apiOptions,
|
|
27130
|
-
linterContext: this.props.linterContext
|
|
27412
|
+
linterContext: this.props.linterContext,
|
|
27413
|
+
strings: this.context.strings
|
|
27131
27414
|
}));
|
|
27132
27415
|
}
|
|
27133
27416
|
|
|
@@ -27187,7 +27470,8 @@ class ImageWidget extends React.Component {
|
|
|
27187
27470
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
27188
27471
|
content: _title + this.props.caption,
|
|
27189
27472
|
apiOptions: apiOptions,
|
|
27190
|
-
linterContext: this.props.linterContext
|
|
27473
|
+
linterContext: this.props.linterContext,
|
|
27474
|
+
strings: this.context.strings
|
|
27191
27475
|
})));
|
|
27192
27476
|
}
|
|
27193
27477
|
return /*#__PURE__*/React.createElement("figure", {
|
|
@@ -27205,7 +27489,8 @@ class ImageWidget extends React.Component {
|
|
|
27205
27489
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
27206
27490
|
content: this.props.title,
|
|
27207
27491
|
apiOptions: apiOptions,
|
|
27208
|
-
linterContext: this.props.linterContext
|
|
27492
|
+
linterContext: this.props.linterContext,
|
|
27493
|
+
strings: this.context.strings
|
|
27209
27494
|
}));
|
|
27210
27495
|
}
|
|
27211
27496
|
if (this.props.caption) {
|
|
@@ -27217,7 +27502,8 @@ class ImageWidget extends React.Component {
|
|
|
27217
27502
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
27218
27503
|
content: this.props.caption,
|
|
27219
27504
|
apiOptions: apiOptions,
|
|
27220
|
-
linterContext: this.props.linterContext
|
|
27505
|
+
linterContext: this.props.linterContext,
|
|
27506
|
+
strings: this.context.strings
|
|
27221
27507
|
}));
|
|
27222
27508
|
}
|
|
27223
27509
|
return /*#__PURE__*/React.createElement("figure", {
|
|
@@ -27236,6 +27522,7 @@ class ImageWidget extends React.Component {
|
|
|
27236
27522
|
};
|
|
27237
27523
|
}
|
|
27238
27524
|
}
|
|
27525
|
+
ImageWidget.contextType = PerseusI18nContext;
|
|
27239
27526
|
ImageWidget.defaultProps = {
|
|
27240
27527
|
alignment: DEFAULT_ALIGNMENT,
|
|
27241
27528
|
title: "",
|
|
@@ -28168,43 +28455,216 @@ Graph.defaultProps = {
|
|
|
28168
28455
|
isMobile: false
|
|
28169
28456
|
};
|
|
28170
28457
|
|
|
28171
|
-
|
|
28172
|
-
|
|
28173
|
-
|
|
28174
|
-
|
|
28175
|
-
|
|
28176
|
-
|
|
28177
|
-
|
|
28178
|
-
|
|
28179
|
-
|
|
28458
|
+
// TODO(FEI-4010): Remove `Perseus` prefix for all types here
|
|
28459
|
+
// TODO(FEI-4011): Use types generated by https://github.com/jaredly/generate-perseus-flowtypes
|
|
28460
|
+
|
|
28461
|
+
// Range is replaced within this file with Interval, but it is used elsewhere
|
|
28462
|
+
// and exported from the package, so we need to keep it around.
|
|
28463
|
+
// TODO(FEI-5054): Figure out how to get global .d.ts files working with monorepos
|
|
28464
|
+
const ItemExtras = [
|
|
28465
|
+
// The user might benefit from using a Scientific Calculator. Provided on Khan Academy when true
|
|
28466
|
+
"calculator",
|
|
28467
|
+
// The user might benefit from using a statistics Chi Squared Table like https://people.richland.edu/james/lecture/m170/tbl-chi.html
|
|
28468
|
+
"chi2Table",
|
|
28469
|
+
// The user might benefit from a monthly payments calculator. Provided on Khan Academy when true
|
|
28470
|
+
"financialCalculatorMonthlyPayment",
|
|
28471
|
+
// The user might benefit from a total amount calculator. Provided on Khan Academy when true
|
|
28472
|
+
"financialCalculatorTotalAmount",
|
|
28473
|
+
// The user might benefit from a time to pay off calculator. Provided on Khan Academy when true
|
|
28474
|
+
"financialCalculatorTimeToPayOff",
|
|
28475
|
+
// The user might benefit from using a Periodic Table of Elements. Provided on Khan Academy when true
|
|
28476
|
+
"periodicTable",
|
|
28477
|
+
// The user might benefit from using a Periodic Table of Elements with key. Provided on Khan Academy when true
|
|
28478
|
+
"periodicTableWithKey",
|
|
28479
|
+
// The user might benefit from using a statistics T Table like https://www.statisticshowto.com/tables/t-distribution-table/
|
|
28480
|
+
"tTable",
|
|
28481
|
+
// The user might benefit from using a statistics Z Table like https://www.ztable.net/
|
|
28482
|
+
"zTable"];
|
|
28483
|
+
|
|
28484
|
+
// prettier-ignore
|
|
28485
|
+
|
|
28486
|
+
// prettier-ignore
|
|
28487
|
+
|
|
28488
|
+
// prettier-ignore
|
|
28489
|
+
|
|
28490
|
+
// prettier-ignore
|
|
28491
|
+
|
|
28492
|
+
// prettier-ignore
|
|
28493
|
+
|
|
28494
|
+
// prettier-ignore
|
|
28495
|
+
|
|
28496
|
+
// prettier-ignore
|
|
28497
|
+
|
|
28498
|
+
// prettier-ignore
|
|
28499
|
+
|
|
28500
|
+
// prettier-ignore
|
|
28501
|
+
|
|
28502
|
+
// prettier-ignore
|
|
28503
|
+
|
|
28504
|
+
// prettier-ignore
|
|
28505
|
+
|
|
28506
|
+
// prettier-ignore
|
|
28507
|
+
|
|
28508
|
+
// prettier-ignore
|
|
28509
|
+
|
|
28510
|
+
// prettier-ignore
|
|
28511
|
+
|
|
28512
|
+
// prettier-ignore
|
|
28513
|
+
|
|
28514
|
+
// prettier-ignore
|
|
28515
|
+
|
|
28516
|
+
// prettier-ignore
|
|
28517
|
+
|
|
28518
|
+
// prettier-ignore
|
|
28519
|
+
|
|
28520
|
+
// prettier-ignore
|
|
28521
|
+
|
|
28522
|
+
// prettier-ignore
|
|
28523
|
+
|
|
28524
|
+
// prettier-ignore
|
|
28525
|
+
|
|
28526
|
+
// prettier-ignore
|
|
28527
|
+
|
|
28528
|
+
// prettier-ignore
|
|
28529
|
+
|
|
28530
|
+
// prettier-ignore
|
|
28531
|
+
|
|
28532
|
+
// prettier-ignore
|
|
28533
|
+
|
|
28534
|
+
// prettier-ignore
|
|
28535
|
+
|
|
28536
|
+
// prettier-ignore
|
|
28537
|
+
|
|
28538
|
+
// prettier-ignore
|
|
28539
|
+
|
|
28540
|
+
// prettier-ignore
|
|
28541
|
+
|
|
28542
|
+
// prettier-ignore
|
|
28543
|
+
|
|
28544
|
+
// prettier-ignore
|
|
28545
|
+
|
|
28546
|
+
// prettier-ignore
|
|
28547
|
+
|
|
28548
|
+
// prettier-ignore
|
|
28549
|
+
|
|
28550
|
+
// prettier-ignore
|
|
28551
|
+
|
|
28552
|
+
// prettier-ignore
|
|
28553
|
+
|
|
28554
|
+
// prettier-ignore
|
|
28555
|
+
|
|
28556
|
+
//prettier-ignore
|
|
28557
|
+
|
|
28558
|
+
// A background image applied to various widgets.
|
|
28559
|
+
|
|
28560
|
+
const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
|
|
28561
|
+
const lockedFigureColors = {
|
|
28562
|
+
blue: "#3D7586",
|
|
28563
|
+
green: "#447A53",
|
|
28564
|
+
gray: "#5D5F66",
|
|
28565
|
+
grayH: "#3B3D45",
|
|
28566
|
+
grayI: "#21242C",
|
|
28567
|
+
purple: "#594094",
|
|
28568
|
+
purpleD: "#8351E8",
|
|
28569
|
+
pink: "#B25071",
|
|
28570
|
+
orange: "#946700",
|
|
28571
|
+
red: "#D92916"
|
|
28572
|
+
};
|
|
28573
|
+
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
28574
|
+
|
|
28575
|
+
const LockedLine = props => {
|
|
28576
|
+
const {
|
|
28577
|
+
color: color$1,
|
|
28578
|
+
lineStyle,
|
|
28579
|
+
kind,
|
|
28580
|
+
points,
|
|
28581
|
+
showStartPoint,
|
|
28582
|
+
showEndPoint
|
|
28583
|
+
} = props;
|
|
28584
|
+
const [point1, point2] = points;
|
|
28585
|
+
let line;
|
|
28586
|
+
if (kind === "line" || kind === "segment") {
|
|
28587
|
+
const LineType = kind === "segment" ? Line$3.Segment : Line$3.ThroughPoints;
|
|
28588
|
+
line = /*#__PURE__*/React.createElement(LineType, {
|
|
28589
|
+
point1: point1.coord,
|
|
28590
|
+
point2: point2.coord,
|
|
28591
|
+
color: lockedFigureColors[color$1],
|
|
28592
|
+
style: lineStyle
|
|
28593
|
+
});
|
|
28594
|
+
} else {
|
|
28595
|
+
// TODO(LEMS-1928): Implement rays
|
|
28596
|
+
return null;
|
|
28597
|
+
}
|
|
28598
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
28599
|
+
className: "locked-line"
|
|
28600
|
+
}, line, showStartPoint && /*#__PURE__*/React.createElement(Point$2, {
|
|
28601
|
+
x: point1.coord[0],
|
|
28602
|
+
y: point1.coord[1],
|
|
28603
|
+
svgCircleProps: {
|
|
28604
|
+
style: {
|
|
28605
|
+
fill: point1.filled ? lockedFigureColors[point1.color] : color.white,
|
|
28606
|
+
stroke: lockedFigureColors[point1.color],
|
|
28607
|
+
strokeWidth: spacing.xxxxSmall_2
|
|
28608
|
+
}
|
|
28609
|
+
}
|
|
28610
|
+
}), showEndPoint && /*#__PURE__*/React.createElement(Point$2, {
|
|
28611
|
+
x: point2.coord[0],
|
|
28612
|
+
y: point2.coord[1],
|
|
28613
|
+
svgCircleProps: {
|
|
28614
|
+
style: {
|
|
28615
|
+
fill: point2.filled ? lockedFigureColors[point2.color] : color.white,
|
|
28616
|
+
stroke: lockedFigureColors[point2.color],
|
|
28617
|
+
strokeWidth: spacing.xxxxSmall_2
|
|
28618
|
+
}
|
|
28619
|
+
}
|
|
28620
|
+
}));
|
|
28621
|
+
};
|
|
28622
|
+
|
|
28623
|
+
const LockedPoint = props => {
|
|
28624
|
+
const {
|
|
28625
|
+
color: color$1,
|
|
28626
|
+
coord,
|
|
28627
|
+
filled
|
|
28628
|
+
} = props;
|
|
28629
|
+
const [x, y] = coord;
|
|
28630
|
+
return /*#__PURE__*/React.createElement(Point$2, {
|
|
28631
|
+
x: x,
|
|
28632
|
+
y: y,
|
|
28633
|
+
svgCircleProps: {
|
|
28634
|
+
style: {
|
|
28635
|
+
fill: filled ? lockedFigureColors[color$1] : color.white,
|
|
28636
|
+
stroke: lockedFigureColors[color$1],
|
|
28637
|
+
strokeWidth: spacing.xxxxSmall_2
|
|
28638
|
+
}
|
|
28639
|
+
}
|
|
28180
28640
|
});
|
|
28181
|
-
}
|
|
28641
|
+
};
|
|
28642
|
+
|
|
28182
28643
|
const GraphLockedLayer = props => {
|
|
28183
28644
|
const {
|
|
28184
28645
|
lockedFigures
|
|
28185
28646
|
} = props;
|
|
28186
28647
|
return /*#__PURE__*/React.createElement(React.Fragment, null, lockedFigures.map((figure, index) => {
|
|
28648
|
+
let Figure;
|
|
28187
28649
|
switch (figure.type) {
|
|
28188
28650
|
case "point":
|
|
28189
|
-
|
|
28190
|
-
|
|
28191
|
-
|
|
28192
|
-
|
|
28193
|
-
|
|
28194
|
-
|
|
28195
|
-
|
|
28196
|
-
|
|
28197
|
-
|
|
28651
|
+
Figure = LockedPoint;
|
|
28652
|
+
break;
|
|
28653
|
+
case "line":
|
|
28654
|
+
Figure = LockedLine;
|
|
28655
|
+
break;
|
|
28656
|
+
default:
|
|
28657
|
+
/**
|
|
28658
|
+
* Devlopment-time future-proofing: This should
|
|
28659
|
+
* fail during type-checking if we add a new locked
|
|
28660
|
+
* shape type and forget to handle it in any other
|
|
28661
|
+
* switch case here.
|
|
28662
|
+
*/
|
|
28663
|
+
throw new UnreachableCaseError(figure);
|
|
28198
28664
|
}
|
|
28199
|
-
|
|
28200
|
-
|
|
28201
|
-
|
|
28202
|
-
* fail during type-checking if we add a new locked
|
|
28203
|
-
* shape type and forget to handle it in any other
|
|
28204
|
-
* switch case here.
|
|
28205
|
-
*/
|
|
28206
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
28207
|
-
throw new UnreachableCaseError(figure.type);
|
|
28665
|
+
return /*#__PURE__*/React.createElement(Figure, _extends({
|
|
28666
|
+
key: `${figure.type}-${index}`
|
|
28667
|
+
}, figure));
|
|
28208
28668
|
}));
|
|
28209
28669
|
};
|
|
28210
28670
|
|
|
@@ -28353,7 +28813,7 @@ function SVGLine(props) {
|
|
|
28353
28813
|
y2: end[1],
|
|
28354
28814
|
style: style,
|
|
28355
28815
|
className: `${className}`,
|
|
28356
|
-
"data-
|
|
28816
|
+
"data-testid": testId
|
|
28357
28817
|
});
|
|
28358
28818
|
}
|
|
28359
28819
|
|
|
@@ -28482,7 +28942,7 @@ const MovableLine = props => {
|
|
|
28482
28942
|
ref: line,
|
|
28483
28943
|
tabIndex: 0,
|
|
28484
28944
|
className: "movable-line",
|
|
28485
|
-
"data-
|
|
28945
|
+
"data-testid": "movable-line",
|
|
28486
28946
|
style: {
|
|
28487
28947
|
cursor: dragging ? "grabbing" : "grab"
|
|
28488
28948
|
}
|
|
@@ -28526,7 +28986,8 @@ const MovableLine = props => {
|
|
|
28526
28986
|
const defaultGraphConfig = {
|
|
28527
28987
|
range: [[0, 1], [0, 1]],
|
|
28528
28988
|
snapStep: [1, 1],
|
|
28529
|
-
markings: "none"
|
|
28989
|
+
markings: "none",
|
|
28990
|
+
showTooltips: false
|
|
28530
28991
|
};
|
|
28531
28992
|
const GraphConfigContext = /*#__PURE__*/createContext(defaultGraphConfig);
|
|
28532
28993
|
function useGraphConfig() {
|
|
@@ -28537,10 +28998,12 @@ function useGraphConfig() {
|
|
|
28537
28998
|
// graph.
|
|
28538
28999
|
const hitboxSizePx = 48;
|
|
28539
29000
|
const StyledMovablePoint = props => {
|
|
29001
|
+
var _Object$entries$find$, _Object$entries$find;
|
|
28540
29002
|
const {
|
|
28541
29003
|
range,
|
|
28542
29004
|
snapStep,
|
|
28543
|
-
markings
|
|
29005
|
+
markings,
|
|
29006
|
+
showTooltips
|
|
28544
29007
|
} = useGraphConfig();
|
|
28545
29008
|
const hitboxRef = useRef(null);
|
|
28546
29009
|
const {
|
|
@@ -28548,6 +29011,10 @@ const StyledMovablePoint = props => {
|
|
|
28548
29011
|
onMove,
|
|
28549
29012
|
color: color$1 = color.blue
|
|
28550
29013
|
} = props;
|
|
29014
|
+
|
|
29015
|
+
// WB Tooltip requires a color name for the background color.
|
|
29016
|
+
// Since the color in props is a hex value, a reverse lookup is needed.
|
|
29017
|
+
const wbColorName = (_Object$entries$find$ = (_Object$entries$find = Object.entries(color).find(([_, value]) => value === color$1)) == null ? void 0 : _Object$entries$find[0]) != null ? _Object$entries$find$ : "blue";
|
|
28551
29018
|
const {
|
|
28552
29019
|
dragging
|
|
28553
29020
|
} = useMovable({
|
|
@@ -28565,26 +29032,27 @@ const StyledMovablePoint = props => {
|
|
|
28565
29032
|
const [[_, horizontalStartY]] = useTransform([0, yMin]);
|
|
28566
29033
|
const [[__, horizontalEndY]] = useTransform([0, yMax]);
|
|
28567
29034
|
const showHairlines = dragging && markings !== "none";
|
|
28568
|
-
|
|
29035
|
+
const hairlines = /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("line", {
|
|
28569
29036
|
x1: verticalStartX,
|
|
28570
29037
|
y1: y,
|
|
28571
29038
|
x2: verticalEndX,
|
|
28572
29039
|
y2: y,
|
|
28573
|
-
stroke: color
|
|
29040
|
+
stroke: color$1
|
|
28574
29041
|
}), /*#__PURE__*/React.createElement("line", {
|
|
28575
29042
|
x1: x,
|
|
28576
29043
|
y1: horizontalStartY,
|
|
28577
29044
|
x2: x,
|
|
28578
29045
|
y2: horizontalEndY,
|
|
28579
|
-
stroke: color
|
|
28580
|
-
}))
|
|
29046
|
+
stroke: color$1
|
|
29047
|
+
}));
|
|
29048
|
+
const svgForPoint = /*#__PURE__*/React.createElement("g", {
|
|
28581
29049
|
ref: hitboxRef,
|
|
28582
29050
|
className: pointClasses,
|
|
28583
29051
|
tabIndex: 0,
|
|
28584
29052
|
style: {
|
|
28585
29053
|
"--movable-point-color": color$1
|
|
28586
29054
|
},
|
|
28587
|
-
"data-
|
|
29055
|
+
"data-testid": "movable-point"
|
|
28588
29056
|
}, /*#__PURE__*/React.createElement("circle", {
|
|
28589
29057
|
className: "movable-point-hitbox",
|
|
28590
29058
|
r: hitboxSizePx / 2,
|
|
@@ -28609,8 +29077,16 @@ const StyledMovablePoint = props => {
|
|
|
28609
29077
|
style: {
|
|
28610
29078
|
fill: color$1
|
|
28611
29079
|
},
|
|
28612
|
-
"data-
|
|
28613
|
-
}))
|
|
29080
|
+
"data-testid": "movable-point__center"
|
|
29081
|
+
}));
|
|
29082
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, showHairlines && hairlines, showTooltips ? /*#__PURE__*/React.createElement(Tooltip$1, {
|
|
29083
|
+
autoUpdate: true,
|
|
29084
|
+
backgroundColor: wbColorName,
|
|
29085
|
+
content: `(${point[0]}, ${point[1]})`,
|
|
29086
|
+
contentStyle: {
|
|
29087
|
+
color: "white"
|
|
29088
|
+
}
|
|
29089
|
+
}, svgForPoint) : svgForPoint);
|
|
28614
29090
|
};
|
|
28615
29091
|
|
|
28616
29092
|
const SegmentGraph = props => {
|
|
@@ -28628,8 +29104,7 @@ const SegmentGraph = props => {
|
|
|
28628
29104
|
},
|
|
28629
29105
|
onMovePoint: (endpointIndex, destination) => {
|
|
28630
29106
|
dispatch(moveControlPoint(endpointIndex, destination, i));
|
|
28631
|
-
}
|
|
28632
|
-
"data-testid": "segment" + i
|
|
29107
|
+
}
|
|
28633
29108
|
})));
|
|
28634
29109
|
};
|
|
28635
29110
|
const SegmentView = props => {
|
|
@@ -28659,8 +29134,7 @@ const LinearGraph = props => {
|
|
|
28659
29134
|
dispatch
|
|
28660
29135
|
} = props;
|
|
28661
29136
|
const {
|
|
28662
|
-
coords: lines
|
|
28663
|
-
type
|
|
29137
|
+
coords: lines
|
|
28664
29138
|
} = props.graphState;
|
|
28665
29139
|
const colors = ["var(--movable-line-stroke-color)", "var(--mafs-violet)"];
|
|
28666
29140
|
return /*#__PURE__*/React.createElement(React.Fragment, null, lines == null ? void 0 : lines.map((line, i) => /*#__PURE__*/React.createElement(LineView, {
|
|
@@ -28669,10 +29143,7 @@ const LinearGraph = props => {
|
|
|
28669
29143
|
onMoveLine: delta => {
|
|
28670
29144
|
dispatch(moveLine(i, delta));
|
|
28671
29145
|
},
|
|
28672
|
-
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i))
|
|
28673
|
-
// "linear" or "linear-system" + index
|
|
28674
|
-
,
|
|
28675
|
-
"data-testid": type + i,
|
|
29146
|
+
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
|
|
28676
29147
|
stroke: colors[i]
|
|
28677
29148
|
})));
|
|
28678
29149
|
};
|
|
@@ -28912,7 +29383,6 @@ const PolygonGraph = props => {
|
|
|
28912
29383
|
} = props;
|
|
28913
29384
|
const {
|
|
28914
29385
|
coords,
|
|
28915
|
-
type,
|
|
28916
29386
|
showAngles,
|
|
28917
29387
|
showSides,
|
|
28918
29388
|
range,
|
|
@@ -28983,8 +29453,7 @@ const PolygonGraph = props => {
|
|
|
28983
29453
|
}), points.map((point, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
28984
29454
|
key: "point-" + i,
|
|
28985
29455
|
point: point,
|
|
28986
|
-
onMove: destination => dispatch(movePoint(i, destination))
|
|
28987
|
-
"data-testid": type + i
|
|
29456
|
+
onMove: destination => dispatch(movePoint(i, destination))
|
|
28988
29457
|
})));
|
|
28989
29458
|
};
|
|
28990
29459
|
|
|
@@ -29543,7 +30012,7 @@ class NumberInput extends React.Component {
|
|
|
29543
30012
|
const placeholder = this.props.placeholder;
|
|
29544
30013
|
return _.isFinite(placeholder) ? +placeholder : null;
|
|
29545
30014
|
}
|
|
29546
|
-
const result = firstNumericalParse(value);
|
|
30015
|
+
const result = firstNumericalParse(value, this.context.strings);
|
|
29547
30016
|
return _.isFinite(result) ? result : this.props.value;
|
|
29548
30017
|
};
|
|
29549
30018
|
/* Set text input focus to this input */
|
|
@@ -29573,7 +30042,7 @@ class NumberInput extends React.Component {
|
|
|
29573
30042
|
if (value == null) {
|
|
29574
30043
|
return true;
|
|
29575
30044
|
}
|
|
29576
|
-
const val = firstNumericalParse(value);
|
|
30045
|
+
const val = firstNumericalParse(value, this.context.strings);
|
|
29577
30046
|
const checkValidity = this.props.checkValidity;
|
|
29578
30047
|
return _.isFinite(val) && checkValidity(val);
|
|
29579
30048
|
};
|
|
@@ -29669,6 +30138,7 @@ class NumberInput extends React.Component {
|
|
|
29669
30138
|
}
|
|
29670
30139
|
}
|
|
29671
30140
|
}
|
|
30141
|
+
NumberInput.contextType = PerseusI18nContext;
|
|
29672
30142
|
NumberInput.propTypes = {
|
|
29673
30143
|
value: PropTypes.number,
|
|
29674
30144
|
format: PropTypes.string,
|
|
@@ -30404,7 +30874,8 @@ const MafsGraph = props => {
|
|
|
30404
30874
|
value: {
|
|
30405
30875
|
range: state.range,
|
|
30406
30876
|
snapStep: state.snapStep,
|
|
30407
|
-
markings: props.markings
|
|
30877
|
+
markings: props.markings,
|
|
30878
|
+
showTooltips: !!props.showTooltips
|
|
30408
30879
|
}
|
|
30409
30880
|
}, /*#__PURE__*/React.createElement(View, {
|
|
30410
30881
|
style: {
|
|
@@ -31666,9 +32137,9 @@ class LegacyInteractiveGraph extends React.Component {
|
|
|
31666
32137
|
// isClickToAddPoints() only applies to points and polygons
|
|
31667
32138
|
if (this.isClickToAddPoints() && this.state.shouldShowInstructions) {
|
|
31668
32139
|
if (this.props.graph.type === "point") {
|
|
31669
|
-
instructions =
|
|
32140
|
+
instructions = this.context.strings.addPoints;
|
|
31670
32141
|
} else if (this.props.graph.type === "polygon") {
|
|
31671
|
-
instructions =
|
|
32142
|
+
instructions = this.context.strings.addVertices;
|
|
31672
32143
|
}
|
|
31673
32144
|
} else {
|
|
31674
32145
|
instructions = undefined;
|
|
@@ -31708,6 +32179,7 @@ class LegacyInteractiveGraph extends React.Component {
|
|
|
31708
32179
|
}));
|
|
31709
32180
|
}
|
|
31710
32181
|
}
|
|
32182
|
+
LegacyInteractiveGraph.contextType = PerseusI18nContext;
|
|
31711
32183
|
LegacyInteractiveGraph.defaultProps = {
|
|
31712
32184
|
labels: ["x", "y"],
|
|
31713
32185
|
range: [[-10, 10], [-10, 10]],
|
|
@@ -31754,7 +32226,8 @@ class InteractiveGraph extends React.Component {
|
|
|
31754
32226
|
ref: this.mafsRef,
|
|
31755
32227
|
gridStep: gridStep,
|
|
31756
32228
|
snapStep: snapStep,
|
|
31757
|
-
box: box
|
|
32229
|
+
box: box,
|
|
32230
|
+
showTooltips: !!this.props.showTooltips
|
|
31758
32231
|
}));
|
|
31759
32232
|
}
|
|
31760
32233
|
return /*#__PURE__*/React.createElement(LegacyInteractiveGraph, _extends({
|
|
@@ -33296,6 +33769,9 @@ var globalStyles = /*#__PURE__*/Object.freeze({
|
|
|
33296
33769
|
});
|
|
33297
33770
|
|
|
33298
33771
|
const AnswerChoices = props => {
|
|
33772
|
+
const {
|
|
33773
|
+
strings
|
|
33774
|
+
} = usePerseusI18n();
|
|
33299
33775
|
const onAnswerChange = selected => {
|
|
33300
33776
|
const {
|
|
33301
33777
|
choices,
|
|
@@ -33313,6 +33789,7 @@ const AnswerChoices = props => {
|
|
|
33313
33789
|
value: content,
|
|
33314
33790
|
label: /*#__PURE__*/React.createElement(Renderer, {
|
|
33315
33791
|
content: content,
|
|
33792
|
+
strings: strings,
|
|
33316
33793
|
inline: true
|
|
33317
33794
|
})
|
|
33318
33795
|
}));
|
|
@@ -33342,14 +33819,13 @@ const AnswerChoices = props => {
|
|
|
33342
33819
|
}));
|
|
33343
33820
|
};
|
|
33344
33821
|
|
|
33345
|
-
const strings = {
|
|
33346
|
-
hideAnswersToggleLabel: i18n._("Hide answer choices")
|
|
33347
|
-
};
|
|
33348
|
-
|
|
33349
33822
|
const HideAnswersToggle = props => {
|
|
33350
33823
|
const idFactory = useUniqueIdWithMock();
|
|
33351
33824
|
const switchId = idFactory.get("hide-answers-toggle");
|
|
33352
33825
|
const labelId = idFactory.get("hide-answers-label");
|
|
33826
|
+
const {
|
|
33827
|
+
strings
|
|
33828
|
+
} = usePerseusI18n();
|
|
33353
33829
|
return /*#__PURE__*/React.createElement(View, {
|
|
33354
33830
|
style: styles$g.switchWrapper
|
|
33355
33831
|
}, /*#__PURE__*/React.createElement(Switch, {
|
|
@@ -33390,9 +33866,12 @@ const AnswerPill = props => {
|
|
|
33390
33866
|
hovered
|
|
33391
33867
|
} = props;
|
|
33392
33868
|
const idFactory = useUniqueIdWithMock();
|
|
33393
|
-
const
|
|
33394
|
-
|
|
33395
|
-
|
|
33869
|
+
const {
|
|
33870
|
+
strings
|
|
33871
|
+
} = usePerseusI18n();
|
|
33872
|
+
const answerString = selectedAnswers.length > 1 ? strings.answers({
|
|
33873
|
+
num: selectedAnswers.length
|
|
33874
|
+
}) : selectedAnswers[0];
|
|
33396
33875
|
|
|
33397
33876
|
// It should no longer be possible to interact with an answer after it
|
|
33398
33877
|
// has been labeled (answered) correctly.
|
|
@@ -33419,6 +33898,7 @@ const AnswerPill = props => {
|
|
|
33419
33898
|
style: [style, popperStyle, styles$f.pill, correct && styles$f.correct, incorrect && styles$f.incorrect, (focused || hovered) && BringToFront]
|
|
33420
33899
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
33421
33900
|
content: answerString,
|
|
33901
|
+
strings: strings,
|
|
33422
33902
|
inline: true
|
|
33423
33903
|
})));
|
|
33424
33904
|
};
|
|
@@ -33513,10 +33993,8 @@ class Marker extends React.Component {
|
|
|
33513
33993
|
const markerDisabled = showCorrectness === "correct";
|
|
33514
33994
|
const active = hovered || focused;
|
|
33515
33995
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, {
|
|
33516
|
-
style: [styles$e.marker, active && !markerDisabled && styles$e.markerActive]
|
|
33517
|
-
|
|
33518
|
-
,
|
|
33519
|
-
"aria-label": markerDisabled ? i18n._("Correct!") : label
|
|
33996
|
+
style: [styles$e.marker, active && !markerDisabled && styles$e.markerActive],
|
|
33997
|
+
"aria-label": markerDisabled ? this.context.strings.correctExcited : label
|
|
33520
33998
|
}, this.renderIcon()), !!selected && showAnswer && /*#__PURE__*/React.createElement(AnswerPill, {
|
|
33521
33999
|
selectedAnswers: selected,
|
|
33522
34000
|
showCorrectness: showCorrectness,
|
|
@@ -33528,6 +34006,7 @@ class Marker extends React.Component {
|
|
|
33528
34006
|
}));
|
|
33529
34007
|
}
|
|
33530
34008
|
}
|
|
34009
|
+
Marker.contextType = PerseusI18nContext;
|
|
33531
34010
|
Marker.defaultProps = {
|
|
33532
34011
|
selected: []
|
|
33533
34012
|
};
|
|
@@ -34099,8 +34578,11 @@ class LabelImage extends React.Component {
|
|
|
34099
34578
|
multipleAnswers,
|
|
34100
34579
|
hideChoicesFromInstructions: hideChoices
|
|
34101
34580
|
} = this.props;
|
|
34102
|
-
const
|
|
34103
|
-
|
|
34581
|
+
const {
|
|
34582
|
+
strings
|
|
34583
|
+
} = this.context;
|
|
34584
|
+
const promptString = isMobile ? multipleAnswers ? strings.tapMultiple : strings.tapSingle : multipleAnswers ? strings.clickMultiple : strings.clickSingle;
|
|
34585
|
+
const choicesString = strings.choices;
|
|
34104
34586
|
return /*#__PURE__*/React.createElement("div", {
|
|
34105
34587
|
className: classNames("perseus-label-image-widget-instructions", css(styles$d.instructions))
|
|
34106
34588
|
}, /*#__PURE__*/React.createElement("div", {
|
|
@@ -34111,7 +34593,8 @@ class LabelImage extends React.Component {
|
|
|
34111
34593
|
className: css(styles$d.instructionsChoice),
|
|
34112
34594
|
key: index
|
|
34113
34595
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
34114
|
-
content: choice
|
|
34596
|
+
content: choice,
|
|
34597
|
+
strings: strings
|
|
34115
34598
|
})))));
|
|
34116
34599
|
}
|
|
34117
34600
|
render() {
|
|
@@ -34162,6 +34645,7 @@ class LabelImage extends React.Component {
|
|
|
34162
34645
|
}));
|
|
34163
34646
|
}
|
|
34164
34647
|
}
|
|
34648
|
+
LabelImage.contextType = PerseusI18nContext;
|
|
34165
34649
|
const styles$d = StyleSheet.create({
|
|
34166
34650
|
instructions: {
|
|
34167
34651
|
paddingBottom: 16
|
|
@@ -34475,7 +34959,8 @@ class Draggable extends React.Component {
|
|
|
34475
34959
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
34476
34960
|
content: this.props.content,
|
|
34477
34961
|
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "draggable"),
|
|
34478
|
-
onRender: this.props.onRender
|
|
34962
|
+
onRender: this.props.onRender,
|
|
34963
|
+
strings: this.context.strings
|
|
34479
34964
|
}));
|
|
34480
34965
|
}
|
|
34481
34966
|
componentDidUpdate(prevProps) {
|
|
@@ -34500,6 +34985,7 @@ class Draggable extends React.Component {
|
|
|
34500
34985
|
}
|
|
34501
34986
|
}
|
|
34502
34987
|
}
|
|
34988
|
+
Draggable.contextType = PerseusI18nContext;
|
|
34503
34989
|
Draggable.defaultProps = {
|
|
34504
34990
|
includePadding: true,
|
|
34505
34991
|
type: ItemState.STATIC,
|
|
@@ -35060,12 +35546,14 @@ class Matcher extends React.Component {
|
|
|
35060
35546
|
className: css(styles$b.column, styles$b.columnLabel)
|
|
35061
35547
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
35062
35548
|
content: this.props.labels[0] || "...",
|
|
35063
|
-
linterContext: this.props.linterContext
|
|
35549
|
+
linterContext: this.props.linterContext,
|
|
35550
|
+
strings: this.context.strings
|
|
35064
35551
|
})), /*#__PURE__*/React.createElement("th", {
|
|
35065
35552
|
className: css(styles$b.column, styles$b.columnRight, styles$b.columnLabel)
|
|
35066
35553
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
35067
35554
|
content: this.props.labels[1] || "...",
|
|
35068
|
-
linterContext: this.props.linterContext
|
|
35555
|
+
linterContext: this.props.linterContext,
|
|
35556
|
+
strings: this.context.strings
|
|
35069
35557
|
}))), /*#__PURE__*/React.createElement("tr", {
|
|
35070
35558
|
className: css(styles$b.row)
|
|
35071
35559
|
}, /*#__PURE__*/React.createElement("td", {
|
|
@@ -35102,6 +35590,7 @@ class Matcher extends React.Component {
|
|
|
35102
35590
|
})))));
|
|
35103
35591
|
}
|
|
35104
35592
|
}
|
|
35593
|
+
Matcher.contextType = PerseusI18nContext;
|
|
35105
35594
|
Matcher.propTypes = {
|
|
35106
35595
|
apiOptions: ApiOptions.propTypes,
|
|
35107
35596
|
labels: PropTypes.array,
|
|
@@ -35387,8 +35876,58 @@ class Matrix extends React.Component {
|
|
|
35387
35876
|
};
|
|
35388
35877
|
};
|
|
35389
35878
|
this.simpleValidate = rubric => {
|
|
35390
|
-
|
|
35391
|
-
|
|
35879
|
+
return Matrix.validate(this.getUserInput(), rubric, this.context.strings);
|
|
35880
|
+
};
|
|
35881
|
+
}
|
|
35882
|
+
static validate(state, rubric, strings) {
|
|
35883
|
+
const solution = rubric.answers;
|
|
35884
|
+
const supplied = state.answers;
|
|
35885
|
+
const solutionSize = getMatrixSize(solution);
|
|
35886
|
+
const suppliedSize = getMatrixSize(supplied);
|
|
35887
|
+
const incorrectSize = solutionSize[0] !== suppliedSize[0] || solutionSize[1] !== suppliedSize[1];
|
|
35888
|
+
const createValidator = KhanAnswerTypes.number.createValidatorFunctional;
|
|
35889
|
+
let message = null;
|
|
35890
|
+
let hasEmptyCell = false;
|
|
35891
|
+
let incorrect = false;
|
|
35892
|
+
_(suppliedSize[0]).times(row => {
|
|
35893
|
+
_(suppliedSize[1]).times(col => {
|
|
35894
|
+
if (supplied[row][col] == null || supplied[row][col].toString().length === 0) {
|
|
35895
|
+
hasEmptyCell = true;
|
|
35896
|
+
}
|
|
35897
|
+
if (!incorrectSize) {
|
|
35898
|
+
const validator = createValidator(solution[row][col], {
|
|
35899
|
+
simplify: true
|
|
35900
|
+
}, strings);
|
|
35901
|
+
const result = validator(supplied[row][col]);
|
|
35902
|
+
if (result.message) {
|
|
35903
|
+
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'null'.
|
|
35904
|
+
message = result.message;
|
|
35905
|
+
}
|
|
35906
|
+
if (!result.correct) {
|
|
35907
|
+
incorrect = true;
|
|
35908
|
+
}
|
|
35909
|
+
}
|
|
35910
|
+
});
|
|
35911
|
+
});
|
|
35912
|
+
if (hasEmptyCell) {
|
|
35913
|
+
return {
|
|
35914
|
+
type: "invalid",
|
|
35915
|
+
message: strings.fillAllCells
|
|
35916
|
+
};
|
|
35917
|
+
}
|
|
35918
|
+
if (incorrectSize) {
|
|
35919
|
+
return {
|
|
35920
|
+
type: "points",
|
|
35921
|
+
earned: 0,
|
|
35922
|
+
total: 1,
|
|
35923
|
+
message: null
|
|
35924
|
+
};
|
|
35925
|
+
}
|
|
35926
|
+
return {
|
|
35927
|
+
type: "points",
|
|
35928
|
+
earned: incorrect ? 0 : 1,
|
|
35929
|
+
total: 1,
|
|
35930
|
+
message: message
|
|
35392
35931
|
};
|
|
35393
35932
|
}
|
|
35394
35933
|
componentDidMount() {
|
|
@@ -35430,7 +35969,8 @@ class Matrix extends React.Component {
|
|
|
35430
35969
|
className: "matrix-prefix"
|
|
35431
35970
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
35432
35971
|
content: this.props.prefix,
|
|
35433
|
-
linterContext: this.props.linterContext
|
|
35972
|
+
linterContext: this.props.linterContext,
|
|
35973
|
+
strings: this.context.strings
|
|
35434
35974
|
})), /*#__PURE__*/React.createElement("div", {
|
|
35435
35975
|
className: "matrix-input"
|
|
35436
35976
|
}, /*#__PURE__*/React.createElement("div", {
|
|
@@ -35543,10 +36083,12 @@ class Matrix extends React.Component {
|
|
|
35543
36083
|
className: "matrix-suffix"
|
|
35544
36084
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
35545
36085
|
content: this.props.suffix,
|
|
35546
|
-
linterContext: this.props.linterContext
|
|
36086
|
+
linterContext: this.props.linterContext,
|
|
36087
|
+
strings: this.context.strings
|
|
35547
36088
|
})));
|
|
35548
36089
|
}
|
|
35549
36090
|
}
|
|
36091
|
+
Matrix.contextType = PerseusI18nContext;
|
|
35550
36092
|
Matrix.defaultProps = {
|
|
35551
36093
|
matrixBoardSize: [3, 3],
|
|
35552
36094
|
answers: [[]],
|
|
@@ -35556,59 +36098,6 @@ Matrix.defaultProps = {
|
|
|
35556
36098
|
apiOptions: ApiOptions.defaults,
|
|
35557
36099
|
linterContext: linterContextDefault
|
|
35558
36100
|
};
|
|
35559
|
-
_.extend(Matrix, {
|
|
35560
|
-
validate: function (state, rubric) {
|
|
35561
|
-
const solution = rubric.answers;
|
|
35562
|
-
const supplied = state.answers;
|
|
35563
|
-
const solutionSize = getMatrixSize(solution);
|
|
35564
|
-
const suppliedSize = getMatrixSize(supplied);
|
|
35565
|
-
const incorrectSize = solutionSize[0] !== suppliedSize[0] || solutionSize[1] !== suppliedSize[1];
|
|
35566
|
-
const createValidator = KhanAnswerTypes.number.createValidatorFunctional;
|
|
35567
|
-
let message = null;
|
|
35568
|
-
let hasEmptyCell = false;
|
|
35569
|
-
let incorrect = false;
|
|
35570
|
-
_(suppliedSize[0]).times(row => {
|
|
35571
|
-
_(suppliedSize[1]).times(col => {
|
|
35572
|
-
if (supplied[row][col] == null || supplied[row][col].toString().length === 0) {
|
|
35573
|
-
hasEmptyCell = true;
|
|
35574
|
-
}
|
|
35575
|
-
if (!incorrectSize) {
|
|
35576
|
-
const validator = createValidator(solution[row][col], {
|
|
35577
|
-
simplify: true
|
|
35578
|
-
});
|
|
35579
|
-
const result = validator(supplied[row][col]);
|
|
35580
|
-
if (result.message) {
|
|
35581
|
-
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'null'.
|
|
35582
|
-
message = result.message;
|
|
35583
|
-
}
|
|
35584
|
-
if (!result.correct) {
|
|
35585
|
-
incorrect = true;
|
|
35586
|
-
}
|
|
35587
|
-
}
|
|
35588
|
-
});
|
|
35589
|
-
});
|
|
35590
|
-
if (hasEmptyCell) {
|
|
35591
|
-
return {
|
|
35592
|
-
type: "invalid",
|
|
35593
|
-
message: i18n._("Make sure you fill in all cells in the matrix.")
|
|
35594
|
-
};
|
|
35595
|
-
}
|
|
35596
|
-
if (incorrectSize) {
|
|
35597
|
-
return {
|
|
35598
|
-
type: "points",
|
|
35599
|
-
earned: 0,
|
|
35600
|
-
total: 1,
|
|
35601
|
-
message: null
|
|
35602
|
-
};
|
|
35603
|
-
}
|
|
35604
|
-
return {
|
|
35605
|
-
type: "points",
|
|
35606
|
-
earned: incorrect ? 0 : 1,
|
|
35607
|
-
total: 1,
|
|
35608
|
-
message: message
|
|
35609
|
-
};
|
|
35610
|
-
}
|
|
35611
|
-
});
|
|
35612
36101
|
const propTransform$1 = editorProps => {
|
|
35613
36102
|
// Remove answers before passing to widget
|
|
35614
36103
|
const blankAnswers = _(editorProps.matrixBoardSize[0]).times(function () {
|
|
@@ -36646,8 +37135,8 @@ class Molecule extends React.Component {
|
|
|
36646
37135
|
// eslint-disable-next-line react/no-string-refs
|
|
36647
37136
|
,
|
|
36648
37137
|
ref: "canvas"
|
|
36649
|
-
},
|
|
36650
|
-
content: this.props.smiles
|
|
37138
|
+
}, this.context.strings.molecularDrawing({
|
|
37139
|
+
content: this.props.smiles || ""
|
|
36651
37140
|
}));
|
|
36652
37141
|
if (this.state.error) {
|
|
36653
37142
|
content = /*#__PURE__*/React.createElement("div", {
|
|
@@ -36659,6 +37148,7 @@ class Molecule extends React.Component {
|
|
|
36659
37148
|
}, content);
|
|
36660
37149
|
}
|
|
36661
37150
|
}
|
|
37151
|
+
Molecule.contextType = PerseusI18nContext;
|
|
36662
37152
|
class MoleculeWidget extends React.Component {
|
|
36663
37153
|
constructor(...args) {
|
|
36664
37154
|
super(...args);
|
|
@@ -37121,18 +37611,21 @@ class NumberLine extends React.Component {
|
|
|
37121
37611
|
};
|
|
37122
37612
|
}
|
|
37123
37613
|
render() {
|
|
37614
|
+
const {
|
|
37615
|
+
strings
|
|
37616
|
+
} = this.context;
|
|
37124
37617
|
const divisionRange = this.props.divisionRange;
|
|
37125
37618
|
const divRangeString = divisionRange[0] + EN_DASH + divisionRange[1];
|
|
37126
37619
|
const invalidNumDivisions = this.props.numDivisions < divisionRange[0] || this.props.numDivisions > divisionRange[1];
|
|
37127
37620
|
const inequalityControls = /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("input", {
|
|
37128
37621
|
type: "button",
|
|
37129
37622
|
className: "simple-button",
|
|
37130
|
-
value:
|
|
37623
|
+
value: strings.switchDirection,
|
|
37131
37624
|
onClick: this.handleReverse
|
|
37132
37625
|
}), /*#__PURE__*/React.createElement("input", {
|
|
37133
37626
|
type: "button",
|
|
37134
37627
|
className: "simple-button",
|
|
37135
|
-
value: _(["le", "ge"]).contains(this.props.rel) ?
|
|
37628
|
+
value: _(["le", "ge"]).contains(this.props.rel) ? strings.circleOpen : strings.circleFilled,
|
|
37136
37629
|
onClick: this.handleToggleStrict
|
|
37137
37630
|
}));
|
|
37138
37631
|
let tickCtrl;
|
|
@@ -37143,7 +37636,7 @@ class NumberLine extends React.Component {
|
|
|
37143
37636
|
} else {
|
|
37144
37637
|
Input = NumberInput;
|
|
37145
37638
|
}
|
|
37146
|
-
tickCtrl = /*#__PURE__*/React.createElement("label", null,
|
|
37639
|
+
tickCtrl = /*#__PURE__*/React.createElement("label", null, strings.numDivisions, " ", /*#__PURE__*/React.createElement(Input
|
|
37147
37640
|
// eslint-disable-next-line react/no-string-refs
|
|
37148
37641
|
, {
|
|
37149
37642
|
ref: "tick-ctrl",
|
|
@@ -37160,13 +37653,14 @@ class NumberLine extends React.Component {
|
|
|
37160
37653
|
className: "perseus-widget " + "perseus-widget-interactive-number-line"
|
|
37161
37654
|
}, tickCtrl, !this.isValid() ? /*#__PURE__*/React.createElement("div", {
|
|
37162
37655
|
className: "perseus-error"
|
|
37163
|
-
},
|
|
37656
|
+
}, "Invalid number line configuration.") : this.props.isTickCtrl && invalidNumDivisions ? /*#__PURE__*/React.createElement("div", {
|
|
37164
37657
|
className: "perseus-error"
|
|
37165
|
-
},
|
|
37658
|
+
}, strings.divisions({
|
|
37166
37659
|
divRangeString: divRangeString
|
|
37167
37660
|
})) : this._renderGraphie(), !this.props.static && this.props.isInequality && inequalityControls);
|
|
37168
37661
|
}
|
|
37169
37662
|
}
|
|
37663
|
+
NumberLine.contextType = PerseusI18nContext;
|
|
37170
37664
|
NumberLine.defaultProps = {
|
|
37171
37665
|
range: [0, 10],
|
|
37172
37666
|
labelStyle: "decimal",
|
|
@@ -37393,7 +37887,8 @@ class Card extends React.Component {
|
|
|
37393
37887
|
}, /*#__PURE__*/React.createElement("div", {
|
|
37394
37888
|
className: className.join(" ")
|
|
37395
37889
|
}, /*#__PURE__*/React.createElement(Renderer, _extends({}, rendererProps, {
|
|
37396
|
-
linterContext: this.props.linterContext
|
|
37890
|
+
linterContext: this.props.linterContext,
|
|
37891
|
+
strings: this.context.strings
|
|
37397
37892
|
}))));
|
|
37398
37893
|
}
|
|
37399
37894
|
shouldComponentUpdate(nextProps, nextState) {
|
|
@@ -37446,6 +37941,7 @@ class Card extends React.Component {
|
|
|
37446
37941
|
document.removeEventListener("touchmove", this.onMouseMove);
|
|
37447
37942
|
}
|
|
37448
37943
|
}
|
|
37944
|
+
Card.contextType = PerseusI18nContext;
|
|
37449
37945
|
Card.propTypes = {
|
|
37450
37946
|
floating: PropTypes.bool.isRequired,
|
|
37451
37947
|
animating: PropTypes.bool,
|
|
@@ -38735,7 +39231,7 @@ class HighlightSetRenderer extends React.PureComponent {
|
|
|
38735
39231
|
}
|
|
38736
39232
|
const hoveredHighlight = this.props.highlights[hoveredHighlightKey];
|
|
38737
39233
|
return /*#__PURE__*/React.createElement(HighlightTooltip, {
|
|
38738
|
-
label:
|
|
39234
|
+
label: this.context.strings.removeHighlight,
|
|
38739
39235
|
focusNode: hoveredHighlight.domRange.endContainer,
|
|
38740
39236
|
focusOffset: hoveredHighlight.domRange.endOffset,
|
|
38741
39237
|
offsetParent: this.props.offsetParent,
|
|
@@ -38765,6 +39261,7 @@ class HighlightSetRenderer extends React.PureComponent {
|
|
|
38765
39261
|
})), this.props.editable && this._renderTooltip());
|
|
38766
39262
|
}
|
|
38767
39263
|
}
|
|
39264
|
+
HighlightSetRenderer.contextType = PerseusI18nContext;
|
|
38768
39265
|
|
|
38769
39266
|
/* eslint-disable react/no-unsafe */
|
|
38770
39267
|
class SelectionTracker extends React.PureComponent {
|
|
@@ -38965,7 +39462,7 @@ class HighlightingUI extends React.PureComponent {
|
|
|
38965
39462
|
onRemoveHighlight: this.props.onRemoveHighlight,
|
|
38966
39463
|
zIndexes: this.props.zIndexes
|
|
38967
39464
|
}), this._selectionIsValid(trackedSelection) && !userIsMouseSelecting && /*#__PURE__*/React.createElement(HighlightTooltip, {
|
|
38968
|
-
label:
|
|
39465
|
+
label: this.context.strings.addHighlight,
|
|
38969
39466
|
onClick: () => this._handleAddHighlight(trackedSelection.proposedHighlight),
|
|
38970
39467
|
focusNode: trackedSelection.focusNode,
|
|
38971
39468
|
focusOffset: trackedSelection.focusOffset,
|
|
@@ -38973,6 +39470,7 @@ class HighlightingUI extends React.PureComponent {
|
|
|
38973
39470
|
})));
|
|
38974
39471
|
}
|
|
38975
39472
|
}
|
|
39473
|
+
HighlightingUI.contextType = PerseusI18nContext;
|
|
38976
39474
|
|
|
38977
39475
|
/**
|
|
38978
39476
|
* This component, whenever its content updates, traverses the DOM to find
|
|
@@ -39263,16 +39761,30 @@ class RefStart extends React.Component {
|
|
|
39263
39761
|
render() {
|
|
39264
39762
|
return /*#__PURE__*/React.createElement("span", {
|
|
39265
39763
|
style: REF_STYLE
|
|
39266
|
-
},
|
|
39764
|
+
}, "_");
|
|
39267
39765
|
}
|
|
39268
39766
|
}
|
|
39269
39767
|
class RefEnd extends React.Component {
|
|
39270
39768
|
render() {
|
|
39271
39769
|
return /*#__PURE__*/React.createElement("span", {
|
|
39272
39770
|
style: REF_STYLE
|
|
39273
|
-
},
|
|
39771
|
+
}, "_");
|
|
39274
39772
|
}
|
|
39275
39773
|
}
|
|
39774
|
+
const AltText = ({
|
|
39775
|
+
id,
|
|
39776
|
+
number
|
|
39777
|
+
}) => {
|
|
39778
|
+
const {
|
|
39779
|
+
strings
|
|
39780
|
+
} = usePerseusI18n();
|
|
39781
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
39782
|
+
key: "alt-text",
|
|
39783
|
+
className: "perseus-sr-only"
|
|
39784
|
+
}, strings[id]({
|
|
39785
|
+
number
|
|
39786
|
+
}));
|
|
39787
|
+
};
|
|
39276
39788
|
const rules = {
|
|
39277
39789
|
newline: SimpleMarkdown.defaultRules.newline,
|
|
39278
39790
|
paragraph: SimpleMarkdown.defaultRules.paragraph,
|
|
@@ -39441,12 +39953,10 @@ const rules = {
|
|
|
39441
39953
|
"aria-hidden": "true"
|
|
39442
39954
|
}, /*#__PURE__*/React.createElement("span", {
|
|
39443
39955
|
style: SQUARE_LABEL_STYLE
|
|
39444
|
-
}, node.content)), /*#__PURE__*/React.createElement(
|
|
39445
|
-
|
|
39446
|
-
className: "perseus-sr-only"
|
|
39447
|
-
}, i18n.$_("[Marker for question %(number)s]", {
|
|
39956
|
+
}, node.content)), /*#__PURE__*/React.createElement(AltText, {
|
|
39957
|
+
id: "questionMarker",
|
|
39448
39958
|
number: node.content
|
|
39449
|
-
})
|
|
39959
|
+
}), node.space ? "\u00A0" : null];
|
|
39450
39960
|
}
|
|
39451
39961
|
},
|
|
39452
39962
|
circleLabel: {
|
|
@@ -39467,12 +39977,10 @@ const rules = {
|
|
|
39467
39977
|
"aria-hidden": true
|
|
39468
39978
|
}, /*#__PURE__*/React.createElement("span", {
|
|
39469
39979
|
style: CIRCLE_LABEL_STYLE
|
|
39470
|
-
}, node.content)), /*#__PURE__*/React.createElement(
|
|
39471
|
-
|
|
39472
|
-
className: "perseus-sr-only"
|
|
39473
|
-
}, i18n.$_("[Circle marker %(number)s]", {
|
|
39980
|
+
}, node.content)), /*#__PURE__*/React.createElement(AltText, {
|
|
39981
|
+
id: "circleMarker",
|
|
39474
39982
|
number: node.content
|
|
39475
|
-
})
|
|
39983
|
+
}), node.space ? "\u00A0" : null];
|
|
39476
39984
|
}
|
|
39477
39985
|
},
|
|
39478
39986
|
squareBracketRef: {
|
|
@@ -39492,12 +40000,10 @@ const rules = {
|
|
|
39492
40000
|
key: "visual-brackets",
|
|
39493
40001
|
className: "perseus-passage-bracket-label",
|
|
39494
40002
|
"aria-hidden": "true"
|
|
39495
|
-
}, "[", node.content, "]"), /*#__PURE__*/React.createElement(
|
|
39496
|
-
|
|
39497
|
-
className: "perseus-sr-only"
|
|
39498
|
-
}, i18n.$_("[Sentence %(number)s]", {
|
|
40003
|
+
}, "[", node.content, "]"), /*#__PURE__*/React.createElement(AltText, {
|
|
40004
|
+
id: "sentenceMarker",
|
|
39499
40005
|
number: node.content
|
|
39500
|
-
})
|
|
40006
|
+
}), node.space ? "\u00A0" : null];
|
|
39501
40007
|
}
|
|
39502
40008
|
},
|
|
39503
40009
|
highlight: {
|
|
@@ -39888,15 +40394,18 @@ class Passage extends React.Component {
|
|
|
39888
40394
|
_renderInstructions(parseState) {
|
|
39889
40395
|
const firstQuestionNumber = parseState.firstQuestionRef;
|
|
39890
40396
|
const firstSentenceRef = parseState.firstSentenceRef;
|
|
40397
|
+
const {
|
|
40398
|
+
strings
|
|
40399
|
+
} = this.context;
|
|
39891
40400
|
let instructions = "";
|
|
39892
40401
|
if (firstQuestionNumber) {
|
|
39893
|
-
instructions +=
|
|
40402
|
+
instructions += strings.symbolPassage({
|
|
39894
40403
|
questionSymbol: "[[" + firstQuestionNumber + "]]",
|
|
39895
40404
|
questionNumber: firstQuestionNumber
|
|
39896
40405
|
});
|
|
39897
40406
|
}
|
|
39898
40407
|
if (firstSentenceRef) {
|
|
39899
|
-
instructions +=
|
|
40408
|
+
instructions += strings.symbolQuestion({
|
|
39900
40409
|
sentenceSymbol: "[" + firstSentenceRef + "]"
|
|
39901
40410
|
});
|
|
39902
40411
|
}
|
|
@@ -39942,6 +40451,9 @@ class Passage extends React.Component {
|
|
|
39942
40451
|
return PassageMarkdown.output(parsed);
|
|
39943
40452
|
}
|
|
39944
40453
|
render() {
|
|
40454
|
+
const {
|
|
40455
|
+
strings
|
|
40456
|
+
} = this.context;
|
|
39945
40457
|
let lineNumbers;
|
|
39946
40458
|
const nLines = this.state.nLines;
|
|
39947
40459
|
if (this.props.showLineNumbers && nLines) {
|
|
@@ -39950,9 +40462,7 @@ class Passage extends React.Component {
|
|
|
39950
40462
|
lineNumbers = _.range(1, nLines + 1).map(lineN => {
|
|
39951
40463
|
const lineAt = lineN + this.state.startLineNumbersAfter;
|
|
39952
40464
|
if (lineAt === 4) {
|
|
39953
|
-
|
|
39954
|
-
// I18N: a label next to a reading passage to denote the line number
|
|
39955
|
-
const translatedLine = i18n._("Line ");
|
|
40465
|
+
const translatedLine = strings.lineLabel;
|
|
39956
40466
|
return /*#__PURE__*/React.createElement("span", {
|
|
39957
40467
|
key: "line-marker",
|
|
39958
40468
|
className: "line-marker"
|
|
@@ -39982,13 +40492,14 @@ class Passage extends React.Component {
|
|
|
39982
40492
|
className: "passage-title"
|
|
39983
40493
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
39984
40494
|
content: this.props.passageTitle,
|
|
39985
|
-
linterContext: this.props.linterContext
|
|
40495
|
+
linterContext: this.props.linterContext,
|
|
40496
|
+
strings: strings
|
|
39986
40497
|
})), lineNumbers && /*#__PURE__*/React.createElement("div", {
|
|
39987
40498
|
className: "line-numbers",
|
|
39988
40499
|
"aria-hidden": true
|
|
39989
40500
|
}, lineNumbers), !hasTitle && /*#__PURE__*/React.createElement("h3", {
|
|
39990
40501
|
className: "perseus-sr-only"
|
|
39991
|
-
},
|
|
40502
|
+
}, strings.beginningPassage), /*#__PURE__*/React.createElement("div", {
|
|
39992
40503
|
className: "passage-text"
|
|
39993
40504
|
}, this.shouldRenderJipt() ?
|
|
39994
40505
|
/*#__PURE__*/
|
|
@@ -39996,18 +40507,20 @@ class Passage extends React.Component {
|
|
|
39996
40507
|
// content to a <Renderer /> which knows how
|
|
39997
40508
|
// to handle rendering JIPT text.
|
|
39998
40509
|
React.createElement(Renderer, {
|
|
39999
|
-
content: this.props.passageText
|
|
40510
|
+
content: this.props.passageText,
|
|
40511
|
+
strings: strings
|
|
40000
40512
|
}) : this._renderContent(parsedContent)), this._hasFootnotes() && [/*#__PURE__*/React.createElement("h4", {
|
|
40001
40513
|
key: "footnote-start",
|
|
40002
40514
|
className: "perseus-sr-only"
|
|
40003
|
-
},
|
|
40515
|
+
}, strings.beginningFootnotes), /*#__PURE__*/React.createElement("div", {
|
|
40004
40516
|
key: "footnotes",
|
|
40005
40517
|
className: "footnotes"
|
|
40006
40518
|
}, this._renderFootnotes())], /*#__PURE__*/React.createElement("div", {
|
|
40007
40519
|
className: "perseus-sr-only"
|
|
40008
|
-
},
|
|
40520
|
+
}, strings.endPassage))));
|
|
40009
40521
|
}
|
|
40010
40522
|
}
|
|
40523
|
+
Passage.contextType = PerseusI18nContext;
|
|
40011
40524
|
Passage.defaultProps = {
|
|
40012
40525
|
passageTitle: "",
|
|
40013
40526
|
passageText: "",
|
|
@@ -40058,10 +40571,12 @@ class PassageRefTarget extends React.Component {
|
|
|
40058
40571
|
// inline={true}
|
|
40059
40572
|
,
|
|
40060
40573
|
apiOptions: this.props.apiOptions,
|
|
40061
|
-
linterContext: this.props.linterContext
|
|
40574
|
+
linterContext: this.props.linterContext,
|
|
40575
|
+
strings: this.context.strings
|
|
40062
40576
|
});
|
|
40063
40577
|
}
|
|
40064
40578
|
}
|
|
40579
|
+
PassageRefTarget.contextType = PerseusI18nContext;
|
|
40065
40580
|
PassageRefTarget.defaultProps = {
|
|
40066
40581
|
content: "",
|
|
40067
40582
|
linterContext: linterContextDefault
|
|
@@ -40245,11 +40760,11 @@ class Plotter extends React.Component {
|
|
|
40245
40760
|
}
|
|
40246
40761
|
}
|
|
40247
40762
|
if ((isBar || isLine) && isMobile && !this.props.static) {
|
|
40248
|
-
self.graphie.dragPrompt = graphie.label([c.dimX / 2, c.dimY / 2],
|
|
40763
|
+
self.graphie.dragPrompt = graphie.label([c.dimX / 2, c.dimY / 2], this.context.strings.dragHandles, "center", false).css("font-weight", "bold").css("color", KhanColors.KA_GREEN).css("display", "none");
|
|
40249
40764
|
}
|
|
40250
40765
|
self.setupCategories(config);
|
|
40251
40766
|
if (isTiledPlot && isMobile) {
|
|
40252
|
-
self.graphie.dotPrompt = graphie.label([c.dimX / 2, c.dimY / 2],
|
|
40767
|
+
self.graphie.dotPrompt = graphie.label([c.dimX / 2, c.dimY / 2], this.context.strings.tapAddPoints, "center", false).css("font-weight", "bold").css("color", KhanColors.KA_GREEN).css("display", "none");
|
|
40253
40768
|
}
|
|
40254
40769
|
if (isTiledPlot) {
|
|
40255
40770
|
self.drawPicHeights(self.state.values, prevState.values);
|
|
@@ -40864,6 +41379,7 @@ class Plotter extends React.Component {
|
|
|
40864
41379
|
});
|
|
40865
41380
|
}
|
|
40866
41381
|
}
|
|
41382
|
+
Plotter.contextType = PerseusI18nContext;
|
|
40867
41383
|
Plotter.defaultProps = {
|
|
40868
41384
|
type: "bar",
|
|
40869
41385
|
labels: ["", ""],
|
|
@@ -41135,7 +41651,7 @@ class Table extends React.Component {
|
|
|
41135
41651
|
};
|
|
41136
41652
|
this.simpleValidate = rubric => {
|
|
41137
41653
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Table'.
|
|
41138
|
-
return Table.validate(this.getUserInput(), rubric);
|
|
41654
|
+
return Table.validate(this.getUserInput(), rubric, this.context.strings);
|
|
41139
41655
|
};
|
|
41140
41656
|
this._handleFocus = inputPath => {
|
|
41141
41657
|
this.props.onFocus(inputPath);
|
|
@@ -41240,7 +41756,8 @@ class Table extends React.Component {
|
|
|
41240
41756
|
key: i
|
|
41241
41757
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
41242
41758
|
content: header,
|
|
41243
|
-
linterContext: this.props.linterContext
|
|
41759
|
+
linterContext: this.props.linterContext,
|
|
41760
|
+
strings: this.context.strings
|
|
41244
41761
|
}));
|
|
41245
41762
|
}))), /*#__PURE__*/React.createElement("tbody", null, _(rows).times(r => {
|
|
41246
41763
|
return /*#__PURE__*/React.createElement("tr", {
|
|
@@ -41262,6 +41779,7 @@ class Table extends React.Component {
|
|
|
41262
41779
|
})));
|
|
41263
41780
|
}
|
|
41264
41781
|
}
|
|
41782
|
+
Table.contextType = PerseusI18nContext;
|
|
41265
41783
|
Table.propTypes = {
|
|
41266
41784
|
answers: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
|
|
41267
41785
|
editableHeaders: PropTypes.bool,
|
|
@@ -41289,7 +41807,7 @@ Table.defaultProps = function () {
|
|
|
41289
41807
|
};
|
|
41290
41808
|
}();
|
|
41291
41809
|
_.extend(Table, {
|
|
41292
|
-
validate: function (state, rubric) {
|
|
41810
|
+
validate: function (state, rubric, strings) {
|
|
41293
41811
|
const filterNonEmpty = function filterNonEmpty(table) {
|
|
41294
41812
|
return _.filter(table, function (row) {
|
|
41295
41813
|
// Check if row has a cell that is nonempty
|
|
@@ -41327,7 +41845,7 @@ _.extend(Table, {
|
|
|
41327
41845
|
const cellSolution = rowSolution[i];
|
|
41328
41846
|
const validator = createValidator(cellSolution, {
|
|
41329
41847
|
simplify: true
|
|
41330
|
-
});
|
|
41848
|
+
}, strings);
|
|
41331
41849
|
const result = validator(cellSupplied);
|
|
41332
41850
|
if (result.message) {
|
|
41333
41851
|
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'null'.
|
|
@@ -42090,7 +42608,7 @@ class OldUnitInput extends React.Component {
|
|
|
42090
42608
|
});
|
|
42091
42609
|
};
|
|
42092
42610
|
this.simpleValidate = (rubric, onInputError) => {
|
|
42093
|
-
return OldUnitInput.validate(this.getUserInput(), rubric);
|
|
42611
|
+
return OldUnitInput.validate(this.getUserInput(), rubric, this.context.strings);
|
|
42094
42612
|
};
|
|
42095
42613
|
this.getUserInput = () => {
|
|
42096
42614
|
return this.props.value;
|
|
@@ -42143,13 +42661,13 @@ class OldUnitInput extends React.Component {
|
|
|
42143
42661
|
return "unit";
|
|
42144
42662
|
};
|
|
42145
42663
|
}
|
|
42146
|
-
static validate(userInput, rubric) {
|
|
42664
|
+
static validate(userInput, rubric, strings) {
|
|
42147
42665
|
const answer = KAS.unitParse(rubric.value).expr;
|
|
42148
42666
|
const guess = KAS.unitParse(userInput);
|
|
42149
42667
|
if (!guess.parsed) {
|
|
42150
42668
|
return {
|
|
42151
42669
|
type: "invalid",
|
|
42152
|
-
message:
|
|
42670
|
+
message: strings.dontUnderstandUnits
|
|
42153
42671
|
};
|
|
42154
42672
|
}
|
|
42155
42673
|
|
|
@@ -42164,8 +42682,7 @@ class OldUnitInput extends React.Component {
|
|
|
42164
42682
|
const sigfigs = rubric.sigfigs;
|
|
42165
42683
|
const sigfigsCorrect = countSigfigs(guess.coefficient) === sigfigs;
|
|
42166
42684
|
if (!sigfigsCorrect) {
|
|
42167
|
-
|
|
42168
|
-
message = i18n._("Check your significant figures.");
|
|
42685
|
+
message = strings.checkSigFigs;
|
|
42169
42686
|
}
|
|
42170
42687
|
|
|
42171
42688
|
// now we need to check that the answer is correct to the precision we
|
|
@@ -42184,8 +42701,7 @@ class OldUnitInput extends React.Component {
|
|
|
42184
42701
|
numericallyCorrect = false;
|
|
42185
42702
|
}
|
|
42186
42703
|
if (!numericallyCorrect) {
|
|
42187
|
-
|
|
42188
|
-
message = i18n._("That answer is numerically incorrect.");
|
|
42704
|
+
message = strings.answerNumericallyIncorrect;
|
|
42189
42705
|
}
|
|
42190
42706
|
let kasCorrect;
|
|
42191
42707
|
const guessUnit = primUnits(guess.expr.simplify());
|
|
@@ -42205,8 +42721,7 @@ class OldUnitInput extends React.Component {
|
|
|
42205
42721
|
});
|
|
42206
42722
|
}
|
|
42207
42723
|
if (!kasCorrect) {
|
|
42208
|
-
|
|
42209
|
-
message = i18n._("Check your units.");
|
|
42724
|
+
message = strings.checkUnits;
|
|
42210
42725
|
}
|
|
42211
42726
|
const correct = kasCorrect && numericallyCorrect && sigfigsCorrect;
|
|
42212
42727
|
return {
|
|
@@ -42255,12 +42770,13 @@ class OldUnitInput extends React.Component {
|
|
|
42255
42770
|
style: {
|
|
42256
42771
|
display: "none"
|
|
42257
42772
|
}
|
|
42258
|
-
},
|
|
42773
|
+
}, this.context.strings.dontUnderstand));
|
|
42259
42774
|
}
|
|
42260
42775
|
}
|
|
42261
42776
|
|
|
42262
42777
|
// Extract the primitive units from a unit expression. This first simplifies
|
|
42263
42778
|
// `expr` to a `Mul` like "5 kg m / s^2" then removes the first term.
|
|
42779
|
+
OldUnitInput.contextType = PerseusI18nContext;
|
|
42264
42780
|
OldUnitInput.defaultProps = {
|
|
42265
42781
|
apiOptions: ApiOptions.defaults,
|
|
42266
42782
|
value: ""
|
|
@@ -42311,9 +42827,12 @@ const VideoTranscriptLink = props => {
|
|
|
42311
42827
|
// which is used by our GraphQL framework.
|
|
42312
42828
|
// @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'VideoKind'.
|
|
42313
42829
|
const result = useVideo(id, kind);
|
|
42830
|
+
const {
|
|
42831
|
+
strings
|
|
42832
|
+
} = usePerseusI18n();
|
|
42314
42833
|
switch (result.status) {
|
|
42315
42834
|
case "loading":
|
|
42316
|
-
return /*#__PURE__*/React.createElement(View, null,
|
|
42835
|
+
return /*#__PURE__*/React.createElement(View, null, strings.loading);
|
|
42317
42836
|
case "success":
|
|
42318
42837
|
{
|
|
42319
42838
|
var _result$data;
|
|
@@ -42326,14 +42845,14 @@ const VideoTranscriptLink = props => {
|
|
|
42326
42845
|
href: "/transcript/" + ((video == null ? void 0 : video.contentId) || "videoNotFound"),
|
|
42327
42846
|
target: "_blank",
|
|
42328
42847
|
className: "visited-no-recolor"
|
|
42329
|
-
},
|
|
42848
|
+
}, strings.videoTranscript));
|
|
42330
42849
|
}
|
|
42331
42850
|
case "error":
|
|
42332
|
-
return /*#__PURE__*/React.createElement(View, null,
|
|
42851
|
+
return /*#__PURE__*/React.createElement(View, null, strings.somethingWrong);
|
|
42333
42852
|
case "aborted":
|
|
42334
|
-
return /*#__PURE__*/React.createElement(View, null,
|
|
42853
|
+
return /*#__PURE__*/React.createElement(View, null, strings.somethingWrong);
|
|
42335
42854
|
default:
|
|
42336
|
-
return /*#__PURE__*/React.createElement(View, null,
|
|
42855
|
+
return /*#__PURE__*/React.createElement(View, null, strings.somethingWrong);
|
|
42337
42856
|
}
|
|
42338
42857
|
};
|
|
42339
42858
|
const styles$4 = StyleSheet.create({
|
|
@@ -42425,7 +42944,7 @@ class Video extends React.Component {
|
|
|
42425
42944
|
key: location + this.props.alignment
|
|
42426
42945
|
}, /*#__PURE__*/React.createElement(View, {
|
|
42427
42946
|
style: a11y.srOnly
|
|
42428
|
-
},
|
|
42947
|
+
}, this.context.strings.videoWrapper), /*#__PURE__*/React.createElement("iframe", {
|
|
42429
42948
|
// TODO(joshuan): Consider not using iframes when we're
|
|
42430
42949
|
// loading this from webapp. This iframe is problematic
|
|
42431
42950
|
// for screenreaders.
|
|
@@ -42440,6 +42959,7 @@ class Video extends React.Component {
|
|
|
42440
42959
|
}));
|
|
42441
42960
|
}
|
|
42442
42961
|
}
|
|
42962
|
+
Video.contextType = PerseusI18nContext;
|
|
42443
42963
|
var Video$1 = {
|
|
42444
42964
|
name: "video",
|
|
42445
42965
|
displayName: "Video",
|
|
@@ -42489,7 +43009,7 @@ const init = function init(options) {
|
|
|
42489
43009
|
|
|
42490
43010
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
42491
43011
|
const libName = "@khanacademy/perseus";
|
|
42492
|
-
const libVersion = "
|
|
43012
|
+
const libVersion = "22.0.0";
|
|
42493
43013
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
42494
43014
|
|
|
42495
43015
|
const apiVersion = {
|
|
@@ -42655,8 +43175,9 @@ class ArticleRenderer extends React.Component {
|
|
|
42655
43175
|
}
|
|
42656
43176
|
}),
|
|
42657
43177
|
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "article"),
|
|
42658
|
-
legacyPerseusLint: this.props.legacyPerseusLint
|
|
42659
|
-
|
|
43178
|
+
legacyPerseusLint: this.props.legacyPerseusLint,
|
|
43179
|
+
strings: this.context.strings
|
|
43180
|
+
})));
|
|
42660
43181
|
});
|
|
42661
43182
|
return /*#__PURE__*/React.createElement("div", {
|
|
42662
43183
|
className: classes
|
|
@@ -42665,6 +43186,7 @@ class ArticleRenderer extends React.Component {
|
|
|
42665
43186
|
}, sections));
|
|
42666
43187
|
}
|
|
42667
43188
|
}
|
|
43189
|
+
ArticleRenderer.contextType = PerseusI18nContext;
|
|
42668
43190
|
ArticleRenderer.defaultProps = {
|
|
42669
43191
|
apiOptions: ApiOptions.defaults,
|
|
42670
43192
|
useNewStyles: false,
|
|
@@ -42717,7 +43239,7 @@ class HintRenderer extends React.Component {
|
|
|
42717
43239
|
tabIndex: "-1"
|
|
42718
43240
|
}, !apiOptions.isMobile && /*#__PURE__*/React.createElement("span", {
|
|
42719
43241
|
className: "perseus-sr-only"
|
|
42720
|
-
},
|
|
43242
|
+
}, this.context.strings.hintPos({
|
|
42721
43243
|
pos: pos + 1
|
|
42722
43244
|
})), !apiOptions.isMobile && totalHints != null && pos != null && /*#__PURE__*/React.createElement("span", {
|
|
42723
43245
|
className: "perseus-hint-label",
|
|
@@ -42734,11 +43256,13 @@ class HintRenderer extends React.Component {
|
|
|
42734
43256
|
images: hint.images,
|
|
42735
43257
|
apiOptions: rendererApiOptions,
|
|
42736
43258
|
findExternalWidgets: this.props.findExternalWidgets,
|
|
42737
|
-
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "hint")
|
|
43259
|
+
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "hint"),
|
|
43260
|
+
strings: this.context.strings
|
|
42738
43261
|
}))
|
|
42739
43262
|
);
|
|
42740
43263
|
}
|
|
42741
43264
|
}
|
|
43265
|
+
HintRenderer.contextType = PerseusI18nContext;
|
|
42742
43266
|
HintRenderer.defaultProps = {
|
|
42743
43267
|
linterContext: PerseusLinter.linterContextDefault
|
|
42744
43268
|
};
|
|
@@ -42920,7 +43444,7 @@ class HintsRenderer extends React.Component {
|
|
|
42920
43444
|
className: classNames$1
|
|
42921
43445
|
}, apiOptions.isMobile && hintsVisible > 0 && /*#__PURE__*/React.createElement("div", {
|
|
42922
43446
|
className: css(styles$2.mobileHintStylesHintTitle, sharedStyles.responsiveLabel)
|
|
42923
|
-
},
|
|
43447
|
+
}, this.context.strings.hints), hints, showGetAnotherHint && /*#__PURE__*/React.createElement("button", {
|
|
42924
43448
|
// @ts-expect-error - TS2322 - Type '{ children: Element[]; rel: string; className: string; onClick: (evt: MouseEvent<HTMLButtonElement, MouseEvent>) => void; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.
|
|
42925
43449
|
rel: "button",
|
|
42926
43450
|
className: css(styles$2.linkButton, styles$2.getAnotherHintButton, apiOptions.isMobile && styles$2.mobileHintStylesGetAnotherHintButton),
|
|
@@ -42935,9 +43459,10 @@ class HintsRenderer extends React.Component {
|
|
|
42935
43459
|
className: css(styles$2.plusText, apiOptions.isMobile && styles$2.mobileHintStylesPlusText)
|
|
42936
43460
|
}, "+"), /*#__PURE__*/React.createElement("span", {
|
|
42937
43461
|
className: css(styles$2.getAnotherHintText)
|
|
42938
|
-
},
|
|
43462
|
+
}, this.context.strings.getAnotherHint, " ", hintRatioCopy)));
|
|
42939
43463
|
}
|
|
42940
43464
|
}
|
|
43465
|
+
HintsRenderer.contextType = PerseusI18nContext;
|
|
42941
43466
|
HintsRenderer.defaultProps = {
|
|
42942
43467
|
linterContext: PerseusLinter.linterContextDefault
|
|
42943
43468
|
};
|
|
@@ -43328,14 +43853,16 @@ class ServerItemRenderer extends React.Component {
|
|
|
43328
43853
|
content: this.props.item.question.content,
|
|
43329
43854
|
widgets: this.props.item.question.widgets,
|
|
43330
43855
|
images: this.props.item.question.images,
|
|
43331
|
-
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "question")
|
|
43856
|
+
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "question"),
|
|
43857
|
+
strings: this.context.strings
|
|
43332
43858
|
}, this.props.dependencies)));
|
|
43333
43859
|
const hintsRenderer = /*#__PURE__*/React.createElement(HintsRenderer, {
|
|
43334
43860
|
hints: this.props.item.hints,
|
|
43335
43861
|
hintsVisible: this.props.hintsVisible,
|
|
43336
43862
|
apiOptions: apiOptions,
|
|
43337
43863
|
ref: elem => this.hintsRenderer = elem,
|
|
43338
|
-
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "hints")
|
|
43864
|
+
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "hints"),
|
|
43865
|
+
strings: this.context.strings
|
|
43339
43866
|
});
|
|
43340
43867
|
return /*#__PURE__*/React.createElement(DependenciesContext.Provider, {
|
|
43341
43868
|
value: this.props.dependencies
|
|
@@ -43349,6 +43876,7 @@ class ServerItemRenderer extends React.Component {
|
|
|
43349
43876
|
}, hintsRenderer)));
|
|
43350
43877
|
}
|
|
43351
43878
|
}
|
|
43879
|
+
ServerItemRenderer.contextType = PerseusI18nContext;
|
|
43352
43880
|
ServerItemRenderer.defaultProps = {
|
|
43353
43881
|
apiOptions: {},
|
|
43354
43882
|
// a deep default is done in `this.update()`
|
|
@@ -43511,114 +44039,6 @@ var constants = /*#__PURE__*/Object.freeze({
|
|
|
43511
44039
|
perseusFrameBorderWidth: perseusFrameBorderWidth
|
|
43512
44040
|
});
|
|
43513
44041
|
|
|
43514
|
-
// TODO(FEI-4010): Remove `Perseus` prefix for all types here
|
|
43515
|
-
// TODO(FEI-4011): Use types generated by https://github.com/jaredly/generate-perseus-flowtypes
|
|
43516
|
-
|
|
43517
|
-
// Range is replaced within this file with Interval, but it is used elsewhere
|
|
43518
|
-
// and exported from the package, so we need to keep it around.
|
|
43519
|
-
// TODO(FEI-5054): Figure out how to get global .d.ts files working with monorepos
|
|
43520
|
-
const ItemExtras = [
|
|
43521
|
-
// The user might benefit from using a Scientific Calculator. Provided on Khan Academy when true
|
|
43522
|
-
"calculator",
|
|
43523
|
-
// The user might benefit from using a statistics Chi Squared Table like https://people.richland.edu/james/lecture/m170/tbl-chi.html
|
|
43524
|
-
"chi2Table",
|
|
43525
|
-
// The user might benefit from a monthly payments calculator. Provided on Khan Academy when true
|
|
43526
|
-
"financialCalculatorMonthlyPayment",
|
|
43527
|
-
// The user might benefit from a total amount calculator. Provided on Khan Academy when true
|
|
43528
|
-
"financialCalculatorTotalAmount",
|
|
43529
|
-
// The user might benefit from a time to pay off calculator. Provided on Khan Academy when true
|
|
43530
|
-
"financialCalculatorTimeToPayOff",
|
|
43531
|
-
// The user might benefit from using a Periodic Table of Elements. Provided on Khan Academy when true
|
|
43532
|
-
"periodicTable",
|
|
43533
|
-
// The user might benefit from using a Periodic Table of Elements with key. Provided on Khan Academy when true
|
|
43534
|
-
"periodicTableWithKey",
|
|
43535
|
-
// The user might benefit from using a statistics T Table like https://www.statisticshowto.com/tables/t-distribution-table/
|
|
43536
|
-
"tTable",
|
|
43537
|
-
// The user might benefit from using a statistics Z Table like https://www.ztable.net/
|
|
43538
|
-
"zTable"];
|
|
43539
|
-
|
|
43540
|
-
// prettier-ignore
|
|
43541
|
-
|
|
43542
|
-
// prettier-ignore
|
|
43543
|
-
|
|
43544
|
-
// prettier-ignore
|
|
43545
|
-
|
|
43546
|
-
// prettier-ignore
|
|
43547
|
-
|
|
43548
|
-
// prettier-ignore
|
|
43549
|
-
|
|
43550
|
-
// prettier-ignore
|
|
43551
|
-
|
|
43552
|
-
// prettier-ignore
|
|
43553
|
-
|
|
43554
|
-
// prettier-ignore
|
|
43555
|
-
|
|
43556
|
-
// prettier-ignore
|
|
43557
|
-
|
|
43558
|
-
// prettier-ignore
|
|
43559
|
-
|
|
43560
|
-
// prettier-ignore
|
|
43561
|
-
|
|
43562
|
-
// prettier-ignore
|
|
43563
|
-
|
|
43564
|
-
// prettier-ignore
|
|
43565
|
-
|
|
43566
|
-
// prettier-ignore
|
|
43567
|
-
|
|
43568
|
-
// prettier-ignore
|
|
43569
|
-
|
|
43570
|
-
// prettier-ignore
|
|
43571
|
-
|
|
43572
|
-
// prettier-ignore
|
|
43573
|
-
|
|
43574
|
-
// prettier-ignore
|
|
43575
|
-
|
|
43576
|
-
// prettier-ignore
|
|
43577
|
-
|
|
43578
|
-
// prettier-ignore
|
|
43579
|
-
|
|
43580
|
-
// prettier-ignore
|
|
43581
|
-
|
|
43582
|
-
// prettier-ignore
|
|
43583
|
-
|
|
43584
|
-
// prettier-ignore
|
|
43585
|
-
|
|
43586
|
-
// prettier-ignore
|
|
43587
|
-
|
|
43588
|
-
// prettier-ignore
|
|
43589
|
-
|
|
43590
|
-
// prettier-ignore
|
|
43591
|
-
|
|
43592
|
-
// prettier-ignore
|
|
43593
|
-
|
|
43594
|
-
// prettier-ignore
|
|
43595
|
-
|
|
43596
|
-
// prettier-ignore
|
|
43597
|
-
|
|
43598
|
-
// prettier-ignore
|
|
43599
|
-
|
|
43600
|
-
// prettier-ignore
|
|
43601
|
-
|
|
43602
|
-
// prettier-ignore
|
|
43603
|
-
|
|
43604
|
-
// prettier-ignore
|
|
43605
|
-
|
|
43606
|
-
// prettier-ignore
|
|
43607
|
-
|
|
43608
|
-
// prettier-ignore
|
|
43609
|
-
|
|
43610
|
-
// prettier-ignore
|
|
43611
|
-
|
|
43612
|
-
//prettier-ignore
|
|
43613
|
-
|
|
43614
|
-
// A background image applied to various widgets.
|
|
43615
|
-
|
|
43616
|
-
const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
|
|
43617
|
-
|
|
43618
|
-
// 2D range: xMin, xMax, yMin, yMax
|
|
43619
|
-
|
|
43620
|
-
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
43621
|
-
|
|
43622
44042
|
/**
|
|
43623
44043
|
* Traverses a {content, widgets, images} renderer props object,
|
|
43624
44044
|
* such as `itemData.question`
|
|
@@ -44227,7 +44647,9 @@ class MultiRenderer extends React.Component {
|
|
|
44227
44647
|
// `<Renderer>`. I'm not sure how else to do this.
|
|
44228
44648
|
const _this$props = this.props,
|
|
44229
44649
|
otherProps = _objectWithoutPropertiesLoose(_this$props, _excluded);
|
|
44230
|
-
return otherProps
|
|
44650
|
+
return _extends({}, otherProps, {
|
|
44651
|
+
strings: this.context.strings
|
|
44652
|
+
});
|
|
44231
44653
|
}
|
|
44232
44654
|
|
|
44233
44655
|
/**
|
|
@@ -44478,7 +44900,7 @@ class MultiRenderer extends React.Component {
|
|
|
44478
44900
|
if (this.state.renderError) {
|
|
44479
44901
|
return /*#__PURE__*/React.createElement("div", {
|
|
44480
44902
|
className: css(styles.error)
|
|
44481
|
-
},
|
|
44903
|
+
}, this.context.strings.errorRendering({
|
|
44482
44904
|
error: String(this.state.renderError)
|
|
44483
44905
|
}));
|
|
44484
44906
|
}
|
|
@@ -44492,6 +44914,7 @@ class MultiRenderer extends React.Component {
|
|
|
44492
44914
|
}));
|
|
44493
44915
|
}
|
|
44494
44916
|
}
|
|
44917
|
+
MultiRenderer.contextType = PerseusI18nContext;
|
|
44495
44918
|
const styles = StyleSheet.create({
|
|
44496
44919
|
error: {
|
|
44497
44920
|
color: "red"
|
|
@@ -45587,5 +46010,5 @@ const EditorJsonify = {
|
|
|
45587
46010
|
}
|
|
45588
46011
|
};
|
|
45589
46012
|
|
|
45590
|
-
export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetId, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, violatingWidgets, allWidgets as widgets };
|
|
46013
|
+
export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetId, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
45591
46014
|
//# sourceMappingURL=index.js.map
|