@atlaskit/editor-plugin-show-diff 10.5.1 → 10.5.3

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.
Files changed (24) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +30 -3
  3. package/dist/cjs/pm-plugins/decorations/colorSchemes/standard.js +13 -1
  4. package/dist/cjs/pm-plugins/decorations/colorSchemes/traditional.js +15 -2
  5. package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +83 -26
  6. package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +23 -4
  7. package/dist/cjs/pm-plugins/decorations/utils/getAttrChangeRanges.js +64 -4
  8. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +30 -3
  9. package/dist/es2019/pm-plugins/decorations/colorSchemes/standard.js +12 -0
  10. package/dist/es2019/pm-plugins/decorations/colorSchemes/traditional.js +14 -1
  11. package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +67 -9
  12. package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +16 -1
  13. package/dist/es2019/pm-plugins/decorations/utils/getAttrChangeRanges.js +53 -4
  14. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +30 -3
  15. package/dist/esm/pm-plugins/decorations/colorSchemes/standard.js +12 -0
  16. package/dist/esm/pm-plugins/decorations/colorSchemes/traditional.js +14 -1
  17. package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +85 -28
  18. package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +23 -4
  19. package/dist/esm/pm-plugins/decorations/utils/getAttrChangeRanges.js +63 -4
  20. package/dist/types/pm-plugins/decorations/colorSchemes/standard.d.ts +1 -0
  21. package/dist/types/pm-plugins/decorations/colorSchemes/traditional.d.ts +2 -1
  22. package/dist/types/pm-plugins/decorations/createInlineChangedDecoration.d.ts +4 -1
  23. package/dist/types/pm-plugins/decorations/utils/getAttrChangeRanges.d.ts +11 -1
  24. package/package.json +3 -3
