@khanacademy/math-input 16.5.1 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -48,7 +48,7 @@ var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
48
48
 
49
49
  // This file is processed by a Rollup plugin (replace) to inject the production
50
50
  const libName = "@khanacademy/math-input";
51
- const libVersion = "16.5.1";
51
+ const libVersion = "17.0.0";
52
52
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
53
53
 
54
54
  function _extends() {
@@ -373,39 +373,37 @@ let CursorContext = /*#__PURE__*/function (CursorContext) {
373
373
 
374
374
  // We only need one MathQuill instance (referred to as MQ in the docs)
375
375
  // and that contains some MQ constants and the MathField constructor
376
- const mathQuillInstance = MathQuill__default["default"].getInterface(2);
377
- function createBaseConfig() {
378
- return {
379
- // LaTeX commands that, when typed, are immediately replaced by the
380
- // appropriate symbol. This does not include ln, log, or any of the
381
- // trig functions; those are always interpreted as commands.
382
- autoCommands: "pi theta phi sqrt nthroot",
383
- // Most of these autoOperatorNames are simply the MathQuill defaults.
384
- // We have to list them all in order to add the `sen` operator (see
385
- // comment below).
386
- autoOperatorNames: ["arccos", "arcsin", "arctan", "arg", "cos", "cosh", "cot", "coth", "csc", "deg", "det", "dim", "exp", "gcd", "hom", "inf", "ker", "lg", "lim", "liminf", "limsup", "ln", "log", "max", "min", "Pr", "projlim", "sec",
387
- // sen is used instead of sin in e.g. Portuguese
388
- "sen", "sin", "sinh", "sup", "tan", "tanh"].join(" "),
389
- // Pop the cursor out of super/subscripts on arithmetic operators
390
- // or (in)equalities.
391
- charsThatBreakOutOfSupSub: "+-*/=<>≠≤≥",
392
- // Prevent excessive super/subscripts or fractions from being
393
- // created without operands, e.g. when somebody holds down a key
394
- supSubsRequireOperand: true,
395
- // The name of this option is somewhat misleading, as tabbing in
396
- // MathQuill breaks you out of a nested context (fraction/script)
397
- // if you're in one, but moves focus to the next input if you're
398
- // not. Spaces (with this option enabled) are just ignored in the
399
- // latter case.
400
- //
401
- // TODO(alex): In order to allow inputting mixed numbers, we will
402
- // have to accept spaces in certain cases. The desired behavior is
403
- // still to escape nested contexts if currently in one, but to
404
- // insert a space if not (we don't expect mixed numbers in nested
405
- // contexts). We should also limit to one consecutive space.
406
- spaceBehavesLikeTab: true
407
- };
408
- }
376
+ const mathQuillInstance = MathQuill__default["default"].getInterface(3);
377
+ const createBaseConfig = () => ({
378
+ // LaTeX commands that, when typed, are immediately replaced by the
379
+ // appropriate symbol. This does not include ln, log, or any of the
380
+ // trig functions; those are always interpreted as commands.
381
+ autoCommands: "pi theta phi sqrt nthroot",
382
+ // Most of these autoOperatorNames are simply the MathQuill defaults.
383
+ // We have to list them all in order to add the `sen` operator (see
384
+ // comment below).
385
+ autoOperatorNames: ["arccos", "arcsin", "arctan", "arg", "cos", "cosh", "cot", "coth", "csc", "deg", "det", "dim", "exp", "gcd", "hom", "inf", "ker", "lg", "lim", "liminf", "limsup", "ln", "log", "max", "min", "Pr", "projlim", "sec",
386
+ // sen is used instead of sin in e.g. Portuguese
387
+ "sen", "sin", "sinh", "sup", "tan", "tanh"].join(" "),
388
+ // Pop the cursor out of super/subscripts on arithmetic operators
389
+ // or (in)equalities.
390
+ charsThatBreakOutOfSupSub: "+-*/=<>≠≤≥",
391
+ // Prevent excessive super/subscripts or fractions from being
392
+ // created without operands, e.g. when somebody holds down a key
393
+ supSubsRequireOperand: true,
394
+ // The name of this option is somewhat misleading, as tabbing in
395
+ // MathQuill breaks you out of a nested context (fraction/script)
396
+ // if you're in one, but moves focus to the next input if you're
397
+ // not. Spaces (with this option enabled) are just ignored in the
398
+ // latter case.
399
+ //
400
+ // TODO(alex): In order to allow inputting mixed numbers, we will
401
+ // have to accept spaces in certain cases. The desired behavior is
402
+ // still to escape nested contexts if currently in one, but to
403
+ // insert a space if not (we don't expect mixed numbers in nested
404
+ // contexts). We should also limit to one consecutive space.
405
+ spaceBehavesLikeTab: true
406
+ });
409
407
 
410
408
  /**
411
409
  * Creates a new [MathField](http://docs.mathquill.com/en/latest/Api_Methods/#mqmathfieldhtml_element-config)
@@ -451,29 +449,27 @@ const Letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"
451
449
  // We only consider numerals, variables, and Greek Letters to be proper
452
450
  // leaf nodes.
453
451
  const ValidLeaves = [...Numerals, ...GreekLetters, ...Letters.map(letter => letter.toLowerCase()), ...Letters.map(letter => letter.toUpperCase())];
454
- function getCursor(mathField) {
455
- return mathField.__controller.cursor;
456
- }
452
+ const mqNodeHasClass = (node, className) => node._el && node._el.classList.contains(className);
457
453
  function isFraction(node) {
458
- return node.jQ && node.jQ.hasClass("mq-fraction");
454
+ return mqNodeHasClass(node, "mq-fraction");
459
455
  }
460
456
  function isNumerator(node) {
461
- return node.jQ && node.jQ.hasClass("mq-numerator");
457
+ return mqNodeHasClass(node, "mq-numerator");
462
458
  }
463
459
  function isDenominator(node) {
464
- return node.jQ && node.jQ.hasClass("mq-denominator");
460
+ return mqNodeHasClass(node, "mq-denominator");
465
461
  }
466
462
  function isSubScript(node) {
467
463
  // NOTE(charlie): MyScript has a structure whereby its superscripts seem
468
464
  // to be represented as a parent node with 'mq-sup-only' containing a
469
465
  // single child with 'mq-sup'.
470
- return node.jQ && (node.jQ.hasClass("mq-sub-only") || node.jQ.hasClass("mq-sub"));
466
+ return mqNodeHasClass(node, "mq-sub-only") || mqNodeHasClass(node, "mq-sub");
471
467
  }
472
468
  function isSuperScript(node) {
473
469
  // NOTE(charlie): MyScript has a structure whereby its superscripts seem
474
470
  // to be represented as a parent node with 'mq-sup-only' containing a
475
471
  // single child with 'mq-sup'.
476
- return node.jQ && (node.jQ.hasClass("mq-sup-only") || node.jQ.hasClass("mq-sup"));
472
+ return mqNodeHasClass(node, "mq-sup-only") || mqNodeHasClass(node, "mq-sup");
477
473
  }
478
474
  function isParens(node) {
479
475
  return node && node.ctrlSeq === "\\left(";
@@ -482,17 +478,17 @@ function isLeaf(node) {
482
478
  return node && node.ctrlSeq && ValidLeaves.includes(node.ctrlSeq.trim());
483
479
  }
484
480
  function isSquareRoot(node) {
485
- return node.blocks && node.blocks[0].jQ && node.blocks[0].jQ.hasClass("mq-sqrt-stem");
481
+ return node.blocks && mqNodeHasClass(node.blocks[0], "mq-sqrt-stem");
486
482
  }
487
483
  function isNthRoot(node) {
488
- return node.blocks && node.blocks[0].jQ && node.blocks[0].jQ.hasClass("mq-nthroot");
484
+ return node.blocks && mqNodeHasClass(node.blocks[0], "mq-nthroot");
489
485
  }
490
486
  function isNthRootIndex(node) {
491
- return node.jQ && node.jQ.hasClass("mq-nthroot");
487
+ return mqNodeHasClass(node, "mq-nthroot");
492
488
  }
493
489
  function isInsideLogIndex(cursor) {
494
490
  const grandparent = cursor.parent.parent;
495
- if (grandparent && grandparent.jQ.hasClass("mq-supsub")) {
491
+ if (grandparent && mqNodeHasClass(grandparent, "mq-supsub")) {
496
492
  const command = maybeFindCommandBeforeParens(grandparent);
497
493
  if (command && command.name === "\\log") {
498
494
  return true;
@@ -616,7 +612,7 @@ function getCursorContext(mathField) {
616
612
  }
617
613
 
618
614
  // First, try to find any fraction to the right, unimpeded.
619
- const cursor = getCursor(mathField);
615
+ const cursor = mathField.cursor();
620
616
  let visitor = cursor;
621
617
  while (visitor[mathQuillInstance.R] !== MathFieldActionType.MQ_END) {
622
618
  if (isFraction(visitor[mathQuillInstance.R])) {
@@ -666,7 +662,7 @@ function handleBackspaceInRootIndex(mathField, cursor) {
666
662
  const latex = grandparent.latex();
667
663
  const reinsertionPoint = grandparent[mathQuillInstance.L];
668
664
  selectNode(grandparent, cursor);
669
- const rootIsEmpty = grandparent.blocks[1].jQ.text() === "";
665
+ const rootIsEmpty = grandparent.blocks[1]._el.textContent === "";
670
666
  if (rootIsEmpty) {
671
667
  // If there is not content under the root then simply delete
672
668
  // the whole thing.
@@ -683,7 +679,7 @@ function handleBackspaceInRootIndex(mathField, cursor) {
683
679
 
684
680
  // Adjust the cursor to be to the left the sqrt.
685
681
  if (reinsertionPoint === MathFieldActionType.MQ_END) {
686
- mathField.moveToDirEnd(mathQuillInstance.L);
682
+ mathField.moveToLeftEnd();
687
683
  } else {
688
684
  cursor.insRightOf(reinsertionPoint);
689
685
  }
@@ -709,7 +705,7 @@ function handleBackspaceInLogIndex(mathField, cursor) {
709
705
  }
710
706
  cursor.select();
711
707
  cursor.endSelection();
712
- const isLogBodyEmpty = grandparent[mathQuillInstance.R].contentjQ.text() === "";
708
+ const isLogBodyEmpty = grandparent[mathQuillInstance.R]._el.textContent === "";
713
709
  if (isLogBodyEmpty) {
714
710
  // If there's no content inside the log's parens then delete the
715
711
  // whole thing.
@@ -790,7 +786,7 @@ function handleBackspaceInsideParens(mathField, cursor) {
790
786
 
791
787
  if (grandparent[mathQuillInstance.L].sub) {
792
788
  // if there is a subscript
793
- if (grandparent[mathQuillInstance.L].sub.jQ.text()) {
789
+ if (grandparent[mathQuillInstance.L].sub._el.textContent) {
794
790
  // and it contains text
795
791
  // move the cursor to the right end of the subscript
796
792
  cursor.insAtRightEnd(grandparent[mathQuillInstance.L].sub);
@@ -827,7 +823,7 @@ function handleBackspaceAfterLigaturedSymbol(mathField) {
827
823
  * See inline comments for precise behavior of different cases.
828
824
  */
829
825
  function handleBackspace(mathField) {
830
- const cursor = getCursor(mathField);
826
+ const cursor = mathField.cursor();
831
827
  if (!cursor.selection) {
832
828
  const parent = cursor.parent;
833
829
  const grandparent = parent.parent;
@@ -936,7 +932,7 @@ function handleRightArrow(mathField, cursor) {
936
932
  }
937
933
  }
938
934
  function handleArrow(mathField, key) {
939
- const cursor = getCursor(mathField);
935
+ const cursor = mathField.cursor();
940
936
  if (key === "LEFT") {
941
937
  handleLeftArrow(mathField, cursor);
942
938
  } else if (key === "RIGHT") {
@@ -947,7 +943,7 @@ function handleArrow(mathField, key) {
947
943
  const ArithmeticOperators = ["+", "-", "\\cdot", "\\times", "\\div"];
948
944
  const EqualityOperators = ["=", "\\neq", "<", "\\leq", ">", "\\geq"];
949
945
  function handleExponent(mathField, key) {
950
- const cursor = getCursor(mathField);
946
+ const cursor = mathField.cursor();
951
947
  // If there's an invalid operator preceding the cursor (anything that
952
948
  // knowingly cannot be raised to a power), add an empty set of
953
949
  // parentheses and apply the exponent to that.
@@ -996,7 +992,7 @@ const KeysForJumpContext = {
996
992
  * Advances the cursor to the next logical position.
997
993
  */
998
994
  function handleJumpOut(mathField, key) {
999
- const cursor = getCursor(mathField);
995
+ const cursor = mathField.cursor();
1000
996
  const context = getCursorContext(mathField);
1001
997
 
1002
998
  // Validate that the current cursor context matches the key's intent.
@@ -1152,7 +1148,7 @@ const keyToMathquillMap = {
1152
1148
  },
1153
1149
 
1154
1150
  FRAC_EXCLUSIVE: mathQuill => {
1155
- const cursor = mathQuill.__controller.cursor;
1151
+ const cursor = mathQuill.cursor();
1156
1152
  // If there's nothing to the left of the cursor, then we want to
1157
1153
  // leave the cursor to the left of the fraction after creating it.
1158
1154
  const shouldNavigateLeft = cursor[mathQuillInstance.L] === ActionType.MQ_END;
@@ -1296,18 +1292,16 @@ class MathWrapper {
1296
1292
  // HACK(charlie): We shouldn't reaching into MathQuill internals like
1297
1293
  // this, but it's the easiest way to allow us to manage the focus state
1298
1294
  // ourselves.
1299
- const controller = this.mathField.__controller;
1300
- controller.cursor.show();
1295
+ this.mathField.cursor().show();
1301
1296
 
1302
1297
  // Set MathQuill's internal state to reflect the focus, otherwise it
1303
1298
  // will consistently try to hide the cursor on key-press and introduce
1304
1299
  // layout jank.
1305
- controller.blurred = false;
1300
+ this.mathField.focus();
1306
1301
  }
1307
1302
  blur() {
1308
- const controller = this.mathField.__controller;
1309
- controller.cursor.hide();
1310
- controller.blurred = true;
1303
+ this.mathField.cursor().hide();
1304
+ this.mathField.blur();
1311
1305
  }
1312
1306
 
1313
1307
  /**
@@ -1355,10 +1349,10 @@ class MathWrapper {
1355
1349
  if (el.hasAttribute("mq-root-block")) {
1356
1350
  // If we're in the empty area place the cursor at the right
1357
1351
  // end of the expression.
1358
- cursor.insAtRightEnd(this.mathField.__controller.root);
1352
+ cursor.insAtRightEnd(this.mathField.controller().root);
1359
1353
  } else {
1360
1354
  // Otherwise place beside the element at x, y.
1361
- const controller = this.mathField.__controller;
1355
+ const controller = this.mathField.controller();
1362
1356
  const pageX = x - document.body.scrollLeft;
1363
1357
  const pageY = y - document.body.scrollTop;
1364
1358
  controller.seek($__default["default"](el), pageX, pageY).cursor.startSelection();
@@ -1384,7 +1378,7 @@ class MathWrapper {
1384
1378
  // note(Matthew): extracted this logic to share it elsewhere,
1385
1379
  // but it's part of the public MathWrapper API
1386
1380
  getCursor() {
1387
- return getCursor(this.mathField);
1381
+ return this.mathField.cursor();
1388
1382
  }
1389
1383
 
1390
1384
  // note(Matthew): extracted this logic to keep this file focused,
@@ -1503,19 +1497,6 @@ class MathInput extends React__namespace.Component {
1503
1497
  this.props.keypadElement && this.props.keypadElement.setCursor(cursor);
1504
1498
  }
1505
1499
  });
1506
-
1507
- // NOTE(charlie): MathQuill binds this handler to manage its
1508
- // drag-to-select behavior. For reasons that I can't explain, the event
1509
- // itself gets triggered even if you tap slightly outside of the
1510
- // bound container (maybe 5px outside of any boundary). As a result, the
1511
- // cursor appears when tapping at those locations, even though the input
1512
- // itself doesn't receive any touch start or mouse down event and, as
1513
- // such, doesn't focus itself. This makes for a confusing UX, as the
1514
- // cursor appears, but the keypad does not and the input otherwise
1515
- // treats itself as unfocused. Thankfully, we don't need this behavior--
1516
- // we manage all of the cursor interactions ourselves--so we can safely
1517
- // unbind the handler.
1518
- this.mathField.mathField.__controller.container.unbind("mousedown.mathquill");
1519
1500
  this.mathField.setContent(this.props.value);
1520
1501
  this._updateInputPadding();
1521
1502
  this._container = ReactDOM__default["default"].findDOMNode(this);
@@ -1863,7 +1844,7 @@ class MathInput extends React__namespace.Component {
1863
1844
  // Pre-emptively check if the input has any child nodes; if not, the
1864
1845
  // input is empty, so we throw the cursor at the start.
1865
1846
  if (!this._root.hasChildNodes()) {
1866
- cursor.insAtLeftEnd(this.mathField.mathField.__controller.root);
1847
+ cursor.insAtLeftEnd(this.mathField.mathField.controller().root);
1867
1848
  return;
1868
1849
  }
1869
1850
 
@@ -1911,9 +1892,9 @@ class MathInput extends React__namespace.Component {
1911
1892
  // or left of all of the math, so we place the cursor at the end to
1912
1893
  // which it's closest.
1913
1894
  if (Math.abs(x - right) < Math.abs(x - left)) {
1914
- cursor.insAtRightEnd(this.mathField.mathField.__controller.root);
1895
+ cursor.insAtRightEnd(this.mathField.mathField.controller().root);
1915
1896
  } else {
1916
- cursor.insAtLeftEnd(this.mathField.mathField.__controller.root);
1897
+ cursor.insAtLeftEnd(this.mathField.mathField.controller().root);
1917
1898
  }
1918
1899
  // In that event, we need to update the cursor context ourselves.
1919
1900
  this.props.keypadElement && this.props.keypadElement.setCursor({