@atlaskit/editor-plugin-show-diff 3.2.0 → 3.2.2
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 +14 -0
- package/afm-cc/tsconfig.json +3 -0
- package/afm-jira/tsconfig.json +3 -0
- package/afm-products/tsconfig.json +3 -0
- package/dist/cjs/pm-plugins/attributeDecorations.js +26 -2
- package/dist/cjs/pm-plugins/calculateDiffDecorations.js +9 -7
- package/dist/cjs/pm-plugins/decorations.js +18 -4
- package/dist/cjs/pm-plugins/deletedRowsHandler.js +214 -0
- package/dist/es2019/pm-plugins/attributeDecorations.js +25 -1
- package/dist/es2019/pm-plugins/calculateDiffDecorations.js +11 -9
- package/dist/es2019/pm-plugins/decorations.js +14 -3
- package/dist/es2019/pm-plugins/deletedRowsHandler.js +185 -0
- package/dist/esm/pm-plugins/attributeDecorations.js +25 -1
- package/dist/esm/pm-plugins/calculateDiffDecorations.js +11 -9
- package/dist/esm/pm-plugins/decorations.js +18 -4
- package/dist/esm/pm-plugins/deletedRowsHandler.js +207 -0
- package/dist/types/pm-plugins/attributeDecorations.d.ts +9 -0
- package/dist/types/pm-plugins/decorations.d.ts +2 -2
- package/dist/types/pm-plugins/deletedRowsHandler.d.ts +30 -0
- package/dist/types-ts4.5/pm-plugins/attributeDecorations.d.ts +9 -0
- package/dist/types-ts4.5/pm-plugins/decorations.d.ts +2 -2
- package/dist/types-ts4.5/pm-plugins/deletedRowsHandler.d.ts +30 -0
- package/package.json +7 -3
- package/afm-post-office/tsconfig.json +0 -30
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-show-diff
|
|
2
2
|
|
|
3
|
+
## 3.2.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`1c0d87f570c52`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1c0d87f570c52) -
|
|
8
|
+
[ux] Update attributes to ignore attr steps that do not affect the document
|
|
9
|
+
|
|
10
|
+
## 3.2.1
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- [`da2782d8dc1e7`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/da2782d8dc1e7) -
|
|
15
|
+
Support table row diff displaying in the editor
|
|
16
|
+
|
|
3
17
|
## 3.2.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/afm-cc/tsconfig.json
CHANGED
package/afm-jira/tsconfig.json
CHANGED
|
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.getAttrChangeRanges = void 0;
|
|
7
|
+
exports.stepIsValidAttrChange = exports.getAttrChangeRanges = void 0;
|
|
8
8
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
9
|
var _steps = require("@atlaskit/adf-schema/steps");
|
|
10
10
|
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
@@ -16,7 +16,7 @@ var filterUndefined = function filterUndefined(x) {
|
|
|
16
16
|
var allowedAttrs = ['id', 'collection', 'url'];
|
|
17
17
|
var getAttrChangeRanges = exports.getAttrChangeRanges = function getAttrChangeRanges(doc, steps) {
|
|
18
18
|
return steps.map(function (step) {
|
|
19
|
-
if (step instanceof _transform.AttrStep && allowedAttrs.includes(step.attr) || step instanceof _steps.SetAttrsStep && (0, _toConsumableArray2.default)(Object.keys(step.attrs)).some(function (v) {
|
|
19
|
+
if (step instanceof _transform.AttrStep && allowedAttrs.includes(step.attr) || step instanceof _steps.SetAttrsStep && step.attrs && (0, _toConsumableArray2.default)(Object.keys(step.attrs)).some(function (v) {
|
|
20
20
|
return allowedAttrs.includes(v);
|
|
21
21
|
})) {
|
|
22
22
|
var $pos = doc.resolve(step.pos);
|
|
@@ -30,4 +30,28 @@ var getAttrChangeRanges = exports.getAttrChangeRanges = function getAttrChangeRa
|
|
|
30
30
|
}
|
|
31
31
|
return undefined;
|
|
32
32
|
}).filter(filterUndefined);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if the step was a valid attr change and affected the doc
|
|
37
|
+
*
|
|
38
|
+
* @param step Attr step to test
|
|
39
|
+
* @param beforeDoc Doc before the step
|
|
40
|
+
* @param afterDoc Doc after the step
|
|
41
|
+
* @returns Boolean if the change should show a decoration
|
|
42
|
+
*/
|
|
43
|
+
var stepIsValidAttrChange = exports.stepIsValidAttrChange = function stepIsValidAttrChange(step, beforeDoc, afterDoc) {
|
|
44
|
+
try {
|
|
45
|
+
if (step instanceof _transform.AttrStep || step instanceof _steps.SetAttrsStep) {
|
|
46
|
+
var attrStepAfter = afterDoc.nodeAt(step.pos);
|
|
47
|
+
var attrStepBefore = beforeDoc.nodeAt(step.pos);
|
|
48
|
+
// The change affected the document
|
|
49
|
+
if (attrStepAfter && attrStepBefore && !attrStepAfter.eq(attrStepBefore)) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
33
57
|
};
|
|
@@ -13,7 +13,6 @@ var _memoizeOne = _interopRequireDefault(require("memoize-one"));
|
|
|
13
13
|
var _prosemirrorChangeset = require("prosemirror-changeset");
|
|
14
14
|
var _steps = require("@atlaskit/adf-schema/steps");
|
|
15
15
|
var _document = require("@atlaskit/editor-common/utils/document");
|
|
16
|
-
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
17
16
|
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
18
17
|
var _attributeDecorations = require("./attributeDecorations");
|
|
19
18
|
var _decorations = require("./decorations");
|
|
@@ -76,7 +75,7 @@ function simplifySteps(steps) {
|
|
|
76
75
|
return steps
|
|
77
76
|
// Remove steps that don't affect document structure or content
|
|
78
77
|
.filter(function (step) {
|
|
79
|
-
return !(step instanceof _steps.AnalyticsStep
|
|
78
|
+
return !(step instanceof _steps.AnalyticsStep);
|
|
80
79
|
})
|
|
81
80
|
// Merge consecutive steps where possible
|
|
82
81
|
.reduce(function (acc, step) {
|
|
@@ -105,7 +104,8 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref)
|
|
|
105
104
|
}
|
|
106
105
|
var tr = state.tr;
|
|
107
106
|
var steppedDoc = originalDoc;
|
|
108
|
-
var
|
|
107
|
+
var attrSteps = [];
|
|
108
|
+
var changeset = _prosemirrorChangeset.ChangeSet.create(originalDoc);
|
|
109
109
|
var _iterator = _createForOfIteratorHelper(steps),
|
|
110
110
|
_step;
|
|
111
111
|
try {
|
|
@@ -113,8 +113,11 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref)
|
|
|
113
113
|
var step = _step.value;
|
|
114
114
|
var result = step.apply(steppedDoc);
|
|
115
115
|
if (result.failed === null && result.doc) {
|
|
116
|
+
if ((0, _attributeDecorations.stepIsValidAttrChange)(step, steppedDoc, result.doc)) {
|
|
117
|
+
attrSteps.push(step);
|
|
118
|
+
}
|
|
116
119
|
steppedDoc = result.doc;
|
|
117
|
-
|
|
120
|
+
changeset = changeset.addSteps(steppedDoc, [step.getMap()], step);
|
|
118
121
|
}
|
|
119
122
|
}
|
|
120
123
|
|
|
@@ -128,7 +131,6 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref)
|
|
|
128
131
|
if (!(0, _document.areNodesEqualIgnoreAttrs)(steppedDoc, tr.doc)) {
|
|
129
132
|
return _view.DecorationSet.empty;
|
|
130
133
|
}
|
|
131
|
-
var changeset = _prosemirrorChangeset.ChangeSet.create(originalDoc).addSteps(steppedDoc, stepMaps, tr.doc);
|
|
132
134
|
var changes = (0, _prosemirrorChangeset.simplifyChanges)(changeset.changes, tr.doc);
|
|
133
135
|
var optimizedChanges = optimizeChanges(changes);
|
|
134
136
|
var decorations = [];
|
|
@@ -147,14 +149,14 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref)
|
|
|
147
149
|
intl: intl
|
|
148
150
|
});
|
|
149
151
|
if (decoration) {
|
|
150
|
-
decorations.push(decoration);
|
|
152
|
+
decorations.push.apply(decorations, (0, _toConsumableArray2.default)(decoration));
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
155
|
});
|
|
154
156
|
(0, _markDecorations.getMarkChangeRanges)(steps).forEach(function (change) {
|
|
155
157
|
decorations.push((0, _decorations.createInlineChangedDecoration)(change, colourScheme));
|
|
156
158
|
});
|
|
157
|
-
(0, _attributeDecorations.getAttrChangeRanges)(tr.doc,
|
|
159
|
+
(0, _attributeDecorations.getAttrChangeRanges)(tr.doc, attrSteps).forEach(function (change) {
|
|
158
160
|
decorations.push.apply(decorations, (0, _toConsumableArray2.default)(calculateNodesForBlockDecoration(tr.doc, change.fromB, change.toB, colourScheme)));
|
|
159
161
|
});
|
|
160
162
|
return _view.DecorationSet.empty.add(tr.doc, decorations);
|
|
@@ -8,6 +8,7 @@ var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
|
|
|
8
8
|
var _messages = require("@atlaskit/editor-common/messages");
|
|
9
9
|
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
10
10
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
11
|
+
var _deletedRowsHandler = require("./deletedRowsHandler");
|
|
11
12
|
var _findSafeInsertPos = require("./findSafeInsertPos");
|
|
12
13
|
var editingStyle = (0, _lazyNodeView.convertToInlineCss)({
|
|
13
14
|
background: "var(--ds-background-accent-purple-subtlest, #F3F0FF)",
|
|
@@ -360,14 +361,27 @@ var createDeletedContentDecoration = exports.createDeletedContentDecoration = fu
|
|
|
360
361
|
if (slice.content.content.length === 0) {
|
|
361
362
|
return;
|
|
362
363
|
}
|
|
363
|
-
var
|
|
364
|
+
var isTableCellContent = slice.content.content.some(function () {
|
|
364
365
|
return slice.content.content.some(function (siblingNode) {
|
|
365
|
-
return ['tableHeader', 'tableCell'
|
|
366
|
+
return ['tableHeader', 'tableCell'].includes(siblingNode.type.name);
|
|
366
367
|
});
|
|
367
368
|
});
|
|
368
|
-
|
|
369
|
+
var isTableRowContent = slice.content.content.some(function () {
|
|
370
|
+
return slice.content.content.some(function (siblingNode) {
|
|
371
|
+
return ['tableRow'].includes(siblingNode.type.name);
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
if (isTableCellContent) {
|
|
369
375
|
return;
|
|
370
376
|
}
|
|
377
|
+
if (isTableRowContent) {
|
|
378
|
+
if (!(0, _platformFeatureFlags.fg)('platform_editor_ai_aifc_patch_ga')) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
var _handleDeletedRows = (0, _deletedRowsHandler.handleDeletedRows)([change], doc, newDoc, nodeViewSerializer, colourScheme),
|
|
382
|
+
decorations = _handleDeletedRows.decorations;
|
|
383
|
+
return decorations;
|
|
384
|
+
}
|
|
371
385
|
var serializer = nodeViewSerializer;
|
|
372
386
|
|
|
373
387
|
// For non-table content, use the existing span wrapper approach
|
|
@@ -502,5 +516,5 @@ var createDeletedContentDecoration = exports.createDeletedContentDecoration = fu
|
|
|
502
516
|
// Widget decoration used for deletions as the content is not in the document
|
|
503
517
|
// and we want to display the deleted content with a style.
|
|
504
518
|
var safeInsertPos = (0, _findSafeInsertPos.findSafeInsertPos)(newDoc, change.fromB, slice);
|
|
505
|
-
return _view.Decoration.widget(safeInsertPos, dom);
|
|
519
|
+
return [_view.Decoration.widget(safeInsertPos, dom)];
|
|
506
520
|
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.handleDeletedRows = exports.createDeletedRowsDecorations = void 0;
|
|
8
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
|
+
var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
|
|
10
|
+
var _document = require("@atlaskit/editor-common/utils/document");
|
|
11
|
+
var _utils = require("@atlaskit/editor-prosemirror/utils");
|
|
12
|
+
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
13
|
+
var _tableMap = require("@atlaskit/editor-tables/table-map");
|
|
14
|
+
var _findSafeInsertPos = require("./findSafeInsertPos");
|
|
15
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
16
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
17
|
+
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; }
|
|
18
|
+
var deletedRowStyle = (0, _lazyNodeView.convertToInlineCss)({
|
|
19
|
+
color: "var(--ds-text-accent-gray, #44546F)",
|
|
20
|
+
textDecoration: 'line-through',
|
|
21
|
+
opacity: 0.6,
|
|
22
|
+
display: 'table-row'
|
|
23
|
+
});
|
|
24
|
+
var deletedTraditionalRowStyle = (0, _lazyNodeView.convertToInlineCss)({
|
|
25
|
+
textDecorationColor: "var(--ds-border-accent-red, #E2483D)",
|
|
26
|
+
textDecoration: 'line-through',
|
|
27
|
+
opacity: 0.6,
|
|
28
|
+
display: 'table-row'
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Extracts information about deleted table rows from a change
|
|
32
|
+
*/
|
|
33
|
+
var extractDeletedRows = function extractDeletedRows(change, originalDoc, newDoc) {
|
|
34
|
+
var deletedRows = [];
|
|
35
|
+
|
|
36
|
+
// Find the table in the original document
|
|
37
|
+
var $fromPos = originalDoc.resolve(change.fromA);
|
|
38
|
+
var tableOld = (0, _utils.findParentNodeClosestToPos)($fromPos, function (node) {
|
|
39
|
+
return node.type.name === 'table';
|
|
40
|
+
});
|
|
41
|
+
if (!tableOld) {
|
|
42
|
+
return deletedRows;
|
|
43
|
+
}
|
|
44
|
+
var oldTableMap = _tableMap.TableMap.get(tableOld.node);
|
|
45
|
+
|
|
46
|
+
// Find the table in the new document at the insertion point
|
|
47
|
+
var $newPos = newDoc.resolve(change.fromB);
|
|
48
|
+
var tableNew = (0, _utils.findParentNodeClosestToPos)($newPos, function (node) {
|
|
49
|
+
return node.type.name === 'table';
|
|
50
|
+
});
|
|
51
|
+
if (!tableNew) {
|
|
52
|
+
return deletedRows;
|
|
53
|
+
}
|
|
54
|
+
var newTableMap = _tableMap.TableMap.get(tableNew.node);
|
|
55
|
+
|
|
56
|
+
// If no rows were deleted, return empty
|
|
57
|
+
if (oldTableMap.height <= newTableMap.height ||
|
|
58
|
+
// For now ignore if there are column deletions as well
|
|
59
|
+
oldTableMap.width !== newTableMap.width) {
|
|
60
|
+
return deletedRows;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Find which rows were deleted by analyzing the change range
|
|
64
|
+
var changeStartInTable = change.fromA - tableOld.pos - 1;
|
|
65
|
+
var changeEndInTable = change.toA - tableOld.pos - 1;
|
|
66
|
+
var currentOffset = 0;
|
|
67
|
+
var rowIndex = 0;
|
|
68
|
+
tableOld.node.content.forEach(function (rowNode, index) {
|
|
69
|
+
var rowStart = currentOffset;
|
|
70
|
+
var rowEnd = currentOffset + rowNode.nodeSize;
|
|
71
|
+
|
|
72
|
+
// Check if this row overlaps with the deletion range
|
|
73
|
+
var rowOverlapsChange = rowStart >= changeStartInTable && rowStart < changeEndInTable || rowEnd > changeStartInTable && rowEnd <= changeEndInTable || rowStart < changeStartInTable && rowEnd > changeEndInTable;
|
|
74
|
+
if (rowOverlapsChange && rowNode.type.name === 'tableRow' && !isEmptyRow(rowNode)) {
|
|
75
|
+
var startOfRow = newTableMap.mapByRow.slice().reverse().find(function (row) {
|
|
76
|
+
return row[0] + tableNew.pos < change.fromB && change.fromB < row[row.length - 1] + tableNew.pos;
|
|
77
|
+
});
|
|
78
|
+
deletedRows.push({
|
|
79
|
+
rowIndex: rowIndex,
|
|
80
|
+
rowNode: rowNode,
|
|
81
|
+
fromA: tableOld.pos + 1 + rowStart,
|
|
82
|
+
toA: tableOld.pos + 1 + rowEnd,
|
|
83
|
+
fromB: startOfRow ? startOfRow[0] + tableNew.start : change.fromB
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
currentOffset += rowNode.nodeSize;
|
|
87
|
+
if (rowNode.type.name === 'tableRow') {
|
|
88
|
+
rowIndex++;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Filter changes that never truly got deleted
|
|
93
|
+
return deletedRows.filter(function (deletedRow) {
|
|
94
|
+
return !tableNew.node.children.some(function (newRow) {
|
|
95
|
+
return (0, _document.areNodesEqualIgnoreAttrs)(newRow, deletedRow.rowNode);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Checks if a table row is empty (contains no meaningful content)
|
|
102
|
+
*/
|
|
103
|
+
var isEmptyRow = function isEmptyRow(rowNode) {
|
|
104
|
+
var isEmpty = true;
|
|
105
|
+
rowNode.descendants(function (node) {
|
|
106
|
+
if (!isEmpty) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// If we find any inline content with size > 0, the row is not empty
|
|
111
|
+
if (node.isInline && node.nodeSize > 0) {
|
|
112
|
+
isEmpty = false;
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// If we find text content, the row is not empty
|
|
117
|
+
if (node.isText && node.text && node.text.trim() !== '') {
|
|
118
|
+
isEmpty = false;
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
});
|
|
123
|
+
return isEmpty;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Creates a DOM representation of a deleted table row
|
|
128
|
+
*/
|
|
129
|
+
var createDeletedRowDOM = function createDeletedRowDOM(rowNode, nodeViewSerializer, colourScheme) {
|
|
130
|
+
var tr = document.createElement('tr');
|
|
131
|
+
tr.setAttribute('style', colourScheme === 'traditional' ? deletedTraditionalRowStyle : deletedRowStyle);
|
|
132
|
+
tr.setAttribute('data-testid', 'show-diff-deleted-row');
|
|
133
|
+
|
|
134
|
+
// Serialize each cell in the row
|
|
135
|
+
rowNode.content.forEach(function (cellNode) {
|
|
136
|
+
if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
|
|
137
|
+
var nodeView = nodeViewSerializer.tryCreateNodeView(cellNode);
|
|
138
|
+
if (nodeView) {
|
|
139
|
+
tr.appendChild(nodeView);
|
|
140
|
+
} else {
|
|
141
|
+
// Fallback to fragment serialization
|
|
142
|
+
var serializedContent = nodeViewSerializer.serializeFragment(cellNode.content);
|
|
143
|
+
if (serializedContent) {
|
|
144
|
+
tr.appendChild(serializedContent);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return tr;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Expands a diff to include whole deleted rows when table rows are affected
|
|
154
|
+
*/
|
|
155
|
+
var expandDiffForDeletedRows = function expandDiffForDeletedRows(changes, originalDoc, newDoc) {
|
|
156
|
+
var deletedRowInfo = [];
|
|
157
|
+
var _iterator = _createForOfIteratorHelper(changes),
|
|
158
|
+
_step;
|
|
159
|
+
try {
|
|
160
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
161
|
+
var change = _step.value;
|
|
162
|
+
// Check if this change affects table content
|
|
163
|
+
var deletedRows = extractDeletedRows(change, originalDoc, newDoc);
|
|
164
|
+
if (deletedRows.length > 0) {
|
|
165
|
+
deletedRowInfo.push.apply(deletedRowInfo, (0, _toConsumableArray2.default)(deletedRows));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
_iterator.e(err);
|
|
170
|
+
} finally {
|
|
171
|
+
_iterator.f();
|
|
172
|
+
}
|
|
173
|
+
return deletedRowInfo;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Creates decorations for deleted table rows
|
|
178
|
+
*/
|
|
179
|
+
var createDeletedRowsDecorations = exports.createDeletedRowsDecorations = function createDeletedRowsDecorations(_ref) {
|
|
180
|
+
var deletedRows = _ref.deletedRows,
|
|
181
|
+
originalDoc = _ref.originalDoc,
|
|
182
|
+
newDoc = _ref.newDoc,
|
|
183
|
+
nodeViewSerializer = _ref.nodeViewSerializer,
|
|
184
|
+
colourScheme = _ref.colourScheme;
|
|
185
|
+
return deletedRows.map(function (deletedRow) {
|
|
186
|
+
var rowDOM = createDeletedRowDOM(deletedRow.rowNode, nodeViewSerializer, colourScheme);
|
|
187
|
+
|
|
188
|
+
// Find safe insertion position for the deleted row
|
|
189
|
+
var safeInsertPos = (0, _findSafeInsertPos.findSafeInsertPos)(newDoc, deletedRow.fromB - 1,
|
|
190
|
+
// -1 to find the first safe position from the table
|
|
191
|
+
originalDoc.slice(deletedRow.fromA, deletedRow.toA));
|
|
192
|
+
return _view.Decoration.widget(safeInsertPos, rowDOM, {});
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Main function to handle deleted rows - computes diff and creates decorations
|
|
198
|
+
*/
|
|
199
|
+
var handleDeletedRows = exports.handleDeletedRows = function handleDeletedRows(changes, originalDoc, newDoc, nodeViewSerializer, colourScheme) {
|
|
200
|
+
// First, expand the changes to include complete deleted rows
|
|
201
|
+
var deletedRows = expandDiffForDeletedRows(changes.filter(function (change) {
|
|
202
|
+
return change.deleted.length > 0;
|
|
203
|
+
}), originalDoc, newDoc);
|
|
204
|
+
var allDecorations = createDeletedRowsDecorations({
|
|
205
|
+
deletedRows: deletedRows,
|
|
206
|
+
originalDoc: originalDoc,
|
|
207
|
+
newDoc: newDoc,
|
|
208
|
+
nodeViewSerializer: nodeViewSerializer,
|
|
209
|
+
colourScheme: colourScheme
|
|
210
|
+
});
|
|
211
|
+
return {
|
|
212
|
+
decorations: allDecorations
|
|
213
|
+
};
|
|
214
|
+
};
|
|
@@ -6,7 +6,7 @@ const filterUndefined = x => !!x;
|
|
|
6
6
|
const allowedAttrs = ['id', 'collection', 'url'];
|
|
7
7
|
export const getAttrChangeRanges = (doc, steps) => {
|
|
8
8
|
return steps.map(step => {
|
|
9
|
-
if (step instanceof AttrStep && allowedAttrs.includes(step.attr) || step instanceof SetAttrsStep && [...Object.keys(step.attrs)].some(v => allowedAttrs.includes(v))) {
|
|
9
|
+
if (step instanceof AttrStep && allowedAttrs.includes(step.attr) || step instanceof SetAttrsStep && step.attrs && [...Object.keys(step.attrs)].some(v => allowedAttrs.includes(v))) {
|
|
10
10
|
const $pos = doc.resolve(step.pos);
|
|
11
11
|
if ($pos.parent.type === doc.type.schema.nodes.mediaSingle) {
|
|
12
12
|
const startPos = $pos.pos + $pos.parentOffset;
|
|
@@ -18,4 +18,28 @@ export const getAttrChangeRanges = (doc, steps) => {
|
|
|
18
18
|
}
|
|
19
19
|
return undefined;
|
|
20
20
|
}).filter(filterUndefined);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Check if the step was a valid attr change and affected the doc
|
|
25
|
+
*
|
|
26
|
+
* @param step Attr step to test
|
|
27
|
+
* @param beforeDoc Doc before the step
|
|
28
|
+
* @param afterDoc Doc after the step
|
|
29
|
+
* @returns Boolean if the change should show a decoration
|
|
30
|
+
*/
|
|
31
|
+
export const stepIsValidAttrChange = (step, beforeDoc, afterDoc) => {
|
|
32
|
+
try {
|
|
33
|
+
if (step instanceof AttrStep || step instanceof SetAttrsStep) {
|
|
34
|
+
const attrStepAfter = afterDoc.nodeAt(step.pos);
|
|
35
|
+
const attrStepBefore = beforeDoc.nodeAt(step.pos);
|
|
36
|
+
// The change affected the document
|
|
37
|
+
if (attrStepAfter && attrStepBefore && !attrStepAfter.eq(attrStepBefore)) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
21
45
|
};
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
import isEqual from 'lodash/isEqual';
|
|
3
3
|
import memoizeOne from 'memoize-one';
|
|
4
4
|
import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
|
|
5
|
-
import { AnalyticsStep
|
|
5
|
+
import { AnalyticsStep } from '@atlaskit/adf-schema/steps';
|
|
6
6
|
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
|
|
7
|
-
import { AttrStep } from '@atlaskit/editor-prosemirror/transform';
|
|
8
7
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
9
|
-
import { getAttrChangeRanges } from './attributeDecorations';
|
|
8
|
+
import { getAttrChangeRanges, stepIsValidAttrChange } from './attributeDecorations';
|
|
10
9
|
import { createInlineChangedDecoration, createDeletedContentDecoration, createBlockChangedDecoration } from './decorations';
|
|
11
10
|
import { getMarkChangeRanges } from './markDecorations';
|
|
12
11
|
const calculateNodesForBlockDecoration = (doc, from, to, colourScheme) => {
|
|
@@ -65,7 +64,7 @@ function optimizeChanges(changes) {
|
|
|
65
64
|
function simplifySteps(steps) {
|
|
66
65
|
return steps
|
|
67
66
|
// Remove steps that don't affect document structure or content
|
|
68
|
-
.filter(step => !(step instanceof AnalyticsStep
|
|
67
|
+
.filter(step => !(step instanceof AnalyticsStep))
|
|
69
68
|
// Merge consecutive steps where possible
|
|
70
69
|
.reduce((acc, step) => {
|
|
71
70
|
var _lastStep$merge;
|
|
@@ -98,12 +97,16 @@ const calculateDiffDecorationsInner = ({
|
|
|
98
97
|
tr
|
|
99
98
|
} = state;
|
|
100
99
|
let steppedDoc = originalDoc;
|
|
101
|
-
const
|
|
100
|
+
const attrSteps = [];
|
|
101
|
+
let changeset = ChangeSet.create(originalDoc);
|
|
102
102
|
for (const step of steps) {
|
|
103
103
|
const result = step.apply(steppedDoc);
|
|
104
104
|
if (result.failed === null && result.doc) {
|
|
105
|
+
if (stepIsValidAttrChange(step, steppedDoc, result.doc)) {
|
|
106
|
+
attrSteps.push(step);
|
|
107
|
+
}
|
|
105
108
|
steppedDoc = result.doc;
|
|
106
|
-
|
|
109
|
+
changeset = changeset.addSteps(steppedDoc, [step.getMap()], step);
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
|
|
@@ -112,7 +115,6 @@ const calculateDiffDecorationsInner = ({
|
|
|
112
115
|
if (!areNodesEqualIgnoreAttrs(steppedDoc, tr.doc)) {
|
|
113
116
|
return DecorationSet.empty;
|
|
114
117
|
}
|
|
115
|
-
const changeset = ChangeSet.create(originalDoc).addSteps(steppedDoc, stepMaps, tr.doc);
|
|
116
118
|
const changes = simplifyChanges(changeset.changes, tr.doc);
|
|
117
119
|
const optimizedChanges = optimizeChanges(changes);
|
|
118
120
|
const decorations = [];
|
|
@@ -131,14 +133,14 @@ const calculateDiffDecorationsInner = ({
|
|
|
131
133
|
intl
|
|
132
134
|
});
|
|
133
135
|
if (decoration) {
|
|
134
|
-
decorations.push(decoration);
|
|
136
|
+
decorations.push(...decoration);
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
});
|
|
138
140
|
getMarkChangeRanges(steps).forEach(change => {
|
|
139
141
|
decorations.push(createInlineChangedDecoration(change, colourScheme));
|
|
140
142
|
});
|
|
141
|
-
getAttrChangeRanges(tr.doc,
|
|
143
|
+
getAttrChangeRanges(tr.doc, attrSteps).forEach(change => {
|
|
142
144
|
decorations.push(...calculateNodesForBlockDecoration(tr.doc, change.fromB, change.toB, colourScheme));
|
|
143
145
|
});
|
|
144
146
|
return DecorationSet.empty.add(tr.doc, decorations);
|
|
@@ -2,6 +2,7 @@ import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
|
2
2
|
import { trackChangesMessages } from '@atlaskit/editor-common/messages';
|
|
3
3
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
4
4
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
|
+
import { handleDeletedRows } from './deletedRowsHandler';
|
|
5
6
|
import { findSafeInsertPos } from './findSafeInsertPos';
|
|
6
7
|
const editingStyle = convertToInlineCss({
|
|
7
8
|
background: "var(--ds-background-accent-purple-subtlest, #F3F0FF)",
|
|
@@ -347,10 +348,20 @@ export const createDeletedContentDecoration = ({
|
|
|
347
348
|
if (slice.content.content.length === 0) {
|
|
348
349
|
return;
|
|
349
350
|
}
|
|
350
|
-
const
|
|
351
|
-
|
|
351
|
+
const isTableCellContent = slice.content.content.some(() => slice.content.content.some(siblingNode => ['tableHeader', 'tableCell'].includes(siblingNode.type.name)));
|
|
352
|
+
const isTableRowContent = slice.content.content.some(() => slice.content.content.some(siblingNode => ['tableRow'].includes(siblingNode.type.name)));
|
|
353
|
+
if (isTableCellContent) {
|
|
352
354
|
return;
|
|
353
355
|
}
|
|
356
|
+
if (isTableRowContent) {
|
|
357
|
+
if (!fg('platform_editor_ai_aifc_patch_ga')) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const {
|
|
361
|
+
decorations
|
|
362
|
+
} = handleDeletedRows([change], doc, newDoc, nodeViewSerializer, colourScheme);
|
|
363
|
+
return decorations;
|
|
364
|
+
}
|
|
354
365
|
const serializer = nodeViewSerializer;
|
|
355
366
|
|
|
356
367
|
// For non-table content, use the existing span wrapper approach
|
|
@@ -481,5 +492,5 @@ export const createDeletedContentDecoration = ({
|
|
|
481
492
|
// Widget decoration used for deletions as the content is not in the document
|
|
482
493
|
// and we want to display the deleted content with a style.
|
|
483
494
|
const safeInsertPos = findSafeInsertPos(newDoc, change.fromB, slice);
|
|
484
|
-
return Decoration.widget(safeInsertPos, dom);
|
|
495
|
+
return [Decoration.widget(safeInsertPos, dom)];
|
|
485
496
|
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
|
+
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
|
|
3
|
+
import { findParentNodeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
|
|
4
|
+
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
5
|
+
import { TableMap } from '@atlaskit/editor-tables/table-map';
|
|
6
|
+
import { findSafeInsertPos } from './findSafeInsertPos';
|
|
7
|
+
const deletedRowStyle = convertToInlineCss({
|
|
8
|
+
color: "var(--ds-text-accent-gray, #44546F)",
|
|
9
|
+
textDecoration: 'line-through',
|
|
10
|
+
opacity: 0.6,
|
|
11
|
+
display: 'table-row'
|
|
12
|
+
});
|
|
13
|
+
const deletedTraditionalRowStyle = convertToInlineCss({
|
|
14
|
+
textDecorationColor: "var(--ds-border-accent-red, #E2483D)",
|
|
15
|
+
textDecoration: 'line-through',
|
|
16
|
+
opacity: 0.6,
|
|
17
|
+
display: 'table-row'
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Extracts information about deleted table rows from a change
|
|
21
|
+
*/
|
|
22
|
+
const extractDeletedRows = (change, originalDoc, newDoc) => {
|
|
23
|
+
const deletedRows = [];
|
|
24
|
+
|
|
25
|
+
// Find the table in the original document
|
|
26
|
+
const $fromPos = originalDoc.resolve(change.fromA);
|
|
27
|
+
const tableOld = findParentNodeClosestToPos($fromPos, node => node.type.name === 'table');
|
|
28
|
+
if (!tableOld) {
|
|
29
|
+
return deletedRows;
|
|
30
|
+
}
|
|
31
|
+
const oldTableMap = TableMap.get(tableOld.node);
|
|
32
|
+
|
|
33
|
+
// Find the table in the new document at the insertion point
|
|
34
|
+
const $newPos = newDoc.resolve(change.fromB);
|
|
35
|
+
const tableNew = findParentNodeClosestToPos($newPos, node => node.type.name === 'table');
|
|
36
|
+
if (!tableNew) {
|
|
37
|
+
return deletedRows;
|
|
38
|
+
}
|
|
39
|
+
const newTableMap = TableMap.get(tableNew.node);
|
|
40
|
+
|
|
41
|
+
// If no rows were deleted, return empty
|
|
42
|
+
if (oldTableMap.height <= newTableMap.height ||
|
|
43
|
+
// For now ignore if there are column deletions as well
|
|
44
|
+
oldTableMap.width !== newTableMap.width) {
|
|
45
|
+
return deletedRows;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Find which rows were deleted by analyzing the change range
|
|
49
|
+
const changeStartInTable = change.fromA - tableOld.pos - 1;
|
|
50
|
+
const changeEndInTable = change.toA - tableOld.pos - 1;
|
|
51
|
+
let currentOffset = 0;
|
|
52
|
+
let rowIndex = 0;
|
|
53
|
+
tableOld.node.content.forEach((rowNode, index) => {
|
|
54
|
+
const rowStart = currentOffset;
|
|
55
|
+
const rowEnd = currentOffset + rowNode.nodeSize;
|
|
56
|
+
|
|
57
|
+
// Check if this row overlaps with the deletion range
|
|
58
|
+
const rowOverlapsChange = rowStart >= changeStartInTable && rowStart < changeEndInTable || rowEnd > changeStartInTable && rowEnd <= changeEndInTable || rowStart < changeStartInTable && rowEnd > changeEndInTable;
|
|
59
|
+
if (rowOverlapsChange && rowNode.type.name === 'tableRow' && !isEmptyRow(rowNode)) {
|
|
60
|
+
const startOfRow = newTableMap.mapByRow.slice().reverse().find(row => row[0] + tableNew.pos < change.fromB && change.fromB < row[row.length - 1] + tableNew.pos);
|
|
61
|
+
deletedRows.push({
|
|
62
|
+
rowIndex,
|
|
63
|
+
rowNode,
|
|
64
|
+
fromA: tableOld.pos + 1 + rowStart,
|
|
65
|
+
toA: tableOld.pos + 1 + rowEnd,
|
|
66
|
+
fromB: startOfRow ? startOfRow[0] + tableNew.start : change.fromB
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
currentOffset += rowNode.nodeSize;
|
|
70
|
+
if (rowNode.type.name === 'tableRow') {
|
|
71
|
+
rowIndex++;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Filter changes that never truly got deleted
|
|
76
|
+
return deletedRows.filter(deletedRow => {
|
|
77
|
+
return !tableNew.node.children.some(newRow => areNodesEqualIgnoreAttrs(newRow, deletedRow.rowNode));
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Checks if a table row is empty (contains no meaningful content)
|
|
83
|
+
*/
|
|
84
|
+
const isEmptyRow = rowNode => {
|
|
85
|
+
let isEmpty = true;
|
|
86
|
+
rowNode.descendants(node => {
|
|
87
|
+
if (!isEmpty) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// If we find any inline content with size > 0, the row is not empty
|
|
92
|
+
if (node.isInline && node.nodeSize > 0) {
|
|
93
|
+
isEmpty = false;
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// If we find text content, the row is not empty
|
|
98
|
+
if (node.isText && node.text && node.text.trim() !== '') {
|
|
99
|
+
isEmpty = false;
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
});
|
|
104
|
+
return isEmpty;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Creates a DOM representation of a deleted table row
|
|
109
|
+
*/
|
|
110
|
+
const createDeletedRowDOM = (rowNode, nodeViewSerializer, colourScheme) => {
|
|
111
|
+
const tr = document.createElement('tr');
|
|
112
|
+
tr.setAttribute('style', colourScheme === 'traditional' ? deletedTraditionalRowStyle : deletedRowStyle);
|
|
113
|
+
tr.setAttribute('data-testid', 'show-diff-deleted-row');
|
|
114
|
+
|
|
115
|
+
// Serialize each cell in the row
|
|
116
|
+
rowNode.content.forEach(cellNode => {
|
|
117
|
+
if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
|
|
118
|
+
const nodeView = nodeViewSerializer.tryCreateNodeView(cellNode);
|
|
119
|
+
if (nodeView) {
|
|
120
|
+
tr.appendChild(nodeView);
|
|
121
|
+
} else {
|
|
122
|
+
// Fallback to fragment serialization
|
|
123
|
+
const serializedContent = nodeViewSerializer.serializeFragment(cellNode.content);
|
|
124
|
+
if (serializedContent) {
|
|
125
|
+
tr.appendChild(serializedContent);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
return tr;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Expands a diff to include whole deleted rows when table rows are affected
|
|
135
|
+
*/
|
|
136
|
+
const expandDiffForDeletedRows = (changes, originalDoc, newDoc) => {
|
|
137
|
+
const deletedRowInfo = [];
|
|
138
|
+
for (const change of changes) {
|
|
139
|
+
// Check if this change affects table content
|
|
140
|
+
const deletedRows = extractDeletedRows(change, originalDoc, newDoc);
|
|
141
|
+
if (deletedRows.length > 0) {
|
|
142
|
+
deletedRowInfo.push(...deletedRows);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return deletedRowInfo;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Creates decorations for deleted table rows
|
|
150
|
+
*/
|
|
151
|
+
export const createDeletedRowsDecorations = ({
|
|
152
|
+
deletedRows,
|
|
153
|
+
originalDoc,
|
|
154
|
+
newDoc,
|
|
155
|
+
nodeViewSerializer,
|
|
156
|
+
colourScheme
|
|
157
|
+
}) => {
|
|
158
|
+
return deletedRows.map(deletedRow => {
|
|
159
|
+
const rowDOM = createDeletedRowDOM(deletedRow.rowNode, nodeViewSerializer, colourScheme);
|
|
160
|
+
|
|
161
|
+
// Find safe insertion position for the deleted row
|
|
162
|
+
const safeInsertPos = findSafeInsertPos(newDoc, deletedRow.fromB - 1,
|
|
163
|
+
// -1 to find the first safe position from the table
|
|
164
|
+
originalDoc.slice(deletedRow.fromA, deletedRow.toA));
|
|
165
|
+
return Decoration.widget(safeInsertPos, rowDOM, {});
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Main function to handle deleted rows - computes diff and creates decorations
|
|
171
|
+
*/
|
|
172
|
+
export const handleDeletedRows = (changes, originalDoc, newDoc, nodeViewSerializer, colourScheme) => {
|
|
173
|
+
// First, expand the changes to include complete deleted rows
|
|
174
|
+
const deletedRows = expandDiffForDeletedRows(changes.filter(change => change.deleted.length > 0), originalDoc, newDoc);
|
|
175
|
+
const allDecorations = createDeletedRowsDecorations({
|
|
176
|
+
deletedRows,
|
|
177
|
+
originalDoc,
|
|
178
|
+
newDoc,
|
|
179
|
+
nodeViewSerializer,
|
|
180
|
+
colourScheme
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
decorations: allDecorations
|
|
184
|
+
};
|
|
185
|
+
};
|
|
@@ -9,7 +9,7 @@ var filterUndefined = function filterUndefined(x) {
|
|
|
9
9
|
var allowedAttrs = ['id', 'collection', 'url'];
|
|
10
10
|
export var getAttrChangeRanges = function getAttrChangeRanges(doc, steps) {
|
|
11
11
|
return steps.map(function (step) {
|
|
12
|
-
if (step instanceof AttrStep && allowedAttrs.includes(step.attr) || step instanceof SetAttrsStep && _toConsumableArray(Object.keys(step.attrs)).some(function (v) {
|
|
12
|
+
if (step instanceof AttrStep && allowedAttrs.includes(step.attr) || step instanceof SetAttrsStep && step.attrs && _toConsumableArray(Object.keys(step.attrs)).some(function (v) {
|
|
13
13
|
return allowedAttrs.includes(v);
|
|
14
14
|
})) {
|
|
15
15
|
var $pos = doc.resolve(step.pos);
|
|
@@ -23,4 +23,28 @@ export var getAttrChangeRanges = function getAttrChangeRanges(doc, steps) {
|
|
|
23
23
|
}
|
|
24
24
|
return undefined;
|
|
25
25
|
}).filter(filterUndefined);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if the step was a valid attr change and affected the doc
|
|
30
|
+
*
|
|
31
|
+
* @param step Attr step to test
|
|
32
|
+
* @param beforeDoc Doc before the step
|
|
33
|
+
* @param afterDoc Doc after the step
|
|
34
|
+
* @returns Boolean if the change should show a decoration
|
|
35
|
+
*/
|
|
36
|
+
export var stepIsValidAttrChange = function stepIsValidAttrChange(step, beforeDoc, afterDoc) {
|
|
37
|
+
try {
|
|
38
|
+
if (step instanceof AttrStep || step instanceof SetAttrsStep) {
|
|
39
|
+
var attrStepAfter = afterDoc.nodeAt(step.pos);
|
|
40
|
+
var attrStepBefore = beforeDoc.nodeAt(step.pos);
|
|
41
|
+
// The change affected the document
|
|
42
|
+
if (attrStepAfter && attrStepBefore && !attrStepAfter.eq(attrStepBefore)) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
26
50
|
};
|
|
@@ -10,11 +10,10 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
10
10
|
import isEqual from 'lodash/isEqual';
|
|
11
11
|
import memoizeOne from 'memoize-one';
|
|
12
12
|
import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
|
|
13
|
-
import { AnalyticsStep
|
|
13
|
+
import { AnalyticsStep } from '@atlaskit/adf-schema/steps';
|
|
14
14
|
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
|
|
15
|
-
import { AttrStep } from '@atlaskit/editor-prosemirror/transform';
|
|
16
15
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
17
|
-
import { getAttrChangeRanges } from './attributeDecorations';
|
|
16
|
+
import { getAttrChangeRanges, stepIsValidAttrChange } from './attributeDecorations';
|
|
18
17
|
import { createInlineChangedDecoration, createDeletedContentDecoration, createBlockChangedDecoration } from './decorations';
|
|
19
18
|
import { getMarkChangeRanges } from './markDecorations';
|
|
20
19
|
var calculateNodesForBlockDecoration = function calculateNodesForBlockDecoration(doc, from, to, colourScheme) {
|
|
@@ -70,7 +69,7 @@ function simplifySteps(steps) {
|
|
|
70
69
|
return steps
|
|
71
70
|
// Remove steps that don't affect document structure or content
|
|
72
71
|
.filter(function (step) {
|
|
73
|
-
return !(step instanceof AnalyticsStep
|
|
72
|
+
return !(step instanceof AnalyticsStep);
|
|
74
73
|
})
|
|
75
74
|
// Merge consecutive steps where possible
|
|
76
75
|
.reduce(function (acc, step) {
|
|
@@ -99,7 +98,8 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref)
|
|
|
99
98
|
}
|
|
100
99
|
var tr = state.tr;
|
|
101
100
|
var steppedDoc = originalDoc;
|
|
102
|
-
var
|
|
101
|
+
var attrSteps = [];
|
|
102
|
+
var changeset = ChangeSet.create(originalDoc);
|
|
103
103
|
var _iterator = _createForOfIteratorHelper(steps),
|
|
104
104
|
_step;
|
|
105
105
|
try {
|
|
@@ -107,8 +107,11 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref)
|
|
|
107
107
|
var step = _step.value;
|
|
108
108
|
var result = step.apply(steppedDoc);
|
|
109
109
|
if (result.failed === null && result.doc) {
|
|
110
|
+
if (stepIsValidAttrChange(step, steppedDoc, result.doc)) {
|
|
111
|
+
attrSteps.push(step);
|
|
112
|
+
}
|
|
110
113
|
steppedDoc = result.doc;
|
|
111
|
-
|
|
114
|
+
changeset = changeset.addSteps(steppedDoc, [step.getMap()], step);
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
117
|
|
|
@@ -122,7 +125,6 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref)
|
|
|
122
125
|
if (!areNodesEqualIgnoreAttrs(steppedDoc, tr.doc)) {
|
|
123
126
|
return DecorationSet.empty;
|
|
124
127
|
}
|
|
125
|
-
var changeset = ChangeSet.create(originalDoc).addSteps(steppedDoc, stepMaps, tr.doc);
|
|
126
128
|
var changes = simplifyChanges(changeset.changes, tr.doc);
|
|
127
129
|
var optimizedChanges = optimizeChanges(changes);
|
|
128
130
|
var decorations = [];
|
|
@@ -141,14 +143,14 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref)
|
|
|
141
143
|
intl: intl
|
|
142
144
|
});
|
|
143
145
|
if (decoration) {
|
|
144
|
-
decorations.push(decoration);
|
|
146
|
+
decorations.push.apply(decorations, _toConsumableArray(decoration));
|
|
145
147
|
}
|
|
146
148
|
}
|
|
147
149
|
});
|
|
148
150
|
getMarkChangeRanges(steps).forEach(function (change) {
|
|
149
151
|
decorations.push(createInlineChangedDecoration(change, colourScheme));
|
|
150
152
|
});
|
|
151
|
-
getAttrChangeRanges(tr.doc,
|
|
153
|
+
getAttrChangeRanges(tr.doc, attrSteps).forEach(function (change) {
|
|
152
154
|
decorations.push.apply(decorations, _toConsumableArray(calculateNodesForBlockDecoration(tr.doc, change.fromB, change.toB, colourScheme)));
|
|
153
155
|
});
|
|
154
156
|
return DecorationSet.empty.add(tr.doc, decorations);
|
|
@@ -2,6 +2,7 @@ import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
|
2
2
|
import { trackChangesMessages } from '@atlaskit/editor-common/messages';
|
|
3
3
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
4
4
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
|
+
import { handleDeletedRows } from './deletedRowsHandler';
|
|
5
6
|
import { findSafeInsertPos } from './findSafeInsertPos';
|
|
6
7
|
var editingStyle = convertToInlineCss({
|
|
7
8
|
background: "var(--ds-background-accent-purple-subtlest, #F3F0FF)",
|
|
@@ -354,14 +355,27 @@ export var createDeletedContentDecoration = function createDeletedContentDecorat
|
|
|
354
355
|
if (slice.content.content.length === 0) {
|
|
355
356
|
return;
|
|
356
357
|
}
|
|
357
|
-
var
|
|
358
|
+
var isTableCellContent = slice.content.content.some(function () {
|
|
358
359
|
return slice.content.content.some(function (siblingNode) {
|
|
359
|
-
return ['tableHeader', 'tableCell'
|
|
360
|
+
return ['tableHeader', 'tableCell'].includes(siblingNode.type.name);
|
|
360
361
|
});
|
|
361
362
|
});
|
|
362
|
-
|
|
363
|
+
var isTableRowContent = slice.content.content.some(function () {
|
|
364
|
+
return slice.content.content.some(function (siblingNode) {
|
|
365
|
+
return ['tableRow'].includes(siblingNode.type.name);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
if (isTableCellContent) {
|
|
363
369
|
return;
|
|
364
370
|
}
|
|
371
|
+
if (isTableRowContent) {
|
|
372
|
+
if (!fg('platform_editor_ai_aifc_patch_ga')) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
var _handleDeletedRows = handleDeletedRows([change], doc, newDoc, nodeViewSerializer, colourScheme),
|
|
376
|
+
decorations = _handleDeletedRows.decorations;
|
|
377
|
+
return decorations;
|
|
378
|
+
}
|
|
365
379
|
var serializer = nodeViewSerializer;
|
|
366
380
|
|
|
367
381
|
// For non-table content, use the existing span wrapper approach
|
|
@@ -496,5 +510,5 @@ export var createDeletedContentDecoration = function createDeletedContentDecorat
|
|
|
496
510
|
// Widget decoration used for deletions as the content is not in the document
|
|
497
511
|
// and we want to display the deleted content with a style.
|
|
498
512
|
var safeInsertPos = findSafeInsertPos(newDoc, change.fromB, slice);
|
|
499
|
-
return Decoration.widget(safeInsertPos, dom);
|
|
513
|
+
return [Decoration.widget(safeInsertPos, dom)];
|
|
500
514
|
};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
3
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
4
|
+
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; }
|
|
5
|
+
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
6
|
+
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
|
|
7
|
+
import { findParentNodeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
|
|
8
|
+
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
9
|
+
import { TableMap } from '@atlaskit/editor-tables/table-map';
|
|
10
|
+
import { findSafeInsertPos } from './findSafeInsertPos';
|
|
11
|
+
var deletedRowStyle = convertToInlineCss({
|
|
12
|
+
color: "var(--ds-text-accent-gray, #44546F)",
|
|
13
|
+
textDecoration: 'line-through',
|
|
14
|
+
opacity: 0.6,
|
|
15
|
+
display: 'table-row'
|
|
16
|
+
});
|
|
17
|
+
var deletedTraditionalRowStyle = convertToInlineCss({
|
|
18
|
+
textDecorationColor: "var(--ds-border-accent-red, #E2483D)",
|
|
19
|
+
textDecoration: 'line-through',
|
|
20
|
+
opacity: 0.6,
|
|
21
|
+
display: 'table-row'
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Extracts information about deleted table rows from a change
|
|
25
|
+
*/
|
|
26
|
+
var extractDeletedRows = function extractDeletedRows(change, originalDoc, newDoc) {
|
|
27
|
+
var deletedRows = [];
|
|
28
|
+
|
|
29
|
+
// Find the table in the original document
|
|
30
|
+
var $fromPos = originalDoc.resolve(change.fromA);
|
|
31
|
+
var tableOld = findParentNodeClosestToPos($fromPos, function (node) {
|
|
32
|
+
return node.type.name === 'table';
|
|
33
|
+
});
|
|
34
|
+
if (!tableOld) {
|
|
35
|
+
return deletedRows;
|
|
36
|
+
}
|
|
37
|
+
var oldTableMap = TableMap.get(tableOld.node);
|
|
38
|
+
|
|
39
|
+
// Find the table in the new document at the insertion point
|
|
40
|
+
var $newPos = newDoc.resolve(change.fromB);
|
|
41
|
+
var tableNew = findParentNodeClosestToPos($newPos, function (node) {
|
|
42
|
+
return node.type.name === 'table';
|
|
43
|
+
});
|
|
44
|
+
if (!tableNew) {
|
|
45
|
+
return deletedRows;
|
|
46
|
+
}
|
|
47
|
+
var newTableMap = TableMap.get(tableNew.node);
|
|
48
|
+
|
|
49
|
+
// If no rows were deleted, return empty
|
|
50
|
+
if (oldTableMap.height <= newTableMap.height ||
|
|
51
|
+
// For now ignore if there are column deletions as well
|
|
52
|
+
oldTableMap.width !== newTableMap.width) {
|
|
53
|
+
return deletedRows;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Find which rows were deleted by analyzing the change range
|
|
57
|
+
var changeStartInTable = change.fromA - tableOld.pos - 1;
|
|
58
|
+
var changeEndInTable = change.toA - tableOld.pos - 1;
|
|
59
|
+
var currentOffset = 0;
|
|
60
|
+
var rowIndex = 0;
|
|
61
|
+
tableOld.node.content.forEach(function (rowNode, index) {
|
|
62
|
+
var rowStart = currentOffset;
|
|
63
|
+
var rowEnd = currentOffset + rowNode.nodeSize;
|
|
64
|
+
|
|
65
|
+
// Check if this row overlaps with the deletion range
|
|
66
|
+
var rowOverlapsChange = rowStart >= changeStartInTable && rowStart < changeEndInTable || rowEnd > changeStartInTable && rowEnd <= changeEndInTable || rowStart < changeStartInTable && rowEnd > changeEndInTable;
|
|
67
|
+
if (rowOverlapsChange && rowNode.type.name === 'tableRow' && !isEmptyRow(rowNode)) {
|
|
68
|
+
var startOfRow = newTableMap.mapByRow.slice().reverse().find(function (row) {
|
|
69
|
+
return row[0] + tableNew.pos < change.fromB && change.fromB < row[row.length - 1] + tableNew.pos;
|
|
70
|
+
});
|
|
71
|
+
deletedRows.push({
|
|
72
|
+
rowIndex: rowIndex,
|
|
73
|
+
rowNode: rowNode,
|
|
74
|
+
fromA: tableOld.pos + 1 + rowStart,
|
|
75
|
+
toA: tableOld.pos + 1 + rowEnd,
|
|
76
|
+
fromB: startOfRow ? startOfRow[0] + tableNew.start : change.fromB
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
currentOffset += rowNode.nodeSize;
|
|
80
|
+
if (rowNode.type.name === 'tableRow') {
|
|
81
|
+
rowIndex++;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Filter changes that never truly got deleted
|
|
86
|
+
return deletedRows.filter(function (deletedRow) {
|
|
87
|
+
return !tableNew.node.children.some(function (newRow) {
|
|
88
|
+
return areNodesEqualIgnoreAttrs(newRow, deletedRow.rowNode);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Checks if a table row is empty (contains no meaningful content)
|
|
95
|
+
*/
|
|
96
|
+
var isEmptyRow = function isEmptyRow(rowNode) {
|
|
97
|
+
var isEmpty = true;
|
|
98
|
+
rowNode.descendants(function (node) {
|
|
99
|
+
if (!isEmpty) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// If we find any inline content with size > 0, the row is not empty
|
|
104
|
+
if (node.isInline && node.nodeSize > 0) {
|
|
105
|
+
isEmpty = false;
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// If we find text content, the row is not empty
|
|
110
|
+
if (node.isText && node.text && node.text.trim() !== '') {
|
|
111
|
+
isEmpty = false;
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
});
|
|
116
|
+
return isEmpty;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Creates a DOM representation of a deleted table row
|
|
121
|
+
*/
|
|
122
|
+
var createDeletedRowDOM = function createDeletedRowDOM(rowNode, nodeViewSerializer, colourScheme) {
|
|
123
|
+
var tr = document.createElement('tr');
|
|
124
|
+
tr.setAttribute('style', colourScheme === 'traditional' ? deletedTraditionalRowStyle : deletedRowStyle);
|
|
125
|
+
tr.setAttribute('data-testid', 'show-diff-deleted-row');
|
|
126
|
+
|
|
127
|
+
// Serialize each cell in the row
|
|
128
|
+
rowNode.content.forEach(function (cellNode) {
|
|
129
|
+
if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
|
|
130
|
+
var nodeView = nodeViewSerializer.tryCreateNodeView(cellNode);
|
|
131
|
+
if (nodeView) {
|
|
132
|
+
tr.appendChild(nodeView);
|
|
133
|
+
} else {
|
|
134
|
+
// Fallback to fragment serialization
|
|
135
|
+
var serializedContent = nodeViewSerializer.serializeFragment(cellNode.content);
|
|
136
|
+
if (serializedContent) {
|
|
137
|
+
tr.appendChild(serializedContent);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return tr;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Expands a diff to include whole deleted rows when table rows are affected
|
|
147
|
+
*/
|
|
148
|
+
var expandDiffForDeletedRows = function expandDiffForDeletedRows(changes, originalDoc, newDoc) {
|
|
149
|
+
var deletedRowInfo = [];
|
|
150
|
+
var _iterator = _createForOfIteratorHelper(changes),
|
|
151
|
+
_step;
|
|
152
|
+
try {
|
|
153
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
154
|
+
var change = _step.value;
|
|
155
|
+
// Check if this change affects table content
|
|
156
|
+
var deletedRows = extractDeletedRows(change, originalDoc, newDoc);
|
|
157
|
+
if (deletedRows.length > 0) {
|
|
158
|
+
deletedRowInfo.push.apply(deletedRowInfo, _toConsumableArray(deletedRows));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} catch (err) {
|
|
162
|
+
_iterator.e(err);
|
|
163
|
+
} finally {
|
|
164
|
+
_iterator.f();
|
|
165
|
+
}
|
|
166
|
+
return deletedRowInfo;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Creates decorations for deleted table rows
|
|
171
|
+
*/
|
|
172
|
+
export var createDeletedRowsDecorations = function createDeletedRowsDecorations(_ref) {
|
|
173
|
+
var deletedRows = _ref.deletedRows,
|
|
174
|
+
originalDoc = _ref.originalDoc,
|
|
175
|
+
newDoc = _ref.newDoc,
|
|
176
|
+
nodeViewSerializer = _ref.nodeViewSerializer,
|
|
177
|
+
colourScheme = _ref.colourScheme;
|
|
178
|
+
return deletedRows.map(function (deletedRow) {
|
|
179
|
+
var rowDOM = createDeletedRowDOM(deletedRow.rowNode, nodeViewSerializer, colourScheme);
|
|
180
|
+
|
|
181
|
+
// Find safe insertion position for the deleted row
|
|
182
|
+
var safeInsertPos = findSafeInsertPos(newDoc, deletedRow.fromB - 1,
|
|
183
|
+
// -1 to find the first safe position from the table
|
|
184
|
+
originalDoc.slice(deletedRow.fromA, deletedRow.toA));
|
|
185
|
+
return Decoration.widget(safeInsertPos, rowDOM, {});
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Main function to handle deleted rows - computes diff and creates decorations
|
|
191
|
+
*/
|
|
192
|
+
export var handleDeletedRows = function handleDeletedRows(changes, originalDoc, newDoc, nodeViewSerializer, colourScheme) {
|
|
193
|
+
// First, expand the changes to include complete deleted rows
|
|
194
|
+
var deletedRows = expandDiffForDeletedRows(changes.filter(function (change) {
|
|
195
|
+
return change.deleted.length > 0;
|
|
196
|
+
}), originalDoc, newDoc);
|
|
197
|
+
var allDecorations = createDeletedRowsDecorations({
|
|
198
|
+
deletedRows: deletedRows,
|
|
199
|
+
originalDoc: originalDoc,
|
|
200
|
+
newDoc: newDoc,
|
|
201
|
+
nodeViewSerializer: nodeViewSerializer,
|
|
202
|
+
colourScheme: colourScheme
|
|
203
|
+
});
|
|
204
|
+
return {
|
|
205
|
+
decorations: allDecorations
|
|
206
|
+
};
|
|
207
|
+
};
|
|
@@ -5,4 +5,13 @@ type StepRange = {
|
|
|
5
5
|
toB: number;
|
|
6
6
|
};
|
|
7
7
|
export declare const getAttrChangeRanges: (doc: PMNode, steps: ProseMirrorStep[]) => StepRange[];
|
|
8
|
+
/**
|
|
9
|
+
* Check if the step was a valid attr change and affected the doc
|
|
10
|
+
*
|
|
11
|
+
* @param step Attr step to test
|
|
12
|
+
* @param beforeDoc Doc before the step
|
|
13
|
+
* @param afterDoc Doc after the step
|
|
14
|
+
* @returns Boolean if the change should show a decoration
|
|
15
|
+
*/
|
|
16
|
+
export declare const stepIsValidAttrChange: (step: ProseMirrorStep, beforeDoc: PMNode, afterDoc: PMNode) => boolean;
|
|
8
17
|
export {};
|
|
@@ -25,12 +25,12 @@ export declare const createBlockChangedDecoration: (change: {
|
|
|
25
25
|
to: number;
|
|
26
26
|
}, colourScheme?: "standard" | "traditional") => Decoration;
|
|
27
27
|
interface DeletedContentDecorationProps {
|
|
28
|
-
change: Change
|
|
28
|
+
change: Pick<Change, 'fromA' | 'toA' | 'fromB' | 'deleted'>;
|
|
29
29
|
colourScheme?: 'standard' | 'traditional';
|
|
30
30
|
doc: PMNode;
|
|
31
31
|
intl: IntlShape;
|
|
32
32
|
newDoc: PMNode;
|
|
33
33
|
nodeViewSerializer: NodeViewSerializer;
|
|
34
34
|
}
|
|
35
|
-
export declare const createDeletedContentDecoration: ({ change, doc, nodeViewSerializer, colourScheme, newDoc, intl, }: DeletedContentDecorationProps) => Decoration | undefined;
|
|
35
|
+
export declare const createDeletedContentDecoration: ({ change, doc, nodeViewSerializer, colourScheme, newDoc, intl, }: DeletedContentDecorationProps) => Decoration[] | undefined;
|
|
36
36
|
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Change } from 'prosemirror-changeset';
|
|
2
|
+
import { type Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
4
|
+
import type { NodeViewSerializer } from './NodeViewSerializer';
|
|
5
|
+
interface DeletedRowInfo {
|
|
6
|
+
fromA: number;
|
|
7
|
+
fromB: number;
|
|
8
|
+
rowIndex: number;
|
|
9
|
+
rowNode: PMNode;
|
|
10
|
+
toA: number;
|
|
11
|
+
}
|
|
12
|
+
type SimpleChange = Pick<Change, 'fromA' | 'toA' | 'fromB' | 'deleted'>;
|
|
13
|
+
interface DeletedRowsHandlerProps {
|
|
14
|
+
colourScheme?: 'standard' | 'traditional';
|
|
15
|
+
deletedRows: DeletedRowInfo[];
|
|
16
|
+
newDoc: PMNode;
|
|
17
|
+
nodeViewSerializer: NodeViewSerializer;
|
|
18
|
+
originalDoc: PMNode;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates decorations for deleted table rows
|
|
22
|
+
*/
|
|
23
|
+
export declare const createDeletedRowsDecorations: ({ deletedRows, originalDoc, newDoc, nodeViewSerializer, colourScheme, }: DeletedRowsHandlerProps) => Decoration[];
|
|
24
|
+
/**
|
|
25
|
+
* Main function to handle deleted rows - computes diff and creates decorations
|
|
26
|
+
*/
|
|
27
|
+
export declare const handleDeletedRows: (changes: SimpleChange[], originalDoc: PMNode, newDoc: PMNode, nodeViewSerializer: NodeViewSerializer, colourScheme?: "standard" | "traditional") => {
|
|
28
|
+
decorations: Decoration[];
|
|
29
|
+
};
|
|
30
|
+
export {};
|
|
@@ -5,4 +5,13 @@ type StepRange = {
|
|
|
5
5
|
toB: number;
|
|
6
6
|
};
|
|
7
7
|
export declare const getAttrChangeRanges: (doc: PMNode, steps: ProseMirrorStep[]) => StepRange[];
|
|
8
|
+
/**
|
|
9
|
+
* Check if the step was a valid attr change and affected the doc
|
|
10
|
+
*
|
|
11
|
+
* @param step Attr step to test
|
|
12
|
+
* @param beforeDoc Doc before the step
|
|
13
|
+
* @param afterDoc Doc after the step
|
|
14
|
+
* @returns Boolean if the change should show a decoration
|
|
15
|
+
*/
|
|
16
|
+
export declare const stepIsValidAttrChange: (step: ProseMirrorStep, beforeDoc: PMNode, afterDoc: PMNode) => boolean;
|
|
8
17
|
export {};
|
|
@@ -25,12 +25,12 @@ export declare const createBlockChangedDecoration: (change: {
|
|
|
25
25
|
to: number;
|
|
26
26
|
}, colourScheme?: "standard" | "traditional") => Decoration;
|
|
27
27
|
interface DeletedContentDecorationProps {
|
|
28
|
-
change: Change
|
|
28
|
+
change: Pick<Change, 'fromA' | 'toA' | 'fromB' | 'deleted'>;
|
|
29
29
|
colourScheme?: 'standard' | 'traditional';
|
|
30
30
|
doc: PMNode;
|
|
31
31
|
intl: IntlShape;
|
|
32
32
|
newDoc: PMNode;
|
|
33
33
|
nodeViewSerializer: NodeViewSerializer;
|
|
34
34
|
}
|
|
35
|
-
export declare const createDeletedContentDecoration: ({ change, doc, nodeViewSerializer, colourScheme, newDoc, intl, }: DeletedContentDecorationProps) => Decoration | undefined;
|
|
35
|
+
export declare const createDeletedContentDecoration: ({ change, doc, nodeViewSerializer, colourScheme, newDoc, intl, }: DeletedContentDecorationProps) => Decoration[] | undefined;
|
|
36
36
|
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Change } from 'prosemirror-changeset';
|
|
2
|
+
import { type Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
4
|
+
import type { NodeViewSerializer } from './NodeViewSerializer';
|
|
5
|
+
interface DeletedRowInfo {
|
|
6
|
+
fromA: number;
|
|
7
|
+
fromB: number;
|
|
8
|
+
rowIndex: number;
|
|
9
|
+
rowNode: PMNode;
|
|
10
|
+
toA: number;
|
|
11
|
+
}
|
|
12
|
+
type SimpleChange = Pick<Change, 'fromA' | 'toA' | 'fromB' | 'deleted'>;
|
|
13
|
+
interface DeletedRowsHandlerProps {
|
|
14
|
+
colourScheme?: 'standard' | 'traditional';
|
|
15
|
+
deletedRows: DeletedRowInfo[];
|
|
16
|
+
newDoc: PMNode;
|
|
17
|
+
nodeViewSerializer: NodeViewSerializer;
|
|
18
|
+
originalDoc: PMNode;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates decorations for deleted table rows
|
|
22
|
+
*/
|
|
23
|
+
export declare const createDeletedRowsDecorations: ({ deletedRows, originalDoc, newDoc, nodeViewSerializer, colourScheme, }: DeletedRowsHandlerProps) => Decoration[];
|
|
24
|
+
/**
|
|
25
|
+
* Main function to handle deleted rows - computes diff and creates decorations
|
|
26
|
+
*/
|
|
27
|
+
export declare const handleDeletedRows: (changes: SimpleChange[], originalDoc: PMNode, newDoc: PMNode, nodeViewSerializer: NodeViewSerializer, colourScheme?: "standard" | "traditional") => {
|
|
28
|
+
decorations: Decoration[];
|
|
29
|
+
};
|
|
30
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-show-diff",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "ShowDiff plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -30,15 +30,16 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@atlaskit/adf-schema": "^51.3.2",
|
|
32
32
|
"@atlaskit/editor-prosemirror": "7.0.0",
|
|
33
|
+
"@atlaskit/editor-tables": "^2.9.0",
|
|
33
34
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
34
|
-
"@atlaskit/tokens": "^7.
|
|
35
|
+
"@atlaskit/tokens": "^7.1.0",
|
|
35
36
|
"@babel/runtime": "^7.0.0",
|
|
36
37
|
"lodash": "^4.17.21",
|
|
37
38
|
"memoize-one": "^6.0.0",
|
|
38
39
|
"prosemirror-changeset": "^2.2.1"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
|
-
"@atlaskit/editor-common": "^110.
|
|
42
|
+
"@atlaskit/editor-common": "^110.22.0",
|
|
42
43
|
"react": "^18.2.0"
|
|
43
44
|
},
|
|
44
45
|
"techstack": {
|
|
@@ -80,6 +81,9 @@
|
|
|
80
81
|
"platform-feature-flags": {
|
|
81
82
|
"platform_editor_ai_aifc_patch_beta_2": {
|
|
82
83
|
"type": "boolean"
|
|
84
|
+
},
|
|
85
|
+
"platform_editor_ai_aifc_patch_ga": {
|
|
86
|
+
"type": "boolean"
|
|
83
87
|
}
|
|
84
88
|
}
|
|
85
89
|
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../../tsconfig.entry-points.post-office.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"target": "es5",
|
|
5
|
-
"outDir": "../../../../../post-office/tsDist/@atlaskit__editor-plugin-show-diff/app",
|
|
6
|
-
"rootDir": "../",
|
|
7
|
-
"composite": true
|
|
8
|
-
},
|
|
9
|
-
"include": [
|
|
10
|
-
"../src/**/*.ts",
|
|
11
|
-
"../src/**/*.tsx"
|
|
12
|
-
],
|
|
13
|
-
"exclude": [
|
|
14
|
-
"../src/**/__tests__/*",
|
|
15
|
-
"../src/**/*.test.*",
|
|
16
|
-
"../src/**/test.*",
|
|
17
|
-
"../src/**/examples.*"
|
|
18
|
-
],
|
|
19
|
-
"references": [
|
|
20
|
-
{
|
|
21
|
-
"path": "../../../platform/feature-flags/afm-post-office/tsconfig.json"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"path": "../../../design-system/tokens/afm-post-office/tsconfig.json"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"path": "../../editor-common/afm-post-office/tsconfig.json"
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
}
|