@khanacademy/math-input 1.0.0 → 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 +10 -0
- 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/index.d.ts +1 -1
- package/dist/components/keypad/index.js.flow +1 -3
- 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/node-manager.d.ts +2 -5
- package/dist/components/node-manager.js.flow +2 -5
- package/dist/components/popover-state-machine.d.ts +1 -8
- package/dist/components/popover-state-machine.js.flow +2 -8
- package/dist/components/provided-keypad.d.ts +1 -4
- package/dist/components/provided-keypad.js.flow +1 -4
- 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.css +0 -3
- package/dist/es/index.js +415 -455
- 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.css +0 -3
- package/dist/index.d.ts +3 -6
- package/dist/index.js +438 -477
- package/dist/index.js.flow +3 -6
- package/dist/index.js.map +1 -1
- package/dist/store/actions.d.ts +64 -0
- package/dist/store/actions.js.flow +101 -0
- package/dist/store/echo-reducer.d.ts +2 -3
- package/dist/store/echo-reducer.js.flow +2 -6
- package/dist/store/index.d.ts +5 -41
- package/dist/store/index.js.flow +5 -52
- package/dist/store/input-reducer.d.ts +2 -5
- package/dist/store/input-reducer.js.flow +3 -6
- package/dist/store/keypad-reducer.d.ts +2 -7
- package/dist/store/keypad-reducer.js.flow +3 -8
- package/dist/store/layout-reducer.d.ts +2 -19
- package/dist/store/layout-reducer.js.flow +3 -20
- package/dist/store/pager-reducer.d.ts +2 -11
- package/dist/store/pager-reducer.js.flow +3 -12
- package/dist/store/shared.d.ts +2 -1
- package/dist/store/shared.js.flow +2 -1
- package/dist/store/types.d.ts +5 -5
- package/dist/store/types.js.flow +5 -5
- package/dist/types.d.ts +28 -16
- package/dist/types.js.flow +32 -20
- package/less/overrides.less +0 -6
- 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/index.tsx +1 -1
- package/src/components/keypad-button.tsx +20 -21
- package/src/components/keypad-container.tsx +9 -10
- package/src/components/keypad.tsx +1 -1
- 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/node-manager.ts +2 -2
- package/src/components/popover-state-machine.ts +2 -10
- package/src/components/provided-keypad.tsx +3 -12
- 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 +67 -0
- package/src/store/actions.ts +179 -0
- package/src/store/echo-reducer.ts +10 -7
- package/src/store/index.ts +24 -24
- package/src/store/input-reducer.ts +7 -6
- package/src/store/keypad-reducer.ts +3 -6
- package/src/store/layout-reducer.ts +12 -11
- package/src/store/pager-reducer.ts +30 -46
- package/src/store/shared.ts +4 -4
- package/src/store/types.ts +21 -5
- package/src/types.ts +32 -20
- package/src/utils.ts +3 -3
- package/tsconfig-build.tsbuildinfo +1 -0
- package/dist/actions/index.d.ts +0 -31
- package/dist/actions/index.js.flow +0 -40
- package/dist/consts.d.ts +0 -51
- package/dist/consts.js.flow +0 -66
- package/src/actions/index.ts +0 -57
- package/src/consts.ts +0 -91
- package/tsconfig.tsbuildinfo +0 -1
- /package/src/components/{gesture-manager.tsx → gesture-manager.ts} +0 -0
- /package/{tsconfig.json → tsconfig-build.json} +0 -0
|
@@ -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
|
|
|
@@ -31,7 +31,7 @@ const allPages = function (props: Props): React.ReactElement {
|
|
|
31
31
|
return pages;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
export default class
|
|
34
|
+
export default class Keypad extends React.Component<Props, State> {
|
|
35
35
|
state: State = {
|
|
36
36
|
selectedPage: "Numbers",
|
|
37
37
|
};
|
|
@@ -6,7 +6,7 @@ import {StyleSheet, css} from "aphrodite";
|
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import {connect} from "react-redux";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {BorderDirection, BorderStyles, KeyType} from "../enums";
|
|
10
10
|
import {View} from "../fake-react-native-web/index";
|
|
11
11
|
|
|
12
12
|
import {
|
|
@@ -23,9 +23,8 @@ import CornerDecal from "./corner-decal";
|
|
|
23
23
|
import Icon from "./icon";
|
|
24
24
|
import MultiSymbolGrid from "./multi-symbol-grid";
|
|
25
25
|
|
|
26
|
-
import type {KeyType} from "../consts";
|
|
27
26
|
import type {State} from "../store/types";
|
|
28
|
-
import type {Border, KeyConfig,
|
|
27
|
+
import type {Border, KeyConfig, IconConfig} from "../types";
|
|
29
28
|
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
30
29
|
|
|
31
30
|
interface ReduxProps {
|
|
@@ -41,7 +40,7 @@ interface Props extends ReduxProps {
|
|
|
41
40
|
focused: boolean;
|
|
42
41
|
popoverEnabled: boolean;
|
|
43
42
|
type: KeyType;
|
|
44
|
-
icon:
|
|
43
|
+
icon: IconConfig;
|
|
45
44
|
style?: StyleType;
|
|
46
45
|
onTouchCancel?: (evt: React.TouchEvent<HTMLDivElement>) => void;
|
|
47
46
|
onTouchEnd?: (evt: React.TouchEvent<HTMLDivElement>) => void;
|
|
@@ -102,7 +101,7 @@ class KeypadButton extends React.PureComponent<Props> {
|
|
|
102
101
|
// object. This method must be called whenever a property that
|
|
103
102
|
// influences the possible outcomes of `this._getFocusStyle` and
|
|
104
103
|
// `this._getButtonStyle` changes (such as `this.buttonSizeStyle`).
|
|
105
|
-
for (const type of Object.
|
|
104
|
+
for (const type of Object.values(KeyType)) {
|
|
106
105
|
css(View.styles.initial, ...this._getFocusStyle(type));
|
|
107
106
|
|
|
108
107
|
for (const borders of Object.values(BorderStyles)) {
|
|
@@ -114,11 +113,11 @@ class KeypadButton extends React.PureComponent<Props> {
|
|
|
114
113
|
}
|
|
115
114
|
};
|
|
116
115
|
|
|
117
|
-
_getFocusStyle = (type) => {
|
|
116
|
+
_getFocusStyle = (type: KeyType) => {
|
|
118
117
|
let focusBackgroundStyle;
|
|
119
118
|
if (
|
|
120
|
-
type ===
|
|
121
|
-
type ===
|
|
119
|
+
type === KeyType.INPUT_NAVIGATION ||
|
|
120
|
+
type === KeyType.KEYPAD_NAVIGATION
|
|
122
121
|
) {
|
|
123
122
|
focusBackgroundStyle = styles.light;
|
|
124
123
|
} else {
|
|
@@ -132,35 +131,35 @@ class KeypadButton extends React.PureComponent<Props> {
|
|
|
132
131
|
// Select the appropriate style for the button.
|
|
133
132
|
let backgroundStyle;
|
|
134
133
|
switch (type) {
|
|
135
|
-
case
|
|
134
|
+
case KeyType.EMPTY:
|
|
136
135
|
backgroundStyle = styles.empty;
|
|
137
136
|
break;
|
|
138
137
|
|
|
139
|
-
case
|
|
140
|
-
case
|
|
138
|
+
case KeyType.MANY:
|
|
139
|
+
case KeyType.VALUE:
|
|
141
140
|
backgroundStyle = styles.value;
|
|
142
141
|
break;
|
|
143
142
|
|
|
144
|
-
case
|
|
143
|
+
case KeyType.OPERATOR:
|
|
145
144
|
backgroundStyle = styles.operator;
|
|
146
145
|
break;
|
|
147
146
|
|
|
148
|
-
case
|
|
149
|
-
case
|
|
147
|
+
case KeyType.INPUT_NAVIGATION:
|
|
148
|
+
case KeyType.KEYPAD_NAVIGATION:
|
|
150
149
|
backgroundStyle = styles.control;
|
|
151
150
|
break;
|
|
152
151
|
|
|
153
|
-
case
|
|
152
|
+
case KeyType.ECHO:
|
|
154
153
|
backgroundStyle = null;
|
|
155
154
|
break;
|
|
156
155
|
}
|
|
157
156
|
|
|
158
157
|
const borderStyle = [];
|
|
159
|
-
if (borders.includes(
|
|
158
|
+
if (borders.includes(BorderDirection.LEFT)) {
|
|
160
159
|
// @ts-expect-error TS2345
|
|
161
160
|
borderStyle.push(styles.leftBorder);
|
|
162
161
|
}
|
|
163
|
-
if (borders.includes(
|
|
162
|
+
if (borders.includes(BorderDirection.BOTTOM)) {
|
|
164
163
|
// @ts-expect-error TS2345
|
|
165
164
|
borderStyle.push(styles.bottomBorder);
|
|
166
165
|
}
|
|
@@ -169,7 +168,7 @@ class KeypadButton extends React.PureComponent<Props> {
|
|
|
169
168
|
styles.buttonBase,
|
|
170
169
|
backgroundStyle,
|
|
171
170
|
...borderStyle,
|
|
172
|
-
type ===
|
|
171
|
+
type === KeyType.ECHO && styles.echo,
|
|
173
172
|
this.buttonSizeStyle,
|
|
174
173
|
// React Native allows you to set the 'style' props on user defined
|
|
175
174
|
// components.
|
|
@@ -198,7 +197,7 @@ class KeypadButton extends React.PureComponent<Props> {
|
|
|
198
197
|
// We render in the focus state if the key is focused, or if it's an
|
|
199
198
|
// echo.
|
|
200
199
|
const renderFocused =
|
|
201
|
-
(!disabled && focused) || popoverEnabled || type ===
|
|
200
|
+
(!disabled && focused) || popoverEnabled || type === KeyType.ECHO;
|
|
202
201
|
const buttonStyle = this._getButtonStyle(type, borders, style);
|
|
203
202
|
const focusStyle = this._getFocusStyle(type);
|
|
204
203
|
const iconWrapperStyle = [
|
|
@@ -219,9 +218,9 @@ class KeypadButton extends React.PureComponent<Props> {
|
|
|
219
218
|
childKeys &&
|
|
220
219
|
childKeys.length > 0 && <CornerDecal style={styles.decalInset} />;
|
|
221
220
|
|
|
222
|
-
if (type ===
|
|
221
|
+
if (type === KeyType.EMPTY) {
|
|
223
222
|
return <View style={buttonStyle} {...eventHandlers} />;
|
|
224
|
-
} else if (type ===
|
|
223
|
+
} else if (type === KeyType.MANY) {
|
|
225
224
|
// TODO(charlie): Make the long-press interaction accessible. See
|
|
226
225
|
// the TODO in key-configs.js for more.
|
|
227
226
|
const manyButtonA11yMarkup = {
|
|
@@ -2,9 +2,9 @@ import {StyleSheet} from "aphrodite";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import {connect} from "react-redux";
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {KeypadTypes, LayoutModes} from "../consts";
|
|
5
|
+
import {LayoutMode, KeypadType} from "../enums";
|
|
7
6
|
import {View} from "../fake-react-native-web/index";
|
|
7
|
+
import {setPageSize} from "../store/actions";
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
innerBorderColor,
|
|
@@ -18,7 +18,6 @@ import NavigationPad from "./navigation-pad";
|
|
|
18
18
|
import Styles from "./styles";
|
|
19
19
|
import * as zIndexes from "./z-indexes";
|
|
20
20
|
|
|
21
|
-
import type {KeypadType} from "../consts";
|
|
22
21
|
import type {State as ReduxState} from "../store/types";
|
|
23
22
|
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
24
23
|
|
|
@@ -28,7 +27,7 @@ interface ReduxProps {
|
|
|
28
27
|
active?: boolean;
|
|
29
28
|
extraKeys?: ReadonlyArray<string>;
|
|
30
29
|
keypadType?: KeypadType;
|
|
31
|
-
layoutMode?:
|
|
30
|
+
layoutMode?: LayoutMode;
|
|
32
31
|
navigationPadEnabled?: boolean;
|
|
33
32
|
}
|
|
34
33
|
|
|
@@ -128,8 +127,8 @@ class KeypadContainer extends React.Component<Props, State> {
|
|
|
128
127
|
// crop themselves. At least we're colocating all the layout
|
|
129
128
|
// information in this component, though.
|
|
130
129
|
roundTopLeft:
|
|
131
|
-
layoutMode ===
|
|
132
|
-
roundTopRight: layoutMode ===
|
|
130
|
+
layoutMode === LayoutMode.COMPACT && !navigationPadEnabled,
|
|
131
|
+
roundTopRight: layoutMode === LayoutMode.COMPACT,
|
|
133
132
|
};
|
|
134
133
|
|
|
135
134
|
// Select the appropriate keyboard given the type.
|
|
@@ -140,10 +139,10 @@ class KeypadContainer extends React.Component<Props, State> {
|
|
|
140
139
|
// clear what that format would look like exactly. Plus, there aren't
|
|
141
140
|
// very many of them. So to keep us moving, we'll just hardcode.
|
|
142
141
|
switch (keypadType) {
|
|
143
|
-
case
|
|
142
|
+
case KeypadType.FRACTION:
|
|
144
143
|
return <FractionKeypad {...keypadProps} />;
|
|
145
144
|
|
|
146
|
-
case
|
|
145
|
+
case KeypadType.EXPRESSION:
|
|
147
146
|
return <ExpressionKeypad {...keypadProps} />;
|
|
148
147
|
|
|
149
148
|
default:
|
|
@@ -186,7 +185,7 @@ class KeypadContainer extends React.Component<Props, State> {
|
|
|
186
185
|
const keypadStyle = [
|
|
187
186
|
row,
|
|
188
187
|
styles.keypadBorder,
|
|
189
|
-
layoutMode ===
|
|
188
|
+
layoutMode === LayoutMode.FULLSCREEN
|
|
190
189
|
? styles.fullscreen
|
|
191
190
|
: styles.compact,
|
|
192
191
|
];
|
|
@@ -211,7 +210,7 @@ class KeypadContainer extends React.Component<Props, State> {
|
|
|
211
210
|
>
|
|
212
211
|
{navigationPadEnabled && (
|
|
213
212
|
<NavigationPad
|
|
214
|
-
roundTopLeft={layoutMode ===
|
|
213
|
+
roundTopLeft={layoutMode === LayoutMode.COMPACT}
|
|
215
214
|
style={styles.navigationPadContainer}
|
|
216
215
|
/>
|
|
217
216
|
)}
|
|
@@ -7,8 +7,8 @@ import * as React from "react";
|
|
|
7
7
|
import ReactDOM from "react-dom";
|
|
8
8
|
import {connect} from "react-redux";
|
|
9
9
|
|
|
10
|
-
import {removeEcho} from "../actions/index";
|
|
11
10
|
import {View} from "../fake-react-native-web/index";
|
|
11
|
+
import {removeEcho} from "../store/actions";
|
|
12
12
|
|
|
13
13
|
import EchoManager from "./echo-manager";
|
|
14
14
|
import PopoverManager from "./popover-manager";
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
|
|
8
|
-
import {KeyTypes} from "../consts";
|
|
9
8
|
import KeyConfigs from "../data/key-configs";
|
|
10
9
|
import Keys from "../data/keys";
|
|
10
|
+
import {KeyType} from "../enums";
|
|
11
11
|
|
|
12
12
|
import EmptyKeypadButton from "./empty-keypad-button";
|
|
13
13
|
import TouchableKeypadButton from "./touchable-keypad-button";
|
|
@@ -35,7 +35,7 @@ class ManyKeypadButton extends React.Component<Props> {
|
|
|
35
35
|
} else {
|
|
36
36
|
const keyConfig = {
|
|
37
37
|
id: Keys.MANY,
|
|
38
|
-
type:
|
|
38
|
+
type: KeyType.MANY,
|
|
39
39
|
childKeyIds: keys,
|
|
40
40
|
};
|
|
41
41
|
return <TouchableKeypadButton keyConfig={keyConfig} {...rest} />;
|
|
@@ -6,20 +6,19 @@
|
|
|
6
6
|
import {StyleSheet} from "aphrodite";
|
|
7
7
|
import * as React from "react";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {IconType} from "../enums";
|
|
10
10
|
import {View} from "../fake-react-native-web/index";
|
|
11
|
+
import {IconConfig} from "../types";
|
|
11
12
|
|
|
12
13
|
import {iconSizeHeightPx, iconSizeWidthPx} from "./common-style";
|
|
13
14
|
import Icon from "./icon";
|
|
14
15
|
import Styles from "./styles";
|
|
15
16
|
|
|
16
|
-
import type {Icon as IconType} from "../types";
|
|
17
|
-
|
|
18
17
|
const {row, column, centered, fullWidth} = Styles;
|
|
19
18
|
|
|
20
19
|
type Props = {
|
|
21
20
|
focused: boolean;
|
|
22
|
-
icons: ReadonlyArray<
|
|
21
|
+
icons: ReadonlyArray<IconConfig>;
|
|
23
22
|
};
|
|
24
23
|
|
|
25
24
|
class MultiSymbolGrid extends React.Component<Props> {
|
|
@@ -32,7 +31,7 @@ class MultiSymbolGrid extends React.Component<Props> {
|
|
|
32
31
|
// Supporting other types of icons is possible but would require
|
|
33
32
|
// some styles coercion and doesn't seem worthwhile right now.
|
|
34
33
|
icons.forEach((icon) => {
|
|
35
|
-
if (icon.type !==
|
|
34
|
+
if (icon.type !== IconType.MATH) {
|
|
36
35
|
throw new Error(
|
|
37
36
|
`Received invalid icon: type=${icon.type}, ` +
|
|
38
37
|
`data=${icon.data}`,
|