@khanacademy/math-input 3.0.0 → 4.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/components/input/__tests__/test-math-wrapper.d.ts +1 -1
  3. package/dist/components/input/__tests__/test-math-wrapper.js.flow +1 -1
  4. package/dist/components/input/key-handlers/handle-arrow.d.ts +3 -0
  5. package/dist/components/input/key-handlers/handle-arrow.js.flow +12 -0
  6. package/dist/components/input/key-handlers/handle-backspace.d.ts +7 -0
  7. package/dist/components/input/key-handlers/handle-backspace.js.flow +14 -0
  8. package/dist/components/input/key-handlers/handle-exponent.d.ts +3 -0
  9. package/dist/components/input/key-handlers/handle-exponent.js.flow +12 -0
  10. package/dist/components/input/key-handlers/handle-jump-out.d.ts +7 -0
  11. package/dist/components/input/key-handlers/handle-jump-out.js.flow +14 -0
  12. package/dist/components/input/math-wrapper.d.ts +7 -78
  13. package/dist/components/input/math-wrapper.js.flow +16 -78
  14. package/dist/components/input/mathquill-helpers.d.ts +46 -0
  15. package/dist/components/input/mathquill-helpers.js.flow +56 -0
  16. package/dist/components/input/mathquill-instance.d.ts +3 -0
  17. package/dist/components/input/mathquill-instance.js.flow +9 -0
  18. package/dist/components/input/mathquill-types.d.ts +25 -0
  19. package/dist/components/input/mathquill-types.js.flow +34 -0
  20. package/dist/components/key-translator.d.ts +4 -0
  21. package/dist/components/key-translator.js.flow +10 -0
  22. package/dist/components/keypad/button-assets.d.ts +2 -2
  23. package/dist/components/keypad/button-assets.js.flow +2 -2
  24. package/dist/components/keypad/keypad-page-items.d.ts +1 -1
  25. package/dist/components/keypad/keypad-page-items.js.flow +1 -1
  26. package/dist/components/keypad-legacy/gesture-manager.d.ts +21 -9
  27. package/dist/components/keypad-legacy/gesture-manager.js.flow +27 -12
  28. package/dist/components/keypad-legacy/gesture-state-machine.d.ts +9 -9
  29. package/dist/components/keypad-legacy/gesture-state-machine.js.flow +10 -10
  30. package/dist/components/keypad-legacy/keypad-button.d.ts +2 -2
  31. package/dist/components/keypad-legacy/keypad-button.js.flow +3 -3
  32. package/dist/components/keypad-legacy/store/actions.d.ts +4 -14
  33. package/dist/components/keypad-legacy/store/actions.js.flow +3 -15
  34. package/dist/components/keypad-legacy/store/types.d.ts +2 -2
  35. package/dist/components/keypad-legacy/store/types.js.flow +2 -2
  36. package/dist/components/keypad-legacy/touchable-keypad-button.d.ts +6 -6
  37. package/dist/components/keypad-legacy/touchable-keypad-button.js.flow +9 -14
  38. package/dist/data/key-configs.d.ts +3 -6
  39. package/dist/data/key-configs.js.flow +3 -8
  40. package/dist/data/keys.d.ts +2 -54
  41. package/dist/data/keys.js.flow +116 -55
  42. package/dist/enums.d.ts +2 -9
  43. package/dist/enums.js.flow +2 -11
  44. package/dist/es/index.js +1781 -1196
  45. package/dist/es/index.js.map +1 -1
  46. package/dist/index.d.ts +3 -2
  47. package/dist/index.js +2069 -1242
  48. package/dist/index.js.flow +4 -2
  49. package/dist/index.js.map +1 -1
  50. package/dist/strings.js +26 -10
  51. package/dist/types.d.ts +10 -12
  52. package/dist/types.js.flow +13 -12
  53. package/package.json +1 -1
  54. package/src/components/input/__tests__/context-tracking.test.ts +43 -44
  55. package/src/components/input/__tests__/mathquill.test.ts +133 -135
  56. package/src/components/input/key-handlers/handle-arrow.ts +70 -0
  57. package/src/components/input/key-handlers/handle-backspace.ts +275 -0
  58. package/src/components/input/key-handlers/handle-exponent.ts +52 -0
  59. package/src/components/input/key-handlers/handle-jump-out.ts +103 -0
  60. package/src/components/input/math-input.tsx +11 -12
  61. package/src/components/input/math-wrapper.ts +88 -837
  62. package/src/components/input/mathquill-helpers.ts +268 -0
  63. package/src/components/input/mathquill-instance.ts +5 -0
  64. package/src/components/input/mathquill-types.ts +55 -0
  65. package/src/components/key-translator.ts +209 -0
  66. package/src/components/keypad/button-assets.tsx +411 -100
  67. package/src/components/keypad/geometry-page/index.tsx +1 -1
  68. package/src/components/keypad/keypad-mathquill.stories.tsx +69 -0
  69. package/src/components/keypad/keypad-page-items.tsx +2 -1
  70. package/src/components/keypad/operators-page/index.tsx +1 -1
  71. package/src/components/keypad-legacy/echo-manager.tsx +4 -4
  72. package/src/components/keypad-legacy/empty-keypad-button.tsx +6 -4
  73. package/src/components/keypad-legacy/gesture-manager.ts +32 -9
  74. package/src/components/keypad-legacy/gesture-state-machine.ts +14 -14
  75. package/src/components/keypad-legacy/keypad-button.tsx +15 -18
  76. package/src/components/keypad-legacy/many-keypad-button.tsx +9 -2
  77. package/src/components/keypad-legacy/store/actions.ts +3 -29
  78. package/src/components/keypad-legacy/store/echo-reducer.ts +2 -5
  79. package/src/components/keypad-legacy/store/index.ts +4 -10
  80. package/src/components/keypad-legacy/store/input-reducer.ts +1 -2
  81. package/src/components/keypad-legacy/store/keypad-reducer.ts +2 -3
  82. package/src/components/keypad-legacy/store/types.ts +2 -2
  83. package/src/components/keypad-legacy/touchable-keypad-button.tsx +8 -13
  84. package/src/components/tabbar/icons.tsx +0 -2
  85. package/src/data/key-configs.ts +751 -304
  86. package/src/data/keys.ts +118 -65
  87. package/src/enums.ts +10 -9
  88. package/src/index.ts +3 -2
  89. package/src/math-input.stories.tsx +1 -1
  90. package/src/types.ts +10 -12
  91. package/tsconfig-build.tsbuildinfo +1 -1
package/dist/index.js CHANGED
@@ -116,64 +116,6 @@ function _toPropertyKey(arg) {
116
116
  return typeof key === "symbol" ? key : String(key);
117
117
  }
118
118
 
