@zzish/math-rich-input 0.1.47 → 0.1.49
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/dist/index.js +1040 -117
- package/dist/math-rich-input.css +1 -1
- package/dist/standalone.js +2 -0
- package/dist/standalone.js.LICENSE.txt +29 -0
- package/package.json +13 -3
- package/src/EquationEditor.jsx +557 -527
- package/src/EquationEditorModal.jsx +104 -58
- package/src/MathRichInput.jsx +2892 -1924
- package/src/Toolbar.jsx +234 -219
- package/src/mathRichInputHelper.js +787 -557
- package/src/standalone.js +110 -0
- package/webpack.standalone.config.js +63 -0
- package/dist/index.css +0 -1
package/src/EquationEditor.jsx
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { addStyles, EditableMathField } from "react-mathquill";
|
|
3
3
|
import SymbolButton from "./SymbolButton.jsx";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
getSymbolsForButtonArea,
|
|
6
|
+
getLatexInsertionParams,
|
|
7
|
+
} from "./equationEditorButtonsHelper.js";
|
|
5
8
|
|
|
6
9
|
import "./EquationEditor.css";
|
|
7
10
|
|
|
@@ -24,38 +27,38 @@ let GLOBAL_lastSymbols = ["", "", ""];
|
|
|
24
27
|
const MAX_BUTTON_ROWS = 5;
|
|
25
28
|
const TAB_NAMES = ["Common", "More", "Greek", "Chemistry", "Physics"];
|
|
26
29
|
const BUTTON_AREA_NAMES = [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
"Common",
|
|
31
|
+
"Operators",
|
|
32
|
+
"Angles",
|
|
33
|
+
"Misc",
|
|
34
|
+
"Sets",
|
|
35
|
+
"GreekLower",
|
|
36
|
+
"GreekUpper",
|
|
37
|
+
"Chemistry",
|
|
38
|
+
"Elements",
|
|
39
|
+
"Physics1",
|
|
40
|
+
"Physics2",
|
|
41
|
+
"Physics3",
|
|
39
42
|
];
|
|
40
43
|
const TAB_NAMES_EXPERT = ["Common", "More", "Greek", "Expert"];
|
|
41
44
|
const BUTTON_AREA_NAMES_EXPERT = [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
"Common",
|
|
46
|
+
"Operators",
|
|
47
|
+
"Angles",
|
|
48
|
+
"Misc",
|
|
49
|
+
"Sets",
|
|
50
|
+
"GreekLower",
|
|
51
|
+
"GreekUpper",
|
|
52
|
+
"Expert",
|
|
53
|
+
"Matrices",
|
|
51
54
|
];
|
|
52
55
|
const TAB_STARTS_AT_AREA_NAMED = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
Common: "Common",
|
|
57
|
+
More: "Misc",
|
|
58
|
+
Greek: "GreekLower",
|
|
59
|
+
Chemistry: "Chemistry",
|
|
60
|
+
Physics: "Physics1",
|
|
61
|
+
Expert: "Expert",
|
|
59
62
|
};
|
|
60
63
|
|
|
61
64
|
let GLOBAL_buttonAreasWidths = null;
|
|
@@ -63,536 +66,563 @@ let GLOBAL_buttonAreasWidthsExpert = null;
|
|
|
63
66
|
let GLOBAL_last_selected_tab = TAB_NAMES[0];
|
|
64
67
|
|
|
65
68
|
export default class EquationEditor extends React.Component {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
this.mathQuill = null;
|
|
76
|
-
this.handleInsertCallback = props.handleInsert;
|
|
77
|
-
this.handleCancelCallback = props.handleCancel;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
handleInsert = () => {
|
|
81
|
-
this.handleInsertCallback(this.state.latex);
|
|
69
|
+
constructor(props) {
|
|
70
|
+
super(props);
|
|
71
|
+
this.state = {
|
|
72
|
+
latex: props.latex,
|
|
73
|
+
pretty: false,
|
|
74
|
+
selectedTab: GLOBAL_last_selected_tab,
|
|
75
|
+
recentSymbolsCount: 0,
|
|
82
76
|
};
|
|
83
77
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
78
|
+
this.mathQuill = null;
|
|
79
|
+
this.handleInsertCallback = props.handleInsert;
|
|
80
|
+
this.handleCancelCallback = props.handleCancel;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
handleInsert = () => {
|
|
84
|
+
this.handleInsertCallback(this.state.latex);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
handleCancel = () => {
|
|
88
|
+
this.handleCancelCallback();
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
prettifyLatex(latex) {
|
|
92
|
+
// NB: replaceAll not suported on safari
|
|
93
|
+
// console.log("Before: "+latex)
|
|
94
|
+
// latex = latex.replaceAll(/\\\\/g,"_DOOUBLE_BCACKSLASH_")
|
|
95
|
+
// latex = latex.replace(/\\/g," \\")
|
|
96
|
+
// latex = latex.replaceAll("_DOOUBLE_BCACKSLASH_","\\\\")
|
|
97
|
+
// latex = latex.replaceAll("="," = ")
|
|
98
|
+
// latex = latex.replaceAll("+"," + ")
|
|
99
|
+
// latex = latex.replaceAll("-"," - ")
|
|
100
|
+
// latex = latex.replaceAll("{ ","{")
|
|
101
|
+
// latex = latex.replaceAll("( ","(")
|
|
102
|
+
// latex = latex.replaceAll("[ ","[")
|
|
103
|
+
// latex = latex.replaceAll(" "," ")
|
|
104
|
+
// latex = latex.trim()
|
|
105
|
+
// console.log("Prettified: "+latex)
|
|
106
|
+
return latex;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
handleChange = (mathQuill) => {
|
|
110
|
+
this.setState({ latex: mathQuill.latex(), pretty: false });
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
handleKeyDown = (e) => {
|
|
114
|
+
try {
|
|
115
|
+
if (!this.props.useExpertMode && e.key === "Enter") {
|
|
116
|
+
this.handleInsertCallback(this.state.latex);
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error(error);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
updateRecentSymbols(symbol) {
|
|
125
|
+
let recentSymbols = GLOBAL_recentSymbols;
|
|
126
|
+
let symbolIndex = -1;
|
|
127
|
+
let minIndex = -1;
|
|
128
|
+
let min = 999999;
|
|
129
|
+
for (let i = 0; i < recentSymbols.length; i++) {
|
|
130
|
+
if (recentSymbols[i].name === symbol) symbolIndex = i;
|
|
131
|
+
if (recentSymbols[i].count > 0)
|
|
132
|
+
recentSymbols[i].count = recentSymbols[i].count * RECENT_DECAY_FACTOR;
|
|
133
|
+
if (recentSymbols[i].count <= min) {
|
|
134
|
+
min = recentSymbols[i].count;
|
|
135
|
+
minIndex = i;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (symbolIndex >= 0) {
|
|
139
|
+
recentSymbols[symbolIndex].count =
|
|
140
|
+
recentSymbols[symbolIndex].count + RECENT_WEIGHT;
|
|
141
|
+
} else {
|
|
142
|
+
if (recentSymbols.length >= RECENT_MAX) {
|
|
143
|
+
recentSymbols.splice(minIndex, 1);
|
|
144
|
+
}
|
|
145
|
+
recentSymbols.push({ name: symbol, count: RECENT_WEIGHT });
|
|
104
146
|
}
|
|
105
147
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
148
|
+
// recentSymbols.sort((a, b) => b.count - a.count) // Order max to min
|
|
149
|
+
|
|
150
|
+
GLOBAL_recentSymbols = recentSymbols;
|
|
151
|
+
GLOBAL_lastSymbols.pop();
|
|
152
|
+
GLOBAL_lastSymbols.unshift(symbol);
|
|
153
|
+
|
|
154
|
+
this.setState({ recentSymbolsCount: this.state.recentSymbolsCount + 1 });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
handleInsertSymbol = (symbol) => {
|
|
158
|
+
if (this.props.useExpertMode) this.handleInsertSymbolKatex(symbol);
|
|
159
|
+
else this.handleInsertSymbolMathQuill(symbol);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
isLetter = (char) => {
|
|
163
|
+
return ("a" <= char && char <= "z") || ("A" <= char && char <= "Z");
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Returns:
|
|
167
|
+
// 1. the provided pos if pos is not inside a latex tag
|
|
168
|
+
// 2. a new pos if pos is close to the start or end of a tag (within 2 characters)
|
|
169
|
+
// 3. -1 if pos is not close to the start or end of a tag
|
|
170
|
+
movePosIfInsideTag(latex, pos) {
|
|
171
|
+
if (pos === 0 || pos === latex.length) return pos;
|
|
172
|
+
|
|
173
|
+
let c = latex.charAt(pos);
|
|
174
|
+
if (!this.isLetter(c)) return pos;
|
|
175
|
+
let cm1 = latex.charAt(pos - 1);
|
|
176
|
+
if (!this.isLetter(cm1) && cm1 !== "\\") return pos - 1;
|
|
177
|
+
|
|
178
|
+
// We are inside a latex tag, let's move out of it
|
|
179
|
+
if (pos === 1) return 0;
|
|
180
|
+
if (pos === latex.length - 1) return latex.length;
|
|
181
|
+
|
|
182
|
+
if (latex.charAt(pos - 1) === "\\") return pos - 1;
|
|
183
|
+
if (!this.isLetter(latex.charAt(pos + 1))) return pos + 1;
|
|
184
|
+
if (latex.charAt(pos - 2) === "\\") return pos - 2;
|
|
185
|
+
if (!this.isLetter(latex.charAt(pos + 2))) return pos + 2;
|
|
186
|
+
|
|
187
|
+
return -1;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
handleInsertSymbolKatex = (symbol) => {
|
|
191
|
+
let oldLatex = null;
|
|
192
|
+
let start = -1;
|
|
193
|
+
let end = -1;
|
|
194
|
+
try {
|
|
195
|
+
this.updateRecentSymbols(symbol);
|
|
196
|
+
oldLatex = this.latexInput.value;
|
|
197
|
+
start = this.latexInput.selectionStart;
|
|
198
|
+
end = this.latexInput.selectionEnd;
|
|
199
|
+
|
|
200
|
+
start = this.movePosIfInsideTag(oldLatex, start);
|
|
201
|
+
end = this.movePosIfInsideTag(oldLatex, end);
|
|
202
|
+
if (start === -1 || end === -1) {
|
|
203
|
+
console.log(
|
|
204
|
+
"Can't insert as cursor position or start or end of selection is inside a tag"
|
|
205
|
+
);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// if (this.latexInput !== document.activeElement)
|
|
210
|
+
this.latexInput.focus();
|
|
211
|
+
|
|
212
|
+
let insertParams = getLatexInsertionParams(symbol);
|
|
213
|
+
let insert = insertParams.latexWithMarks;
|
|
214
|
+
// console.log("Insert params: ");
|
|
215
|
+
// console.log(insertParams);
|
|
216
|
+
// console.log(start + ":" + end);
|
|
217
|
+
// console.log(oldLatex);
|
|
218
|
+
|
|
219
|
+
// Move text before cursor into latex if double mark exists in latex
|
|
220
|
+
let pos = insert.indexOf("##");
|
|
221
|
+
if (pos >= 0) {
|
|
222
|
+
if (start !== end) {
|
|
223
|
+
insert = insert.replace("##", oldLatex.substring(start, end));
|
|
224
|
+
// console.log("insert: " + insert);
|
|
225
|
+
oldLatex = oldLatex.substring(0, start) + oldLatex.substring(end);
|
|
226
|
+
end = start;
|
|
227
|
+
} else if (start > 0) {
|
|
228
|
+
insert = insert.replace("##", oldLatex.charAt(start - 1));
|
|
229
|
+
oldLatex =
|
|
230
|
+
oldLatex.substring(0, start - 1) + oldLatex.substring(start);
|
|
231
|
+
start = start - 1;
|
|
232
|
+
end = start;
|
|
138
233
|
} else {
|
|
139
|
-
|
|
140
|
-
recentSymbols.splice(minIndex, 1);
|
|
141
|
-
}
|
|
142
|
-
recentSymbols.push({ name: symbol, count: RECENT_WEIGHT });
|
|
234
|
+
insert = insert.replace("##", "#");
|
|
143
235
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
236
|
+
}
|
|
237
|
+
// Move text before cursor into latex if selection made and single mark exists in latex
|
|
238
|
+
// or otherwise delete selected text
|
|
239
|
+
if (start !== end) {
|
|
240
|
+
pos = insert.indexOf("#");
|
|
241
|
+
if (pos >= 0)
|
|
242
|
+
insert = insert.replace("#", oldLatex.substring(start, end));
|
|
243
|
+
|
|
244
|
+
oldLatex = oldLatex.substring(0, start) + oldLatex.substring(end);
|
|
245
|
+
end = start;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Remove any extra marks after the first mark
|
|
249
|
+
insert = insert.replace("#", MARK);
|
|
250
|
+
insert = insert.replaceAll("#", "");
|
|
251
|
+
insert = insert.replace(MARK, "#");
|
|
252
|
+
|
|
253
|
+
// Construct new latex
|
|
254
|
+
let newLatex =
|
|
255
|
+
oldLatex.substring(0, start) +
|
|
256
|
+
insert +
|
|
257
|
+
MARK +
|
|
258
|
+
oldLatex.substring(start);
|
|
259
|
+
let newSelectionStart = newLatex.indexOf(MARK);
|
|
260
|
+
newLatex =
|
|
261
|
+
newLatex.substring(0, newSelectionStart) +
|
|
262
|
+
newLatex.substring(newSelectionStart + MARK.length);
|
|
263
|
+
|
|
264
|
+
// Move cursor position back to mark in inserted text if applicable
|
|
265
|
+
pos = insert.indexOf("#");
|
|
266
|
+
if (pos > 0) {
|
|
267
|
+
newSelectionStart = newLatex.lastIndexOf("#", newSelectionStart);
|
|
268
|
+
newLatex =
|
|
269
|
+
newLatex.substring(0, newSelectionStart) +
|
|
270
|
+
newLatex.substring(newSelectionStart + 1);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
this.setState({ latex: newLatex }, () => {
|
|
274
|
+
this.latexInput.selectionStart = newSelectionStart;
|
|
275
|
+
this.latexInput.selectionEnd = newSelectionStart;
|
|
276
|
+
this.latexInput.focus();
|
|
277
|
+
});
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.error(error);
|
|
280
|
+
// console.log("Inserting: " + symbol);
|
|
281
|
+
// console.log("into: " + this.latexInput.value);
|
|
282
|
+
// console.log("at: " + start + ":" + end);
|
|
152
283
|
}
|
|
284
|
+
};
|
|
153
285
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
isLetter = (char) => {
|
|
160
|
-
return ("a" <= char && char <= "z") || ("A" <= char && char <= "Z");
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// Returns:
|
|
164
|
-
// 1. the provided pos if pos is not inside a latex tag
|
|
165
|
-
// 2. a new pos if pos is close to the start or end of a tag (within 2 characters)
|
|
166
|
-
// 3. -1 if pos is not close to the start or end of a tag
|
|
167
|
-
movePosIfInsideTag(latex, pos) {
|
|
168
|
-
if (pos === 0 || pos === latex.length) return pos;
|
|
286
|
+
handleInsertSymbolMathQuill = (symbol) => {
|
|
287
|
+
try {
|
|
288
|
+
this.updateRecentSymbols(symbol);
|
|
169
289
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
290
|
+
let insert = getLatexInsertionParams(symbol);
|
|
291
|
+
if (insert === null || insert === undefined) {
|
|
292
|
+
console.err(
|
|
293
|
+
"Could not get latex insertion parameters for symbol: " + symbol
|
|
294
|
+
);
|
|
295
|
+
this.mathQuill.focus();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
174
298
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (pos === latex.length - 1) return latex.length;
|
|
299
|
+
if (insert.typedText) this.mathQuill.typedText(insert.mathQuillLatex);
|
|
300
|
+
else this.mathQuill.write(insert.mathQuillLatex);
|
|
178
301
|
|
|
179
|
-
|
|
180
|
-
if (!this.isLetter(latex.charAt(pos + 1))) return pos + 1;
|
|
181
|
-
if (latex.charAt(pos - 2) === "\\") return pos - 2;
|
|
182
|
-
if (!this.isLetter(latex.charAt(pos + 2))) return pos + 2;
|
|
302
|
+
this.mathQuill.focus();
|
|
183
303
|
|
|
184
|
-
|
|
304
|
+
for (let i = 0; i < insert.moveCursor; i++)
|
|
305
|
+
this.mathQuill.keystroke("Right");
|
|
306
|
+
for (let i = 0; i > insert.moveCursor; i--)
|
|
307
|
+
this.mathQuill.keystroke("Left");
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.error(error);
|
|
310
|
+
console.log("Symbol: " + symbol);
|
|
185
311
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
"Can't insert as cursor position or start or end of selection is inside a tag"
|
|
202
|
-
);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// if (this.latexInput !== document.activeElement)
|
|
207
|
-
this.latexInput.focus();
|
|
208
|
-
|
|
209
|
-
let insertParams = getLatexInsertionParams(symbol);
|
|
210
|
-
let insert = insertParams.latexWithMarks;
|
|
211
|
-
// console.log("Insert params: ");
|
|
212
|
-
// console.log(insertParams);
|
|
213
|
-
// console.log(start + ":" + end);
|
|
214
|
-
// console.log(oldLatex);
|
|
215
|
-
|
|
216
|
-
// Move text before cursor into latex if double mark exists in latex
|
|
217
|
-
let pos = insert.indexOf("##");
|
|
218
|
-
if (pos >= 0) {
|
|
219
|
-
if (start !== end) {
|
|
220
|
-
insert = insert.replace("##", oldLatex.substring(start, end));
|
|
221
|
-
// console.log("insert: " + insert);
|
|
222
|
-
oldLatex = oldLatex.substring(0, start) + oldLatex.substring(end);
|
|
223
|
-
end = start;
|
|
224
|
-
} else if (start > 0) {
|
|
225
|
-
insert = insert.replace("##", oldLatex.charAt(start - 1));
|
|
226
|
-
oldLatex = oldLatex.substring(0, start - 1) + oldLatex.substring(start);
|
|
227
|
-
start = start - 1;
|
|
228
|
-
end = start;
|
|
229
|
-
} else {
|
|
230
|
-
insert = insert.replace("##", "#");
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
// Move text before cursor into latex if selection made and single mark exists in latex
|
|
234
|
-
// or otherwise delete selected text
|
|
235
|
-
if (start !== end) {
|
|
236
|
-
pos = insert.indexOf("#");
|
|
237
|
-
if (pos >= 0) insert = insert.replace("#", oldLatex.substring(start, end));
|
|
238
|
-
|
|
239
|
-
oldLatex = oldLatex.substring(0, start) + oldLatex.substring(end);
|
|
240
|
-
end = start;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Remove any extra marks after the first mark
|
|
244
|
-
insert = insert.replace("#", MARK);
|
|
245
|
-
insert = insert.replaceAll("#", "");
|
|
246
|
-
insert = insert.replace(MARK, "#");
|
|
247
|
-
|
|
248
|
-
// Construct new latex
|
|
249
|
-
let newLatex = oldLatex.substring(0, start) + insert + MARK + oldLatex.substring(start);
|
|
250
|
-
let newSelectionStart = newLatex.indexOf(MARK);
|
|
251
|
-
newLatex =
|
|
252
|
-
newLatex.substring(0, newSelectionStart) +
|
|
253
|
-
newLatex.substring(newSelectionStart + MARK.length);
|
|
254
|
-
|
|
255
|
-
// Move cursor position back to mark in inserted text if applicable
|
|
256
|
-
pos = insert.indexOf("#");
|
|
257
|
-
if (pos > 0) {
|
|
258
|
-
newSelectionStart = newLatex.lastIndexOf("#", newSelectionStart);
|
|
259
|
-
newLatex =
|
|
260
|
-
newLatex.substring(0, newSelectionStart) +
|
|
261
|
-
newLatex.substring(newSelectionStart + 1);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
this.setState({ latex: newLatex }, () => {
|
|
265
|
-
this.latexInput.selectionStart = newSelectionStart;
|
|
266
|
-
this.latexInput.selectionEnd = newSelectionStart;
|
|
267
|
-
this.latexInput.focus();
|
|
268
|
-
});
|
|
269
|
-
} catch (error) {
|
|
270
|
-
console.error(error);
|
|
271
|
-
// console.log("Inserting: " + symbol);
|
|
272
|
-
// console.log("into: " + this.latexInput.value);
|
|
273
|
-
// console.log("at: " + start + ":" + end);
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
handleInsertSymbolMathQuill = (symbol) => {
|
|
278
|
-
try {
|
|
279
|
-
this.updateRecentSymbols(symbol);
|
|
280
|
-
|
|
281
|
-
let insert = getLatexInsertionParams(symbol);
|
|
282
|
-
if (insert === null || insert === undefined) {
|
|
283
|
-
console.err("Could not get latex insertion parameters for symbol: " + symbol);
|
|
284
|
-
this.mathQuill.focus();
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (insert.typedText) this.mathQuill.typedText(insert.mathQuillLatex);
|
|
289
|
-
else this.mathQuill.write(insert.mathQuillLatex);
|
|
290
|
-
|
|
291
|
-
this.mathQuill.focus();
|
|
292
|
-
|
|
293
|
-
for (let i = 0; i < insert.moveCursor; i++) this.mathQuill.keystroke("Right");
|
|
294
|
-
for (let i = 0; i > insert.moveCursor; i--) this.mathQuill.keystroke("Left");
|
|
295
|
-
} catch (error) {
|
|
296
|
-
console.error(error);
|
|
297
|
-
console.log("Symbol: " + symbol);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
handleSelectTab = (tab) => {
|
|
315
|
+
if (!this.props.useExpertMode) this.mathQuill.focus();
|
|
316
|
+
else this.latexInput.focus();
|
|
317
|
+
|
|
318
|
+
GLOBAL_last_selected_tab = tab;
|
|
319
|
+
this.setState({ selectedTab: tab }, () => {
|
|
320
|
+
let buttonAreas = this.symbolButtonAreasInnerContainer.childNodes;
|
|
321
|
+
for (let i = 0; i < buttonAreas.length; i++) {
|
|
322
|
+
if (buttonAreas[i].id === TAB_STARTS_AT_AREA_NAMED[tab]) {
|
|
323
|
+
buttonAreas[i].scrollIntoView({
|
|
324
|
+
behavior: "smooth",
|
|
325
|
+
inline: "start",
|
|
326
|
+
});
|
|
298
327
|
}
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
mathquillDidMount(mathQuill) {
|
|
333
|
+
this.mathQuill = mathQuill;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
componentDidMount() {
|
|
337
|
+
// let thisComponentNode = ReactDOM.findDOMNode(this);
|
|
338
|
+
this.equationEditorDiv.addEventListener("keydown", this.handleKeyDown);
|
|
339
|
+
|
|
340
|
+
// Restore scroll position for tab to last selected
|
|
341
|
+
let buttonAreas = this.symbolButtonAreasInnerContainer.childNodes;
|
|
342
|
+
for (let i = 0; i < buttonAreas.length; i++) {
|
|
343
|
+
if (
|
|
344
|
+
buttonAreas[i].id === TAB_STARTS_AT_AREA_NAMED[GLOBAL_last_selected_tab]
|
|
345
|
+
) {
|
|
346
|
+
buttonAreas[i].scrollIntoView({ behavior: "auto", inline: "start" });
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
componentWillUnmount() {
|
|
352
|
+
// let thisComponentNode = ReactDOM.findDOMNode(this);
|
|
353
|
+
this.equationEditorDiv.removeEventListener("keydown", this.handleKeyDown);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
isMobile() {
|
|
357
|
+
return (
|
|
358
|
+
(window.innerHeight > window.innerWidth && window.innerHeight < 820) ||
|
|
359
|
+
window.innerHeight < 500
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
isTouchDevice() {
|
|
364
|
+
return (
|
|
365
|
+
"ontouchstart" in window ||
|
|
366
|
+
navigator.maxTouchPoints > 0 ||
|
|
367
|
+
navigator.msMaxTouchPoints > 0
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
handleCursorLeft = () => {
|
|
372
|
+
this.mathQuill.keystroke("Left");
|
|
373
|
+
this.mathQuill.focus();
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
handleCursorRight = () => {
|
|
377
|
+
this.mathQuill.keystroke("Right");
|
|
378
|
+
this.mathQuill.focus();
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
handleLatexInputChange = (event) => {
|
|
382
|
+
this.setState({ latex: event.target.value, pretty: true });
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
renderLatexString = (latex) => {
|
|
386
|
+
const options = {
|
|
387
|
+
children: "",
|
|
388
|
+
displayMode: false,
|
|
389
|
+
output: "htmlAndMathml",
|
|
390
|
+
leqno: false,
|
|
391
|
+
fleqn: false,
|
|
392
|
+
throwOnError: false,
|
|
393
|
+
errorColor: "#cc0000",
|
|
394
|
+
macros: {},
|
|
395
|
+
minRuleThickness: 0,
|
|
396
|
+
colorIsTextColor: false,
|
|
397
|
+
strict: "warn",
|
|
398
|
+
trust: false,
|
|
299
399
|
};
|
|
300
400
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
let buttonAreas = this.symbolButtonAreasInnerContainer.childNodes;
|
|
308
|
-
for (let i = 0; i < buttonAreas.length; i++) {
|
|
309
|
-
if (buttonAreas[i].id === TAB_STARTS_AT_AREA_NAMED[tab]) {
|
|
310
|
-
buttonAreas[i].scrollIntoView({
|
|
311
|
-
behavior: "smooth",
|
|
312
|
-
inline: "start",
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
mathquillDidMount(mathQuill) {
|
|
320
|
-
this.mathQuill = mathQuill;
|
|
321
|
-
mathQuill.focus();
|
|
401
|
+
try {
|
|
402
|
+
// returns HTML markup
|
|
403
|
+
return katex.renderToString(latex, options);
|
|
404
|
+
} catch (err) {
|
|
405
|
+
console.error("Could not convert latex string to html: ", latex);
|
|
406
|
+
return "Error: Unable to render latex";
|
|
322
407
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
render() {
|
|
411
|
+
let katexHtml = null;
|
|
412
|
+
let prettyLatex = null;
|
|
413
|
+
if (this.props.useExpertMode) {
|
|
414
|
+
katexHtml = this.renderLatexString(this.state.latex);
|
|
415
|
+
if (this.state.pretty) prettyLatex = this.state.latex;
|
|
416
|
+
else {
|
|
417
|
+
prettyLatex = this.prettifyLatex(this.state.latex);
|
|
418
|
+
}
|
|
335
419
|
}
|
|
336
420
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
421
|
+
let tabNames = TAB_NAMES;
|
|
422
|
+
let buttonAreaNames = BUTTON_AREA_NAMES;
|
|
423
|
+
let buttonAreaWidths = GLOBAL_buttonAreasWidths;
|
|
424
|
+
if (this.props.useExpertMode) {
|
|
425
|
+
tabNames = TAB_NAMES_EXPERT;
|
|
426
|
+
buttonAreaNames = BUTTON_AREA_NAMES_EXPERT;
|
|
427
|
+
buttonAreaWidths = GLOBAL_buttonAreasWidthsExpert;
|
|
340
428
|
}
|
|
341
429
|
|
|
342
|
-
|
|
430
|
+
const recentButtons = GLOBAL_recentSymbols.slice(0, RECENT_MAX).map(
|
|
431
|
+
(symbol, index) => {
|
|
343
432
|
return (
|
|
344
|
-
|
|
345
|
-
|
|
433
|
+
<SymbolButton
|
|
434
|
+
className="SymbolButton"
|
|
435
|
+
key={"recent=symbol-" + index}
|
|
436
|
+
symbol={symbol.name}
|
|
437
|
+
shouldUpdate={true}
|
|
438
|
+
highlight={
|
|
439
|
+
symbol.name === GLOBAL_lastSymbols[0]
|
|
440
|
+
? "0"
|
|
441
|
+
: symbol.name === GLOBAL_lastSymbols[1]
|
|
442
|
+
? "1"
|
|
443
|
+
: symbol.name === GLOBAL_lastSymbols[2]
|
|
444
|
+
? "2"
|
|
445
|
+
: "none"
|
|
446
|
+
}
|
|
447
|
+
handleClick={(latex) => this.handleInsertSymbol(symbol.name)}
|
|
448
|
+
/>
|
|
346
449
|
);
|
|
450
|
+
}
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
if (buttonAreaWidths === null) {
|
|
454
|
+
let widths = [];
|
|
455
|
+
for (let i = 0; i < buttonAreaNames.length; i++) {
|
|
456
|
+
widths[i] = Math.ceil(
|
|
457
|
+
getSymbolsForButtonArea(buttonAreaNames[i]).length / MAX_BUTTON_ROWS
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
buttonAreaWidths = widths;
|
|
347
461
|
}
|
|
348
462
|
|
|
349
|
-
|
|
463
|
+
const buttonAreasButtons = buttonAreaNames.map((buttonArea, index) => {
|
|
464
|
+
return getSymbolsForButtonArea(buttonArea).map((symbol, index) => {
|
|
350
465
|
return (
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
466
|
+
<SymbolButton
|
|
467
|
+
className="SymbolButton"
|
|
468
|
+
key={buttonArea + "-symbol-" + index}
|
|
469
|
+
highlight={buttonArea.toLowerCase()}
|
|
470
|
+
symbol={symbol}
|
|
471
|
+
handleClick={() => this.handleInsertSymbol(symbol)}
|
|
472
|
+
/>
|
|
354
473
|
);
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
handleCursorLeft = () => {
|
|
358
|
-
this.mathQuill.keystroke("Left");
|
|
359
|
-
this.mathQuill.focus();
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
handleCursorRight = () => {
|
|
363
|
-
this.mathQuill.keystroke("Right");
|
|
364
|
-
this.mathQuill.focus();
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
handleLatexInputChange = (event) => {
|
|
368
|
-
this.setState({ latex: event.target.value, pretty: true });
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
renderLatexString = (latex) => {
|
|
372
|
-
const options = {
|
|
373
|
-
children: "",
|
|
374
|
-
displayMode: false,
|
|
375
|
-
output: "htmlAndMathml",
|
|
376
|
-
leqno: false,
|
|
377
|
-
fleqn: false,
|
|
378
|
-
throwOnError: false,
|
|
379
|
-
errorColor: "#cc0000",
|
|
380
|
-
macros: {},
|
|
381
|
-
minRuleThickness: 0,
|
|
382
|
-
colorIsTextColor: false,
|
|
383
|
-
strict: "warn",
|
|
384
|
-
trust: false,
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
try {
|
|
388
|
-
// returns HTML markup
|
|
389
|
-
return katex.renderToString(latex, options);
|
|
390
|
-
} catch (err) {
|
|
391
|
-
console.error("Could not convert latex string to html: ", latex);
|
|
392
|
-
return "Error: Unable to render latex";
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
render() {
|
|
397
|
-
let katexHtml = null;
|
|
398
|
-
let prettyLatex = null;
|
|
399
|
-
if (this.props.useExpertMode) {
|
|
400
|
-
katexHtml = this.renderLatexString(this.state.latex);
|
|
401
|
-
if (this.state.pretty) prettyLatex = this.state.latex;
|
|
402
|
-
else {
|
|
403
|
-
prettyLatex = this.prettifyLatex(this.state.latex);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
474
|
+
});
|
|
475
|
+
});
|
|
406
476
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if (this.props.useExpertMode) {
|
|
411
|
-
tabNames = TAB_NAMES_EXPERT;
|
|
412
|
-
buttonAreaNames = BUTTON_AREA_NAMES_EXPERT;
|
|
413
|
-
buttonAreaWidths = GLOBAL_buttonAreasWidthsExpert;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
const recentButtons = GLOBAL_recentSymbols.slice(0, RECENT_MAX).map((symbol, index) => {
|
|
417
|
-
return (
|
|
418
|
-
<SymbolButton
|
|
419
|
-
className="SymbolButton"
|
|
420
|
-
key={"recent=symbol-" + index}
|
|
421
|
-
symbol={symbol.name}
|
|
422
|
-
shouldUpdate={true}
|
|
423
|
-
highlight={
|
|
424
|
-
symbol.name === GLOBAL_lastSymbols[0]
|
|
425
|
-
? "0"
|
|
426
|
-
: symbol.name === GLOBAL_lastSymbols[1]
|
|
427
|
-
? "1"
|
|
428
|
-
: symbol.name === GLOBAL_lastSymbols[2]
|
|
429
|
-
? "2"
|
|
430
|
-
: "none"
|
|
431
|
-
}
|
|
432
|
-
handleClick={(latex) => this.handleInsertSymbol(symbol.name)}
|
|
433
|
-
/>
|
|
434
|
-
);
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
if (buttonAreaWidths === null) {
|
|
438
|
-
let widths = [];
|
|
439
|
-
for (let i = 0; i < buttonAreaNames.length; i++) {
|
|
440
|
-
widths[i] = Math.ceil(
|
|
441
|
-
getSymbolsForButtonArea(buttonAreaNames[i]).length / MAX_BUTTON_ROWS
|
|
442
|
-
);
|
|
443
|
-
}
|
|
444
|
-
buttonAreaWidths = widths;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const buttonAreasButtons = buttonAreaNames.map((buttonArea, index) => {
|
|
448
|
-
return getSymbolsForButtonArea(buttonArea).map((symbol, index) => {
|
|
449
|
-
return (
|
|
450
|
-
<SymbolButton
|
|
451
|
-
className="SymbolButton"
|
|
452
|
-
key={buttonArea + "-symbol-" + index}
|
|
453
|
-
highlight={buttonArea.toLowerCase()}
|
|
454
|
-
symbol={symbol}
|
|
455
|
-
handleClick={() => this.handleInsertSymbol(symbol)}
|
|
456
|
-
/>
|
|
457
|
-
);
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
return (
|
|
462
|
-
<div className="EquationEditor" ref={(r) => (this.equationEditorDiv = r)}>
|
|
463
|
-
{/* Normal mode. Add cursor move buttons for mobile devices as they don't have
|
|
477
|
+
return (
|
|
478
|
+
<div className="EquationEditor" ref={(r) => (this.equationEditorDiv = r)}>
|
|
479
|
+
{/* Normal mode. Add cursor move buttons for mobile devices as they don't have
|
|
464
480
|
cursor keys in the soft keyboard. */}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
{this.isTouchDevice() && (
|
|
497
|
-
<button
|
|
498
|
-
className="cursorButton"
|
|
499
|
-
onClick={(t) => this.handleCursorRight()}
|
|
500
|
-
>
|
|
501
|
-
{"\u25B7"}
|
|
502
|
-
</button>
|
|
503
|
-
)}
|
|
504
|
-
</div>
|
|
505
|
-
)}
|
|
506
|
-
|
|
507
|
-
{/* Expert mode */}
|
|
508
|
-
{this.props.useExpertMode && (
|
|
509
|
-
<div className="latexInputAreaWrapper">
|
|
510
|
-
<textarea
|
|
511
|
-
className="latexInput"
|
|
512
|
-
autoFocus
|
|
513
|
-
placeholder="Type latex"
|
|
514
|
-
spellCheck="false"
|
|
515
|
-
ref={(r) => (this.latexInput = r)}
|
|
516
|
-
onChange={this.handleLatexInputChange}
|
|
517
|
-
value={prettyLatex}
|
|
518
|
-
></textarea>
|
|
519
|
-
<div
|
|
520
|
-
className="katexRenderedInput"
|
|
521
|
-
dangerouslySetInnerHTML={{ __html: katexHtml }}
|
|
522
|
-
/>
|
|
523
|
-
</div>
|
|
481
|
+
{!this.props.useExpertMode && (
|
|
482
|
+
<div className="editableAndCursorButtonsWrapper">
|
|
483
|
+
{this.isTouchDevice() && (
|
|
484
|
+
<button
|
|
485
|
+
className="cursorButton"
|
|
486
|
+
onClick={(t) => this.handleCursorLeft()}
|
|
487
|
+
>
|
|
488
|
+
{"\u25C1"}
|
|
489
|
+
</button>
|
|
490
|
+
)}
|
|
491
|
+
|
|
492
|
+
<div
|
|
493
|
+
ref={(r) => (this.editableWrapper = r)}
|
|
494
|
+
className="editableWrapper"
|
|
495
|
+
>
|
|
496
|
+
<div className="centerBox">
|
|
497
|
+
<EditableMathField
|
|
498
|
+
className="editable"
|
|
499
|
+
latex={this.state.latex}
|
|
500
|
+
placeholder="Type equation .."
|
|
501
|
+
config={{
|
|
502
|
+
autoOperatorNames: AUTO_OPERATOR_NAMES,
|
|
503
|
+
}}
|
|
504
|
+
mathquillDidMount={(mathQuill) =>
|
|
505
|
+
this.mathquillDidMount(mathQuill)
|
|
506
|
+
}
|
|
507
|
+
onChange={(mathQuill) => this.handleChange(mathQuill)}
|
|
508
|
+
/>
|
|
509
|
+
{this.state.latex.length === 0 && (
|
|
510
|
+
<div className="editable-placeholder">Type equation</div>
|
|
524
511
|
)}
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
525
514
|
|
|
526
|
-
|
|
515
|
+
{this.isTouchDevice() && (
|
|
516
|
+
<button
|
|
517
|
+
className="cursorButton"
|
|
518
|
+
onClick={(t) => this.handleCursorRight()}
|
|
519
|
+
>
|
|
520
|
+
{"\u25B7"}
|
|
521
|
+
</button>
|
|
522
|
+
)}
|
|
523
|
+
</div>
|
|
524
|
+
)}
|
|
525
|
+
|
|
526
|
+
{/* Expert mode */}
|
|
527
|
+
{this.props.useExpertMode && (
|
|
528
|
+
<div className="latexInputAreaWrapper">
|
|
529
|
+
<textarea
|
|
530
|
+
className="latexInput"
|
|
531
|
+
autoFocus
|
|
532
|
+
placeholder="Type latex"
|
|
533
|
+
spellCheck="false"
|
|
534
|
+
ref={(r) => (this.latexInput = r)}
|
|
535
|
+
onChange={this.handleLatexInputChange}
|
|
536
|
+
value={prettyLatex}
|
|
537
|
+
></textarea>
|
|
538
|
+
<div
|
|
539
|
+
className="katexRenderedInput"
|
|
540
|
+
dangerouslySetInnerHTML={{ __html: katexHtml }}
|
|
541
|
+
/>
|
|
542
|
+
</div>
|
|
543
|
+
)}
|
|
544
|
+
|
|
545
|
+
{/* Cancel and submit button row. Cancel button is only shown on mobile devices as
|
|
527
546
|
there may be no visible background to click on to close the modal. */}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
547
|
+
{this.isMobile() && (
|
|
548
|
+
<button
|
|
549
|
+
className="cancelButton"
|
|
550
|
+
type="button"
|
|
551
|
+
onClick={this.handleCancel}
|
|
552
|
+
>
|
|
553
|
+
Cancel
|
|
554
|
+
</button>
|
|
555
|
+
)}
|
|
556
|
+
<button
|
|
557
|
+
className="insertButton"
|
|
558
|
+
type="button"
|
|
559
|
+
onClick={this.handleInsert}
|
|
560
|
+
>
|
|
561
|
+
Insert
|
|
562
|
+
</button>
|
|
563
|
+
|
|
564
|
+
{/* Recent symbols button bar */}
|
|
565
|
+
{!this.isMobile() && (
|
|
566
|
+
<div className="recentSymbolsButtonArea">
|
|
567
|
+
{GLOBAL_recentSymbols.length > 0 &&
|
|
568
|
+
GLOBAL_recentSymbols.length < 6 && (
|
|
569
|
+
<span className="recentSymbols-recentText-active">Recent:</span>
|
|
570
|
+
)}
|
|
571
|
+
{GLOBAL_recentSymbols.length === 0 && (
|
|
572
|
+
<span className="recentSymbols-recentText-not-active">
|
|
573
|
+
Recent:
|
|
574
|
+
</span>
|
|
575
|
+
)}
|
|
576
|
+
{recentButtons}
|
|
577
|
+
</div>
|
|
578
|
+
)}
|
|
579
|
+
|
|
580
|
+
{/* Symbol buttons area */}
|
|
581
|
+
<div>
|
|
582
|
+
<div className="tabBar">
|
|
583
|
+
{tabNames.map((tab, index) => {
|
|
584
|
+
return (
|
|
585
|
+
<span key={"tabBar-" + tab}>
|
|
586
|
+
{this.state.selectedTab === tab ? (
|
|
587
|
+
<span className="tab-selected" selected={true}>
|
|
588
|
+
{tab}
|
|
589
|
+
</span>
|
|
590
|
+
) : (
|
|
591
|
+
<span
|
|
592
|
+
className="tab"
|
|
593
|
+
onClick={(t) => this.handleSelectTab(tab)}
|
|
594
|
+
>
|
|
595
|
+
{tab}
|
|
596
|
+
</span>
|
|
597
|
+
)}{" "}
|
|
598
|
+
</span>
|
|
599
|
+
);
|
|
600
|
+
})}
|
|
601
|
+
</div>
|
|
602
|
+
|
|
603
|
+
<div className="symbolButtonAreasOuterContainer">
|
|
604
|
+
<div
|
|
605
|
+
className="symbolButtonAreasInnerContainer"
|
|
606
|
+
ref={(r) => (this.symbolButtonAreasInnerContainer = r)}
|
|
607
|
+
>
|
|
608
|
+
{buttonAreasButtons.map((button, index) => {
|
|
609
|
+
return (
|
|
610
|
+
<div
|
|
611
|
+
id={buttonAreaNames[index]}
|
|
612
|
+
key={buttonAreaNames[index]}
|
|
613
|
+
className={
|
|
614
|
+
"symbolButtonArea symbolButtonArea-" +
|
|
615
|
+
buttonAreaWidths[index]
|
|
616
|
+
}
|
|
617
|
+
>
|
|
618
|
+
{button}
|
|
619
|
+
</div>
|
|
620
|
+
);
|
|
621
|
+
})}
|
|
595
622
|
</div>
|
|
596
|
-
|
|
597
|
-
|
|
623
|
+
</div>
|
|
624
|
+
</div>
|
|
625
|
+
</div>
|
|
626
|
+
);
|
|
627
|
+
}
|
|
598
628
|
}
|