@khanacademy/math-input 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/components/compute-layout-parameters.d.ts +2 -1
  3. package/dist/components/compute-layout-parameters.js.flow +2 -21
  4. package/dist/components/echo-manager.d.ts +4 -4
  5. package/dist/components/echo-manager.js.flow +4 -4
  6. package/dist/components/expression-keypad.d.ts +1 -1
  7. package/dist/components/expression-keypad.js.flow +1 -1
  8. package/dist/components/fraction-keypad.d.ts +1 -1
  9. package/dist/components/fraction-keypad.js.flow +1 -1
  10. package/dist/components/gesture-state-machine.d.ts +7 -7
  11. package/dist/components/gesture-state-machine.js.flow +8 -8
  12. package/dist/components/icon.d.ts +2 -2
  13. package/dist/components/icon.js.flow +2 -2
  14. package/dist/components/input/cursor-contexts.d.ts +10 -9
  15. package/dist/components/input/cursor-contexts.js.flow +11 -16
  16. package/dist/components/input/math-wrapper.d.ts +3 -2
  17. package/dist/components/input/math-wrapper.js.flow +3 -16
  18. package/dist/components/keypad/index.d.ts +1 -1
  19. package/dist/components/keypad/index.js.flow +1 -3
  20. package/dist/components/keypad-button.d.ts +8 -8
  21. package/dist/components/keypad-button.js.flow +10 -9
  22. package/dist/components/keypad-container.d.ts +2 -3
  23. package/dist/components/keypad-container.js.flow +2 -3
  24. package/dist/components/keypad.d.ts +1 -1
  25. package/dist/components/keypad.js.flow +1 -1
  26. package/dist/components/multi-symbol-grid.d.ts +2 -2
  27. package/dist/components/multi-symbol-grid.js.flow +2 -2
  28. package/dist/components/node-manager.d.ts +2 -5
  29. package/dist/components/node-manager.js.flow +2 -5
  30. package/dist/components/popover-state-machine.d.ts +1 -8
  31. package/dist/components/popover-state-machine.js.flow +2 -8
  32. package/dist/components/provided-keypad.d.ts +1 -4
  33. package/dist/components/provided-keypad.js.flow +1 -4
  34. package/dist/components/styles.d.ts +1 -2
  35. package/dist/components/styles.js.flow +1 -3
  36. package/dist/components/touchable-keypad-button.d.ts +6 -6
  37. package/dist/components/touchable-keypad-button.js.flow +6 -6
  38. package/dist/data/keys.d.ts +51 -52
  39. package/dist/data/keys.js.flow +50 -99
  40. package/dist/enums.d.ts +49 -0
  41. package/dist/enums.js.flow +63 -0
  42. package/dist/es/index.css +0 -3
  43. package/dist/es/index.js +415 -455
  44. package/dist/es/index.js.map +1 -1
  45. package/dist/fake-react-native-web/view.d.ts +1 -2
  46. package/dist/fake-react-native-web/view.js.flow +1 -2
  47. package/dist/index.css +0 -3
  48. package/dist/index.d.ts +3 -6
  49. package/dist/index.js +438 -477
  50. package/dist/index.js.flow +3 -6
  51. package/dist/index.js.map +1 -1
  52. package/dist/store/actions.d.ts +64 -0
  53. package/dist/store/actions.js.flow +101 -0
  54. package/dist/store/echo-reducer.d.ts +2 -3
  55. package/dist/store/echo-reducer.js.flow +2 -6
  56. package/dist/store/index.d.ts +5 -41
  57. package/dist/store/index.js.flow +5 -52
  58. package/dist/store/input-reducer.d.ts +2 -5
  59. package/dist/store/input-reducer.js.flow +3 -6
  60. package/dist/store/keypad-reducer.d.ts +2 -7
  61. package/dist/store/keypad-reducer.js.flow +3 -8
  62. package/dist/store/layout-reducer.d.ts +2 -19
  63. package/dist/store/layout-reducer.js.flow +3 -20
  64. package/dist/store/pager-reducer.d.ts +2 -11
  65. package/dist/store/pager-reducer.js.flow +3 -12
  66. package/dist/store/shared.d.ts +2 -1
  67. package/dist/store/shared.js.flow +2 -1
  68. package/dist/store/types.d.ts +5 -5
  69. package/dist/store/types.js.flow +5 -5
  70. package/dist/types.d.ts +28 -16
  71. package/dist/types.js.flow +32 -20
  72. package/less/overrides.less +0 -6
  73. package/package.json +1 -1
  74. package/src/components/compute-layout-parameters.ts +6 -6
  75. package/src/components/echo-manager.tsx +10 -10
  76. package/src/components/expression-keypad.tsx +9 -10
  77. package/src/components/fraction-keypad.tsx +11 -12
  78. package/src/components/gesture-state-machine.ts +8 -8
  79. package/src/components/icon.tsx +6 -6
  80. package/src/components/input/__tests__/context-tracking.test.ts +20 -20
  81. package/src/components/input/cursor-contexts.ts +22 -29
  82. package/src/components/input/math-wrapper.ts +75 -67
  83. package/src/components/keypad/index.tsx +1 -1
  84. package/src/components/keypad-button.tsx +20 -21
  85. package/src/components/keypad-container.tsx +9 -10
  86. package/src/components/keypad.tsx +1 -1
  87. package/src/components/many-keypad-button.tsx +2 -2
  88. package/src/components/multi-symbol-grid.tsx +4 -5
  89. package/src/components/multi-symbol-popover.tsx +1 -1
  90. package/src/components/navigation-pad.tsx +1 -1
  91. package/src/components/node-manager.ts +2 -2
  92. package/src/components/popover-state-machine.ts +2 -10
  93. package/src/components/provided-keypad.tsx +3 -12
  94. package/src/components/touchable-keypad-button.tsx +7 -7
  95. package/src/data/key-configs.ts +58 -58
  96. package/src/data/keys.ts +53 -105
  97. package/src/enums.ts +74 -0
  98. package/src/index.ts +3 -9
  99. package/src/math-input.stories.tsx +67 -0
  100. package/src/store/actions.ts +179 -0
  101. package/src/store/echo-reducer.ts +10 -7
  102. package/src/store/index.ts +24 -24
  103. package/src/store/input-reducer.ts +7 -6
  104. package/src/store/keypad-reducer.ts +3 -6
  105. package/src/store/layout-reducer.ts +12 -11
  106. package/src/store/pager-reducer.ts +30 -46
  107. package/src/store/shared.ts +4 -4
  108. package/src/store/types.ts +21 -5
  109. package/src/types.ts +32 -20
  110. package/src/utils.ts +3 -3
  111. package/tsconfig-build.tsbuildinfo +1 -0
  112. package/dist/actions/index.d.ts +0 -31
  113. package/dist/actions/index.js.flow +0 -40
  114. package/dist/consts.d.ts +0 -51
  115. package/dist/consts.js.flow +0 -66
  116. package/src/actions/index.ts +0 -57
  117. package/src/consts.ts +0 -91
  118. package/tsconfig.tsbuildinfo +0 -1
  119. /package/src/components/{gesture-manager.tsx → gesture-manager.ts} +0 -0
  120. /package/{tsconfig.json → tsconfig-build.json} +0 -0
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Constants that define the various contexts in which a cursor can exist. The
2
+ * Enum that defines the various contexts in which a cursor can exist. The
3
3
  * active context is determined first by looking at the cursor's siblings (e.g.,
