@atlaskit/renderer 148.0.4 → 149.0.0

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 (62) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/cjs/actions/index.js +35 -3
  3. package/dist/cjs/entry-points/expand-body.js +12 -0
  4. package/dist/cjs/react/index.js +8 -0
  5. package/dist/cjs/react/marks/annotation.js +3 -1
  6. package/dist/cjs/react/nodes/extension-comments.compiled.css +4 -0
  7. package/dist/cjs/react/nodes/extension-comments.js +144 -0
  8. package/dist/cjs/react/nodes/extension.js +86 -11
  9. package/dist/cjs/steps/index.js +23 -0
  10. package/dist/cjs/ui/Expand.js +10 -0
  11. package/dist/cjs/ui/Renderer/index.js +1 -1
  12. package/dist/cjs/ui/annotations/contexts/AnnotationRangeContext.js +29 -11
  13. package/dist/cjs/ui/annotations/element/extension.compiled.css +6 -0
  14. package/dist/cjs/ui/annotations/element/extension.js +34 -0
  15. package/dist/cjs/ui/annotations/element/index.js +4 -2
  16. package/dist/cjs/ui/annotations/element/mark.js +11 -1
  17. package/dist/cjs/ui/annotations/index.js +2 -1
  18. package/dist/es2019/actions/index.js +33 -3
  19. package/dist/es2019/entry-points/expand-body.js +3 -0
  20. package/dist/es2019/react/index.js +8 -0
  21. package/dist/es2019/react/marks/annotation.js +3 -1
  22. package/dist/es2019/react/nodes/extension-comments.compiled.css +4 -0
  23. package/dist/es2019/react/nodes/extension-comments.js +115 -0
  24. package/dist/es2019/react/nodes/extension.js +83 -7
  25. package/dist/es2019/steps/index.js +25 -0
  26. package/dist/es2019/ui/Expand.js +10 -0
  27. package/dist/es2019/ui/Renderer/index.js +1 -1
  28. package/dist/es2019/ui/annotations/contexts/AnnotationRangeContext.js +28 -12
  29. package/dist/es2019/ui/annotations/element/extension.compiled.css +6 -0
  30. package/dist/es2019/ui/annotations/element/extension.js +26 -0
  31. package/dist/es2019/ui/annotations/element/index.js +4 -2
  32. package/dist/es2019/ui/annotations/element/mark.js +11 -1
  33. package/dist/es2019/ui/annotations/index.js +2 -1
  34. package/dist/esm/actions/index.js +35 -3
  35. package/dist/esm/entry-points/expand-body.js +3 -0
  36. package/dist/esm/react/index.js +8 -0
  37. package/dist/esm/react/marks/annotation.js +3 -1
  38. package/dist/esm/react/nodes/extension-comments.compiled.css +4 -0
  39. package/dist/esm/react/nodes/extension-comments.js +135 -0
  40. package/dist/esm/react/nodes/extension.js +86 -7
  41. package/dist/esm/steps/index.js +23 -0
  42. package/dist/esm/ui/Expand.js +10 -0
  43. package/dist/esm/ui/Renderer/index.js +1 -1
  44. package/dist/esm/ui/annotations/contexts/AnnotationRangeContext.js +30 -12
  45. package/dist/esm/ui/annotations/element/extension.compiled.css +6 -0
  46. package/dist/esm/ui/annotations/element/extension.js +27 -0
  47. package/dist/esm/ui/annotations/element/index.js +4 -2
  48. package/dist/esm/ui/annotations/element/mark.js +11 -1
  49. package/dist/esm/ui/annotations/index.js +2 -1
  50. package/dist/types/actions/index.d.ts +13 -0
  51. package/dist/types/entry-points/expand-body.d.ts +2 -0
  52. package/dist/types/react/marks/annotation.d.ts +1 -1
  53. package/dist/types/react/nodes/extension-comments.d.ts +17 -0
  54. package/dist/types/react/nodes/extension.d.ts +12 -0
  55. package/dist/types/react/types.d.ts +1 -0
  56. package/dist/types/ui/annotations/contexts/AnnotationRangeContext.d.ts +4 -2
  57. package/dist/types/ui/annotations/element/extension.d.ts +17 -0
  58. package/dist/types/ui/annotations/element/index.d.ts +2 -1
  59. package/dist/types/ui/annotations/element/mark.d.ts +3 -107
  60. package/dist/types/ui/utils/expand-body.d.ts +5 -3
  61. package/expand-body/package.json +10 -0
  62. package/package.json +5 -5
@@ -9,6 +9,7 @@ exports.useAnnotationRangeState = exports.useAnnotationRangeDispatch = exports.A
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
11
  var _react = _interopRequireWildcard(require("react"));
12
+ var _fg = require("@atlaskit/platform-feature-flags/fg");
12
13
  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); }
