@pie-lib/math-input-mathlive 1.0.9-next.6637 → 1.0.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.
Files changed (51) hide show
  1. package/README.md +14 -5
  2. package/lib/__tests__/composites.test.js +255 -0
  3. package/lib/__tests__/composites.test.js.map +1 -0
  4. package/lib/__tests__/drop-in-compat.test.js +133 -0
  5. package/lib/__tests__/drop-in-compat.test.js.map +1 -0
  6. package/lib/__tests__/index.test.js +118 -0
  7. package/lib/__tests__/index.test.js.map +1 -0
  8. package/lib/__tests__/input-imperative.test.js +155 -0
  9. package/lib/__tests__/input-imperative.test.js.map +1 -0
  10. package/lib/__tests__/keypad-label.test.js +118 -0
  11. package/lib/__tests__/keypad-label.test.js.map +1 -0
  12. package/lib/__tests__/keypad-render.test.js +206 -0
  13. package/lib/__tests__/keypad-render.test.js.map +1 -0
  14. package/lib/__tests__/latex-bridge.test.js +280 -0
  15. package/lib/__tests__/latex-bridge.test.js.map +1 -0
  16. package/lib/__tests__/mathlive-instance.test.js +279 -0
  17. package/lib/__tests__/mathlive-instance.test.js.map +1 -0
  18. package/lib/__tests__/mount-race.test.js +195 -0
  19. package/lib/__tests__/mount-race.test.js.map +1 -0
  20. package/lib/__tests__/static-prompts.test.js +282 -0
  21. package/lib/__tests__/static-prompts.test.js.map +1 -0
  22. package/lib/keypad/index.js +11 -13
  23. package/lib/keypad/index.js.map +1 -1
  24. package/lib/keys/geometry.js +7 -4
  25. package/lib/keys/geometry.js.map +1 -1
  26. package/lib/latex-bridge.js +18 -3
  27. package/lib/latex-bridge.js.map +1 -1
  28. package/lib/mathlive-instance.js +103 -11
  29. package/lib/mathlive-instance.js.map +1 -1
  30. package/lib/mf/common-styles.js +33 -4
  31. package/lib/mf/common-styles.js.map +1 -1
  32. package/lib/mf/input.js +22 -5
  33. package/lib/mf/input.js.map +1 -1
  34. package/lib/mf/static.js +21 -4
  35. package/lib/mf/static.js.map +1 -1
  36. package/package.json +3 -4
  37. package/src/__tests__/composites.test.jsx +220 -0
  38. package/src/__tests__/keypad-label.test.jsx +81 -14
  39. package/src/__tests__/keypad-render.test.jsx +123 -0
  40. package/src/__tests__/latex-bridge.test.js +21 -0
  41. package/src/__tests__/mathlive-instance.test.js +87 -0
  42. package/src/__tests__/mount-race.test.jsx +130 -0
  43. package/src/__tests__/static-prompts.test.jsx +251 -0
  44. package/src/keypad/index.jsx +11 -13
  45. package/src/keys/geometry.js +7 -2
  46. package/src/latex-bridge.js +18 -3
  47. package/src/mathlive-instance.js +111 -11
  48. package/src/mf/common-styles.js +33 -4
  49. package/src/mf/input.jsx +23 -5
  50. package/src/mf/static.jsx +24 -4
  51. package/LICENSE.md +0 -5
package/src/mf/input.jsx CHANGED
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { styled } from '@mui/material/styles';
4
4
  import debug from 'debug';
5
- import { loadMathLive, getMacros } from '../mathlive-instance';
5
+ import { loadMathLive, getMacros, MATH_MODE_SPACE, applyShadowStyles } from '../mathlive-instance';
6
6
  import { toMathLive, fromMathLive, keyToAction, KEYSTROKES } from '../latex-bridge';
7
7
 
8
8
  const log = debug('pie-lib:math-input-mathlive:input');
@@ -49,9 +49,16 @@ export class Input extends React.Component {
49
49
  }
50
50
 
