@khanacademy/math-input 16.2.0 → 16.4.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,17 @@
1
1
  # @khanacademy/math-input
2
2
 
3
+ ## 16.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#871](https://github.com/Khan/perseus/pull/871) [`610ebba2`](https://github.com/Khan/perseus/commit/610ebba29ab8b2ee4ddf4879f8c8b87993f29b20) Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! - Ensured that tapping an already focused math input will reopen the keypad if it is closed.
8
+
9
+ ## 16.3.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#863](https://github.com/Khan/perseus/pull/863) [`f910bd72`](https://github.com/Khan/perseus/commit/f910bd72fc5cbf88a1a00d57f8aefa8eea2c755d) Thanks [@handeyeco](https://github.com/handeyeco)! - Localize the multiplication symbol in MathInput
14
+
3
15
  ## 16.2.0
4
16
 
5
17
  ### Minor Changes
@@ -82,8 +82,8 @@ declare class MathInput extends React.Component<Props, State> {
82
82
  * @param {number} y - the y coordinate in the viewport
83
83
  */
84
84
  _insertCursorAtClosestNode: (arg1: number, arg2: number) => void;
85
- handleTouchStart: (arg1: React.TouchEvent<HTMLDivElement>) => void;
86
- handleClick: (e: React.MouseEvent<HTMLDivElement>) => void;
85
+ handleTouchStart: (e: React.TouchEvent<HTMLDivElement>, keypadActive: boolean, setKeypadActive: (keypadActive: boolean) => void) => void;
86
+ handleClick: (e: React.MouseEvent<HTMLDivElement>, keypadActive: boolean, setKeypadActive: (keypadActive: boolean) => void) => void;
87
87
  handleTouchMove: (arg1: React.TouchEvent<HTMLDivElement>) => void;
88
88
  handleTouchEnd: (arg1: React.TouchEvent<HTMLDivElement>) => void;
89
89
  /**
package/dist/es/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
2
2
  import Color from '@khanacademy/wonder-blocks-color';
3
3
  import * as i18n from '@khanacademy/wonder-blocks-i18n';
4
- import { getDecimalSeparator } from '@khanacademy/wonder-blocks-i18n';
4
+ import { getDecimalSeparator, getLocale } from '@khanacademy/wonder-blocks-i18n';
5
5
  import { entries } from '@khanacademy/wonder-stuff-core';
6
6
  import { StyleSheet, css } from 'aphrodite';
7
7
  import * as React from 'react';
8
- import { useEffect, useState, useMemo } from 'react';
8
+ import { useState, useMemo, useEffect } from 'react';
9
9
  import ReactDOM from 'react-dom';
10
10
  import $ from 'jquery';
11
11
  import MathQuill from 'mathquill';
@@ -17,7 +17,7 @@ import PropTypes from 'prop-types';
17
17
 
18
18
  // This file is processed by a Rollup plugin (replace) to inject the production
19
19
  const libName = "@khanacademy/math-input";
20
- const libVersion = "16.2.0";
20
+ const libVersion = "16.4.0";
21
21
  addLibraryVersionToPerseusDebug(libName, libVersion);
22
22
 
23
23
  function _extends() {
@@ -100,6 +100,49 @@ View.styles = StyleSheet.create({
100
100
  }
101
101
  });
102
102
 
103
+ /**
104
+ * KeypadContext provides a way to the Keypad and Perseus Renderers to
105
+ * communicate.
106
+ *
107
+ * The StatefulKeypadContextProvider wraps the application
108
+ * while KeypadContext.Consumer wraps things that need this state:
109
+ * - mobile keypad usages
110
+ * - Perseus Renderers (Server/Item/Article)
111
+ */
112
+ // @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>'.
113
+ const KeypadContext = /*#__PURE__*/React.createContext({
114
+ setKeypadActive: keypadActive => {},
115
+ keypadActive: false,
116
+ setKeypadElement: keypadElement => {},
117
+ keypadElement: null,
118
+ setRenderer: renderer => {},
119
+ renderer: null,
120
+ setScrollableElement: scrollableElement => {},
121
+ scrollableElement: null
122
+ });
123
+ function StatefulKeypadContextProvider(props) {
124
+ // whether or not to display the keypad
125
+ const [keypadActive, setKeypadActive] = useState(false);
126
+ // used to communicate between the keypad and the Renderer
127
+ const [keypadElement, setKeypadElement] = useState();
128
+ // this is a KeypadContextRendererInterface from Perseus
129
+ const [renderer, setRenderer] = useState();
130
+ const [scrollableElement, setScrollableElement] = useState();
131
+ const memoizedValue = useMemo(() => ({
132
+ keypadActive,
133
+ setKeypadActive,
134
+ keypadElement,
135
+ setKeypadElement,
136
+ renderer,
137
+ setRenderer,
138
+ scrollableElement,
139
+ setScrollableElement
140
+ }), [keypadActive, setKeypadActive, keypadElement, setKeypadElement, renderer, setRenderer, scrollableElement, setScrollableElement]);
141
+ return /*#__PURE__*/React.createElement(KeypadContext.Provider, {
142
+ value: memoizedValue
143
+ }, props.children);
144
+ }
145
+
103
146
  /**
104
147
  * Common parameters used to style components.
105
148
  */
@@ -789,6 +832,30 @@ const DecimalSeparator = {
789
832
  // - Some languages/locales use different decimal separators than the ones
790
833
  // listed here. Much of the Arab world uses U+066C.
791
834
  const decimalSeparator = getDecimalSeparator() === "," ? DecimalSeparator.COMMA : DecimalSeparator.PERIOD;
835
+ const CDOT_ONLY = ["az", "cs", "da", "de", "hu", "hy", "kk", "ky", "lt", "lv", "nb", "sk", "sr", "sv", "uz"];
836
+ const TIMES_ONLY = ["fr", "tr", "pt-pt"];
837
+
838
+ /**
839
+ * convertDotToTimes (aka `times`) is an option the content creators have to
840
+ * use × (TIMES) rather than · (CDOT) for multiplication (for younger learners).
841
+ * Some locales _only_ use one or the other for all multiplication regardless
842
+ * of age.
843
+ *
844
+ * convertDotToTimesByLocale overrides convertDotToTimes for those locales.
845
+ *
846
+ * @param {boolean} convertDotToTimes - the setting set by content creators
847
+ * @returns {boolean} - true to convert to × (TIMES), false to use · (CDOT)
848
+ */
849
+ function convertDotToTimesByLocale(convertDotToTimes) {
850
+ const locale = getLocale();
851
+ if (CDOT_ONLY.includes(locale)) {
852
+ return false;
853
+ }
854
+ if (TIMES_ONLY.includes(locale)) {
855
+ return true;
856
+ }
857
+ return convertDotToTimes;
858
+ }
792
859
 
793
860
  function handleLeftArrow(mathField, cursor) {
794
861
  // If we're inside a function, and just after the left parentheses, we
@@ -1670,7 +1737,7 @@ class MathInput extends React.Component {
1670
1737
  context: this.mathField.contextForCursor()
1671
1738
  });
1672
1739
  };
1673
- this.handleTouchStart = e => {
1740
+ this.handleTouchStart = (e, keypadActive, setKeypadActive) => {
1674
1741
  e.stopPropagation();
1675
1742
 
1676
1743
  // Hide the cursor handle on touch start, if the handle itself isn't
@@ -1689,6 +1756,11 @@ class MathInput extends React.Component {
1689
1756
  this._insertCursorAtClosestNode(touch.clientX, touch.clientY);
1690
1757
  }
1691
1758
 
1759
+ // If we're already focused, but the keypad isn't active, activate it.
1760
+ if (this.state.focused && !keypadActive) {
1761
+ setKeypadActive(true);
1762
+ }
1763
+
1692
1764
  // Trigger a focus event, if we're not already focused.
1693
1765
  if (!this.state.focused) {
1694
1766
  this.focus();
@@ -1697,7 +1769,7 @@ class MathInput extends React.Component {
1697
1769
  // We want to allow the user to be able to focus the input via click
1698
1770
  // when using ChromeOS third-party browsers that use mobile user agents,
1699
1771
  // but don't actually simulate touch events.
1700
- this.handleClick = e => {
1772
+ this.handleClick = (e, keypadActive, setKeypadActive) => {
1701
1773
  e.stopPropagation();
1702
1774
 
1703
1775
  // Hide the cursor handle on click
@@ -1714,6 +1786,11 @@ class MathInput extends React.Component {
1714
1786
  this._insertCursorAtClosestNode(e.clientX, e.clientY);
1715
1787
  }
1716
1788
 
1789
+ // If we're already focused, but the keypad isn't active, activate it.
1790
+ if (this.state.focused && !keypadActive) {
1791
+ setKeypadActive(true);
1792
+ }
1793
+
1717
1794
  // Trigger a focus event, if we're not already focused.
1718
1795
  if (!this.state.focused) {
1719
1796
  this.focus();
@@ -2012,6 +2089,7 @@ class MathInput extends React.Component {
2012
2089
  if (!this._container.contains(evt.target)) {
2013
2090
  if (this.props.keypadElement && this.props.keypadElement.getDOMNode()) {
2014
2091
  const [x, y] = [evt.clientX, evt.clientY];
2092
+
2015
2093
  // We only want to blur if the click is above the keypad,
2016
2094
  // to the left of the keypad, or to the right of the keypad.
2017
2095
  // The reasoning for not blurring for any clicks below the keypad is
@@ -2077,12 +2155,19 @@ class MathInput extends React.Component {
2077
2155
  // TODO(diedra): Fix the bug that is causing Android to require a two finger tap
2078
2156
  // to the open the keyboard, and then remove the second half of this label.
2079
2157
  const ariaLabel = i18n._("Math input box") + " " + i18n._("Tap with one or two fingers to open keyboard");
2080
- return /*#__PURE__*/React.createElement(View, {
2158
+ return /*#__PURE__*/React.createElement(KeypadContext.Consumer, null, ({
2159
+ keypadActive,
2160
+ setKeypadActive
2161
+ }) => /*#__PURE__*/React.createElement(View, {
2081
2162
  style: styles$7.input,
2082
- onTouchStart: this.handleTouchStart,
2163
+ onTouchStart: e => {
2164
+ this.handleTouchStart(e, keypadActive, setKeypadActive);
2165
+ },
2083
2166
  onTouchMove: this.handleTouchMove,
2084
2167
  onTouchEnd: this.handleTouchEnd,
2085
- onClick: this.handleClick,
2168
+ onClick: e => {
2169
+ this.handleClick(e, keypadActive, setKeypadActive);
2170
+ },
2086
2171
  role: "textbox",
2087
2172
  ariaLabel: ariaLabel
2088
2173
  }, /*#__PURE__*/React.createElement("div", {
@@ -2104,7 +2189,7 @@ class MathInput extends React.Component {
2104
2189
  onTouchMove: this.onCursorHandleTouchMove,
2105
2190
  onTouchEnd: this.onCursorHandleTouchEnd,
2106
2191
  onTouchCancel: this.onCursorHandleTouchCancel
2107
- })));
2192
+ }))));
2108
2193
  }
2109
2194
  }
2110
2195
  MathInput.defaultProps = {
@@ -4879,7 +4964,7 @@ function SharedKeys(props) {
4879
4964
  coord: fractionCoord,
4880
4965
  secondary: true
4881
4966
  }), /*#__PURE__*/React.createElement(KeypadButton, {
4882
- keyConfig: convertDotToTimes ? KeyConfigs.TIMES : KeyConfigs.CDOT,
4967
+ keyConfig: convertDotToTimesByLocale(!!convertDotToTimes) ? KeyConfigs.TIMES : KeyConfigs.CDOT,
4883
4968
  onClickKey: onClickKey,
4884
4969
  coord: [4, 1],
4885
4970
  secondary: true
@@ -5067,49 +5152,6 @@ const styles$1 = StyleSheet.create({
5067
5152
  }
5068
5153
  });
5069
5154
 
5070
- /**
5071
- * KeypadContext provides a way to the Keypad and Perseus Renderers to
5072
- * communicate.
5073
- *
5074
- * The StatefulKeypadContextProvider wraps the application
5075
- * while KeypadContext.Consumer wraps things that need this state:
5076
- * - mobile keypad usages
5077
- * - Perseus Renderers (Server/Item/Article)
5078
- */
5079
- // @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>'.
5080
- const KeypadContext = /*#__PURE__*/React.createContext({
5081
- setKeypadActive: keypadActive => {},
5082
- keypadActive: false,
5083
- setKeypadElement: keypadElement => {},
5084
- keypadElement: null,
5085
- setRenderer: renderer => {},
5086
- renderer: null,
5087
- setScrollableElement: scrollableElement => {},
5088
- scrollableElement: null
5089
- });
5090
- function StatefulKeypadContextProvider(props) {
5091
- // whether or not to display the keypad
5092
- const [keypadActive, setKeypadActive] = useState(false);
5093
- // used to communicate between the keypad and the Renderer
5094
- const [keypadElement, setKeypadElement] = useState();
5095
- // this is a KeypadContextRendererInterface from Perseus
5096
- const [renderer, setRenderer] = useState();
5097
- const [scrollableElement, setScrollableElement] = useState();
5098
- const memoizedValue = useMemo(() => ({
5099
- keypadActive,
5100
- setKeypadActive,
5101
- keypadElement,
5102
- setKeypadElement,
5103
- renderer,
5104
- setRenderer,
5105
- scrollableElement,
5106
- setScrollableElement
5107
- }), [keypadActive, setKeypadActive, keypadElement, setKeypadElement, renderer, setRenderer, scrollableElement, setScrollableElement]);
5108
- return /*#__PURE__*/React.createElement(KeypadContext.Provider, {
5109
- value: memoizedValue
5110
- }, props.children);
5111
- }
5112
-
5113
5155
  function flatten(list) {
5114
5156
  const result = [];
5115
5157
  if (!list) {
@@ -5619,5 +5661,5 @@ let KeypadType = /*#__PURE__*/function (KeypadType) {
5619
5661
  return KeypadType;
5620
5662
  }({});
5621
5663
 
5622
- export { CursorContext, Keypad as DesktopKeypad, KeyArray, KeyConfigs, KeypadContext, MathInput as KeypadInput, KeypadType, MobileKeypad, StatefulKeypadContextProvider, createMathField, getCursorContext, keyToMathquillMap as keyTranslator, keypadElementPropType, libVersion, mathQuillInstance };
5664
+ export { CursorContext, Keypad as DesktopKeypad, KeyArray, KeyConfigs, KeypadContext, MathInput as KeypadInput, KeypadType, MobileKeypad, StatefulKeypadContextProvider, convertDotToTimesByLocale, createMathField, getCursorContext, keyToMathquillMap as keyTranslator, keypadElementPropType, libVersion, mathQuillInstance };
5623
5665
  //# sourceMappingURL=index.js.map