prosemirror-view 1.39.2 → 1.40.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.
package/src/domchange.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {Fragment, DOMParser, TagParseRule, Node, Mark, ResolvedPos} from "prosemirror-model"
2
- import {Selection, TextSelection} from "prosemirror-state"
2
+ import {Selection, TextSelection, Transaction} from "prosemirror-state"
3
3
 
4
4
  import {selectionBetween, selectionFromDOM, selectionToDOM} from "./selection"
5
5
  import {selectionCollapsed, keyEvent, DOMNode} from "./dom"
@@ -223,7 +223,27 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
223
223
 
224
224
  let chFrom = change.start, chTo = change.endA
225
225
 
226
- let tr, storedMarks, markChange
226
+ let mkTr = (base?: Transaction) => {
227
+ let tr = base || view.state.tr.replace(chFrom, chTo, parse.doc.slice(change!.start - parse.from,
228
+ change!.endB - parse.from))
229
+ if (parse.sel) {
230
+ let sel = resolveSelection(view, tr.doc, parse.sel)
231
+ // Chrome will sometimes, during composition, report the
232
+ // selection in the wrong place. If it looks like that is
233
+ // happening, don't update the selection.
234
+ // Edge just doesn't move the cursor forward when you start typing
235
+ // in an empty block or between br nodes.
236
+ if (sel && !(browser.chrome && view.composing && sel.empty &&
237
+ (change!.start != change!.endB || view.input.lastChromeDelete < Date.now() - 100) &&
238
+ (sel.head == chFrom || sel.head == tr.mapping.map(chTo) - 1) ||
239
+ browser.ie && sel.empty && sel.head == chFrom))
240
+ tr.setSelection(sel)
241
+ }
242
+ if (compositionID) tr.setMeta("composition", compositionID)
243
+ return tr.scrollIntoView()
244
+ }
245
+
246
+ let markChange
227
247
  if (inlineChange) {
228
248
  if ($from.pos == $to.pos) { // Deletion
229
249
  // IE11 sometimes weirdly moves the DOM selection around after
@@ -232,42 +252,29 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
232
252
  view.domObserver.suppressSelectionUpdates()
233
253
  setTimeout(() => selectionToDOM(view), 20)
234
254
  }
235
- tr = view.state.tr.delete(chFrom, chTo)
236
- storedMarks = doc.resolve(change.start).marksAcross(doc.resolve(change.endA))
255
+ let tr = mkTr(view.state.tr.delete(chFrom, chTo))
256
+ let marks = doc.resolve(change.start).marksAcross(doc.resolve(change.endA))
257
+ if (marks) tr.ensureMarks(marks)
258
+ view.dispatch(tr)
237
259
  } else if ( // Adding or removing a mark
238
260
  change.endA == change.endB &&
239
261
  (markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset),
240
262
  $fromA.parent.content.cut($fromA.parentOffset, change.endA - $fromA.start())))
241
263
  ) {
242
- tr = view.state.tr
264
+ let tr = mkTr(view.state.tr)
243
265
  if (markChange.type == "add") tr.addMark(chFrom, chTo, markChange.mark)
244
266
  else tr.removeMark(chFrom, chTo, markChange.mark)
267
+ view.dispatch(tr)
245
268
  } else if ($from.parent.child($from.index()).isText && $from.index() == $to.index() - ($to.textOffset ? 0 : 1)) {
246
269
  // Both positions in the same text node -- simply insert text
247
270
  let text = $from.parent.textBetween($from.parentOffset, $to.parentOffset)
248
- if (view.someProp("handleTextInput", f => f(view, chFrom, chTo, text))) return
249
- tr = view.state.tr.insertText(text, chFrom, chTo)
271
+ let deflt = () => mkTr(view.state.tr.insertText(text, chFrom, chTo))
272
+ if (!view.someProp("handleTextInput", f => f(view, chFrom, chTo, text, deflt)))
273
+ view.dispatch(deflt())
250
274
  }
