@khanacademy/math-input 14.2.2 → 15.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.2";
56
+ const libVersion = "15.0.1";
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]);
@@ -885,70 +857,10 @@ function handleBackspace(mathField) {
885
857
  }
886
858
  }
887
859
 
888
- /**
889
- * Constants that are shared between multiple files.
890
- */
891
-
892
- let KeypadType = /*#__PURE__*/function (KeypadType) {
893
- KeypadType["FRACTION"] = "FRACTION";
894
- KeypadType["EXPRESSION"] = "EXPRESSION";
895
- return KeypadType;
896
- }({});
897
- const KeyTypes = ["EMPTY",
898
- // For numerals, variables, and any other characters that themselves
899
- // compose 'values'.
900
- "VALUE",
901
- // For buttons that insert or adjust math in an input.
902
- "OPERATOR",
903
- // For buttons that move the cursor in an input (including via
904
- // deletion).
905
- "INPUT_NAVIGATION",
906
- // For buttons that modify the broader keypad state (e.g., by changing
907
- // the visible pane).
908
- "KEYPAD_NAVIGATION",
909
- // For buttons that house multiple buttons and have no action
910
- // themselves.
911
- "MANY",
912
- // For the echo animation that appears on press.
913
- "ECHO"];
914
- let DeviceOrientation = /*#__PURE__*/function (DeviceOrientation) {
915
- DeviceOrientation["LANDSCAPE"] = "LANDSCAPE";
916
- DeviceOrientation["PORTRAIT"] = "PORTRAIT";
917
- return DeviceOrientation;
918
- }({});
919
- let LayoutMode = /*#__PURE__*/function (LayoutMode) {
920
- LayoutMode["FULLSCREEN"] = "FULLSCREEN";
921
- LayoutMode["COMPACT"] = "COMPACT";
922
- return LayoutMode;
923
- }({});
924
- let BorderDirection = /*#__PURE__*/function (BorderDirection) {
925
- BorderDirection["LEFT"] = "LEFT";
926
- BorderDirection["BOTTOM"] = "BOTTOM";
927
- return BorderDirection;
928
- }({});
929
- const BorderStyles = {
930
- LEFT: [BorderDirection.LEFT],
931
- BOTTOM: [BorderDirection.BOTTOM],
932
- ALL: [BorderDirection.LEFT, BorderDirection.BOTTOM],
933
- NONE: []
860
+ const DecimalSeparator = {
861
+ COMMA: ",",
862
+ PERIOD: "."
934
863
  };
935
- let IconType = /*#__PURE__*/function (IconType) {
936
- IconType["MATH"] = "MATH";
937
- IconType["SVG"] = "SVG";
938
- IconType["TEXT"] = "TEXT";
939
- return IconType;
940
- }({});
941
- let DecimalSeparator = /*#__PURE__*/function (DecimalSeparator) {
942
- DecimalSeparator["COMMA"] = "COMMA";
943
- DecimalSeparator["PERIOD"] = "PERIOD";
944
- return DecimalSeparator;
945
- }({});
946
- let EchoAnimationType = /*#__PURE__*/function (EchoAnimationType) {
947
- EchoAnimationType["SLIDE_AND_FADE"] = "SLIDE_AND_FADE";
948
- EchoAnimationType["FADE_ONLY"] = "FADE_ONLY";
949
- EchoAnimationType["LONG_FADE_ONLY"] = "LONG_FADE_ONLY";
950
- return EchoAnimationType;
951
- }({});
952
864
 
953
865
  // NOTES(kevinb):
954
866
  // - In order to get the correct decimal separator for the current locale,
@@ -1028,7 +940,7 @@ function handleExponent(mathField, key) {
1028
940
  break;
1029
941
  case "EXP_2":
1030
942
  case "EXP_3":
1031
- mathField.write("^".concat(key === "EXP_2" ? 2 : 3));
943
+ mathField.write(`^${key === "EXP_2" ? 2 : 3}`);
1032
944
 
1033
945
  // If we enter a square or a cube, we should leave the cursor
1034
946
  // within the newly inserted parens, if they exist. This takes
@@ -1042,7 +954,7 @@ function handleExponent(mathField, key) {
1042
954
  }
1043
955
  break;
1044
956
  default:
1045
- throw new Error("Invalid exponent key: ".concat(key));
957
+ throw new Error(`Invalid exponent key: ${key}`);
1046
958
  }
1047
959
  }
1048
960
 
@@ -1124,7 +1036,7 @@ function handleJumpOut(mathField, key) {
1124
1036
  cursor.insRightOf(cursor.parent.parent);
1125
1037
  break;
1126
1038
  default:
1127
- throw new Error("Attempted to 'Jump Out' from node, but found no " + "appropriate cursor context: ".concat(context));
1039
+ throw new Error(`Attempted to 'Jump Out' from node, but found no ` + `appropriate cursor context: ${context}`);
1128
1040
  }
1129
1041
  }
1130
1042
 
@@ -1135,7 +1047,6 @@ var ActionType = /*#__PURE__*/function (ActionType) {
1135
1047
  ActionType[ActionType["MQ_END"] = 0] = "MQ_END";
1136
1048
  return ActionType;
1137
1049
  }(ActionType || {});
