@khanacademy/math-input 1.0.1 → 2.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 +5 -1
- package/dist/components/compute-layout-parameters.d.ts +2 -1
- package/dist/components/compute-layout-parameters.js.flow +2 -21
- package/dist/components/echo-manager.d.ts +4 -4
- package/dist/components/echo-manager.js.flow +4 -4
- package/dist/components/expression-keypad.d.ts +1 -1
- package/dist/components/expression-keypad.js.flow +1 -1
- package/dist/components/fraction-keypad.d.ts +1 -1
- package/dist/components/fraction-keypad.js.flow +1 -1
- package/dist/components/gesture-state-machine.d.ts +7 -7
- package/dist/components/gesture-state-machine.js.flow +8 -8
- package/dist/components/icon.d.ts +2 -2
- package/dist/components/icon.js.flow +2 -2
- package/dist/components/input/cursor-contexts.d.ts +10 -9
- package/dist/components/input/cursor-contexts.js.flow +11 -16
- package/dist/components/input/math-wrapper.d.ts +3 -2
- package/dist/components/input/math-wrapper.js.flow +3 -16
- package/dist/components/keypad-button.d.ts +8 -8
- package/dist/components/keypad-button.js.flow +10 -9
- package/dist/components/keypad-container.d.ts +2 -3
- package/dist/components/keypad-container.js.flow +2 -3
- package/dist/components/keypad.d.ts +1 -1
- package/dist/components/keypad.js.flow +1 -1
- package/dist/components/multi-symbol-grid.d.ts +2 -2
- package/dist/components/multi-symbol-grid.js.flow +2 -2
- package/dist/components/styles.d.ts +1 -2
- package/dist/components/styles.js.flow +1 -3
- package/dist/components/touchable-keypad-button.d.ts +6 -6
- package/dist/components/touchable-keypad-button.js.flow +6 -6
- package/dist/data/keys.d.ts +51 -52
- package/dist/data/keys.js.flow +50 -99
- package/dist/enums.d.ts +49 -0
- package/dist/enums.js.flow +63 -0
- package/dist/es/index.js +362 -391
- package/dist/es/index.js.map +1 -1
- package/dist/fake-react-native-web/view.d.ts +1 -2
- package/dist/fake-react-native-web/view.js.flow +1 -2
- package/dist/index.d.ts +3 -6
- package/dist/index.js +379 -406
- package/dist/index.js.flow +3 -6
- package/dist/index.js.map +1 -1
- package/dist/store/actions.d.ts +3 -3
- package/dist/store/actions.js.flow +5 -4
- package/dist/store/shared.d.ts +2 -1
- package/dist/store/shared.js.flow +2 -1
- package/dist/store/types.d.ts +4 -5
- package/dist/store/types.js.flow +4 -5
- package/dist/types.d.ts +15 -16
- package/dist/types.js.flow +20 -20
- package/package.json +1 -1
- package/src/components/compute-layout-parameters.ts +6 -6
- package/src/components/echo-manager.tsx +10 -10
- package/src/components/expression-keypad.tsx +9 -10
- package/src/components/fraction-keypad.tsx +11 -12
- package/src/components/gesture-state-machine.ts +8 -8
- package/src/components/icon.tsx +6 -6
- package/src/components/input/__tests__/context-tracking.test.ts +20 -20
- package/src/components/input/cursor-contexts.ts +22 -29
- package/src/components/input/math-wrapper.ts +75 -67
- package/src/components/keypad-button.tsx +20 -21
- package/src/components/keypad-container.tsx +8 -9
- package/src/components/many-keypad-button.tsx +2 -2
- package/src/components/multi-symbol-grid.tsx +4 -5
- package/src/components/multi-symbol-popover.tsx +1 -1
- package/src/components/navigation-pad.tsx +1 -1
- package/src/components/touchable-keypad-button.tsx +7 -7
- package/src/data/key-configs.ts +58 -58
- package/src/data/keys.ts +53 -105
- package/src/enums.ts +74 -0
- package/src/index.ts +3 -9
- package/src/math-input.stories.tsx +8 -8
- package/src/store/actions.ts +4 -3
- package/src/store/echo-reducer.ts +5 -5
- package/src/store/index.ts +1 -2
- package/src/store/input-reducer.ts +4 -4
- package/src/store/layout-reducer.ts +9 -9
- package/src/store/pager-reducer.ts +3 -3
- package/src/store/shared.ts +4 -4
- package/src/store/types.ts +4 -5
- package/src/types.ts +20 -20
- package/src/utils.ts +3 -3
- package/tsconfig-build.tsbuildinfo +1 -0
- package/dist/consts.d.ts +0 -51
- package/dist/consts.js.flow +0 -66
- package/src/consts.ts +0 -91
- package/tsconfig.tsbuildinfo +0 -1
- /package/{tsconfig.json → tsconfig-build.json} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Keys from "../../../data/keys";
|
|
2
|
-
import
|
|
2
|
+
import {CursorContext} from "../cursor-contexts";
|
|
3
3
|
|
|
4
4
|
import TestMathWrapper from "./test-math-wrapper";
|
|
5
5
|
|
|
@@ -22,20 +22,20 @@ describe("Cursor context", () => {
|
|
|
22
22
|
mathField.pressKey("NUM_1");
|
|
23
23
|
mathField.pressKey("NUM_2");
|
|
24
24
|
const cursor = mathField.pressKey("NUM_3");
|
|
25
|
-
expect(cursor.context).toEqual(
|
|
25
|
+
expect(cursor.context).toEqual(CursorContext.NONE);
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
it("should treat numbers and ternary operators as non-jumpable", () => {
|
|
29
29
|
mathField.pressKey("NUM_1");
|
|
30
30
|
mathField.pressKey(Keys.CDOT);
|
|
31
31
|
const cursor = mathField.pressKey("NUM_2");
|
|
32
|
-
expect(cursor.context).toEqual(
|
|
32
|
+
expect(cursor.context).toEqual(CursorContext.NONE);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
describe("Before fraction", () => {
|
|
36
36
|
it("should detect when immediately to the left", () => {
|
|
37
37
|
const cursor = mathField.pressKey(Keys.FRAC_EXCLUSIVE);
|
|
38
|
-
expect(cursor.context).toEqual(
|
|
38
|
+
expect(cursor.context).toEqual(CursorContext.BEFORE_FRACTION);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
it("should detect when numbers are between", () => {
|
|
@@ -43,7 +43,7 @@ describe("Cursor context", () => {
|
|
|
43
43
|
mathField.pressKey(Keys.FRAC_EXCLUSIVE);
|
|
44
44
|
mathField.pressKey(Keys.LEFT);
|
|
45
45
|
const cursor = mathField.pressKey(Keys.LEFT);
|
|
46
|
-
expect(cursor.context).toEqual(
|
|
46
|
+
expect(cursor.context).toEqual(CursorContext.BEFORE_FRACTION);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
it("should not detect when operators are between", () => {
|
|
@@ -55,7 +55,7 @@ describe("Cursor context", () => {
|
|
|
55
55
|
mathField.pressKey(Keys.LEFT);
|
|
56
56
|
mathField.pressKey(Keys.LEFT);
|
|
57
57
|
const cursor = mathField.pressKey(Keys.LEFT);
|
|
58
|
-
expect(cursor.context).toEqual(
|
|
58
|
+
expect(cursor.context).toEqual(CursorContext.NONE);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
it("should not detect when parens are between", () => {
|
|
@@ -69,7 +69,7 @@ describe("Cursor context", () => {
|
|
|
69
69
|
mathField.pressKey(Keys.LEFT);
|
|
70
70
|
mathField.pressKey(Keys.LEFT);
|
|
71
71
|
const cursor = mathField.pressKey(Keys.LEFT);
|
|
72
|
-
expect(cursor.context).toEqual(
|
|
72
|
+
expect(cursor.context).toEqual(CursorContext.NONE);
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
75
|
|
|
@@ -78,7 +78,7 @@ describe("Cursor context", () => {
|
|
|
78
78
|
mathField.pressKey(Keys.LEFT_PAREN);
|
|
79
79
|
mathField.pressKey(Keys.RIGHT_PAREN);
|
|
80
80
|
const cursor = mathField.pressKey(Keys.LEFT);
|
|
81
|
-
expect(cursor.context).toEqual(
|
|
81
|
+
expect(cursor.context).toEqual(CursorContext.IN_PARENS);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
it("should detect when inside non-empty parens", () => {
|
|
@@ -86,7 +86,7 @@ describe("Cursor context", () => {
|
|
|
86
86
|
mathField.pressKey("NUM_2");
|
|
87
87
|
mathField.pressKey(Keys.RIGHT_PAREN);
|
|
88
88
|
const cursor = mathField.pressKey(Keys.LEFT);
|
|
89
|
-
expect(cursor.context).toEqual(
|
|
89
|
+
expect(cursor.context).toEqual(CursorContext.IN_PARENS);
|
|
90
90
|
});
|
|
91
91
|
});
|
|
92
92
|
|
|
@@ -94,40 +94,40 @@ describe("Cursor context", () => {
|
|
|
94
94
|
it("should detect when inside empty superscript", () => {
|
|
95
95
|
mathField.pressKey("NUM_2");
|
|
96
96
|
const cursor = mathField.pressKey(Keys.EXP);
|
|
97
|
-
expect(cursor.context).toEqual(
|
|
97
|
+
expect(cursor.context).toEqual(CursorContext.IN_SUPER_SCRIPT);
|
|
98
98
|
});
|
|
99
99
|
|
|
100
100
|
it("should detect when inside non-empty superscript", () => {
|
|
101
101
|
mathField.pressKey("NUM_2");
|
|
102
102
|
mathField.pressKey(Keys.EXP);
|
|
103
103
|
const cursor = mathField.pressKey("NUM_3");
|
|
104
|
-
expect(cursor.context).toEqual(
|
|
104
|
+
expect(cursor.context).toEqual(CursorContext.IN_SUPER_SCRIPT);
|
|
105
105
|
});
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
describe("In subscript", () => {
|
|
109
109
|
it("should detect when inside empty superscript", () => {
|
|
110
110
|
const cursor = mathField.pressKey(Keys.LOG_N);
|
|
111
|
-
expect(cursor.context).toEqual(
|
|
111
|
+
expect(cursor.context).toEqual(CursorContext.IN_SUB_SCRIPT);
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
it("should detect when inside non-empty superscript", () => {
|
|
115
115
|
mathField.pressKey(Keys.LOG_N);
|
|
116
116
|
const cursor = mathField.pressKey("NUM_2");
|
|
117
|
-
expect(cursor.context).toEqual(
|
|
117
|
+
expect(cursor.context).toEqual(CursorContext.IN_SUB_SCRIPT);
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
120
|
|
|
121
121
|
describe("In numerator", () => {
|
|
122
122
|
it("should detect when inside empty numerator", () => {
|
|
123
123
|
const cursor = mathField.pressKey(Keys.FRAC_INCLUSIVE);
|
|
124
|
-
expect(cursor.context).toEqual(
|
|
124
|
+
expect(cursor.context).toEqual(CursorContext.IN_NUMERATOR);
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
it("should detect when inside non-empty numerator", () => {
|
|
128
128
|
mathField.pressKey(Keys.FRAC_INCLUSIVE);
|
|
129
129
|
const cursor = mathField.pressKey("NUM_2");
|
|
130
|
-
expect(cursor.context).toEqual(
|
|
130
|
+
expect(cursor.context).toEqual(CursorContext.IN_NUMERATOR);
|
|
131
131
|
});
|
|
132
132
|
});
|
|
133
133
|
|
|
@@ -135,14 +135,14 @@ describe("Cursor context", () => {
|
|
|
135
135
|
it("should detect when inside empty denominator", () => {
|
|
136
136
|
mathField.pressKey(Keys.FRAC_INCLUSIVE);
|
|
137
137
|
const cursor = mathField.pressKey(Keys.RIGHT);
|
|
138
|
-
expect(cursor.context).toEqual(
|
|
138
|
+
expect(cursor.context).toEqual(CursorContext.IN_DENOMINATOR);
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
it("should detect when inside non-empty denominator", () => {
|
|
142
142
|
mathField.pressKey(Keys.FRAC_INCLUSIVE);
|
|
143
143
|
mathField.pressKey(Keys.RIGHT);
|
|
144
144
|
const cursor = mathField.pressKey("NUM_2");
|
|
145
|
-
expect(cursor.context).toEqual(
|
|
145
|
+
expect(cursor.context).toEqual(CursorContext.IN_DENOMINATOR);
|
|
146
146
|
});
|
|
147
147
|
});
|
|
148
148
|
|
|
@@ -153,7 +153,7 @@ describe("Cursor context", () => {
|
|
|
153
153
|
mathField.pressKey("NUM_2");
|
|
154
154
|
mathField.pressKey(Keys.FRAC_EXCLUSIVE);
|
|
155
155
|
const cursor = mathField.pressKey(Keys.LEFT);
|
|
156
|
-
expect(cursor.context).toEqual(
|
|
156
|
+
expect(cursor.context).toEqual(CursorContext.BEFORE_FRACTION);
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
it("should defer to the nearest parent (1)", () => {
|
|
@@ -162,7 +162,7 @@ describe("Cursor context", () => {
|
|
|
162
162
|
mathField.pressKey(Keys.EXP);
|
|
163
163
|
mathField.pressKey(Keys.LEFT_PAREN);
|
|
164
164
|
const cursor = mathField.pressKey("NUM_3");
|
|
165
|
-
expect(cursor.context).toEqual(
|
|
165
|
+
expect(cursor.context).toEqual(CursorContext.IN_PARENS);
|
|
166
166
|
});
|
|
167
167
|
|
|
168
168
|
it("should defer to the nearest parent (2)", () => {
|
|
@@ -171,7 +171,7 @@ describe("Cursor context", () => {
|
|
|
171
171
|
mathField.pressKey(Keys.FRAC_INCLUSIVE);
|
|
172
172
|
mathField.pressKey(Keys.FRAC_INCLUSIVE);
|
|
173
173
|
const cursor = mathField.pressKey(Keys.RIGHT);
|
|
174
|
-
expect(cursor.context).toEqual(
|
|
174
|
+
expect(cursor.context).toEqual(CursorContext.IN_DENOMINATOR);
|
|
175
175
|
});
|
|
176
176
|
});
|
|
177
177
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Enum that defines the various contexts in which a cursor can exist. The
|
|
3
3
|
* active context is determined first by looking at the cursor's siblings (e.g.,
|
|
4
4
|
* for the `BEFORE_FRACTION` context), and then at its direct parent. Though a
|
|
5
5
|
* cursor could in theory be nested in multiple contexts, we only care about the
|
|
@@ -10,35 +10,28 @@
|
|
|
10
10
|
* the radical.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
export
|
|
13
|
+
export enum CursorContext {
|
|
14
|
+
// The cursor is not in any of the other viable contexts.
|
|
15
|
+
NONE = "NONE",
|
|
16
|
+
|
|
14
17
|
// The cursor is within a set of parentheses.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
IN_PARENS = "IN_PARENS",
|
|
19
|
+
|
|
20
|
+
// The cursor is within a superscript (e.g., an exponent).
|
|
21
|
+
IN_SUPER_SCRIPT = "IN_SUPER_SCRIPT",
|
|
22
|
+
|
|
23
|
+
// The cursor is within a subscript (e.g., the base of a custom logarithm).
|
|
24
|
+
IN_SUB_SCRIPT = "IN_SUB_SCRIPT",
|
|
25
|
+
|
|
26
|
+
// The cursor is in the numerator of a fraction.
|
|
27
|
+
IN_NUMERATOR = "IN_NUMERATOR",
|
|
28
|
+
|
|
29
|
+
// The cursor is in the denominator of a fraction.
|
|
30
|
+
IN_DENOMINATOR = "IN_DENOMINATOR",
|
|
31
|
+
|
|
32
|
+
// The cursor is sitting before a fraction; that is, the cursor is within
|
|
20
33
|
// what looks to be a mixed number preceding a fraction. This will only be
|
|
21
34
|
// the case when the only math between the cursor and the fraction to its
|
|
22
35
|
// write is non-leaf math (numbers and variables).
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// TODO: Get rid of these constants in favour of CursorContext type.
|
|
27
|
-
|
|
28
|
-
// The cursor is not in any of the other viable contexts.
|
|
29
|
-
export const NONE = "NONE";
|
|
30
|
-
// The cursor is within a set of parentheses.
|
|
31
|
-
export const IN_PARENS = "IN_PARENS";
|
|
32
|
-
// The cursor is within a superscript (e.g., an exponent).
|
|
33
|
-
export const IN_SUPER_SCRIPT = "IN_SUPER_SCRIPT";
|
|
34
|
-
// The cursor is within a subscript (e.g., the base of a custom logarithm).
|
|
35
|
-
export const IN_SUB_SCRIPT = "IN_SUB_SCRIPT";
|
|
36
|
-
// The cursor is in the numerator of a fraction.
|
|
37
|
-
export const IN_NUMERATOR = "IN_NUMERATOR";
|
|
38
|
-
// The cursor is in the denominator of a fraction.
|
|
39
|
-
export const IN_DENOMINATOR = "IN_DENOMINATOR";
|
|
40
|
-
// The cursor is sitting before a fraction; that is, the cursor is within
|
|
41
|
-
// what looks to be a mixed number preceding a fraction. This will only be
|
|
42
|
-
// the case when the only math between the cursor and the fraction to its
|
|
43
|
-
// write is non-leaf math (numbers and variables).
|
|
44
|
-
export const BEFORE_FRACTION = "BEFORE_FRACTION";
|
|
36
|
+
BEFORE_FRACTION = "BEFORE_FRACTION",
|
|
37
|
+
}
|
|
@@ -7,55 +7,57 @@
|
|
|
7
7
|
import $ from "jquery";
|
|
8
8
|
import MathQuill from "mathquill";
|
|
9
9
|
|
|
10
|
-
import {DecimalSeparators} from "../../consts";
|
|
11
10
|
import Keys from "../../data/keys";
|
|
11
|
+
import {DecimalSeparator} from "../../enums";
|
|
12
12
|
import {decimalSeparator} from "../../utils";
|
|
13
13
|
|
|
14
|
-
import
|
|
14
|
+
import {CursorContext} from "./cursor-contexts";
|
|
15
15
|
|
|
16
16
|
// Keeping `window` in place for test suite and GitHub Pages.
|
|
17
17
|
// If it does not exist, fall back to CommonJS require. - jsatk
|
|
18
18
|
|
|
19
|
-
const decimalSymbol = decimalSeparator ===
|
|
19
|
+
const decimalSymbol = decimalSeparator === DecimalSeparator.COMMA ? "," : ".";
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
enum ActionType {
|
|
22
|
+
WRITE = "write",
|
|
23
|
+
CMD = "cmd",
|
|
24
|
+
KEYSTROKE = "keystroke",
|
|
25
|
+
MQ_END = 0,
|
|
26
|
+
}
|
|
25
27
|
|
|
26
28
|
// A mapping from keys that can be pressed on a keypad to the way in which
|
|
27
29
|
// MathQuill should modify its input in response to that key-press. Any keys
|
|
28
30
|
// that do not provide explicit actions (like the numeral keys) will merely
|
|
29
31
|
// write their contents to MathQuill.
|
|
30
|
-
const KeyActions = {
|
|
31
|
-
[Keys.PLUS]: {str: "+", fn: WRITE},
|
|
32
|
-
[Keys.MINUS]: {str: "-", fn: WRITE},
|
|
33
|
-
[Keys.NEGATIVE]: {str: "-", fn: WRITE},
|
|
34
|
-
[Keys.TIMES]: {str: "\\times", fn: WRITE},
|
|
35
|
-
[Keys.DIVIDE]: {str: "\\div", fn: WRITE},
|
|
32
|
+
const KeyActions: {[K in Keys]?: {str: string; fn: ActionType}} = {
|
|
33
|
+
[Keys.PLUS]: {str: "+", fn: ActionType.WRITE},
|
|
34
|
+
[Keys.MINUS]: {str: "-", fn: ActionType.WRITE},
|
|
35
|
+
[Keys.NEGATIVE]: {str: "-", fn: ActionType.WRITE},
|
|
36
|
+
[Keys.TIMES]: {str: "\\times", fn: ActionType.WRITE},
|
|
37
|
+
[Keys.DIVIDE]: {str: "\\div", fn: ActionType.WRITE},
|
|
36
38
|
[Keys.DECIMAL]: {
|
|
37
39
|
str: decimalSymbol,
|
|
38
|
-
fn: WRITE,
|
|
40
|
+
fn: ActionType.WRITE,
|
|
39
41
|
},
|
|
40
|
-
[Keys.EQUAL]: {str: "=", fn: WRITE},
|
|
41
|
-
[Keys.NEQ]: {str: "\\neq", fn: WRITE},
|
|
42
|
-
[Keys.CDOT]: {str: "\\cdot", fn: WRITE},
|
|
43
|
-
[Keys.PERCENT]: {str: "%", fn: WRITE},
|
|
44
|
-
[Keys.LEFT_PAREN]: {str: "(", fn: CMD},
|
|
45
|
-
[Keys.RIGHT_PAREN]: {str: ")", fn: CMD},
|
|
46
|
-
[Keys.SQRT]: {str: "sqrt", fn: CMD},
|
|
47
|
-
[Keys.PI]: {str: "pi", fn: CMD},
|
|
48
|
-
[Keys.THETA]: {str: "theta", fn: CMD},
|
|
49
|
-
[Keys.RADICAL]: {str: "nthroot", fn: CMD},
|
|
50
|
-
[Keys.LT]: {str: "<", fn: WRITE},
|
|
51
|
-
[Keys.LEQ]: {str: "\\leq", fn: WRITE},
|
|
52
|
-
[Keys.GT]: {str: ">", fn: WRITE},
|
|
53
|
-
[Keys.GEQ]: {str: "\\geq", fn: WRITE},
|
|
54
|
-
[Keys.UP]: {str: "Up", fn: KEYSTROKE},
|
|
55
|
-
[Keys.DOWN]: {str: "Down", fn: KEYSTROKE},
|
|
42
|
+
[Keys.EQUAL]: {str: "=", fn: ActionType.WRITE},
|
|
43
|
+
[Keys.NEQ]: {str: "\\neq", fn: ActionType.WRITE},
|
|
44
|
+
[Keys.CDOT]: {str: "\\cdot", fn: ActionType.WRITE},
|
|
45
|
+
[Keys.PERCENT]: {str: "%", fn: ActionType.WRITE},
|
|
46
|
+
[Keys.LEFT_PAREN]: {str: "(", fn: ActionType.CMD},
|
|
47
|
+
[Keys.RIGHT_PAREN]: {str: ")", fn: ActionType.CMD},
|
|
48
|
+
[Keys.SQRT]: {str: "sqrt", fn: ActionType.CMD},
|
|
49
|
+
[Keys.PI]: {str: "pi", fn: ActionType.CMD},
|
|
50
|
+
[Keys.THETA]: {str: "theta", fn: ActionType.CMD},
|
|
51
|
+
[Keys.RADICAL]: {str: "nthroot", fn: ActionType.CMD},
|
|
52
|
+
[Keys.LT]: {str: "<", fn: ActionType.WRITE},
|
|
53
|
+
[Keys.LEQ]: {str: "\\leq", fn: ActionType.WRITE},
|
|
54
|
+
[Keys.GT]: {str: ">", fn: ActionType.WRITE},
|
|
55
|
+
[Keys.GEQ]: {str: "\\geq", fn: ActionType.WRITE},
|
|
56
|
+
[Keys.UP]: {str: "Up", fn: ActionType.KEYSTROKE},
|
|
57
|
+
[Keys.DOWN]: {str: "Down", fn: ActionType.KEYSTROKE},
|
|
56
58
|
// The `FRAC_EXCLUSIVE` variant is handled manually, since we may need to do
|
|
57
59
|
// some additional navigation depending on the cursor position.
|
|
58
|
-
[Keys.FRAC_INCLUSIVE]: {str: "/", fn: CMD},
|
|
60
|
+
[Keys.FRAC_INCLUSIVE]: {str: "/", fn: ActionType.CMD},
|
|
59
61
|
};
|
|
60
62
|
|
|
61
63
|
const NormalCommands = {
|
|
@@ -110,12 +112,12 @@ const ValidLeaves = [
|
|
|
110
112
|
];
|
|
111
113
|
|
|
112
114
|
const KeysForJumpContext = {
|
|
113
|
-
[
|
|
114
|
-
[
|
|
115
|
-
[
|
|
116
|
-
[
|
|
117
|
-
[
|
|
118
|
-
[
|
|
115
|
+
[CursorContext.IN_PARENS]: Keys.JUMP_OUT_PARENTHESES,
|
|
116
|
+
[CursorContext.IN_SUPER_SCRIPT]: Keys.JUMP_OUT_EXPONENT,
|
|
117
|
+
[CursorContext.IN_SUB_SCRIPT]: Keys.JUMP_OUT_BASE,
|
|
118
|
+
[CursorContext.BEFORE_FRACTION]: Keys.JUMP_INTO_NUMERATOR,
|
|
119
|
+
[CursorContext.IN_NUMERATOR]: Keys.JUMP_OUT_NUMERATOR,
|
|
120
|
+
[CursorContext.IN_DENOMINATOR]: Keys.JUMP_OUT_DENOMINATOR,
|
|
119
121
|
};
|
|
120
122
|
|
|
121
123
|
class MathWrapper {
|
|
@@ -179,14 +181,14 @@ class MathWrapper {
|
|
|
179
181
|
} else if (key === Keys.FRAC_EXCLUSIVE) {
|
|
180
182
|
// If there's nothing to the left of the cursor, then we want to
|
|
181
183
|
// leave the cursor to the left of the fraction after creating it.
|
|
182
|
-
const shouldNavigateLeft = cursor[this.MQ.L] === MQ_END;
|
|
184
|
+
const shouldNavigateLeft = cursor[this.MQ.L] === ActionType.MQ_END;
|
|
183
185
|
this.mathField.cmd("\\frac");
|
|
184
186
|
if (shouldNavigateLeft) {
|
|
185
187
|
this.mathField.keystroke("Left");
|
|
186
188
|
}
|
|
187
189
|
} else if (key === Keys.FRAC) {
|
|
188
190
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
189
|
-
const shouldNavigateLeft = cursor[this.MQ.L] === MQ_END;
|
|
191
|
+
const shouldNavigateLeft = cursor[this.MQ.L] === ActionType.MQ_END;
|
|
190
192
|
this.mathField.cmd("\\frac");
|
|
191
193
|
} else if (key === Keys.LOG_N) {
|
|
192
194
|
this.mathField.write("log_{ }\\left(\\right)");
|
|
@@ -218,9 +220,9 @@ class MathWrapper {
|
|
|
218
220
|
} else if (key === Keys.RIGHT) {
|
|
219
221
|
this._handleRightArrow(cursor);
|
|
220
222
|
} else if (/^[a-zA-Z]$/.test(key)) {
|
|
221
|
-
this.mathField[WRITE](key);
|
|
223
|
+
this.mathField[ActionType.WRITE](key);
|
|
222
224
|
} else if (/^NUM_\d/.test(key)) {
|
|
223
|
-
this.mathField[WRITE](key[4]);
|
|
225
|
+
this.mathField[ActionType.WRITE](key[4]);
|
|
224
226
|
}
|
|
225
227
|
|
|
226
228
|
if (!cursor.selection) {
|
|
@@ -325,7 +327,7 @@ class MathWrapper {
|
|
|
325
327
|
// when upgrading MathQuill.
|
|
326
328
|
|
|
327
329
|
_handleBackspaceInNthRoot(cursor) {
|
|
328
|
-
const isAtLeftEnd = cursor[this.MQ.L] === MQ_END;
|
|
330
|
+
const isAtLeftEnd = cursor[this.MQ.L] === ActionType.MQ_END;
|
|
329
331
|
|
|
330
332
|
const isRootEmpty = this._isInsideEmptyNode(
|
|
331
333
|
cursor.parent.parent.blocks[0].ends,
|
|
@@ -362,17 +364,17 @@ class MathWrapper {
|
|
|
362
364
|
}
|
|
363
365
|
|
|
364
366
|
switch (context) {
|
|
365
|
-
case
|
|
367
|
+
case CursorContext.IN_PARENS:
|
|
366
368
|
// Insert at the end of the parentheses, and then navigate right
|
|
367
369
|
// once more to get 'beyond' the parentheses.
|
|
368
370
|
cursor.insRightOf(cursor.parent.parent);
|
|
369
371
|
break;
|
|
370
372
|
|
|
371
|
-
case
|
|
373
|
+
case CursorContext.BEFORE_FRACTION:
|
|
372
374
|
// Find the nearest fraction to the right of the cursor.
|
|
373
375
|
let fractionNode;
|
|
374
376
|
let visitor = cursor;
|
|
375
|
-
while (visitor[this.MQ.R] !== MQ_END) {
|
|
377
|
+
while (visitor[this.MQ.R] !== ActionType.MQ_END) {
|
|
376
378
|
if (this._isFraction(visitor[this.MQ.R])) {
|
|
377
379
|
fractionNode = visitor[this.MQ.R];
|
|
378
380
|
}
|
|
@@ -384,7 +386,7 @@ class MathWrapper {
|
|
|
384
386
|
this.mathField.keystroke("Right");
|
|
385
387
|
break;
|
|
386
388
|
|
|
387
|
-
case
|
|
389
|
+
case CursorContext.IN_NUMERATOR:
|
|
388
390
|
// HACK(charlie): I can't find a better way to do this. The goal
|
|
389
391
|
// is to place the cursor at the start of the matching
|
|
390
392
|
// denominator. So, we identify the appropriate node, and
|
|
@@ -397,11 +399,11 @@ class MathWrapper {
|
|
|
397
399
|
}
|
|
398
400
|
break;
|
|
399
401
|
|
|
400
|
-
case
|
|
402
|
+
case CursorContext.IN_DENOMINATOR:
|
|
401
403
|
cursor.insRightOf(cursor.parent.parent);
|
|
402
404
|
break;
|
|
403
405
|
|
|
404
|
-
case
|
|
406
|
+
case CursorContext.IN_SUB_SCRIPT:
|
|
405
407
|
// Insert just beyond the superscript.
|
|
406
408
|
cursor.insRightOf(cursor.parent.parent);
|
|
407
409
|
|
|
@@ -413,7 +415,7 @@ class MathWrapper {
|
|
|
413
415
|
}
|
|
414
416
|
break;
|
|
415
417
|
|
|
416
|
-
case
|
|
418
|
+
case CursorContext.IN_SUPER_SCRIPT:
|
|
417
419
|
// Insert just beyond the superscript.
|
|
418
420
|
cursor.insRightOf(cursor.parent.parent);
|
|
419
421
|
break;
|
|
@@ -458,7 +460,10 @@ class MathWrapper {
|
|
|
458
460
|
leftNode.ctrlSeq === "\\le "
|
|
459
461
|
) {
|
|
460
462
|
this._handleBackspaceAfterLigaturedSymbol(cursor);
|
|
461
|
-
} else if (
|
|
463
|
+
} else if (
|
|
464
|
+
this._isNthRoot(grandparent) &&
|
|
465
|
+
leftNode === ActionType.MQ_END
|
|
466
|
+
) {
|
|
462
467
|
this._handleBackspaceInNthRoot(cursor);
|
|
463
468
|
} else {
|
|
464
469
|
this.mathField.keystroke("Backspace");
|
|
@@ -476,10 +481,10 @@ class MathWrapper {
|
|
|
476
481
|
// the entire expression, rather than between the `s` and the left
|
|
477
482
|
// parenthesis.
|
|
478
483
|
// From the cursor's perspective, this requires that our left node is
|
|
479
|
-
// the MQ_END node, that our grandparent is the left parenthesis, and
|
|
484
|
+
// the ActionType.MQ_END node, that our grandparent is the left parenthesis, and
|
|
480
485
|
// the nodes to the left of our grandparent comprise a valid function
|
|
481
486
|
// name.
|
|
482
|
-
if (cursor[this.MQ.L] === MQ_END) {
|
|
487
|
+
if (cursor[this.MQ.L] === ActionType.MQ_END) {
|
|
483
488
|
const parent = cursor.parent;
|
|
484
489
|
const grandparent = parent.parent;
|
|
485
490
|
if (grandparent.ctrlSeq === "\\left(") {
|
|
@@ -518,7 +523,7 @@ class MathWrapper {
|
|
|
518
523
|
|
|
519
524
|
const precedingNode = cursor[this.MQ.L];
|
|
520
525
|
const shouldPrefixWithParens =
|
|
521
|
-
precedingNode === MQ_END ||
|
|
526
|
+
precedingNode === ActionType.MQ_END ||
|
|
522
527
|
invalidPrefixes.includes(precedingNode.ctrlSeq.trim());
|
|
523
528
|
if (shouldPrefixWithParens) {
|
|
524
529
|
this.mathField.write("\\left(\\right)");
|
|
@@ -737,7 +742,10 @@ class MathWrapper {
|
|
|
737
742
|
}
|
|
738
743
|
|
|
739
744
|
_isInsideEmptyNode(cursor) {
|
|
740
|
-
return
|
|
745
|
+
return (
|
|
746
|
+
cursor[this.MQ.L] === ActionType.MQ_END &&
|
|
747
|
+
cursor[this.MQ.R] === ActionType.MQ_END
|
|
748
|
+
);
|
|
741
749
|
}
|
|
742
750
|
|
|
743
751
|
_handleBackspaceInRootIndex(cursor) {
|
|
@@ -769,14 +777,14 @@ class MathWrapper {
|
|
|
769
777
|
this.mathField.write(latex.replace(/^\\sqrt\[\]/, "\\sqrt"));
|
|
770
778
|
|
|
771
779
|
// Adjust the cursor to be to the left the sqrt.
|
|
772
|
-
if (reinsertionPoint === MQ_END) {
|
|
780
|
+
if (reinsertionPoint === ActionType.MQ_END) {
|
|
773
781
|
this.mathField.moveToDirEnd(this.MQ.L);
|
|
774
782
|
} else {
|
|
775
783
|
cursor.insRightOf(reinsertionPoint);
|
|
776
784
|
}
|
|
777
785
|
}
|
|
778
786
|
} else {
|
|
779
|
-
if (cursor[this.MQ.L] !== MQ_END) {
|
|
787
|
+
if (cursor[this.MQ.L] !== ActionType.MQ_END) {
|
|
780
788
|
// If the cursor is not at the leftmost position inside the
|
|
781
789
|
// root's index, delete a character.
|
|
782
790
|
this.mathField.keystroke("Backspace");
|
|
@@ -796,7 +804,7 @@ class MathWrapper {
|
|
|
796
804
|
cursor.insLeftOf(command?.startNode);
|
|
797
805
|
cursor.startSelection();
|
|
798
806
|
|
|
799
|
-
if (grandparent[this.MQ.R] !== MQ_END) {
|
|
807
|
+
if (grandparent[this.MQ.R] !== ActionType.MQ_END) {
|
|
800
808
|
cursor.insRightOf(grandparent[this.MQ.R]);
|
|
801
809
|
} else {
|
|
802
810
|
cursor.insRightOf(grandparent);
|
|
@@ -836,7 +844,7 @@ class MathWrapper {
|
|
|
836
844
|
// the parens.
|
|
837
845
|
cursor.insLeftOf(command.startNode);
|
|
838
846
|
cursor.startSelection();
|
|
839
|
-
if (rightNode === MQ_END) {
|
|
847
|
+
if (rightNode === ActionType.MQ_END) {
|
|
840
848
|
cursor.insAtRightEnd(cursor.parent);
|
|
841
849
|
} else {
|
|
842
850
|
cursor.insLeftOf(rightNode);
|
|
@@ -876,7 +884,7 @@ class MathWrapper {
|
|
|
876
884
|
// - \log(|x+1) => |\log(x+1)|
|
|
877
885
|
// - \log(|) => |
|
|
878
886
|
|
|
879
|
-
if (cursor[this.MQ.L] !== MQ_END) {
|
|
887
|
+
if (cursor[this.MQ.L] !== ActionType.MQ_END) {
|
|
880
888
|
// This command contains math and there's some math to
|
|
881
889
|
// the left of the cursor that we should delete normally
|
|
882
890
|
// before doing anything special.
|
|
@@ -929,9 +937,9 @@ class MathWrapper {
|
|
|
929
937
|
contextForCursor(cursor) {
|
|
930
938
|
// First, try to find any fraction to the right, unimpeded.
|
|
931
939
|
let visitor = cursor;
|
|
932
|
-
while (visitor[this.MQ.R] !== MQ_END) {
|
|
940
|
+
while (visitor[this.MQ.R] !== ActionType.MQ_END) {
|
|
933
941
|
if (this._isFraction(visitor[this.MQ.R])) {
|
|
934
|
-
return
|
|
942
|
+
return CursorContext.BEFORE_FRACTION;
|
|
935
943
|
} else if (!this._isLeaf(visitor[this.MQ.R])) {
|
|
936
944
|
break;
|
|
937
945
|
}
|
|
@@ -941,17 +949,17 @@ class MathWrapper {
|
|
|
941
949
|
// If that didn't work, check if the parent or grandparent is a special
|
|
942
950
|
// context, so that we can jump outwards.
|
|
943
951
|
if (this._isParens(cursor.parent && cursor.parent.parent)) {
|
|
944
|
-
return
|
|
952
|
+
return CursorContext.IN_PARENS;
|
|
945
953
|
} else if (this._isNumerator(cursor.parent)) {
|
|
946
|
-
return
|
|
954
|
+
return CursorContext.IN_NUMERATOR;
|
|
947
955
|
} else if (this._isDenominator(cursor.parent)) {
|
|
948
|
-
return
|
|
956
|
+
return CursorContext.IN_DENOMINATOR;
|
|
949
957
|
} else if (this._isSubScript(cursor.parent)) {
|
|
950
|
-
return
|
|
958
|
+
return CursorContext.IN_SUB_SCRIPT;
|
|
951
959
|
} else if (this._isSuperScript(cursor.parent)) {
|
|
952
|
-
return
|
|
960
|
+
return CursorContext.IN_SUPER_SCRIPT;
|
|
953
961
|
} else {
|
|
954
|
-
return
|
|
962
|
+
return CursorContext.NONE;
|
|
955
963
|
}
|
|
956
964
|
}
|
|
957
965
|
|