@zzish/math-rich-input 0.1.4 → 0.1.6
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 +355 -250
- package/dist/math-rich-input.css +383 -384
- package/package.json +1 -3
- package/rollup.config.js +4 -4
- package/src/AccentBar.css +50 -43
- package/src/AccentBar.jsx +1007 -617
- package/src/EquationEditor.css +95 -99
- package/src/EquationEditor.jsx +416 -368
- package/src/EquationEditorModal.css +59 -68
- package/src/MathRichInput.css +30 -23
- package/src/MathRichInput.jsx +1239 -978
- package/src/SymbolButton.css +31 -31
- package/src/SymbolButton.jsx +47 -49
- package/src/Toolbar.css +20 -30
- package/src/Toolbar.jsx +177 -145
package/src/Toolbar.jsx
CHANGED
|
@@ -1,219 +1,251 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
2
|
import katex from "katex";
|
|
3
3
|
|
|
4
4
|
import AccentBar from "./AccentBar";
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import "./Toolbar.css";
|
|
7
7
|
|
|
8
8
|
// const DEFAULT_TRANSLATE = 0
|
|
9
|
-
const DEFAULT_TOP = 0
|
|
10
|
-
const DEFAULT_LEFT = 0
|
|
11
|
-
const BOTTOM_MARGIN = 5
|
|
9
|
+
const DEFAULT_TOP = 0;
|
|
10
|
+
const DEFAULT_LEFT = 0;
|
|
11
|
+
const BOTTOM_MARGIN = 5;
|
|
12
12
|
|
|
13
|
-
const CTRL_OR_META = /(Mac)/i.test(window.navigator.platform)
|
|
13
|
+
const CTRL_OR_META = /(Mac)/i.test(window.navigator.platform)
|
|
14
|
+
? "\u2318"
|
|
15
|
+
: "CTRL+";
|
|
14
16
|
|
|
15
17
|
export default class Toolbar extends React.Component {
|
|
16
18
|
constructor(props) {
|
|
17
19
|
super(props);
|
|
18
20
|
|
|
19
21
|
this.state = {
|
|
20
|
-
showAccents:false,
|
|
21
|
-
top:DEFAULT_TOP,
|
|
22
|
-
left:DEFAULT_LEFT
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
this.handleButtonClick = this.handleButtonClick.bind(this)
|
|
26
|
-
|
|
27
|
-
this.suppliedClickHandler = null
|
|
28
|
-
this.suppliedAccentLetterFetcher = null
|
|
29
|
-
this.suppliedAccentLetterReplacer = null
|
|
30
|
-
this.accentLetter = null
|
|
31
|
-
this.shouldUpdatePosition = true
|
|
22
|
+
showAccents: false,
|
|
23
|
+
top: DEFAULT_TOP,
|
|
24
|
+
left: DEFAULT_LEFT,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
this.handleButtonClick = this.handleButtonClick.bind(this);
|
|
28
|
+
|
|
29
|
+
this.suppliedClickHandler = null;
|
|
30
|
+
this.suppliedAccentLetterFetcher = null;
|
|
31
|
+
this.suppliedAccentLetterReplacer = null;
|
|
32
|
+
this.accentLetter = null;
|
|
33
|
+
this.shouldUpdatePosition = true;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
handleButtonClick = (event, buttonName) => {
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
+
this.suppliedClickHandler(event, buttonName);
|
|
38
|
+
};
|
|
37
39
|
|
|
38
40
|
showAccentBar = (event) => {
|
|
39
|
-
console.log("showAccentBar")
|
|
41
|
+
console.log("showAccentBar");
|
|
40
42
|
if (!this.state.showAccents)
|
|
41
|
-
this.accentLetter = this.suppliedAccentLetterFetcher()
|
|
42
|
-
this.setState({showAccents: true})
|
|
43
|
-
event.preventDefault()
|
|
44
|
-
}
|
|
43
|
+
this.accentLetter = this.suppliedAccentLetterFetcher();
|
|
44
|
+
this.setState({ showAccents: true });
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
};
|
|
45
47
|
|
|
46
48
|
hideAccentBar = (event) => {
|
|
47
|
-
console.log("hideAccentBar")
|
|
48
|
-
this.setState({showAccents: false})
|
|
49
|
-
}
|
|
49
|
+
console.log("hideAccentBar");
|
|
50
|
+
this.setState({ showAccents: false });
|
|
51
|
+
};
|
|
50
52
|
|
|
51
53
|
showOrHideAccentBar = (event) => {
|
|
52
54
|
if (!this.state.showAccents)
|
|
53
|
-
this.accentLetter = this.suppliedAccentLetterFetcher()
|
|
54
|
-
this.setState({showAccents: !this.state.showAccents})
|
|
55
|
-
}
|
|
55
|
+
this.accentLetter = this.suppliedAccentLetterFetcher();
|
|
56
|
+
this.setState({ showAccents: !this.state.showAccents });
|
|
57
|
+
};
|
|
56
58
|
|
|
57
59
|
handleButtonClick = (event, buttonName) => {
|
|
58
|
-
this.suppliedClickHandler(event, buttonName)
|
|
59
|
-
}
|
|
60
|
+
this.suppliedClickHandler(event, buttonName);
|
|
61
|
+
};
|
|
60
62
|
|
|
61
63
|
handleAccentButtonClick = (event, oldCharacter, newCharacter) => {
|
|
62
|
-
console.log("handleAccentButtonClick")
|
|
63
|
-
this.suppliedAccentLetterInserter(event, oldCharacter, newCharacter)
|
|
64
|
+
console.log("handleAccentButtonClick");
|
|
65
|
+
this.suppliedAccentLetterInserter(event, oldCharacter, newCharacter);
|
|
64
66
|
// this.hideAccentBar(null)
|
|
65
|
-
}
|
|
67
|
+
};
|
|
66
68
|
|
|
67
69
|
componentDidMount() {
|
|
68
70
|
// We render once to find out the component's preferred size and then render again to update it
|
|
69
71
|
if (this.shouldUpdatePosition) {
|
|
70
72
|
// Lets position the accent bar below the input field and right aligned
|
|
71
|
-
let thisBar = this.toolbar
|
|
72
|
-
let node = thisBar.parentNode
|
|
73
|
-
while
|
|
74
|
-
node
|
|
73
|
+
let thisBar = this.toolbar;
|
|
74
|
+
let node = thisBar.parentNode;
|
|
75
|
+
while (
|
|
76
|
+
node !== null &&
|
|
77
|
+
!node.classList.contains("MathRichInput-Container")
|
|
78
|
+
)
|
|
79
|
+
node = node.parentNode;
|
|
75
80
|
|
|
76
81
|
if (node === null) {
|
|
77
|
-
console.error(
|
|
78
|
-
|
|
82
|
+
console.error(
|
|
83
|
+
"Could not find ancestor MathRichInput-Container find parent"
|
|
84
|
+
);
|
|
85
|
+
return;
|
|
79
86
|
}
|
|
80
87
|
|
|
81
|
-
let inputBounds = node.getBoundingClientRect()
|
|
82
|
-
let thisBounds = thisBar.getBoundingClientRect()
|
|
88
|
+
let inputBounds = node.getBoundingClientRect();
|
|
89
|
+
let thisBounds = thisBar.getBoundingClientRect();
|
|
83
90
|
|
|
84
|
-
this.shouldUpdatePosition = false
|
|
91
|
+
this.shouldUpdatePosition = false;
|
|
85
92
|
|
|
86
93
|
// Vertically align above input field
|
|
87
|
-
let top = inputBounds.top - thisBounds.height - BOTTOM_MARGIN
|
|
94
|
+
let top = inputBounds.top - thisBounds.height - BOTTOM_MARGIN;
|
|
88
95
|
|
|
89
96
|
// Horizontally align to right of input field
|
|
90
|
-
let left = inputBounds.right - thisBounds.width
|
|
97
|
+
let left = inputBounds.right - thisBounds.width;
|
|
91
98
|
|
|
92
99
|
// Make sure it is on screen horizontally
|
|
93
100
|
if (thisBounds.width > inputBounds.width)
|
|
94
|
-
left = inputBounds.left - (thisBounds.width - inputBounds.width)/2
|
|
101
|
+
left = inputBounds.left - (thisBounds.width - inputBounds.width) / 2;
|
|
95
102
|
|
|
96
103
|
if (left + thisBounds.width > window.innerWidth)
|
|
97
|
-
left = window.innerWidth - thisBounds.width - 2
|
|
104
|
+
left = window.innerWidth - thisBounds.width - 2;
|
|
98
105
|
|
|
99
|
-
if (left < 0)
|
|
100
|
-
left = 0
|
|
101
|
-
|
|
102
|
-
this.setState({left, top})
|
|
106
|
+
if (left < 0) left = 0;
|
|
103
107
|
|
|
108
|
+
this.setState({ left, top });
|
|
104
109
|
} else {
|
|
105
|
-
this.shouldUpdatePosition = true
|
|
110
|
+
this.shouldUpdatePosition = true;
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
render() {
|
|
110
|
-
console.log("Rendering toolbar")
|
|
111
|
-
this.suppliedClickHandler
|
|
112
|
-
this.suppliedAccentLetterFetcher = this.props.fetchAccentLetter
|
|
113
|
-
this.suppliedAccentLetterInserter = this.props.insertAccentLetter
|
|
114
|
-
|
|
115
|
-
let enableFontStyling = this.props.enableFontStyling ?? true
|
|
116
|
-
let enableMath = this.props.enableMath ?? true
|
|
117
|
-
|
|
118
|
-
let katexHTML = katex.renderToString("f(x)")
|
|
115
|
+
console.log("Rendering toolbar");
|
|
116
|
+
this.suppliedClickHandler = this.props.onButtonClick;
|
|
117
|
+
this.suppliedAccentLetterFetcher = this.props.fetchAccentLetter;
|
|
118
|
+
this.suppliedAccentLetterInserter = this.props.insertAccentLetter;
|
|
119
|
+
|
|
120
|
+
let enableFontStyling = this.props.enableFontStyling ?? true;
|
|
121
|
+
let enableMath = this.props.enableMath ?? true;
|
|
122
|
+
|
|
123
|
+
let katexHTML = katex.renderToString("f(x)");
|
|
119
124
|
// Not really necessary, but lets clean out some unnessary mathml nodes
|
|
120
|
-
let start = katexHTML.indexOf(
|
|
121
|
-
let end = -1
|
|
122
|
-
if (start > 0)
|
|
123
|
-
end = katexHTML.indexOf("</math></span>",start+30)
|
|
125
|
+
let start = katexHTML.indexOf('<span class="katex-mathml">');
|
|
126
|
+
let end = -1;
|
|
127
|
+
if (start > 0) end = katexHTML.indexOf("</math></span>", start + 30);
|
|
124
128
|
if (end > 0)
|
|
125
|
-
katexHTML = katexHTML.substring(0,start) + katexHTML.substring(end+14)
|
|
129
|
+
katexHTML = katexHTML.substring(0, start) + katexHTML.substring(end + 14);
|
|
126
130
|
|
|
127
131
|
// Set up the tranlation to place the toolbar where we want it
|
|
128
|
-
let style = {...this.props.style}
|
|
132
|
+
let style = { ...this.props.style };
|
|
129
133
|
// if (this.state.translateX !== DEFAULT_TRANSLATE) {
|
|
130
134
|
// style.transform = "translate("+this.state.translateX+"px, "+this.state.translateY+"px)";
|
|
131
135
|
// }
|
|
132
136
|
if (this.state.left !== DEFAULT_LEFT || this.state.top !== DEFAULT_TOP) {
|
|
133
|
-
style.left = this.state.left + "px"
|
|
134
|
-
style.top = this.state.top + "px"
|
|
137
|
+
style.left = this.state.left + "px";
|
|
138
|
+
style.top = this.state.top + "px";
|
|
135
139
|
}
|
|
136
140
|
|
|
137
|
-
return
|
|
138
|
-
<div
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
141
|
+
return (
|
|
142
|
+
<div className="Toolbar" style={style} ref={(r) => (this.toolbar = r)}>
|
|
143
|
+
{enableFontStyling && (
|
|
144
|
+
<>
|
|
145
|
+
<button
|
|
146
|
+
name="Bold"
|
|
147
|
+
className={
|
|
148
|
+
"Toolbar-button TB-narrow" +
|
|
149
|
+
(this.props.activeButtons.bold ? " TB-selected" : "")
|
|
150
|
+
}
|
|
151
|
+
onClick={(e) => e.preventDefault()}
|
|
152
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
153
|
+
onMouseUp={(e) => this.handleButtonClick(e, "Bold")}
|
|
154
|
+
>
|
|
155
|
+
<b>B</b>
|
|
156
|
+
</button>
|
|
157
|
+
|
|
158
|
+
<button
|
|
159
|
+
name="Italic"
|
|
160
|
+
className={
|
|
161
|
+
"Toolbar-button TB-narrow" +
|
|
162
|
+
(this.props.activeButtons.italic ? " TB-selected" : "")
|
|
163
|
+
}
|
|
164
|
+
onClick={(e) => e.preventDefault()}
|
|
165
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
166
|
+
onMouseUp={(e) => this.handleButtonClick(e, "Italic")}
|
|
167
|
+
>
|
|
168
|
+
<i>I</i>
|
|
169
|
+
</button>
|
|
170
|
+
|
|
171
|
+
<button
|
|
172
|
+
name="Underline"
|
|
173
|
+
className={
|
|
174
|
+
"Toolbar-button TB-narrow" +
|
|
175
|
+
(this.props.activeButtons.underline ? " TB-selected" : "")
|
|
176
|
+
}
|
|
177
|
+
onClick={(e) => e.preventDefault()}
|
|
178
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
179
|
+
onMouseUp={(e) => this.handleButtonClick(e, "Underline")}
|
|
180
|
+
>
|
|
181
|
+
<u>U</u>
|
|
182
|
+
</button>
|
|
183
|
+
|
|
184
|
+
<button
|
|
185
|
+
name="Superscript"
|
|
186
|
+
className={
|
|
187
|
+
"Toolbar-button TB-narrow" +
|
|
188
|
+
(this.props.activeButtons.superscript ? " TB-selected" : "")
|
|
189
|
+
}
|
|
190
|
+
onClick={(e) => e.preventDefault()}
|
|
191
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
192
|
+
onMouseUp={(e) => this.handleButtonClick(e, "Superscript")}
|
|
193
|
+
>
|
|
194
|
+
a<sup>x</sup>
|
|
195
|
+
</button>
|
|
196
|
+
|
|
197
|
+
<button
|
|
198
|
+
name="Subscript"
|
|
199
|
+
className={
|
|
200
|
+
"Toolbar-button TB-narrow" +
|
|
201
|
+
(this.props.activeButtons.subscript ? " TB-selected" : "")
|
|
202
|
+
}
|
|
203
|
+
onClick={(e) => e.preventDefault()}
|
|
204
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
205
|
+
onMouseUp={(e) => this.handleButtonClick(e, "Subscript")}
|
|
206
|
+
>
|
|
207
|
+
a<sub>x</sub>
|
|
208
|
+
</button>
|
|
209
|
+
</>
|
|
210
|
+
)}
|
|
211
|
+
|
|
212
|
+
<button
|
|
213
|
+
name="Accents"
|
|
214
|
+
className={
|
|
215
|
+
"Toolbar-button TB-narrow" +
|
|
216
|
+
(this.state.showAccents ? " TB-selected" : "")
|
|
217
|
+
}
|
|
194
218
|
onMouseDown={(e) => e.preventDefault()}
|
|
195
219
|
onClick={(e) => e.preventDefault()}
|
|
196
|
-
onMouseUp={(e) => this.showOrHideAccentBar(e)}
|
|
220
|
+
onMouseUp={(e) => this.showOrHideAccentBar(e)}
|
|
221
|
+
>
|
|
197
222
|
á
|
|
198
223
|
</button>
|
|
199
224
|
|
|
200
|
-
{enableMath &&
|
|
201
|
-
<button
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
225
|
+
{enableMath && (
|
|
226
|
+
<button
|
|
227
|
+
name="Equation"
|
|
228
|
+
className="Toolbar-button TB-wide"
|
|
229
|
+
onMouseDown={(e) => this.handleButtonClick(e, "Equation")}
|
|
230
|
+
>
|
|
231
|
+
<div dangerouslySetInnerHTML={{ __html: katexHTML }} />
|
|
232
|
+
<span className="tooltiptext">
|
|
233
|
+
Insert equation [{CTRL_OR_META}Enter]
|
|
234
|
+
</span>
|
|
205
235
|
</button>
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
236
|
+
)}
|
|
237
|
+
|
|
238
|
+
{this.state.showAccents && (
|
|
239
|
+
<AccentBar
|
|
240
|
+
accentLetter={this.accentLetter}
|
|
241
|
+
keyPressed={this.props.keyPressed}
|
|
242
|
+
fetchAccentLetter={this.props.fetchAccentLetter}
|
|
243
|
+
onButtonClick={(e, oldCharacter, newCharacter) =>
|
|
244
|
+
this.handleAccentButtonClick(e, oldCharacter, newCharacter)
|
|
245
|
+
}
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
216
248
|
</div>
|
|
217
|
-
)
|
|
249
|
+
);
|
|
218
250
|
}
|
|
219
251
|
}
|