prosemirror-view 1.33.0 → 1.33.2
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 +20 -0
- package/dist/index.cjs +56 -27
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +66 -36
- package/package.json +1 -1
- package/src/#index.ts# +803 -0
- package/src/decoration.ts +0 -1
- package/src/dom.ts +36 -0
- package/src/domchange.ts +10 -11
- package/src/domcoords.ts +5 -0
- package/src/index.ts +3 -2
- package/src/viewdesc.ts +9 -19
package/src/decoration.ts
CHANGED
|
@@ -413,7 +413,6 @@ export class DecorationSet implements DecorationSource {
|
|
|
413
413
|
return local.length || children.length ? new DecorationSet(local, children) : empty
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
-
/// @internal
|
|
417
416
|
forChild(offset: number, node: Node): DecorationSet | DecorationGroup {
|
|
418
417
|
if (this == empty) return this
|
|
419
418
|
if (node.isLeaf) return DecorationSet.empty
|
package/src/dom.ts
CHANGED
|
@@ -29,6 +29,10 @@ export const textRange = function(node: Text, from?: number, to?: number) {
|
|
|
29
29
|
return range
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
export const clearReusedRange = function() {
|
|
33
|
+
reusedRange = null;
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
// Scans forward and backward through DOM positions equivalent to the
|
|
33
37
|
// given one to see if the two are in the same place (i.e. after a
|
|
34
38
|
// text node vs at the end of that text node)
|
|
@@ -63,6 +67,38 @@ export function nodeSize(node: Node) {
|
|
|
63
67
|
return node.nodeType == 3 ? node.nodeValue!.length : node.childNodes.length
|
|
64
68
|
}
|
|
65
69
|
|
|
70
|
+
export function textNodeBefore(node: Node, offset: number) {
|
|
71
|
+
for (;;) {
|
|
72
|
+
if (node.nodeType == 3 && offset) return node as Text
|
|
73
|
+
if (node.nodeType == 1 && offset > 0) {
|
|
74
|
+
if ((node as HTMLElement).contentEditable != "false") return null
|
|
75
|
+
node = node.childNodes[offset - 1]
|
|
76
|
+
offset = nodeSize(node)
|
|
77
|
+
} else if (node.parentNode && !hasBlockDesc(node)) {
|
|
78
|
+
offset = domIndex(node)
|
|
79
|
+
node = node.parentNode
|
|
80
|
+
} else {
|
|
81
|
+
return null
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function textNodeAfter(node: Node, offset: number) {
|
|
87
|
+
for (;;) {
|
|
88
|
+
if (node.nodeType == 3 && offset < node.nodeValue!.length) return node as Text
|
|
89
|
+
if (node.nodeType == 1 && offset < node.childNodes.length) {
|
|
90
|
+
if ((node as HTMLElement).contentEditable != "false") return null
|
|
91
|
+
node = node.childNodes[offset]
|
|
92
|
+
offset = 0
|
|
93
|
+
} else if (node.parentNode && !hasBlockDesc(node)) {
|
|
94
|
+
offset = domIndex(node) + 1
|
|
95
|
+
node = node.parentNode
|
|
96
|
+
} else {
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
66
102
|
export function isOnEdge(node: Node, offset: number, parent: Node) {
|
|
67
103
|
for (let atStart = offset == 0, atEnd = offset == nodeSize(node); atStart || atEnd;) {
|
|
68
104
|
if (node == parent) return true
|
package/src/domchange.ts
CHANGED
|
@@ -143,13 +143,6 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
143
143
|
return
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
-
// Chrome sometimes leaves the cursor before the inserted text when
|
|
147
|
-
// composing after a cursor wrapper. This moves it forward.
|
|
148
|
-
if (browser.chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from &&
|
|
149
|
-
parse.sel.head == parse.sel.anchor) {
|
|
150
|
-
let size = change.endB - change.start
|
|
151
|
-
parse.sel = {anchor: parse.sel.anchor + size, head: parse.sel.anchor + size}
|
|
152
|
-
}
|
|
153
146
|
|
|
154
147
|
view.input.domChangeCount++
|
|
155
148
|
// Handle the case where overwriting a selection by typing matches
|
|
@@ -197,7 +190,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
197
190
|
}
|
|
198
191
|
// Same for backspace
|
|
199
192
|
if (view.state.selection.anchor > change.start &&
|
|
200
|
-
|
|
193
|
+
looksLikeBackspace(doc, change.start, change.endA, $from, $to) &&
|
|
201
194
|
view.someProp("handleKeyDown", f => f(view, keyEvent(8, "Backspace")))) {
|
|
202
195
|
if (browser.android && browser.chrome) view.domObserver.suppressSelectionUpdates() // #820
|
|
203
196
|
return
|
|
@@ -304,15 +297,21 @@ function isMarkChange(cur: Fragment, prev: Fragment) {
|
|
|
304
297
|
if (Fragment.from(updated).eq(cur)) return {mark, type}
|
|
305
298
|
}
|
|
306
299
|
|
|
307
|
-
function
|
|
308
|
-
if (
|
|
309
|
-
// The content must have shrunk
|
|
300
|
+
function looksLikeBackspace(old: Node, start: number, end: number, $newStart: ResolvedPos, $newEnd: ResolvedPos) {
|
|
301
|
+
if (// The content must have shrunk
|
|
310
302
|
end - start <= $newEnd.pos - $newStart.pos ||
|
|
311
303
|
// newEnd must point directly at or after the end of the block that newStart points into
|
|
312
304
|
skipClosingAndOpening($newStart, true, false) < $newEnd.pos)
|
|
313
305
|
return false
|
|
314
306
|
|
|
315
307
|
let $start = old.resolve(start)
|
|
308
|
+
|
|
309
|
+
// Handle the case where, rather than joining blocks, the change just removed an entire block
|
|
310
|
+
if (!$newStart.parent.isTextblock) {
|
|
311
|
+
let after = $start.nodeAfter
|
|
312
|
+
return after != null && end == start + after.nodeSize
|
|
313
|
+
}
|
|
314
|
+
|
|
316
315
|
// Start must be at the end of a block
|
|
317
316
|
if ($start.parentOffset < $start.parent.content.size || !$start.parent.isTextblock)
|
|
318
317
|
return false
|
package/src/domcoords.ts
CHANGED
|
@@ -6,6 +6,11 @@ import {EditorView} from "./index"
|
|
|
6
6
|
export type Rect = {left: number, right: number, top: number, bottom: number}
|
|
7
7
|
|
|
8
8
|
function windowRect(doc: Document): Rect {
|
|
9
|
+
let vp = doc.defaultView && doc.defaultView.visualViewport
|
|
10
|
+
if (vp) return {
|
|
11
|
+
left: 0, right: vp.width,
|
|
12
|
+
top: 0, bottom: vp.height
|
|
13
|
+
}
|
|
9
14
|
return {left: 0, right: doc.documentElement.clientWidth,
|
|
10
15
|
top: 0, bottom: doc.documentElement.clientHeight}
|
|
11
16
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {selectionToDOM, anchorInRightPlace, syncNodeSelection} from "./selection
|
|
|
10
10
|
import {Decoration, viewDecorations, DecorationSource} from "./decoration"
|
|
11
11
|
import {DOMObserver, safariShadowSelectionRange} from "./domobserver"
|
|
12
12
|
import {readDOMChange} from "./domchange"
|
|
13
|
-
import {DOMSelection, DOMNode, DOMSelectionRange, deepActiveElement} from "./dom"
|
|
13
|
+
import {DOMSelection, DOMNode, DOMSelectionRange, deepActiveElement, clearReusedRange} from "./dom"
|
|
14
14
|
import * as browser from "./browser"
|
|
15
15
|
|
|
16
16
|
export {Decoration, DecorationSet, DecorationAttrs, DecorationSource} from "./decoration"
|
|
@@ -198,7 +198,7 @@ export class EditorView {
|
|
|
198
198
|
// did write to the node.
|
|
199
199
|
let chromeKludge = browser.chrome ? (this.trackWrites = this.domSelectionRange().focusNode) : null
|
|
200
200
|
if (redraw || !this.docView.update(state.doc, outerDeco, innerDeco, this)) {
|
|
201
|
-
this.docView.updateOuterDeco(
|
|
201
|
+
this.docView.updateOuterDeco(outerDeco)
|
|
202
202
|
this.docView.destroy()
|
|
203
203
|
this.docView = docViewDesc(state.doc, outerDeco, innerDeco, this.dom, this)
|
|
204
204
|
}
|
|
@@ -457,6 +457,7 @@ export class EditorView {
|
|
|
457
457
|
}
|
|
458
458
|
this.docView.destroy()
|
|
459
459
|
;(this as any).docView = null
|
|
460
|
+
clearReusedRange();
|
|
460
461
|
}
|
|
461
462
|
|
|
462
463
|
/// This is true when the view has been
|
package/src/viewdesc.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {DOMSerializer, Fragment, Mark, Node, ParseRule} from "prosemirror-model"
|
|
2
2
|
import {TextSelection} from "prosemirror-state"
|
|
3
3
|
|
|
4
|
-
import {domIndex, isEquivalentPosition,
|
|
4
|
+
import {domIndex, isEquivalentPosition, DOMNode, textNodeBefore, textNodeAfter} from "./dom"
|
|
5
5
|
import * as browser from "./browser"
|
|
6
6
|
import {Decoration, DecorationSource, WidgetConstructor, WidgetType, NodeType} from "./decoration"
|
|
7
7
|
import {EditorView} from "./index"
|
|
@@ -760,7 +760,14 @@ export class NodeViewDesc extends ViewDesc {
|
|
|
760
760
|
let {from, to} = view.state.selection
|
|
761
761
|
if (!(view.state.selection instanceof TextSelection) || from < pos || to > pos + this.node.content.size) return null
|
|
762
762
|
let sel = view.domSelectionRange()
|
|
763
|
-
let
|
|
763
|
+
let textBefore = textNodeBefore(sel.focusNode!, sel.focusOffset)
|
|
764
|
+
let textAfter = textNodeAfter(sel.focusNode!, sel.focusOffset)
|
|
765
|
+
let textNode = textBefore || textAfter
|
|
766
|
+
if (textBefore && textAfter && textBefore != textAfter) {
|
|
767
|
+
let descAfter = textAfter.pmViewDesc
|
|
768
|
+
if (!descAfter || descAfter instanceof TextViewDesc && descAfter.node.text != textAfter.nodeValue)
|
|
769
|
+
textNode = textAfter
|
|
770
|
+
}
|
|
764
771
|
if (!textNode || !this.dom.contains(textNode.parentNode)) return null
|
|
765
772
|
|
|
766
773
|
if (this.node.inlineContent) {
|
|
@@ -1470,23 +1477,6 @@ function iosHacks(dom: HTMLElement) {
|
|
|
1470
1477
|
}
|
|
1471
1478
|
}
|
|
1472
1479
|
|
|
1473
|
-
function nearbyTextNode(node: DOMNode, offset: number): Text | null {
|
|
1474
|
-
for (;;) {
|
|
1475
|
-
if (node.nodeType == 3) return node as Text
|
|
1476
|
-
if (node.nodeType == 1 && offset > 0) {
|
|
1477
|
-
if (node.childNodes.length > offset && node.childNodes[offset].nodeType == 3)
|
|
1478
|
-
return node.childNodes[offset] as Text
|
|
1479
|
-
node = node.childNodes[offset - 1]
|
|
1480
|
-
offset = nodeSize(node)
|
|
1481
|
-
} else if (node.nodeType == 1 && offset < node.childNodes.length) {
|
|
1482
|
-
node = node.childNodes[offset]
|
|
1483
|
-
offset = 0
|
|
1484
|
-
} else {
|
|
1485
|
-
return null
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
1480
|
// Find a piece of text in an inline fragment, overlapping from-to
|
|
1491
1481
|
function findTextInFragment(frag: Fragment, text: string, from: number, to: number) {
|
|
1492
1482
|
for (let i = 0, pos = 0; i < frag.childCount && pos <= to;) {
|