@khanacademy/math-input 16.5.1 → 17.0.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 +6 -0
- package/dist/components/input/math-wrapper.d.ts +3 -2
- package/dist/components/input/mathquill-helpers.d.ts +3 -4
- package/dist/components/input/mathquill-instance.d.ts +2 -2
- package/dist/components/input/mathquill-types.d.ts +4 -290
- package/dist/es/index.css +209 -104
- package/dist/es/index.js +62 -81
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +209 -104
- package/dist/index.js +62 -81
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/__tests__/integration.test.tsx +25 -13
- package/src/components/input/__tests__/mathquill.test.ts +37 -35
- package/src/components/input/__tests__/test-math-wrapper.ts +1 -2
- package/src/components/input/math-input.tsx +3 -18
- package/src/components/input/math-wrapper.ts +9 -16
- package/src/components/input/mathquill-helpers.ts +15 -26
- package/src/components/input/mathquill-instance.ts +71 -71
- package/src/components/input/mathquill-types.ts +4 -334
- package/src/components/key-handlers/handle-arrow.ts +1 -2
- package/src/components/key-handlers/handle-backspace.ts +6 -7
- package/src/components/key-handlers/handle-exponent.ts +1 -2
- package/src/components/key-handlers/handle-jump-out.ts +1 -2
- package/src/components/key-handlers/key-translator.ts +1 -1
- package/src/components/keypad/__tests__/keypad-v2-mathquill.test.tsx +11 -5
- package/src/components/keypad/keypad-mathquill.stories.tsx +1 -1
- package/tsconfig-build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Khan Academy's new expression editor for the mobile web.",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "
|
|
6
|
+
"version": "17.0.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"scripts": {},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@khanacademy/perseus-core": "1.4.1",
|
|
24
|
-
"mathquill": "git+https://git@github.com/Khan/mathquill.git#
|
|
24
|
+
"mathquill": "git+https://git@github.com/Khan/mathquill.git#774bb82942c268bd56cd1024dc18b01ac4d90029",
|
|
25
25
|
"performance-now": "^0.2.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
@@ -17,7 +17,7 @@ import {KeypadContext, StatefulKeypadContextProvider} from "../keypad-context";
|
|
|
17
17
|
|
|
18
18
|
import type {KeypadConfiguration} from "../../types";
|
|
19
19
|
|
|
20
|
-
const MQ = MathQuill.getInterface(
|
|
20
|
+
const MQ = MathQuill.getInterface(3);
|
|
21
21
|
|
|
22
22
|
const defaultConfiguration: KeypadConfiguration = {
|
|
23
23
|
keypadType: KeypadType.FRACTION,
|
|
@@ -176,11 +176,14 @@ describe("math input integration", () => {
|
|
|
176
176
|
|
|
177
177
|
// MathQuill is problematic,
|
|
178
178
|
// this is how to get the value of the input directly from MQ
|
|
179
|
-
const mathquillInstance =
|
|
179
|
+
const mathquillInstance = MQ(
|
|
180
180
|
// eslint-disable-next-line testing-library/no-node-access
|
|
181
|
-
|
|
181
|
+
document.getElementsByClassName(
|
|
182
|
+
"mq-editable-field",
|
|
183
|
+
)[0] as HTMLElement,
|
|
184
|
+
);
|
|
182
185
|
|
|
183
|
-
expect(mathquillInstance
|
|
186
|
+
expect(mathquillInstance?.latex()).toBe("8675309");
|
|
184
187
|
});
|
|
185
188
|
|
|
186
189
|
it("can handle symbols", async () => {
|
|
@@ -202,11 +205,14 @@ describe("math input integration", () => {
|
|
|
202
205
|
|
|
203
206
|
// MathQuill is problematic,
|
|
204
207
|
// this is how to get the value of the input directly from MQ
|
|
205
|
-
const mathquillInstance =
|
|
208
|
+
const mathquillInstance = MQ(
|
|
206
209
|
// eslint-disable-next-line testing-library/no-node-access
|
|
207
|
-
|
|
210
|
+
document.getElementsByClassName(
|
|
211
|
+
"mq-editable-field",
|
|
212
|
+
)[0] as HTMLElement,
|
|
213
|
+
);
|
|
208
214
|
|
|
209
|
-
expect(mathquillInstance
|
|
215
|
+
expect(mathquillInstance?.latex()).toBe("42\\%");
|
|
210
216
|
});
|
|
211
217
|
|
|
212
218
|
it("handles fractions correctly in expression", async () => {
|
|
@@ -243,11 +249,14 @@ describe("math input integration", () => {
|
|
|
243
249
|
|
|
244
250
|
// MathQuill is problematic,
|
|
245
251
|
// this is how to get the value of the input directly from MQ
|
|
246
|
-
const mathquillInstance =
|
|
252
|
+
const mathquillInstance = MQ(
|
|
247
253
|
// eslint-disable-next-line testing-library/no-node-access
|
|
248
|
-
|
|
254
|
+
document.getElementsByClassName(
|
|
255
|
+
"mq-editable-field",
|
|
256
|
+
)[0] as HTMLElement,
|
|
257
|
+
);
|
|
249
258
|
|
|
250
|
-
expect(mathquillInstance
|
|
259
|
+
expect(mathquillInstance?.latex()).toBe("1\\frac{4}{2}");
|
|
251
260
|
});
|
|
252
261
|
|
|
253
262
|
it("handles fractions correctly in fraction", async () => {
|
|
@@ -279,10 +288,13 @@ describe("math input integration", () => {
|
|
|
279
288
|
|
|
280
289
|
// MathQuill is problematic,
|
|
281
290
|
// this is how to get the value of the input directly from MQ
|
|
282
|
-
const mathquillInstance =
|
|
291
|
+
const mathquillInstance = MQ(
|
|
283
292
|
// eslint-disable-next-line testing-library/no-node-access
|
|
284
|
-
|
|
293
|
+
document.getElementsByClassName(
|
|
294
|
+
"mq-editable-field",
|
|
295
|
+
)[0] as HTMLElement,
|
|
296
|
+
);
|
|
285
297
|
|
|
286
|
-
expect(mathquillInstance
|
|
298
|
+
expect(mathquillInstance?.latex()).toBe("1\\frac{4}{2}");
|
|
287
299
|
});
|
|
288
300
|
});
|
|
@@ -35,23 +35,23 @@ describe("MathQuill", () => {
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
it("should work after an expression", () => {
|
|
38
|
-
mathField.setContent("35x^2");
|
|
38
|
+
mathField.setContent("35x^{2}");
|
|
39
39
|
mathField.pressKey("FRAC_INCLUSIVE");
|
|
40
|
-
expect(mathField.getContent()).toEqual("\\frac{35x^2}{ }");
|
|
40
|
+
expect(mathField.getContent()).toEqual("\\frac{35x^{2}}{ }");
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
it("should work before an expression", () => {
|
|
44
|
-
mathField.setContent("35x^2");
|
|
44
|
+
mathField.setContent("35x^{2}");
|
|
45
45
|
mathField.moveToStart();
|
|
46
46
|
mathField.pressKey("FRAC_INCLUSIVE");
|
|
47
|
-
expect(mathField.getContent()).toEqual("\\frac{ }{ }35x^2");
|
|
47
|
+
expect(mathField.getContent()).toEqual("\\frac{ }{ }35x^{2}");
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
it("should work with a selected expression", () => {
|
|
51
|
-
mathField.setContent("35x^2");
|
|
51
|
+
mathField.setContent("35x^{2}");
|
|
52
52
|
mathField.selectAll();
|
|
53
53
|
mathField.pressKey("FRAC_INCLUSIVE");
|
|
54
|
-
expect(mathField.getContent()).toEqual("\\frac{35x^2}{ }");
|
|
54
|
+
expect(mathField.getContent()).toEqual("\\frac{35x^{2}}{ }");
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
57
|
|
|
@@ -63,30 +63,30 @@ describe("MathQuill", () => {
|
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
it("should work after an expression", () => {
|
|
66
|
-
mathField.setContent("35x^2");
|
|
66
|
+
mathField.setContent("35x^{2}");
|
|
67
67
|
mathField.pressKey("RIGHT_PAREN");
|
|
68
|
-
expect(mathField.getContent()).toEqual("\\left(35x^2\\right)");
|
|
68
|
+
expect(mathField.getContent()).toEqual("\\left(35x^{2}\\right)");
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
it("should work before an expression", () => {
|
|
72
|
-
mathField.setContent("35x^2");
|
|
72
|
+
mathField.setContent("35x^{2}");
|
|
73
73
|
mathField.moveToStart();
|
|
74
74
|
mathField.pressKey("LEFT_PAREN");
|
|
75
|
-
expect(mathField.getContent()).toEqual("\\left(35x^2\\right)");
|
|
75
|
+
expect(mathField.getContent()).toEqual("\\left(35x^{2}\\right)");
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
it.skip("should work on a selected expression", () => {
|
|
79
79
|
mathField.setContent("35x + 5");
|
|
80
80
|
mathField.selectAll();
|
|
81
81
|
mathField.pressKey("LEFT_PAREN");
|
|
82
|
-
expect(mathField.getContent()).toEqual("\\left(35x^2\\right)");
|
|
82
|
+
expect(mathField.getContent()).toEqual("\\left(35x^{2}\\right)");
|
|
83
83
|
});
|
|
84
84
|
});
|
|
85
85
|
|
|
86
86
|
describe("Squared", () => {
|
|
87
87
|
it("should prefix with empty parens after no content", () => {
|
|
88
88
|
mathField.pressKey("EXP_2");
|
|
89
|
-
expect(mathField.getContent()).toEqual("\\left(\\right)^2");
|
|
89
|
+
expect(mathField.getContent()).toEqual("\\left(\\right)^{2}");
|
|
90
90
|
|
|
91
91
|
// Verify that the cursor is in parens.
|
|
92
92
|
expect(isInsideEmptyParens(mathField.getCursor())).toBeTruthy();
|
|
@@ -95,27 +95,27 @@ describe("MathQuill", () => {
|
|
|
95
95
|
it("should prefix with empty parens after an operator", () => {
|
|
96
96
|
mathField.pressKey("DIVIDE");
|
|
97
97
|
mathField.pressKey("EXP_2");
|
|
98
|
-
expect(mathField.getContent()).toEqual("\\div\\left(\\right)^2");
|
|
98
|
+
expect(mathField.getContent()).toEqual("\\div\\left(\\right)^{2}");
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
it("should work after an expression", () => {
|
|
102
102
|
mathField.setContent("35x");
|
|
103
103
|
mathField.pressKey("EXP_2");
|
|
104
|
-
expect(mathField.getContent()).toEqual("35x^2");
|
|
104
|
+
expect(mathField.getContent()).toEqual("35x^{2}");
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
it.skip("should work on a selected expression", () => {
|
|
108
108
|
mathField.setContent("35x+5");
|
|
109
109
|
mathField.selectAll();
|
|
110
110
|
mathField.pressKey("EXP_2");
|
|
111
|
-
expect(mathField.getContent()).toEqual("\\left(35x+5\\right)^2");
|
|
111
|
+
expect(mathField.getContent()).toEqual("\\left(35x+5\\right)^{2}");
|
|
112
112
|
});
|
|
113
113
|
});
|
|
114
114
|
|
|
115
115
|
describe("Cubed", () => {
|
|
116
116
|
it("should prefix with empty parens after no content", () => {
|
|
117
117
|
mathField.pressKey("EXP_3");
|
|
118
|
-
expect(mathField.getContent()).toEqual("\\left(\\right)^3");
|
|
118
|
+
expect(mathField.getContent()).toEqual("\\left(\\right)^{3}");
|
|
119
119
|
|
|
120
120
|
// Verify that the cursor is in parens.
|
|
121
121
|
expect(isInsideEmptyParens(mathField.getCursor())).toBeTruthy();
|
|
@@ -124,20 +124,20 @@ describe("MathQuill", () => {
|
|
|
124
124
|
it("should prefix with empty parens after an operator", () => {
|
|
125
125
|
mathField.pressKey("EQUAL");
|
|
126
126
|
mathField.pressKey("EXP_3");
|
|
127
|
-
expect(mathField.getContent()).toEqual("=\\left(\\right)^3");
|
|
127
|
+
expect(mathField.getContent()).toEqual("=\\left(\\right)^{3}");
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
it("should work after an expression", () => {
|
|
131
131
|
mathField.setContent("35x");
|
|
132
132
|
mathField.pressKey("EXP_3");
|
|
133
|
-
expect(mathField.getContent()).toEqual("35x^3");
|
|
133
|
+
expect(mathField.getContent()).toEqual("35x^{3}");
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
it.skip("should work on a selected expression", () => {
|
|
137
137
|
mathField.setContent("35x+5");
|
|
138
138
|
mathField.selectAll();
|
|
139
139
|
mathField.pressKey("EXP_3");
|
|
140
|
-
expect(mathField.getContent()).toEqual("\\left(35x+5\\right)^3");
|
|
140
|
+
expect(mathField.getContent()).toEqual("\\left(35x+5\\right)^{3}");
|
|
141
141
|
});
|
|
142
142
|
});
|
|
143
143
|
|
|
@@ -150,7 +150,7 @@ describe("MathQuill", () => {
|
|
|
150
150
|
// writing a unique character to verify cursor position.
|
|
151
151
|
expect(isInsideEmptyParens(mathField.getCursor())).toBeFalsy();
|
|
152
152
|
mathField.pressKey("PLUS");
|
|
153
|
-
expect(mathField.getContent()).toEqual("\\left(\\right)
|
|
153
|
+
expect(mathField.getContent()).toEqual("\\left(\\right)^{+}");
|
|
154
154
|
});
|
|
155
155
|
|
|
156
156
|
it("should prefix with empty parens after an operator", () => {
|
|
@@ -181,9 +181,9 @@ describe("MathQuill", () => {
|
|
|
181
181
|
});
|
|
182
182
|
|
|
183
183
|
it("should work after an expression", () => {
|
|
184
|
-
mathField.setContent("35x^2");
|
|
184
|
+
mathField.setContent("35x^{2}");
|
|
185
185
|
mathField.pressKey("SQRT");
|
|
186
|
-
expect(mathField.getContent()).toEqual("35x^2\\sqrt{ }");
|
|
186
|
+
expect(mathField.getContent()).toEqual("35x^{2}\\sqrt{ }");
|
|
187
187
|
});
|
|
188
188
|
|
|
189
189
|
it("should work on a selected expression", () => {
|
|
@@ -201,9 +201,9 @@ describe("MathQuill", () => {
|
|
|
201
201
|
});
|
|
202
202
|
|
|
203
203
|
it("should work after an expression", () => {
|
|
204
|
-
mathField.setContent("35x^2");
|
|
204
|
+
mathField.setContent("35x^{2}");
|
|
205
205
|
mathField.pressKey("RADICAL");
|
|
206
|
-
expect(mathField.getContent()).toEqual("35x^2\\sqrt[]{}");
|
|
206
|
+
expect(mathField.getContent()).toEqual("35x^{2}\\sqrt[]{}");
|
|
207
207
|
});
|
|
208
208
|
|
|
209
209
|
it.skip("should work on a selected expression", () => {
|
|
@@ -222,9 +222,11 @@ describe("MathQuill", () => {
|
|
|
222
222
|
});
|
|
223
223
|
|
|
224
224
|
it("should work after an expression", () => {
|
|
225
|
-
mathField.setContent("35x^2");
|
|
225
|
+
mathField.setContent("35x^{2}");
|
|
226
226
|
mathField.pressKey("LOG");
|
|
227
|
-
expect(mathField.getContent()).toEqual(
|
|
227
|
+
expect(mathField.getContent()).toEqual(
|
|
228
|
+
"35x^{2}\\log\\left(\\right)",
|
|
229
|
+
);
|
|
228
230
|
});
|
|
229
231
|
|
|
230
232
|
it.skip("should work on a selected expression", () => {
|
|
@@ -242,10 +244,10 @@ describe("MathQuill", () => {
|
|
|
242
244
|
});
|
|
243
245
|
|
|
244
246
|
it("should work after an expression", () => {
|
|
245
|
-
mathField.setContent("35x^2");
|
|
247
|
+
mathField.setContent("35x^{2}");
|
|
246
248
|
mathField.pressKey("LOG_N");
|
|
247
249
|
expect(mathField.getContent()).toEqual(
|
|
248
|
-
"35x^2\\log_{ }\\left(\\right)",
|
|
250
|
+
"35x^{2}\\log_{ }\\left(\\right)",
|
|
249
251
|
);
|
|
250
252
|
});
|
|
251
253
|
|
|
@@ -269,15 +271,15 @@ describe("MathQuill", () => {
|
|
|
269
271
|
});
|
|
270
272
|
|
|
271
273
|
it("should convert a fraction when deleting the denominator", () => {
|
|
272
|
-
mathField.setContent("\\frac{35x^2}{ }");
|
|
274
|
+
mathField.setContent("\\frac{35x^{2}}{ }");
|
|
273
275
|
mathField.pressKey("LEFT");
|
|
274
276
|
mathField.pressKey("BACKSPACE");
|
|
275
|
-
expect(mathField.getContent()).toEqual("35x^2");
|
|
277
|
+
expect(mathField.getContent()).toEqual("35x^{2}");
|
|
276
278
|
});
|
|
277
279
|
|
|
278
280
|
// TODO(kevinb) math isn't selected
|
|
279
281
|
it("should select a fraction when deleting from outside of it", () => {
|
|
280
|
-
const expr = "\\frac{35x+5}{x^2}";
|
|
282
|
+
const expr = "\\frac{35x+5}{x^{2}}";
|
|
281
283
|
mathField.setContent(expr);
|
|
282
284
|
mathField.pressKey("BACKSPACE");
|
|
283
285
|
expect(mathField.isSelected()).toBeTruthy();
|
|
@@ -358,9 +360,9 @@ describe("MathQuill", () => {
|
|
|
358
360
|
|
|
359
361
|
// TODO(kevinb) fix this behavior so that we delete the exponent too
|
|
360
362
|
it.skip("should not delete squared exponents", () => {
|
|
361
|
-
mathField.setContent("35x^2");
|
|
363
|
+
mathField.setContent("35x^{2}");
|
|
362
364
|
mathField.pressKey("BACKSPACE");
|
|
363
|
-
expect(mathField.getContent()).toEqual("35x^2");
|
|
365
|
+
expect(mathField.getContent()).toEqual("35x^{2}");
|
|
364
366
|
mathField.pressKey("BACKSPACE");
|
|
365
367
|
expect(mathField.getContent()).toEqual("35x^{ }");
|
|
366
368
|
});
|
|
@@ -368,7 +370,7 @@ describe("MathQuill", () => {
|
|
|
368
370
|
it("should not delete non-square exponents", () => {
|
|
369
371
|
mathField.setContent("35x^5");
|
|
370
372
|
mathField.pressKey("BACKSPACE");
|
|
371
|
-
expect(mathField.getContent()).toEqual("35x^5");
|
|
373
|
+
expect(mathField.getContent()).toEqual("35x^{5}");
|
|
372
374
|
mathField.pressKey("BACKSPACE");
|
|
373
375
|
expect(mathField.getContent()).toEqual("35x^{ }");
|
|
374
376
|
});
|
|
@@ -592,7 +594,7 @@ describe("MathQuill", () => {
|
|
|
592
594
|
const cursor = mathField.getCursor();
|
|
593
595
|
|
|
594
596
|
expect(cursor[MQ.L].ctrlSeq).toEqual("5");
|
|
595
|
-
expect(mathField.getContent()).toEqual("\\
|
|
597
|
+
expect(mathField.getContent()).toEqual("\\log_{5}\\left(\\right)");
|
|
596
598
|
});
|
|
597
599
|
|
|
598
600
|
it("should select full log when deleting from empty index (1)", () => {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import MathWrapper from "../math-wrapper";
|
|
2
|
-
import {mathQuillInstance} from "../mathquill-instance";
|
|
3
2
|
|
|
4
3
|
export default class TestMathWrapper extends MathWrapper {
|
|
5
4
|
getContent() {
|
|
@@ -15,7 +14,7 @@ export default class TestMathWrapper extends MathWrapper {
|
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
moveToStart() {
|
|
18
|
-
this.mathField.
|
|
17
|
+
this.mathField.moveToLeftEnd();
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
isSelected() {
|
|
@@ -99,21 +99,6 @@ class MathInput extends React.Component<Props, State> {
|
|
|
99
99
|
},
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
// NOTE(charlie): MathQuill binds this handler to manage its
|
|
103
|
-
// drag-to-select behavior. For reasons that I can't explain, the event
|
|
104
|
-
// itself gets triggered even if you tap slightly outside of the
|
|
105
|
-
// bound container (maybe 5px outside of any boundary). As a result, the
|
|
106
|
-
// cursor appears when tapping at those locations, even though the input
|
|
107
|
-
// itself doesn't receive any touch start or mouse down event and, as
|
|
108
|
-
// such, doesn't focus itself. This makes for a confusing UX, as the
|
|
109
|
-
// cursor appears, but the keypad does not and the input otherwise
|
|
110
|
-
// treats itself as unfocused. Thankfully, we don't need this behavior--
|
|
111
|
-
// we manage all of the cursor interactions ourselves--so we can safely
|
|
112
|
-
// unbind the handler.
|
|
113
|
-
this.mathField.mathField.__controller.container.unbind(
|
|
114
|
-
"mousedown.mathquill",
|
|
115
|
-
);
|
|
116
|
-
|
|
117
102
|
this.mathField.setContent(this.props.value);
|
|
118
103
|
|
|
119
104
|
this._updateInputPadding();
|
|
@@ -542,7 +527,7 @@ class MathInput extends React.Component<Props, State> {
|
|
|
542
527
|
// Pre-emptively check if the input has any child nodes; if not, the
|
|
543
528
|
// input is empty, so we throw the cursor at the start.
|
|
544
529
|
if (!this._root.hasChildNodes()) {
|
|
545
|
-
cursor.insAtLeftEnd(this.mathField.mathField.
|
|
530
|
+
cursor.insAtLeftEnd(this.mathField.mathField.controller().root);
|
|
546
531
|
return;
|
|
547
532
|
}
|
|
548
533
|
|
|
@@ -595,9 +580,9 @@ class MathInput extends React.Component<Props, State> {
|
|
|
595
580
|
// or left of all of the math, so we place the cursor at the end to
|
|
596
581
|
// which it's closest.
|
|
597
582
|
if (Math.abs(x - right) < Math.abs(x - left)) {
|
|
598
|
-
cursor.insAtRightEnd(this.mathField.mathField.
|
|
583
|
+
cursor.insAtRightEnd(this.mathField.mathField.controller().root);
|
|
599
584
|
} else {
|
|
600
|
-
cursor.insAtLeftEnd(this.mathField.mathField.
|
|
585
|
+
cursor.insAtLeftEnd(this.mathField.mathField.controller().root);
|
|
601
586
|
}
|
|
602
587
|
// In that event, we need to update the cursor context ourselves.
|
|
603
588
|
this.props.keypadElement &&
|
|
@@ -19,11 +19,7 @@ import $ from "jquery";
|
|
|
19
19
|
import handleBackspace from "../key-handlers/handle-backspace";
|
|
20
20
|
import keyTranslator from "../key-handlers/key-translator";
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
getCursor,
|
|
24
|
-
getCursorContext,
|
|
25
|
-
maybeFindCommand,
|
|
26
|
-
} from "./mathquill-helpers";
|
|
22
|
+
import {getCursorContext, maybeFindCommand} from "./mathquill-helpers";
|
|
27
23
|
import {createMathField, mathQuillInstance} from "./mathquill-instance";
|
|
28
24
|
|
|
29
25
|
import type {
|
|
@@ -31,7 +27,6 @@ import type {
|
|
|
31
27
|
MathFieldUpdaterCallback,
|
|
32
28
|
} from "./mathquill-types";
|
|
33
29
|
import type Key from "../../data/keys";
|
|
34
|
-
import type {Cursor} from "../../types";
|
|
35
30
|
|
|
36
31
|
const mobileKeyTranslator: Record<Key, MathFieldUpdaterCallback> = {
|
|
37
32
|
...keyTranslator,
|
|
@@ -70,19 +65,17 @@ class MathWrapper {
|
|
|
70
65
|
// HACK(charlie): We shouldn't reaching into MathQuill internals like
|
|
71
66
|
// this, but it's the easiest way to allow us to manage the focus state
|
|
72
67
|
// ourselves.
|
|
73
|
-
|
|
74
|
-
controller.cursor.show();
|
|
68
|
+
this.mathField.cursor().show();
|
|
75
69
|
|
|
76
70
|
// Set MathQuill's internal state to reflect the focus, otherwise it
|
|
77
71
|
// will consistently try to hide the cursor on key-press and introduce
|
|
78
72
|
// layout jank.
|
|
79
|
-
|
|
73
|
+
this.mathField.focus();
|
|
80
74
|
}
|
|
81
75
|
|
|
82
76
|
blur() {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
controller.blurred = true;
|
|
77
|
+
this.mathField.cursor().hide();
|
|
78
|
+
this.mathField.blur();
|
|
86
79
|
}
|
|
87
80
|
|
|
88
81
|
/**
|
|
@@ -91,7 +84,7 @@ class MathWrapper {
|
|
|
91
84
|
* @param {Key} key - an enum representing the key that was pressed
|
|
92
85
|
* @returns {object} a cursor object, consisting of a cursor context
|
|
93
86
|
*/
|
|
94
|
-
pressKey(key: Key)
|
|
87
|
+
pressKey(key: Key) {
|
|
95
88
|
const cursor = this.getCursor();
|
|
96
89
|
const translator = mobileKeyTranslator[key];
|
|
97
90
|
|
|
@@ -135,10 +128,10 @@ class MathWrapper {
|
|
|
135
128
|
if (el.hasAttribute("mq-root-block")) {
|
|
136
129
|
// If we're in the empty area place the cursor at the right
|
|
137
130
|
// end of the expression.
|
|
138
|
-
cursor.insAtRightEnd(this.mathField.
|
|
131
|
+
cursor.insAtRightEnd(this.mathField.controller().root);
|
|
139
132
|
} else {
|
|
140
133
|
// Otherwise place beside the element at x, y.
|
|
141
|
-
const controller = this.mathField.
|
|
134
|
+
const controller = this.mathField.controller();
|
|
142
135
|
|
|
143
136
|
const pageX = x - document.body.scrollLeft;
|
|
144
137
|
const pageY = y - document.body.scrollTop;
|
|
@@ -166,7 +159,7 @@ class MathWrapper {
|
|
|
166
159
|
// note(Matthew): extracted this logic to share it elsewhere,
|
|
167
160
|
// but it's part of the public MathWrapper API
|
|
168
161
|
getCursor() {
|
|
169
|
-
return
|
|
162
|
+
return this.mathField.cursor();
|
|
170
163
|
}
|
|
171
164
|
|
|
172
165
|
// note(Matthew): extracted this logic to keep this file focused,
|
|
@@ -2,7 +2,7 @@ import {CursorContext} from "./cursor-contexts";
|
|
|
2
2
|
import {mathQuillInstance} from "./mathquill-instance";
|
|
3
3
|
import {MathFieldActionType} from "./mathquill-types";
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type {MathFieldInterface} from "./mathquill-types";
|
|
6
6
|
|
|
7
7
|
const Numerals = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
|
8
8
|
const GreekLetters = ["\\theta", "\\pi"];
|
|
@@ -44,20 +44,19 @@ const ValidLeaves = [
|
|
|
44
44
|
...Letters.map((letter) => letter.toUpperCase()),
|
|
45
45
|
];
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
47
|
+
const mqNodeHasClass = (node: any, className: string): boolean =>
|
|
48
|
+
node._el && (node._el as HTMLElement).classList.contains(className);
|
|
50
49
|
|
|
51
50
|
export function isFraction(node): boolean {
|
|
52
|
-
return node
|
|
51
|
+
return mqNodeHasClass(node, "mq-fraction");
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
export function isNumerator(node): boolean {
|
|
56
|
-
return node
|
|
55
|
+
return mqNodeHasClass(node, "mq-numerator");
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
export function isDenominator(node): boolean {
|
|
60
|
-
return node
|
|
59
|
+
return mqNodeHasClass(node, "mq-denominator");
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
export function isSubScript(node): boolean {
|
|
@@ -65,8 +64,7 @@ export function isSubScript(node): boolean {
|
|
|
65
64
|
// to be represented as a parent node with 'mq-sup-only' containing a
|
|
66
65
|
// single child with 'mq-sup'.
|
|
67
66
|
return (
|
|
68
|
-
node
|
|
69
|
-
(node.jQ.hasClass("mq-sub-only") || node.jQ.hasClass("mq-sub"))
|
|
67
|
+
mqNodeHasClass(node, "mq-sub-only") || mqNodeHasClass(node, "mq-sub")
|
|
70
68
|
);
|
|
71
69
|
}
|
|
72
70
|
|
|
@@ -75,8 +73,7 @@ export function isSuperScript(node): boolean {
|
|
|
75
73
|
// to be represented as a parent node with 'mq-sup-only' containing a
|
|
76
74
|
// single child with 'mq-sup'.
|
|
77
75
|
return (
|
|
78
|
-
node
|
|
79
|
-
(node.jQ.hasClass("mq-sup-only") || node.jQ.hasClass("mq-sup"))
|
|
76
|
+
mqNodeHasClass(node, "mq-sup-only") || mqNodeHasClass(node, "mq-sup")
|
|
80
77
|
);
|
|
81
78
|
}
|
|
82
79
|
|
|
@@ -89,29 +86,21 @@ export function isLeaf(node): boolean {
|
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
export function isSquareRoot(node): boolean {
|
|
92
|
-
return (
|
|
93
|
-
node.blocks &&
|
|
94
|
-
node.blocks[0].jQ &&
|
|
95
|
-
node.blocks[0].jQ.hasClass("mq-sqrt-stem")
|
|
96
|
-
);
|
|
89
|
+
return node.blocks && mqNodeHasClass(node.blocks[0], "mq-sqrt-stem");
|
|
97
90
|
}
|
|
98
91
|
|
|
99
92
|
export function isNthRoot(node): boolean {
|
|
100
|
-
return (
|
|
101
|
-
node.blocks &&
|
|
102
|
-
node.blocks[0].jQ &&
|
|
103
|
-
node.blocks[0].jQ.hasClass("mq-nthroot")
|
|
104
|
-
);
|
|
93
|
+
return node.blocks && mqNodeHasClass(node.blocks[0], "mq-nthroot");
|
|
105
94
|
}
|
|
106
95
|
|
|
107
96
|
export function isNthRootIndex(node): boolean {
|
|
108
|
-
return node
|
|
97
|
+
return mqNodeHasClass(node, "mq-nthroot");
|
|
109
98
|
}
|
|
110
99
|
|
|
111
|
-
export function isInsideLogIndex(cursor
|
|
100
|
+
export function isInsideLogIndex(cursor): boolean {
|
|
112
101
|
const grandparent = cursor.parent.parent;
|
|
113
102
|
|
|
114
|
-
if (grandparent && grandparent
|
|
103
|
+
if (grandparent && mqNodeHasClass(grandparent, "mq-supsub")) {
|
|
115
104
|
const command = maybeFindCommandBeforeParens(grandparent);
|
|
116
105
|
|
|
117
106
|
if (command && command.name === "\\log") {
|
|
@@ -122,7 +111,7 @@ export function isInsideLogIndex(cursor: MathFieldCursor): boolean {
|
|
|
122
111
|
return false;
|
|
123
112
|
}
|
|
124
113
|
|
|
125
|
-
export function isInsideEmptyNode(cursor
|
|
114
|
+
export function isInsideEmptyNode(cursor): boolean {
|
|
126
115
|
return (
|
|
127
116
|
cursor[mathQuillInstance.L] === MathFieldActionType.MQ_END &&
|
|
128
117
|
cursor[mathQuillInstance.R] === MathFieldActionType.MQ_END
|
|
@@ -244,7 +233,7 @@ export function getCursorContext(
|
|
|
244
233
|
}
|
|
245
234
|
|
|
246
235
|
// First, try to find any fraction to the right, unimpeded.
|
|
247
|
-
const cursor =
|
|
236
|
+
const cursor = mathField.cursor();
|
|
248
237
|
let visitor = cursor;
|
|
249
238
|
while (visitor[mathQuillInstance.R] !== MathFieldActionType.MQ_END) {
|
|
250
239
|
if (isFraction(visitor[mathQuillInstance.R])) {
|