@pie-lib/editable-html-tip-tap 2.1.5 → 2.1.6
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 +9 -0
- package/lib/components/CharacterPicker.js +41 -16
- package/lib/components/CharacterPicker.js.map +1 -1
- package/lib/components/common/toolbar-buttons.js +5 -0
- package/lib/components/common/toolbar-buttons.js.map +1 -1
- package/lib/components/respArea/InlineDropdown.js +2 -5
- package/lib/components/respArea/InlineDropdown.js.map +1 -1
- package/lib/extensions/math.js +47 -34
- package/lib/extensions/math.js.map +1 -1
- package/package.json +3 -3
- package/src/components/CharacterPicker.jsx +46 -16
- package/src/components/__tests__/CharacterPicker.test.jsx +10 -1
- package/src/components/__tests__/InlineDropdown.test.jsx +25 -7
- package/src/components/common/toolbar-buttons.jsx +5 -0
- package/src/components/respArea/InlineDropdown.jsx +2 -2
- package/src/extensions/__tests__/math.test.js +160 -65
- package/src/extensions/math.js +56 -35
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render, waitFor, fireEvent } from '@testing-library/react';
|
|
3
3
|
import { EnsureTextAfterMathPlugin, MathNode, MathNodeView, ZeroWidthSpaceHandlingPlugin } from '../math';
|
|
4
|
+
import * as toolbarUtils from '../../utils/toolbar';
|
|
4
5
|
|
|
5
6
|
jest.mock('@tiptap/react', () => ({
|
|
6
7
|
NodeViewWrapper: ({ children, ...props }) => (
|
|
@@ -147,6 +148,35 @@ describe('MathNode', () => {
|
|
|
147
148
|
expect(commands).toHaveProperty('insertMath');
|
|
148
149
|
expect(typeof commands.insertMath).toBe('function');
|
|
149
150
|
});
|
|
151
|
+
|
|
152
|
+
it('insertMath opens the toolbar after inserting a math node', () => {
|
|
153
|
+
const setToolbarOpenedSpy = jest.spyOn(toolbarUtils, 'setToolbarOpened');
|
|
154
|
+
const mathNode = { type: { name: 'math' }, nodeSize: 1 };
|
|
155
|
+
const tr = {
|
|
156
|
+
insert: jest.fn().mockReturnThis(),
|
|
157
|
+
setSelection: jest.fn().mockReturnThis(),
|
|
158
|
+
doc: {},
|
|
159
|
+
};
|
|
160
|
+
const editor = {
|
|
161
|
+
view: {
|
|
162
|
+
state: {
|
|
163
|
+
schema: { nodes: { math: { create: jest.fn(() => mathNode) } } },
|
|
164
|
+
selection: { $from: { pos: 1 } },
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
const dispatch = jest.fn();
|
|
169
|
+
const insertMath = MathNode.addCommands().insertMath('x^2');
|
|
170
|
+
|
|
171
|
+
insertMath({ tr, editor, dispatch });
|
|
172
|
+
|
|
173
|
+
expect(tr.insert).toHaveBeenCalledWith(1, mathNode);
|
|
174
|
+
expect(tr.setSelection).toHaveBeenCalled();
|
|
175
|
+
expect(dispatch).toHaveBeenCalledWith(tr);
|
|
176
|
+
expect(setToolbarOpenedSpy).toHaveBeenCalledWith(editor, true);
|
|
177
|
+
|
|
178
|
+
setToolbarOpenedSpy.mockRestore();
|
|
179
|
+
});
|
|
150
180
|
});
|
|
151
181
|
|
|
152
182
|
describe('addNodeView', () => {
|
|
@@ -219,12 +249,32 @@ describe('EnsureTextAfterMathPlugin', () => {
|
|
|
219
249
|
});
|
|
220
250
|
|
|
221
251
|
describe('ZeroWidthSpaceHandlingPlugin', () => {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
resolve: jest.fn(() => ({
|
|
252
|
+
const createDocWithMathAndZwsp = (resolveOverrides = {}) => ({
|
|
253
|
+
resolve: jest.fn((pos) => ({
|
|
225
254
|
nodeAfter: null,
|
|
226
255
|
nodeBefore: null,
|
|
256
|
+
pos,
|
|
257
|
+
...resolveOverrides[pos],
|
|
227
258
|
})),
|
|
259
|
+
nodeAt: jest.fn((pos) => {
|
|
260
|
+
if (pos === 1) {
|
|
261
|
+
return { type: { name: 'text' }, textContent: '\u200b' };
|
|
262
|
+
}
|
|
263
|
+
if (pos === 0) {
|
|
264
|
+
return { type: { name: 'math' }, nodeSize: 1 };
|
|
265
|
+
}
|
|
266
|
+
return null;
|
|
267
|
+
}),
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const createDocWithRegularTextBeforeCursor = () => ({
|
|
271
|
+
resolve: jest.fn((pos) => ({ nodeAfter: null, nodeBefore: null, pos })),
|
|
272
|
+
nodeAt: jest.fn((pos) => {
|
|
273
|
+
if (pos === 1) {
|
|
274
|
+
return { type: { name: 'text' }, textContent: 'a' };
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}),
|
|
228
278
|
});
|
|
229
279
|
|
|
230
280
|
const createView = ({ state: stateOverrides = {} } = {}) => {
|
|
@@ -237,66 +287,88 @@ describe('ZeroWidthSpaceHandlingPlugin', () => {
|
|
|
237
287
|
return {
|
|
238
288
|
state: {
|
|
239
289
|
selection: { from: 2, empty: true },
|
|
240
|
-
doc:
|
|
290
|
+
doc: createDocWithMathAndZwsp(),
|
|
241
291
|
tr,
|
|
242
292
|
...stateOverrides,
|
|
243
|
-
doc: { ...createDefaultDoc(), ...stateOverrides.doc },
|
|
244
293
|
},
|
|
245
294
|
dispatch,
|
|
246
295
|
};
|
|
247
296
|
};
|
|
248
297
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
298
|
+
describe('Backspace', () => {
|
|
299
|
+
it('deletes the inline node and zero-width space before the cursor', () => {
|
|
300
|
+
const view = createView();
|
|
301
|
+
const handled = ZeroWidthSpaceHandlingPlugin.props.handleKeyDown(view, { key: 'Backspace' });
|
|
302
|
+
|
|
303
|
+
expect(handled).toBe(true);
|
|
304
|
+
expect(view.state.tr.delete).toHaveBeenCalledWith(0, 2);
|
|
305
|
+
expect(view.dispatch).toHaveBeenCalledWith(view.state.tr);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('returns false when regular text precedes the cursor', () => {
|
|
309
|
+
const view = createView({
|
|
310
|
+
state: {
|
|
311
|
+
doc: createDocWithRegularTextBeforeCursor(),
|
|
312
|
+
},
|
|
313
|
+
});
|
|
253
314
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
315
|
+
const handled = ZeroWidthSpaceHandlingPlugin.props.handleKeyDown(view, { key: 'Backspace' });
|
|
316
|
+
|
|
317
|
+
expect(handled).toBe(false);
|
|
318
|
+
expect(view.state.tr.delete).not.toHaveBeenCalled();
|
|
319
|
+
expect(view.dispatch).not.toHaveBeenCalled();
|
|
320
|
+
});
|
|
257
321
|
});
|
|
258
322
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
.mockReturnValueOnce({ pos: 4 }),
|
|
323
|
+
describe('ArrowLeft', () => {
|
|
324
|
+
it('selects the inline node before a zero-width space', () => {
|
|
325
|
+
const mathNode = { nodeSize: 3 };
|
|
326
|
+
const view = createView({
|
|
327
|
+
state: {
|
|
328
|
+
doc: createDocWithMathAndZwsp({
|
|
329
|
+
0: { nodeAfter: mathNode, nodeBefore: null, pos: 0 },
|
|
330
|
+
}),
|
|
268
331
|
},
|
|
269
|
-
}
|
|
332
|
+
});
|
|
333
|
+
const { NodeSelection } = require('prosemirror-state');
|
|
334
|
+
|
|
335
|
+
const handled = ZeroWidthSpaceHandlingPlugin.props.handleKeyDown(view, { key: 'ArrowLeft' });
|
|
336
|
+
|
|
337
|
+
expect(handled).toBe(true);
|
|
338
|
+
expect(view.state.doc.resolve).toHaveBeenCalledWith(0);
|
|
339
|
+
expect(NodeSelection.create).toHaveBeenCalledWith(view.state.doc, 0);
|
|
340
|
+
expect(view.dispatch).toHaveBeenCalled();
|
|
270
341
|
});
|
|
271
|
-
const { NodeSelection } = require('prosemirror-state');
|
|
272
342
|
|
|
273
|
-
|
|
343
|
+
it('moves the text cursor before the zero-width space when no inline node precedes it', () => {
|
|
344
|
+
const view = createView();
|
|
345
|
+
const { TextSelection } = require('prosemirror-state');
|
|
274
346
|
|
|
275
|
-
|
|
276
|
-
expect(NodeSelection.create).toHaveBeenCalledWith(view.state.doc, 4);
|
|
277
|
-
expect(view.dispatch).toHaveBeenCalled();
|
|
278
|
-
});
|
|
347
|
+
const handled = ZeroWidthSpaceHandlingPlugin.props.handleKeyDown(view, { key: 'ArrowLeft' });
|
|
279
348
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
349
|
+
expect(handled).toBe(true);
|
|
350
|
+
expect(view.state.doc.resolve).toHaveBeenCalledWith(0);
|
|
351
|
+
expect(TextSelection.create).toHaveBeenCalledWith(view.state.doc, 0);
|
|
352
|
+
expect(view.dispatch).toHaveBeenCalled();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('returns false when regular text precedes the cursor', () => {
|
|
356
|
+
const view = createView({
|
|
357
|
+
state: {
|
|
358
|
+
doc: createDocWithRegularTextBeforeCursor(),
|
|
359
|
+
},
|
|
360
|
+
});
|
|
283
361
|
|
|
284
|
-
|
|
362
|
+
const handled = ZeroWidthSpaceHandlingPlugin.props.handleKeyDown(view, { key: 'ArrowLeft' });
|
|
285
363
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
364
|
+
expect(handled).toBe(false);
|
|
365
|
+
expect(view.state.tr.setSelection).not.toHaveBeenCalled();
|
|
366
|
+
expect(view.dispatch).not.toHaveBeenCalled();
|
|
367
|
+
});
|
|
289
368
|
});
|
|
290
369
|
|
|
291
370
|
it('returns false for unrelated keys', () => {
|
|
292
|
-
const view = createView(
|
|
293
|
-
state: {
|
|
294
|
-
doc: {
|
|
295
|
-
textBetween: jest.fn(() => 'a'),
|
|
296
|
-
resolve: jest.fn(),
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
});
|
|
371
|
+
const view = createView();
|
|
300
372
|
|
|
301
373
|
const handled = ZeroWidthSpaceHandlingPlugin.props.handleKeyDown(view, { key: 'Enter' });
|
|
302
374
|
expect(handled).toBe(false);
|
|
@@ -304,6 +376,15 @@ describe('ZeroWidthSpaceHandlingPlugin', () => {
|
|
|
304
376
|
});
|
|
305
377
|
|
|
306
378
|
describe('MathNodeView', () => {
|
|
379
|
+
const createEditorElement = (rect = { top: 0, left: 0, width: 600, height: 400 }) => {
|
|
380
|
+
const element = document.createElement('div');
|
|
381
|
+
Object.defineProperty(element, 'getBoundingClientRect', {
|
|
382
|
+
value: jest.fn(() => rect),
|
|
383
|
+
configurable: true,
|
|
384
|
+
});
|
|
385
|
+
return element;
|
|
386
|
+
};
|
|
387
|
+
|
|
307
388
|
const createMockEditor = () => ({
|
|
308
389
|
state: {
|
|
309
390
|
selection: {
|
|
@@ -320,6 +401,9 @@ describe('MathNodeView', () => {
|
|
|
320
401
|
coordsAtPos: jest.fn(() => ({ top: 100, left: 50 })),
|
|
321
402
|
dispatch: jest.fn(),
|
|
322
403
|
},
|
|
404
|
+
options: {
|
|
405
|
+
element: createEditorElement(),
|
|
406
|
+
},
|
|
323
407
|
commands: {
|
|
324
408
|
focus: jest.fn(),
|
|
325
409
|
},
|
|
@@ -335,13 +419,6 @@ describe('MathNodeView', () => {
|
|
|
335
419
|
|
|
336
420
|
let defaultProps;
|
|
337
421
|
|
|
338
|
-
beforeAll(() => {
|
|
339
|
-
Object.defineProperty(document.body, 'getBoundingClientRect', {
|
|
340
|
-
value: jest.fn(() => ({ top: 0, left: 0 })),
|
|
341
|
-
configurable: true,
|
|
342
|
-
});
|
|
343
|
-
});
|
|
344
|
-
|
|
345
422
|
beforeEach(() => {
|
|
346
423
|
jest.clearAllMocks();
|
|
347
424
|
mockCreatePortal.mockImplementation((node) => node);
|
|
@@ -390,30 +467,29 @@ describe('MathNodeView', () => {
|
|
|
390
467
|
});
|
|
391
468
|
|
|
392
469
|
describe('toolbar positioning', () => {
|
|
393
|
-
it('
|
|
470
|
+
it('positions relative to the editor element using coordsAtPos', async () => {
|
|
394
471
|
const { container } = render(<MathNodeView {...defaultProps} selected={true} />);
|
|
395
472
|
await waitFor(() => {
|
|
396
473
|
const toolbar = container.querySelector('[data-toolbar-for]');
|
|
397
474
|
expect(toolbar).toBeInTheDocument();
|
|
398
|
-
expect(toolbar.style.top).toBe('
|
|
475
|
+
expect(toolbar.style.top).toBe('140px');
|
|
399
476
|
expect(toolbar.style.left).toBe('50px');
|
|
400
477
|
});
|
|
401
478
|
});
|
|
402
479
|
|
|
403
|
-
it('
|
|
404
|
-
const
|
|
405
|
-
containerEl.getBoundingClientRect = jest.fn(() => ({ top: -200, left: 0, width: 600, height: 400 }));
|
|
480
|
+
it('accounts for editor scroll offset when calculating toolbar position', async () => {
|
|
481
|
+
const editorElement = createEditorElement({ top: -200, left: 0, width: 600, height: 400 });
|
|
406
482
|
|
|
407
483
|
const editor = {
|
|
408
484
|
...defaultProps.editor,
|
|
409
|
-
|
|
485
|
+
options: { element: editorElement },
|
|
410
486
|
};
|
|
411
487
|
|
|
412
488
|
const { container } = render(<MathNodeView {...defaultProps} editor={editor} selected={true} />);
|
|
413
489
|
await waitFor(() => {
|
|
414
490
|
const toolbar = container.querySelector('[data-toolbar-for]');
|
|
415
491
|
expect(toolbar).toBeInTheDocument();
|
|
416
|
-
expect(toolbar.style.top).toBe('
|
|
492
|
+
expect(toolbar.style.top).toBe('340px');
|
|
417
493
|
expect(toolbar.style.left).toBe('50px');
|
|
418
494
|
});
|
|
419
495
|
});
|
|
@@ -427,7 +503,7 @@ describe('MathNodeView', () => {
|
|
|
427
503
|
});
|
|
428
504
|
});
|
|
429
505
|
|
|
430
|
-
it('updates
|
|
506
|
+
it('updates position from coordsAtPos when selection changes', async () => {
|
|
431
507
|
const editor = {
|
|
432
508
|
...defaultProps.editor,
|
|
433
509
|
view: {
|
|
@@ -441,7 +517,7 @@ describe('MathNodeView', () => {
|
|
|
441
517
|
await waitFor(() => {
|
|
442
518
|
const toolbar = container.querySelector('[data-toolbar-for]');
|
|
443
519
|
expect(toolbar).toBeInTheDocument();
|
|
444
|
-
expect(toolbar.style.top).toBe('
|
|
520
|
+
expect(toolbar.style.top).toBe('240px');
|
|
445
521
|
expect(toolbar.style.left).toBe('150px');
|
|
446
522
|
});
|
|
447
523
|
});
|
|
@@ -515,18 +591,35 @@ describe('MathNodeView', () => {
|
|
|
515
591
|
});
|
|
516
592
|
});
|
|
517
593
|
|
|
518
|
-
it('unsets editor._toolbarOpened when toolbar is closed', async () => {
|
|
594
|
+
it('unsets editor._toolbarOpened when toolbar is closed and node is not selected', async () => {
|
|
595
|
+
const { getByTestId } = render(<MathNodeView {...defaultProps} selected={false} />);
|
|
596
|
+
|
|
597
|
+
fireEvent.click(getByTestId('math-preview'));
|
|
598
|
+
|
|
599
|
+
await waitFor(() => {
|
|
600
|
+
expect(getByTestId('math-toolbar')).toBeInTheDocument();
|
|
601
|
+
expect(defaultProps.editor._toolbarOpened).toBe(true);
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
fireEvent.click(getByTestId('done-button'));
|
|
605
|
+
|
|
606
|
+
await waitFor(() => {
|
|
607
|
+
expect(defaultProps.editor._toolbarOpened).toBe(false);
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it('keeps editor._toolbarOpened true while the math node remains selected', async () => {
|
|
519
612
|
const { getByTestId } = render(<MathNodeView {...defaultProps} selected={true} />);
|
|
520
613
|
|
|
521
614
|
await waitFor(() => {
|
|
522
615
|
expect(getByTestId('done-button')).toBeInTheDocument();
|
|
616
|
+
expect(defaultProps.editor._toolbarOpened).toBe(true);
|
|
523
617
|
});
|
|
524
618
|
|
|
525
|
-
|
|
526
|
-
fireEvent.click(doneButton);
|
|
619
|
+
fireEvent.click(getByTestId('done-button'));
|
|
527
620
|
|
|
528
621
|
await waitFor(() => {
|
|
529
|
-
expect(defaultProps.editor._toolbarOpened).toBe(
|
|
622
|
+
expect(defaultProps.editor._toolbarOpened).toBe(true);
|
|
530
623
|
});
|
|
531
624
|
});
|
|
532
625
|
|
|
@@ -551,7 +644,7 @@ describe('MathNodeView', () => {
|
|
|
551
644
|
expect(editor.state.tr.setSelection).toHaveBeenCalled();
|
|
552
645
|
expect(editor.view.dispatch).toHaveBeenCalledWith(editor.state.tr);
|
|
553
646
|
expect(editor.commands.focus).toHaveBeenCalled();
|
|
554
|
-
expect(editor._toolbarOpened).toBe(
|
|
647
|
+
expect(editor._toolbarOpened).toBe(true);
|
|
555
648
|
});
|
|
556
649
|
});
|
|
557
650
|
|
|
@@ -608,11 +701,13 @@ describe('MathNodeView', () => {
|
|
|
608
701
|
|
|
609
702
|
const { getAllByTestId, queryAllByTestId } = render(
|
|
610
703
|
<>
|
|
611
|
-
<MathNodeView {...defaultProps} editor={editorA} updateAttributes={updateAttributes} selected={
|
|
704
|
+
<MathNodeView {...defaultProps} editor={editorA} updateAttributes={updateAttributes} selected={false} />
|
|
612
705
|
<MathNodeView {...defaultProps} editor={editorB} node={{ attrs: { latex: 'y^2' } }} selected={false} />
|
|
613
706
|
</>,
|
|
614
707
|
);
|
|
615
708
|
|
|
709
|
+
fireEvent.click(getAllByTestId('math-preview')[0]);
|
|
710
|
+
|
|
616
711
|
await waitFor(() => {
|
|
617
712
|
expect(queryAllByTestId('math-toolbar')).toHaveLength(1);
|
|
618
713
|
});
|
package/src/extensions/math.js
CHANGED
|
@@ -45,6 +45,26 @@ export const EnsureTextAfterMathPlugin = (mathNodeName) =>
|
|
|
45
45
|
},
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
const nodeBeforeZeroWidthSpace = (doc, from) => {
|
|
49
|
+
let i;
|
|
50
|
+
|
|
51
|
+
// finding if previous to the cursor there's a zero-width space
|
|
52
|
+
// and a non-text element, and deleting everything until the space
|
|
53
|
+
for (i = from; i > 0; i--) {
|
|
54
|
+
const currentDoc = doc.nodeAt(i);
|
|
55
|
+
|
|
56
|
+
if (currentDoc?.type?.name === 'text' && currentDoc.textContent !== '\u200b') {
|
|
57
|
+
return -1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (currentDoc && currentDoc?.type?.name !== 'text') {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return i;
|
|
66
|
+
};
|
|
67
|
+
|
|
48
68
|
export const ZeroWidthSpaceHandlingPlugin = new Plugin({
|
|
49
69
|
key: new PluginKey('zeroWidthSpaceHandling'),
|
|
50
70
|
props: {
|
|
@@ -54,35 +74,38 @@ export const ZeroWidthSpaceHandlingPlugin = new Plugin({
|
|
|
54
74
|
const { from, empty } = selection;
|
|
55
75
|
|
|
56
76
|
if (empty && event.key === 'Backspace' && from > 0) {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return true; // handled
|
|
77
|
+
const start = nodeBeforeZeroWidthSpace(doc, from);
|
|
78
|
+
|
|
79
|
+
if (start === -1) {
|
|
80
|
+
return false;
|
|
62
81
|
}
|
|
82
|
+
|
|
83
|
+
const tr = state.tr.delete(start, from);
|
|
84
|
+
dispatch(tr);
|
|
85
|
+
return true; // handled
|
|
63
86
|
}
|
|
64
87
|
|
|
65
88
|
if (empty && event.key === 'ArrowLeft' && from > 0) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
const start = nodeBeforeZeroWidthSpace(doc, from);
|
|
90
|
+
|
|
91
|
+
if (start === -1) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const resolved = state.doc.resolve(start);
|
|
96
|
+
const maybeNode = resolved.nodeAfter || resolved.nodeBefore;
|
|
97
|
+
|
|
98
|
+
// Check if there's an inline selectable node (e.g., your math node)
|
|
99
|
+
if (maybeNode) {
|
|
100
|
+
const nodeResolved = state.doc.resolve(start);
|
|
101
|
+
const tr = state.tr.setSelection(NodeSelection.create(state.doc, nodeResolved.pos));
|
|
102
|
+
dispatch(tr);
|
|
103
|
+
return true;
|
|
104
|
+
} else {
|
|
105
|
+
// Just move the text cursor before the zwsp
|
|
106
|
+
const tr = state.tr.setSelection(TextSelection.create(state.doc, from - 2));
|
|
107
|
+
dispatch(tr);
|
|
108
|
+
return true;
|
|
86
109
|
}
|
|
87
110
|
}
|
|
88
111
|
|
|
@@ -152,14 +175,9 @@ export const MathNode = Node.create({
|
|
|
152
175
|
|
|
153
176
|
dispatch(tr);
|
|
154
177
|
|
|
178
|
+
setToolbarOpened(editor, true);
|
|
155
179
|
return true;
|
|
156
180
|
},
|
|
157
|
-
// insertMath: (latex = '') => ({ commands }) => {
|
|
158
|
-
// return commands.insertContent({
|
|
159
|
-
// type: this.name,
|
|
160
|
-
// attrs: { latex },
|
|
161
|
-
// });
|
|
162
|
-
// },
|
|
163
181
|
};
|
|
164
182
|
},
|
|
165
183
|
|
|
@@ -221,16 +239,19 @@ export const MathNodeView = (props) => {
|
|
|
221
239
|
}, [selected]);
|
|
222
240
|
|
|
223
241
|
useEffect(() => {
|
|
224
|
-
setToolbarOpened(editor, showToolbar);
|
|
225
|
-
}, [editor, showToolbar]);
|
|
242
|
+
setToolbarOpened(editor, selected || showToolbar);
|
|
243
|
+
}, [editor, showToolbar, selected]);
|
|
226
244
|
|
|
227
245
|
useEffect(() => {
|
|
228
246
|
// Calculate position relative to selection
|
|
229
247
|
const { from } = editor.state.selection;
|
|
230
248
|
const start = editor.view.coordsAtPos(from);
|
|
249
|
+
const editorDOM = editor.options.element;
|
|
250
|
+
const editorRect = editorDOM.getBoundingClientRect();
|
|
251
|
+
|
|
231
252
|
setPosition({
|
|
232
|
-
top: 40, // shift above
|
|
233
|
-
left: start.left,
|
|
253
|
+
top: start.top - editorRect.top + 40, // shift above
|
|
254
|
+
left: start.left - editorRect.left,
|
|
234
255
|
});
|
|
235
256
|
|
|
236
257
|
const handleClickOutside = (event) => {
|