@khanacademy/math-input 11.0.0 → 12.0.1
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 +1 -1
- package/dist/components/keypad/mobile-keypad.d.ts +2 -0
- package/dist/components/keypad-legacy/provided-keypad.d.ts +2 -0
- package/dist/components/keypad-switch.d.ts +2 -0
- package/dist/es/index.js +66 -28
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +66 -28
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/input/math-input.tsx +13 -5
- package/src/components/keypad/mobile-keypad.tsx +23 -12
- package/src/components/keypad-legacy/keypad-container.tsx +11 -5
- package/src/components/keypad-legacy/provided-keypad.tsx +21 -3
- package/src/components/keypad-switch.tsx +5 -0
- package/src/full-math-input.stories.tsx +9 -1
- package/tsconfig-build.tsbuildinfo +1 -1
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": "12.0.1",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@khanacademy/wonder-blocks-core": "^6.0.0",
|
|
31
31
|
"@khanacademy/wonder-blocks-i18n": "^2.0.2",
|
|
32
32
|
"@khanacademy/wonder-blocks-popover": "^2.0.11",
|
|
33
|
-
"@khanacademy/wonder-stuff-core": "^1.
|
|
33
|
+
"@khanacademy/wonder-stuff-core": "^1.5.1",
|
|
34
34
|
"aphrodite": "^1.1.0",
|
|
35
35
|
"jquery": "^2.1.1",
|
|
36
36
|
"katex": "^0.11.1",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@khanacademy/wonder-blocks-color": "^2.0.1",
|
|
50
50
|
"@khanacademy/wonder-blocks-core": "^6.0.0",
|
|
51
51
|
"@khanacademy/wonder-blocks-i18n": "^2.0.2",
|
|
52
|
-
"@khanacademy/wonder-stuff-core": "^1.
|
|
52
|
+
"@khanacademy/wonder-stuff-core": "^1.5.1",
|
|
53
53
|
"aphrodite": "^1.1.0",
|
|
54
54
|
"jquery": "^2.1.1",
|
|
55
55
|
"katex": "^0.11.1",
|
|
@@ -185,7 +185,7 @@ class MathInput extends React.Component<Props, State> {
|
|
|
185
185
|
// dismissal. This code needs to be generalized to handle
|
|
186
186
|
// multi-touch.
|
|
187
187
|
if (this.state.focused && this.didTouchOutside && !this.didScroll) {
|
|
188
|
-
this.blur();
|
|
188
|
+
this.blur(true);
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
this.didTouchOutside = false;
|
|
@@ -332,9 +332,11 @@ class MathInput extends React.Component<Props, State> {
|
|
|
332
332
|
}
|
|
333
333
|
};
|
|
334
334
|
|
|
335
|
-
blur: () => void = () => {
|
|
335
|
+
blur: (callPropsOnBlur: boolean) => void = (callPropsOnBlur: boolean) => {
|
|
336
336
|
this.mathField.blur();
|
|
337
|
-
|
|
337
|
+
if (callPropsOnBlur) {
|
|
338
|
+
this.props.onBlur && this.props.onBlur();
|
|
339
|
+
}
|
|
338
340
|
this.setState({focused: false, handle: {visible: false}});
|
|
339
341
|
};
|
|
340
342
|
|
|
@@ -907,11 +909,17 @@ class MathInput extends React.Component<Props, State> {
|
|
|
907
909
|
overrides.css. */}
|
|
908
910
|
<div
|
|
909
911
|
className="keypad-input"
|
|
910
|
-
|
|
911
|
-
tabIndex={"0"}
|
|
912
|
+
tabIndex={0}
|
|
912
913
|
ref={(node) => {
|
|
913
914
|
this.inputRef = node;
|
|
914
915
|
}}
|
|
916
|
+
onFocus={() => {
|
|
917
|
+
this.focus();
|
|
918
|
+
}}
|
|
919
|
+
onBlur={() => {
|
|
920
|
+
this._hideCursorHandle();
|
|
921
|
+
this.blur(false);
|
|
922
|
+
}}
|
|
915
923
|
onKeyUp={this.handleKeyUp}
|
|
916
924
|
>
|
|
917
925
|
{/* NOTE(charlie): This element must be styled with inline
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
KeyHandler,
|
|
14
14
|
KeypadAPI,
|
|
15
15
|
} from "../../types";
|
|
16
|
+
import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core";
|
|
16
17
|
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
17
18
|
|
|
18
19
|
import Keypad from "./index";
|
|
@@ -32,6 +33,7 @@ type Props = {
|
|
|
32
33
|
onElementMounted?: (arg1: any) => void;
|
|
33
34
|
onDismiss?: () => void;
|
|
34
35
|
style?: StyleType;
|
|
36
|
+
onAnalyticsEvent: AnalyticsEventHandlerFn;
|
|
35
37
|
};
|
|
36
38
|
|
|
37
39
|
type State = {
|
|
@@ -62,12 +64,18 @@ class MobileKeypad extends React.Component<Props, State> implements KeypadAPI {
|
|
|
62
64
|
this._throttleResizeHandler,
|
|
63
65
|
);
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
+
}
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
|
|
@@ -99,13 +107,17 @@ class MobileKeypad extends React.Component<Props, State> implements KeypadAPI {
|
|
|
99
107
|
};
|
|
100
108
|
|
|
101
109
|
activate: () => void = () => {
|
|
102
|
-
this.
|
|
110
|
+
if (!this.state.active) {
|
|
111
|
+
this.setState({active: true});
|
|
112
|
+
}
|
|
103
113
|
};
|
|
104
114
|
|
|
105
115
|
dismiss: () => void = () => {
|
|
106
|
-
this.
|
|
107
|
-
this.
|
|
108
|
-
|
|
116
|
+
if (this.state.active) {
|
|
117
|
+
this.setState({active: false}, () => {
|
|
118
|
+
this.props.onDismiss?.();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
109
121
|
};
|
|
110
122
|
|
|
111
123
|
configure: (configuration: KeypadConfiguration, cb: () => void) => void = (
|
|
@@ -190,8 +202,7 @@ class MobileKeypad extends React.Component<Props, State> implements KeypadAPI {
|
|
|
190
202
|
}}
|
|
191
203
|
>
|
|
192
204
|
<Keypad
|
|
193
|
-
|
|
194
|
-
onAnalyticsEvent={async () => {}}
|
|
205
|
+
onAnalyticsEvent={this.props.onAnalyticsEvent}
|
|
195
206
|
extraKeys={keypadConfig?.extraKeys}
|
|
196
207
|
onClickKey={(key) => this._handleClickKey(key)}
|
|
197
208
|
cursorContext={cursor?.context}
|
|
@@ -79,12 +79,18 @@ class KeypadContainer extends React.Component<Props, State> {
|
|
|
79
79
|
this._throttleResizeHandler,
|
|
80
80
|
);
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
82
|
+
// LC-1213: some common older browsers (as of 2023-09-07)
|
|
83
|
+
// don't support ResizeObserver
|
|
84
|
+
if ("ResizeObserver" in window) {
|
|
85
|
+
this._containerResizeObserver = new window.ResizeObserver(
|
|
86
|
+
this._throttleResizeHandler,
|
|
87
|
+
);
|
|
85
88
|
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
if (this._containerRef.current) {
|
|
90
|
+
this._containerResizeObserver.observe(
|
|
91
|
+
this._containerRef.current,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
88
94
|
}
|
|
89
95
|
}
|
|
90
96
|
|
|
@@ -18,12 +18,15 @@ import type {
|
|
|
18
18
|
KeyHandler,
|
|
19
19
|
KeypadAPI,
|
|
20
20
|
} from "../../types";
|
|
21
|
+
import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core";
|
|
21
22
|
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
22
23
|
|
|
23
24
|
type Props = {
|
|
24
25
|
onElementMounted?: (arg1: any) => void;
|
|
25
26
|
onDismiss?: () => void;
|
|
26
27
|
style?: StyleType;
|
|
28
|
+
|
|
29
|
+
onAnalyticsEvent: AnalyticsEventHandlerFn;
|
|
27
30
|
};
|
|
28
31
|
|
|
29
32
|
class ProvidedKeypad extends React.Component<Props> implements KeypadAPI {
|
|
@@ -77,6 +80,13 @@ class ProvidedKeypad extends React.Component<Props> implements KeypadAPI {
|
|
|
77
80
|
<Provider store={this.store}>
|
|
78
81
|
<KeypadContainer
|
|
79
82
|
onElementMounted={(element) => {
|
|
83
|
+
this.props.onAnalyticsEvent({
|
|
84
|
+
type: "math-input:keypad-opened",
|
|
85
|
+
payload: {
|
|
86
|
+
virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1",
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
80
90
|
// Append the dispatch methods that we want to expose
|
|
81
91
|
// externally to the returned React element.
|
|
82
92
|
const elementWithDispatchMethods = {
|
|
@@ -88,10 +98,18 @@ class ProvidedKeypad extends React.Component<Props> implements KeypadAPI {
|
|
|
88
98
|
setKeyHandler: this.setKeyHandler,
|
|
89
99
|
getDOMNode: this.getDOMNode,
|
|
90
100
|
} as const;
|
|
91
|
-
onElementMounted
|
|
92
|
-
|
|
101
|
+
onElementMounted?.(elementWithDispatchMethods);
|
|
102
|
+
}}
|
|
103
|
+
onDismiss={() => {
|
|
104
|
+
this.props.onAnalyticsEvent({
|
|
105
|
+
type: "math-input:keypad-closed",
|
|
106
|
+
payload: {
|
|
107
|
+
virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1",
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
onDismiss?.();
|
|
93
112
|
}}
|
|
94
|
-
onDismiss={onDismiss}
|
|
95
113
|
style={style}
|
|
96
114
|
/>
|
|
97
115
|
</Provider>
|
|
@@ -3,6 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import {MobileKeypad} from "./keypad";
|
|
4
4
|
import LegacyKeypad from "./keypad-legacy";
|
|
5
5
|
|
|
6
|
+
import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core";
|
|
6
7
|
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
7
8
|
|
|
8
9
|
type Props = {
|
|
@@ -11,6 +12,7 @@ type Props = {
|
|
|
11
12
|
style?: StyleType;
|
|
12
13
|
|
|
13
14
|
useV2Keypad?: boolean;
|
|
15
|
+
onAnalyticsEvent: AnalyticsEventHandlerFn;
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
function KeypadSwitch(props: Props) {
|
|
@@ -18,6 +20,9 @@ function KeypadSwitch(props: Props) {
|
|
|
18
20
|
|
|
19
21
|
const KeypadComponent = useV2Keypad ? MobileKeypad : LegacyKeypad;
|
|
20
22
|
|
|
23
|
+
// Note: Although we pass the "onAnalyticsEvent" to both keypad components,
|
|
24
|
+
// only the current one uses it. There's no point in instrumenting the
|
|
25
|
+
// legacy keypad given that it's on its way out the door.
|
|
21
26
|
return <KeypadComponent {...rest} />;
|
|
22
27
|
}
|
|
23
28
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {action} from "@storybook/addon-actions";
|
|
1
2
|
import * as React from "react";
|
|
2
3
|
|
|
3
4
|
import type {KeypadAPI} from "./types";
|
|
@@ -29,6 +30,8 @@ export const Basic = () => {
|
|
|
29
30
|
// Whether the keypad is open or not
|
|
30
31
|
const [keypadOpen, setKeypadOpen] = React.useState<boolean>(false);
|
|
31
32
|
|
|
33
|
+
const input = React.useRef<any>(null);
|
|
34
|
+
|
|
32
35
|
const toggleKeypad = () => {
|
|
33
36
|
if (keypadOpen) {
|
|
34
37
|
keypadElement?.dismiss();
|
|
@@ -73,10 +76,11 @@ export const Basic = () => {
|
|
|
73
76
|
|
|
74
77
|
<KeypadInput
|
|
75
78
|
value={value}
|
|
79
|
+
ref={input}
|
|
76
80
|
keypadElement={keypadElement}
|
|
77
81
|
onChange={(newValue, callback) => {
|
|
78
82
|
setValue(newValue);
|
|
79
|
-
callback();
|
|
83
|
+
callback?.();
|
|
80
84
|
}}
|
|
81
85
|
onFocus={() => {
|
|
82
86
|
keypadElement?.activate();
|
|
@@ -92,7 +96,11 @@ export const Basic = () => {
|
|
|
92
96
|
setKeypadElement(node);
|
|
93
97
|
}
|
|
94
98
|
}}
|
|
99
|
+
onDismiss={() => {
|
|
100
|
+
input.current?.blur();
|
|
101
|
+
}}
|
|
95
102
|
useV2Keypad={v2Keypad}
|
|
103
|
+
onAnalyticsEvent={async (e) => action("onAnalyticsEvent")(e)}
|
|
96
104
|
/>
|
|
97
105
|
</div>
|
|
98
106
|
);
|