@khanacademy/math-input 0.7.2 → 1.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 +10 -0
- package/dist/actions/index.d.ts +31 -0
- package/dist/actions/index.js.flow +40 -0
- package/dist/components/compute-layout-parameters.d.ts +38 -0
- package/dist/components/compute-layout-parameters.js.flow +49 -0
- package/dist/components/corner-decal.d.ts +12 -0
- package/dist/components/corner-decal.js.flow +15 -0
- package/dist/components/echo-manager.d.ts +26 -0
- package/dist/components/echo-manager.js.flow +29 -0
- package/dist/components/empty-keypad-button.d.ts +13 -0
- package/dist/components/empty-keypad-button.js.flow +23 -0
- package/dist/components/expression-keypad.d.ts +22 -0
- package/dist/components/expression-keypad.js.flow +32 -0
- package/dist/components/fraction-keypad.d.ts +21 -0
- package/dist/components/fraction-keypad.js.flow +30 -0
- package/dist/components/gesture-manager.d.ts +74 -0
- package/dist/components/gesture-manager.js.flow +82 -0
- package/dist/components/gesture-state-machine.d.ts +105 -0
- package/dist/components/gesture-state-machine.js.flow +118 -0
- package/dist/components/icon.d.ts +15 -0
- package/dist/components/icon.js.flow +18 -0
- package/dist/components/input/__tests__/test-math-wrapper.d.ts +8 -0
- package/dist/components/input/__tests__/test-math-wrapper.js.flow +14 -0
- package/dist/components/input/cursor-handle.d.ts +1 -1
- package/dist/components/input/cursor-handle.js.flow +1 -1
- package/dist/components/input/drag-listener.d.ts +13 -0
- package/dist/components/input/drag-listener.js.flow +19 -0
- package/dist/components/input/math-input.d.ts +5 -4
- package/dist/components/input/math-input.js.flow +5 -4
- package/dist/components/input/math-wrapper.d.ts +110 -0
- package/dist/components/input/math-wrapper.js.flow +125 -0
- package/dist/components/input/scroll-into-view.d.ts +11 -0
- package/dist/components/input/scroll-into-view.js.flow +20 -0
- package/dist/components/keypad/button-assets.d.ts +4 -3
- package/dist/components/keypad/button-assets.js.flow +3 -3
- package/dist/components/keypad/button.d.ts +1 -2
- package/dist/components/keypad/button.js.flow +1 -2
- package/dist/components/keypad/keypad-page-items.d.ts +15 -10
- package/dist/components/keypad/keypad-page-items.js.flow +20 -10
- package/dist/components/keypad-button.d.ts +52 -0
- package/dist/components/keypad-button.js.flow +79 -0
- package/dist/components/keypad-container.d.ts +40 -0
- package/dist/components/keypad-container.js.flow +58 -0
- package/dist/components/keypad.d.ts +31 -0
- package/dist/components/keypad.js.flow +40 -0
- package/dist/components/many-keypad-button.d.ts +15 -0
- package/dist/components/many-keypad-button.js.flow +17 -0
- package/dist/components/math-icon.d.ts +16 -0
- package/dist/components/math-icon.js.flow +19 -0
- package/dist/components/multi-symbol-grid.d.ts +14 -0
- package/dist/components/multi-symbol-grid.js.flow +16 -0
- package/dist/components/multi-symbol-popover.d.ts +12 -0
- package/dist/components/multi-symbol-popover.js.flow +15 -0
- package/dist/components/navigation-pad.d.ts +14 -0
- package/dist/components/navigation-pad.js.flow +16 -0
- package/dist/components/node-manager.d.ts +53 -0
- package/dist/components/node-manager.js.flow +65 -0
- package/dist/components/popover-manager.d.ts +13 -0
- package/dist/components/popover-manager.js.flow +15 -0
- package/dist/components/popover-state-machine.d.ts +75 -0
- package/dist/components/popover-state-machine.js.flow +83 -0
- package/dist/components/provided-keypad.d.ts +8 -7
- package/dist/components/provided-keypad.js.flow +8 -7
- package/dist/components/styles.d.ts +6 -0
- package/dist/components/styles.js.flow +13 -0
- package/dist/components/svg-icon.d.ts +12 -0
- package/dist/components/svg-icon.js.flow +15 -0
- package/dist/components/tabbar/icons.d.ts +3 -2
- package/dist/components/tabbar/icons.js.flow +3 -2
- package/dist/components/tabbar/item.d.ts +1 -2
- package/dist/components/tabbar/item.js.flow +1 -2
- package/dist/components/tabbar/tabbar.d.ts +3 -3
- package/dist/components/tabbar/tabbar.js.flow +3 -3
- package/dist/components/text-icon.d.ts +13 -0
- package/dist/components/text-icon.js.flow +16 -0
- package/dist/components/touchable-keypad-button.d.ts +30 -0
- package/dist/components/touchable-keypad-button.js.flow +35 -0
- package/dist/components/two-page-keypad.d.ts +20 -0
- package/dist/components/two-page-keypad.js.flow +30 -0
- package/dist/components/velocity-tracker.d.ts +48 -0
- package/dist/components/velocity-tracker.js.flow +54 -0
- package/dist/es/index.js +938 -1059
- package/dist/es/index.js.map +1 -1
- package/dist/fake-react-native-web/text.d.ts +2 -1
- package/dist/fake-react-native-web/text.js.flow +2 -1
- package/dist/fake-react-native-web/view.d.ts +3 -2
- package/dist/fake-react-native-web/view.js.flow +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +988 -1089
- package/dist/index.js.flow +1 -4
- package/dist/index.js.map +1 -1
- package/dist/store/echo-reducer.d.ts +5 -0
- package/dist/store/echo-reducer.js.flow +14 -0
- package/dist/store/index.d.ts +46 -1
- package/dist/store/index.js.flow +64 -1
- package/dist/store/input-reducer.d.ts +7 -0
- package/dist/store/input-reducer.js.flow +16 -0
- package/dist/store/keypad-reducer.d.ts +9 -0
- package/dist/store/keypad-reducer.js.flow +18 -0
- package/dist/store/layout-reducer.d.ts +21 -0
- package/dist/store/layout-reducer.js.flow +30 -0
- package/dist/store/pager-reducer.d.ts +13 -0
- package/dist/store/pager-reducer.js.flow +22 -0
- package/dist/store/shared.d.ts +6 -0
- package/dist/store/shared.js.flow +13 -0
- package/dist/store/types.d.ts +57 -0
- package/dist/store/types.js.flow +63 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.js.flow +61 -0
- package/package.json +1 -1
- package/src/actions/{index.js → index.ts} +5 -5
- package/src/components/__tests__/{gesture-state-machine_test.js → gesture-state-machine.test.ts} +5 -1
- package/src/components/__tests__/{two-page-keypad_test.js → two-page-keypad.test.tsx} +0 -2
- package/src/components/{corner-decal.js → corner-decal.tsx} +6 -5
- package/src/components/{echo-manager.js → echo-manager.tsx} +29 -24
- package/src/components/{empty-keypad-button.js → empty-keypad-button.tsx} +17 -10
- package/src/components/{expression-keypad.js → expression-keypad.tsx} +27 -25
- package/src/components/{fraction-keypad.js → fraction-keypad.tsx} +21 -16
- package/src/components/{gesture-manager.js → gesture-manager.tsx} +10 -4
- package/src/components/{gesture-state-machine.js → gesture-state-machine.ts} +49 -3
- package/src/components/{icon.js → icon.tsx} +12 -14
- package/src/components/input/cursor-handle.tsx +1 -1
- package/src/components/input/{drag-listener.js → drag-listener.ts} +4 -0
- package/src/components/input/math-input.tsx +10 -9
- package/src/components/input/{math-wrapper.js → math-wrapper.ts} +10 -6
- package/src/components/input/{scroll-into-view.js → scroll-into-view.ts} +5 -15
- package/src/components/keypad/button-assets.tsx +4 -5
- package/src/components/keypad/button.tsx +1 -2
- package/src/components/keypad/index.tsx +1 -1
- package/src/components/keypad/keypad-page-items.tsx +33 -10
- package/src/components/{keypad-button.js → keypad-button.tsx} +42 -37
- package/src/components/{keypad-container.js → keypad-container.tsx} +41 -23
- package/src/components/{keypad.js → keypad.tsx} +31 -23
- package/src/components/{many-keypad-button.js → many-keypad-button.tsx} +8 -6
- package/src/components/{math-icon.js → math-icon.tsx} +7 -6
- package/src/components/{multi-symbol-grid.js → multi-symbol-grid.tsx} +8 -8
- package/src/components/{multi-symbol-popover.js → multi-symbol-popover.tsx} +5 -6
- package/src/components/{navigation-pad.js → navigation-pad.tsx} +7 -6
- package/src/components/{node-manager.js → node-manager.ts} +16 -4
- package/src/components/{popover-manager.js → popover-manager.tsx} +13 -16
- package/src/components/{popover-state-machine.js → popover-state-machine.ts} +21 -2
- package/src/components/prop-types.js +1 -67
- package/src/components/provided-keypad.tsx +14 -12
- package/src/components/{svg-icon.js → svg-icon.tsx} +5 -6
- package/src/components/tabbar/icons.tsx +4 -2
- package/src/components/tabbar/item.tsx +1 -3
- package/src/components/tabbar/{tabbar.stories.js → tabbar.stories.tsx} +10 -1
- package/src/components/tabbar/tabbar.tsx +3 -3
- package/src/components/{text-icon.js → text-icon.tsx} +7 -6
- package/src/components/{touchable-keypad-button.js → touchable-keypad-button.tsx} +19 -16
- package/src/components/{two-page-keypad.js → two-page-keypad.tsx} +13 -9
- package/src/components/{velocity-tracker.js → velocity-tracker.ts} +14 -4
- package/src/fake-react-native-web/text.tsx +2 -1
- package/src/fake-react-native-web/view.tsx +3 -2
- package/src/index.ts +1 -4
- package/src/store/echo-reducer.ts +58 -0
- package/src/store/index.ts +14 -425
- package/src/store/input-reducer.ts +55 -0
- package/src/store/keypad-reducer.ts +62 -0
- package/src/store/layout-reducer.ts +133 -0
- package/src/store/pager-reducer.ts +141 -0
- package/src/store/shared.ts +12 -0
- package/src/store/types.ts +65 -0
- package/src/types.ts +69 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/src/components/app.js +0 -73
- package/src/demo.js +0 -9
- package/src/native-app.js +0 -85
- /package/src/components/__tests__/{node-manager_test.js → node-manager.test.ts} +0 -0
- /package/src/components/{compute-layout-parameters.js → compute-layout-parameters.ts} +0 -0
- /package/src/components/input/__tests__/{context-tracking_test.js → context-tracking.test.ts} +0 -0
- /package/src/components/input/__tests__/{mathquill_test.js → mathquill.test.ts} +0 -0
- /package/src/components/input/__tests__/{test-math-wrapper.jsx → test-math-wrapper.ts} +0 -0
- /package/src/components/keypad/{button.stories.js → button.stories.tsx} +0 -0
- /package/src/components/{styles.js → styles.ts} +0 -0
- /package/src/components/tabbar/__tests__/{tabbar_test.js → tabbar.test.tsx} +0 -0
package/dist/es/index.js
CHANGED
|
@@ -330,6 +330,9 @@ CursorHandle.defaultProps = {
|
|
|
330
330
|
const touchSlopPx = 8;
|
|
331
331
|
class DragListener {
|
|
332
332
|
constructor(onDrag, initialEvent) {
|
|
333
|
+
this._scrollListener = void 0;
|
|
334
|
+
this._moveListener = void 0;
|
|
335
|
+
this._endAndCancelListener = void 0;
|
|
333
336
|
// We detect drags in two ways. First, by listening for the window
|
|
334
337
|
// scroll event (we consider any legitimate scroll to be a drag).
|
|
335
338
|
this._scrollListener = () => {
|
|
@@ -488,7 +491,7 @@ const IN_DENOMINATOR = "IN_DENOMINATOR";
|
|
|
488
491
|
// write is non-leaf math (numbers and variables).
|
|
489
492
|
const BEFORE_FRACTION = "BEFORE_FRACTION";
|
|
490
493
|
|
|
491
|
-
var
|
|
494
|
+
var cursorContexts = /*#__PURE__*/Object.freeze({
|
|
492
495
|
__proto__: null,
|
|
493
496
|
NONE: NONE,
|
|
494
497
|
IN_PARENS: IN_PARENS,
|
|
@@ -639,7 +642,13 @@ const KeysForJumpContext = {
|
|
|
639
642
|
[IN_DENOMINATOR]: Keys.JUMP_OUT_DENOMINATOR
|
|
640
643
|
};
|
|
641
644
|
class MathWrapper {
|
|
645
|
+
// MathQuill interface
|
|
646
|
+
// MathQuill input
|
|
647
|
+
|
|
642
648
|
constructor(element, options = {}, callbacks = {}) {
|
|
649
|
+
this.MQ = void 0;
|
|
650
|
+
this.mathField = void 0;
|
|
651
|
+
this.callbacks = void 0;
|
|
643
652
|
this.MQ = MathQuill.getInterface(2);
|
|
644
653
|
this.mathField = this.MQ.MathField(element, {
|
|
645
654
|
// use a span instead of a textarea so that we don't bring up the
|
|
@@ -718,7 +727,7 @@ class MathWrapper {
|
|
|
718
727
|
this._handleBackspace(cursor);
|
|
719
728
|
} else if (key === Keys.LEFT) {
|
|
720
729
|
this._handleLeftArrow(cursor);
|
|
721
|
-
} else if (key === Keys.RIGHT
|
|
730
|
+
} else if (key === Keys.RIGHT) {
|
|
722
731
|
this._handleRightArrow(cursor);
|
|
723
732
|
} else if (/^[a-zA-Z]$/.test(key)) {
|
|
724
733
|
this.mathField[WRITE](key);
|
|
@@ -1217,7 +1226,7 @@ class MathWrapper {
|
|
|
1217
1226
|
if (this._isInsideEmptyNode(cursor)) {
|
|
1218
1227
|
const grandparent = cursor.parent.parent;
|
|
1219
1228
|
const command = this._maybeFindCommandBeforeParens(grandparent);
|
|
1220
|
-
cursor.insLeftOf(command.startNode);
|
|
1229
|
+
cursor.insLeftOf(command == null ? void 0 : command.startNode);
|
|
1221
1230
|
cursor.startSelection();
|
|
1222
1231
|
if (grandparent[this.MQ.R] !== MQ_END) {
|
|
1223
1232
|
cursor.insRightOf(grandparent[this.MQ.R]);
|
|
@@ -1381,18 +1390,6 @@ class MathWrapper {
|
|
|
1381
1390
|
* TODO(charlie): Move this scroll logic out of our components and into a higher
|
|
1382
1391
|
* level in the component tree--perhaps even into webapp, beyond Perseus.
|
|
1383
1392
|
*/
|
|
1384
|
-
|
|
1385
|
-
// Taken from https://dev.opera.com/articles/fixing-the-scrolltop-bug/
|
|
1386
|
-
function bodyOrHtml() {
|
|
1387
|
-
if ("scrollingElement" in document) {
|
|
1388
|
-
return document.scrollingElement;
|
|
1389
|
-
}
|
|
1390
|
-
// Fallback for legacy browsers
|
|
1391
|
-
if (navigator.userAgent.indexOf("WebKit") !== -1) {
|
|
1392
|
-
return document.body;
|
|
1393
|
-
}
|
|
1394
|
-
return document.documentElement;
|
|
1395
|
-
}
|
|
1396
1393
|
const scrollIntoView = (containerNode, keypadNode) => {
|
|
1397
1394
|
// TODO(charlie): There's no need for us to be reading the keypad bounds
|
|
1398
1395
|
// here, since they're pre-determined by logic in the store. We should
|
|
@@ -1402,7 +1399,7 @@ const scrollIntoView = (containerNode, keypadNode) => {
|
|
|
1402
1399
|
const containerTopPx = containerBounds.top;
|
|
1403
1400
|
|
|
1404
1401
|
// Get the element that scrolls the document.
|
|
1405
|
-
const scrollNode =
|
|
1402
|
+
const scrollNode = document.scrollingElement;
|
|
1406
1403
|
const desiredMarginPx = 16;
|
|
1407
1404
|
if (keypadNode) {
|
|
1408
1405
|
// NOTE(charlie): We can't use the bounding rect of the keypad,
|
|
@@ -1421,7 +1418,9 @@ const scrollIntoView = (containerNode, keypadNode) => {
|
|
|
1421
1418
|
// the bottom of the input is just above the top of the keypad,
|
|
1422
1419
|
// taking care not to scroll the input out of view.
|
|
1423
1420
|
const scrollOffset = Math.min(containerBottomPx - keypadTopPx + desiredMarginPx, containerTopPx);
|
|
1424
|
-
scrollNode
|
|
1421
|
+
if (scrollNode) {
|
|
1422
|
+
scrollNode.scrollTop += scrollOffset;
|
|
1423
|
+
}
|
|
1425
1424
|
return;
|
|
1426
1425
|
}
|
|
1427
1426
|
}
|
|
@@ -1429,7 +1428,7 @@ const scrollIntoView = (containerNode, keypadNode) => {
|
|
|
1429
1428
|
// Alternatively, if the input is out of the viewport or nearly out
|
|
1430
1429
|
// of the viewport, scroll it into view. We can do this regardless
|
|
1431
1430
|
// of whether the keypad has been provided.
|
|
1432
|
-
if (containerTopPx < desiredMarginPx) {
|
|
1431
|
+
if (scrollNode && containerTopPx < desiredMarginPx) {
|
|
1433
1432
|
scrollNode.scrollTop -= containerBounds.height + desiredMarginPx;
|
|
1434
1433
|
}
|
|
1435
1434
|
};
|
|
@@ -2133,320 +2132,12 @@ const inlineStyles$1 = {
|
|
|
2133
2132
|
}
|
|
2134
2133
|
};
|
|
2135
2134
|
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
type: KeyTypes.OPERATOR,
|
|
2140
|
-
// I18N: A label for a plus sign.
|
|
2141
|
-
ariaLabel: i18n._("Plus")
|
|
2142
|
-
},
|
|
2143
|
-
[Keys.MINUS]: {
|
|
2144
|
-
type: KeyTypes.OPERATOR,
|
|
2145
|
-
// I18N: A label for a minus sign.
|
|
2146
|
-
ariaLabel: i18n._("Minus")
|
|
2147
|
-
},
|
|
2148
|
-
[Keys.NEGATIVE]: {
|
|
2149
|
-
type: KeyTypes.VALUE,
|
|
2150
|
-
// I18N: A label for a minus sign.
|
|
2151
|
-
ariaLabel: i18n._("Negative")
|
|
2152
|
-
},
|
|
2153
|
-
[Keys.TIMES]: {
|
|
2154
|
-
type: KeyTypes.OPERATOR,
|
|
2155
|
-
// I18N: A label for a multiplication sign (represented with an 'x').
|
|
2156
|
-
ariaLabel: i18n._("Multiply")
|
|
2157
|
-
},
|
|
2158
|
-
[Keys.DIVIDE]: {
|
|
2159
|
-
type: KeyTypes.OPERATOR,
|
|
2160
|
-
// I18N: A label for a division sign.
|
|
2161
|
-
ariaLabel: i18n._("Divide")
|
|
2162
|
-
},
|
|
2163
|
-
[Keys.DECIMAL]: {
|
|
2164
|
-
type: KeyTypes.VALUE,
|
|
2165
|
-
// I18N: A label for a decimal symbol.
|
|
2166
|
-
ariaLabel: i18n._("Decimal"),
|
|
2167
|
-
icon: decimalSeparator === DecimalSeparators.COMMA ? {
|
|
2168
|
-
// TODO(charlie): Get an SVG icon for the comma, or verify with
|
|
2169
|
-
// design that the text-rendered version is acceptable.
|
|
2170
|
-
type: IconTypes.TEXT,
|
|
2171
|
-
data: ","
|
|
2172
|
-
} : {
|
|
2173
|
-
type: IconTypes.SVG,
|
|
2174
|
-
data: Keys.PERIOD
|
|
2175
|
-
}
|
|
2176
|
-
},
|
|
2177
|
-
[Keys.PERCENT]: {
|
|
2178
|
-
type: KeyTypes.OPERATOR,
|
|
2179
|
-
// I18N: A label for a percent sign.
|
|
2180
|
-
ariaLabel: i18n._("Percent")
|
|
2181
|
-
},
|
|
2182
|
-
[Keys.CDOT]: {
|
|
2183
|
-
type: KeyTypes.OPERATOR,
|
|
2184
|
-
// I18N: A label for a multiplication sign (represented as a dot).
|
|
2185
|
-
ariaLabel: i18n._("Multiply")
|
|
2186
|
-
},
|
|
2187
|
-
[Keys.EQUAL]: {
|
|
2188
|
-
type: KeyTypes.OPERATOR,
|
|
2189
|
-
ariaLabel: i18n._("Equals sign")
|
|
2190
|
-
},
|
|
2191
|
-
[Keys.NEQ]: {
|
|
2192
|
-
type: KeyTypes.OPERATOR,
|
|
2193
|
-
ariaLabel: i18n._("Not-equals sign")
|
|
2194
|
-
},
|
|
2195
|
-
[Keys.GT]: {
|
|
2196
|
-
type: KeyTypes.OPERATOR,
|
|
2197
|
-
// I18N: A label for a 'greater than' sign (represented as '>').
|
|
2198
|
-
ariaLabel: i18n._("Greater than sign")
|
|
2199
|
-
},
|
|
2200
|
-
[Keys.LT]: {
|
|
2201
|
-
type: KeyTypes.OPERATOR,
|
|
2202
|
-
// I18N: A label for a 'less than' sign (represented as '<').
|
|
2203
|
-
ariaLabel: i18n._("Less than sign")
|
|
2204
|
-
},
|
|
2205
|
-
[Keys.GEQ]: {
|
|
2206
|
-
type: KeyTypes.OPERATOR,
|
|
2207
|
-
ariaLabel: i18n._("Greater than or equal to sign")
|
|
2208
|
-
},
|
|
2209
|
-
[Keys.LEQ]: {
|
|
2210
|
-
type: KeyTypes.OPERATOR,
|
|
2211
|
-
ariaLabel: i18n._("Less than or equal to sign")
|
|
2212
|
-
},
|
|
2213
|
-
// mobile native
|
|
2214
|
-
[Keys.FRAC_INCLUSIVE]: {
|
|
2215
|
-
type: KeyTypes.OPERATOR,
|
|
2216
|
-
// I18N: A label for a button that creates a new fraction and puts the
|
|
2217
|
-
// current expression in the numerator of that fraction.
|
|
2218
|
-
ariaLabel: i18n._("Fraction, with current expression in numerator")
|
|
2219
|
-
},
|
|
2220
|
-
// mobile native
|
|
2221
|
-
[Keys.FRAC_EXCLUSIVE]: {
|
|
2222
|
-
type: KeyTypes.OPERATOR,
|
|
2223
|
-
// I18N: A label for a button that creates a new fraction next to the
|
|
2224
|
-
// cursor.
|
|
2225
|
-
ariaLabel: i18n._("Fraction, excluding the current expression")
|
|
2226
|
-
},
|
|
2227
|
-
// mobile web
|
|
2228
|
-
[Keys.FRAC]: {
|
|
2229
|
-
type: KeyTypes.OPERATOR,
|
|
2230
|
-
// I18N: A label for a button that creates a new fraction next to the
|
|
2231
|
-
// cursor.
|
|
2232
|
-
ariaLabel: i18n._("Fraction, excluding the current expression")
|
|
2233
|
-
},
|
|
2234
|
-
[Keys.EXP]: {
|
|
2235
|
-
type: KeyTypes.OPERATOR,
|
|
2236
|
-
// I18N: A label for a button that will allow the user to input a custom
|
|
2237
|
-
// exponent.
|
|
2238
|
-
ariaLabel: i18n._("Custom exponent")
|
|
2239
|
-
},
|
|
2240
|
-
[Keys.EXP_2]: {
|
|
2241
|
-
type: KeyTypes.OPERATOR,
|
|
2242
|
-
// I18N: A label for a button that will square (take to the second
|
|
2243
|
-
// power) some math.
|
|
2244
|
-
ariaLabel: i18n._("Square")
|
|
2245
|
-
},
|
|
2246
|
-
[Keys.EXP_3]: {
|
|
2247
|
-
type: KeyTypes.OPERATOR,
|
|
2248
|
-
// I18N: A label for a button that will cube (take to the third power)
|
|
2249
|
-
// some math.
|
|
2250
|
-
ariaLabel: i18n._("Cube")
|
|
2251
|
-
},
|
|
2252
|
-
[Keys.SQRT]: {
|
|
2253
|
-
type: KeyTypes.OPERATOR,
|
|
2254
|
-
ariaLabel: i18n._("Square root")
|
|
2255
|
-
},
|
|
2256
|
-
[Keys.CUBE_ROOT]: {
|
|
2257
|
-
type: KeyTypes.OPERATOR,
|
|
2258
|
-
ariaLabel: i18n._("Cube root")
|
|
2259
|
-
},
|
|
2260
|
-
[Keys.RADICAL]: {
|
|
2261
|
-
type: KeyTypes.OPERATOR,
|
|
2262
|
-
ariaLabel: i18n._("Radical with custom root")
|
|
2263
|
-
},
|
|
2264
|
-
[Keys.LEFT_PAREN]: {
|
|
2265
|
-
type: KeyTypes.OPERATOR,
|
|
2266
|
-
ariaLabel: i18n._("Left parenthesis")
|
|
2267
|
-
},
|
|
2268
|
-
[Keys.RIGHT_PAREN]: {
|
|
2269
|
-
type: KeyTypes.OPERATOR,
|
|
2270
|
-
ariaLabel: i18n._("Right parenthesis")
|
|
2271
|
-
},
|
|
2272
|
-
[Keys.LN]: {
|
|
2273
|
-
type: KeyTypes.OPERATOR,
|
|
2274
|
-
ariaLabel: i18n._("Natural logarithm")
|
|
2275
|
-
},
|
|
2276
|
-
[Keys.LOG]: {
|
|
2277
|
-
type: KeyTypes.OPERATOR,
|
|
2278
|
-
ariaLabel: i18n._("Logarithm with base 10")
|
|
2279
|
-
},
|
|
2280
|
-
[Keys.LOG_N]: {
|
|
2281
|
-
type: KeyTypes.OPERATOR,
|
|
2282
|
-
ariaLabel: i18n._("Logarithm with custom base")
|
|
2283
|
-
},
|
|
2284
|
-
[Keys.SIN]: {
|
|
2285
|
-
type: KeyTypes.OPERATOR,
|
|
2286
|
-
ariaLabel: i18n._("Sine")
|
|
2287
|
-
},
|
|
2288
|
-
[Keys.COS]: {
|
|
2289
|
-
type: KeyTypes.OPERATOR,
|
|
2290
|
-
ariaLabel: i18n._("Cosine")
|
|
2291
|
-
},
|
|
2292
|
-
[Keys.TAN]: {
|
|
2293
|
-
type: KeyTypes.OPERATOR,
|
|
2294
|
-
ariaLabel: i18n._("Tangent")
|
|
2295
|
-
},
|
|
2296
|
-
[Keys.PI]: {
|
|
2297
|
-
type: KeyTypes.VALUE,
|
|
2298
|
-
ariaLabel: i18n._("Pi"),
|
|
2299
|
-
icon: {
|
|
2300
|
-
type: IconTypes.MATH,
|
|
2301
|
-
data: "\\pi"
|
|
2302
|
-
}
|
|
2303
|
-
},
|
|
2304
|
-
[Keys.THETA]: {
|
|
2305
|
-
type: KeyTypes.VALUE,
|
|
2306
|
-
ariaLabel: i18n._("Theta"),
|
|
2307
|
-
icon: {
|
|
2308
|
-
type: IconTypes.MATH,
|
|
2309
|
-
data: "\\theta"
|
|
2310
|
-
}
|
|
2311
|
-
},
|
|
2312
|
-
[Keys.NOOP]: {
|
|
2313
|
-
type: KeyTypes.EMPTY
|
|
2314
|
-
},
|
|
2315
|
-
// Input navigation keys.
|
|
2316
|
-
[Keys.UP]: {
|
|
2317
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2318
|
-
ariaLabel: i18n._("Up arrow")
|
|
2319
|
-
},
|
|
2320
|
-
[Keys.RIGHT]: {
|
|
2321
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2322
|
-
ariaLabel: i18n._("Right arrow")
|
|
2323
|
-
},
|
|
2324
|
-
[Keys.DOWN]: {
|
|
2325
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2326
|
-
ariaLabel: i18n._("Down arrow")
|
|
2327
|
-
},
|
|
2328
|
-
[Keys.LEFT]: {
|
|
2329
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2330
|
-
ariaLabel: i18n._("Left arrow")
|
|
2331
|
-
},
|
|
2332
|
-
[Keys.JUMP_OUT_PARENTHESES]: {
|
|
2333
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2334
|
-
ariaLabel: i18n._("Navigate right out of a set of parentheses")
|
|
2335
|
-
},
|
|
2336
|
-
[Keys.JUMP_OUT_EXPONENT]: {
|
|
2337
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2338
|
-
ariaLabel: i18n._("Navigate right out of an exponent")
|
|
2339
|
-
},
|
|
2340
|
-
[Keys.JUMP_OUT_BASE]: {
|
|
2341
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2342
|
-
ariaLabel: i18n._("Navigate right out of a base")
|
|
2343
|
-
},
|
|
2344
|
-
[Keys.JUMP_INTO_NUMERATOR]: {
|
|
2345
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2346
|
-
ariaLabel: i18n._("Navigate right into the numerator of a fraction")
|
|
2347
|
-
},
|
|
2348
|
-
[Keys.JUMP_OUT_NUMERATOR]: {
|
|
2349
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2350
|
-
ariaLabel: i18n._("Navigate right out of the numerator and into the denominator")
|
|
2351
|
-
},
|
|
2352
|
-
[Keys.JUMP_OUT_DENOMINATOR]: {
|
|
2353
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2354
|
-
ariaLabel: i18n._("Navigate right out of the denominator of a fraction")
|
|
2355
|
-
},
|
|
2356
|
-
[Keys.BACKSPACE]: {
|
|
2357
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2358
|
-
// I18N: A label for a button that will delete some input.
|
|
2359
|
-
ariaLabel: i18n._("Delete")
|
|
2360
|
-
},
|
|
2361
|
-
// Keypad navigation keys.
|
|
2362
|
-
[Keys.DISMISS]: {
|
|
2363
|
-
type: KeyTypes.KEYPAD_NAVIGATION,
|
|
2364
|
-
// I18N: A label for a button that will dismiss/hide a keypad.
|
|
2365
|
-
ariaLabel: i18n._("Dismiss")
|
|
2366
|
-
}
|
|
2367
|
-
};
|
|
2368
|
-
|
|
2369
|
-
// Add in any multi-function buttons. By default, these keys will mix in any
|
|
2370
|
-
// configuration settings from their default child key (i.e., the first key in
|
|
2371
|
-
// the `childKeyIds` array).
|
|
2372
|
-
// TODO(charlie): Make the multi-function button's long-press interaction
|
|
2373
|
-
// accessible.
|
|
2374
|
-
// NOTE(kevinb): This is only used in the mobile native app.
|
|
2375
|
-
KeyConfigs[Keys.FRAC_MULTI] = {
|
|
2376
|
-
childKeyIds: [Keys.FRAC_INCLUSIVE, Keys.FRAC_EXCLUSIVE]
|
|
2377
|
-
};
|
|
2378
|
-
|
|
2379
|
-
// TODO(charlie): Use the numeral color for the 'Many' key.
|
|
2380
|
-
KeyConfigs[Keys.MANY] = {
|
|
2381
|
-
type: KeyTypes.MANY
|
|
2382
|
-
// childKeyIds will be configured by the client.
|
|
2383
|
-
};
|
|
2384
|
-
|
|
2385
|
-
// Add in every numeral.
|
|
2386
|
-
const NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
2387
|
-
for (const num of NUMBERS) {
|
|
2388
|
-
// TODO(charlie): Consider removing the SVG icons that we have for the
|
|
2389
|
-
// numeral keys. They can be rendered just as easily with text (though that
|
|
2390
|
-
// would mean that we'd be using text beyond the variable key).
|
|
2391
|
-
const textRepresentation = `${num}`;
|
|
2392
|
-
KeyConfigs[`NUM_${num}`] = {
|
|
2393
|
-
type: KeyTypes.VALUE,
|
|
2394
|
-
ariaLabel: textRepresentation,
|
|
2395
|
-
icon: {
|
|
2396
|
-
type: IconTypes.TEXT,
|
|
2397
|
-
data: textRepresentation
|
|
2398
|
-
}
|
|
2399
|
-
};
|
|
2400
|
-
}
|
|
2401
|
-
|
|
2402
|
-
// Add in every variable.
|
|
2403
|
-
const LETTERS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
|
|
2404
|
-
for (const letter of LETTERS) {
|
|
2405
|
-
const lowerCaseVariable = letter.toLowerCase();
|
|
2406
|
-
const upperCaseVariable = letter.toUpperCase();
|
|
2407
|
-
for (const textRepresentation of [lowerCaseVariable, upperCaseVariable]) {
|
|
2408
|
-
KeyConfigs[textRepresentation] = {
|
|
2409
|
-
type: KeyTypes.VALUE,
|
|
2410
|
-
ariaLabel: textRepresentation,
|
|
2411
|
-
icon: {
|
|
2412
|
-
type: IconTypes.MATH,
|
|
2413
|
-
data: textRepresentation
|
|
2414
|
-
}
|
|
2415
|
-
};
|
|
2416
|
-
}
|
|
2417
|
-
}
|
|
2418
|
-
for (const key of Object.keys(KeyConfigs)) {
|
|
2419
|
-
KeyConfigs[key] = _extends({
|
|
2420
|
-
id: key,
|
|
2421
|
-
// Default to an SVG icon indexed by the key name.
|
|
2422
|
-
icon: {
|
|
2423
|
-
type: IconTypes.SVG,
|
|
2424
|
-
data: key
|
|
2425
|
-
}
|
|
2426
|
-
}, KeyConfigs[key]);
|
|
2427
|
-
}
|
|
2428
|
-
|
|
2429
|
-
/**
|
|
2430
|
-
* React PropTypes that may be shared between components.
|
|
2431
|
-
*/
|
|
2432
|
-
const iconPropType = PropTypes.shape({
|
|
2433
|
-
type: PropTypes.oneOf(Object.keys(IconTypes)).isRequired,
|
|
2434
|
-
data: PropTypes.string.isRequired
|
|
2435
|
-
});
|
|
2436
|
-
const keyIdPropType = PropTypes.oneOf(Object.keys(KeyConfigs));
|
|
2437
|
-
const keyConfigPropType = PropTypes.shape({
|
|
2438
|
-
ariaLabel: PropTypes.string,
|
|
2439
|
-
id: keyIdPropType.isRequired,
|
|
2440
|
-
type: PropTypes.oneOf(Object.keys(KeyTypes)).isRequired,
|
|
2441
|
-
childKeyIds: PropTypes.arrayOf(keyIdPropType),
|
|
2442
|
-
icon: iconPropType.isRequired
|
|
2443
|
-
});
|
|
2444
|
-
const keypadConfigurationPropType = PropTypes.shape({
|
|
2445
|
-
keypadType: PropTypes.oneOf(Object.keys(KeypadTypes)).isRequired,
|
|
2446
|
-
extraKeys: PropTypes.arrayOf(keyIdPropType)
|
|
2447
|
-
});
|
|
2135
|
+
/**
|
|
2136
|
+
* React PropTypes that may be shared between components.
|
|
2137
|
+
*/
|
|
2448
2138
|
|
|
2449
2139
|
// NOTE(jared): This is no longer guaranteed to be React element
|
|
2140
|
+
// NOTE(matthewc): only seems to be used in Perseus
|
|
2450
2141
|
const keypadElementPropType = PropTypes.shape({
|
|
2451
2142
|
activate: PropTypes.func.isRequired,
|
|
2452
2143
|
dismiss: PropTypes.func.isRequired,
|
|
@@ -2455,44 +2146,6 @@ const keypadElementPropType = PropTypes.shape({
|
|
|
2455
2146
|
setKeyHandler: PropTypes.func.isRequired,
|
|
2456
2147
|
getDOMNode: PropTypes.func.isRequired
|
|
2457
2148
|
});
|
|
2458
|
-
const bordersPropType = PropTypes.arrayOf(PropTypes.oneOf(Object.keys(BorderDirections)));
|
|
2459
|
-
const boundingBoxPropType = PropTypes.shape({
|
|
2460
|
-
height: PropTypes.number,
|
|
2461
|
-
width: PropTypes.number,
|
|
2462
|
-
top: PropTypes.number,
|
|
2463
|
-
right: PropTypes.number,
|
|
2464
|
-
bottom: PropTypes.number,
|
|
2465
|
-
left: PropTypes.number
|
|
2466
|
-
});
|
|
2467
|
-
const echoPropType = PropTypes.shape({
|
|
2468
|
-
animationId: PropTypes.string.isRequired,
|
|
2469
|
-
animationType: PropTypes.oneOf(Object.keys(EchoAnimationTypes)).isRequired,
|
|
2470
|
-
borders: bordersPropType,
|
|
2471
|
-
id: keyIdPropType.isRequired,
|
|
2472
|
-
initialBounds: boundingBoxPropType.isRequired
|
|
2473
|
-
});
|
|
2474
|
-
const cursorContextPropType = PropTypes.oneOf(Object.keys(CursorContexts));
|
|
2475
|
-
const popoverPropType = PropTypes.shape({
|
|
2476
|
-
parentId: keyIdPropType.isRequired,
|
|
2477
|
-
bounds: boundingBoxPropType.isRequired,
|
|
2478
|
-
childKeyIds: PropTypes.arrayOf(keyIdPropType).isRequired
|
|
2479
|
-
});
|
|
2480
|
-
PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]);
|
|
2481
|
-
|
|
2482
|
-
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
2483
|
-
if (source == null) return {};
|
|
2484
|
-
var target = {};
|
|
2485
|
-
var sourceKeys = Object.keys(source);
|
|
2486
|
-
var key, i;
|
|
2487
|
-
|
|
2488
|
-
for (i = 0; i < sourceKeys.length; i++) {
|
|
2489
|
-
key = sourceKeys[i];
|
|
2490
|
-
if (excluded.indexOf(key) >= 0) continue;
|
|
2491
|
-
target[key] = source[key];
|
|
2492
|
-
}
|
|
2493
|
-
|
|
2494
|
-
return target;
|
|
2495
|
-
}
|
|
2496
2149
|
|
|
2497
2150
|
// naming convention: verb + noun
|
|
2498
2151
|
// the noun should be one of the other properties in the object that's
|
|
@@ -2511,8 +2164,6 @@ const activateKeypad = () => {
|
|
|
2511
2164
|
|
|
2512
2165
|
/**
|
|
2513
2166
|
* Configure the keypad with the provided configuration parameters.
|
|
2514
|
-
*
|
|
2515
|
-
* See: `prop-types.js#keypadConfigurationPropType`.
|
|
2516
2167
|
*/
|
|
2517
2168
|
const configureKeypad = configuration => {
|
|
2518
2169
|
return {
|
|
@@ -2548,133 +2199,20 @@ const setCursor = cursor => {
|
|
|
2548
2199
|
};
|
|
2549
2200
|
};
|
|
2550
2201
|
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
* other exercise-related chrome, the size of the input box, the parameters that
|
|
2557
|
-
* define the keypad (i.e., the number of rows, columns, and pages), and so
|
|
2558
|
-
* forth.
|
|
2559
|
-
*
|
|
2560
|
-
* The computations herein make some strong assumptions about the sizes of
|
|
2561
|
-
* various other elements and the situations under which they will be visible
|
|
2562
|
-
* (e.g., browser chrome). However, this is just a heuristic--it's not crucial
|
|
2563
|
-
* that our buttons are sized in a pixel-perfect manner, but rather, that we
|
|
2564
|
-
* make a balanced use of space.
|
|
2565
|
-
*
|
|
2566
|
-
* Note that one goal of the algorithm is to avoid resizing the keypad in the
|
|
2567
|
-
* face of dynamic browser chrome. In order to avoid that awkwardness, we tend
|
|
2568
|
-
* to be conservative in our measurements and make things smaller than they
|
|
2569
|
-
* might need to be.
|
|
2570
|
-
*/
|
|
2571
|
-
const minButtonHeight = 48;
|
|
2572
|
-
const maxButtonSize = 64;
|
|
2573
|
-
const minSpaceAboveKeypad = 32;
|
|
2574
|
-
|
|
2575
|
-
// These values are taken from an iPhone 5, but should be consistent with the
|
|
2576
|
-
// iPhone 4 as well. Regardless, these are meant to be representative of the
|
|
2577
|
-
// possible types of browser chrome that could appear in various context, rather
|
|
2578
|
-
// than pixel-perfect for every device.
|
|
2579
|
-
const safariNavBarWhenShrunk = 44;
|
|
2580
|
-
const safariNavBarWhenExpanded = 64;
|
|
2581
|
-
const safariToolbar = 44;
|
|
2582
|
-
|
|
2583
|
-
// In mobile Safari, the browser chrome is completely hidden in landscape,
|
|
2584
|
-
// though a shrunken navbar and full-sized toolbar on scroll. In portrait, the
|
|
2585
|
-
// shrunken navbar is always visible, but expands on scroll (and the toolbar
|
|
2586
|
-
// appears as well).
|
|
2587
|
-
const maxLandscapeBrowserChrome = safariNavBarWhenShrunk + safariToolbar;
|
|
2588
|
-
const maxPortraitBrowserChrome = safariToolbar + (safariNavBarWhenExpanded - safariNavBarWhenShrunk);
|
|
2589
|
-
|
|
2590
|
-
// This represents the 'worst case' aspect ratio that we care about (for
|
|
2591
|
-
// portrait layouts). It's taken from the iPhone 4. The height is computed by
|
|
2592
|
-
// taking the height of the device and removing the persistent, shrunken navbar.
|
|
2593
|
-
// (We don't need to account for the expanded navbar, since we include the
|
|
2594
|
-
// difference when reserving space above the keypad.)
|
|
2595
|
-
const worstCaseAspectRatio = 320 / (480 - safariNavBarWhenShrunk);
|
|
2596
|
-
const computeLayoutParameters = ({
|
|
2597
|
-
numColumns,
|
|
2598
|
-
numMaxVisibleRows,
|
|
2599
|
-
numPages
|
|
2600
|
-
}, {
|
|
2601
|
-
pageWidthPx,
|
|
2602
|
-
pageHeightPx
|
|
2603
|
-
}, {
|
|
2604
|
-
deviceOrientation,
|
|
2605
|
-
deviceType
|
|
2606
|
-
}, {
|
|
2607
|
-
navigationPadEnabled,
|
|
2608
|
-
paginationEnabled,
|
|
2609
|
-
toolbarEnabled
|
|
2610
|
-
}) => {
|
|
2611
|
-
// First, compute some values that will be used in multiple computations.
|
|
2612
|
-
const effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
|
|
2613
|
-
|
|
2614
|
-
// Then, compute the button dimensions based on the provided parameters.
|
|
2615
|
-
let buttonDimensions;
|
|
2616
|
-
if (deviceType === DeviceTypes.PHONE) {
|
|
2617
|
-
const isLandscape = deviceOrientation === DeviceOrientations.LANDSCAPE;
|
|
2618
|
-
|
|
2619
|
-
// In many cases, the browser chrome will already have been factored
|
|
2620
|
-
// into `pageHeightPx`. But we have no way of knowing if that's
|
|
2621
|
-
// the case or not. As such, we take a conservative approach and
|
|
2622
|
-
// assume that the chrome is _never_ included in `pageHeightPx`.
|
|
2623
|
-
const browserChromeHeight = isLandscape ? maxLandscapeBrowserChrome : maxPortraitBrowserChrome;
|
|
2624
|
-
|
|
2625
|
-
// Count up all the space that we need to reserve on the page.
|
|
2626
|
-
// Namely, we need to account for:
|
|
2627
|
-
// 1. Space between the keypad and the top of the page.
|
|
2628
|
-
// 2. The presence of the exercise toolbar.
|
|
2629
|
-
// 3. The presence of the view pager indicator.
|
|
2630
|
-
// 4. Any browser chrome that may appear later.
|
|
2631
|
-
const reservedSpace = minSpaceAboveKeypad + browserChromeHeight + (toolbarEnabled ? toolbarHeightPx : 0) + (paginationEnabled ? pageIndicatorHeightPx : 0);
|
|
2632
|
-
|
|
2633
|
-
// Next, compute the effective width and height. We can use the page
|
|
2634
|
-
// width as the effective width. For the height, though, we take
|
|
2635
|
-
// another conservative measure when in portrait by assuming that
|
|
2636
|
-
// the device has the worst possible aspect ratio. In other words,
|
|
2637
|
-
// we ignore the device height in portrait and assume the worst.
|
|
2638
|
-
// This prevents the keypad from changing size when browser chrome
|
|
2639
|
-
// appears and disappears.
|
|
2640
|
-
const effectiveWidth = pageWidthPx;
|
|
2641
|
-
const effectiveHeight = isLandscape ? pageHeightPx : pageWidthPx / worstCaseAspectRatio;
|
|
2642
|
-
const maxKeypadHeight = effectiveHeight - reservedSpace;
|
|
2202
|
+
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
2203
|
+
if (source == null) return {};
|
|
2204
|
+
var target = {};
|
|
2205
|
+
var sourceKeys = Object.keys(source);
|
|
2206
|
+
var key, i;
|
|
2643
2207
|
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
const buttonHeightPx = Math.max(Math.min(maxKeypadHeight / numMaxVisibleRows, maxButtonSize), minButtonHeight);
|
|
2649
|
-
let buttonWidthPx;
|
|
2650
|
-
if (numPages > 1) {
|
|
2651
|
-
const _effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
|
|
2652
|
-
buttonWidthPx = effectiveWidth / _effectiveNumColumns;
|
|
2653
|
-
} else {
|
|
2654
|
-
buttonWidthPx = isLandscape ? maxButtonSize : effectiveWidth / numColumns;
|
|
2655
|
-
}
|
|
2656
|
-
buttonDimensions = {
|
|
2657
|
-
widthPx: buttonWidthPx,
|
|
2658
|
-
heightPx: buttonHeightPx
|
|
2659
|
-
};
|
|
2660
|
-
} else if (deviceType === DeviceTypes.TABLET) {
|
|
2661
|
-
buttonDimensions = {
|
|
2662
|
-
widthPx: maxButtonSize,
|
|
2663
|
-
heightPx: maxButtonSize
|
|
2664
|
-
};
|
|
2665
|
-
} else {
|
|
2666
|
-
throw new Error("Invalid device type: " + deviceType);
|
|
2208
|
+
for (i = 0; i < sourceKeys.length; i++) {
|
|
2209
|
+
key = sourceKeys[i];
|
|
2210
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
2211
|
+
target[key] = source[key];
|
|
2667
2212
|
}
|
|
2668
2213
|
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
const numSeparators = (navigationPadEnabled ? 1 : 0) + (!paginationEnabled ? numPages - 1 : 0);
|
|
2672
|
-
const keypadWidth = effectiveNumColumns * buttonDimensions.widthPx + (navigationPadEnabled ? navigationPadWidthPx : 0) + numSeparators * innerBorderWidthPx;
|
|
2673
|
-
return {
|
|
2674
|
-
buttonDimensions,
|
|
2675
|
-
layoutMode: keypadWidth >= pageWidthPx ? LayoutModes.FULLSCREEN : LayoutModes.COMPACT
|
|
2676
|
-
};
|
|
2677
|
-
};
|
|
2214
|
+
return target;
|
|
2215
|
+
}
|
|
2678
2216
|
|
|
2679
2217
|
/**
|
|
2680
2218
|
* The state machine that backs our gesture system. In particular, this state
|
|
@@ -2684,15 +2222,23 @@ const computeLayoutParameters = ({
|
|
|
2684
2222
|
* multi-touch interactions, tracking gesture state on a per-touch basis.
|
|
2685
2223
|
*/
|
|
2686
2224
|
|
|
2687
|
-
|
|
2225
|
+
// exported for tests
|
|
2226
|
+
|
|
2227
|
+
const defaultOptions = {
|
|
2688
2228
|
longPressWaitTimeMs: 50,
|
|
2689
2229
|
swipeThresholdPx: 20,
|
|
2690
2230
|
holdIntervalMs: 250
|
|
2691
2231
|
};
|
|
2692
2232
|
class GestureStateMachine {
|
|
2693
2233
|
constructor(handlers, options, swipeDisabledNodeIds, multiPressableKeys) {
|
|
2234
|
+
this.handlers = void 0;
|
|
2235
|
+
this.options = void 0;
|
|
2236
|
+
this.swipeDisabledNodeIds = void 0;
|
|
2237
|
+
this.multiPressableKeys = void 0;
|
|
2238
|
+
this.touchState = void 0;
|
|
2239
|
+
this.swipeState = void 0;
|
|
2694
2240
|
this.handlers = handlers;
|
|
2695
|
-
this.options = _extends({},
|
|
2241
|
+
this.options = _extends({}, defaultOptions, options);
|
|
2696
2242
|
this.swipeDisabledNodeIds = swipeDisabledNodeIds || [];
|
|
2697
2243
|
this.multiPressableKeys = multiPressableKeys || [];
|
|
2698
2244
|
|
|
@@ -2953,6 +2499,10 @@ class GestureStateMachine {
|
|
|
2953
2499
|
|
|
2954
2500
|
class NodeManager {
|
|
2955
2501
|
constructor() {
|
|
2502
|
+
this._nodesById = void 0;
|
|
2503
|
+
this._bordersById = void 0;
|
|
2504
|
+
this._orderedIds = void 0;
|
|
2505
|
+
this._cachedBoundingBoxesById = void 0;
|
|
2956
2506
|
// A mapping from IDs to DOM nodes.
|
|
2957
2507
|
this._nodesById = {};
|
|
2958
2508
|
|
|
@@ -3002,6 +2552,7 @@ class NodeManager {
|
|
|
3002
2552
|
const seenIds = {};
|
|
3003
2553
|
for (const _id of allIds) {
|
|
3004
2554
|
if (!seenIds[_id]) {
|
|
2555
|
+
// @ts-expect-error TS2345
|
|
3005
2556
|
orderedIds.push(_id);
|
|
3006
2557
|
seenIds[_id] = true;
|
|
3007
2558
|
}
|
|
@@ -3070,6 +2621,9 @@ class NodeManager {
|
|
|
3070
2621
|
|
|
3071
2622
|
class PopoverStateMachine {
|
|
3072
2623
|
constructor(handlers) {
|
|
2624
|
+
this.handlers = void 0;
|
|
2625
|
+
this.popovers = void 0;
|
|
2626
|
+
this.activePopover = void 0;
|
|
3073
2627
|
this.handlers = handlers;
|
|
3074
2628
|
this.activePopover = null;
|
|
3075
2629
|
this.popovers = {};
|
|
@@ -3166,8 +2720,8 @@ class PopoverStateMachine {
|
|
|
3166
2720
|
this.activePopover = id;
|
|
3167
2721
|
this.handlers.onActiveNodesChanged({
|
|
3168
2722
|
popover: {
|
|
3169
|
-
parentId:
|
|
3170
|
-
childIds: this.popovers[
|
|
2723
|
+
parentId: id,
|
|
2724
|
+
childIds: this.popovers[id]
|
|
3171
2725
|
},
|
|
3172
2726
|
focus: this._defaultNodeForPopover(this.activePopover)
|
|
3173
2727
|
});
|
|
@@ -3229,12 +2783,17 @@ class PopoverStateMachine {
|
|
|
3229
2783
|
}
|
|
3230
2784
|
}
|
|
3231
2785
|
|
|
3232
|
-
const _excluded$
|
|
2786
|
+
const _excluded$4 = ["popover"];
|
|
3233
2787
|
const coordsForEvent = evt => {
|
|
3234
2788
|
return [evt.changedTouches[0].clientX, evt.changedTouches[0].clientY];
|
|
3235
2789
|
};
|
|
3236
2790
|
class GestureManager {
|
|
3237
2791
|
constructor(options, handlers, disabledSwipeKeys, multiPressableKeys) {
|
|
2792
|
+
this.swipeEnabled = void 0;
|
|
2793
|
+
this.trackEvents = void 0;
|
|
2794
|
+
this.nodeManager = void 0;
|
|
2795
|
+
this.popoverStateMachine = void 0;
|
|
2796
|
+
this.gestureStateMachine = void 0;
|
|
3238
2797
|
const {
|
|
3239
2798
|
swipeEnabled
|
|
3240
2799
|
} = options;
|
|
@@ -3248,7 +2807,7 @@ class GestureManager {
|
|
|
3248
2807
|
const {
|
|
3249
2808
|
popover
|
|
3250
2809
|
} = activeNodes,
|
|
3251
|
-
rest = _objectWithoutPropertiesLoose(activeNodes, _excluded$
|
|
2810
|
+
rest = _objectWithoutPropertiesLoose(activeNodes, _excluded$4);
|
|
3252
2811
|
handlers.onActiveNodesChanged(_extends({
|
|
3253
2812
|
popover: popover && {
|
|
3254
2813
|
parentId: popover.parentId,
|
|
@@ -3416,50 +2975,413 @@ class GestureManager {
|
|
|
3416
2975
|
}
|
|
3417
2976
|
}
|
|
3418
2977
|
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
style
|
|
3426
|
-
} = this.props;
|
|
3427
|
-
const containerStyle = [styles$d.container, ...(Array.isArray(style) ? style : [style])];
|
|
3428
|
-
return /*#__PURE__*/React.createElement(View, {
|
|
3429
|
-
style: containerStyle
|
|
3430
|
-
}, /*#__PURE__*/React.createElement("svg", {
|
|
3431
|
-
width: triangleSizePx,
|
|
3432
|
-
height: triangleSizePx,
|
|
3433
|
-
viewBox: "4 4 8 8"
|
|
3434
|
-
}, /*#__PURE__*/React.createElement("path", {
|
|
3435
|
-
fill: offBlack,
|
|
3436
|
-
opacity: "0.3",
|
|
3437
|
-
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
|
|
3438
|
-
})));
|
|
3439
|
-
}
|
|
3440
|
-
}
|
|
3441
|
-
CornerDecal.propTypes = {
|
|
3442
|
-
style: PropTypes.any
|
|
3443
|
-
};
|
|
3444
|
-
const triangleSizePx = 7;
|
|
3445
|
-
const styles$d = StyleSheet.create({
|
|
3446
|
-
container: {
|
|
3447
|
-
position: "absolute",
|
|
3448
|
-
top: 0,
|
|
3449
|
-
right: 0,
|
|
3450
|
-
width: triangleSizePx,
|
|
3451
|
-
height: triangleSizePx
|
|
3452
|
-
}
|
|
3453
|
-
});
|
|
3454
|
-
|
|
3455
|
-
/**
|
|
3456
|
-
* Common styles shared across components.
|
|
3457
|
-
*/
|
|
3458
|
-
var Styles = StyleSheet.create({
|
|
3459
|
-
row: {
|
|
3460
|
-
flexDirection: "row"
|
|
2978
|
+
const KeyConfigs = {
|
|
2979
|
+
// Basic math keys.
|
|
2980
|
+
[Keys.PLUS]: {
|
|
2981
|
+
type: KeyTypes.OPERATOR,
|
|
2982
|
+
// I18N: A label for a plus sign.
|
|
2983
|
+
ariaLabel: i18n._("Plus")
|
|
3461
2984
|
},
|
|
3462
|
-
|
|
2985
|
+
[Keys.MINUS]: {
|
|
2986
|
+
type: KeyTypes.OPERATOR,
|
|
2987
|
+
// I18N: A label for a minus sign.
|
|
2988
|
+
ariaLabel: i18n._("Minus")
|
|
2989
|
+
},
|
|
2990
|
+
[Keys.NEGATIVE]: {
|
|
2991
|
+
type: KeyTypes.VALUE,
|
|
2992
|
+
// I18N: A label for a minus sign.
|
|
2993
|
+
ariaLabel: i18n._("Negative")
|
|
2994
|
+
},
|
|
2995
|
+
[Keys.TIMES]: {
|
|
2996
|
+
type: KeyTypes.OPERATOR,
|
|
2997
|
+
// I18N: A label for a multiplication sign (represented with an 'x').
|
|
2998
|
+
ariaLabel: i18n._("Multiply")
|
|
2999
|
+
},
|
|
3000
|
+
[Keys.DIVIDE]: {
|
|
3001
|
+
type: KeyTypes.OPERATOR,
|
|
3002
|
+
// I18N: A label for a division sign.
|
|
3003
|
+
ariaLabel: i18n._("Divide")
|
|
3004
|
+
},
|
|
3005
|
+
[Keys.DECIMAL]: {
|
|
3006
|
+
type: KeyTypes.VALUE,
|
|
3007
|
+
// I18N: A label for a decimal symbol.
|
|
3008
|
+
ariaLabel: i18n._("Decimal"),
|
|
3009
|
+
icon: decimalSeparator === DecimalSeparators.COMMA ? {
|
|
3010
|
+
// TODO(charlie): Get an SVG icon for the comma, or verify with
|
|
3011
|
+
// design that the text-rendered version is acceptable.
|
|
3012
|
+
type: IconTypes.TEXT,
|
|
3013
|
+
data: ","
|
|
3014
|
+
} : {
|
|
3015
|
+
type: IconTypes.SVG,
|
|
3016
|
+
data: Keys.PERIOD
|
|
3017
|
+
}
|
|
3018
|
+
},
|
|
3019
|
+
[Keys.PERCENT]: {
|
|
3020
|
+
type: KeyTypes.OPERATOR,
|
|
3021
|
+
// I18N: A label for a percent sign.
|
|
3022
|
+
ariaLabel: i18n._("Percent")
|
|
3023
|
+
},
|
|
3024
|
+
[Keys.CDOT]: {
|
|
3025
|
+
type: KeyTypes.OPERATOR,
|
|
3026
|
+
// I18N: A label for a multiplication sign (represented as a dot).
|
|
3027
|
+
ariaLabel: i18n._("Multiply")
|
|
3028
|
+
},
|
|
3029
|
+
[Keys.EQUAL]: {
|
|
3030
|
+
type: KeyTypes.OPERATOR,
|
|
3031
|
+
ariaLabel: i18n._("Equals sign")
|
|
3032
|
+
},
|
|
3033
|
+
[Keys.NEQ]: {
|
|
3034
|
+
type: KeyTypes.OPERATOR,
|
|
3035
|
+
ariaLabel: i18n._("Not-equals sign")
|
|
3036
|
+
},
|
|
3037
|
+
[Keys.GT]: {
|
|
3038
|
+
type: KeyTypes.OPERATOR,
|
|
3039
|
+
// I18N: A label for a 'greater than' sign (represented as '>').
|
|
3040
|
+
ariaLabel: i18n._("Greater than sign")
|
|
3041
|
+
},
|
|
3042
|
+
[Keys.LT]: {
|
|
3043
|
+
type: KeyTypes.OPERATOR,
|
|
3044
|
+
// I18N: A label for a 'less than' sign (represented as '<').
|
|
3045
|
+
ariaLabel: i18n._("Less than sign")
|
|
3046
|
+
},
|
|
3047
|
+
[Keys.GEQ]: {
|
|
3048
|
+
type: KeyTypes.OPERATOR,
|
|
3049
|
+
ariaLabel: i18n._("Greater than or equal to sign")
|
|
3050
|
+
},
|
|
3051
|
+
[Keys.LEQ]: {
|
|
3052
|
+
type: KeyTypes.OPERATOR,
|
|
3053
|
+
ariaLabel: i18n._("Less than or equal to sign")
|
|
3054
|
+
},
|
|
3055
|
+
// mobile native
|
|
3056
|
+
[Keys.FRAC_INCLUSIVE]: {
|
|
3057
|
+
type: KeyTypes.OPERATOR,
|
|
3058
|
+
// I18N: A label for a button that creates a new fraction and puts the
|
|
3059
|
+
// current expression in the numerator of that fraction.
|
|
3060
|
+
ariaLabel: i18n._("Fraction, with current expression in numerator")
|
|
3061
|
+
},
|
|
3062
|
+
// mobile native
|
|
3063
|
+
[Keys.FRAC_EXCLUSIVE]: {
|
|
3064
|
+
type: KeyTypes.OPERATOR,
|
|
3065
|
+
// I18N: A label for a button that creates a new fraction next to the
|
|
3066
|
+
// cursor.
|
|
3067
|
+
ariaLabel: i18n._("Fraction, excluding the current expression")
|
|
3068
|
+
},
|
|
3069
|
+
// mobile web
|
|
3070
|
+
[Keys.FRAC]: {
|
|
3071
|
+
type: KeyTypes.OPERATOR,
|
|
3072
|
+
// I18N: A label for a button that creates a new fraction next to the
|
|
3073
|
+
// cursor.
|
|
3074
|
+
ariaLabel: i18n._("Fraction, excluding the current expression")
|
|
3075
|
+
},
|
|
3076
|
+
[Keys.EXP]: {
|
|
3077
|
+
type: KeyTypes.OPERATOR,
|
|
3078
|
+
// I18N: A label for a button that will allow the user to input a custom
|
|
3079
|
+
// exponent.
|
|
3080
|
+
ariaLabel: i18n._("Custom exponent")
|
|
3081
|
+
},
|
|
3082
|
+
[Keys.EXP_2]: {
|
|
3083
|
+
type: KeyTypes.OPERATOR,
|
|
3084
|
+
// I18N: A label for a button that will square (take to the second
|
|
3085
|
+
// power) some math.
|
|
3086
|
+
ariaLabel: i18n._("Square")
|
|
3087
|
+
},
|
|
3088
|
+
[Keys.EXP_3]: {
|
|
3089
|
+
type: KeyTypes.OPERATOR,
|
|
3090
|
+
// I18N: A label for a button that will cube (take to the third power)
|
|
3091
|
+
// some math.
|
|
3092
|
+
ariaLabel: i18n._("Cube")
|
|
3093
|
+
},
|
|
3094
|
+
[Keys.SQRT]: {
|
|
3095
|
+
type: KeyTypes.OPERATOR,
|
|
3096
|
+
ariaLabel: i18n._("Square root")
|
|
3097
|
+
},
|
|
3098
|
+
[Keys.CUBE_ROOT]: {
|
|
3099
|
+
type: KeyTypes.OPERATOR,
|
|
3100
|
+
ariaLabel: i18n._("Cube root")
|
|
3101
|
+
},
|
|
3102
|
+
[Keys.RADICAL]: {
|
|
3103
|
+
type: KeyTypes.OPERATOR,
|
|
3104
|
+
ariaLabel: i18n._("Radical with custom root")
|
|
3105
|
+
},
|
|
3106
|
+
[Keys.LEFT_PAREN]: {
|
|
3107
|
+
type: KeyTypes.OPERATOR,
|
|
3108
|
+
ariaLabel: i18n._("Left parenthesis")
|
|
3109
|
+
},
|
|
3110
|
+
[Keys.RIGHT_PAREN]: {
|
|
3111
|
+
type: KeyTypes.OPERATOR,
|
|
3112
|
+
ariaLabel: i18n._("Right parenthesis")
|
|
3113
|
+
},
|
|
3114
|
+
[Keys.LN]: {
|
|
3115
|
+
type: KeyTypes.OPERATOR,
|
|
3116
|
+
ariaLabel: i18n._("Natural logarithm")
|
|
3117
|
+
},
|
|
3118
|
+
[Keys.LOG]: {
|
|
3119
|
+
type: KeyTypes.OPERATOR,
|
|
3120
|
+
ariaLabel: i18n._("Logarithm with base 10")
|
|
3121
|
+
},
|
|
3122
|
+
[Keys.LOG_N]: {
|
|
3123
|
+
type: KeyTypes.OPERATOR,
|
|
3124
|
+
ariaLabel: i18n._("Logarithm with custom base")
|
|
3125
|
+
},
|
|
3126
|
+
[Keys.SIN]: {
|
|
3127
|
+
type: KeyTypes.OPERATOR,
|
|
3128
|
+
ariaLabel: i18n._("Sine")
|
|
3129
|
+
},
|
|
3130
|
+
[Keys.COS]: {
|
|
3131
|
+
type: KeyTypes.OPERATOR,
|
|
3132
|
+
ariaLabel: i18n._("Cosine")
|
|
3133
|
+
},
|
|
3134
|
+
[Keys.TAN]: {
|
|
3135
|
+
type: KeyTypes.OPERATOR,
|
|
3136
|
+
ariaLabel: i18n._("Tangent")
|
|
3137
|
+
},
|
|
3138
|
+
[Keys.PI]: {
|
|
3139
|
+
type: KeyTypes.VALUE,
|
|
3140
|
+
ariaLabel: i18n._("Pi"),
|
|
3141
|
+
icon: {
|
|
3142
|
+
type: IconTypes.MATH,
|
|
3143
|
+
data: "\\pi"
|
|
3144
|
+
}
|
|
3145
|
+
},
|
|
3146
|
+
[Keys.THETA]: {
|
|
3147
|
+
type: KeyTypes.VALUE,
|
|
3148
|
+
ariaLabel: i18n._("Theta"),
|
|
3149
|
+
icon: {
|
|
3150
|
+
type: IconTypes.MATH,
|
|
3151
|
+
data: "\\theta"
|
|
3152
|
+
}
|
|
3153
|
+
},
|
|
3154
|
+
[Keys.NOOP]: {
|
|
3155
|
+
type: KeyTypes.EMPTY
|
|
3156
|
+
},
|
|
3157
|
+
// Input navigation keys.
|
|
3158
|
+
[Keys.UP]: {
|
|
3159
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3160
|
+
ariaLabel: i18n._("Up arrow")
|
|
3161
|
+
},
|
|
3162
|
+
[Keys.RIGHT]: {
|
|
3163
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3164
|
+
ariaLabel: i18n._("Right arrow")
|
|
3165
|
+
},
|
|
3166
|
+
[Keys.DOWN]: {
|
|
3167
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3168
|
+
ariaLabel: i18n._("Down arrow")
|
|
3169
|
+
},
|
|
3170
|
+
[Keys.LEFT]: {
|
|
3171
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3172
|
+
ariaLabel: i18n._("Left arrow")
|
|
3173
|
+
},
|
|
3174
|
+
[Keys.JUMP_OUT_PARENTHESES]: {
|
|
3175
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3176
|
+
ariaLabel: i18n._("Navigate right out of a set of parentheses")
|
|
3177
|
+
},
|
|
3178
|
+
[Keys.JUMP_OUT_EXPONENT]: {
|
|
3179
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3180
|
+
ariaLabel: i18n._("Navigate right out of an exponent")
|
|
3181
|
+
},
|
|
3182
|
+
[Keys.JUMP_OUT_BASE]: {
|
|
3183
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3184
|
+
ariaLabel: i18n._("Navigate right out of a base")
|
|
3185
|
+
},
|
|
3186
|
+
[Keys.JUMP_INTO_NUMERATOR]: {
|
|
3187
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3188
|
+
ariaLabel: i18n._("Navigate right into the numerator of a fraction")
|
|
3189
|
+
},
|
|
3190
|
+
[Keys.JUMP_OUT_NUMERATOR]: {
|
|
3191
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3192
|
+
ariaLabel: i18n._("Navigate right out of the numerator and into the denominator")
|
|
3193
|
+
},
|
|
3194
|
+
[Keys.JUMP_OUT_DENOMINATOR]: {
|
|
3195
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3196
|
+
ariaLabel: i18n._("Navigate right out of the denominator of a fraction")
|
|
3197
|
+
},
|
|
3198
|
+
[Keys.BACKSPACE]: {
|
|
3199
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3200
|
+
// I18N: A label for a button that will delete some input.
|
|
3201
|
+
ariaLabel: i18n._("Delete")
|
|
3202
|
+
},
|
|
3203
|
+
// Keypad navigation keys.
|
|
3204
|
+
[Keys.DISMISS]: {
|
|
3205
|
+
type: KeyTypes.KEYPAD_NAVIGATION,
|
|
3206
|
+
// I18N: A label for a button that will dismiss/hide a keypad.
|
|
3207
|
+
ariaLabel: i18n._("Dismiss")
|
|
3208
|
+
}
|
|
3209
|
+
};
|
|
3210
|
+
|
|
3211
|
+
// Add in any multi-function buttons. By default, these keys will mix in any
|
|
3212
|
+
// configuration settings from their default child key (i.e., the first key in
|
|
3213
|
+
// the `childKeyIds` array).
|
|
3214
|
+
// TODO(charlie): Make the multi-function button's long-press interaction
|
|
3215
|
+
// accessible.
|
|
3216
|
+
// NOTE(kevinb): This is only used in the mobile native app.
|
|
3217
|
+
KeyConfigs[Keys.FRAC_MULTI] = {
|
|
3218
|
+
childKeyIds: [Keys.FRAC_INCLUSIVE, Keys.FRAC_EXCLUSIVE]
|
|
3219
|
+
};
|
|
3220
|
+
|
|
3221
|
+
// TODO(charlie): Use the numeral color for the 'Many' key.
|
|
3222
|
+
KeyConfigs[Keys.MANY] = {
|
|
3223
|
+
type: KeyTypes.MANY
|
|
3224
|
+
// childKeyIds will be configured by the client.
|
|
3225
|
+
};
|
|
3226
|
+
|
|
3227
|
+
// Add in every numeral.
|
|
3228
|
+
const NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
3229
|
+
for (const num of NUMBERS) {
|
|
3230
|
+
// TODO(charlie): Consider removing the SVG icons that we have for the
|
|
3231
|
+
// numeral keys. They can be rendered just as easily with text (though that
|
|
3232
|
+
// would mean that we'd be using text beyond the variable key).
|
|
3233
|
+
const textRepresentation = `${num}`;
|
|
3234
|
+
KeyConfigs[`NUM_${num}`] = {
|
|
3235
|
+
type: KeyTypes.VALUE,
|
|
3236
|
+
ariaLabel: textRepresentation,
|
|
3237
|
+
icon: {
|
|
3238
|
+
type: IconTypes.TEXT,
|
|
3239
|
+
data: textRepresentation
|
|
3240
|
+
}
|
|
3241
|
+
};
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3244
|
+
// Add in every variable.
|
|
3245
|
+
const LETTERS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
|
|
3246
|
+
for (const letter of LETTERS) {
|
|
3247
|
+
const lowerCaseVariable = letter.toLowerCase();
|
|
3248
|
+
const upperCaseVariable = letter.toUpperCase();
|
|
3249
|
+
for (const textRepresentation of [lowerCaseVariable, upperCaseVariable]) {
|
|
3250
|
+
KeyConfigs[textRepresentation] = {
|
|
3251
|
+
type: KeyTypes.VALUE,
|
|
3252
|
+
ariaLabel: textRepresentation,
|
|
3253
|
+
icon: {
|
|
3254
|
+
type: IconTypes.MATH,
|
|
3255
|
+
data: textRepresentation
|
|
3256
|
+
}
|
|
3257
|
+
};
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
for (const key of Object.keys(KeyConfigs)) {
|
|
3261
|
+
KeyConfigs[key] = _extends({
|
|
3262
|
+
id: key,
|
|
3263
|
+
// Default to an SVG icon indexed by the key name.
|
|
3264
|
+
icon: {
|
|
3265
|
+
type: IconTypes.SVG,
|
|
3266
|
+
data: key
|
|
3267
|
+
}
|
|
3268
|
+
}, KeyConfigs[key]);
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
// Used to generate unique animation IDs for the echo animations. The actual
|
|
3272
|
+
// values are irrelevant as long as they are unique.
|
|
3273
|
+
let _lastAnimationId = 0;
|
|
3274
|
+
const initialEchoState = {
|
|
3275
|
+
echoes: []
|
|
3276
|
+
};
|
|
3277
|
+
const echoReducer = function echoReducer(state = initialEchoState, action) {
|
|
3278
|
+
switch (action.type) {
|
|
3279
|
+
case "PressKey":
|
|
3280
|
+
const keyConfig = KeyConfigs[action.key];
|
|
3281
|
+
|
|
3282
|
+
// Add in the echo animation if the user performs a math
|
|
3283
|
+
// operation.
|
|
3284
|
+
if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
|
|
3285
|
+
return _extends({}, state, {
|
|
3286
|
+
echoes: [...state.echoes, {
|
|
3287
|
+
animationId: "" + _lastAnimationId++,
|
|
3288
|
+
animationType: action.inPopover ? EchoAnimationTypes.LONG_FADE_ONLY : EchoAnimationTypes.FADE_ONLY,
|
|
3289
|
+
borders: action.borders,
|
|
3290
|
+
id: keyConfig.id,
|
|
3291
|
+
initialBounds: action.initialBounds
|
|
3292
|
+
}]
|
|
3293
|
+
});
|
|
3294
|
+
}
|
|
3295
|
+
return state;
|
|
3296
|
+
case "RemoveEcho":
|
|
3297
|
+
const remainingEchoes = state.echoes.filter(echo => {
|
|
3298
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'animationId' does not exist on type 'never'.
|
|
3299
|
+
return echo.animationId !== action.animationId;
|
|
3300
|
+
});
|
|
3301
|
+
return _extends({}, state, {
|
|
3302
|
+
echoes: remainingEchoes
|
|
3303
|
+
});
|
|
3304
|
+
default:
|
|
3305
|
+
return state;
|
|
3306
|
+
}
|
|
3307
|
+
};
|
|
3308
|
+
|
|
3309
|
+
const initialInputState = {
|
|
3310
|
+
keyHandler: null,
|
|
3311
|
+
cursor: {
|
|
3312
|
+
context: NONE
|
|
3313
|
+
}
|
|
3314
|
+
};
|
|
3315
|
+
const inputReducer = function inputReducer(state = initialInputState, action) {
|
|
3316
|
+
switch (action.type) {
|
|
3317
|
+
case "SetKeyHandler":
|
|
3318
|
+
return _extends({}, state, {
|
|
3319
|
+
keyHandler: action.keyHandler
|
|
3320
|
+
});
|
|
3321
|
+
case "PressKey":
|
|
3322
|
+
const keyConfig = KeyConfigs[action.key];
|
|
3323
|
+
if (keyConfig.type !== KeyTypes.KEYPAD_NAVIGATION) {
|
|
3324
|
+
// This is probably an anti-pattern but it works for the
|
|
3325
|
+
// case where we don't actually control the state but we
|
|
3326
|
+
// still want to communicate with the other object
|
|
3327
|
+
return _extends({}, state, {
|
|
3328
|
+
cursor: state.keyHandler == null ? void 0 : state.keyHandler(keyConfig.id)
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
|
|
3332
|
+
// TODO(kevinb) get state from MathQuill and store it?
|
|
3333
|
+
return state;
|
|
3334
|
+
case "SetCursor":
|
|
3335
|
+
return _extends({}, state, {
|
|
3336
|
+
cursor: action.cursor
|
|
3337
|
+
});
|
|
3338
|
+
default:
|
|
3339
|
+
return state;
|
|
3340
|
+
}
|
|
3341
|
+
};
|
|
3342
|
+
|
|
3343
|
+
/**
|
|
3344
|
+
* A small triangular decal to sit in the corner of a parent component.
|
|
3345
|
+
*/
|
|
3346
|
+
class CornerDecal extends React.Component {
|
|
3347
|
+
render() {
|
|
3348
|
+
const {
|
|
3349
|
+
style
|
|
3350
|
+
} = this.props;
|
|
3351
|
+
const containerStyle = [styles$d.container, ...(Array.isArray(style) ? style : [style])];
|
|
3352
|
+
return /*#__PURE__*/React.createElement(View, {
|
|
3353
|
+
style: containerStyle
|
|
3354
|
+
}, /*#__PURE__*/React.createElement("svg", {
|
|
3355
|
+
width: triangleSizePx,
|
|
3356
|
+
height: triangleSizePx,
|
|
3357
|
+
viewBox: "4 4 8 8"
|
|
3358
|
+
}, /*#__PURE__*/React.createElement("path", {
|
|
3359
|
+
fill: offBlack,
|
|
3360
|
+
opacity: "0.3",
|
|
3361
|
+
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
|
|
3362
|
+
})));
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
|
|
3366
|
+
const triangleSizePx = 7;
|
|
3367
|
+
const styles$d = StyleSheet.create({
|
|
3368
|
+
container: {
|
|
3369
|
+
position: "absolute",
|
|
3370
|
+
top: 0,
|
|
3371
|
+
right: 0,
|
|
3372
|
+
width: triangleSizePx,
|
|
3373
|
+
height: triangleSizePx
|
|
3374
|
+
}
|
|
3375
|
+
});
|
|
3376
|
+
|
|
3377
|
+
/**
|
|
3378
|
+
* Common styles shared across components.
|
|
3379
|
+
*/
|
|
3380
|
+
var Styles = StyleSheet.create({
|
|
3381
|
+
row: {
|
|
3382
|
+
flexDirection: "row"
|
|
3383
|
+
},
|
|
3384
|
+
column: {
|
|
3463
3385
|
flexDirection: "column"
|
|
3464
3386
|
},
|
|
3465
3387
|
oneColumn: {
|
|
@@ -3521,10 +3443,6 @@ class MathIcon extends React.Component {
|
|
|
3521
3443
|
});
|
|
3522
3444
|
}
|
|
3523
3445
|
}
|
|
3524
|
-
MathIcon.propTypes = {
|
|
3525
|
-
math: PropTypes.string.isRequired,
|
|
3526
|
-
style: PropTypes.any
|
|
3527
|
-
};
|
|
3528
3446
|
const styles$c = StyleSheet.create({
|
|
3529
3447
|
size: {
|
|
3530
3448
|
height: iconSizeHeightPx,
|
|
@@ -4834,10 +4752,6 @@ class SvgIcon extends React.Component {
|
|
|
4834
4752
|
});
|
|
4835
4753
|
}
|
|
4836
4754
|
}
|
|
4837
|
-
SvgIcon.propTypes = {
|
|
4838
|
-
color: PropTypes.string.isRequired,
|
|
4839
|
-
name: PropTypes.string.isRequired
|
|
4840
|
-
};
|
|
4841
4755
|
|
|
4842
4756
|
/**
|
|
4843
4757
|
* A component that renders a text-based icon.
|
|
@@ -4858,10 +4772,6 @@ class TextIcon extends React.Component {
|
|
|
4858
4772
|
}, /*#__PURE__*/React.createElement(Text, null, character));
|
|
4859
4773
|
}
|
|
4860
4774
|
}
|
|
4861
|
-
TextIcon.propTypes = {
|
|
4862
|
-
character: PropTypes.string.isRequired,
|
|
4863
|
-
style: PropTypes.any
|
|
4864
|
-
};
|
|
4865
4775
|
const styles$b = StyleSheet.create({
|
|
4866
4776
|
size: {
|
|
4867
4777
|
height: iconSizeHeightPx,
|
|
@@ -4906,18 +4816,11 @@ class Icon extends React.PureComponent {
|
|
|
4906
4816
|
character: icon.data,
|
|
4907
4817
|
style: styleWithFocus
|
|
4908
4818
|
});
|
|
4819
|
+
default:
|
|
4820
|
+
throw new Error("No icon or symbol provided");
|
|
4909
4821
|
}
|
|
4910
|
-
throw new Error("No icon or symbol provided");
|
|
4911
4822
|
}
|
|
4912
4823
|
}
|
|
4913
|
-
Icon.propTypes = {
|
|
4914
|
-
focused: PropTypes.bool,
|
|
4915
|
-
icon: iconPropType.isRequired,
|
|
4916
|
-
// An Aphrodite style object, or an array of Aphrodite style objects.
|
|
4917
|
-
// Note that custom styles will only be applied to text and math icons
|
|
4918
|
-
// (and not SVG icons).
|
|
4919
|
-
style: PropTypes.any
|
|
4920
|
-
};
|
|
4921
4824
|
const styles$a = StyleSheet.create({
|
|
4922
4825
|
unfocused: {
|
|
4923
4826
|
color: unfocusedColor
|
|
@@ -5012,13 +4915,9 @@ class MultiSymbolGrid extends React.Component {
|
|
|
5012
4915
|
}))));
|
|
5013
4916
|
}
|
|
5014
4917
|
}
|
|
5015
|
-
throw new Error(
|
|
4918
|
+
throw new Error(`Invalid number of icons: ${icons.length}`);
|
|
5016
4919
|
}
|
|
5017
4920
|
}
|
|
5018
|
-
MultiSymbolGrid.propTypes = {
|
|
5019
|
-
focused: PropTypes.bool,
|
|
5020
|
-
icons: PropTypes.arrayOf(iconPropType).isRequired
|
|
5021
|
-
};
|
|
5022
4921
|
const verticalInsetPx = 2;
|
|
5023
4922
|
const horizontalInsetPx = 4;
|
|
5024
4923
|
const styles$9 = StyleSheet.create({
|
|
@@ -5062,6 +4961,7 @@ const styles$9 = StyleSheet.create({
|
|
|
5062
4961
|
class KeypadButton extends React.PureComponent {
|
|
5063
4962
|
constructor(...args) {
|
|
5064
4963
|
super(...args);
|
|
4964
|
+
this.buttonSizeStyle = void 0;
|
|
5065
4965
|
this._preInjectStyles = () => {
|
|
5066
4966
|
// HACK(charlie): Pre-inject all of the possible styles for the button.
|
|
5067
4967
|
// This avoids a flickering effect in the echo animation whereby the
|
|
@@ -5110,10 +5010,12 @@ class KeypadButton extends React.PureComponent {
|
|
|
5110
5010
|
break;
|
|
5111
5011
|
}
|
|
5112
5012
|
const borderStyle = [];
|
|
5113
|
-
if (borders.
|
|
5013
|
+
if (borders.includes(BorderDirections.LEFT)) {
|
|
5014
|
+
// @ts-expect-error TS2345
|
|
5114
5015
|
borderStyle.push(styles$8.leftBorder);
|
|
5115
5016
|
}
|
|
5116
|
-
if (borders.
|
|
5017
|
+
if (borders.includes(BorderDirections.BOTTOM)) {
|
|
5018
|
+
// @ts-expect-error TS2345
|
|
5117
5019
|
borderStyle.push(styles$8.bottomBorder);
|
|
5118
5020
|
}
|
|
5119
5021
|
return [styles$8.buttonBase, backgroundStyle, ...borderStyle, type === KeyTypes.ECHO && styles$8.echo, this.buttonSizeStyle,
|
|
@@ -5161,7 +5063,7 @@ class KeypadButton extends React.PureComponent {
|
|
|
5161
5063
|
const renderFocused = !disabled && focused || popoverEnabled || type === KeyTypes.ECHO;
|
|
5162
5064
|
const buttonStyle = this._getButtonStyle(type, borders, style);
|
|
5163
5065
|
const focusStyle = this._getFocusStyle(type);
|
|
5164
|
-
const iconWrapperStyle = [styles$8.iconWrapper, disabled
|
|
5066
|
+
const iconWrapperStyle = [styles$8.iconWrapper, disabled ? styles$8.disabled : undefined];
|
|
5165
5067
|
const eventHandlers = {
|
|
5166
5068
|
onTouchCancel,
|
|
5167
5069
|
onTouchEnd,
|
|
@@ -5212,32 +5114,6 @@ class KeypadButton extends React.PureComponent {
|
|
|
5212
5114
|
}
|
|
5213
5115
|
}
|
|
5214
5116
|
}
|
|
5215
|
-
KeypadButton.propTypes = {
|
|
5216
|
-
ariaLabel: PropTypes.string,
|
|
5217
|
-
// The borders to display on the button. Typically, this should be set
|
|
5218
|
-
// using one of the preset `BorderStyles` options.
|
|
5219
|
-
borders: bordersPropType,
|
|
5220
|
-
// Any additional keys that can be accessed by long-pressing on the
|
|
5221
|
-
// button.
|
|
5222
|
-
childKeys: PropTypes.arrayOf(keyConfigPropType),
|
|
5223
|
-
// Whether the button should be rendered in a 'disabled' state, i.e.,
|
|
5224
|
-
// without any touch feedback.
|
|
5225
|
-
disabled: PropTypes.bool,
|
|
5226
|
-
focused: PropTypes.bool,
|
|
5227
|
-
heightPx: PropTypes.number.isRequired,
|
|
5228
|
-
icon: iconPropType,
|
|
5229
|
-
onTouchCancel: PropTypes.func,
|
|
5230
|
-
onTouchEnd: PropTypes.func,
|
|
5231
|
-
onTouchMove: PropTypes.func,
|
|
5232
|
-
onTouchStart: PropTypes.func,
|
|
5233
|
-
popoverEnabled: PropTypes.bool,
|
|
5234
|
-
style: PropTypes.any,
|
|
5235
|
-
type: PropTypes.oneOf(Object.keys(KeyTypes)).isRequired,
|
|
5236
|
-
// NOTE(charlie): We may want to make this optional for phone layouts
|
|
5237
|
-
// (and rely on Flexbox instead), since it might not be pixel perfect
|
|
5238
|
-
// with borders and such.
|
|
5239
|
-
widthPx: PropTypes.number.isRequired
|
|
5240
|
-
};
|
|
5241
5117
|
KeypadButton.defaultProps = {
|
|
5242
5118
|
borders: BorderStyles.ALL,
|
|
5243
5119
|
childKeys: [],
|
|
@@ -5249,8 +5125,6 @@ const focusInsetPx = 4;
|
|
|
5249
5125
|
const focusBoxZIndex = 0;
|
|
5250
5126
|
const styles$8 = StyleSheet.create({
|
|
5251
5127
|
buttonBase: {
|
|
5252
|
-
// HACK(benkomalo): support old style flex box in Android browsers
|
|
5253
|
-
"-webkit-box-flex": "1",
|
|
5254
5128
|
flex: 1,
|
|
5255
5129
|
cursor: "pointer",
|
|
5256
5130
|
// Make the text unselectable
|
|
@@ -5326,20 +5200,23 @@ const styleForButtonDimensions = (heightPx, widthPx) => {
|
|
|
5326
5200
|
}).buttonSize;
|
|
5327
5201
|
};
|
|
5328
5202
|
const mapStateToProps$7 = state => {
|
|
5329
|
-
return
|
|
5203
|
+
return {
|
|
5204
|
+
heightPx: state.layout.buttonDimensions.heightPx,
|
|
5205
|
+
widthPx: state.layout.buttonDimensions.widthPx
|
|
5206
|
+
};
|
|
5330
5207
|
};
|
|
5331
5208
|
var KeypadButton$1 = connect(mapStateToProps$7, null, null, {
|
|
5332
5209
|
forwardRef: true
|
|
5333
5210
|
})(KeypadButton);
|
|
5334
5211
|
|
|
5335
|
-
const _excluded$
|
|
5212
|
+
const _excluded$3 = ["gestureManager"];
|
|
5336
5213
|
class EmptyKeypadButton extends React.Component {
|
|
5337
5214
|
render() {
|
|
5338
5215
|
const _this$props = this.props,
|
|
5339
5216
|
{
|
|
5340
5217
|
gestureManager
|
|
5341
5218
|
} = _this$props,
|
|
5342
|
-
rest = _objectWithoutPropertiesLoose(_this$props, _excluded$
|
|
5219
|
+
rest = _objectWithoutPropertiesLoose(_this$props, _excluded$3);
|
|
5343
5220
|
|
|
5344
5221
|
// Register touch events on the button, but don't register its DOM node
|
|
5345
5222
|
// or compute focus state or anything like that. We want the gesture
|
|
@@ -5354,9 +5231,6 @@ class EmptyKeypadButton extends React.Component {
|
|
|
5354
5231
|
}, KeyConfigs.NOOP, rest));
|
|
5355
5232
|
}
|
|
5356
5233
|
}
|
|
5357
|
-
EmptyKeypadButton.propTypes = {
|
|
5358
|
-
gestureManager: PropTypes.instanceOf(GestureManager)
|
|
5359
|
-
};
|
|
5360
5234
|
const mapStateToProps$6 = state => {
|
|
5361
5235
|
const {
|
|
5362
5236
|
gestures
|
|
@@ -5369,7 +5243,7 @@ var EmptyKeypadButton$1 = connect(mapStateToProps$6, null, null, {
|
|
|
5369
5243
|
forwardRef: true
|
|
5370
5244
|
})(EmptyKeypadButton);
|
|
5371
5245
|
|
|
5372
|
-
const _excluded$
|
|
5246
|
+
const _excluded$2 = ["borders", "childKeyIds", "disabled", "gestureManager", "id", "style"],
|
|
5373
5247
|
_excluded2 = ["keyConfig"];
|
|
5374
5248
|
class TouchableKeypadButton extends React.Component {
|
|
5375
5249
|
shouldComponentUpdate(newProps) {
|
|
@@ -5396,7 +5270,7 @@ class TouchableKeypadButton extends React.Component {
|
|
|
5396
5270
|
id,
|
|
5397
5271
|
style
|
|
5398
5272
|
} = _this$props,
|
|
5399
|
-
rest = _objectWithoutPropertiesLoose(_this$props, _excluded$
|
|
5273
|
+
rest = _objectWithoutPropertiesLoose(_this$props, _excluded$2);
|
|
5400
5274
|
|
|
5401
5275
|
// Only bind the relevant event handlers if the key is enabled.
|
|
5402
5276
|
const eventHandlers = disabled ? {
|
|
@@ -5416,17 +5290,6 @@ class TouchableKeypadButton extends React.Component {
|
|
|
5416
5290
|
}, eventHandlers, rest));
|
|
5417
5291
|
}
|
|
5418
5292
|
}
|
|
5419
|
-
TouchableKeypadButton.propTypes = {
|
|
5420
|
-
borders: bordersPropType,
|
|
5421
|
-
childKeyIds: PropTypes.arrayOf(keyIdPropType),
|
|
5422
|
-
disabled: PropTypes.bool,
|
|
5423
|
-
focused: PropTypes.bool,
|
|
5424
|
-
gestureManager: PropTypes.instanceOf(GestureManager),
|
|
5425
|
-
id: keyIdPropType.isRequired,
|
|
5426
|
-
popoverEnabled: PropTypes.bool,
|
|
5427
|
-
style: PropTypes.any,
|
|
5428
|
-
type: PropTypes.oneOf(Object.keys(KeyTypes)).isRequired
|
|
5429
|
-
};
|
|
5430
5293
|
const extractProps = keyConfig => {
|
|
5431
5294
|
const {
|
|
5432
5295
|
ariaLabel,
|
|
@@ -5479,20 +5342,20 @@ var TouchableKeypadButton$1 = connect(mapStateToProps$5, null, null, {
|
|
|
5479
5342
|
forwardRef: true
|
|
5480
5343
|
})(TouchableKeypadButton);
|
|
5481
5344
|
|
|
5482
|
-
const _excluded$
|
|
5345
|
+
const _excluded$1 = ["keys"];
|
|
5483
5346
|
class ManyKeypadButton extends React.Component {
|
|
5484
5347
|
render() {
|
|
5485
5348
|
const _this$props = this.props,
|
|
5486
5349
|
{
|
|
5487
5350
|
keys
|
|
5488
5351
|
} = _this$props,
|
|
5489
|
-
rest = _objectWithoutPropertiesLoose(_this$props, _excluded$
|
|
5352
|
+
rest = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
|
|
5490
5353
|
|
|
5491
5354
|
// If we have no extra symbols, render an empty button. If we have just
|
|
5492
5355
|
// one, render a standard button. Otherwise, capture them all in a
|
|
5493
5356
|
// single button.
|
|
5494
5357
|
if (keys.length === 0) {
|
|
5495
|
-
return /*#__PURE__*/React.createElement(EmptyKeypadButton$1,
|
|
5358
|
+
return /*#__PURE__*/React.createElement(EmptyKeypadButton$1, null);
|
|
5496
5359
|
} else if (keys.length === 1) {
|
|
5497
5360
|
const keyConfig = KeyConfigs[keys[0]];
|
|
5498
5361
|
return /*#__PURE__*/React.createElement(TouchableKeypadButton$1, _extends({
|
|
@@ -5510,8 +5373,8 @@ class ManyKeypadButton extends React.Component {
|
|
|
5510
5373
|
}
|
|
5511
5374
|
}
|
|
5512
5375
|
}
|
|
5513
|
-
ManyKeypadButton.
|
|
5514
|
-
keys:
|
|
5376
|
+
ManyKeypadButton.defaultProps = {
|
|
5377
|
+
keys: []
|
|
5515
5378
|
};
|
|
5516
5379
|
|
|
5517
5380
|
/**
|
|
@@ -5559,20 +5422,12 @@ class Echo extends React.Component {
|
|
|
5559
5422
|
return /*#__PURE__*/React.createElement("div", {
|
|
5560
5423
|
style: containerStyle
|
|
5561
5424
|
}, /*#__PURE__*/React.createElement(KeypadButton$1, {
|
|
5562
|
-
name: id,
|
|
5563
5425
|
icon: icon,
|
|
5564
5426
|
type: KeyTypes.ECHO,
|
|
5565
5427
|
borders: borders
|
|
5566
5428
|
}));
|
|
5567
5429
|
}
|
|
5568
5430
|
}
|
|
5569
|
-
Echo.propTypes = {
|
|
5570
|
-
animationDurationMs: PropTypes.number.isRequired,
|
|
5571
|
-
borders: bordersPropType,
|
|
5572
|
-
id: keyIdPropType.isRequired,
|
|
5573
|
-
initialBounds: boundingBoxPropType.isRequired,
|
|
5574
|
-
onAnimationFinish: PropTypes.func.isRequired
|
|
5575
|
-
};
|
|
5576
5431
|
class EchoManager extends React.Component {
|
|
5577
5432
|
constructor(...args) {
|
|
5578
5433
|
super(...args);
|
|
@@ -5595,7 +5450,7 @@ class EchoManager extends React.Component {
|
|
|
5595
5450
|
animationTransitionName = "echo-long-fade-only";
|
|
5596
5451
|
break;
|
|
5597
5452
|
default:
|
|
5598
|
-
throw new Error(
|
|
5453
|
+
throw new Error(`Invalid echo animation type: ${animationType}`);
|
|
5599
5454
|
}
|
|
5600
5455
|
return {
|
|
5601
5456
|
animationDurationMs,
|
|
@@ -5640,16 +5495,12 @@ class EchoManager extends React.Component {
|
|
|
5640
5495
|
key: animationId
|
|
5641
5496
|
}, /*#__PURE__*/React.createElement(Echo, _extends({
|
|
5642
5497
|
animationDurationMs: animationDurationMs,
|
|
5643
|
-
onAnimationFinish: () => onAnimationFinish(animationId)
|
|
5498
|
+
onAnimationFinish: () => onAnimationFinish == null ? void 0 : onAnimationFinish(animationId)
|
|
5644
5499
|
}, echo)));
|
|
5645
5500
|
}));
|
|
5646
5501
|
}));
|
|
5647
5502
|
}
|
|
5648
5503
|
}
|
|
5649
|
-
EchoManager.propTypes = {
|
|
5650
|
-
echoes: PropTypes.arrayOf(echoPropType),
|
|
5651
|
-
onAnimationFinish: PropTypes.func.isRequired
|
|
5652
|
-
};
|
|
5653
5504
|
|
|
5654
5505
|
/**
|
|
5655
5506
|
* A popover that renders a set of keys floating above the page.
|
|
@@ -5673,9 +5524,6 @@ class MultiSymbolPopover extends React.Component {
|
|
|
5673
5524
|
}));
|
|
5674
5525
|
}
|
|
5675
5526
|
}
|
|
5676
|
-
MultiSymbolPopover.propTypes = {
|
|
5677
|
-
keys: PropTypes.arrayOf(keyConfigPropType)
|
|
5678
|
-
};
|
|
5679
5527
|
const styles$6 = StyleSheet.create({
|
|
5680
5528
|
container: {
|
|
5681
5529
|
flexDirection: "column-reverse",
|
|
@@ -5696,7 +5544,6 @@ const styles$6 = StyleSheet.create({
|
|
|
5696
5544
|
// classnames specified in popover.less.
|
|
5697
5545
|
const animationTransitionName = "popover";
|
|
5698
5546
|
const animationDurationMs = 200;
|
|
5699
|
-
|
|
5700
5547
|
// A container component used to position a popover absolutely at a specific
|
|
5701
5548
|
// position.
|
|
5702
5549
|
class PopoverContainer extends React.Component {
|
|
@@ -5715,10 +5562,6 @@ class PopoverContainer extends React.Component {
|
|
|
5715
5562
|
}));
|
|
5716
5563
|
}
|
|
5717
5564
|
}
|
|
5718
|
-
PopoverContainer.propTypes = {
|
|
5719
|
-
bounds: boundingBoxPropType.isRequired,
|
|
5720
|
-
childKeys: PropTypes.arrayOf(keyConfigPropType).isRequired
|
|
5721
|
-
};
|
|
5722
5565
|
class PopoverManager extends React.Component {
|
|
5723
5566
|
render() {
|
|
5724
5567
|
const {
|
|
@@ -5739,16 +5582,15 @@ class PopoverManager extends React.Component {
|
|
|
5739
5582
|
})) : null;
|
|
5740
5583
|
}
|
|
5741
5584
|
}
|
|
5742
|
-
PopoverManager.propTypes = {
|
|
5743
|
-
popover: popoverPropType
|
|
5744
|
-
};
|
|
5745
|
-
|
|
5746
|
-
const _excluded$1 = ["initialBounds"];
|
|
5747
5585
|
|
|
5586
|
+
const _excluded = ["initialBounds"];
|
|
5748
5587
|
// eslint-disable-next-line react/no-unsafe
|
|
5749
5588
|
class Keypad extends React.Component {
|
|
5750
5589
|
constructor(...args) {
|
|
5751
5590
|
super(...args);
|
|
5591
|
+
this._isMounted = void 0;
|
|
5592
|
+
this._resizeTimeout = void 0;
|
|
5593
|
+
this._container = void 0;
|
|
5752
5594
|
this._computeContainer = () => {
|
|
5753
5595
|
const domNode = ReactDOM.findDOMNode(this);
|
|
5754
5596
|
this._container = domNode.getBoundingClientRect();
|
|
@@ -5770,7 +5612,7 @@ class Keypad extends React.Component {
|
|
|
5770
5612
|
// Throttle resize events -- taken from:
|
|
5771
5613
|
// https://developer.mozilla.org/en-US/docs/Web/Events/resize
|
|
5772
5614
|
if (this._resizeTimeout == null) {
|
|
5773
|
-
this._resizeTimeout = setTimeout(() => {
|
|
5615
|
+
this._resizeTimeout = window.setTimeout(() => {
|
|
5774
5616
|
this._resizeTimeout = null;
|
|
5775
5617
|
if (this._isMounted) {
|
|
5776
5618
|
this._updateSizeAndPosition();
|
|
@@ -5808,12 +5650,16 @@ class Keypad extends React.Component {
|
|
|
5808
5650
|
const {
|
|
5809
5651
|
initialBounds
|
|
5810
5652
|
} = echo,
|
|
5811
|
-
rest = _objectWithoutPropertiesLoose(echo, _excluded
|
|
5653
|
+
rest = _objectWithoutPropertiesLoose(echo, _excluded);
|
|
5812
5654
|
return _extends({}, rest, {
|
|
5813
5655
|
initialBounds: {
|
|
5656
|
+
// @ts-expect-error TS2533
|
|
5814
5657
|
top: initialBounds.top - this._container.top,
|
|
5658
|
+
// @ts-expect-error TS2533
|
|
5815
5659
|
right: initialBounds.right - this._container.left,
|
|
5660
|
+
// @ts-expect-error TS2533
|
|
5816
5661
|
bottom: initialBounds.bottom - this._container.top,
|
|
5662
|
+
// @ts-expect-error TS2533
|
|
5817
5663
|
left: initialBounds.left - this._container.left,
|
|
5818
5664
|
width: initialBounds.width,
|
|
5819
5665
|
height: initialBounds.height
|
|
@@ -5826,7 +5672,12 @@ class Keypad extends React.Component {
|
|
|
5826
5672
|
// the bottom left corners of the keys over which they appear.
|
|
5827
5673
|
const relativePopover = popover && _extends({}, popover, {
|
|
5828
5674
|
bounds: {
|
|
5829
|
-
bottom:
|
|
5675
|
+
bottom:
|
|
5676
|
+
// @ts-expect-error TS2533
|
|
5677
|
+
this._container.height - (
|
|
5678
|
+
// @ts-expect-error TS2533
|
|
5679
|
+
popover.bounds.bottom - this._container.top),
|
|
5680
|
+
// @ts-expect-error TS2533
|
|
5830
5681
|
left: popover.bounds.left - this._container.left,
|
|
5831
5682
|
width: popover.bounds.width
|
|
5832
5683
|
}
|
|
@@ -5841,23 +5692,12 @@ class Keypad extends React.Component {
|
|
|
5841
5692
|
}));
|
|
5842
5693
|
}
|
|
5843
5694
|
}
|
|
5844
|
-
Keypad.propTypes = {
|
|
5845
|
-
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
|
|
5846
|
-
removeEcho: PropTypes.func.isRequired,
|
|
5847
|
-
style: PropTypes.any,
|
|
5848
|
-
// The props below are injected by redux
|
|
5849
|
-
|
|
5850
|
-
// Whether the keypad is active, i.e., whether it should be rendered as
|
|
5851
|
-
// visible or invisible.
|
|
5852
|
-
active: PropTypes.bool,
|
|
5853
|
-
echoes: PropTypes.arrayOf(echoPropType).isRequired,
|
|
5854
|
-
popover: popoverPropType
|
|
5855
|
-
};
|
|
5856
5695
|
const mapStateToProps$4 = state => {
|
|
5857
|
-
return
|
|
5696
|
+
return {
|
|
5697
|
+
echoes: state.echoes.echoes,
|
|
5858
5698
|
active: state.keypad.active,
|
|
5859
5699
|
popover: state.gestures.popover
|
|
5860
|
-
}
|
|
5700
|
+
};
|
|
5861
5701
|
};
|
|
5862
5702
|
const mapDispatchToProps$1 = dispatch => {
|
|
5863
5703
|
return {
|
|
@@ -6093,12 +5933,6 @@ class TwoPageKeypad extends React.Component {
|
|
|
6093
5933
|
}
|
|
6094
5934
|
}
|
|
6095
5935
|
}
|
|
6096
|
-
TwoPageKeypad.propTypes = {
|
|
6097
|
-
currentPage: PropTypes.oneOf([0, 1]).isRequired,
|
|
6098
|
-
leftPage: PropTypes.node.isRequired,
|
|
6099
|
-
paginationEnabled: PropTypes.bool.isRequired,
|
|
6100
|
-
rightPage: PropTypes.node.isRequired
|
|
6101
|
-
};
|
|
6102
5936
|
const styles$3 = StyleSheet.create({
|
|
6103
5937
|
keypad: {
|
|
6104
5938
|
// Set the background to light grey, so that when the user drags the
|
|
@@ -6133,11 +5967,16 @@ const {
|
|
|
6133
5967
|
roundedTopLeft: roundedTopLeft$2,
|
|
6134
5968
|
roundedTopRight: roundedTopRight$1
|
|
6135
5969
|
} = Styles;
|
|
5970
|
+
const expressionKeypadLayout = {
|
|
5971
|
+
rows: 4,
|
|
5972
|
+
columns: 5,
|
|
5973
|
+
numPages: 2,
|
|
5974
|
+
// Since we include a two-key popover in the top-right, when the popover
|
|
5975
|
+
// is visible, the keypad will expand to fill the equivalent of five
|
|
5976
|
+
// rows vertically.
|
|
5977
|
+
maxVisibleRows: 4
|
|
5978
|
+
};
|
|
6136
5979
|
class ExpressionKeypad extends React.Component {
|
|
6137
|
-
// Though we include an infinite-key popover in the bottom-left, it's
|
|
6138
|
-
// assumed that we don't need to accommodate cases in which that key
|
|
6139
|
-
// contains more than four children.
|
|
6140
|
-
|
|
6141
5980
|
render() {
|
|
6142
5981
|
const {
|
|
6143
5982
|
currentPage,
|
|
@@ -6191,8 +6030,7 @@ class ExpressionKeypad extends React.Component {
|
|
|
6191
6030
|
keyConfig: KeyConfigs.NUM_1,
|
|
6192
6031
|
borders: BorderStyles.BOTTOM
|
|
6193
6032
|
}), /*#__PURE__*/React.createElement(ManyKeypadButton, {
|
|
6194
|
-
keys: extraKeys
|
|
6195
|
-
borders: BorderStyles.NONE
|
|
6033
|
+
keys: extraKeys
|
|
6196
6034
|
})), /*#__PURE__*/React.createElement(View, {
|
|
6197
6035
|
style: [column$1, oneColumn]
|
|
6198
6036
|
}, /*#__PURE__*/React.createElement(TouchableKeypadButton$1, {
|
|
@@ -6330,18 +6168,6 @@ class ExpressionKeypad extends React.Component {
|
|
|
6330
6168
|
});
|
|
6331
6169
|
}
|
|
6332
6170
|
}
|
|
6333
|
-
ExpressionKeypad.propTypes = {
|
|
6334
|
-
currentPage: PropTypes.number.isRequired,
|
|
6335
|
-
cursorContext: cursorContextPropType.isRequired,
|
|
6336
|
-
dynamicJumpOut: PropTypes.bool,
|
|
6337
|
-
extraKeys: PropTypes.arrayOf(keyIdPropType),
|
|
6338
|
-
roundTopLeft: PropTypes.bool,
|
|
6339
|
-
roundTopRight: PropTypes.bool
|
|
6340
|
-
};
|
|
6341
|
-
ExpressionKeypad.rows = 4;
|
|
6342
|
-
ExpressionKeypad.columns = 5;
|
|
6343
|
-
ExpressionKeypad.maxVisibleRows = 4;
|
|
6344
|
-
ExpressionKeypad.numPages = 2;
|
|
6345
6171
|
const styles$2 = StyleSheet.create({
|
|
6346
6172
|
// NOTE(charlie): These backgrounds are applied to as to fill in some
|
|
6347
6173
|
// unfortunate 'cracks' in the layout. However, not all keys in the first
|
|
@@ -6356,9 +6182,10 @@ const styles$2 = StyleSheet.create({
|
|
|
6356
6182
|
}
|
|
6357
6183
|
});
|
|
6358
6184
|
const mapStateToProps$2 = state => {
|
|
6185
|
+
var _state$input$cursor;
|
|
6359
6186
|
return {
|
|
6360
6187
|
currentPage: state.pager.currentPage,
|
|
6361
|
-
cursorContext: state.input.cursor.context,
|
|
6188
|
+
cursorContext: (_state$input$cursor = state.input.cursor) == null ? void 0 : _state$input$cursor.context,
|
|
6362
6189
|
dynamicJumpOut: !state.layout.navigationPadEnabled
|
|
6363
6190
|
};
|
|
6364
6191
|
};
|
|
@@ -6375,11 +6202,16 @@ const {
|
|
|
6375
6202
|
roundedTopLeft: roundedTopLeft$1,
|
|
6376
6203
|
roundedTopRight
|
|
6377
6204
|
} = Styles;
|
|
6378
|
-
|
|
6205
|
+
const fractionKeypadLayout = {
|
|
6206
|
+
rows: 4,
|
|
6207
|
+
columns: 4,
|
|
6208
|
+
numPages: 1,
|
|
6379
6209
|
// Since we include a two-key popover in the top-right, when the popover
|
|
6380
6210
|
// is visible, the keypad will expand to fill the equivalent of five
|
|
6381
6211
|
// rows vertically.
|
|
6382
|
-
|
|
6212
|
+
maxVisibleRows: 5
|
|
6213
|
+
};
|
|
6214
|
+
class FractionKeypad extends React.Component {
|
|
6383
6215
|
render() {
|
|
6384
6216
|
const {
|
|
6385
6217
|
cursorContext,
|
|
@@ -6482,29 +6314,290 @@ class FractionKeypad extends React.Component {
|
|
|
6482
6314
|
borders: BorderStyles.LEFT
|
|
6483
6315
|
})));
|
|
6484
6316
|
}
|
|
6485
|
-
}
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
};
|
|
6492
|
-
|
|
6493
|
-
FractionKeypad
|
|
6494
|
-
|
|
6495
|
-
FractionKeypad
|
|
6496
|
-
|
|
6317
|
+
}
|
|
6318
|
+
const mapStateToProps$1 = state => {
|
|
6319
|
+
var _state$input$cursor;
|
|
6320
|
+
return {
|
|
6321
|
+
cursorContext: (_state$input$cursor = state.input.cursor) == null ? void 0 : _state$input$cursor.context,
|
|
6322
|
+
dynamicJumpOut: !state.layout.navigationPadEnabled
|
|
6323
|
+
};
|
|
6324
|
+
};
|
|
6325
|
+
var FractionKeypad$1 = connect(mapStateToProps$1, null, null, {
|
|
6326
|
+
forwardRef: true
|
|
6327
|
+
})(FractionKeypad);
|
|
6328
|
+
|
|
6329
|
+
const defaultKeypadType = KeypadTypes.EXPRESSION;
|
|
6330
|
+
const keypadForType = {
|
|
6331
|
+
[KeypadTypes.FRACTION]: fractionKeypadLayout,
|
|
6332
|
+
[KeypadTypes.EXPRESSION]: expressionKeypadLayout
|
|
6333
|
+
};
|
|
6334
|
+
|
|
6335
|
+
const initialKeypadState = {
|
|
6336
|
+
extraKeys: ["x", "y", Keys.THETA, Keys.PI],
|
|
6337
|
+
keypadType: defaultKeypadType,
|
|
6338
|
+
active: false
|
|
6339
|
+
};
|
|
6340
|
+
const keypadReducer = function keypadReducer(state = initialKeypadState, action) {
|
|
6341
|
+
switch (action.type) {
|
|
6342
|
+
case "DismissKeypad":
|
|
6343
|
+
return _extends({}, state, {
|
|
6344
|
+
active: false
|
|
6345
|
+
});
|
|
6346
|
+
case "ActivateKeypad":
|
|
6347
|
+
return _extends({}, state, {
|
|
6348
|
+
active: true
|
|
6349
|
+
});
|
|
6350
|
+
case "ConfigureKeypad":
|
|
6351
|
+
return _extends({}, state, {
|
|
6352
|
+
// Default `extraKeys` to the empty array.
|
|
6353
|
+
extraKeys: []
|
|
6354
|
+
}, action.configuration);
|
|
6355
|
+
case "PressKey":
|
|
6356
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
|
|
6357
|
+
const keyConfig = KeyConfigs[action.key];
|
|
6358
|
+
// NOTE(charlie): Our keypad system operates by triggering key
|
|
6359
|
+
// presses with key IDs in a dumb manner, such that the keys
|
|
6360
|
+
// don't know what they can do--instead, the store is
|
|
6361
|
+
// responsible for interpreting key presses and triggering the
|
|
6362
|
+
// right actions when they occur. Hence, we figure off a
|
|
6363
|
+
// dismissal here rather than dispatching a dismiss action in
|
|
6364
|
+
// the first place.
|
|
6365
|
+
if (keyConfig.id === Keys.DISMISS) {
|
|
6366
|
+
return keypadReducer(state, {
|
|
6367
|
+
type: "DismissKeypad"
|
|
6368
|
+
});
|
|
6369
|
+
}
|
|
6370
|
+
return state;
|
|
6371
|
+
default:
|
|
6372
|
+
return state;
|
|
6373
|
+
}
|
|
6374
|
+
};
|
|
6375
|
+
|
|
6376
|
+
/**
|
|
6377
|
+
* An algorithm for computing the appropriate layout parameters for the keypad,
|
|
6378
|
+
* including the size of the buttons and whether or not to render fullscreen,
|
|
6379
|
+
* taking into account a number of factors including the size of the screen, the
|
|
6380
|
+
* orientation of the screen, the presence of browser chrome, the presence of
|
|
6381
|
+
* other exercise-related chrome, the size of the input box, the parameters that
|
|
6382
|
+
* define the keypad (i.e., the number of rows, columns, and pages), and so
|
|
6383
|
+
* forth.
|
|
6384
|
+
*
|
|
6385
|
+
* The computations herein make some strong assumptions about the sizes of
|
|
6386
|
+
* various other elements and the situations under which they will be visible
|
|
6387
|
+
* (e.g., browser chrome). However, this is just a heuristic--it's not crucial
|
|
6388
|
+
* that our buttons are sized in a pixel-perfect manner, but rather, that we
|
|
6389
|
+
* make a balanced use of space.
|
|
6390
|
+
*
|
|
6391
|
+
* Note that one goal of the algorithm is to avoid resizing the keypad in the
|
|
6392
|
+
* face of dynamic browser chrome. In order to avoid that awkwardness, we tend
|
|
6393
|
+
* to be conservative in our measurements and make things smaller than they
|
|
6394
|
+
* might need to be.
|
|
6395
|
+
*/
|
|
6396
|
+
const minButtonHeight = 48;
|
|
6397
|
+
const maxButtonSize = 64;
|
|
6398
|
+
const minSpaceAboveKeypad = 32;
|
|
6399
|
+
|
|
6400
|
+
// These values are taken from an iPhone 5, but should be consistent with the
|
|
6401
|
+
// iPhone 4 as well. Regardless, these are meant to be representative of the
|
|
6402
|
+
// possible types of browser chrome that could appear in various context, rather
|
|
6403
|
+
// than pixel-perfect for every device.
|
|
6404
|
+
const safariNavBarWhenShrunk = 44;
|
|
6405
|
+
const safariNavBarWhenExpanded = 64;
|
|
6406
|
+
const safariToolbar = 44;
|
|
6407
|
+
|
|
6408
|
+
// In mobile Safari, the browser chrome is completely hidden in landscape,
|
|
6409
|
+
// though a shrunken navbar and full-sized toolbar on scroll. In portrait, the
|
|
6410
|
+
// shrunken navbar is always visible, but expands on scroll (and the toolbar
|
|
6411
|
+
// appears as well).
|
|
6412
|
+
const maxLandscapeBrowserChrome = safariNavBarWhenShrunk + safariToolbar;
|
|
6413
|
+
const maxPortraitBrowserChrome = safariToolbar + (safariNavBarWhenExpanded - safariNavBarWhenShrunk);
|
|
6414
|
+
|
|
6415
|
+
// This represents the 'worst case' aspect ratio that we care about (for
|
|
6416
|
+
// portrait layouts). It's taken from the iPhone 4. The height is computed by
|
|
6417
|
+
// taking the height of the device and removing the persistent, shrunken navbar.
|
|
6418
|
+
// (We don't need to account for the expanded navbar, since we include the
|
|
6419
|
+
// difference when reserving space above the keypad.)
|
|
6420
|
+
const worstCaseAspectRatio = 320 / (480 - safariNavBarWhenShrunk);
|
|
6421
|
+
const computeLayoutParameters = ({
|
|
6422
|
+
numColumns,
|
|
6423
|
+
numMaxVisibleRows,
|
|
6424
|
+
numPages
|
|
6425
|
+
}, {
|
|
6426
|
+
pageWidthPx,
|
|
6427
|
+
pageHeightPx
|
|
6428
|
+
}, {
|
|
6429
|
+
deviceOrientation,
|
|
6430
|
+
deviceType
|
|
6431
|
+
}, {
|
|
6432
|
+
navigationPadEnabled,
|
|
6433
|
+
paginationEnabled,
|
|
6434
|
+
toolbarEnabled
|
|
6435
|
+
}) => {
|
|
6436
|
+
// First, compute some values that will be used in multiple computations.
|
|
6437
|
+
const effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
|
|
6438
|
+
|
|
6439
|
+
// Then, compute the button dimensions based on the provided parameters.
|
|
6440
|
+
let buttonDimensions;
|
|
6441
|
+
if (deviceType === DeviceTypes.PHONE) {
|
|
6442
|
+
const isLandscape = deviceOrientation === DeviceOrientations.LANDSCAPE;
|
|
6443
|
+
|
|
6444
|
+
// In many cases, the browser chrome will already have been factored
|
|
6445
|
+
// into `pageHeightPx`. But we have no way of knowing if that's
|
|
6446
|
+
// the case or not. As such, we take a conservative approach and
|
|
6447
|
+
// assume that the chrome is _never_ included in `pageHeightPx`.
|
|
6448
|
+
const browserChromeHeight = isLandscape ? maxLandscapeBrowserChrome : maxPortraitBrowserChrome;
|
|
6449
|
+
|
|
6450
|
+
// Count up all the space that we need to reserve on the page.
|
|
6451
|
+
// Namely, we need to account for:
|
|
6452
|
+
// 1. Space between the keypad and the top of the page.
|
|
6453
|
+
// 2. The presence of the exercise toolbar.
|
|
6454
|
+
// 3. The presence of the view pager indicator.
|
|
6455
|
+
// 4. Any browser chrome that may appear later.
|
|
6456
|
+
const reservedSpace = minSpaceAboveKeypad + browserChromeHeight + (toolbarEnabled ? toolbarHeightPx : 0) + (paginationEnabled ? pageIndicatorHeightPx : 0);
|
|
6457
|
+
|
|
6458
|
+
// Next, compute the effective width and height. We can use the page
|
|
6459
|
+
// width as the effective width. For the height, though, we take
|
|
6460
|
+
// another conservative measure when in portrait by assuming that
|
|
6461
|
+
// the device has the worst possible aspect ratio. In other words,
|
|
6462
|
+
// we ignore the device height in portrait and assume the worst.
|
|
6463
|
+
// This prevents the keypad from changing size when browser chrome
|
|
6464
|
+
// appears and disappears.
|
|
6465
|
+
const effectiveWidth = pageWidthPx;
|
|
6466
|
+
const effectiveHeight = isLandscape ? pageHeightPx : pageWidthPx / worstCaseAspectRatio;
|
|
6467
|
+
const maxKeypadHeight = effectiveHeight - reservedSpace;
|
|
6468
|
+
|
|
6469
|
+
// Finally, compute the button height and width. In computing the
|
|
6470
|
+
// height, accommodate for the maximum number of rows that will ever be
|
|
6471
|
+
// visible (since the toggling of popovers can increase the number of
|
|
6472
|
+
// visible rows).
|
|
6473
|
+
const buttonHeightPx = Math.max(Math.min(maxKeypadHeight / numMaxVisibleRows, maxButtonSize), minButtonHeight);
|
|
6474
|
+
let buttonWidthPx;
|
|
6475
|
+
if (numPages > 1) {
|
|
6476
|
+
const _effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
|
|
6477
|
+
buttonWidthPx = effectiveWidth / _effectiveNumColumns;
|
|
6478
|
+
} else {
|
|
6479
|
+
buttonWidthPx = isLandscape ? maxButtonSize : effectiveWidth / numColumns;
|
|
6480
|
+
}
|
|
6481
|
+
buttonDimensions = {
|
|
6482
|
+
widthPx: buttonWidthPx,
|
|
6483
|
+
heightPx: buttonHeightPx
|
|
6484
|
+
};
|
|
6485
|
+
} else if (deviceType === DeviceTypes.TABLET) {
|
|
6486
|
+
buttonDimensions = {
|
|
6487
|
+
widthPx: maxButtonSize,
|
|
6488
|
+
heightPx: maxButtonSize
|
|
6489
|
+
};
|
|
6490
|
+
} else {
|
|
6491
|
+
throw new Error("Invalid device type: " + deviceType);
|
|
6492
|
+
}
|
|
6493
|
+
|
|
6494
|
+
// Finally, determine whether the keypad should be rendered in the
|
|
6495
|
+
// fullscreen layout by determining its resultant width.
|
|
6496
|
+
const numSeparators = (navigationPadEnabled ? 1 : 0) + (!paginationEnabled ? numPages - 1 : 0);
|
|
6497
|
+
const keypadWidth = effectiveNumColumns * buttonDimensions.widthPx + (navigationPadEnabled ? navigationPadWidthPx : 0) + numSeparators * innerBorderWidthPx;
|
|
6497
6498
|
return {
|
|
6498
|
-
|
|
6499
|
-
|
|
6499
|
+
buttonDimensions,
|
|
6500
|
+
layoutMode: keypadWidth >= pageWidthPx ? LayoutModes.FULLSCREEN : LayoutModes.COMPACT
|
|
6500
6501
|
};
|
|
6501
6502
|
};
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6503
|
+
|
|
6504
|
+
const initialLayoutState = {
|
|
6505
|
+
gridDimensions: {
|
|
6506
|
+
numRows: keypadForType[defaultKeypadType].rows,
|
|
6507
|
+
numColumns: keypadForType[defaultKeypadType].columns,
|
|
6508
|
+
numMaxVisibleRows: keypadForType[defaultKeypadType].maxVisibleRows,
|
|
6509
|
+
numPages: keypadForType[defaultKeypadType].numPages
|
|
6510
|
+
},
|
|
6511
|
+
buttonDimensions: {
|
|
6512
|
+
widthPx: 48,
|
|
6513
|
+
heightPx: 48
|
|
6514
|
+
},
|
|
6515
|
+
pageDimensions: {
|
|
6516
|
+
pageWidthPx: 0,
|
|
6517
|
+
pageHeightPx: 0
|
|
6518
|
+
},
|
|
6519
|
+
layoutMode: LayoutModes.FULLSCREEN,
|
|
6520
|
+
paginationEnabled: false,
|
|
6521
|
+
navigationPadEnabled: false
|
|
6522
|
+
};
|
|
6523
|
+
|
|
6524
|
+
/**
|
|
6525
|
+
* Compute the additional layout state based on the provided page and grid
|
|
6526
|
+
* dimensions.
|
|
6527
|
+
*/
|
|
6528
|
+
const layoutParametersForDimensions = (pageDimensions, gridDimensions) => {
|
|
6529
|
+
const {
|
|
6530
|
+
pageWidthPx,
|
|
6531
|
+
pageHeightPx
|
|
6532
|
+
} = pageDimensions;
|
|
6533
|
+
|
|
6534
|
+
// Determine the device type and orientation.
|
|
6535
|
+
const deviceOrientation = pageWidthPx > pageHeightPx ? DeviceOrientations.LANDSCAPE : DeviceOrientations.PORTRAIT;
|
|
6536
|
+
const deviceType = Math.min(pageWidthPx, pageHeightPx) > tabletCutoffPx ? DeviceTypes.TABLET : DeviceTypes.PHONE;
|
|
6537
|
+
|
|
6538
|
+
// Using that information, make some decisions (or assumptions)
|
|
6539
|
+
// about the resulting layout.
|
|
6540
|
+
const navigationPadEnabled = deviceType === DeviceTypes.TABLET;
|
|
6541
|
+
const paginationEnabled = deviceType === DeviceTypes.PHONE && deviceOrientation === DeviceOrientations.PORTRAIT;
|
|
6542
|
+
const deviceInfo = {
|
|
6543
|
+
deviceOrientation,
|
|
6544
|
+
deviceType
|
|
6545
|
+
};
|
|
6546
|
+
const layoutOptions = {
|
|
6547
|
+
navigationPadEnabled,
|
|
6548
|
+
paginationEnabled,
|
|
6549
|
+
// HACK(charlie): It's not great that we're making assumptions about
|
|
6550
|
+
// the toolbar (which is rendered by webapp, and should always be
|
|
6551
|
+
// visible and anchored to the bottom of the page for phone and
|
|
6552
|
+
// tablet exercises). But this is primarily a heuristic (the goal is
|
|
6553
|
+
// to preserve a 'good' amount of space between the top of the
|
|
6554
|
+
// keypad and the top of the page) so we afford to have some margin
|
|
6555
|
+
// of error.
|
|
6556
|
+
toolbarEnabled: true
|
|
6557
|
+
};
|
|
6558
|
+
return _extends({}, computeLayoutParameters(gridDimensions, pageDimensions, deviceInfo, layoutOptions), {
|
|
6559
|
+
// Pass along some of the layout information, so that other
|
|
6560
|
+
// components in the heirarchy can adapt appropriately.
|
|
6561
|
+
navigationPadEnabled,
|
|
6562
|
+
paginationEnabled
|
|
6563
|
+
});
|
|
6564
|
+
};
|
|
6565
|
+
const layoutReducer = function layoutReducer(state = initialLayoutState, action) {
|
|
6566
|
+
switch (action.type) {
|
|
6567
|
+
case "ConfigureKeypad":
|
|
6568
|
+
const {
|
|
6569
|
+
keypadType
|
|
6570
|
+
} = action.configuration;
|
|
6571
|
+
const gridDimensions = {
|
|
6572
|
+
numRows: keypadForType[keypadType].rows,
|
|
6573
|
+
numColumns: keypadForType[keypadType].columns,
|
|
6574
|
+
numMaxVisibleRows: keypadForType[keypadType].maxVisibleRows,
|
|
6575
|
+
numPages: keypadForType[keypadType].numPages
|
|
6576
|
+
};
|
|
6577
|
+
return _extends({}, state, layoutParametersForDimensions(state.pageDimensions, gridDimensions), {
|
|
6578
|
+
gridDimensions
|
|
6579
|
+
});
|
|
6580
|
+
case "SetPageSize":
|
|
6581
|
+
const {
|
|
6582
|
+
pageWidthPx,
|
|
6583
|
+
pageHeightPx
|
|
6584
|
+
} = action;
|
|
6585
|
+
const pageDimensions = {
|
|
6586
|
+
pageWidthPx,
|
|
6587
|
+
pageHeightPx
|
|
6588
|
+
};
|
|
6589
|
+
return _extends({}, state, layoutParametersForDimensions(pageDimensions, state.gridDimensions), {
|
|
6590
|
+
pageDimensions
|
|
6591
|
+
});
|
|
6592
|
+
default:
|
|
6593
|
+
return state;
|
|
6594
|
+
}
|
|
6595
|
+
};
|
|
6505
6596
|
|
|
6506
6597
|
class VelocityTracker {
|
|
6507
6598
|
constructor(options) {
|
|
6599
|
+
this.options = void 0;
|
|
6600
|
+
this._events = void 0;
|
|
6508
6601
|
this.options = _extends({
|
|
6509
6602
|
velocityTimeout: 100
|
|
6510
6603
|
}, options);
|
|
@@ -6567,195 +6660,119 @@ class VelocityTracker {
|
|
|
6567
6660
|
}
|
|
6568
6661
|
}
|
|
6569
6662
|
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6663
|
+
// We default to the right-most page. This is done so-as to enforce a
|
|
6664
|
+
// consistent orientation between the view pager layout and the flattened
|
|
6665
|
+
// layout, where our default page appears on the far right.
|
|
6666
|
+
const getDefaultPage = numPages => numPages - 1;
|
|
6667
|
+
const initialPagerState = {
|
|
6668
|
+
animateToPosition: false,
|
|
6669
|
+
currentPage: getDefaultPage(keypadForType[defaultKeypadType].numPages),
|
|
6670
|
+
// The cumulative differential in the horizontal direction for the
|
|
6671
|
+
// current swipe.
|
|
6672
|
+
dx: 0,
|
|
6673
|
+
numPages: keypadForType[defaultKeypadType].numPages,
|
|
6674
|
+
pageWidthPx: 0,
|
|
6675
|
+
velocityTracker: new VelocityTracker()
|
|
6573
6676
|
};
|
|
6574
|
-
const
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
return
|
|
6602
|
-
|
|
6603
|
-
});
|
|
6604
|
-
default:
|
|
6605
|
-
return state;
|
|
6606
|
-
}
|
|
6607
|
-
};
|
|
6608
|
-
const defaultKeypadType = KeypadTypes.EXPRESSION;
|
|
6609
|
-
const initialKeypadState = {
|
|
6610
|
-
extraKeys: ["x", "y", Keys.THETA, Keys.PI],
|
|
6611
|
-
keypadType: defaultKeypadType,
|
|
6612
|
-
active: false
|
|
6613
|
-
};
|
|
6614
|
-
const keypadReducer = function keypadReducer(state = initialKeypadState, action) {
|
|
6615
|
-
switch (action.type) {
|
|
6616
|
-
case "DismissKeypad":
|
|
6617
|
-
return _extends({}, state, {
|
|
6618
|
-
active: false
|
|
6619
|
-
});
|
|
6620
|
-
case "ActivateKeypad":
|
|
6621
|
-
return _extends({}, state, {
|
|
6622
|
-
active: true
|
|
6623
|
-
});
|
|
6624
|
-
case "ConfigureKeypad":
|
|
6625
|
-
return _extends({}, state, {
|
|
6626
|
-
// Default `extraKeys` to the empty array.
|
|
6627
|
-
extraKeys: []
|
|
6628
|
-
}, action.configuration);
|
|
6629
|
-
case "PressKey":
|
|
6630
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
|
|
6631
|
-
const keyConfig = KeyConfigs[action.key];
|
|
6632
|
-
// NOTE(charlie): Our keypad system operates by triggering key
|
|
6633
|
-
// presses with key IDs in a dumb manner, such that the keys
|
|
6634
|
-
// don't know what they can do--instead, the store is
|
|
6635
|
-
// responsible for interpreting key presses and triggering the
|
|
6636
|
-
// right actions when they occur. Hence, we figure off a
|
|
6637
|
-
// dismissal here rather than dispatching a dismiss action in
|
|
6638
|
-
// the first place.
|
|
6639
|
-
if (keyConfig.id === Keys.DISMISS) {
|
|
6640
|
-
return keypadReducer(state, {
|
|
6641
|
-
type: "DismissKeypad"
|
|
6642
|
-
});
|
|
6643
|
-
}
|
|
6644
|
-
return state;
|
|
6645
|
-
default:
|
|
6646
|
-
return state;
|
|
6647
|
-
}
|
|
6648
|
-
};
|
|
6649
|
-
|
|
6650
|
-
// We default to the right-most page. This is done so-as to enforce a
|
|
6651
|
-
// consistent orientation between the view pager layout and the flattened
|
|
6652
|
-
// layout, where our default page appears on the far right.
|
|
6653
|
-
const getDefaultPage = numPages => numPages - 1;
|
|
6654
|
-
const initialPagerState = {
|
|
6655
|
-
animateToPosition: false,
|
|
6656
|
-
currentPage: getDefaultPage(keypadForType[defaultKeypadType].numPages),
|
|
6657
|
-
// The cumulative differential in the horizontal direction for the
|
|
6658
|
-
// current swipe.
|
|
6659
|
-
dx: 0,
|
|
6660
|
-
numPages: keypadForType[defaultKeypadType].numPages,
|
|
6661
|
-
pageWidthPx: 0,
|
|
6662
|
-
velocityTracker: new VelocityTracker()
|
|
6663
|
-
};
|
|
6664
|
-
const pagerReducer = function pagerReducer(state = initialPagerState, action) {
|
|
6665
|
-
switch (action.type) {
|
|
6666
|
-
case "ConfigureKeypad":
|
|
6667
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'configuration' does not exist on type '{ type: string; }'.
|
|
6668
|
-
const {
|
|
6669
|
-
keypadType
|
|
6670
|
-
} = action.configuration;
|
|
6671
|
-
const {
|
|
6672
|
-
numPages
|
|
6673
|
-
} = keypadForType[keypadType];
|
|
6674
|
-
return _extends({}, state, {
|
|
6675
|
-
numPages,
|
|
6676
|
-
animateToPosition: false,
|
|
6677
|
-
currentPage: getDefaultPage(numPages),
|
|
6678
|
-
dx: 0
|
|
6679
|
-
});
|
|
6680
|
-
case "SetPageSize":
|
|
6681
|
-
return _extends({}, state, {
|
|
6682
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'pageWidthPx' does not exist on type '{ type: string; }'.
|
|
6683
|
-
pageWidthPx: action.pageWidthPx
|
|
6684
|
-
});
|
|
6685
|
-
case "PressKey":
|
|
6686
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
|
|
6687
|
-
const keyConfig = KeyConfigs[action.key];
|
|
6688
|
-
|
|
6689
|
-
// Reset the keypad page if the user performs a math operation.
|
|
6690
|
-
if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
|
|
6691
|
-
return pagerReducer(state, {
|
|
6692
|
-
type: "ResetKeypadPage"
|
|
6693
|
-
});
|
|
6694
|
-
}
|
|
6695
|
-
return state;
|
|
6696
|
-
case "ResetKeypadPage":
|
|
6697
|
-
return _extends({}, state, {
|
|
6698
|
-
animateToPosition: true,
|
|
6699
|
-
// We start at the right-most page.
|
|
6700
|
-
currentPage: getDefaultPage(state.numPages),
|
|
6701
|
-
dx: 0
|
|
6702
|
-
});
|
|
6703
|
-
case "PageKeypadRight":
|
|
6704
|
-
const nextPage = Math.min(state.currentPage + 1, state.numPages - 1);
|
|
6705
|
-
return _extends({}, state, {
|
|
6706
|
-
animateToPosition: true,
|
|
6707
|
-
currentPage: nextPage,
|
|
6708
|
-
dx: 0
|
|
6709
|
-
});
|
|
6710
|
-
case "PageKeypadLeft":
|
|
6711
|
-
const prevPage = Math.max(state.currentPage - 1, 0);
|
|
6712
|
-
return _extends({}, state, {
|
|
6713
|
-
animateToPosition: true,
|
|
6714
|
-
currentPage: prevPage,
|
|
6715
|
-
dx: 0
|
|
6677
|
+
const pagerReducer = function pagerReducer(state = initialPagerState, action) {
|
|
6678
|
+
switch (action.type) {
|
|
6679
|
+
case "ConfigureKeypad":
|
|
6680
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'configuration' does not exist on type '{ type: string; }'.
|
|
6681
|
+
const {
|
|
6682
|
+
keypadType
|
|
6683
|
+
} = action.configuration;
|
|
6684
|
+
const {
|
|
6685
|
+
numPages
|
|
6686
|
+
} = keypadForType[keypadType];
|
|
6687
|
+
return _extends({}, state, {
|
|
6688
|
+
numPages,
|
|
6689
|
+
animateToPosition: false,
|
|
6690
|
+
currentPage: getDefaultPage(numPages),
|
|
6691
|
+
dx: 0
|
|
6692
|
+
});
|
|
6693
|
+
case "SetPageSize":
|
|
6694
|
+
return _extends({}, state, {
|
|
6695
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'pageWidthPx' does not exist on type '{ type: string; }'.
|
|
6696
|
+
pageWidthPx: action.pageWidthPx
|
|
6697
|
+
});
|
|
6698
|
+
case "PressKey":
|
|
6699
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
|
|
6700
|
+
const keyConfig = KeyConfigs[action.key];
|
|
6701
|
+
|
|
6702
|
+
// Reset the keypad page if the user performs a math operation.
|
|
6703
|
+
if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
|
|
6704
|
+
return pagerReducer(state, {
|
|
6705
|
+
type: "ResetKeypadPage"
|
|
6716
6706
|
});
|
|
6717
|
-
|
|
6707
|
+
}
|
|
6708
|
+
return state;
|
|
6709
|
+
case "ResetKeypadPage":
|
|
6710
|
+
return _extends({}, state, {
|
|
6711
|
+
animateToPosition: true,
|
|
6712
|
+
// We start at the right-most page.
|
|
6713
|
+
currentPage: getDefaultPage(state.numPages),
|
|
6714
|
+
dx: 0
|
|
6715
|
+
});
|
|
6716
|
+
case "PageKeypadRight":
|
|
6717
|
+
const nextPage = Math.min(state.currentPage + 1, state.numPages - 1);
|
|
6718
|
+
return _extends({}, state, {
|
|
6719
|
+
animateToPosition: true,
|
|
6720
|
+
currentPage: nextPage,
|
|
6721
|
+
dx: 0
|
|
6722
|
+
});
|
|
6723
|
+
case "PageKeypadLeft":
|
|
6724
|
+
const prevPage = Math.max(state.currentPage - 1, 0);
|
|
6725
|
+
return _extends({}, state, {
|
|
6726
|
+
animateToPosition: true,
|
|
6727
|
+
currentPage: prevPage,
|
|
6728
|
+
dx: 0
|
|
6729
|
+
});
|
|
6730
|
+
case "OnSwipeChange":
|
|
6731
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
6732
|
+
state.velocityTracker.push(action.dx);
|
|
6733
|
+
return _extends({}, state, {
|
|
6734
|
+
animateToPosition: false,
|
|
6718
6735
|
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6736
|
+
dx: action.dx
|
|
6737
|
+
});
|
|
6738
|
+
case "OnSwipeEnd":
|
|
6739
|
+
const {
|
|
6740
|
+
pageWidthPx,
|
|
6741
|
+
velocityTracker
|
|
6742
|
+
} = state;
|
|
6743
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
6744
|
+
const {
|
|
6745
|
+
dx
|
|
6746
|
+
} = action;
|
|
6747
|
+
const velocity = velocityTracker.getVelocity();
|
|
6748
|
+
|
|
6749
|
+
// NOTE(charlie): These will need refinement. The velocity comes
|
|
6750
|
+
// from Framer.
|
|
6751
|
+
const minFlingVelocity = 0.1;
|
|
6752
|
+
const minFlingDistance = 10;
|
|
6753
|
+
const shouldPageRight = dx < -pageWidthPx / 2 || velocity < -minFlingVelocity && dx < -minFlingDistance;
|
|
6754
|
+
const shouldPageLeft = dx > pageWidthPx / 2 || velocity > minFlingVelocity && dx > minFlingDistance;
|
|
6755
|
+
if (shouldPageRight) {
|
|
6756
|
+
return pagerReducer(state, {
|
|
6757
|
+
type: "PageKeypadRight"
|
|
6724
6758
|
});
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
velocityTracker
|
|
6729
|
-
} = state;
|
|
6730
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
6731
|
-
const {
|
|
6732
|
-
dx
|
|
6733
|
-
} = action;
|
|
6734
|
-
const velocity = velocityTracker.getVelocity();
|
|
6735
|
-
|
|
6736
|
-
// NOTE(charlie): These will need refinement. The velocity comes
|
|
6737
|
-
// from Framer.
|
|
6738
|
-
const minFlingVelocity = 0.1;
|
|
6739
|
-
const minFlingDistance = 10;
|
|
6740
|
-
const shouldPageRight = dx < -pageWidthPx / 2 || velocity < -minFlingVelocity && dx < -minFlingDistance;
|
|
6741
|
-
const shouldPageLeft = dx > pageWidthPx / 2 || velocity > minFlingVelocity && dx > minFlingDistance;
|
|
6742
|
-
if (shouldPageRight) {
|
|
6743
|
-
return pagerReducer(state, {
|
|
6744
|
-
type: "PageKeypadRight"
|
|
6745
|
-
});
|
|
6746
|
-
} else if (shouldPageLeft) {
|
|
6747
|
-
return pagerReducer(state, {
|
|
6748
|
-
type: "PageKeypadLeft"
|
|
6749
|
-
});
|
|
6750
|
-
}
|
|
6751
|
-
return _extends({}, state, {
|
|
6752
|
-
animateToPosition: true,
|
|
6753
|
-
dx: 0
|
|
6759
|
+
} else if (shouldPageLeft) {
|
|
6760
|
+
return pagerReducer(state, {
|
|
6761
|
+
type: "PageKeypadLeft"
|
|
6754
6762
|
});
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6763
|
+
}
|
|
6764
|
+
return _extends({}, state, {
|
|
6765
|
+
animateToPosition: true,
|
|
6766
|
+
dx: 0
|
|
6767
|
+
});
|
|
6768
|
+
default:
|
|
6769
|
+
return state;
|
|
6770
|
+
}
|
|
6771
|
+
};
|
|
6772
|
+
|
|
6773
|
+
const createStore = () => {
|
|
6774
|
+
// TODO(matthewc)[LC-752]: gestureReducer can't be moved from this file
|
|
6775
|
+
// because it depends on `store` being in scope (see note below)
|
|
6759
6776
|
const createGestureManager = swipeEnabled => {
|
|
6760
6777
|
return new GestureManager({
|
|
6761
6778
|
swipeEnabled
|
|
@@ -6829,136 +6846,6 @@ const createStore = () => {
|
|
|
6829
6846
|
return state;
|
|
6830
6847
|
}
|
|
6831
6848
|
};
|
|
6832
|
-
|
|
6833
|
-
// Used to generate unique animation IDs for the echo animations. The actual
|
|
6834
|
-
// values are irrelevant as long as they are unique.
|
|
6835
|
-
let _lastAnimationId = 0;
|
|
6836
|
-
const initialEchoState = {
|
|
6837
|
-
echoes: []
|
|
6838
|
-
};
|
|
6839
|
-
const echoReducer = function echoReducer(state = initialEchoState, action) {
|
|
6840
|
-
switch (action.type) {
|
|
6841
|
-
case "PressKey":
|
|
6842
|
-
const keyConfig = KeyConfigs[action.key];
|
|
6843
|
-
|
|
6844
|
-
// Add in the echo animation if the user performs a math
|
|
6845
|
-
// operation.
|
|
6846
|
-
if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
|
|
6847
|
-
return _extends({}, state, {
|
|
6848
|
-
echoes: [...state.echoes, {
|
|
6849
|
-
animationId: "" + _lastAnimationId++,
|
|
6850
|
-
animationType: action.inPopover ? EchoAnimationTypes.LONG_FADE_ONLY : EchoAnimationTypes.FADE_ONLY,
|
|
6851
|
-
borders: action.borders,
|
|
6852
|
-
id: keyConfig.id,
|
|
6853
|
-
initialBounds: action.initialBounds
|
|
6854
|
-
}]
|
|
6855
|
-
});
|
|
6856
|
-
}
|
|
6857
|
-
return state;
|
|
6858
|
-
case "RemoveEcho":
|
|
6859
|
-
const remainingEchoes = state.echoes.filter(echo => {
|
|
6860
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'animationId' does not exist on type 'never'.
|
|
6861
|
-
return echo.animationId !== action.animationId;
|
|
6862
|
-
});
|
|
6863
|
-
return _extends({}, state, {
|
|
6864
|
-
echoes: remainingEchoes
|
|
6865
|
-
});
|
|
6866
|
-
default:
|
|
6867
|
-
return state;
|
|
6868
|
-
}
|
|
6869
|
-
};
|
|
6870
|
-
const initialLayoutState = {
|
|
6871
|
-
gridDimensions: {
|
|
6872
|
-
numRows: keypadForType[defaultKeypadType].rows,
|
|
6873
|
-
numColumns: keypadForType[defaultKeypadType].columns,
|
|
6874
|
-
numMaxVisibleRows: keypadForType[defaultKeypadType].maxVisibleRows,
|
|
6875
|
-
numPages: keypadForType[defaultKeypadType].numPages
|
|
6876
|
-
},
|
|
6877
|
-
buttonDimensions: {
|
|
6878
|
-
widthPx: 48,
|
|
6879
|
-
heightPx: 48
|
|
6880
|
-
},
|
|
6881
|
-
pageDimensions: {
|
|
6882
|
-
pageWidthPx: 0,
|
|
6883
|
-
pageHeightPx: 0
|
|
6884
|
-
},
|
|
6885
|
-
layoutMode: LayoutModes.FULLSCREEN,
|
|
6886
|
-
paginationEnabled: false,
|
|
6887
|
-
navigationPadEnabled: false
|
|
6888
|
-
};
|
|
6889
|
-
|
|
6890
|
-
/**
|
|
6891
|
-
* Compute the additional layout state based on the provided page and grid
|
|
6892
|
-
* dimensions.
|
|
6893
|
-
*/
|
|
6894
|
-
const layoutParametersForDimensions = (pageDimensions, gridDimensions) => {
|
|
6895
|
-
const {
|
|
6896
|
-
pageWidthPx,
|
|
6897
|
-
pageHeightPx
|
|
6898
|
-
} = pageDimensions;
|
|
6899
|
-
|
|
6900
|
-
// Determine the device type and orientation.
|
|
6901
|
-
const deviceOrientation = pageWidthPx > pageHeightPx ? DeviceOrientations.LANDSCAPE : DeviceOrientations.PORTRAIT;
|
|
6902
|
-
const deviceType = Math.min(pageWidthPx, pageHeightPx) > tabletCutoffPx ? DeviceTypes.TABLET : DeviceTypes.PHONE;
|
|
6903
|
-
|
|
6904
|
-
// Using that information, make some decisions (or assumptions)
|
|
6905
|
-
// about the resulting layout.
|
|
6906
|
-
const navigationPadEnabled = deviceType === DeviceTypes.TABLET;
|
|
6907
|
-
const paginationEnabled = deviceType === DeviceTypes.PHONE && deviceOrientation === DeviceOrientations.PORTRAIT;
|
|
6908
|
-
const deviceInfo = {
|
|
6909
|
-
deviceOrientation,
|
|
6910
|
-
deviceType
|
|
6911
|
-
};
|
|
6912
|
-
const layoutOptions = {
|
|
6913
|
-
navigationPadEnabled,
|
|
6914
|
-
paginationEnabled,
|
|
6915
|
-
// HACK(charlie): It's not great that we're making assumptions about
|
|
6916
|
-
// the toolbar (which is rendered by webapp, and should always be
|
|
6917
|
-
// visible and anchored to the bottom of the page for phone and
|
|
6918
|
-
// tablet exercises). But this is primarily a heuristic (the goal is
|
|
6919
|
-
// to preserve a 'good' amount of space between the top of the
|
|
6920
|
-
// keypad and the top of the page) so we afford to have some margin
|
|
6921
|
-
// of error.
|
|
6922
|
-
toolbarEnabled: true
|
|
6923
|
-
};
|
|
6924
|
-
return _extends({}, computeLayoutParameters(gridDimensions, pageDimensions, deviceInfo, layoutOptions), {
|
|
6925
|
-
// Pass along some of the layout information, so that other
|
|
6926
|
-
// components in the heirarchy can adapt appropriately.
|
|
6927
|
-
navigationPadEnabled,
|
|
6928
|
-
paginationEnabled
|
|
6929
|
-
});
|
|
6930
|
-
};
|
|
6931
|
-
const layoutReducer = function layoutReducer(state = initialLayoutState, action) {
|
|
6932
|
-
switch (action.type) {
|
|
6933
|
-
case "ConfigureKeypad":
|
|
6934
|
-
const {
|
|
6935
|
-
keypadType
|
|
6936
|
-
} = action.configuration;
|
|
6937
|
-
const gridDimensions = {
|
|
6938
|
-
numRows: keypadForType[keypadType].rows,
|
|
6939
|
-
numColumns: keypadForType[keypadType].columns,
|
|
6940
|
-
numMaxVisibleRows: keypadForType[keypadType].maxVisibleRows,
|
|
6941
|
-
numPages: keypadForType[keypadType].numPages
|
|
6942
|
-
};
|
|
6943
|
-
return _extends({}, state, layoutParametersForDimensions(state.pageDimensions, gridDimensions), {
|
|
6944
|
-
gridDimensions
|
|
6945
|
-
});
|
|
6946
|
-
case "SetPageSize":
|
|
6947
|
-
const {
|
|
6948
|
-
pageWidthPx,
|
|
6949
|
-
pageHeightPx
|
|
6950
|
-
} = action;
|
|
6951
|
-
const pageDimensions = {
|
|
6952
|
-
pageWidthPx,
|
|
6953
|
-
pageHeightPx
|
|
6954
|
-
};
|
|
6955
|
-
return _extends({}, state, layoutParametersForDimensions(pageDimensions, state.gridDimensions), {
|
|
6956
|
-
pageDimensions
|
|
6957
|
-
});
|
|
6958
|
-
default:
|
|
6959
|
-
return state;
|
|
6960
|
-
}
|
|
6961
|
-
};
|
|
6962
6849
|
const reducer = Redux.combineReducers({
|
|
6963
6850
|
input: inputReducer,
|
|
6964
6851
|
keypad: keypadReducer,
|
|
@@ -7025,10 +6912,6 @@ class NavigationPad extends React.Component {
|
|
|
7025
6912
|
})));
|
|
7026
6913
|
}
|
|
7027
6914
|
}
|
|
7028
|
-
NavigationPad.propTypes = {
|
|
7029
|
-
roundTopLeft: PropTypes.bool,
|
|
7030
|
-
style: PropTypes.any
|
|
7031
|
-
};
|
|
7032
6915
|
const buttonSizePx = 48;
|
|
7033
6916
|
const borderRadiusPx = 4;
|
|
7034
6917
|
const borderWidthPx$1 = 1;
|
|
@@ -7087,11 +6970,12 @@ const {
|
|
|
7087
6970
|
centered,
|
|
7088
6971
|
fullWidth
|
|
7089
6972
|
} = Styles;
|
|
7090
|
-
|
|
7091
6973
|
// eslint-disable-next-line react/no-unsafe
|
|
7092
6974
|
class KeypadContainer extends React.Component {
|
|
7093
6975
|
constructor(...args) {
|
|
7094
6976
|
super(...args);
|
|
6977
|
+
this._resizeTimeout = void 0;
|
|
6978
|
+
this.hasMounted = void 0;
|
|
7095
6979
|
this.state = {
|
|
7096
6980
|
hasBeenActivated: false,
|
|
7097
6981
|
viewportWidth: "100vw"
|
|
@@ -7100,19 +6984,20 @@ class KeypadContainer extends React.Component {
|
|
|
7100
6984
|
// Throttle the resize callbacks.
|
|
7101
6985
|
// https://developer.mozilla.org/en-US/docs/Web/Events/resize
|
|
7102
6986
|
if (this._resizeTimeout == null) {
|
|
7103
|
-
this._resizeTimeout = setTimeout(() => {
|
|
6987
|
+
this._resizeTimeout = window.setTimeout(() => {
|
|
7104
6988
|
this._resizeTimeout = null;
|
|
7105
6989
|
this._onResize();
|
|
7106
6990
|
}, 66);
|
|
7107
6991
|
}
|
|
7108
6992
|
};
|
|
7109
6993
|
this._onResize = () => {
|
|
6994
|
+
var _this$props$onPageSiz, _this$props;
|
|
7110
6995
|
// Whenever the page resizes, we need to force an update, as the button
|
|
7111
6996
|
// heights and keypad width are computed based on horizontal space.
|
|
7112
6997
|
this.setState({
|
|
7113
6998
|
viewportWidth: window.innerWidth
|
|
7114
6999
|
});
|
|
7115
|
-
this.props.onPageSizeChange(window.innerWidth, window.innerHeight);
|
|
7000
|
+
(_this$props$onPageSiz = (_this$props = this.props).onPageSizeChange) == null ? void 0 : _this$props$onPageSiz.call(_this$props, window.innerWidth, window.innerHeight);
|
|
7116
7001
|
};
|
|
7117
7002
|
this.renderKeypad = () => {
|
|
7118
7003
|
const {
|
|
@@ -7194,7 +7079,10 @@ class KeypadContainer extends React.Component {
|
|
|
7194
7079
|
// NOTE(charlie): We render the transforms as pure inline styles to
|
|
7195
7080
|
// avoid an Aphrodite bug in mobile Safari.
|
|
7196
7081
|
// See: https://github.com/Khan/aphrodite/issues/68.
|
|
7197
|
-
|
|
7082
|
+
let dynamicStyle = _extends({}, active ? inlineStyles.active : inlineStyles.hidden);
|
|
7083
|
+
if (!active && !hasBeenActivated) {
|
|
7084
|
+
dynamicStyle = _extends({}, dynamicStyle, inlineStyles.invisible);
|
|
7085
|
+
}
|
|
7198
7086
|
const keypadContainerStyle = [row, centered, fullWidth, styles.keypadContainer, ...(Array.isArray(style) ? style : [style])];
|
|
7199
7087
|
const keypadStyle = [row, styles.keypadBorder, layoutMode === LayoutModes.FULLSCREEN ? styles.fullscreen : styles.compact];
|
|
7200
7088
|
|
|
@@ -7221,19 +7109,6 @@ class KeypadContainer extends React.Component {
|
|
|
7221
7109
|
}, this.renderKeypad())));
|
|
7222
7110
|
}
|
|
7223
7111
|
}
|
|
7224
|
-
KeypadContainer.propTypes = {
|
|
7225
|
-
active: PropTypes.bool,
|
|
7226
|
-
extraKeys: PropTypes.arrayOf(keyIdPropType),
|
|
7227
|
-
keypadType: PropTypes.oneOf(Object.keys(KeypadTypes)).isRequired,
|
|
7228
|
-
layoutMode: PropTypes.oneOf(Object.keys(LayoutModes)).isRequired,
|
|
7229
|
-
navigationPadEnabled: PropTypes.bool.isRequired,
|
|
7230
|
-
onDismiss: PropTypes.func,
|
|
7231
|
-
// A callback that should be triggered with the root React element on
|
|
7232
|
-
// mount.
|
|
7233
|
-
onElementMounted: PropTypes.func,
|
|
7234
|
-
onPageSizeChange: PropTypes.func.isRequired,
|
|
7235
|
-
style: PropTypes.any
|
|
7236
|
-
};
|
|
7237
7112
|
const keypadAnimationDurationMs = 300;
|
|
7238
7113
|
const borderWidthPx = 1;
|
|
7239
7114
|
const styles = StyleSheet.create({
|
|
@@ -7293,10 +7168,13 @@ const inlineStyles = {
|
|
|
7293
7168
|
}
|
|
7294
7169
|
};
|
|
7295
7170
|
const mapStateToProps = state => {
|
|
7296
|
-
return
|
|
7171
|
+
return {
|
|
7172
|
+
extraKeys: state.keypad.extraKeys,
|
|
7173
|
+
keypadType: state.keypad.keypadType,
|
|
7174
|
+
active: state.keypad.active,
|
|
7297
7175
|
layoutMode: state.layout.layoutMode,
|
|
7298
7176
|
navigationPadEnabled: state.layout.navigationPadEnabled
|
|
7299
|
-
}
|
|
7177
|
+
};
|
|
7300
7178
|
};
|
|
7301
7179
|
const mapDispatchToProps = dispatch => {
|
|
7302
7180
|
return {
|
|
@@ -7309,7 +7187,6 @@ var KeypadContainer$1 = connect(mapStateToProps, mapDispatchToProps, null, {
|
|
|
7309
7187
|
forwardRef: true
|
|
7310
7188
|
})(KeypadContainer);
|
|
7311
7189
|
|
|
7312
|
-
const _excluded = ["onElementMounted"];
|
|
7313
7190
|
class ProvidedKeypad extends React.Component {
|
|
7314
7191
|
constructor(...args) {
|
|
7315
7192
|
super(...args);
|
|
@@ -7353,14 +7230,14 @@ class ProvidedKeypad extends React.Component {
|
|
|
7353
7230
|
this.mounted = false;
|
|
7354
7231
|
}
|
|
7355
7232
|
render() {
|
|
7356
|
-
const
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7233
|
+
const {
|
|
7234
|
+
onElementMounted,
|
|
7235
|
+
onDismiss,
|
|
7236
|
+
style
|
|
7237
|
+
} = this.props;
|
|
7361
7238
|
return /*#__PURE__*/React.createElement(Provider, {
|
|
7362
7239
|
store: this.store
|
|
7363
|
-
}, /*#__PURE__*/React.createElement(KeypadContainer$1,
|
|
7240
|
+
}, /*#__PURE__*/React.createElement(KeypadContainer$1, {
|
|
7364
7241
|
onElementMounted: element => {
|
|
7365
7242
|
// Append the dispatch methods that we want to expose
|
|
7366
7243
|
// externally to the returned React element.
|
|
@@ -7373,10 +7250,12 @@ class ProvidedKeypad extends React.Component {
|
|
|
7373
7250
|
getDOMNode: this.getDOMNode
|
|
7374
7251
|
});
|
|
7375
7252
|
onElementMounted && onElementMounted(elementWithDispatchMethods);
|
|
7376
|
-
}
|
|
7377
|
-
|
|
7253
|
+
},
|
|
7254
|
+
onDismiss: onDismiss,
|
|
7255
|
+
style: style
|
|
7256
|
+
}));
|
|
7378
7257
|
}
|
|
7379
7258
|
}
|
|
7380
7259
|
|
|
7381
|
-
export { CursorContexts, KeyConfigs, ProvidedKeypad as Keypad, MathInput as KeypadInput, KeypadTypes,
|
|
7260
|
+
export { cursorContexts as CursorContexts, KeyConfigs, ProvidedKeypad as Keypad, MathInput as KeypadInput, KeypadTypes, keypadElementPropType };
|
|
7382
7261
|
//# sourceMappingURL=index.js.map
|