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/CHANGELOG.md +20 -0
- package/dist/index.cjs +67 -40
- package/dist/index.d.cts +16 -3
- package/dist/index.d.ts +16 -3
- package/dist/index.js +66 -57
- package/package.json +1 -1
- package/src/decoration.ts +13 -0
- package/src/dom.ts +8 -3
- package/src/domchange.ts +33 -26
- package/src/index.ts +10 -7
- package/src/input.ts +10 -8
- package/src/viewdesc.ts +4 -0
- package/src/#index.ts# +0 -822
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## 1.40.0 (2025-06-02)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Suppress lint warnings about dereferencing methods by making `EditorView.dispatch` a property instead of a method.
|
|
6
|
+
|
|
7
|
+
### New features
|
|
8
|
+
|
|
9
|
+
The `"handleTextInput"` prop now takes an additional argument that can be used to generate the default transaction for that text change.
|
|
10
|
+
|
|
11
|
+
Widget decorations now support a `relaxedSide` option which can be used to allow the DOM cursor to appear at either side.
|
|
12
|
+
|
|
13
|
+
## 1.39.3 (2025-05-14)
|
|
14
|
+
|
|
15
|
+
### Bug fixes
|
|
16
|
+
|
|
17
|
+
Fix an issue where clicks using different mouse buttons could be grouped into a single double- or triple-click.
|
|
18
|
+
|
|
19
|
+
Only delete the selection when starting a composition when it crosses block boundaries.
|
|
20
|
+
|
|
1
21
|
## 1.39.2 (2025-04-22)
|
|
2
22
|
|
|
3
23
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -50,6 +50,7 @@ var isEquivalentPosition = function isEquivalentPosition(node, off, targetNode,
|
|
|
50
50
|
};
|
|
51
51
|
var atomElements = /^(img|br|input|textarea|hr)$/i;
|
|
52
52
|
function scanFor(node, off, targetNode, targetOff, dir) {
|
|
53
|
+
var _a;
|
|
53
54
|
for (;;) {
|
|
54
55
|
if (node == targetNode && off == targetOff) return true;
|
|
55
56
|
if (off == (dir < 0 ? 0 : nodeSize(node))) {
|
|
@@ -58,9 +59,13 @@ function scanFor(node, off, targetNode, targetOff, dir) {
|
|
|
58
59
|
off = domIndex(node) + (dir < 0 ? 0 : 1);
|
|
59
60
|
node = parent;
|
|
60
61
|
} else if (node.nodeType == 1) {
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
62
|
+
var child = node.childNodes[off + (dir < 0 ? -1 : 0)];
|
|
63
|
+
if (child.nodeType == 1 && child.contentEditable == "false") {
|
|
64
|
+
if ((_a = child.pmViewDesc) === null || _a === void 0 ? void 0 : _a.ignoreForSelection) off += dir;else return false;
|
|
65
|
+
} else {
|
|
66
|
+
node = child;
|
|
67
|
+
off = dir < 0 ? nodeSize(node) : 0;
|
|
68
|
+
}
|
|
64
69
|
} else {
|
|
65
70
|
return false;
|
|
66
71
|
}
|
|
@@ -1063,6 +1068,11 @@ var ViewDesc = function () {
|
|
|
1063
1068
|
get: function get() {
|
|
1064
1069
|
return false;
|
|
1065
1070
|
}
|
|
1071
|
+
}, {
|
|
1072
|
+
key: "ignoreForSelection",
|
|
1073
|
+
get: function get() {
|
|
1074
|
+
return false;
|
|
1075
|
+
}
|
|
1066
1076
|
}, {
|
|
1067
1077
|
key: "isText",
|
|
1068
1078
|
value: function isText(text) {
|
|
@@ -1132,6 +1142,11 @@ var WidgetViewDesc = function (_ViewDesc) {
|
|
|
1132
1142
|
get: function get() {
|
|
1133
1143
|
return true;
|
|
1134
1144
|
}
|
|
1145
|
+
}, {
|
|
1146
|
+
key: "ignoreForSelection",
|
|
1147
|
+
get: function get() {
|
|
1148
|
+
return !!this.widget.type.spec.relaxedSide;
|
|
1149
|
+
}
|
|
1135
1150
|
}, {
|
|
1136
1151
|
key: "side",
|
|
1137
1152
|
get: function get() {
|
|
@@ -2879,7 +2894,8 @@ var InputState = _createClass(function InputState() {
|
|
|
2879
2894
|
time: 0,
|
|
2880
2895
|
x: 0,
|
|
2881
2896
|
y: 0,
|
|
2882
|
-
type: ""
|
|
2897
|
+
type: "",
|
|
2898
|
+
button: 0
|
|
2883
2899
|
};
|
|
2884
2900
|
this.lastSelectionOrigin = null;
|
|
2885
2901
|
this.lastSelectionTime = 0;
|
|
@@ -2990,9 +3006,12 @@ editHandlers.keypress = function (view, _event) {
|
|
|
2990
3006
|
var sel = view.state.selection;
|
|
2991
3007
|
if (!(sel instanceof prosemirrorState.TextSelection) || !sel.$from.sameParent(sel.$to)) {
|
|
2992
3008
|
var text = String.fromCharCode(event.charCode);
|
|
3009
|
+
var deflt = function deflt() {
|
|
3010
|
+
return view.state.tr.insertText(text).scrollIntoView();
|
|
3011
|
+
};
|
|
2993
3012
|
if (!/[\r\n]/.test(text) && !view.someProp("handleTextInput", function (f) {
|
|
2994
|
-
return f(view, sel.$from.pos, sel.$to.pos, text);
|
|
2995
|
-
})) view.dispatch(
|
|
3013
|
+
return f(view, sel.$from.pos, sel.$to.pos, text, deflt);
|
|
3014
|
+
})) view.dispatch(deflt());
|
|
2996
3015
|
event.preventDefault();
|
|
2997
3016
|
}
|
|
2998
3017
|
};
|
|
@@ -3105,14 +3124,15 @@ handlers.mousedown = function (view, _event) {
|
|
|
3105
3124
|
var flushed = forceDOMFlush(view);
|
|
3106
3125
|
var now = Date.now(),
|
|
3107
3126
|
type = "singleClick";
|
|
3108
|
-
if (now - view.input.lastClick.time < 500 && isNear(event, view.input.lastClick) && !event[selectNodeModifier]) {
|
|
3127
|
+
if (now - view.input.lastClick.time < 500 && isNear(event, view.input.lastClick) && !event[selectNodeModifier] && view.input.lastClick.button == event.button) {
|
|
3109
3128
|
if (view.input.lastClick.type == "singleClick") type = "doubleClick";else if (view.input.lastClick.type == "doubleClick") type = "tripleClick";
|
|
3110
3129
|
}
|
|
3111
3130
|
view.input.lastClick = {
|
|
3112
3131
|
time: now,
|
|
3113
3132
|
x: event.clientX,
|
|
3114
3133
|
y: event.clientY,
|
|
3115
|
-
type: type
|
|
3134
|
+
type: type,
|
|
3135
|
+
button: event.button
|
|
3116
3136
|
};
|
|
3117
3137
|
var pos = view.posAtCoords(eventCoords(event));
|
|
3118
3138
|
if (!pos) return;
|
|
@@ -3329,8 +3349,9 @@ function endComposition(view) {
|
|
|
3329
3349
|
view.domObserver.forceFlush();
|
|
3330
3350
|
clearComposition(view);
|
|
3331
3351
|
if (restarting || view.docView && view.docView.dirty) {
|
|
3332
|
-
var sel = selectionFromDOM(view)
|
|
3333
|
-
|
|
3352
|
+
var sel = selectionFromDOM(view),
|
|
3353
|
+
cur = view.state.selection;
|
|
3354
|
+
if (sel && !sel.eq(cur)) view.dispatch(view.state.tr.setSelection(sel));else if ((view.markCursor || restarting) && !cur.$from.node(cur.$from.sharedDepth(cur.to)).inlineContent) view.dispatch(view.state.tr.deleteSelection());else view.updateState(view.state);
|
|
3334
3355
|
return true;
|
|
3335
3356
|
}
|
|
3336
3357
|
return false;
|
|
@@ -4595,10 +4616,10 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4595
4616
|
if (chrome && android && view.input.lastKeyCode === 13 && Date.now() - 100 < view.input.lastKeyCodeTime && view.someProp("handleKeyDown", function (f) {
|
|
4596
4617
|
return f(view, keyEvent(13, "Enter"));
|
|
4597
4618
|
})) return;
|
|
4598
|
-
var
|
|
4599
|
-
if (origin == "pointer")
|
|
4600
|
-
if (compositionID)
|
|
4601
|
-
view.dispatch(
|
|
4619
|
+
var tr = view.state.tr.setSelection(newSel);
|
|
4620
|
+
if (origin == "pointer") tr.setMeta("pointer", true);else if (origin == "key") tr.scrollIntoView();
|
|
4621
|
+
if (compositionID) tr.setMeta("composition", compositionID);
|
|
4622
|
+
view.dispatch(tr);
|
|
4602
4623
|
}
|
|
4603
4624
|
return;
|
|
4604
4625
|
}
|
|
@@ -4640,9 +4661,9 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4640
4661
|
if (parse.sel) {
|
|
4641
4662
|
var _sel2 = resolveSelection(view, view.state.doc, parse.sel);
|
|
4642
4663
|
if (_sel2 && !_sel2.eq(view.state.selection)) {
|
|
4643
|
-
var
|
|
4644
|
-
if (compositionID)
|
|
4645
|
-
view.dispatch(
|
|
4664
|
+
var _tr = view.state.tr.setSelection(_sel2);
|
|
4665
|
+
if (compositionID) _tr.setMeta("composition", compositionID);
|
|
4666
|
+
view.dispatch(_tr);
|
|
4646
4667
|
}
|
|
4647
4668
|
}
|
|
4648
4669
|
return;
|
|
@@ -4692,7 +4713,16 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4692
4713
|
}
|
|
4693
4714
|
var chFrom = change.start,
|
|
4694
4715
|
chTo = change.endA;
|
|
4695
|
-
var
|
|
4716
|
+
var mkTr = function mkTr(base) {
|
|
4717
|
+
var tr = base || view.state.tr.replace(chFrom, chTo, parse.doc.slice(change.start - parse.from, change.endB - parse.from));
|
|
4718
|
+
if (parse.sel) {
|
|
4719
|
+
var _sel3 = resolveSelection(view, tr.doc, parse.sel);
|
|
4720
|
+
if (_sel3 && !(chrome && view.composing && _sel3.empty && (change.start != change.endB || view.input.lastChromeDelete < Date.now() - 100) && (_sel3.head == chFrom || _sel3.head == tr.mapping.map(chTo) - 1) || ie && _sel3.empty && _sel3.head == chFrom)) tr.setSelection(_sel3);
|
|
4721
|
+
}
|
|
4722
|
+
if (compositionID) tr.setMeta("composition", compositionID);
|
|
4723
|
+
return tr.scrollIntoView();
|
|
4724
|
+
};
|
|
4725
|
+
var markChange;
|
|
4696
4726
|
if (inlineChange) {
|
|
4697
4727
|
if ($from.pos == $to.pos) {
|
|
4698
4728
|
if (ie && ie_version <= 11 && $from.parentOffset == 0) {
|
|
@@ -4701,27 +4731,26 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4701
4731
|
return selectionToDOM(view);
|
|
4702
4732
|
}, 20);
|
|
4703
4733
|
}
|
|
4704
|
-
|
|
4705
|
-
|
|
4734
|
+
var _tr2 = mkTr(view.state.tr["delete"](chFrom, chTo));
|
|
4735
|
+
var marks = doc.resolve(change.start).marksAcross(doc.resolve(change.endA));
|
|
4736
|
+
if (marks) _tr2.ensureMarks(marks);
|
|
4737
|
+
view.dispatch(_tr2);
|
|
4706
4738
|
} else if (change.endA == change.endB && (markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset), $fromA.parent.content.cut($fromA.parentOffset, change.endA - $fromA.start())))) {
|
|
4707
|
-
|
|
4708
|
-
if (markChange.type == "add")
|
|
4739
|
+
var _tr3 = mkTr(view.state.tr);
|
|
4740
|
+
if (markChange.type == "add") _tr3.addMark(chFrom, chTo, markChange.mark);else _tr3.removeMark(chFrom, chTo, markChange.mark);
|
|
4741
|
+
view.dispatch(_tr3);
|
|
4709
4742
|
} else if ($from.parent.child($from.index()).isText && $from.index() == $to.index() - ($to.textOffset ? 0 : 1)) {
|
|
4710
4743
|
var text = $from.parent.textBetween($from.parentOffset, $to.parentOffset);
|
|
4711
|
-
|
|
4712
|
-
return
|
|
4713
|
-
}
|
|
4714
|
-
|
|
4744
|
+
var deflt = function deflt() {
|
|
4745
|
+
return mkTr(view.state.tr.insertText(text, chFrom, chTo));
|
|
4746
|
+
};
|
|
4747
|
+
if (!view.someProp("handleTextInput", function (f) {
|
|
4748
|
+
return f(view, chFrom, chTo, text, deflt);
|
|
4749
|
+
})) view.dispatch(deflt());
|
|
4715
4750
|
}
|
|
4751
|
+
} else {
|
|
4752
|
+
view.dispatch(mkTr());
|
|
4716
4753
|
}
|
|
4717
|
-
if (!tr) tr = view.state.tr.replace(chFrom, chTo, parse.doc.slice(change.start - parse.from, change.endB - parse.from));
|
|
4718
|
-
if (parse.sel) {
|
|
4719
|
-
var _sel3 = resolveSelection(view, tr.doc, parse.sel);
|
|
4720
|
-
if (_sel3 && !(chrome && view.composing && _sel3.empty && (change.start != change.endB || view.input.lastChromeDelete < Date.now() - 100) && (_sel3.head == chFrom || _sel3.head == tr.mapping.map(chTo) - 1) || ie && _sel3.empty && _sel3.head == chFrom)) tr.setSelection(_sel3);
|
|
4721
|
-
}
|
|
4722
|
-
if (storedMarks) tr.ensureMarks(storedMarks);
|
|
4723
|
-
if (compositionID) tr.setMeta("composition", compositionID);
|
|
4724
|
-
view.dispatch(tr.scrollIntoView());
|
|
4725
4754
|
}
|
|
4726
4755
|
function resolveSelection(view, doc, parsedSel) {
|
|
4727
4756
|
if (Math.max(parsedSel.anchor, parsedSel.head) > doc.content.size) return null;
|
|
@@ -5165,12 +5194,6 @@ var EditorView = function () {
|
|
|
5165
5194
|
value: function dispatchEvent(event) {
|
|
5166
5195
|
return _dispatchEvent(this, event);
|
|
5167
5196
|
}
|
|
5168
|
-
}, {
|
|
5169
|
-
key: "dispatch",
|
|
5170
|
-
value: function dispatch(tr) {
|
|
5171
|
-
var dispatchTransaction = this._props.dispatchTransaction;
|
|
5172
|
-
if (dispatchTransaction) dispatchTransaction.call(this, tr);else this.updateState(this.state.apply(tr));
|
|
5173
|
-
}
|
|
5174
5197
|
}, {
|
|
5175
5198
|
key: "domSelectionRange",
|
|
5176
5199
|
value: function domSelectionRange() {
|
|
@@ -5191,6 +5214,10 @@ var EditorView = function () {
|
|
|
5191
5214
|
}]);
|
|
5192
5215
|
return EditorView;
|
|
5193
5216
|
}();
|
|
5217
|
+
EditorView.prototype.dispatch = function (tr) {
|
|
5218
|
+
var dispatchTransaction = this._props.dispatchTransaction;
|
|
5219
|
+
if (dispatchTransaction) dispatchTransaction.call(this, tr);else this.updateState(this.state.apply(tr));
|
|
5220
|
+
};
|
|
5194
5221
|
function computeDocDeco(view) {
|
|
5195
5222
|
var attrs = Object.create(null);
|
|
5196
5223
|
attrs["class"] = "ProseMirror";
|
package/dist/index.d.cts
CHANGED
|
@@ -48,6 +48,20 @@ declare class Decoration {
|
|
|
48
48
|
*/
|
|
49
49
|
side?: number;
|
|
50
50
|
/**
|
|
51
|
+
By default, the cursor, when at the position of the widget,
|
|
52
|
+
will be strictly kept on the side indicated by
|
|
53
|
+
[`side`](https://prosemirror.net/docs/ref/#view.Decoration^widget^spec.side). Set this to true
|
|
54
|
+
to allow the DOM selection to stay on the other side if the
|
|
55
|
+
client sets it there.
|
|
56
|
+
|
|
57
|
+
**Note**: Mapping of this decoration, which decides on which
|
|
58
|
+
side insertions at its position appear, will still happen
|
|
59
|
+
according to `side`, and keyboard cursor motion will not,
|
|
60
|
+
without further custom handling, visit both sides of the
|
|
61
|
+
widget.
|
|
62
|
+
*/
|
|
63
|
+
relaxedSide?: boolean;
|
|
64
|
+
/**
|
|
51
65
|
The precise set of marks to draw around the widget.
|
|
52
66
|
*/
|
|
53
67
|
marks?: readonly Mark[];
|
|
@@ -367,7 +381,6 @@ editable document. Its state and behavior are determined by its
|
|
|
367
381
|
[props](https://prosemirror.net/docs/ref/#view.DirectEditorProps).
|
|
368
382
|
*/
|
|
369
383
|
declare class EditorView {
|
|
370
|
-
private _props;
|
|
371
384
|
private directPlugins;
|
|
372
385
|
private _root;
|
|
373
386
|
private mounted;
|
|
@@ -587,7 +600,7 @@ declare class EditorView {
|
|
|
587
600
|
This method is bound to the view instance, so that it can be
|
|
588
601
|
easily passed around.
|
|
589
602
|
*/
|
|
590
|
-
dispatch(tr: Transaction)
|
|
603
|
+
dispatch: (tr: Transaction) => void;
|
|
591
604
|
}
|
|
592
605
|
/**
|
|
593
606
|
The type of function [provided](https://prosemirror.net/docs/ref/#view.EditorProps.nodeViews) to
|
|
@@ -652,7 +665,7 @@ interface EditorProps<P = any> {
|
|
|
652
665
|
before the input is applied. If it returns `true`, the default
|
|
653
666
|
behavior of actually inserting the text is suppressed.
|
|
654
667
|
*/
|
|
655
|
-
handleTextInput?: (this: P, view: EditorView, from: number, to: number, text: string) => boolean | void;
|
|
668
|
+
handleTextInput?: (this: P, view: EditorView, from: number, to: number, text: string, deflt: () => Transaction) => boolean | void;
|
|
656
669
|
/**
|
|
657
670
|
Called for each node around a click, from the inside out. The
|
|
658
671
|
`direct` flag will be true for the inner node.
|
package/dist/index.d.ts
CHANGED
|
@@ -48,6 +48,20 @@ declare class Decoration {
|
|
|
48
48
|
*/
|
|
49
49
|
side?: number;
|
|
50
50
|
/**
|
|
51
|
+
By default, the cursor, when at the position of the widget,
|
|
52
|
+
will be strictly kept on the side indicated by
|
|
53
|
+
[`side`](https://prosemirror.net/docs/ref/#view.Decoration^widget^spec.side). Set this to true
|
|
54
|
+
to allow the DOM selection to stay on the other side if the
|
|
55
|
+
client sets it there.
|
|
56
|
+
|
|
57
|
+
**Note**: Mapping of this decoration, which decides on which
|
|
58
|
+
side insertions at its position appear, will still happen
|
|
59
|
+
according to `side`, and keyboard cursor motion will not,
|
|
60
|
+
without further custom handling, visit both sides of the
|
|
61
|
+
widget.
|
|
62
|
+
*/
|
|
63
|
+
relaxedSide?: boolean;
|
|
64
|
+
/**
|
|
51
65
|
The precise set of marks to draw around the widget.
|
|
52
66
|
*/
|
|
53
67
|
marks?: readonly Mark[];
|
|
@@ -367,7 +381,6 @@ editable document. Its state and behavior are determined by its
|
|
|
367
381
|
[props](https://prosemirror.net/docs/ref/#view.DirectEditorProps).
|
|
368
382
|
*/
|
|
369
383
|
declare class EditorView {
|
|
370
|
-
private _props;
|
|
371
384
|
private directPlugins;
|
|
372
385
|
private _root;
|
|
373
386
|
private mounted;
|
|
@@ -587,7 +600,7 @@ declare class EditorView {
|
|
|
587
600
|
This method is bound to the view instance, so that it can be
|
|
588
601
|
easily passed around.
|
|
589
602
|
*/
|
|
590
|
-
dispatch(tr: Transaction)
|
|
603
|
+
dispatch: (tr: Transaction) => void;
|
|
591
604
|
}
|
|
592
605
|
/**
|
|
593
606
|
The type of function [provided](https://prosemirror.net/docs/ref/#view.EditorProps.nodeViews) to
|
|
@@ -652,7 +665,7 @@ interface EditorProps<P = any> {
|
|
|
652
665
|
before the input is applied. If it returns `true`, the default
|
|
653
666
|
behavior of actually inserting the text is suppressed.
|
|
654
667
|
*/
|
|
655
|
-
handleTextInput?: (this: P, view: EditorView, from: number, to: number, text: string) => boolean | void;
|
|
668
|
+
handleTextInput?: (this: P, view: EditorView, from: number, to: number, text: string, deflt: () => Transaction) => boolean | void;
|
|
656
669
|
/**
|
|
657
670
|
Called for each node around a click, from the inside out. The
|
|
658
671
|
`direct` flag will be true for the inner node.
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,7 @@ const isEquivalentPosition = function (node, off, targetNode, targetOff) {
|
|
|
35
35
|
};
|
|
36
36
|
const atomElements = /^(img|br|input|textarea|hr)$/i;
|
|
37
37
|
function scanFor(node, off, targetNode, targetOff, dir) {
|
|
38
|
+
var _a;
|
|
38
39
|
for (;;) {
|
|
39
40
|
if (node == targetNode && off == targetOff)
|
|
40
41
|
return true;
|
|
@@ -47,10 +48,17 @@ function scanFor(node, off, targetNode, targetOff, dir) {
|
|
|
47
48
|
node = parent;
|
|
48
49
|
}
|
|
49
50
|
else if (node.nodeType == 1) {
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
let child = node.childNodes[off + (dir < 0 ? -1 : 0)];
|
|
52
|
+
if (child.nodeType == 1 && child.contentEditable == "false") {
|
|
53
|
+
if ((_a = child.pmViewDesc) === null || _a === void 0 ? void 0 : _a.ignoreForSelection)
|
|
54
|
+
off += dir;
|
|
55
|
+
else
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
node = child;
|
|
60
|
+
off = dir < 0 ? nodeSize(node) : 0;
|
|
61
|
+
}
|
|
54
62
|
}
|
|
55
63
|
else {
|
|
56
64
|
return false;
|
|
@@ -1134,6 +1142,7 @@ class ViewDesc {
|
|
|
1134
1142
|
}
|
|
1135
1143
|
get domAtom() { return false; }
|
|
1136
1144
|
get ignoreForCoords() { return false; }
|
|
1145
|
+
get ignoreForSelection() { return false; }
|
|
1137
1146
|
isText(text) { return false; }
|
|
1138
1147
|
}
|
|
1139
1148
|
// A widget desc represents a widget decoration, which is a DOM node
|
|
@@ -1178,6 +1187,7 @@ class WidgetViewDesc extends ViewDesc {
|
|
|
1178
1187
|
super.destroy();
|
|
1179
1188
|
}
|
|
1180
1189
|
get domAtom() { return true; }
|
|
1190
|
+
get ignoreForSelection() { return !!this.widget.type.spec.relaxedSide; }
|
|
1181
1191
|
get side() { return this.widget.type.side; }
|
|
1182
1192
|
}
|
|
1183
1193
|
class CompositionViewDesc extends ViewDesc {
|
|
@@ -3047,7 +3057,7 @@ class InputState {
|
|
|
3047
3057
|
this.mouseDown = null;
|
|
3048
3058
|
this.lastKeyCode = null;
|
|
3049
3059
|
this.lastKeyCodeTime = 0;
|
|
3050
|
-
this.lastClick = { time: 0, x: 0, y: 0, type: "" };
|
|
3060
|
+
this.lastClick = { time: 0, x: 0, y: 0, type: "", button: 0 };
|
|
3051
3061
|
this.lastSelectionOrigin = null;
|
|
3052
3062
|
this.lastSelectionTime = 0;
|
|
3053
3063
|
this.lastIOSEnter = 0;
|
|
@@ -3175,8 +3185,9 @@ editHandlers.keypress = (view, _event) => {
|
|
|
3175
3185
|
let sel = view.state.selection;
|
|
3176
3186
|
if (!(sel instanceof TextSelection) || !sel.$from.sameParent(sel.$to)) {
|
|
3177
3187
|
let text = String.fromCharCode(event.charCode);
|
|
3178
|
-
|
|
3179
|
-
|
|
3188
|
+
let deflt = () => view.state.tr.insertText(text).scrollIntoView();
|
|
3189
|
+
if (!/[\r\n]/.test(text) && !view.someProp("handleTextInput", f => f(view, sel.$from.pos, sel.$to.pos, text, deflt)))
|
|
3190
|
+
view.dispatch(deflt());
|
|
3180
3191
|
event.preventDefault();
|
|
3181
3192
|
}
|
|
3182
3193
|
};
|
|
@@ -3289,13 +3300,14 @@ handlers.mousedown = (view, _event) => {
|
|
|
3289
3300
|
view.input.shiftKey = event.shiftKey;
|
|
3290
3301
|
let flushed = forceDOMFlush(view);
|
|
3291
3302
|
let now = Date.now(), type = "singleClick";
|
|
3292
|
-
if (now - view.input.lastClick.time < 500 && isNear(event, view.input.lastClick) && !event[selectNodeModifier]
|
|
3303
|
+
if (now - view.input.lastClick.time < 500 && isNear(event, view.input.lastClick) && !event[selectNodeModifier] &&
|
|
3304
|
+
view.input.lastClick.button == event.button) {
|
|
3293
3305
|
if (view.input.lastClick.type == "singleClick")
|
|
3294
3306
|
type = "doubleClick";
|
|
3295
3307
|
else if (view.input.lastClick.type == "doubleClick")
|
|
3296
3308
|
type = "tripleClick";
|
|
3297
3309
|
}
|
|
3298
|
-
view.input.lastClick = { time: now, x: event.clientX, y: event.clientY, type };
|
|
3310
|
+
view.input.lastClick = { time: now, x: event.clientX, y: event.clientY, type, button: event.button };
|
|
3299
3311
|
let pos = view.posAtCoords(eventCoords(event));
|
|
3300
3312
|
if (!pos)
|
|
3301
3313
|
return;
|
|
@@ -3552,10 +3564,10 @@ function endComposition(view, restarting = false) {
|
|
|
3552
3564
|
view.domObserver.forceFlush();
|
|
3553
3565
|
clearComposition(view);
|
|
3554
3566
|
if (restarting || view.docView && view.docView.dirty) {
|
|
3555
|
-
let sel = selectionFromDOM(view);
|
|
3556
|
-
if (sel && !sel.eq(
|
|
3567
|
+
let sel = selectionFromDOM(view), cur = view.state.selection;
|
|
3568
|
+
if (sel && !sel.eq(cur))
|
|
3557
3569
|
view.dispatch(view.state.tr.setSelection(sel));
|
|
3558
|
-
else if ((view.markCursor || restarting) && !
|
|
3570
|
+
else if ((view.markCursor || restarting) && !cur.$from.node(cur.$from.sharedDepth(cur.to)).inlineContent)
|
|
3559
3571
|
view.dispatch(view.state.tr.deleteSelection());
|
|
3560
3572
|
else
|
|
3561
3573
|
view.updateState(view.state);
|
|
@@ -5060,7 +5072,26 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5060
5072
|
}, 20);
|
|
5061
5073
|
}
|
|
5062
5074
|
let chFrom = change.start, chTo = change.endA;
|
|
5063
|
-
let
|
|
5075
|
+
let mkTr = (base) => {
|
|
5076
|
+
let tr = base || view.state.tr.replace(chFrom, chTo, parse.doc.slice(change.start - parse.from, change.endB - parse.from));
|
|
5077
|
+
if (parse.sel) {
|
|
5078
|
+
let sel = resolveSelection(view, tr.doc, parse.sel);
|
|
5079
|
+
// Chrome will sometimes, during composition, report the
|
|
5080
|
+
// selection in the wrong place. If it looks like that is
|
|
5081
|
+
// happening, don't update the selection.
|
|
5082
|
+
// Edge just doesn't move the cursor forward when you start typing
|
|
5083
|
+
// in an empty block or between br nodes.
|
|
5084
|
+
if (sel && !(chrome && view.composing && sel.empty &&
|
|
5085
|
+
(change.start != change.endB || view.input.lastChromeDelete < Date.now() - 100) &&
|
|
5086
|
+
(sel.head == chFrom || sel.head == tr.mapping.map(chTo) - 1) ||
|
|
5087
|
+
ie && sel.empty && sel.head == chFrom))
|
|
5088
|
+
tr.setSelection(sel);
|
|
5089
|
+
}
|
|
5090
|
+
if (compositionID)
|
|
5091
|
+
tr.setMeta("composition", compositionID);
|
|
5092
|
+
return tr.scrollIntoView();
|
|
5093
|
+
};
|
|
5094
|
+
let markChange;
|
|
5064
5095
|
if (inlineChange) {
|
|
5065
5096
|
if ($from.pos == $to.pos) { // Deletion
|
|
5066
5097
|
// IE11 sometimes weirdly moves the DOM selection around after
|
|
@@ -5069,46 +5100,33 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5069
5100
|
view.domObserver.suppressSelectionUpdates();
|
|
5070
5101
|
setTimeout(() => selectionToDOM(view), 20);
|
|
5071
5102
|
}
|
|
5072
|
-
tr = view.state.tr.delete(chFrom, chTo);
|
|
5073
|
-
|
|
5103
|
+
let tr = mkTr(view.state.tr.delete(chFrom, chTo));
|
|
5104
|
+
let marks = doc.resolve(change.start).marksAcross(doc.resolve(change.endA));
|
|
5105
|
+
if (marks)
|
|
5106
|
+
tr.ensureMarks(marks);
|
|
5107
|
+
view.dispatch(tr);
|
|
5074
5108
|
}
|
|
5075
5109
|
else if ( // Adding or removing a mark
|
|
5076
5110
|
change.endA == change.endB &&
|
|
5077
5111
|
(markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset), $fromA.parent.content.cut($fromA.parentOffset, change.endA - $fromA.start())))) {
|
|
5078
|
-
tr = view.state.tr;
|
|
5112
|
+
let tr = mkTr(view.state.tr);
|
|
5079
5113
|
if (markChange.type == "add")
|
|
5080
5114
|
tr.addMark(chFrom, chTo, markChange.mark);
|
|
5081
5115
|
else
|
|
5082
5116
|
tr.removeMark(chFrom, chTo, markChange.mark);
|
|
5117
|
+
view.dispatch(tr);
|
|
5083
5118
|
}
|
|
5084
5119
|
else if ($from.parent.child($from.index()).isText && $from.index() == $to.index() - ($to.textOffset ? 0 : 1)) {
|
|
5085
5120
|
// Both positions in the same text node -- simply insert text
|
|
5086
5121
|
let text = $from.parent.textBetween($from.parentOffset, $to.parentOffset);
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
}
|
|
5091
|
-
}
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
let sel = resolveSelection(view, tr.doc, parse.sel);
|
|
5096
|
-
// Chrome will sometimes, during composition, report the
|
|
5097
|
-
// selection in the wrong place. If it looks like that is
|
|
5098
|
-
// happening, don't update the selection.
|
|
5099
|
-
// Edge just doesn't move the cursor forward when you start typing
|
|
5100
|
-
// in an empty block or between br nodes.
|
|
5101
|
-
if (sel && !(chrome && view.composing && sel.empty &&
|
|
5102
|
-
(change.start != change.endB || view.input.lastChromeDelete < Date.now() - 100) &&
|
|
5103
|
-
(sel.head == chFrom || sel.head == tr.mapping.map(chTo) - 1) ||
|
|
5104
|
-
ie && sel.empty && sel.head == chFrom))
|
|
5105
|
-
tr.setSelection(sel);
|
|
5106
|
-
}
|
|
5107
|
-
if (storedMarks)
|
|
5108
|
-
tr.ensureMarks(storedMarks);
|
|
5109
|
-
if (compositionID)
|
|
5110
|
-
tr.setMeta("composition", compositionID);
|
|
5111
|
-
view.dispatch(tr.scrollIntoView());
|
|
5122
|
+
let deflt = () => mkTr(view.state.tr.insertText(text, chFrom, chTo));
|
|
5123
|
+
if (!view.someProp("handleTextInput", f => f(view, chFrom, chTo, text, deflt)))
|
|
5124
|
+
view.dispatch(deflt());
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
else {
|
|
5128
|
+
view.dispatch(mkTr());
|
|
5129
|
+
}
|
|
5112
5130
|
}
|
|
5113
5131
|
function resolveSelection(view, doc, parsedSel) {
|
|
5114
5132
|
if (Math.max(parsedSel.anchor, parsedSel.head) > doc.content.size)
|
|
@@ -5711,22 +5729,6 @@ class EditorView {
|
|
|
5711
5729
|
return dispatchEvent(this, event);
|
|
5712
5730
|
}
|
|
5713
5731
|
/**
|
|
5714
|
-
Dispatch a transaction. Will call
|
|
5715
|
-
[`dispatchTransaction`](https://prosemirror.net/docs/ref/#view.DirectEditorProps.dispatchTransaction)
|
|
5716
|
-
when given, and otherwise defaults to applying the transaction to
|
|
5717
|
-
the current state and calling
|
|
5718
|
-
[`updateState`](https://prosemirror.net/docs/ref/#view.EditorView.updateState) with the result.
|
|
5719
|
-
This method is bound to the view instance, so that it can be
|
|
5720
|
-
easily passed around.
|
|
5721
|
-
*/
|
|
5722
|
-
dispatch(tr) {
|
|
5723
|
-
let dispatchTransaction = this._props.dispatchTransaction;
|
|
5724
|
-
if (dispatchTransaction)
|
|
5725
|
-
dispatchTransaction.call(this, tr);
|
|
5726
|
-
else
|
|
5727
|
-
this.updateState(this.state.apply(tr));
|
|
5728
|
-
}
|
|
5729
|
-
/**
|
|
5730
5732
|
@internal
|
|
5731
5733
|
*/
|
|
5732
5734
|
domSelectionRange() {
|
|
@@ -5743,6 +5745,13 @@ class EditorView {
|
|
|
5743
5745
|
return this.root.getSelection();
|
|
5744
5746
|
}
|
|
5745
5747
|
}
|
|
5748
|
+
EditorView.prototype.dispatch = function (tr) {
|
|
5749
|
+
let dispatchTransaction = this._props.dispatchTransaction;
|
|
5750
|
+
if (dispatchTransaction)
|
|
5751
|
+
dispatchTransaction.call(this, tr);
|
|
5752
|
+
else
|
|
5753
|
+
this.updateState(this.state.apply(tr));
|
|
5754
|
+
};
|
|
5746
5755
|
function computeDocDeco(view) {
|
|
5747
5756
|
let attrs = Object.create(null);
|
|
5748
5757
|
attrs.class = "ProseMirror";
|
package/package.json
CHANGED
package/src/decoration.ts
CHANGED
|
@@ -156,6 +156,19 @@ export class Decoration {
|
|
|
156
156
|
/// negative, those of the node after when positive.
|
|
157
157
|
side?: number
|
|
158
158
|
|
|
159
|
+
/// By default, the cursor, when at the position of the widget,
|
|
160
|
+
/// will be strictly kept on the side indicated by
|
|
161
|
+
/// [`side`](#view.Decoration^widget^spec.side). Set this to true
|
|
162
|
+
/// to allow the DOM selection to stay on the other side if the
|
|
163
|
+
/// client sets it there.
|
|
164
|
+
///
|
|
165
|
+
/// **Note**: Mapping of this decoration, which decides on which
|
|
166
|
+
/// side insertions at its position appear, will still happen
|
|
167
|
+
/// according to `side`, and keyboard cursor motion will not,
|
|
168
|
+
/// without further custom handling, visit both sides of the
|
|
169
|
+
/// widget.
|
|
170
|
+
relaxedSide?: boolean
|
|
171
|
+
|
|
159
172
|
/// The precise set of marks to draw around the widget.
|
|
160
173
|
marks?: readonly Mark[]
|
|
161
174
|
|
package/src/dom.ts
CHANGED
|
@@ -54,9 +54,14 @@ function scanFor(node: Node, off: number, targetNode: Node, targetOff: number, d
|
|
|
54
54
|
off = domIndex(node) + (dir < 0 ? 0 : 1)
|
|
55
55
|
node = parent
|
|
56
56
|
} else if (node.nodeType == 1) {
|
|
57
|
-
|
|
58
|
-
if ((
|
|
59
|
-
|
|
57
|
+
let child = node.childNodes[off + (dir < 0 ? -1 : 0)]
|
|
58
|
+
if (child.nodeType == 1 && (child as HTMLElement).contentEditable == "false") {
|
|
59
|
+
if (child.pmViewDesc?.ignoreForSelection) off += dir
|
|
60
|
+
else return false
|
|
61
|
+
} else {
|
|
62
|
+
node = child
|
|
63
|
+
off = dir < 0 ? nodeSize(node) : 0
|
|
64
|
+
}
|
|
60
65
|
} else {
|
|
61
66
|
return false
|
|
62
67
|
}
|