@khanacademy/perseus 6.2.1 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/all-widgets.d.ts +2 -2
  3. package/dist/all-widgets.js.flow +2 -2
  4. package/dist/basic-widgets.d.ts +2 -2
  5. package/dist/basic-widgets.js.flow +2 -2
  6. package/dist/components/math-input.d.ts +4 -1
  7. package/dist/components/math-input.js.flow +4 -1
  8. package/dist/components/tex-buttons.d.ts +5 -1
  9. package/dist/components/tex-buttons.js.flow +1 -1
  10. package/dist/es/index.js +172 -205
  11. package/dist/es/index.js.map +1 -1
  12. package/dist/extra-widgets.d.ts +2 -2
  13. package/dist/extra-widgets.js.flow +2 -2
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +170 -201
  16. package/dist/index.js.flow +2 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/strings.js +23 -0
  19. package/dist/types.d.ts +2 -2
  20. package/dist/types.js.flow +2 -2
  21. package/dist/util/geometry.d.ts +25 -0
  22. package/dist/util/geometry.js.flow +37 -0
  23. package/dist/widgets/categorizer.d.ts +2 -2
  24. package/dist/widgets/categorizer.js.flow +2 -2
  25. package/dist/widgets/cs-program.d.ts +2 -2
  26. package/dist/widgets/cs-program.js.flow +2 -2
  27. package/dist/widgets/definition.d.ts +2 -2
  28. package/dist/widgets/definition.js.flow +2 -2
  29. package/dist/widgets/dropdown.d.ts +2 -2
  30. package/dist/widgets/dropdown.js.flow +2 -2
  31. package/dist/widgets/explanation.d.ts +2 -2
  32. package/dist/widgets/explanation.js.flow +2 -2
  33. package/dist/widgets/expression.d.ts +2 -2
  34. package/dist/widgets/expression.js.flow +2 -2
  35. package/dist/widgets/graded-group-set.d.ts +2 -2
  36. package/dist/widgets/graded-group-set.js.flow +2 -2
  37. package/dist/widgets/graded-group.d.ts +2 -2
  38. package/dist/widgets/graded-group.js.flow +2 -2
  39. package/dist/widgets/grapher.d.ts +2 -2
  40. package/dist/widgets/grapher.js.flow +2 -2
  41. package/dist/widgets/group.d.ts +2 -2
  42. package/dist/widgets/group.js.flow +2 -2
  43. package/dist/widgets/iframe.d.ts +2 -2
  44. package/dist/widgets/iframe.js.flow +2 -2
  45. package/dist/widgets/image.d.ts +2 -2
  46. package/dist/widgets/image.js.flow +2 -2
  47. package/dist/widgets/input-number.d.ts +2 -2
  48. package/dist/widgets/input-number.js.flow +2 -2
  49. package/dist/widgets/interaction.d.ts +2 -2
  50. package/dist/widgets/interaction.js.flow +2 -2
  51. package/dist/widgets/interactive-graph.d.ts +4 -12
  52. package/dist/widgets/interactive-graph.js.flow +4 -7
  53. package/dist/widgets/label-image.d.ts +2 -2
  54. package/dist/widgets/label-image.js.flow +2 -2
  55. package/dist/widgets/lights-puzzle.d.ts +2 -2
  56. package/dist/widgets/lights-puzzle.js.flow +2 -2
  57. package/dist/widgets/matcher.d.ts +2 -2
  58. package/dist/widgets/matcher.js.flow +2 -2
  59. package/dist/widgets/matrix.d.ts +2 -2
  60. package/dist/widgets/matrix.js.flow +2 -2
  61. package/dist/widgets/measurer.d.ts +2 -2
  62. package/dist/widgets/measurer.js.flow +2 -2
  63. package/dist/widgets/molecule.d.ts +2 -2
  64. package/dist/widgets/molecule.js.flow +2 -2
  65. package/dist/widgets/number-line.d.ts +2 -2
  66. package/dist/widgets/number-line.js.flow +2 -2
  67. package/dist/widgets/numeric-input.d.ts +2 -2
  68. package/dist/widgets/numeric-input.js.flow +2 -2
  69. package/dist/widgets/orderer.d.ts +2 -2
  70. package/dist/widgets/orderer.js.flow +2 -2
  71. package/dist/widgets/passage-ref-target.d.ts +2 -2
  72. package/dist/widgets/passage-ref-target.js.flow +2 -2
  73. package/dist/widgets/passage-ref.d.ts +2 -2
  74. package/dist/widgets/passage-ref.js.flow +2 -2
  75. package/dist/widgets/passage.d.ts +2 -2
  76. package/dist/widgets/passage.js.flow +2 -2
  77. package/dist/widgets/plotter.d.ts +2 -2
  78. package/dist/widgets/plotter.js.flow +2 -2
  79. package/dist/widgets/radio.d.ts +2 -2
  80. package/dist/widgets/radio.js.flow +2 -2
  81. package/dist/widgets/reaction-diagram.d.ts +2 -2
  82. package/dist/widgets/reaction-diagram.js.flow +2 -2
  83. package/dist/widgets/sequence.d.ts +2 -2
  84. package/dist/widgets/sequence.js.flow +2 -2
  85. package/dist/widgets/simulator.d.ts +2 -2
  86. package/dist/widgets/simulator.js.flow +2 -2
  87. package/dist/widgets/sorter.d.ts +2 -2
  88. package/dist/widgets/sorter.js.flow +2 -2
  89. package/dist/widgets/table.d.ts +2 -2
  90. package/dist/widgets/table.js.flow +2 -2
  91. package/dist/widgets/transformer.d.ts +2 -2
  92. package/dist/widgets/transformer.js.flow +2 -2
  93. package/dist/widgets/unit.d.ts +2 -2
  94. package/dist/widgets/unit.js.flow +2 -2
  95. package/dist/widgets/video.d.ts +2 -2
  96. package/dist/widgets/video.js.flow +2 -2
  97. package/package.json +4 -5
  98. package/src/components/__tests__/math-input.test.tsx +60 -0
  99. package/src/components/math-input.tsx +88 -118
  100. package/src/components/prop-check-box.tsx +0 -1
  101. package/src/components/tex-buttons.tsx +73 -12
  102. package/src/index.ts +2 -0
  103. package/src/types.ts +19 -2
  104. package/src/util/geometry.ts +293 -0
  105. package/src/widgets/__tests__/numeric-input.test.ts +38 -0
  106. package/src/widgets/expression.tsx +0 -1
  107. package/src/widgets/grapher.tsx +0 -1
  108. package/src/widgets/image.tsx +0 -1
  109. package/src/widgets/input-number.tsx +0 -1
  110. package/src/widgets/interactive-graph.tsx +20 -288
  111. package/src/widgets/matrix.tsx +0 -1
  112. package/src/widgets/numeric-input.tsx +6 -2
  113. package/src/widgets/transformer.tsx +0 -1
  114. package/tsconfig-build.json +0 -1
  115. package/tsconfig-build.tsbuildinfo +1 -1
  116. package/dist/__tests__/mock-asset-loading-widget.d.ts +0 -59
  117. package/dist/__tests__/mock-asset-loading-widget.js.flow +0 -70
  118. package/dist/__tests__/mock-widget.d.ts +0 -30
  119. package/dist/__tests__/mock-widget.js.flow +0 -39
  120. package/dist/__tests__/test-items/image-item.d.ts +0 -14
  121. package/dist/__tests__/test-items/image-item.js.flow +0 -20
  122. package/dist/__tests__/test-items/input-number-1-item.d.ts +0 -9
  123. package/dist/__tests__/test-items/input-number-1-item.js.flow +0 -15
  124. package/dist/__tests__/test-items/input-number-2-item.d.ts +0 -9
  125. package/dist/__tests__/test-items/input-number-2-item.js.flow +0 -15
  126. package/dist/__tests__/test-items/table-item.d.ts +0 -9
  127. package/dist/__tests__/test-items/table-item.js.flow +0 -15
  128. package/dist/interactive2/__tests__/movable-mock.d.ts +0 -5
  129. package/dist/interactive2/__tests__/movable-mock.js.flow +0 -11
  130. package/dist/widgets/__stories__/test-keypad-context-wrapper.d.ts +0 -6
  131. package/dist/widgets/__stories__/test-keypad-context-wrapper.js.flow +0 -12
  132. package/dist/widgets/__tests__/renderQuestion.d.ts +0 -13
  133. package/dist/widgets/__tests__/renderQuestion.js.flow +0 -28
