@khanacademy/math-input 10.1.1 → 12.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/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @khanacademy/math-input
2
2
 
3
+ ## 12.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - a383823d: Bump @khanacademy/wonder-stuff-core to v1.5.1 (which adds needed support for `isTruthy` helper function)
8
+
9
+ ## 12.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - fa5f463b: Added onAnalyticsEvent prop to the LegacyKeypad (aka ProvidedKeypad). You must now pass in this prop, which is a function, to handle analytics events originating from the legacy keypad.
14
+ - 6d8ede65: Added `onAnalyticsEvent` prop to MobileKeypad to pipe out Perseus analytics
15
+
16
+ ### Minor Changes
17
+
18
+ - fa1bb6b4: Implemented some focus management fixes and improved the full-math-input story.
19
+
20
+ ### Patch Changes
21
+
22
+ - 3afc0da2: Check for ResizeObserver before using it
23
+
24
+ ## 11.0.0
25
+
26
+ ### Major Changes
27
+
28
+ - 2af4f9fa: Switch from using ProvideKeypad in ArticleRenderer to passing the keypad element down instead
29
+
30
+ ### Patch Changes
31
+
32
+ - Updated dependencies [2af4f9fa]
33
+ - @khanacademy/perseus-core@1.0.0
34
+
3
35
  ## 10.1.1
4
36
 
5
37
  ### Patch Changes
@@ -7,6 +39,7 @@
7
39
  - 3f7be05a: Updated Keypad V2 TabbarItemType to KeypadPageType as a more accurate description.
8
40
  - 810c7bd9: Resize letter SVGs
9
41
  - b161d004: Ensured that the keypad is hidden from screen readers when it is closed.
42
+ - a99a9ca4: Fix direction types for MathQuill interface
10
43
 
11
44
  ## 10.1.0
12
45
 
