@atlaskit/editor-plugin-show-diff 10.0.2 → 10.1.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +25 -8
  3. package/dist/cjs/pm-plugins/decorations/createAnchorDecorationWidgets.js +209 -0
  4. package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +17 -2
  5. package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +19 -3
  6. package/dist/cjs/pm-plugins/decorations/decorationKeys.js +81 -28
  7. package/dist/cjs/pm-plugins/getScrollableDecorations.js +32 -23
  8. package/dist/cjs/pm-plugins/main.js +8 -6
  9. package/dist/cjs/pm-plugins/scrollToDiff.js +5 -6
  10. package/dist/cjs/showDiffPlugin.js +12 -1
  11. package/dist/cjs/ui/IndicatorBar/IndicatorBar.compiled.css +9 -0
  12. package/dist/cjs/ui/IndicatorBar/IndicatorBar.js +33 -0
  13. package/dist/cjs/ui/IndicatorBar/IndicatorBarContentComponent.js +87 -0
  14. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +23 -8
  15. package/dist/es2019/pm-plugins/decorations/createAnchorDecorationWidgets.js +205 -0
  16. package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +14 -2
  17. package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +19 -4
  18. package/dist/es2019/pm-plugins/decorations/decorationKeys.js +64 -14
  19. package/dist/es2019/pm-plugins/getScrollableDecorations.js +31 -25
  20. package/dist/es2019/pm-plugins/main.js +8 -6
  21. package/dist/es2019/pm-plugins/scrollToDiff.js +6 -7
  22. package/dist/es2019/showDiffPlugin.js +12 -1
  23. package/dist/es2019/ui/IndicatorBar/IndicatorBar.compiled.css +9 -0
  24. package/dist/es2019/ui/IndicatorBar/IndicatorBar.js +24 -0
  25. package/dist/es2019/ui/IndicatorBar/IndicatorBarContentComponent.js +82 -0
  26. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +25 -8
  27. package/dist/esm/pm-plugins/decorations/createAnchorDecorationWidgets.js +203 -0
  28. package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +16 -2
  29. package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +20 -4
  30. package/dist/esm/pm-plugins/decorations/decorationKeys.js +78 -27
  31. package/dist/esm/pm-plugins/getScrollableDecorations.js +33 -24
  32. package/dist/esm/pm-plugins/main.js +8 -6
  33. package/dist/esm/pm-plugins/scrollToDiff.js +6 -7
  34. package/dist/esm/showDiffPlugin.js +12 -1
  35. package/dist/esm/ui/IndicatorBar/IndicatorBar.compiled.css +9 -0
  36. package/dist/esm/ui/IndicatorBar/IndicatorBar.js +25 -0
  37. package/dist/esm/ui/IndicatorBar/IndicatorBarContentComponent.js +80 -0
  38. package/dist/types/pm-plugins/calculateDiff/calculateDiffDecorations.d.ts +2 -1
  39. package/dist/types/pm-plugins/decorations/createAnchorDecorationWidgets.d.ts +38 -0
  40. package/dist/types/pm-plugins/decorations/createInlineChangedDecoration.d.ts +4 -1
  41. package/dist/types/pm-plugins/decorations/createNodeChangedDecorationWidget.d.ts +2 -1
  42. package/dist/types/pm-plugins/decorations/decorationKeys.d.ts +56 -17
  43. package/dist/types/pm-plugins/main.d.ts +1 -0
  44. package/dist/types/pm-plugins/scrollToDiff.d.ts +2 -2
  45. package/dist/types/showDiffPluginType.d.ts +8 -0
  46. package/dist/types/ui/IndicatorBar/IndicatorBar.d.ts +10 -0
  47. package/dist/types/ui/IndicatorBar/IndicatorBarContentComponent.d.ts +17 -0
  48. package/package.json +6 -5
@@ -3,6 +3,7 @@ import { Decoration } from '@atlaskit/editor-prosemirror/view';
3
3
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
4
4
  import { editingStyle, editingStyleExtended, editingStyleActive, editingStyleActiveExtended, deletedContentStyle, deletedContentStyleActive, deletedInlineContentStyleExtended } from './colorSchemes/standard';
5
5
  import { traditionalInsertStyle, traditionalInsertStyleActive, getDeletedTraditionalInlineStyle } from './colorSchemes/traditional';
6
+ import { createInlineIndicatorAnchorWidgets } from './createAnchorDecorationWidgets';
6
7
  import { buildDiffDecorationSpec } from './decorationKeys';