@@ -2,11 +2,54 @@ import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document
2
2
  import { findParentNodeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
3
3
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
4
4
  import { TableMap } from '@atlaskit/editor-tables/table-map';
5
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
5
6
  import { isExtendedEnabled } from '../isExtendedEnabled';
6
- import { addedCellOverlayStyle, deletedRowStyle, deletedCellOverlayStyle } from './colorSchemes/standard';
7
- import { deletedTraditionalRowStyle, deletedTraditionalCellOverlayStyle, traditionalAddedCellOverlayStyle } from './colorSchemes/traditional';
7
+ import { addedCellOverlayStyle, addedCellOverlayRoundedStyle, deletedRowStyle, deletedCellOverlayStyle } from './colorSchemes/standard';
8
+ import { deletedTraditionalRowStyle, deletedTraditionalCellOverlayStyle, traditionalAddedCellOverlayStyle, traditionalAddedCellOverlayRoundedStyle } from './colorSchemes/traditional';
8
9
  import { buildDiffDecorationSpec } from './decorationKeys';
9
10
  import { findSafeInsertPos } from './utils/findSafeInsertPos';
11
+ /**
12
+ * Inserted table rows are rendered as decoration widgets, so their cells do not get the
13
+ * `data-reaches-*` attrs normally applied by the table cell node view. Mirror that boundary
14
+ * metadata here so the existing rounded-table CSS can round widget cells like real table cells.
15
+ */
16
+ const getChangedRowCellEdgeAttrs = ({
17
+ rowNode,
18
+ rowStart,
19
+ tableMap
20
+ }) => {
21
+ const cellEdgeAttrs = [];
22
+ rowNode.content.forEach((cellNode, cellOffset) => {
23
+ if (cellNode.type.name !== 'tableCell' && cellNode.type.name !== 'tableHeader') {
24
+ return;
25
+ }
26
+ const cellRect = tableMap.findCell(rowStart + 1 + cellOffset);
27
+ cellEdgeAttrs.push({
28
+ reachesBottom: cellRect.bottom >= tableMap.height,
29
+ reachesLeft: cellRect.left === 0,
30
+ reachesRight: cellRect.right >= tableMap.width,
31
+ reachesTop: cellRect.top === 0
32
+ });
33
+ });
34
+ return cellEdgeAttrs;
35
+ };
36
+ const applyCellEdgeAttrs = (cell, edgeAttrs) => {
37
+ if (!edgeAttrs) {
38
+ return;
39
+ }
40
+ if (edgeAttrs.reachesTop) {
41
+ cell.setAttribute('data-reaches-top', 'true');
42
+ }
43
+ if (edgeAttrs.reachesBottom) {
44
+ cell.setAttribute('data-reaches-bottom', 'true');
45
+ }
46
+ if (edgeAttrs.reachesLeft) {
47
+ cell.setAttribute('data-reaches-left', 'true');
48
+ }
49
+ if (edgeAttrs.reachesRight) {
50
+ cell.setAttribute('data-reaches-right', 'true');
51
+ }
52
+ };
10
53
  /**
11
54
  * Extracts information about deleted table rows from a change
12
55
  */
@@ -52,10 +95,16 @@ const extractChangedRows = ({
52
95
  // Check if this row overlaps with the deletion range
53
96
  const rowOverlapsChange = rowStart >= changeStartInTable && rowStart < changeEndInTable || rowEnd > changeStartInTable && rowEnd <= changeEndInTable || rowStart < changeStartInTable && rowEnd > changeEndInTable;
54
97
  if (rowOverlapsChange && rowNode.type.name === 'tableRow' && (isExtendedEnabled(diffType) || !isEmptyRow(rowNode))) {
98
+ const cellEdgeAttrs = isExtendedEnabled(diffType) && expValEquals('platform_editor_table_diff_rounded_corners', 'isEnabled', true) ? getChangedRowCellEdgeAttrs({
99
+ rowNode,
100
+ rowStart,
101
+ tableMap: oldTableMap
102
+ }) : undefined;
55
103
  const startOfRow = newTableMap.mapByRow.slice().reverse().find(row => row[0] + tableNew.pos < change.fromB && change.fromB < row[row.length - 1] + tableNew.pos);
56
104
  changedRows.push({
57
105
  rowIndex,
58
106
  rowNode,
107
+ cellEdgeAttrs,
59
108
  fromA: tableOld.pos + 1 + rowStart,
60
109
  toA: tableOld.pos + 1 + rowEnd,
61
110
  fromB: startOfRow ? startOfRow[0] + tableNew.start : change.fromB
@@ -102,7 +151,7 @@ const isEmptyRow = rowNode => {
102
151
  /**
103
152
  * Creates a DOM representation of a deleted table row
104
153
  */
105
- const createChangedRowDOM = (rowNode, nodeViewSerializer, colorScheme, isInserted, diffType) => {
154
+ const createChangedRowDOM = (rowNode, cellEdgeAttrs, nodeViewSerializer, colorScheme, isInserted, diffType) => {
106
155
  const tr = document.createElement('tr');
107
156
  if (isExtendedEnabled(diffType)) {
108
157
  if (!isInserted) {
@@ -114,15 +163,23 @@ const createChangedRowDOM = (rowNode, nodeViewSerializer, colorScheme, isInserte
114
163
  tr.setAttribute('data-testid', 'show-diff-deleted-row');
115
164
 
116
165
  // Serialize each cell in the row
166
+ let cellIndex = 0;
117
167
  rowNode.content.forEach(cellNode => {
118
168
  if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
119
169
  const nodeView = nodeViewSerializer.tryCreateNodeView(cellNode);
120
170
  if (nodeView) {
121
- if (nodeView instanceof HTMLElement && isExtendedEnabled(diffType)) {
122
- const overlay = document.createElement('span');
123
- const overlayStyle = colorScheme === 'traditional' ? isInserted ? traditionalAddedCellOverlayStyle : deletedTraditionalCellOverlayStyle : isInserted ? addedCellOverlayStyle : deletedCellOverlayStyle;
124
- overlay.setAttribute('style', overlayStyle);
125
- nodeView.appendChild(overlay);
171
+ if (nodeView instanceof HTMLElement) {
172
+ applyCellEdgeAttrs(nodeView, cellEdgeAttrs === null || cellEdgeAttrs === void 0 ? void 0 : cellEdgeAttrs[cellIndex]);
173
+ if (isExtendedEnabled(diffType)) {
174
+ const overlay = document.createElement('span');
175
+ const isRoundedTable = expValEquals('platform_editor_table_diff_rounded_corners', 'isEnabled', true);
176
+ const isTraditional = colorScheme === 'traditional';
177
+ const deletedCellStyle = isTraditional ? deletedTraditionalCellOverlayStyle : deletedCellOverlayStyle;
178
+ const addedCellStyle = isTraditional ? isRoundedTable ? traditionalAddedCellOverlayRoundedStyle : traditionalAddedCellOverlayStyle : isRoundedTable ? addedCellOverlayRoundedStyle : addedCellOverlayStyle;
179
+ const overlayStyle = isInserted ? addedCellStyle : deletedCellStyle;
180
+ overlay.setAttribute('style', overlayStyle);
181
+ nodeView.appendChild(overlay);
182
+ }
126
183
  }
127
184
  tr.appendChild(nodeView);
128
185
  } else {
@@ -132,6 +189,7 @@ const createChangedRowDOM = (rowNode, nodeViewSerializer, colorScheme, isInserte
132
189
  tr.appendChild(serializedContent);
133
190
  }
134
191
  }
192
+ cellIndex++;
135
193
  }
136
194
  });
137
195
  return tr;
@@ -182,7 +240,7 @@ export const createChangedRowDecorationWidgets = ({
182
240
  diffType
183
241
  });
184
242
  return changedRows.map(changedRow => {
185
- const rowDOM = createChangedRowDOM(changedRow.rowNode, nodeViewSerializer, colorScheme, isInserted, diffType);
243
+ const rowDOM = createChangedRowDOM(changedRow.rowNode, changedRow.cellEdgeAttrs, nodeViewSerializer, colorScheme, isInserted, diffType);
186
244
 
187
245
  // Find safe insertion position for the deleted row
188
246
  const safeInsertPos = findSafeInsertPos(newDoc, changedRow.fromB - 1,
@@ -8,6 +8,16 @@ import { buildDiffDecorationSpec } from './decorationKeys';
8
8
  const displayNoneStyle = convertToInlineCss({
9
9
  display: 'none'
10
10
  });
11
+ const getAtomicInlineNodeClassName = (inlineNodeName, colorScheme) => {
12
+ const classNames = ['show-diff-atomic-inline-changed'];
13
+ if (inlineNodeName) {
14
+ classNames.push(`show-diff-atomic-inline-changed-${inlineNodeName}`);
15
+ }
16
+ if (colorScheme === 'traditional') {
17
+ classNames.push('show-diff-atomic-inline-changed-traditional');
18
+ }
19
+ return classNames.join(' ');
20
+ };
11
21
  /**
12
22
  * Inline decoration used for insertions as the content already exists in the document
13
23
  *
@@ -20,11 +30,13 @@ export const createInlineChangedDecoration = ({
20
30
  colorScheme,
21
31
  isActive = false,
22
32
  isInserted = true,
33
+ isAtomicInlineNode = false,
23
34
  shouldHideDeleted = false,
24
35
  showIndicators = false,
25
36
  doc,
26
37
  diffType,
27
- hideAddedDiffsUnderline = false
38
+ hideAddedDiffsUnderline = false,
39
+ inlineNodeName
28
40
  }) => {
29
41
  const diffId = crypto.randomUUID();
30
42
  if (shouldHideDeleted) {
@@ -66,6 +78,9 @@ export const createInlineChangedDecoration = ({
66
78
  }
67
79
  const decorations = [Decoration.inline(change.fromB, change.toB, {
68
80
  style,
81
+ ...(isAtomicInlineNode && isInserted && {
82
+ class: getAtomicInlineNodeClassName(inlineNodeName, colorScheme)
83
+ }),
69
84
  'data-testid': 'show-diff-changed-decoration'
70
85
  }, buildDiffDecorationSpec({
71
86
  decorationType: 'inline',
@@ -1,5 +1,6 @@
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';
3
4
  const filterUndefined = x => !!x;
4
5
 
5
6
  // Attributes that indicate a change in media image
@@ -11,6 +12,24 @@ const dateAttrs = ['timestamp'];
11
12
  // Attribute that indicates a task item state change
12
13
  const taskItemAttrs = ['state'];
13
14
 
15
+ // Attributes that indicate an emoji change
16
+ const emojiAttrs = ['shortName', 'id', 'text'];
17
+
18
+ // Attributes that indicate a mention change (who is mentioned)
19
+ const mentionAttrs = ['id', 'text'];
20
+
21
+ // Attributes that indicate a status change (label or colour)
22
+ const statusAttrs = ['text', 'color'];
23
+
24
+ // Map of node type name → the attrs that represent a meaningful content change for that node
25
+ const inlineNodeAttrMap = {
26
+ date: dateAttrs,
27
+ emoji: emojiAttrs,
28
+ mention: mentionAttrs,
29
+ status: statusAttrs
30
+ };
31
+ const isInlineAttrChangeNodeName = nodeName => nodeName in inlineNodeAttrMap;
32
+
14
33
  // Attributes excluded from extension change detection (not meaningful content changes)
15
34
  const extensionExcludedAttrs = ['localId'];
16
35
 
@@ -25,7 +44,7 @@ const getStepAttrs = step => {
25
44
  }
26
45
  return [];
27
46
  };
28
- export const getAttrChangeRanges = (doc, steps) => {
47
+ export const getAttrChangeRanges = (doc, steps, originalDoc) => {
29
48
  return steps.map(step => {
30
49
  if (!(step instanceof AttrStep) && !(step instanceof SetAttrsStep)) {
31
50
  return undefined;
@@ -35,14 +54,38 @@ export const getAttrChangeRanges = (doc, steps) => {
35
54
  const nodeAtPos = doc.nodeAt(step.pos);
36
55
 
37
56
  // 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') {
57
+ if (stepAttrs.some(v => dateAttrs.includes(v)) && (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type.name) === 'date' && !expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true)) {
39
58
  return {
40
59
  fromB: step.pos,
41
60
  toB: step.pos + nodeAtPos.nodeSize,
42
- isInline: true
61
+ isInline: true,
62
+ inlineNodeName: 'date'
43
63
  };
44
64
  }
45
65
 
66
+ // The following inline node attr changes are gated behind platform_editor_improve_inline_diffs.
67
+ // The changeset path (createDecorationsForChange) handles the deletion widget via
68
+ // prosemirror-changeset; we only need to add the inline insertion highlight here.
69
+ if (expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true) && nodeAtPos) {
70
+ const nodeName = nodeAtPos.type.name;
71
+ if (isInlineAttrChangeNodeName(nodeName)) {
72
+ const watchedAttrs = inlineNodeAttrMap[nodeName];
73
+ if (stepAttrs.some(v => watchedAttrs.includes(v))) {
74
+ const originalNodeAtPos = originalDoc === null || originalDoc === void 0 ? void 0 : originalDoc.nodeAt(step.pos);
75
+ return {
76
+ fromB: step.pos,
77
+ toB: step.pos + nodeAtPos.nodeSize,
78
+ isInline: true,
79
+ inlineNodeName: nodeName,
80
+ ...(originalNodeAtPos && {
81
+ fromA: step.pos,
82
+ toA: step.pos + originalNodeAtPos.nodeSize
83
+ })
84
+ };
85
+ }
86
+ }
87
+ }
88
+
46
89
  // taskItem node: state attribute change — highlight the taskItem node
47
90
  if (stepAttrs.some(v => taskItemAttrs.includes(v)) && (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type.name) === 'taskItem') {
48
91
  return {
@@ -70,7 +113,13 @@ export const getAttrChangeRanges = (doc, steps) => {
70
113
  };
71
114
  }
72
115
  return undefined;
73
- }).filter(filterUndefined);
116
+ }).filter(filterUndefined)
117
+ // Deduplicate by node position: multiple AttrSteps on the same node
118
+ // (e.g. setNodeAttribute(pos, 'text', ...) + setNodeAttribute(pos, 'color', ...))
119
+ // should produce only one decoration, not one per step.
120
+ // Gated behind the same experiment as the inline node attr changes that introduced
121
+ // the possibility of multiple ranges for the same node position.
122
+ .filter((range, i, arr) => !expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true) || arr.findIndex(r => r.fromB === range.fromB) === i);
74
123
  };
75
124
 
76
125
  /**
@@ -421,16 +421,43 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
421
421
  isInserted: true
422
422
  })));
423
423
  });
424
- getAttrChangeRanges(tr.doc, attrSteps).forEach(function (change) {
424
+ getAttrChangeRanges(tr.doc, attrSteps, originalDoc).forEach(function (change) {
425
425
  if (change.isInline) {
426
- // Inline nodes (e.g. date) need an inline decoration rather than a block decoration
426
+ // Inline nodes (e.g. date, emoji, mention, status) need an inline decoration rather than a block decoration
427
427
  var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
428
+ var isAtomicInlineNodeFlag = expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true);
428
429
  decorations.push.apply(decorations, _toConsumableArray(createInlineChangedDecoration({
429
430
  change: change,
430
431
  colorScheme: colorScheme,
431
432
  isActive: isActive,
432
- isInserted: true
433
+ isInserted: true,
434
+ isAtomicInlineNode: isAtomicInlineNodeFlag,
435
+ inlineNodeName: change.inlineNodeName,
436
+ // Suppress the border-bottom underline for atomic inline nodeviews —
437
+ // it doesn't render on custom nodeview DOM elements and is unnecessary noise.
438
+ hideAddedDiffsUnderline: isAtomicInlineNodeFlag,
439
+ diffType: diffType
433
440
  })));
441
+ // If we have the original node position, also render the old node as a "deleted" widget
442
+ // so the user can see what it looked like before the change.
443
+ if (change.fromA !== undefined && change.toA !== undefined && expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true)) {
444
+ decorations.push.apply(decorations, _toConsumableArray(createNodeChangedDecorationWidget({
445
+ change: {
446
+ fromA: change.fromA,
447
+ toA: change.toA,
448
+ fromB: change.fromB,
449
+ toB: change.toB,
450
+ deleted: []
451
+ },
452
+ doc: originalDoc,
453
+ nodeViewSerializer: nodeViewSerializer,
454
+ colorScheme: colorScheme,
455
+ newDoc: tr.doc,
456
+ intl: intl,
457
+ activeIndexPos: activeIndexPos,
458
+ isInserted: false
459
+ })));
460
+ }
434
461
  } else {
435
462
  decorations.push.apply(decorations, _toConsumableArray(calculateNodesForBlockDecoration({
436
463
  doc: tr.doc,
@@ -170,6 +170,18 @@ export var addedCellOverlayStyle = convertToInlineCss({
170
170
  outline: "1px solid ".concat("var(--ds-border-accent-purple, #AF59E1)"),
171
171
  pointerEvents: 'none'
172
172
  });
173
+ export var addedCellOverlayRoundedStyle = convertToInlineCss({
174
+ position: 'absolute',
175
+ top: 0,
176
+ left: 0,
177
+ width: '100%',
178
+ height: '100%',
179
+ backgroundColor: "rgba(from ".concat("var(--ds-background-accent-purple-subtlest, #F8EEFE)", " r g b / 0.5)"),
180
+ zIndex: 2,
181
+ outline: "1px solid ".concat("var(--ds-border-accent-purple, #AF59E1)"),
182
+ pointerEvents: 'none',
183
+ borderRadius: 'inherit'
184
+ });
173
185
  export var deletedCellOverlayStyle = convertToInlineCss({
174
186
  position: 'absolute',
175
187
  top: 0,
@@ -84,7 +84,7 @@ export var deletedTraditionalBlockOutlineRoundedActive = convertToInlineCss({
84
84
  borderRadius: "calc(".concat("var(--ds-radius-xsmall, 2px)", " + 1px)")
85
85
  });
86
86
 
87
- /** Scroll-navigation “new” emphasis: 4px ring (see {@link traditionalStyleNodeNew}). */
87
+ /** Scroll-navigation “new” emphasis: 4px ring. */
88
88
  export var deletedTraditionalBlockOutlineNew = convertToInlineCss({
89
89
  boxShadow: "0 0 0 4px ".concat("var(--ds-background-accent-red-subtlest, #FFECEB)"),
90
90
  borderRadius: "var(--ds-radius-small, 4px)"
@@ -184,6 +184,19 @@ export var traditionalAddedCellOverlayStyle = convertToInlineCss({
184
184
  outline: "1px solid ".concat("var(--ds-border-accent-green, #22A06B)"),
185
185
  pointerEvents: 'none'
186
186
  });
187
+ export var traditionalAddedCellOverlayRoundedStyle = convertToInlineCss({
188
+ position: 'absolute',
189
+ top: 0,
190
+ left: 0,
191
+ width: '100%',
192
+ height: '100%',
193
+ backgroundColor: "rgba(from ".concat("var(--ds-background-accent-green-subtlest, #DCFFF1)", " r g b / 0.5)"),
194
+ zIndex: 2,
195
+ outline: "1px solid ".concat("var(--ds-border-accent-green, #22A06B)"),
196
+ pointerEvents: 'none',
197
+ border: 'inherit',
198
+ borderRadius: 'inherit'
199
+ });
187
200
  export var traditionalAddedCellOverlayStyleNew = convertToInlineCss({
188
201
  position: 'absolute',
189
202
  top: 0,
@@ -9,19 +9,61 @@ import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document
9
9
  import { findParentNodeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
11
11
  import { TableMap } from '@atlaskit/editor-tables/table-map';
12
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
12
13
  import { isExtendedEnabled } from '../isExtendedEnabled';
13
- import { addedCellOverlayStyle, deletedRowStyle, deletedCellOverlayStyle } from './colorSchemes/standard';
14
- import { deletedTraditionalRowStyle, deletedTraditionalCellOverlayStyle, traditionalAddedCellOverlayStyle } from './colorSchemes/traditional';
14
+ import { addedCellOverlayStyle, addedCellOverlayRoundedStyle, deletedRowStyle, deletedCellOverlayStyle } from './colorSchemes/standard';
15
+ import { deletedTraditionalRowStyle, deletedTraditionalCellOverlayStyle, traditionalAddedCellOverlayStyle, traditionalAddedCellOverlayRoundedStyle } from './colorSchemes/traditional';
15
16
  import { buildDiffDecorationSpec } from './decorationKeys';
16
17
  import { findSafeInsertPos } from './utils/findSafeInsertPos';
18
+ /**
19
+ * Inserted table rows are rendered as decoration widgets, so their cells do not get the
20
+ * `data-reaches-*` attrs normally applied by the table cell node view. Mirror that boundary
21
+ * metadata here so the existing rounded-table CSS can round widget cells like real table cells.
22
+ */
23
+ var getChangedRowCellEdgeAttrs = function getChangedRowCellEdgeAttrs(_ref) {
24
+ var rowNode = _ref.rowNode,
25
+ rowStart = _ref.rowStart,
26
+ tableMap = _ref.tableMap;
27
+ var cellEdgeAttrs = [];
28
+ rowNode.content.forEach(function (cellNode, cellOffset) {
29
+ if (cellNode.type.name !== 'tableCell' && cellNode.type.name !== 'tableHeader') {
30
+ return;
31
+ }
32
+ var cellRect = tableMap.findCell(rowStart + 1 + cellOffset);
33
+ cellEdgeAttrs.push({
34
+ reachesBottom: cellRect.bottom >= tableMap.height,
35
+ reachesLeft: cellRect.left === 0,
36
+ reachesRight: cellRect.right >= tableMap.width,
37
+ reachesTop: cellRect.top === 0
38
+ });
39
+ });
40
+ return cellEdgeAttrs;
41
+ };
42
+ var applyCellEdgeAttrs = function applyCellEdgeAttrs(cell, edgeAttrs) {
43
+ if (!edgeAttrs) {
44
+ return;
45
+ }
46
+ if (edgeAttrs.reachesTop) {
47
+ cell.setAttribute('data-reaches-top', 'true');
48
+ }
49
+ if (edgeAttrs.reachesBottom) {
50
+ cell.setAttribute('data-reaches-bottom', 'true');
51
+ }
52
+ if (edgeAttrs.reachesLeft) {
53
+ cell.setAttribute('data-reaches-left', 'true');
54
+ }
55
+ if (edgeAttrs.reachesRight) {
56
+ cell.setAttribute('data-reaches-right', 'true');
57
+ }
58
+ };
17
59
  /**
18
60
  * Extracts information about deleted table rows from a change
19
61
  */
20
- var extractChangedRows = function extractChangedRows(_ref) {
21
- var change = _ref.change,
22
- originalDoc = _ref.originalDoc,
23
- newDoc = _ref.newDoc,
24
- diffType = _ref.diffType;
62
+ var extractChangedRows = function extractChangedRows(_ref2) {
63
+ var change = _ref2.change,
64
+ originalDoc = _ref2.originalDoc,
65
+ newDoc = _ref2.newDoc,
66
+ diffType = _ref2.diffType;
25
67
  var changedRows = [];
26
68
 
27
69
  // Find the table in the original document
@@ -62,12 +104,18 @@ var extractChangedRows = function extractChangedRows(_ref) {
62
104
  // Check if this row overlaps with the deletion range
63
105
  var rowOverlapsChange = rowStart >= changeStartInTable && rowStart < changeEndInTable || rowEnd > changeStartInTable && rowEnd <= changeEndInTable || rowStart < changeStartInTable && rowEnd > changeEndInTable;
64
106
  if (rowOverlapsChange && rowNode.type.name === 'tableRow' && (isExtendedEnabled(diffType) || !isEmptyRow(rowNode))) {
107
+ var cellEdgeAttrs = isExtendedEnabled(diffType) && expValEquals('platform_editor_table_diff_rounded_corners', 'isEnabled', true) ? getChangedRowCellEdgeAttrs({
108
+ rowNode: rowNode,
109
+ rowStart: rowStart,
110
+ tableMap: oldTableMap
111
+ }) : undefined;
65
112
  var startOfRow = newTableMap.mapByRow.slice().reverse().find(function (row) {
66
113
  return row[0] + tableNew.pos < change.fromB && change.fromB < row[row.length - 1] + tableNew.pos;
67
114
  });
68
115
  changedRows.push({
69
116
  rowIndex: rowIndex,
70
117
  rowNode: rowNode,
118
+ cellEdgeAttrs: cellEdgeAttrs,
71
119
  fromA: tableOld.pos + 1 + rowStart,
72
120
  toA: tableOld.pos + 1 + rowEnd,
73
121
  fromB: startOfRow ? startOfRow[0] + tableNew.start : change.fromB
@@ -116,7 +164,7 @@ var isEmptyRow = function isEmptyRow(rowNode) {
116
164
  /**
117
165
  * Creates a DOM representation of a deleted table row
118
166
  */
119
- var createChangedRowDOM = function createChangedRowDOM(rowNode, nodeViewSerializer, colorScheme, isInserted, diffType) {
167
+ var createChangedRowDOM = function createChangedRowDOM(rowNode, cellEdgeAttrs, nodeViewSerializer, colorScheme, isInserted, diffType) {
120
168
  var tr = document.createElement('tr');
121
169
  if (isExtendedEnabled(diffType)) {
122
170
  if (!isInserted) {
@@ -128,15 +176,23 @@ var createChangedRowDOM = function createChangedRowDOM(rowNode, nodeViewSerializ
128
176
  tr.setAttribute('data-testid', 'show-diff-deleted-row');
129
177
 
130
178
  // Serialize each cell in the row
179
+ var cellIndex = 0;
131
180
  rowNode.content.forEach(function (cellNode) {
132
181
  if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
133
182
  var nodeView = nodeViewSerializer.tryCreateNodeView(cellNode);
134
183
  if (nodeView) {
135
- if (nodeView instanceof HTMLElement && isExtendedEnabled(diffType)) {
136
- var overlay = document.createElement('span');
137
- var overlayStyle = colorScheme === 'traditional' ? isInserted ? traditionalAddedCellOverlayStyle : deletedTraditionalCellOverlayStyle : isInserted ? addedCellOverlayStyle : deletedCellOverlayStyle;
138
- overlay.setAttribute('style', overlayStyle);
139
- nodeView.appendChild(overlay);
184
+ if (nodeView instanceof HTMLElement) {
185
+ applyCellEdgeAttrs(nodeView, cellEdgeAttrs === null || cellEdgeAttrs === void 0 ? void 0 : cellEdgeAttrs[cellIndex]);
186
+ if (isExtendedEnabled(diffType)) {
187
+ var overlay = document.createElement('span');
188
+ var isRoundedTable = expValEquals('platform_editor_table_diff_rounded_corners', 'isEnabled', true);
189
+ var isTraditional = colorScheme === 'traditional';
190
+ var deletedCellStyle = isTraditional ? deletedTraditionalCellOverlayStyle : deletedCellOverlayStyle;
191
+ var addedCellStyle = isTraditional ? isRoundedTable ? traditionalAddedCellOverlayRoundedStyle : traditionalAddedCellOverlayStyle : isRoundedTable ? addedCellOverlayRoundedStyle : addedCellOverlayStyle;
192
+ var overlayStyle = isInserted ? addedCellStyle : deletedCellStyle;
193
+ overlay.setAttribute('style', overlayStyle);
194
+ nodeView.appendChild(overlay);
195
+ }
140
196
  }
141
197
  tr.appendChild(nodeView);
142
198
  } else {
@@ -146,6 +202,7 @@ var createChangedRowDOM = function createChangedRowDOM(rowNode, nodeViewSerializ
146
202
  tr.appendChild(serializedContent);
147
203
  }
148
204
  }
205
+ cellIndex++;
149
206
  }
150
207
  });
151
208
  return tr;
@@ -154,11 +211,11 @@ var createChangedRowDOM = function createChangedRowDOM(rowNode, nodeViewSerializ
154
211
  /**
155
212
  * Expands a diff to include whole changed rows when table rows are affected
156
213
  */
157
- var expandDiffForChangedRows = function expandDiffForChangedRows(_ref2) {
158
- var changes = _ref2.changes,
159
- originalDoc = _ref2.originalDoc,
160
- newDoc = _ref2.newDoc,
161
- diffType = _ref2.diffType;
214
+ var expandDiffForChangedRows = function expandDiffForChangedRows(_ref3) {
215
+ var changes = _ref3.changes,
216
+ originalDoc = _ref3.originalDoc,
217
+ newDoc = _ref3.newDoc,
218
+ diffType = _ref3.diffType;
162
219
  var rowInfo = [];
163
220
  var _iterator = _createForOfIteratorHelper(changes),
164
221
  _step;
@@ -187,15 +244,15 @@ var expandDiffForChangedRows = function expandDiffForChangedRows(_ref2) {
187
244
  /**
188
245
  * Main function to handle deleted rows - computes diff and creates decorations
189
246
  */
190
- export var createChangedRowDecorationWidgets = function createChangedRowDecorationWidgets(_ref3) {
191
- var changes = _ref3.changes,
192
- originalDoc = _ref3.originalDoc,
193
- newDoc = _ref3.newDoc,
194
- nodeViewSerializer = _ref3.nodeViewSerializer,
195
- colorScheme = _ref3.colorScheme,
196
- _ref3$isInserted = _ref3.isInserted,
197
- isInserted = _ref3$isInserted === void 0 ? false : _ref3$isInserted,
198
- diffType = _ref3.diffType;
247
+ export var createChangedRowDecorationWidgets = function createChangedRowDecorationWidgets(_ref4) {
248
+ var changes = _ref4.changes,
249
+ originalDoc = _ref4.originalDoc,
250
+ newDoc = _ref4.newDoc,
251
+ nodeViewSerializer = _ref4.nodeViewSerializer,
252
+ colorScheme = _ref4.colorScheme,
253
+ _ref4$isInserted = _ref4.isInserted,
254
+ isInserted = _ref4$isInserted === void 0 ? false : _ref4$isInserted,
255
+ diffType = _ref4.diffType;
199
256
  // First, expand the changes to include complete deleted rows
200
257
  var changedRows = expandDiffForChangedRows({
201
258
  changes: changes.filter(function (change) {
@@ -206,7 +263,7 @@ export var createChangedRowDecorationWidgets = function createChangedRowDecorati
206
263
  diffType: diffType
207
264
  });
208
265
  return changedRows.map(function (changedRow) {
209
- var rowDOM = createChangedRowDOM(changedRow.rowNode, nodeViewSerializer, colorScheme, isInserted, diffType);
266
+ var rowDOM = createChangedRowDOM(changedRow.rowNode, changedRow.cellEdgeAttrs, nodeViewSerializer, colorScheme, isInserted, diffType);
210
267
 
211
268
  // Find safe insertion position for the deleted row
212
269
  var safeInsertPos = findSafeInsertPos(newDoc, changedRow.fromB - 1,
@@ -1,4 +1,7 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ 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; }
4
+ 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) { _defineProperty(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; }
2
5
  import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
3
6
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
4
7
  import { isExtendedEnabled } from '../isExtendedEnabled';
@@ -9,6 +12,16 @@ import { buildDiffDecorationSpec } from './decorationKeys';
9
12
  var displayNoneStyle = convertToInlineCss({
10
13
  display: 'none'
11
14
  });
15
+ var getAtomicInlineNodeClassName = function getAtomicInlineNodeClassName(inlineNodeName, colorScheme) {
16
+ var classNames = ['show-diff-atomic-inline-changed'];
17
+ if (inlineNodeName) {
18
+ classNames.push("show-diff-atomic-inline-changed-".concat(inlineNodeName));
19
+ }
20
+ if (colorScheme === 'traditional') {
21
+ classNames.push('show-diff-atomic-inline-changed-traditional');
22
+ }
23
+ return classNames.join(' ');
24
+ };
12
25
  /**
13
26
  * Inline decoration used for insertions as the content already exists in the document
14
27
  *
@@ -23,6 +36,8 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
23
36
  isActive = _ref$isActive === void 0 ? false : _ref$isActive,
24
37
  _ref$isInserted = _ref.isInserted,
25
38
  isInserted = _ref$isInserted === void 0 ? true : _ref$isInserted,
39
+ _ref$isAtomicInlineNo = _ref.isAtomicInlineNode,
40
+ isAtomicInlineNode = _ref$isAtomicInlineNo === void 0 ? false : _ref$isAtomicInlineNo,
26
41
  _ref$shouldHideDelete = _ref.shouldHideDeleted,
27
42
  shouldHideDeleted = _ref$shouldHideDelete === void 0 ? false : _ref$shouldHideDelete,
28
43
  _ref$showIndicators = _ref.showIndicators,
@@ -30,7 +45,8 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
30
45
  doc = _ref.doc,
31
46
  diffType = _ref.diffType,
32
47
  _ref$hideAddedDiffsUn = _ref.hideAddedDiffsUnderline,
33
- hideAddedDiffsUnderline = _ref$hideAddedDiffsUn === void 0 ? false : _ref$hideAddedDiffsUn;
48
+ hideAddedDiffsUnderline = _ref$hideAddedDiffsUn === void 0 ? false : _ref$hideAddedDiffsUn,
49
+ inlineNodeName = _ref.inlineNodeName;
34
50
  var diffId = crypto.randomUUID();
35
51
  if (shouldHideDeleted) {
36
52
  return [Decoration.inline(change.fromB, change.toB, {
@@ -69,10 +85,13 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
69
85
  style = isActive ? editingStyleActive : editingStyle;
70
86
  }
71
87
  }
72
- var decorations = [Decoration.inline(change.fromB, change.toB, {
73
- style: style,
88
+ var decorations = [Decoration.inline(change.fromB, change.toB, _objectSpread(_objectSpread({
89
+ style: style
90
+ }, isAtomicInlineNode && isInserted && {
91
+ class: getAtomicInlineNodeClassName(inlineNodeName, colorScheme)
92
+ }), {}, {
74
93
  'data-testid': 'show-diff-changed-decoration'
75
- }, buildDiffDecorationSpec({
94
+ }), buildDiffDecorationSpec({
76
95
  decorationType: 'inline',
77
96
  diffId: diffId,
78
97
  isActive: isActive