@pie-lib/math-toolbar 2.0.0-beta.2 → 2.0.0-next.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/src/index.jsx CHANGED
@@ -1,13 +1,19 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import cx from 'classnames';
4
3
  import EditorAndPad from './editor-and-pad';
5
4
  import { DoneButton } from './done-button';
6
- import { withStyles } from '@material-ui/core/styles';
5
+ import { styled } from '@mui/material/styles';
7
6
  import MathPreview from './math-preview';
8
7
 
9
8
  export { MathPreview };
10
9
 
10
+ const PureToolbarContainer = styled('div')({
11
+ display: 'flex',
12
+ width: '100%',
13
+ zIndex: 8,
14
+ alignItems: 'center',
15
+ });
16
+
11
17
  export class MathToolbar extends React.Component {
12
18
  static propTypes = {
13
19
  autoFocus: PropTypes.bool,
@@ -16,6 +22,8 @@ export class MathToolbar extends React.Component {
16
22
  controlledKeypadMode: PropTypes.bool,
17
23
  keypadMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
18
24
  classNames: PropTypes.object,
25
+ error: PropTypes.string,
26
+ maxResponseAreas: PropTypes.number,
19
27
  showKeypad: PropTypes.bool,
20
28
  noDecimal: PropTypes.bool,
21
29
  additionalKeys: PropTypes.array,
@@ -25,7 +33,9 @@ export class MathToolbar extends React.Component {
25
33
  onDone: PropTypes.func.isRequired,
26
34
  onFocus: PropTypes.func,
27
35
  onBlur: PropTypes.func,
28
- hideDoneButton: PropTypes.bool
36
+ hideDoneButton: PropTypes.bool,
37
+ keyPadCharacterRef: PropTypes.func,
38
+ setKeypadInteraction: PropTypes.func,
29
39
  };
30
40
 
31
41
  static defaultProps = {
@@ -41,13 +51,13 @@ export class MathToolbar extends React.Component {
41
51
  onChange: () => {},
42
52
  onAnswerBlockAdd: () => {},
43
53
  onFocus: () => {},
44
- hideDoneButton: false
54
+ hideDoneButton: false,
45
55
  };
46
56
 
47
57
  constructor(props) {
48
58
  super(props);
49
59
  this.state = {
50
- latex: props.latex
60
+ latex: props.latex,
51
61
  };
52
62
  }
53
63
 
@@ -59,7 +69,7 @@ export class MathToolbar extends React.Component {
59
69
  this.setState({ latex: nextProps.latex });
60
70
  }
61
71
 
62
- onChange = latex => {
72
+ onChange = (latex) => {
63
73
  this.setState({ latex });
64
74
  this.props.onChange(latex);
65
75
  };
@@ -81,7 +91,9 @@ export class MathToolbar extends React.Component {
81
91
  onBlur,
82
92
  hideDoneButton,
83
93
  error,
84
- maxResponseAreas
94
+ keyPadCharacterRef,
95
+ setKeypadInteraction,
96
+ maxResponseAreas,
85
97
  } = this.props;
86
98
 
87
99
  return (
@@ -94,6 +106,8 @@ export class MathToolbar extends React.Component {
94
106
  additionalKeys={additionalKeys}
95
107
  noDecimal={noDecimal}
96
108
  keypadMode={keypadMode}
109
+ keyPadCharacterRef={keyPadCharacterRef}
110
+ setKeypadInteraction={setKeypadInteraction}
97
111
  onChange={this.onChange}
98
112
  onDone={this.done}
99
113
  onFocus={onFocus}
@@ -112,7 +126,7 @@ export class MathToolbar extends React.Component {
112
126
  export class RawPureToolbar extends React.Component {
113
127
  static propTypes = {
114
128
  classNames: PropTypes.object,
115
- latex: PropTypes.string,
129
+ latex: PropTypes.string.isRequired,
116
130
  keypadMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
117
131
  hideInput: PropTypes.bool,
118
132
  noLatexHandling: PropTypes.bool,
@@ -123,14 +137,23 @@ export class RawPureToolbar extends React.Component {
123
137
  onAnswerBlockAdd: PropTypes.func,
124
138
  additionalKeys: PropTypes.array,
125
139
  onFocus: PropTypes.func,
126
- classes: PropTypes.object.isRequired,
127
140
  autoFocus: PropTypes.bool,
128
141
  noDecimal: PropTypes.bool,
129
142
  allowAnswerBlock: PropTypes.bool,
130
143
  controlledKeypad: PropTypes.bool,
131
144
  controlledKeypadMode: PropTypes.bool,
132
145
  showKeypad: PropTypes.bool,
133
- hideDoneButton: PropTypes.bool
146
+ hideDoneButton: PropTypes.bool,
147
+ hideDoneButtonBackground: PropTypes.bool,
148
+ error: PropTypes.any,
149
+ maxResponseAreas: PropTypes.number,
150
+ keyPadCharacterRef: PropTypes.object,
151
+ setKeypadInteraction: PropTypes.func,
152
+ };
153
+
154
+ static defaultProps = {
155
+ classNames: {},
156
+ hideDoneButtonBackground: false,
134
157
  };
135
158
 
136
159
  render() {
@@ -148,19 +171,21 @@ export class RawPureToolbar extends React.Component {
148
171
  hideInput,
149
172
  noLatexHandling,
150
173
  layoutForKeyPad,
174
+ keyPadCharacterRef,
175
+ setKeypadInteraction,
151
176
  latex,
152
177
  onChange,
153
178
  onDone,
154
179
  onFocus,
155
180
  onBlur,
156
181
  hideDoneButton,
157
- classes,
182
+ hideDoneButtonBackground,
158
183
  error,
159
- maxResponseAreas
184
+ maxResponseAreas,
160
185
  } = this.props;
161
186
 
162
187
  return (
163
- <div className={cx(classes.pureToolbar, (classNames || {}).toolbar)} contentEditable={false}>
188
+ <PureToolbarContainer className={(classNames && classNames.toolbar) || ''} ref={keyPadCharacterRef}>
164
189
  <div />
165
190
  <EditorAndPad
166
191
  autoFocus={autoFocus}
@@ -182,21 +207,14 @@ export class RawPureToolbar extends React.Component {
182
207
  onBlur={onBlur}
183
208
  error={error}
184
209
  maxResponseAreas={maxResponseAreas}
210
+ setKeypadInteraction={setKeypadInteraction}
185
211
  />
186
212
  {(!controlledKeypad || (controlledKeypad && showKeypad)) && !hideDoneButton && (
187
- <DoneButton onClick={onDone} />
213
+ <DoneButton hideBackground={hideDoneButtonBackground} onClick={onDone} />
188
214
  )}
189
- </div>
215
+ </PureToolbarContainer>
190
216
  );
191
217
  }
192
218
  }
193
- const styles = () => ({
194
- pureToolbar: {
195
- display: 'flex',
196
- width: '100%',
197
- zIndex: 8,
198
- alignItems: 'center'
199
- }
200
- });
201
219
 
202
- export const PureToolbar = withStyles(styles)(RawPureToolbar);
220
+ export const PureToolbar = RawPureToolbar;
@@ -1,207 +1,164 @@
1
1
  import React from 'react';
2
- import classNames from 'classnames';
3
- import get from 'lodash/get';
4
2
  import debug from 'debug';
5
- import { makeStyles } from '@material-ui/styles';
3
+ import { styled } from '@mui/material/styles';
6
4
  import PropTypes from 'prop-types';
7
5
  import { mq } from '@pie-lib/math-input';
6
+ import { markFractionBaseSuperscripts } from './utils';
7
+
8
+ const { commonMqFontStyles, longdivStyles, supsubStyles } = mq.CommonMqStyles;
8
9
 
9
10
  const log = debug('@pie-lib:math-toolbar:math-preview');
10
11
 
11
- const useStyles = makeStyles(theme => ({
12
- root: {
13
- display: 'inline-flex',
14
- alignItems: 'center',
15
- position: 'relative',
12
+ const MathPreviewContainer = styled('div')(({ theme, isSelected }) => ({
13
+ display: 'inline-flex',
14
+ alignItems: 'center',
15
+ position: 'relative',
16
+ '& *': commonMqFontStyles,
17
+ ...supsubStyles,
18
+ ...longdivStyles,
19
+ '& > .mq-math-mode': {
20
+ border: isSelected ? 'solid 0px lightgrey' : 'solid 1px lightgrey',
21
+ },
22
+ '& > .mq-focused': {
23
+ outline: 'none',
24
+ boxShadow: 'none',
25
+ border: 'solid 1px black',
26
+ borderRadius: '0px',
27
+ },
28
+ '& > .mq-math-mode .mq-root-block': {
29
+ paddingTop: '7px !important',
30
+ },
31
+ '& > .mq-math-mode .mq-overarc ': {
32
+ paddingTop: '0.45em !important',
33
+ },
34
+ '& > .mq-math-mode .mq-sqrt-prefix': {
35
+ verticalAlign: 'baseline !important',
36
+ top: '1px !important',
37
+ left: '-0.1em !important',
38
+ },
39
+ '& > .mq-math-mode .mq-denominator': {
40
+ marginTop: '-5px !important',
41
+ padding: '0.5em 0.1em 0.1em !important',
42
+ },
43
+ '& > .mq-math-mode .mq-numerator, .mq-math-mode .mq-over': {
44
+ padding: '0 0.1em !important',
45
+ paddingBottom: '0 !important',
46
+ marginBottom: '-2px',
47
+ },
48
+ '& > .mq-math-mode .mq-longdiv .mq-longdiv-inner .mq-empty': {
49
+ paddingTop: '6px !important',
50
+ paddingLeft: '4px !important',
51
+ },
52
+ '& > .mq-math-mode .mq-longdiv .mq-longdiv-inner': {
53
+ marginLeft: '0 !important',
54
+ },
55
+ '& > .mq-math-mode .mq-overarrow': {
56
+ fontFamily: 'Roboto, Helvetica, Arial, sans-serif !important',
57
+ },
58
+ '& > .mq-math-mode .mq-paren': {
59
+ verticalAlign: 'top !important',
60
+ padding: '1px 0.1em !important',
61
+ },
62
+ '& > .mq-math-mode .mq-sqrt-stem': {
63
+ borderTop: '0.07em solid',
64
+ marginLeft: '-1.5px',
65
+ marginTop: '-2px !important',
66
+ paddingTop: '5px !important',
67
+ },
68
+ '& .mq-overarrow-inner': {
69
+ paddingTop: '0 !important',
70
+ border: 'none !important',
71
+ },
72
+ '& .mq-editable-field .mq-cursor': {
73
+ marginTop: '-15px !important',
74
+ },
75
+ '& .mq-overarrow.mq-arrow-both': {
76
+ top: '7.5px',
77
+ marginTop: '0px',
78
+ minWidth: '1.23em',
16
79
  '& *': {
17
- fontFamily: 'MJXZERO, MJXTEX !important',
18
- '-webkit-font-smoothing': 'antialiased !important'
80
+ lineHeight: '1 !important',
19
81
  },
20
- '& > .mq-math-mode > span > var': {
21
- fontFamily: 'MJXZERO, MJXTEX-I !important'
82
+ '&:before': {
83
+ top: '-0.4em',
84
+ left: '-1px',
22
85
  },
23
- '& > .mq-math-mode span var': {
24
- fontFamily: 'MJXZERO, MJXTEX-I !important'
86
+ '&:after': {
87
+ top: '0px !important',
88
+ position: 'absolute !important',
89
+ right: '-2px',
25
90
  },
26
- '& > .mq-math-mode .mq-nonSymbola': {
27
- fontFamily: 'MJXZERO, MJXTEX-I !important'
91
+ '&.mq-empty:after': {
92
+ top: '-0.45em',
28
93
  },
29
- '& > .mq-math-mode > span > var.mq-operator-name': {
30
- fontFamily: 'MJXZERO, MJXTEX !important'
31
- },
32
- '& > .mq-math-mode': {
33
- border: 'solid 1px lightgrey'
34
- },
35
- '& > .mq-focused': {
36
- outline: 'none',
37
- boxShadow: 'none',
38
- border: 'solid 1px black',
39
- borderRadius: '0px'
40
- },
41
- '& > .mq-math-mode .mq-root-block': {
42
- paddingTop: '7px !important'
43
- },
44
- '& > .mq-math-mode .mq-overarc ': {
45
- paddingTop: '0.45em !important'
46
- },
47
- '& > .mq-math-mode .mq-sqrt-prefix': {
48
- verticalAlign: 'bottom !important',
49
- top: '0 !important',
50
- left: '-0.1em !important'
51
- },
52
- '& > .mq-math-mode .mq-denominator': {
53
- marginTop: '-5px !important',
54
- padding: '0.5em 0.1em 0.1em !important'
55
- },
56
- '& > .mq-math-mode .mq-numerator, .mq-math-mode .mq-over': {
57
- padding: '0 0.1em !important',
58
- paddingBottom: '0 !important',
59
- marginBottom: '4.5px'
60
- },
61
- '& > .mq-math-mode sup.mq-nthroot': {
62
- fontSize: '70.7% !important',
63
- verticalAlign: '0.5em !important',
64
- paddingRight: '0.15em'
94
+ },
95
+ '& .mq-overarrow.mq-arrow-right': {
96
+ '&:before': {
97
+ top: '-0.4em',
98
+ right: '-1px',
65
99
  },
66
- '& > .mq-longdiv-inner': {
67
- marginTop: '-1px',
68
- marginLeft: '5px !important;',
100
+ },
101
+ '& .mq-overarrow-inner-right': {
102
+ display: 'none !important',
103
+ },
104
+ '& .mq-overarrow-inner-left': {
105
+ display: 'none !important',
106
+ },
107
+ '& .mq-longdiv-inner': {
108
+ borderTop: '1px solid !important',
109
+ paddingTop: '1.5px !important',
110
+ },
111
+ '& .mq-parallelogram': {
112
+ lineHeight: 0.85,
113
+ },
114
+ '& span[data-prime="true"]': {
115
+ fontFamily: 'Roboto, Helvetica, Arial, sans-serif !important',
116
+ },
117
+ ...(isSelected && {
118
+ border: `solid 1px ${theme.palette.primary.main}`,
119
+ }),
120
+ }));
69
121
 
70
- '& > .mq-empty': {
71
- padding: '0 !important',
72
- marginLeft: '0px !important',
73
- marginTop: '2px'
74
- }
75
- },
76
- '& > .mq-math-mode .mq-longdiv': {
77
- display: 'inline-flex !important'
78
- },
79
- '& > .mq-math-mode .mq-longdiv .mq-longdiv-inner .mq-empty': {
80
- paddingTop: '6px !important',
81
- paddingLeft: '4px !important'
82
- },
83
- '& > .mq-math-mode .mq-longdiv .mq-longdiv-inner': {
84
- marginLeft: '0 !important'
85
- },
86
- '& > .mq-math-mode .mq-supsub': {
87
- fontSize: '70.7% !important'
88
- },
89
- '& > .mq-math-mode .mq-overarrow': {
90
- fontFamily: 'Roboto, Helvetica, Arial, sans-serif !important'
91
- },
92
- '& > .mq-math-mode .mq-paren': {
93
- verticalAlign: 'top !important',
94
- padding: '1px 0.1em !important'
95
- },
122
+ const InsideOverlay = styled('span')({
123
+ position: 'absolute',
124
+ bottom: 0,
125
+ left: 0,
126
+ right: 0,
127
+ top: 0,
128
+ });
96
129
 
97
- '& > .mq-math-mode .mq-sqrt-stem': {
98
- borderTop: '0.07em solid',
99
- marginLeft: '-1.5px',
100
- marginTop: '-2px !important',
101
- paddingTop: '5px !important'
102
- },
103
- '& > .mq-supsub ': {
104
- fontSize: '70.7%'
105
- },
130
+ export class RawMathPreview extends React.Component {
131
+ static propTypes = {
132
+ latex: PropTypes.string,
133
+ node: PropTypes.object,
134
+ isSelected: PropTypes.bool,
135
+ onFocus: PropTypes.func,
136
+ onBlur: PropTypes.func,
137
+ };
106
138
 
107
- '& > .mq-math-mode .mq-supsub.mq-sup-only': {
108
- verticalAlign: '-0.1em !important',
139
+ componentDidMount() {
140
+ markFractionBaseSuperscripts();
141
+ }
109
142
 
110
- '& .mq-sup': {
111
- marginBottom: '0px !important'
112
- }
113
- },
114
- '& .mq-overarrow-inner': {
115
- paddingTop: '0 !important',
116
- border: 'none !important'
117
- },
118
- '& .mq-editable-field .mq-cursor': {
119
- marginTop: '-15px !important'
120
- },
121
- '& .mq-overarrow.mq-arrow-both': {
122
- top: '7.5px',
123
- marginTop: '0px',
124
- minWidth: '1.23em',
125
- '& *': {
126
- lineHeight: '1 !important'
127
- },
128
- '&:before': {
129
- top: '-0.4em',
130
- left: '-1px'
131
- },
132
- '&:after': {
133
- top: '0px !important',
134
- position: 'absolute',
135
- right: '-2px'
136
- },
137
- '&.mq-empty:after': {
138
- top: '-0.45em'
139
- }
140
- },
141
- '& .mq-overarrow.mq-arrow-right': {
142
- '&:before': {
143
- top: '-0.4em',
144
- right: '-1px'
145
- }
146
- },
147
- '& .mq-overarrow-inner-right': {
148
- display: 'none !important'
149
- },
150
- '& .mq-overarrow-inner-left': {
151
- display: 'none !important'
152
- },
153
- '& .mq-longdiv-inner': {
154
- borderTop: '1px solid !important',
155
- paddingTop: '1.5px !important'
156
- },
157
- '& .mq-parallelogram': {
158
- lineHeight: 0.85
159
- }
160
- },
161
- selected: {
162
- border: `solid 1px ${get(theme, 'palette.primary.main')}`,
163
- '& > .mq-math-mode': {
164
- border: 'solid 0px lightgrey'
143
+ componentDidUpdate(prevProps) {
144
+ // Re-run only if LaTeX changed
145
+ if (this.props.node.data.get('latex') !== prevProps.node.data.get('latex')) {
146
+ markFractionBaseSuperscripts();
165
147
  }
166
- },
167
- insideOverlay: {
168
- position: 'absolute',
169
- bottom: 0,
170
- left: 0,
171
- right: 0,
172
- top: 0
173
148
  }
174
- }));
175
-
176
- export const RawMathPreview = React.forwardRef((props, ref) => {
177
- log('[render] data: ', props.element.data);
178
- const latex = props.element.data.latex;
179
- const { isSelected, onFocus, onBlur, attributes, children } = props;
180
- const classes = useStyles(props);
181
149
 
182
- return (
183
- <div
184
- className={classNames(classes.root, isSelected && classes.selected)}
185
- {...attributes}
186
- contentEditable={false}
187
- ref={ref}
188
- >
189
- {children}
190
- {' '}
191
- <span className={classes.insideOverlay} />
192
- <mq.Static latex={latex} onFocus={onFocus} onBlur={onBlur} />
193
- </div>
194
- );
195
- });
196
-
197
- RawMathPreview.propTypes = {
198
- element: PropTypes.object,
199
- latex: PropTypes.string,
200
- node: PropTypes.object,
201
- classes: PropTypes.object,
202
- isSelected: PropTypes.bool,
203
- onFocus: PropTypes.func,
204
- onBlur: PropTypes.func,
205
- };
150
+ render() {
151
+ log('[render] data: ', this.props.node.data);
152
+ const latex = this.props.node.data.get('latex');
153
+ const { isSelected, onFocus, onBlur } = this.props;
154
+ return (
155
+ <MathPreviewContainer isSelected={isSelected}>
156
+ {' '}
157
+ <InsideOverlay />
158
+ <mq.Static latex={latex} onFocus={onFocus} onBlur={onBlur} />
159
+ </MathPreviewContainer>
160
+ );
161
+ }
162
+ }
206
163
 
207
164
  export default RawMathPreview;
package/src/utils.js ADDED
@@ -0,0 +1,11 @@
1
+ export const markFractionBaseSuperscripts = () => {
2
+ document.querySelectorAll('.mq-supsub.mq-sup-only').forEach((supsub) => {
3
+ const prev = supsub.previousElementSibling;
4
+
5
+ if (prev && prev.classList.contains('mq-non-leaf') && prev.querySelector('.mq-fraction')) {
6
+ supsub.classList.add('mq-after-fraction-group');
7
+ } else {
8
+ supsub.classList.remove('mq-after-fraction-group');
9
+ }
10
+ });
11
+ };