13
14
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
14
15
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -121,7 +122,9 @@ var AnnotationRangeDispatchContext = exports.AnnotationRangeDispatchContext = /*
121
122
  });
122
123
  var AnnotationRangeProviderInner = exports.AnnotationRangeProviderInner = function AnnotationRangeProviderInner(_ref) {
123
124
  var children = _ref.children,
124
- allowCommentsOnMedia = _ref.allowCommentsOnMedia;
125
+ allowCommentsOnMedia = _ref.allowCommentsOnMedia,
126
+ hasBlockNodeSupport = _ref.hasBlockNodeSupport;
127
+ var extensionCommentsEnabled = !!hasBlockNodeSupport && (0, _fg.fg)('cc_maui_annotations_on_extensions');
125
128
  var _useReducer = (0, _react.useReducer)(reducer, initialState),
126
129
  _useReducer2 = (0, _slicedToArray2.default)(_useReducer, 2),
127
130
  _useReducer2$ = _useReducer2[0],
@@ -149,21 +152,34 @@ var AnnotationRangeProviderInner = exports.AnnotationRangeProviderInner = functi
149
152
  range: range
150
153
  });
151
154
  }, []);
155
+ var hoverDraftRangeRef = (0, _react.useRef)(hoverDraftRange);
156
+ hoverDraftRangeRef.current = hoverDraftRange;
152
157
  var setHoverTarget = (0, _react.useCallback)(function (target) {
153
- // the HoverComponent expects an element deeply nested inside media, these classes work with the current implementation
154
- var mediaNode = target.querySelector('.media-card-inline-player, .media-file-card-view');
155
- if (!mediaNode) {
158
+ var draftRange = hoverDraftRangeRef.current;
159
+ var draftTarget = draftRange === null || draftRange === void 0 ? void 0 : draftRange.startContainer.childNodes[draftRange.startOffset];
160
+ if (extensionCommentsEnabled && draftTarget instanceof HTMLElement && draftTarget.matches('.ak-renderer-extension[data-inline-comments-target="true"]')) {
161
+ // Keep the composer callbacks bound to the chart chosen when its draft opened.
156
162
  return;
157
163
  }
158
- // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- range for media hover highlight
164
+ // The HoverComponent expects an element deeply nested inside media or the eligible
165
+ // block node's wrapper. Each selector is only included when its comment type is
166
+ // supported, so hover targeting never activates on an ineligible block node.
167
+ var selectors = [allowCommentsOnMedia && '.media-card-inline-player, .media-file-card-view', extensionCommentsEnabled && '.ak-renderer-extension[data-inline-comments-target="true"]'].filter(function (selector) {
168
+ return Boolean(selector);
169
+ }).join(', ');
170
+ var hoverableNode = extensionCommentsEnabled && target.matches('.ak-renderer-extension[data-inline-comments-target="true"]') ? target : selectors ? target.querySelector(selectors) : null;
171
+ if (!hoverableNode) {
172
+ return;
173
+ }
174
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- range for media/extension hover highlight
159
175
  var range = document.createRange();
160
- range.setStartBefore(mediaNode);
161
- range.setEndAfter(mediaNode);
176
+ range.setStartBefore(hoverableNode);
177
+ range.setEndAfter(hoverableNode);
162
178
  dispatch({
163
179
  type: 'setHover',
164
180
  range: range
165
181
  });
166
- }, []);
182
+ }, [allowCommentsOnMedia, extensionCommentsEnabled]);
167
183
  var promoteSelectionToDraft = (0, _react.useCallback)(function (position) {
168
184
  dispatch({
169
185
  type: 'promoteSelectionToDraft',
@@ -203,13 +219,13 @@ var AnnotationRangeProviderInner = exports.AnnotationRangeProviderInner = functi
203
219
  clearSelectionRange: clearSelectionRange,
204
220
  clearHoverRange: clearHoverRange,
205
221
  setSelectionRange: setSelectionRange,
206
- setHoverTarget: !!allowCommentsOnMedia ? setHoverTarget : undefined,
222
+ setHoverTarget: allowCommentsOnMedia || extensionCommentsEnabled ? setHoverTarget : undefined,
207
223
  promoteSelectionToDraft: promoteSelectionToDraft,
208
224
  promoteHoverToDraft: promoteHoverToDraft,
209
225
  clearSelectionDraft: clearSelectionDraft,
210
226
  clearHoverDraft: clearHoverDraft
211
227
  };
212
- }, [allowCommentsOnMedia, clearSelectionRange, clearHoverRange, setSelectionRange, setHoverTarget, promoteSelectionToDraft, promoteHoverToDraft, clearSelectionDraft, clearHoverDraft]);
228
+ }, [allowCommentsOnMedia, extensionCommentsEnabled, clearSelectionRange, clearHoverRange, setSelectionRange, setHoverTarget, promoteSelectionToDraft, promoteHoverToDraft, clearSelectionDraft, clearHoverDraft]);
213
229
  return /*#__PURE__*/_react.default.createElement(AnnotationRangeStateContext.Provider, {
214
230
  value: stateData
215
231
  }, /*#__PURE__*/_react.default.createElement(AnnotationRangeDispatchContext.Provider, {
@@ -219,6 +235,7 @@ var AnnotationRangeProviderInner = exports.AnnotationRangeProviderInner = functi
219
235
  var AnnotationRangeProvider = exports.AnnotationRangeProvider = function AnnotationRangeProvider(_ref2) {
220
236
  var children = _ref2.children,
221
237
  allowCommentsOnMedia = _ref2.allowCommentsOnMedia,
238
+ hasBlockNodeSupport = _ref2.hasBlockNodeSupport,
222
239
  isNestedRender = _ref2.isNestedRender;
223
240
  /*
224
241
  * If this is a nested render, we do not provide the context
@@ -226,7 +243,8 @@ var AnnotationRangeProvider = exports.AnnotationRangeProvider = function Annotat
226
243
  * and we need the original context to create annotations on extensions.
227
244
  */
228
245
  return isNestedRender ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, children) : /*#__PURE__*/_react.default.createElement(AnnotationRangeProviderInner, {
229
- allowCommentsOnMedia: allowCommentsOnMedia
246
+ allowCommentsOnMedia: allowCommentsOnMedia,
247
+ hasBlockNodeSupport: hasBlockNodeSupport
230
248
  }, children);
231
249
  };
232
250
  var useAnnotationRangeState = exports.useAnnotationRangeState = function useAnnotationRangeState() {
@@ -0,0 +1,6 @@
1
+ ._154iidpf{top:0}
2
+ ._1ltvidpf{left:0}
3
+ ._1xi2idpf{right:0}
4
+ ._94n5idpf{bottom:0}
5
+ ._kqswstnw{position:absolute}
6
+ ._lcxvglyw{pointer-events:none}
@@ -0,0 +1,34 @@
1
+ /* extension.tsx generated by @compiled/babel-plugin v3.0.2 */
2
+ "use strict";
3
+
4
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.ExtensionAnnotation = void 0;
9
+ require("./extension.compiled.css");
10
+ var _runtime = require("@compiled/react/runtime");
11
+ var _react = _interopRequireDefault(require("react"));
12
+ var styles = {
13
+ anchor: "_kqswstnw _154iidpf _1xi2idpf _94n5idpf _1ltvidpf _lcxvglyw"
14
+ };
15
+
16
+ /** Full-chart anchors for comment navigation, without wrapping or intercepting the iframe. */
17
+ var ExtensionAnnotation = exports.ExtensionAnnotation = function ExtensionAnnotation(_ref) {
18
+ var id = _ref.id,
19
+ dataAttributes = _ref.dataAttributes,
20
+ markRef = _ref.markRef;
21
+ return /*#__PURE__*/_react.default.createElement("div", {
22
+ id: id,
23
+ ref: markRef,
24
+ "data-id": dataAttributes['data-id'],
25
+ "data-mark-type": dataAttributes['data-mark-type'],
26
+ "data-mark-annotation-type": dataAttributes['data-mark-annotation-type'],
27
+ "data-mark-annotation-state": dataAttributes['data-mark-annotation-state'],
28
+ "data-block-mark": dataAttributes['data-block-mark'],
29
+ "data-renderer-mark": dataAttributes['data-renderer-mark'],
30
+ "data-has-focus": dataAttributes['data-has-focus'],
31
+ "data-is-hovered": dataAttributes['data-is-hovered'],
32
+ className: (0, _runtime.ax)([styles.anchor])
33
+ });
34
+ };
@@ -20,7 +20,8 @@ var MarkElement = exports.MarkElement = function MarkElement(_ref) {
20
20
  children = _ref.children,
21
21
  dataAttributes = _ref.dataAttributes,
22
22
  id = _ref.id,
23
- useBlockLevel = _ref.useBlockLevel;
23
+ useBlockLevel = _ref.useBlockLevel,
24
+ isExtension = _ref.isExtension;
24
25
  var updateSubscriber = (0, _useInlineCommentSubscriber.useInlineCommentSubscriberContext)();
25
26
  var states = (0, _react.useContext)(_context.InlineCommentsStateContext);
26
27
  var _useHasFocusEvent = (0, _useEvents.useHasFocusEvent)({
@@ -98,6 +99,7 @@ var MarkElement = exports.MarkElement = function MarkElement(_ref) {
98
99
  hasFocus: hasFocus,
99
100
  isHovered: isHovered,
100
101
  state: states[id],
101
- useBlockLevel: useBlockLevel
102
+ useBlockLevel: useBlockLevel,
103
+ isExtension: isExtension
102
104
  }, children);
103
105
  };
@@ -14,6 +14,7 @@ var _annotation = require("@atlaskit/adf-schema/annotation");
14
14
  var _fg = require("@atlaskit/platform-feature-flags/fg");
15
15
  var _messages = require("../../../messages");
16
16
  var _AnnotationManagerContext = require("../contexts/AnnotationManagerContext");
17
+ var _extension = require("./extension");
17
18
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
18
19
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /**
19
20
  * @jsxRuntime classic
@@ -123,7 +124,8 @@ var MarkComponent = exports.MarkComponent = function MarkComponent(_ref) {
123
124
  hasFocus = _ref.hasFocus,
124
125
  isHovered = _ref.isHovered,
125
126
  onClick = _ref.onClick,
126
- useBlockLevel = _ref.useBlockLevel;
127
+ useBlockLevel = _ref.useBlockLevel,
128
+ isExtension = _ref.isExtension;
127
129
  var intl = (0, _reactIntl.useIntl)();
128
130
  var annotationIds = (0, _react.useMemo)(function () {
129
131
  return (0, _toConsumableArray2.default)(new Set([].concat((0, _toConsumableArray2.default)(annotationParentIds), [id])));
@@ -213,6 +215,14 @@ var MarkComponent = exports.MarkComponent = function MarkComponent(_ref) {
213
215
  } : _objectSpread({
214
216
  'aria-details': annotationIds.join(', ')
215
217
  }, desktopAccessibilityAttributes);
218
+ if (isExtension && (0, _fg.fg)('cc_maui_annotations_on_extensions')) {
219
+ // The chart's badge owns interaction. Anchors only supply geometry and annotation state.
220
+ return (0, _react2.jsx)(_extension.ExtensionAnnotation, {
221
+ id: id,
222
+ dataAttributes: overriddenData,
223
+ markRef: id === currentSelectedAnnotationId ? markRef : undefined
224
+ });
225
+ }
216
226
  return (0, _react2.jsx)(useBlockLevel ? 'div' : 'mark', _objectSpread(_objectSpread(_objectSpread((0, _defineProperty2.default)({
217
227
  ref: id === currentSelectedAnnotationId ? markRef : undefined,
218
228
  id: id
@@ -37,7 +37,7 @@ var AnnotationsPositionContext = exports.AnnotationsPositionContext = /*#__PURE_
37
37
  startPos: 1
38
38
  });
39
39
  var AnnotationsWrapperInner = exports.AnnotationsWrapperInner = function AnnotationsWrapperInner(props) {
40
- var _providers$inlineComm, _providers$inlineComm2;
40
+ var _providers$inlineComm, _providers$inlineComm2, _providers$inlineComm3;
41
41
  var children = props.children,
42
42
  rendererRef = props.rendererRef,
43
43
  adfDocument = props.adfDocument,
@@ -55,6 +55,7 @@ var AnnotationsWrapperInner = exports.AnnotationsWrapperInner = function Annotat
55
55
  value: inlineCommentAnnotationsState
56
56
  }, /*#__PURE__*/_react.default.createElement(_AnnotationRangeContext.AnnotationRangeProvider, {
57
57
  allowCommentsOnMedia: (_providers$inlineComm = providers === null || providers === void 0 || (_providers$inlineComm2 = providers.inlineComment) === null || _providers$inlineComm2 === void 0 ? void 0 : _providers$inlineComm2.allowCommentsOnMedia) !== null && _providers$inlineComm !== void 0 ? _providers$inlineComm : false,
58
+ hasBlockNodeSupport: Boolean(providers === null || providers === void 0 || (_providers$inlineComm3 = providers.inlineComment) === null || _providers$inlineComm3 === void 0 ? void 0 : _providers$inlineComm3.isBlockNodeSupported),
58
59
  isNestedRender: isNestedRender
59
60
  }, /*#__PURE__*/_react.default.createElement(_AnnotationHoverContext.AnnotationHoverContext, null, /*#__PURE__*/_react.default.createElement(_wrapper.AnnotationsContextWrapper, {
60
61
  createAnalyticsEvent: createAnalyticsEvent,
@@ -75,9 +75,10 @@ export default class RendererActions {
75
75
  let to;
76
76
  let nodePos;
77
77
  let step;
78
+ const schema = this.schema;
78
79
  this.doc.descendants((node, pos) => {
79
80
  const found = mark.isInSet(node.marks);
80
- if (found && node.type.name === 'media') {
81
+ if (found && isBlockAnnotationTarget(node, schema)) {
81
82
  nodePos = pos;
82
83
  }
83
84
  if (found && !from) {
@@ -169,7 +170,6 @@ export default class RendererActions {
169
170
  }
170
171
  isRangeAnnotatable(range) {
171
172
  try {
172
- var _startContainer$paren, _endContainer$parentE;
173
173
  if (!range) {
174
174
  return false;
175
175
  }
@@ -177,7 +177,7 @@ export default class RendererActions {
177
177
  startContainer,
178
178
  endContainer
179
179
  } = range;
180
- if ((_startContainer$paren = startContainer.parentElement) !== null && _startContainer$paren !== void 0 && _startContainer$paren.closest('.ak-renderer-extension') || (_endContainer$parentE = endContainer.parentElement) !== null && _endContainer$parentE !== void 0 && _endContainer$parentE.closest('.ak-renderer-extension')) {
180
+ if (this.isInsideNestedExtensionRenderer(startContainer) || this.isInsideNestedExtensionRenderer(endContainer)) {
181
181
  return false;
182
182
  }
183
183
  return this.isValidAnnotationRange(range);
@@ -193,6 +193,36 @@ export default class RendererActions {
193
193
  }
194
194
  }
195
195
 
196
+ /**
197
+ * A range only threatens a nested renderer's own document (e.g. a bodiedExtension's macro
198
+ * body, which renders its own nested `<Renderer>`) when it sits inside that nested renderer's
199
+ * `.ak-renderer-document`. Landing directly on the extension's own outer wrapper -- with no
200
+ * nested document in between -- must stay annotatable so the whole chart/extension can be
201
+ * targeted (MAUI-1256), rather than being rejected outright the way any range touching
202
+ * `.ak-renderer-extension` previously was.
203
+ *
204
+ * Behind `cc_maui_annotations_on_extensions` -- the same gate MAUI-1255 added for extension
205
+ * node-mark support -- to preserve the previous behaviour (reject anything touching
206
+ * `.ak-renderer-extension`) while the feature is off.
207
+ */
208
+ isInsideNestedExtensionRenderer(container) {
209
+ // Preserve the legacy parent-based rejection while the gate is disabled.
210
+ if (!fg('cc_maui_annotations_on_extensions')) {
211
+ var _container$parentElem;
212
+ return !!((_container$parentElem = container.parentElement) !== null && _container$parentElem !== void 0 && _container$parentElem.closest('.ak-renderer-extension'));
213
+ }
214
+ const element = container instanceof Element ? container : container.parentElement;
215
+ if (!element) {
216
+ return false;
217
+ }
218
+ const extensionAncestor = element.closest('.ak-renderer-extension');
219
+ if (!extensionAncestor) {
220
+ return false;
221
+ }
222
+ const nestedDocument = element.closest('.ak-renderer-document');
223
+ return !!nestedDocument && extensionAncestor.contains(nestedDocument);
224
+ }
225
+
196
226
  // eslint-disable-next-line @repo/internal/deprecations/deprecation-ticket-required -- Ignored via go/ED-25883
197
227
  /**
198
228
  * This is replaced by `isRangeAnnotatable`.
@@ -0,0 +1,3 @@
1
+ /* eslint-disable @atlaskit/editor/no-re-export */
2
+
3
+ export { ExpandBodyProvider } from '../ui/utils/expand-body';
@@ -85,6 +85,11 @@ export default class ReactSerializer {
85
85
  if (shouldSkipLinkMark(mark) || shouldSkipBorderMark(mark)) {
86
86
  return content;
87
87
  }
88
+ // Extension annotations are sibling anchors inside the stable extension shell.
89
+ // Wrapping the extension here would remount its iframe whenever marks change.
90
+ if (node.type.name === 'extension' && mark.type.name === 'annotation' && this.allowAnnotations && fg('cc_maui_annotations_on_extensions')) {
91
+ return content;
92
+ }
88
93
  return this.renderMark(markToReact(mark), this.withMediaMarkProps(node, mark, this.getMarkProps(mark, [], node)), `${mark.type.name}-${index}`, content);
89
94
  }, serializedContent);
90
95
  return withExpandBodyBlock(node, currentPath, index, serialized);
@@ -503,6 +508,9 @@ export default class ReactSerializer {
503
508
  var _this$getExtensionHei;
504
509
  return {
505
510
  ...this.getProps(node, path),
511
+ allowAnnotations: this.allowAnnotations,
512
+ allowAnnotationsDraftMode: this.surroundTextNodesWithTextWrapper,
513
+ extensionNode: node,
506
514
  extensionViewportSizes: this.extensionViewportSizes,
507
515
  hideExtensionKeysWhilePending: this.hideExtensionKeysWhilePending,
508
516
  nodeHeight: (_this$getExtensionHei = this.getExtensionHeight) === null || _this$getExtensionHei === void 0 ? void 0 : _this$getExtensionHei.call(this, node),
@@ -13,6 +13,7 @@ const AnnotationComponent = ({
13
13
  annotationParentIds = [],
14
14
  allowAnnotations,
15
15
  useBlockLevel,
16
+ isExtension,
16
17
  isMediaInline
17
18
  }) => {
18
19
  const data = {
@@ -36,7 +37,8 @@ const AnnotationComponent = ({
36
37
  dataAttributes: data,
37
38
  annotationParentIds: annotationParentIds,
38
39
  annotationType: annotationType,
39
- useBlockLevel: useBlockLevel
40
+ useBlockLevel: useBlockLevel,
41
+ isExtension: isExtension
40
42
  }, children);
41
43
  }
42
44
  return (
@@ -0,0 +1,4 @@
1
+ ._154iu2gc{top:var(--ds-space-100,8px)}
2
+ ._1xi2u2gc{right:var(--ds-space-100,8px)}
3
+ ._kqswh2mm{position:relative}
4
+ ._kqswstnw{position:absolute}
@@ -0,0 +1,115 @@
1
+ /* extension-comments.tsx generated by @compiled/babel-plugin v3.0.2 */
2
+ import "./extension-comments.compiled.css";
3
+ import { ax, ix } from "@compiled/react/runtime";
4
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
5
+ import { AnnotationMarkStates, AnnotationTypes } from '@atlaskit/adf-schema/annotation';
6
+ import { VIEW_METHOD } from '@atlaskit/editor-common/analytics';
7
+ import { CommentBadgeNext } from '@atlaskit/editor-common/media-single';
8
+ import { AnnotationUpdateEvent } from '@atlaskit/editor-common/types';
9
+ import { useAnnotationManagerState } from '../../ui/annotations/contexts/AnnotationManagerContext';
10
+ import { useAnnotationRangeState } from '../../ui/annotations/contexts/AnnotationRangeContext';
11
+ import { useInlineCommentSubscriberContext } from '../../ui/annotations/hooks/use-inline-comment-subscriber';
12
+ import { useInlineCommentsFilter } from '../../ui/annotations/hooks/use-inline-comments-filter';
13
+ import AnnotationComponent from '../marks/annotation';
14
+ const containerStyles = null;
15
+ const annotationParentIds = [];
16
+ const annotationDataAttributes = {
17
+ 'data-renderer-mark': true,
18
+ 'data-block-mark': true
19
+ };
20
+ const badgeStyles = null;
21
+ const ExtensionCommentsEnabled = ({
22
+ marks,
23
+ children,
24
+ enabled,
25
+ allowAnnotations,
26
+ allowAnnotationsDraftMode,
27
+ startPos
28
+ }) => {
29
+ const {
30
+ hoverDraftDocumentPosition
31
+ } = useAnnotationRangeState();
32
+ const isDraft = Boolean(enabled && allowAnnotationsDraftMode && hoverDraftDocumentPosition && hoverDraftDocumentPosition.from + 1 === startPos);
33
+ const updateSubscriber = useInlineCommentSubscriberContext();
34
+ const annotationIds = useMemo(() => {
35
+ var _marks$filter$map;
36
+ return (_marks$filter$map = marks === null || marks === void 0 ? void 0 : marks.filter(mark => mark.type.name === 'annotation').map(mark => mark.attrs.id)) !== null && _marks$filter$map !== void 0 ? _marks$filter$map : [];
37
+ }, [marks]);
38
+ const activeIds = useInlineCommentsFilter({
39
+ annotationIds,
40
+ filter: {
41
+ state: AnnotationMarkStates.ACTIVE
42
+ }
43
+ });
44
+ const [focusedId, setFocusedId] = useState();
45
+ const [entered, setEntered] = useState(false);
46
+ const {
47
+ currentSelectedAnnotationId
48
+ } = useAnnotationManagerState();
49
+ const active = activeIds.some(id => id === focusedId || id === currentSelectedAnnotationId);
50
+ useEffect(() => {
51
+ if (!updateSubscriber) {
52
+ return;
53
+ }
54
+ const onFocus = payload => setFocusedId(payload === null || payload === void 0 ? void 0 : payload.annotationId);
55
+ const onBlur = () => setFocusedId(undefined);
56
+ updateSubscriber.on(AnnotationUpdateEvent.SET_ANNOTATION_FOCUS, onFocus);
57
+ updateSubscriber.on(AnnotationUpdateEvent.REMOVE_ANNOTATION_FOCUS, onBlur);
58
+ return () => {
59
+ updateSubscriber.off(AnnotationUpdateEvent.SET_ANNOTATION_FOCUS, onFocus);
60
+ updateSubscriber.off(AnnotationUpdateEvent.REMOVE_ANNOTATION_FOCUS, onBlur);
61
+ };
62
+ }, [updateSubscriber]);
63
+ const onClick = useCallback(event => {
64
+ event.preventDefault();
65
+ event.stopPropagation();
66
+ // Anchor the thread to this chart, not an icon inside the badge or a nested renderer.
67
+ const extension = event.currentTarget.closest('.ak-renderer-extension');
68
+ if (extension && activeIds.length) {
69
+ updateSubscriber === null || updateSubscriber === void 0 ? void 0 : updateSubscriber.emit(AnnotationUpdateEvent.ON_ANNOTATION_CLICK, {
70
+ annotationIds: activeIds,
71
+ eventTarget: extension,
72
+ eventTargetType: 'extension',
73
+ viewMethod: VIEW_METHOD.BADGE
74
+ });
75
+ }
76
+ }, [activeIds, updateSubscriber]);
77
+ const onMouseEnter = useCallback(() => setEntered(true), []);
78
+ const onMouseLeave = useCallback(() => setEntered(false), []);
79
+ return /*#__PURE__*/React.createElement("div", {
80
+ "data-annotation-draft-mark": isDraft || undefined,
81
+ "data-renderer-mark": isDraft || undefined,
82
+ "data-renderer-start-pos": isDraft ? startPos : undefined,
83
+ className: ax(["_kqswh2mm"])
84
+ }, children, allowAnnotations && annotationIds.map(id => /*#__PURE__*/React.createElement(AnnotationComponent, {
85
+ key: id,
86
+ id: id,
87
+ annotationType: AnnotationTypes.INLINE_COMMENT,
88
+ annotationParentIds: annotationParentIds,
89
+ allowAnnotations: true,
90
+ useBlockLevel: true,
91
+ isExtension: true,
92
+ dataAttributes: annotationDataAttributes
93
+ })), enabled && activeIds.length > 0 && updateSubscriber && /*#__PURE__*/React.createElement("div", {
94
+ className: ax(["_kqswstnw _154iu2gc _1xi2u2gc"])
95
+ }, /*#__PURE__*/React.createElement(CommentBadgeNext, {
96
+ status: active ? 'active' : entered ? 'entered' : 'default',
97
+ onClick: onClick,
98
+ onMouseEnter: onMouseEnter,
99
+ onMouseLeave: onMouseLeave
100
+ })));
101
+ };
102
+
103
+ /** Only node-aware provider-approved extensions receive comment visuals. */
104
+ export const ExtensionComments = props => {
105
+ if (!props.enabled && !props.allowAnnotations) {
106
+ return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
107
+ }
108
+ return /*#__PURE__*/React.createElement(ExtensionCommentsEnabled, {
109
+ enabled: props.enabled,
110
+ allowAnnotations: props.allowAnnotations,
111
+ allowAnnotationsDraftMode: props.allowAnnotationsDraftMode,
112
+ startPos: props.startPos,
113
+ marks: props.marks
114
+ }, props.children);
115
+ };
@@ -4,7 +4,7 @@ import _extends from "@babel/runtime/helpers/extends";
4
4
  * @jsx jsx
5
5
  */
6
6
 
7
- import React, { useEffect, useRef } from 'react';
7
+ import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react';
8
8
 
9
9
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
10
10
  import { jsx, css } from '@emotion/react';
@@ -13,8 +13,12 @@ import { overflowShadow } from '@atlaskit/editor-common/ui';
13
13
  import { fg } from '@atlaskit/platform-feature-flags/fg';
14
14
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
15
15
  import { RendererCssClassName } from '../../consts';
16
+ import { ProvidersContext } from '../../ui/annotations/context';
17
+ import { useAnnotationHoverDispatch } from '../../ui/annotations/contexts/AnnotationHoverContext';
18
+ import { useAnnotationRangeDispatch } from '../../ui/annotations/contexts/AnnotationRangeContext';
16
19
  import ExtensionRenderer from '../../ui/ExtensionRenderer';
17
20
  import { calcBreakoutWidthCss } from '../utils/breakout';
21
+ import { ExtensionComments } from './extension-comments';
18
22
  const FORGE_EXTENSION_TYPE = 'com.atlassian.ecosystem';
19
23
  /**
20
24
  * Mirrors `FORGE_INLINE_BODIED_PARAM` in `@atlassian/xen-editor-provider`. Duplicated rather than
@@ -68,7 +72,7 @@ const FireExtensionAsInlineAnalytics = ({
68
72
  return null;
69
73
  };
70
74
  export const renderExtension = (content, layout, options = {}, removeOverflow, extensionId, extensionViewportSizes, nodeHeight, localId, shouldDisplayExtensionAsInline, node, isInsideOfInlineExtension) => {
71
- var _node$parameters, _node$parameters$gues, _node$parameters2;
75
+ var _node$parameters, _node$parameters$gues, _node$parameters2, _options$dataAttribut;
72
76
  const overflowContainerClass = !removeOverflow ? RendererCssClassName.EXTENSION_OVERFLOW_CONTAINER : '';
73
77
 
74
78
  // by default, we assume the extension is at top level, (direct child of doc node)
@@ -147,11 +151,17 @@ export const renderExtension = (content, layout, options = {}, removeOverflow, e
147
151
  },
148
152
  "data-layout": layout,
149
153
  "data-local-id": localId,
154
+ "data-renderer-start-pos": (_options$dataAttribut = options.dataAttributes) === null || _options$dataAttribut === void 0 ? void 0 : _options$dataAttribut['data-renderer-start-pos'],
150
155
  "data-testid": "extension--wrapper",
151
156
  "data-node-type": "extension",
157
+ "data-inline-comments-target": options.isCommentTarget || undefined,
152
158
  "data-top-level": isTopLevel || undefined,
153
159
  "data-forge-inline": isNativeForgeInline || undefined,
154
- "data-migrated-inline": isMigratedInlineBodied || undefined
160
+ "data-migrated-inline": isMigratedInlineBodied || undefined,
161
+ onBlur: options.onBlur,
162
+ onFocus: options.onFocus,
163
+ onMouseEnter: options.onMouseEnter,
164
+ onMouseLeave: options.onMouseLeave
155
165
  }, jsx("div", {
156
166
  tabIndex: options.tabIndex
157
167
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
@@ -165,6 +175,7 @@ export const renderExtension = (content, layout, options = {}, removeOverflow, e
165
175
  }, extensionDiv) : extensionDiv;
166
176
  };
167
177
  const Extension = props => {
178
+ var _providers$inlineComm, _props$dataAttributes;
168
179
  const {
169
180
  text,
170
181
  layout = 'default',
@@ -177,6 +188,55 @@ const Extension = props => {
177
188
  localId,
178
189
  isInsideOfInlineExtension
179
190
  } = props;
191
+ const providers = useContext(ProvidersContext);
192
+ const {
193
+ setHoverTarget
194
+ } = useAnnotationRangeDispatch();
195
+ const {
196
+ cancelTimeout,
197
+ initiateTimeout,
198
+ setIsWithinRange
199
+ } = useAnnotationHoverDispatch();
200
+ const isBlockNodeSupported = providers === null || providers === void 0 ? void 0 : (_providers$inlineComm = providers.inlineComment) === null || _providers$inlineComm === void 0 ? void 0 : _providers$inlineComm.isBlockNodeSupported;
201
+ const isCommentTarget = useMemo(() => Boolean(fg('cc_maui_annotations_on_extensions') && props.extensionNode && (isBlockNodeSupported === null || isBlockNodeSupported === void 0 ? void 0 : isBlockNodeSupported(props.extensionNode)) === true), [props.extensionNode, isBlockNodeSupported]);
202
+ const isFullPageRenderer = props.rendererAppearance === 'full-page' || props.rendererAppearance === 'full-width';
203
+ const onMouseEnter = useCallback(event => {
204
+ cancelTimeout();
205
+ if (event.buttons === 0) {
206
+ setHoverTarget === null || setHoverTarget === void 0 ? void 0 : setHoverTarget(event.currentTarget);
207
+ setIsWithinRange(true);
208
+ }
209
+ }, [cancelTimeout, setHoverTarget, setIsWithinRange]);
210
+ const onMouseLeave = useCallback(() => initiateTimeout(), [initiateTimeout]);
211
+ const onFocus = useCallback(event => {
212
+ if (event.currentTarget.contains(event.relatedTarget)) {
213
+ return;
214
+ }
215
+ cancelTimeout();
216
+ setHoverTarget === null || setHoverTarget === void 0 ? void 0 : setHoverTarget(event.currentTarget);
217
+ setIsWithinRange(true);
218
+ }, [cancelTimeout, setHoverTarget, setIsWithinRange]);
219
+ const onBlur = useCallback(event => {
220
+ if (!event.currentTarget.contains(event.relatedTarget)) {
221
+ initiateTimeout();
222
+ }
223
+ }, [initiateTimeout]);
224
+ const commentTargetOptions = isCommentTarget && isFullPageRenderer ? {
225
+ isCommentTarget: true,
226
+ onBlur,
227
+ onFocus,
228
+ onMouseEnter,
229
+ onMouseLeave
230
+ } : {
231
+ isCommentTarget
232
+ };
233
+ const commentProps = {
234
+ enabled: isCommentTarget,
235
+ allowAnnotations: props.allowAnnotations && fg('cc_maui_annotations_on_extensions'),
236
+ allowAnnotationsDraftMode: props.allowAnnotationsDraftMode,
237
+ startPos: (_props$dataAttributes = props.dataAttributes) === null || _props$dataAttributes === void 0 ? void 0 : _props$dataAttributes['data-renderer-start-pos'],
238
+ marks: props.marks
239
+ };
180
240
  return jsx(ExtensionRenderer
181
241
  // Ignored via go/ees005
182
242
  // eslint-disable-next-line react/jsx-props-no-spreading
@@ -190,12 +250,20 @@ const Extension = props => {
190
250
  try {
191
251
  // Return the result directly if it's a valid JSX.Element
192
252
  if (result && /*#__PURE__*/React.isValidElement(result)) {
193
- return renderExtension(result, layout, {
253
+ return renderExtension(jsx(ExtensionComments, {
254
+ enabled: commentProps.enabled,
255
+ allowAnnotations: commentProps.allowAnnotations,
256
+ allowAnnotationsDraftMode: commentProps.allowAnnotationsDraftMode,
257
+ startPos: commentProps.startPos,
258
+ marks: commentProps.marks
259
+ }, result), layout, {
260
+ dataAttributes: props.dataAttributes,
194
261
  isTopLevel: path.length < 1,
195
262
  handleRef,
196
263
  shadowClassNames,
197
264
  tabIndex: props.tabIndex,
198
- rendererAppearance: props.rendererAppearance
265
+ rendererAppearance: props.rendererAppearance,
266
+ ...commentTargetOptions
199
267
  }, undefined, parameters === null || parameters === void 0 ? void 0 : parameters.extensionId, extensionViewportSizes, nodeHeight, localId, undefined, undefined, isInsideOfInlineExtension);
200
268
  }
201
269
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -208,12 +276,20 @@ const Extension = props => {
208
276
  }
209
277
 
210
278
  // Always return default content if anything goes wrong
211
- return renderExtension(text || 'extension', layout, {
279
+ return renderExtension(jsx(ExtensionComments, {
280
+ enabled: commentProps.enabled,
281
+ allowAnnotations: commentProps.allowAnnotations,
282
+ allowAnnotationsDraftMode: commentProps.allowAnnotationsDraftMode,
283
+ startPos: commentProps.startPos,
284
+ marks: commentProps.marks
285
+ }, text || 'extension'), layout, {
286
+ dataAttributes: props.dataAttributes,
212
287
  isTopLevel: path.length < 1,
213
288
  handleRef,
214
289
  shadowClassNames,
215
290
  tabIndex: props.tabIndex,
216
- rendererAppearance: props.rendererAppearance
291
+ rendererAppearance: props.rendererAppearance,
292
+ ...commentTargetOptions
217
293
  }, undefined, parameters === null || parameters === void 0 ? void 0 : parameters.extensionId, extensionViewportSizes, nodeHeight, localId, undefined, undefined, isInsideOfInlineExtension);
218
294
  });
219
295
  };
@@ -219,6 +219,24 @@ export function resolvePos(node, offset, findEnd = false) {
219
219
  }
220
220
  return resolvedPos + offset;
221
221
  }
222
+ function getSelectedCommentTargetPosition(range) {
223
+ const {
224
+ startContainer,
225
+ startOffset,
226
+ endContainer,
227
+ endOffset
228
+ } = range;
229
+ if (startContainer !== endContainer || endOffset !== startOffset + 1) {
230
+ return false;
231
+ }
232
+ const selectedNode = startContainer.childNodes[startOffset];
233
+ if (!selectedNode || !isElementNode(selectedNode) || selectedNode.dataset.inlineCommentsTarget !== 'true' || !isPositionPointer(selectedNode)) {
234
+ return false;
235
+ }
236
+
237
+ // Subtract 1 from the renderer start position to get the position before the extension node.
238
+ return getStartPos(selectedNode) - 1;
239
+ }
222
240
  export function getPosFromRange(range) {
223
241
  const {
224
242
  startContainer,
@@ -226,6 +244,13 @@ export function getPosFromRange(range) {
226
244
  endContainer,
227
245
  endOffset
228
246
  } = range;
247
+ const selectedCommentTargetPosition = fg('cc_maui_annotations_on_extensions') && getSelectedCommentTargetPosition(range);
248
+ if (selectedCommentTargetPosition !== false) {
249
+ return {
250
+ from: selectedCommentTargetPosition,
251
+ to: selectedCommentTargetPosition
252
+ };
253
+ }
229
254
  const possibleMediaOrMediaSingleElement = findParent(startContainer);
230
255
 
231
256
  // Video hover targets return media single, not media, thus, the extra check in condition.