@khanacademy/math-input 6.0.2 → 7.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 +21 -0
- package/dist/components/input/math-input.d.ts +2 -2
- package/dist/components/input/math-input.js.flow +2 -2
- package/dist/components/keypad/index.d.ts +2 -26
- package/dist/components/keypad/index.js.flow +2 -31
- package/dist/components/keypad/keypad.d.ts +26 -0
- package/dist/components/keypad/keypad.js.flow +37 -0
- package/dist/components/keypad/mobile-keypad.d.ts +39 -0
- package/dist/components/keypad/mobile-keypad.js.flow +57 -0
- package/dist/components/keypad/shared-keys.d.ts +1 -1
- package/dist/components/keypad/shared-keys.js.flow +1 -1
- package/dist/components/keypad-legacy/index.d.ts +1 -0
- package/dist/components/keypad-legacy/index.js.flow +7 -0
- package/dist/components/keypad-legacy/provided-keypad.d.ts +2 -2
- package/dist/components/keypad-legacy/provided-keypad.js.flow +10 -2
- package/dist/components/keypad-legacy/touchable-keypad-button.d.ts +1 -1
- package/dist/components/keypad-legacy/touchable-keypad-button.js.flow +1 -1
- package/dist/components/keypad-legacy/two-page-keypad.d.ts +1 -1
- package/dist/components/keypad-legacy/two-page-keypad.js.flow +1 -1
- package/dist/components/tabbar/index.d.ts +2 -0
- package/dist/components/tabbar/index.js.flow +8 -0
- package/dist/es/index.js +306 -173
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +322 -187
- package/dist/index.js.flow +9 -9
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.js.flow +12 -0
- package/package.json +2 -2
- package/src/components/input/math-input.tsx +4 -5
- package/src/components/keypad/index.tsx +2 -173
- package/src/components/keypad/keypad.stories.tsx +2 -1
- package/src/components/keypad/keypad.tsx +171 -0
- package/src/components/keypad/mobile-keypad.tsx +165 -0
- package/src/components/keypad/shared-keys.tsx +1 -1
- package/src/components/keypad-legacy/index.ts +1 -0
- package/src/components/keypad-legacy/provided-keypad.tsx +7 -2
- package/src/components/keypad-legacy/two-page-keypad.tsx +3 -2
- package/src/components/prop-types.js +0 -1
- package/src/components/tabbar/index.ts +2 -0
- package/src/full-math-input.stories.tsx +78 -0
- package/src/index.ts +28 -9
- package/src/types.ts +11 -0
- package/tsconfig-build.tsbuildinfo +1 -1
- package/src/math-input.stories.tsx +0 -67
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ReactDOM from "react-dom";
|
|
1
2
|
import { CursorContext } from "./components/input/cursor-contexts";
|
|
2
3
|
import Key from "./data/keys";
|
|
3
4
|
import { BorderDirection, EchoAnimationType, IconType, KeyType, KeypadType } from "./enums";
|
|
@@ -62,4 +63,12 @@ export type LayoutProps = {
|
|
|
62
63
|
initialBounds: Bound;
|
|
63
64
|
};
|
|
64
65
|
export type ClickKeyCallback = (key: Key) => void;
|
|
66
|
+
export interface KeypadAPI {
|
|
67
|
+
activate: () => void;
|
|
68
|
+
dismiss: () => void;
|
|
69
|
+
configure: (configuration: KeypadConfiguration, cb: () => void) => void;
|
|
70
|
+
setCursor: (cursor: Cursor) => void;
|
|
71
|
+
setKeyHandler: (keyHandler: KeyHandler) => void;
|
|
72
|
+
getDOMNode: () => ReturnType<typeof ReactDOM.findDOMNode>;
|
|
73
|
+
}
|
|
65
74
|
export {};
|
package/dist/types.js.flow
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Flowgen v1.21.0
|
|
5
5
|
* @flow
|
|
6
6
|
*/
|
|
7
|
+
import ReactDOM from "react-dom";
|
|
7
8
|
import { CursorContext } from "./components/input/cursor-contexts";
|
|
8
9
|
import Key from "./data/keys";
|
|
9
10
|
import {
|
|
@@ -77,3 +78,14 @@ export type LayoutProps = {|
|
|
|
77
78
|
initialBounds: Bound,
|
|
78
79
|
|};
|
|
79
80
|
export type ClickKeyCallback = (key: Key) => void;
|
|
81
|
+
export interface KeypadAPI {
|
|
82
|
+
activate: () => void;
|
|
83
|
+
dismiss: () => void;
|
|
84
|
+
configure: (configuration: KeypadConfiguration, cb: () => void) => void;
|
|
85
|
+
setCursor: (cursor: Cursor) => void;
|
|
86
|
+
setKeyHandler: (keyHandler: KeyHandler) => void;
|
|
87
|
+
getDOMNode: () => $Call<
|
|
88
|
+
<R>((...args: any[]) => R) => R,
|
|
89
|
+
typeof ReactDOM.findDOMNode
|
|
90
|
+
>;
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Khan Academy's new expression editor for the mobile web.",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "
|
|
6
|
+
"version": "7.0.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"scripts": {},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@khanacademy/perseus-core": "0.0.2",
|
|
23
|
-
"mathquill": "git+https://git@github.com/Khan/mathquill.git#
|
|
23
|
+
"mathquill": "git+https://git@github.com/Khan/mathquill.git#32d9f351aaa68537170b3120a52e99b8def3a2c3",
|
|
24
24
|
"performance-now": "^0.2.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
@@ -12,19 +12,18 @@ import {
|
|
|
12
12
|
wonderBlocksBlue,
|
|
13
13
|
offBlack,
|
|
14
14
|
} from "../common-style";
|
|
15
|
-
import ProvidedKeypad from "../keypad-legacy/provided-keypad";
|
|
16
15
|
|
|
17
16
|
import CursorHandle from "./cursor-handle";
|
|
18
17
|
import DragListener from "./drag-listener";
|
|
19
18
|
import MathWrapper from "./math-wrapper";
|
|
20
19
|
import {scrollIntoView} from "./scroll-into-view";
|
|
21
20
|
|
|
22
|
-
import type {Cursor} from "../../types";
|
|
21
|
+
import type {Cursor, KeypadAPI} from "../../types";
|
|
23
22
|
|
|
24
23
|
const constrainingFrictionFactor = 0.8;
|
|
25
24
|
|
|
26
25
|
type Props = {
|
|
27
|
-
keypadElement
|
|
26
|
+
keypadElement?: KeypadAPI;
|
|
28
27
|
onBlur: () => void;
|
|
29
28
|
onChange: (value: string, callback: any) => void;
|
|
30
29
|
onFocus: () => void;
|
|
@@ -267,7 +266,7 @@ class MathInput extends React.Component<Props, State> {
|
|
|
267
266
|
/** Gets and cache they bounds of the keypadElement */
|
|
268
267
|
_getKeypadBounds: () => any = () => {
|
|
269
268
|
if (!this._keypadBounds) {
|
|
270
|
-
const node = this.props.keypadElement
|
|
269
|
+
const node = this.props.keypadElement?.getDOMNode();
|
|
271
270
|
this._cacheKeypadBounds(node);
|
|
272
271
|
}
|
|
273
272
|
return this._keypadBounds;
|
|
@@ -341,7 +340,7 @@ class MathInput extends React.Component<Props, State> {
|
|
|
341
340
|
focus: () => void = () => {
|
|
342
341
|
// Pass this component's handleKey method to the keypad so it can call
|
|
343
342
|
// it whenever it needs to trigger a keypress action.
|
|
344
|
-
this.props.keypadElement
|
|
343
|
+
this.props.keypadElement?.setKeyHandler((key) => {
|
|
345
344
|
const cursor = this.mathField.pressKey(key);
|
|
346
345
|
|
|
347
346
|
// Trigger an `onChange` if the value in the input changed, and hide
|
|
@@ -1,173 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {StyleSheet} from "aphrodite";
|
|
4
|
-
import * as React from "react";
|
|
5
|
-
import {useEffect} from "react";
|
|
6
|
-
|
|
7
|
-
import Key from "../../data/keys";
|
|
8
|
-
import {ClickKeyCallback} from "../../types";
|
|
9
|
-
import {CursorContext} from "../input/cursor-contexts";
|
|
10
|
-
import Tabbar from "../tabbar/tabbar";
|
|
11
|
-
import {TabbarItemType} from "../tabbar/types";
|
|
12
|
-
|
|
13
|
-
import ExtrasPage from "./keypad-pages/extras-page";
|
|
14
|
-
import GeometryPage from "./keypad-pages/geometry-page";
|
|
15
|
-
import NumbersPage from "./keypad-pages/numbers-page";
|
|
16
|
-
import OperatorsPage from "./keypad-pages/operators-page";
|
|
17
|
-
import SharedKeys from "./shared-keys";
|
|
18
|
-
|
|
19
|
-
import type {SendEventFn} from "@khanacademy/perseus-core";
|
|
20
|
-
|
|
21
|
-
export type Props = {
|
|
22
|
-
extraKeys: ReadonlyArray<Key>;
|
|
23
|
-
cursorContext?: typeof CursorContext[keyof typeof CursorContext];
|
|
24
|
-
showDismiss?: boolean;
|
|
25
|
-
|
|
26
|
-
multiplicationDot?: boolean;
|
|
27
|
-
divisionKey?: boolean;
|
|
28
|
-
|
|
29
|
-
trigonometry?: boolean;
|
|
30
|
-
preAlgebra?: boolean;
|
|
31
|
-
logarithms?: boolean;
|
|
32
|
-
basicRelations?: boolean;
|
|
33
|
-
advancedRelations?: boolean;
|
|
34
|
-
|
|
35
|
-
onClickKey: ClickKeyCallback;
|
|
36
|
-
sendEvent: SendEventFn;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const defaultProps = {
|
|
40
|
-
extraKeys: [],
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
function allPages(props: Props): ReadonlyArray<TabbarItemType> {
|
|
44
|
-
const pages: Array<TabbarItemType> = ["Numbers"];
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
// OperatorsButtonSets
|
|
48
|
-
props.preAlgebra ||
|
|
49
|
-
props.logarithms ||
|
|
50
|
-
props.basicRelations ||
|
|
51
|
-
props.advancedRelations
|
|
52
|
-
) {
|
|
53
|
-
pages.push("Operators");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (props.trigonometry) {
|
|
57
|
-
pages.push("Geometry");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (props.extraKeys?.length) {
|
|
61
|
-
pages.push("Extras");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return pages;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// The main (v2) Keypad. Use this component to present an accessible, onscreen
|
|
68
|
-
// keypad to learners for entering math expressions.
|
|
69
|
-
export default function Keypad(props: Props) {
|
|
70
|
-
const [selectedPage, setSelectedPage] =
|
|
71
|
-
React.useState<TabbarItemType>("Numbers");
|
|
72
|
-
const [isMounted, setIsMounted] = React.useState<boolean>(false);
|
|
73
|
-
|
|
74
|
-
const availablePages = allPages(props);
|
|
75
|
-
|
|
76
|
-
const {
|
|
77
|
-
onClickKey,
|
|
78
|
-
cursorContext,
|
|
79
|
-
extraKeys,
|
|
80
|
-
multiplicationDot,
|
|
81
|
-
divisionKey,
|
|
82
|
-
preAlgebra,
|
|
83
|
-
logarithms,
|
|
84
|
-
basicRelations,
|
|
85
|
-
advancedRelations,
|
|
86
|
-
showDismiss,
|
|
87
|
-
sendEvent,
|
|
88
|
-
} = props;
|
|
89
|
-
|
|
90
|
-
useEffect(() => {
|
|
91
|
-
if (!isMounted) {
|
|
92
|
-
sendEvent({
|
|
93
|
-
type: "math-input:keypad-opened",
|
|
94
|
-
payload: {virtualKeypadVersion: "MATH_INPUT_KEYPAD_V2"},
|
|
95
|
-
});
|
|
96
|
-
setIsMounted(true);
|
|
97
|
-
}
|
|
98
|
-
return () => {
|
|
99
|
-
if (isMounted) {
|
|
100
|
-
sendEvent({
|
|
101
|
-
type: "math-input:keypad-closed",
|
|
102
|
-
payload: {virtualKeypadVersion: "MATH_INPUT_KEYPAD_V2"},
|
|
103
|
-
});
|
|
104
|
-
setIsMounted(false);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
}, [sendEvent, isMounted]);
|
|
108
|
-
|
|
109
|
-
return (
|
|
110
|
-
<View>
|
|
111
|
-
<Tabbar
|
|
112
|
-
items={availablePages}
|
|
113
|
-
selectedItem={selectedPage}
|
|
114
|
-
onSelectItem={(tabbarItem: TabbarItemType) => {
|
|
115
|
-
setSelectedPage(tabbarItem);
|
|
116
|
-
}}
|
|
117
|
-
style={styles.tabbar}
|
|
118
|
-
onClickClose={
|
|
119
|
-
showDismiss ? () => onClickKey("DISMISS") : undefined
|
|
120
|
-
}
|
|
121
|
-
/>
|
|
122
|
-
|
|
123
|
-
<View
|
|
124
|
-
style={styles.grid}
|
|
125
|
-
role="grid"
|
|
126
|
-
tabIndex={0}
|
|
127
|
-
aria-label="Keypad"
|
|
128
|
-
>
|
|
129
|
-
{selectedPage === "Numbers" && (
|
|
130
|
-
<NumbersPage onClickKey={onClickKey} />
|
|
131
|
-
)}
|
|
132
|
-
{selectedPage === "Extras" && (
|
|
133
|
-
<ExtrasPage onClickKey={onClickKey} extraKeys={extraKeys} />
|
|
134
|
-
)}
|
|
135
|
-
{selectedPage === "Operators" && (
|
|
136
|
-
<OperatorsPage
|
|
137
|
-
onClickKey={onClickKey}
|
|
138
|
-
preAlgebra={preAlgebra}
|
|
139
|
-
logarithms={logarithms}
|
|
140
|
-
basicRelations={basicRelations}
|
|
141
|
-
advancedRelations={advancedRelations}
|
|
142
|
-
/>
|
|
143
|
-
)}
|
|
144
|
-
{selectedPage === "Geometry" && (
|
|
145
|
-
<GeometryPage onClickKey={onClickKey} />
|
|
146
|
-
)}
|
|
147
|
-
<SharedKeys
|
|
148
|
-
onClickKey={onClickKey}
|
|
149
|
-
cursorContext={cursorContext}
|
|
150
|
-
multiplicationDot={multiplicationDot}
|
|
151
|
-
divisionKey={divisionKey}
|
|
152
|
-
selectedPage={selectedPage}
|
|
153
|
-
/>
|
|
154
|
-
</View>
|
|
155
|
-
</View>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
Keypad.defaultProps = defaultProps;
|
|
160
|
-
|
|
161
|
-
const styles = StyleSheet.create({
|
|
162
|
-
tabbar: {
|
|
163
|
-
background: Color.white,
|
|
164
|
-
},
|
|
165
|
-
grid: {
|
|
166
|
-
display: "grid",
|
|
167
|
-
gridTemplateColumns: "repeat(6, 1fr)",
|
|
168
|
-
gridTemplateRows: "repeat(4, 1fr)",
|
|
169
|
-
backgroundColor: "#DBDCDD",
|
|
170
|
-
maxHeight: 200,
|
|
171
|
-
maxWidth: 300,
|
|
172
|
-
},
|
|
173
|
-
});
|
|
1
|
+
export {default} from "./keypad";
|
|
2
|
+
export {default as MobileKeypad} from "./mobile-keypad";
|
|
@@ -3,7 +3,7 @@ import {INITIAL_VIEWPORTS} from "@storybook/addon-viewport";
|
|
|
3
3
|
import {ComponentStory} from "@storybook/react";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
|
|
6
|
-
import Keypad, {Props as KeypadProps} from "./
|
|
6
|
+
import Keypad, {Props as KeypadProps} from "./keypad";
|
|
7
7
|
|
|
8
8
|
const opsPage = "Operators Page";
|
|
9
9
|
const numsPage = "Numbers Page";
|
|
@@ -26,6 +26,7 @@ export default {
|
|
|
26
26
|
multiplicationDot: false,
|
|
27
27
|
preAlgebra: false,
|
|
28
28
|
trigonometry: false,
|
|
29
|
+
sendEvent: () => {},
|
|
29
30
|
},
|
|
30
31
|
argTypes: {
|
|
31
32
|
advancedRelations: {
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import Color from "@khanacademy/wonder-blocks-color";
|
|
2
|
+
import {View} from "@khanacademy/wonder-blocks-core";
|
|
3
|
+
import {StyleSheet} from "aphrodite";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import {useEffect} from "react";
|
|
6
|
+
|
|
7
|
+
import Key from "../../data/keys";
|
|
8
|
+
import {ClickKeyCallback} from "../../types";
|
|
9
|
+
import {CursorContext} from "../input/cursor-contexts";
|
|
10
|
+
import Tabbar from "../tabbar";
|
|
11
|
+
|
|
12
|
+
import ExtrasPage from "./keypad-pages/extras-page";
|
|
13
|
+
import GeometryPage from "./keypad-pages/geometry-page";
|
|
14
|
+
import NumbersPage from "./keypad-pages/numbers-page";
|
|
15
|
+
import OperatorsPage from "./keypad-pages/operators-page";
|
|
16
|
+
import SharedKeys from "./shared-keys";
|
|
17
|
+
|
|
18
|
+
import type {TabbarItemType} from "../tabbar";
|
|
19
|
+
import type {SendEventFn} from "@khanacademy/perseus-core";
|
|
20
|
+
|
|
21
|
+
export type Props = {
|
|
22
|
+
extraKeys: ReadonlyArray<Key>;
|
|
23
|
+
cursorContext?: typeof CursorContext[keyof typeof CursorContext];
|
|
24
|
+
showDismiss?: boolean;
|
|
25
|
+
|
|
26
|
+
multiplicationDot?: boolean;
|
|
27
|
+
divisionKey?: boolean;
|
|
28
|
+
|
|
29
|
+
trigonometry?: boolean;
|
|
30
|
+
preAlgebra?: boolean;
|
|
31
|
+
logarithms?: boolean;
|
|
32
|
+
basicRelations?: boolean;
|
|
33
|
+
advancedRelations?: boolean;
|
|
34
|
+
|
|
35
|
+
onClickKey: ClickKeyCallback;
|
|
36
|
+
sendEvent: SendEventFn;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const defaultProps = {
|
|
40
|
+
extraKeys: [],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function allPages(props: Props): ReadonlyArray<TabbarItemType> {
|
|
44
|
+
const pages: Array<TabbarItemType> = ["Numbers"];
|
|
45
|
+
|
|
46
|
+
if (
|
|
47
|
+
// OperatorsButtonSets
|
|
48
|
+
props.preAlgebra ||
|
|
49
|
+
props.logarithms ||
|
|
50
|
+
props.basicRelations ||
|
|
51
|
+
props.advancedRelations
|
|
52
|
+
) {
|
|
53
|
+
pages.push("Operators");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (props.trigonometry) {
|
|
57
|
+
pages.push("Geometry");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (props.extraKeys?.length) {
|
|
61
|
+
pages.push("Extras");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return pages;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The main (v2) Keypad. Use this component to present an accessible, onscreen
|
|
68
|
+
// keypad to learners for entering math expressions.
|
|
69
|
+
export default function Keypad(props: Props) {
|
|
70
|
+
const [selectedPage, setSelectedPage] =
|
|
71
|
+
React.useState<TabbarItemType>("Numbers");
|
|
72
|
+
const [isMounted, setIsMounted] = React.useState<boolean>(false);
|
|
73
|
+
|
|
74
|
+
const availablePages = allPages(props);
|
|
75
|
+
|
|
76
|
+
const {
|
|
77
|
+
onClickKey,
|
|
78
|
+
cursorContext,
|
|
79
|
+
extraKeys,
|
|
80
|
+
multiplicationDot,
|
|
81
|
+
divisionKey,
|
|
82
|
+
preAlgebra,
|
|
83
|
+
logarithms,
|
|
84
|
+
basicRelations,
|
|
85
|
+
advancedRelations,
|
|
86
|
+
showDismiss,
|
|
87
|
+
sendEvent,
|
|
88
|
+
} = props;
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!isMounted) {
|
|
92
|
+
sendEvent({
|
|
93
|
+
type: "math-input:keypad-opened",
|
|
94
|
+
payload: {virtualKeypadVersion: "MATH_INPUT_KEYPAD_V2"},
|
|
95
|
+
});
|
|
96
|
+
setIsMounted(true);
|
|
97
|
+
}
|
|
98
|
+
return () => {
|
|
99
|
+
if (isMounted) {
|
|
100
|
+
sendEvent({
|
|
101
|
+
type: "math-input:keypad-closed",
|
|
102
|
+
payload: {virtualKeypadVersion: "MATH_INPUT_KEYPAD_V2"},
|
|
103
|
+
});
|
|
104
|
+
setIsMounted(false);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}, [sendEvent, isMounted]);
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<View>
|
|
111
|
+
<Tabbar
|
|
112
|
+
items={availablePages}
|
|
113
|
+
selectedItem={selectedPage}
|
|
114
|
+
onSelectItem={(tabbarItem: TabbarItemType) => {
|
|
115
|
+
setSelectedPage(tabbarItem);
|
|
116
|
+
}}
|
|
117
|
+
style={styles.tabbar}
|
|
118
|
+
onClickClose={
|
|
119
|
+
showDismiss ? () => onClickKey("DISMISS") : undefined
|
|
120
|
+
}
|
|
121
|
+
/>
|
|
122
|
+
|
|
123
|
+
<View
|
|
124
|
+
style={styles.grid}
|
|
125
|
+
role="grid"
|
|
126
|
+
tabIndex={0}
|
|
127
|
+
aria-label="Keypad"
|
|
128
|
+
>
|
|
129
|
+
{selectedPage === "Numbers" && (
|
|
130
|
+
<NumbersPage onClickKey={onClickKey} />
|
|
131
|
+
)}
|
|
132
|
+
{selectedPage === "Extras" && (
|
|
133
|
+
<ExtrasPage onClickKey={onClickKey} extraKeys={extraKeys} />
|
|
134
|
+
)}
|
|
135
|
+
{selectedPage === "Operators" && (
|
|
136
|
+
<OperatorsPage
|
|
137
|
+
onClickKey={onClickKey}
|
|
138
|
+
preAlgebra={preAlgebra}
|
|
139
|
+
logarithms={logarithms}
|
|
140
|
+
basicRelations={basicRelations}
|
|
141
|
+
advancedRelations={advancedRelations}
|
|
142
|
+
/>
|
|
143
|
+
)}
|
|
144
|
+
{selectedPage === "Geometry" && (
|
|
145
|
+
<GeometryPage onClickKey={onClickKey} />
|
|
146
|
+
)}
|
|
147
|
+
<SharedKeys
|
|
148
|
+
onClickKey={onClickKey}
|
|
149
|
+
cursorContext={cursorContext}
|
|
150
|
+
multiplicationDot={multiplicationDot}
|
|
151
|
+
divisionKey={divisionKey}
|
|
152
|
+
selectedPage={selectedPage}
|
|
153
|
+
/>
|
|
154
|
+
</View>
|
|
155
|
+
</View>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
Keypad.defaultProps = defaultProps;
|
|
160
|
+
|
|
161
|
+
const styles = StyleSheet.create({
|
|
162
|
+
tabbar: {
|
|
163
|
+
background: Color.white,
|
|
164
|
+
},
|
|
165
|
+
grid: {
|
|
166
|
+
display: "grid",
|
|
167
|
+
gridTemplateColumns: "repeat(6, 1fr)",
|
|
168
|
+
gridTemplateRows: "repeat(4, 1fr)",
|
|
169
|
+
backgroundColor: "#DBDCDD",
|
|
170
|
+
},
|
|
171
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
2
|
+
import {StyleSheet} from "aphrodite";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import ReactDOM from "react-dom";
|
|
5
|
+
|
|
6
|
+
import Key from "../../data/keys";
|
|
7
|
+
import {View} from "../../fake-react-native-web/index";
|
|
8
|
+
import {Cursor, KeypadConfiguration, KeyHandler, KeypadAPI} from "../../types";
|
|
9
|
+
|
|
10
|
+
import Keypad from "./index";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* This is the v2 equivalent of v1's ProvidedKeypad. It follows the same
|
|
14
|
+
* external API so that it can be hot-swapped with the v1 keypad and
|
|
15
|
+
* is responsible for connecting the keypad with MathInput and the Renderer.
|
|
16
|
+
*
|
|
17
|
+
* Ideally this strategy of attaching methods on the class component for
|
|
18
|
+
* other components to call will be replaced props/callbacks since React
|
|
19
|
+
* doesn't support this type of code anymore (functional components
|
|
20
|
+
* can't have methods attached to them).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
type Props = {
|
|
24
|
+
onElementMounted?: (arg1: any) => void;
|
|
25
|
+
onDismiss?: () => void;
|
|
26
|
+
style?: StyleType;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type State = {
|
|
30
|
+
active: boolean;
|
|
31
|
+
keypadConfig?: KeypadConfiguration;
|
|
32
|
+
keyHandler?: KeyHandler;
|
|
33
|
+
cursor?: Cursor;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
class MobileKeypad extends React.Component<Props, State> implements KeypadAPI {
|
|
37
|
+
hasMounted = false;
|
|
38
|
+
|
|
39
|
+
state: State = {
|
|
40
|
+
active: false,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
activate: () => void = () => {
|
|
44
|
+
this.setState({active: true});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
dismiss: () => void = () => {
|
|
48
|
+
this.setState({active: false}, () => {
|
|
49
|
+
this.props.onDismiss?.();
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
configure: (configuration: KeypadConfiguration, cb: () => void) => void = (
|
|
54
|
+
configuration,
|
|
55
|
+
cb,
|
|
56
|
+
) => {
|
|
57
|
+
this.setState({keypadConfig: configuration});
|
|
58
|
+
|
|
59
|
+
// TODO(matthewc)[LC-1080]: this was brought in from v1's ProvidedKeypad.
|
|
60
|
+
// We need to investigate whether we still need this.
|
|
61
|
+
// HACK(charlie): In Perseus, triggering a focus causes the keypad to
|
|
62
|
+
// animate into view and re-configure. We'd like to provide the option
|
|
63
|
+
// to re-render the re-configured keypad before animating it into view,
|
|
64
|
+
// to avoid jank in the animation. As such, we support passing a
|
|
65
|
+
// callback into `configureKeypad`. However, implementing this properly
|
|
66
|
+
// would require middleware, etc., so we just hack it on with
|
|
67
|
+
// `setTimeout` for now.
|
|
68
|
+
setTimeout(() => cb && cb());
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
setCursor: (cursor: Cursor) => void = (cursor) => {
|
|
72
|
+
this.setState({cursor});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
setKeyHandler: (keyHandler: KeyHandler) => void = (keyHandler) => {
|
|
76
|
+
this.setState({keyHandler});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
getDOMNode: () => ReturnType<typeof ReactDOM.findDOMNode> = () => {
|
|
80
|
+
return ReactDOM.findDOMNode(this);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
_handleClickKey(key: Key) {
|
|
84
|
+
if (key === "DISMISS") {
|
|
85
|
+
this.dismiss();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const cursor = this.state.keyHandler?.(key);
|
|
90
|
+
this.setState({cursor});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
render(): React.ReactNode {
|
|
94
|
+
const {active, cursor, keypadConfig} = this.state;
|
|
95
|
+
|
|
96
|
+
const containerStyle = [
|
|
97
|
+
styles.keypadContainer,
|
|
98
|
+
active ? styles.activeKeypadContainer : null,
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
const isExpression = keypadConfig?.keypadType === "EXPRESSION";
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<View
|
|
105
|
+
style={containerStyle}
|
|
106
|
+
ref={(element) => {
|
|
107
|
+
if (!this.hasMounted && element) {
|
|
108
|
+
// TODO(matthewc)[LC-1081]: clean up this weird
|
|
109
|
+
// object and type the onElementMounted callback
|
|
110
|
+
// Append the dispatch methods that we want to expose
|
|
111
|
+
// externally to the returned React element.
|
|
112
|
+
const elementWithDispatchMethods = {
|
|
113
|
+
...element,
|
|
114
|
+
activate: this.activate,
|
|
115
|
+
dismiss: this.dismiss,
|
|
116
|
+
configure: this.configure,
|
|
117
|
+
setCursor: this.setCursor,
|
|
118
|
+
setKeyHandler: this.setKeyHandler,
|
|
119
|
+
getDOMNode: this.getDOMNode,
|
|
120
|
+
} as const;
|
|
121
|
+
|
|
122
|
+
this.hasMounted = true;
|
|
123
|
+
this.props.onElementMounted?.(
|
|
124
|
+
elementWithDispatchMethods,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
<Keypad
|
|
130
|
+
// TODO(jeremy)
|
|
131
|
+
sendEvent={async () => {}}
|
|
132
|
+
extraKeys={keypadConfig?.extraKeys}
|
|
133
|
+
onClickKey={(key) => this._handleClickKey(key)}
|
|
134
|
+
cursorContext={cursor?.context}
|
|
135
|
+
multiplicationDot={isExpression}
|
|
136
|
+
divisionKey={isExpression}
|
|
137
|
+
trigonometry={isExpression}
|
|
138
|
+
preAlgebra={isExpression}
|
|
139
|
+
logarithms={isExpression}
|
|
140
|
+
basicRelations={isExpression}
|
|
141
|
+
advancedRelations={isExpression}
|
|
142
|
+
showDismiss
|
|
143
|
+
/>
|
|
144
|
+
</View>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const styles = StyleSheet.create({
|
|
150
|
+
keypadContainer: {
|
|
151
|
+
bottom: 0,
|
|
152
|
+
left: 0,
|
|
153
|
+
right: 0,
|
|
154
|
+
position: "fixed",
|
|
155
|
+
transition: `200ms ease-out`,
|
|
156
|
+
transitionProperty: "transform",
|
|
157
|
+
transform: "translate3d(0, 100%, 0)",
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
activeKeypadContainer: {
|
|
161
|
+
transform: "translate3d(0, 0, 0)",
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
export default MobileKeypad;
|
|
@@ -3,7 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import Keys from "../../data/key-configs";
|
|
4
4
|
import {ClickKeyCallback} from "../../types";
|
|
5
5
|
import {CursorContext} from "../input/cursor-contexts";
|
|
6
|
-
import {TabbarItemType} from "../tabbar
|
|
6
|
+
import {TabbarItemType} from "../tabbar";
|
|
7
7
|
|
|
8
8
|
import {KeypadButton} from "./keypad-button";
|
|
9
9
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from "./provided-keypad";
|
|
@@ -12,7 +12,12 @@ import {
|
|
|
12
12
|
} from "./store/actions";
|
|
13
13
|
import {createStore} from "./store/index";
|
|
14
14
|
|
|
15
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
Cursor,
|
|
17
|
+
KeypadConfiguration,
|
|
18
|
+
KeyHandler,
|
|
19
|
+
KeypadAPI,
|
|
20
|
+
} from "../../types";
|
|
16
21
|
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
17
22
|
|
|
18
23
|
type Props = {
|
|
@@ -21,7 +26,7 @@ type Props = {
|
|
|
21
26
|
style?: StyleType;
|
|
22
27
|
};
|
|
23
28
|
|
|
24
|
-
class ProvidedKeypad extends React.Component<Props> {
|
|
29
|
+
class ProvidedKeypad extends React.Component<Props> implements KeypadAPI {
|
|
25
30
|
store: any;
|
|
26
31
|
|
|
27
32
|
constructor(props) {
|
|
@@ -14,13 +14,14 @@ import {
|
|
|
14
14
|
innerBorderWidthPx,
|
|
15
15
|
offBlack16,
|
|
16
16
|
} from "../common-style";
|
|
17
|
-
import Tabbar from "../tabbar
|
|
18
|
-
import {TabbarItemType} from "../tabbar/types";
|
|
17
|
+
import Tabbar from "../tabbar";
|
|
19
18
|
|
|
20
19
|
import Keypad from "./keypad";
|
|
21
20
|
import {State as ReduxState} from "./store/types";
|
|
22
21
|
import Styles from "./styles";
|
|
23
22
|
|
|
23
|
+
import type {TabbarItemType} from "../tabbar";
|
|
24
|
+
|
|
24
25
|
const {column, row, fullWidth} = Styles;
|
|
25
26
|
|
|
26
27
|
interface ReduxProps {
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import PropTypes from "prop-types";
|
|
6
6
|
|
|
7
7
|
// NOTE(jared): This is no longer guaranteed to be React element
|
|
8
|
-
// NOTE(matthewc): only seems to be used in Perseus
|
|
9
8
|
export const keypadElementPropType = PropTypes.shape({
|
|
10
9
|
activate: PropTypes.func.isRequired,
|
|
11
10
|
dismiss: PropTypes.func.isRequired,
|