@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/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 = "14.2.1";
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 ? " ".concat(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 = "translate(".concat(x, "px, ").concat(y, "px)");
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: "0 0 ".concat(cursorWidthPx, " ").concat(cursorHeightPx)
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 === null || command === void 0 ? void 0 : command.startNode);
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("^".concat(key === "EXP_2" ? 2 : 3));
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("Invalid exponent key: ".concat(key));
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("Attempted to 'Jump Out' from node, but found no " + "appropriate cursor context: ".concat(context));
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("\\".concat(command, "\\left(\\right)"));
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
- // Use a y-coordinate that's just above where the user is actually
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
- // Numbers
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
- // Movement keys
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
- // Operators
1957
- else if (key in keyMap) {
1958
- return keyMap[key];
1959
- }
1960
-
1961
- // The key pressed doesn't map to any of the math input operators
1962
- return null;
1963
- });
1964
- _defineProperty(this, "handleKeyUp", event => {
1965
- const mathQuillKey = this.domKeyToMathQuillKey(event.key);
1966
- if (mathQuillKey) {
1967
- this.mathField.pressKey(mathQuillKey);
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: "1px solid transparent",
2364
+ border: `1px solid transparent`,
2339
2365
  marginRight: 1,
2340
2366
  marginLeft: 1
2341
2367
  },
2342
2368
  hovered: {
2343
- background: "linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ".concat(Color__default["default"].white),
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: "2px solid ".concat(Color__default["default"].blue)
2377
+ border: `2px solid ${Color__default["default"].blue}`
2352
2378
  },
2353
2379
  innerBox: {
2354
2380
  boxSizing: "border-box",
2355
- border: "1px solid transparent",
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: "1px solid ".concat(Color__default["default"].white)
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: "none"
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: "tabbar-item-".concat(item),
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("Unhandled key: ".concat(unhandledKey));
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: "0px 1px 0px ".concat(Color__default["default"].offBlack32),
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: "linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ".concat(Color__default["default"].white),
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: "none"
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("Invalid key: ".concat(key));
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: "none"
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: "0px 1px 0px ".concat(Color__default["default"].offBlack32),
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: "linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ".concat(Color__default["default"].white),
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 ((_props$extraKeys = props.extraKeys) !== null && _props$extraKeys !== void 0 && _props$extraKeys.length) {
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
- this.props.schedule.animationFrame(this.flushClassNameQueue);
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
- constructor() {
5685
- super(...arguments);
5686
- _defineProperty(this, "_containerRef", /*#__PURE__*/React__namespace.createRef());
5687
- _defineProperty(this, "_containerResizeObserver", null);
5688
- _defineProperty(this, "_throttleResize", false);
5689
- _defineProperty(this, "state", {
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
- (_this$props$onElement = (_this$props2 = this.props).onElementMounted) === null || _this$props$onElement === void 0 ? void 0 : _this$props$onElement.call(_this$props2, {
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
- (_this$_containerResiz = this._containerResizeObserver) === null || _this$_containerResiz === void 0 ? void 0 : _this$_containerResiz.disconnect();
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 = (_this$state$keyHandle = (_this$state = this.state).keyHandler) === null || _this$state$keyHandle === void 0 ? void 0 : _this$state$keyHandle.call(_this$state, key);
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 = (keypadConfig === null || keypadConfig === void 0 ? void 0 : keypadConfig.keypadType) === "EXPRESSION";
5801
- const convertDotToTimes = keypadConfig === null || keypadConfig === void 0 ? void 0 : keypadConfig.times;
5802
- return /*#__PURE__*/React__namespace.createElement(AphroditeCSSTransitionGroup, {
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: "".concat(AnimationDurationInMS, "ms ease-out")
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: "".concat(AnimationDurationInMS, "ms ease-out")
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(View, {
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 === null || keypadConfig === void 0 ? void 0 : keypadConfig.extraKeys,
5842
+ extraKeys: keypadConfig?.extraKeys,
5827
5843
  onClickKey: key => this._handleClickKey(key),
5828
- cursorContext: cursor === null || cursor === void 0 ? void 0 : cursor.context,
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
- })) : null);
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("Received invalid icon: type=".concat(icon.type, ", ") + "data=".concat(icon.data));
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("Invalid number of icons: ".concat(icons.length));
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
- constructor() {
7371
- super(...arguments);
7372
- _defineProperty(this, "buttonSizeStyle", void 0);
7373
- _defineProperty(this, "_preInjectStyles", () => {
7374
- // HACK(charlie): Pre-inject all of the possible styles for the button.
7375
- // This avoids a flickering effect in the echo animation whereby the
7376
- // echoes vary in size as they animate. Note that we need to account for
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: ((_gestures$popover = gestures.popover) === null || _gestures$popover === void 0 ? void 0 : _gestures$popover.parentId) === id,
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
- constructor() {
7841
- super(...arguments);
7842
- _defineProperty(this, "_animationConfigForType", animationType => {
7843
- // NOTE(charlie): These must be kept in sync with the transition
7844
- // durations and classnames specified in echo.css.
7845
- let animationDurationMs;
7846
- let animationTransitionName;
7847
- switch (animationType) {
7848
- case EchoAnimationType.SLIDE_AND_FADE:
7849
- animationDurationMs = 400;
7850
- animationTransitionName = "echo-slide-and-fade";
7851
- break;
7852
- case EchoAnimationType.FADE_ONLY:
7853
- animationDurationMs = 300;
7854
- animationTransitionName = "echo-fade-only";
7855
- break;
7856
- case EchoAnimationType.LONG_FADE_ONLY:
7857
- animationDurationMs = 400;
7858
- animationTransitionName = "echo-long-fade-only";
7859
- break;
7860
- default:
7861
- throw new Error("Invalid echo animation type: ".concat(animationType));
7862
- }
7863
- return {
7864
- animationDurationMs,
7865
- animationTransitionName
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 === null || onAnimationFinish === void 0 ? void 0 : onAnimationFinish(animationId)
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
- constructor() {
8206
- super(...arguments);
8207
- _defineProperty(this, "state", {
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: "".concat(innerBorderWidthPx, "px ").concat(innerBorderStyle, " ") + "".concat(innerBorderColor)
8400
+ borderTop: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`
8256
8401
  },
8257
8402
  borderLeft: {
8258
- borderLeft: "".concat(innerBorderWidthPx, "px ").concat(innerBorderStyle, " ") + "".concat(innerBorderColor),
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: "1px solid ".concat(Color__default["default"].offBlack50),
8264
- borderBottom: "1px solid ".concat(Color__default["default"].offBlack50)
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: (_state$input$cursor = state.input.cursor) === null || _state$input$cursor === void 0 ? void 0 : _state$input$cursor.context,
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: (_state$input$cursor = state.input.cursor) === null || _state$input$cursor === void 0 ? void 0 : _state$input$cursor.context,
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
- constructor() {
8753
- super(...arguments);
8754
- _defineProperty(this, "_containerRef", /*#__PURE__*/React__namespace.createRef());
8755
- _defineProperty(this, "_containerResizeObserver", null);
8756
- _defineProperty(this, "_resizeTimeout", void 0);
8757
- _defineProperty(this, "hasMounted", void 0);
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
- (_this$_containerResiz = this._containerResizeObserver) === null || _this$_containerResiz === void 0 ? void 0 : _this$_containerResiz.disconnect();
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: "".concat(keypadAnimationDurationMs, "ms ease-out"),
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: "".concat(innerBorderWidthPx, "px ").concat(innerBorderStyle, " ") + "".concat(innerBorderColor),
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
- var _this$handlers$onSwip, _this$handlers;
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
- (_this$handlers$onSwip2 = (_this$handlers2 = this.handlers).onSwipeChange) === null || _this$handlers$onSwip2 === void 0 ? void 0 : _this$handlers$onSwip2.call(_this$handlers2, pageX - this.swipeState.startX);
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
- var _this$handlers$onSwip3, _this$handlers3;
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
- var _this$handlers$onSwip4, _this$handlers4;
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: (_state$keyHandler = state.keyHandler) === null || _state$keyHandler === void 0 ? void 0 : _state$keyHandler.call(state, keyConfig.id)
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