@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.
- package/CHANGELOG.md +10 -0
- package/dist/actions/index.d.ts +31 -0
- package/dist/actions/index.js.flow +40 -0
- package/dist/components/compute-layout-parameters.d.ts +38 -0
- package/dist/components/compute-layout-parameters.js.flow +49 -0
- package/dist/components/corner-decal.d.ts +12 -0
- package/dist/components/corner-decal.js.flow +15 -0
- package/dist/components/echo-manager.d.ts +26 -0
- package/dist/components/echo-manager.js.flow +29 -0
- package/dist/components/empty-keypad-button.d.ts +13 -0
- package/dist/components/empty-keypad-button.js.flow +23 -0
- package/dist/components/expression-keypad.d.ts +22 -0
- package/dist/components/expression-keypad.js.flow +32 -0
- package/dist/components/fraction-keypad.d.ts +21 -0
- package/dist/components/fraction-keypad.js.flow +30 -0
- package/dist/components/gesture-manager.d.ts +74 -0
- package/dist/components/gesture-manager.js.flow +82 -0
- package/dist/components/gesture-state-machine.d.ts +105 -0
- package/dist/components/gesture-state-machine.js.flow +118 -0
- package/dist/components/icon.d.ts +15 -0
- package/dist/components/icon.js.flow +18 -0
- package/dist/components/input/__tests__/test-math-wrapper.d.ts +8 -0
- package/dist/components/input/__tests__/test-math-wrapper.js.flow +14 -0
- package/dist/components/input/cursor-handle.d.ts +1 -1
- package/dist/components/input/cursor-handle.js.flow +1 -1
- package/dist/components/input/drag-listener.d.ts +13 -0
- package/dist/components/input/drag-listener.js.flow +19 -0
- package/dist/components/input/math-input.d.ts +5 -4
- package/dist/components/input/math-input.js.flow +5 -4
- package/dist/components/input/math-wrapper.d.ts +110 -0
- package/dist/components/input/math-wrapper.js.flow +125 -0
- package/dist/components/input/scroll-into-view.d.ts +11 -0
- package/dist/components/input/scroll-into-view.js.flow +20 -0
- package/dist/components/keypad/button-assets.d.ts +4 -3
- package/dist/components/keypad/button-assets.js.flow +3 -3
- package/dist/components/keypad/button.d.ts +1 -2
- package/dist/components/keypad/button.js.flow +1 -2
- package/dist/components/keypad/keypad-page-items.d.ts +15 -10
- package/dist/components/keypad/keypad-page-items.js.flow +20 -10
- package/dist/components/keypad-button.d.ts +52 -0
- package/dist/components/keypad-button.js.flow +79 -0
- package/dist/components/keypad-container.d.ts +40 -0
- package/dist/components/keypad-container.js.flow +58 -0
- package/dist/components/keypad.d.ts +31 -0
- package/dist/components/keypad.js.flow +40 -0
- package/dist/components/many-keypad-button.d.ts +15 -0
- package/dist/components/many-keypad-button.js.flow +17 -0
- package/dist/components/math-icon.d.ts +16 -0
- package/dist/components/math-icon.js.flow +19 -0
- package/dist/components/multi-symbol-grid.d.ts +14 -0
- package/dist/components/multi-symbol-grid.js.flow +16 -0
- package/dist/components/multi-symbol-popover.d.ts +12 -0
- package/dist/components/multi-symbol-popover.js.flow +15 -0
- package/dist/components/navigation-pad.d.ts +14 -0
- package/dist/components/navigation-pad.js.flow +16 -0
- package/dist/components/node-manager.d.ts +53 -0
- package/dist/components/node-manager.js.flow +65 -0
- package/dist/components/popover-manager.d.ts +13 -0
- package/dist/components/popover-manager.js.flow +15 -0
- package/dist/components/popover-state-machine.d.ts +75 -0
- package/dist/components/popover-state-machine.js.flow +83 -0
- package/dist/components/provided-keypad.d.ts +8 -7
- package/dist/components/provided-keypad.js.flow +8 -7
- package/dist/components/styles.d.ts +6 -0
- package/dist/components/styles.js.flow +13 -0
- package/dist/components/svg-icon.d.ts +12 -0
- package/dist/components/svg-icon.js.flow +15 -0
- package/dist/components/tabbar/icons.d.ts +3 -2
- package/dist/components/tabbar/icons.js.flow +3 -2
- package/dist/components/tabbar/item.d.ts +1 -2
- package/dist/components/tabbar/item.js.flow +1 -2
- package/dist/components/tabbar/tabbar.d.ts +3 -3
- package/dist/components/tabbar/tabbar.js.flow +3 -3
- package/dist/components/text-icon.d.ts +13 -0
- package/dist/components/text-icon.js.flow +16 -0
- package/dist/components/touchable-keypad-button.d.ts +30 -0
- package/dist/components/touchable-keypad-button.js.flow +35 -0
- package/dist/components/two-page-keypad.d.ts +20 -0
- package/dist/components/two-page-keypad.js.flow +30 -0
- package/dist/components/velocity-tracker.d.ts +48 -0
- package/dist/components/velocity-tracker.js.flow +54 -0
- package/dist/es/index.js +938 -1059
- package/dist/es/index.js.map +1 -1
- package/dist/fake-react-native-web/text.d.ts +2 -1
- package/dist/fake-react-native-web/text.js.flow +2 -1
- package/dist/fake-react-native-web/view.d.ts +3 -2
- package/dist/fake-react-native-web/view.js.flow +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +988 -1089
- package/dist/index.js.flow +1 -4
- package/dist/index.js.map +1 -1
- package/dist/store/echo-reducer.d.ts +5 -0
- package/dist/store/echo-reducer.js.flow +14 -0
- package/dist/store/index.d.ts +46 -1
- package/dist/store/index.js.flow +64 -1
- package/dist/store/input-reducer.d.ts +7 -0
- package/dist/store/input-reducer.js.flow +16 -0
- package/dist/store/keypad-reducer.d.ts +9 -0
- package/dist/store/keypad-reducer.js.flow +18 -0
- package/dist/store/layout-reducer.d.ts +21 -0
- package/dist/store/layout-reducer.js.flow +30 -0
- package/dist/store/pager-reducer.d.ts +13 -0
- package/dist/store/pager-reducer.js.flow +22 -0
- package/dist/store/shared.d.ts +6 -0
- package/dist/store/shared.js.flow +13 -0
- package/dist/store/types.d.ts +57 -0
- package/dist/store/types.js.flow +63 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.js.flow +61 -0
- package/package.json +1 -1
- package/src/actions/{index.js → index.ts} +5 -5
- package/src/components/__tests__/{gesture-state-machine_test.js → gesture-state-machine.test.ts} +5 -1
- package/src/components/__tests__/{two-page-keypad_test.js → two-page-keypad.test.tsx} +0 -2
- package/src/components/{corner-decal.js → corner-decal.tsx} +6 -5
- package/src/components/{echo-manager.js → echo-manager.tsx} +29 -24
- package/src/components/{empty-keypad-button.js → empty-keypad-button.tsx} +17 -10
- package/src/components/{expression-keypad.js → expression-keypad.tsx} +27 -25
- package/src/components/{fraction-keypad.js → fraction-keypad.tsx} +21 -16
- package/src/components/{gesture-manager.js → gesture-manager.tsx} +10 -4
- package/src/components/{gesture-state-machine.js → gesture-state-machine.ts} +49 -3
- package/src/components/{icon.js → icon.tsx} +12 -14
- package/src/components/input/cursor-handle.tsx +1 -1
- package/src/components/input/{drag-listener.js → drag-listener.ts} +4 -0
- package/src/components/input/math-input.tsx +10 -9
- package/src/components/input/{math-wrapper.js → math-wrapper.ts} +10 -6
- package/src/components/input/{scroll-into-view.js → scroll-into-view.ts} +5 -15
- package/src/components/keypad/button-assets.tsx +4 -5
- package/src/components/keypad/button.tsx +1 -2
- package/src/components/keypad/index.tsx +1 -1
- package/src/components/keypad/keypad-page-items.tsx +33 -10
- package/src/components/{keypad-button.js → keypad-button.tsx} +42 -37
- package/src/components/{keypad-container.js → keypad-container.tsx} +41 -23
- package/src/components/{keypad.js → keypad.tsx} +31 -23
- package/src/components/{many-keypad-button.js → many-keypad-button.tsx} +8 -6
- package/src/components/{math-icon.js → math-icon.tsx} +7 -6
- package/src/components/{multi-symbol-grid.js → multi-symbol-grid.tsx} +8 -8
- package/src/components/{multi-symbol-popover.js → multi-symbol-popover.tsx} +5 -6
- package/src/components/{navigation-pad.js → navigation-pad.tsx} +7 -6
- package/src/components/{node-manager.js → node-manager.ts} +16 -4
- package/src/components/{popover-manager.js → popover-manager.tsx} +13 -16
- package/src/components/{popover-state-machine.js → popover-state-machine.ts} +21 -2
- package/src/components/prop-types.js +1 -67
- package/src/components/provided-keypad.tsx +14 -12
- package/src/components/{svg-icon.js → svg-icon.tsx} +5 -6
- package/src/components/tabbar/icons.tsx +4 -2
- package/src/components/tabbar/item.tsx +1 -3
- package/src/components/tabbar/{tabbar.stories.js → tabbar.stories.tsx} +10 -1
- package/src/components/tabbar/tabbar.tsx +3 -3
- package/src/components/{text-icon.js → text-icon.tsx} +7 -6
- package/src/components/{touchable-keypad-button.js → touchable-keypad-button.tsx} +19 -16
- package/src/components/{two-page-keypad.js → two-page-keypad.tsx} +13 -9
- package/src/components/{velocity-tracker.js → velocity-tracker.ts} +14 -4
- package/src/fake-react-native-web/text.tsx +2 -1
- package/src/fake-react-native-web/view.tsx +3 -2
- package/src/index.ts +1 -4
- package/src/store/echo-reducer.ts +58 -0
- package/src/store/index.ts +14 -425
- package/src/store/input-reducer.ts +55 -0
- package/src/store/keypad-reducer.ts +62 -0
- package/src/store/layout-reducer.ts +133 -0
- package/src/store/pager-reducer.ts +141 -0
- package/src/store/shared.ts +12 -0
- package/src/store/types.ts +65 -0
- package/src/types.ts +69 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/src/components/app.js +0 -73
- package/src/demo.js +0 -9
- package/src/native-app.js +0 -85
- /package/src/components/__tests__/{node-manager_test.js → node-manager.test.ts} +0 -0
- /package/src/components/{compute-layout-parameters.js → compute-layout-parameters.ts} +0 -0
- /package/src/components/input/__tests__/{context-tracking_test.js → context-tracking.test.ts} +0 -0
- /package/src/components/input/__tests__/{mathquill_test.js → mathquill.test.ts} +0 -0
- /package/src/components/input/__tests__/{test-math-wrapper.jsx → test-math-wrapper.ts} +0 -0
- /package/src/components/keypad/{button.stories.js → button.stories.tsx} +0 -0
- /package/src/components/{styles.js → styles.ts} +0 -0
- /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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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(
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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) =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
interface Props extends ReduxProps {
|
|
34
|
+
extraKeys?: ReadonlyArray<string>;
|
|
35
|
+
roundTopLeft: boolean;
|
|
36
|
+
roundTopRight: boolean;
|
|
37
|
+
}
|
|
42
38
|
|
|
43
|
-
|
|
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
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
roundTopLeft: PropTypes.bool,
|
|
27
|
-
roundTopRight: PropTypes.bool,
|
|
28
|
-
};
|
|
24
|
+
interface ReduxProps {
|
|
25
|
+
cursorContext?: CursorContext;
|
|
26
|
+
dynamicJumpOut: boolean;
|
|
27
|
+
}
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
464
|
+
const leafElements: ReadonlyArray<null | HTMLElement> = [];
|
|
464
465
|
|
|
465
466
|
// Contains only DOMNodes with child elements.
|
|
466
|
-
const nonLeafElements:
|
|
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
|
|
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
|
|
796
|
+
cursor.insLeftOf(command?.startNode);
|
|
793
797
|
cursor.startSelection();
|
|
794
798
|
|
|
795
799
|
if (grandparent[this.MQ.R] !== MQ_END) {
|