@khanacademy/math-input 0.7.2 → 1.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 (176) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/actions/index.d.ts +31 -0
  3. package/dist/actions/index.js.flow +40 -0
  4. package/dist/components/compute-layout-parameters.d.ts +38 -0
  5. package/dist/components/compute-layout-parameters.js.flow +49 -0
  6. package/dist/components/corner-decal.d.ts +12 -0
  7. package/dist/components/corner-decal.js.flow +15 -0
  8. package/dist/components/echo-manager.d.ts +26 -0
  9. package/dist/components/echo-manager.js.flow +29 -0
  10. package/dist/components/empty-keypad-button.d.ts +13 -0
  11. package/dist/components/empty-keypad-button.js.flow +23 -0
  12. package/dist/components/expression-keypad.d.ts +22 -0
  13. package/dist/components/expression-keypad.js.flow +32 -0
  14. package/dist/components/fraction-keypad.d.ts +21 -0
  15. package/dist/components/fraction-keypad.js.flow +30 -0
  16. package/dist/components/gesture-manager.d.ts +74 -0
  17. package/dist/components/gesture-manager.js.flow +82 -0
  18. package/dist/components/gesture-state-machine.d.ts +105 -0
  19. package/dist/components/gesture-state-machine.js.flow +118 -0
  20. package/dist/components/icon.d.ts +15 -0
  21. package/dist/components/icon.js.flow +18 -0
  22. package/dist/components/input/__tests__/test-math-wrapper.d.ts +8 -0
  23. package/dist/components/input/__tests__/test-math-wrapper.js.flow +14 -0
  24. package/dist/components/input/cursor-handle.d.ts +1 -1
  25. package/dist/components/input/cursor-handle.js.flow +1 -1
  26. package/dist/components/input/drag-listener.d.ts +13 -0
  27. package/dist/components/input/drag-listener.js.flow +19 -0
  28. package/dist/components/input/math-input.d.ts +5 -4
  29. package/dist/components/input/math-input.js.flow +5 -4
  30. package/dist/components/input/math-wrapper.d.ts +110 -0
  31. package/dist/components/input/math-wrapper.js.flow +125 -0
  32. package/dist/components/input/scroll-into-view.d.ts +11 -0
  33. package/dist/components/input/scroll-into-view.js.flow +20 -0
  34. package/dist/components/keypad/button-assets.d.ts +4 -3
  35. package/dist/components/keypad/button-assets.js.flow +3 -3
  36. package/dist/components/keypad/button.d.ts +1 -2
  37. package/dist/components/keypad/button.js.flow +1 -2
  38. package/dist/components/keypad/keypad-page-items.d.ts +15 -10
  39. package/dist/components/keypad/keypad-page-items.js.flow +20 -10
  40. package/dist/components/keypad-button.d.ts +52 -0
  41. package/dist/components/keypad-button.js.flow +79 -0
  42. package/dist/components/keypad-container.d.ts +40 -0
  43. package/dist/components/keypad-container.js.flow +58 -0
  44. package/dist/components/keypad.d.ts +31 -0
  45. package/dist/components/keypad.js.flow +40 -0
  46. package/dist/components/many-keypad-button.d.ts +15 -0
  47. package/dist/components/many-keypad-button.js.flow +17 -0
  48. package/dist/components/math-icon.d.ts +16 -0
  49. package/dist/components/math-icon.js.flow +19 -0
  50. package/dist/components/multi-symbol-grid.d.ts +14 -0
  51. package/dist/components/multi-symbol-grid.js.flow +16 -0
  52. package/dist/components/multi-symbol-popover.d.ts +12 -0
  53. package/dist/components/multi-symbol-popover.js.flow +15 -0
  54. package/dist/components/navigation-pad.d.ts +14 -0
  55. package/dist/components/navigation-pad.js.flow +16 -0
  56. package/dist/components/node-manager.d.ts +53 -0
  57. package/dist/components/node-manager.js.flow +65 -0
  58. package/dist/components/popover-manager.d.ts +13 -0
  59. package/dist/components/popover-manager.js.flow +15 -0
  60. package/dist/components/popover-state-machine.d.ts +75 -0
  61. package/dist/components/popover-state-machine.js.flow +83 -0
  62. package/dist/components/provided-keypad.d.ts +8 -7
  63. package/dist/components/provided-keypad.js.flow +8 -7
  64. package/dist/components/styles.d.ts +6 -0
  65. package/dist/components/styles.js.flow +13 -0
  66. package/dist/components/svg-icon.d.ts +12 -0
  67. package/dist/components/svg-icon.js.flow +15 -0
  68. package/dist/components/tabbar/icons.d.ts +3 -2
  69. package/dist/components/tabbar/icons.js.flow +3 -2
  70. package/dist/components/tabbar/item.d.ts +1 -2
  71. package/dist/components/tabbar/item.js.flow +1 -2
  72. package/dist/components/tabbar/tabbar.d.ts +3 -3
  73. package/dist/components/tabbar/tabbar.js.flow +3 -3
  74. package/dist/components/text-icon.d.ts +13 -0
  75. package/dist/components/text-icon.js.flow +16 -0
  76. package/dist/components/touchable-keypad-button.d.ts +30 -0
  77. package/dist/components/touchable-keypad-button.js.flow +35 -0
  78. package/dist/components/two-page-keypad.d.ts +20 -0
  79. package/dist/components/two-page-keypad.js.flow +30 -0
  80. package/dist/components/velocity-tracker.d.ts +48 -0
  81. package/dist/components/velocity-tracker.js.flow +54 -0
  82. package/dist/es/index.js +938 -1059
  83. package/dist/es/index.js.map +1 -1
  84. package/dist/fake-react-native-web/text.d.ts +2 -1
  85. package/dist/fake-react-native-web/text.js.flow +2 -1
  86. package/dist/fake-react-native-web/view.d.ts +3 -2
  87. package/dist/fake-react-native-web/view.js.flow +3 -2
  88. package/dist/index.d.ts +1 -1
  89. package/dist/index.js +988 -1089
  90. package/dist/index.js.flow +1 -4
  91. package/dist/index.js.map +1 -1
  92. package/dist/store/echo-reducer.d.ts +5 -0
  93. package/dist/store/echo-reducer.js.flow +14 -0
  94. package/dist/store/index.d.ts +46 -1
  95. package/dist/store/index.js.flow +64 -1
  96. package/dist/store/input-reducer.d.ts +7 -0
  97. package/dist/store/input-reducer.js.flow +16 -0
  98. package/dist/store/keypad-reducer.d.ts +9 -0
  99. package/dist/store/keypad-reducer.js.flow +18 -0
  100. package/dist/store/layout-reducer.d.ts +21 -0
  101. package/dist/store/layout-reducer.js.flow +30 -0
  102. package/dist/store/pager-reducer.d.ts +13 -0
  103. package/dist/store/pager-reducer.js.flow +22 -0
  104. package/dist/store/shared.d.ts +6 -0
  105. package/dist/store/shared.js.flow +13 -0
  106. package/dist/store/types.d.ts +57 -0
  107. package/dist/store/types.js.flow +63 -0
  108. package/dist/types.d.ts +50 -0
  109. package/dist/types.js.flow +61 -0
  110. package/package.json +1 -1
  111. package/src/actions/{index.js → index.ts} +5 -5
  112. package/src/components/__tests__/{gesture-state-machine_test.js → gesture-state-machine.test.ts} +5 -1
  113. package/src/components/__tests__/{two-page-keypad_test.js → two-page-keypad.test.tsx} +0 -2
  114. package/src/components/{corner-decal.js → corner-decal.tsx} +6 -5
  115. package/src/components/{echo-manager.js → echo-manager.tsx} +29 -24
  116. package/src/components/{empty-keypad-button.js → empty-keypad-button.tsx} +17 -10
  117. package/src/components/{expression-keypad.js → expression-keypad.tsx} +27 -25
  118. package/src/components/{fraction-keypad.js → fraction-keypad.tsx} +21 -16
  119. package/src/components/{gesture-manager.js → gesture-manager.tsx} +10 -4
  120. package/src/components/{gesture-state-machine.js → gesture-state-machine.ts} +49 -3
  121. package/src/components/{icon.js → icon.tsx} +12 -14
  122. package/src/components/input/cursor-handle.tsx +1 -1
  123. package/src/components/input/{drag-listener.js → drag-listener.ts} +4 -0
  124. package/src/components/input/math-input.tsx +10 -9
  125. package/src/components/input/{math-wrapper.js → math-wrapper.ts} +10 -6
  126. package/src/components/input/{scroll-into-view.js → scroll-into-view.ts} +5 -15
  127. package/src/components/keypad/button-assets.tsx +4 -5
  128. package/src/components/keypad/button.tsx +1 -2
  129. package/src/components/keypad/index.tsx +1 -1
  130. package/src/components/keypad/keypad-page-items.tsx +33 -10
  131. package/src/components/{keypad-button.js → keypad-button.tsx} +42 -37
  132. package/src/components/{keypad-container.js → keypad-container.tsx} +41 -23
  133. package/src/components/{keypad.js → keypad.tsx} +31 -23
  134. package/src/components/{many-keypad-button.js → many-keypad-button.tsx} +8 -6
  135. package/src/components/{math-icon.js → math-icon.tsx} +7 -6
  136. package/src/components/{multi-symbol-grid.js → multi-symbol-grid.tsx} +8 -8
  137. package/src/components/{multi-symbol-popover.js → multi-symbol-popover.tsx} +5 -6
  138. package/src/components/{navigation-pad.js → navigation-pad.tsx} +7 -6
  139. package/src/components/{node-manager.js → node-manager.ts} +16 -4
  140. package/src/components/{popover-manager.js → popover-manager.tsx} +13 -16
  141. package/src/components/{popover-state-machine.js → popover-state-machine.ts} +21 -2
  142. package/src/components/prop-types.js +1 -67
  143. package/src/components/provided-keypad.tsx +14 -12
  144. package/src/components/{svg-icon.js → svg-icon.tsx} +5 -6
  145. package/src/components/tabbar/icons.tsx +4 -2
  146. package/src/components/tabbar/item.tsx +1 -3
  147. package/src/components/tabbar/{tabbar.stories.js → tabbar.stories.tsx} +10 -1
  148. package/src/components/tabbar/tabbar.tsx +3 -3
  149. package/src/components/{text-icon.js → text-icon.tsx} +7 -6
  150. package/src/components/{touchable-keypad-button.js → touchable-keypad-button.tsx} +19 -16
  151. package/src/components/{two-page-keypad.js → two-page-keypad.tsx} +13 -9
  152. package/src/components/{velocity-tracker.js → velocity-tracker.ts} +14 -4
  153. package/src/fake-react-native-web/text.tsx +2 -1
  154. package/src/fake-react-native-web/view.tsx +3 -2
  155. package/src/index.ts +1 -4
  156. package/src/store/echo-reducer.ts +58 -0
  157. package/src/store/index.ts +14 -425
  158. package/src/store/input-reducer.ts +55 -0
  159. package/src/store/keypad-reducer.ts +62 -0
  160. package/src/store/layout-reducer.ts +133 -0
  161. package/src/store/pager-reducer.ts +141 -0
  162. package/src/store/shared.ts +12 -0
  163. package/src/store/types.ts +65 -0
  164. package/src/types.ts +69 -0
  165. package/tsconfig.tsbuildinfo +1 -1
  166. package/src/components/app.js +0 -73
  167. package/src/demo.js +0 -9
  168. package/src/native-app.js +0 -85
  169. /package/src/components/__tests__/{node-manager_test.js → node-manager.test.ts} +0 -0
  170. /package/src/components/{compute-layout-parameters.js → compute-layout-parameters.ts} +0 -0
  171. /package/src/components/input/__tests__/{context-tracking_test.js → context-tracking.test.ts} +0 -0
  172. /package/src/components/input/__tests__/{mathquill_test.js → mathquill.test.ts} +0 -0
  173. /package/src/components/input/__tests__/{test-math-wrapper.jsx → test-math-wrapper.ts} +0 -0
  174. /package/src/components/keypad/{button.stories.js → button.stories.tsx} +0 -0
  175. /package/src/components/{styles.js → styles.ts} +0 -0
  176. /package/src/components/tabbar/__tests__/{tabbar_test.js → tabbar.test.tsx} +0 -0
