@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 +12 -0
- package/dist/components/input/math-input.d.ts +2 -2
- package/dist/es/index.js +96 -54
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +121 -75
- package/dist/index.js.map +1 -1
- package/dist/utils.d.ts +12 -0
- package/package.json +1 -1
- package/src/components/input/math-input.tsx +71 -40
- package/src/components/keypad/__tests__/keypad.test.tsx +37 -0
- package/src/components/keypad/shared-keys.tsx +6 -1
- package/src/index.ts +1 -0
- package/src/utils.test.ts +33 -0
- package/src/utils.ts +45 -1
- package/tsconfig-build.tsbuildinfo +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { default as DesktopKeypad } from "./components/keypad";
|
|
|
13
13
|
export { KeypadContext, StatefulKeypadContextProvider, } from "./components/keypad-context";
|
|
14
14
|
export { keypadElementPropType } from "./components/prop-types";
|
|
15
15
|
export type { KeypadAPI, KeypadConfiguration } from "./types";
|
|
16
|
+
export { convertDotToTimesByLocale } from "./utils";
|
|
16
17
|
export type { default as Keys } from "./data/keys";
|
|
17
18
|
export { KeyArray } from "./data/keys";
|
|
18
19
|
export { default as KeyConfigs } from "./data/key-configs";
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
|
|
|
48
48
|
|
|
49
49
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
50
50
|
const libName = "@khanacademy/math-input";
|
|
51
|
-
const libVersion = "16.
|
|
51
|
+
const libVersion = "16.4.0";
|
|
52
52
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
53
53
|
|
|
54
54
|
function _extends() {
|
|
@@ -131,6 +131,49 @@ class View extends React__namespace.Component {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* KeypadContext provides a way to the Keypad and Perseus Renderers to
|
|
136
|
+
* communicate.
|
|
137
|
+
*
|
|
138
|
+
* The StatefulKeypadContextProvider wraps the application
|
|
139
|
+
* while KeypadContext.Consumer wraps things that need this state:
|
|
140
|
+
* - mobile keypad usages
|
|
141
|
+
* - Perseus Renderers (Server/Item/Article)
|
|
142
|
+
*/
|
|
143
|
+
// @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>'.
|
|
144
|
+
const KeypadContext = /*#__PURE__*/React__namespace.createContext({
|
|
145
|
+
setKeypadActive: keypadActive => {},
|
|
146
|
+
keypadActive: false,
|
|
147
|
+
setKeypadElement: keypadElement => {},
|
|
148
|
+
keypadElement: null,
|
|
149
|
+
setRenderer: renderer => {},
|
|
150
|
+
renderer: null,
|
|
151
|
+
setScrollableElement: scrollableElement => {},
|
|
152
|
+
scrollableElement: null
|
|
153
|
+
});
|
|
154
|
+
function StatefulKeypadContextProvider(props) {
|
|
155
|
+
// whether or not to display the keypad
|
|
156
|
+
const [keypadActive, setKeypadActive] = React.useState(false);
|
|
157
|
+
// used to communicate between the keypad and the Renderer
|
|
158
|
+
const [keypadElement, setKeypadElement] = React.useState();
|
|
159
|
+
// this is a KeypadContextRendererInterface from Perseus
|
|
160
|
+
const [renderer, setRenderer] = React.useState();
|
|
161
|
+
const [scrollableElement, setScrollableElement] = React.useState();
|
|
162
|
+
const memoizedValue = React.useMemo(() => ({
|
|
163
|
+
keypadActive,
|
|
164
|
+
setKeypadActive,
|
|
165
|
+
keypadElement,
|
|
166
|
+
setKeypadElement,
|
|
167
|
+
renderer,
|
|
168
|
+
setRenderer,
|
|
169
|
+
scrollableElement,
|
|
170
|
+
setScrollableElement
|
|
171
|
+
}), [keypadActive, setKeypadActive, keypadElement, setKeypadElement, renderer, setRenderer, scrollableElement, setScrollableElement]);
|
|
172
|
+
return /*#__PURE__*/React__namespace.createElement(KeypadContext.Provider, {
|
|
173
|
+
value: memoizedValue
|
|
174
|
+
}, props.children);
|
|
175
|
+
}
|
|
176
|
+
|
|
134
177
|
/**
|
|
135
178
|
* Common parameters used to style components.
|
|
136
179
|
*/
|
|
@@ -821,6 +864,30 @@ const DecimalSeparator = {
|
|
|
821
864
|
// - Some languages/locales use different decimal separators than the ones
|
|
822
865
|
// listed here. Much of the Arab world uses U+066C.
|
|
823
866
|
const decimalSeparator = i18n.getDecimalSeparator() === "," ? DecimalSeparator.COMMA : DecimalSeparator.PERIOD;
|
|
867
|
+
const CDOT_ONLY = ["az", "cs", "da", "de", "hu", "hy", "kk", "ky", "lt", "lv", "nb", "sk", "sr", "sv", "uz"];
|
|
868
|
+
const TIMES_ONLY = ["fr", "tr", "pt-pt"];
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* convertDotToTimes (aka `times`) is an option the content creators have to
|
|
872
|
+
* use × (TIMES) rather than · (CDOT) for multiplication (for younger learners).
|
|
873
|
+
* Some locales _only_ use one or the other for all multiplication regardless
|
|
874
|
+
* of age.
|
|
875
|
+
*
|
|
876
|
+
* convertDotToTimesByLocale overrides convertDotToTimes for those locales.
|
|
877
|
+
*
|
|
878
|
+
* @param {boolean} convertDotToTimes - the setting set by content creators
|
|
879
|
+
* @returns {boolean} - true to convert to × (TIMES), false to use · (CDOT)
|
|
880
|
+
*/
|
|
881
|
+
function convertDotToTimesByLocale(convertDotToTimes) {
|
|
882
|
+
const locale = i18n.getLocale();
|
|
883
|
+
if (CDOT_ONLY.includes(locale)) {
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
if (TIMES_ONLY.includes(locale)) {
|
|
887
|
+
return true;
|
|
888
|
+
}
|
|
889
|
+
return convertDotToTimes;
|
|
890
|
+
}
|
|
824
891
|
|
|
825
892
|
function handleLeftArrow(mathField, cursor) {
|
|
826
893
|
// If we're inside a function, and just after the left parentheses, we
|
|
@@ -1522,6 +1589,7 @@ class MathInput extends React__namespace.Component {
|
|
|
1522
1589
|
if (!this._container.contains(evt.target)) {
|
|
1523
1590
|
if (this.props.keypadElement && this.props.keypadElement.getDOMNode()) {
|
|
1524
1591
|
const [x, y] = [evt.clientX, evt.clientY];
|
|
1592
|
+
|
|
1525
1593
|
// We only want to blur if the click is above the keypad,
|
|
1526
1594
|
// to the left of the keypad, or to the right of the keypad.
|
|
1527
1595
|
// The reasoning for not blurring for any clicks below the keypad is
|
|
@@ -1846,7 +1914,7 @@ class MathInput extends React__namespace.Component {
|
|
|
1846
1914
|
context: this.mathField.contextForCursor()
|
|
1847
1915
|
});
|
|
1848
1916
|
};
|
|
1849
|
-
handleTouchStart = e => {
|
|
1917
|
+
handleTouchStart = (e, keypadActive, setKeypadActive) => {
|
|
1850
1918
|
e.stopPropagation();
|
|
1851
1919
|
|
|
1852
1920
|
// Hide the cursor handle on touch start, if the handle itself isn't
|
|
@@ -1865,6 +1933,11 @@ class MathInput extends React__namespace.Component {
|
|
|
1865
1933
|
this._insertCursorAtClosestNode(touch.clientX, touch.clientY);
|
|
1866
1934
|
}
|
|
1867
1935
|
|
|
1936
|
+
// If we're already focused, but the keypad isn't active, activate it.
|
|
1937
|
+
if (this.state.focused && !keypadActive) {
|
|
1938
|
+
setKeypadActive(true);
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1868
1941
|
// Trigger a focus event, if we're not already focused.
|
|
1869
1942
|
if (!this.state.focused) {
|
|
1870
1943
|
this.focus();
|
|
@@ -1874,7 +1947,7 @@ class MathInput extends React__namespace.Component {
|
|
|
1874
1947
|
// We want to allow the user to be able to focus the input via click
|
|
1875
1948
|
// when using ChromeOS third-party browsers that use mobile user agents,
|
|
1876
1949
|
// but don't actually simulate touch events.
|
|
1877
|
-
handleClick = e => {
|
|
1950
|
+
handleClick = (e, keypadActive, setKeypadActive) => {
|
|
1878
1951
|
e.stopPropagation();
|
|
1879
1952
|
|
|
1880
1953
|
// Hide the cursor handle on click
|
|
@@ -1891,6 +1964,11 @@ class MathInput extends React__namespace.Component {
|
|
|
1891
1964
|
this._insertCursorAtClosestNode(e.clientX, e.clientY);
|
|
1892
1965
|
}
|
|
1893
1966
|
|
|
1967
|
+
// If we're already focused, but the keypad isn't active, activate it.
|
|
1968
|
+
if (this.state.focused && !keypadActive) {
|
|
1969
|
+
setKeypadActive(true);
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1894
1972
|
// Trigger a focus event, if we're not already focused.
|
|
1895
1973
|
if (!this.state.focused) {
|
|
1896
1974
|
this.focus();
|
|
@@ -2114,34 +2192,44 @@ class MathInput extends React__namespace.Component {
|
|
|
2114
2192
|
// TODO(diedra): Fix the bug that is causing Android to require a two finger tap
|
|
2115
2193
|
// to the open the keyboard, and then remove the second half of this label.
|
|
2116
2194
|
const ariaLabel = i18n__namespace._("Math input box") + " " + i18n__namespace._("Tap with one or two fingers to open keyboard");
|
|
2117
|
-
return /*#__PURE__*/React__namespace.createElement(
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2195
|
+
return /*#__PURE__*/React__namespace.createElement(KeypadContext.Consumer, null, _ref => {
|
|
2196
|
+
let {
|
|
2197
|
+
keypadActive,
|
|
2198
|
+
setKeypadActive
|
|
2199
|
+
} = _ref;
|
|
2200
|
+
return /*#__PURE__*/React__namespace.createElement(View, {
|
|
2201
|
+
style: styles$7.input,
|
|
2202
|
+
onTouchStart: e => {
|
|
2203
|
+
this.handleTouchStart(e, keypadActive, setKeypadActive);
|
|
2204
|
+
},
|
|
2205
|
+
onTouchMove: this.handleTouchMove,
|
|
2206
|
+
onTouchEnd: this.handleTouchEnd,
|
|
2207
|
+
onClick: e => {
|
|
2208
|
+
this.handleClick(e, keypadActive, setKeypadActive);
|
|
2209
|
+
},
|
|
2210
|
+
role: "textbox",
|
|
2211
|
+
ariaLabel: ariaLabel
|
|
2212
|
+
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
2213
|
+
className: "keypad-input"
|
|
2214
|
+
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'number | undefined'.
|
|
2215
|
+
,
|
|
2216
|
+
tabIndex: "0",
|
|
2217
|
+
ref: node => {
|
|
2218
|
+
this.inputRef = node;
|
|
2219
|
+
},
|
|
2220
|
+
onKeyUp: this.handleKeyUp
|
|
2221
|
+
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
2222
|
+
ref: node => {
|
|
2223
|
+
this._mathContainer = ReactDOM__default["default"].findDOMNode(node);
|
|
2224
|
+
},
|
|
2225
|
+
style: innerStyle
|
|
2226
|
+
})), focused && handle.visible && /*#__PURE__*/React__namespace.createElement(CursorHandle, _extends({}, handle, {
|
|
2227
|
+
onTouchStart: this.onCursorHandleTouchStart,
|
|
2228
|
+
onTouchMove: this.onCursorHandleTouchMove,
|
|
2229
|
+
onTouchEnd: this.onCursorHandleTouchEnd,
|
|
2230
|
+
onTouchCancel: this.onCursorHandleTouchCancel
|
|
2231
|
+
})));
|
|
2232
|
+
});
|
|
2145
2233
|
}
|
|
2146
2234
|
}
|
|
2147
2235
|
const fontSizePt = 18;
|
|
@@ -5162,7 +5250,7 @@ function SharedKeys(props) {
|
|
|
5162
5250
|
coord: fractionCoord,
|
|
5163
5251
|
secondary: true
|
|
5164
5252
|
}), /*#__PURE__*/React__namespace.createElement(KeypadButton, {
|
|
5165
|
-
keyConfig: convertDotToTimes ? KeyConfigs.TIMES : KeyConfigs.CDOT,
|
|
5253
|
+
keyConfig: convertDotToTimesByLocale(!!convertDotToTimes) ? KeyConfigs.TIMES : KeyConfigs.CDOT,
|
|
5166
5254
|
onClickKey: onClickKey,
|
|
5167
5255
|
coord: [4, 1],
|
|
5168
5256
|
secondary: true
|
|
@@ -5349,49 +5437,6 @@ const styles$1 = aphrodite.StyleSheet.create({
|
|
|
5349
5437
|
}
|
|
5350
5438
|
});
|
|
5351
5439
|
|
|
5352
|
-
/**
|
|
5353
|
-
* KeypadContext provides a way to the Keypad and Perseus Renderers to
|
|
5354
|
-
* communicate.
|
|
5355
|
-
*
|
|
5356
|
-
* The StatefulKeypadContextProvider wraps the application
|
|
5357
|
-
* while KeypadContext.Consumer wraps things that need this state:
|
|
5358
|
-
* - mobile keypad usages
|
|
5359
|
-
* - Perseus Renderers (Server/Item/Article)
|
|
5360
|
-
*/
|
|
5361
|
-
// @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>'.
|
|
5362
|
-
const KeypadContext = /*#__PURE__*/React__namespace.createContext({
|
|
5363
|
-
setKeypadActive: keypadActive => {},
|
|
5364
|
-
keypadActive: false,
|
|
5365
|
-
setKeypadElement: keypadElement => {},
|
|
5366
|
-
keypadElement: null,
|
|
5367
|
-
setRenderer: renderer => {},
|
|
5368
|
-
renderer: null,
|
|
5369
|
-
setScrollableElement: scrollableElement => {},
|
|
5370
|
-
scrollableElement: null
|
|
5371
|
-
});
|
|
5372
|
-
function StatefulKeypadContextProvider(props) {
|
|
5373
|
-
// whether or not to display the keypad
|
|
5374
|
-
const [keypadActive, setKeypadActive] = React.useState(false);
|
|
5375
|
-
// used to communicate between the keypad and the Renderer
|
|
5376
|
-
const [keypadElement, setKeypadElement] = React.useState();
|
|
5377
|
-
// this is a KeypadContextRendererInterface from Perseus
|
|
5378
|
-
const [renderer, setRenderer] = React.useState();
|
|
5379
|
-
const [scrollableElement, setScrollableElement] = React.useState();
|
|
5380
|
-
const memoizedValue = React.useMemo(() => ({
|
|
5381
|
-
keypadActive,
|
|
5382
|
-
setKeypadActive,
|
|
5383
|
-
keypadElement,
|
|
5384
|
-
setKeypadElement,
|
|
5385
|
-
renderer,
|
|
5386
|
-
setRenderer,
|
|
5387
|
-
scrollableElement,
|
|
5388
|
-
setScrollableElement
|
|
5389
|
-
}), [keypadActive, setKeypadActive, keypadElement, setKeypadElement, renderer, setRenderer, scrollableElement, setScrollableElement]);
|
|
5390
|
-
return /*#__PURE__*/React__namespace.createElement(KeypadContext.Provider, {
|
|
5391
|
-
value: memoizedValue
|
|
5392
|
-
}, props.children);
|
|
5393
|
-
}
|
|
5394
|
-
|
|
5395
5440
|
function flatten(list) {
|
|
5396
5441
|
const result = [];
|
|
5397
5442
|
if (!list) {
|
|
@@ -5907,6 +5952,7 @@ exports.KeypadInput = MathInput;
|
|
|
5907
5952
|
exports.KeypadType = KeypadType;
|
|
5908
5953
|
exports.MobileKeypad = MobileKeypad;
|
|
5909
5954
|
exports.StatefulKeypadContextProvider = StatefulKeypadContextProvider;
|
|
5955
|
+
exports.convertDotToTimesByLocale = convertDotToTimesByLocale;
|
|
5910
5956
|
exports.createMathField = createMathField;
|
|
5911
5957
|
exports.getCursorContext = getCursorContext;
|
|
5912
5958
|
exports.keyTranslator = keyToMathquillMap;
|