275
+ } else {
276
+ view.dispatch(mkTr())
251
277
  }
252
-
253
- if (!tr)
254
- tr = view.state.tr.replace(chFrom, chTo, parse.doc.slice(change.start - parse.from, change.endB - parse.from))
255
- if (parse.sel) {
256
- let sel = resolveSelection(view, tr.doc, parse.sel)
257
- // Chrome will sometimes, during composition, report the
258
- // selection in the wrong place. If it looks like that is
259
- // happening, don't update the selection.
260
- // Edge just doesn't move the cursor forward when you start typing
261
- // in an empty block or between br nodes.
262
- if (sel && !(browser.chrome && view.composing && sel.empty &&
263
- (change.start != change.endB || view.input.lastChromeDelete < Date.now() - 100) &&
264
- (sel.head == chFrom || sel.head == tr.mapping.map(chTo) - 1) ||
265
- browser.ie && sel.empty && sel.head == chFrom))
266
- tr.setSelection(sel)
267
- }
268
- if (storedMarks) tr.ensureMarks(storedMarks)
269
- if (compositionID) tr.setMeta("composition", compositionID)
270
- view.dispatch(tr.scrollIntoView())
271
278
  }
272
279
 
273
280
  function resolveSelection(view: EditorView, doc: Node, parsedSel: {anchor: number, head: number}) {
package/src/index.ts CHANGED
@@ -28,7 +28,8 @@ export const __endComposition = endComposition
28
28
  /// editable document. Its state and behavior are determined by its
29
29
  /// [props](#view.DirectEditorProps).
30
30
  export class EditorView {
31
- private _props: DirectEditorProps
31
+ /// @internal
32
+ _props: DirectEditorProps
32
33
  private directPlugins: readonly Plugin[]
33
34
  private _root: Document | ShadowRoot | null = null
34
35
  /// @internal
@@ -490,11 +491,7 @@ export class EditorView {
490
491
  /// [`updateState`](#view.EditorView.updateState) with the result.
491
492
  /// This method is bound to the view instance, so that it can be
492
493
  /// easily passed around.
493
- dispatch(tr: Transaction) {
494
- let dispatchTransaction = this._props.dispatchTransaction
495
- if (dispatchTransaction) dispatchTransaction.call(this, tr)
496
- else this.updateState(this.state.apply(tr))
497
- }
494
+ declare dispatch: (tr: Transaction) => void
498
495
 
499
496
  /// @internal
500
497
  domSelectionRange(): DOMSelectionRange {
@@ -510,6 +507,12 @@ export class EditorView {
510
507
  }
511
508
  }
512
509
 
510
+ EditorView.prototype.dispatch = function(tr: Transaction) {
511
+ let dispatchTransaction = this._props.dispatchTransaction
512
+ if (dispatchTransaction) dispatchTransaction.call(this, tr)
513
+ else this.updateState(this.state.apply(tr))
514
+ }
515
+
513
516
  function computeDocDeco(view: EditorView) {
514
517
  let attrs = Object.create(null)
515
518
  attrs.class = "ProseMirror"
@@ -635,7 +638,7 @@ export interface EditorProps<P = any> {
635
638
  /// Whenever the user directly input text, this handler is called
636
639
  /// before the input is applied. If it returns `true`, the default
637
640
  /// behavior of actually inserting the text is suppressed.
638
- handleTextInput?: (this: P, view: EditorView, from: number, to: number, text: string) => boolean | void
641
+ handleTextInput?: (this: P, view: EditorView, from: number, to: number, text: string, deflt: () => Transaction) => boolean | void
639
642
 
640
643
  /// Called for each node around a click, from the inside out. The
641
644
  /// `direct` flag will be true for the inner node.
package/src/input.ts CHANGED
@@ -21,7 +21,7 @@ export class InputState {
21
21
  mouseDown: MouseDown | null = null
22
22
  lastKeyCode: number | null = null
23
23
  lastKeyCodeTime = 0
24
- lastClick = {time: 0, x: 0, y: 0, type: ""}
24
+ lastClick = {time: 0, x: 0, y: 0, type: "", button: 0}
25
25
  lastSelectionOrigin: string | null = null
26
26
  lastSelectionTime = 0
27
27
  lastIOSEnter = 0
@@ -151,8 +151,9 @@ editHandlers.keypress = (view, _event) => {
151
151
  let sel = view.state.selection
152
152
  if (!(sel instanceof TextSelection) || !sel.$from.sameParent(sel.$to)) {
153
153
  let text = String.fromCharCode(event.charCode)
154
- if (!/[\r\n]/.test(text) && !view.someProp("handleTextInput", f => f(view, sel.$from.pos, sel.$to.pos, text)))
155
- view.dispatch(view.state.tr.insertText(text).scrollIntoView())
154
+ let deflt = () => view.state.tr.insertText(text).scrollIntoView()
155
+ if (!/[\r\n]/.test(text) && !view.someProp("handleTextInput", f => f(view, sel.$from.pos, sel.$to.pos, text, deflt)))
156
+ view.dispatch(deflt())
156
157
  event.preventDefault()
157
158
  }
158
159
  }
@@ -278,11 +279,12 @@ handlers.mousedown = (view, _event) => {
278
279
  view.input.shiftKey = event.shiftKey
279
280
  let flushed = forceDOMFlush(view)
280
281
  let now = Date.now(), type = "singleClick"
281
- if (now - view.input.lastClick.time < 500 && isNear(event, view.input.lastClick) && !event[selectNodeModifier]) {
282
+ if (now - view.input.lastClick.time < 500 && isNear(event, view.input.lastClick) && !event[selectNodeModifier] &&
283
+ view.input.lastClick.button == event.button) {
282
284
  if (view.input.lastClick.type == "singleClick") type = "doubleClick"
283
285
  else if (view.input.lastClick.type == "doubleClick") type = "tripleClick"
284
286
  }
285
- view.input.lastClick = {time: now, x: event.clientX, y: event.clientY, type}
287
+ view.input.lastClick = {time: now, x: event.clientX, y: event.clientY, type, button: event.button}
286
288
 
287
289
  let pos = view.posAtCoords(eventCoords(event))
288
290
  if (!pos) return
@@ -544,9 +546,9 @@ export function endComposition(view: EditorView, restarting = false) {
544
546
  view.domObserver.forceFlush()
545
547
  clearComposition(view)
546
548
  if (restarting || view.docView && view.docView.dirty) {
547
- let sel = selectionFromDOM(view)
548
- if (sel && !sel.eq(view.state.selection)) view.dispatch(view.state.tr.setSelection(sel))
549
- else if ((view.markCursor || restarting) && !view.state.selection.empty) view.dispatch(view.state.tr.deleteSelection())
549
+ let sel = selectionFromDOM(view), cur = view.state.selection
550
+ if (sel && !sel.eq(cur)) view.dispatch(view.state.tr.setSelection(sel))
551
+ else if ((view.markCursor || restarting) && !cur.$from.node(cur.$from.sharedDepth(cur.to)).inlineContent) view.dispatch(view.state.tr.deleteSelection())
550
552
  else view.updateState(view.state)
551
553
  return true
552
554
  }
package/src/viewdesc.ts CHANGED
@@ -528,6 +528,8 @@ export class ViewDesc {
528
528
 
529
529
  get ignoreForCoords() { return false }
530
530
 
531
+ get ignoreForSelection() { return false }
532
+
531
533
  isText(text: string) { return false }
532
534
  }
533
535
 
@@ -576,6 +578,8 @@ class WidgetViewDesc extends ViewDesc {
576
578
 
577
579
  get domAtom() { return true }
578
580
 
581
+ get ignoreForSelection() { return !!this.widget.type.spec.relaxedSide }
582
+
579
583
  get side() { return (this.widget.type as any).side as number }
580
584
  }
581
585