@atlaskit/editor-plugin-show-diff 16.0.13 → 16.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +1 -1
- package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +91 -28
- package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +4 -6
- package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +11 -8
- package/dist/cjs/pm-plugins/utils/isEmptyParagraphSlice.js +11 -0
- package/dist/cjs/ui/ContributorTag/buildContributorTagDom.js +5 -4
- package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +1 -1
- package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +70 -13
- package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +4 -6
- package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +11 -8
- package/dist/es2019/pm-plugins/utils/isEmptyParagraphSlice.js +5 -0
- package/dist/es2019/ui/ContributorTag/buildContributorTagDom.js +5 -4
- package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +1 -1
- package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +92 -29
- package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +4 -6
- package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +11 -8
- package/dist/esm/pm-plugins/utils/isEmptyParagraphSlice.js +5 -0
- package/dist/esm/ui/ContributorTag/buildContributorTagDom.js +5 -4
- package/dist/types/pm-plugins/decorations/createChangedRowDecorationWidgets.d.ts +6 -1
- package/dist/types/pm-plugins/utils/isEmptyParagraphSlice.d.ts +3 -0
- package/dist/types/ui/ContributorTag/buildContributorTagDom.d.ts +4 -4
- package/package.json +2 -2
- package/dist/cjs/pm-plugins/areDocsEqualByBlockStructureAndText.js +0 -55
- package/dist/cjs/pm-plugins/utils/hasVisibleContent.js +0 -37
- package/dist/es2019/pm-plugins/areDocsEqualByBlockStructureAndText.js +0 -50
- package/dist/es2019/pm-plugins/utils/hasVisibleContent.js +0 -28
- package/dist/esm/pm-plugins/areDocsEqualByBlockStructureAndText.js +0 -50
- package/dist/esm/pm-plugins/utils/hasVisibleContent.js +0 -32
- package/dist/types/pm-plugins/areDocsEqualByBlockStructureAndText.d.ts +0 -12
- package/dist/types/pm-plugins/utils/hasVisibleContent.d.ts +0 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-show-diff
|
|
2
2
|
|
|
3
|
+
## 16.0.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`b56b3a7dbfb5a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b56b3a7dbfb5a) -
|
|
8
|
+
[ux] Show contributor tags at the top-left of removed table rows and above table interaction
|
|
9
|
+
overlays and sticky header rows. Behind `confluence_ncs_step_diffing_version_history`.
|
|
10
|
+
- Updated dependencies
|
|
11
|
+
|
|
12
|
+
## 16.0.15
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [`442abbe2a88e0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/442abbe2a88e0) -
|
|
17
|
+
Normalize documents before relaxed show-diff comparisons when Confluence version-history diffing
|
|
18
|
+
is enabled.
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
|
|
21
|
+
## 16.0.14
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- [`6667b6ae8e77c`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6667b6ae8e77c) -
|
|
26
|
+
Ensure contributor tags are rendered for shown attributed diff content, including whitespace-only
|
|
27
|
+
changes and deletion widgets, without tagging structurally empty paragraphs.
|
|
28
|
+
|
|
3
29
|
## 16.0.13
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
|
@@ -11,13 +11,13 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
|
|
|
11
11
|
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
|
|
12
12
|
var _memoizeOne = _interopRequireDefault(require("memoize-one"));
|
|
13
13
|
var _prosemirrorChangeset = require("prosemirror-changeset");
|
|
14
|
+
var _areDocsEqualByBlockStructureAndText = require("@atlaskit/editor-common/utils/areDocsEqualByBlockStructureAndText");
|
|
14
15
|
var _document = require("@atlaskit/editor-common/utils/document");
|
|
15
16
|
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
16
17
|
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
17
18
|
var _unsafeExpValNoExposure = require("@atlaskit/platform-feature-experiments/unsafe-exp-val-no-exposure");
|
|
18
19
|
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
19
20
|
var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
|
|
20
|
-
var _areDocsEqualByBlockStructureAndText = require("../areDocsEqualByBlockStructureAndText");
|
|
21
21
|
var _createAnchorDecorationWidgets = require("../decorations/createAnchorDecorationWidgets");
|
|
22
22
|
var _createBlockChangedDecoration = require("../decorations/createBlockChangedDecoration");
|
|
23
23
|
var _createInlineChangedDecoration = require("../decorations/createInlineChangedDecoration");
|
|
@@ -19,6 +19,7 @@ var _factory = require("./colorSchemes/factory");
|
|
|
19
19
|
var _schemes = require("./colorSchemes/schemes");
|
|
20
20
|
var _createAnchorDecorationWidgets = require("./createAnchorDecorationWidgets");
|
|
21
21
|
var _createChangedRowDecorationWidgetsStyles = require("./createChangedRowDecorationWidgets.styles.legacy");
|
|
22
|
+
var _createContributorTagWidget = require("./createContributorTagWidget");
|
|
22
23
|
var _decorationKeys = require("./decorationKeys");
|
|
23
24
|
var _findSafeInsertPos = require("./utils/findSafeInsertPos");
|
|
24
25
|
var _tableCellEdgeAttrs = require("./utils/tableCellEdgeAttrs");
|
|
@@ -105,20 +106,40 @@ var extractChangedRows = function extractChangedRows(_ref) {
|
|
|
105
106
|
}
|
|
106
107
|
});
|
|
107
108
|
|
|
108
|
-
// Filter changes that never truly got deleted
|
|
109
|
+
// Filter changes that never truly got deleted. Compare the number of equivalent rows rather
|
|
110
|
+
// than only checking whether one still exists: deleting two of three identical empty rows should
|
|
111
|
+
// retain two changed rows even though the third remains in the new table.
|
|
112
|
+
var equivalentRowDeletionCounts = [];
|
|
109
113
|
return changedRows.filter(function (changedRow) {
|
|
110
114
|
// A replacement leaves every sibling row in place, so asking "does this row still exist
|
|
111
115
|
// ANYWHERE in the new table?" can drop a genuinely rewritten row that happens to match a
|
|
112
|
-
// sibling. Compare it against the row at the same index instead.
|
|
113
|
-
// whole-table `some()`: their indices shift, so an index comparison would let every
|
|
114
|
-
// surviving row through and draw a widget for each one.
|
|
116
|
+
// sibling. Compare it against the row at the same index instead.
|
|
115
117
|
if (handleRowReplacement) {
|
|
116
118
|
var counterpart = tableNew.node.maybeChild(changedRow.rowIndex);
|
|
117
119
|
return !counterpart || !(0, _document.areNodesEqualIgnoreAttrs)(counterpart, changedRow.rowNode);
|
|
118
120
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
var deletionCount = equivalentRowDeletionCounts.find(function (_ref2) {
|
|
122
|
+
var rowNode = _ref2.rowNode;
|
|
123
|
+
return (0, _document.areNodesEqualIgnoreAttrs)(rowNode, changedRow.rowNode);
|
|
121
124
|
});
|
|
125
|
+
if (!deletionCount) {
|
|
126
|
+
var oldRowCount = tableOld.node.children.filter(function (oldRow) {
|
|
127
|
+
return (0, _document.areNodesEqualIgnoreAttrs)(oldRow, changedRow.rowNode);
|
|
128
|
+
}).length;
|
|
129
|
+
var newRowCount = tableNew.node.children.filter(function (newRow) {
|
|
130
|
+
return (0, _document.areNodesEqualIgnoreAttrs)(newRow, changedRow.rowNode);
|
|
131
|
+
}).length;
|
|
132
|
+
deletionCount = {
|
|
133
|
+
remaining: Math.max(0, oldRowCount - newRowCount),
|
|
134
|
+
rowNode: changedRow.rowNode
|
|
135
|
+
};
|
|
136
|
+
equivalentRowDeletionCounts.push(deletionCount);
|
|
137
|
+
}
|
|
138
|
+
if (deletionCount.remaining === 0) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
deletionCount.remaining--;
|
|
142
|
+
return true;
|
|
122
143
|
});
|
|
123
144
|
};
|
|
124
145
|
|
|
@@ -201,11 +222,11 @@ var createChangedRowDOM = function createChangedRowDOM(rowNode, cellEdgeAttrs, n
|
|
|
201
222
|
/**
|
|
202
223
|
* Expands a diff to include whole changed rows when table rows are affected
|
|
203
224
|
*/
|
|
204
|
-
var expandDiffForChangedRows = function expandDiffForChangedRows(
|
|
205
|
-
var changes =
|
|
206
|
-
originalDoc =
|
|
207
|
-
newDoc =
|
|
208
|
-
diffType =
|
|
225
|
+
var expandDiffForChangedRows = function expandDiffForChangedRows(_ref3) {
|
|
226
|
+
var changes = _ref3.changes,
|
|
227
|
+
originalDoc = _ref3.originalDoc,
|
|
228
|
+
newDoc = _ref3.newDoc,
|
|
229
|
+
diffType = _ref3.diffType;
|
|
209
230
|
var rowInfo = [];
|
|
210
231
|
var _iterator = _createForOfIteratorHelper(changes),
|
|
211
232
|
_step;
|
|
@@ -234,17 +255,22 @@ var expandDiffForChangedRows = function expandDiffForChangedRows(_ref2) {
|
|
|
234
255
|
/**
|
|
235
256
|
* Main function to handle deleted rows - computes diff and creates decorations
|
|
236
257
|
*/
|
|
237
|
-
var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidgets = function createChangedRowDecorationWidgets(
|
|
238
|
-
var
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
258
|
+
var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidgets = function createChangedRowDecorationWidgets(_ref4) {
|
|
259
|
+
var attributionKey = _ref4.attributionKey,
|
|
260
|
+
changes = _ref4.changes,
|
|
261
|
+
originalDoc = _ref4.originalDoc,
|
|
262
|
+
newDoc = _ref4.newDoc,
|
|
263
|
+
nodeViewSerializer = _ref4.nodeViewSerializer,
|
|
264
|
+
colorScheme = _ref4.colorScheme,
|
|
265
|
+
isActive = _ref4.isActive,
|
|
266
|
+
_ref4$isInserted = _ref4.isInserted,
|
|
267
|
+
isInserted = _ref4$isInserted === void 0 ? false : _ref4$isInserted,
|
|
268
|
+
diffType = _ref4.diffType,
|
|
269
|
+
_ref4$showIndicators = _ref4.showIndicators,
|
|
270
|
+
showIndicators = _ref4$showIndicators === void 0 ? false : _ref4$showIndicators,
|
|
271
|
+
_ref4$showContributor = _ref4.showContributorTags,
|
|
272
|
+
showContributorTags = _ref4$showContributor === void 0 ? false : _ref4$showContributor,
|
|
273
|
+
tagMountContext = _ref4.tagMountContext;
|
|
248
274
|
// First, expand the changes to include complete deleted rows
|
|
249
275
|
var changedRows = expandDiffForChangedRows({
|
|
250
276
|
changes: changes.filter(function (change) {
|
|
@@ -261,8 +287,12 @@ var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidget
|
|
|
261
287
|
var safeInsertPos = (0, _findSafeInsertPos.findSafeInsertPos)(newDoc, changedRow.fromB - 1,
|
|
262
288
|
// -1 to find the first safe position from the table
|
|
263
289
|
originalDoc.slice(changedRow.fromA, changedRow.toA));
|
|
264
|
-
|
|
290
|
+
|
|
291
|
+
// Stable while contributor tags are enabled so a decoration recalculation preserves the tag's
|
|
292
|
+
// identity and controller state.
|
|
293
|
+
var diffId = showContributorTags ? "widget-row-".concat(changedRow.fromA, "-").concat(changedRow.toA) : crypto.randomUUID();
|
|
265
294
|
var decorations = [];
|
|
295
|
+
var rowAnchorNames = [];
|
|
266
296
|
|
|
267
297
|
// `IndicatorBarContentComponent` renders a bar for every widget descriptor and anchors its
|
|
268
298
|
// top and bottom to `anchor-<diffId>`. Without an element carrying that anchor name the bar
|
|
@@ -270,9 +300,9 @@ var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidget
|
|
|
270
300
|
// bar covers only the row being changed. `createNodeChangedDecorationWidget` sets the same
|
|
271
301
|
// property on its own widget DOM for non-table content; this path returns before reaching it.
|
|
272
302
|
if (showIndicators && (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && (0, _fg.fg)('platform_editor_ai_show_diff_patch_1')) {
|
|
273
|
-
|
|
303
|
+
rowAnchorNames.push((0, _decorationKeys.buildAnchorDecorationKey)({
|
|
274
304
|
diffId: diffId
|
|
275
|
-
}))
|
|
305
|
+
}));
|
|
276
306
|
|
|
277
307
|
// A table's content can extend past the doc margin, so the bar also needs a left anchor
|
|
278
308
|
// measured against the table itself or it is clipped once the table is resized.
|
|
@@ -285,14 +315,47 @@ var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidget
|
|
|
285
315
|
decorations.push(leftAnchor);
|
|
286
316
|
}
|
|
287
317
|
}
|
|
288
|
-
|
|
318
|
+
var tagAnchorName = showContributorTags && (0, _createContributorTagWidget.isContributorTagWidgetEnabled)() ? (0, _decorationKeys.buildAnchorDecorationKey)({
|
|
319
|
+
diffId: diffId,
|
|
320
|
+
anchorType: _decorationKeys.AnchorTypeKey.tag
|
|
321
|
+
}) : undefined;
|
|
322
|
+
var tagWidget = tagAnchorName ? (0, _createContributorTagWidget.createContributorTagWidget)({
|
|
323
|
+
anchorAtRangeStart: true,
|
|
324
|
+
anchorName: tagAnchorName,
|
|
325
|
+
diffId: diffId,
|
|
326
|
+
doc: newDoc,
|
|
327
|
+
from: safeInsertPos,
|
|
328
|
+
mountContext: tagMountContext,
|
|
329
|
+
to: safeInsertPos
|
|
330
|
+
}) : undefined;
|
|
331
|
+
if (tagAnchorName) {
|
|
332
|
+
// The tag host is a separate, table-safe widget. CSS anchor positioning aligns it to this
|
|
333
|
+
// synthetic row without placing non-cell DOM inside the `<tr>` or inheriting its deletion
|
|
334
|
+
// opacity and strikethrough.
|
|
335
|
+
rowAnchorNames.push(tagAnchorName);
|
|
336
|
+
}
|
|
337
|
+
if (rowAnchorNames.length > 0) {
|
|
338
|
+
rowDOM.style.setProperty('anchor-name', rowAnchorNames.map(function (anchorName) {
|
|
339
|
+
return "--".concat(anchorName);
|
|
340
|
+
}).join(', '));
|
|
341
|
+
}
|
|
342
|
+
if (tagWidget) {
|
|
343
|
+
// Lets the contributor tag reveal when any part of the deleted row is hovered.
|
|
344
|
+
rowDOM.setAttribute('data-diff-id', diffId);
|
|
345
|
+
}
|
|
346
|
+
var rowWidget = _view.Decoration.widget(safeInsertPos, rowDOM, _objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)({
|
|
347
|
+
attributionKey: tagWidget ? attributionKey : undefined,
|
|
289
348
|
colorScheme: colorScheme,
|
|
290
349
|
decorationType: 'widget',
|
|
291
350
|
diffId: diffId,
|
|
292
|
-
isActive:
|
|
351
|
+
isActive: isActive,
|
|
293
352
|
isInserted: isInserted,
|
|
294
353
|
diffType: diffType
|
|
295
|
-
})))
|
|
354
|
+
})));
|
|
355
|
+
decorations.push(rowWidget);
|
|
356
|
+
if (tagWidget) {
|
|
357
|
+
decorations.push(tagWidget);
|
|
358
|
+
}
|
|
296
359
|
return decorations;
|
|
297
360
|
});
|
|
298
361
|
};
|
|
@@ -12,7 +12,7 @@ var _view = require("@atlaskit/editor-prosemirror/view");
|
|
|
12
12
|
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
|
|
13
13
|
var _buildContributorTagDom = require("../../ui/ContributorTag/buildContributorTagDom");
|
|
14
14
|
var _isExtendedEnabled = require("../isExtendedEnabled");
|
|
15
|
-
var
|
|
15
|
+
var _isEmptyParagraphSlice = require("../utils/isEmptyParagraphSlice");
|
|
16
16
|
var _factory = require("./colorSchemes/factory");
|
|
17
17
|
var _schemes = require("./colorSchemes/schemes");
|
|
18
18
|
var _createAnchorDecorationWidgets = require("./createAnchorDecorationWidgets");
|
|
@@ -163,11 +163,9 @@ var createInlineChangedDecoration = exports.createInlineChangedDecoration = func
|
|
|
163
163
|
// tag host's key, remounting every tag mid-step. Random ids are kept when tags are off.
|
|
164
164
|
var diffId = showContributorTags ? "inline-".concat(change.fromB, "-").concat(change.toB) : crypto.randomUUID();
|
|
165
165
|
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
// a host that is never emitted (EDITOR-8855).
|
|
170
|
-
var canTagChange = showContributorTags && !!doc && (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && (0, _createContributorTagWidget.isContributorTagWidgetEnabled)() && (0, _hasVisibleContent.hasVisibleContent)(doc.slice(change.fromB, change.toB).content);
|
|
166
|
+
// Match the rendered shape: whitespace text is eligible, while an empty paragraph is structural
|
|
167
|
+
// content with no tag host.
|
|
168
|
+
var canTagChange = showContributorTags && !!doc && (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && (0, _createContributorTagWidget.isContributorTagWidgetEnabled)() && !(0, _isEmptyParagraphSlice.isEmptyParagraphSlice)(doc.slice(change.fromB, change.toB));
|
|
171
169
|
if (shouldHideDeleted) {
|
|
172
170
|
return [_view.Decoration.inline(change.fromB, change.toB, {
|
|
173
171
|
style: displayNoneStyle
|
|
@@ -9,7 +9,7 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
|
|
|
9
9
|
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
10
10
|
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
11
11
|
var _isExtendedEnabled = require("../isExtendedEnabled");
|
|
12
|
-
var
|
|
12
|
+
var _isEmptyParagraphSlice = require("../utils/isEmptyParagraphSlice");
|
|
13
13
|
var _createAnchorDecorationWidgets = require("./createAnchorDecorationWidgets");
|
|
14
14
|
var _createChangedRowDecorationWidgets = require("./createChangedRowDecorationWidgets");
|
|
15
15
|
var _createContributorTagWidget = require("./createContributorTagWidget");
|
|
@@ -101,7 +101,7 @@ var createTableCellContentWidgets = function createTableCellContentWidgets(_ref)
|
|
|
101
101
|
* that is not in the current document.
|
|
102
102
|
*/
|
|
103
103
|
var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidget = function createNodeChangedDecorationWidget(_ref2) {
|
|
104
|
-
var
|
|
104
|
+
var _$safeInsertPos$nodeB, _slice$content$firstC, _newDoc$firstChild, _slice$content$firstC2;
|
|
105
105
|
var attributionKey = _ref2.attributionKey,
|
|
106
106
|
change = _ref2.change,
|
|
107
107
|
doc = _ref2.doc,
|
|
@@ -124,7 +124,7 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
124
124
|
reveal = _ref2.reveal,
|
|
125
125
|
tagMountContext = _ref2.tagMountContext;
|
|
126
126
|
var slice = doc.slice(change.fromA, change.toA);
|
|
127
|
-
var shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (
|
|
127
|
+
var shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (0, _isEmptyParagraphSlice.isEmptyParagraphSlice)(slice);
|
|
128
128
|
// Widget decoration used for deletions as the content is not in the document
|
|
129
129
|
// and we want to display the deleted content with a style.
|
|
130
130
|
// For `placeBelow`, anchor at the END of the new content (change.toB) so the deleted
|
|
@@ -166,16 +166,20 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
166
166
|
}
|
|
167
167
|
if (isTableRowContent) {
|
|
168
168
|
return (0, _createChangedRowDecorationWidgets.createChangedRowDecorationWidgets)({
|
|
169
|
+
attributionKey: attributionKey,
|
|
169
170
|
changes: [change],
|
|
170
171
|
originalDoc: doc,
|
|
171
172
|
newDoc: newDoc,
|
|
172
173
|
nodeViewSerializer: nodeViewSerializer,
|
|
173
174
|
colorScheme: colorScheme,
|
|
175
|
+
isActive: isActive,
|
|
174
176
|
isInserted: isInserted,
|
|
175
177
|
diffType: diffType,
|
|
176
178
|
// Needed for the row's own indicator anchor; this path returns before the
|
|
177
179
|
// `anchor-name` assignment further down.
|
|
178
|
-
showIndicators: showIndicators
|
|
180
|
+
showIndicators: showIndicators,
|
|
181
|
+
showContributorTags: showContributorTags,
|
|
182
|
+
tagMountContext: tagMountContext
|
|
179
183
|
});
|
|
180
184
|
}
|
|
181
185
|
var serializer = nodeViewSerializer;
|
|
@@ -226,10 +230,9 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
226
230
|
// Derived from the deleted range so it survives a recalculation — see the same reasoning in
|
|
227
231
|
// `createInlineChangedDecoration`.
|
|
228
232
|
var diffId = showContributorTags ? "widget-".concat(change.fromA, "-").concat(change.toA) : crypto.randomUUID();
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
var canTagWidget = showContributorTags && (0, _hasVisibleContent.hasVisibleContent)(slice.content);
|
|
233
|
+
// Match the rendering predicate above: an empty paragraph has no widget to tag, while whitespace
|
|
234
|
+
// text in a rendered widget remains eligible.
|
|
235
|
+
var canTagWidget = showContributorTags && !shouldSkipDeletedEmptyParagraphDecoration;
|
|
233
236
|
var decorations = [];
|
|
234
237
|
var replacementNode = newDoc.nodeAt(change.fromB);
|
|
235
238
|
var firstReplacedNode = slice.content.firstChild;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isEmptyParagraphSlice = void 0;
|
|
7
|
+
/** Whether a slice contains exactly one structurally empty paragraph. */
|
|
8
|
+
var isEmptyParagraphSlice = exports.isEmptyParagraphSlice = function isEmptyParagraphSlice(slice) {
|
|
9
|
+
var _slice$content$firstC;
|
|
10
|
+
return (slice === null || slice === void 0 ? void 0 : slice.content.childCount) === 1 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.type.name) === 'paragraph' && slice.content.firstChild.content.size === 0;
|
|
11
|
+
};
|
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.buildContributorTagDom = exports.TAG_EXIT_FALLBACK_MS = exports.MAX_TAG_WIDTH = exports.CONTRIBUTOR_TAG_Z_INDEX = exports.CONTRIBUTOR_TAG_TOOLTIP_STYLES = exports.CONTRIBUTOR_TAG_TESTID = exports.CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE = exports.CONTRIBUTOR_TAG_NAME_TESTID = exports.CONTRIBUTOR_TAG_CLASS = void 0;
|
|
8
8
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
9
|
var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
|
|
10
|
+
var _constants = require("@atlaskit/editor-shared-styles/constants");
|
|
10
11
|
var CONTRIBUTOR_TAG_TESTID = exports.CONTRIBUTOR_TAG_TESTID = 'diff-contributor-tag';
|
|
11
12
|
var CONTRIBUTOR_TAG_NAME_TESTID = exports.CONTRIBUTOR_TAG_NAME_TESTID = 'diff-contributor-tag-name';
|
|
12
13
|
|
|
@@ -20,15 +21,15 @@ var MAX_TAG_WIDTH = exports.MAX_TAG_WIDTH = '200px';
|
|
|
20
21
|
/**
|
|
21
22
|
* The tag sits on the line above the change, so it has to paint over that line to be readable —
|
|
22
23
|
* including any diff highlight already on it, which would otherwise slice straight through the tag.
|
|
23
|
-
*
|
|
24
|
-
* change's own highlight is stacked one level below it — see
|
|
25
|
-
* `createInlineChangedDecoration`.
|
|
24
|
+
* It also has to paint above table interaction overlays and sticky-capable header rows, which use
|
|
25
|
+
* `akEditorSmallZIndex`. The change's own highlight is stacked one level below it — see
|
|
26
|
+
* `stackBelowContributorTagStyle` in `createInlineChangedDecoration`.
|
|
26
27
|
*
|
|
27
28
|
* Painting above its own highlight is only safe because the tag can never reach it: `bottom: 100%`
|
|
28
29
|
* puts the root's bottom edge exactly on the highlight's top edge, and the root's `overflow: clip`
|
|
29
30
|
* keeps every pixel the tag paints inside it.
|
|
30
31
|
*/
|
|
31
|
-
var CONTRIBUTOR_TAG_Z_INDEX = exports.CONTRIBUTOR_TAG_Z_INDEX =
|
|
32
|
+
var CONTRIBUTOR_TAG_Z_INDEX = exports.CONTRIBUTOR_TAG_Z_INDEX = _constants.akEditorSmallZIndex + 1;
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* The plugin's whole side of the tag's motion: the fade and the hidden state are
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import isEqual from 'lodash/isEqual';
|
|
2
2
|
import memoizeOne from 'memoize-one';
|
|
3
3
|
import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
|
|
4
|
+
import { areDocsEqualByBlockStructureAndText } from '@atlaskit/editor-common/utils/areDocsEqualByBlockStructureAndText';
|
|
4
5
|
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
|
|
5
6
|
import { Mapping } from '@atlaskit/editor-prosemirror/transform';
|
|
6
7
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
7
8
|
import { UNSAFE_expValNoExposure } from '@atlaskit/platform-feature-experiments/unsafe-exp-val-no-exposure';
|
|
8
9
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
9
10
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
10
|
-
import { areDocsEqualByBlockStructureAndText } from '../areDocsEqualByBlockStructureAndText';
|
|
11
11
|
import { createDocMarginAnchorWidget } from '../decorations/createAnchorDecorationWidgets';
|
|
12
12
|
import { createBlockChangedDecoration } from '../decorations/createBlockChangedDecoration';
|
|
13
13
|
import { createInlineChangedDecoration } from '../decorations/createInlineChangedDecoration';
|
|
@@ -10,7 +10,8 @@ import { buildAddedCellOverlayRoundedStyle, buildAddedCellOverlayStyle, buildDel
|
|
|
10
10
|
import { colorSchemeRegistry, getLegacyColorScheme } from './colorSchemes/schemes';
|
|
11
11
|
import { createLeftAnchorWidget } from './createAnchorDecorationWidgets';
|
|
12
12
|
import { resolveCellOverlayStyleLegacy, resolveDeletedRowStyleLegacy } from './createChangedRowDecorationWidgets.styles.legacy';
|
|
13
|
-
import {
|
|
13
|
+
import { createContributorTagWidget, isContributorTagWidgetEnabled } from './createContributorTagWidget';
|
|
14
|
+
import { AnchorTypeKey, buildAnchorDecorationKey, buildDiffDecorationSpec } from './decorationKeys';
|
|
14
15
|
import { findSafeInsertPos } from './utils/findSafeInsertPos';
|
|
15
16
|
import { applyCellEdgeAttrs, getRowCellEdgeAttrs, getTableTopAndBottomCellEdgeAttrs } from './utils/tableCellEdgeAttrs';
|
|
16
17
|
/**
|
|
@@ -86,18 +87,35 @@ const extractChangedRows = ({
|
|
|
86
87
|
}
|
|
87
88
|
});
|
|
88
89
|
|
|
89
|
-
// Filter changes that never truly got deleted
|
|
90
|
+
// Filter changes that never truly got deleted. Compare the number of equivalent rows rather
|
|
91
|
+
// than only checking whether one still exists: deleting two of three identical empty rows should
|
|
92
|
+
// retain two changed rows even though the third remains in the new table.
|
|
93
|
+
const equivalentRowDeletionCounts = [];
|
|
90
94
|
return changedRows.filter(changedRow => {
|
|
91
95
|
// A replacement leaves every sibling row in place, so asking "does this row still exist
|
|
92
96
|
// ANYWHERE in the new table?" can drop a genuinely rewritten row that happens to match a
|
|
93
|
-
// sibling. Compare it against the row at the same index instead.
|
|
94
|
-
// whole-table `some()`: their indices shift, so an index comparison would let every
|
|
95
|
-
// surviving row through and draw a widget for each one.
|
|
97
|
+
// sibling. Compare it against the row at the same index instead.
|
|
96
98
|
if (handleRowReplacement) {
|
|
97
99
|
const counterpart = tableNew.node.maybeChild(changedRow.rowIndex);
|
|
98
100
|
return !counterpart || !areNodesEqualIgnoreAttrs(counterpart, changedRow.rowNode);
|
|
99
101
|
}
|
|
100
|
-
|
|
102
|
+
let deletionCount = equivalentRowDeletionCounts.find(({
|
|
103
|
+
rowNode
|
|
104
|
+
}) => areNodesEqualIgnoreAttrs(rowNode, changedRow.rowNode));
|
|
105
|
+
if (!deletionCount) {
|
|
106
|
+
const oldRowCount = tableOld.node.children.filter(oldRow => areNodesEqualIgnoreAttrs(oldRow, changedRow.rowNode)).length;
|
|
107
|
+
const newRowCount = tableNew.node.children.filter(newRow => areNodesEqualIgnoreAttrs(newRow, changedRow.rowNode)).length;
|
|
108
|
+
deletionCount = {
|
|
109
|
+
remaining: Math.max(0, oldRowCount - newRowCount),
|
|
110
|
+
rowNode: changedRow.rowNode
|
|
111
|
+
};
|
|
112
|
+
equivalentRowDeletionCounts.push(deletionCount);
|
|
113
|
+
}
|
|
114
|
+
if (deletionCount.remaining === 0) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
deletionCount.remaining--;
|
|
118
|
+
return true;
|
|
101
119
|
});
|
|
102
120
|
};
|
|
103
121
|
|
|
@@ -206,14 +224,18 @@ const expandDiffForChangedRows = ({
|
|
|
206
224
|
* Main function to handle deleted rows - computes diff and creates decorations
|
|
207
225
|
*/
|
|
208
226
|
export const createChangedRowDecorationWidgets = ({
|
|
227
|
+
attributionKey,
|
|
209
228
|
changes,
|
|
210
229
|
originalDoc,
|
|
211
230
|
newDoc,
|
|
212
231
|
nodeViewSerializer,
|
|
213
232
|
colorScheme,
|
|
233
|
+
isActive,
|
|
214
234
|
isInserted = false,
|
|
215
235
|
diffType,
|
|
216
|
-
showIndicators = false
|
|
236
|
+
showIndicators = false,
|
|
237
|
+
showContributorTags = false,
|
|
238
|
+
tagMountContext
|
|
217
239
|
}) => {
|
|
218
240
|
// First, expand the changes to include complete deleted rows
|
|
219
241
|
const changedRows = expandDiffForChangedRows({
|
|
@@ -229,8 +251,12 @@ export const createChangedRowDecorationWidgets = ({
|
|
|
229
251
|
const safeInsertPos = findSafeInsertPos(newDoc, changedRow.fromB - 1,
|
|
230
252
|
// -1 to find the first safe position from the table
|
|
231
253
|
originalDoc.slice(changedRow.fromA, changedRow.toA));
|
|
232
|
-
|
|
254
|
+
|
|
255
|
+
// Stable while contributor tags are enabled so a decoration recalculation preserves the tag's
|
|
256
|
+
// identity and controller state.
|
|
257
|
+
const diffId = showContributorTags ? `widget-row-${changedRow.fromA}-${changedRow.toA}` : crypto.randomUUID();
|
|
233
258
|
const decorations = [];
|
|
259
|
+
const rowAnchorNames = [];
|
|
234
260
|
|
|
235
261
|
// `IndicatorBarContentComponent` renders a bar for every widget descriptor and anchors its
|
|
236
262
|
// top and bottom to `anchor-<diffId>`. Without an element carrying that anchor name the bar
|
|
@@ -238,9 +264,9 @@ export const createChangedRowDecorationWidgets = ({
|
|
|
238
264
|
// bar covers only the row being changed. `createNodeChangedDecorationWidget` sets the same
|
|
239
265
|
// property on its own widget DOM for non-table content; this path returns before reaching it.
|
|
240
266
|
if (showIndicators && isExtendedEnabled(diffType) && fg('platform_editor_ai_show_diff_patch_1')) {
|
|
241
|
-
|
|
267
|
+
rowAnchorNames.push(buildAnchorDecorationKey({
|
|
242
268
|
diffId
|
|
243
|
-
})
|
|
269
|
+
}));
|
|
244
270
|
|
|
245
271
|
// A table's content can extend past the doc margin, so the bar also needs a left anchor
|
|
246
272
|
// measured against the table itself or it is clipped once the table is resized.
|
|
@@ -253,16 +279,47 @@ export const createChangedRowDecorationWidgets = ({
|
|
|
253
279
|
decorations.push(leftAnchor);
|
|
254
280
|
}
|
|
255
281
|
}
|
|
256
|
-
|
|
282
|
+
const tagAnchorName = showContributorTags && isContributorTagWidgetEnabled() ? buildAnchorDecorationKey({
|
|
283
|
+
diffId,
|
|
284
|
+
anchorType: AnchorTypeKey.tag
|
|
285
|
+
}) : undefined;
|
|
286
|
+
const tagWidget = tagAnchorName ? createContributorTagWidget({
|
|
287
|
+
anchorAtRangeStart: true,
|
|
288
|
+
anchorName: tagAnchorName,
|
|
289
|
+
diffId,
|
|
290
|
+
doc: newDoc,
|
|
291
|
+
from: safeInsertPos,
|
|
292
|
+
mountContext: tagMountContext,
|
|
293
|
+
to: safeInsertPos
|
|
294
|
+
}) : undefined;
|
|
295
|
+
if (tagAnchorName) {
|
|
296
|
+
// The tag host is a separate, table-safe widget. CSS anchor positioning aligns it to this
|
|
297
|
+
// synthetic row without placing non-cell DOM inside the `<tr>` or inheriting its deletion
|
|
298
|
+
// opacity and strikethrough.
|
|
299
|
+
rowAnchorNames.push(tagAnchorName);
|
|
300
|
+
}
|
|
301
|
+
if (rowAnchorNames.length > 0) {
|
|
302
|
+
rowDOM.style.setProperty('anchor-name', rowAnchorNames.map(anchorName => `--${anchorName}`).join(', '));
|
|
303
|
+
}
|
|
304
|
+
if (tagWidget) {
|
|
305
|
+
// Lets the contributor tag reveal when any part of the deleted row is hovered.
|
|
306
|
+
rowDOM.setAttribute('data-diff-id', diffId);
|
|
307
|
+
}
|
|
308
|
+
const rowWidget = Decoration.widget(safeInsertPos, rowDOM, {
|
|
257
309
|
...buildDiffDecorationSpec({
|
|
310
|
+
attributionKey: tagWidget ? attributionKey : undefined,
|
|
258
311
|
colorScheme,
|
|
259
312
|
decorationType: 'widget',
|
|
260
313
|
diffId,
|
|
261
|
-
isActive
|
|
314
|
+
isActive,
|
|
262
315
|
isInserted,
|
|
263
316
|
diffType
|
|
264
317
|
})
|
|
265
|
-
})
|
|
318
|
+
});
|
|
319
|
+
decorations.push(rowWidget);
|
|
320
|
+
if (tagWidget) {
|
|
321
|
+
decorations.push(tagWidget);
|
|
322
|
+
}
|
|
266
323
|
return decorations;
|
|
267
324
|
});
|
|
268
325
|
};
|
|
@@ -3,7 +3,7 @@ import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
|
3
3
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
4
4
|
import { CONTRIBUTOR_TAG_Z_INDEX } from '../../ui/ContributorTag/buildContributorTagDom';
|
|
5
5
|
import { isExtendedEnabled } from '../isExtendedEnabled';
|
|
6
|
-
import {
|
|
6
|
+
import { isEmptyParagraphSlice } from '../utils/isEmptyParagraphSlice';
|
|
7
7
|
import { buildAtomicInlineChangedCSSVariables, buildDeletedInlineContentStyleExtended, buildDeletedInlineStyle, buildDeletedInlineStyleStandard, buildInsertStyle, buildInsertStyleActive, buildInsertStyleExtended, buildInsertStyleExtendedActive, buildInsertStyleExtendedNoUnderline, buildInsertStyleExtendedNoUnderlineActive } from './colorSchemes/factory';
|
|
8
8
|
import { colorSchemeRegistry, getLegacyColorScheme } from './colorSchemes/schemes';
|
|
9
9
|
import { createInlineIndicatorAnchorWidgets } from './createAnchorDecorationWidgets';
|
|
@@ -138,11 +138,9 @@ export const createInlineChangedDecoration = ({
|
|
|
138
138
|
// tag host's key, remounting every tag mid-step. Random ids are kept when tags are off.
|
|
139
139
|
const diffId = showContributorTags ? `inline-${change.fromB}-${change.toB}` : crypto.randomUUID();
|
|
140
140
|
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
// a host that is never emitted (EDITOR-8855).
|
|
145
|
-
const canTagChange = showContributorTags && !!doc && isExtendedEnabled(diffType) && isContributorTagWidgetEnabled() && hasVisibleContent(doc.slice(change.fromB, change.toB).content);
|
|
141
|
+
// Match the rendered shape: whitespace text is eligible, while an empty paragraph is structural
|
|
142
|
+
// content with no tag host.
|
|
143
|
+
const canTagChange = showContributorTags && !!doc && isExtendedEnabled(diffType) && isContributorTagWidgetEnabled() && !isEmptyParagraphSlice(doc.slice(change.fromB, change.toB));
|
|
146
144
|
if (shouldHideDeleted) {
|
|
147
145
|
return [Decoration.inline(change.fromB, change.toB, {
|
|
148
146
|
style: displayNoneStyle
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
2
2
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
3
3
|
import { isExtendedEnabled } from '../isExtendedEnabled';
|
|
4
|
-
import {
|
|
4
|
+
import { isEmptyParagraphSlice } from '../utils/isEmptyParagraphSlice';
|
|
5
5
|
import { createLeftAnchorWidget } from './createAnchorDecorationWidgets';
|
|
6
6
|
import { createChangedRowDecorationWidgets } from './createChangedRowDecorationWidgets';
|
|
7
7
|
import { createContributorTagHost, unmountContributorTag } from './createContributorTagWidget';
|
|
@@ -114,9 +114,9 @@ export const createNodeChangedDecorationWidget = ({
|
|
|
114
114
|
reveal,
|
|
115
115
|
tagMountContext
|
|
116
116
|
}) => {
|
|
117
|
-
var
|
|
117
|
+
var _$safeInsertPos$nodeB, _slice$content$firstC, _newDoc$firstChild, _slice$content$firstC2;
|
|
118
118
|
const slice = doc.slice(change.fromA, change.toA);
|
|
119
|
-
const shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (slice
|
|
119
|
+
const shouldSkipDeletedEmptyParagraphDecoration = !isInserted && isEmptyParagraphSlice(slice);
|
|
120
120
|
// Widget decoration used for deletions as the content is not in the document
|
|
121
121
|
// and we want to display the deleted content with a style.
|
|
122
122
|
// For `placeBelow`, anchor at the END of the new content (change.toB) so the deleted
|
|
@@ -150,16 +150,20 @@ export const createNodeChangedDecorationWidget = ({
|
|
|
150
150
|
}
|
|
151
151
|
if (isTableRowContent) {
|
|
152
152
|
return createChangedRowDecorationWidgets({
|
|
153
|
+
attributionKey,
|
|
153
154
|
changes: [change],
|
|
154
155
|
originalDoc: doc,
|
|
155
156
|
newDoc,
|
|
156
157
|
nodeViewSerializer,
|
|
157
158
|
colorScheme,
|
|
159
|
+
isActive,
|
|
158
160
|
isInserted,
|
|
159
161
|
diffType,
|
|
160
162
|
// Needed for the row's own indicator anchor; this path returns before the
|
|
161
163
|
// `anchor-name` assignment further down.
|
|
162
|
-
showIndicators
|
|
164
|
+
showIndicators,
|
|
165
|
+
showContributorTags,
|
|
166
|
+
tagMountContext
|
|
163
167
|
});
|
|
164
168
|
}
|
|
165
169
|
const serializer = nodeViewSerializer;
|
|
@@ -210,10 +214,9 @@ export const createNodeChangedDecorationWidget = ({
|
|
|
210
214
|
// Derived from the deleted range so it survives a recalculation — see the same reasoning in
|
|
211
215
|
// `createInlineChangedDecoration`.
|
|
212
216
|
const diffId = showContributorTags ? `widget-${change.fromA}-${change.toA}` : crypto.randomUUID();
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
const canTagWidget = showContributorTags && hasVisibleContent(slice.content);
|
|
217
|
+
// Match the rendering predicate above: an empty paragraph has no widget to tag, while whitespace
|
|
218
|
+
// text in a rendered widget remains eligible.
|
|
219
|
+
const canTagWidget = showContributorTags && !shouldSkipDeletedEmptyParagraphDecoration;
|
|
217
220
|
const decorations = [];
|
|
218
221
|
const replacementNode = newDoc.nodeAt(change.fromB);
|
|
219
222
|
const firstReplacedNode = slice.content.firstChild;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Whether a slice contains exactly one structurally empty paragraph. */
|
|
2
|
+
export const isEmptyParagraphSlice = slice => {
|
|
3
|
+
var _slice$content$firstC;
|
|
4
|
+
return (slice === null || slice === void 0 ? void 0 : slice.content.childCount) === 1 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.type.name) === 'paragraph' && slice.content.firstChild.content.size === 0;
|
|
5
|
+
};
|