1138
- const decimalSymbol = decimalSeparator === DecimalSeparator.COMMA ? "," : ".";
1139
1050
  function buildGenericCallback(str) {
1140
1051
  let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ActionType.WRITE;
1141
1052
  return function (mathQuill) {
@@ -1160,7 +1071,7 @@ function buildGenericCallback(str) {
1160
1071
  }
1161
1072
  function buildNormalFunctionCallback(command) {
1162
1073
  return function (mathField) {
1163
- mathField.write("\\".concat(command, "\\left(\\right)"));
1074
+ mathField.write(`\\${command}\\left(\\right)`);
1164
1075
  mathField.keystroke("Left");
1165
1076
  };
1166
1077
  }
@@ -1182,7 +1093,7 @@ const keyToMathquillMap = {
1182
1093
  COS: buildNormalFunctionCallback("cos"),
1183
1094
  TAN: buildNormalFunctionCallback("tan"),
1184
1095
  CDOT: buildGenericCallback("\\cdot"),
1185
- DECIMAL: buildGenericCallback(decimalSymbol),
1096
+ DECIMAL: buildGenericCallback(decimalSeparator),
1186
1097
  DIVIDE: buildGenericCallback("\\div"),
1187
1098
  EQUAL: buildGenericCallback("="),
1188
1099
  GEQ: buildGenericCallback("\\geq"),
@@ -1323,6 +1234,7 @@ const keyToMathquillMap = {
1323
1234
  Z: buildGenericCallback("Z")
1324
1235
  };
1325
1236
 
1237
+ // Notes about MathQuill
1326
1238
  const mobileKeyTranslator = {
1327
1239
  ...keyToMathquillMap,
1328
1240
  // note(Matthew): our mobile backspace logic is really complicated
@@ -1344,8 +1256,6 @@ class MathWrapper {
1344
1256
 
1345
1257
  constructor(element) {
1346
1258
  let callbacks = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1347
- _defineProperty(this, "mathField", void 0);
1348
- _defineProperty(this, "callbacks", void 0);
1349
1259
  this.mathField = createMathField(element, () => {
1350
1260
  return {
1351
1261
  // use a span instead of a textarea so that we don't bring up the
@@ -1528,481 +1438,27 @@ const scrollIntoView = (containerNode, keypadNode) => {
1528
1438
  const constrainingFrictionFactor = 0.8;
1529
1439
  // eslint-disable-next-line react/no-unsafe
1530
1440
  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
- });
1441
+ // @ts-expect-error - TS2564 - Property 'recordTouchStartOutside' has no initializer and is not definitely assigned in the constructor.
1916
1442
 
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
- };
1443
+ // @ts-expect-error - TS2564 - Property 'blurOnTouchEndOutside' has no initializer and is not definitely assigned in the constructor.
1945
1444
 
1946
- // Numbers
1947
- if (["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(key)) {
1948
- return "NUM_".concat(key);
1949
- }
1445
+ // @ts-expect-error - TS2564 - Property '_container' has no initializer and is not definitely assigned in the constructor.
1950
1446
 
1951
- // Movement keys
1952
- else if (key === "Backspace") {
1953
- return "BACKSPACE";
1954
- }
1955
-
1956
- // Operators
1957
- else if (key in keyMap) {
1958
- return keyMap[key];
1959
- }
1447
+ // @ts-expect-error - TS2564 - Property '_containerBounds' has no initializer and is not definitely assigned in the constructor.
1960
1448
 
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
- }
1449
+ static defaultProps = {
1450
+ style: {},
1451
+ value: ""
1452
+ };
1453
+ state = {
1454
+ focused: false,
1455
+ handle: {
1456
+ animateIntoPosition: false,
1457
+ visible: false,
1458
+ x: 0,
1459
+ y: 0
1460
+ }
1461
+ };
2006
1462
  componentDidMount() {
2007
1463
  this._isMounted = true;
2008
1464
  this.mathField = new MathWrapper(this._mathContainer, {
@@ -2106,26 +1562,539 @@ class MathInput extends React__namespace.Component {
2106
1562
  if (this.props.keypadElement !== props.keypadElement) {
2107
1563
  this._clearKeypadBoundsCache();
2108
1564
  }
2109
- }
2110
- componentDidUpdate(prevProps, prevState) {
2111
- if (this.mathField.getContent() !== this.props.value) {
2112
- this.mathField.setContent(this.props.value);
1565
+ }
1566
+ componentDidUpdate(prevProps, prevState) {
1567
+ if (this.mathField.getContent() !== this.props.value) {
1568
+ this.mathField.setContent(this.props.value);
1569
+ }
1570
+ if (prevState.focused !== this.state.focused) {
1571
+ this._updateInputPadding();
1572
+ }
1573
+ }
1574
+ componentWillUnmount() {
1575
+ this._isMounted = false;
1576
+ window.removeEventListener("touchstart", this.recordTouchStartOutside);
1577
+ window.removeEventListener("touchend", this.blurOnTouchEndOutside);
1578
+ window.removeEventListener("touchcancel", this.blurOnTouchEndOutside);
1579
+ // @ts-expect-error - TS2769 - No overload matches this call.
1580
+ window.removeEventListener("resize", this._clearKeypadBoundsCache());
1581
+ window.removeEventListener("orientationchange",
1582
+ // @ts-expect-error - TS2769 - No overload matches this call.
1583
+ this._clearKeypadBoundsCache());
1584
+ }
1585
+ _clearKeypadBoundsCache = () => {
1586
+ this._keypadBounds = null;
1587
+ };
1588
+ _cacheKeypadBounds = keypadNode => {
1589
+ this._keypadBounds = keypadNode.getBoundingClientRect();
1590
+ };
1591
+ _updateInputPadding = () => {
1592
+ this._container = ReactDOM__default["default"].findDOMNode(this);
1593
+ this._root = this._container.querySelector(".mq-root-block");
1594
+ const padding = this.getInputInnerPadding();
1595
+ // NOTE(diedra): This overrides the default 2px padding from Mathquil.
1596
+ this._root.style.padding = `${padding.paddingTop}px ${padding.paddingRight}px` + ` ${padding.paddingBottom}px ${padding.paddingLeft}px`;
1597
+ this._root.style.fontSize = `${fontSizePt}pt`;
1598
+ };
1599
+
1600
+ /** Gets and cache they bounds of the keypadElement */
1601
+ _getKeypadBounds = () => {
1602
+ if (!this._keypadBounds) {
1603
+ const node = this.props.keypadElement?.getDOMNode();
1604
+ this._cacheKeypadBounds(node);
1605
+ }
1606
+ return this._keypadBounds;
1607
+ };
1608
+ _updateCursorHandle = animateIntoPosition => {
1609
+ const containerBounds = this._container.getBoundingClientRect();
1610
+ const cursor = this._container.querySelector(".mq-cursor");
1611
+ const cursorBounds = cursor.getBoundingClientRect();
1612
+ const cursorWidth = cursorBounds.width;
1613
+ const gapBelowCursor = 2;
1614
+ const inputInnerPadding = this.getInputInnerPadding();
1615
+
1616
+ // The cursor should never be further right or left than the edge of the
1617
+ // container's values.
1618
+ const furthestRightCursorBound = containerBounds.right - cursorWidth - inputInnerPadding.paddingRight;
1619
+ const furthestLeftCursorBound = containerBounds.left + cursorWidth + inputInnerPadding.paddingLeft;
1620
+ let cursorBoundsLeft = cursorBounds.left;
1621
+ if (cursorBounds.left > furthestRightCursorBound) {
1622
+ cursorBoundsLeft = furthestRightCursorBound;
1623
+ } else if (cursorBounds.left < furthestLeftCursorBound) {
1624
+ cursorBoundsLeft = furthestLeftCursorBound;
1625
+ }
1626
+ this.setState({
1627
+ handle: {
1628
+ visible: true,
1629
+ animateIntoPosition,
1630
+ // We subtract containerBounds' left/top to correct for the
1631
+ // position of the container within the page.
1632
+ x: cursorBoundsLeft + cursorWidth / 2 - containerBounds.left,
1633
+ y: cursorBounds.bottom + gapBelowCursor - containerBounds.top
1634
+ }
1635
+ });
1636
+ };
1637
+ _hideCursorHandle = () => {
1638
+ this.setState({
1639
+ handle: {
1640
+ visible: false,
1641
+ x: 0,
1642
+ y: 0
1643
+ }
1644
+ });
1645
+ };
1646
+ _handleScroll = () => {
1647
+ // If animateIntoPosition is false, the user is currently manually positioning
1648
+ // the cursor. This is important because the user can scroll the input field
1649
+ // with the curor handle, and we don't want to override that ability.
1650
+ // But we do want to hide the handle is the user is just scrolling the input field
1651
+ // normally, because the handle will not move with the scroll.
1652
+ if (this.state.handle.animateIntoPosition !== false) {
1653
+ this._hideCursorHandle();
1654
+ }
1655
+ };
1656
+ blur = () => {
1657
+ this.mathField.blur();
1658
+ this.props.onBlur && this.props.onBlur();
1659
+ this.setState({
1660
+ focused: false,
1661
+ handle: {
1662
+ visible: false
1663
+ }
1664
+ });
1665
+ };
1666
+ focus = () => {
1667
+ // Pass this component's handleKey method to the keypad so it can call
1668
+ // it whenever it needs to trigger a keypress action.
1669
+ this.props.keypadElement?.setKeyHandler(key => {
1670
+ const cursor = this.mathField.pressKey(key);
1671
+
1672
+ // Trigger an `onChange` if the value in the input changed, and hide
1673
+ // the cursor handle whenever the user types a key. If the value
1674
+ // changed as a result of a keypress, we need to be careful not to
1675
+ // call `setState` until after `onChange` has resolved.
1676
+ const hideCursor = () => {
1677
+ this.setState({
1678
+ handle: {
1679
+ visible: false
1680
+ }
1681
+ });
1682
+ };
1683
+ const value = this.mathField.getContent();
1684
+ if (this.props.value !== value) {
1685
+ this.props.onChange(value, hideCursor);
1686
+ } else {
1687
+ hideCursor();
1688
+ }
1689
+ return cursor;
1690
+ });
1691
+ this.mathField.focus();
1692
+ this.props?.onFocus();
1693
+ this.setState({
1694
+ focused: true
1695
+ }, () => {
1696
+ // NOTE(charlie): We use `setTimeout` to allow for a layout pass to
1697
+ // occur. Otherwise, the keypad is measured incorrectly. Ideally,
1698
+ // we'd use requestAnimationFrame here, but it's unsupported on
1699
+ // Android Browser 4.3.
1700
+ setTimeout(() => {
1701
+ if (this._isMounted) {
1702
+ // TODO(benkomalo): the keypad is animating at this point,
1703
+ // so we can't call _cacheKeypadBounds(), even though
1704
+ // it'd be nice to do so. It should probably be the case
1705
+ // that the higher level controller tells us when the
1706
+ // keypad is settled (then scrollIntoView wouldn't have
1707
+ // to make assumptions about that either).
1708
+ const maybeKeypadNode = this.props.keypadElement?.getDOMNode();
1709
+ scrollIntoView(this._container, maybeKeypadNode);
1710
+ }
1711
+ });
1712
+ });
1713
+ };
1714
+
1715
+ /**
1716
+ * Tries to determine which DOM node to place the cursor next to based on
1717
+ * where the user drags the cursor handle. If it finds a node it will
1718
+ * place the cursor next to it, update the handle to be under the cursor,
1719
+ * and return true. If it doesn't find a node, it returns false.
1720
+ *
1721
+ * It searches for nodes by doing it tests at the following points:
1722
+ *
1723
+ * (x - dx, y), (x, y), (x + dx, y)
1724
+ *
1725
+ * If it doesn't find any nodes from the rendered math it will update y
1726
+ * by adding dy.
1727
+ *
1728
+ * The algorithm ends its search when y goes outside the bounds of
1729
+ * containerBounds.
1730
+ *
1731
+ * @param {DOMRect} containerBounds - bounds of the container node
1732
+ * @param {number} x - the initial x coordinate in the viewport
1733
+ * @param {number} y - the initial y coordinate in the viewport
1734
+ * @param {number} dx - horizontal spacing between elementFromPoint calls
1735
+ * @param {number} dy - vertical spacing between elementFromPoint calls,
1736
+ * sign determines direction.
1737
+ * @returns {boolean} - true if a node was hit, false otherwise.
1738
+ */
1739
+ _findHitNode = (containerBounds, x, y, dx, dy) => {
1740
+ while (y >= containerBounds.top && y <= containerBounds.bottom) {
1741
+ y += dy;
1742
+ const points = [[x - dx, y], [x, y], [x + dx, y]];
1743
+ const elements = points
1744
+ // @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
1745
+ .map(point => document.elementFromPoint(...point))
1746
+ // We exclude the root container itself and any nodes marked
1747
+ // as non-leaf which are fractions, parens, and roots. The
1748
+ // children of those nodes are included in the list because
1749
+ // those are the items we care about placing the cursor next
1750
+ // to.
1751
+ //
1752
+ // MathQuill's mq-non-leaf is not applied to all non-leaf nodes
1753
+ // so the naming is a bit confusing. Although fractions are
1754
+ // included, neither mq-numerator nor mq-denominator nodes are
1755
+ // and neither are subscripts or superscripts.
1756
+ .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")));
1757
+ let hitNode = null;
1758
+
1759
+ // Contains only DOMNodes with child elements.
1760
+ const nonLeafElements = [];
1761
+ let max = 0;
1762
+ const counts = {};
1763
+ const elementsById = {};
1764
+ for (const element of elements) {
1765
+ // @ts-expect-error - TS2531 - Object is possibly 'null'.
1766
+ const id = element.getAttribute("mathquill-command-id");
1767
+ if (id != null) {
1768
+ counts[id] = (counts[id] || 0) + 1;
1769
+ elementsById[id] = element;
1770
+ } else {
1771
+ // @ts-expect-error - TS2345 - Argument of type 'Element | null' is not assignable to parameter of type 'HTMLElement | null'.
1772
+ nonLeafElements.push(element);
1773
+ }
1774
+ }
1775
+
1776
+ // When determining which DOMNode to place the cursor beside, we
1777
+ // prefer leaf nodes. Hitting a leaf node is a good sign that the
1778
+ // cursor is really close to some piece of math that has been
1779
+ // rendered because leaf nodes contain text. Non-leaf nodes may
1780
+ // contain a lot of whitespace so the cursor may be further away
1781
+ // from actual text within the expression.
1782
+ //
1783
+ // Since we're doing three hit tests per loop it's possible that
1784
+ // we hit multiple leaf nodes at the same time. In this case we
1785
+ // we prefer the DOMNode with the most hits.
1786
+ // TODO(kevinb) consider preferring nodes hit by [x, y].
1787
+ for (const [id, count] of wonderStuffCore.entries(counts)) {
1788
+ if (count > max) {
1789
+ max = count;
1790
+ hitNode = elementsById[id];
1791
+ }
1792
+ }
1793
+
1794
+ // It's possible that two non-leaf nodes are right beside each
1795
+ // other. We don't bother counting the number of hits for each,
1796
+ // b/c this seems like an unlikely situation. Also, ignoring the
1797
+ // hit count in the situation should not have serious effects on
1798
+ // the overall accuracy of the algorithm.
1799
+ if (hitNode == null && nonLeafElements.length > 0) {
1800
+ // @ts-expect-error - TS2322 - Type 'HTMLElement | null' is not assignable to type 'null'.
1801
+ hitNode = nonLeafElements[0];
1802
+ }
1803
+ if (hitNode !== null) {
1804
+ this.mathField.setCursorPosition(x, y, hitNode);
1805
+ return true;
1806
+ }
1807
+ }
1808
+ return false;
1809
+ };
1810
+
1811
+ /**
1812
+ * Inserts the cursor at the DOM node closest to the given coordinates,
1813
+ * based on hit-tests conducted using #_findHitNode.
1814
+ *
1815
+ * @param {number} x - the x coordinate in the viewport
1816
+ * @param {number} y - the y coordinate in the viewport
1817
+ */
1818
+ _insertCursorAtClosestNode = (x, y) => {
1819
+ const cursor = this.mathField.getCursor();
1820
+
1821
+ // Pre-emptively check if the input has any child nodes; if not, the
1822
+ // input is empty, so we throw the cursor at the start.
1823
+ if (!this._root.hasChildNodes()) {
1824
+ cursor.insAtLeftEnd(this.mathField.mathField.__controller.root);
1825
+ return;
1826
+ }
1827
+
1828
+ // NOTE(diedra): The adding and subtracting of 10 or 15 pixels here accounts
1829
+ // for the padding that surrounds the input values.
1830
+ if (y > this._containerBounds.bottom) {
1831
+ y = this._containerBounds.bottom - 10;
1832
+ } else if (y < this._containerBounds.top) {
1833
+ y = this._containerBounds.top + 10;
1834
+ }
1835
+ if (x > this._containerBounds.right) {
1836
+ x = this._containerBounds.right - 15;
1837
+ } else if (x < this._containerBounds.left) {
1838
+ x = this._containerBounds.left + 15;
1839
+ }
1840
+ let dy;
1841
+
1842
+ // Vertical spacing between hit tests
1843
+ // dy is negative because we're moving upwards.
1844
+ dy = -8;
1845
+
1846
+ // Horizontal spacing between hit tests
1847
+ // Note: This value depends on the font size. If the gap is too small
1848
+ // we end up placing the cursor at the end of the expression when we
1849
+ // shouldn't.
1850
+ const dx = 5;
1851
+ if (this._findHitNode(this._containerBounds, x, y, dx, dy)) {
1852
+ return;
1853
+ }
1854
+
1855
+ // If we haven't found anything start from the top.
1856
+ y = this._containerBounds.top;
1857
+
1858
+ // dy is positive b/c we're going downwards.
1859
+ dy = 8;
1860
+ if (this._findHitNode(this._containerBounds, x, y, dx, dy)) {
1861
+ return;
1862
+ }
1863
+ const firstChildBounds = this._root.firstChild.getBoundingClientRect();
1864
+ const lastChildBounds = this._root.lastChild.getBoundingClientRect();
1865
+ const left = firstChildBounds.left;
1866
+ const right = lastChildBounds.right;
1867
+
1868
+ // We've exhausted all of the options. We're likely either to the right
1869
+ // or left of all of the math, so we place the cursor at the end to
1870
+ // which it's closest.
1871
+ if (Math.abs(x - right) < Math.abs(x - left)) {
1872
+ cursor.insAtRightEnd(this.mathField.mathField.__controller.root);
1873
+ } else {
1874
+ cursor.insAtLeftEnd(this.mathField.mathField.__controller.root);
1875
+ }
1876
+ // In that event, we need to update the cursor context ourselves.
1877
+ this.props.keypadElement && this.props.keypadElement.setCursor({
1878
+ context: this.mathField.contextForCursor()
1879
+ });
1880
+ };
1881
+ handleTouchStart = e => {
1882
+ e.stopPropagation();
1883
+
1884
+ // Hide the cursor handle on touch start, if the handle itself isn't
1885
+ // handling the touch event.
1886
+ this._hideCursorHandle();
1887
+
1888
+ // Cache the container bounds, so as to avoid re-computing. If we don't
1889
+ // have any content, then it's not necessary, since the cursor can't be
1890
+ // moved anyway.
1891
+ if (this.mathField.getContent() !== "") {
1892
+ this._containerBounds = this._container.getBoundingClientRect();
1893
+
1894
+ // Make the cursor visible and set the handle-less cursor's
1895
+ // location.
1896
+ const touch = e.changedTouches[0];
1897
+ this._insertCursorAtClosestNode(touch.clientX, touch.clientY);
1898
+ }
1899
+
1900
+ // Trigger a focus event, if we're not already focused.
1901
+ if (!this.state.focused) {
1902
+ this.focus();
1903
+ }
1904
+ };
1905
+ handleTouchMove = e => {
1906
+ e.stopPropagation();
1907
+
1908
+ // Update the handle-less cursor's location on move, if there's any
1909
+ // content in the box. Note that if the user touched outside the keypad
1910
+ // (e.g., with a different finger) during this touch interaction, we
1911
+ // may have blurred, in which case we should ignore the touch (since
1912
+ // the cursor is no longer visible and the input is no longer
1913
+ // highlighted).
1914
+ if (this.mathField.getContent() !== "" && this.state.focused) {
1915
+ const touch = e.changedTouches[0];
1916
+ this._insertCursorAtClosestNode(touch.clientX, touch.clientY);
1917
+ }
1918
+ };
1919
+ handleTouchEnd = e => {
1920
+ e.stopPropagation();
1921
+
1922
+ // And on touch-end, reveal the cursor, unless the input is empty. Note
1923
+ // that if the user touched outside the keypad (e.g., with a different
1924
+ // finger) during this touch interaction, we may have blurred, in which
1925
+ // case we should ignore the touch (since the cursor is no longer
1926
+ // visible and the input is no longer highlighted).
1927
+ if (this.mathField.getContent() !== "" && this.state.focused) {
1928
+ this._updateCursorHandle();
1929
+ }
1930
+ };
1931
+
1932
+ /**
1933
+ * When a touch starts in the cursor handle, we track it so as to avoid
1934
+ * handling any touch events ourself.
1935
+ *
1936
+ * @param {TouchEvent} e - the raw touch event from the browser
1937
+ */
1938
+ onCursorHandleTouchStart = e => {
1939
+ // NOTE(charlie): The cursor handle is a child of this view, so whenever
1940
+ // it receives a touch event, that event would also typically be bubbled
1941
+ // up to our own handlers. However, we want the cursor to handle its own
1942
+ // touch events, and for this view to only handle touch events that
1943
+ // don't affect the cursor. As such, we `stopPropagation` on any touch
1944
+ // events that are being handled by the cursor, so as to avoid handling
1945
+ // them in our own touch handlers.
1946
+ e.stopPropagation();
1947
+ e.preventDefault();
1948
+
1949
+ // Cache the container bounds, so as to avoid re-computing.
1950
+ this._containerBounds = this._container.getBoundingClientRect();
1951
+ };
1952
+ _constrainToBound = (value, min, max, friction) => {
1953
+ if (value < min) {
1954
+ return min + (value - min) * friction;
1955
+ } else if (value > max) {
1956
+ return max + (value - max) * friction;
1957
+ } else {
1958
+ return value;
1959
+ }
1960
+ };
1961
+
1962
+ /**
1963
+ * When the user moves the cursor handle update the position of the cursor
1964
+ * and the handle.
1965
+ *
1966
+ * @param {TouchEvent} e - the raw touch event from the browser
1967
+ */
1968
+ onCursorHandleTouchMove = e => {
1969
+ e.stopPropagation();
1970
+ const x = e.changedTouches[0].clientX;
1971
+ const y = e.changedTouches[0].clientY;
1972
+ const relativeX = x - this._containerBounds.left;
1973
+ const relativeY = y - 2 * cursorHandleRadiusPx * cursorHandleDistanceMultiplier - this._containerBounds.top;
1974
+
1975
+ // We subtract the containerBounds left/top to correct for the
1976
+ // MathInput's position on the page. On top of that, we subtract an
1977
+ // additional 2 x {height of the cursor} so that the bottom of the
1978
+ // cursor tracks the user's finger, to make it visible under their
1979
+ // touch.
1980
+ this.setState({
1981
+ handle: {
1982
+ animateIntoPosition: false,
1983
+ visible: true,
1984
+ // TODO(charlie): Use clientX and clientY to avoid the need for
1985
+ // scroll offsets. This likely also means that the cursor
1986
+ // detection doesn't work when scrolled, since we're not
1987
+ // offsetting those values.
1988
+ x: this._constrainToBound(relativeX, 0, this._containerBounds.width, constrainingFrictionFactor),
1989
+ y: this._constrainToBound(relativeY, 0, this._containerBounds.height, constrainingFrictionFactor)
1990
+ }
1991
+ });
1992
+
1993
+ // Use a y-coordinate that's just above where the user is actually
1994
+ // touching because they're dragging the handle which is a little
1995
+ // below where the cursor actually is.
1996
+ const distanceAboveFingerToTrySelecting = 22;
1997
+ const adjustedY = y - distanceAboveFingerToTrySelecting;
1998
+ this._insertCursorAtClosestNode(x, adjustedY);
1999
+ };
2000
+
2001
+ /**
2002
+ * When the user releases the cursor handle, animate it back into place.
2003
+ *
2004
+ * @param {TouchEvent} e - the raw touch event from the browser
2005
+ */
2006
+ onCursorHandleTouchEnd = e => {
2007
+ e.stopPropagation();
2008
+ this._updateCursorHandle(true);
2009
+ };
2010
+
2011
+ /**
2012
+ * If the gesture is cancelled mid-drag, simply hide it.
2013
+ *
2014
+ * @param {TouchEvent} e - the raw touch event from the browser
2015
+ */
2016
+ onCursorHandleTouchCancel = e => {
2017
+ e.stopPropagation();
2018
+ this._updateCursorHandle(true);
2019
+ };
2020
+ domKeyToMathQuillKey = key => {
2021
+ const keyMap = {
2022
+ "+": "PLUS",
2023
+ "-": "MINUS",
2024
+ "*": "TIMES",
2025
+ "/": "DIVIDE",
2026
+ ".": "DECIMAL",
2027
+ "%": "PERCENT",
2028
+ "=": "EQUAL",
2029
+ ">": "GT",
2030
+ "<": "LT",
2031
+ "^": "EXP"
2032
+ };
2033
+
2034
+ // Numbers
2035
+ if (["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(key)) {
2036
+ return `NUM_${key}`;
2037
+ }
2038
+
2039
+ // Movement keys
2040
+ else if (key === "Backspace") {
2041
+ return "BACKSPACE";
2113
2042
  }
2114
- if (prevState.focused !== this.state.focused) {
2115
- this._updateInputPadding();
2043
+
2044
+ // Operators
2045
+ else if (key in keyMap) {
2046
+ return keyMap[key];
2116
2047
  }
2117
- }
2118
- componentWillUnmount() {
2119
- this._isMounted = false;
2120
- window.removeEventListener("touchstart", this.recordTouchStartOutside);
2121
- window.removeEventListener("touchend", this.blurOnTouchEndOutside);
2122
- window.removeEventListener("touchcancel", this.blurOnTouchEndOutside);
2123
- // @ts-expect-error - TS2769 - No overload matches this call.
2124
- window.removeEventListener("resize", this._clearKeypadBoundsCache());
2125
- window.removeEventListener("orientationchange",
2126
- // @ts-expect-error - TS2769 - No overload matches this call.
2127
- this._clearKeypadBoundsCache());
2128
- }
2048
+
2049
+ // The key pressed doesn't map to any of the math input operators
2050
+ return null;
2051
+ };
2052
+ handleKeyUp = event => {
2053
+ const mathQuillKey = this.domKeyToMathQuillKey(event.key);
2054
+ if (mathQuillKey) {
2055
+ this.mathField.pressKey(mathQuillKey);
2056
+
2057
+ // TODO(diedra): If the new value being added is off-screen to the right
2058
+ // due to the max-width of the text box, scroll the box to show the newest
2059
+ // value
2060
+ const value = this.mathField.getContent();
2061
+ if (this.props.value !== value) {
2062
+ this.mathField.setContent(this.props.value);
2063
+ this.props.onChange(value, false);
2064
+ this._hideCursorHandle();
2065
+ }
2066
+ }
2067
+ };
2068
+ getBorderWidthPx = () => {
2069
+ // TODO(diedra): Move these to the common style package.
2070
+ const normalBorderWidthPx = 1;
2071
+ const focusedBorderWidthPx = 2;
2072
+ return this.state.focused ? focusedBorderWidthPx : normalBorderWidthPx;
2073
+ };
2074
+
2075
+ // Calculate the appropriate padding based on the border width (which is
2076
+ // considered 'padding', since we're using 'border-box') and the fact
2077
+ // that MathQuill automatically applies 2px of padding to the inner
2078
+ // input.
2079
+ getInputInnerPadding = () => {
2080
+ const paddingInset = totalDesiredPadding - this.getBorderWidthPx();
2081
+
2082
+ // Now, translate that to the appropriate padding for each direction.
2083
+ // The complication here is that we want numerals to be centered within
2084
+ // the input. However, Symbola (MathQuill's font of choice) renders
2085
+ // numerals with approximately 3px of padding below and 1px of padding
2086
+ // above (to make room for ascenders and descenders). So we ignore those
2087
+ // padding values for the vertical directions.
2088
+ const symbolaPaddingBottom = 3;
2089
+ const symbolaPaddingTop = 1;
2090
+ const padding = {
2091
+ paddingTop: paddingInset - symbolaPaddingTop,
2092
+ paddingRight: paddingInset,
2093
+ paddingBottom: paddingInset - symbolaPaddingBottom,
2094
+ paddingLeft: paddingInset
2095
+ };
2096
+ return padding;
2097
+ };
2129
2098
  render() {
2130
2099
  const {
2131
2100
  focused,
@@ -2181,10 +2150,6 @@ class MathInput extends React__namespace.Component {
2181
2150
  })));
2182
2151
  }
2183
2152
  }
2184
- _defineProperty(MathInput, "defaultProps", {
2185
- style: {},
2186
- value: ""
2187
- });
2188
2153
  const fontSizePt = 18;
2189
2154
  const inputMaxWidth = 128;
2190
2155
 
@@ -2335,12 +2300,12 @@ const styles$i = aphrodite.StyleSheet.create({
2335
2300
  height: 38,
2336
2301
  boxSizing: "border-box",
2337
2302
  borderRadius: 3,
2338
- border: "1px solid transparent",
2303
+ border: `1px solid transparent`,
2339
2304
  marginRight: 1,
2340
2305
  marginLeft: 1
2341
2306
  },
2342
2307
  hovered: {
2343
- background: "linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ".concat(Color__default["default"].white),
2308
+ background: `linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ${Color__default["default"].white}`,
2344
2309
  border: "1px solid #1865F2"
2345
2310
  },
2346
2311
  pressed: {
@@ -2348,11 +2313,11 @@ const styles$i = aphrodite.StyleSheet.create({
2348
2313
  },
2349
2314
  focused: {
2350
2315
  outline: "none",
2351
- border: "2px solid ".concat(Color__default["default"].blue)
2316
+ border: `2px solid ${Color__default["default"].blue}`
2352
2317
  },
2353
2318
  innerBox: {
2354
2319
  boxSizing: "border-box",
2355
- border: "1px solid transparent",
2320
+ border: `1px solid transparent`,
2356
2321
  borderRadius: 2,
2357
2322
  display: "flex",
2358
2323
  flex: 1,
@@ -2360,7 +2325,7 @@ const styles$i = aphrodite.StyleSheet.create({
2360
2325
  alignItems: "center"
2361
2326
  },
2362
2327
  innerBoxPressed: {
2363
- border: "1px solid ".concat(Color__default["default"].white)
2328
+ border: `1px solid ${Color__default["default"].white}`
2364
2329
  },
2365
2330
  activeIndicator: {
2366
2331
  position: "absolute",
@@ -2372,7 +2337,7 @@ const styles$i = aphrodite.StyleSheet.create({
2372
2337
  },
2373
2338
  clickable: {
2374
2339
  ":focus": {
2375
- outline: "none"
2340
+ outline: `none`
2376
2341
  }
2377
2342
  }
2378
2343
  });
@@ -2452,7 +2417,7 @@ function Tabbar(props) {
2452
2417
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2453
2418
  style: [styles$h.pages]
2454
2419
  }, items.map(item => /*#__PURE__*/React__namespace.createElement(TabbarItem, {
2455
- key: "tabbar-item-".concat(item),
2420
+ key: `tabbar-item-${item}`,
2456
2421
  itemState: item === selectedItem ? "active" : "inactive",
2457
2422
  itemType: item,
2458
2423
  onClick: () => {
@@ -2465,6 +2430,66 @@ function Tabbar(props) {
2465
2430
  })));
2466
2431
  }
2467
2432
 
2433
+ /**
2434
+ * Constants that are shared between multiple files.
2435
+ */
2436
+
2437
+ let KeypadType = /*#__PURE__*/function (KeypadType) {
2438
+ KeypadType["FRACTION"] = "FRACTION";
2439
+ KeypadType["EXPRESSION"] = "EXPRESSION";
2440
+ return KeypadType;
2441
+ }({});
2442
+ const KeyTypes = ["EMPTY",
2443
+ // For numerals, variables, and any other characters that themselves
2444
+ // compose 'values'.
2445
+ "VALUE",
2446
+ // For buttons that insert or adjust math in an input.
2447
+ "OPERATOR",
2448
+ // For buttons that move the cursor in an input (including via
2449
+ // deletion).
2450
+ "INPUT_NAVIGATION",
2451
+ // For buttons that modify the broader keypad state (e.g., by changing
2452
+ // the visible pane).
2453
+ "KEYPAD_NAVIGATION",
2454
+ // For buttons that house multiple buttons and have no action
2455
+ // themselves.
2456
+ "MANY",
2457
+ // For the echo animation that appears on press.
2458
+ "ECHO"];
2459
+ let DeviceOrientation = /*#__PURE__*/function (DeviceOrientation) {
2460
+ DeviceOrientation["LANDSCAPE"] = "LANDSCAPE";
2461
+ DeviceOrientation["PORTRAIT"] = "PORTRAIT";
2462
+ return DeviceOrientation;
2463
+ }({});
2464
+ let LayoutMode = /*#__PURE__*/function (LayoutMode) {
2465
+ LayoutMode["FULLSCREEN"] = "FULLSCREEN";
2466
+ LayoutMode["COMPACT"] = "COMPACT";
2467
+ return LayoutMode;
2468
+ }({});
2469
+ let BorderDirection = /*#__PURE__*/function (BorderDirection) {
2470
+ BorderDirection["LEFT"] = "LEFT";
2471
+ BorderDirection["BOTTOM"] = "BOTTOM";
2472
+ return BorderDirection;
2473
+ }({});
2474
+ const BorderStyles = {
2475
+ LEFT: [BorderDirection.LEFT],
2476
+ BOTTOM: [BorderDirection.BOTTOM],
2477
+ ALL: [BorderDirection.LEFT, BorderDirection.BOTTOM],
2478
+ NONE: []
2479
+ };
2480
+ let IconType = /*#__PURE__*/function (IconType) {
2481
+ IconType["MATH"] = "MATH";
2482
+ IconType["SVG"] = "SVG";
2483
+ IconType["TEXT"] = "TEXT";
2484
+ return IconType;
2485
+ }({});
2486
+ let EchoAnimationType = /*#__PURE__*/function (EchoAnimationType) {
2487
+ EchoAnimationType["SLIDE_AND_FADE"] = "SLIDE_AND_FADE";
2488
+ EchoAnimationType["FADE_ONLY"] = "FADE_ONLY";
2489
+ EchoAnimationType["LONG_FADE_ONLY"] = "LONG_FADE_ONLY";
2490
+ return EchoAnimationType;
2491
+ }({});
2492
+
2468
2493
  /**
2469
2494
  * This file contains configuration settings for the buttons in the keypad.
2470
2495
  */
@@ -2565,16 +2590,7 @@ const KeyConfigs = {
2565
2590
  keyType: "VALUE",
2566
2591
  // I18N: A label for a 'decimal' sign (represented as '.' or ',').
2567
2592
  ariaLabel: i18n__namespace._("Decimal")
2568
- }),
2569
- icon: decimalSeparator === DecimalSeparator.COMMA ? {
2570
- // TODO(charlie): Get an SVG icon for the comma, or verify with
2571
- // design that the text-rendered version is acceptable.
2572
- type: IconType.TEXT,
2573
- data: ","
2574
- } : {
2575
- type: IconType.SVG,
2576
- data: "PERIOD"
2577
- }
2593
+ })
2578
2594
  },
2579
2595
  PERIOD: {
2580
2596
  ...getDefaultOperatorFields({
@@ -3357,16 +3373,32 @@ function ButtonAsset(_ref) {
3357
3373
  d: "M16.735 16.8558c0 1.024.272 1.812.816 2.364.552.544 1.32.816 2.304.816.528 0 .992-.084 1.392-.252.408-.168.752-.392 1.032-.672.28-.288.492-.62.636-.996.144-.384.216-.792.216-1.224 0-.504-.08-.952-.24-1.344-.152-.4-.372-.74-.66-1.02-.28-.28-.616-.492-1.008-.636-.392-.152-.82-.228-1.284-.228-.488 0-.928.08-1.32.24-.392.16-.728.384-1.008.672-.28.28-.496.616-.648 1.008-.152.384-.228.808-.228 1.272zm4.428 5.364c.16-.2.308-.388.444-.564.136-.184.264-.368.384-.552-.416.296-.88.524-1.392.684-.512.152-1.056.228-1.632.228-.624 0-1.224-.104-1.8-.312-.576-.216-1.088-.532-1.536-.948-.448-.424-.808-.944-1.08-1.56-.264-.624-.396-1.34-.396-2.148 0-.76.14-1.476.42-2.148.288-.672.688-1.256 1.2-1.752.512-.504 1.124-.9 1.836-1.188.712-.288 1.5-.432 2.364-.432.856 0 1.632.14 2.328.42.696.272 1.288.66 1.776 1.164.488.496.864 1.092 1.128 1.788.264.688.396 1.448.396 2.28 0 .52-.048 1.012-.144 1.476-.088.464-.22.916-.396 1.356-.168.432-.376.86-.624 1.284-.24.416-.512.84-.816 1.272l-4.068 5.832c-.12.176-.3.32-.54.432-.232.112-.496.168-.792.168h-2.364l5.304-6.78z",
3358
3374
  fill: "#21242C"
3359
3375
  }));
3360
- // TODO(ned): Per the notes in `KeyConfigs`, shouldn't this be a comma
3361
- // that we replace with the period icon for i18n? Duplicating for now.
3362
3376
  case "DECIMAL":
3363
3377
  case "PERIOD":
3378
+ // Different locales use different symbols for the decimal separator
3379
+ // (, vs .)
3380
+ if (id === "DECIMAL" && decimalSeparator === DecimalSeparator.COMMA) {
3381
+ // comma decimal separator
3382
+ return /*#__PURE__*/React__namespace.createElement("svg", {
3383
+ width: "40",
3384
+ height: "40",
3385
+ viewBox: "0 0 32 32",
3386
+ fill: "none",
3387
+ xmlns: "http://www.w3.org/2000/svg",
3388
+ "data-test-id": "comma-decimal"
3389
+ }, /*#__PURE__*/React__namespace.createElement("path", {
3390
+ d: "M11.5559 25.3544C11.8679 24.661 12.1799 23.933 12.4919 23.1704C12.8039 22.425 13.0986 21.6884 13.3759 20.9604C13.6706 20.2324 13.9219 19.5737 14.1299 18.9844H16.6259L16.7299 19.2704C16.4526 19.877 16.1232 20.5357 15.7419 21.2464C15.3606 21.9397 14.9619 22.633 14.5459 23.3264C14.1299 24.037 13.7139 24.713 13.2979 25.3544H11.5559Z",
3391
+ fill: "#21242C"
3392
+ }));
3393
+ }
3394
+ // period / US decimal separator
3364
3395
  return /*#__PURE__*/React__namespace.createElement("svg", {
3365
3396
  width: "40",
3366
3397
  height: "40",
3367
3398
  viewBox: "0 0 40 40",
3368
3399
  fill: "none",
3369
- xmlns: "http://www.w3.org/2000/svg"
3400
+ xmlns: "http://www.w3.org/2000/svg",
3401
+ "data-test-id": "period-decimal"
3370
3402
  }, /*#__PURE__*/React__namespace.createElement("path", {
3371
3403
  d: "M18.3401 27.512c0-.232.04-.448.12-.648.088-.208.204-.388.348-.54.152-.152.328-.272.528-.36.208-.088.428-.132.66-.132.232 0 .448.044.648.132.208.088.388.208.54.36.152.152.272.332.36.54.088.2.132.416.132.648 0 .24-.044.46-.132.66-.088.2-.208.376-.36.528-.152.152-.332.268-.54.348-.2.088-.416.132-.648.132-.232 0-.452-.044-.66-.132-.2-.08-.376-.196-.528-.348-.144-.152-.26-.328-.348-.528-.08-.2-.12-.42-.12-.66z",
3372
3404
  fill: "#21242C"
@@ -4652,7 +4684,7 @@ function ButtonAsset(_ref) {
4652
4684
  // this line forces an exhaustive check of all keys;
4653
4685
  // if a key is not handled, the compiler will complain.
4654
4686
  const unhandledKey = id;
4655
- throw new Error("Unhandled key: ".concat(unhandledKey));
4687
+ throw new Error(`Unhandled key: ${unhandledKey}`);
4656
4688
  }
4657
4689
  }
4658
4690
 
@@ -4702,7 +4734,7 @@ const styles$g = aphrodite.StyleSheet.create({
4702
4734
  display: "flex",
4703
4735
  justifyContent: "center",
4704
4736
  alignItems: "center",
4705
- boxShadow: "0px 1px 0px ".concat(Color__default["default"].offBlack32),
4737
+ boxShadow: `0px 1px 0px ${Color__default["default"].offBlack32}`,
4706
4738
  boxSizing: "border-box",
4707
4739
  background: Color__default["default"].white,
4708
4740
  borderRadius: 4,
@@ -4727,7 +4759,7 @@ const styles$g = aphrodite.StyleSheet.create({
4727
4759
  pressed: {
4728
4760
  border: "2px solid #1B50B3",
4729
4761
  padding: 0,
4730
- background: "linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ".concat(Color__default["default"].white),
4762
+ background: `linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ${Color__default["default"].white}`,
4731
4763
  boxShadow: "none"
4732
4764
  },
4733
4765
  outerBoxBase: {
@@ -4743,7 +4775,7 @@ const styles$g = aphrodite.StyleSheet.create({
4743
4775
  height: "100%",
4744
4776
  boxSizing: "border-box",
4745
4777
  ":focus": {
4746
- outline: "none"
4778
+ outline: `none`
4747
4779
  }
4748
4780
  }
4749
4781
  });
@@ -5037,7 +5069,7 @@ function getStyles(key) {
5037
5069
  case "LEFT":
5038
5070
  return styles$f.left;
5039
5071
  default:
5040
- throw new Error("Invalid key: ".concat(key));
5072
+ throw new Error(`Invalid key: ${key}`);
5041
5073
  }
5042
5074
  }
5043
5075
  function NavigationButton(_ref) {
@@ -5078,7 +5110,7 @@ const styles$f = aphrodite.StyleSheet.create({
5078
5110
  width: "100%",
5079
5111
  height: "100%",
5080
5112
  ":focus": {
5081
- outline: "none"
5113
+ outline: `none`
5082
5114
  }
5083
5115
  },
5084
5116
  outerBoxBase: {
@@ -5086,7 +5118,7 @@ const styles$f = aphrodite.StyleSheet.create({
5086
5118
  width: "100%"
5087
5119
  },
5088
5120
  base: {
5089
- boxShadow: "0px 1px 0px ".concat(Color__default["default"].offBlack32),
5121
+ boxShadow: `0px 1px 0px ${Color__default["default"].offBlack32}`,
5090
5122
  display: "flex",
5091
5123
  justifyContent: "center",
5092
5124
  alignItems: "center",
@@ -5120,7 +5152,7 @@ const styles$f = aphrodite.StyleSheet.create({
5120
5152
  },
5121
5153
  pressed: {
5122
5154
  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),
5155
+ background: `linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ${Color__default["default"].white}`,
5124
5156
  boxShadow: "none"
5125
5157
  }
5126
5158
  });
@@ -5238,7 +5270,6 @@ const defaultProps = {
5238
5270
  extraKeys: []
5239
5271
  };
5240
5272
  function getAvailableTabs(props) {
5241
- var _props$extraKeys;
5242
5273
  // We don't want to show any available tabs on the fractions keypad
5243
5274
  if (props.fractionsOnly) {
5244
5275
  return [];
@@ -5252,7 +5283,7 @@ function getAvailableTabs(props) {
5252
5283
  if (props.trigonometry) {
5253
5284
  tabs.push("Geometry");
5254
5285
  }
5255
- if ((_props$extraKeys = props.extraKeys) !== null && _props$extraKeys !== void 0 && _props$extraKeys.length) {
5286
+ if (props.extraKeys?.length) {
5256
5287
  tabs.push("Extras");
5257
5288
  }
5258
5289
  return tabs;
@@ -5465,6 +5496,7 @@ class TransitionChild extends React__namespace.Component {
5465
5496
  // We keep track of all of the current applied classes so that we can remove
5466
5497
  // them before a new transition starts in the case of the current transition
5467
5498
  // being interrupted.
5499
+ _isMounted = false;
5468
5500
 
5469
5501
  // The use of getDerivedStateFromProps here is to avoid an extra call to
5470
5502
  // setState if the component re-enters. This can happen if TransitionGroup
@@ -5483,38 +5515,6 @@ class TransitionChild extends React__namespace.Component {
5483
5515
  }
5484
5516
  constructor(props) {
5485
5517
  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
5518
  this._isMounted = false;
5519
5519
  this.classNameQueue = [];
5520
5520
  this.appliedClassNames = new Set();
@@ -5558,6 +5558,18 @@ class TransitionChild extends React__namespace.Component {
5558
5558
  this.removeClass(node, className);
5559
5559
  }
5560
5560
  }
5561
+ addClass = (elem, className) => {
5562
+ if (className) {
5563
+ elem.classList.add(className);
5564
+ this.appliedClassNames.add(className);
5565
+ }
5566
+ };
5567
+ removeClass = (elem, className) => {
5568
+ if (className) {
5569
+ elem.classList.remove(className);
5570
+ this.appliedClassNames.delete(className);
5571
+ }
5572
+ };
5561
5573
  transition(animationType, duration) {
5562
5574
  const node = ReactDOM__default["default"].findDOMNode(this);
5563
5575
  if (!(node instanceof Element)) {
@@ -5599,6 +5611,23 @@ class TransitionChild extends React__namespace.Component {
5599
5611
  // Queue operation for after the next paint.
5600
5612
  this.props.schedule.timeout(this.flushClassNameQueue, 0);
5601
5613
  }
5614
+ flushClassNameQueue = () => {
5615
+ if (this._isMounted) {
5616
+ const node = ReactDOM__default["default"].findDOMNode(this);
5617
+ if (node instanceof Element) {
5618
+ this.classNameQueue.forEach(_ref2 => {
5619
+ let [removeClassName, addClassName] = _ref2;
5620
+ // Remove the old class before adding a new class just
5621
+ // in case the new class is the same as the old one.
5622
+ this.removeClass(node, removeClassName);
5623
+ this.addClass(node, addClassName);
5624
+ });
5625
+ }
5626
+ }
5627
+
5628
+ // Remove all items in the Array.
5629
+ this.classNameQueue.length = 0;
5630
+ };
5602
5631
  render() {
5603
5632
  const {
5604
5633
  status
@@ -5682,71 +5711,13 @@ const AnimationDurationInMS = 200;
5682
5711
  * can't have methods attached to them).
5683
5712
  */
5684
5713
  class MobileKeypad extends React__namespace.Component {
5685
- constructor() {
5686
- super(...arguments);
5687
- _defineProperty(this, "_containerRef", /*#__PURE__*/React__namespace.createRef());
5688
- _defineProperty(this, "_containerResizeObserver", null);
5689
- _defineProperty(this, "_throttleResize", false);
5690
- _defineProperty(this, "state", {
5691
- containerWidth: 0
5692
- });
5693
- _defineProperty(this, "_resize", () => {
5694
- var _this$_containerRef$c;
5695
- const containerWidth = ((_this$_containerRef$c = this._containerRef.current) === null || _this$_containerRef$c === void 0 ? void 0 : _this$_containerRef$c.clientWidth) || 0;
5696
- this.setState({
5697
- containerWidth
5698
- });
5699
- });
5700
- _defineProperty(this, "_throttleResizeHandler", () => {
5701
- if (this._throttleResize) {
5702
- return;
5703
- }
5704
- this._throttleResize = true;
5705
- setTimeout(() => {
5706
- this._resize();
5707
- this._throttleResize = false;
5708
- }, 100);
5709
- });
5710
- _defineProperty(this, "activate", () => {
5711
- this.props.setKeypadActive(true);
5712
- });
5713
- _defineProperty(this, "dismiss", () => {
5714
- var _this$props$onDismiss, _this$props;
5715
- this.props.setKeypadActive(false);
5716
- (_this$props$onDismiss = (_this$props = this.props).onDismiss) === null || _this$props$onDismiss === void 0 ? void 0 : _this$props$onDismiss.call(_this$props);
5717
- });
5718
- _defineProperty(this, "configure", (configuration, cb) => {
5719
- this.setState({
5720
- keypadConfig: configuration
5721
- });
5722
-
5723
- // TODO(matthewc)[LC-1080]: this was brought in from v1's ProvidedKeypad.
5724
- // We need to investigate whether we still need this.
5725
- // HACK(charlie): In Perseus, triggering a focus causes the keypad to
5726
- // animate into view and re-configure. We'd like to provide the option
5727
- // to re-render the re-configured keypad before animating it into view,
5728
- // to avoid jank in the animation. As such, we support passing a
5729
- // callback into `configureKeypad`. However, implementing this properly
5730
- // would require middleware, etc., so we just hack it on with
5731
- // `setTimeout` for now.
5732
- setTimeout(() => cb && cb());
5733
- });
5734
- _defineProperty(this, "setCursor", cursor => {
5735
- this.setState({
5736
- cursor
5737
- });
5738
- });
5739
- _defineProperty(this, "setKeyHandler", keyHandler => {
5740
- this.setState({
5741
- keyHandler
5742
- });
5743
- });
5744
- _defineProperty(this, "getDOMNode", () => {
5745
- return ReactDOM__default["default"].findDOMNode(this);
5746
- });
5747
- }
5714
+ _containerRef = /*#__PURE__*/React__namespace.createRef();
5715
+ _containerResizeObserver = null;
5716
+ _throttleResize = false;
5717
+ state = {
5718
+ containerWidth: 0
5719
+ };
5748
5720
  componentDidMount() {
5749
- var _this$props$onElement, _this$props2;
5750
5721
  this._resize();
5751
5722
  window.addEventListener("resize", this._throttleResizeHandler);
5752
5723
  window.addEventListener("orientationchange", this._throttleResizeHandler);
@@ -5759,7 +5730,7 @@ class MobileKeypad extends React__namespace.Component {
5759
5730
  this._containerResizeObserver.observe(this._containerRef.current);
5760
5731
  }
5761
5732
  }
5762
- (_this$props$onElement = (_this$props2 = this.props).onElementMounted) === null || _this$props$onElement === void 0 ? void 0 : _this$props$onElement.call(_this$props2, {
5733
+ this.props.onElementMounted?.({
5763
5734
  activate: this.activate,
5764
5735
  dismiss: this.dismiss,
5765
5736
  configure: this.configure,
@@ -5769,18 +5740,68 @@ class MobileKeypad extends React__namespace.Component {
5769
5740
  });
5770
5741
  }
5771
5742
  componentWillUnmount() {
5772
- var _this$_containerResiz;
5773
5743
  window.removeEventListener("resize", this._throttleResizeHandler);
5774
5744
  window.removeEventListener("orientationchange", this._throttleResizeHandler);
5775
- (_this$_containerResiz = this._containerResizeObserver) === null || _this$_containerResiz === void 0 ? void 0 : _this$_containerResiz.disconnect();
5745
+ this._containerResizeObserver?.disconnect();
5776
5746
  }
5747
+ _resize = () => {
5748
+ const containerWidth = this._containerRef.current?.clientWidth || 0;
5749
+ this.setState({
5750
+ containerWidth
5751
+ });
5752
+ };
5753
+ _throttleResizeHandler = () => {
5754
+ if (this._throttleResize) {
5755
+ return;
5756
+ }
5757
+ this._throttleResize = true;
5758
+ setTimeout(() => {
5759
+ this._resize();
5760
+ this._throttleResize = false;
5761
+ }, 100);
5762
+ };
5763
+ activate = () => {
5764
+ this.props.setKeypadActive(true);
5765
+ };
5766
+ dismiss = () => {
5767
+ this.props.setKeypadActive(false);
5768
+ this.props.onDismiss?.();
5769
+ };
5770
+ configure = (configuration, cb) => {
5771
+ this.setState({
5772
+ keypadConfig: configuration
5773
+ });
5774
+
5775
+ // TODO(matthewc)[LC-1080]: this was brought in from v1's ProvidedKeypad.
5776
+ // We need to investigate whether we still need this.
5777
+ // HACK(charlie): In Perseus, triggering a focus causes the keypad to
5778
+ // animate into view and re-configure. We'd like to provide the option
5779
+ // to re-render the re-configured keypad before animating it into view,
5780
+ // to avoid jank in the animation. As such, we support passing a
5781
+ // callback into `configureKeypad`. However, implementing this properly
5782
+ // would require middleware, etc., so we just hack it on with
5783
+ // `setTimeout` for now.
5784
+ setTimeout(() => cb && cb());
5785
+ };
5786
+ setCursor = cursor => {
5787
+ this.setState({
5788
+ cursor
5789
+ });
5790
+ };
5791
+ setKeyHandler = keyHandler => {
5792
+ this.setState({
5793
+ keyHandler
5794
+ });
5795
+ };
5796
+ getDOMNode = () => {
5797
+ return ReactDOM__default["default"].findDOMNode(this);
5798
+ };
5777
5799
  _handleClickKey(key) {
5778
- var _this$state$keyHandle, _this$state;
5779
5800
  if (key === "DISMISS") {
5780
5801
  this.dismiss();
5781
5802
  return;
5782
5803
  }
5783
- 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);
5804
+ const cursor = this.state.keyHandler?.(key);
5784
5805
  this.setState({
5785
5806
  cursor
5786
5807
  });
@@ -5798,35 +5819,35 @@ class MobileKeypad extends React__namespace.Component {
5798
5819
  const containerStyle = [styles$c.keypadContainer,
5799
5820
  // styles passed as props
5800
5821
  ...(Array.isArray(style) ? style : [style])];
5801
- const isExpression = (keypadConfig === null || keypadConfig === void 0 ? void 0 : keypadConfig.keypadType) === "EXPRESSION";
5802
- const convertDotToTimes = keypadConfig === null || keypadConfig === void 0 ? void 0 : keypadConfig.times;
5803
- return /*#__PURE__*/React__namespace.createElement(AphroditeCSSTransitionGroup, {
5822
+ const isExpression = keypadConfig?.keypadType === "EXPRESSION";
5823
+ const convertDotToTimes = keypadConfig?.times;
5824
+ return /*#__PURE__*/React__namespace.createElement(View, {
5825
+ style: containerStyle,
5826
+ forwardRef: this._containerRef
5827
+ }, /*#__PURE__*/React__namespace.createElement(AphroditeCSSTransitionGroup, {
5804
5828
  transitionEnterTimeout: AnimationDurationInMS,
5805
5829
  transitionLeaveTimeout: AnimationDurationInMS,
5806
5830
  transitionStyle: {
5807
5831
  enter: {
5808
5832
  transform: "translate3d(0, 100%, 0)",
5809
- transition: "".concat(AnimationDurationInMS, "ms ease-out")
5833
+ transition: `${AnimationDurationInMS}ms ease-out`
5810
5834
  },
5811
5835
  enterActive: {
5812
5836
  transform: "translate3d(0, 0, 0)"
5813
5837
  },
5814
5838
  leave: {
5815
5839
  transform: "translate3d(0, 0, 0)",
5816
- transition: "".concat(AnimationDurationInMS, "ms ease-out")
5840
+ transition: `${AnimationDurationInMS}ms ease-out`
5817
5841
  },
5818
5842
  leaveActive: {
5819
5843
  transform: "translate3d(0, 100%, 0)"
5820
5844
  }
5821
5845
  }
5822
- }, keypadActive ? /*#__PURE__*/React__namespace.createElement(View, {
5823
- style: containerStyle,
5824
- forwardRef: this._containerRef
5825
- }, /*#__PURE__*/React__namespace.createElement(Keypad$2, {
5846
+ }, keypadActive ? /*#__PURE__*/React__namespace.createElement(Keypad$2, {
5826
5847
  onAnalyticsEvent: this.props.onAnalyticsEvent,
5827
- extraKeys: keypadConfig === null || keypadConfig === void 0 ? void 0 : keypadConfig.extraKeys,
5848
+ extraKeys: keypadConfig?.extraKeys,
5828
5849
  onClickKey: key => this._handleClickKey(key),
5829
- cursorContext: cursor === null || cursor === void 0 ? void 0 : cursor.context,
5850
+ cursorContext: cursor?.context,
5830
5851
  fractionsOnly: !isExpression,
5831
5852
  convertDotToTimes: convertDotToTimes,
5832
5853
  divisionKey: isExpression,
@@ -5837,7 +5858,7 @@ class MobileKeypad extends React__namespace.Component {
5837
5858
  advancedRelations: isExpression,
5838
5859
  expandedView: containerWidth > expandedViewThreshold$1,
5839
5860
  showDismiss: true
5840
- })) : null);
5861
+ }) : null));
5841
5862
  }
5842
5863
  }
5843
5864
  const styles$c = aphrodite.StyleSheet.create({
@@ -5960,20 +5981,14 @@ var Styles = aphrodite.StyleSheet.create({
5960
5981
  }
5961
5982
  });
5962
5983
 
5984
+ /**
5985
+ * A component that renders an icon with math (via KaTeX).
5986
+ */
5963
5987
  const {
5964
5988
  row: row$7,
5965
5989
  centered: centered$4
5966
5990
  } = Styles;
5967
5991
  class MathIcon extends React__namespace.Component {
5968
- constructor() {
5969
- super(...arguments);
5970
- _defineProperty(this, "_renderMath", () => {
5971
- const {
5972
- math
5973
- } = this.props;
5974
- katex__default["default"].render(math, ReactDOM__default["default"].findDOMNode(this));
5975
- });
5976
- }
5977
5992
  componentDidMount() {
5978
5993
  this._renderMath();
5979
5994
  }
@@ -5982,6 +5997,12 @@ class MathIcon extends React__namespace.Component {
5982
5997
  this._renderMath();
5983
5998
  }
5984
5999
  }
6000
+ _renderMath = () => {
6001
+ const {
6002
+ math
6003
+ } = this.props;
6004
+ katex__default["default"].render(math, ReactDOM__default["default"].findDOMNode(this));
6005
+ };
5985
6006
  render() {
5986
6007
  const {
5987
6008
  style
@@ -6002,7 +6023,15 @@ const styles$a = aphrodite.StyleSheet.create({
6002
6023
  }
6003
6024
  });
6004
6025
 
6026
+ /**
6027
+ * An autogenerated component that renders the COS iconograpy in SVG.
6028
+ *
6029
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6030
+ */
6005
6031
  class Cos extends React__namespace.Component {
6032
+ static propTypes = {
6033
+ color: PropTypes__default["default"].string.isRequired
6034
+ };
6006
6035
  render() {
6007
6036
  return /*#__PURE__*/React__namespace.createElement("svg", {
6008
6037
  width: "48",
@@ -6023,11 +6052,16 @@ class Cos extends React__namespace.Component {
6023
6052
  })));
6024
6053
  }
6025
6054
  }
6026
- _defineProperty(Cos, "propTypes", {
6027
- color: PropTypes__default["default"].string.isRequired
6028
- });
6029
6055
 
6056
+ /**
6057
+ * An autogenerated component that renders the LOG iconograpy in SVG.
6058
+ *
6059
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6060
+ */
6030
6061
  class Log extends React__namespace.Component {
6062
+ static propTypes = {
6063
+ color: PropTypes__default["default"].string.isRequired
6064
+ };
6031
6065
  render() {
6032
6066
  return /*#__PURE__*/React__namespace.createElement("svg", {
6033
6067
  width: "48",
@@ -6045,11 +6079,16 @@ class Log extends React__namespace.Component {
6045
6079
  })));
6046
6080
  }
6047
6081
  }
6048
- _defineProperty(Log, "propTypes", {
6049
- color: PropTypes__default["default"].string.isRequired
6050
- });
6051
6082
 
6083
+ /**
6084
+ * An autogenerated component that renders the EQUAL iconograpy in SVG.
6085
+ *
6086
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6087
+ */
6052
6088
  class Equal extends React__namespace.Component {
6089
+ static propTypes = {
6090
+ color: PropTypes__default["default"].string.isRequired
6091
+ };
6053
6092
  render() {
6054
6093
  return /*#__PURE__*/React__namespace.createElement("svg", {
6055
6094
  width: "48",
@@ -6073,9 +6112,6 @@ class Equal extends React__namespace.Component {
6073
6112
  })));
6074
6113
  }
6075
6114
  }
6076
- _defineProperty(Equal, "propTypes", {
6077
- color: PropTypes__default["default"].string.isRequired
6078
- });
6079
6115
 
6080
6116
  /**
6081
6117
  * An autogenerated component that renders the BACKSPACE iconograpy in SVG.
@@ -6105,7 +6141,15 @@ const Backspace = () => {
6105
6141
  })));
6106
6142
  };
6107
6143
 
6144
+ /**
6145
+ * An autogenerated component that renders the SQRT iconograpy in SVG.
6146
+ *
6147
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6148
+ */
6108
6149
  class Sqrt extends React__namespace.Component {
6150
+ static propTypes = {
6151
+ color: PropTypes__default["default"].string.isRequired
6152
+ };
6109
6153
  render() {
6110
6154
  return /*#__PURE__*/React__namespace.createElement("svg", {
6111
6155
  width: "48",
@@ -6126,11 +6170,16 @@ class Sqrt extends React__namespace.Component {
6126
6170
  })));
6127
6171
  }
6128
6172
  }
6129
- _defineProperty(Sqrt, "propTypes", {
6130
- color: PropTypes__default["default"].string.isRequired
6131
- });
6132
6173
 
6174
+ /**
6175
+ * An autogenerated component that renders the EXP iconograpy in SVG.
6176
+ *
6177
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6178
+ */
6133
6179
  class Exp extends React__namespace.Component {
6180
+ static propTypes = {
6181
+ color: PropTypes__default["default"].string.isRequired
6182
+ };
6134
6183
  render() {
6135
6184
  return /*#__PURE__*/React__namespace.createElement("svg", {
6136
6185
  width: "48",
@@ -6148,11 +6197,16 @@ class Exp extends React__namespace.Component {
6148
6197
  })));
6149
6198
  }
6150
6199
  }
6151
- _defineProperty(Exp, "propTypes", {
6152
- color: PropTypes__default["default"].string.isRequired
6153
- });
6154
6200
 
6201
+ /**
6202
+ * An autogenerated component that renders the NEQ iconograpy in SVG.
6203
+ *
6204
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6205
+ */
6155
6206
  class Neq extends React__namespace.Component {
6207
+ static propTypes = {
6208
+ color: PropTypes__default["default"].string.isRequired
6209
+ };
6156
6210
  render() {
6157
6211
  return /*#__PURE__*/React__namespace.createElement("svg", {
6158
6212
  width: "48",
@@ -6176,11 +6230,16 @@ class Neq extends React__namespace.Component {
6176
6230
  })));
6177
6231
  }
6178
6232
  }
6179
- _defineProperty(Neq, "propTypes", {
6180
- color: PropTypes__default["default"].string.isRequired
6181
- });
6182
6233
 
6234
+ /**
6235
+ * An autogenerated component that renders the GEQ iconograpy in SVG.
6236
+ *
6237
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6238
+ */
6183
6239
  class Geq extends React__namespace.Component {
6240
+ static propTypes = {
6241
+ color: PropTypes__default["default"].string.isRequired
6242
+ };
6184
6243
  render() {
6185
6244
  return /*#__PURE__*/React__namespace.createElement("svg", {
6186
6245
  width: "48",
@@ -6204,11 +6263,16 @@ class Geq extends React__namespace.Component {
6204
6263
  })));
6205
6264
  }
6206
6265
  }
6207
- _defineProperty(Geq, "propTypes", {
6208
- color: PropTypes__default["default"].string.isRequired
6209
- });
6210
6266
 
6267
+ /**
6268
+ * An autogenerated component that renders the LN iconograpy in SVG.
6269
+ *
6270
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6271
+ */
6211
6272
  class Ln extends React__namespace.Component {
6273
+ static propTypes = {
6274
+ color: PropTypes__default["default"].string.isRequired
6275
+ };
6212
6276
  render() {
6213
6277
  return /*#__PURE__*/React__namespace.createElement("svg", {
6214
6278
  width: "48",
@@ -6226,9 +6290,6 @@ class Ln extends React__namespace.Component {
6226
6290
  })));
6227
6291
  }
6228
6292
  }
6229
- _defineProperty(Ln, "propTypes", {
6230
- color: PropTypes__default["default"].string.isRequired
6231
- });
6232
6293
 
6233
6294
  /**
6234
6295
  * An autogenerated component that renders the DISMISS iconograpy in SVG.
@@ -6255,7 +6316,15 @@ const Dismiss = () => {
6255
6316
  })));
6256
6317
  };
6257
6318
 
6319
+ /**
6320
+ * An autogenerated component that renders the SIN iconograpy in SVG.
6321
+ *
6322
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6323
+ */
6258
6324
  class Sin extends React__namespace.Component {
6325
+ static propTypes = {
6326
+ color: PropTypes__default["default"].string.isRequired
6327
+ };
6259
6328
  render() {
6260
6329
  return /*#__PURE__*/React__namespace.createElement("svg", {
6261
6330
  width: "48",
@@ -6276,11 +6345,16 @@ class Sin extends React__namespace.Component {
6276
6345
  })));
6277
6346
  }
6278
6347
  }
6279
- _defineProperty(Sin, "propTypes", {
6280
- color: PropTypes__default["default"].string.isRequired
6281
- });
6282
6348
 
6349
+ /**
6350
+ * An autogenerated component that renders the LT iconograpy in SVG.
6351
+ *
6352
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6353
+ */
6283
6354
  class Lt extends React__namespace.Component {
6355
+ static propTypes = {
6356
+ color: PropTypes__default["default"].string.isRequired
6357
+ };
6284
6358
  render() {
6285
6359
  return /*#__PURE__*/React__namespace.createElement("svg", {
6286
6360
  width: "48",
@@ -6304,11 +6378,16 @@ class Lt extends React__namespace.Component {
6304
6378
  })));
6305
6379
  }
6306
6380
  }
6307
- _defineProperty(Lt, "propTypes", {
6308
- color: PropTypes__default["default"].string.isRequired
6309
- });
6310
6381
 
6382
+ /**
6383
+ * An autogenerated component that renders the CUBE_ROOT iconograpy in SVG.
6384
+ *
6385
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6386
+ */
6311
6387
  class CubeRoot extends React__namespace.Component {
6388
+ static propTypes = {
6389
+ color: PropTypes__default["default"].string.isRequired
6390
+ };
6312
6391
  render() {
6313
6392
  return /*#__PURE__*/React__namespace.createElement("svg", {
6314
6393
  width: "48",
@@ -6332,11 +6411,16 @@ class CubeRoot extends React__namespace.Component {
6332
6411
  })));
6333
6412
  }
6334
6413
  }
6335
- _defineProperty(CubeRoot, "propTypes", {
6336
- color: PropTypes__default["default"].string.isRequired
6337
- });
6338
6414
 
6415
+ /**
6416
+ * An autogenerated component that renders the PLUS iconograpy in SVG.
6417
+ *
6418
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6419
+ */
6339
6420
  class Plus extends React__namespace.Component {
6421
+ static propTypes = {
6422
+ color: PropTypes__default["default"].string.isRequired
6423
+ };
6340
6424
  render() {
6341
6425
  return /*#__PURE__*/React__namespace.createElement("svg", {
6342
6426
  width: "48",
@@ -6357,11 +6441,16 @@ class Plus extends React__namespace.Component {
6357
6441
  })));
6358
6442
  }
6359
6443
  }
6360
- _defineProperty(Plus, "propTypes", {
6361
- color: PropTypes__default["default"].string.isRequired
6362
- });
6363
6444
 
6445
+ /**
6446
+ * An autogenerated component that renders the TAN iconograpy in SVG.
6447
+ *
6448
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6449
+ */
6364
6450
  class Tan extends React__namespace.Component {
6451
+ static propTypes = {
6452
+ color: PropTypes__default["default"].string.isRequired
6453
+ };
6365
6454
  render() {
6366
6455
  return /*#__PURE__*/React__namespace.createElement("svg", {
6367
6456
  width: "48",
@@ -6382,9 +6471,6 @@ class Tan extends React__namespace.Component {
6382
6471
  })));
6383
6472
  }
6384
6473
  }
6385
- _defineProperty(Tan, "propTypes", {
6386
- color: PropTypes__default["default"].string.isRequired
6387
- });
6388
6474
 
6389
6475
  const Arrow = props => {
6390
6476
  return /*#__PURE__*/React__namespace.createElement("g", _extends({
@@ -6442,7 +6528,15 @@ const Down = () => {
6442
6528
  }));
6443
6529
  };
6444
6530
 
6531
+ /**
6532
+ * An autogenerated component that renders the LEFT_PAREN iconograpy in SVG.
6533
+ *
6534
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6535
+ */
6445
6536
  class LeftParen extends React__namespace.Component {
6537
+ static propTypes = {
6538
+ color: PropTypes__default["default"].string.isRequired
6539
+ };
6446
6540
  render() {
6447
6541
  return /*#__PURE__*/React__namespace.createElement("svg", {
6448
6542
  width: "48",
@@ -6466,11 +6560,16 @@ class LeftParen extends React__namespace.Component {
6466
6560
  })));
6467
6561
  }
6468
6562
  }
6469
- _defineProperty(LeftParen, "propTypes", {
6470
- color: PropTypes__default["default"].string.isRequired
6471
- });
6472
6563
 
6564
+ /**
6565
+ * An autogenerated component that renders the RIGHT_PAREN iconograpy in SVG.
6566
+ *
6567
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6568
+ */
6473
6569
  class RightParen extends React__namespace.Component {
6570
+ static propTypes = {
6571
+ color: PropTypes__default["default"].string.isRequired
6572
+ };
6474
6573
  render() {
6475
6574
  return /*#__PURE__*/React__namespace.createElement("svg", {
6476
6575
  width: "48",
@@ -6494,11 +6593,16 @@ class RightParen extends React__namespace.Component {
6494
6593
  })));
6495
6594
  }
6496
6595
  }
6497
- _defineProperty(RightParen, "propTypes", {
6498
- color: PropTypes__default["default"].string.isRequired
6499
- });
6500
6596
 
6597
+ /**
6598
+ * An autogenerated component that renders the GT iconograpy in SVG.
6599
+ *
6600
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6601
+ */
6501
6602
  class Gt extends React__namespace.Component {
6603
+ static propTypes = {
6604
+ color: PropTypes__default["default"].string.isRequired
6605
+ };
6502
6606
  render() {
6503
6607
  return /*#__PURE__*/React__namespace.createElement("svg", {
6504
6608
  width: "48",
@@ -6522,11 +6626,16 @@ class Gt extends React__namespace.Component {
6522
6626
  })));
6523
6627
  }
6524
6628
  }
6525
- _defineProperty(Gt, "propTypes", {
6526
- color: PropTypes__default["default"].string.isRequired
6527
- });
6528
6629
 
6630
+ /**
6631
+ * An autogenerated component that renders the DIVIDE iconograpy in SVG.
6632
+ *
6633
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6634
+ */
6529
6635
  class Divide extends React__namespace.Component {
6636
+ static propTypes = {
6637
+ color: PropTypes__default["default"].string.isRequired
6638
+ };
6530
6639
  render() {
6531
6640
  return /*#__PURE__*/React__namespace.createElement("svg", {
6532
6641
  width: "48",
@@ -6557,11 +6666,16 @@ class Divide extends React__namespace.Component {
6557
6666
  })));
6558
6667
  }
6559
6668
  }
6560
- _defineProperty(Divide, "propTypes", {
6561
- color: PropTypes__default["default"].string.isRequired
6562
- });
6563
6669
 
6670
+ /**
6671
+ * An autogenerated component that renders the PERIOD iconograpy in SVG.
6672
+ *
6673
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6674
+ */
6564
6675
  class Period extends React__namespace.Component {
6676
+ static propTypes = {
6677
+ color: PropTypes__default["default"].string.isRequired
6678
+ };
6565
6679
  render() {
6566
6680
  return /*#__PURE__*/React__namespace.createElement("svg", {
6567
6681
  width: "48",
@@ -6581,11 +6695,16 @@ class Period extends React__namespace.Component {
6581
6695
  })));
6582
6696
  }
6583
6697
  }
6584
- _defineProperty(Period, "propTypes", {
6585
- color: PropTypes__default["default"].string.isRequired
6586
- });
6587
6698
 
6699
+ /**
6700
+ * An autogenerated component that renders the PERCENT iconograpy in SVG.
6701
+ *
6702
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6703
+ */
6588
6704
  class Percent extends React__namespace.Component {
6705
+ static propTypes = {
6706
+ color: PropTypes__default["default"].string.isRequired
6707
+ };
6589
6708
  render() {
6590
6709
  return /*#__PURE__*/React__namespace.createElement("svg", {
6591
6710
  width: "48",
@@ -6623,11 +6742,16 @@ class Percent extends React__namespace.Component {
6623
6742
  }))));
6624
6743
  }
6625
6744
  }
6626
- _defineProperty(Percent, "propTypes", {
6627
- color: PropTypes__default["default"].string.isRequired
6628
- });
6629
6745
 
6746
+ /**
6747
+ * An autogenerated component that renders the TIMES iconograpy in SVG.
6748
+ *
6749
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6750
+ */
6630
6751
  class Times extends React__namespace.Component {
6752
+ static propTypes = {
6753
+ color: PropTypes__default["default"].string.isRequired
6754
+ };
6631
6755
  render() {
6632
6756
  return /*#__PURE__*/React__namespace.createElement("svg", {
6633
6757
  width: "48",
@@ -6651,11 +6775,16 @@ class Times extends React__namespace.Component {
6651
6775
  })));
6652
6776
  }
6653
6777
  }
6654
- _defineProperty(Times, "propTypes", {
6655
- color: PropTypes__default["default"].string.isRequired
6656
- });
6657
6778
 
6779
+ /**
6780
+ * An autogenerated component that renders the EXP_3 iconograpy in SVG.
6781
+ *
6782
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6783
+ */
6658
6784
  class Exp3 extends React__namespace.Component {
6785
+ static propTypes = {
6786
+ color: PropTypes__default["default"].string.isRequired
6787
+ };
6659
6788
  render() {
6660
6789
  return /*#__PURE__*/React__namespace.createElement("svg", {
6661
6790
  width: "48",
@@ -6673,11 +6802,16 @@ class Exp3 extends React__namespace.Component {
6673
6802
  })));
6674
6803
  }
6675
6804
  }
6676
- _defineProperty(Exp3, "propTypes", {
6677
- color: PropTypes__default["default"].string.isRequired
6678
- });
6679
6805
 
6806
+ /**
6807
+ * An autogenerated component that renders the EXP_2 iconograpy in SVG.
6808
+ *
6809
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6810
+ */
6680
6811
  class Exp2 extends React__namespace.Component {
6812
+ static propTypes = {
6813
+ color: PropTypes__default["default"].string.isRequired
6814
+ };
6681
6815
  render() {
6682
6816
  return /*#__PURE__*/React__namespace.createElement("svg", {
6683
6817
  width: "48",
@@ -6695,9 +6829,6 @@ class Exp2 extends React__namespace.Component {
6695
6829
  })));
6696
6830
  }
6697
6831
  }
6698
- _defineProperty(Exp2, "propTypes", {
6699
- color: PropTypes__default["default"].string.isRequired
6700
- });
6701
6832
 
6702
6833
  /**
6703
6834
  * A component that renders the RIGHT iconograpy in SVG.
@@ -6712,7 +6843,15 @@ const Right = () => {
6712
6843
  }));
6713
6844
  };
6714
6845
 
6846
+ /**
6847
+ * An autogenerated component that renders the CDOT iconograpy in SVG.
6848
+ *
6849
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6850
+ */
6715
6851
  class Cdot extends React__namespace.Component {
6852
+ static propTypes = {
6853
+ color: PropTypes__default["default"].string.isRequired
6854
+ };
6716
6855
  render() {
6717
6856
  return /*#__PURE__*/React__namespace.createElement("svg", {
6718
6857
  width: "48",
@@ -6737,11 +6876,16 @@ class Cdot extends React__namespace.Component {
6737
6876
  }))));
6738
6877
  }
6739
6878
  }
6740
- _defineProperty(Cdot, "propTypes", {
6741
- color: PropTypes__default["default"].string.isRequired
6742
- });
6743
6879
 
6880
+ /**
6881
+ * An autogenerated component that renders the LOG_N iconograpy in SVG.
6882
+ *
6883
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6884
+ */
6744
6885
  class LogN extends React__namespace.Component {
6886
+ static propTypes = {
6887
+ color: PropTypes__default["default"].string.isRequired
6888
+ };
6745
6889
  render() {
6746
6890
  return /*#__PURE__*/React__namespace.createElement("svg", {
6747
6891
  width: "48",
@@ -6759,11 +6903,16 @@ class LogN extends React__namespace.Component {
6759
6903
  })));
6760
6904
  }
6761
6905
  }
6762
- _defineProperty(LogN, "propTypes", {
6763
- color: PropTypes__default["default"].string.isRequired
6764
- });
6765
6906
 
6907
+ /**
6908
+ * An autogenerated component that renders the LEQ iconograpy in SVG.
6909
+ *
6910
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6911
+ */
6766
6912
  class Leq extends React__namespace.Component {
6913
+ static propTypes = {
6914
+ color: PropTypes__default["default"].string.isRequired
6915
+ };
6767
6916
  render() {
6768
6917
  return /*#__PURE__*/React__namespace.createElement("svg", {
6769
6918
  width: "48",
@@ -6787,11 +6936,16 @@ class Leq extends React__namespace.Component {
6787
6936
  })));
6788
6937
  }
6789
6938
  }
6790
- _defineProperty(Leq, "propTypes", {
6791
- color: PropTypes__default["default"].string.isRequired
6792
- });
6793
6939
 
6940
+ /**
6941
+ * An autogenerated component that renders the MINUS iconograpy in SVG.
6942
+ *
6943
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6944
+ */
6794
6945
  class Minus extends React__namespace.Component {
6946
+ static propTypes = {
6947
+ color: PropTypes__default["default"].string.isRequired
6948
+ };
6795
6949
  render() {
6796
6950
  return /*#__PURE__*/React__namespace.createElement("svg", {
6797
6951
  width: "48",
@@ -6812,11 +6966,16 @@ class Minus extends React__namespace.Component {
6812
6966
  })));
6813
6967
  }
6814
6968
  }
6815
- _defineProperty(Minus, "propTypes", {
6816
- color: PropTypes__default["default"].string.isRequired
6817
- });
6818
6969
 
6970
+ /**
6971
+ * An autogenerated component that renders the RADICAL iconograpy in SVG.
6972
+ *
6973
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
6974
+ */
6819
6975
  class Radical extends React__namespace.Component {
6976
+ static propTypes = {
6977
+ color: PropTypes__default["default"].string.isRequired
6978
+ };
6820
6979
  render() {
6821
6980
  return /*#__PURE__*/React__namespace.createElement("svg", {
6822
6981
  width: "48",
@@ -6840,11 +6999,16 @@ class Radical extends React__namespace.Component {
6840
6999
  })));
6841
7000
  }
6842
7001
  }
6843
- _defineProperty(Radical, "propTypes", {
6844
- color: PropTypes__default["default"].string.isRequired
6845
- });
6846
7002
 
7003
+ /**
7004
+ * An autogenerated component that renders the FRAC iconograpy in SVG.
7005
+ *
7006
+ * Generated with: https://gist.github.com/crm416/3c7abc88e520eaed72347af240b32590.
7007
+ */
6847
7008
  class FracInclusive extends React__namespace.Component {
7009
+ static propTypes = {
7010
+ color: PropTypes__default["default"].string.isRequired
7011
+ };
6848
7012
  render() {
6849
7013
  return /*#__PURE__*/React__namespace.createElement("svg", {
6850
7014
  width: "48",
@@ -6877,9 +7041,6 @@ class FracInclusive extends React__namespace.Component {
6877
7041
  }))));
6878
7042
  }
6879
7043
  }
6880
- _defineProperty(FracInclusive, "propTypes", {
6881
- color: PropTypes__default["default"].string.isRequired
6882
- });
6883
7044
 
6884
7045
  /**
6885
7046
  * An autogenerated component that renders the JUMP_OUT_PARENTHESES iconograpy in SVG.
@@ -7263,7 +7424,7 @@ class MultiSymbolGrid extends React__namespace.Component {
7263
7424
  // some styles coercion and doesn't seem worthwhile right now.
7264
7425
  icons.forEach(icon => {
7265
7426
  if (icon.type !== IconType.MATH) {
7266
- throw new Error("Received invalid icon: type=".concat(icon.type, ", ") + "data=".concat(icon.data));
7427
+ throw new Error(`Received invalid icon: type=${icon.type}, ` + `data=${icon.data}`);
7267
7428
  }
7268
7429
  });
7269
7430
  if (icons.length === 1) {
@@ -7324,7 +7485,7 @@ class MultiSymbolGrid extends React__namespace.Component {
7324
7485
  }))));
7325
7486
  }
7326
7487
  }
7327
- throw new Error("Invalid number of icons: ".concat(icons.length));
7488
+ throw new Error(`Invalid number of icons: ${icons.length}`);
7328
7489
  }
7329
7490
  }
7330
7491
  const verticalInsetPx = 2;
@@ -7368,72 +7529,13 @@ const styles$7 = aphrodite.StyleSheet.create({
7368
7529
 
7369
7530
  // eslint-disable-next-line react/no-unsafe
7370
7531
  class KeypadButton extends React__namespace.PureComponent {
7371
- constructor() {
7372
- super(...arguments);
7373
- _defineProperty(this, "buttonSizeStyle", void 0);
7374
- _defineProperty(this, "_preInjectStyles", () => {
7375
- // HACK(charlie): Pre-inject all of the possible styles for the button.
7376
- // This avoids a flickering effect in the echo animation whereby the
7377
- // echoes vary in size as they animate. Note that we need to account for
7378
- // the "initial" styles that `View` will include, as these styles are
7379
- // applied to `View` components and Aphrodite will consolidate the style
7380
- // object. This method must be called whenever a property that
7381
- // influences the possible outcomes of `this._getFocusStyle` and
7382
- // `this._getButtonStyle` changes (such as `this.buttonSizeStyle`).
7383
- for (const type of KeyTypes) {
7384
- aphrodite.css(View.styles.initial, ...this._getFocusStyle(type));
7385
- for (const borders of Object.values(BorderStyles)) {
7386
- aphrodite.css(View.styles.initial, ...this._getButtonStyle(type, borders));
7387
- }
7388
- }
7389
- });
7390
- _defineProperty(this, "_getFocusStyle", type => {
7391
- let focusBackgroundStyle;
7392
- if (type === "INPUT_NAVIGATION" || type === "KEYPAD_NAVIGATION") {
7393
- focusBackgroundStyle = styles$6.light;
7394
- } else {
7395
- focusBackgroundStyle = styles$6.bright;
7396
- }
7397
- return [styles$6.focusBox, focusBackgroundStyle];
7398
- });
7399
- _defineProperty(this, "_getButtonStyle", (type, borders, style) => {
7400
- // Select the appropriate style for the button.
7401
- let backgroundStyle;
7402
- switch (type) {
7403
- case "EMPTY":
7404
- backgroundStyle = styles$6.empty;
7405
- break;
7406
- case "MANY":
7407
- case "VALUE":
7408
- backgroundStyle = styles$6.value;
7409
- break;
7410
- case "OPERATOR":
7411
- backgroundStyle = styles$6.operator;
7412
- break;
7413
- case "INPUT_NAVIGATION":
7414
- case "KEYPAD_NAVIGATION":
7415
- backgroundStyle = styles$6.control;
7416
- break;
7417
- case "ECHO":
7418
- backgroundStyle = null;
7419
- break;
7420
- }
7421
- const borderStyle = [];
7422
- if (borders.includes(BorderDirection.LEFT)) {
7423
- // @ts-expect-error TS2345
7424
- borderStyle.push(styles$6.leftBorder);
7425
- }
7426
- if (borders.includes(BorderDirection.BOTTOM)) {
7427
- // @ts-expect-error TS2345
7428
- borderStyle.push(styles$6.bottomBorder);
7429
- }
7430
- return [styles$6.buttonBase, backgroundStyle, ...borderStyle, type === "ECHO" && styles$6.echo, this.buttonSizeStyle,
7431
- // React Native allows you to set the 'style' props on user defined
7432
- // components.
7433
- // See: https://facebook.github.io/react-native/docs/style.html
7434
- ...(Array.isArray(style) ? style : [style])];
7435
- });
7436
- }
7532
+ static defaultProps = {
7533
+ borders: BorderStyles.ALL,
7534
+ childKeys: [],
7535
+ disabled: false,
7536
+ focused: false,
7537
+ popoverEnabled: false
7538
+ };
7437
7539
  UNSAFE_componentWillMount() {
7438
7540
  this.buttonSizeStyle = styleForButtonDimensions(this.props.heightPx, this.props.widthPx);
7439
7541
  }
@@ -7450,6 +7552,68 @@ class KeypadButton extends React__namespace.PureComponent {
7450
7552
  this._preInjectStyles();
7451
7553
  }
7452
7554
  }
7555
+ _preInjectStyles = () => {
7556
+ // HACK(charlie): Pre-inject all of the possible styles for the button.
7557
+ // This avoids a flickering effect in the echo animation whereby the
7558
+ // echoes vary in size as they animate. Note that we need to account for
7559
+ // the "initial" styles that `View` will include, as these styles are
7560
+ // applied to `View` components and Aphrodite will consolidate the style
7561
+ // object. This method must be called whenever a property that
7562
+ // influences the possible outcomes of `this._getFocusStyle` and
7563
+ // `this._getButtonStyle` changes (such as `this.buttonSizeStyle`).
7564
+ for (const type of KeyTypes) {
7565
+ aphrodite.css(View.styles.initial, ...this._getFocusStyle(type));
7566
+ for (const borders of Object.values(BorderStyles)) {
7567
+ aphrodite.css(View.styles.initial, ...this._getButtonStyle(type, borders));
7568
+ }
7569
+ }
7570
+ };
7571
+ _getFocusStyle = type => {
7572
+ let focusBackgroundStyle;
7573
+ if (type === "INPUT_NAVIGATION" || type === "KEYPAD_NAVIGATION") {
7574
+ focusBackgroundStyle = styles$6.light;
7575
+ } else {
7576
+ focusBackgroundStyle = styles$6.bright;
7577
+ }
7578
+ return [styles$6.focusBox, focusBackgroundStyle];
7579
+ };
7580
+ _getButtonStyle = (type, borders, style) => {
7581
+ // Select the appropriate style for the button.
7582
+ let backgroundStyle;
7583
+ switch (type) {
7584
+ case "EMPTY":
7585
+ backgroundStyle = styles$6.empty;
7586
+ break;
7587
+ case "MANY":
7588
+ case "VALUE":
7589
+ backgroundStyle = styles$6.value;
7590
+ break;
7591
+ case "OPERATOR":
7592
+ backgroundStyle = styles$6.operator;
7593
+ break;
7594
+ case "INPUT_NAVIGATION":
7595
+ case "KEYPAD_NAVIGATION":
7596
+ backgroundStyle = styles$6.control;
7597
+ break;
7598
+ case "ECHO":
7599
+ backgroundStyle = null;
7600
+ break;
7601
+ }
7602
+ const borderStyle = [];
7603
+ if (borders.includes(BorderDirection.LEFT)) {
7604
+ // @ts-expect-error TS2345
7605
+ borderStyle.push(styles$6.leftBorder);
7606
+ }
7607
+ if (borders.includes(BorderDirection.BOTTOM)) {
7608
+ // @ts-expect-error TS2345
7609
+ borderStyle.push(styles$6.bottomBorder);
7610
+ }
7611
+ return [styles$6.buttonBase, backgroundStyle, ...borderStyle, type === "ECHO" && styles$6.echo, this.buttonSizeStyle,
7612
+ // React Native allows you to set the 'style' props on user defined
7613
+ // components.
7614
+ // See: https://facebook.github.io/react-native/docs/style.html
7615
+ ...(Array.isArray(style) ? style : [style])];
7616
+ };
7453
7617
  render() {
7454
7618
  const {
7455
7619
  ariaLabel,
@@ -7523,13 +7687,6 @@ class KeypadButton extends React__namespace.PureComponent {
7523
7687
  }
7524
7688
  }
7525
7689
  }
7526
- _defineProperty(KeypadButton, "defaultProps", {
7527
- borders: BorderStyles.ALL,
7528
- childKeys: [],
7529
- disabled: false,
7530
- focused: false,
7531
- popoverEnabled: false
7532
- });
7533
7690
  const focusInsetPx = 4;
7534
7691
  const focusBoxZIndex = 0;
7535
7692
  const styles$6 = aphrodite.StyleSheet.create({
@@ -7707,7 +7864,6 @@ const extractProps = keyConfig => {
7707
7864
  };
7708
7865
  };
7709
7866
  const mapStateToProps$5 = (state, ownProps) => {
7710
- var _gestures$popover;
7711
7867
  const {
7712
7868
  gestures
7713
7869
  } = state;
@@ -7732,7 +7888,7 @@ const mapStateToProps$5 = (state, ownProps) => {
7732
7888
  id: id,
7733
7889
  // Add in some gesture state.
7734
7890
  focused: gestures.focus === id,
7735
- popoverEnabled: ((_gestures$popover = gestures.popover) === null || _gestures$popover === void 0 ? void 0 : _gestures$popover.parentId) === id,
7891
+ popoverEnabled: gestures.popover?.parentId === id,
7736
7892
  // Pass down the child keys and any extracted props.
7737
7893
  childKeys,
7738
7894
  ...extractProps(useFirstChildProps ? childKeys[0] : keyConfig)
@@ -7750,6 +7906,9 @@ var TouchableKeypadButton$1 = reactRedux.connect(mapStateToProps$5, null, null,
7750
7906
  })(TouchableKeypadButton);
7751
7907
 
7752
7908
  class ManyKeypadButton extends React__namespace.Component {
7909
+ static defaultProps = {
7910
+ keys: []
7911
+ };
7753
7912
  render() {
7754
7913
  const {
7755
7914
  keys,
@@ -7783,9 +7942,6 @@ class ManyKeypadButton extends React__namespace.Component {
7783
7942
  }
7784
7943
  }
7785
7944
  }
7786
- _defineProperty(ManyKeypadButton, "defaultProps", {
7787
- keys: []
7788
- });
7789
7945
 
7790
7946
  /**
7791
7947
  * This file contains all of the z-index values used throughout the math-input
@@ -7838,35 +7994,32 @@ class Echo extends React__namespace.Component {
7838
7994
  }
7839
7995
  }
7840
7996
  class EchoManager extends React__namespace.Component {
7841
- constructor() {
7842
- super(...arguments);
7843
- _defineProperty(this, "_animationConfigForType", animationType => {
7844
- // NOTE(charlie): These must be kept in sync with the transition
7845
- // durations and classnames specified in echo.css.
7846
- let animationDurationMs;
7847
- let animationTransitionName;
7848
- switch (animationType) {
7849
- case EchoAnimationType.SLIDE_AND_FADE:
7850
- animationDurationMs = 400;
7851
- animationTransitionName = "echo-slide-and-fade";
7852
- break;
7853
- case EchoAnimationType.FADE_ONLY:
7854
- animationDurationMs = 300;
7855
- animationTransitionName = "echo-fade-only";
7856
- break;
7857
- case EchoAnimationType.LONG_FADE_ONLY:
7858
- animationDurationMs = 400;
7859
- animationTransitionName = "echo-long-fade-only";
7860
- break;
7861
- default:
7862
- throw new Error("Invalid echo animation type: ".concat(animationType));
7863
- }
7864
- return {
7865
- animationDurationMs,
7866
- animationTransitionName
7867
- };
7868
- });
7869
- }
7997
+ _animationConfigForType = animationType => {
7998
+ // NOTE(charlie): These must be kept in sync with the transition
7999
+ // durations and classnames specified in echo.css.
8000
+ let animationDurationMs;
8001
+ let animationTransitionName;
8002
+ switch (animationType) {
8003
+ case EchoAnimationType.SLIDE_AND_FADE:
8004
+ animationDurationMs = 400;
8005
+ animationTransitionName = "echo-slide-and-fade";
8006
+ break;
8007
+ case EchoAnimationType.FADE_ONLY:
8008
+ animationDurationMs = 300;
8009
+ animationTransitionName = "echo-fade-only";
8010
+ break;
8011
+ case EchoAnimationType.LONG_FADE_ONLY:
8012
+ animationDurationMs = 400;
8013
+ animationTransitionName = "echo-long-fade-only";
8014
+ break;
8015
+ default:
8016
+ throw new Error(`Invalid echo animation type: ${animationType}`);
8017
+ }
8018
+ return {
8019
+ animationDurationMs,
8020
+ animationTransitionName
8021
+ };
8022
+ };
7870
8023
  render() {
7871
8024
  const {
7872
8025
  echoes,
@@ -7904,7 +8057,7 @@ class EchoManager extends React__namespace.Component {
7904
8057
  key: animationId
7905
8058
  }, /*#__PURE__*/React__namespace.createElement(Echo, _extends({
7906
8059
  animationDurationMs: animationDurationMs,
7907
- onAnimationFinish: () => onAnimationFinish === null || onAnimationFinish === void 0 ? void 0 : onAnimationFinish(animationId)
8060
+ onAnimationFinish: () => onAnimationFinish?.(animationId)
7908
8061
  }, echo)));
7909
8062
  }));