package/dist/es/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as KAS from '@khanacademy/kas';
2
- import { keyTranslator, KeypadInput, KeypadType, keypadElementPropType, LegacyKeypad } from '@khanacademy/math-input';
2
+ import { keyTranslator, createMathField, mathQuillInstance, KeypadInput, KeypadType, keypadElementPropType, LegacyKeypad } from '@khanacademy/math-input';
3
3
  import * as PerseusLinter from '@khanacademy/perseus-linter';
4
4
  import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
5
5
  import * as i18n from '@khanacademy/wonder-blocks-i18n';
@@ -8,7 +8,6 @@ import * as React from 'react';
8
8
  import { useState, useEffect, useRef } from 'react';
9
9
  import _, { debounce } from 'underscore';
10
10
  import $ from 'jquery';
11
- import MathQuill from 'mathquill';
12
11
  import * as ReactDOM from 'react-dom';
13
12
  import ReactDOM__default from 'react-dom';
14
13
  import PropTypes from 'prop-types';
@@ -138,10 +137,10 @@ const symbStyle = {
138
137
  const basic = [() => [/*#__PURE__*/React.createElement("span", {
139
138
  key: "plus",
140
139
  style: slightlyBig
141
- }, "+"), "PLUS"], () => [/*#__PURE__*/React.createElement("span", {
140
+ }, "+"), "PLUS", i18n._("Plus")], () => [/*#__PURE__*/React.createElement("span", {
142
141
  key: "minus",
143
142
  style: prettyBig
144
- }, "-"), "MINUS"],
143
+ }, "-"), "MINUS", i18n._("Minus")],
145
144
  // TODO(joel) - display as \cdot when appropriate
146
145
  props => {
147
146
  const {
@@ -151,12 +150,12 @@ props => {
151
150
  return [/*#__PURE__*/React.createElement(TeX, {
152
151
  key: "times",
153
152
  style: prettyBig
154
- }, i18n.doNotTranslate("\\times")), "TIMES"];
153
+ }, i18n.doNotTranslate("\\times")), "TIMES", i18n._("Multiply")];
155
154
  }
156
155
  return [/*#__PURE__*/React.createElement(TeX, {
157
156
  key: "times",
158
157
  style: prettyBig
159
- }, i18n.doNotTranslate("\\cdot")), "CDOT"];
158
+ }, i18n.doNotTranslate("\\cdot")), "CDOT", i18n._("Multiply")];
160
159
  }, () => {
161
160
  const {
162
161
  TeX
@@ -164,7 +163,7 @@ props => {
164
163
  return [/*#__PURE__*/React.createElement(TeX, {
165
164
  key: "frac",
166
165
  style: prettyBig
167
- }, i18n.doNotTranslate("\\frac{x}{y}")), "FRAC"];
166
+ }, i18n.doNotTranslate("\\frac{x}{y}")), "FRAC", i18n._("Fraction, excluding the current expression")];
168
167
  }];
