@zzish/math-rich-input 0.1.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.
@@ -0,0 +1,130 @@
1
+ /* Fixes is a katex bug where horizontal lines are not rendering */
2
+ /* .frac-line {
3
+ background-color: #404040;
4
+ height:0.03em;
5
+ }
6
+ .overline-line {
7
+ background-color: #404040;
8
+ height:0.03em;
9
+ } */
10
+
11
+ .SymbolButton {
12
+ overflow: hidden; /* Important to fix a bug where katex renders parts of the equation outside the box */
13
+ font-size: 14px;
14
+ font-weight: bold;
15
+ color: #202020;
16
+ background: #e0e0e0;
17
+ text-align: center;
18
+ border:0px;
19
+ border-radius: 5px;
20
+ width:36px;
21
+ height:36px;
22
+ padding:1px 1px 1px 1px;
23
+ margin:3px;
24
+ /* transition: all .2s ease-in-out; */
25
+ }
26
+
27
+
28
+ .SymbolButton:hover {
29
+ filter: saturate(2.0);
30
+ color: black;
31
+ /* transform: scale(1.3); */
32
+ cursor: pointer;
33
+ box-shadow: 1px 1px 3px #808080;
34
+ animation: 2s grow;
35
+ animation-fill-mode: forwards;
36
+ }
37
+
38
+ @keyframes grow {
39
+ 00% {
40
+ transform: scale(1);
41
+ box-shadow: 0px 0px 0px #808080;
42
+ }
43
+ 10% {
44
+ transform: scale(1.3);
45
+ box-shadow: 1px 1px 3px #808080;
46
+ }
47
+ 90% {
48
+ transform: scale(1.3);
49
+ box-shadow: 1px 1px 3px #808080;
50
+ }
51
+ 100% {
52
+ transform: scale(1.8);
53
+ box-shadow: 1px 1px 3px #808080;
54
+ }
55
+ }
56
+
57
+ .SymbolButton:active {
58
+ background: #808080;
59
+ color: white;
60
+ transform: scale(1.3);
61
+ cursor: pointer;
62
+ }
63
+
64
+ .SymbolButton-fadeIn {
65
+ opacity:1;
66
+ width:100px;
67
+ height:100px;
68
+ transition: width 0.5s, height 0.5s, opacity 0.5s 0.5s;
69
+ }
70
+
71
+ .highlight-0{
72
+ background: #ebc9eb;
73
+ /* background: #80e0e0; */
74
+ }
75
+ /*
76
+ .highlight-1{
77
+ background: #c0e0e0;
78
+ }
79
+ /* .highlight-2{
80
+ background: #c0e0e0;
81
+ } */
82
+
83
+
84
+ .highlight-operators{
85
+ background: #F8e8D8;
86
+ }
87
+
88
+ .highlight-angles{
89
+ background: #cceded;
90
+ }
91
+
92
+ .highlight-greeklower{
93
+ background: #F8e8D8;
94
+ }
95
+
96
+ .highlight-greekupper{
97
+ background: #cceded;
98
+ }
99
+
100
+ .highlight-misc{
101
+ background: #F8e8D8;
102
+ }
103
+
104
+ .highlight-sets{
105
+ background: #cceded;
106
+ }
107
+
108
+ .highlight-chemistry{
109
+ background: #F8e8D8;
110
+ }
111
+
112
+ .highlight-elements{
113
+ background: #cceded;
114
+ }
115
+
116
+ .highlight-physics2{
117
+ background: #F8e8D8;
118
+ }
119
+
120
+ .highlight-physics3{
121
+ background: #cceded;
122
+ }
123
+
124
+ .highlight-expert{
125
+ background: #f4d6f5;
126
+ }
127
+
128
+ .highlight-matrices{
129
+ background: #f4d6f5;
130
+ }
@@ -0,0 +1,96 @@
1
+ import React from 'react'
2
+
3
+ import katex from "katex";
4
+
5
+ import './SymbolButton.css';
6
+
7
+
8
+ import {
9
+ getSymbolButtonLatex,
10
+ getSymbolButtonScaling,
11
+ // getSymbolButtonTooltip,
12
+ getSymbolButtonHtml,
13
+ setSymbolButtonHtml
14
+ } from './equationEditorButtonsHelper.js';
15
+
16
+ export default class SymbolButton extends React.Component {
17
+
18
+ // This is a fixed component that never changes
19
+ shouldComponentUpdate() {
20
+ if (this.props.shouldUpdate)
21
+ return this.props.shouldUpdate
22
+ return false
23
+ }
24
+
25
+
26
+ constructor(props) {
27
+ super(props);
28
+ this.suppliedClickHandler = null
29
+ }
30
+
31
+ static getButtonForDescendent(element) {
32
+ while (element && element.tagName.toLowerCase() !== "button")
33
+ element = element.parentNode;
34
+ return element
35
+ }
36
+
37
+ static getSymbolName(element) {
38
+ var button = SymbolButton.getButtonForDescendent(element)
39
+ if (button)
40
+ return button.getAttribute("name")
41
+ return null
42
+ }
43
+
44
+ handleSymbolButtonMouseDown = (event) => {
45
+ event.preventDefault()
46
+ }
47
+
48
+ handleSymbolButtonClick = (event) => {
49
+ this.suppliedClickHandler(event)
50
+ event.preventDefault()
51
+ }
52
+
53
+ render() {
54
+ // const {symbol} = this.props;
55
+ const {symbol, handleClick} = this.props;
56
+ this.suppliedClickHandler = handleClick
57
+
58
+ const fontSize = (14*getSymbolButtonScaling(symbol))/100
59
+ var buttonStyle = {fontSize: fontSize+'px'};
60
+ // const tooltipText = getSymbolButtonTooltip(symbol)
61
+
62
+ let katexHTML = getSymbolButtonHtml(symbol)
63
+ if (katexHTML === undefined || katexHTML === null) {
64
+ let latex = getSymbolButtonLatex(symbol)
65
+ katexHTML = katex.renderToString(latex)
66
+
67
+ // Not necessary, but lets clean out some unnessary mathml nodes
68
+ let start = katexHTML.indexOf("<span class=\"katex-mathml\">")
69
+ let end = -1
70
+ if (start > 0)
71
+ end = katexHTML.indexOf("</math></span>",start+30)
72
+ if (end > 0)
73
+ katexHTML = katexHTML.substring(0,start) + katexHTML.substring(end+14)
74
+
75
+ setSymbolButtonHtml(symbol, katexHTML)
76
+ }
77
+
78
+ let useClassName = "SymbolButton"
79
+ if (this.props.highlight) {
80
+ useClassName = useClassName+" highlight-"+this.props.highlight
81
+ }
82
+
83
+ return (
84
+ <span id='SymbolButton'>
85
+ <button name={symbol}
86
+ className={useClassName} style={buttonStyle}
87
+ onMouseDown={this.handleSymbolButtonMouseDown}
88
+ onClick={this.handleSymbolButtonClick}
89
+ ref={(e) => { this.button = e; }}>
90
+ <div name={symbol} dangerouslySetInnerHTML={{__html: katexHTML}} />
91
+ {/* <span className="tooltiptext">{tooltipText}</span> */}
92
+ </button>
93
+ </span>
94
+ )
95
+ }
96
+ }
@@ -0,0 +1,142 @@
1
+ /* Note that the top left position of the toolbar is set programatically in componentDidMount */
2
+
3
+ .Toolbar {
4
+ position: fixed;
5
+ z-index: 1;
6
+ /* width:270px; */
7
+ height:42px;
8
+ background-color: white;
9
+ border: 1px solid #d0d0d0;
10
+ border-radius: 5px;
11
+ box-shadow: 1px 1px 5px #a0a0a0;
12
+ animation: 0.5s fadeIn1;
13
+ animation-fill-mode: forwards;
14
+ top: 0px;
15
+ left: 0px; /* important to set this so that toolbar initially renders full width */
16
+ }
17
+
18
+ @keyframes fadeIn1 {
19
+ 0% {
20
+ opacity: 0;
21
+ }
22
+ 100% {
23
+ opacity: 1.0;
24
+ }
25
+ }
26
+
27
+ .Toolbar-button {
28
+ font-family:sans-serif;
29
+ font-size: 16px;
30
+ width:40px;
31
+ height: 36px;
32
+ background-color: white;
33
+ border-radius: 5px;
34
+ border: 0px;
35
+ padding: 1px;
36
+ margin: 2px;
37
+ }
38
+
39
+
40
+ .Toolbar-button:hover {
41
+ color: white;
42
+ background: #663676;
43
+ cursor: pointer;
44
+ }
45
+
46
+ .Toolbar-button:active {
47
+ background: #808080;
48
+ color: white;
49
+ cursor: pointer;
50
+ }
51
+
52
+ .TB-narrow {
53
+ width:30px;
54
+ }
55
+
56
+ .TB-wide {
57
+ width:46px;
58
+ }
59
+
60
+ .TB-up4 {
61
+ position: relative;
62
+ top:-4px;
63
+ }
64
+
65
+ .TB-selected {
66
+ background: #D0D0D0;
67
+ color: black;
68
+ cursor: pointer;
69
+ animation: 0.2s TB-fadeIn;
70
+ animation-fill-mode: forwards;
71
+ }
72
+
73
+ .TB-selected:hover {
74
+ color: white;
75
+ background: #663676;
76
+ cursor: pointer;
77
+ }
78
+
79
+ @keyframes TB-fadeIn {
80
+ 00% {
81
+ background: white;
82
+ }
83
+ 100% {
84
+ background: #D0D0D0;
85
+ }
86
+ }
87
+
88
+
89
+ /* Tooltip text */
90
+ .Toolbar-button .tooltiptext {
91
+ font-weight: normal;
92
+ visibility: hidden;
93
+ width: 100px;
94
+ background-color: #222;
95
+ color: #fff;
96
+ /*font-weight: bold;*/
97
+ font-size: 12px;
98
+ text-align: center;
99
+ padding: 4px 10px;
100
+ border-radius: 6px;
101
+
102
+ /* Position the tooltip text - see examples below! */
103
+ position: absolute;
104
+ z-index: 2;
105
+ top: -90%; /* Must leave a small gap between tooltip and button */
106
+ left: 50px;
107
+
108
+ /* transform: translate (50%,0%); */
109
+ }
110
+
111
+ /* Show the tooltip text when you mouse over the tooltip container */
112
+ .Toolbar-button:hover .tooltiptext {
113
+ animation: 0.8s fadeIn2;
114
+ animation-fill-mode: forwards;
115
+ visibility: visible;
116
+ /* width: 100px; */
117
+ /* top: -90%; */ /* Must leave a small gap between tooltip and button */
118
+ /* left: 20%; */
119
+ /* margin-left: -50px; */
120
+ }
121
+
122
+ @keyframes fadeIn2 {
123
+ 00% {
124
+ opacity: 0;
125
+ }
126
+ 70% {
127
+ opacity: 0;
128
+ }
129
+ 100% {
130
+ visibility: visible;
131
+ opacity: 0.9;
132
+ }
133
+ }
134
+
135
+ /* .tooltip {
136
+ position: relative;
137
+ display: inline-block;
138
+ border-bottom: 1px dotted black; /* If you want dots under the hoverable text
139
+ } */
140
+
141
+
142
+
@@ -0,0 +1,219 @@
1
+ import React from 'react'
2
+ import katex from "katex";
3
+
4
+ import AccentBar from "./AccentBar";
5
+
6
+ import './Toolbar.css';
7
+
8
+ // const DEFAULT_TRANSLATE = 0
9
+ const DEFAULT_TOP = 0
10
+ const DEFAULT_LEFT = 0
11
+ const BOTTOM_MARGIN = 5
12
+
13
+ const CTRL_OR_META = /(Mac)/i.test(window.navigator.platform) ? "\u2318": "CTRL+"
14
+
15
+ export default class Toolbar extends React.Component {
16
+ constructor(props) {
17
+ super(props);
18
+
19
+ 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
32
+ }
33
+
34
+ handleButtonClick = (event, buttonName) => {
35
+ this.suppliedClickHandler(event, buttonName)
36
+ }
37
+
38
+ showAccentBar = (event) => {
39
+ console.log("showAccentBar")
40
+ if (!this.state.showAccents)
41
+ this.accentLetter = this.suppliedAccentLetterFetcher()
42
+ this.setState({showAccents: true})
43
+ event.preventDefault()
44
+ }
45
+
46
+ hideAccentBar = (event) => {
47
+ console.log("hideAccentBar")
48
+ this.setState({showAccents: false})
49
+ }
50
+
51
+ showOrHideAccentBar = (event) => {
52
+ if (!this.state.showAccents)
53
+ this.accentLetter = this.suppliedAccentLetterFetcher()
54
+ this.setState({showAccents: !this.state.showAccents})
55
+ }
56
+
57
+ handleButtonClick = (event, buttonName) => {
58
+ this.suppliedClickHandler(event, buttonName)
59
+ }
60
+
61
+ handleAccentButtonClick = (event, oldCharacter, newCharacter) => {
62
+ console.log("handleAccentButtonClick")
63
+ this.suppliedAccentLetterInserter(event, oldCharacter, newCharacter)
64
+ // this.hideAccentBar(null)
65
+ }
66
+
67
+ componentDidMount() {
68
+ // We render once to find out the component's preferred size and then render again to update it
69
+ if (this.shouldUpdatePosition) {
70
+ // Lets position the accent bar below the input field and right aligned
71
+ let thisBar = this.toolbar
72
+ let node = thisBar.parentNode
73
+ while(node !== null && !node.classList.contains("MathRichInput-Container"))
74
+ node = node.parentNode
75
+
76
+ if (node === null) {
77
+ console.error("Could not find ancestor MathRichInput-Container find parent")
78
+ return
79
+ }
80
+
81
+ let inputBounds = node.getBoundingClientRect()
82
+ let thisBounds = thisBar.getBoundingClientRect()
83
+
84
+ this.shouldUpdatePosition = false
85
+
86
+ // Vertically align above input field
87
+ let top = inputBounds.top - thisBounds.height - BOTTOM_MARGIN
88
+
89
+ // Horizontally align to right of input field
90
+ let left = inputBounds.right - thisBounds.width
91
+
92
+ // Make sure it is on screen horizontally
93
+ if (thisBounds.width > inputBounds.width)
94
+ left = inputBounds.left - (thisBounds.width - inputBounds.width)/2
95
+
96
+ if (left + thisBounds.width > window.innerWidth)
97
+ left = window.innerWidth - thisBounds.width - 2
98
+
99
+ if (left < 0)
100
+ left = 0
101
+
102
+ this.setState({left, top})
103
+
104
+ } else {
105
+ this.shouldUpdatePosition = true
106
+ }
107
+ }
108
+
109
+ render() {
110
+ console.log("Rendering toolbar")
111
+ this.suppliedClickHandler = this.props.onButtonClick
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)")
119
+ // Not really necessary, but lets clean out some unnessary mathml nodes
120
+ let start = katexHTML.indexOf("<span class=\"katex-mathml\">")
121
+ let end = -1
122
+ if (start > 0)
123
+ end = katexHTML.indexOf("</math></span>",start+30)
124
+ if (end > 0)
125
+ katexHTML = katexHTML.substring(0,start) + katexHTML.substring(end+14)
126
+
127
+ // Set up the tranlation to place the toolbar where we want it
128
+ let style = {...this.props.style}
129
+ // if (this.state.translateX !== DEFAULT_TRANSLATE) {
130
+ // style.transform = "translate("+this.state.translateX+"px, "+this.state.translateY+"px)";
131
+ // }
132
+ 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"
135
+ }
136
+
137
+ return (
138
+ <div
139
+ className='Toolbar'
140
+ style={style}
141
+ ref={(r) => this.toolbar = r}
142
+ >
143
+ {enableFontStyling &&
144
+ <>
145
+ <button name="Bold"
146
+ className={"Toolbar-button TB-narrow" +
147
+ (this.props.activeButtons.bold ? " TB-selected" : "")}
148
+ onClick={(e) => e.preventDefault()}
149
+ onMouseDown={(e) => e.preventDefault()}
150
+ onMouseUp={(e) => this.handleButtonClick(e, "Bold")}>
151
+ <b>B</b>
152
+ </button>
153
+
154
+ <button name="Italic"
155
+ className={"Toolbar-button TB-narrow" +
156
+ (this.props.activeButtons.italic ? " TB-selected" : "")}
157
+ onClick={(e) => e.preventDefault()}
158
+ onMouseDown={(e) => e.preventDefault()}
159
+ onMouseUp={(e) => this.handleButtonClick(e, "Italic")}>
160
+ <i>I</i>
161
+ </button>
162
+
163
+ <button name="Underline"
164
+ className={"Toolbar-button TB-narrow" +
165
+ (this.props.activeButtons.underline ? " TB-selected" : "")}
166
+ onClick={(e) => e.preventDefault()}
167
+ onMouseDown={(e) => e.preventDefault()}
168
+ onMouseUp={(e) => this.handleButtonClick(e, "Underline")}>
169
+ <u>U</u>
170
+ </button>
171
+
172
+ <button name="Superscript"
173
+ className={"Toolbar-button TB-narrow" +
174
+ (this.props.activeButtons.superscript ? " TB-selected" : "")}
175
+ onClick={(e) => e.preventDefault()}
176
+ onMouseDown={(e) => e.preventDefault()}
177
+ onMouseUp={(e) => this.handleButtonClick(e, "Superscript")}>
178
+ a<sup>x</sup>
179
+ </button>
180
+
181
+ <button name="Subscript"
182
+ className={"Toolbar-button TB-narrow TB-up4" +
183
+ (this.props.activeButtons.subscript ? " TB-selected" : "")}
184
+ onClick={(e) => e.preventDefault()}
185
+ onMouseDown={(e) => e.preventDefault()}
186
+ onMouseUp={(e) => this.handleButtonClick(e, "Subscript")}>
187
+ a<sub>x</sub>
188
+ </button>
189
+ </>}
190
+
191
+ <button name="Accents"
192
+ className={"Toolbar-button TB-narrow" +
193
+ (this.state.showAccents ? " TB-selected" : "")}
194
+ onMouseDown={(e) => e.preventDefault()}
195
+ onClick={(e) => e.preventDefault()}
196
+ onMouseUp={(e) => this.showOrHideAccentBar(e)}>
197
+ á
198
+ </button>
199
+
200
+ {enableMath &&
201
+ <button name="Equation"
202
+ className="Toolbar-button TB-wide" onMouseDown={(e) => this.handleButtonClick(e, "Equation")}>
203
+ <div dangerouslySetInnerHTML={{__html: katexHTML}} />
204
+ <span className="tooltiptext">Insert equation [{CTRL_OR_META}Enter]</span>
205
+ </button>
206
+ }
207
+
208
+ {this.state.showAccents &&
209
+ <AccentBar
210
+ accentLetter={this.accentLetter}
211
+ keyPressed={this.props.keyPressed}
212
+ fetchAccentLetter={this.props.fetchAccentLetter}
213
+ onButtonClick={(e, oldCharacter, newCharacter) =>
214
+ this.handleAccentButtonClick(e, oldCharacter, newCharacter)}/>
215
+ }
216
+ </div>
217
+ )
218
+ }
219
+ }