@khanacademy/math-input 17.0.3 → 17.0.5

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.
Files changed (84) hide show
  1. package/dist/es/index.js +2 -2
  2. package/dist/es/index.js.map +1 -1
  3. package/dist/index.js +2 -2
  4. package/dist/index.js.map +1 -1
  5. package/package.json +5 -2
  6. package/.eslintrc.js +0 -18
  7. package/CHANGELOG.md +0 -654
  8. package/less/main.less +0 -2
  9. package/less/overrides.less +0 -122
  10. package/src/components/__tests__/integration.test.tsx +0 -300
  11. package/src/components/aphrodite-css-transition-group/index.tsx +0 -78
  12. package/src/components/aphrodite-css-transition-group/transition-child.tsx +0 -192
  13. package/src/components/aphrodite-css-transition-group/types.ts +0 -20
  14. package/src/components/aphrodite-css-transition-group/util.ts +0 -97
  15. package/src/components/input/__tests__/context-tracking.test.ts +0 -176
  16. package/src/components/input/__tests__/mathquill-helpers.test.ts +0 -105
  17. package/src/components/input/__tests__/mathquill.test.ts +0 -747
  18. package/src/components/input/__tests__/test-math-wrapper.ts +0 -29
  19. package/src/components/input/cursor-contexts.ts +0 -37
  20. package/src/components/input/cursor-handle.tsx +0 -137
  21. package/src/components/input/cursor-styles.ts +0 -10
  22. package/src/components/input/drag-listener.ts +0 -79
  23. package/src/components/input/math-input.tsx +0 -1036
  24. package/src/components/input/math-wrapper.ts +0 -189
  25. package/src/components/input/mathquill-helpers.ts +0 -262
  26. package/src/components/input/mathquill-instance.ts +0 -106
  27. package/src/components/input/mathquill-types.ts +0 -32
  28. package/src/components/input/scroll-into-view.ts +0 -65
  29. package/src/components/key-handlers/__tests__/handle-jump-out.test.ts +0 -94
  30. package/src/components/key-handlers/handle-arrow.ts +0 -70
  31. package/src/components/key-handlers/handle-backspace.ts +0 -277
  32. package/src/components/key-handlers/handle-exponent.ts +0 -53
  33. package/src/components/key-handlers/handle-jump-out.ts +0 -107
  34. package/src/components/key-handlers/key-translator.ts +0 -222
  35. package/src/components/keypad/__tests__/__snapshots__/keypad.test.tsx.snap +0 -1913
  36. package/src/components/keypad/__tests__/__snapshots__/mobile-keypad.test.tsx.snap +0 -600
  37. package/src/components/keypad/__tests__/keypad-button.test.tsx +0 -84
  38. package/src/components/keypad/__tests__/keypad-v2-mathquill.test.tsx +0 -304
  39. package/src/components/keypad/__tests__/keypad-v2.cypress.ts +0 -16
  40. package/src/components/keypad/__tests__/keypad.test.tsx +0 -321
  41. package/src/components/keypad/__tests__/mobile-keypad.test.tsx +0 -115
  42. package/src/components/keypad/__tests__/test-data-tabs.ts +0 -21
  43. package/src/components/keypad/button-assets.tsx +0 -1880
  44. package/src/components/keypad/index.tsx +0 -2
  45. package/src/components/keypad/keypad-button.stories.tsx +0 -81
  46. package/src/components/keypad/keypad-button.tsx +0 -124
  47. package/src/components/keypad/keypad-mathquill.stories.tsx +0 -109
  48. package/src/components/keypad/keypad-pages/extras-page.tsx +0 -35
  49. package/src/components/keypad/keypad-pages/fractions-page.tsx +0 -125
  50. package/src/components/keypad/keypad-pages/geometry-page.tsx +0 -34
  51. package/src/components/keypad/keypad-pages/keypad-pages.stories.tsx +0 -37
  52. package/src/components/keypad/keypad-pages/numbers-page.tsx +0 -94
  53. package/src/components/keypad/keypad-pages/operators-page.tsx +0 -117
  54. package/src/components/keypad/keypad.tsx +0 -233
  55. package/src/components/keypad/mobile-keypad-internals.tsx +0 -240
  56. package/src/components/keypad/mobile-keypad.tsx +0 -24
  57. package/src/components/keypad/navigation-button.tsx +0 -127
  58. package/src/components/keypad/navigation-pad.stories.tsx +0 -26
  59. package/src/components/keypad/navigation-pad.tsx +0 -67
  60. package/src/components/keypad/shared-keys.tsx +0 -109
  61. package/src/components/keypad/utils.ts +0 -34
  62. package/src/components/keypad-context.tsx +0 -70
  63. package/src/components/prop-types.ts +0 -16
  64. package/src/components/tabbar/__tests__/tabbar.test.tsx +0 -105
  65. package/src/components/tabbar/icons.tsx +0 -122
  66. package/src/components/tabbar/index.ts +0 -1
  67. package/src/components/tabbar/item.tsx +0 -146
  68. package/src/components/tabbar/tabbar.stories.tsx +0 -83
  69. package/src/components/tabbar/tabbar.tsx +0 -65
  70. package/src/data/key-configs.ts +0 -770
  71. package/src/data/keys.ts +0 -123
  72. package/src/enums.ts +0 -27
  73. package/src/fake-react-native-web/index.ts +0 -11
  74. package/src/fake-react-native-web/text.tsx +0 -55
  75. package/src/fake-react-native-web/view.tsx +0 -91
  76. package/src/full-keypad.stories.tsx +0 -142
  77. package/src/full-mobile-input.stories.tsx +0 -115
  78. package/src/index.ts +0 -52
  79. package/src/types.ts +0 -70
  80. package/src/utils.test.ts +0 -33
  81. package/src/utils.ts +0 -61
  82. package/src/version.ts +0 -10
  83. package/tsconfig-build.json +0 -11
  84. package/tsconfig-build.tsbuildinfo +0 -1
