@khanacademy/math-input 3.0.0 → 4.1.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 (113) hide show
  1. package/.eslintrc.js +7 -1
  2. package/CHANGELOG.md +42 -0
  3. package/dist/components/input/__tests__/test-math-wrapper.d.ts +1 -1
  4. package/dist/components/input/__tests__/test-math-wrapper.js.flow +1 -1
  5. package/dist/components/input/key-handlers/handle-arrow.d.ts +3 -0
  6. package/dist/components/input/key-handlers/handle-arrow.js.flow +12 -0
  7. package/dist/components/input/key-handlers/handle-backspace.d.ts +7 -0
  8. package/dist/components/input/key-handlers/handle-backspace.js.flow +14 -0
  9. package/dist/components/input/key-handlers/handle-exponent.d.ts +3 -0
  10. package/dist/components/input/key-handlers/handle-exponent.js.flow +12 -0
  11. package/dist/components/input/key-handlers/handle-jump-out.d.ts +7 -0
  12. package/dist/components/input/key-handlers/handle-jump-out.js.flow +14 -0
  13. package/dist/components/input/math-wrapper.d.ts +7 -78
  14. package/dist/components/input/math-wrapper.js.flow +16 -78
  15. package/dist/components/input/mathquill-helpers.d.ts +46 -0
  16. package/dist/components/input/mathquill-helpers.js.flow +56 -0
  17. package/dist/components/input/mathquill-instance.d.ts +3 -0
  18. package/dist/components/input/mathquill-instance.js.flow +9 -0
  19. package/dist/components/input/mathquill-types.d.ts +25 -0
  20. package/dist/components/input/mathquill-types.js.flow +34 -0
  21. package/dist/components/key-translator.d.ts +4 -0
  22. package/dist/components/key-translator.js.flow +10 -0
  23. package/dist/components/keypad/button-assets.d.ts +2 -2
  24. package/dist/components/keypad/button-assets.js.flow +2 -2
  25. package/dist/components/keypad/button.d.ts +1 -0
  26. package/dist/components/keypad/button.js.flow +1 -0
  27. package/dist/components/keypad/extras-page/index.d.ts +10 -0
  28. package/dist/components/keypad/extras-page/index.js.flow +15 -0
  29. package/dist/components/keypad/index.d.ts +7 -1
  30. package/dist/components/keypad/index.js.flow +7 -1
  31. package/dist/components/keypad/keypad-page-items.d.ts +1 -1
  32. package/dist/components/keypad/keypad-page-items.js.flow +1 -1
  33. package/dist/components/keypad-legacy/gesture-manager.d.ts +21 -9
  34. package/dist/components/keypad-legacy/gesture-manager.js.flow +27 -12
  35. package/dist/components/keypad-legacy/gesture-state-machine.d.ts +9 -9
  36. package/dist/components/keypad-legacy/gesture-state-machine.js.flow +10 -10
  37. package/dist/components/keypad-legacy/keypad-button.d.ts +2 -2
  38. package/dist/components/keypad-legacy/keypad-button.js.flow +3 -3
  39. package/dist/components/keypad-legacy/store/actions.d.ts +4 -14
  40. package/dist/components/keypad-legacy/store/actions.js.flow +3 -15
  41. package/dist/components/keypad-legacy/store/types.d.ts +2 -2
  42. package/dist/components/keypad-legacy/store/types.js.flow +2 -2
  43. package/dist/components/keypad-legacy/touchable-keypad-button.d.ts +6 -6
  44. package/dist/components/keypad-legacy/touchable-keypad-button.js.flow +9 -14
  45. package/dist/components/keypad-legacy/two-page-keypad.d.ts +6 -4
  46. package/dist/components/keypad-legacy/two-page-keypad.js.flow +6 -4
  47. package/dist/components/tabbar/tabbar.d.ts +6 -9
  48. package/dist/components/tabbar/tabbar.js.flow +6 -9
  49. package/dist/components/tabbar/types.d.ts +1 -1
  50. package/dist/components/tabbar/types.js.flow +1 -1
  51. package/dist/data/key-configs.d.ts +3 -6
  52. package/dist/data/key-configs.js.flow +3 -8
  53. package/dist/data/keys.d.ts +2 -54
  54. package/dist/data/keys.js.flow +116 -55
  55. package/dist/enums.d.ts +2 -9
  56. package/dist/enums.js.flow +2 -11
  57. package/dist/es/index.js +2000 -1346
  58. package/dist/es/index.js.map +1 -1
  59. package/dist/index.d.ts +3 -2
  60. package/dist/index.js +2288 -1392
  61. package/dist/index.js.flow +4 -2
  62. package/dist/index.js.map +1 -1
  63. package/dist/strings.js +26 -10
  64. package/dist/types.d.ts +10 -12
  65. package/dist/types.js.flow +13 -12
  66. package/package.json +2 -1
  67. package/src/components/input/__tests__/context-tracking.test.ts +43 -44
  68. package/src/components/input/__tests__/mathquill.test.ts +133 -135
  69. package/src/components/input/key-handlers/handle-arrow.ts +70 -0
  70. package/src/components/input/key-handlers/handle-backspace.ts +275 -0
  71. package/src/components/input/key-handlers/handle-exponent.ts +52 -0
  72. package/src/components/input/key-handlers/handle-jump-out.ts +103 -0
  73. package/src/components/input/math-input.tsx +11 -12
  74. package/src/components/input/math-wrapper.ts +88 -837
  75. package/src/components/input/mathquill-helpers.ts +268 -0
  76. package/src/components/input/mathquill-instance.ts +5 -0
  77. package/src/components/input/mathquill-types.ts +55 -0
  78. package/src/components/key-translator.ts +209 -0
  79. package/src/components/keypad/button-assets.tsx +441 -100
  80. package/src/components/keypad/button.tsx +7 -2
  81. package/src/components/keypad/extras-page/index.tsx +27 -0
  82. package/src/components/keypad/geometry-page/index.tsx +1 -1
  83. package/src/components/keypad/index.tsx +34 -7
  84. package/src/components/keypad/keypad-mathquill.stories.tsx +82 -0
  85. package/src/components/keypad/keypad-page-items.tsx +3 -1
  86. package/src/components/keypad/operators-page/index.tsx +1 -1
  87. package/src/components/keypad-legacy/echo-manager.tsx +4 -4
  88. package/src/components/keypad-legacy/empty-keypad-button.tsx +6 -4
  89. package/src/components/keypad-legacy/gesture-manager.ts +32 -9
  90. package/src/components/keypad-legacy/gesture-state-machine.ts +14 -14
  91. package/src/components/keypad-legacy/keypad-button.tsx +15 -18
  92. package/src/components/keypad-legacy/many-keypad-button.tsx +9 -2
  93. package/src/components/keypad-legacy/store/actions.ts +3 -29
  94. package/src/components/keypad-legacy/store/echo-reducer.ts +2 -5
  95. package/src/components/keypad-legacy/store/index.ts +4 -10
  96. package/src/components/keypad-legacy/store/input-reducer.ts +1 -2
  97. package/src/components/keypad-legacy/store/keypad-reducer.ts +2 -3
  98. package/src/components/keypad-legacy/store/types.ts +2 -2
  99. package/src/components/keypad-legacy/touchable-keypad-button.tsx +8 -13
  100. package/src/components/keypad-legacy/two-page-keypad.tsx +18 -6
  101. package/src/components/tabbar/__tests__/tabbar.test.tsx +36 -36
  102. package/src/components/tabbar/icons.tsx +68 -52
  103. package/src/components/tabbar/item.tsx +5 -1
  104. package/src/components/tabbar/tabbar.stories.tsx +23 -12
  105. package/src/components/tabbar/tabbar.tsx +22 -38
  106. package/src/components/tabbar/types.ts +1 -1
  107. package/src/data/key-configs.ts +751 -304
  108. package/src/data/keys.ts +118 -65
  109. package/src/enums.ts +10 -9
  110. package/src/index.ts +3 -2
  111. package/src/math-input.stories.tsx +1 -1
  112. package/src/types.ts +10 -12
  113. package/tsconfig-build.tsbuildinfo +1 -1
package/dist/es/index.js CHANGED
@@ -57,71 +57,13 @@ function _extends() {
57
57
  return _extends.apply(this, arguments);
58
58
  }
59
59
 
