prosemirror-view 1.36.0 → 1.37.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 +10 -0
- package/dist/index.cjs +15 -9
- package/dist/index.d.cts +25 -13
- package/dist/index.d.ts +25 -13
- package/dist/index.js +15 -11
- package/package.json +1 -1
- package/src/README.md +2 -0
- package/src/domobserver.ts +2 -2
- package/src/index.ts +1 -1
- package/src/selection.ts +1 -1
- package/src/viewdesc.ts +40 -27
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## 1.37.0 (2024-11-23)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue on Safari where the (incorrect) top-level selection was used, instead of the workaround that also works inside shadow roots.
|
|
6
|
+
|
|
7
|
+
### New features
|
|
8
|
+
|
|
9
|
+
Mark views can now provide an `ignoreMutation` method. Add ViewMutationRecord to doc template
|
|
10
|
+
|
|
1
11
|
## 1.36.0 (2024-11-05)
|
|
2
12
|
|
|
3
13
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -935,19 +935,20 @@ var ViewDesc = function () {
|
|
|
935
935
|
}
|
|
936
936
|
}, {
|
|
937
937
|
key: "setSelection",
|
|
938
|
-
value: function setSelection(anchor, head,
|
|
938
|
+
value: function setSelection(anchor, head, view) {
|
|
939
939
|
var force = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
940
940
|
var from = Math.min(anchor, head),
|
|
941
941
|
to = Math.max(anchor, head);
|
|
942
942
|
for (var i = 0, offset = 0; i < this.children.length; i++) {
|
|
943
943
|
var child = this.children[i],
|
|
944
944
|
end = offset + child.size;
|
|
945
|
-
if (from > offset && to < end) return child.setSelection(anchor - offset - child.border, head - offset - child.border,
|
|
945
|
+
if (from > offset && to < end) return child.setSelection(anchor - offset - child.border, head - offset - child.border, view, force);
|
|
946
946
|
offset = end;
|
|
947
947
|
}
|
|
948
948
|
var anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
|
|
949
949
|
var headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
|
|
950
|
-
var domSel = root.getSelection();
|
|
950
|
+
var domSel = view.root.getSelection();
|
|
951
|
+
var selRange = view.domSelectionRange();
|
|
951
952
|
var brKludge = false;
|
|
952
953
|
if ((gecko || safari) && anchor == head) {
|
|
953
954
|
var _anchorDOM = anchorDOM,
|
|
@@ -973,11 +974,11 @@ var ViewDesc = function () {
|
|
|
973
974
|
brKludge = prev && (prev.nodeName == "BR" || prev.contentEditable == "false");
|
|
974
975
|
}
|
|
975
976
|
}
|
|
976
|
-
if (gecko &&
|
|
977
|
-
var _after2 =
|
|
977
|
+
if (gecko && selRange.focusNode && selRange.focusNode != headDOM.node && selRange.focusNode.nodeType == 1) {
|
|
978
|
+
var _after2 = selRange.focusNode.childNodes[selRange.focusOffset];
|
|
978
979
|
if (_after2 && _after2.contentEditable == "false") force = true;
|
|
979
980
|
}
|
|
980
|
-
if (!(force || brKludge && safari) && isEquivalentPosition(anchorDOM.node, anchorDOM.offset,
|
|
981
|
+
if (!(force || brKludge && safari) && isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) && isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset)) return;
|
|
981
982
|
var domSelExtended = false;
|
|
982
983
|
if ((domSel.extend || anchor == head) && !brKludge) {
|
|
983
984
|
domSel.collapse(anchorDOM.node, anchorDOM.offset);
|
|
@@ -1213,6 +1214,11 @@ var MarkViewDesc = function (_ViewDesc3) {
|
|
|
1213
1214
|
copy.children = nodes;
|
|
1214
1215
|
return copy;
|
|
1215
1216
|
}
|
|
1217
|
+
}, {
|
|
1218
|
+
key: "ignoreMutation",
|
|
1219
|
+
value: function ignoreMutation(mutation) {
|
|
1220
|
+
return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : _get(_getPrototypeOf(MarkViewDesc.prototype), "ignoreMutation", this).call(this, mutation);
|
|
1221
|
+
}
|
|
1216
1222
|
}, {
|
|
1217
1223
|
key: "destroy",
|
|
1218
1224
|
value: function destroy() {
|
|
@@ -1593,8 +1599,8 @@ var CustomNodeViewDesc = function (_NodeViewDesc2) {
|
|
|
1593
1599
|
}
|
|
1594
1600
|
}, {
|
|
1595
1601
|
key: "setSelection",
|
|
1596
|
-
value: function setSelection(anchor, head,
|
|
1597
|
-
this.spec.setSelection ? this.spec.setSelection(anchor, head, root) : _get(_getPrototypeOf(CustomNodeViewDesc.prototype), "setSelection", this).call(this, anchor, head,
|
|
1602
|
+
value: function setSelection(anchor, head, view, force) {
|
|
1603
|
+
this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root) : _get(_getPrototypeOf(CustomNodeViewDesc.prototype), "setSelection", this).call(this, anchor, head, view, force);
|
|
1598
1604
|
}
|
|
1599
1605
|
}, {
|
|
1600
1606
|
key: "destroy",
|
|
@@ -2179,7 +2185,7 @@ function selectionToDOM(view) {
|
|
|
2179
2185
|
if (!sel.$from.parent.inlineContent) resetEditableFrom = temporarilyEditableNear(view, sel.from);
|
|
2180
2186
|
if (!sel.empty && !sel.$from.parent.inlineContent) resetEditableTo = temporarilyEditableNear(view, sel.to);
|
|
2181
2187
|
}
|
|
2182
|
-
view.docView.setSelection(anchor, head, view
|
|
2188
|
+
view.docView.setSelection(anchor, head, view, force);
|
|
2183
2189
|
if (brokenSelectBetweenUneditable) {
|
|
2184
2190
|
if (resetEditableFrom) resetEditable(resetEditableFrom);
|
|
2185
2191
|
if (resetEditableTo) resetEditable(resetEditableTo);
|
package/dist/index.d.cts
CHANGED
|
@@ -238,6 +238,17 @@ declare global {
|
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
/**
|
|
241
|
+
A ViewMutationRecord represents a DOM
|
|
242
|
+
[mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
|
|
243
|
+
or a selection change happens within the view. When the change is
|
|
244
|
+
a selection change, the record will have a `type` property of
|
|
245
|
+
`"selection"` (which doesn't occur for native mutation records).
|
|
246
|
+
*/
|
|
247
|
+
type ViewMutationRecord = MutationRecord | {
|
|
248
|
+
type: "selection";
|
|
249
|
+
target: DOMNode;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
241
252
|
By default, document nodes are rendered using the result of the
|
|
242
253
|
[`toDOM`](https://prosemirror.net/docs/ref/#model.NodeSpec.toDOM) method of their spec, and managed
|
|
243
254
|
entirely by the editor. For some use cases, such as embedded
|
|
@@ -308,16 +319,11 @@ interface NodeView {
|
|
|
308
319
|
*/
|
|
309
320
|
stopEvent?: (event: Event) => boolean;
|
|
310
321
|
/**
|
|
311
|
-
Called when a
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
a selection change, the record will have a `type` property of
|
|
315
|
-
`"selection"` (which doesn't occur for native mutation records).
|
|
316
|
-
Return false if the editor should re-read the selection or
|
|
317
|
-
re-parse the range around the mutation, true if it can safely be
|
|
318
|
-
ignored.
|
|
322
|
+
Called when a [mutation](https://prosemirror.net/docs/ref/#view.ViewMutationRecord) happens within the
|
|
323
|
+
view. Return false if the editor should re-read the selection or re-parse
|
|
324
|
+
the range around the mutation, true if it can safely be ignored.
|
|
319
325
|
*/
|
|
320
|
-
ignoreMutation?: (mutation:
|
|
326
|
+
ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
|
|
321
327
|
/**
|
|
322
328
|
Called when the node view is removed from the editor or the whole
|
|
323
329
|
editor is destroyed.
|
|
@@ -344,6 +350,12 @@ interface MarkView {
|
|
|
344
350
|
*/
|
|
345
351
|
contentDOM?: HTMLElement | null;
|
|
346
352
|
/**
|
|
353
|
+
Called when a [mutation](https://prosemirror.net/docs/ref/#view.ViewMutationRecord) happens within the
|
|
354
|
+
view. Return false if the editor should re-read the selection or re-parse
|
|
355
|
+
the range around the mutation, true if it can safely be ignored.
|
|
356
|
+
*/
|
|
357
|
+
ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
|
|
358
|
+
/**
|
|
347
359
|
Called when the mark view is removed from the editor or the whole
|
|
348
360
|
editor is destroyed.
|
|
349
361
|
*/
|
|
@@ -391,8 +403,8 @@ declare class ViewDesc {
|
|
|
391
403
|
};
|
|
392
404
|
emptyChildAt(side: number): boolean;
|
|
393
405
|
domAfterPos(pos: number): DOMNode;
|
|
394
|
-
setSelection(anchor: number, head: number,
|
|
395
|
-
ignoreMutation(mutation:
|
|
406
|
+
setSelection(anchor: number, head: number, view: EditorView, force?: boolean): void;
|
|
407
|
+
ignoreMutation(mutation: ViewMutationRecord): boolean;
|
|
396
408
|
get contentLost(): boolean | null;
|
|
397
409
|
markDirty(from: number, to: number): void;
|
|
398
410
|
markParentsDirty(): void;
|
|
@@ -441,7 +453,7 @@ declare class TextViewDesc extends NodeViewDesc {
|
|
|
441
453
|
offset: number;
|
|
442
454
|
};
|
|
443
455
|
localPosFromDOM(dom: DOMNode, offset: number, bias: number): number;
|
|
444
|
-
ignoreMutation(mutation:
|
|
456
|
+
ignoreMutation(mutation: ViewMutationRecord): boolean;
|
|
445
457
|
slice(from: number, to: number, view: EditorView): TextViewDesc;
|
|
446
458
|
markDirty(from: number, to: number): void;
|
|
447
459
|
get domAtom(): boolean;
|
|
@@ -955,4 +967,4 @@ interface DirectEditorProps extends EditorProps {
|
|
|
955
967
|
dispatchTransaction?: (tr: Transaction) => void;
|
|
956
968
|
}
|
|
957
969
|
|
|
958
|
-
export { type DOMEventMap, Decoration, type DecorationAttrs, DecorationSet, type DecorationSource, type DirectEditorProps, type EditorProps, EditorView, type MarkView, type MarkViewConstructor, type NodeView, type NodeViewConstructor };
|
|
970
|
+
export { type DOMEventMap, Decoration, type DecorationAttrs, DecorationSet, type DecorationSource, type DirectEditorProps, type EditorProps, EditorView, type MarkView, type MarkViewConstructor, type NodeView, type NodeViewConstructor, type ViewMutationRecord };
|
package/dist/index.d.ts
CHANGED
|
@@ -238,6 +238,17 @@ declare global {
|
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
/**
|
|
241
|
+
A ViewMutationRecord represents a DOM
|
|
242
|
+
[mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
|
|
243
|
+
or a selection change happens within the view. When the change is
|
|
244
|
+
a selection change, the record will have a `type` property of
|
|
245
|
+
`"selection"` (which doesn't occur for native mutation records).
|
|
246
|
+
*/
|
|
247
|
+
type ViewMutationRecord = MutationRecord | {
|
|
248
|
+
type: "selection";
|
|
249
|
+
target: DOMNode;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
241
252
|
By default, document nodes are rendered using the result of the
|
|
242
253
|
[`toDOM`](https://prosemirror.net/docs/ref/#model.NodeSpec.toDOM) method of their spec, and managed
|
|
243
254
|
entirely by the editor. For some use cases, such as embedded
|
|
@@ -308,16 +319,11 @@ interface NodeView {
|
|
|
308
319
|
*/
|
|
309
320
|
stopEvent?: (event: Event) => boolean;
|
|
310
321
|
/**
|
|
311
|
-
Called when a
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
a selection change, the record will have a `type` property of
|
|
315
|
-
`"selection"` (which doesn't occur for native mutation records).
|
|
316
|
-
Return false if the editor should re-read the selection or
|
|
317
|
-
re-parse the range around the mutation, true if it can safely be
|
|
318
|
-
ignored.
|
|
322
|
+
Called when a [mutation](https://prosemirror.net/docs/ref/#view.ViewMutationRecord) happens within the
|
|
323
|
+
view. Return false if the editor should re-read the selection or re-parse
|
|
324
|
+
the range around the mutation, true if it can safely be ignored.
|
|
319
325
|
*/
|
|
320
|
-
ignoreMutation?: (mutation:
|
|
326
|
+
ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
|
|
321
327
|
/**
|
|
322
328
|
Called when the node view is removed from the editor or the whole
|
|
323
329
|
editor is destroyed.
|
|
@@ -344,6 +350,12 @@ interface MarkView {
|
|
|
344
350
|
*/
|
|
345
351
|
contentDOM?: HTMLElement | null;
|
|
346
352
|
/**
|
|
353
|
+
Called when a [mutation](https://prosemirror.net/docs/ref/#view.ViewMutationRecord) happens within the
|
|
354
|
+
view. Return false if the editor should re-read the selection or re-parse
|
|
355
|
+
the range around the mutation, true if it can safely be ignored.
|
|
356
|
+
*/
|
|
357
|
+
ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
|
|
358
|
+
/**
|
|
347
359
|
Called when the mark view is removed from the editor or the whole
|
|
348
360
|
editor is destroyed.
|
|
349
361
|
*/
|
|
@@ -391,8 +403,8 @@ declare class ViewDesc {
|
|
|
391
403
|
};
|
|
392
404
|
emptyChildAt(side: number): boolean;
|
|
393
405
|
domAfterPos(pos: number): DOMNode;
|
|
394
|
-
setSelection(anchor: number, head: number,
|
|
395
|
-
ignoreMutation(mutation:
|
|
406
|
+
setSelection(anchor: number, head: number, view: EditorView, force?: boolean): void;
|
|
407
|
+
ignoreMutation(mutation: ViewMutationRecord): boolean;
|
|
396
408
|
get contentLost(): boolean | null;
|
|
397
409
|
markDirty(from: number, to: number): void;
|
|
398
410
|
markParentsDirty(): void;
|
|
@@ -441,7 +453,7 @@ declare class TextViewDesc extends NodeViewDesc {
|
|
|
441
453
|
offset: number;
|
|
442
454
|
};
|
|
443
455
|
localPosFromDOM(dom: DOMNode, offset: number, bias: number): number;
|
|
444
|
-
ignoreMutation(mutation:
|
|
456
|
+
ignoreMutation(mutation: ViewMutationRecord): boolean;
|
|
445
457
|
slice(from: number, to: number, view: EditorView): TextViewDesc;
|
|
446
458
|
markDirty(from: number, to: number): void;
|
|
447
459
|
get domAtom(): boolean;
|
|
@@ -955,4 +967,4 @@ interface DirectEditorProps extends EditorProps {
|
|
|
955
967
|
dispatchTransaction?: (tr: Transaction) => void;
|
|
956
968
|
}
|
|
957
969
|
|
|
958
|
-
export { type DOMEventMap, Decoration, type DecorationAttrs, DecorationSet, type DecorationSource, type DirectEditorProps, type EditorProps, EditorView, type MarkView, type MarkViewConstructor, type NodeView, type NodeViewConstructor };
|
|
970
|
+
export { type DOMEventMap, Decoration, type DecorationAttrs, DecorationSet, type DecorationSource, type DirectEditorProps, type EditorProps, EditorView, type MarkView, type MarkViewConstructor, type NodeView, type NodeViewConstructor, type ViewMutationRecord };
|
package/dist/index.js
CHANGED
|
@@ -996,18 +996,19 @@ class ViewDesc {
|
|
|
996
996
|
// custom things with the selection. Note that this falls apart when
|
|
997
997
|
// a selection starts in such a node and ends in another, in which
|
|
998
998
|
// case we just use whatever domFromPos produces as a best effort.
|
|
999
|
-
setSelection(anchor, head,
|
|
999
|
+
setSelection(anchor, head, view, force = false) {
|
|
1000
1000
|
// If the selection falls entirely in a child, give it to that child
|
|
1001
1001
|
let from = Math.min(anchor, head), to = Math.max(anchor, head);
|
|
1002
1002
|
for (let i = 0, offset = 0; i < this.children.length; i++) {
|
|
1003
1003
|
let child = this.children[i], end = offset + child.size;
|
|
1004
1004
|
if (from > offset && to < end)
|
|
1005
|
-
return child.setSelection(anchor - offset - child.border, head - offset - child.border,
|
|
1005
|
+
return child.setSelection(anchor - offset - child.border, head - offset - child.border, view, force);
|
|
1006
1006
|
offset = end;
|
|
1007
1007
|
}
|
|
1008
1008
|
let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
|
|
1009
1009
|
let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
|
|
1010
|
-
let domSel = root.getSelection();
|
|
1010
|
+
let domSel = view.root.getSelection();
|
|
1011
|
+
let selRange = view.domSelectionRange();
|
|
1011
1012
|
let brKludge = false;
|
|
1012
1013
|
// On Firefox, using Selection.collapse to put the cursor after a
|
|
1013
1014
|
// BR node for some reason doesn't always work (#1073). On Safari,
|
|
@@ -1038,14 +1039,14 @@ class ViewDesc {
|
|
|
1038
1039
|
}
|
|
1039
1040
|
// Firefox can act strangely when the selection is in front of an
|
|
1040
1041
|
// uneditable node. See #1163 and https://bugzilla.mozilla.org/show_bug.cgi?id=1709536
|
|
1041
|
-
if (gecko &&
|
|
1042
|
-
let after =
|
|
1042
|
+
if (gecko && selRange.focusNode && selRange.focusNode != headDOM.node && selRange.focusNode.nodeType == 1) {
|
|
1043
|
+
let after = selRange.focusNode.childNodes[selRange.focusOffset];
|
|
1043
1044
|
if (after && after.contentEditable == "false")
|
|
1044
1045
|
force = true;
|
|
1045
1046
|
}
|
|
1046
1047
|
if (!(force || brKludge && safari) &&
|
|
1047
|
-
isEquivalentPosition(anchorDOM.node, anchorDOM.offset,
|
|
1048
|
-
isEquivalentPosition(headDOM.node, headDOM.offset,
|
|
1048
|
+
isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) &&
|
|
1049
|
+
isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset))
|
|
1049
1050
|
return;
|
|
1050
1051
|
// Selection.extend can be used to create an 'inverted' selection
|
|
1051
1052
|
// (one where the focus is before the anchor), but not all
|
|
@@ -1234,6 +1235,9 @@ class MarkViewDesc extends ViewDesc {
|
|
|
1234
1235
|
copy.children = nodes;
|
|
1235
1236
|
return copy;
|
|
1236
1237
|
}
|
|
1238
|
+
ignoreMutation(mutation) {
|
|
1239
|
+
return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation);
|
|
1240
|
+
}
|
|
1237
1241
|
destroy() {
|
|
1238
1242
|
if (this.spec.destroy)
|
|
1239
1243
|
this.spec.destroy();
|
|
@@ -1577,9 +1581,9 @@ class CustomNodeViewDesc extends NodeViewDesc {
|
|
|
1577
1581
|
deselectNode() {
|
|
1578
1582
|
this.spec.deselectNode ? this.spec.deselectNode() : super.deselectNode();
|
|
1579
1583
|
}
|
|
1580
|
-
setSelection(anchor, head,
|
|
1581
|
-
this.spec.setSelection ? this.spec.setSelection(anchor, head, root)
|
|
1582
|
-
: super.setSelection(anchor, head,
|
|
1584
|
+
setSelection(anchor, head, view, force) {
|
|
1585
|
+
this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root)
|
|
1586
|
+
: super.setSelection(anchor, head, view, force);
|
|
1583
1587
|
}
|
|
1584
1588
|
destroy() {
|
|
1585
1589
|
if (this.spec.destroy)
|
|
@@ -2239,7 +2243,7 @@ function selectionToDOM(view, force = false) {
|
|
|
2239
2243
|
if (!sel.empty && !sel.$from.parent.inlineContent)
|
|
2240
2244
|
resetEditableTo = temporarilyEditableNear(view, sel.to);
|
|
2241
2245
|
}
|
|
2242
|
-
view.docView.setSelection(anchor, head, view
|
|
2246
|
+
view.docView.setSelection(anchor, head, view, force);
|
|
2243
2247
|
if (brokenSelectBetweenUneditable) {
|
|
2244
2248
|
if (resetEditableFrom)
|
|
2245
2249
|
resetEditable(resetEditableFrom);
|
package/package.json
CHANGED
package/src/README.md
CHANGED
package/src/domobserver.ts
CHANGED
|
@@ -151,8 +151,8 @@ export class DOMObserver {
|
|
|
151
151
|
let desc = container && this.view.docView.nearestDesc(container)
|
|
152
152
|
if (desc && desc.ignoreMutation({
|
|
153
153
|
type: "selection",
|
|
154
|
-
target: container!.nodeType == 3 ? container!.parentNode : container
|
|
155
|
-
}
|
|
154
|
+
target: container!.nodeType == 3 ? container!.parentNode! : container!
|
|
155
|
+
})) {
|
|
156
156
|
this.setCurSelection()
|
|
157
157
|
return true
|
|
158
158
|
}
|
package/src/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {DOMSelection, DOMNode, DOMSelectionRange, deepActiveElement, clearReused
|
|
|
14
14
|
import * as browser from "./browser"
|
|
15
15
|
|
|
16
16
|
export {Decoration, DecorationSet, DecorationAttrs, DecorationSource} from "./decoration"
|
|
17
|
-
export {NodeView, MarkView} from "./viewdesc"
|
|
17
|
+
export {NodeView, MarkView, ViewMutationRecord} from "./viewdesc"
|
|
18
18
|
|
|
19
19
|
// Exported for testing
|
|
20
20
|
import {serializeForClipboard, parseFromClipboard} from "./clipboard"
|
package/src/selection.ts
CHANGED
|
@@ -84,7 +84,7 @@ export function selectionToDOM(view: EditorView, force = false) {
|
|
|
84
84
|
if (!sel.empty && !sel.$from.parent.inlineContent)
|
|
85
85
|
resetEditableTo = temporarilyEditableNear(view, sel.to)
|
|
86
86
|
}
|
|
87
|
-
view.docView.setSelection(anchor, head, view
|
|
87
|
+
view.docView.setSelection(anchor, head, view, force)
|
|
88
88
|
if (brokenSelectBetweenUneditable) {
|
|
89
89
|
if (resetEditableFrom) resetEditable(resetEditableFrom)
|
|
90
90
|
if (resetEditableTo) resetEditable(resetEditableTo)
|
package/src/viewdesc.ts
CHANGED
|
@@ -10,6 +10,13 @@ declare global {
|
|
|
10
10
|
interface Node { pmViewDesc?: ViewDesc }
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/// A ViewMutationRecord represents a DOM
|
|
14
|
+
/// [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
|
|
15
|
+
/// or a selection change happens within the view. When the change is
|
|
16
|
+
/// a selection change, the record will have a `type` property of
|
|
17
|
+
/// `"selection"` (which doesn't occur for native mutation records).
|
|
18
|
+
export type ViewMutationRecord = MutationRecord | { type: "selection", target: DOMNode }
|
|
19
|
+
|
|
13
20
|
/// By default, document nodes are rendered using the result of the
|
|
14
21
|
/// [`toDOM`](#model.NodeSpec.toDOM) method of their spec, and managed
|
|
15
22
|
/// entirely by the editor. For some use cases, such as embedded
|
|
@@ -70,15 +77,10 @@ export interface NodeView {
|
|
|
70
77
|
/// which this returns true are not handled by the editor.
|
|
71
78
|
stopEvent?: (event: Event) => boolean
|
|
72
79
|
|
|
73
|
-
/// Called when a
|
|
74
|
-
///
|
|
75
|
-
///
|
|
76
|
-
|
|
77
|
-
/// `"selection"` (which doesn't occur for native mutation records).
|
|
78
|
-
/// Return false if the editor should re-read the selection or
|
|
79
|
-
/// re-parse the range around the mutation, true if it can safely be
|
|
80
|
-
/// ignored.
|
|
81
|
-
ignoreMutation?: (mutation: MutationRecord) => boolean
|
|
80
|
+
/// Called when a [mutation](#view.ViewMutationRecord) happens within the
|
|
81
|
+
/// view. Return false if the editor should re-read the selection or re-parse
|
|
82
|
+
/// the range around the mutation, true if it can safely be ignored.
|
|
83
|
+
ignoreMutation?: (mutation: ViewMutationRecord) => boolean
|
|
82
84
|
|
|
83
85
|
/// Called when the node view is removed from the editor or the whole
|
|
84
86
|
/// editor is destroyed.
|
|
@@ -99,6 +101,12 @@ export interface MarkView {
|
|
|
99
101
|
/// The DOM node that should hold the mark's content. When this is not
|
|
100
102
|
/// present, the `dom` property is used as the content DOM.
|
|
101
103
|
contentDOM?: HTMLElement | null
|
|
104
|
+
|
|
105
|
+
/// Called when a [mutation](#view.ViewMutationRecord) happens within the
|
|
106
|
+
/// view. Return false if the editor should re-read the selection or re-parse
|
|
107
|
+
/// the range around the mutation, true if it can safely be ignored.
|
|
108
|
+
ignoreMutation?: (mutation: ViewMutationRecord) => boolean
|
|
109
|
+
|
|
102
110
|
|
|
103
111
|
/// Called when the mark view is removed from the editor or the whole
|
|
104
112
|
/// editor is destroyed.
|
|
@@ -387,19 +395,20 @@ export class ViewDesc {
|
|
|
387
395
|
// custom things with the selection. Note that this falls apart when
|
|
388
396
|
// a selection starts in such a node and ends in another, in which
|
|
389
397
|
// case we just use whatever domFromPos produces as a best effort.
|
|
390
|
-
setSelection(anchor: number, head: number,
|
|
398
|
+
setSelection(anchor: number, head: number, view: EditorView, force = false): void {
|
|
391
399
|
// If the selection falls entirely in a child, give it to that child
|
|
392
400
|
let from = Math.min(anchor, head), to = Math.max(anchor, head)
|
|
393
401
|
for (let i = 0, offset = 0; i < this.children.length; i++) {
|
|
394
402
|
let child = this.children[i], end = offset + child.size
|
|
395
403
|
if (from > offset && to < end)
|
|
396
|
-
return child.setSelection(anchor - offset - child.border, head - offset - child.border,
|
|
404
|
+
return child.setSelection(anchor - offset - child.border, head - offset - child.border, view, force)
|
|
397
405
|
offset = end
|
|
398
406
|
}
|
|
399
407
|
|
|
400
408
|
let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1)
|
|
401
409
|
let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1)
|
|
402
|
-
let domSel = (root as Document).getSelection()!
|
|
410
|
+
let domSel = (view.root as Document).getSelection()!
|
|
411
|
+
let selRange = view.domSelectionRange()
|
|
403
412
|
|
|
404
413
|
let brKludge = false
|
|
405
414
|
// On Firefox, using Selection.collapse to put the cursor after a
|
|
@@ -429,14 +438,14 @@ export class ViewDesc {
|
|
|
429
438
|
}
|
|
430
439
|
// Firefox can act strangely when the selection is in front of an
|
|
431
440
|
// uneditable node. See #1163 and https://bugzilla.mozilla.org/show_bug.cgi?id=1709536
|
|
432
|
-
if (browser.gecko &&
|
|
433
|
-
let after =
|
|
441
|
+
if (browser.gecko && selRange.focusNode && selRange.focusNode != headDOM.node && selRange.focusNode.nodeType == 1) {
|
|
442
|
+
let after = selRange.focusNode.childNodes[selRange.focusOffset]
|
|
434
443
|
if (after && (after as HTMLElement).contentEditable == "false") force = true
|
|
435
444
|
}
|
|
436
445
|
|
|
437
446
|
if (!(force || brKludge && browser.safari) &&
|
|
438
|
-
isEquivalentPosition(anchorDOM.node, anchorDOM.offset,
|
|
439
|
-
isEquivalentPosition(headDOM.node, headDOM.offset,
|
|
447
|
+
isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode!, selRange.anchorOffset) &&
|
|
448
|
+
isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode!, selRange.focusOffset))
|
|
440
449
|
return
|
|
441
450
|
|
|
442
451
|
// Selection.extend can be used to create an 'inverted' selection
|
|
@@ -468,8 +477,8 @@ export class ViewDesc {
|
|
|
468
477
|
}
|
|
469
478
|
}
|
|
470
479
|
|
|
471
|
-
ignoreMutation(mutation:
|
|
472
|
-
return !this.contentDOM &&
|
|
480
|
+
ignoreMutation(mutation: ViewMutationRecord): boolean {
|
|
481
|
+
return !this.contentDOM && mutation.type != "selection"
|
|
473
482
|
}
|
|
474
483
|
|
|
475
484
|
get contentLost() {
|
|
@@ -548,8 +557,8 @@ class WidgetViewDesc extends ViewDesc {
|
|
|
548
557
|
return stop ? stop(event) : false
|
|
549
558
|
}
|
|
550
559
|
|
|
551
|
-
ignoreMutation(mutation:
|
|
552
|
-
return
|
|
560
|
+
ignoreMutation(mutation: ViewMutationRecord) {
|
|
561
|
+
return mutation.type != "selection" || this.widget.spec.ignoreSelection
|
|
553
562
|
}
|
|
554
563
|
|
|
555
564
|
destroy() {
|
|
@@ -578,7 +587,7 @@ class CompositionViewDesc extends ViewDesc {
|
|
|
578
587
|
return {node: this.textDOM, offset: pos}
|
|
579
588
|
}
|
|
580
589
|
|
|
581
|
-
ignoreMutation(mut:
|
|
590
|
+
ignoreMutation(mut: ViewMutationRecord) {
|
|
582
591
|
return mut.type === 'characterData' && mut.target.nodeValue == mut.oldValue
|
|
583
592
|
}
|
|
584
593
|
}
|
|
@@ -629,6 +638,10 @@ class MarkViewDesc extends ViewDesc {
|
|
|
629
638
|
return copy
|
|
630
639
|
}
|
|
631
640
|
|
|
641
|
+
ignoreMutation(mutation: ViewMutationRecord) {
|
|
642
|
+
return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation)
|
|
643
|
+
}
|
|
644
|
+
|
|
632
645
|
destroy() {
|
|
633
646
|
if (this.spec.destroy) this.spec.destroy()
|
|
634
647
|
super.destroy()
|
|
@@ -924,8 +937,8 @@ class TextViewDesc extends NodeViewDesc {
|
|
|
924
937
|
return super.localPosFromDOM(dom, offset, bias)
|
|
925
938
|
}
|
|
926
939
|
|
|
927
|
-
ignoreMutation(mutation:
|
|
928
|
-
return mutation.type != "characterData" &&
|
|
940
|
+
ignoreMutation(mutation: ViewMutationRecord) {
|
|
941
|
+
return mutation.type != "characterData" && mutation.type != "selection"
|
|
929
942
|
}
|
|
930
943
|
|
|
931
944
|
slice(from: number, to: number, view: EditorView) {
|
|
@@ -987,9 +1000,9 @@ class CustomNodeViewDesc extends NodeViewDesc {
|
|
|
987
1000
|
this.spec.deselectNode ? this.spec.deselectNode() : super.deselectNode()
|
|
988
1001
|
}
|
|
989
1002
|
|
|
990
|
-
setSelection(anchor: number, head: number,
|
|
991
|
-
this.spec.setSelection ? this.spec.setSelection(anchor, head, root)
|
|
992
|
-
: super.setSelection(anchor, head,
|
|
1003
|
+
setSelection(anchor: number, head: number, view: EditorView, force: boolean) {
|
|
1004
|
+
this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root)
|
|
1005
|
+
: super.setSelection(anchor, head, view, force)
|
|
993
1006
|
}
|
|
994
1007
|
|
|
995
1008
|
destroy() {
|
|
@@ -1001,7 +1014,7 @@ class CustomNodeViewDesc extends NodeViewDesc {
|
|
|
1001
1014
|
return this.spec.stopEvent ? this.spec.stopEvent(event) : false
|
|
1002
1015
|
}
|
|
1003
1016
|
|
|
1004
|
-
ignoreMutation(mutation:
|
|
1017
|
+
ignoreMutation(mutation: ViewMutationRecord) {
|
|
1005
1018
|
return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation)
|
|
1006
1019
|
}
|
|
1007
1020
|
}
|