@atlaskit/editor-plugin-show-diff 16.0.7 → 16.0.9

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 (42) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/pm-plugins/decorations/colorSchemes/factory.js +6 -0
  3. package/dist/cjs/pm-plugins/decorations/createBlockChangedDecoration.js +12 -1
  4. package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +2 -0
  5. package/dist/cjs/pm-plugins/decorations/createContributorTagWidget.js +20 -4
  6. package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +4 -0
  7. package/dist/cjs/pm-plugins/decorations/decorationKeys.js +13 -6
  8. package/dist/cjs/ui/ContributorTag/buildContributorTagDom.js +10 -1
  9. package/dist/cjs/ui/ContributorTag/contributorTagController.js +1 -1
  10. package/dist/cjs/ui/IndicatorBar/IndicatorBar.compiled.css +0 -1
  11. package/dist/cjs/ui/IndicatorBar/IndicatorBar.js +13 -2
  12. package/dist/cjs/ui/IndicatorBar/IndicatorBarContentComponent.js +6 -0
  13. package/dist/es2019/pm-plugins/decorations/colorSchemes/factory.js +5 -0
  14. package/dist/es2019/pm-plugins/decorations/createBlockChangedDecoration.js +14 -3
  15. package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +2 -0
  16. package/dist/es2019/pm-plugins/decorations/createContributorTagWidget.js +17 -3
  17. package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +4 -0
  18. package/dist/es2019/pm-plugins/decorations/decorationKeys.js +12 -4
  19. package/dist/es2019/ui/ContributorTag/buildContributorTagDom.js +10 -1
  20. package/dist/es2019/ui/ContributorTag/contributorTagController.js +1 -1
  21. package/dist/es2019/ui/IndicatorBar/IndicatorBar.compiled.css +0 -1
  22. package/dist/es2019/ui/IndicatorBar/IndicatorBar.js +13 -2
  23. package/dist/es2019/ui/IndicatorBar/IndicatorBarContentComponent.js +6 -0
  24. package/dist/esm/pm-plugins/decorations/colorSchemes/factory.js +5 -0
  25. package/dist/esm/pm-plugins/decorations/createBlockChangedDecoration.js +14 -3
  26. package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +2 -0
  27. package/dist/esm/pm-plugins/decorations/createContributorTagWidget.js +20 -4
  28. package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +4 -0
  29. package/dist/esm/pm-plugins/decorations/decorationKeys.js +13 -6
  30. package/dist/esm/ui/ContributorTag/buildContributorTagDom.js +10 -1
  31. package/dist/esm/ui/ContributorTag/contributorTagController.js +1 -1
  32. package/dist/esm/ui/IndicatorBar/IndicatorBar.compiled.css +0 -1
  33. package/dist/esm/ui/IndicatorBar/IndicatorBar.js +13 -2
  34. package/dist/esm/ui/IndicatorBar/IndicatorBarContentComponent.js +6 -0
  35. package/dist/types/pm-plugins/decorations/colorSchemes/factory.d.ts +2 -0
  36. package/dist/types/pm-plugins/decorations/createContributorTagWidget.d.ts +9 -2
  37. package/dist/types/pm-plugins/decorations/decorationKeys.d.ts +2 -0
  38. package/dist/types/showDiffPluginType.d.ts +2 -0
  39. package/dist/types/ui/ContributorTag/buildContributorTagDom.d.ts +3 -1
  40. package/dist/types/ui/ContributorTag/contributorTagController.d.ts +2 -0
  41. package/dist/types/ui/IndicatorBar/IndicatorBar.d.ts +4 -1
  42. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 16.0.9
4
+
5
+ ### Patch Changes
6
+
7
+ - [`c00ea38023074`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c00ea38023074) -
8
+ [ux] Contributor tags on changed block nodes now sit on the block's own top-left corner, instead
9
+ of drifting by that node type's margin. Behind `confluence_ncs_step_diffing_version_history`.
10
+ - Updated dependencies
11
+
12
+ ## 16.0.8
13
+
14
+ ### Patch Changes
15
+
16
+ - [`7c955f7988148`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/7c955f7988148) -
17
+ Match show-diff indicator bars to contributor colors using theme-aware ADS border tokens.
18
+ - Updated dependencies
19
+
3
20
  ## 16.0.7
4
21
 
5
22
  ### Patch Changes
@@ -66,6 +66,7 @@ exports.buildInsertStyleRuleNodeActive = buildInsertStyleRuleNodeActive;
66
66
  exports.buildInsertStyleRuleNodeNew = buildInsertStyleRuleNodeNew;
67
67
  exports.buildInsertedBlockNodeStyle = buildInsertedBlockNodeStyle;
68
68
  exports.buildInsertedInlineStyle = buildInsertedInlineStyle;
69
+ exports.getAccentBorderColor = getAccentBorderColor;
69
70
  exports.getAccentTokens = getAccentTokens;
70
71
  exports.getDeletedHighlightHoverBorderColor = getDeletedHighlightHoverBorderColor;
71
72
  exports.getDeletedHighlightHoverColor = getDeletedHighlightHoverColor;
@@ -227,6 +228,11 @@ function getAccentTokens(color) {
227
228
  };
228
229
  }
229
230
 
231
+ /** Border token for a contributor accent, used by the diff indicator. */
232
+ function getAccentBorderColor(color) {
233
+ return borderAccent(color);
234
+ }
235
+
230
236
  /** Ring colour for an active (scroll-target) deleted block outline. */
231
237
  function deletedBlockOutlineActiveRing(colors) {
232
238
  return colors.deletedBlockOutlineActiveEmphasis === 'border' ? borderAccent(colors.deleteActiveColor) : bgSubtlerPressed(colors.deleteActiveColor);
@@ -198,13 +198,20 @@ var createBlockChangedDecoration = exports.createBlockChangedDecoration = functi
198
198
  from: change.from,
199
199
  name: change.name
200
200
  });
201
+ // Named so this block's own tag can position against it; see `createContributorTagWidget`.
202
+ var tagAnchorName = shouldTagBlock && (0, _createContributorTagWidget.isContributorTagWidgetEnabled)() ? (0, _decorationKeys.buildAnchorDecorationKey)({
203
+ diffId: diffId,
204
+ anchorType: _decorationKeys.AnchorTypeKey.tag
205
+ }) : undefined;
201
206
  if (shouldHideDeleted) {
202
207
  return [_view.Decoration.node(change.from, change.to, {
203
208
  style: displayNoneStyle
204
209
  }, (0, _decorationKeys.buildDiffDecorationSpec)({
210
+ colorScheme: colorScheme,
205
211
  decorationType: 'block',
206
212
  diffId: diffId,
207
213
  isActive: isActive,
214
+ isInserted: isInserted,
208
215
  nodeName: change.name,
209
216
  diffType: diffType
210
217
  }))];
@@ -234,13 +241,16 @@ var createBlockChangedDecoration = exports.createBlockChangedDecoration = functi
234
241
  }));
235
242
  }
236
243
  // isInserted is only read under the extended experience, so pass it unconditionally.