51
51
  async componentDidMount() {
52
+ // MathLive loads asynchronously, so this method resumes after an await and
53
+ // has to know whether it is still the current mount. React StrictMode calls
54
+ // mount -> unmount -> mount on the SAME instance in dev, so a boolean
55
+ // "unmounted" flag would latch and block the remount from ever creating a
56
+ // field. A generation counter identifies the live mount instead: only the
57
+ // newest one proceeds, and a real unmount invalidates all pending ones.
58
+ const generation = (this.mountGeneration = (this.mountGeneration || 0) + 1);
52
59
  const ml = await loadMathLive();
53
60
 
54
- if (!ml || !this.holderRef.current) {
61
+ if (generation !== this.mountGeneration || !ml || !this.holderRef.current || this.mathField) {
55
62
  return;
56
63
  }
57
64
 
@@ -64,15 +71,22 @@ export class Input extends React.Component {
64
71
 
65
72
  // pie drives input through its own keypad, so suppress MathLive's.
66
73
  this.mathField.mathVirtualKeyboardPolicy = 'manual';
74
+ // Without this the spacebar does nothing: MathLive's mathModeSpace
75
+ // defaults to an empty string.
76
+ this.mathField.mathModeSpace = MATH_MODE_SPACE;
77
+
78
+ // The mathfield renders in shadow DOM, so page CSS cannot reach it - the
79
+ // stretchy-accent fix has to be injected there directly.
80
+ applyShadowStyles(this.mathField);
67
81
 
68
82
  this.mathField.addEventListener('input', this.onInputEdit);
69
- this.holderRef.current.appendChild(this.mathField);
83
+
84
+ // Belt and braces: drop anything a previous mount may have left behind.
85
+ this.holderRef.current.replaceChildren(this.mathField);
70
86
 
71
87
  if (this.props.innerRef) {
72
88
  this.props.innerRef(this);
73
89
  }
74
-
75
- this.setState?.({});
76
90
  }
77
91
 
78
92
  componentDidUpdate(prevProps) {
@@ -99,6 +113,10 @@ export class Input extends React.Component {
99
113
  }
100
114
 
101
115
  componentWillUnmount() {
116
+ // Invalidate any mount still awaiting the MathLive load, so it does not
117
+ // append a field into a holder that is going away.
118
+ this.mountGeneration = (this.mountGeneration || 0) + 1;
119
+
102
120
  if (this.mathField) {
103
121
  this.mathField.removeEventListener('input', this.onInputEdit);
104
122
  this.mathField.remove();
package/src/mf/static.jsx CHANGED
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { styled } from '@mui/material/styles';
4
4
  import debug from 'debug';
5
- import { loadMathLive, getMacros, latexToMarkup } from '../mathlive-instance';
5
+ import { loadMathLive, getMacros, latexToMarkup, MATH_MODE_SPACE, applyShadowStyles } from '../mathlive-instance';
6
6
  import { toMathLive, fromMathLive, fieldIds } from '../latex-bridge';
7
7
  import { placeholderStyles } from './common-styles';
8
8
 
@@ -64,28 +64,45 @@ export default class Static extends React.Component {
64
64
  }
65
65
 
66
66
  async componentDidMount() {
67
+ // See the note in mf/input.jsx: a generation counter, not a boolean, so a
68
+ // StrictMode mount -> unmount -> mount on the same instance still ends up
69
+ // with exactly one field.
70
+ const generation = (this.mountGeneration = (this.mountGeneration || 0) + 1);
71
+
67
72
  if (!this.isInteractive()) {
68
73
  await loadMathLive();
69
- this.renderMarkup();
74
+
75
+ if (generation === this.mountGeneration) {
76
+ this.renderMarkup();
77
+ }
78
+
70
79
  return;
71
80
  }
72
81
 
73
82
  const ml = await loadMathLive();
74
83
 
75
- if (!ml || !this.holderRef.current) {
84
+ if (generation !== this.mountGeneration || !ml || !this.holderRef.current || this.mathField) {
76
85
  return;
77
86
  }
78
87
 
79
88
  this.mathField = new ml.MathfieldElement({ macros: getMacros() });
80
89
  this.mathField.readOnly = true;
81
90
  this.mathField.mathVirtualKeyboardPolicy = 'manual';
91
+ // Without this the spacebar does nothing: MathLive's mathModeSpace
92
+ // defaults to an empty string.
93
+ this.mathField.mathModeSpace = MATH_MODE_SPACE;
82
94
  this.mathField.value = toMathLive(this.props.latex);
83
95
 
96
+ // The mathfield renders in shadow DOM, so page CSS cannot reach it - the
97
+ // stretchy-accent fix has to be injected there directly.
98
+ applyShadowStyles(this.mathField);
99
+
84
100
  this.mathField.addEventListener('input', this.onPromptInput);
85
101
  this.mathField.addEventListener('focusin', this.onPromptFocus);
86
102
  this.mathField.addEventListener('keydown', this.onKeyDown);
87
103
 
88
- this.holderRef.current.appendChild(this.mathField);
104
+ // Belt and braces: drop anything a previous mount may have left behind.
105
+ this.holderRef.current.replaceChildren(this.mathField);
89
106
 
90
107
  this.createLiveRegion();
91
108
  }
@@ -103,6 +120,9 @@ export default class Static extends React.Component {
103
120
  }
104
121
 
105
122
  componentWillUnmount() {
123
+ // Invalidate any mount still awaiting the MathLive load.
124
+ this.mountGeneration = (this.mountGeneration || 0) + 1;
125
+
106
126
  if (this.mathField) {
107
127
  this.mathField.removeEventListener('input', this.onPromptInput);
108
128
  this.mathField.removeEventListener('focusin', this.onPromptFocus);
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.