prosemirror-view 1.26.3 → 1.27.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/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ ## 1.27.0 (2022-07-20)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug that in some situations caused composition to be disrupted on Chrome due to a workaround for another browser issue activating inappropriately.
6
+
7
+ ### New features
8
+
9
+ `EditorProps` now takes an optional type parameter that indicates the type of `this` in prop functions.
10
+
11
+ ## 1.26.5 (2022-07-07)
12
+
13
+ ### Bug fixes
14
+
15
+ Fix an issue where a composition happening at the same time as widget insertion/removal could corrupt the rendered DOM.
16
+
17
+ Fix a problem where `coordsAtPos` would look inside the DOM for atomic nodes, and sometimes return very odd positions that way.
18
+
19
+ ## 1.26.4 (2022-07-04)
20
+
21
+ ### Bug fixes
22
+
23
+ Avoid spurious type errors on `handleDOMEvents` by widening the type for unknown event objects.
24
+
1
25
  ## 1.26.3 (2022-06-29)
2
26
 
3
27
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -495,7 +495,8 @@ var BIDI = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
495
495
  function _coordsAtPos(view, pos, side) {
496
496
  var _view$docView$domFrom = view.docView.domFromPos(pos, side < 0 ? -1 : 1),
497
497
  node = _view$docView$domFrom.node,
498
- offset = _view$docView$domFrom.offset;
498
+ offset = _view$docView$domFrom.offset,
499
+ atom = _view$docView$domFrom.atom;
499
500
 
500
501
  var supportEmptyRange = webkit || gecko;
501
502
 
@@ -534,13 +535,15 @@ function _coordsAtPos(view, pos, side) {
534
535
  }
535
536
  }
536
537
 
