@khanacademy/perseus 6.2.2 → 6.3.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 +20 -0
- package/dist/components/math-input.d.ts +4 -1
- package/dist/components/math-input.js.flow +4 -1
- package/dist/components/tex-buttons.d.ts +5 -1
- package/dist/components/tex-buttons.js.flow +1 -1
- package/dist/es/index.js +105 -139
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +105 -139
- package/dist/index.js.map +1 -1
- package/dist/strings.js +23 -0
- package/package.json +4 -5
- package/src/components/__tests__/math-input.test.tsx +60 -0
- package/src/components/math-input.tsx +88 -118
- package/src/components/tex-buttons.tsx +73 -12
- package/tsconfig-build.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @khanacademy/perseus
|
|
2
2
|
|
|
3
|
+
## 6.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [cf29ed88]
|
|
8
|
+
- @khanacademy/math-input@4.3.0
|
|
9
|
+
|
|
10
|
+
## 6.3.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- d497118e: MathInput exports tools for generating MathFields, replacing the need for direct MathQuill access
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [e7d21b67]
|
|
19
|
+
- Updated dependencies [d497118e]
|
|
20
|
+
- Updated dependencies [eceb4510]
|
|
21
|
+
- @khanacademy/math-input@4.2.0
|
|
22
|
+
|
|
3
23
|
## 6.2.2
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MathFieldInterface } from "@khanacademy/math-input";
|
|
1
2
|
import * as React from "react";
|
|
2
3
|
import type { ButtonSetsType } from "./tex-buttons";
|
|
3
4
|
type ButtonsVisibleType = "always" | "never" | "focused";
|
|
@@ -22,6 +23,8 @@ type State = {
|
|
|
22
23
|
};
|
|
23
24
|
declare class MathInput extends React.Component<Props, State> {
|
|
24
25
|
mouseDown: boolean;
|
|
26
|
+
__mathFieldWrapperRef: HTMLSpanElement | null;
|
|
27
|
+
__mathField: MathFieldInterface | null;
|
|
25
28
|
static defaultProps: DefaultProps;
|
|
26
29
|
state: State;
|
|
27
30
|
componentDidMount(): void;
|
|
@@ -32,7 +35,7 @@ declare class MathInput extends React.Component<Props, State> {
|
|
|
32
35
|
handleBlur: (arg1: React.FocusEvent) => void;
|
|
33
36
|
_shouldShowButtons: () => boolean;
|
|
34
37
|
insert: (value: any) => void;
|
|
35
|
-
mathField: (
|
|
38
|
+
mathField: () => MathFieldInterface | null;
|
|
36
39
|
focus: () => void;
|
|
37
40
|
blur: () => void;
|
|
38
41
|
render(): React.ReactNode;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Flowgen v1.21.0
|
|
5
5
|
* @flow
|
|
6
6
|
*/
|
|
7
|
+
import { MathFieldInterface } from "@khanacademy/math-input";
|
|
7
8
|
import * as React from "react";
|
|
8
9
|
import type { ButtonSetsType } from "./tex-buttons";
|
|
9
10
|
declare type ButtonsVisibleType = "always" | "never" | "focused";
|
|
@@ -28,6 +29,8 @@ declare type State = {|
|
|
|
28
29
|
|};
|
|
29
30
|
declare class MathInput extends React.Component<Props, State> {
|
|
30
31
|
mouseDown: boolean;
|
|
32
|
+
__mathFieldWrapperRef: HTMLSpanElement | null;
|
|
33
|
+
__mathField: MathFieldInterface | null;
|
|
31
34
|
static defaultProps: DefaultProps;
|
|
32
35
|
state: State;
|
|
33
36
|
componentDidMount(): void;
|
|
@@ -38,7 +41,7 @@ declare class MathInput extends React.Component<Props, State> {
|
|
|
38
41
|
handleBlur: (arg1: SyntheticFocusEvent<>) => void;
|
|
39
42
|
_shouldShowButtons: () => boolean;
|
|
40
43
|
insert: (value: any) => void;
|
|
41
|
-
mathField: (
|
|
44
|
+
mathField: () => MathFieldInterface | null;
|
|
42
45
|
focus: () => void;
|
|
43
46
|
blur: () => void;
|
|
44
47
|
render(): React.Node;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Keys } from "@khanacademy/math-input";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
type ButtonSet = (props: any) => [
|
|
3
|
+
type ButtonSet = (props: any) => [
|
|
4
|
+
React.ReactNode,
|
|
5
|
+
Keys,
|
|
6
|
+
string
|
|
7
|
+
];
|
|
4
8
|
type ButtonSets = {
|
|
5
9
|
readonly [key: string]: ReadonlyArray<ButtonSet>;
|
|
6
10
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Keys } from "@khanacademy/math-input";
|
|
8
8
|
import * as React from "react";
|
|
9
|
-
declare type ButtonSet = (props: any) => [React.Node, Keys];
|
|
9
|
+
declare type ButtonSet = (props: any) => [React.Node, Keys, string];
|
|
10
10
|
declare type ButtonSets = {
|
|
11
11
|
+[key: string]: $ReadOnlyArray<ButtonSet>,
|
|
12
12
|
};
|
package/dist/es/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as KAS from '@khanacademy/kas';
|
|
2
|
-
import { keyTranslator, KeypadInput, KeypadType, keypadElementPropType, LegacyKeypad } from '@khanacademy/math-input';
|
|
2
|
+
import { keyTranslator, createMathField, mathQuillInstance, KeypadInput, KeypadType, keypadElementPropType, LegacyKeypad } from '@khanacademy/math-input';
|
|
3
3
|
import * as PerseusLinter from '@khanacademy/perseus-linter';
|
|
4
4
|
import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
|
|
5
5
|
import * as i18n from '@khanacademy/wonder-blocks-i18n';
|
|
@@ -8,7 +8,6 @@ import * as React from 'react';
|
|
|
8
8
|
import { useState, useEffect, useRef } from 'react';
|
|
9
9
|
import _, { debounce } from 'underscore';
|
|
10
10
|
import $ from 'jquery';
|
|
11
|
-
import MathQuill from 'mathquill';
|
|
12
11
|
import * as ReactDOM from 'react-dom';
|
|
13
12
|
import ReactDOM__default from 'react-dom';
|
|
14
13
|
import PropTypes from 'prop-types';
|
|
@@ -138,10 +137,10 @@ const symbStyle = {
|
|
|
138
137
|
const basic = [() => [/*#__PURE__*/React.createElement("span", {
|
|
139
138
|
key: "plus",
|
|
140
139
|
style: slightlyBig
|
|
141
|
-
}, "+"), "PLUS"], () => [/*#__PURE__*/React.createElement("span", {
|
|
140
|
+
}, "+"), "PLUS", i18n._("Plus")], () => [/*#__PURE__*/React.createElement("span", {
|
|
142
141
|
key: "minus",
|
|
143
142
|
style: prettyBig
|
|
144
|
-
}, "-"), "MINUS"],
|
|
143
|
+
}, "-"), "MINUS", i18n._("Minus")],
|
|
145
144
|
// TODO(joel) - display as \cdot when appropriate
|
|
146
145
|
props => {
|
|
147
146
|
const {
|
|
@@ -151,12 +150,12 @@ props => {
|
|
|
151
150
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
152
151
|
key: "times",
|
|
153
152
|
style: prettyBig
|
|
154
|
-
}, i18n.doNotTranslate("\\times")), "TIMES"];
|
|
153
|
+
}, i18n.doNotTranslate("\\times")), "TIMES", i18n._("Multiply")];
|
|
155
154
|
}
|
|
156
155
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
157
156
|
key: "times",
|
|
158
157
|
style: prettyBig
|
|
159
|
-
}, i18n.doNotTranslate("\\cdot")), "CDOT"];
|
|
158
|
+
}, i18n.doNotTranslate("\\cdot")), "CDOT", i18n._("Multiply")];
|
|
160
159
|
}, () => {
|
|
161
160
|
const {
|
|
162
161
|
TeX
|
|
@@ -164,7 +163,7 @@ props => {
|
|
|
164
163
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
165
164
|
key: "frac",
|
|
166
165
|
style: prettyBig
|
|
167
|
-
}, i18n.doNotTranslate("\\frac{x}{y}")), "FRAC"];
|
|
166
|
+
}, i18n.doNotTranslate("\\frac{x}{y}")), "FRAC", i18n._("Fraction, excluding the current expression")];
|
|
168
167
|
}];
|
|
169
168
|
const buttonSets = {
|
|
170
169
|
basic,
|
|
@@ -174,7 +173,7 @@ const buttonSets = {
|
|
|
174
173
|
} = getDependencies();
|
|
175
174
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
176
175
|
key: "div"
|
|
177
|
-
}, i18n.doNotTranslate("\\div")), "DIVIDE"];
|
|
176
|
+
}, i18n.doNotTranslate("\\div")), "DIVIDE", i18n._("Divide")];
|
|
178
177
|
}]),
|
|
179
178
|
trig: [() => {
|
|
180
179
|
const {
|
|
@@ -182,21 +181,21 @@ const buttonSets = {
|
|
|
182
181
|
} = getDependencies();
|
|
183
182
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
184
183
|
key: "sin"
|
|
185
|
-
}, i18n.doNotTranslate("\\sin")), "SIN"];
|
|
184
|
+
}, i18n.doNotTranslate("\\sin")), "SIN", i18n._("Sine")];
|
|
186
185
|
}, () => {
|
|
187
186
|
const {
|
|
188
187
|
TeX
|
|
189
188
|
} = getDependencies();
|
|
190
189
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
191
190
|
key: "cos"
|
|
192
|
-
}, i18n.doNotTranslate("\\cos")), "COS"];
|
|
191
|
+
}, i18n.doNotTranslate("\\cos")), "COS", i18n._("Cosine")];
|
|
193
192
|
}, () => {
|
|
194
193
|
const {
|
|
195
194
|
TeX
|
|
196
195
|
} = getDependencies();
|
|
197
196
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
198
197
|
key: "tan"
|
|
199
|
-
}, i18n.doNotTranslate("\\tan")), "TAN"];
|
|
198
|
+
}, i18n.doNotTranslate("\\tan")), "TAN", i18n._("Tangent")];
|
|
200
199
|
}, () => {
|
|
201
200
|
const {
|
|
202
201
|
TeX
|
|
@@ -204,7 +203,7 @@ const buttonSets = {
|
|
|
204
203
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
205
204
|
key: "theta",
|
|
206
205
|
style: symbStyle
|
|
207
|
-
}, i18n.doNotTranslate("\\theta")), "THETA"];
|
|
206
|
+
}, i18n.doNotTranslate("\\theta")), "THETA", i18n._("Theta")];
|
|
208
207
|
}, () => {
|
|
209
208
|
const {
|
|
210
209
|
TeX
|
|
@@ -216,7 +215,7 @@ const buttonSets = {
|
|
|
216
215
|
}, i18n.doNotTranslate("phi")), /*#__PURE__*/React.createElement(TeX, {
|
|
217
216
|
key: "pi",
|
|
218
217
|
style: symbStyle
|
|
219
|
-
}, i18n.doNotTranslate("\\phi"))), "PHI"];
|
|
218
|
+
}, i18n.doNotTranslate("\\phi"))), "PHI", i18n._("Phi")];
|
|
220
219
|
}],
|
|
221
220
|
prealgebra: [() => {
|
|
222
221
|
const {
|
|
@@ -224,7 +223,7 @@ const buttonSets = {
|
|
|
224
223
|
} = getDependencies();
|
|
225
224
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
226
225
|
key: "sqrt"
|
|
227
|
-
}, i18n.doNotTranslate("\\sqrt{x}")), "SQRT"];
|
|
226
|
+
}, i18n.doNotTranslate("\\sqrt{x}")), "SQRT", i18n._("Square root")];
|
|
228
227
|
},
|
|
229
228
|
// TODO(joel) - how does desmos do this?
|
|
230
229
|
() => {
|
|
@@ -233,7 +232,7 @@ const buttonSets = {
|
|
|
233
232
|
} = getDependencies();
|
|
234
233
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
235
234
|
key: "nthroot"
|
|
236
|
-
}, i18n.doNotTranslate("\\sqrt[3]{x}")), "NTHROOT3"];
|
|
235
|
+
}, i18n.doNotTranslate("\\sqrt[3]{x}")), "NTHROOT3", i18n._("Cube root")];
|
|
237
236
|
}, () => {
|
|
238
237
|
const {
|
|
239
238
|
TeX
|
|
@@ -241,7 +240,7 @@ const buttonSets = {
|
|
|
241
240
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
242
241
|
key: "pow",
|
|
243
242
|
style: slightlyBig
|
|
244
|
-
}, i18n.doNotTranslate("a^b")), "POW"];
|
|
243
|
+
}, i18n.doNotTranslate("a^b")), "POW", i18n._("Exponent")];
|
|
245
244
|
}, () => {
|
|
246
245
|
const {
|
|
247
246
|
TeX
|
|
@@ -249,7 +248,7 @@ const buttonSets = {
|
|
|
249
248
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
250
249
|
key: "pi",
|
|
251
250
|
style: slightlyBig
|
|
252
|
-
}, i18n.doNotTranslate("\\pi")), "PI"];
|
|
251
|
+
}, i18n.doNotTranslate("\\pi")), "PI", i18n._("Pi")];
|
|
253
252
|
}],
|
|
254
253
|
logarithms: [() => {
|
|
255
254
|
const {
|
|
@@ -257,21 +256,21 @@ const buttonSets = {
|
|
|
257
256
|
} = getDependencies();
|
|
258
257
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
259
258
|
key: "log"
|
|
260
|
-
}, i18n.doNotTranslate("\\log")), "LOG"];
|
|
259
|
+
}, i18n.doNotTranslate("\\log")), "LOG", i18n._("Logarithm with base 10")];
|
|
261
260
|
}, () => {
|
|
262
261
|
const {
|
|
263
262
|
TeX
|
|
264
263
|
} = getDependencies();
|
|
265
264
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
266
265
|
key: "ln"
|
|
267
|
-
}, i18n.doNotTranslate("\\ln")), "LN"];
|
|
266
|
+
}, i18n.doNotTranslate("\\ln")), "LN", i18n._("Natural logarithm")];
|
|
268
267
|
}, () => {
|
|
269
268
|
const {
|
|
270
269
|
TeX
|
|
271
270
|
} = getDependencies();
|
|
272
271
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
273
272
|
key: "log_b"
|
|
274
|
-
}, i18n.doNotTranslate("\\log_b")), "LOG_B"];
|
|
273
|
+
}, i18n.doNotTranslate("\\log_b")), "LOG_B", i18n._("Logarithm with custom base")];
|
|
275
274
|
}],
|
|
276
275
|
"basic relations": [() => {
|
|
277
276
|
const {
|
|
@@ -279,21 +278,21 @@ const buttonSets = {
|
|
|
279
278
|
} = getDependencies();
|
|
280
279
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
281
280
|
key: "eq"
|
|
282
|
-
}, "="), "EQUAL"];
|
|
281
|
+
}, "="), "EQUAL", i18n._("Equals sign")];
|
|
283
282
|
}, () => {
|
|
284
283
|
const {
|
|
285
284
|
TeX
|
|
286
285
|
} = getDependencies();
|
|
287
286
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
288
287
|
key: "lt"
|
|
289
|
-
}, i18n.doNotTranslate("\\lt")), "LT"];
|
|
288
|
+
}, i18n.doNotTranslate("\\lt")), "LT", i18n._("Less than sign")];
|
|
290
289
|
}, () => {
|
|
291
290
|
const {
|
|
292
291
|
TeX
|
|
293
292
|
} = getDependencies();
|
|
294
293
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
295
294
|
key: "gt"
|
|
296
|
-
}, i18n.doNotTranslate("\\gt")), "GT"];
|
|
295
|
+
}, i18n.doNotTranslate("\\gt")), "GT", i18n._("Greater than sign")];
|
|
297
296
|
}],
|
|
298
297
|
"advanced relations": [() => {
|
|
299
298
|
const {
|
|
@@ -301,21 +300,21 @@ const buttonSets = {
|
|
|
301
300
|
} = getDependencies();
|
|
302
301
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
303
302
|
key: "neq"
|
|
304
|
-
}, i18n.doNotTranslate("\\neq")), "NEQ"];
|
|
303
|
+
}, i18n.doNotTranslate("\\neq")), "NEQ", i18n._("Not-equals sign")];
|
|
305
304
|
}, () => {
|
|
306
305
|
const {
|
|
307
306
|
TeX
|
|
308
307
|
} = getDependencies();
|
|
309
308
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
310
309
|
key: "leq"
|
|
311
|
-
}, i18n.doNotTranslate("\\leq")), "LEQ"];
|
|
310
|
+
}, i18n.doNotTranslate("\\leq")), "LEQ", i18n._("Less than or equal to sign")];
|
|
312
311
|
}, () => {
|
|
313
312
|
const {
|
|
314
313
|
TeX
|
|
315
314
|
} = getDependencies();
|
|
316
315
|
return [/*#__PURE__*/React.createElement(TeX, {
|
|
317
316
|
key: "geq"
|
|
318
|
-
}, i18n.doNotTranslate("\\geq")), "GEQ"];
|
|
317
|
+
}, i18n.doNotTranslate("\\geq")), "GEQ", i18n._("Greater than or equal to sign")];
|
|
319
318
|
}]
|
|
320
319
|
};
|
|
321
320
|
const buttonSetsPropType = PropTypes.arrayOf(PropTypes.oneOf(_(buttonSets).keys()));
|
|
@@ -337,7 +336,8 @@ class TexButtons extends React.Component {
|
|
|
337
336
|
// @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'. | TS2339 - Property 'key' does not exist on type 'boolean | ReactChild | ReactFragment | ReactPortal'.
|
|
338
337
|
,
|
|
339
338
|
key: symbol[0].key,
|
|
340
|
-
type: "button"
|
|
339
|
+
type: "button",
|
|
340
|
+
"aria-label": symbol[2]
|
|
341
341
|
}, symbol[0]);
|
|
342
342
|
}));
|
|
343
343
|
const buttonPopup = _(buttonRows).map((row, i) => {
|
|
@@ -372,6 +372,8 @@ class MathInput extends React.Component {
|
|
|
372
372
|
constructor(...args) {
|
|
373
373
|
super(...args);
|
|
374
374
|
this.mouseDown = void 0;
|
|
375
|
+
this.__mathFieldWrapperRef = null;
|
|
376
|
+
this.__mathField = null;
|
|
375
377
|
this.state = {
|
|
376
378
|
focused: false
|
|
377
379
|
};
|
|
@@ -426,12 +428,11 @@ class MathInput extends React.Component {
|
|
|
426
428
|
return this.state.focused;
|
|
427
429
|
};
|
|
428
430
|
this.insert = value => {
|
|
429
|
-
// @ts-expect-error [FEI-5003] - TS2554 - Expected 1 arguments, but got 0.
|
|
430
431
|
const input = this.mathField();
|
|
431
432
|
const inputModifier = customKeyTranslator[value];
|
|
432
433
|
if (inputModifier) {
|
|
433
434
|
inputModifier(input, value);
|
|
434
|
-
input.focus();
|
|
435
|
+
input == null ? void 0 : input.focus();
|
|
435
436
|
return;
|
|
436
437
|
}
|
|
437
438
|
|
|
@@ -442,131 +443,98 @@ class MathInput extends React.Component {
|
|
|
442
443
|
if (_(value).isFunction()) {
|
|
443
444
|
value(input);
|
|
444
445
|
} else if (value[0] === "\\") {
|
|
445
|
-
input.cmd(value).focus();
|
|
446
|
+
input == null ? void 0 : input.cmd(value).focus();
|
|
446
447
|
} else {
|
|
447
|
-
input.write(value).focus();
|
|
448
|
+
input == null ? void 0 : input.write(value).focus();
|
|
449
|
+
}
|
|
450
|
+
input == null ? void 0 : input.focus();
|
|
451
|
+
};
|
|
452
|
+
this.mathField = () => {
|
|
453
|
+
if (!this.__mathField && this.__mathFieldWrapperRef) {
|
|
454
|
+
// Initialize MathQuill.MathField instance
|
|
455
|
+
this.__mathField = createMathField(this.__mathFieldWrapperRef, baseConfig => _extends({}, baseConfig, {
|
|
456
|
+
handlers: {
|
|
457
|
+
edit: mathField => {
|
|
458
|
+
// This handler is guaranteed to be called on change, but
|
|
459
|
+
// unlike React it sometimes generates false positives.
|
|
460
|
+
// One of these is on initialization (with an empty string
|
|
461
|
+
// value), so we have to guard against that below.
|
|
462
|
+
let value = mathField.latex();
|
|
463
|
+
|
|
464
|
+
// Provide a MathQuill-compatible way to generate the
|
|
465
|
+
// not-equals sign without pasting unicode or typing TeX
|
|
466
|
+
value = value.replace(/<>/g, "\\ne");
|
|
467
|
+
|
|
468
|
+
// Use the specified symbol to represent multiplication
|
|
469
|
+
// TODO(alex): Add an option to disallow variables, in
|
|
470
|
+
// which case 'x' should get converted to '\\times'
|
|
471
|
+
if (this.props.convertDotToTimes) {
|
|
472
|
+
value = value.replace(/\\cdot/g, "\\times");
|
|
473
|
+
|
|
474
|
+
// Preserve cursor position in the common case:
|
|
475
|
+
// typing '*' to insert a multiplication sign.
|
|
476
|
+
// We do this by modifying internal MathQuill state
|
|
477
|
+
// directly, instead of waiting for `.latex()` to be
|
|
478
|
+
// called in `componentDidUpdate()`.
|
|
479
|
+
const left = mathField.__controller.cursor[mathQuillInstance.L];
|
|
480
|
+
if (left && left.ctrlSeq === "\\cdot ") {
|
|
481
|
+
mathField.__controller.backspace();
|
|
482
|
+
mathField.cmd("\\times");
|
|
483
|
+
}
|
|
484
|
+
} else {
|
|
485
|
+
value = value.replace(/\\times/g, "\\cdot");
|
|
486
|
+
}
|
|
487
|
+
if (this.props.value !== value) {
|
|
488
|
+
this.props.onChange(value);
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
enter: () => {
|
|
492
|
+
// NOTE(kevinb): This isn't how answers to exercises are
|
|
493
|
+
// submitted. The actual mechanism for this can be found
|
|
494
|
+
// in exercise-problem-template.jsx, see `handleSubmit`.
|
|
495
|
+
|
|
496
|
+
// This handler is called when the user presses the enter
|
|
497
|
+
// key. Since this isn't an actual <input> element, we have
|
|
498
|
+
// to manually trigger the usually automatic form submit.
|
|
499
|
+
if (this.__mathFieldWrapperRef) {
|
|
500
|
+
$(this.__mathFieldWrapperRef).submit();
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
upOutOf: mathField => {
|
|
504
|
+
// This handler is called when the user presses the up
|
|
505
|
+
// arrow key, but there is nowhere in the expression to go
|
|
506
|
+
// up to (no numerator or exponent). For ease of use,
|
|
507
|
+
// interpret this as an attempt to create an exponent.
|
|
508
|
+
mathField.typedText("^");
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}));
|
|
448
512
|
}
|
|
449
|
-
|
|
450
|
-
};
|
|
451
|
-
this.mathField = options => {
|
|
452
|
-
// The MathQuill API is now "versioned" through its own "InterVer"
|
|
453
|
-
// system.
|
|
454
|
-
// See: https://github.com/mathquill/mathquill/pull/459
|
|
455
|
-
const MQ = MathQuill.getInterface(2);
|
|
456
|
-
|
|
457
|
-
// MathQuill.MathField takes a DOM node, MathQuill-ifies it if it's
|
|
458
|
-
// seeing that node for the first time, then returns the associated
|
|
459
|
-
// MathQuill object for that node. It is stable - will always return
|
|
460
|
-
// the same object when called on the same DOM node.
|
|
461
|
-
// eslint-disable-next-line react/no-string-refs
|
|
462
|
-
return MQ.MathField(ReactDOM__default.findDOMNode(this.refs.mathinput), options);
|
|
513
|
+
return this.__mathField;
|
|
463
514
|
};
|
|
464
515
|
this.focus = () => {
|
|
465
|
-
|
|
466
|
-
this.mathField().focus();
|
|
516
|
+
var _this$mathField;
|
|
517
|
+
(_this$mathField = this.mathField()) == null ? void 0 : _this$mathField.focus();
|
|
467
518
|
this.setState({
|
|
468
519
|
focused: true
|
|
469
520
|
});
|
|
470
521
|
};
|
|
471
522
|
this.blur = () => {
|
|
472
|
-
|
|
473
|
-
this.mathField().blur();
|
|
523
|
+
var _this$mathField2;
|
|
524
|
+
(_this$mathField2 = this.mathField()) == null ? void 0 : _this$mathField2.blur();
|
|
474
525
|
this.setState({
|
|
475
526
|
focused: false
|
|
476
527
|
});
|
|
477
528
|
};
|
|
478
529
|
}
|
|
479
530
|
componentDidMount() {
|
|
531
|
+
var _this$mathField3;
|
|
480
532
|
window.addEventListener("mousedown", this.handleMouseDown);
|
|
481
533
|
window.addEventListener("mouseup", this.handleMouseUp);
|
|
482
|
-
let initialized = false;
|
|
483
|
-
|
|
484
|
-
// Initialize MathQuill.MathField instance
|
|
485
|
-
this.mathField({
|
|
486
|
-
// LaTeX commands that, when typed, are immediately replaced by the
|
|
487
|
-
// appropriate symbol. This does not include ln, log, or any of the
|
|
488
|
-
// trig functions; those are always interpreted as commands.
|
|
489
|
-
autoCommands: "pi theta phi sqrt nthroot",
|
|
490
|
-
// Pop the cursor out of super/subscripts on arithmetic operators
|
|
491
|
-
// or (in)equalities.
|
|
492
|
-
charsThatBreakOutOfSupSub: "+-*/=<>≠≤≥",
|
|
493
|
-
// Prevent excessive super/subscripts or fractions from being
|
|
494
|
-
// created without operands, e.g. when somebody holds down a key
|
|
495
|
-
supSubsRequireOperand: true,
|
|
496
|
-
// The name of this option is somewhat misleading, as tabbing in
|
|
497
|
-
// MathQuill breaks you out of a nested context (fraction/script)
|
|
498
|
-
// if you're in one, but moves focus to the next input if you're
|
|
499
|
-
// not. Spaces (with this option enabled) are just ignored in the
|
|
500
|
-
// latter case.
|
|
501
|
-
//
|
|
502
|
-
// TODO(alex): In order to allow inputting mixed numbers, we will
|
|
503
|
-
// have to accept spaces in certain cases. The desired behavior is
|
|
504
|
-
// still to escape nested contexts if currently in one, but to
|
|
505
|
-
// insert a space if not (we don't expect mixed numbers in nested
|
|
506
|
-
// contexts). We should also limit to one consecutive space.
|
|
507
|
-
spaceBehavesLikeTab: true,
|
|
508
|
-
handlers: {
|
|
509
|
-
edited: mathField => {
|
|
510
|
-
// This handler is guaranteed to be called on change, but
|
|
511
|
-
// unlike React it sometimes generates false positives.
|
|
512
|
-
// One of these is on initialization (with an empty string
|
|
513
|
-
// value), so we have to guard against that below.
|
|
514
|
-
let value = mathField.latex();
|
|
515
|
-
|
|
516
|
-
// Provide a MathQuill-compatible way to generate the
|
|
517
|
-
// not-equals sign without pasting unicode or typing TeX
|
|
518
|
-
value = value.replace(/<>/g, "\\ne");
|
|
519
|
-
|
|
520
|
-
// Use the specified symbol to represent multiplication
|
|
521
|
-
// TODO(alex): Add an option to disallow variables, in
|
|
522
|
-
// which case 'x' should get converted to '\\times'
|
|
523
|
-
if (this.props.convertDotToTimes) {
|
|
524
|
-
value = value.replace(/\\cdot/g, "\\times");
|
|
525
|
-
|
|
526
|
-
// Preserve cursor position in the common case:
|
|
527
|
-
// typing '*' to insert a multiplication sign.
|
|
528
|
-
// We do this by modifying internal MathQuill state
|
|
529
|
-
// directly, instead of waiting for `.latex()` to be
|
|
530
|
-
// called in `componentDidUpdate()`.
|
|
531
|
-
const left = mathField.__controller.cursor[MathQuill.L];
|
|
532
|
-
if (left && left.ctrlSeq === "\\cdot ") {
|
|
533
|
-
mathField.__controller.backspace();
|
|
534
|
-
mathField.cmd("\\times");
|
|
535
|
-
}
|
|
536
|
-
} else {
|
|
537
|
-
value = value.replace(/\\times/g, "\\cdot");
|
|
538
|
-
}
|
|
539
|
-
if (initialized && this.props.value !== value) {
|
|
540
|
-
this.props.onChange(value);
|
|
541
|
-
}
|
|
542
|
-
},
|
|
543
|
-
enter: () => {
|
|
544
|
-
// NOTE(kevinb): This isn't how answers to exercises are
|
|
545
|
-
// submitted. The actual mechanism for this can be found
|
|
546
|
-
// in exercise-problem-template.jsx, see `handleSubmit`.
|
|
547
|
-
|
|
548
|
-
// This handler is called when the user presses the enter
|
|
549
|
-
// key. Since this isn't an actual <input> element, we have
|
|
550
|
-
// to manually trigger the usually automatic form submit.
|
|
551
|
-
// eslint-disable-next-line react/no-string-refs
|
|
552
|
-
// @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call. | TS2339 - Property 'submit' does not exist on type 'JQueryStatic'.
|
|
553
|
-
$(ReactDOM__default.findDOMNode(this.refs.mathinput)).submit();
|
|
554
|
-
},
|
|
555
|
-
upOutOf: mathField => {
|
|
556
|
-
// This handler is called when the user presses the up
|
|
557
|
-
// arrow key, but there is nowhere in the expression to go
|
|
558
|
-
// up to (no numerator or exponent). For ease of use,
|
|
559
|
-
// interpret this as an attempt to create an exponent.
|
|
560
|
-
mathField.typedText("^");
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
534
|
|
|
565
535
|
// Ideally, we would be able to pass an initial value directly into
|
|
566
|
-
// the constructor
|
|
567
|
-
|
|
568
|
-
this.mathField().latex(this.props.value);
|
|
569
|
-
initialized = true;
|
|
536
|
+
// the constructor
|
|
537
|
+
(_this$mathField3 = this.mathField()) == null ? void 0 : _this$mathField3.latex(this.props.value);
|
|
570
538
|
}
|
|
571
539
|
componentWillUnmount() {
|
|
572
540
|
window.removeEventListener("mousedown", this.handleMouseDown);
|
|
@@ -609,10 +577,8 @@ class MathInput extends React.Component {
|
|
|
609
577
|
display: "inline-block"
|
|
610
578
|
}
|
|
611
579
|
}, /*#__PURE__*/React.createElement("span", {
|
|
612
|
-
className: className
|
|
613
|
-
|
|
614
|
-
,
|
|
615
|
-
ref: "mathinput",
|
|
580
|
+
className: className,
|
|
581
|
+
ref: ref => this.__mathFieldWrapperRef = ref,
|
|
616
582
|
"aria-label": this.props.labelText,
|
|
617
583
|
onFocus: this.handleFocus,
|
|
618
584
|
onBlur: this.handleBlur
|