prosemirror-view 1.29.2 → 1.30.1
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 +14 -0
- package/LICENSE +1 -1
- package/dist/index.cjs +27 -9
- package/dist/index.d.ts +56 -1
- package/dist/index.js +35 -17
- package/package.json +2 -2
- package/src/domcoords.ts +14 -8
- package/src/domobserver.ts +1 -1
- package/src/index.ts +13 -1
- package/src/input.ts +9 -7
- package/src/viewdesc.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## 1.30.1 (2023-02-04)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix a bug that caused `posAtCoords` to always return the position after a leaf node when querying a position on top of the node's element.
|
|
6
|
+
|
|
7
|
+
Fix an issue where, when putting focus on the editor by clicking at the very start of the document, the editor would restore a previous selection.
|
|
8
|
+
|
|
9
|
+
## 1.30.0 (2023-01-19)
|
|
10
|
+
|
|
11
|
+
### New features
|
|
12
|
+
|
|
13
|
+
`EditorView` now has `pasteHTML` and `pasteText` methods which initiate paste handling.
|
|
14
|
+
|
|
1
15
|
## 1.29.2 (2023-01-17)
|
|
2
16
|
|
|
3
17
|
### Bug fixes
|
package/LICENSE
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (C) 2015-2017 by Marijn Haverbeke <
|
|
1
|
+
Copyright (C) 2015-2017 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
|
|
2
2
|
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
4
|
of this software and associated documentation files (the "Software"), to deal
|
package/dist/index.cjs
CHANGED
|
@@ -386,22 +386,30 @@ function posFromElement(view, elt, coords) {
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
function posFromCaret(view, node, offset, coords) {
|
|
389
|
-
var
|
|
389
|
+
var outsideBlock = -1;
|
|
390
390
|
|
|
391
391
|
for (var cur = node;;) {
|
|
392
392
|
if (cur == view.dom) break;
|
|
393
393
|
var desc = view.docView.nearestDesc(cur, true);
|
|
394
394
|
if (!desc) return null;
|
|
395
395
|
|
|
396
|
-
if (desc.node.isBlock && desc.parent) {
|
|
396
|
+
if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent || !desc.contentDOM)) {
|
|
397
397
|
var rect = desc.dom.getBoundingClientRect();
|
|
398
|
-
|
|
398
|
+
|
|
399
|
+
if (desc.node.isBlock && desc.parent) {
|
|
400
|
+
if (rect.left > coords.left || rect.top > coords.top) outsideBlock = desc.posBefore;else if (rect.right < coords.left || rect.bottom < coords.top) outsideBlock = desc.posAfter;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (!desc.contentDOM && outsideBlock < 0) {
|
|
404
|
+
var before = desc.node.isBlock ? coords.top < (rect.top + rect.bottom) / 2 : coords.left < (rect.left + rect.right) / 2;
|
|
405
|
+
return before ? desc.posBefore : desc.posAfter;
|
|
406
|
+
}
|
|
399
407
|
}
|
|
400
408
|
|
|
401
409
|
cur = desc.dom.parentNode;
|
|
402
410
|
}
|
|
403
411
|
|
|
404
|
-
return
|
|
412
|
+
return outsideBlock > -1 ? outsideBlock : view.docView.posFromDOM(node, offset, -1);
|
|
405
413
|
}
|
|
406
414
|
|
|
407
415
|
function elementFromPoint(element, coords, box) {
|
|
@@ -3795,12 +3803,12 @@ function capturePaste(view, event) {
|
|
|
3795
3803
|
setTimeout(function () {
|
|
3796
3804
|
view.focus();
|
|
3797
3805
|
if (target.parentNode) target.parentNode.removeChild(target);
|
|
3798
|
-
if (plainText) doPaste(view, target.value, null, event);else doPaste(view, target.textContent, target.innerHTML, event);
|
|
3806
|
+
if (plainText) doPaste(view, target.value, null, view.input.shiftKey, event);else doPaste(view, target.textContent, target.innerHTML, view.input.shiftKey, event);
|
|
3799
3807
|
}, 50);
|
|
3800
3808
|
}
|
|
3801
3809
|
|
|
3802
|
-
function doPaste(view, text, html, event) {
|
|
3803
|
-
var slice = parseFromClipboard(view, text, html,
|
|
3810
|
+
function doPaste(view, text, html, preferPlain, event) {
|
|
3811
|
+
var slice = parseFromClipboard(view, text, html, preferPlain, view.state.selection.$from);
|
|
3804
3812
|
if (view.someProp("handlePaste", function (f) {
|
|
3805
3813
|
return f(view, event, slice || prosemirrorModel.Slice.empty);
|
|
3806
3814
|
})) return true;
|
|
@@ -3815,7 +3823,7 @@ editHandlers.paste = function (view, _event) {
|
|
|
3815
3823
|
var event = _event;
|
|
3816
3824
|
if (view.composing && !android) return;
|
|
3817
3825
|
var data = brokenClipboardAPI ? null : event.clipboardData;
|
|
3818
|
-
if (data && doPaste(view, data.getData("text/plain"), data.getData("text/html"), event)) event.preventDefault();else capturePaste(view, event);
|
|
3826
|
+
if (data && doPaste(view, data.getData("text/plain"), data.getData("text/html"), view.input.shiftKey, event)) event.preventDefault();else capturePaste(view, event);
|
|
3819
3827
|
};
|
|
3820
3828
|
|
|
3821
3829
|
var Dragging = _createClass(function Dragging(slice, move) {
|
|
@@ -4943,7 +4951,7 @@ var DOMObserver = function () {
|
|
|
4943
4951
|
|
|
4944
4952
|
var readSel = null;
|
|
4945
4953
|
|
|
4946
|
-
if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 && view.input.lastTouch < Date.now() - 300 && selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) && readSel.eq(prosemirrorState.Selection.near(view.state.doc.resolve(0), 1))) {
|
|
4954
|
+
if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 && Math.max(view.input.lastTouch, view.input.lastClick.time) < Date.now() - 300 && selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) && readSel.eq(prosemirrorState.Selection.near(view.state.doc.resolve(0), 1))) {
|
|
4947
4955
|
view.input.lastFocus = 0;
|
|
4948
4956
|
selectionToDOM(view);
|
|
4949
4957
|
this.currentSelection.set(sel);
|
|
@@ -5772,6 +5780,16 @@ var EditorView = function () {
|
|
|
5772
5780
|
value: function endOfTextblock(dir, state) {
|
|
5773
5781
|
return _endOfTextblock(this, state || this.state, dir);
|
|
5774
5782
|
}
|
|
5783
|
+
}, {
|
|
5784
|
+
key: "pasteHTML",
|
|
5785
|
+
value: function pasteHTML(html, event) {
|
|
5786
|
+
return doPaste(this, "", html, false, event || new ClipboardEvent("paste"));
|
|
5787
|
+
}
|
|
5788
|
+
}, {
|
|
5789
|
+
key: "pasteText",
|
|
5790
|
+
value: function pasteText(text, event) {
|
|
5791
|
+
return doPaste(this, text, null, true, event || new ClipboardEvent("paste"));
|
|
5792
|
+
}
|
|
5775
5793
|
}, {
|
|
5776
5794
|
key: "destroy",
|
|
5777
5795
|
value: function destroy() {
|
package/dist/index.d.ts
CHANGED
|
@@ -319,7 +319,8 @@ declare class ViewDesc {
|
|
|
319
319
|
get posAfter(): number;
|
|
320
320
|
get posAtEnd(): number;
|
|
321
321
|
localPosFromDOM(dom: DOMNode, offset: number, bias: number): number;
|
|
322
|
-
nearestDesc(dom: DOMNode
|
|
322
|
+
nearestDesc(dom: DOMNode): ViewDesc | undefined;
|
|
323
|
+
nearestDesc(dom: DOMNode, onlyNodes: true): NodeViewDesc | undefined;
|
|
323
324
|
getDesc(dom: DOMNode): ViewDesc | undefined;
|
|
324
325
|
posFromDOM(dom: DOMNode, offset: number, bias: number): number;
|
|
325
326
|
descAt(pos: number): ViewDesc | undefined;
|
|
@@ -345,6 +346,50 @@ declare class ViewDesc {
|
|
|
345
346
|
get domAtom(): boolean;
|
|
346
347
|
get ignoreForCoords(): boolean;
|
|
347
348
|
}
|
|
349
|
+
declare class NodeViewDesc extends ViewDesc {
|
|
350
|
+
node: Node;
|
|
351
|
+
outerDeco: readonly Decoration[];
|
|
352
|
+
innerDeco: DecorationSource;
|
|
353
|
+
readonly nodeDOM: DOMNode;
|
|
354
|
+
constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, dom: DOMNode, contentDOM: HTMLElement | null, nodeDOM: DOMNode, view: EditorView, pos: number);
|
|
355
|
+
static create(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView, pos: number): NodeViewDesc | TextViewDesc;
|
|
356
|
+
parseRule(): ParseRule | null;
|
|
357
|
+
matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
|
|
358
|
+
get size(): number;
|
|
359
|
+
get border(): 1 | 0;
|
|
360
|
+
updateChildren(view: EditorView, pos: number): void;
|
|
361
|
+
localCompositionInfo(view: EditorView, pos: number): {
|
|
362
|
+
node: Text;
|
|
363
|
+
pos: number;
|
|
364
|
+
text: string;
|
|
365
|
+
} | null;
|
|
366
|
+
protectLocalComposition(view: EditorView, { node, pos, text }: {
|
|
367
|
+
node: Text;
|
|
368
|
+
pos: number;
|
|
369
|
+
text: string;
|
|
370
|
+
}): void;
|
|
371
|
+
update(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView): boolean;
|
|
372
|
+
updateInner(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView): void;
|
|
373
|
+
updateOuterDeco(outerDeco: readonly Decoration[]): void;
|
|
374
|
+
selectNode(): void;
|
|
375
|
+
deselectNode(): void;
|
|
376
|
+
get domAtom(): boolean;
|
|
377
|
+
}
|
|
378
|
+
declare class TextViewDesc extends NodeViewDesc {
|
|
379
|
+
constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, dom: DOMNode, nodeDOM: DOMNode, view: EditorView);
|
|
380
|
+
parseRule(): ParseRule;
|
|
381
|
+
update(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView): boolean;
|
|
382
|
+
inParent(): boolean;
|
|
383
|
+
domFromPos(pos: number): {
|
|
384
|
+
node: globalThis.Node;
|
|
385
|
+
offset: number;
|
|
386
|
+
};
|
|
387
|
+
localPosFromDOM(dom: DOMNode, offset: number, bias: number): number;
|
|
388
|
+
ignoreMutation(mutation: MutationRecord): boolean;
|
|
389
|
+
slice(from: number, to: number, view: EditorView): TextViewDesc;
|
|
390
|
+
markDirty(from: number, to: number): void;
|
|
391
|
+
get domAtom(): boolean;
|
|
392
|
+
}
|
|
348
393
|
|
|
349
394
|
/**
|
|
350
395
|
An editor view manages the DOM structure that represents an
|
|
@@ -520,6 +565,16 @@ declare class EditorView {
|
|
|
520
565
|
*/
|
|
521
566
|
endOfTextblock(dir: "up" | "down" | "left" | "right" | "forward" | "backward", state?: EditorState): boolean;
|
|
522
567
|
/**
|
|
568
|
+
Run the editor's paste logic with the given HTML string. The
|
|
569
|
+
`event`, if given, will be passed to the
|
|
570
|
+
[`handlePaste`](https://prosemirror.net/docs/ref/#view.EditorProps.handlePaste) hook.
|
|
571
|
+
*/
|
|
572
|
+
pasteHTML(html: string, event?: ClipboardEvent): boolean;
|
|
573
|
+
/**
|
|
574
|
+
Run the editor's paste logic with the given plain-text input.
|
|
575
|
+
*/
|
|
576
|
+
pasteText(text: string, event?: ClipboardEvent): boolean;
|
|
577
|
+
/**
|
|
523
578
|
Removes the editor from the DOM and destroys all [node
|
|
524
579
|
views](https://prosemirror.net/docs/ref/#view.NodeView).
|
|
525
580
|
*/
|
package/dist/index.js
CHANGED
|
@@ -314,25 +314,31 @@ function posFromCaret(view, node, offset, coords) {
|
|
|
314
314
|
// of nodes to see if there are block nodes that the coordinates
|
|
315
315
|
// fall outside of. If so, we take the position before/after that
|
|
316
316
|
// block. If not, we call `posFromDOM` on the raw node/offset.
|
|
317
|
-
let
|
|
317
|
+
let outsideBlock = -1;
|
|
318
318
|
for (let cur = node;;) {
|
|
319
319
|
if (cur == view.dom)
|
|
320
320
|
break;
|
|
321
321
|
let desc = view.docView.nearestDesc(cur, true);
|
|
322
322
|
if (!desc)
|
|
323
323
|
return null;
|
|
324
|
-
if (desc.node.isBlock && desc.parent) {
|
|
324
|
+
if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent || !desc.contentDOM)) {
|
|
325
325
|
let rect = desc.dom.getBoundingClientRect();
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
326
|
+
if (desc.node.isBlock && desc.parent) {
|
|
327
|
+
if (rect.left > coords.left || rect.top > coords.top)
|
|
328
|
+
outsideBlock = desc.posBefore;
|
|
329
|
+
else if (rect.right < coords.left || rect.bottom < coords.top)
|
|
330
|
+
outsideBlock = desc.posAfter;
|
|
331
|
+
}
|
|
332
|
+
if (!desc.contentDOM && outsideBlock < 0) {
|
|
333
|
+
// If we are inside a leaf, return the side of the leaf closer to the coords
|
|
334
|
+
let before = desc.node.isBlock ? coords.top < (rect.top + rect.bottom) / 2
|
|
335
|
+
: coords.left < (rect.left + rect.right) / 2;
|
|
336
|
+
return before ? desc.posBefore : desc.posAfter;
|
|
337
|
+
}
|
|
332
338
|
}
|
|
333
339
|
cur = desc.dom.parentNode;
|
|
334
340
|
}
|
|
335
|
-
return
|
|
341
|
+
return outsideBlock > -1 ? outsideBlock : view.docView.posFromDOM(node, offset, -1);
|
|
336
342
|
}
|
|
337
343
|
function elementFromPoint(element, coords, box) {
|
|
338
344
|
let len = element.childNodes.length;
|
|
@@ -760,8 +766,6 @@ class ViewDesc {
|
|
|
760
766
|
}
|
|
761
767
|
return (atEnd == null ? bias > 0 : atEnd) ? this.posAtEnd : this.posAtStart;
|
|
762
768
|
}
|
|
763
|
-
// Scan up the dom finding the first desc that is a descendant of
|
|
764
|
-
// this one.
|
|
765
769
|
nearestDesc(dom, onlyNodes = false) {
|
|
766
770
|
for (let first = true, cur = dom; cur; cur = cur.parentNode) {
|
|
767
771
|
let desc = this.getDesc(cur), nodeDOM;
|
|
@@ -3345,13 +3349,13 @@ function capturePaste(view, event) {
|
|
|
3345
3349
|
if (target.parentNode)
|
|
3346
3350
|
target.parentNode.removeChild(target);
|
|
3347
3351
|
if (plainText)
|
|
3348
|
-
doPaste(view, target.value, null, event);
|
|
3352
|
+
doPaste(view, target.value, null, view.input.shiftKey, event);
|
|
3349
3353
|
else
|
|
3350
|
-
doPaste(view, target.textContent, target.innerHTML, event);
|
|
3354
|
+
doPaste(view, target.textContent, target.innerHTML, view.input.shiftKey, event);
|
|
3351
3355
|
}, 50);
|
|
3352
3356
|
}
|
|
3353
|
-
function doPaste(view, text, html, event) {
|
|
3354
|
-
let slice = parseFromClipboard(view, text, html,
|
|
3357
|
+
function doPaste(view, text, html, preferPlain, event) {
|
|
3358
|
+
let slice = parseFromClipboard(view, text, html, preferPlain, view.state.selection.$from);
|
|
3355
3359
|
if (view.someProp("handlePaste", f => f(view, event, slice || Slice.empty)))
|
|
3356
3360
|
return true;
|
|
3357
3361
|
if (!slice)
|
|
@@ -3372,7 +3376,7 @@ editHandlers.paste = (view, _event) => {
|
|
|
3372
3376
|
if (view.composing && !android)
|
|
3373
3377
|
return;
|
|
3374
3378
|
let data = brokenClipboardAPI ? null : event.clipboardData;
|
|
3375
|
-
if (data && doPaste(view, data.getData("text/plain"), data.getData("text/html"), event))
|
|
3379
|
+
if (data && doPaste(view, data.getData("text/plain"), data.getData("text/html"), view.input.shiftKey, event))
|
|
3376
3380
|
event.preventDefault();
|
|
3377
3381
|
else
|
|
3378
3382
|
capturePaste(view, event);
|
|
@@ -4381,7 +4385,7 @@ class DOMObserver {
|
|
|
4381
4385
|
// start of the document after focus, restore the selection from
|
|
4382
4386
|
// the state
|
|
4383
4387
|
if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 &&
|
|
4384
|
-
view.input.lastTouch < Date.now() - 300 &&
|
|
4388
|
+
Math.max(view.input.lastTouch, view.input.lastClick.time) < Date.now() - 300 &&
|
|
4385
4389
|
selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) &&
|
|
4386
4390
|
readSel.eq(Selection.near(view.state.doc.resolve(0), 1))) {
|
|
4387
4391
|
view.input.lastFocus = 0;
|
|
@@ -5262,6 +5266,20 @@ class EditorView {
|
|
|
5262
5266
|
return endOfTextblock(this, state || this.state, dir);
|
|
5263
5267
|
}
|
|
5264
5268
|
/**
|
|
5269
|
+
Run the editor's paste logic with the given HTML string. The
|
|
5270
|
+
`event`, if given, will be passed to the
|
|
5271
|
+
[`handlePaste`](https://prosemirror.net/docs/ref/#view.EditorProps.handlePaste) hook.
|
|
5272
|
+
*/
|
|
5273
|
+
pasteHTML(html, event) {
|
|
5274
|
+
return doPaste(this, "", html, false, event || new ClipboardEvent("paste"));
|
|
5275
|
+
}
|
|
5276
|
+
/**
|
|
5277
|
+
Run the editor's paste logic with the given plain-text input.
|
|
5278
|
+
*/
|
|
5279
|
+
pasteText(text, event) {
|
|
5280
|
+
return doPaste(this, text, null, true, event || new ClipboardEvent("paste"));
|
|
5281
|
+
}
|
|
5282
|
+
/**
|
|
5265
5283
|
Removes the editor from the DOM and destroys all [node
|
|
5266
5284
|
views](https://prosemirror.net/docs/ref/#view.NodeView).
|
|
5267
5285
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prosemirror-view",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.1",
|
|
4
4
|
"description": "ProseMirror's view component",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"maintainers": [
|
|
20
20
|
{
|
|
21
21
|
"name": "Marijn Haverbeke",
|
|
22
|
-
"email": "
|
|
22
|
+
"email": "marijn@haverbeke.berlin",
|
|
23
23
|
"web": "http://marijnhaverbeke.nl"
|
|
24
24
|
}
|
|
25
25
|
],
|
package/src/domcoords.ts
CHANGED
|
@@ -2,7 +2,6 @@ import {EditorState} from "prosemirror-state"
|
|
|
2
2
|
import {nodeSize, textRange, parentNode} from "./dom"
|
|
3
3
|
import * as browser from "./browser"
|
|
4
4
|
import {EditorView} from "./index"
|
|
5
|
-
import {NodeViewDesc} from "./viewdesc"
|
|
6
5
|
|
|
7
6
|
export type Rect = {left: number, right: number, top: number, bottom: number}
|
|
8
7
|
|
|
@@ -211,20 +210,27 @@ function posFromCaret(view: EditorView, node: Node, offset: number, coords: {top
|
|
|
211
210
|
// of nodes to see if there are block nodes that the coordinates
|
|
212
211
|
// fall outside of. If so, we take the position before/after that
|
|
213
212
|
// block. If not, we call `posFromDOM` on the raw node/offset.
|
|
214
|
-
let
|
|
213
|
+
let outsideBlock = -1
|
|
215
214
|
for (let cur = node;;) {
|
|
216
215
|
if (cur == view.dom) break
|
|
217
216
|
let desc = view.docView.nearestDesc(cur, true)
|
|
218
217
|
if (!desc) return null
|
|
219
|
-
if (
|
|
218
|
+
if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent || !desc.contentDOM)) {
|
|
220
219
|
let rect = (desc.dom as HTMLElement).getBoundingClientRect()
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
220
|
+
if (desc.node.isBlock && desc.parent) {
|
|
221
|
+
if (rect.left > coords.left || rect.top > coords.top) outsideBlock = desc.posBefore
|
|
222
|
+
else if (rect.right < coords.left || rect.bottom < coords.top) outsideBlock = desc.posAfter
|
|
223
|
+
}
|
|
224
|
+
if (!desc.contentDOM && outsideBlock < 0) {
|
|
225
|
+
// If we are inside a leaf, return the side of the leaf closer to the coords
|
|
226
|
+
let before = desc.node.isBlock ? coords.top < (rect.top + rect.bottom) / 2
|
|
227
|
+
: coords.left < (rect.left + rect.right) / 2
|
|
228
|
+
return before ? desc.posBefore : desc.posAfter
|
|
229
|
+
}
|
|
224
230
|
}
|
|
225
231
|
cur = desc.dom.parentNode!
|
|
226
232
|
}
|
|
227
|
-
return
|
|
233
|
+
return outsideBlock > -1 ? outsideBlock : view.docView.posFromDOM(node, offset, -1)
|
|
228
234
|
}
|
|
229
235
|
|
|
230
236
|
function elementFromPoint(element: HTMLElement, coords: {top: number, left: number}, box: Rect): HTMLElement {
|
|
@@ -413,7 +419,7 @@ function endOfTextblockVertical(view: EditorView, state: EditorState, dir: "up"
|
|
|
413
419
|
for (;;) {
|
|
414
420
|
let nearest = view.docView.nearestDesc(dom, true)
|
|
415
421
|
if (!nearest) break
|
|
416
|
-
if (nearest.node
|
|
422
|
+
if (nearest.node.isBlock) { dom = nearest.contentDOM || nearest.dom; break }
|
|
417
423
|
dom = nearest.dom.parentNode!
|
|
418
424
|
}
|
|
419
425
|
let coords = coordsAtPos(view, $pos.pos, 1)
|
package/src/domobserver.ts
CHANGED
|
@@ -195,7 +195,7 @@ export class DOMObserver {
|
|
|
195
195
|
// start of the document after focus, restore the selection from
|
|
196
196
|
// the state
|
|
197
197
|
if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 &&
|
|
198
|
-
view.input.lastTouch < Date.now() - 300 &&
|
|
198
|
+
Math.max(view.input.lastTouch, view.input.lastClick.time) < Date.now() - 300 &&
|
|
199
199
|
selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) &&
|
|
200
200
|
readSel.eq(Selection.near(view.state.doc.resolve(0), 1))) {
|
|
201
201
|
view.input.lastFocus = 0
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {Slice, ResolvedPos, DOMParser, DOMSerializer, Node, Mark} from "prosemir
|
|
|
4
4
|
import {scrollRectIntoView, posAtCoords, coordsAtPos, endOfTextblock, storeScrollPos,
|
|
5
5
|
resetScrollPos, focusPreventScroll} from "./domcoords"
|
|
6
6
|
import {docViewDesc, ViewDesc, NodeView, NodeViewDesc} from "./viewdesc"
|
|
7
|
-
import {initInput, destroyInput, dispatchEvent, ensureListeners, clearComposition, InputState} from "./input"
|
|
7
|
+
import {initInput, destroyInput, dispatchEvent, ensureListeners, clearComposition, InputState, doPaste} from "./input"
|
|
8
8
|
import {selectionToDOM, anchorInRightPlace, syncNodeSelection} from "./selection"
|
|
9
9
|
import {Decoration, viewDecorations, DecorationSource} from "./decoration"
|
|
10
10
|
import {DOMObserver, safariShadowSelectionRange} from "./domobserver"
|
|
@@ -409,6 +409,18 @@ export class EditorView {
|
|
|
409
409
|
return endOfTextblock(this, state || this.state, dir)
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
+
/// Run the editor's paste logic with the given HTML string. The
|
|
413
|
+
/// `event`, if given, will be passed to the
|
|
414
|
+
/// [`handlePaste`](#view.EditorProps.handlePaste) hook.
|
|
415
|
+
pasteHTML(html: string, event?: ClipboardEvent) {
|
|
416
|
+
return doPaste(this, "", html, false, event || new ClipboardEvent("paste"))
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/// Run the editor's paste logic with the given plain-text input.
|
|
420
|
+
pasteText(text: string, event?: ClipboardEvent) {
|
|
421
|
+
return doPaste(this, text, null, true, event || new ClipboardEvent("paste"))
|
|
422
|
+
}
|
|
423
|
+
|
|
412
424
|
/// Removes the editor from the DOM and destroys all [node
|
|
413
425
|
/// views](#view.NodeView).
|
|
414
426
|
destroy() {
|
package/src/input.ts
CHANGED
|
@@ -583,13 +583,13 @@ function capturePaste(view: EditorView, event: ClipboardEvent) {
|
|
|
583
583
|
setTimeout(() => {
|
|
584
584
|
view.focus()
|
|
585
585
|
if (target.parentNode) target.parentNode.removeChild(target)
|
|
586
|
-
if (plainText) doPaste(view, (target as HTMLTextAreaElement).value, null, event)
|
|
587
|
-
else doPaste(view, target.textContent!, target.innerHTML, event)
|
|
586
|
+
if (plainText) doPaste(view, (target as HTMLTextAreaElement).value, null, view.input.shiftKey, event)
|
|
587
|
+
else doPaste(view, target.textContent!, target.innerHTML, view.input.shiftKey, event)
|
|
588
588
|
}, 50)
|
|
589
589
|
}
|
|
590
590
|
|
|
591
|
-
function doPaste(view: EditorView, text: string, html: string | null, event: ClipboardEvent) {
|
|
592
|
-
let slice = parseFromClipboard(view, text, html,
|
|
591
|
+
export function doPaste(view: EditorView, text: string, html: string | null, preferPlain: boolean, event: ClipboardEvent) {
|
|
592
|
+
let slice = parseFromClipboard(view, text, html, preferPlain, view.state.selection.$from)
|
|
593
593
|
if (view.someProp("handlePaste", f => f(view, event, slice || Slice.empty))) return true
|
|
594
594
|
if (!slice) return false
|
|
595
595
|
|
|
@@ -609,8 +609,10 @@ editHandlers.paste = (view, _event) => {
|
|
|
609
609
|
// where the editor is almost always composing.
|
|
610
610
|
if (view.composing && !browser.android) return
|
|
611
611
|
let data = brokenClipboardAPI ? null : event.clipboardData
|
|
612
|
-
if (data && doPaste(view, data.getData("text/plain"), data.getData("text/html"), event))
|
|
613
|
-
|
|
612
|
+
if (data && doPaste(view, data.getData("text/plain"), data.getData("text/html"), view.input.shiftKey, event))
|
|
613
|
+
event.preventDefault()
|
|
614
|
+
else
|
|
615
|
+
capturePaste(view, event)
|
|
614
616
|
}
|
|
615
617
|
|
|
616
618
|
class Dragging {
|
|
@@ -633,7 +635,7 @@ handlers.dragstart = (view, _event) => {
|
|
|
633
635
|
view.dispatch(view.state.tr.setSelection(NodeSelection.create(view.state.doc, mouseDown.mightDrag.pos)))
|
|
634
636
|
} else if (event.target && (event.target as HTMLElement).nodeType == 1) {
|
|
635
637
|
let desc = view.docView.nearestDesc(event.target as HTMLElement, true)
|
|
636
|
-
if (desc && desc.node
|
|
638
|
+
if (desc && desc.node.type.spec.draggable && desc != view.docView)
|
|
637
639
|
view.dispatch(view.state.tr.setSelection(NodeSelection.create(view.state.doc, desc.posBefore)))
|
|
638
640
|
}
|
|
639
641
|
let slice = view.state.selection.content(), {dom, text} = serializeForClipboard(view, slice)
|
package/src/viewdesc.ts
CHANGED
|
@@ -221,6 +221,8 @@ export class ViewDesc {
|
|
|
221
221
|
|
|
222
222
|
// Scan up the dom finding the first desc that is a descendant of
|
|
223
223
|
// this one.
|
|
224
|
+
nearestDesc(dom: DOMNode): ViewDesc | undefined
|
|
225
|
+
nearestDesc(dom: DOMNode, onlyNodes: true): NodeViewDesc | undefined
|
|
224
226
|
nearestDesc(dom: DOMNode, onlyNodes: boolean = false) {
|
|
225
227
|
for (let first = true, cur: DOMNode | null = dom; cur; cur = cur.parentNode) {
|
|
226
228
|
let desc = this.getDesc(cur), nodeDOM
|