@khanacademy/perseus 31.1.0 → 32.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 (88) hide show
  1. package/dist/components/input-with-examples.d.ts +1 -1
  2. package/dist/components/number-input.d.ts +1 -1
  3. package/dist/components/range-input.d.ts +1 -1
  4. package/dist/components/sortable.d.ts +5 -5
  5. package/dist/components/text-input.d.ts +1 -1
  6. package/dist/components/text-list-editor.d.ts +1 -1
  7. package/dist/es/index.js +570 -562
  8. package/dist/es/index.js.map +1 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +1370 -1370
  11. package/dist/index.js.map +1 -1
  12. package/dist/perseus-types.d.ts +1 -10
  13. package/dist/util.d.ts +0 -1
  14. package/dist/widget-container.d.ts +1 -1
  15. package/dist/widgets/__shared__/test-keypad-context-wrapper.d.ts +6 -0
  16. package/dist/widgets/{categorizer.d.ts → categorizer/categorizer.d.ts} +14 -14
  17. package/dist/widgets/categorizer/index.d.ts +1 -0
  18. package/dist/widgets/{cs-program.d.ts → cs-program/cs-program.d.ts} +7 -7
  19. package/dist/widgets/cs-program/index.d.ts +1 -0
  20. package/dist/widgets/{definition.d.ts → definition/definition.d.ts} +11 -11
  21. package/dist/widgets/definition/index.d.ts +1 -0
  22. package/dist/widgets/{dropdown.d.ts → dropdown/dropdown.d.ts} +9 -9
  23. package/dist/widgets/dropdown/index.d.ts +1 -0
  24. package/dist/widgets/{explanation.d.ts → explanation/explanation.d.ts} +14 -14
  25. package/dist/widgets/explanation/index.d.ts +1 -0
  26. package/dist/widgets/{expression.d.ts → expression/expression.d.ts} +66 -66
  27. package/dist/widgets/expression/index.d.ts +1 -0
  28. package/dist/widgets/{graded-group-answer-bar.d.ts → graded-group/graded-group-answer-bar.d.ts} +3 -3
  29. package/dist/widgets/{graded-group.d.ts → graded-group/graded-group.d.ts} +11 -11
  30. package/dist/widgets/graded-group/index.d.ts +1 -0
  31. package/dist/widgets/{graded-group-set.d.ts → graded-group-set/graded-group-set.d.ts} +11 -11
  32. package/dist/widgets/graded-group-set/index.d.ts +1 -0
  33. package/dist/widgets/{grapher.d.ts → grapher/grapher.d.ts} +15 -15
  34. package/dist/widgets/grapher/index.d.ts +1 -0
  35. package/dist/widgets/{group.d.ts → group/group.d.ts} +12 -12
  36. package/dist/widgets/group/index.d.ts +1 -0
  37. package/dist/widgets/{iframe.d.ts → iframe/iframe.d.ts} +10 -10
  38. package/dist/widgets/iframe/index.d.ts +1 -0
  39. package/dist/widgets/{image.d.ts → image/image.d.ts} +12 -12
  40. package/dist/widgets/image/index.d.ts +1 -0
  41. package/dist/widgets/input-number/index.d.ts +1 -0
  42. package/dist/widgets/{input-number.d.ts → input-number/input-number.d.ts} +19 -19
  43. package/dist/widgets/interaction/index.d.ts +1 -0
  44. package/dist/widgets/{interaction.d.ts → interaction/interaction.d.ts} +12 -12
  45. package/dist/widgets/interactive-graph.d.ts +0 -2
  46. package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +22 -1
  47. package/dist/widgets/interactive-graphs/types.d.ts +1 -0
  48. package/dist/widgets/label-image/index.d.ts +1 -0
  49. package/dist/widgets/{label-image.d.ts → label-image/label-image.d.ts} +24 -24
  50. package/dist/widgets/matcher/index.d.ts +1 -0
  51. package/dist/widgets/{matcher.d.ts → matcher/matcher.d.ts} +11 -11
  52. package/dist/widgets/matrix/index.d.ts +1 -0
  53. package/dist/widgets/{matrix.d.ts → matrix/matrix.d.ts} +12 -12
  54. package/dist/widgets/measurer/index.d.ts +1 -0
  55. package/dist/widgets/measurer/measurer.d.ts +21 -0
  56. package/dist/widgets/molecule/index.d.ts +1 -0
  57. package/dist/widgets/{molecule.d.ts → molecule/molecule.d.ts} +10 -10
  58. package/dist/widgets/number-line/index.d.ts +1 -0
  59. package/dist/widgets/{number-line.d.ts → number-line/number-line.d.ts} +11 -11
  60. package/dist/widgets/numeric-input/index.d.ts +1 -0
  61. package/dist/widgets/{numeric-input.d.ts → numeric-input/numeric-input.d.ts} +13 -13
  62. package/dist/widgets/orderer/index.d.ts +1 -0
  63. package/dist/widgets/{orderer.d.ts → orderer/orderer.d.ts} +10 -10
  64. package/dist/widgets/passage/index.d.ts +2 -0
  65. package/dist/widgets/{passage.d.ts → passage/passage.d.ts} +15 -15
  66. package/dist/widgets/passage-ref/index.d.ts +1 -0
  67. package/dist/widgets/{passage-ref.d.ts → passage-ref/passage-ref.d.ts} +16 -16
  68. package/dist/widgets/passage-ref-target/index.d.ts +1 -0
  69. package/dist/widgets/{passage-ref-target.d.ts → passage-ref-target/passage-ref-target.d.ts} +12 -12
  70. package/dist/widgets/phet-simulation/index.d.ts +1 -0
  71. package/dist/widgets/{phet-simulation.d.ts → phet-simulation/phet-simulation.d.ts} +11 -13
  72. package/dist/widgets/plotter/index.d.ts +1 -0
  73. package/dist/widgets/{plotter.d.ts → plotter/plotter.d.ts} +11 -11
  74. package/dist/widgets/python-program/index.d.ts +1 -0
  75. package/dist/widgets/{python-program.d.ts → python-program/python-program.d.ts} +7 -7
  76. package/dist/widgets/radio/index.d.ts +2 -0
  77. package/dist/widgets/radio/radio-component.d.ts +66 -0
  78. package/dist/widgets/radio/radio.d.ts +22 -66
  79. package/dist/widgets/sorter/index.d.ts +1 -0
  80. package/dist/widgets/{sorter.d.ts → sorter/sorter.d.ts} +8 -8
  81. package/dist/widgets/table/index.d.ts +1 -0
  82. package/dist/widgets/{table.d.ts → table/table.d.ts} +10 -10
  83. package/dist/widgets/video/index.d.ts +1 -0
  84. package/dist/widgets/{video.d.ts → video/video.d.ts} +11 -11
  85. package/package.json +1 -1
  86. package/dist/widgets/measurer.d.ts +0 -21
  87. package/dist/widgets/radio.d.ts +0 -22
  88. /package/dist/widgets/{video-transcript-link.d.ts → video/video-transcript-link.d.ts} +0 -0
package/dist/index.js CHANGED
@@ -1009,32 +1009,6 @@ class TextListEditor extends React__namespace.Component {
1009
1009
  items: nextProps.options.concat("")
1010
1010
  });
1011
1011
  }
1012
- render() {
1013
- const className = ["perseus-text-list-editor", "perseus-clearfix", "layout-" + this.props.layout].join(" ");
1014
- const inputs = ___default["default"].map(this.state.items, function (item, i) {
1015
- return /*#__PURE__*/React__namespace.createElement("li", {
1016
- key: i
1017
- }, /*#__PURE__*/React__namespace.createElement("input", {
1018
- ref: "input_" + i,
1019
- type: "text",
1020
- value: item
1021
- // eslint-disable-next-line react/jsx-no-bind
1022
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1023
- ,
1024
- onChange: this.onChange.bind(this, i)
1025
- // eslint-disable-next-line react/jsx-no-bind
1026
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1027
- ,
1028
- onKeyDown: this.onKeyDown.bind(this, i),
1029
- style: {
1030
- width: getTextWidth(item)
1031
- }
1032
- }));
1033
- }, this);
1034
- return /*#__PURE__*/React__namespace.createElement("ul", {
1035
- className: className
1036
- }, inputs);
1037
- }
1038
1012
  onChange = (index, event) => {
1039
1013
  let items = ___default["default"].clone(this.state.items);
1040
1014
  items[index] = event.target.value;
@@ -1110,6 +1084,32 @@ class TextListEditor extends React__namespace.Component {
1110
1084
  }
1111
1085
  }
1112
1086
  };
1087
+ render() {
1088
+ const className = ["perseus-text-list-editor", "perseus-clearfix", "layout-" + this.props.layout].join(" ");
1089
+ const inputs = ___default["default"].map(this.state.items, function (item, i) {
1090
+ return /*#__PURE__*/React__namespace.createElement("li", {
1091
+ key: i
1092
+ }, /*#__PURE__*/React__namespace.createElement("input", {
1093
+ ref: "input_" + i,
1094
+ type: "text",
1095
+ value: item
1096
+ // eslint-disable-next-line react/jsx-no-bind
1097
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1098
+ ,
1099
+ onChange: this.onChange.bind(this, i)
1100
+ // eslint-disable-next-line react/jsx-no-bind
1101
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1102
+ ,
1103
+ onKeyDown: this.onKeyDown.bind(this, i),
1104
+ style: {
1105
+ width: getTextWidth(item)
1106
+ }
1107
+ }));
1108
+ }, this);
1109
+ return /*#__PURE__*/React__namespace.createElement("ul", {
1110
+ className: className
1111
+ }, inputs);
1112
+ }
1113
1113
  }
1114
1114
 
1115
1115
  /**
@@ -3272,45 +3272,6 @@ function constrainedTickStepsFromTickSteps(tickSteps, ranges) {
3272
3272
  return [constrainTickStep(tickSteps[0], ranges[0]), constrainTickStep(tickSteps[1], ranges[1])];
3273
3273
  }
3274
3274
 
3275
- /**
3276
- * Transparently update deprecated props so that the code to deal
3277
- * with them only lives in one place: (Widget).deprecatedProps
3278
- *
3279
- * For example, if a boolean `foo` was deprecated in favor of a
3280
- * number 'bar':
3281
- * deprecatedProps: {
3282
- * foo: function(props) {
3283
- * return {bar: props.foo ? 1 : 0};
3284
- * }
3285
- * }
3286
- */
3287
- const DeprecationMixin$1 = {
3288
- // This lifecycle stage is only called before first render
3289
- // TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
3290
- UNSAFE_componentWillMount: function () {
3291
- const newProps = {};
3292
- ___default["default"].each(this.deprecatedProps, function (func, prop) {
3293
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
3294
- if (___default["default"].has(this.props, prop)) {
3295
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
3296
- ___default["default"].extend(newProps, func(this.props));
3297
- }
3298
- }, this);
3299
- if (!___default["default"].isEmpty(newProps)) {
3300
- // Set new props directly so that widget renders correctly
3301
- // when it first mounts, even though these will be overwritten
3302
- // almost immediately afterwards...
3303
- ___default["default"].extend(this.props, newProps);
3304
-
3305
- // ...when we propagate the new props upwards and they come
3306
- // back down again.
3307
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API
3308
- // eslint-disable-next-line no-restricted-syntax
3309
- setTimeout(this.props.onChange, 0, newProps);
3310
- }
3311
- }
3312
- };
3313
-
3314
3275
  /**
3315
3276
  * Approximate equality on numbers and primitives.
3316
3277
  */
@@ -3656,7 +3617,6 @@ const Util = {
3656
3617
  gridStepFromTickStep,
3657
3618
  tickStepFromNumTicks,
3658
3619
  constrainedTickStepsFromTickSteps,
3659
- DeprecationMixin: DeprecationMixin$1,
3660
3620
  eq: eq$3,
3661
3621
  deepEq: deepEq$5,
3662
3622
  parseQueryString,
@@ -19651,6 +19611,22 @@ class WidgetContainer extends React__namespace.Component {
19651
19611
  }
19652
19612
  }
19653
19613
 
19614
+ UNSAFE_componentWillReceiveProps(nextProps) {
19615
+ if (this.props.type !== nextProps.type) {
19616
+ throw new Error("WidgetContainer can't change widget type; set a different " + "key instead to recreate the container.");
19617
+ }
19618
+ }
19619
+ shouldComponentUpdate(nextProps, nextState) {
19620
+ return this.props.shouldHighlight !== nextProps.shouldHighlight || this.props.type !== nextProps.type || this.state.widgetProps !== nextState.widgetProps || this.state.sizeClass !== nextState.sizeClass;
19621
+ }
19622
+ getWidget = () => {
19623
+ return this.widgetRef.current;
19624
+ };
19625
+ replaceWidgetProps = newWidgetProps => {
19626
+ this.setState({
19627
+ widgetProps: newWidgetProps
19628
+ });
19629
+ };
19654
19630
  render() {
19655
19631
  let className = classNames__default["default"]({
19656
19632
  "perseus-widget-container": true,
@@ -19745,22 +19721,6 @@ class WidgetContainer extends React__namespace.Component {
19745
19721
  }));
19746
19722
  }));
19747
19723
  }
19748
- UNSAFE_componentWillReceiveProps(nextProps) {
19749
- if (this.props.type !== nextProps.type) {
19750
- throw new Error("WidgetContainer can't change widget type; set a different " + "key instead to recreate the container.");
19751
- }
19752
- }
19753
- shouldComponentUpdate(nextProps, nextState) {
19754
- return this.props.shouldHighlight !== nextProps.shouldHighlight || this.props.type !== nextProps.type || this.state.widgetProps !== nextState.widgetProps || this.state.sizeClass !== nextState.sizeClass;
19755
- }
19756
- getWidget = () => {
19757
- return this.widgetRef.current;
19758
- };
19759
- replaceWidgetProps = newWidgetProps => {
19760
- this.setState({
19761
- widgetProps: newWidgetProps
19762
- });
19763
- };
19764
19724
  }
19765
19725
 
19766
19726
  const {
@@ -21261,6 +21221,34 @@ class TextInput extends React__namespace.Component {
21261
21221
  this.id = uniqueIdForInput();
21262
21222
  }
21263
21223
  }
21224
+ focus = () => {
21225
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'focus' does not exist on type 'Element | Text'.
21226
+ ReactDOM__default["default"].findDOMNode(this).focus();
21227
+ };
21228
+ blur = () => {
21229
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'blur' does not exist on type 'Element | Text'.
21230
+ ReactDOM__default["default"].findDOMNode(this).blur();
21231
+ };
21232
+ getValue = () => {
21233
+ // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element | Text'.
21234
+ return ReactDOM__default["default"].findDOMNode(this)?.value;
21235
+ };
21236
+ getStringValue = () => {
21237
+ // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element | Text'.
21238
+ return ReactDOM__default["default"].findDOMNode(this)?.value.toString();
21239
+ };
21240
+ setSelectionRange = (selectionStart, selectionEnd) => {
21241
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'setSelectionRange' does not exist on type 'Element | Text'.
21242
+ ReactDOM__default["default"].findDOMNode(this).setSelectionRange(selectionStart, selectionEnd);
21243
+ };
21244
+ getSelectionStart = () => {
21245
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'selectionStart' does not exist on type 'Element | Text'.
21246
+ return ReactDOM__default["default"].findDOMNode(this).selectionStart;
21247
+ };
21248
+ getSelectionEnd = () => {
21249
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'selectionEnd' does not exist on type 'Element | Text'.
21250
+ return ReactDOM__default["default"].findDOMNode(this).selectionEnd;
21251
+ };
21264
21252
  render() {
21265
21253
  const {
21266
21254
  labelText,
@@ -21292,34 +21280,6 @@ class TextInput extends React__namespace.Component {
21292
21280
  autoComplete: "off"
21293
21281
  });
21294
21282
  }
21295
- focus = () => {
21296
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'focus' does not exist on type 'Element | Text'.
21297
- ReactDOM__default["default"].findDOMNode(this).focus();
21298
- };
21299
- blur = () => {
21300
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'blur' does not exist on type 'Element | Text'.
21301
- ReactDOM__default["default"].findDOMNode(this).blur();
21302
- };
21303
- getValue = () => {
21304
- // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element | Text'.
21305
- return ReactDOM__default["default"].findDOMNode(this)?.value;
21306
- };
21307
- getStringValue = () => {
21308
- // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element | Text'.
21309
- return ReactDOM__default["default"].findDOMNode(this)?.value.toString();
21310
- };
21311
- setSelectionRange = (selectionStart, selectionEnd) => {
21312
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'setSelectionRange' does not exist on type 'Element | Text'.
21313
- ReactDOM__default["default"].findDOMNode(this).setSelectionRange(selectionStart, selectionEnd);
21314
- };
21315
- getSelectionStart = () => {
21316
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'selectionStart' does not exist on type 'Element | Text'.
21317
- return ReactDOM__default["default"].findDOMNode(this).selectionStart;
21318
- };
21319
- getSelectionEnd = () => {
21320
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'selectionEnd' does not exist on type 'Element | Text'.
21321
- return ReactDOM__default["default"].findDOMNode(this).selectionEnd;
21322
- };
21323
21283
  }
21324
21284
 
21325
21285
  /* eslint-disable react/no-unsafe */
@@ -21569,7 +21529,6 @@ class Tooltip extends React__namespace.Component {
21569
21529
  }
21570
21530
  }
21571
21531
 
21572
- /* eslint-disable react/sort-comp */
21573
21532
  const {
21574
21533
  captureScratchpadTouchStart: captureScratchpadTouchStart$2
21575
21534
  } = Util;
@@ -21623,31 +21582,6 @@ class InputWithExamples extends React__namespace.Component {
21623
21582
  };
21624
21583
  return /*#__PURE__*/React__namespace.createElement(TextInput, inputProps);
21625
21584
  };
