@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/index.js
CHANGED
|
@@ -391,6 +391,9 @@ _defineProperty(CursorHandle, "defaultProps", {
|
|
|
391
391
|
const touchSlopPx = 8;
|
|
392
392
|
class DragListener {
|
|
393
393
|
constructor(onDrag, initialEvent) {
|
|
394
|
+
_defineProperty(this, "_scrollListener", void 0);
|
|
395
|
+
_defineProperty(this, "_moveListener", void 0);
|
|
396
|
+
_defineProperty(this, "_endAndCancelListener", void 0);
|
|
394
397
|
// We detect drags in two ways. First, by listening for the window
|
|
395
398
|
// scroll event (we consider any legitimate scroll to be a drag).
|
|
396
399
|
this._scrollListener = () => {
|
|
@@ -549,7 +552,7 @@ const IN_DENOMINATOR = "IN_DENOMINATOR";
|
|
|
549
552
|
// write is non-leaf math (numbers and variables).
|
|
550
553
|
const BEFORE_FRACTION = "BEFORE_FRACTION";
|
|
551
554
|
|
|
552
|
-
var
|
|
555
|
+
var cursorContexts = /*#__PURE__*/Object.freeze({
|
|
553
556
|
__proto__: null,
|
|
554
557
|
NONE: NONE,
|
|
555
558
|
IN_PARENS: IN_PARENS,
|
|
@@ -560,12 +563,6 @@ var CursorContexts = /*#__PURE__*/Object.freeze({
|
|
|
560
563
|
BEFORE_FRACTION: BEFORE_FRACTION
|
|
561
564
|
});
|
|
562
565
|
|
|
563
|
-
/**
|
|
564
|
-
* This file contains a wrapper around MathQuill so that we can provide a
|
|
565
|
-
* more regular interface for the functionality we need while insulating us
|
|
566
|
-
* from MathQuill changes.
|
|
567
|
-
*/
|
|
568
|
-
|
|
569
566
|
// Keeping `window` in place for test suite and GitHub Pages.
|
|
570
567
|
// If it does not exist, fall back to CommonJS require. - jsatk
|
|
571
568
|
|
|
@@ -700,8 +697,14 @@ const KeysForJumpContext = {
|
|
|
700
697
|
[IN_DENOMINATOR]: Keys.JUMP_OUT_DENOMINATOR
|
|
701
698
|
};
|
|
702
699
|
class MathWrapper {
|
|
700
|
+
// MathQuill interface
|
|
701
|
+
// MathQuill input
|
|
702
|
+
|
|
703
703
|
constructor(element) {
|
|
704
704
|
let callbacks = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
705
|
+
_defineProperty(this, "MQ", void 0);
|
|
706
|
+
_defineProperty(this, "mathField", void 0);
|
|
707
|
+
_defineProperty(this, "callbacks", void 0);
|
|
705
708
|
this.MQ = MathQuill__default["default"].getInterface(2);
|
|
706
709
|
this.mathField = this.MQ.MathField(element, {
|
|
707
710
|
// use a span instead of a textarea so that we don't bring up the
|
|
@@ -780,7 +783,7 @@ class MathWrapper {
|
|
|
780
783
|
this._handleBackspace(cursor);
|
|
781
784
|
} else if (key === Keys.LEFT) {
|
|
782
785
|
this._handleLeftArrow(cursor);
|
|
783
|
-
} else if (key === Keys.RIGHT
|
|
786
|
+
} else if (key === Keys.RIGHT) {
|
|
784
787
|
this._handleRightArrow(cursor);
|
|
785
788
|
} else if (/^[a-zA-Z]$/.test(key)) {
|
|
786
789
|
this.mathField[WRITE](key);
|
|
@@ -1279,7 +1282,7 @@ class MathWrapper {
|
|
|
1279
1282
|
if (this._isInsideEmptyNode(cursor)) {
|
|
1280
1283
|
const grandparent = cursor.parent.parent;
|
|
1281
1284
|
const command = this._maybeFindCommandBeforeParens(grandparent);
|
|
1282
|
-
cursor.insLeftOf(command.startNode);
|
|
1285
|
+
cursor.insLeftOf(command === null || command === void 0 ? void 0 : command.startNode);
|
|
1283
1286
|
cursor.startSelection();
|
|
1284
1287
|
if (grandparent[this.MQ.R] !== MQ_END) {
|
|
1285
1288
|
cursor.insRightOf(grandparent[this.MQ.R]);
|
|
@@ -1443,18 +1446,6 @@ class MathWrapper {
|
|
|
1443
1446
|
* TODO(charlie): Move this scroll logic out of our components and into a higher
|
|
1444
1447
|
* level in the component tree--perhaps even into webapp, beyond Perseus.
|
|
1445
1448
|
*/
|
|
1446
|
-
|
|
1447
|
-
// Taken from https://dev.opera.com/articles/fixing-the-scrolltop-bug/
|
|
1448
|
-
function bodyOrHtml() {
|
|
1449
|
-
if ("scrollingElement" in document) {
|
|
1450
|
-
return document.scrollingElement;
|
|
1451
|
-
}
|
|
1452
|
-
// Fallback for legacy browsers
|
|
1453
|
-
if (navigator.userAgent.indexOf("WebKit") !== -1) {
|
|
1454
|
-
return document.body;
|
|
1455
|
-
}
|
|
1456
|
-
return document.documentElement;
|
|
1457
|
-
}
|
|
1458
1449
|
const scrollIntoView = (containerNode, keypadNode) => {
|
|
1459
1450
|
// TODO(charlie): There's no need for us to be reading the keypad bounds
|
|
1460
1451
|
// here, since they're pre-determined by logic in the store. We should
|
|
@@ -1464,7 +1455,7 @@ const scrollIntoView = (containerNode, keypadNode) => {
|
|
|
1464
1455
|
const containerTopPx = containerBounds.top;
|
|
1465
1456
|
|
|
1466
1457
|
// Get the element that scrolls the document.
|
|
1467
|
-
const scrollNode =
|
|
1458
|
+
const scrollNode = document.scrollingElement;
|
|
1468
1459
|
const desiredMarginPx = 16;
|
|
1469
1460
|
if (keypadNode) {
|
|
1470
1461
|
// NOTE(charlie): We can't use the bounding rect of the keypad,
|
|
@@ -1483,7 +1474,9 @@ const scrollIntoView = (containerNode, keypadNode) => {
|
|
|
1483
1474
|
// the bottom of the input is just above the top of the keypad,
|
|
1484
1475
|
// taking care not to scroll the input out of view.
|
|
1485
1476
|
const scrollOffset = Math.min(containerBottomPx - keypadTopPx + desiredMarginPx, containerTopPx);
|
|
1486
|
-
scrollNode
|
|
1477
|
+
if (scrollNode) {
|
|
1478
|
+
scrollNode.scrollTop += scrollOffset;
|
|
1479
|
+
}
|
|
1487
1480
|
return;
|
|
1488
1481
|
}
|
|
1489
1482
|
}
|
|
@@ -1491,7 +1484,7 @@ const scrollIntoView = (containerNode, keypadNode) => {
|
|
|
1491
1484
|
// Alternatively, if the input is out of the viewport or nearly out
|
|
1492
1485
|
// of the viewport, scroll it into view. We can do this regardless
|
|
1493
1486
|
// of whether the keypad has been provided.
|
|
1494
|
-
if (containerTopPx < desiredMarginPx) {
|
|
1487
|
+
if (scrollNode && containerTopPx < desiredMarginPx) {
|
|
1495
1488
|
scrollNode.scrollTop -= containerBounds.height + desiredMarginPx;
|
|
1496
1489
|
}
|
|
1497
1490
|
};
|
|
@@ -2199,323 +2192,11 @@ const inlineStyles$1 = {
|
|
|
2199
2192
|
};
|
|
2200
2193
|
|
|
2201
2194
|
/**
|
|
2202
|
-
*
|
|
2195
|
+
* React PropTypes that may be shared between components.
|
|
2203
2196
|
*/
|
|
2204
|
-
const KeyConfigs = {
|
|
2205
|
-
// Basic math keys.
|
|
2206
|
-
[Keys.PLUS]: {
|
|
2207
|
-
type: KeyTypes.OPERATOR,
|
|
2208
|
-
// I18N: A label for a plus sign.
|
|
2209
|
-
ariaLabel: i18n__namespace._("Plus")
|
|
2210
|
-
},
|
|
2211
|
-
[Keys.MINUS]: {
|
|
2212
|
-
type: KeyTypes.OPERATOR,
|
|
2213
|
-
// I18N: A label for a minus sign.
|
|
2214
|
-
ariaLabel: i18n__namespace._("Minus")
|
|
2215
|
-
},
|
|
2216
|
-
[Keys.NEGATIVE]: {
|
|
2217
|
-
type: KeyTypes.VALUE,
|
|
2218
|
-
// I18N: A label for a minus sign.
|
|
2219
|
-
ariaLabel: i18n__namespace._("Negative")
|
|
2220
|
-
},
|
|
2221
|
-
[Keys.TIMES]: {
|
|
2222
|
-
type: KeyTypes.OPERATOR,
|
|
2223
|
-
// I18N: A label for a multiplication sign (represented with an 'x').
|
|
2224
|
-
ariaLabel: i18n__namespace._("Multiply")
|
|
2225
|
-
},
|
|
2226
|
-
[Keys.DIVIDE]: {
|
|
2227
|
-
type: KeyTypes.OPERATOR,
|
|
2228
|
-
// I18N: A label for a division sign.
|
|
2229
|
-
ariaLabel: i18n__namespace._("Divide")
|
|
2230
|
-
},
|
|
2231
|
-
[Keys.DECIMAL]: {
|
|
2232
|
-
type: KeyTypes.VALUE,
|
|
2233
|
-
// I18N: A label for a decimal symbol.
|
|
2234
|
-
ariaLabel: i18n__namespace._("Decimal"),
|
|
2235
|
-
icon: decimalSeparator === DecimalSeparators.COMMA ? {
|
|
2236
|
-
// TODO(charlie): Get an SVG icon for the comma, or verify with
|
|
2237
|
-
// design that the text-rendered version is acceptable.
|
|
2238
|
-
type: IconTypes.TEXT,
|
|
2239
|
-
data: ","
|
|
2240
|
-
} : {
|
|
2241
|
-
type: IconTypes.SVG,
|
|
2242
|
-
data: Keys.PERIOD
|
|
2243
|
-
}
|
|
2244
|
-
},
|
|
2245
|
-
[Keys.PERCENT]: {
|
|
2246
|
-
type: KeyTypes.OPERATOR,
|
|
2247
|
-
// I18N: A label for a percent sign.
|
|
2248
|
-
ariaLabel: i18n__namespace._("Percent")
|
|
2249
|
-
},
|
|
2250
|
-
[Keys.CDOT]: {
|
|
2251
|
-
type: KeyTypes.OPERATOR,
|
|
2252
|
-
// I18N: A label for a multiplication sign (represented as a dot).
|
|
2253
|
-
ariaLabel: i18n__namespace._("Multiply")
|
|
2254
|
-
},
|
|
2255
|
-
[Keys.EQUAL]: {
|
|
2256
|
-
type: KeyTypes.OPERATOR,
|
|
2257
|
-
ariaLabel: i18n__namespace._("Equals sign")
|
|
2258
|
-
},
|
|
2259
|
-
[Keys.NEQ]: {
|
|
2260
|
-
type: KeyTypes.OPERATOR,
|
|
2261
|
-
ariaLabel: i18n__namespace._("Not-equals sign")
|
|
2262
|
-
},
|
|
2263
|
-
[Keys.GT]: {
|
|
2264
|
-
type: KeyTypes.OPERATOR,
|
|
2265
|
-
// I18N: A label for a 'greater than' sign (represented as '>').
|
|
2266
|
-
ariaLabel: i18n__namespace._("Greater than sign")
|
|
2267
|
-
},
|
|
2268
|
-
[Keys.LT]: {
|
|
2269
|
-
type: KeyTypes.OPERATOR,
|
|
2270
|
-
// I18N: A label for a 'less than' sign (represented as '<').
|
|
2271
|
-
ariaLabel: i18n__namespace._("Less than sign")
|
|
2272
|
-
},
|
|
2273
|
-
[Keys.GEQ]: {
|
|
2274
|
-
type: KeyTypes.OPERATOR,
|
|
2275
|
-
ariaLabel: i18n__namespace._("Greater than or equal to sign")
|
|
2276
|
-
},
|
|
2277
|
-
[Keys.LEQ]: {
|
|
2278
|
-
type: KeyTypes.OPERATOR,
|
|
2279
|
-
ariaLabel: i18n__namespace._("Less than or equal to sign")
|
|
2280
|
-
},
|
|
2281
|
-
// mobile native
|
|
2282
|
-
[Keys.FRAC_INCLUSIVE]: {
|
|
2283
|
-
type: KeyTypes.OPERATOR,
|
|
2284
|
-
// I18N: A label for a button that creates a new fraction and puts the
|
|
2285
|
-
// current expression in the numerator of that fraction.
|
|
2286
|
-
ariaLabel: i18n__namespace._("Fraction, with current expression in numerator")
|
|
2287
|
-
},
|
|
2288
|
-
// mobile native
|
|
2289
|
-
[Keys.FRAC_EXCLUSIVE]: {
|
|
2290
|
-
type: KeyTypes.OPERATOR,
|
|
2291
|
-
// I18N: A label for a button that creates a new fraction next to the
|
|
2292
|
-
// cursor.
|
|
2293
|
-
ariaLabel: i18n__namespace._("Fraction, excluding the current expression")
|
|
2294
|
-
},
|
|
2295
|
-
// mobile web
|
|
2296
|
-
[Keys.FRAC]: {
|
|
2297
|
-
type: KeyTypes.OPERATOR,
|
|
2298
|
-
// I18N: A label for a button that creates a new fraction next to the
|
|
2299
|
-
// cursor.
|
|
2300
|
-
ariaLabel: i18n__namespace._("Fraction, excluding the current expression")
|
|
2301
|
-
},
|
|
2302
|
-
[Keys.EXP]: {
|
|
2303
|
-
type: KeyTypes.OPERATOR,
|
|
2304
|
-
// I18N: A label for a button that will allow the user to input a custom
|
|
2305
|
-
// exponent.
|
|
2306
|
-
ariaLabel: i18n__namespace._("Custom exponent")
|
|
2307
|
-
},
|
|
2308
|
-
[Keys.EXP_2]: {
|
|
2309
|
-
type: KeyTypes.OPERATOR,
|
|
2310
|
-
// I18N: A label for a button that will square (take to the second
|
|
2311
|
-
// power) some math.
|
|
2312
|
-
ariaLabel: i18n__namespace._("Square")
|
|
2313
|
-
},
|
|
2314
|
-
[Keys.EXP_3]: {
|
|
2315
|
-
type: KeyTypes.OPERATOR,
|
|
2316
|
-
// I18N: A label for a button that will cube (take to the third power)
|
|
2317
|
-
// some math.
|
|
2318
|
-
ariaLabel: i18n__namespace._("Cube")
|
|
2319
|
-
},
|
|
2320
|
-
[Keys.SQRT]: {
|
|
2321
|
-
type: KeyTypes.OPERATOR,
|
|
2322
|
-
ariaLabel: i18n__namespace._("Square root")
|
|
2323
|
-
},
|
|
2324
|
-
[Keys.CUBE_ROOT]: {
|
|
2325
|
-
type: KeyTypes.OPERATOR,
|
|
2326
|
-
ariaLabel: i18n__namespace._("Cube root")
|
|
2327
|
-
},
|
|
2328
|
-
[Keys.RADICAL]: {
|
|
2329
|
-
type: KeyTypes.OPERATOR,
|
|
2330
|
-
ariaLabel: i18n__namespace._("Radical with custom root")
|
|
2331
|
-
},
|
|
2332
|
-
[Keys.LEFT_PAREN]: {
|
|
2333
|
-
type: KeyTypes.OPERATOR,
|
|
2334
|
-
ariaLabel: i18n__namespace._("Left parenthesis")
|
|
2335
|
-
},
|
|
2336
|
-
[Keys.RIGHT_PAREN]: {
|
|
2337
|
-
type: KeyTypes.OPERATOR,
|
|
2338
|
-
ariaLabel: i18n__namespace._("Right parenthesis")
|
|
2339
|
-
},
|
|
2340
|
-
[Keys.LN]: {
|
|
2341
|
-
type: KeyTypes.OPERATOR,
|
|
2342
|
-
ariaLabel: i18n__namespace._("Natural logarithm")
|
|
2343
|
-
},
|
|
2344
|
-
[Keys.LOG]: {
|
|
2345
|
-
type: KeyTypes.OPERATOR,
|
|
2346
|
-
ariaLabel: i18n__namespace._("Logarithm with base 10")
|
|
2347
|
-
},
|
|
2348
|
-
[Keys.LOG_N]: {
|
|
2349
|
-
type: KeyTypes.OPERATOR,
|
|
2350
|
-
ariaLabel: i18n__namespace._("Logarithm with custom base")
|
|
2351
|
-
},
|
|
2352
|
-
[Keys.SIN]: {
|
|
2353
|
-
type: KeyTypes.OPERATOR,
|
|
2354
|
-
ariaLabel: i18n__namespace._("Sine")
|
|
2355
|
-
},
|
|
2356
|
-
[Keys.COS]: {
|
|
2357
|
-
type: KeyTypes.OPERATOR,
|
|
2358
|
-
ariaLabel: i18n__namespace._("Cosine")
|
|
2359
|
-
},
|
|
2360
|
-
[Keys.TAN]: {
|
|
2361
|
-
type: KeyTypes.OPERATOR,
|
|
2362
|
-
ariaLabel: i18n__namespace._("Tangent")
|
|
2363
|
-
},
|
|
2364
|
-
[Keys.PI]: {
|
|
2365
|
-
type: KeyTypes.VALUE,
|
|
2366
|
-
ariaLabel: i18n__namespace._("Pi"),
|
|
2367
|
-
icon: {
|
|
2368
|
-
type: IconTypes.MATH,
|
|
2369
|
-
data: "\\pi"
|
|
2370
|
-
}
|
|
2371
|
-
},
|
|
2372
|
-
[Keys.THETA]: {
|
|
2373
|
-
type: KeyTypes.VALUE,
|
|
2374
|
-
ariaLabel: i18n__namespace._("Theta"),
|
|
2375
|
-
icon: {
|
|
2376
|
-
type: IconTypes.MATH,
|
|
2377
|
-
data: "\\theta"
|
|
2378
|
-
}
|
|
2379
|
-
},
|
|
2380
|
-
[Keys.NOOP]: {
|
|
2381
|
-
type: KeyTypes.EMPTY
|
|
2382
|
-
},
|
|
2383
|
-
// Input navigation keys.
|
|
2384
|
-
[Keys.UP]: {
|
|
2385
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2386
|
-
ariaLabel: i18n__namespace._("Up arrow")
|
|
2387
|
-
},
|
|
2388
|
-
[Keys.RIGHT]: {
|
|
2389
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2390
|
-
ariaLabel: i18n__namespace._("Right arrow")
|
|
2391
|
-
},
|
|
2392
|
-
[Keys.DOWN]: {
|
|
2393
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2394
|
-
ariaLabel: i18n__namespace._("Down arrow")
|
|
2395
|
-
},
|
|
2396
|
-
[Keys.LEFT]: {
|
|
2397
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2398
|
-
ariaLabel: i18n__namespace._("Left arrow")
|
|
2399
|
-
},
|
|
2400
|
-
[Keys.JUMP_OUT_PARENTHESES]: {
|
|
2401
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2402
|
-
ariaLabel: i18n__namespace._("Navigate right out of a set of parentheses")
|
|
2403
|
-
},
|
|
2404
|
-
[Keys.JUMP_OUT_EXPONENT]: {
|
|
2405
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2406
|
-
ariaLabel: i18n__namespace._("Navigate right out of an exponent")
|
|
2407
|
-
},
|
|
2408
|
-
[Keys.JUMP_OUT_BASE]: {
|
|
2409
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2410
|
-
ariaLabel: i18n__namespace._("Navigate right out of a base")
|
|
2411
|
-
},
|
|
2412
|
-
[Keys.JUMP_INTO_NUMERATOR]: {
|
|
2413
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2414
|
-
ariaLabel: i18n__namespace._("Navigate right into the numerator of a fraction")
|
|
2415
|
-
},
|
|
2416
|
-
[Keys.JUMP_OUT_NUMERATOR]: {
|
|
2417
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2418
|
-
ariaLabel: i18n__namespace._("Navigate right out of the numerator and into the denominator")
|
|
2419
|
-
},
|
|
2420
|
-
[Keys.JUMP_OUT_DENOMINATOR]: {
|
|
2421
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2422
|
-
ariaLabel: i18n__namespace._("Navigate right out of the denominator of a fraction")
|
|
2423
|
-
},
|
|
2424
|
-
[Keys.BACKSPACE]: {
|
|
2425
|
-
type: KeyTypes.INPUT_NAVIGATION,
|
|
2426
|
-
// I18N: A label for a button that will delete some input.
|
|
2427
|
-
ariaLabel: i18n__namespace._("Delete")
|
|
2428
|
-
},
|
|
2429
|
-
// Keypad navigation keys.
|
|
2430
|
-
[Keys.DISMISS]: {
|
|
2431
|
-
type: KeyTypes.KEYPAD_NAVIGATION,
|
|
2432
|
-
// I18N: A label for a button that will dismiss/hide a keypad.
|
|
2433
|
-
ariaLabel: i18n__namespace._("Dismiss")
|
|
2434
|
-
}
|
|
2435
|
-
};
|
|
2436
|
-
|
|
2437
|
-
// Add in any multi-function buttons. By default, these keys will mix in any
|
|
2438
|
-
// configuration settings from their default child key (i.e., the first key in
|
|
2439
|
-
// the `childKeyIds` array).
|
|
2440
|
-
// TODO(charlie): Make the multi-function button's long-press interaction
|
|
2441
|
-
// accessible.
|
|
2442
|
-
// NOTE(kevinb): This is only used in the mobile native app.
|
|
2443
|
-
KeyConfigs[Keys.FRAC_MULTI] = {
|
|
2444
|
-
childKeyIds: [Keys.FRAC_INCLUSIVE, Keys.FRAC_EXCLUSIVE]
|
|
2445
|
-
};
|
|
2446
|
-
|
|
2447
|
-
// TODO(charlie): Use the numeral color for the 'Many' key.
|
|
2448
|
-
KeyConfigs[Keys.MANY] = {
|
|
2449
|
-
type: KeyTypes.MANY
|
|
2450
|
-
// childKeyIds will be configured by the client.
|
|
2451
|
-
};
|
|
2452
|
-
|
|
2453
|
-
// Add in every numeral.
|
|
2454
|
-
const NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
2455
|
-
for (const num of NUMBERS) {
|
|
2456
|
-
// TODO(charlie): Consider removing the SVG icons that we have for the
|
|
2457
|
-
// numeral keys. They can be rendered just as easily with text (though that
|
|
2458
|
-
// would mean that we'd be using text beyond the variable key).
|
|
2459
|
-
const textRepresentation = "".concat(num);
|
|
2460
|
-
KeyConfigs["NUM_".concat(num)] = {
|
|
2461
|
-
type: KeyTypes.VALUE,
|
|
2462
|
-
ariaLabel: textRepresentation,
|
|
2463
|
-
icon: {
|
|
2464
|
-
type: IconTypes.TEXT,
|
|
2465
|
-
data: textRepresentation
|
|
2466
|
-
}
|
|
2467
|
-
};
|
|
2468
|
-
}
|
|
2469
|
-
|
|
2470
|
-
// Add in every variable.
|
|
2471
|
-
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"];
|
|
2472
|
-
for (const letter of LETTERS) {
|
|
2473
|
-
const lowerCaseVariable = letter.toLowerCase();
|
|
2474
|
-
const upperCaseVariable = letter.toUpperCase();
|
|
2475
|
-
for (const textRepresentation of [lowerCaseVariable, upperCaseVariable]) {
|
|
2476
|
-
KeyConfigs[textRepresentation] = {
|
|
2477
|
-
type: KeyTypes.VALUE,
|
|
2478
|
-
ariaLabel: textRepresentation,
|
|
2479
|
-
icon: {
|
|
2480
|
-
type: IconTypes.MATH,
|
|
2481
|
-
data: textRepresentation
|
|
2482
|
-
}
|
|
2483
|
-
};
|
|
2484
|
-
}
|
|
2485
|
-
}
|
|
2486
|
-
for (const key of Object.keys(KeyConfigs)) {
|
|
2487
|
-
KeyConfigs[key] = {
|
|
2488
|
-
id: key,
|
|
2489
|
-
// Default to an SVG icon indexed by the key name.
|
|
2490
|
-
icon: {
|
|
2491
|
-
type: IconTypes.SVG,
|
|
2492
|
-
data: key
|
|
2493
|
-
},
|
|
2494
|
-
...KeyConfigs[key]
|
|
2495
|
-
};
|
|
2496
|
-
}
|
|
2497
|
-
|
|
2498
|
-
/**
|
|
2499
|
-
* React PropTypes that may be shared between components.
|
|
2500
|
-
*/
|
|
2501
|
-
const iconPropType = PropTypes__default["default"].shape({
|
|
2502
|
-
type: PropTypes__default["default"].oneOf(Object.keys(IconTypes)).isRequired,
|
|
2503
|
-
data: PropTypes__default["default"].string.isRequired
|
|
2504
|
-
});
|
|
2505
|
-
const keyIdPropType = PropTypes__default["default"].oneOf(Object.keys(KeyConfigs));
|
|
2506
|
-
const keyConfigPropType = PropTypes__default["default"].shape({
|
|
2507
|
-
ariaLabel: PropTypes__default["default"].string,
|
|
2508
|
-
id: keyIdPropType.isRequired,
|
|
2509
|
-
type: PropTypes__default["default"].oneOf(Object.keys(KeyTypes)).isRequired,
|
|
2510
|
-
childKeyIds: PropTypes__default["default"].arrayOf(keyIdPropType),
|
|
2511
|
-
icon: iconPropType.isRequired
|
|
2512
|
-
});
|
|
2513
|
-
const keypadConfigurationPropType = PropTypes__default["default"].shape({
|
|
2514
|
-
keypadType: PropTypes__default["default"].oneOf(Object.keys(KeypadTypes)).isRequired,
|
|
2515
|
-
extraKeys: PropTypes__default["default"].arrayOf(keyIdPropType)
|
|
2516
|
-
});
|
|
2517
2197
|
|
|
2518
2198
|
// NOTE(jared): This is no longer guaranteed to be React element
|
|
2199
|
+
// NOTE(matthewc): only seems to be used in Perseus
|
|
2519
2200
|
const keypadElementPropType = PropTypes__default["default"].shape({
|
|
2520
2201
|
activate: PropTypes__default["default"].func.isRequired,
|
|
2521
2202
|
dismiss: PropTypes__default["default"].func.isRequired,
|
|
@@ -2524,29 +2205,6 @@ const keypadElementPropType = PropTypes__default["default"].shape({
|
|
|
2524
2205
|
setKeyHandler: PropTypes__default["default"].func.isRequired,
|
|
2525
2206
|
getDOMNode: PropTypes__default["default"].func.isRequired
|
|
2526
2207
|
});
|
|
2527
|
-
const bordersPropType = PropTypes__default["default"].arrayOf(PropTypes__default["default"].oneOf(Object.keys(BorderDirections)));
|
|
2528
|
-
const boundingBoxPropType = PropTypes__default["default"].shape({
|
|
2529
|
-
height: PropTypes__default["default"].number,
|
|
2530
|
-
width: PropTypes__default["default"].number,
|
|
2531
|
-
top: PropTypes__default["default"].number,
|
|
2532
|
-
right: PropTypes__default["default"].number,
|
|
2533
|
-
bottom: PropTypes__default["default"].number,
|
|
2534
|
-
left: PropTypes__default["default"].number
|
|
2535
|
-
});
|
|
2536
|
-
const echoPropType = PropTypes__default["default"].shape({
|
|
2537
|
-
animationId: PropTypes__default["default"].string.isRequired,
|
|
2538
|
-
animationType: PropTypes__default["default"].oneOf(Object.keys(EchoAnimationTypes)).isRequired,
|
|
2539
|
-
borders: bordersPropType,
|
|
2540
|
-
id: keyIdPropType.isRequired,
|
|
2541
|
-
initialBounds: boundingBoxPropType.isRequired
|
|
2542
|
-
});
|
|
2543
|
-
const cursorContextPropType = PropTypes__default["default"].oneOf(Object.keys(CursorContexts));
|
|
2544
|
-
const popoverPropType = PropTypes__default["default"].shape({
|
|
2545
|
-
parentId: keyIdPropType.isRequired,
|
|
2546
|
-
bounds: boundingBoxPropType.isRequired,
|
|
2547
|
-
childKeyIds: PropTypes__default["default"].arrayOf(keyIdPropType).isRequired
|
|
2548
|
-
});
|
|
2549
|
-
PropTypes__default["default"].oneOfType([PropTypes__default["default"].arrayOf(PropTypes__default["default"].node), PropTypes__default["default"].node]);
|
|
2550
2208
|
|
|
2551
2209
|
// naming convention: verb + noun
|
|
2552
2210
|
// the noun should be one of the other properties in the object that's
|
|
@@ -2565,8 +2223,6 @@ const activateKeypad = () => {
|
|
|
2565
2223
|
|
|
2566
2224
|
/**
|
|
2567
2225
|
* Configure the keypad with the provided configuration parameters.
|
|
2568
|
-
*
|
|
2569
|
-
* See: `prop-types.js#keypadConfigurationPropType`.
|
|
2570
2226
|
*/
|
|
2571
2227
|
const configureKeypad = configuration => {
|
|
2572
2228
|
return {
|
|
@@ -2602,138 +2258,6 @@ const setCursor = cursor => {
|
|
|
2602
2258
|
};
|
|
2603
2259
|
};
|
|
2604
2260
|
|
|
2605
|
-
/**
|
|
2606
|
-
* An algorithm for computing the appropriate layout parameters for the keypad,
|
|
2607
|
-
* including the size of the buttons and whether or not to render fullscreen,
|
|
2608
|
-
* taking into account a number of factors including the size of the screen, the
|
|
2609
|
-
* orientation of the screen, the presence of browser chrome, the presence of
|
|
2610
|
-
* other exercise-related chrome, the size of the input box, the parameters that
|
|
2611
|
-
* define the keypad (i.e., the number of rows, columns, and pages), and so
|
|
2612
|
-
* forth.
|
|
2613
|
-
*
|
|
2614
|
-
* The computations herein make some strong assumptions about the sizes of
|
|
2615
|
-
* various other elements and the situations under which they will be visible
|
|
2616
|
-
* (e.g., browser chrome). However, this is just a heuristic--it's not crucial
|
|
2617
|
-
* that our buttons are sized in a pixel-perfect manner, but rather, that we
|
|
2618
|
-
* make a balanced use of space.
|
|
2619
|
-
*
|
|
2620
|
-
* Note that one goal of the algorithm is to avoid resizing the keypad in the
|
|
2621
|
-
* face of dynamic browser chrome. In order to avoid that awkwardness, we tend
|
|
2622
|
-
* to be conservative in our measurements and make things smaller than they
|
|
2623
|
-
* might need to be.
|
|
2624
|
-
*/
|
|
2625
|
-
const minButtonHeight = 48;
|
|
2626
|
-
const maxButtonSize = 64;
|
|
2627
|
-
const minSpaceAboveKeypad = 32;
|
|
2628
|
-
|
|
2629
|
-
// These values are taken from an iPhone 5, but should be consistent with the
|
|
2630
|
-
// iPhone 4 as well. Regardless, these are meant to be representative of the
|
|
2631
|
-
// possible types of browser chrome that could appear in various context, rather
|
|
2632
|
-
// than pixel-perfect for every device.
|
|
2633
|
-
const safariNavBarWhenShrunk = 44;
|
|
2634
|
-
const safariNavBarWhenExpanded = 64;
|
|
2635
|
-
const safariToolbar = 44;
|
|
2636
|
-
|
|
2637
|
-
// In mobile Safari, the browser chrome is completely hidden in landscape,
|
|
2638
|
-
// though a shrunken navbar and full-sized toolbar on scroll. In portrait, the
|
|
2639
|
-
// shrunken navbar is always visible, but expands on scroll (and the toolbar
|
|
2640
|
-
// appears as well).
|
|
2641
|
-
const maxLandscapeBrowserChrome = safariNavBarWhenShrunk + safariToolbar;
|
|
2642
|
-
const maxPortraitBrowserChrome = safariToolbar + (safariNavBarWhenExpanded - safariNavBarWhenShrunk);
|
|
2643
|
-
|
|
2644
|
-
// This represents the 'worst case' aspect ratio that we care about (for
|
|
2645
|
-
// portrait layouts). It's taken from the iPhone 4. The height is computed by
|
|
2646
|
-
// taking the height of the device and removing the persistent, shrunken navbar.
|
|
2647
|
-
// (We don't need to account for the expanded navbar, since we include the
|
|
2648
|
-
// difference when reserving space above the keypad.)
|
|
2649
|
-
const worstCaseAspectRatio = 320 / (480 - safariNavBarWhenShrunk);
|
|
2650
|
-
const computeLayoutParameters = (_ref, _ref2, _ref3, _ref4) => {
|
|
2651
|
-
let {
|
|
2652
|
-
numColumns,
|
|
2653
|
-
numMaxVisibleRows,
|
|
2654
|
-
numPages
|
|
2655
|
-
} = _ref;
|
|
2656
|
-
let {
|
|
2657
|
-
pageWidthPx,
|
|
2658
|
-
pageHeightPx
|
|
2659
|
-
} = _ref2;
|
|
2660
|
-
let {
|
|
2661
|
-
deviceOrientation,
|
|
2662
|
-
deviceType
|
|
2663
|
-
} = _ref3;
|
|
2664
|
-
let {
|
|
2665
|
-
navigationPadEnabled,
|
|
2666
|
-
paginationEnabled,
|
|
2667
|
-
toolbarEnabled
|
|
2668
|
-
} = _ref4;
|
|
2669
|
-
// First, compute some values that will be used in multiple computations.
|
|
2670
|
-
const effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
|
|
2671
|
-
|
|
2672
|
-
// Then, compute the button dimensions based on the provided parameters.
|
|
2673
|
-
let buttonDimensions;
|
|
2674
|
-
if (deviceType === DeviceTypes.PHONE) {
|
|
2675
|
-
const isLandscape = deviceOrientation === DeviceOrientations.LANDSCAPE;
|
|
2676
|
-
|
|
2677
|
-
// In many cases, the browser chrome will already have been factored
|
|
2678
|
-
// into `pageHeightPx`. But we have no way of knowing if that's
|
|
2679
|
-
// the case or not. As such, we take a conservative approach and
|
|
2680
|
-
// assume that the chrome is _never_ included in `pageHeightPx`.
|
|
2681
|
-
const browserChromeHeight = isLandscape ? maxLandscapeBrowserChrome : maxPortraitBrowserChrome;
|
|
2682
|
-
|
|
2683
|
-
// Count up all the space that we need to reserve on the page.
|
|
2684
|
-
// Namely, we need to account for:
|
|
2685
|
-
// 1. Space between the keypad and the top of the page.
|
|
2686
|
-
// 2. The presence of the exercise toolbar.
|
|
2687
|
-
// 3. The presence of the view pager indicator.
|
|
2688
|
-
// 4. Any browser chrome that may appear later.
|
|
2689
|
-
const reservedSpace = minSpaceAboveKeypad + browserChromeHeight + (toolbarEnabled ? toolbarHeightPx : 0) + (paginationEnabled ? pageIndicatorHeightPx : 0);
|
|
2690
|
-
|
|
2691
|
-
// Next, compute the effective width and height. We can use the page
|
|
2692
|
-
// width as the effective width. For the height, though, we take
|
|
2693
|
-
// another conservative measure when in portrait by assuming that
|
|
2694
|
-
// the device has the worst possible aspect ratio. In other words,
|
|
2695
|
-
// we ignore the device height in portrait and assume the worst.
|
|
2696
|
-
// This prevents the keypad from changing size when browser chrome
|
|
2697
|
-
// appears and disappears.
|
|
2698
|
-
const effectiveWidth = pageWidthPx;
|
|
2699
|
-
const effectiveHeight = isLandscape ? pageHeightPx : pageWidthPx / worstCaseAspectRatio;
|
|
2700
|
-
const maxKeypadHeight = effectiveHeight - reservedSpace;
|
|
2701
|
-
|
|
2702
|
-
// Finally, compute the button height and width. In computing the
|
|
2703
|
-
// height, accommodate for the maximum number of rows that will ever be
|
|
2704
|
-
// visible (since the toggling of popovers can increase the number of
|
|
2705
|
-
// visible rows).
|
|
2706
|
-
const buttonHeightPx = Math.max(Math.min(maxKeypadHeight / numMaxVisibleRows, maxButtonSize), minButtonHeight);
|
|
2707
|
-
let buttonWidthPx;
|
|
2708
|
-
if (numPages > 1) {
|
|
2709
|
-
const effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
|
|
2710
|
-
buttonWidthPx = effectiveWidth / effectiveNumColumns;
|
|
2711
|
-
} else {
|
|
2712
|
-
buttonWidthPx = isLandscape ? maxButtonSize : effectiveWidth / numColumns;
|
|
2713
|
-
}
|
|
2714
|
-
buttonDimensions = {
|
|
2715
|
-
widthPx: buttonWidthPx,
|
|
2716
|
-
heightPx: buttonHeightPx
|
|
2717
|
-
};
|
|
2718
|
-
} else if (deviceType === DeviceTypes.TABLET) {
|
|
2719
|
-
buttonDimensions = {
|
|
2720
|
-
widthPx: maxButtonSize,
|
|
2721
|
-
heightPx: maxButtonSize
|
|
2722
|
-
};
|
|
2723
|
-
} else {
|
|
2724
|
-
throw new Error("Invalid device type: " + deviceType);
|
|
2725
|
-
}
|
|
2726
|
-
|
|
2727
|
-
// Finally, determine whether the keypad should be rendered in the
|
|
2728
|
-
// fullscreen layout by determining its resultant width.
|
|
2729
|
-
const numSeparators = (navigationPadEnabled ? 1 : 0) + (!paginationEnabled ? numPages - 1 : 0);
|
|
2730
|
-
const keypadWidth = effectiveNumColumns * buttonDimensions.widthPx + (navigationPadEnabled ? navigationPadWidthPx : 0) + numSeparators * innerBorderWidthPx;
|
|
2731
|
-
return {
|
|
2732
|
-
buttonDimensions,
|
|
2733
|
-
layoutMode: keypadWidth >= pageWidthPx ? LayoutModes.FULLSCREEN : LayoutModes.COMPACT
|
|
2734
|
-
};
|
|
2735
|
-
};
|
|
2736
|
-
|
|
2737
2261
|
/**
|
|
2738
2262
|
* The state machine that backs our gesture system. In particular, this state
|
|
2739
2263
|
* machine manages the interplay between focuses, touch ups, and swiping.
|
|
@@ -2742,16 +2266,24 @@ const computeLayoutParameters = (_ref, _ref2, _ref3, _ref4) => {
|
|
|
2742
2266
|
* multi-touch interactions, tracking gesture state on a per-touch basis.
|
|
2743
2267
|
*/
|
|
2744
2268
|
|
|
2745
|
-
|
|
2269
|
+
// exported for tests
|
|
2270
|
+
|
|
2271
|
+
const defaultOptions = {
|
|
2746
2272
|
longPressWaitTimeMs: 50,
|
|
2747
2273
|
swipeThresholdPx: 20,
|
|
2748
2274
|
holdIntervalMs: 250
|
|
2749
2275
|
};
|
|
2750
2276
|
class GestureStateMachine {
|
|
2751
2277
|
constructor(handlers, options, swipeDisabledNodeIds, multiPressableKeys) {
|
|
2278
|
+
_defineProperty(this, "handlers", void 0);
|
|
2279
|
+
_defineProperty(this, "options", void 0);
|
|
2280
|
+
_defineProperty(this, "swipeDisabledNodeIds", void 0);
|
|
2281
|
+
_defineProperty(this, "multiPressableKeys", void 0);
|
|
2282
|
+
_defineProperty(this, "touchState", void 0);
|
|
2283
|
+
_defineProperty(this, "swipeState", void 0);
|
|
2752
2284
|
this.handlers = handlers;
|
|
2753
2285
|
this.options = {
|
|
2754
|
-
...
|
|
2286
|
+
...defaultOptions,
|
|
2755
2287
|
...options
|
|
2756
2288
|
};
|
|
2757
2289
|
this.swipeDisabledNodeIds = swipeDisabledNodeIds || [];
|
|
@@ -3020,6 +2552,10 @@ class GestureStateMachine {
|
|
|
3020
2552
|
|
|
3021
2553
|
class NodeManager {
|
|
3022
2554
|
constructor() {
|
|
2555
|
+
_defineProperty(this, "_nodesById", void 0);
|
|
2556
|
+
_defineProperty(this, "_bordersById", void 0);
|
|
2557
|
+
_defineProperty(this, "_orderedIds", void 0);
|
|
2558
|
+
_defineProperty(this, "_cachedBoundingBoxesById", void 0);
|
|
3023
2559
|
// A mapping from IDs to DOM nodes.
|
|
3024
2560
|
this._nodesById = {};
|
|
3025
2561
|
|
|
@@ -3069,6 +2605,7 @@ class NodeManager {
|
|
|
3069
2605
|
const seenIds = {};
|
|
3070
2606
|
for (const id of allIds) {
|
|
3071
2607
|
if (!seenIds[id]) {
|
|
2608
|
+
// @ts-expect-error TS2345
|
|
3072
2609
|
orderedIds.push(id);
|
|
3073
2610
|
seenIds[id] = true;
|
|
3074
2611
|
}
|
|
@@ -3137,6 +2674,9 @@ class NodeManager {
|
|
|
3137
2674
|
|
|
3138
2675
|
class PopoverStateMachine {
|
|
3139
2676
|
constructor(handlers) {
|
|
2677
|
+
_defineProperty(this, "handlers", void 0);
|
|
2678
|
+
_defineProperty(this, "popovers", void 0);
|
|
2679
|
+
_defineProperty(this, "activePopover", void 0);
|
|
3140
2680
|
this.handlers = handlers;
|
|
3141
2681
|
this.activePopover = null;
|
|
3142
2682
|
this.popovers = {};
|
|
@@ -3233,8 +2773,8 @@ class PopoverStateMachine {
|
|
|
3233
2773
|
this.activePopover = id;
|
|
3234
2774
|
this.handlers.onActiveNodesChanged({
|
|
3235
2775
|
popover: {
|
|
3236
|
-
parentId:
|
|
3237
|
-
childIds: this.popovers[
|
|
2776
|
+
parentId: id,
|
|
2777
|
+
childIds: this.popovers[id]
|
|
3238
2778
|
},
|
|
3239
2779
|
focus: this._defaultNodeForPopover(this.activePopover)
|
|
3240
2780
|
});
|
|
@@ -3296,16 +2836,16 @@ class PopoverStateMachine {
|
|
|
3296
2836
|
}
|
|
3297
2837
|
}
|
|
3298
2838
|
|
|
3299
|
-
/**
|
|
3300
|
-
* A high-level manager for our gesture system. In particular, this class
|
|
3301
|
-
* connects our various bits of logic for managing gestures and interactions,
|
|
3302
|
-
* and links them together.
|
|
3303
|
-
*/
|
|
3304
2839
|
const coordsForEvent = evt => {
|
|
3305
2840
|
return [evt.changedTouches[0].clientX, evt.changedTouches[0].clientY];
|
|
3306
2841
|
};
|
|
3307
2842
|
class GestureManager {
|
|
3308
2843
|
constructor(options, handlers, disabledSwipeKeys, multiPressableKeys) {
|
|
2844
|
+
_defineProperty(this, "swipeEnabled", void 0);
|
|
2845
|
+
_defineProperty(this, "trackEvents", void 0);
|
|
2846
|
+
_defineProperty(this, "nodeManager", void 0);
|
|
2847
|
+
_defineProperty(this, "popoverStateMachine", void 0);
|
|
2848
|
+
_defineProperty(this, "gestureStateMachine", void 0);
|
|
3309
2849
|
const {
|
|
3310
2850
|
swipeEnabled
|
|
3311
2851
|
} = options;
|
|
@@ -3488,54 +3028,434 @@ class GestureManager {
|
|
|
3488
3028
|
}
|
|
3489
3029
|
}
|
|
3490
3030
|
|
|
3491
|
-
class CornerDecal extends React__namespace.Component {
|
|
3492
|
-
render() {
|
|
3493
|
-
const {
|
|
3494
|
-
style
|
|
3495
|
-
} = this.props;
|
|
3496
|
-
const containerStyle = [styles$d.container, ...(Array.isArray(style) ? style : [style])];
|
|
3497
|
-
return /*#__PURE__*/React__namespace.createElement(View, {
|
|
3498
|
-
style: containerStyle
|
|
3499
|
-
}, /*#__PURE__*/React__namespace.createElement("svg", {
|
|
3500
|
-
width: triangleSizePx,
|
|
3501
|
-
height: triangleSizePx,
|
|
3502
|
-
viewBox: "4 4 8 8"
|
|
3503
|
-
}, /*#__PURE__*/React__namespace.createElement("path", {
|
|
3504
|
-
fill: offBlack,
|
|
3505
|
-
opacity: "0.3",
|
|
3506
|
-
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
|
|
3507
|
-
})));
|
|
3508
|
-
}
|
|
3509
|
-
}
|
|
3510
|
-
_defineProperty(CornerDecal, "propTypes", {
|
|
3511
|
-
style: PropTypes__default["default"].any
|
|
3512
|
-
});
|
|
3513
|
-
const triangleSizePx = 7;
|
|
3514
|
-
const styles$d = aphrodite.StyleSheet.create({
|
|
3515
|
-
container: {
|
|
3516
|
-
position: "absolute",
|
|
3517
|
-
top: 0,
|
|
3518
|
-
right: 0,
|
|
3519
|
-
width: triangleSizePx,
|
|
3520
|
-
height: triangleSizePx
|
|
3521
|
-
}
|
|
3522
|
-
});
|
|
3523
|
-
|
|
3524
3031
|
/**
|
|
3525
|
-
*
|
|
3032
|
+
* This file contains configuration settings for the buttons in the keypad.
|
|
3526
3033
|
*/
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3034
|
+
const KeyConfigs = {
|
|
3035
|
+
// Basic math keys.
|
|
3036
|
+
[Keys.PLUS]: {
|
|
3037
|
+
type: KeyTypes.OPERATOR,
|
|
3038
|
+
// I18N: A label for a plus sign.
|
|
3039
|
+
ariaLabel: i18n__namespace._("Plus")
|
|
3530
3040
|
},
|
|
3531
|
-
|
|
3532
|
-
|
|
3041
|
+
[Keys.MINUS]: {
|
|
3042
|
+
type: KeyTypes.OPERATOR,
|
|
3043
|
+
// I18N: A label for a minus sign.
|
|
3044
|
+
ariaLabel: i18n__namespace._("Minus")
|
|
3533
3045
|
},
|
|
3534
|
-
|
|
3535
|
-
|
|
3046
|
+
[Keys.NEGATIVE]: {
|
|
3047
|
+
type: KeyTypes.VALUE,
|
|
3048
|
+
// I18N: A label for a minus sign.
|
|
3049
|
+
ariaLabel: i18n__namespace._("Negative")
|
|
3536
3050
|
},
|
|
3537
|
-
|
|
3538
|
-
|
|
3051
|
+
[Keys.TIMES]: {
|
|
3052
|
+
type: KeyTypes.OPERATOR,
|
|
3053
|
+
// I18N: A label for a multiplication sign (represented with an 'x').
|
|
3054
|
+
ariaLabel: i18n__namespace._("Multiply")
|
|
3055
|
+
},
|
|
3056
|
+
[Keys.DIVIDE]: {
|
|
3057
|
+
type: KeyTypes.OPERATOR,
|
|
3058
|
+
// I18N: A label for a division sign.
|
|
3059
|
+
ariaLabel: i18n__namespace._("Divide")
|
|
3060
|
+
},
|
|
3061
|
+
[Keys.DECIMAL]: {
|
|
3062
|
+
type: KeyTypes.VALUE,
|
|
3063
|
+
// I18N: A label for a decimal symbol.
|
|
3064
|
+
ariaLabel: i18n__namespace._("Decimal"),
|
|
3065
|
+
icon: decimalSeparator === DecimalSeparators.COMMA ? {
|
|
3066
|
+
// TODO(charlie): Get an SVG icon for the comma, or verify with
|
|
3067
|
+
// design that the text-rendered version is acceptable.
|
|
3068
|
+
type: IconTypes.TEXT,
|
|
3069
|
+
data: ","
|
|
3070
|
+
} : {
|
|
3071
|
+
type: IconTypes.SVG,
|
|
3072
|
+
data: Keys.PERIOD
|
|
3073
|
+
}
|
|
3074
|
+
},
|
|
3075
|
+
[Keys.PERCENT]: {
|
|
3076
|
+
type: KeyTypes.OPERATOR,
|
|
3077
|
+
// I18N: A label for a percent sign.
|
|
3078
|
+
ariaLabel: i18n__namespace._("Percent")
|
|
3079
|
+
},
|
|
3080
|
+
[Keys.CDOT]: {
|
|
3081
|
+
type: KeyTypes.OPERATOR,
|
|
3082
|
+
// I18N: A label for a multiplication sign (represented as a dot).
|
|
3083
|
+
ariaLabel: i18n__namespace._("Multiply")
|
|
3084
|
+
},
|
|
3085
|
+
[Keys.EQUAL]: {
|
|
3086
|
+
type: KeyTypes.OPERATOR,
|
|
3087
|
+
ariaLabel: i18n__namespace._("Equals sign")
|
|
3088
|
+
},
|
|
3089
|
+
[Keys.NEQ]: {
|
|
3090
|
+
type: KeyTypes.OPERATOR,
|
|
3091
|
+
ariaLabel: i18n__namespace._("Not-equals sign")
|
|
3092
|
+
},
|
|
3093
|
+
[Keys.GT]: {
|
|
3094
|
+
type: KeyTypes.OPERATOR,
|
|
3095
|
+
// I18N: A label for a 'greater than' sign (represented as '>').
|
|
3096
|
+
ariaLabel: i18n__namespace._("Greater than sign")
|
|
3097
|
+
},
|
|
3098
|
+
[Keys.LT]: {
|
|
3099
|
+
type: KeyTypes.OPERATOR,
|
|
3100
|
+
// I18N: A label for a 'less than' sign (represented as '<').
|
|
3101
|
+
ariaLabel: i18n__namespace._("Less than sign")
|
|
3102
|
+
},
|
|
3103
|
+
[Keys.GEQ]: {
|
|
3104
|
+
type: KeyTypes.OPERATOR,
|
|
3105
|
+
ariaLabel: i18n__namespace._("Greater than or equal to sign")
|
|
3106
|
+
},
|
|
3107
|
+
[Keys.LEQ]: {
|
|
3108
|
+
type: KeyTypes.OPERATOR,
|
|
3109
|
+
ariaLabel: i18n__namespace._("Less than or equal to sign")
|
|
3110
|
+
},
|
|
3111
|
+
// mobile native
|
|
3112
|
+
[Keys.FRAC_INCLUSIVE]: {
|
|
3113
|
+
type: KeyTypes.OPERATOR,
|
|
3114
|
+
// I18N: A label for a button that creates a new fraction and puts the
|
|
3115
|
+
// current expression in the numerator of that fraction.
|
|
3116
|
+
ariaLabel: i18n__namespace._("Fraction, with current expression in numerator")
|
|
3117
|
+
},
|
|
3118
|
+
// mobile native
|
|
3119
|
+
[Keys.FRAC_EXCLUSIVE]: {
|
|
3120
|
+
type: KeyTypes.OPERATOR,
|
|
3121
|
+
// I18N: A label for a button that creates a new fraction next to the
|
|
3122
|
+
// cursor.
|
|
3123
|
+
ariaLabel: i18n__namespace._("Fraction, excluding the current expression")
|
|
3124
|
+
},
|
|
3125
|
+
// mobile web
|
|
3126
|
+
[Keys.FRAC]: {
|
|
3127
|
+
type: KeyTypes.OPERATOR,
|
|
3128
|
+
// I18N: A label for a button that creates a new fraction next to the
|
|
3129
|
+
// cursor.
|
|
3130
|
+
ariaLabel: i18n__namespace._("Fraction, excluding the current expression")
|
|
3131
|
+
},
|
|
3132
|
+
[Keys.EXP]: {
|
|
3133
|
+
type: KeyTypes.OPERATOR,
|
|
3134
|
+
// I18N: A label for a button that will allow the user to input a custom
|
|
3135
|
+
// exponent.
|
|
3136
|
+
ariaLabel: i18n__namespace._("Custom exponent")
|
|
3137
|
+
},
|
|
3138
|
+
[Keys.EXP_2]: {
|
|
3139
|
+
type: KeyTypes.OPERATOR,
|
|
3140
|
+
// I18N: A label for a button that will square (take to the second
|
|
3141
|
+
// power) some math.
|
|
3142
|
+
ariaLabel: i18n__namespace._("Square")
|
|
3143
|
+
},
|
|
3144
|
+
[Keys.EXP_3]: {
|
|
3145
|
+
type: KeyTypes.OPERATOR,
|
|
3146
|
+
// I18N: A label for a button that will cube (take to the third power)
|
|
3147
|
+
// some math.
|
|
3148
|
+
ariaLabel: i18n__namespace._("Cube")
|
|
3149
|
+
},
|
|
3150
|
+
[Keys.SQRT]: {
|
|
3151
|
+
type: KeyTypes.OPERATOR,
|
|
3152
|
+
ariaLabel: i18n__namespace._("Square root")
|
|
3153
|
+
},
|
|
3154
|
+
[Keys.CUBE_ROOT]: {
|
|
3155
|
+
type: KeyTypes.OPERATOR,
|
|
3156
|
+
ariaLabel: i18n__namespace._("Cube root")
|
|
3157
|
+
},
|
|
3158
|
+
[Keys.RADICAL]: {
|
|
3159
|
+
type: KeyTypes.OPERATOR,
|
|
3160
|
+
ariaLabel: i18n__namespace._("Radical with custom root")
|
|
3161
|
+
},
|
|
3162
|
+
[Keys.LEFT_PAREN]: {
|
|
3163
|
+
type: KeyTypes.OPERATOR,
|
|
3164
|
+
ariaLabel: i18n__namespace._("Left parenthesis")
|
|
3165
|
+
},
|
|
3166
|
+
[Keys.RIGHT_PAREN]: {
|
|
3167
|
+
type: KeyTypes.OPERATOR,
|
|
3168
|
+
ariaLabel: i18n__namespace._("Right parenthesis")
|
|
3169
|
+
},
|
|
3170
|
+
[Keys.LN]: {
|
|
3171
|
+
type: KeyTypes.OPERATOR,
|
|
3172
|
+
ariaLabel: i18n__namespace._("Natural logarithm")
|
|
3173
|
+
},
|
|
3174
|
+
[Keys.LOG]: {
|
|
3175
|
+
type: KeyTypes.OPERATOR,
|
|
3176
|
+
ariaLabel: i18n__namespace._("Logarithm with base 10")
|
|
3177
|
+
},
|
|
3178
|
+
[Keys.LOG_N]: {
|
|
3179
|
+
type: KeyTypes.OPERATOR,
|
|
3180
|
+
ariaLabel: i18n__namespace._("Logarithm with custom base")
|
|
3181
|
+
},
|
|
3182
|
+
[Keys.SIN]: {
|
|
3183
|
+
type: KeyTypes.OPERATOR,
|
|
3184
|
+
ariaLabel: i18n__namespace._("Sine")
|
|
3185
|
+
},
|
|
3186
|
+
[Keys.COS]: {
|
|
3187
|
+
type: KeyTypes.OPERATOR,
|
|
3188
|
+
ariaLabel: i18n__namespace._("Cosine")
|
|
3189
|
+
},
|
|
3190
|
+
[Keys.TAN]: {
|
|
3191
|
+
type: KeyTypes.OPERATOR,
|
|
3192
|
+
ariaLabel: i18n__namespace._("Tangent")
|
|
3193
|
+
},
|
|
3194
|
+
[Keys.PI]: {
|
|
3195
|
+
type: KeyTypes.VALUE,
|
|
3196
|
+
ariaLabel: i18n__namespace._("Pi"),
|
|
3197
|
+
icon: {
|
|
3198
|
+
type: IconTypes.MATH,
|
|
3199
|
+
data: "\\pi"
|
|
3200
|
+
}
|
|
3201
|
+
},
|
|
3202
|
+
[Keys.THETA]: {
|
|
3203
|
+
type: KeyTypes.VALUE,
|
|
3204
|
+
ariaLabel: i18n__namespace._("Theta"),
|
|
3205
|
+
icon: {
|
|
3206
|
+
type: IconTypes.MATH,
|
|
3207
|
+
data: "\\theta"
|
|
3208
|
+
}
|
|
3209
|
+
},
|
|
3210
|
+
[Keys.NOOP]: {
|
|
3211
|
+
type: KeyTypes.EMPTY
|
|
3212
|
+
},
|
|
3213
|
+
// Input navigation keys.
|
|
3214
|
+
[Keys.UP]: {
|
|
3215
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3216
|
+
ariaLabel: i18n__namespace._("Up arrow")
|
|
3217
|
+
},
|
|
3218
|
+
[Keys.RIGHT]: {
|
|
3219
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3220
|
+
ariaLabel: i18n__namespace._("Right arrow")
|
|
3221
|
+
},
|
|
3222
|
+
[Keys.DOWN]: {
|
|
3223
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3224
|
+
ariaLabel: i18n__namespace._("Down arrow")
|
|
3225
|
+
},
|
|
3226
|
+
[Keys.LEFT]: {
|
|
3227
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3228
|
+
ariaLabel: i18n__namespace._("Left arrow")
|
|
3229
|
+
},
|
|
3230
|
+
[Keys.JUMP_OUT_PARENTHESES]: {
|
|
3231
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3232
|
+
ariaLabel: i18n__namespace._("Navigate right out of a set of parentheses")
|
|
3233
|
+
},
|
|
3234
|
+
[Keys.JUMP_OUT_EXPONENT]: {
|
|
3235
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3236
|
+
ariaLabel: i18n__namespace._("Navigate right out of an exponent")
|
|
3237
|
+
},
|
|
3238
|
+
[Keys.JUMP_OUT_BASE]: {
|
|
3239
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3240
|
+
ariaLabel: i18n__namespace._("Navigate right out of a base")
|
|
3241
|
+
},
|
|
3242
|
+
[Keys.JUMP_INTO_NUMERATOR]: {
|
|
3243
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3244
|
+
ariaLabel: i18n__namespace._("Navigate right into the numerator of a fraction")
|
|
3245
|
+
},
|
|
3246
|
+
[Keys.JUMP_OUT_NUMERATOR]: {
|
|
3247
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3248
|
+
ariaLabel: i18n__namespace._("Navigate right out of the numerator and into the denominator")
|
|
3249
|
+
},
|
|
3250
|
+
[Keys.JUMP_OUT_DENOMINATOR]: {
|
|
3251
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3252
|
+
ariaLabel: i18n__namespace._("Navigate right out of the denominator of a fraction")
|
|
3253
|
+
},
|
|
3254
|
+
[Keys.BACKSPACE]: {
|
|
3255
|
+
type: KeyTypes.INPUT_NAVIGATION,
|
|
3256
|
+
// I18N: A label for a button that will delete some input.
|
|
3257
|
+
ariaLabel: i18n__namespace._("Delete")
|
|
3258
|
+
},
|
|
3259
|
+
// Keypad navigation keys.
|
|
3260
|
+
[Keys.DISMISS]: {
|
|
3261
|
+
type: KeyTypes.KEYPAD_NAVIGATION,
|
|
3262
|
+
// I18N: A label for a button that will dismiss/hide a keypad.
|
|
3263
|
+
ariaLabel: i18n__namespace._("Dismiss")
|
|
3264
|
+
}
|
|
3265
|
+
};
|
|
3266
|
+
|
|
3267
|
+
// Add in any multi-function buttons. By default, these keys will mix in any
|
|
3268
|
+
// configuration settings from their default child key (i.e., the first key in
|
|
3269
|
+
// the `childKeyIds` array).
|
|
3270
|
+
// TODO(charlie): Make the multi-function button's long-press interaction
|
|
3271
|
+
// accessible.
|
|
3272
|
+
// NOTE(kevinb): This is only used in the mobile native app.
|
|
3273
|
+
KeyConfigs[Keys.FRAC_MULTI] = {
|
|
3274
|
+
childKeyIds: [Keys.FRAC_INCLUSIVE, Keys.FRAC_EXCLUSIVE]
|
|
3275
|
+
};
|
|
3276
|
+
|
|
3277
|
+
// TODO(charlie): Use the numeral color for the 'Many' key.
|
|
3278
|
+
KeyConfigs[Keys.MANY] = {
|
|
3279
|
+
type: KeyTypes.MANY
|
|
3280
|
+
// childKeyIds will be configured by the client.
|
|
3281
|
+
};
|
|
3282
|
+
|
|
3283
|
+
// Add in every numeral.
|
|
3284
|
+
const NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
3285
|
+
for (const num of NUMBERS) {
|
|
3286
|
+
// TODO(charlie): Consider removing the SVG icons that we have for the
|
|
3287
|
+
// numeral keys. They can be rendered just as easily with text (though that
|
|
3288
|
+
// would mean that we'd be using text beyond the variable key).
|
|
3289
|
+
const textRepresentation = "".concat(num);
|
|
3290
|
+
KeyConfigs["NUM_".concat(num)] = {
|
|
3291
|
+
type: KeyTypes.VALUE,
|
|
3292
|
+
ariaLabel: textRepresentation,
|
|
3293
|
+
icon: {
|
|
3294
|
+
type: IconTypes.TEXT,
|
|
3295
|
+
data: textRepresentation
|
|
3296
|
+
}
|
|
3297
|
+
};
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3300
|
+
// Add in every variable.
|
|
3301
|
+
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"];
|
|
3302
|
+
for (const letter of LETTERS) {
|
|
3303
|
+
const lowerCaseVariable = letter.toLowerCase();
|
|
3304
|
+
const upperCaseVariable = letter.toUpperCase();
|
|
3305
|
+
for (const textRepresentation of [lowerCaseVariable, upperCaseVariable]) {
|
|
3306
|
+
KeyConfigs[textRepresentation] = {
|
|
3307
|
+
type: KeyTypes.VALUE,
|
|
3308
|
+
ariaLabel: textRepresentation,
|
|
3309
|
+
icon: {
|
|
3310
|
+
type: IconTypes.MATH,
|
|
3311
|
+
data: textRepresentation
|
|
3312
|
+
}
|
|
3313
|
+
};
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
for (const key of Object.keys(KeyConfigs)) {
|
|
3317
|
+
KeyConfigs[key] = {
|
|
3318
|
+
id: key,
|
|
3319
|
+
// Default to an SVG icon indexed by the key name.
|
|
3320
|
+
icon: {
|
|
3321
|
+
type: IconTypes.SVG,
|
|
3322
|
+
data: key
|
|
3323
|
+
},
|
|
3324
|
+
...KeyConfigs[key]
|
|
3325
|
+
};
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3328
|
+
// Used to generate unique animation IDs for the echo animations. The actual
|
|
3329
|
+
// values are irrelevant as long as they are unique.
|
|
3330
|
+
let _lastAnimationId = 0;
|
|
3331
|
+
const initialEchoState = {
|
|
3332
|
+
echoes: []
|
|
3333
|
+
};
|
|
3334
|
+
const echoReducer = function () {
|
|
3335
|
+
let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialEchoState;
|
|
3336
|
+
let action = arguments.length > 1 ? arguments[1] : undefined;
|
|
3337
|
+
switch (action.type) {
|
|
3338
|
+
case "PressKey":
|
|
3339
|
+
const keyConfig = KeyConfigs[action.key];
|
|
3340
|
+
|
|
3341
|
+
// Add in the echo animation if the user performs a math
|
|
3342
|
+
// operation.
|
|
3343
|
+
if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
|
|
3344
|
+
return {
|
|
3345
|
+
...state,
|
|
3346
|
+
echoes: [...state.echoes, {
|
|
3347
|
+
animationId: "" + _lastAnimationId++,
|
|
3348
|
+
animationType: action.inPopover ? EchoAnimationTypes.LONG_FADE_ONLY : EchoAnimationTypes.FADE_ONLY,
|
|
3349
|
+
borders: action.borders,
|
|
3350
|
+
id: keyConfig.id,
|
|
3351
|
+
initialBounds: action.initialBounds
|
|
3352
|
+
}]
|
|
3353
|
+
};
|
|
3354
|
+
}
|
|
3355
|
+
return state;
|
|
3356
|
+
case "RemoveEcho":
|
|
3357
|
+
const remainingEchoes = state.echoes.filter(echo => {
|
|
3358
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'animationId' does not exist on type 'never'.
|
|
3359
|
+
return echo.animationId !== action.animationId;
|
|
3360
|
+
});
|
|
3361
|
+
return {
|
|
3362
|
+
...state,
|
|
3363
|
+
echoes: remainingEchoes
|
|
3364
|
+
};
|
|
3365
|
+
default:
|
|
3366
|
+
return state;
|
|
3367
|
+
}
|
|
3368
|
+
};
|
|
3369
|
+
|
|
3370
|
+
const initialInputState = {
|
|
3371
|
+
keyHandler: null,
|
|
3372
|
+
cursor: {
|
|
3373
|
+
context: NONE
|
|
3374
|
+
}
|
|
3375
|
+
};
|
|
3376
|
+
const inputReducer = function () {
|
|
3377
|
+
let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialInputState;
|
|
3378
|
+
let action = arguments.length > 1 ? arguments[1] : undefined;
|
|
3379
|
+
switch (action.type) {
|
|
3380
|
+
case "SetKeyHandler":
|
|
3381
|
+
return {
|
|
3382
|
+
...state,
|
|
3383
|
+
keyHandler: action.keyHandler
|
|
3384
|
+
};
|
|
3385
|
+
case "PressKey":
|
|
3386
|
+
const keyConfig = KeyConfigs[action.key];
|
|
3387
|
+
if (keyConfig.type !== KeyTypes.KEYPAD_NAVIGATION) {
|
|
3388
|
+
var _state$keyHandler;
|
|
3389
|
+
// This is probably an anti-pattern but it works for the
|
|
3390
|
+
// case where we don't actually control the state but we
|
|
3391
|
+
// still want to communicate with the other object
|
|
3392
|
+
return {
|
|
3393
|
+
...state,
|
|
3394
|
+
cursor: (_state$keyHandler = state.keyHandler) === null || _state$keyHandler === void 0 ? void 0 : _state$keyHandler.call(state, keyConfig.id)
|
|
3395
|
+
};
|
|
3396
|
+
}
|
|
3397
|
+
|
|
3398
|
+
// TODO(kevinb) get state from MathQuill and store it?
|
|
3399
|
+
return state;
|
|
3400
|
+
case "SetCursor":
|
|
3401
|
+
return {
|
|
3402
|
+
...state,
|
|
3403
|
+
cursor: action.cursor
|
|
3404
|
+
};
|
|
3405
|
+
default:
|
|
3406
|
+
return state;
|
|
3407
|
+
}
|
|
3408
|
+
};
|
|
3409
|
+
|
|
3410
|
+
/**
|
|
3411
|
+
* A small triangular decal to sit in the corner of a parent component.
|
|
3412
|
+
*/
|
|
3413
|
+
class CornerDecal extends React__namespace.Component {
|
|
3414
|
+
render() {
|
|
3415
|
+
const {
|
|
3416
|
+
style
|
|
3417
|
+
} = this.props;
|
|
3418
|
+
const containerStyle = [styles$d.container, ...(Array.isArray(style) ? style : [style])];
|
|
3419
|
+
return /*#__PURE__*/React__namespace.createElement(View, {
|
|
3420
|
+
style: containerStyle
|
|
3421
|
+
}, /*#__PURE__*/React__namespace.createElement("svg", {
|
|
3422
|
+
width: triangleSizePx,
|
|
3423
|
+
height: triangleSizePx,
|
|
3424
|
+
viewBox: "4 4 8 8"
|
|
3425
|
+
}, /*#__PURE__*/React__namespace.createElement("path", {
|
|
3426
|
+
fill: offBlack,
|
|
3427
|
+
opacity: "0.3",
|
|
3428
|
+
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
|
|
3429
|
+
})));
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3433
|
+
const triangleSizePx = 7;
|
|
3434
|
+
const styles$d = aphrodite.StyleSheet.create({
|
|
3435
|
+
container: {
|
|
3436
|
+
position: "absolute",
|
|
3437
|
+
top: 0,
|
|
3438
|
+
right: 0,
|
|
3439
|
+
width: triangleSizePx,
|
|
3440
|
+
height: triangleSizePx
|
|
3441
|
+
}
|
|
3442
|
+
});
|
|
3443
|
+
|
|
3444
|
+
/**
|
|
3445
|
+
* Common styles shared across components.
|
|
3446
|
+
*/
|
|
3447
|
+
var Styles = aphrodite.StyleSheet.create({
|
|
3448
|
+
row: {
|
|
3449
|
+
flexDirection: "row"
|
|
3450
|
+
},
|
|
3451
|
+
column: {
|
|
3452
|
+
flexDirection: "column"
|
|
3453
|
+
},
|
|
3454
|
+
oneColumn: {
|
|
3455
|
+
flexGrow: 1
|
|
3456
|
+
},
|
|
3457
|
+
fullWidth: {
|
|
3458
|
+
width: "100%"
|
|
3539
3459
|
},
|
|
3540
3460
|
stretch: {
|
|
3541
3461
|
alignItems: "stretch"
|
|
@@ -3587,10 +3507,6 @@ class MathIcon extends React__namespace.Component {
|
|
|
3587
3507
|
});
|
|
3588
3508
|
}
|
|
3589
3509
|
}
|
|
3590
|
-
_defineProperty(MathIcon, "propTypes", {
|
|
3591
|
-
math: PropTypes__default["default"].string.isRequired,
|
|
3592
|
-
style: PropTypes__default["default"].any
|
|
3593
|
-
});
|
|
3594
3510
|
const styles$c = aphrodite.StyleSheet.create({
|
|
3595
3511
|
size: {
|
|
3596
3512
|
height: iconSizeHeightPx,
|
|
@@ -4743,6 +4659,9 @@ var Iconography = /*#__PURE__*/Object.freeze({
|
|
|
4743
4659
|
JUMP_OUT_DENOMINATOR: JumpOutDenominator
|
|
4744
4660
|
});
|
|
4745
4661
|
|
|
4662
|
+
/**
|
|
4663
|
+
* A component that renders a single SVG icon.
|
|
4664
|
+
*/
|
|
4746
4665
|
class SvgIcon extends React__namespace.Component {
|
|
4747
4666
|
render() {
|
|
4748
4667
|
const {
|
|
@@ -4757,11 +4676,10 @@ class SvgIcon extends React__namespace.Component {
|
|
|
4757
4676
|
});
|
|
4758
4677
|
}
|
|
4759
4678
|
}
|
|
4760
|
-
_defineProperty(SvgIcon, "propTypes", {
|
|
4761
|
-
color: PropTypes__default["default"].string.isRequired,
|
|
4762
|
-
name: PropTypes__default["default"].string.isRequired
|
|
4763
|
-
});
|
|
4764
4679
|
|
|
4680
|
+
/**
|
|
4681
|
+
* A component that renders a text-based icon.
|
|
4682
|
+
*/
|
|
4765
4683
|
const {
|
|
4766
4684
|
row: row$6,
|
|
4767
4685
|
centered: centered$3
|
|
@@ -4778,10 +4696,6 @@ class TextIcon extends React__namespace.Component {
|
|
|
4778
4696
|
}, /*#__PURE__*/React__namespace.createElement(Text, null, character));
|
|
4779
4697
|
}
|
|
4780
4698
|
}
|
|
4781
|
-
_defineProperty(TextIcon, "propTypes", {
|
|
4782
|
-
character: PropTypes__default["default"].string.isRequired,
|
|
4783
|
-
style: PropTypes__default["default"].any
|
|
4784
|
-
});
|
|
4785
4699
|
const styles$b = aphrodite.StyleSheet.create({
|
|
4786
4700
|
size: {
|
|
4787
4701
|
height: iconSizeHeightPx,
|
|
@@ -4793,6 +4707,9 @@ const styles$b = aphrodite.StyleSheet.create({
|
|
|
4793
4707
|
}
|
|
4794
4708
|
});
|
|
4795
4709
|
|
|
4710
|
+
/**
|
|
4711
|
+
* A component that renders an icon for a symbol with the given name.
|
|
4712
|
+
*/
|
|
4796
4713
|
const focusedColor = "#FFF";
|
|
4797
4714
|
const unfocusedColor = offBlack;
|
|
4798
4715
|
class Icon extends React__namespace.PureComponent {
|
|
@@ -4823,18 +4740,11 @@ class Icon extends React__namespace.PureComponent {
|
|
|
4823
4740
|
character: icon.data,
|
|
4824
4741
|
style: styleWithFocus
|
|
4825
4742
|
});
|
|
4743
|
+
default:
|
|
4744
|
+
throw new Error("No icon or symbol provided");
|
|
4826
4745
|
}
|
|
4827
|
-
throw new Error("No icon or symbol provided");
|
|
4828
4746
|
}
|
|
4829
4747
|
}
|
|
4830
|
-
_defineProperty(Icon, "propTypes", {
|
|
4831
|
-
focused: PropTypes__default["default"].bool,
|
|
4832
|
-
icon: iconPropType.isRequired,
|
|
4833
|
-
// An Aphrodite style object, or an array of Aphrodite style objects.
|
|
4834
|
-
// Note that custom styles will only be applied to text and math icons
|
|
4835
|
-
// (and not SVG icons).
|
|
4836
|
-
style: PropTypes__default["default"].any
|
|
4837
|
-
});
|
|
4838
4748
|
const styles$a = aphrodite.StyleSheet.create({
|
|
4839
4749
|
unfocused: {
|
|
4840
4750
|
color: unfocusedColor
|
|
@@ -4844,6 +4754,10 @@ const styles$a = aphrodite.StyleSheet.create({
|
|
|
4844
4754
|
}
|
|
4845
4755
|
});
|
|
4846
4756
|
|
|
4757
|
+
/**
|
|
4758
|
+
* A grid of symbols, rendered as text and positioned based on the number of
|
|
4759
|
+
* symbols provided. Up to four symbols will be shown.
|
|
4760
|
+
*/
|
|
4847
4761
|
const {
|
|
4848
4762
|
row: row$5,
|
|
4849
4763
|
column: column$3,
|
|
@@ -4925,13 +4839,9 @@ class MultiSymbolGrid extends React__namespace.Component {
|
|
|
4925
4839
|
}))));
|
|
4926
4840
|
}
|
|
4927
4841
|
}
|
|
4928
|
-
throw new Error("Invalid number of icons:"
|
|
4842
|
+
throw new Error("Invalid number of icons: ".concat(icons.length));
|
|
4929
4843
|
}
|
|
4930
4844
|
}
|
|
4931
|
-
_defineProperty(MultiSymbolGrid, "propTypes", {
|
|
4932
|
-
focused: PropTypes__default["default"].bool,
|
|
4933
|
-
icons: PropTypes__default["default"].arrayOf(iconPropType).isRequired
|
|
4934
|
-
});
|
|
4935
4845
|
const verticalInsetPx = 2;
|
|
4936
4846
|
const horizontalInsetPx = 4;
|
|
4937
4847
|
const styles$9 = aphrodite.StyleSheet.create({
|
|
@@ -4975,6 +4885,7 @@ const styles$9 = aphrodite.StyleSheet.create({
|
|
|
4975
4885
|
class KeypadButton extends React__namespace.PureComponent {
|
|
4976
4886
|
constructor() {
|
|
4977
4887
|
super(...arguments);
|
|
4888
|
+
_defineProperty(this, "buttonSizeStyle", void 0);
|
|
4978
4889
|
_defineProperty(this, "_preInjectStyles", () => {
|
|
4979
4890
|
// HACK(charlie): Pre-inject all of the possible styles for the button.
|
|
4980
4891
|
// This avoids a flickering effect in the echo animation whereby the
|
|
@@ -5023,10 +4934,12 @@ class KeypadButton extends React__namespace.PureComponent {
|
|
|
5023
4934
|
break;
|
|
5024
4935
|
}
|
|
5025
4936
|
const borderStyle = [];
|
|
5026
|
-
if (borders.
|
|
4937
|
+
if (borders.includes(BorderDirections.LEFT)) {
|
|
4938
|
+
// @ts-expect-error TS2345
|
|
5027
4939
|
borderStyle.push(styles$8.leftBorder);
|
|
5028
4940
|
}
|
|
5029
|
-
if (borders.
|
|
4941
|
+
if (borders.includes(BorderDirections.BOTTOM)) {
|
|
4942
|
+
// @ts-expect-error TS2345
|
|
5030
4943
|
borderStyle.push(styles$8.bottomBorder);
|
|
5031
4944
|
}
|
|
5032
4945
|
return [styles$8.buttonBase, backgroundStyle, ...borderStyle, type === KeyTypes.ECHO && styles$8.echo, this.buttonSizeStyle,
|
|
@@ -5074,7 +4987,7 @@ class KeypadButton extends React__namespace.PureComponent {
|
|
|
5074
4987
|
const renderFocused = !disabled && focused || popoverEnabled || type === KeyTypes.ECHO;
|
|
5075
4988
|
const buttonStyle = this._getButtonStyle(type, borders, style);
|
|
5076
4989
|
const focusStyle = this._getFocusStyle(type);
|
|
5077
|
-
const iconWrapperStyle = [styles$8.iconWrapper, disabled
|
|
4990
|
+
const iconWrapperStyle = [styles$8.iconWrapper, disabled ? styles$8.disabled : undefined];
|
|
5078
4991
|
const eventHandlers = {
|
|
5079
4992
|
onTouchCancel,
|
|
5080
4993
|
onTouchEnd,
|
|
@@ -5125,32 +5038,6 @@ class KeypadButton extends React__namespace.PureComponent {
|
|
|
5125
5038
|
}
|
|
5126
5039
|
}
|
|
5127
5040
|
}
|
|
5128
|
-
_defineProperty(KeypadButton, "propTypes", {
|
|
5129
|
-
ariaLabel: PropTypes__default["default"].string,
|
|
5130
|
-
// The borders to display on the button. Typically, this should be set
|
|
5131
|
-
// using one of the preset `BorderStyles` options.
|
|
5132
|
-
borders: bordersPropType,
|
|
5133
|
-
// Any additional keys that can be accessed by long-pressing on the
|
|
5134
|
-
// button.
|
|
5135
|
-
childKeys: PropTypes__default["default"].arrayOf(keyConfigPropType),
|
|
5136
|
-
// Whether the button should be rendered in a 'disabled' state, i.e.,
|
|
5137
|
-
// without any touch feedback.
|
|
5138
|
-
disabled: PropTypes__default["default"].bool,
|
|
5139
|
-
focused: PropTypes__default["default"].bool,
|
|
5140
|
-
heightPx: PropTypes__default["default"].number.isRequired,
|
|
5141
|
-
icon: iconPropType,
|
|
5142
|
-
onTouchCancel: PropTypes__default["default"].func,
|
|
5143
|
-
onTouchEnd: PropTypes__default["default"].func,
|
|
5144
|
-
onTouchMove: PropTypes__default["default"].func,
|
|
5145
|
-
onTouchStart: PropTypes__default["default"].func,
|
|
5146
|
-
popoverEnabled: PropTypes__default["default"].bool,
|
|
5147
|
-
style: PropTypes__default["default"].any,
|
|
5148
|
-
type: PropTypes__default["default"].oneOf(Object.keys(KeyTypes)).isRequired,
|
|
5149
|
-
// NOTE(charlie): We may want to make this optional for phone layouts
|
|
5150
|
-
// (and rely on Flexbox instead), since it might not be pixel perfect
|
|
5151
|
-
// with borders and such.
|
|
5152
|
-
widthPx: PropTypes__default["default"].number.isRequired
|
|
5153
|
-
});
|
|
5154
5041
|
_defineProperty(KeypadButton, "defaultProps", {
|
|
5155
5042
|
borders: BorderStyles.ALL,
|
|
5156
5043
|
childKeys: [],
|
|
@@ -5162,8 +5049,6 @@ const focusInsetPx = 4;
|
|
|
5162
5049
|
const focusBoxZIndex = 0;
|
|
5163
5050
|
const styles$8 = aphrodite.StyleSheet.create({
|
|
5164
5051
|
buttonBase: {
|
|
5165
|
-
// HACK(benkomalo): support old style flex box in Android browsers
|
|
5166
|
-
"-webkit-box-flex": "1",
|
|
5167
5052
|
flex: 1,
|
|
5168
5053
|
cursor: "pointer",
|
|
5169
5054
|
// Make the text unselectable
|
|
@@ -5239,7 +5124,10 @@ const styleForButtonDimensions = (heightPx, widthPx) => {
|
|
|
5239
5124
|
}).buttonSize;
|
|
5240
5125
|
};
|
|
5241
5126
|
const mapStateToProps$7 = state => {
|
|
5242
|
-
return
|
|
5127
|
+
return {
|
|
5128
|
+
heightPx: state.layout.buttonDimensions.heightPx,
|
|
5129
|
+
widthPx: state.layout.buttonDimensions.widthPx
|
|
5130
|
+
};
|
|
5243
5131
|
};
|
|
5244
5132
|
var KeypadButton$1 = reactRedux.connect(mapStateToProps$7, null, null, {
|
|
5245
5133
|
forwardRef: true
|
|
@@ -5265,9 +5153,6 @@ class EmptyKeypadButton extends React__namespace.Component {
|
|
|
5265
5153
|
}, KeyConfigs.NOOP, rest));
|
|
5266
5154
|
}
|
|
5267
5155
|
}
|
|
5268
|
-
_defineProperty(EmptyKeypadButton, "propTypes", {
|
|
5269
|
-
gestureManager: PropTypes__default["default"].instanceOf(GestureManager)
|
|
5270
|
-
});
|
|
5271
5156
|
const mapStateToProps$6 = state => {
|
|
5272
5157
|
const {
|
|
5273
5158
|
gestures
|
|
@@ -5324,17 +5209,6 @@ class TouchableKeypadButton extends React__namespace.Component {
|
|
|
5324
5209
|
}, eventHandlers, rest));
|
|
5325
5210
|
}
|
|
5326
5211
|
}
|
|
5327
|
-
_defineProperty(TouchableKeypadButton, "propTypes", {
|
|
5328
|
-
borders: bordersPropType,
|
|
5329
|
-
childKeyIds: PropTypes__default["default"].arrayOf(keyIdPropType),
|
|
5330
|
-
disabled: PropTypes__default["default"].bool,
|
|
5331
|
-
focused: PropTypes__default["default"].bool,
|
|
5332
|
-
gestureManager: PropTypes__default["default"].instanceOf(GestureManager),
|
|
5333
|
-
id: keyIdPropType.isRequired,
|
|
5334
|
-
popoverEnabled: PropTypes__default["default"].bool,
|
|
5335
|
-
style: PropTypes__default["default"].any,
|
|
5336
|
-
type: PropTypes__default["default"].oneOf(Object.keys(KeyTypes)).isRequired
|
|
5337
|
-
});
|
|
5338
5212
|
const extractProps = keyConfig => {
|
|
5339
5213
|
const {
|
|
5340
5214
|
ariaLabel,
|
|
@@ -5400,7 +5274,7 @@ class ManyKeypadButton extends React__namespace.Component {
|
|
|
5400
5274
|
// one, render a standard button. Otherwise, capture them all in a
|
|
5401
5275
|
// single button.
|
|
5402
5276
|
if (keys.length === 0) {
|
|
5403
|
-
return /*#__PURE__*/React__namespace.createElement(EmptyKeypadButton$1,
|
|
5277
|
+
return /*#__PURE__*/React__namespace.createElement(EmptyKeypadButton$1, null);
|
|
5404
5278
|
} else if (keys.length === 1) {
|
|
5405
5279
|
const keyConfig = KeyConfigs[keys[0]];
|
|
5406
5280
|
return /*#__PURE__*/React__namespace.createElement(TouchableKeypadButton$1, _extends({
|
|
@@ -5418,8 +5292,8 @@ class ManyKeypadButton extends React__namespace.Component {
|
|
|
5418
5292
|
}
|
|
5419
5293
|
}
|
|
5420
5294
|
}
|
|
5421
|
-
_defineProperty(ManyKeypadButton, "
|
|
5422
|
-
keys:
|
|
5295
|
+
_defineProperty(ManyKeypadButton, "defaultProps", {
|
|
5296
|
+
keys: []
|
|
5423
5297
|
});
|
|
5424
5298
|
|
|
5425
5299
|
/**
|
|
@@ -5468,20 +5342,12 @@ class Echo extends React__namespace.Component {
|
|
|
5468
5342
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
5469
5343
|
style: containerStyle
|
|
5470
5344
|
}, /*#__PURE__*/React__namespace.createElement(KeypadButton$1, {
|
|
5471
|
-
name: id,
|
|
5472
5345
|
icon: icon,
|
|
5473
5346
|
type: KeyTypes.ECHO,
|
|
5474
5347
|
borders: borders
|
|
5475
5348
|
}));
|
|
5476
5349
|
}
|
|
5477
5350
|
}
|
|
5478
|
-
_defineProperty(Echo, "propTypes", {
|
|
5479
|
-
animationDurationMs: PropTypes__default["default"].number.isRequired,
|
|
5480
|
-
borders: bordersPropType,
|
|
5481
|
-
id: keyIdPropType.isRequired,
|
|
5482
|
-
initialBounds: boundingBoxPropType.isRequired,
|
|
5483
|
-
onAnimationFinish: PropTypes__default["default"].func.isRequired
|
|
5484
|
-
});
|
|
5485
5351
|
class EchoManager extends React__namespace.Component {
|
|
5486
5352
|
constructor() {
|
|
5487
5353
|
super(...arguments);
|
|
@@ -5504,7 +5370,7 @@ class EchoManager extends React__namespace.Component {
|
|
|
5504
5370
|
animationTransitionName = "echo-long-fade-only";
|
|
5505
5371
|
break;
|
|
5506
5372
|
default:
|
|
5507
|
-
throw new Error("Invalid echo animation type:"
|
|
5373
|
+
throw new Error("Invalid echo animation type: ".concat(animationType));
|
|
5508
5374
|
}
|
|
5509
5375
|
return {
|
|
5510
5376
|
animationDurationMs,
|
|
@@ -5549,17 +5415,16 @@ class EchoManager extends React__namespace.Component {
|
|
|
5549
5415
|
key: animationId
|
|
5550
5416
|
}, /*#__PURE__*/React__namespace.createElement(Echo, _extends({
|
|
5551
5417
|
animationDurationMs: animationDurationMs,
|
|
5552
|
-
onAnimationFinish: () => onAnimationFinish(animationId)
|
|
5418
|
+
onAnimationFinish: () => onAnimationFinish === null || onAnimationFinish === void 0 ? void 0 : onAnimationFinish(animationId)
|
|
5553
5419
|
}, echo)));
|
|
5554
5420
|
}));
|
|
5555
5421
|
}));
|
|
5556
5422
|
}
|
|
5557
5423
|
}
|
|
5558
|
-
_defineProperty(EchoManager, "propTypes", {
|
|
5559
|
-
echoes: PropTypes__default["default"].arrayOf(echoPropType),
|
|
5560
|
-
onAnimationFinish: PropTypes__default["default"].func.isRequired
|
|
5561
|
-
});
|
|
5562
5424
|
|
|
5425
|
+
/**
|
|
5426
|
+
* A popover that renders a set of keys floating above the page.
|
|
5427
|
+
*/
|
|
5563
5428
|
class MultiSymbolPopover extends React__namespace.Component {
|
|
5564
5429
|
render() {
|
|
5565
5430
|
const {
|
|
@@ -5579,9 +5444,6 @@ class MultiSymbolPopover extends React__namespace.Component {
|
|
|
5579
5444
|
}));
|
|
5580
5445
|
}
|
|
5581
5446
|
}
|
|
5582
|
-
_defineProperty(MultiSymbolPopover, "propTypes", {
|
|
5583
|
-
keys: PropTypes__default["default"].arrayOf(keyConfigPropType)
|
|
5584
|
-
});
|
|
5585
5447
|
const styles$6 = aphrodite.StyleSheet.create({
|
|
5586
5448
|
container: {
|
|
5587
5449
|
flexDirection: "column-reverse",
|
|
@@ -5598,11 +5460,14 @@ const styles$6 = aphrodite.StyleSheet.create({
|
|
|
5598
5460
|
}
|
|
5599
5461
|
});
|
|
5600
5462
|
|
|
5463
|
+
/**
|
|
5464
|
+
* A component that renders and animates the popovers that appear over the
|
|
5465
|
+
* multi-functional keys.
|
|
5466
|
+
*/
|
|
5601
5467
|
// NOTE(charlie): These must be kept in sync with the transition durations and
|
|
5602
5468
|
// classnames specified in popover.less.
|
|
5603
5469
|
const animationTransitionName = "popover";
|
|
5604
5470
|
const animationDurationMs = 200;
|
|
5605
|
-
|
|
5606
5471
|
// A container component used to position a popover absolutely at a specific
|
|
5607
5472
|
// position.
|
|
5608
5473
|
class PopoverContainer extends React__namespace.Component {
|
|
@@ -5622,10 +5487,6 @@ class PopoverContainer extends React__namespace.Component {
|
|
|
5622
5487
|
}));
|
|
5623
5488
|
}
|
|
5624
5489
|
}
|
|
5625
|
-
_defineProperty(PopoverContainer, "propTypes", {
|
|
5626
|
-
bounds: boundingBoxPropType.isRequired,
|
|
5627
|
-
childKeys: PropTypes__default["default"].arrayOf(keyConfigPropType).isRequired
|
|
5628
|
-
});
|
|
5629
5490
|
class PopoverManager extends React__namespace.Component {
|
|
5630
5491
|
render() {
|
|
5631
5492
|
const {
|
|
@@ -5646,14 +5507,14 @@ class PopoverManager extends React__namespace.Component {
|
|
|
5646
5507
|
})) : null;
|
|
5647
5508
|
}
|
|
5648
5509
|
}
|
|
5649
|
-
_defineProperty(PopoverManager, "propTypes", {
|
|
5650
|
-
popover: popoverPropType
|
|
5651
|
-
});
|
|
5652
5510
|
|
|
5653
5511
|
// eslint-disable-next-line react/no-unsafe
|
|
5654
5512
|
class Keypad extends React__namespace.Component {
|
|
5655
5513
|
constructor() {
|
|
5656
5514
|
super(...arguments);
|
|
5515
|
+
_defineProperty(this, "_isMounted", void 0);
|
|
5516
|
+
_defineProperty(this, "_resizeTimeout", void 0);
|
|
5517
|
+
_defineProperty(this, "_container", void 0);
|
|
5657
5518
|
_defineProperty(this, "_computeContainer", () => {
|
|
5658
5519
|
const domNode = ReactDOM__default["default"].findDOMNode(this);
|
|
5659
5520
|
this._container = domNode.getBoundingClientRect();
|
|
@@ -5675,7 +5536,7 @@ class Keypad extends React__namespace.Component {
|
|
|
5675
5536
|
// Throttle resize events -- taken from:
|
|
5676
5537
|
// https://developer.mozilla.org/en-US/docs/Web/Events/resize
|
|
5677
5538
|
if (this._resizeTimeout == null) {
|
|
5678
|
-
this._resizeTimeout = setTimeout(() => {
|
|
5539
|
+
this._resizeTimeout = window.setTimeout(() => {
|
|
5679
5540
|
this._resizeTimeout = null;
|
|
5680
5541
|
if (this._isMounted) {
|
|
5681
5542
|
this._updateSizeAndPosition();
|
|
@@ -5717,9 +5578,13 @@ class Keypad extends React__namespace.Component {
|
|
|
5717
5578
|
return {
|
|
5718
5579
|
...rest,
|
|
5719
5580
|
initialBounds: {
|
|
5581
|
+
// @ts-expect-error TS2533
|
|
5720
5582
|
top: initialBounds.top - this._container.top,
|
|
5583
|
+
// @ts-expect-error TS2533
|
|
5721
5584
|
right: initialBounds.right - this._container.left,
|
|
5585
|
+
// @ts-expect-error TS2533
|
|
5722
5586
|
bottom: initialBounds.bottom - this._container.top,
|
|
5587
|
+
// @ts-expect-error TS2533
|
|
5723
5588
|
left: initialBounds.left - this._container.left,
|
|
5724
5589
|
width: initialBounds.width,
|
|
5725
5590
|
height: initialBounds.height
|
|
@@ -5733,7 +5598,12 @@ class Keypad extends React__namespace.Component {
|
|
|
5733
5598
|
const relativePopover = popover && {
|
|
5734
5599
|
...popover,
|
|
5735
5600
|
bounds: {
|
|
5736
|
-
bottom:
|
|
5601
|
+
bottom:
|
|
5602
|
+
// @ts-expect-error TS2533
|
|
5603
|
+
this._container.height - (
|
|
5604
|
+
// @ts-expect-error TS2533
|
|
5605
|
+
popover.bounds.bottom - this._container.top),
|
|
5606
|
+
// @ts-expect-error TS2533
|
|
5737
5607
|
left: popover.bounds.left - this._container.left,
|
|
5738
5608
|
width: popover.bounds.width
|
|
5739
5609
|
}
|
|
@@ -5748,21 +5618,9 @@ class Keypad extends React__namespace.Component {
|
|
|
5748
5618
|
}));
|
|
5749
5619
|
}
|
|
5750
5620
|
}
|
|
5751
|
-
_defineProperty(Keypad, "propTypes", {
|
|
5752
|
-
children: PropTypes__default["default"].oneOfType([PropTypes__default["default"].arrayOf(PropTypes__default["default"].node), PropTypes__default["default"].node]),
|
|
5753
|
-
removeEcho: PropTypes__default["default"].func.isRequired,
|
|
5754
|
-
style: PropTypes__default["default"].any,
|
|
5755
|
-
// The props below are injected by redux
|
|
5756
|
-
|
|
5757
|
-
// Whether the keypad is active, i.e., whether it should be rendered as
|
|
5758
|
-
// visible or invisible.
|
|
5759
|
-
active: PropTypes__default["default"].bool,
|
|
5760
|
-
echoes: PropTypes__default["default"].arrayOf(echoPropType).isRequired,
|
|
5761
|
-
popover: popoverPropType
|
|
5762
|
-
});
|
|
5763
5621
|
const mapStateToProps$4 = state => {
|
|
5764
5622
|
return {
|
|
5765
|
-
|
|
5623
|
+
echoes: state.echoes.echoes,
|
|
5766
5624
|
active: state.keypad.active,
|
|
5767
5625
|
popover: state.gestures.popover
|
|
5768
5626
|
};
|
|
@@ -6000,12 +5858,6 @@ class TwoPageKeypad extends React__namespace.Component {
|
|
|
6000
5858
|
}
|
|
6001
5859
|
}
|
|
6002
5860
|
}
|
|
6003
|
-
_defineProperty(TwoPageKeypad, "propTypes", {
|
|
6004
|
-
currentPage: PropTypes__default["default"].oneOf([0, 1]).isRequired,
|
|
6005
|
-
leftPage: PropTypes__default["default"].node.isRequired,
|
|
6006
|
-
paginationEnabled: PropTypes__default["default"].bool.isRequired,
|
|
6007
|
-
rightPage: PropTypes__default["default"].node.isRequired
|
|
6008
|
-
});
|
|
6009
5861
|
const styles$3 = aphrodite.StyleSheet.create({
|
|
6010
5862
|
keypad: {
|
|
6011
5863
|
// Set the background to light grey, so that when the user drags the
|
|
@@ -6029,6 +5881,9 @@ var TwoPageKeypad$1 = reactRedux.connect(mapStateToProps$3, null, null, {
|
|
|
6029
5881
|
forwardRef: true
|
|
6030
5882
|
})(TwoPageKeypad);
|
|
6031
5883
|
|
|
5884
|
+
/**
|
|
5885
|
+
* A keypad that includes all of the expression symbols.
|
|
5886
|
+
*/
|
|
6032
5887
|
const {
|
|
6033
5888
|
row: row$3,
|
|
6034
5889
|
column: column$1,
|
|
@@ -6037,11 +5892,16 @@ const {
|
|
|
6037
5892
|
roundedTopLeft: roundedTopLeft$2,
|
|
6038
5893
|
roundedTopRight: roundedTopRight$1
|
|
6039
5894
|
} = Styles;
|
|
5895
|
+
const expressionKeypadLayout = {
|
|
5896
|
+
rows: 4,
|
|
5897
|
+
columns: 5,
|
|
5898
|
+
numPages: 2,
|
|
5899
|
+
// Since we include a two-key popover in the top-right, when the popover
|
|
5900
|
+
// is visible, the keypad will expand to fill the equivalent of five
|
|
5901
|
+
// rows vertically.
|
|
5902
|
+
maxVisibleRows: 4
|
|
5903
|
+
};
|
|
6040
5904
|
class ExpressionKeypad extends React__namespace.Component {
|
|
6041
|
-
// Though we include an infinite-key popover in the bottom-left, it's
|
|
6042
|
-
// assumed that we don't need to accommodate cases in which that key
|
|
6043
|
-
// contains more than four children.
|
|
6044
|
-
|
|
6045
5905
|
render() {
|
|
6046
5906
|
const {
|
|
6047
5907
|
currentPage,
|
|
@@ -6095,8 +5955,7 @@ class ExpressionKeypad extends React__namespace.Component {
|
|
|
6095
5955
|
keyConfig: KeyConfigs.NUM_1,
|
|
6096
5956
|
borders: BorderStyles.BOTTOM
|
|
6097
5957
|
}), /*#__PURE__*/React__namespace.createElement(ManyKeypadButton, {
|
|
6098
|
-
keys: extraKeys
|
|
6099
|
-
borders: BorderStyles.NONE
|
|
5958
|
+
keys: extraKeys
|
|
6100
5959
|
})), /*#__PURE__*/React__namespace.createElement(View, {
|
|
6101
5960
|
style: [column$1, oneColumn]
|
|
6102
5961
|
}, /*#__PURE__*/React__namespace.createElement(TouchableKeypadButton$1, {
|
|
@@ -6234,18 +6093,6 @@ class ExpressionKeypad extends React__namespace.Component {
|
|
|
6234
6093
|
});
|
|
6235
6094
|
}
|
|
6236
6095
|
}
|
|
6237
|
-
_defineProperty(ExpressionKeypad, "propTypes", {
|
|
6238
|
-
currentPage: PropTypes__default["default"].number.isRequired,
|
|
6239
|
-
cursorContext: cursorContextPropType.isRequired,
|
|
6240
|
-
dynamicJumpOut: PropTypes__default["default"].bool,
|
|
6241
|
-
extraKeys: PropTypes__default["default"].arrayOf(keyIdPropType),
|
|
6242
|
-
roundTopLeft: PropTypes__default["default"].bool,
|
|
6243
|
-
roundTopRight: PropTypes__default["default"].bool
|
|
6244
|
-
});
|
|
6245
|
-
_defineProperty(ExpressionKeypad, "rows", 4);
|
|
6246
|
-
_defineProperty(ExpressionKeypad, "columns", 5);
|
|
6247
|
-
_defineProperty(ExpressionKeypad, "maxVisibleRows", 4);
|
|
6248
|
-
_defineProperty(ExpressionKeypad, "numPages", 2);
|
|
6249
6096
|
const styles$2 = aphrodite.StyleSheet.create({
|
|
6250
6097
|
// NOTE(charlie): These backgrounds are applied to as to fill in some
|
|
6251
6098
|
// unfortunate 'cracks' in the layout. However, not all keys in the first
|
|
@@ -6260,9 +6107,10 @@ const styles$2 = aphrodite.StyleSheet.create({
|
|
|
6260
6107
|
}
|
|
6261
6108
|
});
|
|
6262
6109
|
const mapStateToProps$2 = state => {
|
|
6110
|
+
var _state$input$cursor;
|
|
6263
6111
|
return {
|
|
6264
6112
|
currentPage: state.pager.currentPage,
|
|
6265
|
-
cursorContext: state.input.cursor.context,
|
|
6113
|
+
cursorContext: (_state$input$cursor = state.input.cursor) === null || _state$input$cursor === void 0 ? void 0 : _state$input$cursor.context,
|
|
6266
6114
|
dynamicJumpOut: !state.layout.navigationPadEnabled
|
|
6267
6115
|
};
|
|
6268
6116
|
};
|
|
@@ -6270,16 +6118,25 @@ var ExpressionKeypad$1 = reactRedux.connect(mapStateToProps$2, null, null, {
|
|
|
6270
6118
|
forwardRef: true
|
|
6271
6119
|
})(ExpressionKeypad);
|
|
6272
6120
|
|
|
6121
|
+
/**
|
|
6122
|
+
* A keypad that includes the digits, as well as the symbols required to deal
|
|
6123
|
+
* with fractions, decimals, and percents.
|
|
6124
|
+
*/
|
|
6273
6125
|
const {
|
|
6274
6126
|
row: row$2,
|
|
6275
6127
|
roundedTopLeft: roundedTopLeft$1,
|
|
6276
6128
|
roundedTopRight
|
|
6277
6129
|
} = Styles;
|
|
6278
|
-
|
|
6130
|
+
const fractionKeypadLayout = {
|
|
6131
|
+
rows: 4,
|
|
6132
|
+
columns: 4,
|
|
6133
|
+
numPages: 1,
|
|
6279
6134
|
// Since we include a two-key popover in the top-right, when the popover
|
|
6280
6135
|
// is visible, the keypad will expand to fill the equivalent of five
|
|
6281
6136
|
// rows vertically.
|
|
6282
|
-
|
|
6137
|
+
maxVisibleRows: 5
|
|
6138
|
+
};
|
|
6139
|
+
class FractionKeypad extends React__namespace.Component {
|
|
6283
6140
|
render() {
|
|
6284
6141
|
const {
|
|
6285
6142
|
cursorContext,
|
|
@@ -6382,35 +6239,308 @@ class FractionKeypad extends React__namespace.Component {
|
|
|
6382
6239
|
borders: BorderStyles.LEFT
|
|
6383
6240
|
})));
|
|
6384
6241
|
}
|
|
6385
|
-
}
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
}
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6242
|
+
}
|
|
6243
|
+
const mapStateToProps$1 = state => {
|
|
6244
|
+
var _state$input$cursor;
|
|
6245
|
+
return {
|
|
6246
|
+
cursorContext: (_state$input$cursor = state.input.cursor) === null || _state$input$cursor === void 0 ? void 0 : _state$input$cursor.context,
|
|
6247
|
+
dynamicJumpOut: !state.layout.navigationPadEnabled
|
|
6248
|
+
};
|
|
6249
|
+
};
|
|
6250
|
+
var FractionKeypad$1 = reactRedux.connect(mapStateToProps$1, null, null, {
|
|
6251
|
+
forwardRef: true
|
|
6252
|
+
})(FractionKeypad);
|
|
6253
|
+
|
|
6254
|
+
const defaultKeypadType = KeypadTypes.EXPRESSION;
|
|
6255
|
+
const keypadForType = {
|
|
6256
|
+
[KeypadTypes.FRACTION]: fractionKeypadLayout,
|
|
6257
|
+
[KeypadTypes.EXPRESSION]: expressionKeypadLayout
|
|
6258
|
+
};
|
|
6259
|
+
|
|
6260
|
+
const initialKeypadState = {
|
|
6261
|
+
extraKeys: ["x", "y", Keys.THETA, Keys.PI],
|
|
6262
|
+
keypadType: defaultKeypadType,
|
|
6263
|
+
active: false
|
|
6264
|
+
};
|
|
6265
|
+
const keypadReducer = function () {
|
|
6266
|
+
let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialKeypadState;
|
|
6267
|
+
let action = arguments.length > 1 ? arguments[1] : undefined;
|
|
6268
|
+
switch (action.type) {
|
|
6269
|
+
case "DismissKeypad":
|
|
6270
|
+
return {
|
|
6271
|
+
...state,
|
|
6272
|
+
active: false
|
|
6273
|
+
};
|
|
6274
|
+
case "ActivateKeypad":
|
|
6275
|
+
return {
|
|
6276
|
+
...state,
|
|
6277
|
+
active: true
|
|
6278
|
+
};
|
|
6279
|
+
case "ConfigureKeypad":
|
|
6280
|
+
return {
|
|
6281
|
+
...state,
|
|
6282
|
+
// Default `extraKeys` to the empty array.
|
|
6283
|
+
extraKeys: [],
|
|
6284
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'configuration' does not exist on type '{ type: string; }'.
|
|
6285
|
+
...action.configuration
|
|
6286
|
+
};
|
|
6287
|
+
case "PressKey":
|
|
6288
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
|
|
6289
|
+
const keyConfig = KeyConfigs[action.key];
|
|
6290
|
+
// NOTE(charlie): Our keypad system operates by triggering key
|
|
6291
|
+
// presses with key IDs in a dumb manner, such that the keys
|
|
6292
|
+
// don't know what they can do--instead, the store is
|
|
6293
|
+
// responsible for interpreting key presses and triggering the
|
|
6294
|
+
// right actions when they occur. Hence, we figure off a
|
|
6295
|
+
// dismissal here rather than dispatching a dismiss action in
|
|
6296
|
+
// the first place.
|
|
6297
|
+
if (keyConfig.id === Keys.DISMISS) {
|
|
6298
|
+
return keypadReducer(state, {
|
|
6299
|
+
type: "DismissKeypad"
|
|
6300
|
+
});
|
|
6301
|
+
}
|
|
6302
|
+
return state;
|
|
6303
|
+
default:
|
|
6304
|
+
return state;
|
|
6305
|
+
}
|
|
6306
|
+
};
|
|
6307
|
+
|
|
6308
|
+
/**
|
|
6309
|
+
* An algorithm for computing the appropriate layout parameters for the keypad,
|
|
6310
|
+
* including the size of the buttons and whether or not to render fullscreen,
|
|
6311
|
+
* taking into account a number of factors including the size of the screen, the
|
|
6312
|
+
* orientation of the screen, the presence of browser chrome, the presence of
|
|
6313
|
+
* other exercise-related chrome, the size of the input box, the parameters that
|
|
6314
|
+
* define the keypad (i.e., the number of rows, columns, and pages), and so
|
|
6315
|
+
* forth.
|
|
6316
|
+
*
|
|
6317
|
+
* The computations herein make some strong assumptions about the sizes of
|
|
6318
|
+
* various other elements and the situations under which they will be visible
|
|
6319
|
+
* (e.g., browser chrome). However, this is just a heuristic--it's not crucial
|
|
6320
|
+
* that our buttons are sized in a pixel-perfect manner, but rather, that we
|
|
6321
|
+
* make a balanced use of space.
|
|
6322
|
+
*
|
|
6323
|
+
* Note that one goal of the algorithm is to avoid resizing the keypad in the
|
|
6324
|
+
* face of dynamic browser chrome. In order to avoid that awkwardness, we tend
|
|
6325
|
+
* to be conservative in our measurements and make things smaller than they
|
|
6326
|
+
* might need to be.
|
|
6327
|
+
*/
|
|
6328
|
+
const minButtonHeight = 48;
|
|
6329
|
+
const maxButtonSize = 64;
|
|
6330
|
+
const minSpaceAboveKeypad = 32;
|
|
6331
|
+
|
|
6332
|
+
// These values are taken from an iPhone 5, but should be consistent with the
|
|
6333
|
+
// iPhone 4 as well. Regardless, these are meant to be representative of the
|
|
6334
|
+
// possible types of browser chrome that could appear in various context, rather
|
|
6335
|
+
// than pixel-perfect for every device.
|
|
6336
|
+
const safariNavBarWhenShrunk = 44;
|
|
6337
|
+
const safariNavBarWhenExpanded = 64;
|
|
6338
|
+
const safariToolbar = 44;
|
|
6339
|
+
|
|
6340
|
+
// In mobile Safari, the browser chrome is completely hidden in landscape,
|
|
6341
|
+
// though a shrunken navbar and full-sized toolbar on scroll. In portrait, the
|
|
6342
|
+
// shrunken navbar is always visible, but expands on scroll (and the toolbar
|
|
6343
|
+
// appears as well).
|
|
6344
|
+
const maxLandscapeBrowserChrome = safariNavBarWhenShrunk + safariToolbar;
|
|
6345
|
+
const maxPortraitBrowserChrome = safariToolbar + (safariNavBarWhenExpanded - safariNavBarWhenShrunk);
|
|
6346
|
+
|
|
6347
|
+
// This represents the 'worst case' aspect ratio that we care about (for
|
|
6348
|
+
// portrait layouts). It's taken from the iPhone 4. The height is computed by
|
|
6349
|
+
// taking the height of the device and removing the persistent, shrunken navbar.
|
|
6350
|
+
// (We don't need to account for the expanded navbar, since we include the
|
|
6351
|
+
// difference when reserving space above the keypad.)
|
|
6352
|
+
const worstCaseAspectRatio = 320 / (480 - safariNavBarWhenShrunk);
|
|
6353
|
+
const computeLayoutParameters = (_ref, _ref2, _ref3, _ref4) => {
|
|
6354
|
+
let {
|
|
6355
|
+
numColumns,
|
|
6356
|
+
numMaxVisibleRows,
|
|
6357
|
+
numPages
|
|
6358
|
+
} = _ref;
|
|
6359
|
+
let {
|
|
6360
|
+
pageWidthPx,
|
|
6361
|
+
pageHeightPx
|
|
6362
|
+
} = _ref2;
|
|
6363
|
+
let {
|
|
6364
|
+
deviceOrientation,
|
|
6365
|
+
deviceType
|
|
6366
|
+
} = _ref3;
|
|
6367
|
+
let {
|
|
6368
|
+
navigationPadEnabled,
|
|
6369
|
+
paginationEnabled,
|
|
6370
|
+
toolbarEnabled
|
|
6371
|
+
} = _ref4;
|
|
6372
|
+
// First, compute some values that will be used in multiple computations.
|
|
6373
|
+
const effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
|
|
6374
|
+
|
|
6375
|
+
// Then, compute the button dimensions based on the provided parameters.
|
|
6376
|
+
let buttonDimensions;
|
|
6377
|
+
if (deviceType === DeviceTypes.PHONE) {
|
|
6378
|
+
const isLandscape = deviceOrientation === DeviceOrientations.LANDSCAPE;
|
|
6379
|
+
|
|
6380
|
+
// In many cases, the browser chrome will already have been factored
|
|
6381
|
+
// into `pageHeightPx`. But we have no way of knowing if that's
|
|
6382
|
+
// the case or not. As such, we take a conservative approach and
|
|
6383
|
+
// assume that the chrome is _never_ included in `pageHeightPx`.
|
|
6384
|
+
const browserChromeHeight = isLandscape ? maxLandscapeBrowserChrome : maxPortraitBrowserChrome;
|
|
6385
|
+
|
|
6386
|
+
// Count up all the space that we need to reserve on the page.
|
|
6387
|
+
// Namely, we need to account for:
|
|
6388
|
+
// 1. Space between the keypad and the top of the page.
|
|
6389
|
+
// 2. The presence of the exercise toolbar.
|
|
6390
|
+
// 3. The presence of the view pager indicator.
|
|
6391
|
+
// 4. Any browser chrome that may appear later.
|
|
6392
|
+
const reservedSpace = minSpaceAboveKeypad + browserChromeHeight + (toolbarEnabled ? toolbarHeightPx : 0) + (paginationEnabled ? pageIndicatorHeightPx : 0);
|
|
6393
|
+
|
|
6394
|
+
// Next, compute the effective width and height. We can use the page
|
|
6395
|
+
// width as the effective width. For the height, though, we take
|
|
6396
|
+
// another conservative measure when in portrait by assuming that
|
|
6397
|
+
// the device has the worst possible aspect ratio. In other words,
|
|
6398
|
+
// we ignore the device height in portrait and assume the worst.
|
|
6399
|
+
// This prevents the keypad from changing size when browser chrome
|
|
6400
|
+
// appears and disappears.
|
|
6401
|
+
const effectiveWidth = pageWidthPx;
|
|
6402
|
+
const effectiveHeight = isLandscape ? pageHeightPx : pageWidthPx / worstCaseAspectRatio;
|
|
6403
|
+
const maxKeypadHeight = effectiveHeight - reservedSpace;
|
|
6404
|
+
|
|
6405
|
+
// Finally, compute the button height and width. In computing the
|
|
6406
|
+
// height, accommodate for the maximum number of rows that will ever be
|
|
6407
|
+
// visible (since the toggling of popovers can increase the number of
|
|
6408
|
+
// visible rows).
|
|
6409
|
+
const buttonHeightPx = Math.max(Math.min(maxKeypadHeight / numMaxVisibleRows, maxButtonSize), minButtonHeight);
|
|
6410
|
+
let buttonWidthPx;
|
|
6411
|
+
if (numPages > 1) {
|
|
6412
|
+
const effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
|
|
6413
|
+
buttonWidthPx = effectiveWidth / effectiveNumColumns;
|
|
6414
|
+
} else {
|
|
6415
|
+
buttonWidthPx = isLandscape ? maxButtonSize : effectiveWidth / numColumns;
|
|
6416
|
+
}
|
|
6417
|
+
buttonDimensions = {
|
|
6418
|
+
widthPx: buttonWidthPx,
|
|
6419
|
+
heightPx: buttonHeightPx
|
|
6420
|
+
};
|
|
6421
|
+
} else if (deviceType === DeviceTypes.TABLET) {
|
|
6422
|
+
buttonDimensions = {
|
|
6423
|
+
widthPx: maxButtonSize,
|
|
6424
|
+
heightPx: maxButtonSize
|
|
6425
|
+
};
|
|
6426
|
+
} else {
|
|
6427
|
+
throw new Error("Invalid device type: " + deviceType);
|
|
6428
|
+
}
|
|
6429
|
+
|
|
6430
|
+
// Finally, determine whether the keypad should be rendered in the
|
|
6431
|
+
// fullscreen layout by determining its resultant width.
|
|
6432
|
+
const numSeparators = (navigationPadEnabled ? 1 : 0) + (!paginationEnabled ? numPages - 1 : 0);
|
|
6433
|
+
const keypadWidth = effectiveNumColumns * buttonDimensions.widthPx + (navigationPadEnabled ? navigationPadWidthPx : 0) + numSeparators * innerBorderWidthPx;
|
|
6397
6434
|
return {
|
|
6398
|
-
|
|
6399
|
-
|
|
6435
|
+
buttonDimensions,
|
|
6436
|
+
layoutMode: keypadWidth >= pageWidthPx ? LayoutModes.FULLSCREEN : LayoutModes.COMPACT
|
|
6400
6437
|
};
|
|
6401
6438
|
};
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6439
|
+
|
|
6440
|
+
const initialLayoutState = {
|
|
6441
|
+
gridDimensions: {
|
|
6442
|
+
numRows: keypadForType[defaultKeypadType].rows,
|
|
6443
|
+
numColumns: keypadForType[defaultKeypadType].columns,
|
|
6444
|
+
numMaxVisibleRows: keypadForType[defaultKeypadType].maxVisibleRows,
|
|
6445
|
+
numPages: keypadForType[defaultKeypadType].numPages
|
|
6446
|
+
},
|
|
6447
|
+
buttonDimensions: {
|
|
6448
|
+
widthPx: 48,
|
|
6449
|
+
heightPx: 48
|
|
6450
|
+
},
|
|
6451
|
+
pageDimensions: {
|
|
6452
|
+
pageWidthPx: 0,
|
|
6453
|
+
pageHeightPx: 0
|
|
6454
|
+
},
|
|
6455
|
+
layoutMode: LayoutModes.FULLSCREEN,
|
|
6456
|
+
paginationEnabled: false,
|
|
6457
|
+
navigationPadEnabled: false
|
|
6458
|
+
};
|
|
6405
6459
|
|
|
6406
6460
|
/**
|
|
6407
|
-
*
|
|
6408
|
-
*
|
|
6409
|
-
* Velocity is computed by smoothing linearly over the gestures that have
|
|
6410
|
-
* occurred in the last 100 milliseconds.
|
|
6461
|
+
* Compute the additional layout state based on the provided page and grid
|
|
6462
|
+
* dimensions.
|
|
6411
6463
|
*/
|
|
6464
|
+
const layoutParametersForDimensions = (pageDimensions, gridDimensions) => {
|
|
6465
|
+
const {
|
|
6466
|
+
pageWidthPx,
|
|
6467
|
+
pageHeightPx
|
|
6468
|
+
} = pageDimensions;
|
|
6469
|
+
|
|
6470
|
+
// Determine the device type and orientation.
|
|
6471
|
+
const deviceOrientation = pageWidthPx > pageHeightPx ? DeviceOrientations.LANDSCAPE : DeviceOrientations.PORTRAIT;
|
|
6472
|
+
const deviceType = Math.min(pageWidthPx, pageHeightPx) > tabletCutoffPx ? DeviceTypes.TABLET : DeviceTypes.PHONE;
|
|
6473
|
+
|
|
6474
|
+
// Using that information, make some decisions (or assumptions)
|
|
6475
|
+
// about the resulting layout.
|
|
6476
|
+
const navigationPadEnabled = deviceType === DeviceTypes.TABLET;
|
|
6477
|
+
const paginationEnabled = deviceType === DeviceTypes.PHONE && deviceOrientation === DeviceOrientations.PORTRAIT;
|
|
6478
|
+
const deviceInfo = {
|
|
6479
|
+
deviceOrientation,
|
|
6480
|
+
deviceType
|
|
6481
|
+
};
|
|
6482
|
+
const layoutOptions = {
|
|
6483
|
+
navigationPadEnabled,
|
|
6484
|
+
paginationEnabled,
|
|
6485
|
+
// HACK(charlie): It's not great that we're making assumptions about
|
|
6486
|
+
// the toolbar (which is rendered by webapp, and should always be
|
|
6487
|
+
// visible and anchored to the bottom of the page for phone and
|
|
6488
|
+
// tablet exercises). But this is primarily a heuristic (the goal is
|
|
6489
|
+
// to preserve a 'good' amount of space between the top of the
|
|
6490
|
+
// keypad and the top of the page) so we afford to have some margin
|
|
6491
|
+
// of error.
|
|
6492
|
+
toolbarEnabled: true
|
|
6493
|
+
};
|
|
6494
|
+
return {
|
|
6495
|
+
...computeLayoutParameters(gridDimensions, pageDimensions, deviceInfo, layoutOptions),
|
|
6496
|
+
// Pass along some of the layout information, so that other
|
|
6497
|
+
// components in the heirarchy can adapt appropriately.
|
|
6498
|
+
navigationPadEnabled,
|
|
6499
|
+
paginationEnabled
|
|
6500
|
+
};
|
|
6501
|
+
};
|
|
6502
|
+
const layoutReducer = function () {
|
|
6503
|
+
let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialLayoutState;
|
|
6504
|
+
let action = arguments.length > 1 ? arguments[1] : undefined;
|
|
6505
|
+
switch (action.type) {
|
|
6506
|
+
case "ConfigureKeypad":
|
|
6507
|
+
const {
|
|
6508
|
+
keypadType
|
|
6509
|
+
} = action.configuration;
|
|
6510
|
+
const gridDimensions = {
|
|
6511
|
+
numRows: keypadForType[keypadType].rows,
|
|
6512
|
+
numColumns: keypadForType[keypadType].columns,
|
|
6513
|
+
numMaxVisibleRows: keypadForType[keypadType].maxVisibleRows,
|
|
6514
|
+
numPages: keypadForType[keypadType].numPages
|
|
6515
|
+
};
|
|
6516
|
+
return {
|
|
6517
|
+
...state,
|
|
6518
|
+
...layoutParametersForDimensions(state.pageDimensions, gridDimensions),
|
|
6519
|
+
gridDimensions
|
|
6520
|
+
};
|
|
6521
|
+
case "SetPageSize":
|
|
6522
|
+
const {
|
|
6523
|
+
pageWidthPx,
|
|
6524
|
+
pageHeightPx
|
|
6525
|
+
} = action;
|
|
6526
|
+
const pageDimensions = {
|
|
6527
|
+
pageWidthPx,
|
|
6528
|
+
pageHeightPx
|
|
6529
|
+
};
|
|
6530
|
+
return {
|
|
6531
|
+
...state,
|
|
6532
|
+
...layoutParametersForDimensions(pageDimensions, state.gridDimensions),
|
|
6533
|
+
pageDimensions
|
|
6534
|
+
};
|
|
6535
|
+
default:
|
|
6536
|
+
return state;
|
|
6537
|
+
}
|
|
6538
|
+
};
|
|
6539
|
+
|
|
6412
6540
|
class VelocityTracker {
|
|
6413
6541
|
constructor(options) {
|
|
6542
|
+
_defineProperty(this, "options", void 0);
|
|
6543
|
+
_defineProperty(this, "_events", void 0);
|
|
6414
6544
|
this.options = {
|
|
6415
6545
|
velocityTimeout: 100,
|
|
6416
6546
|
...options
|
|
@@ -6474,216 +6604,128 @@ class VelocityTracker {
|
|
|
6474
6604
|
}
|
|
6475
6605
|
}
|
|
6476
6606
|
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6607
|
+
// We default to the right-most page. This is done so-as to enforce a
|
|
6608
|
+
// consistent orientation between the view pager layout and the flattened
|
|
6609
|
+
// layout, where our default page appears on the far right.
|
|
6610
|
+
const getDefaultPage = numPages => numPages - 1;
|
|
6611
|
+
const initialPagerState = {
|
|
6612
|
+
animateToPosition: false,
|
|
6613
|
+
currentPage: getDefaultPage(keypadForType[defaultKeypadType].numPages),
|
|
6614
|
+
// The cumulative differential in the horizontal direction for the
|
|
6615
|
+
// current swipe.
|
|
6616
|
+
dx: 0,
|
|
6617
|
+
numPages: keypadForType[defaultKeypadType].numPages,
|
|
6618
|
+
pageWidthPx: 0,
|
|
6619
|
+
velocityTracker: new VelocityTracker()
|
|
6480
6620
|
};
|
|
6481
|
-
const
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
return {
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
};
|
|
6548
|
-
case "PressKey":
|
|
6549
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
|
|
6550
|
-
const keyConfig = KeyConfigs[action.key];
|
|
6551
|
-
// NOTE(charlie): Our keypad system operates by triggering key
|
|
6552
|
-
// presses with key IDs in a dumb manner, such that the keys
|
|
6553
|
-
// don't know what they can do--instead, the store is
|
|
6554
|
-
// responsible for interpreting key presses and triggering the
|
|
6555
|
-
// right actions when they occur. Hence, we figure off a
|
|
6556
|
-
// dismissal here rather than dispatching a dismiss action in
|
|
6557
|
-
// the first place.
|
|
6558
|
-
if (keyConfig.id === Keys.DISMISS) {
|
|
6559
|
-
return keypadReducer(state, {
|
|
6560
|
-
type: "DismissKeypad"
|
|
6561
|
-
});
|
|
6562
|
-
}
|
|
6563
|
-
return state;
|
|
6564
|
-
default:
|
|
6565
|
-
return state;
|
|
6566
|
-
}
|
|
6567
|
-
};
|
|
6568
|
-
|
|
6569
|
-
// We default to the right-most page. This is done so-as to enforce a
|
|
6570
|
-
// consistent orientation between the view pager layout and the flattened
|
|
6571
|
-
// layout, where our default page appears on the far right.
|
|
6572
|
-
const getDefaultPage = numPages => numPages - 1;
|
|
6573
|
-
const initialPagerState = {
|
|
6574
|
-
animateToPosition: false,
|
|
6575
|
-
currentPage: getDefaultPage(keypadForType[defaultKeypadType].numPages),
|
|
6576
|
-
// The cumulative differential in the horizontal direction for the
|
|
6577
|
-
// current swipe.
|
|
6578
|
-
dx: 0,
|
|
6579
|
-
numPages: keypadForType[defaultKeypadType].numPages,
|
|
6580
|
-
pageWidthPx: 0,
|
|
6581
|
-
velocityTracker: new VelocityTracker()
|
|
6582
|
-
};
|
|
6583
|
-
const pagerReducer = function () {
|
|
6584
|
-
let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialPagerState;
|
|
6585
|
-
let action = arguments.length > 1 ? arguments[1] : undefined;
|
|
6586
|
-
switch (action.type) {
|
|
6587
|
-
case "ConfigureKeypad":
|
|
6588
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'configuration' does not exist on type '{ type: string; }'.
|
|
6589
|
-
const {
|
|
6590
|
-
keypadType
|
|
6591
|
-
} = action.configuration;
|
|
6592
|
-
const {
|
|
6593
|
-
numPages
|
|
6594
|
-
} = keypadForType[keypadType];
|
|
6595
|
-
return {
|
|
6596
|
-
...state,
|
|
6597
|
-
numPages,
|
|
6598
|
-
animateToPosition: false,
|
|
6599
|
-
currentPage: getDefaultPage(numPages),
|
|
6600
|
-
dx: 0
|
|
6601
|
-
};
|
|
6602
|
-
case "SetPageSize":
|
|
6603
|
-
return {
|
|
6604
|
-
...state,
|
|
6605
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'pageWidthPx' does not exist on type '{ type: string; }'.
|
|
6606
|
-
pageWidthPx: action.pageWidthPx
|
|
6607
|
-
};
|
|
6608
|
-
case "PressKey":
|
|
6609
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
|
|
6610
|
-
const keyConfig = KeyConfigs[action.key];
|
|
6611
|
-
|
|
6612
|
-
// Reset the keypad page if the user performs a math operation.
|
|
6613
|
-
if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
|
|
6614
|
-
return pagerReducer(state, {
|
|
6615
|
-
type: "ResetKeypadPage"
|
|
6616
|
-
});
|
|
6617
|
-
}
|
|
6618
|
-
return state;
|
|
6619
|
-
case "ResetKeypadPage":
|
|
6620
|
-
return {
|
|
6621
|
-
...state,
|
|
6622
|
-
animateToPosition: true,
|
|
6623
|
-
// We start at the right-most page.
|
|
6624
|
-
currentPage: getDefaultPage(state.numPages),
|
|
6625
|
-
dx: 0
|
|
6626
|
-
};
|
|
6627
|
-
case "PageKeypadRight":
|
|
6628
|
-
const nextPage = Math.min(state.currentPage + 1, state.numPages - 1);
|
|
6629
|
-
return {
|
|
6630
|
-
...state,
|
|
6631
|
-
animateToPosition: true,
|
|
6632
|
-
currentPage: nextPage,
|
|
6633
|
-
dx: 0
|
|
6634
|
-
};
|
|
6635
|
-
case "PageKeypadLeft":
|
|
6636
|
-
const prevPage = Math.max(state.currentPage - 1, 0);
|
|
6637
|
-
return {
|
|
6638
|
-
...state,
|
|
6639
|
-
animateToPosition: true,
|
|
6640
|
-
currentPage: prevPage,
|
|
6641
|
-
dx: 0
|
|
6642
|
-
};
|
|
6643
|
-
case "OnSwipeChange":
|
|
6644
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
6645
|
-
state.velocityTracker.push(action.dx);
|
|
6646
|
-
return {
|
|
6647
|
-
...state,
|
|
6648
|
-
animateToPosition: false,
|
|
6649
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
6650
|
-
dx: action.dx
|
|
6651
|
-
};
|
|
6652
|
-
case "OnSwipeEnd":
|
|
6653
|
-
const {
|
|
6654
|
-
pageWidthPx,
|
|
6655
|
-
velocityTracker
|
|
6656
|
-
} = state;
|
|
6621
|
+
const pagerReducer = function () {
|
|
6622
|
+
let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialPagerState;
|
|
6623
|
+
let action = arguments.length > 1 ? arguments[1] : undefined;
|
|
6624
|
+
switch (action.type) {
|
|
6625
|
+
case "ConfigureKeypad":
|
|
6626
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'configuration' does not exist on type '{ type: string; }'.
|
|
6627
|
+
const {
|
|
6628
|
+
keypadType
|
|
6629
|
+
} = action.configuration;
|
|
6630
|
+
const {
|
|
6631
|
+
numPages
|
|
6632
|
+
} = keypadForType[keypadType];
|
|
6633
|
+
return {
|
|
6634
|
+
...state,
|
|
6635
|
+
numPages,
|
|
6636
|
+
animateToPosition: false,
|
|
6637
|
+
currentPage: getDefaultPage(numPages),
|
|
6638
|
+
dx: 0
|
|
6639
|
+
};
|
|
6640
|
+
case "SetPageSize":
|
|
6641
|
+
return {
|
|
6642
|
+
...state,
|
|
6643
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'pageWidthPx' does not exist on type '{ type: string; }'.
|
|
6644
|
+
pageWidthPx: action.pageWidthPx
|
|
6645
|
+
};
|
|
6646
|
+
case "PressKey":
|
|
6647
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'key' does not exist on type '{ type: string; }'.
|
|
6648
|
+
const keyConfig = KeyConfigs[action.key];
|
|
6649
|
+
|
|
6650
|
+
// Reset the keypad page if the user performs a math operation.
|
|
6651
|
+
if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
|
|
6652
|
+
return pagerReducer(state, {
|
|
6653
|
+
type: "ResetKeypadPage"
|
|
6654
|
+
});
|
|
6655
|
+
}
|
|
6656
|
+
return state;
|
|
6657
|
+
case "ResetKeypadPage":
|
|
6658
|
+
return {
|
|
6659
|
+
...state,
|
|
6660
|
+
animateToPosition: true,
|
|
6661
|
+
// We start at the right-most page.
|
|
6662
|
+
currentPage: getDefaultPage(state.numPages),
|
|
6663
|
+
dx: 0
|
|
6664
|
+
};
|
|
6665
|
+
case "PageKeypadRight":
|
|
6666
|
+
const nextPage = Math.min(state.currentPage + 1, state.numPages - 1);
|
|
6667
|
+
return {
|
|
6668
|
+
...state,
|
|
6669
|
+
animateToPosition: true,
|
|
6670
|
+
currentPage: nextPage,
|
|
6671
|
+
dx: 0
|
|
6672
|
+
};
|
|
6673
|
+
case "PageKeypadLeft":
|
|
6674
|
+
const prevPage = Math.max(state.currentPage - 1, 0);
|
|
6675
|
+
return {
|
|
6676
|
+
...state,
|
|
6677
|
+
animateToPosition: true,
|
|
6678
|
+
currentPage: prevPage,
|
|
6679
|
+
dx: 0
|
|
6680
|
+
};
|
|
6681
|
+
case "OnSwipeChange":
|
|
6682
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
6683
|
+
state.velocityTracker.push(action.dx);
|
|
6684
|
+
return {
|
|
6685
|
+
...state,
|
|
6686
|
+
animateToPosition: false,
|
|
6657
6687
|
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
return {
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6688
|
+
dx: action.dx
|
|
6689
|
+
};
|
|
6690
|
+
case "OnSwipeEnd":
|
|
6691
|
+
const {
|
|
6692
|
+
pageWidthPx,
|
|
6693
|
+
velocityTracker
|
|
6694
|
+
} = state;
|
|
6695
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'dx' does not exist on type '{ type: string; }'.
|
|
6696
|
+
const {
|
|
6697
|
+
dx
|
|
6698
|
+
} = action;
|
|
6699
|
+
const velocity = velocityTracker.getVelocity();
|
|
6700
|
+
|
|
6701
|
+
// NOTE(charlie): These will need refinement. The velocity comes
|
|
6702
|
+
// from Framer.
|
|
6703
|
+
const minFlingVelocity = 0.1;
|
|
6704
|
+
const minFlingDistance = 10;
|
|
6705
|
+
const shouldPageRight = dx < -pageWidthPx / 2 || velocity < -minFlingVelocity && dx < -minFlingDistance;
|
|
6706
|
+
const shouldPageLeft = dx > pageWidthPx / 2 || velocity > minFlingVelocity && dx > minFlingDistance;
|
|
6707
|
+
if (shouldPageRight) {
|
|
6708
|
+
return pagerReducer(state, {
|
|
6709
|
+
type: "PageKeypadRight"
|
|
6710
|
+
});
|
|
6711
|
+
} else if (shouldPageLeft) {
|
|
6712
|
+
return pagerReducer(state, {
|
|
6713
|
+
type: "PageKeypadLeft"
|
|
6714
|
+
});
|
|
6715
|
+
}
|
|
6716
|
+
return {
|
|
6717
|
+
...state,
|
|
6718
|
+
animateToPosition: true,
|
|
6719
|
+
dx: 0
|
|
6720
|
+
};
|
|
6721
|
+
default:
|
|
6722
|
+
return state;
|
|
6723
|
+
}
|
|
6724
|
+
};
|
|
6725
|
+
|
|
6726
|
+
const createStore = () => {
|
|
6727
|
+
// TODO(matthewc)[LC-752]: gestureReducer can't be moved from this file
|
|
6728
|
+
// because it depends on `store` being in scope (see note below)
|
|
6687
6729
|
const createGestureManager = swipeEnabled => {
|
|
6688
6730
|
return new GestureManager({
|
|
6689
6731
|
swipeEnabled
|
|
@@ -6762,147 +6804,6 @@ const createStore = () => {
|
|
|
6762
6804
|
return state;
|
|
6763
6805
|
}
|
|
6764
6806
|
};
|
|
6765
|
-
|
|
6766
|
-
// Used to generate unique animation IDs for the echo animations. The actual
|
|
6767
|
-
// values are irrelevant as long as they are unique.
|
|
6768
|
-
let _lastAnimationId = 0;
|
|
6769
|
-
const initialEchoState = {
|
|
6770
|
-
echoes: []
|
|
6771
|
-
};
|
|
6772
|
-
const echoReducer = function () {
|
|
6773
|
-
let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialEchoState;
|
|
6774
|
-
let action = arguments.length > 1 ? arguments[1] : undefined;
|
|
6775
|
-
switch (action.type) {
|
|
6776
|
-
case "PressKey":
|
|
6777
|
-
const keyConfig = KeyConfigs[action.key];
|
|
6778
|
-
|
|
6779
|
-
// Add in the echo animation if the user performs a math
|
|
6780
|
-
// operation.
|
|
6781
|
-
if (keyConfig.type === KeyTypes.VALUE || keyConfig.type === KeyTypes.OPERATOR) {
|
|
6782
|
-
return {
|
|
6783
|
-
...state,
|
|
6784
|
-
echoes: [...state.echoes, {
|
|
6785
|
-
animationId: "" + _lastAnimationId++,
|
|
6786
|
-
animationType: action.inPopover ? EchoAnimationTypes.LONG_FADE_ONLY : EchoAnimationTypes.FADE_ONLY,
|
|
6787
|
-
borders: action.borders,
|
|
6788
|
-
id: keyConfig.id,
|
|
6789
|
-
initialBounds: action.initialBounds
|
|
6790
|
-
}]
|
|
6791
|
-
};
|
|
6792
|
-
}
|
|
6793
|
-
return state;
|
|
6794
|
-
case "RemoveEcho":
|
|
6795
|
-
const remainingEchoes = state.echoes.filter(echo => {
|
|
6796
|
-
// @ts-expect-error [FEI-5003] - TS2339 - Property 'animationId' does not exist on type 'never'.
|
|
6797
|
-
return echo.animationId !== action.animationId;
|
|
6798
|
-
});
|
|
6799
|
-
return {
|
|
6800
|
-
...state,
|
|
6801
|
-
echoes: remainingEchoes
|
|
6802
|
-
};
|
|
6803
|
-
default:
|
|
6804
|
-
return state;
|
|
6805
|
-
}
|
|
6806
|
-
};
|
|
6807
|
-
const initialLayoutState = {
|
|
6808
|
-
gridDimensions: {
|
|
6809
|
-
numRows: keypadForType[defaultKeypadType].rows,
|
|
6810
|
-
numColumns: keypadForType[defaultKeypadType].columns,
|
|
6811
|
-
numMaxVisibleRows: keypadForType[defaultKeypadType].maxVisibleRows,
|
|
6812
|
-
numPages: keypadForType[defaultKeypadType].numPages
|
|
6813
|
-
},
|
|
6814
|
-
buttonDimensions: {
|
|
6815
|
-
widthPx: 48,
|
|
6816
|
-
heightPx: 48
|
|
6817
|
-
},
|
|
6818
|
-
pageDimensions: {
|
|
6819
|
-
pageWidthPx: 0,
|
|
6820
|
-
pageHeightPx: 0
|
|
6821
|
-
},
|
|
6822
|
-
layoutMode: LayoutModes.FULLSCREEN,
|
|
6823
|
-
paginationEnabled: false,
|
|
6824
|
-
navigationPadEnabled: false
|
|
6825
|
-
};
|
|
6826
|
-
|
|
6827
|
-
/**
|
|
6828
|
-
* Compute the additional layout state based on the provided page and grid
|
|
6829
|
-
* dimensions.
|
|
6830
|
-
*/
|
|
6831
|
-
const layoutParametersForDimensions = (pageDimensions, gridDimensions) => {
|
|
6832
|
-
const {
|
|
6833
|
-
pageWidthPx,
|
|
6834
|
-
pageHeightPx
|
|
6835
|
-
} = pageDimensions;
|
|
6836
|
-
|
|
6837
|
-
// Determine the device type and orientation.
|
|
6838
|
-
const deviceOrientation = pageWidthPx > pageHeightPx ? DeviceOrientations.LANDSCAPE : DeviceOrientations.PORTRAIT;
|
|
6839
|
-
const deviceType = Math.min(pageWidthPx, pageHeightPx) > tabletCutoffPx ? DeviceTypes.TABLET : DeviceTypes.PHONE;
|
|
6840
|
-
|
|
6841
|
-
// Using that information, make some decisions (or assumptions)
|
|
6842
|
-
// about the resulting layout.
|
|
6843
|
-
const navigationPadEnabled = deviceType === DeviceTypes.TABLET;
|
|
6844
|
-
const paginationEnabled = deviceType === DeviceTypes.PHONE && deviceOrientation === DeviceOrientations.PORTRAIT;
|
|
6845
|
-
const deviceInfo = {
|
|
6846
|
-
deviceOrientation,
|
|
6847
|
-
deviceType
|
|
6848
|
-
};
|
|
6849
|
-
const layoutOptions = {
|
|
6850
|
-
navigationPadEnabled,
|
|
6851
|
-
paginationEnabled,
|
|
6852
|
-
// HACK(charlie): It's not great that we're making assumptions about
|
|
6853
|
-
// the toolbar (which is rendered by webapp, and should always be
|
|
6854
|
-
// visible and anchored to the bottom of the page for phone and
|
|
6855
|
-
// tablet exercises). But this is primarily a heuristic (the goal is
|
|
6856
|
-
// to preserve a 'good' amount of space between the top of the
|
|
6857
|
-
// keypad and the top of the page) so we afford to have some margin
|
|
6858
|
-
// of error.
|
|
6859
|
-
toolbarEnabled: true
|
|
6860
|
-
};
|
|
6861
|
-
return {
|
|
6862
|
-
...computeLayoutParameters(gridDimensions, pageDimensions, deviceInfo, layoutOptions),
|
|
6863
|
-
// Pass along some of the layout information, so that other
|
|
6864
|
-
// components in the heirarchy can adapt appropriately.
|
|
6865
|
-
navigationPadEnabled,
|
|
6866
|
-
paginationEnabled
|
|
6867
|
-
};
|
|
6868
|
-
};
|
|
6869
|
-
const layoutReducer = function () {
|
|
6870
|
-
let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialLayoutState;
|
|
6871
|
-
let action = arguments.length > 1 ? arguments[1] : undefined;
|
|
6872
|
-
switch (action.type) {
|
|
6873
|
-
case "ConfigureKeypad":
|
|
6874
|
-
const {
|
|
6875
|
-
keypadType
|
|
6876
|
-
} = action.configuration;
|
|
6877
|
-
const gridDimensions = {
|
|
6878
|
-
numRows: keypadForType[keypadType].rows,
|
|
6879
|
-
numColumns: keypadForType[keypadType].columns,
|
|
6880
|
-
numMaxVisibleRows: keypadForType[keypadType].maxVisibleRows,
|
|
6881
|
-
numPages: keypadForType[keypadType].numPages
|
|
6882
|
-
};
|
|
6883
|
-
return {
|
|
6884
|
-
...state,
|
|
6885
|
-
...layoutParametersForDimensions(state.pageDimensions, gridDimensions),
|
|
6886
|
-
gridDimensions
|
|
6887
|
-
};
|
|
6888
|
-
case "SetPageSize":
|
|
6889
|
-
const {
|
|
6890
|
-
pageWidthPx,
|
|
6891
|
-
pageHeightPx
|
|
6892
|
-
} = action;
|
|
6893
|
-
const pageDimensions = {
|
|
6894
|
-
pageWidthPx,
|
|
6895
|
-
pageHeightPx
|
|
6896
|
-
};
|
|
6897
|
-
return {
|
|
6898
|
-
...state,
|
|
6899
|
-
...layoutParametersForDimensions(pageDimensions, state.gridDimensions),
|
|
6900
|
-
pageDimensions
|
|
6901
|
-
};
|
|
6902
|
-
default:
|
|
6903
|
-
return state;
|
|
6904
|
-
}
|
|
6905
|
-
};
|
|
6906
6807
|
const reducer = Redux__namespace.combineReducers({
|
|
6907
6808
|
input: inputReducer,
|
|
6908
6809
|
keypad: keypadReducer,
|
|
@@ -6920,6 +6821,10 @@ const createStore = () => {
|
|
|
6920
6821
|
return store;
|
|
6921
6822
|
};
|
|
6922
6823
|
|
|
6824
|
+
/**
|
|
6825
|
+
* A component that renders a navigation pad, which consists of an arrow for
|
|
6826
|
+
* each possible direction.
|
|
6827
|
+
*/
|
|
6923
6828
|
const {
|
|
6924
6829
|
row: row$1,
|
|
6925
6830
|
column,
|
|
@@ -6965,10 +6870,6 @@ class NavigationPad extends React__namespace.Component {
|
|
|
6965
6870
|
})));
|
|
6966
6871
|
}
|
|
6967
6872
|
}
|
|
6968
|
-
_defineProperty(NavigationPad, "propTypes", {
|
|
6969
|
-
roundTopLeft: PropTypes__default["default"].bool,
|
|
6970
|
-
style: PropTypes__default["default"].any
|
|
6971
|
-
});
|
|
6972
6873
|
const buttonSizePx = 48;
|
|
6973
6874
|
const borderRadiusPx = 4;
|
|
6974
6875
|
const borderWidthPx$1 = 1;
|
|
@@ -7027,11 +6928,12 @@ const {
|
|
|
7027
6928
|
centered,
|
|
7028
6929
|
fullWidth
|
|
7029
6930
|
} = Styles;
|
|
7030
|
-
|
|
7031
6931
|
// eslint-disable-next-line react/no-unsafe
|
|
7032
6932
|
class KeypadContainer extends React__namespace.Component {
|
|
7033
6933
|
constructor() {
|
|
7034
6934
|
super(...arguments);
|
|
6935
|
+
_defineProperty(this, "_resizeTimeout", void 0);
|
|
6936
|
+
_defineProperty(this, "hasMounted", void 0);
|
|
7035
6937
|
_defineProperty(this, "state", {
|
|
7036
6938
|
hasBeenActivated: false,
|
|
7037
6939
|
viewportWidth: "100vw"
|
|
@@ -7040,19 +6942,20 @@ class KeypadContainer extends React__namespace.Component {
|
|
|
7040
6942
|
// Throttle the resize callbacks.
|
|
7041
6943
|
// https://developer.mozilla.org/en-US/docs/Web/Events/resize
|
|
7042
6944
|
if (this._resizeTimeout == null) {
|
|
7043
|
-
this._resizeTimeout = setTimeout(() => {
|
|
6945
|
+
this._resizeTimeout = window.setTimeout(() => {
|
|
7044
6946
|
this._resizeTimeout = null;
|
|
7045
6947
|
this._onResize();
|
|
7046
6948
|
}, 66);
|
|
7047
6949
|
}
|
|
7048
6950
|
});
|
|
7049
6951
|
_defineProperty(this, "_onResize", () => {
|
|
6952
|
+
var _this$props$onPageSiz, _this$props;
|
|
7050
6953
|
// Whenever the page resizes, we need to force an update, as the button
|
|
7051
6954
|
// heights and keypad width are computed based on horizontal space.
|
|
7052
6955
|
this.setState({
|
|
7053
6956
|
viewportWidth: window.innerWidth
|
|
7054
6957
|
});
|
|
7055
|
-
this.props.onPageSizeChange(window.innerWidth, window.innerHeight);
|
|
6958
|
+
(_this$props$onPageSiz = (_this$props = this.props).onPageSizeChange) === null || _this$props$onPageSiz === void 0 ? void 0 : _this$props$onPageSiz.call(_this$props, window.innerWidth, window.innerHeight);
|
|
7056
6959
|
});
|
|
7057
6960
|
_defineProperty(this, "renderKeypad", () => {
|
|
7058
6961
|
const {
|
|
@@ -7134,10 +7037,15 @@ class KeypadContainer extends React__namespace.Component {
|
|
|
7134
7037
|
// NOTE(charlie): We render the transforms as pure inline styles to
|
|
7135
7038
|
// avoid an Aphrodite bug in mobile Safari.
|
|
7136
7039
|
// See: https://github.com/Khan/aphrodite/issues/68.
|
|
7137
|
-
|
|
7138
|
-
...(active ? inlineStyles.active : inlineStyles.hidden)
|
|
7139
|
-
...(!active && !hasBeenActivated ? inlineStyles.invisible : {})
|
|
7040
|
+
let dynamicStyle = {
|
|
7041
|
+
...(active ? inlineStyles.active : inlineStyles.hidden)
|
|
7140
7042
|
};
|
|
7043
|
+
if (!active && !hasBeenActivated) {
|
|
7044
|
+
dynamicStyle = {
|
|
7045
|
+
...dynamicStyle,
|
|
7046
|
+
...inlineStyles.invisible
|
|
7047
|
+
};
|
|
7048
|
+
}
|
|
7141
7049
|
const keypadContainerStyle = [row, centered, fullWidth, styles.keypadContainer, ...(Array.isArray(style) ? style : [style])];
|
|
7142
7050
|
const keypadStyle = [row, styles.keypadBorder, layoutMode === LayoutModes.FULLSCREEN ? styles.fullscreen : styles.compact];
|
|
7143
7051
|
|
|
@@ -7164,19 +7072,6 @@ class KeypadContainer extends React__namespace.Component {
|
|
|
7164
7072
|
}, this.renderKeypad())));
|
|
7165
7073
|
}
|
|
7166
7074
|
}
|
|
7167
|
-
_defineProperty(KeypadContainer, "propTypes", {
|
|
7168
|
-
active: PropTypes__default["default"].bool,
|
|
7169
|
-
extraKeys: PropTypes__default["default"].arrayOf(keyIdPropType),
|
|
7170
|
-
keypadType: PropTypes__default["default"].oneOf(Object.keys(KeypadTypes)).isRequired,
|
|
7171
|
-
layoutMode: PropTypes__default["default"].oneOf(Object.keys(LayoutModes)).isRequired,
|
|
7172
|
-
navigationPadEnabled: PropTypes__default["default"].bool.isRequired,
|
|
7173
|
-
onDismiss: PropTypes__default["default"].func,
|
|
7174
|
-
// A callback that should be triggered with the root React element on
|
|
7175
|
-
// mount.
|
|
7176
|
-
onElementMounted: PropTypes__default["default"].func,
|
|
7177
|
-
onPageSizeChange: PropTypes__default["default"].func.isRequired,
|
|
7178
|
-
style: PropTypes__default["default"].any
|
|
7179
|
-
});
|
|
7180
7075
|
const keypadAnimationDurationMs = 300;
|
|
7181
7076
|
const borderWidthPx = 1;
|
|
7182
7077
|
const styles = aphrodite.StyleSheet.create({
|
|
@@ -7237,7 +7132,9 @@ const inlineStyles = {
|
|
|
7237
7132
|
};
|
|
7238
7133
|
const mapStateToProps = state => {
|
|
7239
7134
|
return {
|
|
7240
|
-
|
|
7135
|
+
extraKeys: state.keypad.extraKeys,
|
|
7136
|
+
keypadType: state.keypad.keypadType,
|
|
7137
|
+
active: state.keypad.active,
|
|
7241
7138
|
layoutMode: state.layout.layoutMode,
|
|
7242
7139
|
navigationPadEnabled: state.layout.navigationPadEnabled
|
|
7243
7140
|
};
|
|
@@ -7298,11 +7195,12 @@ class ProvidedKeypad extends React__namespace.Component {
|
|
|
7298
7195
|
render() {
|
|
7299
7196
|
const {
|
|
7300
7197
|
onElementMounted,
|
|
7301
|
-
|
|
7198
|
+
onDismiss,
|
|
7199
|
+
style
|
|
7302
7200
|
} = this.props;
|
|
7303
7201
|
return /*#__PURE__*/React__namespace.createElement(reactRedux.Provider, {
|
|
7304
7202
|
store: this.store
|
|
7305
|
-
}, /*#__PURE__*/React__namespace.createElement(KeypadContainer$1,
|
|
7203
|
+
}, /*#__PURE__*/React__namespace.createElement(KeypadContainer$1, {
|
|
7306
7204
|
onElementMounted: element => {
|
|
7307
7205
|
// Append the dispatch methods that we want to expose
|
|
7308
7206
|
// externally to the returned React element.
|
|
@@ -7316,16 +7214,17 @@ class ProvidedKeypad extends React__namespace.Component {
|
|
|
7316
7214
|
getDOMNode: this.getDOMNode
|
|
7317
7215
|
};
|
|
7318
7216
|
onElementMounted && onElementMounted(elementWithDispatchMethods);
|
|
7319
|
-
}
|
|
7320
|
-
|
|
7217
|
+
},
|
|
7218
|
+
onDismiss: onDismiss,
|
|
7219
|
+
style: style
|
|
7220
|
+
}));
|
|
7321
7221
|
}
|
|
7322
7222
|
}
|
|
7323
7223
|
|
|
7324
|
-
exports.CursorContexts =
|
|
7224
|
+
exports.CursorContexts = cursorContexts;
|
|
7325
7225
|
exports.KeyConfigs = KeyConfigs;
|
|
7326
7226
|
exports.Keypad = ProvidedKeypad;
|
|
7327
7227
|
exports.KeypadInput = MathInput;
|
|
7328
7228
|
exports.KeypadTypes = KeypadTypes;
|
|
7329
|
-
exports.keypadConfigurationPropType = keypadConfigurationPropType;
|
|
7330
7229
|
exports.keypadElementPropType = keypadElementPropType;
|
|
7331
7230
|
//# sourceMappingURL=index.js.map
|