@atlaskit/editor-plugin-show-diff 11.0.4 → 11.0.5

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
+ ## 11.0.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [`67209770a447f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/67209770a447f) -
8
+ [ED-8371] Fix `scrollToDiff` not always scrolling to the first diff decoration. When
9
+ `scrollIntoView` was requested on an extended diff (e.g. AI Suggested Edits),
10
+ `scrollToFirstDecoration` selected its target using `DecorationSet.find()`, which returns
11
+ decorations in the set's internal order rather than document position and applies no scrollability
12
+ filtering. As a result it could scroll to a later decoration (for example a `removeNode` deletion)
13
+ instead of the topmost one. It now scrolls to the first entry of `getScrollableDecorations` — the
14
+ same filtered, position-sorted list used by the active-index navigation path — so "first" reliably
15
+ means the topmost scrollable diff in the document.
16
+ - Updated dependencies
17
+
3
18
  ## 11.0.4
4
19
 
5
20
  ### Patch Changes
@@ -179,11 +179,13 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
179
179
  }
180
180
  var pluginState = showDiffPluginKey.getState(view.state);
181
181
 
182
- // Scroll to the first decoration when scrollIntoView was requested
182
+ // Scroll to the first decoration when scrollIntoView was requested.
183
+ // Use the same filtered/position-sorted list as the active-index path
184
+ // so "first" reliably means the topmost scrollable diff in the document.
183
185
  if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && (0, _isExtendedEnabled.isExtendedEnabled)(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
184
186
  var _cancelPendingScrollT;
185
187
  (_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 || _cancelPendingScrollT();
186
- cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToFirstDecoration)(view, pluginState.decorations);
188
+ cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToFirstDecoration)(view, (0, _getScrollableDecorations.getScrollableDecorations)(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
187
189
 
188
190
  // Reset the flag so we don't scroll again on subsequent updates
189
191
  view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
@@ -44,38 +44,17 @@ function scrollToSelection(node) {
44
44
  * Unlike `scrollToActiveDecoration`, this does not require an active index —
45
45
  * it simply scrolls to bring the first decoration into view.
46
46
  *
47
+ * The caller must pass the list of scrollable decorations as produced by
48
+ * `getScrollableDecorations`, which is filtered (non-scrollable decorations
49
+ * removed) and sorted by document position. This guarantees "first" means the
50
+ * topmost scrollable diff in the document rather than an arbitrary decoration
51
+ * from the raw `DecorationSet` ordering. This is delegated to
52
+ * `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
53
+ *
47
54
  * @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
48
55
  */
49
- var scrollToFirstDecoration = exports.scrollToFirstDecoration = function scrollToFirstDecoration(view, set) {
50
- var decoration = set.find(undefined, undefined, _decorationKeys.isDiffDecorationSpec).find(_decorationKeys.isDiffDecoration);
51
- if (!decoration) {
52
- return function () {};
53
- }
54
- var rafId = requestAnimationFrame(function () {
55
- var _decoration$type;
56
- rafId = null;
57
- // @ts-expect-error - decoration.type is not typed public API
58
- if (decoration.spec.decorationType === 'widget' && decoration !== null && decoration !== void 0 && (_decoration$type = decoration.type) !== null && _decoration$type !== void 0 && _decoration$type.toDOM) {
59
- // @ts-expect-error - decoration.type is not typed public API
60
- var widgetDom = decoration.type.toDOM;
61
- // Always scroll to the top of this decoration even if it's in view already
62
- scrollToSelection(widgetDom);
63
- } else {
64
- var _view$domAtPos;
65
- var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
66
- var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
67
- if (node instanceof HTMLElement) {
68
- // Always scroll to the top of this decoration even if it's in view already
69
- scrollToSelection(node);
70
- }
71
- }
72
- });
73
- return function () {
74
- if (rafId !== null) {
75
- cancelAnimationFrame(rafId);
76
- rafId = null;
77
- }
78
- };
56
+ var scrollToFirstDecoration = exports.scrollToFirstDecoration = function scrollToFirstDecoration(view, scrollableDecorations) {
57
+ return scrollToActiveDecoration(view, scrollableDecorations, 0);
79
58
  };
80
59
 
81
60
  /**
@@ -91,14 +70,14 @@ var scrollToActiveDecoration = exports.scrollToActiveDecoration = function scrol
91
70
  var rafId = requestAnimationFrame(function () {
92
71
  rafId = null;
93
72
  if ((0, _decorationKeys.isDiffDecoration)(decoration) && decoration.spec.decorationType === 'widget') {
94
- var _decoration$type2;
73
+ var _decoration$type;
95
74
  // @ts-expect-error - decoration.type is not typed public API
96
- var widgetDom = decoration === null || decoration === void 0 || (_decoration$type2 = decoration.type) === null || _decoration$type2 === void 0 ? void 0 : _decoration$type2.toDOM;
75
+ var widgetDom = decoration === null || decoration === void 0 || (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
97
76
  scrollToSelection(widgetDom);
98
77
  } else {
99
- var _view$domAtPos2;
78
+ var _view$domAtPos;
100
79
  var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
101
- var node = targetNode instanceof Element ? targetNode : (_view$domAtPos2 = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos2 === void 0 ? void 0 : _view$domAtPos2.node;
80
+ var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
102
81
  scrollToSelection(node);
103
82
  }
104
83
  });
@@ -186,11 +186,13 @@ export const createPlugin = (config, getIntl, api) => {
186
186
  }
187
187
  const pluginState = showDiffPluginKey.getState(view.state);
188
188
 
189
- // Scroll to the first decoration when scrollIntoView was requested
189
+ // Scroll to the first decoration when scrollIntoView was requested.
190
+ // Use the same filtered/position-sorted list as the active-index path
191
+ // so "first" reliably means the topmost scrollable diff in the document.
190
192
  if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && isExtendedEnabled(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
191
193
  var _cancelPendingScrollT;
192
194
  (_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 ? void 0 : _cancelPendingScrollT();
193
- cancelPendingScrollToDecoration = scrollToFirstDecoration(view, pluginState.decorations);
195
+ cancelPendingScrollToDecoration = scrollToFirstDecoration(view, getScrollableDecorations(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
194
196
 
195
197
  // Reset the flag so we don't scroll again on subsequent updates
196
198
  view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
@@ -1,4 +1,4 @@
1
- import { isDiffDecoration, isDiffDecorationSpec } from './decorations/decorationKeys';
1
+ import { isDiffDecoration } from './decorations/decorationKeys';
2
2
 
3
3
  /**
4
4
  * Extra space above the scrolled-to element so it does not sit flush under the
@@ -39,39 +39,16 @@ function scrollToSelection(node) {
39
39
  * Unlike `scrollToActiveDecoration`, this does not require an active index —
40
40
  * it simply scrolls to bring the first decoration into view.
41
41
  *
42
+ * The caller must pass the list of scrollable decorations as produced by
43
+ * `getScrollableDecorations`, which is filtered (non-scrollable decorations
44
+ * removed) and sorted by document position. This guarantees "first" means the
45
+ * topmost scrollable diff in the document rather than an arbitrary decoration
46
+ * from the raw `DecorationSet` ordering. This is delegated to
47
+ * `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
48
+ *
42
49
  * @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
43
50
  */
44
- export const scrollToFirstDecoration = (view, set) => {
45
- const decoration = set.find(undefined, undefined, isDiffDecorationSpec).find(isDiffDecoration);
46
- if (!decoration) {
47
- return () => {};
48
- }
49
- let rafId = requestAnimationFrame(() => {
50
- var _decoration$type;
51
- rafId = null;
52
- // @ts-expect-error - decoration.type is not typed public API
53
- if (decoration.spec.decorationType === 'widget' && decoration !== null && decoration !== void 0 && (_decoration$type = decoration.type) !== null && _decoration$type !== void 0 && _decoration$type.toDOM) {
54
- // @ts-expect-error - decoration.type is not typed public API
55
- const widgetDom = decoration.type.toDOM;
56
- // Always scroll to the top of this decoration even if it's in view already
57
- scrollToSelection(widgetDom);
58
- } else {
59
- var _view$domAtPos;
60
- const targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
61
- const node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
62
- if (node instanceof HTMLElement) {
63
- // Always scroll to the top of this decoration even if it's in view already
64
- scrollToSelection(node);
65
- }
66
- }
67
- });
68
- return () => {
69
- if (rafId !== null) {
70
- cancelAnimationFrame(rafId);
71
- rafId = null;
72
- }
73
- };
74
- };
51
+ export const scrollToFirstDecoration = (view, scrollableDecorations) => scrollToActiveDecoration(view, scrollableDecorations, 0);
75
52
 
76
53
  /**
77
54
  * Schedules scrolling to the decoration at the given index after the next frame.
@@ -86,14 +63,14 @@ export const scrollToActiveDecoration = (view, decorations, activeIndex) => {
86
63
  let rafId = requestAnimationFrame(() => {
87
64
  rafId = null;
88
65
  if (isDiffDecoration(decoration) && decoration.spec.decorationType === 'widget') {
89
- var _decoration$type2;
66
+ var _decoration$type;
90
67
  // @ts-expect-error - decoration.type is not typed public API
91
- const widgetDom = decoration === null || decoration === void 0 ? void 0 : (_decoration$type2 = decoration.type) === null || _decoration$type2 === void 0 ? void 0 : _decoration$type2.toDOM;
68
+ const widgetDom = decoration === null || decoration === void 0 ? void 0 : (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
92
69
  scrollToSelection(widgetDom);
93
70
  } else {
94
- var _view$domAtPos2;
71
+ var _view$domAtPos;
95
72
  const targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
96
- const node = targetNode instanceof Element ? targetNode : (_view$domAtPos2 = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos2 === void 0 ? void 0 : _view$domAtPos2.node;
73
+ const node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
97
74
  scrollToSelection(node);
98
75
  }
99
76
  });
@@ -172,11 +172,13 @@ export var createPlugin = function createPlugin(config, getIntl, api) {
172
172
  }
173
173
  var pluginState = showDiffPluginKey.getState(view.state);
174
174
 
175
- // Scroll to the first decoration when scrollIntoView was requested
175
+ // Scroll to the first decoration when scrollIntoView was requested.
176
+ // Use the same filtered/position-sorted list as the active-index path
177
+ // so "first" reliably means the topmost scrollable diff in the document.
176
178
  if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && isExtendedEnabled(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
177
179
  var _cancelPendingScrollT;
178
180
  (_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 || _cancelPendingScrollT();
179
- cancelPendingScrollToDecoration = scrollToFirstDecoration(view, pluginState.decorations);
181
+ cancelPendingScrollToDecoration = scrollToFirstDecoration(view, getScrollableDecorations(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
180
182
 
181
183
  // Reset the flag so we don't scroll again on subsequent updates
182
184
  view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
@@ -1,4 +1,4 @@
1
- import { isDiffDecoration, isDiffDecorationSpec } from './decorations/decorationKeys';
1
+ import { isDiffDecoration } from './decorations/decorationKeys';
2
2
 
3
3
  /**
4
4
  * Extra space above the scrolled-to element so it does not sit flush under the
@@ -39,38 +39,17 @@ function scrollToSelection(node) {
39
39
  * Unlike `scrollToActiveDecoration`, this does not require an active index —
40
40
  * it simply scrolls to bring the first decoration into view.
41
41
  *
42
+ * The caller must pass the list of scrollable decorations as produced by
43
+ * `getScrollableDecorations`, which is filtered (non-scrollable decorations
44
+ * removed) and sorted by document position. This guarantees "first" means the
45
+ * topmost scrollable diff in the document rather than an arbitrary decoration
46
+ * from the raw `DecorationSet` ordering. This is delegated to
47
+ * `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
48
+ *
42
49
  * @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
43
50
  */
44
- export var scrollToFirstDecoration = function scrollToFirstDecoration(view, set) {
45
- var decoration = set.find(undefined, undefined, isDiffDecorationSpec).find(isDiffDecoration);
46
- if (!decoration) {
47
- return function () {};
48
- }
49
- var rafId = requestAnimationFrame(function () {
50
- var _decoration$type;
51
- rafId = null;
52
- // @ts-expect-error - decoration.type is not typed public API
53
- if (decoration.spec.decorationType === 'widget' && decoration !== null && decoration !== void 0 && (_decoration$type = decoration.type) !== null && _decoration$type !== void 0 && _decoration$type.toDOM) {
54
- // @ts-expect-error - decoration.type is not typed public API
55
- var widgetDom = decoration.type.toDOM;
56
- // Always scroll to the top of this decoration even if it's in view already
57
- scrollToSelection(widgetDom);
58
- } else {
59
- var _view$domAtPos;
60
- var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
61
- var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
62
- if (node instanceof HTMLElement) {
63
- // Always scroll to the top of this decoration even if it's in view already
64
- scrollToSelection(node);
65
- }
66
- }
67
- });
68
- return function () {
69
- if (rafId !== null) {
70
- cancelAnimationFrame(rafId);
71
- rafId = null;
72
- }
73
- };
51
+ export var scrollToFirstDecoration = function scrollToFirstDecoration(view, scrollableDecorations) {
52
+ return scrollToActiveDecoration(view, scrollableDecorations, 0);
74
53
  };
75
54
 
76
55
  /**
@@ -86,14 +65,14 @@ export var scrollToActiveDecoration = function scrollToActiveDecoration(view, de
86
65
  var rafId = requestAnimationFrame(function () {
87
66
  rafId = null;
88
67
  if (isDiffDecoration(decoration) && decoration.spec.decorationType === 'widget') {
89
- var _decoration$type2;
68
+ var _decoration$type;
90
69
  // @ts-expect-error - decoration.type is not typed public API
91
- var widgetDom = decoration === null || decoration === void 0 || (_decoration$type2 = decoration.type) === null || _decoration$type2 === void 0 ? void 0 : _decoration$type2.toDOM;
70
+ var widgetDom = decoration === null || decoration === void 0 || (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
92
71
  scrollToSelection(widgetDom);
93
72
  } else {
94
- var _view$domAtPos2;
73
+ var _view$domAtPos;
95
74
  var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
96
- var node = targetNode instanceof Element ? targetNode : (_view$domAtPos2 = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos2 === void 0 ? void 0 : _view$domAtPos2.node;
75
+ var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
97
76
  scrollToSelection(node);
98
77
  }
99
78
  });
@@ -1,12 +1,19 @@
1
- import type { EditorView, Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
1
+ import type { EditorView, Decoration } from '@atlaskit/editor-prosemirror/view';
2
2
  /**
3
3
  * Schedules scrolling to the first diff decoration after the next frame.
4
4
  * Unlike `scrollToActiveDecoration`, this does not require an active index —
5
5
  * it simply scrolls to bring the first decoration into view.
6
6
  *
7
+ * The caller must pass the list of scrollable decorations as produced by
8
+ * `getScrollableDecorations`, which is filtered (non-scrollable decorations
9
+ * removed) and sorted by document position. This guarantees "first" means the
10
+ * topmost scrollable diff in the document rather than an arbitrary decoration
11
+ * from the raw `DecorationSet` ordering. This is delegated to
12
+ * `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
13
+ *
7
14
  * @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
8
15
  */
9
- export declare const scrollToFirstDecoration: (view: EditorView, set: DecorationSet) => (() => void);
16
+ export declare const scrollToFirstDecoration: (view: EditorView, scrollableDecorations: Decoration[]) => (() => void);
10
17
  /**
11
18
  * Schedules scrolling to the decoration at the given index after the next frame.
12
19
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "11.0.4",
3
+ "version": "11.0.5",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -54,7 +54,7 @@
54
54
  "react-intl": "^7.0.0"
55
55
  },
56
56
  "peerDependencies": {
57
- "@atlaskit/editor-common": "^117.4.0",
57
+ "@atlaskit/editor-common": "^117.5.0",
58
58
  "react": "^18.2.0 || ^19.2.0"
59
59
  },
60
60
  "techstack": {