@khanacademy/perseus 38.0.1 → 39.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/components/math-input.d.ts +1 -1
  2. package/dist/components/sortable.d.ts +1 -1
  3. package/dist/definition-context.d.ts +1 -1
  4. package/dist/es/index.js +121 -226
  5. package/dist/es/index.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.js +96 -202
  8. package/dist/index.js.map +1 -1
  9. package/dist/interactive2/movable-point.d.ts +1 -1
  10. package/dist/multi-items/prop-type-builders.d.ts +0 -6
  11. package/dist/perseus-api.d.ts +3 -1
  12. package/dist/perseus-types.d.ts +3 -42
  13. package/dist/renderer.d.ts +1 -2
  14. package/dist/server-item-renderer.d.ts +3 -2
  15. package/dist/sigfigs.d.ts +0 -16
  16. package/dist/styles/constants.d.ts +0 -18
  17. package/dist/types.d.ts +4 -6
  18. package/dist/util/math.d.ts +0 -1
  19. package/dist/util.d.ts +2 -1
  20. package/dist/validation.types.d.ts +17 -16
  21. package/dist/widgets/__shared__/noop-validator.d.ts +1 -1
  22. package/dist/widgets/definition/definition.d.ts +1 -2
  23. package/dist/widgets/explanation/explanation.d.ts +1 -2
  24. package/dist/widgets/expression/expression.d.ts +25 -12
  25. package/dist/widgets/iframe/iframe-validator.d.ts +1 -1
  26. package/dist/widgets/image/image.d.ts +1 -2
  27. package/dist/widgets/input-number/input-number.d.ts +0 -1
  28. package/dist/widgets/interaction/interaction.d.ts +1 -2
  29. package/dist/widgets/interactive-graphs/graphs/components/movable-line.d.ts +0 -11
  30. package/dist/widgets/interactive-graphs/graphs/point.d.ts +0 -2
  31. package/dist/widgets/interactive-graphs/graphs/use-transform.d.ts +0 -1
  32. package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +2 -1
  33. package/dist/widgets/interactive-graphs/protractor.d.ts +0 -2
  34. package/dist/widgets/label-image/answer-choices.d.ts +1 -1
  35. package/dist/widgets/label-image/label-image.d.ts +12 -28
  36. package/dist/widgets/matrix/matrix-validator.d.ts +1 -1
  37. package/dist/widgets/matrix/matrix.d.ts +0 -1
  38. package/dist/widgets/measurer/measurer.d.ts +3 -15
  39. package/dist/widgets/molecule/molecule.d.ts +0 -21
  40. package/dist/widgets/number-line/number-line.d.ts +0 -1
  41. package/dist/widgets/numeric-input/numeric-input.d.ts +0 -1
  42. package/dist/widgets/orderer/orderer.d.ts +1 -1
  43. package/dist/widgets/passage/passage.d.ts +1 -2
  44. package/dist/widgets/passage-ref/passage-ref.d.ts +1 -2
  45. package/dist/widgets/radio/base-radio.d.ts +5 -10
  46. package/dist/widgets/radio/focus-ring.d.ts +4 -11
  47. package/dist/widgets/table/table.d.ts +0 -1
  48. package/dist/widgets/video/video.d.ts +1 -2
  49. package/package.json +3 -3
  50. package/dist/util/fix-passage-refs.d.ts +0 -3
  51. package/dist/widgets/radio/choice.jsx.fixture.d.ts +0 -1
package/dist/es/index.js CHANGED
@@ -2402,11 +2402,6 @@ class Expression extends React.Component {
2402
2402
  // @ts-expect-error - TS2339 - Property 'insert' does not exist on type 'ReactInstance'.
2403
2403
  this.refs.input.insert(keyPressed);
2404
2404
  }
