@khanacademy/math-input 15.1.0 → 16.0.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.
Files changed (138) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/components/keypad/index.d.ts +1 -1
  3. package/dist/components/keypad/mobile-keypad-internals.d.ts +49 -0
  4. package/dist/components/keypad/mobile-keypad.d.ts +4 -48
  5. package/dist/es/index.js +168 -4740
  6. package/dist/es/index.js.map +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +166 -4722
  9. package/dist/index.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/components/__tests__/integration.test.tsx +2 -3
  12. package/src/components/keypad/__tests__/mobile-keypad.test.tsx +8 -8
  13. package/src/components/keypad/index.tsx +1 -1
  14. package/src/components/keypad/mobile-keypad-internals.tsx +240 -0
  15. package/src/components/keypad/mobile-keypad.tsx +21 -234
  16. package/src/full-mobile-input.stories.tsx +0 -1
  17. package/src/index.ts +1 -1
  18. package/tsconfig-build.tsbuildinfo +1 -1
  19. package/dist/components/keypad-legacy/compute-layout-parameters.d.ts +0 -28
  20. package/dist/components/keypad-legacy/corner-decal.d.ts +0 -12
  21. package/dist/components/keypad-legacy/echo-manager.d.ts +0 -17
  22. package/dist/components/keypad-legacy/empty-keypad-button.d.ts +0 -13
  23. package/dist/components/keypad-legacy/expression-keypad.d.ts +0 -21
  24. package/dist/components/keypad-legacy/fraction-keypad.d.ts +0 -21
  25. package/dist/components/keypad-legacy/gesture-manager.d.ts +0 -86
  26. package/dist/components/keypad-legacy/gesture-state-machine.d.ts +0 -105
  27. package/dist/components/keypad-legacy/icon.d.ts +0 -15
  28. package/dist/components/keypad-legacy/index.d.ts +0 -1
  29. package/dist/components/keypad-legacy/keypad-button.d.ts +0 -53
  30. package/dist/components/keypad-legacy/keypad-container.d.ts +0 -41
  31. package/dist/components/keypad-legacy/keypad.d.ts +0 -31
  32. package/dist/components/keypad-legacy/many-keypad-button.d.ts +0 -15
  33. package/dist/components/keypad-legacy/math-icon.d.ts +0 -16
  34. package/dist/components/keypad-legacy/multi-symbol-grid.d.ts +0 -14
  35. package/dist/components/keypad-legacy/multi-symbol-popover.d.ts +0 -12
  36. package/dist/components/keypad-legacy/navigation-pad.d.ts +0 -14
  37. package/dist/components/keypad-legacy/node-manager.d.ts +0 -49
  38. package/dist/components/keypad-legacy/popover-manager.d.ts +0 -13
  39. package/dist/components/keypad-legacy/popover-state-machine.d.ts +0 -68
  40. package/dist/components/keypad-legacy/provided-keypad.d.ts +0 -28
  41. package/dist/components/keypad-legacy/store/actions.d.ts +0 -55
  42. package/dist/components/keypad-legacy/store/echo-reducer.d.ts +0 -4
  43. package/dist/components/keypad-legacy/store/index.d.ts +0 -9
  44. package/dist/components/keypad-legacy/store/input-reducer.d.ts +0 -4
  45. package/dist/components/keypad-legacy/store/keypad-reducer.d.ts +0 -4
  46. package/dist/components/keypad-legacy/store/layout-reducer.d.ts +0 -4
  47. package/dist/components/keypad-legacy/store/shared.d.ts +0 -7
  48. package/dist/components/keypad-legacy/store/types.d.ts +0 -47
  49. package/dist/components/keypad-legacy/styles.d.ts +0 -5
  50. package/dist/components/keypad-legacy/svg-icon.d.ts +0 -12
  51. package/dist/components/keypad-legacy/text-icon.d.ts +0 -13
  52. package/dist/components/keypad-legacy/touchable-keypad-button.d.ts +0 -37
  53. package/dist/components/keypad-legacy/two-page-keypad.d.ts +0 -21
  54. package/dist/components/keypad-legacy/z-indexes.d.ts +0 -7
  55. package/dist/components/keypad-switch.d.ts +0 -12
  56. package/src/components/keypad-legacy/__tests__/gesture-state-machine.test.ts +0 -441
  57. package/src/components/keypad-legacy/__tests__/node-manager.test.ts +0 -89
  58. package/src/components/keypad-legacy/compute-layout-parameters.ts +0 -205
  59. package/src/components/keypad-legacy/corner-decal.tsx +0 -56
  60. package/src/components/keypad-legacy/echo-manager.tsx +0 -152
  61. package/src/components/keypad-legacy/empty-keypad-button.tsx +0 -58
  62. package/src/components/keypad-legacy/expression-keypad.tsx +0 -315
  63. package/src/components/keypad-legacy/fraction-keypad.tsx +0 -180
  64. package/src/components/keypad-legacy/gesture-manager.ts +0 -255
  65. package/src/components/keypad-legacy/gesture-state-machine.ts +0 -329
  66. package/src/components/keypad-legacy/icon.tsx +0 -72
  67. package/src/components/keypad-legacy/iconography/arrow.js +0 -22
  68. package/src/components/keypad-legacy/iconography/backspace.js +0 -29
  69. package/src/components/keypad-legacy/iconography/cdot.js +0 -29
  70. package/src/components/keypad-legacy/iconography/cos.js +0 -30
  71. package/src/components/keypad-legacy/iconography/cube-root.js +0 -36
  72. package/src/components/keypad-legacy/iconography/dismiss.js +0 -25
  73. package/src/components/keypad-legacy/iconography/divide.js +0 -34
  74. package/src/components/keypad-legacy/iconography/down.js +0 -16
  75. package/src/components/keypad-legacy/iconography/equal.js +0 -33
  76. package/src/components/keypad-legacy/iconography/exp-2.js +0 -29
  77. package/src/components/keypad-legacy/iconography/exp-3.js +0 -29
  78. package/src/components/keypad-legacy/iconography/exp.js +0 -29
  79. package/src/components/keypad-legacy/iconography/frac.js +0 -44
  80. package/src/components/keypad-legacy/iconography/geq.js +0 -33
  81. package/src/components/keypad-legacy/iconography/gt.js +0 -33
  82. package/src/components/keypad-legacy/iconography/index.js +0 -45
  83. package/src/components/keypad-legacy/iconography/jump-into-numerator.js +0 -41
  84. package/src/components/keypad-legacy/iconography/jump-out-base.js +0 -30
  85. package/src/components/keypad-legacy/iconography/jump-out-denominator.js +0 -41
  86. package/src/components/keypad-legacy/iconography/jump-out-exponent.js +0 -30
  87. package/src/components/keypad-legacy/iconography/jump-out-numerator.js +0 -41
  88. package/src/components/keypad-legacy/iconography/jump-out-parentheses.js +0 -33
  89. package/src/components/keypad-legacy/iconography/left-paren.js +0 -33
  90. package/src/components/keypad-legacy/iconography/left.js +0 -16
  91. package/src/components/keypad-legacy/iconography/leq.js +0 -33
  92. package/src/components/keypad-legacy/iconography/ln.js +0 -29
  93. package/src/components/keypad-legacy/iconography/log-n.js +0 -29
  94. package/src/components/keypad-legacy/iconography/log.js +0 -29
  95. package/src/components/keypad-legacy/iconography/lt.js +0 -33
  96. package/src/components/keypad-legacy/iconography/minus.js +0 -32
  97. package/src/components/keypad-legacy/iconography/neq.js +0 -33
  98. package/src/components/keypad-legacy/iconography/parens.js +0 -33
  99. package/src/components/keypad-legacy/iconography/percent.js +0 -49
  100. package/src/components/keypad-legacy/iconography/period.js +0 -26
  101. package/src/components/keypad-legacy/iconography/plus.js +0 -32
  102. package/src/components/keypad-legacy/iconography/radical.js +0 -36
  103. package/src/components/keypad-legacy/iconography/right-paren.js +0 -33
  104. package/src/components/keypad-legacy/iconography/right.js +0 -16
  105. package/src/components/keypad-legacy/iconography/sin.js +0 -30
  106. package/src/components/keypad-legacy/iconography/sqrt.js +0 -32
  107. package/src/components/keypad-legacy/iconography/tan.js +0 -30
  108. package/src/components/keypad-legacy/iconography/times.js +0 -33
  109. package/src/components/keypad-legacy/iconography/up.js +0 -16
  110. package/src/components/keypad-legacy/index.ts +0 -1
  111. package/src/components/keypad-legacy/keypad-button.tsx +0 -368
  112. package/src/components/keypad-legacy/keypad-container.tsx +0 -358
  113. package/src/components/keypad-legacy/keypad.tsx +0 -162
  114. package/src/components/keypad-legacy/many-keypad-button.tsx +0 -54
  115. package/src/components/keypad-legacy/math-icon.tsx +0 -66
  116. package/src/components/keypad-legacy/multi-symbol-grid.tsx +0 -182
  117. package/src/components/keypad-legacy/multi-symbol-popover.tsx +0 -58
  118. package/src/components/keypad-legacy/navigation-pad.tsx +0 -140
  119. package/src/components/keypad-legacy/node-manager.ts +0 -133
  120. package/src/components/keypad-legacy/popover-manager.tsx +0 -73
  121. package/src/components/keypad-legacy/popover-state-machine.ts +0 -184
  122. package/src/components/keypad-legacy/provided-keypad.tsx +0 -136
  123. package/src/components/keypad-legacy/store/actions.ts +0 -155
  124. package/src/components/keypad-legacy/store/echo-reducer.ts +0 -57
  125. package/src/components/keypad-legacy/store/index.ts +0 -110
  126. package/src/components/keypad-legacy/store/input-reducer.ts +0 -55
  127. package/src/components/keypad-legacy/store/keypad-reducer.ts +0 -58
  128. package/src/components/keypad-legacy/store/layout-reducer.test.ts +0 -171
  129. package/src/components/keypad-legacy/store/layout-reducer.ts +0 -129
  130. package/src/components/keypad-legacy/store/shared.ts +0 -12
  131. package/src/components/keypad-legacy/store/types.ts +0 -78
  132. package/src/components/keypad-legacy/styles.ts +0 -38
  133. package/src/components/keypad-legacy/svg-icon.tsx +0 -24
  134. package/src/components/keypad-legacy/text-icon.tsx +0 -53
  135. package/src/components/keypad-legacy/touchable-keypad-button.tsx +0 -163
  136. package/src/components/keypad-legacy/two-page-keypad.tsx +0 -115
  137. package/src/components/keypad-legacy/z-indexes.ts +0 -8
  138. package/src/components/keypad-switch.tsx +0 -42
