@pie-lib/math-input-mathlive 1.0.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.
Files changed (110) hide show
  1. package/README.md +119 -0
  2. package/lib/__tests__/index.test.js +118 -0
  3. package/lib/__tests__/index.test.js.map +1 -0
  4. package/lib/__tests__/latex-bridge.test.js +186 -0
  5. package/lib/__tests__/latex-bridge.test.js.map +1 -0
  6. package/lib/__tests__/mathlive-instance.test.js +137 -0
  7. package/lib/__tests__/mathlive-instance.test.js.map +1 -0
  8. package/lib/horizontal-keypad.js +101 -0
  9. package/lib/horizontal-keypad.js.map +1 -0
  10. package/lib/index.js +143 -0
  11. package/lib/index.js.map +1 -0
  12. package/lib/keypad/index.js +365 -0
  13. package/lib/keypad/index.js.map +1 -0
  14. package/lib/keypad/keys-layout.js +22 -0
  15. package/lib/keypad/keys-layout.js.map +1 -0
  16. package/lib/keys/basic-operators.js +33 -0
  17. package/lib/keys/basic-operators.js.map +1 -0
  18. package/lib/keys/chars.js +12 -0
  19. package/lib/keys/chars.js.map +1 -0
  20. package/lib/keys/comparison.js +39 -0
  21. package/lib/keys/comparison.js.map +1 -0
  22. package/lib/keys/constants.js +37 -0
  23. package/lib/keys/constants.js.map +1 -0
  24. package/lib/keys/digits.js +46 -0
  25. package/lib/keys/digits.js.map +1 -0
  26. package/lib/keys/edit.js +14 -0
  27. package/lib/keys/edit.js.map +1 -0
  28. package/lib/keys/exponent.js +30 -0
  29. package/lib/keys/exponent.js.map +1 -0
  30. package/lib/keys/fractions.js +29 -0
  31. package/lib/keys/fractions.js.map +1 -0
  32. package/lib/keys/geometry.js +140 -0
  33. package/lib/keys/geometry.js.map +1 -0
  34. package/lib/keys/grades.js +259 -0
  35. package/lib/keys/grades.js.map +1 -0
  36. package/lib/keys/index.js +35 -0
  37. package/lib/keys/index.js.map +1 -0
  38. package/lib/keys/log.js +27 -0
  39. package/lib/keys/log.js.map +1 -0
  40. package/lib/keys/logic.js +19 -0
  41. package/lib/keys/logic.js.map +1 -0
  42. package/lib/keys/matrices.js +19 -0
  43. package/lib/keys/matrices.js.map +1 -0
  44. package/lib/keys/misc.js +62 -0
  45. package/lib/keys/misc.js.map +1 -0
  46. package/lib/keys/navigation.js +20 -0
  47. package/lib/keys/navigation.js.map +1 -0
  48. package/lib/keys/operators.js +15 -0
  49. package/lib/keys/operators.js.map +1 -0
  50. package/lib/keys/statistics.js +40 -0
  51. package/lib/keys/statistics.js.map +1 -0
  52. package/lib/keys/sub-sup.js +19 -0
  53. package/lib/keys/sub-sup.js.map +1 -0
  54. package/lib/keys/trigonometry.js +45 -0
  55. package/lib/keys/trigonometry.js.map +1 -0
  56. package/lib/keys/utils.js +87 -0
  57. package/lib/keys/utils.js.map +1 -0
  58. package/lib/keys/vars.js +24 -0
  59. package/lib/keys/vars.js.map +1 -0
  60. package/lib/latex-bridge.js +195 -0
  61. package/lib/latex-bridge.js.map +1 -0
  62. package/lib/math-input.js +143 -0
  63. package/lib/math-input.js.map +1 -0
  64. package/lib/mathlive-instance.js +319 -0
  65. package/lib/mathlive-instance.js.map +1 -0
  66. package/lib/mf/common-styles.js +111 -0
  67. package/lib/mf/common-styles.js.map +1 -0
  68. package/lib/mf/index.js +72 -0
  69. package/lib/mf/index.js.map +1 -0
  70. package/lib/mf/input.js +266 -0
  71. package/lib/mf/input.js.map +1 -0
  72. package/lib/mf/static.js +396 -0
  73. package/lib/mf/static.js.map +1 -0
  74. package/package.json +31 -0
  75. package/src/__tests__/index.test.jsx +109 -0
  76. package/src/__tests__/latex-bridge.test.js +151 -0
  77. package/src/__tests__/mathlive-instance.test.js +130 -0
  78. package/src/horizontal-keypad.jsx +73 -0
  79. package/src/index.jsx +63 -0
  80. package/src/keypad/index.jsx +335 -0
  81. package/src/keypad/keys-layout.js +16 -0
  82. package/src/keys/basic-operators.js +32 -0
  83. package/src/keys/chars.js +5 -0
  84. package/src/keys/comparison.js +28 -0
  85. package/src/keys/constants.js +35 -0
  86. package/src/keys/digits.js +40 -0
  87. package/src/keys/edit.js +3 -0
  88. package/src/keys/exponent.js +28 -0
  89. package/src/keys/fractions.js +26 -0
  90. package/src/keys/geometry.js +144 -0
  91. package/src/keys/grades.js +367 -0
  92. package/src/keys/index.js +20 -0
  93. package/src/keys/log.js +22 -0
  94. package/src/keys/logic.js +15 -0
  95. package/src/keys/matrices.js +15 -0
  96. package/src/keys/misc.js +65 -0
  97. package/src/keys/navigation.js +8 -0
  98. package/src/keys/operators.js +10 -0
  99. package/src/keys/statistics.js +38 -0
  100. package/src/keys/sub-sup.js +15 -0
  101. package/src/keys/trigonometry.js +15 -0
  102. package/src/keys/utils.js +66 -0
  103. package/src/keys/vars.js +19 -0
  104. package/src/latex-bridge.js +190 -0
  105. package/src/math-input.jsx +115 -0
  106. package/src/mathlive-instance.js +286 -0
  107. package/src/mf/common-styles.js +108 -0
  108. package/src/mf/index.js +26 -0
  109. package/src/mf/input.jsx +223 -0
  110. package/src/mf/static.jsx +358 -0