2405
- // TODO(Nicole): I believe this is going away and won't be needed anymore
2406
- getGrammarTypeForPath(inputPath) {
2407
- /* c8 ignore next */
2408
- return "expression";
2409
- }
2410
2405
  setInputValue(path, newValue, cb) {
2411
2406
  this.props.onChange({
2412
2407
  value: newValue
@@ -2878,7 +2873,9 @@ function flattenScores(widgetScoreMap) {
2878
2873
  function isCorrect(score) {
2879
2874
  return score.type === "points" && score.earned >= score.total;
2880
2875
  }
2881
- function keScoreFromPerseusScore(score, guess, state) {
2876
+ function keScoreFromPerseusScore(score,
2877
+ // It's weird, but this is what we're passing it
2878
+ guess, state) {
2882
2879
  if (score.type === "points") {
2883
2880
  return {
2884
2881
  empty: false,
@@ -3892,36 +3889,14 @@ const ZoomService = new ZoomServiceClass();
3892
3889
  const baseFontFamily$1 = "'Lato', sans-serif";
3893
3890
  // @boldFontFamily: "Lato-Bold", "Lato", sans-serif;
3894
3891
  const boldFontFamily = "'Lato-Bold', 'Lato', sans-serif";
3895
- // @green: #76a005;
3896
- const green$1 = "#76A005";
3897
3892
  // @kaGreen: #71b307;
3898
3893
  const kaGreen$1 = "#71B307";
3899
- // @blue: #1c758a;
3900
- const blue$1 = "#1C758A";
3901
- // @gray: #aaa;
3902
- const gray$1 = "#AAAAAA";
3903
- // @red: #ffbaba;
3904
- const red$1 = "#FFBABA";
3905
- // @grayLight: #aaa;
3906
- const grayLight$1 = "#AAAAAA";
3907
- // @grayLighter: #ddd;
3908
- const grayLighter$1 = "#DDDDDD";
3909
- // @learnstormBlue: #4898fc;
3910
- const learnstormBlue$1 = "#4898FC";
3911
3894
  const white$1 = "#FFFFFF";
3912
- const gray98 = "#FAFAFA";
3913
3895
  const gray97 = "#F6F7F7";
3914
- const gray95 = "#F0F1F2";
3915
- const gray90 = "#E3E5E6";
3916
3896
  const gray85 = "#D6D8DA";
3917
3897
  const gray76 = "#BABEC2";
3918
3898
  const gray68 = "#888D93";
3919
- const gray41 = "#626569";
3920
- const gray25 = "#3B3e40";
3921
3899
  const gray17 = "#21242c";
3922
- const black$1 = "#000000";
3923
- const warning1 = "#F86700";
3924
- const warning3 = "#C75300";
3925
3900
 
3926
3901
  // @pure-sm-min: 568px;
3927
3902
  const pureSmMin$2 = "568px";
@@ -3941,16 +3916,10 @@ const pureMdMax$2 = "1023px";
3941
3916
  const pureLgMax$2 = "1279px";
3942
3917
  // @tableBackgroundAccent: #f9f9f9; // for striping
3943
3918
  const tableBackgroundAccent$1 = "#F9F9F9";
3944
- // @zIndexScratchPad: 1;
3945
- const zIndexScratchPad = 1;
3946
3919
  // @zIndexAboveScratchpad: @zIndexScratchPad + 1;
3947
3920
  const zIndexAboveScratchpad$1 = 2;
3948
3921
  // @zIndexInteractiveComponent: @zIndexAboveScratchpad + 1;
3949
3922
  const zIndexInteractiveComponent$1 = 3;
3950
- // @zIndexCurrentlyDragging: @zIndexInteractiveComponent + 1;
3951
- const zIndexCurrentlyDragging = 4;
3952
- // @zIndexCalculator: @zIndexCurrentlyDragging + 1;
3953
- const zIndexCalculator = 5;
3954
3923
  // @phoneMargin: 16px;
3955
3924
  const phoneMargin = 16;
3956
3925
  const negativePhoneMargin = -16;
@@ -3978,28 +3947,13 @@ var constants$1 = /*#__PURE__*/Object.freeze({
3978
3947
  __proto__: null,
3979
3948
  baseFontFamily: baseFontFamily$1,
3980
3949
  boldFontFamily: boldFontFamily,
3981
- green: green$1,
3982
3950
  kaGreen: kaGreen$1,
3983
- blue: blue$1,
3984
- gray: gray$1,
3985
- red: red$1,
3986
- grayLight: grayLight$1,
3987
- grayLighter: grayLighter$1,
3988
- learnstormBlue: learnstormBlue$1,
3989
3951
  white: white$1,
3990
- gray98: gray98,
3991
3952
  gray97: gray97,
3992
- gray95: gray95,
3993
- gray90: gray90,
3994
3953
  gray85: gray85,
3995
3954
  gray76: gray76,
3996
3955
  gray68: gray68,
3997
- gray41: gray41,
3998
- gray25: gray25,
3999
3956
  gray17: gray17,
4000
- black: black$1,
4001
- warning1: warning1,
4002
- warning3: warning3,
4003
3957
  pureSmMin: pureSmMin$2,
4004
3958
  pureMdMin: pureMdMin$2,
4005
3959
  pureLgMin: pureLgMin$2,
@@ -4009,11 +3963,8 @@ var constants$1 = /*#__PURE__*/Object.freeze({
4009
3963
  pureMdMax: pureMdMax$2,
4010
3964
  pureLgMax: pureLgMax$2,
4011
3965
  tableBackgroundAccent: tableBackgroundAccent$1,
4012
- zIndexScratchPad: zIndexScratchPad,
4013
3966
  zIndexAboveScratchpad: zIndexAboveScratchpad$1,
4014
3967
  zIndexInteractiveComponent: zIndexInteractiveComponent$1,
4015
- zIndexCurrentlyDragging: zIndexCurrentlyDragging,
4016
- zIndexCalculator: zIndexCalculator,
4017
3968
  phoneMargin: phoneMargin,
4018
3969
  negativePhoneMargin: negativePhoneMargin,
4019
3970
  hintBorderWidth: hintBorderWidth,
@@ -19756,6 +19707,7 @@ class Renderer extends React.Component {
19756
19707
  onBlur: _.partial(this._onWidgetBlur, id),
19757
19708
  findWidgets: this.findWidgets,
19758
19709
  reviewModeRubric: reviewModeRubric,
19710
+ reviewMode: this.props.reviewMode,
19759
19711
  onChange: (newProps, cb, silent = false) => {
19760
19712
  this._setWidgetProps(id, newProps, cb, silent);
19761
19713
  },
@@ -20435,14 +20387,6 @@ class Renderer extends React.Component {
20435
20387
  return ReactDOM__default.findDOMNode(widget);
20436
20388
  }
20437
20389
  };
20438
- this.getGrammarTypeForPath = path => {
20439
- // @ts-expect-error - TS2345 - Argument of type 'FocusPath' is not assignable to parameter of type 'List<any>'.
20440
- const widgetId = _.first(path);
20441
- // @ts-expect-error - TS2345 - Argument of type 'FocusPath' is not assignable to parameter of type 'List<any>'.
20442
- const interWidgetPath = _.rest(path);
20443
- const widget = this.getWidgetInstance(widgetId);
20444
- return widget == null || widget.getGrammarTypeForPath == null ? void 0 : widget.getGrammarTypeForPath(interWidgetPath);
20445
- };
20446
20390
  this.getInputPaths = () => {
20447
20391
  const inputPaths = [];
20448
20392
  _.each(this.widgetIds, widgetId => {
@@ -20582,11 +20526,6 @@ class Renderer extends React.Component {
20582
20526
  this.getWidgetIds = () => {
20583
20527
  return this.widgetIds;
20584
20528
  };
20585
- this.guessAndScore = () => {
20586
- const totalGuess = this.getUserInput();
20587
- const totalScore = this.score();
20588
- return [totalGuess, totalScore];
20589
- };
20590
20529
  this.examples = () => {
20591
20530
  const widgetIds = this.widgetIds;
20592
20531
  const examples = _.compact(_.map(widgetIds, widgetId => {
@@ -20808,6 +20747,11 @@ class Renderer extends React.Component {
20808
20747
  const combinedScore = Util.flattenScores(scores);
20809
20748
  return combinedScore;
20810
20749
  }
20750
+ guessAndScore() {
20751
+ const totalGuess = this.getUserInput();
20752
+ const totalScore = this.score();
20753
+ return [totalGuess, totalScore];
20754
+ }
20811
20755
  render() {
20812
20756
  const apiOptions = this.getApiOptions();
20813
20757
  if (this.reuseMarkdown) {
@@ -21024,7 +20968,17 @@ TextInput.defaultProps = {
21024
20968
  disabled: false
21025
20969
  };
21026
20970
 
21027
- /* eslint-disable react/no-unsafe */
20971
+ function _objectWithoutPropertiesLoose(r, e) {
20972
+ if (null == r) return {};
20973
+ var t = {};
20974
+ for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
20975
+ if (e.includes(n)) continue;
20976
+ t[n] = r[n];
20977
+ }
20978
+ return t;
20979
+ }
20980
+
20981
+ const _excluded$5 = ["position", "visibility", "left", "top"];
21028
20982
  const zIndex = 10;
21029
20983
  let HorizontalDirection = /*#__PURE__*/function (HorizontalDirection) {
21030
20984
  HorizontalDirection["Left"] = "left";
@@ -21068,7 +21022,14 @@ const Triangle = props => {
21068
21022
  }
21069
21023
  });
21070
21024
  };
21071
- const TooltipArrow = props => {
21025
+ const TooltipArrow = _ref => {
21026
+ let {
21027
+ position = "relative",
21028
+ visibility = "visible",
21029
+ left = 100,
21030
+ top = 0
21031
+ } = _ref,
21032
+ props = _objectWithoutPropertiesLoose(_ref, _excluded$5);
21072
21033
  // TODO(aria): Think about adding a box-shadow to the triangle here
21073
21034
  // See http://css-tricks.com/triangle-with-shadow/
21074
21035
 
@@ -21079,10 +21040,10 @@ const TooltipArrow = props => {
21079
21040
  return /*#__PURE__*/React.createElement("div", {
21080
21041
  style: {
21081
21042
  display: "block",
21082
- position: props.position,
21083
- visibility: props.visibility,
21084
- left: props.left,
21085
- top: props["top"],
21043
+ position: position,
21044
+ visibility: visibility,
21045
+ left: left,
21046
+ top: top,
21086
21047
  width: props.width + 2,
21087
21048
  height: props.height + 1,
21088
21049
  marginTop: -1,
@@ -21108,12 +21069,6 @@ const TooltipArrow = props => {
21108
21069
  height: props.height
21109
21070
  }));
21110
21071
  };
21111
- TooltipArrow.defaultProps = {
21112
- position: "relative",
21113
- visibility: "visible",
21114
- left: 0,
21115
- top: 0
21116
- };
21117
21072
  const VERTICAL_CORNERS = {
21118
21073
  top: {
21119
21074
  top: "-100%"
@@ -21396,16 +21351,6 @@ InputWithExamples.defaultProps = {
21396
21351
  className: ""
21397
21352
  };
21398
21353
 
21399
- function _objectWithoutPropertiesLoose(r, e) {
21400
- if (null == r) return {};
21401
- var t = {};
21402
- for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
21403
- if (e.includes(n)) continue;
21404
- t[n] = r[n];
21405
- }
21406
- return t;
21407
- }
21408
-
21409
21354
  const _excluded$4 = ["keypadElement", "onFocus", "value"];
21410
21355
  class SimpleKeypadInput extends React.Component {
21411
21356
  constructor(...args) {
@@ -21787,12 +21732,6 @@ class InputNumber extends React.Component {
21787
21732
  /* c8 ignore next */
21788
21733
  return [[]];
21789
21734
  };
21790
- // Note: We believe that this isn't used anywhere but are leaving it during
21791
- // the initial refactor
21792
- this.getGrammarTypeForPath = path => {
21793
- /* c8 ignore next */
21794
- return "number";
21795
- };
21796
21735
  this.setInputValue = (path, newValue, cb) => {
21797
21736
  this.props.onChange({
21798
21737
  currentValue: newValue
@@ -21836,17 +21775,13 @@ class InputNumber extends React.Component {
21836
21775
  }
21837
21776
  return input;
21838
21777
  }
21839
- // HACK(johnsullivan): Create a function with shared logic between
21840
- // this and NumericInput.
21841
- // TODO(jeremy): Deprecate this widget and prefer numeric-input.
21842
- const rubric = this.props.reviewModeRubric;
21843
21778
 
21844
21779
  // Note: This is _very_ similar to what `numeric-input.jsx` does. If
21845
21780
  // you modify this, double-check if you also need to modify that
21846
21781
  // component.
21847
21782
  const inputStyles = [styles$A.default, this.props.size === "small" ? styles$A.small : null, this.props.rightAlign ? styles$A.rightAlign : styles$A.leftAlign];
21848
21783
  // Unanswered
21849
- if (rubric && !this.props.currentValue) {
21784
+ if (this.props.reviewMode && !this.props.currentValue) {
21850
21785
  inputStyles.push(styles$A.answerStateUnanswered);
21851
21786
  }
21852
21787
  return /*#__PURE__*/React.createElement(InputWithExamples
@@ -21993,6 +21928,7 @@ function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
21993
21928
  return value;
21994
21929
  }
21995
21930
  function numericInputValidator(userInput, rubric, strings) {
21931
+ var _matchedAnswer$messag;
21996
21932
  const defaultAnswerForms = answerFormButtons.map(e => e["value"])
21997
21933
  // Don't default to validating the answer as a pi answer
21998
21934
  // if answerForm isn't set on the answer
@@ -22025,48 +21961,34 @@ function numericInputValidator(userInput, rubric, strings) {
22025
21961
  // We may have received TeX; try to parse it before grading.
22026
21962
  // If `currentValue` is not TeX, this should be a no-op.
22027
21963
  const currentValue = ParseTex(userInput.currentValue);
22028
- const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
22029
- const normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1);
22030
-
22031
- // Look through all correct answers for one that matches either
22032
- // precisely or approximately and return the appropriate message:
22033
- // - if precise, return the message that the answer came with
22034
- // - if it needs to be simplified, etc., show that message
22035
- let result = correctAnswers.map(answer => {
22036
- // The coefficient is an attribute of the widget
22037
- let localValue = currentValue;
22038
- if (rubric.coefficient) {
22039
- if (!localValue) {
22040
- localValue = 1;
22041
- } else if (localValue === "-") {
22042
- localValue = -1;
22043
- }
22044
- }
22045
- const validate = createValidator(answer);
22046
- return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
22047
- }).find(match => match.correct || match.empty);
22048
- if (!result) {
22049
- // Otherwise, if the guess is not correct
22050
- const otherAnswers = [].concat(
22051
- // @ts-expect-error - TS2769 - No overload matches this call.
22052
- rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong"));
22053
-
22054
- // Look through all other answers and if one matches either
22055
- // precisely or approximately return the answer's message
22056
- const match = otherAnswers.find(answer => {
22057
- const validate = createValidator(answer);
22058
- return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
21964
+ const normalizedAnswerExpected = rubric.answers.filter(answer => answer.status === "correct").every(answer => Math.abs(answer.value) <= 1);
21965
+
21966
+ // The coefficient is an attribute of the widget
21967
+ let localValue = currentValue;
21968
+ if (rubric.coefficient) {
21969
+ if (!localValue) {
21970
+ localValue = 1;
21971
+ } else if (localValue === "-") {
21972
+ localValue = -1;
21973
+ }
21974
+ }
21975
+ const matchedAnswer = rubric.answers.map(answer => {
21976
+ const validateFn = createValidator(answer);
21977
+ const score = validateFn(maybeParsePercentInput(localValue, normalizedAnswerExpected));
21978
+ return _extends({}, answer, {
21979
+ score
22059
21980
  });
22060
- result = {
22061
- // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22062
- empty: match ? match.status === "ungraded" : false,
22063
- // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22064
- correct: match ? match.status === "correct" : false,
22065
- // @ts-expect-error - TS2339 - Property 'message' does not exist on type 'never'.
22066
- message: match ? match.message : null,
22067
- guess: currentValue
22068
- };
22069
- }
21981
+ }).find(answer => {
21982
+ // NOTE: "answer.score.correct" indicates a match via the validate function.
21983
+ // It does NOT indicate that the answer itself is correct.
21984
+ return answer.score.correct || answer.status === "correct" && answer.score.empty;
21985
+ });
21986
+ const result = (matchedAnswer == null ? void 0 : matchedAnswer.status) === "correct" ? matchedAnswer.score : {
21987
+ empty: (matchedAnswer == null ? void 0 : matchedAnswer.status) === "ungraded",
21988
+ correct: (matchedAnswer == null ? void 0 : matchedAnswer.status) === "correct",
21989
+ message: (_matchedAnswer$messag = matchedAnswer == null ? void 0 : matchedAnswer.message) != null ? _matchedAnswer$messag : null,
21990
+ guess: localValue
21991
+ };
22070
21992
 
22071
21993
  // TODO(eater): Seems silly to translate result to this
22072
21994
  // invalid/points thing and immediately translate it
@@ -22149,10 +22071,6 @@ class NumericInput extends React.Component {
22149
22071
  /* c8 ignore next */
22150
22072
  return [[]];
22151
22073
  };
22152
- this.getGrammarTypeForPath = inputPath => {
22153
- /* c8 ignore next */
22154
- return "number";
22155
- };
22156
22074
  this.setInputValue = (path, newValue, cb) => {
22157
22075
  /* c8 ignore next */
22158
22076
  this.props.onChange({
@@ -22339,7 +22257,7 @@ var NumericInput$1 = {
22339
22257
  * Several widgets don't have "right"/"wrong" validation logic,
22340
22258
  * so this just says to move on past those widgets
22341
22259
  *
22342
- * TODO(LEMS-2396) widgets that use this probably shouldn't have any
22260
+ * TODO(LEMS-2543) widgets that use this probably shouldn't have any
22343
22261
  * validation logic and the thing validating an exercise
22344
22262
  * should just know to skip these
22345
22263
  */
@@ -24401,7 +24319,7 @@ class Passage extends React.Component {
24401
24319
  const enabled = this.state.stylesAreApplied;
24402
24320
 
24403
24321
  // Highlights are read-only in review mode.
24404
- const editable = !this.props.reviewModeRubric;
24322
+ const editable = !this.props.reviewMode;
24405
24323
  return /*#__PURE__*/React.createElement(HighlightableContent, {
24406
24324
  editable: editable,
24407
24325
  enabled: enabled,
@@ -25042,13 +24960,12 @@ function getA11yText(letter, checked, correct, crossedOut, showCorrectness, stri
25042
24960
  /**
25043
24961
  * Renders a circular selection ring around the child.
25044
24962
  */
25045
- const FocusRing = function FocusRing(props) {
25046
- const {
25047
- visible,
25048
- color,
25049
- children,
25050
- multipleSelect
25051
- } = props;
24963
+ const FocusRing = function FocusRing({
24964
+ visible = true,
24965
+ color = kaGreen$1,
24966
+ multipleSelect = false,
24967
+ children
24968
+ }) {
25052
24969
  const borderColor = visible ? color : "transparent";
25053
24970
  const borderRadius = multipleSelect ? 5 : "50%";
25054
24971
  const style = {
@@ -25061,11 +24978,6 @@ const FocusRing = function FocusRing(props) {
25061
24978
  style: style
25062
24979
  }, children);
25063
24980
  };
25064
- FocusRing.defaultProps = {
25065
- visible: true,
25066
- color: kaGreen$1,
25067
- multipleSelect: false
25068
- };
25069
24981
  const styles$v = StyleSheet.create({
25070
24982
  ring: {
25071
24983
  margin: "auto",
@@ -25598,14 +25510,14 @@ var Choice$1 = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.
25598
25510
  forwardedRef: ref
25599
25511
  })));
25600
25512
 
25601
- const _excluded$3 = ["showContent", "content", "forwardedRef"];
25602
- const ChoiceNoneAbove = function ChoiceNoneAbove(props) {
25603
- const {
25604
- showContent,
25513
+ const _excluded$3 = ["content", "forwardedRef", "showContent"];
25514
+ const ChoiceNoneAbove = function ChoiceNoneAbove(_ref) {
25515
+ let {
25605
25516
  content,
25606
- forwardedRef
25607
- } = props,
25608
- rest = _objectWithoutPropertiesLoose(props, _excluded$3);
25517
+ forwardedRef,
25518
+ showContent = true
25519
+ } = _ref,
25520
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$3);
25609
25521
  const {
25610
25522
  strings
25611
25523
  } = usePerseusI18n();
@@ -25632,9 +25544,6 @@ const ChoiceNoneAbove = function ChoiceNoneAbove(props) {
25632
25544
  ref: forwardedRef
25633
25545
  }));
25634
25546
  };
25635
- ChoiceNoneAbove.defaultProps = {
25636
- showContent: true
25637
- };
25638
25547
  var ChoiceNoneAbove$1 = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.createElement(ChoiceNoneAbove, _extends({}, props, {
25639
25548
  forwardedRef: ref
25640
25549
  })));
@@ -25656,19 +25565,20 @@ function getInstructionsText(multipleSelect, countChoices, numCorrect, strings)
25656
25565
  }
25657
25566
  return strings.chooseOneAnswer;
25658
25567
  }
25659
- const BaseRadio = function BaseRadio(props) {
25660
- const {
25661
- apiOptions,
25662
- reviewModeRubric,
25663
- choices,
25664
- editMode,
25665
- multipleSelect,
25666
- labelWrap,
25667
- countChoices,
25668
- numCorrect,
25669
- isLastUsedWidget,
25670
- registerFocusFunction
25671
- } = props;
25568
+ const BaseRadio = function BaseRadio({
25569
+ apiOptions,
25570
+ reviewModeRubric,
25571
+ reviewMode,
25572
+ choices,
25573
+ editMode = false,
25574
+ multipleSelect = false,
25575
+ labelWrap,
25576
+ countChoices,
25577
+ numCorrect,
25578
+ isLastUsedWidget,
25579
+ onChange,
25580
+ registerFocusFunction
25581
+ }) {
25672
25582
  const {
25673
25583
  strings
25674
25584
  } = usePerseusI18n();
@@ -25725,12 +25635,6 @@ const BaseRadio = function BaseRadio(props) {
25725
25635
  // contains a boolean value specifying the new checked and crossed-out
25726
25636
  // value of this choice.
25727
25637
  function updateChoice(choiceIndex, newValues) {
25728
- const {
25729
- multipleSelect,
25730
- choices,
25731
- onChange
25732
- } = props;
25733
-
25734
25638
  // Get the baseline `checked` values. If we're checking a new answer
25735
25639
  // and multiple-select is not on, we should clear all choices to be
25736
25640
  // unchecked. Otherwise, we should copy the old checked values.
@@ -25772,7 +25676,6 @@ const BaseRadio = function BaseRadio(props) {
25772
25676
  });
25773
25677
 
25774
25678
  // some commonly used shorthands
25775
- const reviewMode = !!reviewModeRubric;
25776
25679
  const isMobile = apiOptions.isMobile;
25777
25680
  const firstChoiceHighlighted = choices[0].highlighted;
25778
25681
  const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
@@ -25898,10 +25801,6 @@ const BaseRadio = function BaseRadio(props) {
25898
25801
  className: css(styles$p.responsiveContainer)
25899
25802
  }, fieldset);
25900
25803
  };
25901
- BaseRadio.defaultProps = {
25902
- editMode: false,
25903
- multipleSelect: false
25904
- };
25905
25804
  const styles$p = StyleSheet.create({
25906
25805
  instructions: {
25907
25806
  display: "block",
@@ -26000,7 +25899,7 @@ function radioValidator(userInput, rubric, strings) {
26000
25899
  };
26001
25900
  }
26002
25901
 
26003
- // TODO: should numCorrect actually be on the rubric
25902
+ // TODO(LEMS-2541): should numCorrect actually be on the rubric
26004
25903
  // instead of the userInput?
26005
25904
  if (userInput.numCorrect && userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
26006
25905
  return {
@@ -26010,7 +25909,7 @@ function radioValidator(userInput, rubric, strings) {
26010
25909
  // If NOTA and some other answer are checked, ...
26011
25910
  }
26012
25911
 
26013
- // TODO: should noneOfTheAboveSelected be replaced with a
25912
+ // TODO(LEMS-2541): should noneOfTheAboveSelected be replaced with a
26014
25913
  // combination of choicesSelected and noneOfTheAboveIndex?
26015
25914
  if (userInput.noneOfTheAboveSelected && numSelected > 1) {
26016
25915
  return {
@@ -26020,7 +25919,7 @@ function radioValidator(userInput, rubric, strings) {
26020
25919
  }
26021
25920
  const correct = userInput.choicesSelected.every((selected, i) => {
26022
25921
  let isCorrect;
26023
- // TODO: should noneOfTheAboveIndex actually be on the rubric
25922
+ // TODO(LEMS-2541): should noneOfTheAboveIndex actually be on the rubric
26024
25923
  // instead of the userInput?
26025
25924
  if (userInput.noneOfTheAboveIndex === i) {
26026
25925
  isCorrect = rubric.choices.every((choice, j) => {
@@ -26201,7 +26100,6 @@ class Radio$1 extends React.Component {
26201
26100
  let noneOfTheAboveSelected = false;
26202
26101
  const choiceStates = props.choiceStates;
26203
26102
  const choicesSelected = choiceStates.map(() => false);
26204
- const countChoices = props.countChoices;
26205
26103
  const numCorrect = props.numCorrect;
26206
26104
  for (let i = 0; i < choicesSelected.length; i++) {
26207
26105
  const index = props.choices[i].originalIndex;
@@ -26215,7 +26113,6 @@ class Radio$1 extends React.Component {
26215
26113
  }
26216
26114
  }
26217
26115
  return {
26218
- countChoices,
26219
26116
  choicesSelected,
26220
26117
  numCorrect,
26221
26118
  noneOfTheAboveIndex,
@@ -26231,7 +26128,6 @@ class Radio$1 extends React.Component {
26231
26128
  let noneOfTheAboveIndex = null;
26232
26129
  let noneOfTheAboveSelected = false;
26233
26130
  const choicesSelected = [...values];
26234
- const countChoices = props.countChoices;
26235
26131
  const numCorrect = props.numCorrect;
26236
26132
  const valuesLength = values.length;
26237
26133
  for (let i = 0; i < valuesLength; i++) {
@@ -26249,7 +26145,6 @@ class Radio$1 extends React.Component {
26249
26145
  choicesSelected,
26250
26146
  noneOfTheAboveIndex,
26251
26147
  noneOfTheAboveSelected,
26252
- countChoices,
26253
26148
  numCorrect
26254
26149
  };
26255
26150
  }
@@ -26340,6 +26235,7 @@ class Radio$1 extends React.Component {
26340
26235
  }));
26341
26236
  }
26342
26237
  const choicesProp = choices.map((choice, i) => {
26238
+ var _this$props$reviewMod;
26343
26239
  const content = choice.isNoneOfTheAbove && !choice.content ? strings.noneOfTheAbove : choice.content;
26344
26240
  const {
26345
26241
  selected,
@@ -26350,7 +26246,7 @@ class Radio$1 extends React.Component {
26350
26246
  highlighted,
26351
26247
  previouslyAnswered
26352
26248
  } = choiceStates[i];
26353
- const reviewChoice = this.props.reviewModeRubric && this.props.reviewModeRubric.choices[i];
26249
+ const reviewChoice = (_this$props$reviewMod = this.props.reviewModeRubric) == null ? void 0 : _this$props$reviewMod.choices[i];
26354
26250
  return {
26355
26251
  content: this._renderRenderer(content),
26356
26252
  checked: selected,
@@ -26385,6 +26281,7 @@ class Radio$1 extends React.Component {
26385
26281
  choices: choicesProp,
26386
26282
  onChange: this.updateChoices,
26387
26283
  reviewModeRubric: this.props.reviewModeRubric,
26284
+ reviewMode: this.props.reviewMode,
26388
26285
  deselectEnabled: this.props.deselectEnabled,
26389
26286
  apiOptions: this.props.apiOptions,
26390
26287
  isLastUsedWidget: this.props.isLastUsedWidget,
@@ -29121,7 +29018,7 @@ class Group extends React.Component {
29121
29018
  ref: ref => this.rendererRef = ref,
29122
29019
  apiOptions: apiOptions,
29123
29020
  findExternalWidgets: this.props.findWidgets,
29124
- reviewMode: !!this.props.reviewModeRubric,
29021
+ reviewMode: this.props.reviewMode,
29125
29022
  onInteractWithWidget: onInteractWithWidget,
29126
29023
  linterContext: this.props.linterContext,
29127
29024
  strings: this.context.strings
@@ -29152,23 +29049,23 @@ var Group$1 = {
29152
29049
  isLintable: true
29153
29050
  };
29154
29051
 
29155
- function iframeValidator(state) {
29052
+ function iframeValidator(userInput) {
29156
29053
  // The iframe can tell us whether it's correct or incorrect,
29157
29054
  // and pass an optional message
29158
- if (state.status === "correct") {
29055
+ if (userInput.status === "correct") {
29159
29056
  return {
29160
29057
  type: "points",
29161
29058
  earned: 1,
29162
29059
  total: 1,
29163
- message: state.message || null
29060
+ message: userInput.message || null
29164
29061
  };
29165
29062
  }
29166
- if (state.status === "incorrect") {
29063
+ if (userInput.status === "incorrect") {
29167
29064
  return {
29168
29065
  type: "points",
29169
29066
  earned: 0,
29170
29067
  total: 1,
29171
- message: state.message || null
29068
+ message: userInput.message || null
29172
29069
  };
29173
29070
  }
29174
29071
  return {
@@ -38983,8 +38880,6 @@ const styles$b = StyleSheet.create({
38983
38880
  * No diagonals allowed.
38984
38881
  */
38985
38882
 
38986
- // TODO: should this be using WidgetProps / PerseusLabelImageWidgetOptions?
38987
-
38988
38883
  class LabelImage extends React.Component {
38989
38884
  /**
38990
38885
  * Test whether point is contained within triangle.
@@ -40470,9 +40365,9 @@ var Matcher$1 = {
40470
40365
  validator: matcherValidator
40471
40366
  };
40472
40367
 
40473
- function matrixValidator(state, rubric, strings) {
40368
+ function matrixValidator(userInput, rubric, strings) {
40474
40369
  const solution = rubric.answers;
40475
- const supplied = state.answers;
40370
+ const supplied = userInput.answers;
40476
40371
  const solutionSize = getMatrixSize(solution);
40477
40372
  const suppliedSize = getMatrixSize(supplied);
40478
40373
  const incorrectSize = solutionSize[0] !== suppliedSize[0] || solutionSize[1] !== suppliedSize[1];
@@ -40615,9 +40510,6 @@ class Matrix extends React.Component {
40615
40510
  });
40616
40511
  return inputPaths;
40617
40512
  };
40618
- this.getGrammarTypeForPath = inputPath => {
40619
- return "number";
40620
- };
40621
40513
  this._handleFocus = (row, col) => {
40622
40514
  this.props.onFocus(getInputPath$1(row, col));
40623
40515
  };
@@ -41030,7 +40922,7 @@ class Measurer extends React.Component {
41030
40922
  }
41031
40923
  Measurer.defaultProps = {
41032
40924
  box: [480, 480],
41033
- image: {},
40925
+ image: defaultImage,
41034
40926
  showProtractor: true,
41035
40927
  protractorX: 7.5,
41036
40928
  protractorY: 0.5,
@@ -42415,11 +42307,6 @@ class NumberLine extends React.Component {
42415
42307
  }
42416
42308
  return null;
42417
42309
  }
42418
- getGrammarTypeForPath(inputPath) {
42419
- if ((inputPath == null ? void 0 : inputPath.length) === 1 && inputPath[0] === "tick-ctrl") {
42420
- return "number";
42421
- }
42422
- }
42423
42310
  getUserInput() {
42424
42311
  return {
42425
42312
  numLinePosition: this.props.numLinePosition,
@@ -44513,9 +44400,6 @@ class Table extends React.Component {
44513
44400
  });
44514
44401
  return inputPaths;
44515
44402
  };
44516
- this.getGrammarTypeForPath = inputPath => {
44517
- return "number";
44518
- };
44519
44403
  this.setInputValue = (path, newValue, cb) => {
44520
44404
  // Extract row, column information
44521
44405
  const row = getRowFromPath(path);
@@ -44833,7 +44717,7 @@ const init = function init(options) {
44833
44717
 
44834
44718
  // This file is processed by a Rollup plugin (replace) to inject the production
44835
44719
  const libName = "@khanacademy/perseus";
44836
- const libVersion = "38.0.1";
44720
+ const libVersion = "39.0.1";
44837
44721
  addLibraryVersionToPerseusDebug(libName, libVersion);
44838
44722
 
44839
44723
  const apiVersion = {
@@ -45555,9 +45439,6 @@ class ServerItemRenderer extends React.Component {
45555
45439
  getDOMNodeForPath(path) {
45556
45440
  return this.questionRenderer.getDOMNodeForPath(path);
45557
45441
  }
45558
- getGrammarTypeForPath(path) {
45559
- return this.questionRenderer.getGrammarTypeForPath(path);
45560
- }
45561
45442
  getInputPaths() {
45562
45443
  const questionAreaInputPaths = this.questionRenderer.getInputPaths();
45563
45444
  return questionAreaInputPaths;
@@ -46834,7 +46715,6 @@ const styles = StyleSheet.create({
46834
46715
  * If you're writing new components, though, consider using the Item and Shape
46835
46716
  * types instead.
46836
46717
  */
46837
-
46838
46718
  /**
46839
46719
  * Return a PropType that accepts Items of the given shape, and rejects other
46840
46720
  * objects.
@@ -47066,6 +46946,21 @@ function violatingWidgets(itemData) {
47066
46946
  }
47067
46947
 
47068
46948
  /* eslint-disable eqeqeq, @typescript-eslint/no-unused-vars */
46949
+ /*
46950
+ * A class for formatting number to significant digits.
46951
+ * Copyright (C) 2001 Stephen Ostermiller
46952
+ * http://ostermiller.org/contact.pl?regarding?JavaScript+Significant+Figures
46953
+ *
46954
+ * This program is free software; you can redistribute it and/or modify
46955
+ * it under the terms of the GNU General Public License as published by
46956
+ * the Free Software Foundation; either version 2 of the License, or
46957
+ * (at your option) any later version.
46958
+ *
46959
+ * This program is distributed in the hope that it will be useful,
46960
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
46961
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46962
+ * GNU General Public License for more details.
46963
+ */
47069
46964
 
47070
46965
  /**
47071
46966
  * Format a floating point for display using the specified