@khanacademy/perseus 47.0.0 → 48.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/es/index.js +259 -187
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +249 -187
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +33 -4
- package/dist/renderer.d.ts +0 -4
- package/dist/server-item-renderer.d.ts +0 -6
- package/dist/util/test-utils.d.ts +7 -1
- package/package.json +9 -9
package/dist/es/index.js
CHANGED
|
@@ -2287,9 +2287,6 @@ function scoreExpression(userInput, rubric, strings, locale) {
|
|
|
2287
2287
|
// We matched a graded answer form, so we can now tell the user
|
|
2288
2288
|
// whether their input was correct or incorrect, and hand out
|
|
2289
2289
|
// points accordingly
|
|
2290
|
-
// TODO(eater): Seems silly to translate result to this
|
|
2291
|
-
// invalid/points thing and immediately translate it back in
|
|
2292
|
-
// ItemRenderer.scoreInput()
|
|
2293
2290
|
return {
|
|
2294
2291
|
type: "points",
|
|
2295
2292
|
earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
|
|
@@ -3240,7 +3237,7 @@ const supportsPassiveEvents = () => {
|
|
|
3240
3237
|
window.addEventListener("testPassive", null, opts);
|
|
3241
3238
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
3242
3239
|
window.removeEventListener("testPassive", null, opts);
|
|
3243
|
-
} catch (
|
|
3240
|
+
} catch (_unused) {
|
|
3244
3241
|
// Intentionally left empty!
|
|
3245
3242
|
}
|
|
3246
3243
|
return supportsPassive;
|
|
@@ -3483,7 +3480,9 @@ function changeViewportTag(contentString, callback) {
|
|
|
3483
3480
|
// finished resetting.
|
|
3484
3481
|
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
3485
3482
|
// eslint-disable-next-line no-restricted-syntax
|
|
3486
|
-
|
|
3483
|
+
if (callback != null) {
|
|
3484
|
+
setTimeout(callback, 0);
|
|
3485
|
+
}
|
|
3487
3486
|
}, 0);
|
|
3488
3487
|
}
|
|
3489
3488
|
|
|
@@ -16768,7 +16767,7 @@ class ImageLoader extends React.Component {
|
|
|
16768
16767
|
onKeyUp = e => {
|
|
16769
16768
|
// 13 is enter key, 32 is space key
|
|
16770
16769
|
if (e.keyCode === 13 || e.keyCode === 32) {
|
|
16771
|
-
imgProps.onClick
|
|
16770
|
+
imgProps.onClick == null || imgProps.onClick(e);
|
|
16772
16771
|
}
|
|
16773
16772
|
};
|
|
16774
16773
|
onKeyDown = e => {
|
|
@@ -16849,6 +16848,7 @@ const labelDataCache = {};
|
|
|
16849
16848
|
// Write our own JSONP handler because all the other ones don't do things we
|
|
16850
16849
|
// need.
|
|
16851
16850
|
const doJSONP = function doJSONP(url, options) {
|
|
16851
|
+
var _document$head2;
|
|
16852
16852
|
options = _extends({
|
|
16853
16853
|
callbackName: "callback",
|
|
16854
16854
|
success: $.noop,
|
|
@@ -16862,7 +16862,8 @@ const doJSONP = function doJSONP(url, options) {
|
|
|
16862
16862
|
|
|
16863
16863
|
// A cleanup function to run when we're done.
|
|
16864
16864
|
function cleanup() {
|
|
16865
|
-
|
|
16865
|
+
var _document$head;
|
|
16866
|
+
(_document$head = document.head) == null || _document$head.removeChild(script);
|
|
16866
16867
|
delete window[options.callbackName];
|
|
16867
16868
|
}
|
|
16868
16869
|
|
|
@@ -16880,7 +16881,7 @@ const doJSONP = function doJSONP(url, options) {
|
|
|
16880
16881
|
});
|
|
16881
16882
|
|
|
16882
16883
|
// Insert the script to start the download.
|
|
16883
|
-
document.head
|
|
16884
|
+
(_document$head2 = document.head) == null || _document$head2.appendChild(script);
|
|
16884
16885
|
};
|
|
16885
16886
|
|
|
16886
16887
|
// For offline exercises in the mobile app, we download the graphie data
|
|
@@ -17050,6 +17051,7 @@ class SvgImage extends React.Component {
|
|
|
17050
17051
|
});
|
|
17051
17052
|
};
|
|
17052
17053
|
this._handleZoomClick = e => {
|
|
17054
|
+
var _this$props$trackInte, _this$props;
|
|
17053
17055
|
const $image = $(e.target);
|
|
17054
17056
|
|
|
17055
17057
|
// It's possible that the image is already displayed at its
|
|
@@ -17066,7 +17068,7 @@ class SvgImage extends React.Component {
|
|
|
17066
17068
|
$image.width() < this.props.width || this.props.zoomToFullSizeOnMobile) {
|
|
17067
17069
|
ZoomService.handleZoomClick(e, this.props.zoomToFullSizeOnMobile);
|
|
17068
17070
|
}
|
|
17069
|
-
this.props.trackInteraction
|
|
17071
|
+
(_this$props$trackInte = (_this$props = this.props).trackInteraction) == null || _this$props$trackInte.call(_this$props);
|
|
17070
17072
|
};
|
|
17071
17073
|
this.handleUpdate = status => {
|
|
17072
17074
|
this.props.onUpdate();
|
|
@@ -19602,8 +19604,8 @@ class WidgetContainer extends React.Component {
|
|
|
19602
19604
|
});
|
|
19603
19605
|
|
|
19604
19606
|
// Note: if you add more props here, please consider whether or not
|
|
19605
|
-
// it should be auto-serialized
|
|
19606
|
-
// widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
|
|
19607
|
+
// it should be auto-serialized.
|
|
19608
|
+
// See widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
|
|
19607
19609
|
|
|
19608
19610
|
// We default to an empty object for style instead of null
|
|
19609
19611
|
// because of a strange bug where the static styles aren't applied
|
|
@@ -20582,14 +20584,6 @@ class Renderer extends React.Component {
|
|
|
20582
20584
|
this.getWidgetIds = () => {
|
|
20583
20585
|
return this.widgetIds;
|
|
20584
20586
|
};
|
|
20585
|
-
/**
|
|
20586
|
-
* @deprecated use scorePerseusItem
|
|
20587
|
-
*/
|
|
20588
|
-
this.guessAndScore = () => {
|
|
20589
|
-
const totalGuess = this.getUserInput();
|
|
20590
|
-
const totalScore = this.score();
|
|
20591
|
-
return [totalGuess, totalScore];
|
|
20592
|
-
};
|
|
20593
20587
|
// TranslationLinter callback
|
|
20594
20588
|
this.handletranslationLintErrors = lintErrors => {
|
|
20595
20589
|
if (!this._isMounted) {
|
|
@@ -21556,11 +21550,11 @@ class SimpleKeypadInput extends React.Component {
|
|
|
21556
21550
|
keypadType: KeypadType.FRACTION
|
|
21557
21551
|
}, () => {
|
|
21558
21552
|
if (_this._isMounted) {
|
|
21559
|
-
onFocus
|
|
21553
|
+
onFocus == null || onFocus();
|
|
21560
21554
|
}
|
|
21561
21555
|
});
|
|
21562
21556
|
} else {
|
|
21563
|
-
onFocus
|
|
21557
|
+
onFocus == null || onFocus();
|
|
21564
21558
|
}
|
|
21565
21559
|
},
|
|
21566
21560
|
value: value == null ? "" : "" + value
|
|
@@ -21799,9 +21793,6 @@ function scoreInputNumber(userInput, rubric, strings) {
|
|
|
21799
21793
|
// If `currentValue` is not TeX, this should be a no-op.
|
|
21800
21794
|
const currentValue = ParseTex$1(userInput.currentValue);
|
|
21801
21795
|
const result = val(currentValue);
|
|
21802
|
-
|
|
21803
|
-
// TODO(eater): Seems silly to translate result to this invalid/points
|
|
21804
|
-
// thing and immediately translate it back in ItemRenderer.scoreInput()
|
|
21805
21796
|
if (result.empty) {
|
|
21806
21797
|
return {
|
|
21807
21798
|
type: "invalid",
|
|
@@ -22168,10 +22159,6 @@ function scoreNumericInput(userInput, rubric, strings) {
|
|
|
22168
22159
|
message: (_matchedAnswer$messag = matchedAnswer == null ? void 0 : matchedAnswer.message) != null ? _matchedAnswer$messag : null,
|
|
22169
22160
|
guess: localValue
|
|
22170
22161
|
};
|
|
22171
|
-
|
|
22172
|
-
// TODO(eater): Seems silly to translate result to this
|
|
22173
|
-
// invalid/points thing and immediately translate it
|
|
22174
|
-
// back in ItemRenderer.scoreInput()
|
|
22175
22162
|
if (result.empty) {
|
|
22176
22163
|
return {
|
|
22177
22164
|
type: "invalid",
|
|
@@ -25024,7 +25011,7 @@ const scrollElementToOffset = (elementOrWindow, offset, duration = 0, callback)
|
|
|
25024
25011
|
// TODO(jeff, WEB-1378): Use Wonder Blocks Timing API
|
|
25025
25012
|
// eslint-disable-next-line no-restricted-syntax
|
|
25026
25013
|
clearInterval(scrollInterval);
|
|
25027
|
-
callback
|
|
25014
|
+
callback == null || callback();
|
|
25028
25015
|
}
|
|
25029
25016
|
}, tickLength);
|
|
25030
25017
|
};
|
|
@@ -27048,7 +27035,7 @@ class CSProgram extends React.Component {
|
|
|
27048
27035
|
let data = {};
|
|
27049
27036
|
try {
|
|
27050
27037
|
data = JSON.parse(e.originalEvent.data);
|
|
27051
|
-
} catch (
|
|
27038
|
+
} catch (_unused) {
|
|
27052
27039
|
return;
|
|
27053
27040
|
}
|
|
27054
27041
|
if (_.isUndefined(data.testsPassed)) {
|
|
@@ -27350,7 +27337,7 @@ class Dropdown extends React.Component {
|
|
|
27350
27337
|
key: "placeholder",
|
|
27351
27338
|
value: "0",
|
|
27352
27339
|
disabled: true,
|
|
27353
|
-
label: this.props.placeholder
|
|
27340
|
+
label: this.props.placeholder || " "
|
|
27354
27341
|
}), ...this.props.choices.map((choice, i) => /*#__PURE__*/React.createElement(OptionItem, {
|
|
27355
27342
|
key: String(i + 1),
|
|
27356
27343
|
value: String(i + 1),
|
|
@@ -27371,15 +27358,14 @@ class Dropdown extends React.Component {
|
|
|
27371
27358
|
}
|
|
27372
27359
|
}, this.props.visibleLabel && /*#__PURE__*/React.createElement(LabelLarge, {
|
|
27373
27360
|
tag: "label",
|
|
27374
|
-
|
|
27361
|
+
htmlFor: ids.get("dropdown")
|
|
27375
27362
|
}, this.props.visibleLabel), /*#__PURE__*/React.createElement(SingleSelect, {
|
|
27376
27363
|
id: ids.get("dropdown"),
|
|
27377
27364
|
placeholder: "",
|
|
27378
27365
|
onChange: value => this._handleChange(parseInt(value)),
|
|
27379
27366
|
selectedValue: String(this.props.selected),
|
|
27380
27367
|
disabled: this.props.apiOptions.readOnly,
|
|
27381
|
-
"aria-label": this.props.ariaLabel || this.context.strings.selectAnAnswer
|
|
27382
|
-
"aria-labelledby": ids.get("dropdown-label")
|
|
27368
|
+
"aria-label": this.props.ariaLabel || this.props.visibleLabel || this.context.strings.selectAnAnswer
|
|
27383
27369
|
// This is currently necessary for SRs to read the labels properly.
|
|
27384
27370
|
// However, WB is working on a change to add the "combobox" role to
|
|
27385
27371
|
// all dropdowns.
|
|
@@ -28105,7 +28091,6 @@ class GradedGroupSet extends React.Component {
|
|
|
28105
28091
|
};
|
|
28106
28092
|
}
|
|
28107
28093
|
shouldComponentUpdate(nextProps, nextState) {
|
|
28108
|
-
nextProps.gradedGroups;
|
|
28109
28094
|
return nextProps !== this.props || nextState !== this.state;
|
|
28110
28095
|
}
|
|
28111
28096
|
getPromptJSON() {
|
|
@@ -29574,7 +29559,7 @@ class Iframe extends React.Component {
|
|
|
29574
29559
|
let data = {};
|
|
29575
29560
|
try {
|
|
29576
29561
|
data = JSON.parse(e.originalEvent.data);
|
|
29577
|
-
} catch (
|
|
29562
|
+
} catch (_unused) {
|
|
29578
29563
|
return;
|
|
29579
29564
|
}
|
|
29580
29565
|
if (_.isUndefined(data.testsPassed)) {
|
|
@@ -30639,10 +30624,12 @@ class Graph extends React.Component {
|
|
|
30639
30624
|
this.props.onMouseDown(coord);
|
|
30640
30625
|
}, this) : undefined;
|
|
30641
30626
|
const onMouseOver = $instructionsWrapper ? function () {
|
|
30642
|
-
|
|
30627
|
+
var _$instructionsWrapper;
|
|
30628
|
+
(_$instructionsWrapper = $instructionsWrapper) == null || _$instructionsWrapper.css("opacity", invisible);
|
|
30643
30629
|
} : undefined;
|
|
30644
30630
|
const onMouseOut = $instructionsWrapper ? function () {
|
|
30645
|
-
|
|
30631
|
+
var _$instructionsWrapper2;
|
|
30632
|
+
(_$instructionsWrapper2 = $instructionsWrapper) == null || _$instructionsWrapper2.css("opacity", visible);
|
|
30646
30633
|
} : undefined;
|
|
30647
30634
|
/* eslint-enable indent */
|
|
30648
30635
|
|
|
@@ -31374,6 +31361,7 @@ function replaceOutsideTeX(mathString) {
|
|
|
31374
31361
|
const parsed = mathOnlyParser(mathString);
|
|
31375
31362
|
let result = "";
|
|
31376
31363
|
for (const piece of parsed) {
|
|
31364
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
31377
31365
|
piece.type === "math" ? result += "$" + piece.content + "$" : piece.type === "specialCharacter" ? result += escapeIfUnescaped(piece.content) : result += piece.content;
|
|
31378
31366
|
}
|
|
31379
31367
|
return `\\text{${result}}`;
|
|
@@ -32223,11 +32211,39 @@ const LegacyGrid = ({
|
|
|
32223
32211
|
return null;
|
|
32224
32212
|
};
|
|
32225
32213
|
|
|
32214
|
+
/**
|
|
32215
|
+
* The Perseus "data schema" file.
|
|
32216
|
+
*
|
|
32217
|
+
* This file, and the types in it, represents the "data schema" that Perseus
|
|
32218
|
+
* uses. The @khanacademy/perseus-editor package edits and produces objects
|
|
32219
|
+
* that conform to the types in this file. Similarly, the top-level renderers
|
|
32220
|
+
* in @khanacademy/perseus, consume objects that conform to these types.
|
|
32221
|
+
*
|
|
32222
|
+
* WARNING: This file should not import any types from elsewhere so that it is
|
|
32223
|
+
* easy to reason about changes that alter the Perseus schema. This helps
|
|
32224
|
+
* ensure that it is not changed accidentally when upgrading a dependant
|
|
32225
|
+
* package or other part of Perseus code. Note that TypeScript does type
|
|
32226
|
+
* checking via something called "structural typing". This means that as long
|
|
32227
|
+
* as the shape of a type matches, the name it goes by doesn't matter. As a
|
|
32228
|
+
* result, a `Coord` type that looks like this `[x: number, y: number]` is
|
|
32229
|
+
* _identical_, in TypeScript's eyes, to this `Vector2` type `[x: number, y:
|
|
32230
|
+
* number]`. Also, with tuples, the labels for each entry is ignored, so `[x:
|
|
32231
|
+
* number, y: number]` is compatible with `[min: number, max: number]`. The
|
|
32232
|
+
* labels are for humans, not TypeScript. :)
|
|
32233
|
+
*
|
|
32234
|
+
* If you make changes to types in this file, be very sure that:
|
|
32235
|
+
*
|
|
32236
|
+
* a) the changes are backwards compatible. If they are not, old data from
|
|
32237
|
+
* previous versions of the "schema" could become unrenderable, or worse,
|
|
32238
|
+
* introduce hard-to-diagnose bugs.
|
|
32239
|
+
* b) the parsing code (`util/parse-perseus-json/`) is updated to handle
|
|
32240
|
+
* the new format _as well as_ the old format.
|
|
32241
|
+
*/
|
|
32242
|
+
|
|
32226
32243
|
// TODO(FEI-4010): Remove `Perseus` prefix for all types here
|
|
32227
|
-
// TODO(FEI-4011): Use types generated by https://github.com/jaredly/generate-perseus-flowtypes
|
|
32228
32244
|
|
|
32229
|
-
//
|
|
32230
|
-
|
|
32245
|
+
// Same name as Mafs
|
|
32246
|
+
|
|
32231
32247
|
/**
|
|
32232
32248
|
* Our core set of Perseus widgets.
|
|
32233
32249
|
*
|
|
@@ -32271,6 +32287,7 @@ const LegacyGrid = ({
|
|
|
32271
32287
|
*
|
|
32272
32288
|
* @see {@link https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation}
|
|
32273
32289
|
*/
|
|
32290
|
+
|
|
32274
32291
|
/**
|
|
32275
32292
|
* A map of widget IDs to widget options. This is most often used as the type
|
|
32276
32293
|
* for a set of widgets defined in a `PerseusItem` but can also be useful to
|
|
@@ -32283,6 +32300,7 @@ const LegacyGrid = ({
|
|
|
32283
32300
|
* @see {@link PerseusWidgetTypes} additional widgets can be added to this map type
|
|
32284
32301
|
* by augmenting the PerseusWidgetTypes with new widget types!
|
|
32285
32302
|
*/
|
|
32303
|
+
|
|
32286
32304
|
/**
|
|
32287
32305
|
* A "PerseusItem" is a classic Perseus item. It is rendered by the
|
|
32288
32306
|
* `ServerItemRenderer` and the layout is pre-set.
|
|
@@ -32290,10 +32308,12 @@ const LegacyGrid = ({
|
|
|
32290
32308
|
* To render more complex Perseus items, see the `Item` type in the multi item
|
|
32291
32309
|
* area.
|
|
32292
32310
|
*/
|
|
32311
|
+
|
|
32293
32312
|
/**
|
|
32294
32313
|
* A "PerseusArticle" is an item that is meant to be rendered as an article.
|
|
32295
32314
|
* This item is never scored and is rendered by the `ArticleRenderer`.
|
|
32296
32315
|
*/
|
|
32316
|
+
|
|
32297
32317
|
const ItemExtras = [
|
|
32298
32318
|
// The user might benefit from using a Scientific Calculator. Provided on Khan Academy when true
|
|
32299
32319
|
"calculator",
|
|
@@ -34292,43 +34312,64 @@ function renderPointGraph(state, dispatch) {
|
|
|
34292
34312
|
};
|
|
34293
34313
|
}
|
|
34294
34314
|
function PointGraph(props) {
|
|
34295
|
-
const
|
|
34315
|
+
const {
|
|
34316
|
+
numPoints
|
|
34317
|
+
} = props.graphState;
|
|
34318
|
+
const graphConfig = useGraphConfig();
|
|
34319
|
+
const pointsRef = React.useRef([]);
|
|
34320
|
+
|
|
34321
|
+
// Dimensions to build the graph overlay for Unlimited Point.
|
|
34322
|
+
const {
|
|
34323
|
+
range: [x, y]
|
|
34324
|
+
} = graphConfig;
|
|
34325
|
+
const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
|
|
34326
|
+
|
|
34327
|
+
// This useEffect is to handle the focus snapping for Unlimited Point.
|
|
34328
|
+
React.useEffect(() => {
|
|
34329
|
+
const focusedIndex = props.graphState.focusedPointIndex;
|
|
34330
|
+
if (focusedIndex != null) {
|
|
34331
|
+
var _pointsRef$current$fo;
|
|
34332
|
+
(_pointsRef$current$fo = pointsRef.current[focusedIndex]) == null || _pointsRef$current$fo.focus();
|
|
34333
|
+
}
|
|
34334
|
+
}, [props.graphState.focusedPointIndex, pointsRef]);
|
|
34335
|
+
const statefulProps = _extends({}, props, {
|
|
34336
|
+
graphConfig,
|
|
34337
|
+
pointsRef,
|
|
34338
|
+
top,
|
|
34339
|
+
left
|
|
34340
|
+
});
|
|
34296
34341
|
if (numPoints === "unlimited") {
|
|
34297
|
-
return UnlimitedPointGraph(
|
|
34342
|
+
return UnlimitedPointGraph(statefulProps);
|
|
34298
34343
|
}
|
|
34299
|
-
return LimitedPointGraph(
|
|
34344
|
+
return LimitedPointGraph(statefulProps);
|
|
34300
34345
|
}
|
|
34301
|
-
function LimitedPointGraph(
|
|
34346
|
+
function LimitedPointGraph(statefulProps) {
|
|
34302
34347
|
const {
|
|
34303
34348
|
dispatch
|
|
34304
|
-
} =
|
|
34305
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null,
|
|
34349
|
+
} = statefulProps;
|
|
34350
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, statefulProps.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
34306
34351
|
key: i,
|
|
34307
34352
|
point: point,
|
|
34308
34353
|
sequenceNumber: i + 1,
|
|
34309
34354
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
|
|
34310
34355
|
})));
|
|
34311
34356
|
}
|
|
34312
|
-
function UnlimitedPointGraph(
|
|
34357
|
+
function UnlimitedPointGraph(statefulProps) {
|
|
34313
34358
|
const {
|
|
34314
|
-
dispatch
|
|
34315
|
-
|
|
34316
|
-
|
|
34359
|
+
dispatch,
|
|
34360
|
+
graphConfig,
|
|
34361
|
+
pointsRef,
|
|
34362
|
+
top,
|
|
34363
|
+
left
|
|
34364
|
+
} = statefulProps;
|
|
34365
|
+
const {
|
|
34366
|
+
coords
|
|
34367
|
+
} = statefulProps.graphState;
|
|
34317
34368
|
const {
|
|
34318
|
-
|
|
34369
|
+
graphDimensionsInPixels
|
|
34319
34370
|
} = graphConfig;
|
|
34320
|
-
const
|
|
34321
|
-
const
|
|
34322
|
-
const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
|
|
34323
|
-
const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
|
|
34324
|
-
const itemsRef = React.useRef([]);
|
|
34325
|
-
React.useEffect(() => {
|
|
34326
|
-
const focusedIndex = props.graphState.focusedPointIndex;
|
|
34327
|
-
if (focusedIndex != null) {
|
|
34328
|
-
var _itemsRef$current$foc;
|
|
34329
|
-
(_itemsRef$current$foc = itemsRef.current[focusedIndex]) == null || _itemsRef$current$foc.focus();
|
|
34330
|
-
}
|
|
34331
|
-
}, [props.graphState.focusedPointIndex, itemsRef]);
|
|
34371
|
+
const widthPx = graphDimensionsInPixels[0];
|
|
34372
|
+
const heightPx = graphDimensionsInPixels[1];
|
|
34332
34373
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
|
|
34333
34374
|
style: {
|
|
34334
34375
|
fill: "rgba(0,0,0,0)",
|
|
@@ -34345,13 +34386,13 @@ function UnlimitedPointGraph(props) {
|
|
|
34345
34386
|
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
34346
34387
|
dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));
|
|
34347
34388
|
}
|
|
34348
|
-
}),
|
|
34389
|
+
}), coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
34349
34390
|
key: i,
|
|
34350
34391
|
point: point,
|
|
34351
34392
|
sequenceNumber: i + 1,
|
|
34352
34393
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
|
|
34353
34394
|
ref: ref => {
|
|
34354
|
-
|
|
34395
|
+
pointsRef.current[i] = ref;
|
|
34355
34396
|
},
|
|
34356
34397
|
onFocus: () => {
|
|
34357
34398
|
dispatch(actions.pointGraph.focusPoint(i));
|
|
@@ -34398,36 +34439,42 @@ function renderPolygonGraph(state, dispatch) {
|
|
|
34398
34439
|
interactiveElementsDescription: null
|
|
34399
34440
|
};
|
|
34400
34441
|
}
|
|
34401
|
-
const
|
|
34402
|
-
const [hovered, setHovered] = React.useState(false);
|
|
34403
|
-
// This is more so required for the re-rendering that occurs when state
|
|
34404
|
-
// updates; specifically with regard to line weighting and polygon focus.
|
|
34405
|
-
const [focusVisible, setFocusVisible] = React.useState(false);
|
|
34442
|
+
const PolygonGraph = props => {
|
|
34406
34443
|
const {
|
|
34407
34444
|
dispatch
|
|
34408
34445
|
} = props;
|
|
34409
34446
|
const {
|
|
34447
|
+
numSides,
|
|
34410
34448
|
coords,
|
|
34411
|
-
showAngles,
|
|
34412
|
-
showSides,
|
|
34413
|
-
range,
|
|
34414
34449
|
snapStep,
|
|
34415
34450
|
snapTo = "grid"
|
|
34416
34451
|
} = props.graphState;
|
|
34452
|
+
const graphConfig = useGraphConfig();
|
|
34453
|
+
|
|
34454
|
+
// Ref to implement the dragging behavior on a Limited/Closed Polygon.
|
|
34455
|
+
const polygonRef = React.useRef(null);
|
|
34456
|
+
// Ref to manage dynamic focus for the points in Unlimited Polygon
|
|
34457
|
+
const pointsRef = React.useRef([]);
|
|
34458
|
+
// Ref to manage the last move time for a Limited Polygon.
|
|
34459
|
+
const lastMoveTimeRef = React.useRef(0);
|
|
34460
|
+
|
|
34461
|
+
// Dimensions to build the graph overlay for Unlimited Polgyon.
|
|
34417
34462
|
const {
|
|
34418
|
-
|
|
34419
|
-
} =
|
|
34463
|
+
range: [x, y]
|
|
34464
|
+
} = graphConfig;
|
|
34465
|
+
const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
|
|
34420
34466
|
|
|
34421
34467
|
// TODO(benchristel): can the default set of points be removed here? I don't
|
|
34422
34468
|
// think coords can be null.
|
|
34423
34469
|
const points = coords != null ? coords : [[0, 0]];
|
|
34424
|
-
|
|
34470
|
+
|
|
34471
|
+
// Logic to build the dragging experience. Primarily used by Limited Polygon.
|
|
34425
34472
|
const dragReferencePoint = points[0];
|
|
34426
34473
|
const constrain = ["angles", "sides"].includes(snapTo) ? p => p : p => snap(snapStep, p);
|
|
34427
34474
|
const {
|
|
34428
34475
|
dragging
|
|
34429
34476
|
} = useDraggable({
|
|
34430
|
-
gestureTarget:
|
|
34477
|
+
gestureTarget: polygonRef,
|
|
34431
34478
|
point: dragReferencePoint,
|
|
34432
34479
|
onMove: newPoint => {
|
|
34433
34480
|
const delta = vec.sub(newPoint, dragReferencePoint);
|
|
@@ -34435,7 +34482,61 @@ const LimitedPolygonGraph = props => {
|
|
|
34435
34482
|
},
|
|
34436
34483
|
constrainKeyboardMovement: constrain
|
|
34437
34484
|
});
|
|
34438
|
-
|
|
34485
|
+
|
|
34486
|
+
// Hover/focus state for Limited Polgyon effects.
|
|
34487
|
+
const [hovered, setHovered] = React.useState(false);
|
|
34488
|
+
// This is more so required for the re-rendering that occurs when state
|
|
34489
|
+
// updates; specifically with regard to line weighting and polygon focus.
|
|
34490
|
+
const [focusVisible, setFocusVisible] = React.useState(false);
|
|
34491
|
+
|
|
34492
|
+
// This useEffect is to handle the focus snapping for Unlimited Polygon.
|
|
34493
|
+
React.useEffect(() => {
|
|
34494
|
+
const focusedIndex = props.graphState.focusedPointIndex;
|
|
34495
|
+
if (focusedIndex != null) {
|
|
34496
|
+
var _pointsRef$current$fo;
|
|
34497
|
+
(_pointsRef$current$fo = pointsRef.current[focusedIndex]) == null || _pointsRef$current$fo.focus();
|
|
34498
|
+
}
|
|
34499
|
+
}, [props.graphState.focusedPointIndex, pointsRef]);
|
|
34500
|
+
const statefulProps = _extends({}, props, {
|
|
34501
|
+
graphConfig,
|
|
34502
|
+
polygonRef,
|
|
34503
|
+
pointsRef,
|
|
34504
|
+
lastMoveTimeRef,
|
|
34505
|
+
left,
|
|
34506
|
+
top,
|
|
34507
|
+
dragging,
|
|
34508
|
+
points,
|
|
34509
|
+
constrain,
|
|
34510
|
+
hovered,
|
|
34511
|
+
setHovered,
|
|
34512
|
+
focusVisible,
|
|
34513
|
+
setFocusVisible
|
|
34514
|
+
});
|
|
34515
|
+
return numSides === "unlimited" ? UnlimitedPolygonGraph(statefulProps) : LimitedPolygonGraph(statefulProps);
|
|
34516
|
+
};
|
|
34517
|
+
const LimitedPolygonGraph = statefulProps => {
|
|
34518
|
+
const {
|
|
34519
|
+
dispatch,
|
|
34520
|
+
hovered,
|
|
34521
|
+
setHovered,
|
|
34522
|
+
focusVisible,
|
|
34523
|
+
setFocusVisible,
|
|
34524
|
+
graphConfig,
|
|
34525
|
+
polygonRef,
|
|
34526
|
+
lastMoveTimeRef,
|
|
34527
|
+
dragging,
|
|
34528
|
+
points,
|
|
34529
|
+
constrain
|
|
34530
|
+
} = statefulProps;
|
|
34531
|
+
const {
|
|
34532
|
+
showAngles,
|
|
34533
|
+
showSides,
|
|
34534
|
+
range,
|
|
34535
|
+
snapTo = "grid"
|
|
34536
|
+
} = statefulProps.graphState;
|
|
34537
|
+
const {
|
|
34538
|
+
disableKeyboardInteraction
|
|
34539
|
+
} = graphConfig;
|
|
34439
34540
|
const lines = getLines(points);
|
|
34440
34541
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Polygon, {
|
|
34441
34542
|
points: [...points],
|
|
@@ -34473,7 +34574,7 @@ const LimitedPolygonGraph = props => {
|
|
|
34473
34574
|
points: [...points],
|
|
34474
34575
|
color: "transparent",
|
|
34475
34576
|
svgPolygonProps: {
|
|
34476
|
-
ref,
|
|
34577
|
+
ref: polygonRef,
|
|
34477
34578
|
tabIndex: disableKeyboardInteraction ? -1 : 0,
|
|
34478
34579
|
strokeWidth: TARGET_SIZE,
|
|
34479
34580
|
style: {
|
|
@@ -34485,13 +34586,13 @@ const LimitedPolygonGraph = props => {
|
|
|
34485
34586
|
// Required to remove line weighting when user clicks away
|
|
34486
34587
|
// from the focused polygon
|
|
34487
34588
|
onKeyDownCapture: () => {
|
|
34488
|
-
setFocusVisible(hasFocusVisible(
|
|
34589
|
+
setFocusVisible(hasFocusVisible(polygonRef.current));
|
|
34489
34590
|
},
|
|
34490
34591
|
// Required for lines to darken on focus
|
|
34491
|
-
onFocus: () => setFocusVisible(hasFocusVisible(
|
|
34592
|
+
onFocus: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
|
|
34492
34593
|
// Required for line weighting to update on blur. Without this,
|
|
34493
34594
|
// the user has to hover over the shape for it to update
|
|
34494
|
-
onBlur: () => setFocusVisible(hasFocusVisible(
|
|
34595
|
+
onBlur: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
|
|
34495
34596
|
className: "movable-polygon"
|
|
34496
34597
|
}
|
|
34497
34598
|
}), points.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
@@ -34503,94 +34604,85 @@ const LimitedPolygonGraph = props => {
|
|
|
34503
34604
|
const now = Date.now();
|
|
34504
34605
|
const targetFPS = 40;
|
|
34505
34606
|
const moveThresholdTime = 1000 / targetFPS;
|
|
34506
|
-
if (now -
|
|
34607
|
+
if (now - lastMoveTimeRef.current > moveThresholdTime) {
|
|
34507
34608
|
dispatch(actions.polygon.movePoint(i, destination));
|
|
34508
|
-
|
|
34609
|
+
lastMoveTimeRef.current = now;
|
|
34509
34610
|
}
|
|
34510
34611
|
}
|
|
34511
34612
|
})));
|
|
34512
34613
|
};
|
|
34513
|
-
const UnlimitedPolygonGraph =
|
|
34614
|
+
const UnlimitedPolygonGraph = statefulProps => {
|
|
34514
34615
|
const {
|
|
34515
|
-
dispatch
|
|
34516
|
-
|
|
34616
|
+
dispatch,
|
|
34617
|
+
graphConfig,
|
|
34618
|
+
left,
|
|
34619
|
+
top,
|
|
34620
|
+
pointsRef,
|
|
34621
|
+
points
|
|
34622
|
+
} = statefulProps;
|
|
34517
34623
|
const {
|
|
34518
34624
|
coords,
|
|
34519
34625
|
closedPolygon
|
|
34520
|
-
} =
|
|
34521
|
-
|
|
34626
|
+
} = statefulProps.graphState;
|
|
34627
|
+
|
|
34628
|
+
// If the polygon is closed, return a LimitedPolygon component.
|
|
34629
|
+
if (closedPolygon) {
|
|
34630
|
+
const closedPolygonProps = _extends({}, statefulProps, {
|
|
34631
|
+
numSides: coords.length
|
|
34632
|
+
});
|
|
34633
|
+
return /*#__PURE__*/React.createElement(LimitedPolygonGraph, closedPolygonProps);
|
|
34634
|
+
}
|
|
34522
34635
|
const {
|
|
34523
|
-
range: [x, y],
|
|
34524
34636
|
graphDimensionsInPixels
|
|
34525
34637
|
} = graphConfig;
|
|
34526
34638
|
const widthPx = graphDimensionsInPixels[0];
|
|
34527
34639
|
const heightPx = graphDimensionsInPixels[1];
|
|
34528
|
-
|
|
34529
|
-
|
|
34530
|
-
|
|
34531
|
-
|
|
34532
|
-
|
|
34533
|
-
|
|
34534
|
-
|
|
34535
|
-
|
|
34536
|
-
|
|
34537
|
-
|
|
34538
|
-
|
|
34640
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
|
|
34641
|
+
style: {
|
|
34642
|
+
fill: "rgba(0,0,0,0)",
|
|
34643
|
+
cursor: "crosshair"
|
|
34644
|
+
},
|
|
34645
|
+
width: widthPx,
|
|
34646
|
+
height: heightPx,
|
|
34647
|
+
x: left,
|
|
34648
|
+
y: top,
|
|
34649
|
+
onClick: event => {
|
|
34650
|
+
const elementRect = event.currentTarget.getBoundingClientRect();
|
|
34651
|
+
const x = event.clientX - elementRect.x;
|
|
34652
|
+
const y = event.clientY - elementRect.y;
|
|
34653
|
+
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
34654
|
+
dispatch(actions.polygon.addPoint(graphCoordinates[0]));
|
|
34539
34655
|
}
|
|
34540
|
-
},
|
|
34541
|
-
|
|
34542
|
-
|
|
34543
|
-
|
|
34544
|
-
|
|
34545
|
-
return /*#__PURE__*/React.createElement(LimitedPolygonGraph, closedPolygonProps);
|
|
34546
|
-
} else {
|
|
34547
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
|
|
34656
|
+
}), /*#__PURE__*/React.createElement(Polyline, {
|
|
34657
|
+
points: [...points],
|
|
34658
|
+
color: "var(--movable-line-stroke-color)",
|
|
34659
|
+
svgPolylineProps: {
|
|
34660
|
+
strokeWidth: "var(--movable-line-stroke-weight)",
|
|
34548
34661
|
style: {
|
|
34549
|
-
fill: "
|
|
34550
|
-
cursor: "crosshair"
|
|
34551
|
-
},
|
|
34552
|
-
width: widthPx,
|
|
34553
|
-
height: heightPx,
|
|
34554
|
-
x: left,
|
|
34555
|
-
y: top,
|
|
34556
|
-
onClick: event => {
|
|
34557
|
-
const elementRect = event.currentTarget.getBoundingClientRect();
|
|
34558
|
-
const x = event.clientX - elementRect.x;
|
|
34559
|
-
const y = event.clientY - elementRect.y;
|
|
34560
|
-
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
34561
|
-
dispatch(actions.polygon.addPoint(graphCoordinates[0]));
|
|
34562
|
-
}
|
|
34563
|
-
}), /*#__PURE__*/React.createElement(Polyline, {
|
|
34564
|
-
points: [...points],
|
|
34565
|
-
color: "var(--movable-line-stroke-color)",
|
|
34566
|
-
svgPolylineProps: {
|
|
34567
|
-
strokeWidth: "var(--movable-line-stroke-weight)",
|
|
34568
|
-
style: {
|
|
34569
|
-
fill: "transparent"
|
|
34570
|
-
}
|
|
34662
|
+
fill: "transparent"
|
|
34571
34663
|
}
|
|
34572
|
-
}
|
|
34573
|
-
|
|
34574
|
-
|
|
34575
|
-
|
|
34576
|
-
|
|
34577
|
-
|
|
34578
|
-
|
|
34579
|
-
|
|
34580
|
-
|
|
34581
|
-
|
|
34582
|
-
|
|
34583
|
-
|
|
34584
|
-
|
|
34585
|
-
|
|
34586
|
-
|
|
34587
|
-
|
|
34588
|
-
|
|
34589
|
-
|
|
34590
|
-
dispatch(actions.polygon.clickPoint(i));
|
|
34664
|
+
}
|
|
34665
|
+
}), coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
34666
|
+
key: i,
|
|
34667
|
+
point: point,
|
|
34668
|
+
sequenceNumber: i + 1,
|
|
34669
|
+
onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
|
|
34670
|
+
ref: ref => {
|
|
34671
|
+
pointsRef.current[i] = ref;
|
|
34672
|
+
},
|
|
34673
|
+
onFocus: () => {
|
|
34674
|
+
dispatch(actions.polygon.focusPoint(i));
|
|
34675
|
+
},
|
|
34676
|
+
onClick: () => {
|
|
34677
|
+
// If the point being clicked is the first point and
|
|
34678
|
+
// there's enough points to form a polygon (3 or more)
|
|
34679
|
+
// Close the shape before setting focus.
|
|
34680
|
+
if (i === 0 && coords.length >= 3) {
|
|
34681
|
+
dispatch(actions.polygon.closePolygon());
|
|
34591
34682
|
}
|
|
34592
|
-
|
|
34593
|
-
|
|
34683
|
+
dispatch(actions.polygon.clickPoint(i));
|
|
34684
|
+
}
|
|
34685
|
+
})));
|
|
34594
34686
|
};
|
|
34595
34687
|
function getLines(points) {
|
|
34596
34688
|
return points.map((point, i) => {
|
|
@@ -34605,17 +34697,13 @@ const hasFocusVisible = element => {
|
|
|
34605
34697
|
};
|
|
34606
34698
|
try {
|
|
34607
34699
|
return matches(":focus-visible");
|
|
34608
|
-
} catch (
|
|
34700
|
+
} catch (_unused) {
|
|
34609
34701
|
// jsdom doesn't support :focus-visible
|
|
34610
34702
|
// (see https://github.com/jsdom/jsdom/issues/3426),
|
|
34611
34703
|
// so the call to matches(":focus-visible") will fail in tests.
|
|
34612
34704
|
return matches(":focus");
|
|
34613
34705
|
}
|
|
34614
34706
|
};
|
|
34615
|
-
const PolygonGraph = props => {
|
|
34616
|
-
const numSides = props.graphState.numSides;
|
|
34617
|
-
return numSides === "unlimited" ? UnlimitedPolygonGraph(props) : LimitedPolygonGraph(props);
|
|
34618
|
-
};
|
|
34619
34707
|
|
|
34620
34708
|
function renderQuadraticGraph(state, dispatch) {
|
|
34621
34709
|
return {
|
|
@@ -39981,7 +40069,8 @@ class Sortable extends React.Component {
|
|
|
39981
40069
|
this.setState({
|
|
39982
40070
|
items
|
|
39983
40071
|
}, () => {
|
|
39984
|
-
|
|
40072
|
+
var _this$props$onMeasure, _this$props;
|
|
40073
|
+
(_this$props$onMeasure = (_this$props = this.props).onMeasure) == null || _this$props$onMeasure.call(_this$props, {
|
|
39985
40074
|
widths: widths,
|
|
39986
40075
|
heights: heights
|
|
39987
40076
|
});
|
|
@@ -40007,6 +40096,7 @@ class Sortable extends React.Component {
|
|
|
40007
40096
|
// for testing and other cases where dragging and dropping may
|
|
40008
40097
|
// not be available
|
|
40009
40098
|
moveOptionToIndex(option, index) {
|
|
40099
|
+
var _this$props$onChange, _this$props2;
|
|
40010
40100
|
const {
|
|
40011
40101
|
items
|
|
40012
40102
|
} = this.state;
|
|
@@ -40031,7 +40121,7 @@ class Sortable extends React.Component {
|
|
|
40031
40121
|
this.setState({
|
|
40032
40122
|
items: nextItems
|
|
40033
40123
|
});
|
|
40034
|
-
this.props.onChange
|
|
40124
|
+
(_this$props$onChange = (_this$props2 = this.props).onChange) == null || _this$props$onChange.call(_this$props2, {});
|
|
40035
40125
|
}
|
|
40036
40126
|
onMouseMove(key) {
|
|
40037
40127
|
// Dragging: Rearrange items based on draggable's position
|
|
@@ -40089,6 +40179,7 @@ class Sortable extends React.Component {
|
|
|
40089
40179
|
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
40090
40180
|
// eslint-disable-next-line no-restricted-syntax
|
|
40091
40181
|
const nextAnimationFrame = requestAnimationFrame(() => {
|
|
40182
|
+
var _this$props$onChange2, _this$props3;
|
|
40092
40183
|
const items = _.map(this.state.items, function (item) {
|
|
40093
40184
|
if (item.key === key) {
|
|
40094
40185
|
item.state = ItemState.ANIMATING;
|
|
@@ -40111,7 +40202,7 @@ class Sortable extends React.Component {
|
|
|
40111
40202
|
// HACK: We need to know *that* the widget changed, but currently it's
|
|
40112
40203
|
// not set up in a nice way to tell us *how* it changed, since the
|
|
40113
40204
|
// permutation of the items is stored in state.
|
|
40114
|
-
this.props.onChange
|
|
40205
|
+
(_this$props$onChange2 = (_this$props3 = this.props).onChange) == null || _this$props$onChange2.call(_this$props3, {});
|
|
40115
40206
|
});
|
|
40116
40207
|
|
|
40117
40208
|
// @ts-expect-error - TS2339 - Property 'animationFrameRequest' does not exist on type 'Sortable'.
|
|
@@ -42789,11 +42880,12 @@ class Card extends React.Component {
|
|
|
42789
42880
|
event.preventDefault();
|
|
42790
42881
|
const loc = Util.extractPointerLocation(event);
|
|
42791
42882
|
if (loc) {
|
|
42883
|
+
var _this$props$onMouseDo, _this$props;
|
|
42792
42884
|
this.setState({
|
|
42793
42885
|
dragging: true
|
|
42794
42886
|
});
|
|
42795
42887
|
this.bindMouseMoveUp();
|
|
42796
|
-
this.props.onMouseDown
|
|
42888
|
+
(_this$props$onMouseDo = (_this$props = this.props).onMouseDown) == null || _this$props$onMouseDo.call(_this$props, loc, this);
|
|
42797
42889
|
}
|
|
42798
42890
|
};
|
|
42799
42891
|
this.onMouseMove = event => {
|
|
@@ -42803,18 +42895,20 @@ class Card extends React.Component {
|
|
|
42803
42895
|
event.preventDefault();
|
|
42804
42896
|
const loc = Util.extractPointerLocation(event);
|
|
42805
42897
|
if (loc) {
|
|
42806
|
-
|
|
42898
|
+
var _this$props$onMouseMo, _this$props2;
|
|
42899
|
+
(_this$props$onMouseMo = (_this$props2 = this.props).onMouseMove) == null || _this$props$onMouseMo.call(_this$props2, loc);
|
|
42807
42900
|
}
|
|
42808
42901
|
};
|
|
42809
42902
|
this.onMouseUp = event => {
|
|
42810
42903
|
event.preventDefault();
|
|
42811
42904
|
const loc = Util.extractPointerLocation(event);
|
|
42812
42905
|
if (loc) {
|
|
42906
|
+
var _this$props$onMouseUp, _this$props3;
|
|
42813
42907
|
this.setState({
|
|
42814
42908
|
dragging: false
|
|
42815
42909
|
});
|
|
42816
42910
|
this.unbindMouseMoveUp();
|
|
42817
|
-
this.props.onMouseUp
|
|
42911
|
+
(_this$props$onMouseUp = (_this$props3 = this.props).onMouseUp) == null || _this$props$onMouseUp.call(_this$props3, loc);
|
|
42818
42912
|
}
|
|
42819
42913
|
};
|
|
42820
42914
|
}
|
|
@@ -45076,7 +45170,7 @@ const init = function init() {
|
|
|
45076
45170
|
|
|
45077
45171
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45078
45172
|
const libName = "@khanacademy/perseus";
|
|
45079
|
-
const libVersion = "
|
|
45173
|
+
const libVersion = "48.0.0";
|
|
45080
45174
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45081
45175
|
|
|
45082
45176
|
const apiVersion = {
|
|
@@ -45838,28 +45932,6 @@ class ServerItemRenderer extends React.Component {
|
|
|
45838
45932
|
return this.questionRenderer.getUserInputMap();
|
|
45839
45933
|
}
|
|
45840
45934
|
|
|
45841
|
-
/**
|
|
45842
|
-
* Grades the item.
|
|
45843
|
-
*
|
|
45844
|
-
* @deprecated use scorePerseusItem
|
|
45845
|
-
*/
|
|
45846
|
-
scoreInput() {
|
|
45847
|
-
const guess = this.getUserInput();
|
|
45848
|
-
const score = scorePerseusItem(this.props.item.question, guess, this.context.strings, this.context.locale);
|
|
45849
|
-
|
|
45850
|
-
// Continue to include an empty guess for the now defunct answer area.
|
|
45851
|
-
// TODO(alex): Check whether we rely on the format here for
|
|
45852
|
-
// analyzing ProblemLogs. If not, remove this layer.
|
|
45853
|
-
const maxCompatGuess = [this.questionRenderer.getUserInput(), []];
|
|
45854
|
-
const keScore = keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
|
|
45855
|
-
const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
|
|
45856
|
-
this.setState({
|
|
45857
|
-
questionCompleted: keScore.correct,
|
|
45858
|
-
questionHighlightedWidgets: emptyQuestionAreaWidgets
|
|
45859
|
-
});
|
|
45860
|
-
return keScore;
|
|
45861
|
-
}
|
|
45862
|
-
|
|
45863
45935
|
/**
|
|
45864
45936
|
* Returns an array of all widget IDs in the order they occur in
|
|
45865
45937
|
* the question content.
|