@khanacademy/math-input 15.1.0 → 16.1.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 +24 -0
- package/dist/components/aphrodite-css-transition-group/util.d.ts +2 -1
- package/dist/components/input/math-wrapper.d.ts +1 -1
- package/dist/components/keypad/index.d.ts +1 -1
- package/dist/components/keypad/mobile-keypad-internals.d.ts +49 -0
- package/dist/components/keypad/mobile-keypad.d.ts +4 -48
- package/dist/data/key-configs.d.ts +1 -1
- package/dist/es/index.js +171 -4744
- package/dist/es/index.js.map +1 -1
- package/dist/fake-react-native-web/view.d.ts +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +168 -4722
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/__tests__/integration.test.tsx +2 -3
- package/src/components/input/math-input.tsx +1 -0
- package/src/components/input/math-wrapper.ts +2 -2
- package/src/components/keypad/__tests__/mobile-keypad.test.tsx +8 -8
- package/src/components/keypad/index.tsx +1 -1
- package/src/components/keypad/mobile-keypad-internals.tsx +240 -0
- package/src/components/keypad/mobile-keypad.tsx +21 -234
- package/src/data/key-configs.ts +1 -1
- package/src/full-mobile-input.stories.tsx +0 -1
- package/src/index.ts +1 -1
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/components/keypad-legacy/compute-layout-parameters.d.ts +0 -28
- package/dist/components/keypad-legacy/corner-decal.d.ts +0 -12
- package/dist/components/keypad-legacy/echo-manager.d.ts +0 -17
- package/dist/components/keypad-legacy/empty-keypad-button.d.ts +0 -13
- package/dist/components/keypad-legacy/expression-keypad.d.ts +0 -21
- package/dist/components/keypad-legacy/fraction-keypad.d.ts +0 -21
- package/dist/components/keypad-legacy/gesture-manager.d.ts +0 -86
- package/dist/components/keypad-legacy/gesture-state-machine.d.ts +0 -105
- package/dist/components/keypad-legacy/icon.d.ts +0 -15
- package/dist/components/keypad-legacy/index.d.ts +0 -1
- package/dist/components/keypad-legacy/keypad-button.d.ts +0 -53
- package/dist/components/keypad-legacy/keypad-container.d.ts +0 -41
- package/dist/components/keypad-legacy/keypad.d.ts +0 -31
- package/dist/components/keypad-legacy/many-keypad-button.d.ts +0 -15
- package/dist/components/keypad-legacy/math-icon.d.ts +0 -16
- package/dist/components/keypad-legacy/multi-symbol-grid.d.ts +0 -14
- package/dist/components/keypad-legacy/multi-symbol-popover.d.ts +0 -12
- package/dist/components/keypad-legacy/navigation-pad.d.ts +0 -14
- package/dist/components/keypad-legacy/node-manager.d.ts +0 -49
- package/dist/components/keypad-legacy/popover-manager.d.ts +0 -13
- package/dist/components/keypad-legacy/popover-state-machine.d.ts +0 -68
- package/dist/components/keypad-legacy/provided-keypad.d.ts +0 -28
- package/dist/components/keypad-legacy/store/actions.d.ts +0 -55
- package/dist/components/keypad-legacy/store/echo-reducer.d.ts +0 -4
- package/dist/components/keypad-legacy/store/index.d.ts +0 -9
- package/dist/components/keypad-legacy/store/input-reducer.d.ts +0 -4
- package/dist/components/keypad-legacy/store/keypad-reducer.d.ts +0 -4
- package/dist/components/keypad-legacy/store/layout-reducer.d.ts +0 -4
- package/dist/components/keypad-legacy/store/shared.d.ts +0 -7
- package/dist/components/keypad-legacy/store/types.d.ts +0 -47
- package/dist/components/keypad-legacy/styles.d.ts +0 -5
- package/dist/components/keypad-legacy/svg-icon.d.ts +0 -12
- package/dist/components/keypad-legacy/text-icon.d.ts +0 -13
- package/dist/components/keypad-legacy/touchable-keypad-button.d.ts +0 -37
- package/dist/components/keypad-legacy/two-page-keypad.d.ts +0 -21
- package/dist/components/keypad-legacy/z-indexes.d.ts +0 -7
- package/dist/components/keypad-switch.d.ts +0 -12
- package/src/components/keypad-legacy/__tests__/gesture-state-machine.test.ts +0 -441
- package/src/components/keypad-legacy/__tests__/node-manager.test.ts +0 -89
- package/src/components/keypad-legacy/compute-layout-parameters.ts +0 -205
- package/src/components/keypad-legacy/corner-decal.tsx +0 -56
- package/src/components/keypad-legacy/echo-manager.tsx +0 -152
- package/src/components/keypad-legacy/empty-keypad-button.tsx +0 -58
- package/src/components/keypad-legacy/expression-keypad.tsx +0 -315
- package/src/components/keypad-legacy/fraction-keypad.tsx +0 -180
- package/src/components/keypad-legacy/gesture-manager.ts +0 -255
- package/src/components/keypad-legacy/gesture-state-machine.ts +0 -329
- package/src/components/keypad-legacy/icon.tsx +0 -72
- package/src/components/keypad-legacy/iconography/arrow.js +0 -22
- package/src/components/keypad-legacy/iconography/backspace.js +0 -29
- package/src/components/keypad-legacy/iconography/cdot.js +0 -29
- package/src/components/keypad-legacy/iconography/cos.js +0 -30
- package/src/components/keypad-legacy/iconography/cube-root.js +0 -36
- package/src/components/keypad-legacy/iconography/dismiss.js +0 -25
- package/src/components/keypad-legacy/iconography/divide.js +0 -34
- package/src/components/keypad-legacy/iconography/down.js +0 -16
- package/src/components/keypad-legacy/iconography/equal.js +0 -33
- package/src/components/keypad-legacy/iconography/exp-2.js +0 -29
- package/src/components/keypad-legacy/iconography/exp-3.js +0 -29
- package/src/components/keypad-legacy/iconography/exp.js +0 -29
- package/src/components/keypad-legacy/iconography/frac.js +0 -44
- package/src/components/keypad-legacy/iconography/geq.js +0 -33
- package/src/components/keypad-legacy/iconography/gt.js +0 -33
- package/src/components/keypad-legacy/iconography/index.js +0 -45
- package/src/components/keypad-legacy/iconography/jump-into-numerator.js +0 -41
- package/src/components/keypad-legacy/iconography/jump-out-base.js +0 -30
- package/src/components/keypad-legacy/iconography/jump-out-denominator.js +0 -41
- package/src/components/keypad-legacy/iconography/jump-out-exponent.js +0 -30
- package/src/components/keypad-legacy/iconography/jump-out-numerator.js +0 -41
- package/src/components/keypad-legacy/iconography/jump-out-parentheses.js +0 -33
- package/src/components/keypad-legacy/iconography/left-paren.js +0 -33
- package/src/components/keypad-legacy/iconography/left.js +0 -16
- package/src/components/keypad-legacy/iconography/leq.js +0 -33
- package/src/components/keypad-legacy/iconography/ln.js +0 -29
- package/src/components/keypad-legacy/iconography/log-n.js +0 -29
- package/src/components/keypad-legacy/iconography/log.js +0 -29
- package/src/components/keypad-legacy/iconography/lt.js +0 -33
- package/src/components/keypad-legacy/iconography/minus.js +0 -32
- package/src/components/keypad-legacy/iconography/neq.js +0 -33
- package/src/components/keypad-legacy/iconography/parens.js +0 -33
- package/src/components/keypad-legacy/iconography/percent.js +0 -49
- package/src/components/keypad-legacy/iconography/period.js +0 -26
- package/src/components/keypad-legacy/iconography/plus.js +0 -32
- package/src/components/keypad-legacy/iconography/radical.js +0 -36
- package/src/components/keypad-legacy/iconography/right-paren.js +0 -33
- package/src/components/keypad-legacy/iconography/right.js +0 -16
- package/src/components/keypad-legacy/iconography/sin.js +0 -30
- package/src/components/keypad-legacy/iconography/sqrt.js +0 -32
- package/src/components/keypad-legacy/iconography/tan.js +0 -30
- package/src/components/keypad-legacy/iconography/times.js +0 -33
- package/src/components/keypad-legacy/iconography/up.js +0 -16
- package/src/components/keypad-legacy/index.ts +0 -1
- package/src/components/keypad-legacy/keypad-button.tsx +0 -368
- package/src/components/keypad-legacy/keypad-container.tsx +0 -358
- package/src/components/keypad-legacy/keypad.tsx +0 -162
- package/src/components/keypad-legacy/many-keypad-button.tsx +0 -54
- package/src/components/keypad-legacy/math-icon.tsx +0 -66
- package/src/components/keypad-legacy/multi-symbol-grid.tsx +0 -182
- package/src/components/keypad-legacy/multi-symbol-popover.tsx +0 -58
- package/src/components/keypad-legacy/navigation-pad.tsx +0 -140
- package/src/components/keypad-legacy/node-manager.ts +0 -133
- package/src/components/keypad-legacy/popover-manager.tsx +0 -73
- package/src/components/keypad-legacy/popover-state-machine.ts +0 -184
- package/src/components/keypad-legacy/provided-keypad.tsx +0 -136
- package/src/components/keypad-legacy/store/actions.ts +0 -155
- package/src/components/keypad-legacy/store/echo-reducer.ts +0 -57
- package/src/components/keypad-legacy/store/index.ts +0 -110
- package/src/components/keypad-legacy/store/input-reducer.ts +0 -55
- package/src/components/keypad-legacy/store/keypad-reducer.ts +0 -58
- package/src/components/keypad-legacy/store/layout-reducer.test.ts +0 -171
- package/src/components/keypad-legacy/store/layout-reducer.ts +0 -129
- package/src/components/keypad-legacy/store/shared.ts +0 -12
- package/src/components/keypad-legacy/store/types.ts +0 -78
- package/src/components/keypad-legacy/styles.ts +0 -38
- package/src/components/keypad-legacy/svg-icon.tsx +0 -24
- package/src/components/keypad-legacy/text-icon.tsx +0 -53
- package/src/components/keypad-legacy/touchable-keypad-button.tsx +0 -163
- package/src/components/keypad-legacy/two-page-keypad.tsx +0 -115
- package/src/components/keypad-legacy/z-indexes.ts +0 -8
- package/src/components/keypad-switch.tsx +0 -42
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": "16.1.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"source": "src/index.ts",
|
|
21
21
|
"scripts": {},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@khanacademy/perseus-core": "1.
|
|
23
|
+
"@khanacademy/perseus-core": "1.4.0",
|
|
24
24
|
"mathquill": "git+https://git@github.com/Khan/mathquill.git#48410e80d760bbd5105544d4a4ab459a28dc2cbc",
|
|
25
25
|
"performance-now": "^0.2.0"
|
|
26
26
|
},
|
|
@@ -12,8 +12,8 @@ import React, {useState} from "react";
|
|
|
12
12
|
|
|
13
13
|
import {KeypadType} from "../../enums";
|
|
14
14
|
import MathInput from "../input/math-input";
|
|
15
|
+
import {MobileKeypad} from "../keypad";
|
|
15
16
|
import {KeypadContext, StatefulKeypadContextProvider} from "../keypad-context";
|
|
16
|
-
import KeypadSwitch from "../keypad-switch";
|
|
17
17
|
|
|
18
18
|
import type {KeypadConfiguration} from "../../types";
|
|
19
19
|
|
|
@@ -60,11 +60,10 @@ function KeypadWithContext() {
|
|
|
60
60
|
<KeypadContext.Consumer>
|
|
61
61
|
{({setKeypadElement}) => {
|
|
62
62
|
return (
|
|
63
|
-
<
|
|
63
|
+
<MobileKeypad
|
|
64
64
|
onElementMounted={setKeypadElement}
|
|
65
65
|
onDismiss={() => {}}
|
|
66
66
|
onAnalyticsEvent={async () => {}}
|
|
67
|
-
useV2Keypad
|
|
68
67
|
/>
|
|
69
68
|
);
|
|
70
69
|
}}
|
|
@@ -213,6 +213,7 @@ class MathInput extends React.Component<Props, State> {
|
|
|
213
213
|
);
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
// eslint-disable-next-line react/no-unsafe
|
|
216
217
|
UNSAFE_componentWillReceiveProps(props: Props) {
|
|
217
218
|
if (this.props.keypadElement !== props.keypadElement) {
|
|
218
219
|
this._clearKeypadBoundsCache();
|
|
@@ -26,12 +26,12 @@ import {
|
|
|
26
26
|
} from "./mathquill-helpers";
|
|
27
27
|
import {createMathField, mathQuillInstance} from "./mathquill-instance";
|
|
28
28
|
|
|
29
|
-
import type Key from "../../data/keys";
|
|
30
|
-
import type {Cursor} from "../../types";
|
|
31
29
|
import type {
|
|
32
30
|
MathFieldInterface,
|
|
33
31
|
MathFieldUpdaterCallback,
|
|
34
32
|
} from "./mathquill-types";
|
|
33
|
+
import type Key from "../../data/keys";
|
|
34
|
+
import type {Cursor} from "../../types";
|
|
35
35
|
|
|
36
36
|
const mobileKeyTranslator: Record<Key, MathFieldUpdaterCallback> = {
|
|
37
37
|
...keyTranslator,
|
|
@@ -2,14 +2,14 @@ import {render, screen} from "@testing-library/react";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import "@testing-library/jest-dom";
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import MobileKeypadInternals from "../mobile-keypad-internals";
|
|
6
6
|
|
|
7
7
|
describe("mobile keypad", () => {
|
|
8
8
|
it("should render keypad when active", () => {
|
|
9
9
|
// Arrange
|
|
10
10
|
// Act
|
|
11
11
|
const {container} = render(
|
|
12
|
-
<
|
|
12
|
+
<MobileKeypadInternals
|
|
13
13
|
onAnalyticsEvent={async () => undefined}
|
|
14
14
|
setKeypadActive={(keypadActive: boolean) => undefined}
|
|
15
15
|
keypadActive={true}
|
|
@@ -24,7 +24,7 @@ describe("mobile keypad", () => {
|
|
|
24
24
|
// Arrange
|
|
25
25
|
// Act
|
|
26
26
|
const {container} = render(
|
|
27
|
-
<
|
|
27
|
+
<MobileKeypadInternals
|
|
28
28
|
onAnalyticsEvent={async () => undefined}
|
|
29
29
|
setKeypadActive={(keypadActive: boolean) => undefined}
|
|
30
30
|
keypadActive={false}
|
|
@@ -38,7 +38,7 @@ describe("mobile keypad", () => {
|
|
|
38
38
|
it("should render the keypad when going from keypadActive=false to keypadActive=true", () => {
|
|
39
39
|
// Arrange
|
|
40
40
|
const {rerender} = render(
|
|
41
|
-
<
|
|
41
|
+
<MobileKeypadInternals
|
|
42
42
|
onAnalyticsEvent={async () => undefined}
|
|
43
43
|
setKeypadActive={(keypadActive: boolean) => undefined}
|
|
44
44
|
keypadActive={false}
|
|
@@ -49,7 +49,7 @@ describe("mobile keypad", () => {
|
|
|
49
49
|
|
|
50
50
|
// Act
|
|
51
51
|
rerender(
|
|
52
|
-
<
|
|
52
|
+
<MobileKeypadInternals
|
|
53
53
|
onAnalyticsEvent={async () => undefined}
|
|
54
54
|
setKeypadActive={(keypadActive: boolean) => undefined}
|
|
55
55
|
keypadActive={true}
|
|
@@ -66,7 +66,7 @@ describe("mobile keypad", () => {
|
|
|
66
66
|
|
|
67
67
|
// Act
|
|
68
68
|
render(
|
|
69
|
-
<
|
|
69
|
+
<MobileKeypadInternals
|
|
70
70
|
onAnalyticsEvent={onAnalyticsEvent}
|
|
71
71
|
setKeypadActive={(keypadActive: boolean) => undefined}
|
|
72
72
|
keypadActive={true}
|
|
@@ -87,7 +87,7 @@ describe("mobile keypad", () => {
|
|
|
87
87
|
|
|
88
88
|
// Arrange
|
|
89
89
|
const {rerender, unmount} = render(
|
|
90
|
-
<
|
|
90
|
+
<MobileKeypadInternals
|
|
91
91
|
onAnalyticsEvent={onAnalyticsEvent}
|
|
92
92
|
setKeypadActive={(keypadActive: boolean) => undefined}
|
|
93
93
|
keypadActive={true}
|
|
@@ -96,7 +96,7 @@ describe("mobile keypad", () => {
|
|
|
96
96
|
|
|
97
97
|
// Act
|
|
98
98
|
rerender(
|
|
99
|
-
<
|
|
99
|
+
<MobileKeypadInternals
|
|
100
100
|
onAnalyticsEvent={onAnalyticsEvent}
|
|
101
101
|
setKeypadActive={(keypadActive: boolean) => undefined}
|
|
102
102
|
keypadActive={false}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export {default} from "./keypad";
|
|
2
|
-
export {
|
|
2
|
+
export {MobileKeypad} from "./mobile-keypad";
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {StyleSheet} from "aphrodite";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import ReactDOM from "react-dom";
|
|
4
|
+
|
|
5
|
+
import {View} from "../../fake-react-native-web/index";
|
|
6
|
+
import AphroditeCssTransitionGroup from "../aphrodite-css-transition-group";
|
|
7
|
+
|
|
8
|
+
import Keypad from "./keypad";
|
|
9
|
+
import {expandedViewThreshold} from "./utils";
|
|
10
|
+
|
|
11
|
+
import type Key from "../../data/keys";
|
|
12
|
+
import type {
|
|
13
|
+
Cursor,
|
|
14
|
+
KeypadConfiguration,
|
|
15
|
+
KeyHandler,
|
|
16
|
+
KeypadAPI,
|
|
17
|
+
} from "../../types";
|
|
18
|
+
import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core";
|
|
19
|
+
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
20
|
+
|
|
21
|
+
const AnimationDurationInMS = 200;
|
|
22
|
+
|
|
23
|
+
type Props = {
|
|
24
|
+
onElementMounted?: (arg1: any) => void;
|
|
25
|
+
onDismiss?: () => void;
|
|
26
|
+
style?: StyleType;
|
|
27
|
+
onAnalyticsEvent: AnalyticsEventHandlerFn;
|
|
28
|
+
setKeypadActive: (keypadActive: boolean) => void;
|
|
29
|
+
keypadActive: boolean;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type State = {
|
|
33
|
+
containerWidth: number;
|
|
34
|
+
keypadConfig?: KeypadConfiguration;
|
|
35
|
+
keyHandler?: KeyHandler;
|
|
36
|
+
cursor?: Cursor;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* This is the v2 equivalent of v1's ProvidedKeypad. It follows the same
|
|
41
|
+
* external API so that it can be hot-swapped with the v1 keypad and
|
|
42
|
+
* is responsible for connecting the keypad with MathInput and the Renderer.
|
|
43
|
+
*
|
|
44
|
+
* Ideally this strategy of attaching methods on the class component for
|
|
45
|
+
* other components to call will be replaced props/callbacks since React
|
|
46
|
+
* doesn't support this type of code anymore (functional components
|
|
47
|
+
* can't have methods attached to them).
|
|
48
|
+
*/
|
|
49
|
+
class MobileKeypadInternals
|
|
50
|
+
extends React.Component<Props, State>
|
|
51
|
+
implements KeypadAPI
|
|
52
|
+
{
|
|
53
|
+
_containerRef = React.createRef<HTMLDivElement>();
|
|
54
|
+
_containerResizeObserver: ResizeObserver | null = null;
|
|
55
|
+
_throttleResize = false;
|
|
56
|
+
|
|
57
|
+
state: State = {
|
|
58
|
+
containerWidth: 0,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
componentDidMount() {
|
|
62
|
+
this._resize();
|
|
63
|
+
|
|
64
|
+
window.addEventListener("resize", this._throttleResizeHandler);
|
|
65
|
+
window.addEventListener(
|
|
66
|
+
"orientationchange",
|
|
67
|
+
this._throttleResizeHandler,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// LC-1213: some common older browsers (as of 2023-09-07)
|
|
71
|
+
// don't support ResizeObserver
|
|
72
|
+
if ("ResizeObserver" in window) {
|
|
73
|
+
this._containerResizeObserver = new window.ResizeObserver(
|
|
74
|
+
this._throttleResizeHandler,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (this._containerRef.current) {
|
|
78
|
+
this._containerResizeObserver.observe(
|
|
79
|
+
this._containerRef.current,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.props.onElementMounted?.({
|
|
85
|
+
activate: this.activate,
|
|
86
|
+
dismiss: this.dismiss,
|
|
87
|
+
configure: this.configure,
|
|
88
|
+
setCursor: this.setCursor,
|
|
89
|
+
setKeyHandler: this.setKeyHandler,
|
|
90
|
+
getDOMNode: this.getDOMNode,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
componentWillUnmount() {
|
|
95
|
+
window.removeEventListener("resize", this._throttleResizeHandler);
|
|
96
|
+
window.removeEventListener(
|
|
97
|
+
"orientationchange",
|
|
98
|
+
this._throttleResizeHandler,
|
|
99
|
+
);
|
|
100
|
+
this._containerResizeObserver?.disconnect();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
_resize = () => {
|
|
104
|
+
const containerWidth = this._containerRef.current?.clientWidth || 0;
|
|
105
|
+
this.setState({containerWidth});
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
_throttleResizeHandler = () => {
|
|
109
|
+
if (this._throttleResize) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this._throttleResize = true;
|
|
114
|
+
|
|
115
|
+
setTimeout(() => {
|
|
116
|
+
this._resize();
|
|
117
|
+
this._throttleResize = false;
|
|
118
|
+
}, 100);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
activate: () => void = () => {
|
|
122
|
+
this.props.setKeypadActive(true);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
dismiss: () => void = () => {
|
|
126
|
+
this.props.setKeypadActive(false);
|
|
127
|
+
this.props.onDismiss?.();
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
configure: (configuration: KeypadConfiguration, cb: () => void) => void = (
|
|
131
|
+
configuration,
|
|
132
|
+
cb,
|
|
133
|
+
) => {
|
|
134
|
+
this.setState({keypadConfig: configuration});
|
|
135
|
+
|
|
136
|
+
// TODO(matthewc)[LC-1080]: this was brought in from v1's ProvidedKeypad.
|
|
137
|
+
// We need to investigate whether we still need this.
|
|
138
|
+
// HACK(charlie): In Perseus, triggering a focus causes the keypad to
|
|
139
|
+
// animate into view and re-configure. We'd like to provide the option
|
|
140
|
+
// to re-render the re-configured keypad before animating it into view,
|
|
141
|
+
// to avoid jank in the animation. As such, we support passing a
|
|
142
|
+
// callback into `configureKeypad`. However, implementing this properly
|
|
143
|
+
// would require middleware, etc., so we just hack it on with
|
|
144
|
+
// `setTimeout` for now.
|
|
145
|
+
setTimeout(() => cb && cb());
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
setCursor: (cursor: Cursor) => void = (cursor) => {
|
|
149
|
+
this.setState({cursor});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
setKeyHandler: (keyHandler: KeyHandler) => void = (keyHandler) => {
|
|
153
|
+
this.setState({keyHandler});
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
getDOMNode: () => ReturnType<typeof ReactDOM.findDOMNode> = () => {
|
|
157
|
+
return ReactDOM.findDOMNode(this);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
_handleClickKey(key: Key) {
|
|
161
|
+
if (key === "DISMISS") {
|
|
162
|
+
this.dismiss();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const cursor = this.state.keyHandler?.(key);
|
|
167
|
+
this.setState({cursor});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
render(): React.ReactNode {
|
|
171
|
+
const {keypadActive, style} = this.props;
|
|
172
|
+
const {containerWidth, cursor, keypadConfig} = this.state;
|
|
173
|
+
|
|
174
|
+
const containerStyle = [
|
|
175
|
+
styles.keypadContainer,
|
|
176
|
+
// styles passed as props
|
|
177
|
+
...(Array.isArray(style) ? style : [style]),
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
const isExpression = keypadConfig?.keypadType === "EXPRESSION";
|
|
181
|
+
const convertDotToTimes = keypadConfig?.times;
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<View style={containerStyle} forwardRef={this._containerRef}>
|
|
185
|
+
<AphroditeCssTransitionGroup
|
|
186
|
+
transitionEnterTimeout={AnimationDurationInMS}
|
|
187
|
+
transitionLeaveTimeout={AnimationDurationInMS}
|
|
188
|
+
transitionStyle={{
|
|
189
|
+
enter: {
|
|
190
|
+
transform: "translate3d(0, 100%, 0)",
|
|
191
|
+
transition: `${AnimationDurationInMS}ms ease-out`,
|
|
192
|
+
},
|
|
193
|
+
enterActive: {
|
|
194
|
+
transform: "translate3d(0, 0, 0)",
|
|
195
|
+
},
|
|
196
|
+
leave: {
|
|
197
|
+
transform: "translate3d(0, 0, 0)",
|
|
198
|
+
transition: `${AnimationDurationInMS}ms ease-out`,
|
|
199
|
+
},
|
|
200
|
+
leaveActive: {
|
|
201
|
+
transform: "translate3d(0, 100%, 0)",
|
|
202
|
+
},
|
|
203
|
+
}}
|
|
204
|
+
>
|
|
205
|
+
{keypadActive ? (
|
|
206
|
+
<Keypad
|
|
207
|
+
onAnalyticsEvent={this.props.onAnalyticsEvent}
|
|
208
|
+
extraKeys={keypadConfig?.extraKeys}
|
|
209
|
+
onClickKey={(key) => this._handleClickKey(key)}
|
|
210
|
+
cursorContext={cursor?.context}
|
|
211
|
+
fractionsOnly={!isExpression}
|
|
212
|
+
convertDotToTimes={convertDotToTimes}
|
|
213
|
+
divisionKey={isExpression}
|
|
214
|
+
trigonometry={isExpression}
|
|
215
|
+
preAlgebra={isExpression}
|
|
216
|
+
logarithms={isExpression}
|
|
217
|
+
basicRelations={isExpression}
|
|
218
|
+
advancedRelations={isExpression}
|
|
219
|
+
expandedView={
|
|
220
|
+
containerWidth > expandedViewThreshold
|
|
221
|
+
}
|
|
222
|
+
showDismiss
|
|
223
|
+
/>
|
|
224
|
+
) : null}
|
|
225
|
+
</AphroditeCssTransitionGroup>
|
|
226
|
+
</View>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const styles = StyleSheet.create({
|
|
232
|
+
keypadContainer: {
|
|
233
|
+
bottom: 0,
|
|
234
|
+
left: 0,
|
|
235
|
+
right: 0,
|
|
236
|
+
position: "fixed",
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
export default MobileKeypadInternals;
|
|
@@ -1,237 +1,24 @@
|
|
|
1
|
-
import {StyleSheet} from "aphrodite";
|
|
2
1
|
import * as React from "react";
|
|
3
|
-
import ReactDOM from "react-dom";
|
|
4
2
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
style?: StyleType;
|
|
27
|
-
onAnalyticsEvent: AnalyticsEventHandlerFn;
|
|
28
|
-
setKeypadActive: (keypadActive: boolean) => void;
|
|
29
|
-
keypadActive: boolean;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
type State = {
|
|
33
|
-
containerWidth: number;
|
|
34
|
-
keypadConfig?: KeypadConfiguration;
|
|
35
|
-
keyHandler?: KeyHandler;
|
|
36
|
-
cursor?: Cursor;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* This is the v2 equivalent of v1's ProvidedKeypad. It follows the same
|
|
41
|
-
* external API so that it can be hot-swapped with the v1 keypad and
|
|
42
|
-
* is responsible for connecting the keypad with MathInput and the Renderer.
|
|
43
|
-
*
|
|
44
|
-
* Ideally this strategy of attaching methods on the class component for
|
|
45
|
-
* other components to call will be replaced props/callbacks since React
|
|
46
|
-
* doesn't support this type of code anymore (functional components
|
|
47
|
-
* can't have methods attached to them).
|
|
48
|
-
*/
|
|
49
|
-
class MobileKeypad extends React.Component<Props, State> implements KeypadAPI {
|
|
50
|
-
_containerRef = React.createRef<HTMLDivElement>();
|
|
51
|
-
_containerResizeObserver: ResizeObserver | null = null;
|
|
52
|
-
_throttleResize = false;
|
|
53
|
-
|
|
54
|
-
state: State = {
|
|
55
|
-
containerWidth: 0,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
componentDidMount() {
|
|
59
|
-
this._resize();
|
|
60
|
-
|
|
61
|
-
window.addEventListener("resize", this._throttleResizeHandler);
|
|
62
|
-
window.addEventListener(
|
|
63
|
-
"orientationchange",
|
|
64
|
-
this._throttleResizeHandler,
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
// LC-1213: some common older browsers (as of 2023-09-07)
|
|
68
|
-
// don't support ResizeObserver
|
|
69
|
-
if ("ResizeObserver" in window) {
|
|
70
|
-
this._containerResizeObserver = new window.ResizeObserver(
|
|
71
|
-
this._throttleResizeHandler,
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
if (this._containerRef.current) {
|
|
75
|
-
this._containerResizeObserver.observe(
|
|
76
|
-
this._containerRef.current,
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
this.props.onElementMounted?.({
|
|
82
|
-
activate: this.activate,
|
|
83
|
-
dismiss: this.dismiss,
|
|
84
|
-
configure: this.configure,
|
|
85
|
-
setCursor: this.setCursor,
|
|
86
|
-
setKeyHandler: this.setKeyHandler,
|
|
87
|
-
getDOMNode: this.getDOMNode,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
componentWillUnmount() {
|
|
92
|
-
window.removeEventListener("resize", this._throttleResizeHandler);
|
|
93
|
-
window.removeEventListener(
|
|
94
|
-
"orientationchange",
|
|
95
|
-
this._throttleResizeHandler,
|
|
96
|
-
);
|
|
97
|
-
this._containerResizeObserver?.disconnect();
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
_resize = () => {
|
|
101
|
-
const containerWidth = this._containerRef.current?.clientWidth || 0;
|
|
102
|
-
this.setState({containerWidth});
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
_throttleResizeHandler = () => {
|
|
106
|
-
if (this._throttleResize) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
this._throttleResize = true;
|
|
111
|
-
|
|
112
|
-
setTimeout(() => {
|
|
113
|
-
this._resize();
|
|
114
|
-
this._throttleResize = false;
|
|
115
|
-
}, 100);
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
activate: () => void = () => {
|
|
119
|
-
this.props.setKeypadActive(true);
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
dismiss: () => void = () => {
|
|
123
|
-
this.props.setKeypadActive(false);
|
|
124
|
-
this.props.onDismiss?.();
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
configure: (configuration: KeypadConfiguration, cb: () => void) => void = (
|
|
128
|
-
configuration,
|
|
129
|
-
cb,
|
|
130
|
-
) => {
|
|
131
|
-
this.setState({keypadConfig: configuration});
|
|
132
|
-
|
|
133
|
-
// TODO(matthewc)[LC-1080]: this was brought in from v1's ProvidedKeypad.
|
|
134
|
-
// We need to investigate whether we still need this.
|
|
135
|
-
// HACK(charlie): In Perseus, triggering a focus causes the keypad to
|
|
136
|
-
// animate into view and re-configure. We'd like to provide the option
|
|
137
|
-
// to re-render the re-configured keypad before animating it into view,
|
|
138
|
-
// to avoid jank in the animation. As such, we support passing a
|
|
139
|
-
// callback into `configureKeypad`. However, implementing this properly
|
|
140
|
-
// would require middleware, etc., so we just hack it on with
|
|
141
|
-
// `setTimeout` for now.
|
|
142
|
-
setTimeout(() => cb && cb());
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
setCursor: (cursor: Cursor) => void = (cursor) => {
|
|
146
|
-
this.setState({cursor});
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
setKeyHandler: (keyHandler: KeyHandler) => void = (keyHandler) => {
|
|
150
|
-
this.setState({keyHandler});
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
getDOMNode: () => ReturnType<typeof ReactDOM.findDOMNode> = () => {
|
|
154
|
-
return ReactDOM.findDOMNode(this);
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
_handleClickKey(key: Key) {
|
|
158
|
-
if (key === "DISMISS") {
|
|
159
|
-
this.dismiss();
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const cursor = this.state.keyHandler?.(key);
|
|
164
|
-
this.setState({cursor});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
render(): React.ReactNode {
|
|
168
|
-
const {keypadActive, style} = this.props;
|
|
169
|
-
const {containerWidth, cursor, keypadConfig} = this.state;
|
|
170
|
-
|
|
171
|
-
const containerStyle = [
|
|
172
|
-
styles.keypadContainer,
|
|
173
|
-
// styles passed as props
|
|
174
|
-
...(Array.isArray(style) ? style : [style]),
|
|
175
|
-
];
|
|
176
|
-
|
|
177
|
-
const isExpression = keypadConfig?.keypadType === "EXPRESSION";
|
|
178
|
-
const convertDotToTimes = keypadConfig?.times;
|
|
179
|
-
|
|
180
|
-
return (
|
|
181
|
-
<View style={containerStyle} forwardRef={this._containerRef}>
|
|
182
|
-
<AphroditeCssTransitionGroup
|
|
183
|
-
transitionEnterTimeout={AnimationDurationInMS}
|
|
184
|
-
transitionLeaveTimeout={AnimationDurationInMS}
|
|
185
|
-
transitionStyle={{
|
|
186
|
-
enter: {
|
|
187
|
-
transform: "translate3d(0, 100%, 0)",
|
|
188
|
-
transition: `${AnimationDurationInMS}ms ease-out`,
|
|
189
|
-
},
|
|
190
|
-
enterActive: {
|
|
191
|
-
transform: "translate3d(0, 0, 0)",
|
|
192
|
-
},
|
|
193
|
-
leave: {
|
|
194
|
-
transform: "translate3d(0, 0, 0)",
|
|
195
|
-
transition: `${AnimationDurationInMS}ms ease-out`,
|
|
196
|
-
},
|
|
197
|
-
leaveActive: {
|
|
198
|
-
transform: "translate3d(0, 100%, 0)",
|
|
199
|
-
},
|
|
200
|
-
}}
|
|
201
|
-
>
|
|
202
|
-
{keypadActive ? (
|
|
203
|
-
<Keypad
|
|
204
|
-
onAnalyticsEvent={this.props.onAnalyticsEvent}
|
|
205
|
-
extraKeys={keypadConfig?.extraKeys}
|
|
206
|
-
onClickKey={(key) => this._handleClickKey(key)}
|
|
207
|
-
cursorContext={cursor?.context}
|
|
208
|
-
fractionsOnly={!isExpression}
|
|
209
|
-
convertDotToTimes={convertDotToTimes}
|
|
210
|
-
divisionKey={isExpression}
|
|
211
|
-
trigonometry={isExpression}
|
|
212
|
-
preAlgebra={isExpression}
|
|
213
|
-
logarithms={isExpression}
|
|
214
|
-
basicRelations={isExpression}
|
|
215
|
-
advancedRelations={isExpression}
|
|
216
|
-
expandedView={
|
|
217
|
-
containerWidth > expandedViewThreshold
|
|
218
|
-
}
|
|
219
|
-
showDismiss
|
|
220
|
-
/>
|
|
221
|
-
) : null}
|
|
222
|
-
</AphroditeCssTransitionGroup>
|
|
223
|
-
</View>
|
|
224
|
-
);
|
|
225
|
-
}
|
|
3
|
+
import {KeypadContext} from "../keypad-context";
|
|
4
|
+
|
|
5
|
+
import MobileKeypadInternals from "./mobile-keypad-internals";
|
|
6
|
+
|
|
7
|
+
type Props = Omit<
|
|
8
|
+
React.ComponentProps<typeof MobileKeypadInternals>,
|
|
9
|
+
"keypadActive" | "setKeypadActive"
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
export function MobileKeypad(props: Props) {
|
|
13
|
+
return (
|
|
14
|
+
<KeypadContext.Consumer>
|
|
15
|
+
{({keypadActive, setKeypadActive}) => (
|
|
16
|
+
<MobileKeypadInternals
|
|
17
|
+
{...props}
|
|
18
|
+
keypadActive={keypadActive}
|
|
19
|
+
setKeypadActive={setKeypadActive}
|
|
20
|
+
/>
|
|
21
|
+
)}
|
|
22
|
+
</KeypadContext.Consumer>
|
|
23
|
+
);
|
|
226
24
|
}
|
|
227
|
-
|
|
228
|
-
const styles = StyleSheet.create({
|
|
229
|
-
keypadContainer: {
|
|
230
|
-
bottom: 0,
|
|
231
|
-
left: 0,
|
|
232
|
-
right: 0,
|
|
233
|
-
position: "fixed",
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
export default MobileKeypad;
|
package/src/data/key-configs.ts
CHANGED
|
@@ -5,9 +5,9 @@ import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
|
5
5
|
|
|
6
6
|
import {IconType} from "../enums";
|
|
7
7
|
|
|
8
|
+
import type Key from "./keys";
|
|
8
9
|
import type {KeyType} from "../enums";
|
|
9
10
|
import type {KeyConfig} from "../types";
|
|
10
|
-
import type Key from "./keys";
|
|
11
11
|
|
|
12
12
|
type KeyConfigMapper = (args: {
|
|
13
13
|
key: Key;
|
package/src/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ export {CursorContext} from "./components/input/cursor-contexts";
|
|
|
27
27
|
export {getCursorContext} from "./components/input/mathquill-helpers";
|
|
28
28
|
|
|
29
29
|
// Wrapper around v1 and v2 mobile keypads to switch between them
|
|
30
|
-
export {
|
|
30
|
+
export {MobileKeypad} from "./components/keypad";
|
|
31
31
|
// Unwrapped v2 keypad for desktop
|
|
32
32
|
export {default as DesktopKeypad} from "./components/keypad";
|
|
33
33
|
|