@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,89 +0,0 @@
1
- import NodeManager from "../node-manager";
2
-
3
- describe("NodeManager", () => {
4
- let nodeManager;
5
-
6
- beforeEach(() => {
7
- nodeManager = new NodeManager();
8
- });
9
-
10
- it("should register a single node with no children", () => {
11
- const nodeId = "1";
12
- nodeManager.registerDOMNode(nodeId, {}, []);
13
-
14
- expect(nodeManager._nodesById[nodeId]).toBeTruthy();
15
- expect(nodeManager._orderedIds.includes(nodeId)).toBeTruthy();
16
- });
17
-
18
- it("should register a single node with children", () => {
19
- const nodeId = "1";
20
- const childNodeIds = ["2", "3"];
21
- nodeManager.registerDOMNode(nodeId, {}, childNodeIds);
22
-
23
- expect(nodeManager._orderedIds.includes(nodeId)).toBeTruthy();
24
- expect(nodeManager._nodesById[nodeId]).toBeTruthy();
25
-
26
- for (const childId of childNodeIds) {
27
- // The children should appear in the list of ordered IDs, but not
28
- // in the list of registered nodes.
29
- expect(!nodeManager._nodesById[childId]).toBeTruthy();
30
- expect(nodeManager._orderedIds.includes(childId)).toBeTruthy();
31
- }
32
- });
33
-
34
- it("should order children ahead of their parents", () => {
35
- const nodeId = "1";
36
- const childNodeIds = ["2", "3"];
37
- nodeManager.registerDOMNode(nodeId, {}, childNodeIds);
38
-
39
- const parentIndex = nodeManager._orderedIds.indexOf(nodeId);
40
- for (const childId of childNodeIds) {
41
- // The children should appear ahead of the parent in the ordered
42
- // list.
43
- const childIndex = nodeManager._orderedIds.indexOf(childId);
44
- expect(childIndex < parentIndex).toBeTruthy();
45
- }
46
- });
47
-
48
- it("should de-dupe the list of node IDs", () => {
49
- const nodeId = "1";
50
- const childNodeId = "2";
51
-
52
- // Register both nodes.
53
- nodeManager.registerDOMNode(nodeId, {}, [childNodeId]);
54
- nodeManager.registerDOMNode(childNodeId, {}, []);
55
-
56
- // Verify that both were added to the list of DOM nodes.
57
- for (const id of [nodeId, childNodeId]) {
58
- expect(nodeManager._nodesById[id]).toBeTruthy();
59
- }
60
-
61
- // Verify that the child is ahead of the parent, and only appears once.
62
- expect(nodeManager._orderedIds).toStrictEqual([childNodeId, nodeId]);
63
- });
64
-
65
- it("should handle multiple sets of children", () => {
66
- const firstNodeId = "1";
67
- const firstNodeChildIds = ["2", "3"];
68
- const secondNodeId = "4";
69
- const secondNodeChildIds = ["5", "6"];
70
- const nodeChildIdPairs = [
71
- [firstNodeId, firstNodeChildIds],
72
- [secondNodeId, secondNodeChildIds],
73
- ];
74
-
75
- for (const [nodeId, childNodeIds] of nodeChildIdPairs) {
76
- nodeManager.registerDOMNode(nodeId, {}, childNodeIds);
77
- }
78
-
79
- for (const [nodeId, childNodeIds] of nodeChildIdPairs) {
80
- const parentIndex = nodeManager._orderedIds.indexOf(nodeId);
81
- for (const childId of childNodeIds) {
82
- // The children should appear ahead of the parent in the
83
- // ordered list.
84
- const childIndex = nodeManager._orderedIds.indexOf(childId);
85
- expect(childIndex < parentIndex).toBeTruthy();
86
- }
87
- }
88
- });
89
- });
@@ -1,205 +0,0 @@
1
- /**
2
- * An algorithm for computing the appropriate layout parameters for the keypad,
3
- * including the size of the buttons and whether or not to render fullscreen,
4
- * taking into account a number of factors including the size of the screen, the
5
- * orientation of the screen, the presence of browser chrome, the presence of
6
- * other exercise-related chrome, the size of the input box, the parameters that
7
- * define the keypad (i.e., the number of rows, columns, and pages), and so
8
- * forth.
9
- *
10
- * The computations herein make some strong assumptions about the sizes of
11
- * various other elements and the situations under which they will be visible
12
- * (e.g., browser chrome). However, this is just a heuristic--it's not crucial
13
- * that our buttons are sized in a pixel-perfect manner, but rather, that we
14
- * make a balanced use of space.
15
- *
16
- * Note that one goal of the algorithm is to avoid resizing the keypad in the
17
- * face of dynamic browser chrome. In order to avoid that awkwardness, we tend
18
- * to be conservative in our measurements and make things smaller than they
19
- * might need to be.
20
- */
21
-
22
- import {DeviceOrientation, LayoutMode} from "../../enums";
23
- import {
24
- pageIndicatorHeightPx,
25
- toolbarHeightPx,
26
- navigationPadWidthPx,
27
- innerBorderWidthPx,
28
- } from "../common-style";
29
-
30
- import type {GridDimensions, WidthHeight} from "./store/types";
31
-
32
- const minButtonHeight = 48;
33
- const maxButtonSize = 64;
34
- const minSpaceAboveKeypad = 32;
35
-
36
- // These values are taken from an iPhone 5, but should be consistent with the
37
- // iPhone 4 as well. Regardless, these are meant to be representative of the
38
- // possible types of browser chrome that could appear in various context, rather
39
- // than pixel-perfect for every device.
40
- const safariNavBarWhenShrunk = 44;
41
- const safariNavBarWhenExpanded = 64;
42
- const safariToolbar = 44;
43
-
44
- // In mobile Safari, the browser chrome is completely hidden in landscape,
45
- // though a shrunken navbar and full-sized toolbar on scroll. In portrait, the
46
- // shrunken navbar is always visible, but expands on scroll (and the toolbar
47
- // appears as well).
48
- const maxLandscapeBrowserChrome = safariNavBarWhenShrunk + safariToolbar;
49
- const maxPortraitBrowserChrome =
50
- safariToolbar + (safariNavBarWhenExpanded - safariNavBarWhenShrunk);
51
-
52
- // This represents the 'worst case' aspect ratio that we care about (for
53
- // portrait layouts). It's taken from the iPhone 4. The height is computed by
54
- // taking the height of the device and removing the persistent, shrunken navbar.
55
- // (We don't need to account for the expanded navbar, since we include the
56
- // difference when reserving space above the keypad.)
57
- const worstCaseAspectRatio = 320 / (480 - safariNavBarWhenShrunk);
58
-
59
- type ComputedLayoutProperty = {
60
- buttonDimensions: WidthHeight;
61
- layoutMode: LayoutMode;
62
- };
63
-
64
- function getButtonWidth(
65
- gridDimensions: GridDimensions,
66
- containerDimensions: WidthHeight,
67
- navigationPadEnabled: boolean,
68
- paginationEnabled: boolean,
69
- isLandscape: boolean,
70
- ): number {
71
- const {numColumns, numPages} = gridDimensions;
72
-
73
- // We can use the container width as the effective width.
74
- let effectiveWidth = containerDimensions.width;
75
- if (navigationPadEnabled) {
76
- effectiveWidth -= navigationPadWidthPx;
77
- }
78
-
79
- let buttonWidthPx;
80
- if (numPages > 1) {
81
- const effectiveNumColumns = paginationEnabled
82
- ? numColumns
83
- : numColumns * numPages;
84
- buttonWidthPx = effectiveWidth / effectiveNumColumns;
85
- } else {
86
- buttonWidthPx = isLandscape
87
- ? maxButtonSize
88
- : effectiveWidth / numColumns;
89
- }
90
-
91
- return buttonWidthPx;
92
- }
93
-
94
- function getButtonHeight(
95
- gridDimensions: GridDimensions,
96
- pageDimensions: WidthHeight,
97
- containerDimensions: WidthHeight,
98
- paginationEnabled: boolean,
99
- toolbarEnabled: boolean,
100
- isLandscape: boolean,
101
- ) {
102
- const {numMaxVisibleRows} = gridDimensions;
103
-
104
- // In many cases, the browser chrome will already have been factored
105
- // into `pageHeight`. But we have no way of knowing if that's
106
- // the case or not. As such, we take a conservative approach and
107
- // assume that the chrome is _never_ included in `pageHeight`.
108
- const browserChromeHeight = isLandscape
109
- ? maxLandscapeBrowserChrome
110
- : maxPortraitBrowserChrome;
111
-
112
- // Count up all the space that we need to reserve on the page.
113
- // Namely, we need to account for:
114
- // 1. Space between the keypad and the top of the page.
115
- // 2. The presence of the exercise toolbar.
116
- // 3. The presence of the view pager indicator.
117
- // 4. Any browser chrome that may appear later.
118
- const reservedSpace =
119
- minSpaceAboveKeypad +
120
- browserChromeHeight +
121
- (toolbarEnabled ? toolbarHeightPx : 0) +
122
- (paginationEnabled ? pageIndicatorHeightPx : 0);
123
-
124
- // For the height, we take
125
- // another conservative measure when in portrait by assuming that
126
- // the device has the worst possible aspect ratio. In other words,
127
- // we ignore the device height in portrait and assume the worst.
128
- // This prevents the keypad from changing size when browser chrome
129
- // appears and disappears.
130
- const effectiveHeight = isLandscape
131
- ? pageDimensions.height
132
- : containerDimensions.width / worstCaseAspectRatio;
133
-
134
- // In computing the
135
- // height, accommodate for the maximum number of rows that will ever be
136
- // visible (since the toggling of popovers can increase the number of
137
- // visible rows).
138
- const maxKeypadHeight = effectiveHeight - reservedSpace;
139
-
140
- const buttonHeightPx = Math.max(
141
- Math.min(maxKeypadHeight / numMaxVisibleRows, maxButtonSize),
142
- minButtonHeight,
143
- );
144
-
145
- return buttonHeightPx;
146
- }
147
-
148
- export const computeLayoutParameters = (
149
- gridDimensions: GridDimensions,
150
- pageDimensions: WidthHeight,
151
- containerDimensions: WidthHeight,
152
- deviceOrientation: DeviceOrientation,
153
- navigationPadEnabled: boolean,
154
- paginationEnabled: boolean,
155
- toolbarEnabled: boolean,
156
- ): ComputedLayoutProperty => {
157
- const {numColumns, numPages} = gridDimensions;
158
-
159
- // First, compute some values that will be used in multiple computations.
160
- const effectiveNumColumns = paginationEnabled
161
- ? numColumns
162
- : numColumns * numPages;
163
-
164
- // Then, compute the button dimensions based on the provided parameters.
165
- const isLandscape = deviceOrientation === DeviceOrientation.LANDSCAPE;
166
-
167
- const buttonWidth = getButtonWidth(
168
- gridDimensions,
169
- containerDimensions,
170
- navigationPadEnabled,
171
- paginationEnabled,
172
- isLandscape,
173
- );
174
-
175
- const buttonHeight = getButtonHeight(
176
- gridDimensions,
177
- pageDimensions,
178
- containerDimensions,
179
- paginationEnabled,
180
- toolbarEnabled,
181
- isLandscape,
182
- );
183
-
184
- const buttonDimensions = {
185
- width: buttonWidth,
186
- height: buttonHeight,
187
- };
188
-
189
- // Finally, determine whether the keypad should be rendered in the
190
- // fullscreen layout by determining its resultant width.
191
- const numSeparators =
192
- (navigationPadEnabled ? 1 : 0) +
193
- (!paginationEnabled ? numPages - 1 : 0);
194
- const keypadWidth =
195
- effectiveNumColumns * buttonDimensions.width +
196
- (navigationPadEnabled ? navigationPadWidthPx : 0) +
197
- numSeparators * innerBorderWidthPx;
198
- return {
199
- buttonDimensions,
200
- layoutMode:
201
- keypadWidth >= containerDimensions.width
202
- ? LayoutMode.FULLSCREEN
203
- : LayoutMode.COMPACT,
204
- };
205
- };
@@ -1,56 +0,0 @@
1
- /**
2
- * A small triangular decal to sit in the corner of a parent component.
3
- */
4
-
5
- import {StyleSheet} from "aphrodite";
6
- import * as React from "react";
7
-
8
- import {View} from "../../fake-react-native-web/index";
9
- import {offBlack} from "../common-style";
10
-
11
- import type {StyleType} from "@khanacademy/wonder-blocks-core";
12
-
13
- type Props = {
14
- style: StyleType;
15
- };
16
-
17
- class CornerDecal extends React.Component<Props> {
18
- render() {
19
- const {style} = this.props;
20
-
21
- const containerStyle = [
22
- styles.container,
23
- ...(Array.isArray(style) ? style : [style]),
24
- ];
25
-
26
- return (
27
- <View style={containerStyle}>
28
- <svg
29
- width={triangleSizePx}
30
- height={triangleSizePx}
31
- viewBox="4 4 8 8"
32
- >
33
- <path
34
- fill={offBlack}
35
- opacity="0.3"
36
- d="M5.29289322,5.70710678 L10.2928932,10.7071068 C10.9228581,11.3370716 12,10.8909049 12,10 L12,5 C12,4.44771525 11.5522847,4 11,4 L6,4 C5.10909515,4 4.66292836,5.07714192 5.29289322,5.70710678 Z" // @Nolint
37
- />
38
- </svg>
39
- </View>
40
- );
41
- }
42
- }
43
-
44
- const triangleSizePx = 7;
45
-
46
- const styles = StyleSheet.create({
47
- container: {
48
- position: "absolute",
49
- top: 0,
50
- right: 0,
51
- width: triangleSizePx,
52
- height: triangleSizePx,
53
- },
54
- });
55
-
56
- export default CornerDecal;
@@ -1,152 +0,0 @@
1
- /**
2
- * A component that renders and animates the selection state effect effect.
3
- */
4
-
5
- import * as React from "react";
6
- import {TransitionGroup, CSSTransition} from "react-transition-group";
7
-
8
- import KeyConfigs from "../../data/key-configs";
9
- import {EchoAnimationType} from "../../enums";
10
-
11
- import KeypadButton from "./keypad-button";
12
- import * as zIndexes from "./z-indexes";
13
-
14
- import type Key from "../../data/keys";
15
- import type {Bound, Echo as EchoType} from "../../types";
16
-
17
- type EchoProps = {
18
- animationDurationMs: number;
19
- id: Key;
20
- initialBounds: Bound;
21
- onAnimationFinish: () => void;
22
- };
23
-
24
- class Echo extends React.Component<EchoProps> {
25
- componentDidMount() {
26
- // NOTE(charlie): This is somewhat unfortunate, as the component is
27
- // encoding information about its own animation, of which it should be
28
- // ignorant. However, there doesn't seem to be a cleaner way to make
29
- // this happen, and at least here, all the animation context is
30
- // colocated in this file.
31
- const {animationDurationMs, onAnimationFinish} = this.props;
32
- setTimeout(() => onAnimationFinish(), animationDurationMs);
33
- }
34
-
35
- render() {
36
- const {id, initialBounds} = this.props;
37
- const {icon} = KeyConfigs[id];
38
-
39
- const containerStyle: any = {
40
- zIndex: zIndexes.echo,
41
- position: "absolute",
42
- pointerEvents: "none",
43
- ...initialBounds,
44
- };
45
-
46
- // NOTE(charlie): In some browsers, Aphrodite doesn't seem to flush its
47
- // styles quickly enough, so there's a flickering effect on the first
48
- // animation. Thus, it's much safer to do the styles purely inline.
49
- // <View> makes this difficult because some of its defaults, which are
50
- // applied via StyleSheet, will override our inlines.
51
- return (
52
- <div style={containerStyle}>
53
- <KeypadButton icon={icon} type={"ECHO"} />
54
- </div>
55
- );
56
- }
57
- }
58
-
59
- type EchoManagerProps = {
60
- echoes: ReadonlyArray<EchoType>;
61
- onAnimationFinish?: (animationId: string) => void;
62
- };
63
-
64
- class EchoManager extends React.Component<EchoManagerProps> {
65
- _animationConfigForType = (animationType) => {
66
- // NOTE(charlie): These must be kept in sync with the transition
67
- // durations and classnames specified in echo.css.
68
- let animationDurationMs;
69
- let animationTransitionName;
70
-
71
- switch (animationType) {
72
- case EchoAnimationType.SLIDE_AND_FADE:
73
- animationDurationMs = 400;
74
- animationTransitionName = "echo-slide-and-fade";
75
- break;
76
-
77
- case EchoAnimationType.FADE_ONLY:
78
- animationDurationMs = 300;
79
- animationTransitionName = "echo-fade-only";
80
- break;
81
-
82
- case EchoAnimationType.LONG_FADE_ONLY:
83
- animationDurationMs = 400;
84
- animationTransitionName = "echo-long-fade-only";
85
- break;
86
-
87
- default:
88
- throw new Error(
89
- `Invalid echo animation type: ${animationType}`,
90
- );
91
- }
92
-
93
- return {
94
- animationDurationMs,
95
- animationTransitionName,
96
- };
97
- };
98
-
99
- render() {
100
- const {echoes, onAnimationFinish} = this.props;
101
-
102
- return (
103
- <span>
104
- {Object.keys(EchoAnimationType).map((animationType) => {
105
- // Collect the relevant parameters for the animation type, and
106
- // filter for the appropriate echoes.
107
- const {animationDurationMs, animationTransitionName} =
108
- this._animationConfigForType(animationType);
109
- const echoesForType = echoes.filter((echo) => {
110
- return echo.animationType === animationType;
111
- });
112
-
113
- // TODO(charlie): Manage this animation with Aphrodite styles.
114
- // Right now, there's a bug in the autoprefixer that breaks CSS
115
- // transitions on mobile Safari.
116
- // See: https://github.com/Khan/aphrodite/issues/68.
117
- // As such, we have to do this with a stylesheet.
118
- return (
119
- <TransitionGroup key={animationType}>
120
- {echoesForType.map((echo) => {
121
- const {animationId} = echo;
122
- return (
123
- <CSSTransition
124
- classNames={animationTransitionName}
125
- enter={true}
126
- exit={false}
127
- timeout={{
128
- enter: animationDurationMs,
129
- }}
130
- key={animationId}
131
- >
132
- <Echo
133
- animationDurationMs={
134
- animationDurationMs
135
- }
136
- onAnimationFinish={() =>
137
- onAnimationFinish?.(animationId)
138
- }
139
- {...echo}
140
- />
141
- </CSSTransition>
142
- );
143
- })}
144
- </TransitionGroup>
145
- );
146
- })}
147
- </span>
148
- );
149
- }
150
- }
151
-
152
- export default EchoManager;
@@ -1,58 +0,0 @@
1
- /**
2
- * A keypad button containing no symbols and triggering no actions on click.
3
- */
4
-
5
- import * as React from "react";
6
- import {connect} from "react-redux";
7
-
8
- import KeyConfigs from "../../data/key-configs";
9
-
10
- import KeypadButton from "./keypad-button";
11
-
12
- import type GestureManager from "./gesture-manager";
13
- import type {State} from "./store/types";
14
-
15
- interface ReduxProps {
16
- gestureManager: GestureManager;
17
- }
18
-
19
- class EmptyKeypadButton extends React.Component<ReduxProps> {
20
- render() {
21
- const {gestureManager, ...rest} = this.props;
22
-
23
- // Register touch events on the button, but don't register its DOM node
24
- // or compute focus state or anything like that. We want the gesture
25
- // manager to know about touch events that start on empty buttons, but
26
- // we don't need it to know about their DOM nodes, as it doesn't need
27
- // to focus them or trigger presses.
28
- return (
29
- <KeypadButton
30
- onTouchStart={(evt: React.TouchEvent<HTMLDivElement>) =>
31
- gestureManager.onTouchStart(evt)
32
- }
33
- onTouchEnd={(evt: React.TouchEvent<HTMLDivElement>) =>
34
- gestureManager.onTouchEnd(evt)
35
- }
36
- onTouchMove={(evt: React.TouchEvent<HTMLDivElement>) =>
37
- gestureManager.onTouchMove(evt)
38
- }
39
- onTouchCancel={(evt: React.TouchEvent<HTMLDivElement>) =>
40
- gestureManager.onTouchCancel(evt)
41
- }
42
- {...KeyConfigs.NOOP}
43
- {...rest}
44
- />
45
- );
46
- }
47
- }
48
-
49
- const mapStateToProps = (state: State): ReduxProps => {
50
- const {gestures} = state;
51
- return {
52
- gestureManager: gestures.gestureManager,
53
- };
54
- };
55
-
56
- export default connect(mapStateToProps, null, null, {forwardRef: true})(
57
- EmptyKeypadButton,
58
- );