@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
@@ -2,7 +2,6 @@
2
2
  * A component that renders and animates the selection state effect effect.
3
3
  */
4
4
 
5
- import PropTypes from "prop-types";
6
5
  import * as React from "react";
7
6
  import {TransitionGroup, CSSTransition} from "react-transition-group";
8
7
 
@@ -10,23 +9,20 @@ import {KeyTypes, EchoAnimationTypes} from "../consts";
10
9
  import KeyConfigs from "../data/key-configs";
11
10
 
12
11
  import KeypadButton from "./keypad-button";
13
- import {
14
- echoPropType,
15
- bordersPropType,
16
- boundingBoxPropType,
17
- keyIdPropType,
18
- } from "./prop-types";
19
12
  import * as zIndexes from "./z-indexes";
20
13
 
21
- class Echo extends React.Component {
22
- static propTypes = {
23
- animationDurationMs: PropTypes.number.isRequired,
24
- borders: bordersPropType,
25
- id: keyIdPropType.isRequired,
26
- initialBounds: boundingBoxPropType.isRequired,
27
- onAnimationFinish: PropTypes.func.isRequired,
28
- };
14
+ import type {Key} from "../data/keys";
15
+ import type {Border, Bound} from "../types";
16
+
17
+ type EchoProps = {
18
+ animationDurationMs: number;
19
+ borders: Border;
20
+ id: Key;
21
+ initialBounds: Bound;
22
+ onAnimationFinish: () => void;
23
+ };
29
24
 
25
+ class Echo extends React.Component<EchoProps> {
30
26
  componentDidMount() {
31
27
  // NOTE(charlie): This is somewhat unfortunate, as the component is
32
28
  // encoding information about its own animation, of which it should be
@@ -41,7 +37,7 @@ class Echo extends React.Component {
41
37
  const {borders, id, initialBounds} = this.props;
42
38
  const {icon} = KeyConfigs[id];
43
39
 
44
- const containerStyle = {
40
+ const containerStyle: any = {
45
41
  zIndex: zIndexes.echo,
46
42
  position: "absolute",
47
43
  pointerEvents: "none",
@@ -56,7 +52,6 @@ class Echo extends React.Component {
56
52
  return (
57
53
  <div style={containerStyle}>
58
54
  <KeypadButton
59
- name={id}
60
55
  icon={icon}
61
56
  type={KeyTypes.ECHO}
62
57
  borders={borders}
@@ -66,12 +61,20 @@ class Echo extends React.Component {
66
61
  }
67
62
  }
68
63
 
69
- class EchoManager extends React.Component {
70
- static propTypes = {
71
- echoes: PropTypes.arrayOf(echoPropType),
72
- onAnimationFinish: PropTypes.func.isRequired,
73
- };
64
+ type EchoPropType = {
65
+ animationId: string;
66
+ animationType: keyof typeof EchoAnimationTypes;
67
+ borders: Border;
68
+ id: Key;
69
+ initialBounds: Bound;
70
+ };
71
+
72
+ type EchoManagerProps = {
73
+ echoes: ReadonlyArray<EchoPropType>;
74
+ onAnimationFinish?: (animationId: string) => void;
75
+ };
74
76
 
77
+ class EchoManager extends React.Component<EchoManagerProps> {
75
78
  _animationConfigForType = (animationType) => {
76
79
  // NOTE(charlie): These must be kept in sync with the transition
77
80
  // durations and classnames specified in echo.css.
@@ -95,7 +98,9 @@ class EchoManager extends React.Component {
95
98
  break;
96
99
 
97
100
  default:
98
- throw new Error("Invalid echo animation type:", animationType);
101
+ throw new Error(
102
+ `Invalid echo animation type: ${animationType}`,
103
+ );
99
104
  }
100
105
 
101
106
  return {
@@ -142,7 +147,7 @@ class EchoManager extends React.Component {
142
147
  animationDurationMs
143
148
  }
144
149
  onAnimationFinish={() =>
145
- onAnimationFinish(animationId)
150
+ onAnimationFinish?.(animationId)
146
151
  }
147
152
  {...echo}
148
153
  />
@@ -2,7 +2,6 @@
2
2
  * A keypad button containing no symbols and triggering no actions on click.
3
3
  */
4
4
 
5
- import PropTypes from "prop-types";
6
5
  import * as React from "react";
7
6
  import {connect} from "react-redux";
8
7
 
@@ -11,11 +10,13 @@ import KeyConfigs from "../data/key-configs";
11
10
  import GestureManager from "./gesture-manager";
12
11
  import KeypadButton from "./keypad-button";
13
12
 
14
- class EmptyKeypadButton extends React.Component {
15
- static propTypes = {
16
- gestureManager: PropTypes.instanceOf(GestureManager),
17
- };
13
+ import type {State} from "../store/types";
14
+
15
+ interface ReduxProps {
16
+ gestureManager: GestureManager;
17
+ }
18
18
 
19
+ class EmptyKeypadButton extends React.Component<ReduxProps> {
19
20
  render() {
20
21
  const {gestureManager, ...rest} = this.props;
21
22
 
@@ -26,10 +27,16 @@ class EmptyKeypadButton extends React.Component {
26
27
  // to focus them or trigger presses.
27
28
  return (
28
29
  <KeypadButton
29
- onTouchStart={(evt) => gestureManager.onTouchStart(evt)}
30
- onTouchEnd={(evt) => gestureManager.onTouchEnd(evt)}
31
- onTouchMove={(evt) => gestureManager.onTouchMove(evt)}
32
- onTouchCancel={(evt) => gestureManager.onTouchCancel(evt)}
30
+ onTouchStart={(evt: TouchEvent) =>
31
+ gestureManager.onTouchStart(evt)
32
+ }
33
+ onTouchEnd={(evt: TouchEvent) => gestureManager.onTouchEnd(evt)}
34
+ onTouchMove={(evt: TouchEvent) =>
35
+ gestureManager.onTouchMove(evt)
36
+ }
37
+ onTouchCancel={(evt: TouchEvent) =>
38
+ gestureManager.onTouchCancel(evt)
39
+ }
33
40
  {...KeyConfigs.NOOP}
34
41
  {...rest}
35
42
  />
@@ -37,7 +44,7 @@ class EmptyKeypadButton extends React.Component {
37
44
  }
38
45
  }
39
46
 
40
- const mapStateToProps = (state) => {
47
+ const mapStateToProps = (state: State): ReduxProps => {
41
48
  const {gestures} = state;
42
49
  return {
43
50
  gestureManager: gestures.gestureManager,
@@ -3,7 +3,6 @@
3
3
  */
4
4
 
5
5
  import {StyleSheet} from "aphrodite";
6
- import PropTypes from "prop-types";
7
6
  import * as React from "react";
8
7
  import {connect} from "react-redux";
9
8
 
@@ -14,34 +13,40 @@ import {View} from "../fake-react-native-web/index";
14
13
  import {valueGrey, controlGrey} from "./common-style";
15
14
  import * as CursorContexts from "./input/cursor-contexts";
16
15
  import ManyKeypadButton from "./many-keypad-button";
17
- import {cursorContextPropType, keyIdPropType} from "./prop-types";
18
16
  import Styles from "./styles";
19
17
  import TouchableKeypadButton from "./touchable-keypad-button";
20
18
  import TwoPageKeypad from "./two-page-keypad";
21
19
 
20
+ import type {State} from "../store/types";
21
+ import type {KeypadLayout} from "../types";
22
+ import type {CursorContext} from "./input/cursor-contexts";
23
+
22
24
  const {row, column, oneColumn, fullWidth, roundedTopLeft, roundedTopRight} =
23
25
  Styles;
24
26
 
25
- class ExpressionKeypad extends React.Component {
26
- static propTypes = {
27
- currentPage: PropTypes.number.isRequired,
28
- cursorContext: cursorContextPropType.isRequired,
29
- dynamicJumpOut: PropTypes.bool,
30
- extraKeys: PropTypes.arrayOf(keyIdPropType),
31
- roundTopLeft: PropTypes.bool,
32
- roundTopRight: PropTypes.bool,
33
- };
34
-
35
- static rows = 4;
36
- static columns = 5;
27
+ interface ReduxProps {
28
+ currentPage: number;
29
+ cursorContext?: CursorContext;
30
+ dynamicJumpOut: boolean;
31
+ }
37
32
 
38
- // Though we include an infinite-key popover in the bottom-left, it's
39
- // assumed that we don't need to accommodate cases in which that key
40
- // contains more than four children.
41
- static maxVisibleRows = 4;
33
+ interface Props extends ReduxProps {
34
+ extraKeys?: ReadonlyArray<string>;
35
+ roundTopLeft: boolean;
36
+ roundTopRight: boolean;
37
+ }
42
38
 
43
- static numPages = 2;
39
+ export const expressionKeypadLayout: KeypadLayout = {
40
+ rows: 4,
41
+ columns: 5,
42
+ numPages: 2,
43
+ // Since we include a two-key popover in the top-right, when the popover
44
+ // is visible, the keypad will expand to fill the equivalent of five
45
+ // rows vertically.
46
+ maxVisibleRows: 4,
47
+ };
44
48
 
49
+ class ExpressionKeypad extends React.Component<Props> {
45
50
  render() {
46
51
  const {
47
52
  currentPage,
@@ -109,10 +114,7 @@ class ExpressionKeypad extends React.Component {
109
114
  keyConfig={KeyConfigs.NUM_1}
110
115
  borders={BorderStyles.BOTTOM}
111
116
  />
112
- <ManyKeypadButton
113
- keys={extraKeys}
114
- borders={BorderStyles.NONE}
115
- />
117
+ <ManyKeypadButton keys={extraKeys} />
116
118
  </View>
117
119
  <View style={[column, oneColumn]}>
118
120
  <TouchableKeypadButton
@@ -310,10 +312,10 @@ const styles = StyleSheet.create({
310
312
  },
311
313
  });
312
314
 
313
- const mapStateToProps = (state) => {
315
+ const mapStateToProps = (state: State): ReduxProps => {
314
316
  return {
315
317
  currentPage: state.pager.currentPage,
316
- cursorContext: state.input.cursor.context,
318
+ cursorContext: state.input.cursor?.context,
317
319
  dynamicJumpOut: !state.layout.navigationPadEnabled,
318
320
  };
319
321
  };
@@ -3,7 +3,6 @@
3
3
  * with fractions, decimals, and percents.
4
4
  */
5
5
 
6
- import PropTypes from "prop-types";
7
6
  import * as React from "react";
8
7
  import {connect} from "react-redux";
9
8
 
@@ -13,30 +12,36 @@ import {View} from "../fake-react-native-web/index";
13
12
 
14
13
  import * as CursorContexts from "./input/cursor-contexts";
15
14
  import Keypad from "./keypad";
16
- import {cursorContextPropType} from "./prop-types";
17
15
  import Styles from "./styles";
18
16
  import TouchableKeypadButton from "./touchable-keypad-button";
19
17
 
18
+ import type {State} from "../store/types";
19
+ import type {KeypadLayout} from "../types";
20
+ import type {CursorContext} from "./input/cursor-contexts";
21
+
20
22
  const {row, roundedTopLeft, roundedTopRight} = Styles;
21
23
 
22
- class FractionKeypad extends React.Component {
23
- static propTypes = {
24
- cursorContext: cursorContextPropType.isRequired,
25
- dynamicJumpOut: PropTypes.bool,
26
- roundTopLeft: PropTypes.bool,
27
- roundTopRight: PropTypes.bool,
28
- };
24
+ interface ReduxProps {
25
+ cursorContext?: CursorContext;
26
+ dynamicJumpOut: boolean;
27
+ }
29
28
 
30
- static rows = 4;
31
- static columns = 4;
29
+ interface Props extends ReduxProps {
30
+ roundTopLeft: boolean;
31
+ roundTopRight: boolean;
32
+ }
32
33
 
34
+ export const fractionKeypadLayout: KeypadLayout = {
35
+ rows: 4,
36
+ columns: 4,
37
+ numPages: 1,
33
38
  // Since we include a two-key popover in the top-right, when the popover
34
39
  // is visible, the keypad will expand to fill the equivalent of five
35
40
  // rows vertically.
36
- static maxVisibleRows = 5;
37
-
38
- static numPages = 1;
41
+ maxVisibleRows: 5,
42
+ };
39
43
 
44
+ class FractionKeypad extends React.Component<Props> {
40
45
  render() {
41
46
  const {cursorContext, dynamicJumpOut, roundTopLeft, roundTopRight} =
42
47
  this.props;
@@ -164,9 +169,9 @@ class FractionKeypad extends React.Component {
164
169
  }
165
170
  }
166
171
 
167
- const mapStateToProps = (state) => {
172
+ const mapStateToProps = (state: State): ReduxProps => {
168
173
  return {
169
- cursorContext: state.input.cursor.context,
174
+ cursorContext: state.input.cursor?.context,
170
175
  dynamicJumpOut: !state.layout.navigationPadEnabled,
171
176
  };
172
177
  };
@@ -13,6 +13,12 @@ const coordsForEvent = (evt) => {
13
13
  };
14
14
 
15
15
  class GestureManager {
16
+ swipeEnabled: boolean;
17
+ trackEvents: boolean;
18
+ nodeManager: NodeManager;
19
+ popoverStateMachine: PopoverStateMachine;
20
+ gestureStateMachine: GestureStateMachine;
21
+
16
22
  constructor(options, handlers, disabledSwipeKeys, multiPressableKeys) {
17
23
  const {swipeEnabled} = options;
18
24
 
@@ -94,7 +100,7 @@ class GestureManager {
94
100
  * @param {string} id - the identifier of the DOM node in which the touch
95
101
  * occurred
96
102
  */
97
- onTouchStart(evt, id) {
103
+ onTouchStart(evt: TouchEvent, id?) {
98
104
  if (!this.trackEvents) {
99
105
  return;
100
106
  }
@@ -124,7 +130,7 @@ class GestureManager {
124
130
  *
125
131
  * @param {TouchEvent} evt - the raw touch event from the browser
126
132
  */
127
- onTouchMove(evt) {
133
+ onTouchMove(evt: TouchEvent) {
128
134
  if (!this.trackEvents) {
129
135
  return;
130
136
  }
@@ -148,7 +154,7 @@ class GestureManager {
148
154
  *
149
155
  * @param {TouchEvent} evt - the raw touch event from the browser
150
156
  */
151
- onTouchEnd(evt) {
157
+ onTouchEnd(evt: TouchEvent) {
152
158
  if (!this.trackEvents) {
153
159
  return;
154
160
  }
@@ -169,7 +175,7 @@ class GestureManager {
169
175
  *
170
176
  * @param {TouchEvent} evt - the raw touch event from the browser
171
177
  */
172
- onTouchCancel(evt) {
178
+ onTouchCancel(evt: TouchEvent) {
173
179
  if (!this.trackEvents) {
174
180
  return;
175
181
  }
@@ -1,3 +1,5 @@
1
+ import type {Key} from "../data/keys";
2
+
1
3
  /**
2
4
  * The state machine that backs our gesture system. In particular, this state
3
5
  * machine manages the interplay between focuses, touch ups, and swiping.
@@ -6,17 +8,61 @@
6
8
  * multi-touch interactions, tracking gesture state on a per-touch basis.
7
9
  */
8
10
 
9
- const defaults = {
11
+ // exported for tests
12
+ export type Handlers = {
13
+ onFocus: (id: string) => void;
14
+ onBlur: () => void;
15
+ onTrigger: (id: string) => void;
16
+ onLongPress: (id: string) => void;
17
+ onSwipeChange: (x: number) => void;
18
+ onSwipeEnd: (x: number) => void;
19
+ onTouchEnd: (id: string) => void;
20
+ };
21
+
22
+ type Options = {
23
+ longPressWaitTimeMs: number;
24
+ swipeThresholdPx: number;
25
+ holdIntervalMs: number;
26
+ };
27
+
28
+ type TouchState = {
29
+ activeNodeId: Key;
30
+ pressAndHoldIntervalId: number | null;
31
+ longPressTimeoutId: number | null;
32
+ swipeLocked: boolean;
33
+ startX: number;
34
+ };
35
+
36
+ type TouchStateMap = Record<Key, TouchState>;
37
+
38
+ type SwipeState = {
39
+ touchId: Key;
40
+ startX: number;
41
+ };
42
+
43
+ const defaultOptions: Options = {
10
44
  longPressWaitTimeMs: 50,
11
45
  swipeThresholdPx: 20,
12
46
  holdIntervalMs: 250,
13
47
  };
14
48
 
15
49
  class GestureStateMachine {
16
- constructor(handlers, options, swipeDisabledNodeIds, multiPressableKeys) {
50
+ handlers: Handlers;
51
+ options: Options;
52
+ swipeDisabledNodeIds: Partial<[Key]>;
53
+ multiPressableKeys: Partial<[Key]>;
54
+ touchState: Partial<TouchStateMap>;
55
+ swipeState: SwipeState | null;
56
+
57
+ constructor(
58
+ handlers: Handlers,
59
+ options: Partial<Options>,
60
+ swipeDisabledNodeIds?: [Key],
61
+ multiPressableKeys?: [Key],
62
+ ) {
17
63
  this.handlers = handlers;
18
64
  this.options = {
19
- ...defaults,
65
+ ...defaultOptions,
20
66
  ...options,
21
67
  };
22
68
  this.swipeDisabledNodeIds = swipeDisabledNodeIds || [];
@@ -3,34 +3,32 @@
3
3
  */
4
4
 
5
5
  import {StyleSheet} from "aphrodite";
6
- import PropTypes from "prop-types";
7
6
  import * as React from "react";
8
7
 
9
8
  import {IconTypes} from "../consts";
10
9
 
11
10
  import {offBlack} from "./common-style";
12
11
  import MathIcon from "./math-icon";
13
- import {iconPropType} from "./prop-types";
14
12
  import SvgIcon from "./svg-icon";
15
13
  import TextIcon from "./text-icon";
16
14
 
15
+ import type {Icon as IconPropType} from "../types";
16
+ import type {StyleType} from "@khanacademy/wonder-blocks-core";
17
+
17
18
  const focusedColor = "#FFF";
18
19
  const unfocusedColor = offBlack;
19
20
 
20
- class Icon extends React.PureComponent {
21
- static propTypes = {
22
- focused: PropTypes.bool,
23
- icon: iconPropType.isRequired,
24
- // An Aphrodite style object, or an array of Aphrodite style objects.
25
- // Note that custom styles will only be applied to text and math icons
26
- // (and not SVG icons).
27
- style: PropTypes.any,
28
- };
21
+ type Props = {
22
+ focused: boolean;
23
+ icon: IconPropType;
24
+ style?: StyleType;
25
+ };
29
26
 
27
+ class Icon extends React.PureComponent<Props> {
30
28
  render() {
31
29
  const {focused, icon, style} = this.props;
32
30
 
33
- const styleWithFocus = [
31
+ const styleWithFocus: StyleType = [
34
32
  focused ? styles.focused : styles.unfocused,
35
33
  ...(Array.isArray(style) ? style : [style]),
36
34
  ];
@@ -55,9 +53,9 @@ class Icon extends React.PureComponent {
55
53
  return (
56
54
  <TextIcon character={icon.data} style={styleWithFocus} />
57
55
  );
56
+ default:
57
+ throw new Error("No icon or symbol provided");
58
58
  }
59
-
60
- throw new Error("No icon or symbol provided");
61
59
  }
62
60
  }
63
61
 
@@ -18,7 +18,7 @@ const cursorHeightPx = cursorHandleDistanceMultiplier * (cursorRadiusPx * 4);
18
18
  const cursorWidthPx = 4 * cursorRadiusPx;
19
19
 
20
20
  type Props = {
21
- animateIntoPosition: boolean | null | undefined;
21
+ animateIntoPosition: boolean;
22
22
  onTouchCancel: (arg1: React.TouchEvent<HTMLSpanElement>) => void;
23
23
  onTouchEnd: (arg1: React.TouchEvent<HTMLSpanElement>) => void;
24
24
  onTouchMove: (arg1: React.TouchEvent<HTMLSpanElement>) => void;
@@ -9,6 +9,10 @@
9
9
  const touchSlopPx = 8;
10
10
 
11
11
  class DragListener {
12
+ _scrollListener: () => void;
13
+ _moveListener: (evt: TouchEvent) => void;
14
+ _endAndCancelListener: (evt: TouchEvent) => void;
15
+
12
16
  constructor(onDrag, initialEvent) {
13
17
  // We detect drags in two ways. First, by listening for the window
14
18
  // scroll event (we consider any legitimate scroll to be a drag).
@@ -13,18 +13,21 @@ import {
13
13
  wonderBlocksBlue,
14
14
  offBlack,
15
15
  } from "../common-style";
16
+ import ProvidedKeypad from "../provided-keypad";
16
17
 
17
18
  import CursorHandle from "./cursor-handle";
18
19
  import DragListener from "./drag-listener";
19
20
  import MathWrapper from "./math-wrapper";
20
21
  import {scrollIntoView} from "./scroll-into-view";
21
22
 
23
+ import type {Cursor} from "../../types";
24
+
22
25
  const constrainingFrictionFactor = 0.8;
23
26
 
24
27
  type Props = {
25
- keypadElement: any;
28
+ keypadElement: ProvidedKeypad;
26
29
  onBlur: () => void;
27
- onChange: any;
30
+ onChange: (value: string, callback: any) => void;
28
31
  onFocus: () => void;
29
32
  style: any;
30
33
  value: string;
@@ -36,7 +39,7 @@ type DefaultProps = {
36
39
  };
37
40
 
38
41
  type HandleState = {
39
- animateIntoPosition?: boolean | null | undefined;
42
+ animateIntoPosition?: boolean;
40
43
  visible: boolean;
41
44
  x?: number;
42
45
  y?: number;
@@ -89,7 +92,7 @@ class MathInput extends React.Component<Props, State> {
89
92
  this._mathContainer,
90
93
  {},
91
94
  {
92
- onCursorMove: (cursor) => {
95
+ onCursorMove: (cursor: Cursor) => {
93
96
  // TODO(charlie): It's not great that there is so much coupling
94
97
  // between this keypad and the input behavior. We should wrap
95
98
  // this `MathInput` component in an intermediary component
@@ -275,9 +278,7 @@ class MathInput extends React.Component<Props, State> {
275
278
  return this._keypadBounds;
276
279
  };
277
280
 
278
- _updateCursorHandle: (arg1?: boolean | null | undefined) => void = (
279
- animateIntoPosition,
280
- ) => {
281
+ _updateCursorHandle: (arg1?: boolean) => void = (animateIntoPosition) => {
281
282
  const containerBounds = this._container.getBoundingClientRect();
282
283
  const cursor: any = this._container.querySelector(".mq-cursor");
283
284
  const cursorBounds = cursor.getBoundingClientRect();
@@ -460,10 +461,10 @@ class MathInput extends React.Component<Props, State> {
460
461
 
461
462
  // Contains only DOMNodes without child elements. These should
462
463
  // contain some amount of text though.
463
- const leafElements: Array<null | HTMLElement> = [];
464
+ const leafElements: ReadonlyArray<null | HTMLElement> = [];
464
465
 
465
466
  // Contains only DOMNodes with child elements.
466
- const nonLeafElements: Array<null | HTMLElement> = [];
467
+ const nonLeafElements: ReadonlyArray<null | HTMLElement> = [];
467
468
 
468
469
  let max = 0;
469
470
  const counts: {
@@ -119,6 +119,10 @@ const KeysForJumpContext = {
119
119
  };
120
120
 
121
121
  class MathWrapper {
122
+ MQ: any; // MathQuill interface
123
+ mathField: any; // MathQuill input
124
+ callbacks: any;
125
+
122
126
  constructor(element, options = {}, callbacks = {}) {
123
127
  this.MQ = MathQuill.getInterface(2);
124
128
  this.mathField = this.MQ.MathField(element, {
@@ -150,7 +154,7 @@ class MathWrapper {
150
154
  controller.blurred = true;
151
155
  }
152
156
 
153
- _writeNormalFunction(name) {
157
+ _writeNormalFunction(name: string) {
154
158
  this.mathField.write(`\\${name}\\left(\\right)`);
155
159
  this.mathField.keystroke("Left");
156
160
  }
@@ -161,7 +165,7 @@ class MathWrapper {
161
165
  * @param {Key} key - an enum representing the key that was pressed
162
166
  * @returns {object} a cursor object, consisting of a cursor context
163
167
  */
164
- pressKey(key) {
168
+ pressKey(key: string) {
165
169
  const cursor = this.mathField.__controller.cursor;
166
170
 
167
171
  if (key in KeyActions) {
@@ -211,7 +215,7 @@ class MathWrapper {
211
215
  this._handleBackspace(cursor);
212
216
  } else if (key === Keys.LEFT) {
213
217
  this._handleLeftArrow(cursor);
214
- } else if (key === Keys.RIGHT || key === Keys.JUMP_OUT) {
218
+ } else if (key === Keys.RIGHT) {
215
219
  this._handleRightArrow(cursor);
216
220
  } else if (/^[a-zA-Z]$/.test(key)) {
217
221
  this.mathField[WRITE](key);
@@ -246,7 +250,7 @@ class MathWrapper {
246
250
  * to determine on which side of the node the cursor
247
251
  * should be placed
248
252
  */
249
- setCursorPosition(x, y, hitNode) {
253
+ setCursorPosition(x: number, y: number, hitNode: HTMLElement) {
250
254
  const el = hitNode || document.elementFromPoint(x, y);
251
255
 
252
256
  if (el) {
@@ -295,7 +299,7 @@ class MathWrapper {
295
299
  return this.mathField.latex();
296
300
  }
297
301
 
298
- setContent(latex) {
302
+ setContent(latex: string) {
299
303
  this.mathField.latex(latex);
300
304
  }
301
305
 
@@ -789,7 +793,7 @@ class MathWrapper {
789
793
  const grandparent = cursor.parent.parent;
790
794
  const command = this._maybeFindCommandBeforeParens(grandparent);
791
795
 
792
- cursor.insLeftOf(command.startNode);
796
+ cursor.insLeftOf(command?.startNode);
793
797
  cursor.startSelection();
794
798
 
795
799
  if (grandparent[this.MQ.R] !== MQ_END) {