237
- var style = getBlockNodeStyle({
244
+ var nodeStyle = getBlockNodeStyle({
238
245
  nodeName: change.name,
239
246
  colorScheme: colorScheme,
240
247
  isInserted: isInserted,
241
248
  isActive: isActive,
242
249
  diffType: diffType
243
250
  });
251
+ var style = tagAnchorName ? [nodeStyle, (0, _lazyNodeView.convertToInlineCss)({
252
+ anchorName: "--".concat(tagAnchorName)
253
+ })].filter(Boolean).join(' ') : nodeStyle;
244
254
  var className = getNodeClass(change.name);
245
255
  if (style || className) {
246
256
  decorations.push(_view.Decoration.node(change.from, change.to, _objectSpread({
@@ -278,6 +288,7 @@ var createBlockChangedDecoration = exports.createBlockChangedDecoration = functi
278
288
  if (shouldTagBlock && doc) {
279
289
  var tagWidget = (0, _createContributorTagWidget.createContributorTagWidget)({
280
290
  anchorAtRangeStart: true,
291
+ anchorName: tagAnchorName,
281
292
  doc: doc,
282
293
  from: change.from,
283
294
  to: change.to,
@@ -241,9 +241,11 @@ var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidget
241
241
  originalDoc.slice(changedRow.fromA, changedRow.toA));
242
242
  var diffId = crypto.randomUUID();
243
243
  return _view.Decoration.widget(safeInsertPos, rowDOM, _objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)({
244
+ colorScheme: colorScheme,
244
245
  decorationType: 'widget',
245
246
  diffId: diffId,
246
247
  isActive: false,
248
+ isInserted: isInserted,
247
249
  diffType: diffType
248
250
  })));
249
251
  });
@@ -38,13 +38,15 @@ var isContributorTagWidgetEnabled = exports.isContributorTagWidgetEnabled = func
38
38
  * (EDITOR-8702).
39
39
  */
40
40
  var mountContributorTag = exports.mountContributorTag = function mountContributorTag(_ref) {
41
- var diffId = _ref.diffId,
41
+ var anchorName = _ref.anchorName,
42
+ diffId = _ref.diffId,
42
43
  host = _ref.host,
43
44
  mountContext = _ref.mountContext;
44
45
  if (!mountContext) {
45
46
  return undefined;
46
47
  }
47
48
  var controller = new _contributorTagController.ContributorTagController(_objectSpread(_objectSpread({}, mountContext), {}, {
49
+ anchorName: anchorName,
48
50
  diffId: diffId
49
51
  }));
50
52
  controller.mount(host);
@@ -76,14 +78,25 @@ var unmountContributorTag = exports.unmountContributorTag = function unmountCont
76
78
  /**
77
79
  * A zero-size inline host for one contributor tag. `position: relative` makes it the containing
78
80
  * block of the absolutely positioned tag, so the tag sits on the change with no measurement.
81
+ *
82
+ * An anchored tag takes a static host instead: an anchor is only acceptable when it sits inside the
83
+ * tag's containing block, and the block node is not inside this host. Measured in Chrome 153 — a
84
+ * relative host makes the `anchor()` insets resolve to `auto`.
79
85
  */
80
- var buildContributorTagHost = function buildContributorTagHost(diffId) {
86
+ var buildContributorTagHost = function buildContributorTagHost(diffId, anchorName) {
81
87
  var host = document.createElement('span');
82
88
  host.setAttribute(CONTRIBUTOR_TAG_HOST_ATTRIBUTE, diffId);
83
- host.style.setProperty('position', 'relative');
89
+ if (!anchorName) {
90
+ host.style.setProperty('position', 'relative');
91
+ }
84
92
  return host;
85
93
  };
86
94
 
95
+ /** Without anchor positioning the tag keeps its host-relative placement. */
96
+ var supportsAnchorPositioning = function supportsAnchorPositioning() {
97
+ return typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('bottom', 'anchor(--a top)');
98
+ };
99
+
87
100
  /**
88
101
  * The host for a tag on deleted content, which renders inside its own widget decoration rather than
89
102
  * needing one of its own. Unmount with `unmountContributorTag` from that widget's `destroy`.
@@ -183,6 +196,7 @@ var resolveCodeBlockStart = function resolveCodeBlockStart(doc, pos) {
183
196
  var createContributorTagWidget = exports.createContributorTagWidget = function createContributorTagWidget(_ref3) {
184
197
  var _ref3$anchorAtRangeSt = _ref3.anchorAtRangeStart,
185
198
  anchorAtRangeStart = _ref3$anchorAtRangeSt === void 0 ? false : _ref3$anchorAtRangeSt,
199
+ anchorName = _ref3.anchorName,
186
200
  doc = _ref3.doc,
187
201
  from = _ref3.from,
188
202
  to = _ref3.to,
@@ -191,6 +205,7 @@ var createContributorTagWidget = exports.createContributorTagWidget = function c
191
205
  if (!isContributorTagWidgetEnabled()) {
192
206
  return undefined;
193
207
  }
208
+ var tagAnchorName = anchorName && supportsAnchorPositioning() ? anchorName : undefined;
194
209
 
195
210
  // Reassigned when ProseMirror redraws this decoration, which it may do more than once for the
196
211
  // same `Decoration` instance.
@@ -201,8 +216,9 @@ var createContributorTagWidget = exports.createContributorTagWidget = function c
201
216
  return _view.Decoration.widget(
202
217
  // Keep the host out of the table row's grid (EDITOR-8442).
203
218
  (0, _createAnchorDecorationWidgets.clampAnchorPosIntoCell)(doc, codeBlockPos !== null && codeBlockPos !== void 0 ? codeBlockPos : anchorPos, 1), function () {
204
- var host = buildContributorTagHost(diffId);
219
+ var host = buildContributorTagHost(diffId, tagAnchorName);
205
220
  mount = mountContributorTag({
221
+ anchorName: tagAnchorName,
206
222
  diffId: diffId,
207
223
  host: host,
208
224
  mountContext: mountContext
@@ -84,8 +84,10 @@ var createTableCellContentWidgets = function createTableCellContentWidgets(_ref)
84
84
  // Skip findSafeInsertPos: it walks forward to a schema-valid cell-insert point
85
85
  // and pushes the widget into the next cell. `targetPos` is the correct spot.
86
86
  decorations.push(_view.Decoration.widget(targetPos, dom, _objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)(_objectSpread({
87
+ colorScheme: colorScheme,
87
88
  decorationType: 'widget',
88
89
  diffId: crypto.randomUUID(),
90
+ isInserted: isInserted,
89
91
  diffType: diffType
90
92
  }, (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && {
91
93
  side: -1
@@ -428,8 +430,10 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
428
430
  defaultSpacer.style.display = 'block';
429
431
  defaultSpacer.style.marginTop = "var(--ds-space-100, 8px)";
430
432
  decorations.push(_view.Decoration.widget(safeInsertPos, defaultSpacer, _objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)({
433
+ colorScheme: colorScheme,
431
434
  decorationType: 'widget',
432
435
  diffId: crypto.randomUUID(),
436
+ isInserted: isInserted,
433
437
  diffType: diffType
434
438
  }))));
435
439
  }
@@ -25,7 +25,9 @@ var AnchorTypeKey = exports.AnchorTypeKey = {
25
25
  from: 'from',
26
26
  to: 'to',
27
27
  left: 'left',
28
- docMargin: 'doc-margin'
28
+ docMargin: 'doc-margin',
29
+ /** The decorated block itself, which its contributor tag positions against. */
30
+ tag: 'tag'
29
31
  };
30
32
 
31
33
  /**
@@ -89,9 +91,9 @@ var buildDiffDecorationSpec = exports.buildDiffDecorationSpec = function buildDi
89
91
  attributionKey: attributionKey
90
92
  } : {}), attributionKey && isActive !== undefined ? {
91
93
  isActive: isActive
92
- } : {}), attributionKey && isInserted !== undefined ? {
94
+ } : {}), isInserted !== undefined ? {
93
95
  isInserted: isInserted
94
- } : {}), attributionKey && colorScheme ? {
96
+ } : {}), colorScheme ? {
95
97
  colorScheme: colorScheme
96
98
  } : {}), nodeName ? {
97
99
  nodeName: nodeName
@@ -147,9 +149,14 @@ var isDiffDecoration = exports.isDiffDecoration = function isDiffDecoration(deco
147
149
  var extractDiffDescriptors = exports.extractDiffDescriptors = function extractDiffDescriptors(decorations) {
148
150
  return decorations.find(undefined, undefined, isDiffDecorationSpec).filter(isDiffDecoration).map(function (_ref5) {
149
151
  var spec = _ref5.spec;
150
- return {
151
- id: spec.diffId,
152
+ return _objectSpread(_objectSpread(_objectSpread({}, spec.colorScheme ? {
153
+ colorScheme: spec.colorScheme
154
+ } : {}), {}, {
155
+ id: spec.diffId
156
+ }, spec.isInserted !== undefined ? {
157
+ isInserted: spec.isInserted
158
+ } : {}), {}, {
152
159
  type: spec.decorationType
153
- };
160
+ });
154
161
  });
155
162
  };
@@ -186,10 +186,19 @@ var createSpan = function createSpan(doc, style) {
186
186
  *
187
187
  * Built element by element rather than through `DOMSerializer.renderSpec`, which returns `Node` and
188
188
  * would need a cast per reference below.
189
+ *
190
+ * `anchorName` is the `anchor-name` its block carries — see `createContributorTagWidget`.
189
191
  */
190
- var buildContributorTagDom = exports.buildContributorTagDom = function buildContributorTagDom(doc) {
192
+ var buildContributorTagDom = exports.buildContributorTagDom = function buildContributorTagDom(doc, anchorName) {
191
193
  var labelId = generateLabelId();
192
194
  var root = createSpan(doc, constraintStyle);
195
+ if (anchorName) {
196
+ // Anchored to the block's own box, so the tag clears whatever margin its node type has — the
197
+ // same `anchor()` insets the `IndicatorBar` uses. A browser without anchor positioning rejects
198
+ // these values and keeps the `bottom: 100%` fallback above.
199
+ root.style.setProperty('bottom', "anchor(--".concat(anchorName, " top)"));
200
+ root.style.setProperty('inset-inline-start', "anchor(--".concat(anchorName, " left)"));
201
+ }
193
202
  var tag = createSpan(doc, tagStyle, {
194
203
  class: CONTRIBUTOR_TAG_CLASS,
195
204
  'data-testid': CONTRIBUTOR_TAG_TESTID,
@@ -186,7 +186,7 @@ var ContributorTagController = exports.ContributorTagController = /*#__PURE__*/f
186
186
  return;
187
187
  }
188
188
  if (!this.dom) {
189
- this.dom = (0, _buildContributorTagDom.buildContributorTagDom)(host.ownerDocument);
189
+ this.dom = (0, _buildContributorTagDom.buildContributorTagDom)(host.ownerDocument, this.options.anchorName);
190
190
  this.bindTag(this.dom);
191
191
  host.appendChild(this.dom.root);
192
192
  }
@@ -2,7 +2,6 @@
2
2
  ._18u0mgjw{margin-left:var(--ds-space-negative-200,-1pc)}
3
3
  ._19pk196n{margin-top:var(--ds-space-negative-075,-6px)}
4
4
  ._1bsb1l7b{width:3px}
5
- ._bfhkvbh5{background-color:var(--ds-border-information,#357de8)}
6
5
  ._kqswstnw{position:absolute}
7
6
  ._lcxvglyw{pointer-events:none}
8
7
  ._otyr196n{margin-bottom:var(--ds-space-negative-075,-6px)}
@@ -9,25 +9,36 @@ exports.IndicatorBar = IndicatorBar;
9
9
  require("./IndicatorBar.compiled.css");
10
10
  var React = _interopRequireWildcard(require("react"));
11
11
  var _runtime = require("@compiled/react/runtime");
12
+ var _factory = require("../../pm-plugins/decorations/colorSchemes/factory");
13
+ var _schemes = require("../../pm-plugins/decorations/colorSchemes/schemes");
12
14
  var _decorationKeys = require("../../pm-plugins/decorations/decorationKeys");
13
15
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
14
16
  var barStyles = null;
17
+ var getIndicatorColor = function getIndicatorColor(colorScheme, isInserted) {
18
+ if (colorScheme === undefined || colorScheme === 'standard' || colorScheme === 'traditional' || isInserted === undefined) {
19
+ return "var(--ds-border-information, #357DE8)";
20
+ }
21
+ return (0, _factory.getAccentBorderColor)(_schemes.colorSchemeRegistry[colorScheme][isInserted ? 'insertColor' : 'deleteColor']);
22
+ };
15
23
  function IndicatorBar(_ref) {
16
24
  var anchorTop = _ref.anchorTop,
17
25
  anchorBottom = _ref.anchorBottom,
18
26
  anchorLeft = _ref.anchorLeft,
19
- anchorLeftFallback = _ref.anchorLeftFallback;
27
+ anchorLeftFallback = _ref.anchorLeftFallback,
28
+ colorScheme = _ref.colorScheme,
29
+ isInserted = _ref.isInserted;
20
30
  var leftAnchors = [anchorLeft, anchorLeftFallback, _decorationKeys.AnchorDocMarginKey].filter(Boolean).map(function (anchor) {
21
31
  return "anchor(--".concat(anchor, " left)");
22
32
  }).join(', ');
23
33
  return /*#__PURE__*/React.createElement("span", {
24
34
  "data-testid": "diff-indicator-bar",
25
35
  style: {
36
+ backgroundColor: getIndicatorColor(colorScheme, isInserted),
26
37
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values
27
38
  left: "min(".concat(leftAnchors, ")"),
28
39
  top: "anchor(--".concat(anchorTop, " top)"),
29
40
  bottom: "anchor(--".concat(anchorBottom, " bottom)")
30
41
  },
31
- className: (0, _runtime.ax)(["_2rko1rr0 _kqswstnw _1bsb1l7b _bfhkvbh5 _lcxvglyw _18u0mgjw _17c61i8t _19pk196n _otyr196n"])
42
+ className: (0, _runtime.ax)(["_2rko1rr0 _kqswstnw _1bsb1l7b _lcxvglyw _18u0mgjw _17c61i8t _19pk196n _otyr196n"])
32
43
  });
33
44
  }
@@ -17,6 +17,8 @@ var renderIndicatorBar = function renderIndicatorBar(descriptor) {
17
17
  {
18
18
  return /*#__PURE__*/_react.default.createElement(_IndicatorBar.IndicatorBar, {
19
19
  key: descriptor.id,
20
+ colorScheme: descriptor.colorScheme,
21
+ isInserted: descriptor.isInserted,
20
22
  anchorTop: (0, _decorationKeys.buildAnchorDecorationKey)({
21
23
  diffId: descriptor.id,
22
24
  anchorType: _decorationKeys.AnchorTypeKey.from
@@ -41,6 +43,8 @@ var renderIndicatorBar = function renderIndicatorBar(descriptor) {
41
43
  });
42
44
  return /*#__PURE__*/_react.default.createElement(_IndicatorBar.IndicatorBar, {
43
45
  key: descriptor.id,
46
+ colorScheme: descriptor.colorScheme,
47
+ isInserted: descriptor.isInserted,
44
48
  anchorTop: blockAnchor,
45
49
  anchorBottom: blockAnchor,
46
50
  anchorLeft: (0, _decorationKeys.buildAnchorDecorationKey)({
@@ -53,6 +57,8 @@ var renderIndicatorBar = function renderIndicatorBar(descriptor) {
53
57
  {
54
58
  return /*#__PURE__*/_react.default.createElement(_IndicatorBar.IndicatorBar, {
55
59
  key: descriptor.id,
60
+ colorScheme: descriptor.colorScheme,
61
+ isInserted: descriptor.isInserted,
56
62
  anchorTop: (0, _decorationKeys.buildAnchorDecorationKey)({
57
63
  diffId: descriptor.id
58
64
  }),
@@ -152,6 +152,11 @@ export function getAccentTokens(color) {
152
152
  };
153
153
  }
154
154
 
155
+ /** Border token for a contributor accent, used by the diff indicator. */
156
+ export function getAccentBorderColor(color) {
157
+ return borderAccent(color);
158
+ }
159
+
155
160
  /** Ring colour for an active (scroll-target) deleted block outline. */
156
161
  function deletedBlockOutlineActiveRing(colors) {
157
162
  return colors.deletedBlockOutlineActiveEmphasis === 'border' ? borderAccent(colors.deleteActiveColor) : bgSubtlerPressed(colors.deleteActiveColor);
@@ -6,8 +6,8 @@ import { isExtendedEnabled } from '../isExtendedEnabled';
6
6
  import { buildAddedCellOverlayRoundedStyle, buildAddedCellOverlayStyle, buildDeletedBlockNodeStyle, buildDeletedCellOverlayRoundedStyle, buildDeletedCellOverlayStyle, buildInsertedBlockNodeStyle } from './colorSchemes/factory';
7
7
  import { colorSchemeRegistry, getLegacyColorScheme } from './colorSchemes/schemes';
8
8
  import { createBlockIndicatorAnchorWidgets } from './createAnchorDecorationWidgets';
9
- import { createContributorTagWidget } from './createContributorTagWidget';
10
- import { buildDiffDecorationSpec } from './decorationKeys';
9
+ import { createContributorTagWidget, isContributorTagWidgetEnabled } from './createContributorTagWidget';
10
+ import { AnchorTypeKey, buildAnchorDecorationKey, buildDiffDecorationSpec } from './decorationKeys';
11
11
  import { getBlockNodeStyleLegacy, resolveCellOverlayStyleLegacy } from './createBlockChangedDecoration.styles.legacy';
12
12
  const displayNoneStyle = convertToInlineCss({
13
13
  display: 'none'
@@ -178,13 +178,20 @@ export const createBlockChangedDecoration = ({
178
178
  from: change.from,
179
179
  name: change.name
180
180
  });
181
+ // Named so this block's own tag can position against it; see `createContributorTagWidget`.
182
+ const tagAnchorName = shouldTagBlock && isContributorTagWidgetEnabled() ? buildAnchorDecorationKey({
183
+ diffId,
184
+ anchorType: AnchorTypeKey.tag
185
+ }) : undefined;
181
186
  if (shouldHideDeleted) {
182
187
  return [Decoration.node(change.from, change.to, {
183
188
  style: displayNoneStyle
184
189
  }, buildDiffDecorationSpec({
190
+ colorScheme,
185
191
  decorationType: 'block',
186
192
  diffId,
187
193
  isActive,
194
+ isInserted,
188
195
  nodeName: change.name,
189
196
  diffType
190
197
  }))];
@@ -214,13 +221,16 @@ export const createBlockChangedDecoration = ({
214
221
  }));
215
222
  }
216
223
  // isInserted is only read under the extended experience, so pass it unconditionally.
217
- const style = getBlockNodeStyle({
224
+ const nodeStyle = getBlockNodeStyle({
218
225
  nodeName: change.name,
219
226
  colorScheme,
220
227
  isInserted,
221
228
  isActive,
222
229
  diffType
223
230
  });
231
+ const style = tagAnchorName ? [nodeStyle, convertToInlineCss({
232
+ anchorName: `--${tagAnchorName}`
233
+ })].filter(Boolean).join(' ') : nodeStyle;
224
234
  const className = getNodeClass(change.name);
225
235
  if (style || className) {
226
236
  decorations.push(Decoration.node(change.from, change.to, {
@@ -260,6 +270,7 @@ export const createBlockChangedDecoration = ({
260
270
  if (shouldTagBlock && doc) {
261
271
  const tagWidget = createContributorTagWidget({
262
272
  anchorAtRangeStart: true,
273
+ anchorName: tagAnchorName,
263
274
  doc,
264
275
  from: change.from,
265
276
  to: change.to,
@@ -211,9 +211,11 @@ export const createChangedRowDecorationWidgets = ({
211
211
  const diffId = crypto.randomUUID();
212
212
  return Decoration.widget(safeInsertPos, rowDOM, {
213
213
  ...buildDiffDecorationSpec({
214
+ colorScheme,
214
215
  decorationType: 'widget',
215
216
  diffId,
216
217
  isActive: false,
218
+ isInserted,
217
219
  diffType
218
220
  })
219
221
  });
@@ -27,6 +27,7 @@ export const isContributorTagWidgetEnabled = () => fg('confluence_ncs_step_diffi
27
27
  * (EDITOR-8702).
28
28
  */
29
29
  export const mountContributorTag = ({
30
+ anchorName,
30
31
  diffId,
31
32
  host,
32
33
  mountContext
@@ -36,6 +37,7 @@ export const mountContributorTag = ({
36
37
  }
37
38
  const controller = new ContributorTagController({
38
39
  ...mountContext,
40
+ anchorName,
39
41
  diffId
40
42
  });
41
43
  controller.mount(host);
@@ -65,14 +67,23 @@ export const unmountContributorTag = mount => {
65
67
  /**
66
68
  * A zero-size inline host for one contributor tag. `position: relative` makes it the containing
67
69
  * block of the absolutely positioned tag, so the tag sits on the change with no measurement.
70
+ *
71
+ * An anchored tag takes a static host instead: an anchor is only acceptable when it sits inside the
72
+ * tag's containing block, and the block node is not inside this host. Measured in Chrome 153 — a
73
+ * relative host makes the `anchor()` insets resolve to `auto`.
68
74
  */
69
- const buildContributorTagHost = diffId => {
75
+ const buildContributorTagHost = (diffId, anchorName) => {
70
76
  const host = document.createElement('span');
71
77
  host.setAttribute(CONTRIBUTOR_TAG_HOST_ATTRIBUTE, diffId);
72
- host.style.setProperty('position', 'relative');
78
+ if (!anchorName) {
79
+ host.style.setProperty('position', 'relative');
80
+ }
73
81
  return host;
74
82
  };
75
83
 
84
+ /** Without anchor positioning the tag keeps its host-relative placement. */
85
+ const supportsAnchorPositioning = () => typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('bottom', 'anchor(--a top)');
86
+
76
87
  /**
77
88
  * The host for a tag on deleted content, which renders inside its own widget decoration rather than
78
89
  * needing one of its own. Unmount with `unmountContributorTag` from that widget's `destroy`.
@@ -171,6 +182,7 @@ const resolveCodeBlockStart = (doc, pos) => {
171
182
  */
172
183
  export const createContributorTagWidget = ({
173
184
  anchorAtRangeStart = false,
185
+ anchorName,
174
186
  doc,
175
187
  from,
176
188
  to,
@@ -180,6 +192,7 @@ export const createContributorTagWidget = ({
180
192
  if (!isContributorTagWidgetEnabled()) {
181
193
  return undefined;
182
194
  }
195
+ const tagAnchorName = anchorName && supportsAnchorPositioning() ? anchorName : undefined;
183
196
 
184
197
  // Reassigned when ProseMirror redraws this decoration, which it may do more than once for the
185
198
  // same `Decoration` instance.
@@ -190,8 +203,9 @@ export const createContributorTagWidget = ({
190
203
  return Decoration.widget(
191
204
  // Keep the host out of the table row's grid (EDITOR-8442).
192
205
  clampAnchorPosIntoCell(doc, codeBlockPos !== null && codeBlockPos !== void 0 ? codeBlockPos : anchorPos, 1), () => {
193
- const host = buildContributorTagHost(diffId);
206
+ const host = buildContributorTagHost(diffId, tagAnchorName);
194
207
  mount = mountContributorTag({
208
+ anchorName: tagAnchorName,
195
209
  diffId,
196
210
  host,
197
211
  mountContext
@@ -74,8 +74,10 @@ const createTableCellContentWidgets = ({
74
74
  // and pushes the widget into the next cell. `targetPos` is the correct spot.
75
75
  decorations.push(Decoration.widget(targetPos, dom, {
76
76
  ...buildDiffDecorationSpec({
77
+ colorScheme,
77
78
  decorationType: 'widget',
78
79
  diffId: crypto.randomUUID(),
80
+ isInserted,
79
81
  diffType,
80
82
  ...(isExtendedEnabled(diffType) && {
81
83
  side: -1
@@ -414,8 +416,10 @@ export const createNodeChangedDecorationWidget = ({
414
416
  defaultSpacer.style.marginTop = "var(--ds-space-100, 8px)";
415
417
  decorations.push(Decoration.widget(safeInsertPos, defaultSpacer, {
416
418
  ...buildDiffDecorationSpec({
419
+ colorScheme,
417
420
  decorationType: 'widget',
418
421
  diffId: crypto.randomUUID(),
422
+ isInserted,
419
423
  diffType
420
424
  })
421
425
  }));
@@ -12,7 +12,9 @@ export const AnchorTypeKey = {
12
12
  from: 'from',
13
13
  to: 'to',
14
14
  left: 'left',
15
- docMargin: 'doc-margin'
15
+ docMargin: 'doc-margin',
16
+ /** The decorated block itself, which its contributor tag positions against. */
17
+ tag: 'tag'
16
18
  };
17
19
 
18
20
  /**
@@ -77,14 +79,14 @@ export const buildDiffDecorationSpec = ({
77
79
  ...(attributionKey ? {
78
80
  attributionKey
79
81
  } : {}),
80
- // Only alongside an attribution key, so the spec shape is unchanged when the gate is off.
82
+ // Active state is only needed by contributor tags, so keep it alongside an attribution key.
81
83
  ...(attributionKey && isActive !== undefined ? {
82
84
  isActive
83
85
  } : {}),
84
- ...(attributionKey && isInserted !== undefined ? {
86
+ ...(isInserted !== undefined ? {
85
87
  isInserted
86
88
  } : {}),
87
- ...(attributionKey && colorScheme ? {
89
+ ...(colorScheme ? {
88
90
  colorScheme
89
91
  } : {}),
90
92
  ...(nodeName ? {
@@ -138,7 +140,13 @@ export const extractDiffDescriptors = decorations => decorations.find(undefined,
138
140
  spec
139
141
  }) => {
140
142
  return {
143
+ ...(spec.colorScheme ? {
144
+ colorScheme: spec.colorScheme
145
+ } : {}),
141
146
  id: spec.diffId,
147
+ ...(spec.isInserted !== undefined ? {
148
+ isInserted: spec.isInserted
149
+ } : {}),
142
150
  type: spec.decorationType
143
151
  };
144
152
  });
@@ -172,10 +172,19 @@ const createSpan = (doc, style, attributes = {}) => {
172
172
  *
173
173
  * Built element by element rather than through `DOMSerializer.renderSpec`, which returns `Node` and
174
174
  * would need a cast per reference below.
175
+ *
176
+ * `anchorName` is the `anchor-name` its block carries — see `createContributorTagWidget`.
175
177
  */
176
- export const buildContributorTagDom = doc => {
178
+ export const buildContributorTagDom = (doc, anchorName) => {
177
179
  const labelId = generateLabelId();
178
180
  const root = createSpan(doc, constraintStyle);
181
+ if (anchorName) {
182
+ // Anchored to the block's own box, so the tag clears whatever margin its node type has — the
183
+ // same `anchor()` insets the `IndicatorBar` uses. A browser without anchor positioning rejects
184
+ // these values and keeps the `bottom: 100%` fallback above.
185
+ root.style.setProperty('bottom', `anchor(--${anchorName} top)`);
186
+ root.style.setProperty('inset-inline-start', `anchor(--${anchorName} left)`);
187
+ }
179
188
  const tag = createSpan(doc, tagStyle, {
180
189
  class: CONTRIBUTOR_TAG_CLASS,
181
190
  'data-testid': CONTRIBUTOR_TAG_TESTID,
@@ -160,7 +160,7 @@ export class ContributorTagController {
160
160
  return;
161
161
  }
162
162
  if (!this.dom) {
163
- this.dom = buildContributorTagDom(host.ownerDocument);
163
+ this.dom = buildContributorTagDom(host.ownerDocument, this.options.anchorName);
164
164
  this.bindTag(this.dom);
165
165
  host.appendChild(this.dom.root);
166
166
  }
@@ -2,7 +2,6 @@
2
2
  ._18u0mgjw{margin-left:var(--ds-space-negative-200,-1pc)}
3
3
  ._19pk196n{margin-top:var(--ds-space-negative-075,-6px)}
4
4
  ._1bsb1l7b{width:3px}
5
- ._bfhkvbh5{background-color:var(--ds-border-information,#357de8)}
6
5
  ._kqswstnw{position:absolute}
7
6
  ._lcxvglyw{pointer-events:none}
8
7
  ._otyr196n{margin-bottom:var(--ds-space-negative-075,-6px)}
@@ -2,23 +2,34 @@
2
2
  import "./IndicatorBar.compiled.css";
3
3
  import * as React from 'react';
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
+ import { getAccentBorderColor } from '../../pm-plugins/decorations/colorSchemes/factory';
6
+ import { colorSchemeRegistry } from '../../pm-plugins/decorations/colorSchemes/schemes';
5
7
  import { AnchorDocMarginKey } from '../../pm-plugins/decorations/decorationKeys';
6
8
  const barStyles = null;
9
+ const getIndicatorColor = (colorScheme, isInserted) => {
10
+ if (colorScheme === undefined || colorScheme === 'standard' || colorScheme === 'traditional' || isInserted === undefined) {
11
+ return "var(--ds-border-information, #357DE8)";
12
+ }
13
+ return getAccentBorderColor(colorSchemeRegistry[colorScheme][isInserted ? 'insertColor' : 'deleteColor']);
14
+ };
7
15
  export function IndicatorBar({
8
16
  anchorTop,
9
17
  anchorBottom,
10
18
  anchorLeft,
11
- anchorLeftFallback
19
+ anchorLeftFallback,
20
+ colorScheme,
21
+ isInserted
12
22
  }) {
13
23
  const leftAnchors = [anchorLeft, anchorLeftFallback, AnchorDocMarginKey].filter(Boolean).map(anchor => `anchor(--${anchor} left)`).join(', ');
14
24
  return /*#__PURE__*/React.createElement("span", {
15
25
  "data-testid": "diff-indicator-bar",
16
26
  style: {
27
+ backgroundColor: getIndicatorColor(colorScheme, isInserted),
17
28
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values
18
29
  left: `min(${leftAnchors})`,
19
30
  top: `anchor(--${anchorTop} top)`,
20
31
  bottom: `anchor(--${anchorBottom} bottom)`
21
32
  },
22
- className: ax(["_2rko1rr0 _kqswstnw _1bsb1l7b _bfhkvbh5 _lcxvglyw _18u0mgjw _17c61i8t _19pk196n _otyr196n"])
33
+ className: ax(["_2rko1rr0 _kqswstnw _1bsb1l7b _lcxvglyw _18u0mgjw _17c61i8t _19pk196n _otyr196n"])
23
34
  });
24
35
  }
@@ -10,6 +10,8 @@ const renderIndicatorBar = descriptor => {
10
10
  {
11
11
  return /*#__PURE__*/React.createElement(IndicatorBar, {
12
12
  key: descriptor.id,
13
+ colorScheme: descriptor.colorScheme,
14
+ isInserted: descriptor.isInserted,
13
15
  anchorTop: buildAnchorDecorationKey({
14
16
  diffId: descriptor.id,
15
17
  anchorType: AnchorTypeKey.from
@@ -34,6 +36,8 @@ const renderIndicatorBar = descriptor => {
34
36
  });
35
37
  return /*#__PURE__*/React.createElement(IndicatorBar, {
36
38
  key: descriptor.id,
39
+ colorScheme: descriptor.colorScheme,
40
+ isInserted: descriptor.isInserted,
37
41
  anchorTop: blockAnchor,
38
42
  anchorBottom: blockAnchor,
39
43
  anchorLeft: buildAnchorDecorationKey({
@@ -46,6 +50,8 @@ const renderIndicatorBar = descriptor => {
46
50
  {
47
51
  return /*#__PURE__*/React.createElement(IndicatorBar, {
48
52
  key: descriptor.id,
53
+ colorScheme: descriptor.colorScheme,
54
+ isInserted: descriptor.isInserted,
49
55
  anchorTop: buildAnchorDecorationKey({
50
56
  diffId: descriptor.id
51
57
  }),
@@ -155,6 +155,11 @@ export function getAccentTokens(color) {
155
155
  };
156
156
  }
157
157
 
158
+ /** Border token for a contributor accent, used by the diff indicator. */
159
+ export function getAccentBorderColor(color) {
160
+ return borderAccent(color);
161
+ }
162
+
158
163
  /** Ring colour for an active (scroll-target) deleted block outline. */
159
164
  function deletedBlockOutlineActiveRing(colors) {
160
165
  return colors.deletedBlockOutlineActiveEmphasis === 'border' ? borderAccent(colors.deleteActiveColor) : bgSubtlerPressed(colors.deleteActiveColor);
@@ -10,8 +10,8 @@ import { isExtendedEnabled } from '../isExtendedEnabled';
10
10
  import { buildAddedCellOverlayRoundedStyle, buildAddedCellOverlayStyle, buildDeletedBlockNodeStyle, buildDeletedCellOverlayRoundedStyle, buildDeletedCellOverlayStyle, buildInsertedBlockNodeStyle } from './colorSchemes/factory';
11
11
  import { colorSchemeRegistry, getLegacyColorScheme } from './colorSchemes/schemes';
12
12
  import { createBlockIndicatorAnchorWidgets } from './createAnchorDecorationWidgets';
13
- import { createContributorTagWidget } from './createContributorTagWidget';
14
- import { buildDiffDecorationSpec } from './decorationKeys';
13
+ import { createContributorTagWidget, isContributorTagWidgetEnabled } from './createContributorTagWidget';
14
+ import { AnchorTypeKey, buildAnchorDecorationKey, buildDiffDecorationSpec } from './decorationKeys';
15
15
  import { getBlockNodeStyleLegacy, resolveCellOverlayStyleLegacy } from './createBlockChangedDecoration.styles.legacy';
16
16
  var displayNoneStyle = convertToInlineCss({
17
17
  display: 'none'
@@ -191,13 +191,20 @@ export var createBlockChangedDecoration = function createBlockChangedDecoration(
191
191
  from: change.from,
192
192
  name: change.name
193
193
  });
194
+ // Named so this block's own tag can position against it; see `createContributorTagWidget`.
195
+ var tagAnchorName = shouldTagBlock && isContributorTagWidgetEnabled() ? buildAnchorDecorationKey({
196
+ diffId: diffId,
197
+ anchorType: AnchorTypeKey.tag
198
+ }) : undefined;
194
199
  if (shouldHideDeleted) {
195
200
  return [Decoration.node(change.from, change.to, {
196
201
  style: displayNoneStyle
197
202
  }, buildDiffDecorationSpec({
203
+ colorScheme: colorScheme,
198
204
  decorationType: 'block',
199
205
  diffId: diffId,
200
206
  isActive: isActive,
207
+ isInserted: isInserted,
201
208
  nodeName: change.name,
202
209
  diffType: diffType
203
210
  }))];
@@ -227,13 +234,16 @@ export var createBlockChangedDecoration = function createBlockChangedDecoration(
227
234
  }));
228
235
  }
229
236
  // isInserted is only read under the extended experience, so pass it unconditionally.
230
- var style = getBlockNodeStyle({
237
+ var nodeStyle = getBlockNodeStyle({
231
238
  nodeName: change.name,
232
239
  colorScheme: colorScheme,
233
240
  isInserted: isInserted,
234
241
  isActive: isActive,
235
242
  diffType: diffType
236
243
  });
244
+ var style = tagAnchorName ? [nodeStyle, convertToInlineCss({
245
+ anchorName: "--".concat(tagAnchorName)
246
+ })].filter(Boolean).join(' ') : nodeStyle;
237
247
  var className = getNodeClass(change.name);
238
248
  if (style || className) {
239
249
  decorations.push(Decoration.node(change.from, change.to, _objectSpread({
@@ -271,6 +281,7 @@ export var createBlockChangedDecoration = function createBlockChangedDecoration(
271
281
  if (shouldTagBlock && doc) {
272
282
  var tagWidget = createContributorTagWidget({
273
283
  anchorAtRangeStart: true,
284
+ anchorName: tagAnchorName,
274
285
  doc: doc,
275
286
  from: change.from,
276
287
  to: change.to,
@@ -234,9 +234,11 @@ export var createChangedRowDecorationWidgets = function createChangedRowDecorati
234
234
  originalDoc.slice(changedRow.fromA, changedRow.toA));
235
235
  var diffId = crypto.randomUUID();
236
236
  return Decoration.widget(safeInsertPos, rowDOM, _objectSpread({}, buildDiffDecorationSpec({
237
+ colorScheme: colorScheme,
237
238
  decorationType: 'widget',
238
239
  diffId: diffId,
239
240
  isActive: false,
241
+ isInserted: isInserted,
240
242
  diffType: diffType
241
243
  })));
242
244
  });
@@ -32,13 +32,15 @@ export var isContributorTagWidgetEnabled = function isContributorTagWidgetEnable
32
32
  * (EDITOR-8702).
33
33
  */
34
34
  export var mountContributorTag = function mountContributorTag(_ref) {
35
- var diffId = _ref.diffId,
35
+ var anchorName = _ref.anchorName,
36
+ diffId = _ref.diffId,
36
37
  host = _ref.host,
37
38
  mountContext = _ref.mountContext;
38
39
  if (!mountContext) {
39
40
  return undefined;
40
41
  }
41
42
  var controller = new ContributorTagController(_objectSpread(_objectSpread({}, mountContext), {}, {
43
+ anchorName: anchorName,
42
44
  diffId: diffId
43
45
  }));
44
46
  controller.mount(host);
@@ -70,14 +72,25 @@ export var unmountContributorTag = function unmountContributorTag(mount) {
70
72
  /**
71
73
  * A zero-size inline host for one contributor tag. `position: relative` makes it the containing
72
74
  * block of the absolutely positioned tag, so the tag sits on the change with no measurement.
75
+ *
76
+ * An anchored tag takes a static host instead: an anchor is only acceptable when it sits inside the
77
+ * tag's containing block, and the block node is not inside this host. Measured in Chrome 153 — a
78
+ * relative host makes the `anchor()` insets resolve to `auto`.
73
79
  */
74
- var buildContributorTagHost = function buildContributorTagHost(diffId) {
80
+ var buildContributorTagHost = function buildContributorTagHost(diffId, anchorName) {
75
81
  var host = document.createElement('span');
76
82
  host.setAttribute(CONTRIBUTOR_TAG_HOST_ATTRIBUTE, diffId);
77
- host.style.setProperty('position', 'relative');
83
+ if (!anchorName) {
84
+ host.style.setProperty('position', 'relative');
85
+ }
78
86
  return host;
79
87
  };
80
88
 
89
+ /** Without anchor positioning the tag keeps its host-relative placement. */
90
+ var supportsAnchorPositioning = function supportsAnchorPositioning() {
91
+ return typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('bottom', 'anchor(--a top)');
92
+ };
93
+
81
94
  /**
82
95
  * The host for a tag on deleted content, which renders inside its own widget decoration rather than
83
96
  * needing one of its own. Unmount with `unmountContributorTag` from that widget's `destroy`.
@@ -177,6 +190,7 @@ var resolveCodeBlockStart = function resolveCodeBlockStart(doc, pos) {
177
190
  export var createContributorTagWidget = function createContributorTagWidget(_ref3) {
178
191
  var _ref3$anchorAtRangeSt = _ref3.anchorAtRangeStart,
179
192
  anchorAtRangeStart = _ref3$anchorAtRangeSt === void 0 ? false : _ref3$anchorAtRangeSt,
193
+ anchorName = _ref3.anchorName,
180
194
  doc = _ref3.doc,
181
195
  from = _ref3.from,
182
196
  to = _ref3.to,
@@ -185,6 +199,7 @@ export var createContributorTagWidget = function createContributorTagWidget(_ref
185
199
  if (!isContributorTagWidgetEnabled()) {
186
200
  return undefined;
187
201
  }
202
+ var tagAnchorName = anchorName && supportsAnchorPositioning() ? anchorName : undefined;
188
203
 
189
204
  // Reassigned when ProseMirror redraws this decoration, which it may do more than once for the
190
205
  // same `Decoration` instance.
@@ -195,8 +210,9 @@ export var createContributorTagWidget = function createContributorTagWidget(_ref
195
210
  return Decoration.widget(
196
211
  // Keep the host out of the table row's grid (EDITOR-8442).
197
212
  clampAnchorPosIntoCell(doc, codeBlockPos !== null && codeBlockPos !== void 0 ? codeBlockPos : anchorPos, 1), function () {
198
- var host = buildContributorTagHost(diffId);
213
+ var host = buildContributorTagHost(diffId, tagAnchorName);
199
214
  mount = mountContributorTag({
215
+ anchorName: tagAnchorName,
200
216
  diffId: diffId,
201
217
  host: host,
202
218
  mountContext: mountContext
@@ -77,8 +77,10 @@ var createTableCellContentWidgets = function createTableCellContentWidgets(_ref)
77
77
  // Skip findSafeInsertPos: it walks forward to a schema-valid cell-insert point
78
78
  // and pushes the widget into the next cell. `targetPos` is the correct spot.
79
79
  decorations.push(Decoration.widget(targetPos, dom, _objectSpread({}, buildDiffDecorationSpec(_objectSpread({
80
+ colorScheme: colorScheme,
80
81
  decorationType: 'widget',
81
82
  diffId: crypto.randomUUID(),
83
+ isInserted: isInserted,
82
84
  diffType: diffType
83
85
  }, isExtendedEnabled(diffType) && {
84
86
  side: -1
@@ -421,8 +423,10 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
421
423
  defaultSpacer.style.display = 'block';
422
424
  defaultSpacer.style.marginTop = "var(--ds-space-100, 8px)";
423
425
  decorations.push(Decoration.widget(safeInsertPos, defaultSpacer, _objectSpread({}, buildDiffDecorationSpec({
426
+ colorScheme: colorScheme,
424
427
  decorationType: 'widget',
425
428
  diffId: crypto.randomUUID(),
429
+ isInserted: isInserted,
426
430
  diffType: diffType
427
431
  }))));
428
432
  }
@@ -16,7 +16,9 @@ export var AnchorTypeKey = {
16
16
  from: 'from',
17
17
  to: 'to',
18
18
  left: 'left',
19
- docMargin: 'doc-margin'
19
+ docMargin: 'doc-margin',
20
+ /** The decorated block itself, which its contributor tag positions against. */
21
+ tag: 'tag'
20
22
  };
21
23
 
22
24
  /**
@@ -80,9 +82,9 @@ export var buildDiffDecorationSpec = function buildDiffDecorationSpec(_ref3) {
80
82
  attributionKey: attributionKey
81
83
  } : {}), attributionKey && isActive !== undefined ? {
82
84
  isActive: isActive
83
- } : {}), attributionKey && isInserted !== undefined ? {
85
+ } : {}), isInserted !== undefined ? {
84
86
  isInserted: isInserted
85
- } : {}), attributionKey && colorScheme ? {
87
+ } : {}), colorScheme ? {
86
88
  colorScheme: colorScheme
87
89
  } : {}), nodeName ? {
88
90
  nodeName: nodeName
@@ -138,9 +140,14 @@ export var isDiffDecoration = function isDiffDecoration(decoration) {
138
140
  export var extractDiffDescriptors = function extractDiffDescriptors(decorations) {
139
141
  return decorations.find(undefined, undefined, isDiffDecorationSpec).filter(isDiffDecoration).map(function (_ref5) {
140
142
  var spec = _ref5.spec;
141
- return {
142
- id: spec.diffId,
143
+ return _objectSpread(_objectSpread(_objectSpread({}, spec.colorScheme ? {
144
+ colorScheme: spec.colorScheme
145
+ } : {}), {}, {
146
+ id: spec.diffId
147
+ }, spec.isInserted !== undefined ? {
148
+ isInserted: spec.isInserted
149
+ } : {}), {}, {
143
150
  type: spec.decorationType
144
- };
151
+ });
145
152
  });
146
153
  };
@@ -179,10 +179,19 @@ var createSpan = function createSpan(doc, style) {
179
179
  *
180
180
  * Built element by element rather than through `DOMSerializer.renderSpec`, which returns `Node` and
181
181
  * would need a cast per reference below.
182
+ *
183
+ * `anchorName` is the `anchor-name` its block carries — see `createContributorTagWidget`.
182
184
  */
183
- export var buildContributorTagDom = function buildContributorTagDom(doc) {
185
+ export var buildContributorTagDom = function buildContributorTagDom(doc, anchorName) {
184
186
  var labelId = generateLabelId();
185
187
  var root = createSpan(doc, constraintStyle);
188
+ if (anchorName) {
189
+ // Anchored to the block's own box, so the tag clears whatever margin its node type has — the
190
+ // same `anchor()` insets the `IndicatorBar` uses. A browser without anchor positioning rejects
191
+ // these values and keeps the `bottom: 100%` fallback above.
192
+ root.style.setProperty('bottom', "anchor(--".concat(anchorName, " top)"));
193
+ root.style.setProperty('inset-inline-start', "anchor(--".concat(anchorName, " left)"));
194
+ }
186
195
  var tag = createSpan(doc, tagStyle, {
187
196
  class: CONTRIBUTOR_TAG_CLASS,
188
197
  'data-testid': CONTRIBUTOR_TAG_TESTID,
@@ -179,7 +179,7 @@ export var ContributorTagController = /*#__PURE__*/function () {
179
179
  return;
180
180
  }
181
181
  if (!this.dom) {
182
- this.dom = buildContributorTagDom(host.ownerDocument);
182
+ this.dom = buildContributorTagDom(host.ownerDocument, this.options.anchorName);
183
183
  this.bindTag(this.dom);
184
184
  host.appendChild(this.dom.root);
185
185
  }
@@ -2,7 +2,6 @@
2
2
  ._18u0mgjw{margin-left:var(--ds-space-negative-200,-1pc)}
3
3
  ._19pk196n{margin-top:var(--ds-space-negative-075,-6px)}
4
4
  ._1bsb1l7b{width:3px}
5
- ._bfhkvbh5{background-color:var(--ds-border-information,#357de8)}
6
5
  ._kqswstnw{position:absolute}
7
6
  ._lcxvglyw{pointer-events:none}
8
7
  ._otyr196n{margin-bottom:var(--ds-space-negative-075,-6px)}
@@ -2,24 +2,35 @@
2
2
  import "./IndicatorBar.compiled.css";
3
3
  import * as React from 'react';
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
+ import { getAccentBorderColor } from '../../pm-plugins/decorations/colorSchemes/factory';
6
+ import { colorSchemeRegistry } from '../../pm-plugins/decorations/colorSchemes/schemes';
5
7
  import { AnchorDocMarginKey } from '../../pm-plugins/decorations/decorationKeys';
6
8
  var barStyles = null;
9
+ var getIndicatorColor = function getIndicatorColor(colorScheme, isInserted) {
10
+ if (colorScheme === undefined || colorScheme === 'standard' || colorScheme === 'traditional' || isInserted === undefined) {
11
+ return "var(--ds-border-information, #357DE8)";
12
+ }
13
+ return getAccentBorderColor(colorSchemeRegistry[colorScheme][isInserted ? 'insertColor' : 'deleteColor']);
14
+ };
7
15
  export function IndicatorBar(_ref) {
8
16
  var anchorTop = _ref.anchorTop,
9
17
  anchorBottom = _ref.anchorBottom,
10
18
  anchorLeft = _ref.anchorLeft,
11
- anchorLeftFallback = _ref.anchorLeftFallback;
19
+ anchorLeftFallback = _ref.anchorLeftFallback,
20
+ colorScheme = _ref.colorScheme,
21
+ isInserted = _ref.isInserted;
12
22
  var leftAnchors = [anchorLeft, anchorLeftFallback, AnchorDocMarginKey].filter(Boolean).map(function (anchor) {
13
23
  return "anchor(--".concat(anchor, " left)");
14
24
  }).join(', ');
15
25
  return /*#__PURE__*/React.createElement("span", {
16
26
  "data-testid": "diff-indicator-bar",
17
27
  style: {
28
+ backgroundColor: getIndicatorColor(colorScheme, isInserted),
18
29
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values
19
30
  left: "min(".concat(leftAnchors, ")"),
20
31
  top: "anchor(--".concat(anchorTop, " top)"),
21
32
  bottom: "anchor(--".concat(anchorBottom, " bottom)")
22
33
  },
23
- className: ax(["_2rko1rr0 _kqswstnw _1bsb1l7b _bfhkvbh5 _lcxvglyw _18u0mgjw _17c61i8t _19pk196n _otyr196n"])
34
+ className: ax(["_2rko1rr0 _kqswstnw _1bsb1l7b _lcxvglyw _18u0mgjw _17c61i8t _19pk196n _otyr196n"])
24
35
  });
25
36
  }
@@ -10,6 +10,8 @@ var renderIndicatorBar = function renderIndicatorBar(descriptor) {
10
10
  {
11
11
  return /*#__PURE__*/React.createElement(IndicatorBar, {
12
12
  key: descriptor.id,
13
+ colorScheme: descriptor.colorScheme,
14
+ isInserted: descriptor.isInserted,
13
15
  anchorTop: buildAnchorDecorationKey({
14
16
  diffId: descriptor.id,
15
17
  anchorType: AnchorTypeKey.from
@@ -34,6 +36,8 @@ var renderIndicatorBar = function renderIndicatorBar(descriptor) {
34
36
  });
35
37
  return /*#__PURE__*/React.createElement(IndicatorBar, {
36
38
  key: descriptor.id,
39
+ colorScheme: descriptor.colorScheme,
40
+ isInserted: descriptor.isInserted,
37
41
  anchorTop: blockAnchor,
38
42
  anchorBottom: blockAnchor,
39
43
  anchorLeft: buildAnchorDecorationKey({
@@ -46,6 +50,8 @@ var renderIndicatorBar = function renderIndicatorBar(descriptor) {
46
50
  {
47
51
  return /*#__PURE__*/React.createElement(IndicatorBar, {
48
52
  key: descriptor.id,
53
+ colorScheme: descriptor.colorScheme,
54
+ isInserted: descriptor.isInserted,
49
55
  anchorTop: buildAnchorDecorationKey({
50
56
  diffId: descriptor.id
51
57
  }),
@@ -24,6 +24,8 @@ export declare function getAccentTokens(color: AdsAccentColor): {
24
24
  background: string;
25
25
  text: string;
26
26
  };
27
+ /** Border token for a contributor accent, used by the diff indicator. */
28
+ export declare function getAccentBorderColor(color: AdsAccentColor): string;
27
29
  /** Inline inserted content — default state. */
28
30
  export declare function buildInsertStyle(colors: DiffColorScheme): string;
29
31
  /** Inline insert style for content within a block node (no background, underline only). */
@@ -28,7 +28,8 @@ export type ContributorTagMount = {
28
28
  * destroying the one it replaces, the outgoing `destroy` cannot take the incoming tag down with it
29
29
  * (EDITOR-8702).
30
30
  */
31
- export declare const mountContributorTag: ({ diffId, host, mountContext, }: {
31
+ export declare const mountContributorTag: ({ anchorName, diffId, host, mountContext, }: {
32
+ anchorName?: string;
32
33
  diffId: string;
33
34
  host: HTMLElement;
34
35
  mountContext: ContributorTagMountContext | undefined;
@@ -50,12 +51,18 @@ export declare const createContributorTagHost: (diffId: string, mountContext?: C
50
51
  * The tag is mounted in `toDOM` and taken down in `destroy`, so it lives exactly as long as the host
51
52
  * element ProseMirror drew it into — see `mountContributorTag`.
52
53
  */
53
- export declare const createContributorTagWidget: ({ anchorAtRangeStart, doc, from, to, diffId, mountContext, }: {
54
+ export declare const createContributorTagWidget: ({ anchorAtRangeStart, anchorName, doc, from, to, diffId, mountContext, }: {
54
55
  /**
55
56
  * Anchor the host at `from` instead of the range's first inline content, so a block range hosts
56
57
  * its tag outside the node rather than inside its content DOM.
57
58
  */
58
59
  anchorAtRangeStart?: boolean;
60
+ /**
61
+ * The `anchor-name` the block's own decoration carries, so the tag positions against that box
62
+ * rather than against its host — the block's margin and padding are then the browser's to
63
+ * resolve, and the tag sits on the same corner for every node type (EDITOR-8933).
64
+ */
65
+ anchorName?: string;
59
66
  diffId: string;
60
67
  doc: PMNode;
61
68
  from: number;
@@ -46,6 +46,8 @@ export declare const AnchorTypeKey: {
46
46
  readonly to: 'to';
47
47
  readonly left: 'left';
48
48
  readonly docMargin: 'doc-margin';
49
+ /** The decorated block itself, which its contributor tag positions against. */
50
+ readonly tag: 'tag';
49
51
  };
50
52
  export type InlineAnchorType = Exclude<(typeof AnchorTypeKey)[keyof typeof AnchorTypeKey], typeof AnchorTypeKey.docMargin>;
51
53
  export type AnchorDecorationSpec = (BaseDecorationSpec<typeof DecorationFamily.anchor> & {
@@ -103,7 +103,9 @@ export type DeletedDiffWidget = {
103
103
  };
104
104
  export type SmartDiffThresholds = SmartDiffThresholdsInternal;
105
105
  export type DiffDescriptor = {
106
+ colorScheme?: ColorScheme;
106
107
  id: string;
108
+ isInserted?: boolean;
107
109
  type: 'inline' | 'block' | 'widget';
108
110
  };
109
111
  /**
@@ -60,5 +60,7 @@ export type ContributorTagDom = {
60
60
  *
61
61
  * Built element by element rather than through `DOMSerializer.renderSpec`, which returns `Node` and
62
62
  * would need a cast per reference below.
63
+ *
64
+ * `anchorName` is the `anchor-name` its block carries — see `createContributorTagWidget`.
63
65
  */
64
- export declare const buildContributorTagDom: (doc: Document) => ContributorTagDom;
66
+ export declare const buildContributorTagDom: (doc: Document, anchorName?: string) => ContributorTagDom;
@@ -2,6 +2,8 @@ import type { IntlShape } from 'react-intl';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
3
  import type { ShowDiffPlugin } from '../../showDiffPluginType';
4
4
  export type ContributorTagControllerOptions = {
5
+ /** The `anchor-name` of the block this tag captions, when it has one. */
6
+ anchorName?: string;
5
7
  api?: ExtractInjectionAPI<ShowDiffPlugin>;
6
8
  diffId: string;
7
9
  /**
@@ -2,9 +2,12 @@
2
2
  * @jsxRuntime classic
3
3
  * @jsx jsx
4
4
  */
5
- export declare function IndicatorBar({ anchorTop, anchorBottom, anchorLeft, anchorLeftFallback, }: {
5
+ import type { ColorScheme } from '../../pm-plugins/decorations/colorSchemes/types';
6
+ export declare function IndicatorBar({ anchorTop, anchorBottom, anchorLeft, anchorLeftFallback, colorScheme, isInserted, }: {
6
7
  anchorBottom: string;
7
8
  anchorLeft: string;
8
9
  anchorLeftFallback?: string;
9
10
  anchorTop: string;
11
+ colorScheme?: ColorScheme;
12
+ isInserted?: boolean;
10
13
  }): JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "16.0.7",
3
+ "version": "16.0.9",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -64,7 +64,7 @@
64
64
  "react-intl": "^7.0.0"
65
65
  },
66
66
  "peerDependencies": {
67
- "@atlaskit/editor-common": "^122.3.0",
67
+ "@atlaskit/editor-common": "^122.5.0",
68
68
  "react": "^18.2.0 || ^19.2.0",
69
69
  "react-dom": "^18.2.0 || ^19.2.0",
70
70
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"