169
168
  const buttonSets = {
170
169
  basic,
@@ -174,7 +173,7 @@ const buttonSets = {
174
173
  } = getDependencies();
175
174
  return [/*#__PURE__*/React.createElement(TeX, {
176
175
  key: "div"
177
- }, i18n.doNotTranslate("\\div")), "DIVIDE"];
176
+ }, i18n.doNotTranslate("\\div")), "DIVIDE", i18n._("Divide")];
178
177
  }]),
179
178
  trig: [() => {
180
179
  const {
@@ -182,21 +181,21 @@ const buttonSets = {
182
181
  } = getDependencies();
183
182
  return [/*#__PURE__*/React.createElement(TeX, {
184
183
  key: "sin"
185
- }, i18n.doNotTranslate("\\sin")), "SIN"];
184
+ }, i18n.doNotTranslate("\\sin")), "SIN", i18n._("Sine")];
186
185
  }, () => {
187
186
  const {
188
187
  TeX
189
188
  } = getDependencies();
190
189
  return [/*#__PURE__*/React.createElement(TeX, {
191
190
  key: "cos"
192
- }, i18n.doNotTranslate("\\cos")), "COS"];
191
+ }, i18n.doNotTranslate("\\cos")), "COS", i18n._("Cosine")];
193
192
  }, () => {
194
193
  const {
195
194
  TeX
196
195
  } = getDependencies();
197
196
  return [/*#__PURE__*/React.createElement(TeX, {
198
197
  key: "tan"
199
- }, i18n.doNotTranslate("\\tan")), "TAN"];
198
+ }, i18n.doNotTranslate("\\tan")), "TAN", i18n._("Tangent")];
200
199
  }, () => {
201
200
  const {
202
201
  TeX
@@ -204,7 +203,7 @@ const buttonSets = {
204
203
  return [/*#__PURE__*/React.createElement(TeX, {
205
204
  key: "theta",
206
205
  style: symbStyle
207
- }, i18n.doNotTranslate("\\theta")), "THETA"];
206
+ }, i18n.doNotTranslate("\\theta")), "THETA", i18n._("Theta")];
208
207
  }, () => {
209
208
  const {
210
209
  TeX
@@ -216,7 +215,7 @@ const buttonSets = {
216
215
  }, i18n.doNotTranslate("phi")), /*#__PURE__*/React.createElement(TeX, {
217
216
  key: "pi",
218
217
  style: symbStyle
219
- }, i18n.doNotTranslate("\\phi"))), "PHI"];
218
+ }, i18n.doNotTranslate("\\phi"))), "PHI", i18n._("Phi")];
220
219
  }],
221
220
  prealgebra: [() => {
222
221
  const {
@@ -224,7 +223,7 @@ const buttonSets = {
224
223
  } = getDependencies();
225
224
  return [/*#__PURE__*/React.createElement(TeX, {
226
225
  key: "sqrt"
227
- }, i18n.doNotTranslate("\\sqrt{x}")), "SQRT"];
226
+ }, i18n.doNotTranslate("\\sqrt{x}")), "SQRT", i18n._("Square root")];
228
227
  },
229
228
  // TODO(joel) - how does desmos do this?
230
229
  () => {
@@ -233,7 +232,7 @@ const buttonSets = {
233
232
  } = getDependencies();
234
233
  return [/*#__PURE__*/React.createElement(TeX, {
235
234
  key: "nthroot"
236
- }, i18n.doNotTranslate("\\sqrt[3]{x}")), "NTHROOT3"];
235
+ }, i18n.doNotTranslate("\\sqrt[3]{x}")), "NTHROOT3", i18n._("Cube root")];
237
236
  }, () => {
238
237
  const {
239
238
  TeX
@@ -241,7 +240,7 @@ const buttonSets = {
241
240
  return [/*#__PURE__*/React.createElement(TeX, {
242
241
  key: "pow",
243
242
  style: slightlyBig
244
- }, i18n.doNotTranslate("a^b")), "POW"];
243
+ }, i18n.doNotTranslate("a^b")), "POW", i18n._("Exponent")];
245
244
  }, () => {
246
245
  const {
247
246
  TeX
@@ -249,7 +248,7 @@ const buttonSets = {
249
248
  return [/*#__PURE__*/React.createElement(TeX, {
250
249
  key: "pi",
251
250
  style: slightlyBig
252
- }, i18n.doNotTranslate("\\pi")), "PI"];
251
+ }, i18n.doNotTranslate("\\pi")), "PI", i18n._("Pi")];
253
252
  }],
254
253
  logarithms: [() => {
255
254
  const {
@@ -257,21 +256,21 @@ const buttonSets = {
257
256
  } = getDependencies();
258
257
  return [/*#__PURE__*/React.createElement(TeX, {
259
258
  key: "log"
260
- }, i18n.doNotTranslate("\\log")), "LOG"];
259
+ }, i18n.doNotTranslate("\\log")), "LOG", i18n._("Logarithm with base 10")];
261
260
  }, () => {
262
261
  const {
263
262
  TeX
264
263
  } = getDependencies();
265
264
  return [/*#__PURE__*/React.createElement(TeX, {
266
265
  key: "ln"
267
- }, i18n.doNotTranslate("\\ln")), "LN"];
266
+ }, i18n.doNotTranslate("\\ln")), "LN", i18n._("Natural logarithm")];
268
267
  }, () => {
269
268
  const {
270
269
  TeX
271
270
  } = getDependencies();
272
271
  return [/*#__PURE__*/React.createElement(TeX, {
273
272
  key: "log_b"
274
- }, i18n.doNotTranslate("\\log_b")), "LOG_B"];
273
+ }, i18n.doNotTranslate("\\log_b")), "LOG_B", i18n._("Logarithm with custom base")];
275
274
  }],
