@khanacademy/math-input 3.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/.eslintrc.js +7 -1
  2. package/CHANGELOG.md +42 -0
  3. package/dist/components/input/__tests__/test-math-wrapper.d.ts +1 -1
  4. package/dist/components/input/__tests__/test-math-wrapper.js.flow +1 -1
  5. package/dist/components/input/key-handlers/handle-arrow.d.ts +3 -0
  6. package/dist/components/input/key-handlers/handle-arrow.js.flow +12 -0
  7. package/dist/components/input/key-handlers/handle-backspace.d.ts +7 -0
  8. package/dist/components/input/key-handlers/handle-backspace.js.flow +14 -0
  9. package/dist/components/input/key-handlers/handle-exponent.d.ts +3 -0
  10. package/dist/components/input/key-handlers/handle-exponent.js.flow +12 -0
  11. package/dist/components/input/key-handlers/handle-jump-out.d.ts +7 -0
  12. package/dist/components/input/key-handlers/handle-jump-out.js.flow +14 -0
  13. package/dist/components/input/math-wrapper.d.ts +7 -78
  14. package/dist/components/input/math-wrapper.js.flow +16 -78
  15. package/dist/components/input/mathquill-helpers.d.ts +46 -0
  16. package/dist/components/input/mathquill-helpers.js.flow +56 -0
  17. package/dist/components/input/mathquill-instance.d.ts +3 -0
  18. package/dist/components/input/mathquill-instance.js.flow +9 -0
  19. package/dist/components/input/mathquill-types.d.ts +25 -0
  20. package/dist/components/input/mathquill-types.js.flow +34 -0
  21. package/dist/components/key-translator.d.ts +4 -0
  22. package/dist/components/key-translator.js.flow +10 -0
  23. package/dist/components/keypad/button-assets.d.ts +2 -2
  24. package/dist/components/keypad/button-assets.js.flow +2 -2
  25. package/dist/components/keypad/button.d.ts +1 -0
  26. package/dist/components/keypad/button.js.flow +1 -0
  27. package/dist/components/keypad/extras-page/index.d.ts +10 -0
  28. package/dist/components/keypad/extras-page/index.js.flow +15 -0
  29. package/dist/components/keypad/index.d.ts +7 -1
  30. package/dist/components/keypad/index.js.flow +7 -1
  31. package/dist/components/keypad/keypad-page-items.d.ts +1 -1
  32. package/dist/components/keypad/keypad-page-items.js.flow +1 -1
  33. package/dist/components/keypad-legacy/gesture-manager.d.ts +21 -9
  34. package/dist/components/keypad-legacy/gesture-manager.js.flow +27 -12
  35. package/dist/components/keypad-legacy/gesture-state-machine.d.ts +9 -9
  36. package/dist/components/keypad-legacy/gesture-state-machine.js.flow +10 -10
  37. package/dist/components/keypad-legacy/keypad-button.d.ts +2 -2
  38. package/dist/components/keypad-legacy/keypad-button.js.flow +3 -3
  39. package/dist/components/keypad-legacy/store/actions.d.ts +4 -14
  40. package/dist/components/keypad-legacy/store/actions.js.flow +3 -15
  41. package/dist/components/keypad-legacy/store/types.d.ts +2 -2
  42. package/dist/components/keypad-legacy/store/types.js.flow +2 -2
  43. package/dist/components/keypad-legacy/touchable-keypad-button.d.ts +6 -6
  44. package/dist/components/keypad-legacy/touchable-keypad-button.js.flow +9 -14
  45. package/dist/components/keypad-legacy/two-page-keypad.d.ts +6 -4
  46. package/dist/components/keypad-legacy/two-page-keypad.js.flow +6 -4
  47. package/dist/components/tabbar/tabbar.d.ts +6 -9
  48. package/dist/components/tabbar/tabbar.js.flow +6 -9
  49. package/dist/components/tabbar/types.d.ts +1 -1
  50. package/dist/components/tabbar/types.js.flow +1 -1
  51. package/dist/data/key-configs.d.ts +3 -6
  52. package/dist/data/key-configs.js.flow +3 -8
  53. package/dist/data/keys.d.ts +2 -54
  54. package/dist/data/keys.js.flow +116 -55
  55. package/dist/enums.d.ts +2 -9
  56. package/dist/enums.js.flow +2 -11
  57. package/dist/es/index.js +2000 -1346
  58. package/dist/es/index.js.map +1 -1
  59. package/dist/index.d.ts +3 -2
  60. package/dist/index.js +2288 -1392
  61. package/dist/index.js.flow +4 -2
  62. package/dist/index.js.map +1 -1
  63. package/dist/strings.js +26 -10
  64. package/dist/types.d.ts +10 -12
  65. package/dist/types.js.flow +13 -12
  66. package/package.json +2 -1
  67. package/src/components/input/__tests__/context-tracking.test.ts +43 -44
  68. package/src/components/input/__tests__/mathquill.test.ts +133 -135
  69. package/src/components/input/key-handlers/handle-arrow.ts +70 -0
  70. package/src/components/input/key-handlers/handle-backspace.ts +275 -0
  71. package/src/components/input/key-handlers/handle-exponent.ts +52 -0
  72. package/src/components/input/key-handlers/handle-jump-out.ts +103 -0
  73. package/src/components/input/math-input.tsx +11 -12
  74. package/src/components/input/math-wrapper.ts +88 -837
  75. package/src/components/input/mathquill-helpers.ts +268 -0
  76. package/src/components/input/mathquill-instance.ts +5 -0
  77. package/src/components/input/mathquill-types.ts +55 -0
  78. package/src/components/key-translator.ts +209 -0
  79. package/src/components/keypad/button-assets.tsx +441 -100
  80. package/src/components/keypad/button.tsx +7 -2
  81. package/src/components/keypad/extras-page/index.tsx +27 -0
  82. package/src/components/keypad/geometry-page/index.tsx +1 -1
  83. package/src/components/keypad/index.tsx +34 -7
  84. package/src/components/keypad/keypad-mathquill.stories.tsx +82 -0
  85. package/src/components/keypad/keypad-page-items.tsx +3 -1
  86. package/src/components/keypad/operators-page/index.tsx +1 -1
  87. package/src/components/keypad-legacy/echo-manager.tsx +4 -4
  88. package/src/components/keypad-legacy/empty-keypad-button.tsx +6 -4
  89. package/src/components/keypad-legacy/gesture-manager.ts +32 -9
  90. package/src/components/keypad-legacy/gesture-state-machine.ts +14 -14
  91. package/src/components/keypad-legacy/keypad-button.tsx +15 -18
  92. package/src/components/keypad-legacy/many-keypad-button.tsx +9 -2
  93. package/src/components/keypad-legacy/store/actions.ts +3 -29
  94. package/src/components/keypad-legacy/store/echo-reducer.ts +2 -5
  95. package/src/components/keypad-legacy/store/index.ts +4 -10
  96. package/src/components/keypad-legacy/store/input-reducer.ts +1 -2
  97. package/src/components/keypad-legacy/store/keypad-reducer.ts +2 -3
  98. package/src/components/keypad-legacy/store/types.ts +2 -2
  99. package/src/components/keypad-legacy/touchable-keypad-button.tsx +8 -13
  100. package/src/components/keypad-legacy/two-page-keypad.tsx +18 -6
  101. package/src/components/tabbar/__tests__/tabbar.test.tsx +36 -36
  102. package/src/components/tabbar/icons.tsx +68 -52
  103. package/src/components/tabbar/item.tsx +5 -1
  104. package/src/components/tabbar/tabbar.stories.tsx +23 -12
  105. package/src/components/tabbar/tabbar.tsx +22 -38
  106. package/src/components/tabbar/types.ts +1 -1
  107. package/src/data/key-configs.ts +751 -304
  108. package/src/data/keys.ts +118 -65
  109. package/src/enums.ts +10 -9
  110. package/src/index.ts +3 -2
  111. package/src/math-input.stories.tsx +1 -1
  112. package/src/types.ts +10 -12
  113. package/tsconfig-build.tsbuildinfo +1 -1