@@ -1,255 +0,0 @@
1
- /**
2
- * A high-level manager for our gesture system. In particular, this class
3
- * connects our various bits of logic for managing gestures and interactions,
4
- * and links them together.
5
- */
6
-
7
- import GestureStateMachine from "./gesture-state-machine";
8
- import NodeManager from "./node-manager";
9
- import PopoverStateMachine from "./popover-state-machine";
10
-
11
- import type Key from "../../data/keys";
12
- import type {ActiveNodesObj, LayoutProps} from "../../types";
13
- import type * as React from "react";
14
-
15
- const coordsForEvent = (evt) => {
16
- return [evt.changedTouches[0].clientX, evt.changedTouches[0].clientY];
17
- };
18
-
19
- type Options = {
20
- swipeEnabled: boolean;
21
- };
22
-
23
- type Handlers = {
24
- onSwipeChange?: (dx: number) => void;
25
- onSwipeEnd?: (dx: number) => void;
26
- onActiveNodesChanged: (activeNodes: ActiveNodesObj) => void;
27
- onClick: (key: Key, layoutProps: LayoutProps, inPopover: boolean) => void;
28
- };
29
-
30
- class GestureManager {
31
- swipeEnabled: boolean;
32
- trackEvents: boolean;
33
- nodeManager: NodeManager;
34
- popoverStateMachine: PopoverStateMachine;
35
- gestureStateMachine: GestureStateMachine;
36
-
37
- constructor(
38
- options: Options,
39
- handlers: Handlers,
40
- disabledSwipeKeys: ReadonlyArray<Key>,
41
- multiPressableKeys: ReadonlyArray<Key>,
42
- ) {
43
- const {swipeEnabled} = options;
44
-
45
- this.swipeEnabled = swipeEnabled;
46
-
47
- // Events aren't tracked until event tracking is enabled.
48
- this.trackEvents = false;
49
-
50
- this.nodeManager = new NodeManager();
51
- this.popoverStateMachine = new PopoverStateMachine({
52
- onActiveNodesChanged: (activeNodes) => {
53
- const {popover, ...rest} = activeNodes;
54
- handlers.onActiveNodesChanged({
55
- popover: popover && {
56
- parentId: popover.parentId,
57
- bounds: this.nodeManager.layoutPropsForId(
58
- popover.parentId,
59
- ).initialBounds,
60
- childKeyIds: popover.childIds,
61
- },
62
- ...rest,
63
- });
64
- },
65
- /**
66
- * `onClick` takes two arguments:
67
- *
68
- * @param {string} keyId - the identifier key that should initiate
69
- * a click
70
- * @param {string} domNodeId - the identifier of the DOM node on
71
- * which the click should be considered
72
- * to have occurred
73
- * @param {bool} inPopover - whether the key was contained within a
74
- * popover
75
- *
76
- * These two parameters will often be equivalent. They will differ,
77
- * though, when a popover button is itself clicked, in which case
78
- * we need to mimic the effects of clicking on its 'primary' child
79
- * key, but animate the click on the popover button.
80
- */
81
- onClick: (keyId, domNodeId, inPopover) => {
82
- handlers.onClick(
83
- keyId,
84
- this.nodeManager.layoutPropsForId(domNodeId),
85
- inPopover,
86
- );
87
- },
88
- });
89
- this.gestureStateMachine = new GestureStateMachine(
90
- {
91
- onFocus: (id) => {
92
- this.popoverStateMachine.onFocus(id);
93
- },
94
- onLongPress: (id) => {
95
- this.popoverStateMachine.onLongPress(id);
96
- },
97
- onTouchEnd: (id) => {
98
- this.popoverStateMachine.onTouchEnd(id);
99
- },
100
- onBlur: () => {
101
- this.popoverStateMachine.onBlur();
102
- },
103
- onSwipeChange: handlers.onSwipeChange,
104
- onSwipeEnd: handlers.onSwipeEnd,
105
- onTrigger: (id) => {
106
- this.popoverStateMachine.onTrigger(id);
107
- },
108
- },
109
- {},
110
- disabledSwipeKeys,
111
- multiPressableKeys,
112
- );
113
- }
114
-
115
- /**
116
- * Handle a touch-start event that originated in a node registered with the
117
- * gesture system.
118
- *
119
- * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
120
- * @param {string} id - the identifier of the DOM node in which the touch
121
- * occurred
122
- */
123
- onTouchStart(
124
- evt: React.TouchEvent<HTMLDivElement>,
125
- id?: string | undefined,
126
- ) {
127
- if (!this.trackEvents) {
128
- return;
129
- }
130
-
131
- const [x] = coordsForEvent(evt);
132
-
133
- // TODO(charlie): It doesn't seem to be guaranteed that every touch
134
- // event on `changedTouches` originates from the node through which this
135
- // touch event was sent. In that case, we'd be inappropriately reporting
136
- // the starting node ID.
137
- for (let i = 0; i < evt.changedTouches.length; i++) {
138
- this.gestureStateMachine.onTouchStart(
139
- () => id,
140
- evt.changedTouches[i].identifier,
141
- x,
142
- );
143
- }
144
-
145
- // If an event started in a view that we're managing, we'll handle it
146
- // all the way through.
147
- evt.preventDefault();
148
- }
149
-
150
- /**
151
- * Handle a touch-move event that originated in a node registered with the
152
- * gesture system.
153
- *
154
- * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
155
- */
156
- onTouchMove(evt: React.TouchEvent<HTMLDivElement>) {
157
- if (!this.trackEvents) {
158
- return;
159
- }
160
-
161
- const swipeLocked = this.popoverStateMachine.isPopoverVisible();
162
- const swipeEnabled = this.swipeEnabled && !swipeLocked;
163
- const [x, y] = coordsForEvent(evt);
164
- for (let i = 0; i < evt.changedTouches.length; i++) {
165
- this.gestureStateMachine.onTouchMove(
166
- () => this.nodeManager.idForCoords(x, y),
167
- evt.changedTouches[i].identifier,
168
- x,
169
- swipeEnabled,
170
- );
171
- }
172
- }
173
-
174
- /**
175
- * Handle a touch-end event that originated in a node registered with the
176
- * gesture system.
177
- *
178
- * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
179
- */
180
- onTouchEnd(evt: React.TouchEvent<HTMLDivElement>) {
181
- if (!this.trackEvents) {
182
- return;
183
- }
184
-
185
- const [x, y] = coordsForEvent(evt);
186
- for (let i = 0; i < evt.changedTouches.length; i++) {
187
- this.gestureStateMachine.onTouchEnd(
188
- () => this.nodeManager.idForCoords(x, y),
189
- evt.changedTouches[i].identifier,
190
- x,
191
- );
192
- }
193
- }
194
-
195
- /**
196
- * Handle a touch-cancel event that originated in a node registered with the
197
- * gesture system.
198
- *
199
- * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
200
- */
201
- onTouchCancel(evt: React.TouchEvent<HTMLDivElement>) {
202
- if (!this.trackEvents) {
203
- return;
204
- }
205
-
206
- for (let i = 0; i < evt.changedTouches.length; i++) {
207
- this.gestureStateMachine.onTouchCancel(
208
- evt.changedTouches[i].identifier,
209
- );
210
- }
211
- }
212
-
213
- /**
214
- * Register a DOM node with a given identifier.
215
- *
216
- * @param {string} id - the identifier of the given node
217
- * @param {node} domNode - the DOM node linked to the identifier
218
- * @param {string[]} childIds - the identifiers of any DOM nodes that
219
- * should be considered children of this node,
220
- * in that they should take priority when
221
- * intercepting touch events
222
- * @param {object} borders - an opaque object describing the node's borders
223
- */
224
- registerDOMNode(id, domNode, childIds) {
225
- this.nodeManager.registerDOMNode(id, domNode, childIds);
226
- this.popoverStateMachine.registerPopover(id, childIds);
227
- }
228
-
229
- /**
230
- * Unregister the DOM node with the given identifier.
231
- *
232
- * @param {string} id - the identifier of the node to unregister
233
- */
234
- unregisterDOMNode(id) {
235
- this.nodeManager.unregisterDOMNode(id);
236
- this.popoverStateMachine.unregisterPopover(id);
237
- }
238
-
239
- /**
240
- * Enable event tracking for the gesture manager.
241
- */
242
- enableEventTracking() {
243
- this.trackEvents = true;
244
- }
245
-
246
- /**
247
- * Disable event tracking for the gesture manager. When called, the gesture
248
- * manager will drop any events received by managed nodes.
249
- */
250
- disableEventTracking() {
251
- this.trackEvents = false;
252
- }
253
- }
254
-
255
- export default GestureManager;
@@ -1,329 +0,0 @@
1
- import type Key from "../../data/keys";
2
-
3
- /**
4
- * The state machine that backs our gesture system. In particular, this state
5
- * machine manages the interplay between focuses, touch ups, and swiping.
6
- * It is entirely ignorant of the existence of popovers and the positions of
7
- * DOM nodes, operating solely on IDs. The state machine does accommodate for
8
- * multi-touch interactions, tracking gesture state on a per-touch basis.
9
- */
10
-
11
- // exported for tests
12
- export type Handlers = {
13
- onFocus: (id: string) => void;
14
- onBlur: () => void;
15
- onTrigger: (id: string) => void;
16
- onLongPress: (id: string) => void;
17
- onSwipeChange?: (x: number) => void;
18
- onSwipeEnd?: (x: number) => void;
19
- onTouchEnd: (id: string) => void;
20
- };
21
-
22
- type Options = {
23
- longPressWaitTimeMs: number;
24
- swipeThresholdPx: number;
25
- holdIntervalMs: number;
26
- };
27
-
28
- type TouchState = {
29
- activeNodeId: Key;
30
- pressAndHoldIntervalId: number | null;
31
- longPressTimeoutId: number | null;
32
- swipeLocked: boolean;
33
- startX: number;
34
- };
35
-
36
- type TouchStateMap = Record<Key, TouchState>;
37
-
38
- type SwipeState = {
39
- touchId: Key;
40
- startX: number;
41
- };
42
-
43
- const defaultOptions: Options = {
44
- longPressWaitTimeMs: 50,
45
- swipeThresholdPx: 20,
46
- holdIntervalMs: 250,
47
- };
48
-
49
- class GestureStateMachine {
50
- handlers: Handlers;
51
- options: Options;
52
- swipeDisabledNodeIds: ReadonlyArray<Key>;
53
- multiPressableKeys: ReadonlyArray<Key>;
54
- touchState: Partial<TouchStateMap>;
55
- swipeState: SwipeState | null;
56
-
57
- constructor(
58
- handlers: Handlers,
59
- options: Partial<Options>,
60
- swipeDisabledNodeIds?: ReadonlyArray<Key>,
61
- multiPressableKeys?: ReadonlyArray<Key>,
62
- ) {
63
- this.handlers = handlers;
64
- this.options = {
65
- ...defaultOptions,
66
- ...options,
67
- };
68
- this.swipeDisabledNodeIds = swipeDisabledNodeIds || [];
69
- this.multiPressableKeys = multiPressableKeys || [];
70
-
71
- // TODO(charlie): Add types for this file. It's not great that we're now
72
- // passing around these opaque state objects.
73
- this.touchState = {};
74
- this.swipeState = null;
75
- }
76
-
77
- _maybeCancelLongPressForTouch(touchId) {
78
- const {longPressTimeoutId} = this.touchState[touchId];
79
- if (longPressTimeoutId) {
80
- clearTimeout(longPressTimeoutId);
81
- this.touchState[touchId] = {
82
- ...this.touchState[touchId],
83
- longPressTimeoutId: null,
84
- };
85
- }
86
- }
87
-
88
- _maybeCancelPressAndHoldForTouch(touchId) {
89
- const {pressAndHoldIntervalId} = this.touchState[touchId];
90
- if (pressAndHoldIntervalId) {
91
- // If there was an interval set to detect holds, clear it out.
92
- clearInterval(pressAndHoldIntervalId);
93
- this.touchState[touchId] = {
94
- ...this.touchState[touchId],
95
- pressAndHoldIntervalId: null,
96
- };
97
- }
98
- }
99
-
100
- _cleanupTouchEvent(touchId) {
101
- this._maybeCancelLongPressForTouch(touchId);
102
- this._maybeCancelPressAndHoldForTouch(touchId);
103
- delete this.touchState[touchId];
104
- }
105
-
106
- /**
107
- * Handle a focus event on the node with the given identifier, which may be
108
- * `null` to indicate that the user has dragged their finger off of any
109
- * registered nodes, but is still in the middle of a gesture.
110
- *
111
- * @param {string|null} id - the identifier of the newly focused node, or
112
- * `null` if no node is focused
113
- * @param {number} touchId - a unique identifier associated with the touch
114
- */
115
- _onFocus(id, touchId) {
116
- // If we're in the middle of a long-press, cancel it.
117
- this._maybeCancelLongPressForTouch(touchId);
118
-
119
- // Reset any existing hold-detecting interval.
120
- this._maybeCancelPressAndHoldForTouch(touchId);
121
-
122
- // Set the focused node ID and handle the focus event.
123
- // Note: we can call `onFocus` with `null` IDs. The semantics of an
124
- // `onFocus` with a `null` ID differs from that of `onBlur`. The former
125
- // indicates that a gesture that can focus future nodes is still in
126
- // progress, but that no node is currently focused. The latter
127
- // indicates that the gesture has ended and nothing will be focused.
128
- this.touchState[touchId] = {
129
- ...this.touchState[touchId],
130
- activeNodeId: id,
131
- };
132
- this.handlers.onFocus(id);
133
-
134
- if (id) {
135
- // Handle logic for repeating button presses.
136
- if (this.multiPressableKeys.includes(id)) {
137
- // Start by triggering a click, iOS style.
138
- this.handlers.onTrigger(id);
139
-
140
- // Set up a new hold detector for the current button.
141
- this.touchState[touchId] = {
142
- ...this.touchState[touchId],
143
- pressAndHoldIntervalId: setInterval(() => {
144
- // On every cycle, trigger the click handler.
145
- this.handlers.onTrigger(id);
146
- }, this.options.holdIntervalMs),
147
- };
148
- } else {
149
- // Set up a new hold detector for the current button.
150
- this.touchState[touchId] = {
151
- ...this.touchState[touchId],
152
- longPressTimeoutId: setTimeout(() => {
153
- this.handlers.onLongPress(id);
154
- this.touchState[touchId] = {
155
- ...this.touchState[touchId],
156
- longPressTimeoutId: null,
157
- };
158
- }, this.options.longPressWaitTimeMs),
159
- };
160
- }
161
- }
162
- }
163
-
164
- /**
165
- * Clear out all active gesture information.
166
- */
167
- _onSwipeStart() {
168
- for (const activeTouchId of Object.keys(this.touchState)) {
169
- this._maybeCancelLongPressForTouch(activeTouchId);
170
- this._maybeCancelPressAndHoldForTouch(activeTouchId);
171
- }
172
- this.touchState = {};
173
- this.handlers.onBlur();
174
- }
175
-
176
- /**
177
- * A function that returns the identifier of the node over which the touch
178
- * event occurred. This is provided as a piece of lazy computation, as
179
- * computing the DOM node for a given point is expensive, and the state
180
- * machine won't always need that information. For example, if the user is
181
- * swiping, then `onTouchMove` needs to be performant and doesn't care about
182
- * the node over which the touch occurred.
183
- *
184
- * @typedef idComputation
185
- * @returns {DOMNode} - the identifier of the node over which the touch
186
- * occurred
187
- */
188
-
189
- /**
190
- * Handle a touch-start event on the node with the given identifer.
191
- *
192
- * @param {idComputation} getId - a function that returns identifier of the
193
- * node over which the start event occurred
194
- * @param {number} touchId - a unique identifier associated with the touch
195
- */
196
- onTouchStart(getId, touchId, pageX) {
197
- // Ignore any touch events that start mid-swipe.
198
- if (this.swipeState) {
199
- return;
200
- }
201
-
202
- if (this.touchState[touchId]) {
203
- // It turns out we can get multiple touch starts with no
204
- // intervening move, end, or cancel events in Android WebViews.
205
- // TODO(benkomalo): it's not entirely clear why this happens, but
206
- // it seems to happen with the backspace button. It may be related
207
- // to FastClick (https://github.com/ftlabs/fastclick/issues/71)
208
- // though I haven't verified, and it's probably good to be robust
209
- // here anyways.
210
- return;
211
- }
212
-
213
- const startingNodeId = getId();
214
- this.touchState[touchId] = {
215
- swipeLocked: this.swipeDisabledNodeIds.includes(startingNodeId),
216
- startX: pageX,
217
- };
218
-
219
- this._onFocus(startingNodeId, touchId);
220
- }
221
-
222
- /**
223
- * Handle a touch-move event on the node with the given identifer.
224
- *
225
- * @param {idComputation} getId - a function that returns identifier of the
226
- * node over which the move event occurred
227
- * @param {number} touchId - a unique identifier associated with the touch
228
- * @param {number} pageX - the x coordinate of the touch
229
- * @param {boolean} swipeEnabled - whether the system should allow for
230
- * transitions into a swiping state
231
- */
232
- onTouchMove(getId, touchId, pageX, swipeEnabled) {
233
- if (this.swipeState) {
234
- // Only respect the finger that started a swipe. Any other lingering
235
- // gestures are ignored.
236
- if (this.swipeState.touchId === touchId) {
237
- this.handlers.onSwipeChange?.(pageX - this.swipeState.startX);
238
- }
239
- } else if (this.touchState[touchId]) {
240
- // It could be touch events started outside the keypad and
241
- // moved into it; ignore them.
242
- const {activeNodeId, startX, swipeLocked} =
243
- this.touchState[touchId];
244
-
245
- const dx = pageX - startX;
246
- const shouldBeginSwiping =
247
- swipeEnabled &&
248
- !swipeLocked &&
249
- Math.abs(dx) > this.options.swipeThresholdPx;
250
-
251
- if (shouldBeginSwiping) {
252
- this._onSwipeStart();
253
-
254
- // Trigger the swipe.
255
- this.swipeState = {
256
- touchId,
257
- startX,
258
- };
259
- this.handlers.onSwipeChange?.(pageX - this.swipeState.startX);
260
- } else {
261
- const id = getId();
262
- if (id !== activeNodeId) {
263
- this._onFocus(id, touchId);
264
- }
265
- }
266
- }
267
- }
268
-
269
- /**
270
- * Handle a touch-end event on the node with the given identifer.
271
- *
272
- * @param {idComputation} getId - a function that returns identifier of the
273
- * node over which the end event occurred
274
- * @param {number} touchId - a unique identifier associated with the touch
275
- * @param {number} pageX - the x coordinate of the touch
276
- */
277
- onTouchEnd(getId, touchId, pageX) {
278
- if (this.swipeState) {
279
- // Only respect the finger that started a swipe. Any other lingering
280
- // gestures are ignored.
281
- if (this.swipeState.touchId === touchId) {
282
- this.handlers.onSwipeEnd?.(pageX - this.swipeState.startX);
283
- this.swipeState = null;
284
- }
285
- } else if (this.touchState[touchId]) {
286
- // It could be touch events started outside the keypad and
287
- // moved into it; ignore them.
288
- const {activeNodeId, pressAndHoldIntervalId} =
289
- this.touchState[touchId];
290
-
291
- this._cleanupTouchEvent(touchId);
292
-
293
- const didPressAndHold = !!pressAndHoldIntervalId;
294
- if (didPressAndHold) {
295
- // We don't trigger a touch end if there was a press and hold,
296
- // because the key has been triggered at least once and calling
297
- // the onTouchEnd handler would add an extra trigger.
298
- this.handlers.onBlur();
299
- } else {
300
- // Trigger a touch-end. There's no need to notify clients of a
301
- // blur as clients are responsible for handling any cleanup in
302
- // their touch-end handlers.
303
- this.handlers.onTouchEnd(activeNodeId);
304
- }
305
- }
306
- }
307
-
308
- /**
309
- * Handle a touch-cancel event.
310
- */
311
- onTouchCancel(touchId) {
312
- // If a touch is cancelled and we're swiping, end the swipe with no
313
- // displacement.
314
- if (this.swipeState) {
315
- if (this.swipeState.touchId === touchId) {
316
- this.handlers.onSwipeEnd?.(0);
317
- this.swipeState = null;
318
- }
319
- } else if (this.touchState[touchId]) {
320
- // Otherwise, trigger a full blur. We don't want to trigger a
321
- // touch-up, since the cancellation means that the user probably
322
- // didn't release over a key intentionally.
323
- this._cleanupTouchEvent(touchId);
324
- this.handlers.onBlur();
325
- }
326
- }
327
- }
328
-
329
- export default GestureStateMachine;
@@ -1,72 +0,0 @@
1
- /**
2
- * A component that renders an icon for a symbol with the given name.
3
- */
4
-
5
- import {StyleSheet} from "aphrodite";
6
- import * as React from "react";
7
-
8
- import {IconType} from "../../enums";
9
- import {offBlack} from "../common-style";
10
-
11
- import MathIcon from "./math-icon";
12
- import SvgIcon from "./svg-icon";
13
- import TextIcon from "./text-icon";
14
-
15
- import type {IconConfig} from "../../types";
16
- import type {StyleType} from "@khanacademy/wonder-blocks-core";
17
-
18
- const focusedColor = "#FFF";
19
- const unfocusedColor = offBlack;
20
-
21
- type Props = {
22
- focused: boolean;
23
- icon: IconConfig;
24
- style?: StyleType;
25
- };
26
-
27
- class Icon extends React.PureComponent<Props> {
28
- render() {
29
- const {focused, icon, style} = this.props;
30
-
31
- const styleWithFocus: StyleType = [
32
- focused ? styles.focused : styles.unfocused,
33
- ...(Array.isArray(style) ? style : [style]),
34
- ];
35
-
36
- switch (icon.type) {
37
- case IconType.MATH:
38
- return <MathIcon math={icon.data} style={styleWithFocus} />;
39
-
40
- case IconType.SVG:
41
- // TODO(charlie): Support passing style objects to `SvgIcon`.
42
- // This will require migrating the individual icons to use
43
- // `currentColor` and accept a `className` prop, rather than
44
- // relying on an explicit color prop.
45
- return (
46
- <SvgIcon
47
- name={icon.data}
48
- color={focused ? focusedColor : unfocusedColor}
49
- />
50
- );
51
-
52
- case IconType.TEXT:
53
- return (
54
- <TextIcon character={icon.data} style={styleWithFocus} />
55
- );
56
- default:
57
- throw new Error("No icon or symbol provided");
58
- }
59
- }
60
- }
61
-
62
- const styles = StyleSheet.create({
63
- unfocused: {
64
- color: unfocusedColor,
65
- },
66
-
67
- focused: {
68
- color: focusedColor,
69
- },
70
- });
71
-
72
- export default Icon;
@@ -1,22 +0,0 @@
1
- /**
2
- * An arrow icon, used by the other navigational keys.
3
- */
4
- import * as React from "react";
5
-
6
- const Arrow = (props) => {
7
- return (
8
- <g fill="none" fillRule="evenodd" {...props}>
9
- <path fill="none" d="M0 0h48v48H0z" />
10
- <path fill="none" d="M12 12h24v24H12z" />
11
- <path
12
- stroke="#888D93"
13
- strokeWidth="2"
14
- strokeLinecap="round"
15
- strokeLinejoin="round"
16
- d="M22 18l-6 6 6 6M16 24h16"
17
- />
18
- </g>
19
- );
20
- };
21
-
22
- export default Arrow;