@khanacademy/perseus 6.2.2 → 6.3.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 +13 -0
- package/dist/components/math-input.d.ts +4 -1
- package/dist/components/math-input.js.flow +4 -1
- package/dist/components/tex-buttons.d.ts +5 -1
- package/dist/components/tex-buttons.js.flow +1 -1
- package/dist/es/index.js +105 -139
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +105 -139
- package/dist/index.js.map +1 -1
- package/dist/strings.js +23 -0
- package/package.json +4 -5
- package/src/components/__tests__/math-input.test.tsx +60 -0
- package/src/components/math-input.tsx +88 -118
- package/src/components/tex-buttons.tsx +73 -12
- package/tsconfig-build.tsbuildinfo +1 -1
package/dist/strings.js
CHANGED
|
@@ -52,6 +52,8 @@ module.exports = [
|
|
|
52
52
|
i18n._("Correct (but you crossed it out)", {}),
|
|
53
53
|
i18n._("Correct (selected)", {}),
|
|
54
54
|
i18n._("Correct!", {}),
|
|
55
|
+
i18n._("Cosine", {}),
|
|
56
|
+
i18n._("Cube root", {}),
|
|
55
57
|
i18n._("Current", {}),
|
|
56
58
|
// I18N: As in the command, "Dilate the polygon"
|
|
57
59
|
i18n._("Dilate", {}),
|
|
@@ -59,13 +61,19 @@ module.exports = [
|
|
|
59
61
|
i18n._("Dilation", {}),
|
|
60
62
|
i18n.$_("Dilation about %(point)s by %(scale)s", {}),
|
|
61
63
|
i18n.$_("Dilation of scale %(scale)s about %(point)s", {}),
|
|
64
|
+
i18n._("Divide", {}),
|
|
62
65
|
i18n._("Drag handles to make graph", {}),
|
|
63
66
|
i18n._("End of reading passage.", {}),
|
|
64
67
|
i18n._("entered in an invalid answer.", {}),
|
|
68
|
+
i18n._("Equals sign", {}),
|
|
65
69
|
i18n.$_("Error rendering: %(error)s", {}),
|
|
66
70
|
i18n._("Explain", {}),
|
|
71
|
+
i18n._("Exponent", {}),
|
|
67
72
|
i18n._("False", {}),
|
|
73
|
+
i18n._("Fraction, excluding the current expression", {}),
|
|
68
74
|
i18n._("Get another hint", {}),
|
|
75
|
+
i18n._("Greater than or equal to sign", {}),
|
|
76
|
+
i18n._("Greater than sign", {}),
|
|
69
77
|
i18n._("Hide explanation", {}),
|
|
70
78
|
i18n._("Hint #%(pos)s", {}),
|
|
71
79
|
i18n._("Hints", {}),
|
|
@@ -77,25 +85,36 @@ module.exports = [
|
|
|
77
85
|
i18n._("It looks like you left something blank or ", {}),
|
|
78
86
|
i18n._("Keep trying", {}),
|
|
79
87
|
i18n._("Khan Academy video wrapper", {}),
|
|
88
|
+
i18n._("Less than or equal to sign", {}),
|
|
89
|
+
i18n._("Less than sign", {}),
|
|
80
90
|
// I18N: a label next to a reading passage to denote the line number
|
|
81
91
|
i18n._("Line ", {}),
|
|
82
92
|
i18n.$_("line %(lineNumber)s", {}),
|
|
83
93
|
i18n.$_("lines %(lineRange)s", {}),
|
|
84
94
|
i18n._("Loading...", {}),
|
|
95
|
+
i18n._("Logarithm with base 10", {}),
|
|
96
|
+
i18n._("Logarithm with custom base", {}),
|
|
85
97
|
i18n._("Make circle filled", {}),
|
|
86
98
|
i18n._("Make circle open", {}),
|
|
87
99
|
i18n._("Make sure you fill in all cells in the matrix.", {}),
|
|
88
100
|
i18n._("Make sure you select something for every row.", {}),
|
|
101
|
+
i18n._("Minus", {}),
|
|
89
102
|
i18n.ngettext("Moves: %(num)s","Moves: %(num)s", {}),
|
|
103
|
+
i18n._("Multiply", {}),
|
|
104
|
+
i18n._("Natural logarithm", {}),
|
|
90
105
|
i18n._("Next question", {}),
|
|
91
106
|
i18n._("No", {}),
|
|
92
107
|
i18n._("None of the above", {}),
|
|
108
|
+
i18n._("Not-equals sign", {}),
|
|
93
109
|
i18n._("Note: For this question, the shape will not move.", {}),
|
|
94
110
|
i18n._("Number of divisions:", {}),
|
|
95
111
|
i18n._("Number of trials:", {}),
|
|
96
112
|
i18n._("Parameter", {}),
|
|
113
|
+
i18n._("Phi", {}),
|
|
114
|
+
i18n._("Pi", {}),
|
|
97
115
|
i18n._("Please choose the correct number of answers.", {}),
|
|
98
116
|
i18n.$_("Please make sure the number of divisions is in the range %(divRangeString)s.", {}),
|
|
117
|
+
i18n._("Plus", {}),
|
|
99
118
|
i18n._("Reaction arrow pointing to the right.", {}),
|
|
100
119
|
// I18N: As in the command, "Reflect the polygon"
|
|
101
120
|
i18n._("Reflect", {}),
|
|
@@ -113,15 +132,19 @@ module.exports = [
|
|
|
113
132
|
i18n._("Run simulation", {}),
|
|
114
133
|
i18n._("Sample size:", {}),
|
|
115
134
|
i18n._("See video transcript", {}),
|
|
135
|
+
i18n._("Sine", {}),
|
|
116
136
|
i18n._("Skip to %(title)s", {}),
|
|
117
137
|
i18n._("Something went wrong.", {}),
|
|
118
138
|
i18n._("Sorry, I don't understand that!", {}),
|
|
139
|
+
i18n._("Square root", {}),
|
|
119
140
|
i18n._("Switch direction", {}),
|
|
141
|
+
i18n._("Tangent", {}),
|
|
120
142
|
i18n._("Tap each dot on the image to select all answers that apply.", {}),
|
|
121
143
|
i18n._("Tap each dot on the image to select an answer.", {}),
|
|
122
144
|
i18n._("Tap to add points", {}),
|
|
123
145
|
i18n._("That answer is numerically incorrect.", {}),
|
|
124
146
|
i18n._("The symbol %(questionSymbol)s indicates that question %(questionNumber)s references this portion of the passage.", {}),
|
|
147
|
+
i18n._("Theta", {}),
|
|
125
148
|
i18n._("This controls the proportion or percentage that will be used in your simulation.", {}),
|
|
126
149
|
i18n._("This controls the sample size that will be used in your simulation. For example, if you set this to 100, then for each trial, responses from 100 participants will be simulated.", {}),
|
|
127
150
|
i18n._("This is the number of trials used in the simulation. For example, if set to 50, then the survey will be conducted 50 times.", {}),
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Core Perseus API (includes renderers and widgets)",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "6.
|
|
6
|
+
"version": "6.3.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -23,11 +23,10 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@khanacademy/kas": "^0.3.1",
|
|
25
25
|
"@khanacademy/kmath": "^0.1.1",
|
|
26
|
-
"@khanacademy/math-input": "^4.
|
|
26
|
+
"@khanacademy/math-input": "^4.2.0",
|
|
27
27
|
"@khanacademy/perseus-linter": "^0.3.1",
|
|
28
28
|
"@khanacademy/pure-markdown": "^0.2.3",
|
|
29
|
-
"@khanacademy/simple-markdown": "^0.9.3"
|
|
30
|
-
"mathquill": "git+https://git@github.com/Khan/mathquill.git#a9ae54e057c5c1acc8244a5627acbff29901d992"
|
|
29
|
+
"@khanacademy/simple-markdown": "^0.9.3"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
32
|
"@khanacademy/wonder-blocks-button": "^4.0.8",
|
|
@@ -92,4 +91,4 @@
|
|
|
92
91
|
"underscore": "^1.4.4"
|
|
93
92
|
},
|
|
94
93
|
"keywords": []
|
|
95
|
-
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {render, screen} from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import "@testing-library/jest-dom/extend-expect";
|
|
6
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
7
|
+
import * as Dependencies from "../../dependencies";
|
|
8
|
+
import MathInput from "../math-input";
|
|
9
|
+
|
|
10
|
+
describe("Perseus' MathInput", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
13
|
+
testDependencies,
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("renders", () => {
|
|
18
|
+
// Assemble
|
|
19
|
+
render(
|
|
20
|
+
<MathInput
|
|
21
|
+
onChange={() => {}}
|
|
22
|
+
buttonSets={["basic"]}
|
|
23
|
+
labelText="test"
|
|
24
|
+
/>,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Assert
|
|
28
|
+
expect(screen.getByLabelText("test")).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("is possible to type in the input", () => {
|
|
32
|
+
// Assemble
|
|
33
|
+
const mockOnChange = jest.fn();
|
|
34
|
+
render(<MathInput onChange={mockOnChange} buttonSets={["basic"]} />);
|
|
35
|
+
|
|
36
|
+
// Act
|
|
37
|
+
userEvent.type(screen.getByRole("textbox"), "12345");
|
|
38
|
+
|
|
39
|
+
// Assert
|
|
40
|
+
expect(mockOnChange).toHaveBeenLastCalledWith("12345");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("is possible to use buttons", () => {
|
|
44
|
+
// Assemble
|
|
45
|
+
const mockOnChange = jest.fn();
|
|
46
|
+
render(<MathInput onChange={mockOnChange} buttonSets={["basic"]} />);
|
|
47
|
+
|
|
48
|
+
// Act
|
|
49
|
+
// focusing the input triggers the popover
|
|
50
|
+
screen.getByRole("textbox").focus();
|
|
51
|
+
userEvent.type(screen.getByRole("textbox"), "1");
|
|
52
|
+
userEvent.click(screen.getByRole("button", {name: "Plus"}));
|
|
53
|
+
userEvent.type(screen.getByRole("textbox"), "2");
|
|
54
|
+
userEvent.click(screen.getByRole("button", {name: "Minus"}));
|
|
55
|
+
userEvent.type(screen.getByRole("textbox"), "3");
|
|
56
|
+
|
|
57
|
+
// Assert
|
|
58
|
+
expect(mockOnChange).toHaveBeenLastCalledWith("1+2-3");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
keyTranslator,
|
|
3
|
+
createMathField,
|
|
4
|
+
mathQuillInstance,
|
|
5
|
+
MathFieldInterface,
|
|
6
|
+
} from "@khanacademy/math-input";
|
|
2
7
|
import classNames from "classnames";
|
|
3
8
|
import $ from "jquery";
|
|
4
|
-
import MathQuill from "mathquill";
|
|
5
9
|
import * as React from "react";
|
|
6
10
|
import ReactDOM from "react-dom";
|
|
7
11
|
import _ from "underscore";
|
|
@@ -52,6 +56,8 @@ const customKeyTranslator = {
|
|
|
52
56
|
class MathInput extends React.Component<Props, State> {
|
|
53
57
|
// @ts-expect-error [FEI-5003] - TS2564 - Property 'mouseDown' has no initializer and is not definitely assigned in the constructor.
|
|
54
58
|
mouseDown: boolean;
|
|
59
|
+
__mathFieldWrapperRef: HTMLSpanElement | null = null;
|
|
60
|
+
__mathField: MathFieldInterface | null = null;
|
|
55
61
|
|
|
56
62
|
static defaultProps: DefaultProps = {
|
|
57
63
|
value: "",
|
|
@@ -65,100 +71,9 @@ class MathInput extends React.Component<Props, State> {
|
|
|
65
71
|
window.addEventListener("mousedown", this.handleMouseDown);
|
|
66
72
|
window.addEventListener("mouseup", this.handleMouseUp);
|
|
67
73
|
|
|
68
|
-
let initialized = false;
|
|
69
|
-
|
|
70
|
-
// Initialize MathQuill.MathField instance
|
|
71
|
-
this.mathField({
|
|
72
|
-
// LaTeX commands that, when typed, are immediately replaced by the
|
|
73
|
-
// appropriate symbol. This does not include ln, log, or any of the
|
|
74
|
-
// trig functions; those are always interpreted as commands.
|
|
75
|
-
autoCommands: "pi theta phi sqrt nthroot",
|
|
76
|
-
|
|
77
|
-
// Pop the cursor out of super/subscripts on arithmetic operators
|
|
78
|
-
// or (in)equalities.
|
|
79
|
-
charsThatBreakOutOfSupSub: "+-*/=<>≠≤≥",
|
|
80
|
-
|
|
81
|
-
// Prevent excessive super/subscripts or fractions from being
|
|
82
|
-
// created without operands, e.g. when somebody holds down a key
|
|
83
|
-
supSubsRequireOperand: true,
|
|
84
|
-
|
|
85
|
-
// The name of this option is somewhat misleading, as tabbing in
|
|
86
|
-
// MathQuill breaks you out of a nested context (fraction/script)
|
|
87
|
-
// if you're in one, but moves focus to the next input if you're
|
|
88
|
-
// not. Spaces (with this option enabled) are just ignored in the
|
|
89
|
-
// latter case.
|
|
90
|
-
//
|
|
91
|
-
// TODO(alex): In order to allow inputting mixed numbers, we will
|
|
92
|
-
// have to accept spaces in certain cases. The desired behavior is
|
|
93
|
-
// still to escape nested contexts if currently in one, but to
|
|
94
|
-
// insert a space if not (we don't expect mixed numbers in nested
|
|
95
|
-
// contexts). We should also limit to one consecutive space.
|
|
96
|
-
spaceBehavesLikeTab: true,
|
|
97
|
-
|
|
98
|
-
handlers: {
|
|
99
|
-
edited: (mathField) => {
|
|
100
|
-
// This handler is guaranteed to be called on change, but
|
|
101
|
-
// unlike React it sometimes generates false positives.
|
|
102
|
-
// One of these is on initialization (with an empty string
|
|
103
|
-
// value), so we have to guard against that below.
|
|
104
|
-
let value = mathField.latex();
|
|
105
|
-
|
|
106
|
-
// Provide a MathQuill-compatible way to generate the
|
|
107
|
-
// not-equals sign without pasting unicode or typing TeX
|
|
108
|
-
value = value.replace(/<>/g, "\\ne");
|
|
109
|
-
|
|
110
|
-
// Use the specified symbol to represent multiplication
|
|
111
|
-
// TODO(alex): Add an option to disallow variables, in
|
|
112
|
-
// which case 'x' should get converted to '\\times'
|
|
113
|
-
if (this.props.convertDotToTimes) {
|
|
114
|
-
value = value.replace(/\\cdot/g, "\\times");
|
|
115
|
-
|
|
116
|
-
// Preserve cursor position in the common case:
|
|
117
|
-
// typing '*' to insert a multiplication sign.
|
|
118
|
-
// We do this by modifying internal MathQuill state
|
|
119
|
-
// directly, instead of waiting for `.latex()` to be
|
|
120
|
-
// called in `componentDidUpdate()`.
|
|
121
|
-
const left = mathField.__controller.cursor[MathQuill.L];
|
|
122
|
-
if (left && left.ctrlSeq === "\\cdot ") {
|
|
123
|
-
mathField.__controller.backspace();
|
|
124
|
-
mathField.cmd("\\times");
|
|
125
|
-
}
|
|
126
|
-
} else {
|
|
127
|
-
value = value.replace(/\\times/g, "\\cdot");
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (initialized && this.props.value !== value) {
|
|
131
|
-
this.props.onChange(value);
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
enter: () => {
|
|
135
|
-
// NOTE(kevinb): This isn't how answers to exercises are
|
|
136
|
-
// submitted. The actual mechanism for this can be found
|
|
137
|
-
// in exercise-problem-template.jsx, see `handleSubmit`.
|
|
138
|
-
|
|
139
|
-
// This handler is called when the user presses the enter
|
|
140
|
-
// key. Since this isn't an actual <input> element, we have
|
|
141
|
-
// to manually trigger the usually automatic form submit.
|
|
142
|
-
// eslint-disable-next-line react/no-string-refs
|
|
143
|
-
// @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call. | TS2339 - Property 'submit' does not exist on type 'JQueryStatic'.
|
|
144
|
-
$(ReactDOM.findDOMNode(this.refs.mathinput)).submit();
|
|
145
|
-
},
|
|
146
|
-
upOutOf: (mathField) => {
|
|
147
|
-
// This handler is called when the user presses the up
|
|
148
|
-
// arrow key, but there is nowhere in the expression to go
|
|
149
|
-
// up to (no numerator or exponent). For ease of use,
|
|
150
|
-
// interpret this as an attempt to create an exponent.
|
|
151
|
-
mathField.typedText("^");
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
});
|
|
155
|
-
|
|
156
74
|
// Ideally, we would be able to pass an initial value directly into
|
|
157
|
-
// the constructor
|
|
158
|
-
|
|
159
|
-
this.mathField().latex(this.props.value);
|
|
160
|
-
|
|
161
|
-
initialized = true;
|
|
75
|
+
// the constructor
|
|
76
|
+
this.mathField()?.latex(this.props.value);
|
|
162
77
|
}
|
|
163
78
|
|
|
164
79
|
componentWillUnmount() {
|
|
@@ -224,12 +139,11 @@ class MathInput extends React.Component<Props, State> {
|
|
|
224
139
|
};
|
|
225
140
|
|
|
226
141
|
insert: (value: any) => void = (value) => {
|
|
227
|
-
// @ts-expect-error [FEI-5003] - TS2554 - Expected 1 arguments, but got 0.
|
|
228
142
|
const input = this.mathField();
|
|
229
143
|
const inputModifier = customKeyTranslator[value];
|
|
230
144
|
if (inputModifier) {
|
|
231
145
|
inputModifier(input, value);
|
|
232
|
-
input
|
|
146
|
+
input?.focus();
|
|
233
147
|
return;
|
|
234
148
|
}
|
|
235
149
|
|
|
@@ -240,36 +154,93 @@ class MathInput extends React.Component<Props, State> {
|
|
|
240
154
|
if (_(value).isFunction()) {
|
|
241
155
|
value(input);
|
|
242
156
|
} else if (value[0] === "\\") {
|
|
243
|
-
input
|
|
157
|
+
input?.cmd(value).focus();
|
|
244
158
|
} else {
|
|
245
|
-
input
|
|
159
|
+
input?.write(value).focus();
|
|
246
160
|
}
|
|
247
|
-
input
|
|
161
|
+
input?.focus();
|
|
248
162
|
};
|
|
249
163
|
|
|
250
|
-
mathField: (
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
164
|
+
mathField: () => MathFieldInterface | null = () => {
|
|
165
|
+
if (!this.__mathField && this.__mathFieldWrapperRef) {
|
|
166
|
+
// Initialize MathQuill.MathField instance
|
|
167
|
+
this.__mathField = createMathField(
|
|
168
|
+
this.__mathFieldWrapperRef,
|
|
169
|
+
(baseConfig) => ({
|
|
170
|
+
...baseConfig,
|
|
171
|
+
handlers: {
|
|
172
|
+
edit: (mathField) => {
|
|
173
|
+
// This handler is guaranteed to be called on change, but
|
|
174
|
+
// unlike React it sometimes generates false positives.
|
|
175
|
+
// One of these is on initialization (with an empty string
|
|
176
|
+
// value), so we have to guard against that below.
|
|
177
|
+
let value = mathField.latex();
|
|
178
|
+
|
|
179
|
+
// Provide a MathQuill-compatible way to generate the
|
|
180
|
+
// not-equals sign without pasting unicode or typing TeX
|
|
181
|
+
value = value.replace(/<>/g, "\\ne");
|
|
182
|
+
|
|
183
|
+
// Use the specified symbol to represent multiplication
|
|
184
|
+
// TODO(alex): Add an option to disallow variables, in
|
|
185
|
+
// which case 'x' should get converted to '\\times'
|
|
186
|
+
if (this.props.convertDotToTimes) {
|
|
187
|
+
value = value.replace(/\\cdot/g, "\\times");
|
|
188
|
+
|
|
189
|
+
// Preserve cursor position in the common case:
|
|
190
|
+
// typing '*' to insert a multiplication sign.
|
|
191
|
+
// We do this by modifying internal MathQuill state
|
|
192
|
+
// directly, instead of waiting for `.latex()` to be
|
|
193
|
+
// called in `componentDidUpdate()`.
|
|
194
|
+
const left =
|
|
195
|
+
mathField.__controller.cursor[
|
|
196
|
+
mathQuillInstance.L
|
|
197
|
+
];
|
|
198
|
+
if (left && left.ctrlSeq === "\\cdot ") {
|
|
199
|
+
mathField.__controller.backspace();
|
|
200
|
+
mathField.cmd("\\times");
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
value = value.replace(/\\times/g, "\\cdot");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (this.props.value !== value) {
|
|
207
|
+
this.props.onChange(value);
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
enter: () => {
|
|
211
|
+
// NOTE(kevinb): This isn't how answers to exercises are
|
|
212
|
+
// submitted. The actual mechanism for this can be found
|
|
213
|
+
// in exercise-problem-template.jsx, see `handleSubmit`.
|
|
214
|
+
|
|
215
|
+
// This handler is called when the user presses the enter
|
|
216
|
+
// key. Since this isn't an actual <input> element, we have
|
|
217
|
+
// to manually trigger the usually automatic form submit.
|
|
218
|
+
if (this.__mathFieldWrapperRef) {
|
|
219
|
+
$(this.__mathFieldWrapperRef).submit();
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
upOutOf: (mathField) => {
|
|
223
|
+
// This handler is called when the user presses the up
|
|
224
|
+
// arrow key, but there is nowhere in the expression to go
|
|
225
|
+
// up to (no numerator or exponent). For ease of use,
|
|
226
|
+
// interpret this as an attempt to create an exponent.
|
|
227
|
+
mathField.typedText("^");
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
}),
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return this.__mathField;
|
|
262
235
|
};
|
|
263
236
|
|
|
264
237
|
focus: () => void = () => {
|
|
265
|
-
|
|
266
|
-
this.mathField().focus();
|
|
238
|
+
this.mathField()?.focus();
|
|
267
239
|
this.setState({focused: true});
|
|
268
240
|
};
|
|
269
241
|
|
|
270
242
|
blur: () => void = () => {
|
|
271
|
-
|
|
272
|
-
this.mathField().blur();
|
|
243
|
+
this.mathField()?.blur();
|
|
273
244
|
this.setState({focused: false});
|
|
274
245
|
};
|
|
275
246
|
|
|
@@ -305,8 +276,7 @@ class MathInput extends React.Component<Props, State> {
|
|
|
305
276
|
<div style={{display: "inline-block"}}>
|
|
306
277
|
<span
|
|
307
278
|
className={className}
|
|
308
|
-
|
|
309
|
-
ref="mathinput"
|
|
279
|
+
ref={(ref) => (this.__mathFieldWrapperRef = ref)}
|
|
310
280
|
aria-label={this.props.labelText}
|
|
311
281
|
onFocus={this.handleFocus}
|
|
312
282
|
onBlur={this.handleBlur}
|
|
@@ -10,7 +10,14 @@ const prettyBig = {fontSize: "150%"} as const;
|
|
|
10
10
|
const slightlyBig = {fontSize: "120%"} as const;
|
|
11
11
|
const symbStyle = {fontSize: "130%"} as const;
|
|
12
12
|
|
|
13
|
-
type ButtonSet = (props: any) => [
|
|
13
|
+
type ButtonSet = (props: any) => [
|
|
14
|
+
// inside of button
|
|
15
|
+
React.ReactNode,
|
|
16
|
+
// the key message it sends on click
|
|
17
|
+
Keys,
|
|
18
|
+
// label
|
|
19
|
+
string,
|
|
20
|
+
];
|
|
14
21
|
|
|
15
22
|
type ButtonSets = {
|
|
16
23
|
readonly [key: string]: ReadonlyArray<ButtonSet>;
|
|
@@ -27,12 +34,14 @@ const basic: ReadonlyArray<ButtonSet> = [
|
|
|
27
34
|
+
|
|
28
35
|
</span>,
|
|
29
36
|
"PLUS",
|
|
37
|
+
i18n._("Plus"),
|
|
30
38
|
],
|
|
31
39
|
() => [
|
|
32
40
|
<span key="minus" style={prettyBig}>
|
|
33
41
|
-
|
|
34
42
|
</span>,
|
|
35
43
|
"MINUS",
|
|
44
|
+
i18n._("Minus"),
|
|
36
45
|
],
|
|
37
46
|
|
|
38
47
|
// TODO(joel) - display as \cdot when appropriate
|
|
@@ -44,6 +53,7 @@ const basic: ReadonlyArray<ButtonSet> = [
|
|
|
44
53
|
{i18n.doNotTranslate("\\times")}
|
|
45
54
|
</TeX>,
|
|
46
55
|
"TIMES",
|
|
56
|
+
i18n._("Multiply"),
|
|
47
57
|
];
|
|
48
58
|
}
|
|
49
59
|
return [
|
|
@@ -51,6 +61,7 @@ const basic: ReadonlyArray<ButtonSet> = [
|
|
|
51
61
|
{i18n.doNotTranslate("\\cdot")}
|
|
52
62
|
</TeX>,
|
|
53
63
|
"CDOT",
|
|
64
|
+
i18n._("Multiply"),
|
|
54
65
|
];
|
|
55
66
|
},
|
|
56
67
|
() => {
|
|
@@ -60,6 +71,7 @@ const basic: ReadonlyArray<ButtonSet> = [
|
|
|
60
71
|
{i18n.doNotTranslate("\\frac{x}{y}")}
|
|
61
72
|
</TeX>,
|
|
62
73
|
"FRAC",
|
|
74
|
+
i18n._("Fraction, excluding the current expression"),
|
|
63
75
|
];
|
|
64
76
|
},
|
|
65
77
|
];
|
|
@@ -73,6 +85,7 @@ const buttonSets: ButtonSets = {
|
|
|
73
85
|
return [
|
|
74
86
|
<TeX key="div">{i18n.doNotTranslate("\\div")}</TeX>,
|
|
75
87
|
"DIVIDE",
|
|
88
|
+
i18n._("Divide"),
|
|
76
89
|
];
|
|
77
90
|
},
|
|
78
91
|
]),
|
|
@@ -80,15 +93,27 @@ const buttonSets: ButtonSets = {
|
|
|
80
93
|
trig: [
|
|
81
94
|
() => {
|
|
82
95
|
const {TeX} = getDependencies();
|
|
83
|
-
return [
|
|
96
|
+
return [
|
|
97
|
+
<TeX key="sin">{i18n.doNotTranslate("\\sin")}</TeX>,
|
|
98
|
+
"SIN",
|
|
99
|
+
i18n._("Sine"),
|
|
100
|
+
];
|
|
84
101
|
},
|
|
85
102
|
() => {
|
|
86
103
|
const {TeX} = getDependencies();
|
|
87
|
-
return [
|
|
104
|
+
return [
|
|
105
|
+
<TeX key="cos">{i18n.doNotTranslate("\\cos")}</TeX>,
|
|
106
|
+
"COS",
|
|
107
|
+
i18n._("Cosine"),
|
|
108
|
+
];
|
|
88
109
|
},
|
|
89
110
|
() => {
|
|
90
111
|
const {TeX} = getDependencies();
|
|
91
|
-
return [
|
|
112
|
+
return [
|
|
113
|
+
<TeX key="tan">{i18n.doNotTranslate("\\tan")}</TeX>,
|
|
114
|
+
"TAN",
|
|
115
|
+
i18n._("Tangent"),
|
|
116
|
+
];
|
|
92
117
|
},
|
|
93
118
|
() => {
|
|
94
119
|
const {TeX} = getDependencies();
|
|
@@ -97,6 +122,7 @@ const buttonSets: ButtonSets = {
|
|
|
97
122
|
{i18n.doNotTranslate("\\theta")}
|
|
98
123
|
</TeX>,
|
|
99
124
|
"THETA",
|
|
125
|
+
i18n._("Theta"),
|
|
100
126
|
];
|
|
101
127
|
},
|
|
102
128
|
() => {
|
|
@@ -112,6 +138,7 @@ const buttonSets: ButtonSets = {
|
|
|
112
138
|
</TeX>
|
|
113
139
|
</span>,
|
|
114
140
|
"PHI",
|
|
141
|
+
i18n._("Phi"),
|
|
115
142
|
];
|
|
116
143
|
},
|
|
117
144
|
],
|
|
@@ -122,6 +149,7 @@ const buttonSets: ButtonSets = {
|
|
|
122
149
|
return [
|
|
123
150
|
<TeX key="sqrt">{i18n.doNotTranslate("\\sqrt{x}")}</TeX>,
|
|
124
151
|
"SQRT",
|
|
152
|
+
i18n._("Square root"),
|
|
125
153
|
];
|
|
126
154
|
},
|
|
127
155
|
// TODO(joel) - how does desmos do this?
|
|
@@ -130,6 +158,7 @@ const buttonSets: ButtonSets = {
|
|
|
130
158
|
return [
|
|
131
159
|
<TeX key="nthroot">{i18n.doNotTranslate("\\sqrt[3]{x}")}</TeX>,
|
|
132
160
|
"NTHROOT3",
|
|
161
|
+
i18n._("Cube root"),
|
|
133
162
|
];
|
|
134
163
|
},
|
|
135
164
|
() => {
|
|
@@ -139,6 +168,7 @@ const buttonSets: ButtonSets = {
|
|
|
139
168
|
{i18n.doNotTranslate("a^b")}
|
|
140
169
|
</TeX>,
|
|
141
170
|
"POW",
|
|
171
|
+
i18n._("Exponent"),
|
|
142
172
|
];
|
|
143
173
|
},
|
|
144
174
|
() => {
|
|
@@ -148,6 +178,7 @@ const buttonSets: ButtonSets = {
|
|
|
148
178
|
{i18n.doNotTranslate("\\pi")}
|
|
149
179
|
</TeX>,
|
|
150
180
|
"PI",
|
|
181
|
+
i18n._("Pi"),
|
|
151
182
|
];
|
|
152
183
|
},
|
|
153
184
|
],
|
|
@@ -155,17 +186,26 @@ const buttonSets: ButtonSets = {
|
|
|
155
186
|
logarithms: [
|
|
156
187
|
() => {
|
|
157
188
|
const {TeX} = getDependencies();
|
|
158
|
-
return [
|
|
189
|
+
return [
|
|
190
|
+
<TeX key="log">{i18n.doNotTranslate("\\log")}</TeX>,
|
|
191
|
+
"LOG",
|
|
192
|
+
i18n._("Logarithm with base 10"),
|
|
193
|
+
];
|
|
159
194
|
},
|
|
160
195
|
() => {
|
|
161
196
|
const {TeX} = getDependencies();
|
|
162
|
-
return [
|
|
197
|
+
return [
|
|
198
|
+
<TeX key="ln">{i18n.doNotTranslate("\\ln")}</TeX>,
|
|
199
|
+
"LN",
|
|
200
|
+
i18n._("Natural logarithm"),
|
|
201
|
+
];
|
|
163
202
|
},
|
|
164
203
|
() => {
|
|
165
204
|
const {TeX} = getDependencies();
|
|
166
205
|
return [
|
|
167
206
|
<TeX key="log_b">{i18n.doNotTranslate("\\log_b")}</TeX>,
|
|
168
207
|
"LOG_B",
|
|
208
|
+
i18n._("Logarithm with custom base"),
|
|
169
209
|
];
|
|
170
210
|
},
|
|
171
211
|
],
|
|
@@ -173,30 +213,50 @@ const buttonSets: ButtonSets = {
|
|
|
173
213
|
"basic relations": [
|
|
174
214
|
() => {
|
|
175
215
|
const {TeX} = getDependencies();
|
|
176
|
-
return [<TeX key="eq">{"="}</TeX>, "EQUAL"];
|
|
216
|
+
return [<TeX key="eq">{"="}</TeX>, "EQUAL", i18n._("Equals sign")];
|
|
177
217
|
},
|
|
178
218
|
() => {
|
|
179
219
|
const {TeX} = getDependencies();
|
|
180
|
-
return [
|
|
220
|
+
return [
|
|
221
|
+
<TeX key="lt">{i18n.doNotTranslate("\\lt")}</TeX>,
|
|
222
|
+
"LT",
|
|
223
|
+
i18n._("Less than sign"),
|
|
224
|
+
];
|
|
181
225
|
},
|
|
182
226
|
() => {
|
|
183
227
|
const {TeX} = getDependencies();
|
|
184
|
-
return [
|
|
228
|
+
return [
|
|
229
|
+
<TeX key="gt">{i18n.doNotTranslate("\\gt")}</TeX>,
|
|
230
|
+
"GT",
|
|
231
|
+
i18n._("Greater than sign"),
|
|
232
|
+
];
|
|
185
233
|
},
|
|
186
234
|
],
|
|
187
235
|
|
|
188
236
|
"advanced relations": [
|
|
189
237
|
() => {
|
|
190
238
|
const {TeX} = getDependencies();
|
|
191
|
-
return [
|
|
239
|
+
return [
|
|
240
|
+
<TeX key="neq">{i18n.doNotTranslate("\\neq")}</TeX>,
|
|
241
|
+
"NEQ",
|
|
242
|
+
i18n._("Not-equals sign"),
|
|
243
|
+
];
|
|
192
244
|
},
|
|
193
245
|
() => {
|
|
194
246
|
const {TeX} = getDependencies();
|
|
195
|
-
return [
|
|
247
|
+
return [
|
|
248
|
+
<TeX key="leq">{i18n.doNotTranslate("\\leq")}</TeX>,
|
|
249
|
+
"LEQ",
|
|
250
|
+
i18n._("Less than or equal to sign"),
|
|
251
|
+
];
|
|
196
252
|
},
|
|
197
253
|
() => {
|
|
198
254
|
const {TeX} = getDependencies();
|
|
199
|
-
return [
|
|
255
|
+
return [
|
|
256
|
+
<TeX key="geq">{i18n.doNotTranslate("\\geq")}</TeX>,
|
|
257
|
+
"GEQ",
|
|
258
|
+
i18n._("Greater than or equal to sign"),
|
|
259
|
+
];
|
|
200
260
|
},
|
|
201
261
|
],
|
|
202
262
|
};
|
|
@@ -242,6 +302,7 @@ class TexButtons extends React.Component<Props> {
|
|
|
242
302
|
// @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'. | TS2339 - Property 'key' does not exist on type 'boolean | ReactChild | ReactFragment | ReactPortal'.
|
|
243
303
|
key={symbol[0].key}
|
|
244
304
|
type="button"
|
|
305
|
+
aria-label={symbol[2]}
|
|
245
306
|
>
|
|
246
307
|
{symbol[0]}
|
|
247
308
|
</button>
|