4
4
  * for the `BEFORE_FRACTION` context), and then at its direct parent. Though a
5
5
  * cursor could in theory be nested in multiple contexts, we only care about the
@@ -10,35 +10,28 @@
10
10
  * the radical.
11
11
  */
12
12
 
13
- export type CursorContext = // The cursor is not in any of the other viable contexts.
13
+ export enum CursorContext {
14
+ // The cursor is not in any of the other viable contexts.
15
+ NONE = "NONE",
16
+
14
17
  // The cursor is within a set of parentheses.
15
- | "NONE" // The cursor is within a superscript (e.g., an exponent).
16
- | "IN_PARENS" // The cursor is within a subscript (e.g., the base of a custom logarithm).
17
- | "IN_SUPER_SCRIPT" // The cursor is in the numerator of a fraction.
18
- | "IN_SUB_SCRIPT" // The cursor is in the denominator of a fraction.
19
- | "IN_NUMERATOR" // The cursor is sitting before a fraction; that is, the cursor is within
18
+ IN_PARENS = "IN_PARENS",
19
+
20
+ // The cursor is within a superscript (e.g., an exponent).
21
+ IN_SUPER_SCRIPT = "IN_SUPER_SCRIPT",
22
+
23
+ // The cursor is within a subscript (e.g., the base of a custom logarithm).
24
+ IN_SUB_SCRIPT = "IN_SUB_SCRIPT",
25
+
26
+ // The cursor is in the numerator of a fraction.
27
+ IN_NUMERATOR = "IN_NUMERATOR",
28
+
29
+ // The cursor is in the denominator of a fraction.
30
+ IN_DENOMINATOR = "IN_DENOMINATOR",
31
+
32
+ // The cursor is sitting before a fraction; that is, the cursor is within
20
33
  // what looks to be a mixed number preceding a fraction. This will only be
21
34
  // the case when the only math between the cursor and the fraction to its
22
35
  // write is non-leaf math (numbers and variables).
23
- | "IN_DENOMINATOR"
24
- | "BEFORE_FRACTION";
25
-
26
- // TODO: Get rid of these constants in favour of CursorContext type.
27
-
28
- // The cursor is not in any of the other viable contexts.
29
- export const NONE = "NONE";
30
- // The cursor is within a set of parentheses.
31
- export const IN_PARENS = "IN_PARENS";
32
- // The cursor is within a superscript (e.g., an exponent).
33
- export const IN_SUPER_SCRIPT = "IN_SUPER_SCRIPT";
34
- // The cursor is within a subscript (e.g., the base of a custom logarithm).
35
- export const IN_SUB_SCRIPT = "IN_SUB_SCRIPT";
36
- // The cursor is in the numerator of a fraction.
37
- export const IN_NUMERATOR = "IN_NUMERATOR";
38
- // The cursor is in the denominator of a fraction.
39
- export const IN_DENOMINATOR = "IN_DENOMINATOR";
40
- // The cursor is sitting before a fraction; that is, the cursor is within
41
- // what looks to be a mixed number preceding a fraction. This will only be
42
- // the case when the only math between the cursor and the fraction to its
43
- // write is non-leaf math (numbers and variables).
44
- export const BEFORE_FRACTION = "BEFORE_FRACTION";
36
+ BEFORE_FRACTION = "BEFORE_FRACTION",
37
+ }
@@ -7,55 +7,57 @@
7
7
  import $ from "jquery";