7
8
  const displayNoneStyle = convertToInlineCss({
8
9
  display: 'none'
@@ -19,7 +20,9 @@ export const createInlineChangedDecoration = ({
19
20
  colorScheme,
20
21
  isActive = false,
21
22
  isInserted = true,
22
- shouldHideDeleted = false
23
+ shouldHideDeleted = false,
24
+ showIndicators = false,
25
+ doc
23
26
  }) => {
24
27
  const diffId = crypto.randomUUID();
25
28
  if (shouldHideDeleted) {
@@ -59,7 +62,7 @@ export const createInlineChangedDecoration = ({
59
62
  style = isActive ? editingStyleActive : editingStyle;
60
63
  }
61
64
  }
62
- return [Decoration.inline(change.fromB, change.toB, {
65
+ const decorations = [Decoration.inline(change.fromB, change.toB, {
63
66
  style,
64
67
  'data-testid': 'show-diff-changed-decoration'
65
68
  }, buildDiffDecorationSpec({
@@ -67,4 +70,13 @@ export const createInlineChangedDecoration = ({
67
70
  diffId,
68
71
  isActive
69
72
  }))];
73
+ if (showIndicators && doc && expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
74
+ decorations.push(...createInlineIndicatorAnchorWidgets({
75
+ doc,
76
+ from: change.fromB,
77
+ to: change.toB,
78
+ diffId
79
+ }));
80
+ }
81
+ return decorations;
70
82
  };
@@ -3,8 +3,9 @@ import { Decoration } from '@atlaskit/editor-prosemirror/view';
3
3
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
4
4
  import { editingStyle, editingStyleExtended, editingStyleActive, editingStyleActiveExtended, deletedContentStyle, deletedContentStyleActive, deletedContentStyleNew, deletedContentStyleUnbounded, deletedInlineContentStyleExtended } from './colorSchemes/standard';
5
5
  import { traditionalInsertStyle, traditionalInsertStyleActive, getDeletedTraditionalInlineStyle, deletedTraditionalContentStyleUnbounded, deletedTraditionalContentStyleUnboundedActive } from './colorSchemes/traditional';
6
+ import { createLeftAnchorWidget } from './createAnchorDecorationWidgets';
6
7
  import { createChangedRowDecorationWidgets } from './createChangedRowDecorationWidgets';
7
- import { buildDiffDecorationSpec } from './decorationKeys';
8
+ import { buildDiffDecorationSpec, buildAnchorDecorationKey } from './decorationKeys';
8
9
  import { findSafeInsertPos } from './utils/findSafeInsertPos';
9
10
  import { wrapBlockNodeView } from './utils/wrapBlockNodeView';
10
11
  const getDeletedContentStyleUnbounded = (colorScheme, isActive = false) => {
@@ -98,7 +99,8 @@ export const createNodeChangedDecorationWidget = ({
98
99
  intl,
99
100
  activeIndexPos,
100
101
  // This is false by default as this is generally used to show deleted content
101
- isInserted = false
102
+ isInserted = false,
103
+ showIndicators = false
102
104
  }) => {
103
105
  var _slice$content, _slice$content2, _slice$content2$first, _slice$content3, _slice$content3$first;
104
106
  const slice = doc.slice(change.fromA, change.toA);
@@ -129,6 +131,8 @@ export const createNodeChangedDecorationWidget = ({
129
131
 
130
132
  // For non-table content, use the existing span wrapper approach
131
133
  const dom = document.createElement('span');
134
+ const diffId = crypto.randomUUID();
135
+ const decorations = [];
132
136
 
133
137
  /*
134
138
  * The thinking is we separate out the fragment we got from doc.slice
@@ -230,8 +234,19 @@ export const createNodeChangedDecorationWidget = ({
230
234
  }
231
235
  });
232
236
  dom.setAttribute('data-testid', 'show-diff-deleted-decoration');
233
- const diffId = crypto.randomUUID();
234
- const decorations = [];
237
+ if (showIndicators && expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
238
+ const leftAnchor = createLeftAnchorWidget({
239
+ doc: newDoc,
240
+ from: safeInsertPos,
241
+ diffId
242
+ });
243
+ dom.style.setProperty('anchor-name', `--${buildAnchorDecorationKey({
244
+ diffId
245
+ })}`);
246
+ if (leftAnchor) {
247
+ decorations.push(leftAnchor);
248
+ }
249
+ }
235
250
  decorations.push(Decoration.widget(safeInsertPos, dom, buildDiffDecorationSpec({
236
251
  decorationType: 'widget',
237
252
  diffId,
@@ -1,24 +1,40 @@
1
1
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
2
2
  /**
3
- * Shared prefix for every decoration key produced by the show-diff plugin.
4
- * All keys derive from this, so the full set of plugin decorations can be
5
- * identified by this single prefix.
3
+ * Decoration families produced by the show-diff plugin.
4
+ * Each family owns its own key space and spec shape.
6
5
  */
7
- const DIFF_DECORATION_FAMILY = 'diff';
6
+ export const DecorationFamily = {
7
+ diff: 'diff',
8
+ anchor: 'anchor'
9
+ };
10
+ export const AnchorTypeKey = {
11
+ from: 'from',
12
+ to: 'to',
13
+ left: 'left',
14
+ docMargin: 'doc-margin'
15
+ };
8
16
  /**
9
- * The kinds of decoration the show-diff plugin produces. Each value is the
10
- * leading segment of the generated key, so a decoration's kind can be matched
11
- * with `key?.startsWith(DiffDecorationKey.inline)` etc.
17
+ * The diff-decoration kinds produced by the show-diff plugin. Each value is
18
+ * the leading segment of the generated key, so a diff decoration's kind can be
19
+ * matched with `key?.startsWith(DiffDecorationKey.inline)` etc.
12
20
  */
13
21
  export const DiffDecorationKey = {
14
- inline: `${DIFF_DECORATION_FAMILY}-inline`,
15
- block: `${DIFF_DECORATION_FAMILY}-block`,
16
- widget: `${DIFF_DECORATION_FAMILY}-widget`
22
+ inline: `${DecorationFamily.diff}-inline`,
23
+ block: `${DecorationFamily.diff}-block`,
24
+ widget: `${DecorationFamily.diff}-widget`
25
+ };
26
+ export const AnchorDocMarginKey = `${DecorationFamily.anchor}-${AnchorTypeKey.docMargin}`;
27
+ export const buildAnchorDecorationKey = ({
28
+ diffId,
29
+ anchorType
30
+ }) => {
31
+ return `${DecorationFamily.anchor}-${diffId}${anchorType ? `-${anchorType}` : ''}`;
17
32
  };
18
33
 
19
34
  /**
20
- * Builds a decoration key in the form `{type}-{active|inactive}`
21
- * (e.g. `diff-inline-active`).
35
+ * Builds a diff decoration key. The extended experience includes the `diffId`
36
+ * in the key so independently rendered decorations for the same type remain
37
+ * distinguishable.
22
38
  */
23
39
  export const buildDiffDecorationKey = ({
24
40
  decorationKeyPrefix,
@@ -37,7 +53,7 @@ export const buildDiffDecorationSpec = ({
37
53
  nodeName,
38
54
  side
39
55
  }) => ({
40
- decorationFamily: DIFF_DECORATION_FAMILY,
56
+ decorationFamily: DecorationFamily.diff,
41
57
  decorationType,
42
58
  diffId,
43
59
  key: buildDiffDecorationKey({
@@ -52,7 +68,41 @@ export const buildDiffDecorationSpec = ({
52
68
  side
53
69
  } : {})
54
70
  });
55
- export const extractDiffDescriptors = decorations => decorations.find(undefined, undefined, spec => spec.decorationFamily === 'diff').map(({
71
+ export function buildAnchorDecorationSpec({
72
+ anchorType,
73
+ diffId,
74
+ side
75
+ }) {
76
+ if (anchorType === AnchorTypeKey.docMargin) {
77
+ return {
78
+ decorationFamily: DecorationFamily.anchor,
79
+ anchorType,
80
+ key: AnchorDocMarginKey,
81
+ ...(side !== undefined ? {
82
+ side
83
+ } : {})
84
+ };
85
+ }
86
+ if (!diffId) {
87
+ throw new Error(`diffId is required for anchor type "${anchorType}"`);
88
+ }
89
+ return {
90
+ decorationFamily: DecorationFamily.anchor,
91
+ anchorType,
92
+ diffId,
93
+ key: buildAnchorDecorationKey({
94
+ diffId,
95
+ anchorType
96
+ }),
97
+ ...(side !== undefined ? {
98
+ side
99
+ } : {})
100
+ };
101
+ }
102
+ export const isDiffDecorationSpec = spec => Boolean(spec && typeof spec === 'object' && 'decorationFamily' in spec && spec.decorationFamily === DecorationFamily.diff);
103
+ export const isAnchorDecorationSpec = spec => Boolean(spec && typeof spec === 'object' && 'decorationFamily' in spec && spec.decorationFamily === DecorationFamily.anchor);
104
+ export const isDiffDecoration = decoration => isDiffDecorationSpec(decoration.spec);
105
+ export const extractDiffDescriptors = decorations => decorations.find(undefined, undefined, isDiffDecorationSpec).filter(isDiffDecoration).map(({
56
106
  spec
57
107
  }) => {
58
108
  return {
@@ -1,4 +1,5 @@
1
- import { DiffDecorationKey } from './decorations/decorationKeys';
1
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
2
+ import { DiffDecorationKey, isDiffDecoration, isDiffDecorationSpec } from './decorations/decorationKeys';
2
3
 
3
4
  /**
4
5
  * True if `fragment` contains at least one inline node (text, hardBreak, emoji, mention, etc.).
@@ -37,6 +38,9 @@ export function isInlineDiffDecorationRenderableInDoc(doc, from, to) {
37
38
  function isRangeFullyInside(range1Start, range1End, range2Start, range2End) {
38
39
  return range2Start <= range1Start && range1End <= range2End;
39
40
  }
41
+ function specHasDiffKeyPrefix(spec, keyPrefix) {
42
+ return Boolean(spec && typeof spec === 'object' && 'key' in spec && typeof spec.key === 'string' && spec.key.startsWith(keyPrefix));
43
+ }
40
44
 
41
45
  /**
42
46
  * Gets scrollable decorations from a DecorationSet, filtering out overlapping decorations
@@ -60,41 +64,43 @@ export const getScrollableDecorations = (set, doc) => {
60
64
  if (!set) {
61
65
  return [];
62
66
  }
67
+ const isBlockDecoration = decoration => {
68
+ var _decoration$spec$key$, _decoration$spec, _decoration$spec$key;
69
+ return expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? isDiffDecoration(decoration) && decoration.spec.decorationType === 'block' : (_decoration$spec$key$ = (_decoration$spec = decoration.spec) === null || _decoration$spec === void 0 ? void 0 : (_decoration$spec$key = _decoration$spec.key) === null || _decoration$spec$key === void 0 ? void 0 : _decoration$spec$key.startsWith(DiffDecorationKey.block)) !== null && _decoration$spec$key$ !== void 0 ? _decoration$spec$key$ : false;
70
+ };
71
+ const isInlineDecoration = decoration => {
72
+ var _decoration$spec$key$2, _decoration$spec2, _decoration$spec2$key;
73
+ return expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? isDiffDecoration(decoration) && decoration.spec.decorationType === 'inline' : (_decoration$spec$key$2 = (_decoration$spec2 = decoration.spec) === null || _decoration$spec2 === void 0 ? void 0 : (_decoration$spec2$key = _decoration$spec2.key) === null || _decoration$spec2$key === void 0 ? void 0 : _decoration$spec2$key.startsWith(DiffDecorationKey.inline)) !== null && _decoration$spec$key$2 !== void 0 ? _decoration$spec$key$2 : false;
74
+ };
75
+ const isWidgetDecoration = decoration => {
76
+ var _decoration$spec$key$3, _decoration$spec3, _decoration$spec3$key;
77
+ return expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? isDiffDecoration(decoration) && decoration.spec.decorationType === 'widget' : (_decoration$spec$key$3 = (_decoration$spec3 = decoration.spec) === null || _decoration$spec3 === void 0 ? void 0 : (_decoration$spec3$key = _decoration$spec3.key) === null || _decoration$spec3$key === void 0 ? void 0 : _decoration$spec3$key.startsWith(DiffDecorationKey.widget)) !== null && _decoration$spec$key$3 !== void 0 ? _decoration$spec$key$3 : false;
78
+ };
63
79
  const seenBlockKeys = new Set();
64
- const allDecorations = set.find(undefined, undefined, spec => {
65
- var _spec$key, _spec$key2, _spec$key3;
66
- return ((_spec$key = spec.key) === null || _spec$key === void 0 ? void 0 : _spec$key.startsWith(DiffDecorationKey.inline)) || ((_spec$key2 = spec.key) === null || _spec$key2 === void 0 ? void 0 : _spec$key2.startsWith(DiffDecorationKey.widget)) || ((_spec$key3 = spec.key) === null || _spec$key3 === void 0 ? void 0 : _spec$key3.startsWith(DiffDecorationKey.block));
67
- });
80
+ const allDecorations = expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? set.find(undefined, undefined, isDiffDecorationSpec) : set.find(undefined, undefined, spec => specHasDiffKeyPrefix(spec, DiffDecorationKey.inline) || specHasDiffKeyPrefix(spec, DiffDecorationKey.widget) || specHasDiffKeyPrefix(spec, DiffDecorationKey.block));
68
81
 
69
82
  // First pass: filter out listItem blocks and deduplicates blocks
70
83
  const filtered = allDecorations.filter(dec => {
71
- var _dec$spec, _dec$spec$key, _dec$spec$nodeName, _dec$spec3;
72
- if ((_dec$spec = dec.spec) !== null && _dec$spec !== void 0 && (_dec$spec$key = _dec$spec.key) !== null && _dec$spec$key !== void 0 && _dec$spec$key.startsWith(DiffDecorationKey.block)) {
73
- var _dec$spec2;
84
+ if (!isBlockDecoration(dec) && !isInlineDecoration(dec) && !isWidgetDecoration(dec)) {
85
+ return false;
86
+ }
87
+ if (isBlockDecoration(dec)) {
88
+ var _dec$spec, _dec$spec$nodeName;
74
89
  // Skip listItem blocks as they are not scrollable
75
- if (((_dec$spec2 = dec.spec) === null || _dec$spec2 === void 0 ? void 0 : _dec$spec2.nodeName) === 'listItem') return false;
90
+ if (((_dec$spec = dec.spec) === null || _dec$spec === void 0 ? void 0 : _dec$spec.nodeName) === 'listItem') return false;
91
+ const key = `${dec.from}-${dec.to}-${(_dec$spec$nodeName = dec.spec.nodeName) !== null && _dec$spec$nodeName !== void 0 ? _dec$spec$nodeName : ''}`;
92
+ // Skip blocks that have already been seen
93
+ if (seenBlockKeys.has(key)) return false;
94
+ seenBlockKeys.add(key);
76
95
  }
77
- const key = `${dec.from}-${dec.to}-${(_dec$spec$nodeName = (_dec$spec3 = dec.spec) === null || _dec$spec3 === void 0 ? void 0 : _dec$spec3.nodeName) !== null && _dec$spec$nodeName !== void 0 ? _dec$spec$nodeName : ''}`;
78
- // Skip blocks that have already been seen
79
- if (seenBlockKeys.has(key)) return false;
80
- seenBlockKeys.add(key);
81
96
  return true;
82
97
  });
83
98
 
84
99
  // Separate decorations by type for easier processing
85
- const blocks = filtered.filter(d => {
86
- var _d$spec, _d$spec$key;
87
- return (_d$spec = d.spec) === null || _d$spec === void 0 ? void 0 : (_d$spec$key = _d$spec.key) === null || _d$spec$key === void 0 ? void 0 : _d$spec$key.startsWith(DiffDecorationKey.block);
88
- });
89
- const rawInlines = filtered.filter(d => {
90
- var _d$spec2, _d$spec2$key;
91
- return (_d$spec2 = d.spec) === null || _d$spec2 === void 0 ? void 0 : (_d$spec2$key = _d$spec2.key) === null || _d$spec2$key === void 0 ? void 0 : _d$spec2$key.startsWith(DiffDecorationKey.inline);
92
- });
100
+ const blocks = filtered.filter(isBlockDecoration);
101
+ const rawInlines = filtered.filter(isInlineDecoration);
93
102
  const inlines = doc !== undefined ? rawInlines.filter(d => isInlineDiffDecorationRenderableInDoc(doc, d.from, d.to)) : rawInlines;
94
- const widgets = filtered.filter(d => {
95
- var _d$spec3, _d$spec3$key;
96
- return (_d$spec3 = d.spec) === null || _d$spec3 === void 0 ? void 0 : (_d$spec3$key = _d$spec3.key) === null || _d$spec3$key === void 0 ? void 0 : _d$spec3$key.startsWith(DiffDecorationKey.widget);
97
- });
103
+ const widgets = filtered.filter(isWidgetDecoration);
98
104
 
99
105
  // Second pass: exclude overlapping decorations
100
106
  // Rules:
@@ -32,6 +32,7 @@ export const createPlugin = (config, getIntl, api) => {
32
32
  isInverted: false,
33
33
  diffType: 'inline',
34
34
  hideDeletedDiffs: false,
35
+ showIndicators: false,
35
36
  diffDescriptors: []
36
37
  } : {})
37
38
  };
@@ -41,7 +42,7 @@ export const createPlugin = (config, getIntl, api) => {
41
42
  let newPluginState = currentPluginState;
42
43
  if (meta) {
43
44
  if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SHOW_DIFF') {
44
- var _newPluginState, _newPluginState2, _newPluginState3;
45
+ var _newPluginState, _newPluginState2, _newPluginState3, _newPluginState4;
45
46
  // Update the plugin state with the new metadata
46
47
  newPluginState = {
47
48
  ...currentPluginState,
@@ -64,7 +65,8 @@ export const createPlugin = (config, getIntl, api) => {
64
65
  ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
65
66
  isInverted: (_newPluginState = newPluginState) === null || _newPluginState === void 0 ? void 0 : _newPluginState.isInverted,
66
67
  diffType: (_newPluginState2 = newPluginState) === null || _newPluginState2 === void 0 ? void 0 : _newPluginState2.diffType,
67
- hideDeletedDiffs: (_newPluginState3 = newPluginState) === null || _newPluginState3 === void 0 ? void 0 : _newPluginState3.hideDeletedDiffs
68
+ hideDeletedDiffs: (_newPluginState3 = newPluginState) === null || _newPluginState3 === void 0 ? void 0 : _newPluginState3.hideDeletedDiffs,
69
+ showIndicators: (_newPluginState4 = newPluginState) === null || _newPluginState4 === void 0 ? void 0 : _newPluginState4.showIndicators
68
70
  } : {})
69
71
  });
70
72
  // Update the decorations and their ids
@@ -133,7 +135,8 @@ export const createPlugin = (config, getIntl, api) => {
133
135
  ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
134
136
  isInverted: newPluginState.isInverted,
135
137
  diffType: newPluginState.diffType,
136
- hideDeletedDiffs: newPluginState.hideDeletedDiffs
138
+ hideDeletedDiffs: newPluginState.hideDeletedDiffs,
139
+ showIndicators: newPluginState.showIndicators
137
140
  } : {})
138
141
  });
139
142
  newPluginState.decorations = updatedDecorations;
@@ -174,10 +177,9 @@ export const createPlugin = (config, getIntl, api) => {
174
177
 
175
178
  // Scroll to the first decoration when scrollIntoView was requested
176
179
  if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
177
- var _cancelPendingScrollT, _pluginState$decorati;
180
+ var _cancelPendingScrollT;
178
181
  (_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 ? void 0 : _cancelPendingScrollT();
179
- const allDecorations = ((_pluginState$decorati = pluginState.decorations) === null || _pluginState$decorati === void 0 ? void 0 : _pluginState$decorati.find()) || [];
180
- cancelPendingScrollToDecoration = scrollToFirstDecoration(view, allDecorations);
182
+ cancelPendingScrollToDecoration = scrollToFirstDecoration(view, pluginState.decorations);
181
183
 
182
184
  // Reset the flag so we don't scroll again on subsequent updates
183
185
  view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
@@ -1,4 +1,4 @@
1
- import { DiffDecorationKey } from './decorations/decorationKeys';
1
+ import { isDiffDecoration, isDiffDecorationSpec } from './decorations/decorationKeys';
2
2
 
3
3
  /**
4
4
  * Extra space above the scrolled-to element so it does not sit flush under the
@@ -41,16 +41,16 @@ function scrollToSelection(node) {
41
41
  *
42
42
  * @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
43
43
  */
44
- export const scrollToFirstDecoration = (view, decorations) => {
45
- const decoration = decorations[0];
44
+ export const scrollToFirstDecoration = (view, set) => {
45
+ const decoration = set.find(undefined, undefined, isDiffDecorationSpec).find(isDiffDecoration);
46
46
  if (!decoration) {
47
47
  return () => {};
48
48
  }
49
49
  let rafId = requestAnimationFrame(() => {
50
- var _decoration$spec, _decoration$spec$key, _decoration$type;
50
+ var _decoration$type;
51
51
  rafId = null;
52
52
  // @ts-expect-error - decoration.type is not typed public API
53
- if ((_decoration$spec = decoration.spec) !== null && _decoration$spec !== void 0 && (_decoration$spec$key = _decoration$spec.key) !== null && _decoration$spec$key !== void 0 && _decoration$spec$key.startsWith(DiffDecorationKey.widget) && decoration !== null && decoration !== void 0 && (_decoration$type = decoration.type) !== null && _decoration$type !== void 0 && _decoration$type.toDOM) {
53
+ if (decoration.spec.decorationType === 'widget' && decoration !== null && decoration !== void 0 && (_decoration$type = decoration.type) !== null && _decoration$type !== void 0 && _decoration$type.toDOM) {
54
54
  // @ts-expect-error - decoration.type is not typed public API
55
55
  const widgetDom = decoration.type.toDOM;
56
56
  // Always scroll to the top of this decoration even if it's in view already
@@ -84,9 +84,8 @@ export const scrollToActiveDecoration = (view, decorations, activeIndex) => {
84
84
  return () => {};
85
85
  }
86
86
  let rafId = requestAnimationFrame(() => {
87
- var _decoration$spec2, _decoration$spec2$key;
88
87
  rafId = null;
89
- if ((_decoration$spec2 = decoration.spec) !== null && _decoration$spec2 !== void 0 && (_decoration$spec2$key = _decoration$spec2.key) !== null && _decoration$spec2$key !== void 0 && _decoration$spec2$key.startsWith(DiffDecorationKey.widget)) {
88
+ if (isDiffDecoration(decoration) && decoration.spec.decorationType === 'widget') {
90
89
  var _decoration$type2;
91
90
  // @ts-expect-error - decoration.type is not typed public API
92
91
  const widgetDom = decoration === null || decoration === void 0 ? void 0 : (_decoration$type2 = decoration.type) === null || _decoration$type2 === void 0 ? void 0 : _decoration$type2.toDOM;
@@ -1,6 +1,8 @@
1
+ import React from 'react';
1
2
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
2
3
  import { getScrollableDecorations } from './pm-plugins/getScrollableDecorations';
3
4
  import { createPlugin, showDiffPluginKey } from './pm-plugins/main';
5
+ import { IndicatorBarContentComponent } from './ui/IndicatorBar/IndicatorBarContentComponent';
4
6
  export const showDiffPlugin = ({
5
7
  api,
6
8
  config
@@ -58,6 +60,14 @@ export const showDiffPlugin = ({
58
60
  }) => createPlugin(config, getIntl, api)
59
61
  }];
60
62
  },
63
+ contentComponent: () => {
64
+ if (!expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
65
+ return null;
66
+ }
67
+ return /*#__PURE__*/React.createElement(IndicatorBarContentComponent, {
68
+ api: api
69
+ });
70
+ },
61
71
  getSharedState: editorState => {
62
72
  if (!editorState) {
63
73
  return {
@@ -75,7 +85,8 @@ export const showDiffPlugin = ({
75
85
  activeIndex: pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex,
76
86
  numberOfChanges: decorationCount.length,
77
87
  ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
78
- diffDescriptors: pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffDescriptors
88
+ diffDescriptors: pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffDescriptors,
89
+ showIndicators: pluginState === null || pluginState === void 0 ? void 0 : pluginState.showIndicators
79
90
  } : {})
80
91
  };
81
92
  }
@@ -0,0 +1,9 @@
1
+
2
+ ._2rko1rr0{border-radius:var(--ds-radius-full,9999px)}._17c61i8t{position-visibility:anchors-valid}
3
+ ._18u0mgjw{margin-left:var(--ds-space-negative-200,-1pc)}
4
+ ._19pk196n{margin-top:var(--ds-space-negative-075,-6px)}
5
+ ._1bsb1l7b{width:3px}
6
+ ._bfhkvbh5{background-color:var(--ds-border-information,#357de8)}
7
+ ._kqswstnw{position:absolute}
8
+ ._lcxvglyw{pointer-events:none}
9
+ ._otyr196n{margin-bottom:var(--ds-space-negative-075,-6px)}
@@ -0,0 +1,24 @@
1
+ /* IndicatorBar.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import "./IndicatorBar.compiled.css";
3
+ import * as React from 'react';
4
+ import { ax, ix } from "@compiled/react/runtime";
5
+ import { AnchorDocMarginKey } from '../../pm-plugins/decorations/decorationKeys';
6
+ const barStyles = null;
7
+ export function IndicatorBar({
8
+ anchorTop,
9
+ anchorBottom,
10
+ anchorLeft,
11
+ anchorLeftFallback
12
+ }) {
13
+ const leftAnchors = [anchorLeft, anchorLeftFallback, AnchorDocMarginKey].filter(Boolean).map(anchor => `anchor(--${anchor} left)`).join(', ');
14
+ return /*#__PURE__*/React.createElement("span", {
15
+ "data-testid": "diff-indicator-bar",
16
+ style: {
17
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values
18
+ left: `min(${leftAnchors})`,
19
+ top: `anchor(--${anchorTop} top)`,
20
+ bottom: `anchor(--${anchorBottom} bottom)`
21
+ },
22
+ className: ax(["_2rko1rr0 _kqswstnw _1bsb1l7b _bfhkvbh5 _lcxvglyw _18u0mgjw _17c61i8t _19pk196n _otyr196n"])
23
+ });
24
+ }
@@ -0,0 +1,82 @@
1
+ /* IndicatorBarContentComponent.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import { ax, ix } from "@compiled/react/runtime";
3
+ import React from 'react';
4
+ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
5
+ import { buildAnchorDecorationKey, AnchorTypeKey } from '../../pm-plugins/decorations/decorationKeys';
6
+ import { IndicatorBar } from './IndicatorBar';
7
+ const renderIndicatorBar = descriptor => {
8
+ switch (descriptor.type) {
9
+ case 'inline':
10
+ {
11
+ return /*#__PURE__*/React.createElement(IndicatorBar, {
12
+ key: descriptor.id,
13
+ anchorTop: buildAnchorDecorationKey({
14
+ diffId: descriptor.id,
15
+ anchorType: AnchorTypeKey.from
16
+ }),
17
+ anchorBottom: buildAnchorDecorationKey({
18
+ diffId: descriptor.id,
19
+ anchorType: AnchorTypeKey.to
20
+ }),
21
+ anchorLeft: buildAnchorDecorationKey({
22
+ diffId: descriptor.id,
23
+ anchorType: AnchorTypeKey.left
24
+ })
25
+ });
26
+ }
27
+ case 'widget':
28
+ {
29
+ return /*#__PURE__*/React.createElement(IndicatorBar, {
30
+ key: descriptor.id,
31
+ anchorTop: buildAnchorDecorationKey({
32
+ diffId: descriptor.id
33
+ }),
34
+ anchorBottom: buildAnchorDecorationKey({
35
+ diffId: descriptor.id
36
+ }),
37
+ anchorLeft: buildAnchorDecorationKey({
38
+ diffId: descriptor.id,
39
+ anchorType: AnchorTypeKey.left
40
+ }),
41
+ anchorLeftFallback: buildAnchorDecorationKey({
42
+ diffId: descriptor.id
43
+ })
44
+ });
45
+ }
46
+ /**
47
+ * TODO EDITOR-7711:
48
+ * Support IndicatorBar for block-changed type diffs.
49
+ */
50
+ default:
51
+ return null;
52
+ }
53
+ };
54
+
55
+ /**
56
+ * Renders IndicatorBar elements for each diff ID in the current plugin state.
57
+ *
58
+ * This is rendered as a contentComponent so that the IndicatorBar elements
59
+ * share the same DOM context as their CSS anchor spans, allowing the browser
60
+ * to resolve anchor-name references correctly.
61
+ */
62
+
63
+ export const IndicatorBarContentComponent = ({
64
+ api
65
+ }) => {
66
+ const {
67
+ showIndicators,
68
+ isDisplayingChanges,
69
+ diffDescriptors
70
+ } = useSharedPluginStateWithSelector(api, ['showDiff'], state => {
71
+ var _state$showDiffState, _state$showDiffState2, _state$showDiffState3;
72
+ return {
73
+ showIndicators: (_state$showDiffState = state.showDiffState) === null || _state$showDiffState === void 0 ? void 0 : _state$showDiffState.showIndicators,
74
+ isDisplayingChanges: (_state$showDiffState2 = state.showDiffState) === null || _state$showDiffState2 === void 0 ? void 0 : _state$showDiffState2.isDisplayingChanges,
75
+ diffDescriptors: (_state$showDiffState3 = state.showDiffState) === null || _state$showDiffState3 === void 0 ? void 0 : _state$showDiffState3.diffDescriptors
76
+ };
77
+ });
78
+ if (!showIndicators || !isDisplayingChanges || !(diffDescriptors !== null && diffDescriptors !== void 0 && diffDescriptors.length)) {
79
+ return null;
80
+ }
81
+ return /*#__PURE__*/React.createElement(React.Fragment, null, diffDescriptors.map(renderIndicatorBar));
82
+ };
@@ -14,6 +14,7 @@ import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document
14
14
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
15
15
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
16
16
  import { areDocsEqualByBlockStructureAndText } from '../areDocsEqualByBlockStructureAndText';
17
+ import { createDocMarginAnchorWidget } from '../decorations/createAnchorDecorationWidgets';
17
18
  import { createBlockChangedDecoration } from '../decorations/createBlockChangedDecoration';
18
19
  import { createInlineChangedDecoration } from '../decorations/createInlineChangedDecoration';
19
20
  import { createNodeChangedDecorationWidget } from '../decorations/createNodeChangedDecorationWidget';
@@ -88,10 +89,13 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
88
89
  _ref3$diffType = _ref3.diffType,
89
90
  diffType = _ref3$diffType === void 0 ? 'inline' : _ref3$diffType,
90
91
  _ref3$hideDeletedDiff = _ref3.hideDeletedDiffs,
91
- hideDeletedDiffs = _ref3$hideDeletedDiff === void 0 ? false : _ref3$hideDeletedDiff;
92
+ hideDeletedDiffs = _ref3$hideDeletedDiff === void 0 ? false : _ref3$hideDeletedDiff,
93
+ _ref3$showIndicators = _ref3.showIndicators,
94
+ showIndicators = _ref3$showIndicators === void 0 ? false : _ref3$showIndicators;
92
95
  var originalDoc = pluginState.originalDoc,
93
- steps = pluginState.steps;
94
- if (!originalDoc || !pluginState.isDisplayingChanges) {
96
+ steps = pluginState.steps,
97
+ isDisplayingChanges = pluginState.isDisplayingChanges;
98
+ if (!originalDoc || !isDisplayingChanges) {
95
99
  return {
96
100
  decorations: DecorationSet.empty,
97
101
  diffDescriptors: []
@@ -154,6 +158,13 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
154
158
  steps: steps
155
159
  });
156
160
  var decorations = [];
161
+
162
+ /**
163
+ * If showIndicators is on, we create an anchor widget here to mark the doc margin.
164
+ */
165
+ if (showIndicators && expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
166
+ decorations.push(createDocMarginAnchorWidget());
167
+ }
157
168
  changes.forEach(function (change) {
158
169
  var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
159
170
  // Our default operations are insertions, so it should match the opposite of isInverted.
@@ -162,11 +173,13 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
162
173
  var shouldHideDeleted = expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? isInverted && hideDeletedDiffs : false;
163
174
  decorations.push.apply(decorations, _toConsumableArray(createInlineChangedDecoration(_objectSpread({
164
175
  change: change,
176
+ doc: tr.doc,
165
177
  colorScheme: colorScheme,
166
178
  isActive: isActive
167
179
  }, expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
168
180
  isInserted: isInserted,
169
- shouldHideDeleted: shouldHideDeleted
181
+ shouldHideDeleted: shouldHideDeleted,
182
+ showIndicators: showIndicators
170
183
  }))));
171
184
  decorations.push.apply(decorations, _toConsumableArray(calculateNodesForBlockDecoration(_objectSpread(_objectSpread({
172
185
  doc: tr.doc,
@@ -184,7 +197,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
184
197
  if (change.deleted.length > 0) {
185
198
  var _shouldHideDeleted = expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? !isInverted && hideDeletedDiffs : false;
186
199
  if (!_shouldHideDeleted) {
187
- decorations.push.apply(decorations, _toConsumableArray(createNodeChangedDecorationWidget(_objectSpread({
200
+ decorations.push.apply(decorations, _toConsumableArray(createNodeChangedDecorationWidget(_objectSpread(_objectSpread({
188
201
  change: change,
189
202
  doc: originalDoc,
190
203
  nodeViewSerializer: nodeViewSerializer,
@@ -195,6 +208,8 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
195
208
  }, expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
196
209
  isInserted: !isInserted,
197
210
  diffType: diffType
211
+ }), {}, {
212
+ showIndicators: showIndicators
198
213
  }))));
199
214
  }
200
215
  }
@@ -249,7 +264,8 @@ function (_ref4, _ref5) {
249
264
  activeIndexPos = _ref6$.activeIndexPos,
250
265
  isInverted = _ref6$.isInverted,
251
266
  diffType = _ref6$.diffType,
252
- hideDeletedDiffs = _ref6$.hideDeletedDiffs;
267
+ hideDeletedDiffs = _ref6$.hideDeletedDiffs,
268
+ showIndicators = _ref6$.showIndicators;
253
269
  var _ref7 = _slicedToArray(_ref5, 1),
254
270
  _ref7$ = _ref7[0],
255
271
  lastPluginState = _ref7$.pluginState,
@@ -259,11 +275,12 @@ function (_ref4, _ref5) {
259
275
  lastActiveIndexPos = _ref7$.activeIndexPos,
260
276
  lastIsInverted = _ref7$.isInverted,
261
277
  lastDiffType = _ref7$.diffType,
262
- lastHideDeletedDiffs = _ref7$.hideDeletedDiffs;
278
+ lastHideDeletedDiffs = _ref7$.hideDeletedDiffs,
279
+ lastShowIndicators = _ref7$.showIndicators;
263
280
  var originalDocIsSame = lastPluginState.originalDoc && pluginState.originalDoc && pluginState.originalDoc.eq(lastPluginState.originalDoc);
264
281
  if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
265
282
  var _ref8;
266
- return (_ref8 = colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isInverted === lastIsInverted && diffType === lastDiffType && isEqual(activeIndexPos, lastActiveIndexPos) && originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref8 !== void 0 ? _ref8 : false;
283
+ return (_ref8 = colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isInverted === lastIsInverted && diffType === lastDiffType && isEqual(activeIndexPos, lastActiveIndexPos) && originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && hideDeletedDiffs === lastHideDeletedDiffs && showIndicators === lastShowIndicators) !== null && _ref8 !== void 0 ? _ref8 : false;
267
284
  }
268
285
  return (_ref9 = originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isEqual(activeIndexPos, lastActiveIndexPos) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref9 !== void 0 ? _ref9 : false;
269
286
  });