276
275
  "basic relations": [() => {
277
276
  const {
@@ -279,21 +278,21 @@ const buttonSets = {
279
278
  } = getDependencies();
280
279
  return [/*#__PURE__*/React.createElement(TeX, {
281
280
  key: "eq"
282
- }, "="), "EQUAL"];
281
+ }, "="), "EQUAL", i18n._("Equals sign")];
283
282
  }, () => {
284
283
  const {
285
284
  TeX
286
285
  } = getDependencies();
287
286
  return [/*#__PURE__*/React.createElement(TeX, {
288
287
  key: "lt"
289
- }, i18n.doNotTranslate("\\lt")), "LT"];
288
+ }, i18n.doNotTranslate("\\lt")), "LT", i18n._("Less than sign")];
290
289
  }, () => {
291
290
  const {
292
291
  TeX
293
292
  } = getDependencies();
294
293
  return [/*#__PURE__*/React.createElement(TeX, {
295
294
  key: "gt"
296
- }, i18n.doNotTranslate("\\gt")), "GT"];
295
+ }, i18n.doNotTranslate("\\gt")), "GT", i18n._("Greater than sign")];
297
296
  }],
298
297
  "advanced relations": [() => {
299
298
  const {
@@ -301,21 +300,21 @@ const buttonSets = {
301
300
  } = getDependencies();
302
301
  return [/*#__PURE__*/React.createElement(TeX, {
303
302
  key: "neq"
304
- }, i18n.doNotTranslate("\\neq")), "NEQ"];
303
+ }, i18n.doNotTranslate("\\neq")), "NEQ", i18n._("Not-equals sign")];
305
304
  }, () => {
306
305
  const {
307
306
  TeX
308
307
  } = getDependencies();
309
308
  return [/*#__PURE__*/React.createElement(TeX, {
310
309
  key: "leq"
311
- }, i18n.doNotTranslate("\\leq")), "LEQ"];
310
+ }, i18n.doNotTranslate("\\leq")), "LEQ", i18n._("Less than or equal to sign")];
312
311
  }, () => {
313
312
  const {
314
313
  TeX
315
314
  } = getDependencies();
316
315
  return [/*#__PURE__*/React.createElement(TeX, {
317
316
  key: "geq"
318
- }, i18n.doNotTranslate("\\geq")), "GEQ"];
317
+ }, i18n.doNotTranslate("\\geq")), "GEQ", i18n._("Greater than or equal to sign")];
319
318
  }]
320
319
  };
321
320
  const buttonSetsPropType = PropTypes.arrayOf(PropTypes.oneOf(_(buttonSets).keys()));
@@ -337,7 +336,8 @@ class TexButtons extends React.Component {
337
336
  // @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'. | TS2339 - Property 'key' does not exist on type 'boolean | ReactChild | ReactFragment | ReactPortal'.
338
337
  ,
339
338
  key: symbol[0].key,
340
- type: "button"
339
+ type: "button",
340
+ "aria-label": symbol[2]
341
341
  }, symbol[0]);
342
342
  }));
