@khanacademy/math-input 14.2.2 → 15.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/es/index.js +89 -34
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +1207 -1095
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/src/components/keypad/__tests__/__snapshots__/mobile-keypad.test.tsx.snap +9 -3
- package/src/components/keypad/mobile-keypad.tsx +25 -28
- package/tsconfig-build.tsbuildinfo +1 -1
package/dist/index.js
CHANGED
|
@@ -53,23 +53,9 @@ var Redux__namespace = /*#__PURE__*/_interopNamespace(Redux);
|
|
|
53
53
|
|
|
54
54
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
55
55
|
const libName = "@khanacademy/math-input";
|
|
56
|
-
const libVersion = "
|
|
56
|
+
const libVersion = "15.0.0";
|
|
57
57
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
58
58
|
|
|
59
|
-
function _defineProperty(obj, key, value) {
|
|
60
|
-
key = _toPropertyKey(key);
|
|
61
|
-
if (key in obj) {
|
|
62
|
-
Object.defineProperty(obj, key, {
|
|
63
|
-
value: value,
|
|
64
|
-
enumerable: true,
|
|
65
|
-
configurable: true,
|
|
66
|
-
writable: true
|
|
67
|
-
});
|
|
68
|
-
} else {
|
|
69
|
-
obj[key] = value;
|
|
70
|
-
}
|
|
71
|
-
return obj;
|
|
72
|
-
}
|
|
73
59
|
function _extends() {
|
|
74
60
|
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
75
61
|
for (var i = 1; i < arguments.length; i++) {
|
|
@@ -84,20 +70,6 @@ function _extends() {
|
|
|
84
70
|
};
|
|
85
71
|
return _extends.apply(this, arguments);
|
|
86
72
|
}
|
|
87
|
-
function _toPrimitive(input, hint) {
|
|
88
|
-
if (typeof input !== "object" || input === null) return input;
|
|
89
|
-
var prim = input[Symbol.toPrimitive];
|
|
90
|
-
if (prim !== undefined) {
|
|
91
|
-
var res = prim.call(input, hint || "default");
|
|
92
|
-
if (typeof res !== "object") return res;
|
|
93
|
-
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
94
|
-
}
|
|
95
|
-
return (hint === "string" ? String : Number)(input);
|
|
96
|
-
}
|
|
97
|
-
function _toPropertyKey(arg) {
|
|
98
|
-
var key = _toPrimitive(arg, "string");
|
|
99
|
-
return typeof key === "symbol" ? key : String(key);
|
|
100
|
-
}
|
|
101
73
|
|
|
102
74
|
class Text extends React__namespace.Component {
|
|
103
75
|
render() {
|
|
@@ -133,8 +105,36 @@ const styles$k = aphrodite.StyleSheet.create({
|
|
|
133
105
|
});
|
|
134
106
|
|
|
135
107
|
class View extends React__namespace.Component {
|
|
108
|
+
static styles = aphrodite.StyleSheet.create({
|
|
109
|
+
// From: https://github.com/necolas/react-native-web/blob/master/src/components/View/index.js
|
|
110
|
+
// eslint-disable-next-line react-native/no-unused-styles
|
|
111
|
+
initial: {
|
|
112
|
+
alignItems: "stretch",
|
|
113
|
+
borderWidth: 0,
|
|
114
|
+
borderStyle: "solid",
|
|
115
|
+
boxSizing: "border-box",
|
|
116
|
+
display: "flex",
|
|
117
|
+
flexBasis: "auto",
|
|
118
|
+
flexDirection: "column",
|
|
119
|
+
margin: 0,
|
|
120
|
+
padding: 0,
|
|
121
|
+
position: "relative",
|
|
122
|
+
// button and anchor reset
|
|
123
|
+
backgroundColor: "transparent",
|
|
124
|
+
color: "inherit",
|
|
125
|
+
font: "inherit",
|
|
126
|
+
textAlign: "inherit",
|
|
127
|
+
textDecorationLine: "none",
|
|
128
|
+
// list reset
|
|
129
|
+
listStyle: "none",
|
|
130
|
+
// fix flexbox bugs
|
|
131
|
+
maxWidth: "100%",
|
|
132
|
+
minHeight: 0,
|
|
133
|
+
minWidth: 0
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
136
|
render() {
|
|
137
|
-
const className = aphrodite.css(View.styles.initial, ...(Array.isArray(this.props.style) ? this.props.style : [this.props.style])) + (this.props.extraClassName ?
|
|
137
|
+
const className = aphrodite.css(View.styles.initial, ...(Array.isArray(this.props.style) ? this.props.style : [this.props.style])) + (this.props.extraClassName ? ` ${this.props.extraClassName}` : "");
|
|
138
138
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
139
139
|
className: className,
|
|
140
140
|
style: this.props.dynamicStyle,
|
|
@@ -149,34 +149,6 @@ class View extends React__namespace.Component {
|
|
|
149
149
|
}, this.props.children);
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
-
_defineProperty(View, "styles", aphrodite.StyleSheet.create({
|
|
153
|
-
// From: https://github.com/necolas/react-native-web/blob/master/src/components/View/index.js
|
|
154
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
155
|
-
initial: {
|
|
156
|
-
alignItems: "stretch",
|
|
157
|
-
borderWidth: 0,
|
|
158
|
-
borderStyle: "solid",
|
|
159
|
-
boxSizing: "border-box",
|
|
160
|
-
display: "flex",
|
|
161
|
-
flexBasis: "auto",
|
|
162
|
-
flexDirection: "column",
|
|
163
|
-
margin: 0,
|
|
164
|
-
padding: 0,
|
|
165
|
-
position: "relative",
|
|
166
|
-
// button and anchor reset
|
|
167
|
-
backgroundColor: "transparent",
|
|
168
|
-
color: "inherit",
|
|
169
|
-
font: "inherit",
|
|
170
|
-
textAlign: "inherit",
|
|
171
|
-
textDecorationLine: "none",
|
|
172
|
-
// list reset
|
|
173
|
-
listStyle: "none",
|
|
174
|
-
// fix flexbox bugs
|
|
175
|
-
maxWidth: "100%",
|
|
176
|
-
minHeight: 0,
|
|
177
|
-
minWidth: 0
|
|
178
|
-
}
|
|
179
|
-
}));
|
|
180
152
|
|
|
181
153
|
/**
|
|
182
154
|
* Common parameters used to style components.
|
|
@@ -218,6 +190,9 @@ const navigationPadWidthPx = 192;
|
|
|
218
190
|
// has settled down.
|
|
219
191
|
const toolbarHeightPx = 60;
|
|
220
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Renders the green tear-shaped handle under the cursor.
|
|
195
|
+
*/
|
|
221
196
|
const touchTargetRadiusPx = 2 * cursorHandleRadiusPx;
|
|
222
197
|
const touchTargetHeightPx = 2 * touchTargetRadiusPx;
|
|
223
198
|
const touchTargetWidthPx = 2 * touchTargetRadiusPx;
|
|
@@ -225,6 +200,12 @@ const cursorRadiusPx = cursorHandleRadiusPx;
|
|
|
225
200
|
const cursorHeightPx = cursorHandleDistanceMultiplier * (cursorRadiusPx * 4);
|
|
226
201
|
const cursorWidthPx = 4 * cursorRadiusPx;
|
|
227
202
|
class CursorHandle extends React__namespace.Component {
|
|
203
|
+
static defaultProps = {
|
|
204
|
+
animateIntoPosition: false,
|
|
205
|
+
visible: false,
|
|
206
|
+
x: 0,
|
|
207
|
+
y: 0
|
|
208
|
+
};
|
|
228
209
|
render() {
|
|
229
210
|
const {
|
|
230
211
|
x,
|
|
@@ -235,7 +216,7 @@ class CursorHandle extends React__namespace.Component {
|
|
|
235
216
|
transitionDuration: "100ms",
|
|
236
217
|
transitionProperty: "transform"
|
|
237
218
|
} : {};
|
|
238
|
-
const transformString =
|
|
219
|
+
const transformString = `translate(${x}px, ${y}px)`;
|
|
239
220
|
const outerStyle = {
|
|
240
221
|
position: "absolute",
|
|
241
222
|
// This is essentially webapp's interactiveComponent + 1.
|
|
@@ -261,7 +242,7 @@ class CursorHandle extends React__namespace.Component {
|
|
|
261
242
|
fill: "none",
|
|
262
243
|
width: cursorWidthPx,
|
|
263
244
|
height: cursorHeightPx,
|
|
264
|
-
viewBox:
|
|
245
|
+
viewBox: `0 0 ${cursorWidthPx} ${cursorHeightPx}`
|
|
265
246
|
}, /*#__PURE__*/React__namespace.createElement("filter", {
|
|
266
247
|
id: "math-input_cursor",
|
|
267
248
|
colorInterpolationFilters: "sRGB",
|
|
@@ -307,12 +288,6 @@ class CursorHandle extends React__namespace.Component {
|
|
|
307
288
|
})));
|
|
308
289
|
}
|
|
309
290
|
}
|
|
310
|
-
_defineProperty(CursorHandle, "defaultProps", {
|
|
311
|
-
animateIntoPosition: false,
|
|
312
|
-
visible: false,
|
|
313
|
-
x: 0,
|
|
314
|
-
y: 0
|
|
315
|
-
});
|
|
316
291
|
|
|
317
292
|
/**
|
|
318
293
|
* A gesture recognizer that detects 'drags', crudely defined as either scrolls
|
|
@@ -325,9 +300,6 @@ _defineProperty(CursorHandle, "defaultProps", {
|
|
|
325
300
|
const touchSlopPx = 8;
|
|
326
301
|
class DragListener {
|
|
327
302
|
constructor(onDrag, initialEvent) {
|
|
328
|
-
_defineProperty(this, "_scrollListener", void 0);
|
|
329
|
-
_defineProperty(this, "_moveListener", void 0);
|
|
330
|
-
_defineProperty(this, "_endAndCancelListener", void 0);
|
|
331
303
|
// We detect drags in two ways. First, by listening for the window
|
|
332
304
|
// scroll event (we consider any legitimate scroll to be a drag).
|
|
333
305
|
this._scrollListener = () => {
|
|
@@ -727,7 +699,7 @@ function handleBackspaceInLogIndex(mathField, cursor) {
|
|
|
727
699
|
if (isInsideEmptyNode(cursor)) {
|
|
728
700
|
const grandparent = cursor.parent.parent;
|
|
729
701
|
const command = maybeFindCommandBeforeParens(grandparent);
|
|
730
|
-
cursor.insLeftOf(command
|
|
702
|
+
cursor.insLeftOf(command?.startNode);
|
|
731
703
|
cursor.startSelection();
|
|
732
704
|
if (grandparent[mathQuillInstance.R] !== MathFieldActionType.MQ_END) {
|
|
733
705
|
cursor.insRightOf(grandparent[mathQuillInstance.R]);
|
|
@@ -1028,7 +1000,7 @@ function handleExponent(mathField, key) {
|
|
|
1028
1000
|
break;
|
|
1029
1001
|
case "EXP_2":
|
|
1030
1002
|
case "EXP_3":
|
|
1031
|
-
mathField.write(
|
|
1003
|
+
mathField.write(`^${key === "EXP_2" ? 2 : 3}`);
|
|
1032
1004
|
|
|
1033
1005
|
// If we enter a square or a cube, we should leave the cursor
|
|
1034
1006
|
// within the newly inserted parens, if they exist. This takes
|
|
@@ -1042,7 +1014,7 @@ function handleExponent(mathField, key) {
|
|
|
1042
1014
|
}
|
|
1043
1015
|
break;
|
|
1044
1016
|
default:
|
|
1045
|
-
throw new Error(
|
|
1017
|
+
throw new Error(`Invalid exponent key: ${key}`);
|
|
1046
1018
|
}
|
|
1047
1019
|
}
|
|
1048
1020
|
|
|
@@ -1124,7 +1096,7 @@ function handleJumpOut(mathField, key) {
|
|
|
1124
1096
|
cursor.insRightOf(cursor.parent.parent);
|
|
1125
1097
|
break;
|
|
1126
1098
|
default:
|
|
1127
|
-
throw new Error(
|
|
1099
|
+
throw new Error(`Attempted to 'Jump Out' from node, but found no ` + `appropriate cursor context: ${context}`);
|
|
1128
1100
|
}
|
|
1129
1101
|
}
|
|
1130
1102
|
|
|
@@ -1160,7 +1132,7 @@ function buildGenericCallback(str) {
|
|
|
1160
1132
|
}
|
|
1161
1133
|
function buildNormalFunctionCallback(command) {
|
|
1162
1134
|
return function (mathField) {
|
|
1163
|
-
mathField.write(
|
|
1135
|
+
mathField.write(`\\${command}\\left(\\right)`);
|
|
1164
1136
|
mathField.keystroke("Left");
|
|
1165
1137
|
};
|
|
1166
1138
|
}
|
|
@@ -1323,6 +1295,7 @@ const keyToMathquillMap = {
|
|
|
1323
1295
|
Z: buildGenericCallback("Z")
|
|
1324
1296
|
};
|
|
1325
1297
|
|
|
1298
|
+
// Notes about MathQuill
|
|
1326
1299
|
const mobileKeyTranslator = {
|
|
1327
1300
|
...keyToMathquillMap,
|
|
1328
1301
|
// note(Matthew): our mobile backspace logic is really complicated
|
|
@@ -1344,8 +1317,6 @@ class MathWrapper {
|
|
|
1344
1317
|
|
|
1345
1318
|
constructor(element) {
|
|
1346
1319
|
let callbacks = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1347
|
-
_defineProperty(this, "mathField", void 0);
|
|
1348
|
-
_defineProperty(this, "callbacks", void 0);
|
|
1349
1320
|
this.mathField = createMathField(element, () => {
|
|
1350
1321
|
return {
|
|
1351
1322
|
// use a span instead of a textarea so that we don't bring up the
|
|
@@ -1528,481 +1499,27 @@ const scrollIntoView = (containerNode, keypadNode) => {
|
|
|
1528
1499
|
const constrainingFrictionFactor = 0.8;
|
|
1529
1500
|
// eslint-disable-next-line react/no-unsafe
|
|
1530
1501
|
class MathInput extends React__namespace.Component {
|
|
1531
|
-
constructor
|
|
1532
|
-
super(...arguments);
|
|
1533
|
-
_defineProperty(this, "didTouchOutside", void 0);
|
|
1534
|
-
_defineProperty(this, "didScroll", void 0);
|
|
1535
|
-
_defineProperty(this, "mathField", void 0);
|
|
1536
|
-
_defineProperty(this, "recordTouchStartOutside", void 0);
|
|
1537
|
-
_defineProperty(this, "blurOnTouchEndOutside", void 0);
|
|
1538
|
-
_defineProperty(this, "dragListener", void 0);
|
|
1539
|
-
_defineProperty(this, "inputRef", void 0);
|
|
1540
|
-
_defineProperty(this, "_isMounted", void 0);
|
|
1541
|
-
_defineProperty(this, "_mathContainer", void 0);
|
|
1542
|
-
_defineProperty(this, "_container", void 0);
|
|
1543
|
-
_defineProperty(this, "_root", void 0);
|
|
1544
|
-
_defineProperty(this, "_containerBounds", void 0);
|
|
1545
|
-
_defineProperty(this, "_keypadBounds", void 0);
|
|
1546
|
-
_defineProperty(this, "state", {
|
|
1547
|
-
focused: false,
|
|
1548
|
-
handle: {
|
|
1549
|
-
animateIntoPosition: false,
|
|
1550
|
-
visible: false,
|
|
1551
|
-
x: 0,
|
|
1552
|
-
y: 0
|
|
1553
|
-
}
|
|
1554
|
-
});
|
|
1555
|
-
_defineProperty(this, "_clearKeypadBoundsCache", () => {
|
|
1556
|
-
this._keypadBounds = null;
|
|
1557
|
-
});
|
|
1558
|
-
_defineProperty(this, "_cacheKeypadBounds", keypadNode => {
|
|
1559
|
-
this._keypadBounds = keypadNode.getBoundingClientRect();
|
|
1560
|
-
});
|
|
1561
|
-
_defineProperty(this, "_updateInputPadding", () => {
|
|
1562
|
-
this._container = ReactDOM__default["default"].findDOMNode(this);
|
|
1563
|
-
this._root = this._container.querySelector(".mq-root-block");
|
|
1564
|
-
const padding = this.getInputInnerPadding();
|
|
1565
|
-
// NOTE(diedra): This overrides the default 2px padding from Mathquil.
|
|
1566
|
-
this._root.style.padding = "".concat(padding.paddingTop, "px ").concat(padding.paddingRight, "px") + " ".concat(padding.paddingBottom, "px ").concat(padding.paddingLeft, "px");
|
|
1567
|
-
this._root.style.fontSize = "".concat(fontSizePt, "pt");
|
|
1568
|
-
});
|
|
1569
|
-
_defineProperty(this, "_getKeypadBounds", () => {
|
|
1570
|
-
if (!this._keypadBounds) {
|
|
1571
|
-
var _this$props$keypadEle;
|
|
1572
|
-
const node = (_this$props$keypadEle = this.props.keypadElement) === null || _this$props$keypadEle === void 0 ? void 0 : _this$props$keypadEle.getDOMNode();
|
|
1573
|
-
this._cacheKeypadBounds(node);
|
|
1574
|
-
}
|
|
1575
|
-
return this._keypadBounds;
|
|
1576
|
-
});
|
|
1577
|
-
_defineProperty(this, "_updateCursorHandle", animateIntoPosition => {
|
|
1578
|
-
const containerBounds = this._container.getBoundingClientRect();
|
|
1579
|
-
const cursor = this._container.querySelector(".mq-cursor");
|
|
1580
|
-
const cursorBounds = cursor.getBoundingClientRect();
|
|
1581
|
-
const cursorWidth = cursorBounds.width;
|
|
1582
|
-
const gapBelowCursor = 2;
|
|
1583
|
-
const inputInnerPadding = this.getInputInnerPadding();
|
|
1584
|
-
|
|
1585
|
-
// The cursor should never be further right or left than the edge of the
|
|
1586
|
-
// container's values.
|
|
1587
|
-
const furthestRightCursorBound = containerBounds.right - cursorWidth - inputInnerPadding.paddingRight;
|
|
1588
|
-
const furthestLeftCursorBound = containerBounds.left + cursorWidth + inputInnerPadding.paddingLeft;
|
|
1589
|
-
let cursorBoundsLeft = cursorBounds.left;
|
|
1590
|
-
if (cursorBounds.left > furthestRightCursorBound) {
|
|
1591
|
-
cursorBoundsLeft = furthestRightCursorBound;
|
|
1592
|
-
} else if (cursorBounds.left < furthestLeftCursorBound) {
|
|
1593
|
-
cursorBoundsLeft = furthestLeftCursorBound;
|
|
1594
|
-
}
|
|
1595
|
-
this.setState({
|
|
1596
|
-
handle: {
|
|
1597
|
-
visible: true,
|
|
1598
|
-
animateIntoPosition,
|
|
1599
|
-
// We subtract containerBounds' left/top to correct for the
|
|
1600
|
-
// position of the container within the page.
|
|
1601
|
-
x: cursorBoundsLeft + cursorWidth / 2 - containerBounds.left,
|
|
1602
|
-
y: cursorBounds.bottom + gapBelowCursor - containerBounds.top
|
|
1603
|
-
}
|
|
1604
|
-
});
|
|
1605
|
-
});
|
|
1606
|
-
_defineProperty(this, "_hideCursorHandle", () => {
|
|
1607
|
-
this.setState({
|
|
1608
|
-
handle: {
|
|
1609
|
-
visible: false,
|
|
1610
|
-
x: 0,
|
|
1611
|
-
y: 0
|
|
1612
|
-
}
|
|
1613
|
-
});
|
|
1614
|
-
});
|
|
1615
|
-
_defineProperty(this, "_handleScroll", () => {
|
|
1616
|
-
// If animateIntoPosition is false, the user is currently manually positioning
|
|
1617
|
-
// the cursor. This is important because the user can scroll the input field
|
|
1618
|
-
// with the curor handle, and we don't want to override that ability.
|
|
1619
|
-
// But we do want to hide the handle is the user is just scrolling the input field
|
|
1620
|
-
// normally, because the handle will not move with the scroll.
|
|
1621
|
-
if (this.state.handle.animateIntoPosition !== false) {
|
|
1622
|
-
this._hideCursorHandle();
|
|
1623
|
-
}
|
|
1624
|
-
});
|
|
1625
|
-
_defineProperty(this, "blur", () => {
|
|
1626
|
-
this.mathField.blur();
|
|
1627
|
-
this.props.onBlur && this.props.onBlur();
|
|
1628
|
-
this.setState({
|
|
1629
|
-
focused: false,
|
|
1630
|
-
handle: {
|
|
1631
|
-
visible: false
|
|
1632
|
-
}
|
|
1633
|
-
});
|
|
1634
|
-
});
|
|
1635
|
-
_defineProperty(this, "focus", () => {
|
|
1636
|
-
var _this$props$keypadEle2, _this$props;
|
|
1637
|
-
// Pass this component's handleKey method to the keypad so it can call
|
|
1638
|
-
// it whenever it needs to trigger a keypress action.
|
|
1639
|
-
(_this$props$keypadEle2 = this.props.keypadElement) === null || _this$props$keypadEle2 === void 0 ? void 0 : _this$props$keypadEle2.setKeyHandler(key => {
|
|
1640
|
-
const cursor = this.mathField.pressKey(key);
|
|
1641
|
-
|
|
1642
|
-
// Trigger an `onChange` if the value in the input changed, and hide
|
|
1643
|
-
// the cursor handle whenever the user types a key. If the value
|
|
1644
|
-
// changed as a result of a keypress, we need to be careful not to
|
|
1645
|
-
// call `setState` until after `onChange` has resolved.
|
|
1646
|
-
const hideCursor = () => {
|
|
1647
|
-
this.setState({
|
|
1648
|
-
handle: {
|
|
1649
|
-
visible: false
|
|
1650
|
-
}
|
|
1651
|
-
});
|
|
1652
|
-
};
|
|
1653
|
-
const value = this.mathField.getContent();
|
|
1654
|
-
if (this.props.value !== value) {
|
|
1655
|
-
this.props.onChange(value, hideCursor);
|
|
1656
|
-
} else {
|
|
1657
|
-
hideCursor();
|
|
1658
|
-
}
|
|
1659
|
-
return cursor;
|
|
1660
|
-
});
|
|
1661
|
-
this.mathField.focus();
|
|
1662
|
-
(_this$props = this.props) === null || _this$props === void 0 ? void 0 : _this$props.onFocus();
|
|
1663
|
-
this.setState({
|
|
1664
|
-
focused: true
|
|
1665
|
-
}, () => {
|
|
1666
|
-
// NOTE(charlie): We use `setTimeout` to allow for a layout pass to
|
|
1667
|
-
// occur. Otherwise, the keypad is measured incorrectly. Ideally,
|
|
1668
|
-
// we'd use requestAnimationFrame here, but it's unsupported on
|
|
1669
|
-
// Android Browser 4.3.
|
|
1670
|
-
setTimeout(() => {
|
|
1671
|
-
if (this._isMounted) {
|
|
1672
|
-
var _this$props$keypadEle3;
|
|
1673
|
-
// TODO(benkomalo): the keypad is animating at this point,
|
|
1674
|
-
// so we can't call _cacheKeypadBounds(), even though
|
|
1675
|
-
// it'd be nice to do so. It should probably be the case
|
|
1676
|
-
// that the higher level controller tells us when the
|
|
1677
|
-
// keypad is settled (then scrollIntoView wouldn't have
|
|
1678
|
-
// to make assumptions about that either).
|
|
1679
|
-
const maybeKeypadNode = (_this$props$keypadEle3 = this.props.keypadElement) === null || _this$props$keypadEle3 === void 0 ? void 0 : _this$props$keypadEle3.getDOMNode();
|
|
1680
|
-
scrollIntoView(this._container, maybeKeypadNode);
|
|
1681
|
-
}
|
|
1682
|
-
});
|
|
1683
|
-
});
|
|
1684
|
-
});
|
|
1685
|
-
_defineProperty(this, "_findHitNode", (containerBounds, x, y, dx, dy) => {
|
|
1686
|
-
while (y >= containerBounds.top && y <= containerBounds.bottom) {
|
|
1687
|
-
y += dy;
|
|
1688
|
-
const points = [[x - dx, y], [x, y], [x + dx, y]];
|
|
1689
|
-
const elements = points
|
|
1690
|
-
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
1691
|
-
.map(point => document.elementFromPoint(...point))
|
|
1692
|
-
// We exclude the root container itself and any nodes marked
|
|
1693
|
-
// as non-leaf which are fractions, parens, and roots. The
|
|
1694
|
-
// children of those nodes are included in the list because
|
|
1695
|
-
// those are the items we care about placing the cursor next
|
|
1696
|
-
// to.
|
|
1697
|
-
//
|
|
1698
|
-
// MathQuill's mq-non-leaf is not applied to all non-leaf nodes
|
|
1699
|
-
// so the naming is a bit confusing. Although fractions are
|
|
1700
|
-
// included, neither mq-numerator nor mq-denominator nodes are
|
|
1701
|
-
// and neither are subscripts or superscripts.
|
|
1702
|
-
.filter(element => element && this._root.contains(element) && (!element.classList.contains("mq-root-block") && !element.classList.contains("mq-non-leaf") || element.classList.contains("mq-empty") || element.classList.contains("mq-hasCursor")));
|
|
1703
|
-
let hitNode = null;
|
|
1704
|
-
|
|
1705
|
-
// Contains only DOMNodes with child elements.
|
|
1706
|
-
const nonLeafElements = [];
|
|
1707
|
-
let max = 0;
|
|
1708
|
-
const counts = {};
|
|
1709
|
-
const elementsById = {};
|
|
1710
|
-
for (const element of elements) {
|
|
1711
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
1712
|
-
const id = element.getAttribute("mathquill-command-id");
|
|
1713
|
-
if (id != null) {
|
|
1714
|
-
counts[id] = (counts[id] || 0) + 1;
|
|
1715
|
-
elementsById[id] = element;
|
|
1716
|
-
} else {
|
|
1717
|
-
// @ts-expect-error - TS2345 - Argument of type 'Element | null' is not assignable to parameter of type 'HTMLElement | null'.
|
|
1718
|
-
nonLeafElements.push(element);
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
|
-
// When determining which DOMNode to place the cursor beside, we
|
|
1723
|
-
// prefer leaf nodes. Hitting a leaf node is a good sign that the
|
|
1724
|
-
// cursor is really close to some piece of math that has been
|
|
1725
|
-
// rendered because leaf nodes contain text. Non-leaf nodes may
|
|
1726
|
-
// contain a lot of whitespace so the cursor may be further away
|
|
1727
|
-
// from actual text within the expression.
|
|
1728
|
-
//
|
|
1729
|
-
// Since we're doing three hit tests per loop it's possible that
|
|
1730
|
-
// we hit multiple leaf nodes at the same time. In this case we
|
|
1731
|
-
// we prefer the DOMNode with the most hits.
|
|
1732
|
-
// TODO(kevinb) consider preferring nodes hit by [x, y].
|
|
1733
|
-
for (const [id, count] of wonderStuffCore.entries(counts)) {
|
|
1734
|
-
if (count > max) {
|
|
1735
|
-
max = count;
|
|
1736
|
-
hitNode = elementsById[id];
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
// It's possible that two non-leaf nodes are right beside each
|
|
1741
|
-
// other. We don't bother counting the number of hits for each,
|
|
1742
|
-
// b/c this seems like an unlikely situation. Also, ignoring the
|
|
1743
|
-
// hit count in the situation should not have serious effects on
|
|
1744
|
-
// the overall accuracy of the algorithm.
|
|
1745
|
-
if (hitNode == null && nonLeafElements.length > 0) {
|
|
1746
|
-
// @ts-expect-error - TS2322 - Type 'HTMLElement | null' is not assignable to type 'null'.
|
|
1747
|
-
hitNode = nonLeafElements[0];
|
|
1748
|
-
}
|
|
1749
|
-
if (hitNode !== null) {
|
|
1750
|
-
this.mathField.setCursorPosition(x, y, hitNode);
|
|
1751
|
-
return true;
|
|
1752
|
-
}
|
|
1753
|
-
}
|
|
1754
|
-
return false;
|
|
1755
|
-
});
|
|
1756
|
-
_defineProperty(this, "_insertCursorAtClosestNode", (x, y) => {
|
|
1757
|
-
const cursor = this.mathField.getCursor();
|
|
1758
|
-
|
|
1759
|
-
// Pre-emptively check if the input has any child nodes; if not, the
|
|
1760
|
-
// input is empty, so we throw the cursor at the start.
|
|
1761
|
-
if (!this._root.hasChildNodes()) {
|
|
1762
|
-
cursor.insAtLeftEnd(this.mathField.mathField.__controller.root);
|
|
1763
|
-
return;
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1766
|
-
// NOTE(diedra): The adding and subtracting of 10 or 15 pixels here accounts
|
|
1767
|
-
// for the padding that surrounds the input values.
|
|
1768
|
-
if (y > this._containerBounds.bottom) {
|
|
1769
|
-
y = this._containerBounds.bottom - 10;
|
|
1770
|
-
} else if (y < this._containerBounds.top) {
|
|
1771
|
-
y = this._containerBounds.top + 10;
|
|
1772
|
-
}
|
|
1773
|
-
if (x > this._containerBounds.right) {
|
|
1774
|
-
x = this._containerBounds.right - 15;
|
|
1775
|
-
} else if (x < this._containerBounds.left) {
|
|
1776
|
-
x = this._containerBounds.left + 15;
|
|
1777
|
-
}
|
|
1778
|
-
let dy;
|
|
1779
|
-
|
|
1780
|
-
// Vertical spacing between hit tests
|
|
1781
|
-
// dy is negative because we're moving upwards.
|
|
1782
|
-
dy = -8;
|
|
1783
|
-
|
|
1784
|
-
// Horizontal spacing between hit tests
|
|
1785
|
-
// Note: This value depends on the font size. If the gap is too small
|
|
1786
|
-
// we end up placing the cursor at the end of the expression when we
|
|
1787
|
-
// shouldn't.
|
|
1788
|
-
const dx = 5;
|
|
1789
|
-
if (this._findHitNode(this._containerBounds, x, y, dx, dy)) {
|
|
1790
|
-
return;
|
|
1791
|
-
}
|
|
1792
|
-
|
|
1793
|
-
// If we haven't found anything start from the top.
|
|
1794
|
-
y = this._containerBounds.top;
|
|
1795
|
-
|
|
1796
|
-
// dy is positive b/c we're going downwards.
|
|
1797
|
-
dy = 8;
|
|
1798
|
-
if (this._findHitNode(this._containerBounds, x, y, dx, dy)) {
|
|
1799
|
-
return;
|
|
1800
|
-
}
|
|
1801
|
-
const firstChildBounds = this._root.firstChild.getBoundingClientRect();
|
|
1802
|
-
const lastChildBounds = this._root.lastChild.getBoundingClientRect();
|
|
1803
|
-
const left = firstChildBounds.left;
|
|
1804
|
-
const right = lastChildBounds.right;
|
|
1805
|
-
|
|
1806
|
-
// We've exhausted all of the options. We're likely either to the right
|
|
1807
|
-
// or left of all of the math, so we place the cursor at the end to
|
|
1808
|
-
// which it's closest.
|
|
1809
|
-
if (Math.abs(x - right) < Math.abs(x - left)) {
|
|
1810
|
-
cursor.insAtRightEnd(this.mathField.mathField.__controller.root);
|
|
1811
|
-
} else {
|
|
1812
|
-
cursor.insAtLeftEnd(this.mathField.mathField.__controller.root);
|
|
1813
|
-
}
|
|
1814
|
-
// In that event, we need to update the cursor context ourselves.
|
|
1815
|
-
this.props.keypadElement && this.props.keypadElement.setCursor({
|
|
1816
|
-
context: this.mathField.contextForCursor()
|
|
1817
|
-
});
|
|
1818
|
-
});
|
|
1819
|
-
_defineProperty(this, "handleTouchStart", e => {
|
|
1820
|
-
e.stopPropagation();
|
|
1821
|
-
|
|
1822
|
-
// Hide the cursor handle on touch start, if the handle itself isn't
|
|
1823
|
-
// handling the touch event.
|
|
1824
|
-
this._hideCursorHandle();
|
|
1825
|
-
|
|
1826
|
-
// Cache the container bounds, so as to avoid re-computing. If we don't
|
|
1827
|
-
// have any content, then it's not necessary, since the cursor can't be
|
|
1828
|
-
// moved anyway.
|
|
1829
|
-
if (this.mathField.getContent() !== "") {
|
|
1830
|
-
this._containerBounds = this._container.getBoundingClientRect();
|
|
1831
|
-
|
|
1832
|
-
// Make the cursor visible and set the handle-less cursor's
|
|
1833
|
-
// location.
|
|
1834
|
-
const touch = e.changedTouches[0];
|
|
1835
|
-
this._insertCursorAtClosestNode(touch.clientX, touch.clientY);
|
|
1836
|
-
}
|
|
1837
|
-
|
|
1838
|
-
// Trigger a focus event, if we're not already focused.
|
|
1839
|
-
if (!this.state.focused) {
|
|
1840
|
-
this.focus();
|
|
1841
|
-
}
|
|
1842
|
-
});
|
|
1843
|
-
_defineProperty(this, "handleTouchMove", e => {
|
|
1844
|
-
e.stopPropagation();
|
|
1845
|
-
|
|
1846
|
-
// Update the handle-less cursor's location on move, if there's any
|
|
1847
|
-
// content in the box. Note that if the user touched outside the keypad
|
|
1848
|
-
// (e.g., with a different finger) during this touch interaction, we
|
|
1849
|
-
// may have blurred, in which case we should ignore the touch (since
|
|
1850
|
-
// the cursor is no longer visible and the input is no longer
|
|
1851
|
-
// highlighted).
|
|
1852
|
-
if (this.mathField.getContent() !== "" && this.state.focused) {
|
|
1853
|
-
const touch = e.changedTouches[0];
|
|
1854
|
-
this._insertCursorAtClosestNode(touch.clientX, touch.clientY);
|
|
1855
|
-
}
|
|
1856
|
-
});
|
|
1857
|
-
_defineProperty(this, "handleTouchEnd", e => {
|
|
1858
|
-
e.stopPropagation();
|
|
1859
|
-
|
|
1860
|
-
// And on touch-end, reveal the cursor, unless the input is empty. Note
|
|
1861
|
-
// that if the user touched outside the keypad (e.g., with a different
|
|
1862
|
-
// finger) during this touch interaction, we may have blurred, in which
|
|
1863
|
-
// case we should ignore the touch (since the cursor is no longer
|
|
1864
|
-
// visible and the input is no longer highlighted).
|
|
1865
|
-
if (this.mathField.getContent() !== "" && this.state.focused) {
|
|
1866
|
-
this._updateCursorHandle();
|
|
1867
|
-
}
|
|
1868
|
-
});
|
|
1869
|
-
_defineProperty(this, "onCursorHandleTouchStart", e => {
|
|
1870
|
-
// NOTE(charlie): The cursor handle is a child of this view, so whenever
|
|
1871
|
-
// it receives a touch event, that event would also typically be bubbled
|
|
1872
|
-
// up to our own handlers. However, we want the cursor to handle its own
|
|
1873
|
-
// touch events, and for this view to only handle touch events that
|
|
1874
|
-
// don't affect the cursor. As such, we `stopPropagation` on any touch
|
|
1875
|
-
// events that are being handled by the cursor, so as to avoid handling
|
|
1876
|
-
// them in our own touch handlers.
|
|
1877
|
-
e.stopPropagation();
|
|
1878
|
-
e.preventDefault();
|
|
1879
|
-
|
|
1880
|
-
// Cache the container bounds, so as to avoid re-computing.
|
|
1881
|
-
this._containerBounds = this._container.getBoundingClientRect();
|
|
1882
|
-
});
|
|
1883
|
-
_defineProperty(this, "_constrainToBound", (value, min, max, friction) => {
|
|
1884
|
-
if (value < min) {
|
|
1885
|
-
return min + (value - min) * friction;
|
|
1886
|
-
} else if (value > max) {
|
|
1887
|
-
return max + (value - max) * friction;
|
|
1888
|
-
} else {
|
|
1889
|
-
return value;
|
|
1890
|
-
}
|
|
1891
|
-
});
|
|
1892
|
-
_defineProperty(this, "onCursorHandleTouchMove", e => {
|
|
1893
|
-
e.stopPropagation();
|
|
1894
|
-
const x = e.changedTouches[0].clientX;
|
|
1895
|
-
const y = e.changedTouches[0].clientY;
|
|
1896
|
-
const relativeX = x - this._containerBounds.left;
|
|
1897
|
-
const relativeY = y - 2 * cursorHandleRadiusPx * cursorHandleDistanceMultiplier - this._containerBounds.top;
|
|
1898
|
-
|
|
1899
|
-
// We subtract the containerBounds left/top to correct for the
|
|
1900
|
-
// MathInput's position on the page. On top of that, we subtract an
|
|
1901
|
-
// additional 2 x {height of the cursor} so that the bottom of the
|
|
1902
|
-
// cursor tracks the user's finger, to make it visible under their
|
|
1903
|
-
// touch.
|
|
1904
|
-
this.setState({
|
|
1905
|
-
handle: {
|
|
1906
|
-
animateIntoPosition: false,
|
|
1907
|
-
visible: true,
|
|
1908
|
-
// TODO(charlie): Use clientX and clientY to avoid the need for
|
|
1909
|
-
// scroll offsets. This likely also means that the cursor
|
|
1910
|
-
// detection doesn't work when scrolled, since we're not
|
|
1911
|
-
// offsetting those values.
|
|
1912
|
-
x: this._constrainToBound(relativeX, 0, this._containerBounds.width, constrainingFrictionFactor),
|
|
1913
|
-
y: this._constrainToBound(relativeY, 0, this._containerBounds.height, constrainingFrictionFactor)
|
|
1914
|
-
}
|
|
1915
|
-
});
|
|
1502
|
+
// @ts-expect-error - TS2564 - Property 'recordTouchStartOutside' has no initializer and is not definitely assigned in the constructor.
|
|
1916
1503
|
|
|
1917
|
-
|
|
1918
|
-
// touching because they're dragging the handle which is a little
|
|
1919
|
-
// below where the cursor actually is.
|
|
1920
|
-
const distanceAboveFingerToTrySelecting = 22;
|
|
1921
|
-
const adjustedY = y - distanceAboveFingerToTrySelecting;
|
|
1922
|
-
this._insertCursorAtClosestNode(x, adjustedY);
|
|
1923
|
-
});
|
|
1924
|
-
_defineProperty(this, "onCursorHandleTouchEnd", e => {
|
|
1925
|
-
e.stopPropagation();
|
|
1926
|
-
this._updateCursorHandle(true);
|
|
1927
|
-
});
|
|
1928
|
-
_defineProperty(this, "onCursorHandleTouchCancel", e => {
|
|
1929
|
-
e.stopPropagation();
|
|
1930
|
-
this._updateCursorHandle(true);
|
|
1931
|
-
});
|
|
1932
|
-
_defineProperty(this, "domKeyToMathQuillKey", key => {
|
|
1933
|
-
const keyMap = {
|
|
1934
|
-
"+": "PLUS",
|
|
1935
|
-
"-": "MINUS",
|
|
1936
|
-
"*": "TIMES",
|
|
1937
|
-
"/": "DIVIDE",
|
|
1938
|
-
".": "DECIMAL",
|
|
1939
|
-
"%": "PERCENT",
|
|
1940
|
-
"=": "EQUAL",
|
|
1941
|
-
">": "GT",
|
|
1942
|
-
"<": "LT",
|
|
1943
|
-
"^": "EXP"
|
|
1944
|
-
};
|
|
1504
|
+
// @ts-expect-error - TS2564 - Property 'blurOnTouchEndOutside' has no initializer and is not definitely assigned in the constructor.
|
|
1945
1505
|
|
|
1946
|
-
|
|
1947
|
-
if (["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(key)) {
|
|
1948
|
-
return "NUM_".concat(key);
|
|
1949
|
-
}
|
|
1506
|
+
// @ts-expect-error - TS2564 - Property '_container' has no initializer and is not definitely assigned in the constructor.
|
|
1950
1507
|
|
|
1951
|
-
|
|
1952
|
-
else if (key === "Backspace") {
|
|
1953
|
-
return "BACKSPACE";
|
|
1954
|
-
}
|
|
1508
|
+
// @ts-expect-error - TS2564 - Property '_containerBounds' has no initializer and is not definitely assigned in the constructor.
|
|
1955
1509
|
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
// TODO(diedra): If the new value being added is off-screen to the right
|
|
1970
|
-
// due to the max-width of the text box, scroll the box to show the newest
|
|
1971
|
-
// value
|
|
1972
|
-
const value = this.mathField.getContent();
|
|
1973
|
-
if (this.props.value !== value) {
|
|
1974
|
-
this.mathField.setContent(this.props.value);
|
|
1975
|
-
this.props.onChange(value, false);
|
|
1976
|
-
this._hideCursorHandle();
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
});
|
|
1980
|
-
_defineProperty(this, "getBorderWidthPx", () => {
|
|
1981
|
-
// TODO(diedra): Move these to the common style package.
|
|
1982
|
-
const normalBorderWidthPx = 1;
|
|
1983
|
-
const focusedBorderWidthPx = 2;
|
|
1984
|
-
return this.state.focused ? focusedBorderWidthPx : normalBorderWidthPx;
|
|
1985
|
-
});
|
|
1986
|
-
_defineProperty(this, "getInputInnerPadding", () => {
|
|
1987
|
-
const paddingInset = totalDesiredPadding - this.getBorderWidthPx();
|
|
1988
|
-
|
|
1989
|
-
// Now, translate that to the appropriate padding for each direction.
|
|
1990
|
-
// The complication here is that we want numerals to be centered within
|
|
1991
|
-
// the input. However, Symbola (MathQuill's font of choice) renders
|
|
1992
|
-
// numerals with approximately 3px of padding below and 1px of padding
|
|
1993
|
-
// above (to make room for ascenders and descenders). So we ignore those
|
|
1994
|
-
// padding values for the vertical directions.
|
|
1995
|
-
const symbolaPaddingBottom = 3;
|
|
1996
|
-
const symbolaPaddingTop = 1;
|
|
1997
|
-
const padding = {
|
|
1998
|
-
paddingTop: paddingInset - symbolaPaddingTop,
|
|
1999
|
-
paddingRight: paddingInset,
|
|
2000
|
-
paddingBottom: paddingInset - symbolaPaddingBottom,
|
|
2001
|
-
paddingLeft: paddingInset
|
|
2002
|
-
};
|
|
2003
|
-
return padding;
|
|
2004
|
-
});
|
|
2005
|
-
}
|
|
1510
|
+
static defaultProps = {
|
|
1511
|
+
style: {},
|
|
1512
|
+
value: ""
|
|
1513
|
+
};
|
|
1514
|
+
state = {
|
|
1515
|
+
focused: false,
|
|
1516
|
+
handle: {
|
|
1517
|
+
animateIntoPosition: false,
|
|
1518
|
+
visible: false,
|
|
1519
|
+
x: 0,
|
|
1520
|
+
y: 0
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
2006
1523
|
componentDidMount() {
|
|
2007
1524
|
this._isMounted = true;
|
|
2008
1525
|
this.mathField = new MathWrapper(this._mathContainer, {
|
|
@@ -2126,6 +1643,519 @@ class MathInput extends React__namespace.Component {
|
|
|
2126
1643
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
2127
1644
|
this._clearKeypadBoundsCache());
|
|
2128
1645
|
}
|
|
1646
|
+
_clearKeypadBoundsCache = () => {
|
|
1647
|
+
this._keypadBounds = null;
|
|
1648
|
+
};
|
|
1649
|
+
_cacheKeypadBounds = keypadNode => {
|
|
1650
|
+
this._keypadBounds = keypadNode.getBoundingClientRect();
|
|
1651
|
+
};
|
|
1652
|
+
_updateInputPadding = () => {
|
|
1653
|
+
this._container = ReactDOM__default["default"].findDOMNode(this);
|
|
1654
|
+
this._root = this._container.querySelector(".mq-root-block");
|
|
1655
|
+
const padding = this.getInputInnerPadding();
|
|
1656
|
+
// NOTE(diedra): This overrides the default 2px padding from Mathquil.
|
|
1657
|
+
this._root.style.padding = `${padding.paddingTop}px ${padding.paddingRight}px` + ` ${padding.paddingBottom}px ${padding.paddingLeft}px`;
|
|
1658
|
+
this._root.style.fontSize = `${fontSizePt}pt`;
|
|
1659
|
+
};
|
|
1660
|
+
|
|
1661
|
+
/** Gets and cache they bounds of the keypadElement */
|
|
1662
|
+
_getKeypadBounds = () => {
|
|
1663
|
+
if (!this._keypadBounds) {
|
|
1664
|
+
const node = this.props.keypadElement?.getDOMNode();
|
|
1665
|
+
this._cacheKeypadBounds(node);
|
|
1666
|
+
}
|
|
1667
|
+
return this._keypadBounds;
|
|
1668
|
+
};
|
|
1669
|
+
_updateCursorHandle = animateIntoPosition => {
|
|
1670
|
+
const containerBounds = this._container.getBoundingClientRect();
|
|
1671
|
+
const cursor = this._container.querySelector(".mq-cursor");
|
|
1672
|
+
const cursorBounds = cursor.getBoundingClientRect();
|
|
1673
|
+
const cursorWidth = cursorBounds.width;
|
|
1674
|
+
const gapBelowCursor = 2;
|
|
1675
|
+
const inputInnerPadding = this.getInputInnerPadding();
|
|
1676
|
+
|
|
1677
|
+
// The cursor should never be further right or left than the edge of the
|
|
1678
|
+
// container's values.
|
|
1679
|
+
const furthestRightCursorBound = containerBounds.right - cursorWidth - inputInnerPadding.paddingRight;
|
|
1680
|
+
const furthestLeftCursorBound = containerBounds.left + cursorWidth + inputInnerPadding.paddingLeft;
|
|
1681
|
+
let cursorBoundsLeft = cursorBounds.left;
|
|
1682
|
+
if (cursorBounds.left > furthestRightCursorBound) {
|
|
1683
|
+
cursorBoundsLeft = furthestRightCursorBound;
|
|
1684
|
+
} else if (cursorBounds.left < furthestLeftCursorBound) {
|
|
1685
|
+
cursorBoundsLeft = furthestLeftCursorBound;
|
|
1686
|
+
}
|
|
1687
|
+
this.setState({
|
|
1688
|
+
handle: {
|
|
1689
|
+
visible: true,
|
|
1690
|
+
animateIntoPosition,
|
|
1691
|
+
// We subtract containerBounds' left/top to correct for the
|
|
1692
|
+
// position of the container within the page.
|
|
1693
|
+
x: cursorBoundsLeft + cursorWidth / 2 - containerBounds.left,
|
|
1694
|
+
y: cursorBounds.bottom + gapBelowCursor - containerBounds.top
|
|
1695
|
+
}
|
|
1696
|
+
});
|
|
1697
|
+
};
|
|
1698
|
+
_hideCursorHandle = () => {
|
|
1699
|
+
this.setState({
|
|
1700
|
+
handle: {
|
|
1701
|
+
visible: false,
|
|
1702
|
+
x: 0,
|
|
1703
|
+
y: 0
|
|
1704
|
+
}
|
|
1705
|
+
});
|
|
1706
|
+
};
|
|
1707
|
+
_handleScroll = () => {
|
|
1708
|
+
// If animateIntoPosition is false, the user is currently manually positioning
|
|
1709
|
+
// the cursor. This is important because the user can scroll the input field
|
|
1710
|
+
// with the curor handle, and we don't want to override that ability.
|
|
1711
|
+
// But we do want to hide the handle is the user is just scrolling the input field
|
|
1712
|
+
// normally, because the handle will not move with the scroll.
|
|
1713
|
+
if (this.state.handle.animateIntoPosition !== false) {
|
|
1714
|
+
this._hideCursorHandle();
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1717
|
+
blur = () => {
|
|
1718
|
+
this.mathField.blur();
|
|
1719
|
+
this.props.onBlur && this.props.onBlur();
|
|
1720
|
+
this.setState({
|
|
1721
|
+
focused: false,
|
|
1722
|
+
handle: {
|
|
1723
|
+
visible: false
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
};
|
|
1727
|
+
focus = () => {
|
|
1728
|
+
// Pass this component's handleKey method to the keypad so it can call
|
|
1729
|
+
// it whenever it needs to trigger a keypress action.
|
|
1730
|
+
this.props.keypadElement?.setKeyHandler(key => {
|
|
1731
|
+
const cursor = this.mathField.pressKey(key);
|
|
1732
|
+
|
|
1733
|
+
// Trigger an `onChange` if the value in the input changed, and hide
|
|
1734
|
+
// the cursor handle whenever the user types a key. If the value
|
|
1735
|
+
// changed as a result of a keypress, we need to be careful not to
|
|
1736
|
+
// call `setState` until after `onChange` has resolved.
|
|
1737
|
+
const hideCursor = () => {
|
|
1738
|
+
this.setState({
|
|
1739
|
+
handle: {
|
|
1740
|
+
visible: false
|
|
1741
|
+
}
|
|
1742
|
+
});
|
|
1743
|
+
};
|
|
1744
|
+
const value = this.mathField.getContent();
|
|
1745
|
+
if (this.props.value !== value) {
|
|
1746
|
+
this.props.onChange(value, hideCursor);
|
|
1747
|
+
} else {
|
|
1748
|
+
hideCursor();
|
|
1749
|
+
}
|
|
1750
|
+
return cursor;
|
|
1751
|
+
});
|
|
1752
|
+
this.mathField.focus();
|
|
1753
|
+
this.props?.onFocus();
|
|
1754
|
+
this.setState({
|
|
1755
|
+
focused: true
|
|
1756
|
+
}, () => {
|
|
1757
|
+
// NOTE(charlie): We use `setTimeout` to allow for a layout pass to
|
|
1758
|
+
// occur. Otherwise, the keypad is measured incorrectly. Ideally,
|
|
1759
|
+
// we'd use requestAnimationFrame here, but it's unsupported on
|
|
1760
|
+
// Android Browser 4.3.
|
|
1761
|
+
setTimeout(() => {
|
|
1762
|
+
if (this._isMounted) {
|
|
1763
|
+
// TODO(benkomalo): the keypad is animating at this point,
|
|
1764
|
+
// so we can't call _cacheKeypadBounds(), even though
|
|
1765
|
+
// it'd be nice to do so. It should probably be the case
|
|
1766
|
+
// that the higher level controller tells us when the
|
|
1767
|
+
// keypad is settled (then scrollIntoView wouldn't have
|
|
1768
|
+
// to make assumptions about that either).
|
|
1769
|
+
const maybeKeypadNode = this.props.keypadElement?.getDOMNode();
|
|
1770
|
+
scrollIntoView(this._container, maybeKeypadNode);
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
});
|
|
1774
|
+
};
|
|
1775
|
+
|
|
1776
|
+
/**
|
|
1777
|
+
* Tries to determine which DOM node to place the cursor next to based on
|
|
1778
|
+
* where the user drags the cursor handle. If it finds a node it will
|
|
1779
|
+
* place the cursor next to it, update the handle to be under the cursor,
|
|
1780
|
+
* and return true. If it doesn't find a node, it returns false.
|
|
1781
|
+
*
|
|
1782
|
+
* It searches for nodes by doing it tests at the following points:
|
|
1783
|
+
*
|
|
1784
|
+
* (x - dx, y), (x, y), (x + dx, y)
|
|
1785
|
+
*
|
|
1786
|
+
* If it doesn't find any nodes from the rendered math it will update y
|
|
1787
|
+
* by adding dy.
|
|
1788
|
+
*
|
|
1789
|
+
* The algorithm ends its search when y goes outside the bounds of
|
|
1790
|
+
* containerBounds.
|
|
1791
|
+
*
|
|
1792
|
+
* @param {DOMRect} containerBounds - bounds of the container node
|
|
1793
|
+
* @param {number} x - the initial x coordinate in the viewport
|
|
1794
|
+
* @param {number} y - the initial y coordinate in the viewport
|
|
1795
|
+
* @param {number} dx - horizontal spacing between elementFromPoint calls
|
|
1796
|
+
* @param {number} dy - vertical spacing between elementFromPoint calls,
|
|
1797
|
+
* sign determines direction.
|
|
1798
|
+
* @returns {boolean} - true if a node was hit, false otherwise.
|
|
1799
|
+
*/
|
|
1800
|
+
_findHitNode = (containerBounds, x, y, dx, dy) => {
|
|
1801
|
+
while (y >= containerBounds.top && y <= containerBounds.bottom) {
|
|
1802
|
+
y += dy;
|
|
1803
|
+
const points = [[x - dx, y], [x, y], [x + dx, y]];
|
|
1804
|
+
const elements = points
|
|
1805
|
+
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
1806
|
+
.map(point => document.elementFromPoint(...point))
|
|
1807
|
+
// We exclude the root container itself and any nodes marked
|
|
1808
|
+
// as non-leaf which are fractions, parens, and roots. The
|
|
1809
|
+
// children of those nodes are included in the list because
|
|
1810
|
+
// those are the items we care about placing the cursor next
|
|
1811
|
+
// to.
|
|
1812
|
+
//
|
|
1813
|
+
// MathQuill's mq-non-leaf is not applied to all non-leaf nodes
|
|
1814
|
+
// so the naming is a bit confusing. Although fractions are
|
|
1815
|
+
// included, neither mq-numerator nor mq-denominator nodes are
|
|
1816
|
+
// and neither are subscripts or superscripts.
|
|
1817
|
+
.filter(element => element && this._root.contains(element) && (!element.classList.contains("mq-root-block") && !element.classList.contains("mq-non-leaf") || element.classList.contains("mq-empty") || element.classList.contains("mq-hasCursor")));
|
|
1818
|
+
let hitNode = null;
|
|
1819
|
+
|
|
1820
|
+
// Contains only DOMNodes with child elements.
|
|
1821
|
+
const nonLeafElements = [];
|
|
1822
|
+
let max = 0;
|
|
1823
|
+
const counts = {};
|
|
1824
|
+
const elementsById = {};
|
|
1825
|
+
for (const element of elements) {
|
|
1826
|
+
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
1827
|
+
const id = element.getAttribute("mathquill-command-id");
|
|
1828
|
+
if (id != null) {
|
|
1829
|
+
counts[id] = (counts[id] || 0) + 1;
|
|
1830
|
+
elementsById[id] = element;
|
|
1831
|
+
} else {
|
|
1832
|
+
// @ts-expect-error - TS2345 - Argument of type 'Element | null' is not assignable to parameter of type 'HTMLElement | null'.
|
|
1833
|
+
nonLeafElements.push(element);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
// When determining which DOMNode to place the cursor beside, we
|
|
1838
|
+
// prefer leaf nodes. Hitting a leaf node is a good sign that the
|
|
1839
|
+
// cursor is really close to some piece of math that has been
|
|
1840
|
+
// rendered because leaf nodes contain text. Non-leaf nodes may
|
|
1841
|
+
// contain a lot of whitespace so the cursor may be further away
|
|
1842
|
+
// from actual text within the expression.
|
|
1843
|
+
//
|
|
1844
|
+
// Since we're doing three hit tests per loop it's possible that
|
|
1845
|
+
// we hit multiple leaf nodes at the same time. In this case we
|
|
1846
|
+
// we prefer the DOMNode with the most hits.
|
|
1847
|
+
// TODO(kevinb) consider preferring nodes hit by [x, y].
|
|
1848
|
+
for (const [id, count] of wonderStuffCore.entries(counts)) {
|
|
1849
|
+
if (count > max) {
|
|
1850
|
+
max = count;
|
|
1851
|
+
hitNode = elementsById[id];
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
// It's possible that two non-leaf nodes are right beside each
|
|
1856
|
+
// other. We don't bother counting the number of hits for each,
|
|
1857
|
+
// b/c this seems like an unlikely situation. Also, ignoring the
|
|
1858
|
+
// hit count in the situation should not have serious effects on
|
|
1859
|
+
// the overall accuracy of the algorithm.
|
|
1860
|
+
if (hitNode == null && nonLeafElements.length > 0) {
|
|
1861
|
+
// @ts-expect-error - TS2322 - Type 'HTMLElement | null' is not assignable to type 'null'.
|
|
1862
|
+
hitNode = nonLeafElements[0];
|
|
1863
|
+
}
|
|
1864
|
+
if (hitNode !== null) {
|
|
1865
|
+
this.mathField.setCursorPosition(x, y, hitNode);
|
|
1866
|
+
return true;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
return false;
|
|
1870
|
+
};
|
|
1871
|
+
|
|
1872
|
+
/**
|
|
1873
|
+
* Inserts the cursor at the DOM node closest to the given coordinates,
|
|
1874
|
+
* based on hit-tests conducted using #_findHitNode.
|
|
1875
|
+
*
|
|
1876
|
+
* @param {number} x - the x coordinate in the viewport
|
|
1877
|
+
* @param {number} y - the y coordinate in the viewport
|
|
1878
|
+
*/
|
|
1879
|
+
_insertCursorAtClosestNode = (x, y) => {
|
|
1880
|
+
const cursor = this.mathField.getCursor();
|
|
1881
|
+
|
|
1882
|
+
// Pre-emptively check if the input has any child nodes; if not, the
|
|
1883
|
+
// input is empty, so we throw the cursor at the start.
|
|
1884
|
+
if (!this._root.hasChildNodes()) {
|
|
1885
|
+
cursor.insAtLeftEnd(this.mathField.mathField.__controller.root);
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
// NOTE(diedra): The adding and subtracting of 10 or 15 pixels here accounts
|
|
1890
|
+
// for the padding that surrounds the input values.
|
|
1891
|
+
if (y > this._containerBounds.bottom) {
|
|
1892
|
+
y = this._containerBounds.bottom - 10;
|
|
1893
|
+
} else if (y < this._containerBounds.top) {
|
|
1894
|
+
y = this._containerBounds.top + 10;
|
|
1895
|
+
}
|
|
1896
|
+
if (x > this._containerBounds.right) {
|
|
1897
|
+
x = this._containerBounds.right - 15;
|
|
1898
|
+
} else if (x < this._containerBounds.left) {
|
|
1899
|
+
x = this._containerBounds.left + 15;
|
|
1900
|
+
}
|
|
1901
|
+
let dy;
|
|
1902
|
+
|
|
1903
|
+
// Vertical spacing between hit tests
|
|
1904
|
+
// dy is negative because we're moving upwards.
|
|
1905
|
+
dy = -8;
|
|
1906
|
+
|
|
1907
|
+
// Horizontal spacing between hit tests
|
|
1908
|
+
// Note: This value depends on the font size. If the gap is too small
|
|
1909
|
+
// we end up placing the cursor at the end of the expression when we
|
|
1910
|
+
// shouldn't.
|
|
1911
|
+
const dx = 5;
|
|
1912
|
+
if (this._findHitNode(this._containerBounds, x, y, dx, dy)) {
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
// If we haven't found anything start from the top.
|
|
1917
|
+
y = this._containerBounds.top;
|
|
1918
|
+
|
|
1919
|
+
// dy is positive b/c we're going downwards.
|
|
1920
|
+
dy = 8;
|
|
1921
|
+
if (this._findHitNode(this._containerBounds, x, y, dx, dy)) {
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
const firstChildBounds = this._root.firstChild.getBoundingClientRect();
|
|
1925
|
+
const lastChildBounds = this._root.lastChild.getBoundingClientRect();
|
|
1926
|
+
const left = firstChildBounds.left;
|
|
1927
|
+
const right = lastChildBounds.right;
|
|
1928
|
+
|
|
1929
|
+
// We've exhausted all of the options. We're likely either to the right
|
|
1930
|
+
// or left of all of the math, so we place the cursor at the end to
|
|
1931
|
+
// which it's closest.
|
|
1932
|
+
if (Math.abs(x - right) < Math.abs(x - left)) {
|
|
1933
|
+
cursor.insAtRightEnd(this.mathField.mathField.__controller.root);
|
|
1934
|
+
} else {
|
|
1935
|
+
cursor.insAtLeftEnd(this.mathField.mathField.__controller.root);
|
|
1936
|
+
}
|
|
1937
|
+
// In that event, we need to update the cursor context ourselves.
|
|
1938
|
+
this.props.keypadElement && this.props.keypadElement.setCursor({
|
|
1939
|
+
context: this.mathField.contextForCursor()
|
|
1940
|
+
});
|
|
1941
|
+
};
|
|
1942
|
+
handleTouchStart = e => {
|
|
1943
|
+
e.stopPropagation();
|
|
1944
|
+
|
|
1945
|
+
// Hide the cursor handle on touch start, if the handle itself isn't
|
|
1946
|
+
// handling the touch event.
|
|
1947
|
+
this._hideCursorHandle();
|
|
1948
|
+
|
|
1949
|
+
// Cache the container bounds, so as to avoid re-computing. If we don't
|
|
1950
|
+
// have any content, then it's not necessary, since the cursor can't be
|
|
1951
|
+
// moved anyway.
|
|
1952
|
+
if (this.mathField.getContent() !== "") {
|
|
1953
|
+
this._containerBounds = this._container.getBoundingClientRect();
|
|
1954
|
+
|
|
1955
|
+
// Make the cursor visible and set the handle-less cursor's
|
|
1956
|
+
// location.
|
|
1957
|
+
const touch = e.changedTouches[0];
|
|
1958
|
+
this._insertCursorAtClosestNode(touch.clientX, touch.clientY);
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// Trigger a focus event, if we're not already focused.
|
|
1962
|
+
if (!this.state.focused) {
|
|
1963
|
+
this.focus();
|
|
1964
|
+
}
|
|
1965
|
+
};
|
|
1966
|
+
handleTouchMove = e => {
|
|
1967
|
+
e.stopPropagation();
|
|
1968
|
+
|
|
1969
|
+
// Update the handle-less cursor's location on move, if there's any
|
|
1970
|
+
// content in the box. Note that if the user touched outside the keypad
|
|
1971
|
+
// (e.g., with a different finger) during this touch interaction, we
|
|
1972
|
+
// may have blurred, in which case we should ignore the touch (since
|
|
1973
|
+
// the cursor is no longer visible and the input is no longer
|
|
1974
|
+
// highlighted).
|
|
1975
|
+
if (this.mathField.getContent() !== "" && this.state.focused) {
|
|
1976
|
+
const touch = e.changedTouches[0];
|
|
1977
|
+
this._insertCursorAtClosestNode(touch.clientX, touch.clientY);
|
|
1978
|
+
}
|
|
1979
|
+
};
|
|
1980
|
+
handleTouchEnd = e => {
|
|
1981
|
+
e.stopPropagation();
|
|
1982
|
+
|
|
1983
|
+
// And on touch-end, reveal the cursor, unless the input is empty. Note
|
|
1984
|
+
// that if the user touched outside the keypad (e.g., with a different
|
|
1985
|
+
// finger) during this touch interaction, we may have blurred, in which
|
|
1986
|
+
// case we should ignore the touch (since the cursor is no longer
|
|
1987
|
+
// visible and the input is no longer highlighted).
|
|
1988
|
+
if (this.mathField.getContent() !== "" && this.state.focused) {
|
|
1989
|
+
this._updateCursorHandle();
|
|
1990
|
+
}
|
|
1991
|
+
};
|
|
1992
|
+
|
|
1993
|
+
/**
|
|
1994
|
+
* When a touch starts in the cursor handle, we track it so as to avoid
|
|
1995
|
+
* handling any touch events ourself.
|
|
1996
|
+
*
|
|
1997
|
+
* @param {TouchEvent} e - the raw touch event from the browser
|
|
1998
|
+
*/
|
|
1999
|
+
onCursorHandleTouchStart = e => {
|
|
2000
|
+
// NOTE(charlie): The cursor handle is a child of this view, so whenever
|
|
2001
|
+
// it receives a touch event, that event would also typically be bubbled
|
|
2002
|
+
// up to our own handlers. However, we want the cursor to handle its own
|
|
2003
|
+
// touch events, and for this view to only handle touch events that
|
|
2004
|
+
// don't affect the cursor. As such, we `stopPropagation` on any touch
|
|
2005
|
+
// events that are being handled by the cursor, so as to avoid handling
|
|
2006
|
+
// them in our own touch handlers.
|
|
2007
|
+
e.stopPropagation();
|
|
2008
|
+
e.preventDefault();
|
|
2009
|
+
|
|
2010
|
+
// Cache the container bounds, so as to avoid re-computing.
|
|
2011
|
+
this._containerBounds = this._container.getBoundingClientRect();
|
|
2012
|
+
};
|
|
2013
|
+
_constrainToBound = (value, min, max, friction) => {
|
|
2014
|
+
if (value < min) {
|
|
2015
|
+
return min + (value - min) * friction;
|
|
2016
|
+
} else if (value > max) {
|
|
2017
|
+
return max + (value - max) * friction;
|
|
2018
|
+
} else {
|
|
2019
|
+
return value;
|
|
2020
|
+
}
|
|
2021
|
+
};
|
|
2022
|
+
|
|
2023
|
+
/**
|
|
2024
|
+
* When the user moves the cursor handle update the position of the cursor
|
|
2025
|
+
* and the handle.
|
|
2026
|
+
*
|
|
2027
|
+
* @param {TouchEvent} e - the raw touch event from the browser
|
|
2028
|
+
*/
|
|
2029
|
+
onCursorHandleTouchMove = e => {
|
|
2030
|
+
e.stopPropagation();
|
|
2031
|
+
const x = e.changedTouches[0].clientX;
|
|
2032
|
+
const y = e.changedTouches[0].clientY;
|
|
2033
|
+
const relativeX = x - this._containerBounds.left;
|
|
2034
|
+
const relativeY = y - 2 * cursorHandleRadiusPx * cursorHandleDistanceMultiplier - this._containerBounds.top;
|
|
2035
|
+
|
|
2036
|
+
// We subtract the containerBounds left/top to correct for the
|
|
2037
|
+
// MathInput's position on the page. On top of that, we subtract an
|
|
2038
|
+
// additional 2 x {height of the cursor} so that the bottom of the
|
|
2039
|
+
// cursor tracks the user's finger, to make it visible under their
|
|
2040
|
+
// touch.
|
|
2041
|
+
this.setState({
|
|
2042
|
+
handle: {
|
|
2043
|
+
animateIntoPosition: false,
|
|
2044
|
+
visible: true,
|
|
2045
|
+
// TODO(charlie): Use clientX and clientY to avoid the need for
|
|
2046
|
+
// scroll offsets. This likely also means that the cursor
|
|
2047
|
+
// detection doesn't work when scrolled, since we're not
|
|
2048
|
+
// offsetting those values.
|
|
2049
|
+
x: this._constrainToBound(relativeX, 0, this._containerBounds.width, constrainingFrictionFactor),
|
|
2050
|
+
y: this._constrainToBound(relativeY, 0, this._containerBounds.height, constrainingFrictionFactor)
|
|
2051
|
+
}
|
|
2052
|
+
});
|
|
2053
|
+
|
|
2054
|
+
// Use a y-coordinate that's just above where the user is actually
|
|
2055
|
+
// touching because they're dragging the handle which is a little
|
|
2056
|
+
// below where the cursor actually is.
|
|
2057
|
+
const distanceAboveFingerToTrySelecting = 22;
|
|
2058
|
+
const adjustedY = y - distanceAboveFingerToTrySelecting;
|
|
2059
|
+
this._insertCursorAtClosestNode(x, adjustedY);
|
|
2060
|
+
};
|
|
2061
|
+
|
|
2062
|
+
/**
|
|
2063
|
+
* When the user releases the cursor handle, animate it back into place.
|
|
2064
|
+
*
|
|
2065
|
+
* @param {TouchEvent} e - the raw touch event from the browser
|
|
2066
|
+
*/
|
|
2067
|
+
onCursorHandleTouchEnd = e => {
|
|
2068
|
+
e.stopPropagation();
|
|
2069
|
+
this._updateCursorHandle(true);
|
|
2070
|
+
};
|
|
2071
|
+
|
|
2072
|
+
/**
|
|
2073
|
+
* If the gesture is cancelled mid-drag, simply hide it.
|
|
2074
|
+
*
|
|
2075
|
+
* @param {TouchEvent} e - the raw touch event from the browser
|
|
2076
|
+
*/
|
|
2077
|
+
onCursorHandleTouchCancel = e => {
|
|
2078
|
+
e.stopPropagation();
|
|
2079
|
+
this._updateCursorHandle(true);
|
|
2080
|
+
};
|
|
2081
|
+
domKeyToMathQuillKey = key => {
|
|
2082
|
+
const keyMap = {
|
|
2083
|
+
"+": "PLUS",
|
|
2084
|
+
"-": "MINUS",
|
|
2085
|
+
"*": "TIMES",
|
|
2086
|
+
"/": "DIVIDE",
|
|
2087
|
+
".": "DECIMAL",
|
|
2088
|
+
"%": "PERCENT",
|
|
2089
|
+
"=": "EQUAL",
|
|
2090
|
+
">": "GT",
|
|
2091
|
+
"<": "LT",
|
|
2092
|
+
"^": "EXP"
|
|
2093
|
+
};
|
|
2094
|
+
|
|
2095
|
+
// Numbers
|
|
2096
|
+
if (["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(key)) {
|
|
2097
|
+
return `NUM_${key}`;
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
// Movement keys
|
|
2101
|
+
else if (key === "Backspace") {
|
|
2102
|
+
return "BACKSPACE";
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
// Operators
|
|
2106
|
+
else if (key in keyMap) {
|
|
2107
|
+
return keyMap[key];
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// The key pressed doesn't map to any of the math input operators
|
|
2111
|
+
return null;
|
|
2112
|
+
};
|
|
2113
|
+
handleKeyUp = event => {
|
|
2114
|
+
const mathQuillKey = this.domKeyToMathQuillKey(event.key);
|
|
2115
|
+
if (mathQuillKey) {
|
|
2116
|
+
this.mathField.pressKey(mathQuillKey);
|
|
2117
|
+
|
|
2118
|
+
// TODO(diedra): If the new value being added is off-screen to the right
|
|
2119
|
+
// due to the max-width of the text box, scroll the box to show the newest
|
|
2120
|
+
// value
|
|
2121
|
+
const value = this.mathField.getContent();
|
|
2122
|
+
if (this.props.value !== value) {
|
|
2123
|
+
this.mathField.setContent(this.props.value);
|
|
2124
|
+
this.props.onChange(value, false);
|
|
2125
|
+
this._hideCursorHandle();
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
2129
|
+
getBorderWidthPx = () => {
|
|
2130
|
+
// TODO(diedra): Move these to the common style package.
|
|
2131
|
+
const normalBorderWidthPx = 1;
|
|
2132
|
+
const focusedBorderWidthPx = 2;
|
|
2133
|
+
return this.state.focused ? focusedBorderWidthPx : normalBorderWidthPx;
|
|
2134
|
+
};
|
|
2135
|
+
|
|
2136
|
+
// Calculate the appropriate padding based on the border width (which is
|
|
2137
|
+
// considered 'padding', since we're using 'border-box') and the fact
|
|
2138
|
+
// that MathQuill automatically applies 2px of padding to the inner
|
|
2139
|
+
// input.
|
|
2140
|
+
getInputInnerPadding = () => {
|
|
2141
|
+
const paddingInset = totalDesiredPadding - this.getBorderWidthPx();
|
|
2142
|
+
|
|
2143
|
+
// Now, translate that to the appropriate padding for each direction.
|
|
2144
|
+
// The complication here is that we want numerals to be centered within
|
|
2145
|
+
// the input. However, Symbola (MathQuill's font of choice) renders
|
|
2146
|
+
// numerals with approximately 3px of padding below and 1px of padding
|
|
2147
|
+
// above (to make room for ascenders and descenders). So we ignore those
|
|
2148
|
+
// padding values for the vertical directions.
|
|
2149
|
+
const symbolaPaddingBottom = 3;
|
|
2150
|
+
const symbolaPaddingTop = 1;
|
|
2151
|
+
const padding = {
|
|
2152
|
+
paddingTop: paddingInset - symbolaPaddingTop,
|
|
2153
|
+
paddingRight: paddingInset,
|
|
2154
|
+
paddingBottom: paddingInset - symbolaPaddingBottom,
|
|
2155
|
+
paddingLeft: paddingInset
|
|
2156
|
+
};
|
|
2157
|
+
return padding;
|
|
2158
|
+
};
|
|
2129
2159
|
render() {
|
|
2130
2160
|
const {
|
|
2131
2161
|
focused,
|
|
@@ -2181,10 +2211,6 @@ class MathInput extends React__namespace.Component {
|
|
|
2181
2211
|
})));
|
|
2182
2212
|
}
|
|
2183
2213
|
}
|
|
2184
|
-
_defineProperty(MathInput, "defaultProps", {
|
|
2185
|
-
style: {},
|
|
2186
|
-
value: ""
|
|
2187
|
-
});
|
|
2188
2214
|
const fontSizePt = 18;
|
|
2189
2215
|
const inputMaxWidth = 128;
|
|
2190
2216
|
|
|
@@ -2335,12 +2361,12 @@ const styles$i = aphrodite.StyleSheet.create({
|
|
|
2335
2361
|
height: 38,
|
|
2336
2362
|
boxSizing: "border-box",
|
|
2337
2363
|
borderRadius: 3,
|
|
2338
|
-
border:
|
|
2364
|
+
border: `1px solid transparent`,
|
|
2339
2365
|
marginRight: 1,
|
|
2340
2366
|
marginLeft: 1
|
|
2341
2367
|
},
|
|
2342
2368
|
hovered: {
|
|
2343
|
-
background:
|
|
2369
|
+
background: `linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ${Color__default["default"].white}`,
|
|
2344
2370
|
border: "1px solid #1865F2"
|
|
2345
2371
|
},
|
|
2346
2372
|
pressed: {
|
|
@@ -2348,11 +2374,11 @@ const styles$i = aphrodite.StyleSheet.create({
|
|
|
2348
2374
|
},
|
|
2349
2375
|
focused: {
|
|
2350
2376
|
outline: "none",
|
|
2351
|
-
border:
|
|
2377
|
+
border: `2px solid ${Color__default["default"].blue}`
|
|
2352
2378
|
},
|
|
2353
2379
|
innerBox: {
|
|
2354
2380
|
boxSizing: "border-box",
|
|
2355
|
-
border:
|
|
2381
|
+
border: `1px solid transparent`,
|
|
2356
2382
|
borderRadius: 2,
|
|
2357
2383
|
display: "flex",
|
|
2358
2384
|
flex: 1,
|
|
@@ -2360,7 +2386,7 @@ const styles$i = aphrodite.StyleSheet.create({
|
|
|
2360
2386
|
alignItems: "center"
|
|
2361
2387
|
},
|
|
2362
2388
|
innerBoxPressed: {
|
|
2363
|
-
border:
|
|
2389
|
+
border: `1px solid ${Color__default["default"].white}`
|
|
2364
2390
|
},
|
|
2365
2391
|
activeIndicator: {
|
|
2366
2392
|
position: "absolute",
|
|
@@ -2372,7 +2398,7 @@ const styles$i = aphrodite.StyleSheet.create({
|
|
|
2372
2398
|
},
|
|
2373
2399
|
clickable: {
|
|
2374
2400
|
":focus": {
|
|
2375
|
-
outline:
|
|
2401
|
+
outline: `none`
|
|
2376
2402
|
}
|
|
2377
2403
|
}
|
|
2378
2404
|
});
|
|
@@ -2452,7 +2478,7 @@ function Tabbar(props) {
|
|
|
2452
2478
|
}, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
2453
2479
|
style: [styles$h.pages]
|
|
2454
2480
|
}, items.map(item => /*#__PURE__*/React__namespace.createElement(TabbarItem, {
|
|
2455
|
-
key:
|
|
2481
|
+
key: `tabbar-item-${item}`,
|
|
2456
2482
|
itemState: item === selectedItem ? "active" : "inactive",
|
|
2457
2483
|
itemType: item,
|
|
2458
2484
|
onClick: () => {
|
|
@@ -4652,7 +4678,7 @@ function ButtonAsset(_ref) {
|
|
|
4652
4678
|
// this line forces an exhaustive check of all keys;
|
|
4653
4679
|
// if a key is not handled, the compiler will complain.
|
|
4654
4680
|
const unhandledKey = id;
|
|
4655
|
-
throw new Error(
|
|
4681
|
+
throw new Error(`Unhandled key: ${unhandledKey}`);
|
|
4656
4682
|
}
|
|
4657
4683
|
}
|
|
4658
4684
|
|
|
@@ -4702,7 +4728,7 @@ const styles$g = aphrodite.StyleSheet.create({
|
|
|
4702
4728
|
display: "flex",
|
|
4703
4729
|
justifyContent: "center",
|
|
4704
4730
|
alignItems: "center",
|
|
4705
|
-
boxShadow:
|
|
4731
|
+
boxShadow: `0px 1px 0px ${Color__default["default"].offBlack32}`,
|
|
4706
4732
|
boxSizing: "border-box",
|
|
4707
4733
|
background: Color__default["default"].white,
|
|
4708
4734
|
borderRadius: 4,
|
|
@@ -4727,7 +4753,7 @@ const styles$g = aphrodite.StyleSheet.create({
|
|
|
4727
4753
|
pressed: {
|
|
4728
4754
|
border: "2px solid #1B50B3",
|
|
4729
4755
|
padding: 0,
|
|
4730
|
-
background:
|
|
4756
|
+
background: `linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ${Color__default["default"].white}`,
|
|
4731
4757
|
boxShadow: "none"
|
|
4732
4758
|
},
|
|
4733
4759
|
outerBoxBase: {
|
|
@@ -4743,7 +4769,7 @@ const styles$g = aphrodite.StyleSheet.create({
|
|
|
4743
4769
|
height: "100%",
|
|
4744
4770
|
boxSizing: "border-box",
|
|
4745
4771
|
":focus": {
|
|
4746
|
-
outline:
|
|
4772
|
+
outline: `none`
|
|
4747
4773
|
}
|
|
4748
4774
|
}
|
|
4749
4775
|
});
|
|
@@ -5037,7 +5063,7 @@ function getStyles(key) {
|
|
|
5037
5063
|
case "LEFT":
|
|
5038
5064
|
return styles$f.left;
|
|
5039
5065
|
default:
|
|
5040
|
-
throw new Error(
|
|
5066
|
+
throw new Error(`Invalid key: ${key}`);
|
|
5041
5067
|
}
|
|
5042
5068
|
}
|
|
5043
5069
|
function NavigationButton(_ref) {
|
|
@@ -5078,7 +5104,7 @@ const styles$f = aphrodite.StyleSheet.create({
|
|
|
5078
5104
|
width: "100%",
|
|
5079
5105
|
height: "100%",
|
|
5080
5106
|
":focus": {
|
|
5081
|
-
outline:
|
|
5107
|
+
outline: `none`
|
|
5082
5108
|
}
|
|
5083
5109
|
},
|
|
5084
5110
|
outerBoxBase: {
|
|
@@ -5086,7 +5112,7 @@ const styles$f = aphrodite.StyleSheet.create({
|
|
|
5086
5112
|
width: "100%"
|
|
5087
5113
|
},
|
|
5088
5114
|
base: {
|
|
5089
|
-
boxShadow:
|
|
5115
|
+
boxShadow: `0px 1px 0px ${Color__default["default"].offBlack32}`,
|
|
5090
5116
|
display: "flex",
|
|
5091
5117
|
justifyContent: "center",
|
|
5092
5118
|
alignItems: "center",
|
|
@@ -5120,7 +5146,7 @@ const styles$f = aphrodite.StyleSheet.create({
|
|
|
5120
5146
|
},
|
|
5121
5147
|
pressed: {
|
|
5122
5148
|
border: "2px solid #1B50B3",
|
|
5123
|
-
background:
|
|
5149
|
+
background: `linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ${Color__default["default"].white}`,
|
|
5124
5150
|
boxShadow: "none"
|
|
5125
5151
|
}
|
|
5126
5152
|
});
|
|
@@ -5238,7 +5264,6 @@ const defaultProps = {
|
|
|
5238
5264
|
extraKeys: []
|
|
5239
5265
|
};
|
|
5240
5266
|
function getAvailableTabs(props) {
|
|
5241
|
-
var _props$extraKeys;
|
|
5242
5267
|
// We don't want to show any available tabs on the fractions keypad
|
|
5243
5268
|
if (props.fractionsOnly) {
|
|
5244
5269
|
return [];
|
|
@@ -5252,7 +5277,7 @@ function getAvailableTabs(props) {
|
|
|
5252
5277
|
if (props.trigonometry) {
|
|
5253
5278
|
tabs.push("Geometry");
|
|
5254
5279
|
}
|
|
5255
|
-
if (
|
|
5280
|
+
if (props.extraKeys?.length) {
|
|
5256
5281
|
tabs.push("Extras");
|
|
5257
5282
|
}
|
|
5258
5283
|
return tabs;
|
|
@@ -5465,6 +5490,7 @@ class TransitionChild extends React__namespace.Component {
|
|
|
5465
5490
|
// We keep track of all of the current applied classes so that we can remove
|
|
5466
5491
|
// them before a new transition starts in the case of the current transition
|
|
5467
5492
|
// being interrupted.
|
|
5493
|
+
_isMounted = false;
|
|
5468
5494
|
|
|
5469
5495
|
// The use of getDerivedStateFromProps here is to avoid an extra call to
|
|
5470
5496
|
// setState if the component re-enters. This can happen if TransitionGroup
|
|
@@ -5483,38 +5509,6 @@ class TransitionChild extends React__namespace.Component {
|
|
|
5483
5509
|
}
|
|
5484
5510
|
constructor(props) {
|
|
5485
5511
|
super(props);
|
|
5486
|
-
_defineProperty(this, "classNameQueue", void 0);
|
|
5487
|
-
_defineProperty(this, "appliedClassNames", void 0);
|
|
5488
|
-
_defineProperty(this, "_isMounted", false);
|
|
5489
|
-
_defineProperty(this, "addClass", (elem, className) => {
|
|
5490
|
-
if (className) {
|
|
5491
|
-
elem.classList.add(className);
|
|
5492
|
-
this.appliedClassNames.add(className);
|
|
5493
|
-
}
|
|
5494
|
-
});
|
|
5495
|
-
_defineProperty(this, "removeClass", (elem, className) => {
|
|
5496
|
-
if (className) {
|
|
5497
|
-
elem.classList.remove(className);
|
|
5498
|
-
this.appliedClassNames.delete(className);
|
|
5499
|
-
}
|
|
5500
|
-
});
|
|
5501
|
-
_defineProperty(this, "flushClassNameQueue", () => {
|
|
5502
|
-
if (this._isMounted) {
|
|
5503
|
-
const node = ReactDOM__default["default"].findDOMNode(this);
|
|
5504
|
-
if (node instanceof Element) {
|
|
5505
|
-
this.classNameQueue.forEach(_ref2 => {
|
|
5506
|
-
let [removeClassName, addClassName] = _ref2;
|
|
5507
|
-
// Remove the old class before adding a new class just
|
|
5508
|
-
// in case the new class is the same as the old one.
|
|
5509
|
-
this.removeClass(node, removeClassName);
|
|
5510
|
-
this.addClass(node, addClassName);
|
|
5511
|
-
});
|
|
5512
|
-
}
|
|
5513
|
-
}
|
|
5514
|
-
|
|
5515
|
-
// Remove all items in the Array.
|
|
5516
|
-
this.classNameQueue.length = 0;
|
|
5517
|
-
});
|
|
5518
5512
|
this._isMounted = false;
|
|
5519
5513
|
this.classNameQueue = [];
|
|
5520
5514
|
this.appliedClassNames = new Set();
|
|
@@ -5558,6 +5552,18 @@ class TransitionChild extends React__namespace.Component {
|
|
|
5558
5552
|
this.removeClass(node, className);
|
|
5559
5553
|
}
|
|
5560
5554
|
}
|
|
5555
|
+
addClass = (elem, className) => {
|
|
5556
|
+
if (className) {
|
|
5557
|
+
elem.classList.add(className);
|
|
5558
|
+
this.appliedClassNames.add(className);
|
|
5559
|
+
}
|
|
5560
|
+
};
|
|
5561
|
+
removeClass = (elem, className) => {
|
|
5562
|
+
if (className) {
|
|
5563
|
+
elem.classList.remove(className);
|
|
5564
|
+
this.appliedClassNames.delete(className);
|
|
5565
|
+
}
|
|
5566
|
+
};
|
|
5561
5567
|
transition(animationType, duration) {
|
|
5562
5568
|
const node = ReactDOM__default["default"].findDOMNode(this);
|
|
5563
5569
|
if (!(node instanceof Element)) {
|
|
@@ -5599,6 +5605,23 @@ class TransitionChild extends React__namespace.Component {
|
|
|
5599
5605
|
// Queue operation for after the next paint.
|
|
5600
5606
|
this.props.schedule.timeout(this.flushClassNameQueue, 0);
|
|
5601
5607
|
}
|
|
5608
|
+
flushClassNameQueue = () => {
|
|
5609
|
+
if (this._isMounted) {
|
|
5610
|
+
const node = ReactDOM__default["default"].findDOMNode(this);
|
|
5611
|
+
if (node instanceof Element) {
|
|
5612
|
+
this.classNameQueue.forEach(_ref2 => {
|
|
5613
|
+
let [removeClassName, addClassName] = _ref2;
|
|
5614
|
+
// Remove the old class before adding a new class just
|
|
5615
|
+
// in case the new class is the same as the old one.
|
|
5616
|
+
this.removeClass(node, removeClassName);
|
|
5617
|
+
this.addClass(node, addClassName);
|
|
5618
|
+
});
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
|
|
5622
|
+
// Remove all items in the Array.
|
|
5623
|
+
this.classNameQueue.length = 0;
|
|
5624
|
+
};
|
|
5602
5625
|
render() {
|
|
5603
5626
|
const {
|
|
5604
5627
|
status
|
|
@@ -5682,71 +5705,13 @@ const AnimationDurationInMS = 200;
|
|
|
5682
5705
|
* can't have methods attached to them).
|
|
5683
5706
|
*/
|
|
5684
5707
|
class MobileKeypad extends React__namespace.Component {
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
containerWidth: 0
|
|
5692
|
-
});
|
|
5693
|
-
_defineProperty(this, "_resize", () => {
|
|
5694
|
-
var _this$_containerRef$c;
|
|
5695
|
-
const containerWidth = ((_this$_containerRef$c = this._containerRef.current) === null || _this$_containerRef$c === void 0 ? void 0 : _this$_containerRef$c.clientWidth) || 0;
|
|
5696
|
-
this.setState({
|
|
5697
|
-
containerWidth
|
|
5698
|
-
});
|
|
5699
|
-
});
|
|
5700
|
-
_defineProperty(this, "_throttleResizeHandler", () => {
|
|
5701
|
-
if (this._throttleResize) {
|
|
5702
|
-
return;
|
|
5703
|
-
}
|
|
5704
|
-
this._throttleResize = true;
|
|
5705
|
-
setTimeout(() => {
|
|
5706
|
-
this._resize();
|
|
5707
|
-
this._throttleResize = false;
|
|
5708
|
-
}, 100);
|
|
5709
|
-
});
|
|
5710
|
-
_defineProperty(this, "activate", () => {
|
|
5711
|
-
this.props.setKeypadActive(true);
|
|
5712
|
-
});
|
|
5713
|
-
_defineProperty(this, "dismiss", () => {
|
|
5714
|
-
var _this$props$onDismiss, _this$props;
|
|
5715
|
-
this.props.setKeypadActive(false);
|
|
5716
|
-
(_this$props$onDismiss = (_this$props = this.props).onDismiss) === null || _this$props$onDismiss === void 0 ? void 0 : _this$props$onDismiss.call(_this$props);
|
|
5717
|
-
});
|
|
5718
|
-
_defineProperty(this, "configure", (configuration, cb) => {
|
|
5719
|
-
this.setState({
|
|
5720
|
-
keypadConfig: configuration
|
|
5721
|
-
});
|
|
5722
|
-
|
|
5723
|
-
// TODO(matthewc)[LC-1080]: this was brought in from v1's ProvidedKeypad.
|
|
5724
|
-
// We need to investigate whether we still need this.
|
|
5725
|
-
// HACK(charlie): In Perseus, triggering a focus causes the keypad to
|
|
5726
|
-
// animate into view and re-configure. We'd like to provide the option
|
|
5727
|
-
// to re-render the re-configured keypad before animating it into view,
|
|
5728
|
-
// to avoid jank in the animation. As such, we support passing a
|
|
5729
|
-
// callback into `configureKeypad`. However, implementing this properly
|
|
5730
|
-
// would require middleware, etc., so we just hack it on with
|
|
5731
|
-
// `setTimeout` for now.
|
|
5732
|
-
setTimeout(() => cb && cb());
|
|
5733
|
-
});
|
|
5734
|
-
_defineProperty(this, "setCursor", cursor => {
|
|
5735
|
-
this.setState({
|
|
5736
|
-
cursor
|
|
5737
|
-
});
|
|
5738
|
-
});
|
|
5739
|
-
_defineProperty(this, "setKeyHandler", keyHandler => {
|
|
5740
|
-
this.setState({
|
|
5741
|
-
keyHandler
|
|
5742
|
-
});
|
|
5743
|
-
});
|
|
5744
|
-
_defineProperty(this, "getDOMNode", () => {
|
|
5745
|
-
return ReactDOM__default["default"].findDOMNode(this);
|
|
5746
|
-
});
|
|
5747
|
-
}
|
|
5708
|
+
_containerRef = /*#__PURE__*/React__namespace.createRef();
|
|
5709
|
+
_containerResizeObserver = null;
|
|
5710
|
+
_throttleResize = false;
|
|
5711
|
+
state = {
|
|
5712
|
+
containerWidth: 0
|
|
5713
|
+
};
|
|
5748
5714
|
componentDidMount() {
|
|
5749
|
-
var _this$props$onElement, _this$props2;
|
|
5750
5715
|
this._resize();
|
|
5751
5716
|
window.addEventListener("resize", this._throttleResizeHandler);
|
|
5752
5717
|
window.addEventListener("orientationchange", this._throttleResizeHandler);
|
|
@@ -5759,7 +5724,7 @@ class MobileKeypad extends React__namespace.Component {
|
|
|
5759
5724
|
this._containerResizeObserver.observe(this._containerRef.current);
|
|
5760
5725
|
}
|
|
5761
5726
|
}
|
|
5762
|
-
|
|
5727
|
+
this.props.onElementMounted?.({
|
|
5763
5728
|
activate: this.activate,
|
|
5764
5729
|
dismiss: this.dismiss,
|
|
5765
5730
|
configure: this.configure,
|
|
@@ -5769,18 +5734,68 @@ class MobileKeypad extends React__namespace.Component {
|
|
|
5769
5734
|
});
|
|
5770
5735
|
}
|
|
5771
5736
|
componentWillUnmount() {
|
|
5772
|
-
var _this$_containerResiz;
|
|
5773
5737
|
window.removeEventListener("resize", this._throttleResizeHandler);
|
|
5774
5738
|
window.removeEventListener("orientationchange", this._throttleResizeHandler);
|
|
5775
|
-
|
|
5739
|
+
this._containerResizeObserver?.disconnect();
|
|
5776
5740
|
}
|
|
5741
|
+
_resize = () => {
|
|
5742
|
+
const containerWidth = this._containerRef.current?.clientWidth || 0;
|
|
5743
|
+
this.setState({
|
|
5744
|
+
containerWidth
|
|
5745
|
+
});
|
|
5746
|
+
};
|
|
5747
|
+
_throttleResizeHandler = () => {
|
|
5748
|
+
if (this._throttleResize) {
|
|
5749
|
+
return;
|
|
5750
|
+
}
|
|
5751
|
+
this._throttleResize = true;
|
|
5752
|
+
setTimeout(() => {
|
|
5753
|
+
this._resize();
|
|
5754
|
+
this._throttleResize = false;
|
|
5755
|
+
}, 100);
|
|
5756
|
+
};
|
|
5757
|
+
activate = () => {
|
|
5758
|
+
this.props.setKeypadActive(true);
|
|
5759
|
+
};
|
|
5760
|
+
dismiss = () => {
|
|
5761
|
+
this.props.setKeypadActive(false);
|
|
5762
|
+
this.props.onDismiss?.();
|
|
5763
|
+
};
|
|
5764
|
+
configure = (configuration, cb) => {
|
|
5765
|
+
this.setState({
|
|
5766
|
+
keypadConfig: configuration
|
|
5767
|
+
});
|
|
5768
|
+
|
|
5769
|
+
// TODO(matthewc)[LC-1080]: this was brought in from v1's ProvidedKeypad.
|
|
5770
|
+
// We need to investigate whether we still need this.
|
|
5771
|
+
// HACK(charlie): In Perseus, triggering a focus causes the keypad to
|
|
5772
|
+
// animate into view and re-configure. We'd like to provide the option
|
|
5773
|
+
// to re-render the re-configured keypad before animating it into view,
|
|
5774
|
+
// to avoid jank in the animation. As such, we support passing a
|
|
5775
|
+
// callback into `configureKeypad`. However, implementing this properly
|
|
5776
|
+
// would require middleware, etc., so we just hack it on with
|
|
5777
|
+
// `setTimeout` for now.
|
|
5778
|
+
setTimeout(() => cb && cb());
|
|
5779
|
+
};
|
|
5780
|
+
setCursor = cursor => {
|
|
5781
|
+
this.setState({
|
|
5782
|
+
cursor
|
|
5783
|
+
});
|
|
5784
|
+
};
|
|
5785
|
+
setKeyHandler = keyHandler => {
|
|
5786
|
+
this.setState({
|
|
5787
|
+
keyHandler
|
|
5788
|
+
});
|
|
5789
|
+
};
|
|
5790
|
+
getDOMNode = () => {
|
|
5791
|
+
return ReactDOM__default["default"].findDOMNode(this);
|
|
5792
|
+
};
|
|
5777
5793
|
_handleClickKey(key) {
|
|
5778
|
-
var _this$state$keyHandle, _this$state;
|
|
5779
5794
|
if (key === "DISMISS") {
|
|
5780
5795
|
this.dismiss();
|
|
5781
5796
|
return;
|
|
5782
5797
|
}
|
|
5783
|
-
const cursor =
|
|
5798
|
+
const cursor = this.state.keyHandler?.(key);
|
|
5784
5799
|
this.setState({
|
|
5785
5800
|
cursor
|
|
5786
5801
|
});
|
|
@@ -5798,35 +5813,35 @@ class MobileKeypad extends React__namespace.Component {
|
|
|
5798
5813
|
const containerStyle = [styles$c.keypadContainer,
|
|
5799
5814
|
// styles passed as props
|
|
5800
5815
|
...(Array.isArray(style) ? style : [style])];
|
|
5801
|
-
const isExpression =
|
|
5802
|
-
const convertDotToTimes = keypadConfig
|
|
5803
|
-
return /*#__PURE__*/React__namespace.createElement(
|
|
5816
|
+
const isExpression = keypadConfig?.keypadType === "EXPRESSION";
|
|
5817
|
+
const convertDotToTimes = keypadConfig?.times;
|
|
5818
|
+
return /*#__PURE__*/React__namespace.createElement(View, {
|
|
5819
|
+
style: containerStyle,
|
|
5820
|
+
forwardRef: this._containerRef
|
|
5821
|
+
}, /*#__PURE__*/React__namespace.createElement(AphroditeCSSTransitionGroup, {
|
|
5804
5822
|
transitionEnterTimeout: AnimationDurationInMS,
|
|
5805
5823
|
transitionLeaveTimeout: AnimationDurationInMS,
|
|
5806
5824
|
transitionStyle: {
|
|
5807
5825
|
enter: {
|
|
5808
5826
|
transform: "translate3d(0, 100%, 0)",
|
|
5809
|
-
transition:
|
|
5827
|
+
transition: `${AnimationDurationInMS}ms ease-out`
|
|
5810
5828
|
},
|
|
5811
5829
|
enterActive: {
|
|
5812
5830
|
transform: "translate3d(0, 0, 0)"
|
|
5813
5831
|
},
|
|
5814
5832
|
leave: {
|
|
5815
5833
|
transform: "translate3d(0, 0, 0)",
|
|
5816
|
-
transition:
|
|
5834
|
+
transition: `${AnimationDurationInMS}ms ease-out`
|
|
5817
5835
|
},
|
|
5818
5836
|
leaveActive: {
|
|
5819
5837
|
transform: "translate3d(0, 100%, 0)"
|
|
5820
5838
|
}
|
|
5821
5839
|
}
|
|
5822
|
-
}, keypadActive ? /*#__PURE__*/React__namespace.createElement(
|
|
5823
|
-
style: containerStyle,
|
|
5824
|
-
forwardRef: this._containerRef
|
|
5825
|
-
}, /*#__PURE__*/React__namespace.createElement(Keypad$2, {
|
|
5840
|
+
}, keypadActive ? /*#__PURE__*/React__namespace.createElement(Keypad$2, {
|
|
5826
5841
|
onAnalyticsEvent: this.props.onAnalyticsEvent,
|
|
5827
|
-
extraKeys: keypadConfig
|
|
5842
|
+
extraKeys: keypadConfig?.extraKeys,
|
|
5828
5843
|
onClickKey: key => this._handleClickKey(key),
|
|
5829
|
-
cursorContext: cursor
|
|
5844
|
+
cursorContext: cursor?.context,
|
|
5830
5845
|
fractionsOnly: !isExpression,
|
|
5831
5846
|
convertDotToTimes: convertDotToTimes,
|
|
5832
5847
|
divisionKey: isExpression,
|
|
@@ -5837,7 +5852,7 @@ class MobileKeypad extends React__namespace.Component {
|
|
|
5837
5852
|
advancedRelations: isExpression,
|
|
5838
5853
|
expandedView: containerWidth > expandedViewThreshold$1,
|
|
5839
5854
|
showDismiss: true
|
|
5840
|
-
})
|
|
5855
|
+
}) : null));
|
|
5841
5856
|
}
|
|
5842
5857
|
}
|
|
5843
5858
|
const styles$c = aphrodite.StyleSheet.create({
|
|
@@ -5960,20 +5975,14 @@ var Styles = aphrodite.StyleSheet.create({
|
|
|
5960
5975
|
}
|
|
5961
5976
|
});
|
|
5962
5977
|
|
|
5978
|
+
/**
|
|
5979
|
+
* A component that renders an icon with math (via KaTeX).
|
|
5980
|
+
*/
|
|
5963
5981
|
const {
|
|
5964
5982
|
row: row$7,
|
|
5965
5983
|
centered: centered$4
|
|
5966
5984
|
} = Styles;
|
|
5967
5985
|
class MathIcon extends React__namespace.Component {
|
|
5968
|
-
constructor() {
|
|
5969
|
-
super(...arguments);
|
|
5970
|
-
_defineProperty(this, "_renderMath", () => {
|
|
5971
|
-
const {
|
|
5972
|
-
math
|
|
5973
|
-
} = this.props;
|
|
5974
|
-
katex__default["default"].render(math, ReactDOM__default["default"].findDOMNode(this));
|
|
5975
|
-
});
|
|
5976
|
-
}
|
|
5977
5986
|
componentDidMount() {
|
|
5978
5987
|
this._renderMath();
|
|
5979
5988
|
}
|
|
@@ -5982,6 +5991,12 @@ class MathIcon extends React__namespace.Component {
|
|
|
5982
5991
|
this._renderMath();
|
|
5983
5992
|
}
|
|
5984
5993
|
}
|
|
5994
|
+
_renderMath = () => {
|
|
5995
|
+
const {
|
|
5996
|
+
math
|
|
5997
|
+
} = this.props;
|
|
5998
|
+
katex__default["default"].render(math, ReactDOM__default["default"].findDOMNode(this));
|
|
5999
|
+
};
|
|
5985
6000
|
render() {
|
|
5986
6001
|
const {
|
|
5987
6002
|
style
|
|
@@ -6002,7 +6017,15 @@ const styles$a = aphrodite.StyleSheet.create({
|
|
|
6002
6017
|
}
|
|
6003
6018
|
});
|
|
6004
6019
|
|
|
6020
|
+
/**
|
|
6021
|
+
* An autogenerated component that renders the COS iconograpy in SVG.
|
|
6022
|
+
*
|
|
6023
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6024
|
+
*/
|
|
6005
6025
|
class Cos extends React__namespace.Component {
|
|
6026
|
+
static propTypes = {
|
|
6027
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6028
|
+
};
|
|
6006
6029
|
render() {
|
|
6007
6030
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6008
6031
|
width: "48",
|
|
@@ -6023,11 +6046,16 @@ class Cos extends React__namespace.Component {
|
|
|
6023
6046
|
})));
|
|
6024
6047
|
}
|
|
6025
6048
|
}
|
|
6026
|
-
_defineProperty(Cos, "propTypes", {
|
|
6027
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6028
|
-
});
|
|
6029
6049
|
|
|
6050
|
+
/**
|
|
6051
|
+
* An autogenerated component that renders the LOG iconograpy in SVG.
|
|
6052
|
+
*
|
|
6053
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6054
|
+
*/
|
|
6030
6055
|
class Log extends React__namespace.Component {
|
|
6056
|
+
static propTypes = {
|
|
6057
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6058
|
+
};
|
|
6031
6059
|
render() {
|
|
6032
6060
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6033
6061
|
width: "48",
|
|
@@ -6045,11 +6073,16 @@ class Log extends React__namespace.Component {
|
|
|
6045
6073
|
})));
|
|
6046
6074
|
}
|
|
6047
6075
|
}
|
|
6048
|
-
_defineProperty(Log, "propTypes", {
|
|
6049
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6050
|
-
});
|
|
6051
6076
|
|
|
6077
|
+
/**
|
|
6078
|
+
* An autogenerated component that renders the EQUAL iconograpy in SVG.
|
|
6079
|
+
*
|
|
6080
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6081
|
+
*/
|
|
6052
6082
|
class Equal extends React__namespace.Component {
|
|
6083
|
+
static propTypes = {
|
|
6084
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6085
|
+
};
|
|
6053
6086
|
render() {
|
|
6054
6087
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6055
6088
|
width: "48",
|
|
@@ -6073,9 +6106,6 @@ class Equal extends React__namespace.Component {
|
|
|
6073
6106
|
})));
|
|
6074
6107
|
}
|
|
6075
6108
|
}
|
|
6076
|
-
_defineProperty(Equal, "propTypes", {
|
|
6077
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6078
|
-
});
|
|
6079
6109
|
|
|
6080
6110
|
/**
|
|
6081
6111
|
* An autogenerated component that renders the BACKSPACE iconograpy in SVG.
|
|
@@ -6105,7 +6135,15 @@ const Backspace = () => {
|
|
|
6105
6135
|
})));
|
|
6106
6136
|
};
|
|
6107
6137
|
|
|
6138
|
+
/**
|
|
6139
|
+
* An autogenerated component that renders the SQRT iconograpy in SVG.
|
|
6140
|
+
*
|
|
6141
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6142
|
+
*/
|
|
6108
6143
|
class Sqrt extends React__namespace.Component {
|
|
6144
|
+
static propTypes = {
|
|
6145
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6146
|
+
};
|
|
6109
6147
|
render() {
|
|
6110
6148
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6111
6149
|
width: "48",
|
|
@@ -6126,11 +6164,16 @@ class Sqrt extends React__namespace.Component {
|
|
|
6126
6164
|
})));
|
|
6127
6165
|
}
|
|
6128
6166
|
}
|
|
6129
|
-
_defineProperty(Sqrt, "propTypes", {
|
|
6130
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6131
|
-
});
|
|
6132
6167
|
|
|
6168
|
+
/**
|
|
6169
|
+
* An autogenerated component that renders the EXP iconograpy in SVG.
|
|
6170
|
+
*
|
|
6171
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6172
|
+
*/
|
|
6133
6173
|
class Exp extends React__namespace.Component {
|
|
6174
|
+
static propTypes = {
|
|
6175
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6176
|
+
};
|
|
6134
6177
|
render() {
|
|
6135
6178
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6136
6179
|
width: "48",
|
|
@@ -6148,11 +6191,16 @@ class Exp extends React__namespace.Component {
|
|
|
6148
6191
|
})));
|
|
6149
6192
|
}
|
|
6150
6193
|
}
|
|
6151
|
-
_defineProperty(Exp, "propTypes", {
|
|
6152
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6153
|
-
});
|
|
6154
6194
|
|
|
6195
|
+
/**
|
|
6196
|
+
* An autogenerated component that renders the NEQ iconograpy in SVG.
|
|
6197
|
+
*
|
|
6198
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6199
|
+
*/
|
|
6155
6200
|
class Neq extends React__namespace.Component {
|
|
6201
|
+
static propTypes = {
|
|
6202
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6203
|
+
};
|
|
6156
6204
|
render() {
|
|
6157
6205
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6158
6206
|
width: "48",
|
|
@@ -6176,11 +6224,16 @@ class Neq extends React__namespace.Component {
|
|
|
6176
6224
|
})));
|
|
6177
6225
|
}
|
|
6178
6226
|
}
|
|
6179
|
-
_defineProperty(Neq, "propTypes", {
|
|
6180
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6181
|
-
});
|
|
6182
6227
|
|
|
6228
|
+
/**
|
|
6229
|
+
* An autogenerated component that renders the GEQ iconograpy in SVG.
|
|
6230
|
+
*
|
|
6231
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6232
|
+
*/
|
|
6183
6233
|
class Geq extends React__namespace.Component {
|
|
6234
|
+
static propTypes = {
|
|
6235
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6236
|
+
};
|
|
6184
6237
|
render() {
|
|
6185
6238
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6186
6239
|
width: "48",
|
|
@@ -6204,11 +6257,16 @@ class Geq extends React__namespace.Component {
|
|
|
6204
6257
|
})));
|
|
6205
6258
|
}
|
|
6206
6259
|
}
|
|
6207
|
-
_defineProperty(Geq, "propTypes", {
|
|
6208
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6209
|
-
});
|
|
6210
6260
|
|
|
6261
|
+
/**
|
|
6262
|
+
* An autogenerated component that renders the LN iconograpy in SVG.
|
|
6263
|
+
*
|
|
6264
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6265
|
+
*/
|
|
6211
6266
|
class Ln extends React__namespace.Component {
|
|
6267
|
+
static propTypes = {
|
|
6268
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6269
|
+
};
|
|
6212
6270
|
render() {
|
|
6213
6271
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6214
6272
|
width: "48",
|
|
@@ -6226,9 +6284,6 @@ class Ln extends React__namespace.Component {
|
|
|
6226
6284
|
})));
|
|
6227
6285
|
}
|
|
6228
6286
|
}
|
|
6229
|
-
_defineProperty(Ln, "propTypes", {
|
|
6230
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6231
|
-
});
|
|
6232
6287
|
|
|
6233
6288
|
/**
|
|
6234
6289
|
* An autogenerated component that renders the DISMISS iconograpy in SVG.
|
|
@@ -6255,7 +6310,15 @@ const Dismiss = () => {
|
|
|
6255
6310
|
})));
|
|
6256
6311
|
};
|
|
6257
6312
|
|
|
6313
|
+
/**
|
|
6314
|
+
* An autogenerated component that renders the SIN iconograpy in SVG.
|
|
6315
|
+
*
|
|
6316
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6317
|
+
*/
|
|
6258
6318
|
class Sin extends React__namespace.Component {
|
|
6319
|
+
static propTypes = {
|
|
6320
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6321
|
+
};
|
|
6259
6322
|
render() {
|
|
6260
6323
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6261
6324
|
width: "48",
|
|
@@ -6276,11 +6339,16 @@ class Sin extends React__namespace.Component {
|
|
|
6276
6339
|
})));
|
|
6277
6340
|
}
|
|
6278
6341
|
}
|
|
6279
|
-
_defineProperty(Sin, "propTypes", {
|
|
6280
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6281
|
-
});
|
|
6282
6342
|
|
|
6343
|
+
/**
|
|
6344
|
+
* An autogenerated component that renders the LT iconograpy in SVG.
|
|
6345
|
+
*
|
|
6346
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6347
|
+
*/
|
|
6283
6348
|
class Lt extends React__namespace.Component {
|
|
6349
|
+
static propTypes = {
|
|
6350
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6351
|
+
};
|
|
6284
6352
|
render() {
|
|
6285
6353
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6286
6354
|
width: "48",
|
|
@@ -6304,11 +6372,16 @@ class Lt extends React__namespace.Component {
|
|
|
6304
6372
|
})));
|
|
6305
6373
|
}
|
|
6306
6374
|
}
|
|
6307
|
-
_defineProperty(Lt, "propTypes", {
|
|
6308
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6309
|
-
});
|
|
6310
6375
|
|
|
6376
|
+
/**
|
|
6377
|
+
* An autogenerated component that renders the CUBE_ROOT iconograpy in SVG.
|
|
6378
|
+
*
|
|
6379
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6380
|
+
*/
|
|
6311
6381
|
class CubeRoot extends React__namespace.Component {
|
|
6382
|
+
static propTypes = {
|
|
6383
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6384
|
+
};
|
|
6312
6385
|
render() {
|
|
6313
6386
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6314
6387
|
width: "48",
|
|
@@ -6332,11 +6405,16 @@ class CubeRoot extends React__namespace.Component {
|
|
|
6332
6405
|
})));
|
|
6333
6406
|
}
|
|
6334
6407
|
}
|
|
6335
|
-
_defineProperty(CubeRoot, "propTypes", {
|
|
6336
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6337
|
-
});
|
|
6338
6408
|
|
|
6409
|
+
/**
|
|
6410
|
+
* An autogenerated component that renders the PLUS iconograpy in SVG.
|
|
6411
|
+
*
|
|
6412
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6413
|
+
*/
|
|
6339
6414
|
class Plus extends React__namespace.Component {
|
|
6415
|
+
static propTypes = {
|
|
6416
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6417
|
+
};
|
|
6340
6418
|
render() {
|
|
6341
6419
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6342
6420
|
width: "48",
|
|
@@ -6357,11 +6435,16 @@ class Plus extends React__namespace.Component {
|
|
|
6357
6435
|
})));
|
|
6358
6436
|
}
|
|
6359
6437
|
}
|
|
6360
|
-
_defineProperty(Plus, "propTypes", {
|
|
6361
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6362
|
-
});
|
|
6363
6438
|
|
|
6439
|
+
/**
|
|
6440
|
+
* An autogenerated component that renders the TAN iconograpy in SVG.
|
|
6441
|
+
*
|
|
6442
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6443
|
+
*/
|
|
6364
6444
|
class Tan extends React__namespace.Component {
|
|
6445
|
+
static propTypes = {
|
|
6446
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6447
|
+
};
|
|
6365
6448
|
render() {
|
|
6366
6449
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6367
6450
|
width: "48",
|
|
@@ -6382,9 +6465,6 @@ class Tan extends React__namespace.Component {
|
|
|
6382
6465
|
})));
|
|
6383
6466
|
}
|
|
6384
6467
|
}
|
|
6385
|
-
_defineProperty(Tan, "propTypes", {
|
|
6386
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6387
|
-
});
|
|
6388
6468
|
|
|
6389
6469
|
const Arrow = props => {
|
|
6390
6470
|
return /*#__PURE__*/React__namespace.createElement("g", _extends({
|
|
@@ -6442,7 +6522,15 @@ const Down = () => {
|
|
|
6442
6522
|
}));
|
|
6443
6523
|
};
|
|
6444
6524
|
|
|
6525
|
+
/**
|
|
6526
|
+
* An autogenerated component that renders the LEFT_PAREN iconograpy in SVG.
|
|
6527
|
+
*
|
|
6528
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6529
|
+
*/
|
|
6445
6530
|
class LeftParen extends React__namespace.Component {
|
|
6531
|
+
static propTypes = {
|
|
6532
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6533
|
+
};
|
|
6446
6534
|
render() {
|
|
6447
6535
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6448
6536
|
width: "48",
|
|
@@ -6466,11 +6554,16 @@ class LeftParen extends React__namespace.Component {
|
|
|
6466
6554
|
})));
|
|
6467
6555
|
}
|
|
6468
6556
|
}
|
|
6469
|
-
_defineProperty(LeftParen, "propTypes", {
|
|
6470
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6471
|
-
});
|
|
6472
6557
|
|
|
6558
|
+
/**
|
|
6559
|
+
* An autogenerated component that renders the RIGHT_PAREN iconograpy in SVG.
|
|
6560
|
+
*
|
|
6561
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6562
|
+
*/
|
|
6473
6563
|
class RightParen extends React__namespace.Component {
|
|
6564
|
+
static propTypes = {
|
|
6565
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6566
|
+
};
|
|
6474
6567
|
render() {
|
|
6475
6568
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6476
6569
|
width: "48",
|
|
@@ -6494,11 +6587,16 @@ class RightParen extends React__namespace.Component {
|
|
|
6494
6587
|
})));
|
|
6495
6588
|
}
|
|
6496
6589
|
}
|
|
6497
|
-
_defineProperty(RightParen, "propTypes", {
|
|
6498
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6499
|
-
});
|
|
6500
6590
|
|
|
6591
|
+
/**
|
|
6592
|
+
* An autogenerated component that renders the GT iconograpy in SVG.
|
|
6593
|
+
*
|
|
6594
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6595
|
+
*/
|
|
6501
6596
|
class Gt extends React__namespace.Component {
|
|
6597
|
+
static propTypes = {
|
|
6598
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6599
|
+
};
|
|
6502
6600
|
render() {
|
|
6503
6601
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6504
6602
|
width: "48",
|
|
@@ -6522,11 +6620,16 @@ class Gt extends React__namespace.Component {
|
|
|
6522
6620
|
})));
|
|
6523
6621
|
}
|
|
6524
6622
|
}
|
|
6525
|
-
_defineProperty(Gt, "propTypes", {
|
|
6526
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6527
|
-
});
|
|
6528
6623
|
|
|
6624
|
+
/**
|
|
6625
|
+
* An autogenerated component that renders the DIVIDE iconograpy in SVG.
|
|
6626
|
+
*
|
|
6627
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6628
|
+
*/
|
|
6529
6629
|
class Divide extends React__namespace.Component {
|
|
6630
|
+
static propTypes = {
|
|
6631
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6632
|
+
};
|
|
6530
6633
|
render() {
|
|
6531
6634
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6532
6635
|
width: "48",
|
|
@@ -6557,11 +6660,16 @@ class Divide extends React__namespace.Component {
|
|
|
6557
6660
|
})));
|
|
6558
6661
|
}
|
|
6559
6662
|
}
|
|
6560
|
-
_defineProperty(Divide, "propTypes", {
|
|
6561
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6562
|
-
});
|
|
6563
6663
|
|
|
6664
|
+
/**
|
|
6665
|
+
* An autogenerated component that renders the PERIOD iconograpy in SVG.
|
|
6666
|
+
*
|
|
6667
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6668
|
+
*/
|
|
6564
6669
|
class Period extends React__namespace.Component {
|
|
6670
|
+
static propTypes = {
|
|
6671
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6672
|
+
};
|
|
6565
6673
|
render() {
|
|
6566
6674
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6567
6675
|
width: "48",
|
|
@@ -6581,11 +6689,16 @@ class Period extends React__namespace.Component {
|
|
|
6581
6689
|
})));
|
|
6582
6690
|
}
|
|
6583
6691
|
}
|
|
6584
|
-
_defineProperty(Period, "propTypes", {
|
|
6585
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6586
|
-
});
|
|
6587
6692
|
|
|
6693
|
+
/**
|
|
6694
|
+
* An autogenerated component that renders the PERCENT iconograpy in SVG.
|
|
6695
|
+
*
|
|
6696
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6697
|
+
*/
|
|
6588
6698
|
class Percent extends React__namespace.Component {
|
|
6699
|
+
static propTypes = {
|
|
6700
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6701
|
+
};
|
|
6589
6702
|
render() {
|
|
6590
6703
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6591
6704
|
width: "48",
|
|
@@ -6623,11 +6736,16 @@ class Percent extends React__namespace.Component {
|
|
|
6623
6736
|
}))));
|
|
6624
6737
|
}
|
|
6625
6738
|
}
|
|
6626
|
-
_defineProperty(Percent, "propTypes", {
|
|
6627
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6628
|
-
});
|
|
6629
6739
|
|
|
6740
|
+
/**
|
|
6741
|
+
* An autogenerated component that renders the TIMES iconograpy in SVG.
|
|
6742
|
+
*
|
|
6743
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6744
|
+
*/
|
|
6630
6745
|
class Times extends React__namespace.Component {
|
|
6746
|
+
static propTypes = {
|
|
6747
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6748
|
+
};
|
|
6631
6749
|
render() {
|
|
6632
6750
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6633
6751
|
width: "48",
|
|
@@ -6651,11 +6769,16 @@ class Times extends React__namespace.Component {
|
|
|
6651
6769
|
})));
|
|
6652
6770
|
}
|
|
6653
6771
|
}
|
|
6654
|
-
_defineProperty(Times, "propTypes", {
|
|
6655
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6656
|
-
});
|
|
6657
6772
|
|
|
6773
|
+
/**
|
|
6774
|
+
* An autogenerated component that renders the EXP_3 iconograpy in SVG.
|
|
6775
|
+
*
|
|
6776
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6777
|
+
*/
|
|
6658
6778
|
class Exp3 extends React__namespace.Component {
|
|
6779
|
+
static propTypes = {
|
|
6780
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6781
|
+
};
|
|
6659
6782
|
render() {
|
|
6660
6783
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6661
6784
|
width: "48",
|
|
@@ -6673,11 +6796,16 @@ class Exp3 extends React__namespace.Component {
|
|
|
6673
6796
|
})));
|
|
6674
6797
|
}
|
|
6675
6798
|
}
|
|
6676
|
-
_defineProperty(Exp3, "propTypes", {
|
|
6677
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6678
|
-
});
|
|
6679
6799
|
|
|
6800
|
+
/**
|
|
6801
|
+
* An autogenerated component that renders the EXP_2 iconograpy in SVG.
|
|
6802
|
+
*
|
|
6803
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6804
|
+
*/
|
|
6680
6805
|
class Exp2 extends React__namespace.Component {
|
|
6806
|
+
static propTypes = {
|
|
6807
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6808
|
+
};
|
|
6681
6809
|
render() {
|
|
6682
6810
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6683
6811
|
width: "48",
|
|
@@ -6695,9 +6823,6 @@ class Exp2 extends React__namespace.Component {
|
|
|
6695
6823
|
})));
|
|
6696
6824
|
}
|
|
6697
6825
|
}
|
|
6698
|
-
_defineProperty(Exp2, "propTypes", {
|
|
6699
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6700
|
-
});
|
|
6701
6826
|
|
|
6702
6827
|
/**
|
|
6703
6828
|
* A component that renders the RIGHT iconograpy in SVG.
|
|
@@ -6712,7 +6837,15 @@ const Right = () => {
|
|
|
6712
6837
|
}));
|
|
6713
6838
|
};
|
|
6714
6839
|
|
|
6840
|
+
/**
|
|
6841
|
+
* An autogenerated component that renders the CDOT iconograpy in SVG.
|
|
6842
|
+
*
|
|
6843
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6844
|
+
*/
|
|
6715
6845
|
class Cdot extends React__namespace.Component {
|
|
6846
|
+
static propTypes = {
|
|
6847
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6848
|
+
};
|
|
6716
6849
|
render() {
|
|
6717
6850
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6718
6851
|
width: "48",
|
|
@@ -6737,11 +6870,16 @@ class Cdot extends React__namespace.Component {
|
|
|
6737
6870
|
}))));
|
|
6738
6871
|
}
|
|
6739
6872
|
}
|
|
6740
|
-
_defineProperty(Cdot, "propTypes", {
|
|
6741
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6742
|
-
});
|
|
6743
6873
|
|
|
6874
|
+
/**
|
|
6875
|
+
* An autogenerated component that renders the LOG_N iconograpy in SVG.
|
|
6876
|
+
*
|
|
6877
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6878
|
+
*/
|
|
6744
6879
|
class LogN extends React__namespace.Component {
|
|
6880
|
+
static propTypes = {
|
|
6881
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6882
|
+
};
|
|
6745
6883
|
render() {
|
|
6746
6884
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6747
6885
|
width: "48",
|
|
@@ -6759,11 +6897,16 @@ class LogN extends React__namespace.Component {
|
|
|
6759
6897
|
})));
|
|
6760
6898
|
}
|
|
6761
6899
|
}
|
|
6762
|
-
_defineProperty(LogN, "propTypes", {
|
|
6763
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6764
|
-
});
|
|
6765
6900
|
|
|
6901
|
+
/**
|
|
6902
|
+
* An autogenerated component that renders the LEQ iconograpy in SVG.
|
|
6903
|
+
*
|
|
6904
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6905
|
+
*/
|
|
6766
6906
|
class Leq extends React__namespace.Component {
|
|
6907
|
+
static propTypes = {
|
|
6908
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6909
|
+
};
|
|
6767
6910
|
render() {
|
|
6768
6911
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6769
6912
|
width: "48",
|
|
@@ -6787,11 +6930,16 @@ class Leq extends React__namespace.Component {
|
|
|
6787
6930
|
})));
|
|
6788
6931
|
}
|
|
6789
6932
|
}
|
|
6790
|
-
_defineProperty(Leq, "propTypes", {
|
|
6791
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6792
|
-
});
|
|
6793
6933
|
|
|
6934
|
+
/**
|
|
6935
|
+
* An autogenerated component that renders the MINUS iconograpy in SVG.
|
|
6936
|
+
*
|
|
6937
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6938
|
+
*/
|
|
6794
6939
|
class Minus extends React__namespace.Component {
|
|
6940
|
+
static propTypes = {
|
|
6941
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6942
|
+
};
|
|
6795
6943
|
render() {
|
|
6796
6944
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6797
6945
|
width: "48",
|
|
@@ -6812,11 +6960,16 @@ class Minus extends React__namespace.Component {
|
|
|
6812
6960
|
})));
|
|
6813
6961
|
}
|
|
6814
6962
|
}
|
|
6815
|
-
_defineProperty(Minus, "propTypes", {
|
|
6816
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6817
|
-
});
|
|
6818
6963
|
|
|
6964
|
+
/**
|
|
6965
|
+
* An autogenerated component that renders the RADICAL iconograpy in SVG.
|
|
6966
|
+
*
|
|
6967
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6968
|
+
*/
|
|
6819
6969
|
class Radical extends React__namespace.Component {
|
|
6970
|
+
static propTypes = {
|
|
6971
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6972
|
+
};
|
|
6820
6973
|
render() {
|
|
6821
6974
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6822
6975
|
width: "48",
|
|
@@ -6840,11 +6993,16 @@ class Radical extends React__namespace.Component {
|
|
|
6840
6993
|
})));
|
|
6841
6994
|
}
|
|
6842
6995
|
}
|
|
6843
|
-
_defineProperty(Radical, "propTypes", {
|
|
6844
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6845
|
-
});
|
|
6846
6996
|
|
|
6997
|
+
/**
|
|
6998
|
+
* An autogenerated component that renders the FRAC iconograpy in SVG.
|
|
6999
|
+
*
|
|
7000
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
7001
|
+
*/
|
|
6847
7002
|
class FracInclusive extends React__namespace.Component {
|
|
7003
|
+
static propTypes = {
|
|
7004
|
+
color: PropTypes__default["default"].string.isRequired
|
|
7005
|
+
};
|
|
6848
7006
|
render() {
|
|
6849
7007
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6850
7008
|
width: "48",
|
|
@@ -6877,9 +7035,6 @@ class FracInclusive extends React__namespace.Component {
|
|
|
6877
7035
|
}))));
|
|
6878
7036
|
}
|
|
6879
7037
|
}
|
|
6880
|
-
_defineProperty(FracInclusive, "propTypes", {
|
|
6881
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6882
|
-
});
|
|
6883
7038
|
|
|
6884
7039
|
/**
|
|
6885
7040
|
* An autogenerated component that renders the JUMP_OUT_PARENTHESES iconograpy in SVG.
|
|
@@ -7263,7 +7418,7 @@ class MultiSymbolGrid extends React__namespace.Component {
|
|
|
7263
7418
|
// some styles coercion and doesn't seem worthwhile right now.
|
|
7264
7419
|
icons.forEach(icon => {
|
|
7265
7420
|
if (icon.type !== IconType.MATH) {
|
|
7266
|
-
throw new Error(
|
|
7421
|
+
throw new Error(`Received invalid icon: type=${icon.type}, ` + `data=${icon.data}`);
|
|
7267
7422
|
}
|
|
7268
7423
|
});
|
|
7269
7424
|
if (icons.length === 1) {
|
|
@@ -7324,7 +7479,7 @@ class MultiSymbolGrid extends React__namespace.Component {
|
|
|
7324
7479
|
}))));
|
|
7325
7480
|
}
|
|
7326
7481
|
}
|
|
7327
|
-
throw new Error(
|
|
7482
|
+
throw new Error(`Invalid number of icons: ${icons.length}`);
|
|
7328
7483
|
}
|
|
7329
7484
|
}
|
|
7330
7485
|
const verticalInsetPx = 2;
|
|
@@ -7368,72 +7523,13 @@ const styles$7 = aphrodite.StyleSheet.create({
|
|
|
7368
7523
|
|
|
7369
7524
|
// eslint-disable-next-line react/no-unsafe
|
|
7370
7525
|
class KeypadButton extends React__namespace.PureComponent {
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
// the "initial" styles that `View` will include, as these styles are
|
|
7379
|
-
// applied to `View` components and Aphrodite will consolidate the style
|
|
7380
|
-
// object. This method must be called whenever a property that
|
|
7381
|
-
// influences the possible outcomes of `this._getFocusStyle` and
|
|
7382
|
-
// `this._getButtonStyle` changes (such as `this.buttonSizeStyle`).
|
|
7383
|
-
for (const type of KeyTypes) {
|
|
7384
|
-
aphrodite.css(View.styles.initial, ...this._getFocusStyle(type));
|
|
7385
|
-
for (const borders of Object.values(BorderStyles)) {
|
|
7386
|
-
aphrodite.css(View.styles.initial, ...this._getButtonStyle(type, borders));
|
|
7387
|
-
}
|
|
7388
|
-
}
|
|
7389
|
-
});
|
|
7390
|
-
_defineProperty(this, "_getFocusStyle", type => {
|
|
7391
|
-
let focusBackgroundStyle;
|
|
7392
|
-
if (type === "INPUT_NAVIGATION" || type === "KEYPAD_NAVIGATION") {
|
|
7393
|
-
focusBackgroundStyle = styles$6.light;
|
|
7394
|
-
} else {
|
|
7395
|
-
focusBackgroundStyle = styles$6.bright;
|
|
7396
|
-
}
|
|
7397
|
-
return [styles$6.focusBox, focusBackgroundStyle];
|
|
7398
|
-
});
|
|
7399
|
-
_defineProperty(this, "_getButtonStyle", (type, borders, style) => {
|
|
7400
|
-
// Select the appropriate style for the button.
|
|
7401
|
-
let backgroundStyle;
|
|
7402
|
-
switch (type) {
|
|
7403
|
-
case "EMPTY":
|
|
7404
|
-
backgroundStyle = styles$6.empty;
|
|
7405
|
-
break;
|
|
7406
|
-
case "MANY":
|
|
7407
|
-
case "VALUE":
|
|
7408
|
-
backgroundStyle = styles$6.value;
|
|
7409
|
-
break;
|
|
7410
|
-
case "OPERATOR":
|
|
7411
|
-
backgroundStyle = styles$6.operator;
|
|
7412
|
-
break;
|
|
7413
|
-
case "INPUT_NAVIGATION":
|
|
7414
|
-
case "KEYPAD_NAVIGATION":
|
|
7415
|
-
backgroundStyle = styles$6.control;
|
|
7416
|
-
break;
|
|
7417
|
-
case "ECHO":
|
|
7418
|
-
backgroundStyle = null;
|
|
7419
|
-
break;
|
|
7420
|
-
}
|
|
7421
|
-
const borderStyle = [];
|
|
7422
|
-
if (borders.includes(BorderDirection.LEFT)) {
|
|
7423
|
-
// @ts-expect-error TS2345
|
|
7424
|
-
borderStyle.push(styles$6.leftBorder);
|
|
7425
|
-
}
|
|
7426
|
-
if (borders.includes(BorderDirection.BOTTOM)) {
|
|
7427
|
-
// @ts-expect-error TS2345
|
|
7428
|
-
borderStyle.push(styles$6.bottomBorder);
|
|
7429
|
-
}
|
|
7430
|
-
return [styles$6.buttonBase, backgroundStyle, ...borderStyle, type === "ECHO" && styles$6.echo, this.buttonSizeStyle,
|
|
7431
|
-
// React Native allows you to set the 'style' props on user defined
|
|
7432
|
-
// components.
|
|
7433
|
-
// See: https://facebook.github.io/react-native/docs/style.html
|
|
7434
|
-
...(Array.isArray(style) ? style : [style])];
|
|
7435
|
-
});
|
|
7436
|
-
}
|
|
7526
|
+
static defaultProps = {
|
|
7527
|
+
borders: BorderStyles.ALL,
|
|
7528
|
+
childKeys: [],
|
|
7529
|
+
disabled: false,
|
|
7530
|
+
focused: false,
|
|
7531
|
+
popoverEnabled: false
|
|
7532
|
+
};
|
|
7437
7533
|
UNSAFE_componentWillMount() {
|
|
7438
7534
|
this.buttonSizeStyle = styleForButtonDimensions(this.props.heightPx, this.props.widthPx);
|
|
7439
7535
|
}
|
|
@@ -7450,6 +7546,68 @@ class KeypadButton extends React__namespace.PureComponent {
|
|
|
7450
7546
|
this._preInjectStyles();
|
|
7451
7547
|
}
|
|
7452
7548
|
}
|
|
7549
|
+
_preInjectStyles = () => {
|
|
7550
|
+
// HACK(charlie): Pre-inject all of the possible styles for the button.
|
|
7551
|
+
// This avoids a flickering effect in the echo animation whereby the
|
|
7552
|
+
// echoes vary in size as they animate. Note that we need to account for
|
|
7553
|
+
// the "initial" styles that `View` will include, as these styles are
|
|
7554
|
+
// applied to `View` components and Aphrodite will consolidate the style
|
|
7555
|
+
// object. This method must be called whenever a property that
|
|
7556
|
+
// influences the possible outcomes of `this._getFocusStyle` and
|
|
7557
|
+
// `this._getButtonStyle` changes (such as `this.buttonSizeStyle`).
|
|
7558
|
+
for (const type of KeyTypes) {
|
|
7559
|
+
aphrodite.css(View.styles.initial, ...this._getFocusStyle(type));
|
|
7560
|
+
for (const borders of Object.values(BorderStyles)) {
|
|
7561
|
+
aphrodite.css(View.styles.initial, ...this._getButtonStyle(type, borders));
|
|
7562
|
+
}
|
|
7563
|
+
}
|
|
7564
|
+
};
|
|
7565
|
+
_getFocusStyle = type => {
|
|
7566
|
+
let focusBackgroundStyle;
|
|
7567
|
+
if (type === "INPUT_NAVIGATION" || type === "KEYPAD_NAVIGATION") {
|
|
7568
|
+
focusBackgroundStyle = styles$6.light;
|
|
7569
|
+
} else {
|
|
7570
|
+
focusBackgroundStyle = styles$6.bright;
|
|
7571
|
+
}
|
|
7572
|
+
return [styles$6.focusBox, focusBackgroundStyle];
|
|
7573
|
+
};
|
|
7574
|
+
_getButtonStyle = (type, borders, style) => {
|
|
7575
|
+
// Select the appropriate style for the button.
|
|
7576
|
+
let backgroundStyle;
|
|
7577
|
+
switch (type) {
|
|
7578
|
+
case "EMPTY":
|
|
7579
|
+
backgroundStyle = styles$6.empty;
|
|
7580
|
+
break;
|
|
7581
|
+
case "MANY":
|
|
7582
|
+
case "VALUE":
|
|
7583
|
+
backgroundStyle = styles$6.value;
|
|
7584
|
+
break;
|
|
7585
|
+
case "OPERATOR":
|
|
7586
|
+
backgroundStyle = styles$6.operator;
|
|
7587
|
+
break;
|
|
7588
|
+
case "INPUT_NAVIGATION":
|
|
7589
|
+
case "KEYPAD_NAVIGATION":
|
|
7590
|
+
backgroundStyle = styles$6.control;
|
|
7591
|
+
break;
|
|
7592
|
+
case "ECHO":
|
|
7593
|
+
backgroundStyle = null;
|
|
7594
|
+
break;
|
|
7595
|
+
}
|
|
7596
|
+
const borderStyle = [];
|
|
7597
|
+
if (borders.includes(BorderDirection.LEFT)) {
|
|
7598
|
+
// @ts-expect-error TS2345
|
|
7599
|
+
borderStyle.push(styles$6.leftBorder);
|
|
7600
|
+
}
|
|
7601
|
+
if (borders.includes(BorderDirection.BOTTOM)) {
|
|
7602
|
+
// @ts-expect-error TS2345
|
|
7603
|
+
borderStyle.push(styles$6.bottomBorder);
|
|
7604
|
+
}
|
|
7605
|
+
return [styles$6.buttonBase, backgroundStyle, ...borderStyle, type === "ECHO" && styles$6.echo, this.buttonSizeStyle,
|
|
7606
|
+
// React Native allows you to set the 'style' props on user defined
|
|
7607
|
+
// components.
|
|
7608
|
+
// See: https://facebook.github.io/react-native/docs/style.html
|
|
7609
|
+
...(Array.isArray(style) ? style : [style])];
|
|
7610
|
+
};
|
|
7453
7611
|
render() {
|
|
7454
7612
|
const {
|
|
7455
7613
|
ariaLabel,
|
|
@@ -7523,13 +7681,6 @@ class KeypadButton extends React__namespace.PureComponent {
|
|
|
7523
7681
|
}
|
|
7524
7682
|
}
|
|
7525
7683
|
}
|
|
7526
|
-
_defineProperty(KeypadButton, "defaultProps", {
|
|
7527
|
-
borders: BorderStyles.ALL,
|
|
7528
|
-
childKeys: [],
|
|
7529
|
-
disabled: false,
|
|
7530
|
-
focused: false,
|
|
7531
|
-
popoverEnabled: false
|
|
7532
|
-
});
|
|
7533
7684
|
const focusInsetPx = 4;
|
|
7534
7685
|
const focusBoxZIndex = 0;
|
|
7535
7686
|
const styles$6 = aphrodite.StyleSheet.create({
|
|
@@ -7707,7 +7858,6 @@ const extractProps = keyConfig => {
|
|
|
7707
7858
|
};
|
|
7708
7859
|
};
|
|
7709
7860
|
const mapStateToProps$5 = (state, ownProps) => {
|
|
7710
|
-
var _gestures$popover;
|
|
7711
7861
|
const {
|
|
7712
7862
|
gestures
|
|
7713
7863
|
} = state;
|
|
@@ -7732,7 +7882,7 @@ const mapStateToProps$5 = (state, ownProps) => {
|
|
|
7732
7882
|
id: id,
|
|
7733
7883
|
// Add in some gesture state.
|
|
7734
7884
|
focused: gestures.focus === id,
|
|
7735
|
-
popoverEnabled:
|
|
7885
|
+
popoverEnabled: gestures.popover?.parentId === id,
|
|
7736
7886
|
// Pass down the child keys and any extracted props.
|
|
7737
7887
|
childKeys,
|
|
7738
7888
|
...extractProps(useFirstChildProps ? childKeys[0] : keyConfig)
|
|
@@ -7750,6 +7900,9 @@ var TouchableKeypadButton$1 = reactRedux.connect(mapStateToProps$5, null, null,
|
|
|
7750
7900
|
})(TouchableKeypadButton);
|
|
7751
7901
|
|
|
7752
7902
|
class ManyKeypadButton extends React__namespace.Component {
|
|
7903
|
+
static defaultProps = {
|
|
7904
|
+
keys: []
|
|
7905
|
+
};
|
|
7753
7906
|
render() {
|
|
7754
7907
|
const {
|
|
7755
7908
|
keys,
|
|
@@ -7783,9 +7936,6 @@ class ManyKeypadButton extends React__namespace.Component {
|
|
|
7783
7936
|
}
|
|
7784
7937
|
}
|
|
7785
7938
|
}
|
|
7786
|
-
_defineProperty(ManyKeypadButton, "defaultProps", {
|
|
7787
|
-
keys: []
|
|
7788
|
-
});
|
|
7789
7939
|
|
|
7790
7940
|
/**
|
|
7791
7941
|
* This file contains all of the z-index values used throughout the math-input
|
|
@@ -7838,35 +7988,32 @@ class Echo extends React__namespace.Component {
|
|
|
7838
7988
|
}
|
|
7839
7989
|
}
|
|
7840
7990
|
class EchoManager extends React__namespace.Component {
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
};
|
|
7868
|
-
});
|
|
7869
|
-
}
|
|
7991
|
+
_animationConfigForType = animationType => {
|
|
7992
|
+
// NOTE(charlie): These must be kept in sync with the transition
|
|
7993
|
+
// durations and classnames specified in echo.css.
|
|
7994
|
+
let animationDurationMs;
|
|
7995
|
+
let animationTransitionName;
|
|
7996
|
+
switch (animationType) {
|
|
7997
|
+
case EchoAnimationType.SLIDE_AND_FADE:
|
|
7998
|
+
animationDurationMs = 400;
|
|
7999
|
+
animationTransitionName = "echo-slide-and-fade";
|
|
8000
|
+
break;
|
|
8001
|
+
case EchoAnimationType.FADE_ONLY:
|
|
8002
|
+
animationDurationMs = 300;
|
|
8003
|
+
animationTransitionName = "echo-fade-only";
|
|
8004
|
+
break;
|
|
8005
|
+
case EchoAnimationType.LONG_FADE_ONLY:
|
|
8006
|
+
animationDurationMs = 400;
|
|
8007
|
+
animationTransitionName = "echo-long-fade-only";
|
|
8008
|
+
break;
|
|
8009
|
+
default:
|
|
8010
|
+
throw new Error(`Invalid echo animation type: ${animationType}`);
|
|
8011
|
+
}
|
|
8012
|
+
return {
|
|
8013
|
+
animationDurationMs,
|
|
8014
|
+
animationTransitionName
|
|
8015
|
+
};
|
|
8016
|
+
};
|
|
7870
8017
|
render() {
|
|
7871
8018
|
const {
|
|
7872
8019
|
echoes,
|
|
@@ -7904,7 +8051,7 @@ class EchoManager extends React__namespace.Component {
|
|
|
7904
8051
|
key: animationId
|
|
7905
8052
|
}, /*#__PURE__*/React__namespace.createElement(Echo, _extends({
|
|
7906
8053
|
animationDurationMs: animationDurationMs,
|
|
7907
|
-
onAnimationFinish: () => onAnimationFinish
|
|
8054
|
+
onAnimationFinish: () => onAnimationFinish?.(animationId)
|
|
7908
8055
|
}, echo)));
|
|
7909
8056
|
}));
|
|
7910
8057
|
}));
|
|
@@ -7999,7 +8146,6 @@ class PopoverManager extends React__namespace.Component {
|
|
|
7999
8146
|
// naming convention: verb + noun
|
|
8000
8147
|
// the noun should be one of the other properties in the object that's
|
|
8001
8148
|
// being dispatched
|
|
8002
|
-
|
|
8003
8149
|
const dismissKeypad = () => {
|
|
8004
8150
|
return {
|
|
8005
8151
|
type: "DismissKeypad"
|
|
@@ -8069,43 +8215,12 @@ const pressKey = (key, initialBounds, inPopover) => {
|
|
|
8069
8215
|
};
|
|
8070
8216
|
};
|
|
8071
8217
|
|
|
8218
|
+
/**
|
|
8219
|
+
* A keypad component that acts as a container for rows or columns of buttons,
|
|
8220
|
+
* and manages the rendering of echo animations on top of those buttons.
|
|
8221
|
+
*/
|
|
8072
8222
|
// eslint-disable-next-line react/no-unsafe
|
|
8073
8223
|
class Keypad extends React__namespace.Component {
|
|
8074
|
-
constructor() {
|
|
8075
|
-
super(...arguments);
|
|
8076
|
-
_defineProperty(this, "_isMounted", void 0);
|
|
8077
|
-
_defineProperty(this, "_resizeTimeout", void 0);
|
|
8078
|
-
_defineProperty(this, "_container", void 0);
|
|
8079
|
-
_defineProperty(this, "_computeContainer", () => {
|
|
8080
|
-
const domNode = ReactDOM__default["default"].findDOMNode(this);
|
|
8081
|
-
this._container = domNode.getBoundingClientRect();
|
|
8082
|
-
});
|
|
8083
|
-
_defineProperty(this, "_updateSizeAndPosition", () => {
|
|
8084
|
-
// Mark the container for recalculation next time the keypad is
|
|
8085
|
-
// opened.
|
|
8086
|
-
// TODO(charlie): Since we're not recalculating the container
|
|
8087
|
-
// immediately, if you were to resize the page while a popover were
|
|
8088
|
-
// active, you'd likely get unexpected behavior. This seems very
|
|
8089
|
-
// difficult to do and, as such, incredibly unlikely, but we may
|
|
8090
|
-
// want to reconsider the caching here.
|
|
8091
|
-
this._container = null;
|
|
8092
|
-
});
|
|
8093
|
-
_defineProperty(this, "_onResize", () => {
|
|
8094
|
-
// Whenever the page resizes, we need to recompute the container's
|
|
8095
|
-
// bounding box. This is the only time that the bounding box can change.
|
|
8096
|
-
|
|
8097
|
-
// Throttle resize events -- taken from:
|
|
8098
|
-
// https://developer.mozilla.org/en-US/docs/Web/Events/resize
|
|
8099
|
-
if (this._resizeTimeout == null) {
|
|
8100
|
-
this._resizeTimeout = window.setTimeout(() => {
|
|
8101
|
-
this._resizeTimeout = null;
|
|
8102
|
-
if (this._isMounted) {
|
|
8103
|
-
this._updateSizeAndPosition();
|
|
8104
|
-
}
|
|
8105
|
-
}, 66);
|
|
8106
|
-
}
|
|
8107
|
-
});
|
|
8108
|
-
}
|
|
8109
8224
|
componentDidMount() {
|
|
8110
8225
|
this._isMounted = true;
|
|
8111
8226
|
window.addEventListener("resize", this._onResize);
|
|
@@ -8120,6 +8235,35 @@ class Keypad extends React__namespace.Component {
|
|
|
8120
8235
|
this._isMounted = false;
|
|
8121
8236
|
window.removeEventListener("resize", this._onResize);
|
|
8122
8237
|
}
|
|
8238
|
+
_computeContainer = () => {
|
|
8239
|
+
const domNode = ReactDOM__default["default"].findDOMNode(this);
|
|
8240
|
+
this._container = domNode.getBoundingClientRect();
|
|
8241
|
+
};
|
|
8242
|
+
_updateSizeAndPosition = () => {
|
|
8243
|
+
// Mark the container for recalculation next time the keypad is
|
|
8244
|
+
// opened.
|
|
8245
|
+
// TODO(charlie): Since we're not recalculating the container
|
|
8246
|
+
// immediately, if you were to resize the page while a popover were
|
|
8247
|
+
// active, you'd likely get unexpected behavior. This seems very
|
|
8248
|
+
// difficult to do and, as such, incredibly unlikely, but we may
|
|
8249
|
+
// want to reconsider the caching here.
|
|
8250
|
+
this._container = null;
|
|
8251
|
+
};
|
|
8252
|
+
_onResize = () => {
|
|
8253
|
+
// Whenever the page resizes, we need to recompute the container's
|
|
8254
|
+
// bounding box. This is the only time that the bounding box can change.
|
|
8255
|
+
|
|
8256
|
+
// Throttle resize events -- taken from:
|
|
8257
|
+
// https://developer.mozilla.org/en-US/docs/Web/Events/resize
|
|
8258
|
+
if (this._resizeTimeout == null) {
|
|
8259
|
+
this._resizeTimeout = window.setTimeout(() => {
|
|
8260
|
+
this._resizeTimeout = null;
|
|
8261
|
+
if (this._isMounted) {
|
|
8262
|
+
this._updateSizeAndPosition();
|
|
8263
|
+
}
|
|
8264
|
+
}, 66);
|
|
8265
|
+
}
|
|
8266
|
+
};
|
|
8123
8267
|
render() {
|
|
8124
8268
|
const {
|
|
8125
8269
|
children,
|
|
@@ -8197,18 +8341,18 @@ var Keypad$1 = reactRedux.connect(mapStateToProps$4, mapDispatchToProps$1, null,
|
|
|
8197
8341
|
forwardRef: true
|
|
8198
8342
|
})(Keypad);
|
|
8199
8343
|
|
|
8344
|
+
/**
|
|
8345
|
+
* A keypad with two pages of keys.
|
|
8346
|
+
*/
|
|
8200
8347
|
const {
|
|
8201
8348
|
column: column$2,
|
|
8202
8349
|
row: row$4,
|
|
8203
8350
|
fullWidth: fullWidth$2
|
|
8204
8351
|
} = Styles;
|
|
8205
8352
|
class TwoPageKeypad extends React__namespace.Component {
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
selectedPage: "Numbers"
|
|
8210
|
-
});
|
|
8211
|
-
}
|
|
8353
|
+
state = {
|
|
8354
|
+
selectedPage: "Numbers"
|
|
8355
|
+
};
|
|
8212
8356
|
render() {
|
|
8213
8357
|
const {
|
|
8214
8358
|
leftPage,
|
|
@@ -8253,16 +8397,16 @@ const styles$3 = aphrodite.StyleSheet.create({
|
|
|
8253
8397
|
backgroundColor: offBlack16
|
|
8254
8398
|
},
|
|
8255
8399
|
borderTop: {
|
|
8256
|
-
borderTop:
|
|
8400
|
+
borderTop: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`
|
|
8257
8401
|
},
|
|
8258
8402
|
borderLeft: {
|
|
8259
|
-
borderLeft:
|
|
8403
|
+
borderLeft: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`,
|
|
8260
8404
|
boxSizing: "content-box"
|
|
8261
8405
|
},
|
|
8262
8406
|
tabbar: {
|
|
8263
8407
|
background: Color__default["default"].offWhite,
|
|
8264
|
-
borderTop:
|
|
8265
|
-
borderBottom:
|
|
8408
|
+
borderTop: `1px solid ${Color__default["default"].offBlack50}`,
|
|
8409
|
+
borderBottom: `1px solid ${Color__default["default"].offBlack50}`
|
|
8266
8410
|
}
|
|
8267
8411
|
});
|
|
8268
8412
|
const mapStateToProps$3 = state => {
|
|
@@ -8498,9 +8642,8 @@ const styles$2 = aphrodite.StyleSheet.create({
|
|
|
8498
8642
|
}
|
|
8499
8643
|
});
|
|
8500
8644
|
const mapStateToProps$2 = state => {
|
|
8501
|
-
var _state$input$cursor;
|
|
8502
8645
|
return {
|
|
8503
|
-
cursorContext:
|
|
8646
|
+
cursorContext: state.input.cursor?.context,
|
|
8504
8647
|
dynamicJumpOut: !state.layout.navigationPadEnabled
|
|
8505
8648
|
};
|
|
8506
8649
|
};
|
|
@@ -8631,9 +8774,8 @@ class FractionKeypad extends React__namespace.Component {
|
|
|
8631
8774
|
}
|
|
8632
8775
|
}
|
|
8633
8776
|
const mapStateToProps$1 = state => {
|
|
8634
|
-
var _state$input$cursor;
|
|
8635
8777
|
return {
|
|
8636
|
-
cursorContext:
|
|
8778
|
+
cursorContext: state.input.cursor?.context,
|
|
8637
8779
|
dynamicJumpOut: !state.layout.navigationPadEnabled
|
|
8638
8780
|
};
|
|
8639
8781
|
};
|
|
@@ -8750,71 +8892,12 @@ const {
|
|
|
8750
8892
|
} = Styles;
|
|
8751
8893
|
// eslint-disable-next-line react/no-unsafe
|
|
8752
8894
|
class KeypadContainer extends React__namespace.Component {
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
_defineProperty(this, "state", {
|
|
8760
|
-
hasBeenActivated: false,
|
|
8761
|
-
viewportWidth: "100vw"
|
|
8762
|
-
});
|
|
8763
|
-
_defineProperty(this, "_throttleResizeHandler", () => {
|
|
8764
|
-
// Throttle the resize callbacks.
|
|
8765
|
-
// https://developer.mozilla.org/en-US/docs/Web/Events/resize
|
|
8766
|
-
if (this._resizeTimeout == null) {
|
|
8767
|
-
this._resizeTimeout = window.setTimeout(() => {
|
|
8768
|
-
this._resizeTimeout = null;
|
|
8769
|
-
this._onResize();
|
|
8770
|
-
}, 66);
|
|
8771
|
-
}
|
|
8772
|
-
});
|
|
8773
|
-
_defineProperty(this, "_onResize", () => {
|
|
8774
|
-
var _this$_containerRef$c, _this$_containerRef$c2, _this$props$onPageSiz, _this$props;
|
|
8775
|
-
// Whenever the page resizes, we need to force an update, as the button
|
|
8776
|
-
// heights and keypad width are computed based on horizontal space.
|
|
8777
|
-
this.setState({
|
|
8778
|
-
viewportWidth: window.innerWidth
|
|
8779
|
-
});
|
|
8780
|
-
const containerWidth = ((_this$_containerRef$c = this._containerRef.current) === null || _this$_containerRef$c === void 0 ? void 0 : _this$_containerRef$c.clientWidth) || 0;
|
|
8781
|
-
const containerHeight = ((_this$_containerRef$c2 = this._containerRef.current) === null || _this$_containerRef$c2 === void 0 ? void 0 : _this$_containerRef$c2.clientHeight) || 0;
|
|
8782
|
-
(_this$props$onPageSiz = (_this$props = this.props).onPageSizeChange) === null || _this$props$onPageSiz === void 0 ? void 0 : _this$props$onPageSiz.call(_this$props, window.innerWidth, window.innerHeight, containerWidth, containerHeight);
|
|
8783
|
-
});
|
|
8784
|
-
_defineProperty(this, "renderKeypad", () => {
|
|
8785
|
-
const {
|
|
8786
|
-
extraKeys,
|
|
8787
|
-
keypadType,
|
|
8788
|
-
layoutMode,
|
|
8789
|
-
navigationPadEnabled
|
|
8790
|
-
} = this.props;
|
|
8791
|
-
const keypadProps = {
|
|
8792
|
-
extraKeys,
|
|
8793
|
-
// HACK(charlie): In order to properly round the corners of the
|
|
8794
|
-
// compact keypad, we need to instruct some of our child views to
|
|
8795
|
-
// crop themselves. At least we're colocating all the layout
|
|
8796
|
-
// information in this component, though.
|
|
8797
|
-
roundTopLeft: layoutMode === LayoutMode.COMPACT && !navigationPadEnabled,
|
|
8798
|
-
roundTopRight: layoutMode === LayoutMode.COMPACT
|
|
8799
|
-
};
|
|
8800
|
-
|
|
8801
|
-
// Select the appropriate keyboard given the type.
|
|
8802
|
-
// TODO(charlie): In the future, we might want to move towards a
|
|
8803
|
-
// data-driven approach to defining keyboard layouts, and have a
|
|
8804
|
-
// generic keyboard that takes some "keyboard data" and renders it.
|
|
8805
|
-
// However, the keyboards differ pretty heavily right now and it's not
|
|
8806
|
-
// clear what that format would look like exactly. Plus, there aren't
|
|
8807
|
-
// very many of them. So to keep us moving, we'll just hardcode.
|
|
8808
|
-
switch (keypadType) {
|
|
8809
|
-
case KeypadType.FRACTION:
|
|
8810
|
-
return /*#__PURE__*/React__namespace.createElement(FractionKeypad$1, keypadProps);
|
|
8811
|
-
case KeypadType.EXPRESSION:
|
|
8812
|
-
return /*#__PURE__*/React__namespace.createElement(ExpressionKeypad$1, keypadProps);
|
|
8813
|
-
default:
|
|
8814
|
-
throw new Error("Invalid keypad type: " + keypadType);
|
|
8815
|
-
}
|
|
8816
|
-
});
|
|
8817
|
-
}
|
|
8895
|
+
_containerRef = /*#__PURE__*/React__namespace.createRef();
|
|
8896
|
+
_containerResizeObserver = null;
|
|
8897
|
+
state = {
|
|
8898
|
+
hasBeenActivated: false,
|
|
8899
|
+
viewportWidth: "100vw"
|
|
8900
|
+
};
|
|
8818
8901
|
UNSAFE_componentWillMount() {
|
|
8819
8902
|
if (this.props.active) {
|
|
8820
8903
|
this.setState({
|
|
@@ -8852,11 +8935,63 @@ class KeypadContainer extends React__namespace.Component {
|
|
|
8852
8935
|
}
|
|
8853
8936
|
}
|
|
8854
8937
|
componentWillUnmount() {
|
|
8855
|
-
var _this$_containerResiz;
|
|
8856
8938
|
window.removeEventListener("resize", this._throttleResizeHandler);
|
|
8857
8939
|
window.removeEventListener("orientationchange", this._throttleResizeHandler);
|
|
8858
|
-
|
|
8859
|
-
}
|
|
8940
|
+
this._containerResizeObserver?.disconnect();
|
|
8941
|
+
}
|
|
8942
|
+
_throttleResizeHandler = () => {
|
|
8943
|
+
// Throttle the resize callbacks.
|
|
8944
|
+
// https://developer.mozilla.org/en-US/docs/Web/Events/resize
|
|
8945
|
+
if (this._resizeTimeout == null) {
|
|
8946
|
+
this._resizeTimeout = window.setTimeout(() => {
|
|
8947
|
+
this._resizeTimeout = null;
|
|
8948
|
+
this._onResize();
|
|
8949
|
+
}, 66);
|
|
8950
|
+
}
|
|
8951
|
+
};
|
|
8952
|
+
_onResize = () => {
|
|
8953
|
+
// Whenever the page resizes, we need to force an update, as the button
|
|
8954
|
+
// heights and keypad width are computed based on horizontal space.
|
|
8955
|
+
this.setState({
|
|
8956
|
+
viewportWidth: window.innerWidth
|
|
8957
|
+
});
|
|
8958
|
+
const containerWidth = this._containerRef.current?.clientWidth || 0;
|
|
8959
|
+
const containerHeight = this._containerRef.current?.clientHeight || 0;
|
|
8960
|
+
this.props.onPageSizeChange?.(window.innerWidth, window.innerHeight, containerWidth, containerHeight);
|
|
8961
|
+
};
|
|
8962
|
+
renderKeypad = () => {
|
|
8963
|
+
const {
|
|
8964
|
+
extraKeys,
|
|
8965
|
+
keypadType,
|
|
8966
|
+
layoutMode,
|
|
8967
|
+
navigationPadEnabled
|
|
8968
|
+
} = this.props;
|
|
8969
|
+
const keypadProps = {
|
|
8970
|
+
extraKeys,
|
|
8971
|
+
// HACK(charlie): In order to properly round the corners of the
|
|
8972
|
+
// compact keypad, we need to instruct some of our child views to
|
|
8973
|
+
// crop themselves. At least we're colocating all the layout
|
|
8974
|
+
// information in this component, though.
|
|
8975
|
+
roundTopLeft: layoutMode === LayoutMode.COMPACT && !navigationPadEnabled,
|
|
8976
|
+
roundTopRight: layoutMode === LayoutMode.COMPACT
|
|
8977
|
+
};
|
|
8978
|
+
|
|
8979
|
+
// Select the appropriate keyboard given the type.
|
|
8980
|
+
// TODO(charlie): In the future, we might want to move towards a
|
|
8981
|
+
// data-driven approach to defining keyboard layouts, and have a
|
|
8982
|
+
// generic keyboard that takes some "keyboard data" and renders it.
|
|
8983
|
+
// However, the keyboards differ pretty heavily right now and it's not
|
|
8984
|
+
// clear what that format would look like exactly. Plus, there aren't
|
|
8985
|
+
// very many of them. So to keep us moving, we'll just hardcode.
|
|
8986
|
+
switch (keypadType) {
|
|
8987
|
+
case KeypadType.FRACTION:
|
|
8988
|
+
return /*#__PURE__*/React__namespace.createElement(FractionKeypad$1, keypadProps);
|
|
8989
|
+
case KeypadType.EXPRESSION:
|
|
8990
|
+
return /*#__PURE__*/React__namespace.createElement(ExpressionKeypad$1, keypadProps);
|
|
8991
|
+
default:
|
|
8992
|
+
throw new Error("Invalid keypad type: " + keypadType);
|
|
8993
|
+
}
|
|
8994
|
+
};
|
|
8860
8995
|
render() {
|
|
8861
8996
|
const {
|
|
8862
8997
|
active,
|
|
@@ -8916,7 +9051,7 @@ const styles = aphrodite.StyleSheet.create({
|
|
|
8916
9051
|
left: 0,
|
|
8917
9052
|
right: 0,
|
|
8918
9053
|
position: "fixed",
|
|
8919
|
-
transition:
|
|
9054
|
+
transition: `${keypadAnimationDurationMs}ms ease-out`,
|
|
8920
9055
|
transitionProperty: "transform",
|
|
8921
9056
|
zIndex: keypad
|
|
8922
9057
|
},
|
|
@@ -8937,7 +9072,7 @@ const styles = aphrodite.StyleSheet.create({
|
|
|
8937
9072
|
},
|
|
8938
9073
|
navigationPadContainer: {
|
|
8939
9074
|
// Add a separator between the navigation pad and the keypad.
|
|
8940
|
-
borderRight:
|
|
9075
|
+
borderRight: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`,
|
|
8941
9076
|
boxSizing: "content-box"
|
|
8942
9077
|
},
|
|
8943
9078
|
// Defer to the navigation pad, such that the navigation pad is always
|
|
@@ -8992,10 +9127,7 @@ var KeypadContainer$1 = reactRedux.connect(mapStateToProps, mapDispatchToProps,
|
|
|
8992
9127
|
* It is entirely ignorant of the existence of popovers and the positions of
|
|
8993
9128
|
* DOM nodes, operating solely on IDs. The state machine does accommodate for
|
|
8994
9129
|
* multi-touch interactions, tracking gesture state on a per-touch basis.
|
|
8995
|
-
*/
|
|
8996
|
-
|
|
8997
|
-
// exported for tests
|
|
8998
|
-
|
|
9130
|
+
*/ // exported for tests
|
|
8999
9131
|
const defaultOptions = {
|
|
9000
9132
|
longPressWaitTimeMs: 50,
|
|
9001
9133
|
swipeThresholdPx: 20,
|
|
@@ -9003,12 +9135,6 @@ const defaultOptions = {
|
|
|
9003
9135
|
};
|
|
9004
9136
|
class GestureStateMachine {
|
|
9005
9137
|
constructor(handlers, options, swipeDisabledNodeIds, multiPressableKeys) {
|
|
9006
|
-
_defineProperty(this, "handlers", void 0);
|
|
9007
|
-
_defineProperty(this, "options", void 0);
|
|
9008
|
-
_defineProperty(this, "swipeDisabledNodeIds", void 0);
|
|
9009
|
-
_defineProperty(this, "multiPressableKeys", void 0);
|
|
9010
|
-
_defineProperty(this, "touchState", void 0);
|
|
9011
|
-
_defineProperty(this, "swipeState", void 0);
|
|
9012
9138
|
this.handlers = handlers;
|
|
9013
9139
|
this.options = {
|
|
9014
9140
|
...defaultOptions,
|
|
@@ -9180,8 +9306,7 @@ class GestureStateMachine {
|
|
|
9180
9306
|
// Only respect the finger that started a swipe. Any other lingering
|
|
9181
9307
|
// gestures are ignored.
|
|
9182
9308
|
if (this.swipeState.touchId === touchId) {
|
|
9183
|
-
|
|
9184
|
-
(_this$handlers$onSwip = (_this$handlers = this.handlers).onSwipeChange) === null || _this$handlers$onSwip === void 0 ? void 0 : _this$handlers$onSwip.call(_this$handlers, pageX - this.swipeState.startX);
|
|
9309
|
+
this.handlers.onSwipeChange?.(pageX - this.swipeState.startX);
|
|
9185
9310
|
}
|
|
9186
9311
|
} else if (this.touchState[touchId]) {
|
|
9187
9312
|
// It could be touch events started outside the keypad and
|
|
@@ -9194,7 +9319,6 @@ class GestureStateMachine {
|
|
|
9194
9319
|
const dx = pageX - startX;
|
|
9195
9320
|
const shouldBeginSwiping = swipeEnabled && !swipeLocked && Math.abs(dx) > this.options.swipeThresholdPx;
|
|
9196
9321
|
if (shouldBeginSwiping) {
|
|
9197
|
-
var _this$handlers$onSwip2, _this$handlers2;
|
|
9198
9322
|
this._onSwipeStart();
|
|
9199
9323
|
|
|
9200
9324
|
// Trigger the swipe.
|
|
@@ -9202,7 +9326,7 @@ class GestureStateMachine {
|
|
|
9202
9326
|
touchId,
|
|
9203
9327
|
startX
|
|
9204
9328
|
};
|
|
9205
|
-
|
|
9329
|
+
this.handlers.onSwipeChange?.(pageX - this.swipeState.startX);
|
|
9206
9330
|
} else {
|
|
9207
9331
|
const id = getId();
|
|
9208
9332
|
if (id !== activeNodeId) {
|
|
@@ -9225,8 +9349,7 @@ class GestureStateMachine {
|
|
|
9225
9349
|
// Only respect the finger that started a swipe. Any other lingering
|
|
9226
9350
|
// gestures are ignored.
|
|
9227
9351
|
if (this.swipeState.touchId === touchId) {
|
|
9228
|
-
|
|
9229
|
-
(_this$handlers$onSwip3 = (_this$handlers3 = this.handlers).onSwipeEnd) === null || _this$handlers$onSwip3 === void 0 ? void 0 : _this$handlers$onSwip3.call(_this$handlers3, pageX - this.swipeState.startX);
|
|
9352
|
+
this.handlers.onSwipeEnd?.(pageX - this.swipeState.startX);
|
|
9230
9353
|
this.swipeState = null;
|
|
9231
9354
|
}
|
|
9232
9355
|
} else if (this.touchState[touchId]) {
|
|
@@ -9260,8 +9383,7 @@ class GestureStateMachine {
|
|
|
9260
9383
|
// displacement.
|
|
9261
9384
|
if (this.swipeState) {
|
|
9262
9385
|
if (this.swipeState.touchId === touchId) {
|
|
9263
|
-
|
|
9264
|
-
(_this$handlers$onSwip4 = (_this$handlers4 = this.handlers).onSwipeEnd) === null || _this$handlers$onSwip4 === void 0 ? void 0 : _this$handlers$onSwip4.call(_this$handlers4, 0);
|
|
9386
|
+
this.handlers.onSwipeEnd?.(0);
|
|
9265
9387
|
this.swipeState = null;
|
|
9266
9388
|
}
|
|
9267
9389
|
} else if (this.touchState[touchId]) {
|
|
@@ -9284,9 +9406,6 @@ class GestureStateMachine {
|
|
|
9284
9406
|
|
|
9285
9407
|
class NodeManager {
|
|
9286
9408
|
constructor() {
|
|
9287
|
-
_defineProperty(this, "_nodesById", void 0);
|
|
9288
|
-
_defineProperty(this, "_orderedIds", void 0);
|
|
9289
|
-
_defineProperty(this, "_cachedBoundingBoxesById", void 0);
|
|
9290
9409
|
// A mapping from IDs to DOM nodes.
|
|
9291
9410
|
this._nodesById = {};
|
|
9292
9411
|
|
|
@@ -9399,9 +9518,6 @@ class NodeManager {
|
|
|
9399
9518
|
|
|
9400
9519
|
class PopoverStateMachine {
|
|
9401
9520
|
constructor(handlers) {
|
|
9402
|
-
_defineProperty(this, "handlers", void 0);
|
|
9403
|
-
_defineProperty(this, "popovers", void 0);
|
|
9404
|
-
_defineProperty(this, "activePopover", void 0);
|
|
9405
9521
|
this.handlers = handlers;
|
|
9406
9522
|
this.activePopover = null;
|
|
9407
9523
|
this.popovers = {};
|
|
@@ -9561,16 +9677,16 @@ class PopoverStateMachine {
|
|
|
9561
9677
|
}
|
|
9562
9678
|
}
|
|
9563
9679
|
|
|
9680
|
+
/**
|
|
9681
|
+
* A high-level manager for our gesture system. In particular, this class
|
|
9682
|
+
* connects our various bits of logic for managing gestures and interactions,
|
|
9683
|
+
* and links them together.
|
|
9684
|
+
*/
|
|
9564
9685
|
const coordsForEvent = evt => {
|
|
9565
9686
|
return [evt.changedTouches[0].clientX, evt.changedTouches[0].clientY];
|
|
9566
9687
|
};
|
|
9567
9688
|
class GestureManager {
|
|
9568
9689
|
constructor(options, handlers, disabledSwipeKeys, multiPressableKeys) {
|
|
9569
|
-
_defineProperty(this, "swipeEnabled", void 0);
|
|
9570
|
-
_defineProperty(this, "trackEvents", void 0);
|
|
9571
|
-
_defineProperty(this, "nodeManager", void 0);
|
|
9572
|
-
_defineProperty(this, "popoverStateMachine", void 0);
|
|
9573
|
-
_defineProperty(this, "gestureStateMachine", void 0);
|
|
9574
9690
|
const {
|
|
9575
9691
|
swipeEnabled
|
|
9576
9692
|
} = options;
|
|
@@ -9811,13 +9927,12 @@ const inputReducer = function () {
|
|
|
9811
9927
|
case "PressKey":
|
|
9812
9928
|
const keyConfig = KeyConfigs[action.key];
|
|
9813
9929
|
if (keyConfig.type !== "KEYPAD_NAVIGATION") {
|
|
9814
|
-
var _state$keyHandler;
|
|
9815
9930
|
// This is probably an anti-pattern but it works for the
|
|
9816
9931
|
// case where we don't actually control the state but we
|
|
9817
9932
|
// still want to communicate with the other object
|
|
9818
9933
|
return {
|
|
9819
9934
|
...state,
|
|
9820
|
-
cursor:
|
|
9935
|
+
cursor: state.keyHandler?.(keyConfig.id)
|
|
9821
9936
|
};
|
|
9822
9937
|
}
|
|
9823
9938
|
|
|
@@ -10186,66 +10301,6 @@ const createStore = () => {
|
|
|
10186
10301
|
class ProvidedKeypad extends React__namespace.Component {
|
|
10187
10302
|
constructor(props) {
|
|
10188
10303
|
super(props);
|
|
10189
|
-
_defineProperty(this, "store", void 0);
|
|
10190
|
-
_defineProperty(this, "activate", () => {
|
|
10191
|
-
this.props.setKeypadActive(true);
|
|
10192
|
-
});
|
|
10193
|
-
_defineProperty(this, "dismiss", () => {
|
|
10194
|
-
this.props.setKeypadActive(false);
|
|
10195
|
-
});
|
|
10196
|
-
_defineProperty(this, "configure", (configuration, cb) => {
|
|
10197
|
-
this.store.dispatch(configureKeypad(configuration));
|
|
10198
|
-
|
|
10199
|
-
// HACK(charlie): In Perseus, triggering a focus causes the keypad to
|
|
10200
|
-
// animate into view and re-configure. We'd like to provide the option
|
|
10201
|
-
// to re-render the re-configured keypad before animating it into view,
|
|
10202
|
-
// to avoid jank in the animation. As such, we support passing a
|
|
10203
|
-
// callback into `configureKeypad`. However, implementing this properly
|
|
10204
|
-
// would require middleware, etc., so we just hack it on with
|
|
10205
|
-
// `setTimeout` for now.
|
|
10206
|
-
setTimeout(() => cb && cb());
|
|
10207
|
-
});
|
|
10208
|
-
_defineProperty(this, "setCursor", cursor => {
|
|
10209
|
-
this.store.dispatch(setCursor(cursor));
|
|
10210
|
-
});
|
|
10211
|
-
_defineProperty(this, "setKeyHandler", keyHandler => {
|
|
10212
|
-
this.store.dispatch(setKeyHandler(keyHandler));
|
|
10213
|
-
});
|
|
10214
|
-
_defineProperty(this, "getDOMNode", () => {
|
|
10215
|
-
return ReactDOM__default["default"].findDOMNode(this);
|
|
10216
|
-
});
|
|
10217
|
-
_defineProperty(this, "onElementMounted", element => {
|
|
10218
|
-
var _this$props$onElement, _this$props;
|
|
10219
|
-
this.props.onAnalyticsEvent({
|
|
10220
|
-
type: "math-input:keypad-opened",
|
|
10221
|
-
payload: {
|
|
10222
|
-
virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
|
|
10223
|
-
}
|
|
10224
|
-
});
|
|
10225
|
-
|
|
10226
|
-
// Append the dispatch methods that we want to expose
|
|
10227
|
-
// externally to the returned React element.
|
|
10228
|
-
const elementWithDispatchMethods = {
|
|
10229
|
-
...element,
|
|
10230
|
-
activate: this.activate,
|
|
10231
|
-
dismiss: this.dismiss,
|
|
10232
|
-
configure: this.configure,
|
|
10233
|
-
setCursor: this.setCursor,
|
|
10234
|
-
setKeyHandler: this.setKeyHandler,
|
|
10235
|
-
getDOMNode: this.getDOMNode
|
|
10236
|
-
};
|
|
10237
|
-
(_this$props$onElement = (_this$props = this.props).onElementMounted) === null || _this$props$onElement === void 0 ? void 0 : _this$props$onElement.call(_this$props, elementWithDispatchMethods);
|
|
10238
|
-
});
|
|
10239
|
-
_defineProperty(this, "onDismiss", () => {
|
|
10240
|
-
var _this$props$onDismiss, _this$props2;
|
|
10241
|
-
this.props.onAnalyticsEvent({
|
|
10242
|
-
type: "math-input:keypad-closed",
|
|
10243
|
-
payload: {
|
|
10244
|
-
virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
|
|
10245
|
-
}
|
|
10246
|
-
});
|
|
10247
|
-
(_this$props$onDismiss = (_this$props2 = this.props).onDismiss) === null || _this$props$onDismiss === void 0 ? void 0 : _this$props$onDismiss.call(_this$props2);
|
|
10248
|
-
});
|
|
10249
10304
|
this.store = createStore();
|
|
10250
10305
|
}
|
|
10251
10306
|
componentDidUpdate(prevProps) {
|
|
@@ -10256,6 +10311,63 @@ class ProvidedKeypad extends React__namespace.Component {
|
|
|
10256
10311
|
this.store.dispatch(dismissKeypad());
|
|
10257
10312
|
}
|
|
10258
10313
|
}
|
|
10314
|
+
activate = () => {
|
|
10315
|
+
this.props.setKeypadActive(true);
|
|
10316
|
+
};
|
|
10317
|
+
dismiss = () => {
|
|
10318
|
+
this.props.setKeypadActive(false);
|
|
10319
|
+
};
|
|
10320
|
+
configure = (configuration, cb) => {
|
|
10321
|
+
this.store.dispatch(configureKeypad(configuration));
|
|
10322
|
+
|
|
10323
|
+
// HACK(charlie): In Perseus, triggering a focus causes the keypad to
|
|
10324
|
+
// animate into view and re-configure. We'd like to provide the option
|
|
10325
|
+
// to re-render the re-configured keypad before animating it into view,
|
|
10326
|
+
// to avoid jank in the animation. As such, we support passing a
|
|
10327
|
+
// callback into `configureKeypad`. However, implementing this properly
|
|
10328
|
+
// would require middleware, etc., so we just hack it on with
|
|
10329
|
+
// `setTimeout` for now.
|
|
10330
|
+
setTimeout(() => cb && cb());
|
|
10331
|
+
};
|
|
10332
|
+
setCursor = cursor => {
|
|
10333
|
+
this.store.dispatch(setCursor(cursor));
|
|
10334
|
+
};
|
|
10335
|
+
setKeyHandler = keyHandler => {
|
|
10336
|
+
this.store.dispatch(setKeyHandler(keyHandler));
|
|
10337
|
+
};
|
|
10338
|
+
getDOMNode = () => {
|
|
10339
|
+
return ReactDOM__default["default"].findDOMNode(this);
|
|
10340
|
+
};
|
|
10341
|
+
onElementMounted = element => {
|
|
10342
|
+
this.props.onAnalyticsEvent({
|
|
10343
|
+
type: "math-input:keypad-opened",
|
|
10344
|
+
payload: {
|
|
10345
|
+
virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
|
|
10346
|
+
}
|
|
10347
|
+
});
|
|
10348
|
+
|
|
10349
|
+
// Append the dispatch methods that we want to expose
|
|
10350
|
+
// externally to the returned React element.
|
|
10351
|
+
const elementWithDispatchMethods = {
|
|
10352
|
+
...element,
|
|
10353
|
+
activate: this.activate,
|
|
10354
|
+
dismiss: this.dismiss,
|
|
10355
|
+
configure: this.configure,
|
|
10356
|
+
setCursor: this.setCursor,
|
|
10357
|
+
setKeyHandler: this.setKeyHandler,
|
|
10358
|
+
getDOMNode: this.getDOMNode
|
|
10359
|
+
};
|
|
10360
|
+
this.props.onElementMounted?.(elementWithDispatchMethods);
|
|
10361
|
+
};
|
|
10362
|
+
onDismiss = () => {
|
|
10363
|
+
this.props.onAnalyticsEvent({
|
|
10364
|
+
type: "math-input:keypad-closed",
|
|
10365
|
+
payload: {
|
|
10366
|
+
virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
|
|
10367
|
+
}
|
|
10368
|
+
});
|
|
10369
|
+
this.props.onDismiss?.();
|
|
10370
|
+
};
|
|
10259
10371
|
render() {
|
|
10260
10372
|
const {
|
|
10261
10373
|
style
|