@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
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import type {ActiveNodesObj} from "../../types";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A state machine for the popover state. In particular, this class manages the
|
|
5
|
-
* mapping of parent nodes to their children, and translates touch events that
|
|
6
|
-
* traverse various nodes to actions that are conditioned on whether a popover
|
|
7
|
-
* is present.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
type Handlers = {
|
|
11
|
-
onActiveNodesChanged: (activeNodes: ActiveNodesObj) => void;
|
|
12
|
-
onClick: (keyId: string, domNodeId: string, inPopover: boolean) => void;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
class PopoverStateMachine {
|
|
16
|
-
handlers: Handlers;
|
|
17
|
-
popovers: Record<string, ReadonlyArray<string>>;
|
|
18
|
-
activePopover: string | null;
|
|
19
|
-
|
|
20
|
-
constructor(handlers) {
|
|
21
|
-
this.handlers = handlers;
|
|
22
|
-
|
|
23
|
-
this.activePopover = null;
|
|
24
|
-
this.popovers = {};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Register a popover container as containing a set of children.
|
|
29
|
-
*
|
|
30
|
-
* @param {string} id - the identifier of the popover container
|
|
31
|
-
* @param {string[]} childIds - the identifiers of the nodes contained in
|
|
32
|
-
* the popover container
|
|
33
|
-
*/
|
|
34
|
-
registerPopover(id, childIds) {
|
|
35
|
-
this.popovers[id] = childIds;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Unregister a popover container.
|
|
40
|
-
*
|
|
41
|
-
* @param {string} id - the identifier of the popover container to
|
|
42
|
-
* unregister
|
|
43
|
-
*/
|
|
44
|
-
unregisterPopover(id) {
|
|
45
|
-
delete this.popovers[id];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* @returns {boolean} - whether a popover is active and visible
|
|
50
|
-
*/
|
|
51
|
-
isPopoverVisible() {
|
|
52
|
-
return this.activePopover != null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Blur the active nodes.
|
|
57
|
-
*/
|
|
58
|
-
onBlur() {
|
|
59
|
-
this.activePopover = null;
|
|
60
|
-
this.handlers.onActiveNodesChanged({
|
|
61
|
-
popover: null,
|
|
62
|
-
focus: null,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Handle a focus event on the node with the given identifier.
|
|
68
|
-
*
|
|
69
|
-
* @param {string} id - the identifier of the node that was focused
|
|
70
|
-
*/
|
|
71
|
-
onFocus(id) {
|
|
72
|
-
if (this.activePopover) {
|
|
73
|
-
// If we have a popover that is currently active, we focus this
|
|
74
|
-
// node if it's in the popover, and remove any highlight otherwise.
|
|
75
|
-
if (this._isNodeInsidePopover(this.activePopover, id)) {
|
|
76
|
-
this.handlers.onActiveNodesChanged({
|
|
77
|
-
popover: {
|
|
78
|
-
parentId: this.activePopover,
|
|
79
|
-
childIds: this.popovers[this.activePopover],
|
|
80
|
-
},
|
|
81
|
-
focus: id,
|
|
82
|
-
});
|
|
83
|
-
} else {
|
|
84
|
-
this.handlers.onActiveNodesChanged({
|
|
85
|
-
popover: {
|
|
86
|
-
parentId: this.activePopover,
|
|
87
|
-
childIds: this.popovers[this.activePopover],
|
|
88
|
-
},
|
|
89
|
-
focus: null,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
this.activePopover = null;
|
|
94
|
-
this.handlers.onActiveNodesChanged({
|
|
95
|
-
popover: null,
|
|
96
|
-
focus: id,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Handle a long press event on the node with the given identifier.
|
|
103
|
-
*
|
|
104
|
-
* @param {string} id - the identifier of the node that was long-pressed
|
|
105
|
-
*/
|
|
106
|
-
onLongPress(id) {
|
|
107
|
-
// We only care about long presses if they occur on a popover, and we
|
|
108
|
-
// don't already have a popover active.
|
|
109
|
-
if (!this.activePopover && this.popovers[id]) {
|
|
110
|
-
// NOTE(charlie): There's an assumption here that focusing the
|
|
111
|
-
// first child is the correct behavior for a newly focused popover.
|
|
112
|
-
// This relies on the fact that the children are rendered
|
|
113
|
-
// bottom-up. If that rendering changes, this logic will need to
|
|
114
|
-
// change as well.
|
|
115
|
-
this.activePopover = id;
|
|
116
|
-
this.handlers.onActiveNodesChanged({
|
|
117
|
-
popover: {
|
|
118
|
-
parentId: id,
|
|
119
|
-
childIds: this.popovers[id],
|
|
120
|
-
},
|
|
121
|
-
focus: this._defaultNodeForPopover(this.activePopover),
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Handle the trigger (click or hold) of the node with the given identifier.
|
|
128
|
-
*
|
|
129
|
-
* @param {string} id - the identifier of the node that was triggered
|
|
130
|
-
*/
|
|
131
|
-
onTrigger(id) {
|
|
132
|
-
this.handlers.onClick(id, id, false);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Handle a touch-end event on the node with the given identifier.
|
|
137
|
-
*
|
|
138
|
-
* @param {string} id - the identifier of the node over which the touch
|
|
139
|
-
* ended
|
|
140
|
-
*/
|
|
141
|
-
onTouchEnd(id) {
|
|
142
|
-
const inPopover = !!this.activePopover;
|
|
143
|
-
if (inPopover) {
|
|
144
|
-
// If we have a popover that is currently active, we trigger a
|
|
145
|
-
// click on this node if and only if it's in the popover, with the
|
|
146
|
-
// exception that, if the node passed back _is_ the active popover,
|
|
147
|
-
// then we trigger its default node. This latter case should only
|
|
148
|
-
// be triggered if the user were to tap down on a popover-enabled
|
|
149
|
-
// node, hold for long enough for the popover to appear, and then
|
|
150
|
-
// release without ever moving their finger, in which case, the
|
|
151
|
-
// underlying gesture system would have no idea that the popover's
|
|
152
|
-
// first child node was now focused.
|
|
153
|
-
if (this._isNodeInsidePopover(this.activePopover, id)) {
|
|
154
|
-
this.handlers.onClick(id, id, inPopover);
|
|
155
|
-
} else if (this.activePopover === id) {
|
|
156
|
-
const keyId = this._defaultNodeForPopover(id);
|
|
157
|
-
this.handlers.onClick(keyId, keyId, inPopover);
|
|
158
|
-
}
|
|
159
|
-
} else if (this.popovers[id]) {
|
|
160
|
-
// Otherwise, if the node is itself a popover revealer, trigger the
|
|
161
|
-
// clicking of its default node, but pass back the popover node ID
|
|
162
|
-
// for layout purposes.
|
|
163
|
-
const keyId = this._defaultNodeForPopover(id);
|
|
164
|
-
const domNodeId = id;
|
|
165
|
-
this.handlers.onClick(keyId, domNodeId, inPopover);
|
|
166
|
-
} else if (id != null) {
|
|
167
|
-
// Finally, if we have no active popover, and we touched up over a
|
|
168
|
-
// valid key, trigger a click.
|
|
169
|
-
this.onTrigger(id);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
this.onBlur();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
_isNodeInsidePopover(popover, id) {
|
|
176
|
-
return this.popovers[popover].includes(id);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
_defaultNodeForPopover(popover) {
|
|
180
|
-
return this.popovers[popover][0];
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export default PopoverStateMachine;
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import ReactDOM from "react-dom";
|
|
3
|
-
import {Provider} from "react-redux";
|
|
4
|
-
|
|
5
|
-
import KeypadContainer from "./keypad-container";
|
|
6
|
-
import {
|
|
7
|
-
activateKeypad,
|
|
8
|
-
dismissKeypad,
|
|
9
|
-
configureKeypad,
|
|
10
|
-
setCursor,
|
|
11
|
-
setKeyHandler,
|
|
12
|
-
} from "./store/actions";
|
|
13
|
-
import {createStore} from "./store/index";
|
|
14
|
-
|
|
15
|
-
import type {
|
|
16
|
-
Cursor,
|
|
17
|
-
KeypadConfiguration,
|
|
18
|
-
KeyHandler,
|
|
19
|
-
KeypadAPI,
|
|
20
|
-
} from "../../types";
|
|
21
|
-
import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core";
|
|
22
|
-
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
23
|
-
|
|
24
|
-
type Props = {
|
|
25
|
-
setKeypadActive: (keypadActive: boolean) => void;
|
|
26
|
-
keypadActive: boolean;
|
|
27
|
-
onElementMounted?: (arg1: any) => void;
|
|
28
|
-
onDismiss?: () => void;
|
|
29
|
-
style?: StyleType;
|
|
30
|
-
|
|
31
|
-
onAnalyticsEvent: AnalyticsEventHandlerFn;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
class ProvidedKeypad extends React.Component<Props> implements KeypadAPI {
|
|
35
|
-
store: any;
|
|
36
|
-
|
|
37
|
-
constructor(props) {
|
|
38
|
-
super(props);
|
|
39
|
-
this.store = createStore();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
componentDidUpdate(prevProps) {
|
|
43
|
-
if (this.props.keypadActive && !prevProps.keypadActive) {
|
|
44
|
-
this.store.dispatch(activateKeypad());
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!this.props.keypadActive && prevProps.keypadActive) {
|
|
48
|
-
this.store.dispatch(dismissKeypad());
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
activate: () => void = () => {
|
|
53
|
-
this.props.setKeypadActive(true);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
dismiss: () => void = () => {
|
|
57
|
-
this.props.setKeypadActive(false);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
configure: (configuration: KeypadConfiguration, cb: () => void) => void = (
|
|
61
|
-
configuration,
|
|
62
|
-
cb,
|
|
63
|
-
) => {
|
|
64
|
-
this.store.dispatch(configureKeypad(configuration));
|
|
65
|
-
|
|
66
|
-
// HACK(charlie): In Perseus, triggering a focus causes the keypad to
|
|
67
|
-
// animate into view and re-configure. We'd like to provide the option
|
|
68
|
-
// to re-render the re-configured keypad before animating it into view,
|
|
69
|
-
// to avoid jank in the animation. As such, we support passing a
|
|
70
|
-
// callback into `configureKeypad`. However, implementing this properly
|
|
71
|
-
// would require middleware, etc., so we just hack it on with
|
|
72
|
-
// `setTimeout` for now.
|
|
73
|
-
setTimeout(() => cb && cb());
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
setCursor: (cursor: Cursor) => void = (cursor) => {
|
|
77
|
-
this.store.dispatch(setCursor(cursor));
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
setKeyHandler: (keyHandler: KeyHandler) => void = (keyHandler) => {
|
|
81
|
-
this.store.dispatch(setKeyHandler(keyHandler));
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
getDOMNode: () => ReturnType<typeof ReactDOM.findDOMNode> = () => {
|
|
85
|
-
return ReactDOM.findDOMNode(this);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
onElementMounted: (element: any) => void = (element) => {
|
|
89
|
-
this.props.onAnalyticsEvent({
|
|
90
|
-
type: "math-input:keypad-opened",
|
|
91
|
-
payload: {
|
|
92
|
-
virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1",
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Append the dispatch methods that we want to expose
|
|
97
|
-
// externally to the returned React element.
|
|
98
|
-
const elementWithDispatchMethods = {
|
|
99
|
-
...element,
|
|
100
|
-
activate: this.activate,
|
|
101
|
-
dismiss: this.dismiss,
|
|
102
|
-
configure: this.configure,
|
|
103
|
-
setCursor: this.setCursor,
|
|
104
|
-
setKeyHandler: this.setKeyHandler,
|
|
105
|
-
getDOMNode: this.getDOMNode,
|
|
106
|
-
} as const;
|
|
107
|
-
this.props.onElementMounted?.(elementWithDispatchMethods);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
onDismiss: () => void = () => {
|
|
111
|
-
this.props.onAnalyticsEvent({
|
|
112
|
-
type: "math-input:keypad-closed",
|
|
113
|
-
payload: {
|
|
114
|
-
virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1",
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
this.props.onDismiss?.();
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
render(): React.ReactNode {
|
|
122
|
-
const {style} = this.props;
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<Provider store={this.store}>
|
|
126
|
-
<KeypadContainer
|
|
127
|
-
onElementMounted={this.onElementMounted}
|
|
128
|
-
onDismiss={this.onDismiss}
|
|
129
|
-
style={style}
|
|
130
|
-
/>
|
|
131
|
-
</Provider>
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export default ProvidedKeypad;
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import type Key from "../../../data/keys";
|
|
2
|
-
import type {
|
|
3
|
-
Bound,
|
|
4
|
-
KeypadConfiguration,
|
|
5
|
-
KeyHandler,
|
|
6
|
-
Cursor,
|
|
7
|
-
ActiveNodesObj,
|
|
8
|
-
} from "../../../types";
|
|
9
|
-
|
|
10
|
-
// naming convention: verb + noun
|
|
11
|
-
// the noun should be one of the other properties in the object that's
|
|
12
|
-
// being dispatched
|
|
13
|
-
|
|
14
|
-
type DismissKeypadAction = {
|
|
15
|
-
type: "DismissKeypad";
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const dismissKeypad = (): DismissKeypadAction => {
|
|
19
|
-
return {
|
|
20
|
-
type: "DismissKeypad",
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
type ActivateKeypadAction = {
|
|
25
|
-
type: "ActivateKeypad";
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const activateKeypad = (): ActivateKeypadAction => {
|
|
29
|
-
return {
|
|
30
|
-
type: "ActivateKeypad",
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Configure the keypad with the provided configuration parameters.
|
|
36
|
-
*/
|
|
37
|
-
type ConfigureKeypadAction = {
|
|
38
|
-
type: "ConfigureKeypad";
|
|
39
|
-
configuration: KeypadConfiguration;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export const configureKeypad = (
|
|
43
|
-
configuration: KeypadConfiguration,
|
|
44
|
-
): ConfigureKeypadAction => {
|
|
45
|
-
return {
|
|
46
|
-
type: "ConfigureKeypad",
|
|
47
|
-
configuration,
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
type SetPageSizeAction = {
|
|
52
|
-
type: "SetPageSize";
|
|
53
|
-
pageWidth: number;
|
|
54
|
-
pageHeight: number;
|
|
55
|
-
containerWidth: number;
|
|
56
|
-
containerHeight: number;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export const setPageSize = (
|
|
60
|
-
pageWidth: number,
|
|
61
|
-
pageHeight: number,
|
|
62
|
-
containerWidth: number,
|
|
63
|
-
containerHeight: number,
|
|
64
|
-
): SetPageSizeAction => {
|
|
65
|
-
return {
|
|
66
|
-
type: "SetPageSize",
|
|
67
|
-
pageWidth,
|
|
68
|
-
pageHeight,
|
|
69
|
-
containerWidth,
|
|
70
|
-
containerHeight,
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
type RemoveEchoAction = {
|
|
75
|
-
type: "RemoveEcho";
|
|
76
|
-
animationId: string;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export const removeEcho = (animationId: string): RemoveEchoAction => {
|
|
80
|
-
return {
|
|
81
|
-
type: "RemoveEcho",
|
|
82
|
-
animationId,
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// Input-related actions.
|
|
87
|
-
type SetKeyHandlerAction = {
|
|
88
|
-
type: "SetKeyHandler";
|
|
89
|
-
keyHandler: KeyHandler;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
export const setKeyHandler = (keyHandler: KeyHandler): SetKeyHandlerAction => {
|
|
93
|
-
return {
|
|
94
|
-
type: "SetKeyHandler",
|
|
95
|
-
keyHandler,
|
|
96
|
-
};
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
type SetCursorAction = {
|
|
100
|
-
type: "SetCursor";
|
|
101
|
-
cursor: Cursor;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export const setCursor = (cursor: Cursor): SetCursorAction => {
|
|
105
|
-
return {
|
|
106
|
-
type: "SetCursor",
|
|
107
|
-
cursor,
|
|
108
|
-
};
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
// Gesture actions
|
|
112
|
-
type SetActiveNodesAction = {
|
|
113
|
-
type: "SetActiveNodes";
|
|
114
|
-
activeNodes: any;
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
export const setActiveNodes = (
|
|
118
|
-
activeNodes: ActiveNodesObj,
|
|
119
|
-
): SetActiveNodesAction => {
|
|
120
|
-
return {
|
|
121
|
-
type: "SetActiveNodes",
|
|
122
|
-
activeNodes,
|
|
123
|
-
};
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
type PressKeyAction = {
|
|
127
|
-
type: "PressKey";
|
|
128
|
-
key: Key;
|
|
129
|
-
initialBounds: Bound;
|
|
130
|
-
inPopover: boolean;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
export const pressKey = (
|
|
134
|
-
key: Key,
|
|
135
|
-
initialBounds: Bound,
|
|
136
|
-
inPopover: any,
|
|
137
|
-
): PressKeyAction => {
|
|
138
|
-
return {
|
|
139
|
-
type: "PressKey",
|
|
140
|
-
key,
|
|
141
|
-
initialBounds,
|
|
142
|
-
inPopover,
|
|
143
|
-
};
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
export type Action =
|
|
147
|
-
| DismissKeypadAction
|
|
148
|
-
| ActivateKeypadAction
|
|
149
|
-
| ConfigureKeypadAction
|
|
150
|
-
| SetPageSizeAction
|
|
151
|
-
| RemoveEchoAction
|
|
152
|
-
| SetKeyHandlerAction
|
|
153
|
-
| SetCursorAction
|
|
154
|
-
| SetActiveNodesAction
|
|
155
|
-
| PressKeyAction;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import KeyConfigs from "../../../data/key-configs";
|
|
2
|
-
import {EchoAnimationType} from "../../../enums";
|
|
3
|
-
|
|
4
|
-
import type {Action} from "./actions";
|
|
5
|
-
import type {EchoState} from "./types";
|
|
6
|
-
|
|
7
|
-
// Used to generate unique animation IDs for the echo animations. The actual
|
|
8
|
-
// values are irrelevant as long as they are unique.
|
|
9
|
-
let _lastAnimationId = 0;
|
|
10
|
-
|
|
11
|
-
const initialEchoState = {
|
|
12
|
-
echoes: [],
|
|
13
|
-
} as const;
|
|
14
|
-
|
|
15
|
-
const echoReducer = function (
|
|
16
|
-
state: EchoState = initialEchoState,
|
|
17
|
-
action: Action,
|
|
18
|
-
): EchoState {
|
|
19
|
-
switch (action.type) {
|
|
20
|
-
case "PressKey":
|
|
21
|
-
const keyConfig = KeyConfigs[action.key];
|
|
22
|
-
|
|
23
|
-
// Add in the echo animation if the user performs a math
|
|
24
|
-
// operation.
|
|
25
|
-
if (keyConfig.type === "VALUE" || keyConfig.type === "OPERATOR") {
|
|
26
|
-
return {
|
|
27
|
-
...state,
|
|
28
|
-
echoes: [
|
|
29
|
-
...state.echoes,
|
|
30
|
-
{
|
|
31
|
-
animationId: "" + _lastAnimationId++,
|
|
32
|
-
animationType: action.inPopover
|
|
33
|
-
? EchoAnimationType.LONG_FADE_ONLY
|
|
34
|
-
: EchoAnimationType.FADE_ONLY,
|
|
35
|
-
id: keyConfig.id,
|
|
36
|
-
initialBounds: action.initialBounds,
|
|
37
|
-
},
|
|
38
|
-
],
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
return state;
|
|
42
|
-
|
|
43
|
-
case "RemoveEcho":
|
|
44
|
-
const remainingEchoes = state.echoes.filter((echo) => {
|
|
45
|
-
return echo.animationId !== action.animationId;
|
|
46
|
-
});
|
|
47
|
-
return {
|
|
48
|
-
...state,
|
|
49
|
-
echoes: remainingEchoes,
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
default:
|
|
53
|
-
return state;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export default echoReducer;
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import * as Redux from "redux";
|
|
2
|
-
|
|
3
|
-
import GestureManager from "../gesture-manager";
|
|
4
|
-
|
|
5
|
-
import {setActiveNodes, pressKey} from "./actions";
|
|
6
|
-
import echoReducer from "./echo-reducer";
|
|
7
|
-
import inputReducer from "./input-reducer";
|
|
8
|
-
import keypadReducer from "./keypad-reducer";
|
|
9
|
-
import layoutReducer from "./layout-reducer";
|
|
10
|
-
import {defaultKeypadType, keypadForType} from "./shared";
|
|
11
|
-
|
|
12
|
-
import type Key from "../../../data/keys";
|
|
13
|
-
import type {LayoutProps, ActiveNodesObj} from "../../../types";
|
|
14
|
-
import type {Action} from "./actions";
|
|
15
|
-
import type {GestureState} from "./types";
|
|
16
|
-
|
|
17
|
-
export const createStore = () => {
|
|
18
|
-
// TODO(matthewc)[LC-752]: gestureReducer can't be moved from this file
|
|
19
|
-
// because it depends on `store` being in scope (see note below)
|
|
20
|
-
const createGestureManager = (swipeEnabled: boolean) => {
|
|
21
|
-
return new GestureManager(
|
|
22
|
-
{
|
|
23
|
-
swipeEnabled,
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
onActiveNodesChanged: (activeNodes: ActiveNodesObj) => {
|
|
27
|
-
store.dispatch(setActiveNodes(activeNodes));
|
|
28
|
-
},
|
|
29
|
-
onClick: (
|
|
30
|
-
key: Key,
|
|
31
|
-
layoutProps: LayoutProps,
|
|
32
|
-
inPopover: boolean,
|
|
33
|
-
) => {
|
|
34
|
-
store.dispatch(
|
|
35
|
-
pressKey(key, layoutProps.initialBounds, inPopover),
|
|
36
|
-
);
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
[],
|
|
40
|
-
["BACKSPACE", "UP", "RIGHT", "DOWN", "LEFT"],
|
|
41
|
-
);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const initialGestureState = {
|
|
45
|
-
popover: null,
|
|
46
|
-
focus: null,
|
|
47
|
-
gestureManager: createGestureManager(
|
|
48
|
-
keypadForType[defaultKeypadType].numPages > 1,
|
|
49
|
-
),
|
|
50
|
-
} as const;
|
|
51
|
-
|
|
52
|
-
const gestureReducer = function (
|
|
53
|
-
state: GestureState = initialGestureState,
|
|
54
|
-
action: Action,
|
|
55
|
-
): GestureState {
|
|
56
|
-
switch (action.type) {
|
|
57
|
-
case "DismissKeypad":
|
|
58
|
-
// NOTE(charlie): In the past, we enforced the "gesture manager
|
|
59
|
-
// will not receive any events when the keypad is hidden"
|
|
60
|
-
// assumption by assuming that the keypad would be hidden when
|
|
61
|
-
// dismissed and, as such, that none of its managed DOM nodes
|
|
62
|
-
// would be able to receive touch events. However, on mobile
|
|
63
|
-
// Safari, we're seeing that some of the keys receive touch
|
|
64
|
-
// events even when off-screen, inexplicably. So, to guard
|
|
65
|
-
// against that bug and make the contract explicit, we enable
|
|
66
|
-
// and disable event tracking on activation and dismissal.
|
|
67
|
-
state.gestureManager.disableEventTracking();
|
|
68
|
-
return state;
|
|
69
|
-
|
|
70
|
-
case "ActivateKeypad":
|
|
71
|
-
state.gestureManager.enableEventTracking();
|
|
72
|
-
return state;
|
|
73
|
-
|
|
74
|
-
case "SetActiveNodes":
|
|
75
|
-
return {
|
|
76
|
-
...state,
|
|
77
|
-
...action.activeNodes,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
case "ConfigureKeypad":
|
|
81
|
-
const {keypadType} = action.configuration;
|
|
82
|
-
const {numPages} = keypadForType[keypadType];
|
|
83
|
-
const swipeEnabled = numPages > 1;
|
|
84
|
-
return {
|
|
85
|
-
popover: null,
|
|
86
|
-
focus: null,
|
|
87
|
-
gestureManager: createGestureManager(swipeEnabled),
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
default:
|
|
91
|
-
return state;
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const reducer = Redux.combineReducers({
|
|
96
|
-
input: inputReducer,
|
|
97
|
-
keypad: keypadReducer,
|
|
98
|
-
gestures: gestureReducer,
|
|
99
|
-
echoes: echoReducer,
|
|
100
|
-
layout: layoutReducer,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// TODO(charlie): This non-inlined return is necessary so as to allow the
|
|
104
|
-
// gesture manager to dispatch actions on the store in its callbacks. We
|
|
105
|
-
// should come up with a better pattern to remove the two-way dependency.
|
|
106
|
-
// eslint-disable-next-line import/no-deprecated
|
|
107
|
-
const store = Redux.createStore(reducer);
|
|
108
|
-
|
|
109
|
-
return store;
|
|
110
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import KeyConfigs from "../../../data/key-configs";
|
|
2
|
-
import {CursorContext} from "../../input/cursor-contexts";
|
|
3
|
-
|
|
4
|
-
import type {Cursor, KeyHandler} from "../../../types";
|
|
5
|
-
import type {Action} from "./actions";
|
|
6
|
-
import type {InputState} from "./types";
|
|
7
|
-
|
|
8
|
-
const initialInputState: {
|
|
9
|
-
keyHandler: KeyHandler | null;
|
|
10
|
-
cursor: Cursor;
|
|
11
|
-
} = {
|
|
12
|
-
keyHandler: null,
|
|
13
|
-
cursor: {
|
|
14
|
-
context: CursorContext.NONE,
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const inputReducer = function (
|
|
19
|
-
state: InputState = initialInputState,
|
|
20
|
-
action: Action,
|
|
21
|
-
): InputState {
|
|
22
|
-
switch (action.type) {
|
|
23
|
-
case "SetKeyHandler":
|
|
24
|
-
return {
|
|
25
|
-
...state,
|
|
26
|
-
keyHandler: action.keyHandler,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
case "PressKey":
|
|
30
|
-
const keyConfig = KeyConfigs[action.key];
|
|
31
|
-
if (keyConfig.type !== "KEYPAD_NAVIGATION") {
|
|
32
|
-
// This is probably an anti-pattern but it works for the
|
|
33
|
-
// case where we don't actually control the state but we
|
|
34
|
-
// still want to communicate with the other object
|
|
35
|
-
return {
|
|
36
|
-
...state,
|
|
37
|
-
cursor: state.keyHandler?.(keyConfig.id),
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// TODO(kevinb) get state from MathQuill and store it?
|
|
42
|
-
return state;
|
|
43
|
-
|
|
44
|
-
case "SetCursor":
|
|
45
|
-
return {
|
|
46
|
-
...state,
|
|
47
|
-
cursor: action.cursor,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
default:
|
|
51
|
-
return state;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export default inputReducer;
|