@khanacademy/math-input 13.1.0 → 14.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,22 @@
1
1
  # @khanacademy/math-input
2
2
 
3
+ ## 14.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 4f8afadd: Fix provided-keypad so that it doesn't re-render unnecessarily.
8
+ - 7d8905b6: Removes "grid" role from keypad to un-muddle screen reader experience.
9
+ - 55d4cd00: Print package name and version when loaded in the page
10
+ - Updated dependencies [55d4cd00]
11
+ - @khanacademy/perseus-core@1.1.1
12
+
13
+ ## 14.0.0
14
+
15
+ ### Major Changes
16
+
17
+ - 14138bb0: Move StatefulKeypadContextProvider into math-input
18
+ - 14138bb0: Hoist keypad active state into keypad context
19
+
3
20
  ## 13.1.0
4
21
 
5
22
  ### Minor Changes
@@ -19,9 +19,10 @@ type Props = {
19
19
  onDismiss?: () => void;
20
20
  style?: StyleType;
21
21
  onAnalyticsEvent: AnalyticsEventHandlerFn;
22
+ setKeypadActive: (keypadActive: boolean) => void;
23
+ keypadActive: boolean;
22
24
  };
23
25
  type State = {
24
- active: boolean;
25
26
  containerWidth: number;
26
27
  hasBeenActivated: boolean;
27
28
  keypadConfig?: KeypadConfiguration;
@@ -1,21 +1,15 @@
1
1
  /**
2
- * KeypadContext provides a way to the Keypad and (Server)ItemRenderer to
2
+ * KeypadContext provides a way to the Keypad and Perseus Renderers to
3
3
  * communicate.
4
4
  *
5
- * The KeypadContext.Provider wraps the ExerciseFooter while KeypadContext.Consumer
6
- * wraps each (Server)ItemRenderer render site and the Keypad rendered in the
7
- * ExerciseFooter.
5
+ * The StatefulKeypadContextProvider wraps the application
6
+ * while KeypadContext.Consumer wraps things that need this state:
7
+ * - mobile keypad usages
8
+ * - Perseus Renderers (Server/Item/Article)
8
9
  */
9
10
  import * as React from "react";
10
- import type { KeypadAPI } from "../types";
11
- import type { KeypadContextRendererInterface } from "@khanacademy/perseus-core";
12
- type KeypadContext = {
13
- setKeypadElement: (keypadElement?: KeypadAPI) => void;
14
- keypadElement: KeypadAPI | null | undefined;
15
- setRenderer: (renderer?: KeypadContextRendererInterface | null | undefined) => void;
16
- renderer: KeypadContextRendererInterface | null | undefined;
17
- setScrollableElement: (scrollableElement?: HTMLElement | null | undefined) => void;
18
- scrollableElement: HTMLElement | null | undefined;
19
- };
20
- declare const context: React.Context<KeypadContext>;
21
- export default context;
11
+ import type { KeypadContextType } from "../types";
12
+ export declare const KeypadContext: React.Context<KeypadContextType>;
13
+ type Props = React.PropsWithChildren<unknown>;
14
+ export declare function StatefulKeypadContextProvider(props: Props): JSX.Element;
15
+ export {};
@@ -4,6 +4,8 @@ import type { Cursor, KeypadConfiguration, KeyHandler, KeypadAPI } from "../../t
4
4
  import type { AnalyticsEventHandlerFn } from "@khanacademy/perseus-core";
5
5
  import type { StyleType } from "@khanacademy/wonder-blocks-core";
6
6
  type Props = {
7
+ setKeypadActive: (keypadActive: boolean) => void;
8
+ keypadActive: boolean;
7
9
  onElementMounted?: (arg1: any) => void;
8
10
  onDismiss?: () => void;
9
11
  style?: StyleType;
@@ -12,12 +14,15 @@ type Props = {
12
14
  declare class ProvidedKeypad extends React.Component<Props> implements KeypadAPI {
13
15
  store: any;
14
16
  constructor(props: any);
17
+ componentDidUpdate(prevProps: any): void;
15
18
  activate: () => void;
16
19
  dismiss: () => void;
17
20
  configure: (configuration: KeypadConfiguration, cb: () => void) => void;
18
21
  setCursor: (cursor: Cursor) => void;
19
22
  setKeyHandler: (keyHandler: KeyHandler) => void;
20
23
  getDOMNode: () => ReturnType<typeof ReactDOM.findDOMNode>;
24
+ onElementMounted: (element: any) => void;
25
+ onDismiss: () => void;
21
26
  render(): React.ReactNode;
22
27
  }
23
28
  export default ProvidedKeypad;
package/dist/es/index.js CHANGED
@@ -1,10 +1,11 @@
1
+ import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
1
2
  import Color from '@khanacademy/wonder-blocks-color';
2
3
  import * as i18n from '@khanacademy/wonder-blocks-i18n';
3
4
  import { getDecimalSeparator } from '@khanacademy/wonder-blocks-i18n';
4
5
  import { entries } from '@khanacademy/wonder-stuff-core';
5
6
  import { StyleSheet, css } from 'aphrodite';
6
7
  import * as React from 'react';
7
- import { useEffect } from 'react';
8
+ import { useEffect, useState } from 'react';
8
9
  import ReactDOM from 'react-dom';
9
10
  import $ from 'jquery';
10
11
  import MathQuill from 'mathquill';
@@ -16,6 +17,11 @@ import PropTypes from 'prop-types';
16
17
  import { TransitionGroup, CSSTransition } from 'react-transition-group';
17
18
  import * as Redux from 'redux';
18
19
 
20
+ // This file is processed by a Rollup plugin (replace) to inject the production
21
+ const libName = "@khanacademy/math-input";
22
+ const libVersion = "14.0.1";
23
+ addLibraryVersionToPerseusDebug(libName, libVersion);
24
+
19
25
  function _extends() {
20
26
  _extends = Object.assign || function (target) {
21
27
  for (var i = 1; i < arguments.length; i++) {
@@ -5023,7 +5029,6 @@ function Keypad$2(props) {
5023
5029
  style: styles$d.keypadInnerContainer
5024
5030
  }, /*#__PURE__*/React.createElement(View$1, {
5025
5031
  style: [styles$d.keypadGrid, gridStyle],
5026
- role: "grid",
5027
5032
  tabIndex: 0,
5028
5033
  "aria-label": "Keypad"
5029
5034
  }, selectedPage === "Fractions" && /*#__PURE__*/React.createElement(FractionsPage, {
@@ -5085,17 +5090,6 @@ const styles$d = StyleSheet.create({
5085
5090
  }
5086
5091
  });
5087
5092
 
5088
- /**
5089
- * This is the v2 equivalent of v1's ProvidedKeypad. It follows the same
5090
- * external API so that it can be hot-swapped with the v1 keypad and
5091
- * is responsible for connecting the keypad with MathInput and the Renderer.
5092
- *
5093
- * Ideally this strategy of attaching methods on the class component for
5094
- * other components to call will be replaced props/callbacks since React
5095
- * doesn't support this type of code anymore (functional components
5096
- * can't have methods attached to them).
5097
- */
5098
-
5099
5093
  class MobileKeypad extends React.Component {
5100
5094
  constructor(...args) {
5101
5095
  super(...args);
@@ -5104,7 +5098,6 @@ class MobileKeypad extends React.Component {
5104
5098
  this._throttleResize = false;
5105
5099
  this.hasMounted = false;
5106
5100
  this.state = {
5107
- active: false,
5108
5101
  containerWidth: 0,
5109
5102
  hasBeenActivated: false
5110
5103
  };
@@ -5126,18 +5119,15 @@ class MobileKeypad extends React.Component {
5126
5119
  }, 100);
5127
5120
  };
5128
5121
  this.activate = () => {
5122
+ this.props.setKeypadActive(true);
5129
5123
  this.setState({
5130
- active: true,
5131
5124
  hasBeenActivated: true
5132
5125
  });
5133
5126
  };
5134
5127
  this.dismiss = () => {
5135
- this.setState({
5136
- active: false
5137
- }, () => {
5138
- var _this$props$onDismiss, _this$props;
5139
- (_this$props$onDismiss = (_this$props = this.props).onDismiss) == null ? void 0 : _this$props$onDismiss.call(_this$props);
5140
- });
5128
+ var _this$props$onDismiss, _this$props;
5129
+ this.props.setKeypadActive(false);
5130
+ (_this$props$onDismiss = (_this$props = this.props).onDismiss) == null ? void 0 : _this$props$onDismiss.call(_this$props);
5141
5131
  };
5142
5132
  this.configure = (configuration, cb) => {
5143
5133
  this.setState({
@@ -5202,10 +5192,10 @@ class MobileKeypad extends React.Component {
5202
5192
  }
5203
5193
  render() {
5204
5194
  const {
5195
+ keypadActive,
5205
5196
  style
5206
5197
  } = this.props;
5207
5198
  const {
5208
- active,
5209
5199
  hasBeenActivated,
5210
5200
  containerWidth,
5211
5201
  cursor,
@@ -5213,7 +5203,7 @@ class MobileKeypad extends React.Component {
5213
5203
  } = this.state;
5214
5204
  const containerStyle = [
5215
5205
  // internal styles
5216
- styles$c.keypadContainer, active && styles$c.activeKeypadContainer,
5206
+ styles$c.keypadContainer, keypadActive && styles$c.activeKeypadContainer,
5217
5207
  // styles passed as props
5218
5208
  ...(Array.isArray(style) ? style : [style])];
5219
5209
 
@@ -5222,7 +5212,7 @@ class MobileKeypad extends React.Component {
5222
5212
  // during the initial render.
5223
5213
  // Done inline (dynamicStyle) since stylesheets might not be loaded yet.
5224
5214
  let dynamicStyle = {};
5225
- if (!active && !hasBeenActivated) {
5215
+ if (!keypadActive && !hasBeenActivated) {
5226
5216
  dynamicStyle = {
5227
5217
  visibility: "hidden"
5228
5218
  };
@@ -5287,6 +5277,51 @@ const styles$c = StyleSheet.create({
5287
5277
  }
5288
5278
  });
5289
5279
 
5280
+ /**
5281
+ * KeypadContext provides a way to the Keypad and Perseus Renderers to
5282
+ * communicate.
5283
+ *
5284
+ * The StatefulKeypadContextProvider wraps the application
5285
+ * while KeypadContext.Consumer wraps things that need this state:
5286
+ * - mobile keypad usages
5287
+ * - Perseus Renderers (Server/Item/Article)
5288
+ */
5289
+ // @ts-expect-error - TS2322 - Type 'Context<{ setKeypadElement: (keypadElement: HTMLElement | null | undefined) => void; keypadElement: null; setRenderer: (renderer: RendererInterface | null | undefined) => void; renderer: null; setScrollableElement: (scrollableElement: HTMLElement | ... 1 more ... | undefined) => void; scrollableElement: null; }>' is not assignable to type 'Context<KeypadContext>'.
5290
+ const KeypadContext = /*#__PURE__*/React.createContext({
5291
+ setKeypadActive: keypadActive => {},
5292
+ keypadActive: false,
5293
+ setKeypadElement: keypadElement => {},
5294
+ keypadElement: null,
5295
+ setRenderer: renderer => {},
5296
+ renderer: null,
5297
+ setScrollableElement: scrollableElement => {},
5298
+ scrollableElement: null
5299
+ });
5300
+ function StatefulKeypadContextProvider(props) {
5301
+ // whether or not to display the keypad
5302
+ const [keypadActive, setKeypadActive] = useState(false);
5303
+ // used to communicate between the keypad and the Renderer
5304
+ const [keypadElement, setKeypadElement] = useState();
5305
+ // this is a KeypadContextRendererInterface from Perseus
5306
+ const [renderer, setRenderer] = useState();
5307
+ const [scrollableElement, setScrollableElement] = useState();
5308
+ return /*#__PURE__*/React.createElement(KeypadContext.Provider, {
5309
+ value: {
5310
+ setKeypadActive,
5311
+ keypadActive,
5312
+ setKeypadElement,
5313
+ keypadElement,
5314
+ setRenderer,
5315
+ renderer,
5316
+ // The scrollableElement options can likely be removed after
5317
+ // the exercises-package is officially deprecated. They don't appear
5318
+ // to be used anywhere except for the exercises-package and tests.
5319
+ setScrollableElement,
5320
+ scrollableElement
5321
+ }
5322
+ }, props.children);
5323
+ }
5324
+
5290
5325
  /**
5291
5326
  * A small triangular decal to sit in the corner of a parent component.
5292
5327
  */
@@ -9685,10 +9720,10 @@ class ProvidedKeypad extends React.Component {
9685
9720
  super(props);
9686
9721
  this.store = void 0;
9687
9722
  this.activate = () => {
9688
- this.store.dispatch(activateKeypad());
9723
+ this.props.setKeypadActive(true);
9689
9724
  };
9690
9725
  this.dismiss = () => {
9691
- this.store.dispatch(dismissKeypad());
9726
+ this.props.setKeypadActive(false);
9692
9727
  };
9693
9728
  this.configure = (configuration, cb) => {
9694
9729
  this.store.dispatch(configureKeypad(configuration));
@@ -9711,46 +9746,56 @@ class ProvidedKeypad extends React.Component {
9711
9746
  this.getDOMNode = () => {
9712
9747
  return ReactDOM.findDOMNode(this);
9713
9748
  };
9749
+ this.onElementMounted = element => {
9750
+ var _this$props$onElement, _this$props;
9751
+ this.props.onAnalyticsEvent({
9752
+ type: "math-input:keypad-opened",
9753
+ payload: {
9754
+ virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
9755
+ }
9756
+ });
9757
+
9758
+ // Append the dispatch methods that we want to expose
9759
+ // externally to the returned React element.
9760
+ const elementWithDispatchMethods = _extends({}, element, {
9761
+ activate: this.activate,
9762
+ dismiss: this.dismiss,
9763
+ configure: this.configure,
9764
+ setCursor: this.setCursor,
9765
+ setKeyHandler: this.setKeyHandler,
9766
+ getDOMNode: this.getDOMNode
9767
+ });
9768
+ (_this$props$onElement = (_this$props = this.props).onElementMounted) == null ? void 0 : _this$props$onElement.call(_this$props, elementWithDispatchMethods);
9769
+ };
9770
+ this.onDismiss = () => {
9771
+ var _this$props$onDismiss, _this$props2;
9772
+ this.props.onAnalyticsEvent({
9773
+ type: "math-input:keypad-closed",
9774
+ payload: {
9775
+ virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
9776
+ }
9777
+ });
9778
+ (_this$props$onDismiss = (_this$props2 = this.props).onDismiss) == null ? void 0 : _this$props$onDismiss.call(_this$props2);
9779
+ };
9714
9780
  this.store = createStore();
9715
9781
  }
9782
+ componentDidUpdate(prevProps) {
9783
+ if (this.props.keypadActive && !prevProps.keypadActive) {
9784
+ this.store.dispatch(activateKeypad());
9785
+ }
9786
+ if (!this.props.keypadActive && prevProps.keypadActive) {
9787
+ this.store.dispatch(dismissKeypad());
9788
+ }
9789
+ }
9716
9790
  render() {
9717
9791
  const {
9718
- onElementMounted,
9719
- onDismiss,
9720
9792
  style
9721
9793
  } = this.props;
9722
9794
  return /*#__PURE__*/React.createElement(Provider, {
9723
9795
  store: this.store
9724
9796
  }, /*#__PURE__*/React.createElement(KeypadContainer$1, {
9725
- onElementMounted: element => {
9726
- this.props.onAnalyticsEvent({
9727
- type: "math-input:keypad-opened",
9728
- payload: {
9729
- virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
9730
- }
9731
- });
9732
-
9733
- // Append the dispatch methods that we want to expose
9734
- // externally to the returned React element.
9735
- const elementWithDispatchMethods = _extends({}, element, {
9736
- activate: this.activate,
9737
- dismiss: this.dismiss,
9738
- configure: this.configure,
9739
- setCursor: this.setCursor,
9740
- setKeyHandler: this.setKeyHandler,
9741
- getDOMNode: this.getDOMNode
9742
- });
9743
- onElementMounted == null ? void 0 : onElementMounted(elementWithDispatchMethods);
9744
- },
9745
- onDismiss: () => {
9746
- this.props.onAnalyticsEvent({
9747
- type: "math-input:keypad-closed",
9748
- payload: {
9749
- virtualKeypadVersion: "MATH_INPUT_KEYPAD_V1"
9750
- }
9751
- });
9752
- onDismiss == null ? void 0 : onDismiss();
9753
- },
9797
+ onElementMounted: this.onElementMounted,
9798
+ onDismiss: this.onDismiss,
9754
9799
  style: style
9755
9800
  }));
9756
9801
  }
@@ -9767,27 +9812,17 @@ function KeypadSwitch(props) {
9767
9812
  // Note: Although we pass the "onAnalyticsEvent" to both keypad components,
9768
9813
  // only the current one uses it. There's no point in instrumenting the
9769
9814
  // legacy keypad given that it's on its way out the door.
9770
- return /*#__PURE__*/React.createElement(KeypadComponent, rest);
9815
+ return /*#__PURE__*/React.createElement(KeypadContext.Consumer, null, ({
9816
+ setKeypadActive,
9817
+ keypadActive
9818
+ }) => {
9819
+ return /*#__PURE__*/React.createElement(KeypadComponent, _extends({}, rest, {
9820
+ keypadActive: keypadActive,
9821
+ setKeypadActive: setKeypadActive
9822
+ }));
9823
+ });
9771
9824
  }
9772
9825
 
9773
- /**
9774
- * KeypadContext provides a way to the Keypad and (Server)ItemRenderer to
9775
- * communicate.
9776
- *
9777
- * The KeypadContext.Provider wraps the ExerciseFooter while KeypadContext.Consumer
9778
- * wraps each (Server)ItemRenderer render site and the Keypad rendered in the
9779
- * ExerciseFooter.
9780
- */
9781
- // @ts-expect-error - TS2322 - Type 'Context<{ setKeypadElement: (keypadElement: HTMLElement | null | undefined) => void; keypadElement: null; setRenderer: (renderer: RendererInterface | null | undefined) => void; renderer: null; setScrollableElement: (scrollableElement: HTMLElement | ... 1 more ... | undefined) => void; scrollableElement: null; }>' is not assignable to type 'Context<KeypadContext>'.
9782
- const context = /*#__PURE__*/React.createContext({
9783
- setKeypadElement: keypadElement => {},
9784
- keypadElement: null,
9785
- setRenderer: renderer => {},
9786
- renderer: null,
9787
- setScrollableElement: scrollableElement => {},
9788
- scrollableElement: null
9789
- });
9790
-
9791
9826
  /**
9792
9827
  * React PropTypes that may be shared between components.
9793
9828
  */
@@ -9803,5 +9838,5 @@ const keypadElementPropType = PropTypes.shape({
9803
9838
  getDOMNode: PropTypes.func.isRequired
9804
9839
  });
9805
9840
 
9806
- export { CursorContext, Keypad$2 as DesktopKeypad, KeyConfigs, context as KeypadContext, MathInput as KeypadInput, KeypadType, KeypadSwitch as MobileKeypad, createMathField, getCursorContext, keyToMathquillMap as keyTranslator, keypadElementPropType, mathQuillInstance };
9841
+ export { CursorContext, Keypad$2 as DesktopKeypad, KeyConfigs, KeypadContext, MathInput as KeypadInput, KeypadType, KeypadSwitch as MobileKeypad, StatefulKeypadContextProvider, createMathField, getCursorContext, keyToMathquillMap as keyTranslator, keypadElementPropType, libVersion, mathQuillInstance };
9807
9842
  //# sourceMappingURL=index.js.map