@atlaskit/editor-plugin-show-diff 12.1.4 → 13.0.1

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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 13.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`73a676426d5ed`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/73a676426d5ed) -
8
+ [ux] Preserve spacing between a block diff inserted at the top of a document and the node that
9
+ follows it.
10
+ - Updated dependencies
11
+
12
+ ## 13.0.0
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies
17
+
3
18
  ## 12.1.4
4
19
 
5
20
  ### Patch Changes
@@ -15,6 +15,10 @@ var _findSafeInsertPos = require("./utils/findSafeInsertPos");
15
15
  var _wrapBlockNodeView = require("./utils/wrapBlockNodeView");
16
16
  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; }
17
17
  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; }
18
+ var isHeadingLevel = function isHeadingLevel(level) {
19
+ return typeof level === 'number' && level >= 1 && level <= 6;
20
+ };
21
+
18
22
  /**
19
23
  * This function is used to create a decoration widget to show content
20
24
  * that is not in the current document.
@@ -250,32 +254,34 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
250
254
  }
251
255
  })));
252
256
 
253
- // When a single block node is purely deleted at the very start of the doc (first child),
254
- // the deleted widget decoration overlaps with the existing first child's decoration.
255
- // To fix this, we insert an additional empty widget span before the deleted content
256
- // to push it down and create visual separation.
257
- //
258
- // Conditions for the placeholder:
259
- // 1. isPureDeletion: change.fromB === change.toB — nothing was inserted at the same position.
260
- // Node type changes (e.g. paragraph → heading) have both a deletion and an insertion,
261
- // so fromB !== toB — we skip the placeholder in that case.
262
- // 2. isSingleBlock: exactly one block node was deleted (not inline, not multi-block).
263
- // 3. safeInsertPos: that it is the first node
257
+ // The first document node keeps its margin-top reset even when an inverted diff renders a visible
258
+ // block before it. Add an empty widget matching the following heading so the editor's own heading
259
+ // styles supply the appropriate spacing. For other node types, retain the existing generic spacer.
264
260
  var isPureDeletion = change.fromB === change.toB;
265
261
  var isSingleBlock = slice.content.childCount === 1 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.isBlock);
266
- var resolvedPos = newDoc.resolve(change.fromB);
267
- var isFirstDocChild = resolvedPos.depth === 0 && resolvedPos.index(0) === 0;
268
- if (isFirstDocChild && isSingleBlock && isPureDeletion && (0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
269
- var emptyWidgetSpan = document.createElement('span');
270
- emptyWidgetSpan.style.display = 'block';
271
- // Use a design token for the margin to keep spacing consistent with the design system
272
- emptyWidgetSpan.style.marginTop = "var(--ds-space-100, 8px)";
273
- var widget = _view.Decoration.widget(safeInsertPos, emptyWidgetSpan, _objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)({
274
- decorationType: 'widget',
275
- diffId: crypto.randomUUID(),
276
- diffType: diffType
277
- })));
278
- decorations.push(widget);
262
+ var isDiffWidgetAtStartOfDoc = $safeInsertPos.depth === 0 && $safeInsertPos.index(0) === 0;
263
+ if (isDiffWidgetAtStartOfDoc && isSingleBlock && isPureDeletion && (0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
264
+ var followingNode = $safeInsertPos.nodeAfter;
265
+ var headingLevel = (followingNode === null || followingNode === void 0 ? void 0 : followingNode.type.name) === 'heading' ? followingNode.attrs.level : undefined;
266
+ if (isHeadingLevel(headingLevel)) {
267
+ var nodeLikeSpacer = dom.ownerDocument.createElement("h".concat(headingLevel));
268
+ nodeLikeSpacer.dataset.testid = 'show-diff-node-like-margin-spacer';
269
+ nodeLikeSpacer.setAttribute('aria-hidden', 'true');
270
+ nodeLikeSpacer.contentEditable = 'false';
271
+ decorations.push(_view.Decoration.widget(safeInsertPos, nodeLikeSpacer, {
272
+ side: 0
273
+ }));
274
+ } else if (followingNode) {
275
+ var defaultSpacer = dom.ownerDocument.createElement('span');
276
+ defaultSpacer.dataset.testid = 'show-diff-default-margin-spacer';
277
+ defaultSpacer.style.display = 'block';
278
+ defaultSpacer.style.marginTop = "var(--ds-space-100, 8px)";
279
+ decorations.push(_view.Decoration.widget(safeInsertPos, defaultSpacer, _objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)({
280
+ decorationType: 'widget',
281
+ diffId: crypto.randomUUID(),
282
+ diffType: diffType
283
+ }))));
284
+ }
279
285
  }
280
286
  return decorations;
281
287
  };
@@ -5,6 +5,7 @@ import { createChangedRowDecorationWidgets } from './createChangedRowDecorationW
5
5
  import { buildDiffDecorationSpec, buildAnchorDecorationKey } from './decorationKeys';
6
6
  import { findSafeInsertPos } from './utils/findSafeInsertPos';
7
7
  import { wrapBlockNodeView, injectInnerWrapper, createContentWrapper } from './utils/wrapBlockNodeView';
8
+ const isHeadingLevel = level => typeof level === 'number' && level >= 1 && level <= 6;
8
9
 
9
10
  /**
10
11
  * This function is used to create a decoration widget to show content
@@ -232,34 +233,36 @@ export const createNodeChangedDecorationWidget = ({
232
233
  }
233
234
  }));
234
235
 
235
- // When a single block node is purely deleted at the very start of the doc (first child),
236
- // the deleted widget decoration overlaps with the existing first child's decoration.
237
- // To fix this, we insert an additional empty widget span before the deleted content
238
- // to push it down and create visual separation.
239
- //
240
- // Conditions for the placeholder:
241
- // 1. isPureDeletion: change.fromB === change.toB — nothing was inserted at the same position.
242
- // Node type changes (e.g. paragraph → heading) have both a deletion and an insertion,
243
- // so fromB !== toB — we skip the placeholder in that case.
244
- // 2. isSingleBlock: exactly one block node was deleted (not inline, not multi-block).
245
- // 3. safeInsertPos: that it is the first node
236
+ // The first document node keeps its margin-top reset even when an inverted diff renders a visible
237
+ // block before it. Add an empty widget matching the following heading so the editor's own heading
238
+ // styles supply the appropriate spacing. For other node types, retain the existing generic spacer.
246
239
  const isPureDeletion = change.fromB === change.toB;
247
240
  const isSingleBlock = slice.content.childCount === 1 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.isBlock);
248
- const resolvedPos = newDoc.resolve(change.fromB);
249
- const isFirstDocChild = resolvedPos.depth === 0 && resolvedPos.index(0) === 0;
250
- if (isFirstDocChild && isSingleBlock && isPureDeletion && isExtendedEnabled(diffType)) {
251
- const emptyWidgetSpan = document.createElement('span');
252
- emptyWidgetSpan.style.display = 'block';
253
- // Use a design token for the margin to keep spacing consistent with the design system
254
- emptyWidgetSpan.style.marginTop = "var(--ds-space-100, 8px)";
255
- const widget = Decoration.widget(safeInsertPos, emptyWidgetSpan, {
256
- ...buildDiffDecorationSpec({
257
- decorationType: 'widget',
258
- diffId: crypto.randomUUID(),
259
- diffType
260
- })
261
- });
262
- decorations.push(widget);
241
+ const isDiffWidgetAtStartOfDoc = $safeInsertPos.depth === 0 && $safeInsertPos.index(0) === 0;
242
+ if (isDiffWidgetAtStartOfDoc && isSingleBlock && isPureDeletion && isExtendedEnabled(diffType)) {
243
+ const followingNode = $safeInsertPos.nodeAfter;
244
+ const headingLevel = (followingNode === null || followingNode === void 0 ? void 0 : followingNode.type.name) === 'heading' ? followingNode.attrs.level : undefined;
245
+ if (isHeadingLevel(headingLevel)) {
246
+ const nodeLikeSpacer = dom.ownerDocument.createElement(`h${headingLevel}`);
247
+ nodeLikeSpacer.dataset.testid = 'show-diff-node-like-margin-spacer';
248
+ nodeLikeSpacer.setAttribute('aria-hidden', 'true');
249
+ nodeLikeSpacer.contentEditable = 'false';
250
+ decorations.push(Decoration.widget(safeInsertPos, nodeLikeSpacer, {
251
+ side: 0
252
+ }));
253
+ } else if (followingNode) {
254
+ const defaultSpacer = dom.ownerDocument.createElement('span');
255
+ defaultSpacer.dataset.testid = 'show-diff-default-margin-spacer';
256
+ defaultSpacer.style.display = 'block';
257
+ defaultSpacer.style.marginTop = "var(--ds-space-100, 8px)";
258
+ decorations.push(Decoration.widget(safeInsertPos, defaultSpacer, {
259
+ ...buildDiffDecorationSpec({
260
+ decorationType: 'widget',
261
+ diffId: crypto.randomUUID(),
262
+ diffType
263
+ })
264
+ }));
265
+ }
263
266
  }
264
267
  return decorations;
265
268
  };
@@ -8,6 +8,9 @@ import { createChangedRowDecorationWidgets } from './createChangedRowDecorationW
8
8
  import { buildDiffDecorationSpec, buildAnchorDecorationKey } from './decorationKeys';
9
9
  import { findSafeInsertPos } from './utils/findSafeInsertPos';
10
10
  import { wrapBlockNodeView, injectInnerWrapper, createContentWrapper } from './utils/wrapBlockNodeView';
11
+ var isHeadingLevel = function isHeadingLevel(level) {
12
+ return typeof level === 'number' && level >= 1 && level <= 6;
13
+ };
11
14
 
12
15
  /**
13
16
  * This function is used to create a decoration widget to show content
@@ -244,32 +247,34 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
244
247
  }
245
248
  })));
246
249
 
247
- // When a single block node is purely deleted at the very start of the doc (first child),
248
- // the deleted widget decoration overlaps with the existing first child's decoration.
249
- // To fix this, we insert an additional empty widget span before the deleted content
250
- // to push it down and create visual separation.
251
- //
252
- // Conditions for the placeholder:
253
- // 1. isPureDeletion: change.fromB === change.toB — nothing was inserted at the same position.
254
- // Node type changes (e.g. paragraph → heading) have both a deletion and an insertion,
255
- // so fromB !== toB — we skip the placeholder in that case.
256
- // 2. isSingleBlock: exactly one block node was deleted (not inline, not multi-block).
257
- // 3. safeInsertPos: that it is the first node
250
+ // The first document node keeps its margin-top reset even when an inverted diff renders a visible
251
+ // block before it. Add an empty widget matching the following heading so the editor's own heading
252
+ // styles supply the appropriate spacing. For other node types, retain the existing generic spacer.
258
253
  var isPureDeletion = change.fromB === change.toB;
259
254
  var isSingleBlock = slice.content.childCount === 1 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.isBlock);
260
- var resolvedPos = newDoc.resolve(change.fromB);
261
- var isFirstDocChild = resolvedPos.depth === 0 && resolvedPos.index(0) === 0;
262
- if (isFirstDocChild && isSingleBlock && isPureDeletion && isExtendedEnabled(diffType)) {
263
- var emptyWidgetSpan = document.createElement('span');
264
- emptyWidgetSpan.style.display = 'block';
265
- // Use a design token for the margin to keep spacing consistent with the design system
266
- emptyWidgetSpan.style.marginTop = "var(--ds-space-100, 8px)";
267
- var widget = Decoration.widget(safeInsertPos, emptyWidgetSpan, _objectSpread({}, buildDiffDecorationSpec({
268
- decorationType: 'widget',
269
- diffId: crypto.randomUUID(),
270
- diffType: diffType
271
- })));
272
- decorations.push(widget);
255
+ var isDiffWidgetAtStartOfDoc = $safeInsertPos.depth === 0 && $safeInsertPos.index(0) === 0;
256
+ if (isDiffWidgetAtStartOfDoc && isSingleBlock && isPureDeletion && isExtendedEnabled(diffType)) {
257
+ var followingNode = $safeInsertPos.nodeAfter;
258
+ var headingLevel = (followingNode === null || followingNode === void 0 ? void 0 : followingNode.type.name) === 'heading' ? followingNode.attrs.level : undefined;
259
+ if (isHeadingLevel(headingLevel)) {
260
+ var nodeLikeSpacer = dom.ownerDocument.createElement("h".concat(headingLevel));
261
+ nodeLikeSpacer.dataset.testid = 'show-diff-node-like-margin-spacer';
262
+ nodeLikeSpacer.setAttribute('aria-hidden', 'true');
263
+ nodeLikeSpacer.contentEditable = 'false';
264
+ decorations.push(Decoration.widget(safeInsertPos, nodeLikeSpacer, {
265
+ side: 0
266
+ }));
267
+ } else if (followingNode) {
268
+ var defaultSpacer = dom.ownerDocument.createElement('span');
269
+ defaultSpacer.dataset.testid = 'show-diff-default-margin-spacer';
270
+ defaultSpacer.style.display = 'block';
271
+ defaultSpacer.style.marginTop = "var(--ds-space-100, 8px)";
272
+ decorations.push(Decoration.widget(safeInsertPos, defaultSpacer, _objectSpread({}, buildDiffDecorationSpec({
273
+ decorationType: 'widget',
274
+ diffId: crypto.randomUUID(),
275
+ diffType: diffType
276
+ }))));
277
+ }
273
278
  }
274
279
  return decorations;
275
280
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "12.1.4",
3
+ "version": "13.0.1",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -22,8 +22,8 @@
22
22
  "dependencies": {
23
23
  "@atlaskit/adf-schema": "^56.7.0",
24
24
  "@atlaskit/custom-steps": "^1.0.0",
25
- "@atlaskit/editor-plugin-analytics": "^14.0.0",
26
- "@atlaskit/editor-plugin-user-intent": "^12.0.0",
25
+ "@atlaskit/editor-plugin-analytics": "^15.0.0",
26
+ "@atlaskit/editor-plugin-user-intent": "^13.0.0",
27
27
  "@atlaskit/editor-prosemirror": "^8.0.0",
28
28
  "@atlaskit/editor-tables": "^3.0.0",
29
29
  "@atlaskit/platform-feature-flags": "^2.1.0",
@@ -40,7 +40,7 @@
40
40
  "@atlaskit/button": "^25.0.0",
41
41
  "@atlaskit/css": "^1.0.0",
42
42
  "@atlaskit/dropdown-menu": "^18.0.0",
43
- "@atlaskit/editor-core": "^223.2.0",
43
+ "@atlaskit/editor-core": "^224.0.0",
44
44
  "@atlaskit/editor-json-transformer": "^9.2.0",
45
45
  "@atlaskit/form": "^17.0.0",
46
46
  "@atlaskit/primitives": "^22.2.0",
@@ -53,7 +53,7 @@
53
53
  "react-intl": "^7.0.0"
54
54
  },
55
55
  "peerDependencies": {
56
- "@atlaskit/editor-common": "^118.12.0",
56
+ "@atlaskit/editor-common": "^119.1.0",
57
57
  "react": "^18.2.0 || ^19.2.0"
58
58
  },
59
59
  "techstack": {
@@ -1,43 +0,0 @@
1
- /**
2
- * Testing structured MCP docs for review — ignore this file.
3
- * Contact #dst-structured-content in Slack with questions.
4
- */
5
-
6
- import path from 'path';
7
-
8
- import type { StructuredContentSource } from '@atlassian/structured-docs-types/types';
9
-
10
- import packageJson from './package.json';
11
-
12
- const packagePath = path.resolve(__dirname);
13
-
14
- const documentation: StructuredContentSource = {
15
- components: [
16
- {
17
- name: 'Editor Plugin Show Diff',
18
- description: 'ShowDiff plugin for @atlaskit/editor-core',
19
- status: 'general-availability',
20
- import: {
21
- name: 'showDiffPlugin',
22
- package: '@atlaskit/editor-plugin-show-diff',
23
- type: 'named',
24
- packagePath,
25
- packageJson,
26
- },
27
- usageGuidelines: [],
28
- contentGuidelines: [],
29
- accessibilityGuidelines: [],
30
- keywords: ['editor', 'editor-plugin-show-diff', 'atlaskit'],
31
- categories: ['editor'],
32
- examples: [
33
- {
34
- name: 'Basic',
35
- description: 'Show diff plugin in editor.',
36
- source: path.resolve(packagePath, './examples/1-basic.tsx'),
37
- },
38
- ],
39
- },
40
- ],
41
- };
42
-
43
- export default documentation;