8
8
  import MathQuill from "mathquill";
9
9
 
10
- import {DecimalSeparators} from "../../consts";
11
10
  import Keys from "../../data/keys";
11
+ import {DecimalSeparator} from "../../enums";
12
12
  import {decimalSeparator} from "../../utils";
13
13
 
14
- import * as CursorContexts from "./cursor-contexts";
14
+ import {CursorContext} from "./cursor-contexts";
15
15
 
16
16
  // Keeping `window` in place for test suite and GitHub Pages.
17
17
  // If it does not exist, fall back to CommonJS require. - jsatk
18
18
 
19
- const decimalSymbol = decimalSeparator === DecimalSeparators.COMMA ? "," : ".";
19
+ const decimalSymbol = decimalSeparator === DecimalSeparator.COMMA ? "," : ".";
20
20
 
21
- const WRITE = "write";
22
- const CMD = "cmd";
23
- const KEYSTROKE = "keystroke";
24
- const MQ_END = 0;
21
+ enum ActionType {
22
+ WRITE = "write",
23
+ CMD = "cmd",
24
+ KEYSTROKE = "keystroke",
25
+ MQ_END = 0,
26
+ }
25
27
 
26
28
  // A mapping from keys that can be pressed on a keypad to the way in which
27
29
  // MathQuill should modify its input in response to that key-press. Any keys
28
30
  // that do not provide explicit actions (like the numeral keys) will merely
29
31
  // write their contents to MathQuill.
