@atlaskit/editor-plugin-show-diff 8.4.5 → 8.4.7
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 +16 -0
- package/dist/cjs/pm-plugins/areDocsEqualByBlockStructureAndText.js +6 -10
- package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +13 -20
- package/dist/cjs/pm-plugins/calculateDiff/diffBySteps.js +141 -4
- package/dist/cjs/pm-plugins/decorations/createBlockChangedDecoration.js +13 -27
- package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +12 -34
- package/dist/cjs/pm-plugins/decorations/utils/getAttrChangeRanges.js +30 -32
- package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeView.js +5 -9
- package/dist/cjs/pm-plugins/main.js +13 -18
- package/dist/cjs/showDiffPlugin.js +1 -3
- package/dist/es2019/pm-plugins/areDocsEqualByBlockStructureAndText.js +6 -10
- package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +13 -20
- package/dist/es2019/pm-plugins/calculateDiff/diffBySteps.js +137 -4
- package/dist/es2019/pm-plugins/decorations/createBlockChangedDecoration.js +15 -27
- package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +10 -32
- package/dist/es2019/pm-plugins/decorations/utils/getAttrChangeRanges.js +24 -26
- package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeView.js +6 -10
- package/dist/es2019/pm-plugins/main.js +13 -18
- package/dist/es2019/showDiffPlugin.js +1 -3
- package/dist/esm/pm-plugins/areDocsEqualByBlockStructureAndText.js +6 -10
- package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +13 -20
- package/dist/esm/pm-plugins/calculateDiff/diffBySteps.js +141 -4
- package/dist/esm/pm-plugins/decorations/createBlockChangedDecoration.js +14 -27
- package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +12 -34
- package/dist/esm/pm-plugins/decorations/utils/getAttrChangeRanges.js +30 -32
- package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeView.js +6 -10
- package/dist/esm/pm-plugins/main.js +13 -18
- package/dist/esm/showDiffPlugin.js +1 -3
- package/package.json +7 -14
|
@@ -11,7 +11,6 @@ var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
|
|
|
11
11
|
var _state2 = require("@atlaskit/editor-prosemirror/state");
|
|
12
12
|
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
13
13
|
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
14
|
-
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
15
14
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
16
15
|
var _calculateDiffDecorations = require("./calculateDiff/calculateDiffDecorations");
|
|
17
16
|
var _enforceCustomStepRegisters = require("./enforceCustomStepRegisters");
|
|
@@ -22,9 +21,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
|
|
|
22
21
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
23
22
|
var showDiffPluginKey = exports.showDiffPluginKey = new _state2.PluginKey('showDiffPlugin');
|
|
24
23
|
var createPlugin = exports.createPlugin = function createPlugin(config, getIntl, api) {
|
|
25
|
-
|
|
26
|
-
(0, _enforceCustomStepRegisters.enforceCustomStepRegisters)();
|
|
27
|
-
}
|
|
24
|
+
(0, _enforceCustomStepRegisters.enforceCustomStepRegisters)();
|
|
28
25
|
var nodeViewSerializer = new _NodeViewSerializer.NodeViewSerializer({});
|
|
29
26
|
var setNodeViewSerializer = function setNodeViewSerializer(editorView) {
|
|
30
27
|
nodeViewSerializer.init({
|
|
@@ -65,7 +62,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
|
|
|
65
62
|
nodeViewSerializer: nodeViewSerializer,
|
|
66
63
|
colorScheme: config === null || config === void 0 ? void 0 : config.colorScheme,
|
|
67
64
|
intl: getIntl(),
|
|
68
|
-
activeIndexPos:
|
|
65
|
+
activeIndexPos: newPluginState.activeIndexPos,
|
|
69
66
|
api: api
|
|
70
67
|
}, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
|
|
71
68
|
isInverted: (_newPluginState = newPluginState) === null || _newPluginState === void 0 ? void 0 : _newPluginState.isInverted,
|
|
@@ -84,7 +81,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
|
|
|
84
81
|
diffType: 'inline',
|
|
85
82
|
hideDeletedDiffs: false
|
|
86
83
|
} : {});
|
|
87
|
-
} else if ((
|
|
84
|
+
} else if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SCROLL_TO_NEXT' || (meta === null || meta === void 0 ? void 0 : meta.action) === 'SCROLL_TO_PREVIOUS') {
|
|
88
85
|
// Update the active index in plugin state and recalculate decorations
|
|
89
86
|
var _decorations = (0, _getScrollableDecorations.getScrollableDecorations)(currentPluginState.decorations, newState.doc);
|
|
90
87
|
if (_decorations.length > 0) {
|
|
@@ -170,19 +167,17 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
|
|
|
170
167
|
}
|
|
171
168
|
|
|
172
169
|
// Check for any potential scroll side-effects
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
api === null || api === void 0 || api.core.actions.execute(api.expand.commands.toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
|
|
183
|
-
}
|
|
184
|
-
cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToActiveDecoration)(view, scrollableDecorations, pluginState.activeIndex);
|
|
170
|
+
var activeIndexChanged = (pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && pluginState.activeIndex !== previousActiveIndex;
|
|
171
|
+
previousActiveIndex = pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex;
|
|
172
|
+
if ((pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && activeIndexChanged) {
|
|
173
|
+
var _cancelPendingScrollT2, _api$expand;
|
|
174
|
+
(_cancelPendingScrollT2 = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT2 === void 0 || _cancelPendingScrollT2();
|
|
175
|
+
var scrollableDecorations = (0, _getScrollableDecorations.getScrollableDecorations)(pluginState.decorations, view.state.doc);
|
|
176
|
+
var activeDecoration = scrollableDecorations[pluginState.activeIndex];
|
|
177
|
+
if (activeDecoration && api !== null && api !== void 0 && (_api$expand = api.expand) !== null && _api$expand !== void 0 && (_api$expand = _api$expand.commands) !== null && _api$expand !== void 0 && _api$expand.toggleExpandRange) {
|
|
178
|
+
api === null || api === void 0 || api.core.actions.execute(api.expand.commands.toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
|
|
185
179
|
}
|
|
180
|
+
cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToActiveDecoration)(view, scrollableDecorations, pluginState.activeIndex);
|
|
186
181
|
}
|
|
187
182
|
},
|
|
188
183
|
destroy: function destroy() {
|
|
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.showDiffPlugin = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
-
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
10
9
|
var _getScrollableDecorations = require("./pm-plugins/getScrollableDecorations");
|
|
11
10
|
var _main = require("./pm-plugins/main");
|
|
12
11
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
@@ -55,7 +54,6 @@ var showDiffPlugin = exports.showDiffPlugin = function showDiffPlugin(_ref) {
|
|
|
55
54
|
}];
|
|
56
55
|
},
|
|
57
56
|
getSharedState: function getSharedState(editorState) {
|
|
58
|
-
var _pluginState$decorati;
|
|
59
57
|
if (!editorState) {
|
|
60
58
|
return {
|
|
61
59
|
isDisplayingChanges: false,
|
|
@@ -63,7 +61,7 @@ var showDiffPlugin = exports.showDiffPlugin = function showDiffPlugin(_ref) {
|
|
|
63
61
|
};
|
|
64
62
|
}
|
|
65
63
|
var pluginState = _main.showDiffPluginKey.getState(editorState);
|
|
66
|
-
var decorationCount = (0,
|
|
64
|
+
var decorationCount = (0, _getScrollableDecorations.getScrollableDecorations)(pluginState === null || pluginState === void 0 ? void 0 : pluginState.decorations, editorState.doc);
|
|
67
65
|
return {
|
|
68
66
|
isDisplayingChanges: decorationCount.length > 0,
|
|
69
67
|
activeIndex: pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Transform } from '@atlaskit/editor-prosemirror/transform';
|
|
2
|
-
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Returns a copy of the document with all marks removed from all text.
|
|
@@ -42,13 +41,10 @@ export function areDocsEqualByBlockStructureAndText(doc1, doc2) {
|
|
|
42
41
|
if (doc1.textContent !== doc2.textContent) {
|
|
43
42
|
return false;
|
|
44
43
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return doc1.nodeSize === doc2.nodeSize && isBlockStructureEqual(stripped1, stripped2);
|
|
52
|
-
}
|
|
53
|
-
return doc1.nodeSize === doc2.nodeSize && isBlockStructureEqual(doc1, doc2);
|
|
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);
|
|
54
50
|
}
|
|
@@ -4,7 +4,6 @@ import memoizeOne from 'memoize-one';
|
|
|
4
4
|
import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
|
|
5
5
|
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
|
|
6
6
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
7
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
8
7
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
9
8
|
import { areDocsEqualByBlockStructureAndText } from '../areDocsEqualByBlockStructureAndText';
|
|
10
9
|
import { createBlockChangedDecoration } from '../decorations/createBlockChangedDecoration';
|
|
@@ -110,25 +109,19 @@ const calculateDiffDecorationsInner = ({
|
|
|
110
109
|
// Rather than using .eq() we use a custom function that only checks for structural
|
|
111
110
|
// changes and ignores differences in attributes which don't affect decoration positions
|
|
112
111
|
if (!areNodesEqualIgnoreAttrs(steppedDoc, tr.doc)) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
recoveredViaContentEquality
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
if (fg('platform_editor_show_diff_equality_fallback')) {
|
|
128
|
-
if (!recoveredViaContentEquality) {
|
|
129
|
-
return DecorationSet.empty;
|
|
112
|
+
var _api$analytics;
|
|
113
|
+
const recoveredViaContentEquality = areDocsEqualByBlockStructureAndText(steppedDoc, tr.doc);
|
|
114
|
+
api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.fireAnalyticsEvent({
|
|
115
|
+
eventType: 'track',
|
|
116
|
+
action: 'nodesNotEqual',
|
|
117
|
+
actionSubject: 'showDiff',
|
|
118
|
+
attributes: {
|
|
119
|
+
docSizeEqual: steppedDoc.nodeSize === tr.doc.nodeSize,
|
|
120
|
+
colorScheme,
|
|
121
|
+
recoveredViaContentEquality
|
|
130
122
|
}
|
|
131
|
-
}
|
|
123
|
+
});
|
|
124
|
+
if (!recoveredViaContentEquality) {
|
|
132
125
|
return DecorationSet.empty;
|
|
133
126
|
}
|
|
134
127
|
}
|
|
@@ -202,7 +195,7 @@ const calculateDiffDecorationsInner = ({
|
|
|
202
195
|
}));
|
|
203
196
|
});
|
|
204
197
|
getAttrChangeRanges(tr.doc, attrSteps).forEach(change => {
|
|
205
|
-
if (
|
|
198
|
+
if (change.isInline) {
|
|
206
199
|
// Inline nodes (e.g. date) need an inline decoration rather than a block decoration
|
|
207
200
|
const isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
|
|
208
201
|
decorations.push(createInlineChangedDecoration({
|
|
@@ -4,6 +4,103 @@ import { Mapping, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
|
4
4
|
import { optimizeChanges } from './optimizeChanges';
|
|
5
5
|
const mapPosition = (mapping, pos) => mapping.map(pos);
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Given a ProseMirror doc and a position range [from, to], expand
|
|
9
|
+
* both endpoints outward to the nearest word boundaries.
|
|
10
|
+
*
|
|
11
|
+
* A "word character" is any non-whitespace, non-punctuation character, plus hyphen.
|
|
12
|
+
* This covers accented/Unicode letters (e.g. é, ñ, ü) while still stopping at
|
|
13
|
+
* commas, periods, and other punctuation. Hyphens are explicitly included so that
|
|
14
|
+
* hyphenated words (e.g. "deep-sea") are treated as a single word.
|
|
15
|
+
* Expansion stops at whitespace, punctuation (excluding hyphen), or the textblock edges.
|
|
16
|
+
*
|
|
17
|
+
* If `from` and `to` resolve into different parent nodes, or if the
|
|
18
|
+
* parent is not a textblock, the range is returned unchanged.
|
|
19
|
+
*/
|
|
20
|
+
const expandToWordBoundaries = (doc, from, to) => {
|
|
21
|
+
const $from = doc.resolve(from);
|
|
22
|
+
|
|
23
|
+
// Only expand inside a textblock.
|
|
24
|
+
if (!$from.parent.isTextblock) {
|
|
25
|
+
return {
|
|
26
|
+
from,
|
|
27
|
+
to
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// When `from !== to`, verify both ends are in the same textblock.
|
|
32
|
+
if (from !== to) {
|
|
33
|
+
const $to = doc.resolve(to);
|
|
34
|
+
if ($from.parent !== $to.parent) {
|
|
35
|
+
return {
|
|
36
|
+
from,
|
|
37
|
+
to
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const parent = $from.parent;
|
|
42
|
+
const text = parent.textContent;
|
|
43
|
+
const parentStart = $from.start(); // absolute position of the first character in the textblock
|
|
44
|
+
|
|
45
|
+
// Convert absolute doc positions to zero-based string indices.
|
|
46
|
+
let fromIdx = from - parentStart;
|
|
47
|
+
let toIdx = to - parentStart;
|
|
48
|
+
|
|
49
|
+
// any non whitespace and non punctuation chars
|
|
50
|
+
// @ts-ignore TS1501: This regular expression flag is only available when targeting 'es6' or later.
|
|
51
|
+
const isWordChar = ch => /[\p{L}\p{N}_-]/u.test(ch);
|
|
52
|
+
|
|
53
|
+
// Detect whether the position sits mid-word: there is a word character
|
|
54
|
+
// on both sides of the position (or, for a non-empty range, on the
|
|
55
|
+
// outer side of each endpoint).
|
|
56
|
+
const isMidWord = idx => idx > 0 && idx < text.length && isWordChar(text[idx - 1]) && isWordChar(text[idx]);
|
|
57
|
+
|
|
58
|
+
// For a zero-width range (pure insertion / deletion point), only expand
|
|
59
|
+
// if the point is mid-word — i.e. both the char before and after are
|
|
60
|
+
// word characters. Otherwise the point is already at a word boundary.
|
|
61
|
+
if (from === to) {
|
|
62
|
+
if (!isMidWord(fromIdx)) {
|
|
63
|
+
return {
|
|
64
|
+
from,
|
|
65
|
+
to
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// Expand both directions from the mid-word point.
|
|
69
|
+
while (fromIdx > 0 && isWordChar(text[fromIdx - 1])) {
|
|
70
|
+
fromIdx--;
|
|
71
|
+
}
|
|
72
|
+
while (toIdx < text.length && isWordChar(text[toIdx])) {
|
|
73
|
+
toIdx++;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
from: parentStart + fromIdx,
|
|
77
|
+
to: parentStart + toIdx
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Non-empty range: expand each endpoint outward if it is mid-word.
|
|
82
|
+
|
|
83
|
+
// Expand left only if `from` is truly mid-word: the character at `from`
|
|
84
|
+
// (inside the range) and the character before `from` are both word chars.
|
|
85
|
+
if (fromIdx > 0 && fromIdx < text.length && isWordChar(text[fromIdx]) && isWordChar(text[fromIdx - 1])) {
|
|
86
|
+
while (fromIdx > 0 && isWordChar(text[fromIdx - 1])) {
|
|
87
|
+
fromIdx--;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Expand right only if `to` is truly mid-word: the character just before
|
|
92
|
+
// `to` (last char of the range) and the character at `to` are both word chars.
|
|
93
|
+
if (toIdx > 0 && toIdx < text.length && isWordChar(text[toIdx - 1]) && isWordChar(text[toIdx])) {
|
|
94
|
+
while (toIdx < text.length && isWordChar(text[toIdx])) {
|
|
95
|
+
toIdx++;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
from: parentStart + fromIdx,
|
|
100
|
+
to: parentStart + toIdx
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
7
104
|
/**
|
|
8
105
|
* Compare marks between two nodes
|
|
9
106
|
* We have to check each child because adding a mark splits text into multiple nodes
|
|
@@ -147,10 +244,46 @@ export const diffBySteps = (originalDoc, steps) => {
|
|
|
147
244
|
// wrong text/positions. Use the post-step doc here.
|
|
148
245
|
const optimizedGranularStepChanges = optimizeChanges(simplifyChanges(granularStepChanges.changes, rangedStep.doc));
|
|
149
246
|
for (const granularChange of optimizedGranularStepChanges) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
247
|
+
// Expand each granular change to the nearest word boundaries in
|
|
248
|
+
// both the pre-step doc (A-side) and the post-step doc (B-side).
|
|
249
|
+
// This ensures that a mid-word edit like "sanitised" → "sanitized"
|
|
250
|
+
// shows as deleting the whole original word and inserting the whole
|
|
251
|
+
// new word, rather than a single-character swap.
|
|
252
|
+
const expandedA = expandToWordBoundaries(rangedStep.before, granularChange.fromA, granularChange.toA);
|
|
253
|
+
const expandedB = expandToWordBoundaries(rangedStep.doc, granularChange.fromB, granularChange.toB);
|
|
254
|
+
|
|
255
|
+
// When one side expanded further than the other (e.g. a space
|
|
256
|
+
// was inserted mid-word: "altogether" → "all together"), the
|
|
257
|
+
// less-expanded side must grow to match — otherwise the renderer
|
|
258
|
+
// shows a partial word as plain text next to a deletion/insertion.
|
|
259
|
+
// We compare left and right deltas independently so partial
|
|
260
|
+
// expansion on one side doesn't prevent the other side from
|
|
261
|
+
// being pulled out further.
|
|
262
|
+
const aLeftDelta = granularChange.fromA - expandedA.from;
|
|
263
|
+
const aRightDelta = expandedA.to - granularChange.toA;
|
|
264
|
+
const bLeftDelta = granularChange.fromB - expandedB.from;
|
|
265
|
+
const bRightDelta = expandedB.to - granularChange.toB;
|
|
266
|
+
let finalA = expandedA;
|
|
267
|
+
let finalB = expandedB;
|
|
268
|
+
|
|
269
|
+
// If A expanded further on either side, nudge B outward
|
|
270
|
+
// by the excess and re-expand to snap to word boundaries.
|
|
271
|
+
if (aLeftDelta > bLeftDelta || aRightDelta > bRightDelta) {
|
|
272
|
+
const extraLeft = Math.max(0, aLeftDelta - bLeftDelta);
|
|
273
|
+
const extraRight = Math.max(0, aRightDelta - bRightDelta);
|
|
274
|
+
finalB = expandToWordBoundaries(rangedStep.doc, Math.max(expandedB.from - extraLeft, 0), expandedB.to + extraRight);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// If B expanded further on either side, nudge A outward.
|
|
278
|
+
if (bLeftDelta > aLeftDelta || bRightDelta > aRightDelta) {
|
|
279
|
+
const extraLeft = Math.max(0, bLeftDelta - aLeftDelta);
|
|
280
|
+
const extraRight = Math.max(0, bRightDelta - aRightDelta);
|
|
281
|
+
finalA = expandToWordBoundaries(rangedStep.before, Math.max(expandedA.from - extraLeft, 0), expandedA.to + extraRight);
|
|
282
|
+
}
|
|
283
|
+
const granularFromA = mapPosition(beforeStepToOriginal, finalA.from);
|
|
284
|
+
const granularToA = mapPosition(beforeStepToOriginal, finalA.to);
|
|
285
|
+
const granularFromB = mapPosition(afterStepToFinal, finalB.from);
|
|
286
|
+
const granularToB = mapPosition(afterStepToFinal, finalB.to);
|
|
154
287
|
changes.push({
|
|
155
288
|
fromA: granularFromA,
|
|
156
289
|
toA: granularToA,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
2
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
4
3
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
5
4
|
import { standardDecorationMarkerVariable, editingStyleQuoteNode, editingStyleRuleNode, editingStyleCardBlockNode, editingStyleNode, deletedContentStyleNew, deletedStyleQuoteNode, addedCellOverlayStyle, deletedCellOverlayStyle } from './colorSchemes/standard';
|
|
6
|
-
import {
|
|
5
|
+
import { traditionalDecorationMarkerVariableActive, traditionalDecorationMarkerVariableNew, traditionalDeletedDecorationMarkerVariableActive, traditionalDeletedDecorationMarkerVariableNew, traditionalStyleQuoteNodeActive, traditionalStyleQuoteNodeNew, traditionalStyleRuleNodeActive, traditionalStyleRuleNodeNew, traditionalStyleCardBlockNodeActive, traditionalStyleCardBlockNodeNew, traditionalStyleNodeActive, traditionalStyleNodeNew, getDeletedTraditionalInlineStyle, deletedTraditionalStyleQuoteNode, traditionalAddedCellOverlayStyle, deletedTraditionalCellOverlayStyle } from './colorSchemes/traditional';
|
|
7
6
|
const displayNoneStyle = convertToInlineCss({
|
|
8
7
|
display: 'none'
|
|
9
8
|
});
|
|
@@ -26,7 +25,7 @@ const getBlockNodeStyle = ({
|
|
|
26
25
|
// Handle table separately to avoid border issues
|
|
27
26
|
'tableRow', 'paragraph',
|
|
28
27
|
// Paragraph and heading nodes do not need special styling
|
|
29
|
-
'heading', 'hardBreak', 'decisionList', 'taskList',
|
|
28
|
+
'heading', 'hardBreak', 'decisionList', 'taskList', 'bulletList', 'orderedList', 'layoutSection'].includes(nodeName)) {
|
|
30
29
|
// Layout nodes do not need special styling
|
|
31
30
|
return undefined;
|
|
32
31
|
}
|
|
@@ -43,51 +42,51 @@ const getBlockNodeStyle = ({
|
|
|
43
42
|
if (['extension', 'embedCard', 'listItem'].includes(nodeName)) {
|
|
44
43
|
if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
|
|
45
44
|
if (isInserted) {
|
|
46
|
-
return isTraditional && isActive ? traditionalDecorationMarkerVariableActive : isTraditional ?
|
|
45
|
+
return isTraditional && isActive ? traditionalDecorationMarkerVariableActive : isTraditional ? traditionalDecorationMarkerVariableNew : standardDecorationMarkerVariable;
|
|
47
46
|
} else {
|
|
48
|
-
return isTraditional && isActive ? traditionalDeletedDecorationMarkerVariableActive : isTraditional ?
|
|
47
|
+
return isTraditional && isActive ? traditionalDeletedDecorationMarkerVariableActive : isTraditional ? traditionalDeletedDecorationMarkerVariableNew : deletedContentStyleNew;
|
|
49
48
|
}
|
|
50
49
|
}
|
|
51
|
-
return isTraditional && isActive ? traditionalDecorationMarkerVariableActive : isTraditional ?
|
|
50
|
+
return isTraditional && isActive ? traditionalDecorationMarkerVariableActive : isTraditional ? traditionalDecorationMarkerVariableNew : standardDecorationMarkerVariable;
|
|
52
51
|
}
|
|
53
52
|
if (nodeName === 'blockquote') {
|
|
54
53
|
if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
|
|
55
54
|
if (isInserted) {
|
|
56
|
-
return isTraditional ? isActive ? traditionalStyleQuoteNodeActive :
|
|
55
|
+
return isTraditional ? isActive ? traditionalStyleQuoteNodeActive : traditionalStyleQuoteNodeNew : editingStyleQuoteNode;
|
|
57
56
|
} else {
|
|
58
57
|
return isTraditional ? deletedTraditionalStyleQuoteNode : deletedStyleQuoteNode;
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
|
-
return isTraditional ? isActive ? traditionalStyleQuoteNodeActive :
|
|
60
|
+
return isTraditional ? isActive ? traditionalStyleQuoteNodeActive : traditionalStyleQuoteNodeNew : editingStyleQuoteNode;
|
|
62
61
|
}
|
|
63
62
|
if (nodeName === 'rule') {
|
|
64
63
|
if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
|
|
65
64
|
if (isInserted) {
|
|
66
|
-
return isTraditional ? isActive ? traditionalStyleRuleNodeActive :
|
|
65
|
+
return isTraditional ? isActive ? traditionalStyleRuleNodeActive : traditionalStyleRuleNodeNew : editingStyleRuleNode;
|
|
67
66
|
} else {
|
|
68
67
|
return isTraditional ? getDeletedTraditionalInlineStyle(false) : deletedContentStyleNew;
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
|
-
return isTraditional ? isActive ? traditionalStyleRuleNodeActive :
|
|
70
|
+
return isTraditional ? isActive ? traditionalStyleRuleNodeActive : traditionalStyleRuleNodeNew : editingStyleRuleNode;
|
|
72
71
|
}
|
|
73
72
|
if (nodeName === 'blockCard') {
|
|
74
73
|
if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
|
|
75
74
|
if (isInserted) {
|
|
76
|
-
return isTraditional ? isActive ? traditionalStyleCardBlockNodeActive :
|
|
75
|
+
return isTraditional ? isActive ? traditionalStyleCardBlockNodeActive : traditionalStyleCardBlockNodeNew : editingStyleCardBlockNode;
|
|
77
76
|
} else {
|
|
78
77
|
return isTraditional ? getDeletedTraditionalInlineStyle(false) : deletedContentStyleNew;
|
|
79
78
|
}
|
|
80
79
|
}
|
|
81
|
-
return isTraditional ? isActive ? traditionalStyleCardBlockNodeActive :
|
|
80
|
+
return isTraditional ? isActive ? traditionalStyleCardBlockNodeActive : traditionalStyleCardBlockNodeNew : editingStyleCardBlockNode;
|
|
82
81
|
}
|
|
83
82
|
if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
|
|
84
83
|
if (isInserted) {
|
|
85
|
-
return isTraditional ? isActive ? traditionalStyleNodeActive :
|
|
84
|
+
return isTraditional ? isActive ? traditionalStyleNodeActive : traditionalStyleNodeNew : editingStyleNode;
|
|
86
85
|
} else {
|
|
87
86
|
return isTraditional ? getDeletedTraditionalInlineStyle(false) : deletedContentStyleNew;
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
|
-
return isTraditional ? isActive ? traditionalStyleNodeActive :
|
|
89
|
+
return isTraditional ? isActive ? traditionalStyleNodeActive : traditionalStyleNodeNew : editingStyleNode;
|
|
91
90
|
};
|
|
92
91
|
|
|
93
92
|
/**
|
|
@@ -140,20 +139,9 @@ export const createBlockChangedDecoration = ({
|
|
|
140
139
|
});
|
|
141
140
|
}
|
|
142
141
|
const className = getNodeClass(change.name);
|
|
143
|
-
if (
|
|
144
|
-
if (style || className) {
|
|
145
|
-
decorations.push(Decoration.node(change.from, change.to, {
|
|
146
|
-
style: style,
|
|
147
|
-
'data-testid': 'show-diff-changed-decoration-node',
|
|
148
|
-
class: className
|
|
149
|
-
}, {
|
|
150
|
-
key: 'diff-block',
|
|
151
|
-
nodeName: change.name
|
|
152
|
-
}));
|
|
153
|
-
}
|
|
154
|
-
} else {
|
|
142
|
+
if (style || className) {
|
|
155
143
|
decorations.push(Decoration.node(change.from, change.to, {
|
|
156
|
-
style,
|
|
144
|
+
style: style,
|
|
157
145
|
'data-testid': 'show-diff-changed-decoration-node',
|
|
158
146
|
class: className
|
|
159
147
|
}, {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
2
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
4
3
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
5
4
|
import { editingStyle, editingStyleActive, deletedContentStyle, deletedContentStyleActive, deletedContentStyleNew, deletedContentStyleUnbounded, deletedInlineContentBackground } from './colorSchemes/standard';
|
|
6
5
|
import { traditionalInsertStyle, traditionalInsertStyleActive, getDeletedTraditionalInlineStyle, deletedTraditionalContentStyleUnbounded, deletedTraditionalContentStyleUnboundedActive } from './colorSchemes/traditional';
|
|
@@ -42,21 +41,6 @@ const getDeletedContentStyle = (colorScheme, isActive = false) => {
|
|
|
42
41
|
return expValEquals('platform_editor_enghealth_a11y_jan_fixes', 'isEnabled', true) ? deletedContentStyleNew : deletedContentStyle;
|
|
43
42
|
};
|
|
44
43
|
|
|
45
|
-
/**
|
|
46
|
-
* Wraps content with deleted styling without opacity (for use when content is a direct child of dom)
|
|
47
|
-
*/
|
|
48
|
-
const createDeletedStyleWrapperWithoutOpacity = (colorScheme, isActive) => {
|
|
49
|
-
const wrapper = document.createElement('span');
|
|
50
|
-
if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
|
|
51
|
-
wrapper.setAttribute('style',
|
|
52
|
-
// Merge into existing styles when cleaning up
|
|
53
|
-
getDeletedContentStyle(colorScheme, isActive) + deletedInlineContentBackground);
|
|
54
|
-
} else {
|
|
55
|
-
wrapper.setAttribute('style', getDeletedContentStyle(colorScheme, isActive));
|
|
56
|
-
}
|
|
57
|
-
return wrapper;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
44
|
/**
|
|
61
45
|
* CSS backgrounds don't work when applied to a wrapper around a paragraph, so
|
|
62
46
|
* the wrapper needs to be injected inside the node around the child content
|
|
@@ -117,7 +101,7 @@ export const createNodeChangedDecorationWidget = ({
|
|
|
117
101
|
}) => {
|
|
118
102
|
var _slice$content, _slice$content2, _slice$content2$first, _slice$content3, _slice$content3$first;
|
|
119
103
|
const slice = doc.slice(change.fromA, change.toA);
|
|
120
|
-
const shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (slice === null || slice === void 0 ? void 0 : (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.childCount) === 1 && (slice === null || slice === void 0 ? void 0 : (_slice$content2 = slice.content) === null || _slice$content2 === void 0 ? void 0 : (_slice$content2$first = _slice$content2.firstChild) === null || _slice$content2$first === void 0 ? void 0 : _slice$content2$first.type.name) === 'paragraph' && (slice === null || slice === void 0 ? void 0 : (_slice$content3 = slice.content) === null || _slice$content3 === void 0 ? void 0 : (_slice$content3$first = _slice$content3.firstChild) === null || _slice$content3$first === void 0 ? void 0 : _slice$content3$first.content.size) === 0
|
|
104
|
+
const shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (slice === null || slice === void 0 ? void 0 : (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.childCount) === 1 && (slice === null || slice === void 0 ? void 0 : (_slice$content2 = slice.content) === null || _slice$content2 === void 0 ? void 0 : (_slice$content2$first = _slice$content2.firstChild) === null || _slice$content2$first === void 0 ? void 0 : _slice$content2$first.type.name) === 'paragraph' && (slice === null || slice === void 0 ? void 0 : (_slice$content3 = slice.content) === null || _slice$content3 === void 0 ? void 0 : (_slice$content3$first = _slice$content3.firstChild) === null || _slice$content3$first === void 0 ? void 0 : _slice$content3$first.content.size) === 0;
|
|
121
105
|
// Widget decoration used for deletions as the content is not in the document
|
|
122
106
|
// and we want to display the deleted content with a style.
|
|
123
107
|
const safeInsertPos = findSafeInsertPos(newDoc, change.fromB, slice);
|
|
@@ -228,22 +212,16 @@ export const createNodeChangedDecorationWidget = ({
|
|
|
228
212
|
} else {
|
|
229
213
|
const fallbackNode = fallbackSerialization();
|
|
230
214
|
if (fallbackNode) {
|
|
231
|
-
if (
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
dom.append(injectedNode);
|
|
240
|
-
} else {
|
|
241
|
-
const wrapper = createContentWrapper(colorScheme, isActive, isInserted);
|
|
242
|
-
wrapper.append(fallbackNode);
|
|
243
|
-
dom.append(wrapper);
|
|
244
|
-
}
|
|
215
|
+
if (fallbackNode instanceof HTMLElement) {
|
|
216
|
+
const injectedNode = injectInnerWrapper({
|
|
217
|
+
node: fallbackNode,
|
|
218
|
+
colorScheme,
|
|
219
|
+
isActive,
|
|
220
|
+
isInserted
|
|
221
|
+
});
|
|
222
|
+
dom.append(injectedNode);
|
|
245
223
|
} else {
|
|
246
|
-
const wrapper =
|
|
224
|
+
const wrapper = createContentWrapper(colorScheme, isActive, isInserted);
|
|
247
225
|
wrapper.append(fallbackNode);
|
|
248
226
|
dom.append(wrapper);
|
|
249
227
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { SetAttrsStep } from '@atlaskit/adf-schema/steps';
|
|
2
2
|
import { AttrStep } from '@atlaskit/editor-prosemirror/transform';
|
|
3
|
-
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
4
3
|
const filterUndefined = x => !!x;
|
|
5
4
|
|
|
6
5
|
// Attributes that indicate a change in media image
|
|
@@ -34,33 +33,32 @@ export const getAttrChangeRanges = (doc, steps) => {
|
|
|
34
33
|
const stepAttrs = getStepAttrs(step);
|
|
35
34
|
const $pos = doc.resolve(step.pos);
|
|
36
35
|
const nodeAtPos = doc.nodeAt(step.pos);
|
|
37
|
-
if (expValEquals('platform_editor_show_diff_improvements', 'isEnabled', true)) {
|
|
38
|
-
// date node: timestamp attribute change — highlight the date node itself (inline)
|
|
39
|
-
if (stepAttrs.some(v => dateAttrs.includes(v)) && (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type.name) === 'date') {
|
|
40
|
-
return {
|
|
41
|
-
fromB: step.pos,
|
|
42
|
-
toB: step.pos + nodeAtPos.nodeSize,
|
|
43
|
-
isInline: true
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
36
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
37
|
+
// date node: timestamp attribute change — highlight the date node itself (inline)
|
|
38
|
+
if (stepAttrs.some(v => dateAttrs.includes(v)) && (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type.name) === 'date') {
|
|
39
|
+
return {
|
|
40
|
+
fromB: step.pos,
|
|
41
|
+
toB: step.pos + nodeAtPos.nodeSize,
|
|
42
|
+
isInline: true
|
|
43
|
+
};
|
|
44
|
+
}
|
|
54
45
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
46
|
+
// taskItem node: state attribute change — highlight the taskItem node
|
|
47
|
+
if (stepAttrs.some(v => taskItemAttrs.includes(v)) && (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type.name) === 'taskItem') {
|
|
48
|
+
return {
|
|
49
|
+
fromB: step.pos,
|
|
50
|
+
toB: step.pos + nodeAtPos.nodeSize
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// extension nodes: any attribute change except localId — highlight the node
|
|
55
|
+
if (nodeAtPos && extensionNodeNames.includes(nodeAtPos.type.name) && stepAttrs.some(v => !extensionExcludedAttrs.includes(v))) {
|
|
56
|
+
const isInline = nodeAtPos.type.name === 'inlineExtension';
|
|
57
|
+
return {
|
|
58
|
+
fromB: step.pos,
|
|
59
|
+
toB: step.pos + nodeAtPos.nodeSize,
|
|
60
|
+
isInline
|
|
61
|
+
};
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
// media node: id/collection/url attribute change — highlight the mediaSingle parent
|