@khanacademy/math-input 0.7.2 → 1.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 +16 -0
- package/dist/components/compute-layout-parameters.d.ts +38 -0
- package/dist/components/compute-layout-parameters.js.flow +49 -0
- package/dist/components/corner-decal.d.ts +12 -0
- package/dist/components/corner-decal.js.flow +15 -0
- package/dist/components/echo-manager.d.ts +26 -0
- package/dist/components/echo-manager.js.flow +29 -0
- package/dist/components/empty-keypad-button.d.ts +13 -0
- package/dist/components/empty-keypad-button.js.flow +23 -0
- package/dist/components/expression-keypad.d.ts +22 -0
- package/dist/components/expression-keypad.js.flow +32 -0
- package/dist/components/fraction-keypad.d.ts +21 -0
- package/dist/components/fraction-keypad.js.flow +30 -0
- package/dist/components/gesture-manager.d.ts +74 -0
- package/dist/components/gesture-manager.js.flow +82 -0
- package/dist/components/gesture-state-machine.d.ts +105 -0
- package/dist/components/gesture-state-machine.js.flow +118 -0
- package/dist/components/icon.d.ts +15 -0
- package/dist/components/icon.js.flow +18 -0
- package/dist/components/input/__tests__/test-math-wrapper.d.ts +8 -0
- package/dist/components/input/__tests__/test-math-wrapper.js.flow +14 -0
- package/dist/components/input/cursor-handle.d.ts +1 -1
- package/dist/components/input/cursor-handle.js.flow +1 -1
- package/dist/components/input/drag-listener.d.ts +13 -0
- package/dist/components/input/drag-listener.js.flow +19 -0
- package/dist/components/input/math-input.d.ts +5 -4
- package/dist/components/input/math-input.js.flow +5 -4
- package/dist/components/input/math-wrapper.d.ts +110 -0
- package/dist/components/input/math-wrapper.js.flow +125 -0
- package/dist/components/input/scroll-into-view.d.ts +11 -0
- package/dist/components/input/scroll-into-view.js.flow +20 -0
- package/dist/components/keypad/button-assets.d.ts +4 -3
- package/dist/components/keypad/button-assets.js.flow +3 -3
- package/dist/components/keypad/button.d.ts +1 -2
- package/dist/components/keypad/button.js.flow +1 -2
- package/dist/components/keypad/index.d.ts +1 -1
- package/dist/components/keypad/index.js.flow +1 -3
- package/dist/components/keypad/keypad-page-items.d.ts +15 -10
- package/dist/components/keypad/keypad-page-items.js.flow +20 -10
- package/dist/components/keypad-button.d.ts +52 -0
- package/dist/components/keypad-button.js.flow +79 -0
- package/dist/components/keypad-container.d.ts +40 -0
- package/dist/components/keypad-container.js.flow +58 -0
- package/dist/components/keypad.d.ts +31 -0
- package/dist/components/keypad.js.flow +40 -0
- package/dist/components/many-keypad-button.d.ts +15 -0
- package/dist/components/many-keypad-button.js.flow +17 -0
- package/dist/components/math-icon.d.ts +16 -0
- package/dist/components/math-icon.js.flow +19 -0
- package/dist/components/multi-symbol-grid.d.ts +14 -0
- package/dist/components/multi-symbol-grid.js.flow +16 -0
- package/dist/components/multi-symbol-popover.d.ts +12 -0
- package/dist/components/multi-symbol-popover.js.flow +15 -0
- package/dist/components/navigation-pad.d.ts +14 -0
- package/dist/components/navigation-pad.js.flow +16 -0
- package/dist/components/node-manager.d.ts +50 -0
- package/dist/components/node-manager.js.flow +62 -0
- package/dist/components/popover-manager.d.ts +13 -0
- package/dist/components/popover-manager.js.flow +15 -0
- package/dist/components/popover-state-machine.d.ts +68 -0
- package/dist/components/popover-state-machine.js.flow +77 -0
- package/dist/components/provided-keypad.d.ts +8 -10
- package/dist/components/provided-keypad.js.flow +8 -10
- package/dist/components/styles.d.ts +6 -0
- package/dist/components/styles.js.flow +13 -0
- package/dist/components/svg-icon.d.ts +12 -0
- package/dist/components/svg-icon.js.flow +15 -0
- package/dist/components/tabbar/icons.d.ts +3 -2
- package/dist/components/tabbar/icons.js.flow +3 -2
- package/dist/components/tabbar/item.d.ts +1 -2
- package/dist/components/tabbar/item.js.flow +1 -2
- package/dist/components/tabbar/tabbar.d.ts +3 -3
- package/dist/components/tabbar/tabbar.js.flow +3 -3
- package/dist/components/text-icon.d.ts +13 -0
- package/dist/components/text-icon.js.flow +16 -0
- package/dist/components/touchable-keypad-button.d.ts +30 -0
- package/dist/components/touchable-keypad-button.js.flow +35 -0
- package/dist/components/two-page-keypad.d.ts +20 -0
- package/dist/components/two-page-keypad.js.flow +30 -0
- package/dist/components/velocity-tracker.d.ts +48 -0
- package/dist/components/velocity-tracker.js.flow +54 -0
- package/dist/es/index.css +0 -3
- package/dist/es/index.js +933 -1065
- package/dist/es/index.js.map +1 -1
- package/dist/fake-react-native-web/text.d.ts +2 -1
- package/dist/fake-react-native-web/text.js.flow +2 -1
- package/dist/fake-react-native-web/view.d.ts +3 -2
- package/dist/fake-react-native-web/view.js.flow +3 -2
- package/dist/index.css +0 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +977 -1090
- package/dist/index.js.flow +1 -4
- package/dist/index.js.map +1 -1
- package/dist/store/actions.d.ts +64 -0
- package/dist/store/actions.js.flow +100 -0
- package/dist/store/echo-reducer.d.ts +4 -0
- package/dist/store/echo-reducer.js.flow +10 -0
- package/dist/store/index.d.ts +10 -1
- package/dist/store/index.js.flow +17 -1
- package/dist/store/input-reducer.d.ts +4 -0
- package/dist/store/input-reducer.js.flow +13 -0
- package/dist/store/keypad-reducer.d.ts +4 -0
- package/dist/store/keypad-reducer.js.flow +13 -0
- package/dist/store/layout-reducer.d.ts +4 -0
- package/dist/store/layout-reducer.js.flow +13 -0
- package/dist/store/pager-reducer.d.ts +4 -0
- package/dist/store/pager-reducer.js.flow +13 -0
- package/dist/store/shared.d.ts +6 -0
- package/dist/store/shared.js.flow +13 -0
- package/dist/store/types.d.ts +58 -0
- package/dist/store/types.js.flow +64 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.js.flow +73 -0
- package/less/overrides.less +0 -6
- package/package.json +1 -1
- package/src/components/__tests__/{gesture-state-machine_test.js → gesture-state-machine.test.ts} +5 -1
- package/src/components/__tests__/{two-page-keypad_test.js → two-page-keypad.test.tsx} +0 -2
- package/src/components/{corner-decal.js → corner-decal.tsx} +6 -5
- package/src/components/{echo-manager.js → echo-manager.tsx} +29 -24
- package/src/components/{empty-keypad-button.js → empty-keypad-button.tsx} +17 -10
- package/src/components/{expression-keypad.js → expression-keypad.tsx} +27 -25
- package/src/components/{fraction-keypad.js → fraction-keypad.tsx} +21 -16
- package/src/components/{gesture-manager.js → gesture-manager.ts} +10 -4
- package/src/components/{gesture-state-machine.js → gesture-state-machine.ts} +49 -3
- package/src/components/{icon.js → icon.tsx} +12 -14
- package/src/components/input/cursor-handle.tsx +1 -1
- package/src/components/input/{drag-listener.js → drag-listener.ts} +4 -0
- package/src/components/input/math-input.tsx +10 -9
- package/src/components/input/{math-wrapper.js → math-wrapper.ts} +10 -6
- package/src/components/input/{scroll-into-view.js → scroll-into-view.ts} +5 -15
- package/src/components/keypad/button-assets.tsx +4 -5
- package/src/components/keypad/button.tsx +1 -2
- package/src/components/keypad/index.tsx +2 -2
- package/src/components/keypad/keypad-page-items.tsx +33 -10
- package/src/components/{keypad-button.js → keypad-button.tsx} +42 -37
- package/src/components/{keypad-container.js → keypad-container.tsx} +42 -24
- package/src/components/{keypad.js → keypad.tsx} +32 -24
- package/src/components/{many-keypad-button.js → many-keypad-button.tsx} +8 -6
- package/src/components/{math-icon.js → math-icon.tsx} +7 -6
- package/src/components/{multi-symbol-grid.js → multi-symbol-grid.tsx} +8 -8
- package/src/components/{multi-symbol-popover.js → multi-symbol-popover.tsx} +5 -6
- package/src/components/{navigation-pad.js → navigation-pad.tsx} +7 -6
- package/src/components/{node-manager.js → node-manager.ts} +16 -4
- package/src/components/{popover-manager.js → popover-manager.tsx} +13 -16
- package/src/components/{popover-state-machine.js → popover-state-machine.ts} +13 -2
- package/src/components/prop-types.js +1 -67
- package/src/components/provided-keypad.tsx +16 -23
- package/src/components/{svg-icon.js → svg-icon.tsx} +5 -6
- package/src/components/tabbar/icons.tsx +4 -2
- package/src/components/tabbar/item.tsx +1 -3
- package/src/components/tabbar/{tabbar.stories.js → tabbar.stories.tsx} +10 -1
- package/src/components/tabbar/tabbar.tsx +3 -3
- package/src/components/{text-icon.js → text-icon.tsx} +7 -6
- package/src/components/{touchable-keypad-button.js → touchable-keypad-button.tsx} +19 -16
- package/src/components/{two-page-keypad.js → two-page-keypad.tsx} +13 -9
- package/src/components/{velocity-tracker.js → velocity-tracker.ts} +14 -4
- package/src/fake-react-native-web/text.tsx +2 -1
- package/src/fake-react-native-web/view.tsx +3 -2
- package/src/index.ts +1 -4
- package/src/math-input.stories.tsx +67 -0
- package/src/store/actions.ts +178 -0
- package/src/store/echo-reducer.ts +61 -0
- package/src/store/index.ts +39 -449
- package/src/store/input-reducer.ts +56 -0
- package/src/store/keypad-reducer.ts +59 -0
- package/src/store/layout-reducer.ts +134 -0
- package/src/store/pager-reducer.ts +125 -0
- package/src/store/shared.ts +12 -0
- package/src/store/types.ts +82 -0
- package/src/types.ts +81 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/src/actions/index.js +0 -57
- package/src/components/app.js +0 -73
- package/src/demo.js +0 -9
- package/src/native-app.js +0 -85
- /package/src/components/__tests__/{node-manager_test.js → node-manager.test.ts} +0 -0
- /package/src/components/{compute-layout-parameters.js → compute-layout-parameters.ts} +0 -0
- /package/src/components/input/__tests__/{context-tracking_test.js → context-tracking.test.ts} +0 -0
- /package/src/components/input/__tests__/{mathquill_test.js → mathquill.test.ts} +0 -0
- /package/src/components/input/__tests__/{test-math-wrapper.jsx → test-math-wrapper.ts} +0 -0
- /package/src/components/keypad/{button.stories.js → button.stories.tsx} +0 -0
- /package/src/components/{styles.js → styles.ts} +0 -0
- /package/src/components/tabbar/__tests__/{tabbar_test.js → tabbar.test.tsx} +0 -0
package/src/store/index.ts
CHANGED
|
@@ -1,292 +1,52 @@
|
|
|
1
1
|
import * as Redux from "redux";
|
|
2
2
|
|
|
3
|
-
import {tabletCutoffPx} from "../components/common-style";
|
|
4
|
-
import {computeLayoutParameters} from "../components/compute-layout-parameters";
|
|
5
|
-
import ExpressionKeypad from "../components/expression-keypad";
|
|
6
|
-
import FractionKeypad from "../components/fraction-keypad";
|
|
7
3
|
import GestureManager from "../components/gesture-manager";
|
|
8
|
-
import * as CursorContexts from "../components/input/cursor-contexts";
|
|
9
|
-
import VelocityTracker from "../components/velocity-tracker";
|
|
10
|
-
import {
|
|
11
|
-
DeviceOrientations,
|
|
12
|
-
DeviceTypes,
|
|
13
|
-
EchoAnimationTypes,
|
|
14
|
-
KeyTypes,
|
|
15
|
-
KeypadTypes,
|
|
16
|
-
LayoutModes,
|
|
17
|
-
} from "../consts";
|
|
18
|
-
import KeyConfigs from "../data/key-configs";
|
|
19
4
|
import Keys from "../data/keys";
|
|
20
5
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const inputReducer = function (state = initialInputState, action: any) {
|
|
38
|
-
switch (action.type) {
|
|
39
|
-
case "SetKeyHandler":
|
|
40
|
-
return {
|
|
41
|
-
...state,
|
|
42
|
-
keyHandler: action.keyHandler,
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
case "PressKey":
|
|
46
|
-
const keyConfig = KeyConfigs[action.key];
|
|
47
|
-
if (keyConfig.type !== KeyTypes.KEYPAD_NAVIGATION) {
|
|
48
|
-
// This is probably an anti-pattern but it works for the
|
|
49
|
-
// case where we don't actually control the state but we
|
|
50
|
-
// still want to communicate with the other object
|
|
51
|
-
return {
|
|
52
|
-
...state,
|
|
53
|
-
cursor: state.keyHandler(keyConfig.id),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// TODO(kevinb) get state from MathQuill and store it?
|
|
58
|
-
return state;
|
|
59
|
-
|
|
60
|
-
case "SetCursor":
|
|
61
|
-
return {
|
|
62
|
-
...state,
|
|
63
|
-
cursor: action.cursor,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
default:
|
|
67
|
-
return state;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const defaultKeypadType = KeypadTypes.EXPRESSION;
|
|
72
|
-
|
|
73
|
-
const initialKeypadState = {
|
|
74
|
-
extraKeys: ["x", "y", Keys.THETA, Keys.PI],
|
|
75
|
-
keypadType: defaultKeypadType,
|
|
76
|
-
active: false,
|
|
77
|
-
} as const;
|
|
78
|
-
|
|
79
|
-
const keypadReducer = function (
|
|
80
|
-
state = initialKeypadState,
|
|
81
|
-
action: {
|
|
82
|
-
type: string;
|
|
83
|
-
},
|
|
84
|
-
) {
|
|
85
|
-
switch (action.type) {
|
|
86
|
-
case "DismissKeypad":
|
|
87
|
-
return {
|
|
88
|
-
...state,
|
|
89
|
-
active: false,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
case "ActivateKeypad":
|
|
93
|
-
return {
|
|
94
|
-
...state,
|
|
95
|
-
active: true,
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
case "ConfigureKeypad":
|
|
99
|
-
return {
|
|
100
|
-
...state,
|
|
101
|
-
// Default `extraKeys` to the empty array.
|
|
102
|
-
extraKeys: [],
|
|
103
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'configuration' does not exist on type '{ type: string; }'.
|
|
104
|
-
...action.configuration,
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
case "PressKey":
|
|
108
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
|
|
109
|
-
const keyConfig = KeyConfigs[action.key];
|
|
110
|
-
// NOTE(charlie): Our keypad system operates by triggering key
|
|
111
|
-
// presses with key IDs in a dumb manner, such that the keys
|
|
112
|
-
// don't know what they can do--instead, the store is
|
|
113
|
-
// responsible for interpreting key presses and triggering the
|
|
114
|
-
// right actions when they occur. Hence, we figure off a
|
|
115
|
-
// dismissal here rather than dispatching a dismiss action in
|
|
116
|
-
// the first place.
|
|
117
|
-
if (keyConfig.id === Keys.DISMISS) {
|
|
118
|
-
return keypadReducer(state, {type: "DismissKeypad"});
|
|
119
|
-
}
|
|
120
|
-
return state;
|
|
121
|
-
|
|
122
|
-
default:
|
|
123
|
-
return state;
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
// We default to the right-most page. This is done so-as to enforce a
|
|
128
|
-
// consistent orientation between the view pager layout and the flattened
|
|
129
|
-
// layout, where our default page appears on the far right.
|
|
130
|
-
const getDefaultPage = (numPages) => numPages - 1;
|
|
131
|
-
|
|
132
|
-
const initialPagerState = {
|
|
133
|
-
animateToPosition: false,
|
|
134
|
-
currentPage: getDefaultPage(keypadForType[defaultKeypadType].numPages),
|
|
135
|
-
// The cumulative differential in the horizontal direction for the
|
|
136
|
-
// current swipe.
|
|
137
|
-
dx: 0,
|
|
138
|
-
numPages: keypadForType[defaultKeypadType].numPages,
|
|
139
|
-
pageWidthPx: 0,
|
|
140
|
-
velocityTracker: new VelocityTracker(),
|
|
141
|
-
} as const;
|
|
142
|
-
|
|
143
|
-
const pagerReducer = function (
|
|
144
|
-
state = initialPagerState,
|
|
145
|
-
action: {
|
|
146
|
-
type: string;
|
|
147
|
-
},
|
|
148
|
-
) {
|
|
149
|
-
switch (action.type) {
|
|
150
|
-
case "ConfigureKeypad":
|
|
151
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'configuration' does not exist on type '{ type: string; }'.
|
|
152
|
-
const {keypadType} = action.configuration;
|
|
153
|
-
const {numPages} = keypadForType[keypadType];
|
|
154
|
-
return {
|
|
155
|
-
...state,
|
|
156
|
-
numPages,
|
|
157
|
-
animateToPosition: false,
|
|
158
|
-
currentPage: getDefaultPage(numPages),
|
|
159
|
-
dx: 0,
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
case "SetPageSize":
|
|
163
|
-
return {
|
|
164
|
-
...state,
|
|
165
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'pageWidthPx' does not exist on type '{ type: string; }'.
|
|
166
|
-
pageWidthPx: action.pageWidthPx,
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
case "PressKey":
|
|
170
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
|
|
171
|
-
const keyConfig = KeyConfigs[action.key];
|
|
172
|
-
|
|
173
|
-
// Reset the keypad page if the user performs a math operation.
|
|
174
|
-
if (
|
|
175
|
-
keyConfig.type === KeyTypes.VALUE ||
|
|
176
|
-
keyConfig.type === KeyTypes.OPERATOR
|
|
177
|
-
) {
|
|
178
|
-
return pagerReducer(state, {type: "ResetKeypadPage"});
|
|
179
|
-
}
|
|
180
|
-
return state;
|
|
181
|
-
|
|
182
|
-
case "ResetKeypadPage":
|
|
183
|
-
return {
|
|
184
|
-
...state,
|
|
185
|
-
animateToPosition: true,
|
|
186
|
-
// We start at the right-most page.
|
|
187
|
-
currentPage: getDefaultPage(state.numPages),
|
|
188
|
-
dx: 0,
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
case "PageKeypadRight":
|
|
192
|
-
const nextPage = Math.min(
|
|
193
|
-
state.currentPage + 1,
|
|
194
|
-
state.numPages - 1,
|
|
195
|
-
);
|
|
196
|
-
return {
|
|
197
|
-
...state,
|
|
198
|
-
animateToPosition: true,
|
|
199
|
-
currentPage: nextPage,
|
|
200
|
-
dx: 0,
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
case "PageKeypadLeft":
|
|
204
|
-
const prevPage = Math.max(state.currentPage - 1, 0);
|
|
205
|
-
return {
|
|
206
|
-
...state,
|
|
207
|
-
animateToPosition: true,
|
|
208
|
-
currentPage: prevPage,
|
|
209
|
-
dx: 0,
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
case "OnSwipeChange":
|
|
213
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
214
|
-
state.velocityTracker.push(action.dx);
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
...state,
|
|
218
|
-
animateToPosition: false,
|
|
219
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
220
|
-
dx: action.dx,
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
case "OnSwipeEnd":
|
|
224
|
-
const {pageWidthPx, velocityTracker} = state;
|
|
225
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
226
|
-
const {dx} = action;
|
|
227
|
-
const velocity = velocityTracker.getVelocity();
|
|
228
|
-
|
|
229
|
-
// NOTE(charlie): These will need refinement. The velocity comes
|
|
230
|
-
// from Framer.
|
|
231
|
-
const minFlingVelocity = 0.1;
|
|
232
|
-
const minFlingDistance = 10;
|
|
233
|
-
|
|
234
|
-
const shouldPageRight =
|
|
235
|
-
dx < -pageWidthPx / 2 ||
|
|
236
|
-
(velocity < -minFlingVelocity && dx < -minFlingDistance);
|
|
237
|
-
|
|
238
|
-
const shouldPageLeft =
|
|
239
|
-
dx > pageWidthPx / 2 ||
|
|
240
|
-
(velocity > minFlingVelocity && dx > minFlingDistance);
|
|
241
|
-
|
|
242
|
-
if (shouldPageRight) {
|
|
243
|
-
return pagerReducer(state, {type: "PageKeypadRight"});
|
|
244
|
-
} else if (shouldPageLeft) {
|
|
245
|
-
return pagerReducer(state, {type: "PageKeypadLeft"});
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return {
|
|
249
|
-
...state,
|
|
250
|
-
animateToPosition: true,
|
|
251
|
-
dx: 0,
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
default:
|
|
255
|
-
return state;
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
|
|
6
|
+
import {onSwipeChange, onSwipeEnd, setActiveNodes, pressKey} from "./actions";
|
|
7
|
+
import echoReducer from "./echo-reducer";
|
|
8
|
+
import inputReducer from "./input-reducer";
|
|
9
|
+
import keypadReducer from "./keypad-reducer";
|
|
10
|
+
import layoutReducer from "./layout-reducer";
|
|
11
|
+
import pagerReducer from "./pager-reducer";
|
|
12
|
+
import {defaultKeypadType, keypadForType} from "./shared";
|
|
13
|
+
|
|
14
|
+
import type {Key} from "../data/keys";
|
|
15
|
+
import type {LayoutProps, ActiveNodesObj} from "../types";
|
|
16
|
+
import type {Action} from "./actions";
|
|
17
|
+
import type {GestureState} from "./types";
|
|
18
|
+
|
|
19
|
+
export const createStore = () => {
|
|
20
|
+
// TODO(matthewc)[LC-752]: gestureReducer can't be moved from this file
|
|
21
|
+
// because it depends on `store` being in scope (see note below)
|
|
259
22
|
const createGestureManager = (swipeEnabled: boolean) => {
|
|
260
23
|
return new GestureManager(
|
|
261
24
|
{
|
|
262
25
|
swipeEnabled,
|
|
263
26
|
},
|
|
264
27
|
{
|
|
265
|
-
onSwipeChange: (dx) => {
|
|
266
|
-
store.dispatch(
|
|
267
|
-
type: "OnSwipeChange",
|
|
268
|
-
dx,
|
|
269
|
-
});
|
|
28
|
+
onSwipeChange: (dx: number) => {
|
|
29
|
+
store.dispatch(onSwipeChange(dx));
|
|
270
30
|
},
|
|
271
|
-
onSwipeEnd: (dx) => {
|
|
272
|
-
store.dispatch(
|
|
273
|
-
type: "OnSwipeEnd",
|
|
274
|
-
dx,
|
|
275
|
-
});
|
|
31
|
+
onSwipeEnd: (dx: number) => {
|
|
32
|
+
store.dispatch(onSwipeEnd(dx));
|
|
276
33
|
},
|
|
277
|
-
onActiveNodesChanged: (activeNodes) => {
|
|
278
|
-
store.dispatch(
|
|
279
|
-
type: "SetActiveNodes",
|
|
280
|
-
activeNodes,
|
|
281
|
-
});
|
|
34
|
+
onActiveNodesChanged: (activeNodes: ActiveNodesObj) => {
|
|
35
|
+
store.dispatch(setActiveNodes(activeNodes));
|
|
282
36
|
},
|
|
283
|
-
onClick: (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
37
|
+
onClick: (
|
|
38
|
+
key: Key,
|
|
39
|
+
layoutProps: LayoutProps,
|
|
40
|
+
inPopover: boolean,
|
|
41
|
+
) => {
|
|
42
|
+
store.dispatch(
|
|
43
|
+
pressKey(
|
|
44
|
+
key,
|
|
45
|
+
layoutProps.borders,
|
|
46
|
+
layoutProps.initialBounds,
|
|
47
|
+
inPopover,
|
|
48
|
+
),
|
|
49
|
+
);
|
|
290
50
|
},
|
|
291
51
|
},
|
|
292
52
|
[],
|
|
@@ -302,7 +62,10 @@ export const createStore = (): any => {
|
|
|
302
62
|
),
|
|
303
63
|
} as const;
|
|
304
64
|
|
|
305
|
-
const gestureReducer = function (
|
|
65
|
+
const gestureReducer = function (
|
|
66
|
+
state: GestureState = initialGestureState,
|
|
67
|
+
action: Action,
|
|
68
|
+
): GestureState {
|
|
306
69
|
switch (action.type) {
|
|
307
70
|
case "DismissKeypad":
|
|
308
71
|
// NOTE(charlie): In the past, we enforced the "gesture manager
|
|
@@ -342,179 +105,6 @@ export const createStore = (): any => {
|
|
|
342
105
|
}
|
|
343
106
|
};
|
|
344
107
|
|
|
345
|
-
// Used to generate unique animation IDs for the echo animations. The actual
|
|
346
|
-
// values are irrelevant as long as they are unique.
|
|
347
|
-
let _lastAnimationId = 0;
|
|
348
|
-
|
|
349
|
-
const initialEchoState = {
|
|
350
|
-
echoes: [],
|
|
351
|
-
} as const;
|
|
352
|
-
|
|
353
|
-
const echoReducer = function (state = initialEchoState, action) {
|
|
354
|
-
switch (action.type) {
|
|
355
|
-
case "PressKey":
|
|
356
|
-
const keyConfig = KeyConfigs[action.key];
|
|
357
|
-
|
|
358
|
-
// Add in the echo animation if the user performs a math
|
|
359
|
-
// operation.
|
|
360
|
-
if (
|
|
361
|
-
keyConfig.type === KeyTypes.VALUE ||
|
|
362
|
-
keyConfig.type === KeyTypes.OPERATOR
|
|
363
|
-
) {
|
|
364
|
-
return {
|
|
365
|
-
...state,
|
|
366
|
-
echoes: [
|
|
367
|
-
...state.echoes,
|
|
368
|
-
{
|
|
369
|
-
animationId: "" + _lastAnimationId++,
|
|
370
|
-
animationType: action.inPopover
|
|
371
|
-
? EchoAnimationTypes.LONG_FADE_ONLY
|
|
372
|
-
: EchoAnimationTypes.FADE_ONLY,
|
|
373
|
-
borders: action.borders,
|
|
374
|
-
id: keyConfig.id,
|
|
375
|
-
initialBounds: action.initialBounds,
|
|
376
|
-
},
|
|
377
|
-
],
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
return state;
|
|
381
|
-
|
|
382
|
-
case "RemoveEcho":
|
|
383
|
-
const remainingEchoes = state.echoes.filter((echo) => {
|
|
384
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'animationId' does not exist on type 'never'.
|
|
385
|
-
return echo.animationId !== action.animationId;
|
|
386
|
-
});
|
|
387
|
-
return {
|
|
388
|
-
...state,
|
|
389
|
-
echoes: remainingEchoes,
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
default:
|
|
393
|
-
return state;
|
|
394
|
-
}
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
const initialLayoutState = {
|
|
398
|
-
gridDimensions: {
|
|
399
|
-
numRows: keypadForType[defaultKeypadType].rows,
|
|
400
|
-
numColumns: keypadForType[defaultKeypadType].columns,
|
|
401
|
-
numMaxVisibleRows: keypadForType[defaultKeypadType].maxVisibleRows,
|
|
402
|
-
numPages: keypadForType[defaultKeypadType].numPages,
|
|
403
|
-
},
|
|
404
|
-
buttonDimensions: {
|
|
405
|
-
widthPx: 48,
|
|
406
|
-
heightPx: 48,
|
|
407
|
-
},
|
|
408
|
-
pageDimensions: {
|
|
409
|
-
pageWidthPx: 0,
|
|
410
|
-
pageHeightPx: 0,
|
|
411
|
-
},
|
|
412
|
-
layoutMode: LayoutModes.FULLSCREEN,
|
|
413
|
-
paginationEnabled: false,
|
|
414
|
-
navigationPadEnabled: false,
|
|
415
|
-
} as const;
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Compute the additional layout state based on the provided page and grid
|
|
419
|
-
* dimensions.
|
|
420
|
-
*/
|
|
421
|
-
const layoutParametersForDimensions = (
|
|
422
|
-
pageDimensions:
|
|
423
|
-
| {
|
|
424
|
-
pageHeightPx: never;
|
|
425
|
-
pageWidthPx: never;
|
|
426
|
-
}
|
|
427
|
-
| {
|
|
428
|
-
pageHeightPx: number;
|
|
429
|
-
pageWidthPx: number;
|
|
430
|
-
},
|
|
431
|
-
gridDimensions,
|
|
432
|
-
) => {
|
|
433
|
-
const {pageWidthPx, pageHeightPx} = pageDimensions;
|
|
434
|
-
|
|
435
|
-
// Determine the device type and orientation.
|
|
436
|
-
const deviceOrientation =
|
|
437
|
-
pageWidthPx > pageHeightPx
|
|
438
|
-
? DeviceOrientations.LANDSCAPE
|
|
439
|
-
: DeviceOrientations.PORTRAIT;
|
|
440
|
-
const deviceType =
|
|
441
|
-
Math.min(pageWidthPx, pageHeightPx) > tabletCutoffPx
|
|
442
|
-
? DeviceTypes.TABLET
|
|
443
|
-
: DeviceTypes.PHONE;
|
|
444
|
-
|
|
445
|
-
// Using that information, make some decisions (or assumptions)
|
|
446
|
-
// about the resulting layout.
|
|
447
|
-
const navigationPadEnabled = deviceType === DeviceTypes.TABLET;
|
|
448
|
-
const paginationEnabled =
|
|
449
|
-
deviceType === DeviceTypes.PHONE &&
|
|
450
|
-
deviceOrientation === DeviceOrientations.PORTRAIT;
|
|
451
|
-
|
|
452
|
-
const deviceInfo = {deviceOrientation, deviceType} as const;
|
|
453
|
-
const layoutOptions = {
|
|
454
|
-
navigationPadEnabled,
|
|
455
|
-
paginationEnabled,
|
|
456
|
-
// HACK(charlie): It's not great that we're making assumptions about
|
|
457
|
-
// the toolbar (which is rendered by webapp, and should always be
|
|
458
|
-
// visible and anchored to the bottom of the page for phone and
|
|
459
|
-
// tablet exercises). But this is primarily a heuristic (the goal is
|
|
460
|
-
// to preserve a 'good' amount of space between the top of the
|
|
461
|
-
// keypad and the top of the page) so we afford to have some margin
|
|
462
|
-
// of error.
|
|
463
|
-
toolbarEnabled: true,
|
|
464
|
-
} as const;
|
|
465
|
-
|
|
466
|
-
return {
|
|
467
|
-
...computeLayoutParameters(
|
|
468
|
-
gridDimensions,
|
|
469
|
-
pageDimensions,
|
|
470
|
-
deviceInfo,
|
|
471
|
-
layoutOptions,
|
|
472
|
-
),
|
|
473
|
-
// Pass along some of the layout information, so that other
|
|
474
|
-
// components in the heirarchy can adapt appropriately.
|
|
475
|
-
navigationPadEnabled,
|
|
476
|
-
paginationEnabled,
|
|
477
|
-
};
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
const layoutReducer = function (state = initialLayoutState, action: any) {
|
|
481
|
-
switch (action.type) {
|
|
482
|
-
case "ConfigureKeypad":
|
|
483
|
-
const {keypadType} = action.configuration;
|
|
484
|
-
const gridDimensions = {
|
|
485
|
-
numRows: keypadForType[keypadType].rows,
|
|
486
|
-
numColumns: keypadForType[keypadType].columns,
|
|
487
|
-
numMaxVisibleRows: keypadForType[keypadType].maxVisibleRows,
|
|
488
|
-
numPages: keypadForType[keypadType].numPages,
|
|
489
|
-
} as const;
|
|
490
|
-
|
|
491
|
-
return {
|
|
492
|
-
...state,
|
|
493
|
-
...layoutParametersForDimensions(
|
|
494
|
-
state.pageDimensions,
|
|
495
|
-
gridDimensions,
|
|
496
|
-
),
|
|
497
|
-
gridDimensions,
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
case "SetPageSize":
|
|
501
|
-
const {pageWidthPx, pageHeightPx} = action;
|
|
502
|
-
const pageDimensions = {pageWidthPx, pageHeightPx} as const;
|
|
503
|
-
|
|
504
|
-
return {
|
|
505
|
-
...state,
|
|
506
|
-
...layoutParametersForDimensions(
|
|
507
|
-
pageDimensions,
|
|
508
|
-
state.gridDimensions,
|
|
509
|
-
),
|
|
510
|
-
pageDimensions,
|
|
511
|
-
};
|
|
512
|
-
|
|
513
|
-
default:
|
|
514
|
-
return state;
|
|
515
|
-
}
|
|
516
|
-
};
|
|
517
|
-
|
|
518
108
|
const reducer = Redux.combineReducers({
|
|
519
109
|
input: inputReducer,
|
|
520
110
|
keypad: keypadReducer,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as CursorContexts from "../components/input/cursor-contexts";
|
|
2
|
+
import {KeyTypes} from "../consts";
|
|
3
|
+
import KeyConfigs from "../data/key-configs";
|
|
4
|
+
|
|
5
|
+
import type {Cursor, KeyHandler} from "../types";
|
|
6
|
+
import type {Action} from "./actions";
|
|
7
|
+
import type {InputState} from "./types";
|
|
8
|
+
|
|
9
|
+
const initialInputState: {
|
|
10
|
+
keyHandler: KeyHandler | null;
|
|
11
|
+
cursor: Cursor;
|
|
12
|
+
} = {
|
|
13
|
+
keyHandler: null,
|
|
14
|
+
cursor: {
|
|
15
|
+
context: CursorContexts.NONE,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const inputReducer = function (
|
|
20
|
+
state: InputState = initialInputState,
|
|
21
|
+
action: Action,
|
|
22
|
+
): InputState {
|
|
23
|
+
switch (action.type) {
|
|
24
|
+
case "SetKeyHandler":
|
|
25
|
+
return {
|
|
26
|
+
...state,
|
|
27
|
+
keyHandler: action.keyHandler,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
case "PressKey":
|
|
31
|
+
const keyConfig = KeyConfigs[action.key];
|
|
32
|
+
if (keyConfig.type !== KeyTypes.KEYPAD_NAVIGATION) {
|
|
33
|
+
// This is probably an anti-pattern but it works for the
|
|
34
|
+
// case where we don't actually control the state but we
|
|
35
|
+
// still want to communicate with the other object
|
|
36
|
+
return {
|
|
37
|
+
...state,
|
|
38
|
+
cursor: state.keyHandler?.(keyConfig.id),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// TODO(kevinb) get state from MathQuill and store it?
|
|
43
|
+
return state;
|
|
44
|
+
|
|
45
|
+
case "SetCursor":
|
|
46
|
+
return {
|
|
47
|
+
...state,
|
|
48
|
+
cursor: action.cursor,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
default:
|
|
52
|
+
return state;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default inputReducer;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import KeyConfigs from "../data/key-configs";
|
|
2
|
+
import Keys from "../data/keys";
|
|
3
|
+
|
|
4
|
+
import {defaultKeypadType} from "./shared";
|
|
5
|
+
|
|
6
|
+
import type {Action} from "./actions";
|
|
7
|
+
import type {KeypadState} from "./types";
|
|
8
|
+
|
|
9
|
+
const initialKeypadState = {
|
|
10
|
+
extraKeys: ["x", "y", Keys.THETA, Keys.PI],
|
|
11
|
+
keypadType: defaultKeypadType,
|
|
12
|
+
active: false,
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
const keypadReducer = function (
|
|
16
|
+
state: KeypadState = initialKeypadState,
|
|
17
|
+
action: Action,
|
|
18
|
+
): KeypadState {
|
|
19
|
+
switch (action.type) {
|
|
20
|
+
case "DismissKeypad":
|
|
21
|
+
return {
|
|
22
|
+
...state,
|
|
23
|
+
active: false,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
case "ActivateKeypad":
|
|
27
|
+
return {
|
|
28
|
+
...state,
|
|
29
|
+
active: true,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
case "ConfigureKeypad":
|
|
33
|
+
return {
|
|
34
|
+
...state,
|
|
35
|
+
// Default `extraKeys` to the empty array.
|
|
36
|
+
extraKeys: [],
|
|
37
|
+
...action.configuration,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
case "PressKey":
|
|
41
|
+
const keyConfig = KeyConfigs[action.key];
|
|
42
|
+
// NOTE(charlie): Our keypad system operates by triggering key
|
|
43
|
+
// presses with key IDs in a dumb manner, such that the keys
|
|
44
|
+
// don't know what they can do--instead, the store is
|
|
45
|
+
// responsible for interpreting key presses and triggering the
|
|
46
|
+
// right actions when they occur. Hence, we figure off a
|
|
47
|
+
// dismissal here rather than dispatching a dismiss action in
|
|
48
|
+
// the first place.
|
|
49
|
+
if (keyConfig.id === Keys.DISMISS) {
|
|
50
|
+
return keypadReducer(state, {type: "DismissKeypad"});
|
|
51
|
+
}
|
|
52
|
+
return state;
|
|
53
|
+
|
|
54
|
+
default:
|
|
55
|
+
return state;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default keypadReducer;
|