343
343
  const buttonPopup = _(buttonRows).map((row, i) => {
@@ -372,6 +372,8 @@ class MathInput extends React.Component {
372
372
  constructor(...args) {
373
373
  super(...args);
374
374
  this.mouseDown = void 0;
375
+ this.__mathFieldWrapperRef = null;
376
+ this.__mathField = null;
375
377
  this.state = {
376
378
  focused: false
377
379
  };
@@ -426,12 +428,11 @@ class MathInput extends React.Component {
426
428
  return this.state.focused;
427
429
  };
428
430
  this.insert = value => {
429
- // @ts-expect-error [FEI-5003] - TS2554 - Expected 1 arguments, but got 0.
430
431
  const input = this.mathField();
431
432
  const inputModifier = customKeyTranslator[value];
432
433
  if (inputModifier) {
433
434
  inputModifier(input, value);
434
- input.focus();
435
+ input == null ? void 0 : input.focus();
435
436
  return;
436
437
  }
437
438
 
@@ -442,131 +443,98 @@ class MathInput extends React.Component {
442
443
  if (_(value).isFunction()) {
443
444
  value(input);
444
445
  } else if (value[0] === "\\") {
445
- input.cmd(value).focus();
446
+ input == null ? void 0 : input.cmd(value).focus();
446
447
  } else {
447
- input.write(value).focus();
448
+ input == null ? void 0 : input.write(value).focus();
449
+ }
450
+ input == null ? void 0 : input.focus();
451
+ };
452
+ this.mathField = () => {
453
+ if (!this.__mathField && this.__mathFieldWrapperRef) {
454
+ // Initialize MathQuill.MathField instance
455
+ this.__mathField = createMathField(this.__mathFieldWrapperRef, baseConfig => _extends({}, baseConfig, {
456
+ handlers: {
457
+ edit: mathField => {
458
+ // This handler is guaranteed to be called on change, but
459
+ // unlike React it sometimes generates false positives.
460
+ // One of these is on initialization (with an empty string
461
+ // value), so we have to guard against that below.
462
+ let value = mathField.latex();
463
+
464
+ // Provide a MathQuill-compatible way to generate the
465
+ // not-equals sign without pasting unicode or typing TeX
466
+ value = value.replace(/<>/g, "\\ne");
467
+
468
+ // Use the specified symbol to represent multiplication
469
+ // TODO(alex): Add an option to disallow variables, in
470
+ // which case 'x' should get converted to '\\times'
471
+ if (this.props.convertDotToTimes) {
472
+ value = value.replace(/\\cdot/g, "\\times");
473
+
474
+ // Preserve cursor position in the common case:
475
+ // typing '*' to insert a multiplication sign.
476
+ // We do this by modifying internal MathQuill state
477
+ // directly, instead of waiting for `.latex()` to be
478
+ // called in `componentDidUpdate()`.
479
+ const left = mathField.__controller.cursor[mathQuillInstance.L];
480
+ if (left && left.ctrlSeq === "\\cdot ") {
481
+ mathField.__controller.backspace();
482
+ mathField.cmd("\\times");
483
+ }
484
+ } else {
485
+ value = value.replace(/\\times/g, "\\cdot");
486
+ }
487
+ if (this.props.value !== value) {
488
+ this.props.onChange(value);
489
+ }
490
+ },
491
+ enter: () => {
492
+ // NOTE(kevinb): This isn't how answers to exercises are
493
+ // submitted. The actual mechanism for this can be found
494
+ // in exercise-problem-template.jsx, see `handleSubmit`.
495
+
496
+ // This handler is called when the user presses the enter
497
+ // key. Since this isn't an actual <input> element, we have
498
+ // to manually trigger the usually automatic form submit.
499
+ if (this.__mathFieldWrapperRef) {
500
+ $(this.__mathFieldWrapperRef).submit();
501
+ }
502
+ },
503
+ upOutOf: mathField => {
504
+ // This handler is called when the user presses the up
505
+ // arrow key, but there is nowhere in the expression to go
506
+ // up to (no numerator or exponent). For ease of use,
507
+ // interpret this as an attempt to create an exponent.
508
+ mathField.typedText("^");
509
+ }
510
+ }
511
+ }));
448
512
  }
449
- input.focus();
450
- };
451
- this.mathField = options => {
452
- // The MathQuill API is now "versioned" through its own "InterVer"
453
- // system.
454
- // See: https://github.com/mathquill/mathquill/pull/459
455
- const MQ = MathQuill.getInterface(2);
456
-
457
- // MathQuill.MathField takes a DOM node, MathQuill-ifies it if it's
458
- // seeing that node for the first time, then returns the associated
459
- // MathQuill object for that node. It is stable - will always return
460
- // the same object when called on the same DOM node.
461
- // eslint-disable-next-line react/no-string-refs
462
- return MQ.MathField(ReactDOM__default.findDOMNode(this.refs.mathinput), options);
513
+ return this.__mathField;
463
514
  };
464
515
  this.focus = () => {
465
- // @ts-expect-error [FEI-5003] - TS2554 - Expected 1 arguments, but got 0.
466
- this.mathField().focus();
516
+ var _this$mathField;
517
+ (_this$mathField = this.mathField()) == null ? void 0 : _this$mathField.focus();
467
518
  this.setState({
468
519
  focused: true
469
520
  });
470
521
  };
471
522
  this.blur = () => {
472
- // @ts-expect-error [FEI-5003] - TS2554 - Expected 1 arguments, but got 0.
473
- this.mathField().blur();
523
+ var _this$mathField2;
524
+ (_this$mathField2 = this.mathField()) == null ? void 0 : _this$mathField2.blur();
474
525
  this.setState({
475
526
  focused: false
476
527
  });
477
528
  };
478
529
  }
479
530
  componentDidMount() {
531
+ var _this$mathField3;
480
532
  window.addEventListener("mousedown", this.handleMouseDown);
481
533
  window.addEventListener("mouseup", this.handleMouseUp);
482
- let initialized = false;
483
-
484
- // Initialize MathQuill.MathField instance
485
- this.mathField({
486
- // LaTeX commands that, when typed, are immediately replaced by the
487
- // appropriate symbol. This does not include ln, log, or any of the
488
- // trig functions; those are always interpreted as commands.
489
- autoCommands: "pi theta phi sqrt nthroot",
490
- // Pop the cursor out of super/subscripts on arithmetic operators
491
- // or (in)equalities.
492
- charsThatBreakOutOfSupSub: "+-*/=<>≠≤≥",
493
- // Prevent excessive super/subscripts or fractions from being
494
- // created without operands, e.g. when somebody holds down a key
495
- supSubsRequireOperand: true,
496
- // The name of this option is somewhat misleading, as tabbing in
497
- // MathQuill breaks you out of a nested context (fraction/script)
498
- // if you're in one, but moves focus to the next input if you're
499
- // not. Spaces (with this option enabled) are just ignored in the
500
- // latter case.
501
- //
502
- // TODO(alex): In order to allow inputting mixed numbers, we will
503
- // have to accept spaces in certain cases. The desired behavior is
504
- // still to escape nested contexts if currently in one, but to
505
- // insert a space if not (we don't expect mixed numbers in nested
506
- // contexts). We should also limit to one consecutive space.
507
- spaceBehavesLikeTab: true,
508
- handlers: {
509
- edited: mathField => {
510
- // This handler is guaranteed to be called on change, but
511
- // unlike React it sometimes generates false positives.
512
- // One of these is on initialization (with an empty string
513
- // value), so we have to guard against that below.
514
- let value = mathField.latex();
515
-
516
- // Provide a MathQuill-compatible way to generate the
517
- // not-equals sign without pasting unicode or typing TeX
518
- value = value.replace(/<>/g, "\\ne");
519
-
520
- // Use the specified symbol to represent multiplication
521
- // TODO(alex): Add an option to disallow variables, in
522
- // which case 'x' should get converted to '\\times'
523
- if (this.props.convertDotToTimes) {
524
- value = value.replace(/\\cdot/g, "\\times");
525
-
526
- // Preserve cursor position in the common case:
527
- // typing '*' to insert a multiplication sign.
528
- // We do this by modifying internal MathQuill state
529
- // directly, instead of waiting for `.latex()` to be
530
- // called in `componentDidUpdate()`.
531
- const left = mathField.__controller.cursor[MathQuill.L];
532
- if (left && left.ctrlSeq === "\\cdot ") {
533
- mathField.__controller.backspace();
534
- mathField.cmd("\\times");
535
- }
536
- } else {
537
- value = value.replace(/\\times/g, "\\cdot");
538
- }
539
- if (initialized && this.props.value !== value) {
540
- this.props.onChange(value);
541
- }
542
- },
543
- enter: () => {
544
- // NOTE(kevinb): This isn't how answers to exercises are
545
- // submitted. The actual mechanism for this can be found
546
- // in exercise-problem-template.jsx, see `handleSubmit`.
547
-
548
- // This handler is called when the user presses the enter
549
- // key. Since this isn't an actual <input> element, we have
550
- // to manually trigger the usually automatic form submit.
551
- // eslint-disable-next-line react/no-string-refs
552
- // @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call. | TS2339 - Property 'submit' does not exist on type 'JQueryStatic'.
553
- $(ReactDOM__default.findDOMNode(this.refs.mathinput)).submit();
554
- },
555
- upOutOf: mathField => {
556
- // This handler is called when the user presses the up
557
- // arrow key, but there is nowhere in the expression to go
558
- // up to (no numerator or exponent). For ease of use,
559
- // interpret this as an attempt to create an exponent.
560
- mathField.typedText("^");
561
- }
562
- }
563
- });
564
534
 
565
535
  // Ideally, we would be able to pass an initial value directly into
566
- // the constructor above
567
- // @ts-expect-error [FEI-5003] - TS2554 - Expected 1 arguments, but got 0.
568
- this.mathField().latex(this.props.value);
569
- initialized = true;
536
+ // the constructor
537
+ (_this$mathField3 = this.mathField()) == null ? void 0 : _this$mathField3.latex(this.props.value);
570
538
  }
571
539
  componentWillUnmount() {
572
540
  window.removeEventListener("mousedown", this.handleMouseDown);
@@ -609,10 +577,8 @@ class MathInput extends React.Component {
609
577
  display: "inline-block"
610
578
  }
611
579
  }, /*#__PURE__*/React.createElement("span", {
612
- className: className
613
- // eslint-disable-next-line react/no-string-refs
614
- ,
615
- ref: "mathinput",
580
+ className: className,
581
+ ref: ref => this.__mathFieldWrapperRef = ref,
616
582
  "aria-label": this.props.labelText,
617
583
  onFocus: this.handleFocus,
618
584
  onBlur: this.handleBlur
@@ -2458,7 +2424,6 @@ const KhanAnswerTypes = {
2458
2424
  }
2459
2425
  };
2460
2426
 
2461
- /* eslint-disable react/prop-types */
2462
2427
  const ERROR_MESSAGE = i18n._("Sorry, I don't understand that!");
2463
2428
  const insertBraces = value => {
2464
2429
  // HACK(alex): Make sure that all LaTeX super/subscripts are wrapped
@@ -3392,7 +3357,7 @@ const DeprecationMixin$1 = {
3392
3357
  /**
3393
3358
  * Approximate equality on numbers and primitives.
3394
3359
  */
3395
- function eq$1(x, y) {
3360
+ function eq$2(x, y) {
3396
3361
  if (typeof x === "number" && typeof y === "number") {
3397
3362
  return Math.abs(x - y) < 1e-9;
3398
3363
  }
@@ -3403,13 +3368,13 @@ function eq$1(x, y) {
3403
3368
  * Deep approximate equality on primitives, numbers, arrays, and objects.
3404
3369
  * Recursive.
3405
3370
  */
3406
- function deepEq$5(x, y) {
3371
+ function deepEq$6(x, y) {
3407
3372
  if (Array.isArray(x) && Array.isArray(y)) {
3408
3373
  if (x.length !== y.length) {
3409
3374
  return false;
3410
3375
  }
3411
3376
  for (let i = 0; i < x.length; i++) {
3412
- if (!deepEq$5(x[i], y[i])) {
3377
+ if (!deepEq$6(x[i], y[i])) {
3413
3378
  return false;
3414
3379
  }
3415
3380
  }
@@ -3419,7 +3384,7 @@ function deepEq$5(x, y) {
3419
3384
  return false;
3420
3385
  }
3421
3386
  if (typeof x === "function" && typeof y === "function") {
3422
- return eq$1(x, y);
3387
+ return eq$2(x, y);
3423
3388
  }
3424
3389
  if (typeof x === "function" || typeof y === "function") {
3425
3390
  return false;
@@ -3427,16 +3392,16 @@ function deepEq$5(x, y) {
3427
3392
  if (typeof x === "object" && typeof y === "object" && !!x && !!y) {
3428
3393
  return x === y || _.all(x, function (v, k) {
3429
3394
  // @ts-expect-error [FEI-5003] - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
3430
- return deepEq$5(y[k], v);
3395
+ return deepEq$6(y[k], v);
3431
3396
  }) && _.all(y, function (v, k) {
3432
3397
  // @ts-expect-error [FEI-5003] - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
3433
- return deepEq$5(x[k], v);
3398
+ return deepEq$6(x[k], v);
3434
3399
  });
3435
3400
  }
3436
3401
  if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
3437
3402
  return false;
3438
3403
  }
3439
- return eq$1(x, y);
3404
+ return eq$2(x, y);
3440
3405
  }
3441
3406
 
3442
3407
  /**
@@ -3754,8 +3719,8 @@ const Util = {
3754
3719
  tickStepFromNumTicks,
3755
3720
  constrainedTickStepsFromTickSteps,
3756
3721
  DeprecationMixin: DeprecationMixin$1,
3757
- eq: eq$1,
3758
- deepEq: deepEq$5,
3722
+ eq: eq$2,
3723
+ deepEq: deepEq$6,
3759
3724
  parseQueryString,
3760
3725
  updateQueryString: updateQueryString$2,
3761
3726
  strongEncodeURIComponent,
@@ -14017,7 +13982,7 @@ _.extend(MovableAngle.prototype, {
14017
13982
 
14018
13983
  /* eslint-disable @babel/no-invalid-this */
14019
13984
  const nestedMap$1 = Util.nestedMap;
14020
- const deepEq$4 = Util.deepEq;
13985
+ const deepEq$5 = Util.deepEq;
14021
13986
 
14022
13987
  /**
14023
13988
  * A base class for all Graphie Movables
@@ -14114,7 +14079,7 @@ const createSimpleClass = function createSimpleClass(addFunction) {
14114
14079
  this._prevProps = this.props;
14115
14080
  },
14116
14081
  modify: function (graphie) {
14117
- if (!deepEq$4(this.props, this._prevProps)) {
14082
+ if (!deepEq$5(this.props, this._prevProps)) {
14118
14083
  this.remove();
14119
14084
  this.add(graphie);
14120
14085
  this._prevProps = this.props;
@@ -16387,7 +16352,7 @@ var Movables = {
16387
16352
  const GraphieMovable = GraphieClasses.GraphieMovable;
16388
16353
  const createGraphie = GraphUtils.createGraphie;
16389
16354
  const {
16390
- deepEq: deepEq$3,
16355
+ deepEq: deepEq$4,
16391
16356
  nestedMap
16392
16357
  } = Util;
16393
16358
  const {
@@ -16622,7 +16587,7 @@ class Graphie extends React.Component {
16622
16587
  if (this.props.setup !== prevProps.setup) {
16623
16588
  Log.error("<Graphie> was given a new setup function. " + "This is a bad idea; please refactor your code to give " + "the same setup function reference to <Graphie> on " + "every render.", Errors.Internal);
16624
16589
  }
16625
- if (!deepEq$3(this.props.options, prevProps.options) || !deepEq$3(this.props.box, prevProps.box) || !deepEq$3(this.props.range, prevProps.range)) {
16590
+ if (!deepEq$4(this.props.options, prevProps.options) || !deepEq$4(this.props.box, prevProps.box) || !deepEq$4(this.props.range, prevProps.range)) {
16626
16591
  this._setupGraphie();
16627
16592
  }
16628
16593
  this._updateMovables();
@@ -21045,7 +21010,7 @@ SimpleKeypadInput.propTypes = {
21045
21010
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
21046
21011
  };
21047
21012
 
21048
- /* eslint-disable react/prop-types */
21013
+ /* eslint-disable react/sort-comp */
21049
21014
  const ParseTex$1 = TexWrangler.parseTex;
21050
21015
  const answerTypes = {
21051
21016
  number: {
@@ -21348,7 +21313,6 @@ var InputNumber$1 = {
21348
21313
  isLintable: true
21349
21314
  };
21350
21315
 
21351
- /* eslint-disable react/prop-types */
21352
21316
  const ParseTex = TexWrangler.parseTex;
21353
21317
  const answerFormButtons = [{
21354
21318
  title: "Integers",
@@ -21511,7 +21475,11 @@ class NumericInput extends React.Component {
21511
21475
  return answerStrings[0];
21512
21476
  }
21513
21477
  static validate(useInput, rubric) {
21514
- const allAnswerForms = answerFormButtons.map(e => e["value"]);
21478
+ const allAnswerForms = answerFormButtons.map(e => e["value"])
21479
+ // Don't default to validating the answer as a pi answer
21480
+ // if answerForm isn't set on the answer
21481
+ // https://khanacademy.atlassian.net/browse/LC-691
21482
+ .filter(e => e !== "pi");
21515
21483
  const createValidator = answer => {
21516
21484
  const stringAnswer = `${answer.value}`;
21517
21485
  return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
@@ -26565,7 +26533,7 @@ var Iframe$1 = {
26565
26533
  hidden: true
26566
26534
  };
26567
26535
 
26568
- /* eslint-disable react/prop-types */
26536
+ /* eslint-disable react/sort-comp */
26569
26537
  const defaultBoxSize$2 = 400;
26570
26538
  const defaultRange = [0, 10];
26571
26539
  const defaultBackgroundImage$3 = {
@@ -27686,36 +27654,29 @@ Graph.defaultProps = {
27686
27654
  isMobile: false
27687
27655
  };
27688
27656
 
27657
+ /**
27658
+ * A collection of geomtry-related utility functions
27659
+ */
27689
27660
  const {
27690
- DeprecationMixin
27661
+ eq: eq$1,
27662
+ deepEq: deepEq$3
27691
27663
  } = Util;
27692
- const TRASH_ICON_URI = "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
27693
- const defaultBackgroundImage$1 = {
27694
- url: null
27695
- };
27696
- const eq = Util.eq;
27697
- const deepEq$2 = Util.deepEq;
27698
- const UNLIMITED = "unlimited";
27699
27664
 
27700
- // Sample background image:
27701
- // https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
27665
+ // This should really be a readonly tuple of [number, number]
27666
+
27667
+ // a, b, c
27702
27668
 
27703
- function ccw(a, b, c) {
27704
- return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
27705
- }
27706
- function collinear(a, b, c) {
27707
- return eq(ccw(a, b, c), 0);
27708
- }
27709
27669
  function sign(val) {
27710
- if (eq(val, 0)) {
27670
+ if (eq$1(val, 0)) {
27711
27671
  return 0;
27712
27672
  }
27713
27673
  return val > 0 ? 1 : -1;
27714
27674
  }
27715
-
27716
- // default to defaultValue if actual is null or undefined
27717
- function defaultVal(actual, defaultValue) {
27718
- return actual == null ? defaultValue : actual;
27675
+ function ccw(a, b, c) {
27676
+ return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
27677
+ }
27678
+ function collinear(a, b, c) {
27679
+ return eq$1(ccw(a, b, c), 0);
27719
27680
  }
27720
27681
 
27721
27682
  // Given rect bounding points A and B, whether point C is inside the rect
@@ -27728,14 +27689,12 @@ function pointInRect(a, b, c) {
27728
27689
  function intersects(ab, cd) {
27729
27690
  const triplets = [[ab[0], ab[1], cd[0]], [ab[0], ab[1], cd[1]], [cd[0], cd[1], ab[0]], [cd[0], cd[1], ab[1]]];
27730
27691
  const orientations = _.map(triplets, function (triplet) {
27731
- // @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
27732
27692
  return sign(ccw(...triplet));
27733
27693
  });
27734
27694
  if (orientations[0] !== orientations[1] && orientations[2] !== orientations[3]) {
27735
27695
  return true;
27736
27696
  }
27737
27697
  for (let i = 0; i < 4; i++) {
27738
- // @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
27739
27698
  if (orientations[i] === 0 && pointInRect(...triplets[i])) {
27740
27699
  return true;
27741
27700
  }
@@ -27813,13 +27772,13 @@ function similar(coords1, coords2, tolerance) {
27813
27772
  angles = rotate(angles, i);
27814
27773
  // @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
27815
27774
  sides = rotate(sides, i);
27816
- if (deepEq$2(angles1, angles)) {
27775
+ if (deepEq$3(angles1, angles)) {
27817
27776
  const sidePairs = _.zip(sides1, sides);
27818
27777
  const factors = _.map(sidePairs, function (pair) {
27819
27778
  return pair[0] / pair[1];
27820
27779
  });
27821
27780
  const same = _.all(factors, function (factor) {
27822
- return eq(factors[0], factor);
27781
+ return eq$1(factors[0], factor);
27823
27782
  });
27824
27783
  const congruentEnough = _.all(sidePairs, function (pair) {
27825
27784
  return number.equal(pair[0], pair[1], tolerance);
@@ -27832,11 +27791,6 @@ function similar(coords1, coords2, tolerance) {
27832
27791
  return false;
27833
27792
  }
27834
27793
 
27835
- // Less than or approximately equal
27836
- function leq(a, b) {
27837
- return a < b || eq(a, b);
27838
- }
27839
-
27840
27794
  // Given triangle with sides ABC return angle opposite side C in degrees
27841
27795
  function lawOfCosines(a, b, c) {
27842
27796
  return Math.acos((a * a + b * b - c * c) / (2 * a * b)) * 180 / Math.PI;
@@ -27875,13 +27829,8 @@ function rotate(array, n) {
27875
27829
  n = typeof n === "undefined" ? 1 : n % array.length;
27876
27830
  return array.slice(n).concat(array.slice(0, n));
27877
27831
  }
27878
- function capitalize(str) {
27879
- return str.replace(/(?:^|-)(.)/g, function (match, letter) {
27880
- return letter.toUpperCase();
27881
- });
27882
- }
27883
27832
  function getLineEquation(first, second) {
27884
- if (eq(first[0], second[0])) {
27833
+ if (eq$1(first[0], second[0])) {
27885
27834
  return "x = " + first[0].toFixed(3);
27886
27835
  }
27887
27836
  const m = (second[1] - first[1]) / (second[0] - first[0]);
@@ -27910,6 +27859,35 @@ firstPoints, secondPoints) {
27910
27859
  const y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / determinant;
27911
27860
  return "Intersection: (" + x.toFixed(3) + ", " + y.toFixed(3) + ")";
27912
27861
  }
27862
+
27863
+ const {
27864
+ DeprecationMixin
27865
+ } = Util;
27866
+ const TRASH_ICON_URI = "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
27867
+ const defaultBackgroundImage$1 = {
27868
+ url: null
27869
+ };
27870
+ const eq = Util.eq;
27871
+ const deepEq$2 = Util.deepEq;
27872
+ const UNLIMITED = "unlimited";
27873
+
27874
+ // Sample background image:
27875
+ // https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
27876
+
27877
+ // default to defaultValue if actual is null or undefined
27878
+ function defaultVal(actual, defaultValue) {
27879
+ return actual == null ? defaultValue : actual;
27880
+ }
27881
+
27882
+ // Less than or approximately equal
27883
+ function leq(a, b) {
27884
+ return a < b || eq(a, b);
27885
+ }
27886
+ function capitalize(str) {
27887
+ return str.replace(/(?:^|-)(.)/g, function (match, letter) {
27888
+ return letter.toUpperCase();
27889
+ });
27890
+ }
27913
27891
  function numSteps(range, step) {
27914
27892
  return Math.floor((range[1] - range[0]) / step);
27915
27893
  }
@@ -29061,17 +29039,6 @@ class InteractiveGraph extends React.Component {
29061
29039
  _.invoke(this.points, "remove");
29062
29040
  (_this$angle2 = this.angle) == null ? void 0 : _this$angle2.remove();
29063
29041
  };
29064
- this.toggleShowAngles = () => {
29065
- if (this.props.graph.type !== "polygon") {
29066
- throw makeInvalidTypeError("toggleShowAngles", "polygon");
29067
- }
29068
- const graph = _.extend({}, this.props.graph, {
29069
- showAngles: !this.props.graph.showAngles
29070
- });
29071
- this.onChange({
29072
- graph: graph
29073
- });
29074
- };
29075
29042
  this.toggleShowSides = () => {
29076
29043
  if (this.props.graph.type !== "polygon") {
29077
29044
  throw makeInvalidTypeError("toggleShowSides", "polygon");
@@ -45588,7 +45555,7 @@ const styles$1 = StyleSheet.create({
45588
45555
  }
45589
45556
  });
45590
45557
 
45591
- /* eslint-disable react/prop-types */
45558
+ /* eslint-disable @babel/no-invalid-this */
45592
45559
 
45593
45560
  /* A checkbox that syncs its value to props using the
45594
45561
  * renderer's onChange method, and gets the prop name