@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
|
+
import { akEditorSmallZIndex } from '@atlaskit/editor-shared-styles/constants';
|
|
2
3
|
export const CONTRIBUTOR_TAG_TESTID = 'diff-contributor-tag';
|
|
3
4
|
export const CONTRIBUTOR_TAG_NAME_TESTID = 'diff-contributor-tag-name';
|
|
4
5
|
|
|
@@ -12,15 +13,15 @@ export const MAX_TAG_WIDTH = '200px';
|
|
|
12
13
|
/**
|
|
13
14
|
* The tag sits on the line above the change, so it has to paint over that line to be readable —
|
|
14
15
|
* including any diff highlight already on it, which would otherwise slice straight through the tag.
|
|
15
|
-
*
|
|
16
|
-
* change's own highlight is stacked one level below it — see
|
|
17
|
-
* `createInlineChangedDecoration`.
|
|
16
|
+
* It also has to paint above table interaction overlays and sticky-capable header rows, which use
|
|
17
|
+
* `akEditorSmallZIndex`. The change's own highlight is stacked one level below it — see
|
|
18
|
+
* `stackBelowContributorTagStyle` in `createInlineChangedDecoration`.
|
|
18
19
|
*
|
|
19
20
|
* Painting above its own highlight is only safe because the tag can never reach it: `bottom: 100%`
|
|
20
21
|
* puts the root's bottom edge exactly on the highlight's top edge, and the root's `overflow: clip`
|
|
21
22
|
* keeps every pixel the tag paints inside it.
|
|
22
23
|
*/
|
|
23
|
-
export const CONTRIBUTOR_TAG_Z_INDEX =
|
|
24
|
+
export const CONTRIBUTOR_TAG_Z_INDEX = akEditorSmallZIndex + 1;
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* The plugin's whole side of the tag's motion: the fade and the hidden state are
|
|
@@ -9,13 +9,13 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
|
|
|
9
9
|
import isEqual from 'lodash/isEqual';
|
|
10
10
|
import memoizeOne from 'memoize-one';
|
|
11
11
|
import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
|
|
12
|
+
import { areDocsEqualByBlockStructureAndText } from '@atlaskit/editor-common/utils/areDocsEqualByBlockStructureAndText';
|
|
12
13
|
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
|
|
13
14
|
import { Mapping } from '@atlaskit/editor-prosemirror/transform';
|
|
14
15
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
15
16
|
import { UNSAFE_expValNoExposure } from '@atlaskit/platform-feature-experiments/unsafe-exp-val-no-exposure';
|
|
16
17
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
17
18
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
18
|
-
import { areDocsEqualByBlockStructureAndText } from '../areDocsEqualByBlockStructureAndText';
|
|
19
19
|
import { createDocMarginAnchorWidget } from '../decorations/createAnchorDecorationWidgets';
|
|
20
20
|
import { createBlockChangedDecoration } from '../decorations/createBlockChangedDecoration';
|
|
21
21
|
import { createInlineChangedDecoration } from '../decorations/createInlineChangedDecoration';
|
|
@@ -17,7 +17,8 @@ import { buildAddedCellOverlayRoundedStyle, buildAddedCellOverlayStyle, buildDel
|
|
|
17
17
|
import { colorSchemeRegistry, getLegacyColorScheme } from './colorSchemes/schemes';
|
|
18
18
|
import { createLeftAnchorWidget } from './createAnchorDecorationWidgets';
|
|
19
19
|
import { resolveCellOverlayStyleLegacy, resolveDeletedRowStyleLegacy } from './createChangedRowDecorationWidgets.styles.legacy';
|
|
20
|
-
import {
|
|
20
|
+
import { createContributorTagWidget, isContributorTagWidgetEnabled } from './createContributorTagWidget';
|
|
21
|
+
import { AnchorTypeKey, buildAnchorDecorationKey, buildDiffDecorationSpec } from './decorationKeys';
|
|
21
22
|
import { findSafeInsertPos } from './utils/findSafeInsertPos';
|
|
22
23
|
import { applyCellEdgeAttrs, getRowCellEdgeAttrs, getTableTopAndBottomCellEdgeAttrs } from './utils/tableCellEdgeAttrs';
|
|
23
24
|
/**
|
|
@@ -98,20 +99,40 @@ var extractChangedRows = function extractChangedRows(_ref) {
|
|
|
98
99
|
}
|
|
99
100
|
});
|
|
100
101
|
|
|
101
|
-
// Filter changes that never truly got deleted
|
|
102
|
+
// Filter changes that never truly got deleted. Compare the number of equivalent rows rather
|
|
103
|
+
// than only checking whether one still exists: deleting two of three identical empty rows should
|
|
104
|
+
// retain two changed rows even though the third remains in the new table.
|
|
105
|
+
var equivalentRowDeletionCounts = [];
|
|
102
106
|
return changedRows.filter(function (changedRow) {
|
|
103
107
|
// A replacement leaves every sibling row in place, so asking "does this row still exist
|
|
104
108
|
// ANYWHERE in the new table?" can drop a genuinely rewritten row that happens to match a
|
|
105
|
-
// sibling. Compare it against the row at the same index instead.
|
|
106
|
-
// whole-table `some()`: their indices shift, so an index comparison would let every
|
|
107
|
-
// surviving row through and draw a widget for each one.
|
|
109
|
+
// sibling. Compare it against the row at the same index instead.
|
|
108
110
|
if (handleRowReplacement) {
|
|
109
111
|
var counterpart = tableNew.node.maybeChild(changedRow.rowIndex);
|
|
110
112
|
return !counterpart || !areNodesEqualIgnoreAttrs(counterpart, changedRow.rowNode);
|
|
111
113
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
var deletionCount = equivalentRowDeletionCounts.find(function (_ref2) {
|
|
115
|
+
var rowNode = _ref2.rowNode;
|
|
116
|
+
return areNodesEqualIgnoreAttrs(rowNode, changedRow.rowNode);
|
|
114
117
|
});
|
|
118
|
+
if (!deletionCount) {
|
|
119
|
+
var oldRowCount = tableOld.node.children.filter(function (oldRow) {
|
|
120
|
+
return areNodesEqualIgnoreAttrs(oldRow, changedRow.rowNode);
|
|
121
|
+
}).length;
|
|
122
|
+
var newRowCount = tableNew.node.children.filter(function (newRow) {
|
|
123
|
+
return areNodesEqualIgnoreAttrs(newRow, changedRow.rowNode);
|
|
124
|
+
}).length;
|
|
125
|
+
deletionCount = {
|
|
126
|
+
remaining: Math.max(0, oldRowCount - newRowCount),
|
|
127
|
+
rowNode: changedRow.rowNode
|
|
128
|
+
};
|
|
129
|
+
equivalentRowDeletionCounts.push(deletionCount);
|
|
130
|
+
}
|
|
131
|
+
if (deletionCount.remaining === 0) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
deletionCount.remaining--;
|
|
135
|
+
return true;
|
|
115
136
|
});
|
|
116
137
|
};
|
|
117
138
|
|
|
@@ -194,11 +215,11 @@ var createChangedRowDOM = function createChangedRowDOM(rowNode, cellEdgeAttrs, n
|
|
|
194
215
|
/**
|
|
195
216
|
* Expands a diff to include whole changed rows when table rows are affected
|
|
196
217
|
*/
|
|
197
|
-
var expandDiffForChangedRows = function expandDiffForChangedRows(
|
|
198
|
-
var changes =
|
|
199
|
-
originalDoc =
|
|
200
|
-
newDoc =
|
|
201
|
-
diffType =
|
|
218
|
+
var expandDiffForChangedRows = function expandDiffForChangedRows(_ref3) {
|
|
219
|
+
var changes = _ref3.changes,
|
|
220
|
+
originalDoc = _ref3.originalDoc,
|
|
221
|
+
newDoc = _ref3.newDoc,
|
|
222
|
+
diffType = _ref3.diffType;
|
|
202
223
|
var rowInfo = [];
|
|
203
224
|
var _iterator = _createForOfIteratorHelper(changes),
|
|
204
225
|
_step;
|
|
@@ -227,17 +248,22 @@ var expandDiffForChangedRows = function expandDiffForChangedRows(_ref2) {
|
|
|
227
248
|
/**
|
|
228
249
|
* Main function to handle deleted rows - computes diff and creates decorations
|
|
229
250
|
*/
|
|
230
|
-
export var createChangedRowDecorationWidgets = function createChangedRowDecorationWidgets(
|
|
231
|
-
var
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
251
|
+
export var createChangedRowDecorationWidgets = function createChangedRowDecorationWidgets(_ref4) {
|
|
252
|
+
var attributionKey = _ref4.attributionKey,
|
|
253
|
+
changes = _ref4.changes,
|
|
254
|
+
originalDoc = _ref4.originalDoc,
|
|
255
|
+
newDoc = _ref4.newDoc,
|
|
256
|
+
nodeViewSerializer = _ref4.nodeViewSerializer,
|
|
257
|
+
colorScheme = _ref4.colorScheme,
|
|
258
|
+
isActive = _ref4.isActive,
|
|
259
|
+
_ref4$isInserted = _ref4.isInserted,
|
|
260
|
+
isInserted = _ref4$isInserted === void 0 ? false : _ref4$isInserted,
|
|
261
|
+
diffType = _ref4.diffType,
|
|
262
|
+
_ref4$showIndicators = _ref4.showIndicators,
|
|
263
|
+
showIndicators = _ref4$showIndicators === void 0 ? false : _ref4$showIndicators,
|
|
264
|
+
_ref4$showContributor = _ref4.showContributorTags,
|
|
265
|
+
showContributorTags = _ref4$showContributor === void 0 ? false : _ref4$showContributor,
|
|
266
|
+
tagMountContext = _ref4.tagMountContext;
|
|
241
267
|
// First, expand the changes to include complete deleted rows
|
|
242
268
|
var changedRows = expandDiffForChangedRows({
|
|
243
269
|
changes: changes.filter(function (change) {
|
|
@@ -254,8 +280,12 @@ export var createChangedRowDecorationWidgets = function createChangedRowDecorati
|
|
|
254
280
|
var safeInsertPos = findSafeInsertPos(newDoc, changedRow.fromB - 1,
|
|
255
281
|
// -1 to find the first safe position from the table
|
|
256
282
|
originalDoc.slice(changedRow.fromA, changedRow.toA));
|
|
257
|
-
|
|
283
|
+
|
|
284
|
+
// Stable while contributor tags are enabled so a decoration recalculation preserves the tag's
|
|
285
|
+
// identity and controller state.
|
|
286
|
+
var diffId = showContributorTags ? "widget-row-".concat(changedRow.fromA, "-").concat(changedRow.toA) : crypto.randomUUID();
|
|
258
287
|
var decorations = [];
|
|
288
|
+
var rowAnchorNames = [];
|
|
259
289
|
|
|
260
290
|
// `IndicatorBarContentComponent` renders a bar for every widget descriptor and anchors its
|
|
261
291
|
// top and bottom to `anchor-<diffId>`. Without an element carrying that anchor name the bar
|
|
@@ -263,9 +293,9 @@ export var createChangedRowDecorationWidgets = function createChangedRowDecorati
|
|
|
263
293
|
// bar covers only the row being changed. `createNodeChangedDecorationWidget` sets the same
|
|
264
294
|
// property on its own widget DOM for non-table content; this path returns before reaching it.
|
|
265
295
|
if (showIndicators && isExtendedEnabled(diffType) && fg('platform_editor_ai_show_diff_patch_1')) {
|
|
266
|
-
|
|
296
|
+
rowAnchorNames.push(buildAnchorDecorationKey({
|
|
267
297
|
diffId: diffId
|
|
268
|
-
}))
|
|
298
|
+
}));
|
|
269
299
|
|
|
270
300
|
// A table's content can extend past the doc margin, so the bar also needs a left anchor
|
|
271
301
|
// measured against the table itself or it is clipped once the table is resized.
|
|
@@ -278,14 +308,47 @@ export var createChangedRowDecorationWidgets = function createChangedRowDecorati
|
|
|
278
308
|
decorations.push(leftAnchor);
|
|
279
309
|
}
|
|
280
310
|
}
|
|
281
|
-
|
|
311
|
+
var tagAnchorName = showContributorTags && isContributorTagWidgetEnabled() ? buildAnchorDecorationKey({
|
|
312
|
+
diffId: diffId,
|
|
313
|
+
anchorType: AnchorTypeKey.tag
|
|
314
|
+
}) : undefined;
|
|
315
|
+
var tagWidget = tagAnchorName ? createContributorTagWidget({
|
|
316
|
+
anchorAtRangeStart: true,
|
|
317
|
+
anchorName: tagAnchorName,
|
|
318
|
+
diffId: diffId,
|
|
319
|
+
doc: newDoc,
|
|
320
|
+
from: safeInsertPos,
|
|
321
|
+
mountContext: tagMountContext,
|
|
322
|
+
to: safeInsertPos
|
|
323
|
+
}) : undefined;
|
|
324
|
+
if (tagAnchorName) {
|
|
325
|
+
// The tag host is a separate, table-safe widget. CSS anchor positioning aligns it to this
|
|
326
|
+
// synthetic row without placing non-cell DOM inside the `<tr>` or inheriting its deletion
|
|
327
|
+
// opacity and strikethrough.
|
|
328
|
+
rowAnchorNames.push(tagAnchorName);
|
|
329
|
+
}
|
|
330
|
+
if (rowAnchorNames.length > 0) {
|
|
331
|
+
rowDOM.style.setProperty('anchor-name', rowAnchorNames.map(function (anchorName) {
|
|
332
|
+
return "--".concat(anchorName);
|
|
333
|
+
}).join(', '));
|
|
334
|
+
}
|
|
335
|
+
if (tagWidget) {
|
|
336
|
+
// Lets the contributor tag reveal when any part of the deleted row is hovered.
|
|
337
|
+
rowDOM.setAttribute('data-diff-id', diffId);
|
|
338
|
+
}
|
|
339
|
+
var rowWidget = Decoration.widget(safeInsertPos, rowDOM, _objectSpread({}, buildDiffDecorationSpec({
|
|
340
|
+
attributionKey: tagWidget ? attributionKey : undefined,
|
|
282
341
|
colorScheme: colorScheme,
|
|
283
342
|
decorationType: 'widget',
|
|
284
343
|
diffId: diffId,
|
|
285
|
-
isActive:
|
|
344
|
+
isActive: isActive,
|
|
286
345
|
isInserted: isInserted,
|
|
287
346
|
diffType: diffType
|
|
288
|
-
})))
|
|
347
|
+
})));
|
|
348
|
+
decorations.push(rowWidget);
|
|
349
|
+
if (tagWidget) {
|
|
350
|
+
decorations.push(tagWidget);
|
|
351
|
+
}
|
|
289
352
|
return decorations;
|
|
290
353
|
});
|
|
291
354
|
};
|
|
@@ -7,7 +7,7 @@ import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
|
7
7
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
8
8
|
import { CONTRIBUTOR_TAG_Z_INDEX } from '../../ui/ContributorTag/buildContributorTagDom';
|
|
9
9
|
import { isExtendedEnabled } from '../isExtendedEnabled';
|
|
10
|
-
import {
|
|
10
|
+
import { isEmptyParagraphSlice } from '../utils/isEmptyParagraphSlice';
|
|
11
11
|
import { buildAtomicInlineChangedCSSVariables, buildDeletedInlineContentStyleExtended, buildDeletedInlineStyle, buildDeletedInlineStyleStandard, buildInsertStyle, buildInsertStyleActive, buildInsertStyleExtended, buildInsertStyleExtendedActive, buildInsertStyleExtendedNoUnderline, buildInsertStyleExtendedNoUnderlineActive } from './colorSchemes/factory';
|
|
12
12
|
import { colorSchemeRegistry, getLegacyColorScheme } from './colorSchemes/schemes';
|
|
13
13
|
import { createInlineIndicatorAnchorWidgets } from './createAnchorDecorationWidgets';
|
|
@@ -156,11 +156,9 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
|
|
|
156
156
|
// tag host's key, remounting every tag mid-step. Random ids are kept when tags are off.
|
|
157
157
|
var diffId = showContributorTags ? "inline-".concat(change.fromB, "-").concat(change.toB) : crypto.randomUUID();
|
|
158
158
|
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
// a host that is never emitted (EDITOR-8855).
|
|
163
|
-
var canTagChange = showContributorTags && !!doc && isExtendedEnabled(diffType) && isContributorTagWidgetEnabled() && hasVisibleContent(doc.slice(change.fromB, change.toB).content);
|
|
159
|
+
// Match the rendered shape: whitespace text is eligible, while an empty paragraph is structural
|
|
160
|
+
// content with no tag host.
|
|
161
|
+
var canTagChange = showContributorTags && !!doc && isExtendedEnabled(diffType) && isContributorTagWidgetEnabled() && !isEmptyParagraphSlice(doc.slice(change.fromB, change.toB));
|
|
164
162
|
if (shouldHideDeleted) {
|
|
165
163
|
return [Decoration.inline(change.fromB, change.toB, {
|
|
166
164
|
style: displayNoneStyle
|
|
@@ -4,7 +4,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
4
4
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
5
5
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
6
6
|
import { isExtendedEnabled } from '../isExtendedEnabled';
|
|
7
|
-
import {
|
|
7
|
+
import { isEmptyParagraphSlice } from '../utils/isEmptyParagraphSlice';
|
|
8
8
|
import { createLeftAnchorWidget } from './createAnchorDecorationWidgets';
|
|
9
9
|
import { createChangedRowDecorationWidgets } from './createChangedRowDecorationWidgets';
|
|
10
10
|
import { createContributorTagHost, unmountContributorTag } from './createContributorTagWidget';
|
|
@@ -94,7 +94,7 @@ var createTableCellContentWidgets = function createTableCellContentWidgets(_ref)
|
|
|
94
94
|
* that is not in the current document.
|
|
95
95
|
*/
|
|
96
96
|
export var createNodeChangedDecorationWidget = function createNodeChangedDecorationWidget(_ref2) {
|
|
97
|
-
var
|
|
97
|
+
var _$safeInsertPos$nodeB, _slice$content$firstC, _newDoc$firstChild, _slice$content$firstC2;
|
|
98
98
|
var attributionKey = _ref2.attributionKey,
|
|
99
99
|
change = _ref2.change,
|
|
100
100
|
doc = _ref2.doc,
|
|
@@ -117,7 +117,7 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
|
|
|
117
117
|
reveal = _ref2.reveal,
|
|
118
118
|
tagMountContext = _ref2.tagMountContext;
|
|
119
119
|
var slice = doc.slice(change.fromA, change.toA);
|
|
120
|
-
var shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (slice
|
|
120
|
+
var shouldSkipDeletedEmptyParagraphDecoration = !isInserted && isEmptyParagraphSlice(slice);
|
|
121
121
|
// Widget decoration used for deletions as the content is not in the document
|
|
122
122
|
// and we want to display the deleted content with a style.
|
|
123
123
|
// For `placeBelow`, anchor at the END of the new content (change.toB) so the deleted
|
|
@@ -159,16 +159,20 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
|
|
|
159
159
|
}
|
|
160
160
|
if (isTableRowContent) {
|
|
161
161
|
return createChangedRowDecorationWidgets({
|
|
162
|
+
attributionKey: attributionKey,
|
|
162
163
|
changes: [change],
|
|
163
164
|
originalDoc: doc,
|
|
164
165
|
newDoc: newDoc,
|
|
165
166
|
nodeViewSerializer: nodeViewSerializer,
|
|
166
167
|
colorScheme: colorScheme,
|
|
168
|
+
isActive: isActive,
|
|
167
169
|
isInserted: isInserted,
|
|
168
170
|
diffType: diffType,
|
|
169
171
|
// Needed for the row's own indicator anchor; this path returns before the
|
|
170
172
|
// `anchor-name` assignment further down.
|
|
171
|
-
showIndicators: showIndicators
|
|
173
|
+
showIndicators: showIndicators,
|
|
174
|
+
showContributorTags: showContributorTags,
|
|
175
|
+
tagMountContext: tagMountContext
|
|
172
176
|
});
|
|
173
177
|
}
|
|
174
178
|
var serializer = nodeViewSerializer;
|
|
@@ -219,10 +223,9 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
|
|
|
219
223
|
// Derived from the deleted range so it survives a recalculation — see the same reasoning in
|
|
220
224
|
// `createInlineChangedDecoration`.
|
|
221
225
|
var diffId = showContributorTags ? "widget-".concat(change.fromA, "-").concat(change.toA) : crypto.randomUUID();
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
var canTagWidget = showContributorTags && hasVisibleContent(slice.content);
|
|
226
|
+
// Match the rendering predicate above: an empty paragraph has no widget to tag, while whitespace
|
|
227
|
+
// text in a rendered widget remains eligible.
|
|
228
|
+
var canTagWidget = showContributorTags && !shouldSkipDeletedEmptyParagraphDecoration;
|
|
226
229
|
var decorations = [];
|
|
227
230
|
var replacementNode = newDoc.nodeAt(change.fromB);
|
|
228
231
|
var firstReplacedNode = slice.content.firstChild;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Whether a slice contains exactly one structurally empty paragraph. */
|
|
2
|
+
export var isEmptyParagraphSlice = function 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
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
2
|
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
3
|
+
import { akEditorSmallZIndex } from '@atlaskit/editor-shared-styles/constants';
|
|
3
4
|
export var CONTRIBUTOR_TAG_TESTID = 'diff-contributor-tag';
|
|
4
5
|
export var CONTRIBUTOR_TAG_NAME_TESTID = 'diff-contributor-tag-name';
|
|
5
6
|
|
|
@@ -13,15 +14,15 @@ export var MAX_TAG_WIDTH = '200px';
|
|
|
13
14
|
/**
|
|
14
15
|
* The tag sits on the line above the change, so it has to paint over that line to be readable —
|
|
15
16
|
* including any diff highlight already on it, which would otherwise slice straight through the tag.
|
|
16
|
-
*
|
|
17
|
-
* change's own highlight is stacked one level below it — see
|
|
18
|
-
* `createInlineChangedDecoration`.
|
|
17
|
+
* It also has to paint above table interaction overlays and sticky-capable header rows, which use
|
|
18
|
+
* `akEditorSmallZIndex`. The change's own highlight is stacked one level below it — see
|
|
19
|
+
* `stackBelowContributorTagStyle` in `createInlineChangedDecoration`.
|
|
19
20
|
*
|
|
20
21
|
* Painting above its own highlight is only safe because the tag can never reach it: `bottom: 100%`
|
|
21
22
|
* puts the root's bottom edge exactly on the highlight's top edge, and the root's `overflow: clip`
|
|
22
23
|
* keeps every pixel the tag paints inside it.
|
|
23
24
|
*/
|
|
24
|
-
export var CONTRIBUTOR_TAG_Z_INDEX =
|
|
25
|
+
export var CONTRIBUTOR_TAG_Z_INDEX = akEditorSmallZIndex + 1;
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* The plugin's whole side of the tag's motion: the fade and the hidden state are
|
|
@@ -4,18 +4,23 @@ import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
|
4
4
|
import type { DiffType } from '../../showDiffPluginType';
|
|
5
5
|
import type { NodeViewSerializer } from '../NodeViewSerializer';
|
|
6
6
|
import type { ColorScheme } from './colorSchemes/types';
|
|
7
|
+
import { type ContributorTagMountContext } from './createContributorTagWidget';
|
|
7
8
|
type SimpleChange = Pick<Change, 'fromA' | 'toA' | 'fromB' | 'deleted'>;
|
|
8
9
|
/**
|
|
9
10
|
* Main function to handle deleted rows - computes diff and creates decorations
|
|
10
11
|
*/
|
|
11
|
-
export declare const createChangedRowDecorationWidgets: ({ changes, originalDoc, newDoc, nodeViewSerializer, colorScheme, isInserted, diffType, showIndicators, }: {
|
|
12
|
+
export declare const createChangedRowDecorationWidgets: ({ attributionKey, changes, originalDoc, newDoc, nodeViewSerializer, colorScheme, isActive, isInserted, diffType, showIndicators, showContributorTags, tagMountContext, }: {
|
|
13
|
+
attributionKey?: string;
|
|
12
14
|
changes: SimpleChange[];
|
|
13
15
|
colorScheme?: ColorScheme;
|
|
14
16
|
diffType?: DiffType;
|
|
17
|
+
isActive?: boolean;
|
|
15
18
|
isInserted?: boolean;
|
|
16
19
|
newDoc: PMNode;
|
|
17
20
|
nodeViewSerializer: NodeViewSerializer;
|
|
18
21
|
originalDoc: PMNode;
|
|
22
|
+
showContributorTags?: boolean;
|
|
19
23
|
showIndicators?: boolean;
|
|
24
|
+
tagMountContext?: ContributorTagMountContext;
|
|
20
25
|
}) => Decoration[];
|
|
21
26
|
export {};
|
|
@@ -9,15 +9,15 @@ export declare const MAX_TAG_WIDTH = "200px";
|
|
|
9
9
|
/**
|
|
10
10
|
* The tag sits on the line above the change, so it has to paint over that line to be readable —
|
|
11
11
|
* including any diff highlight already on it, which would otherwise slice straight through the tag.
|
|
12
|
-
*
|
|
13
|
-
* change's own highlight is stacked one level below it — see
|
|
14
|
-
* `createInlineChangedDecoration`.
|
|
12
|
+
* It also has to paint above table interaction overlays and sticky-capable header rows, which use
|
|
13
|
+
* `akEditorSmallZIndex`. The change's own highlight is stacked one level below it — see
|
|
14
|
+
* `stackBelowContributorTagStyle` in `createInlineChangedDecoration`.
|
|
15
15
|
*
|
|
16
16
|
* Painting above its own highlight is only safe because the tag can never reach it: `bottom: 100%`
|
|
17
17
|
* puts the root's bottom edge exactly on the highlight's top edge, and the root's `overflow: clip`
|
|
18
18
|
* keeps every pixel the tag paints inside it.
|
|
19
19
|
*/
|
|
20
|
-
export declare const CONTRIBUTOR_TAG_Z_INDEX
|
|
20
|
+
export declare const CONTRIBUTOR_TAG_Z_INDEX: number;
|
|
21
21
|
/**
|
|
22
22
|
* The plugin's whole side of the tag's motion: the fade and the hidden state are
|
|
23
23
|
* `contributorTagStyles` in both EditorContentContainer stylesheets, keyed on this literal and the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-show-diff",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.16",
|
|
4
4
|
"description": "ShowDiff plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@atlaskit/platform-feature-experiments": "^0.3.0",
|
|
33
33
|
"@atlaskit/platform-feature-flags": "^2.2.0",
|
|
34
34
|
"@atlaskit/primitives": "^22.5.0",
|
|
35
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
35
|
+
"@atlaskit/tmp-editor-statsig": "^197.0.0",
|
|
36
36
|
"@atlaskit/tokens": "^17.0.0",
|
|
37
37
|
"@babel/runtime": "^7.0.0",
|
|
38
38
|
"@compiled/react": "^1.0.2",
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.areDocsEqualByBlockStructureAndText = areDocsEqualByBlockStructureAndText;
|
|
7
|
-
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
8
|
-
/**
|
|
9
|
-
* Returns a copy of the document with all marks removed from all text.
|
|
10
|
-
* This normalises mark fragmentation — e.g. annotation marks reordering can split
|
|
11
|
-
* a single text run into many text nodes, making structural comparison unreliable.
|
|
12
|
-
* After stripping, ProseMirror will merge adjacent text nodes so childCounts are stable.
|
|
13
|
-
*/
|
|
14
|
-
function stripMarks(doc) {
|
|
15
|
-
var tr = new _transform.Transform(doc);
|
|
16
|
-
tr.removeMark(0, doc.content.size);
|
|
17
|
-
return tr.doc;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Returns true if both (mark-stripped) nodes have the same block tree structure (node type and child count at every level)
|
|
22
|
-
*/
|
|
23
|
-
function isBlockStructureEqual(node1, node2) {
|
|
24
|
-
if (node1.type !== node2.type || node1.childCount !== node2.childCount) {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
for (var i = 0; i < node1.childCount; i++) {
|
|
28
|
-
if (!isBlockStructureEqual(node1.child(i), node2.child(i))) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Looser equality for "safe diff" cases: same full text content and same block structure
|
|
37
|
-
* (e.g. text moved across text-node boundaries). Used when strict areNodesEqualIgnoreAttrs fails.
|
|
38
|
-
* This is safe because we ensure decorations get applied to valid positions.
|
|
39
|
-
*
|
|
40
|
-
* Marks are intentionally ignored — two documents that differ only in mark application
|
|
41
|
-
* (e.g. bold/italic boundaries or annotation mark ordering) are considered equal here.
|
|
42
|
-
* Both documents are mark-stripped before comparison so that mark-driven text fragmentation
|
|
43
|
-
* does not produce false inequalities.
|
|
44
|
-
*/
|
|
45
|
-
function areDocsEqualByBlockStructureAndText(doc1, doc2) {
|
|
46
|
-
if (doc1.textContent !== doc2.textContent) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
// Strip marks before comparing so that mark-driven text fragmentation
|
|
50
|
-
// (e.g. annotation mark reordering producing different childCounts) does not
|
|
51
|
-
// cause false inequalities.
|
|
52
|
-
var stripped1 = stripMarks(doc1);
|
|
53
|
-
var stripped2 = stripMarks(doc2);
|
|
54
|
-
return doc1.nodeSize === doc2.nodeSize && isBlockStructureEqual(stripped1, stripped2);
|
|
55
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
Object.defineProperty(exports, "__esModule", {
|
|
5
|
-
value: true
|
|
6
|
-
});
|
|
7
|
-
exports.hasVisibleContent = void 0;
|
|
8
|
-
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
|
-
var _charsByOffset = require("./charsByOffset");
|
|
10
|
-
/**
|
|
11
|
-
* Whether `fragment` holds anything a reader can see.
|
|
12
|
-
*
|
|
13
|
-
* Text counts once it carries a single non-whitespace character; a leaf node (mention, emoji, date,
|
|
14
|
-
* media, rule, hardBreak, …) counts in its own right, exactly as `resolveTagAnchorPos` treats one.
|
|
15
|
-
* Everything else is descended into, so a paragraph is visible when its content is.
|
|
16
|
-
*
|
|
17
|
-
* A change whose content is nothing but whitespace — a space typed between two words, a run of
|
|
18
|
-
* trailing spaces, an added blank paragraph — has no visible content for a contributor tag to
|
|
19
|
-
* caption, so it carries no tag (EDITOR-8855).
|
|
20
|
-
*/
|
|
21
|
-
var _hasVisibleContent = exports.hasVisibleContent = function hasVisibleContent(fragment) {
|
|
22
|
-
var visible = false;
|
|
23
|
-
fragment.forEach(function (node) {
|
|
24
|
-
if (visible) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
if (node.isText) {
|
|
28
|
-
var _node$text;
|
|
29
|
-
visible = (0, _toConsumableArray2.default)((_node$text = node.text) !== null && _node$text !== void 0 ? _node$text : '').some(function (char) {
|
|
30
|
-
return !(0, _charsByOffset.isWhitespaceChar)(char);
|
|
31
|
-
});
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
visible = node.isLeaf || _hasVisibleContent(node.content);
|
|
35
|
-
});
|
|
36
|
-
return visible;
|
|
37
|
-
};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { Transform } from '@atlaskit/editor-prosemirror/transform';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Returns a copy of the document with all marks removed from all text.
|
|
5
|
-
* This normalises mark fragmentation — e.g. annotation marks reordering can split
|
|
6
|
-
* a single text run into many text nodes, making structural comparison unreliable.
|
|
7
|
-
* After stripping, ProseMirror will merge adjacent text nodes so childCounts are stable.
|
|
8
|
-
*/
|
|
9
|
-
function stripMarks(doc) {
|
|
10
|
-
const tr = new Transform(doc);
|
|
11
|
-
tr.removeMark(0, doc.content.size);
|
|
12
|
-
return tr.doc;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Returns true if both (mark-stripped) nodes have the same block tree structure (node type and child count at every level)
|
|
17
|
-
*/
|
|
18
|
-
function isBlockStructureEqual(node1, node2) {
|
|
19
|
-
if (node1.type !== node2.type || node1.childCount !== node2.childCount) {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
for (let i = 0; i < node1.childCount; i++) {
|
|
23
|
-
if (!isBlockStructureEqual(node1.child(i), node2.child(i))) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Looser equality for "safe diff" cases: same full text content and same block structure
|
|
32
|
-
* (e.g. text moved across text-node boundaries). Used when strict areNodesEqualIgnoreAttrs fails.
|
|
33
|
-
* This is safe because we ensure decorations get applied to valid positions.
|
|
34
|
-
*
|
|
35
|
-
* Marks are intentionally ignored — two documents that differ only in mark application
|
|
36
|
-
* (e.g. bold/italic boundaries or annotation mark ordering) are considered equal here.
|
|
37
|
-
* Both documents are mark-stripped before comparison so that mark-driven text fragmentation
|
|
38
|
-
* does not produce false inequalities.
|
|
39
|
-
*/
|
|
40
|
-
export function areDocsEqualByBlockStructureAndText(doc1, doc2) {
|
|
41
|
-
if (doc1.textContent !== doc2.textContent) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
// Strip marks before comparing so that mark-driven text fragmentation
|
|
45
|
-
// (e.g. annotation mark reordering producing different childCounts) does not
|
|
46
|
-
// cause false inequalities.
|
|
47
|
-
const stripped1 = stripMarks(doc1);
|
|
48
|
-
const stripped2 = stripMarks(doc2);
|
|
49
|
-
return doc1.nodeSize === doc2.nodeSize && isBlockStructureEqual(stripped1, stripped2);
|
|
50
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { isWhitespaceChar } from './charsByOffset';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Whether `fragment` holds anything a reader can see.
|
|
5
|
-
*
|
|
6
|
-
* Text counts once it carries a single non-whitespace character; a leaf node (mention, emoji, date,
|
|
7
|
-
* media, rule, hardBreak, …) counts in its own right, exactly as `resolveTagAnchorPos` treats one.
|
|
8
|
-
* Everything else is descended into, so a paragraph is visible when its content is.
|
|
9
|
-
*
|
|
10
|
-
* A change whose content is nothing but whitespace — a space typed between two words, a run of
|
|
11
|
-
* trailing spaces, an added blank paragraph — has no visible content for a contributor tag to
|
|
12
|
-
* caption, so it carries no tag (EDITOR-8855).
|
|
13
|
-
*/
|
|
14
|
-
export const hasVisibleContent = fragment => {
|
|
15
|
-
let visible = false;
|
|
16
|
-
fragment.forEach(node => {
|
|
17
|
-
if (visible) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
if (node.isText) {
|
|
21
|
-
var _node$text;
|
|
22
|
-
visible = [...((_node$text = node.text) !== null && _node$text !== void 0 ? _node$text : '')].some(char => !isWhitespaceChar(char));
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
visible = node.isLeaf || hasVisibleContent(node.content);
|
|
26
|
-
});
|
|
27
|
-
return visible;
|
|
28
|
-
};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { Transform } from '@atlaskit/editor-prosemirror/transform';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Returns a copy of the document with all marks removed from all text.
|
|
5
|
-
* This normalises mark fragmentation — e.g. annotation marks reordering can split
|
|
6
|
-
* a single text run into many text nodes, making structural comparison unreliable.
|
|
7
|
-
* After stripping, ProseMirror will merge adjacent text nodes so childCounts are stable.
|
|
8
|
-
*/
|
|
9
|
-
function stripMarks(doc) {
|
|
10
|
-
var tr = new Transform(doc);
|
|
11
|
-
tr.removeMark(0, doc.content.size);
|
|
12
|
-
return tr.doc;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Returns true if both (mark-stripped) nodes have the same block tree structure (node type and child count at every level)
|
|
17
|
-
*/
|
|
18
|
-
function isBlockStructureEqual(node1, node2) {
|
|
19
|
-
if (node1.type !== node2.type || node1.childCount !== node2.childCount) {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
for (var i = 0; i < node1.childCount; i++) {
|
|
23
|
-
if (!isBlockStructureEqual(node1.child(i), node2.child(i))) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Looser equality for "safe diff" cases: same full text content and same block structure
|
|
32
|
-
* (e.g. text moved across text-node boundaries). Used when strict areNodesEqualIgnoreAttrs fails.
|
|
33
|
-
* This is safe because we ensure decorations get applied to valid positions.
|
|
34
|
-
*
|
|
35
|
-
* Marks are intentionally ignored — two documents that differ only in mark application
|
|
36
|
-
* (e.g. bold/italic boundaries or annotation mark ordering) are considered equal here.
|
|
37
|
-
* Both documents are mark-stripped before comparison so that mark-driven text fragmentation
|
|
38
|
-
* does not produce false inequalities.
|
|
39
|
-
*/
|
|
40
|
-
export function areDocsEqualByBlockStructureAndText(doc1, doc2) {
|
|
41
|
-
if (doc1.textContent !== doc2.textContent) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
// Strip marks before comparing so that mark-driven text fragmentation
|
|
45
|
-
// (e.g. annotation mark reordering producing different childCounts) does not
|
|
46
|
-
// cause false inequalities.
|
|
47
|
-
var stripped1 = stripMarks(doc1);
|
|
48
|
-
var stripped2 = stripMarks(doc2);
|
|
49
|
-
return doc1.nodeSize === doc2.nodeSize && isBlockStructureEqual(stripped1, stripped2);
|
|
50
|
-
}
|