package/dist/es/index.js CHANGED
@@ -330,6 +330,9 @@ CursorHandle.defaultProps = {
330
330
  const touchSlopPx = 8;
331
331
  class DragListener {
332
332
  constructor(onDrag, initialEvent) {
333
+ this._scrollListener = void 0;
334
+ this._moveListener = void 0;
335
+ this._endAndCancelListener = void 0;
333
336
  // We detect drags in two ways. First, by listening for the window
334
337
  // scroll event (we consider any legitimate scroll to be a drag).
335
338
  this._scrollListener = () => {
@@ -488,7 +491,7 @@ const IN_DENOMINATOR = "IN_DENOMINATOR";
488
491
  // write is non-leaf math (numbers and variables).
489
492
  const BEFORE_FRACTION = "BEFORE_FRACTION";
490
493
 
491
- var CursorContexts = /*#__PURE__*/Object.freeze({
494
+ var cursorContexts = /*#__PURE__*/Object.freeze({
492
495
  __proto__: null,
493
496
  NONE: NONE,
494
497
  IN_PARENS: IN_PARENS,
@@ -639,7 +642,13 @@ const KeysForJumpContext = {
639
642
  [IN_DENOMINATOR]: Keys.JUMP_OUT_DENOMINATOR
640
643
  };
641
644
  class MathWrapper {
645
+ // MathQuill interface
646
+ // MathQuill input
647
+
642
648
  constructor(element, options = {}, callbacks = {}) {
649
+ this.MQ = void 0;
650
+ this.mathField = void 0;
651
+ this.callbacks = void 0;
643
652
  this.MQ = MathQuill.getInterface(2);
644
653
  this.mathField = this.MQ.MathField(element, {
645
654
  // use a span instead of a textarea so that we don't bring up the
@@ -718,7 +727,7 @@ class MathWrapper {
718
727
  this._handleBackspace(cursor);
719
728
  } else if (key === Keys.LEFT) {
720
729
  this._handleLeftArrow(cursor);
721
- } else if (key === Keys.RIGHT || key === Keys.JUMP_OUT) {
730
+ } else if (key === Keys.RIGHT) {
722
731
  this._handleRightArrow(cursor);
723
732
  } else if (/^[a-zA-Z]$/.test(key)) {
724
733
  this.mathField[WRITE](key);
@@ -1217,7 +1226,7 @@ class MathWrapper {
1217
1226
  if (this._isInsideEmptyNode(cursor)) {
1218
1227
  const grandparent = cursor.parent.parent;
1219
1228
  const command = this._maybeFindCommandBeforeParens(grandparent);
1220
- cursor.insLeftOf(command.startNode);
1229
+ cursor.insLeftOf(command == null ? void 0 : command.startNode);
1221
1230
  cursor.startSelection();
1222
1231
  if (grandparent[this.MQ.R] !== MQ_END) {
1223
1232
  cursor.insRightOf(grandparent[this.MQ.R]);
@@ -1381,18 +1390,6 @@ class MathWrapper {
1381
1390
  * TODO(charlie): Move this scroll logic out of our components and into a higher
1382
1391
  * level in the component tree--perhaps even into webapp, beyond Perseus.
1383
1392
  */
1384
-
1385
- // Taken from https://dev.opera.com/articles/fixing-the-scrolltop-bug/
1386
- function bodyOrHtml() {
1387
- if ("scrollingElement" in document) {
1388
- return document.scrollingElement;
1389
- }
1390
- // Fallback for legacy browsers
1391
- if (navigator.userAgent.indexOf("WebKit") !== -1) {
1392
- return document.body;
1393
- }
1394
- return document.documentElement;
1395
- }
1396
1393
  const scrollIntoView = (containerNode, keypadNode) => {
1397
1394
  // TODO(charlie): There's no need for us to be reading the keypad bounds
1398
1395
  // here, since they're pre-determined by logic in the store. We should
@@ -1402,7 +1399,7 @@ const scrollIntoView = (containerNode, keypadNode) => {
1402
1399
  const containerTopPx = containerBounds.top;
1403
1400
 
1404
1401
  // Get the element that scrolls the document.
1405
- const scrollNode = bodyOrHtml();
1402
+ const scrollNode = document.scrollingElement;
1406
1403
  const desiredMarginPx = 16;
1407
1404
  if (keypadNode) {
1408
1405
  // NOTE(charlie): We can't use the bounding rect of the keypad,
@@ -1421,7 +1418,9 @@ const scrollIntoView = (containerNode, keypadNode) => {
1421
1418
  // the bottom of the input is just above the top of the keypad,
1422
1419
  // taking care not to scroll the input out of view.
1423
1420
  const scrollOffset = Math.min(containerBottomPx - keypadTopPx + desiredMarginPx, containerTopPx);
1424
- scrollNode.scrollTop += scrollOffset;
1421
+ if (scrollNode) {
1422
+ scrollNode.scrollTop += scrollOffset;
1423
+ }
1425
1424
  return;
1426
1425
  }
1427
1426
  }
@@ -1429,7 +1428,7 @@ const scrollIntoView = (containerNode, keypadNode) => {
1429
1428
  // Alternatively, if the input is out of the viewport or nearly out
1430
1429
  // of the viewport, scroll it into view. We can do this regardless
1431
1430
  // of whether the keypad has been provided.
1432
- if (containerTopPx < desiredMarginPx) {
1431
+ if (scrollNode && containerTopPx < desiredMarginPx) {
1433
1432
  scrollNode.scrollTop -= containerBounds.height + desiredMarginPx;
1434
1433
  }
1435
1434
  };
@@ -2133,320 +2132,12 @@ const inlineStyles$1 = {
2133
2132
  }
2134
2133
  };
2135
2134
 
2136
- const KeyConfigs = {
2137
- // Basic math keys.
2138
- [Keys.PLUS]: {
2139
- type: KeyTypes.OPERATOR,
2140
- // I18N: A label for a plus sign.
2141
- ariaLabel: i18n._("Plus")
2142
- },
2143
- [Keys.MINUS]: {
2144
- type: KeyTypes.OPERATOR,
2145
- // I18N: A label for a minus sign.
2146
- ariaLabel: i18n._("Minus")
2147
- },
2148
- [Keys.NEGATIVE]: {
2149
- type: KeyTypes.VALUE,
2150
- // I18N: A label for a minus sign.
2151
- ariaLabel: i18n._("Negative")
2152
- },
2153
- [Keys.TIMES]: {
2154
- type: KeyTypes.OPERATOR,
2155
- // I18N: A label for a multiplication sign (represented with an 'x').
2156
- ariaLabel: i18n._("Multiply")
2157
- },
2158
- [Keys.DIVIDE]: {
2159
- type: KeyTypes.OPERATOR,
2160
- // I18N: A label for a division sign.
2161
- ariaLabel: i18n._("Divide")
2162
- },
2163
- [Keys.DECIMAL]: {
2164
- type: KeyTypes.VALUE,
2165
- // I18N: A label for a decimal symbol.
2166
- ariaLabel: i18n._("Decimal"),
2167
- icon: decimalSeparator === DecimalSeparators.COMMA ? {
2168
- // TODO(charlie): Get an SVG icon for the comma, or verify with
2169
- // design that the text-rendered version is acceptable.
2170
- type: IconTypes.TEXT,
2171
- data: ","
2172
- } : {
2173
- type: IconTypes.SVG,
2174
- data: Keys.PERIOD
2175
- }
2176
- },
2177
- [Keys.PERCENT]: {
2178
- type: KeyTypes.OPERATOR,
2179
- // I18N: A label for a percent sign.
2180
- ariaLabel: i18n._("Percent")
2181
- },
2182
- [Keys.CDOT]: {
2183
- type: KeyTypes.OPERATOR,
2184
- // I18N: A label for a multiplication sign (represented as a dot).
2185
- ariaLabel: i18n._("Multiply")
2186
- },
2187
- [Keys.EQUAL]: {
2188
- type: KeyTypes.OPERATOR,
2189
- ariaLabel: i18n._("Equals sign")
2190
- },
2191
- [Keys.NEQ]: {
2192
- type: KeyTypes.OPERATOR,
2193
- ariaLabel: i18n._("Not-equals sign")
2194
- },
2195
- [Keys.GT]: {
2196
- type: KeyTypes.OPERATOR,
2197
- // I18N: A label for a 'greater than' sign (represented as '>').
2198
- ariaLabel: i18n._("Greater than sign")
2199
- },
2200
- [Keys.LT]: {
2201
- type: KeyTypes.OPERATOR,
2202
- // I18N: A label for a 'less than' sign (represented as '<').
2203
- ariaLabel: i18n._("Less than sign")
2204
- },
2205
- [Keys.GEQ]: {
2206
- type: KeyTypes.OPERATOR,
2207
- ariaLabel: i18n._("Greater than or equal to sign")
2208
- },
2209
- [Keys.LEQ]: {
2210
- type: KeyTypes.OPERATOR,
2211
- ariaLabel: i18n._("Less than or equal to sign")
2212
- },
2213
- // mobile native
2214
- [Keys.FRAC_INCLUSIVE]: {
2215
- type: KeyTypes.OPERATOR,
2216
- // I18N: A label for a button that creates a new fraction and puts the
2217
- // current expression in the numerator of that fraction.
2218
- ariaLabel: i18n._("Fraction, with current expression in numerator")
2219
- },
2220
- // mobile native
2221
- [Keys.FRAC_EXCLUSIVE]: {
2222
- type: KeyTypes.OPERATOR,
2223
- // I18N: A label for a button that creates a new fraction next to the
2224
- // cursor.
2225
- ariaLabel: i18n._("Fraction, excluding the current expression")
2226
- },
2227
- // mobile web
2228
- [Keys.FRAC]: {
2229
- type: KeyTypes.OPERATOR,
2230
- // I18N: A label for a button that creates a new fraction next to the
2231
- // cursor.
2232
- ariaLabel: i18n._("Fraction, excluding the current expression")
2233
- },
2234
- [Keys.EXP]: {
2235
- type: KeyTypes.OPERATOR,
2236
- // I18N: A label for a button that will allow the user to input a custom
2237
- // exponent.
2238
- ariaLabel: i18n._("Custom exponent")
2239
- },
2240
- [Keys.EXP_2]: {
2241
- type: KeyTypes.OPERATOR,
2242
- // I18N: A label for a button that will square (take to the second
2243
- // power) some math.
2244
- ariaLabel: i18n._("Square")
2245
- },
2246
- [Keys.EXP_3]: {
2247
- type: KeyTypes.OPERATOR,
2248
- // I18N: A label for a button that will cube (take to the third power)
2249
- // some math.
2250
- ariaLabel: i18n._("Cube")
2251
- },
2252
- [Keys.SQRT]: {
2253
- type: KeyTypes.OPERATOR,
2254
- ariaLabel: i18n._("Square root")
2255
- },
2256
- [Keys.CUBE_ROOT]: {
2257
- type: KeyTypes.OPERATOR,
2258
- ariaLabel: i18n._("Cube root")
2259
- },
2260
- [Keys.RADICAL]: {
2261
- type: KeyTypes.OPERATOR,
2262
- ariaLabel: i18n._("Radical with custom root")
2263
- },
2264
- [Keys.LEFT_PAREN]: {
2265
- type: KeyTypes.OPERATOR,
2266
- ariaLabel: i18n._("Left parenthesis")
2267
- },
2268
- [Keys.RIGHT_PAREN]: {
2269
- type: KeyTypes.OPERATOR,
2270
- ariaLabel: i18n._("Right parenthesis")
2271
- },
2272
- [Keys.LN]: {
2273
- type: KeyTypes.OPERATOR,
2274
- ariaLabel: i18n._("Natural logarithm")
2275
- },
2276
- [Keys.LOG]: {
2277
- type: KeyTypes.OPERATOR,
2278
- ariaLabel: i18n._("Logarithm with base 10")
2279
- },
2280
- [Keys.LOG_N]: {
2281
- type: KeyTypes.OPERATOR,
2282
- ariaLabel: i18n._("Logarithm with custom base")
2283
- },
2284
- [Keys.SIN]: {
2285
- type: KeyTypes.OPERATOR,
2286
- ariaLabel: i18n._("Sine")
2287
- },
2288
- [Keys.COS]: {
2289
- type: KeyTypes.OPERATOR,
2290
- ariaLabel: i18n._("Cosine")
2291
- },
2292
- [Keys.TAN]: {
2293
- type: KeyTypes.OPERATOR,
2294
- ariaLabel: i18n._("Tangent")
2295
- },
2296
- [Keys.PI]: {
2297
- type: KeyTypes.VALUE,
2298
- ariaLabel: i18n._("Pi"),
2299
- icon: {
2300
- type: IconTypes.MATH,
2301
- data: "\\pi"
2302
- }
2303
- },
2304
- [Keys.THETA]: {
2305
- type: KeyTypes.VALUE,
2306
- ariaLabel: i18n._("Theta"),
2307
- icon: {
2308
- type: IconTypes.MATH,
2309
- data: "\\theta"
2310
- }
2311
- },
2312
- [Keys.NOOP]: {
2313
- type: KeyTypes.EMPTY
2314
- },
2315
- // Input navigation keys.
2316
- [Keys.UP]: {
2317
- type: KeyTypes.INPUT_NAVIGATION,
2318
- ariaLabel: i18n._("Up arrow")
2319
- },
2320
- [Keys.RIGHT]: {
2321
- type: KeyTypes.INPUT_NAVIGATION,
2322
- ariaLabel: i18n._("Right arrow")
2323
- },
2324
- [Keys.DOWN]: {
2325
- type: KeyTypes.INPUT_NAVIGATION,
2326
- ariaLabel: i18n._("Down arrow")
2327
- },
2328
- [Keys.LEFT]: {
2329
- type: KeyTypes.INPUT_NAVIGATION,
2330
- ariaLabel: i18n._("Left arrow")
2331
- },
2332
- [Keys.JUMP_OUT_PARENTHESES]: {
2333
- type: KeyTypes.INPUT_NAVIGATION,
2334
- ariaLabel: i18n._("Navigate right out of a set of parentheses")
2335
- },
2336
- [Keys.JUMP_OUT_EXPONENT]: {
2337
- type: KeyTypes.INPUT_NAVIGATION,
2338
- ariaLabel: i18n._("Navigate right out of an exponent")
2339
- },
2340
- [Keys.JUMP_OUT_BASE]: {
2341
- type: KeyTypes.INPUT_NAVIGATION,
2342
- ariaLabel: i18n._("Navigate right out of a base")
2343
- },
2344
- [Keys.JUMP_INTO_NUMERATOR]: {
2345
- type: KeyTypes.INPUT_NAVIGATION,
2346
- ariaLabel: i18n._("Navigate right into the numerator of a fraction")
2347
- },
2348
- [Keys.JUMP_OUT_NUMERATOR]: {
2349
- type: KeyTypes.INPUT_NAVIGATION,
2350
- ariaLabel: i18n._("Navigate right out of the numerator and into the denominator")
2351
- },
2352
- [Keys.JUMP_OUT_DENOMINATOR]: {
2353
- type: KeyTypes.INPUT_NAVIGATION,
2354
- ariaLabel: i18n._("Navigate right out of the denominator of a fraction")
2355
- },
2356
- [Keys.BACKSPACE]: {
2357
- type: KeyTypes.INPUT_NAVIGATION,
2358
- // I18N: A label for a button that will delete some input.
2359
- ariaLabel: i18n._("Delete")
2360
- },
2361
- // Keypad navigation keys.
2362
- [Keys.DISMISS]: {
2363
- type: KeyTypes.KEYPAD_NAVIGATION,
2364
- // I18N: A label for a button that will dismiss/hide a keypad.
2365
- ariaLabel: i18n._("Dismiss")
2366
- }
2367
- };
2368
-
2369
- // Add in any multi-function buttons. By default, these keys will mix in any
2370
- // configuration settings from their default child key (i.e., the first key in
2371
- // the `childKeyIds` array).
2372
- // TODO(charlie): Make the multi-function button's long-press interaction
2373
- // accessible.
2374
- // NOTE(kevinb): This is only used in the mobile native app.
2375
- KeyConfigs[Keys.FRAC_MULTI] = {
2376
- childKeyIds: [Keys.FRAC_INCLUSIVE, Keys.FRAC_EXCLUSIVE]
2377
- };
2378
-
2379
- // TODO(charlie): Use the numeral color for the 'Many' key.
2380
- KeyConfigs[Keys.MANY] = {
2381
- type: KeyTypes.MANY
2382
- // childKeyIds will be configured by the client.
2383
- };
2384
-
2385
- // Add in every numeral.
2386
- const NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
2387
- for (const num of NUMBERS) {
2388
- // TODO(charlie): Consider removing the SVG icons that we have for the
2389
- // numeral keys. They can be rendered just as easily with text (though that
2390
- // would mean that we'd be using text beyond the variable key).
2391
- const textRepresentation = `${num}`;
2392
- KeyConfigs[`NUM_${num}`] = {
2393
- type: KeyTypes.VALUE,
2394
- ariaLabel: textRepresentation,
2395
- icon: {
2396
- type: IconTypes.TEXT,
2397
- data: textRepresentation
2398
- }
2399
- };
2400
- }
2401
-
2402
- // Add in every variable.
2403
- 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"];
2404
- for (const letter of LETTERS) {
2405
- const lowerCaseVariable = letter.toLowerCase();
2406
- const upperCaseVariable = letter.toUpperCase();
2407
- for (const textRepresentation of [lowerCaseVariable, upperCaseVariable]) {
2408
- KeyConfigs[textRepresentation] = {
2409
- type: KeyTypes.VALUE,
2410
- ariaLabel: textRepresentation,
2411
- icon: {
2412
- type: IconTypes.MATH,
2413
- data: textRepresentation
2414
- }
2415
- };
2416
- }
2417
- }
2418
- for (const key of Object.keys(KeyConfigs)) {
2419
- KeyConfigs[key] = _extends({
2420
- id: key,
2421
- // Default to an SVG icon indexed by the key name.
2422
- icon: {
2423
- type: IconTypes.SVG,
2424
- data: key
2425
- }
2426
- }, KeyConfigs[key]);
2427
- }
2428
-
2429
- /**
2430
- * React PropTypes that may be shared between components.
2431
- */
2432
- const iconPropType = PropTypes.shape({
2433
- type: PropTypes.oneOf(Object.keys(IconTypes)).isRequired,
2434
- data: PropTypes.string.isRequired
2435
- });
2436
- const keyIdPropType = PropTypes.oneOf(Object.keys(KeyConfigs));
2437
- const keyConfigPropType = PropTypes.shape({
2438
- ariaLabel: PropTypes.string,
2439
- id: keyIdPropType.isRequired,
2440
- type: PropTypes.oneOf(Object.keys(KeyTypes)).isRequired,
2441
- childKeyIds: PropTypes.arrayOf(keyIdPropType),
2442
- icon: iconPropType.isRequired
2443
- });
2444
- const keypadConfigurationPropType = PropTypes.shape({
2445
- keypadType: PropTypes.oneOf(Object.keys(KeypadTypes)).isRequired,
2446
- extraKeys: PropTypes.arrayOf(keyIdPropType)
2447
- });
2135
+ /**
2136
+ * React PropTypes that may be shared between components.
2137
+ */
2448
2138
 
2449
2139
  // NOTE(jared): This is no longer guaranteed to be React element
2140
+ // NOTE(matthewc): only seems to be used in Perseus
2450
2141
  const keypadElementPropType = PropTypes.shape({
2451
2142
  activate: PropTypes.func.isRequired,
2452
2143
  dismiss: PropTypes.func.isRequired,
@@ -2455,44 +2146,6 @@ const keypadElementPropType = PropTypes.shape({
2455
2146
  setKeyHandler: PropTypes.func.isRequired,
2456
2147
  getDOMNode: PropTypes.func.isRequired
2457
2148
  });
2458
- const bordersPropType = PropTypes.arrayOf(PropTypes.oneOf(Object.keys(BorderDirections)));
2459
- const boundingBoxPropType = PropTypes.shape({
2460
- height: PropTypes.number,
2461
- width: PropTypes.number,
2462
- top: PropTypes.number,
2463
- right: PropTypes.number,
2464
- bottom: PropTypes.number,
2465
- left: PropTypes.number
2466
- });
2467
- const echoPropType = PropTypes.shape({
2468
- animationId: PropTypes.string.isRequired,
2469
- animationType: PropTypes.oneOf(Object.keys(EchoAnimationTypes)).isRequired,
2470
- borders: bordersPropType,
2471
- id: keyIdPropType.isRequired,
2472
- initialBounds: boundingBoxPropType.isRequired
2473
- });
2474
- const cursorContextPropType = PropTypes.oneOf(Object.keys(CursorContexts));
2475
- const popoverPropType = PropTypes.shape({
2476
- parentId: keyIdPropType.isRequired,
2477
- bounds: boundingBoxPropType.isRequired,
2478
- childKeyIds: PropTypes.arrayOf(keyIdPropType).isRequired
2479
- });
2480
- PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]);
2481
-
2482
- function _objectWithoutPropertiesLoose(source, excluded) {
2483
- if (source == null) return {};
2484
- var target = {};
2485
- var sourceKeys = Object.keys(source);
2486
- var key, i;
2487
-
2488
- for (i = 0; i < sourceKeys.length; i++) {
2489
- key = sourceKeys[i];
2490
- if (excluded.indexOf(key) >= 0) continue;
2491
- target[key] = source[key];
2492
- }
2493
-
2494
- return target;
2495
- }
2496
2149
 
2497
2150
  // naming convention: verb + noun
2498
2151
  // the noun should be one of the other properties in the object that's
@@ -2511,8 +2164,6 @@ const activateKeypad = () => {
2511
2164
 
2512
2165
  /**
2513
2166
  * Configure the keypad with the provided configuration parameters.
2514
- *
2515
- * See: `prop-types.js#keypadConfigurationPropType`.
2516
2167
  */
2517
2168
  const configureKeypad = configuration => {
2518
2169
  return {
@@ -2548,133 +2199,20 @@ const setCursor = cursor => {
2548
2199
  };
2549
2200
  };
2550
2201
 
2551
- /**
2552
- * An algorithm for computing the appropriate layout parameters for the keypad,
2553
- * including the size of the buttons and whether or not to render fullscreen,
2554
- * taking into account a number of factors including the size of the screen, the
2555
- * orientation of the screen, the presence of browser chrome, the presence of
2556
- * other exercise-related chrome, the size of the input box, the parameters that
2557
- * define the keypad (i.e., the number of rows, columns, and pages), and so
2558
- * forth.
2559
- *
2560
- * The computations herein make some strong assumptions about the sizes of
2561
- * various other elements and the situations under which they will be visible
2562
- * (e.g., browser chrome). However, this is just a heuristic--it's not crucial
2563
- * that our buttons are sized in a pixel-perfect manner, but rather, that we
2564
- * make a balanced use of space.
2565
- *
2566
- * Note that one goal of the algorithm is to avoid resizing the keypad in the
2567
- * face of dynamic browser chrome. In order to avoid that awkwardness, we tend
2568
- * to be conservative in our measurements and make things smaller than they
2569
- * might need to be.
2570
- */
2571
- const minButtonHeight = 48;
2572
- const maxButtonSize = 64;
2573
- const minSpaceAboveKeypad = 32;
2574
-
2575
- // These values are taken from an iPhone 5, but should be consistent with the
2576
- // iPhone 4 as well. Regardless, these are meant to be representative of the
2577
- // possible types of browser chrome that could appear in various context, rather
2578
- // than pixel-perfect for every device.
2579
- const safariNavBarWhenShrunk = 44;
2580
- const safariNavBarWhenExpanded = 64;
2581
- const safariToolbar = 44;
2582
-
2583
- // In mobile Safari, the browser chrome is completely hidden in landscape,
2584
- // though a shrunken navbar and full-sized toolbar on scroll. In portrait, the
2585
- // shrunken navbar is always visible, but expands on scroll (and the toolbar
2586
- // appears as well).
2587
- const maxLandscapeBrowserChrome = safariNavBarWhenShrunk + safariToolbar;
2588
- const maxPortraitBrowserChrome = safariToolbar + (safariNavBarWhenExpanded - safariNavBarWhenShrunk);
2589
-
2590
- // This represents the 'worst case' aspect ratio that we care about (for
2591
- // portrait layouts). It's taken from the iPhone 4. The height is computed by
2592
- // taking the height of the device and removing the persistent, shrunken navbar.
2593
- // (We don't need to account for the expanded navbar, since we include the
2594
- // difference when reserving space above the keypad.)
2595
- const worstCaseAspectRatio = 320 / (480 - safariNavBarWhenShrunk);
2596
- const computeLayoutParameters = ({
2597
- numColumns,
2598
- numMaxVisibleRows,
2599
- numPages
2600
- }, {
2601
- pageWidthPx,
2602
- pageHeightPx
2603
- }, {
2604
- deviceOrientation,
2605
- deviceType
2606
- }, {
2607
- navigationPadEnabled,
2608
- paginationEnabled,
2609
- toolbarEnabled
2610
- }) => {
2611
- // First, compute some values that will be used in multiple computations.
2612
- const effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
2613
-
2614
- // Then, compute the button dimensions based on the provided parameters.
2615
- let buttonDimensions;
2616
- if (deviceType === DeviceTypes.PHONE) {
2617
- const isLandscape = deviceOrientation === DeviceOrientations.LANDSCAPE;
2618
-
2619
- // In many cases, the browser chrome will already have been factored
2620
- // into `pageHeightPx`. But we have no way of knowing if that's
2621
- // the case or not. As such, we take a conservative approach and
2622
- // assume that the chrome is _never_ included in `pageHeightPx`.
2623
- const browserChromeHeight = isLandscape ? maxLandscapeBrowserChrome : maxPortraitBrowserChrome;
2624
-
2625
- // Count up all the space that we need to reserve on the page.
2626
- // Namely, we need to account for:
2627
- // 1. Space between the keypad and the top of the page.
2628
- // 2. The presence of the exercise toolbar.
2629
- // 3. The presence of the view pager indicator.
2630
- // 4. Any browser chrome that may appear later.
2631
- const reservedSpace = minSpaceAboveKeypad + browserChromeHeight + (toolbarEnabled ? toolbarHeightPx : 0) + (paginationEnabled ? pageIndicatorHeightPx : 0);
2632
-
2633
- // Next, compute the effective width and height. We can use the page
2634
- // width as the effective width. For the height, though, we take
2635
- // another conservative measure when in portrait by assuming that
2636
- // the device has the worst possible aspect ratio. In other words,
2637
- // we ignore the device height in portrait and assume the worst.
2638
- // This prevents the keypad from changing size when browser chrome
2639
- // appears and disappears.
2640
- const effectiveWidth = pageWidthPx;
2641
- const effectiveHeight = isLandscape ? pageHeightPx : pageWidthPx / worstCaseAspectRatio;
2642
- const maxKeypadHeight = effectiveHeight - reservedSpace;
2202
+ function _objectWithoutPropertiesLoose(source, excluded) {
2203
+ if (source == null) return {};
2204
+ var target = {};
2205
+ var sourceKeys = Object.keys(source);
2206
+ var key, i;
2643
2207
 
2644
- // Finally, compute the button height and width. In computing the
2645
- // height, accommodate for the maximum number of rows that will ever be
2646
- // visible (since the toggling of popovers can increase the number of
2647
- // visible rows).
2648
- const buttonHeightPx = Math.max(Math.min(maxKeypadHeight / numMaxVisibleRows, maxButtonSize), minButtonHeight);
2649
- let buttonWidthPx;
2650
- if (numPages > 1) {
2651
- const _effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
2652
- buttonWidthPx = effectiveWidth / _effectiveNumColumns;
2653
- } else {
2654
- buttonWidthPx = isLandscape ? maxButtonSize : effectiveWidth / numColumns;
2655
- }
2656
- buttonDimensions = {
2657
- widthPx: buttonWidthPx,
2658
- heightPx: buttonHeightPx
2659
- };
2660
- } else if (deviceType === DeviceTypes.TABLET) {
2661
- buttonDimensions = {
2662
- widthPx: maxButtonSize,
2663
- heightPx: maxButtonSize
2664
- };
2665
- } else {
2666
- throw new Error("Invalid device type: " + deviceType);
2208
+ for (i = 0; i < sourceKeys.length; i++) {
2209
+ key = sourceKeys[i];
2210
+ if (excluded.indexOf(key) >= 0) continue;
2211
+ target[key] = source[key];
2667
2212
  }
2668
2213
 
2669
- // Finally, determine whether the keypad should be rendered in the
2670
- // fullscreen layout by determining its resultant width.
2671
- const numSeparators = (navigationPadEnabled ? 1 : 0) + (!paginationEnabled ? numPages - 1 : 0);
2672
- const keypadWidth = effectiveNumColumns * buttonDimensions.widthPx + (navigationPadEnabled ? navigationPadWidthPx : 0) + numSeparators * innerBorderWidthPx;
2673
- return {
2674
- buttonDimensions,
2675
- layoutMode: keypadWidth >= pageWidthPx ? LayoutModes.FULLSCREEN : LayoutModes.COMPACT
2676
- };
2677
- };
2214
+ return target;
2215
+ }
2678
2216
 
2679
2217
  /**
2680
2218
  * The state machine that backs our gesture system. In particular, this state
@@ -2684,15 +2222,23 @@ const computeLayoutParameters = ({
2684
2222
  * multi-touch interactions, tracking gesture state on a per-touch basis.
2685
2223
  */
2686
2224
 
2687
- const defaults = {
2225
+ // exported for tests
2226
+
2227
+ const defaultOptions = {
2688
2228
  longPressWaitTimeMs: 50,
2689
2229
  swipeThresholdPx: 20,
2690
2230
  holdIntervalMs: 250
2691
2231
  };
2692
2232
  class GestureStateMachine {
2693
2233
  constructor(handlers, options, swipeDisabledNodeIds, multiPressableKeys) {
2234
+ this.handlers = void 0;
2235
+ this.options = void 0;
2236
+ this.swipeDisabledNodeIds = void 0;
2237
+ this.multiPressableKeys = void 0;
2238
+ this.touchState = void 0;
2239
+ this.swipeState = void 0;
2694
2240
  this.handlers = handlers;
2695
- this.options = _extends({}, defaults, options);
2241
+ this.options = _extends({}, defaultOptions, options);
2696
2242
  this.swipeDisabledNodeIds = swipeDisabledNodeIds || [];
2697
2243
  this.multiPressableKeys = multiPressableKeys || [];
2698
2244
 
@@ -2953,6 +2499,10 @@ class GestureStateMachine {
2953
2499
 
2954
2500
  class NodeManager {
2955
2501
  constructor() {
2502
+ this._nodesById = void 0;
2503
+ this._bordersById = void 0;
2504
+ this._orderedIds = void 0;
2505
+ this._cachedBoundingBoxesById = void 0;
2956
2506
  // A mapping from IDs to DOM nodes.
2957
2507
  this._nodesById = {};
2958
2508
 
@@ -3002,6 +2552,7 @@ class NodeManager {
3002
2552
  const seenIds = {};
3003
2553
  for (const _id of allIds) {
3004
2554
  if (!seenIds[_id]) {
2555
+ // @ts-expect-error TS2345
3005
2556
  orderedIds.push(_id);
3006
2557
  seenIds[_id] = true;
3007
2558
  }
@@ -3070,6 +2621,9 @@ class NodeManager {
3070
2621
 
3071
2622
  class PopoverStateMachine {
3072
2623
  constructor(handlers) {
2624
+ this.handlers = void 0;
2625
+ this.popovers = void 0;
2626
+ this.activePopover = void 0;
3073
2627
  this.handlers = handlers;
3074
2628
  this.activePopover = null;
3075
2629
  this.popovers = {};
@@ -3166,8 +2720,8 @@ class PopoverStateMachine {
3166
2720
  this.activePopover = id;
3167
2721
  this.handlers.onActiveNodesChanged({
3168
2722
  popover: {
3169
- parentId: this.activePopover,
3170
- childIds: this.popovers[this.activePopover]
2723
+ parentId: id,
2724
+ childIds: this.popovers[id]
3171
2725
  },
3172
2726
  focus: this._defaultNodeForPopover(this.activePopover)
3173
2727
  });
@@ -3229,12 +2783,17 @@ class PopoverStateMachine {
3229
2783
  }
3230
2784
  }
3231
2785
 
3232
- const _excluded$5 = ["popover"];
2786
+ const _excluded$4 = ["popover"];
3233
2787
  const coordsForEvent = evt => {
3234
2788
  return [evt.changedTouches[0].clientX, evt.changedTouches[0].clientY];
3235
2789
  };
3236
2790
  class GestureManager {
3237
2791
  constructor(options, handlers, disabledSwipeKeys, multiPressableKeys) {
2792
+ this.swipeEnabled = void 0;
2793
+ this.trackEvents = void 0;
2794
+ this.nodeManager = void 0;
2795
+ this.popoverStateMachine = void 0;
2796
+ this.gestureStateMachine = void 0;
3238
2797
  const {
3239
2798
  swipeEnabled
3240
2799
  } = options;
@@ -3248,7 +2807,7 @@ class GestureManager {
3248
2807
  const {
3249
2808
  popover
3250
2809
  } = activeNodes,
3251
- rest = _objectWithoutPropertiesLoose(activeNodes, _excluded$5);
2810
+ rest = _objectWithoutPropertiesLoose(activeNodes, _excluded$4);
3252
2811
  handlers.onActiveNodesChanged(_extends({
3253
2812
  popover: popover && {
3254
2813
  parentId: popover.parentId,
@@ -3416,50 +2975,413 @@ class GestureManager {
3416
2975
  }
3417
2976
  }
3418
2977
 
3419
- /**
3420
- * A small triangular decal to sit in the corner of a parent component.
3421
- */
3422
- class CornerDecal extends React.Component {
3423
- render() {
3424
- const {
3425
- style
3426
- } = this.props;
3427
- const containerStyle = [styles$d.container, ...(Array.isArray(style) ? style : [style])];
3428
- return /*#__PURE__*/React.createElement(View, {
3429
- style: containerStyle
3430
- }, /*#__PURE__*/React.createElement("svg", {
3431
- width: triangleSizePx,
3432
- height: triangleSizePx,
3433
- viewBox: "4 4 8 8"
3434
- }, /*#__PURE__*/React.createElement("path", {
3435
- fill: offBlack,
3436
- opacity: "0.3",
3437
- d: "M5.29289322,5.70710678 L10.2928932,10.7071068 C10.9228581,11.3370716 12,10.8909049 12,10 L12,5 C12,4.44771525 11.5522847,4 11,4 L6,4 C5.10909515,4 4.66292836,5.07714192 5.29289322,5.70710678 Z" // @Nolint
3438
- })));
3439
- }
3440
- }
3441
- CornerDecal.propTypes = {
3442
- style: PropTypes.any
3443
- };
3444
- const triangleSizePx = 7;
3445
- const styles$d = StyleSheet.create({
3446
- container: {
3447
- position: "absolute",
3448
- top: 0,
3449
- right: 0,
3450
- width: triangleSizePx,
3451
- height: triangleSizePx
3452
- }
3453
- });
3454
-
3455
- /**
3456
- * Common styles shared across components.
3457
- */
3458
- var Styles = StyleSheet.create({
3459
- row: {
3460
- flexDirection: "row"
2978
+ const KeyConfigs = {
2979
+ // Basic math keys.
2980
+ [Keys.PLUS]: {
2981
+ type: KeyTypes.OPERATOR,
2982
+ // I18N: A label for a plus sign.
2983
+ ariaLabel: i18n._("Plus")
3461
2984
  },
3462
- column: {
2985
+ [Keys.MINUS]: {
2986
+ type: KeyTypes.OPERATOR,
2987
+ // I18N: A label for a minus sign.
2988
+ ariaLabel: i18n._("Minus")
2989
+ },
2990
+ [Keys.NEGATIVE]: {
2991
+ type: KeyTypes.VALUE,
2992
+ // I18N: A label for a minus sign.
2993
+ ariaLabel: i18n._("Negative")
2994
+ },
2995
+ [Keys.TIMES]: {
2996
+ type: KeyTypes.OPERATOR,
2997
+ // I18N: A label for a multiplication sign (represented with an 'x').
2998
+ ariaLabel: i18n._("Multiply")
2999
+ },
3000
+ [Keys.DIVIDE]: {
3001
+ type: KeyTypes.OPERATOR,
3002
+ // I18N: A label for a division sign.
3003
+ ariaLabel: i18n._("Divide")
3004
+ },
3005
+ [Keys.DECIMAL]: {
3006
+ type: KeyTypes.VALUE,
3007
+ // I18N: A label for a decimal symbol.
3008
+ ariaLabel: i18n._("Decimal"),
3009
+ icon: decimalSeparator === DecimalSeparators.COMMA ? {
3010
+ // TODO(charlie): Get an SVG icon for the comma, or verify with
3011
+ // design that the text-rendered version is acceptable.
3012
+ type: IconTypes.TEXT,
3013
+ data: ","
3014
+ } : {
3015
+ type: IconTypes.SVG,
3016
+ data: Keys.PERIOD
3017
+ }
3018
+ },
3019
+ [Keys.PERCENT]: {
3020
+ type: KeyTypes.OPERATOR,
3021
+ // I18N: A label for a percent sign.
3022
+ ariaLabel: i18n._("Percent")
3023
+ },
3024
+ [Keys.CDOT]: {
3025
+ type: KeyTypes.OPERATOR,
3026
+ // I18N: A label for a multiplication sign (represented as a dot).
3027
+ ariaLabel: i18n._("Multiply")
3028
+ },
3029
+ [Keys.EQUAL]: {
3030
+ type: KeyTypes.OPERATOR,
3031
+ ariaLabel: i18n._("Equals sign")
3032
+ },
3033
+ [Keys.NEQ]: {
3034
+ type: KeyTypes.OPERATOR,
3035
+ ariaLabel: i18n._("Not-equals sign")
3036
+ },
3037
+ [Keys.GT]: {
3038
+ type: KeyTypes.OPERATOR,
3039
+ // I18N: A label for a 'greater than' sign (represented as '>').
3040
+ ariaLabel: i18n._("Greater than sign")
3041
+ },
3042
+ [Keys.LT]: {
3043
+ type: KeyTypes.OPERATOR,
3044
+ // I18N: A label for a 'less than' sign (represented as '<').
3045
+ ariaLabel: i18n._("Less than sign")
3046
+ },
3047
+ [Keys.GEQ]: {
3048
+ type: KeyTypes.OPERATOR,
3049
+ ariaLabel: i18n._("Greater than or equal to sign")
3050
+ },
3051
+ [Keys.LEQ]: {
3052
+ type: KeyTypes.OPERATOR,
3053
+ ariaLabel: i18n._("Less than or equal to sign")
3054
+ },
3055
+ // mobile native
3056
+ [Keys.FRAC_INCLUSIVE]: {
3057
+ type: KeyTypes.OPERATOR,
3058
+ // I18N: A label for a button that creates a new fraction and puts the
3059
+ // current expression in the numerator of that fraction.
3060
+ ariaLabel: i18n._("Fraction, with current expression in numerator")
3061
+ },
3062
+ // mobile native
3063
+ [Keys.FRAC_EXCLUSIVE]: {
3064
+ type: KeyTypes.OPERATOR,
3065
+ // I18N: A label for a button that creates a new fraction next to the
3066
+ // cursor.
3067
+ ariaLabel: i18n._("Fraction, excluding the current expression")
3068
+ },
3069
+ // mobile web
3070
+ [Keys.FRAC]: {
3071
+ type: KeyTypes.OPERATOR,
3072
+ // I18N: A label for a button that creates a new fraction next to the
3073
+ // cursor.
3074
+ ariaLabel: i18n._("Fraction, excluding the current expression")
3075
+ },
3076
+ [Keys.EXP]: {
3077
+ type: KeyTypes.OPERATOR,
3078
+ // I18N: A label for a button that will allow the user to input a custom
3079
+ // exponent.
3080
+ ariaLabel: i18n._("Custom exponent")
3081
+ },
3082
+ [Keys.EXP_2]: {
3083
+ type: KeyTypes.OPERATOR,
3084
+ // I18N: A label for a button that will square (take to the second
3085
+ // power) some math.
3086
+ ariaLabel: i18n._("Square")
3087
+ },
3088
+ [Keys.EXP_3]: {
3089
+ type: KeyTypes.OPERATOR,
3090
+ // I18N: A label for a button that will cube (take to the third power)
3091
+ // some math.
3092
+ ariaLabel: i18n._("Cube")
3093
+ },
3094
+ [Keys.SQRT]: {
3095
+ type: KeyTypes.OPERATOR,
3096
+ ariaLabel: i18n._("Square root")
3097
+ },
3098
+ [Keys.CUBE_ROOT]: {
3099
+ type: KeyTypes.OPERATOR,
3100
+ ariaLabel: i18n._("Cube root")
3101
+ },
3102
+ [Keys.RADICAL]: {
3103
+ type: KeyTypes.OPERATOR,
3104
+ ariaLabel: i18n._("Radical with custom root")
3105
+ },
3106
+ [Keys.LEFT_PAREN]: {
3107
+ type: KeyTypes.OPERATOR,
3108
+ ariaLabel: i18n._("Left parenthesis")
3109
+ },
3110
+ [Keys.RIGHT_PAREN]: {
3111
+ type: KeyTypes.OPERATOR,
3112
+ ariaLabel: i18n._("Right parenthesis")
3113
+ },
3114
+ [Keys.LN]: {
3115
+ type: KeyTypes.OPERATOR,
3116
+ ariaLabel: i18n._("Natural logarithm")
3117
+ },
3118
+ [Keys.LOG]: {
3119
+ type: KeyTypes.OPERATOR,
3120
+ ariaLabel: i18n._("Logarithm with base 10")
3121
+ },
3122
+ [Keys.LOG_N]: {
3123
+ type: KeyTypes.OPERATOR,
3124
+ ariaLabel: i18n._("Logarithm with custom base")
3125
+ },
3126
+ [Keys.SIN]: {
3127
+ type: KeyTypes.OPERATOR,
3128
+ ariaLabel: i18n._("Sine")
3129
+ },
3130
+ [Keys.COS]: {
3131
+ type: KeyTypes.OPERATOR,
3132
+ ariaLabel: i18n._("Cosine")
3133
+ },
3134
+ [Keys.TAN]: {
3135
+ type: KeyTypes.OPERATOR,
3136
+ ariaLabel: i18n._("Tangent")
3137
+ },
3138
+ [Keys.PI]: {
3139
+ type: KeyTypes.VALUE,
3140
+ ariaLabel: i18n._("Pi"),
3141
+ icon: {
3142
+ type: IconTypes.MATH,
3143
+ data: "\\pi"
3144
+ }
3145
+ },
3146
+ [Keys.THETA]: {
3147
+ type: KeyTypes.VALUE,
3148
+ ariaLabel: i18n._("Theta"),
3149
+ icon: {
3150
+ type: IconTypes.MATH,
3151
+ data: "\\theta"
3152
+ }
3153
+ },
3154
+ [Keys.NOOP]: {
3155
+ type: KeyTypes.EMPTY
3156
+ },
3157
+ // Input navigation keys.
3158
+ [Keys.UP]: {
3159
+ type: KeyTypes.INPUT_NAVIGATION,
3160
+ ariaLabel: i18n._("Up arrow")
3161
+ },
3162
+ [Keys.RIGHT]: {
3163
+ type: KeyTypes.INPUT_NAVIGATION,
3164
+ ariaLabel: i18n._("Right arrow")
3165
+ },
3166
+ [Keys.DOWN]: {
3167
+ type: KeyTypes.INPUT_NAVIGATION,
3168
+ ariaLabel: i18n._("Down arrow")
3169
+ },
3170
+ [Keys.LEFT]: {
3171
+ type: KeyTypes.INPUT_NAVIGATION,
3172
+ ariaLabel: i18n._("Left arrow")
3173
+ },
3174
+ [Keys.JUMP_OUT_PARENTHESES]: {
3175
+ type: KeyTypes.INPUT_NAVIGATION,
3176
+ ariaLabel: i18n._("Navigate right out of a set of parentheses")
3177
+ },
3178
+ [Keys.JUMP_OUT_EXPONENT]: {
3179
+ type: KeyTypes.INPUT_NAVIGATION,
3180
+ ariaLabel: i18n._("Navigate right out of an exponent")
3181
+ },
3182
+ [Keys.JUMP_OUT_BASE]: {
3183
+ type: KeyTypes.INPUT_NAVIGATION,
3184
+ ariaLabel: i18n._("Navigate right out of a base")
3185
+ },
3186
+ [Keys.JUMP_INTO_NUMERATOR]: {
3187
+ type: KeyTypes.INPUT_NAVIGATION,
3188
+ ariaLabel: i18n._("Navigate right into the numerator of a fraction")
3189
+ },
3190
+ [Keys.JUMP_OUT_NUMERATOR]: {
3191
+ type: KeyTypes.INPUT_NAVIGATION,
3192
+ ariaLabel: i18n._("Navigate right out of the numerator and into the denominator")
3193
+ },
3194
+ [Keys.JUMP_OUT_DENOMINATOR]: {
3195
+ type: KeyTypes.INPUT_NAVIGATION,
3196
+ ariaLabel: i18n._("Navigate right out of the denominator of a fraction")
3197
+ },
3198
+ [Keys.BACKSPACE]: {
3199
+ type: KeyTypes.INPUT_NAVIGATION,
3200
+ // I18N: A label for a button that will delete some input.
3201
+ ariaLabel: i18n._("Delete")
3202
+ },
3203
+ // Keypad navigation keys.
3204
+ [Keys.DISMISS]: {
3205
+ type: KeyTypes.KEYPAD_NAVIGATION,
3206
+ // I18N: A label for a button that will dismiss/hide a keypad.
3207
+ ariaLabel: i18n._("Dismiss")
3208
+ }
3209
+ };
3210
+
3211
+ // Add in any multi-function buttons. By default, these keys will mix in any
3212
+ // configuration settings from their default child key (i.e., the first key in
3213
+ // the `childKeyIds` array).
3214
+ // TODO(charlie): Make the multi-function button's long-press interaction
3215
+ // accessible.
3216
+ // NOTE(kevinb): This is only used in the mobile native app.
3217
+ KeyConfigs[Keys.FRAC_MULTI] = {
3218
+ childKeyIds: [Keys.FRAC_INCLUSIVE, Keys.FRAC_EXCLUSIVE]
3219
+ };
3220
+
3221
+ // TODO(charlie): Use the numeral color for the 'Many' key.
3222
+ KeyConfigs[Keys.MANY] = {
3223
+ type: KeyTypes.MANY
3224
+ // childKeyIds will be configured by the client.
3225
+ };
3226
+
3227
+ // Add in every numeral.
3228
+ const NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
3229
+ for (const num of NUMBERS) {
3230
+ // TODO(charlie): Consider removing the SVG icons that we have for the
3231
+ // numeral keys. They can be rendered just as easily with text (though that
3232
+ // would mean that we'd be using text beyond the variable key).
3233
+ const textRepresentation = `${num}`;
3234
+ KeyConfigs[`NUM_${num}`] = {
3235
+ type: KeyTypes.VALUE,
3236
+ ariaLabel: textRepresentation,
3237
+ icon: {
3238
+ type: IconTypes.TEXT,
3239
+ data: textRepresentation
3240
+ }
3241
+ };
3242
+ }
3243
+
3244
+ // Add in every variable.
3245
+ 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"];
3246
+ for (const letter of LETTERS) {
3247
+ const lowerCaseVariable = letter.toLowerCase();
3248
+ const upperCaseVariable = letter.toUpperCase();
3249
+ for (const textRepresentation of [lowerCaseVariable, upperCaseVariable]) {
3250
+ KeyConfigs[textRepresentation] = {
3251
+ type: KeyTypes.VALUE,
3252
+ ariaLabel: textRepresentation,
3253
+ icon: {
3254
+ type: IconTypes.MATH,
3255
+ data: textRepresentation
3256
+ }
3257
+ };
3258
+ }
3259
+ }
3260
+ for (const key of Object.keys(KeyConfigs)) {
3261
+ KeyConfigs[key] = _extends({
3262
+ id: key,
3263
+ // Default to an SVG icon indexed by the key name.
3264
+ icon: {
3265
+ type: IconTypes.SVG,
3266
+ data: key
3267
+ }
3268
+ }, KeyConfigs[key]);
3269
+ }
3270
+
3271
+ // Used to generate unique animation IDs for the echo animations. The actual
3272
+ // values are irrelevant as long as they are unique.
3273
+ let _lastAnimationId = 0;
3274
+ const initialEchoState = {
3275
+ echoes: []
3276
+ };
3277
+ const echoReducer = function echoReducer(state = initialEchoState, action) {
3278
+ switch (action.type) {
3279
+ case "PressKey":
3280
+ const keyConfig = KeyConfigs[action.key];
3281
+
3282
+ // Add in the echo animation if the user performs a math
3283
+ // operation.
3284
+ if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
3285
+ return _extends({}, state, {
3286
+ echoes: [...state.echoes, {
3287
+ animationId: "" + _lastAnimationId++,
3288
+ animationType: action.inPopover ? EchoAnimationTypes.LONG_FADE_ONLY : EchoAnimationTypes.FADE_ONLY,
3289
+ borders: action.borders,
3290
+ id: keyConfig.id,
3291
+ initialBounds: action.initialBounds
3292
+ }]
3293
+ });
3294
+ }
3295
+ return state;
3296
+ case "RemoveEcho":
3297
+ const remainingEchoes = state.echoes.filter(echo => {
3298
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'animationId' does not exist on type 'never'.
3299
+ return echo.animationId !== action.animationId;
3300
+ });
3301
+ return _extends({}, state, {
3302
+ echoes: remainingEchoes
3303
+ });
3304
+ default:
3305
+ return state;
3306
+ }
3307
+ };
3308
+
3309
+ const initialInputState = {
3310
+ keyHandler: null,
3311
+ cursor: {
3312
+ context: NONE
3313
+ }
3314
+ };
3315
+ const inputReducer = function inputReducer(state = initialInputState, action) {
3316
+ switch (action.type) {
3317
+ case "SetKeyHandler":
3318
+ return _extends({}, state, {
3319
+ keyHandler: action.keyHandler
3320
+ });
3321
+ case "PressKey":
3322
+ const keyConfig = KeyConfigs[action.key];
3323
+ if (keyConfig.type !== KeyTypes.KEYPAD_NAVIGATION) {
3324
+ // This is probably an anti-pattern but it works for the
3325
+ // case where we don't actually control the state but we
3326
+ // still want to communicate with the other object
3327
+ return _extends({}, state, {
3328
+ cursor: state.keyHandler == null ? void 0 : state.keyHandler(keyConfig.id)
3329
+ });
3330
+ }
3331
+
3332
+ // TODO(kevinb) get state from MathQuill and store it?
3333
+ return state;
3334
+ case "SetCursor":
3335
+ return _extends({}, state, {
3336
+ cursor: action.cursor
3337
+ });
3338
+ default:
3339
+ return state;
3340
+ }
3341
+ };
3342
+
3343
+ /**
3344
+ * A small triangular decal to sit in the corner of a parent component.
3345
+ */
3346
+ class CornerDecal extends React.Component {
3347
+ render() {
3348
+ const {
3349
+ style
3350
+ } = this.props;
3351
+ const containerStyle = [styles$d.container, ...(Array.isArray(style) ? style : [style])];
3352
+ return /*#__PURE__*/React.createElement(View, {
3353
+ style: containerStyle
3354
+ }, /*#__PURE__*/React.createElement("svg", {
3355
+ width: triangleSizePx,
3356
+ height: triangleSizePx,
3357
+ viewBox: "4 4 8 8"
3358
+ }, /*#__PURE__*/React.createElement("path", {
3359
+ fill: offBlack,
3360
+ opacity: "0.3",
3361
+ d: "M5.29289322,5.70710678 L10.2928932,10.7071068 C10.9228581,11.3370716 12,10.8909049 12,10 L12,5 C12,4.44771525 11.5522847,4 11,4 L6,4 C5.10909515,4 4.66292836,5.07714192 5.29289322,5.70710678 Z" // @Nolint
3362
+ })));
3363
+ }
3364
+ }
3365
+
3366
+ const triangleSizePx = 7;
3367
+ const styles$d = StyleSheet.create({
3368
+ container: {
3369
+ position: "absolute",
3370
+ top: 0,
3371
+ right: 0,
3372
+ width: triangleSizePx,
3373
+ height: triangleSizePx
3374
+ }
3375
+ });
3376
+
3377
+ /**
3378
+ * Common styles shared across components.
3379
+ */
3380
+ var Styles = StyleSheet.create({
3381
+ row: {
3382
+ flexDirection: "row"
3383
+ },
3384
+ column: {
3463
3385
  flexDirection: "column"
3464
3386
  },
3465
3387
  oneColumn: {
@@ -3521,10 +3443,6 @@ class MathIcon extends React.Component {
3521
3443
  });
3522
3444
  }
3523
3445
  }
3524
- MathIcon.propTypes = {
3525
- math: PropTypes.string.isRequired,
3526
- style: PropTypes.any
3527
- };
3528
3446
  const styles$c = StyleSheet.create({
3529
3447
  size: {
3530
3448
  height: iconSizeHeightPx,
@@ -4834,10 +4752,6 @@ class SvgIcon extends React.Component {
4834
4752
  });
4835
4753
  }
4836
4754
  }
4837
- SvgIcon.propTypes = {
4838
- color: PropTypes.string.isRequired,
4839
- name: PropTypes.string.isRequired
4840
- };
4841
4755
 
4842
4756
  /**
4843
4757
  * A component that renders a text-based icon.
@@ -4858,10 +4772,6 @@ class TextIcon extends React.Component {
4858
4772
  }, /*#__PURE__*/React.createElement(Text, null, character));
4859
4773
  }
4860
4774
  }
4861
- TextIcon.propTypes = {
4862
- character: PropTypes.string.isRequired,
4863
- style: PropTypes.any
4864
- };
4865
4775
  const styles$b = StyleSheet.create({
4866
4776
  size: {
4867
4777
  height: iconSizeHeightPx,
@@ -4906,18 +4816,11 @@ class Icon extends React.PureComponent {
4906
4816
  character: icon.data,
4907
4817
  style: styleWithFocus
4908
4818
  });
4819
+ default:
4820
+ throw new Error("No icon or symbol provided");
4909
4821
  }
4910
- throw new Error("No icon or symbol provided");
4911
4822
  }
4912
4823
  }
4913
- Icon.propTypes = {
4914
- focused: PropTypes.bool,
4915
- icon: iconPropType.isRequired,
4916
- // An Aphrodite style object, or an array of Aphrodite style objects.
4917
- // Note that custom styles will only be applied to text and math icons
4918
- // (and not SVG icons).
4919
- style: PropTypes.any
4920
- };
4921
4824
  const styles$a = StyleSheet.create({
4922
4825
  unfocused: {
4923
4826
  color: unfocusedColor
@@ -5012,13 +4915,9 @@ class MultiSymbolGrid extends React.Component {
5012
4915
  }))));
5013
4916
  }
5014
4917
  }
5015
- throw new Error("Invalid number of icons:", icons.length);
4918
+ throw new Error(`Invalid number of icons: ${icons.length}`);
5016
4919
  }
5017
4920
  }
5018
- MultiSymbolGrid.propTypes = {
5019
- focused: PropTypes.bool,
5020
- icons: PropTypes.arrayOf(iconPropType).isRequired
5021
- };
5022
4921
  const verticalInsetPx = 2;
5023
4922
  const horizontalInsetPx = 4;
5024
4923
  const styles$9 = StyleSheet.create({
@@ -5062,6 +4961,7 @@ const styles$9 = StyleSheet.create({
5062
4961
  class KeypadButton extends React.PureComponent {
5063
4962
  constructor(...args) {
5064
4963
  super(...args);
4964
+ this.buttonSizeStyle = void 0;
5065
4965
  this._preInjectStyles = () => {
5066
4966
  // HACK(charlie): Pre-inject all of the possible styles for the button.
5067
4967
  // This avoids a flickering effect in the echo animation whereby the
@@ -5110,10 +5010,12 @@ class KeypadButton extends React.PureComponent {
5110
5010
  break;
5111
5011
  }
5112
5012
  const borderStyle = [];
5113
- if (borders.indexOf(BorderDirections.LEFT) !== -1) {
5013
+ if (borders.includes(BorderDirections.LEFT)) {
5014
+ // @ts-expect-error TS2345
5114
5015
  borderStyle.push(styles$8.leftBorder);
5115
5016
  }
5116
- if (borders.indexOf(BorderDirections.BOTTOM) !== -1) {
5017
+ if (borders.includes(BorderDirections.BOTTOM)) {
5018
+ // @ts-expect-error TS2345
5117
5019
  borderStyle.push(styles$8.bottomBorder);
5118
5020
  }
5119
5021
  return [styles$8.buttonBase, backgroundStyle, ...borderStyle, type === KeyTypes.ECHO && styles$8.echo, this.buttonSizeStyle,
@@ -5161,7 +5063,7 @@ class KeypadButton extends React.PureComponent {
5161
5063
  const renderFocused = !disabled && focused || popoverEnabled || type === KeyTypes.ECHO;
5162
5064
  const buttonStyle = this._getButtonStyle(type, borders, style);
5163
5065
  const focusStyle = this._getFocusStyle(type);
5164
- const iconWrapperStyle = [styles$8.iconWrapper, disabled && styles$8.disabled];
5066
+ const iconWrapperStyle = [styles$8.iconWrapper, disabled ? styles$8.disabled : undefined];
5165
5067
  const eventHandlers = {
5166
5068
  onTouchCancel,
5167
5069
  onTouchEnd,
@@ -5212,32 +5114,6 @@ class KeypadButton extends React.PureComponent {
5212
5114
  }
5213
5115
  }
5214
5116
  }
5215
- KeypadButton.propTypes = {
5216
- ariaLabel: PropTypes.string,
5217
- // The borders to display on the button. Typically, this should be set
5218
- // using one of the preset `BorderStyles` options.
5219
- borders: bordersPropType,
5220
- // Any additional keys that can be accessed by long-pressing on the
5221
- // button.
5222
- childKeys: PropTypes.arrayOf(keyConfigPropType),
5223
- // Whether the button should be rendered in a 'disabled' state, i.e.,
5224
- // without any touch feedback.
5225
- disabled: PropTypes.bool,
5226
- focused: PropTypes.bool,
5227
- heightPx: PropTypes.number.isRequired,
5228
- icon: iconPropType,
5229
- onTouchCancel: PropTypes.func,
5230
- onTouchEnd: PropTypes.func,
5231
- onTouchMove: PropTypes.func,
5232
- onTouchStart: PropTypes.func,
5233
- popoverEnabled: PropTypes.bool,
5234
- style: PropTypes.any,
5235
- type: PropTypes.oneOf(Object.keys(KeyTypes)).isRequired,
5236
- // NOTE(charlie): We may want to make this optional for phone layouts
5237
- // (and rely on Flexbox instead), since it might not be pixel perfect
5238
- // with borders and such.
5239
- widthPx: PropTypes.number.isRequired
5240
- };
5241
5117
  KeypadButton.defaultProps = {
5242
5118
  borders: BorderStyles.ALL,
5243
5119
  childKeys: [],
@@ -5249,8 +5125,6 @@ const focusInsetPx = 4;
5249
5125
  const focusBoxZIndex = 0;
5250
5126
  const styles$8 = StyleSheet.create({
5251
5127
  buttonBase: {
5252
- // HACK(benkomalo): support old style flex box in Android browsers
5253
- "-webkit-box-flex": "1",
5254
5128
  flex: 1,
5255
5129
  cursor: "pointer",
5256
5130
  // Make the text unselectable
@@ -5326,20 +5200,23 @@ const styleForButtonDimensions = (heightPx, widthPx) => {
5326
5200
  }).buttonSize;
5327
5201
  };
5328
5202
  const mapStateToProps$7 = state => {
5329
- return state.layout.buttonDimensions;
5203
+ return {
5204
+ heightPx: state.layout.buttonDimensions.heightPx,
5205
+ widthPx: state.layout.buttonDimensions.widthPx
5206
+ };
5330
5207
  };
5331
5208
  var KeypadButton$1 = connect(mapStateToProps$7, null, null, {
5332
5209
  forwardRef: true
5333
5210
  })(KeypadButton);
5334
5211
 
5335
- const _excluded$4 = ["gestureManager"];
5212
+ const _excluded$3 = ["gestureManager"];
5336
5213
  class EmptyKeypadButton extends React.Component {
5337
5214
  render() {
5338
5215
  const _this$props = this.props,
5339
5216
  {
5340
5217
  gestureManager
5341
5218
  } = _this$props,
5342
- rest = _objectWithoutPropertiesLoose(_this$props, _excluded$4);
5219
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded$3);
5343
5220
 
5344
5221
  // Register touch events on the button, but don't register its DOM node
5345
5222
  // or compute focus state or anything like that. We want the gesture
@@ -5354,9 +5231,6 @@ class EmptyKeypadButton extends React.Component {
5354
5231
  }, KeyConfigs.NOOP, rest));
5355
5232
  }
5356
5233
  }
5357
- EmptyKeypadButton.propTypes = {
5358
- gestureManager: PropTypes.instanceOf(GestureManager)
5359
- };
5360
5234
  const mapStateToProps$6 = state => {
5361
5235
  const {
5362
5236
  gestures
@@ -5369,7 +5243,7 @@ var EmptyKeypadButton$1 = connect(mapStateToProps$6, null, null, {
5369
5243
  forwardRef: true
5370
5244
  })(EmptyKeypadButton);
5371
5245
 
5372
- const _excluded$3 = ["borders", "childKeyIds", "disabled", "gestureManager", "id", "style"],
5246
+ const _excluded$2 = ["borders", "childKeyIds", "disabled", "gestureManager", "id", "style"],
5373
5247
  _excluded2 = ["keyConfig"];
5374
5248
  class TouchableKeypadButton extends React.Component {
5375
5249
  shouldComponentUpdate(newProps) {
@@ -5396,7 +5270,7 @@ class TouchableKeypadButton extends React.Component {
5396
5270
  id,
5397
5271
  style
5398
5272
  } = _this$props,
5399
- rest = _objectWithoutPropertiesLoose(_this$props, _excluded$3);
5273
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded$2);
5400
5274
 
5401
5275
  // Only bind the relevant event handlers if the key is enabled.
5402
5276
  const eventHandlers = disabled ? {
@@ -5416,17 +5290,6 @@ class TouchableKeypadButton extends React.Component {
5416
5290
  }, eventHandlers, rest));
5417
5291
  }
5418
5292
  }
5419
- TouchableKeypadButton.propTypes = {
5420
- borders: bordersPropType,
5421
- childKeyIds: PropTypes.arrayOf(keyIdPropType),
5422
- disabled: PropTypes.bool,
5423
- focused: PropTypes.bool,
5424
- gestureManager: PropTypes.instanceOf(GestureManager),
5425
- id: keyIdPropType.isRequired,
5426
- popoverEnabled: PropTypes.bool,
5427
- style: PropTypes.any,
5428
- type: PropTypes.oneOf(Object.keys(KeyTypes)).isRequired
5429
- };
5430
5293
  const extractProps = keyConfig => {
5431
5294
  const {
5432
5295
  ariaLabel,
@@ -5479,20 +5342,20 @@ var TouchableKeypadButton$1 = connect(mapStateToProps$5, null, null, {
5479
5342
  forwardRef: true
5480
5343
  })(TouchableKeypadButton);
5481
5344
 
5482
- const _excluded$2 = ["keys"];
5345
+ const _excluded$1 = ["keys"];
5483
5346
  class ManyKeypadButton extends React.Component {
5484
5347
  render() {
5485
5348
  const _this$props = this.props,
5486
5349
  {
5487
5350
  keys
5488
5351
  } = _this$props,
5489
- rest = _objectWithoutPropertiesLoose(_this$props, _excluded$2);
5352
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
5490
5353
 
5491
5354
  // If we have no extra symbols, render an empty button. If we have just
5492
5355
  // one, render a standard button. Otherwise, capture them all in a
5493
5356
  // single button.
5494
5357
  if (keys.length === 0) {
5495
- return /*#__PURE__*/React.createElement(EmptyKeypadButton$1, rest);
5358
+ return /*#__PURE__*/React.createElement(EmptyKeypadButton$1, null);
5496
5359
  } else if (keys.length === 1) {
5497
5360
  const keyConfig = KeyConfigs[keys[0]];
5498
5361
  return /*#__PURE__*/React.createElement(TouchableKeypadButton$1, _extends({
@@ -5510,8 +5373,8 @@ class ManyKeypadButton extends React.Component {
5510
5373
  }
5511
5374
  }
5512
5375
  }
5513
- ManyKeypadButton.propTypes = {
5514
- keys: PropTypes.arrayOf(keyIdPropType).isRequired
5376
+ ManyKeypadButton.defaultProps = {
5377
+ keys: []
5515
5378
  };
5516
5379
 
5517
5380
  /**
@@ -5559,20 +5422,12 @@ class Echo extends React.Component {
5559
5422
  return /*#__PURE__*/React.createElement("div", {
5560
5423
  style: containerStyle
5561
5424
  }, /*#__PURE__*/React.createElement(KeypadButton$1, {
5562
- name: id,
5563
5425
  icon: icon,
5564
5426
  type: KeyTypes.ECHO,
5565
5427
  borders: borders
5566
5428
  }));
5567
5429
  }
5568
5430
  }
5569
- Echo.propTypes = {
5570
- animationDurationMs: PropTypes.number.isRequired,
5571
- borders: bordersPropType,
5572
- id: keyIdPropType.isRequired,
5573
- initialBounds: boundingBoxPropType.isRequired,
5574
- onAnimationFinish: PropTypes.func.isRequired
5575
- };
5576
5431
  class EchoManager extends React.Component {
5577
5432
  constructor(...args) {
5578
5433
  super(...args);
@@ -5595,7 +5450,7 @@ class EchoManager extends React.Component {
5595
5450
  animationTransitionName = "echo-long-fade-only";
5596
5451
  break;
5597
5452
  default:
5598
- throw new Error("Invalid echo animation type:", animationType);
5453
+ throw new Error(`Invalid echo animation type: ${animationType}`);
5599
5454
  }
5600
5455
  return {
5601
5456
  animationDurationMs,
@@ -5640,16 +5495,12 @@ class EchoManager extends React.Component {
5640
5495
  key: animationId
5641
5496
  }, /*#__PURE__*/React.createElement(Echo, _extends({
5642
5497
  animationDurationMs: animationDurationMs,
5643
- onAnimationFinish: () => onAnimationFinish(animationId)
5498
+ onAnimationFinish: () => onAnimationFinish == null ? void 0 : onAnimationFinish(animationId)
5644
5499
  }, echo)));
5645
5500
  }));
5646
5501
  }));
5647
5502
  }
5648
5503
  }
5649
- EchoManager.propTypes = {
5650
- echoes: PropTypes.arrayOf(echoPropType),
5651
- onAnimationFinish: PropTypes.func.isRequired
5652
- };
5653
5504
 
5654
5505
  /**
5655
5506
  * A popover that renders a set of keys floating above the page.
@@ -5673,9 +5524,6 @@ class MultiSymbolPopover extends React.Component {
5673
5524
  }));
5674
5525
  }
5675
5526
  }
5676
- MultiSymbolPopover.propTypes = {
5677
- keys: PropTypes.arrayOf(keyConfigPropType)
5678
- };
5679
5527
  const styles$6 = StyleSheet.create({
5680
5528
  container: {
5681
5529
  flexDirection: "column-reverse",
@@ -5696,7 +5544,6 @@ const styles$6 = StyleSheet.create({
5696
5544
  // classnames specified in popover.less.
5697
5545
  const animationTransitionName = "popover";
5698
5546
  const animationDurationMs = 200;
5699
-
5700
5547
  // A container component used to position a popover absolutely at a specific
5701
5548
  // position.
5702
5549
  class PopoverContainer extends React.Component {
@@ -5715,10 +5562,6 @@ class PopoverContainer extends React.Component {
5715
5562
  }));
5716
5563
  }
5717
5564
  }
5718
- PopoverContainer.propTypes = {
5719
- bounds: boundingBoxPropType.isRequired,
5720
- childKeys: PropTypes.arrayOf(keyConfigPropType).isRequired
5721
- };
5722
5565
  class PopoverManager extends React.Component {
5723
5566
  render() {
5724
5567
  const {
@@ -5739,16 +5582,15 @@ class PopoverManager extends React.Component {
5739
5582
  })) : null;
5740
5583
  }
5741
5584
  }
5742
- PopoverManager.propTypes = {
5743
- popover: popoverPropType
5744
- };
5745
-
5746
- const _excluded$1 = ["initialBounds"];
5747
5585
 
5586
+ const _excluded = ["initialBounds"];
5748
5587
  // eslint-disable-next-line react/no-unsafe
5749
5588
  class Keypad extends React.Component {
5750
5589
  constructor(...args) {
5751
5590
  super(...args);
5591
+ this._isMounted = void 0;
5592
+ this._resizeTimeout = void 0;
5593
+ this._container = void 0;
5752
5594
  this._computeContainer = () => {
5753
5595
  const domNode = ReactDOM.findDOMNode(this);
5754
5596
  this._container = domNode.getBoundingClientRect();
@@ -5770,7 +5612,7 @@ class Keypad extends React.Component {
5770
5612
  // Throttle resize events -- taken from:
5771
5613
  // https://developer.mozilla.org/en-US/docs/Web/Events/resize
5772
5614
  if (this._resizeTimeout == null) {
5773
- this._resizeTimeout = setTimeout(() => {
5615
+ this._resizeTimeout = window.setTimeout(() => {
5774
5616
  this._resizeTimeout = null;
5775
5617
  if (this._isMounted) {
5776
5618
  this._updateSizeAndPosition();
@@ -5808,12 +5650,16 @@ class Keypad extends React.Component {
5808
5650
  const {
5809
5651
  initialBounds
5810
5652
  } = echo,
5811
- rest = _objectWithoutPropertiesLoose(echo, _excluded$1);
5653
+ rest = _objectWithoutPropertiesLoose(echo, _excluded);
5812
5654
  return _extends({}, rest, {
5813
5655
  initialBounds: {
5656
+ // @ts-expect-error TS2533
5814
5657
  top: initialBounds.top - this._container.top,
5658
+ // @ts-expect-error TS2533
5815
5659
  right: initialBounds.right - this._container.left,
5660
+ // @ts-expect-error TS2533
5816
5661
  bottom: initialBounds.bottom - this._container.top,
5662
+ // @ts-expect-error TS2533
5817
5663
  left: initialBounds.left - this._container.left,
5818
5664
  width: initialBounds.width,
5819
5665
  height: initialBounds.height
@@ -5826,7 +5672,12 @@ class Keypad extends React.Component {
5826
5672
  // the bottom left corners of the keys over which they appear.
5827
5673
  const relativePopover = popover && _extends({}, popover, {
5828
5674
  bounds: {
5829
- bottom: this._container.height - (popover.bounds.bottom - this._container.top),
5675
+ bottom:
5676
+ // @ts-expect-error TS2533
5677
+ this._container.height - (
5678
+ // @ts-expect-error TS2533
5679
+ popover.bounds.bottom - this._container.top),
5680
+ // @ts-expect-error TS2533
5830
5681
  left: popover.bounds.left - this._container.left,
5831
5682
  width: popover.bounds.width
5832
5683
  }
@@ -5841,23 +5692,12 @@ class Keypad extends React.Component {
5841
5692
  }));
5842
5693
  }
5843
5694
  }
5844
- Keypad.propTypes = {
5845
- children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
5846
- removeEcho: PropTypes.func.isRequired,
5847
- style: PropTypes.any,
5848
- // The props below are injected by redux
5849
-
5850
- // Whether the keypad is active, i.e., whether it should be rendered as
5851
- // visible or invisible.
5852
- active: PropTypes.bool,
5853
- echoes: PropTypes.arrayOf(echoPropType).isRequired,
5854
- popover: popoverPropType
5855
- };
5856
5695
  const mapStateToProps$4 = state => {
5857
- return _extends({}, state.echoes, {
5696
+ return {
5697
+ echoes: state.echoes.echoes,
5858
5698
  active: state.keypad.active,
5859
5699
  popover: state.gestures.popover
5860
- });
5700
+ };
5861
5701
  };
5862
5702
  const mapDispatchToProps$1 = dispatch => {
5863
5703
  return {
@@ -6093,12 +5933,6 @@ class TwoPageKeypad extends React.Component {
6093
5933
  }
6094
5934
  }
6095
5935
  }
6096
- TwoPageKeypad.propTypes = {
6097
- currentPage: PropTypes.oneOf([0, 1]).isRequired,
6098
- leftPage: PropTypes.node.isRequired,
6099
- paginationEnabled: PropTypes.bool.isRequired,
6100
- rightPage: PropTypes.node.isRequired
6101
- };
6102
5936
  const styles$3 = StyleSheet.create({
6103
5937
  keypad: {
6104
5938
  // Set the background to light grey, so that when the user drags the
@@ -6133,11 +5967,16 @@ const {
6133
5967
  roundedTopLeft: roundedTopLeft$2,
6134
5968
  roundedTopRight: roundedTopRight$1
6135
5969
  } = Styles;
5970
+ const expressionKeypadLayout = {
5971
+ rows: 4,
5972
+ columns: 5,
5973
+ numPages: 2,
5974
+ // Since we include a two-key popover in the top-right, when the popover
5975
+ // is visible, the keypad will expand to fill the equivalent of five
5976
+ // rows vertically.
5977
+ maxVisibleRows: 4
5978
+ };
6136
5979
  class ExpressionKeypad extends React.Component {
6137
- // Though we include an infinite-key popover in the bottom-left, it's
6138
- // assumed that we don't need to accommodate cases in which that key
6139
- // contains more than four children.
6140
-
6141
5980
  render() {
6142
5981
  const {
6143
5982
  currentPage,
@@ -6191,8 +6030,7 @@ class ExpressionKeypad extends React.Component {
6191
6030
  keyConfig: KeyConfigs.NUM_1,
6192
6031
  borders: BorderStyles.BOTTOM
6193
6032
  }), /*#__PURE__*/React.createElement(ManyKeypadButton, {
6194
- keys: extraKeys,
6195
- borders: BorderStyles.NONE
6033
+ keys: extraKeys
6196
6034
  })), /*#__PURE__*/React.createElement(View, {
6197
6035
  style: [column$1, oneColumn]
6198
6036
  }, /*#__PURE__*/React.createElement(TouchableKeypadButton$1, {
@@ -6330,18 +6168,6 @@ class ExpressionKeypad extends React.Component {
6330
6168
  });
6331
6169
  }
6332
6170
  }
6333
- ExpressionKeypad.propTypes = {
6334
- currentPage: PropTypes.number.isRequired,
6335
- cursorContext: cursorContextPropType.isRequired,
6336
- dynamicJumpOut: PropTypes.bool,
6337
- extraKeys: PropTypes.arrayOf(keyIdPropType),
6338
- roundTopLeft: PropTypes.bool,
6339
- roundTopRight: PropTypes.bool
6340
- };
6341
- ExpressionKeypad.rows = 4;
6342
- ExpressionKeypad.columns = 5;
6343
- ExpressionKeypad.maxVisibleRows = 4;
6344
- ExpressionKeypad.numPages = 2;
6345
6171
  const styles$2 = StyleSheet.create({
6346
6172
  // NOTE(charlie): These backgrounds are applied to as to fill in some
6347
6173
  // unfortunate 'cracks' in the layout. However, not all keys in the first
@@ -6356,9 +6182,10 @@ const styles$2 = StyleSheet.create({
6356
6182
  }
6357
6183
  });
6358
6184
  const mapStateToProps$2 = state => {
6185
+ var _state$input$cursor;
6359
6186
  return {
6360
6187
  currentPage: state.pager.currentPage,
6361
- cursorContext: state.input.cursor.context,
6188
+ cursorContext: (_state$input$cursor = state.input.cursor) == null ? void 0 : _state$input$cursor.context,
6362
6189
  dynamicJumpOut: !state.layout.navigationPadEnabled
6363
6190
  };
6364
6191
  };
@@ -6375,11 +6202,16 @@ const {
6375
6202
  roundedTopLeft: roundedTopLeft$1,
6376
6203
  roundedTopRight
6377
6204
  } = Styles;
6378
- class FractionKeypad extends React.Component {
6205
+ const fractionKeypadLayout = {
6206
+ rows: 4,
6207
+ columns: 4,
6208
+ numPages: 1,
6379
6209
  // Since we include a two-key popover in the top-right, when the popover
6380
6210
  // is visible, the keypad will expand to fill the equivalent of five
6381
6211
  // rows vertically.
6382
-
6212
+ maxVisibleRows: 5
6213
+ };
6214
+ class FractionKeypad extends React.Component {
6383
6215
  render() {
6384
6216
  const {
6385
6217
  cursorContext,
@@ -6482,29 +6314,290 @@ class FractionKeypad extends React.Component {
6482
6314
  borders: BorderStyles.LEFT
6483
6315
  })));
6484
6316
  }
6485
- }
6486
- FractionKeypad.propTypes = {
6487
- cursorContext: cursorContextPropType.isRequired,
6488
- dynamicJumpOut: PropTypes.bool,
6489
- roundTopLeft: PropTypes.bool,
6490
- roundTopRight: PropTypes.bool
6491
- };
6492
- FractionKeypad.rows = 4;
6493
- FractionKeypad.columns = 4;
6494
- FractionKeypad.maxVisibleRows = 5;
6495
- FractionKeypad.numPages = 1;
6496
- const mapStateToProps$1 = state => {
6317
+ }
6318
+ const mapStateToProps$1 = state => {
6319
+ var _state$input$cursor;
6320
+ return {
6321
+ cursorContext: (_state$input$cursor = state.input.cursor) == null ? void 0 : _state$input$cursor.context,
6322
+ dynamicJumpOut: !state.layout.navigationPadEnabled
6323
+ };
6324
+ };
6325
+ var FractionKeypad$1 = connect(mapStateToProps$1, null, null, {
6326
+ forwardRef: true
6327
+ })(FractionKeypad);
6328
+
6329
+ const defaultKeypadType = KeypadTypes.EXPRESSION;
6330
+ const keypadForType = {
6331
+ [KeypadTypes.FRACTION]: fractionKeypadLayout,
6332
+ [KeypadTypes.EXPRESSION]: expressionKeypadLayout
6333
+ };
6334
+
6335
+ const initialKeypadState = {
6336
+ extraKeys: ["x", "y", Keys.THETA, Keys.PI],
6337
+ keypadType: defaultKeypadType,
6338
+ active: false
6339
+ };
6340
+ const keypadReducer = function keypadReducer(state = initialKeypadState, action) {
6341
+ switch (action.type) {
6342
+ case "DismissKeypad":
6343
+ return _extends({}, state, {
6344
+ active: false
6345
+ });
6346
+ case "ActivateKeypad":
6347
+ return _extends({}, state, {
6348
+ active: true
6349
+ });
6350
+ case "ConfigureKeypad":
6351
+ return _extends({}, state, {
6352
+ // Default `extraKeys` to the empty array.
6353
+ extraKeys: []
6354
+ }, action.configuration);
6355
+ case "PressKey":
6356
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
6357
+ const keyConfig = KeyConfigs[action.key];
6358
+ // NOTE(charlie): Our keypad system operates by triggering key
6359
+ // presses with key IDs in a dumb manner, such that the keys
6360
+ // don't know what they can do--instead, the store is
6361
+ // responsible for interpreting key presses and triggering the
6362
+ // right actions when they occur. Hence, we figure off a
6363
+ // dismissal here rather than dispatching a dismiss action in
6364
+ // the first place.
6365
+ if (keyConfig.id === Keys.DISMISS) {
6366
+ return keypadReducer(state, {
6367
+ type: "DismissKeypad"
6368
+ });
6369
+ }
6370
+ return state;
6371
+ default:
6372
+ return state;
6373
+ }
6374
+ };
6375
+
6376
+ /**
6377
+ * An algorithm for computing the appropriate layout parameters for the keypad,
6378
+ * including the size of the buttons and whether or not to render fullscreen,
6379
+ * taking into account a number of factors including the size of the screen, the
6380
+ * orientation of the screen, the presence of browser chrome, the presence of
6381
+ * other exercise-related chrome, the size of the input box, the parameters that
6382
+ * define the keypad (i.e., the number of rows, columns, and pages), and so
6383
+ * forth.
6384
+ *
6385
+ * The computations herein make some strong assumptions about the sizes of
6386
+ * various other elements and the situations under which they will be visible
6387
+ * (e.g., browser chrome). However, this is just a heuristic--it's not crucial
6388
+ * that our buttons are sized in a pixel-perfect manner, but rather, that we
6389
+ * make a balanced use of space.
6390
+ *
6391
+ * Note that one goal of the algorithm is to avoid resizing the keypad in the
6392
+ * face of dynamic browser chrome. In order to avoid that awkwardness, we tend
6393
+ * to be conservative in our measurements and make things smaller than they
6394
+ * might need to be.
6395
+ */
6396
+ const minButtonHeight = 48;
6397
+ const maxButtonSize = 64;
6398
+ const minSpaceAboveKeypad = 32;
6399
+
6400
+ // These values are taken from an iPhone 5, but should be consistent with the
6401
+ // iPhone 4 as well. Regardless, these are meant to be representative of the
6402
+ // possible types of browser chrome that could appear in various context, rather
6403
+ // than pixel-perfect for every device.
6404
+ const safariNavBarWhenShrunk = 44;
6405
+ const safariNavBarWhenExpanded = 64;
6406
+ const safariToolbar = 44;
6407
+
6408
+ // In mobile Safari, the browser chrome is completely hidden in landscape,
6409
+ // though a shrunken navbar and full-sized toolbar on scroll. In portrait, the
6410
+ // shrunken navbar is always visible, but expands on scroll (and the toolbar
6411
+ // appears as well).
6412
+ const maxLandscapeBrowserChrome = safariNavBarWhenShrunk + safariToolbar;
6413
+ const maxPortraitBrowserChrome = safariToolbar + (safariNavBarWhenExpanded - safariNavBarWhenShrunk);
6414
+
6415
+ // This represents the 'worst case' aspect ratio that we care about (for
6416
+ // portrait layouts). It's taken from the iPhone 4. The height is computed by
6417
+ // taking the height of the device and removing the persistent, shrunken navbar.
6418
+ // (We don't need to account for the expanded navbar, since we include the
6419
+ // difference when reserving space above the keypad.)
6420
+ const worstCaseAspectRatio = 320 / (480 - safariNavBarWhenShrunk);
6421
+ const computeLayoutParameters = ({
6422
+ numColumns,
6423
+ numMaxVisibleRows,
6424
+ numPages
6425
+ }, {
6426
+ pageWidthPx,
6427
+ pageHeightPx
6428
+ }, {
6429
+ deviceOrientation,
6430
+ deviceType
6431
+ }, {
6432
+ navigationPadEnabled,
6433
+ paginationEnabled,
6434
+ toolbarEnabled
6435
+ }) => {
6436
+ // First, compute some values that will be used in multiple computations.
6437
+ const effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
6438
+
6439
+ // Then, compute the button dimensions based on the provided parameters.
6440
+ let buttonDimensions;
6441
+ if (deviceType === DeviceTypes.PHONE) {
6442
+ const isLandscape = deviceOrientation === DeviceOrientations.LANDSCAPE;
6443
+
6444
+ // In many cases, the browser chrome will already have been factored
6445
+ // into `pageHeightPx`. But we have no way of knowing if that's
6446
+ // the case or not. As such, we take a conservative approach and
6447
+ // assume that the chrome is _never_ included in `pageHeightPx`.
6448
+ const browserChromeHeight = isLandscape ? maxLandscapeBrowserChrome : maxPortraitBrowserChrome;
6449
+
6450
+ // Count up all the space that we need to reserve on the page.
6451
+ // Namely, we need to account for:
6452
+ // 1. Space between the keypad and the top of the page.
6453
+ // 2. The presence of the exercise toolbar.
6454
+ // 3. The presence of the view pager indicator.
6455
+ // 4. Any browser chrome that may appear later.
6456
+ const reservedSpace = minSpaceAboveKeypad + browserChromeHeight + (toolbarEnabled ? toolbarHeightPx : 0) + (paginationEnabled ? pageIndicatorHeightPx : 0);
6457
+
6458
+ // Next, compute the effective width and height. We can use the page
6459
+ // width as the effective width. For the height, though, we take
6460
+ // another conservative measure when in portrait by assuming that
6461
+ // the device has the worst possible aspect ratio. In other words,
6462
+ // we ignore the device height in portrait and assume the worst.
6463
+ // This prevents the keypad from changing size when browser chrome
6464
+ // appears and disappears.
6465
+ const effectiveWidth = pageWidthPx;
6466
+ const effectiveHeight = isLandscape ? pageHeightPx : pageWidthPx / worstCaseAspectRatio;
6467
+ const maxKeypadHeight = effectiveHeight - reservedSpace;
6468
+
6469
+ // Finally, compute the button height and width. In computing the
6470
+ // height, accommodate for the maximum number of rows that will ever be
6471
+ // visible (since the toggling of popovers can increase the number of
6472
+ // visible rows).
6473
+ const buttonHeightPx = Math.max(Math.min(maxKeypadHeight / numMaxVisibleRows, maxButtonSize), minButtonHeight);
6474
+ let buttonWidthPx;
6475
+ if (numPages > 1) {
6476
+ const _effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
6477
+ buttonWidthPx = effectiveWidth / _effectiveNumColumns;
6478
+ } else {
6479
+ buttonWidthPx = isLandscape ? maxButtonSize : effectiveWidth / numColumns;
6480
+ }
6481
+ buttonDimensions = {
6482
+ widthPx: buttonWidthPx,
6483
+ heightPx: buttonHeightPx
6484
+ };
6485
+ } else if (deviceType === DeviceTypes.TABLET) {
6486
+ buttonDimensions = {
6487
+ widthPx: maxButtonSize,
6488
+ heightPx: maxButtonSize
6489
+ };
6490
+ } else {
6491
+ throw new Error("Invalid device type: " + deviceType);
6492
+ }
6493
+
6494
+ // Finally, determine whether the keypad should be rendered in the
6495
+ // fullscreen layout by determining its resultant width.
6496
+ const numSeparators = (navigationPadEnabled ? 1 : 0) + (!paginationEnabled ? numPages - 1 : 0);
6497
+ const keypadWidth = effectiveNumColumns * buttonDimensions.widthPx + (navigationPadEnabled ? navigationPadWidthPx : 0) + numSeparators * innerBorderWidthPx;
6497
6498
  return {
6498
- cursorContext: state.input.cursor.context,
6499
- dynamicJumpOut: !state.layout.navigationPadEnabled
6499
+ buttonDimensions,
6500
+ layoutMode: keypadWidth >= pageWidthPx ? LayoutModes.FULLSCREEN : LayoutModes.COMPACT
6500
6501
  };
6501
6502
  };
6502
- var FractionKeypad$1 = connect(mapStateToProps$1, null, null, {
6503
- forwardRef: true
6504
- })(FractionKeypad);
6503
+
6504
+ const initialLayoutState = {
6505
+ gridDimensions: {
6506
+ numRows: keypadForType[defaultKeypadType].rows,
6507
+ numColumns: keypadForType[defaultKeypadType].columns,
6508
+ numMaxVisibleRows: keypadForType[defaultKeypadType].maxVisibleRows,
6509
+ numPages: keypadForType[defaultKeypadType].numPages
6510
+ },
6511
+ buttonDimensions: {
6512
+ widthPx: 48,
6513
+ heightPx: 48
6514
+ },
6515
+ pageDimensions: {
6516
+ pageWidthPx: 0,
6517
+ pageHeightPx: 0
6518
+ },
6519
+ layoutMode: LayoutModes.FULLSCREEN,
6520
+ paginationEnabled: false,
6521
+ navigationPadEnabled: false
6522
+ };
6523
+
6524
+ /**
6525
+ * Compute the additional layout state based on the provided page and grid
6526
+ * dimensions.
6527
+ */
6528
+ const layoutParametersForDimensions = (pageDimensions, gridDimensions) => {
6529
+ const {
6530
+ pageWidthPx,
6531
+ pageHeightPx
6532
+ } = pageDimensions;
6533
+
6534
+ // Determine the device type and orientation.
6535
+ const deviceOrientation = pageWidthPx > pageHeightPx ? DeviceOrientations.LANDSCAPE : DeviceOrientations.PORTRAIT;
6536
+ const deviceType = Math.min(pageWidthPx, pageHeightPx) > tabletCutoffPx ? DeviceTypes.TABLET : DeviceTypes.PHONE;
6537
+
6538
+ // Using that information, make some decisions (or assumptions)
6539
+ // about the resulting layout.
6540
+ const navigationPadEnabled = deviceType === DeviceTypes.TABLET;
6541
+ const paginationEnabled = deviceType === DeviceTypes.PHONE && deviceOrientation === DeviceOrientations.PORTRAIT;
6542
+ const deviceInfo = {
6543
+ deviceOrientation,
6544
+ deviceType
6545
+ };
6546
+ const layoutOptions = {
6547
+ navigationPadEnabled,
6548
+ paginationEnabled,
6549
+ // HACK(charlie): It's not great that we're making assumptions about
6550
+ // the toolbar (which is rendered by webapp, and should always be
6551
+ // visible and anchored to the bottom of the page for phone and
6552
+ // tablet exercises). But this is primarily a heuristic (the goal is
6553
+ // to preserve a 'good' amount of space between the top of the
6554
+ // keypad and the top of the page) so we afford to have some margin
6555
+ // of error.
6556
+ toolbarEnabled: true
6557
+ };
6558
+ return _extends({}, computeLayoutParameters(gridDimensions, pageDimensions, deviceInfo, layoutOptions), {
6559
+ // Pass along some of the layout information, so that other
6560
+ // components in the heirarchy can adapt appropriately.
6561
+ navigationPadEnabled,
6562
+ paginationEnabled
6563
+ });
6564
+ };
6565
+ const layoutReducer = function layoutReducer(state = initialLayoutState, action) {
6566
+ switch (action.type) {
6567
+ case "ConfigureKeypad":
6568
+ const {
6569
+ keypadType
6570
+ } = action.configuration;
6571
+ const gridDimensions = {
6572
+ numRows: keypadForType[keypadType].rows,
6573
+ numColumns: keypadForType[keypadType].columns,
6574
+ numMaxVisibleRows: keypadForType[keypadType].maxVisibleRows,
6575
+ numPages: keypadForType[keypadType].numPages
6576
+ };
6577
+ return _extends({}, state, layoutParametersForDimensions(state.pageDimensions, gridDimensions), {
6578
+ gridDimensions
6579
+ });
6580
+ case "SetPageSize":
6581
+ const {
6582
+ pageWidthPx,
6583
+ pageHeightPx
6584
+ } = action;
6585
+ const pageDimensions = {
6586
+ pageWidthPx,
6587
+ pageHeightPx
6588
+ };
6589
+ return _extends({}, state, layoutParametersForDimensions(pageDimensions, state.gridDimensions), {
6590
+ pageDimensions
6591
+ });
6592
+ default:
6593
+ return state;
6594
+ }
6595
+ };
6505
6596
 
6506
6597
  class VelocityTracker {
6507
6598
  constructor(options) {
6599
+ this.options = void 0;
6600
+ this._events = void 0;
6508
6601
  this.options = _extends({
6509
6602
  velocityTimeout: 100
6510
6603
  }, options);
@@ -6567,195 +6660,119 @@ class VelocityTracker {
6567
6660
  }
6568
6661
  }
6569
6662
 
6570
- const keypadForType = {
6571
- [KeypadTypes.FRACTION]: FractionKeypad$1,
6572
- [KeypadTypes.EXPRESSION]: ExpressionKeypad$1
6663
+ // We default to the right-most page. This is done so-as to enforce a
6664
+ // consistent orientation between the view pager layout and the flattened
6665
+ // layout, where our default page appears on the far right.
6666
+ const getDefaultPage = numPages => numPages - 1;
6667
+ const initialPagerState = {
6668
+ animateToPosition: false,
6669
+ currentPage: getDefaultPage(keypadForType[defaultKeypadType].numPages),
6670
+ // The cumulative differential in the horizontal direction for the
6671
+ // current swipe.
6672
+ dx: 0,
6673
+ numPages: keypadForType[defaultKeypadType].numPages,
6674
+ pageWidthPx: 0,
6675
+ velocityTracker: new VelocityTracker()
6573
6676
  };
6574
- const createStore = () => {
6575
- const initialInputState = {
6576
- keyHandler: null,
6577
- cursor: {
6578
- context: NONE
6579
- }
6580
- };
6581
- const inputReducer = function inputReducer(state = initialInputState, action) {
6582
- switch (action.type) {
6583
- case "SetKeyHandler":
6584
- return _extends({}, state, {
6585
- keyHandler: action.keyHandler
6586
- });
6587
- case "PressKey":
6588
- const keyConfig = KeyConfigs[action.key];
6589
- if (keyConfig.type !== KeyTypes.KEYPAD_NAVIGATION) {
6590
- // This is probably an anti-pattern but it works for the
6591
- // case where we don't actually control the state but we
6592
- // still want to communicate with the other object
6593
- return _extends({}, state, {
6594
- cursor: state.keyHandler(keyConfig.id)
6595
- });
6596
- }
6597
-
6598
- // TODO(kevinb) get state from MathQuill and store it?
6599
- return state;
6600
- case "SetCursor":
6601
- return _extends({}, state, {
6602
- cursor: action.cursor
6603
- });
6604
- default:
6605
- return state;
6606
- }
6607
- };
6608
- const defaultKeypadType = KeypadTypes.EXPRESSION;
6609
- const initialKeypadState = {
6610
- extraKeys: ["x", "y", Keys.THETA, Keys.PI],
6611
- keypadType: defaultKeypadType,
6612
- active: false
6613
- };
6614
- const keypadReducer = function keypadReducer(state = initialKeypadState, action) {
6615
- switch (action.type) {
6616
- case "DismissKeypad":
6617
- return _extends({}, state, {
6618
- active: false
6619
- });
6620
- case "ActivateKeypad":
6621
- return _extends({}, state, {
6622
- active: true
6623
- });
6624
- case "ConfigureKeypad":
6625
- return _extends({}, state, {
6626
- // Default `extraKeys` to the empty array.
6627
- extraKeys: []
6628
- }, action.configuration);
6629
- case "PressKey":
6630
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
6631
- const keyConfig = KeyConfigs[action.key];
6632
- // NOTE(charlie): Our keypad system operates by triggering key
6633
- // presses with key IDs in a dumb manner, such that the keys
6634
- // don't know what they can do--instead, the store is
6635
- // responsible for interpreting key presses and triggering the
6636
- // right actions when they occur. Hence, we figure off a
6637
- // dismissal here rather than dispatching a dismiss action in
6638
- // the first place.
6639
- if (keyConfig.id === Keys.DISMISS) {
6640
- return keypadReducer(state, {
6641
- type: "DismissKeypad"
6642
- });
6643
- }
6644
- return state;
6645
- default:
6646
- return state;
6647
- }
6648
- };
6649
-
6650
- // We default to the right-most page. This is done so-as to enforce a
6651
- // consistent orientation between the view pager layout and the flattened
6652
- // layout, where our default page appears on the far right.
6653
- const getDefaultPage = numPages => numPages - 1;
6654
- const initialPagerState = {
6655
- animateToPosition: false,
6656
- currentPage: getDefaultPage(keypadForType[defaultKeypadType].numPages),
6657
- // The cumulative differential in the horizontal direction for the
6658
- // current swipe.
6659
- dx: 0,
6660
- numPages: keypadForType[defaultKeypadType].numPages,
6661
- pageWidthPx: 0,
6662
- velocityTracker: new VelocityTracker()
6663
- };
6664
- const pagerReducer = function pagerReducer(state = initialPagerState, action) {
6665
- switch (action.type) {
6666
- case "ConfigureKeypad":
6667
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'configuration' does not exist on type '{ type: string; }'.
6668
- const {
6669
- keypadType
6670
- } = action.configuration;
6671
- const {
6672
- numPages
6673
- } = keypadForType[keypadType];
6674
- return _extends({}, state, {
6675
- numPages,
6676
- animateToPosition: false,
6677
- currentPage: getDefaultPage(numPages),
6678
- dx: 0
6679
- });
6680
- case "SetPageSize":
6681
- return _extends({}, state, {
6682
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'pageWidthPx' does not exist on type '{ type: string; }'.
6683
- pageWidthPx: action.pageWidthPx
6684
- });
6685
- case "PressKey":
6686
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
6687
- const keyConfig = KeyConfigs[action.key];
6688
-
6689
- // Reset the keypad page if the user performs a math operation.
6690
- if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
6691
- return pagerReducer(state, {
6692
- type: "ResetKeypadPage"
6693
- });
6694
- }
6695
- return state;
6696
- case "ResetKeypadPage":
6697
- return _extends({}, state, {
6698
- animateToPosition: true,
6699
- // We start at the right-most page.
6700
- currentPage: getDefaultPage(state.numPages),
6701
- dx: 0
6702
- });
6703
- case "PageKeypadRight":
6704
- const nextPage = Math.min(state.currentPage + 1, state.numPages - 1);
6705
- return _extends({}, state, {
6706
- animateToPosition: true,
6707
- currentPage: nextPage,
6708
- dx: 0
6709
- });
6710
- case "PageKeypadLeft":
6711
- const prevPage = Math.max(state.currentPage - 1, 0);
6712
- return _extends({}, state, {
6713
- animateToPosition: true,
6714
- currentPage: prevPage,
6715
- dx: 0
6677
+ const pagerReducer = function pagerReducer(state = initialPagerState, action) {
6678
+ switch (action.type) {
6679
+ case "ConfigureKeypad":
6680
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'configuration' does not exist on type '{ type: string; }'.
6681
+ const {
6682
+ keypadType
6683
+ } = action.configuration;
6684
+ const {
6685
+ numPages
6686
+ } = keypadForType[keypadType];
6687
+ return _extends({}, state, {
6688
+ numPages,
6689
+ animateToPosition: false,
6690
+ currentPage: getDefaultPage(numPages),
6691
+ dx: 0
6692
+ });
6693
+ case "SetPageSize":
6694
+ return _extends({}, state, {
6695
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'pageWidthPx' does not exist on type '{ type: string; }'.
6696
+ pageWidthPx: action.pageWidthPx
6697
+ });
6698
+ case "PressKey":
6699
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
6700
+ const keyConfig = KeyConfigs[action.key];
6701
+
6702
+ // Reset the keypad page if the user performs a math operation.
6703
+ if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
6704
+ return pagerReducer(state, {
6705
+ type: "ResetKeypadPage"
6716
6706
  });
6717
- case "OnSwipeChange":
6707
+ }
6708
+ return state;
6709
+ case "ResetKeypadPage":
6710
+ return _extends({}, state, {
6711
+ animateToPosition: true,
6712
+ // We start at the right-most page.
6713
+ currentPage: getDefaultPage(state.numPages),
6714
+ dx: 0
6715
+ });
6716
+ case "PageKeypadRight":
6717
+ const nextPage = Math.min(state.currentPage + 1, state.numPages - 1);
6718
+ return _extends({}, state, {
6719
+ animateToPosition: true,
6720
+ currentPage: nextPage,
6721
+ dx: 0
6722
+ });
6723
+ case "PageKeypadLeft":
6724
+ const prevPage = Math.max(state.currentPage - 1, 0);
6725
+ return _extends({}, state, {
6726
+ animateToPosition: true,
6727
+ currentPage: prevPage,
6728
+ dx: 0
6729
+ });
6730
+ case "OnSwipeChange":
6731
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
6732
+ state.velocityTracker.push(action.dx);
6733
+ return _extends({}, state, {
6734
+ animateToPosition: false,
6718
6735
  // @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
6719
- state.velocityTracker.push(action.dx);
6720
- return _extends({}, state, {
6721
- animateToPosition: false,
6722
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
6723
- dx: action.dx
6736
+ dx: action.dx
6737
+ });
6738
+ case "OnSwipeEnd":
6739
+ const {
6740
+ pageWidthPx,
6741
+ velocityTracker
6742
+ } = state;
6743
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
6744
+ const {
6745
+ dx
6746
+ } = action;
6747
+ const velocity = velocityTracker.getVelocity();
6748
+
6749
+ // NOTE(charlie): These will need refinement. The velocity comes
6750
+ // from Framer.
6751
+ const minFlingVelocity = 0.1;
6752
+ const minFlingDistance = 10;
6753
+ const shouldPageRight = dx < -pageWidthPx / 2 || velocity < -minFlingVelocity && dx < -minFlingDistance;
6754
+ const shouldPageLeft = dx > pageWidthPx / 2 || velocity > minFlingVelocity && dx > minFlingDistance;
6755
+ if (shouldPageRight) {
6756
+ return pagerReducer(state, {
6757
+ type: "PageKeypadRight"
6724
6758
  });
6725
- case "OnSwipeEnd":
6726
- const {
6727
- pageWidthPx,
6728
- velocityTracker
6729
- } = state;
6730
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
6731
- const {
6732
- dx
6733
- } = action;
6734
- const velocity = velocityTracker.getVelocity();
6735
-
6736
- // NOTE(charlie): These will need refinement. The velocity comes
6737
- // from Framer.
6738
- const minFlingVelocity = 0.1;
6739
- const minFlingDistance = 10;
6740
- const shouldPageRight = dx < -pageWidthPx / 2 || velocity < -minFlingVelocity && dx < -minFlingDistance;
6741
- const shouldPageLeft = dx > pageWidthPx / 2 || velocity > minFlingVelocity && dx > minFlingDistance;
6742
- if (shouldPageRight) {
6743
- return pagerReducer(state, {
6744
- type: "PageKeypadRight"
6745
- });
6746
- } else if (shouldPageLeft) {
6747
- return pagerReducer(state, {
6748
- type: "PageKeypadLeft"
6749
- });
6750
- }
6751
- return _extends({}, state, {
6752
- animateToPosition: true,
6753
- dx: 0
6759
+ } else if (shouldPageLeft) {
6760
+ return pagerReducer(state, {
6761
+ type: "PageKeypadLeft"
6754
6762
  });
6755
- default:
6756
- return state;
6757
- }
6758
- };
6763
+ }
6764
+ return _extends({}, state, {
6765
+ animateToPosition: true,
6766
+ dx: 0
6767
+ });
6768
+ default:
6769
+ return state;
6770
+ }
6771
+ };
6772
+
6773
+ const createStore = () => {
6774
+ // TODO(matthewc)[LC-752]: gestureReducer can't be moved from this file
6775
+ // because it depends on `store` being in scope (see note below)
6759
6776
  const createGestureManager = swipeEnabled => {
6760
6777
  return new GestureManager({
6761
6778
  swipeEnabled
@@ -6829,136 +6846,6 @@ const createStore = () => {
6829
6846
  return state;
6830
6847
  }
6831
6848
  };
6832
-
6833
- // Used to generate unique animation IDs for the echo animations. The actual
6834
- // values are irrelevant as long as they are unique.
6835
- let _lastAnimationId = 0;
6836
- const initialEchoState = {
6837
- echoes: []
6838
- };
6839
- const echoReducer = function echoReducer(state = initialEchoState, action) {
6840
- switch (action.type) {
6841
- case "PressKey":
6842
- const keyConfig = KeyConfigs[action.key];
6843
-
6844
- // Add in the echo animation if the user performs a math
6845
- // operation.
6846
- if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
6847
- return _extends({}, state, {
6848
- echoes: [...state.echoes, {
6849
- animationId: "" + _lastAnimationId++,
6850
- animationType: action.inPopover ? EchoAnimationTypes.LONG_FADE_ONLY : EchoAnimationTypes.FADE_ONLY,
6851
- borders: action.borders,
6852
- id: keyConfig.id,
6853
- initialBounds: action.initialBounds
6854
- }]
6855
- });
6856
- }
6857
- return state;
6858
- case "RemoveEcho":
6859
- const remainingEchoes = state.echoes.filter(echo => {
6860
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'animationId' does not exist on type 'never'.
6861
- return echo.animationId !== action.animationId;
6862
- });
6863
- return _extends({}, state, {
6864
- echoes: remainingEchoes
6865
- });
6866
- default:
6867
- return state;
6868
- }
6869
- };
6870
- const initialLayoutState = {
6871
- gridDimensions: {
6872
- numRows: keypadForType[defaultKeypadType].rows,
6873
- numColumns: keypadForType[defaultKeypadType].columns,
6874
- numMaxVisibleRows: keypadForType[defaultKeypadType].maxVisibleRows,
6875
- numPages: keypadForType[defaultKeypadType].numPages
6876
- },
6877
- buttonDimensions: {
6878
- widthPx: 48,
6879
- heightPx: 48
6880
- },
6881
- pageDimensions: {
6882
- pageWidthPx: 0,
6883
- pageHeightPx: 0
6884
- },
6885
- layoutMode: LayoutModes.FULLSCREEN,
6886
- paginationEnabled: false,
6887
- navigationPadEnabled: false
6888
- };
6889
-
6890
- /**
6891
- * Compute the additional layout state based on the provided page and grid
6892
- * dimensions.
6893
- */
6894
- const layoutParametersForDimensions = (pageDimensions, gridDimensions) => {
6895
- const {
6896
- pageWidthPx,
6897
- pageHeightPx
6898
- } = pageDimensions;
6899
-
6900
- // Determine the device type and orientation.
6901
- const deviceOrientation = pageWidthPx > pageHeightPx ? DeviceOrientations.LANDSCAPE : DeviceOrientations.PORTRAIT;
6902
- const deviceType = Math.min(pageWidthPx, pageHeightPx) > tabletCutoffPx ? DeviceTypes.TABLET : DeviceTypes.PHONE;
6903
-
6904
- // Using that information, make some decisions (or assumptions)
6905
- // about the resulting layout.
6906
- const navigationPadEnabled = deviceType === DeviceTypes.TABLET;
6907
- const paginationEnabled = deviceType === DeviceTypes.PHONE && deviceOrientation === DeviceOrientations.PORTRAIT;
6908
- const deviceInfo = {
6909
- deviceOrientation,
6910
- deviceType
6911
- };
6912
- const layoutOptions = {
6913
- navigationPadEnabled,
6914
- paginationEnabled,
6915
- // HACK(charlie): It's not great that we're making assumptions about
6916
- // the toolbar (which is rendered by webapp, and should always be
6917
- // visible and anchored to the bottom of the page for phone and
6918
- // tablet exercises). But this is primarily a heuristic (the goal is
6919
- // to preserve a 'good' amount of space between the top of the
6920
- // keypad and the top of the page) so we afford to have some margin
6921
- // of error.
6922
- toolbarEnabled: true
6923
- };
6924
- return _extends({}, computeLayoutParameters(gridDimensions, pageDimensions, deviceInfo, layoutOptions), {
6925
- // Pass along some of the layout information, so that other
6926
- // components in the heirarchy can adapt appropriately.
6927
- navigationPadEnabled,
6928
- paginationEnabled
6929
- });
6930
- };
6931
- const layoutReducer = function layoutReducer(state = initialLayoutState, action) {
6932
- switch (action.type) {
6933
- case "ConfigureKeypad":
6934
- const {
6935
- keypadType
6936
- } = action.configuration;
6937
- const gridDimensions = {
6938
- numRows: keypadForType[keypadType].rows,
6939
- numColumns: keypadForType[keypadType].columns,
6940
- numMaxVisibleRows: keypadForType[keypadType].maxVisibleRows,
6941
- numPages: keypadForType[keypadType].numPages
6942
- };
6943
- return _extends({}, state, layoutParametersForDimensions(state.pageDimensions, gridDimensions), {
6944
- gridDimensions
6945
- });
6946
- case "SetPageSize":
6947
- const {
6948
- pageWidthPx,
6949
- pageHeightPx
6950
- } = action;
6951
- const pageDimensions = {
6952
- pageWidthPx,
6953
- pageHeightPx
6954
- };
6955
- return _extends({}, state, layoutParametersForDimensions(pageDimensions, state.gridDimensions), {
6956
- pageDimensions
6957
- });
6958
- default:
6959
- return state;
6960
- }
6961
- };
6962
6849
  const reducer = Redux.combineReducers({
6963
6850
  input: inputReducer,
6964
6851
  keypad: keypadReducer,
@@ -7025,10 +6912,6 @@ class NavigationPad extends React.Component {
7025
6912
  })));
7026
6913
  }
7027
6914
  }
7028
- NavigationPad.propTypes = {
7029
- roundTopLeft: PropTypes.bool,
7030
- style: PropTypes.any
7031
- };
7032
6915
  const buttonSizePx = 48;
7033
6916
  const borderRadiusPx = 4;
7034
6917
  const borderWidthPx$1 = 1;
@@ -7087,11 +6970,12 @@ const {
7087
6970
  centered,
7088
6971
  fullWidth
7089
6972
  } = Styles;
7090
-
7091
6973
  // eslint-disable-next-line react/no-unsafe
7092
6974
  class KeypadContainer extends React.Component {
7093
6975
  constructor(...args) {
7094
6976
  super(...args);
6977
+ this._resizeTimeout = void 0;
6978
+ this.hasMounted = void 0;
7095
6979
  this.state = {
7096
6980
  hasBeenActivated: false,
7097
6981
  viewportWidth: "100vw"
@@ -7100,19 +6984,20 @@ class KeypadContainer extends React.Component {
7100
6984
  // Throttle the resize callbacks.
7101
6985
  // https://developer.mozilla.org/en-US/docs/Web/Events/resize
7102
6986
  if (this._resizeTimeout == null) {
7103
- this._resizeTimeout = setTimeout(() => {
6987
+ this._resizeTimeout = window.setTimeout(() => {
7104
6988
  this._resizeTimeout = null;
7105
6989
  this._onResize();
7106
6990
  }, 66);
7107
6991
  }
7108
6992
  };
7109
6993
  this._onResize = () => {
6994
+ var _this$props$onPageSiz, _this$props;
7110
6995
  // Whenever the page resizes, we need to force an update, as the button
7111
6996
  // heights and keypad width are computed based on horizontal space.
7112
6997
  this.setState({
7113
6998
  viewportWidth: window.innerWidth
7114
6999
  });
7115
- this.props.onPageSizeChange(window.innerWidth, window.innerHeight);
7000
+ (_this$props$onPageSiz = (_this$props = this.props).onPageSizeChange) == null ? void 0 : _this$props$onPageSiz.call(_this$props, window.innerWidth, window.innerHeight);
7116
7001
  };
7117
7002
  this.renderKeypad = () => {
7118
7003
  const {
@@ -7194,7 +7079,10 @@ class KeypadContainer extends React.Component {
7194
7079
  // NOTE(charlie): We render the transforms as pure inline styles to
7195
7080
  // avoid an Aphrodite bug in mobile Safari.
7196
7081
  // See: https://github.com/Khan/aphrodite/issues/68.
7197
- const dynamicStyle = _extends({}, active ? inlineStyles.active : inlineStyles.hidden, !active && !hasBeenActivated ? inlineStyles.invisible : {});
7082
+ let dynamicStyle = _extends({}, active ? inlineStyles.active : inlineStyles.hidden);
7083
+ if (!active && !hasBeenActivated) {
7084
+ dynamicStyle = _extends({}, dynamicStyle, inlineStyles.invisible);
7085
+ }
7198
7086
  const keypadContainerStyle = [row, centered, fullWidth, styles.keypadContainer, ...(Array.isArray(style) ? style : [style])];
7199
7087
  const keypadStyle = [row, styles.keypadBorder, layoutMode === LayoutModes.FULLSCREEN ? styles.fullscreen : styles.compact];
7200
7088
 
@@ -7221,19 +7109,6 @@ class KeypadContainer extends React.Component {
7221
7109
  }, this.renderKeypad())));
7222
7110
  }
7223
7111
  }
7224
- KeypadContainer.propTypes = {
7225
- active: PropTypes.bool,
7226
- extraKeys: PropTypes.arrayOf(keyIdPropType),
7227
- keypadType: PropTypes.oneOf(Object.keys(KeypadTypes)).isRequired,
7228
- layoutMode: PropTypes.oneOf(Object.keys(LayoutModes)).isRequired,
7229
- navigationPadEnabled: PropTypes.bool.isRequired,
7230
- onDismiss: PropTypes.func,
7231
- // A callback that should be triggered with the root React element on
7232
- // mount.
7233
- onElementMounted: PropTypes.func,
7234
- onPageSizeChange: PropTypes.func.isRequired,
7235
- style: PropTypes.any
7236
- };
7237
7112
  const keypadAnimationDurationMs = 300;
7238
7113
  const borderWidthPx = 1;
7239
7114
  const styles = StyleSheet.create({
@@ -7293,10 +7168,13 @@ const inlineStyles = {
7293
7168
  }
7294
7169
  };
7295
7170
  const mapStateToProps = state => {
7296
- return _extends({}, state.keypad, {
7171
+ return {
7172
+ extraKeys: state.keypad.extraKeys,
7173
+ keypadType: state.keypad.keypadType,
7174
+ active: state.keypad.active,
7297
7175
  layoutMode: state.layout.layoutMode,
7298
7176
  navigationPadEnabled: state.layout.navigationPadEnabled
7299
- });
7177
+ };
7300
7178
  };
7301
7179
  const mapDispatchToProps = dispatch => {
7302
7180
  return {
@@ -7309,7 +7187,6 @@ var KeypadContainer$1 = connect(mapStateToProps, mapDispatchToProps, null, {
7309
7187
  forwardRef: true
7310
7188
  })(KeypadContainer);
7311
7189
 
7312
- const _excluded = ["onElementMounted"];
7313
7190
  class ProvidedKeypad extends React.Component {
7314
7191
  constructor(...args) {
7315
7192
  super(...args);
@@ -7353,14 +7230,14 @@ class ProvidedKeypad extends React.Component {
7353
7230
  this.mounted = false;
7354
7231
  }
7355
7232
  render() {
7356
- const _this$props = this.props,
7357
- {
7358
- onElementMounted
7359
- } = _this$props,
7360
- rest = _objectWithoutPropertiesLoose(_this$props, _excluded);
7233
+ const {
7234
+ onElementMounted,
7235
+ onDismiss,
7236
+ style
7237
+ } = this.props;
7361
7238
  return /*#__PURE__*/React.createElement(Provider, {
7362
7239
  store: this.store
7363
- }, /*#__PURE__*/React.createElement(KeypadContainer$1, _extends({
7240
+ }, /*#__PURE__*/React.createElement(KeypadContainer$1, {
7364
7241
  onElementMounted: element => {
7365
7242
  // Append the dispatch methods that we want to expose
7366
7243
  // externally to the returned React element.
@@ -7373,10 +7250,12 @@ class ProvidedKeypad extends React.Component {
7373
7250
  getDOMNode: this.getDOMNode
7374
7251
  });
7375
7252
  onElementMounted && onElementMounted(elementWithDispatchMethods);
7376
- }
7377
- }, rest)));
7253
+ },
7254
+ onDismiss: onDismiss,
7255
+ style: style
7256
+ }));
7378
7257
  }
7379
7258
  }
7380
7259
 
7381
- export { CursorContexts, KeyConfigs, ProvidedKeypad as Keypad, MathInput as KeypadInput, KeypadTypes, keypadConfigurationPropType, keypadElementPropType };
7260
+ export { cursorContexts as CursorContexts, KeyConfigs, ProvidedKeypad as Keypad, MathInput as KeypadInput, KeypadTypes, keypadElementPropType };
7382
7261
  //# sourceMappingURL=index.js.map