@atlaskit/editor-plugin-show-diff 0.1.1 → 0.1.3
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 +17 -0
- package/dist/cjs/pm-plugins/NodeViewSerializer.js +102 -0
- package/dist/cjs/pm-plugins/decorations.js +98 -14
- package/dist/cjs/pm-plugins/main.js +45 -19
- package/dist/es2019/pm-plugins/NodeViewSerializer.js +83 -0
- package/dist/es2019/pm-plugins/decorations.js +94 -14
- package/dist/es2019/pm-plugins/main.js +43 -17
- package/dist/esm/pm-plugins/NodeViewSerializer.js +95 -0
- package/dist/esm/pm-plugins/decorations.js +98 -14
- package/dist/esm/pm-plugins/main.js +45 -19
- package/dist/types/pm-plugins/NodeViewSerializer.d.ts +62 -0
- package/dist/types/pm-plugins/decorations.d.ts +5 -1
- package/dist/types-ts4.5/pm-plugins/NodeViewSerializer.d.ts +62 -0
- package/dist/types-ts4.5/pm-plugins/decorations.d.ts +5 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-show-diff
|
|
2
2
|
|
|
3
|
+
## 0.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`8700ce859da07`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8700ce859da07) -
|
|
8
|
+
[EDITOR-1249] Added inline node support for show diff
|
|
9
|
+
|
|
10
|
+
## 0.1.2
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- [`7fe4c9e51271d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/7fe4c9e51271d) -
|
|
15
|
+
Fix initial show diff after performance fix.
|
|
16
|
+
- [`b2d53a70dbaa5`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b2d53a70dbaa5) -
|
|
17
|
+
Improve show diff performance by storing decorations in state.
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
|
|
3
20
|
## 0.1.1
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.NodeViewSerializer = void 0;
|
|
8
|
+
exports.isEditorViewWithNodeViews = isEditorViewWithNodeViews;
|
|
9
|
+
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
10
|
+
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
11
|
+
var _model = require("@atlaskit/editor-prosemirror/model");
|
|
12
|
+
/**
|
|
13
|
+
* Utilities for working with ProseMirror node views and DOM serialization within the
|
|
14
|
+
* Show Diff editor plugin.
|
|
15
|
+
*
|
|
16
|
+
* This module centralizes:
|
|
17
|
+
* - Access to the editor's `nodeViews` registry (when available on `EditorView`)
|
|
18
|
+
* - Safe attempts to instantiate a node view for a given node, with a blocklist to
|
|
19
|
+
* avoid node types that are known to be problematic in this context (e.g. tables)
|
|
20
|
+
* - Schema-driven serialization of nodes and fragments to DOM via `DOMSerializer`
|
|
21
|
+
*
|
|
22
|
+
* The Show Diff decorations leverage this to either render nodes using their
|
|
23
|
+
* corresponding node view implementation, or fall back to DOM serialization.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Narrowed `EditorView` that exposes the internal `nodeViews` registry.
|
|
28
|
+
* Many editor instances provide this, but it's not part of the base type.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Type guard to detect whether an `EditorView` exposes a `nodeViews` map.
|
|
33
|
+
*/
|
|
34
|
+
function isEditorViewWithNodeViews(view) {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
return view.nodeViews !== undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Encapsulates DOM serialization and node view access/creation.
|
|
41
|
+
*
|
|
42
|
+
* Responsible for:
|
|
43
|
+
* - Creating a `DOMSerializer` from the provided schema
|
|
44
|
+
* - Reading `nodeViews` from an `EditorView` (if present) or using an explicit mapping
|
|
45
|
+
* - Preventing node view creation for blocklisted node types
|
|
46
|
+
*/
|
|
47
|
+
var NodeViewSerializer = exports.NodeViewSerializer = /*#__PURE__*/function () {
|
|
48
|
+
function NodeViewSerializer(params) {
|
|
49
|
+
var _ref, _params$nodeViews, _this$editorView, _params$blocklist;
|
|
50
|
+
(0, _classCallCheck2.default)(this, NodeViewSerializer);
|
|
51
|
+
this.serializer = _model.DOMSerializer.fromSchema(params.schema);
|
|
52
|
+
if (params.editorView && isEditorViewWithNodeViews(params.editorView)) {
|
|
53
|
+
this.editorView = params.editorView;
|
|
54
|
+
}
|
|
55
|
+
this.nodeViews = (_ref = (_params$nodeViews = params.nodeViews) !== null && _params$nodeViews !== void 0 ? _params$nodeViews : (_this$editorView = this.editorView) === null || _this$editorView === void 0 ? void 0 : _this$editorView.nodeViews) !== null && _ref !== void 0 ? _ref : {};
|
|
56
|
+
this.nodeViewBlocklist = new Set((_params$blocklist = params.blocklist) !== null && _params$blocklist !== void 0 ? _params$blocklist : ['tableRow', 'table']);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Attempts to create a node view for the given node.
|
|
61
|
+
*
|
|
62
|
+
* Returns `null` when there is no `EditorView`, no constructor for the node type,
|
|
63
|
+
* or the node type is blocklisted. Otherwise returns the constructed node view instance.
|
|
64
|
+
*/
|
|
65
|
+
return (0, _createClass2.default)(NodeViewSerializer, [{
|
|
66
|
+
key: "tryCreateNodeView",
|
|
67
|
+
value: function tryCreateNodeView(targetNode) {
|
|
68
|
+
if (!this.editorView) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
var constructor = this.nodeViews[targetNode.type.name];
|
|
72
|
+
if (!constructor) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
if (this.nodeViewBlocklist.has(targetNode.type.name)) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return constructor(targetNode, this.editorView, function () {
|
|
79
|
+
return 0;
|
|
80
|
+
}, [], {});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Serializes a node to a DOM `Node` using the schema's `DOMSerializer`.
|
|
85
|
+
*/
|
|
86
|
+
}, {
|
|
87
|
+
key: "serializeNode",
|
|
88
|
+
value: function serializeNode(node) {
|
|
89
|
+
return this.serializer.serializeNode(node);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Serializes a fragment to a `DocumentFragment` using the schema's `DOMSerializer`.
|
|
94
|
+
*/
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
96
|
+
}, {
|
|
97
|
+
key: "serializeFragment",
|
|
98
|
+
value: function serializeFragment(fragment) {
|
|
99
|
+
return this.serializer.serializeFragment(fragment);
|
|
100
|
+
}
|
|
101
|
+
}]);
|
|
102
|
+
}();
|
|
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.createInlineChangedDecoration = exports.createDeletedContentDecoration = void 0;
|
|
7
7
|
var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
|
|
8
|
-
var _model = require("@atlaskit/editor-prosemirror/model");
|
|
9
8
|
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
9
|
+
var _NodeViewSerializer = require("./NodeViewSerializer");
|
|
10
10
|
var style = (0, _lazyNodeView.convertToInlineCss)({
|
|
11
11
|
background: "var(--ds-background-accent-purple-subtlest, #F3F0FF)",
|
|
12
12
|
textDecoration: 'underline',
|
|
@@ -14,7 +14,6 @@ var style = (0, _lazyNodeView.convertToInlineCss)({
|
|
|
14
14
|
textDecorationThickness: "var(--ds-space-025, 2px)",
|
|
15
15
|
textDecorationColor: "var(--ds-border-accent-purple, #8270DB)"
|
|
16
16
|
});
|
|
17
|
-
|
|
18
17
|
/**
|
|
19
18
|
* Inline decoration used for insertions as the content already exists in the document
|
|
20
19
|
*
|
|
@@ -29,7 +28,18 @@ var createInlineChangedDecoration = exports.createInlineChangedDecoration = func
|
|
|
29
28
|
};
|
|
30
29
|
var deletedContentStyle = (0, _lazyNodeView.convertToInlineCss)({
|
|
31
30
|
color: "var(--ds-text-accent-gray, #44546F)",
|
|
32
|
-
textDecoration: 'line-through'
|
|
31
|
+
textDecoration: 'line-through',
|
|
32
|
+
position: 'relative',
|
|
33
|
+
opacity: 0.6
|
|
34
|
+
});
|
|
35
|
+
var deletedContentStyleUnbounded = (0, _lazyNodeView.convertToInlineCss)({
|
|
36
|
+
position: 'absolute',
|
|
37
|
+
top: '50%',
|
|
38
|
+
width: '100%',
|
|
39
|
+
display: 'inline-block',
|
|
40
|
+
borderTop: "1px solid ".concat("var(--ds-text-accent-gray, #44546F)"),
|
|
41
|
+
pointerEvents: 'none',
|
|
42
|
+
zIndex: 1
|
|
33
43
|
});
|
|
34
44
|
|
|
35
45
|
/**
|
|
@@ -43,7 +53,9 @@ var deletedContentStyle = (0, _lazyNodeView.convertToInlineCss)({
|
|
|
43
53
|
var createDeletedContentDecoration = exports.createDeletedContentDecoration = function createDeletedContentDecoration(_ref) {
|
|
44
54
|
var change = _ref.change,
|
|
45
55
|
doc = _ref.doc,
|
|
46
|
-
tr = _ref.tr
|
|
56
|
+
tr = _ref.tr,
|
|
57
|
+
editorView = _ref.editorView,
|
|
58
|
+
nodeViews = _ref.nodeViews;
|
|
47
59
|
var dom = document.createElement('span');
|
|
48
60
|
dom.setAttribute('style', deletedContentStyle);
|
|
49
61
|
|
|
@@ -53,22 +65,94 @@ var createDeletedContentDecoration = exports.createDeletedContentDecoration = fu
|
|
|
53
65
|
* or sliced End depth is and match only the content and not with the entire node.
|
|
54
66
|
*/
|
|
55
67
|
var slice = doc.slice(change.fromA, change.toA);
|
|
68
|
+
var nodeViewSerializer = new _NodeViewSerializer.NodeViewSerializer({
|
|
69
|
+
schema: tr.doc.type.schema,
|
|
70
|
+
editorView: editorView,
|
|
71
|
+
nodeViews: nodeViews
|
|
72
|
+
});
|
|
56
73
|
slice.content.forEach(function (node) {
|
|
57
|
-
|
|
74
|
+
// Create a wrapper for each node with strikethrough
|
|
75
|
+
var createWrapperWithStrikethrough = function createWrapperWithStrikethrough() {
|
|
76
|
+
var wrapper = document.createElement('span');
|
|
77
|
+
wrapper.style.position = 'relative';
|
|
78
|
+
wrapper.style.width = 'fit-content';
|
|
79
|
+
var strikethrough = document.createElement('span');
|
|
80
|
+
strikethrough.setAttribute('style', deletedContentStyleUnbounded);
|
|
81
|
+
wrapper.append(strikethrough);
|
|
82
|
+
return wrapper;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Helper function to handle multiple child nodes
|
|
86
|
+
var handleMultipleChildNodes = function handleMultipleChildNodes(node) {
|
|
87
|
+
if (node.content.childCount > 1 && node.type.inlineContent) {
|
|
88
|
+
node.content.forEach(function (childNode) {
|
|
89
|
+
var childNodeView = nodeViewSerializer.tryCreateNodeView(childNode);
|
|
90
|
+
if (childNodeView) {
|
|
91
|
+
var lineBreak = document.createElement('br');
|
|
92
|
+
targetNode = node;
|
|
93
|
+
dom.append(lineBreak);
|
|
94
|
+
var wrapper = createWrapperWithStrikethrough();
|
|
95
|
+
wrapper.append(childNodeView.dom);
|
|
96
|
+
dom.append(wrapper);
|
|
97
|
+
} else {
|
|
98
|
+
// Fallback to serializing the individual child node
|
|
99
|
+
var serializedChild = nodeViewSerializer.serializeNode(childNode);
|
|
100
|
+
dom.append(serializedChild);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return true; // Indicates we handled multiple children
|
|
104
|
+
}
|
|
105
|
+
return false; // Indicates single child, continue with normal logic
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Determine which node to use and how to serialize
|
|
58
109
|
var isFirst = slice.content.firstChild === node;
|
|
59
110
|
var isLast = slice.content.lastChild === node;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
111
|
+
var hasInlineContent = node.content.childCount > 0 && node.type.inlineContent === true;
|
|
112
|
+
var targetNode;
|
|
113
|
+
var fallbackSerialization;
|
|
114
|
+
if ((isFirst || isLast && slice.content.childCount > 2) && hasInlineContent) {
|
|
115
|
+
if (handleMultipleChildNodes(node)) {
|
|
116
|
+
return;
|
|
65
117
|
}
|
|
118
|
+
targetNode = node.content.content[0];
|
|
119
|
+
fallbackSerialization = function fallbackSerialization() {
|
|
120
|
+
return nodeViewSerializer.serializeFragment(node.content);
|
|
121
|
+
};
|
|
66
122
|
} else if (isLast && slice.content.childCount === 2) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
123
|
+
if (handleMultipleChildNodes(node)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
targetNode = node;
|
|
127
|
+
fallbackSerialization = function fallbackSerialization() {
|
|
128
|
+
if (node.type.name === 'text') {
|
|
129
|
+
return document.createTextNode(node.text || '');
|
|
130
|
+
}
|
|
131
|
+
if (node.type.name === 'paragraph') {
|
|
132
|
+
var lineBreak = document.createElement('br');
|
|
133
|
+
dom.append(lineBreak);
|
|
134
|
+
return nodeViewSerializer.serializeFragment(node.content);
|
|
135
|
+
}
|
|
136
|
+
return nodeViewSerializer.serializeFragment(node.content);
|
|
137
|
+
};
|
|
138
|
+
} else {
|
|
139
|
+
if (handleMultipleChildNodes(node)) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
targetNode = node.content.content[0] || node;
|
|
143
|
+
fallbackSerialization = function fallbackSerialization() {
|
|
144
|
+
return nodeViewSerializer.serializeNode(node);
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Try to create node view, fallback to serialization
|
|
149
|
+
var nodeView = nodeViewSerializer.tryCreateNodeView(targetNode);
|
|
150
|
+
if (nodeView) {
|
|
151
|
+
var wrapper = createWrapperWithStrikethrough();
|
|
152
|
+
wrapper.append(nodeView.dom);
|
|
153
|
+
dom.append(wrapper);
|
|
70
154
|
} else {
|
|
71
|
-
dom.append(
|
|
155
|
+
dom.append(fallbackSerialization());
|
|
72
156
|
}
|
|
73
157
|
});
|
|
74
158
|
dom.setAttribute('data-testid', 'show-diff-deleted-decoration');
|
|
@@ -21,7 +21,9 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
|
|
|
21
21
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } // eslint-disable-next-line @atlassian/tangerine/import/entry-points
|
|
22
22
|
var calculateDecorations = function calculateDecorations(_ref) {
|
|
23
23
|
var state = _ref.state,
|
|
24
|
-
pluginState = _ref.pluginState
|
|
24
|
+
pluginState = _ref.pluginState,
|
|
25
|
+
editorView = _ref.editorView,
|
|
26
|
+
nodeViews = _ref.nodeViews;
|
|
25
27
|
var originalDoc = pluginState.originalDoc,
|
|
26
28
|
steps = pluginState.steps;
|
|
27
29
|
if (!originalDoc || !pluginState.isDisplayingChanges) {
|
|
@@ -59,7 +61,9 @@ var calculateDecorations = function calculateDecorations(_ref) {
|
|
|
59
61
|
decorations.push((0, _decorations.createDeletedContentDecoration)({
|
|
60
62
|
change: change,
|
|
61
63
|
doc: originalDoc,
|
|
62
|
-
tr: tr
|
|
64
|
+
tr: tr,
|
|
65
|
+
editorView: editorView,
|
|
66
|
+
nodeViews: nodeViews
|
|
63
67
|
}));
|
|
64
68
|
}
|
|
65
69
|
});
|
|
@@ -70,60 +74,82 @@ var calculateDecorations = function calculateDecorations(_ref) {
|
|
|
70
74
|
};
|
|
71
75
|
var showDiffPluginKey = exports.showDiffPluginKey = new _state.PluginKey('showDiffPlugin');
|
|
72
76
|
var createPlugin = exports.createPlugin = function createPlugin(config) {
|
|
77
|
+
var editorView;
|
|
78
|
+
var setEditorView = function setEditorView(newEditorView) {
|
|
79
|
+
editorView = newEditorView;
|
|
80
|
+
};
|
|
73
81
|
return new _safePlugin.SafePlugin({
|
|
74
82
|
key: showDiffPluginKey,
|
|
75
83
|
state: {
|
|
76
84
|
init: function init(_, state) {
|
|
77
|
-
var _config$steps, _config$steps2;
|
|
85
|
+
var _config$steps, _config$steps2, _editorView;
|
|
78
86
|
var schema = state.schema;
|
|
79
87
|
var isDisplayingChanges = ((_config$steps = config === null || config === void 0 ? void 0 : config.steps) !== null && _config$steps !== void 0 ? _config$steps : []).length > 0;
|
|
88
|
+
var steps = ((_config$steps2 = config === null || config === void 0 ? void 0 : config.steps) !== null && _config$steps2 !== void 0 ? _config$steps2 : []).map(function (step) {
|
|
89
|
+
return _transform.Step.fromJSON(schema, step);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
+
var nodeViews = ((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.nodeViews) || {};
|
|
80
94
|
return {
|
|
81
|
-
steps:
|
|
82
|
-
return _transform.Step.fromJSON(schema, step);
|
|
83
|
-
}),
|
|
95
|
+
steps: steps,
|
|
84
96
|
originalDoc: config !== null && config !== void 0 && config.originalDoc ? (0, _processRawValue.processRawValue)(state.schema, config.originalDoc) : undefined,
|
|
85
97
|
decorations: calculateDecorations({
|
|
86
98
|
state: state,
|
|
87
99
|
pluginState: {
|
|
88
|
-
steps:
|
|
100
|
+
steps: steps,
|
|
89
101
|
originalDoc: config !== null && config !== void 0 && config.originalDoc ? (0, _processRawValue.processRawValue)(state.schema, config.originalDoc) : undefined,
|
|
90
102
|
isDisplayingChanges: isDisplayingChanges
|
|
91
|
-
}
|
|
103
|
+
},
|
|
104
|
+
editorView: editorView,
|
|
105
|
+
nodeViews: nodeViews
|
|
92
106
|
}),
|
|
93
107
|
isDisplayingChanges: isDisplayingChanges
|
|
94
108
|
};
|
|
95
109
|
},
|
|
96
110
|
apply: function apply(tr, currentPluginState, oldState, newState) {
|
|
111
|
+
var _editorView2;
|
|
97
112
|
var meta = tr.getMeta(showDiffPluginKey);
|
|
98
113
|
var newPluginState = currentPluginState;
|
|
114
|
+
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
116
|
+
var nodeViews = ((_editorView2 = editorView) === null || _editorView2 === void 0 ? void 0 : _editorView2.nodeViews) || {};
|
|
99
117
|
if (meta) {
|
|
100
118
|
if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SHOW_DIFF') {
|
|
119
|
+
var _meta$steps, _meta$originalDoc;
|
|
120
|
+
// Calculate and store decorations in state
|
|
121
|
+
var decorations = calculateDecorations({
|
|
122
|
+
state: newState,
|
|
123
|
+
pluginState: {
|
|
124
|
+
steps: (_meta$steps = meta.steps) !== null && _meta$steps !== void 0 ? _meta$steps : currentPluginState.steps,
|
|
125
|
+
originalDoc: (_meta$originalDoc = meta.originalDoc) !== null && _meta$originalDoc !== void 0 ? _meta$originalDoc : currentPluginState.originalDoc,
|
|
126
|
+
isDisplayingChanges: true
|
|
127
|
+
},
|
|
128
|
+
editorView: editorView,
|
|
129
|
+
nodeViews: nodeViews
|
|
130
|
+
});
|
|
101
131
|
newPluginState = _objectSpread(_objectSpread(_objectSpread({}, currentPluginState), meta), {}, {
|
|
132
|
+
decorations: decorations,
|
|
102
133
|
isDisplayingChanges: true
|
|
103
134
|
});
|
|
104
135
|
} else if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'HIDE_DIFF') {
|
|
105
136
|
newPluginState = _objectSpread(_objectSpread(_objectSpread({}, currentPluginState), meta), {}, {
|
|
137
|
+
decorations: _view.DecorationSet.empty,
|
|
106
138
|
isDisplayingChanges: false
|
|
107
139
|
});
|
|
108
140
|
} else {
|
|
109
141
|
newPluginState = _objectSpread(_objectSpread({}, currentPluginState), meta);
|
|
110
142
|
}
|
|
111
143
|
}
|
|
112
|
-
|
|
113
|
-
// Calculate and store decorations in state
|
|
114
|
-
var decorations = calculateDecorations({
|
|
115
|
-
state: newState,
|
|
116
|
-
pluginState: {
|
|
117
|
-
steps: newPluginState.steps,
|
|
118
|
-
originalDoc: newPluginState.originalDoc,
|
|
119
|
-
isDisplayingChanges: newPluginState.isDisplayingChanges
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
144
|
return _objectSpread(_objectSpread({}, newPluginState), {}, {
|
|
123
|
-
decorations: decorations
|
|
145
|
+
decorations: newPluginState.decorations.map(tr.mapping, tr.doc)
|
|
124
146
|
});
|
|
125
147
|
}
|
|
126
148
|
},
|
|
149
|
+
view: function view(editorView) {
|
|
150
|
+
setEditorView(editorView);
|
|
151
|
+
return {};
|
|
152
|
+
},
|
|
127
153
|
props: {
|
|
128
154
|
decorations: function decorations(state) {
|
|
129
155
|
var pluginState = showDiffPluginKey.getState(state);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Utilities for working with ProseMirror node views and DOM serialization within the
|
|
5
|
+
* Show Diff editor plugin.
|
|
6
|
+
*
|
|
7
|
+
* This module centralizes:
|
|
8
|
+
* - Access to the editor's `nodeViews` registry (when available on `EditorView`)
|
|
9
|
+
* - Safe attempts to instantiate a node view for a given node, with a blocklist to
|
|
10
|
+
* avoid node types that are known to be problematic in this context (e.g. tables)
|
|
11
|
+
* - Schema-driven serialization of nodes and fragments to DOM via `DOMSerializer`
|
|
12
|
+
*
|
|
13
|
+
* The Show Diff decorations leverage this to either render nodes using their
|
|
14
|
+
* corresponding node view implementation, or fall back to DOM serialization.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Narrowed `EditorView` that exposes the internal `nodeViews` registry.
|
|
19
|
+
* Many editor instances provide this, but it's not part of the base type.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Type guard to detect whether an `EditorView` exposes a `nodeViews` map.
|
|
24
|
+
*/
|
|
25
|
+
export function isEditorViewWithNodeViews(view) {
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
return view.nodeViews !== undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Encapsulates DOM serialization and node view access/creation.
|
|
32
|
+
*
|
|
33
|
+
* Responsible for:
|
|
34
|
+
* - Creating a `DOMSerializer` from the provided schema
|
|
35
|
+
* - Reading `nodeViews` from an `EditorView` (if present) or using an explicit mapping
|
|
36
|
+
* - Preventing node view creation for blocklisted node types
|
|
37
|
+
*/
|
|
38
|
+
export class NodeViewSerializer {
|
|
39
|
+
constructor(params) {
|
|
40
|
+
var _ref, _params$nodeViews, _this$editorView, _params$blocklist;
|
|
41
|
+
this.serializer = DOMSerializer.fromSchema(params.schema);
|
|
42
|
+
if (params.editorView && isEditorViewWithNodeViews(params.editorView)) {
|
|
43
|
+
this.editorView = params.editorView;
|
|
44
|
+
}
|
|
45
|
+
this.nodeViews = (_ref = (_params$nodeViews = params.nodeViews) !== null && _params$nodeViews !== void 0 ? _params$nodeViews : (_this$editorView = this.editorView) === null || _this$editorView === void 0 ? void 0 : _this$editorView.nodeViews) !== null && _ref !== void 0 ? _ref : {};
|
|
46
|
+
this.nodeViewBlocklist = new Set((_params$blocklist = params.blocklist) !== null && _params$blocklist !== void 0 ? _params$blocklist : ['tableRow', 'table']);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Attempts to create a node view for the given node.
|
|
51
|
+
*
|
|
52
|
+
* Returns `null` when there is no `EditorView`, no constructor for the node type,
|
|
53
|
+
* or the node type is blocklisted. Otherwise returns the constructed node view instance.
|
|
54
|
+
*/
|
|
55
|
+
tryCreateNodeView(targetNode) {
|
|
56
|
+
if (!this.editorView) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const constructor = this.nodeViews[targetNode.type.name];
|
|
60
|
+
if (!constructor) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (this.nodeViewBlocklist.has(targetNode.type.name)) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return constructor(targetNode, this.editorView, () => 0, [], {});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Serializes a node to a DOM `Node` using the schema's `DOMSerializer`.
|
|
71
|
+
*/
|
|
72
|
+
serializeNode(node) {
|
|
73
|
+
return this.serializer.serializeNode(node);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Serializes a fragment to a `DocumentFragment` using the schema's `DOMSerializer`.
|
|
78
|
+
*/
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
+
serializeFragment(fragment) {
|
|
81
|
+
return this.serializer.serializeFragment(fragment);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
|
-
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
3
2
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
+
import { NodeViewSerializer } from './NodeViewSerializer';
|
|
4
4
|
const style = convertToInlineCss({
|
|
5
5
|
background: "var(--ds-background-accent-purple-subtlest, #F3F0FF)",
|
|
6
6
|
textDecoration: 'underline',
|
|
@@ -8,7 +8,6 @@ const style = convertToInlineCss({
|
|
|
8
8
|
textDecorationThickness: "var(--ds-space-025, 2px)",
|
|
9
9
|
textDecorationColor: "var(--ds-border-accent-purple, #8270DB)"
|
|
10
10
|
});
|
|
11
|
-
|
|
12
11
|
/**
|
|
13
12
|
* Inline decoration used for insertions as the content already exists in the document
|
|
14
13
|
*
|
|
@@ -21,7 +20,18 @@ export const createInlineChangedDecoration = change => Decoration.inline(change.
|
|
|
21
20
|
}, {});
|
|
22
21
|
const deletedContentStyle = convertToInlineCss({
|
|
23
22
|
color: "var(--ds-text-accent-gray, #44546F)",
|
|
24
|
-
textDecoration: 'line-through'
|
|
23
|
+
textDecoration: 'line-through',
|
|
24
|
+
position: 'relative',
|
|
25
|
+
opacity: 0.6
|
|
26
|
+
});
|
|
27
|
+
const deletedContentStyleUnbounded = convertToInlineCss({
|
|
28
|
+
position: 'absolute',
|
|
29
|
+
top: '50%',
|
|
30
|
+
width: '100%',
|
|
31
|
+
display: 'inline-block',
|
|
32
|
+
borderTop: `1px solid ${"var(--ds-text-accent-gray, #44546F)"}`,
|
|
33
|
+
pointerEvents: 'none',
|
|
34
|
+
zIndex: 1
|
|
25
35
|
});
|
|
26
36
|
|
|
27
37
|
/**
|
|
@@ -35,7 +45,9 @@ const deletedContentStyle = convertToInlineCss({
|
|
|
35
45
|
export const createDeletedContentDecoration = ({
|
|
36
46
|
change,
|
|
37
47
|
doc,
|
|
38
|
-
tr
|
|
48
|
+
tr,
|
|
49
|
+
editorView,
|
|
50
|
+
nodeViews
|
|
39
51
|
}) => {
|
|
40
52
|
const dom = document.createElement('span');
|
|
41
53
|
dom.setAttribute('style', deletedContentStyle);
|
|
@@ -46,22 +58,90 @@ export const createDeletedContentDecoration = ({
|
|
|
46
58
|
* or sliced End depth is and match only the content and not with the entire node.
|
|
47
59
|
*/
|
|
48
60
|
const slice = doc.slice(change.fromA, change.toA);
|
|
61
|
+
const nodeViewSerializer = new NodeViewSerializer({
|
|
62
|
+
schema: tr.doc.type.schema,
|
|
63
|
+
editorView,
|
|
64
|
+
nodeViews
|
|
65
|
+
});
|
|
49
66
|
slice.content.forEach(node => {
|
|
50
|
-
|
|
67
|
+
// Create a wrapper for each node with strikethrough
|
|
68
|
+
const createWrapperWithStrikethrough = () => {
|
|
69
|
+
const wrapper = document.createElement('span');
|
|
70
|
+
wrapper.style.position = 'relative';
|
|
71
|
+
wrapper.style.width = 'fit-content';
|
|
72
|
+
const strikethrough = document.createElement('span');
|
|
73
|
+
strikethrough.setAttribute('style', deletedContentStyleUnbounded);
|
|
74
|
+
wrapper.append(strikethrough);
|
|
75
|
+
return wrapper;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Helper function to handle multiple child nodes
|
|
79
|
+
const handleMultipleChildNodes = node => {
|
|
80
|
+
if (node.content.childCount > 1 && node.type.inlineContent) {
|
|
81
|
+
node.content.forEach(childNode => {
|
|
82
|
+
const childNodeView = nodeViewSerializer.tryCreateNodeView(childNode);
|
|
83
|
+
if (childNodeView) {
|
|
84
|
+
const lineBreak = document.createElement('br');
|
|
85
|
+
targetNode = node;
|
|
86
|
+
dom.append(lineBreak);
|
|
87
|
+
const wrapper = createWrapperWithStrikethrough();
|
|
88
|
+
wrapper.append(childNodeView.dom);
|
|
89
|
+
dom.append(wrapper);
|
|
90
|
+
} else {
|
|
91
|
+
// Fallback to serializing the individual child node
|
|
92
|
+
const serializedChild = nodeViewSerializer.serializeNode(childNode);
|
|
93
|
+
dom.append(serializedChild);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
return true; // Indicates we handled multiple children
|
|
97
|
+
}
|
|
98
|
+
return false; // Indicates single child, continue with normal logic
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Determine which node to use and how to serialize
|
|
51
102
|
const isFirst = slice.content.firstChild === node;
|
|
52
103
|
const isLast = slice.content.lastChild === node;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
104
|
+
const hasInlineContent = node.content.childCount > 0 && node.type.inlineContent === true;
|
|
105
|
+
let targetNode;
|
|
106
|
+
let fallbackSerialization;
|
|
107
|
+
if ((isFirst || isLast && slice.content.childCount > 2) && hasInlineContent) {
|
|
108
|
+
if (handleMultipleChildNodes(node)) {
|
|
109
|
+
return;
|
|
58
110
|
}
|
|
111
|
+
targetNode = node.content.content[0];
|
|
112
|
+
fallbackSerialization = () => nodeViewSerializer.serializeFragment(node.content);
|
|
59
113
|
} else if (isLast && slice.content.childCount === 2) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
114
|
+
if (handleMultipleChildNodes(node)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
targetNode = node;
|
|
118
|
+
fallbackSerialization = () => {
|
|
119
|
+
if (node.type.name === 'text') {
|
|
120
|
+
return document.createTextNode(node.text || '');
|
|
121
|
+
}
|
|
122
|
+
if (node.type.name === 'paragraph') {
|
|
123
|
+
const lineBreak = document.createElement('br');
|
|
124
|
+
dom.append(lineBreak);
|
|
125
|
+
return nodeViewSerializer.serializeFragment(node.content);
|
|
126
|
+
}
|
|
127
|
+
return nodeViewSerializer.serializeFragment(node.content);
|
|
128
|
+
};
|
|
129
|
+
} else {
|
|
130
|
+
if (handleMultipleChildNodes(node)) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
targetNode = node.content.content[0] || node;
|
|
134
|
+
fallbackSerialization = () => nodeViewSerializer.serializeNode(node);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Try to create node view, fallback to serialization
|
|
138
|
+
const nodeView = nodeViewSerializer.tryCreateNodeView(targetNode);
|
|
139
|
+
if (nodeView) {
|
|
140
|
+
const wrapper = createWrapperWithStrikethrough();
|
|
141
|
+
wrapper.append(nodeView.dom);
|
|
142
|
+
dom.append(wrapper);
|
|
63
143
|
} else {
|
|
64
|
-
dom.append(
|
|
144
|
+
dom.append(fallbackSerialization());
|
|
65
145
|
}
|
|
66
146
|
});
|
|
67
147
|
dom.setAttribute('data-testid', 'show-diff-deleted-decoration');
|
|
@@ -9,7 +9,9 @@ import { createInlineChangedDecoration, createDeletedContentDecoration } from '.
|
|
|
9
9
|
import { getMarkChangeRanges } from './markDecorations';
|
|
10
10
|
const calculateDecorations = ({
|
|
11
11
|
state,
|
|
12
|
-
pluginState
|
|
12
|
+
pluginState,
|
|
13
|
+
editorView,
|
|
14
|
+
nodeViews
|
|
13
15
|
}) => {
|
|
14
16
|
const {
|
|
15
17
|
originalDoc,
|
|
@@ -43,7 +45,9 @@ const calculateDecorations = ({
|
|
|
43
45
|
decorations.push(createDeletedContentDecoration({
|
|
44
46
|
change,
|
|
45
47
|
doc: originalDoc,
|
|
46
|
-
tr
|
|
48
|
+
tr,
|
|
49
|
+
editorView,
|
|
50
|
+
nodeViews
|
|
47
51
|
}));
|
|
48
52
|
}
|
|
49
53
|
});
|
|
@@ -54,41 +58,69 @@ const calculateDecorations = ({
|
|
|
54
58
|
};
|
|
55
59
|
export const showDiffPluginKey = new PluginKey('showDiffPlugin');
|
|
56
60
|
export const createPlugin = config => {
|
|
61
|
+
let editorView;
|
|
62
|
+
const setEditorView = newEditorView => {
|
|
63
|
+
editorView = newEditorView;
|
|
64
|
+
};
|
|
57
65
|
return new SafePlugin({
|
|
58
66
|
key: showDiffPluginKey,
|
|
59
67
|
state: {
|
|
60
68
|
init(_, state) {
|
|
61
|
-
var _config$steps, _config$steps2;
|
|
69
|
+
var _config$steps, _config$steps2, _editorView;
|
|
62
70
|
const schema = state.schema;
|
|
63
71
|
const isDisplayingChanges = ((_config$steps = config === null || config === void 0 ? void 0 : config.steps) !== null && _config$steps !== void 0 ? _config$steps : []).length > 0;
|
|
72
|
+
const steps = ((_config$steps2 = config === null || config === void 0 ? void 0 : config.steps) !== null && _config$steps2 !== void 0 ? _config$steps2 : []).map(step => ProseMirrorStep.fromJSON(schema, step));
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
const nodeViews = ((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.nodeViews) || {};
|
|
64
76
|
return {
|
|
65
|
-
steps
|
|
77
|
+
steps,
|
|
66
78
|
originalDoc: config !== null && config !== void 0 && config.originalDoc ? processRawValue(state.schema, config.originalDoc) : undefined,
|
|
67
79
|
decorations: calculateDecorations({
|
|
68
80
|
state,
|
|
69
81
|
pluginState: {
|
|
70
|
-
steps
|
|
82
|
+
steps,
|
|
71
83
|
originalDoc: config !== null && config !== void 0 && config.originalDoc ? processRawValue(state.schema, config.originalDoc) : undefined,
|
|
72
84
|
isDisplayingChanges
|
|
73
|
-
}
|
|
85
|
+
},
|
|
86
|
+
editorView,
|
|
87
|
+
nodeViews
|
|
74
88
|
}),
|
|
75
89
|
isDisplayingChanges
|
|
76
90
|
};
|
|
77
91
|
},
|
|
78
92
|
apply: (tr, currentPluginState, oldState, newState) => {
|
|
93
|
+
var _editorView2;
|
|
79
94
|
const meta = tr.getMeta(showDiffPluginKey);
|
|
80
95
|
let newPluginState = currentPluginState;
|
|
96
|
+
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
+
const nodeViews = ((_editorView2 = editorView) === null || _editorView2 === void 0 ? void 0 : _editorView2.nodeViews) || {};
|
|
81
99
|
if (meta) {
|
|
82
100
|
if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SHOW_DIFF') {
|
|
101
|
+
var _meta$steps, _meta$originalDoc;
|
|
102
|
+
// Calculate and store decorations in state
|
|
103
|
+
const decorations = calculateDecorations({
|
|
104
|
+
state: newState,
|
|
105
|
+
pluginState: {
|
|
106
|
+
steps: (_meta$steps = meta.steps) !== null && _meta$steps !== void 0 ? _meta$steps : currentPluginState.steps,
|
|
107
|
+
originalDoc: (_meta$originalDoc = meta.originalDoc) !== null && _meta$originalDoc !== void 0 ? _meta$originalDoc : currentPluginState.originalDoc,
|
|
108
|
+
isDisplayingChanges: true
|
|
109
|
+
},
|
|
110
|
+
editorView,
|
|
111
|
+
nodeViews: nodeViews
|
|
112
|
+
});
|
|
83
113
|
newPluginState = {
|
|
84
114
|
...currentPluginState,
|
|
85
115
|
...meta,
|
|
116
|
+
decorations,
|
|
86
117
|
isDisplayingChanges: true
|
|
87
118
|
};
|
|
88
119
|
} else if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'HIDE_DIFF') {
|
|
89
120
|
newPluginState = {
|
|
90
121
|
...currentPluginState,
|
|
91
122
|
...meta,
|
|
123
|
+
decorations: DecorationSet.empty,
|
|
92
124
|
isDisplayingChanges: false
|
|
93
125
|
};
|
|
94
126
|
} else {
|
|
@@ -98,22 +130,16 @@ export const createPlugin = config => {
|
|
|
98
130
|
};
|
|
99
131
|
}
|
|
100
132
|
}
|
|
101
|
-
|
|
102
|
-
// Calculate and store decorations in state
|
|
103
|
-
const decorations = calculateDecorations({
|
|
104
|
-
state: newState,
|
|
105
|
-
pluginState: {
|
|
106
|
-
steps: newPluginState.steps,
|
|
107
|
-
originalDoc: newPluginState.originalDoc,
|
|
108
|
-
isDisplayingChanges: newPluginState.isDisplayingChanges
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
133
|
return {
|
|
112
134
|
...newPluginState,
|
|
113
|
-
decorations
|
|
135
|
+
decorations: newPluginState.decorations.map(tr.mapping, tr.doc)
|
|
114
136
|
};
|
|
115
137
|
}
|
|
116
138
|
},
|
|
139
|
+
view(editorView) {
|
|
140
|
+
setEditorView(editorView);
|
|
141
|
+
return {};
|
|
142
|
+
},
|
|
117
143
|
props: {
|
|
118
144
|
decorations: state => {
|
|
119
145
|
const pluginState = showDiffPluginKey.getState(state);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
2
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
|
+
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Utilities for working with ProseMirror node views and DOM serialization within the
|
|
7
|
+
* Show Diff editor plugin.
|
|
8
|
+
*
|
|
9
|
+
* This module centralizes:
|
|
10
|
+
* - Access to the editor's `nodeViews` registry (when available on `EditorView`)
|
|
11
|
+
* - Safe attempts to instantiate a node view for a given node, with a blocklist to
|
|
12
|
+
* avoid node types that are known to be problematic in this context (e.g. tables)
|
|
13
|
+
* - Schema-driven serialization of nodes and fragments to DOM via `DOMSerializer`
|
|
14
|
+
*
|
|
15
|
+
* The Show Diff decorations leverage this to either render nodes using their
|
|
16
|
+
* corresponding node view implementation, or fall back to DOM serialization.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Narrowed `EditorView` that exposes the internal `nodeViews` registry.
|
|
21
|
+
* Many editor instances provide this, but it's not part of the base type.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Type guard to detect whether an `EditorView` exposes a `nodeViews` map.
|
|
26
|
+
*/
|
|
27
|
+
export function isEditorViewWithNodeViews(view) {
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
return view.nodeViews !== undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Encapsulates DOM serialization and node view access/creation.
|
|
34
|
+
*
|
|
35
|
+
* Responsible for:
|
|
36
|
+
* - Creating a `DOMSerializer` from the provided schema
|
|
37
|
+
* - Reading `nodeViews` from an `EditorView` (if present) or using an explicit mapping
|
|
38
|
+
* - Preventing node view creation for blocklisted node types
|
|
39
|
+
*/
|
|
40
|
+
export var NodeViewSerializer = /*#__PURE__*/function () {
|
|
41
|
+
function NodeViewSerializer(params) {
|
|
42
|
+
var _ref, _params$nodeViews, _this$editorView, _params$blocklist;
|
|
43
|
+
_classCallCheck(this, NodeViewSerializer);
|
|
44
|
+
this.serializer = DOMSerializer.fromSchema(params.schema);
|
|
45
|
+
if (params.editorView && isEditorViewWithNodeViews(params.editorView)) {
|
|
46
|
+
this.editorView = params.editorView;
|
|
47
|
+
}
|
|
48
|
+
this.nodeViews = (_ref = (_params$nodeViews = params.nodeViews) !== null && _params$nodeViews !== void 0 ? _params$nodeViews : (_this$editorView = this.editorView) === null || _this$editorView === void 0 ? void 0 : _this$editorView.nodeViews) !== null && _ref !== void 0 ? _ref : {};
|
|
49
|
+
this.nodeViewBlocklist = new Set((_params$blocklist = params.blocklist) !== null && _params$blocklist !== void 0 ? _params$blocklist : ['tableRow', 'table']);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Attempts to create a node view for the given node.
|
|
54
|
+
*
|
|
55
|
+
* Returns `null` when there is no `EditorView`, no constructor for the node type,
|
|
56
|
+
* or the node type is blocklisted. Otherwise returns the constructed node view instance.
|
|
57
|
+
*/
|
|
58
|
+
return _createClass(NodeViewSerializer, [{
|
|
59
|
+
key: "tryCreateNodeView",
|
|
60
|
+
value: function tryCreateNodeView(targetNode) {
|
|
61
|
+
if (!this.editorView) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
var constructor = this.nodeViews[targetNode.type.name];
|
|
65
|
+
if (!constructor) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
if (this.nodeViewBlocklist.has(targetNode.type.name)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return constructor(targetNode, this.editorView, function () {
|
|
72
|
+
return 0;
|
|
73
|
+
}, [], {});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Serializes a node to a DOM `Node` using the schema's `DOMSerializer`.
|
|
78
|
+
*/
|
|
79
|
+
}, {
|
|
80
|
+
key: "serializeNode",
|
|
81
|
+
value: function serializeNode(node) {
|
|
82
|
+
return this.serializer.serializeNode(node);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Serializes a fragment to a `DocumentFragment` using the schema's `DOMSerializer`.
|
|
87
|
+
*/
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
89
|
+
}, {
|
|
90
|
+
key: "serializeFragment",
|
|
91
|
+
value: function serializeFragment(fragment) {
|
|
92
|
+
return this.serializer.serializeFragment(fragment);
|
|
93
|
+
}
|
|
94
|
+
}]);
|
|
95
|
+
}();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
|
-
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
3
2
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
+
import { NodeViewSerializer } from './NodeViewSerializer';
|
|
4
4
|
var style = convertToInlineCss({
|
|
5
5
|
background: "var(--ds-background-accent-purple-subtlest, #F3F0FF)",
|
|
6
6
|
textDecoration: 'underline',
|
|
@@ -8,7 +8,6 @@ var style = convertToInlineCss({
|
|
|
8
8
|
textDecorationThickness: "var(--ds-space-025, 2px)",
|
|
9
9
|
textDecorationColor: "var(--ds-border-accent-purple, #8270DB)"
|
|
10
10
|
});
|
|
11
|
-
|
|
12
11
|
/**
|
|
13
12
|
* Inline decoration used for insertions as the content already exists in the document
|
|
14
13
|
*
|
|
@@ -23,7 +22,18 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
|
|
|
23
22
|
};
|
|
24
23
|
var deletedContentStyle = convertToInlineCss({
|
|
25
24
|
color: "var(--ds-text-accent-gray, #44546F)",
|
|
26
|
-
textDecoration: 'line-through'
|
|
25
|
+
textDecoration: 'line-through',
|
|
26
|
+
position: 'relative',
|
|
27
|
+
opacity: 0.6
|
|
28
|
+
});
|
|
29
|
+
var deletedContentStyleUnbounded = convertToInlineCss({
|
|
30
|
+
position: 'absolute',
|
|
31
|
+
top: '50%',
|
|
32
|
+
width: '100%',
|
|
33
|
+
display: 'inline-block',
|
|
34
|
+
borderTop: "1px solid ".concat("var(--ds-text-accent-gray, #44546F)"),
|
|
35
|
+
pointerEvents: 'none',
|
|
36
|
+
zIndex: 1
|
|
27
37
|
});
|
|
28
38
|
|
|
29
39
|
/**
|
|
@@ -37,7 +47,9 @@ var deletedContentStyle = convertToInlineCss({
|
|
|
37
47
|
export var createDeletedContentDecoration = function createDeletedContentDecoration(_ref) {
|
|
38
48
|
var change = _ref.change,
|
|
39
49
|
doc = _ref.doc,
|
|
40
|
-
tr = _ref.tr
|
|
50
|
+
tr = _ref.tr,
|
|
51
|
+
editorView = _ref.editorView,
|
|
52
|
+
nodeViews = _ref.nodeViews;
|
|
41
53
|
var dom = document.createElement('span');
|
|
42
54
|
dom.setAttribute('style', deletedContentStyle);
|
|
43
55
|
|
|
@@ -47,22 +59,94 @@ export var createDeletedContentDecoration = function createDeletedContentDecorat
|
|
|
47
59
|
* or sliced End depth is and match only the content and not with the entire node.
|
|
48
60
|
*/
|
|
49
61
|
var slice = doc.slice(change.fromA, change.toA);
|
|
62
|
+
var nodeViewSerializer = new NodeViewSerializer({
|
|
63
|
+
schema: tr.doc.type.schema,
|
|
64
|
+
editorView: editorView,
|
|
65
|
+
nodeViews: nodeViews
|
|
66
|
+
});
|
|
50
67
|
slice.content.forEach(function (node) {
|
|
51
|
-
|
|
68
|
+
// Create a wrapper for each node with strikethrough
|
|
69
|
+
var createWrapperWithStrikethrough = function createWrapperWithStrikethrough() {
|
|
70
|
+
var wrapper = document.createElement('span');
|
|
71
|
+
wrapper.style.position = 'relative';
|
|
72
|
+
wrapper.style.width = 'fit-content';
|
|
73
|
+
var strikethrough = document.createElement('span');
|
|
74
|
+
strikethrough.setAttribute('style', deletedContentStyleUnbounded);
|
|
75
|
+
wrapper.append(strikethrough);
|
|
76
|
+
return wrapper;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Helper function to handle multiple child nodes
|
|
80
|
+
var handleMultipleChildNodes = function handleMultipleChildNodes(node) {
|
|
81
|
+
if (node.content.childCount > 1 && node.type.inlineContent) {
|
|
82
|
+
node.content.forEach(function (childNode) {
|
|
83
|
+
var childNodeView = nodeViewSerializer.tryCreateNodeView(childNode);
|
|
84
|
+
if (childNodeView) {
|
|
85
|
+
var lineBreak = document.createElement('br');
|
|
86
|
+
targetNode = node;
|
|
87
|
+
dom.append(lineBreak);
|
|
88
|
+
var wrapper = createWrapperWithStrikethrough();
|
|
89
|
+
wrapper.append(childNodeView.dom);
|
|
90
|
+
dom.append(wrapper);
|
|
91
|
+
} else {
|
|
92
|
+
// Fallback to serializing the individual child node
|
|
93
|
+
var serializedChild = nodeViewSerializer.serializeNode(childNode);
|
|
94
|
+
dom.append(serializedChild);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return true; // Indicates we handled multiple children
|
|
98
|
+
}
|
|
99
|
+
return false; // Indicates single child, continue with normal logic
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Determine which node to use and how to serialize
|
|
52
103
|
var isFirst = slice.content.firstChild === node;
|
|
53
104
|
var isLast = slice.content.lastChild === node;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
105
|
+
var hasInlineContent = node.content.childCount > 0 && node.type.inlineContent === true;
|
|
106
|
+
var targetNode;
|
|
107
|
+
var fallbackSerialization;
|
|
108
|
+
if ((isFirst || isLast && slice.content.childCount > 2) && hasInlineContent) {
|
|
109
|
+
if (handleMultipleChildNodes(node)) {
|
|
110
|
+
return;
|
|
59
111
|
}
|
|
112
|
+
targetNode = node.content.content[0];
|
|
113
|
+
fallbackSerialization = function fallbackSerialization() {
|
|
114
|
+
return nodeViewSerializer.serializeFragment(node.content);
|
|
115
|
+
};
|
|
60
116
|
} else if (isLast && slice.content.childCount === 2) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
117
|
+
if (handleMultipleChildNodes(node)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
targetNode = node;
|
|
121
|
+
fallbackSerialization = function fallbackSerialization() {
|
|
122
|
+
if (node.type.name === 'text') {
|
|
123
|
+
return document.createTextNode(node.text || '');
|
|
124
|
+
}
|
|
125
|
+
if (node.type.name === 'paragraph') {
|
|
126
|
+
var lineBreak = document.createElement('br');
|
|
127
|
+
dom.append(lineBreak);
|
|
128
|
+
return nodeViewSerializer.serializeFragment(node.content);
|
|
129
|
+
}
|
|
130
|
+
return nodeViewSerializer.serializeFragment(node.content);
|
|
131
|
+
};
|
|
132
|
+
} else {
|
|
133
|
+
if (handleMultipleChildNodes(node)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
targetNode = node.content.content[0] || node;
|
|
137
|
+
fallbackSerialization = function fallbackSerialization() {
|
|
138
|
+
return nodeViewSerializer.serializeNode(node);
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Try to create node view, fallback to serialization
|
|
143
|
+
var nodeView = nodeViewSerializer.tryCreateNodeView(targetNode);
|
|
144
|
+
if (nodeView) {
|
|
145
|
+
var wrapper = createWrapperWithStrikethrough();
|
|
146
|
+
wrapper.append(nodeView.dom);
|
|
147
|
+
dom.append(wrapper);
|
|
64
148
|
} else {
|
|
65
|
-
dom.append(
|
|
149
|
+
dom.append(fallbackSerialization());
|
|
66
150
|
}
|
|
67
151
|
});
|
|
68
152
|
dom.setAttribute('data-testid', 'show-diff-deleted-decoration');
|
|
@@ -15,7 +15,9 @@ import { createInlineChangedDecoration, createDeletedContentDecoration } from '.
|
|
|
15
15
|
import { getMarkChangeRanges } from './markDecorations';
|
|
16
16
|
var calculateDecorations = function calculateDecorations(_ref) {
|
|
17
17
|
var state = _ref.state,
|
|
18
|
-
pluginState = _ref.pluginState
|
|
18
|
+
pluginState = _ref.pluginState,
|
|
19
|
+
editorView = _ref.editorView,
|
|
20
|
+
nodeViews = _ref.nodeViews;
|
|
19
21
|
var originalDoc = pluginState.originalDoc,
|
|
20
22
|
steps = pluginState.steps;
|
|
21
23
|
if (!originalDoc || !pluginState.isDisplayingChanges) {
|
|
@@ -53,7 +55,9 @@ var calculateDecorations = function calculateDecorations(_ref) {
|
|
|
53
55
|
decorations.push(createDeletedContentDecoration({
|
|
54
56
|
change: change,
|
|
55
57
|
doc: originalDoc,
|
|
56
|
-
tr: tr
|
|
58
|
+
tr: tr,
|
|
59
|
+
editorView: editorView,
|
|
60
|
+
nodeViews: nodeViews
|
|
57
61
|
}));
|
|
58
62
|
}
|
|
59
63
|
});
|
|
@@ -64,60 +68,82 @@ var calculateDecorations = function calculateDecorations(_ref) {
|
|
|
64
68
|
};
|
|
65
69
|
export var showDiffPluginKey = new PluginKey('showDiffPlugin');
|
|
66
70
|
export var createPlugin = function createPlugin(config) {
|
|
71
|
+
var editorView;
|
|
72
|
+
var setEditorView = function setEditorView(newEditorView) {
|
|
73
|
+
editorView = newEditorView;
|
|
74
|
+
};
|
|
67
75
|
return new SafePlugin({
|
|
68
76
|
key: showDiffPluginKey,
|
|
69
77
|
state: {
|
|
70
78
|
init: function init(_, state) {
|
|
71
|
-
var _config$steps, _config$steps2;
|
|
79
|
+
var _config$steps, _config$steps2, _editorView;
|
|
72
80
|
var schema = state.schema;
|
|
73
81
|
var isDisplayingChanges = ((_config$steps = config === null || config === void 0 ? void 0 : config.steps) !== null && _config$steps !== void 0 ? _config$steps : []).length > 0;
|
|
82
|
+
var steps = ((_config$steps2 = config === null || config === void 0 ? void 0 : config.steps) !== null && _config$steps2 !== void 0 ? _config$steps2 : []).map(function (step) {
|
|
83
|
+
return ProseMirrorStep.fromJSON(schema, step);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
87
|
+
var nodeViews = ((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.nodeViews) || {};
|
|
74
88
|
return {
|
|
75
|
-
steps:
|
|
76
|
-
return ProseMirrorStep.fromJSON(schema, step);
|
|
77
|
-
}),
|
|
89
|
+
steps: steps,
|
|
78
90
|
originalDoc: config !== null && config !== void 0 && config.originalDoc ? processRawValue(state.schema, config.originalDoc) : undefined,
|
|
79
91
|
decorations: calculateDecorations({
|
|
80
92
|
state: state,
|
|
81
93
|
pluginState: {
|
|
82
|
-
steps:
|
|
94
|
+
steps: steps,
|
|
83
95
|
originalDoc: config !== null && config !== void 0 && config.originalDoc ? processRawValue(state.schema, config.originalDoc) : undefined,
|
|
84
96
|
isDisplayingChanges: isDisplayingChanges
|
|
85
|
-
}
|
|
97
|
+
},
|
|
98
|
+
editorView: editorView,
|
|
99
|
+
nodeViews: nodeViews
|
|
86
100
|
}),
|
|
87
101
|
isDisplayingChanges: isDisplayingChanges
|
|
88
102
|
};
|
|
89
103
|
},
|
|
90
104
|
apply: function apply(tr, currentPluginState, oldState, newState) {
|
|
105
|
+
var _editorView2;
|
|
91
106
|
var meta = tr.getMeta(showDiffPluginKey);
|
|
92
107
|
var newPluginState = currentPluginState;
|
|
108
|
+
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
110
|
+
var nodeViews = ((_editorView2 = editorView) === null || _editorView2 === void 0 ? void 0 : _editorView2.nodeViews) || {};
|
|
93
111
|
if (meta) {
|
|
94
112
|
if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SHOW_DIFF') {
|
|
113
|
+
var _meta$steps, _meta$originalDoc;
|
|
114
|
+
// Calculate and store decorations in state
|
|
115
|
+
var decorations = calculateDecorations({
|
|
116
|
+
state: newState,
|
|
117
|
+
pluginState: {
|
|
118
|
+
steps: (_meta$steps = meta.steps) !== null && _meta$steps !== void 0 ? _meta$steps : currentPluginState.steps,
|
|
119
|
+
originalDoc: (_meta$originalDoc = meta.originalDoc) !== null && _meta$originalDoc !== void 0 ? _meta$originalDoc : currentPluginState.originalDoc,
|
|
120
|
+
isDisplayingChanges: true
|
|
121
|
+
},
|
|
122
|
+
editorView: editorView,
|
|
123
|
+
nodeViews: nodeViews
|
|
124
|
+
});
|
|
95
125
|
newPluginState = _objectSpread(_objectSpread(_objectSpread({}, currentPluginState), meta), {}, {
|
|
126
|
+
decorations: decorations,
|
|
96
127
|
isDisplayingChanges: true
|
|
97
128
|
});
|
|
98
129
|
} else if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'HIDE_DIFF') {
|
|
99
130
|
newPluginState = _objectSpread(_objectSpread(_objectSpread({}, currentPluginState), meta), {}, {
|
|
131
|
+
decorations: DecorationSet.empty,
|
|
100
132
|
isDisplayingChanges: false
|
|
101
133
|
});
|
|
102
134
|
} else {
|
|
103
135
|
newPluginState = _objectSpread(_objectSpread({}, currentPluginState), meta);
|
|
104
136
|
}
|
|
105
137
|
}
|
|
106
|
-
|
|
107
|
-
// Calculate and store decorations in state
|
|
108
|
-
var decorations = calculateDecorations({
|
|
109
|
-
state: newState,
|
|
110
|
-
pluginState: {
|
|
111
|
-
steps: newPluginState.steps,
|
|
112
|
-
originalDoc: newPluginState.originalDoc,
|
|
113
|
-
isDisplayingChanges: newPluginState.isDisplayingChanges
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
138
|
return _objectSpread(_objectSpread({}, newPluginState), {}, {
|
|
117
|
-
decorations: decorations
|
|
139
|
+
decorations: newPluginState.decorations.map(tr.mapping, tr.doc)
|
|
118
140
|
});
|
|
119
141
|
}
|
|
120
142
|
},
|
|
143
|
+
view: function view(editorView) {
|
|
144
|
+
setEditorView(editorView);
|
|
145
|
+
return {};
|
|
146
|
+
},
|
|
121
147
|
props: {
|
|
122
148
|
decorations: function decorations(state) {
|
|
123
149
|
var pluginState = showDiffPluginKey.getState(state);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type NodeViewConstructor } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
|
+
import type { Schema, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
4
|
+
/**
|
|
5
|
+
* Utilities for working with ProseMirror node views and DOM serialization within the
|
|
6
|
+
* Show Diff editor plugin.
|
|
7
|
+
*
|
|
8
|
+
* This module centralizes:
|
|
9
|
+
* - Access to the editor's `nodeViews` registry (when available on `EditorView`)
|
|
10
|
+
* - Safe attempts to instantiate a node view for a given node, with a blocklist to
|
|
11
|
+
* avoid node types that are known to be problematic in this context (e.g. tables)
|
|
12
|
+
* - Schema-driven serialization of nodes and fragments to DOM via `DOMSerializer`
|
|
13
|
+
*
|
|
14
|
+
* The Show Diff decorations leverage this to either render nodes using their
|
|
15
|
+
* corresponding node view implementation, or fall back to DOM serialization.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Narrowed `EditorView` that exposes the internal `nodeViews` registry.
|
|
19
|
+
* Many editor instances provide this, but it's not part of the base type.
|
|
20
|
+
*/
|
|
21
|
+
export interface EditorViewWithNodeViews extends EditorView {
|
|
22
|
+
nodeViews: Record<string, NodeViewConstructor>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Type guard to detect whether an `EditorView` exposes a `nodeViews` map.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isEditorViewWithNodeViews(view: EditorView): view is EditorViewWithNodeViews;
|
|
28
|
+
/**
|
|
29
|
+
* Encapsulates DOM serialization and node view access/creation.
|
|
30
|
+
*
|
|
31
|
+
* Responsible for:
|
|
32
|
+
* - Creating a `DOMSerializer` from the provided schema
|
|
33
|
+
* - Reading `nodeViews` from an `EditorView` (if present) or using an explicit mapping
|
|
34
|
+
* - Preventing node view creation for blocklisted node types
|
|
35
|
+
*/
|
|
36
|
+
export declare class NodeViewSerializer {
|
|
37
|
+
private serializer;
|
|
38
|
+
private editorView?;
|
|
39
|
+
private nodeViews;
|
|
40
|
+
private nodeViewBlocklist;
|
|
41
|
+
constructor(params: {
|
|
42
|
+
schema: Schema;
|
|
43
|
+
editorView?: EditorView;
|
|
44
|
+
nodeViews?: Record<string, NodeViewConstructor>;
|
|
45
|
+
blocklist?: string[];
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* Attempts to create a node view for the given node.
|
|
49
|
+
*
|
|
50
|
+
* Returns `null` when there is no `EditorView`, no constructor for the node type,
|
|
51
|
+
* or the node type is blocklisted. Otherwise returns the constructed node view instance.
|
|
52
|
+
*/
|
|
53
|
+
tryCreateNodeView(targetNode: PMNode): import("prosemirror-view").NodeView | null;
|
|
54
|
+
/**
|
|
55
|
+
* Serializes a node to a DOM `Node` using the schema's `DOMSerializer`.
|
|
56
|
+
*/
|
|
57
|
+
serializeNode(node: PMNode): Node;
|
|
58
|
+
/**
|
|
59
|
+
* Serializes a fragment to a `DocumentFragment` using the schema's `DOMSerializer`.
|
|
60
|
+
*/
|
|
61
|
+
serializeFragment(fragment: any): DocumentFragment | HTMLElement;
|
|
62
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Change } from 'prosemirror-changeset';
|
|
2
|
+
import { type NodeViewConstructor } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
3
|
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
4
|
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
5
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
4
6
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
5
7
|
/**
|
|
6
8
|
* Inline decoration used for insertions as the content already exists in the document
|
|
@@ -16,6 +18,8 @@ interface DeletedContentDecorationProps {
|
|
|
16
18
|
change: Change;
|
|
17
19
|
doc: PMNode;
|
|
18
20
|
tr: Transaction;
|
|
21
|
+
nodeViews: Record<string, NodeViewConstructor>;
|
|
22
|
+
editorView?: EditorView;
|
|
19
23
|
}
|
|
20
24
|
/**
|
|
21
25
|
* Creates a widget to represent the deleted content in the editor
|
|
@@ -25,5 +29,5 @@ interface DeletedContentDecorationProps {
|
|
|
25
29
|
* @param props.tr The relevant transaction this decoration is being created against
|
|
26
30
|
* @returns Prosemirror widget decoration
|
|
27
31
|
*/
|
|
28
|
-
export declare const createDeletedContentDecoration: ({ change, doc, tr, }: DeletedContentDecorationProps) => Decoration;
|
|
32
|
+
export declare const createDeletedContentDecoration: ({ change, doc, tr, editorView, nodeViews, }: DeletedContentDecorationProps) => Decoration;
|
|
29
33
|
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type NodeViewConstructor } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
|
+
import type { Schema, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
4
|
+
/**
|
|
5
|
+
* Utilities for working with ProseMirror node views and DOM serialization within the
|
|
6
|
+
* Show Diff editor plugin.
|
|
7
|
+
*
|
|
8
|
+
* This module centralizes:
|
|
9
|
+
* - Access to the editor's `nodeViews` registry (when available on `EditorView`)
|
|
10
|
+
* - Safe attempts to instantiate a node view for a given node, with a blocklist to
|
|
11
|
+
* avoid node types that are known to be problematic in this context (e.g. tables)
|
|
12
|
+
* - Schema-driven serialization of nodes and fragments to DOM via `DOMSerializer`
|
|
13
|
+
*
|
|
14
|
+
* The Show Diff decorations leverage this to either render nodes using their
|
|
15
|
+
* corresponding node view implementation, or fall back to DOM serialization.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Narrowed `EditorView` that exposes the internal `nodeViews` registry.
|
|
19
|
+
* Many editor instances provide this, but it's not part of the base type.
|
|
20
|
+
*/
|
|
21
|
+
export interface EditorViewWithNodeViews extends EditorView {
|
|
22
|
+
nodeViews: Record<string, NodeViewConstructor>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Type guard to detect whether an `EditorView` exposes a `nodeViews` map.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isEditorViewWithNodeViews(view: EditorView): view is EditorViewWithNodeViews;
|
|
28
|
+
/**
|
|
29
|
+
* Encapsulates DOM serialization and node view access/creation.
|
|
30
|
+
*
|
|
31
|
+
* Responsible for:
|
|
32
|
+
* - Creating a `DOMSerializer` from the provided schema
|
|
33
|
+
* - Reading `nodeViews` from an `EditorView` (if present) or using an explicit mapping
|
|
34
|
+
* - Preventing node view creation for blocklisted node types
|
|
35
|
+
*/
|
|
36
|
+
export declare class NodeViewSerializer {
|
|
37
|
+
private serializer;
|
|
38
|
+
private editorView?;
|
|
39
|
+
private nodeViews;
|
|
40
|
+
private nodeViewBlocklist;
|
|
41
|
+
constructor(params: {
|
|
42
|
+
schema: Schema;
|
|
43
|
+
editorView?: EditorView;
|
|
44
|
+
nodeViews?: Record<string, NodeViewConstructor>;
|
|
45
|
+
blocklist?: string[];
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* Attempts to create a node view for the given node.
|
|
49
|
+
*
|
|
50
|
+
* Returns `null` when there is no `EditorView`, no constructor for the node type,
|
|
51
|
+
* or the node type is blocklisted. Otherwise returns the constructed node view instance.
|
|
52
|
+
*/
|
|
53
|
+
tryCreateNodeView(targetNode: PMNode): import("prosemirror-view").NodeView | null;
|
|
54
|
+
/**
|
|
55
|
+
* Serializes a node to a DOM `Node` using the schema's `DOMSerializer`.
|
|
56
|
+
*/
|
|
57
|
+
serializeNode(node: PMNode): Node;
|
|
58
|
+
/**
|
|
59
|
+
* Serializes a fragment to a `DocumentFragment` using the schema's `DOMSerializer`.
|
|
60
|
+
*/
|
|
61
|
+
serializeFragment(fragment: any): DocumentFragment | HTMLElement;
|
|
62
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Change } from 'prosemirror-changeset';
|
|
2
|
+
import { type NodeViewConstructor } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
3
|
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
4
|
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
5
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
4
6
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
5
7
|
/**
|
|
6
8
|
* Inline decoration used for insertions as the content already exists in the document
|
|
@@ -16,6 +18,8 @@ interface DeletedContentDecorationProps {
|
|
|
16
18
|
change: Change;
|
|
17
19
|
doc: PMNode;
|
|
18
20
|
tr: Transaction;
|
|
21
|
+
nodeViews: Record<string, NodeViewConstructor>;
|
|
22
|
+
editorView?: EditorView;
|
|
19
23
|
}
|
|
20
24
|
/**
|
|
21
25
|
* Creates a widget to represent the deleted content in the editor
|
|
@@ -25,5 +29,5 @@ interface DeletedContentDecorationProps {
|
|
|
25
29
|
* @param props.tr The relevant transaction this decoration is being created against
|
|
26
30
|
* @returns Prosemirror widget decoration
|
|
27
31
|
*/
|
|
28
|
-
export declare const createDeletedContentDecoration: ({ change, doc, tr, }: DeletedContentDecorationProps) => Decoration;
|
|
32
|
+
export declare const createDeletedContentDecoration: ({ change, doc, tr, editorView, nodeViews, }: DeletedContentDecorationProps) => Decoration;
|
|
29
33
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-show-diff",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "ShowDiff plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"prosemirror-changeset": "^2.2.1"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@atlaskit/editor-common": "^107.
|
|
40
|
+
"@atlaskit/editor-common": "^107.25.0",
|
|
41
41
|
"react": "^18.2.0"
|
|
42
42
|
},
|
|
43
43
|
"techstack": {
|