@@ -1,189 +0,0 @@
1
- // Notes about MathQuill
2
- //
3
- // MathQuill's stores its layout as nested linked lists. Each node in the
4
- // list has MQ.L '-1' and MQ.R '1' properties that define links to
5
- // the left and right nodes respectively. They also have
6
- //
7
- // ctrlSeq: contains the latex code snippet that defines that node.
8
- // jQ: jQuery object for the DOM node(s) for this MathQuill node.
9
- // ends: pointers to the nodes at the ends of the container.
10
- // parent: parent node.
11
- // blocks: an array containing one or more nodes that make up the node.
12
- // sub?: subscript node if there is one as is the case in log_n
13
- //
14
- // All of the code below is super fragile. Please be especially careful
15
- // when upgrading MathQuill.
16
-
17
- import $ from "jquery";
18
-
19
- import handleBackspace from "../key-handlers/handle-backspace";
20
- import keyTranslator from "../key-handlers/key-translator";
21
-
22
- import {getCursorContext, maybeFindCommand} from "./mathquill-helpers";
23
- import {createMathField, mathQuillInstance} from "./mathquill-instance";
24
-
25
- import type {
26
- MathFieldInterface,
27
- MathFieldUpdaterCallback,
28
- } from "./mathquill-types";
29
- import type Key from "../../data/keys";
30
-
31
- const mobileKeyTranslator: Record<Key, MathFieldUpdaterCallback> = {
32
- ...keyTranslator,
33
- // note(Matthew): our mobile backspace logic is really complicated
34
- // and for some reason doesn't really work in the desktop experience.
35
- // So we default to the basic backspace functionality in the
36
- // key translator and overwrite it with the complicated logic here
37
- // until we can unify the experiences (if we even want to).
38
- // https://khanacademy.atlassian.net/browse/LC-906
39
- BACKSPACE: handleBackspace,
40
- };
41
-
42
- /**
43
- * This file contains a wrapper around MathQuill so that we can provide a
44
- * more regular interface for the functionality we need while insulating us
45
- * from MathQuill changes.
46
- */
47
- class MathWrapper {
48
- mathField: MathFieldInterface; // MathQuill MathField input
49
- callbacks: any;
50
-
51
- constructor(element, callbacks = {}) {
52
- this.mathField = createMathField(element, () => {
53
- return {
54
- // use a span instead of a textarea so that we don't bring up the
55
- // native keyboard on mobile when selecting the input
56
- substituteTextarea: function () {
57
- return document.createElement("span");
58
- },
59
- };
60
- });
61
- this.callbacks = callbacks;
62
- }
63
-
64
- focus() {
65
- // HACK(charlie): We shouldn't reaching into MathQuill internals like
66
- // this, but it's the easiest way to allow us to manage the focus state
67
- // ourselves.
68
- this.mathField.cursor().show();
69
-
70
- // Set MathQuill's internal state to reflect the focus, otherwise it
71
- // will consistently try to hide the cursor on key-press and introduce
72
- // layout jank.
73
- this.mathField.focus();
74
- }
75
-
76
- blur() {
77
- this.mathField.cursor().hide();
78
- this.mathField.blur();
79
- }
80
-
81
- /**
82
- * Handle a key press and return the resulting cursor state.
83
- *
84
- * @param {Key} key - an enum representing the key that was pressed
85
- * @returns {object} a cursor object, consisting of a cursor context
86
- */
87
- pressKey(key: Key) {
88
- const cursor = this.getCursor();
89
- const translator = mobileKeyTranslator[key];
90
-
91
- if (translator) {
92
- translator(this.mathField, key);
93
- }
94
-
95
- if (!cursor.selection) {
96
- // don't show the cursor for selections
97
- cursor.show();
98
- }
99
-
100
- if (this.callbacks.onSelectionChanged) {
101
- this.callbacks.onSelectionChanged(cursor.selection);
102
- }
103
-
104
- // NOTE(charlie): It's insufficient to do this as an `edited` handler
105
- // on the MathField, as that handler isn't triggered on navigation
106
- // events.
107
- return {
108
- context: this.contextForCursor(),
109
- };
110
- }
111
-
112
- /**
113
- * Place the cursor beside the node located at the given coordinates.
114
- *
115
- * @param {number} x - the x coordinate in the viewport
116
- * @param {number} y - the y coordinate in the viewport
117
- * @param {Node} hitNode - the node next to which the cursor should be
118
- * placed; if provided, the coordinates will be used
119
- * to determine on which side of the node the cursor
120
- * should be placed
121
- */
122
- setCursorPosition(x: number, y: number, hitNode: HTMLElement) {
123
- const el = hitNode || document.elementFromPoint(x, y);
124
-
125
- if (el) {
126
- const cursor = this.getCursor();
127
-
128
- if (el.hasAttribute("mq-root-block")) {
129
- // If we're in the empty area place the cursor at the right
130
- // end of the expression.
131
- cursor.insAtRightEnd(this.mathField.controller().root);
132
- } else {
133
- // Otherwise place beside the element at x, y.
134
- const controller = this.mathField.controller();
135
-
136
- const pageX = x - document.body.scrollLeft;
137
- const pageY = y - document.body.scrollTop;
138
- controller.seek($(el), pageX, pageY).cursor.startSelection();
139
-
140
- // Unless that would leave us mid-command, in which case, we
141
- // need to adjust and place the cursor inside the parens
142
- // following the command.
143
- const command = maybeFindCommand(cursor[mathQuillInstance.L]);
144
- if (command && command.endNode) {
145
- // NOTE(charlie): endNode should definitely be \left(.
146
- cursor.insLeftOf(command.endNode);
147
- this.mathField.keystroke("Right");
148
- }
149
- }
150
-
151
- if (this.callbacks.onCursorMove) {
152
- this.callbacks.onCursorMove({
153
- context: this.contextForCursor(),
154
- });
155
- }
156
- }
157
- }
158
-
159
- // note(Matthew): extracted this logic to share it elsewhere,
160
- // but it's part of the public MathWrapper API
161
- getCursor() {
162
- return this.mathField.cursor();
163
- }
164
-
165
- // note(Matthew): extracted this logic to keep this file focused,
166
- // but it's part of the public MathWrapper API
167
- contextForCursor() {
168
- return getCursorContext(this.mathField);
169
- }
170
-
171
- getSelection() {
172
- return this.getCursor().selection;
173
- }
174
-
175
- getContent() {
176
- return this.mathField.latex();
177
- }
178
-
179
- setContent(latex: string) {
180
- this.mathField.latex(latex);
181
- }
182
-
183
- isEmpty() {
184
- const cursor = this.getCursor();
185
- return cursor.parent.id === 1 && cursor[1] === 0 && cursor[-1] === 0;
186
- }
187
- }
188
-
189
- export default MathWrapper;
@@ -1,262 +0,0 @@
1
- import {CursorContext} from "./cursor-contexts";
2
- import {mathQuillInstance} from "./mathquill-instance";
3
- import {MathFieldActionType} from "./mathquill-types";
4
-
5
- import type {MathFieldInterface} from "./mathquill-types";
6
-
7
- const Numerals = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
8
- const GreekLetters = ["\\theta", "\\pi"];
9
- const Letters = [
10
- "A",
11
- "B",
12
- "C",
13
- "D",
14
- "E",
15
- "F",
16
- "G",
17
- "H",
18
- "I",
19
- "J",
20
- "K",
21
- "L",
22
- "M",
23
- "N",
24
- "O",
25
- "P",
26
- "Q",
27
- "R",
28
- "S",
29
- "T",
30
- "U",
31
- "V",
32
- "W",
33
- "X",
34
- "Y",
35
- "Z",
36
- ];
37
-
38
- // We only consider numerals, variables, and Greek Letters to be proper
39
- // leaf nodes.
40
- const ValidLeaves = [
41
- ...Numerals,
42
- ...GreekLetters,
43
- ...Letters.map((letter) => letter.toLowerCase()),
44
- ...Letters.map((letter) => letter.toUpperCase()),
45
- ];
46
-
47
- const mqNodeHasClass = (node: any, className: string): boolean =>
48
- node._el && (node._el as HTMLElement).classList.contains(className);
49
-
50
- export function isFraction(node): boolean {
51
- return mqNodeHasClass(node, "mq-fraction");
52
- }
53
-
54
- export function isNumerator(node): boolean {
55
- return mqNodeHasClass(node, "mq-numerator");
56
- }
57
-
58
- export function isDenominator(node): boolean {
59
- return mqNodeHasClass(node, "mq-denominator");
60
- }
61
-
62
- export function isSubScript(node): boolean {
63
- // NOTE(charlie): MyScript has a structure whereby its superscripts seem
64
- // to be represented as a parent node with 'mq-sup-only' containing a
65
- // single child with 'mq-sup'.
66
- return (
67
- mqNodeHasClass(node, "mq-sub-only") || mqNodeHasClass(node, "mq-sub")
68
- );
69
- }
70
-
71
- export function isSuperScript(node): boolean {
72
- // NOTE(charlie): MyScript has a structure whereby its superscripts seem
73
- // to be represented as a parent node with 'mq-sup-only' containing a
74
- // single child with 'mq-sup'.
75
- return (
76
- mqNodeHasClass(node, "mq-sup-only") || mqNodeHasClass(node, "mq-sup")
77
- );
78
- }
79
-
80
- export function isParens(node): boolean {
81
- return node && node.ctrlSeq === "\\left(";
82
- }
83
-
84
- export function isLeaf(node): boolean {
85
- return node && node.ctrlSeq && ValidLeaves.includes(node.ctrlSeq.trim());
86
- }
87
-
88
- export function isSquareRoot(node): boolean {
89
- return node.blocks && mqNodeHasClass(node.blocks[0], "mq-sqrt-stem");
90
- }
91
-
92
- export function isNthRoot(node): boolean {
93
- return node.blocks && mqNodeHasClass(node.blocks[0], "mq-nthroot");
94
- }
95
-
96
- export function isNthRootIndex(node): boolean {
97
- return mqNodeHasClass(node, "mq-nthroot");
98
- }
99
-
100
- export function isInsideLogIndex(cursor): boolean {
101
- const grandparent = cursor.parent.parent;
102
-
103
- if (grandparent && mqNodeHasClass(grandparent, "mq-supsub")) {
104
- const command = maybeFindCommandBeforeParens(grandparent);
105
-
106
- if (command && command.name === "\\log") {
107
- return true;
108
- }
109
- }
110
-
111
- return false;
112
- }
113
-
114
- export function isInsideEmptyNode(cursor): boolean {
115
- return (
116
- cursor[mathQuillInstance.L] === MathFieldActionType.MQ_END &&
117
- cursor[mathQuillInstance.R] === MathFieldActionType.MQ_END
118
- );
119
- }
120
-
121
- export function selectNode(node, cursor) {
122
- cursor.insLeftOf(node);
123
- cursor.startSelection();
124
- cursor.insRightOf(node);
125
- cursor.select();
126
- cursor.endSelection();
127
- }
128
-
129
- /**
130
- * Return the start node, end node, and full name of the command of which
131
- * the initial node is a part, or `null` if the node is not part of a
132
- * command.
133
- *
134
- * @param {node} initialNode - the node to included as part of the command
135
- * @returns {null|object} - `null` or an object containing the start node
136
- * (`startNode`), end node (`endNode`), and full
137
- * name (`name`) of the command
138
- */
139
- export function maybeFindCommand(initialNode) {
140
- if (!initialNode) {
141
- return null;
142
- }
143
-
144
- // MathQuill stores commands as separate characters so that
145
- // users can delete commands one character at a time. We iterate over
146
- // the nodes from right to left until we hit a sequence starting with a
147
- // '\\', which signifies the start of a command; then we iterate from
148
- // left to right until we hit a '\\left(', which signifies the end of a
149
- // command. If we encounter any character that doesn't belong in a
150
- // command, we return null. We match a single character at a time.
151
- // Ex) ['\\l', 'o', 'g ', '\\left(', ...]
152
- const commandCharRegex = /^[a-z]$/;
153
- const commandStartRegex = /^\\[a-z]$/;
154
- const commandEndSeq = "\\left(";
155
-
156
- // Note: We allowlist the set of valid commands, since relying solely on
157
- // a command being prefixed with a backslash leads to undesired
158
- // behavior. For example, Greek symbols, left parentheses, and square
159
- // roots all get treated as commands.
160
- const validCommands = ["\\log", "\\ln", "\\cos", "\\sin", "\\tan"];
161
-
162
- let name = "";
163
- let startNode;
164
- let endNode;
165
-
166
- // Collect the portion of the command from the current node, leftwards
167
- // until the start of the command.
168
- let node = initialNode;
169
- while (node !== 0) {
170
- const ctrlSeq = node.ctrlSeq.trim();
171
- if (commandCharRegex.test(ctrlSeq)) {
172
- name = ctrlSeq + name;
173
- } else if (commandStartRegex.test(ctrlSeq)) {
174
- name = ctrlSeq + name;
175
- startNode = node;
176
- break;
177
- } else {
178
- break;
179
- }
180
-
181
- node = node[mathQuillInstance.L];
182
- }
183
-
184
- // If we hit the start of a command, then grab the rest of it by
185
- // iterating rightwards to compute the full name of the command, along
186
- // with its terminal node.
187
- if (startNode) {
188
- // Next, iterate from the start to the right.
189
- node = initialNode[mathQuillInstance.R];
190
- while (node !== 0) {
191
- const ctrlSeq = node.ctrlSeq.trim();
192
- if (commandCharRegex.test(ctrlSeq)) {
193
- // If we have a single character, add it to the command
194
- // name.
195
- name = name + ctrlSeq;
196
- } else if (ctrlSeq === commandEndSeq) {
197
- // If we hit the command end delimiter (the left
198
- // parentheses surrounding its arguments), stop.
199
- endNode = node;
200
- break;
201
- }
202
-
203
- node = node[mathQuillInstance.R];
204
- }
205
- if (validCommands.includes(name)) {
206
- return {name, startNode, endNode};
207
- } else {
208
- return null;
209
- }
210
- } else {
211
- return null;
212
- }
213
- }
214
-
215
- /**
216
- * Return the start node, end node, and full name of the command to the left
217
- * of `\\left(`, or `null` if there is no command.
218
- *
219
- * @param {node} leftParenNode - node where .ctrlSeq == `\\left(`
220
- * @returns {null|object} - `null` or an object containing the start node
221
- * (`startNode`), end node (`endNode`), and full
222
- * name (`name`) of the command
223
- */
224
- export function maybeFindCommandBeforeParens(leftParenNode) {
225
- return maybeFindCommand(leftParenNode[mathQuillInstance.L]);
226
- }
227
-
228
- export function getCursorContext(
229
- mathField?: MathFieldInterface,
230
- ): typeof CursorContext[keyof typeof CursorContext] {
231
- if (!mathField) {
232
- return CursorContext.NONE;
233
- }
234
-
235
- // First, try to find any fraction to the right, unimpeded.
236
- const cursor = mathField.cursor();
237
- let visitor = cursor;
238
- while (visitor[mathQuillInstance.R] !== MathFieldActionType.MQ_END) {
239
- if (isFraction(visitor[mathQuillInstance.R])) {
240
- return CursorContext.BEFORE_FRACTION;
241
- } else if (!isLeaf(visitor[mathQuillInstance.R])) {
242
- break;
243
- }
244
- visitor = visitor[mathQuillInstance.R];
245
- }
246
-
247
- // If that didn't work, check if the parent or grandparent is a special
248
- // context, so that we can jump outwards.
249
- if (isParens(cursor.parent && cursor.parent.parent)) {
250
- return CursorContext.IN_PARENS;
251
- } else if (isNumerator(cursor.parent)) {
252
- return CursorContext.IN_NUMERATOR;
253
- } else if (isDenominator(cursor.parent)) {
254
- return CursorContext.IN_DENOMINATOR;
255
- } else if (isSubScript(cursor.parent)) {
256
- return CursorContext.IN_SUB_SCRIPT;
257
- } else if (isSuperScript(cursor.parent)) {
258
- return CursorContext.IN_SUPER_SCRIPT;
259
- } else {
260
- return CursorContext.NONE;
261
- }
262
- }
@@ -1,106 +0,0 @@
1
- import * as i18n from "@khanacademy/wonder-blocks-i18n";
2
- import MathQuill from "mathquill";
3
-
4
- import type {
5
- MathQuillInterface,
6
- MathFieldConfig,
7
- MathFieldInterface,
8
- } from "./mathquill-types";
9
-
10
- // We only need one MathQuill instance (referred to as MQ in the docs)
11
- // and that contains some MQ constants and the MathField constructor
12
- export const mathQuillInstance: MathQuillInterface = MathQuill.getInterface(3);
13
-
14
- const createBaseConfig = (): MathFieldConfig => ({
15
- // LaTeX commands that, when typed, are immediately replaced by the
16
- // appropriate symbol. This does not include ln, log, or any of the
17
- // trig functions; those are always interpreted as commands.
18
- autoCommands: "pi theta phi sqrt nthroot",
19
- // Most of these autoOperatorNames are simply the MathQuill defaults.
20
- // We have to list them all in order to add the `sen` operator (see
21
- // comment below).
22
- autoOperatorNames: [
23
- "arccos",
24
- "arcsin",
25
- "arctan",
26
- "arg",
27
- "cos",
28
- "cosh",
29
- "cot",
30
- "coth",
31
- "csc",
32
- "deg",
33
- "det",
34
- "dim",
35
- "exp",
36
- "gcd",
37
- "hom",
38
- "inf",
39
- "ker",
40
- "lg",
41
- "lim",
42
- "liminf",
43
- "limsup",
44
- "ln",
45
- "log",
46
- "max",
47
- "min",
48
- "Pr",
49
- "projlim",
50
- "sec",
51
- // sen is used instead of sin in e.g. Portuguese
52
- "sen",
53
- "sin",
54
- "sinh",
55
- "sup",
56
- "tan",
57
- "tanh",
58
- ].join(" "),
59
-
60
- // Pop the cursor out of super/subscripts on arithmetic operators
61
- // or (in)equalities.
62
- charsThatBreakOutOfSupSub: "+-*/=<>≠≤≥",
63
-
64
- // Prevent excessive super/subscripts or fractions from being
65
- // created without operands, e.g. when somebody holds down a key
66
- supSubsRequireOperand: true,
67
-
68
- // The name of this option is somewhat misleading, as tabbing in
69
- // MathQuill breaks you out of a nested context (fraction/script)
70
- // if you're in one, but moves focus to the next input if you're
71
- // not. Spaces (with this option enabled) are just ignored in the
72
- // latter case.
73
- //
74
- // TODO(alex): In order to allow inputting mixed numbers, we will
75
- // have to accept spaces in certain cases. The desired behavior is
76
- // still to escape nested contexts if currently in one, but to
77
- // insert a space if not (we don't expect mixed numbers in nested
78
- // contexts). We should also limit to one consecutive space.
79
- spaceBehavesLikeTab: true,
80
- });
81
-
82
- /**
83
- * Creates a new [MathField](http://docs.mathquill.com/en/latest/Api_Methods/#mqmathfieldhtml_element-config)
84
- * instance within the given `container`.
85
- *
86
- * An optional configuration callback can be provided to customize
87
- * the created MathField. A default configuration is passed to this
88
- * callback which can then be adjusted as needed. The configuration
89
- * returned from this callback is used to create the MathField.
90
- * This allows callers to do minimal configuration as only configs
91
- * that vary from the default need to be provided.
92
- */
93
- export function createMathField(
94
- container: HTMLDivElement | HTMLSpanElement,
95
- configCallback?: (baseConfig: MathFieldConfig) => MathFieldConfig,
96
- ): MathFieldInterface {
97
- const baseConfig = createBaseConfig();
98
- const config = configCallback ? configCallback(baseConfig) : baseConfig;
99
-
100
- const mathField = mathQuillInstance
101
- .MathField(container, config)
102
- // translated in ./math-input.tsx
103
- .setAriaLabel(i18n._("Math input box")) as MathFieldInterface;
104
-
105
- return mathField;
106
- }
@@ -1,32 +0,0 @@
1
- import type Key from "../../data/keys";
2
- import type MathQuill from "mathquill";
3
-
4
- export type MathQuillInterface = MathQuill.v3.API;
5
-
6
- export type MathFieldConfig = MathQuill.v3.Config;
7
-
8
- /**
9
- * Editable math fields have all of the above methods in addition to
10
- * the ones listed here.
11
- * https://docs.mathquill.com/en/latest/Api_Methods/
12
- */
13
- export type MathFieldInterface = MathQuill.v3.EditableMathQuill;
14
-
15
- export enum MathFieldActionType {
16
- WRITE = "write",
17
- CMD = "cmd",
18
- KEYSTROKE = "keystroke",
19
- MQ_END = 0,
20
- }
21
-
22
- /**
23
- * The MathQuill MathField Cursor
24
- * it's not part of the public API for MathQuill,
25
- * we reach into the internals to get it
26
- */
27
- export type MathFieldCursor = any;
28
-
29
- export type MathFieldUpdaterCallback = (
30
- mathField: MathFieldInterface,
31
- key: Key,
32
- ) => void;
@@ -1,65 +0,0 @@
1
- /**
2
- * A single function used to scroll a DOM node into view, optionally taking into
3
- * account that it may be obscured by the custom keypad. The logic makes the
4
- * strong assumption that the keypad will be anchored to the bottom of the page
5
- * in calculating its height, as this method may be called before the keypad has
6
- * animated into view.
7
- *
8
- * TODO(charlie): Move this scroll logic out of our components and into a higher
9
- * level in the component tree--perhaps even into webapp, beyond Perseus.
10
- */
11
-
12
- // HACK(charlie): This should be injected by webapp somehow.
13
- // TODO(charlie): Add a link to the webapp location as soon as the footer
14
- // has settled down.
15
- export const toolbarHeightPx = 60;
16
-
17
- export const scrollIntoView = (containerNode, keypadNode) => {
18
- // TODO(charlie): There's no need for us to be reading the keypad bounds
19
- // here, since they're pre-determined by logic in the store. We should
20
- // instead pass around an object that knows the bounds.
21
- const containerBounds = containerNode.getBoundingClientRect();
22
- const containerBottomPx = containerBounds.bottom;
23
- const containerTopPx = containerBounds.top;
24
-
25
- // Get the element that scrolls the document.
26
- const scrollNode = document.scrollingElement;
27
-
28
- const desiredMarginPx = 16;
29
-
30
- if (keypadNode) {
31
- // NOTE(charlie): We can't use the bounding rect of the keypad,
32
- // as it is likely in the process of animating in. Instead, to
33
- // calculate its top, we make the strong assumption that the
34
- // keypad will end up anchored at the bottom of the page, but above the
35
- // toolbar, and use its height, which is known at this point. Note that,
36
- // in the native apps (where the toolbar is rendered natively), this
37
- // will result in us leaving excess space between the input and the
38
- // keypad, but that seems okay.
39
- const pageHeightPx = window.innerHeight;
40
- const keypadHeightPx = keypadNode.clientHeight;
41
- const keypadTopPx = pageHeightPx - (keypadHeightPx + toolbarHeightPx);
42
-
43
- if (containerBottomPx > keypadTopPx) {
44
- // If the input would be obscured by the keypad, scroll such that
45
- // the bottom of the input is just above the top of the keypad,
46
- // taking care not to scroll the input out of view.
47
- const scrollOffset = Math.min(
48
- containerBottomPx - keypadTopPx + desiredMarginPx,
49
- containerTopPx,
50
- );
51
-
52
- if (scrollNode) {
53
- scrollNode.scrollTop += scrollOffset;
54
- }
55
- return;
56
- }
57
- }
58
-
59
- // Alternatively, if the input is out of the viewport or nearly out
60
- // of the viewport, scroll it into view. We can do this regardless
61
- // of whether the keypad has been provided.
62
- if (scrollNode && containerTopPx < desiredMarginPx) {
63
- scrollNode.scrollTop -= containerBounds.height + desiredMarginPx;
64
- }
65
- };