30
- const KeyActions = {
31
- [Keys.PLUS]: {str: "+", fn: WRITE},
32
- [Keys.MINUS]: {str: "-", fn: WRITE},
33
- [Keys.NEGATIVE]: {str: "-", fn: WRITE},
34
- [Keys.TIMES]: {str: "\\times", fn: WRITE},
35
- [Keys.DIVIDE]: {str: "\\div", fn: WRITE},
32
+ const KeyActions: {[K in Keys]?: {str: string; fn: ActionType}} = {
33
+ [Keys.PLUS]: {str: "+", fn: ActionType.WRITE},
34
+ [Keys.MINUS]: {str: "-", fn: ActionType.WRITE},
35
+ [Keys.NEGATIVE]: {str: "-", fn: ActionType.WRITE},
36
+ [Keys.TIMES]: {str: "\\times", fn: ActionType.WRITE},
37
+ [Keys.DIVIDE]: {str: "\\div", fn: ActionType.WRITE},
36
38
  [Keys.DECIMAL]: {
37
39
  str: decimalSymbol,
38
- fn: WRITE,
40
+ fn: ActionType.WRITE,
39
41
  },
40
- [Keys.EQUAL]: {str: "=", fn: WRITE},
41
- [Keys.NEQ]: {str: "\\neq", fn: WRITE},
42
- [Keys.CDOT]: {str: "\\cdot", fn: WRITE},
43
- [Keys.PERCENT]: {str: "%", fn: WRITE},
44
- [Keys.LEFT_PAREN]: {str: "(", fn: CMD},
45
- [Keys.RIGHT_PAREN]: {str: ")", fn: CMD},
46
- [Keys.SQRT]: {str: "sqrt", fn: CMD},
47
- [Keys.PI]: {str: "pi", fn: CMD},
48
- [Keys.THETA]: {str: "theta", fn: CMD},
49
- [Keys.RADICAL]: {str: "nthroot", fn: CMD},
50
- [Keys.LT]: {str: "<", fn: WRITE},
51
- [Keys.LEQ]: {str: "\\leq", fn: WRITE},
52
- [Keys.GT]: {str: ">", fn: WRITE},
53
- [Keys.GEQ]: {str: "\\geq", fn: WRITE},
54
- [Keys.UP]: {str: "Up", fn: KEYSTROKE},
55
- [Keys.DOWN]: {str: "Down", fn: KEYSTROKE},
42
+ [Keys.EQUAL]: {str: "=", fn: ActionType.WRITE},
43
+ [Keys.NEQ]: {str: "\\neq", fn: ActionType.WRITE},
44
+ [Keys.CDOT]: {str: "\\cdot", fn: ActionType.WRITE},
45
+ [Keys.PERCENT]: {str: "%", fn: ActionType.WRITE},
46
+ [Keys.LEFT_PAREN]: {str: "(", fn: ActionType.CMD},
47
+ [Keys.RIGHT_PAREN]: {str: ")", fn: ActionType.CMD},
48
+ [Keys.SQRT]: {str: "sqrt", fn: ActionType.CMD},
49
+ [Keys.PI]: {str: "pi", fn: ActionType.CMD},
50
+ [Keys.THETA]: {str: "theta", fn: ActionType.CMD},
51
+ [Keys.RADICAL]: {str: "nthroot", fn: ActionType.CMD},
52
+ [Keys.LT]: {str: "<", fn: ActionType.WRITE},
53
+ [Keys.LEQ]: {str: "\\leq", fn: ActionType.WRITE},
54
+ [Keys.GT]: {str: ">", fn: ActionType.WRITE},
55
+ [Keys.GEQ]: {str: "\\geq", fn: ActionType.WRITE},
56
+ [Keys.UP]: {str: "Up", fn: ActionType.KEYSTROKE},
57
+ [Keys.DOWN]: {str: "Down", fn: ActionType.KEYSTROKE},
56
58
  // The `FRAC_EXCLUSIVE` variant is handled manually, since we may need to do
57
59
  // some additional navigation depending on the cursor position.
58
- [Keys.FRAC_INCLUSIVE]: {str: "/", fn: CMD},
60
+ [Keys.FRAC_INCLUSIVE]: {str: "/", fn: ActionType.CMD},
59
61
  };
60
62
 
61
63
  const NormalCommands = {
@@ -110,12 +112,12 @@ const ValidLeaves = [
110
112
  ];
111
113
 
112
114
  const KeysForJumpContext = {
113
- [CursorContexts.IN_PARENS]: Keys.JUMP_OUT_PARENTHESES,
114
- [CursorContexts.IN_SUPER_SCRIPT]: Keys.JUMP_OUT_EXPONENT,
115
- [CursorContexts.IN_SUB_SCRIPT]: Keys.JUMP_OUT_BASE,
116
- [CursorContexts.BEFORE_FRACTION]: Keys.JUMP_INTO_NUMERATOR,
117
- [CursorContexts.IN_NUMERATOR]: Keys.JUMP_OUT_NUMERATOR,
118
- [CursorContexts.IN_DENOMINATOR]: Keys.JUMP_OUT_DENOMINATOR,
115
+ [CursorContext.IN_PARENS]: Keys.JUMP_OUT_PARENTHESES,
116
+ [CursorContext.IN_SUPER_SCRIPT]: Keys.JUMP_OUT_EXPONENT,
117
+ [CursorContext.IN_SUB_SCRIPT]: Keys.JUMP_OUT_BASE,
118
+ [CursorContext.BEFORE_FRACTION]: Keys.JUMP_INTO_NUMERATOR,
119
+ [CursorContext.IN_NUMERATOR]: Keys.JUMP_OUT_NUMERATOR,
120
+ [CursorContext.IN_DENOMINATOR]: Keys.JUMP_OUT_DENOMINATOR,
119
121
  };
120
122
 
121
123
  class MathWrapper {
@@ -179,14 +181,14 @@ class MathWrapper {
179
181
  } else if (key === Keys.FRAC_EXCLUSIVE) {
180
182
  // If there's nothing to the left of the cursor, then we want to
181
183
  // leave the cursor to the left of the fraction after creating it.
182
- const shouldNavigateLeft = cursor[this.MQ.L] === MQ_END;
184
+ const shouldNavigateLeft = cursor[this.MQ.L] === ActionType.MQ_END;
183
185
  this.mathField.cmd("\\frac");
184
186
  if (shouldNavigateLeft) {
185
187
  this.mathField.keystroke("Left");
186
188
  }
187
189
  } else if (key === Keys.FRAC) {
188
190
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
189
- const shouldNavigateLeft = cursor[this.MQ.L] === MQ_END;
191
+ const shouldNavigateLeft = cursor[this.MQ.L] === ActionType.MQ_END;
190
192
  this.mathField.cmd("\\frac");
191
193
  } else if (key === Keys.LOG_N) {
192
194
  this.mathField.write("log_{ }\\left(\\right)");
@@ -218,9 +220,9 @@ class MathWrapper {
218
220
  } else if (key === Keys.RIGHT) {
219
221
  this._handleRightArrow(cursor);
220
222
  } else if (/^[a-zA-Z]$/.test(key)) {
221
- this.mathField[WRITE](key);
223
+ this.mathField[ActionType.WRITE](key);
222
224
  } else if (/^NUM_\d/.test(key)) {
223
- this.mathField[WRITE](key[4]);
225
+ this.mathField[ActionType.WRITE](key[4]);
224
226
  }
225
227
 
226
228
  if (!cursor.selection) {
@@ -325,7 +327,7 @@ class MathWrapper {
325
327
  // when upgrading MathQuill.
326
328
 
327
329
  _handleBackspaceInNthRoot(cursor) {
328
- const isAtLeftEnd = cursor[this.MQ.L] === MQ_END;
330
+ const isAtLeftEnd = cursor[this.MQ.L] === ActionType.MQ_END;
329
331
 
330
332
  const isRootEmpty = this._isInsideEmptyNode(
331
333
  cursor.parent.parent.blocks[0].ends,
@@ -362,17 +364,17 @@ class MathWrapper {
362
364
  }
363
365
 
364
366
  switch (context) {
365
- case CursorContexts.IN_PARENS:
367
+ case CursorContext.IN_PARENS:
366
368
  // Insert at the end of the parentheses, and then navigate right
367
369
  // once more to get 'beyond' the parentheses.
368
370
  cursor.insRightOf(cursor.parent.parent);
369
371
  break;
370
372
 
371
- case CursorContexts.BEFORE_FRACTION:
373
+ case CursorContext.BEFORE_FRACTION:
372
374
  // Find the nearest fraction to the right of the cursor.
373
375
  let fractionNode;
374
376
  let visitor = cursor;
375
- while (visitor[this.MQ.R] !== MQ_END) {
377
+ while (visitor[this.MQ.R] !== ActionType.MQ_END) {
376
378
  if (this._isFraction(visitor[this.MQ.R])) {
377
379
  fractionNode = visitor[this.MQ.R];
378
380
  }
@@ -384,7 +386,7 @@ class MathWrapper {
384
386
  this.mathField.keystroke("Right");
385
387
  break;
386
388
 
387
- case CursorContexts.IN_NUMERATOR:
389
+ case CursorContext.IN_NUMERATOR:
388
390
  // HACK(charlie): I can't find a better way to do this. The goal
389
391
  // is to place the cursor at the start of the matching
390
392
  // denominator. So, we identify the appropriate node, and
@@ -397,11 +399,11 @@ class MathWrapper {
397
399
  }
398
400
  break;
399
401
 
400
- case CursorContexts.IN_DENOMINATOR:
402
+ case CursorContext.IN_DENOMINATOR:
401
403
  cursor.insRightOf(cursor.parent.parent);
402
404
  break;
403
405
 
404
- case CursorContexts.IN_SUB_SCRIPT:
406
+ case CursorContext.IN_SUB_SCRIPT:
405
407
  // Insert just beyond the superscript.
406
408
  cursor.insRightOf(cursor.parent.parent);
407
409
 
@@ -413,7 +415,7 @@ class MathWrapper {
413
415
  }
414
416
  break;
415
417
 
416
- case CursorContexts.IN_SUPER_SCRIPT:
418
+ case CursorContext.IN_SUPER_SCRIPT:
417
419
  // Insert just beyond the superscript.
418
420
  cursor.insRightOf(cursor.parent.parent);
419
421
  break;
@@ -458,7 +460,10 @@ class MathWrapper {
458
460
  leftNode.ctrlSeq === "\\le "
459
461
  ) {
460
462
  this._handleBackspaceAfterLigaturedSymbol(cursor);
461
- } else if (this._isNthRoot(grandparent) && leftNode === MQ_END) {
463
+ } else if (
464
+ this._isNthRoot(grandparent) &&
465
+ leftNode === ActionType.MQ_END
466
+ ) {
462
467
  this._handleBackspaceInNthRoot(cursor);
463
468
  } else {
464
469
  this.mathField.keystroke("Backspace");
@@ -476,10 +481,10 @@ class MathWrapper {
476
481
  // the entire expression, rather than between the `s` and the left
477
482
  // parenthesis.
478
483
  // From the cursor's perspective, this requires that our left node is
479
- // the MQ_END node, that our grandparent is the left parenthesis, and
484
+ // the ActionType.MQ_END node, that our grandparent is the left parenthesis, and
480
485
  // the nodes to the left of our grandparent comprise a valid function
481
486
  // name.
482
- if (cursor[this.MQ.L] === MQ_END) {
487
+ if (cursor[this.MQ.L] === ActionType.MQ_END) {
483
488
  const parent = cursor.parent;
484
489
  const grandparent = parent.parent;
485
490
  if (grandparent.ctrlSeq === "\\left(") {
@@ -518,7 +523,7 @@ class MathWrapper {
518
523
 
519
524
  const precedingNode = cursor[this.MQ.L];
520
525
  const shouldPrefixWithParens =
521
- precedingNode === MQ_END ||
526
+ precedingNode === ActionType.MQ_END ||
522
527
  invalidPrefixes.includes(precedingNode.ctrlSeq.trim());
523
528
  if (shouldPrefixWithParens) {
524
529
  this.mathField.write("\\left(\\right)");
@@ -737,7 +742,10 @@ class MathWrapper {
737
742
  }
738
743
 
739
744
  _isInsideEmptyNode(cursor) {
740
- return cursor[this.MQ.L] === MQ_END && cursor[this.MQ.R] === MQ_END;
745
+ return (
746
+ cursor[this.MQ.L] === ActionType.MQ_END &&
747
+ cursor[this.MQ.R] === ActionType.MQ_END
748
+ );
741
749
  }
742
750
 
743
751
  _handleBackspaceInRootIndex(cursor) {
@@ -769,14 +777,14 @@ class MathWrapper {
769
777
  this.mathField.write(latex.replace(/^\\sqrt\[\]/, "\\sqrt"));
770
778
 
771
779
  // Adjust the cursor to be to the left the sqrt.
772
- if (reinsertionPoint === MQ_END) {
780
+ if (reinsertionPoint === ActionType.MQ_END) {
773
781
  this.mathField.moveToDirEnd(this.MQ.L);
774
782
  } else {
775
783
  cursor.insRightOf(reinsertionPoint);
776
784
  }
777
785
  }
778
786
  } else {
779
- if (cursor[this.MQ.L] !== MQ_END) {
787
+ if (cursor[this.MQ.L] !== ActionType.MQ_END) {
780
788
  // If the cursor is not at the leftmost position inside the
781
789
  // root's index, delete a character.
782
790
  this.mathField.keystroke("Backspace");
@@ -796,7 +804,7 @@ class MathWrapper {
796
804
  cursor.insLeftOf(command?.startNode);
797
805
  cursor.startSelection();
798
806
 
799
- if (grandparent[this.MQ.R] !== MQ_END) {
807
+ if (grandparent[this.MQ.R] !== ActionType.MQ_END) {
800
808
  cursor.insRightOf(grandparent[this.MQ.R]);
801
809
  } else {
802
810
  cursor.insRightOf(grandparent);
@@ -836,7 +844,7 @@ class MathWrapper {
836
844
  // the parens.
837
845
  cursor.insLeftOf(command.startNode);
838
846
  cursor.startSelection();
839
- if (rightNode === MQ_END) {
847
+ if (rightNode === ActionType.MQ_END) {
840
848
  cursor.insAtRightEnd(cursor.parent);
841
849
  } else {
842
850
  cursor.insLeftOf(rightNode);
@@ -876,7 +884,7 @@ class MathWrapper {
876
884
  // - \log(|x+1) => |\log(x+1)|
877
885
  // - \log(|) => |
878
886
 
879
- if (cursor[this.MQ.L] !== MQ_END) {
887
+ if (cursor[this.MQ.L] !== ActionType.MQ_END) {
880
888
  // This command contains math and there's some math to
881
889
  // the left of the cursor that we should delete normally
882
890
  // before doing anything special.
@@ -929,9 +937,9 @@ class MathWrapper {
929
937
  contextForCursor(cursor) {
930
938
  // First, try to find any fraction to the right, unimpeded.
931
939
  let visitor = cursor;
932
- while (visitor[this.MQ.R] !== MQ_END) {
940
+ while (visitor[this.MQ.R] !== ActionType.MQ_END) {
933
941
  if (this._isFraction(visitor[this.MQ.R])) {
934
- return CursorContexts.BEFORE_FRACTION;
942
+ return CursorContext.BEFORE_FRACTION;
935
943
  } else if (!this._isLeaf(visitor[this.MQ.R])) {
936
944
  break;
937
945
  }
@@ -941,17 +949,17 @@ class MathWrapper {
941
949
  // If that didn't work, check if the parent or grandparent is a special
942
950
  // context, so that we can jump outwards.
943
951
  if (this._isParens(cursor.parent && cursor.parent.parent)) {
944
- return CursorContexts.IN_PARENS;
952
+ return CursorContext.IN_PARENS;
945
953
  } else if (this._isNumerator(cursor.parent)) {
946
- return CursorContexts.IN_NUMERATOR;
954
+ return CursorContext.IN_NUMERATOR;
947
955
  } else if (this._isDenominator(cursor.parent)) {
948
- return CursorContexts.IN_DENOMINATOR;
956
+ return CursorContext.IN_DENOMINATOR;
949
957
  } else if (this._isSubScript(cursor.parent)) {
950
- return CursorContexts.IN_SUB_SCRIPT;
958
+ return CursorContext.IN_SUB_SCRIPT;
951
959
  } else if (this._isSuperScript(cursor.parent)) {
952
- return CursorContexts.IN_SUPER_SCRIPT;
960
+ return CursorContext.IN_SUPER_SCRIPT;
953
961
  } else {
954
- return CursorContexts.NONE;
962
+ return CursorContext.NONE;
955
963
  }
956
964
  }
957
965
 
@@ -31,7 +31,7 @@ const allPages = function (props: Props): React.ReactElement {
31
31
  return pages;
32
32
  };
33
33
 
34
- export default class PreAlgebraKeypad extends React.Component<Props, State> {
34
+ export default class Keypad extends React.Component<Props, State> {
35
35
  state: State = {
36
36
  selectedPage: "Numbers",
37
37
  };
@@ -6,7 +6,7 @@ import {StyleSheet, css} from "aphrodite";
6
6
  import * as React from "react";
7
7
  import {connect} from "react-redux";
8
8
 
9
- import {KeyTypes, BorderDirections, BorderStyles} from "../consts";
9
+ import {BorderDirection, BorderStyles, KeyType} from "../enums";
10
10
  import {View} from "../fake-react-native-web/index";
11
11
 
12
12
  import {
@@ -23,9 +23,8 @@ import CornerDecal from "./corner-decal";
23
23
  import Icon from "./icon";
24
24
  import MultiSymbolGrid from "./multi-symbol-grid";
25
25
 
26
- import type {KeyType} from "../consts";
27
26
  import type {State} from "../store/types";
28
- import type {Border, KeyConfig, Icon as IconType} from "../types";
27
+ import type {Border, KeyConfig, IconConfig} from "../types";
29
28
  import type {StyleType} from "@khanacademy/wonder-blocks-core";
30
29
 
31
30
  interface ReduxProps {
@@ -41,7 +40,7 @@ interface Props extends ReduxProps {
41
40
  focused: boolean;
42
41
  popoverEnabled: boolean;
43
42
  type: KeyType;
44
- icon: IconType;
43
+ icon: IconConfig;
45
44
  style?: StyleType;
46
45
  onTouchCancel?: (evt: React.TouchEvent<HTMLDivElement>) => void;
47
46
  onTouchEnd?: (evt: React.TouchEvent<HTMLDivElement>) => void;
@@ -102,7 +101,7 @@ class KeypadButton extends React.PureComponent<Props> {
102
101
  // object. This method must be called whenever a property that
103
102
  // influences the possible outcomes of `this._getFocusStyle` and
104
103
  // `this._getButtonStyle` changes (such as `this.buttonSizeStyle`).
105
- for (const type of Object.keys(KeyTypes)) {
104
+ for (const type of Object.values(KeyType)) {
106
105
  css(View.styles.initial, ...this._getFocusStyle(type));
107
106
 
108
107
  for (const borders of Object.values(BorderStyles)) {
@@ -114,11 +113,11 @@ class KeypadButton extends React.PureComponent<Props> {
114
113
  }
115
114
  };
116
115
 
117
- _getFocusStyle = (type) => {
116
+ _getFocusStyle = (type: KeyType) => {
118
117
  let focusBackgroundStyle;
119
118
  if (
120
- type === KeyTypes.INPUT_NAVIGATION ||
121
- type === KeyTypes.KEYPAD_NAVIGATION
119
+ type === KeyType.INPUT_NAVIGATION ||
120
+ type === KeyType.KEYPAD_NAVIGATION
122
121
  ) {
123
122
  focusBackgroundStyle = styles.light;
124
123
  } else {
@@ -132,35 +131,35 @@ class KeypadButton extends React.PureComponent<Props> {
132
131
  // Select the appropriate style for the button.
133
132
  let backgroundStyle;
134
133
  switch (type) {
135
- case KeyTypes.EMPTY:
134
+ case KeyType.EMPTY:
136
135
  backgroundStyle = styles.empty;
137
136
  break;
138
137
 
139
- case KeyTypes.MANY:
140
- case KeyTypes.VALUE:
138
+ case KeyType.MANY:
139
+ case KeyType.VALUE:
141
140
  backgroundStyle = styles.value;
142
141
  break;
143
142
 
144
- case KeyTypes.OPERATOR:
143
+ case KeyType.OPERATOR:
145
144
  backgroundStyle = styles.operator;
146
145
  break;
147
146
 
148
- case KeyTypes.INPUT_NAVIGATION:
149
- case KeyTypes.KEYPAD_NAVIGATION:
147
+ case KeyType.INPUT_NAVIGATION:
148
+ case KeyType.KEYPAD_NAVIGATION:
150
149
  backgroundStyle = styles.control;
151
150
  break;
152
151
 
153
- case KeyTypes.ECHO:
152
+ case KeyType.ECHO:
154
153
  backgroundStyle = null;
155
154
  break;
156
155
  }
157
156
 
158
157
  const borderStyle = [];
159
- if (borders.includes(BorderDirections.LEFT)) {
158
+ if (borders.includes(BorderDirection.LEFT)) {
160
159
  // @ts-expect-error TS2345
161
160
  borderStyle.push(styles.leftBorder);
162
161
  }
163
- if (borders.includes(BorderDirections.BOTTOM)) {
162
+ if (borders.includes(BorderDirection.BOTTOM)) {
164
163
  // @ts-expect-error TS2345
165
164
  borderStyle.push(styles.bottomBorder);
166
165
  }
@@ -169,7 +168,7 @@ class KeypadButton extends React.PureComponent<Props> {
169
168
  styles.buttonBase,
170
169
  backgroundStyle,
171
170
  ...borderStyle,
172
- type === KeyTypes.ECHO && styles.echo,
171
+ type === KeyType.ECHO && styles.echo,
173
172
  this.buttonSizeStyle,
174
173
  // React Native allows you to set the 'style' props on user defined
175
174
  // components.
@@ -198,7 +197,7 @@ class KeypadButton extends React.PureComponent<Props> {
198
197
  // We render in the focus state if the key is focused, or if it's an
199
198
  // echo.
200
199
  const renderFocused =
201
- (!disabled && focused) || popoverEnabled || type === KeyTypes.ECHO;
200
+ (!disabled && focused) || popoverEnabled || type === KeyType.ECHO;
202
201
  const buttonStyle = this._getButtonStyle(type, borders, style);
203
202
  const focusStyle = this._getFocusStyle(type);
204
203
  const iconWrapperStyle = [
@@ -219,9 +218,9 @@ class KeypadButton extends React.PureComponent<Props> {
219
218
  childKeys &&
220
219
  childKeys.length > 0 && <CornerDecal style={styles.decalInset} />;
221
220
 
222
- if (type === KeyTypes.EMPTY) {
221
+ if (type === KeyType.EMPTY) {
223
222
  return <View style={buttonStyle} {...eventHandlers} />;
224
- } else if (type === KeyTypes.MANY) {
223
+ } else if (type === KeyType.MANY) {
225
224
  // TODO(charlie): Make the long-press interaction accessible. See
226
225
  // the TODO in key-configs.js for more.
227
226
  const manyButtonA11yMarkup = {
@@ -2,9 +2,9 @@ import {StyleSheet} from "aphrodite";
2
2
  import * as React from "react";
3
3
  import {connect} from "react-redux";
4
4
 
5
- import {setPageSize} from "../actions/index";
6
- import {KeypadTypes, LayoutModes} from "../consts";
5
+ import {LayoutMode, KeypadType} from "../enums";
7
6
  import {View} from "../fake-react-native-web/index";
7
+ import {setPageSize} from "../store/actions";
8
8
 
9
9
  import {
10
10
  innerBorderColor,
@@ -18,7 +18,6 @@ import NavigationPad from "./navigation-pad";
18
18
  import Styles from "./styles";
19
19
  import * as zIndexes from "./z-indexes";
20
20
 
21
- import type {KeypadType} from "../consts";
22
21
  import type {State as ReduxState} from "../store/types";
23
22
  import type {StyleType} from "@khanacademy/wonder-blocks-core";
24
23
 
@@ -28,7 +27,7 @@ interface ReduxProps {
28
27
  active?: boolean;
29
28
  extraKeys?: ReadonlyArray<string>;
30
29
  keypadType?: KeypadType;
31
- layoutMode?: keyof typeof LayoutModes;
30
+ layoutMode?: LayoutMode;
32
31
  navigationPadEnabled?: boolean;
33
32
  }
34
33
 
@@ -128,8 +127,8 @@ class KeypadContainer extends React.Component<Props, State> {
128
127
  // crop themselves. At least we're colocating all the layout
129
128
  // information in this component, though.
130
129
  roundTopLeft:
131
- layoutMode === LayoutModes.COMPACT && !navigationPadEnabled,
132
- roundTopRight: layoutMode === LayoutModes.COMPACT,
130
+ layoutMode === LayoutMode.COMPACT && !navigationPadEnabled,
131
+ roundTopRight: layoutMode === LayoutMode.COMPACT,
133
132
  };
134
133
 
135
134
  // Select the appropriate keyboard given the type.
@@ -140,10 +139,10 @@ class KeypadContainer extends React.Component<Props, State> {
140
139
  // clear what that format would look like exactly. Plus, there aren't
141
140
  // very many of them. So to keep us moving, we'll just hardcode.
142
141
  switch (keypadType) {
143
- case KeypadTypes.FRACTION:
142
+ case KeypadType.FRACTION:
144
143
  return <FractionKeypad {...keypadProps} />;
145
144
 
146
- case KeypadTypes.EXPRESSION:
145
+ case KeypadType.EXPRESSION:
147
146
  return <ExpressionKeypad {...keypadProps} />;
148
147
 
149
148
  default:
@@ -186,7 +185,7 @@ class KeypadContainer extends React.Component<Props, State> {
186
185
  const keypadStyle = [
187
186
  row,
188
187
  styles.keypadBorder,
189
- layoutMode === LayoutModes.FULLSCREEN
188
+ layoutMode === LayoutMode.FULLSCREEN
190
189
  ? styles.fullscreen
191
190
  : styles.compact,
192
191
  ];
@@ -211,7 +210,7 @@ class KeypadContainer extends React.Component<Props, State> {
211
210
  >
212
211
  {navigationPadEnabled && (
213
212
  <NavigationPad
214
- roundTopLeft={layoutMode === LayoutModes.COMPACT}
213
+ roundTopLeft={layoutMode === LayoutMode.COMPACT}
215
214
  style={styles.navigationPadContainer}
216
215
  />
217
216
  )}
@@ -7,8 +7,8 @@ import * as React from "react";
7
7
  import ReactDOM from "react-dom";
8
8
  import {connect} from "react-redux";
9
9
 
10
- import {removeEcho} from "../actions/index";
11
10
  import {View} from "../fake-react-native-web/index";
11
+ import {removeEcho} from "../store/actions";
12
12
 
13
13
  import EchoManager from "./echo-manager";
14
14
  import PopoverManager from "./popover-manager";
@@ -5,9 +5,9 @@
5
5
 
6
6
  import * as React from "react";
7
7
 
8
- import {KeyTypes} from "../consts";
9
8
  import KeyConfigs from "../data/key-configs";
10
9
  import Keys from "../data/keys";
10
+ import {KeyType} from "../enums";
11
11
 
12
12
  import EmptyKeypadButton from "./empty-keypad-button";
13
13
  import TouchableKeypadButton from "./touchable-keypad-button";
@@ -35,7 +35,7 @@ class ManyKeypadButton extends React.Component<Props> {
35
35
  } else {
36
36
  const keyConfig = {
37
37
  id: Keys.MANY,
38
- type: KeyTypes.MANY,
38
+ type: KeyType.MANY,
39
39
  childKeyIds: keys,
40
40
  };
41
41
  return <TouchableKeypadButton keyConfig={keyConfig} {...rest} />;
@@ -6,20 +6,19 @@
6
6
  import {StyleSheet} from "aphrodite";
7
7
  import * as React from "react";
8
8
 
9
- import {IconTypes} from "../consts";
9
+ import {IconType} from "../enums";
10
10
  import {View} from "../fake-react-native-web/index";
11
+ import {IconConfig} from "../types";
11
12
 
12
13
  import {iconSizeHeightPx, iconSizeWidthPx} from "./common-style";
13
14
  import Icon from "./icon";
14
15
  import Styles from "./styles";
15
16
 
16
- import type {Icon as IconType} from "../types";
17
-
18
17
  const {row, column, centered, fullWidth} = Styles;
19
18
 
20
19
  type Props = {
21
20
  focused: boolean;
22
- icons: ReadonlyArray<IconType>;
21
+ icons: ReadonlyArray<IconConfig>;
23
22
  };
24
23
 
25
24
  class MultiSymbolGrid extends React.Component<Props> {
@@ -32,7 +31,7 @@ class MultiSymbolGrid extends React.Component<Props> {
32
31
  // Supporting other types of icons is possible but would require
33
32
  // some styles coercion and doesn't seem worthwhile right now.
34
33
  icons.forEach((icon) => {
35
- if (icon.type !== IconTypes.MATH) {
34
+ if (icon.type !== IconType.MATH) {
36
35
  throw new Error(
37
36
  `Received invalid icon: type=${icon.type}, ` +
38
37
  `data=${icon.data}`,
@@ -5,7 +5,7 @@
5
5
  import {StyleSheet} from "aphrodite";
6
6
  import * as React from "react";
7
7
 
8
- import {BorderStyles} from "../consts";
8
+ import {BorderStyles} from "../enums";
9
9
  import {View} from "../fake-react-native-web/index";
10
10
  import {KeyConfig} from "../types";
11
11