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/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
- looksLikeJoin(doc, change.start, change.endA, $from, $to) &&
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 looksLikeJoin(old: Node, start: number, end: number, $newStart: ResolvedPos, $newEnd: ResolvedPos) {
308
- if (!$newStart.parent.isTextblock ||
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, nodeSize, DOMNode} from "./dom"
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 textNode = nearbyTextNode(sel.focusNode!, sel.focusOffset)
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;) {