@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.
@@ -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
  });
@@ -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
- // `--pie-white` inverts with the scheme where `common.white` did not, so the
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: color.backgroundDark(),
16
+ backgroundColor: theme.palette.grey[200],
21
17
  },
22
18
  }),
23
19
  '& .MuiIconButton-label': {
@@ -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
- this.input.mathField.__controller.cursor.insLeftOf(this.input.mathField.__controller.cursor.parent[-1].parent);
344
- this.input.mathField.el().dispatchEvent(new KeyboardEvent('keydown', { keyCode: 8 }));
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 mathquill instance's latex is different
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
- const inputIsDifferent = this.input.mathField.latex() !== nextProps.latex;
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)) {
@@ -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
- border: `solid 1px ${theme.palette.primary.main}`,
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.