@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.
Files changed (183) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/components/compute-layout-parameters.d.ts +38 -0
  3. package/dist/components/compute-layout-parameters.js.flow +49 -0
  4. package/dist/components/corner-decal.d.ts +12 -0
  5. package/dist/components/corner-decal.js.flow +15 -0
  6. package/dist/components/echo-manager.d.ts +26 -0
  7. package/dist/components/echo-manager.js.flow +29 -0
  8. package/dist/components/empty-keypad-button.d.ts +13 -0
  9. package/dist/components/empty-keypad-button.js.flow +23 -0
  10. package/dist/components/expression-keypad.d.ts +22 -0
  11. package/dist/components/expression-keypad.js.flow +32 -0
  12. package/dist/components/fraction-keypad.d.ts +21 -0
  13. package/dist/components/fraction-keypad.js.flow +30 -0
  14. package/dist/components/gesture-manager.d.ts +74 -0
  15. package/dist/components/gesture-manager.js.flow +82 -0
  16. package/dist/components/gesture-state-machine.d.ts +105 -0
  17. package/dist/components/gesture-state-machine.js.flow +118 -0
  18. package/dist/components/icon.d.ts +15 -0
  19. package/dist/components/icon.js.flow +18 -0
  20. package/dist/components/input/__tests__/test-math-wrapper.d.ts +8 -0
  21. package/dist/components/input/__tests__/test-math-wrapper.js.flow +14 -0
  22. package/dist/components/input/cursor-handle.d.ts +1 -1
  23. package/dist/components/input/cursor-handle.js.flow +1 -1
  24. package/dist/components/input/drag-listener.d.ts +13 -0
  25. package/dist/components/input/drag-listener.js.flow +19 -0
  26. package/dist/components/input/math-input.d.ts +5 -4
  27. package/dist/components/input/math-input.js.flow +5 -4
  28. package/dist/components/input/math-wrapper.d.ts +110 -0
  29. package/dist/components/input/math-wrapper.js.flow +125 -0
  30. package/dist/components/input/scroll-into-view.d.ts +11 -0
  31. package/dist/components/input/scroll-into-view.js.flow +20 -0
  32. package/dist/components/keypad/button-assets.d.ts +4 -3
  33. package/dist/components/keypad/button-assets.js.flow +3 -3
  34. package/dist/components/keypad/button.d.ts +1 -2
  35. package/dist/components/keypad/button.js.flow +1 -2
  36. package/dist/components/keypad/index.d.ts +1 -1
  37. package/dist/components/keypad/index.js.flow +1 -3
  38. package/dist/components/keypad/keypad-page-items.d.ts +15 -10
  39. package/dist/components/keypad/keypad-page-items.js.flow +20 -10
  40. package/dist/components/keypad-button.d.ts +52 -0
  41. package/dist/components/keypad-button.js.flow +79 -0
  42. package/dist/components/keypad-container.d.ts +40 -0
  43. package/dist/components/keypad-container.js.flow +58 -0
  44. package/dist/components/keypad.d.ts +31 -0
  45. package/dist/components/keypad.js.flow +40 -0
  46. package/dist/components/many-keypad-button.d.ts +15 -0
  47. package/dist/components/many-keypad-button.js.flow +17 -0
  48. package/dist/components/math-icon.d.ts +16 -0
  49. package/dist/components/math-icon.js.flow +19 -0
  50. package/dist/components/multi-symbol-grid.d.ts +14 -0
  51. package/dist/components/multi-symbol-grid.js.flow +16 -0
  52. package/dist/components/multi-symbol-popover.d.ts +12 -0
  53. package/dist/components/multi-symbol-popover.js.flow +15 -0
  54. package/dist/components/navigation-pad.d.ts +14 -0
  55. package/dist/components/navigation-pad.js.flow +16 -0
  56. package/dist/components/node-manager.d.ts +50 -0
  57. package/dist/components/node-manager.js.flow +62 -0
  58. package/dist/components/popover-manager.d.ts +13 -0
  59. package/dist/components/popover-manager.js.flow +15 -0
  60. package/dist/components/popover-state-machine.d.ts +68 -0
  61. package/dist/components/popover-state-machine.js.flow +77 -0
  62. package/dist/components/provided-keypad.d.ts +8 -10
  63. package/dist/components/provided-keypad.js.flow +8 -10
  64. package/dist/components/styles.d.ts +6 -0
  65. package/dist/components/styles.js.flow +13 -0
  66. package/dist/components/svg-icon.d.ts +12 -0
  67. package/dist/components/svg-icon.js.flow +15 -0
  68. package/dist/components/tabbar/icons.d.ts +3 -2
  69. package/dist/components/tabbar/icons.js.flow +3 -2
  70. package/dist/components/tabbar/item.d.ts +1 -2
  71. package/dist/components/tabbar/item.js.flow +1 -2
  72. package/dist/components/tabbar/tabbar.d.ts +3 -3
  73. package/dist/components/tabbar/tabbar.js.flow +3 -3
  74. package/dist/components/text-icon.d.ts +13 -0
  75. package/dist/components/text-icon.js.flow +16 -0
  76. package/dist/components/touchable-keypad-button.d.ts +30 -0
  77. package/dist/components/touchable-keypad-button.js.flow +35 -0
  78. package/dist/components/two-page-keypad.d.ts +20 -0
  79. package/dist/components/two-page-keypad.js.flow +30 -0
  80. package/dist/components/velocity-tracker.d.ts +48 -0
  81. package/dist/components/velocity-tracker.js.flow +54 -0
  82. package/dist/es/index.css +0 -3
  83. package/dist/es/index.js +933 -1065
  84. package/dist/es/index.js.map +1 -1
  85. package/dist/fake-react-native-web/text.d.ts +2 -1
  86. package/dist/fake-react-native-web/text.js.flow +2 -1
  87. package/dist/fake-react-native-web/view.d.ts +3 -2
  88. package/dist/fake-react-native-web/view.js.flow +3 -2
  89. package/dist/index.css +0 -3
  90. package/dist/index.d.ts +1 -1
  91. package/dist/index.js +977 -1090
  92. package/dist/index.js.flow +1 -4
  93. package/dist/index.js.map +1 -1
  94. package/dist/store/actions.d.ts +64 -0
  95. package/dist/store/actions.js.flow +100 -0
  96. package/dist/store/echo-reducer.d.ts +4 -0
  97. package/dist/store/echo-reducer.js.flow +10 -0
  98. package/dist/store/index.d.ts +10 -1
  99. package/dist/store/index.js.flow +17 -1
  100. package/dist/store/input-reducer.d.ts +4 -0
  101. package/dist/store/input-reducer.js.flow +13 -0
  102. package/dist/store/keypad-reducer.d.ts +4 -0
  103. package/dist/store/keypad-reducer.js.flow +13 -0
  104. package/dist/store/layout-reducer.d.ts +4 -0
  105. package/dist/store/layout-reducer.js.flow +13 -0
  106. package/dist/store/pager-reducer.d.ts +4 -0
  107. package/dist/store/pager-reducer.js.flow +13 -0
  108. package/dist/store/shared.d.ts +6 -0
  109. package/dist/store/shared.js.flow +13 -0
  110. package/dist/store/types.d.ts +58 -0
  111. package/dist/store/types.js.flow +64 -0
  112. package/dist/types.d.ts +63 -0
  113. package/dist/types.js.flow +73 -0
  114. package/less/overrides.less +0 -6
  115. package/package.json +1 -1
  116. package/src/components/__tests__/{gesture-state-machine_test.js → gesture-state-machine.test.ts} +5 -1
  117. package/src/components/__tests__/{two-page-keypad_test.js → two-page-keypad.test.tsx} +0 -2
  118. package/src/components/{corner-decal.js → corner-decal.tsx} +6 -5
  119. package/src/components/{echo-manager.js → echo-manager.tsx} +29 -24
  120. package/src/components/{empty-keypad-button.js → empty-keypad-button.tsx} +17 -10
  121. package/src/components/{expression-keypad.js → expression-keypad.tsx} +27 -25
  122. package/src/components/{fraction-keypad.js → fraction-keypad.tsx} +21 -16
  123. package/src/components/{gesture-manager.js → gesture-manager.ts} +10 -4
  124. package/src/components/{gesture-state-machine.js → gesture-state-machine.ts} +49 -3
  125. package/src/components/{icon.js → icon.tsx} +12 -14
  126. package/src/components/input/cursor-handle.tsx +1 -1
  127. package/src/components/input/{drag-listener.js → drag-listener.ts} +4 -0
  128. package/src/components/input/math-input.tsx +10 -9
  129. package/src/components/input/{math-wrapper.js → math-wrapper.ts} +10 -6
  130. package/src/components/input/{scroll-into-view.js → scroll-into-view.ts} +5 -15
  131. package/src/components/keypad/button-assets.tsx +4 -5
  132. package/src/components/keypad/button.tsx +1 -2
  133. package/src/components/keypad/index.tsx +2 -2
  134. package/src/components/keypad/keypad-page-items.tsx +33 -10
  135. package/src/components/{keypad-button.js → keypad-button.tsx} +42 -37
  136. package/src/components/{keypad-container.js → keypad-container.tsx} +42 -24
  137. package/src/components/{keypad.js → keypad.tsx} +32 -24
  138. package/src/components/{many-keypad-button.js → many-keypad-button.tsx} +8 -6
  139. package/src/components/{math-icon.js → math-icon.tsx} +7 -6
  140. package/src/components/{multi-symbol-grid.js → multi-symbol-grid.tsx} +8 -8
  141. package/src/components/{multi-symbol-popover.js → multi-symbol-popover.tsx} +5 -6
  142. package/src/components/{navigation-pad.js → navigation-pad.tsx} +7 -6
  143. package/src/components/{node-manager.js → node-manager.ts} +16 -4
  144. package/src/components/{popover-manager.js → popover-manager.tsx} +13 -16
  145. package/src/components/{popover-state-machine.js → popover-state-machine.ts} +13 -2
  146. package/src/components/prop-types.js +1 -67
  147. package/src/components/provided-keypad.tsx +16 -23
  148. package/src/components/{svg-icon.js → svg-icon.tsx} +5 -6
  149. package/src/components/tabbar/icons.tsx +4 -2
  150. package/src/components/tabbar/item.tsx +1 -3
  151. package/src/components/tabbar/{tabbar.stories.js → tabbar.stories.tsx} +10 -1
  152. package/src/components/tabbar/tabbar.tsx +3 -3
  153. package/src/components/{text-icon.js → text-icon.tsx} +7 -6
  154. package/src/components/{touchable-keypad-button.js → touchable-keypad-button.tsx} +19 -16
  155. package/src/components/{two-page-keypad.js → two-page-keypad.tsx} +13 -9
  156. package/src/components/{velocity-tracker.js → velocity-tracker.ts} +14 -4
  157. package/src/fake-react-native-web/text.tsx +2 -1
  158. package/src/fake-react-native-web/view.tsx +3 -2
  159. package/src/index.ts +1 -4
  160. package/src/math-input.stories.tsx +67 -0
  161. package/src/store/actions.ts +178 -0
  162. package/src/store/echo-reducer.ts +61 -0
  163. package/src/store/index.ts +39 -449
  164. package/src/store/input-reducer.ts +56 -0
  165. package/src/store/keypad-reducer.ts +59 -0
  166. package/src/store/layout-reducer.ts +134 -0
  167. package/src/store/pager-reducer.ts +125 -0
  168. package/src/store/shared.ts +12 -0
  169. package/src/store/types.ts +82 -0
  170. package/src/types.ts +81 -0
  171. package/tsconfig.tsbuildinfo +1 -1
  172. package/src/actions/index.js +0 -57
  173. package/src/components/app.js +0 -73
  174. package/src/demo.js +0 -9
  175. package/src/native-app.js +0 -85
  176. /package/src/components/__tests__/{node-manager_test.js → node-manager.test.ts} +0 -0
  177. /package/src/components/{compute-layout-parameters.js → compute-layout-parameters.ts} +0 -0
  178. /package/src/components/input/__tests__/{context-tracking_test.js → context-tracking.test.ts} +0 -0
  179. /package/src/components/input/__tests__/{mathquill_test.js → mathquill.test.ts} +0 -0
  180. /package/src/components/input/__tests__/{test-math-wrapper.jsx → test-math-wrapper.ts} +0 -0
  181. /package/src/components/keypad/{button.stories.js → button.stories.tsx} +0 -0
  182. /package/src/components/{styles.js → styles.ts} +0 -0
  183. /package/src/components/tabbar/__tests__/{tabbar_test.js → tabbar.test.tsx} +0 -0
@@ -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
- const keypadForType = {
22
- [KeypadTypes.FRACTION]: FractionKeypad,
23
- [KeypadTypes.EXPRESSION]: ExpressionKeypad,
24
- } as const;
25
-
26
- export const createStore = (): any => {
27
- const initialInputState: {
28
- keyHandler: any;
29
- cursor: any;
30
- } = {
31
- keyHandler: null,
32
- cursor: {
33
- context: CursorContexts.NONE,
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: (key, layoutProps, inPopover) => {
284
- store.dispatch({
285
- type: "PressKey",
286
- key,
287
- ...layoutProps,
288
- inPopover,
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 (state = initialGestureState, action: any) {
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;