7910
8063
  }));
@@ -7999,7 +8152,6 @@ class PopoverManager extends React__namespace.Component {
7999
8152
  // naming convention: verb + noun
8000
8153
  // the noun should be one of the other properties in the object that's
8001
8154
  // being dispatched
8002
-
8003
8155
  const dismissKeypad = () => {
8004
8156
  return {
8005
8157
  type: "DismissKeypad"
@@ -8069,43 +8221,12 @@ const pressKey = (key, initialBounds, inPopover) => {
8069
8221
  };
8070
8222
  };
8071
8223
 
8224
+ /**
8225
+ * A keypad component that acts as a container for rows or columns of buttons,
8226
+ * and manages the rendering of echo animations on top of those buttons.
8227
+ */
8072
8228
  // eslint-disable-next-line react/no-unsafe
8073
8229
  class Keypad extends React__namespace.Component {
8074
- constructor() {
8075
- super(...arguments);
8076
- _defineProperty(this, "_isMounted", void 0);
8077
- _defineProperty(this, "_resizeTimeout", void 0);
8078
- _defineProperty(this, "_container", void 0);
8079
- _defineProperty(this, "_computeContainer", () => {
8080
- const domNode = ReactDOM__default["default"].findDOMNode(this);
8081
- this._container = domNode.getBoundingClientRect();
8082
- });
8083
- _defineProperty(this, "_updateSizeAndPosition", () => {
8084
- // Mark the container for recalculation next time the keypad is
8085
- // opened.
8086
- // TODO(charlie): Since we're not recalculating the container
8087
- // immediately, if you were to resize the page while a popover were
8088
- // active, you'd likely get unexpected behavior. This seems very
8089
- // difficult to do and, as such, incredibly unlikely, but we may
8090
- // want to reconsider the caching here.
8091
- this._container = null;
8092
- });
8093
- _defineProperty(this, "_onResize", () => {
8094
- // Whenever the page resizes, we need to recompute the container's
8095
- // bounding box. This is the only time that the bounding box can change.
8096
-
8097
- // Throttle resize events -- taken from:
8098
- // https://developer.mozilla.org/en-US/docs/Web/Events/resize
8099
- if (this._resizeTimeout == null) {
8100
- this._resizeTimeout = window.setTimeout(() => {
8101
- this._resizeTimeout = null;
8102
- if (this._isMounted) {
8103
- this._updateSizeAndPosition();
8104
- }
8105
- }, 66);
8106
- }
8107
- });
8108
- }
8109
8230
  componentDidMount() {
8110
8231
  this._isMounted = true;
8111
8232
  window.addEventListener("resize", this._onResize);
@@ -8120,6 +8241,35 @@ class Keypad extends React__namespace.Component {
8120
8241
  this._isMounted = false;
8121
8242
  window.removeEventListener("resize", this._onResize);
8122
8243
  }
8244
+ _computeContainer = () => {
8245
+ const domNode = ReactDOM__default["default"].findDOMNode(this);
8246
+ this._container = domNode.getBoundingClientRect();
8247
+ };
8248
+ _updateSizeAndPosition = () => {
8249
+ // Mark the container for recalculation next time the keypad is
8250
+ // opened.
8251
+ // TODO(charlie): Since we're not recalculating the container
8252
+ // immediately, if you were to resize the page while a popover were
8253
+ // active, you'd likely get unexpected behavior. This seems very
8254
+ // difficult to do and, as such, incredibly unlikely, but we may
8255
+ // want to reconsider the caching here.
8256
+ this._container = null;
8257
+ };
8258
+ _onResize = () => {
8259
+ // Whenever the page resizes, we need to recompute the container's
8260
+ // bounding box. This is the only time that the bounding box can change.
8261
+
8262
+ // Throttle resize events -- taken from:
8263
+ // https://developer.mozilla.org/en-US/docs/Web/Events/resize
8264
+ if (this._resizeTimeout == null) {
8265
+ this._resizeTimeout = window.setTimeout(() => {
8266
+ this._resizeTimeout = null;
8267
+ if (this._isMounted) {
8268
+ this._updateSizeAndPosition();
8269
+ }
8270
+ }, 66);
8271
+ }
8272
+ };
8123
8273
  render() {
8124
8274
  const {
8125
8275
  children,
@@ -8197,18 +8347,18 @@ var Keypad$1 = reactRedux.connect(mapStateToProps$4, mapDispatchToProps$1, null,
8197
8347
  forwardRef: true
8198
8348
  })(Keypad);
8199
8349
 
8350
+ /**
8351
+ * A keypad with two pages of keys.
8352
+ */
8200
8353
  const {
8201
8354
  column: column$2,
8202
8355
  row: row$4,
8203
8356
  fullWidth: fullWidth$2
8204
8357
  } = Styles;
8205
8358
  class TwoPageKeypad extends React__namespace.Component {
8206
- constructor() {
8207
- super(...arguments);
8208
- _defineProperty(this, "state", {
8209
- selectedPage: "Numbers"
8210
- });
8211
- }
8359
+ state = {
8360
+ selectedPage: "Numbers"
8361
+ };
8212
8362
  render() {
8213
8363
  const {
8214
8364
  leftPage,
@@ -8253,16 +8403,16 @@ const styles$3 = aphrodite.StyleSheet.create({
8253
8403
  backgroundColor: offBlack16
8254
8404
  },
8255
8405
  borderTop: {
8256
- borderTop: "".concat(innerBorderWidthPx, "px ").concat(innerBorderStyle, " ") + "".concat(innerBorderColor)
8406
+ borderTop: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`
8257
8407
  },
8258
8408
  borderLeft: {
8259
- borderLeft: "".concat(innerBorderWidthPx, "px ").concat(innerBorderStyle, " ") + "".concat(innerBorderColor),
8409
+ borderLeft: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`,
8260
8410
  boxSizing: "content-box"
8261
8411
  },
8262
8412
  tabbar: {
8263
8413
  background: Color__default["default"].offWhite,
8264
- borderTop: "1px solid ".concat(Color__default["default"].offBlack50),
8265
- borderBottom: "1px solid ".concat(Color__default["default"].offBlack50)
8414
+ borderTop: `1px solid ${Color__default["default"].offBlack50}`,
8415
+ borderBottom: `1px solid ${Color__default["default"].offBlack50}`
8266
8416
  }
8267
8417
  });
8268
8418
  const mapStateToProps$3 = state => {
@@ -8498,9 +8648,8 @@ const styles$2 = aphrodite.StyleSheet.create({
8498
8648
  }
8499
8649
  });
8500
8650
  const mapStateToProps$2 = state => {
8501
- var _state$input$cursor;
8502
8651
  return {
8503
- cursorContext: (_state$input$cursor = state.input.cursor) === null || _state$input$cursor === void 0 ? void 0 : _state$input$cursor.context,
8652
+ cursorContext: state.input.cursor?.context,
8504
8653
  dynamicJumpOut: !state.layout.navigationPadEnabled
8505
8654
  };
8506
8655
  };
@@ -8631,9 +8780,8 @@ class FractionKeypad extends React__namespace.Component {
8631
8780
  }
8632
8781
  }
8633
8782
  const mapStateToProps$1 = state => {
8634
- var _state$input$cursor;
8635
8783
  return {
8636
- cursorContext: (_state$input$cursor = state.input.cursor) === null || _state$input$cursor === void 0 ? void 0 : _state$input$cursor.context,
8784
+ cursorContext: state.input.cursor?.context,
8637
8785
  dynamicJumpOut: !state.layout.navigationPadEnabled
8638
8786
  };
8639
8787
  };
@@ -8750,71 +8898,12 @@ const {
8750
8898
  } = Styles;
8751
8899
  // eslint-disable-next-line react/no-unsafe
8752
8900
  class KeypadContainer extends React__namespace.Component {
8753
- constructor() {
8754
- super(...arguments);
8755
- _defineProperty(this, "_containerRef", /*#__PURE__*/React__namespace.createRef());
8756
- _defineProperty(this, "_containerResizeObserver", null);
8757
- _defineProperty(this, "_resizeTimeout", void 0);
8758
- _defineProperty(this, "hasMounted", void 0);
8759
- _defineProperty(this, "state", {
8760
- hasBeenActivated: false,
8761
- viewportWidth: "100vw"
8762
- });
8763
- _defineProperty(this, "_throttleResizeHandler", () => {
8764
- // Throttle the resize callbacks.
8765
- // https://developer.mozilla.org/en-US/docs/Web/Events/resize
8766
- if (this._resizeTimeout == null) {
8767
- this._resizeTimeout = window.setTimeout(() => {
8768
- this._resizeTimeout = null;
8769
- this._onResize();
8770
- }, 66);
8771
- }
8772
- });
8773
- _defineProperty(this, "_onResize", () => {
8774
- var _this$_containerRef$c, _this$_containerRef$c2, _this$props$onPageSiz, _this$props;
8775
- // Whenever the page resizes, we need to force an update, as the button
8776
- // heights and keypad width are computed based on horizontal space.
8777
- this.setState({
8778
- viewportWidth: window.innerWidth
8779
- });
8780
- const containerWidth = ((_this$_containerRef$c = this._containerRef.current) === null || _this$_containerRef$c === void 0 ? void 0 : _this$_containerRef$c.clientWidth) || 0;
8781
- const containerHeight = ((_this$_containerRef$c2 = this._containerRef.current) === null || _this$_containerRef$c2 === void 0 ? void 0 : _this$_containerRef$c2.clientHeight) || 0;
8782
- (_this$props$onPageSiz = (_this$props = this.props).onPageSizeChange) === null || _this$props$onPageSiz === void 0 ? void 0 : _this$props$onPageSiz.call(_this$props, window.innerWidth, window.innerHeight, containerWidth, containerHeight);
8783
- });
8784
- _defineProperty(this, "renderKeypad", () => {
8785
- const {
8786
- extraKeys,
8787
- keypadType,
8788
- layoutMode,
8789
- navigationPadEnabled
8790
- } = this.props;
8791
- const keypadProps = {
8792
- extraKeys,
8793
- // HACK(charlie): In order to properly round the corners of the
8794
- // compact keypad, we need to instruct some of our child views to
8795
- // crop themselves. At least we're colocating all the layout
8796
- // information in this component, though.
8797
- roundTopLeft: layoutMode === LayoutMode.COMPACT && !navigationPadEnabled,
8798
- roundTopRight: layoutMode === LayoutMode.COMPACT
8799
- };
8800
-
8801
- // Select the appropriate keyboard given the type.
8802
- // TODO(charlie): In the future, we might want to move towards a
8803
- // data-driven approach to defining keyboard layouts, and have a
8804
- // generic keyboard that takes some "keyboard data" and renders it.
8805
- // However, the keyboards differ pretty heavily right now and it's not
8806
- // clear what that format would look like exactly. Plus, there aren't
8807
- // very many of them. So to keep us moving, we'll just hardcode.
8808
- switch (keypadType) {
8809
- case KeypadType.FRACTION:
8810
- return /*#__PURE__*/React__namespace.createElement(FractionKeypad$1, keypadProps);
8811
- case KeypadType.EXPRESSION:
8812
- return /*#__PURE__*/React__namespace.createElement(ExpressionKeypad$1, keypadProps);
8813
- default:
8814
- throw new Error("Invalid keypad type: " + keypadType);
8815
- }
8816
- });
8817
- }
8901
+ _containerRef = /*#__PURE__*/React__namespace.createRef();
8902
+ _containerResizeObserver = null;
8903
+ state = {
8904
+ hasBeenActivated: false,
8905
+ viewportWidth: "100vw"
8906
+ };
8818
8907
  UNSAFE_componentWillMount() {
8819
8908
  if (this.props.active) {
8820
8909
  this.setState({
@@ -8852,11 +8941,63 @@ class KeypadContainer extends React__namespace.Component {
8852
8941
  }
8853
8942
  }
8854
8943
  componentWillUnmount() {
8855
- var _this$_containerResiz;
8856
8944
  window.removeEventListener("resize", this._throttleResizeHandler);
8857
8945
  window.removeEventListener("orientationchange", this._throttleResizeHandler);
8858
- (_this$_containerResiz = this._containerResizeObserver) === null || _this$_containerResiz === void 0 ? void 0 : _this$_containerResiz.disconnect();
8859
- }
8946
+ this._containerResizeObserver?.disconnect();
8947
+ }
8948
+ _throttleResizeHandler = () => {
8949
+ // Throttle the resize callbacks.
8950
+ // https://developer.mozilla.org/en-US/docs/Web/Events/resize
8951
+ if (this._resizeTimeout == null) {
8952
+ this._resizeTimeout = window.setTimeout(() => {
8953
+ this._resizeTimeout = null;
8954
+ this._onResize();
8955
+ }, 66);
8956
+ }
8957
+ };
8958
+ _onResize = () => {
8959
+ // Whenever the page resizes, we need to force an update, as the button
8960
+ // heights and keypad width are computed based on horizontal space.
8961
+ this.setState({
8962
+ viewportWidth: window.innerWidth
8963
+ });
8964
+ const containerWidth = this._containerRef.current?.clientWidth || 0;
8965
+ const containerHeight = this._containerRef.current?.clientHeight || 0;
8966
+ this.props.onPageSizeChange?.(window.innerWidth, window.innerHeight, containerWidth, containerHeight);
8967
+ };
8968
+ renderKeypad = () => {
8969
+ const {
8970
+ extraKeys,
8971
+ keypadType,
8972
+ layoutMode,
8973
+ navigationPadEnabled
8974
+ } = this.props;
8975
+ const keypadProps = {
8976
+ extraKeys,
8977
+ // HACK(charlie): In order to properly round the corners of the
8978
+ // compact keypad, we need to instruct some of our child views to
8979
+ // crop themselves. At least we're colocating all the layout
8980
+ // information in this component, though.
8981
+ roundTopLeft: layoutMode === LayoutMode.COMPACT && !navigationPadEnabled,
8982
+ roundTopRight: layoutMode === LayoutMode.COMPACT
8983
+ };
8984
+
8985
+ // Select the appropriate keyboard given the type.
8986
+ // TODO(charlie): In the future, we might want to move towards a
8987
+ // data-driven approach to defining keyboard layouts, and have a
8988
+ // generic keyboard that takes some "keyboard data" and renders it.
8989
+ // However, the keyboards differ pretty heavily right now and it's not
8990
+ // clear what that format would look like exactly. Plus, there aren't
8991
+ // very many of them. So to keep us moving, we'll just hardcode.
8992
+ switch (keypadType) {
8993
+ case KeypadType.FRACTION:
8994
+ return /*#__PURE__*/React__namespace.createElement(FractionKeypad$1, keypadProps);
8995
+ case KeypadType.EXPRESSION:
8996
+ return /*#__PURE__*/React__namespace.createElement(ExpressionKeypad$1, keypadProps);
8997
+ default:
8998
+ throw new Error("Invalid keypad type: " + keypadType);
8999
+ }
9000
+ };
8860
9001
  render() {
8861
9002
  const {
8862
9003
  active,
@@ -8916,7 +9057,7 @@ const styles = aphrodite.StyleSheet.create({
8916
9057
  left: 0,
8917
9058
  right: 0,
8918
9059
  position: "fixed",
8919
- transition: "".concat(keypadAnimationDurationMs, "ms ease-out"),
9060
+ transition: `${keypadAnimationDurationMs}ms ease-out`,
8920
9061
  transitionProperty: "transform",
8921
9062
  zIndex: keypad
8922
9063
  },
@@ -8937,7 +9078,7 @@ const styles = aphrodite.StyleSheet.create({
8937
9078
  },
8938
9079
  navigationPadContainer: {
8939
9080
  // Add a separator between the navigation pad and the keypad.
8940
- borderRight: "".concat(innerBorderWidthPx, "px ").concat(innerBorderStyle, " ") + "".concat(innerBorderColor),
9081
+ borderRight: `${innerBorderWidthPx}px ${innerBorderStyle} ` + `${innerBorderColor}`,
8941
9082
  boxSizing: "content-box"
8942
9083
  },
8943
9084
  // Defer to the navigation pad, such that the navigation pad is always
@@ -8992,10 +9133,7 @@ var KeypadContainer$1 = reactRedux.connect(mapStateToProps, mapDispatchToProps,
8992
9133
  * It is entirely ignorant of the existence of popovers and the positions of
8993
9134
  * DOM nodes, operating solely on IDs. The state machine does accommodate for
8994
9135
  * multi-touch interactions, tracking gesture state on a per-touch basis.
8995
- */
8996
-
8997
- // exported for tests
8998
-
9136
+ */ // exported for tests
8999
9137
  const defaultOptions = {
9000
9138
  longPressWaitTimeMs: 50,
9001
9139
  swipeThresholdPx: 20,
@@ -9003,12 +9141,6 @@ const defaultOptions = {
9003
9141
  };
9004
9142
  class GestureStateMachine {
9005
9143
  constructor(handlers, options, swipeDisabledNodeIds, multiPressableKeys) {
9006
- _defineProperty(this, "handlers", void 0);
9007
- _defineProperty(this, "options", void 0);
9008
- _defineProperty(this, "swipeDisabledNodeIds", void 0);
9009
- _defineProperty(this, "multiPressableKeys", void 0);
9010
- _defineProperty(this, "touchState", void 0);
9011
- _defineProperty(this, "swipeState", void 0);
9012
9144
  this.handlers = handlers;
9013
9145
  this.options = {
9014
9146
  ...defaultOptions,
@@ -9180,8 +9312,7 @@ class GestureStateMachine {
9180
9312
  // Only respect the finger that started a swipe. Any other lingering
9181
9313
  // gestures are ignored.
9182
9314
  if (this.swipeState.touchId === touchId) {
9183
- var _this$handlers$onSwip, _this$handlers;
9184
- (_this$handlers$onSwip = (_this$handlers = this.handlers).onSwipeChange) === null || _this$handlers$onSwip === void 0 ? void 0 : _this$handlers$onSwip.call(_this$handlers, pageX - this.swipeState.startX);
9315
+ this.handlers.onSwipeChange?.(pageX - this.swipeState.startX);
9185
9316
  }
9186
9317
  } else if (this.touchState[touchId]) {
9187
9318
  // It could be touch events started outside the keypad and
@@ -9194,7 +9325,6 @@ class GestureStateMachine {
9194
9325
  const dx = pageX - startX;
9195
9326
  const shouldBeginSwiping = swipeEnabled && !swipeLocked && Math.abs(dx) > this.options.swipeThresholdPx;
9196
9327
  if (shouldBeginSwiping) {
9197
- var _this$handlers$onSwip2, _this$handlers2;
9198
9328
  this._onSwipeStart();
9199
9329
 
9200
9330
  // Trigger the swipe.
@@ -9202,7 +9332,7 @@ class GestureStateMachine {
9202
9332
  touchId,
9203
9333
  startX
9204
9334
  };
9205
- (_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);
9335
+ this.handlers.onSwipeChange?.(pageX - this.swipeState.startX);
9206
9336
  } else {
9207
9337
  const id = getId();
9208
9338
  if (id !== activeNodeId) {
@@ -9225,8 +9355,7 @@ class GestureStateMachine {
9225
9355
  // Only respect the finger that started a swipe. Any other lingering
9226
9356
  // gestures are ignored.
9227
9357
  if (this.swipeState.touchId === touchId) {
9228
- var _this$handlers$onSwip3, _this$handlers3;
9229
- (_this$handlers$onSwip3 = (_this$handlers3 = this.handlers).onSwipeEnd) === null || _this$handlers$onSwip3 === void 0 ? void 0 : _this$handlers$onSwip3.call(_this$handlers3, pageX - this.swipeState.startX);
9358
+ this.handlers.onSwipeEnd?.(pageX - this.swipeState.startX);
9230
9359
  this.swipeState = null;
9231
9360
  }
9232
9361
  } else if (this.touchState[touchId]) {
@@ -9260,8 +9389,7 @@ class GestureStateMachine {
9260
9389
  // displacement.
9261
9390
  if (this.swipeState) {
9262
9391
  if (this.swipeState.touchId === touchId) {
9263
- var _this$handlers$onSwip4, _this$handlers4;
9264
- (_this$handlers$onSwip4 = (_this$handlers4 = this.handlers).onSwipeEnd) === null || _this$handlers$onSwip4 === void 0 ? void 0 : _this$handlers$onSwip4.call(_this$handlers4, 0);
9392
+ this.handlers.onSwipeEnd?.(0);
9265
9393
  this.swipeState = null;
9266
9394
  }
9267
9395
  } else if (this.touchState[touchId]) {
@@ -9284,9 +9412,6 @@ class GestureStateMachine {
9284
9412
 
9285
9413
  class NodeManager {
9286
9414
  constructor() {
9287
- _defineProperty(this, "_nodesById", void 0);
9288
- _defineProperty(this, "_orderedIds", void 0);
9289
- _defineProperty(this, "_cachedBoundingBoxesById", void 0);
9290
9415
  // A mapping from IDs to DOM nodes.
9291
9416
  this._nodesById = {};
9292
9417
 
@@ -9399,9 +9524,6 @@ class NodeManager {
9399
9524
 
9400
9525
  class PopoverStateMachine {
9401
9526
  constructor(handlers) {
9402
- _defineProperty(this, "handlers", void 0);
9403
- _defineProperty(this, "popovers", void 0);
9404
- _defineProperty(this, "activePopover", void 0);
9405
9527
  this.handlers = handlers;
9406
9528
  this.activePopover = null;
9407
9529
  this.popovers = {};
@@ -9561,16 +9683,16 @@ class PopoverStateMachine {
9561
9683
  }
9562
9684
  }
9563
9685
 
9686
+ /**
9687
+ * A high-level manager for our gesture system. In particular, this class
9688
+ * connects our various bits of logic for managing gestures and interactions,
9689
+ * and links them together.
9690
+ */
9564
9691
  const coordsForEvent = evt => {
9565
9692
  return [evt.changedTouches[0].clientX, evt.changedTouches[0].clientY];
9566
9693
  };
9567
9694
  class GestureManager {
9568
9695
  constructor(options, handlers, disabledSwipeKeys, multiPressableKeys) {
9569
- _defineProperty(this, "swipeEnabled", void 0);
9570
- _defineProperty(this, "trackEvents", void 0);
9571
- _defineProperty(this, "nodeManager", void 0);
9572
- _defineProperty(this, "popoverStateMachine", void 0);
9573
- _defineProperty(this, "gestureStateMachine", void 0);
9574
9696
  const {
9575
9697
  swipeEnabled
9576
9698
  } = options;
@@ -9811,13 +9933,12 @@ const inputReducer = function () {
9811
9933
  case "PressKey":
9812
9934
  const keyConfig = KeyConfigs[action.key];
9813
9935
  if (keyConfig.type !== "KEYPAD_NAVIGATION") {
9814
- var _state$keyHandler;
9815
9936
  // This is probably an anti-pattern but it works for the
9816
9937
  // case where we don't actually control the state but we
9817
9938
  // still want to communicate with the other object
9818
9939
  return {
9819
9940
  ...state,
9820
- cursor: (_state$keyHandler = state.keyHandler) === null || _state$keyHandler === void 0 ? void 0 : _state$keyHandler.call(state, keyConfig.id)
9941
+ cursor: state.keyHandler?.(keyConfig.id)
9821
9942
  };
9822
9943
  }
9823
9944
 
@@ -10186,66 +10307,6 @@ const createStore = () => {
10186
10307
  class ProvidedKeypad extends React__namespace.Component {
10187
10308
  constructor(props) {
10188
10309
  super(props);
10189
- _defineProperty(this, "store", void 0);
10190
- _defineProperty(this, "activate", () => {
10191
- this.props.setKeypadActive(true);
10192
- });
10193
- _defineProperty(this, "dismiss", () => {
10194
- this.props.setKeypadActive(false);
10195
- });
10196
- _defineProperty(this, "configure", (configuration, cb) => {
10197
- this.store.dispatch(configureKeypad(configuration));
10198
-
10199
- // HACK(charlie): In Perseus, triggering a focus causes the keypad to
10200
- // animate into view and re-configure. We'd like to provide the option
10201
- // to re-render the re-configured keypad before animating it into view,
10202
- // to avoid jank in the animation. As such, we support passing a
10203
- // callback into `configureKeypad`. However, implementing this properly
10204
- // would require middleware, etc., so we just hack it on with
10205
- // `setTimeout` for now.
10206
- setTimeout(() => cb && cb());
10207
- });
10208
- _defineProperty(this, "setCursor", cursor => {
10209
- this.store.dispatch(setCursor(cursor));
10210
- });
10211
- _defineProperty(this, "setKeyHandler", keyHandler => {
10212
- this.store.dispatch(setKeyHandler(keyHandler));
10213
- });
10214
- _defineProperty(this, "getDOMNode", () => {
10215
- return ReactDOM__default["default"].findDOMNode(this);
10216
- });
10217
- _defineProperty(this, "onElementMounted", element => {
10218
- var _this$props$onElement, _this$props;
10219
- this.props.onAnalyticsEvent({
10220
- type: "math-input:keypad-opened",
10221
- payload: {
10222
- virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
10223
- }
10224
- });
10225
-
10226
- // Append the dispatch methods that we want to expose
10227
- // externally to the returned React element.
10228
- const elementWithDispatchMethods = {
10229
- ...element,
10230
- activate: this.activate,
10231
- dismiss: this.dismiss,
10232
- configure: this.configure,
10233
- setCursor: this.setCursor,
10234
- setKeyHandler: this.setKeyHandler,
10235
- getDOMNode: this.getDOMNode
10236
- };
10237
- (_this$props$onElement = (_this$props = this.props).onElementMounted) === null || _this$props$onElement === void 0 ? void 0 : _this$props$onElement.call(_this$props, elementWithDispatchMethods);
10238
- });
10239
- _defineProperty(this, "onDismiss", () => {
10240
- var _this$props$onDismiss, _this$props2;
10241
- this.props.onAnalyticsEvent({
10242
- type: "math-input:keypad-closed",
10243
- payload: {
10244
- virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
10245
- }
10246
- });
10247
- (_this$props$onDismiss = (_this$props2 = this.props).onDismiss) === null || _this$props$onDismiss === void 0 ? void 0 : _this$props$onDismiss.call(_this$props2);
10248
- });
10249
10310
  this.store = createStore();
10250
10311
  }
10251
10312
  componentDidUpdate(prevProps) {
@@ -10256,6 +10317,63 @@ class ProvidedKeypad extends React__namespace.Component {
10256
10317
  this.store.dispatch(dismissKeypad());
10257
10318
  }
10258
10319
  }
10320
+ activate = () => {
10321
+ this.props.setKeypadActive(true);
10322
+ };
10323
+ dismiss = () => {
10324
+ this.props.setKeypadActive(false);
10325
+ };
10326
+ configure = (configuration, cb) => {
10327
+ this.store.dispatch(configureKeypad(configuration));
10328
+
10329
+ // HACK(charlie): In Perseus, triggering a focus causes the keypad to
10330
+ // animate into view and re-configure. We'd like to provide the option
10331
+ // to re-render the re-configured keypad before animating it into view,
10332
+ // to avoid jank in the animation. As such, we support passing a
10333
+ // callback into `configureKeypad`. However, implementing this properly
10334
+ // would require middleware, etc., so we just hack it on with
10335
+ // `setTimeout` for now.
10336
+ setTimeout(() => cb && cb());
10337
+ };
10338
+ setCursor = cursor => {
10339
+ this.store.dispatch(setCursor(cursor));
10340
+ };
10341
+ setKeyHandler = keyHandler => {
10342
+ this.store.dispatch(setKeyHandler(keyHandler));
10343
+ };
10344
+ getDOMNode = () => {
10345
+ return ReactDOM__default["default"].findDOMNode(this);
10346
+ };
10347
+ onElementMounted = element => {
10348
+ this.props.onAnalyticsEvent({
10349
+ type: "math-input:keypad-opened",
10350
+ payload: {
10351
+ virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
10352
+ }
10353
+ });
10354
+
10355
+ // Append the dispatch methods that we want to expose
10356
+ // externally to the returned React element.
10357
+ const elementWithDispatchMethods = {
10358
+ ...element,
10359
+ activate: this.activate,
10360
+ dismiss: this.dismiss,
10361
+ configure: this.configure,
10362
+ setCursor: this.setCursor,
10363
+ setKeyHandler: this.setKeyHandler,
10364
+ getDOMNode: this.getDOMNode
10365
+ };
10366
+ this.props.onElementMounted?.(elementWithDispatchMethods);
10367
+ };
10368
+ onDismiss = () => {
10369
+ this.props.onAnalyticsEvent({
10370
+ type: "math-input:keypad-closed",
10371
+ payload: {
10372
+ virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
10373
+ }
10374
+ });
10375
+ this.props.onDismiss?.();
10376
+ };
10259
10377
  render() {
10260
10378
  const {
10261
10379
  style