@pie-lib/math-toolbar 3.0.10 → 3.0.11-beta.10
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 +0 -38
- package/lib/__tests__/editor-and-pad-mathlive.test.js +272 -0
- package/lib/__tests__/editor-and-pad.test.js +78 -0
- package/lib/__tests__/index.test.js +27 -0
- package/lib/__tests__/math-preview.test.js +91 -0
- package/lib/done-button.js +4 -7
- package/lib/done-button.js.map +1 -1
- package/lib/editor-and-pad.js +15 -11
- package/lib/editor-and-pad.js.map +1 -1
- package/lib/math-preview.js +21 -7
- package/lib/math-preview.js.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/editor-and-pad-mathlive.test.js +178 -0
- package/src/__tests__/math-preview.test.js +65 -0
- package/src/done-button.jsx +3 -7
- package/src/editor-and-pad.jsx +11 -5
- package/src/math-preview.jsx +19 -7
- package/LICENSE.md +0 -5
|
@@ -2,6 +2,45 @@ import React from 'react';
|
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
3
|
import MathPreview from '../math-preview';
|
|
4
4
|
|
|
5
|
+
/** Every rule emotion has injected so far, as CSSStyleRule objects. */
|
|
6
|
+
const injectedRules = () =>
|
|
7
|
+
Array.from(document.querySelectorAll('style[data-emotion]'))
|
|
8
|
+
.flatMap((tag) => (tag.sheet ? Array.from(tag.sheet.cssRules) : []))
|
|
9
|
+
.filter((rule) => rule.selectorText);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The `.ML__latex` rule that actually applies to `element`.
|
|
13
|
+
*
|
|
14
|
+
* Matching with `element.matches` rather than string comparison means the
|
|
15
|
+
* assertions cover the selector too: a rule written as `& > .ML__latex` would
|
|
16
|
+
* not match the nested markup MathLive emits, and these tests would fail.
|
|
17
|
+
*/
|
|
18
|
+
const mathLiveRuleFor = (element) =>
|
|
19
|
+
injectedRules().find((rule) => rule.selectorText.includes('.ML__latex') && element.matches(rule.selectorText));
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Stand in for MathLive's `convertLatexToMarkup` output, which is
|
|
23
|
+
* `<span class="ML__latex">...</span>` nested inside `mf.Static`'s holder span.
|
|
24
|
+
* MathLive does not load under jsdom, so the markup is added by hand.
|
|
25
|
+
*/
|
|
26
|
+
const addStaticMarkup = (container, depth = 1) => {
|
|
27
|
+
let parent = container.firstChild;
|
|
28
|
+
|
|
29
|
+
for (let i = 1; i < depth; i += 1) {
|
|
30
|
+
const wrapper = document.createElement('span');
|
|
31
|
+
|
|
32
|
+
parent.appendChild(wrapper);
|
|
33
|
+
parent = wrapper;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const markup = document.createElement('span');
|
|
37
|
+
|
|
38
|
+
markup.className = 'ML__latex';
|
|
39
|
+
parent.appendChild(markup);
|
|
40
|
+
|
|
41
|
+
return markup;
|
|
42
|
+
};
|
|
43
|
+
|
|
5
44
|
describe('MathPreview', () => {
|
|
6
45
|
const defaultProps = {
|
|
7
46
|
latex: 'sqrt(5)',
|
|
@@ -15,4 +54,30 @@ describe('MathPreview', () => {
|
|
|
15
54
|
const { container } = render(<MathPreview {...defaultProps} />);
|
|
16
55
|
expect(container.firstChild).toBeInTheDocument();
|
|
17
56
|
});
|
|
57
|
+
|
|
58
|
+
describe('MathLive static markup', () => {
|
|
59
|
+
it('is outlined and padded when not selected', () => {
|
|
60
|
+
const { container } = render(<MathPreview {...defaultProps} isSelected={false} />);
|
|
61
|
+
const rule = mathLiveRuleFor(addStaticMarkup(container));
|
|
62
|
+
|
|
63
|
+
expect(rule).toBeDefined();
|
|
64
|
+
expect(rule.style.getPropertyValue('border')).toEqual('solid 1px lightgrey');
|
|
65
|
+
expect(rule.style.getPropertyValue('padding')).toEqual('5px');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('drops its outline and padding when selected, so only the container border shows', () => {
|
|
69
|
+
const { container } = render(<MathPreview {...defaultProps} isSelected />);
|
|
70
|
+
const rule = mathLiveRuleFor(addStaticMarkup(container));
|
|
71
|
+
|
|
72
|
+
expect(rule).toBeDefined();
|
|
73
|
+
expect(rule.style.getPropertyValue('border')).toEqual('solid 0px lightgrey');
|
|
74
|
+
expect(rule.style.getPropertyValue('padding')).toEqual('0px');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('is styled at any depth below the container, not just as a direct child', () => {
|
|
78
|
+
const { container } = render(<MathPreview {...defaultProps} isSelected={false} />);
|
|
79
|
+
|
|
80
|
+
expect(mathLiveRuleFor(addStaticMarkup(container, 3))).toBeDefined();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
18
83
|
});
|
package/src/done-button.jsx
CHANGED
|
@@ -4,20 +4,16 @@ import IconButton from '@mui/material/IconButton';
|
|
|
4
4
|
import Check from '@mui/icons-material/Check';
|
|
5
5
|
import { styled } from '@mui/material/styles';
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
|
-
import { color } from '@pie-lib/render-ui';
|
|
8
7
|
|
|
9
|
-
const StyledIconButton = styled(IconButton)(({ hideBackground }) => ({
|
|
8
|
+
const StyledIconButton = styled(IconButton)(({ theme, hideBackground }) => ({
|
|
10
9
|
verticalAlign: 'top',
|
|
11
10
|
width: '28px',
|
|
12
11
|
height: '28px',
|
|
13
12
|
color: '#00bb00',
|
|
14
13
|
...(hideBackground && {
|
|
15
|
-
|
|
16
|
-
// button keeps reading as raised against the toolbar instead of staying a
|
|
17
|
-
// white chip on a dark surface.
|
|
18
|
-
backgroundColor: color.white(),
|
|
14
|
+
backgroundColor: theme.palette.common.white,
|
|
19
15
|
'&:hover': {
|
|
20
|
-
backgroundColor:
|
|
16
|
+
backgroundColor: theme.palette.grey[200],
|
|
21
17
|
},
|
|
22
18
|
}),
|
|
23
19
|
'& .MuiIconButton-label': {
|
package/src/editor-and-pad.jsx
CHANGED
|
@@ -9,7 +9,7 @@ import { isEqual } from 'lodash-es';
|
|
|
9
9
|
import FormControl from '@mui/material/FormControl';
|
|
10
10
|
import InputLabel from '@mui/material/InputLabel';
|
|
11
11
|
|
|
12
|
-
import { HorizontalKeypad, mq, updateSpans } from '@pie-lib/math-input';
|
|
12
|
+
import { HorizontalKeypad, mq, updateSpans } from '@pie-lib/math-input-mathlive';
|
|
13
13
|
import { color } from '@pie-lib/render-ui';
|
|
14
14
|
import { markFractionBaseSuperscripts } from './utils';
|
|
15
15
|
|
|
@@ -340,8 +340,11 @@ export class EditorAndPad extends React.Component {
|
|
|
340
340
|
|
|
341
341
|
if (this.input && regexMatch && regexMatch?.length) {
|
|
342
342
|
try {
|
|
343
|
-
|
|
344
|
-
|
|
343
|
+
// Undo the empty fraction the editor just created after a digit.
|
|
344
|
+
// MathQuill needed cursor surgery via `__controller`; MathLive has no
|
|
345
|
+
// such internals, so use the public delete command. `keystroke` accepts
|
|
346
|
+
// the MathQuill name and maps it to the engine's selector.
|
|
347
|
+
this.input.keystroke('Backspace');
|
|
345
348
|
} catch (e) {
|
|
346
349
|
// eslint-disable-next-line no-console
|
|
347
350
|
console.error(e.toString());
|
|
@@ -353,10 +356,13 @@ export class EditorAndPad extends React.Component {
|
|
|
353
356
|
onChange(latex);
|
|
354
357
|
};
|
|
355
358
|
|
|
356
|
-
/** Only render if the
|
|
359
|
+
/** Only render if the editor's latex is different
|
|
357
360
|
* or the keypad state changed from one state to the other (shown / hidden) */
|
|
358
361
|
shouldComponentUpdate(nextProps, nextState) {
|
|
359
|
-
|
|
362
|
+
// `this.input` is set via a ref, and the underlying math engine loads
|
|
363
|
+
// asynchronously, so neither is guaranteed to exist on early renders.
|
|
364
|
+
// Treat "not ready" as different, so the first real render is not skipped.
|
|
365
|
+
const inputIsDifferent = !this.input || this.input.latex() !== nextProps.latex;
|
|
360
366
|
log('[shouldComponentUpdate] ', 'inputIsDifferent: ', inputIsDifferent);
|
|
361
367
|
|
|
362
368
|
if (!isEqual(this.props.error, nextProps.error)) {
|
package/src/math-preview.jsx
CHANGED
|
@@ -2,14 +2,14 @@ import React from 'react';
|
|
|
2
2
|
import debug from 'debug';
|
|
3
3
|
import { styled } from '@mui/material/styles';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
|
-
import { mq } from '@pie-lib/math-input';
|
|
5
|
+
import { mq } from '@pie-lib/math-input-mathlive';
|
|
6
6
|
import { markFractionBaseSuperscripts } from './utils';
|
|
7
7
|
|
|
8
8
|
const { commonMqFontStyles, longdivStyles, supsubStyles } = mq.CommonMqStyles;
|
|
9
9
|
|
|
10
10
|
const log = debug('@pie-lib:math-toolbar:math-preview');
|
|
11
11
|
|
|
12
|
-
const MathPreviewContainer = styled('div')(({ theme, isSelected }) => ({
|
|
12
|
+
const MathPreviewContainer = styled('div')(({ theme, isSelected, outlineOnWrapper }) => ({
|
|
13
13
|
display: 'inline-flex',
|
|
14
14
|
alignItems: 'center',
|
|
15
15
|
position: 'relative',
|
|
@@ -19,6 +19,16 @@ const MathPreviewContainer = styled('div')(({ theme, isSelected }) => ({
|
|
|
19
19
|
'& > .mq-math-mode': {
|
|
20
20
|
border: isSelected ? 'solid 0px lightgrey' : 'solid 1px lightgrey',
|
|
21
21
|
},
|
|
22
|
+
'& .ML__latex': {
|
|
23
|
+
border: isSelected ? 'solid 0px lightgrey' : 'solid 1px lightgrey',
|
|
24
|
+
padding: isSelected ? '0px' : '5px',
|
|
25
|
+
},
|
|
26
|
+
...(!outlineOnWrapper && {
|
|
27
|
+
'&:not(:has(.ML__latex))': {
|
|
28
|
+
border: isSelected ? 'solid 0px lightgrey' : 'solid 1px lightgrey',
|
|
29
|
+
padding: isSelected ? '0px' : '5px',
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
22
32
|
'& > .mq-focused': {
|
|
23
33
|
outline: 'none',
|
|
24
34
|
boxShadow: 'none',
|
|
@@ -114,9 +124,10 @@ const MathPreviewContainer = styled('div')(({ theme, isSelected }) => ({
|
|
|
114
124
|
'& span[data-prime="true"]': {
|
|
115
125
|
fontFamily: 'Roboto, Helvetica, Arial, sans-serif !important',
|
|
116
126
|
},
|
|
117
|
-
...(isSelected &&
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
...(isSelected &&
|
|
128
|
+
!outlineOnWrapper && {
|
|
129
|
+
border: `solid 1px ${theme.palette.primary.main}`,
|
|
130
|
+
}),
|
|
120
131
|
}));
|
|
121
132
|
|
|
122
133
|
const InsideOverlay = styled('span')({
|
|
@@ -131,6 +142,7 @@ export class RawMathPreview extends React.Component {
|
|
|
131
142
|
static propTypes = {
|
|
132
143
|
latex: PropTypes.string,
|
|
133
144
|
isSelected: PropTypes.bool,
|
|
145
|
+
outlineOnWrapper: PropTypes.bool,
|
|
134
146
|
onFocus: PropTypes.func,
|
|
135
147
|
onBlur: PropTypes.func,
|
|
136
148
|
};
|
|
@@ -147,10 +159,10 @@ export class RawMathPreview extends React.Component {
|
|
|
147
159
|
}
|
|
148
160
|
|
|
149
161
|
render() {
|
|
150
|
-
const { isSelected, onFocus, onBlur, latex } = this.props;
|
|
162
|
+
const { isSelected, outlineOnWrapper, onFocus, onBlur, latex } = this.props;
|
|
151
163
|
log('[render] latex: ', latex);
|
|
152
164
|
return (
|
|
153
|
-
<MathPreviewContainer isSelected={isSelected}>
|
|
165
|
+
<MathPreviewContainer isSelected={isSelected} outlineOnWrapper={outlineOnWrapper}>
|
|
154
166
|
{' '}
|
|
155
167
|
<InsideOverlay />
|
|
156
168
|
<mq.Static latex={latex} onFocus={onFocus} onBlur={onBlur} />
|
package/LICENSE.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
Copyright 2019 CoreSpring Inc
|
|
2
|
-
|
|
3
|
-
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
4
|
-
|
|
5
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|