60
- /**
61
- * This file contains constants for keypad buttons that aren't single
62
- * alphanumeric characters.
63
- */
64
- // TODO(charlie): There's duplication between this file and key-configs.js.
65
- // We should clean it up by removing this file and requiring clients to use the
66
- // `id` field on the key configurations.
67
- var Keys = /*#__PURE__*/function (Keys) {
68
- Keys["PLUS"] = "PLUS";
69
- Keys["MINUS"] = "MINUS";
70
- Keys["NEGATIVE"] = "NEGATIVE";
71
- Keys["TIMES"] = "TIMES";
72
- Keys["DIVIDE"] = "DIVIDE";
73
- Keys["DECIMAL"] = "DECIMAL";
74
- Keys["PERIOD"] = "PERIOD";
75
- Keys["PERCENT"] = "PERCENT";
76
- Keys["CDOT"] = "CDOT";
77
- Keys["EQUAL"] = "EQUAL";
78
- Keys["NEQ"] = "NEQ";
79
- Keys["GT"] = "GT";
80
- Keys["LT"] = "LT";
81
- Keys["GEQ"] = "GEQ";
82
- Keys["LEQ"] = "LEQ";
83
- Keys["FRAC_INCLUSIVE"] = "FRAC_INCLUSIVE";
84
- Keys["FRAC_EXCLUSIVE"] = "FRAC_EXCLUSIVE";
85
- Keys["FRAC"] = "FRAC";
86
- Keys["EXP"] = "EXP";
87
- Keys["EXP_2"] = "EXP_2";
88
- Keys["EXP_3"] = "EXP_3";
89
- Keys["SQRT"] = "SQRT";
90
- Keys["CUBE_ROOT"] = "CUBE_ROOT";
91
- Keys["RADICAL"] = "RADICAL";
92
- Keys["LEFT_PAREN"] = "LEFT_PAREN";
93
- Keys["RIGHT_PAREN"] = "RIGHT_PAREN";
94
- Keys["LN"] = "LN";
95
- Keys["LOG"] = "LOG";
96
- Keys["LOG_N"] = "LOG_N";
97
- Keys["SIN"] = "SIN";
98
- Keys["COS"] = "COS";
99
- Keys["TAN"] = "TAN";
100
- Keys["PI"] = "PI";
101
- Keys["THETA"] = "THETA";
102
- Keys["UP"] = "UP";
103
- Keys["RIGHT"] = "RIGHT";
104
- Keys["DOWN"] = "DOWN";
105
- Keys["LEFT"] = "LEFT";
106
- Keys["BACKSPACE"] = "BACKSPACE";
107
- Keys["DISMISS"] = "DISMISS";
108
- Keys["JUMP_OUT_PARENTHESES"] = "JUMP_OUT_PARENTHESES";
109
- Keys["JUMP_OUT_EXPONENT"] = "JUMP_OUT_EXPONENT";
110
- Keys["JUMP_OUT_BASE"] = "JUMP_OUT_BASE";
111
- Keys["JUMP_INTO_NUMERATOR"] = "JUMP_INTO_NUMERATOR";
112
- Keys["JUMP_OUT_NUMERATOR"] = "JUMP_OUT_NUMERATOR";
113
- Keys["JUMP_OUT_DENOMINATOR"] = "JUMP_OUT_DENOMINATOR";
114
- Keys["NOOP"] = "NOOP";
115
- return Keys;
116
- }(Keys || {}); // mobile native only
117
-
118
60
  class Text extends React.Component {
119
61
  render() {
120
62
  const {
121
63
  numberOfLines,
122
64
  style
123
65
  } = this.props;
124
- const className = css(styles$g.initial, ...(Array.isArray(style) ? style : [style]), numberOfLines === 1 && styles$g.singleLineStyle);
66
+ const className = css(styles$h.initial, ...(Array.isArray(style) ? style : [style]), numberOfLines === 1 && styles$h.singleLineStyle);
125
67
  return /*#__PURE__*/React.createElement("span", {
126
68
  className: className,
127
69
  style: this.props.dynamicStyle
@@ -130,7 +72,7 @@ class Text extends React.Component {
130
72
  }
131
73
 
132
74
  // https://github.com/necolas/react-native-web/blob/master/src/components/Text/index.js
133
- const styles$g = StyleSheet.create({
75
+ const styles$h = StyleSheet.create({
134
76
  initial: {
135
77
  color: "inherit",
136
78
  display: "inline",
@@ -408,16 +350,23 @@ let KeypadType = /*#__PURE__*/function (KeypadType) {
408
350
  KeypadType["EXPRESSION"] = "EXPRESSION";
409
351
  return KeypadType;
410
352
  }({});
411
- let KeyType = /*#__PURE__*/function (KeyType) {
412
- KeyType["EMPTY"] = "EMPTY";
413
- KeyType["VALUE"] = "VALUE";
414
- KeyType["OPERATOR"] = "OPERATOR";
415
- KeyType["INPUT_NAVIGATION"] = "INPUT_NAVIGATION";
416
- KeyType["KEYPAD_NAVIGATION"] = "KEYPAD_NAVIGATION";
417
- KeyType["MANY"] = "MANY";
418
- KeyType["ECHO"] = "ECHO";
419
- return KeyType;
420
- }({});
353
+ const KeyTypes = ["EMPTY",
354
+ // For numerals, variables, and any other characters that themselves
355
+ // compose 'values'.
356
+ "VALUE",
357
+ // For buttons that insert or adjust math in an input.
358
+ "OPERATOR",
359
+ // For buttons that move the cursor in an input (including via
360
+ // deletion).
361
+ "INPUT_NAVIGATION",
362
+ // For buttons that modify the broader keypad state (e.g., by changing
363
+ // the visible pane).
364
+ "KEYPAD_NAVIGATION",
365
+ // For buttons that house multiple buttons and have no action
366
+ // themselves.
367
+ "MANY",
368
+ // For the echo animation that appears on press.
369
+ "ECHO"];
421
370
  let DeviceOrientation = /*#__PURE__*/function (DeviceOrientation) {
422
371
  DeviceOrientation["LANDSCAPE"] = "LANDSCAPE";
423
372
  DeviceOrientation["PORTRAIT"] = "PORTRAIT";
@@ -470,131 +419,208 @@ let EchoAnimationType = /*#__PURE__*/function (EchoAnimationType) {
470
419
  // listed here. Much of the Arab world uses U+066C.
471
420
  const decimalSeparator = getDecimalSeparator() === "," ? DecimalSeparator.COMMA : DecimalSeparator.PERIOD;
472
421
 
473
- /**
474
- * This file contains a wrapper around MathQuill so that we can provide a
475
- * more regular interface for the functionality we need while insulating us
476
- * from MathQuill changes.
477
- */
478
-
479
- // Keeping `window` in place for test suite and GitHub Pages.
480
- // If it does not exist, fall back to CommonJS require. - jsatk
422
+ var MQ = MathQuill.getInterface(2);
481
423
 
482
- const decimalSymbol = decimalSeparator === DecimalSeparator.COMMA ? "," : ".";
483
424
  var ActionType = /*#__PURE__*/function (ActionType) {
484
425
  ActionType["WRITE"] = "write";
485
426
  ActionType["CMD"] = "cmd";
486
427
  ActionType["KEYSTROKE"] = "keystroke";
487
428
  ActionType[ActionType["MQ_END"] = 0] = "MQ_END";
488
429
  return ActionType;
489
- }(ActionType || {}); // A mapping from keys that can be pressed on a keypad to the way in which
490
- // MathQuill should modify its input in response to that key-press. Any keys
491
- // that do not provide explicit actions (like the numeral keys) will merely
492
- // write their contents to MathQuill.
493
- const KeyActions = {
494
- [Keys.PLUS]: {
495
- str: "+",
496
- fn: ActionType.WRITE
497
- },
498
- [Keys.MINUS]: {
499
- str: "-",
500
- fn: ActionType.WRITE
501
- },
502
- [Keys.NEGATIVE]: {
503
- str: "-",
504
- fn: ActionType.WRITE
505
- },
506
- [Keys.TIMES]: {
507
- str: "\\times",
508
- fn: ActionType.WRITE
509
- },
510
- [Keys.DIVIDE]: {
511
- str: "\\div",
512
- fn: ActionType.WRITE
513
- },
514
- [Keys.DECIMAL]: {
515
- str: decimalSymbol,
516
- fn: ActionType.WRITE
517
- },
518
- [Keys.EQUAL]: {
519
- str: "=",
520
- fn: ActionType.WRITE
521
- },
522
- [Keys.NEQ]: {
523
- str: "\\neq",
524
- fn: ActionType.WRITE
525
- },
526
- [Keys.CDOT]: {
527
- str: "\\cdot",
528
- fn: ActionType.WRITE
529
- },
530
- [Keys.PERCENT]: {
531
- str: "%",
532
- fn: ActionType.WRITE
533
- },
534
- [Keys.LEFT_PAREN]: {
535
- str: "(",
536
- fn: ActionType.CMD
537
- },
538
- [Keys.RIGHT_PAREN]: {
539
- str: ")",
540
- fn: ActionType.CMD
541
- },
542
- [Keys.SQRT]: {
543
- str: "sqrt",
544
- fn: ActionType.CMD
545
- },
546
- [Keys.PI]: {
547
- str: "pi",
548
- fn: ActionType.CMD
549
- },
550
- [Keys.THETA]: {
551
- str: "theta",
552
- fn: ActionType.CMD
553
- },
554
- [Keys.RADICAL]: {
555
- str: "nthroot",
556
- fn: ActionType.CMD
557
- },
558
- [Keys.LT]: {
559
- str: "<",
560
- fn: ActionType.WRITE
561
- },
562
- [Keys.LEQ]: {
563
- str: "\\leq",
564
- fn: ActionType.WRITE
565
- },
566
- [Keys.GT]: {
567
- str: ">",
568
- fn: ActionType.WRITE
569
- },
570
- [Keys.GEQ]: {
571
- str: "\\geq",
572
- fn: ActionType.WRITE
573
- },
574
- [Keys.UP]: {
575
- str: "Up",
576
- fn: ActionType.KEYSTROKE
577
- },
578
- [Keys.DOWN]: {
579
- str: "Down",
580
- fn: ActionType.KEYSTROKE
581
- },
430
+ }(ActionType || {});
431
+ const decimalSymbol = decimalSeparator === DecimalSeparator.COMMA ? "," : ".";
432
+ function buildGenericCallback(str, type = ActionType.WRITE) {
433
+ return function (mathQuill) {
434
+ switch (type) {
435
+ case ActionType.WRITE:
436
+ {
437
+ mathQuill.write(str);
438
+ return;
439
+ }
440
+ case ActionType.CMD:
441
+ {
442
+ mathQuill.cmd(str);
443
+ return;
444
+ }
445
+ case ActionType.KEYSTROKE:
446
+ {
447
+ mathQuill.keystroke(str);
448
+ return;
449
+ }
450
+ }
451
+ };
452
+ }
453
+ const keyToMathquillMap = {
454
+ CDOT: buildGenericCallback("\\cdot"),
455
+ COS: buildGenericCallback("cos"),
456
+ DECIMAL: buildGenericCallback(decimalSymbol),
457
+ DIVIDE: buildGenericCallback("\\div"),
458
+ EQUAL: buildGenericCallback("="),
459
+ EXP: buildGenericCallback("^"),
460
+ EXP_2: buildGenericCallback("^2"),
461
+ EXP_3: buildGenericCallback("^3"),
462
+ GEQ: buildGenericCallback("\\geq"),
463
+ GT: buildGenericCallback(">"),
464
+ LEQ: buildGenericCallback("\\leq"),
465
+ LN: buildGenericCallback("\\ln"),
466
+ LOG: buildGenericCallback("\\log"),
467
+ LT: buildGenericCallback("<"),
468
+ MINUS: buildGenericCallback("-"),
469
+ NEGATIVE: buildGenericCallback("-"),
470
+ NEQ: buildGenericCallback("\\neq"),
471
+ PERCENT: buildGenericCallback("%"),
472
+ PERIOD: buildGenericCallback("."),
473
+ PLUS: buildGenericCallback("+"),
474
+ SIN: buildGenericCallback("sin"),
475
+ TAN: buildGenericCallback("tan"),
476
+ TIMES: buildGenericCallback("\\times"),
582
477
  // The `FRAC_EXCLUSIVE` variant is handled manually, since we may need to do
583
478
  // some additional navigation depending on the cursor position.
584
- [Keys.FRAC_INCLUSIVE]: {
585
- str: "/",
586
- fn: ActionType.CMD
587
- }
588
- };
589
- const NormalCommands = {
590
- [Keys.LOG]: "log",
591
- [Keys.LN]: "ln",
592
- [Keys.SIN]: "sin",
593
- [Keys.COS]: "cos",
594
- [Keys.TAN]: "tan"
479
+ FRAC_INCLUSIVE: buildGenericCallback("/", ActionType.CMD),
480
+ LEFT_PAREN: buildGenericCallback("(", ActionType.CMD),
481
+ RIGHT_PAREN: buildGenericCallback(")", ActionType.CMD),
482
+ SQRT: buildGenericCallback("sqrt", ActionType.CMD),
483
+ PHI: buildGenericCallback("\\phi", ActionType.CMD),
484
+ PI: buildGenericCallback("pi", ActionType.CMD),
485
+ THETA: buildGenericCallback("theta", ActionType.CMD),
486
+ RADICAL: buildGenericCallback("nthroot", ActionType.CMD),
487
+ UP: buildGenericCallback("Up", ActionType.KEYSTROKE),
488
+ DOWN: buildGenericCallback("Down", ActionType.KEYSTROKE),
489
+ CUBE_ROOT: mathQuill => {
490
+ mathQuill.write("\\sqrt[3]{}");
491
+ mathQuill.keystroke("Left"); // under the root
492
+ },
493
+
494
+ FRAC_EXCLUSIVE: mathQuill => {
495
+ const cursor = mathQuill.__controller.cursor;
496
+ // If there's nothing to the left of the cursor, then we want to
497
+ // leave the cursor to the left of the fraction after creating it.
498
+ const shouldNavigateLeft = cursor[MQ.L] === ActionType.MQ_END;
499
+ mathQuill.cmd("\\frac");
500
+ if (shouldNavigateLeft) {
501
+ mathQuill.keystroke("Left");
502
+ }
503
+ },
504
+ LOG_B: mathQuill => {
505
+ mathQuill.typedText("log_");
506
+ mathQuill.keystroke("Right");
507
+ mathQuill.typedText("(");
508
+ mathQuill.keystroke("Left");
509
+ mathQuill.keystroke("Left");
510
+ },
511
+ LOG_N: mathQuill => {
512
+ mathQuill.write("log_{ }\\left(\\right)");
513
+ mathQuill.keystroke("Left"); // into parentheses
514
+ mathQuill.keystroke("Left"); // out of parentheses
515
+ mathQuill.keystroke("Left"); // into index
516
+ },
517
+
518
+ NTHROOT3: mathQuill => {
519
+ mathQuill.typedText("nthroot3");
520
+ mathQuill.keystroke("Right");
521
+ },
522
+ POW: mathQuill => {
523
+ const contents = mathQuill.latex();
524
+ mathQuill.typedText("^");
525
+
526
+ // If the input hasn't changed (for example, if we're
527
+ // attempting to add an exponent on an empty input or an empty
528
+ // denominator), insert our own "a^b"
529
+ if (mathQuill.latex() === contents) {
530
+ mathQuill.typedText("a^b");
531
+ }
532
+ },
533
+ // These need to be overwritten by the consumer
534
+ // if they're going to be used
535
+ FRAC: () => {},
536
+ RIGHT: () => {},
537
+ LEFT: () => {},
538
+ BACKSPACE: () => {},
539
+ DISMISS: () => {},
540
+ JUMP_OUT_PARENTHESES: () => {},
541
+ JUMP_OUT_EXPONENT: () => {},
542
+ JUMP_OUT_BASE: () => {},
543
+ JUMP_INTO_NUMERATOR: () => {},
544
+ JUMP_OUT_NUMERATOR: () => {},
545
+ JUMP_OUT_DENOMINATOR: () => {},
546
+ NOOP: () => {},
547
+ MANY: () => {},
548
+ NUM_0: buildGenericCallback("0"),
549
+ NUM_1: buildGenericCallback("1"),
550
+ NUM_2: buildGenericCallback("2"),
551
+ NUM_3: buildGenericCallback("3"),
552
+ NUM_4: buildGenericCallback("4"),
553
+ NUM_5: buildGenericCallback("5"),
554
+ NUM_6: buildGenericCallback("6"),
555
+ NUM_7: buildGenericCallback("7"),
556
+ NUM_8: buildGenericCallback("8"),
557
+ NUM_9: buildGenericCallback("9"),
558
+ a: buildGenericCallback("a"),
559
+ b: buildGenericCallback("b"),
560
+ c: buildGenericCallback("c"),
561
+ d: buildGenericCallback("d"),
562
+ e: buildGenericCallback("e"),
563
+ f: buildGenericCallback("f"),
564
+ g: buildGenericCallback("g"),
565
+ h: buildGenericCallback("h"),
566
+ i: buildGenericCallback("i"),
567
+ j: buildGenericCallback("j"),
568
+ k: buildGenericCallback("k"),
569
+ l: buildGenericCallback("l"),
570
+ m: buildGenericCallback("m"),
571
+ n: buildGenericCallback("n"),
572
+ o: buildGenericCallback("o"),
573
+ p: buildGenericCallback("p"),
574
+ q: buildGenericCallback("q"),
575
+ r: buildGenericCallback("r"),
576
+ s: buildGenericCallback("s"),
577
+ t: buildGenericCallback("t"),
578
+ u: buildGenericCallback("u"),
579
+ v: buildGenericCallback("v"),
580
+ w: buildGenericCallback("w"),
581
+ x: buildGenericCallback("x"),
582
+ y: buildGenericCallback("y"),
583
+ z: buildGenericCallback("z"),
584
+ A: buildGenericCallback("A"),
585
+ B: buildGenericCallback("B"),
586
+ C: buildGenericCallback("C"),
587
+ D: buildGenericCallback("D"),
588
+ E: buildGenericCallback("E"),
589
+ F: buildGenericCallback("F"),
590
+ G: buildGenericCallback("G"),
591
+ H: buildGenericCallback("H"),
592
+ I: buildGenericCallback("I"),
593
+ J: buildGenericCallback("J"),
594
+ K: buildGenericCallback("K"),
595
+ L: buildGenericCallback("L"),
596
+ M: buildGenericCallback("M"),
597
+ N: buildGenericCallback("N"),
598
+ O: buildGenericCallback("O"),
599
+ P: buildGenericCallback("P"),
600
+ Q: buildGenericCallback("Q"),
601
+ R: buildGenericCallback("R"),
602
+ S: buildGenericCallback("S"),
603
+ T: buildGenericCallback("T"),
604
+ U: buildGenericCallback("U"),
605
+ V: buildGenericCallback("V"),
606
+ W: buildGenericCallback("W"),
607
+ X: buildGenericCallback("X"),
608
+ Y: buildGenericCallback("Y"),
609
+ Z: buildGenericCallback("Z")
595
610
  };
596
- const ArithmeticOperators = ["+", "-", "\\cdot", "\\times", "\\div"];
597
- const EqualityOperators = ["=", "\\neq", "<", "\\leq", ">", "\\geq"];
611
+
612
+ let MathFieldActionType = /*#__PURE__*/function (MathFieldActionType) {
613
+ MathFieldActionType["WRITE"] = "write";
614
+ MathFieldActionType["CMD"] = "cmd";
615
+ MathFieldActionType["KEYSTROKE"] = "keystroke";
616
+ MathFieldActionType[MathFieldActionType["MQ_END"] = 0] = "MQ_END";
617
+ return MathFieldActionType;
618
+ }({});
619
+
620
+ // The MathQuill MathField Cursor
621
+ // it's not part of the public API for MathQuill,
622
+ // we reach into the internals to get it
623
+
598
624
  const Numerals = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
599
625
  const GreekLetters = ["\\theta", "\\pi"];
600
626
  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"];
@@ -602,24 +628,623 @@ const Letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"
602
628
  // We only consider numerals, variables, and Greek Letters to be proper
603
629
  // leaf nodes.
604
630
  const ValidLeaves = [...Numerals, ...GreekLetters, ...Letters.map(letter => letter.toLowerCase()), ...Letters.map(letter => letter.toUpperCase())];
631
+ function getCursor(mathField) {
632
+ return mathField.__controller.cursor;
633
+ }
634
+ function isFraction(node) {
635
+ return node.jQ && node.jQ.hasClass("mq-fraction");
636
+ }
637
+ function isNumerator(node) {
638
+ return node.jQ && node.jQ.hasClass("mq-numerator");
639
+ }
640
+ function isDenominator(node) {
641
+ return node.jQ && node.jQ.hasClass("mq-denominator");
642
+ }
643
+ function isSubScript(node) {
644
+ // NOTE(charlie): MyScript has a structure whereby its superscripts seem
645
+ // to be represented as a parent node with 'mq-sup-only' containing a
646
+ // single child with 'mq-sup'.
647
+ return node.jQ && (node.jQ.hasClass("mq-sub-only") || node.jQ.hasClass("mq-sub"));
648
+ }
649
+ function isSuperScript(node) {
650
+ // NOTE(charlie): MyScript has a structure whereby its superscripts seem
651
+ // to be represented as a parent node with 'mq-sup-only' containing a
652
+ // single child with 'mq-sup'.
653
+ return node.jQ && (node.jQ.hasClass("mq-sup-only") || node.jQ.hasClass("mq-sup"));
654
+ }
655
+ function isParens(node) {
656
+ return node && node.ctrlSeq === "\\left(";
657
+ }
658
+ function isLeaf(node) {
659
+ return node && node.ctrlSeq && ValidLeaves.includes(node.ctrlSeq.trim());
660
+ }
661
+ function isSquareRoot(node) {
662
+ return node.blocks && node.blocks[0].jQ && node.blocks[0].jQ.hasClass("mq-sqrt-stem");
663
+ }
664
+ function isNthRoot(node) {
665
+ return node.blocks && node.blocks[0].jQ && node.blocks[0].jQ.hasClass("mq-nthroot");
666
+ }
667
+ function isNthRootIndex(node) {
668
+ return node.jQ && node.jQ.hasClass("mq-nthroot");
669
+ }
670
+ function isInsideLogIndex(cursor) {
671
+ const grandparent = cursor.parent.parent;
672
+ if (grandparent && grandparent.jQ.hasClass("mq-supsub")) {
673
+ const command = maybeFindCommandBeforeParens(grandparent);
674
+ if (command && command.name === "\\log") {
675
+ return true;
676
+ }
677
+ }
678
+ return false;
679
+ }
680
+ function isInsideEmptyNode(cursor) {
681
+ return cursor[MQ.L] === MathFieldActionType.MQ_END && cursor[MQ.R] === MathFieldActionType.MQ_END;
682
+ }
683
+ function selectNode(node, cursor) {
684
+ cursor.insLeftOf(node);
685
+ cursor.startSelection();
686
+ cursor.insRightOf(node);
687
+ cursor.select();
688
+ cursor.endSelection();
689
+ }
690
+
691
+ /**
692
+ * Return the start node, end node, and full name of the command of which
693
+ * the initial node is a part, or `null` if the node is not part of a
694
+ * command.
695
+ *
696
+ * @param {node} initialNode - the node to included as part of the command
697
+ * @returns {null|object} - `null` or an object containing the start node
698
+ * (`startNode`), end node (`endNode`), and full
699
+ * name (`name`) of the command
700
+ */
701
+ function maybeFindCommand(initialNode) {
702
+ if (!initialNode) {
703
+ return null;
704
+ }
705
+
706
+ // MathQuill stores commands as separate characters so that
707
+ // users can delete commands one character at a time. We iterate over
708
+ // the nodes from right to left until we hit a sequence starting with a
709
+ // '\\', which signifies the start of a command; then we iterate from
710
+ // left to right until we hit a '\\left(', which signifies the end of a
711
+ // command. If we encounter any character that doesn't belong in a
712
+ // command, we return null. We match a single character at a time.
713
+ // Ex) ['\\l', 'o', 'g ', '\\left(', ...]
714
+ const commandCharRegex = /^[a-z]$/;
715
+ const commandStartRegex = /^\\[a-z]$/;
716
+ const commandEndSeq = "\\left(";
717
+
718
+ // Note: We allowlist the set of valid commands, since relying solely on
719
+ // a command being prefixed with a backslash leads to undesired
720
+ // behavior. For example, Greek symbols, left parentheses, and square
721
+ // roots all get treated as commands.
722
+ const validCommands = ["\\log", "\\ln", "\\cos", "\\sin", "\\tan"];
723
+ let name = "";
724
+ let startNode;
725
+ let endNode;
726
+
727
+ // Collect the portion of the command from the current node, leftwards
728
+ // until the start of the command.
729
+ let node = initialNode;
730
+ while (node !== 0) {
731
+ const ctrlSeq = node.ctrlSeq.trim();
732
+ if (commandCharRegex.test(ctrlSeq)) {
733
+ name = ctrlSeq + name;
734
+ } else if (commandStartRegex.test(ctrlSeq)) {
735
+ name = ctrlSeq + name;
736
+ startNode = node;
737
+ break;
738
+ } else {
739
+ break;
740
+ }
741
+ node = node[MQ.L];
742
+ }
743
+
744
+ // If we hit the start of a command, then grab the rest of it by
745
+ // iterating rightwards to compute the full name of the command, along
746
+ // with its terminal node.
747
+ if (startNode) {
748
+ // Next, iterate from the start to the right.
749
+ node = initialNode[MQ.R];
750
+ while (node !== 0) {
751
+ const ctrlSeq = node.ctrlSeq.trim();
752
+ if (commandCharRegex.test(ctrlSeq)) {
753
+ // If we have a single character, add it to the command
754
+ // name.
755
+ name = name + ctrlSeq;
756
+ } else if (ctrlSeq === commandEndSeq) {
757
+ // If we hit the command end delimiter (the left
758
+ // parentheses surrounding its arguments), stop.
759
+ endNode = node;
760
+ break;
761
+ }
762
+ node = node[MQ.R];
763
+ }
764
+ if (validCommands.includes(name)) {
765
+ return {
766
+ name,
767
+ startNode,
768
+ endNode
769
+ };
770
+ } else {
771
+ return null;
772
+ }
773
+ } else {
774
+ return null;
775
+ }
776
+ }
777
+
778
+ /**
779
+ * Return the start node, end node, and full name of the command to the left
780
+ * of `\\left(`, or `null` if there is no command.
781
+ *
782
+ * @param {node} leftParenNode - node where .ctrlSeq == `\\left(`
783
+ * @returns {null|object} - `null` or an object containing the start node
784
+ * (`startNode`), end node (`endNode`), and full
785
+ * name (`name`) of the command
786
+ */
787
+ function maybeFindCommandBeforeParens(leftParenNode) {
788
+ return maybeFindCommand(leftParenNode[MQ.L]);
789
+ }
790
+ function contextForCursor(cursor) {
791
+ // First, try to find any fraction to the right, unimpeded.
792
+ let visitor = cursor;
793
+ while (visitor[MQ.R] !== MathFieldActionType.MQ_END) {
794
+ if (isFraction(visitor[MQ.R])) {
795
+ return CursorContext.BEFORE_FRACTION;
796
+ } else if (!isLeaf(visitor[MQ.R])) {
797
+ break;
798
+ }
799
+ visitor = visitor[MQ.R];
800
+ }
801
+
802
+ // If that didn't work, check if the parent or grandparent is a special
803
+ // context, so that we can jump outwards.
804
+ if (isParens(cursor.parent && cursor.parent.parent)) {
805
+ return CursorContext.IN_PARENS;
806
+ } else if (isNumerator(cursor.parent)) {
807
+ return CursorContext.IN_NUMERATOR;
808
+ } else if (isDenominator(cursor.parent)) {
809
+ return CursorContext.IN_DENOMINATOR;
810
+ } else if (isSubScript(cursor.parent)) {
811
+ return CursorContext.IN_SUB_SCRIPT;
812
+ } else if (isSuperScript(cursor.parent)) {
813
+ return CursorContext.IN_SUPER_SCRIPT;
814
+ } else {
815
+ return CursorContext.NONE;
816
+ }
817
+ }
818
+
819
+ function handleLeftArrow(mathField, cursor) {
820
+ // If we're inside a function, and just after the left parentheses, we
821
+ // need to skip the entire function name, rather than move the cursor
822
+ // inside of it. For example, when hitting left from within the
823
+ // parentheses in `cos()`, we want to place the cursor to the left of
824
+ // the entire expression, rather than between the `s` and the left
825
+ // parenthesis.
826
+ // From the cursor's perspective, this requires that our left node is
827
+ // the ActionType.MQ_END node, that our grandparent is the left parenthesis, and
828
+ // the nodes to the left of our grandparent comprise a valid function
829
+ // name.
830
+ if (cursor[MQ.L] === MathFieldActionType.MQ_END) {
831
+ const parent = cursor.parent;
832
+ const grandparent = parent.parent;
833
+ if (grandparent.ctrlSeq === "\\left(") {
834
+ const command = maybeFindCommandBeforeParens(grandparent);
835
+ if (command) {
836
+ cursor.insLeftOf(command.startNode);
837
+ return;
838
+ }
839
+ }
840
+ }
841
+
842
+ // Otherwise, we default to the standard MathQull left behavior.
843
+ mathField.keystroke("Left");
844
+ }
845
+ function handleRightArrow(mathField, cursor) {
846
+ const command = maybeFindCommand(cursor[MQ.R]);
847
+ if (command) {
848
+ // Similarly, if a function is to our right, then we need to place
849
+ // the cursor at the start of its parenthetical content, which is
850
+ // done by putting it to the left of ites parentheses and then
851
+ // moving right once.
852
+ cursor.insLeftOf(command.endNode);
853
+ mathField.keystroke("Right");
854
+ } else {
855
+ // Otherwise, we default to the standard MathQull right behavior.
856
+ mathField.keystroke("Right");
857
+ }
858
+ }
859
+ function handleArrow(mathField, key) {
860
+ const cursor = getCursor(mathField);
861
+ if (key === "LEFT") {
862
+ handleLeftArrow(mathField, cursor);
863
+ } else if (key === "RIGHT") {
864
+ handleRightArrow(mathField, cursor);
865
+ }
866
+ }
867
+
868
+ function handleBackspaceInNthRoot(mathField, cursor) {
869
+ const isAtLeftEnd = cursor[MQ.L] === MathFieldActionType.MQ_END;
870
+ const isRootEmpty = isInsideEmptyNode(cursor.parent.parent.blocks[0].ends);
871
+ if (isAtLeftEnd) {
872
+ selectNode(cursor.parent.parent, cursor);
873
+ if (isRootEmpty) {
874
+ mathField.keystroke("Backspace");
875
+ }
876
+ } else {
877
+ mathField.keystroke("Backspace");
878
+ }
879
+ }
880
+ function handleBackspaceInRootIndex(mathField, cursor) {
881
+ if (isInsideEmptyNode(cursor)) {
882
+ // When deleting the index in a nthroot, we change from the nthroot
883
+ // to a sqrt, e.g. \sqrt[|]{35x-5} => |\sqrt{35x-5}. If there's no
884
+ // content under the root, then we delete the whole thing.
885
+
886
+ const grandparent = cursor.parent.parent;
887
+ const latex = grandparent.latex();
888
+ const reinsertionPoint = grandparent[MQ.L];
889
+ selectNode(grandparent, cursor);
890
+ const rootIsEmpty = grandparent.blocks[1].jQ.text() === "";
891
+ if (rootIsEmpty) {
892
+ // If there is not content under the root then simply delete
893
+ // the whole thing.
894
+ mathField.keystroke("Backspace");
895
+ } else {
896
+ // Replace the nthroot with a sqrt if there was content under
897
+ // the root.
898
+
899
+ // Start by deleting the selection.
900
+ mathField.keystroke("Backspace");
901
+
902
+ // Replace the nth-root with a sqrt.
903
+ mathField.write(latex.replace(/^\\sqrt\[\]/, "\\sqrt"));
904
+
905
+ // Adjust the cursor to be to the left the sqrt.
906
+ if (reinsertionPoint === MathFieldActionType.MQ_END) {
907
+ mathField.moveToDirEnd(MQ.L);
908
+ } else {
909
+ cursor.insRightOf(reinsertionPoint);
910
+ }
911
+ }
912
+ } else {
913
+ if (cursor[MQ.L] !== MathFieldActionType.MQ_END) {
914
+ // If the cursor is not at the leftmost position inside the
915
+ // root's index, delete a character.
916
+ mathField.keystroke("Backspace");
917
+ }
918
+ }
919
+ }
920
+ function handleBackspaceInLogIndex(mathField, cursor) {
921
+ if (isInsideEmptyNode(cursor)) {
922
+ const grandparent = cursor.parent.parent;
923
+ const command = maybeFindCommandBeforeParens(grandparent);
924
+ cursor.insLeftOf(command == null ? void 0 : command.startNode);
925
+ cursor.startSelection();
926
+ if (grandparent[MQ.R] !== MathFieldActionType.MQ_END) {
927
+ cursor.insRightOf(grandparent[MQ.R]);
928
+ } else {
929
+ cursor.insRightOf(grandparent);
930
+ }
931
+ cursor.select();
932
+ cursor.endSelection();
933
+ const isLogBodyEmpty = grandparent[MQ.R].contentjQ.text() === "";
934
+ if (isLogBodyEmpty) {
935
+ // If there's no content inside the log's parens then delete the
936
+ // whole thing.
937
+ mathField.keystroke("Backspace");
938
+ }
939
+ } else {
940
+ mathField.keystroke("Backspace");
941
+ }
942
+ }
943
+ function handleBackspaceOutsideParens(cursor) {
944
+ // In this case the node with '\\left(' for its ctrlSeq
945
+ // is the parent of the expression contained within the
946
+ // parentheses.
947
+ //
948
+ // Handle selecting an expression before deleting:
949
+ // (x+1)| => |(x+1)|
950
+ // \log(x+1)| => |\log(x+1)|
951
+
952
+ const leftNode = cursor[MQ.L];
953
+ const rightNode = cursor[MQ.R];
954
+ const command = maybeFindCommandBeforeParens(leftNode);
955
+ if (command && command.startNode) {
956
+ // There's a command before the parens so we select it as well as
957
+ // the parens.
958
+ cursor.insLeftOf(command.startNode);
959
+ cursor.startSelection();
960
+ if (rightNode === MathFieldActionType.MQ_END) {
961
+ cursor.insAtRightEnd(cursor.parent);
962
+ } else {
963
+ cursor.insLeftOf(rightNode);
964
+ }
965
+ cursor.select();
966
+ cursor.endSelection();
967
+ } else {
968
+ cursor.startSelection();
969
+ cursor.insLeftOf(leftNode); // left of \\left(
970
+ cursor.select();
971
+ cursor.endSelection();
972
+ }
973
+ }
974
+ function handleBackspaceInsideParens(mathField, cursor) {
975
+ // Handle situations when the cursor is inside parens or a
976
+ // command that uses parens, e.g. \log() or \tan()
977
+ //
978
+ // MathQuill represents log(x+1) in roughly the following way
979
+ // [l, o, g, \\left[parent:[x, +, 1]]]
980
+ //
981
+ // If the cursor is inside the parentheses it's next to one of:
982
+ // x, +, or 1. This makes sub_sub_expr its parent and sub_expr
983
+ // it's parent.
984
+ //
985
+ // Interestingly parent doesn't have any nodes to the left or
986
+ // right of it (even though the corresponding DOM node has
987
+ // ( and ) characters on either side.
988
+ //
989
+ // The grandparent's ctrlSeq is `\\left(`. The `\\right)` isn't
990
+ // stored anywhere. NOTE(kevinb): I believe this is because
991
+ // MathQuill knows what the close paren should be and does the
992
+ // right thing at render time.
993
+ //
994
+ // This conditional branch handles the following cases:
995
+ // - \log(x+1|) => \log(x+|)
996
+ // - \log(|x+1) => |\log(x+1)|
997
+ // - \log(|) => |
998
+
999
+ if (cursor[MQ.L] !== MathFieldActionType.MQ_END) {
1000
+ // This command contains math and there's some math to
1001
+ // the left of the cursor that we should delete normally
1002
+ // before doing anything special.
1003
+ mathField.keystroke("Backspace");
1004
+ return;
1005
+ }
1006
+ const grandparent = cursor.parent.parent;
1007
+
1008
+ // If the cursors is inside the parens at the start but the command
1009
+ // has a subscript as is the case in log_n then move the cursor into
1010
+ // the subscript, e.g. \log_{5}(|x+1) => \log_{5|}(x+1)
1011
+
1012
+ if (grandparent[MQ.L].sub) {
1013
+ // if there is a subscript
1014
+ if (grandparent[MQ.L].sub.jQ.text()) {
1015
+ // and it contains text
1016
+ // move the cursor to the right end of the subscript
1017
+ cursor.insAtRightEnd(grandparent[MQ.L].sub);
1018
+ return;
1019
+ }
1020
+ }
1021
+
1022
+ // Determine if the parens are empty before we modify the
1023
+ // cursor's position.
1024
+ const isEmpty = isInsideEmptyNode(cursor);
1025
+
1026
+ // Insert the cursor to the left of the command if there is one
1027
+ // or before the '\\left(` if there isn't
1028
+ const command = maybeFindCommandBeforeParens(grandparent);
1029
+ cursor.insLeftOf(command && command.startNode || grandparent);
1030
+ cursor.startSelection();
1031
+ cursor.insRightOf(grandparent);
1032
+ cursor.select();
1033
+ cursor.endSelection();
1034
+
1035
+ // Delete the selection, but only if the parens were empty to
1036
+ // begin with.
1037
+ if (isEmpty) {
1038
+ mathField.keystroke("Backspace");
1039
+ }
1040
+ }
1041
+ function handleBackspaceAfterLigaturedSymbol(mathField) {
1042
+ mathField.keystroke("Backspace");
1043
+ mathField.keystroke("Backspace");
1044
+ }
1045
+
1046
+ /**
1047
+ * Selects and deletes part of the expression based on the cursor location.
1048
+ * See inline comments for precise behavior of different cases.
1049
+ */
1050
+ function handleBackspace(mathField) {
1051
+ const cursor = getCursor(mathField);
1052
+ if (!cursor.selection) {
1053
+ const parent = cursor.parent;
1054
+ const grandparent = parent.parent;
1055
+ const leftNode = cursor[MQ.L];
1056
+ if (isFraction(leftNode)) {
1057
+ selectNode(leftNode, cursor);
1058
+ } else if (isSquareRoot(leftNode)) {
1059
+ selectNode(leftNode, cursor);
1060
+ } else if (isNthRoot(leftNode)) {
1061
+ selectNode(leftNode, cursor);
1062
+ } else if (isNthRootIndex(parent)) {
1063
+ handleBackspaceInRootIndex(mathField, cursor);
1064
+ } else if (leftNode.ctrlSeq === "\\left(") {
1065
+ handleBackspaceOutsideParens(cursor);
1066
+ } else if (grandparent.ctrlSeq === "\\left(") {
1067
+ handleBackspaceInsideParens(mathField, cursor);
1068
+ } else if (isInsideLogIndex(cursor)) {
1069
+ handleBackspaceInLogIndex(mathField, cursor);
1070
+ } else if (leftNode.ctrlSeq === "\\ge " || leftNode.ctrlSeq === "\\le ") {
1071
+ handleBackspaceAfterLigaturedSymbol(mathField);
1072
+ } else if (isNthRoot(grandparent) && leftNode === MathFieldActionType.MQ_END) {
1073
+ handleBackspaceInNthRoot(mathField, cursor);
1074
+ } else {
1075
+ mathField.keystroke("Backspace");
1076
+ }
1077
+ } else {
1078
+ mathField.keystroke("Backspace");
1079
+ }
1080
+ }
1081
+
1082
+ const ArithmeticOperators = ["+", "-", "\\cdot", "\\times", "\\div"];
1083
+ const EqualityOperators = ["=", "\\neq", "<", "\\leq", ">", "\\geq"];
1084
+ function handleExponent(mathField, key) {
1085
+ const cursor = getCursor(mathField);
1086
+ // If there's an invalid operator preceding the cursor (anything that
1087
+ // knowingly cannot be raised to a power), add an empty set of
1088
+ // parentheses and apply the exponent to that.
1089
+ const invalidPrefixes = [...ArithmeticOperators, ...EqualityOperators];
1090
+ const precedingNode = cursor[MQ.L];
1091
+ const shouldPrefixWithParens = precedingNode === MathFieldActionType.MQ_END || invalidPrefixes.includes(precedingNode.ctrlSeq.trim());
1092
+ if (shouldPrefixWithParens) {
1093
+ mathField.write("\\left(\\right)");
1094
+ }
1095
+
1096
+ // Insert the appropriate exponent operator.
1097
+ switch (key) {
1098
+ case "EXP":
1099
+ mathField.cmd("^");
1100
+ break;
1101
+ case "EXP_2":
1102
+ case "EXP_3":
1103
+ mathField.write(`^${key === "EXP_2" ? 2 : 3}`);
1104
+
1105
+ // If we enter a square or a cube, we should leave the cursor
1106
+ // within the newly inserted parens, if they exist. This takes
1107
+ // exactly four left strokes, since the cursor by default would
1108
+ // end up to the right of the exponent.
1109
+ if (shouldPrefixWithParens) {
1110
+ mathField.keystroke("Left");
1111
+ mathField.keystroke("Left");
1112
+ mathField.keystroke("Left");
1113
+ mathField.keystroke("Left");
1114
+ }
1115
+ break;
1116
+ default:
1117
+ throw new Error(`Invalid exponent key: ${key}`);
1118
+ }
1119
+ }
1120
+
605
1121
  const KeysForJumpContext = {
606
- [CursorContext.IN_PARENS]: Keys.JUMP_OUT_PARENTHESES,
607
- [CursorContext.IN_SUPER_SCRIPT]: Keys.JUMP_OUT_EXPONENT,
608
- [CursorContext.IN_SUB_SCRIPT]: Keys.JUMP_OUT_BASE,
609
- [CursorContext.BEFORE_FRACTION]: Keys.JUMP_INTO_NUMERATOR,
610
- [CursorContext.IN_NUMERATOR]: Keys.JUMP_OUT_NUMERATOR,
611
- [CursorContext.IN_DENOMINATOR]: Keys.JUMP_OUT_DENOMINATOR
1122
+ [CursorContext.IN_PARENS]: "JUMP_OUT_PARENTHESES",
1123
+ [CursorContext.IN_SUPER_SCRIPT]: "JUMP_OUT_EXPONENT",
1124
+ [CursorContext.IN_SUB_SCRIPT]: "JUMP_OUT_BASE",
1125
+ [CursorContext.BEFORE_FRACTION]: "JUMP_INTO_NUMERATOR",
1126
+ [CursorContext.IN_NUMERATOR]: "JUMP_OUT_NUMERATOR",
1127
+ [CursorContext.IN_DENOMINATOR]: "JUMP_OUT_DENOMINATOR"
612
1128
  };
1129
+
1130
+ /**
1131
+ * Advances the cursor to the next logical position.
1132
+ */
1133
+ function handleJumpOut(mathField, key) {
1134
+ const cursor = getCursor(mathField);
1135
+ const context = contextForCursor(cursor);
1136
+
1137
+ // Validate that the current cursor context matches the key's intent.
1138
+ if (KeysForJumpContext[context] !== key) {
1139
+ // If we don't have a valid cursor context, yet the user was able
1140
+ // to trigger a jump-out key, that's a broken invariant. Rather
1141
+ // than throw an error (which would kick the user out of the
1142
+ // exercise), we do nothing, as a fallback strategy. The user can
1143
+ // still move the cursor manually.
1144
+ return;
1145
+ }
1146
+ switch (context) {
1147
+ case CursorContext.IN_PARENS:
1148
+ // Insert at the end of the parentheses, and then navigate right
1149
+ // once more to get 'beyond' the parentheses.
1150
+ cursor.insRightOf(cursor.parent.parent);
1151
+ break;
1152
+ case CursorContext.BEFORE_FRACTION:
1153
+ // Find the nearest fraction to the right of the cursor.
1154
+ let fractionNode;
1155
+ let visitor = cursor;
1156
+ while (visitor[MQ.R] !== MathFieldActionType.MQ_END) {
1157
+ if (isFraction(visitor[MQ.R])) {
1158
+ fractionNode = visitor[MQ.R];
1159
+ }
1160
+ visitor = visitor[MQ.R];
1161
+ }
1162
+
1163
+ // Jump into it!
1164
+ cursor.insLeftOf(fractionNode);
1165
+ mathField.keystroke("Right");
1166
+ break;
1167
+ case CursorContext.IN_NUMERATOR:
1168
+ // HACK(charlie): I can't find a better way to do this. The goal
1169
+ // is to place the cursor at the start of the matching
1170
+ // denominator. So, we identify the appropriate node, and
1171
+ // continue rightwards until we find ourselves inside of it.
1172
+ // It's possible that there are cases in which we don't reach
1173
+ // the denominator, though I can't think of any.
1174
+ const siblingDenominator = cursor.parent.parent.blocks[1];
1175
+ while (cursor.parent !== siblingDenominator) {
1176
+ mathField.keystroke("Right");
1177
+ }
1178
+ break;
1179
+ case CursorContext.IN_DENOMINATOR:
1180
+ cursor.insRightOf(cursor.parent.parent);
1181
+ break;
1182
+ case CursorContext.IN_SUB_SCRIPT:
1183
+ // Insert just beyond the superscript.
1184
+ cursor.insRightOf(cursor.parent.parent);
1185
+
1186
+ // Navigate right once more, if we're right before parens. This
1187
+ // is to handle the standard case in which the subscript is the
1188
+ // base of a custom log.
1189
+ if (isParens(cursor[MQ.R])) {
1190
+ mathField.keystroke("Right");
1191
+ }
1192
+ break;
1193
+ case CursorContext.IN_SUPER_SCRIPT:
1194
+ // Insert just beyond the superscript.
1195
+ cursor.insRightOf(cursor.parent.parent);
1196
+ break;
1197
+ default:
1198
+ throw new Error(`Attempted to 'Jump Out' from node, but found no ` + `appropriate cursor context: ${context}`);
1199
+ }
1200
+ }
1201
+
1202
+ function buildNormalFunctionCallback(command) {
1203
+ return function (mathField) {
1204
+ mathField.write(`\\${command}\\left(\\right)`);
1205
+ mathField.keystroke("Left");
1206
+ };
1207
+ }
1208
+ const customKeyTranslator = _extends({}, keyToMathquillMap, {
1209
+ // note(Matthew): in all likelihood, this should be moved
1210
+ // to the shared key2MathQuill translator. During this refactor
1211
+ // I tried to keep logic the same while deduplicating code.
1212
+ // Perseus' Expression MathInput treats this stuff differently
1213
+ // (or doesn't do anything with them at all), so I kept it that way
1214
+ BACKSPACE: handleBackspace,
1215
+ EXP: handleExponent,
1216
+ EXP_2: handleExponent,
1217
+ EXP_3: handleExponent,
1218
+ FRAC: mathQuill => {
1219
+ mathQuill.cmd("\\frac");
1220
+ },
1221
+ JUMP_OUT_PARENTHESES: handleJumpOut,
1222
+ JUMP_OUT_EXPONENT: handleJumpOut,
1223
+ JUMP_OUT_BASE: handleJumpOut,
1224
+ JUMP_INTO_NUMERATOR: handleJumpOut,
1225
+ JUMP_OUT_NUMERATOR: handleJumpOut,
1226
+ JUMP_OUT_DENOMINATOR: handleJumpOut,
1227
+ LEFT: handleArrow,
1228
+ RIGHT: handleArrow,
1229
+ LOG: buildNormalFunctionCallback("log"),
1230
+ LN: buildNormalFunctionCallback("ln"),
1231
+ SIN: buildNormalFunctionCallback("sin"),
1232
+ COS: buildNormalFunctionCallback("cos"),
1233
+ TAN: buildNormalFunctionCallback("tan")
1234
+ });
1235
+
1236
+ /**
1237
+ * This file contains a wrapper around MathQuill so that we can provide a
1238
+ * more regular interface for the functionality we need while insulating us
1239
+ * from MathQuill changes.
1240
+ */
613
1241
  class MathWrapper {
614
- // MathQuill interface
615
1242
  // MathQuill input
616
1243
 
617
1244
  constructor(element, options = {}, callbacks = {}) {
618
- this.MQ = void 0;
619
1245
  this.mathField = void 0;
620
1246
  this.callbacks = void 0;
621
- this.MQ = MathQuill.getInterface(2);
622
- this.mathField = this.MQ.MathField(element, {
1247
+ this.mathField = MQ.MathField(element, {
623
1248
  // use a span instead of a textarea so that we don't bring up the
624
1249
  // native keyboard on mobile when selecting the input
625
1250
  substituteTextarea: function () {
@@ -645,10 +1270,6 @@ class MathWrapper {
645
1270
  controller.cursor.hide();
646
1271
  controller.blurred = true;
647
1272
  }
648
- _writeNormalFunction(name) {
649
- this.mathField.write(`\\${name}\\left(\\right)`);
650
- this.mathField.keystroke("Left");
651
- }
652
1273
 
653
1274
  /**
654
1275
  * Handle a key press and return the resulting cursor state.
@@ -657,51 +1278,10 @@ class MathWrapper {
657
1278
  * @returns {object} a cursor object, consisting of a cursor context
658
1279
  */
659
1280
  pressKey(key) {
660
- const cursor = this.mathField.__controller.cursor;
661
- if (key in KeyActions) {
662
- const {
663
- str,
664
- fn
665
- } = KeyActions[key];
666
- if (str && fn) {
667
- this.mathField[fn](str);
668
- }
669
- } else if (Object.keys(NormalCommands).includes(key)) {
670
- this._writeNormalFunction(NormalCommands[key]);
671
- } else if (key === Keys.FRAC_EXCLUSIVE) {
672
- // If there's nothing to the left of the cursor, then we want to
673
- // leave the cursor to the left of the fraction after creating it.
674
- const shouldNavigateLeft = cursor[this.MQ.L] === ActionType.MQ_END;
675
- this.mathField.cmd("\\frac");
676
- if (shouldNavigateLeft) {
677
- this.mathField.keystroke("Left");
678
- }
679
- } else if (key === Keys.FRAC) {
680
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
681
- cursor[this.MQ.L] === ActionType.MQ_END;
682
- this.mathField.cmd("\\frac");
683
- } else if (key === Keys.LOG_N) {
684
- this.mathField.write("log_{ }\\left(\\right)");
685
- this.mathField.keystroke("Left"); // into parentheses
686
- this.mathField.keystroke("Left"); // out of parentheses
687
- this.mathField.keystroke("Left"); // into index
688
- } else if (key === Keys.CUBE_ROOT) {
689
- this.mathField.write("\\sqrt[3]{}");
690
- this.mathField.keystroke("Left"); // under the root
691
- } else if (key === Keys.EXP || key === Keys.EXP_2 || key === Keys.EXP_3) {
692
- this._handleExponent(cursor, key);
693
- } 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) {
694
- this._handleJumpOut(cursor, key);
695
- } else if (key === Keys.BACKSPACE) {
696
- this._handleBackspace(cursor);
697
- } else if (key === Keys.LEFT) {
698
- this._handleLeftArrow(cursor);
699
- } else if (key === Keys.RIGHT) {
700
- this._handleRightArrow(cursor);
701
- } else if (/^[a-zA-Z]$/.test(key)) {
702
- this.mathField[ActionType.WRITE](key);
703
- } else if (/^NUM_\d/.test(key)) {
704
- this.mathField[ActionType.WRITE](key[4]);
1281
+ const cursor = this.getCursor();
1282
+ const translator = customKeyTranslator[key];
1283
+ if (translator) {
1284
+ translator(this.mathField, key);
705
1285
  }
706
1286
  if (!cursor.selection) {
707
1287
  // don't show the cursor for selections
@@ -747,7 +1327,7 @@ class MathWrapper {
747
1327
  // Unless that would leave us mid-command, in which case, we
748
1328
  // need to adjust and place the cursor inside the parens
749
1329
  // following the command.
750
- const command = this._maybeFindCommand(cursor[this.MQ.L]);
1330
+ const command = maybeFindCommand(cursor[MQ.L]);
751
1331
  if (command && command.endNode) {
752
1332
  // NOTE(charlie): endNode should definitely be \left(.
753
1333
  cursor.insLeftOf(command.endNode);
@@ -761,8 +1341,17 @@ class MathWrapper {
761
1341
  }
762
1342
  }
763
1343
  }
1344
+
1345
+ // note(Matthew): extracted this logic to share it elsewhere,
1346
+ // but it's part of the public MathWrapper API
764
1347
  getCursor() {
765
- return this.mathField.__controller.cursor;
1348
+ return getCursor(this.mathField);
1349
+ }
1350
+
1351
+ // note(Matthew): extracted this logic to keep this file focused,
1352
+ // but it's part of the public MathWrapper API
1353
+ contextForCursor(cursor) {
1354
+ return contextForCursor(cursor);
766
1355
  }
767
1356
  getSelection() {
768
1357
  return this.getCursor().selection;
@@ -777,576 +1366,6 @@ class MathWrapper {
777
1366
  const cursor = this.getCursor();
778
1367
  return cursor.parent.id === 1 && cursor[1] === 0 && cursor[-1] === 0;
779
1368
  }
780
-
781
- // Notes about MathQuill
782
- //
783
- // MathQuill's stores its layout as nested linked lists. Each node in the
784
- // list has this.MQ.L '-1' and this.MQ.R '1' properties that define links to
785
- // the left and right nodes respectively. They also have
786
- //
787
- // ctrlSeq: contains the latex code snippet that defines that node.
788
- // jQ: jQuery object for the DOM node(s) for this MathQuill node.
789
- // ends: pointers to the nodes at the ends of the container.
790
- // parent: parent node.
791
- // blocks: an array containing one or more nodes that make up the node.
792
- // sub?: subscript node if there is one as is the case in log_n
793
- //
794
- // All of the code below is super fragile. Please be especially careful
795
- // when upgrading MathQuill.
796
-
797
- _handleBackspaceInNthRoot(cursor) {
798
- const isAtLeftEnd = cursor[this.MQ.L] === ActionType.MQ_END;
799
- const isRootEmpty = this._isInsideEmptyNode(cursor.parent.parent.blocks[0].ends);
800
- if (isAtLeftEnd) {
801
- this._selectNode(cursor.parent.parent, cursor);
802
- if (isRootEmpty) {
803
- this.mathField.keystroke("Backspace");
804
- }
805
- } else {
806
- this.mathField.keystroke("Backspace");
807
- }
808
- }
809
-
810
- /**
811
- * Advances the cursor to the next logical position.
812
- *
813
- * @param {cursor} cursor
814
- * @private
815
- */
816
- _handleJumpOut(cursor, key) {
817
- const context = this.contextForCursor(cursor);
818
-
819
- // Validate that the current cursor context matches the key's intent.
820
- if (KeysForJumpContext[context] !== key) {
821
- // If we don't have a valid cursor context, yet the user was able
822
- // to trigger a jump-out key, that's a broken invariant. Rather
823
- // than throw an error (which would kick the user out of the
824
- // exercise), we do nothing, as a fallback strategy. The user can
825
- // still move the cursor manually.
826
- return;
827
- }
828
- switch (context) {
829
- case CursorContext.IN_PARENS:
830
- // Insert at the end of the parentheses, and then navigate right
831
- // once more to get 'beyond' the parentheses.
832
- cursor.insRightOf(cursor.parent.parent);
833
- break;
834
- case CursorContext.BEFORE_FRACTION:
835
- // Find the nearest fraction to the right of the cursor.
836
- let fractionNode;
837
- let visitor = cursor;
838
- while (visitor[this.MQ.R] !== ActionType.MQ_END) {
839
- if (this._isFraction(visitor[this.MQ.R])) {
840
- fractionNode = visitor[this.MQ.R];
841
- }
842
- visitor = visitor[this.MQ.R];
843
- }
844
-
845
- // Jump into it!
846
- cursor.insLeftOf(fractionNode);
847
- this.mathField.keystroke("Right");
848
- break;
849
- case CursorContext.IN_NUMERATOR:
850
- // HACK(charlie): I can't find a better way to do this. The goal
851
- // is to place the cursor at the start of the matching
852
- // denominator. So, we identify the appropriate node, and
853
- // continue rightwards until we find ourselves inside of it.
854
- // It's possible that there are cases in which we don't reach
855
- // the denominator, though I can't think of any.
856
- const siblingDenominator = cursor.parent.parent.blocks[1];
857
- while (cursor.parent !== siblingDenominator) {
858
- this.mathField.keystroke("Right");
859
- }
860
- break;
861
- case CursorContext.IN_DENOMINATOR:
862
- cursor.insRightOf(cursor.parent.parent);
863
- break;
864
- case CursorContext.IN_SUB_SCRIPT:
865
- // Insert just beyond the superscript.
866
- cursor.insRightOf(cursor.parent.parent);
867
-
868
- // Navigate right once more, if we're right before parens. This
869
- // is to handle the standard case in which the subscript is the
870
- // base of a custom log.
871
- if (this._isParens(cursor[this.MQ.R])) {
872
- this.mathField.keystroke("Right");
873
- }
874
- break;
875
- case CursorContext.IN_SUPER_SCRIPT:
876
- // Insert just beyond the superscript.
877
- cursor.insRightOf(cursor.parent.parent);
878
- break;
879
- default:
880
- throw new Error(`Attempted to 'Jump Out' from node, but found no ` + `appropriate cursor context: ${context}`);
881
- }
882
- }
883
-
884
- /**
885
- * Selects and deletes part of the expression based on the cursor location.
886
- * See inline comments for precise behavior of different cases.
887
- *
888
- * @param {cursor} cursor
889
- * @private
890
- */
891
- _handleBackspace(cursor) {
892
- if (!cursor.selection) {
893
- const parent = cursor.parent;
894
- const grandparent = parent.parent;
895
- const leftNode = cursor[this.MQ.L];
896
- if (this._isFraction(leftNode)) {
897
- this._selectNode(leftNode, cursor);
898
- } else if (this._isSquareRoot(leftNode)) {
899
- this._selectNode(leftNode, cursor);
900
- } else if (this._isNthRoot(leftNode)) {
901
- this._selectNode(leftNode, cursor);
902
- } else if (this._isNthRootIndex(parent)) {
903
- this._handleBackspaceInRootIndex(cursor);
904
- } else if (leftNode.ctrlSeq === "\\left(") {
905
- this._handleBackspaceOutsideParens(cursor);
906
- } else if (grandparent.ctrlSeq === "\\left(") {
907
- this._handleBackspaceInsideParens(cursor);
908
- } else if (this._isInsideLogIndex(cursor)) {
909
- this._handleBackspaceInLogIndex(cursor);
910
- } else if (leftNode.ctrlSeq === "\\ge " || leftNode.ctrlSeq === "\\le ") {
911
- this._handleBackspaceAfterLigaturedSymbol(cursor);
912
- } else if (this._isNthRoot(grandparent) && leftNode === ActionType.MQ_END) {
913
- this._handleBackspaceInNthRoot(cursor);
914
- } else {
915
- this.mathField.keystroke("Backspace");
916
- }
917
- } else {
918
- this.mathField.keystroke("Backspace");
919
- }
920
- }
921
- _handleLeftArrow(cursor) {
922
- // If we're inside a function, and just after the left parentheses, we
923
- // need to skip the entire function name, rather than move the cursor
924
- // inside of it. For example, when hitting left from within the
925
- // parentheses in `cos()`, we want to place the cursor to the left of
926
- // the entire expression, rather than between the `s` and the left
927
- // parenthesis.
928
- // From the cursor's perspective, this requires that our left node is
929
- // the ActionType.MQ_END node, that our grandparent is the left parenthesis, and
930
- // the nodes to the left of our grandparent comprise a valid function
931
- // name.
932
- if (cursor[this.MQ.L] === ActionType.MQ_END) {
933
- const parent = cursor.parent;
934
- const grandparent = parent.parent;
935
- if (grandparent.ctrlSeq === "\\left(") {
936
- const command = this._maybeFindCommandBeforeParens(grandparent);
937
- if (command) {
938
- cursor.insLeftOf(command.startNode);
939
- return;
940
- }
941
- }
942
- }
943
-
944
- // Otherwise, we default to the standard MathQull left behavior.
945
- this.mathField.keystroke("Left");
946
- }
947
- _handleRightArrow(cursor) {
948
- const command = this._maybeFindCommand(cursor[this.MQ.R]);
949
- if (command) {
950
- // Similarly, if a function is to our right, then we need to place
951
- // the cursor at the start of its parenthetical content, which is
952
- // done by putting it to the left of ites parentheses and then
953
- // moving right once.
954
- cursor.insLeftOf(command.endNode);
955
- this.mathField.keystroke("Right");
956
- } else {
957
- // Otherwise, we default to the standard MathQull right behavior.
958
- this.mathField.keystroke("Right");
959
- }
960
- }
961
- _handleExponent(cursor, key) {
962
- // If there's an invalid operator preceding the cursor (anything that
963
- // knowingly cannot be raised to a power), add an empty set of
964
- // parentheses and apply the exponent to that.
965
- const invalidPrefixes = [...ArithmeticOperators, ...EqualityOperators];
966
- const precedingNode = cursor[this.MQ.L];
967
- const shouldPrefixWithParens = precedingNode === ActionType.MQ_END || invalidPrefixes.includes(precedingNode.ctrlSeq.trim());
968
- if (shouldPrefixWithParens) {
969
- this.mathField.write("\\left(\\right)");
970
- }
971
-
972
- // Insert the appropriate exponent operator.
973
- switch (key) {
974
- case Keys.EXP:
975
- this.mathField.cmd("^");
976
- break;
977
- case Keys.EXP_2:
978
- case Keys.EXP_3:
979
- this.mathField.write(`^${key === Keys.EXP_2 ? 2 : 3}`);
980
-
981
- // If we enter a square or a cube, we should leave the cursor
982
- // within the newly inserted parens, if they exist. This takes
983
- // exactly four left strokes, since the cursor by default would
984
- // end up to the right of the exponent.
985
- if (shouldPrefixWithParens) {
986
- this.mathField.keystroke("Left");
987
- this.mathField.keystroke("Left");
988
- this.mathField.keystroke("Left");
989
- this.mathField.keystroke("Left");
990
- }
991
- break;
992
- default:
993
- throw new Error(`Invalid exponent key: ${key}`);
994
- }
995
- }
996
-
997
- /**
998
- * Return the start node, end node, and full name of the command of which
999
- * the initial node is a part, or `null` if the node is not part of a
1000
- * command.
1001
- *
1002
- * @param {node} initialNode - the node to included as part of the command
1003
- * @returns {null|object} - `null` or an object containing the start node
1004
- * (`startNode`), end node (`endNode`), and full
1005
- * name (`name`) of the command
1006
- * @private
1007
- */
1008
- _maybeFindCommand(initialNode) {
1009
- if (!initialNode) {
1010
- return null;
1011
- }
1012
-
1013
- // MathQuill stores commands as separate characters so that
1014
- // users can delete commands one character at a time. We iterate over
1015
- // the nodes from right to left until we hit a sequence starting with a
1016
- // '\\', which signifies the start of a command; then we iterate from
1017
- // left to right until we hit a '\\left(', which signifies the end of a
1018
- // command. If we encounter any character that doesn't belong in a
1019
- // command, we return null. We match a single character at a time.
1020
- // Ex) ['\\l', 'o', 'g ', '\\left(', ...]
1021
- const commandCharRegex = /^[a-z]$/;
1022
- const commandStartRegex = /^\\[a-z]$/;
1023
- const commandEndSeq = "\\left(";
1024
-
1025
- // Note: We allowlist the set of valid commands, since relying solely on
1026
- // a command being prefixed with a backslash leads to undesired
1027
- // behavior. For example, Greek symbols, left parentheses, and square
1028
- // roots all get treated as commands.
1029
- const validCommands = ["\\log", "\\ln", "\\cos", "\\sin", "\\tan"];
1030
- let name = "";
1031
- let startNode;
1032
- let endNode;
1033
-
1034
- // Collect the portion of the command from the current node, leftwards
1035
- // until the start of the command.
1036
- let node = initialNode;
1037
- while (node !== 0) {
1038
- const ctrlSeq = node.ctrlSeq.trim();
1039
- if (commandCharRegex.test(ctrlSeq)) {
1040
- name = ctrlSeq + name;
1041
- } else if (commandStartRegex.test(ctrlSeq)) {
1042
- name = ctrlSeq + name;
1043
- startNode = node;
1044
- break;
1045
- } else {
1046
- break;
1047
- }
1048
- node = node[this.MQ.L];
1049
- }
1050
-
1051
- // If we hit the start of a command, then grab the rest of it by
1052
- // iterating rightwards to compute the full name of the command, along
1053
- // with its terminal node.
1054
- if (startNode) {
1055
- // Next, iterate from the start to the right.
1056
- node = initialNode[this.MQ.R];
1057
- while (node !== 0) {
1058
- const ctrlSeq = node.ctrlSeq.trim();
1059
- if (commandCharRegex.test(ctrlSeq)) {
1060
- // If we have a single character, add it to the command
1061
- // name.
1062
- name = name + ctrlSeq;
1063
- } else if (ctrlSeq === commandEndSeq) {
1064
- // If we hit the command end delimiter (the left
1065
- // parentheses surrounding its arguments), stop.
1066
- endNode = node;
1067
- break;
1068
- }
1069
- node = node[this.MQ.R];
1070
- }
1071
- if (validCommands.includes(name)) {
1072
- return {
1073
- name,
1074
- startNode,
1075
- endNode
1076
- };
1077
- } else {
1078
- return null;
1079
- }
1080
- } else {
1081
- return null;
1082
- }
1083
- }
1084
-
1085
- /**
1086
- * Return the start node, end node, and full name of the command to the left
1087
- * of `\\left(`, or `null` if there is no command.
1088
- *
1089
- * @param {node} leftParenNode - node where .ctrlSeq == `\\left(`
1090
- * @returns {null|object} - `null` or an object containing the start node
1091
- * (`startNode`), end node (`endNode`), and full
1092
- * name (`name`) of the command
1093
- * @private
1094
- */
1095
- _maybeFindCommandBeforeParens(leftParenNode) {
1096
- return this._maybeFindCommand(leftParenNode[this.MQ.L]);
1097
- }
1098
- _selectNode(node, cursor) {
1099
- cursor.insLeftOf(node);
1100
- cursor.startSelection();
1101
- cursor.insRightOf(node);
1102
- cursor.select();
1103
- cursor.endSelection();
1104
- }
1105
- _isFraction(node) {
1106
- return node.jQ && node.jQ.hasClass("mq-fraction");
1107
- }
1108
- _isNumerator(node) {
1109
- return node.jQ && node.jQ.hasClass("mq-numerator");
1110
- }
1111
- _isDenominator(node) {
1112
- return node.jQ && node.jQ.hasClass("mq-denominator");
1113
- }
1114
- _isSubScript(node) {
1115
- // NOTE(charlie): MyScript has a structure whereby its superscripts seem
1116
- // to be represented as a parent node with 'mq-sup-only' containing a
1117
- // single child with 'mq-sup'.
1118
- return node.jQ && (node.jQ.hasClass("mq-sub-only") || node.jQ.hasClass("mq-sub"));
1119
- }
1120
- _isSuperScript(node) {
1121
- // NOTE(charlie): MyScript has a structure whereby its superscripts seem
1122
- // to be represented as a parent node with 'mq-sup-only' containing a
1123
- // single child with 'mq-sup'.
1124
- return node.jQ && (node.jQ.hasClass("mq-sup-only") || node.jQ.hasClass("mq-sup"));
1125
- }
1126
- _isParens(node) {
1127
- return node && node.ctrlSeq === "\\left(";
1128
- }
1129
- _isLeaf(node) {
1130
- return node && node.ctrlSeq && ValidLeaves.includes(node.ctrlSeq.trim());
1131
- }
1132
- _isSquareRoot(node) {
1133
- return node.blocks && node.blocks[0].jQ && node.blocks[0].jQ.hasClass("mq-sqrt-stem");
1134
- }
1135
- _isNthRoot(node) {
1136
- return node.blocks && node.blocks[0].jQ && node.blocks[0].jQ.hasClass("mq-nthroot");
1137
- }
1138
- _isNthRootIndex(node) {
1139
- return node.jQ && node.jQ.hasClass("mq-nthroot");
1140
- }
1141
- _isInsideLogIndex(cursor) {
1142
- const grandparent = cursor.parent.parent;
1143
- if (grandparent && grandparent.jQ.hasClass("mq-supsub")) {
1144
- const command = this._maybeFindCommandBeforeParens(grandparent);
1145
- if (command && command.name === "\\log") {
1146
- return true;
1147
- }
1148
- }
1149
- return false;
1150
- }
1151
- _isInsideEmptyNode(cursor) {
1152
- return cursor[this.MQ.L] === ActionType.MQ_END && cursor[this.MQ.R] === ActionType.MQ_END;
1153
- }
1154
- _handleBackspaceInRootIndex(cursor) {
1155
- if (this._isInsideEmptyNode(cursor)) {
1156
- // When deleting the index in a nthroot, we change from the nthroot
1157
- // to a sqrt, e.g. \sqrt[|]{35x-5} => |\sqrt{35x-5}. If there's no
1158
- // content under the root, then we delete the whole thing.
1159
-
1160
- const grandparent = cursor.parent.parent;
1161
- const latex = grandparent.latex();
1162
- const reinsertionPoint = grandparent[this.MQ.L];
1163
- this._selectNode(grandparent, cursor);
1164
- const rootIsEmpty = grandparent.blocks[1].jQ.text() === "";
1165
- if (rootIsEmpty) {
1166
- // If there is not content under the root then simply delete
1167
- // the whole thing.
1168
- this.mathField.keystroke("Backspace");
1169
- } else {
1170
- // Replace the nthroot with a sqrt if there was content under
1171
- // the root.
1172
-
1173
- // Start by deleting the selection.
1174
- this.mathField.keystroke("Backspace");
1175
-
1176
- // Replace the nth-root with a sqrt.
1177
- this.mathField.write(latex.replace(/^\\sqrt\[\]/, "\\sqrt"));
1178
-
1179
- // Adjust the cursor to be to the left the sqrt.
1180
- if (reinsertionPoint === ActionType.MQ_END) {
1181
- this.mathField.moveToDirEnd(this.MQ.L);
1182
- } else {
1183
- cursor.insRightOf(reinsertionPoint);
1184
- }
1185
- }
1186
- } else {
1187
- if (cursor[this.MQ.L] !== ActionType.MQ_END) {
1188
- // If the cursor is not at the leftmost position inside the
1189
- // root's index, delete a character.
1190
- this.mathField.keystroke("Backspace");
1191
- }
1192
- }
1193
- }
1194
- _handleBackspaceInLogIndex(cursor) {
1195
- if (this._isInsideEmptyNode(cursor)) {
1196
- const grandparent = cursor.parent.parent;
1197
- const command = this._maybeFindCommandBeforeParens(grandparent);
1198
- cursor.insLeftOf(command == null ? void 0 : command.startNode);
1199
- cursor.startSelection();
1200
- if (grandparent[this.MQ.R] !== ActionType.MQ_END) {
1201
- cursor.insRightOf(grandparent[this.MQ.R]);
1202
- } else {
1203
- cursor.insRightOf(grandparent);
1204
- }
1205
- cursor.select();
1206
- cursor.endSelection();
1207
- const isLogBodyEmpty = grandparent[this.MQ.R].contentjQ.text() === "";
1208
- if (isLogBodyEmpty) {
1209
- // If there's no content inside the log's parens then delete the
1210
- // whole thing.
1211
- this.mathField.keystroke("Backspace");
1212
- }
1213
- } else {
1214
- this.mathField.keystroke("Backspace");
1215
- }
1216
- }
1217
- _handleBackspaceOutsideParens(cursor) {
1218
- // In this case the node with '\\left(' for its ctrlSeq
1219
- // is the parent of the expression contained within the
1220
- // parentheses.
1221
- //
1222
- // Handle selecting an expression before deleting:
1223
- // (x+1)| => |(x+1)|
1224
- // \log(x+1)| => |\log(x+1)|
1225
-
1226
- const leftNode = cursor[this.MQ.L];
1227
- const rightNode = cursor[this.MQ.R];
1228
- const command = this._maybeFindCommandBeforeParens(leftNode);
1229
- if (command && command.startNode) {
1230
- // There's a command before the parens so we select it as well as
1231
- // the parens.
1232
- cursor.insLeftOf(command.startNode);
1233
- cursor.startSelection();
1234
- if (rightNode === ActionType.MQ_END) {
1235
- cursor.insAtRightEnd(cursor.parent);
1236
- } else {
1237
- cursor.insLeftOf(rightNode);
1238
- }
1239
- cursor.select();
1240
- cursor.endSelection();
1241
- } else {
1242
- cursor.startSelection();
1243
- cursor.insLeftOf(leftNode); // left of \\left(
1244
- cursor.select();
1245
- cursor.endSelection();
1246
- }
1247
- }
1248
- _handleBackspaceInsideParens(cursor) {
1249
- // Handle situations when the cursor is inside parens or a
1250
- // command that uses parens, e.g. \log() or \tan()
1251
- //
1252
- // MathQuill represents log(x+1) in roughly the following way
1253
- // [l, o, g, \\left[parent:[x, +, 1]]]
1254
- //
1255
- // If the cursor is inside the parentheses it's next to one of:
1256
- // x, +, or 1. This makes sub_sub_expr its parent and sub_expr
1257
- // it's parent.
1258
- //
1259
- // Interestingly parent doesn't have any nodes to the left or
1260
- // right of it (even though the corresponding DOM node has
1261
- // ( and ) characters on either side.
1262
- //
1263
- // The grandparent's ctrlSeq is `\\left(`. The `\\right)` isn't
1264
- // stored anywhere. NOTE(kevinb): I believe this is because
1265
- // MathQuill knows what the close paren should be and does the
1266
- // right thing at render time.
1267
- //
1268
- // This conditional branch handles the following cases:
1269
- // - \log(x+1|) => \log(x+|)
1270
- // - \log(|x+1) => |\log(x+1)|
1271
- // - \log(|) => |
1272
-
1273
- if (cursor[this.MQ.L] !== ActionType.MQ_END) {
1274
- // This command contains math and there's some math to
1275
- // the left of the cursor that we should delete normally
1276
- // before doing anything special.
1277
- this.mathField.keystroke("Backspace");
1278
- return;
1279
- }
1280
- const grandparent = cursor.parent.parent;
1281
-
1282
- // If the cursors is inside the parens at the start but the command
1283
- // has a subscript as is the case in log_n then move the cursor into
1284
- // the subscript, e.g. \log_{5}(|x+1) => \log_{5|}(x+1)
1285
-
1286
- if (grandparent[this.MQ.L].sub) {
1287
- // if there is a subscript
1288
- if (grandparent[this.MQ.L].sub.jQ.text()) {
1289
- // and it contains text
1290
- // move the cursor to the right end of the subscript
1291
- cursor.insAtRightEnd(grandparent[this.MQ.L].sub);
1292
- return;
1293
- }
1294
- }
1295
-
1296
- // Determine if the parens are empty before we modify the
1297
- // cursor's position.
1298
- const isEmpty = this._isInsideEmptyNode(cursor);
1299
-
1300
- // Insert the cursor to the left of the command if there is one
1301
- // or before the '\\left(` if there isn't
1302
- const command = this._maybeFindCommandBeforeParens(grandparent);
1303
- cursor.insLeftOf(command && command.startNode || grandparent);
1304
- cursor.startSelection();
1305
- cursor.insRightOf(grandparent);
1306
- cursor.select();
1307
- cursor.endSelection();
1308
-
1309
- // Delete the selection, but only if the parens were empty to
1310
- // begin with.
1311
- if (isEmpty) {
1312
- this.mathField.keystroke("Backspace");
1313
- }
1314
- }
1315
- _handleBackspaceAfterLigaturedSymbol(cursor) {
1316
- this.mathField.keystroke("Backspace");
1317
- this.mathField.keystroke("Backspace");
1318
- }
1319
- contextForCursor(cursor) {
1320
- // First, try to find any fraction to the right, unimpeded.
1321
- let visitor = cursor;
1322
- while (visitor[this.MQ.R] !== ActionType.MQ_END) {
1323
- if (this._isFraction(visitor[this.MQ.R])) {
1324
- return CursorContext.BEFORE_FRACTION;
1325
- } else if (!this._isLeaf(visitor[this.MQ.R])) {
1326
- break;
1327
- }
1328
- visitor = visitor[this.MQ.R];
1329
- }
1330
-
1331
- // If that didn't work, check if the parent or grandparent is a special
1332
- // context, so that we can jump outwards.
1333
- if (this._isParens(cursor.parent && cursor.parent.parent)) {
1334
- return CursorContext.IN_PARENS;
1335
- } else if (this._isNumerator(cursor.parent)) {
1336
- return CursorContext.IN_NUMERATOR;
1337
- } else if (this._isDenominator(cursor.parent)) {
1338
- return CursorContext.IN_DENOMINATOR;
1339
- } else if (this._isSubScript(cursor.parent)) {
1340
- return CursorContext.IN_SUB_SCRIPT;
1341
- } else if (this._isSuperScript(cursor.parent)) {
1342
- return CursorContext.IN_SUPER_SCRIPT;
1343
- } else {
1344
- return CursorContext.NONE;
1345
- }
1346
- }
1347
- _isAtTopLevel(cursor) {
1348
- return !cursor.parent.parent;
1349
- }
1350
1369
  }
1351
1370
 
1352
1371
  /**
@@ -1805,16 +1824,16 @@ class MathInput extends React.Component {
1805
1824
  };
1806
1825
  this.domKeyToMathQuillKey = key => {
1807
1826
  const keyMap = {
1808
- "+": Keys.PLUS,
1809
- "-": Keys.MINUS,
1810
- "*": Keys.TIMES,
1811
- "/": Keys.DIVIDE,
1812
- ".": Keys.DECIMAL,
1813
- "%": Keys.PERCENT,
1814
- "=": Keys.EQUAL,
1815
- ">": Keys.GT,
1816
- "<": Keys.LT,
1817
- "^": Keys.EXP
1827
+ "+": "PLUS",
1828
+ "-": "MINUS",
1829
+ "*": "TIMES",
1830
+ "/": "DIVIDE",
1831
+ ".": "DECIMAL",
1832
+ "%": "PERCENT",
1833
+ "=": "EQUAL",
1834
+ ">": "GT",
1835
+ "<": "LT",
1836
+ "^": "EXP"
1818
1837
  };
1819
1838
 
1820
1839
  // Numbers
@@ -1824,7 +1843,7 @@ class MathInput extends React.Component {
1824
1843
 
1825
1844
  // Movement keys
1826
1845
  else if (key === "Backspace") {
1827
- return Keys.BACKSPACE;
1846
+ return "BACKSPACE";
1828
1847
  }
1829
1848
 
1830
1849
  // Operators
@@ -2023,7 +2042,7 @@ class MathInput extends React.Component {
2023
2042
  // to the open the keyboard, and then remove the second half of this label.
2024
2043
  const ariaLabel = i18n._("Math input box") + " " + i18n._("Tap with one or two fingers to open keyboard");
2025
2044
  return /*#__PURE__*/React.createElement(View, {
2026
- style: styles$f.input,
2045
+ style: styles$g.input,
2027
2046
  onTouchStart: this.handleTouchStart,
2028
2047
  onTouchMove: this.handleTouchMove,
2029
2048
  onTouchEnd: this.handleTouchEnd,
@@ -2069,7 +2088,7 @@ const numeralHeightPx = 20;
2069
2088
  const totalDesiredPadding = 12;
2070
2089
  const minHeightPx = numeralHeightPx + totalDesiredPadding * 2;
2071
2090
  const minWidthPx = 64;
2072
- const styles$f = StyleSheet.create({
2091
+ const styles$g = StyleSheet.create({
2073
2092
  input: {
2074
2093
  position: "relative",
2075
2094
  display: "inline-block",
@@ -2116,40 +2135,84 @@ const keypadElementPropType = PropTypes.shape({
2116
2135
  getDOMNode: PropTypes.func.isRequired
2117
2136
  });
2118
2137
 
2119
- // I tried to make the below {[key in Keys]: KeyConfig}
2120
- // but we are doing all kinds of sneaky magic that makes it hard to
2121
- // type this safely. Leaving it for now as a generic index signature.
2138
+ const getDefaultOperatorFields = ({
2139
+ key,
2140
+ keyType: _keyType = "OPERATOR",
2141
+ iconType: _iconType = IconType.SVG,
2142
+ ariaLabel: _ariaLabel = key,
2143
+ data: _data = key
2144
+ }) => ({
2145
+ id: key,
2146
+ type: _keyType,
2147
+ ariaLabel: _ariaLabel,
2148
+ icon: {
2149
+ type: _iconType,
2150
+ data: _data
2151
+ }
2152
+ });
2153
+ const getDefaultValueFields = ({
2154
+ key,
2155
+ keyType: _keyType2 = "VALUE",
2156
+ iconType: _iconType2 = IconType.MATH,
2157
+ ariaLabel: _ariaLabel2 = key,
2158
+ data: _data2 = key
2159
+ }) => ({
2160
+ id: key,
2161
+ type: _keyType2,
2162
+ ariaLabel: _ariaLabel2,
2163
+ icon: {
2164
+ type: _iconType2,
2165
+ data: _data2
2166
+ }
2167
+ });
2168
+ const getDefaultNumberFields = ({
2169
+ key,
2170
+ data: _data3 = key.replace("NUM_", ""),
2171
+ keyType: _keyType3 = "VALUE",
2172
+ iconType: _iconType3 = IconType.TEXT,
2173
+ ariaLabel: _ariaLabel3 = _data3
2174
+ }) => ({
2175
+ id: key,
2176
+ type: _keyType3,
2177
+ ariaLabel: _ariaLabel3,
2178
+ icon: {
2179
+ type: _iconType3,
2180
+ data: _data3
2181
+ }
2182
+ });
2122
2183
  const KeyConfigs = {
2123
- // Basic math keys.
2124
- [Keys.PLUS]: {
2125
- type: KeyType.OPERATOR,
2126
- // I18N: A label for a plus sign.
2184
+ // Basic math
2185
+ PLUS: _extends({}, getDefaultOperatorFields({
2186
+ key: "PLUS",
2187
+ // I18N: A label for a 'plus' sign.
2127
2188
  ariaLabel: i18n._("Plus")
2128
- },
2129
- [Keys.MINUS]: {
2130
- type: KeyType.OPERATOR,
2131
- // I18N: A label for a minus sign.
2189
+ })),
2190
+ MINUS: _extends({}, getDefaultOperatorFields({
2191
+ key: "MINUS",
2192
+ // I18N: A label for a 'minus' sign.
2132
2193
  ariaLabel: i18n._("Minus")
2133
- },
2134
- [Keys.NEGATIVE]: {
2135
- type: KeyType.VALUE,
2136
- // I18N: A label for a minus sign.
2194
+ })),
2195
+ NEGATIVE: _extends({}, getDefaultOperatorFields({
2196
+ key: "NEGATIVE",
2197
+ // I18N: A label for a 'negative' sign.
2137
2198
  ariaLabel: i18n._("Negative")
2138
- },
2139
- [Keys.TIMES]: {
2140
- type: KeyType.OPERATOR,
2141
- // I18N: A label for a multiplication sign (represented with an 'x').
2199
+ })),
2200
+ TIMES: _extends({}, getDefaultOperatorFields({
2201
+ key: "TIMES",
2202
+ // I18N: A label for a 'multiply' sign.
2142
2203
  ariaLabel: i18n._("Multiply")
2143
- },
2144
- [Keys.DIVIDE]: {
2145
- type: KeyType.OPERATOR,
2146
- // I18N: A label for a division sign.
2204
+ })),
2205
+ DIVIDE: _extends({}, getDefaultOperatorFields({
2206
+ key: "DIVIDE",
2207
+ // I18N: A label for a 'divide' sign.
2147
2208
  ariaLabel: i18n._("Divide")
2148
- },
2149
- [Keys.DECIMAL]: {
2150
- type: KeyType.VALUE,
2151
- // I18N: A label for a decimal symbol.
2152
- ariaLabel: i18n._("Decimal"),
2209
+ })),
2210
+ DECIMAL: _extends({}, getDefaultOperatorFields({
2211
+ key: "DECIMAL",
2212
+ keyType: "VALUE",
2213
+ // I18N: A label for a 'decimal' sign (represented as '.' or ',').
2214
+ ariaLabel: i18n._("Decimal")
2215
+ }), {
2153
2216
  icon: decimalSeparator === DecimalSeparator.COMMA ? {
2154
2217
  // TODO(charlie): Get an SVG icon for the comma, or verify with
2155
2218
  // design that the text-rendered version is acceptable.
@@ -2157,252 +2220,447 @@ const KeyConfigs = {
2157
2220
  data: ","
2158
2221
  } : {
2159
2222
  type: IconType.SVG,
2160
- data: Keys.PERIOD
2223
+ data: "PERIOD"
2161
2224
  }
2162
- },
2163
- [Keys.PERCENT]: {
2164
- type: KeyType.OPERATOR,
2165
- // I18N: A label for a percent sign.
2225
+ }),
2226
+ PERIOD: _extends({}, getDefaultOperatorFields({
2227
+ key: "PERIOD",
2228
+ keyType: "VALUE",
2229
+ ariaLabel: "."
2230
+ })),
2231
+ PERCENT: _extends({}, getDefaultOperatorFields({
2232
+ key: "PERCENT",
2233
+ // I18N: A label for a 'percent' sign (represented as '%').
2166
2234
  ariaLabel: i18n._("Percent")
2167
- },
2168
- [Keys.CDOT]: {
2169
- type: KeyType.OPERATOR,
2170
- // I18N: A label for a multiplication sign (represented as a dot).
2235
+ })),
2236
+ CDOT: _extends({}, getDefaultOperatorFields({
2237
+ key: "CDOT",
2238
+ // I18N: A label for a 'centered dot' multiplication sign (represented as '⋅').
2171
2239
  ariaLabel: i18n._("Multiply")
2172
- },
2173
- [Keys.EQUAL]: {
2174
- type: KeyType.OPERATOR,
2240
+ })),
2241
+ EQUAL: _extends({}, getDefaultOperatorFields({
2242
+ key: "EQUAL",
2243
+ // I18N: A label for an 'equals' sign (represented as '=').
2175
2244
  ariaLabel: i18n._("Equals sign")
2176
- },
2177
- [Keys.NEQ]: {
2178
- type: KeyType.OPERATOR,
2245
+ })),
2246
+ NEQ: _extends({}, getDefaultOperatorFields({
2247
+ key: "NEQ",
2248
+ // I18N: A label for a 'not-equals' sign (represented as '≠').
2179
2249
  ariaLabel: i18n._("Not-equals sign")
2180
- },
2181
- [Keys.GT]: {
2182
- type: KeyType.OPERATOR,
2250
+ })),
2251
+ GT: _extends({}, getDefaultOperatorFields({
2252
+ key: "GT",
2183
2253
  // I18N: A label for a 'greater than' sign (represented as '>').
2184
2254
  ariaLabel: i18n._("Greater than sign")
2185
- },
2186
- [Keys.LT]: {
2187
- type: KeyType.OPERATOR,
2255
+ })),
2256
+ LT: _extends({}, getDefaultOperatorFields({
2257
+ key: "LT",
2188
2258
  // I18N: A label for a 'less than' sign (represented as '<').
2189
2259
  ariaLabel: i18n._("Less than sign")
2190
- },
2191
- [Keys.GEQ]: {
2192
- type: KeyType.OPERATOR,
2260
+ })),
2261
+ GEQ: _extends({}, getDefaultOperatorFields({
2262
+ key: "GEQ",
2263
+ // I18N: A label for a 'greater than or equal to' sign (represented as '≥').
2193
2264
  ariaLabel: i18n._("Greater than or equal to sign")
2194
- },
2195
- [Keys.LEQ]: {
2196
- type: KeyType.OPERATOR,
2265
+ })),
2266
+ LEQ: _extends({}, getDefaultOperatorFields({
2267
+ key: "LEQ",
2268
+ // I18N: A label for a 'less than or equal to' sign (represented as '≤').
2197
2269
  ariaLabel: i18n._("Less than or equal to sign")
2198
- },
2270
+ })),
2199
2271
  // mobile native
2200
- [Keys.FRAC_INCLUSIVE]: {
2201
- type: KeyType.OPERATOR,
2272
+ FRAC_INCLUSIVE: _extends({}, getDefaultOperatorFields({
2273
+ key: "FRAC_INCLUSIVE",
2202
2274
  // I18N: A label for a button that creates a new fraction and puts the
2203
2275
  // current expression in the numerator of that fraction.
2204
2276
  ariaLabel: i18n._("Fraction, with current expression in numerator")
2205
- },
2277
+ })),
2206
2278
  // mobile native
2207
- [Keys.FRAC_EXCLUSIVE]: {
2208
- type: KeyType.OPERATOR,
2279
+ FRAC_EXCLUSIVE: _extends({}, getDefaultOperatorFields({
2280
+ key: "FRAC_EXCLUSIVE",
2209
2281
  // I18N: A label for a button that creates a new fraction next to the
2210
2282
  // cursor.
2211
2283
  ariaLabel: i18n._("Fraction, excluding the current expression")
2212
- },
2284
+ })),
2213
2285
  // mobile web
2214
- [Keys.FRAC]: {
2215
- type: KeyType.OPERATOR,
2286
+ FRAC: _extends({}, getDefaultOperatorFields({
2287
+ key: "FRAC",
2216
2288
  // I18N: A label for a button that creates a new fraction next to the
2217
2289
  // cursor.
2218
2290
  ariaLabel: i18n._("Fraction, excluding the current expression")
2219
- },
2220
- [Keys.EXP]: {
2221
- type: KeyType.OPERATOR,
2222
- // I18N: A label for a button that will allow the user to input a custom
2223
- // exponent.
2291
+ })),
2292
+ EXP: _extends({}, getDefaultOperatorFields({
2293
+ key: "EXP",
2294
+ // I18N: A label for a button that will allow the user to input a
2295
+ // custom exponent.
2224
2296
  ariaLabel: i18n._("Custom exponent")
2225
- },
2226
- [Keys.EXP_2]: {
2227
- type: KeyType.OPERATOR,
2297
+ })),
2298
+ EXP_2: _extends({}, getDefaultOperatorFields({
2299
+ key: "EXP_2",
2228
2300
  // I18N: A label for a button that will square (take to the second
2229
2301
  // power) some math.
2230
2302
  ariaLabel: i18n._("Square")
2231
- },
2232
- [Keys.EXP_3]: {
2233
- type: KeyType.OPERATOR,
2303
+ })),
2304
+ EXP_3: _extends({}, getDefaultOperatorFields({
2305
+ key: "EXP_3",
2234
2306
  // I18N: A label for a button that will cube (take to the third power)
2235
2307
  // some math.
2236
2308
  ariaLabel: i18n._("Cube")
2237
- },
2238
- [Keys.SQRT]: {
2239
- type: KeyType.OPERATOR,
2309
+ })),
2310
+ SQRT: _extends({}, getDefaultOperatorFields({
2311
+ key: "SQRT",
2312
+ // I18N: A label for a button that will allow the user to input a
2313
+ // square root.
2240
2314
  ariaLabel: i18n._("Square root")
2241
- },
2242
- [Keys.CUBE_ROOT]: {
2243
- type: KeyType.OPERATOR,
2315
+ })),
2316
+ CUBE_ROOT: _extends({}, getDefaultOperatorFields({
2317
+ key: "CUBE_ROOT",
2318
+ // I18N: A label for a button that will allow the user to input a
2319
+ // cube root.
2244
2320
  ariaLabel: i18n._("Cube root")
2245
- },
2246
- [Keys.RADICAL]: {
2247
- type: KeyType.OPERATOR,
2321
+ })),
2322
+ RADICAL: _extends({}, getDefaultOperatorFields({
2323
+ key: "RADICAL",
2324
+ // I18N: A label for a button that will allow the user to input a
2325
+ // radical with a custom root.
2248
2326
  ariaLabel: i18n._("Radical with custom root")
2249
- },
2250
- [Keys.LEFT_PAREN]: {
2251
- type: KeyType.OPERATOR,
2327
+ })),
2328
+ LEFT_PAREN: _extends({}, getDefaultOperatorFields({
2329
+ key: "LEFT_PAREN",
2330
+ // I18N: A label for a button that will allow the user to input a
2331
+ // left parenthesis (i.e. '(')
2252
2332
  ariaLabel: i18n._("Left parenthesis")
2253
- },
2254
- [Keys.RIGHT_PAREN]: {
2255
- type: KeyType.OPERATOR,
2333
+ })),
2334
+ RIGHT_PAREN: _extends({}, getDefaultOperatorFields({
2335
+ key: "RIGHT_PAREN",
2336
+ // I18N: A label for a button that will allow the user to input a
2337
+ // right parenthesis (i.e. ')')
2256
2338
  ariaLabel: i18n._("Right parenthesis")
2257
- },
2258
- [Keys.LN]: {
2259
- type: KeyType.OPERATOR,
2339
+ })),
2340
+ LN: _extends({}, getDefaultOperatorFields({
2341
+ key: "LN",
2342
+ // I18N: A label for a button that will allow the user to input a
2343
+ // natural logarithm.
2260
2344
  ariaLabel: i18n._("Natural logarithm")
2261
- },
2262
- [Keys.LOG]: {
2263
- type: KeyType.OPERATOR,
2345
+ })),
2346
+ LOG: _extends({}, getDefaultOperatorFields({
2347
+ key: "LOG",
2348
+ // I18N: A label for a button that will allow the user to input a
2349
+ // logarithm with base 10.
2264
2350
  ariaLabel: i18n._("Logarithm with base 10")
2265
- },
2266
- [Keys.LOG_N]: {
2267
- type: KeyType.OPERATOR,
2351
+ })),
2352
+ LOG_N: _extends({}, getDefaultOperatorFields({
2353
+ key: "LOG_N",
2354
+ // I18N: A label for a button that will allow the user to input a
2355
+ // logarithm with a custom base.
2268
2356
  ariaLabel: i18n._("Logarithm with custom base")
2269
- },
2270
- [Keys.SIN]: {
2271
- type: KeyType.OPERATOR,
2357
+ })),
2358
+ SIN: _extends({}, getDefaultOperatorFields({
2359
+ key: "SIN",
2360
+ // I18N: A label for a button that will allow the user to input a
2361
+ // sine function.
2272
2362
  ariaLabel: i18n._("Sine")
2273
- },
2274
- [Keys.COS]: {
2275
- type: KeyType.OPERATOR,
2363
+ })),
2364
+ COS: _extends({}, getDefaultOperatorFields({
2365
+ key: "COS",
2366
+ // I18N: A label for a button that will allow the user to input a
2367
+ // cosine function.
2276
2368
  ariaLabel: i18n._("Cosine")
2277
- },
2278
- [Keys.TAN]: {
2279
- type: KeyType.OPERATOR,
2369
+ })),
2370
+ TAN: _extends({}, getDefaultOperatorFields({
2371
+ key: "TAN",
2372
+ // I18N: A label for a button that will allow the user to input a
2373
+ // tangent function.
2280
2374
  ariaLabel: i18n._("Tangent")
2281
- },
2282
- [Keys.PI]: {
2283
- type: KeyType.VALUE,
2284
- ariaLabel: i18n._("Pi"),
2285
- icon: {
2286
- type: IconType.MATH,
2287
- data: "\\pi"
2288
- }
2289
- },
2290
- [Keys.THETA]: {
2291
- type: KeyType.VALUE,
2292
- ariaLabel: i18n._("Theta"),
2293
- icon: {
2294
- type: IconType.MATH,
2295
- data: "\\theta"
2296
- }
2297
- },
2298
- [Keys.NOOP]: {
2299
- type: KeyType.EMPTY
2300
- },
2301
- // Input navigation keys.
2302
- [Keys.UP]: {
2303
- type: KeyType.INPUT_NAVIGATION,
2375
+ })),
2376
+ PI: _extends({}, getDefaultValueFields({
2377
+ key: "PI",
2378
+ data: "\\pi",
2379
+ // I18N: A label for a button that will allow the user to input the
2380
+ // mathematical constant pi (i.e., π)
2381
+ ariaLabel: i18n._("Pi")
2382
+ })),
2383
+ THETA: _extends({}, getDefaultValueFields({
2384
+ key: "THETA",
2385
+ data: "\\theta",
2386
+ // I18N: A label for a button that will allow the user to input the
2387
+ // mathematical constant theta (i.e., θ)
2388
+ ariaLabel: i18n._("Theta")
2389
+ })),
2390
+ NOOP: _extends({}, getDefaultOperatorFields({
2391
+ key: "NOOP",
2392
+ keyType: "EMPTY"
2393
+ })),
2394
+ // Input navigation
2395
+ UP: _extends({}, getDefaultOperatorFields({
2396
+ key: "UP",
2397
+ keyType: "INPUT_NAVIGATION",
2304
2398
  ariaLabel: i18n._("Up arrow")
2305
- },
2306
- [Keys.RIGHT]: {
2307
- type: KeyType.INPUT_NAVIGATION,
2399
+ })),
2400
+ RIGHT: _extends({}, getDefaultOperatorFields({
2401
+ key: "RIGHT",
2402
+ keyType: "INPUT_NAVIGATION",
2308
2403
  ariaLabel: i18n._("Right arrow")
2309
- },
2310
- [Keys.DOWN]: {
2311
- type: KeyType.INPUT_NAVIGATION,
2404
+ })),
2405
+ DOWN: _extends({}, getDefaultOperatorFields({
2406
+ key: "DOWN",
2407
+ keyType: "INPUT_NAVIGATION",
2312
2408
  ariaLabel: i18n._("Down arrow")
2313
- },
2314
- [Keys.LEFT]: {
2315
- type: KeyType.INPUT_NAVIGATION,
2409
+ })),
2410
+ LEFT: _extends({}, getDefaultOperatorFields({
2411
+ key: "LEFT",
2412
+ keyType: "INPUT_NAVIGATION",
2316
2413
  ariaLabel: i18n._("Left arrow")
2317
- },
2318
- [Keys.JUMP_OUT_PARENTHESES]: {
2319
- type: KeyType.INPUT_NAVIGATION,
2414
+ })),
2415
+ JUMP_OUT_PARENTHESES: _extends({}, getDefaultOperatorFields({
2416
+ key: "JUMP_OUT_PARENTHESES",
2417
+ keyType: "INPUT_NAVIGATION",
2320
2418
  ariaLabel: i18n._("Navigate right out of a set of parentheses")
2321
- },
2322
- [Keys.JUMP_OUT_EXPONENT]: {
2323
- type: KeyType.INPUT_NAVIGATION,
2419
+ })),
2420
+ JUMP_OUT_EXPONENT: _extends({}, getDefaultOperatorFields({
2421
+ key: "JUMP_OUT_EXPONENT",
2422
+ keyType: "INPUT_NAVIGATION",
2324
2423
  ariaLabel: i18n._("Navigate right out of an exponent")
2325
- },
2326
- [Keys.JUMP_OUT_BASE]: {
2327
- type: KeyType.INPUT_NAVIGATION,
2424
+ })),
2425
+ JUMP_OUT_BASE: _extends({}, getDefaultOperatorFields({
2426
+ key: "JUMP_OUT_BASE",
2427
+ keyType: "INPUT_NAVIGATION",
2328
2428
  ariaLabel: i18n._("Navigate right out of a base")
2329
- },
2330
- [Keys.JUMP_INTO_NUMERATOR]: {
2331
- type: KeyType.INPUT_NAVIGATION,
2429
+ })),
2430
+ JUMP_INTO_NUMERATOR: _extends({}, getDefaultOperatorFields({
2431
+ key: "JUMP_INTO_NUMERATOR",
2432
+ keyType: "INPUT_NAVIGATION",
2332
2433
  ariaLabel: i18n._("Navigate right into the numerator of a fraction")
2333
- },
2334
- [Keys.JUMP_OUT_NUMERATOR]: {
2335
- type: KeyType.INPUT_NAVIGATION,
2434
+ })),
2435
+ JUMP_OUT_NUMERATOR: _extends({}, getDefaultOperatorFields({
2436
+ key: "JUMP_OUT_NUMERATOR",
2437
+ keyType: "INPUT_NAVIGATION",
2336
2438
  ariaLabel: i18n._("Navigate right out of the numerator and into the denominator")
2337
- },
2338
- [Keys.JUMP_OUT_DENOMINATOR]: {
2339
- type: KeyType.INPUT_NAVIGATION,
2439
+ })),
2440
+ JUMP_OUT_DENOMINATOR: _extends({}, getDefaultOperatorFields({
2441
+ key: "JUMP_OUT_DENOMINATOR",
2442
+ keyType: "INPUT_NAVIGATION",
2340
2443
  ariaLabel: i18n._("Navigate right out of the denominator of a fraction")
2341
- },
2342
- [Keys.BACKSPACE]: {
2343
- type: KeyType.INPUT_NAVIGATION,
2344
- // I18N: A label for a button that will delete some input.
2444
+ })),
2445
+ BACKSPACE: _extends({}, getDefaultOperatorFields({
2446
+ key: "BACKSPACE",
2447
+ keyType: "INPUT_NAVIGATION",
2345
2448
  ariaLabel: i18n._("Delete")
2346
- },
2347
- // Keypad navigation keys.
2348
- [Keys.DISMISS]: {
2349
- type: KeyType.KEYPAD_NAVIGATION,
2449
+ })),
2450
+ // Keypad navigation
2451
+ DISMISS: _extends({}, getDefaultOperatorFields({
2452
+ key: "DISMISS",
2453
+ keyType: "KEYPAD_NAVIGATION",
2350
2454
  // I18N: A label for a button that will dismiss/hide a keypad.
2351
2455
  ariaLabel: i18n._("Dismiss")
2352
- },
2456
+ })),
2353
2457
  // TODO(charlie): Use the numeral color for the 'Many' key.
2354
- // MANY: {
2355
- // type: KeyType.MANY,
2356
- // // childKeyIds will be configured by the client.
2357
- // },
2358
-
2359
- [Keys.PERIOD]: {}
2458
+ MANY: _extends({}, getDefaultOperatorFields({
2459
+ key: "MANY",
2460
+ keyType: "MANY"
2461
+ })),
2462
+ // NUMBERS
2463
+ NUM_0: _extends({}, getDefaultNumberFields({
2464
+ key: "NUM_0"
2465
+ })),
2466
+ NUM_1: _extends({}, getDefaultNumberFields({
2467
+ key: "NUM_1"
2468
+ })),
2469
+ NUM_2: _extends({}, getDefaultNumberFields({
2470
+ key: "NUM_2"
2471
+ })),
2472
+ NUM_3: _extends({}, getDefaultNumberFields({
2473
+ key: "NUM_3"
2474
+ })),
2475
+ NUM_4: _extends({}, getDefaultNumberFields({
2476
+ key: "NUM_4"
2477
+ })),
2478
+ NUM_5: _extends({}, getDefaultNumberFields({
2479
+ key: "NUM_5"
2480
+ })),
2481
+ NUM_6: _extends({}, getDefaultNumberFields({
2482
+ key: "NUM_6"
2483
+ })),
2484
+ NUM_7: _extends({}, getDefaultNumberFields({
2485
+ key: "NUM_7"
2486
+ })),
2487
+ NUM_8: _extends({}, getDefaultNumberFields({
2488
+ key: "NUM_8"
2489
+ })),
2490
+ NUM_9: _extends({}, getDefaultNumberFields({
2491
+ key: "NUM_9"
2492
+ })),
2493
+ // LETTERS
2494
+ A: _extends({}, getDefaultValueFields({
2495
+ key: "A"
2496
+ })),
2497
+ B: _extends({}, getDefaultValueFields({
2498
+ key: "B"
2499
+ })),
2500
+ C: _extends({}, getDefaultValueFields({
2501
+ key: "C"
2502
+ })),
2503
+ D: _extends({}, getDefaultValueFields({
2504
+ key: "D"
2505
+ })),
2506
+ E: _extends({}, getDefaultValueFields({
2507
+ key: "E"
2508
+ })),
2509
+ F: _extends({}, getDefaultValueFields({
2510
+ key: "F"
2511
+ })),
2512
+ G: _extends({}, getDefaultValueFields({
2513
+ key: "G"
2514
+ })),
2515
+ H: _extends({}, getDefaultValueFields({
2516
+ key: "H"
2517
+ })),
2518
+ I: _extends({}, getDefaultValueFields({
2519
+ key: "I"
2520
+ })),
2521
+ J: _extends({}, getDefaultValueFields({
2522
+ key: "J"
2523
+ })),
2524
+ K: _extends({}, getDefaultValueFields({
2525
+ key: "K"
2526
+ })),
2527
+ L: _extends({}, getDefaultValueFields({
2528
+ key: "L"
2529
+ })),
2530
+ M: _extends({}, getDefaultValueFields({
2531
+ key: "M"
2532
+ })),
2533
+ N: _extends({}, getDefaultValueFields({
2534
+ key: "N"
2535
+ })),
2536
+ O: _extends({}, getDefaultValueFields({
2537
+ key: "O"
2538
+ })),
2539
+ P: _extends({}, getDefaultValueFields({
2540
+ key: "P"
2541
+ })),
2542
+ Q: _extends({}, getDefaultValueFields({
2543
+ key: "Q"
2544
+ })),
2545
+ R: _extends({}, getDefaultValueFields({
2546
+ key: "R"
2547
+ })),
2548
+ S: _extends({}, getDefaultValueFields({
2549
+ key: "S"
2550
+ })),
2551
+ T: _extends({}, getDefaultValueFields({
2552
+ key: "T"
2553
+ })),
2554
+ U: _extends({}, getDefaultValueFields({
2555
+ key: "U"
2556
+ })),
2557
+ V: _extends({}, getDefaultValueFields({
2558
+ key: "V"
2559
+ })),
2560
+ W: _extends({}, getDefaultValueFields({
2561
+ key: "W"
2562
+ })),
2563
+ X: _extends({}, getDefaultValueFields({
2564
+ key: "X"
2565
+ })),
2566
+ Y: _extends({}, getDefaultValueFields({
2567
+ key: "Y"
2568
+ })),
2569
+ Z: _extends({}, getDefaultValueFields({
2570
+ key: "Z"
2571
+ })),
2572
+ a: _extends({}, getDefaultValueFields({
2573
+ key: "a"
2574
+ })),
2575
+ b: _extends({}, getDefaultValueFields({
2576
+ key: "b"
2577
+ })),
2578
+ c: _extends({}, getDefaultValueFields({
2579
+ key: "c"
2580
+ })),
2581
+ d: _extends({}, getDefaultValueFields({
2582
+ key: "d"
2583
+ })),
2584
+ e: _extends({}, getDefaultValueFields({
2585
+ key: "e"
2586
+ })),
2587
+ f: _extends({}, getDefaultValueFields({
2588
+ key: "f"
2589
+ })),
2590
+ g: _extends({}, getDefaultValueFields({
2591
+ key: "g"
2592
+ })),
2593
+ h: _extends({}, getDefaultValueFields({
2594
+ key: "h"
2595
+ })),
2596
+ i: _extends({}, getDefaultValueFields({
2597
+ key: "i"
2598
+ })),
2599
+ j: _extends({}, getDefaultValueFields({
2600
+ key: "j"
2601
+ })),
2602
+ k: _extends({}, getDefaultValueFields({
2603
+ key: "k"
2604
+ })),
2605
+ l: _extends({}, getDefaultValueFields({
2606
+ key: "l"
2607
+ })),
2608
+ m: _extends({}, getDefaultValueFields({
2609
+ key: "m"
2610
+ })),
2611
+ n: _extends({}, getDefaultValueFields({
2612
+ key: "n"
2613
+ })),
2614
+ o: _extends({}, getDefaultValueFields({
2615
+ key: "o"
2616
+ })),
2617
+ p: _extends({}, getDefaultValueFields({
2618
+ key: "p"
2619
+ })),
2620
+ q: _extends({}, getDefaultValueFields({
2621
+ key: "q"
2622
+ })),
2623
+ r: _extends({}, getDefaultValueFields({
2624
+ key: "r"
2625
+ })),
2626
+ s: _extends({}, getDefaultValueFields({
2627
+ key: "s"
2628
+ })),
2629
+ t: _extends({}, getDefaultValueFields({
2630
+ key: "t"
2631
+ })),
2632
+ u: _extends({}, getDefaultValueFields({
2633
+ key: "u"
2634
+ })),
2635
+ v: _extends({}, getDefaultValueFields({
2636
+ key: "v"
2637
+ })),
2638
+ w: _extends({}, getDefaultValueFields({
2639
+ key: "w"
2640
+ })),
2641
+ x: _extends({}, getDefaultValueFields({
2642
+ key: "x"
2643
+ })),
2644
+ y: _extends({}, getDefaultValueFields({
2645
+ key: "y"
2646
+ })),
2647
+ z: _extends({}, getDefaultValueFields({
2648
+ key: "z"
2649
+ })),
2650
+ PHI: _extends({}, getDefaultValueFields({
2651
+ key: "PHI"
2652
+ })),
2653
+ NTHROOT3: _extends({}, getDefaultValueFields({
2654
+ key: "NTHROOT3"
2655
+ })),
2656
+ POW: _extends({}, getDefaultValueFields({
2657
+ key: "POW"
2658
+ })),
2659
+ LOG_B: _extends({}, getDefaultValueFields({
2660
+ key: "LOG_B"
2661
+ }))
2360
2662
  };
2361
2663
 
2362
- // Add in every numeral.
2363
- const NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
2364
- for (const num of NUMBERS) {
2365
- // TODO(charlie): Consider removing the SVG icons that we have for the
2366
- // numeral keys. They can be rendered just as easily with text (though that
2367
- // would mean that we'd be using text beyond the variable key).
2368
- const textRepresentation = `${num}`;
2369
- KeyConfigs[`NUM_${num}`] = {
2370
- type: KeyType.VALUE,
2371
- ariaLabel: textRepresentation,
2372
- icon: {
2373
- type: IconType.TEXT,
2374
- data: textRepresentation
2375
- }
2376
- };
2377
- }
2378
-
2379
- // Add in every variable.
2380
- 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"];
2381
- for (const letter of LETTERS) {
2382
- const lowerCaseVariable = letter.toLowerCase();
2383
- const upperCaseVariable = letter.toUpperCase();
2384
- for (const textRepresentation of [lowerCaseVariable, upperCaseVariable]) {
2385
- KeyConfigs[textRepresentation] = {
2386
- type: KeyType.VALUE,
2387
- ariaLabel: textRepresentation,
2388
- icon: {
2389
- type: IconType.MATH,
2390
- data: textRepresentation
2391
- }
2392
- };
2393
- }
2394
- }
2395
- for (const key of Object.keys(KeyConfigs)) {
2396
- KeyConfigs[key] = _extends({
2397
- id: key,
2398
- // Default to an SVG icon indexed by the key name.
2399
- icon: {
2400
- type: IconType.SVG,
2401
- data: key
2402
- }
2403
- }, KeyConfigs[key]);
2404
- }
2405
-
2406
2664
  function _objectWithoutPropertiesLoose(source, excluded) {
2407
2665
  if (source == null) return {};
2408
2666
  var target = {};
@@ -2426,7 +2684,7 @@ class CornerDecal extends React.Component {
2426
2684
  const {
2427
2685
  style
2428
2686
  } = this.props;
2429
- const containerStyle = [styles$e.container, ...(Array.isArray(style) ? style : [style])];
2687
+ const containerStyle = [styles$f.container, ...(Array.isArray(style) ? style : [style])];
2430
2688
  return /*#__PURE__*/React.createElement(View, {
2431
2689
  style: containerStyle
2432
2690
  }, /*#__PURE__*/React.createElement("svg", {
@@ -2442,7 +2700,7 @@ class CornerDecal extends React.Component {
2442
2700
  }
2443
2701
 
2444
2702
  const triangleSizePx = 7;
2445
- const styles$e = StyleSheet.create({
2703
+ const styles$f = StyleSheet.create({
2446
2704
  container: {
2447
2705
  position: "absolute",
2448
2706
  top: 0,
@@ -2515,13 +2773,13 @@ class MathIcon extends React.Component {
2515
2773
  const {
2516
2774
  style
2517
2775
  } = this.props;
2518
- const containerStyle = [row$7, centered$4, styles$d.size, styles$d.base, ...(Array.isArray(style) ? style : [style])];
2776
+ const containerStyle = [row$7, centered$4, styles$e.size, styles$e.base, ...(Array.isArray(style) ? style : [style])];
2519
2777
  return /*#__PURE__*/React.createElement(View, {
2520
2778
  style: containerStyle
2521
2779
  });
2522
2780
  }
2523
2781
  }
2524
- const styles$d = StyleSheet.create({
2782
+ const styles$e = StyleSheet.create({
2525
2783
  size: {
2526
2784
  height: iconSizeHeightPx,
2527
2785
  width: iconSizeWidthPx
@@ -3844,13 +4102,13 @@ class TextIcon extends React.Component {
3844
4102
  character,
3845
4103
  style
3846
4104
  } = this.props;
3847
- const containerStyle = [row$6, centered$3, styles$c.size, styles$c.base, ...(Array.isArray(style) ? style : [style])];
4105
+ const containerStyle = [row$6, centered$3, styles$d.size, styles$d.base, ...(Array.isArray(style) ? style : [style])];
3848
4106
  return /*#__PURE__*/React.createElement(View, {
3849
4107
  style: containerStyle
3850
4108
  }, /*#__PURE__*/React.createElement(Text, null, character));
3851
4109
  }
3852
4110
  }
3853
- const styles$c = StyleSheet.create({
4111
+ const styles$d = StyleSheet.create({
3854
4112
  size: {
3855
4113
  height: iconSizeHeightPx,
3856
4114
  width: iconSizeWidthPx
@@ -3873,7 +4131,7 @@ class Icon extends React.PureComponent {
3873
4131
  icon,
3874
4132
  style
3875
4133
  } = this.props;
3876
- const styleWithFocus = [focused ? styles$b.focused : styles$b.unfocused, ...(Array.isArray(style) ? style : [style])];
4134
+ const styleWithFocus = [focused ? styles$c.focused : styles$c.unfocused, ...(Array.isArray(style) ? style : [style])];
3877
4135
  switch (icon.type) {
3878
4136
  case IconType.MATH:
3879
4137
  return /*#__PURE__*/React.createElement(MathIcon, {
@@ -3899,7 +4157,7 @@ class Icon extends React.PureComponent {
3899
4157
  }
3900
4158
  }
3901
4159
  }
3902
- const styles$b = StyleSheet.create({
4160
+ const styles$c = StyleSheet.create({
3903
4161
  unfocused: {
3904
4162
  color: unfocusedColor
3905
4163
  },
@@ -3941,19 +4199,19 @@ class MultiSymbolGrid extends React.Component {
3941
4199
  focused: focused
3942
4200
  });
3943
4201
  } else {
3944
- const primaryIconStyle = styles$a.base;
3945
- const secondaryIconStyle = [styles$a.base, styles$a.secondary];
4202
+ const primaryIconStyle = styles$b.base;
4203
+ const secondaryIconStyle = [styles$b.base, styles$b.secondary];
3946
4204
  if (icons.length === 2) {
3947
4205
  return /*#__PURE__*/React.createElement(View, {
3948
- style: [row$5, styles$a.size]
4206
+ style: [row$5, styles$b.size]
3949
4207
  }, /*#__PURE__*/React.createElement(View, {
3950
- style: [column$3, centered$2, fullWidth$3, styles$a.middleLeft]
4208
+ style: [column$3, centered$2, fullWidth$3, styles$b.middleLeft]
3951
4209
  }, /*#__PURE__*/React.createElement(Icon, {
3952
4210
  style: primaryIconStyle,
3953
4211
  icon: icons[0],
3954
4212
  focused: focused
3955
4213
  })), /*#__PURE__*/React.createElement(View, {
3956
- style: [column$3, centered$2, fullWidth$3, styles$a.middleRight]
4214
+ style: [column$3, centered$2, fullWidth$3, styles$b.middleRight]
3957
4215
  }, /*#__PURE__*/React.createElement(Icon, {
3958
4216
  style: secondaryIconStyle,
3959
4217
  icon: icons[1],
@@ -3961,17 +4219,17 @@ class MultiSymbolGrid extends React.Component {
3961
4219
  })));
3962
4220
  } else if (icons.length >= 3) {
3963
4221
  return /*#__PURE__*/React.createElement(View, {
3964
- style: [column$3, styles$a.size]
4222
+ style: [column$3, styles$b.size]
3965
4223
  }, /*#__PURE__*/React.createElement(View, {
3966
4224
  style: row$5
3967
4225
  }, /*#__PURE__*/React.createElement(View, {
3968
- style: [centered$2, fullWidth$3, styles$a.topLeft]
4226
+ style: [centered$2, fullWidth$3, styles$b.topLeft]
3969
4227
  }, /*#__PURE__*/React.createElement(Icon, {
3970
4228
  style: primaryIconStyle,
3971
4229
  icon: icons[0],
3972
4230
  focused: focused
3973
4231
  })), /*#__PURE__*/React.createElement(View, {
3974
- style: [centered$2, fullWidth$3, styles$a.topRight]
4232
+ style: [centered$2, fullWidth$3, styles$b.topRight]
3975
4233
  }, /*#__PURE__*/React.createElement(Icon, {
3976
4234
  style: secondaryIconStyle,
3977
4235
  icon: icons[1],
@@ -3979,13 +4237,13 @@ class MultiSymbolGrid extends React.Component {
3979
4237
  }))), /*#__PURE__*/React.createElement(View, {
3980
4238
  style: row$5
3981
4239
  }, /*#__PURE__*/React.createElement(View, {
3982
- style: [centered$2, fullWidth$3, styles$a.bottomLeft]
4240
+ style: [centered$2, fullWidth$3, styles$b.bottomLeft]
3983
4241
  }, /*#__PURE__*/React.createElement(Icon, {
3984
4242
  style: secondaryIconStyle,
3985
4243
  icon: icons[2],
3986
4244
  focused: focused
3987
4245
  })), /*#__PURE__*/React.createElement(View, {
3988
- style: [centered$2, fullWidth$3, styles$a.bottomRight]
4246
+ style: [centered$2, fullWidth$3, styles$b.bottomRight]
3989
4247
  }, icons[3] && /*#__PURE__*/React.createElement(Icon, {
3990
4248
  style: secondaryIconStyle,
3991
4249
  icon: icons[3],
@@ -3998,7 +4256,7 @@ class MultiSymbolGrid extends React.Component {
3998
4256
  }
3999
4257
  const verticalInsetPx = 2;
4000
4258
  const horizontalInsetPx = 4;
4001
- const styles$a = StyleSheet.create({
4259
+ const styles$b = StyleSheet.create({
4002
4260
  size: {
4003
4261
  height: iconSizeHeightPx,
4004
4262
  width: iconSizeWidthPx
@@ -4049,7 +4307,7 @@ class KeypadButton$1 extends React.PureComponent {
4049
4307
  // object. This method must be called whenever a property that
4050
4308
  // influences the possible outcomes of `this._getFocusStyle` and
4051
4309
  // `this._getButtonStyle` changes (such as `this.buttonSizeStyle`).
4052
- for (const type of Object.values(KeyType)) {
4310
+ for (const type of KeyTypes) {
4053
4311
  css(View.styles.initial, ...this._getFocusStyle(type));
4054
4312
  for (const borders of Object.values(BorderStyles)) {
4055
4313
  css(View.styles.initial, ...this._getButtonStyle(type, borders));
@@ -4058,45 +4316,45 @@ class KeypadButton$1 extends React.PureComponent {
4058
4316
  };
4059
4317
  this._getFocusStyle = type => {
4060
4318
  let focusBackgroundStyle;
4061
- if (type === KeyType.INPUT_NAVIGATION || type === KeyType.KEYPAD_NAVIGATION) {
4062
- focusBackgroundStyle = styles$9.light;
4319
+ if (type === "INPUT_NAVIGATION" || type === "KEYPAD_NAVIGATION") {
4320
+ focusBackgroundStyle = styles$a.light;
4063
4321
  } else {
4064
- focusBackgroundStyle = styles$9.bright;
4322
+ focusBackgroundStyle = styles$a.bright;
4065
4323
  }
4066
- return [styles$9.focusBox, focusBackgroundStyle];
4324
+ return [styles$a.focusBox, focusBackgroundStyle];
4067
4325
  };
4068
4326
  this._getButtonStyle = (type, borders, style) => {
4069
4327
  // Select the appropriate style for the button.
4070
4328
  let backgroundStyle;
4071
4329
  switch (type) {
4072
- case KeyType.EMPTY:
4073
- backgroundStyle = styles$9.empty;
4330
+ case "EMPTY":
4331
+ backgroundStyle = styles$a.empty;
4074
4332
  break;
4075
- case KeyType.MANY:
4076
- case KeyType.VALUE:
4077
- backgroundStyle = styles$9.value;
4333
+ case "MANY":
4334
+ case "VALUE":
4335
+ backgroundStyle = styles$a.value;
4078
4336
  break;
4079
- case KeyType.OPERATOR:
4080
- backgroundStyle = styles$9.operator;
4337
+ case "OPERATOR":
4338
+ backgroundStyle = styles$a.operator;
4081
4339
  break;
4082
- case KeyType.INPUT_NAVIGATION:
4083
- case KeyType.KEYPAD_NAVIGATION:
4084
- backgroundStyle = styles$9.control;
4340
+ case "INPUT_NAVIGATION":
4341
+ case "KEYPAD_NAVIGATION":
4342
+ backgroundStyle = styles$a.control;
4085
4343
  break;
4086
- case KeyType.ECHO:
4344
+ case "ECHO":
4087
4345
  backgroundStyle = null;
4088
4346
  break;
4089
4347
  }
4090
4348
  const borderStyle = [];
4091
4349
  if (borders.includes(BorderDirection.LEFT)) {
4092
4350
  // @ts-expect-error TS2345
4093
- borderStyle.push(styles$9.leftBorder);
4351
+ borderStyle.push(styles$a.leftBorder);
4094
4352
  }
4095
4353
  if (borders.includes(BorderDirection.BOTTOM)) {
4096
4354
  // @ts-expect-error TS2345
4097
- borderStyle.push(styles$9.bottomBorder);
4355
+ borderStyle.push(styles$a.bottomBorder);
4098
4356
  }
4099
- return [styles$9.buttonBase, backgroundStyle, ...borderStyle, type === KeyType.ECHO && styles$9.echo, this.buttonSizeStyle,
4357
+ return [styles$a.buttonBase, backgroundStyle, ...borderStyle, type === "ECHO" && styles$a.echo, this.buttonSizeStyle,
4100
4358
  // React Native allows you to set the 'style' props on user defined
4101
4359
  // components.
4102
4360
  // See: https://facebook.github.io/react-native/docs/style.html
@@ -4138,10 +4396,10 @@ class KeypadButton$1 extends React.PureComponent {
4138
4396
 
4139
4397
  // We render in the focus state if the key is focused, or if it's an
4140
4398
  // echo.
4141
- const renderFocused = !disabled && focused || popoverEnabled || type === KeyType.ECHO;
4399
+ const renderFocused = !disabled && focused || popoverEnabled || type === "ECHO";
4142
4400
  const buttonStyle = this._getButtonStyle(type, borders, style);
4143
4401
  const focusStyle = this._getFocusStyle(type);
4144
- const iconWrapperStyle = [styles$9.iconWrapper, disabled ? styles$9.disabled : undefined];
4402
+ const iconWrapperStyle = [styles$a.iconWrapper, disabled ? styles$a.disabled : undefined];
4145
4403
  const eventHandlers = {
4146
4404
  onTouchCancel,
4147
4405
  onTouchEnd,
@@ -4152,13 +4410,13 @@ class KeypadButton$1 extends React.PureComponent {
4152
4410
  style: focusStyle
4153
4411
  });
4154
4412
  const maybeCornerDecal = !renderFocused && !disabled && childKeys && childKeys.length > 0 && /*#__PURE__*/React.createElement(CornerDecal, {
4155
- style: styles$9.decalInset
4413
+ style: styles$a.decalInset
4156
4414
  });
4157
- if (type === KeyType.EMPTY) {
4415
+ if (type === "EMPTY") {
4158
4416
  return /*#__PURE__*/React.createElement(View, _extends({
4159
4417
  style: buttonStyle
4160
4418
  }, eventHandlers));
4161
- } else if (type === KeyType.MANY) {
4419
+ } else if (type === "MANY") {
4162
4420
  // TODO(charlie): Make the long-press interaction accessible. See
4163
4421
  // the TODO in key-configs.js for more.
4164
4422
  const manyButtonA11yMarkup = {
@@ -4201,7 +4459,7 @@ KeypadButton$1.defaultProps = {
4201
4459
  };
4202
4460
  const focusInsetPx = 4;
4203
4461
  const focusBoxZIndex = 0;
4204
- const styles$9 = StyleSheet.create({
4462
+ const styles$a = StyleSheet.create({
4205
4463
  buttonBase: {
4206
4464
  flex: 1,
4207
4465
  cursor: "pointer",
@@ -4359,7 +4617,7 @@ class TouchableKeypadButton extends React.Component {
4359
4617
  onTouchMove: evt => gestureManager.onTouchMove(evt),
4360
4618
  onTouchCancel: evt => gestureManager.onTouchCancel(evt)
4361
4619
  };
4362
- const styleWithAddons = [...(Array.isArray(style) ? style : [style]), styles$8.preventScrolls];
4620
+ const styleWithAddons = [...(Array.isArray(style) ? style : [style]), styles$9.preventScrolls];
4363
4621
  return /*#__PURE__*/React.createElement(KeypadButton$2, _extends({
4364
4622
  ref: node => gestureManager.registerDOMNode(id, ReactDOM.findDOMNode(node), childKeyIds),
4365
4623
  borders: borders,
@@ -4398,7 +4656,7 @@ const mapStateToProps$5 = (state, ownProps) => {
4398
4656
 
4399
4657
  // Override with the default child props, if the key is a multi-symbol key
4400
4658
  // (but not a many-symbol key, which operates under different rules).
4401
- const useFirstChildProps = type !== KeyType.MANY && childKeys && childKeys.length > 0;
4659
+ const useFirstChildProps = type !== "MANY" && childKeys && childKeys.length > 0;
4402
4660
  return _extends({}, rest, {
4403
4661
  childKeyIds: childKeyIds,
4404
4662
  gestureManager: gestures.gestureManager,
@@ -4410,7 +4668,7 @@ const mapStateToProps$5 = (state, ownProps) => {
4410
4668
  childKeys
4411
4669
  }, extractProps(useFirstChildProps ? childKeys[0] : keyConfig));
4412
4670
  };
4413
- const styles$8 = StyleSheet.create({
4671
+ const styles$9 = StyleSheet.create({
4414
4672
  preventScrolls: {
4415
4673
  // Touch events that start in the touchable buttons shouldn't be
4416
4674
  // allowed to produce page scrolls.
@@ -4443,8 +4701,13 @@ class ManyKeypadButton extends React.Component {
4443
4701
  } else {
4444
4702
  const keyConfig = {
4445
4703
  id: "MANY",
4446
- type: KeyType.MANY,
4447
- childKeyIds: keys
4704
+ type: "MANY",
4705
+ childKeyIds: keys,
4706
+ ariaLabel: keys.map(key => KeyConfigs[key].ariaLabel).join(", "),
4707
+ icon: {
4708
+ type: IconType.SVG,
4709
+ data: "many"
4710
+ }
4448
4711
  };
4449
4712
  return /*#__PURE__*/React.createElement(TouchableKeypadButton$1, _extends({
4450
4713
  keyConfig: keyConfig
@@ -4460,50 +4723,69 @@ const IconAsset = function IconAsset({
4460
4723
  tintColor,
4461
4724
  type
4462
4725
  }) {
4463
- if (type === "Geometry") {
4464
- return /*#__PURE__*/React.createElement("svg", {
4465
- width: "32",
4466
- height: "32",
4467
- viewBox: "0 0 32 32",
4468
- fill: "none",
4469
- xmlns: "http://www.w3.org/2000/svg"
4470
- }, /*#__PURE__*/React.createElement("path", {
4471
- fillRule: "evenodd",
4472
- clipRule: "evenodd",
4473
- d: "M7.57584 7.09442C7.92723 6.92984 8.3421 6.98339 8.64018 7.23179L26.6402 22.2318C26.9636 22.5013 27.0836 22.9446 26.9403 23.3404C26.7969 23.7363 26.421 24 26 24H8C7.44772 24 7 23.5523 7 23V8.00001C7 7.61199 7.22446 7.259 7.57584 7.09442ZM9 10.1351V17H13C13.5523 17 14 17.4477 14 18V22H23.238L9 10.1351ZM12 22V19H9V22H12Z",
4474
- fill: tintColor
4475
- }));
4476
- } else if (type === "Operators") {
4477
- return /*#__PURE__*/React.createElement("svg", {
4478
- width: "32",
4479
- height: "32",
4480
- viewBox: "0 0 32 32",
4481
- fill: "none",
4482
- xmlns: "http://www.w3.org/2000/svg"
4483
- }, /*#__PURE__*/React.createElement("path", {
4484
- fillRule: "evenodd",
4485
- clipRule: "evenodd",
4486
- d: "M29 6H30V7H29V6ZM27 6C27 4.89543 27.8954 4 29 4H30C31.1046 4 32 4.89543 32 6V7C32 8.10457 31.1046 9 30 9H29C27.8954 9 27 8.10457 27 7V6ZM11.1318 6.50386C11.3098 6.19229 11.6411 6 12 6H14C14.5523 6 15 6.44772 15 7C15 7.55228 14.5523 8 14 8H12.5803L8.86824 14.4961C8.68527 14.8163 8.34091 15.0098 7.97225 14.9996C7.6036 14.9894 7.2705 14.7771 7.10557 14.4472L5.10557 10.4472C4.85858 9.95324 5.05881 9.35256 5.55279 9.10557C6.04676 8.85858 6.64744 9.05881 6.89443 9.55279L8.0588 11.8815L11.1318 6.50386ZM7.70676 16.2925C8.09748 16.6829 8.09779 17.316 7.70745 17.7068C7.28543 18.1292 6.84383 18.7303 6.51157 19.3658C6.17039 20.0184 6 20.601 6 21C6 21.3789 6.17235 21.9897 6.51638 22.6649C6.85315 23.3259 7.28488 23.9121 7.66786 24.2557C8.07892 24.6246 8.11314 25.2568 7.74429 25.6679C7.37544 26.0789 6.7432 26.1131 6.33214 25.7443C5.7161 25.1915 5.14783 24.3844 4.73434 23.5728C4.32813 22.7755 3.99999 21.8345 4 21C4.00001 20.1391 4.3301 19.2217 4.73917 18.4392C5.15715 17.6397 5.71554 16.8708 6.29255 16.2932C6.68288 15.9025 7.31605 15.9022 7.70676 16.2925ZM11.2932 16.2925C11.684 15.9022 12.3171 15.9025 12.7075 16.2932C13.2845 16.8708 13.8428 17.6397 14.2608 18.4392C14.6699 19.2217 15 20.1391 15 21C15 21.8345 14.6719 22.7755 14.2657 23.5728C13.8522 24.3844 13.2839 25.1915 12.6679 25.7443C12.2568 26.1131 11.6246 26.0789 11.2557 25.6679C10.8869 25.2568 10.9211 24.6246 11.3321 24.2557C11.7151 23.9121 12.1469 23.3259 12.4836 22.6649C12.8276 21.9897 13 21.3789 13 21C13 20.601 12.8296 20.0184 12.4884 19.3658C12.1562 18.7303 11.7146 18.1292 11.2925 17.7068C10.9022 17.316 10.9025 16.6829 11.2932 16.2925ZM27.9363 17.6489C28.1302 18.166 27.8682 18.7424 27.3511 18.9363L21.848 21L27.3511 23.0637C27.8682 23.2576 28.1302 23.834 27.9363 24.3511C27.7424 24.8682 27.166 25.1302 26.6489 24.9363L18.6489 21.9363C18.2586 21.79 18 21.4168 18 21C18 20.5832 18.2586 20.21 18.6489 20.0637L26.6489 17.0637C27.166 16.8698 27.7424 17.1318 27.9363 17.6489ZM21 8V13H24V8H21ZM20 6C19.4477 6 19 6.44772 19 7V14C19 14.5523 19.4477 15 20 15H25C25.5523 15 26 14.5523 26 14V7C26 6.44772 25.5523 6 25 6H20Z",
4487
- fill: tintColor
4488
- }));
4489
- } else if (type === "Numbers") {
4490
- return /*#__PURE__*/React.createElement("svg", {
4491
- width: "32",
4492
- height: "32",
4493
- viewBox: "0 0 32 32",
4494
- fill: "none",
4495
- xmlns: "http://www.w3.org/2000/svg"
4496
- }, /*#__PURE__*/React.createElement("path", {
4497
- d: "M10.4123 19.5794V21.0004H4.71434V19.5794H6.73034V14.0214C6.73034 13.9001 6.73267 13.7764 6.73734 13.6504C6.742 13.5244 6.749 13.3961 6.75834 13.2654L5.42834 14.3714C5.335 14.4414 5.244 14.4858 5.15534 14.5044C5.06667 14.5231 4.98267 14.5254 4.90334 14.5114C4.824 14.4928 4.754 14.4648 4.69334 14.4274C4.63267 14.3854 4.586 14.3434 4.55334 14.3014L3.94434 13.4824L7.06634 10.8364H8.65534V19.5794H10.4123ZM18.7924 19.2294C19.0024 19.2294 19.1658 19.2878 19.2824 19.4044C19.4038 19.5211 19.4644 19.6751 19.4644 19.8664V21.0004H12.4224V20.3704C12.4224 20.2491 12.4481 20.1184 12.4994 19.9784C12.5508 19.8338 12.6371 19.7031 12.7584 19.5864L15.7684 16.5694C16.0251 16.3128 16.2514 16.0678 16.4474 15.8344C16.6434 15.5964 16.8068 15.3654 16.9374 15.1414C17.0681 14.9128 17.1661 14.6818 17.2314 14.4484C17.2968 14.2151 17.3294 13.9701 17.3294 13.7134C17.3294 13.2608 17.2128 12.9178 16.9794 12.6844C16.7461 12.4464 16.4171 12.3274 15.9924 12.3274C15.8058 12.3274 15.6331 12.3554 15.4744 12.4114C15.3204 12.4628 15.1804 12.5351 15.0544 12.6284C14.9331 12.7218 14.8281 12.8314 14.7394 12.9574C14.6508 13.0834 14.5854 13.2211 14.5434 13.3704C14.4594 13.6038 14.3451 13.7601 14.2004 13.8394C14.0604 13.9141 13.8598 13.9304 13.5984 13.8884L12.5764 13.7064C12.6511 13.2118 12.7911 12.7778 12.9964 12.4044C13.2018 12.0311 13.4584 11.7208 13.7664 11.4734C14.0744 11.2261 14.4268 11.0418 14.8234 10.9204C15.2201 10.7944 15.6471 10.7314 16.1044 10.7314C16.5851 10.7314 17.0214 10.8038 17.4134 10.9484C17.8101 11.0884 18.1484 11.2868 18.4284 11.5434C18.7084 11.7954 18.9254 12.1011 19.0794 12.4604C19.2334 12.8198 19.3104 13.2164 19.3104 13.6504C19.3104 14.0238 19.2568 14.3691 19.1494 14.6864C19.0421 15.0038 18.8951 15.3071 18.7084 15.5964C18.5264 15.8811 18.3141 16.1588 18.0714 16.4294C17.8288 16.7001 17.5721 16.9731 17.3014 17.2484L15.1454 19.4534C15.3834 19.3834 15.6191 19.3298 15.8524 19.2924C16.0858 19.2504 16.3051 19.2294 16.5104 19.2294H18.7924ZM21.4535 13.7064C21.5282 13.2118 21.6682 12.7778 21.8735 12.4044C22.0789 12.0311 22.3355 11.7208 22.6435 11.4734C22.9515 11.2261 23.3015 11.0418 23.6935 10.9204C24.0902 10.7944 24.5172 10.7314 24.9745 10.7314C25.4599 10.7314 25.8939 10.8014 26.2765 10.9414C26.6639 11.0768 26.9905 11.2634 27.2565 11.5014C27.5225 11.7394 27.7255 12.0171 27.8655 12.3344C28.0102 12.6518 28.0825 12.9924 28.0825 13.3564C28.0825 13.6784 28.0475 13.9631 27.9775 14.2104C27.9122 14.4531 27.8119 14.6654 27.6765 14.8474C27.5459 15.0294 27.3825 15.1834 27.1865 15.3094C26.9952 15.4354 26.7735 15.5404 26.5215 15.6244C27.6882 16.0071 28.2715 16.7841 28.2715 17.9554C28.2715 18.4734 28.1759 18.9308 27.9845 19.3274C27.7932 19.7194 27.5365 20.0484 27.2145 20.3144C26.8925 20.5804 26.5169 20.7811 26.0875 20.9164C25.6629 21.0471 25.2172 21.1124 24.7505 21.1124C24.2559 21.1124 23.8195 21.0564 23.4415 20.9444C23.0635 20.8324 22.7299 20.6644 22.4405 20.4404C22.1559 20.2164 21.9109 19.9364 21.7055 19.6004C21.5002 19.2598 21.3205 18.8631 21.1665 18.4104L22.0205 18.0604C22.2445 17.9671 22.4522 17.9414 22.6435 17.9834C22.8395 18.0254 22.9795 18.1281 23.0635 18.2914C23.1569 18.4688 23.2549 18.6321 23.3575 18.7814C23.4649 18.9308 23.5839 19.0614 23.7145 19.1734C23.8452 19.2808 23.9922 19.3648 24.1555 19.4254C24.3235 19.4861 24.5149 19.5164 24.7295 19.5164C25.0002 19.5164 25.2359 19.4721 25.4365 19.3834C25.6372 19.2948 25.8052 19.1804 25.9405 19.0404C26.0759 18.8958 26.1762 18.7348 26.2415 18.5574C26.3115 18.3754 26.3465 18.1958 26.3465 18.0184C26.3465 17.7851 26.3255 17.5751 26.2835 17.3884C26.2415 17.1971 26.1435 17.0361 25.9895 16.9054C25.8402 16.7701 25.6162 16.6674 25.3175 16.5974C25.0235 16.5228 24.6222 16.4854 24.1135 16.4854V15.1274C24.5382 15.1274 24.8859 15.0924 25.1565 15.0224C25.4272 14.9524 25.6395 14.8544 25.7935 14.7284C25.9475 14.6024 26.0525 14.4508 26.1085 14.2734C26.1692 14.0961 26.1995 13.9024 26.1995 13.6924C26.1995 13.2491 26.0829 12.9108 25.8495 12.6774C25.6209 12.4441 25.2942 12.3274 24.8695 12.3274C24.6829 12.3274 24.5102 12.3554 24.3515 12.4114C24.1975 12.4628 24.0575 12.5351 23.9315 12.6284C23.8102 12.7218 23.7052 12.8314 23.6165 12.9574C23.5279 13.0834 23.4625 13.2211 23.4205 13.3704C23.3319 13.6038 23.2175 13.7601 23.0775 13.8394C22.9375 13.9141 22.7345 13.9304 22.4685 13.8884L21.4535 13.7064Z",
4498
- fill: tintColor
4499
- }));
4726
+ switch (type) {
4727
+ case "Geometry":
4728
+ {
4729
+ return /*#__PURE__*/React.createElement("svg", {
4730
+ width: "32",
4731
+ height: "32",
4732
+ viewBox: "0 0 32 32",
4733
+ fill: "none",
4734
+ xmlns: "http://www.w3.org/2000/svg"
4735
+ }, /*#__PURE__*/React.createElement("path", {
4736
+ fillRule: "evenodd",
4737
+ clipRule: "evenodd",
4738
+ d: "M7.57584 7.09442C7.92723 6.92984 8.3421 6.98339 8.64018 7.23179L26.6402 22.2318C26.9636 22.5013 27.0836 22.9446 26.9403 23.3404C26.7969 23.7363 26.421 24 26 24H8C7.44772 24 7 23.5523 7 23V8.00001C7 7.61199 7.22446 7.259 7.57584 7.09442ZM9 10.1351V17H13C13.5523 17 14 17.4477 14 18V22H23.238L9 10.1351ZM12 22V19H9V22H12Z",
4739
+ fill: tintColor
4740
+ }));
4741
+ }
4742
+ case "Operators":
4743
+ {
4744
+ return /*#__PURE__*/React.createElement("svg", {
4745
+ viewBox: "0 0 32 32",
4746
+ fill: "none",
4747
+ xmlns: "http://www.w3.org/2000/svg"
4748
+ }, /*#__PURE__*/React.createElement("path", {
4749
+ fillRule: "evenodd",
4750
+ clipRule: "evenodd",
4751
+ d: "M29 6H30V7H29V6ZM27 6C27 4.89543 27.8954 4 29 4H30C31.1046 4 32 4.89543 32 6V7C32 8.10457 31.1046 9 30 9H29C27.8954 9 27 8.10457 27 7V6ZM11.1318 6.50386C11.3098 6.19229 11.6411 6 12 6H14C14.5523 6 15 6.44772 15 7C15 7.55228 14.5523 8 14 8H12.5803L8.86824 14.4961C8.68527 14.8163 8.34091 15.0098 7.97225 14.9996C7.6036 14.9894 7.2705 14.7771 7.10557 14.4472L5.10557 10.4472C4.85858 9.95324 5.05881 9.35256 5.55279 9.10557C6.04676 8.85858 6.64744 9.05881 6.89443 9.55279L8.0588 11.8815L11.1318 6.50386ZM7.70676 16.2925C8.09748 16.6829 8.09779 17.316 7.70745 17.7068C7.28543 18.1292 6.84383 18.7303 6.51157 19.3658C6.17039 20.0184 6 20.601 6 21C6 21.3789 6.17235 21.9897 6.51638 22.6649C6.85315 23.3259 7.28488 23.9121 7.66786 24.2557C8.07892 24.6246 8.11314 25.2568 7.74429 25.6679C7.37544 26.0789 6.7432 26.1131 6.33214 25.7443C5.7161 25.1915 5.14783 24.3844 4.73434 23.5728C4.32813 22.7755 3.99999 21.8345 4 21C4.00001 20.1391 4.3301 19.2217 4.73917 18.4392C5.15715 17.6397 5.71554 16.8708 6.29255 16.2932C6.68288 15.9025 7.31605 15.9022 7.70676 16.2925ZM11.2932 16.2925C11.684 15.9022 12.3171 15.9025 12.7075 16.2932C13.2845 16.8708 13.8428 17.6397 14.2608 18.4392C14.6699 19.2217 15 20.1391 15 21C15 21.8345 14.6719 22.7755 14.2657 23.5728C13.8522 24.3844 13.2839 25.1915 12.6679 25.7443C12.2568 26.1131 11.6246 26.0789 11.2557 25.6679C10.8869 25.2568 10.9211 24.6246 11.3321 24.2557C11.7151 23.9121 12.1469 23.3259 12.4836 22.6649C12.8276 21.9897 13 21.3789 13 21C13 20.601 12.8296 20.0184 12.4884 19.3658C12.1562 18.7303 11.7146 18.1292 11.2925 17.7068C10.9022 17.316 10.9025 16.6829 11.2932 16.2925ZM27.9363 17.6489C28.1302 18.166 27.8682 18.7424 27.3511 18.9363L21.848 21L27.3511 23.0637C27.8682 23.2576 28.1302 23.834 27.9363 24.3511C27.7424 24.8682 27.166 25.1302 26.6489 24.9363L18.6489 21.9363C18.2586 21.79 18 21.4168 18 21C18 20.5832 18.2586 20.21 18.6489 20.0637L26.6489 17.0637C27.166 16.8698 27.7424 17.1318 27.9363 17.6489ZM21 8V13H24V8H21ZM20 6C19.4477 6 19 6.44772 19 7V14C19 14.5523 19.4477 15 20 15H25C25.5523 15 26 14.5523 26 14V7C26 6.44772 25.5523 6 25 6H20Z",
4752
+ fill: tintColor
4753
+ }));
4754
+ }
4755
+ case "Numbers":
4756
+ {
4757
+ return /*#__PURE__*/React.createElement("svg", {
4758
+ width: "32",
4759
+ height: "32",
4760
+ viewBox: "0 0 32 32",
4761
+ fill: "none",
4762
+ xmlns: "http://www.w3.org/2000/svg"
4763
+ }, /*#__PURE__*/React.createElement("path", {
4764
+ d: "M10.4123 19.5794V21.0004H4.71434V19.5794H6.73034V14.0214C6.73034 13.9001 6.73267 13.7764 6.73734 13.6504C6.742 13.5244 6.749 13.3961 6.75834 13.2654L5.42834 14.3714C5.335 14.4414 5.244 14.4858 5.15534 14.5044C5.06667 14.5231 4.98267 14.5254 4.90334 14.5114C4.824 14.4928 4.754 14.4648 4.69334 14.4274C4.63267 14.3854 4.586 14.3434 4.55334 14.3014L3.94434 13.4824L7.06634 10.8364H8.65534V19.5794H10.4123ZM18.7924 19.2294C19.0024 19.2294 19.1658 19.2878 19.2824 19.4044C19.4038 19.5211 19.4644 19.6751 19.4644 19.8664V21.0004H12.4224V20.3704C12.4224 20.2491 12.4481 20.1184 12.4994 19.9784C12.5508 19.8338 12.6371 19.7031 12.7584 19.5864L15.7684 16.5694C16.0251 16.3128 16.2514 16.0678 16.4474 15.8344C16.6434 15.5964 16.8068 15.3654 16.9374 15.1414C17.0681 14.9128 17.1661 14.6818 17.2314 14.4484C17.2968 14.2151 17.3294 13.9701 17.3294 13.7134C17.3294 13.2608 17.2128 12.9178 16.9794 12.6844C16.7461 12.4464 16.4171 12.3274 15.9924 12.3274C15.8058 12.3274 15.6331 12.3554 15.4744 12.4114C15.3204 12.4628 15.1804 12.5351 15.0544 12.6284C14.9331 12.7218 14.8281 12.8314 14.7394 12.9574C14.6508 13.0834 14.5854 13.2211 14.5434 13.3704C14.4594 13.6038 14.3451 13.7601 14.2004 13.8394C14.0604 13.9141 13.8598 13.9304 13.5984 13.8884L12.5764 13.7064C12.6511 13.2118 12.7911 12.7778 12.9964 12.4044C13.2018 12.0311 13.4584 11.7208 13.7664 11.4734C14.0744 11.2261 14.4268 11.0418 14.8234 10.9204C15.2201 10.7944 15.6471 10.7314 16.1044 10.7314C16.5851 10.7314 17.0214 10.8038 17.4134 10.9484C17.8101 11.0884 18.1484 11.2868 18.4284 11.5434C18.7084 11.7954 18.9254 12.1011 19.0794 12.4604C19.2334 12.8198 19.3104 13.2164 19.3104 13.6504C19.3104 14.0238 19.2568 14.3691 19.1494 14.6864C19.0421 15.0038 18.8951 15.3071 18.7084 15.5964C18.5264 15.8811 18.3141 16.1588 18.0714 16.4294C17.8288 16.7001 17.5721 16.9731 17.3014 17.2484L15.1454 19.4534C15.3834 19.3834 15.6191 19.3298 15.8524 19.2924C16.0858 19.2504 16.3051 19.2294 16.5104 19.2294H18.7924ZM21.4535 13.7064C21.5282 13.2118 21.6682 12.7778 21.8735 12.4044C22.0789 12.0311 22.3355 11.7208 22.6435 11.4734C22.9515 11.2261 23.3015 11.0418 23.6935 10.9204C24.0902 10.7944 24.5172 10.7314 24.9745 10.7314C25.4599 10.7314 25.8939 10.8014 26.2765 10.9414C26.6639 11.0768 26.9905 11.2634 27.2565 11.5014C27.5225 11.7394 27.7255 12.0171 27.8655 12.3344C28.0102 12.6518 28.0825 12.9924 28.0825 13.3564C28.0825 13.6784 28.0475 13.9631 27.9775 14.2104C27.9122 14.4531 27.8119 14.6654 27.6765 14.8474C27.5459 15.0294 27.3825 15.1834 27.1865 15.3094C26.9952 15.4354 26.7735 15.5404 26.5215 15.6244C27.6882 16.0071 28.2715 16.7841 28.2715 17.9554C28.2715 18.4734 28.1759 18.9308 27.9845 19.3274C27.7932 19.7194 27.5365 20.0484 27.2145 20.3144C26.8925 20.5804 26.5169 20.7811 26.0875 20.9164C25.6629 21.0471 25.2172 21.1124 24.7505 21.1124C24.2559 21.1124 23.8195 21.0564 23.4415 20.9444C23.0635 20.8324 22.7299 20.6644 22.4405 20.4404C22.1559 20.2164 21.9109 19.9364 21.7055 19.6004C21.5002 19.2598 21.3205 18.8631 21.1665 18.4104L22.0205 18.0604C22.2445 17.9671 22.4522 17.9414 22.6435 17.9834C22.8395 18.0254 22.9795 18.1281 23.0635 18.2914C23.1569 18.4688 23.2549 18.6321 23.3575 18.7814C23.4649 18.9308 23.5839 19.0614 23.7145 19.1734C23.8452 19.2808 23.9922 19.3648 24.1555 19.4254C24.3235 19.4861 24.5149 19.5164 24.7295 19.5164C25.0002 19.5164 25.2359 19.4721 25.4365 19.3834C25.6372 19.2948 25.8052 19.1804 25.9405 19.0404C26.0759 18.8958 26.1762 18.7348 26.2415 18.5574C26.3115 18.3754 26.3465 18.1958 26.3465 18.0184C26.3465 17.7851 26.3255 17.5751 26.2835 17.3884C26.2415 17.1971 26.1435 17.0361 25.9895 16.9054C25.8402 16.7701 25.6162 16.6674 25.3175 16.5974C25.0235 16.5228 24.6222 16.4854 24.1135 16.4854V15.1274C24.5382 15.1274 24.8859 15.0924 25.1565 15.0224C25.4272 14.9524 25.6395 14.8544 25.7935 14.7284C25.9475 14.6024 26.0525 14.4508 26.1085 14.2734C26.1692 14.0961 26.1995 13.9024 26.1995 13.6924C26.1995 13.2491 26.0829 12.9108 25.8495 12.6774C25.6209 12.4441 25.2942 12.3274 24.8695 12.3274C24.6829 12.3274 24.5102 12.3554 24.3515 12.4114C24.1975 12.4628 24.0575 12.5351 23.9315 12.6284C23.8102 12.7218 23.7052 12.8314 23.6165 12.9574C23.5279 13.0834 23.4625 13.2211 23.4205 13.3704C23.3319 13.6038 23.2175 13.7601 23.0775 13.8394C22.9375 13.9141 22.7345 13.9304 22.4685 13.8884L21.4535 13.7064Z",
4765
+ fill: tintColor
4766
+ }));
4767
+ }
4768
+ case "Extras":
4769
+ {
4770
+ return /*#__PURE__*/React.createElement("svg", {
4771
+ xmlns: "http://www.w3.org/2000/svg",
4772
+ width: "20",
4773
+ height: "20",
4774
+ fill: "currentColor",
4775
+ viewBox: "0 0 256 256"
4776
+ }, /*#__PURE__*/React.createElement("path", {
4777
+ 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"
4778
+ }));
4779
+ }
4780
+ default:
4781
+ {
4782
+ // type as never;
4783
+ throw new Error("Invalid icon type");
4784
+ }
4500
4785
  }
4501
-
4502
- // type as never;
4503
- throw new Error("Invalid icon type");
4504
4786
  };
4505
4787
 
4506
- const styles$7 = StyleSheet.create({
4788
+ const styles$8 = StyleSheet.create({
4507
4789
  base: {
4508
4790
  display: "flex",
4509
4791
  width: 44,
@@ -4567,7 +4849,8 @@ class TabbarItem extends React.Component {
4567
4849
  } = this.props;
4568
4850
  return /*#__PURE__*/React.createElement(Clickable, {
4569
4851
  onClick: onClick,
4570
- disabled: itemState === "disabled"
4852
+ disabled: itemState === "disabled",
4853
+ "aria-label": itemType
4571
4854
  }, ({
4572
4855
  hovered,
4573
4856
  focused,
@@ -4575,14 +4858,14 @@ class TabbarItem extends React.Component {
4575
4858
  }) => {
4576
4859
  const tintColor = imageTintColor(itemState, hovered, focused, pressed);
4577
4860
  return /*#__PURE__*/React.createElement(View$1, {
4578
- style: [styles$7.base, itemState !== "disabled" && hovered && styles$7.hovered, focused && styles$7.focused, pressed && styles$7.pressed]
4861
+ style: [styles$8.base, itemState !== "disabled" && hovered && styles$8.hovered, focused && styles$8.focused, pressed && styles$8.pressed]
4579
4862
  }, /*#__PURE__*/React.createElement(View$1, {
4580
- style: [styles$7.innerBox, pressed && styles$7.innerBoxPressed]
4863
+ style: [styles$8.innerBox, pressed && styles$8.innerBoxPressed]
4581
4864
  }, /*#__PURE__*/React.createElement(IconAsset, {
4582
4865
  type: itemType,
4583
4866
  tintColor: tintColor
4584
4867
  })), itemState === "active" && /*#__PURE__*/React.createElement(View$1, {
4585
- style: [styles$7.activeIndicator, {
4868
+ style: [styles$8.activeIndicator, {
4586
4869
  backgroundColor: tintColor
4587
4870
  }]
4588
4871
  }));
@@ -4590,43 +4873,31 @@ class TabbarItem extends React.Component {
4590
4873
  }
4591
4874
  }
4592
4875
 
4593
- const styles$6 = StyleSheet.create({
4876
+ const styles$7 = StyleSheet.create({
4594
4877
  tabbar: {
4595
4878
  display: "flex",
4596
4879
  flexDirection: "row",
4597
- background: Color.offWhite,
4598
4880
  paddingTop: 2,
4599
- paddingBottom: 2,
4600
- borderTop: `1px solid ${Color.offBlack50}`,
4601
- borderBottom: `1px solid ${Color.offBlack50}`
4881
+ paddingBottom: 2
4602
4882
  }
4603
4883
  });
4604
- class Tabbar extends React.Component {
4605
- constructor(...args) {
4606
- super(...args);
4607
- this.state = {
4608
- selectedItem: 0
4609
- };
4610
- }
4611
- render() {
4612
- const {
4613
- items,
4614
- onSelect
4615
- } = this.props;
4616
- return /*#__PURE__*/React.createElement(View$1, {
4617
- style: styles$6.tabbar
4618
- }, items.map((item, index) => /*#__PURE__*/React.createElement(TabbarItem, {
4619
- key: `tabbar-item-${index}`,
4620
- itemState: index === this.state.selectedItem ? "active" : "inactive",
4621
- itemType: item,
4622
- onClick: () => {
4623
- this.setState({
4624
- selectedItem: index
4625
- });
4626
- onSelect(item);
4627
- }
4628
- })));
4629
- }
4884
+ function Tabbar(props) {
4885
+ const {
4886
+ items,
4887
+ selectedItem,
4888
+ onSelectItem,
4889
+ style
4890
+ } = props;
4891
+ return /*#__PURE__*/React.createElement(View$1, {
4892
+ style: [styles$7.tabbar, style]
4893
+ }, items.map(item => /*#__PURE__*/React.createElement(TabbarItem, {
4894
+ key: `tabbar-item-${item}`,
4895
+ itemState: item === selectedItem ? "active" : "inactive",
4896
+ itemType: item,
4897
+ onClick: () => {
4898
+ onSelectItem(item);
4899
+ }
4900
+ })));
4630
4901
  }
4631
4902
 
4632
4903
  /**
@@ -4674,7 +4945,7 @@ class Echo extends React.Component {
4674
4945
  style: containerStyle
4675
4946
  }, /*#__PURE__*/React.createElement(KeypadButton$2, {
4676
4947
  icon: icon,
4677
- type: KeyType.ECHO
4948
+ type: "ECHO"
4678
4949
  }));
4679
4950
  }
4680
4951
  }
@@ -4764,7 +5035,7 @@ class MultiSymbolPopover extends React.Component {
4764
5035
  // TODO(charlie): We have to require this lazily because of a cyclic
4765
5036
  // dependence in our components.
4766
5037
  return /*#__PURE__*/React.createElement(View, {
4767
- style: styles$5.container
5038
+ style: styles$6.container
4768
5039
  }, keys.map(key => {
4769
5040
  return /*#__PURE__*/React.createElement(TouchableKeypadButton$1, {
4770
5041
  keyConfig: key,
@@ -4773,7 +5044,7 @@ class MultiSymbolPopover extends React.Component {
4773
5044
  }));
4774
5045
  }
4775
5046
  }
4776
- const styles$5 = StyleSheet.create({
5047
+ const styles$6 = StyleSheet.create({
4777
5048
  container: {
4778
5049
  flexDirection: "column-reverse",
4779
5050
  position: "relative",
@@ -4888,18 +5159,6 @@ const setCursor = cursor => {
4888
5159
 
4889
5160
  // Gesture actions
4890
5161
 
4891
- const onSwipeChange = dx => {
4892
- return {
4893
- type: "OnSwipeChange",
4894
- dx
4895
- };
4896
- };
4897
- const onSwipeEnd = dx => {
4898
- return {
4899
- type: "OnSwipeEnd",
4900
- dx
4901
- };
4902
- };
4903
5162
  const setActiveNodes = activeNodes => {
4904
5163
  return {
4905
5164
  type: "SetActiveNodes",
@@ -5068,31 +5327,33 @@ class TwoPageKeypad extends React.Component {
5068
5327
  } = this.state;
5069
5328
  if (paginationEnabled) {
5070
5329
  return /*#__PURE__*/React.createElement(Keypad$2, {
5071
- style: [column$2, styles$4.keypad]
5330
+ style: [column$2, styles$5.keypad]
5072
5331
  }, /*#__PURE__*/React.createElement(Tabbar, {
5073
5332
  items: ["Numbers", "Operators"],
5074
- onSelect: selectedItem => {
5333
+ selectedItem: selectedPage,
5334
+ onSelectItem: selectedItem => {
5075
5335
  this.setState({
5076
5336
  selectedPage: selectedItem
5077
5337
  });
5078
- }
5338
+ },
5339
+ style: styles$5.tabbar
5079
5340
  }), /*#__PURE__*/React.createElement(View, {
5080
- style: styles$4.borderTop
5341
+ style: styles$5.borderTop
5081
5342
  }, selectedPage === "Numbers" && rightPage, selectedPage === "Operators" && leftPage));
5082
5343
  } else {
5083
5344
  return /*#__PURE__*/React.createElement(Keypad$2, {
5084
- style: styles$4.keypad
5345
+ style: styles$5.keypad
5085
5346
  }, /*#__PURE__*/React.createElement(View, {
5086
5347
  style: row$4
5087
5348
  }, /*#__PURE__*/React.createElement(View, {
5088
5349
  style: fullWidth$2
5089
5350
  }, leftPage), /*#__PURE__*/React.createElement(View, {
5090
- style: [styles$4.borderLeft, fullWidth$2]
5351
+ style: [styles$5.borderLeft, fullWidth$2]
5091
5352
  }, rightPage)));
5092
5353
  }
5093
5354
  }
5094
5355
  }
5095
- const styles$4 = StyleSheet.create({
5356
+ const styles$5 = StyleSheet.create({
5096
5357
  keypad: {
5097
5358
  // Set the background to light grey, so that when the user drags the
5098
5359
  // keypad pages past the edges, there's a grey backdrop.
@@ -5104,6 +5365,11 @@ const styles$4 = StyleSheet.create({
5104
5365
  borderLeft: {
5105
5366
  borderLeft: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`,
5106
5367
  boxSizing: "content-box"
5368
+ },
5369
+ tabbar: {
5370
+ background: Color.offWhite,
5371
+ borderTop: `1px solid ${Color.offBlack50}`,
5372
+ borderBottom: `1px solid ${Color.offBlack50}`
5107
5373
  }
5108
5374
  });
5109
5375
  const mapStateToProps$3 = state => {
@@ -5173,7 +5439,7 @@ class ExpressionKeypad extends React.Component {
5173
5439
  } else {
5174
5440
  dismissOrJumpOutKey = KeyConfigs.DISMISS;
5175
5441
  }
5176
- const rightPageStyle = [row$3, fullWidth$1, styles$3.rightPage, roundTopRight && roundedTopRight$1];
5442
+ const rightPageStyle = [row$3, fullWidth$1, styles$4.rightPage, roundTopRight && roundedTopRight$1];
5177
5443
  const rightPage = /*#__PURE__*/React.createElement(View, {
5178
5444
  style: rightPageStyle
5179
5445
  }, /*#__PURE__*/React.createElement(View, {
@@ -5245,7 +5511,7 @@ class ExpressionKeypad extends React.Component {
5245
5511
  keyConfig: dismissOrJumpOutKey,
5246
5512
  borders: BorderStyles.LEFT
5247
5513
  })));
5248
- const leftPageStyle = [row$3, fullWidth$1, styles$3.leftPage, roundTopLeft && roundedTopLeft$2];
5514
+ const leftPageStyle = [row$3, fullWidth$1, styles$4.leftPage, roundTopLeft && roundedTopLeft$2];
5249
5515
  const leftPage = /*#__PURE__*/React.createElement(View, {
5250
5516
  style: leftPageStyle
5251
5517
  }, /*#__PURE__*/React.createElement(View, {
@@ -5325,7 +5591,7 @@ class ExpressionKeypad extends React.Component {
5325
5591
  });
5326
5592
  }
5327
5593
  }
5328
- const styles$3 = StyleSheet.create({
5594
+ const styles$4 = StyleSheet.create({
5329
5595
  // NOTE(charlie): These backgrounds are applied to as to fill in some
5330
5596
  // unfortunate 'cracks' in the layout. However, not all keys in the first
5331
5597
  // page use this background color (namely, the 'command' keys, backspace and
@@ -5501,7 +5767,7 @@ class NavigationPad extends React.Component {
5501
5767
  roundTopLeft,
5502
5768
  style
5503
5769
  } = this.props;
5504
- const containerStyle = [column, centered$1, styles$2.container, roundTopLeft && roundedTopLeft, ...(Array.isArray(style) ? style : [style])];
5770
+ const containerStyle = [column, centered$1, styles$3.container, roundTopLeft && roundedTopLeft, ...(Array.isArray(style) ? style : [style])];
5505
5771
  return /*#__PURE__*/React.createElement(View, {
5506
5772
  style: containerStyle
5507
5773
  }, /*#__PURE__*/React.createElement(View, {
@@ -5509,32 +5775,32 @@ class NavigationPad extends React.Component {
5509
5775
  }, /*#__PURE__*/React.createElement(TouchableKeypadButton$1, {
5510
5776
  keyConfig: KeyConfigs.UP,
5511
5777
  borders: BorderStyles.NONE,
5512
- style: [styles$2.navigationKey, styles$2.topArrow]
5778
+ style: [styles$3.navigationKey, styles$3.topArrow]
5513
5779
  })), /*#__PURE__*/React.createElement(View, {
5514
5780
  style: [row$1, centered$1, stretch]
5515
5781
  }, /*#__PURE__*/React.createElement(TouchableKeypadButton$1, {
5516
5782
  keyConfig: KeyConfigs.LEFT,
5517
5783
  borders: BorderStyles.NONE,
5518
- style: [styles$2.navigationKey, styles$2.leftArrow]
5784
+ style: [styles$3.navigationKey, styles$3.leftArrow]
5519
5785
  }), /*#__PURE__*/React.createElement(View, {
5520
- style: styles$2.horizontalSpacer
5786
+ style: styles$3.horizontalSpacer
5521
5787
  }), /*#__PURE__*/React.createElement(TouchableKeypadButton$1, {
5522
5788
  keyConfig: KeyConfigs.RIGHT,
5523
5789
  borders: BorderStyles.NONE,
5524
- style: [styles$2.navigationKey, styles$2.rightArrow]
5790
+ style: [styles$3.navigationKey, styles$3.rightArrow]
5525
5791
  })), /*#__PURE__*/React.createElement(View, {
5526
5792
  style: [row$1, centered$1]
5527
5793
  }, /*#__PURE__*/React.createElement(TouchableKeypadButton$1, {
5528
5794
  keyConfig: KeyConfigs.DOWN,
5529
5795
  borders: BorderStyles.NONE,
5530
- style: [styles$2.navigationKey, styles$2.bottomArrow]
5796
+ style: [styles$3.navigationKey, styles$3.bottomArrow]
5531
5797
  })));
5532
5798
  }
5533
5799
  }
5534
5800
  const buttonSizePx = 48;
5535
5801
  const borderRadiusPx = 4;
5536
5802
  const borderWidthPx$1 = 1;
5537
- const styles$2 = StyleSheet.create({
5803
+ const styles$3 = StyleSheet.create({
5538
5804
  container: {
5539
5805
  backgroundColor: controlGrey,
5540
5806
  width: navigationPadWidthPx
@@ -5702,8 +5968,8 @@ class KeypadContainer extends React.Component {
5702
5968
  if (!active && !hasBeenActivated) {
5703
5969
  dynamicStyle = _extends({}, dynamicStyle, inlineStyles.invisible);
5704
5970
  }
5705
- const keypadContainerStyle = [row, centered, fullWidth, styles$1.keypadContainer, ...(Array.isArray(style) ? style : [style])];
5706
- const keypadStyle = [row, styles$1.keypadBorder, layoutMode === LayoutMode.FULLSCREEN ? styles$1.fullscreen : styles$1.compact];
5971
+ const keypadContainerStyle = [row, centered, fullWidth, styles$2.keypadContainer, ...(Array.isArray(style) ? style : [style])];
5972
+ const keypadStyle = [row, styles$2.keypadBorder, layoutMode === LayoutMode.FULLSCREEN ? styles$2.fullscreen : styles$2.compact];
5707
5973
 
5708
5974
  // TODO(charlie): When the keypad is shorter than the width of the
5709
5975
  // screen, add a border on its left and right edges, and round out the
@@ -5722,15 +5988,15 @@ class KeypadContainer extends React.Component {
5722
5988
  }
5723
5989
  }, navigationPadEnabled && /*#__PURE__*/React.createElement(NavigationPad, {
5724
5990
  roundTopLeft: layoutMode === LayoutMode.COMPACT,
5725
- style: styles$1.navigationPadContainer
5991
+ style: styles$2.navigationPadContainer
5726
5992
  }), /*#__PURE__*/React.createElement(View, {
5727
- style: styles$1.keypadLayout
5993
+ style: styles$2.keypadLayout
5728
5994
  }, this.renderKeypad())));
5729
5995
  }
5730
5996
  }
5731
5997
  const keypadAnimationDurationMs = 300;
5732
5998
  const borderWidthPx = 1;
5733
- const styles$1 = StyleSheet.create({
5999
+ const styles$2 = StyleSheet.create({
5734
6000
  keypadContainer: {
5735
6001
  bottom: 0,
5736
6002
  left: 0,
@@ -5991,7 +6257,8 @@ class GestureStateMachine {
5991
6257
  // Only respect the finger that started a swipe. Any other lingering
5992
6258
  // gestures are ignored.
5993
6259
  if (this.swipeState.touchId === touchId) {
5994
- this.handlers.onSwipeChange(pageX - this.swipeState.startX);
6260
+ var _this$handlers$onSwip, _this$handlers;
6261
+ (_this$handlers$onSwip = (_this$handlers = this.handlers).onSwipeChange) == null ? void 0 : _this$handlers$onSwip.call(_this$handlers, pageX - this.swipeState.startX);
5995
6262
  }
5996
6263
  } else if (this.touchState[touchId]) {
5997
6264
  // It could be touch events started outside the keypad and
@@ -6004,6 +6271,7 @@ class GestureStateMachine {
6004
6271
  const dx = pageX - startX;
6005
6272
  const shouldBeginSwiping = swipeEnabled && !swipeLocked && Math.abs(dx) > this.options.swipeThresholdPx;
6006
6273
  if (shouldBeginSwiping) {
6274
+ var _this$handlers$onSwip2, _this$handlers2;
6007
6275
  this._onSwipeStart();
6008
6276
 
6009
6277
  // Trigger the swipe.
@@ -6011,7 +6279,7 @@ class GestureStateMachine {
6011
6279
  touchId,
6012
6280
  startX
6013
6281
  };
6014
- this.handlers.onSwipeChange(pageX - this.swipeState.startX);
6282
+ (_this$handlers$onSwip2 = (_this$handlers2 = this.handlers).onSwipeChange) == null ? void 0 : _this$handlers$onSwip2.call(_this$handlers2, pageX - this.swipeState.startX);
6015
6283
  } else {
6016
6284
  const id = getId();
6017
6285
  if (id !== activeNodeId) {
@@ -6034,7 +6302,8 @@ class GestureStateMachine {
6034
6302
  // Only respect the finger that started a swipe. Any other lingering
6035
6303
  // gestures are ignored.
6036
6304
  if (this.swipeState.touchId === touchId) {
6037
- this.handlers.onSwipeEnd(pageX - this.swipeState.startX);
6305
+ var _this$handlers$onSwip3, _this$handlers3;
6306
+ (_this$handlers$onSwip3 = (_this$handlers3 = this.handlers).onSwipeEnd) == null ? void 0 : _this$handlers$onSwip3.call(_this$handlers3, pageX - this.swipeState.startX);
6038
6307
  this.swipeState = null;
6039
6308
  }
6040
6309
  } else if (this.touchState[touchId]) {
@@ -6068,7 +6337,8 @@ class GestureStateMachine {
6068
6337
  // displacement.
6069
6338
  if (this.swipeState) {
6070
6339
  if (this.swipeState.touchId === touchId) {
6071
- this.handlers.onSwipeEnd(0);
6340
+ var _this$handlers$onSwip4, _this$handlers4;
6341
+ (_this$handlers$onSwip4 = (_this$handlers4 = this.handlers).onSwipeEnd) == null ? void 0 : _this$handlers$onSwip4.call(_this$handlers4, 0);
6072
6342
  this.swipeState = null;
6073
6343
  }
6074
6344
  } else if (this.touchState[touchId]) {
@@ -6446,7 +6716,7 @@ class GestureManager {
6446
6716
  * Handle a touch-start event that originated in a node registered with the
6447
6717
  * gesture system.
6448
6718
  *
6449
- * @param {TouchEvent} evt - the raw touch event from the browser
6719
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
6450
6720
  * @param {string} id - the identifier of the DOM node in which the touch
6451
6721
  * occurred
6452
6722
  */
@@ -6473,7 +6743,7 @@ class GestureManager {
6473
6743
  * Handle a touch-move event that originated in a node registered with the
6474
6744
  * gesture system.
6475
6745
  *
6476
- * @param {TouchEvent} evt - the raw touch event from the browser
6746
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
6477
6747
  */
6478
6748
  onTouchMove(evt) {
6479
6749
  if (!this.trackEvents) {
@@ -6491,7 +6761,7 @@ class GestureManager {
6491
6761
  * Handle a touch-end event that originated in a node registered with the
6492
6762
  * gesture system.
6493
6763
  *
6494
- * @param {TouchEvent} evt - the raw touch event from the browser
6764
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
6495
6765
  */
6496
6766
  onTouchEnd(evt) {
6497
6767
  if (!this.trackEvents) {
@@ -6507,7 +6777,7 @@ class GestureManager {
6507
6777
  * Handle a touch-cancel event that originated in a node registered with the
6508
6778
  * gesture system.
6509
6779
  *
6510
- * @param {TouchEvent} evt - the raw touch event from the browser
6780
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
6511
6781
  */
6512
6782
  onTouchCancel(evt) {
6513
6783
  if (!this.trackEvents) {
@@ -6573,7 +6843,7 @@ const echoReducer = function echoReducer(state = initialEchoState, action) {
6573
6843
 
6574
6844
  // Add in the echo animation if the user performs a math
6575
6845
  // operation.
6576
- if (keyConfig.type === KeyType.VALUE || keyConfig.type === KeyType.OPERATOR) {
6846
+ if (keyConfig.type === "VALUE" || keyConfig.type === "OPERATOR") {
6577
6847
  return _extends({}, state, {
6578
6848
  echoes: [...state.echoes, {
6579
6849
  animationId: "" + _lastAnimationId++,
@@ -6610,7 +6880,7 @@ const inputReducer = function inputReducer(state = initialInputState, action) {
6610
6880
  });
6611
6881
  case "PressKey":
6612
6882
  const keyConfig = KeyConfigs[action.key];
6613
- if (keyConfig.type !== KeyType.KEYPAD_NAVIGATION) {
6883
+ if (keyConfig.type !== "KEYPAD_NAVIGATION") {
6614
6884
  // This is probably an anti-pattern but it works for the
6615
6885
  // case where we don't actually control the state but we
6616
6886
  // still want to communicate with the other object
@@ -6637,7 +6907,7 @@ const keypadForType = {
6637
6907
  };
6638
6908
 
6639
6909
  const initialKeypadState = {
6640
- extraKeys: ["x", "y", Keys.THETA, Keys.PI],
6910
+ extraKeys: ["x", "y", "THETA", "PI"],
6641
6911
  keypadType: defaultKeypadType,
6642
6912
  active: false
6643
6913
  };
@@ -6665,7 +6935,7 @@ const keypadReducer = function keypadReducer(state = initialKeypadState, action)
6665
6935
  // right actions when they occur. Hence, we figure off a
6666
6936
  // dismissal here rather than dispatching a dismiss action in
6667
6937
  // the first place.
6668
- if (keyConfig.id === Keys.DISMISS) {
6938
+ if (keyConfig.id === "DISMISS") {
6669
6939
  return keypadReducer(state, {
6670
6940
  type: "DismissKeypad"
6671
6941
  });
@@ -6904,19 +7174,13 @@ const createStore = () => {
6904
7174
  return new GestureManager({
6905
7175
  swipeEnabled
6906
7176
  }, {
6907
- onSwipeChange: dx => {
6908
- store.dispatch(onSwipeChange(dx));
6909
- },
6910
- onSwipeEnd: dx => {
6911
- store.dispatch(onSwipeEnd(dx));
6912
- },
6913
7177
  onActiveNodesChanged: activeNodes => {
6914
7178
  store.dispatch(setActiveNodes(activeNodes));
6915
7179
  },
6916
7180
  onClick: (key, layoutProps, inPopover) => {
6917
7181
  store.dispatch(pressKey(key, layoutProps.initialBounds, inPopover));
6918
7182
  }
6919
- }, [], [Keys.BACKSPACE, Keys.UP, Keys.RIGHT, Keys.DOWN, Keys.LEFT]);
7183
+ }, [], ["BACKSPACE", "UP", "RIGHT", "DOWN", "LEFT"]);
6920
7184
  };
6921
7185
  const initialGestureState = {
6922
7186
  popover: null,
@@ -7036,7 +7300,7 @@ class ProvidedKeypad extends React.Component {
7036
7300
  }
7037
7301
  }
7038
7302
 
7039
- const styles = StyleSheet.create({
7303
+ const styles$1 = StyleSheet.create({
7040
7304
  base: {
7041
7305
  display: "flex",
7042
7306
  justifyContent: "center",
@@ -7091,6 +7355,7 @@ class Button extends React.Component {
7091
7355
  render() {
7092
7356
  const {
7093
7357
  onPress,
7358
+ ariaLabel,
7094
7359
  children,
7095
7360
  style,
7096
7361
  tintColor
@@ -7099,18 +7364,19 @@ class Button extends React.Component {
7099
7364
  style: style
7100
7365
  }, /*#__PURE__*/React.createElement(Clickable, {
7101
7366
  onClick: onPress,
7102
- style: styles.clickable
7367
+ style: styles$1.clickable,
7368
+ "aria-label": ariaLabel
7103
7369
  }, ({
7104
7370
  hovered,
7105
7371
  focused,
7106
7372
  pressed
7107
7373
  }) => {
7108
7374
  return /*#__PURE__*/React.createElement(View$1, {
7109
- style: [styles.outerBoxBase, hovered && styles.outerBoxHover, pressed && styles.outerBoxPressed]
7375
+ style: [styles$1.outerBoxBase, hovered && styles$1.outerBoxHover, pressed && styles$1.outerBoxPressed]
7110
7376
  }, /*#__PURE__*/React.createElement(View$1, {
7111
- style: [styles.base, tintColor != null ? {
7377
+ style: [styles$1.base, tintColor != null ? {
7112
7378
  background: tintColor
7113
- } : undefined, hovered && styles.hovered, focused && styles.focused, pressed && styles.pressed]
7379
+ } : undefined, hovered && styles$1.hovered, focused && styles$1.focused, pressed && styles$1.pressed]
7114
7380
  }, children));
7115
7381
  }));
7116
7382
  }
@@ -7128,9 +7394,6 @@ asset.
7128
7394
  In the future it would be great if these were included from files so that
7129
7395
  no copying and pasting is necessary.
7130
7396
  */
7131
-
7132
- // TODO: This should be an enumeration of all of the possible legal values
7133
-
7134
7397
  function ButtonAsset({
7135
7398
  id
7136
7399
  }) {
@@ -7275,84 +7538,94 @@ function ButtonAsset({
7275
7538
  }));
7276
7539
  case "MINUS":
7277
7540
  return /*#__PURE__*/React.createElement("svg", {
7278
- width: "40",
7279
- height: "40",
7280
- viewBox: "0 0 40 40",
7281
- fill: "none",
7282
- xmlns: "http://www.w3.org/2000/svg"
7541
+ xmlns: "http://www.w3.org/2000/svg",
7542
+ width: "20",
7543
+ height: "20",
7544
+ fill: "currentColor",
7545
+ viewBox: "0 0 256 256"
7283
7546
  }, /*#__PURE__*/React.createElement("path", {
7284
- d: "M27 20H13",
7285
- stroke: "#21242C",
7286
- strokeWidth: "2",
7287
- strokeLinecap: "round",
7288
- strokeLinejoin: "round"
7547
+ d: "M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128Z"
7289
7548
  }));
7290
7549
  case "PLUS":
7550
+ return (
7551
+ /*#__PURE__*/
7552
+ // Phosphor Icons - Plus Bold
7553
+ React.createElement("svg", {
7554
+ xmlns: "http://www.w3.org/2000/svg",
7555
+ width: "20",
7556
+ height: "20",
7557
+ fill: "currentColor",
7558
+ viewBox: "0 0 256 256"
7559
+ }, /*#__PURE__*/React.createElement("path", {
7560
+ 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"
7561
+ }), " ")
7562
+ );
7563
+ case "TIMES":
7564
+ return (
7565
+ /*#__PURE__*/
7566
+ // Phosphor Icons - X Bold
7567
+ React.createElement("svg", {
7568
+ xmlns: "http://www.w3.org/2000/svg",
7569
+ width: "20",
7570
+ height: "20",
7571
+ fill: "#000000",
7572
+ viewBox: "0 0 256 256"
7573
+ }, /*#__PURE__*/React.createElement("path", {
7574
+ 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"
7575
+ }))
7576
+ );
7577
+ case "BACKSPACE":
7291
7578
  return /*#__PURE__*/React.createElement("svg", {
7292
- width: "40",
7293
- height: "40",
7294
- viewBox: "0 0 40 40",
7295
- fill: "none",
7296
- xmlns: "http://www.w3.org/2000/svg"
7579
+ xmlns: "http://www.w3.org/2000/svg",
7580
+ width: "20",
7581
+ height: "20",
7582
+ fill: "currentColor",
7583
+ viewBox: "0 0 256 256"
7297
7584
  }, /*#__PURE__*/React.createElement("path", {
7298
- d: "M27 20H13",
7299
- stroke: "#21242C",
7300
- strokeWidth: "2",
7301
- strokeLinecap: "round",
7302
- strokeLinejoin: "round"
7303
- }), /*#__PURE__*/React.createElement("path", {
7304
- d: "M20 13V27",
7305
- stroke: "#21242C",
7306
- strokeWidth: "2",
7307
- strokeLinecap: "round",
7308
- strokeLinejoin: "round"
7585
+ 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"
7309
7586
  }));
7310
- case "TIMES":
7587
+ case "DISMISS":
7311
7588
  return /*#__PURE__*/React.createElement("svg", {
7312
- width: "40",
7313
- height: "40",
7314
- viewBox: "0 0 40 40",
7315
- fill: "none",
7316
- xmlns: "http://www.w3.org/2000/svg"
7589
+ xmlns: "http://www.w3.org/2000/svg",
7590
+ width: "20",
7591
+ height: "20",
7592
+ fill: "currentColor",
7593
+ viewBox: "0 0 256 256"
7317
7594
  }, /*#__PURE__*/React.createElement("path", {
7318
- d: "M24.9498 24.9493L15.0503 15.0498",
7319
- stroke: "#21242C",
7320
- strokeWidth: "2",
7321
- strokeLinecap: "round",
7322
- strokeLinejoin: "round"
7323
- }), /*#__PURE__*/React.createElement("path", {
7324
- d: "M24.9498 15.0507L15.0503 24.9502",
7325
- stroke: "#21242C",
7326
- strokeWidth: "2",
7327
- strokeLinecap: "round",
7328
- strokeLinejoin: "round"
7595
+ // flip to point down
7596
+ transform: "scale(1,-1) translate(0, -260)",
7597
+ 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"
7329
7598
  }));
7330
- case "BACKSPACE":
7599
+ case "FRAC":
7331
7600
  return /*#__PURE__*/React.createElement("svg", {
7332
- width: "40",
7333
- height: "40",
7334
- viewBox: "0 0 40 40",
7601
+ width: "48",
7602
+ height: "48",
7603
+ viewBox: "0 0 48 48"
7604
+ }, /*#__PURE__*/React.createElement("g", {
7335
7605
  fill: "none",
7336
- xmlns: "http://www.w3.org/2000/svg"
7606
+ fillRule: "evenodd"
7337
7607
  }, /*#__PURE__*/React.createElement("path", {
7338
- fillRule: "evenodd",
7339
- clipRule: "evenodd",
7340
- 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",
7341
- fill: "#21242C"
7342
- }));
7343
- case "DISMISS":
7344
- return /*#__PURE__*/React.createElement("svg", {
7345
- width: "40",
7346
- height: "40",
7347
- viewBox: "0 0 40 40",
7348
7608
  fill: "none",
7349
- xmlns: "http://www.w3.org/2000/svg"
7609
+ d: "M0 0h48v48H0z"
7610
+ }), /*#__PURE__*/React.createElement("g", {
7611
+ transform: "translate(12 12)"
7350
7612
  }, /*#__PURE__*/React.createElement("path", {
7351
- fillRule: "evenodd",
7352
- clipRule: "evenodd",
7353
- 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",
7354
- fill: "#21242C"
7355
- }));
7613
+ fill: "none",
7614
+ d: "M0 0h24v24H0z"
7615
+ }), /*#__PURE__*/React.createElement("path", {
7616
+ 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",
7617
+ fill: "currentColor"
7618
+ }), /*#__PURE__*/React.createElement("rect", {
7619
+ fill: "currentColor",
7620
+ x: "2",
7621
+ y: "11",
7622
+ width: "20",
7623
+ height: "2",
7624
+ rx: "1"
7625
+ }), /*#__PURE__*/React.createElement("path", {
7626
+ 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",
7627
+ fill: "currentColor"
7628
+ }))));
7356
7629
  case "FRAC_INCLUSIVE":
7357
7630
  return /*#__PURE__*/React.createElement("svg", {
7358
7631
  width: "40",
@@ -7379,26 +7652,23 @@ function ButtonAsset({
7379
7652
  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",
7380
7653
  fill: "#21242C"
7381
7654
  }));
7655
+ // TODO(ned): Per the notes in `KeyConfigs`, shouldn't this be a comma
7656
+ // that we replace with the period icon for i18n? Duplicating for now.
7382
7657
  case "DECIMAL":
7658
+ case "PERIOD":
7383
7659
  return /*#__PURE__*/React.createElement("svg", {
7384
- width: "40",
7385
- height: "40",
7386
- viewBox: "0 0 40 40",
7387
- fill: "none",
7388
- xmlns: "http://www.w3.org/2000/svg"
7389
- }, /*#__PURE__*/React.createElement("g", {
7390
- clipPath: "url(#clip0)"
7391
- }, /*#__PURE__*/React.createElement("circle", {
7392
- cx: "20",
7393
- cy: "20",
7394
- r: "1.5",
7395
- fill: "#21242C"
7396
- })), /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("clipPath", {
7397
- id: "clip0"
7660
+ xmlns: "http://www.w3.org/2000/svg",
7661
+ width: "20",
7662
+ height: "20",
7663
+ fill: "currentColor",
7664
+ viewBox: "0 0 256 256"
7398
7665
  }, /*#__PURE__*/React.createElement("path", {
7399
- d: "M18.5 18.5H21.5V21.5H18.5V18.5Z",
7400
- fill: "white"
7401
- }))));
7666
+ d: "M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128Z"
7667
+ // moves decimal down to baseline of number icons,
7668
+ // otherwise indistinguishable from cdot
7669
+ ,
7670
+ transform: "translate(0 80)"
7671
+ }));
7402
7672
  case "RADICAL":
7403
7673
  return /*#__PURE__*/React.createElement("svg", {
7404
7674
  width: "40",
@@ -7413,6 +7683,38 @@ function ButtonAsset({
7413
7683
  fill: "#21242C"
7414
7684
  }));
7415
7685
  case "SQRT":
7686
+ return /*#__PURE__*/React.createElement("svg", {
7687
+ xmlns: "http://www.w3.org/2000/svg",
7688
+ width: "20",
7689
+ height: "20",
7690
+ fill: "currentColor",
7691
+ viewBox: "0 0 256 256"
7692
+ }, /*#__PURE__*/React.createElement("path", {
7693
+ 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"
7694
+ }));
7695
+ case "CUBE_ROOT":
7696
+ return /*#__PURE__*/React.createElement("svg", {
7697
+ width: "48",
7698
+ height: "48",
7699
+ viewBox: "0 0 48 48"
7700
+ }, /*#__PURE__*/React.createElement("g", {
7701
+ fill: "none",
7702
+ fillRule: "evenodd",
7703
+ transform: "translate(0, -4)"
7704
+ }, /*#__PURE__*/React.createElement("path", {
7705
+ fill: "none",
7706
+ d: "M0 0h48v48H0z"
7707
+ }), /*#__PURE__*/React.createElement("path", {
7708
+ 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",
7709
+ fill: "currentColor"
7710
+ }), /*#__PURE__*/React.createElement("path", {
7711
+ stroke: "currentColor",
7712
+ strokeWidth: "2",
7713
+ strokeLinecap: "round",
7714
+ strokeLinejoin: "round",
7715
+ d: "M14 29l4 6 9-14h7"
7716
+ })));
7717
+ case "EXP":
7416
7718
  return /*#__PURE__*/React.createElement("svg", {
7417
7719
  width: "40",
7418
7720
  height: "40",
@@ -7420,11 +7722,10 @@ function ButtonAsset({
7420
7722
  fill: "none",
7421
7723
  xmlns: "http://www.w3.org/2000/svg"
7422
7724
  }, /*#__PURE__*/React.createElement("path", {
7423
- d: "M10 21L14 27L23 13H30",
7424
- stroke: "#21242C",
7425
- strokeWidth: "2",
7426
- strokeLinecap: "round",
7427
- strokeLinejoin: "round"
7725
+ fillRule: "evenodd",
7726
+ clipRule: "evenodd",
7727
+ fill: "#21242C",
7728
+ 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"
7428
7729
  }));
7429
7730
  case "EXP_2":
7430
7731
  return /*#__PURE__*/React.createElement("svg", {
@@ -7439,25 +7740,20 @@ function ButtonAsset({
7439
7740
  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",
7440
7741
  fill: "#21242C"
7441
7742
  }));
7442
- case "EXP":
7743
+ case "EXP_3":
7443
7744
  return /*#__PURE__*/React.createElement("svg", {
7444
- width: "40",
7445
- height: "40",
7745
+ width: "42",
7746
+ height: "42",
7446
7747
  viewBox: "0 0 40 40",
7447
7748
  fill: "none",
7448
7749
  xmlns: "http://www.w3.org/2000/svg"
7449
7750
  }, /*#__PURE__*/React.createElement("path", {
7751
+ transform: "translate(0, -8)",
7450
7752
  fillRule: "evenodd",
7451
7753
  clipRule: "evenodd",
7452
- 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",
7453
- fill: "#21242C"
7754
+ fill: "#21242C",
7755
+ 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"
7454
7756
  }));
7455
- case "PI":
7456
- //TODO(NickR): use correct font, size, and color for this. It's not an SVG asset
7457
- return /*#__PURE__*/React.createElement("span", null, "pi");
7458
- case "X":
7459
- //TODO(NickR): use correct font, size, and color for this. It's not an SVG asset
7460
- return /*#__PURE__*/React.createElement("span", null, "x");
7461
7757
  case "TAN":
7462
7758
  return /*#__PURE__*/React.createElement("svg", {
7463
7759
  width: "40",
@@ -7491,7 +7787,334 @@ function ButtonAsset({
7491
7787
  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",
7492
7788
  fill: "#21242C"
7493
7789
  }));
7494
- default:
7790
+ case "DIVIDE":
7791
+ return /*#__PURE__*/React.createElement("svg", {
7792
+ xmlns: "http://www.w3.org/2000/svg",
7793
+ width: "20",
7794
+ height: "20",
7795
+ fill: "currentColor",
7796
+ viewBox: "0 0 256 256"
7797
+ }, /*#__PURE__*/React.createElement("path", {
7798
+ 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"
7799
+ }));
7800
+ case "EQUAL":
7801
+ return /*#__PURE__*/React.createElement("svg", {
7802
+ xmlns: "http://www.w3.org/2000/svg",
7803
+ width: "20",
7804
+ height: "20",
7805
+ fill: "currentColor",
7806
+ viewBox: "0 0 256 256"
7807
+ }, /*#__PURE__*/React.createElement("path", {
7808
+ 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"
7809
+ }));
7810
+ case "GT":
7811
+ return /*#__PURE__*/React.createElement("svg", {
7812
+ width: "44",
7813
+ height: "44",
7814
+ viewBox: "0 0 48 48"
7815
+ }, /*#__PURE__*/React.createElement("g", {
7816
+ fill: "none",
7817
+ fillRule: "evenodd"
7818
+ }, /*#__PURE__*/React.createElement("path", {
7819
+ fill: "none",
7820
+ d: "M0 0h48v48H0z"
7821
+ }), /*#__PURE__*/React.createElement("path", {
7822
+ fill: "none",
7823
+ d: "M12 12h24v24H12z"
7824
+ }), /*#__PURE__*/React.createElement("path", {
7825
+ stroke: "currentColor",
7826
+ strokeWidth: "2",
7827
+ strokeLinecap: "round",
7828
+ strokeLinejoin: "round",
7829
+ d: "M16 30l16-6-16-6"
7830
+ })));
7831
+ case "LT":
7832
+ return /*#__PURE__*/React.createElement("svg", {
7833
+ width: "44",
7834
+ height: "44",
7835
+ viewBox: "0 0 48 48"
7836
+ }, /*#__PURE__*/React.createElement("g", {
7837
+ fill: "none",
7838
+ fillRule: "evenodd"
7839
+ }, /*#__PURE__*/React.createElement("path", {
7840
+ fill: "none",
7841
+ d: "M0 0h48v48H0z"
7842
+ }), /*#__PURE__*/React.createElement("path", {
7843
+ fill: "none",
7844
+ d: "M12 12h24v24H12z"
7845
+ }), /*#__PURE__*/React.createElement("path", {
7846
+ stroke: "currentColor",
7847
+ strokeWidth: "2",
7848
+ strokeLinecap: "round",
7849
+ strokeLinejoin: "round",
7850
+ d: "M32 30l-16-6 16-6"
7851
+ })));
7852
+ case "GEQ":
7853
+ return /*#__PURE__*/React.createElement("svg", {
7854
+ width: "44",
7855
+ height: "44",
7856
+ viewBox: "0 0 48 48"
7857
+ }, /*#__PURE__*/React.createElement("g", {
7858
+ fill: "none",
7859
+ fillRule: "evenodd"
7860
+ }, /*#__PURE__*/React.createElement("path", {
7861
+ fill: "none",
7862
+ d: "M0 0h48v48H0z"
7863
+ }), /*#__PURE__*/React.createElement("path", {
7864
+ fill: "none",
7865
+ d: "M12 12h24v24H12z"
7866
+ }), /*#__PURE__*/React.createElement("path", {
7867
+ d: "M16 33h16M16 30l16-6-16-6",
7868
+ stroke: "currentColor",
7869
+ strokeWidth: "2",
7870
+ strokeLinecap: "round",
7871
+ strokeLinejoin: "round"
7872
+ })));
7873
+ case "LEQ":
7874
+ return /*#__PURE__*/React.createElement("svg", {
7875
+ width: "44",
7876
+ height: "44",
7877
+ viewBox: "0 0 48 48"
7878
+ }, /*#__PURE__*/React.createElement("g", {
7879
+ fill: "none",
7880
+ fillRule: "evenodd"
7881
+ }, /*#__PURE__*/React.createElement("path", {
7882
+ fill: "none",
7883
+ d: "M0 0h48v48H0z"
7884
+ }), /*#__PURE__*/React.createElement("path", {
7885
+ fill: "none",
7886
+ d: "M12 12h24v24H12z"
7887
+ }), /*#__PURE__*/React.createElement("path", {
7888
+ d: "M16 33h16M32 30l-16-6 16-6",
7889
+ stroke: "currentColor",
7890
+ strokeWidth: "2",
7891
+ strokeLinecap: "round",
7892
+ strokeLinejoin: "round"
7893
+ })));
7894
+ case "NEQ":
7895
+ return /*#__PURE__*/React.createElement("svg", {
7896
+ width: "44",
7897
+ height: "44",
7898
+ viewBox: "0 0 48 48"
7899
+ }, /*#__PURE__*/React.createElement("g", {
7900
+ fill: "none",
7901
+ fillRule: "evenodd"
7902
+ }, /*#__PURE__*/React.createElement("path", {
7903
+ fill: "none",
7904
+ d: "M0 0h48v48H0z"
7905
+ }), /*#__PURE__*/React.createElement("path", {
7906
+ fill: "none",
7907
+ d: "M12 12h24v24H12z"
7908
+ }), /*#__PURE__*/React.createElement("path", {
7909
+ d: "M19 33l10-18M16 21h17M16 27h17",
7910
+ stroke: "currentColor",
7911
+ strokeWidth: "2",
7912
+ strokeLinecap: "round",
7913
+ strokeLinejoin: "round"
7914
+ })));
7915
+ case "LN":
7916
+ return /*#__PURE__*/React.createElement("svg", {
7917
+ width: "48",
7918
+ height: "48",
7919
+ viewBox: "0 0 48 48"
7920
+ }, /*#__PURE__*/React.createElement("g", {
7921
+ fill: "none",
7922
+ fillRule: "evenodd"
7923
+ }, /*#__PURE__*/React.createElement("path", {
7924
+ fill: "none",
7925
+ d: "M0 0h48v48H0z"
7926
+ }), /*#__PURE__*/React.createElement("path", {
7927
+ 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",
7928
+ fill: "currentColor"
7929
+ })));
7930
+ case "LOG":
7931
+ return /*#__PURE__*/React.createElement("svg", {
7932
+ width: "48",
7933
+ height: "48",
7934
+ viewBox: "0 0 48 48"
7935
+ }, /*#__PURE__*/React.createElement("g", {
7936
+ fill: "none",
7937
+ fillRule: "evenodd"
7938
+ }, /*#__PURE__*/React.createElement("path", {
7939
+ fill: "none",
7940
+ d: "M0 0h48v48H0z"
7941
+ }), /*#__PURE__*/React.createElement("path", {
7942
+ 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",
7943
+ fill: "currentColor"
7944
+ })));
7945
+ case "LOG_N":
7946
+ return /*#__PURE__*/React.createElement("svg", {
7947
+ width: "48",
7948
+ height: "48",
7949
+ viewBox: "0 0 48 48"
7950
+ }, /*#__PURE__*/React.createElement("g", {
7951
+ fill: "none",
7952
+ fillRule: "evenodd"
7953
+ }, /*#__PURE__*/React.createElement("path", {
7954
+ fill: "none",
7955
+ d: "M0 0h48v48H0z"
7956
+ }), /*#__PURE__*/React.createElement("path", {
7957
+ 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",
7958
+ fill: "currentColor"
7959
+ })));
7960
+ case "PERCENT":
7961
+ return /*#__PURE__*/React.createElement("svg", {
7962
+ xmlns: "http://www.w3.org/2000/svg",
7963
+ width: "20",
7964
+ height: "20",
7965
+ fill: "currentColor",
7966
+ viewBox: "0 0 256 256"
7967
+ }, /*#__PURE__*/React.createElement("path", {
7968
+ 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"
7969
+ }));
7970
+ case "CDOT":
7971
+ return /*#__PURE__*/React.createElement("svg", {
7972
+ xmlns: "http://www.w3.org/2000/svg",
7973
+ width: "20",
7974
+ height: "20",
7975
+ fill: "currentColor",
7976
+ viewBox: "0 0 256 256"
7977
+ }, /*#__PURE__*/React.createElement("path", {
7978
+ d: "M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128Z"
7979
+ }));
7980
+ case "PI":
7981
+ return /*#__PURE__*/React.createElement("svg", {
7982
+ xmlns: "http://www.w3.org/2000/svg",
7983
+ width: "20",
7984
+ height: "20",
7985
+ fill: "currentColor",
7986
+ viewBox: "0 0 256 256"
7987
+ }, /*#__PURE__*/React.createElement("path", {
7988
+ 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"
7989
+ }));
7990
+ case "a":
7991
+ return /*#__PURE__*/React.createElement("svg", {
7992
+ xmlns: "http://www.w3.org/2000/svg",
7993
+ width: "20",
7994
+ height: "20",
7995
+ viewBox: "0 0 384 512"
7996
+ }, /*#__PURE__*/React.createElement("path", {
7997
+ d: "M221.5 51.7C216.6 39.8 204.9 32 192 32s-24.6 7.8-29.5 19.7l-120 288-40 96c-6.8 16.3 .9 35 17.2 41.8s35-.9 41.8-17.2L93.3 384H290.7l31.8 76.3c6.8 16.3 25.5 24 41.8 17.2s24-25.5 17.2-41.8l-40-96-120-288zM264 320H120l72-172.8L264 320z"
7998
+ }));
7999
+ case "b":
8000
+ return /*#__PURE__*/React.createElement("svg", {
8001
+ xmlns: "http://www.w3.org/2000/svg",
8002
+ width: "20",
8003
+ height: "20",
8004
+ viewBox: "0 0 320 512"
8005
+ }, /*#__PURE__*/React.createElement("path", {
8006
+ d: "M64 32C28.7 32 0 60.7 0 96V256 416c0 35.3 28.7 64 64 64H192c70.7 0 128-57.3 128-128c0-46.5-24.8-87.3-62-109.7c18.7-22.3 30-51 30-82.3c0-70.7-57.3-128-128-128H64zm96 192H64V96h96c35.3 0 64 28.7 64 64s-28.7 64-64 64zM64 288h96 32c35.3 0 64 28.7 64 64s-28.7 64-64 64H64V288z"
8007
+ }));
8008
+ case "c":
8009
+ return /*#__PURE__*/React.createElement("svg", {
8010
+ xmlns: "http://www.w3.org/2000/svg",
8011
+ width: "20",
8012
+ height: "20",
8013
+ viewBox: "0 0 384 512"
8014
+ }, /*#__PURE__*/React.createElement("path", {
8015
+ d: "M329.1 142.9c-62.5-62.5-155.8-62.5-218.3 0s-62.5 163.8 0 226.3s155.8 62.5 218.3 0c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3c-87.5 87.5-221.3 87.5-308.8 0s-87.5-229.3 0-316.8s221.3-87.5 308.8 0c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0z"
8016
+ }));
8017
+ case "x":
8018
+ // MATHEMATICAL ITALIC SMALL CHI
8019
+ // https://en.wikipedia.org/wiki/Chi_(letter)#Mathematical_chi
8020
+ return /*#__PURE__*/React.createElement("svg", {
8021
+ xmlns: "http://www.w3.org/2000/svg",
8022
+ width: "20",
8023
+ height: "20",
8024
+ fill: "currentColor",
8025
+ viewBox: "0 0 256 256"
8026
+ }, /*#__PURE__*/React.createElement("text", {
8027
+ fontSize: "200px",
8028
+ x: "50%",
8029
+ y: "50%",
8030
+ dominantBaseline: "middle",
8031
+ textAnchor: "middle"
8032
+ }, "\uD835\uDF12"));
8033
+ case "X":
8034
+ // MATHEMATICAL ITALIC CAPITAL CHI
8035
+ // https://en.wikipedia.org/wiki/Chi_(letter)#Mathematical_chi
8036
+ return /*#__PURE__*/React.createElement("svg", {
8037
+ xmlns: "http://www.w3.org/2000/svg",
8038
+ width: "20",
8039
+ height: "20",
8040
+ fill: "currentColor",
8041
+ viewBox: "0 0 256 256"
8042
+ }, /*#__PURE__*/React.createElement("text", {
8043
+ fontSize: "200px",
8044
+ x: "50%",
8045
+ y: "50%",
8046
+ dominantBaseline: "middle",
8047
+ textAnchor: "middle"
8048
+ }, "\uD835\uDEF8"));
8049
+
8050
+ /**
8051
+ * ANYTHING BELOW IS NOT YET HANDLED
8052
+ */
8053
+ case "MANY":
8054
+ case "FRAC_EXCLUSIVE":
8055
+ case "THETA":
8056
+ case "NOOP":
8057
+ case "UP":
8058
+ case "DOWN":
8059
+ case "LEFT":
8060
+ case "RIGHT":
8061
+ case "JUMP_OUT_PARENTHESES":
8062
+ case "JUMP_OUT_EXPONENT":
8063
+ case "JUMP_OUT_BASE":
8064
+ case "JUMP_INTO_NUMERATOR":
8065
+ case "JUMP_OUT_NUMERATOR":
8066
+ case "JUMP_OUT_DENOMINATOR":
8067
+ case "PHI":
8068
+ case "NTHROOT3":
8069
+ case "POW":
8070
+ case "LOG_B":
8071
+ case "d":
8072
+ case "e":
8073
+ case "f":
8074
+ case "g":
8075
+ case "h":
8076
+ case "i":
8077
+ case "j":
8078
+ case "k":
8079
+ case "l":
8080
+ case "m":
8081
+ case "n":
8082
+ case "o":
8083
+ case "p":
8084
+ case "q":
8085
+ case "r":
8086
+ case "s":
8087
+ case "t":
8088
+ case "u":
8089
+ case "v":
8090
+ case "w":
8091
+ case "y":
8092
+ case "z":
8093
+ case "A":
8094
+ case "B":
8095
+ case "C":
8096
+ case "D":
8097
+ case "E":
8098
+ case "F":
8099
+ case "G":
8100
+ case "H":
8101
+ case "I":
8102
+ case "J":
8103
+ case "K":
8104
+ case "L":
8105
+ case "M":
8106
+ case "N":
8107
+ case "O":
8108
+ case "P":
8109
+ case "Q":
8110
+ case "R":
8111
+ case "S":
8112
+ case "T":
8113
+ case "U":
8114
+ case "V":
8115
+ case "W":
8116
+ case "Y":
8117
+ case "Z":
7495
8118
  // placeholder
7496
8119
  return /*#__PURE__*/React.createElement("svg", {
7497
8120
  width: "40",
@@ -7509,6 +8132,11 @@ function ButtonAsset({
7509
8132
  lengthAdjust: "spacingAndGlyphs",
7510
8133
  fill: "red"
7511
8134
  }, "placeholder"));
8135
+ default:
8136
+ // this line forces an exhaustive check of all keys;
8137
+ // if a key is not handled, the compiler will complain.
8138
+ const unhandledKey = id;
8139
+ throw new Error(`Unhandled key: ${unhandledKey}`);
7512
8140
  }
7513
8141
  }
7514
8142
 
@@ -7532,7 +8160,8 @@ const KeypadButton = ({
7532
8160
  }) => /*#__PURE__*/React.createElement(Button, {
7533
8161
  onPress: () => onClickKey(keyConfig.id),
7534
8162
  tintColor: tintColor,
7535
- style: style
8163
+ style: style,
8164
+ ariaLabel: keyConfig.id
7536
8165
  }, /*#__PURE__*/React.createElement(ButtonAsset, {
7537
8166
  id: keyConfig.id
7538
8167
  }));
@@ -7584,6 +8213,20 @@ const PlaceHolderButtons = ({
7584
8213
  }
7585
8214
  }))));
7586
8215
 
8216
+ class ExtrasPage extends React.Component {
8217
+ render() {
8218
+ const {
8219
+ extraKeys,
8220
+ onClickKey
8221
+ } = this.props;
8222
+ return /*#__PURE__*/React.createElement(KeypadPageContainer, null, extraKeys.map(key => /*#__PURE__*/React.createElement(KeypadButton, {
8223
+ key: key,
8224
+ keyConfig: KeyConfigs[key],
8225
+ onClickKey: onClickKey
8226
+ })));
8227
+ }
8228
+ }
8229
+
7587
8230
  class GeometryPage extends React.Component {
7588
8231
  render() {
7589
8232
  const {
@@ -7611,7 +8254,7 @@ class GeometryPage extends React.Component {
7611
8254
  gridColumn: 6
7612
8255
  }
7613
8256
  }), /*#__PURE__*/React.createElement(SecondaryKeypadButton, {
7614
- keyConfig: KeyConfigs.X,
8257
+ keyConfig: KeyConfigs.x,
7615
8258
  onClickKey: onClickKey,
7616
8259
  style: {
7617
8260
  gridColumn: 5
@@ -7832,7 +8475,7 @@ class OperatorsPage extends React.Component {
7832
8475
  onClickKey: onClickKey,
7833
8476
  placeholder: !this.props.logarithms
7834
8477
  }), /*#__PURE__*/React.createElement(SecondaryKeypadButton, {
7835
- keyConfig: KeyConfigs.X,
8478
+ keyConfig: KeyConfigs.x,
7836
8479
  onClickKey: onClickKey,
7837
8480
  style: {
7838
8481
  gridColumn: 5
@@ -7871,8 +8514,12 @@ class OperatorsPage extends React.Component {
7871
8514
  }
7872
8515
  }
7873
8516
 
7874
- const allPages = function allPages(props) {
8517
+ function allPages(props) {
8518
+ var _props$extraKeys;
7875
8519
  const pages = ["Numbers"];
8520
+ if ((_props$extraKeys = props.extraKeys) != null && _props$extraKeys.length) {
8521
+ pages.push("Extras");
8522
+ }
7876
8523
  if (
7877
8524
  // OperatorsButtonSets
7878
8525
  props.preAlgebra || props.logarithms || props.basicRelations || props.advancedRelations) {
@@ -7881,9 +8528,8 @@ const allPages = function allPages(props) {
7881
8528
  if (props.trigonometry) {
7882
8529
  pages.push("Geometry");
7883
8530
  }
7884
- // @ts-expect-error [FEI-5003] - TS2739 - Type 'TabbarItemType[]' is missing the following properties from type 'ReactElement<any, string | JSXElementConstructor<any>>': type, props, key
7885
8531
  return pages;
7886
- };
8532
+ }
7887
8533
  class Keypad extends React.Component {
7888
8534
  constructor(...args) {
7889
8535
  super(...args);
@@ -7896,18 +8542,26 @@ class Keypad extends React.Component {
7896
8542
  selectedPage
7897
8543
  } = this.state;
7898
8544
  const availablePages = allPages(this.props);
7899
- return /*#__PURE__*/React.createElement(View$1, null, /*#__PURE__*/React.createElement(Tabbar
7900
- // @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call.
7901
- , {
8545
+ return /*#__PURE__*/React.createElement(View$1, null, /*#__PURE__*/React.createElement(Tabbar, {
7902
8546
  items: availablePages,
7903
- onSelect: tabbarItem => {
8547
+ selectedItem: selectedPage,
8548
+ onSelectItem: tabbarItem => {
7904
8549
  this.setState({
7905
8550
  selectedPage: tabbarItem
7906
8551
  });
7907
- }
7908
- }), selectedPage === "Numbers" && /*#__PURE__*/React.createElement(NumbersPage, this.props), selectedPage === "Operators" && /*#__PURE__*/React.createElement(OperatorsPage, this.props), selectedPage === "Geometry" && /*#__PURE__*/React.createElement(GeometryPage, this.props));
8552
+ },
8553
+ style: styles.tabbar
8554
+ }), selectedPage === "Numbers" && /*#__PURE__*/React.createElement(NumbersPage, this.props), selectedPage === "Extras" && /*#__PURE__*/React.createElement(ExtrasPage, this.props), selectedPage === "Operators" && /*#__PURE__*/React.createElement(OperatorsPage, this.props), selectedPage === "Geometry" && /*#__PURE__*/React.createElement(GeometryPage, this.props));
7909
8555
  }
7910
8556
  }
8557
+ Keypad.defaultProps = {
8558
+ extraKeys: []
8559
+ };
8560
+ const styles = StyleSheet.create({
8561
+ tabbar: {
8562
+ background: Color.white
8563
+ }
8564
+ });
7911
8565
 
7912
- export { CursorContext, KeyConfigs, KeyType, Keypad, MathInput as KeypadInput, KeypadType, Keys, ProvidedKeypad as LegacyKeypad, keypadElementPropType };
8566
+ export { CursorContext, KeyConfigs, Keypad, MathInput as KeypadInput, KeypadType, ProvidedKeypad as LegacyKeypad, keyToMathquillMap as keyTranslator, keypadElementPropType };
7913
8567
  //# sourceMappingURL=index.js.map