@khanacademy/math-input 14.2.1 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @khanacademy/math-input
2
2
 
3
+ ## 15.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#786](https://github.com/Khan/perseus/pull/786) [`af4ebf37`](https://github.com/Khan/perseus/commit/af4ebf37dfed15ffd93a8cf2a20d0be464120dd7) Thanks [@handeyeco](https://github.com/handeyeco)! - Added a new required dep in Perseus and MathInput (required by WB)
8
+
9
+ ### Patch Changes
10
+
11
+ - [#791](https://github.com/Khan/perseus/pull/791) [`3eb0e158`](https://github.com/Khan/perseus/commit/3eb0e15860224cc595d5b7e78d2a5d60e808561c) Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! - Minor fix to ensure that the keypadElement is always provided to mobile keypad consumers
12
+
13
+ ## 14.2.2
14
+
15
+ ### Patch Changes
16
+
17
+ - [#787](https://github.com/Khan/perseus/pull/787) [`ed00ee59`](https://github.com/Khan/perseus/commit/ed00ee59dcdeb20a66709c6b7d3474da55d58e4d) Thanks [@jeremywiebe](https://github.com/jeremywiebe)! - Fix a bug where the mobile keypad didn't animate in the first time it appeared.
18
+
19
+ - Updated dependencies [[`79403e06`](https://github.com/Khan/perseus/commit/79403e06eedb597d7818d6c858bbba6f51ff3fe1)]:
20
+ - @khanacademy/perseus-core@1.3.0
21
+
3
22
  ## 14.2.1
4
23
 
5
24
  ### Patch Changes
package/dist/es/index.js CHANGED
@@ -20,7 +20,7 @@ import * as Redux from 'redux';
20
20
 
21
21
  // This file is processed by a Rollup plugin (replace) to inject the production
22
22
  const libName = "@khanacademy/math-input";
23
- const libVersion = "14.2.1";
23
+ const libVersion = "15.0.0";
24
24
  addLibraryVersionToPerseusDebug(libName, libVersion);
25
25
 
26
26
  function _extends() {
@@ -1279,10 +1279,9 @@ const mobileKeyTranslator = _extends({}, keyToMathquillMap, {
1279
1279
  * from MathQuill changes.
1280
1280
  */
1281
1281
  class MathWrapper {
1282
- // MathQuill MathField input
1283
-
1284
1282
  constructor(element, callbacks = {}) {
1285
1283
  this.mathField = void 0;
1284
+ // MathQuill MathField input
1286
1285
  this.callbacks = void 0;
1287
1286
  this.mathField = createMathField(element, () => {
1288
1287
  return {
@@ -1471,14 +1470,18 @@ class MathInput extends React.Component {
1471
1470
  this.didTouchOutside = void 0;
1472
1471
  this.didScroll = void 0;
1473
1472
  this.mathField = void 0;
1473
+ // @ts-expect-error - TS2564 - Property 'recordTouchStartOutside' has no initializer and is not definitely assigned in the constructor.
1474
1474
  this.recordTouchStartOutside = void 0;
1475
+ // @ts-expect-error - TS2564 - Property 'blurOnTouchEndOutside' has no initializer and is not definitely assigned in the constructor.
1475
1476
  this.blurOnTouchEndOutside = void 0;
1476
1477
  this.dragListener = void 0;
1477
1478
  this.inputRef = void 0;
1478
1479
  this._isMounted = void 0;
1479
1480
  this._mathContainer = void 0;
1481
+ // @ts-expect-error - TS2564 - Property '_container' has no initializer and is not definitely assigned in the constructor.
1480
1482
  this._container = void 0;
1481
1483
  this._root = void 0;
1484
+ // @ts-expect-error - TS2564 - Property '_containerBounds' has no initializer and is not definitely assigned in the constructor.
1482
1485
  this._containerBounds = void 0;
1483
1486
  this._keypadBounds = void 0;
1484
1487
  this.state = {
@@ -1504,6 +1507,7 @@ class MathInput extends React.Component {
1504
1507
  this._root.style.padding = `${padding.paddingTop}px ${padding.paddingRight}px` + ` ${padding.paddingBottom}px ${padding.paddingLeft}px`;
1505
1508
  this._root.style.fontSize = `${fontSizePt}pt`;
1506
1509
  };
1510
+ /** Gets and cache they bounds of the keypadElement */
1507
1511
  this._getKeypadBounds = () => {
1508
1512
  if (!this._keypadBounds) {
1509
1513
  var _this$props$keypadEle;
@@ -1574,7 +1578,7 @@ class MathInput extends React.Component {
1574
1578
  var _this$props$keypadEle2, _this$props;
1575
1579
  // Pass this component's handleKey method to the keypad so it can call
1576
1580
  // it whenever it needs to trigger a keypress action.
1577
- (_this$props$keypadEle2 = this.props.keypadElement) == null ? void 0 : _this$props$keypadEle2.setKeyHandler(key => {
1581
+ (_this$props$keypadEle2 = this.props.keypadElement) == null || _this$props$keypadEle2.setKeyHandler(key => {
1578
1582
  const cursor = this.mathField.pressKey(key);
1579
1583
 
1580
1584
  // Trigger an `onChange` if the value in the input changed, and hide
@@ -1597,7 +1601,7 @@ class MathInput extends React.Component {
1597
1601
  return cursor;
1598
1602
  });
1599
1603
  this.mathField.focus();
1600
- (_this$props = this.props) == null ? void 0 : _this$props.onFocus();
1604
+ (_this$props = this.props) == null || _this$props.onFocus();
1601
1605
  this.setState({
1602
1606
  focused: true
1603
1607
  }, () => {
@@ -1620,6 +1624,30 @@ class MathInput extends React.Component {
1620
1624
  });
1621
1625
  });
1622
1626
  };
1627
+ /**
1628
+ * Tries to determine which DOM node to place the cursor next to based on
1629
+ * where the user drags the cursor handle. If it finds a node it will
1630
+ * place the cursor next to it, update the handle to be under the cursor,
1631
+ * and return true. If it doesn't find a node, it returns false.
1632
+ *
1633
+ * It searches for nodes by doing it tests at the following points:
1634
+ *
1635
+ * (x - dx, y), (x, y), (x + dx, y)
1636
+ *
1637
+ * If it doesn't find any nodes from the rendered math it will update y
1638
+ * by adding dy.
1639
+ *
1640
+ * The algorithm ends its search when y goes outside the bounds of
1641
+ * containerBounds.
1642
+ *
1643
+ * @param {DOMRect} containerBounds - bounds of the container node
1644
+ * @param {number} x - the initial x coordinate in the viewport
1645
+ * @param {number} y - the initial y coordinate in the viewport
1646
+ * @param {number} dx - horizontal spacing between elementFromPoint calls
1647
+ * @param {number} dy - vertical spacing between elementFromPoint calls,
1648
+ * sign determines direction.
1649
+ * @returns {boolean} - true if a node was hit, false otherwise.
1650
+ */
1623
1651
  this._findHitNode = (containerBounds, x, y, dx, dy) => {
1624
1652
  while (y >= containerBounds.top && y <= containerBounds.bottom) {
1625
1653
  y += dy;
@@ -1691,6 +1719,13 @@ class MathInput extends React.Component {
1691
1719
  }
1692
1720
  return false;
1693
1721
  };
1722
+ /**
1723
+ * Inserts the cursor at the DOM node closest to the given coordinates,
1724
+ * based on hit-tests conducted using #_findHitNode.
1725
+ *
1726
+ * @param {number} x - the x coordinate in the viewport
1727
+ * @param {number} y - the y coordinate in the viewport
1728
+ */
1694
1729
  this._insertCursorAtClosestNode = (x, y) => {
1695
1730
  const cursor = this.mathField.getCursor();
1696
1731
 
@@ -1804,6 +1839,12 @@ class MathInput extends React.Component {
1804
1839
  this._updateCursorHandle();
1805
1840
  }
1806
1841
  };
1842
+ /**
1843
+ * When a touch starts in the cursor handle, we track it so as to avoid
1844
+ * handling any touch events ourself.
1845
+ *
1846
+ * @param {TouchEvent} e - the raw touch event from the browser
1847
+ */
1807
1848
  this.onCursorHandleTouchStart = e => {
1808
1849
  // NOTE(charlie): The cursor handle is a child of this view, so whenever
1809
1850
  // it receives a touch event, that event would also typically be bubbled
@@ -1827,6 +1868,12 @@ class MathInput extends React.Component {
1827
1868
  return value;
1828
1869
  }
1829
1870
  };
1871
+ /**
1872
+ * When the user moves the cursor handle update the position of the cursor
1873
+ * and the handle.
1874
+ *
1875
+ * @param {TouchEvent} e - the raw touch event from the browser
1876
+ */
1830
1877
  this.onCursorHandleTouchMove = e => {
1831
1878
  e.stopPropagation();
1832
1879
  const x = e.changedTouches[0].clientX;
@@ -1859,10 +1906,20 @@ class MathInput extends React.Component {
1859
1906
  const adjustedY = y - distanceAboveFingerToTrySelecting;
1860
1907
  this._insertCursorAtClosestNode(x, adjustedY);
1861
1908
  };
1909
+ /**
1910
+ * When the user releases the cursor handle, animate it back into place.
1911
+ *
1912
+ * @param {TouchEvent} e - the raw touch event from the browser
1913
+ */
1862
1914
  this.onCursorHandleTouchEnd = e => {
1863
1915
  e.stopPropagation();
1864
1916
  this._updateCursorHandle(true);
1865
1917
  };
1918
+ /**
1919
+ * If the gesture is cancelled mid-drag, simply hide it.
1920
+ *
1921
+ * @param {TouchEvent} e - the raw touch event from the browser
1922
+ */
1866
1923
  this.onCursorHandleTouchCancel = e => {
1867
1924
  e.stopPropagation();
1868
1925
  this._updateCursorHandle(true);
@@ -1921,6 +1978,10 @@ class MathInput extends React.Component {
1921
1978
  const focusedBorderWidthPx = 2;
1922
1979
  return this.state.focused ? focusedBorderWidthPx : normalBorderWidthPx;
1923
1980
  };
1981
+ // Calculate the appropriate padding based on the border width (which is
1982
+ // considered 'padding', since we're using 'border-box') and the fact
1983
+ // that MathQuill automatically applies 2px of padding to the inner
1984
+ // input.
1924
1985
  this.getInputInnerPadding = () => {
1925
1986
  const paddingInset = totalDesiredPadding - this.getBorderWidthPx();
1926
1987
 
@@ -5160,13 +5221,6 @@ function processStyleType(style) {
5160
5221
  }
5161
5222
 
5162
5223
  class TransitionChild extends React.Component {
5163
- // Each 2-tuple in the queue represents two classnames: one to remove and
5164
- // one to add (in that order).
5165
-
5166
- // We keep track of all of the current applied classes so that we can remove
5167
- // them before a new transition starts in the case of the current transition
5168
- // being interrupted.
5169
-
5170
5224
  // The use of getDerivedStateFromProps here is to avoid an extra call to
5171
5225
  // setState if the component re-enters. This can happen if TransitionGroup
5172
5226
  // sets `in` from `false` to `true`.
@@ -5183,7 +5237,12 @@ class TransitionChild extends React.Component {
5183
5237
  }
5184
5238
  constructor(props) {
5185
5239
  super(props);
5240
+ // Each 2-tuple in the queue represents two classnames: one to remove and
5241
+ // one to add (in that order).
5186
5242
  this.classNameQueue = void 0;
5243
+ // We keep track of all of the current applied classes so that we can remove
5244
+ // them before a new transition starts in the case of the current transition
5245
+ // being interrupted.
5187
5246
  this.appliedClassNames = void 0;
5188
5247
  this._isMounted = false;
5189
5248
  this.addClass = (elem, className) => {
@@ -5295,7 +5354,8 @@ class TransitionChild extends React.Component {
5295
5354
  }
5296
5355
  queueClass(removeClassName, addClassName) {
5297
5356
  this.classNameQueue.push([removeClassName, addClassName]);
5298
- this.props.schedule.animationFrame(this.flushClassNameQueue);
5357
+ // Queue operation for after the next paint.
5358
+ this.props.schedule.timeout(this.flushClassNameQueue, 0);
5299
5359
  }
5300
5360
  render() {
5301
5361
  const {
@@ -5411,7 +5471,7 @@ class MobileKeypad extends React.Component {
5411
5471
  this.dismiss = () => {
5412
5472
  var _this$props$onDismiss, _this$props;
5413
5473
  this.props.setKeypadActive(false);
5414
- (_this$props$onDismiss = (_this$props = this.props).onDismiss) == null ? void 0 : _this$props$onDismiss.call(_this$props);
5474
+ (_this$props$onDismiss = (_this$props = this.props).onDismiss) == null || _this$props$onDismiss.call(_this$props);
5415
5475
  };
5416
5476
  this.configure = (configuration, cb) => {
5417
5477
  this.setState({
@@ -5457,7 +5517,7 @@ class MobileKeypad extends React.Component {
5457
5517
  this._containerResizeObserver.observe(this._containerRef.current);
5458
5518
  }
5459
5519
  }
5460
- (_this$props$onElement = (_this$props2 = this.props).onElementMounted) == null ? void 0 : _this$props$onElement.call(_this$props2, {
5520
+ (_this$props$onElement = (_this$props2 = this.props).onElementMounted) == null || _this$props$onElement.call(_this$props2, {
5461
5521
  activate: this.activate,
5462
5522
  dismiss: this.dismiss,
5463
5523
  configure: this.configure,
@@ -5470,7 +5530,7 @@ class MobileKeypad extends React.Component {
5470
5530
  var _this$_containerResiz;
5471
5531
  window.removeEventListener("resize", this._throttleResizeHandler);
5472
5532
  window.removeEventListener("orientationchange", this._throttleResizeHandler);
5473
- (_this$_containerResiz = this._containerResizeObserver) == null ? void 0 : _this$_containerResiz.disconnect();
5533
+ (_this$_containerResiz = this._containerResizeObserver) == null || _this$_containerResiz.disconnect();
5474
5534
  }
5475
5535
  _handleClickKey(key) {
5476
5536
  var _this$state$keyHandle, _this$state;
@@ -5498,7 +5558,10 @@ class MobileKeypad extends React.Component {
5498
5558
  ...(Array.isArray(style) ? style : [style])];
5499
5559
  const isExpression = (keypadConfig == null ? void 0 : keypadConfig.keypadType) === "EXPRESSION";
5500
5560
  const convertDotToTimes = keypadConfig == null ? void 0 : keypadConfig.times;
5501
- return /*#__PURE__*/React.createElement(AphroditeCSSTransitionGroup, {
5561
+ return /*#__PURE__*/React.createElement(View, {
5562
+ style: containerStyle,
5563
+ forwardRef: this._containerRef
5564
+ }, /*#__PURE__*/React.createElement(AphroditeCSSTransitionGroup, {
5502
5565
  transitionEnterTimeout: AnimationDurationInMS,
5503
5566
  transitionLeaveTimeout: AnimationDurationInMS,
5504
5567
  transitionStyle: {
@@ -5517,10 +5580,7 @@ class MobileKeypad extends React.Component {
5517
5580
  transform: "translate3d(0, 100%, 0)"
5518
5581
  }
5519
5582
  }
5520
- }, keypadActive ? /*#__PURE__*/React.createElement(View, {
5521
- style: containerStyle,
5522
- forwardRef: this._containerRef
5523
- }, /*#__PURE__*/React.createElement(Keypad$2, {
5583
+ }, keypadActive ? /*#__PURE__*/React.createElement(Keypad$2, {
5524
5584
  onAnalyticsEvent: this.props.onAnalyticsEvent,
5525
5585
  extraKeys: keypadConfig == null ? void 0 : keypadConfig.extraKeys,
5526
5586
  onClickKey: key => this._handleClickKey(key),
@@ -5535,7 +5595,7 @@ class MobileKeypad extends React.Component {
5535
5595
  advancedRelations: isExpression,
5536
5596
  expandedView: containerWidth > expandedViewThreshold$1,
5537
5597
  showDismiss: true
5538
- })) : null);
5598
+ }) : null));
5539
5599
  }
5540
5600
  }
5541
5601
  const styles$c = StyleSheet.create({
@@ -7839,7 +7899,6 @@ class PopoverManager extends React.Component {
7839
7899
  // naming convention: verb + noun
7840
7900
  // the noun should be one of the other properties in the object that's
7841
7901
  // being dispatched
7842
-
7843
7902
  const dismissKeypad = () => {
7844
7903
  return {
7845
7904
  type: "DismissKeypad"
@@ -8621,7 +8680,7 @@ class KeypadContainer extends React.Component {
8621
8680
  });
8622
8681
  const containerWidth = ((_this$_containerRef$c = this._containerRef.current) == null ? void 0 : _this$_containerRef$c.clientWidth) || 0;
8623
8682
  const containerHeight = ((_this$_containerRef$c2 = this._containerRef.current) == null ? void 0 : _this$_containerRef$c2.clientHeight) || 0;
8624
- (_this$props$onPageSiz = (_this$props = this.props).onPageSizeChange) == null ? void 0 : _this$props$onPageSiz.call(_this$props, window.innerWidth, window.innerHeight, containerWidth, containerHeight);
8683
+ (_this$props$onPageSiz = (_this$props = this.props).onPageSizeChange) == null || _this$props$onPageSiz.call(_this$props, window.innerWidth, window.innerHeight, containerWidth, containerHeight);
8625
8684
  };
8626
8685
  this.renderKeypad = () => {
8627
8686
  const {
@@ -8697,7 +8756,7 @@ class KeypadContainer extends React.Component {
8697
8756
  var _this$_containerResiz;
8698
8757
  window.removeEventListener("resize", this._throttleResizeHandler);
8699
8758
  window.removeEventListener("orientationchange", this._throttleResizeHandler);
8700
- (_this$_containerResiz = this._containerResizeObserver) == null ? void 0 : _this$_containerResiz.disconnect();
8759
+ (_this$_containerResiz = this._containerResizeObserver) == null || _this$_containerResiz.disconnect();
8701
8760
  }
8702
8761
  render() {
8703
8762
  const {
@@ -8829,10 +8888,7 @@ var KeypadContainer$1 = connect(mapStateToProps, mapDispatchToProps, null, {
8829
8888
  * It is entirely ignorant of the existence of popovers and the positions of
8830
8889
  * DOM nodes, operating solely on IDs. The state machine does accommodate for
8831
8890
  * multi-touch interactions, tracking gesture state on a per-touch basis.
8832
- */
8833
-
8834
- // exported for tests
8835
-
8891
+ */ // exported for tests
8836
8892
  const defaultOptions = {
8837
8893
  longPressWaitTimeMs: 50,
8838
8894
  swipeThresholdPx: 20,
@@ -9009,7 +9065,7 @@ class GestureStateMachine {
9009
9065
  // gestures are ignored.
9010
9066
  if (this.swipeState.touchId === touchId) {
9011
9067
  var _this$handlers$onSwip, _this$handlers;
9012
- (_this$handlers$onSwip = (_this$handlers = this.handlers).onSwipeChange) == null ? void 0 : _this$handlers$onSwip.call(_this$handlers, pageX - this.swipeState.startX);
9068
+ (_this$handlers$onSwip = (_this$handlers = this.handlers).onSwipeChange) == null || _this$handlers$onSwip.call(_this$handlers, pageX - this.swipeState.startX);
9013
9069
  }
9014
9070
  } else if (this.touchState[touchId]) {
9015
9071
  // It could be touch events started outside the keypad and
@@ -9030,7 +9086,7 @@ class GestureStateMachine {
9030
9086
  touchId,
9031
9087
  startX
9032
9088
  };
9033
- (_this$handlers$onSwip2 = (_this$handlers2 = this.handlers).onSwipeChange) == null ? void 0 : _this$handlers$onSwip2.call(_this$handlers2, pageX - this.swipeState.startX);
9089
+ (_this$handlers$onSwip2 = (_this$handlers2 = this.handlers).onSwipeChange) == null || _this$handlers$onSwip2.call(_this$handlers2, pageX - this.swipeState.startX);
9034
9090
  } else {
9035
9091
  const id = getId();
9036
9092
  if (id !== activeNodeId) {
@@ -9054,7 +9110,7 @@ class GestureStateMachine {
9054
9110
  // gestures are ignored.
9055
9111
  if (this.swipeState.touchId === touchId) {
9056
9112
  var _this$handlers$onSwip3, _this$handlers3;
9057
- (_this$handlers$onSwip3 = (_this$handlers3 = this.handlers).onSwipeEnd) == null ? void 0 : _this$handlers$onSwip3.call(_this$handlers3, pageX - this.swipeState.startX);
9113
+ (_this$handlers$onSwip3 = (_this$handlers3 = this.handlers).onSwipeEnd) == null || _this$handlers$onSwip3.call(_this$handlers3, pageX - this.swipeState.startX);
9058
9114
  this.swipeState = null;
9059
9115
  }
9060
9116
  } else if (this.touchState[touchId]) {
@@ -9089,7 +9145,7 @@ class GestureStateMachine {
9089
9145
  if (this.swipeState) {
9090
9146
  if (this.swipeState.touchId === touchId) {
9091
9147
  var _this$handlers$onSwip4, _this$handlers4;
9092
- (_this$handlers$onSwip4 = (_this$handlers4 = this.handlers).onSwipeEnd) == null ? void 0 : _this$handlers$onSwip4.call(_this$handlers4, 0);
9148
+ (_this$handlers$onSwip4 = (_this$handlers4 = this.handlers).onSwipeEnd) == null || _this$handlers$onSwip4.call(_this$handlers4, 0);
9093
9149
  this.swipeState = null;
9094
9150
  }
9095
9151
  } else if (this.touchState[touchId]) {
@@ -10033,7 +10089,7 @@ class ProvidedKeypad extends React.Component {
10033
10089
  setKeyHandler: this.setKeyHandler,
10034
10090
  getDOMNode: this.getDOMNode
10035
10091
  });
10036
- (_this$props$onElement = (_this$props = this.props).onElementMounted) == null ? void 0 : _this$props$onElement.call(_this$props, elementWithDispatchMethods);
10092
+ (_this$props$onElement = (_this$props = this.props).onElementMounted) == null || _this$props$onElement.call(_this$props, elementWithDispatchMethods);
10037
10093
  };
10038
10094
  this.onDismiss = () => {
10039
10095
  var _this$props$onDismiss, _this$props2;
@@ -10043,7 +10099,7 @@ class ProvidedKeypad extends React.Component {
10043
10099
  virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
10044
10100
  }
10045
10101
  });
10046
- (_this$props$onDismiss = (_this$props2 = this.props).onDismiss) == null ? void 0 : _this$props$onDismiss.call(_this$props2);
10102
+ (_this$props$onDismiss = (_this$props2 = this.props).onDismiss) == null || _this$props$onDismiss.call(_this$props2);
10047
10103
  };
10048
10104
  this.store = createStore();
10049
10105
  }