@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.
- package/CHANGELOG.md +11 -0
- package/dist/components/keypad/index.d.ts +1 -1
- package/dist/components/keypad/mobile-keypad-internals.d.ts +49 -0
- package/dist/components/keypad/mobile-keypad.d.ts +4 -48
- package/dist/es/index.js +168 -4740
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +166 -4722
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/integration.test.tsx +2 -3
- package/src/components/keypad/__tests__/mobile-keypad.test.tsx +8 -8
- package/src/components/keypad/index.tsx +1 -1
- package/src/components/keypad/mobile-keypad-internals.tsx +240 -0
- package/src/components/keypad/mobile-keypad.tsx +21 -234
- package/src/full-mobile-input.stories.tsx +0 -1
- package/src/index.ts +1 -1
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/components/keypad-legacy/compute-layout-parameters.d.ts +0 -28
- package/dist/components/keypad-legacy/corner-decal.d.ts +0 -12
- package/dist/components/keypad-legacy/echo-manager.d.ts +0 -17
- package/dist/components/keypad-legacy/empty-keypad-button.d.ts +0 -13
- package/dist/components/keypad-legacy/expression-keypad.d.ts +0 -21
- package/dist/components/keypad-legacy/fraction-keypad.d.ts +0 -21
- package/dist/components/keypad-legacy/gesture-manager.d.ts +0 -86
- package/dist/components/keypad-legacy/gesture-state-machine.d.ts +0 -105
- package/dist/components/keypad-legacy/icon.d.ts +0 -15
- package/dist/components/keypad-legacy/index.d.ts +0 -1
- package/dist/components/keypad-legacy/keypad-button.d.ts +0 -53
- package/dist/components/keypad-legacy/keypad-container.d.ts +0 -41
- package/dist/components/keypad-legacy/keypad.d.ts +0 -31
- package/dist/components/keypad-legacy/many-keypad-button.d.ts +0 -15
- package/dist/components/keypad-legacy/math-icon.d.ts +0 -16
- package/dist/components/keypad-legacy/multi-symbol-grid.d.ts +0 -14
- package/dist/components/keypad-legacy/multi-symbol-popover.d.ts +0 -12
- package/dist/components/keypad-legacy/navigation-pad.d.ts +0 -14
- package/dist/components/keypad-legacy/node-manager.d.ts +0 -49
- package/dist/components/keypad-legacy/popover-manager.d.ts +0 -13
- package/dist/components/keypad-legacy/popover-state-machine.d.ts +0 -68
- package/dist/components/keypad-legacy/provided-keypad.d.ts +0 -28
- package/dist/components/keypad-legacy/store/actions.d.ts +0 -55
- package/dist/components/keypad-legacy/store/echo-reducer.d.ts +0 -4
- package/dist/components/keypad-legacy/store/index.d.ts +0 -9
- package/dist/components/keypad-legacy/store/input-reducer.d.ts +0 -4
- package/dist/components/keypad-legacy/store/keypad-reducer.d.ts +0 -4
- package/dist/components/keypad-legacy/store/layout-reducer.d.ts +0 -4
- package/dist/components/keypad-legacy/store/shared.d.ts +0 -7
- package/dist/components/keypad-legacy/store/types.d.ts +0 -47
- package/dist/components/keypad-legacy/styles.d.ts +0 -5
- package/dist/components/keypad-legacy/svg-icon.d.ts +0 -12
- package/dist/components/keypad-legacy/text-icon.d.ts +0 -13
- package/dist/components/keypad-legacy/touchable-keypad-button.d.ts +0 -37
- package/dist/components/keypad-legacy/two-page-keypad.d.ts +0 -21
- package/dist/components/keypad-legacy/z-indexes.d.ts +0 -7
- package/dist/components/keypad-switch.d.ts +0 -12
- package/src/components/keypad-legacy/__tests__/gesture-state-machine.test.ts +0 -441
- package/src/components/keypad-legacy/__tests__/node-manager.test.ts +0 -89
- package/src/components/keypad-legacy/compute-layout-parameters.ts +0 -205
- package/src/components/keypad-legacy/corner-decal.tsx +0 -56
- package/src/components/keypad-legacy/echo-manager.tsx +0 -152
- package/src/components/keypad-legacy/empty-keypad-button.tsx +0 -58
- package/src/components/keypad-legacy/expression-keypad.tsx +0 -315
- package/src/components/keypad-legacy/fraction-keypad.tsx +0 -180
- package/src/components/keypad-legacy/gesture-manager.ts +0 -255
- package/src/components/keypad-legacy/gesture-state-machine.ts +0 -329
- package/src/components/keypad-legacy/icon.tsx +0 -72
- package/src/components/keypad-legacy/iconography/arrow.js +0 -22
- package/src/components/keypad-legacy/iconography/backspace.js +0 -29
- package/src/components/keypad-legacy/iconography/cdot.js +0 -29
- package/src/components/keypad-legacy/iconography/cos.js +0 -30
- package/src/components/keypad-legacy/iconography/cube-root.js +0 -36
- package/src/components/keypad-legacy/iconography/dismiss.js +0 -25
- package/src/components/keypad-legacy/iconography/divide.js +0 -34
- package/src/components/keypad-legacy/iconography/down.js +0 -16
- package/src/components/keypad-legacy/iconography/equal.js +0 -33
- package/src/components/keypad-legacy/iconography/exp-2.js +0 -29
- package/src/components/keypad-legacy/iconography/exp-3.js +0 -29
- package/src/components/keypad-legacy/iconography/exp.js +0 -29
- package/src/components/keypad-legacy/iconography/frac.js +0 -44
- package/src/components/keypad-legacy/iconography/geq.js +0 -33
- package/src/components/keypad-legacy/iconography/gt.js +0 -33
- package/src/components/keypad-legacy/iconography/index.js +0 -45
- package/src/components/keypad-legacy/iconography/jump-into-numerator.js +0 -41
- package/src/components/keypad-legacy/iconography/jump-out-base.js +0 -30
- package/src/components/keypad-legacy/iconography/jump-out-denominator.js +0 -41
- package/src/components/keypad-legacy/iconography/jump-out-exponent.js +0 -30
- package/src/components/keypad-legacy/iconography/jump-out-numerator.js +0 -41
- package/src/components/keypad-legacy/iconography/jump-out-parentheses.js +0 -33
- package/src/components/keypad-legacy/iconography/left-paren.js +0 -33
- package/src/components/keypad-legacy/iconography/left.js +0 -16
- package/src/components/keypad-legacy/iconography/leq.js +0 -33
- package/src/components/keypad-legacy/iconography/ln.js +0 -29
- package/src/components/keypad-legacy/iconography/log-n.js +0 -29
- package/src/components/keypad-legacy/iconography/log.js +0 -29
- package/src/components/keypad-legacy/iconography/lt.js +0 -33
- package/src/components/keypad-legacy/iconography/minus.js +0 -32
- package/src/components/keypad-legacy/iconography/neq.js +0 -33
- package/src/components/keypad-legacy/iconography/parens.js +0 -33
- package/src/components/keypad-legacy/iconography/percent.js +0 -49
- package/src/components/keypad-legacy/iconography/period.js +0 -26
- package/src/components/keypad-legacy/iconography/plus.js +0 -32
- package/src/components/keypad-legacy/iconography/radical.js +0 -36
- package/src/components/keypad-legacy/iconography/right-paren.js +0 -33
- package/src/components/keypad-legacy/iconography/right.js +0 -16
- package/src/components/keypad-legacy/iconography/sin.js +0 -30
- package/src/components/keypad-legacy/iconography/sqrt.js +0 -32
- package/src/components/keypad-legacy/iconography/tan.js +0 -30
- package/src/components/keypad-legacy/iconography/times.js +0 -33
- package/src/components/keypad-legacy/iconography/up.js +0 -16
- package/src/components/keypad-legacy/index.ts +0 -1
- package/src/components/keypad-legacy/keypad-button.tsx +0 -368
- package/src/components/keypad-legacy/keypad-container.tsx +0 -358
- package/src/components/keypad-legacy/keypad.tsx +0 -162
- package/src/components/keypad-legacy/many-keypad-button.tsx +0 -54
- package/src/components/keypad-legacy/math-icon.tsx +0 -66
- package/src/components/keypad-legacy/multi-symbol-grid.tsx +0 -182
- package/src/components/keypad-legacy/multi-symbol-popover.tsx +0 -58
- package/src/components/keypad-legacy/navigation-pad.tsx +0 -140
- package/src/components/keypad-legacy/node-manager.ts +0 -133
- package/src/components/keypad-legacy/popover-manager.tsx +0 -73
- package/src/components/keypad-legacy/popover-state-machine.ts +0 -184
- package/src/components/keypad-legacy/provided-keypad.tsx +0 -136
- package/src/components/keypad-legacy/store/actions.ts +0 -155
- package/src/components/keypad-legacy/store/echo-reducer.ts +0 -57
- package/src/components/keypad-legacy/store/index.ts +0 -110
- package/src/components/keypad-legacy/store/input-reducer.ts +0 -55
- package/src/components/keypad-legacy/store/keypad-reducer.ts +0 -58
- package/src/components/keypad-legacy/store/layout-reducer.test.ts +0 -171
- package/src/components/keypad-legacy/store/layout-reducer.ts +0 -129
- package/src/components/keypad-legacy/store/shared.ts +0 -12
- package/src/components/keypad-legacy/store/types.ts +0 -78
- package/src/components/keypad-legacy/styles.ts +0 -38
- package/src/components/keypad-legacy/svg-icon.tsx +0 -24
- package/src/components/keypad-legacy/text-icon.tsx +0 -53
- package/src/components/keypad-legacy/touchable-keypad-button.tsx +0 -163
- package/src/components/keypad-legacy/two-page-keypad.tsx +0 -115
- package/src/components/keypad-legacy/z-indexes.ts +0 -8
- 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
|
-
);
|