@@ -0,0 +1,26 @@
1
+ import Input from './input';
2
+ import Static from './static';
3
+ import CommonMqStyles, {
4
+ commonMathLiveStyles,
5
+ commonKeyboardStyles,
6
+ commonMqFontStyles,
7
+ commonMqKeyboardStyles,
8
+ longdivStyles,
9
+ supsubStyles,
10
+ placeholderStyles,
11
+ } from './common-styles';
12
+
13
+ export {
14
+ Input,
15
+ Static,
16
+ // Same object shape as @pie-lib/math-input's `mq.CommonMqStyles`, so
17
+ // `const { commonMqFontStyles, ... } = mq.CommonMqStyles` keeps working.
18
+ CommonMqStyles,
19
+ commonMathLiveStyles,
20
+ commonKeyboardStyles,
21
+ commonMqFontStyles,
22
+ commonMqKeyboardStyles,
23
+ longdivStyles,
24
+ supsubStyles,
25
+ placeholderStyles,
26
+ };
@@ -0,0 +1,223 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { styled } from '@mui/material/styles';
4
+ import debug from 'debug';
5
+ import { loadMathLive, getMacros } from '../mathlive-instance';
6
+ import { toMathLive, fromMathLive } from '../latex-bridge';
7
+
8
+ const log = debug('pie-lib:math-input-mathlive:input');
9
+
10
+ const Holder = styled('span')({
11
+ display: 'inline-block',
12
+ '& math-field': {
13
+ display: 'block',
14
+ width: '100%',
15
+ },
16
+ // MathLive ships its own virtual keyboard toggle; pie supplies its own keypad.
17
+ '& math-field::part(virtual-keyboard-toggle)': {
18
+ display: 'none',
19
+ },
20
+ '& math-field::part(menu-toggle)': {
21
+ display: 'none',
22
+ },
23
+ });
24
+
25
+ /**
26
+ * Editable math field. Drop-in replacement for @pie-lib/math-input's `mq.Input`.
27
+ *
28
+ * Keeps the same imperative surface used by MathInput's keypad
29
+ * (`write`, `command`, `keystroke`, `clear`, `focus`, `blur`) so callers do not
30
+ * change, and the same `latex` / `onChange` props.
31
+ */
32
+ export class Input extends React.Component {
33
+ static propTypes = {
34
+ className: PropTypes.string,
35
+ latex: PropTypes.string,
36
+ onChange: PropTypes.func,
37
+ onClick: PropTypes.func,
38
+ onFocus: PropTypes.func,
39
+ onBlur: PropTypes.func,
40
+ readOnly: PropTypes.bool,
41
+ innerRef: PropTypes.func,
42
+ };
43
+
44
+ constructor(props) {
45
+ super(props);
46
+ this.holderRef = React.createRef();
47
+ // Tracks the last latex we emitted, so prop echoes don't reset the cursor.
48
+ this.lastEmitted = undefined;
49
+ }
50
+
51
+ async componentDidMount() {
52
+ const ml = await loadMathLive();
53
+
54
+ if (!ml || !this.holderRef.current) {
55
+ return;
56
+ }
57
+
58
+ this.mathField = new ml.MathfieldElement({ macros: getMacros() });
59
+ this.mathField.value = toMathLive(this.props.latex || '');
60
+
61
+ if (this.props.readOnly) {
62
+ this.mathField.readOnly = true;
63
+ }
64
+
65
+ // pie drives input through its own keypad, so suppress MathLive's.
66
+ this.mathField.mathVirtualKeyboardPolicy = 'manual';
67
+
68
+ this.mathField.addEventListener('input', this.onInputEdit);
69
+ this.holderRef.current.appendChild(this.mathField);
70
+
71
+ if (this.props.innerRef) {
72
+ this.props.innerRef(this);
73
+ }
74
+
75
+ this.setState?.({});
76
+ }
77
+
78
+ componentDidUpdate(prevProps) {
79
+ if (!this.mathField) {
80
+ return;
81
+ }
82
+
83
+ const { latex, readOnly } = this.props;
84
+
85
+ if (readOnly !== prevProps.readOnly) {
86
+ this.mathField.readOnly = !!readOnly;
87
+ }
88
+
89
+ // Only push the prop back into the field when it genuinely differs from
90
+ // what the field already holds. This is the analogue of the old
91
+ // shouldComponentUpdate latex-equality guard and prevents cursor thrash.
92
+ if (latex !== undefined && latex !== null && latex !== this.lastEmitted) {
93
+ const next = toMathLive(latex);
94
+
95
+ if (next !== this.mathField.value) {
96
+ this.mathField.value = next;
97
+ }
98
+ }
99
+ }
100
+
101
+ componentWillUnmount() {
102
+ if (this.mathField) {
103
+ this.mathField.removeEventListener('input', this.onInputEdit);
104
+ this.mathField.remove();
105
+ this.mathField = undefined;
106
+ }
107
+
108
+ if (this.props.innerRef) {
109
+ this.props.innerRef(null);
110
+ }
111
+ }
112
+
113
+ onInputEdit = () => {
114
+ const { onChange } = this.props;
115
+
116
+ if (!this.mathField) {
117
+ return;
118
+ }
119
+
120
+ const latex = fromMathLive(this.mathField.getValue('latex'));
121
+
122
+ this.lastEmitted = latex;
123
+
124
+ if (onChange) {
125
+ onChange(latex);
126
+ }
127
+ };
128
+
129
+ /** Current latex, in stored (MathQuill-compatible) form. */
130
+ latex() {
131
+ return this.mathField ? fromMathLive(this.mathField.getValue('latex')) : '';
132
+ }
133
+
134
+ clear() {
135
+ if (this.mathField) {
136
+ this.mathField.value = '';
137
+ }
138
+
139
+ return '';
140
+ }
141
+
142
+ blur() {
143
+ log('blur mathfield');
144
+ this.mathField && this.mathField.blur();
145
+ }
146
+
147
+ focus() {
148
+ log('focus mathfield...');
149
+ this.mathField && this.mathField.focus();
150
+ }
151
+
152
+ /** MathQuill `cmd()` equivalent. */
153
+ command(v) {
154
+ log('command: ', v);
155
+
156
+ if (!this.mathField) {
157
+ return '';
158
+ }
159
+
160
+ const values = Array.isArray(v) ? v : [v];
161
+
162
+ values.forEach((vv) => this.mathField.insert(vv, { focus: true }));
163
+ this.mathField.focus();
164
+
165
+ return this.latex();
166
+ }
167
+
168
+ /** MathQuill `keystroke()` equivalent - takes a MathLive selector. */
169
+ keystroke(selector) {
170
+ if (!this.mathField) {
171
+ return '';
172
+ }
173
+
174
+ this.mathField.executeCommand(selector);
175
+ this.mathField.focus();
176
+
177
+ return this.latex();
178
+ }
179
+
180
+ /** MathQuill `write()` equivalent. Accepts an array, as `cmd()` did. */
181
+ write(v) {
182
+ log('write: ', v);
183
+
184
+ if (!this.mathField) {
185
+ return '';
186
+ }
187
+
188
+ const values = Array.isArray(v) ? v : [v];
189
+
190
+ values.forEach((vv) => this.mathField.insert(vv, { focus: true }));
191
+ this.mathField.focus();
192
+
193
+ return this.latex();
194
+ }
195
+
196
+ /**
197
+ * Enter must not insert a line break: math input is single-expression, and
198
+ * the MathQuill version swallowed charCode 13 for the same reason. Newlines
199
+ * are authored explicitly through the newLine embed, not the Enter key.
200
+ */
201
+ onKeyDown = (event) => {
202
+ if (event.key === 'Enter') {
203
+ event.preventDefault();
204
+ }
205
+ };
206
+
207
+ render() {
208
+ const { className, onFocus, onBlur, onClick } = this.props;
209
+
210
+ return (
211
+ <Holder
212
+ className={className}
213
+ onFocus={onFocus}
214
+ onBlur={onBlur}
215
+ onClick={onClick}
216
+ onKeyDown={this.onKeyDown}
217
+ ref={this.holderRef}
218
+ />
219
+ );
220
+ }
221
+ }
222
+
223
+ export default Input;
@@ -0,0 +1,358 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { styled } from '@mui/material/styles';
4
+ import debug from 'debug';
5
+ import { loadMathLive, getMacros, latexToMarkup } from '../mathlive-instance';
6
+ import { toMathLive, fromMathLive, fieldIds } from '../latex-bridge';
7
+
8
+ const log = debug('pie-lib:math-input-mathlive:static');
9
+
10
+ const Holder = styled('span')({
11
+ display: 'inline-block',
12
+ '& math-field': {
13
+ display: 'inline-block',
14
+ border: 'none',
15
+ background: 'transparent',
16
+ },
17
+ '& math-field::part(virtual-keyboard-toggle)': {
18
+ display: 'none',
19
+ },
20
+ '& math-field::part(menu-toggle)': {
21
+ display: 'none',
22
+ },
23
+ });
24
+
25
+ /**
26
+ * Static math. Drop-in replacement for @pie-lib/math-input's `mq.Static`.
27
+ *
28
+ * Two modes, chosen automatically:
29
+ *
30
+ * - **display** (no answer blocks in the latex): rendered with
31
+ * `convertLatexToMarkup`. No mathfield instance, so it is cheap enough for
32
+ * keypad labels - the old implementation created a full MathQuill
33
+ * StaticMath per button.
34
+ *
35
+ * - **interactive** (latex contains `\MathQuillMathField[..]{}` /
36
+ * `\placeholder[..]{}`): a read-only `<math-field>` whose placeholders stay
37
+ * editable. Sub-field values are surfaced through the prompts API, replacing
38
+ * MathQuill's `innerFields`.
39
+ */
40
+ export default class Static extends React.Component {
41
+ static propTypes = {
42
+ latex: PropTypes.string.isRequired,
43
+ className: PropTypes.string,
44
+ onFocus: PropTypes.func,
45
+ onBlur: PropTypes.func,
46
+ getFieldName: PropTypes.func,
47
+ onSubFieldChange: PropTypes.func,
48
+ onSubFieldFocus: PropTypes.func,
49
+ setInput: PropTypes.func,
50
+ };
51
+
52
+ static defaultProps = {
53
+ getFieldName: () => {},
54
+ };
55
+
56
+ constructor(props) {
57
+ super(props);
58
+ this.holderRef = React.createRef();
59
+ this.state = { markup: '' };
60
+ this.promptValues = {};
61
+ }
62
+
63
+ async componentDidMount() {
64
+ if (!this.isInteractive()) {
65
+ await loadMathLive();
66
+ this.renderMarkup();
67
+ return;
68
+ }
69
+
70
+ const ml = await loadMathLive();
71
+
72
+ if (!ml || !this.holderRef.current) {
73
+ return;
74
+ }
75
+
76
+ this.mathField = new ml.MathfieldElement({ macros: getMacros() });
77
+ this.mathField.readOnly = true;
78
+ this.mathField.mathVirtualKeyboardPolicy = 'manual';
79
+ this.mathField.value = toMathLive(this.props.latex);
80
+
81
+ this.mathField.addEventListener('input', this.onPromptInput);
82
+ this.mathField.addEventListener('focusin', this.onPromptFocus);
83
+ this.mathField.addEventListener('keydown', this.onKeyDown);
84
+
85
+ this.holderRef.current.appendChild(this.mathField);
86
+
87
+ this.createLiveRegion();
88
+ }
89
+
90
+ componentDidUpdate(prevProps) {
91
+ if (prevProps.latex === this.props.latex) {
92
+ return;
93
+ }
94
+
95
+ if (this.mathField) {
96
+ this.mathField.value = toMathLive(this.props.latex);
97
+ } else {
98
+ this.renderMarkup();
99
+ }
100
+ }
101
+
102
+ componentWillUnmount() {
103
+ if (this.mathField) {
104
+ this.mathField.removeEventListener('input', this.onPromptInput);
105
+ this.mathField.removeEventListener('focusin', this.onPromptFocus);
106
+ this.mathField.removeEventListener('keydown', this.onKeyDown);
107
+ this.mathField.remove();
108
+ this.mathField = undefined;
109
+ }
110
+
111
+ this.removeLiveRegion();
112
+ }
113
+
114
+ /**
115
+ * Off-screen aria-live region, ported from @pie-lib/math-input's Static.
116
+ *
117
+ * MathLive has its own accessibility layer, but it does not announce that
118
+ * typed input was converted into a math symbol, which screen-reader users
119
+ * relied on in the MathQuill implementation.
120
+ */
121
+ createLiveRegion = () => {
122
+ if (typeof document === 'undefined' || this.liveRegion) {
123
+ return;
124
+ }
125
+
126
+ this.liveRegion = document.createElement('div');
127
+ Object.assign(this.liveRegion.style, {
128
+ position: 'absolute',
129
+ width: '1px',
130
+ height: '1px',
131
+ marginTop: '-1px',
132
+ clip: 'rect(1px, 1px, 1px, 1px)',
133
+ overflow: 'hidden',
134
+ });
135
+ this.liveRegion.setAttribute('aria-live', 'polite');
136
+ this.liveRegion.setAttribute('aria-atomic', 'true');
137
+
138
+ document.body.appendChild(this.liveRegion);
139
+ };
140
+
141
+ removeLiveRegion = () => {
142
+ if (this.liveRegion && this.liveRegion.parentNode) {
143
+ this.liveRegion.parentNode.removeChild(this.liveRegion);
144
+ }
145
+
146
+ this.liveRegion = undefined;
147
+ };
148
+
149
+ announce = (message) => {
150
+ if (!this.liveRegion) {
151
+ return;
152
+ }
153
+
154
+ this.liveRegion.textContent = message;
155
+
156
+ // Clear once announced, so the same message can be announced again.
157
+ clearTimeout(this.announceTimeout);
158
+ this.announceTimeout = setTimeout(() => {
159
+ if (this.liveRegion) {
160
+ this.liveRegion.textContent = '';
161
+ }
162
+ }, 500);
163
+ };
164
+
165
+ onKeyDown = (event) => {
166
+ // Deletions should not be announced as conversions.
167
+ this.isDeleting = event?.key === 'Backspace' || event?.key === 'Delete';
168
+ };
169
+
170
+ /**
171
+ * Announce when typed input became a math symbol. The MathQuill version
172
+ * inferred this by counting braces; here a prompt whose latex grew by more
173
+ * than the single character typed means a command was expanded.
174
+ */
175
+ announceConversion = (next) => {
176
+ if (this.isDeleting) {
177
+ this.isDeleting = false;
178
+ return;
179
+ }
180
+
181
+ const converted = Object.keys(next).some((id) => {
182
+ const before = this.promptValues[id] || '';
183
+ const after = next[id] || '';
184
+
185
+ return after.length > before.length + 1 && /\\/.test(after.slice(before.length));
186
+ });
187
+
188
+ if (converted) {
189
+ this.announce('Converted to math symbol');
190
+ }
191
+ };
192
+
193
+ /** Does this latex contain answer blocks? */
194
+ isInteractive() {
195
+ const latex = this.props.latex || '';
196
+
197
+ return fieldIds(latex).length > 0 || latex.indexOf('\\placeholder') !== -1;
198
+ }
199
+
200
+ renderMarkup() {
201
+ const markup = latexToMarkup(this.props.latex);
202
+
203
+ if (markup !== this.state.markup) {
204
+ this.setState({ markup });
205
+ }
206
+ }
207
+
208
+ /** MathQuill's `innerFields` equivalent, keyed by prompt id. */
209
+ values() {
210
+ if (!this.mathField) {
211
+ return {};
212
+ }
213
+
214
+ const out = {};
215
+
216
+ (this.mathField.getPrompts() || []).forEach((id) => {
217
+ out[id] = fromMathLive(this.mathField.getPromptValue(id));
218
+ });
219
+
220
+ return out;
221
+ }
222
+
223
+ /**
224
+ * A MathQuill-inner-field-like handle for a prompt.
225
+ *
226
+ * The old API handed callers a MathQuill field object, and consumers commonly
227
+ * called `field.latex()` on it. There is no equivalent object in MathLive -
228
+ * prompts are addressed by id - so this adapter exposes the parts that were
229
+ * actually used: `id` and a get/set `latex()`.
230
+ */
231
+ promptHandle(id) {
232
+ return {
233
+ id,
234
+ latex: (value) => {
235
+ if (!this.mathField) {
236
+ return '';
237
+ }
238
+
239
+ if (value === undefined) {
240
+ return fromMathLive(this.mathField.getPromptValue(id));
241
+ }
242
+
243
+ this.mathField.setPromptValue(id, toMathLive(value), {});
244
+
245
+ return value;
246
+ },
247
+ };
248
+ }
249
+
250
+ /** All prompt handles, the analogue of `mathField.innerFields`. */
251
+ get innerFields() {
252
+ if (!this.mathField) {
253
+ return [];
254
+ }
255
+
256
+ return (this.mathField.getPrompts() || []).map((id) => this.promptHandle(id));
257
+ }
258
+
259
+ onPromptInput = () => {
260
+ const { onSubFieldChange, getFieldName } = this.props;
261
+
262
+ if (!this.mathField) {
263
+ return;
264
+ }
265
+
266
+ const next = this.values();
267
+
268
+ this.announceConversion(next);
269
+
270
+ if (!onSubFieldChange) {
271
+ this.promptValues = next;
272
+ return;
273
+ }
274
+
275
+ const fields = this.innerFields;
276
+
277
+ // Emit only the prompts whose value actually changed, mirroring the
278
+ // per-field granularity of the old onSubFieldChange(name, latex).
279
+ Object.keys(next).forEach((id) => {
280
+ if (next[id] === this.promptValues[id]) {
281
+ return;
282
+ }
283
+
284
+ const name = getFieldName(this.promptHandle(id), fields) || id;
285
+
286
+ onSubFieldChange(name, next[id]);
287
+ });
288
+
289
+ this.promptValues = next;
290
+ };
291
+
292
+ onPromptFocus = () => {
293
+ const { onSubFieldFocus, getFieldName } = this.props;
294
+
295
+ if (!this.mathField || !onSubFieldFocus) {
296
+ return;
297
+ }
298
+
299
+ try {
300
+ const ids = this.mathField.getPrompts() || [];
301
+ // The focused prompt is the one containing the current selection.
302
+ const focused = ids.find((id) => {
303
+ const range = this.mathField.getPromptRange(id);
304
+ const selection = this.mathField.selection;
305
+
306
+ if (!range || !selection || !selection.ranges || !selection.ranges.length) {
307
+ return false;
308
+ }
309
+
310
+ const [start, end] = range;
311
+ const [selStart] = selection.ranges[0];
312
+
313
+ return selStart >= start && selStart <= end;
314
+ });
315
+
316
+ if (focused) {
317
+ const handle = this.promptHandle(focused);
318
+ const name = getFieldName(handle, this.innerFields) || focused;
319
+
320
+ if (this.props.setInput) {
321
+ this.props.setInput(handle);
322
+ }
323
+
324
+ onSubFieldFocus(name, handle);
325
+ }
326
+ } catch (e) {
327
+ log('error resolving focused prompt: %s', e.message);
328
+ }
329
+ };
330
+
331
+ focus() {
332
+ this.mathField && this.mathField.focus();
333
+ }
334
+
335
+ blur() {
336
+ this.mathField && this.mathField.blur();
337
+ }
338
+
339
+ render() {
340
+ const { className, onFocus, onBlur } = this.props;
341
+ const { markup } = this.state;
342
+
343
+ if (this.mathField || this.isInteractive()) {
344
+ return <Holder className={className} onFocus={onFocus} onBlur={onBlur} ref={this.holderRef} />;
345
+ }
346
+
347
+ return (
348
+ <Holder
349
+ className={className}
350
+ onFocus={onFocus}
351
+ onBlur={onBlur}
352
+ ref={this.holderRef}
353
+ // eslint-disable-next-line react/no-danger
354
+ dangerouslySetInnerHTML={{ __html: markup }}
355
+ />
356
+ );
357
+ }
358
+ }