prosemirror-view 1.24.0-beta.3 → 1.25.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 +26 -0
- package/dist/index.cjs +23 -18
- package/dist/index.d.ts +25 -9
- package/dist/index.js +21 -18
- package/package.json +6 -3
- package/src/capturekeys.ts +4 -4
- package/src/clipboard.ts +9 -8
- package/src/domchange.ts +5 -4
- package/src/index.ts +28 -14
- package/src/viewdesc.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## 1.25.0 (2022-06-01)
|
|
2
|
+
|
|
3
|
+
### New features
|
|
4
|
+
|
|
5
|
+
Mark views can now be specified with a separate editor prop, `markViews`, to make it easier to get the types right.
|
|
6
|
+
|
|
7
|
+
## 1.24.1 (2022-05-30)
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
Fix a regression that caused the key handler to crash.
|
|
12
|
+
|
|
13
|
+
## 1.24.0 (2022-05-30)
|
|
14
|
+
|
|
15
|
+
### Bug fixes
|
|
16
|
+
|
|
17
|
+
Fix an issue where bogus table wrappers could be added when copy-pasting table content nodes within an editor.
|
|
18
|
+
|
|
19
|
+
Fix a crash that could occur when two textblocks were joined in the editor DOM.
|
|
20
|
+
|
|
21
|
+
Treat ctrl-b/f/p/n on macOS similar to arrow key presses, anticipating cursor motion.
|
|
22
|
+
|
|
23
|
+
### New features
|
|
24
|
+
|
|
25
|
+
Include TypeScript type declarations.
|
|
26
|
+
|
|
1
27
|
## 1.23.13 (2022-04-26)
|
|
2
28
|
|
|
3
29
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -2866,13 +2866,13 @@ function captureKeyDown(view, event) {
|
|
|
2866
2866
|
return stopNativeHorizontalDelete(view, 1) || skipIgnoredNodesRight(view);
|
|
2867
2867
|
} else if (code == 13 || code == 27) {
|
|
2868
2868
|
return true;
|
|
2869
|
-
} else if (code == 37) {
|
|
2869
|
+
} else if (code == 37 || mac && code == 66 && mods == "c") {
|
|
2870
2870
|
return selectHorizontally(view, -1, mods) || skipIgnoredNodesLeft(view);
|
|
2871
|
-
} else if (code == 39) {
|
|
2871
|
+
} else if (code == 39 || mac && code == 70 && mods == "c") {
|
|
2872
2872
|
return selectHorizontally(view, 1, mods) || skipIgnoredNodesRight(view);
|
|
2873
|
-
} else if (code == 38) {
|
|
2873
|
+
} else if (code == 38 || mac && code == 80 && mods == "c") {
|
|
2874
2874
|
return selectVertically(view, -1, mods) || skipIgnoredNodesLeft(view);
|
|
2875
|
-
} else if (code == 40) {
|
|
2875
|
+
} else if (code == 40 || mac && code == 78 && mods == "c") {
|
|
2876
2876
|
return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesRight(view);
|
|
2877
2877
|
} else if (mods == (mac ? "m" : "c") && (code == 66 || code == 73 || code == 89 || code == 90)) {
|
|
2878
2878
|
return true;
|
|
@@ -2902,7 +2902,8 @@ function serializeForClipboard(view, slice) {
|
|
|
2902
2902
|
document: doc
|
|
2903
2903
|
}));
|
|
2904
2904
|
var firstChild = wrap.firstChild,
|
|
2905
|
-
needsWrap
|
|
2905
|
+
needsWrap,
|
|
2906
|
+
wrappers = 0;
|
|
2906
2907
|
|
|
2907
2908
|
while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
|
|
2908
2909
|
for (var i = needsWrap.length - 1; i >= 0; i--) {
|
|
@@ -2913,17 +2914,13 @@ function serializeForClipboard(view, slice) {
|
|
|
2913
2914
|
}
|
|
2914
2915
|
|
|
2915
2916
|
wrap.appendChild(wrapper);
|
|
2916
|
-
|
|
2917
|
-
if (needsWrap[i] != "tbody") {
|
|
2918
|
-
openStart++;
|
|
2919
|
-
openEnd++;
|
|
2920
|
-
}
|
|
2917
|
+
wrappers++;
|
|
2921
2918
|
}
|
|
2922
2919
|
|
|
2923
2920
|
firstChild = wrap.firstChild;
|
|
2924
2921
|
}
|
|
2925
2922
|
|
|
2926
|
-
if (firstChild && firstChild.nodeType == 1) firstChild.setAttribute("data-pm-slice", "".concat(openStart, " ").concat(openEnd, " ").concat(JSON.stringify(context)));
|
|
2923
|
+
if (firstChild && firstChild.nodeType == 1) firstChild.setAttribute("data-pm-slice", "".concat(openStart, " ").concat(openEnd).concat(wrappers ? " -".concat(wrappers) : "", " ").concat(JSON.stringify(context)));
|
|
2927
2924
|
var text = view.someProp("clipboardTextSerializer", function (f) {
|
|
2928
2925
|
return f(slice);
|
|
2929
2926
|
}) || slice.content.textBetween(0, slice.content.size, "\n\n");
|
|
@@ -2969,7 +2966,10 @@ function parseFromClipboard(view, text, html, plainText, $context) {
|
|
|
2969
2966
|
}
|
|
2970
2967
|
|
|
2971
2968
|
var contextNode = dom && dom.querySelector("[data-pm-slice]");
|
|
2972
|
-
var sliceData = contextNode && /^(\d+) (\d+) (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "");
|
|
2969
|
+
var sliceData = contextNode && /^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "");
|
|
2970
|
+
if (sliceData && sliceData[3]) for (var i = +sliceData[3]; i > 0 && dom.firstChild; i--) {
|
|
2971
|
+
dom = dom.firstChild;
|
|
2972
|
+
}
|
|
2973
2973
|
|
|
2974
2974
|
if (!slice) {
|
|
2975
2975
|
var parser = view.someProp("clipboardParser") || view.someProp("domParser") || prosemirrorModel.DOMParser.fromSchema(view.state.schema);
|
|
@@ -2986,7 +2986,7 @@ function parseFromClipboard(view, text, html, plainText, $context) {
|
|
|
2986
2986
|
}
|
|
2987
2987
|
|
|
2988
2988
|
if (sliceData) {
|
|
2989
|
-
slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[
|
|
2989
|
+
slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[4]);
|
|
2990
2990
|
} else {
|
|
2991
2991
|
slice = prosemirrorModel.Slice.maxOpen(normalizeSiblings(slice.content, $context), true);
|
|
2992
2992
|
|
|
@@ -5111,7 +5111,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5111
5111
|
|
|
5112
5112
|
var $from = parse.doc.resolveNoCache(change.start - parse.from);
|
|
5113
5113
|
var $to = parse.doc.resolveNoCache(change.endB - parse.from);
|
|
5114
|
-
var
|
|
5114
|
+
var $fromA = doc.resolve(change.start);
|
|
5115
|
+
var inlineChange = $from.sameParent($to) && $from.parent.inlineContent && $fromA.end() >= change.endA;
|
|
5115
5116
|
var nextSel;
|
|
5116
5117
|
|
|
5117
5118
|
if ((ios && view.input.lastIOSEnter > Date.now() - 225 && (!inlineChange || addedNodes.some(function (n) {
|
|
@@ -5144,7 +5145,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5144
5145
|
|
|
5145
5146
|
var chFrom = change.start,
|
|
5146
5147
|
chTo = change.endA;
|
|
5147
|
-
var tr, storedMarks, markChange
|
|
5148
|
+
var tr, storedMarks, markChange;
|
|
5148
5149
|
|
|
5149
5150
|
if (inlineChange) {
|
|
5150
5151
|
if ($from.pos == $to.pos) {
|
|
@@ -5157,7 +5158,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5157
5158
|
|
|
5158
5159
|
tr = view.state.tr["delete"](chFrom, chTo);
|
|
5159
5160
|
storedMarks = doc.resolve(change.start).marksAcross(doc.resolve(change.endA));
|
|
5160
|
-
} else if (change.endA == change.endB && (
|
|
5161
|
+
} 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())))) {
|
|
5161
5162
|
tr = view.state.tr;
|
|
5162
5163
|
if (markChange.type == "add") tr.addMark(chFrom, chTo, markChange.mark);else tr.removeMark(chFrom, chTo, markChange.mark);
|
|
5163
5164
|
} else if ($from.parent.child($from.index()).isText && $from.index() == $to.index() - ($to.textOffset ? 0 : 1)) {
|
|
@@ -5697,11 +5698,15 @@ function selectionContextChanged(sel1, sel2) {
|
|
|
5697
5698
|
|
|
5698
5699
|
function buildNodeViews(view) {
|
|
5699
5700
|
var result = Object.create(null);
|
|
5700
|
-
|
|
5701
|
+
|
|
5702
|
+
function add(obj) {
|
|
5701
5703
|
for (var _prop3 in obj) {
|
|
5702
5704
|
if (!Object.prototype.hasOwnProperty.call(result, _prop3)) result[_prop3] = obj[_prop3];
|
|
5703
5705
|
}
|
|
5704
|
-
}
|
|
5706
|
+
}
|
|
5707
|
+
|
|
5708
|
+
view.someProp("nodeViews", add);
|
|
5709
|
+
view.someProp("markViews", add);
|
|
5705
5710
|
return result;
|
|
5706
5711
|
}
|
|
5707
5712
|
|
package/dist/index.d.ts
CHANGED
|
@@ -536,7 +536,11 @@ declare class EditorView {
|
|
|
536
536
|
*/
|
|
537
537
|
dispatch(tr: Transaction): void;
|
|
538
538
|
}
|
|
539
|
-
declare type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number
|
|
539
|
+
declare type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
|
|
540
|
+
declare type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
|
|
541
|
+
dom: HTMLElement;
|
|
542
|
+
contentDOM?: HTMLElement;
|
|
543
|
+
};
|
|
540
544
|
/**
|
|
541
545
|
Helper type that maps event names to event object types, but
|
|
542
546
|
includes events that TypeScript's HTMLElementEventMap doesn't know
|
|
@@ -676,14 +680,12 @@ interface EditorProps {
|
|
|
676
680
|
*/
|
|
677
681
|
transformPasted?: (slice: Slice) => Slice;
|
|
678
682
|
/**
|
|
679
|
-
Allows you to pass custom rendering and behavior logic for
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
transactions to update it. For marks, the third argument is a
|
|
686
|
-
boolean that indicates whether the mark's content is inline.
|
|
683
|
+
Allows you to pass custom rendering and behavior logic for
|
|
684
|
+
nodes. Should map node names to constructor functions that
|
|
685
|
+
produce a [`NodeView`](https://prosemirror.net/docs/ref/#view.NodeView) object implementing the
|
|
686
|
+
node's display behavior. The third argument `getPos` is a
|
|
687
|
+
function that can be called to get the node's current position,
|
|
688
|
+
which can be useful when creating transactions to update it.
|
|
687
689
|
|
|
688
690
|
`decorations` is an array of node or inline decorations that are
|
|
689
691
|
active around the node. They are automatically drawn in the
|
|
@@ -697,11 +699,25 @@ interface EditorProps {
|
|
|
697
699
|
on the content. But if you, for example, want to create a nested
|
|
698
700
|
editor with the content, it may make sense to provide it with the
|
|
699
701
|
inner decorations.
|
|
702
|
+
|
|
703
|
+
(For backwards compatibility reasons, [mark
|
|
704
|
+
views](https://prosemirror.net/docs/ref/#view.ViewProps.markViews) can also be included in this
|
|
705
|
+
object.)
|
|
700
706
|
*/
|
|
701
707
|
nodeViews?: {
|
|
702
708
|
[node: string]: NodeViewConstructor;
|
|
703
709
|
};
|
|
704
710
|
/**
|
|
711
|
+
Pass custom mark rendering functions. Note that these cannot
|
|
712
|
+
provide the kind of dynamic behavior that [node
|
|
713
|
+
views](https://prosemirror.net/docs/ref/#view.NodeView) can—they just provide custom rendering
|
|
714
|
+
logic. The third argument indicates whether the mark's content
|
|
715
|
+
is inline.
|
|
716
|
+
*/
|
|
717
|
+
markViews?: {
|
|
718
|
+
[mark: string]: MarkViewConstructor;
|
|
719
|
+
};
|
|
720
|
+
/**
|
|
705
721
|
The DOM serializer to use when putting content onto the
|
|
706
722
|
clipboard. If not given, the result of
|
|
707
723
|
[`DOMSerializer.fromSchema`](https://prosemirror.net/docs/ref/#model.DOMSerializer^fromSchema)
|
package/dist/index.js
CHANGED
|
@@ -2509,16 +2509,16 @@ function captureKeyDown(view, event) {
|
|
|
2509
2509
|
else if (code == 13 || code == 27) { // Enter, Esc
|
|
2510
2510
|
return true;
|
|
2511
2511
|
}
|
|
2512
|
-
else if (code == 37) { // Left arrow
|
|
2512
|
+
else if (code == 37 || (mac && code == 66 && mods == "c")) { // Left arrow, Ctrl-b on Mac
|
|
2513
2513
|
return selectHorizontally(view, -1, mods) || skipIgnoredNodesLeft(view);
|
|
2514
2514
|
}
|
|
2515
|
-
else if (code == 39) { // Right arrow
|
|
2515
|
+
else if (code == 39 || (mac && code == 70 && mods == "c")) { // Right arrow, Ctrl-f on Mac
|
|
2516
2516
|
return selectHorizontally(view, 1, mods) || skipIgnoredNodesRight(view);
|
|
2517
2517
|
}
|
|
2518
|
-
else if (code == 38) { // Up arrow
|
|
2518
|
+
else if (code == 38 || (mac && code == 80 && mods == "c")) { // Up arrow, Ctrl-p on Mac
|
|
2519
2519
|
return selectVertically(view, -1, mods) || skipIgnoredNodesLeft(view);
|
|
2520
2520
|
}
|
|
2521
|
-
else if (code == 40) { // Down arrow
|
|
2521
|
+
else if (code == 40 || (mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
|
|
2522
2522
|
return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesRight(view);
|
|
2523
2523
|
}
|
|
2524
2524
|
else if (mods == (mac ? "m" : "c") &&
|
|
@@ -2540,22 +2540,19 @@ function serializeForClipboard(view, slice) {
|
|
|
2540
2540
|
let serializer = view.someProp("clipboardSerializer") || DOMSerializer.fromSchema(view.state.schema);
|
|
2541
2541
|
let doc = detachedDoc(), wrap = doc.createElement("div");
|
|
2542
2542
|
wrap.appendChild(serializer.serializeFragment(content, { document: doc }));
|
|
2543
|
-
let firstChild = wrap.firstChild, needsWrap;
|
|
2543
|
+
let firstChild = wrap.firstChild, needsWrap, wrappers = 0;
|
|
2544
2544
|
while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
|
|
2545
2545
|
for (let i = needsWrap.length - 1; i >= 0; i--) {
|
|
2546
2546
|
let wrapper = doc.createElement(needsWrap[i]);
|
|
2547
2547
|
while (wrap.firstChild)
|
|
2548
2548
|
wrapper.appendChild(wrap.firstChild);
|
|
2549
2549
|
wrap.appendChild(wrapper);
|
|
2550
|
-
|
|
2551
|
-
openStart++;
|
|
2552
|
-
openEnd++;
|
|
2553
|
-
}
|
|
2550
|
+
wrappers++;
|
|
2554
2551
|
}
|
|
2555
2552
|
firstChild = wrap.firstChild;
|
|
2556
2553
|
}
|
|
2557
2554
|
if (firstChild && firstChild.nodeType == 1)
|
|
2558
|
-
firstChild.setAttribute("data-pm-slice", `${openStart} ${openEnd} ${JSON.stringify(context)}`);
|
|
2555
|
+
firstChild.setAttribute("data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${JSON.stringify(context)}`);
|
|
2559
2556
|
let text = view.someProp("clipboardTextSerializer", f => f(slice)) ||
|
|
2560
2557
|
slice.content.textBetween(0, slice.content.size, "\n\n");
|
|
2561
2558
|
return { dom: wrap, text };
|
|
@@ -2593,7 +2590,10 @@ function parseFromClipboard(view, text, html, plainText, $context) {
|
|
|
2593
2590
|
restoreReplacedSpaces(dom);
|
|
2594
2591
|
}
|
|
2595
2592
|
let contextNode = dom && dom.querySelector("[data-pm-slice]");
|
|
2596
|
-
let sliceData = contextNode && /^(\d+) (\d+) (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "");
|
|
2593
|
+
let sliceData = contextNode && /^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "");
|
|
2594
|
+
if (sliceData && sliceData[3])
|
|
2595
|
+
for (let i = +sliceData[3]; i > 0 && dom.firstChild; i--)
|
|
2596
|
+
dom = dom.firstChild;
|
|
2597
2597
|
if (!slice) {
|
|
2598
2598
|
let parser = view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser.fromSchema(view.state.schema);
|
|
2599
2599
|
slice = parser.parseSlice(dom, {
|
|
@@ -2608,7 +2608,7 @@ function parseFromClipboard(view, text, html, plainText, $context) {
|
|
|
2608
2608
|
});
|
|
2609
2609
|
}
|
|
2610
2610
|
if (sliceData) {
|
|
2611
|
-
slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[
|
|
2611
|
+
slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[4]);
|
|
2612
2612
|
}
|
|
2613
2613
|
else { // HTML wasn't created by ProseMirror. Make sure top-level siblings are coherent
|
|
2614
2614
|
slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true);
|
|
@@ -4566,7 +4566,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4566
4566
|
}
|
|
4567
4567
|
let $from = parse.doc.resolveNoCache(change.start - parse.from);
|
|
4568
4568
|
let $to = parse.doc.resolveNoCache(change.endB - parse.from);
|
|
4569
|
-
let
|
|
4569
|
+
let $fromA = doc.resolve(change.start);
|
|
4570
|
+
let inlineChange = $from.sameParent($to) && $from.parent.inlineContent && $fromA.end() >= change.endA;
|
|
4570
4571
|
let nextSel;
|
|
4571
4572
|
// If this looks like the effect of pressing Enter (or was recorded
|
|
4572
4573
|
// as being an iOS enter press), just dispatch an Enter key instead.
|
|
@@ -4609,7 +4610,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4609
4610
|
}, 20);
|
|
4610
4611
|
}
|
|
4611
4612
|
let chFrom = change.start, chTo = change.endA;
|
|
4612
|
-
let tr, storedMarks, markChange
|
|
4613
|
+
let tr, storedMarks, markChange;
|
|
4613
4614
|
if (inlineChange) {
|
|
4614
4615
|
if ($from.pos == $to.pos) { // Deletion
|
|
4615
4616
|
// IE11 sometimes weirdly moves the DOM selection around after
|
|
@@ -4622,8 +4623,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4622
4623
|
storedMarks = doc.resolve(change.start).marksAcross(doc.resolve(change.endA));
|
|
4623
4624
|
}
|
|
4624
4625
|
else if ( // Adding or removing a mark
|
|
4625
|
-
change.endA == change.endB &&
|
|
4626
|
-
(markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset), $
|
|
4626
|
+
change.endA == change.endB &&
|
|
4627
|
+
(markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset), $fromA.parent.content.cut($fromA.parentOffset, change.endA - $fromA.start())))) {
|
|
4627
4628
|
tr = view.state.tr;
|
|
4628
4629
|
if (markChange.type == "add")
|
|
4629
4630
|
tr.addMark(chFrom, chTo, markChange.mark);
|
|
@@ -5225,11 +5226,13 @@ function selectionContextChanged(sel1, sel2) {
|
|
|
5225
5226
|
}
|
|
5226
5227
|
function buildNodeViews(view) {
|
|
5227
5228
|
let result = Object.create(null);
|
|
5228
|
-
|
|
5229
|
+
function add(obj) {
|
|
5229
5230
|
for (let prop in obj)
|
|
5230
5231
|
if (!Object.prototype.hasOwnProperty.call(result, prop))
|
|
5231
5232
|
result[prop] = obj[prop];
|
|
5232
|
-
}
|
|
5233
|
+
}
|
|
5234
|
+
view.someProp("nodeViews", add);
|
|
5235
|
+
view.someProp("markViews", add);
|
|
5233
5236
|
return result;
|
|
5234
5237
|
}
|
|
5235
5238
|
function changedNodeViews(a, b) {
|
package/package.json
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prosemirror-view",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.0",
|
|
4
4
|
"description": "ProseMirror's view component",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
|
-
"
|
|
11
|
-
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
},
|
|
14
|
+
"./style/prosemirror.css": "./style/prosemirror.css"
|
|
12
15
|
},
|
|
13
16
|
"sideEffects": false,
|
|
14
17
|
"style": "style/prosemirror.css",
|
package/src/capturekeys.ts
CHANGED
|
@@ -256,13 +256,13 @@ export function captureKeyDown(view: EditorView, event: KeyboardEvent) {
|
|
|
256
256
|
return stopNativeHorizontalDelete(view, 1) || skipIgnoredNodesRight(view)
|
|
257
257
|
} else if (code == 13 || code == 27) { // Enter, Esc
|
|
258
258
|
return true
|
|
259
|
-
} else if (code == 37) { // Left arrow
|
|
259
|
+
} else if (code == 37 || (browser.mac && code == 66 && mods == "c")) { // Left arrow, Ctrl-b on Mac
|
|
260
260
|
return selectHorizontally(view, -1, mods) || skipIgnoredNodesLeft(view)
|
|
261
|
-
} else if (code == 39) { // Right arrow
|
|
261
|
+
} else if (code == 39 || (browser.mac && code == 70 && mods == "c")) { // Right arrow, Ctrl-f on Mac
|
|
262
262
|
return selectHorizontally(view, 1, mods) || skipIgnoredNodesRight(view)
|
|
263
|
-
} else if (code == 38) { // Up arrow
|
|
263
|
+
} else if (code == 38 || (browser.mac && code == 80 && mods == "c")) { // Up arrow, Ctrl-p on Mac
|
|
264
264
|
return selectVertically(view, -1, mods) || skipIgnoredNodesLeft(view)
|
|
265
|
-
} else if (code == 40) { // Down arrow
|
|
265
|
+
} else if (code == 40 || (browser.mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
|
|
266
266
|
return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesRight(view)
|
|
267
267
|
} else if (mods == (browser.mac ? "m" : "c") &&
|
|
268
268
|
(code == 66 || code == 73 || code == 89 || code == 90)) { // Mod-[biyz]
|
package/src/clipboard.ts
CHANGED
|
@@ -16,22 +16,20 @@ export function serializeForClipboard(view: EditorView, slice: Slice) {
|
|
|
16
16
|
let doc = detachedDoc(), wrap = doc.createElement("div")
|
|
17
17
|
wrap.appendChild(serializer.serializeFragment(content, {document: doc}))
|
|
18
18
|
|
|
19
|
-
let firstChild = wrap.firstChild, needsWrap
|
|
19
|
+
let firstChild = wrap.firstChild, needsWrap, wrappers = 0
|
|
20
20
|
while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
|
|
21
21
|
for (let i = needsWrap.length - 1; i >= 0; i--) {
|
|
22
22
|
let wrapper = doc.createElement(needsWrap[i])
|
|
23
23
|
while (wrap.firstChild) wrapper.appendChild(wrap.firstChild)
|
|
24
24
|
wrap.appendChild(wrapper)
|
|
25
|
-
|
|
26
|
-
openStart++
|
|
27
|
-
openEnd++
|
|
28
|
-
}
|
|
25
|
+
wrappers++
|
|
29
26
|
}
|
|
30
27
|
firstChild = wrap.firstChild
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
if (firstChild && firstChild.nodeType == 1)
|
|
34
|
-
(firstChild as HTMLElement).setAttribute(
|
|
31
|
+
(firstChild as HTMLElement).setAttribute(
|
|
32
|
+
"data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${JSON.stringify(context)}`)
|
|
35
33
|
|
|
36
34
|
let text = view.someProp("clipboardTextSerializer", f => f(slice)) ||
|
|
37
35
|
slice.content.textBetween(0, slice.content.size, "\n\n")
|
|
@@ -67,7 +65,10 @@ export function parseFromClipboard(view: EditorView, text: string, html: string
|
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
let contextNode = dom && dom.querySelector("[data-pm-slice]")
|
|
70
|
-
let sliceData = contextNode && /^(\d+) (\d+) (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "")
|
|
68
|
+
let sliceData = contextNode && /^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "")
|
|
69
|
+
if (sliceData && sliceData[3]) for (let i = +sliceData[3]; i > 0 && dom!.firstChild; i--)
|
|
70
|
+
dom = dom!.firstChild as HTMLElement
|
|
71
|
+
|
|
71
72
|
if (!slice) {
|
|
72
73
|
let parser = view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser.fromSchema(view.state.schema)
|
|
73
74
|
slice = parser.parseSlice(dom!, {
|
|
@@ -81,7 +82,7 @@ export function parseFromClipboard(view: EditorView, text: string, html: string
|
|
|
81
82
|
})
|
|
82
83
|
}
|
|
83
84
|
if (sliceData) {
|
|
84
|
-
slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[
|
|
85
|
+
slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[4])
|
|
85
86
|
} else { // HTML wasn't created by ProseMirror. Make sure top-level siblings are coherent
|
|
86
87
|
slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true)
|
|
87
88
|
if (slice.openStart || slice.openEnd) {
|
package/src/domchange.ts
CHANGED
|
@@ -167,7 +167,8 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
167
167
|
|
|
168
168
|
let $from = parse.doc.resolveNoCache(change.start - parse.from)
|
|
169
169
|
let $to = parse.doc.resolveNoCache(change.endB - parse.from)
|
|
170
|
-
let
|
|
170
|
+
let $fromA = doc.resolve(change.start)
|
|
171
|
+
let inlineChange = $from.sameParent($to) && $from.parent.inlineContent && $fromA.end() >= change.endA
|
|
171
172
|
let nextSel
|
|
172
173
|
// If this looks like the effect of pressing Enter (or was recorded
|
|
173
174
|
// as being an iOS enter press), just dispatch an Enter key instead.
|
|
@@ -213,7 +214,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
213
214
|
|
|
214
215
|
let chFrom = change.start, chTo = change.endA
|
|
215
216
|
|
|
216
|
-
let tr, storedMarks, markChange
|
|
217
|
+
let tr, storedMarks, markChange
|
|
217
218
|
if (inlineChange) {
|
|
218
219
|
if ($from.pos == $to.pos) { // Deletion
|
|
219
220
|
// IE11 sometimes weirdly moves the DOM selection around after
|
|
@@ -225,9 +226,9 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
225
226
|
tr = view.state.tr.delete(chFrom, chTo)
|
|
226
227
|
storedMarks = doc.resolve(change.start).marksAcross(doc.resolve(change.endA))
|
|
227
228
|
} else if ( // Adding or removing a mark
|
|
228
|
-
change.endA == change.endB &&
|
|
229
|
+
change.endA == change.endB &&
|
|
229
230
|
(markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset),
|
|
230
|
-
$
|
|
231
|
+
$fromA.parent.content.cut($fromA.parentOffset, change.endA - $fromA.start())))
|
|
231
232
|
) {
|
|
232
233
|
tr = view.state.tr
|
|
233
234
|
if (markChange.type == "add") tr.addMark(chFrom, chTo, markChange.mark)
|
package/src/index.ts
CHANGED
|
@@ -42,7 +42,7 @@ export class EditorView {
|
|
|
42
42
|
/// @internal
|
|
43
43
|
cursorWrapper: {dom: DOMNode, deco: Decoration} | null = null
|
|
44
44
|
/// @internal
|
|
45
|
-
nodeViews:
|
|
45
|
+
nodeViews: NodeViewSet
|
|
46
46
|
/// @internal
|
|
47
47
|
lastSelectedViewDesc: ViewDesc | undefined = undefined
|
|
48
48
|
/// @internal
|
|
@@ -472,15 +472,17 @@ function selectionContextChanged(sel1: Selection, sel2: Selection) {
|
|
|
472
472
|
}
|
|
473
473
|
|
|
474
474
|
function buildNodeViews(view: EditorView) {
|
|
475
|
-
let result:
|
|
476
|
-
|
|
475
|
+
let result: NodeViewSet = Object.create(null)
|
|
476
|
+
function add(obj: NodeViewSet) {
|
|
477
477
|
for (let prop in obj) if (!Object.prototype.hasOwnProperty.call(result, prop))
|
|
478
478
|
result[prop] = obj[prop]
|
|
479
|
-
}
|
|
479
|
+
}
|
|
480
|
+
view.someProp("nodeViews", add)
|
|
481
|
+
view.someProp("markViews", add)
|
|
480
482
|
return result
|
|
481
483
|
}
|
|
482
484
|
|
|
483
|
-
function changedNodeViews(a:
|
|
485
|
+
function changedNodeViews(a: NodeViewSet, b: NodeViewSet) {
|
|
484
486
|
let nA = 0, nB = 0
|
|
485
487
|
for (let prop in a) {
|
|
486
488
|
if (a[prop] != b[prop]) return true
|
|
@@ -495,8 +497,11 @@ function checkStateComponent(plugin: Plugin) {
|
|
|
495
497
|
throw new RangeError("Plugins passed directly to the view must not have a state component")
|
|
496
498
|
}
|
|
497
499
|
|
|
498
|
-
type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number
|
|
500
|
+
type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number,
|
|
499
501
|
decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView
|
|
502
|
+
type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {dom: HTMLElement, contentDOM?: HTMLElement}
|
|
503
|
+
|
|
504
|
+
type NodeViewSet = {[name: string]: NodeViewConstructor | MarkViewConstructor}
|
|
500
505
|
|
|
501
506
|
/// Helper type that maps event names to event object types, but
|
|
502
507
|
/// includes events that TypeScript's HTMLElementEventMap doesn't know
|
|
@@ -613,14 +618,12 @@ export interface EditorProps {
|
|
|
613
618
|
/// the document.
|
|
614
619
|
transformPasted?: (slice: Slice) => Slice
|
|
615
620
|
|
|
616
|
-
/// Allows you to pass custom rendering and behavior logic for
|
|
617
|
-
///
|
|
618
|
-
///
|
|
619
|
-
///
|
|
620
|
-
///
|
|
621
|
-
///
|
|
622
|
-
/// transactions to update it. For marks, the third argument is a
|
|
623
|
-
/// boolean that indicates whether the mark's content is inline.
|
|
621
|
+
/// Allows you to pass custom rendering and behavior logic for
|
|
622
|
+
/// nodes. Should map node names to constructor functions that
|
|
623
|
+
/// produce a [`NodeView`](#view.NodeView) object implementing the
|
|
624
|
+
/// node's display behavior. The third argument `getPos` is a
|
|
625
|
+
/// function that can be called to get the node's current position,
|
|
626
|
+
/// which can be useful when creating transactions to update it.
|
|
624
627
|
///
|
|
625
628
|
/// `decorations` is an array of node or inline decorations that are
|
|
626
629
|
/// active around the node. They are automatically drawn in the
|
|
@@ -634,8 +637,19 @@ export interface EditorProps {
|
|
|
634
637
|
/// on the content. But if you, for example, want to create a nested
|
|
635
638
|
/// editor with the content, it may make sense to provide it with the
|
|
636
639
|
/// inner decorations.
|
|
640
|
+
///
|
|
641
|
+
/// (For backwards compatibility reasons, [mark
|
|
642
|
+
/// views](#view.ViewProps.markViews) can also be included in this
|
|
643
|
+
/// object.)
|
|
637
644
|
nodeViews?: {[node: string]: NodeViewConstructor}
|
|
638
645
|
|
|
646
|
+
/// Pass custom mark rendering functions. Note that these cannot
|
|
647
|
+
/// provide the kind of dynamic behavior that [node
|
|
648
|
+
/// views](#view.NodeView) can—they just provide custom rendering
|
|
649
|
+
/// logic. The third argument indicates whether the mark's content
|
|
650
|
+
/// is inline.
|
|
651
|
+
markViews?: {[mark: string]: MarkViewConstructor}
|
|
652
|
+
|
|
639
653
|
/// The DOM serializer to use when putting content onto the
|
|
640
654
|
/// clipboard. If not given, the result of
|
|
641
655
|
/// [`DOMSerializer.fromSchema`](#model.DOMSerializer^fromSchema)
|
package/src/viewdesc.ts
CHANGED
|
@@ -565,9 +565,9 @@ class MarkViewDesc extends ViewDesc {
|
|
|
565
565
|
|
|
566
566
|
static create(parent: ViewDesc, mark: Mark, inline: boolean, view: EditorView) {
|
|
567
567
|
let custom = view.nodeViews[mark.type.name]
|
|
568
|
-
let spec:
|
|
568
|
+
let spec: {dom: HTMLElement, contentDOM?: HTMLElement} = custom && (custom as any)(mark, view, inline)
|
|
569
569
|
if (!spec || !spec.dom)
|
|
570
|
-
spec = DOMSerializer.renderSpec(document, mark.type.spec.toDOM!(mark, inline))
|
|
570
|
+
spec = DOMSerializer.renderSpec(document, mark.type.spec.toDOM!(mark, inline)) as any
|
|
571
571
|
return new MarkViewDesc(parent, mark, spec.dom, spec.contentDOM || spec.dom as HTMLElement)
|
|
572
572
|
}
|
|
573
573
|
|
|
@@ -631,7 +631,7 @@ export class NodeViewDesc extends ViewDesc {
|
|
|
631
631
|
static create(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[],
|
|
632
632
|
innerDeco: DecorationSource, view: EditorView, pos: number) {
|
|
633
633
|
let custom = view.nodeViews[node.type.name], descObj: ViewDesc
|
|
634
|
-
let spec = custom && custom(node, view, () => {
|
|
634
|
+
let spec: NodeView | undefined = custom && (custom as any)(node, view, () => {
|
|
635
635
|
// (This is a function that allows the custom view to find its
|
|
636
636
|
// own position)
|
|
637
637
|
if (!descObj) return pos
|