@@ -50,7 +50,7 @@ declare class MathInput extends React.Component<Props, State> {
50
50
  _updateCursorHandle: (arg1?: boolean) => void;
51
51
  _hideCursorHandle: () => void;
52
52
  _handleScroll: () => void;
53
- blur: () => void;
53
+ blur: (callPropsOnBlur: boolean) => void;
54
54
  focus: () => void;
55
55
  /**
56
56
  * Tries to determine which DOM node to place the cursor next to based on
@@ -1,7 +1,7 @@
1
1
  import type Key from "../../data/keys";
2
2
  export interface MathQuillInterface {
3
- L: "L";
4
- R: "R";
3
+ L: -1;
4
+ R: 1;
5
5
  /**
6
6
  * Creates an editable MathQuill initialized with the contents of the HTML
7
7
  * element and returns a MathField object.
@@ -2,6 +2,7 @@ import * as React from "react";
2
2
  import ReactDOM from "react-dom";
3
3
  import type Key from "../../data/keys";
4
4
  import type { Cursor, KeypadConfiguration, KeyHandler, KeypadAPI } from "../../types";
5
+ import type { AnalyticsEventHandlerFn } from "@khanacademy/perseus-core";
5
6
  import type { StyleType } from "@khanacademy/wonder-blocks-core";
6
7
  /**
7
8
  * This is the v2 equivalent of v1's ProvidedKeypad. It follows the same
@@ -17,6 +18,7 @@ type Props = {
17
18
  onElementMounted?: (arg1: any) => void;
18
19
  onDismiss?: () => void;
19
20
  style?: StyleType;
21
+ onAnalyticsEvent: AnalyticsEventHandlerFn;
20
22
  };
21
23
  type State = {
22
24
  active: boolean;
@@ -7,12 +7,13 @@
7
7
  * ExerciseFooter.
8
8
  */
9
9
  import * as React from "react";
10
- import type { RendererInterface } from "@khanacademy/perseus-core";
10
+ import type { KeypadAPI } from "../types";
11
+ import type { KeypadContextRendererInterface } from "@khanacademy/perseus-core";
11
12
  type KeypadContext = {
12
- setKeypadElement: (keypadElement?: HTMLElement | null | undefined) => void;
13
- keypadElement: HTMLElement | null | undefined;
14
- setRenderer: (renderer?: RendererInterface | null | undefined) => void;
15
- renderer: RendererInterface | null | undefined;
13
+ setKeypadElement: (keypadElement?: KeypadAPI) => void;
14
+ keypadElement: KeypadAPI | null | undefined;
15
+ setRenderer: (renderer?: KeypadContextRendererInterface | null | undefined) => void;
16
+ renderer: KeypadContextRendererInterface | null | undefined;
16
17
  setScrollableElement: (scrollableElement?: HTMLElement | null | undefined) => void;
17
18
  scrollableElement: HTMLElement | null | undefined;
18
19
  };
@@ -1,11 +1,13 @@
1
1
  import * as React from "react";
2
2
  import ReactDOM from "react-dom";
3
3
  import type { Cursor, KeypadConfiguration, KeyHandler, KeypadAPI } from "../../types";
4
+ import type { AnalyticsEventHandlerFn } from "@khanacademy/perseus-core";
4
5
  import type { StyleType } from "@khanacademy/wonder-blocks-core";
5
6
  type Props = {
6
7
  onElementMounted?: (arg1: any) => void;
7
8
  onDismiss?: () => void;
8
9
  style?: StyleType;
10
+ onAnalyticsEvent: AnalyticsEventHandlerFn;
9
11
  };
10
12
  declare class ProvidedKeypad extends React.Component<Props> implements KeypadAPI {
11
13
  store: any;
@@ -1,10 +1,12 @@
1
1
  /// <reference types="react" />
2
+ import type { AnalyticsEventHandlerFn } from "@khanacademy/perseus-core";
2
3
  import type { StyleType } from "@khanacademy/wonder-blocks-core";
3
4
  type Props = {
4
5
  onElementMounted?: (arg1: any) => void;
5
6
  onDismiss?: () => void;
6
7
  style?: StyleType;
7
8
  useV2Keypad?: boolean;
9
+ onAnalyticsEvent: AnalyticsEventHandlerFn;
8
10
  };
9
11
  declare function KeypadSwitch(props: Props): JSX.Element;
10
12
  export default KeypadSwitch;
package/dist/es/index.js CHANGED
@@ -1553,9 +1553,11 @@ class MathInput extends React.Component {
1553
1553
  this._hideCursorHandle();
1554
1554
  }
1555
1555
  };
1556
- this.blur = () => {
1556
+ this.blur = callPropsOnBlur => {
1557
1557
  this.mathField.blur();
1558
- this.props.onBlur && this.props.onBlur();
1558
+ if (callPropsOnBlur) {
1559
+ this.props.onBlur && this.props.onBlur();
1560
+ }
1559
1561
  this.setState({
1560
1562
  focused: false,
1561
1563
  handle: {
@@ -1564,7 +1566,7 @@ class MathInput extends React.Component {
1564
1566
  });
1565
1567
  };
1566
1568
  this.focus = () => {
1567
- var _this$props$keypadEle2;
1569
+ var _this$props$keypadEle2, _this$props;
1568
1570
  // Pass this component's handleKey method to the keypad so it can call
1569
1571
  // it whenever it needs to trigger a keypress action.
1570
1572
  (_this$props$keypadEle2 = this.props.keypadElement) == null ? void 0 : _this$props$keypadEle2.setKeyHandler(key => {
@@ -1590,7 +1592,7 @@ class MathInput extends React.Component {
1590
1592
  return cursor;
1591
1593
  });
1592
1594
  this.mathField.focus();
1593
- this.props.onFocus && this.props.onFocus();
1595
+ (_this$props = this.props) == null ? void 0 : _this$props.onFocus();
1594
1596
  this.setState({
1595
1597
  focused: true
1596
1598
  }, () => {
@@ -1600,13 +1602,14 @@ class MathInput extends React.Component {
1600
1602
  // Android Browser 4.3.
1601
1603
  setTimeout(() => {
1602
1604
  if (this._isMounted) {
1605
+ var _this$props$keypadEle3;
1603
1606
  // TODO(benkomalo): the keypad is animating at this point,
1604
1607
  // so we can't call _cacheKeypadBounds(), even though
1605
1608
  // it'd be nice to do so. It should probably be the case
1606
1609
  // that the higher level controller tells us when the
1607
1610
  // keypad is settled (then scrollIntoView wouldn't have
1608
1611
  // to make assumptions about that either).
1609
- const maybeKeypadNode = this.props.keypadElement && this.props.keypadElement.getDOMNode();
1612
+ const maybeKeypadNode = (_this$props$keypadEle3 = this.props.keypadElement) == null ? void 0 : _this$props$keypadEle3.getDOMNode();
1610
1613
  scrollIntoView(this._container, maybeKeypadNode);
1611
1614
  }
1612
1615
  });
@@ -2011,7 +2014,7 @@ class MathInput extends React.Component {
2011
2014
  // dismissal. This code needs to be generalized to handle
2012
2015
  // multi-touch.
2013
2016
  if (this.state.focused && this.didTouchOutside && !this.didScroll) {
2014
- this.blur();
2017
+ this.blur(true);
2015
2018
  }
2016
2019
  this.didTouchOutside = false;
2017
2020
  this.didScroll = false;
@@ -2087,13 +2090,18 @@ class MathInput extends React.Component {
2087
2090
  role: "textbox",
2088
2091
  ariaLabel: ariaLabel
2089
2092
  }, /*#__PURE__*/React.createElement("div", {
2090
- className: "keypad-input"
2091
- // @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'number | undefined'.
2092
- ,
2093
- tabIndex: "0",
2093
+ className: "keypad-input",
2094
+ tabIndex: 0,
2094
2095
  ref: node => {
2095
2096
  this.inputRef = node;
2096
2097
  },
2098
+ onFocus: () => {
2099
+ this.focus();
2100
+ },
2101
+ onBlur: () => {
2102
+ this._hideCursorHandle();
2103
+ this.blur(false);
2104
+ },
2097
2105
  onKeyUp: this.handleKeyUp
2098
2106
  }, /*#__PURE__*/React.createElement("div", {
2099
2107
  ref: node => {
@@ -5099,8 +5107,8 @@ class MobileKeypad extends React.Component {
5099
5107
  this._throttleResize = false;
5100
5108
  this.hasMounted = false;
5101
5109
  this.state = {
5102
- containerWidth: 0,
5103
- active: false
5110
+ active: false,
5111
+ containerWidth: 0
5104
5112
  };
5105
5113
  this._resize = () => {
5106
5114
  var _this$_containerRef$c;
@@ -5120,17 +5128,21 @@ class MobileKeypad extends React.Component {
5120
5128
  }, 100);
5121
5129
  };
5122
5130
  this.activate = () => {
5123
- this.setState({
5124
- active: true
5125
- });
5131
+ if (!this.state.active) {
5132
+ this.setState({
5133
+ active: true
5134
+ });
5135
+ }
5126
5136
  };
5127
5137
  this.dismiss = () => {
5128
- this.setState({
5129
- active: false
5130
- }, () => {
5131
- var _this$props$onDismiss, _this$props;
5132
- (_this$props$onDismiss = (_this$props = this.props).onDismiss) == null ? void 0 : _this$props$onDismiss.call(_this$props);
5133
- });
5138
+ if (this.state.active) {
5139
+ this.setState({
5140
+ active: false
5141
+ }, () => {
5142
+ var _this$props$onDismiss, _this$props;
5143
+ (_this$props$onDismiss = (_this$props = this.props).onDismiss) == null ? void 0 : _this$props$onDismiss.call(_this$props);
5144
+ });
5145
+ }
5134
5146
  };
5135
5147
  this.configure = (configuration, cb) => {
5136
5148
  this.setState({
@@ -5166,9 +5178,14 @@ class MobileKeypad extends React.Component {
5166
5178
  this._resize();
5167
5179
  window.addEventListener("resize", this._throttleResizeHandler);
5168
5180
  window.addEventListener("orientationchange", this._throttleResizeHandler);
5169
- this._containerResizeObserver = new ResizeObserver(this._throttleResizeHandler);
5170
- if (this._containerRef.current) {
5171
- this._containerResizeObserver.observe(this._containerRef.current);
5181
+
5182
+ // LC-1213: some common older browsers (as of 2023-09-07)
5183
+ // don't support ResizeObserver
5184
+ if ("ResizeObserver" in window) {
5185
+ this._containerResizeObserver = new window.ResizeObserver(this._throttleResizeHandler);
5186
+ if (this._containerRef.current) {
5187
+ this._containerResizeObserver.observe(this._containerRef.current);
5188
+ }
5172
5189
  }
5173
5190
  }
5174
5191
  componentWillUnMount() {
@@ -5226,10 +5243,8 @@ class MobileKeypad extends React.Component {
5226
5243
  (_this$props$onElement = (_this$props2 = this.props).onElementMounted) == null ? void 0 : _this$props$onElement.call(_this$props2, elementWithDispatchMethods);
5227
5244
  }
5228
5245
  }
5229
- }, /*#__PURE__*/React.createElement(Keypad$2
5230
- // TODO(jeremy)
5231
- , {
5232
- onAnalyticsEvent: async function () {},
5246
+ }, /*#__PURE__*/React.createElement(Keypad$2, {
5247
+ onAnalyticsEvent: this.props.onAnalyticsEvent,
5233
5248
  extraKeys: keypadConfig == null ? void 0 : keypadConfig.extraKeys,
5234
5249
  onClickKey: key => this._handleClickKey(key),
5235
5250
  cursorContext: cursor == null ? void 0 : cursor.context,
@@ -8344,9 +8359,14 @@ class KeypadContainer extends React.Component {
8344
8359
  // And update it on resize.
8345
8360
  window.addEventListener("resize", this._throttleResizeHandler);
8346
8361
  window.addEventListener("orientationchange", this._throttleResizeHandler);
8347
- this._containerResizeObserver = new ResizeObserver(this._throttleResizeHandler);
8348
- if (this._containerRef.current) {
8349
- this._containerResizeObserver.observe(this._containerRef.current);
8362
+
8363
+ // LC-1213: some common older browsers (as of 2023-09-07)
8364
+ // don't support ResizeObserver
8365
+ if ("ResizeObserver" in window) {
8366
+ this._containerResizeObserver = new window.ResizeObserver(this._throttleResizeHandler);
8367
+ if (this._containerRef.current) {
8368
+ this._containerResizeObserver.observe(this._containerRef.current);
8369
+ }
8350
8370
  }
8351
8371
  }
8352
8372
  UNSAFE_componentWillReceiveProps(nextProps) {
@@ -9694,6 +9714,13 @@ class ProvidedKeypad extends React.Component {
9694
9714
  store: this.store
9695
9715
  }, /*#__PURE__*/React.createElement(KeypadContainer$1, {
9696
9716
  onElementMounted: element => {
9717
+ this.props.onAnalyticsEvent({
9718
+ type: "math-input:keypad-opened",
9719
+ payload: {
9720
+ virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
9721
+ }
9722
+ });
9723
+
9697
9724
  // Append the dispatch methods that we want to expose
9698
9725
  // externally to the returned React element.
9699
9726
  const elementWithDispatchMethods = _extends({}, element, {
@@ -9704,9 +9731,17 @@ class ProvidedKeypad extends React.Component {
9704
9731
  setKeyHandler: this.setKeyHandler,
9705
9732
  getDOMNode: this.getDOMNode
9706
9733
  });
9707
- onElementMounted && onElementMounted(elementWithDispatchMethods);
9734
+ onElementMounted == null ? void 0 : onElementMounted(elementWithDispatchMethods);
9735
+ },
9736
+ onDismiss: () => {
9737
+ this.props.onAnalyticsEvent({
9738
+ type: "math-input:keypad-closed",
9739
+ payload: {
9740
+ virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
9741
+ }
9742
+ });
9743
+ onDismiss == null ? void 0 : onDismiss();
9708
9744
  },
9709
- onDismiss: onDismiss,
9710
9745
  style: style
9711
9746
  }));
9712
9747
  }
@@ -9719,6 +9754,10 @@ function KeypadSwitch(props) {
9719
9754
  } = props,
9720
9755
  rest = _objectWithoutPropertiesLoose(props, _excluded);
9721
9756
  const KeypadComponent = useV2Keypad ? MobileKeypad : ProvidedKeypad;
9757
+
9758
+ // Note: Although we pass the "onAnalyticsEvent" to both keypad components,
9759
+ // only the current one uses it. There's no point in instrumenting the
9760
+ // legacy keypad given that it's on its way out the door.
9722
9761
  return /*#__PURE__*/React.createElement(KeypadComponent, rest);
9723
9762
  }
9724
9763