537
- if (!view.state.doc.resolve(pos).parent.inlineContent) {
538
- if (offset && (side < 0 || offset == nodeSize(node))) {
538
+ var $dom = view.state.doc.resolve(pos - (atom || 0));
539
+
540
+ if (!$dom.parent.inlineContent) {
541
+ if (atom == null && offset && (side < 0 || offset == nodeSize(node))) {
539
542
  var before = node.childNodes[offset - 1];
540
543
  if (before.nodeType == 1) return flattenH(before.getBoundingClientRect(), false);
541
544
  }
542
545
 
543
- if (offset < nodeSize(node)) {
546
+ if (atom == null && offset < nodeSize(node)) {
544
547
  var after = node.childNodes[offset];
545
548
  if (after.nodeType == 1) return flattenH(after.getBoundingClientRect(), true);
546
549
  }
@@ -548,13 +551,13 @@ function _coordsAtPos(view, pos, side) {
548
551
  return flattenH(node.getBoundingClientRect(), side >= 0);
549
552
  }
550
553
 
551
- if (offset && (side < 0 || offset == nodeSize(node))) {
554
+ if (atom == null && offset && (side < 0 || offset == nodeSize(node))) {
552
555
  var _before = node.childNodes[offset - 1];
553
556
  var target = _before.nodeType == 3 ? textRange(_before, nodeSize(_before) - (supportEmptyRange ? 0 : 1)) : _before.nodeType == 1 && (_before.nodeName != "BR" || !_before.nextSibling) ? _before : null;
554
557
  if (target) return flattenV(singleRect(target, 1), false);
555
558
  }
556
559
 
557
- if (offset < nodeSize(node)) {
560
+ if (atom == null && offset < nodeSize(node)) {
558
561
  var _after = node.childNodes[offset];
559
562
 
560
563
  while (_after.pmViewDesc && _after.pmViewDesc.ignoreForCoords) {
@@ -905,7 +908,8 @@ var ViewDesc = function () {
905
908
  value: function domFromPos(pos, side) {
906
909
  if (!this.contentDOM) return {
907
910
  node: this.dom,
908
- offset: 0
911
+ offset: 0,
912
+ atom: pos + 1
909
913
  };
910
914
  var i = 0,
911
915
  offset = 0;
@@ -2077,7 +2081,7 @@ var ViewTreeUpdater = function () {
2077
2081
  if (child.dirty == NODE_DIRTY && child.dom == child.contentDOM) child.dirty = CONTENT_DIRTY;
2078
2082
  if (!child.update(node, outerDeco, innerDeco, view)) return false;
2079
2083
  this.destroyBetween(this.index, index);
2080
- this.index = index + 1;
2084
+ this.index++;
2081
2085
  return true;
2082
2086
  }
2083
2087
  }, {
@@ -5045,7 +5049,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
5045
5049
  var sel = view.state.selection;
5046
5050
  var parse = parseBetween(view, from, to);
5047
5051
 
5048
- if (chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from) {
5052
+ if (chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from && parse.sel.head == parse.sel.anchor) {
5049
5053
  var text = view.cursorWrapper.deco.type.toDOM.nextSibling;
5050
5054
  var size = text && text.nodeValue ? text.nodeValue.length : 1;
5051
5055
  parse.sel = {
package/dist/index.d.ts CHANGED
@@ -326,6 +326,7 @@ declare class ViewDesc {
326
326
  domFromPos(pos: number, side: number): {
327
327
  node: DOMNode;
328
328
  offset: number;
329
+ atom?: number;
329
330
  };
330
331
  parseRange(from: number, to: number, base?: number): {
331
332
  node: DOMNode;
@@ -563,7 +564,7 @@ includes events that TypeScript's HTMLElementEventMap doesn't know
563
564
  about.
564
565
  */
565
566
  interface DOMEventMap extends HTMLElementEventMap {
566
- [event: string]: Event;
567
+ [event: string]: any;
567
568
  }
568
569
  /**
569
570
  Props are configuration values that can be passed to an editor view
@@ -579,8 +580,12 @@ called one at a time, starting with the base props and then
579
580
  searching through the plugins (in order of appearance) until one of
580
581
  them returns true. For some props, the first plugin that yields a
581
582
  value gets precedence.
583
+
584
+ The optional type parameter refers to the type of `this` in prop
585
+ functions, and is used to pass in the plugin type when defining a
586
+ [plugin](https://prosemirror.net/docs/ref/#state.Plugin).
582
587
  */
583
- interface EditorProps {
588
+ interface EditorProps<P = any> {
584
589
  /**
585
590
  Can be an object mapping DOM event type names to functions that
586
591
  handle them. Such functions will be called before any handling
@@ -591,72 +596,72 @@ interface EditorProps {
591
596
  default behavior).
592
597
  */
593
598
  handleDOMEvents?: {
594
- [event in keyof DOMEventMap]?: (view: EditorView, event: DOMEventMap[event]) => boolean | void;
599
+ [event in keyof DOMEventMap]?: (this: P, view: EditorView, event: DOMEventMap[event]) => boolean | void;
595
600
  };
596
601
  /**
597
602
  Called when the editor receives a `keydown` event.
598
603
  */
599
- handleKeyDown?: (view: EditorView, event: KeyboardEvent) => boolean | void;
604
+ handleKeyDown?: (this: P, view: EditorView, event: KeyboardEvent) => boolean | void;
600
605
  /**
601
606
  Handler for `keypress` events.
602
607
  */
603
- handleKeyPress?: (view: EditorView, event: KeyboardEvent) => boolean | void;
608
+ handleKeyPress?: (this: P, view: EditorView, event: KeyboardEvent) => boolean | void;
604
609
  /**
605
610
  Whenever the user directly input text, this handler is called
606
611
  before the input is applied. If it returns `true`, the default
607
612
  behavior of actually inserting the text is suppressed.
608
613
  */
609
- handleTextInput?: (view: EditorView, from: number, to: number, text: string) => boolean | void;
614
+ handleTextInput?: (this: P, view: EditorView, from: number, to: number, text: string) => boolean | void;
610
615
  /**
611
616
  Called for each node around a click, from the inside out. The
612
617
  `direct` flag will be true for the inner node.
613
618
  */
614
- handleClickOn?: (view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void;
619
+ handleClickOn?: (this: P, view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void;
615
620
  /**
616
621
  Called when the editor is clicked, after `handleClickOn` handlers
617
622
  have been called.
618
623
  */
619
- handleClick?: (view: EditorView, pos: number, event: MouseEvent) => boolean | void;
624
+ handleClick?: (this: P, view: EditorView, pos: number, event: MouseEvent) => boolean | void;
620
625
  /**
621
626
  Called for each node around a double click.
622
627
  */
623
- handleDoubleClickOn?: (view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void;
628
+ handleDoubleClickOn?: (this: P, view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void;
624
629
  /**
625
630
  Called when the editor is double-clicked, after `handleDoubleClickOn`.
626
631
  */
627
- handleDoubleClick?: (view: EditorView, pos: number, event: MouseEvent) => boolean | void;
632
+ handleDoubleClick?: (this: P, view: EditorView, pos: number, event: MouseEvent) => boolean | void;
628
633
  /**
629
634
  Called for each node around a triple click.
630
635
  */
631
- handleTripleClickOn?: (view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void;
636
+ handleTripleClickOn?: (this: P, view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void;
632
637
  /**
633
638
  Called when the editor is triple-clicked, after `handleTripleClickOn`.
634
639
  */
635
- handleTripleClick?: (view: EditorView, pos: number, event: MouseEvent) => boolean | void;
640
+ handleTripleClick?: (this: P, view: EditorView, pos: number, event: MouseEvent) => boolean | void;
636
641
  /**
637
642
  Can be used to override the behavior of pasting. `slice` is the
638
643
  pasted content parsed by the editor, but you can directly access
639
644
  the event to get at the raw content.
640
645
  */
641
- handlePaste?: (view: EditorView, event: ClipboardEvent, slice: Slice) => boolean | void;
646
+ handlePaste?: (this: P, view: EditorView, event: ClipboardEvent, slice: Slice) => boolean | void;
642
647
  /**
643
648
  Called when something is dropped on the editor. `moved` will be
644
649
  true if this drop moves from the current selection (which should
645
650
  thus be deleted).
646
651
  */
647
- handleDrop?: (view: EditorView, event: DragEvent, slice: Slice, moved: boolean) => boolean | void;
652
+ handleDrop?: (this: P, view: EditorView, event: DragEvent, slice: Slice, moved: boolean) => boolean | void;
648
653
  /**
649
654
  Called when the view, after updating its state, tries to scroll
650
655
  the selection into view. A handler function may return false to
651
656
  indicate that it did not handle the scrolling and further
652
657
  handlers or the default behavior should be tried.
653
658
  */
654
- handleScrollToSelection?: (view: EditorView) => boolean;
659
+ handleScrollToSelection?: (this: P, view: EditorView) => boolean;
655
660
  /**
656
661
  Can be used to override the way a selection is created when
657
662
  reading a DOM selection between the given anchor and head.
658
663
  */
659
- createSelectionBetween?: (view: EditorView, anchor: ResolvedPos, head: ResolvedPos) => Selection | null;
664
+ createSelectionBetween?: (this: P, view: EditorView, anchor: ResolvedPos, head: ResolvedPos) => Selection | null;
660
665
  /**
661
666
  The [parser](https://prosemirror.net/docs/ref/#model.DOMParser) to use when reading editor changes
662
667
  from the DOM. Defaults to calling
@@ -668,7 +673,7 @@ interface EditorProps {
668
673
  Can be used to transform pasted HTML text, _before_ it is parsed,
669
674
  for example to clean it up.
670
675
  */
671
- transformPastedHTML?: (html: string) => string;
676
+ transformPastedHTML?: (this: P, html: string) => string;
672
677
  /**
673
678
  The [parser](https://prosemirror.net/docs/ref/#model.DOMParser) to use when reading content from
674
679
  the clipboard. When not given, the value of the
@@ -679,7 +684,7 @@ interface EditorProps {
679
684
  Transform pasted plain text. The `plain` flag will be true when
680
685
  the text is pasted as plain text.
681
686
  */
682
- transformPastedText?: (text: string, plain: boolean) => string;
687
+ transformPastedText?: (this: P, text: string, plain: boolean) => string;
683
688
  /**
684
689
  A function to parse text from the clipboard into a document
685
690
  slice. Called after
@@ -689,12 +694,12 @@ interface EditorProps {
689
694
  [`clipboardParser`](https://prosemirror.net/docs/ref/#view.EditorProps.clipboardParser) on it.
690
695
  The `plain` flag will be true when the text is pasted as plain text.
691
696
  */
692
- clipboardTextParser?: (text: string, $context: ResolvedPos, plain: boolean) => Slice;
697
+ clipboardTextParser?: (this: P, text: string, $context: ResolvedPos, plain: boolean) => Slice;
693
698
  /**
694
699
  Can be used to transform pasted content before it is applied to
695
700
  the document.
696
701
  */
697
- transformPasted?: (slice: Slice) => Slice;
702
+ transformPasted?: (this: P, slice: Slice) => Slice;
698
703
  /**
699
704
  Allows you to pass custom rendering and behavior logic for
700
705
  nodes. Should map node names to constructor functions that
@@ -749,17 +754,17 @@ interface EditorProps {
749
754
  editor will use [`textBetween`](https://prosemirror.net/docs/ref/#model.Node.textBetween) on the
750
755
  selected range.
751
756
  */
752
- clipboardTextSerializer?: (content: Slice) => string;
757
+ clipboardTextSerializer?: (this: P, content: Slice) => string;
753
758
  /**
754
759
  A set of [document decorations](https://prosemirror.net/docs/ref/#view.Decoration) to show in the
755
760
  view.
756
761
  */
757
- decorations?: (state: EditorState) => DecorationSource | null | undefined;
762
+ decorations?: (this: P, state: EditorState) => DecorationSource | null | undefined;
758
763
  /**
759
764
  When this returns false, the content of the view is not directly
760
765
  editable.
761
766
  */
762
- editable?: (state: EditorState) => boolean;
767
+ editable?: (this: P, state: EditorState) => boolean;
763
768
  /**
764
769
  Control the DOM attributes of the editable element. May be either
765
770
  an object or a function going from an editor state to an object.
package/dist/index.js CHANGED
@@ -422,7 +422,7 @@ const BIDI = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
422
422
  // Given a position in the document model, get a bounding box of the
423
423
  // character at that position, relative to the window.
424
424
  function coordsAtPos(view, pos, side) {
425
- let { node, offset } = view.docView.domFromPos(pos, side < 0 ? -1 : 1);
425
+ let { node, offset, atom } = view.docView.domFromPos(pos, side < 0 ? -1 : 1);
426
426
  let supportEmptyRange = webkit || gecko;
427
427
  if (node.nodeType == 3) {
428
428
  // These browsers support querying empty text ranges. Prefer that in
@@ -461,14 +461,15 @@ function coordsAtPos(view, pos, side) {
461
461
  return flattenV(singleRect(textRange(node, from, to), takeSide), takeSide < 0);
462
462
  }
463
463
  }
464
+ let $dom = view.state.doc.resolve(pos - (atom || 0));
464
465
  // Return a horizontal line in block context
465
- if (!view.state.doc.resolve(pos).parent.inlineContent) {
466
- if (offset && (side < 0 || offset == nodeSize(node))) {
466
+ if (!$dom.parent.inlineContent) {
467
+ if (atom == null && offset && (side < 0 || offset == nodeSize(node))) {
467
468
  let before = node.childNodes[offset - 1];
468
469
  if (before.nodeType == 1)
469
470
  return flattenH(before.getBoundingClientRect(), false);
470
471
  }
471
- if (offset < nodeSize(node)) {
472
+ if (atom == null && offset < nodeSize(node)) {
472
473
  let after = node.childNodes[offset];
473
474
  if (after.nodeType == 1)
474
475
  return flattenH(after.getBoundingClientRect(), true);
@@ -476,7 +477,7 @@ function coordsAtPos(view, pos, side) {
476
477
  return flattenH(node.getBoundingClientRect(), side >= 0);
477
478
  }
478
479
  // Inline, not in text node (this is not Bidi-safe)
479
- if (offset && (side < 0 || offset == nodeSize(node))) {
480
+ if (atom == null && offset && (side < 0 || offset == nodeSize(node))) {
480
481
  let before = node.childNodes[offset - 1];
481
482
  let target = before.nodeType == 3 ? textRange(before, nodeSize(before) - (supportEmptyRange ? 0 : 1))
482
483
  // BR nodes tend to only return the rectangle before them.
@@ -485,7 +486,7 @@ function coordsAtPos(view, pos, side) {
485
486
  if (target)
486
487
  return flattenV(singleRect(target, 1), false);
487
488
  }
488
- if (offset < nodeSize(node)) {
489
+ if (atom == null && offset < nodeSize(node)) {
489
490
  let after = node.childNodes[offset];
490
491
  while (after.pmViewDesc && after.pmViewDesc.ignoreForCoords)
491
492
  after = after.nextSibling;
@@ -797,7 +798,7 @@ class ViewDesc {
797
798
  }
798
799
  domFromPos(pos, side) {
799
800
  if (!this.contentDOM)
800
- return { node: this.dom, offset: 0 };
801
+ return { node: this.dom, offset: 0, atom: pos + 1 };
801
802
  // First find the position in the child array
802
803
  let i = 0, offset = 0;
803
804
  for (let curPos = 0; i < this.children.length; i++) {
@@ -1723,7 +1724,7 @@ class ViewTreeUpdater {
1723
1724
  if (!child.update(node, outerDeco, innerDeco, view))
1724
1725
  return false;
1725
1726
  this.destroyBetween(this.index, index);
1726
- this.index = index + 1;
1727
+ this.index++;
1727
1728
  return true;
1728
1729
  }
1729
1730
  findIndexWithChild(domNode) {
@@ -4500,7 +4501,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
4500
4501
  let parse = parseBetween(view, from, to);
4501
4502
  // Chrome sometimes leaves the cursor before the inserted text when
4502
4503
  // composing after a cursor wrapper. This moves it forward.
4503
- if (chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from) {
4504
+ if (chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from &&
4505
+ parse.sel.head == parse.sel.anchor) {
4504
4506
  let text = view.cursorWrapper.deco.type.toDOM.nextSibling;
4505
4507
  let size = text && text.nodeValue ? text.nodeValue.length : 1;
4506
4508
  parse.sel = { anchor: parse.sel.anchor + size, head: parse.sel.anchor + size };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-view",
3
- "version": "1.26.3",
3
+ "version": "1.27.0",
4
4
  "description": "ProseMirror's view component",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
package/src/domchange.ts CHANGED
@@ -99,7 +99,8 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
99
99
 
100
100
  // Chrome sometimes leaves the cursor before the inserted text when
101
101
  // composing after a cursor wrapper. This moves it forward.
102
- if (browser.chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from) {
102
+ if (browser.chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from &&
103
+ parse.sel.head == parse.sel.anchor) {
103
104
  let text = (view.cursorWrapper.deco.type as any).toDOM.nextSibling as DOMNode
104
105
  let size = text && text.nodeValue ? text.nodeValue.length : 1
105
106
  parse.sel = {anchor: parse.sel.anchor + size, head: parse.sel.anchor + size}
package/src/domcoords.ts CHANGED
@@ -315,7 +315,7 @@ const BIDI = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
315
315
  // Given a position in the document model, get a bounding box of the
316
316
  // character at that position, relative to the window.
317
317
  export function coordsAtPos(view: EditorView, pos: number, side: number): Rect {
318
- let {node, offset} = view.docView.domFromPos(pos, side < 0 ? -1 : 1)
318
+ let {node, offset, atom} = view.docView.domFromPos(pos, side < 0 ? -1 : 1)
319
319
 
320
320
  let supportEmptyRange = browser.webkit || browser.gecko
321
321
  if (node.nodeType == 3) {
@@ -345,13 +345,14 @@ export function coordsAtPos(view: EditorView, pos: number, side: number): Rect {
345
345
  }
346
346
  }
347
347
 
348
+ let $dom = view.state.doc.resolve(pos - (atom || 0))
348
349
  // Return a horizontal line in block context
349
- if (!view.state.doc.resolve(pos).parent.inlineContent) {
350
- if (offset && (side < 0 || offset == nodeSize(node))) {
350
+ if (!$dom.parent.inlineContent) {
351
+ if (atom == null && offset && (side < 0 || offset == nodeSize(node))) {
351
352
  let before = node.childNodes[offset - 1]
352
353
  if (before.nodeType == 1) return flattenH((before as HTMLElement).getBoundingClientRect(), false)
353
354
  }
354
- if (offset < nodeSize(node)) {
355
+ if (atom == null && offset < nodeSize(node)) {
355
356
  let after = node.childNodes[offset]
356
357
  if (after.nodeType == 1) return flattenH((after as HTMLElement).getBoundingClientRect(), true)
357
358
  }
@@ -359,7 +360,7 @@ export function coordsAtPos(view: EditorView, pos: number, side: number): Rect {
359
360
  }
360
361
 
361
362
  // Inline, not in text node (this is not Bidi-safe)
362
- if (offset && (side < 0 || offset == nodeSize(node))) {
363
+ if (atom == null && offset && (side < 0 || offset == nodeSize(node))) {
363
364
  let before = node.childNodes[offset - 1]
364
365
  let target = before.nodeType == 3 ? textRange(before as Text, nodeSize(before) - (supportEmptyRange ? 0 : 1))
365
366
  // BR nodes tend to only return the rectangle before them.
@@ -367,7 +368,7 @@ export function coordsAtPos(view: EditorView, pos: number, side: number): Rect {
367
368
  : before.nodeType == 1 && (before.nodeName != "BR" || !before.nextSibling) ? before : null
368
369
  if (target) return flattenV(singleRect(target as Range | HTMLElement, 1), false)
369
370
  }
370
- if (offset < nodeSize(node)) {
371
+ if (atom == null && offset < nodeSize(node)) {
371
372
  let after = node.childNodes[offset]
372
373
  while (after.pmViewDesc && after.pmViewDesc.ignoreForCoords) after = after.nextSibling!
373
374
  let target = !after ? null : after.nodeType == 3 ? textRange(after as Text, 0, (supportEmptyRange ? 0 : 1))
package/src/index.ts CHANGED
@@ -512,7 +512,7 @@ type NodeViewSet = {[name: string]: NodeViewConstructor | MarkViewConstructor}
512
512
  /// includes events that TypeScript's HTMLElementEventMap doesn't know
513
513
  /// about.
514
514
  export interface DOMEventMap extends HTMLElementEventMap {
515
- [event: string]: Event
515
+ [event: string]: any
516
516
  }
517
517
 
518
518
  /// Props are configuration values that can be passed to an editor view
@@ -528,7 +528,11 @@ export interface DOMEventMap extends HTMLElementEventMap {
528
528
  /// searching through the plugins (in order of appearance) until one of
529
529
  /// them returns true. For some props, the first plugin that yields a
530
530
  /// value gets precedence.
531
- export interface EditorProps {
531
+ ///
532
+ /// The optional type parameter refers to the type of `this` in prop
533
+ /// functions, and is used to pass in the plugin type when defining a
534
+ /// [plugin](#state.Plugin).
535
+ export interface EditorProps<P = any> {
532
536
  /// Can be an object mapping DOM event type names to functions that
533
537
  /// handle them. Such functions will be called before any handling
534
538
  /// ProseMirror does of events fired on the editable DOM element.
@@ -537,59 +541,59 @@ export interface EditorProps {
537
541
  /// `preventDefault` yourself (or not, if you want to allow the
538
542
  /// default behavior).
539
543
  handleDOMEvents?: {
540
- [event in keyof DOMEventMap]?: (view: EditorView, event: DOMEventMap[event]) => boolean | void
544
+ [event in keyof DOMEventMap]?: (this: P, view: EditorView, event: DOMEventMap[event]) => boolean | void
541
545
  }
542
546
 
543
547
  /// Called when the editor receives a `keydown` event.
544
- handleKeyDown?: (view: EditorView, event: KeyboardEvent) => boolean | void
548
+ handleKeyDown?: (this: P, view: EditorView, event: KeyboardEvent) => boolean | void
545
549
 
546
550
  /// Handler for `keypress` events.
547
- handleKeyPress?: (view: EditorView, event: KeyboardEvent) => boolean | void
551
+ handleKeyPress?: (this: P, view: EditorView, event: KeyboardEvent) => boolean | void
548
552
 
549
553
  /// Whenever the user directly input text, this handler is called
550
554
  /// before the input is applied. If it returns `true`, the default
551
555
  /// behavior of actually inserting the text is suppressed.
552
- handleTextInput?: (view: EditorView, from: number, to: number, text: string) => boolean | void
556
+ handleTextInput?: (this: P, view: EditorView, from: number, to: number, text: string) => boolean | void
553
557
 
554
558
  /// Called for each node around a click, from the inside out. The
555
559
  /// `direct` flag will be true for the inner node.
556
- handleClickOn?: (view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void
560
+ handleClickOn?: (this: P, view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void
557
561
 
558
562
  /// Called when the editor is clicked, after `handleClickOn` handlers
559
563
  /// have been called.
560
- handleClick?: (view: EditorView, pos: number, event: MouseEvent) => boolean | void
564
+ handleClick?: (this: P, view: EditorView, pos: number, event: MouseEvent) => boolean | void
561
565
 
562
566
  /// Called for each node around a double click.
563
- handleDoubleClickOn?: (view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void
567
+ handleDoubleClickOn?: (this: P, view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void
564
568
 
565
569
  /// Called when the editor is double-clicked, after `handleDoubleClickOn`.
566
- handleDoubleClick?: (view: EditorView, pos: number, event: MouseEvent) => boolean | void
570
+ handleDoubleClick?: (this: P, view: EditorView, pos: number, event: MouseEvent) => boolean | void
567
571
 
568
572
  /// Called for each node around a triple click.
569
- handleTripleClickOn?: (view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void
573
+ handleTripleClickOn?: (this: P, view: EditorView, pos: number, node: Node, nodePos: number, event: MouseEvent, direct: boolean) => boolean | void
570
574
 
571
575
  /// Called when the editor is triple-clicked, after `handleTripleClickOn`.
572
- handleTripleClick?: (view: EditorView, pos: number, event: MouseEvent) => boolean | void
576
+ handleTripleClick?: (this: P, view: EditorView, pos: number, event: MouseEvent) => boolean | void
573
577
 
574
578
  /// Can be used to override the behavior of pasting. `slice` is the
575
579
  /// pasted content parsed by the editor, but you can directly access
576
580
  /// the event to get at the raw content.
577
- handlePaste?: (view: EditorView, event: ClipboardEvent, slice: Slice) => boolean | void
581
+ handlePaste?: (this: P, view: EditorView, event: ClipboardEvent, slice: Slice) => boolean | void
578
582
 
579
583
  /// Called when something is dropped on the editor. `moved` will be
580
584
  /// true if this drop moves from the current selection (which should
581
585
  /// thus be deleted).
582
- handleDrop?: (view: EditorView, event: DragEvent, slice: Slice, moved: boolean) => boolean | void
586
+ handleDrop?: (this: P, view: EditorView, event: DragEvent, slice: Slice, moved: boolean) => boolean | void
583
587
 
584
588
  /// Called when the view, after updating its state, tries to scroll
585
589
  /// the selection into view. A handler function may return false to
586
590
  /// indicate that it did not handle the scrolling and further
587
591
  /// handlers or the default behavior should be tried.
588
- handleScrollToSelection?: (view: EditorView) => boolean
592
+ handleScrollToSelection?: (this: P, view: EditorView) => boolean
589
593
 
590
594
  /// Can be used to override the way a selection is created when
591
595
  /// reading a DOM selection between the given anchor and head.
592
- createSelectionBetween?: (view: EditorView, anchor: ResolvedPos, head: ResolvedPos) => Selection | null
596
+ createSelectionBetween?: (this: P, view: EditorView, anchor: ResolvedPos, head: ResolvedPos) => Selection | null
593
597
 
594
598
  /// The [parser](#model.DOMParser) to use when reading editor changes
595
599
  /// from the DOM. Defaults to calling
@@ -599,7 +603,7 @@ export interface EditorProps {
599
603
 
600
604
  /// Can be used to transform pasted HTML text, _before_ it is parsed,
601
605
  /// for example to clean it up.
602
- transformPastedHTML?: (html: string) => string
606
+ transformPastedHTML?: (this: P, html: string) => string
603
607
 
604
608
  /// The [parser](#model.DOMParser) to use when reading content from
605
609
  /// the clipboard. When not given, the value of the
@@ -608,7 +612,7 @@ export interface EditorProps {
608
612
 
609
613
  /// Transform pasted plain text. The `plain` flag will be true when
610
614
  /// the text is pasted as plain text.
611
- transformPastedText?: (text: string, plain: boolean) => string
615
+ transformPastedText?: (this: P, text: string, plain: boolean) => string
612
616
 
613
617
  /// A function to parse text from the clipboard into a document
614
618
  /// slice. Called after
@@ -617,11 +621,11 @@ export interface EditorProps {
617
621
  /// in `<p>` tags, and call
618
622
  /// [`clipboardParser`](#view.EditorProps.clipboardParser) on it.
619
623
  /// The `plain` flag will be true when the text is pasted as plain text.
620
- clipboardTextParser?: (text: string, $context: ResolvedPos, plain: boolean) => Slice
624
+ clipboardTextParser?: (this: P, text: string, $context: ResolvedPos, plain: boolean) => Slice
621
625
 
622
626
  /// Can be used to transform pasted content before it is applied to
623
627
  /// the document.
624
- transformPasted?: (slice: Slice) => Slice
628
+ transformPasted?: (this: P, slice: Slice) => Slice
625
629
 
626
630
  /// Allows you to pass custom rendering and behavior logic for
627
631
  /// nodes. Should map node names to constructor functions that
@@ -668,15 +672,15 @@ export interface EditorProps {
668
672
  /// selection when copying text to the clipboard. By default, the
669
673
  /// editor will use [`textBetween`](#model.Node.textBetween) on the
670
674
  /// selected range.
671
- clipboardTextSerializer?: (content: Slice) => string
675
+ clipboardTextSerializer?: (this: P, content: Slice) => string
672
676
 
673
677
  /// A set of [document decorations](#view.Decoration) to show in the
674
678
  /// view.
675
- decorations?: (state: EditorState) => DecorationSource | null | undefined
679
+ decorations?: (this: P, state: EditorState) => DecorationSource | null | undefined
676
680
 
677
681
  /// When this returns false, the content of the view is not directly
678
682
  /// editable.
679
- editable?: (state: EditorState) => boolean
683
+ editable?: (this: P, state: EditorState) => boolean
680
684
 
681
685
  /// Control the DOM attributes of the editable element. May be either
682
686
  /// an object or a function going from an editor state to an object.
package/src/viewdesc.ts CHANGED
@@ -262,8 +262,8 @@ export class ViewDesc {
262
262
  }
263
263
  }
264
264
 
265
- domFromPos(pos: number, side: number): {node: DOMNode, offset: number} {
266
- if (!this.contentDOM) return {node: this.dom, offset: 0}
265
+ domFromPos(pos: number, side: number): {node: DOMNode, offset: number, atom?: number} {
266
+ if (!this.contentDOM) return {node: this.dom, offset: 0, atom: pos + 1}
267
267
  // First find the position in the child array
268
268
  let i = 0, offset = 0
269
269
  for (let curPos = 0; i < this.children.length; i++) {
@@ -1196,7 +1196,7 @@ class ViewTreeUpdater {
1196
1196
  if (child.dirty == NODE_DIRTY && child.dom == child.contentDOM) child.dirty = CONTENT_DIRTY
1197
1197
  if (!child.update(node, outerDeco, innerDeco, view)) return false
1198
1198
  this.destroyBetween(this.index, index)
1199
- this.index = index + 1
1199
+ this.index++
1200
1200
  return true
1201
1201
  }
1202
1202