@@ -0,0 +1,27 @@
1
+ import * as React from "react";
2
+
3
+ import Keys from "../../../data/key-configs";
4
+ import Key from "../../../data/keys";
5
+ import {KeypadPageContainer, KeypadButton} from "../keypad-page-items";
6
+
7
+ type Props = {
8
+ extraKeys: ReadonlyArray<Key>;
9
+ onClickKey: (keyConfig: string) => void;
10
+ };
11
+
12
+ export default class ExtrasPage extends React.Component<Props> {
13
+ render(): React.ReactNode {
14
+ const {extraKeys, onClickKey} = this.props;
15
+ return (
16
+ <KeypadPageContainer>
17
+ {extraKeys.map((key) => (
18
+ <KeypadButton
19
+ key={key}
20
+ keyConfig={Keys[key]}
21
+ onClickKey={onClickKey}
22
+ />
23
+ ))}
24
+ </KeypadPageContainer>
25
+ );
26
+ }
27
+ }
@@ -46,7 +46,7 @@ export default class GeometryPage extends React.Component<Props> {
46
46
  />
47
47
  {/* Row 2 */}
48
48
  <SecondaryKeypadButton
49
- keyConfig={Keys.X}
49
+ keyConfig={Keys.x}
50
50
  onClickKey={onClickKey}
51
51
  style={{
52
52
  gridColumn: 5,
@@ -1,28 +1,41 @@
1
+ import Color from "@khanacademy/wonder-blocks-color";
1
2
  import {View} from "@khanacademy/wonder-blocks-core";
3
+ import {StyleSheet} from "aphrodite";
2
4
  import * as React from "react";
3
5
 
6
+ import Key from "../../data/keys";
4
7
  import Tabbar from "../tabbar/tabbar";
8
+ import {TabbarItemType} from "../tabbar/types";
5
9
 
10
+ import ExtrasPage from "./extras-page";
6
11
  import GeometryPage from "./geometry-page";
7
12
  import NumbersPage from "./numbers-page";
8
13
  import {NumbersPageOptions} from "./numbers-page/types";
9
14
  import OperatorsPage from "./operators-page";
10
15
  import {OperatorsButtonSets} from "./operators-page/types";
11
16
 
12
- import type {TabbarItemType} from "../tabbar/types";
13
-
14
17
  export type Props = {
15
18
  onClickKey: (keyConfig: string) => void;
16
19
  trigonometry?: boolean;
20
+ extraKeys: ReadonlyArray<Key>;
17
21
  } & OperatorsButtonSets &
18
22
  NumbersPageOptions;
23
+
19
24
  type State = {
20
25
  selectedPage: TabbarItemType;
21
26
  };
22
27
 
23
- const allPages = function (props: Props): React.ReactElement {
28
+ type DefaultProps = {
29
+ extraKeys: Props["extraKeys"];
30
+ };
31
+
32
+ function allPages(props: Props): ReadonlyArray<TabbarItemType> {
24
33
  const pages: Array<TabbarItemType> = ["Numbers"];
25
34
 
35
+ if (props.extraKeys?.length) {
36
+ pages.push("Extras");
37
+ }
38
+
26
39
  if (
27
40
  // OperatorsButtonSets
28
41
  props.preAlgebra ||
@@ -32,17 +45,23 @@ const allPages = function (props: Props): React.ReactElement {
32
45
  ) {
33
46
  pages.push("Operators");
34
47
  }
48
+
35
49
  if (props.trigonometry) {
36
50
  pages.push("Geometry");
37
51
  }
38
- // @ts-expect-error [FEI-5003] - TS2739 - Type 'TabbarItemType[]' is missing the following properties from type 'ReactElement<any, string | JSXElementConstructor<any>>': type, props, key
52
+
39
53
  return pages;
40
- };
54
+ }
41
55
 
42
56
  export default class Keypad extends React.Component<Props, State> {
43
57
  state: State = {
44
58
  selectedPage: "Numbers",
45
59
  };
60
+
61
+ static defaultProps: DefaultProps = {
62
+ extraKeys: [],
63
+ };
64
+
46
65
  render(): React.ReactNode {
47
66
  const {selectedPage} = this.state;
48
67
 
@@ -51,13 +70,15 @@ export default class Keypad extends React.Component<Props, State> {
51
70
  return (
52
71
  <View>
53
72
  <Tabbar
54
- // @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call.
55
73
  items={availablePages}
56
- onSelect={(tabbarItem: TabbarItemType) => {
74
+ selectedItem={selectedPage}
75
+ onSelectItem={(tabbarItem: TabbarItemType) => {
57
76
  this.setState({selectedPage: tabbarItem});
58
77
  }}
78
+ style={styles.tabbar}
59
79
  />
60
80
  {selectedPage === "Numbers" && <NumbersPage {...this.props} />}
81
+ {selectedPage === "Extras" && <ExtrasPage {...this.props} />}
61
82
  {selectedPage === "Operators" && (
62
83
  <OperatorsPage {...this.props} />
63
84
  )}
@@ -68,3 +89,9 @@ export default class Keypad extends React.Component<Props, State> {
68
89
  );
69
90
  }
70
91
  }
92
+
93
+ const styles = StyleSheet.create({
94
+ tabbar: {
95
+ background: Color.white,
96
+ },
97
+ });
@@ -0,0 +1,82 @@
1
+ import Color from "@khanacademy/wonder-blocks-color";
2
+ import {Popover} from "@khanacademy/wonder-blocks-popover";
3
+ import MathQuill from "mathquill";
4
+ import * as React from "react";
5
+
6
+ import Key from "../../data/keys";
7
+ import keyTranslator from "../key-translator";
8
+
9
+ import Keypad from "./index";
10
+
11
+ export default {
12
+ title: "v2 Keypad With Mathquill",
13
+ };
14
+
15
+ const mathQuillConfig = {
16
+ autoCommands: "pi theta phi sqrt nthroot",
17
+ charsThatBreakOutOfSupSub: "+-*/=<>≠≤≥",
18
+ supSubsRequireOperand: true,
19
+ spaceBehavesLikeTab: true,
20
+ };
21
+
22
+ export function V2KeypadWithMathquill() {
23
+ const mathquillWrapperRef = React.useRef<HTMLDivElement>(null);
24
+ const [mathQuill, setMathQuill] = React.useState<MathQuill>();
25
+
26
+ React.useEffect(() => {
27
+ if (!mathQuill && mathquillWrapperRef.current) {
28
+ const MQ = MathQuill.getInterface(2);
29
+ const mathQuillInstance = MQ.MathField(
30
+ mathquillWrapperRef.current,
31
+ mathQuillConfig,
32
+ );
33
+ setMathQuill(mathQuillInstance);
34
+ }
35
+ }, [mathQuill]);
36
+
37
+ function handleClickKey(key: Key) {
38
+ if (!mathQuill) {
39
+ return;
40
+ }
41
+
42
+ const mathQuillCallback = keyTranslator[key];
43
+ if (mathQuillCallback) {
44
+ mathQuillCallback(mathQuill, key);
45
+ } else {
46
+ // eslint-disable-next-line no-console
47
+ console.warn(`No translation to Mathquill for: ${key}`);
48
+ }
49
+ }
50
+
51
+ return (
52
+ <div style={{maxWidth: "400px", margin: "2em"}}>
53
+ <Popover
54
+ content={
55
+ <div>
56
+ <Keypad
57
+ extraKeys={["a", "b", "c"]}
58
+ onClickKey={handleClickKey}
59
+ advancedRelations
60
+ basicRelations
61
+ divisionKey
62
+ logarithms
63
+ multiplicationDot
64
+ preAlgebra
65
+ trigonometry
66
+ />
67
+ </div>
68
+ }
69
+ dismissEnabled
70
+ >
71
+ <div
72
+ style={{
73
+ width: "100%",
74
+ marginBottom: "1em",
75
+ border: `1px solid ${Color.offBlack16}`,
76
+ }}
77
+ ref={mathquillWrapperRef}
78
+ />
79
+ </Popover>
80
+ </div>
81
+ );
82
+ }
@@ -1,10 +1,11 @@
1
1
  import {View} from "@khanacademy/wonder-blocks-core";
2
2
  import * as React from "react";
3
3
 
4
+ import {KeyConfig} from "../../types";
5
+
4
6
  import Button from "./button";
5
7
  import ButtonAsset from "./button-assets";
6
8
 
7
- import type {KeyConfig} from "../../data/key-configs";
8
9
  import type {StyleType} from "@khanacademy/wonder-blocks-core";
9
10
 
10
11
  type KeypadPageContainerProps = {
@@ -46,6 +47,7 @@ export const KeypadButton = ({
46
47
  onPress={() => onClickKey(keyConfig.id)}
47
48
  tintColor={tintColor}
48
49
  style={style}
50
+ ariaLabel={keyConfig.id}
49
51
  >
50
52
  <ButtonAsset id={keyConfig.id} />
51
53
  </Button>
@@ -42,7 +42,7 @@ export default class OperatorsPage extends React.Component<Props> {
42
42
  placeholder={!this.props.logarithms}
43
43
  />
44
44
  <SecondaryKeypadButton
45
- keyConfig={Keys.X}
45
+ keyConfig={Keys.x}
46
46
  onClickKey={onClickKey}
47
47
  style={{
48
48
  gridColumn: 5,
@@ -6,8 +6,8 @@ import * as React from "react";
6
6
  import {TransitionGroup, CSSTransition} from "react-transition-group";
7
7
 
8
8
  import KeyConfigs from "../../data/key-configs";
9
- import Keys from "../../data/keys";
10
- import {KeyType, EchoAnimationType} from "../../enums";
9
+ import Key from "../../data/keys";
10
+ import {EchoAnimationType} from "../../enums";
11
11
 
12
12
  import KeypadButton from "./keypad-button";
13
13
  import * as zIndexes from "./z-indexes";
@@ -16,7 +16,7 @@ import type {Bound, Echo as EchoType} from "../../types";
16
16
 
17
17
  type EchoProps = {
18
18
  animationDurationMs: number;
19
- id: Keys;
19
+ id: Key;
20
20
  initialBounds: Bound;
21
21
  onAnimationFinish: () => void;
22
22
  };
@@ -50,7 +50,7 @@ class Echo extends React.Component<EchoProps> {
50
50
  // applied via StyleSheet, will override our inlines.
51
51
  return (
52
52
  <div style={containerStyle}>
53
- <KeypadButton icon={icon} type={KeyType.ECHO} />
53
+ <KeypadButton icon={icon} type={"ECHO"} />
54
54
  </div>
55
55
  );
56
56
  }
@@ -27,14 +27,16 @@ class EmptyKeypadButton extends React.Component<ReduxProps> {
27
27
  // to focus them or trigger presses.
28
28
  return (
29
29
  <KeypadButton
30
- onTouchStart={(evt: TouchEvent) =>
30
+ onTouchStart={(evt: React.TouchEvent<HTMLDivElement>) =>
31
31
  gestureManager.onTouchStart(evt)
32
32
  }
33
- onTouchEnd={(evt: TouchEvent) => gestureManager.onTouchEnd(evt)}
34
- onTouchMove={(evt: TouchEvent) =>
33
+ onTouchEnd={(evt: React.TouchEvent<HTMLDivElement>) =>
34
+ gestureManager.onTouchEnd(evt)
35
+ }
36
+ onTouchMove={(evt: React.TouchEvent<HTMLDivElement>) =>
35
37
  gestureManager.onTouchMove(evt)
36
38
  }
37
- onTouchCancel={(evt: TouchEvent) =>
39
+ onTouchCancel={(evt: React.TouchEvent<HTMLDivElement>) =>
38
40
  gestureManager.onTouchCancel(evt)
39
41
  }
40
42
  {...KeyConfigs.NOOP}
@@ -3,6 +3,10 @@
3
3
  * connects our various bits of logic for managing gestures and interactions,
4
4
  * and links them together.
5
5
  */
6
+ import * as React from "react";
7
+
8
+ import Key from "../../data/keys";
9
+ import {ActiveNodesObj, LayoutProps} from "../../types";
6
10
 
7
11
  import GestureStateMachine from "./gesture-state-machine";
8
12
  import NodeManager from "./node-manager";
@@ -12,6 +16,17 @@ const coordsForEvent = (evt) => {
12
16
  return [evt.changedTouches[0].clientX, evt.changedTouches[0].clientY];
13
17
  };
14
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
+
15
30
  class GestureManager {
16
31
  swipeEnabled: boolean;
17
32
  trackEvents: boolean;
@@ -19,7 +34,12 @@ class GestureManager {
19
34
  popoverStateMachine: PopoverStateMachine;
20
35
  gestureStateMachine: GestureStateMachine;
21
36
 
22
- constructor(options, handlers, disabledSwipeKeys, multiPressableKeys) {
37
+ constructor(
38
+ options: Options,
39
+ handlers: Handlers,
40
+ disabledSwipeKeys: ReadonlyArray<Key>,
41
+ multiPressableKeys: ReadonlyArray<Key>,
42
+ ) {
23
43
  const {swipeEnabled} = options;
24
44
 
25
45
  this.swipeEnabled = swipeEnabled;
@@ -96,11 +116,14 @@ class GestureManager {
96
116
  * Handle a touch-start event that originated in a node registered with the
97
117
  * gesture system.
98
118
  *
99
- * @param {TouchEvent} evt - the raw touch event from the browser
119
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
100
120
  * @param {string} id - the identifier of the DOM node in which the touch
101
121
  * occurred
102
122
  */
103
- onTouchStart(evt: TouchEvent, id?) {
123
+ onTouchStart(
124
+ evt: React.TouchEvent<HTMLDivElement>,
125
+ id?: string | undefined,
126
+ ) {
104
127
  if (!this.trackEvents) {
105
128
  return;
106
129
  }
@@ -128,9 +151,9 @@ class GestureManager {
128
151
  * Handle a touch-move event that originated in a node registered with the
129
152
  * gesture system.
130
153
  *
131
- * @param {TouchEvent} evt - the raw touch event from the browser
154
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
132
155
  */
133
- onTouchMove(evt: TouchEvent) {
156
+ onTouchMove(evt: React.TouchEvent<HTMLDivElement>) {
134
157
  if (!this.trackEvents) {
135
158
  return;
136
159
  }
@@ -152,9 +175,9 @@ class GestureManager {
152
175
  * Handle a touch-end event that originated in a node registered with the
153
176
  * gesture system.
154
177
  *
155
- * @param {TouchEvent} evt - the raw touch event from the browser
178
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
156
179
  */
157
- onTouchEnd(evt: TouchEvent) {
180
+ onTouchEnd(evt: React.TouchEvent<HTMLDivElement>) {
158
181
  if (!this.trackEvents) {
159
182
  return;
160
183
  }
@@ -173,9 +196,9 @@ class GestureManager {
173
196
  * Handle a touch-cancel event that originated in a node registered with the
174
197
  * gesture system.
175
198
  *
176
- * @param {TouchEvent} evt - the raw touch event from the browser
199
+ * @param {React.TouchEvent<HTMLDivElement>} evt - the raw touch event from the browser
177
200
  */
178
- onTouchCancel(evt: TouchEvent) {
201
+ onTouchCancel(evt: React.TouchEvent<HTMLDivElement>) {
179
202
  if (!this.trackEvents) {
180
203
  return;
181
204
  }
@@ -1,4 +1,4 @@
1
- import Keys from "../../data/keys";
1
+ import Key from "../../data/keys";
2
2
 
3
3
  /**
4
4
  * The state machine that backs our gesture system. In particular, this state
@@ -14,8 +14,8 @@ export type Handlers = {
14
14
  onBlur: () => void;
15
15
  onTrigger: (id: string) => void;
16
16
  onLongPress: (id: string) => void;
17
- onSwipeChange: (x: number) => void;
18
- onSwipeEnd: (x: number) => void;
17
+ onSwipeChange?: (x: number) => void;
18
+ onSwipeEnd?: (x: number) => void;
19
19
  onTouchEnd: (id: string) => void;
20
20
  };
21
21
 
@@ -26,17 +26,17 @@ type Options = {
26
26
  };
27
27
 
28
28
  type TouchState = {
29
- activeNodeId: Keys;
29
+ activeNodeId: Key;
30
30
  pressAndHoldIntervalId: number | null;
31
31
  longPressTimeoutId: number | null;
32
32
  swipeLocked: boolean;
33
33
  startX: number;
34
34
  };
35
35
 
36
- type TouchStateMap = Record<Keys, TouchState>;
36
+ type TouchStateMap = Record<Key, TouchState>;
37
37
 
38
38
  type SwipeState = {
39
- touchId: Keys;
39
+ touchId: Key;
40
40
  startX: number;
41
41
  };
42
42
 
@@ -49,16 +49,16 @@ const defaultOptions: Options = {
49
49
  class GestureStateMachine {
50
50
  handlers: Handlers;
51
51
  options: Options;
52
- swipeDisabledNodeIds: Partial<[Keys]>;
53
- multiPressableKeys: Partial<[Keys]>;
52
+ swipeDisabledNodeIds: ReadonlyArray<Key>;
53
+ multiPressableKeys: ReadonlyArray<Key>;
54
54
  touchState: Partial<TouchStateMap>;
55
55
  swipeState: SwipeState | null;
56
56
 
57
57
  constructor(
58
58
  handlers: Handlers,
59
59
  options: Partial<Options>,
60
- swipeDisabledNodeIds?: [Keys],
61
- multiPressableKeys?: [Keys],
60
+ swipeDisabledNodeIds?: ReadonlyArray<Key>,
61
+ multiPressableKeys?: ReadonlyArray<Key>,
62
62
  ) {
63
63
  this.handlers = handlers;
64
64
  this.options = {
@@ -234,7 +234,7 @@ class GestureStateMachine {
234
234
  // Only respect the finger that started a swipe. Any other lingering
235
235
  // gestures are ignored.
236
236
  if (this.swipeState.touchId === touchId) {
237
- this.handlers.onSwipeChange(pageX - this.swipeState.startX);
237
+ this.handlers.onSwipeChange?.(pageX - this.swipeState.startX);
238
238
  }
239
239
  } else if (this.touchState[touchId]) {
240
240
  // It could be touch events started outside the keypad and
@@ -256,7 +256,7 @@ class GestureStateMachine {
256
256
  touchId,
257
257
  startX,
258
258
  };
259
- this.handlers.onSwipeChange(pageX - this.swipeState.startX);
259
+ this.handlers.onSwipeChange?.(pageX - this.swipeState.startX);
260
260
  } else {
261
261
  const id = getId();
262
262
  if (id !== activeNodeId) {
@@ -279,7 +279,7 @@ class GestureStateMachine {
279
279
  // Only respect the finger that started a swipe. Any other lingering
280
280
  // gestures are ignored.
281
281
  if (this.swipeState.touchId === touchId) {
282
- this.handlers.onSwipeEnd(pageX - this.swipeState.startX);
282
+ this.handlers.onSwipeEnd?.(pageX - this.swipeState.startX);
283
283
  this.swipeState = null;
284
284
  }
285
285
  } else if (this.touchState[touchId]) {
@@ -313,7 +313,7 @@ class GestureStateMachine {
313
313
  // displacement.
314
314
  if (this.swipeState) {
315
315
  if (this.swipeState.touchId === touchId) {
316
- this.handlers.onSwipeEnd(0);
316
+ this.handlers.onSwipeEnd?.(0);
317
317
  this.swipeState = null;
318
318
  }
319
319
  } else if (this.touchState[touchId]) {
@@ -6,7 +6,7 @@ import {StyleSheet, css} from "aphrodite";
6
6
  import * as React from "react";
7
7
  import {connect} from "react-redux";
8
8
 
9
- import {BorderDirection, BorderStyles, KeyType} from "../../enums";
9
+ import {BorderDirection, BorderStyles, KeyType, KeyTypes} from "../../enums";
10
10
  import {View} from "../../fake-react-native-web/index";
11
11
  import {
12
12
  wonderBlocksBlue,
@@ -40,7 +40,7 @@ interface Props extends ReduxProps {
40
40
  focused: boolean;
41
41
  popoverEnabled: boolean;
42
42
  type: KeyType;
43
- icon: IconConfig;
43
+ icon?: IconConfig;
44
44
  style?: StyleType;
45
45
  onTouchCancel?: (evt: React.TouchEvent<HTMLDivElement>) => void;
46
46
  onTouchEnd?: (evt: React.TouchEvent<HTMLDivElement>) => void;
@@ -101,7 +101,7 @@ class KeypadButton extends React.PureComponent<Props> {
101
101
  // object. This method must be called whenever a property that
102
102
  // influences the possible outcomes of `this._getFocusStyle` and
103
103
  // `this._getButtonStyle` changes (such as `this.buttonSizeStyle`).
104
- for (const type of Object.values(KeyType)) {
104
+ for (const type of KeyTypes) {
105
105
  css(View.styles.initial, ...this._getFocusStyle(type));
106
106
 
107
107
  for (const borders of Object.values(BorderStyles)) {
@@ -115,10 +115,7 @@ class KeypadButton extends React.PureComponent<Props> {
115
115
 
116
116
  _getFocusStyle = (type: KeyType) => {
117
117
  let focusBackgroundStyle;
118
- if (
119
- type === KeyType.INPUT_NAVIGATION ||
120
- type === KeyType.KEYPAD_NAVIGATION
121
- ) {
118
+ if (type === "INPUT_NAVIGATION" || type === "KEYPAD_NAVIGATION") {
122
119
  focusBackgroundStyle = styles.light;
123
120
  } else {
124
121
  focusBackgroundStyle = styles.bright;
@@ -131,25 +128,25 @@ class KeypadButton extends React.PureComponent<Props> {
131
128
  // Select the appropriate style for the button.
132
129
  let backgroundStyle;
133
130
  switch (type) {
134
- case KeyType.EMPTY:
131
+ case "EMPTY":
135
132
  backgroundStyle = styles.empty;
136
133
  break;
137
134
 
138
- case KeyType.MANY:
139
- case KeyType.VALUE:
135
+ case "MANY":
136
+ case "VALUE":
140
137
  backgroundStyle = styles.value;
141
138
  break;
142
139
 
143
- case KeyType.OPERATOR:
140
+ case "OPERATOR":
144
141
  backgroundStyle = styles.operator;
145
142
  break;
146
143
 
147
- case KeyType.INPUT_NAVIGATION:
148
- case KeyType.KEYPAD_NAVIGATION:
144
+ case "INPUT_NAVIGATION":
145
+ case "KEYPAD_NAVIGATION":
149
146
  backgroundStyle = styles.control;
150
147
  break;
151
148
 
152
- case KeyType.ECHO:
149
+ case "ECHO":
153
150
  backgroundStyle = null;
154
151
  break;
155
152
  }
@@ -168,7 +165,7 @@ class KeypadButton extends React.PureComponent<Props> {
168
165
  styles.buttonBase,
169
166
  backgroundStyle,
170
167
  ...borderStyle,
171
- type === KeyType.ECHO && styles.echo,
168
+ type === "ECHO" && styles.echo,
172
169
  this.buttonSizeStyle,
173
170
  // React Native allows you to set the 'style' props on user defined
174
171
  // components.
@@ -197,7 +194,7 @@ class KeypadButton extends React.PureComponent<Props> {
197
194
  // We render in the focus state if the key is focused, or if it's an
198
195
  // echo.
199
196
  const renderFocused =
200
- (!disabled && focused) || popoverEnabled || type === KeyType.ECHO;
197
+ (!disabled && focused) || popoverEnabled || type === "ECHO";
201
198
  const buttonStyle = this._getButtonStyle(type, borders, style);
202
199
  const focusStyle = this._getFocusStyle(type);
203
200
  const iconWrapperStyle = [
@@ -218,9 +215,9 @@ class KeypadButton extends React.PureComponent<Props> {
218
215
  childKeys &&
219
216
  childKeys.length > 0 && <CornerDecal style={styles.decalInset} />;
220
217
 
221
- if (type === KeyType.EMPTY) {
218
+ if (type === "EMPTY") {
222
219
  return <View style={buttonStyle} {...eventHandlers} />;
223
- } else if (type === KeyType.MANY) {
220
+ } else if (type === "MANY") {
224
221
  // TODO(charlie): Make the long-press interaction accessible. See
225
222
  // the TODO in key-configs.js for more.
226
223
  const manyButtonA11yMarkup = {
@@ -6,7 +6,7 @@
6
6
  import * as React from "react";
7
7
 
8
8
  import KeyConfigs from "../../data/key-configs";
9
- import {KeyType} from "../../enums";
9
+ import {IconType} from "../../enums";
10
10
  import {KeyConfig} from "../../types";
11
11
 
12
12
  import EmptyKeypadButton from "./empty-keypad-button";
@@ -35,8 +35,15 @@ class ManyKeypadButton extends React.Component<Props> {
35
35
  } else {
36
36
  const keyConfig: KeyConfig = {
37
37
  id: "MANY",
38
- type: KeyType.MANY,
38
+ type: "MANY",
39
39
  childKeyIds: keys,
40
+ ariaLabel: keys
41
+ .map((key) => KeyConfigs[key].ariaLabel)
42
+ .join(", "),
43
+ icon: {
44
+ type: IconType.SVG,
45
+ data: "many",
46
+ },
40
47
  };
41
48
  return <TouchableKeypadButton keyConfig={keyConfig} {...rest} />;
42
49
  }
@@ -1,4 +1,4 @@
1
- import Keys from "../../../data/keys";
1
+ import Key from "../../../data/keys";
2
2
 
3
3
  import type {
4
4
  Bound,
@@ -104,30 +104,6 @@ export const setCursor = (cursor: Cursor): SetCursorAction => {
104
104
  };
105
105
 
106
106
  // Gesture actions
107
- type OnSwipeChangeAction = {
108
- type: "OnSwipeChange";
109
- dx: number;
110
- };
111
-
112
- export const onSwipeChange = (dx: number): OnSwipeChangeAction => {
113
- return {
114
- type: "OnSwipeChange",
115
- dx,
116
- };
117
- };
118
-
119
- type OnSwipeEndAction = {
120
- type: "OnSwipeEnd";
121
- dx: number;
122
- };
123
-
124
- export const onSwipeEnd = (dx: number): OnSwipeEndAction => {
125
- return {
126
- type: "OnSwipeEnd",
127
- dx,
128
- };
129
- };
130
-
131
107
  type SetActiveNodesAction = {
132
108
  type: "SetActiveNodes";
133
109
  activeNodes: any;
@@ -144,13 +120,13 @@ export const setActiveNodes = (
144
120
 
145
121
  type PressKeyAction = {
146
122
  type: "PressKey";
147
- key: Keys;
123
+ key: Key;
148
124
  initialBounds: Bound;
149
125
  inPopover: boolean;
150
126
  };
151
127
 
152
128
  export const pressKey = (
153
- key: Keys,
129
+ key: Key,
154
130
  initialBounds: Bound,
155
131
  inPopover: any,
156
132
  ): PressKeyAction => {
@@ -170,7 +146,5 @@ export type Action =
170
146
  | RemoveEchoAction
171
147
  | SetKeyHandlerAction
172
148
  | SetCursorAction
173
- | OnSwipeChangeAction
174
- | OnSwipeEndAction
175
149
  | SetActiveNodesAction
176
150
  | PressKeyAction;