@khanacademy/math-input 14.2.1 → 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 +19 -0
- package/dist/es/index.js +91 -35
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +1210 -1097
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/src/components/aphrodite-css-transition-group/transition-child.tsx +2 -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)) {
|
|
@@ -5596,8 +5602,26 @@ class TransitionChild extends React__namespace.Component {
|
|
|
5596
5602
|
}
|
|
5597
5603
|
queueClass(removeClassName, addClassName) {
|
|
5598
5604
|
this.classNameQueue.push([removeClassName, addClassName]);
|
|
5599
|
-
|
|
5600
|
-
|
|
5605
|
+
// Queue operation for after the next paint.
|
|
5606
|
+
this.props.schedule.timeout(this.flushClassNameQueue, 0);
|
|
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
|
+
};
|
|
5601
5625
|
render() {
|
|
5602
5626
|
const {
|
|
5603
5627
|
status
|
|
@@ -5681,71 +5705,13 @@ const AnimationDurationInMS = 200;
|
|
|
5681
5705
|
* can't have methods attached to them).
|
|
5682
5706
|
*/
|
|
5683
5707
|
class MobileKeypad extends React__namespace.Component {
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
containerWidth: 0
|
|
5691
|
-
});
|
|
5692
|
-
_defineProperty(this, "_resize", () => {
|
|
5693
|
-
var _this$_containerRef$c;
|
|
5694
|
-
const containerWidth = ((_this$_containerRef$c = this._containerRef.current) === null || _this$_containerRef$c === void 0 ? void 0 : _this$_containerRef$c.clientWidth) || 0;
|
|
5695
|
-
this.setState({
|
|
5696
|
-
containerWidth
|
|
5697
|
-
});
|
|
5698
|
-
});
|
|
5699
|
-
_defineProperty(this, "_throttleResizeHandler", () => {
|
|
5700
|
-
if (this._throttleResize) {
|
|
5701
|
-
return;
|
|
5702
|
-
}
|
|
5703
|
-
this._throttleResize = true;
|
|
5704
|
-
setTimeout(() => {
|
|
5705
|
-
this._resize();
|
|
5706
|
-
this._throttleResize = false;
|
|
5707
|
-
}, 100);
|
|
5708
|
-
});
|
|
5709
|
-
_defineProperty(this, "activate", () => {
|
|
5710
|
-
this.props.setKeypadActive(true);
|
|
5711
|
-
});
|
|
5712
|
-
_defineProperty(this, "dismiss", () => {
|
|
5713
|
-
var _this$props$onDismiss, _this$props;
|
|
5714
|
-
this.props.setKeypadActive(false);
|
|
5715
|
-
(_this$props$onDismiss = (_this$props = this.props).onDismiss) === null || _this$props$onDismiss === void 0 ? void 0 : _this$props$onDismiss.call(_this$props);
|
|
5716
|
-
});
|
|
5717
|
-
_defineProperty(this, "configure", (configuration, cb) => {
|
|
5718
|
-
this.setState({
|
|
5719
|
-
keypadConfig: configuration
|
|
5720
|
-
});
|
|
5721
|
-
|
|
5722
|
-
// TODO(matthewc)[LC-1080]: this was brought in from v1's ProvidedKeypad.
|
|
5723
|
-
// We need to investigate whether we still need this.
|
|
5724
|
-
// HACK(charlie): In Perseus, triggering a focus causes the keypad to
|
|
5725
|
-
// animate into view and re-configure. We'd like to provide the option
|
|
5726
|
-
// to re-render the re-configured keypad before animating it into view,
|
|
5727
|
-
// to avoid jank in the animation. As such, we support passing a
|
|
5728
|
-
// callback into `configureKeypad`. However, implementing this properly
|
|
5729
|
-
// would require middleware, etc., so we just hack it on with
|
|
5730
|
-
// `setTimeout` for now.
|
|
5731
|
-
setTimeout(() => cb && cb());
|
|
5732
|
-
});
|
|
5733
|
-
_defineProperty(this, "setCursor", cursor => {
|
|
5734
|
-
this.setState({
|
|
5735
|
-
cursor
|
|
5736
|
-
});
|
|
5737
|
-
});
|
|
5738
|
-
_defineProperty(this, "setKeyHandler", keyHandler => {
|
|
5739
|
-
this.setState({
|
|
5740
|
-
keyHandler
|
|
5741
|
-
});
|
|
5742
|
-
});
|
|
5743
|
-
_defineProperty(this, "getDOMNode", () => {
|
|
5744
|
-
return ReactDOM__default["default"].findDOMNode(this);
|
|
5745
|
-
});
|
|
5746
|
-
}
|
|
5708
|
+
_containerRef = /*#__PURE__*/React__namespace.createRef();
|
|
5709
|
+
_containerResizeObserver = null;
|
|
5710
|
+
_throttleResize = false;
|
|
5711
|
+
state = {
|
|
5712
|
+
containerWidth: 0
|
|
5713
|
+
};
|
|
5747
5714
|
componentDidMount() {
|
|
5748
|
-
var _this$props$onElement, _this$props2;
|
|
5749
5715
|
this._resize();
|
|
5750
5716
|
window.addEventListener("resize", this._throttleResizeHandler);
|
|
5751
5717
|
window.addEventListener("orientationchange", this._throttleResizeHandler);
|
|
@@ -5758,7 +5724,7 @@ class MobileKeypad extends React__namespace.Component {
|
|
|
5758
5724
|
this._containerResizeObserver.observe(this._containerRef.current);
|
|
5759
5725
|
}
|
|
5760
5726
|
}
|
|
5761
|
-
|
|
5727
|
+
this.props.onElementMounted?.({
|
|
5762
5728
|
activate: this.activate,
|
|
5763
5729
|
dismiss: this.dismiss,
|
|
5764
5730
|
configure: this.configure,
|
|
@@ -5768,18 +5734,68 @@ class MobileKeypad extends React__namespace.Component {
|
|
|
5768
5734
|
});
|
|
5769
5735
|
}
|
|
5770
5736
|
componentWillUnmount() {
|
|
5771
|
-
var _this$_containerResiz;
|
|
5772
5737
|
window.removeEventListener("resize", this._throttleResizeHandler);
|
|
5773
5738
|
window.removeEventListener("orientationchange", this._throttleResizeHandler);
|
|
5774
|
-
|
|
5739
|
+
this._containerResizeObserver?.disconnect();
|
|
5775
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
|
+
};
|
|
5776
5793
|
_handleClickKey(key) {
|
|
5777
|
-
var _this$state$keyHandle, _this$state;
|
|
5778
5794
|
if (key === "DISMISS") {
|
|
5779
5795
|
this.dismiss();
|
|
5780
5796
|
return;
|
|
5781
5797
|
}
|
|
5782
|
-
const cursor =
|
|
5798
|
+
const cursor = this.state.keyHandler?.(key);
|
|
5783
5799
|
this.setState({
|
|
5784
5800
|
cursor
|
|
5785
5801
|
});
|
|
@@ -5797,35 +5813,35 @@ class MobileKeypad extends React__namespace.Component {
|
|
|
5797
5813
|
const containerStyle = [styles$c.keypadContainer,
|
|
5798
5814
|
// styles passed as props
|
|
5799
5815
|
...(Array.isArray(style) ? style : [style])];
|
|
5800
|
-
const isExpression =
|
|
5801
|
-
const convertDotToTimes = keypadConfig
|
|
5802
|
-
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, {
|
|
5803
5822
|
transitionEnterTimeout: AnimationDurationInMS,
|
|
5804
5823
|
transitionLeaveTimeout: AnimationDurationInMS,
|
|
5805
5824
|
transitionStyle: {
|
|
5806
5825
|
enter: {
|
|
5807
5826
|
transform: "translate3d(0, 100%, 0)",
|
|
5808
|
-
transition:
|
|
5827
|
+
transition: `${AnimationDurationInMS}ms ease-out`
|
|
5809
5828
|
},
|
|
5810
5829
|
enterActive: {
|
|
5811
5830
|
transform: "translate3d(0, 0, 0)"
|
|
5812
5831
|
},
|
|
5813
5832
|
leave: {
|
|
5814
5833
|
transform: "translate3d(0, 0, 0)",
|
|
5815
|
-
transition:
|
|
5834
|
+
transition: `${AnimationDurationInMS}ms ease-out`
|
|
5816
5835
|
},
|
|
5817
5836
|
leaveActive: {
|
|
5818
5837
|
transform: "translate3d(0, 100%, 0)"
|
|
5819
5838
|
}
|
|
5820
5839
|
}
|
|
5821
|
-
}, keypadActive ? /*#__PURE__*/React__namespace.createElement(
|
|
5822
|
-
style: containerStyle,
|
|
5823
|
-
forwardRef: this._containerRef
|
|
5824
|
-
}, /*#__PURE__*/React__namespace.createElement(Keypad$2, {
|
|
5840
|
+
}, keypadActive ? /*#__PURE__*/React__namespace.createElement(Keypad$2, {
|
|
5825
5841
|
onAnalyticsEvent: this.props.onAnalyticsEvent,
|
|
5826
|
-
extraKeys: keypadConfig
|
|
5842
|
+
extraKeys: keypadConfig?.extraKeys,
|
|
5827
5843
|
onClickKey: key => this._handleClickKey(key),
|
|
5828
|
-
cursorContext: cursor
|
|
5844
|
+
cursorContext: cursor?.context,
|
|
5829
5845
|
fractionsOnly: !isExpression,
|
|
5830
5846
|
convertDotToTimes: convertDotToTimes,
|
|
5831
5847
|
divisionKey: isExpression,
|
|
@@ -5836,7 +5852,7 @@ class MobileKeypad extends React__namespace.Component {
|
|
|
5836
5852
|
advancedRelations: isExpression,
|
|
5837
5853
|
expandedView: containerWidth > expandedViewThreshold$1,
|
|
5838
5854
|
showDismiss: true
|
|
5839
|
-
})
|
|
5855
|
+
}) : null));
|
|
5840
5856
|
}
|
|
5841
5857
|
}
|
|
5842
5858
|
const styles$c = aphrodite.StyleSheet.create({
|
|
@@ -5959,20 +5975,14 @@ var Styles = aphrodite.StyleSheet.create({
|
|
|
5959
5975
|
}
|
|
5960
5976
|
});
|
|
5961
5977
|
|
|
5978
|
+
/**
|
|
5979
|
+
* A component that renders an icon with math (via KaTeX).
|
|
5980
|
+
*/
|
|
5962
5981
|
const {
|
|
5963
5982
|
row: row$7,
|
|
5964
5983
|
centered: centered$4
|
|
5965
5984
|
} = Styles;
|
|
5966
5985
|
class MathIcon extends React__namespace.Component {
|
|
5967
|
-
constructor() {
|
|
5968
|
-
super(...arguments);
|
|
5969
|
-
_defineProperty(this, "_renderMath", () => {
|
|
5970
|
-
const {
|
|
5971
|
-
math
|
|
5972
|
-
} = this.props;
|
|
5973
|
-
katex__default["default"].render(math, ReactDOM__default["default"].findDOMNode(this));
|
|
5974
|
-
});
|
|
5975
|
-
}
|
|
5976
5986
|
componentDidMount() {
|
|
5977
5987
|
this._renderMath();
|
|
5978
5988
|
}
|
|
@@ -5981,6 +5991,12 @@ class MathIcon extends React__namespace.Component {
|
|
|
5981
5991
|
this._renderMath();
|
|
5982
5992
|
}
|
|
5983
5993
|
}
|
|
5994
|
+
_renderMath = () => {
|
|
5995
|
+
const {
|
|
5996
|
+
math
|
|
5997
|
+
} = this.props;
|
|
5998
|
+
katex__default["default"].render(math, ReactDOM__default["default"].findDOMNode(this));
|
|
5999
|
+
};
|
|
5984
6000
|
render() {
|
|
5985
6001
|
const {
|
|
5986
6002
|
style
|
|
@@ -6001,7 +6017,15 @@ const styles$a = aphrodite.StyleSheet.create({
|
|
|
6001
6017
|
}
|
|
6002
6018
|
});
|
|
6003
6019
|
|
|
6020
|
+
/**
|
|
6021
|
+
* An autogenerated component that renders the COS iconograpy in SVG.
|
|
6022
|
+
*
|
|
6023
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6024
|
+
*/
|
|
6004
6025
|
class Cos extends React__namespace.Component {
|
|
6026
|
+
static propTypes = {
|
|
6027
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6028
|
+
};
|
|
6005
6029
|
render() {
|
|
6006
6030
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6007
6031
|
width: "48",
|
|
@@ -6022,11 +6046,16 @@ class Cos extends React__namespace.Component {
|
|
|
6022
6046
|
})));
|
|
6023
6047
|
}
|
|
6024
6048
|
}
|
|
6025
|
-
_defineProperty(Cos, "propTypes", {
|
|
6026
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6027
|
-
});
|
|
6028
6049
|
|
|
6050
|
+
/**
|
|
6051
|
+
* An autogenerated component that renders the LOG iconograpy in SVG.
|
|
6052
|
+
*
|
|
6053
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6054
|
+
*/
|
|
6029
6055
|
class Log extends React__namespace.Component {
|
|
6056
|
+
static propTypes = {
|
|
6057
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6058
|
+
};
|
|
6030
6059
|
render() {
|
|
6031
6060
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6032
6061
|
width: "48",
|
|
@@ -6044,11 +6073,16 @@ class Log extends React__namespace.Component {
|
|
|
6044
6073
|
})));
|
|
6045
6074
|
}
|
|
6046
6075
|
}
|
|
6047
|
-
_defineProperty(Log, "propTypes", {
|
|
6048
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6049
|
-
});
|
|
6050
6076
|
|
|
6077
|
+
/**
|
|
6078
|
+
* An autogenerated component that renders the EQUAL iconograpy in SVG.
|
|
6079
|
+
*
|
|
6080
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6081
|
+
*/
|
|
6051
6082
|
class Equal extends React__namespace.Component {
|
|
6083
|
+
static propTypes = {
|
|
6084
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6085
|
+
};
|
|
6052
6086
|
render() {
|
|
6053
6087
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6054
6088
|
width: "48",
|
|
@@ -6072,9 +6106,6 @@ class Equal extends React__namespace.Component {
|
|
|
6072
6106
|
})));
|
|
6073
6107
|
}
|
|
6074
6108
|
}
|
|
6075
|
-
_defineProperty(Equal, "propTypes", {
|
|
6076
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6077
|
-
});
|
|
6078
6109
|
|
|
6079
6110
|
/**
|
|
6080
6111
|
* An autogenerated component that renders the BACKSPACE iconograpy in SVG.
|
|
@@ -6104,7 +6135,15 @@ const Backspace = () => {
|
|
|
6104
6135
|
})));
|
|
6105
6136
|
};
|
|
6106
6137
|
|
|
6138
|
+
/**
|
|
6139
|
+
* An autogenerated component that renders the SQRT iconograpy in SVG.
|
|
6140
|
+
*
|
|
6141
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6142
|
+
*/
|
|
6107
6143
|
class Sqrt extends React__namespace.Component {
|
|
6144
|
+
static propTypes = {
|
|
6145
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6146
|
+
};
|
|
6108
6147
|
render() {
|
|
6109
6148
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6110
6149
|
width: "48",
|
|
@@ -6125,11 +6164,16 @@ class Sqrt extends React__namespace.Component {
|
|
|
6125
6164
|
})));
|
|
6126
6165
|
}
|
|
6127
6166
|
}
|
|
6128
|
-
_defineProperty(Sqrt, "propTypes", {
|
|
6129
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6130
|
-
});
|
|
6131
6167
|
|
|
6168
|
+
/**
|
|
6169
|
+
* An autogenerated component that renders the EXP iconograpy in SVG.
|
|
6170
|
+
*
|
|
6171
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6172
|
+
*/
|
|
6132
6173
|
class Exp extends React__namespace.Component {
|
|
6174
|
+
static propTypes = {
|
|
6175
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6176
|
+
};
|
|
6133
6177
|
render() {
|
|
6134
6178
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6135
6179
|
width: "48",
|
|
@@ -6147,11 +6191,16 @@ class Exp extends React__namespace.Component {
|
|
|
6147
6191
|
})));
|
|
6148
6192
|
}
|
|
6149
6193
|
}
|
|
6150
|
-
_defineProperty(Exp, "propTypes", {
|
|
6151
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6152
|
-
});
|
|
6153
6194
|
|
|
6195
|
+
/**
|
|
6196
|
+
* An autogenerated component that renders the NEQ iconograpy in SVG.
|
|
6197
|
+
*
|
|
6198
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6199
|
+
*/
|
|
6154
6200
|
class Neq extends React__namespace.Component {
|
|
6201
|
+
static propTypes = {
|
|
6202
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6203
|
+
};
|
|
6155
6204
|
render() {
|
|
6156
6205
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6157
6206
|
width: "48",
|
|
@@ -6175,11 +6224,16 @@ class Neq extends React__namespace.Component {
|
|
|
6175
6224
|
})));
|
|
6176
6225
|
}
|
|
6177
6226
|
}
|
|
6178
|
-
_defineProperty(Neq, "propTypes", {
|
|
6179
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6180
|
-
});
|
|
6181
6227
|
|
|
6228
|
+
/**
|
|
6229
|
+
* An autogenerated component that renders the GEQ iconograpy in SVG.
|
|
6230
|
+
*
|
|
6231
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6232
|
+
*/
|
|
6182
6233
|
class Geq extends React__namespace.Component {
|
|
6234
|
+
static propTypes = {
|
|
6235
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6236
|
+
};
|
|
6183
6237
|
render() {
|
|
6184
6238
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6185
6239
|
width: "48",
|
|
@@ -6203,11 +6257,16 @@ class Geq extends React__namespace.Component {
|
|
|
6203
6257
|
})));
|
|
6204
6258
|
}
|
|
6205
6259
|
}
|
|
6206
|
-
_defineProperty(Geq, "propTypes", {
|
|
6207
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6208
|
-
});
|
|
6209
6260
|
|
|
6261
|
+
/**
|
|
6262
|
+
* An autogenerated component that renders the LN iconograpy in SVG.
|
|
6263
|
+
*
|
|
6264
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6265
|
+
*/
|
|
6210
6266
|
class Ln extends React__namespace.Component {
|
|
6267
|
+
static propTypes = {
|
|
6268
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6269
|
+
};
|
|
6211
6270
|
render() {
|
|
6212
6271
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6213
6272
|
width: "48",
|
|
@@ -6225,9 +6284,6 @@ class Ln extends React__namespace.Component {
|
|
|
6225
6284
|
})));
|
|
6226
6285
|
}
|
|
6227
6286
|
}
|
|
6228
|
-
_defineProperty(Ln, "propTypes", {
|
|
6229
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6230
|
-
});
|
|
6231
6287
|
|
|
6232
6288
|
/**
|
|
6233
6289
|
* An autogenerated component that renders the DISMISS iconograpy in SVG.
|
|
@@ -6254,7 +6310,15 @@ const Dismiss = () => {
|
|
|
6254
6310
|
})));
|
|
6255
6311
|
};
|
|
6256
6312
|
|
|
6313
|
+
/**
|
|
6314
|
+
* An autogenerated component that renders the SIN iconograpy in SVG.
|
|
6315
|
+
*
|
|
6316
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6317
|
+
*/
|
|
6257
6318
|
class Sin extends React__namespace.Component {
|
|
6319
|
+
static propTypes = {
|
|
6320
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6321
|
+
};
|
|
6258
6322
|
render() {
|
|
6259
6323
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6260
6324
|
width: "48",
|
|
@@ -6275,11 +6339,16 @@ class Sin extends React__namespace.Component {
|
|
|
6275
6339
|
})));
|
|
6276
6340
|
}
|
|
6277
6341
|
}
|
|
6278
|
-
_defineProperty(Sin, "propTypes", {
|
|
6279
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6280
|
-
});
|
|
6281
6342
|
|
|
6343
|
+
/**
|
|
6344
|
+
* An autogenerated component that renders the LT iconograpy in SVG.
|
|
6345
|
+
*
|
|
6346
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6347
|
+
*/
|
|
6282
6348
|
class Lt extends React__namespace.Component {
|
|
6349
|
+
static propTypes = {
|
|
6350
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6351
|
+
};
|
|
6283
6352
|
render() {
|
|
6284
6353
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6285
6354
|
width: "48",
|
|
@@ -6303,11 +6372,16 @@ class Lt extends React__namespace.Component {
|
|
|
6303
6372
|
})));
|
|
6304
6373
|
}
|
|
6305
6374
|
}
|
|
6306
|
-
_defineProperty(Lt, "propTypes", {
|
|
6307
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6308
|
-
});
|
|
6309
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
|
+
*/
|
|
6310
6381
|
class CubeRoot extends React__namespace.Component {
|
|
6382
|
+
static propTypes = {
|
|
6383
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6384
|
+
};
|
|
6311
6385
|
render() {
|
|
6312
6386
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6313
6387
|
width: "48",
|
|
@@ -6331,11 +6405,16 @@ class CubeRoot extends React__namespace.Component {
|
|
|
6331
6405
|
})));
|
|
6332
6406
|
}
|
|
6333
6407
|
}
|
|
6334
|
-
_defineProperty(CubeRoot, "propTypes", {
|
|
6335
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6336
|
-
});
|
|
6337
6408
|
|
|
6409
|
+
/**
|
|
6410
|
+
* An autogenerated component that renders the PLUS iconograpy in SVG.
|
|
6411
|
+
*
|
|
6412
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6413
|
+
*/
|
|
6338
6414
|
class Plus extends React__namespace.Component {
|
|
6415
|
+
static propTypes = {
|
|
6416
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6417
|
+
};
|
|
6339
6418
|
render() {
|
|
6340
6419
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6341
6420
|
width: "48",
|
|
@@ -6356,11 +6435,16 @@ class Plus extends React__namespace.Component {
|
|
|
6356
6435
|
})));
|
|
6357
6436
|
}
|
|
6358
6437
|
}
|
|
6359
|
-
_defineProperty(Plus, "propTypes", {
|
|
6360
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6361
|
-
});
|
|
6362
6438
|
|
|
6439
|
+
/**
|
|
6440
|
+
* An autogenerated component that renders the TAN iconograpy in SVG.
|
|
6441
|
+
*
|
|
6442
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6443
|
+
*/
|
|
6363
6444
|
class Tan extends React__namespace.Component {
|
|
6445
|
+
static propTypes = {
|
|
6446
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6447
|
+
};
|
|
6364
6448
|
render() {
|
|
6365
6449
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6366
6450
|
width: "48",
|
|
@@ -6381,9 +6465,6 @@ class Tan extends React__namespace.Component {
|
|
|
6381
6465
|
})));
|
|
6382
6466
|
}
|
|
6383
6467
|
}
|
|
6384
|
-
_defineProperty(Tan, "propTypes", {
|
|
6385
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6386
|
-
});
|
|
6387
6468
|
|
|
6388
6469
|
const Arrow = props => {
|
|
6389
6470
|
return /*#__PURE__*/React__namespace.createElement("g", _extends({
|
|
@@ -6441,7 +6522,15 @@ const Down = () => {
|
|
|
6441
6522
|
}));
|
|
6442
6523
|
};
|
|
6443
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
|
+
*/
|
|
6444
6530
|
class LeftParen extends React__namespace.Component {
|
|
6531
|
+
static propTypes = {
|
|
6532
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6533
|
+
};
|
|
6445
6534
|
render() {
|
|
6446
6535
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6447
6536
|
width: "48",
|
|
@@ -6465,11 +6554,16 @@ class LeftParen extends React__namespace.Component {
|
|
|
6465
6554
|
})));
|
|
6466
6555
|
}
|
|
6467
6556
|
}
|
|
6468
|
-
_defineProperty(LeftParen, "propTypes", {
|
|
6469
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6470
|
-
});
|
|
6471
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
|
+
*/
|
|
6472
6563
|
class RightParen extends React__namespace.Component {
|
|
6564
|
+
static propTypes = {
|
|
6565
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6566
|
+
};
|
|
6473
6567
|
render() {
|
|
6474
6568
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6475
6569
|
width: "48",
|
|
@@ -6493,11 +6587,16 @@ class RightParen extends React__namespace.Component {
|
|
|
6493
6587
|
})));
|
|
6494
6588
|
}
|
|
6495
6589
|
}
|
|
6496
|
-
_defineProperty(RightParen, "propTypes", {
|
|
6497
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6498
|
-
});
|
|
6499
6590
|
|
|
6591
|
+
/**
|
|
6592
|
+
* An autogenerated component that renders the GT iconograpy in SVG.
|
|
6593
|
+
*
|
|
6594
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6595
|
+
*/
|
|
6500
6596
|
class Gt extends React__namespace.Component {
|
|
6597
|
+
static propTypes = {
|
|
6598
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6599
|
+
};
|
|
6501
6600
|
render() {
|
|
6502
6601
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6503
6602
|
width: "48",
|
|
@@ -6521,11 +6620,16 @@ class Gt extends React__namespace.Component {
|
|
|
6521
6620
|
})));
|
|
6522
6621
|
}
|
|
6523
6622
|
}
|
|
6524
|
-
_defineProperty(Gt, "propTypes", {
|
|
6525
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6526
|
-
});
|
|
6527
6623
|
|
|
6624
|
+
/**
|
|
6625
|
+
* An autogenerated component that renders the DIVIDE iconograpy in SVG.
|
|
6626
|
+
*
|
|
6627
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6628
|
+
*/
|
|
6528
6629
|
class Divide extends React__namespace.Component {
|
|
6630
|
+
static propTypes = {
|
|
6631
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6632
|
+
};
|
|
6529
6633
|
render() {
|
|
6530
6634
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6531
6635
|
width: "48",
|
|
@@ -6556,11 +6660,16 @@ class Divide extends React__namespace.Component {
|
|
|
6556
6660
|
})));
|
|
6557
6661
|
}
|
|
6558
6662
|
}
|
|
6559
|
-
_defineProperty(Divide, "propTypes", {
|
|
6560
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6561
|
-
});
|
|
6562
6663
|
|
|
6664
|
+
/**
|
|
6665
|
+
* An autogenerated component that renders the PERIOD iconograpy in SVG.
|
|
6666
|
+
*
|
|
6667
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6668
|
+
*/
|
|
6563
6669
|
class Period extends React__namespace.Component {
|
|
6670
|
+
static propTypes = {
|
|
6671
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6672
|
+
};
|
|
6564
6673
|
render() {
|
|
6565
6674
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6566
6675
|
width: "48",
|
|
@@ -6580,11 +6689,16 @@ class Period extends React__namespace.Component {
|
|
|
6580
6689
|
})));
|
|
6581
6690
|
}
|
|
6582
6691
|
}
|
|
6583
|
-
_defineProperty(Period, "propTypes", {
|
|
6584
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6585
|
-
});
|
|
6586
6692
|
|
|
6693
|
+
/**
|
|
6694
|
+
* An autogenerated component that renders the PERCENT iconograpy in SVG.
|
|
6695
|
+
*
|
|
6696
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6697
|
+
*/
|
|
6587
6698
|
class Percent extends React__namespace.Component {
|
|
6699
|
+
static propTypes = {
|
|
6700
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6701
|
+
};
|
|
6588
6702
|
render() {
|
|
6589
6703
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6590
6704
|
width: "48",
|
|
@@ -6622,11 +6736,16 @@ class Percent extends React__namespace.Component {
|
|
|
6622
6736
|
}))));
|
|
6623
6737
|
}
|
|
6624
6738
|
}
|
|
6625
|
-
_defineProperty(Percent, "propTypes", {
|
|
6626
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6627
|
-
});
|
|
6628
6739
|
|
|
6740
|
+
/**
|
|
6741
|
+
* An autogenerated component that renders the TIMES iconograpy in SVG.
|
|
6742
|
+
*
|
|
6743
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6744
|
+
*/
|
|
6629
6745
|
class Times extends React__namespace.Component {
|
|
6746
|
+
static propTypes = {
|
|
6747
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6748
|
+
};
|
|
6630
6749
|
render() {
|
|
6631
6750
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6632
6751
|
width: "48",
|
|
@@ -6650,11 +6769,16 @@ class Times extends React__namespace.Component {
|
|
|
6650
6769
|
})));
|
|
6651
6770
|
}
|
|
6652
6771
|
}
|
|
6653
|
-
_defineProperty(Times, "propTypes", {
|
|
6654
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6655
|
-
});
|
|
6656
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
|
+
*/
|
|
6657
6778
|
class Exp3 extends React__namespace.Component {
|
|
6779
|
+
static propTypes = {
|
|
6780
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6781
|
+
};
|
|
6658
6782
|
render() {
|
|
6659
6783
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6660
6784
|
width: "48",
|
|
@@ -6672,11 +6796,16 @@ class Exp3 extends React__namespace.Component {
|
|
|
6672
6796
|
})));
|
|
6673
6797
|
}
|
|
6674
6798
|
}
|
|
6675
|
-
_defineProperty(Exp3, "propTypes", {
|
|
6676
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6677
|
-
});
|
|
6678
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
|
+
*/
|
|
6679
6805
|
class Exp2 extends React__namespace.Component {
|
|
6806
|
+
static propTypes = {
|
|
6807
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6808
|
+
};
|
|
6680
6809
|
render() {
|
|
6681
6810
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6682
6811
|
width: "48",
|
|
@@ -6694,9 +6823,6 @@ class Exp2 extends React__namespace.Component {
|
|
|
6694
6823
|
})));
|
|
6695
6824
|
}
|
|
6696
6825
|
}
|
|
6697
|
-
_defineProperty(Exp2, "propTypes", {
|
|
6698
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6699
|
-
});
|
|
6700
6826
|
|
|
6701
6827
|
/**
|
|
6702
6828
|
* A component that renders the RIGHT iconograpy in SVG.
|
|
@@ -6711,7 +6837,15 @@ const Right = () => {
|
|
|
6711
6837
|
}));
|
|
6712
6838
|
};
|
|
6713
6839
|
|
|
6840
|
+
/**
|
|
6841
|
+
* An autogenerated component that renders the CDOT iconograpy in SVG.
|
|
6842
|
+
*
|
|
6843
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6844
|
+
*/
|
|
6714
6845
|
class Cdot extends React__namespace.Component {
|
|
6846
|
+
static propTypes = {
|
|
6847
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6848
|
+
};
|
|
6715
6849
|
render() {
|
|
6716
6850
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6717
6851
|
width: "48",
|
|
@@ -6736,11 +6870,16 @@ class Cdot extends React__namespace.Component {
|
|
|
6736
6870
|
}))));
|
|
6737
6871
|
}
|
|
6738
6872
|
}
|
|
6739
|
-
_defineProperty(Cdot, "propTypes", {
|
|
6740
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6741
|
-
});
|
|
6742
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
|
+
*/
|
|
6743
6879
|
class LogN extends React__namespace.Component {
|
|
6880
|
+
static propTypes = {
|
|
6881
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6882
|
+
};
|
|
6744
6883
|
render() {
|
|
6745
6884
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6746
6885
|
width: "48",
|
|
@@ -6758,11 +6897,16 @@ class LogN extends React__namespace.Component {
|
|
|
6758
6897
|
})));
|
|
6759
6898
|
}
|
|
6760
6899
|
}
|
|
6761
|
-
_defineProperty(LogN, "propTypes", {
|
|
6762
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6763
|
-
});
|
|
6764
6900
|
|
|
6901
|
+
/**
|
|
6902
|
+
* An autogenerated component that renders the LEQ iconograpy in SVG.
|
|
6903
|
+
*
|
|
6904
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6905
|
+
*/
|
|
6765
6906
|
class Leq extends React__namespace.Component {
|
|
6907
|
+
static propTypes = {
|
|
6908
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6909
|
+
};
|
|
6766
6910
|
render() {
|
|
6767
6911
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6768
6912
|
width: "48",
|
|
@@ -6786,11 +6930,16 @@ class Leq extends React__namespace.Component {
|
|
|
6786
6930
|
})));
|
|
6787
6931
|
}
|
|
6788
6932
|
}
|
|
6789
|
-
_defineProperty(Leq, "propTypes", {
|
|
6790
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6791
|
-
});
|
|
6792
6933
|
|
|
6934
|
+
/**
|
|
6935
|
+
* An autogenerated component that renders the MINUS iconograpy in SVG.
|
|
6936
|
+
*
|
|
6937
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6938
|
+
*/
|
|
6793
6939
|
class Minus extends React__namespace.Component {
|
|
6940
|
+
static propTypes = {
|
|
6941
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6942
|
+
};
|
|
6794
6943
|
render() {
|
|
6795
6944
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6796
6945
|
width: "48",
|
|
@@ -6811,11 +6960,16 @@ class Minus extends React__namespace.Component {
|
|
|
6811
6960
|
})));
|
|
6812
6961
|
}
|
|
6813
6962
|
}
|
|
6814
|
-
_defineProperty(Minus, "propTypes", {
|
|
6815
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6816
|
-
});
|
|
6817
6963
|
|
|
6964
|
+
/**
|
|
6965
|
+
* An autogenerated component that renders the RADICAL iconograpy in SVG.
|
|
6966
|
+
*
|
|
6967
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
6968
|
+
*/
|
|
6818
6969
|
class Radical extends React__namespace.Component {
|
|
6970
|
+
static propTypes = {
|
|
6971
|
+
color: PropTypes__default["default"].string.isRequired
|
|
6972
|
+
};
|
|
6819
6973
|
render() {
|
|
6820
6974
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6821
6975
|
width: "48",
|
|
@@ -6839,11 +6993,16 @@ class Radical extends React__namespace.Component {
|
|
|
6839
6993
|
})));
|
|
6840
6994
|
}
|
|
6841
6995
|
}
|
|
6842
|
-
_defineProperty(Radical, "propTypes", {
|
|
6843
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6844
|
-
});
|
|
6845
6996
|
|
|
6997
|
+
/**
|
|
6998
|
+
* An autogenerated component that renders the FRAC iconograpy in SVG.
|
|
6999
|
+
*
|
|
7000
|
+
* Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
|
|
7001
|
+
*/
|
|
6846
7002
|
class FracInclusive extends React__namespace.Component {
|
|
7003
|
+
static propTypes = {
|
|
7004
|
+
color: PropTypes__default["default"].string.isRequired
|
|
7005
|
+
};
|
|
6847
7006
|
render() {
|
|
6848
7007
|
return /*#__PURE__*/React__namespace.createElement("svg", {
|
|
6849
7008
|
width: "48",
|
|
@@ -6876,9 +7035,6 @@ class FracInclusive extends React__namespace.Component {
|
|
|
6876
7035
|
}))));
|
|
6877
7036
|
}
|
|
6878
7037
|
}
|
|
6879
|
-
_defineProperty(FracInclusive, "propTypes", {
|
|
6880
|
-
color: PropTypes__default["default"].string.isRequired
|
|
6881
|
-
});
|
|
6882
7038
|
|
|
6883
7039
|
/**
|
|
6884
7040
|
* An autogenerated component that renders the JUMP_OUT_PARENTHESES iconograpy in SVG.
|
|
@@ -7262,7 +7418,7 @@ class MultiSymbolGrid extends React__namespace.Component {
|
|
|
7262
7418
|
// some styles coercion and doesn't seem worthwhile right now.
|
|
7263
7419
|
icons.forEach(icon => {
|
|
7264
7420
|
if (icon.type !== IconType.MATH) {
|
|
7265
|
-
throw new Error(
|
|
7421
|
+
throw new Error(`Received invalid icon: type=${icon.type}, ` + `data=${icon.data}`);
|
|
7266
7422
|
}
|
|
7267
7423
|
});
|
|
7268
7424
|
if (icons.length === 1) {
|
|
@@ -7323,7 +7479,7 @@ class MultiSymbolGrid extends React__namespace.Component {
|
|
|
7323
7479
|
}))));
|
|
7324
7480
|
}
|
|
7325
7481
|
}
|
|
7326
|
-
throw new Error(
|
|
7482
|
+
throw new Error(`Invalid number of icons: ${icons.length}`);
|
|
7327
7483
|
}
|
|
7328
7484
|
}
|
|
7329
7485
|
const verticalInsetPx = 2;
|
|
@@ -7367,72 +7523,13 @@ const styles$7 = aphrodite.StyleSheet.create({
|
|
|
7367
7523
|
|
|
7368
7524
|
// eslint-disable-next-line react/no-unsafe
|
|
7369
7525
|
class KeypadButton extends React__namespace.PureComponent {
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
// the "initial" styles that `View` will include, as these styles are
|
|
7378
|
-
// applied to `View` components and Aphrodite will consolidate the style
|
|
7379
|
-
// object. This method must be called whenever a property that
|
|
7380
|
-
// influences the possible outcomes of `this._getFocusStyle` and
|
|
7381
|
-
// `this._getButtonStyle` changes (such as `this.buttonSizeStyle`).
|
|
7382
|
-
for (const type of KeyTypes) {
|
|
7383
|
-
aphrodite.css(View.styles.initial, ...this._getFocusStyle(type));
|
|
7384
|
-
for (const borders of Object.values(BorderStyles)) {
|
|
7385
|
-
aphrodite.css(View.styles.initial, ...this._getButtonStyle(type, borders));
|
|
7386
|
-
}
|
|
7387
|
-
}
|
|
7388
|
-
});
|
|
7389
|
-
_defineProperty(this, "_getFocusStyle", type => {
|
|
7390
|
-
let focusBackgroundStyle;
|
|
7391
|
-
if (type === "INPUT_NAVIGATION" || type === "KEYPAD_NAVIGATION") {
|
|
7392
|
-
focusBackgroundStyle = styles$6.light;
|
|
7393
|
-
} else {
|
|
7394
|
-
focusBackgroundStyle = styles$6.bright;
|
|
7395
|
-
}
|
|
7396
|
-
return [styles$6.focusBox, focusBackgroundStyle];
|
|
7397
|
-
});
|
|
7398
|
-
_defineProperty(this, "_getButtonStyle", (type, borders, style) => {
|
|
7399
|
-
// Select the appropriate style for the button.
|
|
7400
|
-
let backgroundStyle;
|
|
7401
|
-
switch (type) {
|
|
7402
|
-
case "EMPTY":
|
|
7403
|
-
backgroundStyle = styles$6.empty;
|
|
7404
|
-
break;
|
|
7405
|
-
case "MANY":
|
|
7406
|
-
case "VALUE":
|
|
7407
|
-
backgroundStyle = styles$6.value;
|
|
7408
|
-
break;
|
|
7409
|
-
case "OPERATOR":
|
|
7410
|
-
backgroundStyle = styles$6.operator;
|
|
7411
|
-
break;
|
|
7412
|
-
case "INPUT_NAVIGATION":
|
|
7413
|
-
case "KEYPAD_NAVIGATION":
|
|
7414
|
-
backgroundStyle = styles$6.control;
|
|
7415
|
-
break;
|
|
7416
|
-
case "ECHO":
|
|
7417
|
-
backgroundStyle = null;
|
|
7418
|
-
break;
|
|
7419
|
-
}
|
|
7420
|
-
const borderStyle = [];
|
|
7421
|
-
if (borders.includes(BorderDirection.LEFT)) {
|
|
7422
|
-
// @ts-expect-error TS2345
|
|
7423
|
-
borderStyle.push(styles$6.leftBorder);
|
|
7424
|
-
}
|
|
7425
|
-
if (borders.includes(BorderDirection.BOTTOM)) {
|
|
7426
|
-
// @ts-expect-error TS2345
|
|
7427
|
-
borderStyle.push(styles$6.bottomBorder);
|
|
7428
|
-
}
|
|
7429
|
-
return [styles$6.buttonBase, backgroundStyle, ...borderStyle, type === "ECHO" && styles$6.echo, this.buttonSizeStyle,
|
|
7430
|
-
// React Native allows you to set the 'style' props on user defined
|
|
7431
|
-
// components.
|
|
7432
|
-
// See: https://facebook.github.io/react-native/docs/style.html
|
|
7433
|
-
...(Array.isArray(style) ? style : [style])];
|
|
7434
|
-
});
|
|
7435
|
-
}
|
|
7526
|
+
static defaultProps = {
|
|
7527
|
+
borders: BorderStyles.ALL,
|
|
7528
|
+
childKeys: [],
|
|
7529
|
+
disabled: false,
|
|
7530
|
+
focused: false,
|
|
7531
|
+
popoverEnabled: false
|
|
7532
|
+
};
|
|
7436
7533
|
UNSAFE_componentWillMount() {
|
|
7437
7534
|
this.buttonSizeStyle = styleForButtonDimensions(this.props.heightPx, this.props.widthPx);
|
|
7438
7535
|
}
|
|
@@ -7449,6 +7546,68 @@ class KeypadButton extends React__namespace.PureComponent {
|
|
|
7449
7546
|
this._preInjectStyles();
|
|
7450
7547
|
}
|
|
7451
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
|
+
};
|
|
7452
7611
|
render() {
|
|
7453
7612
|
const {
|
|
7454
7613
|
ariaLabel,
|
|
@@ -7522,13 +7681,6 @@ class KeypadButton extends React__namespace.PureComponent {
|
|
|
7522
7681
|
}
|
|
7523
7682
|
}
|
|
7524
7683
|
}
|
|
7525
|
-
_defineProperty(KeypadButton, "defaultProps", {
|
|
7526
|
-
borders: BorderStyles.ALL,
|
|
7527
|
-
childKeys: [],
|
|
7528
|
-
disabled: false,
|
|
7529
|
-
focused: false,
|
|
7530
|
-
popoverEnabled: false
|
|
7531
|
-
});
|
|
7532
7684
|
const focusInsetPx = 4;
|
|
7533
7685
|
const focusBoxZIndex = 0;
|
|
7534
7686
|
const styles$6 = aphrodite.StyleSheet.create({
|
|
@@ -7706,7 +7858,6 @@ const extractProps = keyConfig => {
|
|
|
7706
7858
|
};
|
|
7707
7859
|
};
|
|
7708
7860
|
const mapStateToProps$5 = (state, ownProps) => {
|
|
7709
|
-
var _gestures$popover;
|
|
7710
7861
|
const {
|
|
7711
7862
|
gestures
|
|
7712
7863
|
} = state;
|
|
@@ -7731,7 +7882,7 @@ const mapStateToProps$5 = (state, ownProps) => {
|
|
|
7731
7882
|
id: id,
|
|
7732
7883
|
// Add in some gesture state.
|
|
7733
7884
|
focused: gestures.focus === id,
|
|
7734
|
-
popoverEnabled:
|
|
7885
|
+
popoverEnabled: gestures.popover?.parentId === id,
|
|
7735
7886
|
// Pass down the child keys and any extracted props.
|
|
7736
7887
|
childKeys,
|
|
7737
7888
|
...extractProps(useFirstChildProps ? childKeys[0] : keyConfig)
|
|
@@ -7749,6 +7900,9 @@ var TouchableKeypadButton$1 = reactRedux.connect(mapStateToProps$5, null, null,
|
|
|
7749
7900
|
})(TouchableKeypadButton);
|
|
7750
7901
|
|
|
7751
7902
|
class ManyKeypadButton extends React__namespace.Component {
|
|
7903
|
+
static defaultProps = {
|
|
7904
|
+
keys: []
|
|
7905
|
+
};
|
|
7752
7906
|
render() {
|
|
7753
7907
|
const {
|
|
7754
7908
|
keys,
|
|
@@ -7782,9 +7936,6 @@ class ManyKeypadButton extends React__namespace.Component {
|
|
|
7782
7936
|
}
|
|
7783
7937
|
}
|
|
7784
7938
|
}
|
|
7785
|
-
_defineProperty(ManyKeypadButton, "defaultProps", {
|
|
7786
|
-
keys: []
|
|
7787
|
-
});
|
|
7788
7939
|
|
|
7789
7940
|
/**
|
|
7790
7941
|
* This file contains all of the z-index values used throughout the math-input
|
|
@@ -7837,35 +7988,32 @@ class Echo extends React__namespace.Component {
|
|
|
7837
7988
|
}
|
|
7838
7989
|
}
|
|
7839
7990
|
class EchoManager extends React__namespace.Component {
|
|
7840
|
-
|
|
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
|
-
}
|
|
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
|
+
};
|
|
7869
8017
|
render() {
|
|
7870
8018
|
const {
|
|
7871
8019
|
echoes,
|
|
@@ -7903,7 +8051,7 @@ class EchoManager extends React__namespace.Component {
|
|
|
7903
8051
|
key: animationId
|
|
7904
8052
|
}, /*#__PURE__*/React__namespace.createElement(Echo, _extends({
|
|
7905
8053
|
animationDurationMs: animationDurationMs,
|
|
7906
|
-
onAnimationFinish: () => onAnimationFinish
|
|
8054
|
+
onAnimationFinish: () => onAnimationFinish?.(animationId)
|
|
7907
8055
|
}, echo)));
|
|
7908
8056
|
}));
|
|
7909
8057
|
}));
|
|
@@ -7998,7 +8146,6 @@ class PopoverManager extends React__namespace.Component {
|
|
|
7998
8146
|
// naming convention: verb + noun
|
|
7999
8147
|
// the noun should be one of the other properties in the object that's
|
|
8000
8148
|
// being dispatched
|
|
8001
|
-
|
|
8002
8149
|
const dismissKeypad = () => {
|
|
8003
8150
|
return {
|
|
8004
8151
|
type: "DismissKeypad"
|
|
@@ -8068,43 +8215,12 @@ const pressKey = (key, initialBounds, inPopover) => {
|
|
|
8068
8215
|
};
|
|
8069
8216
|
};
|
|
8070
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
|
+
*/
|
|
8071
8222
|
// eslint-disable-next-line react/no-unsafe
|
|
8072
8223
|
class Keypad extends React__namespace.Component {
|
|
8073
|
-
constructor() {
|
|
8074
|
-
super(...arguments);
|
|
8075
|
-
_defineProperty(this, "_isMounted", void 0);
|
|
8076
|
-
_defineProperty(this, "_resizeTimeout", void 0);
|
|
8077
|
-
_defineProperty(this, "_container", void 0);
|
|
8078
|
-
_defineProperty(this, "_computeContainer", () => {
|
|
8079
|
-
const domNode = ReactDOM__default["default"].findDOMNode(this);
|
|
8080
|
-
this._container = domNode.getBoundingClientRect();
|
|
8081
|
-
});
|
|
8082
|
-
_defineProperty(this, "_updateSizeAndPosition", () => {
|
|
8083
|
-
// Mark the container for recalculation next time the keypad is
|
|
8084
|
-
// opened.
|
|
8085
|
-
// TODO(charlie): Since we're not recalculating the container
|
|
8086
|
-
// immediately, if you were to resize the page while a popover were
|
|
8087
|
-
// active, you'd likely get unexpected behavior. This seems very
|
|
8088
|
-
// difficult to do and, as such, incredibly unlikely, but we may
|
|
8089
|
-
// want to reconsider the caching here.
|
|
8090
|
-
this._container = null;
|
|
8091
|
-
});
|
|
8092
|
-
_defineProperty(this, "_onResize", () => {
|
|
8093
|
-
// Whenever the page resizes, we need to recompute the container's
|
|
8094
|
-
// bounding box. This is the only time that the bounding box can change.
|
|
8095
|
-
|
|
8096
|
-
// Throttle resize events -- taken from:
|
|
8097
|
-
// https://developer.mozilla.org/en-US/docs/Web/Events/resize
|
|
8098
|
-
if (this._resizeTimeout == null) {
|
|
8099
|
-
this._resizeTimeout = window.setTimeout(() => {
|
|
8100
|
-
this._resizeTimeout = null;
|
|
8101
|
-
if (this._isMounted) {
|
|
8102
|
-
this._updateSizeAndPosition();
|
|
8103
|
-
}
|
|
8104
|
-
}, 66);
|
|
8105
|
-
}
|
|
8106
|
-
});
|
|
8107
|
-
}
|
|
8108
8224
|
componentDidMount() {
|
|
8109
8225
|
this._isMounted = true;
|
|
8110
8226
|
window.addEventListener("resize", this._onResize);
|
|
@@ -8119,6 +8235,35 @@ class Keypad extends React__namespace.Component {
|
|
|
8119
8235
|
this._isMounted = false;
|
|
8120
8236
|
window.removeEventListener("resize", this._onResize);
|
|
8121
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
|
+
};
|
|
8122
8267
|
render() {
|
|
8123
8268
|
const {
|
|
8124
8269
|
children,
|
|
@@ -8196,18 +8341,18 @@ var Keypad$1 = reactRedux.connect(mapStateToProps$4, mapDispatchToProps$1, null,
|
|
|
8196
8341
|
forwardRef: true
|
|
8197
8342
|
})(Keypad);
|
|
8198
8343
|
|
|
8344
|
+
/**
|
|
8345
|
+
* A keypad with two pages of keys.
|
|
8346
|
+
*/
|
|
8199
8347
|
const {
|
|
8200
8348
|
column: column$2,
|
|
8201
8349
|
row: row$4,
|
|
8202
8350
|
fullWidth: fullWidth$2
|
|
8203
8351
|
} = Styles;
|
|
8204
8352
|
class TwoPageKeypad extends React__namespace.Component {
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
selectedPage: "Numbers"
|
|
8209
|
-
});
|
|
8210
|
-
}
|
|
8353
|
+
state = {
|
|
8354
|
+
selectedPage: "Numbers"
|
|
8355
|
+
};
|
|
8211
8356
|
render() {
|
|
8212
8357
|
const {
|
|
8213
8358
|
leftPage,
|
|
@@ -8252,16 +8397,16 @@ const styles$3 = aphrodite.StyleSheet.create({
|
|
|
8252
8397
|
backgroundColor: offBlack16
|
|
8253
8398
|
},
|
|
8254
8399
|
borderTop: {
|
|
8255
|
-
borderTop:
|
|
8400
|
+
borderTop: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`
|
|
8256
8401
|
},
|
|
8257
8402
|
borderLeft: {
|
|
8258
|
-
borderLeft:
|
|
8403
|
+
borderLeft: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`,
|
|
8259
8404
|
boxSizing: "content-box"
|
|
8260
8405
|
},
|
|
8261
8406
|
tabbar: {
|
|
8262
8407
|
background: Color__default["default"].offWhite,
|
|
8263
|
-
borderTop:
|
|
8264
|
-
borderBottom:
|
|
8408
|
+
borderTop: `1px solid ${Color__default["default"].offBlack50}`,
|
|
8409
|
+
borderBottom: `1px solid ${Color__default["default"].offBlack50}`
|
|
8265
8410
|
}
|
|
8266
8411
|
});
|
|
8267
8412
|
const mapStateToProps$3 = state => {
|
|
@@ -8497,9 +8642,8 @@ const styles$2 = aphrodite.StyleSheet.create({
|
|
|
8497
8642
|
}
|
|
8498
8643
|
});
|
|
8499
8644
|
const mapStateToProps$2 = state => {
|
|
8500
|
-
var _state$input$cursor;
|
|
8501
8645
|
return {
|
|
8502
|
-
cursorContext:
|
|
8646
|
+
cursorContext: state.input.cursor?.context,
|
|
8503
8647
|
dynamicJumpOut: !state.layout.navigationPadEnabled
|
|
8504
8648
|
};
|
|
8505
8649
|
};
|
|
@@ -8630,9 +8774,8 @@ class FractionKeypad extends React__namespace.Component {
|
|
|
8630
8774
|
}
|
|
8631
8775
|
}
|
|
8632
8776
|
const mapStateToProps$1 = state => {
|
|
8633
|
-
var _state$input$cursor;
|
|
8634
8777
|
return {
|
|
8635
|
-
cursorContext:
|
|
8778
|
+
cursorContext: state.input.cursor?.context,
|
|
8636
8779
|
dynamicJumpOut: !state.layout.navigationPadEnabled
|
|
8637
8780
|
};
|
|
8638
8781
|
};
|
|
@@ -8749,71 +8892,12 @@ const {
|
|
|
8749
8892
|
} = Styles;
|
|
8750
8893
|
// eslint-disable-next-line react/no-unsafe
|
|
8751
8894
|
class KeypadContainer extends React__namespace.Component {
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
_defineProperty(this, "state", {
|
|
8759
|
-
hasBeenActivated: false,
|
|
8760
|
-
viewportWidth: "100vw"
|
|
8761
|
-
});
|
|
8762
|
-
_defineProperty(this, "_throttleResizeHandler", () => {
|
|
8763
|
-
// Throttle the resize callbacks.
|
|
8764
|
-
// https://developer.mozilla.org/en-US/docs/Web/Events/resize
|
|
8765
|
-
if (this._resizeTimeout == null) {
|
|
8766
|
-
this._resizeTimeout = window.setTimeout(() => {
|
|
8767
|
-
this._resizeTimeout = null;
|
|
8768
|
-
this._onResize();
|
|
8769
|
-
}, 66);
|
|
8770
|
-
}
|
|
8771
|
-
});
|
|
8772
|
-
_defineProperty(this, "_onResize", () => {
|
|
8773
|
-
var _this$_containerRef$c, _this$_containerRef$c2, _this$props$onPageSiz, _this$props;
|
|
8774
|
-
// Whenever the page resizes, we need to force an update, as the button
|
|
8775
|
-
// heights and keypad width are computed based on horizontal space.
|
|
8776
|
-
this.setState({
|
|
8777
|
-
viewportWidth: window.innerWidth
|
|
8778
|
-
});
|
|
8779
|
-
const containerWidth = ((_this$_containerRef$c = this._containerRef.current) === null || _this$_containerRef$c === void 0 ? void 0 : _this$_containerRef$c.clientWidth) || 0;
|
|
8780
|
-
const containerHeight = ((_this$_containerRef$c2 = this._containerRef.current) === null || _this$_containerRef$c2 === void 0 ? void 0 : _this$_containerRef$c2.clientHeight) || 0;
|
|
8781
|
-
(_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);
|
|
8782
|
-
});
|
|
8783
|
-
_defineProperty(this, "renderKeypad", () => {
|
|
8784
|
-
const {
|
|
8785
|
-
extraKeys,
|
|
8786
|
-
keypadType,
|
|
8787
|
-
layoutMode,
|
|
8788
|
-
navigationPadEnabled
|
|
8789
|
-
} = this.props;
|
|
8790
|
-
const keypadProps = {
|
|
8791
|
-
extraKeys,
|
|
8792
|
-
// HACK(charlie): In order to properly round the corners of the
|
|
8793
|
-
// compact keypad, we need to instruct some of our child views to
|
|
8794
|
-
// crop themselves. At least we're colocating all the layout
|
|
8795
|
-
// information in this component, though.
|
|
8796
|
-
roundTopLeft: layoutMode === LayoutMode.COMPACT && !navigationPadEnabled,
|
|
8797
|
-
roundTopRight: layoutMode === LayoutMode.COMPACT
|
|
8798
|
-
};
|
|
8799
|
-
|
|
8800
|
-
// Select the appropriate keyboard given the type.
|
|
8801
|
-
// TODO(charlie): In the future, we might want to move towards a
|
|
8802
|
-
// data-driven approach to defining keyboard layouts, and have a
|
|
8803
|
-
// generic keyboard that takes some "keyboard data" and renders it.
|
|
8804
|
-
// However, the keyboards differ pretty heavily right now and it's not
|
|
8805
|
-
// clear what that format would look like exactly. Plus, there aren't
|
|
8806
|
-
// very many of them. So to keep us moving, we'll just hardcode.
|
|
8807
|
-
switch (keypadType) {
|
|
8808
|
-
case KeypadType.FRACTION:
|
|
8809
|
-
return /*#__PURE__*/React__namespace.createElement(FractionKeypad$1, keypadProps);
|
|
8810
|
-
case KeypadType.EXPRESSION:
|
|
8811
|
-
return /*#__PURE__*/React__namespace.createElement(ExpressionKeypad$1, keypadProps);
|
|
8812
|
-
default:
|
|
8813
|
-
throw new Error("Invalid keypad type: " + keypadType);
|
|
8814
|
-
}
|
|
8815
|
-
});
|
|
8816
|
-
}
|
|
8895
|
+
_containerRef = /*#__PURE__*/React__namespace.createRef();
|
|
8896
|
+
_containerResizeObserver = null;
|
|
8897
|
+
state = {
|
|
8898
|
+
hasBeenActivated: false,
|
|
8899
|
+
viewportWidth: "100vw"
|
|
8900
|
+
};
|
|
8817
8901
|
UNSAFE_componentWillMount() {
|
|
8818
8902
|
if (this.props.active) {
|
|
8819
8903
|
this.setState({
|
|
@@ -8851,11 +8935,63 @@ class KeypadContainer extends React__namespace.Component {
|
|
|
8851
8935
|
}
|
|
8852
8936
|
}
|
|
8853
8937
|
componentWillUnmount() {
|
|
8854
|
-
var _this$_containerResiz;
|
|
8855
8938
|
window.removeEventListener("resize", this._throttleResizeHandler);
|
|
8856
8939
|
window.removeEventListener("orientationchange", this._throttleResizeHandler);
|
|
8857
|
-
|
|
8858
|
-
}
|
|
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
|
+
};
|
|
8859
8995
|
render() {
|
|
8860
8996
|
const {
|
|
8861
8997
|
active,
|
|
@@ -8915,7 +9051,7 @@ const styles = aphrodite.StyleSheet.create({
|
|
|
8915
9051
|
left: 0,
|
|
8916
9052
|
right: 0,
|
|
8917
9053
|
position: "fixed",
|
|
8918
|
-
transition:
|
|
9054
|
+
transition: `${keypadAnimationDurationMs}ms ease-out`,
|
|
8919
9055
|
transitionProperty: "transform",
|
|
8920
9056
|
zIndex: keypad
|
|
8921
9057
|
},
|
|
@@ -8936,7 +9072,7 @@ const styles = aphrodite.StyleSheet.create({
|
|
|
8936
9072
|
},
|
|
8937
9073
|
navigationPadContainer: {
|
|
8938
9074
|
// Add a separator between the navigation pad and the keypad.
|
|
8939
|
-
borderRight:
|
|
9075
|
+
borderRight: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`,
|
|
8940
9076
|
boxSizing: "content-box"
|
|
8941
9077
|
},
|
|
8942
9078
|
// Defer to the navigation pad, such that the navigation pad is always
|
|
@@ -8991,10 +9127,7 @@ var KeypadContainer$1 = reactRedux.connect(mapStateToProps, mapDispatchToProps,
|
|
|
8991
9127
|
* It is entirely ignorant of the existence of popovers and the positions of
|
|
8992
9128
|
* DOM nodes, operating solely on IDs. The state machine does accommodate for
|
|
8993
9129
|
* multi-touch interactions, tracking gesture state on a per-touch basis.
|
|
8994
|
-
*/
|
|
8995
|
-
|
|
8996
|
-
// exported for tests
|
|
8997
|
-
|
|
9130
|
+
*/ // exported for tests
|
|
8998
9131
|
const defaultOptions = {
|
|
8999
9132
|
longPressWaitTimeMs: 50,
|
|
9000
9133
|
swipeThresholdPx: 20,
|
|
@@ -9002,12 +9135,6 @@ const defaultOptions = {
|
|
|
9002
9135
|
};
|
|
9003
9136
|
class GestureStateMachine {
|
|
9004
9137
|
constructor(handlers, options, swipeDisabledNodeIds, multiPressableKeys) {
|
|
9005
|
-
_defineProperty(this, "handlers", void 0);
|
|
9006
|
-
_defineProperty(this, "options", void 0);
|
|
9007
|
-
_defineProperty(this, "swipeDisabledNodeIds", void 0);
|
|
9008
|
-
_defineProperty(this, "multiPressableKeys", void 0);
|
|
9009
|
-
_defineProperty(this, "touchState", void 0);
|
|
9010
|
-
_defineProperty(this, "swipeState", void 0);
|
|
9011
9138
|
this.handlers = handlers;
|
|
9012
9139
|
this.options = {
|
|
9013
9140
|
...defaultOptions,
|
|
@@ -9179,8 +9306,7 @@ class GestureStateMachine {
|
|
|
9179
9306
|
// Only respect the finger that started a swipe. Any other lingering
|
|
9180
9307
|
// gestures are ignored.
|
|
9181
9308
|
if (this.swipeState.touchId === touchId) {
|
|
9182
|
-
|
|
9183
|
-
(_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);
|
|
9184
9310
|
}
|
|
9185
9311
|
} else if (this.touchState[touchId]) {
|
|
9186
9312
|
// It could be touch events started outside the keypad and
|
|
@@ -9193,7 +9319,6 @@ class GestureStateMachine {
|
|
|
9193
9319
|
const dx = pageX - startX;
|
|
9194
9320
|
const shouldBeginSwiping = swipeEnabled && !swipeLocked && Math.abs(dx) > this.options.swipeThresholdPx;
|
|
9195
9321
|
if (shouldBeginSwiping) {
|
|
9196
|
-
var _this$handlers$onSwip2, _this$handlers2;
|
|
9197
9322
|
this._onSwipeStart();
|
|
9198
9323
|
|
|
9199
9324
|
// Trigger the swipe.
|
|
@@ -9201,7 +9326,7 @@ class GestureStateMachine {
|
|
|
9201
9326
|
touchId,
|
|
9202
9327
|
startX
|
|
9203
9328
|
};
|
|
9204
|
-
|
|
9329
|
+
this.handlers.onSwipeChange?.(pageX - this.swipeState.startX);
|
|
9205
9330
|
} else {
|
|
9206
9331
|
const id = getId();
|
|
9207
9332
|
if (id !== activeNodeId) {
|
|
@@ -9224,8 +9349,7 @@ class GestureStateMachine {
|
|
|
9224
9349
|
// Only respect the finger that started a swipe. Any other lingering
|
|
9225
9350
|
// gestures are ignored.
|
|
9226
9351
|
if (this.swipeState.touchId === touchId) {
|
|
9227
|
-
|
|
9228
|
-
(_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);
|
|
9229
9353
|
this.swipeState = null;
|
|
9230
9354
|
}
|
|
9231
9355
|
} else if (this.touchState[touchId]) {
|
|
@@ -9259,8 +9383,7 @@ class GestureStateMachine {
|
|
|
9259
9383
|
// displacement.
|
|
9260
9384
|
if (this.swipeState) {
|
|
9261
9385
|
if (this.swipeState.touchId === touchId) {
|
|
9262
|
-
|
|
9263
|
-
(_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);
|
|
9264
9387
|
this.swipeState = null;
|
|
9265
9388
|
}
|
|
9266
9389
|
} else if (this.touchState[touchId]) {
|
|
@@ -9283,9 +9406,6 @@ class GestureStateMachine {
|
|
|
9283
9406
|
|
|
9284
9407
|
class NodeManager {
|
|
9285
9408
|
constructor() {
|
|
9286
|
-
_defineProperty(this, "_nodesById", void 0);
|
|
9287
|
-
_defineProperty(this, "_orderedIds", void 0);
|
|
9288
|
-
_defineProperty(this, "_cachedBoundingBoxesById", void 0);
|
|
9289
9409
|
// A mapping from IDs to DOM nodes.
|
|
9290
9410
|
this._nodesById = {};
|
|
9291
9411
|
|
|
@@ -9398,9 +9518,6 @@ class NodeManager {
|
|
|
9398
9518
|
|
|
9399
9519
|
class PopoverStateMachine {
|
|
9400
9520
|
constructor(handlers) {
|
|
9401
|
-
_defineProperty(this, "handlers", void 0);
|
|
9402
|
-
_defineProperty(this, "popovers", void 0);
|
|
9403
|
-
_defineProperty(this, "activePopover", void 0);
|
|
9404
9521
|
this.handlers = handlers;
|
|
9405
9522
|
this.activePopover = null;
|
|
9406
9523
|
this.popovers = {};
|
|
@@ -9560,16 +9677,16 @@ class PopoverStateMachine {
|
|
|
9560
9677
|
}
|
|
9561
9678
|
}
|
|
9562
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
|
+
*/
|
|
9563
9685
|
const coordsForEvent = evt => {
|
|
9564
9686
|
return [evt.changedTouches[0].clientX, evt.changedTouches[0].clientY];
|
|
9565
9687
|
};
|
|
9566
9688
|
class GestureManager {
|
|
9567
9689
|
constructor(options, handlers, disabledSwipeKeys, multiPressableKeys) {
|
|
9568
|
-
_defineProperty(this, "swipeEnabled", void 0);
|
|
9569
|
-
_defineProperty(this, "trackEvents", void 0);
|
|
9570
|
-
_defineProperty(this, "nodeManager", void 0);
|
|
9571
|
-
_defineProperty(this, "popoverStateMachine", void 0);
|
|
9572
|
-
_defineProperty(this, "gestureStateMachine", void 0);
|
|
9573
9690
|
const {
|
|
9574
9691
|
swipeEnabled
|
|
9575
9692
|
} = options;
|
|
@@ -9810,13 +9927,12 @@ const inputReducer = function () {
|
|
|
9810
9927
|
case "PressKey":
|
|
9811
9928
|
const keyConfig = KeyConfigs[action.key];
|
|
9812
9929
|
if (keyConfig.type !== "KEYPAD_NAVIGATION") {
|
|
9813
|
-
var _state$keyHandler;
|
|
9814
9930
|
// This is probably an anti-pattern but it works for the
|
|
9815
9931
|
// case where we don't actually control the state but we
|
|
9816
9932
|
// still want to communicate with the other object
|
|
9817
9933
|
return {
|
|
9818
9934
|
...state,
|
|
9819
|
-
cursor:
|
|
9935
|
+
cursor: state.keyHandler?.(keyConfig.id)
|
|
9820
9936
|
};
|
|
9821
9937
|
}
|
|
9822
9938
|
|
|
@@ -10185,66 +10301,6 @@ const createStore = () => {
|
|
|
10185
10301
|
class ProvidedKeypad extends React__namespace.Component {
|
|
10186
10302
|
constructor(props) {
|
|
10187
10303
|
super(props);
|
|
10188
|
-
_defineProperty(this, "store", void 0);
|
|
10189
|
-
_defineProperty(this, "activate", () => {
|
|
10190
|
-
this.props.setKeypadActive(true);
|
|
10191
|
-
});
|
|
10192
|
-
_defineProperty(this, "dismiss", () => {
|
|
10193
|
-
this.props.setKeypadActive(false);
|
|
10194
|
-
});
|
|
10195
|
-
_defineProperty(this, "configure", (configuration, cb) => {
|
|
10196
|
-
this.store.dispatch(configureKeypad(configuration));
|
|
10197
|
-
|
|
10198
|
-
// HACK(charlie): In Perseus, triggering a focus causes the keypad to
|
|
10199
|
-
// animate into view and re-configure. We'd like to provide the option
|
|
10200
|
-
// to re-render the re-configured keypad before animating it into view,
|
|
10201
|
-
// to avoid jank in the animation. As such, we support passing a
|
|
10202
|
-
// callback into `configureKeypad`. However, implementing this properly
|
|
10203
|
-
// would require middleware, etc., so we just hack it on with
|
|
10204
|
-
// `setTimeout` for now.
|
|
10205
|
-
setTimeout(() => cb && cb());
|
|
10206
|
-
});
|
|
10207
|
-
_defineProperty(this, "setCursor", cursor => {
|
|
10208
|
-
this.store.dispatch(setCursor(cursor));
|
|
10209
|
-
});
|
|
10210
|
-
_defineProperty(this, "setKeyHandler", keyHandler => {
|
|
10211
|
-
this.store.dispatch(setKeyHandler(keyHandler));
|
|
10212
|
-
});
|
|
10213
|
-
_defineProperty(this, "getDOMNode", () => {
|
|
10214
|
-
return ReactDOM__default["default"].findDOMNode(this);
|
|
10215
|
-
});
|
|
10216
|
-
_defineProperty(this, "onElementMounted", element => {
|
|
10217
|
-
var _this$props$onElement, _this$props;
|
|
10218
|
-
this.props.onAnalyticsEvent({
|
|
10219
|
-
type: "math-input:keypad-opened",
|
|
10220
|
-
payload: {
|
|
10221
|
-
virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
|
|
10222
|
-
}
|
|
10223
|
-
});
|
|
10224
|
-
|
|
10225
|
-
// Append the dispatch methods that we want to expose
|
|
10226
|
-
// externally to the returned React element.
|
|
10227
|
-
const elementWithDispatchMethods = {
|
|
10228
|
-
...element,
|
|
10229
|
-
activate: this.activate,
|
|
10230
|
-
dismiss: this.dismiss,
|
|
10231
|
-
configure: this.configure,
|
|
10232
|
-
setCursor: this.setCursor,
|
|
10233
|
-
setKeyHandler: this.setKeyHandler,
|
|
10234
|
-
getDOMNode: this.getDOMNode
|
|
10235
|
-
};
|
|
10236
|
-
(_this$props$onElement = (_this$props = this.props).onElementMounted) === null || _this$props$onElement === void 0 ? void 0 : _this$props$onElement.call(_this$props, elementWithDispatchMethods);
|
|
10237
|
-
});
|
|
10238
|
-
_defineProperty(this, "onDismiss", () => {
|
|
10239
|
-
var _this$props$onDismiss, _this$props2;
|
|
10240
|
-
this.props.onAnalyticsEvent({
|
|
10241
|
-
type: "math-input:keypad-closed",
|
|
10242
|
-
payload: {
|
|
10243
|
-
virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
|
|
10244
|
-
}
|
|
10245
|
-
});
|
|
10246
|
-
(_this$props$onDismiss = (_this$props2 = this.props).onDismiss) === null || _this$props$onDismiss === void 0 ? void 0 : _this$props$onDismiss.call(_this$props2);
|
|
10247
|
-
});
|
|
10248
10304
|
this.store = createStore();
|
|
10249
10305
|
}
|
|
10250
10306
|
componentDidUpdate(prevProps) {
|
|
@@ -10255,6 +10311,63 @@ class ProvidedKeypad extends React__namespace.Component {
|
|
|
10255
10311
|
this.store.dispatch(dismissKeypad());
|
|
10256
10312
|
}
|
|
10257
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
|
+
};
|
|
10258
10371
|
render() {
|
|
10259
10372
|
const {
|
|
10260
10373
|
style
|