@atlaskit/editor-plugin-show-diff 2.0.0 → 2.1.1

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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 2.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`c6b6ef91296ca`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c6b6ef91296ca) -
8
+ [ux] Better support for block nodes for deleted diffs.
9
+ - Updated dependencies
10
+
11
+ ## 2.1.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [`5eadb7f870272`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/5eadb7f870272) -
16
+ [ux] Adds a new plugin configuration to adjust the styling scheme for diffs. By default it will
17
+ use standard, but traditional (for green + red) is also available.
18
+
3
19
  ## 2.0.0
4
20
 
5
21
  ### Patch Changes
@@ -53,7 +53,7 @@ var NodeViewSerializer = exports.NodeViewSerializer = /*#__PURE__*/function () {
53
53
  editorView: params.editorView
54
54
  });
55
55
  }
56
- this.nodeViewBlocklist = new Set((_params$blocklist = params.blocklist) !== null && _params$blocklist !== void 0 ? _params$blocklist : ['table', 'tableRow', 'tableHeader', 'tableCell', 'paragraph']);
56
+ this.nodeViewBlocklist = new Set((_params$blocklist = params.blocklist) !== null && _params$blocklist !== void 0 ? _params$blocklist : ['paragraph']);
57
57
  }
58
58
 
59
59
  /**
@@ -83,20 +83,49 @@ var NodeViewSerializer = exports.NodeViewSerializer = /*#__PURE__*/function () {
83
83
  }, {
84
84
  key: "tryCreateNodeView",
85
85
  value: function tryCreateNodeView(targetNode) {
86
- var _this$nodeViews;
86
+ var _this$nodeViews,
87
+ _this = this;
87
88
  if (!this.editorView) {
88
89
  return null;
89
90
  }
90
91
  var constructor = (_this$nodeViews = this.nodeViews) === null || _this$nodeViews === void 0 ? void 0 : _this$nodeViews[targetNode.type.name];
91
- if (!constructor) {
92
- return null;
93
- }
94
92
  if (this.nodeViewBlocklist.has(targetNode.type.name)) {
95
93
  return null;
96
94
  }
97
- return constructor(targetNode, this.editorView, function () {
98
- return 0;
99
- }, [], {});
95
+ if (!constructor) {
96
+ var _targetNode$type$spec, _targetNode$type$spec2;
97
+ if (targetNode.isInline) {
98
+ return null;
99
+ }
100
+ var toDOMResult = (_targetNode$type$spec = (_targetNode$type$spec2 = targetNode.type.spec).toDOM) === null || _targetNode$type$spec === void 0 ? void 0 : _targetNode$type$spec.call(_targetNode$type$spec2, targetNode);
101
+ if (!toDOMResult) {
102
+ return null;
103
+ }
104
+ var _DOMSerializer$render = _model.DOMSerializer.renderSpec(document, toDOMResult),
105
+ _dom = _DOMSerializer$render.dom,
106
+ _contentDOM = _DOMSerializer$render.contentDOM;
107
+ if (_dom instanceof HTMLElement) {
108
+ if (targetNode.type.name === 'paragraph' && targetNode.children.length === 1) {
109
+ return this.serializeFragment(targetNode.content);
110
+ }
111
+
112
+ // Iteratively populate children
113
+ targetNode.children.forEach(function (child) {
114
+ _contentDOM === null || _contentDOM === void 0 || _contentDOM.append(_this.tryCreateNodeView(child) || _this.serializeNode(child));
115
+ });
116
+ }
117
+ return _dom;
118
+ }
119
+ var _constructor = constructor(targetNode, this.editorView, function () {
120
+ return 0;
121
+ }, [], {}),
122
+ dom = _constructor.dom,
123
+ contentDOM = _constructor.contentDOM;
124
+ // Iteratively populate children
125
+ targetNode.children.forEach(function (child) {
126
+ contentDOM === null || contentDOM === void 0 || contentDOM.append(_this.tryCreateNodeView(child) || _this.serializeNode(child));
127
+ });
128
+ return dom;
100
129
  }
101
130
 
102
131
  /**
@@ -15,7 +15,8 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
15
15
  var calculateDiffDecorations = exports.calculateDiffDecorations = function calculateDiffDecorations(_ref) {
16
16
  var state = _ref.state,
17
17
  pluginState = _ref.pluginState,
18
- nodeViewSerializer = _ref.nodeViewSerializer;
18
+ nodeViewSerializer = _ref.nodeViewSerializer,
19
+ colourScheme = _ref.colourScheme;
19
20
  var originalDoc = pluginState.originalDoc,
20
21
  steps = pluginState.steps;
21
22
  if (!originalDoc || !pluginState.isDisplayingChanges) {
@@ -49,13 +50,14 @@ var calculateDiffDecorations = exports.calculateDiffDecorations = function calcu
49
50
  var decorations = [];
50
51
  changes.forEach(function (change) {
51
52
  if (change.inserted.length > 0) {
52
- decorations.push((0, _decorations.createInlineChangedDecoration)(change));
53
+ decorations.push((0, _decorations.createInlineChangedDecoration)(change, colourScheme));
53
54
  }
54
55
  if (change.deleted.length > 0) {
55
56
  var decoration = (0, _decorations.createDeletedContentDecoration)({
56
57
  change: change,
57
58
  doc: originalDoc,
58
- nodeViewSerializer: nodeViewSerializer
59
+ nodeViewSerializer: nodeViewSerializer,
60
+ colourScheme: colourScheme
59
61
  });
60
62
  if (decoration) {
61
63
  decorations.push(decoration);
@@ -63,7 +65,7 @@ var calculateDiffDecorations = exports.calculateDiffDecorations = function calcu
63
65
  }
64
66
  });
65
67
  (0, _markDecorations.getMarkChangeRanges)(steps).forEach(function (change) {
66
- decorations.push((0, _decorations.createInlineChangedDecoration)(change));
68
+ decorations.push((0, _decorations.createInlineChangedDecoration)(change, colourScheme));
67
69
  });
68
70
  return _view.DecorationSet.empty.add(tr.doc, decorations);
69
71
  };
@@ -6,22 +6,30 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.createInlineChangedDecoration = exports.createDeletedContentDecoration = void 0;
7
7
  var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
8
8
  var _view = require("@atlaskit/editor-prosemirror/view");
9
- var style = (0, _lazyNodeView.convertToInlineCss)({
9
+ var editingStyle = (0, _lazyNodeView.convertToInlineCss)({
10
10
  background: "var(--ds-background-accent-purple-subtlest, #F3F0FF)",
11
11
  textDecoration: 'underline',
12
12
  textDecorationStyle: 'dotted',
13
13
  textDecorationThickness: "var(--ds-space-025, 2px)",
14
14
  textDecorationColor: "var(--ds-border-accent-purple, #8270DB)"
15
15
  });
16
+ var traditionalInsertStyle = (0, _lazyNodeView.convertToInlineCss)({
17
+ background: "var(--ds-background-accent-green-subtlest, #DCFFF1)",
18
+ textDecoration: 'underline',
19
+ textDecorationStyle: 'solid',
20
+ textDecorationThickness: "var(--ds-space-025, 2px)",
21
+ textDecorationColor: "var(--ds-border-accent-green, #22A06B)"
22
+ });
23
+
16
24
  /**
17
25
  * Inline decoration used for insertions as the content already exists in the document
18
26
  *
19
27
  * @param change Changeset "change" containing information about the change content + range
20
28
  * @returns Prosemirror inline decoration
21
29
  */
22
- var createInlineChangedDecoration = exports.createInlineChangedDecoration = function createInlineChangedDecoration(change) {
30
+ var createInlineChangedDecoration = exports.createInlineChangedDecoration = function createInlineChangedDecoration(change, colourScheme) {
23
31
  return _view.Decoration.inline(change.fromB, change.toB, {
24
- style: style,
32
+ style: colourScheme === 'traditional' ? traditionalInsertStyle : editingStyle,
25
33
  'data-testid': 'show-diff-changed-decoration'
26
34
  }, {});
27
35
  };
@@ -40,10 +48,32 @@ var deletedContentStyleUnbounded = (0, _lazyNodeView.convertToInlineCss)({
40
48
  pointerEvents: 'none',
41
49
  zIndex: 1
42
50
  });
51
+ var deletedTraditionalContentStyle = (0, _lazyNodeView.convertToInlineCss)({
52
+ textDecorationColor: "var(--ds-border-accent-red, #E2483D)",
53
+ textDecoration: 'line-through',
54
+ position: 'relative',
55
+ opacity: 1
56
+ });
57
+ var deletedTraditionalContentStyleUnbounded = (0, _lazyNodeView.convertToInlineCss)({
58
+ position: 'absolute',
59
+ top: '50%',
60
+ width: '100%',
61
+ display: 'inline-block',
62
+ borderTop: "1px solid ".concat("var(--ds-border-accent-red, #E2483D)"),
63
+ pointerEvents: 'none',
64
+ zIndex: 1
65
+ });
66
+ var getDeletedContentStyleUnbounded = function getDeletedContentStyleUnbounded(colourScheme) {
67
+ return colourScheme === 'traditional' ? deletedTraditionalContentStyleUnbounded : deletedContentStyleUnbounded;
68
+ };
69
+ var getDeletedContentStyle = function getDeletedContentStyle(colourScheme) {
70
+ return colourScheme === 'traditional' ? deletedTraditionalContentStyle : deletedContentStyle;
71
+ };
43
72
  var createDeletedContentDecoration = exports.createDeletedContentDecoration = function createDeletedContentDecoration(_ref) {
44
73
  var change = _ref.change,
45
74
  doc = _ref.doc,
46
- nodeViewSerializer = _ref.nodeViewSerializer;
75
+ nodeViewSerializer = _ref.nodeViewSerializer,
76
+ colourScheme = _ref.colourScheme;
47
77
  var slice = doc.slice(change.fromA, change.toA);
48
78
  if (slice.content.content.length === 0) {
49
79
  return;
@@ -60,7 +90,7 @@ var createDeletedContentDecoration = exports.createDeletedContentDecoration = fu
60
90
 
61
91
  // For non-table content, use the existing span wrapper approach
62
92
  var dom = document.createElement('span');
63
- dom.setAttribute('style', deletedContentStyle);
93
+ dom.setAttribute('style', getDeletedContentStyle(colourScheme));
64
94
 
65
95
  /*
66
96
  * The thinking is we separate out the fragment we got from doc.slice
@@ -75,7 +105,7 @@ var createDeletedContentDecoration = exports.createDeletedContentDecoration = fu
75
105
  wrapper.style.position = 'relative';
76
106
  wrapper.style.width = 'fit-content';
77
107
  var strikethrough = document.createElement('span');
78
- strikethrough.setAttribute('style', deletedContentStyleUnbounded);
108
+ strikethrough.setAttribute('style', getDeletedContentStyleUnbounded(colourScheme));
79
109
  wrapper.append(strikethrough);
80
110
  return wrapper;
81
111
  };
@@ -90,7 +120,7 @@ var createDeletedContentDecoration = exports.createDeletedContentDecoration = fu
90
120
  targetNode = node;
91
121
  dom.append(lineBreak);
92
122
  var wrapper = createWrapperWithStrikethrough();
93
- wrapper.append(childNodeView.dom);
123
+ wrapper.append(childNodeView);
94
124
  dom.append(wrapper);
95
125
  } else {
96
126
  // Fallback to serializing the individual child node
@@ -113,7 +143,7 @@ var createDeletedContentDecoration = exports.createDeletedContentDecoration = fu
113
143
  if (handleMultipleChildNodes(node)) {
114
144
  return;
115
145
  }
116
- targetNode = node.content.content[0];
146
+ targetNode = node;
117
147
  fallbackSerialization = function fallbackSerialization() {
118
148
  return serializer.serializeFragment(node.content);
119
149
  };
@@ -137,7 +167,7 @@ var createDeletedContentDecoration = exports.createDeletedContentDecoration = fu
137
167
  if (handleMultipleChildNodes(node)) {
138
168
  return;
139
169
  }
140
- targetNode = node.content.content[0] || node;
170
+ targetNode = node;
141
171
  fallbackSerialization = function fallbackSerialization() {
142
172
  return serializer.serializeNode(node);
143
173
  };
@@ -146,9 +176,13 @@ var createDeletedContentDecoration = exports.createDeletedContentDecoration = fu
146
176
  // Try to create node view, fallback to serialization
147
177
  var nodeView = serializer.tryCreateNodeView(targetNode);
148
178
  if (nodeView) {
149
- var wrapper = createWrapperWithStrikethrough();
150
- wrapper.append(nodeView.dom);
151
- dom.append(wrapper);
179
+ if (targetNode.isInline) {
180
+ var wrapper = createWrapperWithStrikethrough();
181
+ wrapper.append(nodeView);
182
+ dom.append(wrapper);
183
+ } else {
184
+ dom.append(nodeView);
185
+ }
152
186
  } else if (nodeViewSerializer.getFilteredNodeViewBlocklist(['paragraph', 'tableRow']).has(targetNode.type.name)) {
153
187
  // Skip the case where the node is a paragraph or table row that way it can still be rendered and delete the entire table
154
188
  return;
@@ -48,7 +48,8 @@ var createPlugin = exports.createPlugin = function createPlugin(config) {
48
48
  var decorations = (0, _calculateDiffDecorations.calculateDiffDecorations)({
49
49
  state: newState,
50
50
  pluginState: newPluginState,
51
- nodeViewSerializer: nodeViewSerializer
51
+ nodeViewSerializer: nodeViewSerializer,
52
+ colourScheme: config === null || config === void 0 ? void 0 : config.colourScheme
52
53
  });
53
54
  // Update the decorations
54
55
  newPluginState.decorations = decorations;
@@ -43,7 +43,7 @@ export class NodeViewSerializer {
43
43
  editorView: params.editorView
44
44
  });
45
45
  }
46
- this.nodeViewBlocklist = new Set((_params$blocklist = params.blocklist) !== null && _params$blocklist !== void 0 ? _params$blocklist : ['table', 'tableRow', 'tableHeader', 'tableCell', 'paragraph']);
46
+ this.nodeViewBlocklist = new Set((_params$blocklist = params.blocklist) !== null && _params$blocklist !== void 0 ? _params$blocklist : ['paragraph']);
47
47
  }
48
48
 
49
49
  /**
@@ -74,13 +74,43 @@ export class NodeViewSerializer {
74
74
  return null;
75
75
  }
76
76
  const constructor = (_this$nodeViews = this.nodeViews) === null || _this$nodeViews === void 0 ? void 0 : _this$nodeViews[targetNode.type.name];
77
- if (!constructor) {
78
- return null;
79
- }
80
77
  if (this.nodeViewBlocklist.has(targetNode.type.name)) {
81
78
  return null;
82
79
  }
83
- return constructor(targetNode, this.editorView, () => 0, [], {});
80
+ if (!constructor) {
81
+ var _targetNode$type$spec, _targetNode$type$spec2;
82
+ if (targetNode.isInline) {
83
+ return null;
84
+ }
85
+ const toDOMResult = (_targetNode$type$spec = (_targetNode$type$spec2 = targetNode.type.spec).toDOM) === null || _targetNode$type$spec === void 0 ? void 0 : _targetNode$type$spec.call(_targetNode$type$spec2, targetNode);
86
+ if (!toDOMResult) {
87
+ return null;
88
+ }
89
+ const {
90
+ dom,
91
+ contentDOM
92
+ } = DOMSerializer.renderSpec(document, toDOMResult);
93
+ if (dom instanceof HTMLElement) {
94
+ if (targetNode.type.name === 'paragraph' && targetNode.children.length === 1) {
95
+ return this.serializeFragment(targetNode.content);
96
+ }
97
+
98
+ // Iteratively populate children
99
+ targetNode.children.forEach(child => {
100
+ contentDOM === null || contentDOM === void 0 ? void 0 : contentDOM.append(this.tryCreateNodeView(child) || this.serializeNode(child));
101
+ });
102
+ }
103
+ return dom;
104
+ }
105
+ const {
106
+ dom,
107
+ contentDOM
108
+ } = constructor(targetNode, this.editorView, () => 0, [], {});
109
+ // Iteratively populate children
110
+ targetNode.children.forEach(child => {
111
+ contentDOM === null || contentDOM === void 0 ? void 0 : contentDOM.append(this.tryCreateNodeView(child) || this.serializeNode(child));
112
+ });
113
+ return dom;
84
114
  }
85
115
 
86
116
  /**
@@ -6,7 +6,8 @@ import { getMarkChangeRanges } from './markDecorations';
6
6
  export const calculateDiffDecorations = ({
7
7
  state,
8
8
  pluginState,
9
- nodeViewSerializer
9
+ nodeViewSerializer,
10
+ colourScheme
10
11
  }) => {
11
12
  const {
12
13
  originalDoc,
@@ -36,13 +37,14 @@ export const calculateDiffDecorations = ({
36
37
  const decorations = [];
37
38
  changes.forEach(change => {
38
39
  if (change.inserted.length > 0) {
39
- decorations.push(createInlineChangedDecoration(change));
40
+ decorations.push(createInlineChangedDecoration(change, colourScheme));
40
41
  }
41
42
  if (change.deleted.length > 0) {
42
43
  const decoration = createDeletedContentDecoration({
43
44
  change,
44
45
  doc: originalDoc,
45
- nodeViewSerializer
46
+ nodeViewSerializer,
47
+ colourScheme
46
48
  });
47
49
  if (decoration) {
48
50
  decorations.push(decoration);
@@ -50,7 +52,7 @@ export const calculateDiffDecorations = ({
50
52
  }
51
53
  });
52
54
  getMarkChangeRanges(steps).forEach(change => {
53
- decorations.push(createInlineChangedDecoration(change));
55
+ decorations.push(createInlineChangedDecoration(change, colourScheme));
54
56
  });
55
57
  return DecorationSet.empty.add(tr.doc, decorations);
56
58
  };
@@ -1,20 +1,28 @@
1
1
  import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
2
2
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
3
- const style = convertToInlineCss({
3
+ const editingStyle = convertToInlineCss({
4
4
  background: "var(--ds-background-accent-purple-subtlest, #F3F0FF)",
5
5
  textDecoration: 'underline',
6
6
  textDecorationStyle: 'dotted',
7
7
  textDecorationThickness: "var(--ds-space-025, 2px)",
8
8
  textDecorationColor: "var(--ds-border-accent-purple, #8270DB)"
9
9
  });
10
+ const traditionalInsertStyle = convertToInlineCss({
11
+ background: "var(--ds-background-accent-green-subtlest, #DCFFF1)",
12
+ textDecoration: 'underline',
13
+ textDecorationStyle: 'solid',
14
+ textDecorationThickness: "var(--ds-space-025, 2px)",
15
+ textDecorationColor: "var(--ds-border-accent-green, #22A06B)"
16
+ });
17
+
10
18
  /**
11
19
  * Inline decoration used for insertions as the content already exists in the document
12
20
  *
13
21
  * @param change Changeset "change" containing information about the change content + range
14
22
  * @returns Prosemirror inline decoration
15
23
  */
16
- export const createInlineChangedDecoration = change => Decoration.inline(change.fromB, change.toB, {
17
- style,
24
+ export const createInlineChangedDecoration = (change, colourScheme) => Decoration.inline(change.fromB, change.toB, {
25
+ style: colourScheme === 'traditional' ? traditionalInsertStyle : editingStyle,
18
26
  'data-testid': 'show-diff-changed-decoration'
19
27
  }, {});
20
28
  const deletedContentStyle = convertToInlineCss({
@@ -32,10 +40,28 @@ const deletedContentStyleUnbounded = convertToInlineCss({
32
40
  pointerEvents: 'none',
33
41
  zIndex: 1
34
42
  });
43
+ const deletedTraditionalContentStyle = convertToInlineCss({
44
+ textDecorationColor: "var(--ds-border-accent-red, #E2483D)",
45
+ textDecoration: 'line-through',
46
+ position: 'relative',
47
+ opacity: 1
48
+ });
49
+ const deletedTraditionalContentStyleUnbounded = convertToInlineCss({
50
+ position: 'absolute',
51
+ top: '50%',
52
+ width: '100%',
53
+ display: 'inline-block',
54
+ borderTop: `1px solid ${"var(--ds-border-accent-red, #E2483D)"}`,
55
+ pointerEvents: 'none',
56
+ zIndex: 1
57
+ });
58
+ const getDeletedContentStyleUnbounded = colourScheme => colourScheme === 'traditional' ? deletedTraditionalContentStyleUnbounded : deletedContentStyleUnbounded;
59
+ const getDeletedContentStyle = colourScheme => colourScheme === 'traditional' ? deletedTraditionalContentStyle : deletedContentStyle;
35
60
  export const createDeletedContentDecoration = ({
36
61
  change,
37
62
  doc,
38
- nodeViewSerializer
63
+ nodeViewSerializer,
64
+ colourScheme
39
65
  }) => {
40
66
  const slice = doc.slice(change.fromA, change.toA);
41
67
  if (slice.content.content.length === 0) {
@@ -49,7 +75,7 @@ export const createDeletedContentDecoration = ({
49
75
 
50
76
  // For non-table content, use the existing span wrapper approach
51
77
  const dom = document.createElement('span');
52
- dom.setAttribute('style', deletedContentStyle);
78
+ dom.setAttribute('style', getDeletedContentStyle(colourScheme));
53
79
 
54
80
  /*
55
81
  * The thinking is we separate out the fragment we got from doc.slice
@@ -64,7 +90,7 @@ export const createDeletedContentDecoration = ({
64
90
  wrapper.style.position = 'relative';
65
91
  wrapper.style.width = 'fit-content';
66
92
  const strikethrough = document.createElement('span');
67
- strikethrough.setAttribute('style', deletedContentStyleUnbounded);
93
+ strikethrough.setAttribute('style', getDeletedContentStyleUnbounded(colourScheme));
68
94
  wrapper.append(strikethrough);
69
95
  return wrapper;
70
96
  };
@@ -79,7 +105,7 @@ export const createDeletedContentDecoration = ({
79
105
  targetNode = node;
80
106
  dom.append(lineBreak);
81
107
  const wrapper = createWrapperWithStrikethrough();
82
- wrapper.append(childNodeView.dom);
108
+ wrapper.append(childNodeView);
83
109
  dom.append(wrapper);
84
110
  } else {
85
111
  // Fallback to serializing the individual child node
@@ -102,7 +128,7 @@ export const createDeletedContentDecoration = ({
102
128
  if (handleMultipleChildNodes(node)) {
103
129
  return;
104
130
  }
105
- targetNode = node.content.content[0];
131
+ targetNode = node;
106
132
  fallbackSerialization = () => serializer.serializeFragment(node.content);
107
133
  } else if (isLast && slice.content.childCount === 2) {
108
134
  if (handleMultipleChildNodes(node)) {
@@ -124,16 +150,20 @@ export const createDeletedContentDecoration = ({
124
150
  if (handleMultipleChildNodes(node)) {
125
151
  return;
126
152
  }
127
- targetNode = node.content.content[0] || node;
153
+ targetNode = node;
128
154
  fallbackSerialization = () => serializer.serializeNode(node);
129
155
  }
130
156
 
131
157
  // Try to create node view, fallback to serialization
132
158
  const nodeView = serializer.tryCreateNodeView(targetNode);
133
159
  if (nodeView) {
134
- const wrapper = createWrapperWithStrikethrough();
135
- wrapper.append(nodeView.dom);
136
- dom.append(wrapper);
160
+ if (targetNode.isInline) {
161
+ const wrapper = createWrapperWithStrikethrough();
162
+ wrapper.append(nodeView);
163
+ dom.append(wrapper);
164
+ } else {
165
+ dom.append(nodeView);
166
+ }
137
167
  } else if (nodeViewSerializer.getFilteredNodeViewBlocklist(['paragraph', 'tableRow']).has(targetNode.type.name)) {
138
168
  // Skip the case where the node is a paragraph or table row that way it can still be rendered and delete the entire table
139
169
  return;
@@ -40,7 +40,8 @@ export const createPlugin = config => {
40
40
  const decorations = calculateDiffDecorations({
41
41
  state: newState,
42
42
  pluginState: newPluginState,
43
- nodeViewSerializer
43
+ nodeViewSerializer,
44
+ colourScheme: config === null || config === void 0 ? void 0 : config.colourScheme
44
45
  });
45
46
  // Update the decorations
46
47
  newPluginState.decorations = decorations;
@@ -46,7 +46,7 @@ export var NodeViewSerializer = /*#__PURE__*/function () {
46
46
  editorView: params.editorView
47
47
  });
48
48
  }
49
- this.nodeViewBlocklist = new Set((_params$blocklist = params.blocklist) !== null && _params$blocklist !== void 0 ? _params$blocklist : ['table', 'tableRow', 'tableHeader', 'tableCell', 'paragraph']);
49
+ this.nodeViewBlocklist = new Set((_params$blocklist = params.blocklist) !== null && _params$blocklist !== void 0 ? _params$blocklist : ['paragraph']);
50
50
  }
51
51
 
52
52
  /**
@@ -76,20 +76,49 @@ export var NodeViewSerializer = /*#__PURE__*/function () {
76
76
  }, {
77
77
  key: "tryCreateNodeView",
78
78
  value: function tryCreateNodeView(targetNode) {
79
- var _this$nodeViews;
79
+ var _this$nodeViews,
80
+ _this = this;
80
81
  if (!this.editorView) {
81
82
  return null;
82
83
  }
83
84
  var constructor = (_this$nodeViews = this.nodeViews) === null || _this$nodeViews === void 0 ? void 0 : _this$nodeViews[targetNode.type.name];
84
- if (!constructor) {
85
- return null;
86
- }
87
85
  if (this.nodeViewBlocklist.has(targetNode.type.name)) {
88
86
  return null;
89
87
  }
90
- return constructor(targetNode, this.editorView, function () {
91
- return 0;
92
- }, [], {});
88
+ if (!constructor) {
89
+ var _targetNode$type$spec, _targetNode$type$spec2;
90
+ if (targetNode.isInline) {
91
+ return null;
92
+ }
93
+ var toDOMResult = (_targetNode$type$spec = (_targetNode$type$spec2 = targetNode.type.spec).toDOM) === null || _targetNode$type$spec === void 0 ? void 0 : _targetNode$type$spec.call(_targetNode$type$spec2, targetNode);
94
+ if (!toDOMResult) {
95
+ return null;
96
+ }
97
+ var _DOMSerializer$render = DOMSerializer.renderSpec(document, toDOMResult),
98
+ _dom = _DOMSerializer$render.dom,
99
+ _contentDOM = _DOMSerializer$render.contentDOM;
100
+ if (_dom instanceof HTMLElement) {
101
+ if (targetNode.type.name === 'paragraph' && targetNode.children.length === 1) {
102
+ return this.serializeFragment(targetNode.content);
103
+ }
104
+
105
+ // Iteratively populate children
106
+ targetNode.children.forEach(function (child) {
107
+ _contentDOM === null || _contentDOM === void 0 || _contentDOM.append(_this.tryCreateNodeView(child) || _this.serializeNode(child));
108
+ });
109
+ }
110
+ return _dom;
111
+ }
112
+ var _constructor = constructor(targetNode, this.editorView, function () {
113
+ return 0;
114
+ }, [], {}),
115
+ dom = _constructor.dom,
116
+ contentDOM = _constructor.contentDOM;
117
+ // Iteratively populate children
118
+ targetNode.children.forEach(function (child) {
119
+ contentDOM === null || contentDOM === void 0 || contentDOM.append(_this.tryCreateNodeView(child) || _this.serializeNode(child));
120
+ });
121
+ return dom;
93
122
  }
94
123
 
95
124
  /**
@@ -9,7 +9,8 @@ import { getMarkChangeRanges } from './markDecorations';
9
9
  export var calculateDiffDecorations = function calculateDiffDecorations(_ref) {
10
10
  var state = _ref.state,
11
11
  pluginState = _ref.pluginState,
12
- nodeViewSerializer = _ref.nodeViewSerializer;
12
+ nodeViewSerializer = _ref.nodeViewSerializer,
13
+ colourScheme = _ref.colourScheme;
13
14
  var originalDoc = pluginState.originalDoc,
14
15
  steps = pluginState.steps;
15
16
  if (!originalDoc || !pluginState.isDisplayingChanges) {
@@ -43,13 +44,14 @@ export var calculateDiffDecorations = function calculateDiffDecorations(_ref) {
43
44
  var decorations = [];
44
45
  changes.forEach(function (change) {
45
46
  if (change.inserted.length > 0) {
46
- decorations.push(createInlineChangedDecoration(change));
47
+ decorations.push(createInlineChangedDecoration(change, colourScheme));
47
48
  }
48
49
  if (change.deleted.length > 0) {
49
50
  var decoration = createDeletedContentDecoration({
50
51
  change: change,
51
52
  doc: originalDoc,
52
- nodeViewSerializer: nodeViewSerializer
53
+ nodeViewSerializer: nodeViewSerializer,
54
+ colourScheme: colourScheme
53
55
  });
54
56
  if (decoration) {
55
57
  decorations.push(decoration);
@@ -57,7 +59,7 @@ export var calculateDiffDecorations = function calculateDiffDecorations(_ref) {
57
59
  }
58
60
  });
59
61
  getMarkChangeRanges(steps).forEach(function (change) {
60
- decorations.push(createInlineChangedDecoration(change));
62
+ decorations.push(createInlineChangedDecoration(change, colourScheme));
61
63
  });
62
64
  return DecorationSet.empty.add(tr.doc, decorations);
63
65
  };
@@ -1,21 +1,29 @@
1
1
  import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
2
2
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
3
- var style = convertToInlineCss({
3
+ var editingStyle = convertToInlineCss({
4
4
  background: "var(--ds-background-accent-purple-subtlest, #F3F0FF)",
5
5
  textDecoration: 'underline',
6
6
  textDecorationStyle: 'dotted',
7
7
  textDecorationThickness: "var(--ds-space-025, 2px)",
8
8
  textDecorationColor: "var(--ds-border-accent-purple, #8270DB)"
9
9
  });
10
+ var traditionalInsertStyle = convertToInlineCss({
11
+ background: "var(--ds-background-accent-green-subtlest, #DCFFF1)",
12
+ textDecoration: 'underline',
13
+ textDecorationStyle: 'solid',
14
+ textDecorationThickness: "var(--ds-space-025, 2px)",
15
+ textDecorationColor: "var(--ds-border-accent-green, #22A06B)"
16
+ });
17
+
10
18
  /**
11
19
  * Inline decoration used for insertions as the content already exists in the document
12
20
  *
13
21
  * @param change Changeset "change" containing information about the change content + range
14
22
  * @returns Prosemirror inline decoration
15
23
  */
16
- export var createInlineChangedDecoration = function createInlineChangedDecoration(change) {
24
+ export var createInlineChangedDecoration = function createInlineChangedDecoration(change, colourScheme) {
17
25
  return Decoration.inline(change.fromB, change.toB, {
18
- style: style,
26
+ style: colourScheme === 'traditional' ? traditionalInsertStyle : editingStyle,
19
27
  'data-testid': 'show-diff-changed-decoration'
20
28
  }, {});
21
29
  };
@@ -34,10 +42,32 @@ var deletedContentStyleUnbounded = convertToInlineCss({
34
42
  pointerEvents: 'none',
35
43
  zIndex: 1
36
44
  });
45
+ var deletedTraditionalContentStyle = convertToInlineCss({
46
+ textDecorationColor: "var(--ds-border-accent-red, #E2483D)",
47
+ textDecoration: 'line-through',
48
+ position: 'relative',
49
+ opacity: 1
50
+ });
51
+ var deletedTraditionalContentStyleUnbounded = convertToInlineCss({
52
+ position: 'absolute',
53
+ top: '50%',
54
+ width: '100%',
55
+ display: 'inline-block',
56
+ borderTop: "1px solid ".concat("var(--ds-border-accent-red, #E2483D)"),
57
+ pointerEvents: 'none',
58
+ zIndex: 1
59
+ });
60
+ var getDeletedContentStyleUnbounded = function getDeletedContentStyleUnbounded(colourScheme) {
61
+ return colourScheme === 'traditional' ? deletedTraditionalContentStyleUnbounded : deletedContentStyleUnbounded;
62
+ };
63
+ var getDeletedContentStyle = function getDeletedContentStyle(colourScheme) {
64
+ return colourScheme === 'traditional' ? deletedTraditionalContentStyle : deletedContentStyle;
65
+ };
37
66
  export var createDeletedContentDecoration = function createDeletedContentDecoration(_ref) {
38
67
  var change = _ref.change,
39
68
  doc = _ref.doc,
40
- nodeViewSerializer = _ref.nodeViewSerializer;
69
+ nodeViewSerializer = _ref.nodeViewSerializer,
70
+ colourScheme = _ref.colourScheme;
41
71
  var slice = doc.slice(change.fromA, change.toA);
42
72
  if (slice.content.content.length === 0) {
43
73
  return;
@@ -54,7 +84,7 @@ export var createDeletedContentDecoration = function createDeletedContentDecorat
54
84
 
55
85
  // For non-table content, use the existing span wrapper approach
56
86
  var dom = document.createElement('span');
57
- dom.setAttribute('style', deletedContentStyle);
87
+ dom.setAttribute('style', getDeletedContentStyle(colourScheme));
58
88
 
59
89
  /*
60
90
  * The thinking is we separate out the fragment we got from doc.slice
@@ -69,7 +99,7 @@ export var createDeletedContentDecoration = function createDeletedContentDecorat
69
99
  wrapper.style.position = 'relative';
70
100
  wrapper.style.width = 'fit-content';
71
101
  var strikethrough = document.createElement('span');
72
- strikethrough.setAttribute('style', deletedContentStyleUnbounded);
102
+ strikethrough.setAttribute('style', getDeletedContentStyleUnbounded(colourScheme));
73
103
  wrapper.append(strikethrough);
74
104
  return wrapper;
75
105
  };
@@ -84,7 +114,7 @@ export var createDeletedContentDecoration = function createDeletedContentDecorat
84
114
  targetNode = node;
85
115
  dom.append(lineBreak);
86
116
  var wrapper = createWrapperWithStrikethrough();
87
- wrapper.append(childNodeView.dom);
117
+ wrapper.append(childNodeView);
88
118
  dom.append(wrapper);
89
119
  } else {
90
120
  // Fallback to serializing the individual child node
@@ -107,7 +137,7 @@ export var createDeletedContentDecoration = function createDeletedContentDecorat
107
137
  if (handleMultipleChildNodes(node)) {
108
138
  return;
109
139
  }
110
- targetNode = node.content.content[0];
140
+ targetNode = node;
111
141
  fallbackSerialization = function fallbackSerialization() {
112
142
  return serializer.serializeFragment(node.content);
113
143
  };
@@ -131,7 +161,7 @@ export var createDeletedContentDecoration = function createDeletedContentDecorat
131
161
  if (handleMultipleChildNodes(node)) {
132
162
  return;
133
163
  }
134
- targetNode = node.content.content[0] || node;
164
+ targetNode = node;
135
165
  fallbackSerialization = function fallbackSerialization() {
136
166
  return serializer.serializeNode(node);
137
167
  };
@@ -140,9 +170,13 @@ export var createDeletedContentDecoration = function createDeletedContentDecorat
140
170
  // Try to create node view, fallback to serialization
141
171
  var nodeView = serializer.tryCreateNodeView(targetNode);
142
172
  if (nodeView) {
143
- var wrapper = createWrapperWithStrikethrough();
144
- wrapper.append(nodeView.dom);
145
- dom.append(wrapper);
173
+ if (targetNode.isInline) {
174
+ var wrapper = createWrapperWithStrikethrough();
175
+ wrapper.append(nodeView);
176
+ dom.append(wrapper);
177
+ } else {
178
+ dom.append(nodeView);
179
+ }
146
180
  } else if (nodeViewSerializer.getFilteredNodeViewBlocklist(['paragraph', 'tableRow']).has(targetNode.type.name)) {
147
181
  // Skip the case where the node is a paragraph or table row that way it can still be rendered and delete the entire table
148
182
  return;
@@ -41,7 +41,8 @@ export var createPlugin = function createPlugin(config) {
41
41
  var decorations = calculateDiffDecorations({
42
42
  state: newState,
43
43
  pluginState: newPluginState,
44
- nodeViewSerializer: nodeViewSerializer
44
+ nodeViewSerializer: nodeViewSerializer,
45
+ colourScheme: config === null || config === void 0 ? void 0 : config.colourScheme
45
46
  });
46
47
  // Update the decorations
47
48
  newPluginState.decorations = decorations;
@@ -55,7 +55,7 @@ export declare class NodeViewSerializer {
55
55
  * Returns `null` when there is no `EditorView`, no constructor for the node type,
56
56
  * or the node type is blocklisted. Otherwise returns the constructed node view instance.
57
57
  */
58
- tryCreateNodeView(targetNode: PMNode): import("prosemirror-view").NodeView | null;
58
+ tryCreateNodeView(targetNode: PMNode): Node | null;
59
59
  /**
60
60
  * Serializes a node to a DOM `Node` using the schema's `DOMSerializer`.
61
61
  */
@@ -3,7 +3,8 @@ import { type EditorState } from '@atlaskit/editor-prosemirror/state';
3
3
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
4
4
  import type { ShowDiffPluginState } from './main';
5
5
  import type { NodeViewSerializer } from './NodeViewSerializer';
6
- export declare const calculateDiffDecorations: ({ state, pluginState, nodeViewSerializer, }: {
6
+ export declare const calculateDiffDecorations: ({ state, pluginState, nodeViewSerializer, colourScheme, }: {
7
+ colourScheme?: "standard" | "traditional";
7
8
  nodeViewSerializer: NodeViewSerializer;
8
9
  pluginState: Omit<ShowDiffPluginState, "decorations">;
9
10
  state: EditorState;
@@ -11,11 +11,12 @@ import type { NodeViewSerializer } from './NodeViewSerializer';
11
11
  export declare const createInlineChangedDecoration: (change: {
12
12
  fromB: number;
13
13
  toB: number;
14
- }) => Decoration;
14
+ }, colourScheme?: "standard" | "traditional") => Decoration;
15
15
  interface DeletedContentDecorationProps {
16
16
  change: Change;
17
+ colourScheme?: 'standard' | 'traditional';
17
18
  doc: PMNode;
18
19
  nodeViewSerializer: NodeViewSerializer;
19
20
  }
20
- export declare const createDeletedContentDecoration: ({ change, doc, nodeViewSerializer, }: DeletedContentDecorationProps) => Decoration | undefined;
21
+ export declare const createDeletedContentDecoration: ({ change, doc, nodeViewSerializer, colourScheme, }: DeletedContentDecorationProps) => Decoration | undefined;
21
22
  export {};
@@ -4,6 +4,12 @@ import type { JSONDocNode } from '@atlaskit/editor-json-transformer';
4
4
  import type { Node } from '@atlaskit/editor-prosemirror/model';
5
5
  import type { Step } from '@atlaskit/editor-prosemirror/transform';
6
6
  export type DiffParams = {
7
+ /**
8
+ * Color scheme to use for displaying diffs.
9
+ * 'standard' (default) uses purple for highlighting changes
10
+ * 'traditional' uses green for additions and red for deletions
11
+ */
12
+ colourScheme?: 'standard' | 'traditional';
7
13
  originalDoc: JSONDocNode;
8
14
  /**
9
15
  * Prosemirror steps. This is used to calculate and show the diff in the editor
@@ -55,7 +55,7 @@ export declare class NodeViewSerializer {
55
55
  * Returns `null` when there is no `EditorView`, no constructor for the node type,
56
56
  * or the node type is blocklisted. Otherwise returns the constructed node view instance.
57
57
  */
58
- tryCreateNodeView(targetNode: PMNode): import("prosemirror-view").NodeView | null;
58
+ tryCreateNodeView(targetNode: PMNode): Node | null;
59
59
  /**
60
60
  * Serializes a node to a DOM `Node` using the schema's `DOMSerializer`.
61
61
  */
@@ -3,7 +3,8 @@ import { type EditorState } from '@atlaskit/editor-prosemirror/state';
3
3
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
4
4
  import type { ShowDiffPluginState } from './main';
5
5
  import type { NodeViewSerializer } from './NodeViewSerializer';
6
- export declare const calculateDiffDecorations: ({ state, pluginState, nodeViewSerializer, }: {
6
+ export declare const calculateDiffDecorations: ({ state, pluginState, nodeViewSerializer, colourScheme, }: {
7
+ colourScheme?: "standard" | "traditional";
7
8
  nodeViewSerializer: NodeViewSerializer;
8
9
  pluginState: Omit<ShowDiffPluginState, "decorations">;
9
10
  state: EditorState;
@@ -11,11 +11,12 @@ import type { NodeViewSerializer } from './NodeViewSerializer';
11
11
  export declare const createInlineChangedDecoration: (change: {
12
12
  fromB: number;
13
13
  toB: number;
14
- }) => Decoration;
14
+ }, colourScheme?: "standard" | "traditional") => Decoration;
15
15
  interface DeletedContentDecorationProps {
16
16
  change: Change;
17
+ colourScheme?: 'standard' | 'traditional';
17
18
  doc: PMNode;
18
19
  nodeViewSerializer: NodeViewSerializer;
19
20
  }
20
- export declare const createDeletedContentDecoration: ({ change, doc, nodeViewSerializer, }: DeletedContentDecorationProps) => Decoration | undefined;
21
+ export declare const createDeletedContentDecoration: ({ change, doc, nodeViewSerializer, colourScheme, }: DeletedContentDecorationProps) => Decoration | undefined;
21
22
  export {};
@@ -4,6 +4,12 @@ import type { JSONDocNode } from '@atlaskit/editor-json-transformer';
4
4
  import type { Node } from '@atlaskit/editor-prosemirror/model';
5
5
  import type { Step } from '@atlaskit/editor-prosemirror/transform';
6
6
  export type DiffParams = {
7
+ /**
8
+ * Color scheme to use for displaying diffs.
9
+ * 'standard' (default) uses purple for highlighting changes
10
+ * 'traditional' uses green for additions and red for deletions
11
+ */
12
+ colourScheme?: 'standard' | 'traditional';
7
13
  originalDoc: JSONDocNode;
8
14
  /**
9
15
  * Prosemirror steps. This is used to calculate and show the diff in the editor
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -34,7 +34,7 @@
34
34
  "prosemirror-changeset": "^2.2.1"
35
35
  },
36
36
  "peerDependencies": {
37
- "@atlaskit/editor-common": "^109.0.0",
37
+ "@atlaskit/editor-common": "^109.7.0",
38
38
  "react": "^18.2.0"
39
39
  },
40
40
  "techstack": {