21626
- render() {
21627
- const input = this._renderInput();
21628
- const examplesContent = ___default["default"].map(this.props.examples, example => {
21629
- return "- " + example;
21630
- }).join("\n");
21631
- const showExamples = this.props.shouldShowExamples && this.state.showExamples;
21632
- return /*#__PURE__*/React__namespace.createElement(Tooltip
21633
- // eslint-disable-next-line react/no-string-refs
21634
- , {
21635
- ref: "tooltip",
21636
- className: "perseus-formats-tooltip preview-measure",
21637
- horizontalPosition: HorizontalDirection.Left,
21638
- horizontalAlign: HorizontalDirection.Left,
21639
- verticalPosition: VerticalDirection.Bottom,
21640
- arrowSize: 10,
21641
- borderColor: "#ccc",
21642
- show: showExamples
21643
- }, input, /*#__PURE__*/React__namespace.createElement("div", {
21644
- id: this._getUniqueId()
21645
- }, /*#__PURE__*/React__namespace.createElement(Renderer, {
21646
- content: examplesContent,
21647
- linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "input-with-examples"),
21648
- strings: this.context.strings
21649
- })));
21650
- }
21651
21585
  _handleFocus = () => {
21652
21586
  this.props.onFocus();
21653
21587
  this.setState({
@@ -21685,6 +21619,31 @@ class InputWithExamples extends React__namespace.Component {
21685
21619
  handleChange = e => {
21686
21620
  this.props.onChange(e.target.value);
21687
21621
  };
21622
+ render() {
21623
+ const input = this._renderInput();
21624
+ const examplesContent = ___default["default"].map(this.props.examples, example => {
21625
+ return "- " + example;
21626
+ }).join("\n");
21627
+ const showExamples = this.props.shouldShowExamples && this.state.showExamples;
21628
+ return /*#__PURE__*/React__namespace.createElement(Tooltip
21629
+ // eslint-disable-next-line react/no-string-refs
21630
+ , {
21631
+ ref: "tooltip",
21632
+ className: "perseus-formats-tooltip preview-measure",
21633
+ horizontalPosition: HorizontalDirection.Left,
21634
+ horizontalAlign: HorizontalDirection.Left,
21635
+ verticalPosition: VerticalDirection.Bottom,
21636
+ arrowSize: 10,
21637
+ borderColor: "#ccc",
21638
+ show: showExamples
21639
+ }, input, /*#__PURE__*/React__namespace.createElement("div", {
21640
+ id: this._getUniqueId()
21641
+ }, /*#__PURE__*/React__namespace.createElement(Renderer, {
21642
+ content: examplesContent,
21643
+ linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "input-with-examples"),
21644
+ strings: this.context.strings
21645
+ })));
21646
+ }
21688
21647
  }
21689
21648
 
21690
21649
  class SimpleKeypadInput extends React__namespace.Component {
@@ -21915,7 +21874,6 @@ var TexWrangler = {
21915
21874
  modifyTex
21916
21875
  };
21917
21876
 
21918
- /* eslint-disable react/sort-comp */
21919
21877
  const ParseTex$1 = TexWrangler.parseTex;
21920
21878
  const answerTypes = {
21921
21879
  number: {
@@ -21992,58 +21950,65 @@ class InputNumber extends React__namespace.Component {
21992
21950
  apiOptions: ApiOptions.defaults,
21993
21951
  linterContext: PerseusLinter.linterContextDefault
21994
21952
  };
21995
- shouldShowExamples = () => {
21996
- return this.props.answerType !== "number";
21997
- };
21998
- render() {
21999
- if (this.props.apiOptions.customKeypad) {
22000
- // TODO(charlie): Support "Review Mode".
22001
- const input = /*#__PURE__*/React__namespace.createElement(SimpleKeypadInput
22002
- // eslint-disable-next-line react/no-string-refs
22003
- , {
22004
- ref: "input",
22005
- value: this.props.currentValue,
22006
- keypadElement: this.props.keypadElement,
22007
- onChange: this.handleChange,
22008
- onFocus: this._handleFocus,
22009
- onBlur: this._handleBlur
22010
- });
22011
- if (this.props.rightAlign) {
22012
- return /*#__PURE__*/React__namespace.createElement("div", {
22013
- className: "perseus-input-right-align"
22014
- }, input);
22015
- }
22016
- return input;
21953
+ static validate(state, rubric, strings) {
21954
+ let onInputError = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : () => {};
21955
+ if (rubric.answerType == null) {
21956
+ rubric.answerType = "number";
22017
21957
  }
22018
- // HACK(johnsullivan): Create a function with shared logic between
22019
- // this and NumericInput.
22020
- // TODO(jeremy): Deprecate this widget and prefer numeric-input.
22021
- const rubric = this.props.reviewModeRubric;
22022
21958
 
22023
- // Note: This is _very_ similar to what `numeric-input.jsx` does. If
22024
- // you modify this, double-check if you also need to modify that
22025
- // component.
22026
- const inputStyles = [styles$A.default, this.props.size === "small" ? styles$A.small : null, this.props.rightAlign ? styles$A.rightAlign : styles$A.leftAlign];
22027
- // Unanswered
22028
- if (rubric && !this.props.currentValue) {
22029
- inputStyles.push(styles$A.answerStateUnanswered);
21959
+ // note(matthewc): this will get immediately parsed again by
21960
+ // `KhanAnswerTypes.number.convertToPredicate`, but a string is
21961
+ // expected here
21962
+ const stringValue = `${rubric.value}`;
21963
+ const val = KhanAnswerTypes.number.createValidatorFunctional(stringValue, {
21964
+ simplify: rubric.simplify,
21965
+ inexact: rubric.inexact || undefined,
21966
+ maxError: rubric.maxError,
21967
+ forms: answerTypes[rubric.answerType].forms
21968
+ }, strings);
21969
+
21970
+ // We may have received TeX; try to parse it before grading.
21971
+ // If `currentValue` is not TeX, this should be a no-op.
21972
+ const currentValue = ParseTex$1(state.currentValue);
21973
+ const result = val(currentValue);
21974
+
21975
+ // TODO(eater): Seems silly to translate result to this invalid/points
21976
+ // thing and immediately translate it back in ItemRenderer.scoreInput()
21977
+ if (result.empty) {
21978
+ // TODO(FEI-3867): remove null-check once we have APIOptionsInternal
21979
+ const apiResult = onInputError?.(null,
21980
+ // reserved for some widget identifier
21981
+ state.currentValue, result.message);
21982
+ return {
21983
+ type: "invalid",
21984
+ message: apiResult === false ? null : result.message
21985
+ };
22030
21986
  }
22031
- return /*#__PURE__*/React__namespace.createElement(InputWithExamples
22032
- // eslint-disable-next-line react/no-string-refs
22033
- , {
22034
- ref: "input",
22035
- value: this.props.currentValue,
22036
- onChange: this.handleChange,
22037
- style: inputStyles,
22038
- examples: this.examples(),
22039
- shouldShowExamples: this.shouldShowExamples(),
22040
- onFocus: this._handleFocus,
22041
- onBlur: this._handleBlur,
22042
- id: this.props.widgetId,
22043
- disabled: this.props.apiOptions.readOnly,
22044
- linterContext: this.props.linterContext
22045
- });
21987
+ return {
21988
+ type: "points",
21989
+ earned: result.correct ? 1 : 0,
21990
+ total: 1,
21991
+ message: result.message
21992
+ };
21993
+ }
21994
+ static getUserInputFromProps(props) {
21995
+ return {
21996
+ currentValue: props.currentValue
21997
+ };
22046
21998
  }
21999
+ static getOneCorrectAnswerFromRubric(rubric) {
22000
+ if (rubric.value == null) {
22001
+ return;
22002
+ }
22003
+ let answerString = String(rubric.value);
22004
+ if (rubric.inexact && rubric.maxError) {
22005
+ answerString += " \u00B1 " + rubric.maxError;
22006
+ }
22007
+ return answerString;
22008
+ }
22009
+ shouldShowExamples = () => {
22010
+ return this.props.answerType !== "number";
22011
+ };
22047
22012
  handleChange = (newValue, cb) => {
22048
22013
  this.props.onChange({
22049
22014
  currentValue: newValue
@@ -22109,61 +22074,54 @@ class InputNumber extends React__namespace.Component {
22109
22074
  const examples = ___default["default"].map(forms, form => formExamples$1[form](this.props, strings));
22110
22075
  return [strings.yourAnswer].concat(examples);
22111
22076
  };
22112
- static validate(state, rubric, strings) {
22113
- let onInputError = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : () => {};
22114
- if (rubric.answerType == null) {
22115
- rubric.answerType = "number";
22077
+ render() {
22078
+ if (this.props.apiOptions.customKeypad) {
22079
+ // TODO(charlie): Support "Review Mode".
22080
+ const input = /*#__PURE__*/React__namespace.createElement(SimpleKeypadInput
22081
+ // eslint-disable-next-line react/no-string-refs
22082
+ , {
22083
+ ref: "input",
22084
+ value: this.props.currentValue,
22085
+ keypadElement: this.props.keypadElement,
22086
+ onChange: this.handleChange,
22087
+ onFocus: this._handleFocus,
22088
+ onBlur: this._handleBlur
22089
+ });
22090
+ if (this.props.rightAlign) {
22091
+ return /*#__PURE__*/React__namespace.createElement("div", {
22092
+ className: "perseus-input-right-align"
22093
+ }, input);
22094
+ }
22095
+ return input;
22116
22096
  }
22097
+ // HACK(johnsullivan): Create a function with shared logic between
22098
+ // this and NumericInput.
22099
+ // TODO(jeremy): Deprecate this widget and prefer numeric-input.
22100
+ const rubric = this.props.reviewModeRubric;
22117
22101
 
22118
- // note(matthewc): this will get immediately parsed again by
22119
- // `KhanAnswerTypes.number.convertToPredicate`, but a string is
22120
- // expected here
22121
- const stringValue = `${rubric.value}`;
22122
- const val = KhanAnswerTypes.number.createValidatorFunctional(stringValue, {
22123
- simplify: rubric.simplify,
22124
- inexact: rubric.inexact || undefined,
22125
- maxError: rubric.maxError,
22126
- forms: answerTypes[rubric.answerType].forms
22127
- }, strings);
22128
-
22129
- // We may have received TeX; try to parse it before grading.
22130
- // If `currentValue` is not TeX, this should be a no-op.
22131
- const currentValue = ParseTex$1(state.currentValue);
22132
- const result = val(currentValue);
22133
-
22134
- // TODO(eater): Seems silly to translate result to this invalid/points
22135
- // thing and immediately translate it back in ItemRenderer.scoreInput()
22136
- if (result.empty) {
22137
- // TODO(FEI-3867): remove null-check once we have APIOptionsInternal
22138
- const apiResult = onInputError?.(null,
22139
- // reserved for some widget identifier
22140
- state.currentValue, result.message);
22141
- return {
22142
- type: "invalid",
22143
- message: apiResult === false ? null : result.message
22144
- };
22145
- }
22146
- return {
22147
- type: "points",
22148
- earned: result.correct ? 1 : 0,
22149
- total: 1,
22150
- message: result.message
22151
- };
22152
- }
22153
- static getUserInputFromProps(props) {
22154
- return {
22155
- currentValue: props.currentValue
22156
- };
22157
- }
22158
- static getOneCorrectAnswerFromRubric(rubric) {
22159
- if (rubric.value == null) {
22160
- return;
22161
- }
22162
- let answerString = String(rubric.value);
22163
- if (rubric.inexact && rubric.maxError) {
22164
- answerString += " \u00B1 " + rubric.maxError;
22102
+ // Note: This is _very_ similar to what `numeric-input.jsx` does. If
22103
+ // you modify this, double-check if you also need to modify that
22104
+ // component.
22105
+ const inputStyles = [styles$A.default, this.props.size === "small" ? styles$A.small : null, this.props.rightAlign ? styles$A.rightAlign : styles$A.leftAlign];
22106
+ // Unanswered
22107
+ if (rubric && !this.props.currentValue) {
22108
+ inputStyles.push(styles$A.answerStateUnanswered);
22165
22109
  }
22166
- return answerString;
22110
+ return /*#__PURE__*/React__namespace.createElement(InputWithExamples
22111
+ // eslint-disable-next-line react/no-string-refs
22112
+ , {
22113
+ ref: "input",
22114
+ value: this.props.currentValue,
22115
+ onChange: this.handleChange,
22116
+ style: inputStyles,
22117
+ examples: this.examples(),
22118
+ shouldShowExamples: this.shouldShowExamples(),
22119
+ onFocus: this._handleFocus,
22120
+ onBlur: this._handleBlur,
22121
+ id: this.props.widgetId,
22122
+ disabled: this.props.apiOptions.readOnly,
22123
+ linterContext: this.props.linterContext
22124
+ });
22167
22125
  }
22168
22126
  }
22169
22127
  const styles$A = aphrodite.StyleSheet.create({
@@ -22607,7 +22565,6 @@ var NumericInput$1 = {
22607
22565
  isLintable: true
22608
22566
  };
22609
22567
 
22610
- /* eslint-disable react/sort-comp */
22611
22568
  const EN_DASH$1 = "\u2013";
22612
22569
  class PassageRef extends React__namespace.Component {
22613
22570
  static contextType = PerseusI18nContext;
@@ -22625,48 +22582,38 @@ class PassageRef extends React__namespace.Component {
22625
22582
  lineRange: null,
22626
22583
  content: null
22627
22584
  };
22585
+ static validate(userInput, rubric) {
22586
+ return {
22587
+ type: "points",
22588
+ earned: 0,
22589
+ total: 0,
22590
+ message: null
22591
+ };
22592
+ }
22593
+ componentDidMount() {
22594
+ // TODO(scottgrant): This is a hack to remove the deprecated call to
22595
+ // this.isMounted() but is still considered an anti-pattern.
22596
+ this._isMounted = true;
22597
+ this._deferredUpdateRange();
22598
+ this._throttledUpdateRange = ___default["default"].throttle(this._deferredUpdateRange, 500);
22599
+ window.addEventListener("resize", this._throttledUpdateRange);
22600
+ }
22628
22601
  shouldComponentUpdate(nextProps, nextState) {
22629
22602
  return !___default["default"].isEqual(this.props, nextProps) || !___default["default"].isEqual(this.state, nextState);
22630
22603
  }
22604
+ componentDidUpdate() {
22605
+ this._deferredUpdateRange();
22606
+ }
22607
+ componentWillUnmount() {
22608
+ window.removeEventListener("resize", this._throttledUpdateRange);
22609
+ this._isMounted = false;
22610
+ }
22631
22611
 
22632
22612
  // TODO(tamarab): getUserInput needs to be updated to only return
22633
22613
  // props input by the user. Currently returns all the widget's props.
22634
22614
  getUserInput = () => {
22635
22615
  return removeDenylistProps(this.props);
22636
22616
  };
22637
- render() {
22638
- const {
22639
- strings
22640
- } = this.context;
22641
- const lineRange = this.state.lineRange;
22642
- let lineRangeOutput;
22643
- if (!lineRange) {
22644
- lineRangeOutput = strings.lineRange({
22645
- lineRange: `?${EN_DASH$1}?`
22646
- });
22647
- } else if (lineRange[0] === lineRange[1]) {
22648
- lineRangeOutput = strings.lineNumber({
22649
- lineNumber: String(lineRange[0])
22650
- });
22651
- } else {
22652
- lineRangeOutput = strings.lineRange({
22653
- lineRange: lineRange[0] + EN_DASH$1 + lineRange[1]
22654
- });
22655
- }
22656
- let summaryOutput;
22657
- if (this.props.summaryText) {
22658
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
22659
- const summaryTree = PerseusMarkdown.parseInline(this.props.summaryText);
22660
- summaryOutput = /*#__PURE__*/React__namespace.createElement("span", {
22661
- "aria-hidden": true
22662
- }, " ", "(\u201C", PerseusMarkdown.basicOutput(summaryTree), "\u201D)");
22663
- } else {
22664
- summaryOutput = null;
22665
- }
22666
- return /*#__PURE__*/React__namespace.createElement("span", null, lineRangeOutput, summaryOutput, lineRange && /*#__PURE__*/React__namespace.createElement("div", {
22667
- className: "perseus-sr-only"
22668
- }, this.state.content));
22669
- }
22670
22617
  change = (() => {
22671
22618
  var _this = this;
22672
22619
  return function () {
@@ -22676,21 +22623,6 @@ class PassageRef extends React__namespace.Component {
22676
22623
  return change.apply(_this, args);
22677
22624
  };
22678
22625
  })();
22679
- componentDidMount() {
22680
- // TODO(scottgrant): This is a hack to remove the deprecated call to
22681
- // this.isMounted() but is still considered an anti-pattern.
22682
- this._isMounted = true;
22683
- this._deferredUpdateRange();
22684
- this._throttledUpdateRange = ___default["default"].throttle(this._deferredUpdateRange, 500);
22685
- window.addEventListener("resize", this._throttledUpdateRange);
22686
- }
22687
- componentDidUpdate() {
22688
- this._deferredUpdateRange();
22689
- }
22690
- componentWillUnmount() {
22691
- window.removeEventListener("resize", this._throttledUpdateRange);
22692
- this._isMounted = false;
22693
- }
22694
22626
  _deferredUpdateRange = () => {
22695
22627
  ___default["default"].defer(this._updateRange);
22696
22628
  };
@@ -22720,13 +22652,38 @@ class PassageRef extends React__namespace.Component {
22720
22652
  simpleValidate = rubric => {
22721
22653
  return PassageRef.validate(this.getUserInput(), rubric);
22722
22654
  };
22723
- static validate(userInput, rubric) {
22724
- return {
22725
- type: "points",
22726
- earned: 0,
22727
- total: 0,
22728
- message: null
22729
- };
22655
+ render() {
22656
+ const {
22657
+ strings
22658
+ } = this.context;
22659
+ const lineRange = this.state.lineRange;
22660
+ let lineRangeOutput;
22661
+ if (!lineRange) {
22662
+ lineRangeOutput = strings.lineRange({
22663
+ lineRange: `?${EN_DASH$1}?`
22664
+ });
22665
+ } else if (lineRange[0] === lineRange[1]) {
22666
+ lineRangeOutput = strings.lineNumber({
22667
+ lineNumber: String(lineRange[0])
22668
+ });
22669
+ } else {
22670
+ lineRangeOutput = strings.lineRange({
22671
+ lineRange: lineRange[0] + EN_DASH$1 + lineRange[1]
22672
+ });
22673
+ }
22674
+ let summaryOutput;
22675
+ if (this.props.summaryText) {
22676
+ // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
22677
+ const summaryTree = PerseusMarkdown.parseInline(this.props.summaryText);
22678
+ summaryOutput = /*#__PURE__*/React__namespace.createElement("span", {
22679
+ "aria-hidden": true
22680
+ }, " ", "(\u201C", PerseusMarkdown.basicOutput(summaryTree), "\u201D)");
22681
+ } else {
22682
+ summaryOutput = null;
22683
+ }
22684
+ return /*#__PURE__*/React__namespace.createElement("span", null, lineRangeOutput, summaryOutput, lineRange && /*#__PURE__*/React__namespace.createElement("div", {
22685
+ className: "perseus-sr-only"
22686
+ }, this.state.content));
22730
22687
  }
22731
22688
  }
22732
22689
  var PassageRef$1 = {
@@ -24678,6 +24635,33 @@ class Categorizer extends React__namespace.Component {
24678
24635
  state = {
24679
24636
  uniqueId: ___default["default"].uniqueId("perseus_radio_")
24680
24637
  };
24638
+ static validate(userInput, rubric, strings) {
24639
+ let completed = true;
24640
+ let allCorrect = true;
24641
+ rubric.values.forEach((value, i) => {
24642
+ if (userInput.values[i] == null) {
24643
+ completed = false;
24644
+ }
24645
+ if (userInput.values[i] !== value) {
24646
+ allCorrect = false;
24647
+ }
24648
+ });
24649
+ if (!completed) {
24650
+ return {
24651
+ type: "invalid",
24652
+ message: strings.invalidSelection
24653
+ };
24654
+ }
24655
+ return {
24656
+ type: "points",
24657
+ earned: allCorrect ? 1 : 0,
24658
+ total: 1,
24659
+ message: null
24660
+ };
24661
+ }
24662
+ static getUserInputFromProps(props) {
24663
+ return WidgetJsonifyDeprecated.getUserInputFromProps(props);
24664
+ }
24681
24665
  change = (() => {
24682
24666
  var _this = this;
24683
24667
  return function () {
@@ -24691,6 +24675,16 @@ class Categorizer extends React__namespace.Component {
24691
24675
  getUserInput = () => {
24692
24676
  return Categorizer.getUserInputFromProps(this.props);
24693
24677
  };
24678
+ onChange(itemNum, catNum) {
24679
+ const values = [...this.props.values];
24680
+ // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'never'.
24681
+ values[itemNum] = catNum;
24682
+ this.change("values", values);
24683
+ this.props.trackInteraction();
24684
+ }
24685
+ simpleValidate = rubric => {
24686
+ return Categorizer.validate(this.getUserInput(), rubric, this.context.strings);
24687
+ };
24694
24688
  render() {
24695
24689
  const self = this;
24696
24690
 
@@ -24767,43 +24761,6 @@ class Categorizer extends React__namespace.Component {
24767
24761
  className: extraClassNames + " " + aphrodite.css(...inlineStyles)
24768
24762
  }, table);
24769
24763
  }
24770
- onChange(itemNum, catNum) {
24771
- const values = [...this.props.values];
24772
- // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'never'.
24773
- values[itemNum] = catNum;
24774
- this.change("values", values);
24775
- this.props.trackInteraction();
24776
- }
24777
- simpleValidate = rubric => {
24778
- return Categorizer.validate(this.getUserInput(), rubric, this.context.strings);
24779
- };
24780
- static validate(userInput, rubric, strings) {
24781
- let completed = true;
24782
- let allCorrect = true;
24783
- rubric.values.forEach((value, i) => {
24784
- if (userInput.values[i] == null) {
24785
- completed = false;
24786
- }
24787
- if (userInput.values[i] !== value) {
24788
- allCorrect = false;
24789
- }
24790
- });
24791
- if (!completed) {
24792
- return {
24793
- type: "invalid",
24794
- message: strings.invalidSelection
24795
- };
24796
- }
24797
- return {
24798
- type: "points",
24799
- earned: allCorrect ? 1 : 0,
24800
- total: 1,
24801
- message: null
24802
- };
24803
- }
24804
- static getUserInputFromProps(props) {
24805
- return WidgetJsonifyDeprecated.getUserInputFromProps(props);
24806
- }
24807
24764
  }
24808
24765
 
24809
24766
  // TODO(benkomalo): inject page-margin into Perseus instead of hardcoding.
@@ -25311,8 +25268,6 @@ var Dropdown$1 = {
25311
25268
  transform: optionsTransform
25312
25269
  };
25313
25270
 
25314
- /* eslint-disable react/sort-comp */
25315
-
25316
25271
  // transform = _.identity
25317
25272
 
25318
25273
  function mediaQueryIsMatched(mediaQuery) {
@@ -25323,6 +25278,10 @@ function mediaQueryIsMatched(mediaQuery) {
25323
25278
  }
25324
25279
  class Explanation extends React__namespace.Component {
25325
25280
  static contextType = PerseusI18nContext;
25281
+ state = {
25282
+ expanded: false
25283
+ };
25284
+ _mounted = false;
25326
25285
  static defaultProps = {
25327
25286
  showPrompt: "Explain",
25328
25287
  hidePrompt: "Hide explanation",
@@ -25338,10 +25297,6 @@ class Explanation extends React__namespace.Component {
25338
25297
  message: null
25339
25298
  };
25340
25299
  }
25341
- state = {
25342
- expanded: false
25343
- };
25344
- _mounted = false;
25345
25300
  componentDidMount() {
25346
25301
  this._mounted = true;
25347
25302
  }
@@ -25363,6 +25318,12 @@ class Explanation extends React__namespace.Component {
25363
25318
  });
25364
25319
  this.props.trackInteraction();
25365
25320
  };
25321
+ getUserInput = () => {
25322
+ return {};
25323
+ };
25324
+ simpleValidate = rubric => {
25325
+ return Explanation.validate(this.getUserInput(), rubric);
25326
+ };
25366
25327
  render() {
25367
25328
  const promptText = this.state.expanded ? this.props.hidePrompt : this.props.showPrompt;
25368
25329
  const caretIcon = this.state.expanded ? caretUp__default["default"] : caretDown__default["default"];
@@ -25418,12 +25379,6 @@ class Explanation extends React__namespace.Component {
25418
25379
  strings: this.context.strings
25419
25380
  })))));
25420
25381
  }
25421
- getUserInput = () => {
25422
- return {};
25423
- };
25424
- simpleValidate = rubric => {
25425
- return Explanation.validate(this.getUserInput(), rubric);
25426
- };
25427
25382
  }
25428
25383
  const leftBorderSpacing = 23;
25429
25384
  const verticalContentPadding = 10;
@@ -26847,6 +26802,70 @@ class FunctionGrapher extends React__namespace.Component {
26847
26802
  return change.apply(_this, args);
26848
26803
  };
26849
26804
  })();
26805
+ renderPlot = () => {
26806
+ const model = this.props.model;
26807
+ const xRange = this.props.graph.range[0];
26808
+ const style = {
26809
+ stroke: this.props.isMobile ? KhanColors.BLUE_C : KhanColors.DYNAMIC,
26810
+ ...(this.props.isMobile ? {
26811
+ "stroke-width": 3
26812
+ } : {})
26813
+ };
26814
+ const coeffs = model.getCoefficients(this._coords(), this._asymptote());
26815
+ if (!coeffs) {
26816
+ return;
26817
+ }
26818
+ const functionProps = model.getPropsForCoeffs(coeffs, xRange);
26819
+ return /*#__PURE__*/React__namespace.createElement(model.Movable, _extends({}, functionProps, {
26820
+ key: this.props.model.url,
26821
+ range: xRange,
26822
+ style: style
26823
+ }));
26824
+ };
26825
+ renderAsymptote = () => {
26826
+ const model = this.props.model;
26827
+ const graph = this.props.graph;
26828
+ const asymptote = this._asymptote();
26829
+ const dashed = {
26830
+ strokeDasharray: "- "
26831
+ };
26832
+ return asymptote && /*#__PURE__*/React__namespace.createElement(MovableLine$2, {
26833
+ onMove: (newCoord, oldCoord) => {
26834
+ // Calculate and apply displacement
26835
+ const delta = kmath.vector.subtract(newCoord, oldCoord);
26836
+ const newAsymptote = ___default["default"].map(this._asymptote(), coord => kmath.vector.add(coord, delta));
26837
+ this.props.onChange({
26838
+ asymptote: newAsymptote
26839
+ });
26840
+ },
26841
+ constraints: [
26842
+ // @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
26843
+ Interactive2.MovableLine.constraints.bound(),
26844
+ // @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
26845
+ Interactive2.MovableLine.constraints.snap(), (newCoord, oldCoord) => {
26846
+ // Calculate and apply proposed displacement
26847
+ const delta = kmath.vector.subtract(newCoord, oldCoord);
26848
+ const proposedAsymptote = ___default["default"].map(this._asymptote(), coord => kmath.vector.add(coord, delta));
26849
+ // Verify that resulting asymptote is valid for graph
26850
+ if (model.extraAsymptoteConstraint) {
26851
+ return model.extraAsymptoteConstraint(newCoord, oldCoord, this._coords(), proposedAsymptote, graph);
26852
+ }
26853
+ return true;
26854
+ }],
26855
+ normalStyle: dashed,
26856
+ highlightStyle: dashed
26857
+ }, ___default["default"].map(asymptote, (coord, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$3, {
26858
+ key: `asymptoteCoord-${i}`,
26859
+ coord: coord,
26860
+ static: true,
26861
+ draw: null,
26862
+ extendLine: true,
26863
+ showHairlines: this.props.showHairlines,
26864
+ hideHairlines: this.props.hideHairlines,
26865
+ showTooltips: this.props.showTooltips,
26866
+ isMobile: this.props.isMobile
26867
+ })));
26868
+ };
26850
26869
  render() {
26851
26870
  const pointForCoord = (coord, i) => {
26852
26871
  return /*#__PURE__*/React__namespace.createElement(MovablePoint$3, {
@@ -26934,70 +26953,6 @@ class FunctionGrapher extends React__namespace.Component {
26934
26953
  setDrawingAreaAvailable: this.props.setDrawingAreaAvailable
26935
26954
  }), this.props.model && this.renderPlot(), this.props.model && this.renderAsymptote(), this.props.model && points)));
26936
26955
  }
26937
- renderPlot = () => {
26938
- const model = this.props.model;
26939
- const xRange = this.props.graph.range[0];
26940
- const style = {
26941
- stroke: this.props.isMobile ? KhanColors.BLUE_C : KhanColors.DYNAMIC,
26942
- ...(this.props.isMobile ? {
26943
- "stroke-width": 3
26944
- } : {})
26945
- };
26946
- const coeffs = model.getCoefficients(this._coords(), this._asymptote());
26947
- if (!coeffs) {
26948
- return;
26949
- }
26950
- const functionProps = model.getPropsForCoeffs(coeffs, xRange);
26951
- return /*#__PURE__*/React__namespace.createElement(model.Movable, _extends({}, functionProps, {
26952
- key: this.props.model.url,
26953
- range: xRange,
26954
- style: style
26955
- }));
26956
- };
26957
- renderAsymptote = () => {
26958
- const model = this.props.model;
26959
- const graph = this.props.graph;
26960
- const asymptote = this._asymptote();
26961
- const dashed = {
26962
- strokeDasharray: "- "
26963
- };
26964
- return asymptote && /*#__PURE__*/React__namespace.createElement(MovableLine$2, {
26965
- onMove: (newCoord, oldCoord) => {
26966
- // Calculate and apply displacement
26967
- const delta = kmath.vector.subtract(newCoord, oldCoord);
26968
- const newAsymptote = ___default["default"].map(this._asymptote(), coord => kmath.vector.add(coord, delta));
26969
- this.props.onChange({
26970
- asymptote: newAsymptote
26971
- });
26972
- },
26973
- constraints: [
26974
- // @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
26975
- Interactive2.MovableLine.constraints.bound(),
26976
- // @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
26977
- Interactive2.MovableLine.constraints.snap(), (newCoord, oldCoord) => {
26978
- // Calculate and apply proposed displacement
26979
- const delta = kmath.vector.subtract(newCoord, oldCoord);
26980
- const proposedAsymptote = ___default["default"].map(this._asymptote(), coord => kmath.vector.add(coord, delta));
26981
- // Verify that resulting asymptote is valid for graph
26982
- if (model.extraAsymptoteConstraint) {
26983
- return model.extraAsymptoteConstraint(newCoord, oldCoord, this._coords(), proposedAsymptote, graph);
26984
- }
26985
- return true;
26986
- }],
26987
- normalStyle: dashed,
26988
- highlightStyle: dashed
26989
- }, ___default["default"].map(asymptote, (coord, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$3, {
26990
- key: `asymptoteCoord-${i}`,
26991
- coord: coord,
26992
- static: true,
26993
- draw: null,
26994
- extendLine: true,
26995
- showHairlines: this.props.showHairlines,
26996
- hideHairlines: this.props.hideHairlines,
26997
- showTooltips: this.props.showTooltips,
26998
- isMobile: this.props.isMobile
26999
- })));
27000
- };
27001
26956
  }
27002
26957
 
27003
26958
  // Really this is props.plot, I think
@@ -27007,6 +26962,118 @@ class Grapher extends React__namespace.Component {
27007
26962
  horizHairline;
27008
26963
  vertHairline;
27009
26964
  static defaultProps = DEFAULT_GRAPHER_PROPS;
26965
+ static validate(state, rubric) {
26966
+ return validate$1(state, rubric);
26967
+ }
26968
+ static getUserInputFromProps(props) {
26969
+ return props.plot;
26970
+ }
26971
+ handlePlotChanges = newPlot => {
26972
+ const plot = ___default["default"].extend({}, this.props.plot, newPlot);
26973
+ this.props.onChange({
26974
+ plot: plot
26975
+ });
26976
+ this.props.trackInteraction();
26977
+ };
26978
+ handleActiveTypeChange = newType => {
26979
+ const graph = this.props.graph;
26980
+ const plot = ___default["default"].extend({}, this.props.plot, defaultPlotProps(newType, graph));
26981
+ this.props.onChange({
26982
+ plot: plot
26983
+ });
26984
+ };
26985
+ _getGridConfig(options) {
26986
+ return options.step.map((step, i) => {
26987
+ return Util.gridDimensionConfig(step, options.range[i], options.box[i], options.gridStep[i]);
26988
+ });
26989
+ }
26990
+ _calculateMobileTickStep(gridStep, step, ranges) {
26991
+ const tickStep = Util.constrainedTickStepsFromTickSteps(step, ranges);
26992
+
26993
+ // According to the graphInit documentation in graphie.js, tickStep is
26994
+ // relative to the grid units so we need to adjust all values by the
26995
+ // grid step.
26996
+ tickStep[0] = tickStep[0] / gridStep[0];
26997
+ tickStep[1] = tickStep[1] / gridStep[1];
26998
+ return tickStep;
26999
+ }
27000
+ _setupGraphie = (graphie, options) => {
27001
+ const isMobile = this.props.apiOptions.isMobile;
27002
+ if (options.markings === "graph") {
27003
+ graphie.graphInit({
27004
+ range: options.range,
27005
+ scale: ___default["default"].pluck(options.gridConfig, "scale"),
27006
+ axisArrows: "<->",
27007
+ labelFormat: function (s) {
27008
+ return "\\small{" + s + "}";
27009
+ },
27010
+ gridStep: options.gridStep,
27011
+ snapStep: options.snapStep,
27012
+ tickStep: isMobile ? this._calculateMobileTickStep(options.gridStep, options.step, options.range) : ___default["default"].pluck(options.gridConfig, "tickStep"),
27013
+ labelStep: 1,
27014
+ unityLabels: ___default["default"].pluck(options.gridConfig, "unityLabel"),
27015
+ isMobile: isMobile
27016
+ });
27017
+ graphie.label([0, options.range[1][1]], options.labels[1], isMobile ? "below right" : "above");
27018
+ graphie.label([options.range[0][1], 0], options.labels[0], isMobile ? "above left" : "right");
27019
+ } else if (options.markings === "grid") {
27020
+ graphie.graphInit({
27021
+ range: options.range,
27022
+ scale: ___default["default"].pluck(options.gridConfig, "scale"),
27023
+ gridStep: options.gridStep,
27024
+ axes: false,
27025
+ ticks: false,
27026
+ labels: false,
27027
+ isMobile: isMobile
27028
+ });
27029
+ } else if (options.markings === "none") {
27030
+ graphie.init({
27031
+ range: options.range,
27032
+ scale: ___default["default"].pluck(options.gridConfig, "scale")
27033
+ });
27034
+ }
27035
+ if (this.props.apiOptions.isMobile) {
27036
+ const hairlineStyle = {
27037
+ normalStyle: {
27038
+ strokeWidth: 1
27039
+ }
27040
+ };
27041
+ this.horizHairline = new WrappedLine(graphie, [0, 0], [0, 0], hairlineStyle);
27042
+ this.horizHairline.attr({
27043
+ stroke: KhanColors.INTERACTIVE
27044
+ });
27045
+ this.horizHairline.hide();
27046
+ this.vertHairline = new WrappedLine(graphie, [0, 0], [0, 0], hairlineStyle);
27047
+ this.vertHairline.attr({
27048
+ stroke: KhanColors.INTERACTIVE
27049
+ });
27050
+ this.vertHairline.hide();
27051
+ }
27052
+ };
27053
+ showHairlines = point => {
27054
+ if (this.props.apiOptions.isMobile && this.props.markings !== "none") {
27055
+ // Hairlines are already initialized when the graph is loaded, so
27056
+ // here we just move them to the updated location and make them
27057
+ // visible.
27058
+ this.horizHairline.moveTo([this.props.graph.range[0][0], point[1]], [this.props.graph.range[0][1], point[1]]);
27059
+ this.horizHairline.show();
27060
+ this.vertHairline.moveTo([point[0], this.props.graph.range[1][0]], [point[0], this.props.graph.range[1][1]]);
27061
+ this.vertHairline.show();
27062
+ }
27063
+ };
27064
+ hideHairlines = () => {
27065
+ if (this.props.apiOptions.isMobile) {
27066
+ this.horizHairline.hide();
27067
+ this.vertHairline.hide();
27068
+ }
27069
+ };
27070
+ simpleValidate(rubric) {
27071
+ return validate$1(this.getUserInput(), rubric);
27072
+ }
27073
+ getUserInput() {
27074
+ return Grapher.getUserInputFromProps(this.props);
27075
+ }
27076
+ focus() {}
27010
27077
  render() {
27011
27078
  const type = this.props.plot.type;
27012
27079
  const coords = this.props.plot.coords;
@@ -27061,118 +27128,6 @@ class Grapher extends React__namespace.Component {
27061
27128
  };
27062
27129
  return /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement(FunctionGrapher, grapherProps), this.props.availableTypes.length > 1 && typeSelector);
27063
27130
  }
27064
- handlePlotChanges = newPlot => {
27065
- const plot = ___default["default"].extend({}, this.props.plot, newPlot);
27066
- this.props.onChange({
27067
- plot: plot
27068
- });
27069
- this.props.trackInteraction();
27070
- };
27071
- handleActiveTypeChange = newType => {
27072
- const graph = this.props.graph;
27073
- const plot = ___default["default"].extend({}, this.props.plot, defaultPlotProps(newType, graph));
27074
- this.props.onChange({
27075
- plot: plot
27076
- });
27077
- };
27078
- _getGridConfig(options) {
27079
- return options.step.map((step, i) => {
27080
- return Util.gridDimensionConfig(step, options.range[i], options.box[i], options.gridStep[i]);
27081
- });
27082
- }
27083
- _calculateMobileTickStep(gridStep, step, ranges) {
27084
- const tickStep = Util.constrainedTickStepsFromTickSteps(step, ranges);
27085
-
27086
- // According to the graphInit documentation in graphie.js, tickStep is
27087
- // relative to the grid units so we need to adjust all values by the
27088
- // grid step.
27089
- tickStep[0] = tickStep[0] / gridStep[0];
27090
- tickStep[1] = tickStep[1] / gridStep[1];
27091
- return tickStep;
27092
- }
27093
- _setupGraphie = (graphie, options) => {
27094
- const isMobile = this.props.apiOptions.isMobile;
27095
- if (options.markings === "graph") {
27096
- graphie.graphInit({
27097
- range: options.range,
27098
- scale: ___default["default"].pluck(options.gridConfig, "scale"),
27099
- axisArrows: "<->",
27100
- labelFormat: function (s) {
27101
- return "\\small{" + s + "}";
27102
- },
27103
- gridStep: options.gridStep,
27104
- snapStep: options.snapStep,
27105
- tickStep: isMobile ? this._calculateMobileTickStep(options.gridStep, options.step, options.range) : ___default["default"].pluck(options.gridConfig, "tickStep"),
27106
- labelStep: 1,
27107
- unityLabels: ___default["default"].pluck(options.gridConfig, "unityLabel"),
27108
- isMobile: isMobile
27109
- });
27110
- graphie.label([0, options.range[1][1]], options.labels[1], isMobile ? "below right" : "above");
27111
- graphie.label([options.range[0][1], 0], options.labels[0], isMobile ? "above left" : "right");
27112
- } else if (options.markings === "grid") {
27113
- graphie.graphInit({
27114
- range: options.range,
27115
- scale: ___default["default"].pluck(options.gridConfig, "scale"),
27116
- gridStep: options.gridStep,
27117
- axes: false,
27118
- ticks: false,
27119
- labels: false,
27120
- isMobile: isMobile
27121
- });
27122
- } else if (options.markings === "none") {
27123
- graphie.init({
27124
- range: options.range,
27125
- scale: ___default["default"].pluck(options.gridConfig, "scale")
27126
- });
27127
- }
27128
- if (this.props.apiOptions.isMobile) {
27129
- const hairlineStyle = {
27130
- normalStyle: {
27131
- strokeWidth: 1
27132
- }
27133
- };
27134
- this.horizHairline = new WrappedLine(graphie, [0, 0], [0, 0], hairlineStyle);
27135
- this.horizHairline.attr({
27136
- stroke: KhanColors.INTERACTIVE
27137
- });
27138
- this.horizHairline.hide();
27139
- this.vertHairline = new WrappedLine(graphie, [0, 0], [0, 0], hairlineStyle);
27140
- this.vertHairline.attr({
27141
- stroke: KhanColors.INTERACTIVE
27142
- });
27143
- this.vertHairline.hide();
27144
- }
27145
- };
27146
- showHairlines = point => {
27147
- if (this.props.apiOptions.isMobile && this.props.markings !== "none") {
27148
- // Hairlines are already initialized when the graph is loaded, so
27149
- // here we just move them to the updated location and make them
27150
- // visible.
27151
- this.horizHairline.moveTo([this.props.graph.range[0][0], point[1]], [this.props.graph.range[0][1], point[1]]);
27152
- this.horizHairline.show();
27153
- this.vertHairline.moveTo([point[0], this.props.graph.range[1][0]], [point[0], this.props.graph.range[1][1]]);
27154
- this.vertHairline.show();
27155
- }
27156
- };
27157
- hideHairlines = () => {
27158
- if (this.props.apiOptions.isMobile) {
27159
- this.horizHairline.hide();
27160
- this.vertHairline.hide();
27161
- }
27162
- };
27163
- simpleValidate(rubric) {
27164
- return validate$1(this.getUserInput(), rubric);
27165
- }
27166
- getUserInput() {
27167
- return Grapher.getUserInputFromProps(this.props);
27168
- }
27169
- focus() {}
27170
- static validate(state, rubric) {
27171
- return validate$1(state, rubric);
27172
- }
27173
- static getUserInputFromProps(props) {
27174
- return props.plot;
27175
- }
27176
27131
  }
27177
27132
  const propTransform$2 = editorProps => {
27178
27133
  const widgetProps = {
@@ -27355,7 +27310,7 @@ var Group$1 = {
27355
27310
  isLintable: true
27356
27311
  };
27357
27312
 
27358
- /* eslint-disable react/forbid-prop-types, react/sort-comp */
27313
+ /* eslint-disable react/forbid-prop-types */
27359
27314
  const {
27360
27315
  updateQueryString
27361
27316
  } = Util;
@@ -27380,6 +27335,12 @@ class Iframe extends React__namespace.Component {
27380
27335
  allowFullScreen: false,
27381
27336
  allowTopNavigation: false
27382
27337
  };
27338
+ componentDidMount() {
27339
+ $__default["default"](window).on("message", this.handleMessageEvent);
27340
+ }
27341
+ componentWillUnmount() {
27342
+ $__default["default"](window).off("message", this.handleMessageEvent);
27343
+ }
27383
27344
  getUserInput = () => {
27384
27345
  return WidgetJsonifyDeprecated.getUserInput.call(this);
27385
27346
  };
@@ -27402,12 +27363,20 @@ class Iframe extends React__namespace.Component {
27402
27363
  message: data.message
27403
27364
  });
27404
27365
  };
27405
- componentDidMount() {
27406
- $__default["default"](window).on("message", this.handleMessageEvent);
27407
- }
27408
- componentWillUnmount() {
27409
- $__default["default"](window).off("message", this.handleMessageEvent);
27410
- }
27366
+ change = (() => {
27367
+ var _this = this;
27368
+ return function () {
27369
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
27370
+ args[_key] = arguments[_key];
27371
+ }
27372
+ // @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
27373
+ return change.apply(_this, args);
27374
+ };
27375
+ })();
27376
+ simpleValidate = rubric => {
27377
+ // @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Iframe'.
27378
+ return Iframe.validate(this.getUserInput(), rubric);
27379
+ };
27411
27380
  render() {
27412
27381
  const style = {
27413
27382
  width: String(this.props.width),
@@ -27469,20 +27438,6 @@ class Iframe extends React__namespace.Component {
27469
27438
  allowFullScreen: this.props.allowFullScreen
27470
27439
  });
27471
27440
  }
27472
- change = (() => {
27473
- var _this = this;
27474
- return function () {
27475
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
27476
- args[_key] = arguments[_key];
27477
- }
27478
- // @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
27479
- return change.apply(_this, args);
27480
- };
27481
- })();
27482
- simpleValidate = rubric => {
27483
- // @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Iframe'.
27484
- return Iframe.validate(this.getUserInput(), rubric);
27485
- };
27486
27441
  }
27487
27442
 
27488
27443
  /**
@@ -27522,7 +27477,6 @@ var Iframe$1 = {
27522
27477
  hidden: true
27523
27478
  };
27524
27479
 
27525
- /* eslint-disable react/sort-comp */
27526
27480
  const defaultBoxSize = 400;
27527
27481
  const defaultRange = [0, 10];
27528
27482
  const defaultBackgroundImage$2 = {
@@ -27548,6 +27502,14 @@ class ImageWidget extends React__namespace.Component {
27548
27502
  caption: "",
27549
27503
  linterContext: PerseusLinter.linterContextDefault
27550
27504
  };
27505
+ static validate(userInput, rubric) {
27506
+ return {
27507
+ type: "points",
27508
+ earned: 0,
27509
+ total: 0,
27510
+ message: null
27511
+ };
27512
+ }
27551
27513
  change = (() => {
27552
27514
  var _this = this;
27553
27515
  return function () {
@@ -27697,14 +27659,6 @@ class ImageWidget extends React__namespace.Component {
27697
27659
  }
27698
27660
  }, title, image, caption);
27699
27661
  }
27700
- static validate(userInput, rubric) {
27701
- return {
27702
- type: "points",
27703
- earned: 0,
27704
- total: 0,
27705
- message: null
27706
- };
27707
- }
27708
27662
  }
27709
27663
  var Image$1 = {
27710
27664
  name: "image",
@@ -27796,6 +27750,14 @@ class Interaction extends React__namespace.Component {
27796
27750
  },
27797
27751
  elements: []
27798
27752
  };
27753
+ static validate(state, rubric) {
27754
+ return {
27755
+ type: "points",
27756
+ earned: 0,
27757
+ total: 0,
27758
+ message: null
27759
+ };
27760
+ }
27799
27761
  state = {
27800
27762
  variables: _getInitialVariables(this.props.elements),
27801
27763
  functions: _getInitialFunctions(this.props.elements)
@@ -27903,6 +27865,16 @@ class Interaction extends React__namespace.Component {
27903
27865
  return change.apply(_this, args);
27904
27866
  };
27905
27867
  })();
27868
+ getUserInput = () => {
27869
+ // TODO(eater): Perhaps we want to be able to record the state of the
27870
+ // user's interaction. Unfortunately sending all the props will
27871
+ // probably make the attempt payload too large. So for now, don't send
27872
+ // anything.
27873
+ return {};
27874
+ };
27875
+ simpleValidate = rubric => {
27876
+ return Interaction.validate(this.getUserInput(), rubric);
27877
+ };
27906
27878
  render() {
27907
27879
  const range = this.props.graph.range;
27908
27880
  let labels = this.props.graph.labels;
@@ -28230,24 +28202,6 @@ class Interaction extends React__namespace.Component {
28230
28202
  }
28231
28203
  }, this));
28232
28204
  }
28233
- getUserInput = () => {
28234
- // TODO(eater): Perhaps we want to be able to record the state of the
28235
- // user's interaction. Unfortunately sending all the props will
28236
- // probably make the attempt payload too large. So for now, don't send
28237
- // anything.
28238
- return {};
28239
- };
28240
- simpleValidate = rubric => {
28241
- return Interaction.validate(this.getUserInput(), rubric);
28242
- };
28243
- static validate(state, rubric) {
28244
- return {
28245
- type: "points",
28246
- earned: 0,
28247
- total: 0,
28248
- message: null
28249
- };
28250
- }
28251
28205
  }
28252
28206
  const _getInitialVariables = elements => {
28253
28207
  const variables = {};
@@ -29756,44 +29710,6 @@ class NumberInput extends React__namespace.Component {
29756
29710
  state = {
29757
29711
  format: this.props.format
29758
29712
  };
29759
- render() {
29760
- let classes = classNames__default["default"]({
29761
- "number-input": true,
29762
- "invalid-input": !this._checkValidity(this.props.value),
29763
- mini: this.props.size === "mini",
29764
- small: this.props.size === "small",
29765
- normal: this.props.size === "normal"
29766
- });
29767
- if (this.props.className != null) {
29768
- classes = classes + " " + this.props.className;
29769
- }
29770
- const {
29771
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
29772
- onFormatChange,
29773
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
29774
- checkValidity,
29775
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
29776
- useArrowKeys,
29777
- ...restProps
29778
- } = this.props;
29779
- return /*#__PURE__*/React__namespace.createElement("input", _extends({}, restProps, {
29780
- className: classes,
29781
- type: "text"
29782
- // eslint-disable-next-line react/no-string-refs
29783
- ,
29784
- ref: "input",
29785
- onChange: this._handleChange,
29786
- onFocus: this._handleFocus,
29787
- onBlur: this._handleBlur,
29788
- onKeyPress: this._handleBlur,
29789
- onKeyDown: this._onKeyDown
29790
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLInputElement>'.
29791
- ,
29792
- onTouchStart: captureScratchpadTouchStart,
29793
- defaultValue: toNumericString(this.props.value, this.state.format),
29794
- value: undefined
29795
- }));
29796
- }
29797
29713
  componentDidUpdate(prevProps) {
29798
29714
  if (!kmath.number.equal(this.getValue(), this.props.value)) {
29799
29715
  this._setValue(this.props.value, this.state.format);
@@ -29910,6 +29826,44 @@ class NumberInput extends React__namespace.Component {
29910
29826
  // @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'val' does not exist on type 'JQueryStatic'.
29911
29827
  $__default["default"](ReactDOM__default["default"].findDOMNode(this.refs.input)).val(toNumericString(val, format));
29912
29828
  };
29829
+ render() {
29830
+ let classes = classNames__default["default"]({
29831
+ "number-input": true,
29832
+ "invalid-input": !this._checkValidity(this.props.value),
29833
+ mini: this.props.size === "mini",
29834
+ small: this.props.size === "small",
29835
+ normal: this.props.size === "normal"
29836
+ });
29837
+ if (this.props.className != null) {
29838
+ classes = classes + " " + this.props.className;
29839
+ }
29840
+ const {
29841
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
29842
+ onFormatChange,
29843
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
29844
+ checkValidity,
29845
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
29846
+ useArrowKeys,
29847
+ ...restProps
29848
+ } = this.props;
29849
+ return /*#__PURE__*/React__namespace.createElement("input", _extends({}, restProps, {
29850
+ className: classes,
29851
+ type: "text"
29852
+ // eslint-disable-next-line react/no-string-refs
29853
+ ,
29854
+ ref: "input",
29855
+ onChange: this._handleChange,
29856
+ onFocus: this._handleFocus,
29857
+ onBlur: this._handleBlur,
29858
+ onKeyPress: this._handleBlur,
29859
+ onKeyDown: this._onKeyDown
29860
+ // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLInputElement>'.
29861
+ ,
29862
+ onTouchStart: captureScratchpadTouchStart,
29863
+ defaultValue: toNumericString(this.props.value, this.state.format),
29864
+ value: undefined
29865
+ }));
29866
+ }
29913
29867
  }
29914
29868
 
29915
29869
  const truth = () => true;
@@ -29927,6 +29881,14 @@ class RangeInput extends React__namespace.Component {
29927
29881
  static defaultProps = {
29928
29882
  placeholder: [null, null]
29929
29883
  };
29884
+ onChange = (i, newVal) => {
29885
+ const value = this.props.value;
29886
+ if (i === 0) {
29887
+ this.props.onChange([newVal, value[1]]);
29888
+ } else {
29889
+ this.props.onChange([value[0], newVal]);
29890
+ }
29891
+ };
29930
29892
  render() {
29931
29893
  const value = this.props.value;
29932
29894
  const checkValidity = this.props.checkValidity || truth;
@@ -29948,14 +29910,6 @@ class RangeInput extends React__namespace.Component {
29948
29910
  placeholder: this.props.placeholder[1]
29949
29911
  })));
29950
29912
  }
29951
- onChange = (i, newVal) => {
29952
- const value = this.props.value;
29953
- if (i === 0) {
29954
- this.props.onChange([newVal, value[1]]);
29955
- } else {
29956
- this.props.onChange([value[0], newVal]);
29957
- }
29958
- };
29959
29913
  }
29960
29914
 
29961
29915
  var components = /*#__PURE__*/Object.freeze({
@@ -30119,12 +30073,6 @@ const ItemExtras = [
30119
30073
 
30120
30074
  // prettier-ignore
30121
30075
 
30122
- // prettier-ignore
30123
-
30124
- // prettier-ignore
30125
-
30126
- // prettier-ignore
30127
-
30128
30076
  //prettier-ignore
30129
30077
 
30130
30078
  // A background image applied to various widgets.
@@ -30525,6 +30473,9 @@ const GraphLockedLayer = props => {
30525
30473
  };
30526
30474
 
30527
30475
  const actions = {
30476
+ global: {
30477
+ deleteIntent
30478
+ },
30528
30479
  angle: {
30529
30480
  movePoint
30530
30481
  },
@@ -30543,7 +30494,9 @@ const actions = {
30543
30494
  pointGraph: {
30544
30495
  movePoint,
30545
30496
  addPoint,
30546
- removePoint
30497
+ removePoint,
30498
+ focusPoint,
30499
+ blurPoint
30547
30500
  },
30548
30501
  polygon: {
30549
30502
  movePoint,
@@ -30564,6 +30517,12 @@ const actions = {
30564
30517
  movePoint
30565
30518
  }
30566
30519
  };
30520
+ const DELETE_INTENT = "delete-intent";
30521
+ function deleteIntent() {
30522
+ return {
30523
+ type: DELETE_INTENT
30524
+ };
30525
+ }
30567
30526
  const MOVE_LINE = "move-line";
30568
30527
  function moveLine(itemIndex, delta) {
30569
30528
  return {
@@ -30586,6 +30545,19 @@ function removePoint(index) {
30586
30545
  index
30587
30546
  };
30588
30547
  }
30548
+ const FOCUS_POINT = "focus-point";
30549
+ function focusPoint(index) {
30550
+ return {
30551
+ type: FOCUS_POINT,
30552
+ index
30553
+ };
30554
+ }
30555
+ const BLUR_POINT = "blur-point";
30556
+ function blurPoint() {
30557
+ return {
30558
+ type: BLUR_POINT
30559
+ };
30560
+ }
30589
30561
  const MOVE_ALL = "move-all";
30590
30562
  function moveAll(delta) {
30591
30563
  return {
@@ -32199,7 +32171,14 @@ function UnlimitedPointGraph(props) {
32199
32171
  }), props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
32200
32172
  key: i,
32201
32173
  point: point,
32202
- onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
32174
+ onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
32175
+ onFocusChange: isFocused => {
32176
+ if (isFocused) {
32177
+ dispatch(actions.pointGraph.focusPoint(i));
32178
+ } else {
32179
+ dispatch(actions.pointGraph.blurPoint());
32180
+ }
32181
+ }
32203
32182
  })));
32204
32183
  }
32205
32184
  function PointGraph(props) {
@@ -32355,6 +32334,7 @@ const MafsGraph = props => {
32355
32334
  } = props;
32356
32335
  const [width, height] = props.box;
32357
32336
  const tickStep = props.step;
32337
+ const graphRef = React__namespace.useRef(null);
32358
32338
  return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
32359
32339
  value: {
32360
32340
  range: state.range,
@@ -32372,16 +32352,24 @@ const MafsGraph = props => {
32372
32352
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
32373
32353
  className: "mafs-graph",
32374
32354
  style: {
32375
- height,
32376
- width: "intrinsic",
32377
32355
  position: "relative",
32378
32356
  padding: "25px 25px 0 0",
32379
32357
  boxSizing: "content-box",
32380
32358
  marginLeft: "20px",
32381
32359
  marginBottom: "20px",
32382
32360
  pointerEvents: props.static ? "none" : "auto",
32383
- userSelect: "none"
32384
- }
32361
+ userSelect: "none",
32362
+ width,
32363
+ height
32364
+ },
32365
+ onKeyUp: event => {
32366
+ if (event.key === "Backspace") {
32367
+ dispatch(actions.global.deleteIntent());
32368
+ graphRef.current?.focus();
32369
+ }
32370
+ },
32371
+ ref: graphRef,
32372
+ tabIndex: 0
32385
32373
  }, /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
32386
32374
  box: props.box,
32387
32375
  backgroundImage: props.backgroundImage
@@ -32567,7 +32555,8 @@ function initializeGraphState(params) {
32567
32555
  ...shared,
32568
32556
  type: graph.type,
32569
32557
  coords: getPointCoords(graph, range, step),
32570
- numPoints: graph.numPoints || 0
32558
+ numPoints: graph.numPoints || 0,
32559
+ focusedPointIndex: null
32571
32560
  };
32572
32561
  case "circle":
32573
32562
  return {
@@ -32827,10 +32816,49 @@ function interactiveGraphReducer(state, action) {
32827
32816
  return doAddPoint(state, action);
32828
32817
  case REMOVE_POINT:
32829
32818
  return doRemovePoint(state, action);
32819
+ case FOCUS_POINT:
32820
+ return doFocusPoint(state, action);
32821
+ case BLUR_POINT:
32822
+ return doBlurPoint(state);
32823
+ case DELETE_INTENT:
32824
+ return doDeleteIntent(state);
32830
32825
  default:
32831
32826
  throw new wonderStuffCore.UnreachableCaseError(action);
32832
32827
  }
32833
32828
  }
32829
+ function doDeleteIntent(state, action) {
32830
+ // For unlimited point graphs
32831
+ if (state.type === "point" && state.numPoints === "unlimited") {
32832
+ // if there's a focused point
32833
+ if (state.focusedPointIndex !== null) {
32834
+ // Remove the focused focus
32835
+ return doRemovePoint(state, actions.pointGraph.removePoint(state.focusedPointIndex));
32836
+ }
32837
+ }
32838
+ return state;
32839
+ }
32840
+ function doFocusPoint(state, action) {
32841
+ switch (state.type) {
32842
+ case "point":
32843
+ return {
32844
+ ...state,
32845
+ focusedPointIndex: action.index
32846
+ };
32847
+ default:
32848
+ return state;
32849
+ }
32850
+ }
32851
+ function doBlurPoint(state, action) {
32852
+ switch (state.type) {
32853
+ case "point":
32854
+ return {
32855
+ ...state,
32856
+ focusedPointIndex: null
32857
+ };
32858
+ default:
32859
+ return state;
32860
+ }
32861
+ }
32834
32862
  function doMovePointInFigure(state, action) {
32835
32863
  switch (state.type) {
32836
32864
  case "segment":
@@ -33196,7 +33224,8 @@ function doRemovePoint(state, action) {
33196
33224
  }
33197
33225
  return {
33198
33226
  ...state,
33199
- coords: state.coords.filter((_, i) => i !== action.index)
33227
+ coords: state.coords.filter((_, i) => i !== action.index),
33228
+ focusedPointIndex: null
33200
33229
  };
33201
33230
  }
33202
33231
  const getDeltaVertex = (maxMoves, minMoves, delta) => {
@@ -33624,9 +33653,6 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
33624
33653
  }));
33625
33654
  });
33626
33655
 
33627
- const {
33628
- DeprecationMixin
33629
- } = Util;
33630
33656
  const TRASH_ICON_URI = "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
33631
33657
  const defaultBackgroundImage = {
33632
33658
  url: null
@@ -33655,13 +33681,6 @@ function capitalize(str) {
33655
33681
  function numSteps(range, step) {
33656
33682
  return Math.floor((range[1] - range[0]) / step);
33657
33683
  }
33658
- const deprecatedProps = {
33659
- showGraph: function (props) {
33660
- return {
33661
- markings: props.showGraph ? "graph" : "none"
33662
- };
33663
- }
33664
- };
33665
33684
  const _getShouldShowInstructions = props => {
33666
33685
  return _isClickToAddPoints(props) && (
33667
33686
  // @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
@@ -33719,11 +33738,6 @@ class LegacyInteractiveGraph extends React__namespace.Component {
33719
33738
  state = {
33720
33739
  shouldShowInstructions: _getShouldShowInstructions(this.props)
33721
33740
  };
33722
-
33723
- // TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
33724
- UNSAFE_componentWillMount() {
33725
- DeprecationMixin.UNSAFE_componentWillMount.call(this);
33726
- }
33727
33741
  componentDidMount() {
33728
33742
  if (this.refs.graph) {
33729
33743
  // eslint-disable-next-line react/no-string-refs
@@ -33762,7 +33776,6 @@ class LegacyInteractiveGraph extends React__namespace.Component {
33762
33776
  // @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
33763
33777
  props.graph.coords == null || props.graph.coords.length === 0);
33764
33778
  };
33765
- deprecatedProps = deprecatedProps;
33766
33779
  setGraphie = newGraphie => {
33767
33780
  this.graphie = newGraphie;
33768
33781
  this.setupGraphie();
@@ -37552,73 +37565,6 @@ class Draggable extends React__namespace.Component {
37552
37565
  passive: false
37553
37566
  } : false);
37554
37567
  }
37555
- componentWillUnmount() {
37556
- this._mounted = false;
37557
- // Event handlers should be unbound before component unmounting, but
37558
- // just in case...
37559
- if (this.isMouseMoveUpBound) {
37560
- this.unbindMouseMoveUp();
37561
- }
37562
- if (this.animationFrameRequest) {
37563
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
37564
- // eslint-disable-next-line no-restricted-syntax
37565
- cancelAnimationFrame(this.animationFrameRequest);
37566
- }
37567
- document.removeEventListener("touchmove", this.onMouseMove);
37568
- }
37569
- getCurrentPosition = () => {
37570
- return {
37571
- left: this.state.startPosition.left + this.state.mouse.left - this.state.startMouse.left,
37572
- top: this.state.startPosition.top + this.state.mouse.top - this.state.startMouse.top
37573
- };
37574
- };
37575
- render() {
37576
- const {
37577
- includePadding,
37578
- layout,
37579
- state: type
37580
- } = this.props;
37581
-
37582
- // We need to keep backwards compatbility with rules specified directly
37583
- // in CSS. Hence the hacky tacking on of manual classNames.
37584
- // See sortable.less for details.
37585
- let className = aphrodite.css(styles$d.card, styles$d.draggable, layout === Layout.HORIZONTAL && styles$d.horizontalCard, layout === Layout.VERTICAL && styles$d.verticalCard, type === ItemState.DRAGGING && styles$d.dragging, type === ItemState.DISABLED && styles$d.disabled, !includePadding && styles$d.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
37586
- if (!includePadding) {
37587
- className += " perseus-sortable-draggable-unpadded";
37588
- }
37589
- const style = {
37590
- position: "static"
37591
- };
37592
- if (this.props.state === ItemState.DRAGGING || this.props.state === ItemState.ANIMATING) {
37593
- ___default["default"].extend(style, {
37594
- position: "absolute"
37595
- }, this.getCurrentPosition());
37596
- }
37597
- if (this.props.width) {
37598
- style.width = this.props.width + 1; // Fix for non-integer widths
37599
- }
37600
-
37601
- if (this.props.height) {
37602
- style.height = this.props.height;
37603
- }
37604
- if (this.props.margin != null) {
37605
- style.margin = this.props.margin;
37606
- }
37607
- return /*#__PURE__*/React__namespace.createElement("li", {
37608
- className: className,
37609
- style: style,
37610
- onMouseDown: this.onMouseDown,
37611
- onTouchStart: this.onMouseDown,
37612
- onTouchMove: this.onMouseMove,
37613
- onTouchEnd: this.onMouseUp,
37614
- onTouchCancel: this.onMouseUp
37615
- }, /*#__PURE__*/React__namespace.createElement(Renderer, {
37616
- content: this.props.content,
37617
- linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "draggable"),
37618
- onRender: this.props.onRender,
37619
- strings: this.context.strings
37620
- }));
37621
- }
37622
37568
  componentDidUpdate(prevProps) {
37623
37569
  if (this.props.state === prevProps.state) {
37624
37570
  return;
@@ -37640,6 +37586,26 @@ class Draggable extends React__namespace.Component {
37640
37586
  $__default["default"](ReactDOM__default["default"].findDOMNode(this)).finish();
37641
37587
  }
37642
37588
  }
37589
+ componentWillUnmount() {
37590
+ this._mounted = false;
37591
+ // Event handlers should be unbound before component unmounting, but
37592
+ // just in case...
37593
+ if (this.isMouseMoveUpBound) {
37594
+ this.unbindMouseMoveUp();
37595
+ }
37596
+ if (this.animationFrameRequest) {
37597
+ // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
37598
+ // eslint-disable-next-line no-restricted-syntax
37599
+ cancelAnimationFrame(this.animationFrameRequest);
37600
+ }
37601
+ document.removeEventListener("touchmove", this.onMouseMove);
37602
+ }
37603
+ getCurrentPosition = () => {
37604
+ return {
37605
+ left: this.state.startPosition.left + this.state.mouse.left - this.state.startMouse.left,
37606
+ top: this.state.startPosition.top + this.state.mouse.top - this.state.startMouse.top
37607
+ };
37608
+ };
37643
37609
  bindMouseMoveUp = () => {
37644
37610
  this.isMouseMoveUpBound = true;
37645
37611
  $__default["default"](document).on("mousemove", this.onMouseMove);
@@ -37727,6 +37693,53 @@ class Draggable extends React__namespace.Component {
37727
37693
  this.props.onMouseUp();
37728
37694
  }
37729
37695
  };
37696
+ render() {
37697
+ const {
37698
+ includePadding,
37699
+ layout,
37700
+ state: type
37701
+ } = this.props;
37702
+
37703
+ // We need to keep backwards compatbility with rules specified directly
37704
+ // in CSS. Hence the hacky tacking on of manual classNames.
37705
+ // See sortable.less for details.
37706
+ let className = aphrodite.css(styles$d.card, styles$d.draggable, layout === Layout.HORIZONTAL && styles$d.horizontalCard, layout === Layout.VERTICAL && styles$d.verticalCard, type === ItemState.DRAGGING && styles$d.dragging, type === ItemState.DISABLED && styles$d.disabled, !includePadding && styles$d.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
37707
+ if (!includePadding) {
37708
+ className += " perseus-sortable-draggable-unpadded";
37709
+ }
37710
+ const style = {
37711
+ position: "static"
37712
+ };
37713
+ if (this.props.state === ItemState.DRAGGING || this.props.state === ItemState.ANIMATING) {
37714
+ ___default["default"].extend(style, {
37715
+ position: "absolute"
37716
+ }, this.getCurrentPosition());
37717
+ }
37718
+ if (this.props.width) {
37719
+ style.width = this.props.width + 1; // Fix for non-integer widths
37720
+ }
37721
+
37722
+ if (this.props.height) {
37723
+ style.height = this.props.height;
37724
+ }
37725
+ if (this.props.margin != null) {
37726
+ style.margin = this.props.margin;
37727
+ }
37728
+ return /*#__PURE__*/React__namespace.createElement("li", {
37729
+ className: className,
37730
+ style: style,
37731
+ onMouseDown: this.onMouseDown,
37732
+ onTouchStart: this.onMouseDown,
37733
+ onTouchMove: this.onMouseMove,
37734
+ onTouchEnd: this.onMouseUp,
37735
+ onTouchCancel: this.onMouseUp
37736
+ }, /*#__PURE__*/React__namespace.createElement(Renderer, {
37737
+ content: this.props.content,
37738
+ linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "draggable"),
37739
+ onRender: this.props.onRender,
37740
+ strings: this.context.strings
37741
+ }));
37742
+ }
37730
37743
  }
37731
37744
  class Sortable extends React__namespace.Component {
37732
37745
  static defaultProps = {
@@ -37740,6 +37753,33 @@ class Sortable extends React__namespace.Component {
37740
37753
  linterContext: PerseusLinter__namespace.linterContextDefault,
37741
37754
  waitForTexRendererToLoad: true
37742
37755
  };
37756
+ remeasureItems = ___default["default"].debounce(() => {
37757
+ this.setState({
37758
+ // Clear item measurements
37759
+ items: Sortable.clearItemMeasurements(this.state.items)
37760
+ });
37761
+ }, 20);
37762
+ static itemsFromProps(props) {
37763
+ const state = props.disabled ? ItemState.DISABLED : ItemState.STATIC;
37764
+ return props.options.map((option, i) => {
37765
+ return {
37766
+ option: option,
37767
+ key: i,
37768
+ state,
37769
+ width: 0,
37770
+ height: 0
37771
+ };
37772
+ });
37773
+ }
37774
+ static clearItemMeasurements(items) {
37775
+ return items.map(item => {
37776
+ return {
37777
+ ...item,
37778
+ width: 0,
37779
+ height: 0
37780
+ };
37781
+ });
37782
+ }
37743
37783
  constructor(props) {
37744
37784
  super(props);
37745
37785
  // Don't call this.setState() here!
@@ -37773,27 +37813,6 @@ class Sortable extends React__namespace.Component {
37773
37813
  }, 0);
37774
37814
  }
37775
37815
  }
37776
- static itemsFromProps(props) {
37777
- const state = props.disabled ? ItemState.DISABLED : ItemState.STATIC;
37778
- return props.options.map((option, i) => {
37779
- return {
37780
- option: option,
37781
- key: i,
37782
- state,
37783
- width: 0,
37784
- height: 0
37785
- };
37786
- });
37787
- }
37788
- static clearItemMeasurements(items) {
37789
- return items.map(item => {
37790
- return {
37791
- ...item,
37792
- width: 0,
37793
- height: 0
37794
- };
37795
- });
37796
- }
37797
37816
  measureItems() {
37798
37817
  // Measure all items and cache what their dimensions should be, taking
37799
37818
  // into account constraints and the current layout. This allows syncing
@@ -37845,129 +37864,6 @@ class Sortable extends React__namespace.Component {
37845
37864
  });
37846
37865
  });
37847
37866
  }
37848
- remeasureItems = ___default["default"].debounce(() => {
37849
- this.setState({
37850
- // Clear item measurements
37851
- items: Sortable.clearItemMeasurements(this.state.items)
37852
- });
37853
- }, 20);
37854
- render() {
37855
- // To minimize layout shift, we display a spinner until our math
37856
- // renderer is ready to render the math inside the sortable. To
37857
- // do this, we:
37858
- // - render a dummy TeX component to force the math renderer to load
37859
- // - display a spinner until the TeX component calls its onRender
37860
- // callback, signifying that the math is rendered (from which we can
37861
- // infer that the math renderer has loaded)
37862
- //
37863
- // If we didn't do this, the user might see a sortable with empty
37864
- // cells on first render, and then the math would pop in a few moments
37865
- // later once the rendering library loaded.
37866
- if (this.props.waitForTexRendererToLoad && !this.state.texRendererLoaded) {
37867
- const {
37868
- TeX
37869
- } = getDependencies();
37870
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(wonderBlocksProgressSpinner.CircularSpinner, null), /*#__PURE__*/React__namespace.createElement("div", {
37871
- style: {
37872
- display: "none"
37873
- }
37874
- }, /*#__PURE__*/React__namespace.createElement(TeX, {
37875
- onRender: () => this.setState({
37876
- texRendererLoaded: true
37877
- })
37878
- }, "1")));
37879
- }
37880
- const cards = [];
37881
- const {
37882
- layout
37883
- } = this.props;
37884
- // We need to keep backwards compatbility with rules specified directly
37885
- // in CSS. See sortable.less for details.
37886
- const className = aphrodite.css(styles$d.sortable) + " perseus-sortable";
37887
- const syncWidth = this.props.constraints?.width || layout === Layout.VERTICAL;
37888
- const syncHeight = this.props.constraints?.height || layout === Layout.HORIZONTAL;
37889
- ___default["default"].each(this.state.items, function (item, i, items) {
37890
- const isLast = i === items.length - 1;
37891
- const isStatic = item.state === ItemState.STATIC || item.state === ItemState.DISABLED;
37892
- let margin;
37893
-
37894
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37895
- if (this.props.layout === Layout.HORIZONTAL) {
37896
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37897
- margin = "0 " + this.props.margin + "px 0 0"; // right
37898
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37899
- } else if (this.props.layout === Layout.VERTICAL) {
37900
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37901
- margin = "0 0 " + this.props.margin + "px 0"; // bottom
37902
- }
37903
-
37904
- cards.push( /*#__PURE__*/React__namespace.createElement(Draggable, {
37905
- content: item.option,
37906
- key: item.key,
37907
- state: item.state
37908
- // @ts-expect-error - TS2769 - No overload matches this call.
37909
- ,
37910
- ref: item.key,
37911
- width: syncWidth ? item.width : undefined,
37912
- height: syncHeight ? item.height : undefined,
37913
- layout: layout
37914
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37915
- ,
37916
- includePadding: this.props.padding,
37917
- margin: isLast && isStatic ? 0 : margin,
37918
- endPosition: item.endPosition,
37919
- linterContext: PerseusLinter__namespace.pushContextStack(
37920
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37921
- this.props.linterContext, "sortable")
37922
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37923
- ,
37924
- onRender: this.remeasureItems
37925
- // eslint-disable-next-line react/jsx-no-bind
37926
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37927
- ,
37928
- onMouseDown: this.onMouseDown.bind(this, item.key)
37929
- // eslint-disable-next-line react/jsx-no-bind
37930
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37931
- ,
37932
- onMouseMove: this.onMouseMove.bind(this, item.key)
37933
- // eslint-disable-next-line react/jsx-no-bind
37934
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37935
- ,
37936
- onMouseUp: this.onMouseUp.bind(this, item.key)
37937
- // eslint-disable-next-line react/jsx-no-bind
37938
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37939
- ,
37940
- onTouchMove: this.onMouseMove.bind(this, item.key)
37941
- // eslint-disable-next-line react/jsx-no-bind
37942
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37943
- ,
37944
- onTouchEnd: this.onMouseUp.bind(this, item.key)
37945
- // eslint-disable-next-line react/jsx-no-bind
37946
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37947
- ,
37948
- onTouchCancel: this.onMouseUp.bind(this, item.key)
37949
- // eslint-disable-next-line react/jsx-no-bind
37950
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37951
- ,
37952
- onAnimationEnd: this.onAnimationEnd.bind(
37953
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37954
- this, item.key)
37955
- }));
37956
- if (item.state === ItemState.DRAGGING || item.state === ItemState.ANIMATING) {
37957
- cards.push( /*#__PURE__*/React__namespace.createElement(Placeholder, {
37958
- key: "placeholder_" + item.key,
37959
- ref: "placeholder_" + item.key,
37960
- width: item.width,
37961
- height: item.height,
37962
- layout: layout,
37963
- margin: isLast ? 0 : margin
37964
- }));
37965
- }
37966
- }, this);
37967
- return /*#__PURE__*/React__namespace.createElement("ul", {
37968
- className: className
37969
- }, cards);
37970
- }
37971
37867
  onMouseDown(key) {
37972
37868
  // Static -> Dragging
37973
37869
  const items = ___default["default"].map(this.state.items, function (item) {
@@ -38113,6 +38009,123 @@ class Sortable extends React__namespace.Component {
38113
38009
  getOptions() {
38114
38010
  return ___default["default"].pluck(this.state.items, "option");
38115
38011
  }
38012
+ render() {
38013
+ // To minimize layout shift, we display a spinner until our math
38014
+ // renderer is ready to render the math inside the sortable. To
38015
+ // do this, we:
38016
+ // - render a dummy TeX component to force the math renderer to load
38017
+ // - display a spinner until the TeX component calls its onRender
38018
+ // callback, signifying that the math is rendered (from which we can
38019
+ // infer that the math renderer has loaded)
38020
+ //
38021
+ // If we didn't do this, the user might see a sortable with empty
38022
+ // cells on first render, and then the math would pop in a few moments
38023
+ // later once the rendering library loaded.
38024
+ if (this.props.waitForTexRendererToLoad && !this.state.texRendererLoaded) {
38025
+ const {
38026
+ TeX
38027
+ } = getDependencies();
38028
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(wonderBlocksProgressSpinner.CircularSpinner, null), /*#__PURE__*/React__namespace.createElement("div", {
38029
+ style: {
38030
+ display: "none"
38031
+ }
38032
+ }, /*#__PURE__*/React__namespace.createElement(TeX, {
38033
+ onRender: () => this.setState({
38034
+ texRendererLoaded: true
38035
+ })
38036
+ }, "1")));
38037
+ }
38038
+ const cards = [];
38039
+ const {
38040
+ layout
38041
+ } = this.props;
38042
+ // We need to keep backwards compatbility with rules specified directly
38043
+ // in CSS. See sortable.less for details.
38044
+ const className = aphrodite.css(styles$d.sortable) + " perseus-sortable";
38045
+ const syncWidth = this.props.constraints?.width || layout === Layout.VERTICAL;
38046
+ const syncHeight = this.props.constraints?.height || layout === Layout.HORIZONTAL;
38047
+ ___default["default"].each(this.state.items, function (item, i, items) {
38048
+ const isLast = i === items.length - 1;
38049
+ const isStatic = item.state === ItemState.STATIC || item.state === ItemState.DISABLED;
38050
+ let margin;
38051
+
38052
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38053
+ if (this.props.layout === Layout.HORIZONTAL) {
38054
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38055
+ margin = "0 " + this.props.margin + "px 0 0"; // right
38056
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38057
+ } else if (this.props.layout === Layout.VERTICAL) {
38058
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38059
+ margin = "0 0 " + this.props.margin + "px 0"; // bottom
38060
+ }
38061
+
38062
+ cards.push( /*#__PURE__*/React__namespace.createElement(Draggable, {
38063
+ content: item.option,
38064
+ key: item.key,
38065
+ state: item.state
38066
+ // @ts-expect-error - TS2769 - No overload matches this call.
38067
+ ,
38068
+ ref: item.key,
38069
+ width: syncWidth ? item.width : undefined,
38070
+ height: syncHeight ? item.height : undefined,
38071
+ layout: layout
38072
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38073
+ ,
38074
+ includePadding: this.props.padding,
38075
+ margin: isLast && isStatic ? 0 : margin,
38076
+ endPosition: item.endPosition,
38077
+ linterContext: PerseusLinter__namespace.pushContextStack(
38078
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38079
+ this.props.linterContext, "sortable")
38080
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38081
+ ,
38082
+ onRender: this.remeasureItems
38083
+ // eslint-disable-next-line react/jsx-no-bind
38084
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38085
+ ,
38086
+ onMouseDown: this.onMouseDown.bind(this, item.key)
38087
+ // eslint-disable-next-line react/jsx-no-bind
38088
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38089
+ ,
38090
+ onMouseMove: this.onMouseMove.bind(this, item.key)
38091
+ // eslint-disable-next-line react/jsx-no-bind
38092
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38093
+ ,
38094
+ onMouseUp: this.onMouseUp.bind(this, item.key)
38095
+ // eslint-disable-next-line react/jsx-no-bind
38096
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38097
+ ,
38098
+ onTouchMove: this.onMouseMove.bind(this, item.key)
38099
+ // eslint-disable-next-line react/jsx-no-bind
38100
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38101
+ ,
38102
+ onTouchEnd: this.onMouseUp.bind(this, item.key)
38103
+ // eslint-disable-next-line react/jsx-no-bind
38104
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38105
+ ,
38106
+ onTouchCancel: this.onMouseUp.bind(this, item.key)
38107
+ // eslint-disable-next-line react/jsx-no-bind
38108
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38109
+ ,
38110
+ onAnimationEnd: this.onAnimationEnd.bind(
38111
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38112
+ this, item.key)
38113
+ }));
38114
+ if (item.state === ItemState.DRAGGING || item.state === ItemState.ANIMATING) {
38115
+ cards.push( /*#__PURE__*/React__namespace.createElement(Placeholder, {
38116
+ key: "placeholder_" + item.key,
38117
+ ref: "placeholder_" + item.key,
38118
+ width: item.width,
38119
+ height: item.height,
38120
+ layout: layout,
38121
+ margin: isLast ? 0 : margin
38122
+ }));
38123
+ }
38124
+ }, this);
38125
+ return /*#__PURE__*/React__namespace.createElement("ul", {
38126
+ className: className
38127
+ }, cards);
38128
+ }
38116
38129
  }
38117
38130
  const styles$d = aphrodite.StyleSheet.create({
38118
38131
  sortable: {
@@ -38198,6 +38211,61 @@ class Matcher extends React__namespace.Component {
38198
38211
  rightHeight: 0,
38199
38212
  texRendererLoaded: false
38200
38213
  };
38214
+ changeAndTrack = e => {
38215
+ this.props.onChange(e);
38216
+ this.props.trackInteraction();
38217
+ };
38218
+ onMeasureLeft = dimensions => {
38219
+ const height = ___default["default"].max(dimensions.heights);
38220
+ this.setState({
38221
+ leftHeight: height
38222
+ });
38223
+ };
38224
+ onMeasureRight = dimensions => {
38225
+ const height = ___default["default"].max(dimensions.heights);
38226
+ this.setState({
38227
+ rightHeight: height
38228
+ });
38229
+ };
38230
+ getUserInput = () => {
38231
+ // If the math renderer hasn't loaded then we won't be able to get the
38232
+ // contents of the sortables on the left and right, so we just return
38233
+ // empty arrays until we render for the first time.
38234
+ if (!this.state.texRendererLoaded) {
38235
+ return {
38236
+ left: [],
38237
+ right: []
38238
+ };
38239
+ }
38240
+ return {
38241
+ // eslint-disable-next-line react/no-string-refs
38242
+ // @ts-expect-error - TS2339 - Property 'getOptions' does not exist on type 'ReactInstance'.
38243
+ left: this.refs.left.getOptions(),
38244
+ // eslint-disable-next-line react/no-string-refs
38245
+ // @ts-expect-error - TS2339 - Property 'getOptions' does not exist on type 'ReactInstance'.
38246
+ right: this.refs.right.getOptions()
38247
+ };
38248
+ };
38249
+
38250
+ // Programatic API for moving options
38251
+ // This is used by testing
38252
+ moveLeftOptionToIndex = (option, index) => {
38253
+ // eslint-disable-next-line react/no-string-refs
38254
+ // @ts-expect-error - TS2339 - Property 'moveOptionToIndex' does not exist on type 'ReactInstance'.
38255
+ this.refs.left.moveOptionToIndex(option, index);
38256
+ };
38257
+
38258
+ // Programatic API for moving options
38259
+ // This is used by testing
38260
+ moveRightOptionToIndex = (option, index) => {
38261
+ // eslint-disable-next-line react/no-string-refs
38262
+ // @ts-expect-error - TS2339 - Property 'moveOptionToIndex' does not exist on type 'ReactInstance'.
38263
+ this.refs.right.moveOptionToIndex(option, index);
38264
+ };
38265
+ simpleValidate = rubric => {
38266
+ // @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Matcher'.
38267
+ return Matcher.validate(this.getUserInput(), rubric);
38268
+ };
38201
38269
  render() {
38202
38270
  // To minimize layout shift, we display a spinner until our math
38203
38271
  // renderer is ready to render the math inside the matcher. To
@@ -38293,61 +38361,6 @@ class Matcher extends React__namespace.Component {
38293
38361
  ref: "right"
38294
38362
  })))));
38295
38363
  }
38296
- changeAndTrack = e => {
38297
- this.props.onChange(e);
38298
- this.props.trackInteraction();
38299
- };
38300
- onMeasureLeft = dimensions => {
38301
- const height = ___default["default"].max(dimensions.heights);
38302
- this.setState({
38303
- leftHeight: height
38304
- });
38305
- };
38306
- onMeasureRight = dimensions => {
38307
- const height = ___default["default"].max(dimensions.heights);
38308
- this.setState({
38309
- rightHeight: height
38310
- });
38311
- };
38312
- getUserInput = () => {
38313
- // If the math renderer hasn't loaded then we won't be able to get the
38314
- // contents of the sortables on the left and right, so we just return
38315
- // empty arrays until we render for the first time.
38316
- if (!this.state.texRendererLoaded) {
38317
- return {
38318
- left: [],
38319
- right: []
38320
- };
38321
- }
38322
- return {
38323
- // eslint-disable-next-line react/no-string-refs
38324
- // @ts-expect-error - TS2339 - Property 'getOptions' does not exist on type 'ReactInstance'.
38325
- left: this.refs.left.getOptions(),
38326
- // eslint-disable-next-line react/no-string-refs
38327
- // @ts-expect-error - TS2339 - Property 'getOptions' does not exist on type 'ReactInstance'.
38328
- right: this.refs.right.getOptions()
38329
- };
38330
- };
38331
-
38332
- // Programatic API for moving options
38333
- // This is used by testing
38334
- moveLeftOptionToIndex = (option, index) => {
38335
- // eslint-disable-next-line react/no-string-refs
38336
- // @ts-expect-error - TS2339 - Property 'moveOptionToIndex' does not exist on type 'ReactInstance'.
38337
- this.refs.left.moveOptionToIndex(option, index);
38338
- };
38339
-
38340
- // Programatic API for moving options
38341
- // This is used by testing
38342
- moveRightOptionToIndex = (option, index) => {
38343
- // eslint-disable-next-line react/no-string-refs
38344
- // @ts-expect-error - TS2339 - Property 'moveOptionToIndex' does not exist on type 'ReactInstance'.
38345
- this.refs.right.moveOptionToIndex(option, index);
38346
- };
38347
- simpleValidate = rubric => {
38348
- // @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Matcher'.
38349
- return Matcher.validate(this.getUserInput(), rubric);
38350
- };
38351
38364
  }
38352
38365
  ___default["default"].extend(Matcher, {
38353
38366
  validate: function (state, rubric) {
@@ -38539,6 +38552,144 @@ class Matrix extends React__namespace.Component {
38539
38552
  // Used in the `onBlur` and `onFocus` handlers
38540
38553
  this.cursorPosition = [0, 0];
38541
38554
  }
38555
+ getInputPaths = () => {
38556
+ const inputPaths = [];
38557
+ const maxRows = this.props.matrixBoardSize[0];
38558
+ const maxCols = this.props.matrixBoardSize[1];
38559
+ ___default["default"](maxRows).times(row => {
38560
+ ___default["default"](maxCols).times(col => {
38561
+ const inputPath = getInputPath$1(row, col);
38562
+ inputPaths.push(inputPath);
38563
+ });
38564
+ });
38565
+ return inputPaths;
38566
+ };
38567
+ getGrammarTypeForPath = inputPath => {
38568
+ return "number";
38569
+ };
38570
+ _handleFocus = (row, col) => {
38571
+ this.props.onFocus(getInputPath$1(row, col));
38572
+ };
38573
+ _handleBlur = (row, col) => {
38574
+ this.props.onBlur(getInputPath$1(row, col));
38575
+ };
38576
+ focus = () => {
38577
+ this.focusInputPath(getDefaultPath$1());
38578
+ return true;
38579
+ };
38580
+ focusInputPath = path => {
38581
+ const inputID = getRefForPath$1(path);
38582
+ // eslint-disable-next-line react/no-string-refs
38583
+ // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
38584
+ this.refs[inputID].focus();
38585
+ };
38586
+ blurInputPath = path => {
38587
+ if (path.length === 0) {
38588
+ path = getDefaultPath$1();
38589
+ }
38590
+ const inputID = getRefForPath$1(path);
38591
+ // eslint-disable-next-line react/no-string-refs
38592
+ // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
38593
+ this.refs[inputID].blur();
38594
+ };
38595
+ getDOMNodeForPath = inputPath => {
38596
+ const inputID = getRefForPath$1(inputPath);
38597
+ // eslint-disable-next-line react/no-string-refs
38598
+ return ReactDOM__default["default"].findDOMNode(this.refs[inputID]);
38599
+ };
38600
+ setInputValue = (inputPath, value, callback) => {
38601
+ const row = getRowFromPath$1(inputPath);
38602
+ const col = getColumnFromPath$1(inputPath);
38603
+ this.onValueChange(row, col, value, callback);
38604
+ };
38605
+ handleKeyDown = (row, col, e) => {
38606
+ const maxRow = this.props.matrixBoardSize[0];
38607
+ const maxCol = this.props.matrixBoardSize[1];
38608
+ let enterTheMatrix = null;
38609
+
38610
+ // eslint-disable-next-line react/no-string-refs
38611
+ const curInput = this.refs[getRefForPath$1(getInputPath$1(row, col))];
38612
+ // @ts-expect-error - TS2339 - Property 'getStringValue' does not exist on type 'ReactInstance'.
38613
+ const curValueString = curInput.getStringValue();
38614
+ // @ts-expect-error - TS2339 - Property 'getSelectionStart' does not exist on type 'ReactInstance'.
38615
+ const cursorStartPosition = curInput.getSelectionStart();
38616
+ // @ts-expect-error - TS2339 - Property 'getSelectionEnd' does not exist on type 'ReactInstance'.
38617
+ const cursorEndPosition = curInput.getSelectionEnd();
38618
+ let nextPath = null;
38619
+ if (e.key === "ArrowUp" && row > 0) {
38620
+ // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'null'.
38621
+ nextPath = getInputPath$1(row - 1, col);
38622
+ } else if (e.key === "ArrowDown" && row + 1 < maxRow) {
38623
+ // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'null'.
38624
+ nextPath = getInputPath$1(row + 1, col);
38625
+ } else if (e.key === "ArrowLeft" && col > 0) {
38626
+ if (cursorStartPosition === 0 && cursorEndPosition === 0) {
38627
+ // Only go to next input if we're at the *start* of the content
38628
+ // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'null'.
38629
+ nextPath = getInputPath$1(row, col - 1);
38630
+ }
38631
+ } else if (e.key === "ArrowRight" && col + 1 < maxCol) {
38632
+ if (cursorStartPosition === curValueString.length) {
38633
+ // Only go to next input if we're at the *end* of the content
38634
+ // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'null'.
38635
+ nextPath = getInputPath$1(row, col + 1);
38636
+ }
38637
+ } else if (e.key === "Enter") {
38638
+ // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'null'.
38639
+ enterTheMatrix = this.state.enterTheMatrix + 1;
38640
+ } else if (e.key === "Escape") {
38641
+ // @ts-expect-error - TS2322 - Type '0' is not assignable to type 'null'.
38642
+ enterTheMatrix = 0;
38643
+ }
38644
+ if (nextPath) {
38645
+ // Prevent the cursor from jumping again inside the next input
38646
+ e.preventDefault();
38647
+
38648
+ // Focus the input and move the cursor to the end of it.
38649
+ // eslint-disable-next-line react/no-string-refs
38650
+ const input = this.refs[getRefForPath$1(nextPath)];
38651
+
38652
+ // Multiply by 2 to ensure the cursor always ends up at the end;
38653
+ // Opera sometimes sees a carriage return as 2 characters.
38654
+ // @ts-expect-error - TS2339 - Property 'getStringValue' does not exist on type 'ReactInstance'.
38655
+ const inputValString = input.getStringValue();
38656
+ const valueLength = inputValString.length * 2;
38657
+
38658
+ // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
38659
+ input.focus();
38660
+ if (e.key === "ArrowRight") {
38661
+ // @ts-expect-error - TS2339 - Property 'setSelectionRange' does not exist on type 'ReactInstance'.
38662
+ input.setSelectionRange(0, 0);
38663
+ } else {
38664
+ // @ts-expect-error - TS2339 - Property 'setSelectionRange' does not exist on type 'ReactInstance'.
38665
+ input.setSelectionRange(valueLength, valueLength);
38666
+ }
38667
+ }
38668
+ if (enterTheMatrix != null) {
38669
+ this.setState({
38670
+ enterTheMatrix: enterTheMatrix
38671
+ });
38672
+ }
38673
+ };
38674
+ onValueChange = (row, column, value, cb) => {
38675
+ const answers = this.props.answers.map(answer => [...answer]);
38676
+ if (!answers[row]) {
38677
+ answers[row] = [];
38678
+ }
38679
+ answers[row][column] = value;
38680
+ this.props.onChange({
38681
+ answers
38682
+ }, cb);
38683
+ this.props.trackInteraction();
38684
+ };
38685
+ getUserInput = () => {
38686
+ return {
38687
+ answers: this.props.answers
38688
+ };
38689
+ };
38690
+ simpleValidate = rubric => {
38691
+ return Matrix.validate(this.getUserInput(), rubric, this.context.strings);
38692
+ };
38542
38693
  render() {
38543
38694
  // Set the input sizes through JS so we can control the size of the
38544
38695
  // brackets. (If we set them in CSS we won't know values until the
@@ -38694,144 +38845,6 @@ class Matrix extends React__namespace.Component {
38694
38845
  strings: this.context.strings
38695
38846
  })));
38696
38847
  }
38697
- getInputPaths = () => {
38698
- const inputPaths = [];
38699
- const maxRows = this.props.matrixBoardSize[0];
38700
- const maxCols = this.props.matrixBoardSize[1];
38701
- ___default["default"](maxRows).times(row => {
38702
- ___default["default"](maxCols).times(col => {
38703
- const inputPath = getInputPath$1(row, col);
38704
- inputPaths.push(inputPath);
38705
- });
38706
- });
38707
- return inputPaths;
38708
- };
38709
- getGrammarTypeForPath = inputPath => {
38710
- return "number";
38711
- };
38712
- _handleFocus = (row, col) => {
38713
- this.props.onFocus(getInputPath$1(row, col));
38714
- };
38715
- _handleBlur = (row, col) => {
38716
- this.props.onBlur(getInputPath$1(row, col));
38717
- };
38718
- focus = () => {
38719
- this.focusInputPath(getDefaultPath$1());
38720
- return true;
38721
- };
38722
- focusInputPath = path => {
38723
- const inputID = getRefForPath$1(path);
38724
- // eslint-disable-next-line react/no-string-refs
38725
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
38726
- this.refs[inputID].focus();
38727
- };
38728
- blurInputPath = path => {
38729
- if (path.length === 0) {
38730
- path = getDefaultPath$1();
38731
- }
38732
- const inputID = getRefForPath$1(path);
38733
- // eslint-disable-next-line react/no-string-refs
38734
- // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
38735
- this.refs[inputID].blur();
38736
- };
38737
- getDOMNodeForPath = inputPath => {
38738
- const inputID = getRefForPath$1(inputPath);
38739
- // eslint-disable-next-line react/no-string-refs
38740
- return ReactDOM__default["default"].findDOMNode(this.refs[inputID]);
38741
- };
38742
- setInputValue = (inputPath, value, callback) => {
38743
- const row = getRowFromPath$1(inputPath);
38744
- const col = getColumnFromPath$1(inputPath);
38745
- this.onValueChange(row, col, value, callback);
38746
- };
38747
- handleKeyDown = (row, col, e) => {
38748
- const maxRow = this.props.matrixBoardSize[0];
38749
- const maxCol = this.props.matrixBoardSize[1];
38750
- let enterTheMatrix = null;
38751
-
38752
- // eslint-disable-next-line react/no-string-refs
38753
- const curInput = this.refs[getRefForPath$1(getInputPath$1(row, col))];
38754
- // @ts-expect-error - TS2339 - Property 'getStringValue' does not exist on type 'ReactInstance'.
38755
- const curValueString = curInput.getStringValue();
38756
- // @ts-expect-error - TS2339 - Property 'getSelectionStart' does not exist on type 'ReactInstance'.
38757
- const cursorStartPosition = curInput.getSelectionStart();
38758
- // @ts-expect-error - TS2339 - Property 'getSelectionEnd' does not exist on type 'ReactInstance'.
38759
- const cursorEndPosition = curInput.getSelectionEnd();
38760
- let nextPath = null;
38761
- if (e.key === "ArrowUp" && row > 0) {
38762
- // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'null'.
38763
- nextPath = getInputPath$1(row - 1, col);
38764
- } else if (e.key === "ArrowDown" && row + 1 < maxRow) {
38765
- // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'null'.
38766
- nextPath = getInputPath$1(row + 1, col);
38767
- } else if (e.key === "ArrowLeft" && col > 0) {
38768
- if (cursorStartPosition === 0 && cursorEndPosition === 0) {
38769
- // Only go to next input if we're at the *start* of the content
38770
- // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'null'.
38771
- nextPath = getInputPath$1(row, col - 1);
38772
- }
38773
- } else if (e.key === "ArrowRight" && col + 1 < maxCol) {
38774
- if (cursorStartPosition === curValueString.length) {
38775
- // Only go to next input if we're at the *end* of the content
38776
- // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'null'.
38777
- nextPath = getInputPath$1(row, col + 1);
38778
- }
38779
- } else if (e.key === "Enter") {
38780
- // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'null'.
38781
- enterTheMatrix = this.state.enterTheMatrix + 1;
38782
- } else if (e.key === "Escape") {
38783
- // @ts-expect-error - TS2322 - Type '0' is not assignable to type 'null'.
38784
- enterTheMatrix = 0;
38785
- }
38786
- if (nextPath) {
38787
- // Prevent the cursor from jumping again inside the next input
38788
- e.preventDefault();
38789
-
38790
- // Focus the input and move the cursor to the end of it.
38791
- // eslint-disable-next-line react/no-string-refs
38792
- const input = this.refs[getRefForPath$1(nextPath)];
38793
-
38794
- // Multiply by 2 to ensure the cursor always ends up at the end;
38795
- // Opera sometimes sees a carriage return as 2 characters.
38796
- // @ts-expect-error - TS2339 - Property 'getStringValue' does not exist on type 'ReactInstance'.
38797
- const inputValString = input.getStringValue();
38798
- const valueLength = inputValString.length * 2;
38799
-
38800
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
38801
- input.focus();
38802
- if (e.key === "ArrowRight") {
38803
- // @ts-expect-error - TS2339 - Property 'setSelectionRange' does not exist on type 'ReactInstance'.
38804
- input.setSelectionRange(0, 0);
38805
- } else {
38806
- // @ts-expect-error - TS2339 - Property 'setSelectionRange' does not exist on type 'ReactInstance'.
38807
- input.setSelectionRange(valueLength, valueLength);
38808
- }
38809
- }
38810
- if (enterTheMatrix != null) {
38811
- this.setState({
38812
- enterTheMatrix: enterTheMatrix
38813
- });
38814
- }
38815
- };
38816
- onValueChange = (row, column, value, cb) => {
38817
- const answers = this.props.answers.map(answer => [...answer]);
38818
- if (!answers[row]) {
38819
- answers[row] = [];
38820
- }
38821
- answers[row][column] = value;
38822
- this.props.onChange({
38823
- answers
38824
- }, cb);
38825
- this.props.trackInteraction();
38826
- };
38827
- getUserInput = () => {
38828
- return {
38829
- answers: this.props.answers
38830
- };
38831
- };
38832
- simpleValidate = rubric => {
38833
- return Matrix.validate(this.getUserInput(), rubric, this.context.strings);
38834
- };
38835
38848
  }
38836
38849
  const propTransform$1 = editorProps => {
38837
38850
  // Remove answers before passing to widget
@@ -38863,7 +38876,7 @@ var Matrix$1 = {
38863
38876
  isLintable: true
38864
38877
  };
38865
38878
 
38866
- /* eslint-disable @babel/no-invalid-this, react/sort-comp */
38879
+ /* eslint-disable @babel/no-invalid-this */
38867
38880
  const defaultImage = {
38868
38881
  url: null,
38869
38882
  top: 0,
@@ -38905,6 +38918,64 @@ const Measurer = createReactClass__default["default"]({
38905
38918
  getInitialState: function () {
38906
38919
  return {};
38907
38920
  },
38921
+ componentDidMount: function () {
38922
+ this.setupGraphie();
38923
+ },
38924
+ componentDidUpdate: function (prevProps) {
38925
+ const shouldSetupGraphie = ___default["default"].any(["box", "showProtractor", "showRuler", "rulerLabel", "rulerTicks", "rulerPixels", "rulerLength"], function (prop) {
38926
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38927
+ return prevProps[prop] !== this.props[prop];
38928
+ }, this);
38929
+ if (shouldSetupGraphie) {
38930
+ this.setupGraphie();
38931
+ }
38932
+ },
38933
+ setupGraphie: function () {
38934
+ // eslint-disable-next-line react/no-string-refs
38935
+ const graphieDiv = ReactDOM__default["default"].findDOMNode(this.refs.graphieDiv);
38936
+ // @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'empty' does not exist on type 'JQueryStatic'.
38937
+ $__default["default"](graphieDiv).empty();
38938
+ // @ts-expect-error - Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLElement'.
38939
+ const graphie = this.graphie = GraphUtils.createGraphie(graphieDiv);
38940
+ const scale = [40, 40];
38941
+ const range = [[0, this.props.box[0] / scale[0]], [0, this.props.box[1] / scale[1]]];
38942
+ graphie.init({
38943
+ range: range,
38944
+ scale: scale
38945
+ });
38946
+ graphie.addMouseLayer({
38947
+ allowScratchpad: true,
38948
+ setDrawingAreaAvailable: this.props.apiOptions.setDrawingAreaAvailable
38949
+ });
38950
+ if (this.protractor) {
38951
+ this.protractor.remove();
38952
+ }
38953
+ if (this.props.showProtractor) {
38954
+ // @ts-expect-error - Property 'protractor' does not exist on type 'Graphie'.
38955
+ this.protractor = graphie.protractor([this.props.protractorX, this.props.protractorY]);
38956
+ }
38957
+ if (this.ruler) {
38958
+ this.ruler.remove();
38959
+ }
38960
+ if (this.props.showRuler) {
38961
+ // @ts-expect-error - Property 'ruler' does not exist on type 'Graphie'.
38962
+ this.ruler = graphie.ruler({
38963
+ center: [(range[0][0] + range[0][1]) / 2, (range[1][0] + range[1][1]) / 2],
38964
+ label: this.props.rulerLabel,
38965
+ pixelsPerUnit: this.props.rulerPixels,
38966
+ ticksPerUnit: this.props.rulerTicks,
38967
+ units: this.props.rulerLength
38968
+ });
38969
+ }
38970
+ },
38971
+ getUserInput: function () {
38972
+ return {};
38973
+ },
38974
+ simpleValidate: function (rubric) {
38975
+ // TODO(joel) - I don't understand how this is useful!
38976
+ return Measurer.validate(this.getUserInput(), rubric);
38977
+ },
38978
+ focus: $__default["default"].noop,
38908
38979
  render: function () {
38909
38980
  const image = ___default["default"].extend({}, defaultImage, this.props.image);
38910
38981
 
@@ -38928,65 +38999,7 @@ const Measurer = createReactClass__default["default"]({
38928
38999
  className: "graphie",
38929
39000
  ref: "graphieDiv"
38930
39001
  }));
38931
- },
38932
- componentDidMount: function () {
38933
- this.setupGraphie();
38934
- },
38935
- componentDidUpdate: function (prevProps) {
38936
- const shouldSetupGraphie = ___default["default"].any(["box", "showProtractor", "showRuler", "rulerLabel", "rulerTicks", "rulerPixels", "rulerLength"], function (prop) {
38937
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38938
- return prevProps[prop] !== this.props[prop];
38939
- }, this);
38940
- if (shouldSetupGraphie) {
38941
- this.setupGraphie();
38942
- }
38943
- },
38944
- setupGraphie: function () {
38945
- // eslint-disable-next-line react/no-string-refs
38946
- const graphieDiv = ReactDOM__default["default"].findDOMNode(this.refs.graphieDiv);
38947
- // @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'empty' does not exist on type 'JQueryStatic'.
38948
- $__default["default"](graphieDiv).empty();
38949
- // @ts-expect-error - Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLElement'.
38950
- const graphie = this.graphie = GraphUtils.createGraphie(graphieDiv);
38951
- const scale = [40, 40];
38952
- const range = [[0, this.props.box[0] / scale[0]], [0, this.props.box[1] / scale[1]]];
38953
- graphie.init({
38954
- range: range,
38955
- scale: scale
38956
- });
38957
- graphie.addMouseLayer({
38958
- allowScratchpad: true,
38959
- setDrawingAreaAvailable: this.props.apiOptions.setDrawingAreaAvailable
38960
- });
38961
- if (this.protractor) {
38962
- this.protractor.remove();
38963
- }
38964
- if (this.props.showProtractor) {
38965
- // @ts-expect-error - Property 'protractor' does not exist on type 'Graphie'.
38966
- this.protractor = graphie.protractor([this.props.protractorX, this.props.protractorY]);
38967
- }
38968
- if (this.ruler) {
38969
- this.ruler.remove();
38970
- }
38971
- if (this.props.showRuler) {
38972
- // @ts-expect-error - Property 'ruler' does not exist on type 'Graphie'.
38973
- this.ruler = graphie.ruler({
38974
- center: [(range[0][0] + range[0][1]) / 2, (range[1][0] + range[1][1]) / 2],
38975
- label: this.props.rulerLabel,
38976
- pixelsPerUnit: this.props.rulerPixels,
38977
- ticksPerUnit: this.props.rulerTicks,
38978
- units: this.props.rulerLength
38979
- });
38980
- }
38981
- },
38982
- getUserInput: function () {
38983
- return {};
38984
- },
38985
- simpleValidate: function (rubric) {
38986
- // TODO(joel) - I don't understand how this is useful!
38987
- return Measurer.validate(this.getUserInput(), rubric);
38988
- },
38989
- focus: $__default["default"].noop
39002
+ }
38990
39003
  });
38991
39004
  ___default["default"].extend(Measurer, {
38992
39005
  validate: function (state, rubric) {
@@ -40568,55 +40581,6 @@ class Card extends React__namespace.Component {
40568
40581
  state = {
40569
40582
  dragging: false
40570
40583
  };
40571
- render() {
40572
- let style = {};
40573
- if (this.props.floating) {
40574
- style = {
40575
- position: "absolute",
40576
- left: this.props.startOffset.left,
40577
- top: this.props.startOffset.top
40578
- };
40579
- }
40580
- if (this.props.width) {
40581
- style.width = this.props.width;
40582
- }
40583
- const className = ["card"];
40584
- if (this.props.stack) {
40585
- className.push("stack");
40586
- }
40587
- if (this.props.floating && !this.props.animating) {
40588
- className.push("dragging");
40589
- style.left += this.props.mouse.left - this.props.startMouse.left;
40590
- style.top += this.props.mouse.top - this.props.startMouse.top;
40591
- }
40592
-
40593
- // Pull out the content to get rendered
40594
- const rendererProps = ___default["default"].pick(this.props, "content");
40595
- const onMouseDown = this.props.animating ? $__default["default"].noop : this.onMouseDown;
40596
- return /*#__PURE__*/React__namespace.createElement("div", {
40597
- className: "card-wrap " + ClassNames.INTERACTIVE,
40598
- style: style,
40599
- onMouseDown: onMouseDown,
40600
- onTouchStart: onMouseDown,
40601
- onTouchEnd: this.onMouseUp,
40602
- onTouchCancel: this.onMouseUp
40603
- }, /*#__PURE__*/React__namespace.createElement("div", {
40604
- className: className.join(" ")
40605
- }, /*#__PURE__*/React__namespace.createElement(Renderer, _extends({}, rendererProps, {
40606
- linterContext: this.props.linterContext,
40607
- strings: this.context.strings
40608
- }))));
40609
- }
40610
- shouldComponentUpdate(nextProps, nextState) {
40611
- // Cards in the bank or drag list don't usually change -- they only
40612
- // reorder themselves -- so we want to skip the update to things a
40613
- // little faster. We also need to re-render if the content changes,
40614
- // which happens only in the editor. (We do want to update the floating
40615
- // card on mouse move to update its position.)
40616
- return this.props.floating || nextProps.floating || this.props.content !== nextProps.content ||
40617
- // TODO(alpert): Remove ref here after fixing facebook/react#1392.
40618
- this.props.fakeRef !== nextProps.fakeRef;
40619
- }
40620
40584
  componentDidMount() {
40621
40585
  this.mouseMoveUpBound = false;
40622
40586
 
@@ -40635,6 +40599,16 @@ class Card extends React__namespace.Component {
40635
40599
  passive: false
40636
40600
  } : false);
40637
40601
  }
40602
+ shouldComponentUpdate(nextProps, nextState) {
40603
+ // Cards in the bank or drag list don't usually change -- they only
40604
+ // reorder themselves -- so we want to skip the update to things a
40605
+ // little faster. We also need to re-render if the content changes,
40606
+ // which happens only in the editor. (We do want to update the floating
40607
+ // card on mouse move to update its position.)
40608
+ return this.props.floating || nextProps.floating || this.props.content !== nextProps.content ||
40609
+ // TODO(alpert): Remove ref here after fixing facebook/react#1392.
40610
+ this.props.fakeRef !== nextProps.fakeRef;
40611
+ }
40638
40612
  componentDidUpdate(prevProps, prevState) {
40639
40613
  if (this.props.animating && !prevProps.animating) {
40640
40614
  // If we just were changed into animating, start the animation.
@@ -40698,6 +40672,45 @@ class Card extends React__namespace.Component {
40698
40672
  this.props.onMouseUp && this.props.onMouseUp(loc);
40699
40673
  }
40700
40674
  };
40675
+ render() {
40676
+ let style = {};
40677
+ if (this.props.floating) {
40678
+ style = {
40679
+ position: "absolute",
40680
+ left: this.props.startOffset.left,
40681
+ top: this.props.startOffset.top
40682
+ };
40683
+ }
40684
+ if (this.props.width) {
40685
+ style.width = this.props.width;
40686
+ }
40687
+ const className = ["card"];
40688
+ if (this.props.stack) {
40689
+ className.push("stack");
40690
+ }
40691
+ if (this.props.floating && !this.props.animating) {
40692
+ className.push("dragging");
40693
+ style.left += this.props.mouse.left - this.props.startMouse.left;
40694
+ style.top += this.props.mouse.top - this.props.startMouse.top;
40695
+ }
40696
+
40697
+ // Pull out the content to get rendered
40698
+ const rendererProps = ___default["default"].pick(this.props, "content");
40699
+ const onMouseDown = this.props.animating ? $__default["default"].noop : this.onMouseDown;
40700
+ return /*#__PURE__*/React__namespace.createElement("div", {
40701
+ className: "card-wrap " + ClassNames.INTERACTIVE,
40702
+ style: style,
40703
+ onMouseDown: onMouseDown,
40704
+ onTouchStart: onMouseDown,
40705
+ onTouchEnd: this.onMouseUp,
40706
+ onTouchCancel: this.onMouseUp
40707
+ }, /*#__PURE__*/React__namespace.createElement("div", {
40708
+ className: className.join(" ")
40709
+ }, /*#__PURE__*/React__namespace.createElement(Renderer, _extends({}, rendererProps, {
40710
+ linterContext: this.props.linterContext,
40711
+ strings: this.context.strings
40712
+ }))));
40713
+ }
40701
40714
  }
40702
40715
  const NORMAL = "normal",
40703
40716
  HORIZONTAL$1 = "horizontal";
@@ -40730,109 +40743,6 @@ class Orderer extends React__namespace.Component {
40730
40743
  });
40731
40744
  }
40732
40745
  }
40733
- render() {
40734
- // This is the card we are currently dragging
40735
- const dragging = this.state.dragging && /*#__PURE__*/React__namespace.createElement(Card
40736
- // eslint-disable-next-line react/no-string-refs
40737
- , {
40738
- ref: "dragging",
40739
- floating: true,
40740
- content: this.state.dragContent,
40741
- startOffset: this.state.offsetPos,
40742
- startMouse: this.state.grabPos,
40743
- mouse: this.state.mousePos,
40744
- width: this.state.dragWidth,
40745
- onMouseUp: this.onRelease,
40746
- onMouseMove: this.onMouseMove,
40747
- key: this.state.dragKey || "draggingCard",
40748
- linterContext: this.props.linterContext
40749
- });
40750
-
40751
- // This is the card that is currently animating
40752
- const animating = this.state.animating && /*#__PURE__*/React__namespace.createElement(Card, {
40753
- floating: true,
40754
- animating: true,
40755
- content: this.state.dragContent,
40756
- startOffset: this.state.offsetPos,
40757
- width: this.state.dragWidth,
40758
- animateTo: this.state.animateTo,
40759
- onAnimationEnd: this.state.onAnimationEnd,
40760
- key: this.state.dragKey || "draggingCard",
40761
- linterContext: this.props.linterContext
40762
- });
40763
-
40764
- // This is the list of draggable, rearrangable cards
40765
- const sortableCards = ___default["default"].map(this.state.current, function (opt, i) {
40766
- return /*#__PURE__*/React__namespace.createElement(Card, {
40767
- key: `sortableCard${i}`,
40768
- ref: "sortable" + i,
40769
- fakeRef: "sortable" + i,
40770
- floating: false,
40771
- content: opt.content,
40772
- width: opt.width
40773
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
40774
- ,
40775
- linterContext: this.props.linterContext
40776
- // eslint-disable-next-line react/jsx-no-bind
40777
- ,
40778
- onMouseDown:
40779
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
40780
- this.state.animating ? $__default["default"].noop :
40781
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
40782
- this.onClick.bind(null, "current", i)
40783
- });
40784
- }, this);
40785
- if (this.state.placeholderIndex != null) {
40786
- const placeholder = /*#__PURE__*/React__namespace.createElement(PlaceholderCard
40787
- // eslint-disable-next-line react/no-string-refs
40788
- , {
40789
- ref: "placeholder",
40790
- width: this.state.dragWidth,
40791
- height: this.state.dragHeight,
40792
- key: "placeholder"
40793
- });
40794
- sortableCards.splice(this.state.placeholderIndex, 0, placeholder);
40795
- }
40796
- const anySortableCards = sortableCards.length > 0;
40797
- // @ts-expect-error - TS2345 - Argument of type 'false | Element' is not assignable to parameter of type 'Element'.
40798
- sortableCards.push(dragging, animating);
40799
-
40800
- // If there are no cards in the list, then add a "hint" card
40801
- const sortable = /*#__PURE__*/React__namespace.createElement("div", {
40802
- className: "perseus-clearfix draggable-box"
40803
- }, !anySortableCards && /*#__PURE__*/React__namespace.createElement(DragHintCard, null), /*#__PURE__*/React__namespace.createElement("div", {
40804
- ref: "dragList"
40805
- }, sortableCards));
40806
-
40807
- // This is the bank of stacks of cards
40808
- const bank =
40809
- /*#__PURE__*/
40810
- // eslint-disable-next-line react/no-string-refs
40811
- React__namespace.createElement("div", {
40812
- ref: "bank",
40813
- className: "bank perseus-clearfix"
40814
- }, ___default["default"].map(this.props.options, (opt, i) => {
40815
- return /*#__PURE__*/React__namespace.createElement(Card, {
40816
- ref: "bank" + i,
40817
- floating: false,
40818
- content: opt.content,
40819
- stack: true,
40820
- key: i,
40821
- linterContext: this.props.linterContext
40822
- // eslint-disable-next-line react/jsx-no-bind
40823
- ,
40824
- onMouseDown: this.state.animating ? $__default["default"].noop : this.onClick.bind(null, "bank", i),
40825
- onMouseMove: this.onMouseMove,
40826
- onMouseUp: this.onRelease
40827
- });
40828
- }, this));
40829
- return /*#__PURE__*/React__namespace.createElement("div", {
40830
- className: "draggy-boxy-thing orderer " + "height-" + this.props.height + " " + "layout-" + this.props.layout + " " + "above-scratchpad blank-background " + "perseus-clearfix " + ClassNames.INTERACTIVE
40831
- // eslint-disable-next-line react/no-string-refs
40832
- ,
40833
- ref: "orderer"
40834
- }, bank, sortable);
40835
- }
40836
40746
  onClick = (type, index, loc, draggable) => {
40837
40747
  // @ts-expect-error - TS2769 - No overload matches this call.
40838
40748
  const $draggable = $__default["default"](ReactDOM__default["default"].findDOMNode(draggable));
@@ -41069,6 +40979,109 @@ class Orderer extends React__namespace.Component {
41069
40979
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Orderer'.
41070
40980
  return Orderer.validate(this.getUserInput(), rubric);
41071
40981
  };
40982
+ render() {
40983
+ // This is the card we are currently dragging
40984
+ const dragging = this.state.dragging && /*#__PURE__*/React__namespace.createElement(Card
40985
+ // eslint-disable-next-line react/no-string-refs
40986
+ , {
40987
+ ref: "dragging",
40988
+ floating: true,
40989
+ content: this.state.dragContent,
40990
+ startOffset: this.state.offsetPos,
40991
+ startMouse: this.state.grabPos,
40992
+ mouse: this.state.mousePos,
40993
+ width: this.state.dragWidth,
40994
+ onMouseUp: this.onRelease,
40995
+ onMouseMove: this.onMouseMove,
40996
+ key: this.state.dragKey || "draggingCard",
40997
+ linterContext: this.props.linterContext
40998
+ });
40999
+
41000
+ // This is the card that is currently animating
41001
+ const animating = this.state.animating && /*#__PURE__*/React__namespace.createElement(Card, {
41002
+ floating: true,
41003
+ animating: true,
41004
+ content: this.state.dragContent,
41005
+ startOffset: this.state.offsetPos,
41006
+ width: this.state.dragWidth,
41007
+ animateTo: this.state.animateTo,
41008
+ onAnimationEnd: this.state.onAnimationEnd,
41009
+ key: this.state.dragKey || "draggingCard",
41010
+ linterContext: this.props.linterContext
41011
+ });
41012
+
41013
+ // This is the list of draggable, rearrangable cards
41014
+ const sortableCards = ___default["default"].map(this.state.current, function (opt, i) {
41015
+ return /*#__PURE__*/React__namespace.createElement(Card, {
41016
+ key: `sortableCard${i}`,
41017
+ ref: "sortable" + i,
41018
+ fakeRef: "sortable" + i,
41019
+ floating: false,
41020
+ content: opt.content,
41021
+ width: opt.width
41022
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41023
+ ,
41024
+ linterContext: this.props.linterContext
41025
+ // eslint-disable-next-line react/jsx-no-bind
41026
+ ,
41027
+ onMouseDown:
41028
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41029
+ this.state.animating ? $__default["default"].noop :
41030
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41031
+ this.onClick.bind(null, "current", i)
41032
+ });
41033
+ }, this);
41034
+ if (this.state.placeholderIndex != null) {
41035
+ const placeholder = /*#__PURE__*/React__namespace.createElement(PlaceholderCard
41036
+ // eslint-disable-next-line react/no-string-refs
41037
+ , {
41038
+ ref: "placeholder",
41039
+ width: this.state.dragWidth,
41040
+ height: this.state.dragHeight,
41041
+ key: "placeholder"
41042
+ });
41043
+ sortableCards.splice(this.state.placeholderIndex, 0, placeholder);
41044
+ }
41045
+ const anySortableCards = sortableCards.length > 0;
41046
+ // @ts-expect-error - TS2345 - Argument of type 'false | Element' is not assignable to parameter of type 'Element'.
41047
+ sortableCards.push(dragging, animating);
41048
+
41049
+ // If there are no cards in the list, then add a "hint" card
41050
+ const sortable = /*#__PURE__*/React__namespace.createElement("div", {
41051
+ className: "perseus-clearfix draggable-box"
41052
+ }, !anySortableCards && /*#__PURE__*/React__namespace.createElement(DragHintCard, null), /*#__PURE__*/React__namespace.createElement("div", {
41053
+ ref: "dragList"
41054
+ }, sortableCards));
41055
+
41056
+ // This is the bank of stacks of cards
41057
+ const bank =
41058
+ /*#__PURE__*/
41059
+ // eslint-disable-next-line react/no-string-refs
41060
+ React__namespace.createElement("div", {
41061
+ ref: "bank",
41062
+ className: "bank perseus-clearfix"
41063
+ }, ___default["default"].map(this.props.options, (opt, i) => {
41064
+ return /*#__PURE__*/React__namespace.createElement(Card, {
41065
+ ref: "bank" + i,
41066
+ floating: false,
41067
+ content: opt.content,
41068
+ stack: true,
41069
+ key: i,
41070
+ linterContext: this.props.linterContext
41071
+ // eslint-disable-next-line react/jsx-no-bind
41072
+ ,
41073
+ onMouseDown: this.state.animating ? $__default["default"].noop : this.onClick.bind(null, "bank", i),
41074
+ onMouseMove: this.onMouseMove,
41075
+ onMouseUp: this.onRelease
41076
+ });
41077
+ }, this));
41078
+ return /*#__PURE__*/React__namespace.createElement("div", {
41079
+ className: "draggy-boxy-thing orderer " + "height-" + this.props.height + " " + "layout-" + this.props.layout + " " + "above-scratchpad blank-background " + "perseus-clearfix " + ClassNames.INTERACTIVE
41080
+ // eslint-disable-next-line react/no-string-refs
41081
+ ,
41082
+ ref: "orderer"
41083
+ }, bank, sortable);
41084
+ }
41072
41085
  }
41073
41086
  ___default["default"].extend(Orderer, {
41074
41087
  validate: function (state, rubric) {
@@ -41882,8 +41895,6 @@ class HighlightSetRenderer extends React__namespace.PureComponent {
41882
41895
  hoveredHighlightKey: null,
41883
41896
  hoveringTooltipFor: null
41884
41897
  };
41885
-
41886
- // eslint-disable-next-line react/sort-comp
41887
41898
  _highlightRenderers = {};
41888
41899
  componentDidMount() {
41889
41900
  this._updateEditListeners(false, this.props.editable);
@@ -42219,8 +42230,6 @@ class HighlightingUI extends React__namespace.PureComponent {
42219
42230
  */
42220
42231
  class WordIndexer extends React__namespace.PureComponent {
42221
42232
  _container;
42222
- /* eslint-enable react/sort-comp */
42223
-
42224
42233
  componentDidMount() {
42225
42234
  this._sendWordsUpdate();
42226
42235
  }
@@ -42482,7 +42491,6 @@ const styles$8 = aphrodite.StyleSheet.create({
42482
42491
  }
42483
42492
  });
42484
42493
 
42485
- /* eslint-disable react/sort-comp */
42486
42494
  function getInitialParseState() {
42487
42495
  return {
42488
42496
  currentRef: [],
@@ -42497,16 +42505,15 @@ function getInitialParseState() {
42497
42505
  };
42498
42506
  }
42499
42507
  class RefStart extends React__namespace.Component {
42508
+ // @ts-expect-error - TS2322 - Type '() => React.ReactNode' is not assignable to type '() => ReactElement<any, string | JSXElementConstructor<any>>'.
42509
+ getRefContent = () => {
42510
+ return this.props.refContent;
42511
+ };
42500
42512
  render() {
42501
42513
  return /*#__PURE__*/React__namespace.createElement("span", {
42502
42514
  style: REF_STYLE
42503
42515
  }, "_");
42504
42516
  }
42505
-
42506
- // @ts-expect-error - TS2322 - Type '() => React.ReactNode' is not assignable to type '() => ReactElement<any, string | JSXElementConstructor<any>>'.
42507
- getRefContent = () => {
42508
- return this.props.refContent;
42509
- };
42510
42517
  }
42511
42518
  class RefEnd extends React__namespace.Component {
42512
42519
  render() {
@@ -42905,6 +42912,14 @@ class Passage extends React__namespace.Component {
42905
42912
  startLineNumbersAfter: 0,
42906
42913
  stylesAreApplied: false
42907
42914
  };
42915
+ static validate(state, rubric) {
42916
+ return {
42917
+ type: "points",
42918
+ earned: 0,
42919
+ total: 0,
42920
+ message: null
42921
+ };
42922
+ }
42908
42923
  componentDidMount() {
42909
42924
  this._updateState();
42910
42925
 
@@ -43126,17 +43141,6 @@ class Passage extends React__namespace.Component {
43126
43141
  return Passage.validate(this.getUserInput(), rubric);
43127
43142
  }
43128
43143
 
43129
- /* eslint-disable react/sort-comp */
43130
- static validate(state, rubric) {
43131
- return {
43132
- type: "points",
43133
- earned: 0,
43134
- total: 0,
43135
- message: null
43136
- };
43137
- }
43138
- /* eslint-enable react/sort-comp */
43139
-
43140
43144
  /**
43141
43145
  * Rendering
43142
43146
  *
@@ -43375,20 +43379,6 @@ class PhetSimulation extends React__namespace.Component {
43375
43379
  getUserInput() {
43376
43380
  return null;
43377
43381
  }
43378
- change = (() => {
43379
- var _this = this;
43380
- return function () {
43381
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
43382
- args[_key] = arguments[_key];
43383
- }
43384
- // @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
43385
- return change.apply(_this, args);
43386
- };
43387
- })();
43388
- simpleValidate = rubric => {
43389
- // @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof PhetSimulation'.
43390
- return PhetSimulation.validate(this.getUserInput(), rubric);
43391
- };
43392
43382
 
43393
43383
  // kaLocales and PhET locales use different formats and abbreviations.
43394
43384
  // PhET accepts different formats, i.e. kaLocale's hyphens, but it does not accept
@@ -43480,7 +43470,7 @@ class PhetSimulation extends React__namespace.Component {
43480
43470
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
43481
43471
  const sandboxProperties = "allow-same-origin allow-scripts";
43482
43472
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
43483
- style: styles$6.container
43473
+ style: styles$6.widgetContainer
43484
43474
  }, this.state.banner !== null &&
43485
43475
  /*#__PURE__*/
43486
43476
  // TODO(anna): Make this banner focusable
@@ -43492,19 +43482,16 @@ class PhetSimulation extends React__namespace.Component {
43492
43482
  layout: "floating",
43493
43483
  kind: this.state.banner.kind,
43494
43484
  text: this.state.banner.message
43495
- })), /*#__PURE__*/React__namespace.createElement("iframe", {
43485
+ })), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
43486
+ style: styles$6.iframeContainer
43487
+ }, /*#__PURE__*/React__namespace.createElement("iframe", {
43496
43488
  ref: this.iframeRef,
43497
43489
  title: this.props.description,
43498
43490
  sandbox: sandboxProperties,
43499
- style: {
43500
- minWidth: 400,
43501
- height: 360,
43502
- width: "100%",
43503
- borderWidth: 0
43504
- },
43491
+ className: aphrodite.css(styles$6.iframeResponsive),
43505
43492
  src: this.state.url?.toString(),
43506
43493
  allow: "fullscreen"
43507
- }), /*#__PURE__*/React__namespace.createElement(IconButton__default["default"], {
43494
+ })), /*#__PURE__*/React__namespace.createElement(IconButton__default["default"], {
43508
43495
  icon: cornersOutIcon__default["default"],
43509
43496
  onClick: () => {
43510
43497
  this.iframeRef.current?.requestFullscreen();
@@ -43534,21 +43521,35 @@ const makeSafeUrl = (urlString, locale) => {
43534
43521
  return url;
43535
43522
  };
43536
43523
  const styles$6 = aphrodite.StyleSheet.create({
43537
- container: {
43524
+ widgetContainer: {
43538
43525
  borderRadius: borderRadiusLarge,
43539
43526
  borderWidth: 1,
43540
43527
  borderColor: basicBorderColor,
43541
- padding: phoneMargin,
43542
- paddingBottom: 0,
43543
- width: 650
43528
+ padding: wonderBlocksTokens.spacing.medium_16,
43529
+ paddingBottom: 0
43530
+ },
43531
+ iframeContainer: {
43532
+ position: "relative",
43533
+ overflow: "hidden",
43534
+ width: "100%",
43535
+ // 16:9 aspect ratio
43536
+ paddingTop: "56.25%"
43537
+ },
43538
+ iframeResponsive: {
43539
+ borderWidth: 0,
43540
+ position: "absolute",
43541
+ top: 0,
43542
+ left: 0,
43543
+ bottom: 0,
43544
+ right: 0,
43545
+ width: "100%",
43546
+ height: "100%"
43544
43547
  }
43545
43548
  });
43546
43549
  var PhetSimulation$1 = {
43547
43550
  name: "phet-simulation",
43548
43551
  displayName: "PhET Simulation",
43549
43552
  widget: PhetSimulation,
43550
- // Hides widget from content creators until full release
43551
- hidden: true,
43552
43553
  isLintable: true
43553
43554
  };
43554
43555
 
@@ -44605,65 +44606,6 @@ class Table extends React__namespace.Component {
44605
44606
  _getColumns = () => {
44606
44607
  return this.props.answers[0].length;
44607
44608
  };
44608
- render() {
44609
- const rows = this._getRows();
44610
- const columns = this._getColumns();
44611
- const headers = this.props.headers;
44612
- let InputComponent;
44613
- let inputStyle;
44614
- const extraInputProps = {};
44615
- if (this.props.apiOptions.customKeypad) {
44616
- InputComponent = SimpleKeypadInput;
44617
- // NOTE(charlie): This is intended to match the "width: 80px" in
44618
- // input in table.less. Those values should be kept in-sync.
44619
- inputStyle = {
44620
- width: 80
44621
- };
44622
- extraInputProps.keypadElement = this.props.keypadElement;
44623
- } else {
44624
- InputComponent = "input";
44625
- inputStyle = {};
44626
- }
44627
- return /*#__PURE__*/React__namespace.createElement("table", {
44628
- className: "perseus-widget-table-of-values non-markdown"
44629
- }, /*#__PURE__*/React__namespace.createElement("thead", null, /*#__PURE__*/React__namespace.createElement("tr", null, ___default["default"].map(headers, (header, i) => {
44630
- if (this.props.editableHeaders) {
44631
- return /*#__PURE__*/React__namespace.createElement("th", {
44632
- key: i
44633
- }, /*#__PURE__*/React__namespace.createElement(this.props.Editor, {
44634
- ref: "columnHeader" + i,
44635
- apiOptions: this.props.apiOptions,
44636
- content: header,
44637
- widgetEnabled: false,
44638
- onChange: ___default["default"].partial(this.onHeaderChange, i)
44639
- }));
44640
- }
44641
- return /*#__PURE__*/React__namespace.createElement("th", {
44642
- key: i
44643
- }, /*#__PURE__*/React__namespace.createElement(Renderer, {
44644
- content: header,
44645
- linterContext: this.props.linterContext,
44646
- strings: this.context.strings
44647
- }));
44648
- }))), /*#__PURE__*/React__namespace.createElement("tbody", null, ___default["default"](rows).times(r => {
44649
- return /*#__PURE__*/React__namespace.createElement("tr", {
44650
- key: r
44651
- }, ___default["default"](columns).times(c => {
44652
- return /*#__PURE__*/React__namespace.createElement("td", {
44653
- key: c
44654
- }, /*#__PURE__*/React__namespace.createElement(InputComponent, _extends({
44655
- ref: getRefForPath(getInputPath(r, c)),
44656
- type: "text",
44657
- value: this.props.answers[r][c],
44658
- disabled: this.props.apiOptions.readOnly,
44659
- onFocus: ___default["default"].partial(this._handleFocus, getInputPath(r, c)),
44660
- onBlur: ___default["default"].partial(this._handleBlur, getInputPath(r, c)),
44661
- onChange: ___default["default"].partial(this.onValueChange, r, c),
44662
- style: inputStyle
44663
- }, extraInputProps)));
44664
- }));
44665
- })));
44666
- }
44667
44609
  getUserInput = () => {
44668
44610
  return ___default["default"].map(this.props.answers, ___default["default"].clone);
44669
44611
  };
@@ -44755,6 +44697,65 @@ class Table extends React__namespace.Component {
44755
44697
  answers: answers
44756
44698
  }, cb);
44757
44699
  };
44700
+ render() {
44701
+ const rows = this._getRows();
44702
+ const columns = this._getColumns();
44703
+ const headers = this.props.headers;
44704
+ let InputComponent;
44705
+ let inputStyle;
44706
+ const extraInputProps = {};
44707
+ if (this.props.apiOptions.customKeypad) {
44708
+ InputComponent = SimpleKeypadInput;
44709
+ // NOTE(charlie): This is intended to match the "width: 80px" in
44710
+ // input in table.less. Those values should be kept in-sync.
44711
+ inputStyle = {
44712
+ width: 80
44713
+ };
44714
+ extraInputProps.keypadElement = this.props.keypadElement;
44715
+ } else {
44716
+ InputComponent = "input";
44717
+ inputStyle = {};
44718
+ }
44719
+ return /*#__PURE__*/React__namespace.createElement("table", {
44720
+ className: "perseus-widget-table-of-values non-markdown"
44721
+ }, /*#__PURE__*/React__namespace.createElement("thead", null, /*#__PURE__*/React__namespace.createElement("tr", null, ___default["default"].map(headers, (header, i) => {
44722
+ if (this.props.editableHeaders) {
44723
+ return /*#__PURE__*/React__namespace.createElement("th", {
44724
+ key: i
44725
+ }, /*#__PURE__*/React__namespace.createElement(this.props.Editor, {
44726
+ ref: "columnHeader" + i,
44727
+ apiOptions: this.props.apiOptions,
44728
+ content: header,
44729
+ widgetEnabled: false,
44730
+ onChange: ___default["default"].partial(this.onHeaderChange, i)
44731
+ }));
44732
+ }
44733
+ return /*#__PURE__*/React__namespace.createElement("th", {
44734
+ key: i
44735
+ }, /*#__PURE__*/React__namespace.createElement(Renderer, {
44736
+ content: header,
44737
+ linterContext: this.props.linterContext,
44738
+ strings: this.context.strings
44739
+ }));
44740
+ }))), /*#__PURE__*/React__namespace.createElement("tbody", null, ___default["default"](rows).times(r => {
44741
+ return /*#__PURE__*/React__namespace.createElement("tr", {
44742
+ key: r
44743
+ }, ___default["default"](columns).times(c => {
44744
+ return /*#__PURE__*/React__namespace.createElement("td", {
44745
+ key: c
44746
+ }, /*#__PURE__*/React__namespace.createElement(InputComponent, _extends({
44747
+ ref: getRefForPath(getInputPath(r, c)),
44748
+ type: "text",
44749
+ value: this.props.answers[r][c],
44750
+ disabled: this.props.apiOptions.readOnly,
44751
+ onFocus: ___default["default"].partial(this._handleFocus, getInputPath(r, c)),
44752
+ onBlur: ___default["default"].partial(this._handleBlur, getInputPath(r, c)),
44753
+ onChange: ___default["default"].partial(this.onValueChange, r, c),
44754
+ style: inputStyle
44755
+ }, extraInputProps)));
44756
+ }));
44757
+ })));
44758
+ }
44758
44759
  }
44759
44760
  ___default["default"].extend(Table, {
44760
44761
  validate: function (state, rubric, strings) {
@@ -45053,7 +45054,7 @@ const init = function (options) {
45053
45054
 
45054
45055
  // This file is processed by a Rollup plugin (replace) to inject the production
45055
45056
  const libName = "@khanacademy/perseus";
45056
- const libVersion = "31.1.0";
45057
+ const libVersion = "32.0.1";
45057
45058
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45058
45059
 
45059
45060
  const apiVersion = {
@@ -46865,8 +46866,6 @@ class MultiRenderer extends React__namespace.Component {
46865
46866
  // Keep state in sync with props.
46866
46867
  this.state = this._tryMakeRendererState(this.props);
46867
46868
  }
46868
- /* eslint-enable react/sort-comp */
46869
-
46870
46869
  UNSAFE_componentWillReceiveProps(nextProps) {
46871
46870
  // Keep state in sync with props.
46872
46871
  if (nextProps.item !== this.props.item) {
@@ -48748,6 +48747,7 @@ exports.itemVersion = ItemVersion;
48748
48747
  exports.libVersion = libVersion;
48749
48748
  exports.lockedFigureColors = lockedFigureColors;
48750
48749
  exports.lockedFigureFillStyles = lockedFigureFillStyles;
48750
+ exports.makeSafeUrl = makeSafeUrl;
48751
48751
  exports.mediaQueries = mediaQueries;
48752
48752
  exports.parsePerseusItem = parsePerseusItem;
48753
48753
  exports.plotterPlotTypes = plotterPlotTypes;