119
- /**
120
- * This file contains constants for keypad buttons that aren't single
121
- * alphanumeric characters.
122
- */
123
- // TODO(charlie): There's duplication between this file and key-configs.js.
124
- // We should clean it up by removing this file and requiring clients to use the
125
- // `id` field on the key configurations.
126
- var Keys = /*#__PURE__*/function (Keys) {
127
- Keys["PLUS"] = "PLUS";
128
- Keys["MINUS"] = "MINUS";
129
- Keys["NEGATIVE"] = "NEGATIVE";
130
- Keys["TIMES"] = "TIMES";
131
- Keys["DIVIDE"] = "DIVIDE";
132
- Keys["DECIMAL"] = "DECIMAL";
133
- Keys["PERIOD"] = "PERIOD";
134
- Keys["PERCENT"] = "PERCENT";
135
- Keys["CDOT"] = "CDOT";
136
- Keys["EQUAL"] = "EQUAL";
137
- Keys["NEQ"] = "NEQ";
138
- Keys["GT"] = "GT";
139
- Keys["LT"] = "LT";
140
- Keys["GEQ"] = "GEQ";
141
- Keys["LEQ"] = "LEQ";
142
- Keys["FRAC_INCLUSIVE"] = "FRAC_INCLUSIVE";
143
- Keys["FRAC_EXCLUSIVE"] = "FRAC_EXCLUSIVE";
144
- Keys["FRAC"] = "FRAC";
145
- Keys["EXP"] = "EXP";
146
- Keys["EXP_2"] = "EXP_2";
147
- Keys["EXP_3"] = "EXP_3";
148
- Keys["SQRT"] = "SQRT";
149
- Keys["CUBE_ROOT"] = "CUBE_ROOT";
150
- Keys["RADICAL"] = "RADICAL";
151
- Keys["LEFT_PAREN"] = "LEFT_PAREN";
152
- Keys["RIGHT_PAREN"] = "RIGHT_PAREN";
153
- Keys["LN"] = "LN";
154
- Keys["LOG"] = "LOG";
155
- Keys["LOG_N"] = "LOG_N";
156
- Keys["SIN"] = "SIN";
157
- Keys["COS"] = "COS";
158
- Keys["TAN"] = "TAN";
159
- Keys["PI"] = "PI";
160
- Keys["THETA"] = "THETA";
161
- Keys["UP"] = "UP";
162
- Keys["RIGHT"] = "RIGHT";
163
- Keys["DOWN"] = "DOWN";
164
- Keys["LEFT"] = "LEFT";
165
- Keys["BACKSPACE"] = "BACKSPACE";
166
- Keys["DISMISS"] = "DISMISS";
167
- Keys["JUMP_OUT_PARENTHESES"] = "JUMP_OUT_PARENTHESES";
168
- Keys["JUMP_OUT_EXPONENT"] = "JUMP_OUT_EXPONENT";
169
- Keys["JUMP_OUT_BASE"] = "JUMP_OUT_BASE";
170
- Keys["JUMP_INTO_NUMERATOR"] = "JUMP_INTO_NUMERATOR";
171
- Keys["JUMP_OUT_NUMERATOR"] = "JUMP_OUT_NUMERATOR";
172
- Keys["JUMP_OUT_DENOMINATOR"] = "JUMP_OUT_DENOMINATOR";
173
- Keys["NOOP"] = "NOOP";
174
- return Keys;
175
- }(Keys || {}); // mobile native only
176
-
177
119
  class Text extends React__namespace.Component {
178
120
  render() {
179
121
  const {
@@ -468,16 +410,23 @@ let KeypadType = /*#__PURE__*/function (KeypadType) {
468
410
  KeypadType["EXPRESSION"] = "EXPRESSION";
469
411
  return KeypadType;
470
412
  }({});
471
- let KeyType = /*#__PURE__*/function (KeyType) {
472
- KeyType["EMPTY"] = "EMPTY";
473
- KeyType["VALUE"] = "VALUE";
474
- KeyType["OPERATOR"] = "OPERATOR";
475
- KeyType["INPUT_NAVIGATION"] = "INPUT_NAVIGATION";
476
- KeyType["KEYPAD_NAVIGATION"] = "KEYPAD_NAVIGATION";
477
- KeyType["MANY"] = "MANY";
478
- KeyType["ECHO"] = "ECHO";
479
- return KeyType;
480
- }({});
413
+ const KeyTypes = ["EMPTY",
414
+ // For numerals, variables, and any other characters that themselves
415
+ // compose 'values'.
416
+ "VALUE",
417
+ // For buttons that insert or adjust math in an input.
418
+ "OPERATOR",
419
+ // For buttons that move the cursor in an input (including via
420
+ // deletion).
421
+ "INPUT_NAVIGATION",
422
+ // For buttons that modify the broader keypad state (e.g., by changing
423
+ // the visible pane).
424
+ "KEYPAD_NAVIGATION",
425
+ // For buttons that house multiple buttons and have no action
426
+ // themselves.
427
+ "MANY",
428
+ // For the echo animation that appears on press.
429
+ "ECHO"];
481
430
  let DeviceOrientation = /*#__PURE__*/function (DeviceOrientation) {
482
431
  DeviceOrientation["LANDSCAPE"] = "LANDSCAPE";
483
432
  DeviceOrientation["PORTRAIT"] = "PORTRAIT";
@@ -530,125 +479,209 @@ let EchoAnimationType = /*#__PURE__*/function (EchoAnimationType) {
530
479
  // listed here. Much of the Arab world uses U+066C.
531
480
  const decimalSeparator = i18n.getDecimalSeparator() === "," ? DecimalSeparator.COMMA : DecimalSeparator.PERIOD;
532
481
 
533
- // Keeping `window` in place for test suite and GitHub Pages.
534
- // If it does not exist, fall back to CommonJS require. - jsatk
482
+ var MQ = MathQuill__default["default"].getInterface(2);
535
483
 
536
- const decimalSymbol = decimalSeparator === DecimalSeparator.COMMA ? "," : ".";
537
484
  var ActionType = /*#__PURE__*/function (ActionType) {
538
485
  ActionType["WRITE"] = "write";
539
486
  ActionType["CMD"] = "cmd";
540
487
  ActionType["KEYSTROKE"] = "keystroke";
541
488
  ActionType[ActionType["MQ_END"] = 0] = "MQ_END";
542
489
  return ActionType;
543
- }(ActionType || {}); // A mapping from keys that can be pressed on a keypad to the way in which
544
- // MathQuill should modify its input in response to that key-press. Any keys
545
- // that do not provide explicit actions (like the numeral keys) will merely
546
- // write their contents to MathQuill.
547
- const KeyActions = {
548
- [Keys.PLUS]: {
549
- str: "+",
550
- fn: ActionType.WRITE
551
- },
552
- [Keys.MINUS]: {
553
- str: "-",
554
- fn: ActionType.WRITE
555
- },
556
- [Keys.NEGATIVE]: {
557
- str: "-",
558
- fn: ActionType.WRITE
559
- },
560
- [Keys.TIMES]: {
561
- str: "\\times",
562
- fn: ActionType.WRITE
563
- },
564
- [Keys.DIVIDE]: {
565
- str: "\\div",
566
- fn: ActionType.WRITE
567
- },
568
- [Keys.DECIMAL]: {
569
- str: decimalSymbol,
570
- fn: ActionType.WRITE
571
- },
572
- [Keys.EQUAL]: {
573
- str: "=",
574
- fn: ActionType.WRITE
575
- },
576
- [Keys.NEQ]: {
577
- str: "\\neq",
578
- fn: ActionType.WRITE
579
- },
580
- [Keys.CDOT]: {
581
- str: "\\cdot",
582
- fn: ActionType.WRITE
583
- },
584
- [Keys.PERCENT]: {
585
- str: "%",
586
- fn: ActionType.WRITE
587
- },
588
- [Keys.LEFT_PAREN]: {
589
- str: "(",
590
- fn: ActionType.CMD
591
- },
592
- [Keys.RIGHT_PAREN]: {
593
- str: ")",
594
- fn: ActionType.CMD
595
- },
596
- [Keys.SQRT]: {
597
- str: "sqrt",
598
- fn: ActionType.CMD
599
- },
600
- [Keys.PI]: {
601
- str: "pi",
602
- fn: ActionType.CMD
603
- },
604
- [Keys.THETA]: {
605
- str: "theta",
606
- fn: ActionType.CMD
607
- },
608
- [Keys.RADICAL]: {
609
- str: "nthroot",
610
- fn: ActionType.CMD
611
- },
612
- [Keys.LT]: {
613
- str: "<",
614
- fn: ActionType.WRITE
615
- },
616
- [Keys.LEQ]: {
617
- str: "\\leq",
618
- fn: ActionType.WRITE
619
- },
620
- [Keys.GT]: {
621
- str: ">",
622
- fn: ActionType.WRITE
623
- },
624
- [Keys.GEQ]: {
625
- str: "\\geq",
626
- fn: ActionType.WRITE
627
- },
628
- [Keys.UP]: {
629
- str: "Up",
630
- fn: ActionType.KEYSTROKE
631
- },
632
- [Keys.DOWN]: {
633
- str: "Down",
634
- fn: ActionType.KEYSTROKE
635
- },
490
+ }(ActionType || {});
491
+ const decimalSymbol = decimalSeparator === DecimalSeparator.COMMA ? "," : ".";
492
+ function buildGenericCallback(str) {
493
+ let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ActionType.WRITE;
494
+ return function (mathQuill) {
495
+ switch (type) {
496
+ case ActionType.WRITE:
497
+ {
498
+ mathQuill.write(str);
499
+ return;
500
+ }
501
+ case ActionType.CMD:
502
+ {
503
+ mathQuill.cmd(str);
504
+ return;
505
+ }
506
+ case ActionType.KEYSTROKE:
507
+ {
508
+ mathQuill.keystroke(str);
509
+ return;
510
+ }
511
+ }
512
+ };
513
+ }
514
+ const keyToMathquillMap = {
515
+ CDOT: buildGenericCallback("\\cdot"),
516
+ COS: buildGenericCallback("cos"),
517
+ DECIMAL: buildGenericCallback(decimalSymbol),
518
+ DIVIDE: buildGenericCallback("\\div"),
519
+ EQUAL: buildGenericCallback("="),
520
+ EXP: buildGenericCallback("^"),
521
+ EXP_2: buildGenericCallback("^2"),
522
+ EXP_3: buildGenericCallback("^3"),
523
+ GEQ: buildGenericCallback("\\geq"),
524
+ GT: buildGenericCallback(">"),
525
+ LEQ: buildGenericCallback("\\leq"),
526
+ LN: buildGenericCallback("\\ln"),
527
+ LOG: buildGenericCallback("\\log"),
528
+ LT: buildGenericCallback("<"),
529
+ MINUS: buildGenericCallback("-"),
530
+ NEGATIVE: buildGenericCallback("-"),
531
+ NEQ: buildGenericCallback("\\neq"),
532
+ PERCENT: buildGenericCallback("%"),
533
+ PERIOD: buildGenericCallback("."),
534
+ PLUS: buildGenericCallback("+"),
535
+ SIN: buildGenericCallback("sin"),
536
+ TAN: buildGenericCallback("tan"),
537
+ TIMES: buildGenericCallback("\\times"),
636
538
  // The `FRAC_EXCLUSIVE` variant is handled manually, since we may need to do
637
539
  // some additional navigation depending on the cursor position.
638
- [Keys.FRAC_INCLUSIVE]: {
639
- str: "/",
640
- fn: ActionType.CMD
641
- }
642
- };
643
- const NormalCommands = {
644
- [Keys.LOG]: "log",
645
- [Keys.LN]: "ln",
646
- [Keys.SIN]: "sin",
647
- [Keys.COS]: "cos",
648
- [Keys.TAN]: "tan"
540
+ FRAC_INCLUSIVE: buildGenericCallback("/", ActionType.CMD),
541
+ LEFT_PAREN: buildGenericCallback("(", ActionType.CMD),
542
+ RIGHT_PAREN: buildGenericCallback(")", ActionType.CMD),
543
+ SQRT: buildGenericCallback("sqrt", ActionType.CMD),
544
+ PHI: buildGenericCallback("\\phi", ActionType.CMD),
545
+ PI: buildGenericCallback("pi", ActionType.CMD),
546
+ THETA: buildGenericCallback("theta", ActionType.CMD),
547
+ RADICAL: buildGenericCallback("nthroot", ActionType.CMD),
548
+ UP: buildGenericCallback("Up", ActionType.KEYSTROKE),
549
+ DOWN: buildGenericCallback("Down", ActionType.KEYSTROKE),
550
+ CUBE_ROOT: mathQuill => {
551
+ mathQuill.write("\\sqrt[3]{}");
552
+ mathQuill.keystroke("Left"); // under the root
553
+ },
554
+
555
+ FRAC_EXCLUSIVE: mathQuill => {
556
+ const cursor = mathQuill.__controller.cursor;
557
+ // If there's nothing to the left of the cursor, then we want to
558
+ // leave the cursor to the left of the fraction after creating it.
559
+ const shouldNavigateLeft = cursor[MQ.L] === ActionType.MQ_END;
560
+ mathQuill.cmd("\\frac");
561
+ if (shouldNavigateLeft) {
562
+ mathQuill.keystroke("Left");
563
+ }
564
+ },
565
+ LOG_B: mathQuill => {
566
+ mathQuill.typedText("log_");
567
+ mathQuill.keystroke("Right");
568
+ mathQuill.typedText("(");
569
+ mathQuill.keystroke("Left");
570
+ mathQuill.keystroke("Left");
571
+ },
572
+ LOG_N: mathQuill => {
573
+ mathQuill.write("log_{ }\\left(\\right)");
574
+ mathQuill.keystroke("Left"); // into parentheses
575
+ mathQuill.keystroke("Left"); // out of parentheses
576
+ mathQuill.keystroke("Left"); // into index
577
+ },
578
+
579
+ NTHROOT3: mathQuill => {
580
+ mathQuill.typedText("nthroot3");
581
+ mathQuill.keystroke("Right");
582
+ },
583
+ POW: mathQuill => {
584
+ const contents = mathQuill.latex();
585
+ mathQuill.typedText("^");
586
+
587
+ // If the input hasn't changed (for example, if we're
588
+ // attempting to add an exponent on an empty input or an empty
589
+ // denominator), insert our own "a^b"
590
+ if (mathQuill.latex() === contents) {
591
+ mathQuill.typedText("a^b");
592
+ }
593
+ },
594
+ // These need to be overwritten by the consumer
595
+ // if they're going to be used
596
+ FRAC: () => {},
597
+ RIGHT: () => {},
598
+ LEFT: () => {},
599
+ BACKSPACE: () => {},
600
+ DISMISS: () => {},
601
+ JUMP_OUT_PARENTHESES: () => {},
602
+ JUMP_OUT_EXPONENT: () => {},
603
+ JUMP_OUT_BASE: () => {},
604
+ JUMP_INTO_NUMERATOR: () => {},
605
+ JUMP_OUT_NUMERATOR: () => {},
606
+ JUMP_OUT_DENOMINATOR: () => {},
607
+ NOOP: () => {},
608
+ MANY: () => {},
609
+ NUM_0: buildGenericCallback("0"),
610
+ NUM_1: buildGenericCallback("1"),
611
+ NUM_2: buildGenericCallback("2"),
612
+ NUM_3: buildGenericCallback("3"),
613
+ NUM_4: buildGenericCallback("4"),
614
+ NUM_5: buildGenericCallback("5"),
615
+ NUM_6: buildGenericCallback("6"),
616
+ NUM_7: buildGenericCallback("7"),
617
+ NUM_8: buildGenericCallback("8"),
618
+ NUM_9: buildGenericCallback("9"),
619
+ a: buildGenericCallback("a"),
620
+ b: buildGenericCallback("b"),
621
+ c: buildGenericCallback("c"),
622
+ d: buildGenericCallback("d"),
623
+ e: buildGenericCallback("e"),
624
+ f: buildGenericCallback("f"),
625
+ g: buildGenericCallback("g"),
626
+ h: buildGenericCallback("h"),
627
+ i: buildGenericCallback("i"),
628
+ j: buildGenericCallback("j"),
629
+ k: buildGenericCallback("k"),
630
+ l: buildGenericCallback("l"),
631
+ m: buildGenericCallback("m"),
632
+ n: buildGenericCallback("n"),
633
+ o: buildGenericCallback("o"),
634
+ p: buildGenericCallback("p"),
635
+ q: buildGenericCallback("q"),
636
+ r: buildGenericCallback("r"),
637
+ s: buildGenericCallback("s"),
638
+ t: buildGenericCallback("t"),
639
+ u: buildGenericCallback("u"),
640
+ v: buildGenericCallback("v"),
641
+ w: buildGenericCallback("w"),
642
+ x: buildGenericCallback("x"),
643
+ y: buildGenericCallback("y"),
644
+ z: buildGenericCallback("z"),
645
+ A: buildGenericCallback("A"),
646
+ B: buildGenericCallback("B"),
647
+ C: buildGenericCallback("C"),
648
+ D: buildGenericCallback("D"),
649
+ E: buildGenericCallback("E"),
650
+ F: buildGenericCallback("F"),
651
+ G: buildGenericCallback("G"),
652
+ H: buildGenericCallback("H"),
653
+ I: buildGenericCallback("I"),
654
+ J: buildGenericCallback("J"),
655
+ K: buildGenericCallback("K"),
656
+ L: buildGenericCallback("L"),
657
+ M: buildGenericCallback("M"),
658
+ N: buildGenericCallback("N"),
659
+ O: buildGenericCallback("O"),
660
+ P: buildGenericCallback("P"),
661
+ Q: buildGenericCallback("Q"),
662
+ R: buildGenericCallback("R"),
663
+ S: buildGenericCallback("S"),
664
+ T: buildGenericCallback("T"),
665
+ U: buildGenericCallback("U"),
666
+ V: buildGenericCallback("V"),
667
+ W: buildGenericCallback("W"),
668
+ X: buildGenericCallback("X"),
669
+ Y: buildGenericCallback("Y"),
670
+ Z: buildGenericCallback("Z")
649
671
  };
650
- const ArithmeticOperators = ["+", "-", "\\cdot", "\\times", "\\div"];
651
- const EqualityOperators = ["=", "\\neq", "<", "\\leq", ">", "\\geq"];
672
+
673
+ let MathFieldActionType = /*#__PURE__*/function (MathFieldActionType) {
674
+ MathFieldActionType["WRITE"] = "write";
675
+ MathFieldActionType["CMD"] = "cmd";
676
+ MathFieldActionType["KEYSTROKE"] = "keystroke";
677
+ MathFieldActionType[MathFieldActionType["MQ_END"] = 0] = "MQ_END";
678
+ return MathFieldActionType;
679
+ }({});
680
+
681
+ // The MathQuill MathField Cursor
682
+ // it's not part of the public API for MathQuill,
683
+ // we reach into the internals to get it
684
+
652
685
  const Numerals = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
653
686
  const GreekLetters = ["\\theta", "\\pi"];
654
687
  const Letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
@@ -656,25 +689,625 @@ const Letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"
656
689
  // We only consider numerals, variables, and Greek Letters to be proper
657
690
  // leaf nodes.
658
691
  const ValidLeaves = [...Numerals, ...GreekLetters, ...Letters.map(letter => letter.toLowerCase()), ...Letters.map(letter => letter.toUpperCase())];
692
+ function getCursor(mathField) {
693
+ return mathField.__controller.cursor;
694
+ }
695
+ function isFraction(node) {
696
+ return node.jQ && node.jQ.hasClass("mq-fraction");
697
+ }
698
+ function isNumerator(node) {
699
+ return node.jQ && node.jQ.hasClass("mq-numerator");
700
+ }
701
+ function isDenominator(node) {
702
+ return node.jQ && node.jQ.hasClass("mq-denominator");
703
+ }
704
+ function isSubScript(node) {
705
+ // NOTE(charlie): MyScript has a structure whereby its superscripts seem
706
+ // to be represented as a parent node with 'mq-sup-only' containing a
707
+ // single child with 'mq-sup'.
708
+ return node.jQ && (node.jQ.hasClass("mq-sub-only") || node.jQ.hasClass("mq-sub"));
709
+ }
710
+ function isSuperScript(node) {
711
+ // NOTE(charlie): MyScript has a structure whereby its superscripts seem
712
+ // to be represented as a parent node with 'mq-sup-only' containing a
713
+ // single child with 'mq-sup'.
714
+ return node.jQ && (node.jQ.hasClass("mq-sup-only") || node.jQ.hasClass("mq-sup"));
715
+ }
716
+ function isParens(node) {
717
+ return node && node.ctrlSeq === "\\left(";
718
+ }
719
+ function isLeaf(node) {
720
+ return node && node.ctrlSeq && ValidLeaves.includes(node.ctrlSeq.trim());
721
+ }
722
+ function isSquareRoot(node) {
723
+ return node.blocks && node.blocks[0].jQ && node.blocks[0].jQ.hasClass("mq-sqrt-stem");
724
+ }
725
+ function isNthRoot(node) {
726
+ return node.blocks && node.blocks[0].jQ && node.blocks[0].jQ.hasClass("mq-nthroot");
727
+ }
728
+ function isNthRootIndex(node) {
729
+ return node.jQ && node.jQ.hasClass("mq-nthroot");
730
+ }
731
+ function isInsideLogIndex(cursor) {
732
+ const grandparent = cursor.parent.parent;
733
+ if (grandparent && grandparent.jQ.hasClass("mq-supsub")) {
734
+ const command = maybeFindCommandBeforeParens(grandparent);
735
+ if (command && command.name === "\\log") {
736
+ return true;
737
+ }
738
+ }
739
+ return false;
740
+ }
741
+ function isInsideEmptyNode(cursor) {
742
+ return cursor[MQ.L] === MathFieldActionType.MQ_END && cursor[MQ.R] === MathFieldActionType.MQ_END;
743
+ }
744
+ function selectNode(node, cursor) {
745
+ cursor.insLeftOf(node);
746
+ cursor.startSelection();
747
+ cursor.insRightOf(node);
748
+ cursor.select();
749
+ cursor.endSelection();
750
+ }
751
+
752
+ /**
753
+ * Return the start node, end node, and full name of the command of which
754
+ * the initial node is a part, or `null` if the node is not part of a
755
+ * command.
756
+ *
757
+ * @param {node} initialNode - the node to included as part of the command
758
+ * @returns {null|object} - `null` or an object containing the start node
759
+ * (`startNode`), end node (`endNode`), and full
760
+ * name (`name`) of the command
761
+ */
762
+ function maybeFindCommand(initialNode) {
763
+ if (!initialNode) {
764
+ return null;
765
+ }
766
+
767
+ // MathQuill stores commands as separate characters so that
768
+ // users can delete commands one character at a time. We iterate over
769
+ // the nodes from right to left until we hit a sequence starting with a
770
+ // '\\', which signifies the start of a command; then we iterate from
771
+ // left to right until we hit a '\\left(', which signifies the end of a
772
+ // command. If we encounter any character that doesn't belong in a
773
+ // command, we return null. We match a single character at a time.
774
+ // Ex) ['\\l', 'o', 'g ', '\\left(', ...]
775
+ const commandCharRegex = /^[a-z]$/;
776
+ const commandStartRegex = /^\\[a-z]$/;
777
+ const commandEndSeq = "\\left(";
778
+
779
+ // Note: We allowlist the set of valid commands, since relying solely on
780
+ // a command being prefixed with a backslash leads to undesired
781
+ // behavior. For example, Greek symbols, left parentheses, and square
782
+ // roots all get treated as commands.
783
+ const validCommands = ["\\log", "\\ln", "\\cos", "\\sin", "\\tan"];
784
+ let name = "";
785
+ let startNode;
786
+ let endNode;
787
+
788
+ // Collect the portion of the command from the current node, leftwards
789
+ // until the start of the command.
790
+ let node = initialNode;
791
+ while (node !== 0) {
792
+ const ctrlSeq = node.ctrlSeq.trim();
793
+ if (commandCharRegex.test(ctrlSeq)) {
794
+ name = ctrlSeq + name;
795
+ } else if (commandStartRegex.test(ctrlSeq)) {
796
+ name = ctrlSeq + name;
797
+ startNode = node;
798
+ break;
799
+ } else {
800
+ break;
801
+ }
802
+ node = node[MQ.L];
803
+ }
804
+
805
+ // If we hit the start of a command, then grab the rest of it by
806
+ // iterating rightwards to compute the full name of the command, along
807
+ // with its terminal node.
808
+ if (startNode) {
809
+ // Next, iterate from the start to the right.
810
+ node = initialNode[MQ.R];
811
+ while (node !== 0) {
812
+ const ctrlSeq = node.ctrlSeq.trim();
813
+ if (commandCharRegex.test(ctrlSeq)) {
814
+ // If we have a single character, add it to the command
815
+ // name.
816
+ name = name + ctrlSeq;
817
+ } else if (ctrlSeq === commandEndSeq) {
818
+ // If we hit the command end delimiter (the left
819
+ // parentheses surrounding its arguments), stop.
820
+ endNode = node;
821
+ break;
822
+ }
823
+ node = node[MQ.R];
824
+ }
825
+ if (validCommands.includes(name)) {
826
+ return {
827
+ name,
828
+ startNode,
829
+ endNode
830
+ };
831
+ } else {
832
+ return null;
833
+ }
834
+ } else {
835
+ return null;
836
+ }
837
+ }
838
+
839
+ /**
840
+ * Return the start node, end node, and full name of the command to the left
841
+ * of `\\left(`, or `null` if there is no command.
842
+ *
843
+ * @param {node} leftParenNode - node where .ctrlSeq == `\\left(`
844
+ * @returns {null|object} - `null` or an object containing the start node
845
+ * (`startNode`), end node (`endNode`), and full
846
+ * name (`name`) of the command
847
+ */
848
+ function maybeFindCommandBeforeParens(leftParenNode) {
849
+ return maybeFindCommand(leftParenNode[MQ.L]);
850
+ }
851
+ function contextForCursor(cursor) {
852
+ // First, try to find any fraction to the right, unimpeded.
853
+ let visitor = cursor;
854
+ while (visitor[MQ.R] !== MathFieldActionType.MQ_END) {
855
+ if (isFraction(visitor[MQ.R])) {
856
+ return CursorContext.BEFORE_FRACTION;
857
+ } else if (!isLeaf(visitor[MQ.R])) {
858
+ break;
859
+ }
860
+ visitor = visitor[MQ.R];
861
+ }
862
+
863
+ // If that didn't work, check if the parent or grandparent is a special
864
+ // context, so that we can jump outwards.
865
+ if (isParens(cursor.parent && cursor.parent.parent)) {
866
+ return CursorContext.IN_PARENS;
867
+ } else if (isNumerator(cursor.parent)) {
868
+ return CursorContext.IN_NUMERATOR;
869
+ } else if (isDenominator(cursor.parent)) {
870
+ return CursorContext.IN_DENOMINATOR;
871
+ } else if (isSubScript(cursor.parent)) {
872
+ return CursorContext.IN_SUB_SCRIPT;
873
+ } else if (isSuperScript(cursor.parent)) {
874
+ return CursorContext.IN_SUPER_SCRIPT;
875
+ } else {
876
+ return CursorContext.NONE;
877
+ }
878
+ }
879
+
880
+ function handleLeftArrow(mathField, cursor) {
881
+ // If we're inside a function, and just after the left parentheses, we
882
+ // need to skip the entire function name, rather than move the cursor
883
+ // inside of it. For example, when hitting left from within the
884
+ // parentheses in `cos()`, we want to place the cursor to the left of
885
+ // the entire expression, rather than between the `s` and the left
886
+ // parenthesis.
887
+ // From the cursor's perspective, this requires that our left node is
888
+ // the ActionType.MQ_END node, that our grandparent is the left parenthesis, and
889
+ // the nodes to the left of our grandparent comprise a valid function
890
+ // name.
891
+ if (cursor[MQ.L] === MathFieldActionType.MQ_END) {
892
+ const parent = cursor.parent;
893
+ const grandparent = parent.parent;
894
+ if (grandparent.ctrlSeq === "\\left(") {
895
+ const command = maybeFindCommandBeforeParens(grandparent);
896
+ if (command) {
897
+ cursor.insLeftOf(command.startNode);
898
+ return;
899
+ }
900
+ }
901
+ }
902
+
903
+ // Otherwise, we default to the standard MathQull left behavior.
904
+ mathField.keystroke("Left");
905
+ }
906
+ function handleRightArrow(mathField, cursor) {
907
+ const command = maybeFindCommand(cursor[MQ.R]);
908
+ if (command) {
909
+ // Similarly, if a function is to our right, then we need to place
910
+ // the cursor at the start of its parenthetical content, which is
911
+ // done by putting it to the left of ites parentheses and then
912
+ // moving right once.
913
+ cursor.insLeftOf(command.endNode);
914
+ mathField.keystroke("Right");
915
+ } else {
916
+ // Otherwise, we default to the standard MathQull right behavior.
917
+ mathField.keystroke("Right");
918
+ }
919
+ }
920
+ function handleArrow(mathField, key) {
921
+ const cursor = getCursor(mathField);
922
+ if (key === "LEFT") {
923
+ handleLeftArrow(mathField, cursor);
924
+ } else if (key === "RIGHT") {
925
+ handleRightArrow(mathField, cursor);
926
+ }
927
+ }
928
+
929
+ function handleBackspaceInNthRoot(mathField, cursor) {
930
+ const isAtLeftEnd = cursor[MQ.L] === MathFieldActionType.MQ_END;
931
+ const isRootEmpty = isInsideEmptyNode(cursor.parent.parent.blocks[0].ends);
932
+ if (isAtLeftEnd) {
933
+ selectNode(cursor.parent.parent, cursor);
934
+ if (isRootEmpty) {
935
+ mathField.keystroke("Backspace");
936
+ }
937
+ } else {
938
+ mathField.keystroke("Backspace");
939
+ }
940
+ }
941
+ function handleBackspaceInRootIndex(mathField, cursor) {
942
+ if (isInsideEmptyNode(cursor)) {
943
+ // When deleting the index in a nthroot, we change from the nthroot
944
+ // to a sqrt, e.g. \sqrt[|]{35x-5} => |\sqrt{35x-5}. If there's no
945
+ // content under the root, then we delete the whole thing.
946
+
947
+ const grandparent = cursor.parent.parent;
948
+ const latex = grandparent.latex();
949
+ const reinsertionPoint = grandparent[MQ.L];
950
+ selectNode(grandparent, cursor);
951
+ const rootIsEmpty = grandparent.blocks[1].jQ.text() === "";
952
+ if (rootIsEmpty) {
953
+ // If there is not content under the root then simply delete
954
+ // the whole thing.
955
+ mathField.keystroke("Backspace");
956
+ } else {
957
+ // Replace the nthroot with a sqrt if there was content under
958
+ // the root.
959
+
960
+ // Start by deleting the selection.
961
+ mathField.keystroke("Backspace");
962
+
963
+ // Replace the nth-root with a sqrt.
964
+ mathField.write(latex.replace(/^\\sqrt\[\]/, "\\sqrt"));
965
+
966
+ // Adjust the cursor to be to the left the sqrt.
967
+ if (reinsertionPoint === MathFieldActionType.MQ_END) {
968
+ mathField.moveToDirEnd(MQ.L);
969
+ } else {
970
+ cursor.insRightOf(reinsertionPoint);
971
+ }
972
+ }
973
+ } else {
974
+ if (cursor[MQ.L] !== MathFieldActionType.MQ_END) {
975
+ // If the cursor is not at the leftmost position inside the
976
+ // root's index, delete a character.
977
+ mathField.keystroke("Backspace");
978
+ }
979
+ }
980
+ }
981
+ function handleBackspaceInLogIndex(mathField, cursor) {
982
+ if (isInsideEmptyNode(cursor)) {
983
+ const grandparent = cursor.parent.parent;
984
+ const command = maybeFindCommandBeforeParens(grandparent);
985
+ cursor.insLeftOf(command === null || command === void 0 ? void 0 : command.startNode);
986
+ cursor.startSelection();
987
+ if (grandparent[MQ.R] !== MathFieldActionType.MQ_END) {
988
+ cursor.insRightOf(grandparent[MQ.R]);
989
+ } else {
990
+ cursor.insRightOf(grandparent);
991
+ }
992
+ cursor.select();
993
+ cursor.endSelection();
994
+ const isLogBodyEmpty = grandparent[MQ.R].contentjQ.text() === "";
995
+ if (isLogBodyEmpty) {
996
+ // If there's no content inside the log's parens then delete the
997
+ // whole thing.
998
+ mathField.keystroke("Backspace");
999
+ }
1000
+ } else {
1001
+ mathField.keystroke("Backspace");
1002
+ }
1003
+ }
1004
+ function handleBackspaceOutsideParens(cursor) {
1005
+ // In this case the node with '\\left(' for its ctrlSeq
1006
+ // is the parent of the expression contained within the
1007
+ // parentheses.
1008
+ //
1009
+ // Handle selecting an expression before deleting:
1010
+ // (x+1)| => |(x+1)|
1011
+ // \log(x+1)| => |\log(x+1)|
1012
+
1013
+ const leftNode = cursor[MQ.L];
1014
+ const rightNode = cursor[MQ.R];
1015
+ const command = maybeFindCommandBeforeParens(leftNode);
1016
+ if (command && command.startNode) {
1017
+ // There's a command before the parens so we select it as well as
1018
+ // the parens.
1019
+ cursor.insLeftOf(command.startNode);
1020
+ cursor.startSelection();
1021
+ if (rightNode === MathFieldActionType.MQ_END) {
1022
+ cursor.insAtRightEnd(cursor.parent);
1023
+ } else {
1024
+ cursor.insLeftOf(rightNode);
1025
+ }
1026
+ cursor.select();
1027
+ cursor.endSelection();
1028
+ } else {
1029
+ cursor.startSelection();
1030
+ cursor.insLeftOf(leftNode); // left of \\left(
1031
+ cursor.select();
1032
+ cursor.endSelection();
1033
+ }
1034
+ }
1035
+ function handleBackspaceInsideParens(mathField, cursor) {
1036
+ // Handle situations when the cursor is inside parens or a
1037
+ // command that uses parens, e.g. \log() or \tan()
1038
+ //
1039
+ // MathQuill represents log(x+1) in roughly the following way
1040
+ // [l, o, g, \\left[parent:[x, +, 1]]]
1041
+ //
1042
+ // If the cursor is inside the parentheses it's next to one of:
1043
+ // x, +, or 1. This makes sub_sub_expr its parent and sub_expr
1044
+ // it's parent.
1045
+ //
1046
+ // Interestingly parent doesn't have any nodes to the left or
1047
+ // right of it (even though the corresponding DOM node has
1048
+ // ( and ) characters on either side.
1049
+ //
1050
+ // The grandparent's ctrlSeq is `\\left(`. The `\\right)` isn't
1051
+ // stored anywhere. NOTE(kevinb): I believe this is because
1052
+ // MathQuill knows what the close paren should be and does the
1053
+ // right thing at render time.
1054
+ //
1055
+ // This conditional branch handles the following cases:
1056
+ // - \log(x+1|) => \log(x+|)
1057
+ // - \log(|x+1) => |\log(x+1)|
1058
+ // - \log(|) => |
1059
+
1060
+ if (cursor[MQ.L] !== MathFieldActionType.MQ_END) {
1061
+ // This command contains math and there's some math to
1062
+ // the left of the cursor that we should delete normally
1063
+ // before doing anything special.
1064
+ mathField.keystroke("Backspace");
1065
+ return;
1066
+ }
1067
+ const grandparent = cursor.parent.parent;
1068
+
1069
+ // If the cursors is inside the parens at the start but the command
1070
+ // has a subscript as is the case in log_n then move the cursor into
1071
+ // the subscript, e.g. \log_{5}(|x+1) => \log_{5|}(x+1)
1072
+
1073
+ if (grandparent[MQ.L].sub) {
1074
+ // if there is a subscript
1075
+ if (grandparent[MQ.L].sub.jQ.text()) {
1076
+ // and it contains text
1077
+ // move the cursor to the right end of the subscript
1078
+ cursor.insAtRightEnd(grandparent[MQ.L].sub);
1079
+ return;
1080
+ }
1081
+ }
1082
+
1083
+ // Determine if the parens are empty before we modify the
1084
+ // cursor's position.
1085
+ const isEmpty = isInsideEmptyNode(cursor);
1086
+
1087
+ // Insert the cursor to the left of the command if there is one
1088
+ // or before the '\\left(` if there isn't
1089
+ const command = maybeFindCommandBeforeParens(grandparent);
1090
+ cursor.insLeftOf(command && command.startNode || grandparent);
1091
+ cursor.startSelection();
1092
+ cursor.insRightOf(grandparent);
1093
+ cursor.select();
1094
+ cursor.endSelection();
1095
+
1096
+ // Delete the selection, but only if the parens were empty to
1097
+ // begin with.
1098
+ if (isEmpty) {
1099
+ mathField.keystroke("Backspace");
1100
+ }
1101
+ }
1102
+ function handleBackspaceAfterLigaturedSymbol(mathField) {
1103
+ mathField.keystroke("Backspace");
1104
+ mathField.keystroke("Backspace");
1105
+ }
1106
+
1107
+ /**
1108
+ * Selects and deletes part of the expression based on the cursor location.
1109
+ * See inline comments for precise behavior of different cases.
1110
+ */
1111
+ function handleBackspace(mathField) {
1112
+ const cursor = getCursor(mathField);
1113
+ if (!cursor.selection) {
1114
+ const parent = cursor.parent;
1115
+ const grandparent = parent.parent;
1116
+ const leftNode = cursor[MQ.L];
1117
+ if (isFraction(leftNode)) {
1118
+ selectNode(leftNode, cursor);
1119
+ } else if (isSquareRoot(leftNode)) {
1120
+ selectNode(leftNode, cursor);
1121
+ } else if (isNthRoot(leftNode)) {
1122
+ selectNode(leftNode, cursor);
1123
+ } else if (isNthRootIndex(parent)) {
1124
+ handleBackspaceInRootIndex(mathField, cursor);
1125
+ } else if (leftNode.ctrlSeq === "\\left(") {
1126
+ handleBackspaceOutsideParens(cursor);
1127
+ } else if (grandparent.ctrlSeq === "\\left(") {
1128
+ handleBackspaceInsideParens(mathField, cursor);
1129
+ } else if (isInsideLogIndex(cursor)) {
1130
+ handleBackspaceInLogIndex(mathField, cursor);
1131
+ } else if (leftNode.ctrlSeq === "\\ge " || leftNode.ctrlSeq === "\\le ") {
1132
+ handleBackspaceAfterLigaturedSymbol(mathField);
1133
+ } else if (isNthRoot(grandparent) && leftNode === MathFieldActionType.MQ_END) {
1134
+ handleBackspaceInNthRoot(mathField, cursor);
1135
+ } else {
1136
+ mathField.keystroke("Backspace");
1137
+ }
1138
+ } else {
1139
+ mathField.keystroke("Backspace");
1140
+ }
1141
+ }
1142
+
1143
+ const ArithmeticOperators = ["+", "-", "\\cdot", "\\times", "\\div"];
1144
+ const EqualityOperators = ["=", "\\neq", "<", "\\leq", ">", "\\geq"];
1145
+ function handleExponent(mathField, key) {
1146
+ const cursor = getCursor(mathField);
1147
+ // If there's an invalid operator preceding the cursor (anything that
1148
+ // knowingly cannot be raised to a power), add an empty set of
1149
+ // parentheses and apply the exponent to that.
1150
+ const invalidPrefixes = [...ArithmeticOperators, ...EqualityOperators];
1151
+ const precedingNode = cursor[MQ.L];
1152
+ const shouldPrefixWithParens = precedingNode === MathFieldActionType.MQ_END || invalidPrefixes.includes(precedingNode.ctrlSeq.trim());
1153
+ if (shouldPrefixWithParens) {
1154
+ mathField.write("\\left(\\right)");
1155
+ }
1156
+
1157
+ // Insert the appropriate exponent operator.
1158
+ switch (key) {
1159
+ case "EXP":
1160
+ mathField.cmd("^");
1161
+ break;
1162
+ case "EXP_2":
1163
+ case "EXP_3":
1164
+ mathField.write("^".concat(key === "EXP_2" ? 2 : 3));
1165
+
1166
+ // If we enter a square or a cube, we should leave the cursor
1167
+ // within the newly inserted parens, if they exist. This takes
1168
+ // exactly four left strokes, since the cursor by default would
1169
+ // end up to the right of the exponent.
1170
+ if (shouldPrefixWithParens) {
1171
+ mathField.keystroke("Left");
1172
+ mathField.keystroke("Left");
1173
+ mathField.keystroke("Left");
1174
+ mathField.keystroke("Left");
1175
+ }
1176
+ break;
1177
+ default:
1178
+ throw new Error("Invalid exponent key: ".concat(key));
1179
+ }
1180
+ }
1181
+
659
1182
  const KeysForJumpContext = {
660
- [CursorContext.IN_PARENS]: Keys.JUMP_OUT_PARENTHESES,
661
- [CursorContext.IN_SUPER_SCRIPT]: Keys.JUMP_OUT_EXPONENT,
662
- [CursorContext.IN_SUB_SCRIPT]: Keys.JUMP_OUT_BASE,
663
- [CursorContext.BEFORE_FRACTION]: Keys.JUMP_INTO_NUMERATOR,
664
- [CursorContext.IN_NUMERATOR]: Keys.JUMP_OUT_NUMERATOR,
665
- [CursorContext.IN_DENOMINATOR]: Keys.JUMP_OUT_DENOMINATOR
1183
+ [CursorContext.IN_PARENS]: "JUMP_OUT_PARENTHESES",
1184
+ [CursorContext.IN_SUPER_SCRIPT]: "JUMP_OUT_EXPONENT",
1185
+ [CursorContext.IN_SUB_SCRIPT]: "JUMP_OUT_BASE",
1186
+ [CursorContext.BEFORE_FRACTION]: "JUMP_INTO_NUMERATOR",
1187
+ [CursorContext.IN_NUMERATOR]: "JUMP_OUT_NUMERATOR",
1188
+ [CursorContext.IN_DENOMINATOR]: "JUMP_OUT_DENOMINATOR"
666
1189
  };
1190
+
1191
+ /**
1192
+ * Advances the cursor to the next logical position.
1193
+ */
1194
+ function handleJumpOut(mathField, key) {
1195
+ const cursor = getCursor(mathField);
1196
+ const context = contextForCursor(cursor);
1197
+
1198
+ // Validate that the current cursor context matches the key's intent.
1199
+ if (KeysForJumpContext[context] !== key) {
1200
+ // If we don't have a valid cursor context, yet the user was able
1201
+ // to trigger a jump-out key, that's a broken invariant. Rather
1202
+ // than throw an error (which would kick the user out of the
1203
+ // exercise), we do nothing, as a fallback strategy. The user can
1204
+ // still move the cursor manually.
1205
+ return;
1206
+ }
1207
+ switch (context) {
1208
+ case CursorContext.IN_PARENS:
1209
+ // Insert at the end of the parentheses, and then navigate right
1210
+ // once more to get 'beyond' the parentheses.
1211
+ cursor.insRightOf(cursor.parent.parent);
1212
+ break;
1213
+ case CursorContext.BEFORE_FRACTION:
1214
+ // Find the nearest fraction to the right of the cursor.
1215
+ let fractionNode;
1216
+ let visitor = cursor;
1217
+ while (visitor[MQ.R] !== MathFieldActionType.MQ_END) {
1218
+ if (isFraction(visitor[MQ.R])) {
1219
+ fractionNode = visitor[MQ.R];
1220
+ }
1221
+ visitor = visitor[MQ.R];
1222
+ }
1223
+
1224
+ // Jump into it!
1225
+ cursor.insLeftOf(fractionNode);
1226
+ mathField.keystroke("Right");
1227
+ break;
1228
+ case CursorContext.IN_NUMERATOR:
1229
+ // HACK(charlie): I can't find a better way to do this. The goal
1230
+ // is to place the cursor at the start of the matching
1231
+ // denominator. So, we identify the appropriate node, and
1232
+ // continue rightwards until we find ourselves inside of it.
1233
+ // It's possible that there are cases in which we don't reach
1234
+ // the denominator, though I can't think of any.
1235
+ const siblingDenominator = cursor.parent.parent.blocks[1];
1236
+ while (cursor.parent !== siblingDenominator) {
1237
+ mathField.keystroke("Right");
1238
+ }
1239
+ break;
1240
+ case CursorContext.IN_DENOMINATOR:
1241
+ cursor.insRightOf(cursor.parent.parent);
1242
+ break;
1243
+ case CursorContext.IN_SUB_SCRIPT:
1244
+ // Insert just beyond the superscript.
1245
+ cursor.insRightOf(cursor.parent.parent);
1246
+
1247
+ // Navigate right once more, if we're right before parens. This
1248
+ // is to handle the standard case in which the subscript is the
1249
+ // base of a custom log.
1250
+ if (isParens(cursor[MQ.R])) {
1251
+ mathField.keystroke("Right");
1252
+ }
1253
+ break;
1254
+ case CursorContext.IN_SUPER_SCRIPT:
1255
+ // Insert just beyond the superscript.
1256
+ cursor.insRightOf(cursor.parent.parent);
1257
+ break;
1258
+ default:
1259
+ throw new Error("Attempted to 'Jump Out' from node, but found no " + "appropriate cursor context: ".concat(context));
1260
+ }
1261
+ }
1262
+
1263
+ function buildNormalFunctionCallback(command) {
1264
+ return function (mathField) {
1265
+ mathField.write("\\".concat(command, "\\left(\\right)"));
1266
+ mathField.keystroke("Left");
1267
+ };
1268
+ }
1269
+ const customKeyTranslator = {
1270
+ ...keyToMathquillMap,
1271
+ // note(Matthew): in all likelihood, this should be moved
1272
+ // to the shared key2MathQuill translator. During this refactor
1273
+ // I tried to keep logic the same while deduplicating code.
1274
+ // Perseus' Expression MathInput treats this stuff differently
1275
+ // (or doesn't do anything with them at all), so I kept it that way
1276
+ BACKSPACE: handleBackspace,
1277
+ EXP: handleExponent,
1278
+ EXP_2: handleExponent,
1279
+ EXP_3: handleExponent,
1280
+ FRAC: mathQuill => {
1281
+ mathQuill.cmd("\\frac");
1282
+ },
1283
+ JUMP_OUT_PARENTHESES: handleJumpOut,
1284
+ JUMP_OUT_EXPONENT: handleJumpOut,
1285
+ JUMP_OUT_BASE: handleJumpOut,
1286
+ JUMP_INTO_NUMERATOR: handleJumpOut,
1287
+ JUMP_OUT_NUMERATOR: handleJumpOut,
1288
+ JUMP_OUT_DENOMINATOR: handleJumpOut,
1289
+ LEFT: handleArrow,
1290
+ RIGHT: handleArrow,
1291
+ LOG: buildNormalFunctionCallback("log"),
1292
+ LN: buildNormalFunctionCallback("ln"),
1293
+ SIN: buildNormalFunctionCallback("sin"),
1294
+ COS: buildNormalFunctionCallback("cos"),
1295
+ TAN: buildNormalFunctionCallback("tan")
1296
+ };
1297
+
1298
+ /**
1299
+ * This file contains a wrapper around MathQuill so that we can provide a
1300
+ * more regular interface for the functionality we need while insulating us
1301
+ * from MathQuill changes.
1302
+ */
667
1303
  class MathWrapper {
668
- // MathQuill interface
669
1304
  // MathQuill input
670
1305
 
671
1306
  constructor(element) {
672
1307
  let callbacks = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
673
- _defineProperty(this, "MQ", void 0);
674
1308
  _defineProperty(this, "mathField", void 0);
675
1309
  _defineProperty(this, "callbacks", void 0);
676
- this.MQ = MathQuill__default["default"].getInterface(2);
677
- this.mathField = this.MQ.MathField(element, {
1310
+ this.mathField = MQ.MathField(element, {
678
1311
  // use a span instead of a textarea so that we don't bring up the
679
1312
  // native keyboard on mobile when selecting the input
680
1313
  substituteTextarea: function () {
@@ -700,10 +1333,6 @@ class MathWrapper {
700
1333
  controller.cursor.hide();
701
1334
  controller.blurred = true;
702
1335
  }
703
- _writeNormalFunction(name) {
704
- this.mathField.write("\\".concat(name, "\\left(\\right)"));
705
- this.mathField.keystroke("Left");
706
- }
707
1336
 
708
1337
  /**
709
1338
  * Handle a key press and return the resulting cursor state.
@@ -712,51 +1341,10 @@ class MathWrapper {
712
1341
  * @returns {object} a cursor object, consisting of a cursor context
713
1342
  */
714
1343
  pressKey(key) {
715
- const cursor = this.mathField.__controller.cursor;
716
- if (key in KeyActions) {
717
- const {
718
- str,
719
- fn
720
- } = KeyActions[key];
721
- if (str && fn) {
722
- this.mathField[fn](str);
723
- }
724
- } else if (Object.keys(NormalCommands).includes(key)) {
725
- this._writeNormalFunction(NormalCommands[key]);
726
- } else if (key === Keys.FRAC_EXCLUSIVE) {
727
- // If there's nothing to the left of the cursor, then we want to
728
- // leave the cursor to the left of the fraction after creating it.
729
- const shouldNavigateLeft = cursor[this.MQ.L] === ActionType.MQ_END;
730
- this.mathField.cmd("\\frac");
731
- if (shouldNavigateLeft) {
732
- this.mathField.keystroke("Left");
733
- }
734
- } else if (key === Keys.FRAC) {
735
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
736
- cursor[this.MQ.L] === ActionType.MQ_END;
737
- this.mathField.cmd("\\frac");
738
- } else if (key === Keys.LOG_N) {
739
- this.mathField.write("log_{ }\\left(\\right)");
740
- this.mathField.keystroke("Left"); // into parentheses
741
- this.mathField.keystroke("Left"); // out of parentheses
742
- this.mathField.keystroke("Left"); // into index
743
- } else if (key === Keys.CUBE_ROOT) {
744
- this.mathField.write("\\sqrt[3]{}");
745
- this.mathField.keystroke("Left"); // under the root
746
- } else if (key === Keys.EXP || key === Keys.EXP_2 || key === Keys.EXP_3) {
747
- this._handleExponent(cursor, key);
748
- } else if (key === Keys.JUMP_OUT_PARENTHESES || key === Keys.JUMP_OUT_EXPONENT || key === Keys.JUMP_OUT_BASE || key === Keys.JUMP_INTO_NUMERATOR || key === Keys.JUMP_OUT_NUMERATOR || key === Keys.JUMP_OUT_DENOMINATOR) {
749
- this._handleJumpOut(cursor, key);
750
- } else if (key === Keys.BACKSPACE) {
751
- this._handleBackspace(cursor);
752
- } else if (key === Keys.LEFT) {
753
- this._handleLeftArrow(cursor);
754
- } else if (key === Keys.RIGHT) {
755
- this._handleRightArrow(cursor);
756
- } else if (/^[a-zA-Z]$/.test(key)) {
757
- this.mathField[ActionType.WRITE](key);
758
- } else if (/^NUM_\d/.test(key)) {
759
- this.mathField[ActionType.WRITE](key[4]);
1344
+ const cursor = this.getCursor();
1345
+ const translator = customKeyTranslator[key];
1346
+ if (translator) {
1347
+ translator(this.mathField, key);
760
1348
  }
761
1349
  if (!cursor.selection) {
762
1350
  // don't show the cursor for selections
@@ -802,7 +1390,7 @@ class MathWrapper {
802
1390
  // Unless that would leave us mid-command, in which case, we
803
1391
  // need to adjust and place the cursor inside the parens
804
1392
  // following the command.
805
- const command = this._maybeFindCommand(cursor[this.MQ.L]);
1393
+ const command = maybeFindCommand(cursor[MQ.L]);
806
1394
  if (command && command.endNode) {
807
1395
  // NOTE(charlie): endNode should definitely be \left(.
808
1396
  cursor.insLeftOf(command.endNode);
@@ -816,8 +1404,17 @@ class MathWrapper {
816
1404
  }
817
1405
  }
818
1406
  }
1407
+
1408
+ // note(Matthew): extracted this logic to share it elsewhere,
1409
+ // but it's part of the public MathWrapper API
819
1410
  getCursor() {
820
- return this.mathField.__controller.cursor;
1411
+ return getCursor(this.mathField);
1412
+ }
1413
+
1414
+ // note(Matthew): extracted this logic to keep this file focused,
1415
+ // but it's part of the public MathWrapper API
1416
+ contextForCursor(cursor) {
1417
+ return contextForCursor(cursor);
821
1418
  }
822
1419
  getSelection() {
823
1420
  return this.getCursor().selection;
@@ -832,576 +1429,6 @@ class MathWrapper {
832
1429
  const cursor = this.getCursor();
833
1430
  return cursor.parent.id === 1 && cursor[1] === 0 && cursor[-1] === 0;
834
1431
  }
835
-
836
- // Notes about MathQuill
837
- //
838
- // MathQuill's stores its layout as nested linked lists. Each node in the
839
- // list has this.MQ.L '-1' and this.MQ.R '1' properties that define links to
840
- // the left and right nodes respectively. They also have
841
- //
842
- // ctrlSeq: contains the latex code snippet that defines that node.
843
- // jQ: jQuery object for the DOM node(s) for this MathQuill node.
844
- // ends: pointers to the nodes at the ends of the container.
845
- // parent: parent node.
846
- // blocks: an array containing one or more nodes that make up the node.
847
- // sub?: subscript node if there is one as is the case in log_n
848
- //
849
- // All of the code below is super fragile. Please be especially careful
850
- // when upgrading MathQuill.
851
-
852
- _handleBackspaceInNthRoot(cursor) {
853
- const isAtLeftEnd = cursor[this.MQ.L] === ActionType.MQ_END;
854
- const isRootEmpty = this._isInsideEmptyNode(cursor.parent.parent.blocks[0].ends);
855
- if (isAtLeftEnd) {
856
- this._selectNode(cursor.parent.parent, cursor);
857
- if (isRootEmpty) {
858
- this.mathField.keystroke("Backspace");
859
- }
860
- } else {
861
- this.mathField.keystroke("Backspace");
862
- }
863
- }
864
-
865
- /**
866
- * Advances the cursor to the next logical position.
867
- *
868
- * @param {cursor} cursor
869
- * @private
870
- */
871
- _handleJumpOut(cursor, key) {
872
- const context = this.contextForCursor(cursor);
873
-
874
- // Validate that the current cursor context matches the key's intent.
875
- if (KeysForJumpContext[context] !== key) {
876
- // If we don't have a valid cursor context, yet the user was able
877
- // to trigger a jump-out key, that's a broken invariant. Rather
878
- // than throw an error (which would kick the user out of the
879
- // exercise), we do nothing, as a fallback strategy. The user can
880
- // still move the cursor manually.
881
- return;
882
- }
883
- switch (context) {
884
- case CursorContext.IN_PARENS:
885
- // Insert at the end of the parentheses, and then navigate right
886
- // once more to get 'beyond' the parentheses.
887
- cursor.insRightOf(cursor.parent.parent);
888
- break;
889
- case CursorContext.BEFORE_FRACTION:
890
- // Find the nearest fraction to the right of the cursor.
891
- let fractionNode;
892
- let visitor = cursor;
893
- while (visitor[this.MQ.R] !== ActionType.MQ_END) {
894
- if (this._isFraction(visitor[this.MQ.R])) {
895
- fractionNode = visitor[this.MQ.R];
896
- }
897
- visitor = visitor[this.MQ.R];
898
- }
899
-
900
- // Jump into it!
901
- cursor.insLeftOf(fractionNode);
902
- this.mathField.keystroke("Right");
903
- break;
904
- case CursorContext.IN_NUMERATOR:
905
- // HACK(charlie): I can't find a better way to do this. The goal
906
- // is to place the cursor at the start of the matching
907
- // denominator. So, we identify the appropriate node, and
908
- // continue rightwards until we find ourselves inside of it.
909
- // It's possible that there are cases in which we don't reach
910
- // the denominator, though I can't think of any.
911
- const siblingDenominator = cursor.parent.parent.blocks[1];
912
- while (cursor.parent !== siblingDenominator) {
913
- this.mathField.keystroke("Right");
914
- }
915
- break;
916
- case CursorContext.IN_DENOMINATOR:
917
- cursor.insRightOf(cursor.parent.parent);
918
- break;
919
- case CursorContext.IN_SUB_SCRIPT:
920
- // Insert just beyond the superscript.
921
- cursor.insRightOf(cursor.parent.parent);
922
-
923
- // Navigate right once more, if we're right before parens. This
924
- // is to handle the standard case in which the subscript is the
925
- // base of a custom log.
926
- if (this._isParens(cursor[this.MQ.R])) {
927
- this.mathField.keystroke("Right");
928
- }
929
- break;
930
- case CursorContext.IN_SUPER_SCRIPT:
931
- // Insert just beyond the superscript.
932
- cursor.insRightOf(cursor.parent.parent);
933
- break;
934
- default:
935
- throw new Error("Attempted to 'Jump Out' from node, but found no " + "appropriate cursor context: ".concat(context));
936
- }
937
- }
938
-
939
- /**
940
- * Selects and deletes part of the expression based on the cursor location.
941
- * See inline comments for precise behavior of different cases.
942
- *
943
- * @param {cursor} cursor
944
- * @private
945
- */
946
- _handleBackspace(cursor) {
947
- if (!cursor.selection) {
948
- const parent = cursor.parent;
949
- const grandparent = parent.parent;
950
- const leftNode = cursor[this.MQ.L];
951
- if (this._isFraction(leftNode)) {
952
- this._selectNode(leftNode, cursor);
953
- } else if (this._isSquareRoot(leftNode)) {
954
- this._selectNode(leftNode, cursor);
955
- } else if (this._isNthRoot(leftNode)) {
956
- this._selectNode(leftNode, cursor);
957
- } else if (this._isNthRootIndex(parent)) {
958
- this._handleBackspaceInRootIndex(cursor);
959
- } else if (leftNode.ctrlSeq === "\\left(") {
960
- this._handleBackspaceOutsideParens(cursor);
961
- } else if (grandparent.ctrlSeq === "\\left(") {
962
- this._handleBackspaceInsideParens(cursor);
963
- } else if (this._isInsideLogIndex(cursor)) {
964
- this._handleBackspaceInLogIndex(cursor);
965
- } else if (leftNode.ctrlSeq === "\\ge " || leftNode.ctrlSeq === "\\le ") {
966
- this._handleBackspaceAfterLigaturedSymbol(cursor);
967
- } else if (this._isNthRoot(grandparent) && leftNode === ActionType.MQ_END) {
968
- this._handleBackspaceInNthRoot(cursor);
969
- } else {
970
- this.mathField.keystroke("Backspace");
971
- }
972
- } else {
973
- this.mathField.keystroke("Backspace");
974
- }
975
- }
976
- _handleLeftArrow(cursor) {
977
- // If we're inside a function, and just after the left parentheses, we
978
- // need to skip the entire function name, rather than move the cursor
979
- // inside of it. For example, when hitting left from within the
980
- // parentheses in `cos()`, we want to place the cursor to the left of
981
- // the entire expression, rather than between the `s` and the left
982
- // parenthesis.
983
- // From the cursor's perspective, this requires that our left node is
984
- // the ActionType.MQ_END node, that our grandparent is the left parenthesis, and
985
- // the nodes to the left of our grandparent comprise a valid function
986
- // name.
987
- if (cursor[this.MQ.L] === ActionType.MQ_END) {
988
- const parent = cursor.parent;
989
- const grandparent = parent.parent;
990
- if (grandparent.ctrlSeq === "\\left(") {
991
- const command = this._maybeFindCommandBeforeParens(grandparent);
992
- if (command) {
993
- cursor.insLeftOf(command.startNode);
994
- return;
995
- }
996
- }
997
- }
998
-
999
- // Otherwise, we default to the standard MathQull left behavior.
1000
- this.mathField.keystroke("Left");
1001
- }
1002
- _handleRightArrow(cursor) {
1003
- const command = this._maybeFindCommand(cursor[this.MQ.R]);
1004
- if (command) {
1005
- // Similarly, if a function is to our right, then we need to place
1006
- // the cursor at the start of its parenthetical content, which is
1007
- // done by putting it to the left of ites parentheses and then
1008
- // moving right once.
1009
- cursor.insLeftOf(command.endNode);
1010
- this.mathField.keystroke("Right");
1011
- } else {
1012
- // Otherwise, we default to the standard MathQull right behavior.
1013
- this.mathField.keystroke("Right");
1014
- }
1015
- }
1016
- _handleExponent(cursor, key) {
1017
- // If there's an invalid operator preceding the cursor (anything that
1018
- // knowingly cannot be raised to a power), add an empty set of
1019
- // parentheses and apply the exponent to that.
1020
- const invalidPrefixes = [...ArithmeticOperators, ...EqualityOperators];
1021
- const precedingNode = cursor[this.MQ.L];
1022
- const shouldPrefixWithParens = precedingNode === ActionType.MQ_END || invalidPrefixes.includes(precedingNode.ctrlSeq.trim());
1023
- if (shouldPrefixWithParens) {
1024
- this.mathField.write("\\left(\\right)");
1025
- }
1026
-
1027
- // Insert the appropriate exponent operator.
1028
- switch (key) {
1029
- case Keys.EXP:
1030
- this.mathField.cmd("^");
1031
- break;
1032
- case Keys.EXP_2:
1033
- case Keys.EXP_3:
1034
- this.mathField.write("^".concat(key === Keys.EXP_2 ? 2 : 3));
1035
-
1036
- // If we enter a square or a cube, we should leave the cursor
1037
- // within the newly inserted parens, if they exist. This takes
1038
- // exactly four left strokes, since the cursor by default would
1039
- // end up to the right of the exponent.
1040
- if (shouldPrefixWithParens) {
1041
- this.mathField.keystroke("Left");
1042
- this.mathField.keystroke("Left");
1043
- this.mathField.keystroke("Left");
1044
- this.mathField.keystroke("Left");
1045
- }
1046
- break;
1047
- default:
1048
- throw new Error("Invalid exponent key: ".concat(key));
1049
- }
1050
- }
1051
-
1052
- /**
1053
- * Return the start node, end node, and full name of the command of which
1054
- * the initial node is a part, or `null` if the node is not part of a
1055
- * command.
1056
- *
1057
- * @param {node} initialNode - the node to included as part of the command
1058
- * @returns {null|object} - `null` or an object containing the start node
1059
- * (`startNode`), end node (`endNode`), and full
1060
- * name (`name`) of the command
1061
- * @private
1062
- */
1063
- _maybeFindCommand(initialNode) {
1064
- if (!initialNode) {
1065
- return null;
1066
- }
1067
-
1068
- // MathQuill stores commands as separate characters so that
1069
- // users can delete commands one character at a time. We iterate over
1070
- // the nodes from right to left until we hit a sequence starting with a
1071
- // '\\', which signifies the start of a command; then we iterate from
1072
- // left to right until we hit a '\\left(', which signifies the end of a
1073
- // command. If we encounter any character that doesn't belong in a
1074
- // command, we return null. We match a single character at a time.
1075
- // Ex) ['\\l', 'o', 'g ', '\\left(', ...]
1076
- const commandCharRegex = /^[a-z]$/;
1077
- const commandStartRegex = /^\\[a-z]$/;
1078
- const commandEndSeq = "\\left(";
1079
-
1080
- // Note: We allowlist the set of valid commands, since relying solely on
1081
- // a command being prefixed with a backslash leads to undesired
1082
- // behavior. For example, Greek symbols, left parentheses, and square
1083
- // roots all get treated as commands.
1084
- const validCommands = ["\\log", "\\ln", "\\cos", "\\sin", "\\tan"];
1085
- let name = "";
1086
- let startNode;
1087
- let endNode;
1088
-
1089
- // Collect the portion of the command from the current node, leftwards
1090
- // until the start of the command.
1091
- let node = initialNode;
1092
- while (node !== 0) {
1093
- const ctrlSeq = node.ctrlSeq.trim();
1094
- if (commandCharRegex.test(ctrlSeq)) {
1095
- name = ctrlSeq + name;
1096
- } else if (commandStartRegex.test(ctrlSeq)) {
1097
- name = ctrlSeq + name;
1098
- startNode = node;
1099
- break;
1100
- } else {
1101
- break;
1102
- }
1103
- node = node[this.MQ.L];
1104
- }
1105
-
1106
- // If we hit the start of a command, then grab the rest of it by
1107
- // iterating rightwards to compute the full name of the command, along
1108
- // with its terminal node.
1109
- if (startNode) {
1110
- // Next, iterate from the start to the right.
1111
- node = initialNode[this.MQ.R];
1112
- while (node !== 0) {
1113
- const ctrlSeq = node.ctrlSeq.trim();
1114
- if (commandCharRegex.test(ctrlSeq)) {
1115
- // If we have a single character, add it to the command
1116
- // name.
1117
- name = name + ctrlSeq;
1118
- } else if (ctrlSeq === commandEndSeq) {
1119
- // If we hit the command end delimiter (the left
1120
- // parentheses surrounding its arguments), stop.
1121
- endNode = node;
1122
- break;
1123
- }
1124
- node = node[this.MQ.R];
1125
- }
1126
- if (validCommands.includes(name)) {
1127
- return {
1128
- name,
1129
- startNode,
1130
- endNode
1131
- };
1132
- } else {
1133
- return null;
1134
- }
1135
- } else {
1136
- return null;
1137
- }
1138
- }
1139
-
1140
- /**
1141
- * Return the start node, end node, and full name of the command to the left
1142
- * of `\\left(`, or `null` if there is no command.
1143
- *
1144
- * @param {node} leftParenNode - node where .ctrlSeq == `\\left(`
1145
- * @returns {null|object} - `null` or an object containing the start node
1146
- * (`startNode`), end node (`endNode`), and full
1147
- * name (`name`) of the command
1148
- * @private
1149
- */
1150
- _maybeFindCommandBeforeParens(leftParenNode) {
1151
- return this._maybeFindCommand(leftParenNode[this.MQ.L]);
1152
- }
1153
- _selectNode(node, cursor) {
1154
- cursor.insLeftOf(node);
1155
- cursor.startSelection();
1156
- cursor.insRightOf(node);
1157
- cursor.select();
1158
- cursor.endSelection();
1159
- }
1160
- _isFraction(node) {
1161
- return node.jQ && node.jQ.hasClass("mq-fraction");
1162
- }
1163
- _isNumerator(node) {
1164
- return node.jQ && node.jQ.hasClass("mq-numerator");
1165
- }
1166
- _isDenominator(node) {
1167
- return node.jQ && node.jQ.hasClass("mq-denominator");
1168
- }
1169
- _isSubScript(node) {
1170
- // NOTE(charlie): MyScript has a structure whereby its superscripts seem
1171
- // to be represented as a parent node with 'mq-sup-only' containing a
1172
- // single child with 'mq-sup'.
1173
- return node.jQ && (node.jQ.hasClass("mq-sub-only") || node.jQ.hasClass("mq-sub"));
1174
- }
1175
- _isSuperScript(node) {
1176
- // NOTE(charlie): MyScript has a structure whereby its superscripts seem
1177
- // to be represented as a parent node with 'mq-sup-only' containing a
1178
- // single child with 'mq-sup'.
1179
- return node.jQ && (node.jQ.hasClass("mq-sup-only") || node.jQ.hasClass("mq-sup"));
1180
- }
1181
- _isParens(node) {
1182
- return node && node.ctrlSeq === "\\left(";
1183
- }
1184
- _isLeaf(node) {
1185
- return node && node.ctrlSeq && ValidLeaves.includes(node.ctrlSeq.trim());
1186
- }
1187
- _isSquareRoot(node) {
1188
- return node.blocks && node.blocks[0].jQ && node.blocks[0].jQ.hasClass("mq-sqrt-stem");
1189
- }
1190
- _isNthRoot(node) {
1191
- return node.blocks && node.blocks[0].jQ && node.blocks[0].jQ.hasClass("mq-nthroot");
1192
- }
1193
- _isNthRootIndex(node) {
1194
- return node.jQ && node.jQ.hasClass("mq-nthroot");
1195
- }
1196
- _isInsideLogIndex(cursor) {
1197
- const grandparent = cursor.parent.parent;
1198
- if (grandparent && grandparent.jQ.hasClass("mq-supsub")) {
1199
- const command = this._maybeFindCommandBeforeParens(grandparent);
1200
- if (command && command.name === "\\log") {
1201
- return true;
1202
- }
1203
- }
1204
- return false;
1205
- }
1206
- _isInsideEmptyNode(cursor) {
1207
- return cursor[this.MQ.L] === ActionType.MQ_END && cursor[this.MQ.R] === ActionType.MQ_END;
1208
- }
1209
- _handleBackspaceInRootIndex(cursor) {
1210
- if (this._isInsideEmptyNode(cursor)) {
1211
- // When deleting the index in a nthroot, we change from the nthroot
1212
- // to a sqrt, e.g. \sqrt[|]{35x-5} => |\sqrt{35x-5}. If there's no
1213
- // content under the root, then we delete the whole thing.
1214
-
1215
- const grandparent = cursor.parent.parent;
1216
- const latex = grandparent.latex();
1217
- const reinsertionPoint = grandparent[this.MQ.L];
1218
- this._selectNode(grandparent, cursor);
1219
- const rootIsEmpty = grandparent.blocks[1].jQ.text() === "";
1220
- if (rootIsEmpty) {
1221
- // If there is not content under the root then simply delete
1222
- // the whole thing.
1223
- this.mathField.keystroke("Backspace");
1224
- } else {
1225
- // Replace the nthroot with a sqrt if there was content under
1226
- // the root.
1227
-
1228
- // Start by deleting the selection.
1229
- this.mathField.keystroke("Backspace");
1230
-
1231
- // Replace the nth-root with a sqrt.
1232
- this.mathField.write(latex.replace(/^\\sqrt\[\]/, "\\sqrt"));
1233
-
1234
- // Adjust the cursor to be to the left the sqrt.
1235
- if (reinsertionPoint === ActionType.MQ_END) {
1236
- this.mathField.moveToDirEnd(this.MQ.L);
1237
- } else {
1238
- cursor.insRightOf(reinsertionPoint);
1239
- }
1240
- }
1241
- } else {
1242
- if (cursor[this.MQ.L] !== ActionType.MQ_END) {
1243
- // If the cursor is not at the leftmost position inside the
1244
- // root's index, delete a character.
1245
- this.mathField.keystroke("Backspace");
1246
- }
1247
- }
1248
- }
1249
- _handleBackspaceInLogIndex(cursor) {
1250
- if (this._isInsideEmptyNode(cursor)) {
1251
- const grandparent = cursor.parent.parent;
1252
- const command = this._maybeFindCommandBeforeParens(grandparent);
1253
- cursor.insLeftOf(command === null || command === void 0 ? void 0 : command.startNode);
1254
- cursor.startSelection();
1255
- if (grandparent[this.MQ.R] !== ActionType.MQ_END) {
1256
- cursor.insRightOf(grandparent[this.MQ.R]);
1257
- } else {
1258
- cursor.insRightOf(grandparent);
1259
- }
1260
- cursor.select();
1261
- cursor.endSelection();
1262
- const isLogBodyEmpty = grandparent[this.MQ.R].contentjQ.text() === "";
1263
- if (isLogBodyEmpty) {
1264
- // If there's no content inside the log's parens then delete the
1265
- // whole thing.
1266
- this.mathField.keystroke("Backspace");
1267
- }
1268
- } else {
1269
- this.mathField.keystroke("Backspace");
1270
- }
1271
- }
1272
- _handleBackspaceOutsideParens(cursor) {
1273
- // In this case the node with '\\left(' for its ctrlSeq
1274
- // is the parent of the expression contained within the
1275
- // parentheses.
1276
- //
1277
- // Handle selecting an expression before deleting:
1278
- // (x+1)| => |(x+1)|
1279
- // \log(x+1)| => |\log(x+1)|
1280
-
1281
- const leftNode = cursor[this.MQ.L];
1282
- const rightNode = cursor[this.MQ.R];
1283
- const command = this._maybeFindCommandBeforeParens(leftNode);
1284
- if (command && command.startNode) {
1285
- // There's a command before the parens so we select it as well as
1286
- // the parens.
1287
- cursor.insLeftOf(command.startNode);
1288
- cursor.startSelection();
1289
- if (rightNode === ActionType.MQ_END) {
1290
- cursor.insAtRightEnd(cursor.parent);
1291
- } else {
1292
- cursor.insLeftOf(rightNode);
1293
- }
1294
- cursor.select();
1295
- cursor.endSelection();
1296
- } else {
1297
- cursor.startSelection();
1298
- cursor.insLeftOf(leftNode); // left of \\left(
1299
- cursor.select();
1300
- cursor.endSelection();
1301
- }
1302
- }
1303
- _handleBackspaceInsideParens(cursor) {
1304
- // Handle situations when the cursor is inside parens or a
1305
- // command that uses parens, e.g. \log() or \tan()
1306
- //
1307
- // MathQuill represents log(x+1) in roughly the following way
1308
- // [l, o, g, \\left[parent:[x, +, 1]]]
1309
- //
1310
- // If the cursor is inside the parentheses it's next to one of:
1311
- // x, +, or 1. This makes sub_sub_expr its parent and sub_expr
1312
- // it's parent.
1313
- //
1314
- // Interestingly parent doesn't have any nodes to the left or
1315
- // right of it (even though the corresponding DOM node has
1316
- // ( and ) characters on either side.
1317
- //
1318
- // The grandparent's ctrlSeq is `\\left(`. The `\\right)` isn't
1319
- // stored anywhere. NOTE(kevinb): I believe this is because
1320
- // MathQuill knows what the close paren should be and does the
1321
- // right thing at render time.
1322
- //
1323
- // This conditional branch handles the following cases:
1324
- // - \log(x+1|) => \log(x+|)
1325
- // - \log(|x+1) => |\log(x+1)|
1326
- // - \log(|) => |
1327
-
1328
- if (cursor[this.MQ.L] !== ActionType.MQ_END) {
1329
- // This command contains math and there's some math to
1330
- // the left of the cursor that we should delete normally
1331
- // before doing anything special.
1332
- this.mathField.keystroke("Backspace");
1333
- return;
1334
- }
1335
- const grandparent = cursor.parent.parent;
1336
-
1337
- // If the cursors is inside the parens at the start but the command
1338
- // has a subscript as is the case in log_n then move the cursor into
1339
- // the subscript, e.g. \log_{5}(|x+1) => \log_{5|}(x+1)
1340
-
1341
- if (grandparent[this.MQ.L].sub) {
1342
- // if there is a subscript
1343
- if (grandparent[this.MQ.L].sub.jQ.text()) {
1344
- // and it contains text
1345
- // move the cursor to the right end of the subscript
1346
- cursor.insAtRightEnd(grandparent[this.MQ.L].sub);
1347
- return;
1348
- }
1349
- }
1350
-
1351
- // Determine if the parens are empty before we modify the
1352
- // cursor's position.
1353
- const isEmpty = this._isInsideEmptyNode(cursor);
1354
-
1355
- // Insert the cursor to the left of the command if there is one
1356
- // or before the '\\left(` if there isn't
1357
- const command = this._maybeFindCommandBeforeParens(grandparent);
1358
- cursor.insLeftOf(command && command.startNode || grandparent);
1359
- cursor.startSelection();
1360
- cursor.insRightOf(grandparent);
1361
- cursor.select();
1362
- cursor.endSelection();
1363
-
1364
- // Delete the selection, but only if the parens were empty to
1365
- // begin with.
1366
- if (isEmpty) {
1367
- this.mathField.keystroke("Backspace");
1368
- }
1369
- }
1370
- _handleBackspaceAfterLigaturedSymbol(cursor) {
1371
- this.mathField.keystroke("Backspace");
1372
- this.mathField.keystroke("Backspace");
1373
- }
1374
- contextForCursor(cursor) {
1375
- // First, try to find any fraction to the right, unimpeded.
1376
- let visitor = cursor;
1377
- while (visitor[this.MQ.R] !== ActionType.MQ_END) {
1378
- if (this._isFraction(visitor[this.MQ.R])) {
1379
- return CursorContext.BEFORE_FRACTION;
1380
- } else if (!this._isLeaf(visitor[this.MQ.R])) {
1381
- break;
1382
- }
1383
- visitor = visitor[this.MQ.R];
1384
- }
1385
-
1386
- // If that didn't work, check if the parent or grandparent is a special
1387
- // context, so that we can jump outwards.
1388
- if (this._isParens(cursor.parent && cursor.parent.parent)) {
1389
- return CursorContext.IN_PARENS;
1390
- } else if (this._isNumerator(cursor.parent)) {
1391
- return CursorContext.IN_NUMERATOR;
1392
- } else if (this._isDenominator(cursor.parent)) {
1393
- return CursorContext.IN_DENOMINATOR;
1394
- } else if (this._isSubScript(cursor.parent)) {
1395
- return CursorContext.IN_SUB_SCRIPT;
1396
- } else if (this._isSuperScript(cursor.parent)) {
1397
- return CursorContext.IN_SUPER_SCRIPT;
1398
- } else {
1399
- return CursorContext.NONE;
1400
- }
1401
- }
1402
- _isAtTopLevel(cursor) {
1403
- return !cursor.parent.parent;
1404
- }
1405
1432
  }
1406
1433
 
1407
1434
  /**
@@ -1860,16 +1887,16 @@ class MathInput extends React__namespace.Component {
1860
1887
  });
1861
1888
  _defineProperty(this, "domKeyToMathQuillKey", key => {
1862
1889
  const keyMap = {
1863
- "+": Keys.PLUS,
1864
- "-": Keys.MINUS,
1865
- "*": Keys.TIMES,
1866
- "/": Keys.DIVIDE,
1867
- ".": Keys.DECIMAL,
1868
- "%": Keys.PERCENT,
1869
- "=": Keys.EQUAL,
1870
- ">": Keys.GT,
1871
- "<": Keys.LT,
1872
- "^": Keys.EXP
1890
+ "+": "PLUS",
1891
+ "-": "MINUS",
1892
+ "*": "TIMES",
1893
+ "/": "DIVIDE",
1894
+ ".": "DECIMAL",
1895
+ "%": "PERCENT",
1896
+ "=": "EQUAL",
1897
+ ">": "GT",
1898
+ "<": "LT",
1899
+ "^": "EXP"
1873
1900
  };
1874
1901
 
1875
1902
  // Numbers
@@ -1879,7 +1906,7 @@ class MathInput extends React__namespace.Component {
1879
1906
 
1880
1907
  // Movement keys
1881
1908
  else if (key === "Backspace") {
1882
- return Keys.BACKSPACE;
1909
+ return "BACKSPACE";
1883
1910
  }
1884
1911
 
1885
1912
  // Operators
@@ -2177,40 +2204,104 @@ const keypadElementPropType = PropTypes__default["default"].shape({
2177
2204
  /**
2178
2205
  * This file contains configuration settings for the buttons in the keypad.
2179
2206
  */
2180
- // I tried to make the below {[key in Keys]: KeyConfig}
2181
- // but we are doing all kinds of sneaky magic that makes it hard to
2182
- // type this safely. Leaving it for now as a generic index signature.
2207
+ const getDefaultOperatorFields = _ref => {
2208
+ let {
2209
+ key,
2210
+ keyType = "OPERATOR",
2211
+ iconType = IconType.SVG,
2212
+ ariaLabel = key,
2213
+ data = key
2214
+ } = _ref;
2215
+ return {
2216
+ id: key,
2217
+ type: keyType,
2218
+ ariaLabel,
2219
+ icon: {
2220
+ type: iconType,
2221
+ data
2222
+ }
2223
+ };
2224
+ };
2225
+ const getDefaultValueFields = _ref2 => {
2226
+ let {
2227
+ key,
2228
+ keyType = "VALUE",
2229
+ iconType = IconType.MATH,
2230
+ ariaLabel = key,
2231
+ data = key
2232
+ } = _ref2;
2233
+ return {
2234
+ id: key,
2235
+ type: keyType,
2236
+ ariaLabel,
2237
+ icon: {
2238
+ type: iconType,
2239
+ data
2240
+ }
2241
+ };
2242
+ };
2243
+ const getDefaultNumberFields = _ref3 => {
2244
+ let {
2245
+ key,
2246
+ data = key.replace("NUM_", ""),
2247
+ keyType = "VALUE",
2248
+ iconType = IconType.TEXT,
2249
+ ariaLabel = data
2250
+ } = _ref3;
2251
+ return {
2252
+ id: key,
2253
+ type: keyType,
2254
+ ariaLabel,
2255
+ icon: {
2256
+ type: iconType,
2257
+ data
2258
+ }
2259
+ };
2260
+ };
2183
2261
  const KeyConfigs = {
2184
- // Basic math keys.
2185
- [Keys.PLUS]: {
2186
- type: KeyType.OPERATOR,
2187
- // I18N: A label for a plus sign.
2188
- ariaLabel: i18n__namespace._("Plus")
2189
- },
2190
- [Keys.MINUS]: {
2191
- type: KeyType.OPERATOR,
2192
- // I18N: A label for a minus sign.
2193
- ariaLabel: i18n__namespace._("Minus")
2194
- },
2195
- [Keys.NEGATIVE]: {
2196
- type: KeyType.VALUE,
2197
- // I18N: A label for a minus sign.
2198
- ariaLabel: i18n__namespace._("Negative")
2199
- },
2200
- [Keys.TIMES]: {
2201
- type: KeyType.OPERATOR,
2202
- // I18N: A label for a multiplication sign (represented with an 'x').
2203
- ariaLabel: i18n__namespace._("Multiply")
2204
- },
2205
- [Keys.DIVIDE]: {
2206
- type: KeyType.OPERATOR,
2207
- // I18N: A label for a division sign.
2208
- ariaLabel: i18n__namespace._("Divide")
2209
- },
2210
- [Keys.DECIMAL]: {
2211
- type: KeyType.VALUE,
2212
- // I18N: A label for a decimal symbol.
2213
- ariaLabel: i18n__namespace._("Decimal"),
2262
+ // Basic math
2263
+ PLUS: {
2264
+ ...getDefaultOperatorFields({
2265
+ key: "PLUS",
2266
+ // I18N: A label for a 'plus' sign.
2267
+ ariaLabel: i18n__namespace._("Plus")
2268
+ })
2269
+ },
2270
+ MINUS: {
2271
+ ...getDefaultOperatorFields({
2272
+ key: "MINUS",
2273
+ // I18N: A label for a 'minus' sign.
2274
+ ariaLabel: i18n__namespace._("Minus")
2275
+ })
2276
+ },
2277
+ NEGATIVE: {
2278
+ ...getDefaultOperatorFields({
2279
+ key: "NEGATIVE",
2280
+ // I18N: A label for a 'negative' sign.
2281
+ ariaLabel: i18n__namespace._("Negative")
2282
+ })
2283
+ },
2284
+ TIMES: {
2285
+ ...getDefaultOperatorFields({
2286
+ key: "TIMES",
2287
+ // I18N: A label for a 'multiply' sign.
2288
+ ariaLabel: i18n__namespace._("Multiply")
2289
+ })
2290
+ },
2291
+ DIVIDE: {
2292
+ ...getDefaultOperatorFields({
2293
+ key: "DIVIDE",
2294
+ // I18N: A label for a 'divide' sign.
2295
+ ariaLabel: i18n__namespace._("Divide")
2296
+ })
2297
+ },
2298
+ DECIMAL: {
2299
+ ...getDefaultOperatorFields({
2300
+ key: "DECIMAL",
2301
+ keyType: "VALUE",
2302
+ // I18N: A label for a 'decimal' sign (represented as '.' or ',').
2303
+ ariaLabel: i18n__namespace._("Decimal")
2304
+ }),
2214
2305
  icon: decimalSeparator === DecimalSeparator.COMMA ? {
2215
2306
  // TODO(charlie): Get an SVG icon for the comma, or verify with
2216
2307
  // design that the text-rendered version is acceptable.
@@ -2218,252 +2309,662 @@ const KeyConfigs = {
2218
2309
  data: ","
2219
2310
  } : {
2220
2311
  type: IconType.SVG,
2221
- data: Keys.PERIOD
2312
+ data: "PERIOD"
2222
2313
  }
2223
2314
  },
2224
- [Keys.PERCENT]: {
2225
- type: KeyType.OPERATOR,
2226
- // I18N: A label for a percent sign.
2227
- ariaLabel: i18n__namespace._("Percent")
2228
- },
2229
- [Keys.CDOT]: {
2230
- type: KeyType.OPERATOR,
2231
- // I18N: A label for a multiplication sign (represented as a dot).
2232
- ariaLabel: i18n__namespace._("Multiply")
2233
- },
2234
- [Keys.EQUAL]: {
2235
- type: KeyType.OPERATOR,
2236
- ariaLabel: i18n__namespace._("Equals sign")
2237
- },
2238
- [Keys.NEQ]: {
2239
- type: KeyType.OPERATOR,
2240
- ariaLabel: i18n__namespace._("Not-equals sign")
2241
- },
2242
- [Keys.GT]: {
2243
- type: KeyType.OPERATOR,
2244
- // I18N: A label for a 'greater than' sign (represented as '>').
2245
- ariaLabel: i18n__namespace._("Greater than sign")
2246
- },
2247
- [Keys.LT]: {
2248
- type: KeyType.OPERATOR,
2249
- // I18N: A label for a 'less than' sign (represented as '<').
2250
- ariaLabel: i18n__namespace._("Less than sign")
2251
- },
2252
- [Keys.GEQ]: {
2253
- type: KeyType.OPERATOR,
2254
- ariaLabel: i18n__namespace._("Greater than or equal to sign")
2255
- },
2256
- [Keys.LEQ]: {
2257
- type: KeyType.OPERATOR,
2258
- ariaLabel: i18n__namespace._("Less than or equal to sign")
2315
+ PERIOD: {
2316
+ ...getDefaultOperatorFields({
2317
+ key: "PERIOD",
2318
+ keyType: "VALUE",
2319
+ ariaLabel: "."
2320
+ })
2321
+ },
2322
+ PERCENT: {
2323
+ ...getDefaultOperatorFields({
2324
+ key: "PERCENT",
2325
+ // I18N: A label for a 'percent' sign (represented as '%').
2326
+ ariaLabel: i18n__namespace._("Percent")
2327
+ })
2328
+ },
2329
+ CDOT: {
2330
+ ...getDefaultOperatorFields({
2331
+ key: "CDOT",
2332
+ // I18N: A label for a 'centered dot' multiplication sign (represented as '⋅').
2333
+ ariaLabel: i18n__namespace._("Multiply")
2334
+ })
2335
+ },
2336
+ EQUAL: {
2337
+ ...getDefaultOperatorFields({
2338
+ key: "EQUAL",
2339
+ // I18N: A label for an 'equals' sign (represented as '=').
2340
+ ariaLabel: i18n__namespace._("Equals sign")
2341
+ })
2342
+ },
2343
+ NEQ: {
2344
+ ...getDefaultOperatorFields({
2345
+ key: "NEQ",
2346
+ // I18N: A label for a 'not-equals' sign (represented as '≠').
2347
+ ariaLabel: i18n__namespace._("Not-equals sign")
2348
+ })
2349
+ },
2350
+ GT: {
2351
+ ...getDefaultOperatorFields({
2352
+ key: "GT",
2353
+ // I18N: A label for a 'greater than' sign (represented as '>').
2354
+ ariaLabel: i18n__namespace._("Greater than sign")
2355
+ })
2356
+ },
2357
+ LT: {
2358
+ ...getDefaultOperatorFields({
2359
+ key: "LT",
2360
+ // I18N: A label for a 'less than' sign (represented as '<').
2361
+ ariaLabel: i18n__namespace._("Less than sign")
2362
+ })
2363
+ },
2364
+ GEQ: {
2365
+ ...getDefaultOperatorFields({
2366
+ key: "GEQ",
2367
+ // I18N: A label for a 'greater than or equal to' sign (represented as '≥').
2368
+ ariaLabel: i18n__namespace._("Greater than or equal to sign")
2369
+ })
2370
+ },
2371
+ LEQ: {
2372
+ ...getDefaultOperatorFields({
2373
+ key: "LEQ",
2374
+ // I18N: A label for a 'less than or equal to' sign (represented as '≤').
2375
+ ariaLabel: i18n__namespace._("Less than or equal to sign")
2376
+ })
2259
2377
  },
2260
2378
  // mobile native
2261
- [Keys.FRAC_INCLUSIVE]: {
2262
- type: KeyType.OPERATOR,
2263
- // I18N: A label for a button that creates a new fraction and puts the
2264
- // current expression in the numerator of that fraction.
2265
- ariaLabel: i18n__namespace._("Fraction, with current expression in numerator")
2379
+ FRAC_INCLUSIVE: {
2380
+ ...getDefaultOperatorFields({
2381
+ key: "FRAC_INCLUSIVE",
2382
+ // I18N: A label for a button that creates a new fraction and puts the
2383
+ // current expression in the numerator of that fraction.
2384
+ ariaLabel: i18n__namespace._("Fraction, with current expression in numerator")
2385
+ })
2266
2386
  },
2267
2387
  // mobile native
2268
- [Keys.FRAC_EXCLUSIVE]: {
2269
- type: KeyType.OPERATOR,
2270
- // I18N: A label for a button that creates a new fraction next to the
2271
- // cursor.
2272
- ariaLabel: i18n__namespace._("Fraction, excluding the current expression")
2388
+ FRAC_EXCLUSIVE: {
2389
+ ...getDefaultOperatorFields({
2390
+ key: "FRAC_EXCLUSIVE",
2391
+ // I18N: A label for a button that creates a new fraction next to the
2392
+ // cursor.
2393
+ ariaLabel: i18n__namespace._("Fraction, excluding the current expression")
2394
+ })
2273
2395
  },
2274
2396
  // mobile web
2275
- [Keys.FRAC]: {
2276
- type: KeyType.OPERATOR,
2277
- // I18N: A label for a button that creates a new fraction next to the
2278
- // cursor.
2279
- ariaLabel: i18n__namespace._("Fraction, excluding the current expression")
2280
- },
2281
- [Keys.EXP]: {
2282
- type: KeyType.OPERATOR,
2283
- // I18N: A label for a button that will allow the user to input a custom
2284
- // exponent.
2285
- ariaLabel: i18n__namespace._("Custom exponent")
2286
- },
2287
- [Keys.EXP_2]: {
2288
- type: KeyType.OPERATOR,
2289
- // I18N: A label for a button that will square (take to the second
2290
- // power) some math.
2291
- ariaLabel: i18n__namespace._("Square")
2292
- },
2293
- [Keys.EXP_3]: {
2294
- type: KeyType.OPERATOR,
2295
- // I18N: A label for a button that will cube (take to the third power)
2296
- // some math.
2297
- ariaLabel: i18n__namespace._("Cube")
2298
- },
2299
- [Keys.SQRT]: {
2300
- type: KeyType.OPERATOR,
2301
- ariaLabel: i18n__namespace._("Square root")
2302
- },
2303
- [Keys.CUBE_ROOT]: {
2304
- type: KeyType.OPERATOR,
2305
- ariaLabel: i18n__namespace._("Cube root")
2306
- },
2307
- [Keys.RADICAL]: {
2308
- type: KeyType.OPERATOR,
2309
- ariaLabel: i18n__namespace._("Radical with custom root")
2310
- },
2311
- [Keys.LEFT_PAREN]: {
2312
- type: KeyType.OPERATOR,
2313
- ariaLabel: i18n__namespace._("Left parenthesis")
2314
- },
2315
- [Keys.RIGHT_PAREN]: {
2316
- type: KeyType.OPERATOR,
2317
- ariaLabel: i18n__namespace._("Right parenthesis")
2318
- },
2319
- [Keys.LN]: {
2320
- type: KeyType.OPERATOR,
2321
- ariaLabel: i18n__namespace._("Natural logarithm")
2322
- },
2323
- [Keys.LOG]: {
2324
- type: KeyType.OPERATOR,
2325
- ariaLabel: i18n__namespace._("Logarithm with base 10")
2326
- },
2327
- [Keys.LOG_N]: {
2328
- type: KeyType.OPERATOR,
2329
- ariaLabel: i18n__namespace._("Logarithm with custom base")
2330
- },
2331
- [Keys.SIN]: {
2332
- type: KeyType.OPERATOR,
2333
- ariaLabel: i18n__namespace._("Sine")
2334
- },
2335
- [Keys.COS]: {
2336
- type: KeyType.OPERATOR,
2337
- ariaLabel: i18n__namespace._("Cosine")
2338
- },
2339
- [Keys.TAN]: {
2340
- type: KeyType.OPERATOR,
2341
- ariaLabel: i18n__namespace._("Tangent")
2342
- },
2343
- [Keys.PI]: {
2344
- type: KeyType.VALUE,
2345
- ariaLabel: i18n__namespace._("Pi"),
2346
- icon: {
2347
- type: IconType.MATH,
2348
- data: "\\pi"
2349
- }
2350
- },
2351
- [Keys.THETA]: {
2352
- type: KeyType.VALUE,
2353
- ariaLabel: i18n__namespace._("Theta"),
2354
- icon: {
2355
- type: IconType.MATH,
2356
- data: "\\theta"
2357
- }
2358
- },
2359
- [Keys.NOOP]: {
2360
- type: KeyType.EMPTY
2361
- },
2362
- // Input navigation keys.
2363
- [Keys.UP]: {
2364
- type: KeyType.INPUT_NAVIGATION,
2365
- ariaLabel: i18n__namespace._("Up arrow")
2366
- },
2367
- [Keys.RIGHT]: {
2368
- type: KeyType.INPUT_NAVIGATION,
2369
- ariaLabel: i18n__namespace._("Right arrow")
2370
- },
2371
- [Keys.DOWN]: {
2372
- type: KeyType.INPUT_NAVIGATION,
2373
- ariaLabel: i18n__namespace._("Down arrow")
2374
- },
2375
- [Keys.LEFT]: {
2376
- type: KeyType.INPUT_NAVIGATION,
2377
- ariaLabel: i18n__namespace._("Left arrow")
2378
- },
2379
- [Keys.JUMP_OUT_PARENTHESES]: {
2380
- type: KeyType.INPUT_NAVIGATION,
2381
- ariaLabel: i18n__namespace._("Navigate right out of a set of parentheses")
2382
- },
2383
- [Keys.JUMP_OUT_EXPONENT]: {
2384
- type: KeyType.INPUT_NAVIGATION,
2385
- ariaLabel: i18n__namespace._("Navigate right out of an exponent")
2386
- },
2387
- [Keys.JUMP_OUT_BASE]: {
2388
- type: KeyType.INPUT_NAVIGATION,
2389
- ariaLabel: i18n__namespace._("Navigate right out of a base")
2390
- },
2391
- [Keys.JUMP_INTO_NUMERATOR]: {
2392
- type: KeyType.INPUT_NAVIGATION,
2393
- ariaLabel: i18n__namespace._("Navigate right into the numerator of a fraction")
2394
- },
2395
- [Keys.JUMP_OUT_NUMERATOR]: {
2396
- type: KeyType.INPUT_NAVIGATION,
2397
- ariaLabel: i18n__namespace._("Navigate right out of the numerator and into the denominator")
2398
- },
2399
- [Keys.JUMP_OUT_DENOMINATOR]: {
2400
- type: KeyType.INPUT_NAVIGATION,
2401
- ariaLabel: i18n__namespace._("Navigate right out of the denominator of a fraction")
2402
- },
2403
- [Keys.BACKSPACE]: {
2404
- type: KeyType.INPUT_NAVIGATION,
2405
- // I18N: A label for a button that will delete some input.
2406
- ariaLabel: i18n__namespace._("Delete")
2407
- },
2408
- // Keypad navigation keys.
2409
- [Keys.DISMISS]: {
2410
- type: KeyType.KEYPAD_NAVIGATION,
2411
- // I18N: A label for a button that will dismiss/hide a keypad.
2412
- ariaLabel: i18n__namespace._("Dismiss")
2397
+ FRAC: {
2398
+ ...getDefaultOperatorFields({
2399
+ key: "FRAC",
2400
+ // I18N: A label for a button that creates a new fraction next to the
2401
+ // cursor.
2402
+ ariaLabel: i18n__namespace._("Fraction, excluding the current expression")
2403
+ })
2404
+ },
2405
+ EXP: {
2406
+ ...getDefaultOperatorFields({
2407
+ key: "EXP",
2408
+ // I18N: A label for a button that will allow the user to input a
2409
+ // custom exponent.
2410
+ ariaLabel: i18n__namespace._("Custom exponent")
2411
+ })
2412
+ },
2413
+ EXP_2: {
2414
+ ...getDefaultOperatorFields({
2415
+ key: "EXP_2",
2416
+ // I18N: A label for a button that will square (take to the second
2417
+ // power) some math.
2418
+ ariaLabel: i18n__namespace._("Square")
2419
+ })
2420
+ },
2421
+ EXP_3: {
2422
+ ...getDefaultOperatorFields({
2423
+ key: "EXP_3",
2424
+ // I18N: A label for a button that will cube (take to the third power)
2425
+ // some math.
2426
+ ariaLabel: i18n__namespace._("Cube")
2427
+ })
2428
+ },
2429
+ SQRT: {
2430
+ ...getDefaultOperatorFields({
2431
+ key: "SQRT",
2432
+ // I18N: A label for a button that will allow the user to input a
2433
+ // square root.
2434
+ ariaLabel: i18n__namespace._("Square root")
2435
+ })
2436
+ },
2437
+ CUBE_ROOT: {
2438
+ ...getDefaultOperatorFields({
2439
+ key: "CUBE_ROOT",
2440
+ // I18N: A label for a button that will allow the user to input a
2441
+ // cube root.
2442
+ ariaLabel: i18n__namespace._("Cube root")
2443
+ })
2444
+ },
2445
+ RADICAL: {
2446
+ ...getDefaultOperatorFields({
2447
+ key: "RADICAL",
2448
+ // I18N: A label for a button that will allow the user to input a
2449
+ // radical with a custom root.
2450
+ ariaLabel: i18n__namespace._("Radical with custom root")
2451
+ })
2452
+ },
2453
+ LEFT_PAREN: {
2454
+ ...getDefaultOperatorFields({
2455
+ key: "LEFT_PAREN",
2456
+ // I18N: A label for a button that will allow the user to input a
2457
+ // left parenthesis (i.e. '(')
2458
+ ariaLabel: i18n__namespace._("Left parenthesis")
2459
+ })
2460
+ },
2461
+ RIGHT_PAREN: {
2462
+ ...getDefaultOperatorFields({
2463
+ key: "RIGHT_PAREN",
2464
+ // I18N: A label for a button that will allow the user to input a
2465
+ // right parenthesis (i.e. ')')
2466
+ ariaLabel: i18n__namespace._("Right parenthesis")
2467
+ })
2468
+ },
2469
+ LN: {
2470
+ ...getDefaultOperatorFields({
2471
+ key: "LN",
2472
+ // I18N: A label for a button that will allow the user to input a
2473
+ // natural logarithm.
2474
+ ariaLabel: i18n__namespace._("Natural logarithm")
2475
+ })
2476
+ },
2477
+ LOG: {
2478
+ ...getDefaultOperatorFields({
2479
+ key: "LOG",
2480
+ // I18N: A label for a button that will allow the user to input a
2481
+ // logarithm with base 10.
2482
+ ariaLabel: i18n__namespace._("Logarithm with base 10")
2483
+ })
2484
+ },
2485
+ LOG_N: {
2486
+ ...getDefaultOperatorFields({
2487
+ key: "LOG_N",
2488
+ // I18N: A label for a button that will allow the user to input a
2489
+ // logarithm with a custom base.
2490
+ ariaLabel: i18n__namespace._("Logarithm with custom base")
2491
+ })
2492
+ },
2493
+ SIN: {
2494
+ ...getDefaultOperatorFields({
2495
+ key: "SIN",
2496
+ // I18N: A label for a button that will allow the user to input a
2497
+ // sine function.
2498
+ ariaLabel: i18n__namespace._("Sine")
2499
+ })
2500
+ },
2501
+ COS: {
2502
+ ...getDefaultOperatorFields({
2503
+ key: "COS",
2504
+ // I18N: A label for a button that will allow the user to input a
2505
+ // cosine function.
2506
+ ariaLabel: i18n__namespace._("Cosine")
2507
+ })
2508
+ },
2509
+ TAN: {
2510
+ ...getDefaultOperatorFields({
2511
+ key: "TAN",
2512
+ // I18N: A label for a button that will allow the user to input a
2513
+ // tangent function.
2514
+ ariaLabel: i18n__namespace._("Tangent")
2515
+ })
2516
+ },
2517
+ PI: {
2518
+ ...getDefaultValueFields({
2519
+ key: "PI",
2520
+ data: "\\pi",
2521
+ // I18N: A label for a button that will allow the user to input the
2522
+ // mathematical constant pi (i.e., π)
2523
+ ariaLabel: i18n__namespace._("Pi")
2524
+ })
2525
+ },
2526
+ THETA: {
2527
+ ...getDefaultValueFields({
2528
+ key: "THETA",
2529
+ data: "\\theta",
2530
+ // I18N: A label for a button that will allow the user to input the
2531
+ // mathematical constant theta (i.e., θ)
2532
+ ariaLabel: i18n__namespace._("Theta")
2533
+ })
2534
+ },
2535
+ NOOP: {
2536
+ ...getDefaultOperatorFields({
2537
+ key: "NOOP",
2538
+ keyType: "EMPTY"
2539
+ })
2540
+ },
2541
+ // Input navigation
2542
+ UP: {
2543
+ ...getDefaultOperatorFields({
2544
+ key: "UP",
2545
+ keyType: "INPUT_NAVIGATION",
2546
+ ariaLabel: i18n__namespace._("Up arrow")
2547
+ })
2548
+ },
2549
+ RIGHT: {
2550
+ ...getDefaultOperatorFields({
2551
+ key: "RIGHT",
2552
+ keyType: "INPUT_NAVIGATION",
2553
+ ariaLabel: i18n__namespace._("Right arrow")
2554
+ })
2555
+ },
2556
+ DOWN: {
2557
+ ...getDefaultOperatorFields({
2558
+ key: "DOWN",
2559
+ keyType: "INPUT_NAVIGATION",
2560
+ ariaLabel: i18n__namespace._("Down arrow")
2561
+ })
2562
+ },
2563
+ LEFT: {
2564
+ ...getDefaultOperatorFields({
2565
+ key: "LEFT",
2566
+ keyType: "INPUT_NAVIGATION",
2567
+ ariaLabel: i18n__namespace._("Left arrow")
2568
+ })
2569
+ },
2570
+ JUMP_OUT_PARENTHESES: {
2571
+ ...getDefaultOperatorFields({
2572
+ key: "JUMP_OUT_PARENTHESES",
2573
+ keyType: "INPUT_NAVIGATION",
2574
+ ariaLabel: i18n__namespace._("Navigate right out of a set of parentheses")
2575
+ })
2576
+ },
2577
+ JUMP_OUT_EXPONENT: {
2578
+ ...getDefaultOperatorFields({
2579
+ key: "JUMP_OUT_EXPONENT",
2580
+ keyType: "INPUT_NAVIGATION",
2581
+ ariaLabel: i18n__namespace._("Navigate right out of an exponent")
2582
+ })
2583
+ },
2584
+ JUMP_OUT_BASE: {
2585
+ ...getDefaultOperatorFields({
2586
+ key: "JUMP_OUT_BASE",
2587
+ keyType: "INPUT_NAVIGATION",
2588
+ ariaLabel: i18n__namespace._("Navigate right out of a base")
2589
+ })
2590
+ },
2591
+ JUMP_INTO_NUMERATOR: {
2592
+ ...getDefaultOperatorFields({
2593
+ key: "JUMP_INTO_NUMERATOR",
2594
+ keyType: "INPUT_NAVIGATION",
2595
+ ariaLabel: i18n__namespace._("Navigate right into the numerator of a fraction")
2596
+ })
2597
+ },
2598
+ JUMP_OUT_NUMERATOR: {
2599
+ ...getDefaultOperatorFields({
2600
+ key: "JUMP_OUT_NUMERATOR",
2601
+ keyType: "INPUT_NAVIGATION",
2602
+ ariaLabel: i18n__namespace._("Navigate right out of the numerator and into the denominator")
2603
+ })
2604
+ },
2605
+ JUMP_OUT_DENOMINATOR: {
2606
+ ...getDefaultOperatorFields({
2607
+ key: "JUMP_OUT_DENOMINATOR",
2608
+ keyType: "INPUT_NAVIGATION",
2609
+ ariaLabel: i18n__namespace._("Navigate right out of the denominator of a fraction")
2610
+ })
2611
+ },
2612
+ BACKSPACE: {
2613
+ ...getDefaultOperatorFields({
2614
+ key: "BACKSPACE",
2615
+ keyType: "INPUT_NAVIGATION",
2616
+ ariaLabel: i18n__namespace._("Delete")
2617
+ })
2618
+ },
2619
+ // Keypad navigation
2620
+ DISMISS: {
2621
+ ...getDefaultOperatorFields({
2622
+ key: "DISMISS",
2623
+ keyType: "KEYPAD_NAVIGATION",
2624
+ // I18N: A label for a button that will dismiss/hide a keypad.
2625
+ ariaLabel: i18n__namespace._("Dismiss")
2626
+ })
2413
2627
  },
2414
2628
  // TODO(charlie): Use the numeral color for the 'Many' key.
2415
- // MANY: {
2416
- // type: KeyType.MANY,
2417
- // // childKeyIds will be configured by the client.
2418
- // },
2419
-
2420
- [Keys.PERIOD]: {}
2421
- };
2422
-
2423
- // Add in every numeral.
2424
- const NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
2425
- for (const num of NUMBERS) {
2426
- // TODO(charlie): Consider removing the SVG icons that we have for the
2427
- // numeral keys. They can be rendered just as easily with text (though that
2428
- // would mean that we'd be using text beyond the variable key).
2429
- const textRepresentation = "".concat(num);
2430
- KeyConfigs["NUM_".concat(num)] = {
2431
- type: KeyType.VALUE,
2432
- ariaLabel: textRepresentation,
2433
- icon: {
2434
- type: IconType.TEXT,
2435
- data: textRepresentation
2436
- }
2437
- };
2438
- }
2439
-
2440
- // Add in every variable.
2441
- const LETTERS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
2442
- for (const letter of LETTERS) {
2443
- const lowerCaseVariable = letter.toLowerCase();
2444
- const upperCaseVariable = letter.toUpperCase();
2445
- for (const textRepresentation of [lowerCaseVariable, upperCaseVariable]) {
2446
- KeyConfigs[textRepresentation] = {
2447
- type: KeyType.VALUE,
2448
- ariaLabel: textRepresentation,
2449
- icon: {
2450
- type: IconType.MATH,
2451
- data: textRepresentation
2452
- }
2453
- };
2629
+ MANY: {
2630
+ ...getDefaultOperatorFields({
2631
+ key: "MANY",
2632
+ keyType: "MANY"
2633
+ })
2634
+ },
2635
+ // NUMBERS
2636
+ NUM_0: {
2637
+ ...getDefaultNumberFields({
2638
+ key: "NUM_0"
2639
+ })
2640
+ },
2641
+ NUM_1: {
2642
+ ...getDefaultNumberFields({
2643
+ key: "NUM_1"
2644
+ })
2645
+ },
2646
+ NUM_2: {
2647
+ ...getDefaultNumberFields({
2648
+ key: "NUM_2"
2649
+ })
2650
+ },
2651
+ NUM_3: {
2652
+ ...getDefaultNumberFields({
2653
+ key: "NUM_3"
2654
+ })
2655
+ },
2656
+ NUM_4: {
2657
+ ...getDefaultNumberFields({
2658
+ key: "NUM_4"
2659
+ })
2660
+ },
2661
+ NUM_5: {
2662
+ ...getDefaultNumberFields({
2663
+ key: "NUM_5"
2664
+ })
2665
+ },
2666
+ NUM_6: {
2667
+ ...getDefaultNumberFields({
2668
+ key: "NUM_6"
2669
+ })
2670
+ },
2671
+ NUM_7: {
2672
+ ...getDefaultNumberFields({
2673
+ key: "NUM_7"
2674
+ })
2675
+ },
2676
+ NUM_8: {
2677
+ ...getDefaultNumberFields({
2678
+ key: "NUM_8"
2679
+ })
2680
+ },
2681
+ NUM_9: {
2682
+ ...getDefaultNumberFields({
2683
+ key: "NUM_9"
2684
+ })
2685
+ },
2686
+ // LETTERS
2687
+ A: {
2688
+ ...getDefaultValueFields({
2689
+ key: "A"
2690
+ })
2691
+ },
2692
+ B: {
2693
+ ...getDefaultValueFields({
2694
+ key: "B"
2695
+ })
2696
+ },
2697
+ C: {
2698
+ ...getDefaultValueFields({
2699
+ key: "C"
2700
+ })
2701
+ },
2702
+ D: {
2703
+ ...getDefaultValueFields({
2704
+ key: "D"
2705
+ })
2706
+ },
2707
+ E: {
2708
+ ...getDefaultValueFields({
2709
+ key: "E"
2710
+ })
2711
+ },
2712
+ F: {
2713
+ ...getDefaultValueFields({
2714
+ key: "F"
2715
+ })
2716
+ },
2717
+ G: {
2718
+ ...getDefaultValueFields({
2719
+ key: "G"
2720
+ })
2721
+ },
2722
+ H: {
2723
+ ...getDefaultValueFields({
2724
+ key: "H"
2725
+ })
2726
+ },
2727
+ I: {
2728
+ ...getDefaultValueFields({
2729
+ key: "I"
2730
+ })
2731
+ },
2732
+ J: {
2733
+ ...getDefaultValueFields({
2734
+ key: "J"
2735
+ })
2736
+ },
2737
+ K: {
2738
+ ...getDefaultValueFields({
2739
+ key: "K"
2740
+ })
2741
+ },
2742
+ L: {
2743
+ ...getDefaultValueFields({
2744
+ key: "L"
2745
+ })
2746
+ },
2747
+ M: {
2748
+ ...getDefaultValueFields({
2749
+ key: "M"
2750
+ })
2751
+ },
2752
+ N: {
2753
+ ...getDefaultValueFields({
2754
+ key: "N"
2755
+ })
2756
+ },
2757
+ O: {
2758
+ ...getDefaultValueFields({
2759
+ key: "O"
2760
+ })
2761
+ },
2762
+ P: {
2763
+ ...getDefaultValueFields({
2764
+ key: "P"
2765
+ })
2766
+ },
2767
+ Q: {
2768
+ ...getDefaultValueFields({
2769
+ key: "Q"
2770
+ })
2771
+ },
2772
+ R: {
2773
+ ...getDefaultValueFields({
2774
+ key: "R"
2775
+ })
2776
+ },
2777
+ S: {
2778
+ ...getDefaultValueFields({
2779
+ key: "S"
2780
+ })
2781
+ },
2782
+ T: {
2783
+ ...getDefaultValueFields({
2784
+ key: "T"
2785
+ })
2786
+ },
2787
+ U: {
2788
+ ...getDefaultValueFields({
2789
+ key: "U"
2790
+ })
2791
+ },
2792
+ V: {
2793
+ ...getDefaultValueFields({
2794
+ key: "V"
2795
+ })
2796
+ },
2797
+ W: {
2798
+ ...getDefaultValueFields({
2799
+ key: "W"
2800
+ })
2801
+ },
2802
+ X: {
2803
+ ...getDefaultValueFields({
2804
+ key: "X"
2805
+ })
2806
+ },
2807
+ Y: {
2808
+ ...getDefaultValueFields({
2809
+ key: "Y"
2810
+ })
2811
+ },
2812
+ Z: {
2813
+ ...getDefaultValueFields({
2814
+ key: "Z"
2815
+ })
2816
+ },
2817
+ a: {
2818
+ ...getDefaultValueFields({
2819
+ key: "a"
2820
+ })
2821
+ },
2822
+ b: {
2823
+ ...getDefaultValueFields({
2824
+ key: "b"
2825
+ })
2826
+ },
2827
+ c: {
2828
+ ...getDefaultValueFields({
2829
+ key: "c"
2830
+ })
2831
+ },
2832
+ d: {
2833
+ ...getDefaultValueFields({
2834
+ key: "d"
2835
+ })
2836
+ },
2837
+ e: {
2838
+ ...getDefaultValueFields({
2839
+ key: "e"
2840
+ })
2841
+ },
2842
+ f: {
2843
+ ...getDefaultValueFields({
2844
+ key: "f"
2845
+ })
2846
+ },
2847
+ g: {
2848
+ ...getDefaultValueFields({
2849
+ key: "g"
2850
+ })
2851
+ },
2852
+ h: {
2853
+ ...getDefaultValueFields({
2854
+ key: "h"
2855
+ })
2856
+ },
2857
+ i: {
2858
+ ...getDefaultValueFields({
2859
+ key: "i"
2860
+ })
2861
+ },
2862
+ j: {
2863
+ ...getDefaultValueFields({
2864
+ key: "j"
2865
+ })
2866
+ },
2867
+ k: {
2868
+ ...getDefaultValueFields({
2869
+ key: "k"
2870
+ })
2871
+ },
2872
+ l: {
2873
+ ...getDefaultValueFields({
2874
+ key: "l"
2875
+ })
2876
+ },
2877
+ m: {
2878
+ ...getDefaultValueFields({
2879
+ key: "m"
2880
+ })
2881
+ },
2882
+ n: {
2883
+ ...getDefaultValueFields({
2884
+ key: "n"
2885
+ })
2886
+ },
2887
+ o: {
2888
+ ...getDefaultValueFields({
2889
+ key: "o"
2890
+ })
2891
+ },
2892
+ p: {
2893
+ ...getDefaultValueFields({
2894
+ key: "p"
2895
+ })
2896
+ },
2897
+ q: {
2898
+ ...getDefaultValueFields({
2899
+ key: "q"
2900
+ })
2901
+ },
2902
+ r: {
2903
+ ...getDefaultValueFields({
2904
+ key: "r"
2905
+ })
2906
+ },
2907
+ s: {
2908
+ ...getDefaultValueFields({
2909
+ key: "s"
2910
+ })
2911
+ },
2912
+ t: {
2913
+ ...getDefaultValueFields({
2914
+ key: "t"
2915
+ })
2916
+ },
2917
+ u: {
2918
+ ...getDefaultValueFields({
2919
+ key: "u"
2920
+ })
2921
+ },
2922
+ v: {
2923
+ ...getDefaultValueFields({
2924
+ key: "v"
2925
+ })
2926
+ },
2927
+ w: {
2928
+ ...getDefaultValueFields({
2929
+ key: "w"
2930
+ })
2931
+ },
2932
+ x: {
2933
+ ...getDefaultValueFields({
2934
+ key: "x"
2935
+ })
2936
+ },
2937
+ y: {
2938
+ ...getDefaultValueFields({
2939
+ key: "y"
2940
+ })
2941
+ },
2942
+ z: {
2943
+ ...getDefaultValueFields({
2944
+ key: "z"
2945
+ })
2946
+ },
2947
+ PHI: {
2948
+ ...getDefaultValueFields({
2949
+ key: "PHI"
2950
+ })
2951
+ },
2952
+ NTHROOT3: {
2953
+ ...getDefaultValueFields({
2954
+ key: "NTHROOT3"
2955
+ })
2956
+ },
2957
+ POW: {
2958
+ ...getDefaultValueFields({
2959
+ key: "POW"
2960
+ })
2961
+ },
2962
+ LOG_B: {
2963
+ ...getDefaultValueFields({
2964
+ key: "LOG_B"
2965
+ })
2454
2966
  }
2455
- }
2456
- for (const key of Object.keys(KeyConfigs)) {
2457
- KeyConfigs[key] = {
2458
- id: key,
2459
- // Default to an SVG icon indexed by the key name.
2460
- icon: {
2461
- type: IconType.SVG,
2462
- data: key
2463
- },
2464
- ...KeyConfigs[key]
2465
- };
2466
- }
2967
+ };
2467
2968
 
2468
2969
  /**
2469
2970
  * A small triangular decal to sit in the corner of a parent component.
@@ -3953,7 +4454,7 @@ class KeypadButton$1 extends React__namespace.PureComponent {
3953
4454
  // object. This method must be called whenever a property that
3954
4455
  // influences the possible outcomes of `this._getFocusStyle` and
3955
4456
  // `this._getButtonStyle` changes (such as `this.buttonSizeStyle`).
3956
- for (const type of Object.values(KeyType)) {
4457
+ for (const type of KeyTypes) {
3957
4458
  aphrodite.css(View.styles.initial, ...this._getFocusStyle(type));
3958
4459
  for (const borders of Object.values(BorderStyles)) {
3959
4460
  aphrodite.css(View.styles.initial, ...this._getButtonStyle(type, borders));
@@ -3962,7 +4463,7 @@ class KeypadButton$1 extends React__namespace.PureComponent {
3962
4463
  });
3963
4464
  _defineProperty(this, "_getFocusStyle", type => {
3964
4465
  let focusBackgroundStyle;
3965
- if (type === KeyType.INPUT_NAVIGATION || type === KeyType.KEYPAD_NAVIGATION) {
4466
+ if (type === "INPUT_NAVIGATION" || type === "KEYPAD_NAVIGATION") {
3966
4467
  focusBackgroundStyle = styles$9.light;
3967
4468
  } else {
3968
4469
  focusBackgroundStyle = styles$9.bright;
@@ -3973,21 +4474,21 @@ class KeypadButton$1 extends React__namespace.PureComponent {
3973
4474
  // Select the appropriate style for the button.
3974
4475
  let backgroundStyle;
3975
4476
  switch (type) {
3976
- case KeyType.EMPTY:
4477
+ case "EMPTY":
3977
4478
  backgroundStyle = styles$9.empty;
3978
4479
  break;
3979
- case KeyType.MANY:
3980
- case KeyType.VALUE:
4480
+ case "MANY":
4481
+ case "VALUE":
3981
4482
  backgroundStyle = styles$9.value;
3982
4483
  break;
3983
- case KeyType.OPERATOR:
4484
+ case "OPERATOR":
3984
4485
  backgroundStyle = styles$9.operator;
3985
4486
  break;
3986
- case KeyType.INPUT_NAVIGATION:
3987
- case KeyType.KEYPAD_NAVIGATION:
4487
+ case "INPUT_NAVIGATION":
4488
+ case "KEYPAD_NAVIGATION":
3988
4489
  backgroundStyle = styles$9.control;
3989
4490
  break;
3990
- case KeyType.ECHO:
4491
+ case "ECHO":
3991
4492
  backgroundStyle = null;
3992
4493
  break;
3993
4494
  }
@@ -4000,7 +4501,7 @@ class KeypadButton$1 extends React__namespace.PureComponent {
4000
4501
  // @ts-expect-error TS2345
4001
4502
  borderStyle.push(styles$9.bottomBorder);
4002
4503
  }
4003
- return [styles$9.buttonBase, backgroundStyle, ...borderStyle, type === KeyType.ECHO && styles$9.echo, this.buttonSizeStyle,
4504
+ return [styles$9.buttonBase, backgroundStyle, ...borderStyle, type === "ECHO" && styles$9.echo, this.buttonSizeStyle,
4004
4505
  // React Native allows you to set the 'style' props on user defined
4005
4506
  // components.
4006
4507
  // See: https://facebook.github.io/react-native/docs/style.html
@@ -4042,7 +4543,7 @@ class KeypadButton$1 extends React__namespace.PureComponent {
4042
4543
 
4043
4544
  // We render in the focus state if the key is focused, or if it's an
4044
4545
  // echo.
4045
- const renderFocused = !disabled && focused || popoverEnabled || type === KeyType.ECHO;
4546
+ const renderFocused = !disabled && focused || popoverEnabled || type === "ECHO";
4046
4547
  const buttonStyle = this._getButtonStyle(type, borders, style);
4047
4548
  const focusStyle = this._getFocusStyle(type);
4048
4549
  const iconWrapperStyle = [styles$9.iconWrapper, disabled ? styles$9.disabled : undefined];
@@ -4058,11 +4559,11 @@ class KeypadButton$1 extends React__namespace.PureComponent {
4058
4559
  const maybeCornerDecal = !renderFocused && !disabled && childKeys && childKeys.length > 0 && /*#__PURE__*/React__namespace.createElement(CornerDecal, {
4059
4560
  style: styles$9.decalInset
4060
4561
  });
4061
- if (type === KeyType.EMPTY) {
4562
+ if (type === "EMPTY") {
4062
4563
  return /*#__PURE__*/React__namespace.createElement(View, _extends({
4063
4564
  style: buttonStyle
4064
4565
  }, eventHandlers));
4065
- } else if (type === KeyType.MANY) {
4566
+ } else if (type === "MANY") {
4066
4567
  // TODO(charlie): Make the long-press interaction accessible. See
4067
4568
  // the TODO in key-configs.js for more.
4068
4569
  const manyButtonA11yMarkup = {
@@ -4297,7 +4798,7 @@ const mapStateToProps$5 = (state, ownProps) => {
4297
4798
 
4298
4799
  // Override with the default child props, if the key is a multi-symbol key
4299
4800
  // (but not a many-symbol key, which operates under different rules).
4300
- const useFirstChildProps = type !== KeyType.MANY && childKeys && childKeys.length > 0;
4801
+ const useFirstChildProps = type !== "MANY" && childKeys && childKeys.length > 0;
4301
4802
  return {
4302
4803
  ...rest,
4303
4804
  childKeyIds: childKeyIds,
@@ -4342,8 +4843,13 @@ class ManyKeypadButton extends React__namespace.Component {
4342
4843
  } else {
4343
4844
  const keyConfig = {
4344
4845
  id: "MANY",
4345
- type: KeyType.MANY,
4346
- childKeyIds: keys
4846
+ type: "MANY",
4847
+ childKeyIds: keys,
4848
+ ariaLabel: keys.map(key => KeyConfigs[key].ariaLabel).join(", "),
4849
+ icon: {
4850
+ type: IconType.SVG,
4851
+ data: "many"
4852
+ }
4347
4853
  };
4348
4854
  return /*#__PURE__*/React__namespace.createElement(TouchableKeypadButton$1, _extends({
4349
4855
  keyConfig: keyConfig
@@ -4375,8 +4881,6 @@ const IconAsset = function (_ref) {
4375
4881
  }));
4376
4882
  } else if (type === "Operators") {
4377
4883
  return /*#__PURE__*/React__namespace.createElement("svg", {
4378
- width: "32",
4379
- height: "32",
4380
4884
  viewBox: "0 0 32 32",
4381
4885
  fill: "none",
4382
4886
  xmlns: "http://www.w3.org/2000/svg"
@@ -4576,7 +5080,7 @@ class Echo extends React__namespace.Component {
4576
5080
  style: containerStyle
4577
5081
  }, /*#__PURE__*/React__namespace.createElement(KeypadButton$2, {
4578
5082
  icon: icon,
4579
- type: KeyType.ECHO
5083
+ type: "ECHO"
4580
5084
  }));
4581
5085
  }
4582
5086
  }
@@ -4795,18 +5299,6 @@ const setCursor = cursor => {
4795
5299
 
4796
5300
  // Gesture actions
4797
5301
 
4798
- const onSwipeChange = dx => {
4799
- return {
4800
- type: "OnSwipeChange",
4801
- dx
4802
- };
4803
- };
4804
- const onSwipeEnd = dx => {
4805
- return {
4806
- type: "OnSwipeEnd",
4807
- dx
4808
- };
4809
- };
4810
5302
  const setActiveNodes = activeNodes => {
4811
5303
  return {
4812
5304
  type: "SetActiveNodes",
@@ -5910,7 +6402,8 @@ class GestureStateMachine {
5910
6402
  // Only respect the finger that started a swipe. Any other lingering
5911
6403
  // gestures are ignored.
5912
6404
  if (this.swipeState.touchId === touchId) {
5913
- this.handlers.onSwipeChange(pageX - this.swipeState.startX);
6405
+ var _this$handlers$onSwip, _this$handlers;
6406
+ (_this$handlers$onSwip = (_this$handlers = this.handlers).onSwipeChange) === null || _this$handlers$onSwip === void 0 ? void 0 : _this$handlers$onSwip.call(_this$handlers, pageX - this.swipeState.startX);
5914
6407
  }
5915
6408
  } else if (this.touchState[touchId]) {
5916
6409
  // It could be touch events started outside the keypad and
@@ -5923,6 +6416,7 @@ class GestureStateMachine {
5923
6416
  const dx = pageX - startX;
5924
6417
  const shouldBeginSwiping = swipeEnabled && !swipeLocked && Math.abs(dx) > this.options.swipeThresholdPx;
5925
6418
  if (shouldBeginSwiping) {
6419
+ var _this$handlers$onSwip2, _this$handlers2;
5926
6420
  this._onSwipeStart();
5927
6421
 
5928
6422
  // Trigger the swipe.
@@ -5930,7 +6424,7 @@ class GestureStateMachine {
5930
6424
  touchId,
5931
6425
  startX
5932
6426
  };
5933
- this.handlers.onSwipeChange(pageX - this.swipeState.startX);
6427
+ (_this$handlers$onSwip2 = (_this$handlers2 = this.handlers).onSwipeChange) === null || _this$handlers$onSwip2 === void 0 ? void 0 : _this$handlers$onSwip2.call(_this$handlers2, pageX - this.swipeState.startX);
5934
6428
  } else {
5935
6429
  const id = getId();
5936
6430
  if (id !== activeNodeId) {
@@ -5953,7 +6447,8 @@ class GestureStateMachine {
5953
6447
  // Only respect the finger that started a swipe. Any other lingering
5954
6448
  // gestures are ignored.
5955
6449
  if (this.swipeState.touchId === touchId) {
5956
- this.handlers.onSwipeEnd(pageX - this.swipeState.startX);
6450
+ var _this$handlers$onSwip3, _this$handlers3;
6451
+ (_this$handlers$onSwip3 = (_this$handlers3 = this.handlers).onSwipeEnd) === null || _this$handlers$onSwip3 === void 0 ? void 0 : _this$handlers$onSwip3.call(_this$handlers3, pageX - this.swipeState.startX);
5957
6452
  this.swipeState = null;
5958
6453
  }
5959
6454
  } else if (this.touchState[touchId]) {
@@ -5987,7 +6482,8 @@ class GestureStateMachine {
5987
6482
  // displacement.
5988
6483
  if (this.swipeState) {
5989
6484
  if (this.swipeState.touchId === touchId) {
5990
- this.handlers.onSwipeEnd(0);
6485
+ var _this$handlers$onSwip4, _this$handlers4;
6486
+ (_this$handlers$onSwip4 = (_this$handlers4 = this.handlers).onSwipeEnd) === null || _this$handlers$onSwip4 === void 0 ? void 0 : _this$handlers$onSwip4.call(_this$handlers4, 0);
5991
6487
  this.swipeState = null;
5992
6488
  }
5993
6489
  } else if (this.touchState[touchId]) {
@@ -6365,7 +6861,7 @@ class GestureManager {
6365
6861
  * Handle a touch-start event that originated in a node registered with the
6366
6862
  * gesture system.
6367
6863
  *
6368
- * @param {TouchEvent} evt - the raw touch event from the browser
6864
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
6369
6865
  * @param {string} id - the identifier of the DOM node in which the touch
6370
6866
  * occurred
6371
6867
  */
@@ -6392,7 +6888,7 @@ class GestureManager {
6392
6888
  * Handle a touch-move event that originated in a node registered with the
6393
6889
  * gesture system.
6394
6890
  *
6395
- * @param {TouchEvent} evt - the raw touch event from the browser
6891
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
6396
6892
  */
6397
6893
  onTouchMove(evt) {
6398
6894
  if (!this.trackEvents) {
@@ -6410,7 +6906,7 @@ class GestureManager {
6410
6906
  * Handle a touch-end event that originated in a node registered with the
6411
6907
  * gesture system.
6412
6908
  *
6413
- * @param {TouchEvent} evt - the raw touch event from the browser
6909
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
6414
6910
  */
6415
6911
  onTouchEnd(evt) {
6416
6912
  if (!this.trackEvents) {
@@ -6426,7 +6922,7 @@ class GestureManager {
6426
6922
  * Handle a touch-cancel event that originated in a node registered with the
6427
6923
  * gesture system.
6428
6924
  *
6429
- * @param {TouchEvent} evt - the raw touch event from the browser
6925
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
6430
6926
  */
6431
6927
  onTouchCancel(evt) {
6432
6928
  if (!this.trackEvents) {
@@ -6494,7 +6990,7 @@ const echoReducer = function () {
6494
6990
 
6495
6991
  // Add in the echo animation if the user performs a math
6496
6992
  // operation.
6497
- if (keyConfig.type === KeyType.VALUE || keyConfig.type === KeyType.OPERATOR) {
6993
+ if (keyConfig.type === "VALUE" || keyConfig.type === "OPERATOR") {
6498
6994
  return {
6499
6995
  ...state,
6500
6996
  echoes: [...state.echoes, {
@@ -6536,7 +7032,7 @@ const inputReducer = function () {
6536
7032
  };
6537
7033
  case "PressKey":
6538
7034
  const keyConfig = KeyConfigs[action.key];
6539
- if (keyConfig.type !== KeyType.KEYPAD_NAVIGATION) {
7035
+ if (keyConfig.type !== "KEYPAD_NAVIGATION") {
6540
7036
  var _state$keyHandler;
6541
7037
  // This is probably an anti-pattern but it works for the
6542
7038
  // case where we don't actually control the state but we
@@ -6566,7 +7062,7 @@ const keypadForType = {
6566
7062
  };
6567
7063
 
6568
7064
  const initialKeypadState = {
6569
- extraKeys: ["x", "y", Keys.THETA, Keys.PI],
7065
+ extraKeys: ["x", "y", "THETA", "PI"],
6570
7066
  keypadType: defaultKeypadType,
6571
7067
  active: false
6572
7068
  };
@@ -6600,7 +7096,7 @@ const keypadReducer = function () {
6600
7096
  // right actions when they occur. Hence, we figure off a
6601
7097
  // dismissal here rather than dispatching a dismiss action in
6602
7098
  // the first place.
6603
- if (keyConfig.id === Keys.DISMISS) {
7099
+ if (keyConfig.id === "DISMISS") {
6604
7100
  return keypadReducer(state, {
6605
7101
  type: "DismissKeypad"
6606
7102
  });
@@ -6850,19 +7346,13 @@ const createStore = () => {
6850
7346
  return new GestureManager({
6851
7347
  swipeEnabled
6852
7348
  }, {
6853
- onSwipeChange: dx => {
6854
- store.dispatch(onSwipeChange(dx));
6855
- },
6856
- onSwipeEnd: dx => {
6857
- store.dispatch(onSwipeEnd(dx));
6858
- },
6859
7349
  onActiveNodesChanged: activeNodes => {
6860
7350
  store.dispatch(setActiveNodes(activeNodes));
6861
7351
  },
6862
7352
  onClick: (key, layoutProps, inPopover) => {
6863
7353
  store.dispatch(pressKey(key, layoutProps.initialBounds, inPopover));
6864
7354
  }
6865
- }, [], [Keys.BACKSPACE, Keys.UP, Keys.RIGHT, Keys.DOWN, Keys.LEFT]);
7355
+ }, [], ["BACKSPACE", "UP", "RIGHT", "DOWN", "LEFT"]);
6866
7356
  };
6867
7357
  const initialGestureState = {
6868
7358
  popover: null,
@@ -7081,9 +7571,6 @@ asset.
7081
7571
  In the future it would be great if these were included from files so that
7082
7572
  no copying and pasting is necessary.
7083
7573
  */
7084
-
7085
- // TODO: This should be an enumeration of all of the possible legal values
7086
-
7087
7574
  function ButtonAsset(_ref) {
7088
7575
  let {
7089
7576
  id
@@ -7229,84 +7716,94 @@ function ButtonAsset(_ref) {
7229
7716
  }));
7230
7717
  case "MINUS":
7231
7718
  return /*#__PURE__*/React__namespace.createElement("svg", {
7232
- width: "40",
7233
- height: "40",
7234
- viewBox: "0 0 40 40",
7235
- fill: "none",
7236
- xmlns: "http://www.w3.org/2000/svg"
7719
+ xmlns: "http://www.w3.org/2000/svg",
7720
+ width: "20",
7721
+ height: "20",
7722
+ fill: "currentColor",
7723
+ viewBox: "0 0 256 256"
7237
7724
  }, /*#__PURE__*/React__namespace.createElement("path", {
7238
- d: "M27 20H13",
7239
- stroke: "#21242C",
7240
- strokeWidth: "2",
7241
- strokeLinecap: "round",
7242
- strokeLinejoin: "round"
7725
+ d: "M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128Z"
7243
7726
  }));
7244
7727
  case "PLUS":
7728
+ return (
7729
+ /*#__PURE__*/
7730
+ // Phosphor Icons - Plus Bold
7731
+ React__namespace.createElement("svg", {
7732
+ xmlns: "http://www.w3.org/2000/svg",
7733
+ width: "20",
7734
+ height: "20",
7735
+ fill: "currentColor",
7736
+ viewBox: "0 0 256 256"
7737
+ }, /*#__PURE__*/React__namespace.createElement("path", {
7738
+ d: "M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z"
7739
+ }), " ")
7740
+ );
7741
+ case "TIMES":
7742
+ return (
7743
+ /*#__PURE__*/
7744
+ // Phosphor Icons - X Bold
7745
+ React__namespace.createElement("svg", {
7746
+ xmlns: "http://www.w3.org/2000/svg",
7747
+ width: "20",
7748
+ height: "20",
7749
+ fill: "#000000",
7750
+ viewBox: "0 0 256 256"
7751
+ }, /*#__PURE__*/React__namespace.createElement("path", {
7752
+ d: "M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"
7753
+ }))
7754
+ );
7755
+ case "BACKSPACE":
7245
7756
  return /*#__PURE__*/React__namespace.createElement("svg", {
7246
- width: "40",
7247
- height: "40",
7248
- viewBox: "0 0 40 40",
7249
- fill: "none",
7250
- xmlns: "http://www.w3.org/2000/svg"
7757
+ xmlns: "http://www.w3.org/2000/svg",
7758
+ width: "20",
7759
+ height: "20",
7760
+ fill: "currentColor",
7761
+ viewBox: "0 0 256 256"
7251
7762
  }, /*#__PURE__*/React__namespace.createElement("path", {
7252
- d: "M27 20H13",
7253
- stroke: "#21242C",
7254
- strokeWidth: "2",
7255
- strokeLinecap: "round",
7256
- strokeLinejoin: "round"
7257
- }), /*#__PURE__*/React__namespace.createElement("path", {
7258
- d: "M20 13V27",
7259
- stroke: "#21242C",
7260
- strokeWidth: "2",
7261
- strokeLinecap: "round",
7262
- strokeLinejoin: "round"
7763
+ d: "M216,40H68.53a16.08,16.08,0,0,0-13.72,7.77L9.14,123.88a8,8,0,0,0,0,8.24l45.67,76.11A16.08,16.08,0,0,0,68.53,216H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40ZM61.67,204.12,68.53,200h0ZM216,200H68.53l-43.2-72,43.2-72H216ZM106.34,146.34,124.69,128l-18.35-18.34a8,8,0,0,1,11.32-11.32L136,116.69l18.34-18.35a8,8,0,0,1,11.32,11.32L147.31,128l18.35,18.34a8,8,0,0,1-11.32,11.32L136,139.31l-18.34,18.35a8,8,0,0,1-11.32-11.32Z"
7263
7764
  }));
7264
- case "TIMES":
7765
+ case "DISMISS":
7265
7766
  return /*#__PURE__*/React__namespace.createElement("svg", {
7266
- width: "40",
7267
- height: "40",
7268
- viewBox: "0 0 40 40",
7269
- fill: "none",
7270
- xmlns: "http://www.w3.org/2000/svg"
7767
+ xmlns: "http://www.w3.org/2000/svg",
7768
+ width: "20",
7769
+ height: "20",
7770
+ fill: "currentColor",
7771
+ viewBox: "0 0 256 256"
7271
7772
  }, /*#__PURE__*/React__namespace.createElement("path", {
7272
- d: "M24.9498 24.9493L15.0503 15.0498",
7273
- stroke: "#21242C",
7274
- strokeWidth: "2",
7275
- strokeLinecap: "round",
7276
- strokeLinejoin: "round"
7277
- }), /*#__PURE__*/React__namespace.createElement("path", {
7278
- d: "M24.9498 15.0507L15.0503 24.9502",
7279
- stroke: "#21242C",
7280
- strokeWidth: "2",
7281
- strokeLinecap: "round",
7282
- strokeLinejoin: "round"
7773
+ // flip to point down
7774
+ transform: "scale(1,-1) translate(0, -260)",
7775
+ d: "M205.66,125.66a8,8,0,0,1-11.32,0L128,59.31,61.66,125.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,125.66Z"
7283
7776
  }));
7284
- case "BACKSPACE":
7777
+ case "FRAC":
7285
7778
  return /*#__PURE__*/React__namespace.createElement("svg", {
7286
- width: "40",
7287
- height: "40",
7288
- viewBox: "0 0 40 40",
7779
+ width: "48",
7780
+ height: "48",
7781
+ viewBox: "0 0 48 48"
7782
+ }, /*#__PURE__*/React__namespace.createElement("g", {
7289
7783
  fill: "none",
7290
- xmlns: "http://www.w3.org/2000/svg"
7784
+ fillRule: "evenodd"
7291
7785
  }, /*#__PURE__*/React__namespace.createElement("path", {
7292
- fillRule: "evenodd",
7293
- clipRule: "evenodd",
7294
- d: "M10 20L16 14H28.1716V26L16 26L10 20ZM8.58579 18.5858L14.5858 12.5858C14.9609 12.2107 15.4696 12 16 12H28.1716C29.2761 12 30.1716 12.8954 30.1716 14V26C30.1716 27.1046 29.2761 28 28.1716 28H16C15.4696 28 14.9609 27.7893 14.5858 27.4142L8.58579 21.4142C7.80474 20.6332 7.80474 19.3668 8.58579 18.5858ZM22.4645 17.2929L21.1716 18.5858L19.8787 17.2929C19.4882 16.9024 18.855 16.9024 18.4645 17.2929C18.074 17.6834 18.074 18.3166 18.4645 18.7071L19.7574 20L18.4645 21.2929C18.074 21.6834 18.074 22.3166 18.4645 22.7071C18.855 23.0976 19.4882 23.0976 19.8787 22.7071L21.1716 21.4142L22.4645 22.7071C22.855 23.0976 23.4882 23.0976 23.8787 22.7071C24.2693 22.3166 24.2693 21.6834 23.8787 21.2929L22.5858 20L23.8787 18.7071C24.2693 18.3166 24.2693 17.6834 23.8787 17.2929C23.4882 16.9024 22.855 16.9024 22.4645 17.2929Z",
7295
- fill: "#21242C"
7296
- }));
7297
- case "DISMISS":
7298
- return /*#__PURE__*/React__namespace.createElement("svg", {
7299
- width: "40",
7300
- height: "40",
7301
- viewBox: "0 0 40 40",
7302
7786
  fill: "none",
7303
- xmlns: "http://www.w3.org/2000/svg"
7787
+ d: "M0 0h48v48H0z"
7788
+ }), /*#__PURE__*/React__namespace.createElement("g", {
7789
+ transform: "translate(12 12)"
7304
7790
  }, /*#__PURE__*/React__namespace.createElement("path", {
7305
- fillRule: "evenodd",
7306
- clipRule: "evenodd",
7307
- d: "M10 12C10 10.8954 10.8954 10 12 10H28C29.1046 10 30 10.8954 30 12V21H28V12L12 12V21H10V12ZM19 14C19 13.4477 19.4477 13 20 13C20.5523 13 21 13.4477 21 14C21 14.5523 20.5523 15 20 15C19.4477 15 19 14.5523 19 14ZM17 13C16.4477 13 16 13.4477 16 14C16 14.5523 16.4477 15 17 15C17.5523 15 18 14.5523 18 14C18 13.4477 17.5523 13 17 13ZM13 14C13 13.4477 13.4477 13 14 13C14.5523 13 15 13.4477 15 14C15 14.5523 14.5523 15 14 15C13.4477 15 13 14.5523 13 14ZM20 16C19.4477 16 19 16.4477 19 17C19 17.5523 19.4477 18 20 18C20.5523 18 21 17.5523 21 17C21 16.4477 20.5523 16 20 16ZM16 17C16 16.4477 16.4477 16 17 16C17.5523 16 18 16.4477 18 17C18 17.5523 17.5523 18 17 18C16.4477 18 16 17.5523 16 17ZM14 16C13.4477 16 13 16.4477 13 17C13 17.5523 13.4477 18 14 18C14.5523 18 15 17.5523 15 17C15 16.4477 14.5523 16 14 16ZM22 14C22 13.4477 22.4477 13 23 13C23.5523 13 24 13.4477 24 14C24 14.5523 23.5523 15 23 15C22.4477 15 22 14.5523 22 14ZM23 16C22.4477 16 22 16.4477 22 17C22 17.5523 22.4477 18 23 18C23.5523 18 24 17.5523 24 17C24 16.4477 23.5523 16 23 16ZM25 14C25 13.4477 25.4477 13 26 13C26.5523 13 27 13.4477 27 14C27 14.5523 26.5523 15 26 15C25.4477 15 25 14.5523 25 14ZM26 16C25.4477 16 25 16.4477 25 17C25 17.5523 25.4477 18 26 18C26.5523 18 27 17.5523 27 17C27 16.4477 26.5523 16 26 16ZM16 20C16 19.4477 16.4477 19 17 19H23C23.5523 19 24 19.4477 24 20C24 20.5523 23.5523 21 23 21H17C16.4477 21 16 20.5523 16 20ZM26.7071 25.7071C27.0976 25.3166 27.0976 24.6834 26.7071 24.2929C26.3166 23.9024 25.6834 23.9024 25.2929 24.2929L20 29.5858L14.7071 24.2929C14.3166 23.9024 13.6834 23.9024 13.2929 24.2929C12.9024 24.6834 12.9024 25.3166 13.2929 25.7071L19.2929 31.7071C19.6834 32.0976 20.3166 32.0976 20.7071 31.7071L26.7071 25.7071Z",
7308
- fill: "#21242C"
7309
- }));
7791
+ fill: "none",
7792
+ d: "M0 0h24v24H0z"
7793
+ }), /*#__PURE__*/React__namespace.createElement("path", {
7794
+ d: "M8 16.997c0-.55.453-.997.997-.997h6.006c.55 0 .997.453.997.997v6.006c0 .55-.453.997-.997.997H8.997c-.55 0-.997-.453-.997-.997v-6.006zM10 18h4v4h-4v-4z",
7795
+ fill: "currentColor"
7796
+ }), /*#__PURE__*/React__namespace.createElement("rect", {
7797
+ fill: "currentColor",
7798
+ x: "2",
7799
+ y: "11",
7800
+ width: "20",
7801
+ height: "2",
7802
+ rx: "1"
7803
+ }), /*#__PURE__*/React__namespace.createElement("path", {
7804
+ d: "M8 .997C8 .447 8.453 0 8.997 0h6.006c.55 0 .997.453.997.997v6.006c0 .55-.453.997-.997.997H8.997C8.447 8 8 7.547 8 7.003V.997zM10 2h4v4h-4V2z",
7805
+ fill: "currentColor"
7806
+ }))));
7310
7807
  case "FRAC_INCLUSIVE":
7311
7808
  return /*#__PURE__*/React__namespace.createElement("svg", {
7312
7809
  width: "40",
@@ -7333,26 +7830,23 @@ function ButtonAsset(_ref) {
7333
7830
  d: "M12.9571 13.2929C13.3476 13.6834 13.3476 14.3166 12.9571 14.7071C11.6871 15.9771 11 17.9485 11 20C11 22.0515 11.6871 24.0229 12.9571 25.2929C13.3476 25.6834 13.3476 26.3166 12.9571 26.7071C12.5666 27.0976 11.9334 27.0976 11.5429 26.7071C9.81292 24.9771 9 22.4485 9 20C9 17.5515 9.81292 15.0229 11.5429 13.2929C11.9334 12.9024 12.5666 12.9024 12.9571 13.2929ZM14 20C14 19.4477 14.4477 19 15 19H25C25.5523 19 26 19.4477 26 20C26 20.5523 25.5523 21 25 21H15C14.4477 21 14 20.5523 14 20ZM28.4571 13.2929C28.0666 12.9024 27.4334 12.9024 27.0429 13.2929C26.6524 13.6834 26.6524 14.3166 27.0429 14.7071C28.3129 15.9771 29 17.9485 29 20C29 22.0515 28.3129 24.0229 27.0429 25.2929C26.6524 25.6834 26.6524 26.3166 27.0429 26.7071C27.4334 27.0976 28.0666 27.0976 28.4571 26.7071C30.1871 24.9771 31 22.4485 31 20C31 17.5515 30.1871 15.0229 28.4571 13.2929Z",
7334
7831
  fill: "#21242C"
7335
7832
  }));
7833
+ // TODO(ned): Per the notes in `KeyConfigs`, shouldn't this be a comma
7834
+ // that we replace with the period icon for i18n? Duplicating for now.
7336
7835
  case "DECIMAL":
7836
+ case "PERIOD":
7337
7837
  return /*#__PURE__*/React__namespace.createElement("svg", {
7338
- width: "40",
7339
- height: "40",
7340
- viewBox: "0 0 40 40",
7341
- fill: "none",
7342
- xmlns: "http://www.w3.org/2000/svg"
7343
- }, /*#__PURE__*/React__namespace.createElement("g", {
7344
- clipPath: "url(#clip0)"
7345
- }, /*#__PURE__*/React__namespace.createElement("circle", {
7346
- cx: "20",
7347
- cy: "20",
7348
- r: "1.5",
7349
- fill: "#21242C"
7350
- })), /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("clipPath", {
7351
- id: "clip0"
7838
+ xmlns: "http://www.w3.org/2000/svg",
7839
+ width: "20",
7840
+ height: "20",
7841
+ fill: "currentColor",
7842
+ viewBox: "0 0 256 256"
7352
7843
  }, /*#__PURE__*/React__namespace.createElement("path", {
7353
- d: "M18.5 18.5H21.5V21.5H18.5V18.5Z",
7354
- fill: "white"
7355
- }))));
7844
+ d: "M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128Z"
7845
+ // moves decimal down to baseline of number icons,
7846
+ // otherwise indistinguishable from cdot
7847
+ ,
7848
+ transform: "translate(0 80)"
7849
+ }));
7356
7850
  case "RADICAL":
7357
7851
  return /*#__PURE__*/React__namespace.createElement("svg", {
7358
7852
  width: "40",
@@ -7367,6 +7861,38 @@ function ButtonAsset(_ref) {
7367
7861
  fill: "#21242C"
7368
7862
  }));
7369
7863
  case "SQRT":
7864
+ return /*#__PURE__*/React__namespace.createElement("svg", {
7865
+ xmlns: "http://www.w3.org/2000/svg",
7866
+ width: "20",
7867
+ height: "20",
7868
+ fill: "currentColor",
7869
+ viewBox: "0 0 256 256"
7870
+ }, /*#__PURE__*/React__namespace.createElement("path", {
7871
+ d: "M240,72V96a8,8,0,0,1-16,0V80H125.55L79.49,202.81a8,8,0,0,1-15,0l-48-128a8,8,0,1,1,15-5.62L72,177.22l40.51-108A8,8,0,0,1,120,64H232A8,8,0,0,1,240,72Z"
7872
+ }));
7873
+ case "CUBE_ROOT":
7874
+ return /*#__PURE__*/React__namespace.createElement("svg", {
7875
+ width: "48",
7876
+ height: "48",
7877
+ viewBox: "0 0 48 48"
7878
+ }, /*#__PURE__*/React__namespace.createElement("g", {
7879
+ fill: "none",
7880
+ fillRule: "evenodd",
7881
+ transform: "translate(0, -4)"
7882
+ }, /*#__PURE__*/React__namespace.createElement("path", {
7883
+ fill: "none",
7884
+ d: "M0 0h48v48H0z"
7885
+ }), /*#__PURE__*/React__namespace.createElement("path", {
7886
+ d: "M17.91 23.12c1.66 0 2.76-.81 2.76-1.98 0-.96-.86-1.51-1.57-1.58.79-.13 1.46-.72 1.46-1.5 0-1.1-.95-1.83-2.65-1.83-1.23 0-2.11.45-2.67 1.08l.83 1.08c.47-.42 1.05-.64 1.66-.64.64 0 1.12.19 1.12.61 0 .35-.39.52-1.08.52-.25 0-.77 0-.9-.01v1.53c.1-.01.61-.01.9-.01.91 0 1.19.18 1.19.56 0 .37-.38.65-1.12.65-.58 0-1.34-.23-1.82-.7l-.87 1.17c.52.6 1.48 1.05 2.76 1.05z",
7887
+ fill: "currentColor"
7888
+ }), /*#__PURE__*/React__namespace.createElement("path", {
7889
+ stroke: "currentColor",
7890
+ strokeWidth: "2",
7891
+ strokeLinecap: "round",
7892
+ strokeLinejoin: "round",
7893
+ d: "M14 29l4 6 9-14h7"
7894
+ })));
7895
+ case "EXP":
7370
7896
  return /*#__PURE__*/React__namespace.createElement("svg", {
7371
7897
  width: "40",
7372
7898
  height: "40",
@@ -7374,11 +7900,10 @@ function ButtonAsset(_ref) {
7374
7900
  fill: "none",
7375
7901
  xmlns: "http://www.w3.org/2000/svg"
7376
7902
  }, /*#__PURE__*/React__namespace.createElement("path", {
7377
- d: "M10 21L14 27L23 13H30",
7378
- stroke: "#21242C",
7379
- strokeWidth: "2",
7380
- strokeLinecap: "round",
7381
- strokeLinejoin: "round"
7903
+ fillRule: "evenodd",
7904
+ clipRule: "evenodd",
7905
+ fill: "#21242C",
7906
+ d: "M28 8C28 7.44772 28.4477 7 29 7H35C35.5523 7 36 7.44772 36 8V14C36 14.5523 35.5523 15 35 15H29C28.4477 15 28 14.5523 28 14V8ZM30 9H34V13H30V9ZM14 13C14 12.4477 14.4477 12 15 12H25C25.5523 12 26 12.4477 26 13V27C26 27.5523 25.5523 28 25 28H15C14.4477 28 14 27.5523 14 27V13ZM16 14H24V26H16V14Z"
7382
7907
  }));
7383
7908
  case "EXP_2":
7384
7909
  return /*#__PURE__*/React__namespace.createElement("svg", {
@@ -7393,25 +7918,20 @@ function ButtonAsset(_ref) {
7393
7918
  d: "M33.5791 13.7461C33.4874 13.6545 33.3591 13.6086 33.1941 13.6086H31.4011C31.2397 13.6086 31.0674 13.6251 30.8841 13.6581C30.7007 13.6875 30.5156 13.7296 30.3286 13.7846L32.0226 12.0521C32.2352 11.8358 32.4369 11.6213 32.6276 11.4086C32.8182 11.196 32.9851 10.9778 33.1281 10.7541C33.2747 10.5268 33.3902 10.2885 33.4746 10.0391C33.5589 9.78981 33.6011 9.51847 33.6011 9.22514C33.6011 8.88414 33.5406 8.57247 33.4196 8.29014C33.2986 8.00781 33.1281 7.76764 32.9081 7.56964C32.6881 7.36797 32.4222 7.21214 32.1106 7.10214C31.8026 6.98847 31.4597 6.93164 31.0821 6.93164C30.7227 6.93164 30.3872 6.98114 30.0756 7.08014C29.7639 7.17547 29.4871 7.32031 29.2451 7.51464C29.0031 7.70897 28.8014 7.95281 28.6401 8.24614C28.4787 8.53947 28.3687 8.88047 28.3101 9.26914L29.1131 9.41214C29.3184 9.44514 29.4761 9.43231 29.5861 9.37364C29.6997 9.31131 29.7896 9.18847 29.8556 9.00514C29.8886 8.88781 29.9399 8.77964 30.0096 8.68064C30.0792 8.58164 30.1617 8.49547 30.2571 8.42214C30.3561 8.34881 30.4661 8.29197 30.5871 8.25164C30.7117 8.20764 30.8474 8.18564 30.9941 8.18564C31.3277 8.18564 31.5862 8.27914 31.7696 8.46614C31.9529 8.64947 32.0446 8.91897 32.0446 9.27464C32.0446 9.47631 32.0189 9.66881 31.9676 9.85214C31.9162 10.0355 31.8392 10.217 31.7366 10.3966C31.6339 10.5726 31.5056 10.7541 31.3516 10.9411C31.1976 11.1245 31.0197 11.317 30.8181 11.5186L28.4531 13.8891C28.3577 13.9808 28.2899 14.0835 28.2496 14.1971C28.2092 14.3071 28.1891 14.4098 28.1891 14.5051V15.0001H33.7221V14.1091C33.7221 13.9588 33.6744 13.8378 33.5791 13.7461ZM14 13.0001C14 12.4479 14.4477 12.0001 15 12.0001H25C25.5523 12.0001 26 12.4479 26 13.0001V27.0001C26 27.5524 25.5523 28.0001 25 28.0001H15C14.4477 28.0001 14 27.5524 14 27.0001V13.0001ZM16 14.0001H24V26.0001H16V14.0001Z",
7394
7919
  fill: "#21242C"
7395
7920
  }));
7396
- case "EXP":
7921
+ case "EXP_3":
7397
7922
  return /*#__PURE__*/React__namespace.createElement("svg", {
7398
- width: "40",
7399
- height: "40",
7923
+ width: "42",
7924
+ height: "42",
7400
7925
  viewBox: "0 0 40 40",
7401
7926
  fill: "none",
7402
7927
  xmlns: "http://www.w3.org/2000/svg"
7403
7928
  }, /*#__PURE__*/React__namespace.createElement("path", {
7929
+ transform: "translate(0, -8)",
7404
7930
  fillRule: "evenodd",
7405
7931
  clipRule: "evenodd",
7406
- d: "M28 8C28 7.44772 28.4477 7 29 7H35C35.5523 7 36 7.44772 36 8V14C36 14.5523 35.5523 15 35 15H29C28.4477 15 28 14.5523 28 14V8ZM30 9H34V13H30V9ZM14 13C14 12.4477 14.4477 12 15 12H25C25.5523 12 26 12.4477 26 13V27C26 27.5523 25.5523 28 25 28H15C14.4477 28 14 27.5523 14 27V13ZM16 14H24V26H16V14Z",
7407
- fill: "#21242C"
7932
+ fill: "#21242C",
7933
+ d: "M14 21c0-.552.456-1 1.002-1h9.996A1 1 0 0 1 26 21v14c0 .552-.456 1-1.002 1h-9.996A1 1 0 0 1 14 35V21zm2 1h8v12h-8V22zM30.92 23.12c1.66 0 2.76-.81 2.76-1.98 0-.96-.86-1.51-1.57-1.58.79-.13 1.46-.72 1.46-1.5 0-1.1-.95-1.83-2.65-1.83-1.23 0-2.11.45-2.67 1.08l.83 1.08c.47-.42 1.05-.64 1.66-.64.64 0 1.12.19 1.12.61 0 .35-.39.52-1.08.52-.25 0-.77 0-.9-.01v1.53c.1-.01.61-.01.9-.01.91 0 1.19.18 1.19.56 0 .37-.38.65-1.12.65-.58 0-1.34-.23-1.82-.7l-.87 1.17c.52.6 1.48 1.05 2.76 1.05z"
7408
7934
  }));
7409
- case "PI":
7410
- //TODO(NickR): use correct font, size, and color for this. It's not an SVG asset
7411
- return /*#__PURE__*/React__namespace.createElement("span", null, "pi");
7412
- case "X":
7413
- //TODO(NickR): use correct font, size, and color for this. It's not an SVG asset
7414
- return /*#__PURE__*/React__namespace.createElement("span", null, "x");
7415
7935
  case "TAN":
7416
7936
  return /*#__PURE__*/React__namespace.createElement("svg", {
7417
7937
  width: "40",
@@ -7445,7 +7965,310 @@ function ButtonAsset(_ref) {
7445
7965
  d: "M16.4215 19.392C16.3682 19.4773 16.3122 19.5387 16.2535 19.576C16.1948 19.608 16.1202 19.624 16.0295 19.624C15.9335 19.624 15.8295 19.5973 15.7175 19.544C15.6108 19.4907 15.4855 19.432 15.3415 19.368C15.1975 19.2987 15.0322 19.2373 14.8455 19.184C14.6642 19.1307 14.4482 19.104 14.1975 19.104C13.8082 19.104 13.5015 19.1867 13.2775 19.352C13.0588 19.5173 12.9495 19.7333 12.9495 20C12.9495 20.176 13.0055 20.3253 13.1175 20.448C13.2348 20.5653 13.3868 20.6693 13.5735 20.76C13.7655 20.8507 13.9815 20.9333 14.2215 21.008C14.4615 21.0773 14.7042 21.1547 14.9495 21.24C15.2002 21.3253 15.4455 21.424 15.6855 21.536C15.9255 21.6427 16.1388 21.7813 16.3255 21.952C16.5175 22.1173 16.6695 22.3173 16.7815 22.552C16.8988 22.7867 16.9575 23.0693 16.9575 23.4C16.9575 23.7947 16.8855 24.16 16.7415 24.496C16.6028 24.8267 16.3948 25.1147 16.1175 25.36C15.8402 25.6 15.4962 25.7893 15.0855 25.928C14.6802 26.0613 14.2108 26.128 13.6775 26.128C13.3948 26.128 13.1175 26.1013 12.8455 26.048C12.5788 26 12.3202 25.9307 12.0695 25.84C11.8242 25.7493 11.5948 25.6427 11.3815 25.52C11.1735 25.3973 10.9895 25.264 10.8295 25.12L11.2855 24.368C11.3442 24.2773 11.4135 24.208 11.4935 24.16C11.5735 24.112 11.6748 24.088 11.7975 24.088C11.9202 24.088 12.0348 24.1227 12.1415 24.192C12.2535 24.2613 12.3815 24.336 12.5255 24.416C12.6695 24.496 12.8375 24.5707 13.0295 24.64C13.2268 24.7093 13.4748 24.744 13.7735 24.744C14.0082 24.744 14.2082 24.7173 14.3735 24.664C14.5442 24.6053 14.6828 24.5307 14.7895 24.44C14.9015 24.3493 14.9815 24.2453 15.0295 24.128C15.0828 24.0053 15.1095 23.88 15.1095 23.752C15.1095 23.56 15.0508 23.4027 14.9335 23.28C14.8215 23.1573 14.6695 23.0507 14.4775 22.96C14.2908 22.8693 14.0748 22.7893 13.8295 22.72C13.5895 22.6453 13.3415 22.5653 13.0855 22.48C12.8348 22.3947 12.5868 22.296 12.3415 22.184C12.1015 22.0667 11.8855 21.92 11.6935 21.744C11.5068 21.568 11.3548 21.352 11.2375 21.096C11.1255 20.84 11.0695 20.5307 11.0695 20.168C11.0695 19.832 11.1362 19.512 11.2695 19.208C11.4028 18.904 11.5975 18.64 11.8535 18.416C12.1148 18.1867 12.4375 18.0053 12.8215 17.872C13.2108 17.7333 13.6588 17.664 14.1655 17.664C14.7308 17.664 15.2455 17.7573 15.7095 17.944C16.1735 18.1307 16.5602 18.376 16.8695 18.68L16.4215 19.392ZM20.4928 17.792V26H18.5088V17.792H20.4928ZM20.7648 15.4C20.7648 15.5707 20.7301 15.7307 20.6608 15.88C20.5914 16.0293 20.4981 16.16 20.3808 16.272C20.2688 16.384 20.1354 16.4747 19.9808 16.544C19.8261 16.608 19.6608 16.64 19.4848 16.64C19.3141 16.64 19.1514 16.608 18.9968 16.544C18.8474 16.4747 18.7168 16.384 18.6048 16.272C18.4928 16.16 18.4021 16.0293 18.3328 15.88C18.2688 15.7307 18.2368 15.5707 18.2368 15.4C18.2368 15.224 18.2688 15.0587 18.3328 14.904C18.4021 14.7493 18.4928 14.616 18.6048 14.504C18.7168 14.392 18.8474 14.304 18.9968 14.24C19.1514 14.1707 19.3141 14.136 19.4848 14.136C19.6608 14.136 19.8261 14.1707 19.9808 14.24C20.1354 14.304 20.2688 14.392 20.3808 14.504C20.4981 14.616 20.5914 14.7493 20.6608 14.904C20.7301 15.0587 20.7648 15.224 20.7648 15.4ZM24.3553 18.8C24.5206 18.6347 24.6939 18.4827 24.8753 18.344C25.0619 18.2 25.2566 18.08 25.4593 17.984C25.6673 17.8827 25.8886 17.8053 26.1233 17.752C26.3579 17.6933 26.6139 17.664 26.8913 17.664C27.3393 17.664 27.7366 17.7413 28.0833 17.896C28.4299 18.0453 28.7179 18.2587 28.9473 18.536C29.1819 18.808 29.3579 19.136 29.4753 19.52C29.5979 19.8987 29.6593 20.3173 29.6593 20.776V26H27.6833V20.776C27.6833 20.2747 27.5686 19.888 27.3393 19.616C27.1099 19.3387 26.7606 19.2 26.2913 19.2C25.9499 19.2 25.6299 19.2773 25.3313 19.432C25.0326 19.5867 24.7499 19.7973 24.4833 20.064V26H22.5073V17.792H23.7153C23.9713 17.792 24.1393 17.912 24.2193 18.152L24.3553 18.8Z",
7446
7966
  fill: "#21242C"
7447
7967
  }));
7448
- default:
7968
+ case "DIVIDE":
7969
+ return /*#__PURE__*/React__namespace.createElement("svg", {
7970
+ xmlns: "http://www.w3.org/2000/svg",
7971
+ width: "20",
7972
+ height: "20",
7973
+ fill: "currentColor",
7974
+ viewBox: "0 0 256 256"
7975
+ }, /*#__PURE__*/React__namespace.createElement("path", {
7976
+ d: "M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128ZM128,80a16,16,0,1,0-16-16A16,16,0,0,0,128,80Zm0,96a16,16,0,1,0,16,16A16,16,0,0,0,128,176Z"
7977
+ }));
7978
+ case "EQUAL":
7979
+ return /*#__PURE__*/React__namespace.createElement("svg", {
7980
+ xmlns: "http://www.w3.org/2000/svg",
7981
+ width: "20",
7982
+ height: "20",
7983
+ fill: "currentColor",
7984
+ viewBox: "0 0 256 256"
7985
+ }, /*#__PURE__*/React__namespace.createElement("path", {
7986
+ d: "M224,160a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,160ZM40,104H216a8,8,0,0,0,0-16H40a8,8,0,0,0,0,16Z"
7987
+ }));
7988
+ case "GT":
7989
+ return /*#__PURE__*/React__namespace.createElement("svg", {
7990
+ width: "44",
7991
+ height: "44",
7992
+ viewBox: "0 0 48 48"
7993
+ }, /*#__PURE__*/React__namespace.createElement("g", {
7994
+ fill: "none",
7995
+ fillRule: "evenodd"
7996
+ }, /*#__PURE__*/React__namespace.createElement("path", {
7997
+ fill: "none",
7998
+ d: "M0 0h48v48H0z"
7999
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8000
+ fill: "none",
8001
+ d: "M12 12h24v24H12z"
8002
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8003
+ stroke: "currentColor",
8004
+ strokeWidth: "2",
8005
+ strokeLinecap: "round",
8006
+ strokeLinejoin: "round",
8007
+ d: "M16 30l16-6-16-6"
8008
+ })));
8009
+ case "LT":
8010
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8011
+ width: "44",
8012
+ height: "44",
8013
+ viewBox: "0 0 48 48"
8014
+ }, /*#__PURE__*/React__namespace.createElement("g", {
8015
+ fill: "none",
8016
+ fillRule: "evenodd"
8017
+ }, /*#__PURE__*/React__namespace.createElement("path", {
8018
+ fill: "none",
8019
+ d: "M0 0h48v48H0z"
8020
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8021
+ fill: "none",
8022
+ d: "M12 12h24v24H12z"
8023
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8024
+ stroke: "currentColor",
8025
+ strokeWidth: "2",
8026
+ strokeLinecap: "round",
8027
+ strokeLinejoin: "round",
8028
+ d: "M32 30l-16-6 16-6"
8029
+ })));
8030
+ case "GEQ":
8031
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8032
+ width: "44",
8033
+ height: "44",
8034
+ viewBox: "0 0 48 48"
8035
+ }, /*#__PURE__*/React__namespace.createElement("g", {
8036
+ fill: "none",
8037
+ fillRule: "evenodd"
8038
+ }, /*#__PURE__*/React__namespace.createElement("path", {
8039
+ fill: "none",
8040
+ d: "M0 0h48v48H0z"
8041
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8042
+ fill: "none",
8043
+ d: "M12 12h24v24H12z"
8044
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8045
+ d: "M16 33h16M16 30l16-6-16-6",
8046
+ stroke: "currentColor",
8047
+ strokeWidth: "2",
8048
+ strokeLinecap: "round",
8049
+ strokeLinejoin: "round"
8050
+ })));
8051
+ case "LEQ":
8052
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8053
+ width: "44",
8054
+ height: "44",
8055
+ viewBox: "0 0 48 48"
8056
+ }, /*#__PURE__*/React__namespace.createElement("g", {
8057
+ fill: "none",
8058
+ fillRule: "evenodd"
8059
+ }, /*#__PURE__*/React__namespace.createElement("path", {
8060
+ fill: "none",
8061
+ d: "M0 0h48v48H0z"
8062
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8063
+ fill: "none",
8064
+ d: "M12 12h24v24H12z"
8065
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8066
+ d: "M16 33h16M32 30l-16-6 16-6",
8067
+ stroke: "currentColor",
8068
+ strokeWidth: "2",
8069
+ strokeLinecap: "round",
8070
+ strokeLinejoin: "round"
8071
+ })));
8072
+ case "NEQ":
8073
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8074
+ width: "44",
8075
+ height: "44",
8076
+ viewBox: "0 0 48 48"
8077
+ }, /*#__PURE__*/React__namespace.createElement("g", {
8078
+ fill: "none",
8079
+ fillRule: "evenodd"
8080
+ }, /*#__PURE__*/React__namespace.createElement("path", {
8081
+ fill: "none",
8082
+ d: "M0 0h48v48H0z"
8083
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8084
+ fill: "none",
8085
+ d: "M12 12h24v24H12z"
8086
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8087
+ d: "M19 33l10-18M16 21h17M16 27h17",
8088
+ stroke: "currentColor",
8089
+ strokeWidth: "2",
8090
+ strokeLinecap: "round",
8091
+ strokeLinejoin: "round"
8092
+ })));
8093
+ case "LN":
8094
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8095
+ width: "48",
8096
+ height: "48",
8097
+ viewBox: "0 0 48 48"
8098
+ }, /*#__PURE__*/React__namespace.createElement("g", {
8099
+ fill: "none",
8100
+ fillRule: "evenodd"
8101
+ }, /*#__PURE__*/React__namespace.createElement("path", {
8102
+ fill: "none",
8103
+ d: "M0 0h48v48H0z"
8104
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8105
+ d: "M20.836 29v-9.338h-1.778V29h1.778zm8.106 0v-4.774c0-1.316-.714-2.156-2.198-2.156-1.106 0-1.932.532-2.366 1.05v-.882H22.6V29h1.778v-4.55c.294-.406.84-.798 1.54-.798.756 0 1.246.322 1.246 1.26V29h1.778z",
8106
+ fill: "currentColor"
8107
+ })));
8108
+ case "LOG":
8109
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8110
+ width: "48",
8111
+ height: "48",
8112
+ viewBox: "0 0 48 48"
8113
+ }, /*#__PURE__*/React__namespace.createElement("g", {
8114
+ fill: "none",
8115
+ fillRule: "evenodd"
8116
+ }, /*#__PURE__*/React__namespace.createElement("path", {
8117
+ fill: "none",
8118
+ d: "M0 0h48v48H0z"
8119
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8120
+ d: "M16.776 29v-9.338h-1.778V29h1.778zm4.9.168c2.24 0 3.584-1.624 3.584-3.556 0-1.918-1.344-3.542-3.584-3.542-2.226 0-3.57 1.624-3.57 3.542 0 1.932 1.344 3.556 3.57 3.556zm0-1.582c-1.106 0-1.722-.91-1.722-1.974 0-1.05.616-1.96 1.722-1.96 1.106 0 1.736.91 1.736 1.96 0 1.064-.63 1.974-1.736 1.974zm7.672 4.158c1.666 0 3.654-.63 3.654-3.206v-6.3H31.21v.868c-.546-.686-1.274-1.036-2.086-1.036-1.708 0-2.982 1.232-2.982 3.444 0 2.254 1.288 3.444 2.982 3.444.826 0 1.554-.392 2.086-1.064v.686c0 1.33-1.008 1.708-1.862 1.708-.854 0-1.568-.238-2.114-.84l-.798 1.288c.854.742 1.75 1.008 2.912 1.008zm.336-4.368c-1.008 0-1.708-.7-1.708-1.862 0-1.162.7-1.862 1.708-1.862.588 0 1.232.322 1.526.77v2.184c-.294.434-.938.77-1.526.77z",
8121
+ fill: "currentColor"
8122
+ })));
8123
+ case "LOG_N":
8124
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8125
+ width: "48",
8126
+ height: "48",
8127
+ viewBox: "0 0 48 48"
8128
+ }, /*#__PURE__*/React__namespace.createElement("g", {
8129
+ fill: "none",
8130
+ fillRule: "evenodd"
8131
+ }, /*#__PURE__*/React__namespace.createElement("path", {
8132
+ fill: "none",
8133
+ d: "M0 0h48v48H0z"
8134
+ }), /*#__PURE__*/React__namespace.createElement("path", {
8135
+ d: "M30 28.997c0-.55.453-.997.997-.997h6.006c.55 0 .997.453.997.997v6.006c0 .55-.453.997-.997.997h-6.006c-.55 0-.997-.453-.997-.997v-6.006zM32 30h4v4h-4v-4zM12.776 29v-9.338h-1.778V29h1.778zm4.9.168c2.24 0 3.584-1.624 3.584-3.556 0-1.918-1.344-3.542-3.584-3.542-2.226 0-3.57 1.624-3.57 3.542 0 1.932 1.344 3.556 3.57 3.556zm0-1.582c-1.106 0-1.722-.91-1.722-1.974 0-1.05.616-1.96 1.722-1.96 1.106 0 1.736.91 1.736 1.96 0 1.064-.63 1.974-1.736 1.974zm7.672 4.158c1.666 0 3.654-.63 3.654-3.206v-6.3H27.21v.868c-.546-.686-1.274-1.036-2.086-1.036-1.708 0-2.982 1.232-2.982 3.444 0 2.254 1.288 3.444 2.982 3.444.826 0 1.554-.392 2.086-1.064v.686c0 1.33-1.008 1.708-1.862 1.708-.854 0-1.568-.238-2.114-.84l-.798 1.288c.854.742 1.75 1.008 2.912 1.008zm.336-4.368c-1.008 0-1.708-.7-1.708-1.862 0-1.162.7-1.862 1.708-1.862.588 0 1.232.322 1.526.77v2.184c-.294.434-.938.77-1.526.77z",
8136
+ fill: "currentColor"
8137
+ })));
8138
+ case "PERCENT":
8139
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8140
+ xmlns: "http://www.w3.org/2000/svg",
8141
+ width: "20",
8142
+ height: "20",
8143
+ fill: "currentColor",
8144
+ viewBox: "0 0 256 256"
8145
+ }, /*#__PURE__*/React__namespace.createElement("path", {
8146
+ d: "M205.66,61.64l-144,144a8,8,0,0,1-11.32-11.32l144-144a8,8,0,0,1,11.32,11.31ZM50.54,101.44a36,36,0,0,1,50.92-50.91h0a36,36,0,0,1-50.92,50.91ZM56,76A20,20,0,1,0,90.14,61.84h0A20,20,0,0,0,56,76ZM216,180a36,36,0,1,1-10.54-25.46h0A35.76,35.76,0,0,1,216,180Zm-16,0a20,20,0,1,0-5.86,14.14A19.87,19.87,0,0,0,200,180Z"
8147
+ }));
8148
+ case "CDOT":
8149
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8150
+ xmlns: "http://www.w3.org/2000/svg",
8151
+ width: "20",
8152
+ height: "20",
8153
+ fill: "currentColor",
8154
+ viewBox: "0 0 256 256"
8155
+ }, /*#__PURE__*/React__namespace.createElement("path", {
8156
+ d: "M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128Z"
8157
+ }));
8158
+ case "PI":
8159
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8160
+ xmlns: "http://www.w3.org/2000/svg",
8161
+ width: "20",
8162
+ height: "20",
8163
+ fill: "currentColor",
8164
+ viewBox: "0 0 256 256"
8165
+ }, /*#__PURE__*/React__namespace.createElement("path", {
8166
+ d: "M232,172a36,36,0,0,1-72,0V72H96V200a8,8,0,0,1-16,0V72H72a40,40,0,0,0-40,40,8,8,0,0,1-16,0A56.06,56.06,0,0,1,72,56H224a8,8,0,0,1,0,16H176V172a20,20,0,0,0,40,0,8,8,0,0,1,16,0Z"
8167
+ }));
8168
+ case "x":
8169
+ // MATHEMATICAL ITALIC SMALL CHI
8170
+ // https://en.wikipedia.org/wiki/Chi_(letter)#Mathematical_chi
8171
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8172
+ xmlns: "http://www.w3.org/2000/svg",
8173
+ width: "20",
8174
+ height: "20",
8175
+ fill: "currentColor",
8176
+ viewBox: "0 0 256 256"
8177
+ }, /*#__PURE__*/React__namespace.createElement("text", {
8178
+ fontSize: "200px",
8179
+ x: "50%",
8180
+ y: "50%",
8181
+ dominantBaseline: "middle",
8182
+ textAnchor: "middle"
8183
+ }, "\uD835\uDF12"));
8184
+ case "X":
8185
+ // MATHEMATICAL ITALIC CAPITAL CHI
8186
+ // https://en.wikipedia.org/wiki/Chi_(letter)#Mathematical_chi
8187
+ return /*#__PURE__*/React__namespace.createElement("svg", {
8188
+ xmlns: "http://www.w3.org/2000/svg",
8189
+ width: "20",
8190
+ height: "20",
8191
+ fill: "currentColor",
8192
+ viewBox: "0 0 256 256"
8193
+ }, /*#__PURE__*/React__namespace.createElement("text", {
8194
+ fontSize: "200px",
8195
+ x: "50%",
8196
+ y: "50%",
8197
+ dominantBaseline: "middle",
8198
+ textAnchor: "middle"
8199
+ }, "\uD835\uDEF8"));
8200
+
8201
+ /**
8202
+ * ANYTHING BELOW IS NOT YET HANDLED
8203
+ */
8204
+ case "MANY":
8205
+ case "FRAC_EXCLUSIVE":
8206
+ case "THETA":
8207
+ case "NOOP":
8208
+ case "UP":
8209
+ case "DOWN":
8210
+ case "LEFT":
8211
+ case "RIGHT":
8212
+ case "JUMP_OUT_PARENTHESES":
8213
+ case "JUMP_OUT_EXPONENT":
8214
+ case "JUMP_OUT_BASE":
8215
+ case "JUMP_INTO_NUMERATOR":
8216
+ case "JUMP_OUT_NUMERATOR":
8217
+ case "JUMP_OUT_DENOMINATOR":
8218
+ case "PHI":
8219
+ case "NTHROOT3":
8220
+ case "POW":
8221
+ case "LOG_B":
8222
+ case "a":
8223
+ case "b":
8224
+ case "c":
8225
+ case "d":
8226
+ case "e":
8227
+ case "f":
8228
+ case "g":
8229
+ case "h":
8230
+ case "i":
8231
+ case "j":
8232
+ case "k":
8233
+ case "l":
8234
+ case "m":
8235
+ case "n":
8236
+ case "o":
8237
+ case "p":
8238
+ case "q":
8239
+ case "r":
8240
+ case "s":
8241
+ case "t":
8242
+ case "u":
8243
+ case "v":
8244
+ case "w":
8245
+ case "y":
8246
+ case "z":
8247
+ case "A":
8248
+ case "B":
8249
+ case "C":
8250
+ case "D":
8251
+ case "E":
8252
+ case "F":
8253
+ case "G":
8254
+ case "H":
8255
+ case "I":
8256
+ case "J":
8257
+ case "K":
8258
+ case "L":
8259
+ case "M":
8260
+ case "N":
8261
+ case "O":
8262
+ case "P":
8263
+ case "Q":
8264
+ case "R":
8265
+ case "S":
8266
+ case "T":
8267
+ case "U":
8268
+ case "V":
8269
+ case "W":
8270
+ case "Y":
8271
+ case "Z":
7449
8272
  // placeholder
7450
8273
  return /*#__PURE__*/React__namespace.createElement("svg", {
7451
8274
  width: "40",
@@ -7463,6 +8286,11 @@ function ButtonAsset(_ref) {
7463
8286
  lengthAdjust: "spacingAndGlyphs",
7464
8287
  fill: "red"
7465
8288
  }, "placeholder"));
8289
+ default:
8290
+ // this line forces an exhaustive check of all keys;
8291
+ // if a key is not handled, the compiler will complain.
8292
+ const unhandledKey = id;
8293
+ throw new Error("Unhandled key: ".concat(unhandledKey));
7466
8294
  }
7467
8295
  }
7468
8296
 
@@ -7580,7 +8408,7 @@ class GeometryPage extends React__namespace.Component {
7580
8408
  gridColumn: 6
7581
8409
  }
7582
8410
  }), /*#__PURE__*/React__namespace.createElement(SecondaryKeypadButton, {
7583
- keyConfig: KeyConfigs.X,
8411
+ keyConfig: KeyConfigs.x,
7584
8412
  onClickKey: onClickKey,
7585
8413
  style: {
7586
8414
  gridColumn: 5
@@ -7813,7 +8641,7 @@ class OperatorsPage extends React__namespace.Component {
7813
8641
  onClickKey: onClickKey,
7814
8642
  placeholder: !this.props.logarithms
7815
8643
  }), /*#__PURE__*/React__namespace.createElement(SecondaryKeypadButton, {
7816
- keyConfig: KeyConfigs.X,
8644
+ keyConfig: KeyConfigs.x,
7817
8645
  onClickKey: onClickKey,
7818
8646
  style: {
7819
8647
  gridColumn: 5
@@ -7892,11 +8720,10 @@ class Keypad extends React__namespace.Component {
7892
8720
 
7893
8721
  exports.CursorContext = CursorContext;
7894
8722
  exports.KeyConfigs = KeyConfigs;
7895
- exports.KeyType = KeyType;
7896
8723
  exports.Keypad = Keypad;
7897
8724
  exports.KeypadInput = MathInput;
7898
8725
  exports.KeypadType = KeypadType;
7899
- exports.Keys = Keys;
7900
8726
  exports.LegacyKeypad = ProvidedKeypad;
8727
+ exports.keyTranslator = keyToMathquillMap;
7901
8728
  exports.keypadElementPropType = keypadElementPropType;
7902
8729
  //# sourceMappingURL=index.js.map