prosemirror-view 1.33.2 → 1.33.4
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 +12 -0
- package/dist/index.cjs +55 -25
- package/dist/index.d.cts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +49 -23
- package/package.json +2 -2
- package/src/dom.ts +2 -2
- package/src/domchange.ts +2 -2
- package/src/domobserver.ts +21 -12
- package/src/index.ts +5 -3
- package/src/input.ts +21 -1
- package/src/viewdesc.ts +12 -16
- package/src/#index.ts# +0 -803
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 1.33.4 (2024-04-08)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
When available, use `Selection.getComposedRanges` on Safari to find the selection inside a shadow DOM.
|
|
6
|
+
|
|
7
|
+
## 1.33.3 (2024-03-22)
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
Further improve preserving of compositions for IME systems that keep the cursor before the composed text.
|
|
12
|
+
|
|
1
13
|
## 1.33.2 (2024-03-21)
|
|
2
14
|
|
|
3
15
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -69,7 +69,7 @@ function textNodeBefore$1(node, offset) {
|
|
|
69
69
|
for (;;) {
|
|
70
70
|
if (node.nodeType == 3 && offset) return node;
|
|
71
71
|
if (node.nodeType == 1 && offset > 0) {
|
|
72
|
-
if (node.contentEditable
|
|
72
|
+
if (node.contentEditable == "false") return null;
|
|
73
73
|
node = node.childNodes[offset - 1];
|
|
74
74
|
offset = nodeSize(node);
|
|
75
75
|
} else if (node.parentNode && !hasBlockDesc(node)) {
|
|
@@ -84,7 +84,7 @@ function textNodeAfter$1(node, offset) {
|
|
|
84
84
|
for (;;) {
|
|
85
85
|
if (node.nodeType == 3 && offset < node.nodeValue.length) return node;
|
|
86
86
|
if (node.nodeType == 1 && offset < node.childNodes.length) {
|
|
87
|
-
if (node.contentEditable
|
|
87
|
+
if (node.contentEditable == "false") return null;
|
|
88
88
|
node = node.childNodes[offset];
|
|
89
89
|
offset = 0;
|
|
90
90
|
} else if (node.parentNode && !hasBlockDesc(node)) {
|
|
@@ -1044,6 +1044,11 @@ var ViewDesc = function () {
|
|
|
1044
1044
|
get: function get() {
|
|
1045
1045
|
return false;
|
|
1046
1046
|
}
|
|
1047
|
+
}, {
|
|
1048
|
+
key: "isText",
|
|
1049
|
+
value: function isText(text) {
|
|
1050
|
+
return false;
|
|
1051
|
+
}
|
|
1047
1052
|
}]);
|
|
1048
1053
|
return ViewDesc;
|
|
1049
1054
|
}();
|
|
@@ -1308,14 +1313,7 @@ var NodeViewDesc = function (_ViewDesc4) {
|
|
|
1308
1313
|
from = _view$state$selection.from,
|
|
1309
1314
|
to = _view$state$selection.to;
|
|
1310
1315
|
if (!(view.state.selection instanceof prosemirrorState.TextSelection) || from < pos || to > pos + this.node.content.size) return null;
|
|
1311
|
-
var
|
|
1312
|
-
var textBefore = textNodeBefore$1(sel.focusNode, sel.focusOffset);
|
|
1313
|
-
var textAfter = textNodeAfter$1(sel.focusNode, sel.focusOffset);
|
|
1314
|
-
var textNode = textBefore || textAfter;
|
|
1315
|
-
if (textBefore && textAfter && textBefore != textAfter) {
|
|
1316
|
-
var descAfter = textAfter.pmViewDesc;
|
|
1317
|
-
if (!descAfter || descAfter instanceof TextViewDesc && descAfter.node.text != textAfter.nodeValue) textNode = textAfter;
|
|
1318
|
-
}
|
|
1316
|
+
var textNode = view.input.compositionNode;
|
|
1319
1317
|
if (!textNode || !this.dom.contains(textNode.parentNode)) return null;
|
|
1320
1318
|
if (this.node.inlineContent) {
|
|
1321
1319
|
var text = textNode.nodeValue;
|
|
@@ -1505,6 +1503,11 @@ var TextViewDesc = function (_NodeViewDesc) {
|
|
|
1505
1503
|
get: function get() {
|
|
1506
1504
|
return false;
|
|
1507
1505
|
}
|
|
1506
|
+
}, {
|
|
1507
|
+
key: "isText",
|
|
1508
|
+
value: function isText(text) {
|
|
1509
|
+
return this.node.text == text;
|
|
1510
|
+
}
|
|
1508
1511
|
}]);
|
|
1509
1512
|
return TextViewDesc;
|
|
1510
1513
|
}(NodeViewDesc);
|
|
@@ -2822,6 +2825,7 @@ var InputState = _createClass(function InputState() {
|
|
|
2822
2825
|
this.lastTouch = 0;
|
|
2823
2826
|
this.lastAndroidDelete = 0;
|
|
2824
2827
|
this.composing = false;
|
|
2828
|
+
this.compositionNode = null;
|
|
2825
2829
|
this.composingTimeout = -1;
|
|
2826
2830
|
this.compositionNodes = [];
|
|
2827
2831
|
this.compositionEndedAt = -2e8;
|
|
@@ -3209,6 +3213,7 @@ editHandlers.compositionend = function (view, event) {
|
|
|
3209
3213
|
view.input.composing = false;
|
|
3210
3214
|
view.input.compositionEndedAt = event.timeStamp;
|
|
3211
3215
|
view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
|
|
3216
|
+
view.input.compositionNode = null;
|
|
3212
3217
|
if (view.input.compositionPendingChanges) Promise.resolve().then(function () {
|
|
3213
3218
|
return view.domObserver.flush();
|
|
3214
3219
|
});
|
|
@@ -3229,6 +3234,22 @@ function clearComposition(view) {
|
|
|
3229
3234
|
}
|
|
3230
3235
|
while (view.input.compositionNodes.length > 0) view.input.compositionNodes.pop().markParentsDirty();
|
|
3231
3236
|
}
|
|
3237
|
+
function findCompositionNode(view) {
|
|
3238
|
+
var sel = view.domSelectionRange();
|
|
3239
|
+
if (!sel.focusNode) return null;
|
|
3240
|
+
var textBefore = textNodeBefore$1(sel.focusNode, sel.focusOffset);
|
|
3241
|
+
var textAfter = textNodeAfter$1(sel.focusNode, sel.focusOffset);
|
|
3242
|
+
if (textBefore && textAfter && textBefore != textAfter) {
|
|
3243
|
+
var descAfter = textAfter.pmViewDesc;
|
|
3244
|
+
if (!descAfter || !descAfter.isText(textAfter.nodeValue)) {
|
|
3245
|
+
return textAfter;
|
|
3246
|
+
} else if (view.input.compositionNode == textAfter) {
|
|
3247
|
+
var descBefore = textBefore.pmViewDesc;
|
|
3248
|
+
if (!(!descBefore || !descBefore.isText(textBefore.nodeValue))) return textAfter;
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
return textBefore;
|
|
3252
|
+
}
|
|
3232
3253
|
function timestampFromCustomEvent() {
|
|
3233
3254
|
var event = document.createEvent("Event");
|
|
3234
3255
|
event.initEvent("event", true, true);
|
|
@@ -4328,20 +4349,11 @@ function checkCSS(view) {
|
|
|
4328
4349
|
cssCheckWarned = true;
|
|
4329
4350
|
}
|
|
4330
4351
|
}
|
|
4331
|
-
function
|
|
4332
|
-
var
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
found = event.getTargetRanges()[0];
|
|
4337
|
-
}
|
|
4338
|
-
view.dom.addEventListener("beforeinput", read, true);
|
|
4339
|
-
document.execCommand("indent");
|
|
4340
|
-
view.dom.removeEventListener("beforeinput", read, true);
|
|
4341
|
-
var anchorNode = found.startContainer,
|
|
4342
|
-
anchorOffset = found.startOffset;
|
|
4343
|
-
var focusNode = found.endContainer,
|
|
4344
|
-
focusOffset = found.endOffset;
|
|
4352
|
+
function rangeToSelectionRange(view, range) {
|
|
4353
|
+
var anchorNode = range.startContainer,
|
|
4354
|
+
anchorOffset = range.startOffset;
|
|
4355
|
+
var focusNode = range.endContainer,
|
|
4356
|
+
focusOffset = range.endOffset;
|
|
4345
4357
|
var currentAnchor = view.domAtPos(view.state.selection.anchor);
|
|
4346
4358
|
if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset)) {
|
|
4347
4359
|
var _ref3 = [focusNode, focusOffset, anchorNode, anchorOffset];
|
|
@@ -4357,6 +4369,22 @@ function safariShadowSelectionRange(view) {
|
|
|
4357
4369
|
focusOffset: focusOffset
|
|
4358
4370
|
};
|
|
4359
4371
|
}
|
|
4372
|
+
function safariShadowSelectionRange(view, selection) {
|
|
4373
|
+
if (selection.getComposedRanges) {
|
|
4374
|
+
var range = selection.getComposedRanges(view.root)[0];
|
|
4375
|
+
if (range) return rangeToSelectionRange(view, range);
|
|
4376
|
+
}
|
|
4377
|
+
var found;
|
|
4378
|
+
function read(event) {
|
|
4379
|
+
event.preventDefault();
|
|
4380
|
+
event.stopImmediatePropagation();
|
|
4381
|
+
found = event.getTargetRanges()[0];
|
|
4382
|
+
}
|
|
4383
|
+
view.dom.addEventListener("beforeinput", read, true);
|
|
4384
|
+
document.execCommand("indent");
|
|
4385
|
+
view.dom.removeEventListener("beforeinput", read, true);
|
|
4386
|
+
return found ? rangeToSelectionRange(view, found) : null;
|
|
4387
|
+
}
|
|
4360
4388
|
function parseBetween(view, from_, to_) {
|
|
4361
4389
|
var _view$docView$parseRa = view.docView.parseRange(from_, to_),
|
|
4362
4390
|
parent = _view$docView$parseRa.node,
|
|
@@ -4798,6 +4826,7 @@ var EditorView = function () {
|
|
|
4798
4826
|
var forceSelUpdate = updateDoc && (ie || chrome) && !this.composing && !prev.selection.empty && !state.selection.empty && selectionContextChanged(prev.selection, state.selection);
|
|
4799
4827
|
if (updateDoc) {
|
|
4800
4828
|
var chromeKludge = chrome ? this.trackWrites = this.domSelectionRange().focusNode : null;
|
|
4829
|
+
if (this.composing) this.input.compositionNode = findCompositionNode(this);
|
|
4801
4830
|
if (redraw || !this.docView.update(state.doc, outerDeco, innerDeco, this)) {
|
|
4802
4831
|
this.docView.updateOuterDeco(outerDeco);
|
|
4803
4832
|
this.docView.destroy();
|
|
@@ -5027,7 +5056,8 @@ var EditorView = function () {
|
|
|
5027
5056
|
}, {
|
|
5028
5057
|
key: "domSelectionRange",
|
|
5029
5058
|
value: function domSelectionRange() {
|
|
5030
|
-
|
|
5059
|
+
var sel = this.domSelection();
|
|
5060
|
+
return safari && this.root.nodeType === 11 && deepActiveElement(this.dom.ownerDocument) == this.dom && safariShadowSelectionRange(this, sel) || sel;
|
|
5031
5061
|
}
|
|
5032
5062
|
}, {
|
|
5033
5063
|
key: "domSelection",
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EditorState, Transaction, Selection, Plugin } from 'prosemirror-state';
|
|
2
|
-
import { Mark, Node,
|
|
2
|
+
import { Mark, Node, TagParseRule, Slice, ResolvedPos, DOMParser, DOMSerializer } from 'prosemirror-model';
|
|
3
3
|
import { Mapping } from 'prosemirror-transform';
|
|
4
4
|
|
|
5
5
|
type DOMNode = InstanceType<typeof window.Node>;
|
|
@@ -324,7 +324,7 @@ declare class ViewDesc {
|
|
|
324
324
|
matchesMark(mark: Mark): boolean;
|
|
325
325
|
matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
|
|
326
326
|
matchesHack(nodeName: string): boolean;
|
|
327
|
-
parseRule():
|
|
327
|
+
parseRule(): Omit<TagParseRule, "tag"> | null;
|
|
328
328
|
stopEvent(event: Event): boolean;
|
|
329
329
|
get size(): number;
|
|
330
330
|
get border(): number;
|
|
@@ -361,6 +361,7 @@ declare class ViewDesc {
|
|
|
361
361
|
markParentsDirty(): void;
|
|
362
362
|
get domAtom(): boolean;
|
|
363
363
|
get ignoreForCoords(): boolean;
|
|
364
|
+
isText(text: string): boolean;
|
|
364
365
|
}
|
|
365
366
|
declare class NodeViewDesc extends ViewDesc {
|
|
366
367
|
node: Node;
|
|
@@ -369,7 +370,7 @@ declare class NodeViewDesc extends ViewDesc {
|
|
|
369
370
|
readonly nodeDOM: DOMNode;
|
|
370
371
|
constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, dom: DOMNode, contentDOM: HTMLElement | null, nodeDOM: DOMNode, view: EditorView, pos: number);
|
|
371
372
|
static create(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView, pos: number): NodeViewDesc | TextViewDesc;
|
|
372
|
-
parseRule():
|
|
373
|
+
parseRule(): Omit<TagParseRule, "tag"> | null;
|
|
373
374
|
matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
|
|
374
375
|
get size(): number;
|
|
375
376
|
get border(): 0 | 1;
|
|
@@ -393,7 +394,9 @@ declare class NodeViewDesc extends ViewDesc {
|
|
|
393
394
|
}
|
|
394
395
|
declare class TextViewDesc extends NodeViewDesc {
|
|
395
396
|
constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, dom: DOMNode, nodeDOM: DOMNode, view: EditorView);
|
|
396
|
-
parseRule():
|
|
397
|
+
parseRule(): {
|
|
398
|
+
skip: any;
|
|
399
|
+
};
|
|
397
400
|
update(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView): boolean;
|
|
398
401
|
inParent(): boolean;
|
|
399
402
|
domFromPos(pos: number): {
|
|
@@ -405,6 +408,7 @@ declare class TextViewDesc extends NodeViewDesc {
|
|
|
405
408
|
slice(from: number, to: number, view: EditorView): TextViewDesc;
|
|
406
409
|
markDirty(from: number, to: number): void;
|
|
407
410
|
get domAtom(): boolean;
|
|
411
|
+
isText(text: string): boolean;
|
|
408
412
|
}
|
|
409
413
|
|
|
410
414
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EditorState, Transaction, Selection, Plugin } from 'prosemirror-state';
|
|
2
|
-
import { Mark, Node,
|
|
2
|
+
import { Mark, Node, TagParseRule, Slice, ResolvedPos, DOMParser, DOMSerializer } from 'prosemirror-model';
|
|
3
3
|
import { Mapping } from 'prosemirror-transform';
|
|
4
4
|
|
|
5
5
|
type DOMNode = InstanceType<typeof window.Node>;
|
|
@@ -324,7 +324,7 @@ declare class ViewDesc {
|
|
|
324
324
|
matchesMark(mark: Mark): boolean;
|
|
325
325
|
matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
|
|
326
326
|
matchesHack(nodeName: string): boolean;
|
|
327
|
-
parseRule():
|
|
327
|
+
parseRule(): Omit<TagParseRule, "tag"> | null;
|
|
328
328
|
stopEvent(event: Event): boolean;
|
|
329
329
|
get size(): number;
|
|
330
330
|
get border(): number;
|
|
@@ -361,6 +361,7 @@ declare class ViewDesc {
|
|
|
361
361
|
markParentsDirty(): void;
|
|
362
362
|
get domAtom(): boolean;
|
|
363
363
|
get ignoreForCoords(): boolean;
|
|
364
|
+
isText(text: string): boolean;
|
|
364
365
|
}
|
|
365
366
|
declare class NodeViewDesc extends ViewDesc {
|
|
366
367
|
node: Node;
|
|
@@ -369,7 +370,7 @@ declare class NodeViewDesc extends ViewDesc {
|
|
|
369
370
|
readonly nodeDOM: DOMNode;
|
|
370
371
|
constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, dom: DOMNode, contentDOM: HTMLElement | null, nodeDOM: DOMNode, view: EditorView, pos: number);
|
|
371
372
|
static create(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView, pos: number): NodeViewDesc | TextViewDesc;
|
|
372
|
-
parseRule():
|
|
373
|
+
parseRule(): Omit<TagParseRule, "tag"> | null;
|
|
373
374
|
matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
|
|
374
375
|
get size(): number;
|
|
375
376
|
get border(): 0 | 1;
|
|
@@ -393,7 +394,9 @@ declare class NodeViewDesc extends ViewDesc {
|
|
|
393
394
|
}
|
|
394
395
|
declare class TextViewDesc extends NodeViewDesc {
|
|
395
396
|
constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, dom: DOMNode, nodeDOM: DOMNode, view: EditorView);
|
|
396
|
-
parseRule():
|
|
397
|
+
parseRule(): {
|
|
398
|
+
skip: any;
|
|
399
|
+
};
|
|
397
400
|
update(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView): boolean;
|
|
398
401
|
inParent(): boolean;
|
|
399
402
|
domFromPos(pos: number): {
|
|
@@ -405,6 +408,7 @@ declare class TextViewDesc extends NodeViewDesc {
|
|
|
405
408
|
slice(from: number, to: number, view: EditorView): TextViewDesc;
|
|
406
409
|
markDirty(from: number, to: number): void;
|
|
407
410
|
get domAtom(): boolean;
|
|
411
|
+
isText(text: string): boolean;
|
|
408
412
|
}
|
|
409
413
|
|
|
410
414
|
/**
|
package/dist/index.js
CHANGED
|
@@ -65,7 +65,7 @@ function textNodeBefore$1(node, offset) {
|
|
|
65
65
|
if (node.nodeType == 3 && offset)
|
|
66
66
|
return node;
|
|
67
67
|
if (node.nodeType == 1 && offset > 0) {
|
|
68
|
-
if (node.contentEditable
|
|
68
|
+
if (node.contentEditable == "false")
|
|
69
69
|
return null;
|
|
70
70
|
node = node.childNodes[offset - 1];
|
|
71
71
|
offset = nodeSize(node);
|
|
@@ -84,7 +84,7 @@ function textNodeAfter$1(node, offset) {
|
|
|
84
84
|
if (node.nodeType == 3 && offset < node.nodeValue.length)
|
|
85
85
|
return node;
|
|
86
86
|
if (node.nodeType == 1 && offset < node.childNodes.length) {
|
|
87
|
-
if (node.contentEditable
|
|
87
|
+
if (node.contentEditable == "false")
|
|
88
88
|
return null;
|
|
89
89
|
node = node.childNodes[offset];
|
|
90
90
|
offset = 0;
|
|
@@ -1115,6 +1115,7 @@ class ViewDesc {
|
|
|
1115
1115
|
}
|
|
1116
1116
|
get domAtom() { return false; }
|
|
1117
1117
|
get ignoreForCoords() { return false; }
|
|
1118
|
+
isText(text) { return false; }
|
|
1118
1119
|
}
|
|
1119
1120
|
// A widget desc represents a widget decoration, which is a DOM node
|
|
1120
1121
|
// drawn between the document nodes.
|
|
@@ -1377,15 +1378,7 @@ class NodeViewDesc extends ViewDesc {
|
|
|
1377
1378
|
let { from, to } = view.state.selection;
|
|
1378
1379
|
if (!(view.state.selection instanceof TextSelection) || from < pos || to > pos + this.node.content.size)
|
|
1379
1380
|
return null;
|
|
1380
|
-
let
|
|
1381
|
-
let textBefore = textNodeBefore$1(sel.focusNode, sel.focusOffset);
|
|
1382
|
-
let textAfter = textNodeAfter$1(sel.focusNode, sel.focusOffset);
|
|
1383
|
-
let textNode = textBefore || textAfter;
|
|
1384
|
-
if (textBefore && textAfter && textBefore != textAfter) {
|
|
1385
|
-
let descAfter = textAfter.pmViewDesc;
|
|
1386
|
-
if (!descAfter || descAfter instanceof TextViewDesc && descAfter.node.text != textAfter.nodeValue)
|
|
1387
|
-
textNode = textAfter;
|
|
1388
|
-
}
|
|
1381
|
+
let textNode = view.input.compositionNode;
|
|
1389
1382
|
if (!textNode || !this.dom.contains(textNode.parentNode))
|
|
1390
1383
|
return null;
|
|
1391
1384
|
if (this.node.inlineContent) {
|
|
@@ -1527,6 +1520,7 @@ class TextViewDesc extends NodeViewDesc {
|
|
|
1527
1520
|
this.dirty = NODE_DIRTY;
|
|
1528
1521
|
}
|
|
1529
1522
|
get domAtom() { return false; }
|
|
1523
|
+
isText(text) { return this.node.text == text; }
|
|
1530
1524
|
}
|
|
1531
1525
|
// A dummy desc used to tag trailing BR or IMG nodes created to work
|
|
1532
1526
|
// around contentEditable terribleness.
|
|
@@ -3000,6 +2994,7 @@ class InputState {
|
|
|
3000
2994
|
this.lastTouch = 0;
|
|
3001
2995
|
this.lastAndroidDelete = 0;
|
|
3002
2996
|
this.composing = false;
|
|
2997
|
+
this.compositionNode = null;
|
|
3003
2998
|
this.composingTimeout = -1;
|
|
3004
2999
|
this.compositionNodes = [];
|
|
3005
3000
|
this.compositionEndedAt = -2e8;
|
|
@@ -3436,6 +3431,7 @@ editHandlers.compositionend = (view, event) => {
|
|
|
3436
3431
|
view.input.composing = false;
|
|
3437
3432
|
view.input.compositionEndedAt = event.timeStamp;
|
|
3438
3433
|
view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
|
|
3434
|
+
view.input.compositionNode = null;
|
|
3439
3435
|
if (view.input.compositionPendingChanges)
|
|
3440
3436
|
Promise.resolve().then(() => view.domObserver.flush());
|
|
3441
3437
|
view.input.compositionID++;
|
|
@@ -3455,6 +3451,25 @@ function clearComposition(view) {
|
|
|
3455
3451
|
while (view.input.compositionNodes.length > 0)
|
|
3456
3452
|
view.input.compositionNodes.pop().markParentsDirty();
|
|
3457
3453
|
}
|
|
3454
|
+
function findCompositionNode(view) {
|
|
3455
|
+
let sel = view.domSelectionRange();
|
|
3456
|
+
if (!sel.focusNode)
|
|
3457
|
+
return null;
|
|
3458
|
+
let textBefore = textNodeBefore$1(sel.focusNode, sel.focusOffset);
|
|
3459
|
+
let textAfter = textNodeAfter$1(sel.focusNode, sel.focusOffset);
|
|
3460
|
+
if (textBefore && textAfter && textBefore != textAfter) {
|
|
3461
|
+
let descAfter = textAfter.pmViewDesc;
|
|
3462
|
+
if (!descAfter || !descAfter.isText(textAfter.nodeValue)) {
|
|
3463
|
+
return textAfter;
|
|
3464
|
+
}
|
|
3465
|
+
else if (view.input.compositionNode == textAfter) {
|
|
3466
|
+
let descBefore = textBefore.pmViewDesc;
|
|
3467
|
+
if (!(!descBefore || !descBefore.isText(textBefore.nodeValue)))
|
|
3468
|
+
return textAfter;
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
return textBefore;
|
|
3472
|
+
}
|
|
3458
3473
|
function timestampFromCustomEvent() {
|
|
3459
3474
|
let event = document.createEvent("Event");
|
|
3460
3475
|
event.initEvent("event", true, true);
|
|
@@ -4689,9 +4704,25 @@ function checkCSS(view) {
|
|
|
4689
4704
|
cssCheckWarned = true;
|
|
4690
4705
|
}
|
|
4691
4706
|
}
|
|
4707
|
+
function rangeToSelectionRange(view, range) {
|
|
4708
|
+
let anchorNode = range.startContainer, anchorOffset = range.startOffset;
|
|
4709
|
+
let focusNode = range.endContainer, focusOffset = range.endOffset;
|
|
4710
|
+
let currentAnchor = view.domAtPos(view.state.selection.anchor);
|
|
4711
|
+
// Since such a range doesn't distinguish between anchor and head,
|
|
4712
|
+
// use a heuristic that flips it around if its end matches the
|
|
4713
|
+
// current anchor.
|
|
4714
|
+
if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
|
|
4715
|
+
[anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
|
|
4716
|
+
return { anchorNode, anchorOffset, focusNode, focusOffset };
|
|
4717
|
+
}
|
|
4692
4718
|
// Used to work around a Safari Selection/shadow DOM bug
|
|
4693
4719
|
// Based on https://github.com/codemirror/dev/issues/414 fix
|
|
4694
|
-
function safariShadowSelectionRange(view) {
|
|
4720
|
+
function safariShadowSelectionRange(view, selection) {
|
|
4721
|
+
if (selection.getComposedRanges) {
|
|
4722
|
+
let range = selection.getComposedRanges(view.root)[0];
|
|
4723
|
+
if (range)
|
|
4724
|
+
return rangeToSelectionRange(view, range);
|
|
4725
|
+
}
|
|
4695
4726
|
let found;
|
|
4696
4727
|
function read(event) {
|
|
4697
4728
|
event.preventDefault();
|
|
@@ -4706,15 +4737,7 @@ function safariShadowSelectionRange(view) {
|
|
|
4706
4737
|
view.dom.addEventListener("beforeinput", read, true);
|
|
4707
4738
|
document.execCommand("indent");
|
|
4708
4739
|
view.dom.removeEventListener("beforeinput", read, true);
|
|
4709
|
-
|
|
4710
|
-
let focusNode = found.endContainer, focusOffset = found.endOffset;
|
|
4711
|
-
let currentAnchor = view.domAtPos(view.state.selection.anchor);
|
|
4712
|
-
// Since such a range doesn't distinguish between anchor and head,
|
|
4713
|
-
// use a heuristic that flips it around if its end matches the
|
|
4714
|
-
// current anchor.
|
|
4715
|
-
if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
|
|
4716
|
-
[anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
|
|
4717
|
-
return { anchorNode, anchorOffset, focusNode, focusOffset };
|
|
4740
|
+
return found ? rangeToSelectionRange(view, found) : null;
|
|
4718
4741
|
}
|
|
4719
4742
|
|
|
4720
4743
|
// Note that all referencing and parsing is done with the
|
|
@@ -5276,6 +5299,8 @@ class EditorView {
|
|
|
5276
5299
|
// tracks that and forces a selection reset when our update
|
|
5277
5300
|
// did write to the node.
|
|
5278
5301
|
let chromeKludge = chrome ? (this.trackWrites = this.domSelectionRange().focusNode) : null;
|
|
5302
|
+
if (this.composing)
|
|
5303
|
+
this.input.compositionNode = findCompositionNode(this);
|
|
5279
5304
|
if (redraw || !this.docView.update(state.doc, outerDeco, innerDeco, this)) {
|
|
5280
5305
|
this.docView.updateOuterDeco(outerDeco);
|
|
5281
5306
|
this.docView.destroy();
|
|
@@ -5590,8 +5615,9 @@ class EditorView {
|
|
|
5590
5615
|
@internal
|
|
5591
5616
|
*/
|
|
5592
5617
|
domSelectionRange() {
|
|
5593
|
-
|
|
5594
|
-
|
|
5618
|
+
let sel = this.domSelection();
|
|
5619
|
+
return safari && this.root.nodeType === 11 &&
|
|
5620
|
+
deepActiveElement(this.dom.ownerDocument) == this.dom && safariShadowSelectionRange(this, sel) || sel;
|
|
5595
5621
|
}
|
|
5596
5622
|
/**
|
|
5597
5623
|
@internal
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prosemirror-view",
|
|
3
|
-
"version": "1.33.
|
|
3
|
+
"version": "1.33.4",
|
|
4
4
|
"description": "ProseMirror's view component",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"url": "git://github.com/prosemirror/prosemirror-view.git"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"prosemirror-model": "^1.
|
|
31
|
+
"prosemirror-model": "^1.20.0",
|
|
32
32
|
"prosemirror-state": "^1.0.0",
|
|
33
33
|
"prosemirror-transform": "^1.1.0"
|
|
34
34
|
},
|
package/src/dom.ts
CHANGED
|
@@ -71,7 +71,7 @@ export function textNodeBefore(node: Node, offset: number) {
|
|
|
71
71
|
for (;;) {
|
|
72
72
|
if (node.nodeType == 3 && offset) return node as Text
|
|
73
73
|
if (node.nodeType == 1 && offset > 0) {
|
|
74
|
-
if ((node as HTMLElement).contentEditable
|
|
74
|
+
if ((node as HTMLElement).contentEditable == "false") return null
|
|
75
75
|
node = node.childNodes[offset - 1]
|
|
76
76
|
offset = nodeSize(node)
|
|
77
77
|
} else if (node.parentNode && !hasBlockDesc(node)) {
|
|
@@ -87,7 +87,7 @@ export function textNodeAfter(node: Node, offset: number) {
|
|
|
87
87
|
for (;;) {
|
|
88
88
|
if (node.nodeType == 3 && offset < node.nodeValue!.length) return node as Text
|
|
89
89
|
if (node.nodeType == 1 && offset < node.childNodes.length) {
|
|
90
|
-
if ((node as HTMLElement).contentEditable
|
|
90
|
+
if ((node as HTMLElement).contentEditable == "false") return null
|
|
91
91
|
node = node.childNodes[offset]
|
|
92
92
|
offset = 0
|
|
93
93
|
} else if (node.parentNode && !hasBlockDesc(node)) {
|
package/src/domchange.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Fragment, DOMParser,
|
|
1
|
+
import {Fragment, DOMParser, TagParseRule, Node, Mark, ResolvedPos} from "prosemirror-model"
|
|
2
2
|
import {Selection, TextSelection} from "prosemirror-state"
|
|
3
3
|
|
|
4
4
|
import {selectionBetween, selectionFromDOM, selectionToDOM} from "./selection"
|
|
@@ -55,7 +55,7 @@ function parseBetween(view: EditorView, from_: number, to_: number) {
|
|
|
55
55
|
return {doc, sel, from, to}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function ruleFromNode(dom: DOMNode):
|
|
58
|
+
function ruleFromNode(dom: DOMNode): Omit<TagParseRule, "tag"> | null {
|
|
59
59
|
let desc = dom.pmViewDesc
|
|
60
60
|
if (desc) {
|
|
61
61
|
return desc.parseRule()
|
package/src/domobserver.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {Selection} from "prosemirror-state"
|
|
2
2
|
import * as browser from "./browser"
|
|
3
|
-
import {domIndex, isEquivalentPosition, selectionCollapsed, parentNode, DOMSelectionRange, DOMNode} from "./dom"
|
|
3
|
+
import {domIndex, isEquivalentPosition, selectionCollapsed, parentNode, DOMSelectionRange, DOMNode, DOMSelection} from "./dom"
|
|
4
4
|
import {hasFocusAndSelection, selectionToDOM, selectionFromDOM} from "./selection"
|
|
5
5
|
import {EditorView} from "./index"
|
|
6
6
|
|
|
@@ -278,9 +278,27 @@ function checkCSS(view: EditorView) {
|
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
function rangeToSelectionRange(view: EditorView, range: StaticRange) {
|
|
282
|
+
let anchorNode = range.startContainer, anchorOffset = range.startOffset
|
|
283
|
+
let focusNode = range.endContainer, focusOffset = range.endOffset
|
|
284
|
+
|
|
285
|
+
let currentAnchor = view.domAtPos(view.state.selection.anchor)
|
|
286
|
+
// Since such a range doesn't distinguish between anchor and head,
|
|
287
|
+
// use a heuristic that flips it around if its end matches the
|
|
288
|
+
// current anchor.
|
|
289
|
+
if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
|
|
290
|
+
[anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset]
|
|
291
|
+
return {anchorNode, anchorOffset, focusNode, focusOffset}
|
|
292
|
+
}
|
|
293
|
+
|
|
281
294
|
// Used to work around a Safari Selection/shadow DOM bug
|
|
282
295
|
// Based on https://github.com/codemirror/dev/issues/414 fix
|
|
283
|
-
export function safariShadowSelectionRange(view: EditorView): DOMSelectionRange {
|
|
296
|
+
export function safariShadowSelectionRange(view: EditorView, selection: DOMSelection): DOMSelectionRange | null {
|
|
297
|
+
if ((selection as any).getComposedRanges) {
|
|
298
|
+
let range = (selection as any).getComposedRanges(view.root)[0] as StaticRange
|
|
299
|
+
if (range) return rangeToSelectionRange(view, range)
|
|
300
|
+
}
|
|
301
|
+
|
|
284
302
|
let found: StaticRange | undefined
|
|
285
303
|
function read(event: InputEvent) {
|
|
286
304
|
event.preventDefault()
|
|
@@ -297,14 +315,5 @@ export function safariShadowSelectionRange(view: EditorView): DOMSelectionRange
|
|
|
297
315
|
document.execCommand("indent")
|
|
298
316
|
view.dom.removeEventListener("beforeinput", read, true)
|
|
299
317
|
|
|
300
|
-
|
|
301
|
-
let focusNode = found!.endContainer, focusOffset = found!.endOffset
|
|
302
|
-
|
|
303
|
-
let currentAnchor = view.domAtPos(view.state.selection.anchor)
|
|
304
|
-
// Since such a range doesn't distinguish between anchor and head,
|
|
305
|
-
// use a heuristic that flips it around if its end matches the
|
|
306
|
-
// current anchor.
|
|
307
|
-
if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
|
|
308
|
-
[anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset]
|
|
309
|
-
return {anchorNode, anchorOffset, focusNode, focusOffset}
|
|
318
|
+
return found ? rangeToSelectionRange(view, found) : null
|
|
310
319
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {scrollRectIntoView, posAtCoords, coordsAtPos, endOfTextblock, storeScrol
|
|
|
5
5
|
resetScrollPos, focusPreventScroll} from "./domcoords"
|
|
6
6
|
import {docViewDesc, ViewDesc, NodeView, NodeViewDesc} from "./viewdesc"
|
|
7
7
|
import {initInput, destroyInput, dispatchEvent, ensureListeners, clearComposition,
|
|
8
|
-
InputState, doPaste, Dragging} from "./input"
|
|
8
|
+
InputState, doPaste, Dragging, findCompositionNode} from "./input"
|
|
9
9
|
import {selectionToDOM, anchorInRightPlace, syncNodeSelection} from "./selection"
|
|
10
10
|
import {Decoration, viewDecorations, DecorationSource} from "./decoration"
|
|
11
11
|
import {DOMObserver, safariShadowSelectionRange} from "./domobserver"
|
|
@@ -197,6 +197,7 @@ export class EditorView {
|
|
|
197
197
|
// tracks that and forces a selection reset when our update
|
|
198
198
|
// did write to the node.
|
|
199
199
|
let chromeKludge = browser.chrome ? (this.trackWrites = this.domSelectionRange().focusNode) : null
|
|
200
|
+
if (this.composing) this.input.compositionNode = findCompositionNode(this)
|
|
200
201
|
if (redraw || !this.docView.update(state.doc, outerDeco, innerDeco, this)) {
|
|
201
202
|
this.docView.updateOuterDeco(outerDeco)
|
|
202
203
|
this.docView.destroy()
|
|
@@ -487,8 +488,9 @@ export class EditorView {
|
|
|
487
488
|
|
|
488
489
|
/// @internal
|
|
489
490
|
domSelectionRange(): DOMSelectionRange {
|
|
490
|
-
|
|
491
|
-
|
|
491
|
+
let sel = this.domSelection()
|
|
492
|
+
return browser.safari && this.root.nodeType === 11 &&
|
|
493
|
+
deepActiveElement(this.dom.ownerDocument) == this.dom && safariShadowSelectionRange(this, sel) || sel
|
|
492
494
|
}
|
|
493
495
|
|
|
494
496
|
/// @internal
|
package/src/input.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as browser from "./browser"
|
|
|
6
6
|
import {captureKeyDown} from "./capturekeys"
|
|
7
7
|
import {parseFromClipboard, serializeForClipboard} from "./clipboard"
|
|
8
8
|
import {selectionBetween, selectionToDOM, selectionFromDOM} from "./selection"
|
|
9
|
-
import {keyEvent, DOMNode} from "./dom"
|
|
9
|
+
import {keyEvent, DOMNode, textNodeBefore, textNodeAfter} from "./dom"
|
|
10
10
|
import {EditorView} from "./index"
|
|
11
11
|
import {ViewDesc} from "./viewdesc"
|
|
12
12
|
|
|
@@ -30,6 +30,7 @@ export class InputState {
|
|
|
30
30
|
lastTouch = 0
|
|
31
31
|
lastAndroidDelete = 0
|
|
32
32
|
composing = false
|
|
33
|
+
compositionNode: Text | null = null
|
|
33
34
|
composingTimeout = -1
|
|
34
35
|
compositionNodes: ViewDesc[] = []
|
|
35
36
|
compositionEndedAt = -2e8
|
|
@@ -490,6 +491,7 @@ editHandlers.compositionend = (view, event) => {
|
|
|
490
491
|
view.input.composing = false
|
|
491
492
|
view.input.compositionEndedAt = event.timeStamp
|
|
492
493
|
view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0
|
|
494
|
+
view.input.compositionNode = null
|
|
493
495
|
if (view.input.compositionPendingChanges) Promise.resolve().then(() => view.domObserver.flush())
|
|
494
496
|
view.input.compositionID++
|
|
495
497
|
scheduleComposeEnd(view, 20)
|
|
@@ -509,6 +511,24 @@ export function clearComposition(view: EditorView) {
|
|
|
509
511
|
while (view.input.compositionNodes.length > 0) view.input.compositionNodes.pop()!.markParentsDirty()
|
|
510
512
|
}
|
|
511
513
|
|
|
514
|
+
export function findCompositionNode(view: EditorView) {
|
|
515
|
+
let sel = view.domSelectionRange()
|
|
516
|
+
if (!sel.focusNode) return null
|
|
517
|
+
let textBefore = textNodeBefore(sel.focusNode, sel.focusOffset)
|
|
518
|
+
let textAfter = textNodeAfter(sel.focusNode, sel.focusOffset)
|
|
519
|
+
if (textBefore && textAfter && textBefore != textAfter) {
|
|
520
|
+
let descAfter = textAfter.pmViewDesc
|
|
521
|
+
if (!descAfter || !descAfter.isText(textAfter.nodeValue!)) {
|
|
522
|
+
return textAfter
|
|
523
|
+
} else if (view.input.compositionNode == textAfter) {
|
|
524
|
+
let descBefore = textBefore.pmViewDesc
|
|
525
|
+
if (!(!descBefore || !descBefore.isText(textBefore.nodeValue!)))
|
|
526
|
+
return textAfter
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return textBefore
|
|
530
|
+
}
|
|
531
|
+
|
|
512
532
|
function timestampFromCustomEvent() {
|
|
513
533
|
let event = document.createEvent("Event")
|
|
514
534
|
event.initEvent("event", true, true)
|