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